From 300238cecede382fbd9084499e57f3b30acc75fa Mon Sep 17 00:00:00 2001 From: Gustavo Padovan Date: Mon, 31 Jul 2017 16:36:55 -0300 Subject: dma-buf/sync_file: document flags field Documentation for it was missing. Signed-off-by: Gustavo Padovan Acked-by: Sumit Semwal Link: https://patchwork.freedesktop.org/patch/msgid/20170731193655.6176-1-gustavo@padovan.org --- include/linux/sync_file.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/sync_file.h b/include/linux/sync_file.h index 0ad87c434ae6..790ca021203a 100644 --- a/include/linux/sync_file.h +++ b/include/linux/sync_file.h @@ -25,8 +25,12 @@ * @file: file representing this fence * @sync_file_list: membership in global file list * @wq: wait queue for fence signaling + * @flags: flags for the sync_file * @fence: fence with the fences in the sync_file * @cb: fence callback information + * + * flags: + * POLL_ENABLED: whether userspace is currently poll()'ing or not */ struct sync_file { struct file *file; -- cgit v1.2.3-59-g8ed1b From 9928688492aea161eeeaad7af34f372794cb2f53 Mon Sep 17 00:00:00 2001 From: Inki Dae Date: Mon, 3 Jul 2017 17:42:17 +0900 Subject: drm/bridge: change return type of drm_bridge_add function This patch changes return type of drm_bridge_add function. This function never return negative value but returns only 0. So it changes the return type of this function to void one. Signed-off-by: Inki Dae Signed-off-by: Archit Taneja Link: https://patchwork.freedesktop.org/patch/msgid/1499071350-25168-2-git-send-email-inki.dae@samsung.com --- drivers/gpu/drm/drm_bridge.c | 7 +------ include/drm/drm_bridge.h | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index dc8cdfe1dcac..1638bfe9627c 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -67,17 +67,12 @@ static LIST_HEAD(bridge_list); * drm_bridge_add - add the given bridge to the global bridge list * * @bridge: bridge control structure - * - * RETURNS: - * Unconditionally returns Zero. */ -int drm_bridge_add(struct drm_bridge *bridge) +void drm_bridge_add(struct drm_bridge *bridge) { mutex_lock(&bridge_lock); list_add_tail(&bridge->list, &bridge_list); mutex_unlock(&bridge_lock); - - return 0; } EXPORT_SYMBOL(drm_bridge_add); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 6522d4cbc9d9..682d01ba920c 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -245,7 +245,7 @@ struct drm_bridge { void *driver_private; }; -int drm_bridge_add(struct drm_bridge *bridge); +void drm_bridge_add(struct drm_bridge *bridge); void drm_bridge_remove(struct drm_bridge *bridge); struct drm_bridge *of_drm_find_bridge(struct device_node *np); int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, -- cgit v1.2.3-59-g8ed1b From 25a8ef26fd47e4b60cfae094a43ad9bfc49e676b Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 18 Aug 2017 16:49:51 +0300 Subject: drm/dp: Add defines for DP SDP types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add defines for the secondary data packet (SDP) types from the spec. These are the DP specific ones, and in addition HDMI infoframe types (see enum hdmi_infoframe_type) are also valid SDP types. v2: Add more SDP types v3: Note the DP version that added each SDP type (Rodrigo) Cc: dri-devel@lists.freedesktop.org Reviewed-by: Rodrigo Vivi Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20170818134958.15502-2-ville.syrjala@linux.intel.com --- include/drm/drm_dp_helper.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index b17476a6909c..2c412a15cfa1 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -871,6 +871,18 @@ void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]); u8 drm_dp_link_rate_to_bw_code(int link_rate); int drm_dp_bw_code_to_link_rate(u8 link_bw); +#define DP_SDP_AUDIO_TIMESTAMP 0x01 +#define DP_SDP_AUDIO_STREAM 0x02 +#define DP_SDP_EXTENSION 0x04 /* DP 1.1 */ +#define DP_SDP_AUDIO_COPYMANAGEMENT 0x05 /* DP 1.2 */ +#define DP_SDP_ISRC 0x06 /* DP 1.2 */ +#define DP_SDP_VSC 0x07 /* DP 1.2 */ +#define DP_SDP_CAMERA_GENERIC(i) (0x08 + (i)) /* 0-7, DP 1.3 */ +#define DP_SDP_PPS 0x10 /* DP 1.4 */ +#define DP_SDP_VSC_EXT_VESA 0x20 /* DP 1.4 */ +#define DP_SDP_VSC_EXT_CEA 0x21 /* DP 1.4 */ +/* 0x80+ CEA-861 infoframe types */ + struct edp_sdp_header { u8 HB0; /* Secondary Data Packet ID */ u8 HB1; /* Secondary Data Packet Type */ -- cgit v1.2.3-59-g8ed1b From 63b19547cc3d96041d7bc7ab8de6292b0ebaf2c9 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 23 Jul 2017 17:25:43 +0100 Subject: iio: Use macro magic to avoid manual assign of driver_module Starting point in boiler plate reduction similar to that done for many similar cases elsewhere in the kernel. Signed-off-by: Jonathan Cameron Reviewed-by: Lars-Peter Clausen --- drivers/iio/industrialio-core.c | 35 +++++++++-------------------------- drivers/iio/industrialio-trigger.c | 6 +++--- include/linux/iio/iio.h | 31 +++++++++++++++++++++++++++++-- 3 files changed, 41 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 17ec4cee51dc..7a5aa127c52e 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1660,14 +1660,11 @@ static int iio_check_unique_scan_index(struct iio_dev *indio_dev) static const struct iio_buffer_setup_ops noop_ring_setup_ops; -/** - * iio_device_register() - register a device with the IIO subsystem - * @indio_dev: Device structure filled by the device driver - **/ -int iio_device_register(struct iio_dev *indio_dev) +int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) { int ret; + indio_dev->driver_module = this_mod; /* If the calling driver did not initialize of_node, do it here */ if (!indio_dev->dev.of_node && indio_dev->dev.parent) indio_dev->dev.of_node = indio_dev->dev.parent->of_node; @@ -1713,7 +1710,8 @@ int iio_device_register(struct iio_dev *indio_dev) indio_dev->setup_ops = &noop_ring_setup_ops; cdev_init(&indio_dev->chrdev, &iio_buffer_fileops); - indio_dev->chrdev.owner = indio_dev->info->driver_module; + + indio_dev->chrdev.owner = this_mod; ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev); if (ret < 0) @@ -1731,7 +1729,7 @@ error_unreg_debugfs: iio_device_unregister_debugfs(indio_dev); return ret; } -EXPORT_SYMBOL(iio_device_register); +EXPORT_SYMBOL(__iio_device_register); /** * iio_device_unregister() - unregister a device from the IIO subsystem @@ -1763,23 +1761,8 @@ static void devm_iio_device_unreg(struct device *dev, void *res) iio_device_unregister(*(struct iio_dev **)res); } -/** - * devm_iio_device_register - Resource-managed iio_device_register() - * @dev: Device to allocate iio_dev for - * @indio_dev: Device structure filled by the device driver - * - * Managed iio_device_register. The IIO device registered with this - * function is automatically unregistered on driver detach. This function - * calls iio_device_register() internally. Refer to that function for more - * information. - * - * If an iio_dev registered with this function needs to be unregistered - * separately, devm_iio_device_unregister() must be used. - * - * RETURNS: - * 0 on success, negative error number on failure. - */ -int devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev) +int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev, + struct module *this_mod) { struct iio_dev **ptr; int ret; @@ -1789,7 +1772,7 @@ int devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev) return -ENOMEM; *ptr = indio_dev; - ret = iio_device_register(indio_dev); + ret = __iio_device_register(indio_dev, this_mod); if (!ret) devres_add(dev, ptr); else @@ -1797,7 +1780,7 @@ int devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev) return ret; } -EXPORT_SYMBOL_GPL(devm_iio_device_register); +EXPORT_SYMBOL_GPL(__devm_iio_device_register); /** * devm_iio_device_unregister - Resource-managed iio_device_unregister() diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 4061fed93f1f..9596fedacedb 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -250,7 +250,7 @@ static int iio_trigger_attach_poll_func(struct iio_trigger *trig, = bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER); /* Prevent the module from being removed whilst attached to a trigger */ - __module_get(pf->indio_dev->info->driver_module); + __module_get(pf->indio_dev->driver_module); /* Get irq number */ pf->irq = iio_trigger_get_irq(trig); @@ -286,7 +286,7 @@ out_free_irq: out_put_irq: iio_trigger_put_irq(trig, pf->irq); out_put_module: - module_put(pf->indio_dev->info->driver_module); + module_put(pf->indio_dev->driver_module); return ret; } @@ -307,7 +307,7 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig, trig->attached_own_device = false; iio_trigger_put_irq(trig, pf->irq); free_irq(pf->irq, pf); - module_put(pf->indio_dev->info->driver_module); + module_put(pf->indio_dev->driver_module); return ret; } diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index d68bec297a45..97a014300947 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -518,6 +518,7 @@ struct iio_buffer_setup_ops { /** * struct iio_dev - industrial I/O device * @id: [INTERN] used to identify device internally + * @driver_module: [INTERN] used to make it harder to undercut users * @modes: [DRIVER] operating modes supported by device * @currentmode: [DRIVER] current operating mode * @dev: [DRIVER] device structure, should be assigned a parent @@ -558,6 +559,7 @@ struct iio_buffer_setup_ops { */ struct iio_dev { int id; + struct module *driver_module; int modes; int currentmode; @@ -604,9 +606,34 @@ struct iio_dev { const struct iio_chan_spec *iio_find_channel_from_si(struct iio_dev *indio_dev, int si); -int iio_device_register(struct iio_dev *indio_dev); +/** + * iio_device_register() - register a device with the IIO subsystem + * @indio_dev: Device structure filled by the device driver + **/ +#define iio_device_register(iio_dev) \ + __iio_device_register((iio_dev), THIS_MODULE) +int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod); void iio_device_unregister(struct iio_dev *indio_dev); -int devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev); +/** + * devm_iio_device_register - Resource-managed iio_device_register() + * @dev: Device to allocate iio_dev for + * @indio_dev: Device structure filled by the device driver + * + * Managed iio_device_register. The IIO device registered with this + * function is automatically unregistered on driver detach. This function + * calls iio_device_register() internally. Refer to that function for more + * information. + * + * If an iio_dev registered with this function needs to be unregistered + * separately, devm_iio_device_unregister() must be used. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +#define devm_iio_device_register(dev, indio_dev) \ + __devm_iio_device_register((dev), (indio_dev), THIS_MODULE); +int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev, + struct module *this_mod); void devm_iio_device_unregister(struct device *dev, struct iio_dev *indio_dev); int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp); int iio_device_claim_direct_mode(struct iio_dev *indio_dev); -- cgit v1.2.3-59-g8ed1b From 035c70aeb64b861aa88a666c61fda8b2ae49aeae Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 23 Jul 2017 17:25:44 +0100 Subject: iio: triggers: Use macros to avoid boilerplate assignment of owner. This trig_ops.owner assignment occurs in all trigger drivers and can be simply automated using a macro as has been done in many other places in the kernel. Signed-off-by: Jonathan Cameron Reviewed-by: Lars-Peter Clausen --- drivers/iio/industrialio-trigger.c | 18 ++++++++++++------ include/linux/iio/trigger.h | 19 +++++++++++++------ 2 files changed, 25 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 9596fedacedb..faf00202915a 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -66,10 +66,13 @@ ATTRIBUTE_GROUPS(iio_trig_dev); static struct iio_trigger *__iio_trigger_find_by_name(const char *name); -int iio_trigger_register(struct iio_trigger *trig_info) +int __iio_trigger_register(struct iio_trigger *trig_info, + struct module *this_mod) { int ret; + trig_info->owner = this_mod; + /* trig_info->ops is required for the module member */ if (!trig_info->ops) return -EINVAL; @@ -105,7 +108,7 @@ error_unregister_id: ida_simple_remove(&iio_trigger_ida, trig_info->id); return ret; } -EXPORT_SYMBOL(iio_trigger_register); +EXPORT_SYMBOL(__iio_trigger_register); void iio_trigger_unregister(struct iio_trigger *trig_info) { @@ -663,9 +666,10 @@ static void devm_iio_trigger_unreg(struct device *dev, void *res) } /** - * devm_iio_trigger_register - Resource-managed iio_trigger_register() + * __devm_iio_trigger_register - Resource-managed iio_trigger_register() * @dev: device this trigger was allocated for * @trig_info: trigger to register + * @this_mod: module registering the trigger * * Managed iio_trigger_register(). The IIO trigger registered with this * function is automatically unregistered on driver detach. This function @@ -678,7 +682,9 @@ static void devm_iio_trigger_unreg(struct device *dev, void *res) * RETURNS: * 0 on success, negative error number on failure. */ -int devm_iio_trigger_register(struct device *dev, struct iio_trigger *trig_info) +int __devm_iio_trigger_register(struct device *dev, + struct iio_trigger *trig_info, + struct module *this_mod) { struct iio_trigger **ptr; int ret; @@ -688,7 +694,7 @@ int devm_iio_trigger_register(struct device *dev, struct iio_trigger *trig_info) return -ENOMEM; *ptr = trig_info; - ret = iio_trigger_register(trig_info); + ret = __iio_trigger_register(trig_info, this_mod); if (!ret) devres_add(dev, ptr); else @@ -696,7 +702,7 @@ int devm_iio_trigger_register(struct device *dev, struct iio_trigger *trig_info) return ret; } -EXPORT_SYMBOL_GPL(devm_iio_trigger_register); +EXPORT_SYMBOL_GPL(__devm_iio_trigger_register); /** * devm_iio_trigger_unregister - Resource-managed iio_trigger_unregister() diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index ea08302f2d7b..999793212b40 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -62,6 +62,7 @@ struct iio_trigger_ops { **/ struct iio_trigger { const struct iio_trigger_ops *ops; + struct module *owner; int id; const char *name; struct device dev; @@ -87,14 +88,14 @@ static inline struct iio_trigger *to_iio_trigger(struct device *d) static inline void iio_trigger_put(struct iio_trigger *trig) { - module_put(trig->ops->owner); + module_put(trig->owner); put_device(&trig->dev); } static inline struct iio_trigger *iio_trigger_get(struct iio_trigger *trig) { get_device(&trig->dev); - __module_get(trig->ops->owner); + __module_get(trig->owner); return trig; } @@ -127,10 +128,16 @@ static inline void *iio_trigger_get_drvdata(struct iio_trigger *trig) * iio_trigger_register() - register a trigger with the IIO core * @trig_info: trigger to be registered **/ -int iio_trigger_register(struct iio_trigger *trig_info); - -int devm_iio_trigger_register(struct device *dev, - struct iio_trigger *trig_info); +#define iio_trigger_register(trig_info) \ + __iio_trigger_register((trig_info), THIS_MODULE) +int __iio_trigger_register(struct iio_trigger *trig_info, + struct module *this_mod); + +#define devm_iio_trigger_register(dev, trig_info) \ + __devm_iio_trigger_register((dev), (trig_info), THIS_MODULE) +int __devm_iio_trigger_register(struct device *dev, + struct iio_trigger *trig_info, + struct module *this_mod); /** * iio_trigger_unregister() - unregister a trigger from the core -- cgit v1.2.3-59-g8ed1b From a92e145059cb883155a24a2d3ac33296d33d9df7 Mon Sep 17 00:00:00 2001 From: Tom St Denis Date: Wed, 23 Aug 2017 15:17:47 -0400 Subject: drm/ttm: Add DMA map/unmap tracepoint (v3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also exports two functions that vendor drivers can call to trace DMA mappings. This is meant to help translate IOMMU mappings of bus addresses back to physical pages. Used by the umr amdgpu debugger for instance. Signed-off-by: Tom St Denis Reviewed-by: Christian König Signed-off-by: Alex Deucher (v2): Use dev_name() to get PCI path instead. (v3): Use correct types for dma/phys addresses --- drivers/gpu/drm/ttm/Makefile | 3 +- drivers/gpu/drm/ttm/ttm_debug.c | 74 +++++++++++++++++++++++++++++ drivers/gpu/drm/ttm/ttm_trace.h | 87 +++++++++++++++++++++++++++++++++++ drivers/gpu/drm/ttm/ttm_tracepoints.c | 45 ++++++++++++++++++ include/drm/ttm/ttm_debug.h | 31 +++++++++++++ 5 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/ttm/ttm_debug.c create mode 100644 drivers/gpu/drm/ttm/ttm_trace.h create mode 100644 drivers/gpu/drm/ttm/ttm_tracepoints.c create mode 100644 include/drm/ttm/ttm_debug.h (limited to 'include') diff --git a/drivers/gpu/drm/ttm/Makefile b/drivers/gpu/drm/ttm/Makefile index 4d0c938ff4b2..a44fdfbe6351 100644 --- a/drivers/gpu/drm/ttm/Makefile +++ b/drivers/gpu/drm/ttm/Makefile @@ -1,10 +1,11 @@ # # Makefile for the drm device driver. This driver provides support for the +ccflags-y := -I$(src)/. ttm-y := ttm_memory.o ttm_tt.o ttm_bo.o \ ttm_bo_util.o ttm_bo_vm.o ttm_module.o \ ttm_object.o ttm_lock.o ttm_execbuf_util.o ttm_page_alloc.o \ - ttm_bo_manager.o ttm_page_alloc_dma.o + ttm_bo_manager.o ttm_page_alloc_dma.o ttm_debug.o ttm_tracepoints.o ttm-$(CONFIG_AGP) += ttm_agp_backend.o obj-$(CONFIG_DRM_TTM) += ttm.o diff --git a/drivers/gpu/drm/ttm/ttm_debug.c b/drivers/gpu/drm/ttm/ttm_debug.c new file mode 100644 index 000000000000..ef5f0d090154 --- /dev/null +++ b/drivers/gpu/drm/ttm/ttm_debug.c @@ -0,0 +1,74 @@ +/************************************************************************** + * + * Copyright (c) 2017 Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +/* + * Authors: Tom St Denis + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ttm_trace.h" + +void ttm_trace_dma_map(struct device *dev, struct ttm_dma_tt *tt) +{ + unsigned i; + + if (unlikely(trace_ttm_dma_map_enabled())) { + for (i = 0; i < tt->ttm.num_pages; i++) { + trace_ttm_dma_map( + dev, + tt->ttm.pages[i], + tt->dma_address[i]); + } + } +} +EXPORT_SYMBOL(ttm_trace_dma_map); + +void ttm_trace_dma_unmap(struct device *dev, struct ttm_dma_tt *tt) +{ + unsigned i; + + if (unlikely(trace_ttm_dma_unmap_enabled())) { + for (i = 0; i < tt->ttm.num_pages; i++) { + trace_ttm_dma_unmap( + dev, + tt->ttm.pages[i], + tt->dma_address[i]); + } + } +} +EXPORT_SYMBOL(ttm_trace_dma_unmap); + diff --git a/drivers/gpu/drm/ttm/ttm_trace.h b/drivers/gpu/drm/ttm/ttm_trace.h new file mode 100644 index 000000000000..23279b9b8e64 --- /dev/null +++ b/drivers/gpu/drm/ttm/ttm_trace.h @@ -0,0 +1,87 @@ +/************************************************************************** + * + * Copyright (c) 2017 Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +/* + * Authors: Tom St Denis + */ +#if !defined(_TTM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TTM_TRACE_H_ + +#include +#include +#include + +#include + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM ttm +#define TRACE_INCLUDE_FILE ttm_trace + +TRACE_EVENT(ttm_dma_map, + TP_PROTO(struct device *dev, struct page *page, dma_addr_t dma_address), + TP_ARGS(dev, page, dma_address), + TP_STRUCT__entry( + __string(device, dev_name(dev)) + __field(dma_addr_t, dma) + __field(phys_addr_t, phys) + ), + TP_fast_assign( + __assign_str(device, dev_name(dev)); + __entry->dma = dma_address; + __entry->phys = page_to_phys(page); + ), + TP_printk("%s: %pad => %pa", + __get_str(device), + &__entry->dma, + &__entry->phys) +); + +TRACE_EVENT(ttm_dma_unmap, + TP_PROTO(struct device *dev, struct page *page, dma_addr_t dma_address), + TP_ARGS(dev, page, dma_address), + TP_STRUCT__entry( + __string(device, dev_name(dev)) + __field(dma_addr_t, dma) + __field(phys_addr_t, phys) + ), + TP_fast_assign( + __assign_str(device, dev_name(dev)); + __entry->dma = dma_address; + __entry->phys = page_to_phys(page); + ), + TP_printk("%s: %pad => %pa", + __get_str(device), + &__entry->dma, + &__entry->phys) +); + +#endif + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#include + diff --git a/drivers/gpu/drm/ttm/ttm_tracepoints.c b/drivers/gpu/drm/ttm/ttm_tracepoints.c new file mode 100644 index 000000000000..861a6266822b --- /dev/null +++ b/drivers/gpu/drm/ttm/ttm_tracepoints.c @@ -0,0 +1,45 @@ +/************************************************************************** + * + * Copyright (c) 2017 Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +/* + * Authors: Tom St Denis + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREATE_TRACE_POINTS +#include "ttm_trace.h" diff --git a/include/drm/ttm/ttm_debug.h b/include/drm/ttm/ttm_debug.h new file mode 100644 index 000000000000..b5e460fa5086 --- /dev/null +++ b/include/drm/ttm/ttm_debug.h @@ -0,0 +1,31 @@ +/************************************************************************** + * + * Copyright (c) 2017 Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ +/* + * Authors: Tom St Denis + */ +extern void ttm_trace_dma_map(struct device *dev, struct ttm_dma_tt *tt); +extern void ttm_trace_dma_unmap(struct device *dev, struct ttm_dma_tt *tt); -- cgit v1.2.3-59-g8ed1b From a4dec819c8bba6365eb893a4ca88db4dd1210110 Mon Sep 17 00:00:00 2001 From: Tom St Denis Date: Fri, 18 Aug 2017 10:04:57 -0400 Subject: drm/ttm: Add helper functions to populate/map in one call (v2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions replace a section of common code found in radeon/amdgpu drivers (and possibly others) as part of the ttm_tt_*populate() callbacks. v2: squash in fix for sw iommu from Tom Signed-off-by: Tom St Denis Reviewed-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/ttm/ttm_page_alloc.c | 41 ++++++++++++++++++++++++++++++++++++ include/drm/ttm/ttm_page_alloc.h | 21 ++++++++++++++++++ 2 files changed, 62 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index 871599826773..6a660d196d87 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -920,6 +920,47 @@ void ttm_pool_unpopulate(struct ttm_tt *ttm) } EXPORT_SYMBOL(ttm_pool_unpopulate); +int ttm_populate_and_map_pages(struct device *dev, struct ttm_dma_tt *tt) +{ + unsigned i; + int r; + + r = ttm_pool_populate(&tt->ttm); + if (r) + return r; + + for (i = 0; i < tt->ttm.num_pages; i++) { + tt->dma_address[i] = dma_map_page(dev, tt->ttm.pages[i], + 0, PAGE_SIZE, + DMA_BIDIRECTIONAL); + if (dma_mapping_error(dev, tt->dma_address[i])) { + while (i--) { + dma_unmap_page(dev, tt->dma_address[i], + PAGE_SIZE, DMA_BIDIRECTIONAL); + tt->dma_address[i] = 0; + } + ttm_pool_unpopulate(&tt->ttm); + return -EFAULT; + } + } + return 0; +} +EXPORT_SYMBOL(ttm_populate_and_map_pages); + +void ttm_unmap_and_unpopulate_pages(struct device *dev, struct ttm_dma_tt *tt) +{ + unsigned i; + + for (i = 0; i < tt->ttm.num_pages; i++) { + if (tt->dma_address[i]) { + dma_unmap_page(dev, tt->dma_address[i], + PAGE_SIZE, DMA_BIDIRECTIONAL); + } + } + ttm_pool_unpopulate(&tt->ttm); +} +EXPORT_SYMBOL(ttm_unmap_and_unpopulate_pages); + int ttm_page_alloc_debugfs(struct seq_file *m, void *data) { struct ttm_page_pool *p; diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h index 49a828425fa2..bf21166f2b97 100644 --- a/include/drm/ttm/ttm_page_alloc.h +++ b/include/drm/ttm/ttm_page_alloc.h @@ -83,6 +83,17 @@ extern int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data); extern int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev); extern void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev); + +/** + * Populates and DMA maps pages to fullfil a ttm_dma_populate() request + */ +int ttm_populate_and_map_pages(struct device *dev, struct ttm_dma_tt *tt); + +/** + * Unpopulates and DMA unmaps pages as part of a + * ttm_dma_unpopulate() request */ +void ttm_unmap_and_unpopulate_pages(struct device *dev, struct ttm_dma_tt *tt); + #else static inline int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages) @@ -105,6 +116,16 @@ static inline void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev) { } + +static inline int ttm_populate_and_map_pages(struct device *dev, struct ttm_dma_tt *tt) +{ + return -ENOMEM; +} + +static inline void ttm_unmap_and_unpopulate_pages(struct device *dev, struct ttm_dma_tt *tt) +{ +} + #endif #endif -- cgit v1.2.3-59-g8ed1b From 96bec198352799794b0f8937620e811ef8b9fa22 Mon Sep 17 00:00:00 2001 From: Tom St Denis Date: Thu, 24 Aug 2017 06:46:39 -0400 Subject: drm/ttm: Remove needless 'extern' on functions in header. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minor tidy up. Signed-off-by: Tom St Denis Reviewed-by: Christian König Signed-off-by: Alex Deucher --- include/drm/ttm/ttm_page_alloc.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/drm/ttm/ttm_page_alloc.h b/include/drm/ttm/ttm_page_alloc.h index bf21166f2b97..38a2b4770c35 100644 --- a/include/drm/ttm/ttm_page_alloc.h +++ b/include/drm/ttm/ttm_page_alloc.h @@ -47,7 +47,7 @@ void ttm_page_alloc_fini(void); * * Add backing pages to all of @ttm */ -extern int ttm_pool_populate(struct ttm_tt *ttm); +int ttm_pool_populate(struct ttm_tt *ttm); /** * ttm_pool_unpopulate: @@ -56,12 +56,12 @@ extern int ttm_pool_populate(struct ttm_tt *ttm); * * Free all pages of @ttm */ -extern void ttm_pool_unpopulate(struct ttm_tt *ttm); +void ttm_pool_unpopulate(struct ttm_tt *ttm); /** * Output the state of pools to debugfs file */ -extern int ttm_page_alloc_debugfs(struct seq_file *m, void *data); +int ttm_page_alloc_debugfs(struct seq_file *m, void *data); #if defined(CONFIG_SWIOTLB) || defined(CONFIG_INTEL_IOMMU) @@ -78,10 +78,10 @@ void ttm_dma_page_alloc_fini(void); /** * Output the state of pools to debugfs file */ -extern int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data); +int ttm_dma_page_alloc_debugfs(struct seq_file *m, void *data); -extern int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev); -extern void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev); +int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev); +void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev); /** -- cgit v1.2.3-59-g8ed1b From e1eb899b45781b9bb77e6d7772d6e67bb0bc1a18 Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 25 Aug 2017 09:14:43 +0200 Subject: drm/amdgpu: add IOCTL interface for per VM BOs v3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the IOCTL interface so that applications can allocate per VM BOs. Still WIP since not all corner cases are tested yet, but this reduces average CS overhead for 10K BOs from 21ms down to 48us. v2: add some extra checks, remove the WIP tag v3: rename new flag to AMDGPU_GEM_CREATE_VM_ALWAYS_VALID Signed-off-by: Christian König Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 7 ++-- drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 63 ++++++++++++++++++++++--------- drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c | 3 +- include/uapi/drm/amdgpu_drm.h | 2 + 5 files changed, 55 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 103635ab784c..5809f55e0d9d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -455,9 +455,10 @@ struct amdgpu_sa_bo { */ void amdgpu_gem_force_release(struct amdgpu_device *adev); int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size, - int alignment, u32 initial_domain, - u64 flags, bool kernel, - struct drm_gem_object **obj); + int alignment, u32 initial_domain, + u64 flags, bool kernel, + struct reservation_object *resv, + struct drm_gem_object **obj); int amdgpu_mode_dumb_create(struct drm_file *file_priv, struct drm_device *dev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index 9afa9c097e1f..b6cb276f0a70 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -149,7 +149,7 @@ static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED | AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS | AMDGPU_GEM_CREATE_VRAM_CLEARED, - true, &gobj); + true, NULL, &gobj); if (ret) { pr_err("failed to allocate framebuffer (%d)\n", aligned_size); return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index e32a2b55b54f..f1e61b3df640 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -44,11 +44,12 @@ void amdgpu_gem_object_free(struct drm_gem_object *gobj) } int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size, - int alignment, u32 initial_domain, - u64 flags, bool kernel, - struct drm_gem_object **obj) + int alignment, u32 initial_domain, + u64 flags, bool kernel, + struct reservation_object *resv, + struct drm_gem_object **obj) { - struct amdgpu_bo *robj; + struct amdgpu_bo *bo; int r; *obj = NULL; @@ -59,7 +60,7 @@ int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size, retry: r = amdgpu_bo_create(adev, size, alignment, kernel, initial_domain, - flags, NULL, NULL, 0, &robj); + flags, NULL, resv, 0, &bo); if (r) { if (r != -ERESTARTSYS) { if (initial_domain == AMDGPU_GEM_DOMAIN_VRAM) { @@ -71,7 +72,7 @@ retry: } return r; } - *obj = &robj->gem_base; + *obj = &bo->gem_base; return 0; } @@ -119,6 +120,10 @@ int amdgpu_gem_object_open(struct drm_gem_object *obj, if (mm && mm != current->mm) return -EPERM; + if (abo->flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID && + abo->tbo.resv != vm->root.base.bo->tbo.resv) + return -EPERM; + r = amdgpu_bo_reserve(abo, false); if (r) return r; @@ -142,13 +147,14 @@ void amdgpu_gem_object_close(struct drm_gem_object *obj, struct amdgpu_vm *vm = &fpriv->vm; struct amdgpu_bo_list_entry vm_pd; - struct list_head list; + struct list_head list, duplicates; struct ttm_validate_buffer tv; struct ww_acquire_ctx ticket; struct amdgpu_bo_va *bo_va; int r; INIT_LIST_HEAD(&list); + INIT_LIST_HEAD(&duplicates); tv.bo = &bo->tbo; tv.shared = true; @@ -156,7 +162,7 @@ void amdgpu_gem_object_close(struct drm_gem_object *obj, amdgpu_vm_get_pd_bo(vm, &list, &vm_pd); - r = ttm_eu_reserve_buffers(&ticket, &list, false, NULL); + r = ttm_eu_reserve_buffers(&ticket, &list, false, &duplicates); if (r) { dev_err(adev->dev, "leaking bo va because " "we fail to reserve bo (%d)\n", r); @@ -191,9 +197,12 @@ int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { struct amdgpu_device *adev = dev->dev_private; + struct amdgpu_fpriv *fpriv = filp->driver_priv; + struct amdgpu_vm *vm = &fpriv->vm; union drm_amdgpu_gem_create *args = data; uint64_t flags = args->in.domain_flags; uint64_t size = args->in.bo_size; + struct reservation_object *resv = NULL; struct drm_gem_object *gobj; uint32_t handle; int r; @@ -202,7 +211,8 @@ int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, if (flags & ~(AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED | AMDGPU_GEM_CREATE_NO_CPU_ACCESS | AMDGPU_GEM_CREATE_CPU_GTT_USWC | - AMDGPU_GEM_CREATE_VRAM_CLEARED)) + AMDGPU_GEM_CREATE_VRAM_CLEARED | + AMDGPU_GEM_CREATE_VM_ALWAYS_VALID)) return -EINVAL; /* reject invalid gem domains */ @@ -229,9 +239,25 @@ int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, } size = roundup(size, PAGE_SIZE); + if (flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID) { + r = amdgpu_bo_reserve(vm->root.base.bo, false); + if (r) + return r; + + resv = vm->root.base.bo->tbo.resv; + } + r = amdgpu_gem_object_create(adev, size, args->in.alignment, (u32)(0xffffffff & args->in.domains), - flags, false, &gobj); + flags, false, resv, &gobj); + if (flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID) { + if (!r) { + struct amdgpu_bo *abo = gem_to_amdgpu_bo(gobj); + + abo->parent = amdgpu_bo_ref(vm->root.base.bo); + } + amdgpu_bo_unreserve(vm->root.base.bo); + } if (r) return r; @@ -273,9 +299,8 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data, } /* create a gem object to contain this object in */ - r = amdgpu_gem_object_create(adev, args->size, 0, - AMDGPU_GEM_DOMAIN_CPU, 0, - 0, &gobj); + r = amdgpu_gem_object_create(adev, args->size, 0, AMDGPU_GEM_DOMAIN_CPU, + 0, 0, NULL, &gobj); if (r) return r; @@ -527,7 +552,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, struct amdgpu_bo_list_entry vm_pd; struct ttm_validate_buffer tv; struct ww_acquire_ctx ticket; - struct list_head list; + struct list_head list, duplicates; uint64_t va_flags; int r = 0; @@ -563,6 +588,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, } INIT_LIST_HEAD(&list); + INIT_LIST_HEAD(&duplicates); if ((args->operation != AMDGPU_VA_OP_CLEAR) && !(args->flags & AMDGPU_VM_PAGE_PRT)) { gobj = drm_gem_object_lookup(filp, args->handle); @@ -579,7 +605,7 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, amdgpu_vm_get_pd_bo(&fpriv->vm, &list, &vm_pd); - r = ttm_eu_reserve_buffers(&ticket, &list, true, NULL); + r = ttm_eu_reserve_buffers(&ticket, &list, true, &duplicates); if (r) goto error_unref; @@ -645,6 +671,7 @@ error_unref: int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { + struct amdgpu_device *adev = dev->dev_private; struct drm_amdgpu_gem_op *args = data; struct drm_gem_object *gobj; struct amdgpu_bo *robj; @@ -692,6 +719,9 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, if (robj->allowed_domains == AMDGPU_GEM_DOMAIN_VRAM) robj->allowed_domains |= AMDGPU_GEM_DOMAIN_GTT; + if (robj->flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID) + amdgpu_vm_bo_invalidate(adev, robj, true); + amdgpu_bo_unreserve(robj); break; default: @@ -721,8 +751,7 @@ int amdgpu_mode_dumb_create(struct drm_file *file_priv, r = amdgpu_gem_object_create(adev, args->size, 0, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, - ttm_bo_type_device, - &gobj); + false, NULL, &gobj); if (r) return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c index 5b3f92891f89..7e0826469b5e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_prime.c @@ -136,7 +136,8 @@ struct dma_buf *amdgpu_gem_prime_export(struct drm_device *dev, { struct amdgpu_bo *bo = gem_to_amdgpu_bo(gobj); - if (amdgpu_ttm_tt_get_usermm(bo->tbo.ttm)) + if (amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) || + bo->flags & AMDGPU_GEM_CREATE_VM_ALWAYS_VALID) return ERR_PTR(-EPERM); return drm_gem_prime_export(dev, gobj, flags); diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 7b8fa11c2285..e055776f2f4c 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -87,6 +87,8 @@ extern "C" { #define AMDGPU_GEM_CREATE_SHADOW (1 << 4) /* Flag that allocating the BO should use linear VRAM */ #define AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS (1 << 5) +/* Flag that BO is always valid in this VM */ +#define AMDGPU_GEM_CREATE_VM_ALWAYS_VALID (1 << 6) struct drm_amdgpu_gem_create_in { /** the requested memory size */ -- cgit v1.2.3-59-g8ed1b From 0890540e21cf1156b4cf960a4c1c734db4e816f9 Mon Sep 17 00:00:00 2001 From: Lionel Landwerlin Date: Wed, 30 Aug 2017 17:12:05 +0100 Subject: drm/i915: add GT number to intel_device_info Up to Coffeelake we could deduce this GT number from the device ID. This doesn't seem to be the case anymore. This change reorders pciids per GT and adds a gt field to intel_device_info. We set this field on the following platforms : - SNB/IVB/HSW/BDW/SKL/KBL/CFL/CNL Before & After : $ modinfo drivers/gpu/drm/i915/i915.ko | grep ^alias | wc -l 209 v2: Add SNB & IVB (Chris) v3: Fix compilation error in early-quirks (Lionel) v4: Fix inconsistency between FEATURE/PLATFORM macros (Ville) Signed-off-by: Lionel Landwerlin Reviewed-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20170830161208.29221-2-lionel.g.landwerlin@intel.com --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_pci.c | 193 +++++++++++++++++++++++++++++++--------- include/drm/i915_pciids.h | 152 +++++++++++++++++++------------ 3 files changed, 246 insertions(+), 100 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 064bf0f5d481..124ef872f5f0 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -860,6 +860,7 @@ struct intel_device_info { u8 gen; u16 gen_mask; enum intel_platform platform; + u8 gt; /* GT number, 0 if undefined */ u8 ring_mask; /* Rings supported by the HW */ u8 num_rings; #define DEFINE_FLAG(name) u8 name:1 diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c index a1e6b696bcfa..f56aa8e3890b 100644 --- a/drivers/gpu/drm/i915/i915_pci.c +++ b/drivers/gpu/drm/i915/i915_pci.c @@ -224,15 +224,34 @@ static const struct intel_device_info intel_ironlake_m_info = { GEN_DEFAULT_PIPEOFFSETS, \ CURSOR_OFFSETS -static const struct intel_device_info intel_sandybridge_d_info = { - GEN6_FEATURES, - .platform = INTEL_SANDYBRIDGE, +#define SNB_D_PLATFORM \ + GEN6_FEATURES, \ + .platform = INTEL_SANDYBRIDGE + +static const struct intel_device_info intel_sandybridge_d_gt1_info = { + SNB_D_PLATFORM, + .gt = 1, }; -static const struct intel_device_info intel_sandybridge_m_info = { - GEN6_FEATURES, - .platform = INTEL_SANDYBRIDGE, - .is_mobile = 1, +static const struct intel_device_info intel_sandybridge_d_gt2_info = { + SNB_D_PLATFORM, + .gt = 2, +}; + +#define SNB_M_PLATFORM \ + GEN6_FEATURES, \ + .platform = INTEL_SANDYBRIDGE, \ + .is_mobile = 1 + + +static const struct intel_device_info intel_sandybridge_m_gt1_info = { + SNB_M_PLATFORM, + .gt = 1, +}; + +static const struct intel_device_info intel_sandybridge_m_gt2_info = { + SNB_M_PLATFORM, + .gt = 2, }; #define GEN7_FEATURES \ @@ -249,22 +268,41 @@ static const struct intel_device_info intel_sandybridge_m_info = { GEN_DEFAULT_PIPEOFFSETS, \ IVB_CURSOR_OFFSETS -static const struct intel_device_info intel_ivybridge_d_info = { - GEN7_FEATURES, - .platform = INTEL_IVYBRIDGE, - .has_l3_dpf = 1, +#define IVB_D_PLATFORM \ + GEN7_FEATURES, \ + .platform = INTEL_IVYBRIDGE, \ + .has_l3_dpf = 1 + +static const struct intel_device_info intel_ivybridge_d_gt1_info = { + IVB_D_PLATFORM, + .gt = 1, }; -static const struct intel_device_info intel_ivybridge_m_info = { - GEN7_FEATURES, - .platform = INTEL_IVYBRIDGE, - .is_mobile = 1, - .has_l3_dpf = 1, +static const struct intel_device_info intel_ivybridge_d_gt2_info = { + IVB_D_PLATFORM, + .gt = 2, +}; + +#define IVB_M_PLATFORM \ + GEN7_FEATURES, \ + .platform = INTEL_IVYBRIDGE, \ + .is_mobile = 1, \ + .has_l3_dpf = 1 + +static const struct intel_device_info intel_ivybridge_m_gt1_info = { + IVB_M_PLATFORM, + .gt = 1, +}; + +static const struct intel_device_info intel_ivybridge_m_gt2_info = { + IVB_M_PLATFORM, + .gt = 2, }; static const struct intel_device_info intel_ivybridge_q_info = { GEN7_FEATURES, .platform = INTEL_IVYBRIDGE, + .gt = 2, .num_pipes = 0, /* legal, last one wins */ .has_l3_dpf = 1, }; @@ -299,10 +337,24 @@ static const struct intel_device_info intel_valleyview_info = { .has_rc6p = 0 /* RC6p removed-by HSW */, \ .has_runtime_pm = 1 -static const struct intel_device_info intel_haswell_info = { - HSW_FEATURES, - .platform = INTEL_HASWELL, - .has_l3_dpf = 1, +#define HSW_PLATFORM \ + HSW_FEATURES, \ + .platform = INTEL_HASWELL, \ + .has_l3_dpf = 1 + +static const struct intel_device_info intel_haswell_gt1_info = { + HSW_PLATFORM, + .gt = 1, +}; + +static const struct intel_device_info intel_haswell_gt2_info = { + HSW_PLATFORM, + .gt = 2, +}; + +static const struct intel_device_info intel_haswell_gt3_info = { + HSW_PLATFORM, + .gt = 3, }; #define BDW_FEATURES \ @@ -318,12 +370,27 @@ static const struct intel_device_info intel_haswell_info = { .gen = 8, \ .platform = INTEL_BROADWELL -static const struct intel_device_info intel_broadwell_info = { +static const struct intel_device_info intel_broadwell_gt1_info = { + BDW_PLATFORM, + .gt = 1, +}; + +static const struct intel_device_info intel_broadwell_gt2_info = { BDW_PLATFORM, + .gt = 2, +}; + +static const struct intel_device_info intel_broadwell_rsvd_info = { + BDW_PLATFORM, + .gt = 3, + /* According to the device ID those devices are GT3, they were + * previously treated as not GT3, keep it like that. + */ }; static const struct intel_device_info intel_broadwell_gt3_info = { BDW_PLATFORM, + .gt = 3, .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, }; @@ -358,13 +425,29 @@ static const struct intel_device_info intel_cherryview_info = { .has_guc = 1, \ .ddb_size = 896 -static const struct intel_device_info intel_skylake_info = { +static const struct intel_device_info intel_skylake_gt1_info = { SKL_PLATFORM, + .gt = 1, }; -static const struct intel_device_info intel_skylake_gt3_info = { +static const struct intel_device_info intel_skylake_gt2_info = { SKL_PLATFORM, - .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, + .gt = 2, +}; + +#define SKL_GT3_PLUS_PLATFORM \ + SKL_PLATFORM, \ + .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING + + +static const struct intel_device_info intel_skylake_gt3_info = { + SKL_GT3_PLUS_PLATFORM, + .gt = 3, +}; + +static const struct intel_device_info intel_skylake_gt4_info = { + SKL_GT3_PLUS_PLATFORM, + .gt = 4, }; #define GEN9_LP_FEATURES \ @@ -415,12 +498,19 @@ static const struct intel_device_info intel_geminilake_info = { .has_guc = 1, \ .ddb_size = 896 -static const struct intel_device_info intel_kabylake_info = { +static const struct intel_device_info intel_kabylake_gt1_info = { KBL_PLATFORM, + .gt = 1, +}; + +static const struct intel_device_info intel_kabylake_gt2_info = { + KBL_PLATFORM, + .gt = 2, }; static const struct intel_device_info intel_kabylake_gt3_info = { KBL_PLATFORM, + .gt = 3, .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, }; @@ -433,20 +523,28 @@ static const struct intel_device_info intel_kabylake_gt3_info = { .has_guc = 1, \ .ddb_size = 896 -static const struct intel_device_info intel_coffeelake_info = { +static const struct intel_device_info intel_coffeelake_gt1_info = { + CFL_PLATFORM, + .gt = 1, +}; + +static const struct intel_device_info intel_coffeelake_gt2_info = { CFL_PLATFORM, + .gt = 2, }; static const struct intel_device_info intel_coffeelake_gt3_info = { CFL_PLATFORM, + .gt = 3, .ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING, }; -static const struct intel_device_info intel_cannonlake_info = { +static const struct intel_device_info intel_cannonlake_gt2_info = { BDW_FEATURES, .is_alpha_support = 1, .platform = INTEL_CANNONLAKE, .gen = 10, + .gt = 2, .ddb_size = 1024, .has_csr = 1, .color = { .degamma_lut_size = 0, .gamma_lut_size = 1024 } @@ -475,31 +573,40 @@ static const struct pci_device_id pciidlist[] = { INTEL_PINEVIEW_IDS(&intel_pineview_info), INTEL_IRONLAKE_D_IDS(&intel_ironlake_d_info), INTEL_IRONLAKE_M_IDS(&intel_ironlake_m_info), - INTEL_SNB_D_IDS(&intel_sandybridge_d_info), - INTEL_SNB_M_IDS(&intel_sandybridge_m_info), + INTEL_SNB_D_GT1_IDS(&intel_sandybridge_d_gt1_info), + INTEL_SNB_D_GT2_IDS(&intel_sandybridge_d_gt2_info), + INTEL_SNB_M_GT1_IDS(&intel_sandybridge_m_gt1_info), + INTEL_SNB_M_GT2_IDS(&intel_sandybridge_m_gt2_info), INTEL_IVB_Q_IDS(&intel_ivybridge_q_info), /* must be first IVB */ - INTEL_IVB_M_IDS(&intel_ivybridge_m_info), - INTEL_IVB_D_IDS(&intel_ivybridge_d_info), - INTEL_HSW_IDS(&intel_haswell_info), + INTEL_IVB_M_GT1_IDS(&intel_ivybridge_m_gt1_info), + INTEL_IVB_M_GT2_IDS(&intel_ivybridge_m_gt2_info), + INTEL_IVB_D_GT1_IDS(&intel_ivybridge_d_gt1_info), + INTEL_IVB_D_GT2_IDS(&intel_ivybridge_d_gt2_info), + INTEL_HSW_GT1_IDS(&intel_haswell_gt1_info), + INTEL_HSW_GT2_IDS(&intel_haswell_gt2_info), + INTEL_HSW_GT3_IDS(&intel_haswell_gt3_info), INTEL_VLV_IDS(&intel_valleyview_info), - INTEL_BDW_GT12_IDS(&intel_broadwell_info), + INTEL_BDW_GT1_IDS(&intel_broadwell_gt1_info), + INTEL_BDW_GT2_IDS(&intel_broadwell_gt2_info), INTEL_BDW_GT3_IDS(&intel_broadwell_gt3_info), - INTEL_BDW_RSVD_IDS(&intel_broadwell_info), + INTEL_BDW_RSVD_IDS(&intel_broadwell_rsvd_info), INTEL_CHV_IDS(&intel_cherryview_info), - INTEL_SKL_GT1_IDS(&intel_skylake_info), - INTEL_SKL_GT2_IDS(&intel_skylake_info), + INTEL_SKL_GT1_IDS(&intel_skylake_gt1_info), + INTEL_SKL_GT2_IDS(&intel_skylake_gt2_info), INTEL_SKL_GT3_IDS(&intel_skylake_gt3_info), - INTEL_SKL_GT4_IDS(&intel_skylake_gt3_info), + INTEL_SKL_GT4_IDS(&intel_skylake_gt4_info), INTEL_BXT_IDS(&intel_broxton_info), INTEL_GLK_IDS(&intel_geminilake_info), - INTEL_KBL_GT1_IDS(&intel_kabylake_info), - INTEL_KBL_GT2_IDS(&intel_kabylake_info), + INTEL_KBL_GT1_IDS(&intel_kabylake_gt1_info), + INTEL_KBL_GT2_IDS(&intel_kabylake_gt2_info), INTEL_KBL_GT3_IDS(&intel_kabylake_gt3_info), INTEL_KBL_GT4_IDS(&intel_kabylake_gt3_info), - INTEL_CFL_S_IDS(&intel_coffeelake_info), - INTEL_CFL_H_IDS(&intel_coffeelake_info), - INTEL_CFL_U_IDS(&intel_coffeelake_gt3_info), - INTEL_CNL_IDS(&intel_cannonlake_info), + INTEL_CFL_S_GT1_IDS(&intel_coffeelake_gt1_info), + INTEL_CFL_S_GT2_IDS(&intel_coffeelake_gt2_info), + INTEL_CFL_H_GT2_IDS(&intel_coffeelake_gt2_info), + INTEL_CFL_U_GT3_IDS(&intel_coffeelake_gt3_info), + INTEL_CNL_U_GT2_IDS(&intel_cannonlake_gt2_info), + INTEL_CNL_Y_GT2_IDS(&intel_cannonlake_gt2_info), {0, 0, 0} }; MODULE_DEVICE_TABLE(pci, pciidlist); diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 34c8f5600ce0..1257e15c1a03 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -118,92 +118,125 @@ #define INTEL_IRONLAKE_M_IDS(info) \ INTEL_VGA_DEVICE(0x0046, info) -#define INTEL_SNB_D_IDS(info) \ +#define INTEL_SNB_D_GT1_IDS(info) \ INTEL_VGA_DEVICE(0x0102, info), \ - INTEL_VGA_DEVICE(0x0112, info), \ - INTEL_VGA_DEVICE(0x0122, info), \ INTEL_VGA_DEVICE(0x010A, info) -#define INTEL_SNB_M_IDS(info) \ - INTEL_VGA_DEVICE(0x0106, info), \ +#define INTEL_SNB_D_GT2_IDS(info) \ + INTEL_VGA_DEVICE(0x0112, info), \ + INTEL_VGA_DEVICE(0x0122, info) + +#define INTEL_SNB_D_IDS(info) \ + INTEL_SNB_D_GT1_IDS(info), \ + INTEL_SNB_D_GT2_IDS(info) + +#define INTEL_SNB_M_GT1_IDS(info) \ + INTEL_VGA_DEVICE(0x0106, info) + +#define INTEL_SNB_M_GT2_IDS(info) \ INTEL_VGA_DEVICE(0x0116, info), \ INTEL_VGA_DEVICE(0x0126, info) +#define INTEL_SNB_M_IDS(info) \ + INTEL_SNB_M_GT1_IDS(info), \ + INTEL_SNB_M_GT2_IDS(info) + +#define INTEL_IVB_M_GT1_IDS(info) \ + INTEL_VGA_DEVICE(0x0156, info) /* GT1 mobile */ + +#define INTEL_IVB_M_GT2_IDS(info) \ + INTEL_VGA_DEVICE(0x0166, info) /* GT2 mobile */ + #define INTEL_IVB_M_IDS(info) \ - INTEL_VGA_DEVICE(0x0156, info), /* GT1 mobile */ \ - INTEL_VGA_DEVICE(0x0166, info) /* GT2 mobile */ + INTEL_IVB_M_GT1_IDS(info), \ + INTEL_IVB_M_GT2_IDS(info) -#define INTEL_IVB_D_IDS(info) \ +#define INTEL_IVB_D_GT1_IDS(info) \ INTEL_VGA_DEVICE(0x0152, info), /* GT1 desktop */ \ + INTEL_VGA_DEVICE(0x015a, info) /* GT1 server */ + +#define INTEL_IVB_D_GT2_IDS(info) \ INTEL_VGA_DEVICE(0x0162, info), /* GT2 desktop */ \ - INTEL_VGA_DEVICE(0x015a, info), /* GT1 server */ \ INTEL_VGA_DEVICE(0x016a, info) /* GT2 server */ +#define INTEL_IVB_D_IDS(info) \ + INTEL_IVB_D_GT1_IDS(info), \ + INTEL_IVB_D_GT2_IDS(info) + #define INTEL_IVB_Q_IDS(info) \ INTEL_QUANTA_VGA_DEVICE(info) /* Quanta transcode */ -#define INTEL_HSW_IDS(info) \ +#define INTEL_HSW_GT1_IDS(info) \ INTEL_VGA_DEVICE(0x0402, info), /* GT1 desktop */ \ - INTEL_VGA_DEVICE(0x0412, info), /* GT2 desktop */ \ - INTEL_VGA_DEVICE(0x0422, info), /* GT3 desktop */ \ INTEL_VGA_DEVICE(0x040a, info), /* GT1 server */ \ - INTEL_VGA_DEVICE(0x041a, info), /* GT2 server */ \ - INTEL_VGA_DEVICE(0x042a, info), /* GT3 server */ \ INTEL_VGA_DEVICE(0x040B, info), /* GT1 reserved */ \ - INTEL_VGA_DEVICE(0x041B, info), /* GT2 reserved */ \ - INTEL_VGA_DEVICE(0x042B, info), /* GT3 reserved */ \ INTEL_VGA_DEVICE(0x040E, info), /* GT1 reserved */ \ - INTEL_VGA_DEVICE(0x041E, info), /* GT2 reserved */ \ - INTEL_VGA_DEVICE(0x042E, info), /* GT3 reserved */ \ INTEL_VGA_DEVICE(0x0C02, info), /* SDV GT1 desktop */ \ - INTEL_VGA_DEVICE(0x0C12, info), /* SDV GT2 desktop */ \ - INTEL_VGA_DEVICE(0x0C22, info), /* SDV GT3 desktop */ \ INTEL_VGA_DEVICE(0x0C0A, info), /* SDV GT1 server */ \ - INTEL_VGA_DEVICE(0x0C1A, info), /* SDV GT2 server */ \ - INTEL_VGA_DEVICE(0x0C2A, info), /* SDV GT3 server */ \ INTEL_VGA_DEVICE(0x0C0B, info), /* SDV GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0C1B, info), /* SDV GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0C2B, info), /* SDV GT3 reserved */ \ INTEL_VGA_DEVICE(0x0C0E, info), /* SDV GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0C1E, info), /* SDV GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0C2E, info), /* SDV GT3 reserved */ \ INTEL_VGA_DEVICE(0x0A02, info), /* ULT GT1 desktop */ \ - INTEL_VGA_DEVICE(0x0A12, info), /* ULT GT2 desktop */ \ - INTEL_VGA_DEVICE(0x0A22, info), /* ULT GT3 desktop */ \ INTEL_VGA_DEVICE(0x0A0A, info), /* ULT GT1 server */ \ - INTEL_VGA_DEVICE(0x0A1A, info), /* ULT GT2 server */ \ - INTEL_VGA_DEVICE(0x0A2A, info), /* ULT GT3 server */ \ INTEL_VGA_DEVICE(0x0A0B, info), /* ULT GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0A1B, info), /* ULT GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0A2B, info), /* ULT GT3 reserved */ \ INTEL_VGA_DEVICE(0x0D02, info), /* CRW GT1 desktop */ \ - INTEL_VGA_DEVICE(0x0D12, info), /* CRW GT2 desktop */ \ - INTEL_VGA_DEVICE(0x0D22, info), /* CRW GT3 desktop */ \ INTEL_VGA_DEVICE(0x0D0A, info), /* CRW GT1 server */ \ - INTEL_VGA_DEVICE(0x0D1A, info), /* CRW GT2 server */ \ - INTEL_VGA_DEVICE(0x0D2A, info), /* CRW GT3 server */ \ INTEL_VGA_DEVICE(0x0D0B, info), /* CRW GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0D1B, info), /* CRW GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0D2B, info), /* CRW GT3 reserved */ \ INTEL_VGA_DEVICE(0x0D0E, info), /* CRW GT1 reserved */ \ - INTEL_VGA_DEVICE(0x0D1E, info), /* CRW GT2 reserved */ \ - INTEL_VGA_DEVICE(0x0D2E, info), /* CRW GT3 reserved */ \ INTEL_VGA_DEVICE(0x0406, info), /* GT1 mobile */ \ + INTEL_VGA_DEVICE(0x0C06, info), /* SDV GT1 mobile */ \ + INTEL_VGA_DEVICE(0x0A06, info), /* ULT GT1 mobile */ \ + INTEL_VGA_DEVICE(0x0A0E, info), /* ULX GT1 mobile */ \ + INTEL_VGA_DEVICE(0x0D06, info) /* CRW GT1 mobile */ + +#define INTEL_HSW_GT2_IDS(info) \ + INTEL_VGA_DEVICE(0x0412, info), /* GT2 desktop */ \ + INTEL_VGA_DEVICE(0x041a, info), /* GT2 server */ \ + INTEL_VGA_DEVICE(0x041B, info), /* GT2 reserved */ \ + INTEL_VGA_DEVICE(0x041E, info), /* GT2 reserved */ \ + INTEL_VGA_DEVICE(0x0C12, info), /* SDV GT2 desktop */ \ + INTEL_VGA_DEVICE(0x0C1A, info), /* SDV GT2 server */ \ + INTEL_VGA_DEVICE(0x0C1B, info), /* SDV GT2 reserved */ \ + INTEL_VGA_DEVICE(0x0C1E, info), /* SDV GT2 reserved */ \ + INTEL_VGA_DEVICE(0x0A12, info), /* ULT GT2 desktop */ \ + INTEL_VGA_DEVICE(0x0A1A, info), /* ULT GT2 server */ \ + INTEL_VGA_DEVICE(0x0A1B, info), /* ULT GT2 reserved */ \ + INTEL_VGA_DEVICE(0x0D12, info), /* CRW GT2 desktop */ \ + INTEL_VGA_DEVICE(0x0D1A, info), /* CRW GT2 server */ \ + INTEL_VGA_DEVICE(0x0D1B, info), /* CRW GT2 reserved */ \ + INTEL_VGA_DEVICE(0x0D1E, info), /* CRW GT2 reserved */ \ INTEL_VGA_DEVICE(0x0416, info), /* GT2 mobile */ \ INTEL_VGA_DEVICE(0x0426, info), /* GT2 mobile */ \ - INTEL_VGA_DEVICE(0x0C06, info), /* SDV GT1 mobile */ \ INTEL_VGA_DEVICE(0x0C16, info), /* SDV GT2 mobile */ \ - INTEL_VGA_DEVICE(0x0C26, info), /* SDV GT3 mobile */ \ - INTEL_VGA_DEVICE(0x0A06, info), /* ULT GT1 mobile */ \ INTEL_VGA_DEVICE(0x0A16, info), /* ULT GT2 mobile */ \ - INTEL_VGA_DEVICE(0x0A26, info), /* ULT GT3 mobile */ \ - INTEL_VGA_DEVICE(0x0A0E, info), /* ULX GT1 mobile */ \ INTEL_VGA_DEVICE(0x0A1E, info), /* ULX GT2 mobile */ \ + INTEL_VGA_DEVICE(0x0D16, info) /* CRW GT2 mobile */ + +#define INTEL_HSW_GT3_IDS(info) \ + INTEL_VGA_DEVICE(0x0422, info), /* GT3 desktop */ \ + INTEL_VGA_DEVICE(0x042a, info), /* GT3 server */ \ + INTEL_VGA_DEVICE(0x042B, info), /* GT3 reserved */ \ + INTEL_VGA_DEVICE(0x042E, info), /* GT3 reserved */ \ + INTEL_VGA_DEVICE(0x0C22, info), /* SDV GT3 desktop */ \ + INTEL_VGA_DEVICE(0x0C2A, info), /* SDV GT3 server */ \ + INTEL_VGA_DEVICE(0x0C2B, info), /* SDV GT3 reserved */ \ + INTEL_VGA_DEVICE(0x0C2E, info), /* SDV GT3 reserved */ \ + INTEL_VGA_DEVICE(0x0A22, info), /* ULT GT3 desktop */ \ + INTEL_VGA_DEVICE(0x0A2A, info), /* ULT GT3 server */ \ + INTEL_VGA_DEVICE(0x0A2B, info), /* ULT GT3 reserved */ \ + INTEL_VGA_DEVICE(0x0D22, info), /* CRW GT3 desktop */ \ + INTEL_VGA_DEVICE(0x0D2A, info), /* CRW GT3 server */ \ + INTEL_VGA_DEVICE(0x0D2B, info), /* CRW GT3 reserved */ \ + INTEL_VGA_DEVICE(0x0D2E, info), /* CRW GT3 reserved */ \ + INTEL_VGA_DEVICE(0x0C26, info), /* SDV GT3 mobile */ \ + INTEL_VGA_DEVICE(0x0A26, info), /* ULT GT3 mobile */ \ INTEL_VGA_DEVICE(0x0A2E, info), /* ULT GT3 reserved */ \ - INTEL_VGA_DEVICE(0x0D06, info), /* CRW GT1 mobile */ \ - INTEL_VGA_DEVICE(0x0D16, info), /* CRW GT2 mobile */ \ INTEL_VGA_DEVICE(0x0D26, info) /* CRW GT3 mobile */ +#define INTEL_HSW_IDS(info) \ + INTEL_HSW_GT1_IDS(info), \ + INTEL_HSW_GT2_IDS(info), \ + INTEL_HSW_GT3_IDS(info) + #define INTEL_VLV_IDS(info) \ INTEL_VGA_DEVICE(0x0f30, info), \ INTEL_VGA_DEVICE(0x0f31, info), \ @@ -212,17 +245,19 @@ INTEL_VGA_DEVICE(0x0157, info), \ INTEL_VGA_DEVICE(0x0155, info) -#define INTEL_BDW_GT12_IDS(info) \ +#define INTEL_BDW_GT1_IDS(info) \ INTEL_VGA_DEVICE(0x1602, info), /* GT1 ULT */ \ INTEL_VGA_DEVICE(0x1606, info), /* GT1 ULT */ \ INTEL_VGA_DEVICE(0x160B, info), /* GT1 Iris */ \ INTEL_VGA_DEVICE(0x160E, info), /* GT1 ULX */ \ - INTEL_VGA_DEVICE(0x1612, info), /* GT2 Halo */ \ + INTEL_VGA_DEVICE(0x160A, info), /* GT1 Server */ \ + INTEL_VGA_DEVICE(0x160D, info) /* GT1 Workstation */ + +#define INTEL_BDW_GT2_IDS(info) \ + INTEL_VGA_DEVICE(0x1612, info), /* GT2 Halo */ \ INTEL_VGA_DEVICE(0x1616, info), /* GT2 ULT */ \ INTEL_VGA_DEVICE(0x161B, info), /* GT2 ULT */ \ - INTEL_VGA_DEVICE(0x161E, info), /* GT2 ULX */ \ - INTEL_VGA_DEVICE(0x160A, info), /* GT1 Server */ \ - INTEL_VGA_DEVICE(0x160D, info), /* GT1 Workstation */ \ + INTEL_VGA_DEVICE(0x161E, info), /* GT2 ULX */ \ INTEL_VGA_DEVICE(0x161A, info), /* GT2 Server */ \ INTEL_VGA_DEVICE(0x161D, info) /* GT2 Workstation */ @@ -243,7 +278,8 @@ INTEL_VGA_DEVICE(0x163D, info) /* Workstation */ #define INTEL_BDW_IDS(info) \ - INTEL_BDW_GT12_IDS(info), \ + INTEL_BDW_GT1_IDS(info), \ + INTEL_BDW_GT2_IDS(info), \ INTEL_BDW_GT3_IDS(info), \ INTEL_BDW_RSVD_IDS(info) @@ -335,20 +371,22 @@ INTEL_KBL_GT4_IDS(info) /* CFL S */ -#define INTEL_CFL_S_IDS(info) \ +#define INTEL_CFL_S_GT1_IDS(info) \ INTEL_VGA_DEVICE(0x3E90, info), /* SRV GT1 */ \ - INTEL_VGA_DEVICE(0x3E93, info), /* SRV GT1 */ \ + INTEL_VGA_DEVICE(0x3E93, info) /* SRV GT1 */ + +#define INTEL_CFL_S_GT2_IDS(info) \ INTEL_VGA_DEVICE(0x3E91, info), /* SRV GT2 */ \ INTEL_VGA_DEVICE(0x3E92, info), /* SRV GT2 */ \ INTEL_VGA_DEVICE(0x3E96, info) /* SRV GT2 */ /* CFL H */ -#define INTEL_CFL_H_IDS(info) \ +#define INTEL_CFL_H_GT2_IDS(info) \ INTEL_VGA_DEVICE(0x3E9B, info), /* Halo GT2 */ \ INTEL_VGA_DEVICE(0x3E94, info) /* Halo GT2 */ /* CFL U */ -#define INTEL_CFL_U_IDS(info) \ +#define INTEL_CFL_U_GT3_IDS(info) \ INTEL_VGA_DEVICE(0x3EA6, info), /* ULT GT3 */ \ INTEL_VGA_DEVICE(0x3EA7, info), /* ULT GT3 */ \ INTEL_VGA_DEVICE(0x3EA8, info), /* ULT GT3 */ \ -- cgit v1.2.3-59-g8ed1b From 2093ea2e098b602e0ecefcd30211193b888da5d3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 1 Sep 2017 16:40:41 +0200 Subject: drm/vtables: Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The callback is named .atomic_check, not .atomc_check. Reviewed-by: Ville Syrjälä Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20170901144042.6023-1-thierry.reding@gmail.com --- include/drm/drm_modeset_helper_vtables.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index c55cf3ff6847..16646c44b7df 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -314,7 +314,7 @@ struct drm_crtc_helper_funcs { * implementation in drm_atomic_helper_check(). * * When using drm_atomic_helper_check_planes() this hook is called - * after the &drm_plane_helper_funcs.atomc_check hook for planes, which + * after the &drm_plane_helper_funcs.atomic_check hook for planes, which * allows drivers to assign shared resources requested by planes in this * callback here. For more complicated dependencies the driver can call * the provided check helpers multiple times until the computed state -- cgit v1.2.3-59-g8ed1b From 46ad42a3756d5e2078ed05bc99aa197815afac5d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 1 Sep 2017 16:40:42 +0200 Subject: drm/atomic: Fix typo in kerneldoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The for_each_crtc_in_state() is used to iterate over CRTCs rather than connectors. Reviewed-by: Ville Syrjälä Signed-off-by: Thierry Reding Link: https://patchwork.freedesktop.org/patch/msgid/20170901144042.6023-2-thierry.reding@gmail.com --- include/drm/drm_atomic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 8a5808eb5628..f73b663c1f76 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -643,7 +643,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); for_each_if (connector) /** - * for_each_crtc_in_state - iterate over all connectors in an atomic update + * for_each_crtc_in_state - iterate over all CRTCs in an atomic update * @__state: &struct drm_atomic_state pointer * @crtc: &struct drm_crtc iteration cursor * @crtc_state: &struct drm_crtc_state iteration cursor -- cgit v1.2.3-59-g8ed1b From 97623c0a80a605ef3fae337081ed008796bf8cc2 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 23 Jul 2017 17:26:21 +0100 Subject: iio: drop iio_info.driver_module and iio_trigger_ops.owner. The equivalents are now assigned automatically in the relevant registration calls and so are not needed in these operations structures. Signed-off-by: Jonathan Cameron Reviewed-by: Lars-Peter Clausen --- include/linux/iio/iio.h | 3 --- include/linux/iio/trigger.h | 2 -- 2 files changed, 5 deletions(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 97a014300947..486ffbb1a926 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -369,8 +369,6 @@ struct iio_dev; /** * struct iio_info - constant information about device - * @driver_module: module structure used to ensure correct - * ownership of chrdevs etc * @event_attrs: event control attributes * @attrs: general purpose device attributes * @read_raw: function to request a value from the device. @@ -425,7 +423,6 @@ struct iio_dev; * were flushed and there was an error. **/ struct iio_info { - struct module *driver_module; const struct attribute_group *event_attrs; const struct attribute_group *attrs; diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index 999793212b40..1afe349af1fa 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -23,7 +23,6 @@ struct iio_trigger; /** * struct iio_trigger_ops - operations structure for an iio_trigger. - * @owner: used to monitor usage count of the trigger. * @set_trigger_state: switch on/off the trigger on demand * @try_reenable: function to reenable the trigger when the * use count is zero (may be NULL) @@ -34,7 +33,6 @@ struct iio_trigger; * instances of a given device. **/ struct iio_trigger_ops { - struct module *owner; int (*set_trigger_state)(struct iio_trigger *trig, bool state); int (*try_reenable)(struct iio_trigger *trig); int (*validate_device)(struct iio_trigger *trig, -- cgit v1.2.3-59-g8ed1b From 6b8ed8720011f703bd9be63c905b592666e1bf62 Mon Sep 17 00:00:00 2001 From: Gabriel Krisman Bertazi Date: Thu, 31 Aug 2017 16:52:14 -0300 Subject: drm: Fix example comment of format modifier blob To represent formats 98-102, the supported formats mask must be 0x7c00000000 and not 0x3c00000000. Signed-off-by: Gabriel Krisman Bertazi Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20170831195215.13302-1-krisman@collabora.co.uk --- include/uapi/drm/drm_mode.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index a2bb7161f020..e040c952accc 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -749,9 +749,9 @@ struct drm_format_modifier { * If the number formats grew to 128, and formats 98-102 are * supported with the modifier: * - * 0x0000003c00000000 0000000000000000 + * 0x0000007c00000000 0000000000000000 * ^ - * |__offset = 64, formats = 0x3c00000000 + * |__offset = 64, formats = 0x7c00000000 * */ __u64 formats; -- cgit v1.2.3-59-g8ed1b From 8e4f7d9b8c652b4eb3ceb790e1e9fe433f9e3b58 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 2 Sep 2017 21:54:03 +0200 Subject: ALSA: ac97: split out the generic ac97 registers Split out from the ac97_codec.h the ac97 generic registers, which can be used by a codec, typically a generic ac97 codec, and by the ac97 bus, to scan an ac97 AC-Link. This split encompasses all the AC97 standard registers, but not the codec specific ones. In order to have a clean split between former ac97 bus implementation and the new coming one in sound/ac97, it is safer to not include any former ac97 includes, excepting in sound/ac97/compat.c. Amongst the thing to isolate : - don't have the struct snd_ac97 in sound/ac97/* (except compat.c) to not be "fooled" by a definition which would come with ac97_codec.h by "chance". - don't have to have snd_a97_*() functions, as they rely on struct snd_ac97. - don't want the struct snd_ac97_bus_ops, there is a new one Signed-off-by: Robert Jarzmik Reviewed-by: Takashi Iwai Signed-off-by: Mark Brown --- include/sound/ac97/regs.h | 262 +++++++++++++++++++++++++++++++++++++++++++++ include/sound/ac97_codec.h | 239 +---------------------------------------- 2 files changed, 263 insertions(+), 238 deletions(-) create mode 100644 include/sound/ac97/regs.h (limited to 'include') diff --git a/include/sound/ac97/regs.h b/include/sound/ac97/regs.h new file mode 100644 index 000000000000..4bb86d379bd5 --- /dev/null +++ b/include/sound/ac97/regs.h @@ -0,0 +1,262 @@ +/* + * Copyright (c) by Jaroslav Kysela + * Universal interface for Audio Codec '97 + * + * For more details look to AC '97 component specification revision 2.1 + * by Intel Corporation (http://developer.intel.com). + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * AC'97 codec registers + */ + +#define AC97_RESET 0x00 /* Reset */ +#define AC97_MASTER 0x02 /* Master Volume */ +#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ +#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ +#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ +#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ +#define AC97_PHONE 0x0c /* Phone Volume (optional) */ +#define AC97_MIC 0x0e /* MIC Volume */ +#define AC97_LINE 0x10 /* Line In Volume */ +#define AC97_CD 0x12 /* CD Volume */ +#define AC97_VIDEO 0x14 /* Video Volume (optional) */ +#define AC97_AUX 0x16 /* AUX Volume (optional) */ +#define AC97_PCM 0x18 /* PCM Volume */ +#define AC97_REC_SEL 0x1a /* Record Select */ +#define AC97_REC_GAIN 0x1c /* Record Gain */ +#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ +#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ +#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ +#define AC97_INT_PAGING 0x24 /* Audio Interrupt & Paging (AC'97 2.3) */ +#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ +/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ +#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */ +#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ +#define AC97_SPDIF 0x3a /* S/PDIF control */ +/* range 0x3c-0x58 - MODEM */ +#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */ +#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */ +#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */ +#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */ +#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */ +#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */ +#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */ +#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */ +#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */ +#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */ +#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */ +#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */ +#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */ +#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */ +/* range 0x5a-0x7b - Vendor Specific */ +#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ +#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ +/* range 0x60-0x6f (page 1) - extended codec registers */ +#define AC97_CODEC_CLASS_REV 0x60 /* Codec Class/Revision */ +#define AC97_PCI_SVID 0x62 /* PCI Subsystem Vendor ID */ +#define AC97_PCI_SID 0x64 /* PCI Subsystem ID */ +#define AC97_FUNC_SELECT 0x66 /* Function Select */ +#define AC97_FUNC_INFO 0x68 /* Function Information */ +#define AC97_SENSE_INFO 0x6a /* Sense Details */ + +/* volume controls */ +#define AC97_MUTE_MASK_MONO 0x8000 +#define AC97_MUTE_MASK_STEREO 0x8080 + +/* slot allocation */ +#define AC97_SLOT_TAG 0 +#define AC97_SLOT_CMD_ADDR 1 +#define AC97_SLOT_CMD_DATA 2 +#define AC97_SLOT_PCM_LEFT 3 +#define AC97_SLOT_PCM_RIGHT 4 +#define AC97_SLOT_MODEM_LINE1 5 +#define AC97_SLOT_PCM_CENTER 6 +#define AC97_SLOT_MIC 6 /* input */ +#define AC97_SLOT_SPDIF_LEFT1 6 +#define AC97_SLOT_PCM_SLEFT 7 /* surround left */ +#define AC97_SLOT_PCM_LEFT_0 7 /* double rate operation */ +#define AC97_SLOT_SPDIF_LEFT 7 +#define AC97_SLOT_PCM_SRIGHT 8 /* surround right */ +#define AC97_SLOT_PCM_RIGHT_0 8 /* double rate operation */ +#define AC97_SLOT_SPDIF_RIGHT 8 +#define AC97_SLOT_LFE 9 +#define AC97_SLOT_SPDIF_RIGHT1 9 +#define AC97_SLOT_MODEM_LINE2 10 +#define AC97_SLOT_PCM_LEFT_1 10 /* double rate operation */ +#define AC97_SLOT_SPDIF_LEFT2 10 +#define AC97_SLOT_HANDSET 11 /* output */ +#define AC97_SLOT_PCM_RIGHT_1 11 /* double rate operation */ +#define AC97_SLOT_SPDIF_RIGHT2 11 +#define AC97_SLOT_MODEM_GPIO 12 /* modem GPIO */ +#define AC97_SLOT_PCM_CENTER_1 12 /* double rate operation */ + +/* basic capabilities (reset register) */ +#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */ +#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */ +#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */ +#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */ +#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */ +#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */ +#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */ +#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */ +#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */ +#define AC97_BC_DAC_MASK 0x00c0 +#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */ +#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ +#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ +#define AC97_BC_ADC_MASK 0x0300 +#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */ + +/* general purpose */ +#define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */ +#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */ +#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */ + +/* powerdown bits */ +#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */ +#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */ +#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */ +#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */ +#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */ +#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */ +#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */ +#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */ +#define AC97_PD_PR4 0x1000 /* Power down AC-Link */ +#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */ +#define AC97_PD_PR6 0x4000 /* Headphone amplifier */ +#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */ + +/* extended audio ID bit defines */ +#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ +#define AC97_EI_DRA 0x0002 /* Double rate supported */ +#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */ +#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */ +#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */ +#define AC97_EI_DACS_SLOT_SHIFT 4 +#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */ +#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */ +#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */ +#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */ +#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */ +#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */ +#define AC97_EI_REV_23 0x0800 /* AC'97 revision 2.3 */ +#define AC97_EI_REV_SHIFT 10 +#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */ +#define AC97_EI_ADDR_SHIFT 14 + +/* extended audio status and control bit defines */ +#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ +#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ +#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */ +#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ +#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */ +#define AC97_EA_SPSA_SLOT_SHIFT 4 +#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ +#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ +#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ +#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ +#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ +#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */ +#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */ +#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */ +#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ +#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ +#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ +#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ +#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ + +/* S/PDIF control bit defines */ +#define AC97_SC_PRO 0x0001 /* Professional status */ +#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ +#define AC97_SC_COPY 0x0004 /* Copyright status */ +#define AC97_SC_PRE 0x0008 /* Preemphasis status */ +#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ +#define AC97_SC_CC_SHIFT 4 +#define AC97_SC_L 0x0800 /* Generation Level status */ +#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */ +#define AC97_SC_SPSR_SHIFT 12 +#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ +#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ +#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ +#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ +#define AC97_SC_V 0x8000 /* Validity status */ + +/* Interrupt and Paging bit defines (AC'97 2.3) */ +#define AC97_PAGE_MASK 0x000f /* Page Selector */ +#define AC97_PAGE_VENDOR 0 /* Vendor-specific registers */ +#define AC97_PAGE_1 1 /* Extended Codec Registers page 1 */ +#define AC97_INT_ENABLE 0x0800 /* Interrupt Enable */ +#define AC97_INT_SENSE 0x1000 /* Sense Cycle */ +#define AC97_INT_CAUSE_SENSE 0x2000 /* Sense Cycle Completed (RO) */ +#define AC97_INT_CAUSE_GPIO 0x4000 /* GPIO bits changed (RO) */ +#define AC97_INT_STATUS 0x8000 /* Interrupt Status */ + +/* extended modem ID bit defines */ +#define AC97_MEI_LINE1 0x0001 /* Line1 present */ +#define AC97_MEI_LINE2 0x0002 /* Line2 present */ +#define AC97_MEI_HANDSET 0x0004 /* Handset present */ +#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */ +#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */ +#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */ +#define AC97_MEI_ADDR_SHIFT 14 + +/* extended modem status and control bit defines */ +#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */ +#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */ +#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */ +#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */ +#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */ +#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */ +#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */ +#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */ +#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */ +#define AC97_MEA_PRB 0x0200 /* reserved */ +#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */ +#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */ +#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */ +#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */ +#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */ +#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */ + +/* modem gpio status defines */ +#define AC97_GPIO_LINE1_OH 0x0001 /* Off Hook Line1 */ +#define AC97_GPIO_LINE1_RI 0x0002 /* Ring Detect Line1 */ +#define AC97_GPIO_LINE1_CID 0x0004 /* Caller ID path enable Line1 */ +#define AC97_GPIO_LINE1_LCS 0x0008 /* Loop Current Sense Line1 */ +#define AC97_GPIO_LINE1_PULSE 0x0010 /* Opt./ Pulse Dial Line1 (out) */ +#define AC97_GPIO_LINE1_HL1R 0x0020 /* Opt./ Handset to Line1 relay control (out) */ +#define AC97_GPIO_LINE1_HOHD 0x0040 /* Opt./ Handset off hook detect Line1 (in) */ +#define AC97_GPIO_LINE12_AC 0x0080 /* Opt./ Int.bit 1 / Line1/2 AC (out) */ +#define AC97_GPIO_LINE12_DC 0x0100 /* Opt./ Int.bit 2 / Line1/2 DC (out) */ +#define AC97_GPIO_LINE12_RS 0x0200 /* Opt./ Int.bit 3 / Line1/2 RS (out) */ +#define AC97_GPIO_LINE2_OH 0x0400 /* Off Hook Line2 */ +#define AC97_GPIO_LINE2_RI 0x0800 /* Ring Detect Line2 */ +#define AC97_GPIO_LINE2_CID 0x1000 /* Caller ID path enable Line2 */ +#define AC97_GPIO_LINE2_LCS 0x2000 /* Loop Current Sense Line2 */ +#define AC97_GPIO_LINE2_PULSE 0x4000 /* Opt./ Pulse Dial Line2 (out) */ +#define AC97_GPIO_LINE2_HL1R 0x8000 /* Opt./ Handset to Line2 relay control (out) */ + diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index 15aa5f07c955..89d311a503d3 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -35,244 +36,6 @@ /* maximum number of devices on the AC97 bus */ #define AC97_BUS_MAX_DEVICES 4 -/* - * AC'97 codec registers - */ - -#define AC97_RESET 0x00 /* Reset */ -#define AC97_MASTER 0x02 /* Master Volume */ -#define AC97_HEADPHONE 0x04 /* Headphone Volume (optional) */ -#define AC97_MASTER_MONO 0x06 /* Master Volume Mono (optional) */ -#define AC97_MASTER_TONE 0x08 /* Master Tone (Bass & Treble) (optional) */ -#define AC97_PC_BEEP 0x0a /* PC Beep Volume (optinal) */ -#define AC97_PHONE 0x0c /* Phone Volume (optional) */ -#define AC97_MIC 0x0e /* MIC Volume */ -#define AC97_LINE 0x10 /* Line In Volume */ -#define AC97_CD 0x12 /* CD Volume */ -#define AC97_VIDEO 0x14 /* Video Volume (optional) */ -#define AC97_AUX 0x16 /* AUX Volume (optional) */ -#define AC97_PCM 0x18 /* PCM Volume */ -#define AC97_REC_SEL 0x1a /* Record Select */ -#define AC97_REC_GAIN 0x1c /* Record Gain */ -#define AC97_REC_GAIN_MIC 0x1e /* Record Gain MIC (optional) */ -#define AC97_GENERAL_PURPOSE 0x20 /* General Purpose (optional) */ -#define AC97_3D_CONTROL 0x22 /* 3D Control (optional) */ -#define AC97_INT_PAGING 0x24 /* Audio Interrupt & Paging (AC'97 2.3) */ -#define AC97_POWERDOWN 0x26 /* Powerdown control / status */ -/* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */ -#define AC97_EXTENDED_ID 0x28 /* Extended Audio ID */ -#define AC97_EXTENDED_STATUS 0x2a /* Extended Audio Status and Control */ -#define AC97_PCM_FRONT_DAC_RATE 0x2c /* PCM Front DAC Rate */ -#define AC97_PCM_SURR_DAC_RATE 0x2e /* PCM Surround DAC Rate */ -#define AC97_PCM_LFE_DAC_RATE 0x30 /* PCM LFE DAC Rate */ -#define AC97_PCM_LR_ADC_RATE 0x32 /* PCM LR ADC Rate */ -#define AC97_PCM_MIC_ADC_RATE 0x34 /* PCM MIC ADC Rate */ -#define AC97_CENTER_LFE_MASTER 0x36 /* Center + LFE Master Volume */ -#define AC97_SURROUND_MASTER 0x38 /* Surround (Rear) Master Volume */ -#define AC97_SPDIF 0x3a /* S/PDIF control */ -/* range 0x3c-0x58 - MODEM */ -#define AC97_EXTENDED_MID 0x3c /* Extended Modem ID */ -#define AC97_EXTENDED_MSTATUS 0x3e /* Extended Modem Status and Control */ -#define AC97_LINE1_RATE 0x40 /* Line1 DAC/ADC Rate */ -#define AC97_LINE2_RATE 0x42 /* Line2 DAC/ADC Rate */ -#define AC97_HANDSET_RATE 0x44 /* Handset DAC/ADC Rate */ -#define AC97_LINE1_LEVEL 0x46 /* Line1 DAC/ADC Level */ -#define AC97_LINE2_LEVEL 0x48 /* Line2 DAC/ADC Level */ -#define AC97_HANDSET_LEVEL 0x4a /* Handset DAC/ADC Level */ -#define AC97_GPIO_CFG 0x4c /* GPIO Configuration */ -#define AC97_GPIO_POLARITY 0x4e /* GPIO Pin Polarity/Type, 0=low, 1=high active */ -#define AC97_GPIO_STICKY 0x50 /* GPIO Pin Sticky, 0=not, 1=sticky */ -#define AC97_GPIO_WAKEUP 0x52 /* GPIO Pin Wakeup, 0=no int, 1=yes int */ -#define AC97_GPIO_STATUS 0x54 /* GPIO Pin Status, slot 12 */ -#define AC97_MISC_AFE 0x56 /* Miscellaneous Modem AFE Status and Control */ -/* range 0x5a-0x7b - Vendor Specific */ -#define AC97_VENDOR_ID1 0x7c /* Vendor ID1 */ -#define AC97_VENDOR_ID2 0x7e /* Vendor ID2 / revision */ -/* range 0x60-0x6f (page 1) - extended codec registers */ -#define AC97_CODEC_CLASS_REV 0x60 /* Codec Class/Revision */ -#define AC97_PCI_SVID 0x62 /* PCI Subsystem Vendor ID */ -#define AC97_PCI_SID 0x64 /* PCI Subsystem ID */ -#define AC97_FUNC_SELECT 0x66 /* Function Select */ -#define AC97_FUNC_INFO 0x68 /* Function Information */ -#define AC97_SENSE_INFO 0x6a /* Sense Details */ - -/* volume controls */ -#define AC97_MUTE_MASK_MONO 0x8000 -#define AC97_MUTE_MASK_STEREO 0x8080 - -/* slot allocation */ -#define AC97_SLOT_TAG 0 -#define AC97_SLOT_CMD_ADDR 1 -#define AC97_SLOT_CMD_DATA 2 -#define AC97_SLOT_PCM_LEFT 3 -#define AC97_SLOT_PCM_RIGHT 4 -#define AC97_SLOT_MODEM_LINE1 5 -#define AC97_SLOT_PCM_CENTER 6 -#define AC97_SLOT_MIC 6 /* input */ -#define AC97_SLOT_SPDIF_LEFT1 6 -#define AC97_SLOT_PCM_SLEFT 7 /* surround left */ -#define AC97_SLOT_PCM_LEFT_0 7 /* double rate operation */ -#define AC97_SLOT_SPDIF_LEFT 7 -#define AC97_SLOT_PCM_SRIGHT 8 /* surround right */ -#define AC97_SLOT_PCM_RIGHT_0 8 /* double rate operation */ -#define AC97_SLOT_SPDIF_RIGHT 8 -#define AC97_SLOT_LFE 9 -#define AC97_SLOT_SPDIF_RIGHT1 9 -#define AC97_SLOT_MODEM_LINE2 10 -#define AC97_SLOT_PCM_LEFT_1 10 /* double rate operation */ -#define AC97_SLOT_SPDIF_LEFT2 10 -#define AC97_SLOT_HANDSET 11 /* output */ -#define AC97_SLOT_PCM_RIGHT_1 11 /* double rate operation */ -#define AC97_SLOT_SPDIF_RIGHT2 11 -#define AC97_SLOT_MODEM_GPIO 12 /* modem GPIO */ -#define AC97_SLOT_PCM_CENTER_1 12 /* double rate operation */ - -/* basic capabilities (reset register) */ -#define AC97_BC_DEDICATED_MIC 0x0001 /* Dedicated Mic PCM In Channel */ -#define AC97_BC_RESERVED1 0x0002 /* Reserved (was Modem Line Codec support) */ -#define AC97_BC_BASS_TREBLE 0x0004 /* Bass & Treble Control */ -#define AC97_BC_SIM_STEREO 0x0008 /* Simulated stereo */ -#define AC97_BC_HEADPHONE 0x0010 /* Headphone Out Support */ -#define AC97_BC_LOUDNESS 0x0020 /* Loudness (bass boost) Support */ -#define AC97_BC_16BIT_DAC 0x0000 /* 16-bit DAC resolution */ -#define AC97_BC_18BIT_DAC 0x0040 /* 18-bit DAC resolution */ -#define AC97_BC_20BIT_DAC 0x0080 /* 20-bit DAC resolution */ -#define AC97_BC_DAC_MASK 0x00c0 -#define AC97_BC_16BIT_ADC 0x0000 /* 16-bit ADC resolution */ -#define AC97_BC_18BIT_ADC 0x0100 /* 18-bit ADC resolution */ -#define AC97_BC_20BIT_ADC 0x0200 /* 20-bit ADC resolution */ -#define AC97_BC_ADC_MASK 0x0300 -#define AC97_BC_3D_TECH_ID_MASK 0x7c00 /* Per-vendor ID of 3D enhancement */ - -/* general purpose */ -#define AC97_GP_DRSS_MASK 0x0c00 /* double rate slot select */ -#define AC97_GP_DRSS_1011 0x0000 /* LR(C) 10+11(+12) */ -#define AC97_GP_DRSS_78 0x0400 /* LR 7+8 */ - -/* powerdown bits */ -#define AC97_PD_ADC_STATUS 0x0001 /* ADC status (RO) */ -#define AC97_PD_DAC_STATUS 0x0002 /* DAC status (RO) */ -#define AC97_PD_MIXER_STATUS 0x0004 /* Analog mixer status (RO) */ -#define AC97_PD_VREF_STATUS 0x0008 /* Vref status (RO) */ -#define AC97_PD_PR0 0x0100 /* Power down PCM ADCs and input MUX */ -#define AC97_PD_PR1 0x0200 /* Power down PCM front DAC */ -#define AC97_PD_PR2 0x0400 /* Power down Mixer (Vref still on) */ -#define AC97_PD_PR3 0x0800 /* Power down Mixer (Vref off) */ -#define AC97_PD_PR4 0x1000 /* Power down AC-Link */ -#define AC97_PD_PR5 0x2000 /* Disable internal clock usage */ -#define AC97_PD_PR6 0x4000 /* Headphone amplifier */ -#define AC97_PD_EAPD 0x8000 /* External Amplifer Power Down (EAPD) */ - -/* extended audio ID bit defines */ -#define AC97_EI_VRA 0x0001 /* Variable bit rate supported */ -#define AC97_EI_DRA 0x0002 /* Double rate supported */ -#define AC97_EI_SPDIF 0x0004 /* S/PDIF out supported */ -#define AC97_EI_VRM 0x0008 /* Variable bit rate supported for MIC */ -#define AC97_EI_DACS_SLOT_MASK 0x0030 /* DACs slot assignment */ -#define AC97_EI_DACS_SLOT_SHIFT 4 -#define AC97_EI_CDAC 0x0040 /* PCM Center DAC available */ -#define AC97_EI_SDAC 0x0080 /* PCM Surround DACs available */ -#define AC97_EI_LDAC 0x0100 /* PCM LFE DAC available */ -#define AC97_EI_AMAP 0x0200 /* indicates optional slot/DAC mapping based on codec ID */ -#define AC97_EI_REV_MASK 0x0c00 /* AC'97 revision mask */ -#define AC97_EI_REV_22 0x0400 /* AC'97 revision 2.2 */ -#define AC97_EI_REV_23 0x0800 /* AC'97 revision 2.3 */ -#define AC97_EI_REV_SHIFT 10 -#define AC97_EI_ADDR_MASK 0xc000 /* physical codec ID (address) */ -#define AC97_EI_ADDR_SHIFT 14 - -/* extended audio status and control bit defines */ -#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ -#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ -#define AC97_EA_SPDIF 0x0004 /* S/PDIF out enable bit */ -#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ -#define AC97_EA_SPSA_SLOT_MASK 0x0030 /* Mask for slot assignment bits */ -#define AC97_EA_SPSA_SLOT_SHIFT 4 -#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ -#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ -#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ -#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ -#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ -#define AC97_EA_SDAC 0x0080 /* PCM Surround DACs are ready (Read only) */ -#define AC97_EA_LDAC 0x0100 /* PCM LFE DAC is ready (Read only) */ -#define AC97_EA_MDAC 0x0200 /* MIC ADC is ready (Read only) */ -#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ -#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ -#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ -#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ -#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ - -/* S/PDIF control bit defines */ -#define AC97_SC_PRO 0x0001 /* Professional status */ -#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ -#define AC97_SC_COPY 0x0004 /* Copyright status */ -#define AC97_SC_PRE 0x0008 /* Preemphasis status */ -#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ -#define AC97_SC_CC_SHIFT 4 -#define AC97_SC_L 0x0800 /* Generation Level status */ -#define AC97_SC_SPSR_MASK 0x3000 /* S/PDIF Sample Rate bits */ -#define AC97_SC_SPSR_SHIFT 12 -#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ -#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ -#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ -#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ -#define AC97_SC_V 0x8000 /* Validity status */ - -/* Interrupt and Paging bit defines (AC'97 2.3) */ -#define AC97_PAGE_MASK 0x000f /* Page Selector */ -#define AC97_PAGE_VENDOR 0 /* Vendor-specific registers */ -#define AC97_PAGE_1 1 /* Extended Codec Registers page 1 */ -#define AC97_INT_ENABLE 0x0800 /* Interrupt Enable */ -#define AC97_INT_SENSE 0x1000 /* Sense Cycle */ -#define AC97_INT_CAUSE_SENSE 0x2000 /* Sense Cycle Completed (RO) */ -#define AC97_INT_CAUSE_GPIO 0x4000 /* GPIO bits changed (RO) */ -#define AC97_INT_STATUS 0x8000 /* Interrupt Status */ - -/* extended modem ID bit defines */ -#define AC97_MEI_LINE1 0x0001 /* Line1 present */ -#define AC97_MEI_LINE2 0x0002 /* Line2 present */ -#define AC97_MEI_HANDSET 0x0004 /* Handset present */ -#define AC97_MEI_CID1 0x0008 /* caller ID decode for Line1 is supported */ -#define AC97_MEI_CID2 0x0010 /* caller ID decode for Line2 is supported */ -#define AC97_MEI_ADDR_MASK 0xc000 /* physical codec ID (address) */ -#define AC97_MEI_ADDR_SHIFT 14 - -/* extended modem status and control bit defines */ -#define AC97_MEA_GPIO 0x0001 /* GPIO is ready (ro) */ -#define AC97_MEA_MREF 0x0002 /* Vref is up to nominal level (ro) */ -#define AC97_MEA_ADC1 0x0004 /* ADC1 operational (ro) */ -#define AC97_MEA_DAC1 0x0008 /* DAC1 operational (ro) */ -#define AC97_MEA_ADC2 0x0010 /* ADC2 operational (ro) */ -#define AC97_MEA_DAC2 0x0020 /* DAC2 operational (ro) */ -#define AC97_MEA_HADC 0x0040 /* HADC operational (ro) */ -#define AC97_MEA_HDAC 0x0080 /* HDAC operational (ro) */ -#define AC97_MEA_PRA 0x0100 /* GPIO power down (high) */ -#define AC97_MEA_PRB 0x0200 /* reserved */ -#define AC97_MEA_PRC 0x0400 /* ADC1 power down (high) */ -#define AC97_MEA_PRD 0x0800 /* DAC1 power down (high) */ -#define AC97_MEA_PRE 0x1000 /* ADC2 power down (high) */ -#define AC97_MEA_PRF 0x2000 /* DAC2 power down (high) */ -#define AC97_MEA_PRG 0x4000 /* HADC power down (high) */ -#define AC97_MEA_PRH 0x8000 /* HDAC power down (high) */ - -/* modem gpio status defines */ -#define AC97_GPIO_LINE1_OH 0x0001 /* Off Hook Line1 */ -#define AC97_GPIO_LINE1_RI 0x0002 /* Ring Detect Line1 */ -#define AC97_GPIO_LINE1_CID 0x0004 /* Caller ID path enable Line1 */ -#define AC97_GPIO_LINE1_LCS 0x0008 /* Loop Current Sense Line1 */ -#define AC97_GPIO_LINE1_PULSE 0x0010 /* Opt./ Pulse Dial Line1 (out) */ -#define AC97_GPIO_LINE1_HL1R 0x0020 /* Opt./ Handset to Line1 relay control (out) */ -#define AC97_GPIO_LINE1_HOHD 0x0040 /* Opt./ Handset off hook detect Line1 (in) */ -#define AC97_GPIO_LINE12_AC 0x0080 /* Opt./ Int.bit 1 / Line1/2 AC (out) */ -#define AC97_GPIO_LINE12_DC 0x0100 /* Opt./ Int.bit 2 / Line1/2 DC (out) */ -#define AC97_GPIO_LINE12_RS 0x0200 /* Opt./ Int.bit 3 / Line1/2 RS (out) */ -#define AC97_GPIO_LINE2_OH 0x0400 /* Off Hook Line2 */ -#define AC97_GPIO_LINE2_RI 0x0800 /* Ring Detect Line2 */ -#define AC97_GPIO_LINE2_CID 0x1000 /* Caller ID path enable Line2 */ -#define AC97_GPIO_LINE2_LCS 0x2000 /* Loop Current Sense Line2 */ -#define AC97_GPIO_LINE2_PULSE 0x4000 /* Opt./ Pulse Dial Line2 (out) */ -#define AC97_GPIO_LINE2_HL1R 0x8000 /* Opt./ Handset to Line2 relay control (out) */ - /* specific - SigmaTel */ #define AC97_SIGMATEL_OUTSEL 0x64 /* Output Select, STAC9758 */ #define AC97_SIGMATEL_INSEL 0x66 /* Input Select, STAC9758 */ -- cgit v1.2.3-59-g8ed1b From 74426fbff66eea8e8d1f42c8238c268d1e63a832 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 2 Sep 2017 21:54:04 +0200 Subject: ALSA: ac97: add an ac97 bus AC97 is a bus for sound usage. It enables for a AC97 AC-Link to link one controller to 0 to 4 AC97 codecs. The goal of this new implementation is to implement a device/driver model for AC97, with an automatic scan of the bus and automatic discovery of AC97 codec devices. Signed-off-by: Robert Jarzmik Reviewed-by: Takashi Iwai Signed-off-by: Mark Brown --- include/sound/ac97/codec.h | 118 +++++++++ include/sound/ac97/compat.h | 20 ++ include/sound/ac97/controller.h | 85 +++++++ sound/ac97/Kconfig | 19 ++ sound/ac97/Makefile | 8 + sound/ac97/ac97_core.h | 16 ++ sound/ac97/bus.c | 539 ++++++++++++++++++++++++++++++++++++++++ sound/ac97/codec.c | 15 ++ sound/ac97/snd_ac97_compat.c | 108 ++++++++ 9 files changed, 928 insertions(+) create mode 100644 include/sound/ac97/codec.h create mode 100644 include/sound/ac97/compat.h create mode 100644 include/sound/ac97/controller.h create mode 100644 sound/ac97/Kconfig create mode 100644 sound/ac97/Makefile create mode 100644 sound/ac97/ac97_core.h create mode 100644 sound/ac97/bus.c create mode 100644 sound/ac97/codec.c create mode 100644 sound/ac97/snd_ac97_compat.c (limited to 'include') diff --git a/include/sound/ac97/codec.h b/include/sound/ac97/codec.h new file mode 100644 index 000000000000..ec04be9ab119 --- /dev/null +++ b/include/sound/ac97/codec.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 Robert Jarzmik + * + * 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. + */ +#ifndef __SOUND_AC97_CODEC2_H +#define __SOUND_AC97_CODEC2_H + +#include + +#define AC97_ID(vendor_id1, vendor_id2) \ + ((((vendor_id1) & 0xffff) << 16) | ((vendor_id2) & 0xffff)) +#define AC97_DRIVER_ID(vendor_id1, vendor_id2, mask_id1, mask_id2, _data) \ + { .id = (((vendor_id1) & 0xffff) << 16) | ((vendor_id2) & 0xffff), \ + .mask = (((mask_id1) & 0xffff) << 16) | ((mask_id2) & 0xffff), \ + .data = (_data) } + +struct ac97_controller; +struct clk; + +/** + * struct ac97_id - matches a codec device and driver on an ac97 bus + * @id: The significant bits if the codec vendor ID1 and ID2 + * @mask: Bitmask specifying which bits of the id field are significant when + * matching. A driver binds to a device when : + * ((vendorID1 << 8 | vendorID2) & (mask_id1 << 8 | mask_id2)) == id. + * @data: Private data used by the driver. + */ +struct ac97_id { + unsigned int id; + unsigned int mask; + void *data; +}; + +/** + * ac97_codec_device - a ac97 codec + * @dev: the core device + * @vendor_id: the vendor_id of the codec, as sensed on the AC-link + * @num: the codec number, 0 is primary, 1 is first slave, etc ... + * @clk: the clock BIT_CLK provided by the codec + * @ac97_ctrl: ac97 digital controller on the same AC-link + * + * This is the device instantiated for each codec living on a AC-link. There are + * normally 0 to 4 codec devices per AC-link, and all of them are controlled by + * an AC97 digital controller. + */ +struct ac97_codec_device { + struct device dev; + unsigned int vendor_id; + unsigned int num; + struct clk *clk; + struct ac97_controller *ac97_ctrl; +}; + +/** + * ac97_codec_driver - a ac97 codec driver + * @driver: the device driver structure + * @probe: the function called when a ac97_codec_device is matched + * @remove: the function called when the device is unbound/removed + * @shutdown: shutdown function (might be NULL) + * @id_table: ac97 vendor_id match table, { } member terminated + */ +struct ac97_codec_driver { + struct device_driver driver; + int (*probe)(struct ac97_codec_device *); + int (*remove)(struct ac97_codec_device *); + void (*shutdown)(struct ac97_codec_device *); + const struct ac97_id *id_table; +}; + +static inline struct ac97_codec_device *to_ac97_device(struct device *d) +{ + return container_of(d, struct ac97_codec_device, dev); +} + +static inline struct ac97_codec_driver *to_ac97_driver(struct device_driver *d) +{ + return container_of(d, struct ac97_codec_driver, driver); +} + +#if IS_ENABLED(CONFIG_AC97_BUS_NEW) +int snd_ac97_codec_driver_register(struct ac97_codec_driver *drv); +void snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv); +#else +static inline int +snd_ac97_codec_driver_register(struct ac97_codec_driver *drv) +{ + return 0; +} +static inline void +snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv) +{ +} +#endif + + +static inline struct device * +ac97_codec_dev2dev(struct ac97_codec_device *adev) +{ + return &adev->dev; +} + +static inline void *ac97_get_drvdata(struct ac97_codec_device *adev) +{ + return dev_get_drvdata(ac97_codec_dev2dev(adev)); +} + +static inline void ac97_set_drvdata(struct ac97_codec_device *adev, + void *data) +{ + dev_set_drvdata(ac97_codec_dev2dev(adev), data); +} + +void *snd_ac97_codec_get_platdata(const struct ac97_codec_device *adev); + +#endif diff --git a/include/sound/ac97/compat.h b/include/sound/ac97/compat.h new file mode 100644 index 000000000000..1351cba40048 --- /dev/null +++ b/include/sound/ac97/compat.h @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 Robert Jarzmik + * + * 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 file is for backward compatibility with snd_ac97 structure and its + * multiple usages, such as the snd_ac97_bus and snd_ac97_build_ops. + * + */ +#ifndef AC97_COMPAT_H +#define AC97_COMPAT_H + +#include + +struct snd_ac97 *snd_ac97_compat_alloc(struct ac97_codec_device *adev); +void snd_ac97_compat_release(struct snd_ac97 *ac97); + +#endif diff --git a/include/sound/ac97/controller.h b/include/sound/ac97/controller.h new file mode 100644 index 000000000000..b36ecdd64f14 --- /dev/null +++ b/include/sound/ac97/controller.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 Robert Jarzmik + * + * 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. + */ +#ifndef AC97_CONTROLLER_H +#define AC97_CONTROLLER_H + +#include +#include + +#define AC97_BUS_MAX_CODECS 4 +#define AC97_SLOTS_AVAILABLE_ALL 0xf + +struct ac97_controller_ops; + +/** + * struct ac97_controller - The AC97 controller of the AC-Link + * @ops: the AC97 operations. + * @controllers: linked list of all existing controllers. + * @adap: the shell device ac97-%d, ie. ac97 adapter + * @nr: the number of the shell device + * @slots_available: the mask of accessible/scanable codecs. + * @parent: the device providing the AC97 controller. + * @codecs: the 4 possible AC97 codecs (NULL if none found). + * @codecs_pdata: platform_data for each codec (NULL if no pdata). + * + * This structure is internal to AC97 bus, and should not be used by the + * controllers themselves, excepting for using @dev. + */ +struct ac97_controller { + const struct ac97_controller_ops *ops; + struct list_head controllers; + struct device adap; + int nr; + unsigned short slots_available; + struct device *parent; + struct ac97_codec_device *codecs[AC97_BUS_MAX_CODECS]; + void *codecs_pdata[AC97_BUS_MAX_CODECS]; +}; + +/** + * struct ac97_controller_ops - The AC97 operations + * @reset: Cold reset of the AC97 AC-Link. + * @warm_reset: Warm reset of the AC97 AC-Link. + * @read: Read of a single AC97 register. + * Returns the register value or a negative error code. + * @write: Write of a single AC97 register. + * + * These are the basic operation an AC97 controller must provide for an AC97 + * access functions. Amongst these, all but the last 2 are mandatory. + * The slot number is also known as the AC97 codec number, between 0 and 3. + */ +struct ac97_controller_ops { + void (*reset)(struct ac97_controller *adrv); + void (*warm_reset)(struct ac97_controller *adrv); + int (*write)(struct ac97_controller *adrv, int slot, + unsigned short reg, unsigned short val); + int (*read)(struct ac97_controller *adrv, int slot, unsigned short reg); +}; + +#if IS_ENABLED(CONFIG_AC97_BUS_NEW) +struct ac97_controller *snd_ac97_controller_register( + const struct ac97_controller_ops *ops, struct device *dev, + unsigned short slots_available, void **codecs_pdata); +void snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl); +#else +static inline struct ac97_controller * +snd_ac97_controller_register(const struct ac97_controller_ops *ops, + struct device *dev, + unsigned short slots_available, + void **codecs_pdata) +{ + return ERR_PTR(-ENODEV); +} + +static inline void +snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl) +{ +} +#endif + +#endif diff --git a/sound/ac97/Kconfig b/sound/ac97/Kconfig new file mode 100644 index 000000000000..f8a64e15e5bf --- /dev/null +++ b/sound/ac97/Kconfig @@ -0,0 +1,19 @@ +# +# AC97 configuration +# + + +config AC97_BUS_NEW + tristate + select AC97 + help + This is the new AC97 bus type, successor of AC97_BUS. The ported + drivers which benefit from the AC97 automatic probing should "select" + this instead of the AC97_BUS. + Say Y here if you want to have AC97 devices, which are sound oriented + devices around an AC-Link. + +config AC97_BUS_COMPAT + bool + depends on AC97_BUS_NEW + depends on !AC97_BUS diff --git a/sound/ac97/Makefile b/sound/ac97/Makefile new file mode 100644 index 000000000000..f9c2640bfb59 --- /dev/null +++ b/sound/ac97/Makefile @@ -0,0 +1,8 @@ +# +# make for AC97 bus drivers +# + +obj-$(CONFIG_AC97_BUS_NEW) += ac97.o + +ac97-y += bus.o codec.o +ac97-$(CONFIG_AC97_BUS_COMPAT) += snd_ac97_compat.o diff --git a/sound/ac97/ac97_core.h b/sound/ac97/ac97_core.h new file mode 100644 index 000000000000..08441a4fda7c --- /dev/null +++ b/sound/ac97/ac97_core.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2016 Robert Jarzmik + * + * 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. + */ + +unsigned int snd_ac97_bus_scan_one(struct ac97_controller *ac97, + unsigned int codec_num); + +static inline bool ac97_ids_match(unsigned int id1, unsigned int id2, + unsigned int mask) +{ + return (id1 & mask) == (id2 & mask); +} diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c new file mode 100644 index 000000000000..31f858eceffc --- /dev/null +++ b/sound/ac97/bus.c @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2016 Robert Jarzmik + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ac97_core.h" + +/* + * Protects ac97_controllers and each ac97_controller structure. + */ +static DEFINE_MUTEX(ac97_controllers_mutex); +static DEFINE_IDR(ac97_adapter_idr); +static LIST_HEAD(ac97_controllers); + +static struct bus_type ac97_bus_type; + +static inline struct ac97_controller* +to_ac97_controller(struct device *ac97_adapter) +{ + return container_of(ac97_adapter, struct ac97_controller, adap); +} + +static int ac97_unbound_ctrl_write(struct ac97_controller *adrv, int slot, + unsigned short reg, unsigned short val) +{ + return -ENODEV; +} + +static int ac97_unbound_ctrl_read(struct ac97_controller *adrv, int slot, + unsigned short reg) +{ + return -ENODEV; +} + +static const struct ac97_controller_ops ac97_unbound_ctrl_ops = { + .write = ac97_unbound_ctrl_write, + .read = ac97_unbound_ctrl_read, +}; + +static struct ac97_controller ac97_unbound_ctrl = { + .ops = &ac97_unbound_ctrl_ops, +}; + +static struct ac97_codec_device * +ac97_codec_find(struct ac97_controller *ac97_ctrl, unsigned int codec_num) +{ + if (codec_num >= AC97_BUS_MAX_CODECS) + return ERR_PTR(-EINVAL); + + return ac97_ctrl->codecs[codec_num]; +} + +static void ac97_codec_release(struct device *dev) +{ + struct ac97_codec_device *adev; + struct ac97_controller *ac97_ctrl; + + adev = to_ac97_device(dev); + ac97_ctrl = adev->ac97_ctrl; + ac97_ctrl->codecs[adev->num] = NULL; + kfree(adev); +} + +static int ac97_codec_add(struct ac97_controller *ac97_ctrl, int idx, + unsigned int vendor_id) +{ + struct ac97_codec_device *codec; + int ret; + + codec = kzalloc(sizeof(*codec), GFP_KERNEL); + if (!codec) + return -ENOMEM; + ac97_ctrl->codecs[idx] = codec; + codec->vendor_id = vendor_id; + codec->dev.release = ac97_codec_release; + codec->dev.bus = &ac97_bus_type; + codec->dev.parent = &ac97_ctrl->adap; + codec->num = idx; + codec->ac97_ctrl = ac97_ctrl; + + device_initialize(&codec->dev); + dev_set_name(&codec->dev, "%s:%u", dev_name(ac97_ctrl->parent), idx); + + ret = device_add(&codec->dev); + if (ret) + goto err_free_codec; + + return 0; +err_free_codec: + put_device(&codec->dev); + kfree(codec); + ac97_ctrl->codecs[idx] = NULL; + + return ret; +} + +unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv, + unsigned int codec_num) +{ + unsigned short vid1, vid2; + int ret; + + ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID1); + vid1 = (ret & 0xffff); + if (ret < 0) + return 0; + + ret = adrv->ops->read(adrv, codec_num, AC97_VENDOR_ID2); + vid2 = (ret & 0xffff); + if (ret < 0) + return 0; + + dev_dbg(&adrv->adap, "%s(codec_num=%u): vendor_id=0x%08x\n", + __func__, codec_num, AC97_ID(vid1, vid2)); + return AC97_ID(vid1, vid2); +} + +static int ac97_bus_scan(struct ac97_controller *ac97_ctrl) +{ + int ret, i; + unsigned int vendor_id; + + for (i = 0; i < AC97_BUS_MAX_CODECS; i++) { + if (ac97_codec_find(ac97_ctrl, i)) + continue; + if (!(ac97_ctrl->slots_available & BIT(i))) + continue; + vendor_id = snd_ac97_bus_scan_one(ac97_ctrl, i); + if (!vendor_id) + continue; + + ret = ac97_codec_add(ac97_ctrl, i, vendor_id); + if (ret < 0) + return ret; + } + return 0; +} + +static int ac97_bus_reset(struct ac97_controller *ac97_ctrl) +{ + ac97_ctrl->ops->reset(ac97_ctrl); + + return 0; +} + +/** + * snd_ac97_codec_driver_register - register an AC97 codec driver + * @dev: AC97 driver codec to register + * + * Register an AC97 codec driver to the ac97 bus driver, aka. the AC97 digital + * controller. + * + * Returns 0 on success or error code + */ +int snd_ac97_codec_driver_register(struct ac97_codec_driver *drv) +{ + drv->driver.bus = &ac97_bus_type; + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_register); + +/** + * snd_ac97_codec_driver_unregister - unregister an AC97 codec driver + * @dev: AC97 codec driver to unregister + * + * Unregister a previously registered ac97 codec driver. + */ +void snd_ac97_codec_driver_unregister(struct ac97_codec_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(snd_ac97_codec_driver_unregister); + +/** + * snd_ac97_codec_get_platdata - get platform_data + * @adev: the ac97 codec device + * + * For legacy platforms, in order to have platform_data in codec drivers + * available, while ac97 device are auto-created upon probe, this retrieves the + * platdata which was setup on ac97 controller registration. + * + * Returns the platform data pointer + */ +void *snd_ac97_codec_get_platdata(const struct ac97_codec_device *adev) +{ + struct ac97_controller *ac97_ctrl = adev->ac97_ctrl; + + return ac97_ctrl->codecs_pdata[adev->num]; +} +EXPORT_SYMBOL_GPL(snd_ac97_codec_get_platdata); + +static void ac97_ctrl_codecs_unregister(struct ac97_controller *ac97_ctrl) +{ + int i; + + for (i = 0; i < AC97_BUS_MAX_CODECS; i++) + if (ac97_ctrl->codecs[i]) { + ac97_ctrl->codecs[i]->ac97_ctrl = &ac97_unbound_ctrl; + device_unregister(&ac97_ctrl->codecs[i]->dev); + } +} + +static ssize_t cold_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + struct ac97_controller *ac97_ctrl; + + mutex_lock(&ac97_controllers_mutex); + ac97_ctrl = to_ac97_controller(dev); + ac97_ctrl->ops->reset(ac97_ctrl); + mutex_unlock(&ac97_controllers_mutex); + return len; +} +static DEVICE_ATTR_WO(cold_reset); + +static ssize_t warm_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t len) +{ + struct ac97_controller *ac97_ctrl; + + if (!dev) + return -ENODEV; + + mutex_lock(&ac97_controllers_mutex); + ac97_ctrl = to_ac97_controller(dev); + ac97_ctrl->ops->warm_reset(ac97_ctrl); + mutex_unlock(&ac97_controllers_mutex); + return len; +} +static DEVICE_ATTR_WO(warm_reset); + +static struct attribute *ac97_controller_device_attrs[] = { + &dev_attr_cold_reset.attr, + &dev_attr_warm_reset.attr, + NULL +}; + +static struct attribute_group ac97_adapter_attr_group = { + .name = "ac97_operations", + .attrs = ac97_controller_device_attrs, +}; + +static const struct attribute_group *ac97_adapter_groups[] = { + &ac97_adapter_attr_group, + NULL, +}; + +static void ac97_del_adapter(struct ac97_controller *ac97_ctrl) +{ + mutex_lock(&ac97_controllers_mutex); + ac97_ctrl_codecs_unregister(ac97_ctrl); + list_del(&ac97_ctrl->controllers); + mutex_unlock(&ac97_controllers_mutex); + + device_unregister(&ac97_ctrl->adap); +} + +static void ac97_adapter_release(struct device *dev) +{ + struct ac97_controller *ac97_ctrl; + + ac97_ctrl = to_ac97_controller(dev); + idr_remove(&ac97_adapter_idr, ac97_ctrl->nr); + dev_dbg(&ac97_ctrl->adap, "adapter unregistered by %s\n", + dev_name(ac97_ctrl->parent)); +} + +static const struct device_type ac97_adapter_type = { + .groups = ac97_adapter_groups, + .release = ac97_adapter_release, +}; + +static int ac97_add_adapter(struct ac97_controller *ac97_ctrl) +{ + int ret; + + mutex_lock(&ac97_controllers_mutex); + ret = idr_alloc(&ac97_adapter_idr, ac97_ctrl, 0, 0, GFP_KERNEL); + ac97_ctrl->nr = ret; + if (ret >= 0) { + dev_set_name(&ac97_ctrl->adap, "ac97-%d", ret); + ac97_ctrl->adap.type = &ac97_adapter_type; + ac97_ctrl->adap.parent = ac97_ctrl->parent; + ret = device_register(&ac97_ctrl->adap); + if (ret) + put_device(&ac97_ctrl->adap); + } + if (!ret) + list_add(&ac97_ctrl->controllers, &ac97_controllers); + mutex_unlock(&ac97_controllers_mutex); + + if (!ret) + dev_dbg(&ac97_ctrl->adap, "adapter registered by %s\n", + dev_name(ac97_ctrl->parent)); + return ret; +} + +/** + * snd_ac97_controller_register - register an ac97 controller + * @ops: the ac97 bus operations + * @dev: the device providing the ac97 DC function + * @slots_available: mask of the ac97 codecs that can be scanned and probed + * bit0 => codec 0, bit1 => codec 1 ... bit 3 => codec 3 + * + * Register a digital controller which can control up to 4 ac97 codecs. This is + * the controller side of the AC97 AC-link, while the slave side are the codecs. + * + * Returns a valid controller upon success, negative pointer value upon error + */ +struct ac97_controller *snd_ac97_controller_register( + const struct ac97_controller_ops *ops, struct device *dev, + unsigned short slots_available, void **codecs_pdata) +{ + struct ac97_controller *ac97_ctrl; + int ret, i; + + ac97_ctrl = kzalloc(sizeof(*ac97_ctrl), GFP_KERNEL); + if (!ac97_ctrl) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < AC97_BUS_MAX_CODECS && codecs_pdata; i++) + ac97_ctrl->codecs_pdata[i] = codecs_pdata[i]; + + ac97_ctrl->ops = ops; + ac97_ctrl->slots_available = slots_available; + ac97_ctrl->parent = dev; + ret = ac97_add_adapter(ac97_ctrl); + + if (ret) + goto err; + ac97_bus_reset(ac97_ctrl); + ac97_bus_scan(ac97_ctrl); + + return ac97_ctrl; +err: + kfree(ac97_ctrl); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(snd_ac97_controller_register); + +/** + * snd_ac97_controller_unregister - unregister an ac97 controller + * @ac97_ctrl: the device previously provided to ac97_controller_register() + * + */ +void snd_ac97_controller_unregister(struct ac97_controller *ac97_ctrl) +{ + ac97_del_adapter(ac97_ctrl); +} +EXPORT_SYMBOL_GPL(snd_ac97_controller_unregister); + +#ifdef CONFIG_PM +static int ac97_pm_runtime_suspend(struct device *dev) +{ + struct ac97_codec_device *codec = to_ac97_device(dev); + int ret = pm_generic_runtime_suspend(dev); + + if (ret == 0 && dev->driver) { + if (pm_runtime_is_irq_safe(dev)) + clk_disable(codec->clk); + else + clk_disable_unprepare(codec->clk); + } + + return ret; +} + +static int ac97_pm_runtime_resume(struct device *dev) +{ + struct ac97_codec_device *codec = to_ac97_device(dev); + int ret; + + if (dev->driver) { + if (pm_runtime_is_irq_safe(dev)) + ret = clk_enable(codec->clk); + else + ret = clk_prepare_enable(codec->clk); + if (ret) + return ret; + } + + return pm_generic_runtime_resume(dev); +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops ac97_pm = { + .suspend = pm_generic_suspend, + .resume = pm_generic_resume, + .freeze = pm_generic_freeze, + .thaw = pm_generic_thaw, + .poweroff = pm_generic_poweroff, + .restore = pm_generic_restore, + SET_RUNTIME_PM_OPS( + ac97_pm_runtime_suspend, + ac97_pm_runtime_resume, + NULL) +}; + +static int ac97_get_enable_clk(struct ac97_codec_device *adev) +{ + int ret; + + adev->clk = clk_get(&adev->dev, "ac97_clk"); + if (IS_ERR(adev->clk)) + return PTR_ERR(adev->clk); + + ret = clk_prepare_enable(adev->clk); + if (ret) + clk_put(adev->clk); + + return ret; +} + +static void ac97_put_disable_clk(struct ac97_codec_device *adev) +{ + clk_disable_unprepare(adev->clk); + clk_put(adev->clk); +} + +static ssize_t vendor_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ac97_codec_device *codec = to_ac97_device(dev); + + return sprintf(buf, "%08x", codec->vendor_id); +} +DEVICE_ATTR_RO(vendor_id); + +static struct attribute *ac97_dev_attrs[] = { + &dev_attr_vendor_id.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ac97_dev); + +static int ac97_bus_match(struct device *dev, struct device_driver *drv) +{ + struct ac97_codec_device *adev = to_ac97_device(dev); + struct ac97_codec_driver *adrv = to_ac97_driver(drv); + const struct ac97_id *id = adrv->id_table; + int i = 0; + + if (adev->vendor_id == 0x0 || adev->vendor_id == 0xffffffff) + return false; + + do { + if (ac97_ids_match(id[i].id, adev->vendor_id, id[i].mask)) + return true; + } while (id[i++].id); + + return false; +} + +static int ac97_bus_probe(struct device *dev) +{ + struct ac97_codec_device *adev = to_ac97_device(dev); + struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver); + int ret; + + ret = ac97_get_enable_clk(adev); + if (ret) + return ret; + + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + ret = adrv->probe(adev); + if (ret == 0) + return 0; + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + ac97_put_disable_clk(adev); + + return ret; +} + +static int ac97_bus_remove(struct device *dev) +{ + struct ac97_codec_device *adev = to_ac97_device(dev); + struct ac97_codec_driver *adrv = to_ac97_driver(dev->driver); + int ret; + + ret = pm_runtime_get_sync(dev); + if (ret) + return ret; + + ret = adrv->remove(adev); + pm_runtime_put_noidle(dev); + if (ret == 0) + ac97_put_disable_clk(adev); + + return ret; +} + +static struct bus_type ac97_bus_type = { + .name = "ac97bus", + .dev_groups = ac97_dev_groups, + .match = ac97_bus_match, + .pm = &ac97_pm, + .probe = ac97_bus_probe, + .remove = ac97_bus_remove, +}; + +static int __init ac97_bus_init(void) +{ + return bus_register(&ac97_bus_type); +} +subsys_initcall(ac97_bus_init); + +static void __exit ac97_bus_exit(void) +{ + bus_unregister(&ac97_bus_type); +} +module_exit(ac97_bus_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Jarzmik "); diff --git a/sound/ac97/codec.c b/sound/ac97/codec.c new file mode 100644 index 000000000000..a835f03744bf --- /dev/null +++ b/sound/ac97/codec.c @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2016 Robert Jarzmik + * + * 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. + */ + +#include +#include +#include +#include +#include +#include /* For compat_ac97_* */ + diff --git a/sound/ac97/snd_ac97_compat.c b/sound/ac97/snd_ac97_compat.c new file mode 100644 index 000000000000..61544e0d8de4 --- /dev/null +++ b/sound/ac97/snd_ac97_compat.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2016 Robert Jarzmik + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "ac97_core.h" + +static void compat_ac97_reset(struct snd_ac97 *ac97) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + if (actrl->ops->reset) + actrl->ops->reset(actrl); +} + +static void compat_ac97_warm_reset(struct snd_ac97 *ac97) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + if (actrl->ops->warm_reset) + actrl->ops->warm_reset(actrl); +} + +static void compat_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + actrl->ops->write(actrl, ac97->num, reg, val); +} + +static unsigned short compat_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + + return actrl->ops->read(actrl, ac97->num, reg); +} + +static struct snd_ac97_bus_ops compat_snd_ac97_bus_ops = { + .reset = compat_ac97_reset, + .warm_reset = compat_ac97_warm_reset, + .write = compat_ac97_write, + .read = compat_ac97_read, +}; + +static struct snd_ac97_bus compat_soc_ac97_bus = { + .ops = &compat_snd_ac97_bus_ops, +}; + +struct snd_ac97 *snd_ac97_compat_alloc(struct ac97_codec_device *adev) +{ + struct snd_ac97 *ac97; + + ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL); + if (ac97 == NULL) + return ERR_PTR(-ENOMEM); + + ac97->dev = adev->dev; + ac97->private_data = adev; + ac97->bus = &compat_soc_ac97_bus; + return ac97; +} +EXPORT_SYMBOL_GPL(snd_ac97_compat_alloc); + +void snd_ac97_compat_release(struct snd_ac97 *ac97) +{ + kfree(ac97); +} +EXPORT_SYMBOL_GPL(snd_ac97_compat_release); + +int snd_ac97_reset(struct snd_ac97 *ac97, bool try_warm, unsigned int id, + unsigned int id_mask) +{ + struct ac97_codec_device *adev = to_ac97_device(ac97->private_data); + struct ac97_controller *actrl = adev->ac97_ctrl; + unsigned int scanned; + + if (try_warm) { + compat_ac97_warm_reset(ac97); + scanned = snd_ac97_bus_scan_one(actrl, adev->num); + if (ac97_ids_match(scanned, adev->vendor_id, id_mask)) + return 1; + } + + compat_ac97_reset(ac97); + compat_ac97_warm_reset(ac97); + scanned = snd_ac97_bus_scan_one(actrl, adev->num); + if (ac97_ids_match(scanned, adev->vendor_id, id_mask)) + return 0; + + return -ENODEV; +} +EXPORT_SYMBOL_GPL(snd_ac97_reset); -- cgit v1.2.3-59-g8ed1b From 6f8acad646d29fbf5665a6e0c9adae71c3c2131e Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Sat, 2 Sep 2017 21:54:06 +0200 Subject: ASoC: arm: make pxa2xx-ac97-lib ac97 codec agnostic All pxa library functions don't use the input parameters for nothing but slot number. This simplifies their prototypes, and makes them usable by both the legacy ac97 bus and the new ac97 bus. Signed-off-by: Robert Jarzmik Signed-off-by: Mark Brown --- include/sound/pxa2xx-lib.h | 15 +++++++++------ sound/arm/pxa2xx-ac97-lib.c | 37 +++++++++++++++++++++---------------- sound/arm/pxa2xx-ac97.c | 35 +++++++++++++++++++++++++++-------- sound/soc/pxa/pxa2xx-ac97.c | 32 ++++++++++++++++++++++++++------ 4 files changed, 83 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/sound/pxa2xx-lib.h b/include/sound/pxa2xx-lib.h index 6ef629bde164..85352becec7c 100644 --- a/include/sound/pxa2xx-lib.h +++ b/include/sound/pxa2xx-lib.h @@ -1,10 +1,13 @@ #ifndef PXA2XX_LIB_H #define PXA2XX_LIB_H +#include #include -#include /* PCM */ +struct snd_pcm_substream; +struct snd_pcm_hw_params; +struct snd_pcm; extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); @@ -21,12 +24,12 @@ extern void pxa2xx_pcm_free_dma_buffers(struct snd_pcm *pcm); /* AC97 */ -extern unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg); -extern void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val); +extern int pxa2xx_ac97_read(int slot, unsigned short reg); +extern int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val); -extern bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97); -extern bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97); -extern void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97); +extern bool pxa2xx_ac97_try_warm_reset(void); +extern bool pxa2xx_ac97_try_cold_reset(void); +extern void pxa2xx_ac97_finish_reset(void); extern int pxa2xx_ac97_hw_suspend(void); extern int pxa2xx_ac97_hw_resume(void); diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index 39c3969ac1c7..5950a9e218d9 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -20,7 +20,6 @@ #include #include -#include #include #include @@ -46,38 +45,41 @@ extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio); * 1 jiffy timeout if interrupt never comes). */ -unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) +int pxa2xx_ac97_read(int slot, unsigned short reg) { - unsigned short val = -1; + int val = -ENODEV; volatile u32 *reg_addr; + if (slot > 0) + return -ENODEV; + mutex_lock(&car_mutex); /* set up primary or secondary codec space */ if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS) - reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; + reg_addr = slot ? &SMC_REG_BASE : &PMC_REG_BASE; else - reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; + reg_addr = slot ? &SAC_REG_BASE : &PAC_REG_BASE; reg_addr += (reg >> 1); /* start read access across the ac97 link */ GSR = GSR_CDONE | GSR_SDONE; gsr_bits = 0; - val = *reg_addr; + val = (*reg_addr & 0xffff); if (reg == AC97_GPIO_STATUS) goto out; if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 && !((GSR | gsr_bits) & GSR_SDONE)) { printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", __func__, reg, GSR | gsr_bits); - val = -1; + val = -ETIMEDOUT; goto out; } /* valid data now */ GSR = GSR_CDONE | GSR_SDONE; gsr_bits = 0; - val = *reg_addr; + val = (*reg_addr & 0xffff); /* but we've just started another cycle... */ wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1); @@ -86,29 +88,32 @@ out: mutex_unlock(&car_mutex); } EXPORT_SYMBOL_GPL(pxa2xx_ac97_read); -void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - unsigned short val) +int pxa2xx_ac97_write(int slot, unsigned short reg, unsigned short val) { volatile u32 *reg_addr; + int ret = 0; mutex_lock(&car_mutex); /* set up primary or secondary codec space */ if (cpu_is_pxa25x() && reg == AC97_GPIO_STATUS) - reg_addr = ac97->num ? &SMC_REG_BASE : &PMC_REG_BASE; + reg_addr = slot ? &SMC_REG_BASE : &PMC_REG_BASE; else - reg_addr = ac97->num ? &SAC_REG_BASE : &PAC_REG_BASE; + reg_addr = slot ? &SAC_REG_BASE : &PAC_REG_BASE; reg_addr += (reg >> 1); GSR = GSR_CDONE | GSR_SDONE; gsr_bits = 0; *reg_addr = val; if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 && - !((GSR | gsr_bits) & GSR_CDONE)) + !((GSR | gsr_bits) & GSR_CDONE)) { printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", __func__, reg, GSR | gsr_bits); + ret = -EIO; + } mutex_unlock(&car_mutex); + return ret; } EXPORT_SYMBOL_GPL(pxa2xx_ac97_write); @@ -188,7 +193,7 @@ static inline void pxa_ac97_cold_pxa3xx(void) } #endif -bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) +bool pxa2xx_ac97_try_warm_reset(void) { unsigned long gsr; unsigned int timeout = 100; @@ -225,7 +230,7 @@ bool pxa2xx_ac97_try_warm_reset(struct snd_ac97 *ac97) } EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_warm_reset); -bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) +bool pxa2xx_ac97_try_cold_reset(void) { unsigned long gsr; unsigned int timeout = 1000; @@ -263,7 +268,7 @@ bool pxa2xx_ac97_try_cold_reset(struct snd_ac97 *ac97) EXPORT_SYMBOL_GPL(pxa2xx_ac97_try_cold_reset); -void pxa2xx_ac97_finish_reset(struct snd_ac97 *ac97) +void pxa2xx_ac97_finish_reset(void) { GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR |= GCR_SDONE_IE|GCR_CDONE_IE; diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c index fbd5dad0c484..4bc244c40f80 100644 --- a/sound/arm/pxa2xx-ac97.c +++ b/sound/arm/pxa2xx-ac97.c @@ -29,19 +29,38 @@ #include "pxa2xx-pcm.h" -static void pxa2xx_ac97_reset(struct snd_ac97 *ac97) +static void pxa2xx_ac97_legacy_reset(struct snd_ac97 *ac97) { - if (!pxa2xx_ac97_try_cold_reset(ac97)) { - pxa2xx_ac97_try_warm_reset(ac97); - } + if (!pxa2xx_ac97_try_cold_reset()) + pxa2xx_ac97_try_warm_reset(); + + pxa2xx_ac97_finish_reset(); +} + +static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + int ret; + + ret = pxa2xx_ac97_read(ac97->num, reg); + if (ret < 0) + return 0; + else + return (unsigned short)(ret & 0xffff); +} + +static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97, + unsigned short reg, unsigned short val) +{ + int __always_unused ret; - pxa2xx_ac97_finish_reset(ac97); + ret = pxa2xx_ac97_write(ac97->num, reg, val); } static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { - .read = pxa2xx_ac97_read, - .write = pxa2xx_ac97_write, - .reset = pxa2xx_ac97_reset, + .read = pxa2xx_ac97_legacy_read, + .write = pxa2xx_ac97_legacy_write, + .reset = pxa2xx_ac97_legacy_reset, }; static struct pxad_param pxa2xx_ac97_pcm_out_req = { diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index f49bf02e5ec2..803818aabee9 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -29,21 +29,41 @@ static void pxa2xx_ac97_warm_reset(struct snd_ac97 *ac97) { - pxa2xx_ac97_try_warm_reset(ac97); + pxa2xx_ac97_try_warm_reset(); - pxa2xx_ac97_finish_reset(ac97); + pxa2xx_ac97_finish_reset(); } static void pxa2xx_ac97_cold_reset(struct snd_ac97 *ac97) { - pxa2xx_ac97_try_cold_reset(ac97); + pxa2xx_ac97_try_cold_reset(); - pxa2xx_ac97_finish_reset(ac97); + pxa2xx_ac97_finish_reset(); +} + +static unsigned short pxa2xx_ac97_legacy_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + int ret; + + ret = pxa2xx_ac97_read(ac97->num, reg); + if (ret < 0) + return 0; + else + return (unsigned short)(ret & 0xffff); +} + +static void pxa2xx_ac97_legacy_write(struct snd_ac97 *ac97, + unsigned short reg, unsigned short val) +{ + int ret; + + ret = pxa2xx_ac97_write(ac97->num, reg, val); } static struct snd_ac97_bus_ops pxa2xx_ac97_ops = { - .read = pxa2xx_ac97_read, - .write = pxa2xx_ac97_write, + .read = pxa2xx_ac97_legacy_read, + .write = pxa2xx_ac97_legacy_write, .warm_reset = pxa2xx_ac97_warm_reset, .reset = pxa2xx_ac97_cold_reset, }; -- cgit v1.2.3-59-g8ed1b From 17ad4fdd09e1613d7047aa9e00d7de68ad350204 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 1 Sep 2017 15:57:29 +0100 Subject: drm/i915/perf: Remove __user from u64 in drm_i915_perf_oa_config Sparse complains that these integers from which we form void __user *, and so we don't need the annotation itself inside the uABI. Signed-off-by: Chris Wilson Cc: Lionel Landwerlin Cc: Matthew Auld Link: https://patchwork.freedesktop.org/patch/msgid/20170901145729.21363-2-chris@chris-wilson.co.uk Reviewed-by: Lionel Landwerlin --- include/uapi/drm/i915_drm.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 6598fb76d2c2..d8d10d932759 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -1509,9 +1509,9 @@ struct drm_i915_perf_oa_config { __u32 n_boolean_regs; __u32 n_flex_regs; - __u64 __user mux_regs_ptr; - __u64 __user boolean_regs_ptr; - __u64 __user flex_regs_ptr; + __u64 mux_regs_ptr; + __u64 boolean_regs_ptr; + __u64 flex_regs_ptr; }; #if defined(__cplusplus) -- cgit v1.2.3-59-g8ed1b From c4860ad60564838994b74e7ee7dd12ceeda0f520 Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Mon, 31 Jul 2017 19:55:08 +0100 Subject: lib/scatterlist: Fix offset type in sg_alloc_table_from_pages Scatterlist entries have an unsigned int for the offset so correct the sg_alloc_table_from_pages function accordingly. Since these are offsets withing a page, unsigned int is wide enough. Also converts callers which were using unsigned long locally with the lower_32_bits annotation to make it explicitly clear what is happening. v2: Use offset_in_page. (Chris Wilson) Signed-off-by: Tvrtko Ursulin Cc: Masahiro Yamada Cc: Pawel Osciak Cc: Marek Szyprowski Cc: Kyungmin Park Cc: Tomasz Stanislawski Cc: Matt Porter Cc: Alexandre Bounine Cc: linux-media@vger.kernel.org Cc: linux-kernel@vger.kernel.org Acked-by: Marek Szyprowski (v1) Reviewed-by: Chris Wilson Reviewed-by: Mauro Carvalho Chehab Link: https://patchwork.freedesktop.org/patch/msgid/20170731185512.20010-1-tvrtko.ursulin@linux.intel.com --- drivers/media/v4l2-core/videobuf2-dma-contig.c | 4 ++-- drivers/rapidio/devices/rio_mport_cdev.c | 4 ++-- include/linux/scatterlist.h | 2 +- lib/scatterlist.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 4f246d166111..2405077fdc71 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -479,7 +479,7 @@ static void *vb2_dc_get_userptr(struct device *dev, unsigned long vaddr, { struct vb2_dc_buf *buf; struct frame_vector *vec; - unsigned long offset; + unsigned int offset; int n_pages, i; int ret = 0; struct sg_table *sgt; @@ -507,7 +507,7 @@ static void *vb2_dc_get_userptr(struct device *dev, unsigned long vaddr, buf->dev = dev; buf->dma_dir = dma_dir; - offset = vaddr & ~PAGE_MASK; + offset = lower_32_bits(offset_in_page(vaddr)); vec = vb2_create_framevec(vaddr, size, dma_dir == DMA_FROM_DEVICE); if (IS_ERR(vec)) { ret = PTR_ERR(vec); diff --git a/drivers/rapidio/devices/rio_mport_cdev.c b/drivers/rapidio/devices/rio_mport_cdev.c index 5beb0c361076..5c1b6388122a 100644 --- a/drivers/rapidio/devices/rio_mport_cdev.c +++ b/drivers/rapidio/devices/rio_mport_cdev.c @@ -876,10 +876,10 @@ rio_dma_transfer(struct file *filp, u32 transfer_mode, * offset within the internal buffer specified by handle parameter. */ if (xfer->loc_addr) { - unsigned long offset; + unsigned int offset; long pinned; - offset = (unsigned long)(uintptr_t)xfer->loc_addr & ~PAGE_MASK; + offset = lower_32_bits(offset_in_page(xfer->loc_addr)); nr_pages = PAGE_ALIGN(xfer->length + offset) >> PAGE_SHIFT; page_list = kmalloc_array(nr_pages, diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 4b3286ac60c8..205aefb4ed93 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -263,7 +263,7 @@ int __sg_alloc_table(struct sg_table *, unsigned int, unsigned int, int sg_alloc_table(struct sg_table *, unsigned int, gfp_t); int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, unsigned int n_pages, - unsigned long offset, unsigned long size, + unsigned int offset, unsigned long size, gfp_t gfp_mask); size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, diff --git a/lib/scatterlist.c b/lib/scatterlist.c index be7b4dd6b68d..dee0c5004e2f 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -391,7 +391,7 @@ EXPORT_SYMBOL(sg_alloc_table); */ int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, unsigned int n_pages, - unsigned long offset, unsigned long size, + unsigned int offset, unsigned long size, gfp_t gfp_mask) { unsigned int chunks; -- cgit v1.2.3-59-g8ed1b From c125906b839b794c580a5de911de65bd2c63aaee Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Thu, 3 Aug 2017 10:13:12 +0100 Subject: lib/scatterlist: Avoid potential scatterlist entry overflow Since the scatterlist length field is an unsigned int, make sure that sg_alloc_table_from_pages does not overflow it while coalescing pages to a single entry. v2: Drop reference to future use. Use UINT_MAX. v3: max_segment must be page aligned. v4: Do not rely on compiler to optimise out the rounddown. (Joonas Lahtinen) v5: Simplified loops and use post-increments rather than pre-increments. Use PAGE_MASK and fix comment typo. (Andy Shevchenko) v6: Commit spelling fix. Signed-off-by: Tvrtko Ursulin Cc: Masahiro Yamada Cc: linux-kernel@vger.kernel.org Reviewed-by: Chris Wilson Cc: Joonas Lahtinen Cc: Andy Shevchenko Link: https://patchwork.freedesktop.org/patch/msgid/20170803091312.22875-1-tvrtko.ursulin@linux.intel.com --- include/linux/scatterlist.h | 6 ++++++ lib/scatterlist.c | 31 ++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 205aefb4ed93..6dd2ddbc6230 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -20,6 +20,12 @@ struct scatterlist { #endif }; +/* + * Since the above length field is an unsigned int, below we define the maximum + * length in bytes that can be stored in one scatterlist entry. + */ +#define SCATTERLIST_MAX_SEGMENT (UINT_MAX & PAGE_MASK) + /* * These macros should be used after a dma_map_sg call has been done * to get bus addresses of each of the SG entries and their lengths. diff --git a/lib/scatterlist.c b/lib/scatterlist.c index dee0c5004e2f..7b2e74da2c44 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -394,17 +394,22 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, unsigned int offset, unsigned long size, gfp_t gfp_mask) { - unsigned int chunks; - unsigned int i; - unsigned int cur_page; + const unsigned int max_segment = SCATTERLIST_MAX_SEGMENT; + unsigned int chunks, cur_page, seg_len, i; int ret; struct scatterlist *s; /* compute number of contiguous chunks */ chunks = 1; - for (i = 1; i < n_pages; ++i) - if (page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) - ++chunks; + seg_len = 0; + for (i = 1; i < n_pages; i++) { + seg_len += PAGE_SIZE; + if (seg_len >= max_segment || + page_to_pfn(pages[i]) != page_to_pfn(pages[i - 1]) + 1) { + chunks++; + seg_len = 0; + } + } ret = sg_alloc_table(sgt, chunks, gfp_mask); if (unlikely(ret)) @@ -413,17 +418,21 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, /* merging chunks and putting them into the scatterlist */ cur_page = 0; for_each_sg(sgt->sgl, s, sgt->orig_nents, i) { - unsigned long chunk_size; - unsigned int j; + unsigned int j, chunk_size; /* look for the end of the current chunk */ - for (j = cur_page + 1; j < n_pages; ++j) - if (page_to_pfn(pages[j]) != + seg_len = 0; + for (j = cur_page + 1; j < n_pages; j++) { + seg_len += PAGE_SIZE; + if (seg_len >= max_segment || + page_to_pfn(pages[j]) != page_to_pfn(pages[j - 1]) + 1) break; + } chunk_size = ((j - cur_page) << PAGE_SHIFT) - offset; - sg_set_page(s, pages[cur_page], min(size, chunk_size), offset); + sg_set_page(s, pages[cur_page], + min_t(unsigned long, size, chunk_size), offset); size -= chunk_size; offset = 0; cur_page = j; -- cgit v1.2.3-59-g8ed1b From 89d8589cd72c6f48b19c370517d16f3ee23909df Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Thu, 3 Aug 2017 10:13:51 +0100 Subject: lib/scatterlist: Introduce and export __sg_alloc_table_from_pages Drivers like i915 benefit from being able to control the maxium size of the sg coalesced segment while building the scatter- gather list. Introduce and export the __sg_alloc_table_from_pages function which will allow it that control. v2: Reorder parameters. (Chris Wilson) v3: Fix incomplete reordering in v2. v4: max_segment needs to be page aligned. v5: Rebase. v6: Rebase. v7: Fix spelling in commit and mention max segment size in __sg_alloc_table_from_pages kerneldoc. (Andrew Morton) Signed-off-by: Tvrtko Ursulin Cc: Masahiro Yamada Cc: linux-kernel@vger.kernel.org Cc: Chris Wilson Reviewed-by: Chris Wilson Cc: Joonas Lahtinen Cc: Andrew Morton Link: https://patchwork.freedesktop.org/patch/msgid/20170803091351.23594-1-tvrtko.ursulin@linux.intel.com --- include/linux/scatterlist.h | 11 +++++--- lib/scatterlist.c | 66 +++++++++++++++++++++++++++++++++------------ 2 files changed, 56 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 6dd2ddbc6230..874b50c232de 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -267,10 +267,13 @@ void sg_free_table(struct sg_table *); int __sg_alloc_table(struct sg_table *, unsigned int, unsigned int, struct scatterlist *, gfp_t, sg_alloc_fn *); int sg_alloc_table(struct sg_table *, unsigned int, gfp_t); -int sg_alloc_table_from_pages(struct sg_table *sgt, - struct page **pages, unsigned int n_pages, - unsigned int offset, unsigned long size, - gfp_t gfp_mask); +int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, + unsigned int n_pages, unsigned int offset, + unsigned long size, unsigned int max_segment, + gfp_t gfp_mask); +int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, + unsigned int n_pages, unsigned int offset, + unsigned long size, gfp_t gfp_mask); size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, void *buf, size_t buflen, off_t skip, bool to_buffer); diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 7b2e74da2c44..7c1c55f7daaa 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -370,35 +370,38 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) EXPORT_SYMBOL(sg_alloc_table); /** - * sg_alloc_table_from_pages - Allocate and initialize an sg table from - * an array of pages - * @sgt: The sg table header to use - * @pages: Pointer to an array of page pointers - * @n_pages: Number of pages in the pages array - * @offset: Offset from start of the first page to the start of a buffer - * @size: Number of valid bytes in the buffer (after offset) - * @gfp_mask: GFP allocation mask + * __sg_alloc_table_from_pages - Allocate and initialize an sg table from + * an array of pages + * @sgt: The sg table header to use + * @pages: Pointer to an array of page pointers + * @n_pages: Number of pages in the pages array + * @offset: Offset from start of the first page to the start of a buffer + * @size: Number of valid bytes in the buffer (after offset) + * @max_segment: Maximum size of a scatterlist node in bytes (page aligned) + * @gfp_mask: GFP allocation mask * * Description: * Allocate and initialize an sg table from a list of pages. Contiguous - * ranges of the pages are squashed into a single scatterlist node. A user - * may provide an offset at a start and a size of valid data in a buffer - * specified by the page array. The returned sg table is released by - * sg_free_table. + * ranges of the pages are squashed into a single scatterlist node up to the + * maximum size specified in @max_segment. An user may provide an offset at a + * start and a size of valid data in a buffer specified by the page array. + * The returned sg table is released by sg_free_table. * * Returns: * 0 on success, negative error on failure */ -int sg_alloc_table_from_pages(struct sg_table *sgt, - struct page **pages, unsigned int n_pages, - unsigned int offset, unsigned long size, - gfp_t gfp_mask) +int __sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, + unsigned int n_pages, unsigned int offset, + unsigned long size, unsigned int max_segment, + gfp_t gfp_mask) { - const unsigned int max_segment = SCATTERLIST_MAX_SEGMENT; unsigned int chunks, cur_page, seg_len, i; int ret; struct scatterlist *s; + if (WARN_ON(!max_segment || offset_in_page(max_segment))) + return -EINVAL; + /* compute number of contiguous chunks */ chunks = 1; seg_len = 0; @@ -440,6 +443,35 @@ int sg_alloc_table_from_pages(struct sg_table *sgt, return 0; } +EXPORT_SYMBOL(__sg_alloc_table_from_pages); + +/** + * sg_alloc_table_from_pages - Allocate and initialize an sg table from + * an array of pages + * @sgt: The sg table header to use + * @pages: Pointer to an array of page pointers + * @n_pages: Number of pages in the pages array + * @offset: Offset from start of the first page to the start of a buffer + * @size: Number of valid bytes in the buffer (after offset) + * @gfp_mask: GFP allocation mask + * + * Description: + * Allocate and initialize an sg table from a list of pages. Contiguous + * ranges of the pages are squashed into a single scatterlist node. A user + * may provide an offset at a start and a size of valid data in a buffer + * specified by the page array. The returned sg table is released by + * sg_free_table. + * + * Returns: + * 0 on success, negative error on failure + */ +int sg_alloc_table_from_pages(struct sg_table *sgt, struct page **pages, + unsigned int n_pages, unsigned int offset, + unsigned long size, gfp_t gfp_mask) +{ + return __sg_alloc_table_from_pages(sgt, pages, n_pages, offset, size, + SCATTERLIST_MAX_SEGMENT, gfp_mask); +} EXPORT_SYMBOL(sg_alloc_table_from_pages); void __sg_page_iter_start(struct sg_page_iter *piter, -- cgit v1.2.3-59-g8ed1b From f46640b931e588aeec5285b4a9547b354ad10cd0 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 4 Sep 2017 12:48:36 +0200 Subject: drm/atomic: Return commit in drm_crtc_commit_get for better annotation This will allow code to do x->commit = drm_crtc_commit_get(commit), making it clearer where references are used. Signed-off-by: Maarten Lankhorst Link: https://patchwork.freedesktop.org/patch/msgid/20170904104838.23822-5-maarten.lankhorst@linux.intel.com Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic_helper.c | 3 +-- include/drm/drm_atomic.h | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 1bc32cd74d78..94ad11f76e0e 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1633,8 +1633,7 @@ static int stall_checks(struct drm_crtc *crtc, bool nonblock) return -EBUSY; } } else if (i == 1) { - stall_commit = commit; - drm_crtc_commit_get(stall_commit); + stall_commit = drm_crtc_commit_get(commit); break; } diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index f73b663c1f76..c0451e2a51af 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -252,10 +252,14 @@ void __drm_crtc_commit_free(struct kref *kref); * @commit: CRTC commit * * Increases the reference of @commit. + * + * Returns: + * The pointer to @commit, with reference increased. */ -static inline void drm_crtc_commit_get(struct drm_crtc_commit *commit) +static inline struct drm_crtc_commit *drm_crtc_commit_get(struct drm_crtc_commit *commit) { kref_get(&commit->ref); + return commit; } /** -- cgit v1.2.3-59-g8ed1b From 163bcc2c74a22c891c1906e6e343e28a70a54978 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 4 Sep 2017 17:04:56 +0200 Subject: drm/atomic: Move drm_crtc_commit to drm_crtc_state, v4. Most code only cares about the current commit or previous commit. Fortuantely we already have a place to track those. Move it to drm_crtc_state where it belongs. :) The per-crtc commit_list is kept for places where we have to look deeper than the current or previous commit for checking whether to stall on unpin. This is used in drm_atomic_helper_setup_commit and intel_has_pending_fb_unpin. Changes since v1: - Update kerneldoc for drm_crtc.commit_list. (danvet) Changes since v2: - Remove drm_atomic_helper_async_check hunk. (pinchartl) Changes since v3: - Fix use-after-free in drm_atomic_helper_commit_cleanup_done(). Signed-off-by: Maarten Lankhorst Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20170904150456.31049-1-maarten.lankhorst@linux.intel.com [mlankhorst: preceeding -> preceding (checkpatch)] --- drivers/gpu/drm/drm_atomic.c | 7 ---- drivers/gpu/drm/drm_atomic_helper.c | 82 ++++++++++++++++--------------------- include/drm/drm_atomic.h | 1 - include/drm/drm_crtc.h | 23 +++++++++-- 4 files changed, 54 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 1b755439f591..58df70a5aab1 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -163,13 +163,6 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) crtc->funcs->atomic_destroy_state(crtc, state->crtcs[i].state); - if (state->crtcs[i].commit) { - kfree(state->crtcs[i].commit->event); - state->crtcs[i].commit->event = NULL; - drm_crtc_commit_put(state->crtcs[i].commit); - } - - state->crtcs[i].commit = NULL; state->crtcs[i].ptr = NULL; state->crtcs[i].state = NULL; } diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 94ad11f76e0e..075b0b386adc 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1262,12 +1262,12 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_vblanks); void drm_atomic_helper_wait_for_flip_done(struct drm_device *dev, struct drm_atomic_state *old_state) { - struct drm_crtc_state *unused; + struct drm_crtc_state *new_crtc_state; struct drm_crtc *crtc; int i; - for_each_new_crtc_in_state(old_state, crtc, unused, i) { - struct drm_crtc_commit *commit = old_state->crtcs[i].commit; + for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { + struct drm_crtc_commit *commit = new_crtc_state->commit; int ret; if (!commit) @@ -1730,7 +1730,7 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, kref_init(&commit->ref); commit->crtc = crtc; - state->crtcs[i].commit = commit; + new_crtc_state->commit = commit; ret = stall_checks(crtc, nonblock); if (ret) @@ -1768,22 +1768,6 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, } EXPORT_SYMBOL(drm_atomic_helper_setup_commit); - -static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc) -{ - struct drm_crtc_commit *commit; - int i = 0; - - list_for_each_entry(commit, &crtc->commit_list, commit_entry) { - /* skip the first entry, that's the current commit */ - if (i == 1) - return commit; - i++; - } - - return NULL; -} - /** * drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits * @old_state: atomic state object with old state structures @@ -1799,17 +1783,13 @@ static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc) void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state) { struct drm_crtc *crtc; - struct drm_crtc_state *new_crtc_state; + struct drm_crtc_state *old_crtc_state; struct drm_crtc_commit *commit; int i; long ret; - for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { - spin_lock(&crtc->commit_lock); - commit = preceeding_commit(crtc); - if (commit) - drm_crtc_commit_get(commit); - spin_unlock(&crtc->commit_lock); + for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) { + commit = old_crtc_state->commit; if (!commit) continue; @@ -1827,8 +1807,6 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state) if (ret == 0) DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", crtc->base.id, crtc->name); - - drm_crtc_commit_put(commit); } } EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); @@ -1851,15 +1829,25 @@ EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state) { struct drm_crtc *crtc; - struct drm_crtc_state *new_crtc_state; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; struct drm_crtc_commit *commit; int i; - for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { - commit = old_state->crtcs[i].commit; + for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, new_crtc_state, i) { + commit = new_crtc_state->commit; if (!commit) continue; + /* + * copy new_crtc_state->commit to old_crtc_state->commit, + * it's unsafe to touch new_crtc_state after hw_done, + * but we still need to do so in cleanup_done(). + */ + if (old_crtc_state->commit) + drm_crtc_commit_put(old_crtc_state->commit); + + old_crtc_state->commit = drm_crtc_commit_get(commit); + /* backend must have consumed any event by now */ WARN_ON(new_crtc_state->event); complete_all(&commit->hw_done); @@ -1881,13 +1869,13 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done); void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state) { struct drm_crtc *crtc; - struct drm_crtc_state *new_crtc_state; + struct drm_crtc_state *old_crtc_state; struct drm_crtc_commit *commit; int i; long ret; - for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) { - commit = old_state->crtcs[i].commit; + for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) { + commit = old_crtc_state->commit; if (WARN_ON(!commit)) continue; @@ -2293,20 +2281,13 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state, struct drm_private_state *old_obj_state, *new_obj_state; if (stall) { - for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { - spin_lock(&crtc->commit_lock); - commit = list_first_entry_or_null(&crtc->commit_list, - struct drm_crtc_commit, commit_entry); - if (commit) - drm_crtc_commit_get(commit); - spin_unlock(&crtc->commit_lock); + for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { + commit = old_crtc_state->commit; if (!commit) continue; ret = wait_for_completion_interruptible(&commit->hw_done); - drm_crtc_commit_put(commit); - if (ret) return ret; } @@ -2331,13 +2312,13 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state, state->crtcs[i].state = old_crtc_state; crtc->state = new_crtc_state; - if (state->crtcs[i].commit) { + if (new_crtc_state->commit) { spin_lock(&crtc->commit_lock); - list_add(&state->crtcs[i].commit->commit_entry, + list_add(&new_crtc_state->commit->commit_entry, &crtc->commit_list); spin_unlock(&crtc->commit_lock); - state->crtcs[i].commit->event = NULL; + new_crtc_state->commit->event = NULL; } } @@ -3171,6 +3152,7 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, state->connectors_changed = false; state->color_mgmt_changed = false; state->zpos_changed = false; + state->commit = NULL; state->event = NULL; state->pageflip_flags = 0; } @@ -3209,6 +3191,12 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state); */ void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state) { + if (state->commit) { + kfree(state->commit->event); + state->commit->event = NULL; + drm_crtc_commit_put(state->commit); + } + drm_property_blob_put(state->mode_blob); drm_property_blob_put(state->degamma_lut); drm_property_blob_put(state->ctm); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index c0451e2a51af..a80a8dadef00 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -144,7 +144,6 @@ struct __drm_planes_state { struct __drm_crtcs_state { struct drm_crtc *ptr; struct drm_crtc_state *state, *old_state, *new_state; - struct drm_crtc_commit *commit; s32 __user *out_fence_ptr; unsigned last_vblank_count; }; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 1a642020e306..901f5c054a2c 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -253,6 +253,15 @@ struct drm_crtc_state { */ struct drm_pending_vblank_event *event; + /** + * @commit: + * + * This tracks how the commit for this update proceeds through the + * various phases. This is never cleared, except when we destroy the + * state, so that subsequent commits can synchronize with previous ones. + */ + struct drm_crtc_commit *commit; + struct drm_atomic_state *state; }; @@ -808,10 +817,16 @@ struct drm_crtc { * @commit_list: * * List of &drm_crtc_commit structures tracking pending commits. - * Protected by @commit_lock. This list doesn't hold its own full - * reference, but burrows it from the ongoing commit. Commit entries - * must be removed from this list once the commit is fully completed, - * but before it's correspoding &drm_atomic_state gets destroyed. + * Protected by @commit_lock. This list holds its own full reference, + * as does the ongoing commit. + * + * "Note that the commit for a state change is also tracked in + * &drm_crtc_state.commit. For accessing the immediately preceding + * commit in an atomic update it is recommended to just use that + * pointer in the old CRTC state, since accessing that doesn't need + * any locking or list-walking. @commit_list should only be used to + * stall for framebuffer cleanup that's signalled through + * &drm_crtc_commit.cleanup_done." */ struct list_head commit_list; -- cgit v1.2.3-59-g8ed1b From 21a01abbe32a3cbeb903378a24e504bfd9fe0648 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 4 Sep 2017 12:48:37 +0200 Subject: drm/atomic: Fix freeing connector/plane state too early by tracking commits, v3. Currently we neatly track the crtc state, but forget to look at plane/connector state. When doing a nonblocking modeset, immediately followed by a setprop before the modeset completes, the setprop will see the modesets new state as the old state and free it. This has to be solved by waiting for hw_done on the connector, even if it's not assigned to a crtc. When a connector is unbound we take the last crtc commit, and when it stays unbound we create a new fake crtc commit for that gets signaled on hw_done for all the planes/connectors. We wait for it the same way as we do for crtc's, which will make sure we never run into a use-after-free situation. Changes since v1: - Only create a single disable commit. (danvet) - Fix leak in intel_legacy_cursor_update. Changes since v2: - Make reference counting in drm_atomic_helper_setup_commit more obvious. (pinchartl) - Call cleanup_done for fake commit. (danvet) - Add comments to drm_atomic_helper_setup_commit. (danvet, pinchartl) - Add comment to drm_atomic_helper_swap_state. (pinchartl) Signed-off-by: Maarten Lankhorst Testcase: kms_atomic_transition.plane-use-after-nonblocking-unbind* Cc: Laurent Pinchart Link: https://patchwork.freedesktop.org/patch/msgid/20170904104838.23822-6-maarten.lankhorst@linux.intel.com Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 4 + drivers/gpu/drm/drm_atomic_helper.c | 172 +++++++++++++++++++++++++++++++++-- drivers/gpu/drm/i915/intel_display.c | 2 + include/drm/drm_atomic.h | 12 +++ include/drm/drm_connector.h | 7 ++ include/drm/drm_plane.h | 7 ++ 6 files changed, 198 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 58df70a5aab1..98f42397c529 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -192,6 +192,10 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) } state->num_private_objs = 0; + if (state->fake_commit) { + drm_crtc_commit_put(state->fake_commit); + state->fake_commit = NULL; + } } EXPORT_SYMBOL(drm_atomic_state_default_clear); diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 8bc2f155a7e4..820adcda45b8 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1667,6 +1667,38 @@ static void release_crtc_commit(struct completion *completion) drm_crtc_commit_put(commit); } +static void init_commit(struct drm_crtc_commit *commit, struct drm_crtc *crtc) +{ + init_completion(&commit->flip_done); + init_completion(&commit->hw_done); + init_completion(&commit->cleanup_done); + INIT_LIST_HEAD(&commit->commit_entry); + kref_init(&commit->ref); + commit->crtc = crtc; +} + +static struct drm_crtc_commit * +crtc_or_fake_commit(struct drm_atomic_state *state, struct drm_crtc *crtc) +{ + if (crtc) { + struct drm_crtc_state *new_crtc_state; + + new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + + return new_crtc_state->commit; + } + + if (!state->fake_commit) { + state->fake_commit = kzalloc(sizeof(*state->fake_commit), GFP_KERNEL); + if (!state->fake_commit) + return NULL; + + init_commit(state->fake_commit, NULL); + } + + return state->fake_commit; +} + /** * drm_atomic_helper_setup_commit - setup possibly nonblocking commit * @state: new modeset state to be committed @@ -1715,6 +1747,10 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, { struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct drm_connector *conn; + struct drm_connector_state *old_conn_state, *new_conn_state; + struct drm_plane *plane; + struct drm_plane_state *old_plane_state, *new_plane_state; struct drm_crtc_commit *commit; int i, ret; @@ -1723,12 +1759,7 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, if (!commit) return -ENOMEM; - init_completion(&commit->flip_done); - init_completion(&commit->hw_done); - init_completion(&commit->cleanup_done); - INIT_LIST_HEAD(&commit->commit_entry); - kref_init(&commit->ref); - commit->crtc = crtc; + init_commit(commit, crtc); new_crtc_state->commit = commit; @@ -1764,6 +1795,42 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, drm_crtc_commit_get(commit); } + for_each_oldnew_connector_in_state(state, conn, old_conn_state, new_conn_state, i) { + /* commit tracked through new_crtc_state->commit, no need to do it explicitly */ + if (new_conn_state->crtc) + continue; + + /* Userspace is not allowed to get ahead of the previous + * commit with nonblocking ones. */ + if (nonblock && old_conn_state->commit && + !try_wait_for_completion(&old_conn_state->commit->flip_done)) + return -EBUSY; + + commit = crtc_or_fake_commit(state, old_conn_state->crtc); + if (!commit) + return -ENOMEM; + + new_conn_state->commit = drm_crtc_commit_get(commit); + } + + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { + /* commit tracked through new_crtc_state->commit, no need to do it explicitly */ + if (new_plane_state->crtc) + continue; + + /* Userspace is not allowed to get ahead of the previous + * commit with nonblocking ones. */ + if (nonblock && old_plane_state->commit && + !try_wait_for_completion(&old_plane_state->commit->flip_done)) + return -EBUSY; + + commit = crtc_or_fake_commit(state, old_plane_state->crtc); + if (!commit) + return -ENOMEM; + + new_plane_state->commit = drm_crtc_commit_get(commit); + } + return 0; } EXPORT_SYMBOL(drm_atomic_helper_setup_commit); @@ -1784,6 +1851,10 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state) { struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; + struct drm_plane *plane; + struct drm_plane_state *old_plane_state; + struct drm_connector *conn; + struct drm_connector_state *old_conn_state; struct drm_crtc_commit *commit; int i; long ret; @@ -1808,6 +1879,48 @@ void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *old_state) DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n", crtc->base.id, crtc->name); } + + for_each_old_connector_in_state(old_state, conn, old_conn_state, i) { + commit = old_conn_state->commit; + + if (!commit) + continue; + + ret = wait_for_completion_timeout(&commit->hw_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CONNECTOR:%d:%s] hw_done timed out\n", + conn->base.id, conn->name); + + /* Currently no support for overwriting flips, hence + * stall for previous one to execute completely. */ + ret = wait_for_completion_timeout(&commit->flip_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[CONNECTOR:%d:%s] flip_done timed out\n", + conn->base.id, conn->name); + } + + for_each_old_plane_in_state(old_state, plane, old_plane_state, i) { + commit = old_plane_state->commit; + + if (!commit) + continue; + + ret = wait_for_completion_timeout(&commit->hw_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[PLANE:%d:%s] hw_done timed out\n", + plane->base.id, plane->name); + + /* Currently no support for overwriting flips, hence + * stall for previous one to execute completely. */ + ret = wait_for_completion_timeout(&commit->flip_done, + 10*HZ); + if (ret == 0) + DRM_ERROR("[PLANE:%d:%s] flip_done timed out\n", + plane->base.id, plane->name); + } } EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies); @@ -1852,6 +1965,11 @@ void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *old_state) WARN_ON(new_crtc_state->event); complete_all(&commit->hw_done); } + + if (old_state->fake_commit) { + complete_all(&old_state->fake_commit->hw_done); + complete_all(&old_state->fake_commit->flip_done); + } } EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done); @@ -1885,6 +2003,9 @@ void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *old_state) list_del(&commit->commit_entry); spin_unlock(&crtc->commit_lock); } + + if (old_state->fake_commit) + complete_all(&old_state->fake_commit->cleanup_done); } EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done); @@ -2264,6 +2385,15 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state, struct drm_private_state *old_obj_state, *new_obj_state; if (stall) { + /* + * We have to stall for hw_done here before + * drm_atomic_helper_wait_for_dependencies() because flip + * depth > 1 is not yet supported by all drivers. As long as + * obj->state is directly dereferenced anywhere in the drivers + * atomic_commit_tail function, then it's unsafe to swap state + * before drm_atomic_helper_commit_hw_done() is called. + */ + for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { commit = old_crtc_state->commit; @@ -2274,6 +2404,28 @@ int drm_atomic_helper_swap_state(struct drm_atomic_state *state, if (ret) return ret; } + + for_each_old_connector_in_state(state, connector, old_conn_state, i) { + commit = old_conn_state->commit; + + if (!commit) + continue; + + ret = wait_for_completion_interruptible(&commit->hw_done); + if (ret) + return ret; + } + + for_each_old_plane_in_state(state, plane, old_plane_state, i) { + commit = old_plane_state->commit; + + if (!commit) + continue; + + ret = wait_for_completion_interruptible(&commit->hw_done); + if (ret) + return ret; + } } for_each_oldnew_connector_in_state(state, connector, old_conn_state, new_conn_state, i) { @@ -3242,6 +3394,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane, drm_framebuffer_get(state->fb); state->fence = NULL; + state->commit = NULL; } EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state); @@ -3283,6 +3436,9 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state) if (state->fence) dma_fence_put(state->fence); + + if (state->commit) + drm_crtc_commit_put(state->commit); } EXPORT_SYMBOL(__drm_atomic_helper_plane_destroy_state); @@ -3361,6 +3517,7 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, memcpy(state, connector->state, sizeof(*state)); if (state->crtc) drm_connector_get(connector); + state->commit = NULL; } EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); @@ -3487,6 +3644,9 @@ __drm_atomic_helper_connector_destroy_state(struct drm_connector_state *state) { if (state->crtc) drm_connector_put(state->connector); + + if (state->commit) + drm_crtc_commit_put(state->commit); } EXPORT_SYMBOL(__drm_atomic_helper_connector_destroy_state); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 67a8b6d9fd91..2e7376f9019f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13616,8 +13616,10 @@ intel_legacy_cursor_update(struct drm_plane *plane, /* Swap plane state */ new_plane_state->fence = old_plane_state->fence; + new_plane_state->commit = old_plane_state->commit; *to_intel_plane_state(old_plane_state) = *to_intel_plane_state(new_plane_state); new_plane_state->fence = NULL; + new_plane_state->commit = NULL; new_plane_state->fb = old_fb; to_intel_plane_state(new_plane_state)->vma = old_vma; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index a80a8dadef00..07a71daa3582 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -235,6 +235,18 @@ struct drm_atomic_state { struct drm_modeset_acquire_ctx *acquire_ctx; + /** + * @fake_commit: + * + * Used for signaling unbound planes/connectors. + * When a connector or plane is not bound to any CRTC, it's still important + * to preserve linearity to prevent the atomic states from being freed to early. + * + * This commit (if set) is not bound to any crtc, but will be completed when + * drm_atomic_helper_commit_hw_done() is called. + */ + struct drm_crtc_commit *fake_commit; + /** * @commit_work: * diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index ea8da401c93c..8837649d16e8 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -347,6 +347,13 @@ struct drm_connector_state { struct drm_atomic_state *state; + /** + * @commit: Tracks the pending commit to prevent use-after-free conditions. + * + * Is only set when @crtc is NULL. + */ + struct drm_crtc_commit *commit; + struct drm_tv_connector_state tv; /** diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 73f90f9d057f..7d96116fd4c4 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -123,6 +123,13 @@ struct drm_plane_state { */ bool visible; + /** + * @commit: Tracks the pending commit to prevent use-after-free conditions. + * + * Is only set when @crtc is NULL. + */ + struct drm_crtc_commit *commit; + struct drm_atomic_state *state; }; -- cgit v1.2.3-59-g8ed1b From 669c9215afea4e3684ef13e54e6908e9ae34f0ae Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 4 Sep 2017 12:48:38 +0200 Subject: drm/atomic: Make async plane update checks work as intended, v2. By always keeping track of the last commit in plane_state, we know whether there is an active update on the plane or not. With that information we can reject the fast update, and force the slowpath to be used as was originally intended. We cannot use plane_state->crtc->state here, because this only mentions the most recent commit for the crtc, but not the planes that were part of it. We specifically care about what the last commit involving this plane is, which can only be tracked with a pointer in the plane state. Changes since v1: - Clean up the whole function here, instead of partially earlier. - Add mention in the commit message why we need commit in plane_state. - Swap plane->state in intel_legacy_cursor_update, instead of reassigning all variables. With this commit We know that the cursor is not part of any active commits so this hack can be removed. Cc: Gustavo Padovan Signed-off-by: Maarten Lankhorst Reviewed-by: Gustavo Padovan Reviewed-by: Daniel Vetter #v1 Link: https://patchwork.freedesktop.org/patch/msgid/20170904104838.23822-7-maarten.lankhorst@linux.intel.com [mlankhorst: Amend commit for merge conflicts with drm-intel] --- drivers/gpu/drm/drm_atomic_helper.c | 53 ++++++++++-------------------------- drivers/gpu/drm/i915/intel_display.c | 26 +++++++++++------- include/drm/drm_plane.h | 5 ++-- 3 files changed, 34 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 820adcda45b8..b97c054f9786 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -1388,35 +1388,31 @@ int drm_atomic_helper_async_check(struct drm_device *dev, { struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; - struct drm_crtc_commit *commit; - struct drm_plane *__plane, *plane = NULL; - struct drm_plane_state *__plane_state, *plane_state = NULL; + struct drm_plane *plane; + struct drm_plane_state *old_plane_state, *new_plane_state; const struct drm_plane_helper_funcs *funcs; - int i, j, n_planes = 0; + int i, n_planes = 0; for_each_new_crtc_in_state(state, crtc, crtc_state, i) { if (drm_atomic_crtc_needs_modeset(crtc_state)) return -EINVAL; } - for_each_new_plane_in_state(state, __plane, __plane_state, i) { + for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) n_planes++; - plane = __plane; - plane_state = __plane_state; - } /* FIXME: we support only single plane updates for now */ - if (!plane || n_planes != 1) + if (n_planes != 1) return -EINVAL; - if (!plane_state->crtc) + if (!new_plane_state->crtc) return -EINVAL; funcs = plane->helper_private; if (!funcs->atomic_async_update) return -EINVAL; - if (plane_state->fence) + if (new_plane_state->fence) return -EINVAL; /* @@ -1424,31 +1420,11 @@ int drm_atomic_helper_async_check(struct drm_device *dev, * the plane. This prevents our async update's changes from getting * overridden by a previous synchronous update's state. */ - for_each_new_crtc_in_state(state, crtc, crtc_state, i) { - if (plane->crtc != crtc) - continue; - - spin_lock(&crtc->commit_lock); - commit = list_first_entry_or_null(&crtc->commit_list, - struct drm_crtc_commit, - commit_entry); - if (!commit) { - spin_unlock(&crtc->commit_lock); - continue; - } - spin_unlock(&crtc->commit_lock); - - if (!crtc->state->state) - continue; + if (old_plane_state->commit && + !try_wait_for_completion(&old_plane_state->commit->hw_done)) + return -EBUSY; - for_each_plane_in_state(crtc->state->state, __plane, - __plane_state, j) { - if (__plane == plane) - return -EINVAL; - } - } - - return funcs->atomic_async_check(plane, plane_state); + return funcs->atomic_async_check(plane, new_plane_state); } EXPORT_SYMBOL(drm_atomic_helper_async_check); @@ -1814,9 +1790,10 @@ int drm_atomic_helper_setup_commit(struct drm_atomic_state *state, } for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) { - /* commit tracked through new_crtc_state->commit, no need to do it explicitly */ - if (new_plane_state->crtc) - continue; + /* + * Unlike connectors, always track planes explicitly for + * async pageflip support. + */ /* Userspace is not allowed to get ahead of the previous * commit with nonblocking ones. */ diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2e7376f9019f..3f505682963f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13548,6 +13548,14 @@ intel_legacy_cursor_update(struct drm_plane *plane, goto slow; old_plane_state = plane->state; + /* + * Don't do an async update if there is an outstanding commit modifying + * the plane. This prevents our async update's changes from getting + * overridden by a previous synchronous update's state. + */ + if (old_plane_state->commit && + !try_wait_for_completion(&old_plane_state->commit->hw_done)) + goto slow; /* * If any parameters change that may affect watermarks, @@ -13609,19 +13617,12 @@ intel_legacy_cursor_update(struct drm_plane *plane, } old_fb = old_plane_state->fb; - old_vma = to_intel_plane_state(old_plane_state)->vma; i915_gem_track_fb(intel_fb_obj(old_fb), intel_fb_obj(fb), intel_plane->frontbuffer_bit); /* Swap plane state */ - new_plane_state->fence = old_plane_state->fence; - new_plane_state->commit = old_plane_state->commit; - *to_intel_plane_state(old_plane_state) = *to_intel_plane_state(new_plane_state); - new_plane_state->fence = NULL; - new_plane_state->commit = NULL; - new_plane_state->fb = old_fb; - to_intel_plane_state(new_plane_state)->vma = old_vma; + plane->state = new_plane_state; if (plane->state->visible) { trace_intel_update_plane(plane, to_intel_crtc(crtc)); @@ -13633,12 +13634,17 @@ intel_legacy_cursor_update(struct drm_plane *plane, intel_plane->disable_plane(intel_plane, to_intel_crtc(crtc)); } - intel_cleanup_plane_fb(plane, new_plane_state); + old_vma = fetch_and_zero(&to_intel_plane_state(old_plane_state)->vma); + if (old_vma) + intel_unpin_fb_vma(old_vma); out_unlock: mutex_unlock(&dev_priv->drm.struct_mutex); out_free: - intel_plane_destroy_state(plane, new_plane_state); + if (ret) + intel_plane_destroy_state(plane, new_plane_state); + else + intel_plane_destroy_state(plane, old_plane_state); return ret; slow: diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 7d96116fd4c4..feb9941d0cdb 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -124,9 +124,10 @@ struct drm_plane_state { bool visible; /** - * @commit: Tracks the pending commit to prevent use-after-free conditions. + * @commit: Tracks the pending commit to prevent use-after-free conditions, + * and for async plane updates. * - * Is only set when @crtc is NULL. + * May be NULL. */ struct drm_crtc_commit *commit; -- cgit v1.2.3-59-g8ed1b From 77ac3b00b13185741effd0d5e2f1f05e4bfef7dc Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 19 Jul 2017 16:39:20 +0200 Subject: drm/atomic: Remove deprecated accessor macros Now that the last users have been converted, we can finally get rid of for_each_obj_in_state, we have better macros to replace them with. Signed-off-by: Maarten Lankhorst Cc: Daniel Vetter Cc: Jani Nikula Cc: Sean Paul Cc: David Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20170719143920.25685-8-maarten.lankhorst@linux.intel.com Reviewed-by: Daniel Vetter --- include/drm/drm_atomic.h | 75 --------------------------------------------- include/drm/drm_connector.h | 3 +- include/drm/drm_crtc.h | 8 ++--- include/drm/drm_plane.h | 8 ++--- 4 files changed, 9 insertions(+), 85 deletions(-) (limited to 'include') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 07a71daa3582..5834580d75bc 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -569,31 +569,6 @@ int __must_check drm_atomic_nonblocking_commit(struct drm_atomic_state *state); void drm_state_dump(struct drm_device *dev, struct drm_printer *p); -/** - * for_each_connector_in_state - iterate over all connectors in an atomic update - * @__state: &struct drm_atomic_state pointer - * @connector: &struct drm_connector iteration cursor - * @connector_state: &struct drm_connector_state iteration cursor - * @__i: int iteration cursor, for macro-internal use - * - * This iterates over all connectors in an atomic update. Note that before the - * software state is committed (by calling drm_atomic_helper_swap_state(), this - * points to the new state, while afterwards it points to the old state. Due to - * this tricky confusion this macro is deprecated. - * - * FIXME: - * - * Replace all usage of this with one of the explicit iterators below and then - * remove this macro. - */ -#define for_each_connector_in_state(__state, connector, connector_state, __i) \ - for ((__i) = 0; \ - (__i) < (__state)->num_connector && \ - ((connector) = (__state)->connectors[__i].ptr, \ - (connector_state) = (__state)->connectors[__i].state, 1); \ - (__i)++) \ - for_each_if (connector) - /** * for_each_oldnew_connector_in_state - iterate over all connectors in an atomic update * @__state: &struct drm_atomic_state pointer @@ -657,31 +632,6 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i)++) \ for_each_if (connector) -/** - * for_each_crtc_in_state - iterate over all CRTCs in an atomic update - * @__state: &struct drm_atomic_state pointer - * @crtc: &struct drm_crtc iteration cursor - * @crtc_state: &struct drm_crtc_state iteration cursor - * @__i: int iteration cursor, for macro-internal use - * - * This iterates over all CRTCs in an atomic update. Note that before the - * software state is committed (by calling drm_atomic_helper_swap_state(), this - * points to the new state, while afterwards it points to the old state. Due to - * this tricky confusion this macro is deprecated. - * - * FIXME: - * - * Replace all usage of this with one of the explicit iterators below and then - * remove this macro. - */ -#define for_each_crtc_in_state(__state, crtc, crtc_state, __i) \ - for ((__i) = 0; \ - (__i) < (__state)->dev->mode_config.num_crtc && \ - ((crtc) = (__state)->crtcs[__i].ptr, \ - (crtc_state) = (__state)->crtcs[__i].state, 1); \ - (__i)++) \ - for_each_if (crtc_state) - /** * for_each_oldnew_crtc_in_state - iterate over all CRTCs in an atomic update * @__state: &struct drm_atomic_state pointer @@ -741,31 +691,6 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i)++) \ for_each_if (crtc) -/** - * for_each_plane_in_state - iterate over all planes in an atomic update - * @__state: &struct drm_atomic_state pointer - * @plane: &struct drm_plane iteration cursor - * @plane_state: &struct drm_plane_state iteration cursor - * @__i: int iteration cursor, for macro-internal use - * - * This iterates over all planes in an atomic update. Note that before the - * software state is committed (by calling drm_atomic_helper_swap_state(), this - * points to the new state, while afterwards it points to the old state. Due to - * this tricky confusion this macro is deprecated. - * - * FIXME: - * - * Replace all usage of this with one of the explicit iterators below and then - * remove this macro. - */ -#define for_each_plane_in_state(__state, plane, plane_state, __i) \ - for ((__i) = 0; \ - (__i) < (__state)->dev->mode_config.num_total_plane && \ - ((plane) = (__state)->planes[__i].ptr, \ - (plane_state) = (__state)->planes[__i].state, 1); \ - (__i)++) \ - for_each_if (plane_state) - /** * for_each_oldnew_plane_in_state - iterate over all planes in an atomic update * @__state: &struct drm_atomic_state pointer diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 8837649d16e8..b34904dc8b9b 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -895,8 +895,7 @@ struct drm_connector { * This is protected by @drm_mode_config.connection_mutex. Note that * nonblocking atomic commits access the current connector state without * taking locks. Either by going through the &struct drm_atomic_state - * pointers, see for_each_connector_in_state(), - * for_each_oldnew_connector_in_state(), + * pointers, see for_each_oldnew_connector_in_state(), * for_each_old_connector_in_state() and * for_each_new_connector_in_state(). Or through careful ordering of * atomic commit operations as implemented in the atomic helpers, see diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 901f5c054a2c..80c97210eda5 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -806,10 +806,10 @@ struct drm_crtc { * This is protected by @mutex. Note that nonblocking atomic commits * access the current CRTC state without taking locks. Either by going * through the &struct drm_atomic_state pointers, see - * for_each_crtc_in_state(), for_each_oldnew_crtc_in_state(), - * for_each_old_crtc_in_state() and for_each_new_crtc_in_state(). Or - * through careful ordering of atomic commit operations as implemented - * in the atomic helpers, see &struct drm_crtc_commit. + * for_each_oldnew_crtc_in_state(), for_each_old_crtc_in_state() and + * for_each_new_crtc_in_state(). Or through careful ordering of atomic + * commit operations as implemented in the atomic helpers, see + * &struct drm_crtc_commit. */ struct drm_crtc_state *state; diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index feb9941d0cdb..82a217bd77f0 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -539,10 +539,10 @@ struct drm_plane { * This is protected by @mutex. Note that nonblocking atomic commits * access the current plane state without taking locks. Either by going * through the &struct drm_atomic_state pointers, see - * for_each_plane_in_state(), for_each_oldnew_plane_in_state(), - * for_each_old_plane_in_state() and for_each_new_plane_in_state(). Or - * through careful ordering of atomic commit operations as implemented - * in the atomic helpers, see &struct drm_crtc_commit. + * for_each_oldnew_plane_in_state(), for_each_old_plane_in_state() and + * for_each_new_plane_in_state(). Or through careful ordering of atomic + * commit operations as implemented in the atomic helpers, see + * &struct drm_crtc_commit. */ struct drm_plane_state *state; -- cgit v1.2.3-59-g8ed1b From 0bb9c2b27f5e503e683ad27a0f0a6286f5b5da34 Mon Sep 17 00:00:00 2001 From: Dhinakaran Pandiyan Date: Wed, 6 Sep 2017 17:14:58 -0700 Subject: drm/dp/mst: Sideband message transaction to power up/down nodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The POWER_DOWN_PHY and POWER_UP_PHY sideband message transactions allow the source to reqest any node in a mst path or a whole path to be powered down or up. This allows drivers to target a specific sink in the MST topology, an improvement over just power managing the imediate downstream device. Secondly, since the request-reply protocol waits for an ACK, we can be sure that a downstream sink has enough time to respond to a power up/down request. v2: Fix memory leak (Lyude) Cc: Lyude Cc: Ville Syrjälä Cc: Harry Wentland Signed-off-by: Dhinakaran Pandiyan Link: https://patchwork.freedesktop.org/patch/msgid/20170907001458.9399-1-dhinakaran.pandiyan@intel.com Reviewed-by: Lyude Paul Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/drm_dp_mst_topology.c | 74 +++++++++++++++++++++++++++++++++++ include/drm/drm_dp_mst_helper.h | 2 + 2 files changed, 76 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 41b492f99955..70dcfa58d3c2 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -294,6 +294,12 @@ static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req, memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes); idx += req->u.i2c_write.num_bytes; break; + + case DP_POWER_DOWN_PHY: + case DP_POWER_UP_PHY: + buf[idx] = (req->u.port_num.port_number & 0xf) << 4; + idx++; + break; } raw->cur_len = idx; } @@ -538,6 +544,21 @@ fail_len: return false; } +static bool drm_dp_sideband_parse_power_updown_phy_ack(struct drm_dp_sideband_msg_rx *raw, + struct drm_dp_sideband_msg_reply_body *repmsg) +{ + int idx = 1; + + repmsg->u.port_number.port_number = (raw->msg[idx] >> 4) & 0xf; + idx++; + if (idx > raw->curlen) { + DRM_DEBUG_KMS("power up/down phy parse length fail %d %d\n", + idx, raw->curlen); + return false; + } + return true; +} + static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw, struct drm_dp_sideband_msg_reply_body *msg) { @@ -567,6 +588,9 @@ static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw, return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg); case DP_ALLOCATE_PAYLOAD: return drm_dp_sideband_parse_allocate_payload_ack(raw, msg); + case DP_POWER_DOWN_PHY: + case DP_POWER_UP_PHY: + return drm_dp_sideband_parse_power_updown_phy_ack(raw, msg); default: DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type); return false; @@ -693,6 +717,22 @@ static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_n return 0; } +static int build_power_updown_phy(struct drm_dp_sideband_msg_tx *msg, + int port_num, bool power_up) +{ + struct drm_dp_sideband_msg_req_body req; + + if (power_up) + req.req_type = DP_POWER_UP_PHY; + else + req.req_type = DP_POWER_DOWN_PHY; + + req.u.port_num.port_number = port_num; + drm_dp_encode_sideband_req(&req, msg); + msg->path_msg = true; + return 0; +} + static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_vcpi *vcpi) { @@ -1724,6 +1764,40 @@ fail_put: return ret; } +int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, bool power_up) +{ + struct drm_dp_sideband_msg_tx *txmsg; + int len, ret; + + port = drm_dp_get_validated_port_ref(mgr, port); + if (!port) + return -EINVAL; + + txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL); + if (!txmsg) { + drm_dp_put_port(port); + return -ENOMEM; + } + + txmsg->dst = port->parent; + len = build_power_updown_phy(txmsg, port->port_num, power_up); + drm_dp_queue_down_tx(mgr, txmsg); + + ret = drm_dp_mst_wait_tx_reply(port->parent, txmsg); + if (ret > 0) { + if (txmsg->reply.reply_type == 1) + ret = -EINVAL; + else + ret = 0; + } + kfree(txmsg); + drm_dp_put_port(port); + + return ret; +} +EXPORT_SYMBOL(drm_dp_send_power_updown_phy); + static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr, int id, struct drm_dp_payload *payload) diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h index d55abb75f29a..7f78d26a0766 100644 --- a/include/drm/drm_dp_mst_helper.h +++ b/include/drm/drm_dp_mst_helper.h @@ -631,5 +631,7 @@ int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state, int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state, struct drm_dp_mst_topology_mgr *mgr, int slots); +int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_port *port, bool power_up); #endif -- cgit v1.2.3-59-g8ed1b From 6f8bcc744aad50d719845e4ce06a7831e96e1109 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Tue, 12 Sep 2017 15:37:44 +0200 Subject: drm/atomic: Prepare drm_modeset_lock infrastructure for interruptible waiting, v2. When we want to make drm_atomic_commit interruptible, there are a lot of places that call the lock function, which we don't have control over. Rather than trying to convert every single one, it's easier to toggle interruptible waiting per acquire_ctx. If drm_modeset_acquire_init is called with DRM_MODESET_ACQUIRE_INTERRUPTIBLE, then we will perform interruptible waits in drm_modeset_lock and drm_modeset_backoff. Changes since v1: - Fix locking example in drm_modeset_lock.c to be compatible with interruptible waiting (xexaxo) and make it default. Uninterruptible waiting shouldn't happen except in corner cases, but the example will still apply if the flag is removed. - Add drm_modeset_lock_single_interruptible() to documentation. - Fix dead link to removed drm_modeset_lock_interruptible() in drm_modeset_lock(). Signed-off-by: Maarten Lankhorst Reviewed-by: Daniel Vetter #v1 Cc: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/20170912133749.6532-2-maarten.lankhorst@linux.intel.com Reviewed-by: Emil Velikov --- drivers/gpu/drm/drm_debugfs_crc.c | 2 +- drivers/gpu/drm/drm_modeset_lock.c | 96 +++++++++++++++++++------------------- include/drm/drm_modeset_lock.h | 12 +++-- 3 files changed, 57 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_debugfs_crc.c b/drivers/gpu/drm/drm_debugfs_crc.c index f9e26dda56d6..9dd879589a2c 100644 --- a/drivers/gpu/drm/drm_debugfs_crc.c +++ b/drivers/gpu/drm/drm_debugfs_crc.c @@ -155,7 +155,7 @@ static int crtc_crc_open(struct inode *inode, struct file *filep) int ret = 0; if (drm_drv_uses_atomic_modeset(crtc->dev)) { - ret = drm_modeset_lock_interruptible(&crtc->mutex, NULL); + ret = drm_modeset_lock_single_interruptible(&crtc->mutex); if (ret) return ret; diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index af4e906c630d..e123497da0ca 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -39,23 +39,28 @@ * * The basic usage pattern is to:: * - * drm_modeset_acquire_init(&ctx) + * drm_modeset_acquire_init(ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE) * retry: * foreach (lock in random_ordered_set_of_locks) { - * ret = drm_modeset_lock(lock, &ctx) + * ret = drm_modeset_lock(lock, ctx) * if (ret == -EDEADLK) { - * drm_modeset_backoff(&ctx); - * goto retry; + * ret = drm_modeset_backoff(ctx); + * if (!ret) + * goto retry; * } + * if (ret) + * goto out; * } * ... do stuff ... - * drm_modeset_drop_locks(&ctx); - * drm_modeset_acquire_fini(&ctx); + * out: + * drm_modeset_drop_locks(ctx); + * drm_modeset_acquire_fini(ctx); * * If all that is needed is a single modeset lock, then the &struct * drm_modeset_acquire_ctx is not needed and the locking can be simplified - * by passing a NULL instead of ctx in the drm_modeset_lock() - * call and, when done, by calling drm_modeset_unlock(). + * by passing a NULL instead of ctx in the drm_modeset_lock() call or + * calling drm_modeset_lock_single_interruptible(). To unlock afterwards + * call drm_modeset_unlock(). * * On top of these per-object locks using &ww_mutex there's also an overall * &drm_mode_config.mutex, for protecting everything else. Mostly this means @@ -178,7 +183,11 @@ EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); /** * drm_modeset_acquire_init - initialize acquire context * @ctx: the acquire context - * @flags: for future + * @flags: 0 or %DRM_MODESET_ACQUIRE_INTERRUPTIBLE + * + * When passing %DRM_MODESET_ACQUIRE_INTERRUPTIBLE to @flags, + * all calls to drm_modeset_lock() will perform an interruptible + * wait. */ void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, uint32_t flags) @@ -186,6 +195,9 @@ void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, memset(ctx, 0, sizeof(*ctx)); ww_acquire_init(&ctx->ww_ctx, &crtc_ww_class); INIT_LIST_HEAD(&ctx->locked); + + if (flags & DRM_MODESET_ACQUIRE_INTERRUPTIBLE) + ctx->interruptible = true; } EXPORT_SYMBOL(drm_modeset_acquire_init); @@ -261,8 +273,19 @@ static inline int modeset_lock(struct drm_modeset_lock *lock, return ret; } -static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx, - bool interruptible) +/** + * drm_modeset_backoff - deadlock avoidance backoff + * @ctx: the acquire context + * + * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK), + * you must call this function to drop all currently held locks and + * block until the contended lock becomes available. + * + * This function returns 0 on success, or -ERESTARTSYS if this context + * is initialized with %DRM_MODESET_ACQUIRE_INTERRUPTIBLE and the + * wait has been interrupted. + */ +int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) { struct drm_modeset_lock *contended = ctx->contended; @@ -273,35 +296,10 @@ static int modeset_backoff(struct drm_modeset_acquire_ctx *ctx, drm_modeset_drop_locks(ctx); - return modeset_lock(contended, ctx, interruptible, true); -} - -/** - * drm_modeset_backoff - deadlock avoidance backoff - * @ctx: the acquire context - * - * If deadlock is detected (ie. drm_modeset_lock() returns -EDEADLK), - * you must call this function to drop all currently held locks and - * block until the contended lock becomes available. - */ -void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx) -{ - modeset_backoff(ctx, false); + return modeset_lock(contended, ctx, ctx->interruptible, true); } EXPORT_SYMBOL(drm_modeset_backoff); -/** - * drm_modeset_backoff_interruptible - deadlock avoidance backoff - * @ctx: the acquire context - * - * Interruptible version of drm_modeset_backoff() - */ -int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx) -{ - return modeset_backoff(ctx, true); -} -EXPORT_SYMBOL(drm_modeset_backoff_interruptible); - /** * drm_modeset_lock_init - initialize lock * @lock: lock to init @@ -324,14 +322,18 @@ EXPORT_SYMBOL(drm_modeset_lock_init); * deadlock scenario has been detected and it is an error to attempt * to take any more locks without first calling drm_modeset_backoff(). * + * If the @ctx is not NULL and initialized with + * %DRM_MODESET_ACQUIRE_INTERRUPTIBLE, this function will fail with + * -ERESTARTSYS when interrupted. + * * If @ctx is NULL then the function call behaves like a normal, - * non-nesting mutex_lock() call. + * uninterruptible non-nesting mutex_lock() call. */ int drm_modeset_lock(struct drm_modeset_lock *lock, struct drm_modeset_acquire_ctx *ctx) { if (ctx) - return modeset_lock(lock, ctx, false, false); + return modeset_lock(lock, ctx, ctx->interruptible, false); ww_mutex_lock(&lock->mutex, NULL); return 0; @@ -339,21 +341,19 @@ int drm_modeset_lock(struct drm_modeset_lock *lock, EXPORT_SYMBOL(drm_modeset_lock); /** - * drm_modeset_lock_interruptible - take modeset lock + * drm_modeset_lock_single_interruptible - take a single modeset lock * @lock: lock to take - * @ctx: acquire ctx * - * Interruptible version of drm_modeset_lock() + * This function behaves as drm_modeset_lock() with a NULL context, + * but performs interruptible waits. + * + * This function returns 0 on success, or -ERESTARTSYS when interrupted. */ -int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock, - struct drm_modeset_acquire_ctx *ctx) +int drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock) { - if (ctx) - return modeset_lock(lock, ctx, true, false); - return ww_mutex_lock_interruptible(&lock->mutex, NULL); } -EXPORT_SYMBOL(drm_modeset_lock_interruptible); +EXPORT_SYMBOL(drm_modeset_lock_single_interruptible); /** * drm_modeset_unlock - drop modeset lock diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h index 4b27c2bb955c..a685d1bb21f2 100644 --- a/include/drm/drm_modeset_lock.h +++ b/include/drm/drm_modeset_lock.h @@ -34,6 +34,7 @@ struct drm_modeset_lock; * @contended: used internally for -EDEADLK handling * @locked: list of held locks * @trylock_only: trylock mode used in atomic contexts/panic notifiers + * @interruptible: whether interruptible locking should be used. * * Each thread competing for a set of locks must use one acquire * ctx. And if any lock fxn returns -EDEADLK, it must backoff and @@ -59,6 +60,9 @@ struct drm_modeset_acquire_ctx { * Trylock mode, use only for panic handlers! */ bool trylock_only; + + /* Perform interruptible waits on this context. */ + bool interruptible; }; /** @@ -82,12 +86,13 @@ struct drm_modeset_lock { struct list_head head; }; +#define DRM_MODESET_ACQUIRE_INTERRUPTIBLE BIT(0) + void drm_modeset_acquire_init(struct drm_modeset_acquire_ctx *ctx, uint32_t flags); void drm_modeset_acquire_fini(struct drm_modeset_acquire_ctx *ctx); void drm_modeset_drop_locks(struct drm_modeset_acquire_ctx *ctx); -void drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx); -int drm_modeset_backoff_interruptible(struct drm_modeset_acquire_ctx *ctx); +int drm_modeset_backoff(struct drm_modeset_acquire_ctx *ctx); void drm_modeset_lock_init(struct drm_modeset_lock *lock); @@ -111,8 +116,7 @@ static inline bool drm_modeset_is_locked(struct drm_modeset_lock *lock) int drm_modeset_lock(struct drm_modeset_lock *lock, struct drm_modeset_acquire_ctx *ctx); -int drm_modeset_lock_interruptible(struct drm_modeset_lock *lock, - struct drm_modeset_acquire_ctx *ctx); +int __must_check drm_modeset_lock_single_interruptible(struct drm_modeset_lock *lock); void drm_modeset_unlock(struct drm_modeset_lock *lock); struct drm_device; -- cgit v1.2.3-59-g8ed1b From 3fd3a6ffe279827543cbf4063e307ed18bad6763 Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Wed, 13 Sep 2017 14:52:55 +0300 Subject: drm/i915: Simplify i915_reg_read_ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert to use the freshly available made INTEL_GEN_MASK for easier grepping and improve function readability and clarify the UABI documentation. No functional changes. v2: - Lift GEM_BUG_ONs and use is_power_of_2 (Chris) - Retain -EINVAL on bad flags behavior (Chris) v3: - Extract flags with 'entry->size - 1' (Chris) v4: - Add GEM_BUG_ON on for flags vs entry offset (Chris) v5: - Use 'u16' to match 'dev_priv' (Ville) v6: - Fix checkpatch.pl errors Signed-off-by: Joonas Lahtinen Cc: Tvrtko Ursulin Cc: Chris Wilson Cc: Ville Syrjälä Reviewed-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20170913115255.13851-2-joonas.lahtinen@linux.intel.com --- drivers/gpu/drm/i915/intel_uncore.c | 95 +++++++++++++++++-------------------- include/uapi/drm/i915_drm.h | 6 ++- 2 files changed, 48 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 1b38eb94d461..97525de2cee4 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -1292,72 +1292,65 @@ void intel_uncore_fini(struct drm_i915_private *dev_priv) intel_uncore_forcewake_reset(dev_priv, false); } -#define GEN_RANGE(l, h) GENMASK((h) - 1, (l) - 1) - -static const struct register_whitelist { - i915_reg_t offset_ldw, offset_udw; - uint32_t size; - /* supported gens, 0x10 for 4, 0x30 for 4 and 5, etc. */ - uint32_t gen_bitmask; -} whitelist[] = { - { .offset_ldw = RING_TIMESTAMP(RENDER_RING_BASE), - .offset_udw = RING_TIMESTAMP_UDW(RENDER_RING_BASE), - .size = 8, .gen_bitmask = GEN_RANGE(4, 10) }, -}; +static const struct reg_whitelist { + i915_reg_t offset_ldw; + i915_reg_t offset_udw; + u16 gen_mask; + u8 size; +} reg_read_whitelist[] = { { + .offset_ldw = RING_TIMESTAMP(RENDER_RING_BASE), + .offset_udw = RING_TIMESTAMP_UDW(RENDER_RING_BASE), + .gen_mask = INTEL_GEN_MASK(4, 10), + .size = 8 +} }; int i915_reg_read_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { struct drm_i915_private *dev_priv = to_i915(dev); struct drm_i915_reg_read *reg = data; - struct register_whitelist const *entry = whitelist; - unsigned size; - i915_reg_t offset_ldw, offset_udw; - int i, ret = 0; - - for (i = 0; i < ARRAY_SIZE(whitelist); i++, entry++) { - if (i915_mmio_reg_offset(entry->offset_ldw) == (reg->offset & -entry->size) && - (INTEL_INFO(dev_priv)->gen_mask & entry->gen_bitmask)) + struct reg_whitelist const *entry; + unsigned int flags; + int remain; + int ret = 0; + + entry = reg_read_whitelist; + remain = ARRAY_SIZE(reg_read_whitelist); + while (remain) { + u32 entry_offset = i915_mmio_reg_offset(entry->offset_ldw); + + GEM_BUG_ON(!is_power_of_2(entry->size)); + GEM_BUG_ON(entry->size > 8); + GEM_BUG_ON(entry_offset & (entry->size - 1)); + + if (INTEL_INFO(dev_priv)->gen_mask & entry->gen_mask && + entry_offset == (reg->offset & -entry->size)) break; + entry++; + remain--; } - if (i == ARRAY_SIZE(whitelist)) + if (!remain) return -EINVAL; - /* We use the low bits to encode extra flags as the register should - * be naturally aligned (and those that are not so aligned merely - * limit the available flags for that register). - */ - offset_ldw = entry->offset_ldw; - offset_udw = entry->offset_udw; - size = entry->size; - size |= reg->offset ^ i915_mmio_reg_offset(offset_ldw); + flags = reg->offset & (entry->size - 1); intel_runtime_pm_get(dev_priv); - - switch (size) { - case 8 | 1: - reg->val = I915_READ64_2x32(offset_ldw, offset_udw); - break; - case 8: - reg->val = I915_READ64(offset_ldw); - break; - case 4: - reg->val = I915_READ(offset_ldw); - break; - case 2: - reg->val = I915_READ16(offset_ldw); - break; - case 1: - reg->val = I915_READ8(offset_ldw); - break; - default: + if (entry->size == 8 && flags == I915_REG_READ_8B_WA) + reg->val = I915_READ64_2x32(entry->offset_ldw, + entry->offset_udw); + else if (entry->size == 8 && flags == 0) + reg->val = I915_READ64(entry->offset_ldw); + else if (entry->size == 4 && flags == 0) + reg->val = I915_READ(entry->offset_ldw); + else if (entry->size == 2 && flags == 0) + reg->val = I915_READ16(entry->offset_ldw); + else if (entry->size == 1 && flags == 0) + reg->val = I915_READ8(entry->offset_ldw); + else ret = -EINVAL; - goto out; - } - -out: intel_runtime_pm_put(dev_priv); + return ret; } diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index d8d10d932759..b4505d55990d 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -1308,14 +1308,16 @@ struct drm_i915_reg_read { * be specified */ __u64 offset; +#define I915_REG_READ_8B_WA BIT(0) + __u64 val; /* Return value */ }; /* Known registers: * * Render engine timestamp - 0x2358 + 64bit - gen7+ * - Note this register returns an invalid value if using the default - * single instruction 8byte read, in order to workaround that use - * offset (0x2538 | 1) instead. + * single instruction 8byte read, in order to workaround that pass + * flag I915_REG_READ_8B_WA in offset field. * */ -- cgit v1.2.3-59-g8ed1b From 439e7271dc2b63de379e37971dc2f64d71e24f8a Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Thu, 31 Aug 2017 16:37:41 -0400 Subject: livepatch: introduce shadow variable API Add exported API for livepatch modules: klp_shadow_get() klp_shadow_alloc() klp_shadow_get_or_alloc() klp_shadow_free() klp_shadow_free_all() that implement "shadow" variables, which allow callers to associate new shadow fields to existing data structures. This is intended to be used by livepatch modules seeking to emulate additions to data structure definitions. See Documentation/livepatch/shadow-vars.txt for a summary of the new shadow variable API, including a few common use cases. See samples/livepatch/livepatch-shadow-* for example modules that demonstrate shadow variables. [jkosina@suse.cz: fix __klp_shadow_get_or_alloc() comment as spotted by Josh] Signed-off-by: Joe Lawrence Acked-by: Josh Poimboeuf Acked-by: Miroslav Benes Signed-off-by: Jiri Kosina --- Documentation/livepatch/shadow-vars.txt | 192 +++++++++++++++++++++ include/linux/livepatch.h | 8 + kernel/livepatch/Makefile | 2 +- kernel/livepatch/shadow.c | 277 ++++++++++++++++++++++++++++++ samples/Kconfig | 5 +- samples/livepatch/Makefile | 3 + samples/livepatch/livepatch-shadow-fix1.c | 173 +++++++++++++++++++ samples/livepatch/livepatch-shadow-fix2.c | 168 ++++++++++++++++++ samples/livepatch/livepatch-shadow-mod.c | 224 ++++++++++++++++++++++++ 9 files changed, 1048 insertions(+), 4 deletions(-) create mode 100644 Documentation/livepatch/shadow-vars.txt create mode 100644 kernel/livepatch/shadow.c create mode 100644 samples/livepatch/livepatch-shadow-fix1.c create mode 100644 samples/livepatch/livepatch-shadow-fix2.c create mode 100644 samples/livepatch/livepatch-shadow-mod.c (limited to 'include') diff --git a/Documentation/livepatch/shadow-vars.txt b/Documentation/livepatch/shadow-vars.txt new file mode 100644 index 000000000000..e3ffc4301bfa --- /dev/null +++ b/Documentation/livepatch/shadow-vars.txt @@ -0,0 +1,192 @@ +================ +Shadow Variables +================ + +Shadow variables are a simple way for livepatch modules to associate +additional "shadow" data with existing data structures. Shadow data is +allocated separately from parent data structures, which are left +unmodified. The shadow variable API described in this document is used +to allocate/attach and detach/release shadow variables to their parents. + +The implementation introduces a global, in-kernel hashtable that +associates pointers to parent objects and a numeric identifier of the +shadow data. The numeric identifier is a simple enumeration that may be +used to describe shadow variable version, class or type, etc. More +specifically, the parent pointer serves as the hashtable key while the +numeric id subsequently filters hashtable queries. Multiple shadow +variables may attach to the same parent object, but their numeric +identifier distinguishes between them. + + +1. Brief API summary +==================== + +(See the full API usage docbook notes in livepatch/shadow.c.) + +A hashtable references all shadow variables. These references are +stored and retrieved through a pair. + +* The klp_shadow variable data structure encapsulates both tracking +meta-data and shadow-data: + - meta-data + - obj - pointer to parent object + - id - data identifier + - data[] - storage for shadow data + +It is important to note that the klp_shadow_alloc() and +klp_shadow_get_or_alloc() calls, described below, store a *copy* of the +data that the functions are provided. Callers should provide whatever +mutual exclusion is required of the shadow data. + +* klp_shadow_get() - retrieve a shadow variable data pointer + - search hashtable for pair + +* klp_shadow_alloc() - allocate and add a new shadow variable + - search hashtable for pair + - if exists + - WARN and return NULL + - if doesn't already exist + - allocate a new shadow variable + - copy data into the new shadow variable + - add to the global hashtable + +* klp_shadow_get_or_alloc() - get existing or alloc a new shadow variable + - search hashtable for pair + - if exists + - return existing shadow variable + - if doesn't already exist + - allocate a new shadow variable + - copy data into the new shadow variable + - add pair to the global hashtable + +* klp_shadow_free() - detach and free a shadow variable + - find and remove a reference from global hashtable + - if found, free shadow variable + +* klp_shadow_free_all() - detach and free all <*, id> shadow variables + - find and remove any <*, id> references from global hashtable + - if found, free shadow variable + + +2. Use cases +============ + +(See the example shadow variable livepatch modules in samples/livepatch/ +for full working demonstrations.) + +For the following use-case examples, consider commit 1d147bfa6429 +("mac80211: fix AP powersave TX vs. wakeup race"), which added a +spinlock to net/mac80211/sta_info.h :: struct sta_info. Each use-case +example can be considered a stand-alone livepatch implementation of this +fix. + + +Matching parent's lifecycle +--------------------------- + +If parent data structures are frequently created and destroyed, it may +be easiest to align their shadow variables lifetimes to the same +allocation and release functions. In this case, the parent data +structure is typically allocated, initialized, then registered in some +manner. Shadow variable allocation and setup can then be considered +part of the parent's initialization and should be completed before the +parent "goes live" (ie, any shadow variable get-API requests are made +for this pair.) + +For commit 1d147bfa6429, when a parent sta_info structure is allocated, +allocate a shadow copy of the ps_lock pointer, then initialize it: + +#define PS_LOCK 1 +struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, + const u8 *addr, gfp_t gfp) +{ + struct sta_info *sta; + spinlock_t *ps_lock; + + /* Parent structure is created */ + sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp); + + /* Attach a corresponding shadow variable, then initialize it */ + ps_lock = klp_shadow_alloc(sta, PS_LOCK, NULL, sizeof(ps_lock), gfp); + if (!ps_lock) + goto shadow_fail; + spin_lock_init(ps_lock); + ... + +When requiring a ps_lock, query the shadow variable API to retrieve one +for a specific struct sta_info: + +void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) +{ + spinlock_t *ps_lock; + + /* sync with ieee80211_tx_h_unicast_ps_buf */ + ps_lock = klp_shadow_get(sta, PS_LOCK); + if (ps_lock) + spin_lock(ps_lock); + ... + +When the parent sta_info structure is freed, first free the shadow +variable: + +void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) +{ + klp_shadow_free(sta, PS_LOCK); + kfree(sta); + ... + + +In-flight parent objects +------------------------ + +Sometimes it may not be convenient or possible to allocate shadow +variables alongside their parent objects. Or a livepatch fix may +require shadow varibles to only a subset of parent object instances. In +these cases, the klp_shadow_get_or_alloc() call can be used to attach +shadow variables to parents already in-flight. + +For commit 1d147bfa6429, a good spot to allocate a shadow spinlock is +inside ieee80211_sta_ps_deliver_wakeup(): + +#define PS_LOCK 1 +void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) +{ + DEFINE_SPINLOCK(ps_lock_fallback); + spinlock_t *ps_lock; + + /* sync with ieee80211_tx_h_unicast_ps_buf */ + ps_lock = klp_shadow_get_or_alloc(sta, PS_LOCK, + &ps_lock_fallback, sizeof(ps_lock_fallback), + GFP_ATOMIC); + if (ps_lock) + spin_lock(ps_lock); + ... + +This usage will create a shadow variable, only if needed, otherwise it +will use one that was already created for this pair. + +Like the previous use-case, the shadow spinlock needs to be cleaned up. +A shadow variable can be freed just before its parent object is freed, +or even when the shadow variable itself is no longer required. + + +Other use-cases +--------------- + +Shadow variables can also be used as a flag indicating that a data +structure was allocated by new, livepatched code. In this case, it +doesn't matter what data value the shadow variable holds, its existence +suggests how to handle the parent object. + + +3. References +============= + +* https://github.com/dynup/kpatch +The livepatch implementation is based on the kpatch version of shadow +variables. + +* http://files.mkgnu.net/files/dynamos/doc/papers/dynamos_eurosys_07.pdf +Dynamic and Adaptive Updates of Non-Quiescent Subsystems in Commodity +Operating System Kernels (Kritis Makris, Kyung Dong Ryu 2007) presented +a datatype update technique called "shadow data structures". diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index 194991ef9347..d08eddc00497 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -164,6 +164,14 @@ static inline bool klp_have_reliable_stack(void) IS_ENABLED(CONFIG_HAVE_RELIABLE_STACKTRACE); } +void *klp_shadow_get(void *obj, unsigned long id); +void *klp_shadow_alloc(void *obj, unsigned long id, void *data, + size_t size, gfp_t gfp_flags); +void *klp_shadow_get_or_alloc(void *obj, unsigned long id, void *data, + size_t size, gfp_t gfp_flags); +void klp_shadow_free(void *obj, unsigned long id); +void klp_shadow_free_all(unsigned long id); + #else /* !CONFIG_LIVEPATCH */ static inline int klp_module_coming(struct module *mod) { return 0; } diff --git a/kernel/livepatch/Makefile b/kernel/livepatch/Makefile index 2b8bdb1925da..b36ceda6488e 100644 --- a/kernel/livepatch/Makefile +++ b/kernel/livepatch/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_LIVEPATCH) += livepatch.o -livepatch-objs := core.o patch.o transition.o +livepatch-objs := core.o patch.o shadow.o transition.o diff --git a/kernel/livepatch/shadow.c b/kernel/livepatch/shadow.c new file mode 100644 index 000000000000..67e4360521f3 --- /dev/null +++ b/kernel/livepatch/shadow.c @@ -0,0 +1,277 @@ +/* + * shadow.c - Shadow Variables + * + * Copyright (C) 2014 Josh Poimboeuf + * Copyright (C) 2014 Seth Jennings + * Copyright (C) 2017 Joe Lawrence + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/** + * DOC: Shadow variable API concurrency notes: + * + * The shadow variable API provides a simple relationship between an + * pair and a pointer value. It is the responsibility of the + * caller to provide any mutual exclusion required of the shadow data. + * + * Once a shadow variable is attached to its parent object via the + * klp_shadow_*alloc() API calls, it is considered live: any subsequent + * call to klp_shadow_get() may then return the shadow variable's data + * pointer. Callers of klp_shadow_*alloc() should prepare shadow data + * accordingly. + * + * The klp_shadow_*alloc() API calls may allocate memory for new shadow + * variable structures. Their implementation does not call kmalloc + * inside any spinlocks, but API callers should pass GFP flags according + * to their specific needs. + * + * The klp_shadow_hash is an RCU-enabled hashtable and is safe against + * concurrent klp_shadow_free() and klp_shadow_get() operations. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +static DEFINE_HASHTABLE(klp_shadow_hash, 12); + +/* + * klp_shadow_lock provides exclusive access to the klp_shadow_hash and + * the shadow variables it references. + */ +static DEFINE_SPINLOCK(klp_shadow_lock); + +/** + * struct klp_shadow - shadow variable structure + * @node: klp_shadow_hash hash table node + * @rcu_head: RCU is used to safely free this structure + * @obj: pointer to parent object + * @id: data identifier + * @data: data area + */ +struct klp_shadow { + struct hlist_node node; + struct rcu_head rcu_head; + void *obj; + unsigned long id; + char data[]; +}; + +/** + * klp_shadow_match() - verify a shadow variable matches given + * @shadow: shadow variable to match + * @obj: pointer to parent object + * @id: data identifier + * + * Return: true if the shadow variable matches. + */ +static inline bool klp_shadow_match(struct klp_shadow *shadow, void *obj, + unsigned long id) +{ + return shadow->obj == obj && shadow->id == id; +} + +/** + * klp_shadow_get() - retrieve a shadow variable data pointer + * @obj: pointer to parent object + * @id: data identifier + * + * Return: the shadow variable data element, NULL on failure. + */ +void *klp_shadow_get(void *obj, unsigned long id) +{ + struct klp_shadow *shadow; + + rcu_read_lock(); + + hash_for_each_possible_rcu(klp_shadow_hash, shadow, node, + (unsigned long)obj) { + + if (klp_shadow_match(shadow, obj, id)) { + rcu_read_unlock(); + return shadow->data; + } + } + + rcu_read_unlock(); + + return NULL; +} +EXPORT_SYMBOL_GPL(klp_shadow_get); + +void *__klp_shadow_get_or_alloc(void *obj, unsigned long id, void *data, + size_t size, gfp_t gfp_flags, bool warn_on_exist) +{ + struct klp_shadow *new_shadow; + void *shadow_data; + unsigned long flags; + + /* Check if the shadow variable already exists */ + shadow_data = klp_shadow_get(obj, id); + if (shadow_data) + goto exists; + + /* Allocate a new shadow variable for use inside the lock below */ + new_shadow = kzalloc(size + sizeof(*new_shadow), gfp_flags); + if (!new_shadow) + return NULL; + + new_shadow->obj = obj; + new_shadow->id = id; + + /* Initialize the shadow variable if data provided */ + if (data) + memcpy(new_shadow->data, data, size); + + /* Look for again under the lock */ + spin_lock_irqsave(&klp_shadow_lock, flags); + shadow_data = klp_shadow_get(obj, id); + if (unlikely(shadow_data)) { + /* + * Shadow variable was found, throw away speculative + * allocation. + */ + spin_unlock_irqrestore(&klp_shadow_lock, flags); + kfree(new_shadow); + goto exists; + } + + /* No found, so attach the newly allocated one */ + hash_add_rcu(klp_shadow_hash, &new_shadow->node, + (unsigned long)new_shadow->obj); + spin_unlock_irqrestore(&klp_shadow_lock, flags); + + return new_shadow->data; + +exists: + if (warn_on_exist) { + WARN(1, "Duplicate shadow variable <%p, %lx>\n", obj, id); + return NULL; + } + + return shadow_data; +} + +/** + * klp_shadow_alloc() - allocate and add a new shadow variable + * @obj: pointer to parent object + * @id: data identifier + * @data: pointer to data to attach to parent + * @size: size of attached data + * @gfp_flags: GFP mask for allocation + * + * Allocates @size bytes for new shadow variable data using @gfp_flags + * and copies @size bytes from @data into the new shadow variable's own + * data space. If @data is NULL, @size bytes are still allocated, but + * no copy is performed. The new shadow variable is then added to the + * global hashtable. + * + * If an existing shadow variable can be found, this routine + * will issue a WARN, exit early and return NULL. + * + * Return: the shadow variable data element, NULL on duplicate or + * failure. + */ +void *klp_shadow_alloc(void *obj, unsigned long id, void *data, + size_t size, gfp_t gfp_flags) +{ + return __klp_shadow_get_or_alloc(obj, id, data, size, gfp_flags, true); +} +EXPORT_SYMBOL_GPL(klp_shadow_alloc); + +/** + * klp_shadow_get_or_alloc() - get existing or allocate a new shadow variable + * @obj: pointer to parent object + * @id: data identifier + * @data: pointer to data to attach to parent + * @size: size of attached data + * @gfp_flags: GFP mask for allocation + * + * Returns a pointer to existing shadow data if an shadow + * variable is already present. Otherwise, it creates a new shadow + * variable like klp_shadow_alloc(). + * + * This function guarantees that only one shadow variable exists with + * the given @id for the given @obj. It also guarantees that the shadow + * variable will be initialized by the given @data only when it did not + * exist before. + * + * Return: the shadow variable data element, NULL on failure. + */ +void *klp_shadow_get_or_alloc(void *obj, unsigned long id, void *data, + size_t size, gfp_t gfp_flags) +{ + return __klp_shadow_get_or_alloc(obj, id, data, size, gfp_flags, false); +} +EXPORT_SYMBOL_GPL(klp_shadow_get_or_alloc); + +/** + * klp_shadow_free() - detach and free a shadow variable + * @obj: pointer to parent object + * @id: data identifier + * + * This function releases the memory for this shadow variable + * instance, callers should stop referencing it accordingly. + */ +void klp_shadow_free(void *obj, unsigned long id) +{ + struct klp_shadow *shadow; + unsigned long flags; + + spin_lock_irqsave(&klp_shadow_lock, flags); + + /* Delete from hash */ + hash_for_each_possible(klp_shadow_hash, shadow, node, + (unsigned long)obj) { + + if (klp_shadow_match(shadow, obj, id)) { + hash_del_rcu(&shadow->node); + kfree_rcu(shadow, rcu_head); + break; + } + } + + spin_unlock_irqrestore(&klp_shadow_lock, flags); +} +EXPORT_SYMBOL_GPL(klp_shadow_free); + +/** + * klp_shadow_free_all() - detach and free all <*, id> shadow variables + * @id: data identifier + * + * This function releases the memory for all <*, id> shadow variable + * instances, callers should stop referencing them accordingly. + */ +void klp_shadow_free_all(unsigned long id) +{ + struct klp_shadow *shadow; + unsigned long flags; + int i; + + spin_lock_irqsave(&klp_shadow_lock, flags); + + /* Delete all <*, id> from hash */ + hash_for_each(klp_shadow_hash, i, shadow, node) { + if (klp_shadow_match(shadow, shadow->obj, id)) { + hash_del_rcu(&shadow->node); + kfree_rcu(shadow, rcu_head); + } + } + + spin_unlock_irqrestore(&klp_shadow_lock, flags); +} +EXPORT_SYMBOL_GPL(klp_shadow_free_all); diff --git a/samples/Kconfig b/samples/Kconfig index 9cb63188d3ef..c332a3b9de05 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -71,11 +71,10 @@ config SAMPLE_RPMSG_CLIENT the rpmsg bus. config SAMPLE_LIVEPATCH - tristate "Build live patching sample -- loadable modules only" + tristate "Build live patching samples -- loadable modules only" depends on LIVEPATCH && m help - Builds a sample live patch that replaces the procfs handler - for /proc/cmdline to print "this has been live patched". + Build sample live patch demonstrations. config SAMPLE_CONFIGFS tristate "Build configfs patching sample -- loadable modules only" diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile index 10319d7ea0b1..539e81d433cd 100644 --- a/samples/livepatch/Makefile +++ b/samples/livepatch/Makefile @@ -1 +1,4 @@ obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o +obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o +obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o +obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o diff --git a/samples/livepatch/livepatch-shadow-fix1.c b/samples/livepatch/livepatch-shadow-fix1.c new file mode 100644 index 000000000000..fbe0a1f3d99b --- /dev/null +++ b/samples/livepatch/livepatch-shadow-fix1.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2017 Joe Lawrence + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * livepatch-shadow-fix1.c - Shadow variables, livepatch demo + * + * Purpose + * ------- + * + * Fixes the memory leak introduced in livepatch-shadow-mod through the + * use of a shadow variable. This fix demonstrates the "extending" of + * short-lived data structures by patching its allocation and release + * functions. + * + * + * Usage + * ----- + * + * This module is not intended to be standalone. See the "Usage" + * section of livepatch-shadow-mod.c. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +/* Shadow variable enums */ +#define SV_LEAK 1 + +/* Allocate new dummies every second */ +#define ALLOC_PERIOD 1 +/* Check for expired dummies after a few new ones have been allocated */ +#define CLEANUP_PERIOD (3 * ALLOC_PERIOD) +/* Dummies expire after a few cleanup instances */ +#define EXPIRE_PERIOD (4 * CLEANUP_PERIOD) + +struct dummy { + struct list_head list; + unsigned long jiffies_expire; +}; + +struct dummy *livepatch_fix1_dummy_alloc(void) +{ + struct dummy *d; + void *leak; + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) + return NULL; + + d->jiffies_expire = jiffies + + msecs_to_jiffies(1000 * EXPIRE_PERIOD); + + /* + * Patch: save the extra memory location into a SV_LEAK shadow + * variable. A patched dummy_free routine can later fetch this + * pointer to handle resource release. + */ + leak = kzalloc(sizeof(int), GFP_KERNEL); + klp_shadow_alloc(d, SV_LEAK, &leak, sizeof(leak), GFP_KERNEL); + + pr_info("%s: dummy @ %p, expires @ %lx\n", + __func__, d, d->jiffies_expire); + + return d; +} + +void livepatch_fix1_dummy_free(struct dummy *d) +{ + void **shadow_leak, *leak; + + /* + * Patch: fetch the saved SV_LEAK shadow variable, detach and + * free it. Note: handle cases where this shadow variable does + * not exist (ie, dummy structures allocated before this livepatch + * was loaded.) + */ + shadow_leak = klp_shadow_get(d, SV_LEAK); + if (shadow_leak) { + leak = *shadow_leak; + klp_shadow_free(d, SV_LEAK); + kfree(leak); + pr_info("%s: dummy @ %p, prevented leak @ %p\n", + __func__, d, leak); + } else { + pr_info("%s: dummy @ %p leaked!\n", __func__, d); + } + + kfree(d); +} + +static struct klp_func funcs[] = { + { + .old_name = "dummy_alloc", + .new_func = livepatch_fix1_dummy_alloc, + }, + { + .old_name = "dummy_free", + .new_func = livepatch_fix1_dummy_free, + }, { } +}; + +static struct klp_object objs[] = { + { + .name = "livepatch_shadow_mod", + .funcs = funcs, + }, { } +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +static int livepatch_shadow_fix1_init(void) +{ + int ret; + + if (!klp_have_reliable_stack() && !patch.immediate) { + /* + * WARNING: Be very careful when using 'patch.immediate' in + * your patches. It's ok to use it for simple patches like + * this, but for more complex patches which change function + * semantics, locking semantics, or data structures, it may not + * be safe. Use of this option will also prevent removal of + * the patch. + * + * See Documentation/livepatch/livepatch.txt for more details. + */ + patch.immediate = true; + pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n"); + } + + ret = klp_register_patch(&patch); + if (ret) + return ret; + ret = klp_enable_patch(&patch); + if (ret) { + WARN_ON(klp_unregister_patch(&patch)); + return ret; + } + return 0; +} + +static void livepatch_shadow_fix1_exit(void) +{ + /* Cleanup any existing SV_LEAK shadow variables */ + klp_shadow_free_all(SV_LEAK); + + WARN_ON(klp_unregister_patch(&patch)); +} + +module_init(livepatch_shadow_fix1_init); +module_exit(livepatch_shadow_fix1_exit); +MODULE_LICENSE("GPL"); +MODULE_INFO(livepatch, "Y"); diff --git a/samples/livepatch/livepatch-shadow-fix2.c b/samples/livepatch/livepatch-shadow-fix2.c new file mode 100644 index 000000000000..53c1794bdc5f --- /dev/null +++ b/samples/livepatch/livepatch-shadow-fix2.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2017 Joe Lawrence + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * livepatch-shadow-fix2.c - Shadow variables, livepatch demo + * + * Purpose + * ------- + * + * Adds functionality to livepatch-shadow-mod's in-flight data + * structures through a shadow variable. The livepatch patches a + * routine that periodically inspects data structures, incrementing a + * per-data-structure counter, creating the counter if needed. + * + * + * Usage + * ----- + * + * This module is not intended to be standalone. See the "Usage" + * section of livepatch-shadow-mod.c. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +/* Shadow variable enums */ +#define SV_LEAK 1 +#define SV_COUNTER 2 + +struct dummy { + struct list_head list; + unsigned long jiffies_expire; +}; + +bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies) +{ + int *shadow_count; + int count; + + /* + * Patch: handle in-flight dummy structures, if they do not + * already have a SV_COUNTER shadow variable, then attach a + * new one. + */ + count = 0; + shadow_count = klp_shadow_get_or_alloc(d, SV_COUNTER, + &count, sizeof(count), + GFP_NOWAIT); + if (shadow_count) + *shadow_count += 1; + + return time_after(jiffies, d->jiffies_expire); +} + +void livepatch_fix2_dummy_free(struct dummy *d) +{ + void **shadow_leak, *leak; + int *shadow_count; + + /* Patch: copy the memory leak patch from the fix1 module. */ + shadow_leak = klp_shadow_get(d, SV_LEAK); + if (shadow_leak) { + leak = *shadow_leak; + klp_shadow_free(d, SV_LEAK); + kfree(leak); + pr_info("%s: dummy @ %p, prevented leak @ %p\n", + __func__, d, leak); + } else { + pr_info("%s: dummy @ %p leaked!\n", __func__, d); + } + + /* + * Patch: fetch the SV_COUNTER shadow variable and display + * the final count. Detach the shadow variable. + */ + shadow_count = klp_shadow_get(d, SV_COUNTER); + if (shadow_count) { + pr_info("%s: dummy @ %p, check counter = %d\n", + __func__, d, *shadow_count); + klp_shadow_free(d, SV_COUNTER); + } + + kfree(d); +} + +static struct klp_func funcs[] = { + { + .old_name = "dummy_check", + .new_func = livepatch_fix2_dummy_check, + }, + { + .old_name = "dummy_free", + .new_func = livepatch_fix2_dummy_free, + }, { } +}; + +static struct klp_object objs[] = { + { + .name = "livepatch_shadow_mod", + .funcs = funcs, + }, { } +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +static int livepatch_shadow_fix2_init(void) +{ + int ret; + + if (!klp_have_reliable_stack() && !patch.immediate) { + /* + * WARNING: Be very careful when using 'patch.immediate' in + * your patches. It's ok to use it for simple patches like + * this, but for more complex patches which change function + * semantics, locking semantics, or data structures, it may not + * be safe. Use of this option will also prevent removal of + * the patch. + * + * See Documentation/livepatch/livepatch.txt for more details. + */ + patch.immediate = true; + pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n"); + } + + ret = klp_register_patch(&patch); + if (ret) + return ret; + ret = klp_enable_patch(&patch); + if (ret) { + WARN_ON(klp_unregister_patch(&patch)); + return ret; + } + return 0; +} + +static void livepatch_shadow_fix2_exit(void) +{ + /* Cleanup any existing SV_COUNTER shadow variables */ + klp_shadow_free_all(SV_COUNTER); + + WARN_ON(klp_unregister_patch(&patch)); +} + +module_init(livepatch_shadow_fix2_init); +module_exit(livepatch_shadow_fix2_exit); +MODULE_LICENSE("GPL"); +MODULE_INFO(livepatch, "Y"); diff --git a/samples/livepatch/livepatch-shadow-mod.c b/samples/livepatch/livepatch-shadow-mod.c new file mode 100644 index 000000000000..4c54b250332d --- /dev/null +++ b/samples/livepatch/livepatch-shadow-mod.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2017 Joe Lawrence + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * livepatch-shadow-mod.c - Shadow variables, buggy module demo + * + * Purpose + * ------- + * + * As a demonstration of livepatch shadow variable API, this module + * introduces memory leak behavior that livepatch modules + * livepatch-shadow-fix1.ko and livepatch-shadow-fix2.ko correct and + * enhance. + * + * WARNING - even though the livepatch-shadow-fix modules patch the + * memory leak, please load these modules at your own risk -- some + * amount of memory may leaked before the bug is patched. + * + * + * Usage + * ----- + * + * Step 1 - Load the buggy demonstration module: + * + * insmod samples/livepatch/livepatch-shadow-mod.ko + * + * Watch dmesg output for a few moments to see new dummy being allocated + * and a periodic cleanup check. (Note: a small amount of memory is + * being leaked.) + * + * + * Step 2 - Load livepatch fix1: + * + * insmod samples/livepatch/livepatch-shadow-fix1.ko + * + * Continue watching dmesg and note that now livepatch_fix1_dummy_free() + * and livepatch_fix1_dummy_alloc() are logging messages about leaked + * memory and eventually leaks prevented. + * + * + * Step 3 - Load livepatch fix2 (on top of fix1): + * + * insmod samples/livepatch/livepatch-shadow-fix2.ko + * + * This module extends functionality through shadow variables, as a new + * "check" counter is added to the dummy structure. Periodic dmesg + * messages will log these as dummies are cleaned up. + * + * + * Step 4 - Cleanup + * + * Unwind the demonstration by disabling the livepatch fix modules, then + * removing them and the demo module: + * + * echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix2/enabled + * echo 0 > /sys/kernel/livepatch/livepatch_shadow_fix1/enabled + * rmmod livepatch-shadow-fix2 + * rmmod livepatch-shadow-fix1 + * rmmod livepatch-shadow-mod + */ + + +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joe Lawrence "); +MODULE_DESCRIPTION("Buggy module for shadow variable demo"); + +/* Allocate new dummies every second */ +#define ALLOC_PERIOD 1 +/* Check for expired dummies after a few new ones have been allocated */ +#define CLEANUP_PERIOD (3 * ALLOC_PERIOD) +/* Dummies expire after a few cleanup instances */ +#define EXPIRE_PERIOD (4 * CLEANUP_PERIOD) + +/* + * Keep a list of all the dummies so we can clean up any residual ones + * on module exit + */ +LIST_HEAD(dummy_list); +DEFINE_MUTEX(dummy_list_mutex); + +struct dummy { + struct list_head list; + unsigned long jiffies_expire; +}; + +noinline struct dummy *dummy_alloc(void) +{ + struct dummy *d; + void *leak; + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) + return NULL; + + d->jiffies_expire = jiffies + + msecs_to_jiffies(1000 * EXPIRE_PERIOD); + + /* Oops, forgot to save leak! */ + leak = kzalloc(sizeof(int), GFP_KERNEL); + + pr_info("%s: dummy @ %p, expires @ %lx\n", + __func__, d, d->jiffies_expire); + + return d; +} + +noinline void dummy_free(struct dummy *d) +{ + pr_info("%s: dummy @ %p, expired = %lx\n", + __func__, d, d->jiffies_expire); + + kfree(d); +} + +noinline bool dummy_check(struct dummy *d, unsigned long jiffies) +{ + return time_after(jiffies, d->jiffies_expire); +} + +/* + * alloc_work_func: allocates new dummy structures, allocates additional + * memory, aptly named "leak", but doesn't keep + * permanent record of it. + */ + +static void alloc_work_func(struct work_struct *work); +static DECLARE_DELAYED_WORK(alloc_dwork, alloc_work_func); + +static void alloc_work_func(struct work_struct *work) +{ + struct dummy *d; + + d = dummy_alloc(); + if (!d) + return; + + mutex_lock(&dummy_list_mutex); + list_add(&d->list, &dummy_list); + mutex_unlock(&dummy_list_mutex); + + schedule_delayed_work(&alloc_dwork, + msecs_to_jiffies(1000 * ALLOC_PERIOD)); +} + +/* + * cleanup_work_func: frees dummy structures. Without knownledge of + * "leak", it leaks the additional memory that + * alloc_work_func created. + */ + +static void cleanup_work_func(struct work_struct *work); +static DECLARE_DELAYED_WORK(cleanup_dwork, cleanup_work_func); + +static void cleanup_work_func(struct work_struct *work) +{ + struct dummy *d, *tmp; + unsigned long j; + + j = jiffies; + pr_info("%s: jiffies = %lx\n", __func__, j); + + mutex_lock(&dummy_list_mutex); + list_for_each_entry_safe(d, tmp, &dummy_list, list) { + + /* Kick out and free any expired dummies */ + if (dummy_check(d, j)) { + list_del(&d->list); + dummy_free(d); + } + } + mutex_unlock(&dummy_list_mutex); + + schedule_delayed_work(&cleanup_dwork, + msecs_to_jiffies(1000 * CLEANUP_PERIOD)); +} + +static int livepatch_shadow_mod_init(void) +{ + schedule_delayed_work(&alloc_dwork, + msecs_to_jiffies(1000 * ALLOC_PERIOD)); + schedule_delayed_work(&cleanup_dwork, + msecs_to_jiffies(1000 * CLEANUP_PERIOD)); + + return 0; +} + +static void livepatch_shadow_mod_exit(void) +{ + struct dummy *d, *tmp; + + /* Wait for any dummies at work */ + cancel_delayed_work_sync(&alloc_dwork); + cancel_delayed_work_sync(&cleanup_dwork); + + /* Cleanup residual dummies */ + list_for_each_entry_safe(d, tmp, &dummy_list, list) { + list_del(&d->list); + dummy_free(d); + } +} + +module_init(livepatch_shadow_mod_init); +module_exit(livepatch_shadow_mod_exit); -- cgit v1.2.3-59-g8ed1b From 042ebd293b862c491e31aea17b540317a1b9af21 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Wed, 6 Sep 2017 17:15:04 +0800 Subject: scsi: libsas: kill useless ha_event and do some cleanup The ha_event now has only one event HAE_RESET, and this event does nothing. Kill it and do some cleanup. This is a preparation for enhance libsas hotplug feature in the next patches. Signed-off-by: Jason Yan Signed-off-by: John Garry Reviewed-by: Johannes Thumshirn Reviewed-by: Christoph Hellwig CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams Signed-off-by: Martin K. Petersen --- drivers/scsi/aic94xx/aic94xx_hwi.c | 3 --- drivers/scsi/hisi_sas/hisi_sas_main.c | 1 - drivers/scsi/libsas/sas_dump.c | 10 ---------- drivers/scsi/libsas/sas_dump.h | 1 - drivers/scsi/libsas/sas_event.c | 20 -------------------- drivers/scsi/libsas/sas_init.c | 12 ------------ include/scsi/libsas.h | 21 --------------------- 7 files changed, 68 deletions(-) (limited to 'include') diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c index f2671a8fa7e3..ec09438184a3 100644 --- a/drivers/scsi/aic94xx/aic94xx_hwi.c +++ b/drivers/scsi/aic94xx/aic94xx_hwi.c @@ -721,11 +721,8 @@ Out: */ static void asd_chip_reset(struct asd_ha_struct *asd_ha) { - struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; - ASD_DPRINTK("chip reset for %s\n", pci_name(asd_ha->pcidev)); asd_chip_hardrst(asd_ha); - sas_ha->notify_ha_event(sas_ha, HAE_RESET); } /* ---------- Done List Routines ---------- */ diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 16664f2e15fb..29d6cb39299c 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -1074,7 +1074,6 @@ static int hisi_sas_controller_reset(struct hisi_hba *hisi_hba) hisi_sas_release_tasks(hisi_hba); spin_unlock_irqrestore(&hisi_hba->lock, flags); - sas_ha->notify_ha_event(sas_ha, HAE_RESET); clear_bit(HISI_SAS_REJECT_CMD_BIT, &hisi_hba->flags); /* Init and wait for PHYs to come up and all libsas event finished. */ diff --git a/drivers/scsi/libsas/sas_dump.c b/drivers/scsi/libsas/sas_dump.c index cd6f99c1ae7e..7e5d262e7a7d 100644 --- a/drivers/scsi/libsas/sas_dump.c +++ b/drivers/scsi/libsas/sas_dump.c @@ -24,10 +24,6 @@ #include "sas_dump.h" -static const char *sas_hae_str[] = { - [0] = "HAE_RESET", -}; - static const char *sas_porte_str[] = { [0] = "PORTE_BYTES_DMAED", [1] = "PORTE_BROADCAST_RCVD", @@ -53,12 +49,6 @@ void sas_dprint_phye(int phyid, enum phy_event pe) SAS_DPRINTK("phy%d: phy event: %s\n", phyid, sas_phye_str[pe]); } -void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he) -{ - SAS_DPRINTK("ha %s: %s event\n", dev_name(sas_ha->dev), - sas_hae_str[he]); -} - void sas_dump_port(struct asd_sas_port *port) { SAS_DPRINTK("port%d: class:0x%x\n", port->id, port->class); diff --git a/drivers/scsi/libsas/sas_dump.h b/drivers/scsi/libsas/sas_dump.h index 800e4c69093f..6aaee6b0fcdb 100644 --- a/drivers/scsi/libsas/sas_dump.h +++ b/drivers/scsi/libsas/sas_dump.h @@ -26,5 +26,4 @@ void sas_dprint_porte(int phyid, enum port_event pe); void sas_dprint_phye(int phyid, enum phy_event pe); -void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he); void sas_dump_port(struct asd_sas_port *port); diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index c0d0d979b76d..70c4653edd81 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -124,14 +124,6 @@ void sas_enable_revalidation(struct sas_ha_struct *ha) mutex_unlock(&ha->disco_mutex); } -static int notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) -{ - BUG_ON(event >= HA_NUM_EVENTS); - - return sas_queue_event(event, &sas_ha->pending, - &sas_ha->ha_events[event].work, sas_ha); -} - static int notify_port_event(struct asd_sas_phy *phy, enum port_event event) { struct sas_ha_struct *ha = phy->ha; @@ -154,18 +146,6 @@ int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) int sas_init_events(struct sas_ha_struct *sas_ha) { - static const work_func_t sas_ha_event_fns[HA_NUM_EVENTS] = { - [HAE_RESET] = sas_hae_reset, - }; - - int i; - - for (i = 0; i < HA_NUM_EVENTS; i++) { - INIT_SAS_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); - sas_ha->ha_events[i].ha = sas_ha; - } - - sas_ha->notify_ha_event = notify_ha_event; sas_ha->notify_port_event = notify_port_event; sas_ha->notify_phy_event = sas_notify_phy_event; diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 64e9cdda1c3c..d3f5b57b3ebf 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -106,17 +106,6 @@ void sas_hash_addr(u8 *hashed, const u8 *sas_addr) hashed[2] = r & 0xFF; } - -/* ---------- HA events ---------- */ - -void sas_hae_reset(struct work_struct *work) -{ - struct sas_ha_event *ev = to_sas_ha_event(work); - struct sas_ha_struct *ha = ev->ha; - - clear_bit(HAE_RESET, &ha->pending); -} - int sas_register_ha(struct sas_ha_struct *sas_ha) { int error = 0; @@ -154,7 +143,6 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) INIT_LIST_HEAD(&sas_ha->eh_ata_q); return 0; - Undo_ports: sas_unregister_ports(sas_ha); Undo_phys: diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 6c0dc6155ee7..52b3e863bea8 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -60,11 +60,6 @@ enum sas_phy_type { * so when updating/adding events here, please also * update the other file too. */ -enum ha_event { - HAE_RESET = 0U, - HA_NUM_EVENTS = 1, -}; - enum port_event { PORTE_BYTES_DMAED = 0U, PORTE_BROADCAST_RCVD = 1, @@ -362,18 +357,6 @@ struct scsi_core { }; -struct sas_ha_event { - struct sas_work work; - struct sas_ha_struct *ha; -}; - -static inline struct sas_ha_event *to_sas_ha_event(struct work_struct *work) -{ - struct sas_ha_event *ev = container_of(work, typeof(*ev), work.work); - - return ev; -} - enum sas_ha_state { SAS_HA_REGISTERED, SAS_HA_DRAINING, @@ -383,9 +366,6 @@ enum sas_ha_state { struct sas_ha_struct { /* private: */ - struct sas_ha_event ha_events[HA_NUM_EVENTS]; - unsigned long pending; - struct list_head defer_q; /* work queued while draining */ struct mutex drain_mutex; unsigned long state; @@ -415,7 +395,6 @@ struct sas_ha_struct { * their siblings when forming wide ports */ /* LLDD calls these to notify the class of an event. */ - int (*notify_ha_event)(struct sas_ha_struct *, enum ha_event); int (*notify_port_event)(struct asd_sas_phy *, enum port_event); int (*notify_phy_event)(struct asd_sas_phy *, enum phy_event); -- cgit v1.2.3-59-g8ed1b From 0d78f969b10f27e0be34210d482a01e1ee92994c Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Wed, 6 Sep 2017 17:15:05 +0800 Subject: scsi: libsas: remove the numbering for each event enum Numbering for each event enum makes no sense. Remove the numbering so that we don't have to calculate the number by hand every time. Signed-off-by: Jason Yan CC: John Garry CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl Reviewed-by: Johannes Thumshirn Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- include/scsi/libsas.h | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 52b3e863bea8..4276f5b5686f 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -62,31 +62,31 @@ enum sas_phy_type { */ enum port_event { PORTE_BYTES_DMAED = 0U, - PORTE_BROADCAST_RCVD = 1, - PORTE_LINK_RESET_ERR = 2, - PORTE_TIMER_EVENT = 3, - PORTE_HARD_RESET = 4, - PORT_NUM_EVENTS = 5, + PORTE_BROADCAST_RCVD, + PORTE_LINK_RESET_ERR, + PORTE_TIMER_EVENT, + PORTE_HARD_RESET, + PORT_NUM_EVENTS, }; enum phy_event { PHYE_LOSS_OF_SIGNAL = 0U, - PHYE_OOB_DONE = 1, - PHYE_OOB_ERROR = 2, - PHYE_SPINUP_HOLD = 3, /* hot plug SATA, no COMWAKE sent */ - PHYE_RESUME_TIMEOUT = 4, - PHY_NUM_EVENTS = 5, + PHYE_OOB_DONE, + PHYE_OOB_ERROR, + PHYE_SPINUP_HOLD, /* hot plug SATA, no COMWAKE sent */ + PHYE_RESUME_TIMEOUT, + PHY_NUM_EVENTS, }; enum discover_event { DISCE_DISCOVER_DOMAIN = 0U, - DISCE_REVALIDATE_DOMAIN = 1, - DISCE_PORT_GONE = 2, - DISCE_PROBE = 3, - DISCE_SUSPEND = 4, - DISCE_RESUME = 5, - DISCE_DESTRUCT = 6, - DISC_NUM_EVENTS = 7, + DISCE_REVALIDATE_DOMAIN, + DISCE_PORT_GONE, + DISCE_PROBE, + DISCE_SUSPEND, + DISCE_RESUME, + DISCE_DESTRUCT, + DISC_NUM_EVENTS, }; /* ---------- Expander Devices ---------- */ -- cgit v1.2.3-59-g8ed1b From 8a11282aa1b7d618f29f531e9d992561f87cfd8a Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Wed, 6 Sep 2017 17:15:06 +0800 Subject: scsi: libsas: remove unused port_gone_completion and DISCE_PORT_GONE No one uses the port_gone_completion in struct asd_sas_port and DISCE_PORT_GONE in enum disover_event, clean them out. Signed-off-by: Jason Yan CC: Johannes Thumshirn CC: Ewan Milne CC: Christoph Hellwig CC: Tomas Henzl CC: Dan Williams Reviewed-by: Johannes Thumshirn Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- include/scsi/libsas.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 4276f5b5686f..ab6e18f1c01f 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -81,7 +81,6 @@ enum phy_event { enum discover_event { DISCE_DISCOVER_DOMAIN = 0U, DISCE_REVALIDATE_DOMAIN, - DISCE_PORT_GONE, DISCE_PROBE, DISCE_SUSPEND, DISCE_RESUME, @@ -256,8 +255,6 @@ struct sas_discovery { /* The port struct is Class:RW, driver:RO */ struct asd_sas_port { /* private: */ - struct completion port_gone_completion; - struct sas_discovery disc; struct domain_device *port_dev; spinlock_t dev_list_lock; -- cgit v1.2.3-59-g8ed1b From a5ea8a6803fdec949f752d3aa36b1a88e453b929 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Mon, 11 Sep 2017 18:37:45 +0200 Subject: drm/gem-fb-helper: Use debug message on gem lookup failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GEM lookup failure can easily be triggered by userspace so make it a debug message, not an error message. Also remove unnecessary inner parentheses and fix alphabetical struct declaration order. Cc: Laurent Pinchart Signed-off-by: Noralf Trønnes Reviewed-by: Daniel Vetter Reviewed-by: Laurent Pinchart Link: https://patchwork.freedesktop.org/patch/msgid/1505147865-18194-2-git-send-email-noralf@tronnes.org --- drivers/gpu/drm/drm_gem_framebuffer_helper.c | 4 ++-- include/drm/drm_gem_framebuffer_helper.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index d54a083dc5dd..fc7e995541c9 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -154,7 +154,7 @@ drm_gem_fb_create_with_funcs(struct drm_device *dev, struct drm_file *file, objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); if (!objs[i]) { - DRM_DEV_ERROR(dev->dev, "Failed to lookup GEM\n"); + DRM_DEBUG_KMS("Failed to lookup GEM object\n"); ret = -ENOENT; goto err_gem_object_put; } @@ -232,7 +232,7 @@ int drm_gem_fb_prepare_fb(struct drm_plane *plane, struct dma_buf *dma_buf; struct dma_fence *fence; - if ((plane->state->fb == state->fb) || !state->fb) + if (plane->state->fb == state->fb || !state->fb) return 0; dma_buf = drm_gem_fb_get_obj(state->fb, 0)->dma_buf; diff --git a/include/drm/drm_gem_framebuffer_helper.h b/include/drm/drm_gem_framebuffer_helper.h index db9cfa07235e..5ca7cdc3f527 100644 --- a/include/drm/drm_gem_framebuffer_helper.h +++ b/include/drm/drm_gem_framebuffer_helper.h @@ -2,8 +2,8 @@ #define __DRM_GEM_FB_HELPER_H__ struct drm_device; -struct drm_file; struct drm_fb_helper_surface_size; +struct drm_file; struct drm_framebuffer; struct drm_framebuffer_funcs; struct drm_gem_object; -- cgit v1.2.3-59-g8ed1b From f3ae7d9155c79bb8f97ca3ff61ea979dec402952 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 5 Sep 2017 16:43:49 +0200 Subject: dmaengine: xilinx_dma: Move enum xdma_ip_type to driver file The enum xdma_ip_type is only used inside the Xilinx DMA driver and not exported to any consumers (nor should it be). So move it from the global header to driver file itself. Signed-off-by: Lars-Peter Clausen Acked-by: Michal Simek Signed-off-by: Vinod Koul --- drivers/dma/xilinx/xilinx_dma.c | 14 ++++++++++++++ include/linux/dma/xilinx_dma.h | 14 -------------- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index 8722bcba489d..5eef13380ca8 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -366,6 +366,20 @@ struct xilinx_dma_chan { u16 tdest; }; +/** + * enum xdma_ip_type: DMA IP type. + * + * XDMA_TYPE_AXIDMA: Axi dma ip. + * XDMA_TYPE_CDMA: Axi cdma ip. + * XDMA_TYPE_VDMA: Axi vdma ip. + * + */ +enum xdma_ip_type { + XDMA_TYPE_AXIDMA = 0, + XDMA_TYPE_CDMA, + XDMA_TYPE_VDMA, +}; + struct xilinx_dma_config { enum xdma_ip_type dmatype; int (*clk_init)(struct platform_device *pdev, struct clk **axi_clk, diff --git a/include/linux/dma/xilinx_dma.h b/include/linux/dma/xilinx_dma.h index 3ae300052553..34b98f276ed0 100644 --- a/include/linux/dma/xilinx_dma.h +++ b/include/linux/dma/xilinx_dma.h @@ -41,20 +41,6 @@ struct xilinx_vdma_config { int ext_fsync; }; -/** - * enum xdma_ip_type: DMA IP type. - * - * XDMA_TYPE_AXIDMA: Axi dma ip. - * XDMA_TYPE_CDMA: Axi cdma ip. - * XDMA_TYPE_VDMA: Axi vdma ip. - * - */ -enum xdma_ip_type { - XDMA_TYPE_AXIDMA = 0, - XDMA_TYPE_CDMA, - XDMA_TYPE_VDMA, -}; - int xilinx_vdma_channel_set_config(struct dma_chan *dchan, struct xilinx_vdma_config *cfg); -- cgit v1.2.3-59-g8ed1b From 833bdb47c826a1a64daac194c98887d1d68f2ce8 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Tue, 12 Sep 2017 23:37:19 +0300 Subject: dt-bindings: power: add R8A77970 SYSC power domain definitions Add macros usable by the device tree sources to reference R8A77970 SYSC power domains by index. Based on the original (and large) patch by Daisuke Matsushita . Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Reviewed-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- include/dt-bindings/power/r8a77970-sysc.h | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 include/dt-bindings/power/r8a77970-sysc.h (limited to 'include') diff --git a/include/dt-bindings/power/r8a77970-sysc.h b/include/dt-bindings/power/r8a77970-sysc.h new file mode 100644 index 000000000000..bf54779d1625 --- /dev/null +++ b/include/dt-bindings/power/r8a77970-sysc.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 Cogent Embedded Inc. + * + * 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. + */ +#ifndef __DT_BINDINGS_POWER_R8A77970_SYSC_H__ +#define __DT_BINDINGS_POWER_R8A77970_SYSC_H__ + +/* + * These power domain indices match the numbers of the interrupt bits + * representing the power areas in the various Interrupt Registers + * (e.g. SYSCISR, Interrupt Status Register) + */ + +#define R8A77970_PD_CA53_CPU0 5 +#define R8A77970_PD_CA53_CPU1 6 +#define R8A77970_PD_CR7 13 +#define R8A77970_PD_CA53_SCU 21 +#define R8A77970_PD_A2IR0 23 +#define R8A77970_PD_A3IR 24 +#define R8A77970_PD_A2IR1 27 +#define R8A77970_PD_A2IR2 28 +#define R8A77970_PD_A2IR3 29 +#define R8A77970_PD_A2SC0 30 +#define R8A77970_PD_A2SC1 31 + +/* Always-on power area */ +#define R8A77970_PD_ALWAYS_ON 32 + +#endif /* __DT_BINDINGS_POWER_R8A77970_SYSC_H__ */ -- cgit v1.2.3-59-g8ed1b From c673fe7f0cd525629db9a87a77610fca137951e0 Mon Sep 17 00:00:00 2001 From: Dhinakaran Pandiyan Date: Wed, 13 Sep 2017 23:21:27 -0700 Subject: drm/dp: DPCD register defines for link status within ESI field Link status is available in the ESI field on devices with DPCD r1.2 or higher. DP spec also says "An MST upstream device shall use this field instead of the Link/Sink Device Status field registers, starting from DPCD Address 00200h." v2: Prefixed DP_ (Jani) Rewrote commment to stay within 80 cols. Cc: Jani Nikula Reviewed-by: Jani Nikula Signed-off-by: Dhinakaran Pandiyan Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20170914062127.12047-1-dhinakaran.pandiyan@intel.com --- include/drm/drm_dp_helper.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 2c412a15cfa1..11c39f15f1b3 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -738,6 +738,11 @@ #define DP_RECEIVER_ALPM_STATUS 0x200b /* eDP 1.4 */ # define DP_ALPM_LOCK_TIMEOUT_ERROR (1 << 0) +#define DP_LANE0_1_STATUS_ESI 0x200c /* status same as 0x202 */ +#define DP_LANE2_3_STATUS_ESI 0x200d /* status same as 0x203 */ +#define DP_LANE_ALIGN_STATUS_UPDATED_ESI 0x200e /* status same as 0x204 */ +#define DP_SINK_STATUS_ESI 0x200f /* status same as 0x205 */ + #define DP_DPRX_FEATURE_ENUMERATION_LIST 0x2210 /* DP 1.3 */ # define DP_GTC_CAP (1 << 0) /* DP 1.3 */ # define DP_SST_SPLIT_SDP_CAP (1 << 1) /* DP 1.4 */ -- cgit v1.2.3-59-g8ed1b From 4b4e02c83167dca260e6bf974809979d44694e19 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 11 Sep 2017 20:32:07 -0700 Subject: typec: tcpm: Move out of staging Move tcpm (USB Type-C Port Manager) out of staging. Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/staging/typec/Kconfig | 8 - drivers/staging/typec/Makefile | 1 - drivers/staging/typec/TODO | 10 - drivers/staging/typec/fusb302/fusb302.c | 4 +- drivers/staging/typec/pd.h | 298 --- drivers/staging/typec/pd_bdo.h | 31 - drivers/staging/typec/pd_vdo.h | 251 --- drivers/staging/typec/tcpci.c | 4 +- drivers/staging/typec/tcpm.c | 3616 ------------------------------- drivers/staging/typec/tcpm.h | 204 -- drivers/usb/typec/Kconfig | 8 + drivers/usb/typec/Makefile | 1 + drivers/usb/typec/tcpm.c | 3615 ++++++++++++++++++++++++++++++ include/linux/usb/pd.h | 298 +++ include/linux/usb/pd_bdo.h | 31 + include/linux/usb/pd_vdo.h | 251 +++ include/linux/usb/tcpm.h | 204 ++ 17 files changed, 4412 insertions(+), 4423 deletions(-) delete mode 100644 drivers/staging/typec/pd.h delete mode 100644 drivers/staging/typec/pd_bdo.h delete mode 100644 drivers/staging/typec/pd_vdo.h delete mode 100644 drivers/staging/typec/tcpm.c delete mode 100644 drivers/staging/typec/tcpm.h create mode 100644 drivers/usb/typec/tcpm.c create mode 100644 include/linux/usb/pd.h create mode 100644 include/linux/usb/pd_bdo.h create mode 100644 include/linux/usb/pd_vdo.h create mode 100644 include/linux/usb/tcpm.h (limited to 'include') diff --git a/drivers/staging/typec/Kconfig b/drivers/staging/typec/Kconfig index 37a0781b0d0c..31fad23c2553 100644 --- a/drivers/staging/typec/Kconfig +++ b/drivers/staging/typec/Kconfig @@ -1,13 +1,5 @@ menu "USB Power Delivery and Type-C drivers" -config TYPEC_TCPM - tristate "USB Type-C Port Controller Manager" - depends on USB - select TYPEC - help - The Type-C Port Controller Manager provides a USB PD and USB Type-C - state machine for use with Type-C Port Controllers. - if TYPEC_TCPM config TYPEC_TCPCI diff --git a/drivers/staging/typec/Makefile b/drivers/staging/typec/Makefile index 30a7e29cbc9e..e1df3f0fde10 100644 --- a/drivers/staging/typec/Makefile +++ b/drivers/staging/typec/Makefile @@ -1,3 +1,2 @@ -obj-$(CONFIG_TYPEC_TCPM) += tcpm.o obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o obj-y += fusb302/ diff --git a/drivers/staging/typec/TODO b/drivers/staging/typec/TODO index bc1f97a2d1bf..53fe2f726c88 100644 --- a/drivers/staging/typec/TODO +++ b/drivers/staging/typec/TODO @@ -1,13 +1,3 @@ -tcpm: -- Add documentation (at the very least for the API to low level drivers) -- Split PD code into separate file -- Check if it makes sense to use tracepoints instead of debugfs for debug logs -- Implement Alternate Mode handling -- Address "#if 0" code if not addressed with the above -- Validate all comments marked with "XXX"; either address or remove comments -- Add support for USB PD 3.0. While not mandatory, at least fast role swap - as well as authentication support would be very desirable. - tcpci: - Test with real hardware diff --git a/drivers/staging/typec/fusb302/fusb302.c b/drivers/staging/typec/fusb302/fusb302.c index fc6a3cf74eb3..e790b67d4953 100644 --- a/drivers/staging/typec/fusb302/fusb302.c +++ b/drivers/staging/typec/fusb302/fusb302.c @@ -37,11 +37,11 @@ #include #include #include +#include +#include #include #include "fusb302_reg.h" -#include "../tcpm.h" -#include "../pd.h" /* * When the device is SNK, BC_LVL interrupt is used to monitor cc pins diff --git a/drivers/staging/typec/pd.h b/drivers/staging/typec/pd.h deleted file mode 100644 index e00051ced806..000000000000 --- a/drivers/staging/typec/pd.h +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright 2015-2017 Google, Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __LINUX_USB_PD_H -#define __LINUX_USB_PD_H - -#include -#include - -/* USB PD Messages */ -enum pd_ctrl_msg_type { - /* 0 Reserved */ - PD_CTRL_GOOD_CRC = 1, - PD_CTRL_GOTO_MIN = 2, - PD_CTRL_ACCEPT = 3, - PD_CTRL_REJECT = 4, - PD_CTRL_PING = 5, - PD_CTRL_PS_RDY = 6, - PD_CTRL_GET_SOURCE_CAP = 7, - PD_CTRL_GET_SINK_CAP = 8, - PD_CTRL_DR_SWAP = 9, - PD_CTRL_PR_SWAP = 10, - PD_CTRL_VCONN_SWAP = 11, - PD_CTRL_WAIT = 12, - PD_CTRL_SOFT_RESET = 13, - /* 14-15 Reserved */ -}; - -enum pd_data_msg_type { - /* 0 Reserved */ - PD_DATA_SOURCE_CAP = 1, - PD_DATA_REQUEST = 2, - PD_DATA_BIST = 3, - PD_DATA_SINK_CAP = 4, - /* 5-14 Reserved */ - PD_DATA_VENDOR_DEF = 15, -}; - -#define PD_REV10 0x0 -#define PD_REV20 0x1 - -#define PD_HEADER_CNT_SHIFT 12 -#define PD_HEADER_CNT_MASK 0x7 -#define PD_HEADER_ID_SHIFT 9 -#define PD_HEADER_ID_MASK 0x7 -#define PD_HEADER_PWR_ROLE BIT(8) -#define PD_HEADER_REV_SHIFT 6 -#define PD_HEADER_REV_MASK 0x3 -#define PD_HEADER_DATA_ROLE BIT(5) -#define PD_HEADER_TYPE_SHIFT 0 -#define PD_HEADER_TYPE_MASK 0xf - -#define PD_HEADER(type, pwr, data, id, cnt) \ - ((((type) & PD_HEADER_TYPE_MASK) << PD_HEADER_TYPE_SHIFT) | \ - ((pwr) == TYPEC_SOURCE ? PD_HEADER_PWR_ROLE : 0) | \ - ((data) == TYPEC_HOST ? PD_HEADER_DATA_ROLE : 0) | \ - (PD_REV20 << PD_HEADER_REV_SHIFT) | \ - (((id) & PD_HEADER_ID_MASK) << PD_HEADER_ID_SHIFT) | \ - (((cnt) & PD_HEADER_CNT_MASK) << PD_HEADER_CNT_SHIFT)) - -#define PD_HEADER_LE(type, pwr, data, id, cnt) \ - cpu_to_le16(PD_HEADER((type), (pwr), (data), (id), (cnt))) - -static inline unsigned int pd_header_cnt(u16 header) -{ - return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK; -} - -static inline unsigned int pd_header_cnt_le(__le16 header) -{ - return pd_header_cnt(le16_to_cpu(header)); -} - -static inline unsigned int pd_header_type(u16 header) -{ - return (header >> PD_HEADER_TYPE_SHIFT) & PD_HEADER_TYPE_MASK; -} - -static inline unsigned int pd_header_type_le(__le16 header) -{ - return pd_header_type(le16_to_cpu(header)); -} - -static inline unsigned int pd_header_msgid(u16 header) -{ - return (header >> PD_HEADER_ID_SHIFT) & PD_HEADER_ID_MASK; -} - -static inline unsigned int pd_header_msgid_le(__le16 header) -{ - return pd_header_msgid(le16_to_cpu(header)); -} - -#define PD_MAX_PAYLOAD 7 - -/** - * struct pd_message - PD message as seen on wire - * @header: PD message header - * @payload: PD message payload - */ -struct pd_message { - __le16 header; - __le32 payload[PD_MAX_PAYLOAD]; -} __packed; - -/* PDO: Power Data Object */ -#define PDO_MAX_OBJECTS 7 - -enum pd_pdo_type { - PDO_TYPE_FIXED = 0, - PDO_TYPE_BATT = 1, - PDO_TYPE_VAR = 2, -}; - -#define PDO_TYPE_SHIFT 30 -#define PDO_TYPE_MASK 0x3 - -#define PDO_TYPE(t) ((t) << PDO_TYPE_SHIFT) - -#define PDO_VOLT_MASK 0x3ff -#define PDO_CURR_MASK 0x3ff -#define PDO_PWR_MASK 0x3ff - -#define PDO_FIXED_DUAL_ROLE BIT(29) /* Power role swap supported */ -#define PDO_FIXED_SUSPEND BIT(28) /* USB Suspend supported (Source) */ -#define PDO_FIXED_HIGHER_CAP BIT(28) /* Requires more than vSafe5V (Sink) */ -#define PDO_FIXED_EXTPOWER BIT(27) /* Externally powered */ -#define PDO_FIXED_USB_COMM BIT(26) /* USB communications capable */ -#define PDO_FIXED_DATA_SWAP BIT(25) /* Data role swap supported */ -#define PDO_FIXED_VOLT_SHIFT 10 /* 50mV units */ -#define PDO_FIXED_CURR_SHIFT 0 /* 10mA units */ - -#define PDO_FIXED_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_FIXED_VOLT_SHIFT) -#define PDO_FIXED_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_FIXED_CURR_SHIFT) - -#define PDO_FIXED(mv, ma, flags) \ - (PDO_TYPE(PDO_TYPE_FIXED) | (flags) | \ - PDO_FIXED_VOLT(mv) | PDO_FIXED_CURR(ma)) - -#define PDO_BATT_MAX_VOLT_SHIFT 20 /* 50mV units */ -#define PDO_BATT_MIN_VOLT_SHIFT 10 /* 50mV units */ -#define PDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */ - -#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MIN_VOLT_SHIFT) -#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MAX_VOLT_SHIFT) -#define PDO_BATT_MAX_POWER(mw) ((((mw) / 250) & PDO_PWR_MASK) << PDO_BATT_MAX_PWR_SHIFT) - -#define PDO_BATT(min_mv, max_mv, max_mw) \ - (PDO_TYPE(PDO_TYPE_BATT) | PDO_BATT_MIN_VOLT(min_mv) | \ - PDO_BATT_MAX_VOLT(max_mv) | PDO_BATT_MAX_POWER(max_mw)) - -#define PDO_VAR_MAX_VOLT_SHIFT 20 /* 50mV units */ -#define PDO_VAR_MIN_VOLT_SHIFT 10 /* 50mV units */ -#define PDO_VAR_MAX_CURR_SHIFT 0 /* 10mA units */ - -#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MIN_VOLT_SHIFT) -#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MAX_VOLT_SHIFT) -#define PDO_VAR_MAX_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_VAR_MAX_CURR_SHIFT) - -#define PDO_VAR(min_mv, max_mv, max_ma) \ - (PDO_TYPE(PDO_TYPE_VAR) | PDO_VAR_MIN_VOLT(min_mv) | \ - PDO_VAR_MAX_VOLT(max_mv) | PDO_VAR_MAX_CURR(max_ma)) - -static inline enum pd_pdo_type pdo_type(u32 pdo) -{ - return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK; -} - -static inline unsigned int pdo_fixed_voltage(u32 pdo) -{ - return ((pdo >> PDO_FIXED_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; -} - -static inline unsigned int pdo_min_voltage(u32 pdo) -{ - return ((pdo >> PDO_VAR_MIN_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; -} - -static inline unsigned int pdo_max_voltage(u32 pdo) -{ - return ((pdo >> PDO_VAR_MAX_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; -} - -static inline unsigned int pdo_max_current(u32 pdo) -{ - return ((pdo >> PDO_VAR_MAX_CURR_SHIFT) & PDO_CURR_MASK) * 10; -} - -static inline unsigned int pdo_max_power(u32 pdo) -{ - return ((pdo >> PDO_BATT_MAX_PWR_SHIFT) & PDO_PWR_MASK) * 250; -} - -/* RDO: Request Data Object */ -#define RDO_OBJ_POS_SHIFT 28 -#define RDO_OBJ_POS_MASK 0x7 -#define RDO_GIVE_BACK BIT(27) /* Supports reduced operating current */ -#define RDO_CAP_MISMATCH BIT(26) /* Not satisfied by source caps */ -#define RDO_USB_COMM BIT(25) /* USB communications capable */ -#define RDO_NO_SUSPEND BIT(24) /* USB Suspend not supported */ - -#define RDO_PWR_MASK 0x3ff -#define RDO_CURR_MASK 0x3ff - -#define RDO_FIXED_OP_CURR_SHIFT 10 -#define RDO_FIXED_MAX_CURR_SHIFT 0 - -#define RDO_OBJ(idx) (((idx) & RDO_OBJ_POS_MASK) << RDO_OBJ_POS_SHIFT) - -#define PDO_FIXED_OP_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_OP_CURR_SHIFT) -#define PDO_FIXED_MAX_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_MAX_CURR_SHIFT) - -#define RDO_FIXED(idx, op_ma, max_ma, flags) \ - (RDO_OBJ(idx) | (flags) | \ - PDO_FIXED_OP_CURR(op_ma) | PDO_FIXED_MAX_CURR(max_ma)) - -#define RDO_BATT_OP_PWR_SHIFT 10 /* 250mW units */ -#define RDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */ - -#define RDO_BATT_OP_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_OP_PWR_SHIFT) -#define RDO_BATT_MAX_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_MAX_PWR_SHIFT) - -#define RDO_BATT(idx, op_mw, max_mw, flags) \ - (RDO_OBJ(idx) | (flags) | \ - RDO_BATT_OP_PWR(op_mw) | RDO_BATT_MAX_PWR(max_mw)) - -static inline unsigned int rdo_index(u32 rdo) -{ - return (rdo >> RDO_OBJ_POS_SHIFT) & RDO_OBJ_POS_MASK; -} - -static inline unsigned int rdo_op_current(u32 rdo) -{ - return ((rdo >> RDO_FIXED_OP_CURR_SHIFT) & RDO_CURR_MASK) * 10; -} - -static inline unsigned int rdo_max_current(u32 rdo) -{ - return ((rdo >> RDO_FIXED_MAX_CURR_SHIFT) & - RDO_CURR_MASK) * 10; -} - -static inline unsigned int rdo_op_power(u32 rdo) -{ - return ((rdo >> RDO_BATT_OP_PWR_SHIFT) & RDO_PWR_MASK) * 250; -} - -static inline unsigned int rdo_max_power(u32 rdo) -{ - return ((rdo >> RDO_BATT_MAX_PWR_SHIFT) & RDO_PWR_MASK) * 250; -} - -/* USB PD timers and counters */ -#define PD_T_NO_RESPONSE 5000 /* 4.5 - 5.5 seconds */ -#define PD_T_DB_DETECT 10000 /* 10 - 15 seconds */ -#define PD_T_SEND_SOURCE_CAP 150 /* 100 - 200 ms */ -#define PD_T_SENDER_RESPONSE 60 /* 24 - 30 ms, relaxed */ -#define PD_T_SOURCE_ACTIVITY 45 -#define PD_T_SINK_ACTIVITY 135 -#define PD_T_SINK_WAIT_CAP 240 -#define PD_T_PS_TRANSITION 500 -#define PD_T_SRC_TRANSITION 35 -#define PD_T_DRP_SNK 40 -#define PD_T_DRP_SRC 30 -#define PD_T_PS_SOURCE_OFF 920 -#define PD_T_PS_SOURCE_ON 480 -#define PD_T_PS_HARD_RESET 30 -#define PD_T_SRC_RECOVER 760 -#define PD_T_SRC_RECOVER_MAX 1000 -#define PD_T_SRC_TURN_ON 275 -#define PD_T_SAFE_0V 650 -#define PD_T_VCONN_SOURCE_ON 100 -#define PD_T_SINK_REQUEST 100 /* 100 ms minimum */ -#define PD_T_ERROR_RECOVERY 100 /* minimum 25 is insufficient */ -#define PD_T_SRCSWAPSTDBY 625 /* Maximum of 650ms */ -#define PD_T_NEWSRC 250 /* Maximum of 275ms */ - -#define PD_T_DRP_TRY 100 /* 75 - 150 ms */ -#define PD_T_DRP_TRYWAIT 600 /* 400 - 800 ms */ - -#define PD_T_CC_DEBOUNCE 200 /* 100 - 200 ms */ -#define PD_T_PD_DEBOUNCE 20 /* 10 - 20 ms */ - -#define PD_N_CAPS_COUNT (PD_T_NO_RESPONSE / PD_T_SEND_SOURCE_CAP) -#define PD_N_HARD_RESET_COUNT 2 - -#endif /* __LINUX_USB_PD_H */ diff --git a/drivers/staging/typec/pd_bdo.h b/drivers/staging/typec/pd_bdo.h deleted file mode 100644 index 90b94d9fea5d..000000000000 --- a/drivers/staging/typec/pd_bdo.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2015-2017 Google, Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __LINUX_USB_PD_BDO_H -#define __LINUX_USB_PD_BDO_H - -/* BDO : BIST Data Object */ -#define BDO_MODE_RECV (0 << 28) -#define BDO_MODE_TRANSMIT (1 << 28) -#define BDO_MODE_COUNTERS (2 << 28) -#define BDO_MODE_CARRIER0 (3 << 28) -#define BDO_MODE_CARRIER1 (4 << 28) -#define BDO_MODE_CARRIER2 (5 << 28) -#define BDO_MODE_CARRIER3 (6 << 28) -#define BDO_MODE_EYE (7 << 28) -#define BDO_MODE_TESTDATA (8 << 28) - -#define BDO_MODE_MASK(mode) ((mode) & 0xf0000000) - -#endif diff --git a/drivers/staging/typec/pd_vdo.h b/drivers/staging/typec/pd_vdo.h deleted file mode 100644 index d92259f8de0a..000000000000 --- a/drivers/staging/typec/pd_vdo.h +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2015-2017 Google, Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __LINUX_USB_PD_VDO_H -#define __LINUX_USB_PD_VDO_H - -#include "pd.h" - -/* - * VDO : Vendor Defined Message Object - * VDM object is minimum of VDM header + 6 additional data objects. - */ - -#define VDO_MAX_OBJECTS 6 -#define VDO_MAX_SIZE (VDO_MAX_OBJECTS + 1) - -/* - * VDM header - * ---------- - * <31:16> :: SVID - * <15> :: VDM type ( 1b == structured, 0b == unstructured ) - * <14:13> :: Structured VDM version (can only be 00 == 1.0 currently) - * <12:11> :: reserved - * <10:8> :: object position (1-7 valid ... used for enter/exit mode only) - * <7:6> :: command type (SVDM only?) - * <5> :: reserved (SVDM), command type (UVDM) - * <4:0> :: command - */ -#define VDO(vid, type, custom) \ - (((vid) << 16) | \ - ((type) << 15) | \ - ((custom) & 0x7FFF)) - -#define VDO_SVDM_TYPE (1 << 15) -#define VDO_SVDM_VERS(x) ((x) << 13) -#define VDO_OPOS(x) ((x) << 8) -#define VDO_CMDT(x) ((x) << 6) -#define VDO_OPOS_MASK VDO_OPOS(0x7) -#define VDO_CMDT_MASK VDO_CMDT(0x3) - -#define CMDT_INIT 0 -#define CMDT_RSP_ACK 1 -#define CMDT_RSP_NAK 2 -#define CMDT_RSP_BUSY 3 - -/* reserved for SVDM ... for Google UVDM */ -#define VDO_SRC_INITIATOR (0 << 5) -#define VDO_SRC_RESPONDER (1 << 5) - -#define CMD_DISCOVER_IDENT 1 -#define CMD_DISCOVER_SVID 2 -#define CMD_DISCOVER_MODES 3 -#define CMD_ENTER_MODE 4 -#define CMD_EXIT_MODE 5 -#define CMD_ATTENTION 6 - -#define VDO_CMD_VENDOR(x) (((10 + (x)) & 0x1f)) - -/* ChromeOS specific commands */ -#define VDO_CMD_VERSION VDO_CMD_VENDOR(0) -#define VDO_CMD_SEND_INFO VDO_CMD_VENDOR(1) -#define VDO_CMD_READ_INFO VDO_CMD_VENDOR(2) -#define VDO_CMD_REBOOT VDO_CMD_VENDOR(5) -#define VDO_CMD_FLASH_ERASE VDO_CMD_VENDOR(6) -#define VDO_CMD_FLASH_WRITE VDO_CMD_VENDOR(7) -#define VDO_CMD_ERASE_SIG VDO_CMD_VENDOR(8) -#define VDO_CMD_PING_ENABLE VDO_CMD_VENDOR(10) -#define VDO_CMD_CURRENT VDO_CMD_VENDOR(11) -#define VDO_CMD_FLIP VDO_CMD_VENDOR(12) -#define VDO_CMD_GET_LOG VDO_CMD_VENDOR(13) -#define VDO_CMD_CCD_EN VDO_CMD_VENDOR(14) - -#define PD_VDO_VID(vdo) ((vdo) >> 16) -#define PD_VDO_SVDM(vdo) (((vdo) >> 15) & 1) -#define PD_VDO_OPOS(vdo) (((vdo) >> 8) & 0x7) -#define PD_VDO_CMD(vdo) ((vdo) & 0x1f) -#define PD_VDO_CMDT(vdo) (((vdo) >> 6) & 0x3) - -/* - * SVDM Identity request -> response - * - * Request is simply properly formatted SVDM header - * - * Response is 4 data objects: - * [0] :: SVDM header - * [1] :: Identitiy header - * [2] :: Cert Stat VDO - * [3] :: (Product | Cable) VDO - * [4] :: AMA VDO - * - */ -#define VDO_INDEX_HDR 0 -#define VDO_INDEX_IDH 1 -#define VDO_INDEX_CSTAT 2 -#define VDO_INDEX_CABLE 3 -#define VDO_INDEX_PRODUCT 3 -#define VDO_INDEX_AMA 4 - -/* - * SVDM Identity Header - * -------------------- - * <31> :: data capable as a USB host - * <30> :: data capable as a USB device - * <29:27> :: product type - * <26> :: modal operation supported (1b == yes) - * <25:16> :: Reserved, Shall be set to zero - * <15:0> :: USB-IF assigned VID for this cable vendor - */ -#define IDH_PTYPE_UNDEF 0 -#define IDH_PTYPE_HUB 1 -#define IDH_PTYPE_PERIPH 2 -#define IDH_PTYPE_PCABLE 3 -#define IDH_PTYPE_ACABLE 4 -#define IDH_PTYPE_AMA 5 - -#define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \ - ((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \ - | (is_modal) << 26 | ((vid) & 0xffff)) - -#define PD_IDH_PTYPE(vdo) (((vdo) >> 27) & 0x7) -#define PD_IDH_VID(vdo) ((vdo) & 0xffff) -#define PD_IDH_MODAL_SUPP(vdo) ((vdo) & (1 << 26)) - -/* - * Cert Stat VDO - * ------------- - * <31:0> : USB-IF assigned XID for this cable - */ -#define PD_CSTAT_XID(vdo) (vdo) - -/* - * Product VDO - * ----------- - * <31:16> : USB Product ID - * <15:0> : USB bcdDevice - */ -#define VDO_PRODUCT(pid, bcd) (((pid) & 0xffff) << 16 | ((bcd) & 0xffff)) -#define PD_PRODUCT_PID(vdo) (((vdo) >> 16) & 0xffff) - -/* - * Cable VDO - * --------- - * <31:28> :: Cable HW version - * <27:24> :: Cable FW version - * <23:20> :: Reserved, Shall be set to zero - * <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C) - * <17> :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle) - * <16:13> :: cable latency (0001 == <10ns(~1m length)) - * <12:11> :: cable termination type (11b == both ends active VCONN req) - * <10> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable) - * <9> :: SSTX2 Directionality support - * <8> :: SSRX1 Directionality support - * <7> :: SSRX2 Directionality support - * <6:5> :: Vbus current handling capability - * <4> :: Vbus through cable (0b == no, 1b == yes) - * <3> :: SOP" controller present? (0b == no, 1b == yes) - * <2:0> :: USB SS Signaling support - */ -#define CABLE_ATYPE 0 -#define CABLE_BTYPE 1 -#define CABLE_CTYPE 2 -#define CABLE_PLUG 0 -#define CABLE_RECEPTACLE 1 -#define CABLE_CURR_1A5 0 -#define CABLE_CURR_3A 1 -#define CABLE_CURR_5A 2 -#define CABLE_USBSS_U2_ONLY 0 -#define CABLE_USBSS_U31_GEN1 1 -#define CABLE_USBSS_U31_GEN2 2 -#define VDO_CABLE(hw, fw, cbl, gdr, lat, term, tx1d, tx2d, rx1d, rx2d, cur,\ - vps, sopp, usbss) \ - (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18 \ - | (gdr) << 17 | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11 \ - | (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 \ - | ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3 \ - | ((usbss) & 0x7)) - -/* - * AMA VDO - * --------- - * <31:28> :: Cable HW version - * <27:24> :: Cable FW version - * <23:12> :: Reserved, Shall be set to zero - * <11> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable) - * <10> :: SSTX2 Directionality support - * <9> :: SSRX1 Directionality support - * <8> :: SSRX2 Directionality support - * <7:5> :: Vconn power - * <4> :: Vconn power required - * <3> :: Vbus power required - * <2:0> :: USB SS Signaling support - */ -#define VDO_AMA(hw, fw, tx1d, tx2d, rx1d, rx2d, vcpwr, vcr, vbr, usbss) \ - (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 \ - | (tx1d) << 11 | (tx2d) << 10 | (rx1d) << 9 | (rx2d) << 8 \ - | ((vcpwr) & 0x7) << 5 | (vcr) << 4 | (vbr) << 3 \ - | ((usbss) & 0x7)) - -#define PD_VDO_AMA_VCONN_REQ(vdo) (((vdo) >> 4) & 1) -#define PD_VDO_AMA_VBUS_REQ(vdo) (((vdo) >> 3) & 1) - -#define AMA_VCONN_PWR_1W 0 -#define AMA_VCONN_PWR_1W5 1 -#define AMA_VCONN_PWR_2W 2 -#define AMA_VCONN_PWR_3W 3 -#define AMA_VCONN_PWR_4W 4 -#define AMA_VCONN_PWR_5W 5 -#define AMA_VCONN_PWR_6W 6 -#define AMA_USBSS_U2_ONLY 0 -#define AMA_USBSS_U31_GEN1 1 -#define AMA_USBSS_U31_GEN2 2 -#define AMA_USBSS_BBONLY 3 - -/* - * SVDM Discover SVIDs request -> response - * - * Request is properly formatted VDM Header with discover SVIDs command. - * Response is a set of SVIDs of all all supported SVIDs with all zero's to - * mark the end of SVIDs. If more than 12 SVIDs are supported command SHOULD be - * repeated. - */ -#define VDO_SVID(svid0, svid1) (((svid0) & 0xffff) << 16 | ((svid1) & 0xffff)) -#define PD_VDO_SVID_SVID0(vdo) ((vdo) >> 16) -#define PD_VDO_SVID_SVID1(vdo) ((vdo) & 0xffff) - -/* USB-IF SIDs */ -#define USB_SID_PD 0xff00 /* power delivery */ -#define USB_SID_DISPLAYPORT 0xff01 -#define USB_SID_MHL 0xff02 /* Mobile High-Definition Link */ - -/* VDM command timeouts (in ms) */ - -#define PD_T_VDM_UNSTRUCTURED 500 -#define PD_T_VDM_BUSY 100 -#define PD_T_VDM_WAIT_MODE_E 100 -#define PD_T_VDM_SNDR_RSP 30 -#define PD_T_VDM_E_MODE 25 -#define PD_T_VDM_RCVR_RSP 15 - -#endif /* __LINUX_USB_PD_VDO_H */ diff --git a/drivers/staging/typec/tcpci.c b/drivers/staging/typec/tcpci.c index df72d8b01e73..4636804ea1a4 100644 --- a/drivers/staging/typec/tcpci.c +++ b/drivers/staging/typec/tcpci.c @@ -20,11 +20,11 @@ #include #include #include +#include +#include #include -#include "pd.h" #include "tcpci.h" -#include "tcpm.h" #define PD_RETRY_COUNT 3 diff --git a/drivers/staging/typec/tcpm.c b/drivers/staging/typec/tcpm.c deleted file mode 100644 index cb25ec8334b0..000000000000 --- a/drivers/staging/typec/tcpm.c +++ /dev/null @@ -1,3616 +0,0 @@ -/* - * Copyright 2015-2017 Google, Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * USB Power Delivery protocol stack. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "pd.h" -#include "pd_vdo.h" -#include "pd_bdo.h" -#include "tcpm.h" - -#define FOREACH_STATE(S) \ - S(INVALID_STATE), \ - S(DRP_TOGGLING), \ - S(SRC_UNATTACHED), \ - S(SRC_ATTACH_WAIT), \ - S(SRC_ATTACHED), \ - S(SRC_STARTUP), \ - S(SRC_SEND_CAPABILITIES), \ - S(SRC_NEGOTIATE_CAPABILITIES), \ - S(SRC_TRANSITION_SUPPLY), \ - S(SRC_READY), \ - S(SRC_WAIT_NEW_CAPABILITIES), \ - \ - S(SNK_UNATTACHED), \ - S(SNK_ATTACH_WAIT), \ - S(SNK_DEBOUNCED), \ - S(SNK_ATTACHED), \ - S(SNK_STARTUP), \ - S(SNK_DISCOVERY), \ - S(SNK_DISCOVERY_DEBOUNCE), \ - S(SNK_DISCOVERY_DEBOUNCE_DONE), \ - S(SNK_WAIT_CAPABILITIES), \ - S(SNK_NEGOTIATE_CAPABILITIES), \ - S(SNK_TRANSITION_SINK), \ - S(SNK_TRANSITION_SINK_VBUS), \ - S(SNK_READY), \ - \ - S(ACC_UNATTACHED), \ - S(DEBUG_ACC_ATTACHED), \ - S(AUDIO_ACC_ATTACHED), \ - S(AUDIO_ACC_DEBOUNCE), \ - \ - S(HARD_RESET_SEND), \ - S(HARD_RESET_START), \ - S(SRC_HARD_RESET_VBUS_OFF), \ - S(SRC_HARD_RESET_VBUS_ON), \ - S(SNK_HARD_RESET_SINK_OFF), \ - S(SNK_HARD_RESET_WAIT_VBUS), \ - S(SNK_HARD_RESET_SINK_ON), \ - \ - S(SOFT_RESET), \ - S(SOFT_RESET_SEND), \ - \ - S(DR_SWAP_ACCEPT), \ - S(DR_SWAP_SEND), \ - S(DR_SWAP_SEND_TIMEOUT), \ - S(DR_SWAP_CANCEL), \ - S(DR_SWAP_CHANGE_DR), \ - \ - S(PR_SWAP_ACCEPT), \ - S(PR_SWAP_SEND), \ - S(PR_SWAP_SEND_TIMEOUT), \ - S(PR_SWAP_CANCEL), \ - S(PR_SWAP_START), \ - S(PR_SWAP_SRC_SNK_TRANSITION_OFF), \ - S(PR_SWAP_SRC_SNK_SOURCE_OFF), \ - S(PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED), \ - S(PR_SWAP_SRC_SNK_SINK_ON), \ - S(PR_SWAP_SNK_SRC_SINK_OFF), \ - S(PR_SWAP_SNK_SRC_SOURCE_ON), \ - S(PR_SWAP_SNK_SRC_SOURCE_ON_VBUS_RAMPED_UP), \ - \ - S(VCONN_SWAP_ACCEPT), \ - S(VCONN_SWAP_SEND), \ - S(VCONN_SWAP_SEND_TIMEOUT), \ - S(VCONN_SWAP_CANCEL), \ - S(VCONN_SWAP_START), \ - S(VCONN_SWAP_WAIT_FOR_VCONN), \ - S(VCONN_SWAP_TURN_ON_VCONN), \ - S(VCONN_SWAP_TURN_OFF_VCONN), \ - \ - S(SNK_TRY), \ - S(SNK_TRY_WAIT), \ - S(SNK_TRY_WAIT_DEBOUNCE), \ - S(SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS), \ - S(SRC_TRYWAIT), \ - S(SRC_TRYWAIT_DEBOUNCE), \ - S(SRC_TRYWAIT_UNATTACHED), \ - \ - S(SRC_TRY), \ - S(SRC_TRY_WAIT), \ - S(SRC_TRY_DEBOUNCE), \ - S(SNK_TRYWAIT), \ - S(SNK_TRYWAIT_DEBOUNCE), \ - S(SNK_TRYWAIT_VBUS), \ - S(BIST_RX), \ - \ - S(ERROR_RECOVERY), \ - S(PORT_RESET), \ - S(PORT_RESET_WAIT_OFF) - -#define GENERATE_ENUM(e) e -#define GENERATE_STRING(s) #s - -enum tcpm_state { - FOREACH_STATE(GENERATE_ENUM) -}; - -static const char * const tcpm_states[] = { - FOREACH_STATE(GENERATE_STRING) -}; - -enum vdm_states { - VDM_STATE_ERR_BUSY = -3, - VDM_STATE_ERR_SEND = -2, - VDM_STATE_ERR_TMOUT = -1, - VDM_STATE_DONE = 0, - /* Anything >0 represents an active state */ - VDM_STATE_READY = 1, - VDM_STATE_BUSY = 2, - VDM_STATE_WAIT_RSP_BUSY = 3, -}; - -enum pd_msg_request { - PD_MSG_NONE = 0, - PD_MSG_CTRL_REJECT, - PD_MSG_CTRL_WAIT, - PD_MSG_DATA_SINK_CAP, - PD_MSG_DATA_SOURCE_CAP, -}; - -/* Events from low level driver */ - -#define TCPM_CC_EVENT BIT(0) -#define TCPM_VBUS_EVENT BIT(1) -#define TCPM_RESET_EVENT BIT(2) - -#define LOG_BUFFER_ENTRIES 1024 -#define LOG_BUFFER_ENTRY_SIZE 128 - -/* Alternate mode support */ - -#define SVID_DISCOVERY_MAX 16 - -struct pd_mode_data { - int svid_index; /* current SVID index */ - int nsvids; - u16 svids[SVID_DISCOVERY_MAX]; - int altmodes; /* number of alternate modes */ - struct typec_altmode_desc altmode_desc[SVID_DISCOVERY_MAX]; -}; - -struct tcpm_port { - struct device *dev; - - struct mutex lock; /* tcpm state machine lock */ - struct workqueue_struct *wq; - - struct typec_capability typec_caps; - struct typec_port *typec_port; - - struct tcpc_dev *tcpc; - - enum typec_role vconn_role; - enum typec_role pwr_role; - enum typec_data_role data_role; - enum typec_pwr_opmode pwr_opmode; - - struct usb_pd_identity partner_ident; - struct typec_partner_desc partner_desc; - struct typec_partner *partner; - - enum typec_cc_status cc_req; - - enum typec_cc_status cc1; - enum typec_cc_status cc2; - enum typec_cc_polarity polarity; - - bool attached; - bool connected; - enum typec_port_type port_type; - bool vbus_present; - bool vbus_never_low; - bool vbus_source; - bool vbus_charge; - - bool send_discover; - bool op_vsafe5v; - - int try_role; - int try_snk_count; - int try_src_count; - - enum pd_msg_request queued_message; - - enum tcpm_state enter_state; - enum tcpm_state prev_state; - enum tcpm_state state; - enum tcpm_state delayed_state; - unsigned long delayed_runtime; - unsigned long delay_ms; - - spinlock_t pd_event_lock; - u32 pd_events; - - struct work_struct event_work; - struct delayed_work state_machine; - struct delayed_work vdm_state_machine; - bool state_machine_running; - - struct completion tx_complete; - enum tcpm_transmit_status tx_status; - - struct mutex swap_lock; /* swap command lock */ - bool swap_pending; - bool non_pd_role_swap; - struct completion swap_complete; - int swap_status; - - unsigned int message_id; - unsigned int caps_count; - unsigned int hard_reset_count; - bool pd_capable; - bool explicit_contract; - unsigned int rx_msgid; - - /* Partner capabilities/requests */ - u32 sink_request; - u32 source_caps[PDO_MAX_OBJECTS]; - unsigned int nr_source_caps; - u32 sink_caps[PDO_MAX_OBJECTS]; - unsigned int nr_sink_caps; - - /* Local capabilities */ - u32 src_pdo[PDO_MAX_OBJECTS]; - unsigned int nr_src_pdo; - u32 snk_pdo[PDO_MAX_OBJECTS]; - unsigned int nr_snk_pdo; - u32 snk_vdo[VDO_MAX_OBJECTS]; - unsigned int nr_snk_vdo; - - unsigned int max_snk_mv; - unsigned int max_snk_ma; - unsigned int max_snk_mw; - unsigned int operating_snk_mw; - - /* Requested current / voltage */ - u32 current_limit; - u32 supply_voltage; - - u32 bist_request; - - /* PD state for Vendor Defined Messages */ - enum vdm_states vdm_state; - u32 vdm_retries; - /* next Vendor Defined Message to send */ - u32 vdo_data[VDO_MAX_SIZE]; - u8 vdo_count; - /* VDO to retry if UFP responder replied busy */ - u32 vdo_retry; - - /* Alternate mode data */ - - struct pd_mode_data mode_data; - struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX]; - struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX]; - - /* Deadline in jiffies to exit src_try_wait state */ - unsigned long max_wait; - -#ifdef CONFIG_DEBUG_FS - struct dentry *dentry; - struct mutex logbuffer_lock; /* log buffer access lock */ - int logbuffer_head; - int logbuffer_tail; - u8 *logbuffer[LOG_BUFFER_ENTRIES]; -#endif -}; - -struct pd_rx_event { - struct work_struct work; - struct tcpm_port *port; - struct pd_message msg; -}; - -#define tcpm_cc_is_sink(cc) \ - ((cc) == TYPEC_CC_RP_DEF || (cc) == TYPEC_CC_RP_1_5 || \ - (cc) == TYPEC_CC_RP_3_0) - -#define tcpm_port_is_sink(port) \ - ((tcpm_cc_is_sink((port)->cc1) && !tcpm_cc_is_sink((port)->cc2)) || \ - (tcpm_cc_is_sink((port)->cc2) && !tcpm_cc_is_sink((port)->cc1))) - -#define tcpm_cc_is_source(cc) ((cc) == TYPEC_CC_RD) -#define tcpm_cc_is_audio(cc) ((cc) == TYPEC_CC_RA) -#define tcpm_cc_is_open(cc) ((cc) == TYPEC_CC_OPEN) - -#define tcpm_port_is_source(port) \ - ((tcpm_cc_is_source((port)->cc1) && \ - !tcpm_cc_is_source((port)->cc2)) || \ - (tcpm_cc_is_source((port)->cc2) && \ - !tcpm_cc_is_source((port)->cc1))) - -#define tcpm_port_is_debug(port) \ - (tcpm_cc_is_source((port)->cc1) && tcpm_cc_is_source((port)->cc2)) - -#define tcpm_port_is_audio(port) \ - (tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_audio((port)->cc2)) - -#define tcpm_port_is_audio_detached(port) \ - ((tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_open((port)->cc2)) || \ - (tcpm_cc_is_audio((port)->cc2) && tcpm_cc_is_open((port)->cc1))) - -#define tcpm_try_snk(port) \ - ((port)->try_snk_count == 0 && (port)->try_role == TYPEC_SINK && \ - (port)->port_type == TYPEC_PORT_DRP) - -#define tcpm_try_src(port) \ - ((port)->try_src_count == 0 && (port)->try_role == TYPEC_SOURCE && \ - (port)->port_type == TYPEC_PORT_DRP) - -static enum tcpm_state tcpm_default_state(struct tcpm_port *port) -{ - if (port->port_type == TYPEC_PORT_DRP) { - if (port->try_role == TYPEC_SINK) - return SNK_UNATTACHED; - else if (port->try_role == TYPEC_SOURCE) - return SRC_UNATTACHED; - else if (port->tcpc->config->default_role == TYPEC_SINK) - return SNK_UNATTACHED; - /* Fall through to return SRC_UNATTACHED */ - } else if (port->port_type == TYPEC_PORT_UFP) { - return SNK_UNATTACHED; - } - return SRC_UNATTACHED; -} - -static inline -struct tcpm_port *typec_cap_to_tcpm(const struct typec_capability *cap) -{ - return container_of(cap, struct tcpm_port, typec_caps); -} - -static bool tcpm_port_is_disconnected(struct tcpm_port *port) -{ - return (!port->attached && port->cc1 == TYPEC_CC_OPEN && - port->cc2 == TYPEC_CC_OPEN) || - (port->attached && ((port->polarity == TYPEC_POLARITY_CC1 && - port->cc1 == TYPEC_CC_OPEN) || - (port->polarity == TYPEC_POLARITY_CC2 && - port->cc2 == TYPEC_CC_OPEN))); -} - -/* - * Logging - */ - -#ifdef CONFIG_DEBUG_FS - -static bool tcpm_log_full(struct tcpm_port *port) -{ - return port->logbuffer_tail == - (port->logbuffer_head + 1) % LOG_BUFFER_ENTRIES; -} - -__printf(2, 0) -static void _tcpm_log(struct tcpm_port *port, const char *fmt, va_list args) -{ - char tmpbuffer[LOG_BUFFER_ENTRY_SIZE]; - u64 ts_nsec = local_clock(); - unsigned long rem_nsec; - - if (!port->logbuffer[port->logbuffer_head]) { - port->logbuffer[port->logbuffer_head] = - kzalloc(LOG_BUFFER_ENTRY_SIZE, GFP_KERNEL); - if (!port->logbuffer[port->logbuffer_head]) - return; - } - - vsnprintf(tmpbuffer, sizeof(tmpbuffer), fmt, args); - - mutex_lock(&port->logbuffer_lock); - - if (tcpm_log_full(port)) { - port->logbuffer_head = max(port->logbuffer_head - 1, 0); - strcpy(tmpbuffer, "overflow"); - } - - if (port->logbuffer_head < 0 || - port->logbuffer_head >= LOG_BUFFER_ENTRIES) { - dev_warn(port->dev, - "Bad log buffer index %d\n", port->logbuffer_head); - goto abort; - } - - if (!port->logbuffer[port->logbuffer_head]) { - dev_warn(port->dev, - "Log buffer index %d is NULL\n", port->logbuffer_head); - goto abort; - } - - rem_nsec = do_div(ts_nsec, 1000000000); - scnprintf(port->logbuffer[port->logbuffer_head], - LOG_BUFFER_ENTRY_SIZE, "[%5lu.%06lu] %s", - (unsigned long)ts_nsec, rem_nsec / 1000, - tmpbuffer); - port->logbuffer_head = (port->logbuffer_head + 1) % LOG_BUFFER_ENTRIES; - -abort: - mutex_unlock(&port->logbuffer_lock); -} - -__printf(2, 3) -static void tcpm_log(struct tcpm_port *port, const char *fmt, ...) -{ - va_list args; - - /* Do not log while disconnected and unattached */ - if (tcpm_port_is_disconnected(port) && - (port->state == SRC_UNATTACHED || port->state == SNK_UNATTACHED || - port->state == DRP_TOGGLING)) - return; - - va_start(args, fmt); - _tcpm_log(port, fmt, args); - va_end(args); -} - -__printf(2, 3) -static void tcpm_log_force(struct tcpm_port *port, const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - _tcpm_log(port, fmt, args); - va_end(args); -} - -static void tcpm_log_source_caps(struct tcpm_port *port) -{ - int i; - - for (i = 0; i < port->nr_source_caps; i++) { - u32 pdo = port->source_caps[i]; - enum pd_pdo_type type = pdo_type(pdo); - char msg[64]; - - switch (type) { - case PDO_TYPE_FIXED: - scnprintf(msg, sizeof(msg), - "%u mV, %u mA [%s%s%s%s%s%s]", - pdo_fixed_voltage(pdo), - pdo_max_current(pdo), - (pdo & PDO_FIXED_DUAL_ROLE) ? - "R" : "", - (pdo & PDO_FIXED_SUSPEND) ? - "S" : "", - (pdo & PDO_FIXED_HIGHER_CAP) ? - "H" : "", - (pdo & PDO_FIXED_USB_COMM) ? - "U" : "", - (pdo & PDO_FIXED_DATA_SWAP) ? - "D" : "", - (pdo & PDO_FIXED_EXTPOWER) ? - "E" : ""); - break; - case PDO_TYPE_VAR: - scnprintf(msg, sizeof(msg), - "%u-%u mV, %u mA", - pdo_min_voltage(pdo), - pdo_max_voltage(pdo), - pdo_max_current(pdo)); - break; - case PDO_TYPE_BATT: - scnprintf(msg, sizeof(msg), - "%u-%u mV, %u mW", - pdo_min_voltage(pdo), - pdo_max_voltage(pdo), - pdo_max_power(pdo)); - break; - default: - strcpy(msg, "undefined"); - break; - } - tcpm_log(port, " PDO %d: type %d, %s", - i, type, msg); - } -} - -static int tcpm_seq_show(struct seq_file *s, void *v) -{ - struct tcpm_port *port = (struct tcpm_port *)s->private; - int tail; - - mutex_lock(&port->logbuffer_lock); - tail = port->logbuffer_tail; - while (tail != port->logbuffer_head) { - seq_printf(s, "%s\n", port->logbuffer[tail]); - tail = (tail + 1) % LOG_BUFFER_ENTRIES; - } - if (!seq_has_overflowed(s)) - port->logbuffer_tail = tail; - mutex_unlock(&port->logbuffer_lock); - - return 0; -} - -static int tcpm_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, tcpm_seq_show, inode->i_private); -} - -static const struct file_operations tcpm_debug_operations = { - .open = tcpm_debug_open, - .llseek = seq_lseek, - .read = seq_read, - .release = single_release, -}; - -static struct dentry *rootdir; - -static int tcpm_debugfs_init(struct tcpm_port *port) -{ - mutex_init(&port->logbuffer_lock); - /* /sys/kernel/debug/tcpm/usbcX */ - if (!rootdir) { - rootdir = debugfs_create_dir("tcpm", NULL); - if (!rootdir) - return -ENOMEM; - } - - port->dentry = debugfs_create_file(dev_name(port->dev), - S_IFREG | 0444, rootdir, - port, &tcpm_debug_operations); - - return 0; -} - -static void tcpm_debugfs_exit(struct tcpm_port *port) -{ - debugfs_remove(port->dentry); -} - -#else - -__printf(2, 3) -static void tcpm_log(const struct tcpm_port *port, const char *fmt, ...) { } -__printf(2, 3) -static void tcpm_log_force(struct tcpm_port *port, const char *fmt, ...) { } -static void tcpm_log_source_caps(struct tcpm_port *port) { } -static int tcpm_debugfs_init(const struct tcpm_port *port) { return 0; } -static void tcpm_debugfs_exit(const struct tcpm_port *port) { } - -#endif - -static int tcpm_pd_transmit(struct tcpm_port *port, - enum tcpm_transmit_type type, - const struct pd_message *msg) -{ - unsigned long timeout; - int ret; - - if (msg) - tcpm_log(port, "PD TX, header: %#x", le16_to_cpu(msg->header)); - else - tcpm_log(port, "PD TX, type: %#x", type); - - reinit_completion(&port->tx_complete); - ret = port->tcpc->pd_transmit(port->tcpc, type, msg); - if (ret < 0) - return ret; - - mutex_unlock(&port->lock); - timeout = wait_for_completion_timeout(&port->tx_complete, - msecs_to_jiffies(PD_T_TCPC_TX_TIMEOUT)); - mutex_lock(&port->lock); - if (!timeout) - return -ETIMEDOUT; - - switch (port->tx_status) { - case TCPC_TX_SUCCESS: - port->message_id = (port->message_id + 1) & PD_HEADER_ID_MASK; - return 0; - case TCPC_TX_DISCARDED: - return -EAGAIN; - case TCPC_TX_FAILED: - default: - return -EIO; - } -} - -void tcpm_pd_transmit_complete(struct tcpm_port *port, - enum tcpm_transmit_status status) -{ - tcpm_log(port, "PD TX complete, status: %u", status); - port->tx_status = status; - complete(&port->tx_complete); -} -EXPORT_SYMBOL_GPL(tcpm_pd_transmit_complete); - -static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode, - enum tcpc_usb_switch config) -{ - int ret = 0; - - tcpm_log(port, "Requesting mux mode %d, config %d, polarity %d", - mode, config, port->polarity); - - if (port->tcpc->mux) - ret = port->tcpc->mux->set(port->tcpc->mux, mode, config, - port->polarity); - - return ret; -} - -static int tcpm_set_polarity(struct tcpm_port *port, - enum typec_cc_polarity polarity) -{ - int ret; - - tcpm_log(port, "polarity %d", polarity); - - ret = port->tcpc->set_polarity(port->tcpc, polarity); - if (ret < 0) - return ret; - - port->polarity = polarity; - - return 0; -} - -static int tcpm_set_vconn(struct tcpm_port *port, bool enable) -{ - int ret; - - tcpm_log(port, "vconn:=%d", enable); - - ret = port->tcpc->set_vconn(port->tcpc, enable); - if (!ret) { - port->vconn_role = enable ? TYPEC_SOURCE : TYPEC_SINK; - typec_set_vconn_role(port->typec_port, port->vconn_role); - } - - return ret; -} - -static u32 tcpm_get_current_limit(struct tcpm_port *port) -{ - enum typec_cc_status cc; - u32 limit; - - cc = port->polarity ? port->cc2 : port->cc1; - switch (cc) { - case TYPEC_CC_RP_1_5: - limit = 1500; - break; - case TYPEC_CC_RP_3_0: - limit = 3000; - break; - case TYPEC_CC_RP_DEF: - default: - if (port->tcpc->get_current_limit) - limit = port->tcpc->get_current_limit(port->tcpc); - else - limit = 0; - break; - } - - return limit; -} - -static int tcpm_set_current_limit(struct tcpm_port *port, u32 max_ma, u32 mv) -{ - int ret = -EOPNOTSUPP; - - tcpm_log(port, "Setting voltage/current limit %u mV %u mA", mv, max_ma); - - if (port->tcpc->set_current_limit) - ret = port->tcpc->set_current_limit(port->tcpc, max_ma, mv); - - return ret; -} - -/* - * Determine RP value to set based on maximum current supported - * by a port if configured as source. - * Returns CC value to report to link partner. - */ -static enum typec_cc_status tcpm_rp_cc(struct tcpm_port *port) -{ - const u32 *src_pdo = port->src_pdo; - int nr_pdo = port->nr_src_pdo; - int i; - - /* - * Search for first entry with matching voltage. - * It should report the maximum supported current. - */ - for (i = 0; i < nr_pdo; i++) { - const u32 pdo = src_pdo[i]; - - if (pdo_type(pdo) == PDO_TYPE_FIXED && - pdo_fixed_voltage(pdo) == 5000) { - unsigned int curr = pdo_max_current(pdo); - - if (curr >= 3000) - return TYPEC_CC_RP_3_0; - else if (curr >= 1500) - return TYPEC_CC_RP_1_5; - return TYPEC_CC_RP_DEF; - } - } - - return TYPEC_CC_RP_DEF; -} - -static int tcpm_set_attached_state(struct tcpm_port *port, bool attached) -{ - return port->tcpc->set_roles(port->tcpc, attached, port->pwr_role, - port->data_role); -} - -static int tcpm_set_roles(struct tcpm_port *port, bool attached, - enum typec_role role, enum typec_data_role data) -{ - int ret; - - if (data == TYPEC_HOST) - ret = tcpm_mux_set(port, TYPEC_MUX_USB, - TCPC_USB_SWITCH_CONNECT); - else - ret = tcpm_mux_set(port, TYPEC_MUX_NONE, - TCPC_USB_SWITCH_DISCONNECT); - if (ret < 0) - return ret; - - ret = port->tcpc->set_roles(port->tcpc, attached, role, data); - if (ret < 0) - return ret; - - port->pwr_role = role; - port->data_role = data; - typec_set_data_role(port->typec_port, data); - typec_set_pwr_role(port->typec_port, role); - - return 0; -} - -static int tcpm_set_pwr_role(struct tcpm_port *port, enum typec_role role) -{ - int ret; - - ret = port->tcpc->set_roles(port->tcpc, true, role, - port->data_role); - if (ret < 0) - return ret; - - port->pwr_role = role; - typec_set_pwr_role(port->typec_port, role); - - return 0; -} - -static int tcpm_pd_send_source_caps(struct tcpm_port *port) -{ - struct pd_message msg; - int i; - - memset(&msg, 0, sizeof(msg)); - if (!port->nr_src_pdo) { - /* No source capabilities defined, sink only */ - msg.header = PD_HEADER_LE(PD_CTRL_REJECT, - port->pwr_role, - port->data_role, - port->message_id, 0); - } else { - msg.header = PD_HEADER_LE(PD_DATA_SOURCE_CAP, - port->pwr_role, - port->data_role, - port->message_id, - port->nr_src_pdo); - } - for (i = 0; i < port->nr_src_pdo; i++) - msg.payload[i] = cpu_to_le32(port->src_pdo[i]); - - return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); -} - -static int tcpm_pd_send_sink_caps(struct tcpm_port *port) -{ - struct pd_message msg; - int i; - - memset(&msg, 0, sizeof(msg)); - if (!port->nr_snk_pdo) { - /* No sink capabilities defined, source only */ - msg.header = PD_HEADER_LE(PD_CTRL_REJECT, - port->pwr_role, - port->data_role, - port->message_id, 0); - } else { - msg.header = PD_HEADER_LE(PD_DATA_SINK_CAP, - port->pwr_role, - port->data_role, - port->message_id, - port->nr_snk_pdo); - } - for (i = 0; i < port->nr_snk_pdo; i++) - msg.payload[i] = cpu_to_le32(port->snk_pdo[i]); - - return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); -} - -static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state, - unsigned int delay_ms) -{ - if (delay_ms) { - tcpm_log(port, "pending state change %s -> %s @ %u ms", - tcpm_states[port->state], tcpm_states[state], - delay_ms); - port->delayed_state = state; - mod_delayed_work(port->wq, &port->state_machine, - msecs_to_jiffies(delay_ms)); - port->delayed_runtime = jiffies + msecs_to_jiffies(delay_ms); - port->delay_ms = delay_ms; - } else { - tcpm_log(port, "state change %s -> %s", - tcpm_states[port->state], tcpm_states[state]); - port->delayed_state = INVALID_STATE; - port->prev_state = port->state; - port->state = state; - /* - * Don't re-queue the state machine work item if we're currently - * in the state machine and we're immediately changing states. - * tcpm_state_machine_work() will continue running the state - * machine. - */ - if (!port->state_machine_running) - mod_delayed_work(port->wq, &port->state_machine, 0); - } -} - -static void tcpm_set_state_cond(struct tcpm_port *port, enum tcpm_state state, - unsigned int delay_ms) -{ - if (port->enter_state == port->state) - tcpm_set_state(port, state, delay_ms); - else - tcpm_log(port, - "skipped %sstate change %s -> %s [%u ms], context state %s", - delay_ms ? "delayed " : "", - tcpm_states[port->state], tcpm_states[state], - delay_ms, tcpm_states[port->enter_state]); -} - -static void tcpm_queue_message(struct tcpm_port *port, - enum pd_msg_request message) -{ - port->queued_message = message; - mod_delayed_work(port->wq, &port->state_machine, 0); -} - -/* - * VDM/VDO handling functions - */ -static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, - const u32 *data, int cnt) -{ - port->vdo_count = cnt + 1; - port->vdo_data[0] = header; - memcpy(&port->vdo_data[1], data, sizeof(u32) * cnt); - /* Set ready, vdm state machine will actually send */ - port->vdm_retries = 0; - port->vdm_state = VDM_STATE_READY; -} - -static void svdm_consume_identity(struct tcpm_port *port, const __le32 *payload, - int cnt) -{ - u32 vdo = le32_to_cpu(payload[VDO_INDEX_IDH]); - u32 product = le32_to_cpu(payload[VDO_INDEX_PRODUCT]); - - memset(&port->mode_data, 0, sizeof(port->mode_data)); - - port->partner_ident.id_header = vdo; - port->partner_ident.cert_stat = le32_to_cpu(payload[VDO_INDEX_CSTAT]); - port->partner_ident.product = product; - - typec_partner_set_identity(port->partner); - - tcpm_log(port, "Identity: %04x:%04x.%04x", - PD_IDH_VID(vdo), - PD_PRODUCT_PID(product), product & 0xffff); -} - -static bool svdm_consume_svids(struct tcpm_port *port, const __le32 *payload, - int cnt) -{ - struct pd_mode_data *pmdata = &port->mode_data; - int i; - - for (i = 1; i < cnt; i++) { - u32 p = le32_to_cpu(payload[i]); - u16 svid; - - svid = (p >> 16) & 0xffff; - if (!svid) - return false; - - if (pmdata->nsvids >= SVID_DISCOVERY_MAX) - goto abort; - - pmdata->svids[pmdata->nsvids++] = svid; - tcpm_log(port, "SVID %d: 0x%x", pmdata->nsvids, svid); - - svid = p & 0xffff; - if (!svid) - return false; - - if (pmdata->nsvids >= SVID_DISCOVERY_MAX) - goto abort; - - pmdata->svids[pmdata->nsvids++] = svid; - tcpm_log(port, "SVID %d: 0x%x", pmdata->nsvids, svid); - } - return true; -abort: - tcpm_log(port, "SVID_DISCOVERY_MAX(%d) too low!", SVID_DISCOVERY_MAX); - return false; -} - -static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload, - int cnt) -{ - struct pd_mode_data *pmdata = &port->mode_data; - struct typec_altmode_desc *paltmode; - struct typec_mode_desc *pmode; - int i; - - if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) { - /* Already logged in svdm_consume_svids() */ - return; - } - - paltmode = &pmdata->altmode_desc[pmdata->altmodes]; - memset(paltmode, 0, sizeof(*paltmode)); - - paltmode->svid = pmdata->svids[pmdata->svid_index]; - - tcpm_log(port, " Alternate mode %d: SVID 0x%04x", - pmdata->altmodes, paltmode->svid); - - for (i = 1; i < cnt && paltmode->n_modes < ALTMODE_MAX_MODES; i++) { - pmode = &paltmode->modes[paltmode->n_modes]; - memset(pmode, 0, sizeof(*pmode)); - pmode->vdo = le32_to_cpu(payload[i]); - pmode->index = i - 1; - paltmode->n_modes++; - tcpm_log(port, " VDO %d: 0x%08x", - pmode->index, pmode->vdo); - } - port->partner_altmode[pmdata->altmodes] = - typec_partner_register_altmode(port->partner, paltmode); - if (port->partner_altmode[pmdata->altmodes] == NULL) { - tcpm_log(port, - "Failed to register alternate modes for SVID 0x%04x", - paltmode->svid); - return; - } - pmdata->altmodes++; -} - -#define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header) - -static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, - u32 *response) -{ - u32 p0 = le32_to_cpu(payload[0]); - int cmd_type = PD_VDO_CMDT(p0); - int cmd = PD_VDO_CMD(p0); - struct pd_mode_data *modep; - int rlen = 0; - u16 svid; - int i; - - tcpm_log(port, "Rx VDM cmd 0x%x type %d cmd %d len %d", - p0, cmd_type, cmd, cnt); - - modep = &port->mode_data; - - switch (cmd_type) { - case CMDT_INIT: - switch (cmd) { - case CMD_DISCOVER_IDENT: - /* 6.4.4.3.1: Only respond as UFP (device) */ - if (port->data_role == TYPEC_DEVICE && - port->nr_snk_vdo) { - for (i = 0; i < port->nr_snk_vdo; i++) - response[i + 1] = port->snk_vdo[i]; - rlen = port->nr_snk_vdo + 1; - } - break; - case CMD_DISCOVER_SVID: - break; - case CMD_DISCOVER_MODES: - break; - case CMD_ENTER_MODE: - break; - case CMD_EXIT_MODE: - break; - case CMD_ATTENTION: - break; - default: - break; - } - if (rlen >= 1) { - response[0] = p0 | VDO_CMDT(CMDT_RSP_ACK); - } else if (rlen == 0) { - response[0] = p0 | VDO_CMDT(CMDT_RSP_NAK); - rlen = 1; - } else { - response[0] = p0 | VDO_CMDT(CMDT_RSP_BUSY); - rlen = 1; - } - break; - case CMDT_RSP_ACK: - /* silently drop message if we are not connected */ - if (!port->partner) - break; - - switch (cmd) { - case CMD_DISCOVER_IDENT: - /* 6.4.4.3.1 */ - svdm_consume_identity(port, payload, cnt); - response[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID); - rlen = 1; - break; - case CMD_DISCOVER_SVID: - /* 6.4.4.3.2 */ - if (svdm_consume_svids(port, payload, cnt)) { - response[0] = VDO(USB_SID_PD, 1, - CMD_DISCOVER_SVID); - rlen = 1; - } else if (modep->nsvids && supports_modal(port)) { - response[0] = VDO(modep->svids[0], 1, - CMD_DISCOVER_MODES); - rlen = 1; - } - break; - case CMD_DISCOVER_MODES: - /* 6.4.4.3.3 */ - svdm_consume_modes(port, payload, cnt); - modep->svid_index++; - if (modep->svid_index < modep->nsvids) { - svid = modep->svids[modep->svid_index]; - response[0] = VDO(svid, 1, CMD_DISCOVER_MODES); - rlen = 1; - } else { - /* enter alternate mode if/when implemented */ - } - break; - case CMD_ENTER_MODE: - break; - default: - break; - } - break; - default: - break; - } - - return rlen; -} - -static void tcpm_handle_vdm_request(struct tcpm_port *port, - const __le32 *payload, int cnt) -{ - int rlen = 0; - u32 response[8] = { }; - u32 p0 = le32_to_cpu(payload[0]); - - if (port->vdm_state == VDM_STATE_BUSY) { - /* If UFP responded busy retry after timeout */ - if (PD_VDO_CMDT(p0) == CMDT_RSP_BUSY) { - port->vdm_state = VDM_STATE_WAIT_RSP_BUSY; - port->vdo_retry = (p0 & ~VDO_CMDT_MASK) | - CMDT_INIT; - mod_delayed_work(port->wq, &port->vdm_state_machine, - msecs_to_jiffies(PD_T_VDM_BUSY)); - return; - } - port->vdm_state = VDM_STATE_DONE; - } - - if (PD_VDO_SVDM(p0)) - rlen = tcpm_pd_svdm(port, payload, cnt, response); - - if (rlen > 0) { - tcpm_queue_vdm(port, response[0], &response[1], rlen - 1); - mod_delayed_work(port->wq, &port->vdm_state_machine, 0); - } -} - -static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd, - const u32 *data, int count) -{ - u32 header; - - if (WARN_ON(count > VDO_MAX_SIZE - 1)) - count = VDO_MAX_SIZE - 1; - - /* set VDM header with VID & CMD */ - header = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? - 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), cmd); - tcpm_queue_vdm(port, header, data, count); - - mod_delayed_work(port->wq, &port->vdm_state_machine, 0); -} - -static unsigned int vdm_ready_timeout(u32 vdm_hdr) -{ - unsigned int timeout; - int cmd = PD_VDO_CMD(vdm_hdr); - - /* its not a structured VDM command */ - if (!PD_VDO_SVDM(vdm_hdr)) - return PD_T_VDM_UNSTRUCTURED; - - switch (PD_VDO_CMDT(vdm_hdr)) { - case CMDT_INIT: - if (cmd == CMD_ENTER_MODE || cmd == CMD_EXIT_MODE) - timeout = PD_T_VDM_WAIT_MODE_E; - else - timeout = PD_T_VDM_SNDR_RSP; - break; - default: - if (cmd == CMD_ENTER_MODE || cmd == CMD_EXIT_MODE) - timeout = PD_T_VDM_E_MODE; - else - timeout = PD_T_VDM_RCVR_RSP; - break; - } - return timeout; -} - -static void vdm_run_state_machine(struct tcpm_port *port) -{ - struct pd_message msg; - int i, res; - - switch (port->vdm_state) { - case VDM_STATE_READY: - /* Only transmit VDM if attached */ - if (!port->attached) { - port->vdm_state = VDM_STATE_ERR_BUSY; - break; - } - - /* - * if there's traffic or we're not in PDO ready state don't send - * a VDM. - */ - if (port->state != SRC_READY && port->state != SNK_READY) - break; - - /* Prepare and send VDM */ - memset(&msg, 0, sizeof(msg)); - msg.header = PD_HEADER_LE(PD_DATA_VENDOR_DEF, - port->pwr_role, - port->data_role, - port->message_id, port->vdo_count); - for (i = 0; i < port->vdo_count; i++) - msg.payload[i] = cpu_to_le32(port->vdo_data[i]); - res = tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); - if (res < 0) { - port->vdm_state = VDM_STATE_ERR_SEND; - } else { - unsigned long timeout; - - port->vdm_retries = 0; - port->vdm_state = VDM_STATE_BUSY; - timeout = vdm_ready_timeout(port->vdo_data[0]); - mod_delayed_work(port->wq, &port->vdm_state_machine, - timeout); - } - break; - case VDM_STATE_WAIT_RSP_BUSY: - port->vdo_data[0] = port->vdo_retry; - port->vdo_count = 1; - port->vdm_state = VDM_STATE_READY; - break; - case VDM_STATE_BUSY: - port->vdm_state = VDM_STATE_ERR_TMOUT; - break; - case VDM_STATE_ERR_SEND: - /* - * A partner which does not support USB PD will not reply, - * so this is not a fatal error. At the same time, some - * devices may not return GoodCRC under some circumstances, - * so we need to retry. - */ - if (port->vdm_retries < 3) { - tcpm_log(port, "VDM Tx error, retry"); - port->vdm_retries++; - port->vdm_state = VDM_STATE_READY; - } - break; - default: - break; - } -} - -static void vdm_state_machine_work(struct work_struct *work) -{ - struct tcpm_port *port = container_of(work, struct tcpm_port, - vdm_state_machine.work); - enum vdm_states prev_state; - - mutex_lock(&port->lock); - - /* - * Continue running as long as the port is not busy and there was - * a state change. - */ - do { - prev_state = port->vdm_state; - vdm_run_state_machine(port); - } while (port->vdm_state != prev_state && - port->vdm_state != VDM_STATE_BUSY); - - mutex_unlock(&port->lock); -} - -/* - * PD (data, control) command handling functions - */ -static void tcpm_pd_data_request(struct tcpm_port *port, - const struct pd_message *msg) -{ - enum pd_data_msg_type type = pd_header_type_le(msg->header); - unsigned int cnt = pd_header_cnt_le(msg->header); - unsigned int i; - - switch (type) { - case PD_DATA_SOURCE_CAP: - if (port->pwr_role != TYPEC_SINK) - break; - - for (i = 0; i < cnt; i++) - port->source_caps[i] = le32_to_cpu(msg->payload[i]); - - port->nr_source_caps = cnt; - - tcpm_log_source_caps(port); - - /* - * This message may be received even if VBUS is not - * present. This is quite unexpected; see USB PD - * specification, sections 8.3.3.6.3.1 and 8.3.3.6.3.2. - * However, at the same time, we must be ready to - * receive this message and respond to it 15ms after - * receiving PS_RDY during power swap operations, no matter - * if VBUS is available or not (USB PD specification, - * section 6.5.9.2). - * So we need to accept the message either way, - * but be prepared to keep waiting for VBUS after it was - * handled. - */ - tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); - break; - case PD_DATA_REQUEST: - if (port->pwr_role != TYPEC_SOURCE || - cnt != 1) { - tcpm_queue_message(port, PD_MSG_CTRL_REJECT); - break; - } - port->sink_request = le32_to_cpu(msg->payload[0]); - tcpm_set_state(port, SRC_NEGOTIATE_CAPABILITIES, 0); - break; - case PD_DATA_SINK_CAP: - /* We don't do anything with this at the moment... */ - for (i = 0; i < cnt; i++) - port->sink_caps[i] = le32_to_cpu(msg->payload[i]); - port->nr_sink_caps = cnt; - break; - case PD_DATA_VENDOR_DEF: - tcpm_handle_vdm_request(port, msg->payload, cnt); - break; - case PD_DATA_BIST: - if (port->state == SRC_READY || port->state == SNK_READY) { - port->bist_request = le32_to_cpu(msg->payload[0]); - tcpm_set_state(port, BIST_RX, 0); - } - break; - default: - tcpm_log(port, "Unhandled data message type %#x", type); - break; - } -} - -static void tcpm_pd_ctrl_request(struct tcpm_port *port, - const struct pd_message *msg) -{ - enum pd_ctrl_msg_type type = pd_header_type_le(msg->header); - enum tcpm_state next_state; - - switch (type) { - case PD_CTRL_GOOD_CRC: - case PD_CTRL_PING: - break; - case PD_CTRL_GET_SOURCE_CAP: - switch (port->state) { - case SRC_READY: - case SNK_READY: - tcpm_queue_message(port, PD_MSG_DATA_SOURCE_CAP); - break; - default: - tcpm_queue_message(port, PD_MSG_CTRL_REJECT); - break; - } - break; - case PD_CTRL_GET_SINK_CAP: - switch (port->state) { - case SRC_READY: - case SNK_READY: - tcpm_queue_message(port, PD_MSG_DATA_SINK_CAP); - break; - default: - tcpm_queue_message(port, PD_MSG_CTRL_REJECT); - break; - } - break; - case PD_CTRL_GOTO_MIN: - break; - case PD_CTRL_PS_RDY: - switch (port->state) { - case SNK_TRANSITION_SINK: - if (port->vbus_present) { - tcpm_set_current_limit(port, - port->current_limit, - port->supply_voltage); - port->explicit_contract = true; - tcpm_set_state(port, SNK_READY, 0); - } else { - /* - * Seen after power swap. Keep waiting for VBUS - * in a transitional state. - */ - tcpm_set_state(port, - SNK_TRANSITION_SINK_VBUS, 0); - } - break; - case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED: - tcpm_set_state(port, PR_SWAP_SRC_SNK_SINK_ON, 0); - break; - case PR_SWAP_SNK_SRC_SINK_OFF: - tcpm_set_state(port, PR_SWAP_SNK_SRC_SOURCE_ON, 0); - break; - case VCONN_SWAP_WAIT_FOR_VCONN: - tcpm_set_state(port, VCONN_SWAP_TURN_OFF_VCONN, 0); - break; - default: - break; - } - break; - case PD_CTRL_REJECT: - case PD_CTRL_WAIT: - switch (port->state) { - case SNK_NEGOTIATE_CAPABILITIES: - /* USB PD specification, Figure 8-43 */ - if (port->explicit_contract) - next_state = SNK_READY; - else - next_state = SNK_WAIT_CAPABILITIES; - tcpm_set_state(port, next_state, 0); - break; - case DR_SWAP_SEND: - port->swap_status = (type == PD_CTRL_WAIT ? - -EAGAIN : -EOPNOTSUPP); - tcpm_set_state(port, DR_SWAP_CANCEL, 0); - break; - case PR_SWAP_SEND: - port->swap_status = (type == PD_CTRL_WAIT ? - -EAGAIN : -EOPNOTSUPP); - tcpm_set_state(port, PR_SWAP_CANCEL, 0); - break; - case VCONN_SWAP_SEND: - port->swap_status = (type == PD_CTRL_WAIT ? - -EAGAIN : -EOPNOTSUPP); - tcpm_set_state(port, VCONN_SWAP_CANCEL, 0); - break; - default: - break; - } - break; - case PD_CTRL_ACCEPT: - switch (port->state) { - case SNK_NEGOTIATE_CAPABILITIES: - tcpm_set_state(port, SNK_TRANSITION_SINK, 0); - break; - case SOFT_RESET_SEND: - port->message_id = 0; - port->rx_msgid = -1; - if (port->pwr_role == TYPEC_SOURCE) - next_state = SRC_SEND_CAPABILITIES; - else - next_state = SNK_WAIT_CAPABILITIES; - tcpm_set_state(port, next_state, 0); - break; - case DR_SWAP_SEND: - tcpm_set_state(port, DR_SWAP_CHANGE_DR, 0); - break; - case PR_SWAP_SEND: - tcpm_set_state(port, PR_SWAP_START, 0); - break; - case VCONN_SWAP_SEND: - tcpm_set_state(port, VCONN_SWAP_START, 0); - break; - default: - break; - } - break; - case PD_CTRL_SOFT_RESET: - tcpm_set_state(port, SOFT_RESET, 0); - break; - case PD_CTRL_DR_SWAP: - if (port->port_type != TYPEC_PORT_DRP) { - tcpm_queue_message(port, PD_MSG_CTRL_REJECT); - break; - } - /* - * XXX - * 6.3.9: If an alternate mode is active, a request to swap - * alternate modes shall trigger a port reset. - */ - switch (port->state) { - case SRC_READY: - case SNK_READY: - tcpm_set_state(port, DR_SWAP_ACCEPT, 0); - break; - default: - tcpm_queue_message(port, PD_MSG_CTRL_WAIT); - break; - } - break; - case PD_CTRL_PR_SWAP: - if (port->port_type != TYPEC_PORT_DRP) { - tcpm_queue_message(port, PD_MSG_CTRL_REJECT); - break; - } - switch (port->state) { - case SRC_READY: - case SNK_READY: - tcpm_set_state(port, PR_SWAP_ACCEPT, 0); - break; - default: - tcpm_queue_message(port, PD_MSG_CTRL_WAIT); - break; - } - break; - case PD_CTRL_VCONN_SWAP: - switch (port->state) { - case SRC_READY: - case SNK_READY: - tcpm_set_state(port, VCONN_SWAP_ACCEPT, 0); - break; - default: - tcpm_queue_message(port, PD_MSG_CTRL_WAIT); - break; - } - break; - default: - tcpm_log(port, "Unhandled ctrl message type %#x", type); - break; - } -} - -static void tcpm_pd_rx_handler(struct work_struct *work) -{ - struct pd_rx_event *event = container_of(work, - struct pd_rx_event, work); - const struct pd_message *msg = &event->msg; - unsigned int cnt = pd_header_cnt_le(msg->header); - struct tcpm_port *port = event->port; - - mutex_lock(&port->lock); - - tcpm_log(port, "PD RX, header: %#x [%d]", le16_to_cpu(msg->header), - port->attached); - - if (port->attached) { - enum pd_ctrl_msg_type type = pd_header_type_le(msg->header); - unsigned int msgid = pd_header_msgid_le(msg->header); - - /* - * USB PD standard, 6.6.1.2: - * "... if MessageID value in a received Message is the - * same as the stored value, the receiver shall return a - * GoodCRC Message with that MessageID value and drop - * the Message (this is a retry of an already received - * Message). Note: this shall not apply to the Soft_Reset - * Message which always has a MessageID value of zero." - */ - if (msgid == port->rx_msgid && type != PD_CTRL_SOFT_RESET) - goto done; - port->rx_msgid = msgid; - - /* - * If both ends believe to be DFP/host, we have a data role - * mismatch. - */ - if (!!(le16_to_cpu(msg->header) & PD_HEADER_DATA_ROLE) == - (port->data_role == TYPEC_HOST)) { - tcpm_log(port, - "Data role mismatch, initiating error recovery"); - tcpm_set_state(port, ERROR_RECOVERY, 0); - } else { - if (cnt) - tcpm_pd_data_request(port, msg); - else - tcpm_pd_ctrl_request(port, msg); - } - } - -done: - mutex_unlock(&port->lock); - kfree(event); -} - -void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg) -{ - struct pd_rx_event *event; - - event = kzalloc(sizeof(*event), GFP_ATOMIC); - if (!event) - return; - - INIT_WORK(&event->work, tcpm_pd_rx_handler); - event->port = port; - memcpy(&event->msg, msg, sizeof(*msg)); - queue_work(port->wq, &event->work); -} -EXPORT_SYMBOL_GPL(tcpm_pd_receive); - -static int tcpm_pd_send_control(struct tcpm_port *port, - enum pd_ctrl_msg_type type) -{ - struct pd_message msg; - - memset(&msg, 0, sizeof(msg)); - msg.header = PD_HEADER_LE(type, port->pwr_role, - port->data_role, - port->message_id, 0); - - return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); -} - -/* - * Send queued message without affecting state. - * Return true if state machine should go back to sleep, - * false otherwise. - */ -static bool tcpm_send_queued_message(struct tcpm_port *port) -{ - enum pd_msg_request queued_message; - - do { - queued_message = port->queued_message; - port->queued_message = PD_MSG_NONE; - - switch (queued_message) { - case PD_MSG_CTRL_WAIT: - tcpm_pd_send_control(port, PD_CTRL_WAIT); - break; - case PD_MSG_CTRL_REJECT: - tcpm_pd_send_control(port, PD_CTRL_REJECT); - break; - case PD_MSG_DATA_SINK_CAP: - tcpm_pd_send_sink_caps(port); - break; - case PD_MSG_DATA_SOURCE_CAP: - tcpm_pd_send_source_caps(port); - break; - default: - break; - } - } while (port->queued_message != PD_MSG_NONE); - - if (port->delayed_state != INVALID_STATE) { - if (time_is_after_jiffies(port->delayed_runtime)) { - mod_delayed_work(port->wq, &port->state_machine, - port->delayed_runtime - jiffies); - return true; - } - port->delayed_state = INVALID_STATE; - } - return false; -} - -static int tcpm_pd_check_request(struct tcpm_port *port) -{ - u32 pdo, rdo = port->sink_request; - unsigned int max, op, pdo_max, index; - enum pd_pdo_type type; - - index = rdo_index(rdo); - if (!index || index > port->nr_src_pdo) - return -EINVAL; - - pdo = port->src_pdo[index - 1]; - type = pdo_type(pdo); - switch (type) { - case PDO_TYPE_FIXED: - case PDO_TYPE_VAR: - max = rdo_max_current(rdo); - op = rdo_op_current(rdo); - pdo_max = pdo_max_current(pdo); - - if (op > pdo_max) - return -EINVAL; - if (max > pdo_max && !(rdo & RDO_CAP_MISMATCH)) - return -EINVAL; - - if (type == PDO_TYPE_FIXED) - tcpm_log(port, - "Requested %u mV, %u mA for %u / %u mA", - pdo_fixed_voltage(pdo), pdo_max, op, max); - else - tcpm_log(port, - "Requested %u -> %u mV, %u mA for %u / %u mA", - pdo_min_voltage(pdo), pdo_max_voltage(pdo), - pdo_max, op, max); - break; - case PDO_TYPE_BATT: - max = rdo_max_power(rdo); - op = rdo_op_power(rdo); - pdo_max = pdo_max_power(pdo); - - if (op > pdo_max) - return -EINVAL; - if (max > pdo_max && !(rdo & RDO_CAP_MISMATCH)) - return -EINVAL; - tcpm_log(port, - "Requested %u -> %u mV, %u mW for %u / %u mW", - pdo_min_voltage(pdo), pdo_max_voltage(pdo), - pdo_max, op, max); - break; - default: - return -EINVAL; - } - - port->op_vsafe5v = index == 1; - - return 0; -} - -static int tcpm_pd_select_pdo(struct tcpm_port *port) -{ - unsigned int i, max_mw = 0, max_mv = 0; - int ret = -EINVAL; - - /* - * Select the source PDO providing the most power while staying within - * the board's voltage limits. Prefer PDO providing exp - */ - for (i = 0; i < port->nr_source_caps; i++) { - u32 pdo = port->source_caps[i]; - enum pd_pdo_type type = pdo_type(pdo); - unsigned int mv, ma, mw; - - if (type == PDO_TYPE_FIXED) - mv = pdo_fixed_voltage(pdo); - else - mv = pdo_min_voltage(pdo); - - if (type == PDO_TYPE_BATT) { - mw = pdo_max_power(pdo); - } else { - ma = min(pdo_max_current(pdo), - port->max_snk_ma); - mw = ma * mv / 1000; - } - - /* Perfer higher voltages if available */ - if ((mw > max_mw || (mw == max_mw && mv > max_mv)) && - mv <= port->max_snk_mv) { - ret = i; - max_mw = mw; - max_mv = mv; - } - } - - return ret; -} - -static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) -{ - unsigned int mv, ma, mw, flags; - unsigned int max_ma, max_mw; - enum pd_pdo_type type; - int index; - u32 pdo; - - index = tcpm_pd_select_pdo(port); - if (index < 0) - return -EINVAL; - pdo = port->source_caps[index]; - type = pdo_type(pdo); - - if (type == PDO_TYPE_FIXED) - mv = pdo_fixed_voltage(pdo); - else - mv = pdo_min_voltage(pdo); - - /* Select maximum available current within the board's power limit */ - if (type == PDO_TYPE_BATT) { - mw = pdo_max_power(pdo); - ma = 1000 * min(mw, port->max_snk_mw) / mv; - } else { - ma = min(pdo_max_current(pdo), - 1000 * port->max_snk_mw / mv); - } - ma = min(ma, port->max_snk_ma); - - flags = RDO_USB_COMM | RDO_NO_SUSPEND; - - /* Set mismatch bit if offered power is less than operating power */ - mw = ma * mv / 1000; - max_ma = ma; - max_mw = mw; - if (mw < port->operating_snk_mw) { - flags |= RDO_CAP_MISMATCH; - max_mw = port->operating_snk_mw; - max_ma = max_mw * 1000 / mv; - } - - tcpm_log(port, "cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d", - port->cc_req, port->cc1, port->cc2, port->vbus_source, - port->vconn_role == TYPEC_SOURCE ? "source" : "sink", - port->polarity); - - if (type == PDO_TYPE_BATT) { - *rdo = RDO_BATT(index + 1, mw, max_mw, flags); - - tcpm_log(port, "Requesting PDO %d: %u mV, %u mW%s", - index, mv, mw, - flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); - } else { - *rdo = RDO_FIXED(index + 1, ma, max_ma, flags); - - tcpm_log(port, "Requesting PDO %d: %u mV, %u mA%s", - index, mv, ma, - flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); - } - - port->current_limit = ma; - port->supply_voltage = mv; - - return 0; -} - -static int tcpm_pd_send_request(struct tcpm_port *port) -{ - struct pd_message msg; - int ret; - u32 rdo; - - ret = tcpm_pd_build_request(port, &rdo); - if (ret < 0) - return ret; - - memset(&msg, 0, sizeof(msg)); - msg.header = PD_HEADER_LE(PD_DATA_REQUEST, - port->pwr_role, - port->data_role, - port->message_id, 1); - msg.payload[0] = cpu_to_le32(rdo); - - return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); -} - -static int tcpm_set_vbus(struct tcpm_port *port, bool enable) -{ - int ret; - - if (enable && port->vbus_charge) - return -EINVAL; - - tcpm_log(port, "vbus:=%d charge=%d", enable, port->vbus_charge); - - ret = port->tcpc->set_vbus(port->tcpc, enable, port->vbus_charge); - if (ret < 0) - return ret; - - port->vbus_source = enable; - return 0; -} - -static int tcpm_set_charge(struct tcpm_port *port, bool charge) -{ - int ret; - - if (charge && port->vbus_source) - return -EINVAL; - - if (charge != port->vbus_charge) { - tcpm_log(port, "vbus=%d charge:=%d", port->vbus_source, charge); - ret = port->tcpc->set_vbus(port->tcpc, port->vbus_source, - charge); - if (ret < 0) - return ret; - } - port->vbus_charge = charge; - return 0; -} - -static bool tcpm_start_drp_toggling(struct tcpm_port *port) -{ - int ret; - - if (port->tcpc->start_drp_toggling && - port->port_type == TYPEC_PORT_DRP) { - tcpm_log_force(port, "Start DRP toggling"); - ret = port->tcpc->start_drp_toggling(port->tcpc, - tcpm_rp_cc(port)); - if (!ret) - return true; - } - - return false; -} - -static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc) -{ - tcpm_log(port, "cc:=%d", cc); - port->cc_req = cc; - port->tcpc->set_cc(port->tcpc, cc); -} - -static int tcpm_init_vbus(struct tcpm_port *port) -{ - int ret; - - ret = port->tcpc->set_vbus(port->tcpc, false, false); - port->vbus_source = false; - port->vbus_charge = false; - return ret; -} - -static int tcpm_init_vconn(struct tcpm_port *port) -{ - int ret; - - ret = port->tcpc->set_vconn(port->tcpc, false); - port->vconn_role = TYPEC_SINK; - return ret; -} - -static void tcpm_typec_connect(struct tcpm_port *port) -{ - if (!port->connected) { - /* Make sure we don't report stale identity information */ - memset(&port->partner_ident, 0, sizeof(port->partner_ident)); - port->partner_desc.usb_pd = port->pd_capable; - if (tcpm_port_is_debug(port)) - port->partner_desc.accessory = TYPEC_ACCESSORY_DEBUG; - else if (tcpm_port_is_audio(port)) - port->partner_desc.accessory = TYPEC_ACCESSORY_AUDIO; - else - port->partner_desc.accessory = TYPEC_ACCESSORY_NONE; - port->partner = typec_register_partner(port->typec_port, - &port->partner_desc); - port->connected = true; - } -} - -static int tcpm_src_attach(struct tcpm_port *port) -{ - enum typec_cc_polarity polarity = - port->cc2 == TYPEC_CC_RD ? TYPEC_POLARITY_CC2 - : TYPEC_POLARITY_CC1; - int ret; - - if (port->attached) - return 0; - - ret = tcpm_set_polarity(port, polarity); - if (ret < 0) - return ret; - - ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST); - if (ret < 0) - return ret; - - ret = port->tcpc->set_pd_rx(port->tcpc, true); - if (ret < 0) - goto out_disable_mux; - - /* - * USB Type-C specification, version 1.2, - * chapter 4.5.2.2.8.1 (Attached.SRC Requirements) - * Enable VCONN only if the non-RD port is set to RA. - */ - if ((polarity == TYPEC_POLARITY_CC1 && port->cc2 == TYPEC_CC_RA) || - (polarity == TYPEC_POLARITY_CC2 && port->cc1 == TYPEC_CC_RA)) { - ret = tcpm_set_vconn(port, true); - if (ret < 0) - goto out_disable_pd; - } - - ret = tcpm_set_vbus(port, true); - if (ret < 0) - goto out_disable_vconn; - - port->pd_capable = false; - - port->partner = NULL; - - port->attached = true; - port->send_discover = true; - - return 0; - -out_disable_vconn: - tcpm_set_vconn(port, false); -out_disable_pd: - port->tcpc->set_pd_rx(port->tcpc, false); -out_disable_mux: - tcpm_mux_set(port, TYPEC_MUX_NONE, TCPC_USB_SWITCH_DISCONNECT); - return ret; -} - -static void tcpm_typec_disconnect(struct tcpm_port *port) -{ - if (port->connected) { - typec_unregister_partner(port->partner); - port->partner = NULL; - port->connected = false; - } -} - -static void tcpm_unregister_altmodes(struct tcpm_port *port) -{ - struct pd_mode_data *modep = &port->mode_data; - int i; - - for (i = 0; i < modep->altmodes; i++) { - typec_unregister_altmode(port->partner_altmode[i]); - port->partner_altmode[i] = NULL; - } - - memset(modep, 0, sizeof(*modep)); -} - -static void tcpm_reset_port(struct tcpm_port *port) -{ - tcpm_unregister_altmodes(port); - tcpm_typec_disconnect(port); - port->attached = false; - port->pd_capable = false; - - /* - * First Rx ID should be 0; set this to a sentinel of -1 so that - * we can check tcpm_pd_rx_handler() if we had seen it before. - */ - port->rx_msgid = -1; - - port->tcpc->set_pd_rx(port->tcpc, false); - tcpm_init_vbus(port); /* also disables charging */ - tcpm_init_vconn(port); - tcpm_set_current_limit(port, 0, 0); - tcpm_set_polarity(port, TYPEC_POLARITY_CC1); - tcpm_set_attached_state(port, false); - port->try_src_count = 0; - port->try_snk_count = 0; -} - -static void tcpm_detach(struct tcpm_port *port) -{ - if (!port->attached) - return; - - if (tcpm_port_is_disconnected(port)) - port->hard_reset_count = 0; - - tcpm_reset_port(port); -} - -static void tcpm_src_detach(struct tcpm_port *port) -{ - tcpm_detach(port); -} - -static int tcpm_snk_attach(struct tcpm_port *port) -{ - int ret; - - if (port->attached) - return 0; - - ret = tcpm_set_polarity(port, port->cc2 != TYPEC_CC_OPEN ? - TYPEC_POLARITY_CC2 : TYPEC_POLARITY_CC1); - if (ret < 0) - return ret; - - ret = tcpm_set_roles(port, true, TYPEC_SINK, TYPEC_DEVICE); - if (ret < 0) - return ret; - - port->pd_capable = false; - - port->partner = NULL; - - port->attached = true; - port->send_discover = true; - - return 0; -} - -static void tcpm_snk_detach(struct tcpm_port *port) -{ - tcpm_detach(port); - - /* XXX: (Dis)connect SuperSpeed mux? */ -} - -static int tcpm_acc_attach(struct tcpm_port *port) -{ - int ret; - - if (port->attached) - return 0; - - ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST); - if (ret < 0) - return ret; - - port->partner = NULL; - - tcpm_typec_connect(port); - - port->attached = true; - - return 0; -} - -static void tcpm_acc_detach(struct tcpm_port *port) -{ - tcpm_detach(port); -} - -static inline enum tcpm_state hard_reset_state(struct tcpm_port *port) -{ - if (port->hard_reset_count < PD_N_HARD_RESET_COUNT) - return HARD_RESET_SEND; - if (port->pd_capable) - return ERROR_RECOVERY; - if (port->pwr_role == TYPEC_SOURCE) - return SRC_UNATTACHED; - if (port->state == SNK_WAIT_CAPABILITIES) - return SNK_READY; - return SNK_UNATTACHED; -} - -static inline enum tcpm_state ready_state(struct tcpm_port *port) -{ - if (port->pwr_role == TYPEC_SOURCE) - return SRC_READY; - else - return SNK_READY; -} - -static inline enum tcpm_state unattached_state(struct tcpm_port *port) -{ - if (port->port_type == TYPEC_PORT_DRP) { - if (port->pwr_role == TYPEC_SOURCE) - return SRC_UNATTACHED; - else - return SNK_UNATTACHED; - } else if (port->port_type == TYPEC_PORT_DFP) { - return SRC_UNATTACHED; - } - - return SNK_UNATTACHED; -} - -static void tcpm_check_send_discover(struct tcpm_port *port) -{ - if (port->data_role == TYPEC_HOST && port->send_discover && - port->pd_capable) { - tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0); - port->send_discover = false; - } -} - -static void tcpm_swap_complete(struct tcpm_port *port, int result) -{ - if (port->swap_pending) { - port->swap_status = result; - port->swap_pending = false; - port->non_pd_role_swap = false; - complete(&port->swap_complete); - } -} - -static enum typec_pwr_opmode tcpm_get_pwr_opmode(enum typec_cc_status cc) -{ - switch (cc) { - case TYPEC_CC_RP_1_5: - return TYPEC_PWR_MODE_1_5A; - case TYPEC_CC_RP_3_0: - return TYPEC_PWR_MODE_3_0A; - case TYPEC_CC_RP_DEF: - default: - return TYPEC_PWR_MODE_USB; - } -} - -static void run_state_machine(struct tcpm_port *port) -{ - int ret; - enum typec_pwr_opmode opmode; - unsigned int msecs; - - port->enter_state = port->state; - switch (port->state) { - case DRP_TOGGLING: - break; - /* SRC states */ - case SRC_UNATTACHED: - if (!port->non_pd_role_swap) - tcpm_swap_complete(port, -ENOTCONN); - tcpm_src_detach(port); - if (tcpm_start_drp_toggling(port)) { - tcpm_set_state(port, DRP_TOGGLING, 0); - break; - } - tcpm_set_cc(port, tcpm_rp_cc(port)); - if (port->port_type == TYPEC_PORT_DRP) - tcpm_set_state(port, SNK_UNATTACHED, PD_T_DRP_SNK); - break; - case SRC_ATTACH_WAIT: - if (tcpm_port_is_debug(port)) - tcpm_set_state(port, DEBUG_ACC_ATTACHED, - PD_T_CC_DEBOUNCE); - else if (tcpm_port_is_audio(port)) - tcpm_set_state(port, AUDIO_ACC_ATTACHED, - PD_T_CC_DEBOUNCE); - else if (tcpm_port_is_source(port)) - tcpm_set_state(port, - tcpm_try_snk(port) ? SNK_TRY - : SRC_ATTACHED, - PD_T_CC_DEBOUNCE); - break; - - case SNK_TRY: - port->try_snk_count++; - /* - * Requirements: - * - Do not drive vconn or vbus - * - Terminate CC pins (both) to Rd - * Action: - * - Wait for tDRPTry (PD_T_DRP_TRY). - * Until then, ignore any state changes. - */ - tcpm_set_cc(port, TYPEC_CC_RD); - tcpm_set_state(port, SNK_TRY_WAIT, PD_T_DRP_TRY); - break; - case SNK_TRY_WAIT: - if (tcpm_port_is_sink(port)) { - tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE, 0); - } else { - tcpm_set_state(port, SRC_TRYWAIT, 0); - port->max_wait = 0; - } - break; - case SNK_TRY_WAIT_DEBOUNCE: - tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS, - PD_T_PD_DEBOUNCE); - break; - case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: - if (port->vbus_present && tcpm_port_is_sink(port)) { - tcpm_set_state(port, SNK_ATTACHED, 0); - } else { - tcpm_set_state(port, SRC_TRYWAIT, 0); - port->max_wait = 0; - } - break; - case SRC_TRYWAIT: - tcpm_set_cc(port, tcpm_rp_cc(port)); - if (port->max_wait == 0) { - port->max_wait = jiffies + - msecs_to_jiffies(PD_T_DRP_TRY); - tcpm_set_state(port, SRC_TRYWAIT_UNATTACHED, - PD_T_DRP_TRY); - } else { - if (time_is_after_jiffies(port->max_wait)) - tcpm_set_state(port, SRC_TRYWAIT_UNATTACHED, - jiffies_to_msecs(port->max_wait - - jiffies)); - else - tcpm_set_state(port, SNK_UNATTACHED, 0); - } - break; - case SRC_TRYWAIT_DEBOUNCE: - tcpm_set_state(port, SRC_ATTACHED, PD_T_CC_DEBOUNCE); - break; - case SRC_TRYWAIT_UNATTACHED: - tcpm_set_state(port, SNK_UNATTACHED, 0); - break; - - case SRC_ATTACHED: - ret = tcpm_src_attach(port); - tcpm_set_state(port, SRC_UNATTACHED, - ret < 0 ? 0 : PD_T_PS_SOURCE_ON); - break; - case SRC_STARTUP: - opmode = tcpm_get_pwr_opmode(tcpm_rp_cc(port)); - typec_set_pwr_opmode(port->typec_port, opmode); - port->pwr_opmode = TYPEC_PWR_MODE_USB; - port->caps_count = 0; - port->message_id = 0; - port->rx_msgid = -1; - port->explicit_contract = false; - tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); - break; - case SRC_SEND_CAPABILITIES: - port->caps_count++; - if (port->caps_count > PD_N_CAPS_COUNT) { - tcpm_set_state(port, SRC_READY, 0); - break; - } - ret = tcpm_pd_send_source_caps(port); - if (ret < 0) { - tcpm_set_state(port, SRC_SEND_CAPABILITIES, - PD_T_SEND_SOURCE_CAP); - } else { - /* - * Per standard, we should clear the reset counter here. - * However, that can result in state machine hang-ups. - * Reset it only in READY state to improve stability. - */ - /* port->hard_reset_count = 0; */ - port->caps_count = 0; - port->pd_capable = true; - tcpm_set_state_cond(port, hard_reset_state(port), - PD_T_SEND_SOURCE_CAP); - } - break; - case SRC_NEGOTIATE_CAPABILITIES: - ret = tcpm_pd_check_request(port); - if (ret < 0) { - tcpm_pd_send_control(port, PD_CTRL_REJECT); - if (!port->explicit_contract) { - tcpm_set_state(port, - SRC_WAIT_NEW_CAPABILITIES, 0); - } else { - tcpm_set_state(port, SRC_READY, 0); - } - } else { - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); - tcpm_set_state(port, SRC_TRANSITION_SUPPLY, - PD_T_SRC_TRANSITION); - } - break; - case SRC_TRANSITION_SUPPLY: - /* XXX: regulator_set_voltage(vbus, ...) */ - tcpm_pd_send_control(port, PD_CTRL_PS_RDY); - port->explicit_contract = true; - typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_PD); - port->pwr_opmode = TYPEC_PWR_MODE_PD; - tcpm_set_state_cond(port, SRC_READY, 0); - break; - case SRC_READY: -#if 1 - port->hard_reset_count = 0; -#endif - port->try_src_count = 0; - - tcpm_swap_complete(port, 0); - tcpm_typec_connect(port); - tcpm_check_send_discover(port); - /* - * 6.3.5 - * Sending ping messages is not necessary if - * - the source operates at vSafe5V - * or - * - The system is not operating in PD mode - * or - * - Both partners are connected using a Type-C connector - * - * There is no actual need to send PD messages since the local - * port type-c and the spec does not clearly say whether PD is - * possible when type-c is connected to Type-A/B - */ - break; - case SRC_WAIT_NEW_CAPABILITIES: - /* Nothing to do... */ - break; - - /* SNK states */ - case SNK_UNATTACHED: - if (!port->non_pd_role_swap) - tcpm_swap_complete(port, -ENOTCONN); - tcpm_snk_detach(port); - if (tcpm_start_drp_toggling(port)) { - tcpm_set_state(port, DRP_TOGGLING, 0); - break; - } - tcpm_set_cc(port, TYPEC_CC_RD); - if (port->port_type == TYPEC_PORT_DRP) - tcpm_set_state(port, SRC_UNATTACHED, PD_T_DRP_SRC); - break; - case SNK_ATTACH_WAIT: - if ((port->cc1 == TYPEC_CC_OPEN && - port->cc2 != TYPEC_CC_OPEN) || - (port->cc1 != TYPEC_CC_OPEN && - port->cc2 == TYPEC_CC_OPEN)) - tcpm_set_state(port, SNK_DEBOUNCED, - PD_T_CC_DEBOUNCE); - else if (tcpm_port_is_disconnected(port)) - tcpm_set_state(port, SNK_UNATTACHED, - PD_T_PD_DEBOUNCE); - break; - case SNK_DEBOUNCED: - if (tcpm_port_is_disconnected(port)) - tcpm_set_state(port, SNK_UNATTACHED, - PD_T_PD_DEBOUNCE); - else if (port->vbus_present) - tcpm_set_state(port, - tcpm_try_src(port) ? SRC_TRY - : SNK_ATTACHED, - 0); - else - /* Wait for VBUS, but not forever */ - tcpm_set_state(port, PORT_RESET, PD_T_PS_SOURCE_ON); - break; - - case SRC_TRY: - port->try_src_count++; - tcpm_set_cc(port, tcpm_rp_cc(port)); - port->max_wait = 0; - tcpm_set_state(port, SRC_TRY_WAIT, 0); - break; - case SRC_TRY_WAIT: - if (port->max_wait == 0) { - port->max_wait = jiffies + - msecs_to_jiffies(PD_T_DRP_TRY); - msecs = PD_T_DRP_TRY; - } else { - if (time_is_after_jiffies(port->max_wait)) - msecs = jiffies_to_msecs(port->max_wait - - jiffies); - else - msecs = 0; - } - tcpm_set_state(port, SNK_TRYWAIT, msecs); - break; - case SRC_TRY_DEBOUNCE: - tcpm_set_state(port, SRC_ATTACHED, PD_T_PD_DEBOUNCE); - break; - case SNK_TRYWAIT: - tcpm_set_cc(port, TYPEC_CC_RD); - tcpm_set_state(port, SNK_TRYWAIT_VBUS, PD_T_CC_DEBOUNCE); - break; - case SNK_TRYWAIT_VBUS: - /* - * TCPM stays in this state indefinitely until VBUS - * is detected as long as Rp is not detected for - * more than a time period of tPDDebounce. - */ - if (port->vbus_present && tcpm_port_is_sink(port)) { - tcpm_set_state(port, SNK_ATTACHED, 0); - break; - } - if (!tcpm_port_is_sink(port)) - tcpm_set_state(port, SNK_TRYWAIT_DEBOUNCE, 0); - break; - case SNK_TRYWAIT_DEBOUNCE: - tcpm_set_state(port, SNK_UNATTACHED, PD_T_PD_DEBOUNCE); - break; - case SNK_ATTACHED: - ret = tcpm_snk_attach(port); - if (ret < 0) - tcpm_set_state(port, SNK_UNATTACHED, 0); - else - tcpm_set_state(port, SNK_STARTUP, 0); - break; - case SNK_STARTUP: - opmode = tcpm_get_pwr_opmode(port->polarity ? - port->cc2 : port->cc1); - typec_set_pwr_opmode(port->typec_port, opmode); - port->pwr_opmode = TYPEC_PWR_MODE_USB; - port->message_id = 0; - port->rx_msgid = -1; - port->explicit_contract = false; - tcpm_set_state(port, SNK_DISCOVERY, 0); - break; - case SNK_DISCOVERY: - if (port->vbus_present) { - tcpm_set_current_limit(port, - tcpm_get_current_limit(port), - 5000); - tcpm_set_charge(port, true); - tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); - break; - } - /* - * For DRP, timeouts differ. Also, handling is supposed to be - * different and much more complex (dead battery detection; - * see USB power delivery specification, section 8.3.3.6.1.5.1). - */ - tcpm_set_state(port, hard_reset_state(port), - port->port_type == TYPEC_PORT_DRP ? - PD_T_DB_DETECT : PD_T_NO_RESPONSE); - break; - case SNK_DISCOVERY_DEBOUNCE: - tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE_DONE, - PD_T_CC_DEBOUNCE); - break; - case SNK_DISCOVERY_DEBOUNCE_DONE: - if (!tcpm_port_is_disconnected(port) && - tcpm_port_is_sink(port) && - time_is_after_jiffies(port->delayed_runtime)) { - tcpm_set_state(port, SNK_DISCOVERY, - port->delayed_runtime - jiffies); - break; - } - tcpm_set_state(port, unattached_state(port), 0); - break; - case SNK_WAIT_CAPABILITIES: - ret = port->tcpc->set_pd_rx(port->tcpc, true); - if (ret < 0) { - tcpm_set_state(port, SNK_READY, 0); - break; - } - /* - * If VBUS has never been low, and we time out waiting - * for source cap, try a soft reset first, in case we - * were already in a stable contract before this boot. - * Do this only once. - */ - if (port->vbus_never_low) { - port->vbus_never_low = false; - tcpm_set_state(port, SOFT_RESET_SEND, - PD_T_SINK_WAIT_CAP); - } else { - tcpm_set_state(port, hard_reset_state(port), - PD_T_SINK_WAIT_CAP); - } - break; - case SNK_NEGOTIATE_CAPABILITIES: - port->pd_capable = true; - port->hard_reset_count = 0; - ret = tcpm_pd_send_request(port); - if (ret < 0) { - /* Let the Source send capabilities again. */ - tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); - } else { - tcpm_set_state_cond(port, hard_reset_state(port), - PD_T_SENDER_RESPONSE); - } - break; - case SNK_TRANSITION_SINK: - case SNK_TRANSITION_SINK_VBUS: - tcpm_set_state(port, hard_reset_state(port), - PD_T_PS_TRANSITION); - break; - case SNK_READY: - port->try_snk_count = 0; - if (port->explicit_contract) { - typec_set_pwr_opmode(port->typec_port, - TYPEC_PWR_MODE_PD); - port->pwr_opmode = TYPEC_PWR_MODE_PD; - } - - tcpm_swap_complete(port, 0); - tcpm_typec_connect(port); - tcpm_check_send_discover(port); - break; - - /* Accessory states */ - case ACC_UNATTACHED: - tcpm_acc_detach(port); - tcpm_set_state(port, SRC_UNATTACHED, 0); - break; - case DEBUG_ACC_ATTACHED: - case AUDIO_ACC_ATTACHED: - ret = tcpm_acc_attach(port); - if (ret < 0) - tcpm_set_state(port, ACC_UNATTACHED, 0); - break; - case AUDIO_ACC_DEBOUNCE: - tcpm_set_state(port, ACC_UNATTACHED, PD_T_CC_DEBOUNCE); - break; - - /* Hard_Reset states */ - case HARD_RESET_SEND: - tcpm_pd_transmit(port, TCPC_TX_HARD_RESET, NULL); - tcpm_set_state(port, HARD_RESET_START, 0); - break; - case HARD_RESET_START: - port->hard_reset_count++; - port->tcpc->set_pd_rx(port->tcpc, false); - tcpm_unregister_altmodes(port); - port->send_discover = true; - if (port->pwr_role == TYPEC_SOURCE) - tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF, - PD_T_PS_HARD_RESET); - else - tcpm_set_state(port, SNK_HARD_RESET_SINK_OFF, 0); - break; - case SRC_HARD_RESET_VBUS_OFF: - tcpm_set_vconn(port, true); - tcpm_set_vbus(port, false); - tcpm_set_roles(port, false, TYPEC_SOURCE, TYPEC_HOST); - tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER); - break; - case SRC_HARD_RESET_VBUS_ON: - tcpm_set_vbus(port, true); - port->tcpc->set_pd_rx(port->tcpc, true); - tcpm_set_attached_state(port, true); - tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); - break; - case SNK_HARD_RESET_SINK_OFF: - tcpm_set_vconn(port, false); - tcpm_set_charge(port, false); - tcpm_set_roles(port, false, TYPEC_SINK, TYPEC_DEVICE); - /* - * VBUS may or may not toggle, depending on the adapter. - * If it doesn't toggle, transition to SNK_HARD_RESET_SINK_ON - * directly after timeout. - */ - tcpm_set_state(port, SNK_HARD_RESET_SINK_ON, PD_T_SAFE_0V); - break; - case SNK_HARD_RESET_WAIT_VBUS: - /* Assume we're disconnected if VBUS doesn't come back. */ - tcpm_set_state(port, SNK_UNATTACHED, - PD_T_SRC_RECOVER_MAX + PD_T_SRC_TURN_ON); - break; - case SNK_HARD_RESET_SINK_ON: - /* Note: There is no guarantee that VBUS is on in this state */ - /* - * XXX: - * The specification suggests that dual mode ports in sink - * mode should transition to state PE_SRC_Transition_to_default. - * See USB power delivery specification chapter 8.3.3.6.1.3. - * This would mean to to - * - turn off VCONN, reset power supply - * - request hardware reset - * - turn on VCONN - * - Transition to state PE_Src_Startup - * SNK only ports shall transition to state Snk_Startup - * (see chapter 8.3.3.3.8). - * Similar, dual-mode ports in source mode should transition - * to PE_SNK_Transition_to_default. - */ - tcpm_set_attached_state(port, true); - tcpm_set_state(port, SNK_STARTUP, 0); - break; - - /* Soft_Reset states */ - case SOFT_RESET: - port->message_id = 0; - port->rx_msgid = -1; - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); - if (port->pwr_role == TYPEC_SOURCE) - tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); - else - tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); - break; - case SOFT_RESET_SEND: - port->message_id = 0; - port->rx_msgid = -1; - if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET)) - tcpm_set_state_cond(port, hard_reset_state(port), 0); - else - tcpm_set_state_cond(port, hard_reset_state(port), - PD_T_SENDER_RESPONSE); - break; - - /* DR_Swap states */ - case DR_SWAP_SEND: - tcpm_pd_send_control(port, PD_CTRL_DR_SWAP); - tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT, - PD_T_SENDER_RESPONSE); - break; - case DR_SWAP_ACCEPT: - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); - tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); - break; - case DR_SWAP_SEND_TIMEOUT: - tcpm_swap_complete(port, -ETIMEDOUT); - tcpm_set_state(port, ready_state(port), 0); - break; - case DR_SWAP_CHANGE_DR: - if (port->data_role == TYPEC_HOST) { - tcpm_unregister_altmodes(port); - tcpm_set_roles(port, true, port->pwr_role, - TYPEC_DEVICE); - } else { - tcpm_set_roles(port, true, port->pwr_role, - TYPEC_HOST); - port->send_discover = true; - } - tcpm_set_state(port, ready_state(port), 0); - break; - - /* PR_Swap states */ - case PR_SWAP_ACCEPT: - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); - tcpm_set_state(port, PR_SWAP_START, 0); - break; - case PR_SWAP_SEND: - tcpm_pd_send_control(port, PD_CTRL_PR_SWAP); - tcpm_set_state_cond(port, PR_SWAP_SEND_TIMEOUT, - PD_T_SENDER_RESPONSE); - break; - case PR_SWAP_SEND_TIMEOUT: - tcpm_swap_complete(port, -ETIMEDOUT); - tcpm_set_state(port, ready_state(port), 0); - break; - case PR_SWAP_START: - if (port->pwr_role == TYPEC_SOURCE) - tcpm_set_state(port, PR_SWAP_SRC_SNK_TRANSITION_OFF, - PD_T_SRC_TRANSITION); - else - tcpm_set_state(port, PR_SWAP_SNK_SRC_SINK_OFF, 0); - break; - case PR_SWAP_SRC_SNK_TRANSITION_OFF: - tcpm_set_vbus(port, false); - port->explicit_contract = false; - /* allow time for Vbus discharge, must be < tSrcSwapStdby */ - tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF, - PD_T_SRCSWAPSTDBY); - break; - case PR_SWAP_SRC_SNK_SOURCE_OFF: - tcpm_set_cc(port, TYPEC_CC_RD); - /* allow CC debounce */ - tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED, - PD_T_CC_DEBOUNCE); - break; - case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED: - /* - * USB-PD standard, 6.2.1.4, Port Power Role: - * "During the Power Role Swap Sequence, for the initial Source - * Port, the Port Power Role field shall be set to Sink in the - * PS_RDY Message indicating that the initial Source’s power - * supply is turned off" - */ - tcpm_set_pwr_role(port, TYPEC_SINK); - if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) { - tcpm_set_state(port, ERROR_RECOVERY, 0); - break; - } - tcpm_set_state_cond(port, SNK_UNATTACHED, PD_T_PS_SOURCE_ON); - break; - case PR_SWAP_SRC_SNK_SINK_ON: - tcpm_set_state(port, SNK_STARTUP, 0); - break; - case PR_SWAP_SNK_SRC_SINK_OFF: - tcpm_set_charge(port, false); - tcpm_set_state(port, hard_reset_state(port), - PD_T_PS_SOURCE_OFF); - break; - case PR_SWAP_SNK_SRC_SOURCE_ON: - tcpm_set_cc(port, tcpm_rp_cc(port)); - tcpm_set_vbus(port, true); - /* - * allow time VBUS ramp-up, must be < tNewSrc - * Also, this window overlaps with CC debounce as well. - * So, Wait for the max of two which is PD_T_NEWSRC - */ - tcpm_set_state(port, PR_SWAP_SNK_SRC_SOURCE_ON_VBUS_RAMPED_UP, - PD_T_NEWSRC); - break; - case PR_SWAP_SNK_SRC_SOURCE_ON_VBUS_RAMPED_UP: - /* - * USB PD standard, 6.2.1.4: - * "Subsequent Messages initiated by the Policy Engine, - * such as the PS_RDY Message sent to indicate that Vbus - * is ready, will have the Port Power Role field set to - * Source." - */ - tcpm_set_pwr_role(port, TYPEC_SOURCE); - tcpm_pd_send_control(port, PD_CTRL_PS_RDY); - tcpm_set_state(port, SRC_STARTUP, 0); - break; - - case VCONN_SWAP_ACCEPT: - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); - tcpm_set_state(port, VCONN_SWAP_START, 0); - break; - case VCONN_SWAP_SEND: - tcpm_pd_send_control(port, PD_CTRL_VCONN_SWAP); - tcpm_set_state(port, VCONN_SWAP_SEND_TIMEOUT, - PD_T_SENDER_RESPONSE); - break; - case VCONN_SWAP_SEND_TIMEOUT: - tcpm_swap_complete(port, -ETIMEDOUT); - tcpm_set_state(port, ready_state(port), 0); - break; - case VCONN_SWAP_START: - if (port->vconn_role == TYPEC_SOURCE) - tcpm_set_state(port, VCONN_SWAP_WAIT_FOR_VCONN, 0); - else - tcpm_set_state(port, VCONN_SWAP_TURN_ON_VCONN, 0); - break; - case VCONN_SWAP_WAIT_FOR_VCONN: - tcpm_set_state(port, hard_reset_state(port), - PD_T_VCONN_SOURCE_ON); - break; - case VCONN_SWAP_TURN_ON_VCONN: - tcpm_set_vconn(port, true); - tcpm_pd_send_control(port, PD_CTRL_PS_RDY); - tcpm_set_state(port, ready_state(port), 0); - break; - case VCONN_SWAP_TURN_OFF_VCONN: - tcpm_set_vconn(port, false); - tcpm_set_state(port, ready_state(port), 0); - break; - - case DR_SWAP_CANCEL: - case PR_SWAP_CANCEL: - case VCONN_SWAP_CANCEL: - tcpm_swap_complete(port, port->swap_status); - if (port->pwr_role == TYPEC_SOURCE) - tcpm_set_state(port, SRC_READY, 0); - else - tcpm_set_state(port, SNK_READY, 0); - break; - - case BIST_RX: - switch (BDO_MODE_MASK(port->bist_request)) { - case BDO_MODE_CARRIER2: - tcpm_pd_transmit(port, TCPC_TX_BIST_MODE_2, NULL); - break; - default: - break; - } - /* Always switch to unattached state */ - tcpm_set_state(port, unattached_state(port), 0); - break; - case ERROR_RECOVERY: - tcpm_swap_complete(port, -EPROTO); - tcpm_set_state(port, PORT_RESET, 0); - break; - case PORT_RESET: - tcpm_reset_port(port); - tcpm_set_cc(port, TYPEC_CC_OPEN); - tcpm_set_state(port, PORT_RESET_WAIT_OFF, - PD_T_ERROR_RECOVERY); - break; - case PORT_RESET_WAIT_OFF: - tcpm_set_state(port, - tcpm_default_state(port), - port->vbus_present ? PD_T_PS_SOURCE_OFF : 0); - break; - default: - WARN(1, "Unexpected port state %d\n", port->state); - break; - } -} - -static void tcpm_state_machine_work(struct work_struct *work) -{ - struct tcpm_port *port = container_of(work, struct tcpm_port, - state_machine.work); - enum tcpm_state prev_state; - - mutex_lock(&port->lock); - port->state_machine_running = true; - - if (port->queued_message && tcpm_send_queued_message(port)) - goto done; - - /* If we were queued due to a delayed state change, update it now */ - if (port->delayed_state) { - tcpm_log(port, "state change %s -> %s [delayed %ld ms]", - tcpm_states[port->state], - tcpm_states[port->delayed_state], port->delay_ms); - port->prev_state = port->state; - port->state = port->delayed_state; - port->delayed_state = INVALID_STATE; - } - - /* - * Continue running as long as we have (non-delayed) state changes - * to make. - */ - do { - prev_state = port->state; - run_state_machine(port); - if (port->queued_message) - tcpm_send_queued_message(port); - } while (port->state != prev_state && !port->delayed_state); - -done: - port->state_machine_running = false; - mutex_unlock(&port->lock); -} - -static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, - enum typec_cc_status cc2) -{ - enum typec_cc_status old_cc1, old_cc2; - enum tcpm_state new_state; - - old_cc1 = port->cc1; - old_cc2 = port->cc2; - port->cc1 = cc1; - port->cc2 = cc2; - - tcpm_log_force(port, - "CC1: %u -> %u, CC2: %u -> %u [state %s, polarity %d, %s]", - old_cc1, cc1, old_cc2, cc2, tcpm_states[port->state], - port->polarity, - tcpm_port_is_disconnected(port) ? "disconnected" - : "connected"); - - switch (port->state) { - case DRP_TOGGLING: - if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || - tcpm_port_is_source(port)) - tcpm_set_state(port, SRC_ATTACH_WAIT, 0); - else if (tcpm_port_is_sink(port)) - tcpm_set_state(port, SNK_ATTACH_WAIT, 0); - break; - case SRC_UNATTACHED: - case ACC_UNATTACHED: - if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || - tcpm_port_is_source(port)) - tcpm_set_state(port, SRC_ATTACH_WAIT, 0); - break; - case SRC_ATTACH_WAIT: - if (tcpm_port_is_disconnected(port) || - tcpm_port_is_audio_detached(port)) - tcpm_set_state(port, SRC_UNATTACHED, 0); - else if (cc1 != old_cc1 || cc2 != old_cc2) - tcpm_set_state(port, SRC_ATTACH_WAIT, 0); - break; - case SRC_ATTACHED: - case SRC_SEND_CAPABILITIES: - case SRC_READY: - if (tcpm_port_is_disconnected(port) || - !tcpm_port_is_source(port)) - tcpm_set_state(port, SRC_UNATTACHED, 0); - break; - case SNK_UNATTACHED: - if (tcpm_port_is_sink(port)) - tcpm_set_state(port, SNK_ATTACH_WAIT, 0); - break; - case SNK_ATTACH_WAIT: - if ((port->cc1 == TYPEC_CC_OPEN && - port->cc2 != TYPEC_CC_OPEN) || - (port->cc1 != TYPEC_CC_OPEN && - port->cc2 == TYPEC_CC_OPEN)) - new_state = SNK_DEBOUNCED; - else if (tcpm_port_is_disconnected(port)) - new_state = SNK_UNATTACHED; - else - break; - if (new_state != port->delayed_state) - tcpm_set_state(port, SNK_ATTACH_WAIT, 0); - break; - case SNK_DEBOUNCED: - if (tcpm_port_is_disconnected(port)) - new_state = SNK_UNATTACHED; - else if (port->vbus_present) - new_state = tcpm_try_src(port) ? SRC_TRY : SNK_ATTACHED; - else - new_state = SNK_UNATTACHED; - if (new_state != port->delayed_state) - tcpm_set_state(port, SNK_DEBOUNCED, 0); - break; - case SNK_READY: - if (tcpm_port_is_disconnected(port)) - tcpm_set_state(port, unattached_state(port), 0); - else if (!port->pd_capable && - (cc1 != old_cc1 || cc2 != old_cc2)) - tcpm_set_current_limit(port, - tcpm_get_current_limit(port), - 5000); - break; - - case AUDIO_ACC_ATTACHED: - if (cc1 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_OPEN) - tcpm_set_state(port, AUDIO_ACC_DEBOUNCE, 0); - break; - case AUDIO_ACC_DEBOUNCE: - if (tcpm_port_is_audio(port)) - tcpm_set_state(port, AUDIO_ACC_ATTACHED, 0); - break; - - case DEBUG_ACC_ATTACHED: - if (cc1 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_OPEN) - tcpm_set_state(port, ACC_UNATTACHED, 0); - break; - - case SNK_TRY: - /* Do nothing, waiting for timeout */ - break; - - case SNK_DISCOVERY: - /* CC line is unstable, wait for debounce */ - if (tcpm_port_is_disconnected(port)) - tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE, 0); - break; - case SNK_DISCOVERY_DEBOUNCE: - break; - - case SRC_TRYWAIT: - /* Hand over to state machine if needed */ - if (!port->vbus_present && tcpm_port_is_source(port)) - tcpm_set_state(port, SRC_TRYWAIT_DEBOUNCE, 0); - break; - case SRC_TRYWAIT_DEBOUNCE: - if (port->vbus_present || !tcpm_port_is_source(port)) - tcpm_set_state(port, SRC_TRYWAIT, 0); - break; - case SNK_TRY_WAIT_DEBOUNCE: - if (!tcpm_port_is_sink(port)) { - port->max_wait = 0; - tcpm_set_state(port, SRC_TRYWAIT, 0); - } - break; - case SRC_TRY_WAIT: - if (tcpm_port_is_source(port)) - tcpm_set_state(port, SRC_TRY_DEBOUNCE, 0); - break; - case SRC_TRY_DEBOUNCE: - tcpm_set_state(port, SRC_TRY_WAIT, 0); - break; - case SNK_TRYWAIT_DEBOUNCE: - if (tcpm_port_is_sink(port)) - tcpm_set_state(port, SNK_TRYWAIT_VBUS, 0); - break; - case SNK_TRYWAIT_VBUS: - if (!tcpm_port_is_sink(port)) - tcpm_set_state(port, SNK_TRYWAIT_DEBOUNCE, 0); - break; - case SNK_TRYWAIT: - /* Do nothing, waiting for tCCDebounce */ - break; - case PR_SWAP_SNK_SRC_SINK_OFF: - case PR_SWAP_SRC_SNK_TRANSITION_OFF: - case PR_SWAP_SRC_SNK_SOURCE_OFF: - case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED: - case PR_SWAP_SNK_SRC_SOURCE_ON: - /* - * CC state change is expected in PR_SWAP - * Ignore it. - */ - break; - - default: - if (tcpm_port_is_disconnected(port)) - tcpm_set_state(port, unattached_state(port), 0); - break; - } -} - -static void _tcpm_pd_vbus_on(struct tcpm_port *port) -{ - tcpm_log_force(port, "VBUS on"); - port->vbus_present = true; - switch (port->state) { - case SNK_TRANSITION_SINK_VBUS: - port->explicit_contract = true; - tcpm_set_state(port, SNK_READY, 0); - break; - case SNK_DISCOVERY: - tcpm_set_state(port, SNK_DISCOVERY, 0); - break; - - case SNK_DEBOUNCED: - tcpm_set_state(port, tcpm_try_src(port) ? SRC_TRY - : SNK_ATTACHED, - 0); - break; - case SNK_HARD_RESET_WAIT_VBUS: - tcpm_set_state(port, SNK_HARD_RESET_SINK_ON, 0); - break; - case SRC_ATTACHED: - tcpm_set_state(port, SRC_STARTUP, 0); - break; - case SRC_HARD_RESET_VBUS_ON: - tcpm_set_state(port, SRC_STARTUP, 0); - break; - - case SNK_TRY: - /* Do nothing, waiting for timeout */ - break; - case SRC_TRYWAIT: - /* Do nothing, Waiting for Rd to be detected */ - break; - case SRC_TRYWAIT_DEBOUNCE: - tcpm_set_state(port, SRC_TRYWAIT, 0); - break; - case SNK_TRY_WAIT_DEBOUNCE: - /* Do nothing, waiting for PD_DEBOUNCE to do be done */ - break; - case SNK_TRYWAIT: - /* Do nothing, waiting for tCCDebounce */ - break; - case SNK_TRYWAIT_VBUS: - if (tcpm_port_is_sink(port)) - tcpm_set_state(port, SNK_ATTACHED, 0); - break; - case SNK_TRYWAIT_DEBOUNCE: - /* Do nothing, waiting for Rp */ - break; - case SRC_TRY_WAIT: - case SRC_TRY_DEBOUNCE: - /* Do nothing, waiting for sink detection */ - break; - default: - break; - } -} - -static void _tcpm_pd_vbus_off(struct tcpm_port *port) -{ - tcpm_log_force(port, "VBUS off"); - port->vbus_present = false; - port->vbus_never_low = false; - switch (port->state) { - case SNK_HARD_RESET_SINK_OFF: - tcpm_set_state(port, SNK_HARD_RESET_WAIT_VBUS, 0); - break; - case SRC_HARD_RESET_VBUS_OFF: - tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, 0); - break; - case HARD_RESET_SEND: - break; - - case SNK_TRY: - /* Do nothing, waiting for timeout */ - break; - case SRC_TRYWAIT: - /* Hand over to state machine if needed */ - if (tcpm_port_is_source(port)) - tcpm_set_state(port, SRC_TRYWAIT_DEBOUNCE, 0); - break; - case SNK_TRY_WAIT_DEBOUNCE: - /* Do nothing, waiting for PD_DEBOUNCE to do be done */ - break; - case SNK_TRYWAIT: - case SNK_TRYWAIT_VBUS: - case SNK_TRYWAIT_DEBOUNCE: - break; - case SNK_ATTACH_WAIT: - tcpm_set_state(port, SNK_UNATTACHED, 0); - break; - - case SNK_NEGOTIATE_CAPABILITIES: - break; - - case PR_SWAP_SRC_SNK_TRANSITION_OFF: - tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF, 0); - break; - - case PR_SWAP_SNK_SRC_SINK_OFF: - /* Do nothing, expected */ - break; - - case PORT_RESET_WAIT_OFF: - tcpm_set_state(port, tcpm_default_state(port), 0); - break; - case SRC_TRY_WAIT: - case SRC_TRY_DEBOUNCE: - /* Do nothing, waiting for sink detection */ - break; - default: - if (port->pwr_role == TYPEC_SINK && - port->attached) - tcpm_set_state(port, SNK_UNATTACHED, 0); - break; - } -} - -static void _tcpm_pd_hard_reset(struct tcpm_port *port) -{ - tcpm_log_force(port, "Received hard reset"); - /* - * If we keep receiving hard reset requests, executing the hard reset - * must have failed. Revert to error recovery if that happens. - */ - tcpm_set_state(port, - port->hard_reset_count < PD_N_HARD_RESET_COUNT ? - HARD_RESET_START : ERROR_RECOVERY, - 0); -} - -static void tcpm_pd_event_handler(struct work_struct *work) -{ - struct tcpm_port *port = container_of(work, struct tcpm_port, - event_work); - u32 events; - - mutex_lock(&port->lock); - - spin_lock(&port->pd_event_lock); - while (port->pd_events) { - events = port->pd_events; - port->pd_events = 0; - spin_unlock(&port->pd_event_lock); - if (events & TCPM_RESET_EVENT) - _tcpm_pd_hard_reset(port); - if (events & TCPM_VBUS_EVENT) { - bool vbus; - - vbus = port->tcpc->get_vbus(port->tcpc); - if (vbus) - _tcpm_pd_vbus_on(port); - else - _tcpm_pd_vbus_off(port); - } - if (events & TCPM_CC_EVENT) { - enum typec_cc_status cc1, cc2; - - if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0) - _tcpm_cc_change(port, cc1, cc2); - } - spin_lock(&port->pd_event_lock); - } - spin_unlock(&port->pd_event_lock); - mutex_unlock(&port->lock); -} - -void tcpm_cc_change(struct tcpm_port *port) -{ - spin_lock(&port->pd_event_lock); - port->pd_events |= TCPM_CC_EVENT; - spin_unlock(&port->pd_event_lock); - queue_work(port->wq, &port->event_work); -} -EXPORT_SYMBOL_GPL(tcpm_cc_change); - -void tcpm_vbus_change(struct tcpm_port *port) -{ - spin_lock(&port->pd_event_lock); - port->pd_events |= TCPM_VBUS_EVENT; - spin_unlock(&port->pd_event_lock); - queue_work(port->wq, &port->event_work); -} -EXPORT_SYMBOL_GPL(tcpm_vbus_change); - -void tcpm_pd_hard_reset(struct tcpm_port *port) -{ - spin_lock(&port->pd_event_lock); - port->pd_events = TCPM_RESET_EVENT; - spin_unlock(&port->pd_event_lock); - queue_work(port->wq, &port->event_work); -} -EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset); - -static int tcpm_dr_set(const struct typec_capability *cap, - enum typec_data_role data) -{ - struct tcpm_port *port = typec_cap_to_tcpm(cap); - int ret; - - mutex_lock(&port->swap_lock); - mutex_lock(&port->lock); - - if (port->port_type != TYPEC_PORT_DRP) { - ret = -EINVAL; - goto port_unlock; - } - if (port->state != SRC_READY && port->state != SNK_READY) { - ret = -EAGAIN; - goto port_unlock; - } - - if (port->data_role == data) { - ret = 0; - goto port_unlock; - } - - /* - * XXX - * 6.3.9: If an alternate mode is active, a request to swap - * alternate modes shall trigger a port reset. - * Reject data role swap request in this case. - */ - - if (!port->pd_capable) { - /* - * If the partner is not PD capable, reset the port to - * trigger a role change. This can only work if a preferred - * role is configured, and if it matches the requested role. - */ - if (port->try_role == TYPEC_NO_PREFERRED_ROLE || - port->try_role == port->pwr_role) { - ret = -EINVAL; - goto port_unlock; - } - port->non_pd_role_swap = true; - tcpm_set_state(port, PORT_RESET, 0); - } else { - tcpm_set_state(port, DR_SWAP_SEND, 0); - } - - port->swap_status = 0; - port->swap_pending = true; - reinit_completion(&port->swap_complete); - mutex_unlock(&port->lock); - - if (!wait_for_completion_timeout(&port->swap_complete, - msecs_to_jiffies(PD_ROLE_SWAP_TIMEOUT))) - ret = -ETIMEDOUT; - else - ret = port->swap_status; - - port->non_pd_role_swap = false; - goto swap_unlock; - -port_unlock: - mutex_unlock(&port->lock); -swap_unlock: - mutex_unlock(&port->swap_lock); - return ret; -} - -static int tcpm_pr_set(const struct typec_capability *cap, - enum typec_role role) -{ - struct tcpm_port *port = typec_cap_to_tcpm(cap); - int ret; - - mutex_lock(&port->swap_lock); - mutex_lock(&port->lock); - - if (port->port_type != TYPEC_PORT_DRP) { - ret = -EINVAL; - goto port_unlock; - } - if (port->state != SRC_READY && port->state != SNK_READY) { - ret = -EAGAIN; - goto port_unlock; - } - - if (role == port->pwr_role) { - ret = 0; - goto port_unlock; - } - - port->swap_status = 0; - port->swap_pending = true; - reinit_completion(&port->swap_complete); - tcpm_set_state(port, PR_SWAP_SEND, 0); - mutex_unlock(&port->lock); - - if (!wait_for_completion_timeout(&port->swap_complete, - msecs_to_jiffies(PD_ROLE_SWAP_TIMEOUT))) - ret = -ETIMEDOUT; - else - ret = port->swap_status; - - goto swap_unlock; - -port_unlock: - mutex_unlock(&port->lock); -swap_unlock: - mutex_unlock(&port->swap_lock); - return ret; -} - -static int tcpm_vconn_set(const struct typec_capability *cap, - enum typec_role role) -{ - struct tcpm_port *port = typec_cap_to_tcpm(cap); - int ret; - - mutex_lock(&port->swap_lock); - mutex_lock(&port->lock); - - if (port->state != SRC_READY && port->state != SNK_READY) { - ret = -EAGAIN; - goto port_unlock; - } - - if (role == port->vconn_role) { - ret = 0; - goto port_unlock; - } - - port->swap_status = 0; - port->swap_pending = true; - reinit_completion(&port->swap_complete); - tcpm_set_state(port, VCONN_SWAP_SEND, 0); - mutex_unlock(&port->lock); - - if (!wait_for_completion_timeout(&port->swap_complete, - msecs_to_jiffies(PD_ROLE_SWAP_TIMEOUT))) - ret = -ETIMEDOUT; - else - ret = port->swap_status; - - goto swap_unlock; - -port_unlock: - mutex_unlock(&port->lock); -swap_unlock: - mutex_unlock(&port->swap_lock); - return ret; -} - -static int tcpm_try_role(const struct typec_capability *cap, int role) -{ - struct tcpm_port *port = typec_cap_to_tcpm(cap); - struct tcpc_dev *tcpc = port->tcpc; - int ret = 0; - - mutex_lock(&port->lock); - if (tcpc->try_role) - ret = tcpc->try_role(tcpc, role); - if (!ret && !tcpc->config->try_role_hw) - port->try_role = role; - port->try_src_count = 0; - port->try_snk_count = 0; - mutex_unlock(&port->lock); - - return ret; -} - -static void tcpm_init(struct tcpm_port *port) -{ - enum typec_cc_status cc1, cc2; - - port->tcpc->init(port->tcpc); - - tcpm_reset_port(port); - - /* - * XXX - * Should possibly wait for VBUS to settle if it was enabled locally - * since tcpm_reset_port() will disable VBUS. - */ - port->vbus_present = port->tcpc->get_vbus(port->tcpc); - if (port->vbus_present) - port->vbus_never_low = true; - - tcpm_set_state(port, tcpm_default_state(port), 0); - - if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0) - _tcpm_cc_change(port, cc1, cc2); - - /* - * Some adapters need a clean slate at startup, and won't recover - * otherwise. So do not try to be fancy and force a clean disconnect. - */ - tcpm_set_state(port, PORT_RESET, 0); -} - -static int tcpm_port_type_set(const struct typec_capability *cap, - enum typec_port_type type) -{ - struct tcpm_port *port = typec_cap_to_tcpm(cap); - - mutex_lock(&port->lock); - if (type == port->port_type) - goto port_unlock; - - port->port_type = type; - - if (!port->connected) { - tcpm_set_state(port, PORT_RESET, 0); - } else if (type == TYPEC_PORT_UFP) { - if (!(port->pwr_role == TYPEC_SINK && - port->data_role == TYPEC_DEVICE)) - tcpm_set_state(port, PORT_RESET, 0); - } else if (type == TYPEC_PORT_DFP) { - if (!(port->pwr_role == TYPEC_SOURCE && - port->data_role == TYPEC_HOST)) - tcpm_set_state(port, PORT_RESET, 0); - } - -port_unlock: - mutex_unlock(&port->lock); - return 0; -} - -void tcpm_tcpc_reset(struct tcpm_port *port) -{ - mutex_lock(&port->lock); - /* XXX: Maintain PD connection if possible? */ - tcpm_init(port); - mutex_unlock(&port->lock); -} -EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); - -static int tcpm_copy_pdos(u32 *dest_pdo, const u32 *src_pdo, - unsigned int nr_pdo) -{ - unsigned int i; - - if (nr_pdo > PDO_MAX_OBJECTS) - nr_pdo = PDO_MAX_OBJECTS; - - for (i = 0; i < nr_pdo; i++) - dest_pdo[i] = src_pdo[i]; - - return nr_pdo; -} - -static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo, - unsigned int nr_vdo) -{ - unsigned int i; - - if (nr_vdo > VDO_MAX_OBJECTS) - nr_vdo = VDO_MAX_OBJECTS; - - for (i = 0; i < nr_vdo; i++) - dest_vdo[i] = src_vdo[i]; - - return nr_vdo; -} - -void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo) -{ - mutex_lock(&port->lock); - port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, pdo, nr_pdo); - switch (port->state) { - case SRC_UNATTACHED: - case SRC_ATTACH_WAIT: - case SRC_TRYWAIT: - tcpm_set_cc(port, tcpm_rp_cc(port)); - break; - case SRC_SEND_CAPABILITIES: - case SRC_NEGOTIATE_CAPABILITIES: - case SRC_READY: - case SRC_WAIT_NEW_CAPABILITIES: - tcpm_set_cc(port, tcpm_rp_cc(port)); - tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); - break; - default: - break; - } - mutex_unlock(&port->lock); -} -EXPORT_SYMBOL_GPL(tcpm_update_source_capabilities); - -void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo, - unsigned int max_snk_mv, - unsigned int max_snk_ma, - unsigned int max_snk_mw, - unsigned int operating_snk_mw) -{ - mutex_lock(&port->lock); - port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, pdo, nr_pdo); - port->max_snk_mv = max_snk_mv; - port->max_snk_ma = max_snk_ma; - port->max_snk_mw = max_snk_mw; - port->operating_snk_mw = operating_snk_mw; - - switch (port->state) { - case SNK_NEGOTIATE_CAPABILITIES: - case SNK_READY: - case SNK_TRANSITION_SINK: - case SNK_TRANSITION_SINK_VBUS: - tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); - break; - default: - break; - } - mutex_unlock(&port->lock); -} -EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities); - -struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) -{ - struct tcpm_port *port; - int i, err; - - if (!dev || !tcpc || !tcpc->config || - !tcpc->get_vbus || !tcpc->set_cc || !tcpc->get_cc || - !tcpc->set_polarity || !tcpc->set_vconn || !tcpc->set_vbus || - !tcpc->set_pd_rx || !tcpc->set_roles || !tcpc->pd_transmit) - return ERR_PTR(-EINVAL); - - port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); - if (!port) - return ERR_PTR(-ENOMEM); - - port->dev = dev; - port->tcpc = tcpc; - - mutex_init(&port->lock); - mutex_init(&port->swap_lock); - - port->wq = create_singlethread_workqueue(dev_name(dev)); - if (!port->wq) - return ERR_PTR(-ENOMEM); - INIT_DELAYED_WORK(&port->state_machine, tcpm_state_machine_work); - INIT_DELAYED_WORK(&port->vdm_state_machine, vdm_state_machine_work); - INIT_WORK(&port->event_work, tcpm_pd_event_handler); - - spin_lock_init(&port->pd_event_lock); - - init_completion(&port->tx_complete); - init_completion(&port->swap_complete); - - port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcpc->config->src_pdo, - tcpc->config->nr_src_pdo); - port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo, - tcpc->config->nr_snk_pdo); - port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcpc->config->snk_vdo, - tcpc->config->nr_snk_vdo); - - port->max_snk_mv = tcpc->config->max_snk_mv; - port->max_snk_ma = tcpc->config->max_snk_ma; - port->max_snk_mw = tcpc->config->max_snk_mw; - port->operating_snk_mw = tcpc->config->operating_snk_mw; - if (!tcpc->config->try_role_hw) - port->try_role = tcpc->config->default_role; - else - port->try_role = TYPEC_NO_PREFERRED_ROLE; - - port->typec_caps.prefer_role = tcpc->config->default_role; - port->typec_caps.type = tcpc->config->type; - port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ - port->typec_caps.pd_revision = 0x0200; /* USB-PD spec release 2.0 */ - port->typec_caps.dr_set = tcpm_dr_set; - port->typec_caps.pr_set = tcpm_pr_set; - port->typec_caps.vconn_set = tcpm_vconn_set; - port->typec_caps.try_role = tcpm_try_role; - port->typec_caps.port_type_set = tcpm_port_type_set; - - port->partner_desc.identity = &port->partner_ident; - port->port_type = tcpc->config->type; - - port->typec_port = typec_register_port(port->dev, &port->typec_caps); - if (!port->typec_port) { - err = -ENOMEM; - goto out_destroy_wq; - } - - if (tcpc->config->alt_modes) { - const struct typec_altmode_desc *paltmode = tcpc->config->alt_modes; - - i = 0; - while (paltmode->svid && i < ARRAY_SIZE(port->port_altmode)) { - port->port_altmode[i] = - typec_port_register_altmode(port->typec_port, - paltmode); - if (!port->port_altmode[i]) { - tcpm_log(port, - "%s: failed to register port alternate mode 0x%x", - dev_name(dev), paltmode->svid); - break; - } - i++; - paltmode++; - } - } - - tcpm_debugfs_init(port); - mutex_lock(&port->lock); - tcpm_init(port); - mutex_unlock(&port->lock); - - tcpm_log(port, "%s: registered", dev_name(dev)); - return port; - -out_destroy_wq: - destroy_workqueue(port->wq); - return ERR_PTR(err); -} -EXPORT_SYMBOL_GPL(tcpm_register_port); - -void tcpm_unregister_port(struct tcpm_port *port) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++) - typec_unregister_altmode(port->port_altmode[i]); - typec_unregister_port(port->typec_port); - tcpm_debugfs_exit(port); - destroy_workqueue(port->wq); -} -EXPORT_SYMBOL_GPL(tcpm_unregister_port); - -MODULE_AUTHOR("Guenter Roeck "); -MODULE_DESCRIPTION("USB Type-C Port Manager"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/typec/tcpm.h b/drivers/staging/typec/tcpm.h deleted file mode 100644 index 073197f0d2bb..000000000000 --- a/drivers/staging/typec/tcpm.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright 2015-2017 Google, Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __LINUX_USB_TCPM_H -#define __LINUX_USB_TCPM_H - -#include -#include -#include "pd.h" - -enum typec_cc_status { - TYPEC_CC_OPEN, - TYPEC_CC_RA, - TYPEC_CC_RD, - TYPEC_CC_RP_DEF, - TYPEC_CC_RP_1_5, - TYPEC_CC_RP_3_0, -}; - -enum typec_cc_polarity { - TYPEC_POLARITY_CC1, - TYPEC_POLARITY_CC2, -}; - -/* Time to wait for TCPC to complete transmit */ -#define PD_T_TCPC_TX_TIMEOUT 100 /* in ms */ -#define PD_ROLE_SWAP_TIMEOUT (MSEC_PER_SEC * 10) - -enum tcpm_transmit_status { - TCPC_TX_SUCCESS = 0, - TCPC_TX_DISCARDED = 1, - TCPC_TX_FAILED = 2, -}; - -enum tcpm_transmit_type { - TCPC_TX_SOP = 0, - TCPC_TX_SOP_PRIME = 1, - TCPC_TX_SOP_PRIME_PRIME = 2, - TCPC_TX_SOP_DEBUG_PRIME = 3, - TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4, - TCPC_TX_HARD_RESET = 5, - TCPC_TX_CABLE_RESET = 6, - TCPC_TX_BIST_MODE_2 = 7 -}; - -/** - * struct tcpc_config - Port configuration - * @src_pdo: PDO parameters sent to port partner as response to - * PD_CTRL_GET_SOURCE_CAP message - * @nr_src_pdo: Number of entries in @src_pdo - * @snk_pdo: PDO parameters sent to partner as response to - * PD_CTRL_GET_SINK_CAP message - * @nr_snk_pdo: Number of entries in @snk_pdo - * @max_snk_mv: Maximum acceptable sink voltage in mV - * @max_snk_ma: Maximum sink current in mA - * @max_snk_mw: Maximum required sink power in mW - * @operating_snk_mw: - * Required operating sink power in mW - * @type: Port type (TYPEC_PORT_DFP, TYPEC_PORT_UFP, or - * TYPEC_PORT_DRP) - * @default_role: - * Default port role (TYPEC_SINK or TYPEC_SOURCE). - * Set to TYPEC_NO_PREFERRED_ROLE if no default role. - * @try_role_hw:True if try.{Src,Snk} is implemented in hardware - * @alt_modes: List of supported alternate modes - */ -struct tcpc_config { - const u32 *src_pdo; - unsigned int nr_src_pdo; - - const u32 *snk_pdo; - unsigned int nr_snk_pdo; - - const u32 *snk_vdo; - unsigned int nr_snk_vdo; - - unsigned int max_snk_mv; - unsigned int max_snk_ma; - unsigned int max_snk_mw; - unsigned int operating_snk_mw; - - enum typec_port_type type; - enum typec_role default_role; - bool try_role_hw; /* try.{src,snk} implemented in hardware */ - - const struct typec_altmode_desc *alt_modes; -}; - -enum tcpc_usb_switch { - TCPC_USB_SWITCH_CONNECT, - TCPC_USB_SWITCH_DISCONNECT, -}; - -/* Mux state attributes */ -#define TCPC_MUX_USB_ENABLED BIT(0) /* USB enabled */ -#define TCPC_MUX_DP_ENABLED BIT(1) /* DP enabled */ -#define TCPC_MUX_POLARITY_INVERTED BIT(2) /* Polarity inverted */ - -/* Mux modes, decoded to attributes */ -enum tcpc_mux_mode { - TYPEC_MUX_NONE = 0, /* Open switch */ - TYPEC_MUX_USB = TCPC_MUX_USB_ENABLED, /* USB only */ - TYPEC_MUX_DP = TCPC_MUX_DP_ENABLED, /* DP only */ - TYPEC_MUX_DOCK = TCPC_MUX_USB_ENABLED | /* Both USB and DP */ - TCPC_MUX_DP_ENABLED, -}; - -struct tcpc_mux_dev { - int (*set)(struct tcpc_mux_dev *dev, enum tcpc_mux_mode mux_mode, - enum tcpc_usb_switch usb_config, - enum typec_cc_polarity polarity); - bool dfp_only; - void *priv_data; -}; - -/** - * struct tcpc_dev - Port configuration and callback functions - * @config: Pointer to port configuration - * @get_vbus: Called to read current VBUS state - * @get_current_limit: - * Optional; called by the tcpm core when configured as a snk - * and cc=Rp-def. This allows the tcpm to provide a fallback - * current-limit detection method for the cc=Rp-def case. - * For example, some tcpcs may include BC1.2 charger detection - * and use that in this case. - * @set_cc: Called to set value of CC pins - * @get_cc: Called to read current CC pin values - * @set_polarity: - * Called to set polarity - * @set_vconn: Called to enable or disable VCONN - * @set_vbus: Called to enable or disable VBUS - * @set_current_limit: - * Optional; called to set current limit as negotiated - * with partner. - * @set_pd_rx: Called to enable or disable reception of PD messages - * @set_roles: Called to set power and data roles - * @start_drp_toggling: - * Optional; if supported by hardware, called to start DRP - * toggling. DRP toggling is stopped automatically if - * a connection is established. - * @try_role: Optional; called to set a preferred role - * @pd_transmit:Called to transmit PD message - * @mux: Pointer to multiplexer data - */ -struct tcpc_dev { - const struct tcpc_config *config; - - int (*init)(struct tcpc_dev *dev); - int (*get_vbus)(struct tcpc_dev *dev); - int (*get_current_limit)(struct tcpc_dev *dev); - int (*set_cc)(struct tcpc_dev *dev, enum typec_cc_status cc); - int (*get_cc)(struct tcpc_dev *dev, enum typec_cc_status *cc1, - enum typec_cc_status *cc2); - int (*set_polarity)(struct tcpc_dev *dev, - enum typec_cc_polarity polarity); - int (*set_vconn)(struct tcpc_dev *dev, bool on); - int (*set_vbus)(struct tcpc_dev *dev, bool on, bool charge); - int (*set_current_limit)(struct tcpc_dev *dev, u32 max_ma, u32 mv); - int (*set_pd_rx)(struct tcpc_dev *dev, bool on); - int (*set_roles)(struct tcpc_dev *dev, bool attached, - enum typec_role role, enum typec_data_role data); - int (*start_drp_toggling)(struct tcpc_dev *dev, - enum typec_cc_status cc); - int (*try_role)(struct tcpc_dev *dev, int role); - int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type, - const struct pd_message *msg); - struct tcpc_mux_dev *mux; -}; - -struct tcpm_port; - -struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc); -void tcpm_unregister_port(struct tcpm_port *port); - -void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo); -void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, - unsigned int nr_pdo, - unsigned int max_snk_mv, - unsigned int max_snk_ma, - unsigned int max_snk_mw, - unsigned int operating_snk_mw); - -void tcpm_vbus_change(struct tcpm_port *port); -void tcpm_cc_change(struct tcpm_port *port); -void tcpm_pd_receive(struct tcpm_port *port, - const struct pd_message *msg); -void tcpm_pd_transmit_complete(struct tcpm_port *port, - enum tcpm_transmit_status status); -void tcpm_pd_hard_reset(struct tcpm_port *port); -void tcpm_tcpc_reset(struct tcpm_port *port); - -#endif /* __LINUX_USB_TCPM_H */ diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index bc1b7745f1d4..888605860091 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -4,6 +4,14 @@ menu "USB Power Delivery and Type-C drivers" config TYPEC tristate +config TYPEC_TCPM + tristate "USB Type-C Port Controller Manager" + depends on USB + select TYPEC + help + The Type-C Port Controller Manager provides a USB PD and USB Type-C + state machine for use with Type-C Port Controllers. + config TYPEC_WCOVE tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver" depends on ACPI diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index bc214f15f1b5..eb883984724b 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_TYPEC) += typec.o +obj-$(CONFIG_TYPEC_TCPM) += tcpm.o obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o obj-$(CONFIG_TYPEC_UCSI) += ucsi/ diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm.c new file mode 100644 index 000000000000..f557c479fdc2 --- /dev/null +++ b/drivers/usb/typec/tcpm.c @@ -0,0 +1,3615 @@ +/* + * Copyright 2015-2017 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * USB Power Delivery protocol stack. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FOREACH_STATE(S) \ + S(INVALID_STATE), \ + S(DRP_TOGGLING), \ + S(SRC_UNATTACHED), \ + S(SRC_ATTACH_WAIT), \ + S(SRC_ATTACHED), \ + S(SRC_STARTUP), \ + S(SRC_SEND_CAPABILITIES), \ + S(SRC_NEGOTIATE_CAPABILITIES), \ + S(SRC_TRANSITION_SUPPLY), \ + S(SRC_READY), \ + S(SRC_WAIT_NEW_CAPABILITIES), \ + \ + S(SNK_UNATTACHED), \ + S(SNK_ATTACH_WAIT), \ + S(SNK_DEBOUNCED), \ + S(SNK_ATTACHED), \ + S(SNK_STARTUP), \ + S(SNK_DISCOVERY), \ + S(SNK_DISCOVERY_DEBOUNCE), \ + S(SNK_DISCOVERY_DEBOUNCE_DONE), \ + S(SNK_WAIT_CAPABILITIES), \ + S(SNK_NEGOTIATE_CAPABILITIES), \ + S(SNK_TRANSITION_SINK), \ + S(SNK_TRANSITION_SINK_VBUS), \ + S(SNK_READY), \ + \ + S(ACC_UNATTACHED), \ + S(DEBUG_ACC_ATTACHED), \ + S(AUDIO_ACC_ATTACHED), \ + S(AUDIO_ACC_DEBOUNCE), \ + \ + S(HARD_RESET_SEND), \ + S(HARD_RESET_START), \ + S(SRC_HARD_RESET_VBUS_OFF), \ + S(SRC_HARD_RESET_VBUS_ON), \ + S(SNK_HARD_RESET_SINK_OFF), \ + S(SNK_HARD_RESET_WAIT_VBUS), \ + S(SNK_HARD_RESET_SINK_ON), \ + \ + S(SOFT_RESET), \ + S(SOFT_RESET_SEND), \ + \ + S(DR_SWAP_ACCEPT), \ + S(DR_SWAP_SEND), \ + S(DR_SWAP_SEND_TIMEOUT), \ + S(DR_SWAP_CANCEL), \ + S(DR_SWAP_CHANGE_DR), \ + \ + S(PR_SWAP_ACCEPT), \ + S(PR_SWAP_SEND), \ + S(PR_SWAP_SEND_TIMEOUT), \ + S(PR_SWAP_CANCEL), \ + S(PR_SWAP_START), \ + S(PR_SWAP_SRC_SNK_TRANSITION_OFF), \ + S(PR_SWAP_SRC_SNK_SOURCE_OFF), \ + S(PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED), \ + S(PR_SWAP_SRC_SNK_SINK_ON), \ + S(PR_SWAP_SNK_SRC_SINK_OFF), \ + S(PR_SWAP_SNK_SRC_SOURCE_ON), \ + S(PR_SWAP_SNK_SRC_SOURCE_ON_VBUS_RAMPED_UP), \ + \ + S(VCONN_SWAP_ACCEPT), \ + S(VCONN_SWAP_SEND), \ + S(VCONN_SWAP_SEND_TIMEOUT), \ + S(VCONN_SWAP_CANCEL), \ + S(VCONN_SWAP_START), \ + S(VCONN_SWAP_WAIT_FOR_VCONN), \ + S(VCONN_SWAP_TURN_ON_VCONN), \ + S(VCONN_SWAP_TURN_OFF_VCONN), \ + \ + S(SNK_TRY), \ + S(SNK_TRY_WAIT), \ + S(SNK_TRY_WAIT_DEBOUNCE), \ + S(SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS), \ + S(SRC_TRYWAIT), \ + S(SRC_TRYWAIT_DEBOUNCE), \ + S(SRC_TRYWAIT_UNATTACHED), \ + \ + S(SRC_TRY), \ + S(SRC_TRY_WAIT), \ + S(SRC_TRY_DEBOUNCE), \ + S(SNK_TRYWAIT), \ + S(SNK_TRYWAIT_DEBOUNCE), \ + S(SNK_TRYWAIT_VBUS), \ + S(BIST_RX), \ + \ + S(ERROR_RECOVERY), \ + S(PORT_RESET), \ + S(PORT_RESET_WAIT_OFF) + +#define GENERATE_ENUM(e) e +#define GENERATE_STRING(s) #s + +enum tcpm_state { + FOREACH_STATE(GENERATE_ENUM) +}; + +static const char * const tcpm_states[] = { + FOREACH_STATE(GENERATE_STRING) +}; + +enum vdm_states { + VDM_STATE_ERR_BUSY = -3, + VDM_STATE_ERR_SEND = -2, + VDM_STATE_ERR_TMOUT = -1, + VDM_STATE_DONE = 0, + /* Anything >0 represents an active state */ + VDM_STATE_READY = 1, + VDM_STATE_BUSY = 2, + VDM_STATE_WAIT_RSP_BUSY = 3, +}; + +enum pd_msg_request { + PD_MSG_NONE = 0, + PD_MSG_CTRL_REJECT, + PD_MSG_CTRL_WAIT, + PD_MSG_DATA_SINK_CAP, + PD_MSG_DATA_SOURCE_CAP, +}; + +/* Events from low level driver */ + +#define TCPM_CC_EVENT BIT(0) +#define TCPM_VBUS_EVENT BIT(1) +#define TCPM_RESET_EVENT BIT(2) + +#define LOG_BUFFER_ENTRIES 1024 +#define LOG_BUFFER_ENTRY_SIZE 128 + +/* Alternate mode support */ + +#define SVID_DISCOVERY_MAX 16 + +struct pd_mode_data { + int svid_index; /* current SVID index */ + int nsvids; + u16 svids[SVID_DISCOVERY_MAX]; + int altmodes; /* number of alternate modes */ + struct typec_altmode_desc altmode_desc[SVID_DISCOVERY_MAX]; +}; + +struct tcpm_port { + struct device *dev; + + struct mutex lock; /* tcpm state machine lock */ + struct workqueue_struct *wq; + + struct typec_capability typec_caps; + struct typec_port *typec_port; + + struct tcpc_dev *tcpc; + + enum typec_role vconn_role; + enum typec_role pwr_role; + enum typec_data_role data_role; + enum typec_pwr_opmode pwr_opmode; + + struct usb_pd_identity partner_ident; + struct typec_partner_desc partner_desc; + struct typec_partner *partner; + + enum typec_cc_status cc_req; + + enum typec_cc_status cc1; + enum typec_cc_status cc2; + enum typec_cc_polarity polarity; + + bool attached; + bool connected; + enum typec_port_type port_type; + bool vbus_present; + bool vbus_never_low; + bool vbus_source; + bool vbus_charge; + + bool send_discover; + bool op_vsafe5v; + + int try_role; + int try_snk_count; + int try_src_count; + + enum pd_msg_request queued_message; + + enum tcpm_state enter_state; + enum tcpm_state prev_state; + enum tcpm_state state; + enum tcpm_state delayed_state; + unsigned long delayed_runtime; + unsigned long delay_ms; + + spinlock_t pd_event_lock; + u32 pd_events; + + struct work_struct event_work; + struct delayed_work state_machine; + struct delayed_work vdm_state_machine; + bool state_machine_running; + + struct completion tx_complete; + enum tcpm_transmit_status tx_status; + + struct mutex swap_lock; /* swap command lock */ + bool swap_pending; + bool non_pd_role_swap; + struct completion swap_complete; + int swap_status; + + unsigned int message_id; + unsigned int caps_count; + unsigned int hard_reset_count; + bool pd_capable; + bool explicit_contract; + unsigned int rx_msgid; + + /* Partner capabilities/requests */ + u32 sink_request; + u32 source_caps[PDO_MAX_OBJECTS]; + unsigned int nr_source_caps; + u32 sink_caps[PDO_MAX_OBJECTS]; + unsigned int nr_sink_caps; + + /* Local capabilities */ + u32 src_pdo[PDO_MAX_OBJECTS]; + unsigned int nr_src_pdo; + u32 snk_pdo[PDO_MAX_OBJECTS]; + unsigned int nr_snk_pdo; + u32 snk_vdo[VDO_MAX_OBJECTS]; + unsigned int nr_snk_vdo; + + unsigned int max_snk_mv; + unsigned int max_snk_ma; + unsigned int max_snk_mw; + unsigned int operating_snk_mw; + + /* Requested current / voltage */ + u32 current_limit; + u32 supply_voltage; + + u32 bist_request; + + /* PD state for Vendor Defined Messages */ + enum vdm_states vdm_state; + u32 vdm_retries; + /* next Vendor Defined Message to send */ + u32 vdo_data[VDO_MAX_SIZE]; + u8 vdo_count; + /* VDO to retry if UFP responder replied busy */ + u32 vdo_retry; + + /* Alternate mode data */ + + struct pd_mode_data mode_data; + struct typec_altmode *partner_altmode[SVID_DISCOVERY_MAX]; + struct typec_altmode *port_altmode[SVID_DISCOVERY_MAX]; + + /* Deadline in jiffies to exit src_try_wait state */ + unsigned long max_wait; + +#ifdef CONFIG_DEBUG_FS + struct dentry *dentry; + struct mutex logbuffer_lock; /* log buffer access lock */ + int logbuffer_head; + int logbuffer_tail; + u8 *logbuffer[LOG_BUFFER_ENTRIES]; +#endif +}; + +struct pd_rx_event { + struct work_struct work; + struct tcpm_port *port; + struct pd_message msg; +}; + +#define tcpm_cc_is_sink(cc) \ + ((cc) == TYPEC_CC_RP_DEF || (cc) == TYPEC_CC_RP_1_5 || \ + (cc) == TYPEC_CC_RP_3_0) + +#define tcpm_port_is_sink(port) \ + ((tcpm_cc_is_sink((port)->cc1) && !tcpm_cc_is_sink((port)->cc2)) || \ + (tcpm_cc_is_sink((port)->cc2) && !tcpm_cc_is_sink((port)->cc1))) + +#define tcpm_cc_is_source(cc) ((cc) == TYPEC_CC_RD) +#define tcpm_cc_is_audio(cc) ((cc) == TYPEC_CC_RA) +#define tcpm_cc_is_open(cc) ((cc) == TYPEC_CC_OPEN) + +#define tcpm_port_is_source(port) \ + ((tcpm_cc_is_source((port)->cc1) && \ + !tcpm_cc_is_source((port)->cc2)) || \ + (tcpm_cc_is_source((port)->cc2) && \ + !tcpm_cc_is_source((port)->cc1))) + +#define tcpm_port_is_debug(port) \ + (tcpm_cc_is_source((port)->cc1) && tcpm_cc_is_source((port)->cc2)) + +#define tcpm_port_is_audio(port) \ + (tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_audio((port)->cc2)) + +#define tcpm_port_is_audio_detached(port) \ + ((tcpm_cc_is_audio((port)->cc1) && tcpm_cc_is_open((port)->cc2)) || \ + (tcpm_cc_is_audio((port)->cc2) && tcpm_cc_is_open((port)->cc1))) + +#define tcpm_try_snk(port) \ + ((port)->try_snk_count == 0 && (port)->try_role == TYPEC_SINK && \ + (port)->port_type == TYPEC_PORT_DRP) + +#define tcpm_try_src(port) \ + ((port)->try_src_count == 0 && (port)->try_role == TYPEC_SOURCE && \ + (port)->port_type == TYPEC_PORT_DRP) + +static enum tcpm_state tcpm_default_state(struct tcpm_port *port) +{ + if (port->port_type == TYPEC_PORT_DRP) { + if (port->try_role == TYPEC_SINK) + return SNK_UNATTACHED; + else if (port->try_role == TYPEC_SOURCE) + return SRC_UNATTACHED; + else if (port->tcpc->config->default_role == TYPEC_SINK) + return SNK_UNATTACHED; + /* Fall through to return SRC_UNATTACHED */ + } else if (port->port_type == TYPEC_PORT_UFP) { + return SNK_UNATTACHED; + } + return SRC_UNATTACHED; +} + +static inline +struct tcpm_port *typec_cap_to_tcpm(const struct typec_capability *cap) +{ + return container_of(cap, struct tcpm_port, typec_caps); +} + +static bool tcpm_port_is_disconnected(struct tcpm_port *port) +{ + return (!port->attached && port->cc1 == TYPEC_CC_OPEN && + port->cc2 == TYPEC_CC_OPEN) || + (port->attached && ((port->polarity == TYPEC_POLARITY_CC1 && + port->cc1 == TYPEC_CC_OPEN) || + (port->polarity == TYPEC_POLARITY_CC2 && + port->cc2 == TYPEC_CC_OPEN))); +} + +/* + * Logging + */ + +#ifdef CONFIG_DEBUG_FS + +static bool tcpm_log_full(struct tcpm_port *port) +{ + return port->logbuffer_tail == + (port->logbuffer_head + 1) % LOG_BUFFER_ENTRIES; +} + +__printf(2, 0) +static void _tcpm_log(struct tcpm_port *port, const char *fmt, va_list args) +{ + char tmpbuffer[LOG_BUFFER_ENTRY_SIZE]; + u64 ts_nsec = local_clock(); + unsigned long rem_nsec; + + if (!port->logbuffer[port->logbuffer_head]) { + port->logbuffer[port->logbuffer_head] = + kzalloc(LOG_BUFFER_ENTRY_SIZE, GFP_KERNEL); + if (!port->logbuffer[port->logbuffer_head]) + return; + } + + vsnprintf(tmpbuffer, sizeof(tmpbuffer), fmt, args); + + mutex_lock(&port->logbuffer_lock); + + if (tcpm_log_full(port)) { + port->logbuffer_head = max(port->logbuffer_head - 1, 0); + strcpy(tmpbuffer, "overflow"); + } + + if (port->logbuffer_head < 0 || + port->logbuffer_head >= LOG_BUFFER_ENTRIES) { + dev_warn(port->dev, + "Bad log buffer index %d\n", port->logbuffer_head); + goto abort; + } + + if (!port->logbuffer[port->logbuffer_head]) { + dev_warn(port->dev, + "Log buffer index %d is NULL\n", port->logbuffer_head); + goto abort; + } + + rem_nsec = do_div(ts_nsec, 1000000000); + scnprintf(port->logbuffer[port->logbuffer_head], + LOG_BUFFER_ENTRY_SIZE, "[%5lu.%06lu] %s", + (unsigned long)ts_nsec, rem_nsec / 1000, + tmpbuffer); + port->logbuffer_head = (port->logbuffer_head + 1) % LOG_BUFFER_ENTRIES; + +abort: + mutex_unlock(&port->logbuffer_lock); +} + +__printf(2, 3) +static void tcpm_log(struct tcpm_port *port, const char *fmt, ...) +{ + va_list args; + + /* Do not log while disconnected and unattached */ + if (tcpm_port_is_disconnected(port) && + (port->state == SRC_UNATTACHED || port->state == SNK_UNATTACHED || + port->state == DRP_TOGGLING)) + return; + + va_start(args, fmt); + _tcpm_log(port, fmt, args); + va_end(args); +} + +__printf(2, 3) +static void tcpm_log_force(struct tcpm_port *port, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + _tcpm_log(port, fmt, args); + va_end(args); +} + +static void tcpm_log_source_caps(struct tcpm_port *port) +{ + int i; + + for (i = 0; i < port->nr_source_caps; i++) { + u32 pdo = port->source_caps[i]; + enum pd_pdo_type type = pdo_type(pdo); + char msg[64]; + + switch (type) { + case PDO_TYPE_FIXED: + scnprintf(msg, sizeof(msg), + "%u mV, %u mA [%s%s%s%s%s%s]", + pdo_fixed_voltage(pdo), + pdo_max_current(pdo), + (pdo & PDO_FIXED_DUAL_ROLE) ? + "R" : "", + (pdo & PDO_FIXED_SUSPEND) ? + "S" : "", + (pdo & PDO_FIXED_HIGHER_CAP) ? + "H" : "", + (pdo & PDO_FIXED_USB_COMM) ? + "U" : "", + (pdo & PDO_FIXED_DATA_SWAP) ? + "D" : "", + (pdo & PDO_FIXED_EXTPOWER) ? + "E" : ""); + break; + case PDO_TYPE_VAR: + scnprintf(msg, sizeof(msg), + "%u-%u mV, %u mA", + pdo_min_voltage(pdo), + pdo_max_voltage(pdo), + pdo_max_current(pdo)); + break; + case PDO_TYPE_BATT: + scnprintf(msg, sizeof(msg), + "%u-%u mV, %u mW", + pdo_min_voltage(pdo), + pdo_max_voltage(pdo), + pdo_max_power(pdo)); + break; + default: + strcpy(msg, "undefined"); + break; + } + tcpm_log(port, " PDO %d: type %d, %s", + i, type, msg); + } +} + +static int tcpm_seq_show(struct seq_file *s, void *v) +{ + struct tcpm_port *port = (struct tcpm_port *)s->private; + int tail; + + mutex_lock(&port->logbuffer_lock); + tail = port->logbuffer_tail; + while (tail != port->logbuffer_head) { + seq_printf(s, "%s\n", port->logbuffer[tail]); + tail = (tail + 1) % LOG_BUFFER_ENTRIES; + } + if (!seq_has_overflowed(s)) + port->logbuffer_tail = tail; + mutex_unlock(&port->logbuffer_lock); + + return 0; +} + +static int tcpm_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, tcpm_seq_show, inode->i_private); +} + +static const struct file_operations tcpm_debug_operations = { + .open = tcpm_debug_open, + .llseek = seq_lseek, + .read = seq_read, + .release = single_release, +}; + +static struct dentry *rootdir; + +static int tcpm_debugfs_init(struct tcpm_port *port) +{ + mutex_init(&port->logbuffer_lock); + /* /sys/kernel/debug/tcpm/usbcX */ + if (!rootdir) { + rootdir = debugfs_create_dir("tcpm", NULL); + if (!rootdir) + return -ENOMEM; + } + + port->dentry = debugfs_create_file(dev_name(port->dev), + S_IFREG | 0444, rootdir, + port, &tcpm_debug_operations); + + return 0; +} + +static void tcpm_debugfs_exit(struct tcpm_port *port) +{ + debugfs_remove(port->dentry); +} + +#else + +__printf(2, 3) +static void tcpm_log(const struct tcpm_port *port, const char *fmt, ...) { } +__printf(2, 3) +static void tcpm_log_force(struct tcpm_port *port, const char *fmt, ...) { } +static void tcpm_log_source_caps(struct tcpm_port *port) { } +static int tcpm_debugfs_init(const struct tcpm_port *port) { return 0; } +static void tcpm_debugfs_exit(const struct tcpm_port *port) { } + +#endif + +static int tcpm_pd_transmit(struct tcpm_port *port, + enum tcpm_transmit_type type, + const struct pd_message *msg) +{ + unsigned long timeout; + int ret; + + if (msg) + tcpm_log(port, "PD TX, header: %#x", le16_to_cpu(msg->header)); + else + tcpm_log(port, "PD TX, type: %#x", type); + + reinit_completion(&port->tx_complete); + ret = port->tcpc->pd_transmit(port->tcpc, type, msg); + if (ret < 0) + return ret; + + mutex_unlock(&port->lock); + timeout = wait_for_completion_timeout(&port->tx_complete, + msecs_to_jiffies(PD_T_TCPC_TX_TIMEOUT)); + mutex_lock(&port->lock); + if (!timeout) + return -ETIMEDOUT; + + switch (port->tx_status) { + case TCPC_TX_SUCCESS: + port->message_id = (port->message_id + 1) & PD_HEADER_ID_MASK; + return 0; + case TCPC_TX_DISCARDED: + return -EAGAIN; + case TCPC_TX_FAILED: + default: + return -EIO; + } +} + +void tcpm_pd_transmit_complete(struct tcpm_port *port, + enum tcpm_transmit_status status) +{ + tcpm_log(port, "PD TX complete, status: %u", status); + port->tx_status = status; + complete(&port->tx_complete); +} +EXPORT_SYMBOL_GPL(tcpm_pd_transmit_complete); + +static int tcpm_mux_set(struct tcpm_port *port, enum tcpc_mux_mode mode, + enum tcpc_usb_switch config) +{ + int ret = 0; + + tcpm_log(port, "Requesting mux mode %d, config %d, polarity %d", + mode, config, port->polarity); + + if (port->tcpc->mux) + ret = port->tcpc->mux->set(port->tcpc->mux, mode, config, + port->polarity); + + return ret; +} + +static int tcpm_set_polarity(struct tcpm_port *port, + enum typec_cc_polarity polarity) +{ + int ret; + + tcpm_log(port, "polarity %d", polarity); + + ret = port->tcpc->set_polarity(port->tcpc, polarity); + if (ret < 0) + return ret; + + port->polarity = polarity; + + return 0; +} + +static int tcpm_set_vconn(struct tcpm_port *port, bool enable) +{ + int ret; + + tcpm_log(port, "vconn:=%d", enable); + + ret = port->tcpc->set_vconn(port->tcpc, enable); + if (!ret) { + port->vconn_role = enable ? TYPEC_SOURCE : TYPEC_SINK; + typec_set_vconn_role(port->typec_port, port->vconn_role); + } + + return ret; +} + +static u32 tcpm_get_current_limit(struct tcpm_port *port) +{ + enum typec_cc_status cc; + u32 limit; + + cc = port->polarity ? port->cc2 : port->cc1; + switch (cc) { + case TYPEC_CC_RP_1_5: + limit = 1500; + break; + case TYPEC_CC_RP_3_0: + limit = 3000; + break; + case TYPEC_CC_RP_DEF: + default: + if (port->tcpc->get_current_limit) + limit = port->tcpc->get_current_limit(port->tcpc); + else + limit = 0; + break; + } + + return limit; +} + +static int tcpm_set_current_limit(struct tcpm_port *port, u32 max_ma, u32 mv) +{ + int ret = -EOPNOTSUPP; + + tcpm_log(port, "Setting voltage/current limit %u mV %u mA", mv, max_ma); + + if (port->tcpc->set_current_limit) + ret = port->tcpc->set_current_limit(port->tcpc, max_ma, mv); + + return ret; +} + +/* + * Determine RP value to set based on maximum current supported + * by a port if configured as source. + * Returns CC value to report to link partner. + */ +static enum typec_cc_status tcpm_rp_cc(struct tcpm_port *port) +{ + const u32 *src_pdo = port->src_pdo; + int nr_pdo = port->nr_src_pdo; + int i; + + /* + * Search for first entry with matching voltage. + * It should report the maximum supported current. + */ + for (i = 0; i < nr_pdo; i++) { + const u32 pdo = src_pdo[i]; + + if (pdo_type(pdo) == PDO_TYPE_FIXED && + pdo_fixed_voltage(pdo) == 5000) { + unsigned int curr = pdo_max_current(pdo); + + if (curr >= 3000) + return TYPEC_CC_RP_3_0; + else if (curr >= 1500) + return TYPEC_CC_RP_1_5; + return TYPEC_CC_RP_DEF; + } + } + + return TYPEC_CC_RP_DEF; +} + +static int tcpm_set_attached_state(struct tcpm_port *port, bool attached) +{ + return port->tcpc->set_roles(port->tcpc, attached, port->pwr_role, + port->data_role); +} + +static int tcpm_set_roles(struct tcpm_port *port, bool attached, + enum typec_role role, enum typec_data_role data) +{ + int ret; + + if (data == TYPEC_HOST) + ret = tcpm_mux_set(port, TYPEC_MUX_USB, + TCPC_USB_SWITCH_CONNECT); + else + ret = tcpm_mux_set(port, TYPEC_MUX_NONE, + TCPC_USB_SWITCH_DISCONNECT); + if (ret < 0) + return ret; + + ret = port->tcpc->set_roles(port->tcpc, attached, role, data); + if (ret < 0) + return ret; + + port->pwr_role = role; + port->data_role = data; + typec_set_data_role(port->typec_port, data); + typec_set_pwr_role(port->typec_port, role); + + return 0; +} + +static int tcpm_set_pwr_role(struct tcpm_port *port, enum typec_role role) +{ + int ret; + + ret = port->tcpc->set_roles(port->tcpc, true, role, + port->data_role); + if (ret < 0) + return ret; + + port->pwr_role = role; + typec_set_pwr_role(port->typec_port, role); + + return 0; +} + +static int tcpm_pd_send_source_caps(struct tcpm_port *port) +{ + struct pd_message msg; + int i; + + memset(&msg, 0, sizeof(msg)); + if (!port->nr_src_pdo) { + /* No source capabilities defined, sink only */ + msg.header = PD_HEADER_LE(PD_CTRL_REJECT, + port->pwr_role, + port->data_role, + port->message_id, 0); + } else { + msg.header = PD_HEADER_LE(PD_DATA_SOURCE_CAP, + port->pwr_role, + port->data_role, + port->message_id, + port->nr_src_pdo); + } + for (i = 0; i < port->nr_src_pdo; i++) + msg.payload[i] = cpu_to_le32(port->src_pdo[i]); + + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + +static int tcpm_pd_send_sink_caps(struct tcpm_port *port) +{ + struct pd_message msg; + int i; + + memset(&msg, 0, sizeof(msg)); + if (!port->nr_snk_pdo) { + /* No sink capabilities defined, source only */ + msg.header = PD_HEADER_LE(PD_CTRL_REJECT, + port->pwr_role, + port->data_role, + port->message_id, 0); + } else { + msg.header = PD_HEADER_LE(PD_DATA_SINK_CAP, + port->pwr_role, + port->data_role, + port->message_id, + port->nr_snk_pdo); + } + for (i = 0; i < port->nr_snk_pdo; i++) + msg.payload[i] = cpu_to_le32(port->snk_pdo[i]); + + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + +static void tcpm_set_state(struct tcpm_port *port, enum tcpm_state state, + unsigned int delay_ms) +{ + if (delay_ms) { + tcpm_log(port, "pending state change %s -> %s @ %u ms", + tcpm_states[port->state], tcpm_states[state], + delay_ms); + port->delayed_state = state; + mod_delayed_work(port->wq, &port->state_machine, + msecs_to_jiffies(delay_ms)); + port->delayed_runtime = jiffies + msecs_to_jiffies(delay_ms); + port->delay_ms = delay_ms; + } else { + tcpm_log(port, "state change %s -> %s", + tcpm_states[port->state], tcpm_states[state]); + port->delayed_state = INVALID_STATE; + port->prev_state = port->state; + port->state = state; + /* + * Don't re-queue the state machine work item if we're currently + * in the state machine and we're immediately changing states. + * tcpm_state_machine_work() will continue running the state + * machine. + */ + if (!port->state_machine_running) + mod_delayed_work(port->wq, &port->state_machine, 0); + } +} + +static void tcpm_set_state_cond(struct tcpm_port *port, enum tcpm_state state, + unsigned int delay_ms) +{ + if (port->enter_state == port->state) + tcpm_set_state(port, state, delay_ms); + else + tcpm_log(port, + "skipped %sstate change %s -> %s [%u ms], context state %s", + delay_ms ? "delayed " : "", + tcpm_states[port->state], tcpm_states[state], + delay_ms, tcpm_states[port->enter_state]); +} + +static void tcpm_queue_message(struct tcpm_port *port, + enum pd_msg_request message) +{ + port->queued_message = message; + mod_delayed_work(port->wq, &port->state_machine, 0); +} + +/* + * VDM/VDO handling functions + */ +static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, + const u32 *data, int cnt) +{ + port->vdo_count = cnt + 1; + port->vdo_data[0] = header; + memcpy(&port->vdo_data[1], data, sizeof(u32) * cnt); + /* Set ready, vdm state machine will actually send */ + port->vdm_retries = 0; + port->vdm_state = VDM_STATE_READY; +} + +static void svdm_consume_identity(struct tcpm_port *port, const __le32 *payload, + int cnt) +{ + u32 vdo = le32_to_cpu(payload[VDO_INDEX_IDH]); + u32 product = le32_to_cpu(payload[VDO_INDEX_PRODUCT]); + + memset(&port->mode_data, 0, sizeof(port->mode_data)); + + port->partner_ident.id_header = vdo; + port->partner_ident.cert_stat = le32_to_cpu(payload[VDO_INDEX_CSTAT]); + port->partner_ident.product = product; + + typec_partner_set_identity(port->partner); + + tcpm_log(port, "Identity: %04x:%04x.%04x", + PD_IDH_VID(vdo), + PD_PRODUCT_PID(product), product & 0xffff); +} + +static bool svdm_consume_svids(struct tcpm_port *port, const __le32 *payload, + int cnt) +{ + struct pd_mode_data *pmdata = &port->mode_data; + int i; + + for (i = 1; i < cnt; i++) { + u32 p = le32_to_cpu(payload[i]); + u16 svid; + + svid = (p >> 16) & 0xffff; + if (!svid) + return false; + + if (pmdata->nsvids >= SVID_DISCOVERY_MAX) + goto abort; + + pmdata->svids[pmdata->nsvids++] = svid; + tcpm_log(port, "SVID %d: 0x%x", pmdata->nsvids, svid); + + svid = p & 0xffff; + if (!svid) + return false; + + if (pmdata->nsvids >= SVID_DISCOVERY_MAX) + goto abort; + + pmdata->svids[pmdata->nsvids++] = svid; + tcpm_log(port, "SVID %d: 0x%x", pmdata->nsvids, svid); + } + return true; +abort: + tcpm_log(port, "SVID_DISCOVERY_MAX(%d) too low!", SVID_DISCOVERY_MAX); + return false; +} + +static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload, + int cnt) +{ + struct pd_mode_data *pmdata = &port->mode_data; + struct typec_altmode_desc *paltmode; + struct typec_mode_desc *pmode; + int i; + + if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) { + /* Already logged in svdm_consume_svids() */ + return; + } + + paltmode = &pmdata->altmode_desc[pmdata->altmodes]; + memset(paltmode, 0, sizeof(*paltmode)); + + paltmode->svid = pmdata->svids[pmdata->svid_index]; + + tcpm_log(port, " Alternate mode %d: SVID 0x%04x", + pmdata->altmodes, paltmode->svid); + + for (i = 1; i < cnt && paltmode->n_modes < ALTMODE_MAX_MODES; i++) { + pmode = &paltmode->modes[paltmode->n_modes]; + memset(pmode, 0, sizeof(*pmode)); + pmode->vdo = le32_to_cpu(payload[i]); + pmode->index = i - 1; + paltmode->n_modes++; + tcpm_log(port, " VDO %d: 0x%08x", + pmode->index, pmode->vdo); + } + port->partner_altmode[pmdata->altmodes] = + typec_partner_register_altmode(port->partner, paltmode); + if (port->partner_altmode[pmdata->altmodes] == NULL) { + tcpm_log(port, + "Failed to register alternate modes for SVID 0x%04x", + paltmode->svid); + return; + } + pmdata->altmodes++; +} + +#define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header) + +static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt, + u32 *response) +{ + u32 p0 = le32_to_cpu(payload[0]); + int cmd_type = PD_VDO_CMDT(p0); + int cmd = PD_VDO_CMD(p0); + struct pd_mode_data *modep; + int rlen = 0; + u16 svid; + int i; + + tcpm_log(port, "Rx VDM cmd 0x%x type %d cmd %d len %d", + p0, cmd_type, cmd, cnt); + + modep = &port->mode_data; + + switch (cmd_type) { + case CMDT_INIT: + switch (cmd) { + case CMD_DISCOVER_IDENT: + /* 6.4.4.3.1: Only respond as UFP (device) */ + if (port->data_role == TYPEC_DEVICE && + port->nr_snk_vdo) { + for (i = 0; i < port->nr_snk_vdo; i++) + response[i + 1] = port->snk_vdo[i]; + rlen = port->nr_snk_vdo + 1; + } + break; + case CMD_DISCOVER_SVID: + break; + case CMD_DISCOVER_MODES: + break; + case CMD_ENTER_MODE: + break; + case CMD_EXIT_MODE: + break; + case CMD_ATTENTION: + break; + default: + break; + } + if (rlen >= 1) { + response[0] = p0 | VDO_CMDT(CMDT_RSP_ACK); + } else if (rlen == 0) { + response[0] = p0 | VDO_CMDT(CMDT_RSP_NAK); + rlen = 1; + } else { + response[0] = p0 | VDO_CMDT(CMDT_RSP_BUSY); + rlen = 1; + } + break; + case CMDT_RSP_ACK: + /* silently drop message if we are not connected */ + if (!port->partner) + break; + + switch (cmd) { + case CMD_DISCOVER_IDENT: + /* 6.4.4.3.1 */ + svdm_consume_identity(port, payload, cnt); + response[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID); + rlen = 1; + break; + case CMD_DISCOVER_SVID: + /* 6.4.4.3.2 */ + if (svdm_consume_svids(port, payload, cnt)) { + response[0] = VDO(USB_SID_PD, 1, + CMD_DISCOVER_SVID); + rlen = 1; + } else if (modep->nsvids && supports_modal(port)) { + response[0] = VDO(modep->svids[0], 1, + CMD_DISCOVER_MODES); + rlen = 1; + } + break; + case CMD_DISCOVER_MODES: + /* 6.4.4.3.3 */ + svdm_consume_modes(port, payload, cnt); + modep->svid_index++; + if (modep->svid_index < modep->nsvids) { + svid = modep->svids[modep->svid_index]; + response[0] = VDO(svid, 1, CMD_DISCOVER_MODES); + rlen = 1; + } else { + /* enter alternate mode if/when implemented */ + } + break; + case CMD_ENTER_MODE: + break; + default: + break; + } + break; + default: + break; + } + + return rlen; +} + +static void tcpm_handle_vdm_request(struct tcpm_port *port, + const __le32 *payload, int cnt) +{ + int rlen = 0; + u32 response[8] = { }; + u32 p0 = le32_to_cpu(payload[0]); + + if (port->vdm_state == VDM_STATE_BUSY) { + /* If UFP responded busy retry after timeout */ + if (PD_VDO_CMDT(p0) == CMDT_RSP_BUSY) { + port->vdm_state = VDM_STATE_WAIT_RSP_BUSY; + port->vdo_retry = (p0 & ~VDO_CMDT_MASK) | + CMDT_INIT; + mod_delayed_work(port->wq, &port->vdm_state_machine, + msecs_to_jiffies(PD_T_VDM_BUSY)); + return; + } + port->vdm_state = VDM_STATE_DONE; + } + + if (PD_VDO_SVDM(p0)) + rlen = tcpm_pd_svdm(port, payload, cnt, response); + + if (rlen > 0) { + tcpm_queue_vdm(port, response[0], &response[1], rlen - 1); + mod_delayed_work(port->wq, &port->vdm_state_machine, 0); + } +} + +static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd, + const u32 *data, int count) +{ + u32 header; + + if (WARN_ON(count > VDO_MAX_SIZE - 1)) + count = VDO_MAX_SIZE - 1; + + /* set VDM header with VID & CMD */ + header = VDO(vid, ((vid & USB_SID_PD) == USB_SID_PD) ? + 1 : (PD_VDO_CMD(cmd) <= CMD_ATTENTION), cmd); + tcpm_queue_vdm(port, header, data, count); + + mod_delayed_work(port->wq, &port->vdm_state_machine, 0); +} + +static unsigned int vdm_ready_timeout(u32 vdm_hdr) +{ + unsigned int timeout; + int cmd = PD_VDO_CMD(vdm_hdr); + + /* its not a structured VDM command */ + if (!PD_VDO_SVDM(vdm_hdr)) + return PD_T_VDM_UNSTRUCTURED; + + switch (PD_VDO_CMDT(vdm_hdr)) { + case CMDT_INIT: + if (cmd == CMD_ENTER_MODE || cmd == CMD_EXIT_MODE) + timeout = PD_T_VDM_WAIT_MODE_E; + else + timeout = PD_T_VDM_SNDR_RSP; + break; + default: + if (cmd == CMD_ENTER_MODE || cmd == CMD_EXIT_MODE) + timeout = PD_T_VDM_E_MODE; + else + timeout = PD_T_VDM_RCVR_RSP; + break; + } + return timeout; +} + +static void vdm_run_state_machine(struct tcpm_port *port) +{ + struct pd_message msg; + int i, res; + + switch (port->vdm_state) { + case VDM_STATE_READY: + /* Only transmit VDM if attached */ + if (!port->attached) { + port->vdm_state = VDM_STATE_ERR_BUSY; + break; + } + + /* + * if there's traffic or we're not in PDO ready state don't send + * a VDM. + */ + if (port->state != SRC_READY && port->state != SNK_READY) + break; + + /* Prepare and send VDM */ + memset(&msg, 0, sizeof(msg)); + msg.header = PD_HEADER_LE(PD_DATA_VENDOR_DEF, + port->pwr_role, + port->data_role, + port->message_id, port->vdo_count); + for (i = 0; i < port->vdo_count; i++) + msg.payload[i] = cpu_to_le32(port->vdo_data[i]); + res = tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); + if (res < 0) { + port->vdm_state = VDM_STATE_ERR_SEND; + } else { + unsigned long timeout; + + port->vdm_retries = 0; + port->vdm_state = VDM_STATE_BUSY; + timeout = vdm_ready_timeout(port->vdo_data[0]); + mod_delayed_work(port->wq, &port->vdm_state_machine, + timeout); + } + break; + case VDM_STATE_WAIT_RSP_BUSY: + port->vdo_data[0] = port->vdo_retry; + port->vdo_count = 1; + port->vdm_state = VDM_STATE_READY; + break; + case VDM_STATE_BUSY: + port->vdm_state = VDM_STATE_ERR_TMOUT; + break; + case VDM_STATE_ERR_SEND: + /* + * A partner which does not support USB PD will not reply, + * so this is not a fatal error. At the same time, some + * devices may not return GoodCRC under some circumstances, + * so we need to retry. + */ + if (port->vdm_retries < 3) { + tcpm_log(port, "VDM Tx error, retry"); + port->vdm_retries++; + port->vdm_state = VDM_STATE_READY; + } + break; + default: + break; + } +} + +static void vdm_state_machine_work(struct work_struct *work) +{ + struct tcpm_port *port = container_of(work, struct tcpm_port, + vdm_state_machine.work); + enum vdm_states prev_state; + + mutex_lock(&port->lock); + + /* + * Continue running as long as the port is not busy and there was + * a state change. + */ + do { + prev_state = port->vdm_state; + vdm_run_state_machine(port); + } while (port->vdm_state != prev_state && + port->vdm_state != VDM_STATE_BUSY); + + mutex_unlock(&port->lock); +} + +/* + * PD (data, control) command handling functions + */ +static void tcpm_pd_data_request(struct tcpm_port *port, + const struct pd_message *msg) +{ + enum pd_data_msg_type type = pd_header_type_le(msg->header); + unsigned int cnt = pd_header_cnt_le(msg->header); + unsigned int i; + + switch (type) { + case PD_DATA_SOURCE_CAP: + if (port->pwr_role != TYPEC_SINK) + break; + + for (i = 0; i < cnt; i++) + port->source_caps[i] = le32_to_cpu(msg->payload[i]); + + port->nr_source_caps = cnt; + + tcpm_log_source_caps(port); + + /* + * This message may be received even if VBUS is not + * present. This is quite unexpected; see USB PD + * specification, sections 8.3.3.6.3.1 and 8.3.3.6.3.2. + * However, at the same time, we must be ready to + * receive this message and respond to it 15ms after + * receiving PS_RDY during power swap operations, no matter + * if VBUS is available or not (USB PD specification, + * section 6.5.9.2). + * So we need to accept the message either way, + * but be prepared to keep waiting for VBUS after it was + * handled. + */ + tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); + break; + case PD_DATA_REQUEST: + if (port->pwr_role != TYPEC_SOURCE || + cnt != 1) { + tcpm_queue_message(port, PD_MSG_CTRL_REJECT); + break; + } + port->sink_request = le32_to_cpu(msg->payload[0]); + tcpm_set_state(port, SRC_NEGOTIATE_CAPABILITIES, 0); + break; + case PD_DATA_SINK_CAP: + /* We don't do anything with this at the moment... */ + for (i = 0; i < cnt; i++) + port->sink_caps[i] = le32_to_cpu(msg->payload[i]); + port->nr_sink_caps = cnt; + break; + case PD_DATA_VENDOR_DEF: + tcpm_handle_vdm_request(port, msg->payload, cnt); + break; + case PD_DATA_BIST: + if (port->state == SRC_READY || port->state == SNK_READY) { + port->bist_request = le32_to_cpu(msg->payload[0]); + tcpm_set_state(port, BIST_RX, 0); + } + break; + default: + tcpm_log(port, "Unhandled data message type %#x", type); + break; + } +} + +static void tcpm_pd_ctrl_request(struct tcpm_port *port, + const struct pd_message *msg) +{ + enum pd_ctrl_msg_type type = pd_header_type_le(msg->header); + enum tcpm_state next_state; + + switch (type) { + case PD_CTRL_GOOD_CRC: + case PD_CTRL_PING: + break; + case PD_CTRL_GET_SOURCE_CAP: + switch (port->state) { + case SRC_READY: + case SNK_READY: + tcpm_queue_message(port, PD_MSG_DATA_SOURCE_CAP); + break; + default: + tcpm_queue_message(port, PD_MSG_CTRL_REJECT); + break; + } + break; + case PD_CTRL_GET_SINK_CAP: + switch (port->state) { + case SRC_READY: + case SNK_READY: + tcpm_queue_message(port, PD_MSG_DATA_SINK_CAP); + break; + default: + tcpm_queue_message(port, PD_MSG_CTRL_REJECT); + break; + } + break; + case PD_CTRL_GOTO_MIN: + break; + case PD_CTRL_PS_RDY: + switch (port->state) { + case SNK_TRANSITION_SINK: + if (port->vbus_present) { + tcpm_set_current_limit(port, + port->current_limit, + port->supply_voltage); + port->explicit_contract = true; + tcpm_set_state(port, SNK_READY, 0); + } else { + /* + * Seen after power swap. Keep waiting for VBUS + * in a transitional state. + */ + tcpm_set_state(port, + SNK_TRANSITION_SINK_VBUS, 0); + } + break; + case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED: + tcpm_set_state(port, PR_SWAP_SRC_SNK_SINK_ON, 0); + break; + case PR_SWAP_SNK_SRC_SINK_OFF: + tcpm_set_state(port, PR_SWAP_SNK_SRC_SOURCE_ON, 0); + break; + case VCONN_SWAP_WAIT_FOR_VCONN: + tcpm_set_state(port, VCONN_SWAP_TURN_OFF_VCONN, 0); + break; + default: + break; + } + break; + case PD_CTRL_REJECT: + case PD_CTRL_WAIT: + switch (port->state) { + case SNK_NEGOTIATE_CAPABILITIES: + /* USB PD specification, Figure 8-43 */ + if (port->explicit_contract) + next_state = SNK_READY; + else + next_state = SNK_WAIT_CAPABILITIES; + tcpm_set_state(port, next_state, 0); + break; + case DR_SWAP_SEND: + port->swap_status = (type == PD_CTRL_WAIT ? + -EAGAIN : -EOPNOTSUPP); + tcpm_set_state(port, DR_SWAP_CANCEL, 0); + break; + case PR_SWAP_SEND: + port->swap_status = (type == PD_CTRL_WAIT ? + -EAGAIN : -EOPNOTSUPP); + tcpm_set_state(port, PR_SWAP_CANCEL, 0); + break; + case VCONN_SWAP_SEND: + port->swap_status = (type == PD_CTRL_WAIT ? + -EAGAIN : -EOPNOTSUPP); + tcpm_set_state(port, VCONN_SWAP_CANCEL, 0); + break; + default: + break; + } + break; + case PD_CTRL_ACCEPT: + switch (port->state) { + case SNK_NEGOTIATE_CAPABILITIES: + tcpm_set_state(port, SNK_TRANSITION_SINK, 0); + break; + case SOFT_RESET_SEND: + port->message_id = 0; + port->rx_msgid = -1; + if (port->pwr_role == TYPEC_SOURCE) + next_state = SRC_SEND_CAPABILITIES; + else + next_state = SNK_WAIT_CAPABILITIES; + tcpm_set_state(port, next_state, 0); + break; + case DR_SWAP_SEND: + tcpm_set_state(port, DR_SWAP_CHANGE_DR, 0); + break; + case PR_SWAP_SEND: + tcpm_set_state(port, PR_SWAP_START, 0); + break; + case VCONN_SWAP_SEND: + tcpm_set_state(port, VCONN_SWAP_START, 0); + break; + default: + break; + } + break; + case PD_CTRL_SOFT_RESET: + tcpm_set_state(port, SOFT_RESET, 0); + break; + case PD_CTRL_DR_SWAP: + if (port->port_type != TYPEC_PORT_DRP) { + tcpm_queue_message(port, PD_MSG_CTRL_REJECT); + break; + } + /* + * XXX + * 6.3.9: If an alternate mode is active, a request to swap + * alternate modes shall trigger a port reset. + */ + switch (port->state) { + case SRC_READY: + case SNK_READY: + tcpm_set_state(port, DR_SWAP_ACCEPT, 0); + break; + default: + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); + break; + } + break; + case PD_CTRL_PR_SWAP: + if (port->port_type != TYPEC_PORT_DRP) { + tcpm_queue_message(port, PD_MSG_CTRL_REJECT); + break; + } + switch (port->state) { + case SRC_READY: + case SNK_READY: + tcpm_set_state(port, PR_SWAP_ACCEPT, 0); + break; + default: + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); + break; + } + break; + case PD_CTRL_VCONN_SWAP: + switch (port->state) { + case SRC_READY: + case SNK_READY: + tcpm_set_state(port, VCONN_SWAP_ACCEPT, 0); + break; + default: + tcpm_queue_message(port, PD_MSG_CTRL_WAIT); + break; + } + break; + default: + tcpm_log(port, "Unhandled ctrl message type %#x", type); + break; + } +} + +static void tcpm_pd_rx_handler(struct work_struct *work) +{ + struct pd_rx_event *event = container_of(work, + struct pd_rx_event, work); + const struct pd_message *msg = &event->msg; + unsigned int cnt = pd_header_cnt_le(msg->header); + struct tcpm_port *port = event->port; + + mutex_lock(&port->lock); + + tcpm_log(port, "PD RX, header: %#x [%d]", le16_to_cpu(msg->header), + port->attached); + + if (port->attached) { + enum pd_ctrl_msg_type type = pd_header_type_le(msg->header); + unsigned int msgid = pd_header_msgid_le(msg->header); + + /* + * USB PD standard, 6.6.1.2: + * "... if MessageID value in a received Message is the + * same as the stored value, the receiver shall return a + * GoodCRC Message with that MessageID value and drop + * the Message (this is a retry of an already received + * Message). Note: this shall not apply to the Soft_Reset + * Message which always has a MessageID value of zero." + */ + if (msgid == port->rx_msgid && type != PD_CTRL_SOFT_RESET) + goto done; + port->rx_msgid = msgid; + + /* + * If both ends believe to be DFP/host, we have a data role + * mismatch. + */ + if (!!(le16_to_cpu(msg->header) & PD_HEADER_DATA_ROLE) == + (port->data_role == TYPEC_HOST)) { + tcpm_log(port, + "Data role mismatch, initiating error recovery"); + tcpm_set_state(port, ERROR_RECOVERY, 0); + } else { + if (cnt) + tcpm_pd_data_request(port, msg); + else + tcpm_pd_ctrl_request(port, msg); + } + } + +done: + mutex_unlock(&port->lock); + kfree(event); +} + +void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg) +{ + struct pd_rx_event *event; + + event = kzalloc(sizeof(*event), GFP_ATOMIC); + if (!event) + return; + + INIT_WORK(&event->work, tcpm_pd_rx_handler); + event->port = port; + memcpy(&event->msg, msg, sizeof(*msg)); + queue_work(port->wq, &event->work); +} +EXPORT_SYMBOL_GPL(tcpm_pd_receive); + +static int tcpm_pd_send_control(struct tcpm_port *port, + enum pd_ctrl_msg_type type) +{ + struct pd_message msg; + + memset(&msg, 0, sizeof(msg)); + msg.header = PD_HEADER_LE(type, port->pwr_role, + port->data_role, + port->message_id, 0); + + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + +/* + * Send queued message without affecting state. + * Return true if state machine should go back to sleep, + * false otherwise. + */ +static bool tcpm_send_queued_message(struct tcpm_port *port) +{ + enum pd_msg_request queued_message; + + do { + queued_message = port->queued_message; + port->queued_message = PD_MSG_NONE; + + switch (queued_message) { + case PD_MSG_CTRL_WAIT: + tcpm_pd_send_control(port, PD_CTRL_WAIT); + break; + case PD_MSG_CTRL_REJECT: + tcpm_pd_send_control(port, PD_CTRL_REJECT); + break; + case PD_MSG_DATA_SINK_CAP: + tcpm_pd_send_sink_caps(port); + break; + case PD_MSG_DATA_SOURCE_CAP: + tcpm_pd_send_source_caps(port); + break; + default: + break; + } + } while (port->queued_message != PD_MSG_NONE); + + if (port->delayed_state != INVALID_STATE) { + if (time_is_after_jiffies(port->delayed_runtime)) { + mod_delayed_work(port->wq, &port->state_machine, + port->delayed_runtime - jiffies); + return true; + } + port->delayed_state = INVALID_STATE; + } + return false; +} + +static int tcpm_pd_check_request(struct tcpm_port *port) +{ + u32 pdo, rdo = port->sink_request; + unsigned int max, op, pdo_max, index; + enum pd_pdo_type type; + + index = rdo_index(rdo); + if (!index || index > port->nr_src_pdo) + return -EINVAL; + + pdo = port->src_pdo[index - 1]; + type = pdo_type(pdo); + switch (type) { + case PDO_TYPE_FIXED: + case PDO_TYPE_VAR: + max = rdo_max_current(rdo); + op = rdo_op_current(rdo); + pdo_max = pdo_max_current(pdo); + + if (op > pdo_max) + return -EINVAL; + if (max > pdo_max && !(rdo & RDO_CAP_MISMATCH)) + return -EINVAL; + + if (type == PDO_TYPE_FIXED) + tcpm_log(port, + "Requested %u mV, %u mA for %u / %u mA", + pdo_fixed_voltage(pdo), pdo_max, op, max); + else + tcpm_log(port, + "Requested %u -> %u mV, %u mA for %u / %u mA", + pdo_min_voltage(pdo), pdo_max_voltage(pdo), + pdo_max, op, max); + break; + case PDO_TYPE_BATT: + max = rdo_max_power(rdo); + op = rdo_op_power(rdo); + pdo_max = pdo_max_power(pdo); + + if (op > pdo_max) + return -EINVAL; + if (max > pdo_max && !(rdo & RDO_CAP_MISMATCH)) + return -EINVAL; + tcpm_log(port, + "Requested %u -> %u mV, %u mW for %u / %u mW", + pdo_min_voltage(pdo), pdo_max_voltage(pdo), + pdo_max, op, max); + break; + default: + return -EINVAL; + } + + port->op_vsafe5v = index == 1; + + return 0; +} + +static int tcpm_pd_select_pdo(struct tcpm_port *port) +{ + unsigned int i, max_mw = 0, max_mv = 0; + int ret = -EINVAL; + + /* + * Select the source PDO providing the most power while staying within + * the board's voltage limits. Prefer PDO providing exp + */ + for (i = 0; i < port->nr_source_caps; i++) { + u32 pdo = port->source_caps[i]; + enum pd_pdo_type type = pdo_type(pdo); + unsigned int mv, ma, mw; + + if (type == PDO_TYPE_FIXED) + mv = pdo_fixed_voltage(pdo); + else + mv = pdo_min_voltage(pdo); + + if (type == PDO_TYPE_BATT) { + mw = pdo_max_power(pdo); + } else { + ma = min(pdo_max_current(pdo), + port->max_snk_ma); + mw = ma * mv / 1000; + } + + /* Perfer higher voltages if available */ + if ((mw > max_mw || (mw == max_mw && mv > max_mv)) && + mv <= port->max_snk_mv) { + ret = i; + max_mw = mw; + max_mv = mv; + } + } + + return ret; +} + +static int tcpm_pd_build_request(struct tcpm_port *port, u32 *rdo) +{ + unsigned int mv, ma, mw, flags; + unsigned int max_ma, max_mw; + enum pd_pdo_type type; + int index; + u32 pdo; + + index = tcpm_pd_select_pdo(port); + if (index < 0) + return -EINVAL; + pdo = port->source_caps[index]; + type = pdo_type(pdo); + + if (type == PDO_TYPE_FIXED) + mv = pdo_fixed_voltage(pdo); + else + mv = pdo_min_voltage(pdo); + + /* Select maximum available current within the board's power limit */ + if (type == PDO_TYPE_BATT) { + mw = pdo_max_power(pdo); + ma = 1000 * min(mw, port->max_snk_mw) / mv; + } else { + ma = min(pdo_max_current(pdo), + 1000 * port->max_snk_mw / mv); + } + ma = min(ma, port->max_snk_ma); + + flags = RDO_USB_COMM | RDO_NO_SUSPEND; + + /* Set mismatch bit if offered power is less than operating power */ + mw = ma * mv / 1000; + max_ma = ma; + max_mw = mw; + if (mw < port->operating_snk_mw) { + flags |= RDO_CAP_MISMATCH; + max_mw = port->operating_snk_mw; + max_ma = max_mw * 1000 / mv; + } + + tcpm_log(port, "cc=%d cc1=%d cc2=%d vbus=%d vconn=%s polarity=%d", + port->cc_req, port->cc1, port->cc2, port->vbus_source, + port->vconn_role == TYPEC_SOURCE ? "source" : "sink", + port->polarity); + + if (type == PDO_TYPE_BATT) { + *rdo = RDO_BATT(index + 1, mw, max_mw, flags); + + tcpm_log(port, "Requesting PDO %d: %u mV, %u mW%s", + index, mv, mw, + flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); + } else { + *rdo = RDO_FIXED(index + 1, ma, max_ma, flags); + + tcpm_log(port, "Requesting PDO %d: %u mV, %u mA%s", + index, mv, ma, + flags & RDO_CAP_MISMATCH ? " [mismatch]" : ""); + } + + port->current_limit = ma; + port->supply_voltage = mv; + + return 0; +} + +static int tcpm_pd_send_request(struct tcpm_port *port) +{ + struct pd_message msg; + int ret; + u32 rdo; + + ret = tcpm_pd_build_request(port, &rdo); + if (ret < 0) + return ret; + + memset(&msg, 0, sizeof(msg)); + msg.header = PD_HEADER_LE(PD_DATA_REQUEST, + port->pwr_role, + port->data_role, + port->message_id, 1); + msg.payload[0] = cpu_to_le32(rdo); + + return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); +} + +static int tcpm_set_vbus(struct tcpm_port *port, bool enable) +{ + int ret; + + if (enable && port->vbus_charge) + return -EINVAL; + + tcpm_log(port, "vbus:=%d charge=%d", enable, port->vbus_charge); + + ret = port->tcpc->set_vbus(port->tcpc, enable, port->vbus_charge); + if (ret < 0) + return ret; + + port->vbus_source = enable; + return 0; +} + +static int tcpm_set_charge(struct tcpm_port *port, bool charge) +{ + int ret; + + if (charge && port->vbus_source) + return -EINVAL; + + if (charge != port->vbus_charge) { + tcpm_log(port, "vbus=%d charge:=%d", port->vbus_source, charge); + ret = port->tcpc->set_vbus(port->tcpc, port->vbus_source, + charge); + if (ret < 0) + return ret; + } + port->vbus_charge = charge; + return 0; +} + +static bool tcpm_start_drp_toggling(struct tcpm_port *port) +{ + int ret; + + if (port->tcpc->start_drp_toggling && + port->port_type == TYPEC_PORT_DRP) { + tcpm_log_force(port, "Start DRP toggling"); + ret = port->tcpc->start_drp_toggling(port->tcpc, + tcpm_rp_cc(port)); + if (!ret) + return true; + } + + return false; +} + +static void tcpm_set_cc(struct tcpm_port *port, enum typec_cc_status cc) +{ + tcpm_log(port, "cc:=%d", cc); + port->cc_req = cc; + port->tcpc->set_cc(port->tcpc, cc); +} + +static int tcpm_init_vbus(struct tcpm_port *port) +{ + int ret; + + ret = port->tcpc->set_vbus(port->tcpc, false, false); + port->vbus_source = false; + port->vbus_charge = false; + return ret; +} + +static int tcpm_init_vconn(struct tcpm_port *port) +{ + int ret; + + ret = port->tcpc->set_vconn(port->tcpc, false); + port->vconn_role = TYPEC_SINK; + return ret; +} + +static void tcpm_typec_connect(struct tcpm_port *port) +{ + if (!port->connected) { + /* Make sure we don't report stale identity information */ + memset(&port->partner_ident, 0, sizeof(port->partner_ident)); + port->partner_desc.usb_pd = port->pd_capable; + if (tcpm_port_is_debug(port)) + port->partner_desc.accessory = TYPEC_ACCESSORY_DEBUG; + else if (tcpm_port_is_audio(port)) + port->partner_desc.accessory = TYPEC_ACCESSORY_AUDIO; + else + port->partner_desc.accessory = TYPEC_ACCESSORY_NONE; + port->partner = typec_register_partner(port->typec_port, + &port->partner_desc); + port->connected = true; + } +} + +static int tcpm_src_attach(struct tcpm_port *port) +{ + enum typec_cc_polarity polarity = + port->cc2 == TYPEC_CC_RD ? TYPEC_POLARITY_CC2 + : TYPEC_POLARITY_CC1; + int ret; + + if (port->attached) + return 0; + + ret = tcpm_set_polarity(port, polarity); + if (ret < 0) + return ret; + + ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST); + if (ret < 0) + return ret; + + ret = port->tcpc->set_pd_rx(port->tcpc, true); + if (ret < 0) + goto out_disable_mux; + + /* + * USB Type-C specification, version 1.2, + * chapter 4.5.2.2.8.1 (Attached.SRC Requirements) + * Enable VCONN only if the non-RD port is set to RA. + */ + if ((polarity == TYPEC_POLARITY_CC1 && port->cc2 == TYPEC_CC_RA) || + (polarity == TYPEC_POLARITY_CC2 && port->cc1 == TYPEC_CC_RA)) { + ret = tcpm_set_vconn(port, true); + if (ret < 0) + goto out_disable_pd; + } + + ret = tcpm_set_vbus(port, true); + if (ret < 0) + goto out_disable_vconn; + + port->pd_capable = false; + + port->partner = NULL; + + port->attached = true; + port->send_discover = true; + + return 0; + +out_disable_vconn: + tcpm_set_vconn(port, false); +out_disable_pd: + port->tcpc->set_pd_rx(port->tcpc, false); +out_disable_mux: + tcpm_mux_set(port, TYPEC_MUX_NONE, TCPC_USB_SWITCH_DISCONNECT); + return ret; +} + +static void tcpm_typec_disconnect(struct tcpm_port *port) +{ + if (port->connected) { + typec_unregister_partner(port->partner); + port->partner = NULL; + port->connected = false; + } +} + +static void tcpm_unregister_altmodes(struct tcpm_port *port) +{ + struct pd_mode_data *modep = &port->mode_data; + int i; + + for (i = 0; i < modep->altmodes; i++) { + typec_unregister_altmode(port->partner_altmode[i]); + port->partner_altmode[i] = NULL; + } + + memset(modep, 0, sizeof(*modep)); +} + +static void tcpm_reset_port(struct tcpm_port *port) +{ + tcpm_unregister_altmodes(port); + tcpm_typec_disconnect(port); + port->attached = false; + port->pd_capable = false; + + /* + * First Rx ID should be 0; set this to a sentinel of -1 so that + * we can check tcpm_pd_rx_handler() if we had seen it before. + */ + port->rx_msgid = -1; + + port->tcpc->set_pd_rx(port->tcpc, false); + tcpm_init_vbus(port); /* also disables charging */ + tcpm_init_vconn(port); + tcpm_set_current_limit(port, 0, 0); + tcpm_set_polarity(port, TYPEC_POLARITY_CC1); + tcpm_set_attached_state(port, false); + port->try_src_count = 0; + port->try_snk_count = 0; +} + +static void tcpm_detach(struct tcpm_port *port) +{ + if (!port->attached) + return; + + if (tcpm_port_is_disconnected(port)) + port->hard_reset_count = 0; + + tcpm_reset_port(port); +} + +static void tcpm_src_detach(struct tcpm_port *port) +{ + tcpm_detach(port); +} + +static int tcpm_snk_attach(struct tcpm_port *port) +{ + int ret; + + if (port->attached) + return 0; + + ret = tcpm_set_polarity(port, port->cc2 != TYPEC_CC_OPEN ? + TYPEC_POLARITY_CC2 : TYPEC_POLARITY_CC1); + if (ret < 0) + return ret; + + ret = tcpm_set_roles(port, true, TYPEC_SINK, TYPEC_DEVICE); + if (ret < 0) + return ret; + + port->pd_capable = false; + + port->partner = NULL; + + port->attached = true; + port->send_discover = true; + + return 0; +} + +static void tcpm_snk_detach(struct tcpm_port *port) +{ + tcpm_detach(port); + + /* XXX: (Dis)connect SuperSpeed mux? */ +} + +static int tcpm_acc_attach(struct tcpm_port *port) +{ + int ret; + + if (port->attached) + return 0; + + ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST); + if (ret < 0) + return ret; + + port->partner = NULL; + + tcpm_typec_connect(port); + + port->attached = true; + + return 0; +} + +static void tcpm_acc_detach(struct tcpm_port *port) +{ + tcpm_detach(port); +} + +static inline enum tcpm_state hard_reset_state(struct tcpm_port *port) +{ + if (port->hard_reset_count < PD_N_HARD_RESET_COUNT) + return HARD_RESET_SEND; + if (port->pd_capable) + return ERROR_RECOVERY; + if (port->pwr_role == TYPEC_SOURCE) + return SRC_UNATTACHED; + if (port->state == SNK_WAIT_CAPABILITIES) + return SNK_READY; + return SNK_UNATTACHED; +} + +static inline enum tcpm_state ready_state(struct tcpm_port *port) +{ + if (port->pwr_role == TYPEC_SOURCE) + return SRC_READY; + else + return SNK_READY; +} + +static inline enum tcpm_state unattached_state(struct tcpm_port *port) +{ + if (port->port_type == TYPEC_PORT_DRP) { + if (port->pwr_role == TYPEC_SOURCE) + return SRC_UNATTACHED; + else + return SNK_UNATTACHED; + } else if (port->port_type == TYPEC_PORT_DFP) { + return SRC_UNATTACHED; + } + + return SNK_UNATTACHED; +} + +static void tcpm_check_send_discover(struct tcpm_port *port) +{ + if (port->data_role == TYPEC_HOST && port->send_discover && + port->pd_capable) { + tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0); + port->send_discover = false; + } +} + +static void tcpm_swap_complete(struct tcpm_port *port, int result) +{ + if (port->swap_pending) { + port->swap_status = result; + port->swap_pending = false; + port->non_pd_role_swap = false; + complete(&port->swap_complete); + } +} + +static enum typec_pwr_opmode tcpm_get_pwr_opmode(enum typec_cc_status cc) +{ + switch (cc) { + case TYPEC_CC_RP_1_5: + return TYPEC_PWR_MODE_1_5A; + case TYPEC_CC_RP_3_0: + return TYPEC_PWR_MODE_3_0A; + case TYPEC_CC_RP_DEF: + default: + return TYPEC_PWR_MODE_USB; + } +} + +static void run_state_machine(struct tcpm_port *port) +{ + int ret; + enum typec_pwr_opmode opmode; + unsigned int msecs; + + port->enter_state = port->state; + switch (port->state) { + case DRP_TOGGLING: + break; + /* SRC states */ + case SRC_UNATTACHED: + if (!port->non_pd_role_swap) + tcpm_swap_complete(port, -ENOTCONN); + tcpm_src_detach(port); + if (tcpm_start_drp_toggling(port)) { + tcpm_set_state(port, DRP_TOGGLING, 0); + break; + } + tcpm_set_cc(port, tcpm_rp_cc(port)); + if (port->port_type == TYPEC_PORT_DRP) + tcpm_set_state(port, SNK_UNATTACHED, PD_T_DRP_SNK); + break; + case SRC_ATTACH_WAIT: + if (tcpm_port_is_debug(port)) + tcpm_set_state(port, DEBUG_ACC_ATTACHED, + PD_T_CC_DEBOUNCE); + else if (tcpm_port_is_audio(port)) + tcpm_set_state(port, AUDIO_ACC_ATTACHED, + PD_T_CC_DEBOUNCE); + else if (tcpm_port_is_source(port)) + tcpm_set_state(port, + tcpm_try_snk(port) ? SNK_TRY + : SRC_ATTACHED, + PD_T_CC_DEBOUNCE); + break; + + case SNK_TRY: + port->try_snk_count++; + /* + * Requirements: + * - Do not drive vconn or vbus + * - Terminate CC pins (both) to Rd + * Action: + * - Wait for tDRPTry (PD_T_DRP_TRY). + * Until then, ignore any state changes. + */ + tcpm_set_cc(port, TYPEC_CC_RD); + tcpm_set_state(port, SNK_TRY_WAIT, PD_T_DRP_TRY); + break; + case SNK_TRY_WAIT: + if (tcpm_port_is_sink(port)) { + tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE, 0); + } else { + tcpm_set_state(port, SRC_TRYWAIT, 0); + port->max_wait = 0; + } + break; + case SNK_TRY_WAIT_DEBOUNCE: + tcpm_set_state(port, SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS, + PD_T_PD_DEBOUNCE); + break; + case SNK_TRY_WAIT_DEBOUNCE_CHECK_VBUS: + if (port->vbus_present && tcpm_port_is_sink(port)) { + tcpm_set_state(port, SNK_ATTACHED, 0); + } else { + tcpm_set_state(port, SRC_TRYWAIT, 0); + port->max_wait = 0; + } + break; + case SRC_TRYWAIT: + tcpm_set_cc(port, tcpm_rp_cc(port)); + if (port->max_wait == 0) { + port->max_wait = jiffies + + msecs_to_jiffies(PD_T_DRP_TRY); + tcpm_set_state(port, SRC_TRYWAIT_UNATTACHED, + PD_T_DRP_TRY); + } else { + if (time_is_after_jiffies(port->max_wait)) + tcpm_set_state(port, SRC_TRYWAIT_UNATTACHED, + jiffies_to_msecs(port->max_wait - + jiffies)); + else + tcpm_set_state(port, SNK_UNATTACHED, 0); + } + break; + case SRC_TRYWAIT_DEBOUNCE: + tcpm_set_state(port, SRC_ATTACHED, PD_T_CC_DEBOUNCE); + break; + case SRC_TRYWAIT_UNATTACHED: + tcpm_set_state(port, SNK_UNATTACHED, 0); + break; + + case SRC_ATTACHED: + ret = tcpm_src_attach(port); + tcpm_set_state(port, SRC_UNATTACHED, + ret < 0 ? 0 : PD_T_PS_SOURCE_ON); + break; + case SRC_STARTUP: + opmode = tcpm_get_pwr_opmode(tcpm_rp_cc(port)); + typec_set_pwr_opmode(port->typec_port, opmode); + port->pwr_opmode = TYPEC_PWR_MODE_USB; + port->caps_count = 0; + port->message_id = 0; + port->rx_msgid = -1; + port->explicit_contract = false; + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); + break; + case SRC_SEND_CAPABILITIES: + port->caps_count++; + if (port->caps_count > PD_N_CAPS_COUNT) { + tcpm_set_state(port, SRC_READY, 0); + break; + } + ret = tcpm_pd_send_source_caps(port); + if (ret < 0) { + tcpm_set_state(port, SRC_SEND_CAPABILITIES, + PD_T_SEND_SOURCE_CAP); + } else { + /* + * Per standard, we should clear the reset counter here. + * However, that can result in state machine hang-ups. + * Reset it only in READY state to improve stability. + */ + /* port->hard_reset_count = 0; */ + port->caps_count = 0; + port->pd_capable = true; + tcpm_set_state_cond(port, hard_reset_state(port), + PD_T_SEND_SOURCE_CAP); + } + break; + case SRC_NEGOTIATE_CAPABILITIES: + ret = tcpm_pd_check_request(port); + if (ret < 0) { + tcpm_pd_send_control(port, PD_CTRL_REJECT); + if (!port->explicit_contract) { + tcpm_set_state(port, + SRC_WAIT_NEW_CAPABILITIES, 0); + } else { + tcpm_set_state(port, SRC_READY, 0); + } + } else { + tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_set_state(port, SRC_TRANSITION_SUPPLY, + PD_T_SRC_TRANSITION); + } + break; + case SRC_TRANSITION_SUPPLY: + /* XXX: regulator_set_voltage(vbus, ...) */ + tcpm_pd_send_control(port, PD_CTRL_PS_RDY); + port->explicit_contract = true; + typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_PD); + port->pwr_opmode = TYPEC_PWR_MODE_PD; + tcpm_set_state_cond(port, SRC_READY, 0); + break; + case SRC_READY: +#if 1 + port->hard_reset_count = 0; +#endif + port->try_src_count = 0; + + tcpm_swap_complete(port, 0); + tcpm_typec_connect(port); + tcpm_check_send_discover(port); + /* + * 6.3.5 + * Sending ping messages is not necessary if + * - the source operates at vSafe5V + * or + * - The system is not operating in PD mode + * or + * - Both partners are connected using a Type-C connector + * + * There is no actual need to send PD messages since the local + * port type-c and the spec does not clearly say whether PD is + * possible when type-c is connected to Type-A/B + */ + break; + case SRC_WAIT_NEW_CAPABILITIES: + /* Nothing to do... */ + break; + + /* SNK states */ + case SNK_UNATTACHED: + if (!port->non_pd_role_swap) + tcpm_swap_complete(port, -ENOTCONN); + tcpm_snk_detach(port); + if (tcpm_start_drp_toggling(port)) { + tcpm_set_state(port, DRP_TOGGLING, 0); + break; + } + tcpm_set_cc(port, TYPEC_CC_RD); + if (port->port_type == TYPEC_PORT_DRP) + tcpm_set_state(port, SRC_UNATTACHED, PD_T_DRP_SRC); + break; + case SNK_ATTACH_WAIT: + if ((port->cc1 == TYPEC_CC_OPEN && + port->cc2 != TYPEC_CC_OPEN) || + (port->cc1 != TYPEC_CC_OPEN && + port->cc2 == TYPEC_CC_OPEN)) + tcpm_set_state(port, SNK_DEBOUNCED, + PD_T_CC_DEBOUNCE); + else if (tcpm_port_is_disconnected(port)) + tcpm_set_state(port, SNK_UNATTACHED, + PD_T_PD_DEBOUNCE); + break; + case SNK_DEBOUNCED: + if (tcpm_port_is_disconnected(port)) + tcpm_set_state(port, SNK_UNATTACHED, + PD_T_PD_DEBOUNCE); + else if (port->vbus_present) + tcpm_set_state(port, + tcpm_try_src(port) ? SRC_TRY + : SNK_ATTACHED, + 0); + else + /* Wait for VBUS, but not forever */ + tcpm_set_state(port, PORT_RESET, PD_T_PS_SOURCE_ON); + break; + + case SRC_TRY: + port->try_src_count++; + tcpm_set_cc(port, tcpm_rp_cc(port)); + port->max_wait = 0; + tcpm_set_state(port, SRC_TRY_WAIT, 0); + break; + case SRC_TRY_WAIT: + if (port->max_wait == 0) { + port->max_wait = jiffies + + msecs_to_jiffies(PD_T_DRP_TRY); + msecs = PD_T_DRP_TRY; + } else { + if (time_is_after_jiffies(port->max_wait)) + msecs = jiffies_to_msecs(port->max_wait - + jiffies); + else + msecs = 0; + } + tcpm_set_state(port, SNK_TRYWAIT, msecs); + break; + case SRC_TRY_DEBOUNCE: + tcpm_set_state(port, SRC_ATTACHED, PD_T_PD_DEBOUNCE); + break; + case SNK_TRYWAIT: + tcpm_set_cc(port, TYPEC_CC_RD); + tcpm_set_state(port, SNK_TRYWAIT_VBUS, PD_T_CC_DEBOUNCE); + break; + case SNK_TRYWAIT_VBUS: + /* + * TCPM stays in this state indefinitely until VBUS + * is detected as long as Rp is not detected for + * more than a time period of tPDDebounce. + */ + if (port->vbus_present && tcpm_port_is_sink(port)) { + tcpm_set_state(port, SNK_ATTACHED, 0); + break; + } + if (!tcpm_port_is_sink(port)) + tcpm_set_state(port, SNK_TRYWAIT_DEBOUNCE, 0); + break; + case SNK_TRYWAIT_DEBOUNCE: + tcpm_set_state(port, SNK_UNATTACHED, PD_T_PD_DEBOUNCE); + break; + case SNK_ATTACHED: + ret = tcpm_snk_attach(port); + if (ret < 0) + tcpm_set_state(port, SNK_UNATTACHED, 0); + else + tcpm_set_state(port, SNK_STARTUP, 0); + break; + case SNK_STARTUP: + opmode = tcpm_get_pwr_opmode(port->polarity ? + port->cc2 : port->cc1); + typec_set_pwr_opmode(port->typec_port, opmode); + port->pwr_opmode = TYPEC_PWR_MODE_USB; + port->message_id = 0; + port->rx_msgid = -1; + port->explicit_contract = false; + tcpm_set_state(port, SNK_DISCOVERY, 0); + break; + case SNK_DISCOVERY: + if (port->vbus_present) { + tcpm_set_current_limit(port, + tcpm_get_current_limit(port), + 5000); + tcpm_set_charge(port, true); + tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); + break; + } + /* + * For DRP, timeouts differ. Also, handling is supposed to be + * different and much more complex (dead battery detection; + * see USB power delivery specification, section 8.3.3.6.1.5.1). + */ + tcpm_set_state(port, hard_reset_state(port), + port->port_type == TYPEC_PORT_DRP ? + PD_T_DB_DETECT : PD_T_NO_RESPONSE); + break; + case SNK_DISCOVERY_DEBOUNCE: + tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE_DONE, + PD_T_CC_DEBOUNCE); + break; + case SNK_DISCOVERY_DEBOUNCE_DONE: + if (!tcpm_port_is_disconnected(port) && + tcpm_port_is_sink(port) && + time_is_after_jiffies(port->delayed_runtime)) { + tcpm_set_state(port, SNK_DISCOVERY, + port->delayed_runtime - jiffies); + break; + } + tcpm_set_state(port, unattached_state(port), 0); + break; + case SNK_WAIT_CAPABILITIES: + ret = port->tcpc->set_pd_rx(port->tcpc, true); + if (ret < 0) { + tcpm_set_state(port, SNK_READY, 0); + break; + } + /* + * If VBUS has never been low, and we time out waiting + * for source cap, try a soft reset first, in case we + * were already in a stable contract before this boot. + * Do this only once. + */ + if (port->vbus_never_low) { + port->vbus_never_low = false; + tcpm_set_state(port, SOFT_RESET_SEND, + PD_T_SINK_WAIT_CAP); + } else { + tcpm_set_state(port, hard_reset_state(port), + PD_T_SINK_WAIT_CAP); + } + break; + case SNK_NEGOTIATE_CAPABILITIES: + port->pd_capable = true; + port->hard_reset_count = 0; + ret = tcpm_pd_send_request(port); + if (ret < 0) { + /* Let the Source send capabilities again. */ + tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); + } else { + tcpm_set_state_cond(port, hard_reset_state(port), + PD_T_SENDER_RESPONSE); + } + break; + case SNK_TRANSITION_SINK: + case SNK_TRANSITION_SINK_VBUS: + tcpm_set_state(port, hard_reset_state(port), + PD_T_PS_TRANSITION); + break; + case SNK_READY: + port->try_snk_count = 0; + if (port->explicit_contract) { + typec_set_pwr_opmode(port->typec_port, + TYPEC_PWR_MODE_PD); + port->pwr_opmode = TYPEC_PWR_MODE_PD; + } + + tcpm_swap_complete(port, 0); + tcpm_typec_connect(port); + tcpm_check_send_discover(port); + break; + + /* Accessory states */ + case ACC_UNATTACHED: + tcpm_acc_detach(port); + tcpm_set_state(port, SRC_UNATTACHED, 0); + break; + case DEBUG_ACC_ATTACHED: + case AUDIO_ACC_ATTACHED: + ret = tcpm_acc_attach(port); + if (ret < 0) + tcpm_set_state(port, ACC_UNATTACHED, 0); + break; + case AUDIO_ACC_DEBOUNCE: + tcpm_set_state(port, ACC_UNATTACHED, PD_T_CC_DEBOUNCE); + break; + + /* Hard_Reset states */ + case HARD_RESET_SEND: + tcpm_pd_transmit(port, TCPC_TX_HARD_RESET, NULL); + tcpm_set_state(port, HARD_RESET_START, 0); + break; + case HARD_RESET_START: + port->hard_reset_count++; + port->tcpc->set_pd_rx(port->tcpc, false); + tcpm_unregister_altmodes(port); + port->send_discover = true; + if (port->pwr_role == TYPEC_SOURCE) + tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF, + PD_T_PS_HARD_RESET); + else + tcpm_set_state(port, SNK_HARD_RESET_SINK_OFF, 0); + break; + case SRC_HARD_RESET_VBUS_OFF: + tcpm_set_vconn(port, true); + tcpm_set_vbus(port, false); + tcpm_set_roles(port, false, TYPEC_SOURCE, TYPEC_HOST); + tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER); + break; + case SRC_HARD_RESET_VBUS_ON: + tcpm_set_vbus(port, true); + port->tcpc->set_pd_rx(port->tcpc, true); + tcpm_set_attached_state(port, true); + tcpm_set_state(port, SRC_UNATTACHED, PD_T_PS_SOURCE_ON); + break; + case SNK_HARD_RESET_SINK_OFF: + tcpm_set_vconn(port, false); + tcpm_set_charge(port, false); + tcpm_set_roles(port, false, TYPEC_SINK, TYPEC_DEVICE); + /* + * VBUS may or may not toggle, depending on the adapter. + * If it doesn't toggle, transition to SNK_HARD_RESET_SINK_ON + * directly after timeout. + */ + tcpm_set_state(port, SNK_HARD_RESET_SINK_ON, PD_T_SAFE_0V); + break; + case SNK_HARD_RESET_WAIT_VBUS: + /* Assume we're disconnected if VBUS doesn't come back. */ + tcpm_set_state(port, SNK_UNATTACHED, + PD_T_SRC_RECOVER_MAX + PD_T_SRC_TURN_ON); + break; + case SNK_HARD_RESET_SINK_ON: + /* Note: There is no guarantee that VBUS is on in this state */ + /* + * XXX: + * The specification suggests that dual mode ports in sink + * mode should transition to state PE_SRC_Transition_to_default. + * See USB power delivery specification chapter 8.3.3.6.1.3. + * This would mean to to + * - turn off VCONN, reset power supply + * - request hardware reset + * - turn on VCONN + * - Transition to state PE_Src_Startup + * SNK only ports shall transition to state Snk_Startup + * (see chapter 8.3.3.3.8). + * Similar, dual-mode ports in source mode should transition + * to PE_SNK_Transition_to_default. + */ + tcpm_set_attached_state(port, true); + tcpm_set_state(port, SNK_STARTUP, 0); + break; + + /* Soft_Reset states */ + case SOFT_RESET: + port->message_id = 0; + port->rx_msgid = -1; + tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + if (port->pwr_role == TYPEC_SOURCE) + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); + else + tcpm_set_state(port, SNK_WAIT_CAPABILITIES, 0); + break; + case SOFT_RESET_SEND: + port->message_id = 0; + port->rx_msgid = -1; + if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET)) + tcpm_set_state_cond(port, hard_reset_state(port), 0); + else + tcpm_set_state_cond(port, hard_reset_state(port), + PD_T_SENDER_RESPONSE); + break; + + /* DR_Swap states */ + case DR_SWAP_SEND: + tcpm_pd_send_control(port, PD_CTRL_DR_SWAP); + tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT, + PD_T_SENDER_RESPONSE); + break; + case DR_SWAP_ACCEPT: + tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); + break; + case DR_SWAP_SEND_TIMEOUT: + tcpm_swap_complete(port, -ETIMEDOUT); + tcpm_set_state(port, ready_state(port), 0); + break; + case DR_SWAP_CHANGE_DR: + if (port->data_role == TYPEC_HOST) { + tcpm_unregister_altmodes(port); + tcpm_set_roles(port, true, port->pwr_role, + TYPEC_DEVICE); + } else { + tcpm_set_roles(port, true, port->pwr_role, + TYPEC_HOST); + port->send_discover = true; + } + tcpm_set_state(port, ready_state(port), 0); + break; + + /* PR_Swap states */ + case PR_SWAP_ACCEPT: + tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_set_state(port, PR_SWAP_START, 0); + break; + case PR_SWAP_SEND: + tcpm_pd_send_control(port, PD_CTRL_PR_SWAP); + tcpm_set_state_cond(port, PR_SWAP_SEND_TIMEOUT, + PD_T_SENDER_RESPONSE); + break; + case PR_SWAP_SEND_TIMEOUT: + tcpm_swap_complete(port, -ETIMEDOUT); + tcpm_set_state(port, ready_state(port), 0); + break; + case PR_SWAP_START: + if (port->pwr_role == TYPEC_SOURCE) + tcpm_set_state(port, PR_SWAP_SRC_SNK_TRANSITION_OFF, + PD_T_SRC_TRANSITION); + else + tcpm_set_state(port, PR_SWAP_SNK_SRC_SINK_OFF, 0); + break; + case PR_SWAP_SRC_SNK_TRANSITION_OFF: + tcpm_set_vbus(port, false); + port->explicit_contract = false; + /* allow time for Vbus discharge, must be < tSrcSwapStdby */ + tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF, + PD_T_SRCSWAPSTDBY); + break; + case PR_SWAP_SRC_SNK_SOURCE_OFF: + tcpm_set_cc(port, TYPEC_CC_RD); + /* allow CC debounce */ + tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED, + PD_T_CC_DEBOUNCE); + break; + case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED: + /* + * USB-PD standard, 6.2.1.4, Port Power Role: + * "During the Power Role Swap Sequence, for the initial Source + * Port, the Port Power Role field shall be set to Sink in the + * PS_RDY Message indicating that the initial Source’s power + * supply is turned off" + */ + tcpm_set_pwr_role(port, TYPEC_SINK); + if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) { + tcpm_set_state(port, ERROR_RECOVERY, 0); + break; + } + tcpm_set_state_cond(port, SNK_UNATTACHED, PD_T_PS_SOURCE_ON); + break; + case PR_SWAP_SRC_SNK_SINK_ON: + tcpm_set_state(port, SNK_STARTUP, 0); + break; + case PR_SWAP_SNK_SRC_SINK_OFF: + tcpm_set_charge(port, false); + tcpm_set_state(port, hard_reset_state(port), + PD_T_PS_SOURCE_OFF); + break; + case PR_SWAP_SNK_SRC_SOURCE_ON: + tcpm_set_cc(port, tcpm_rp_cc(port)); + tcpm_set_vbus(port, true); + /* + * allow time VBUS ramp-up, must be < tNewSrc + * Also, this window overlaps with CC debounce as well. + * So, Wait for the max of two which is PD_T_NEWSRC + */ + tcpm_set_state(port, PR_SWAP_SNK_SRC_SOURCE_ON_VBUS_RAMPED_UP, + PD_T_NEWSRC); + break; + case PR_SWAP_SNK_SRC_SOURCE_ON_VBUS_RAMPED_UP: + /* + * USB PD standard, 6.2.1.4: + * "Subsequent Messages initiated by the Policy Engine, + * such as the PS_RDY Message sent to indicate that Vbus + * is ready, will have the Port Power Role field set to + * Source." + */ + tcpm_set_pwr_role(port, TYPEC_SOURCE); + tcpm_pd_send_control(port, PD_CTRL_PS_RDY); + tcpm_set_state(port, SRC_STARTUP, 0); + break; + + case VCONN_SWAP_ACCEPT: + tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_set_state(port, VCONN_SWAP_START, 0); + break; + case VCONN_SWAP_SEND: + tcpm_pd_send_control(port, PD_CTRL_VCONN_SWAP); + tcpm_set_state(port, VCONN_SWAP_SEND_TIMEOUT, + PD_T_SENDER_RESPONSE); + break; + case VCONN_SWAP_SEND_TIMEOUT: + tcpm_swap_complete(port, -ETIMEDOUT); + tcpm_set_state(port, ready_state(port), 0); + break; + case VCONN_SWAP_START: + if (port->vconn_role == TYPEC_SOURCE) + tcpm_set_state(port, VCONN_SWAP_WAIT_FOR_VCONN, 0); + else + tcpm_set_state(port, VCONN_SWAP_TURN_ON_VCONN, 0); + break; + case VCONN_SWAP_WAIT_FOR_VCONN: + tcpm_set_state(port, hard_reset_state(port), + PD_T_VCONN_SOURCE_ON); + break; + case VCONN_SWAP_TURN_ON_VCONN: + tcpm_set_vconn(port, true); + tcpm_pd_send_control(port, PD_CTRL_PS_RDY); + tcpm_set_state(port, ready_state(port), 0); + break; + case VCONN_SWAP_TURN_OFF_VCONN: + tcpm_set_vconn(port, false); + tcpm_set_state(port, ready_state(port), 0); + break; + + case DR_SWAP_CANCEL: + case PR_SWAP_CANCEL: + case VCONN_SWAP_CANCEL: + tcpm_swap_complete(port, port->swap_status); + if (port->pwr_role == TYPEC_SOURCE) + tcpm_set_state(port, SRC_READY, 0); + else + tcpm_set_state(port, SNK_READY, 0); + break; + + case BIST_RX: + switch (BDO_MODE_MASK(port->bist_request)) { + case BDO_MODE_CARRIER2: + tcpm_pd_transmit(port, TCPC_TX_BIST_MODE_2, NULL); + break; + default: + break; + } + /* Always switch to unattached state */ + tcpm_set_state(port, unattached_state(port), 0); + break; + case ERROR_RECOVERY: + tcpm_swap_complete(port, -EPROTO); + tcpm_set_state(port, PORT_RESET, 0); + break; + case PORT_RESET: + tcpm_reset_port(port); + tcpm_set_cc(port, TYPEC_CC_OPEN); + tcpm_set_state(port, PORT_RESET_WAIT_OFF, + PD_T_ERROR_RECOVERY); + break; + case PORT_RESET_WAIT_OFF: + tcpm_set_state(port, + tcpm_default_state(port), + port->vbus_present ? PD_T_PS_SOURCE_OFF : 0); + break; + default: + WARN(1, "Unexpected port state %d\n", port->state); + break; + } +} + +static void tcpm_state_machine_work(struct work_struct *work) +{ + struct tcpm_port *port = container_of(work, struct tcpm_port, + state_machine.work); + enum tcpm_state prev_state; + + mutex_lock(&port->lock); + port->state_machine_running = true; + + if (port->queued_message && tcpm_send_queued_message(port)) + goto done; + + /* If we were queued due to a delayed state change, update it now */ + if (port->delayed_state) { + tcpm_log(port, "state change %s -> %s [delayed %ld ms]", + tcpm_states[port->state], + tcpm_states[port->delayed_state], port->delay_ms); + port->prev_state = port->state; + port->state = port->delayed_state; + port->delayed_state = INVALID_STATE; + } + + /* + * Continue running as long as we have (non-delayed) state changes + * to make. + */ + do { + prev_state = port->state; + run_state_machine(port); + if (port->queued_message) + tcpm_send_queued_message(port); + } while (port->state != prev_state && !port->delayed_state); + +done: + port->state_machine_running = false; + mutex_unlock(&port->lock); +} + +static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1, + enum typec_cc_status cc2) +{ + enum typec_cc_status old_cc1, old_cc2; + enum tcpm_state new_state; + + old_cc1 = port->cc1; + old_cc2 = port->cc2; + port->cc1 = cc1; + port->cc2 = cc2; + + tcpm_log_force(port, + "CC1: %u -> %u, CC2: %u -> %u [state %s, polarity %d, %s]", + old_cc1, cc1, old_cc2, cc2, tcpm_states[port->state], + port->polarity, + tcpm_port_is_disconnected(port) ? "disconnected" + : "connected"); + + switch (port->state) { + case DRP_TOGGLING: + if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || + tcpm_port_is_source(port)) + tcpm_set_state(port, SRC_ATTACH_WAIT, 0); + else if (tcpm_port_is_sink(port)) + tcpm_set_state(port, SNK_ATTACH_WAIT, 0); + break; + case SRC_UNATTACHED: + case ACC_UNATTACHED: + if (tcpm_port_is_debug(port) || tcpm_port_is_audio(port) || + tcpm_port_is_source(port)) + tcpm_set_state(port, SRC_ATTACH_WAIT, 0); + break; + case SRC_ATTACH_WAIT: + if (tcpm_port_is_disconnected(port) || + tcpm_port_is_audio_detached(port)) + tcpm_set_state(port, SRC_UNATTACHED, 0); + else if (cc1 != old_cc1 || cc2 != old_cc2) + tcpm_set_state(port, SRC_ATTACH_WAIT, 0); + break; + case SRC_ATTACHED: + case SRC_SEND_CAPABILITIES: + case SRC_READY: + if (tcpm_port_is_disconnected(port) || + !tcpm_port_is_source(port)) + tcpm_set_state(port, SRC_UNATTACHED, 0); + break; + case SNK_UNATTACHED: + if (tcpm_port_is_sink(port)) + tcpm_set_state(port, SNK_ATTACH_WAIT, 0); + break; + case SNK_ATTACH_WAIT: + if ((port->cc1 == TYPEC_CC_OPEN && + port->cc2 != TYPEC_CC_OPEN) || + (port->cc1 != TYPEC_CC_OPEN && + port->cc2 == TYPEC_CC_OPEN)) + new_state = SNK_DEBOUNCED; + else if (tcpm_port_is_disconnected(port)) + new_state = SNK_UNATTACHED; + else + break; + if (new_state != port->delayed_state) + tcpm_set_state(port, SNK_ATTACH_WAIT, 0); + break; + case SNK_DEBOUNCED: + if (tcpm_port_is_disconnected(port)) + new_state = SNK_UNATTACHED; + else if (port->vbus_present) + new_state = tcpm_try_src(port) ? SRC_TRY : SNK_ATTACHED; + else + new_state = SNK_UNATTACHED; + if (new_state != port->delayed_state) + tcpm_set_state(port, SNK_DEBOUNCED, 0); + break; + case SNK_READY: + if (tcpm_port_is_disconnected(port)) + tcpm_set_state(port, unattached_state(port), 0); + else if (!port->pd_capable && + (cc1 != old_cc1 || cc2 != old_cc2)) + tcpm_set_current_limit(port, + tcpm_get_current_limit(port), + 5000); + break; + + case AUDIO_ACC_ATTACHED: + if (cc1 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_OPEN) + tcpm_set_state(port, AUDIO_ACC_DEBOUNCE, 0); + break; + case AUDIO_ACC_DEBOUNCE: + if (tcpm_port_is_audio(port)) + tcpm_set_state(port, AUDIO_ACC_ATTACHED, 0); + break; + + case DEBUG_ACC_ATTACHED: + if (cc1 == TYPEC_CC_OPEN || cc2 == TYPEC_CC_OPEN) + tcpm_set_state(port, ACC_UNATTACHED, 0); + break; + + case SNK_TRY: + /* Do nothing, waiting for timeout */ + break; + + case SNK_DISCOVERY: + /* CC line is unstable, wait for debounce */ + if (tcpm_port_is_disconnected(port)) + tcpm_set_state(port, SNK_DISCOVERY_DEBOUNCE, 0); + break; + case SNK_DISCOVERY_DEBOUNCE: + break; + + case SRC_TRYWAIT: + /* Hand over to state machine if needed */ + if (!port->vbus_present && tcpm_port_is_source(port)) + tcpm_set_state(port, SRC_TRYWAIT_DEBOUNCE, 0); + break; + case SRC_TRYWAIT_DEBOUNCE: + if (port->vbus_present || !tcpm_port_is_source(port)) + tcpm_set_state(port, SRC_TRYWAIT, 0); + break; + case SNK_TRY_WAIT_DEBOUNCE: + if (!tcpm_port_is_sink(port)) { + port->max_wait = 0; + tcpm_set_state(port, SRC_TRYWAIT, 0); + } + break; + case SRC_TRY_WAIT: + if (tcpm_port_is_source(port)) + tcpm_set_state(port, SRC_TRY_DEBOUNCE, 0); + break; + case SRC_TRY_DEBOUNCE: + tcpm_set_state(port, SRC_TRY_WAIT, 0); + break; + case SNK_TRYWAIT_DEBOUNCE: + if (tcpm_port_is_sink(port)) + tcpm_set_state(port, SNK_TRYWAIT_VBUS, 0); + break; + case SNK_TRYWAIT_VBUS: + if (!tcpm_port_is_sink(port)) + tcpm_set_state(port, SNK_TRYWAIT_DEBOUNCE, 0); + break; + case SNK_TRYWAIT: + /* Do nothing, waiting for tCCDebounce */ + break; + case PR_SWAP_SNK_SRC_SINK_OFF: + case PR_SWAP_SRC_SNK_TRANSITION_OFF: + case PR_SWAP_SRC_SNK_SOURCE_OFF: + case PR_SWAP_SRC_SNK_SOURCE_OFF_CC_DEBOUNCED: + case PR_SWAP_SNK_SRC_SOURCE_ON: + /* + * CC state change is expected in PR_SWAP + * Ignore it. + */ + break; + + default: + if (tcpm_port_is_disconnected(port)) + tcpm_set_state(port, unattached_state(port), 0); + break; + } +} + +static void _tcpm_pd_vbus_on(struct tcpm_port *port) +{ + tcpm_log_force(port, "VBUS on"); + port->vbus_present = true; + switch (port->state) { + case SNK_TRANSITION_SINK_VBUS: + port->explicit_contract = true; + tcpm_set_state(port, SNK_READY, 0); + break; + case SNK_DISCOVERY: + tcpm_set_state(port, SNK_DISCOVERY, 0); + break; + + case SNK_DEBOUNCED: + tcpm_set_state(port, tcpm_try_src(port) ? SRC_TRY + : SNK_ATTACHED, + 0); + break; + case SNK_HARD_RESET_WAIT_VBUS: + tcpm_set_state(port, SNK_HARD_RESET_SINK_ON, 0); + break; + case SRC_ATTACHED: + tcpm_set_state(port, SRC_STARTUP, 0); + break; + case SRC_HARD_RESET_VBUS_ON: + tcpm_set_state(port, SRC_STARTUP, 0); + break; + + case SNK_TRY: + /* Do nothing, waiting for timeout */ + break; + case SRC_TRYWAIT: + /* Do nothing, Waiting for Rd to be detected */ + break; + case SRC_TRYWAIT_DEBOUNCE: + tcpm_set_state(port, SRC_TRYWAIT, 0); + break; + case SNK_TRY_WAIT_DEBOUNCE: + /* Do nothing, waiting for PD_DEBOUNCE to do be done */ + break; + case SNK_TRYWAIT: + /* Do nothing, waiting for tCCDebounce */ + break; + case SNK_TRYWAIT_VBUS: + if (tcpm_port_is_sink(port)) + tcpm_set_state(port, SNK_ATTACHED, 0); + break; + case SNK_TRYWAIT_DEBOUNCE: + /* Do nothing, waiting for Rp */ + break; + case SRC_TRY_WAIT: + case SRC_TRY_DEBOUNCE: + /* Do nothing, waiting for sink detection */ + break; + default: + break; + } +} + +static void _tcpm_pd_vbus_off(struct tcpm_port *port) +{ + tcpm_log_force(port, "VBUS off"); + port->vbus_present = false; + port->vbus_never_low = false; + switch (port->state) { + case SNK_HARD_RESET_SINK_OFF: + tcpm_set_state(port, SNK_HARD_RESET_WAIT_VBUS, 0); + break; + case SRC_HARD_RESET_VBUS_OFF: + tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, 0); + break; + case HARD_RESET_SEND: + break; + + case SNK_TRY: + /* Do nothing, waiting for timeout */ + break; + case SRC_TRYWAIT: + /* Hand over to state machine if needed */ + if (tcpm_port_is_source(port)) + tcpm_set_state(port, SRC_TRYWAIT_DEBOUNCE, 0); + break; + case SNK_TRY_WAIT_DEBOUNCE: + /* Do nothing, waiting for PD_DEBOUNCE to do be done */ + break; + case SNK_TRYWAIT: + case SNK_TRYWAIT_VBUS: + case SNK_TRYWAIT_DEBOUNCE: + break; + case SNK_ATTACH_WAIT: + tcpm_set_state(port, SNK_UNATTACHED, 0); + break; + + case SNK_NEGOTIATE_CAPABILITIES: + break; + + case PR_SWAP_SRC_SNK_TRANSITION_OFF: + tcpm_set_state(port, PR_SWAP_SRC_SNK_SOURCE_OFF, 0); + break; + + case PR_SWAP_SNK_SRC_SINK_OFF: + /* Do nothing, expected */ + break; + + case PORT_RESET_WAIT_OFF: + tcpm_set_state(port, tcpm_default_state(port), 0); + break; + case SRC_TRY_WAIT: + case SRC_TRY_DEBOUNCE: + /* Do nothing, waiting for sink detection */ + break; + default: + if (port->pwr_role == TYPEC_SINK && + port->attached) + tcpm_set_state(port, SNK_UNATTACHED, 0); + break; + } +} + +static void _tcpm_pd_hard_reset(struct tcpm_port *port) +{ + tcpm_log_force(port, "Received hard reset"); + /* + * If we keep receiving hard reset requests, executing the hard reset + * must have failed. Revert to error recovery if that happens. + */ + tcpm_set_state(port, + port->hard_reset_count < PD_N_HARD_RESET_COUNT ? + HARD_RESET_START : ERROR_RECOVERY, + 0); +} + +static void tcpm_pd_event_handler(struct work_struct *work) +{ + struct tcpm_port *port = container_of(work, struct tcpm_port, + event_work); + u32 events; + + mutex_lock(&port->lock); + + spin_lock(&port->pd_event_lock); + while (port->pd_events) { + events = port->pd_events; + port->pd_events = 0; + spin_unlock(&port->pd_event_lock); + if (events & TCPM_RESET_EVENT) + _tcpm_pd_hard_reset(port); + if (events & TCPM_VBUS_EVENT) { + bool vbus; + + vbus = port->tcpc->get_vbus(port->tcpc); + if (vbus) + _tcpm_pd_vbus_on(port); + else + _tcpm_pd_vbus_off(port); + } + if (events & TCPM_CC_EVENT) { + enum typec_cc_status cc1, cc2; + + if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0) + _tcpm_cc_change(port, cc1, cc2); + } + spin_lock(&port->pd_event_lock); + } + spin_unlock(&port->pd_event_lock); + mutex_unlock(&port->lock); +} + +void tcpm_cc_change(struct tcpm_port *port) +{ + spin_lock(&port->pd_event_lock); + port->pd_events |= TCPM_CC_EVENT; + spin_unlock(&port->pd_event_lock); + queue_work(port->wq, &port->event_work); +} +EXPORT_SYMBOL_GPL(tcpm_cc_change); + +void tcpm_vbus_change(struct tcpm_port *port) +{ + spin_lock(&port->pd_event_lock); + port->pd_events |= TCPM_VBUS_EVENT; + spin_unlock(&port->pd_event_lock); + queue_work(port->wq, &port->event_work); +} +EXPORT_SYMBOL_GPL(tcpm_vbus_change); + +void tcpm_pd_hard_reset(struct tcpm_port *port) +{ + spin_lock(&port->pd_event_lock); + port->pd_events = TCPM_RESET_EVENT; + spin_unlock(&port->pd_event_lock); + queue_work(port->wq, &port->event_work); +} +EXPORT_SYMBOL_GPL(tcpm_pd_hard_reset); + +static int tcpm_dr_set(const struct typec_capability *cap, + enum typec_data_role data) +{ + struct tcpm_port *port = typec_cap_to_tcpm(cap); + int ret; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (port->port_type != TYPEC_PORT_DRP) { + ret = -EINVAL; + goto port_unlock; + } + if (port->state != SRC_READY && port->state != SNK_READY) { + ret = -EAGAIN; + goto port_unlock; + } + + if (port->data_role == data) { + ret = 0; + goto port_unlock; + } + + /* + * XXX + * 6.3.9: If an alternate mode is active, a request to swap + * alternate modes shall trigger a port reset. + * Reject data role swap request in this case. + */ + + if (!port->pd_capable) { + /* + * If the partner is not PD capable, reset the port to + * trigger a role change. This can only work if a preferred + * role is configured, and if it matches the requested role. + */ + if (port->try_role == TYPEC_NO_PREFERRED_ROLE || + port->try_role == port->pwr_role) { + ret = -EINVAL; + goto port_unlock; + } + port->non_pd_role_swap = true; + tcpm_set_state(port, PORT_RESET, 0); + } else { + tcpm_set_state(port, DR_SWAP_SEND, 0); + } + + port->swap_status = 0; + port->swap_pending = true; + reinit_completion(&port->swap_complete); + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->swap_complete, + msecs_to_jiffies(PD_ROLE_SWAP_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->swap_status; + + port->non_pd_role_swap = false; + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + return ret; +} + +static int tcpm_pr_set(const struct typec_capability *cap, + enum typec_role role) +{ + struct tcpm_port *port = typec_cap_to_tcpm(cap); + int ret; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (port->port_type != TYPEC_PORT_DRP) { + ret = -EINVAL; + goto port_unlock; + } + if (port->state != SRC_READY && port->state != SNK_READY) { + ret = -EAGAIN; + goto port_unlock; + } + + if (role == port->pwr_role) { + ret = 0; + goto port_unlock; + } + + port->swap_status = 0; + port->swap_pending = true; + reinit_completion(&port->swap_complete); + tcpm_set_state(port, PR_SWAP_SEND, 0); + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->swap_complete, + msecs_to_jiffies(PD_ROLE_SWAP_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->swap_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + return ret; +} + +static int tcpm_vconn_set(const struct typec_capability *cap, + enum typec_role role) +{ + struct tcpm_port *port = typec_cap_to_tcpm(cap); + int ret; + + mutex_lock(&port->swap_lock); + mutex_lock(&port->lock); + + if (port->state != SRC_READY && port->state != SNK_READY) { + ret = -EAGAIN; + goto port_unlock; + } + + if (role == port->vconn_role) { + ret = 0; + goto port_unlock; + } + + port->swap_status = 0; + port->swap_pending = true; + reinit_completion(&port->swap_complete); + tcpm_set_state(port, VCONN_SWAP_SEND, 0); + mutex_unlock(&port->lock); + + if (!wait_for_completion_timeout(&port->swap_complete, + msecs_to_jiffies(PD_ROLE_SWAP_TIMEOUT))) + ret = -ETIMEDOUT; + else + ret = port->swap_status; + + goto swap_unlock; + +port_unlock: + mutex_unlock(&port->lock); +swap_unlock: + mutex_unlock(&port->swap_lock); + return ret; +} + +static int tcpm_try_role(const struct typec_capability *cap, int role) +{ + struct tcpm_port *port = typec_cap_to_tcpm(cap); + struct tcpc_dev *tcpc = port->tcpc; + int ret = 0; + + mutex_lock(&port->lock); + if (tcpc->try_role) + ret = tcpc->try_role(tcpc, role); + if (!ret && !tcpc->config->try_role_hw) + port->try_role = role; + port->try_src_count = 0; + port->try_snk_count = 0; + mutex_unlock(&port->lock); + + return ret; +} + +static void tcpm_init(struct tcpm_port *port) +{ + enum typec_cc_status cc1, cc2; + + port->tcpc->init(port->tcpc); + + tcpm_reset_port(port); + + /* + * XXX + * Should possibly wait for VBUS to settle if it was enabled locally + * since tcpm_reset_port() will disable VBUS. + */ + port->vbus_present = port->tcpc->get_vbus(port->tcpc); + if (port->vbus_present) + port->vbus_never_low = true; + + tcpm_set_state(port, tcpm_default_state(port), 0); + + if (port->tcpc->get_cc(port->tcpc, &cc1, &cc2) == 0) + _tcpm_cc_change(port, cc1, cc2); + + /* + * Some adapters need a clean slate at startup, and won't recover + * otherwise. So do not try to be fancy and force a clean disconnect. + */ + tcpm_set_state(port, PORT_RESET, 0); +} + +static int tcpm_port_type_set(const struct typec_capability *cap, + enum typec_port_type type) +{ + struct tcpm_port *port = typec_cap_to_tcpm(cap); + + mutex_lock(&port->lock); + if (type == port->port_type) + goto port_unlock; + + port->port_type = type; + + if (!port->connected) { + tcpm_set_state(port, PORT_RESET, 0); + } else if (type == TYPEC_PORT_UFP) { + if (!(port->pwr_role == TYPEC_SINK && + port->data_role == TYPEC_DEVICE)) + tcpm_set_state(port, PORT_RESET, 0); + } else if (type == TYPEC_PORT_DFP) { + if (!(port->pwr_role == TYPEC_SOURCE && + port->data_role == TYPEC_HOST)) + tcpm_set_state(port, PORT_RESET, 0); + } + +port_unlock: + mutex_unlock(&port->lock); + return 0; +} + +void tcpm_tcpc_reset(struct tcpm_port *port) +{ + mutex_lock(&port->lock); + /* XXX: Maintain PD connection if possible? */ + tcpm_init(port); + mutex_unlock(&port->lock); +} +EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); + +static int tcpm_copy_pdos(u32 *dest_pdo, const u32 *src_pdo, + unsigned int nr_pdo) +{ + unsigned int i; + + if (nr_pdo > PDO_MAX_OBJECTS) + nr_pdo = PDO_MAX_OBJECTS; + + for (i = 0; i < nr_pdo; i++) + dest_pdo[i] = src_pdo[i]; + + return nr_pdo; +} + +static int tcpm_copy_vdos(u32 *dest_vdo, const u32 *src_vdo, + unsigned int nr_vdo) +{ + unsigned int i; + + if (nr_vdo > VDO_MAX_OBJECTS) + nr_vdo = VDO_MAX_OBJECTS; + + for (i = 0; i < nr_vdo; i++) + dest_vdo[i] = src_vdo[i]; + + return nr_vdo; +} + +void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo) +{ + mutex_lock(&port->lock); + port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, pdo, nr_pdo); + switch (port->state) { + case SRC_UNATTACHED: + case SRC_ATTACH_WAIT: + case SRC_TRYWAIT: + tcpm_set_cc(port, tcpm_rp_cc(port)); + break; + case SRC_SEND_CAPABILITIES: + case SRC_NEGOTIATE_CAPABILITIES: + case SRC_READY: + case SRC_WAIT_NEW_CAPABILITIES: + tcpm_set_cc(port, tcpm_rp_cc(port)); + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); + break; + default: + break; + } + mutex_unlock(&port->lock); +} +EXPORT_SYMBOL_GPL(tcpm_update_source_capabilities); + +void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo, + unsigned int max_snk_mv, + unsigned int max_snk_ma, + unsigned int max_snk_mw, + unsigned int operating_snk_mw) +{ + mutex_lock(&port->lock); + port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, pdo, nr_pdo); + port->max_snk_mv = max_snk_mv; + port->max_snk_ma = max_snk_ma; + port->max_snk_mw = max_snk_mw; + port->operating_snk_mw = operating_snk_mw; + + switch (port->state) { + case SNK_NEGOTIATE_CAPABILITIES: + case SNK_READY: + case SNK_TRANSITION_SINK: + case SNK_TRANSITION_SINK_VBUS: + tcpm_set_state(port, SNK_NEGOTIATE_CAPABILITIES, 0); + break; + default: + break; + } + mutex_unlock(&port->lock); +} +EXPORT_SYMBOL_GPL(tcpm_update_sink_capabilities); + +struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) +{ + struct tcpm_port *port; + int i, err; + + if (!dev || !tcpc || !tcpc->config || + !tcpc->get_vbus || !tcpc->set_cc || !tcpc->get_cc || + !tcpc->set_polarity || !tcpc->set_vconn || !tcpc->set_vbus || + !tcpc->set_pd_rx || !tcpc->set_roles || !tcpc->pd_transmit) + return ERR_PTR(-EINVAL); + + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return ERR_PTR(-ENOMEM); + + port->dev = dev; + port->tcpc = tcpc; + + mutex_init(&port->lock); + mutex_init(&port->swap_lock); + + port->wq = create_singlethread_workqueue(dev_name(dev)); + if (!port->wq) + return ERR_PTR(-ENOMEM); + INIT_DELAYED_WORK(&port->state_machine, tcpm_state_machine_work); + INIT_DELAYED_WORK(&port->vdm_state_machine, vdm_state_machine_work); + INIT_WORK(&port->event_work, tcpm_pd_event_handler); + + spin_lock_init(&port->pd_event_lock); + + init_completion(&port->tx_complete); + init_completion(&port->swap_complete); + + port->nr_src_pdo = tcpm_copy_pdos(port->src_pdo, tcpc->config->src_pdo, + tcpc->config->nr_src_pdo); + port->nr_snk_pdo = tcpm_copy_pdos(port->snk_pdo, tcpc->config->snk_pdo, + tcpc->config->nr_snk_pdo); + port->nr_snk_vdo = tcpm_copy_vdos(port->snk_vdo, tcpc->config->snk_vdo, + tcpc->config->nr_snk_vdo); + + port->max_snk_mv = tcpc->config->max_snk_mv; + port->max_snk_ma = tcpc->config->max_snk_ma; + port->max_snk_mw = tcpc->config->max_snk_mw; + port->operating_snk_mw = tcpc->config->operating_snk_mw; + if (!tcpc->config->try_role_hw) + port->try_role = tcpc->config->default_role; + else + port->try_role = TYPEC_NO_PREFERRED_ROLE; + + port->typec_caps.prefer_role = tcpc->config->default_role; + port->typec_caps.type = tcpc->config->type; + port->typec_caps.revision = 0x0120; /* Type-C spec release 1.2 */ + port->typec_caps.pd_revision = 0x0200; /* USB-PD spec release 2.0 */ + port->typec_caps.dr_set = tcpm_dr_set; + port->typec_caps.pr_set = tcpm_pr_set; + port->typec_caps.vconn_set = tcpm_vconn_set; + port->typec_caps.try_role = tcpm_try_role; + port->typec_caps.port_type_set = tcpm_port_type_set; + + port->partner_desc.identity = &port->partner_ident; + port->port_type = tcpc->config->type; + + port->typec_port = typec_register_port(port->dev, &port->typec_caps); + if (!port->typec_port) { + err = -ENOMEM; + goto out_destroy_wq; + } + + if (tcpc->config->alt_modes) { + const struct typec_altmode_desc *paltmode = tcpc->config->alt_modes; + + i = 0; + while (paltmode->svid && i < ARRAY_SIZE(port->port_altmode)) { + port->port_altmode[i] = + typec_port_register_altmode(port->typec_port, + paltmode); + if (!port->port_altmode[i]) { + tcpm_log(port, + "%s: failed to register port alternate mode 0x%x", + dev_name(dev), paltmode->svid); + break; + } + i++; + paltmode++; + } + } + + tcpm_debugfs_init(port); + mutex_lock(&port->lock); + tcpm_init(port); + mutex_unlock(&port->lock); + + tcpm_log(port, "%s: registered", dev_name(dev)); + return port; + +out_destroy_wq: + destroy_workqueue(port->wq); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(tcpm_register_port); + +void tcpm_unregister_port(struct tcpm_port *port) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++) + typec_unregister_altmode(port->port_altmode[i]); + typec_unregister_port(port->typec_port); + tcpm_debugfs_exit(port); + destroy_workqueue(port->wq); +} +EXPORT_SYMBOL_GPL(tcpm_unregister_port); + +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("USB Type-C Port Manager"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h new file mode 100644 index 000000000000..e00051ced806 --- /dev/null +++ b/include/linux/usb/pd.h @@ -0,0 +1,298 @@ +/* + * Copyright 2015-2017 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __LINUX_USB_PD_H +#define __LINUX_USB_PD_H + +#include +#include + +/* USB PD Messages */ +enum pd_ctrl_msg_type { + /* 0 Reserved */ + PD_CTRL_GOOD_CRC = 1, + PD_CTRL_GOTO_MIN = 2, + PD_CTRL_ACCEPT = 3, + PD_CTRL_REJECT = 4, + PD_CTRL_PING = 5, + PD_CTRL_PS_RDY = 6, + PD_CTRL_GET_SOURCE_CAP = 7, + PD_CTRL_GET_SINK_CAP = 8, + PD_CTRL_DR_SWAP = 9, + PD_CTRL_PR_SWAP = 10, + PD_CTRL_VCONN_SWAP = 11, + PD_CTRL_WAIT = 12, + PD_CTRL_SOFT_RESET = 13, + /* 14-15 Reserved */ +}; + +enum pd_data_msg_type { + /* 0 Reserved */ + PD_DATA_SOURCE_CAP = 1, + PD_DATA_REQUEST = 2, + PD_DATA_BIST = 3, + PD_DATA_SINK_CAP = 4, + /* 5-14 Reserved */ + PD_DATA_VENDOR_DEF = 15, +}; + +#define PD_REV10 0x0 +#define PD_REV20 0x1 + +#define PD_HEADER_CNT_SHIFT 12 +#define PD_HEADER_CNT_MASK 0x7 +#define PD_HEADER_ID_SHIFT 9 +#define PD_HEADER_ID_MASK 0x7 +#define PD_HEADER_PWR_ROLE BIT(8) +#define PD_HEADER_REV_SHIFT 6 +#define PD_HEADER_REV_MASK 0x3 +#define PD_HEADER_DATA_ROLE BIT(5) +#define PD_HEADER_TYPE_SHIFT 0 +#define PD_HEADER_TYPE_MASK 0xf + +#define PD_HEADER(type, pwr, data, id, cnt) \ + ((((type) & PD_HEADER_TYPE_MASK) << PD_HEADER_TYPE_SHIFT) | \ + ((pwr) == TYPEC_SOURCE ? PD_HEADER_PWR_ROLE : 0) | \ + ((data) == TYPEC_HOST ? PD_HEADER_DATA_ROLE : 0) | \ + (PD_REV20 << PD_HEADER_REV_SHIFT) | \ + (((id) & PD_HEADER_ID_MASK) << PD_HEADER_ID_SHIFT) | \ + (((cnt) & PD_HEADER_CNT_MASK) << PD_HEADER_CNT_SHIFT)) + +#define PD_HEADER_LE(type, pwr, data, id, cnt) \ + cpu_to_le16(PD_HEADER((type), (pwr), (data), (id), (cnt))) + +static inline unsigned int pd_header_cnt(u16 header) +{ + return (header >> PD_HEADER_CNT_SHIFT) & PD_HEADER_CNT_MASK; +} + +static inline unsigned int pd_header_cnt_le(__le16 header) +{ + return pd_header_cnt(le16_to_cpu(header)); +} + +static inline unsigned int pd_header_type(u16 header) +{ + return (header >> PD_HEADER_TYPE_SHIFT) & PD_HEADER_TYPE_MASK; +} + +static inline unsigned int pd_header_type_le(__le16 header) +{ + return pd_header_type(le16_to_cpu(header)); +} + +static inline unsigned int pd_header_msgid(u16 header) +{ + return (header >> PD_HEADER_ID_SHIFT) & PD_HEADER_ID_MASK; +} + +static inline unsigned int pd_header_msgid_le(__le16 header) +{ + return pd_header_msgid(le16_to_cpu(header)); +} + +#define PD_MAX_PAYLOAD 7 + +/** + * struct pd_message - PD message as seen on wire + * @header: PD message header + * @payload: PD message payload + */ +struct pd_message { + __le16 header; + __le32 payload[PD_MAX_PAYLOAD]; +} __packed; + +/* PDO: Power Data Object */ +#define PDO_MAX_OBJECTS 7 + +enum pd_pdo_type { + PDO_TYPE_FIXED = 0, + PDO_TYPE_BATT = 1, + PDO_TYPE_VAR = 2, +}; + +#define PDO_TYPE_SHIFT 30 +#define PDO_TYPE_MASK 0x3 + +#define PDO_TYPE(t) ((t) << PDO_TYPE_SHIFT) + +#define PDO_VOLT_MASK 0x3ff +#define PDO_CURR_MASK 0x3ff +#define PDO_PWR_MASK 0x3ff + +#define PDO_FIXED_DUAL_ROLE BIT(29) /* Power role swap supported */ +#define PDO_FIXED_SUSPEND BIT(28) /* USB Suspend supported (Source) */ +#define PDO_FIXED_HIGHER_CAP BIT(28) /* Requires more than vSafe5V (Sink) */ +#define PDO_FIXED_EXTPOWER BIT(27) /* Externally powered */ +#define PDO_FIXED_USB_COMM BIT(26) /* USB communications capable */ +#define PDO_FIXED_DATA_SWAP BIT(25) /* Data role swap supported */ +#define PDO_FIXED_VOLT_SHIFT 10 /* 50mV units */ +#define PDO_FIXED_CURR_SHIFT 0 /* 10mA units */ + +#define PDO_FIXED_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_FIXED_VOLT_SHIFT) +#define PDO_FIXED_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_FIXED_CURR_SHIFT) + +#define PDO_FIXED(mv, ma, flags) \ + (PDO_TYPE(PDO_TYPE_FIXED) | (flags) | \ + PDO_FIXED_VOLT(mv) | PDO_FIXED_CURR(ma)) + +#define PDO_BATT_MAX_VOLT_SHIFT 20 /* 50mV units */ +#define PDO_BATT_MIN_VOLT_SHIFT 10 /* 50mV units */ +#define PDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */ + +#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MIN_VOLT_SHIFT) +#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_BATT_MAX_VOLT_SHIFT) +#define PDO_BATT_MAX_POWER(mw) ((((mw) / 250) & PDO_PWR_MASK) << PDO_BATT_MAX_PWR_SHIFT) + +#define PDO_BATT(min_mv, max_mv, max_mw) \ + (PDO_TYPE(PDO_TYPE_BATT) | PDO_BATT_MIN_VOLT(min_mv) | \ + PDO_BATT_MAX_VOLT(max_mv) | PDO_BATT_MAX_POWER(max_mw)) + +#define PDO_VAR_MAX_VOLT_SHIFT 20 /* 50mV units */ +#define PDO_VAR_MIN_VOLT_SHIFT 10 /* 50mV units */ +#define PDO_VAR_MAX_CURR_SHIFT 0 /* 10mA units */ + +#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MIN_VOLT_SHIFT) +#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & PDO_VOLT_MASK) << PDO_VAR_MAX_VOLT_SHIFT) +#define PDO_VAR_MAX_CURR(ma) ((((ma) / 10) & PDO_CURR_MASK) << PDO_VAR_MAX_CURR_SHIFT) + +#define PDO_VAR(min_mv, max_mv, max_ma) \ + (PDO_TYPE(PDO_TYPE_VAR) | PDO_VAR_MIN_VOLT(min_mv) | \ + PDO_VAR_MAX_VOLT(max_mv) | PDO_VAR_MAX_CURR(max_ma)) + +static inline enum pd_pdo_type pdo_type(u32 pdo) +{ + return (pdo >> PDO_TYPE_SHIFT) & PDO_TYPE_MASK; +} + +static inline unsigned int pdo_fixed_voltage(u32 pdo) +{ + return ((pdo >> PDO_FIXED_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; +} + +static inline unsigned int pdo_min_voltage(u32 pdo) +{ + return ((pdo >> PDO_VAR_MIN_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; +} + +static inline unsigned int pdo_max_voltage(u32 pdo) +{ + return ((pdo >> PDO_VAR_MAX_VOLT_SHIFT) & PDO_VOLT_MASK) * 50; +} + +static inline unsigned int pdo_max_current(u32 pdo) +{ + return ((pdo >> PDO_VAR_MAX_CURR_SHIFT) & PDO_CURR_MASK) * 10; +} + +static inline unsigned int pdo_max_power(u32 pdo) +{ + return ((pdo >> PDO_BATT_MAX_PWR_SHIFT) & PDO_PWR_MASK) * 250; +} + +/* RDO: Request Data Object */ +#define RDO_OBJ_POS_SHIFT 28 +#define RDO_OBJ_POS_MASK 0x7 +#define RDO_GIVE_BACK BIT(27) /* Supports reduced operating current */ +#define RDO_CAP_MISMATCH BIT(26) /* Not satisfied by source caps */ +#define RDO_USB_COMM BIT(25) /* USB communications capable */ +#define RDO_NO_SUSPEND BIT(24) /* USB Suspend not supported */ + +#define RDO_PWR_MASK 0x3ff +#define RDO_CURR_MASK 0x3ff + +#define RDO_FIXED_OP_CURR_SHIFT 10 +#define RDO_FIXED_MAX_CURR_SHIFT 0 + +#define RDO_OBJ(idx) (((idx) & RDO_OBJ_POS_MASK) << RDO_OBJ_POS_SHIFT) + +#define PDO_FIXED_OP_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_OP_CURR_SHIFT) +#define PDO_FIXED_MAX_CURR(ma) ((((ma) / 10) & RDO_CURR_MASK) << RDO_FIXED_MAX_CURR_SHIFT) + +#define RDO_FIXED(idx, op_ma, max_ma, flags) \ + (RDO_OBJ(idx) | (flags) | \ + PDO_FIXED_OP_CURR(op_ma) | PDO_FIXED_MAX_CURR(max_ma)) + +#define RDO_BATT_OP_PWR_SHIFT 10 /* 250mW units */ +#define RDO_BATT_MAX_PWR_SHIFT 0 /* 250mW units */ + +#define RDO_BATT_OP_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_OP_PWR_SHIFT) +#define RDO_BATT_MAX_PWR(mw) ((((mw) / 250) & RDO_PWR_MASK) << RDO_BATT_MAX_PWR_SHIFT) + +#define RDO_BATT(idx, op_mw, max_mw, flags) \ + (RDO_OBJ(idx) | (flags) | \ + RDO_BATT_OP_PWR(op_mw) | RDO_BATT_MAX_PWR(max_mw)) + +static inline unsigned int rdo_index(u32 rdo) +{ + return (rdo >> RDO_OBJ_POS_SHIFT) & RDO_OBJ_POS_MASK; +} + +static inline unsigned int rdo_op_current(u32 rdo) +{ + return ((rdo >> RDO_FIXED_OP_CURR_SHIFT) & RDO_CURR_MASK) * 10; +} + +static inline unsigned int rdo_max_current(u32 rdo) +{ + return ((rdo >> RDO_FIXED_MAX_CURR_SHIFT) & + RDO_CURR_MASK) * 10; +} + +static inline unsigned int rdo_op_power(u32 rdo) +{ + return ((rdo >> RDO_BATT_OP_PWR_SHIFT) & RDO_PWR_MASK) * 250; +} + +static inline unsigned int rdo_max_power(u32 rdo) +{ + return ((rdo >> RDO_BATT_MAX_PWR_SHIFT) & RDO_PWR_MASK) * 250; +} + +/* USB PD timers and counters */ +#define PD_T_NO_RESPONSE 5000 /* 4.5 - 5.5 seconds */ +#define PD_T_DB_DETECT 10000 /* 10 - 15 seconds */ +#define PD_T_SEND_SOURCE_CAP 150 /* 100 - 200 ms */ +#define PD_T_SENDER_RESPONSE 60 /* 24 - 30 ms, relaxed */ +#define PD_T_SOURCE_ACTIVITY 45 +#define PD_T_SINK_ACTIVITY 135 +#define PD_T_SINK_WAIT_CAP 240 +#define PD_T_PS_TRANSITION 500 +#define PD_T_SRC_TRANSITION 35 +#define PD_T_DRP_SNK 40 +#define PD_T_DRP_SRC 30 +#define PD_T_PS_SOURCE_OFF 920 +#define PD_T_PS_SOURCE_ON 480 +#define PD_T_PS_HARD_RESET 30 +#define PD_T_SRC_RECOVER 760 +#define PD_T_SRC_RECOVER_MAX 1000 +#define PD_T_SRC_TURN_ON 275 +#define PD_T_SAFE_0V 650 +#define PD_T_VCONN_SOURCE_ON 100 +#define PD_T_SINK_REQUEST 100 /* 100 ms minimum */ +#define PD_T_ERROR_RECOVERY 100 /* minimum 25 is insufficient */ +#define PD_T_SRCSWAPSTDBY 625 /* Maximum of 650ms */ +#define PD_T_NEWSRC 250 /* Maximum of 275ms */ + +#define PD_T_DRP_TRY 100 /* 75 - 150 ms */ +#define PD_T_DRP_TRYWAIT 600 /* 400 - 800 ms */ + +#define PD_T_CC_DEBOUNCE 200 /* 100 - 200 ms */ +#define PD_T_PD_DEBOUNCE 20 /* 10 - 20 ms */ + +#define PD_N_CAPS_COUNT (PD_T_NO_RESPONSE / PD_T_SEND_SOURCE_CAP) +#define PD_N_HARD_RESET_COUNT 2 + +#endif /* __LINUX_USB_PD_H */ diff --git a/include/linux/usb/pd_bdo.h b/include/linux/usb/pd_bdo.h new file mode 100644 index 000000000000..90b94d9fea5d --- /dev/null +++ b/include/linux/usb/pd_bdo.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015-2017 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __LINUX_USB_PD_BDO_H +#define __LINUX_USB_PD_BDO_H + +/* BDO : BIST Data Object */ +#define BDO_MODE_RECV (0 << 28) +#define BDO_MODE_TRANSMIT (1 << 28) +#define BDO_MODE_COUNTERS (2 << 28) +#define BDO_MODE_CARRIER0 (3 << 28) +#define BDO_MODE_CARRIER1 (4 << 28) +#define BDO_MODE_CARRIER2 (5 << 28) +#define BDO_MODE_CARRIER3 (6 << 28) +#define BDO_MODE_EYE (7 << 28) +#define BDO_MODE_TESTDATA (8 << 28) + +#define BDO_MODE_MASK(mode) ((mode) & 0xf0000000) + +#endif diff --git a/include/linux/usb/pd_vdo.h b/include/linux/usb/pd_vdo.h new file mode 100644 index 000000000000..d92259f8de0a --- /dev/null +++ b/include/linux/usb/pd_vdo.h @@ -0,0 +1,251 @@ +/* + * Copyright 2015-2017 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __LINUX_USB_PD_VDO_H +#define __LINUX_USB_PD_VDO_H + +#include "pd.h" + +/* + * VDO : Vendor Defined Message Object + * VDM object is minimum of VDM header + 6 additional data objects. + */ + +#define VDO_MAX_OBJECTS 6 +#define VDO_MAX_SIZE (VDO_MAX_OBJECTS + 1) + +/* + * VDM header + * ---------- + * <31:16> :: SVID + * <15> :: VDM type ( 1b == structured, 0b == unstructured ) + * <14:13> :: Structured VDM version (can only be 00 == 1.0 currently) + * <12:11> :: reserved + * <10:8> :: object position (1-7 valid ... used for enter/exit mode only) + * <7:6> :: command type (SVDM only?) + * <5> :: reserved (SVDM), command type (UVDM) + * <4:0> :: command + */ +#define VDO(vid, type, custom) \ + (((vid) << 16) | \ + ((type) << 15) | \ + ((custom) & 0x7FFF)) + +#define VDO_SVDM_TYPE (1 << 15) +#define VDO_SVDM_VERS(x) ((x) << 13) +#define VDO_OPOS(x) ((x) << 8) +#define VDO_CMDT(x) ((x) << 6) +#define VDO_OPOS_MASK VDO_OPOS(0x7) +#define VDO_CMDT_MASK VDO_CMDT(0x3) + +#define CMDT_INIT 0 +#define CMDT_RSP_ACK 1 +#define CMDT_RSP_NAK 2 +#define CMDT_RSP_BUSY 3 + +/* reserved for SVDM ... for Google UVDM */ +#define VDO_SRC_INITIATOR (0 << 5) +#define VDO_SRC_RESPONDER (1 << 5) + +#define CMD_DISCOVER_IDENT 1 +#define CMD_DISCOVER_SVID 2 +#define CMD_DISCOVER_MODES 3 +#define CMD_ENTER_MODE 4 +#define CMD_EXIT_MODE 5 +#define CMD_ATTENTION 6 + +#define VDO_CMD_VENDOR(x) (((10 + (x)) & 0x1f)) + +/* ChromeOS specific commands */ +#define VDO_CMD_VERSION VDO_CMD_VENDOR(0) +#define VDO_CMD_SEND_INFO VDO_CMD_VENDOR(1) +#define VDO_CMD_READ_INFO VDO_CMD_VENDOR(2) +#define VDO_CMD_REBOOT VDO_CMD_VENDOR(5) +#define VDO_CMD_FLASH_ERASE VDO_CMD_VENDOR(6) +#define VDO_CMD_FLASH_WRITE VDO_CMD_VENDOR(7) +#define VDO_CMD_ERASE_SIG VDO_CMD_VENDOR(8) +#define VDO_CMD_PING_ENABLE VDO_CMD_VENDOR(10) +#define VDO_CMD_CURRENT VDO_CMD_VENDOR(11) +#define VDO_CMD_FLIP VDO_CMD_VENDOR(12) +#define VDO_CMD_GET_LOG VDO_CMD_VENDOR(13) +#define VDO_CMD_CCD_EN VDO_CMD_VENDOR(14) + +#define PD_VDO_VID(vdo) ((vdo) >> 16) +#define PD_VDO_SVDM(vdo) (((vdo) >> 15) & 1) +#define PD_VDO_OPOS(vdo) (((vdo) >> 8) & 0x7) +#define PD_VDO_CMD(vdo) ((vdo) & 0x1f) +#define PD_VDO_CMDT(vdo) (((vdo) >> 6) & 0x3) + +/* + * SVDM Identity request -> response + * + * Request is simply properly formatted SVDM header + * + * Response is 4 data objects: + * [0] :: SVDM header + * [1] :: Identitiy header + * [2] :: Cert Stat VDO + * [3] :: (Product | Cable) VDO + * [4] :: AMA VDO + * + */ +#define VDO_INDEX_HDR 0 +#define VDO_INDEX_IDH 1 +#define VDO_INDEX_CSTAT 2 +#define VDO_INDEX_CABLE 3 +#define VDO_INDEX_PRODUCT 3 +#define VDO_INDEX_AMA 4 + +/* + * SVDM Identity Header + * -------------------- + * <31> :: data capable as a USB host + * <30> :: data capable as a USB device + * <29:27> :: product type + * <26> :: modal operation supported (1b == yes) + * <25:16> :: Reserved, Shall be set to zero + * <15:0> :: USB-IF assigned VID for this cable vendor + */ +#define IDH_PTYPE_UNDEF 0 +#define IDH_PTYPE_HUB 1 +#define IDH_PTYPE_PERIPH 2 +#define IDH_PTYPE_PCABLE 3 +#define IDH_PTYPE_ACABLE 4 +#define IDH_PTYPE_AMA 5 + +#define VDO_IDH(usbh, usbd, ptype, is_modal, vid) \ + ((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \ + | (is_modal) << 26 | ((vid) & 0xffff)) + +#define PD_IDH_PTYPE(vdo) (((vdo) >> 27) & 0x7) +#define PD_IDH_VID(vdo) ((vdo) & 0xffff) +#define PD_IDH_MODAL_SUPP(vdo) ((vdo) & (1 << 26)) + +/* + * Cert Stat VDO + * ------------- + * <31:0> : USB-IF assigned XID for this cable + */ +#define PD_CSTAT_XID(vdo) (vdo) + +/* + * Product VDO + * ----------- + * <31:16> : USB Product ID + * <15:0> : USB bcdDevice + */ +#define VDO_PRODUCT(pid, bcd) (((pid) & 0xffff) << 16 | ((bcd) & 0xffff)) +#define PD_PRODUCT_PID(vdo) (((vdo) >> 16) & 0xffff) + +/* + * Cable VDO + * --------- + * <31:28> :: Cable HW version + * <27:24> :: Cable FW version + * <23:20> :: Reserved, Shall be set to zero + * <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C) + * <17> :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle) + * <16:13> :: cable latency (0001 == <10ns(~1m length)) + * <12:11> :: cable termination type (11b == both ends active VCONN req) + * <10> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable) + * <9> :: SSTX2 Directionality support + * <8> :: SSRX1 Directionality support + * <7> :: SSRX2 Directionality support + * <6:5> :: Vbus current handling capability + * <4> :: Vbus through cable (0b == no, 1b == yes) + * <3> :: SOP" controller present? (0b == no, 1b == yes) + * <2:0> :: USB SS Signaling support + */ +#define CABLE_ATYPE 0 +#define CABLE_BTYPE 1 +#define CABLE_CTYPE 2 +#define CABLE_PLUG 0 +#define CABLE_RECEPTACLE 1 +#define CABLE_CURR_1A5 0 +#define CABLE_CURR_3A 1 +#define CABLE_CURR_5A 2 +#define CABLE_USBSS_U2_ONLY 0 +#define CABLE_USBSS_U31_GEN1 1 +#define CABLE_USBSS_U31_GEN2 2 +#define VDO_CABLE(hw, fw, cbl, gdr, lat, term, tx1d, tx2d, rx1d, rx2d, cur,\ + vps, sopp, usbss) \ + (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18 \ + | (gdr) << 17 | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11 \ + | (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 \ + | ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3 \ + | ((usbss) & 0x7)) + +/* + * AMA VDO + * --------- + * <31:28> :: Cable HW version + * <27:24> :: Cable FW version + * <23:12> :: Reserved, Shall be set to zero + * <11> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable) + * <10> :: SSTX2 Directionality support + * <9> :: SSRX1 Directionality support + * <8> :: SSRX2 Directionality support + * <7:5> :: Vconn power + * <4> :: Vconn power required + * <3> :: Vbus power required + * <2:0> :: USB SS Signaling support + */ +#define VDO_AMA(hw, fw, tx1d, tx2d, rx1d, rx2d, vcpwr, vcr, vbr, usbss) \ + (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 \ + | (tx1d) << 11 | (tx2d) << 10 | (rx1d) << 9 | (rx2d) << 8 \ + | ((vcpwr) & 0x7) << 5 | (vcr) << 4 | (vbr) << 3 \ + | ((usbss) & 0x7)) + +#define PD_VDO_AMA_VCONN_REQ(vdo) (((vdo) >> 4) & 1) +#define PD_VDO_AMA_VBUS_REQ(vdo) (((vdo) >> 3) & 1) + +#define AMA_VCONN_PWR_1W 0 +#define AMA_VCONN_PWR_1W5 1 +#define AMA_VCONN_PWR_2W 2 +#define AMA_VCONN_PWR_3W 3 +#define AMA_VCONN_PWR_4W 4 +#define AMA_VCONN_PWR_5W 5 +#define AMA_VCONN_PWR_6W 6 +#define AMA_USBSS_U2_ONLY 0 +#define AMA_USBSS_U31_GEN1 1 +#define AMA_USBSS_U31_GEN2 2 +#define AMA_USBSS_BBONLY 3 + +/* + * SVDM Discover SVIDs request -> response + * + * Request is properly formatted VDM Header with discover SVIDs command. + * Response is a set of SVIDs of all all supported SVIDs with all zero's to + * mark the end of SVIDs. If more than 12 SVIDs are supported command SHOULD be + * repeated. + */ +#define VDO_SVID(svid0, svid1) (((svid0) & 0xffff) << 16 | ((svid1) & 0xffff)) +#define PD_VDO_SVID_SVID0(vdo) ((vdo) >> 16) +#define PD_VDO_SVID_SVID1(vdo) ((vdo) & 0xffff) + +/* USB-IF SIDs */ +#define USB_SID_PD 0xff00 /* power delivery */ +#define USB_SID_DISPLAYPORT 0xff01 +#define USB_SID_MHL 0xff02 /* Mobile High-Definition Link */ + +/* VDM command timeouts (in ms) */ + +#define PD_T_VDM_UNSTRUCTURED 500 +#define PD_T_VDM_BUSY 100 +#define PD_T_VDM_WAIT_MODE_E 100 +#define PD_T_VDM_SNDR_RSP 30 +#define PD_T_VDM_E_MODE 25 +#define PD_T_VDM_RCVR_RSP 15 + +#endif /* __LINUX_USB_PD_VDO_H */ diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h new file mode 100644 index 000000000000..073197f0d2bb --- /dev/null +++ b/include/linux/usb/tcpm.h @@ -0,0 +1,204 @@ +/* + * Copyright 2015-2017 Google, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __LINUX_USB_TCPM_H +#define __LINUX_USB_TCPM_H + +#include +#include +#include "pd.h" + +enum typec_cc_status { + TYPEC_CC_OPEN, + TYPEC_CC_RA, + TYPEC_CC_RD, + TYPEC_CC_RP_DEF, + TYPEC_CC_RP_1_5, + TYPEC_CC_RP_3_0, +}; + +enum typec_cc_polarity { + TYPEC_POLARITY_CC1, + TYPEC_POLARITY_CC2, +}; + +/* Time to wait for TCPC to complete transmit */ +#define PD_T_TCPC_TX_TIMEOUT 100 /* in ms */ +#define PD_ROLE_SWAP_TIMEOUT (MSEC_PER_SEC * 10) + +enum tcpm_transmit_status { + TCPC_TX_SUCCESS = 0, + TCPC_TX_DISCARDED = 1, + TCPC_TX_FAILED = 2, +}; + +enum tcpm_transmit_type { + TCPC_TX_SOP = 0, + TCPC_TX_SOP_PRIME = 1, + TCPC_TX_SOP_PRIME_PRIME = 2, + TCPC_TX_SOP_DEBUG_PRIME = 3, + TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4, + TCPC_TX_HARD_RESET = 5, + TCPC_TX_CABLE_RESET = 6, + TCPC_TX_BIST_MODE_2 = 7 +}; + +/** + * struct tcpc_config - Port configuration + * @src_pdo: PDO parameters sent to port partner as response to + * PD_CTRL_GET_SOURCE_CAP message + * @nr_src_pdo: Number of entries in @src_pdo + * @snk_pdo: PDO parameters sent to partner as response to + * PD_CTRL_GET_SINK_CAP message + * @nr_snk_pdo: Number of entries in @snk_pdo + * @max_snk_mv: Maximum acceptable sink voltage in mV + * @max_snk_ma: Maximum sink current in mA + * @max_snk_mw: Maximum required sink power in mW + * @operating_snk_mw: + * Required operating sink power in mW + * @type: Port type (TYPEC_PORT_DFP, TYPEC_PORT_UFP, or + * TYPEC_PORT_DRP) + * @default_role: + * Default port role (TYPEC_SINK or TYPEC_SOURCE). + * Set to TYPEC_NO_PREFERRED_ROLE if no default role. + * @try_role_hw:True if try.{Src,Snk} is implemented in hardware + * @alt_modes: List of supported alternate modes + */ +struct tcpc_config { + const u32 *src_pdo; + unsigned int nr_src_pdo; + + const u32 *snk_pdo; + unsigned int nr_snk_pdo; + + const u32 *snk_vdo; + unsigned int nr_snk_vdo; + + unsigned int max_snk_mv; + unsigned int max_snk_ma; + unsigned int max_snk_mw; + unsigned int operating_snk_mw; + + enum typec_port_type type; + enum typec_role default_role; + bool try_role_hw; /* try.{src,snk} implemented in hardware */ + + const struct typec_altmode_desc *alt_modes; +}; + +enum tcpc_usb_switch { + TCPC_USB_SWITCH_CONNECT, + TCPC_USB_SWITCH_DISCONNECT, +}; + +/* Mux state attributes */ +#define TCPC_MUX_USB_ENABLED BIT(0) /* USB enabled */ +#define TCPC_MUX_DP_ENABLED BIT(1) /* DP enabled */ +#define TCPC_MUX_POLARITY_INVERTED BIT(2) /* Polarity inverted */ + +/* Mux modes, decoded to attributes */ +enum tcpc_mux_mode { + TYPEC_MUX_NONE = 0, /* Open switch */ + TYPEC_MUX_USB = TCPC_MUX_USB_ENABLED, /* USB only */ + TYPEC_MUX_DP = TCPC_MUX_DP_ENABLED, /* DP only */ + TYPEC_MUX_DOCK = TCPC_MUX_USB_ENABLED | /* Both USB and DP */ + TCPC_MUX_DP_ENABLED, +}; + +struct tcpc_mux_dev { + int (*set)(struct tcpc_mux_dev *dev, enum tcpc_mux_mode mux_mode, + enum tcpc_usb_switch usb_config, + enum typec_cc_polarity polarity); + bool dfp_only; + void *priv_data; +}; + +/** + * struct tcpc_dev - Port configuration and callback functions + * @config: Pointer to port configuration + * @get_vbus: Called to read current VBUS state + * @get_current_limit: + * Optional; called by the tcpm core when configured as a snk + * and cc=Rp-def. This allows the tcpm to provide a fallback + * current-limit detection method for the cc=Rp-def case. + * For example, some tcpcs may include BC1.2 charger detection + * and use that in this case. + * @set_cc: Called to set value of CC pins + * @get_cc: Called to read current CC pin values + * @set_polarity: + * Called to set polarity + * @set_vconn: Called to enable or disable VCONN + * @set_vbus: Called to enable or disable VBUS + * @set_current_limit: + * Optional; called to set current limit as negotiated + * with partner. + * @set_pd_rx: Called to enable or disable reception of PD messages + * @set_roles: Called to set power and data roles + * @start_drp_toggling: + * Optional; if supported by hardware, called to start DRP + * toggling. DRP toggling is stopped automatically if + * a connection is established. + * @try_role: Optional; called to set a preferred role + * @pd_transmit:Called to transmit PD message + * @mux: Pointer to multiplexer data + */ +struct tcpc_dev { + const struct tcpc_config *config; + + int (*init)(struct tcpc_dev *dev); + int (*get_vbus)(struct tcpc_dev *dev); + int (*get_current_limit)(struct tcpc_dev *dev); + int (*set_cc)(struct tcpc_dev *dev, enum typec_cc_status cc); + int (*get_cc)(struct tcpc_dev *dev, enum typec_cc_status *cc1, + enum typec_cc_status *cc2); + int (*set_polarity)(struct tcpc_dev *dev, + enum typec_cc_polarity polarity); + int (*set_vconn)(struct tcpc_dev *dev, bool on); + int (*set_vbus)(struct tcpc_dev *dev, bool on, bool charge); + int (*set_current_limit)(struct tcpc_dev *dev, u32 max_ma, u32 mv); + int (*set_pd_rx)(struct tcpc_dev *dev, bool on); + int (*set_roles)(struct tcpc_dev *dev, bool attached, + enum typec_role role, enum typec_data_role data); + int (*start_drp_toggling)(struct tcpc_dev *dev, + enum typec_cc_status cc); + int (*try_role)(struct tcpc_dev *dev, int role); + int (*pd_transmit)(struct tcpc_dev *dev, enum tcpm_transmit_type type, + const struct pd_message *msg); + struct tcpc_mux_dev *mux; +}; + +struct tcpm_port; + +struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc); +void tcpm_unregister_port(struct tcpm_port *port); + +void tcpm_update_source_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo); +void tcpm_update_sink_capabilities(struct tcpm_port *port, const u32 *pdo, + unsigned int nr_pdo, + unsigned int max_snk_mv, + unsigned int max_snk_ma, + unsigned int max_snk_mw, + unsigned int operating_snk_mw); + +void tcpm_vbus_change(struct tcpm_port *port); +void tcpm_cc_change(struct tcpm_port *port); +void tcpm_pd_receive(struct tcpm_port *port, + const struct pd_message *msg); +void tcpm_pd_transmit_complete(struct tcpm_port *port, + enum tcpm_transmit_status status); +void tcpm_pd_hard_reset(struct tcpm_port *port); +void tcpm_tcpc_reset(struct tcpm_port *port); + +#endif /* __LINUX_USB_TCPM_H */ -- cgit v1.2.3-59-g8ed1b From ee427e259567bc9ba817ddfa5abaf7033f5603e9 Mon Sep 17 00:00:00 2001 From: Lionel Landwerlin Date: Mon, 18 Sep 2017 12:42:41 +0100 Subject: uapi/drm/i915: document field usage of drm_i915_perf_oa_config Document the expected length of buffers config pointers (tuple of u32 values). Signed-off-by: Lionel Landwerlin Reviewed-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20170918114241.30105-1-lionel.g.landwerlin@intel.com --- include/uapi/drm/i915_drm.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index b4505d55990d..fe25a01c81f2 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -1511,6 +1511,11 @@ struct drm_i915_perf_oa_config { __u32 n_boolean_regs; __u32 n_flex_regs; + /* + * These fields are pointers to tuples of u32 values (register + * address, value). For example the expected length of the buffer + * pointed by mux_regs_ptr is (2 * sizeof(u32) * n_mux_regs). + */ __u64 mux_regs_ptr; __u64 boolean_regs_ptr; __u64 flex_regs_ptr; -- cgit v1.2.3-59-g8ed1b From ef838a81dd4de1e08454406812e42d7b9b417c4d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 13 Sep 2017 10:18:27 +0200 Subject: serial: Add common rs485 device tree parsing function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several drivers have the same device tree parsing code. Create a common helper function for it. This patch bases on work done by Sascha Hauer. Signed-off-by: Uwe Kleine-König Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/serial_core.c | 36 ++++++++++++++++++++++++++++++++++++ include/linux/serial_core.h | 5 +++++ 2 files changed, 41 insertions(+) (limited to 'include') diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 3a14cccbd7ff..f4e6c8662987 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -3026,5 +3026,41 @@ EXPORT_SYMBOL(uart_resume_port); EXPORT_SYMBOL(uart_add_one_port); EXPORT_SYMBOL(uart_remove_one_port); +/** + * of_get_rs485_mode() - Implement parsing rs485 properties + * @np: uart node + * @rs485conf: output parameter + * + * This function implements the device tree binding described in + * Documentation/devicetree/bindings/serial/rs485.txt. + */ +void of_get_rs485_mode(struct device_node *np, struct serial_rs485 *rs485conf) +{ + u32 rs485_delay[2]; + int ret; + + ret = of_property_read_u32_array(np, "rs485-rts-delay", rs485_delay, 2); + if (!ret) { + rs485conf->delay_rts_before_send = rs485_delay[0]; + rs485conf->delay_rts_after_send = rs485_delay[1]; + } else { + rs485conf->delay_rts_before_send = 0; + rs485conf->delay_rts_after_send = 0; + } + + /* + * clear full-duplex and enabled flags to get to a defined state with + * the two following properties. + */ + rs485conf->flags &= ~(SER_RS485_RX_DURING_TX | SER_RS485_ENABLED); + + if (of_property_read_bool(np, "rs485-rx-during-tx")) + rs485conf->flags |= SER_RS485_RX_DURING_TX; + + if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) + rs485conf->flags |= SER_RS485_ENABLED; +} +EXPORT_SYMBOL_GPL(of_get_rs485_mode); + MODULE_DESCRIPTION("Serial driver core"); MODULE_LICENSE("GPL"); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 5553e04e59c9..37b044e78333 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -501,4 +501,9 @@ static inline int uart_handle_break(struct uart_port *port) (cflag) & CRTSCTS || \ !((cflag) & CLOCAL)) +/* + * Common device tree parsing helpers + */ +void of_get_rs485_mode(struct device_node *np, struct serial_rs485 *rs485conf); + #endif /* LINUX_SERIAL_CORE_H */ -- cgit v1.2.3-59-g8ed1b From 7016e0627171878810798a842a416dddee4e3329 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 13 Sep 2017 13:58:15 -0700 Subject: net: Convert int functions to bool Global function ipv6_rcv_saddr_equal and static functions ipv6_rcv_saddr_equal and ipv4_rcv_saddr_equal currently return int. bool is slightly more descriptive for these functions so change their return type from int to bool. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/addrconf.h | 4 ++-- net/ipv4/inet_connection_sock.c | 36 ++++++++++++++++++------------------ 2 files changed, 20 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index f44ff2476758..87981cd63180 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -94,8 +94,8 @@ int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr, u32 banned_flags); int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr, u32 banned_flags); -int inet_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2, - bool match_wildcard); +bool inet_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2, + bool match_wildcard); void addrconf_join_solict(struct net_device *dev, const struct in6_addr *addr); void addrconf_leave_solict(struct inet6_dev *idev, const struct in6_addr *addr); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index b9c64b40a83a..9707372b78ed 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -39,11 +39,11 @@ EXPORT_SYMBOL(inet_csk_timer_bug_msg); * IPV6_ADDR_ANY only equals to IPV6_ADDR_ANY, * and 0.0.0.0 equals to 0.0.0.0 only */ -static int ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6, - const struct in6_addr *sk2_rcv_saddr6, - __be32 sk1_rcv_saddr, __be32 sk2_rcv_saddr, - bool sk1_ipv6only, bool sk2_ipv6only, - bool match_wildcard) +static bool ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6, + const struct in6_addr *sk2_rcv_saddr6, + __be32 sk1_rcv_saddr, __be32 sk2_rcv_saddr, + bool sk1_ipv6only, bool sk2_ipv6only, + bool match_wildcard) { int addr_type = ipv6_addr_type(sk1_rcv_saddr6); int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED; @@ -52,29 +52,29 @@ static int ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6, if (addr_type == IPV6_ADDR_MAPPED && addr_type2 == IPV6_ADDR_MAPPED) { if (!sk2_ipv6only) { if (sk1_rcv_saddr == sk2_rcv_saddr) - return 1; + return true; if (!sk1_rcv_saddr || !sk2_rcv_saddr) return match_wildcard; } - return 0; + return false; } if (addr_type == IPV6_ADDR_ANY && addr_type2 == IPV6_ADDR_ANY) - return 1; + return true; if (addr_type2 == IPV6_ADDR_ANY && match_wildcard && !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED)) - return 1; + return true; if (addr_type == IPV6_ADDR_ANY && match_wildcard && !(sk1_ipv6only && addr_type2 == IPV6_ADDR_MAPPED)) - return 1; + return true; if (sk2_rcv_saddr6 && ipv6_addr_equal(sk1_rcv_saddr6, sk2_rcv_saddr6)) - return 1; + return true; - return 0; + return false; } #endif @@ -82,20 +82,20 @@ static int ipv6_rcv_saddr_equal(const struct in6_addr *sk1_rcv_saddr6, * match_wildcard == false: addresses must be exactly the same, i.e. * 0.0.0.0 only equals to 0.0.0.0 */ -static int ipv4_rcv_saddr_equal(__be32 sk1_rcv_saddr, __be32 sk2_rcv_saddr, - bool sk2_ipv6only, bool match_wildcard) +static bool ipv4_rcv_saddr_equal(__be32 sk1_rcv_saddr, __be32 sk2_rcv_saddr, + bool sk2_ipv6only, bool match_wildcard) { if (!sk2_ipv6only) { if (sk1_rcv_saddr == sk2_rcv_saddr) - return 1; + return true; if (!sk1_rcv_saddr || !sk2_rcv_saddr) return match_wildcard; } - return 0; + return false; } -int inet_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2, - bool match_wildcard) +bool inet_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2, + bool match_wildcard) { #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family == AF_INET6) -- cgit v1.2.3-59-g8ed1b From f4ac6476945ff62939420bcf8266e39f8d5d54bd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 14 Sep 2017 12:35:36 +0200 Subject: libata: Add new med_power_with_dipm link_power_management_policy setting As described by Matthew Garret quite a while back: https://mjg59.dreamwidth.org/34868.html Intel CPUs starting with the Haswell generation need SATA links to power down for the "package" part of the CPU to reach low power-states like PC7 / P8 which bring a significant power-saving with them. The default max_performance lpm policy does not allow for these high PC states, both the medium_power and min_power policies do allow this. The min_power policy saves significantly more power, but there are some reports of some disks / SSDs not liking min_power leading to system crashes and in some cases even data corruption has been reported. Matthew has found a document documenting the default settings of Intel's IRST Windows driver with which most laptops ship: https://www-ssl.intel.com/content/dam/doc/reference-guide/sata-devices-implementation-recommendations.pdf Matthew wrote a patch changing med_power to match those defaults, but that never got anywhere as some people where reporting issues with the patch-set that patch was a part of. This commit is another attempt to make the default IRST driver settings available under Linux, but instead of changing medium_power and potentially introducing regressions, this commit adds a new med_power_with_dipm setting which is identical to the existing medium_power accept that it enables dipm on top, which makes it match the Windows IRST driver settings, which should hopefully be safe to use on most devices. The med_power_with_dipm setting is close to min_power, except that: a) It does not use host-initiated slumber mode (ASP not set), but it does allow device-initiated slumber b) It does not enable DevSlp mode On my T440s test laptop I get the following power savings when idle: medium_power 0.9W med_power_with_dipm 1.2W min_power 1.2W Suggested-by: Matthew Garrett Cc: Matthew Garrett Signed-off-by: Hans de Goede Signed-off-by: Tejun Heo --- drivers/ata/libata-core.c | 1 + drivers/ata/libata-eh.c | 10 +++++----- drivers/ata/libata-scsi.c | 9 +++++---- include/linux/libata.h | 1 + 4 files changed, 12 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index ee4c1ec9dca0..65f7574afc55 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -3964,6 +3964,7 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, scontrol &= ~(0x1 << 8); scontrol |= (0x6 << 8); break; + case ATA_LPM_MED_POWER_WITH_DIPM: case ATA_LPM_MIN_POWER: if (ata_link_nr_enabled(link) > 0) /* no restrictions on LPM transitions */ diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index e4effef0c83f..49b3745d2c1f 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -3454,9 +3454,9 @@ static int ata_eh_maybe_retry_flush(struct ata_device *dev) * @r_failed_dev: out parameter for failed device * * Enable SATA Interface power management. This will enable - * Device Interface Power Management (DIPM) for min_power - * policy, and then call driver specific callbacks for - * enabling Host Initiated Power management. + * Device Interface Power Management (DIPM) for min_power and + * medium_power_with_dipm policies, and then call driver specific + * callbacks for enabling Host Initiated Power management. * * LOCKING: * EH context. @@ -3502,7 +3502,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, hints &= ~ATA_LPM_HIPM; /* disable DIPM before changing link config */ - if (policy != ATA_LPM_MIN_POWER && dipm) { + if (policy < ATA_LPM_MED_POWER_WITH_DIPM && dipm) { err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_DISABLE, SATA_DIPM); if (err_mask && err_mask != AC_ERR_DEV) { @@ -3545,7 +3545,7 @@ static int ata_eh_set_lpm(struct ata_link *link, enum ata_lpm_policy policy, /* host config updated, enable DIPM if transitioning to MIN_POWER */ ata_for_each_dev(dev, link, ENABLED) { - if (policy == ATA_LPM_MIN_POWER && !no_dipm && + if (policy >= ATA_LPM_MED_POWER_WITH_DIPM && !no_dipm && ata_id_has_dipm(dev->id)) { err_mask = ata_dev_set_feature(dev, SETFEATURES_SATA_ENABLE, SATA_DIPM); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 44ba292f2cd7..673e72f438eb 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -106,10 +106,11 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { }; static const char *ata_lpm_policy_names[] = { - [ATA_LPM_UNKNOWN] = "max_performance", - [ATA_LPM_MAX_POWER] = "max_performance", - [ATA_LPM_MED_POWER] = "medium_power", - [ATA_LPM_MIN_POWER] = "min_power", + [ATA_LPM_UNKNOWN] = "max_performance", + [ATA_LPM_MAX_POWER] = "max_performance", + [ATA_LPM_MED_POWER] = "medium_power", + [ATA_LPM_MED_POWER_WITH_DIPM] = "med_power_with_dipm", + [ATA_LPM_MIN_POWER] = "min_power", }; static ssize_t ata_scsi_lpm_store(struct device *device, diff --git a/include/linux/libata.h b/include/linux/libata.h index 931c32f1f18d..ed9826b21c5e 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -522,6 +522,7 @@ enum ata_lpm_policy { ATA_LPM_UNKNOWN, ATA_LPM_MAX_POWER, ATA_LPM_MED_POWER, + ATA_LPM_MED_POWER_WITH_DIPM, /* Med power + DIPM as win IRST does */ ATA_LPM_MIN_POWER, }; -- cgit v1.2.3-59-g8ed1b From ecadea00f588d7047572a878072c766c75725d1f Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Sat, 9 Sep 2017 00:34:19 +0300 Subject: dt-bindings: clock: Add R8A77970 CPG core clock definitions Add macros usable by the device tree sources to reference the R8A77970 CPG core clocks by index. The data come from the table 8.2c of R-Car Series, 3rd Generation User's Manual: Hardware (Rev. 0.55, Jun. 30, 2017). Based on the original (and large) patch by Daisuke Matsushita . Signed-off-by: Vladimir Barinov Signed-off-by: Sergei Shtylyov Signed-off-by: Geert Uytterhoeven --- include/dt-bindings/clock/r8a77970-cpg-mssr.h | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 include/dt-bindings/clock/r8a77970-cpg-mssr.h (limited to 'include') diff --git a/include/dt-bindings/clock/r8a77970-cpg-mssr.h b/include/dt-bindings/clock/r8a77970-cpg-mssr.h new file mode 100644 index 000000000000..4146395595b1 --- /dev/null +++ b/include/dt-bindings/clock/r8a77970-cpg-mssr.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2016 Renesas Electronics Corp. + * Copyright (C) 2017 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __DT_BINDINGS_CLOCK_R8A77970_CPG_MSSR_H__ +#define __DT_BINDINGS_CLOCK_R8A77970_CPG_MSSR_H__ + +#include + +/* r8a77970 CPG Core Clocks */ +#define R8A77970_CLK_Z2 0 +#define R8A77970_CLK_ZR 1 +#define R8A77970_CLK_ZTR 2 +#define R8A77970_CLK_ZTRD2 3 +#define R8A77970_CLK_ZT 4 +#define R8A77970_CLK_ZX 5 +#define R8A77970_CLK_S1D1 6 +#define R8A77970_CLK_S1D2 7 +#define R8A77970_CLK_S1D4 8 +#define R8A77970_CLK_S2D1 9 +#define R8A77970_CLK_S2D2 10 +#define R8A77970_CLK_S2D4 11 +#define R8A77970_CLK_LB 12 +#define R8A77970_CLK_CL 13 +#define R8A77970_CLK_ZB3 14 +#define R8A77970_CLK_ZB3D2 15 +#define R8A77970_CLK_DDR 16 +#define R8A77970_CLK_CR 17 +#define R8A77970_CLK_CRD2 18 +#define R8A77970_CLK_SD0H 19 +#define R8A77970_CLK_SD0 20 +#define R8A77970_CLK_RPC 21 +#define R8A77970_CLK_RPCD2 22 +#define R8A77970_CLK_MSO 23 +#define R8A77970_CLK_CANFD 24 +#define R8A77970_CLK_CSI0 25 +#define R8A77970_CLK_FRAY 26 +#define R8A77970_CLK_CP 27 +#define R8A77970_CLK_CPEX 28 +#define R8A77970_CLK_R 29 +#define R8A77970_CLK_OSC 30 + +#endif /* __DT_BINDINGS_CLOCK_R8A77970_CPG_MSSR_H__ */ -- cgit v1.2.3-59-g8ed1b From 457c25efc592bb5539e18161c505f7a865013fb7 Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Mon, 18 Sep 2017 18:14:26 +0800 Subject: ASoC: rt5663: Add the function of impedance sensing Support the function of impedance sensing. It could be set the matrix row number of the impedance sensing table and the related parameters in the DTS. Signed-off-by: Oder Chiou Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/rt5663.txt | 16 ++ include/sound/rt5663.h | 3 + sound/soc/codecs/rt5663.c | 218 ++++++++++++++++++++- 3 files changed, 233 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/sound/rt5663.txt b/Documentation/devicetree/bindings/sound/rt5663.txt index ff381718c517..497bcfc58b71 100644 --- a/Documentation/devicetree/bindings/sound/rt5663.txt +++ b/Documentation/devicetree/bindings/sound/rt5663.txt @@ -19,6 +19,22 @@ Optional properties: Based on the different PCB layout, add the manual offset value to compensate the DC offset for each L and R channel, and they are different between headphone and headset. +- "realtek,impedance_sensing_num" + The matrix row number of the impedance sensing table. + If the value is 0, it means the impedance sensing is not supported. +- "realtek,impedance_sensing_table" + The matrix rows of the impedance sensing table are consisted by impedance + minimum, impedance maximun, volume, DC offset w/o and w/ mic of each L and + R channel accordingly. Example is shown as following. + < 0 300 7 0xffd160 0xffd1c0 0xff8a10 0xff8ab0 + 301 65535 4 0xffe470 0xffe470 0xffb8e0 0xffb8e0> + The first and second column are defined for the impedance range. If the + detected impedance value is in the range, then the volume value of the + third column will be set to codec. In our codec design, each volume value + should compensate different DC offset to avoid the pop sound, and it is + also different between headphone and headset. In the example, the + "realtek,impedance_sensing_num" is 2. It means that there are 2 ranges of + impedance in the impedance sensing function. Pins on the device (for linking into audio routes) for RT5663: diff --git a/include/sound/rt5663.h b/include/sound/rt5663.h index 7d00e5849706..7b90a8f1034c 100644 --- a/include/sound/rt5663.h +++ b/include/sound/rt5663.h @@ -16,6 +16,9 @@ struct rt5663_platform_data { unsigned int dc_offset_r_manual; unsigned int dc_offset_l_manual_mic; unsigned int dc_offset_r_manual_mic; + + unsigned int impedance_sensing_num; + unsigned int *impedance_sensing_table; }; #endif diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index ab9e0ebff5a7..767f219f6c42 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -38,6 +38,16 @@ enum { CODEC_VER_0, }; +struct impedance_mapping_table { + unsigned int imp_min; + unsigned int imp_max; + unsigned int vol; + unsigned int dc_offset_l_manual; + unsigned int dc_offset_r_manual; + unsigned int dc_offset_l_manual_mic; + unsigned int dc_offset_r_manual_mic; +}; + struct rt5663_priv { struct snd_soc_codec *codec; struct rt5663_platform_data pdata; @@ -45,6 +55,7 @@ struct rt5663_priv { struct delayed_work jack_detect_work; struct snd_soc_jack *hs_jack; struct timer_list btn_check_timer; + struct impedance_mapping_table *imp_table; int codec_ver; int sysclk; @@ -1575,6 +1586,9 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) rt5663->jack_type = SND_JACK_HEADSET; rt5663_enable_push_button_irq(codec, true); + if (rt5663->pdata.impedance_sensing_num) + break; + if (rt5663->pdata.dc_offset_l_manual_mic) { regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2, rt5663->pdata.dc_offset_l_manual_mic >> @@ -1596,6 +1610,9 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) default: rt5663->jack_type = SND_JACK_HEADPHONE; + if (rt5663->pdata.impedance_sensing_num) + break; + if (rt5663->pdata.dc_offset_l_manual) { regmap_write(rt5663->regmap, RT5663_MIC_DECRO_2, rt5663->pdata.dc_offset_l_manual >> 16); @@ -1623,6 +1640,177 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert) return rt5663->jack_type; } +static int rt5663_impedance_sensing(struct snd_soc_codec *codec) +{ + struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec); + unsigned int value, i, reg84, reg26, reg2fa, reg91, reg10, reg80; + + for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) { + if (rt5663->imp_table[i].vol == 7) + break; + } + + if (rt5663->jack_type == SND_JACK_HEADSET) { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff); + } else { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual & 0xffff); + } + + reg84 = snd_soc_read(codec, RT5663_ASRC_2); + reg26 = snd_soc_read(codec, RT5663_STO1_ADC_MIXER); + reg2fa = snd_soc_read(codec, RT5663_DUMMY_1); + reg91 = snd_soc_read(codec, RT5663_HP_CHARGE_PUMP_1); + reg10 = snd_soc_read(codec, RT5663_RECMIX); + reg80 = snd_soc_read(codec, RT5663_GLB_CLK); + + snd_soc_update_bits(codec, RT5663_STO_DRE_1, 0x8000, 0); + snd_soc_write(codec, RT5663_ASRC_2, 0); + snd_soc_write(codec, RT5663_STO1_ADC_MIXER, 0x4040); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK | + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_VREF1 | RT5663_PWR_VREF2); + usleep_range(10000, 10005); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_FV1_MASK | RT5663_PWR_FV2_MASK, + RT5663_PWR_FV1 | RT5663_PWR_FV2); + snd_soc_update_bits(codec, RT5663_GLB_CLK, RT5663_SCLK_SRC_MASK, + RT5663_SCLK_SRC_RCCLK); + snd_soc_update_bits(codec, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK, + RT5663_DIG_25M_CLK_EN); + snd_soc_update_bits(codec, RT5663_ADDA_CLK_1, RT5663_I2S_PD1_MASK, 0); + snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0xff00); + snd_soc_write(codec, RT5663_PRE_DIV_GATING_2, 0xfffc); + snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_1, 0x1232); + snd_soc_write(codec, RT5663_HP_LOGIC_2, 0x0005); + snd_soc_write(codec, RT5663_DEPOP_2, 0x3003); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0x0030); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0x0003); + snd_soc_update_bits(codec, RT5663_PWR_DIG_2, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F); + snd_soc_update_bits(codec, RT5663_PWR_DIG_1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_ON | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1); + msleep(40); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2); + msleep(30); + snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_2, 0x1371); + snd_soc_write(codec, RT5663_STO_DAC_MIXER, 0); + snd_soc_write(codec, RT5663_BYPASS_STO_DAC, 0x000c); + snd_soc_write(codec, RT5663_HP_BIAS, 0xafaa); + snd_soc_write(codec, RT5663_CHARGE_PUMP_1, 0x2224); + snd_soc_write(codec, RT5663_HP_OUT_EN, 0x8088); + snd_soc_write(codec, RT5663_CHOP_ADC, 0x3000); + snd_soc_write(codec, RT5663_ADDA_RST, 0xc000); + snd_soc_write(codec, RT5663_STO1_HPF_ADJ1, 0x3320); + snd_soc_write(codec, RT5663_HP_CALIB_2, 0x00c9); + snd_soc_write(codec, RT5663_DUMMY_1, 0x004c); + snd_soc_write(codec, RT5663_ANA_BIAS_CUR_1, 0x7733); + snd_soc_write(codec, RT5663_CHARGE_PUMP_2, 0x7777); + snd_soc_write(codec, RT5663_STO_DRE_9, 0x0007); + snd_soc_write(codec, RT5663_STO_DRE_10, 0x0007); + snd_soc_write(codec, RT5663_DUMMY_2, 0x02a4); + snd_soc_write(codec, RT5663_RECMIX, 0x0005); + snd_soc_write(codec, RT5663_HP_IMP_SEN_1, 0x4334); + snd_soc_update_bits(codec, RT5663_IRQ_3, 0x0004, 0x0004); + snd_soc_write(codec, RT5663_HP_LOGIC_1, 0x2200); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0x3000); + snd_soc_write(codec, RT5663_HP_LOGIC_1, 0x6200); + + for (i = 0; i < 100; i++) { + msleep(20); + if (snd_soc_read(codec, RT5663_INT_ST_1) & 0x2) + break; + } + + value = snd_soc_read(codec, RT5663_HP_IMP_SEN_4); + + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0); + snd_soc_write(codec, RT5663_INT_ST_1, 0); + snd_soc_write(codec, RT5663_HP_LOGIC_1, 0); + snd_soc_update_bits(codec, RT5663_RC_CLK, RT5663_DIG_25M_CLK_MASK, + RT5663_DIG_25M_CLK_DIS); + snd_soc_write(codec, RT5663_GLB_CLK, reg80); + snd_soc_write(codec, RT5663_RECMIX, reg10); + snd_soc_write(codec, RT5663_DUMMY_2, 0x00a4); + snd_soc_write(codec, RT5663_DUMMY_1, reg2fa); + snd_soc_write(codec, RT5663_HP_CALIB_2, 0x00c8); + snd_soc_write(codec, RT5663_STO1_HPF_ADJ1, 0xb320); + snd_soc_write(codec, RT5663_ADDA_RST, 0xe400); + snd_soc_write(codec, RT5663_CHOP_ADC, 0x2000); + snd_soc_write(codec, RT5663_HP_OUT_EN, 0x0008); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_2, + RT5663_PWR_RECMIX1 | RT5663_PWR_RECMIX2, 0); + snd_soc_update_bits(codec, RT5663_PWR_DIG_1, + RT5663_PWR_DAC_L1 | RT5663_PWR_DAC_R1 | + RT5663_PWR_LDO_DACREF_MASK | RT5663_PWR_ADC_L1 | + RT5663_PWR_ADC_R1, 0); + snd_soc_update_bits(codec, RT5663_PWR_DIG_2, + RT5663_PWR_ADC_S1F | RT5663_PWR_DAC_S1F, 0); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0003, 0); + snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0); + snd_soc_write(codec, RT5663_HP_LOGIC_2, 0); + snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_1, reg91); + snd_soc_update_bits(codec, RT5663_PWR_ANLG_1, + RT5663_PWR_VREF1_MASK | RT5663_PWR_VREF2_MASK, 0); + snd_soc_write(codec, RT5663_STO1_ADC_MIXER, reg26); + snd_soc_write(codec, RT5663_ASRC_2, reg84); + + for (i = 0; i < rt5663->pdata.impedance_sensing_num; i++) { + if (value >= rt5663->imp_table[i].imp_min && + value <= rt5663->imp_table[i].imp_max) + break; + } + + snd_soc_update_bits(codec, RT5663_STO_DRE_9, RT5663_DRE_GAIN_HP_MASK, + rt5663->imp_table[i].vol); + snd_soc_update_bits(codec, RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_MASK, + rt5663->imp_table[i].vol); + + if (rt5663->jack_type == SND_JACK_HEADSET) { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual_mic & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual_mic >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual_mic & 0xffff); + } else { + snd_soc_write(codec, RT5663_MIC_DECRO_2, + rt5663->imp_table[i].dc_offset_l_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_3, + rt5663->imp_table[i].dc_offset_l_manual & 0xffff); + snd_soc_write(codec, RT5663_MIC_DECRO_5, + rt5663->imp_table[i].dc_offset_r_manual >> 16); + snd_soc_write(codec, RT5663_MIC_DECRO_6, + rt5663->imp_table[i].dc_offset_r_manual & 0xffff); + } + + return 0; +} + static int rt5663_button_detect(struct snd_soc_codec *codec) { int btn_type, val; @@ -1701,6 +1889,8 @@ static void rt5663_jack_detect_work(struct work_struct *work) break; case CODEC_VER_0: report = rt5663_jack_detect(rt5663->codec, 1); + if (rt5663->pdata.impedance_sensing_num) + rt5663_impedance_sensing(rt5663->codec); break; default: dev_err(codec->dev, "Unknown CODEC Version\n"); @@ -1796,10 +1986,6 @@ static const struct snd_kcontrol_new rt5663_v2_specific_controls[] = { }; static const struct snd_kcontrol_new rt5663_specific_controls[] = { - /* Headphone Output Volume */ - SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9, - RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1, - rt5663_hp_vol_tlv), /* Mic Boost Volume*/ SOC_SINGLE_TLV("IN1 Capture Volume", RT5663_CBJ_2, RT5663_GAIN_BST1_SHIFT, 8, 0, in_bst_tlv), @@ -1807,6 +1993,13 @@ static const struct snd_kcontrol_new rt5663_specific_controls[] = { SOC_ENUM("IF1 ADC Data Swap", rt5663_if1_adc_enum), }; +static const struct snd_kcontrol_new rt5663_hpvol_controls[] = { + /* Headphone Output Volume */ + SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_STO_DRE_9, + RT5663_STO_DRE_10, RT5663_DRE_GAIN_HP_SHIFT, 23, 1, + rt5663_hp_vol_tlv), +}; + static int rt5663_is_sys_clk_from_pll(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_widget *sink) { @@ -2889,6 +3082,10 @@ static int rt5663_probe(struct snd_soc_codec *codec) ARRAY_SIZE(rt5663_specific_dapm_routes)); snd_soc_add_codec_controls(codec, rt5663_specific_controls, ARRAY_SIZE(rt5663_specific_controls)); + + if (!rt5663->imp_table) + snd_soc_add_codec_controls(codec, rt5663_hpvol_controls, + ARRAY_SIZE(rt5663_hpvol_controls)); break; } @@ -3177,6 +3374,8 @@ static void rt5663_calibrate(struct rt5663_priv *rt5663) static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) { + int table_size; + device_property_read_u32(dev, "realtek,dc_offset_l_manual", &rt5663->pdata.dc_offset_l_manual); device_property_read_u32(dev, "realtek,dc_offset_r_manual", @@ -3185,6 +3384,17 @@ static int rt5663_parse_dp(struct rt5663_priv *rt5663, struct device *dev) &rt5663->pdata.dc_offset_l_manual_mic); device_property_read_u32(dev, "realtek,dc_offset_r_manual_mic", &rt5663->pdata.dc_offset_r_manual_mic); + device_property_read_u32(dev, "realtek,impedance_sensing_num", + &rt5663->pdata.impedance_sensing_num); + + if (rt5663->pdata.impedance_sensing_num) { + table_size = sizeof(struct impedance_mapping_table) * + rt5663->pdata.impedance_sensing_num; + rt5663->imp_table = devm_kzalloc(dev, table_size, GFP_KERNEL); + device_property_read_u32_array(dev, + "realtek,impedance_sensing_table", + (u32 *)rt5663->imp_table, table_size); + } return 0; } -- cgit v1.2.3-59-g8ed1b From ac6c35a4d8c77083525044a192cb1a8711381e94 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 18 Sep 2017 21:20:03 +0300 Subject: drm: add backwards compatibility support for drm_kms_helper.edid_firmware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add drm_kms_helper.edid_firmware module parameter with param ops hooks to set drm.edid_firmware instead, for backwards compatibility. Suggested-by: Ville Syrjälä Cc: Abdiel Janulgue Cc: Daniel Vetter Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Acked-by: Dave Airlie Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20170918182003.22238-2-jani.nikula@intel.com --- drivers/gpu/drm/drm_edid_load.c | 16 ++++++++++++++++ drivers/gpu/drm/drm_kms_helper_common.c | 28 ++++++++++++++++++++++++++++ include/drm/drm_edid.h | 2 ++ 3 files changed, 46 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 1c0495acf341..a4915099aaa9 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -31,6 +31,22 @@ module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644); MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob " "from built-in data or /lib/firmware instead. "); +/* Use only for backward compatibility with drm_kms_helper.edid_firmware */ +int __drm_set_edid_firmware_path(const char *path) +{ + scnprintf(edid_firmware, sizeof(edid_firmware), "%s", path); + + return 0; +} +EXPORT_SYMBOL(__drm_set_edid_firmware_path); + +/* Use only for backward compatibility with drm_kms_helper.edid_firmware */ +int __drm_get_edid_firmware_path(char *buf, size_t bufsize) +{ + return scnprintf(buf, bufsize, "%s", edid_firmware); +} +EXPORT_SYMBOL(__drm_get_edid_firmware_path); + #define GENERIC_EDIDS 6 static const char * const generic_edid_name[GENERIC_EDIDS] = { "edid/800x600.bin", diff --git a/drivers/gpu/drm/drm_kms_helper_common.c b/drivers/gpu/drm/drm_kms_helper_common.c index 6e35a56a6102..93e2b30fe1a5 100644 --- a/drivers/gpu/drm/drm_kms_helper_common.c +++ b/drivers/gpu/drm/drm_kms_helper_common.c @@ -26,6 +26,7 @@ */ #include +#include #include "drm_crtc_helper_internal.h" @@ -33,6 +34,33 @@ MODULE_AUTHOR("David Airlie, Jesse Barnes"); MODULE_DESCRIPTION("DRM KMS helper"); MODULE_LICENSE("GPL and additional rights"); +#if IS_ENABLED(CONFIG_DRM_LOAD_EDID_FIRMWARE) + +/* Backward compatibility for drm_kms_helper.edid_firmware */ +static int edid_firmware_set(const char *val, const struct kernel_param *kp) +{ + DRM_NOTE("drm_kms_firmware.edid_firmware is deprecated, please use drm.edid_firmware intead.\n"); + + return __drm_set_edid_firmware_path(val); +} + +static int edid_firmware_get(char *buffer, const struct kernel_param *kp) +{ + return __drm_get_edid_firmware_path(buffer, PAGE_SIZE); +} + +static const struct kernel_param_ops edid_firmware_ops = { + .set = edid_firmware_set, + .get = edid_firmware_get, +}; + +module_param_cb(edid_firmware, &edid_firmware_ops, NULL, 0644); +__MODULE_PARM_TYPE(edid_firmware, "charp"); +MODULE_PARM_DESC(edid_firmware, + "DEPRECATED. Use drm.edid_firmware module parameter instead."); + +#endif + static int __init drm_kms_helper_init(void) { int ret; diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 1e1908a6b1d6..6f35909b8add 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -341,6 +341,8 @@ int drm_av_sync_delay(struct drm_connector *connector, #ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE struct edid *drm_load_edid_firmware(struct drm_connector *connector); +int __drm_set_edid_firmware_path(const char *path); +int __drm_get_edid_firmware_path(char *buf, size_t bufsize); #else static inline struct edid * drm_load_edid_firmware(struct drm_connector *connector) -- cgit v1.2.3-59-g8ed1b From a5c6951c49fbf7509419d877620625ca3cdae9b1 Mon Sep 17 00:00:00 2001 From: Robert Jarzmik Date: Wed, 13 Sep 2017 21:37:17 +0200 Subject: mfd: wm97xx-core: core support for wm97xx Codec The WM9705, WM9712 and WM9713 are highly integrated codecs, with an audio codec, DAC and ADC, GPIO unit and a touchscreen interface. Historically the support was spread across drivers/input/touchscreen and sound/soc/codecs. The sharing was done through ac97 bus sharing. This model will not withstand the new AC97 bus model, where codecs are discovered on runtime. Signed-off-by: Robert Jarzmik Acked-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/wm97xx-core.c | 366 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/wm97xx.h | 25 ++++ 4 files changed, 406 insertions(+) create mode 100644 drivers/mfd/wm97xx-core.c create mode 100644 include/linux/mfd/wm97xx.h (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index fc5e4fef89d2..ac5ad6d0837c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1746,6 +1746,20 @@ config MFD_WM8994 core support for the WM8994, in order to use the actual functionaltiy of the device other drivers must be enabled. +config MFD_WM97xx + tristate "Wolfson Microelectronics WM97xx" + select MFD_CORE + select REGMAP_AC97 + select AC97_BUS_COMPAT + depends on AC97_BUS_NEW + help + The WM9705, WM9712 and WM9713 is a highly integrated hi-fi CODEC + designed for smartphone applications. As well as audio functionality + it has on board GPIO and a touchscreen functionality which is + supported via the relevant subsystems. This driver provides core + support for the WM97xx, in order to use the actual functionaltiy of + the device other drivers must be enabled. + config MFD_STW481X tristate "Support for ST Microelectronics STw481x" depends on I2C && (ARCH_NOMADIK || COMPILE_TEST) diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c3d0a1b39bb6..3266977f54b8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o wm8994-objs := wm8994-core.o wm8994-irq.o wm8994-regmap.o obj-$(CONFIG_MFD_WM8994) += wm8994.o +obj-$(CONFIG_MFD_WM97xx) += wm97xx-core.o obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o diff --git a/drivers/mfd/wm97xx-core.c b/drivers/mfd/wm97xx-core.c new file mode 100644 index 000000000000..4141ee52a70b --- /dev/null +++ b/drivers/mfd/wm97xx-core.c @@ -0,0 +1,366 @@ +/* + * Wolfson WM97xx -- Core device + * + * Copyright (C) 2017 Robert Jarzmik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Features: + * - an AC97 audio codec + * - a touchscreen driver + * - a GPIO block + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WM9705_VENDOR_ID 0x574d4c05 +#define WM9712_VENDOR_ID 0x574d4c12 +#define WM9713_VENDOR_ID 0x574d4c13 +#define WM97xx_VENDOR_ID_MASK 0xffffffff + +struct wm97xx_priv { + struct regmap *regmap; + struct snd_ac97 *ac97; + struct device *dev; + struct wm97xx_platform_data codec_pdata; +}; + +static bool wm97xx_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_RESET ... AC97_PCM_SURR_DAC_RATE: + case AC97_PCM_LR_ADC_RATE: + case AC97_CENTER_LFE_MASTER: + case AC97_SPDIF ... AC97_LINE1_LEVEL: + case AC97_GPIO_CFG ... 0x5c: + case AC97_CODEC_CLASS_REV ... AC97_PCI_SID: + case 0x74 ... AC97_VENDOR_ID2: + return true; + default: + return false; + } +} + +static bool wm97xx_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_VENDOR_ID1: + case AC97_VENDOR_ID2: + return false; + default: + return wm97xx_readable_reg(dev, reg); + } +} + +static const struct reg_default wm9705_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x0a, 0x8000 }, + { 0x0c, 0x8008 }, + { 0x0e, 0x8008 }, + { 0x10, 0x8808 }, + { 0x12, 0x8808 }, + { 0x14, 0x8808 }, + { 0x16, 0x8808 }, + { 0x18, 0x8808 }, + { 0x1a, 0x0000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0000 }, + { 0x2c, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x5a, 0x0000 }, + { 0x5c, 0x0000 }, + { 0x72, 0x0808 }, + { 0x74, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0000 }, + { 0x7a, 0x0000 }, +}; + +static const struct regmap_config wm9705_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9705_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9705_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9705_cells[] = { + { .name = "wm9705-codec", }, + { .name = "wm97xx-ts", }, +}; + +static bool wm9712_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AC97_REC_GAIN: + return true; + default: + return regmap_ac97_default_volatile(dev, reg); + } +} + +static const struct reg_default wm9712_reg_defaults[] = { + { 0x02, 0x8000 }, + { 0x04, 0x8000 }, + { 0x06, 0x8000 }, + { 0x08, 0x0f0f }, + { 0x0a, 0xaaa0 }, + { 0x0c, 0xc008 }, + { 0x0e, 0x6808 }, + { 0x10, 0xe808 }, + { 0x12, 0xaaa0 }, + { 0x14, 0xad00 }, + { 0x16, 0x8000 }, + { 0x18, 0xe808 }, + { 0x1a, 0x3000 }, + { 0x1c, 0x8000 }, + { 0x20, 0x0000 }, + { 0x22, 0x0000 }, + { 0x26, 0x000f }, + { 0x28, 0x0605 }, + { 0x2a, 0x0410 }, + { 0x2c, 0xbb80 }, + { 0x2e, 0xbb80 }, + { 0x32, 0xbb80 }, + { 0x34, 0x2000 }, + { 0x4c, 0xf83e }, + { 0x4e, 0xffff }, + { 0x50, 0x0000 }, + { 0x52, 0x0000 }, + { 0x56, 0xf83e }, + { 0x58, 0x0008 }, + { 0x5c, 0x0000 }, + { 0x60, 0xb032 }, + { 0x62, 0x3e00 }, + { 0x64, 0x0000 }, + { 0x76, 0x0006 }, + { 0x78, 0x0001 }, + { 0x7a, 0x0000 }, +}; + +static const struct regmap_config wm9712_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9712_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9712_reg_defaults), + .volatile_reg = wm9712_volatile_reg, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9712_cells[] = { + { .name = "wm9712-codec", }, + { .name = "wm97xx-ts", }, +}; + +static const struct reg_default wm9713_reg_defaults[] = { + { 0x02, 0x8080 }, /* Speaker Output Volume */ + { 0x04, 0x8080 }, /* Headphone Output Volume */ + { 0x06, 0x8080 }, /* Out3/OUT4 Volume */ + { 0x08, 0xc880 }, /* Mono Volume */ + { 0x0a, 0xe808 }, /* LINEIN Volume */ + { 0x0c, 0xe808 }, /* DAC PGA Volume */ + { 0x0e, 0x0808 }, /* MIC PGA Volume */ + { 0x10, 0x00da }, /* MIC Routing Control */ + { 0x12, 0x8000 }, /* Record PGA Volume */ + { 0x14, 0xd600 }, /* Record Routing */ + { 0x16, 0xaaa0 }, /* PCBEEP Volume */ + { 0x18, 0xaaa0 }, /* VxDAC Volume */ + { 0x1a, 0xaaa0 }, /* AUXDAC Volume */ + { 0x1c, 0x0000 }, /* Output PGA Mux */ + { 0x1e, 0x0000 }, /* DAC 3D control */ + { 0x20, 0x0f0f }, /* DAC Tone Control*/ + { 0x22, 0x0040 }, /* MIC Input Select & Bias */ + { 0x24, 0x0000 }, /* Output Volume Mapping & Jack */ + { 0x26, 0x7f00 }, /* Powerdown Ctrl/Stat*/ + { 0x28, 0x0405 }, /* Extended Audio ID */ + { 0x2a, 0x0410 }, /* Extended Audio Start/Ctrl */ + { 0x2c, 0xbb80 }, /* Audio DACs Sample Rate */ + { 0x2e, 0xbb80 }, /* AUXDAC Sample Rate */ + { 0x32, 0xbb80 }, /* Audio ADCs Sample Rate */ + { 0x36, 0x4523 }, /* PCM codec control */ + { 0x3a, 0x2000 }, /* SPDIF control */ + { 0x3c, 0xfdff }, /* Powerdown 1 */ + { 0x3e, 0xffff }, /* Powerdown 2 */ + { 0x40, 0x0000 }, /* General Purpose */ + { 0x42, 0x0000 }, /* Fast Power-Up Control */ + { 0x44, 0x0080 }, /* MCLK/PLL Control */ + { 0x46, 0x0000 }, /* MCLK/PLL Control */ + + { 0x4c, 0xfffe }, /* GPIO Pin Configuration */ + { 0x4e, 0xffff }, /* GPIO Pin Polarity / Type */ + { 0x50, 0x0000 }, /* GPIO Pin Sticky */ + { 0x52, 0x0000 }, /* GPIO Pin Wake-Up */ + /* GPIO Pin Status */ + { 0x56, 0xfffe }, /* GPIO Pin Sharing */ + { 0x58, 0x4000 }, /* GPIO PullUp/PullDown */ + { 0x5a, 0x0000 }, /* Additional Functions 1 */ + { 0x5c, 0x0000 }, /* Additional Functions 2 */ + { 0x60, 0xb032 }, /* ALC Control */ + { 0x62, 0x3e00 }, /* ALC / Noise Gate Control */ + { 0x64, 0x0000 }, /* AUXDAC input control */ + { 0x74, 0x0000 }, /* Digitiser Reg 1 */ + { 0x76, 0x0006 }, /* Digitiser Reg 2 */ + { 0x78, 0x0001 }, /* Digitiser Reg 3 */ + { 0x7a, 0x0000 }, /* Digitiser Read Back */ +}; + +static const struct regmap_config wm9713_regmap_config = { + .reg_bits = 16, + .reg_stride = 2, + .val_bits = 16, + .max_register = 0x7e, + .cache_type = REGCACHE_RBTREE, + + .reg_defaults = wm9713_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm9713_reg_defaults), + .volatile_reg = regmap_ac97_default_volatile, + .readable_reg = wm97xx_readable_reg, + .writeable_reg = wm97xx_writeable_reg, +}; + +static struct mfd_cell wm9713_cells[] = { + { .name = "wm9713-codec", }, + { .name = "wm97xx-ts", }, +}; + +static int wm97xx_ac97_probe(struct ac97_codec_device *adev) +{ + struct wm97xx_priv *wm97xx; + const struct regmap_config *config; + struct wm97xx_platform_data *codec_pdata; + struct mfd_cell *cells; + int ret = -ENODEV, nb_cells, i; + struct wm97xx_pdata *pdata = snd_ac97_codec_get_platdata(adev); + + wm97xx = devm_kzalloc(ac97_codec_dev2dev(adev), + sizeof(*wm97xx), GFP_KERNEL); + if (!wm97xx) + return -ENOMEM; + + wm97xx->dev = ac97_codec_dev2dev(adev); + wm97xx->ac97 = snd_ac97_compat_alloc(adev); + if (IS_ERR(wm97xx->ac97)) + return PTR_ERR(wm97xx->ac97); + + + ac97_set_drvdata(adev, wm97xx); + dev_info(wm97xx->dev, "wm97xx core found, id=0x%x\n", + adev->vendor_id); + + codec_pdata = &wm97xx->codec_pdata; + codec_pdata->ac97 = wm97xx->ac97; + codec_pdata->batt_pdata = pdata->batt_pdata; + + switch (adev->vendor_id) { + case WM9705_VENDOR_ID: + config = &wm9705_regmap_config; + cells = wm9705_cells; + nb_cells = ARRAY_SIZE(wm9705_cells); + break; + case WM9712_VENDOR_ID: + config = &wm9712_regmap_config; + cells = wm9712_cells; + nb_cells = ARRAY_SIZE(wm9712_cells); + break; + case WM9713_VENDOR_ID: + config = &wm9713_regmap_config; + cells = wm9713_cells; + nb_cells = ARRAY_SIZE(wm9713_cells); + break; + default: + goto err_free_compat; + } + + for (i = 0; i < nb_cells; i++) { + cells[i].platform_data = codec_pdata; + cells[i].pdata_size = sizeof(*codec_pdata); + } + + codec_pdata->regmap = devm_regmap_init_ac97(wm97xx->ac97, config); + if (IS_ERR(codec_pdata->regmap)) { + ret = PTR_ERR(codec_pdata->regmap); + goto err_free_compat; + } + + ret = devm_mfd_add_devices(wm97xx->dev, PLATFORM_DEVID_NONE, + cells, nb_cells, NULL, 0, NULL); + if (ret) + goto err_free_compat; + + return ret; + +err_free_compat: + snd_ac97_compat_release(wm97xx->ac97); + return ret; +} + +static int wm97xx_ac97_remove(struct ac97_codec_device *adev) +{ + struct wm97xx_priv *wm97xx = ac97_get_drvdata(adev); + + snd_ac97_compat_release(wm97xx->ac97); + + return 0; +} + +static const struct ac97_id wm97xx_ac97_ids[] = { + { .id = WM9705_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { .id = WM9712_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { .id = WM9713_VENDOR_ID, .mask = WM97xx_VENDOR_ID_MASK }, + { } +}; + +static struct ac97_codec_driver wm97xx_ac97_driver = { + .driver = { + .name = "wm97xx-core", + }, + .probe = wm97xx_ac97_probe, + .remove = wm97xx_ac97_remove, + .id_table = wm97xx_ac97_ids, +}; + +static int __init wm97xx_module_init(void) +{ + return snd_ac97_codec_driver_register(&wm97xx_ac97_driver); +} +module_init(wm97xx_module_init); + +static void __exit wm97xx_module_exit(void) +{ + snd_ac97_codec_driver_unregister(&wm97xx_ac97_driver); +} +module_exit(wm97xx_module_exit); + +MODULE_DESCRIPTION("WM9712, WM9713 core driver"); +MODULE_AUTHOR("Robert Jarzmik "); +MODULE_LICENSE("GPL"); + diff --git a/include/linux/mfd/wm97xx.h b/include/linux/mfd/wm97xx.h new file mode 100644 index 000000000000..45fb54f19d09 --- /dev/null +++ b/include/linux/mfd/wm97xx.h @@ -0,0 +1,25 @@ +/* + * wm97xx client interface + * + * Copyright (C) 2017 Robert Jarzmik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_MFD_WM97XX_H +#define __LINUX_MFD_WM97XX_H + +struct regmap; +struct wm97xx_batt_pdata; +struct snd_ac97; + +struct wm97xx_platform_data { + struct snd_ac97 *ac97; + struct regmap *regmap; + struct wm97xx_batt_pdata *batt_pdata; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From f454322efbf6faee695f517c6b52c4dc03cacd3e Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Tue, 22 Aug 2017 02:16:11 +0300 Subject: signal: replace sigset_to_compat() with put_compat_sigset() There are 4 callers of sigset_to_compat() in the entire kernel. One is in sparc compat rt_sigaction(2), the rest are in kernel/signal.c itself. All are followed by copy_to_user(), and all but the sparc one are under "if it's big-endian..." ifdefs. Let's transform sigset_to_compat() into put_compat_sigset() that also calls copy_to_user(). Suggested-by: Al Viro Signed-off-by: Dmitry V. Levin Signed-off-by: Al Viro --- arch/sparc/kernel/sys_sparc32.c | 6 +++--- include/linux/compat.h | 3 ++- kernel/compat.c | 20 ++++++++++++++------ kernel/signal.c | 27 ++++++--------------------- 4 files changed, 25 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c index bca44f3e6b86..5e2bec9e41b2 100644 --- a/arch/sparc/kernel/sys_sparc32.c +++ b/arch/sparc/kernel/sys_sparc32.c @@ -159,7 +159,6 @@ COMPAT_SYSCALL_DEFINE5(rt_sigaction, int, sig, { struct k_sigaction new_ka, old_ka; int ret; - compat_sigset_t set32; /* XXX: Don't preclude handling different sized sigset_t's. */ if (sigsetsize != sizeof(compat_sigset_t)) @@ -167,6 +166,7 @@ COMPAT_SYSCALL_DEFINE5(rt_sigaction, int, sig, if (act) { u32 u_handler, u_restorer; + compat_sigset_t set32; new_ka.ka_restorer = restorer; ret = get_user(u_handler, &act->sa_handler); @@ -183,9 +183,9 @@ COMPAT_SYSCALL_DEFINE5(rt_sigaction, int, sig, ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { - sigset_to_compat(&set32, &old_ka.sa.sa_mask); ret = put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler); - ret |= copy_to_user(&oact->sa_mask, &set32, sizeof(compat_sigset_t)); + ret |= put_compat_sigset(&oact->sa_mask, &old_ka.sa.sa_mask, + sizeof(oact->sa_mask)); ret |= put_user(old_ka.sa.sa_flags, &oact->sa_flags); ret |= put_user(ptr_to_compat(old_ka.sa.sa_restorer), &oact->sa_restorer); if (ret) diff --git a/include/linux/compat.h b/include/linux/compat.h index a5619de3437d..ab1baa79abe8 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -456,7 +456,8 @@ asmlinkage long compat_sys_settimeofday(struct compat_timeval __user *tv, asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp); extern void sigset_from_compat(sigset_t *set, const compat_sigset_t *compat); -extern void sigset_to_compat(compat_sigset_t *compat, const sigset_t *set); +extern int put_compat_sigset(compat_sigset_t __user *compat, + const sigset_t *set, unsigned int size); asmlinkage long compat_sys_migrate_pages(compat_pid_t pid, compat_ulong_t maxnode, const compat_ulong_t __user *old_nodes, diff --git a/kernel/compat.c b/kernel/compat.c index 772e038d04d9..18dd902c9052 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -497,15 +497,23 @@ sigset_from_compat(sigset_t *set, const compat_sigset_t *compat) } EXPORT_SYMBOL_GPL(sigset_from_compat); -void -sigset_to_compat(compat_sigset_t *compat, const sigset_t *set) +int +put_compat_sigset(compat_sigset_t __user *compat, const sigset_t *set, + unsigned int size) { + /* size <= sizeof(compat_sigset_t) <= sizeof(sigset_t) */ +#ifdef __BIG_ENDIAN + compat_sigset_t v; switch (_NSIG_WORDS) { - case 4: compat->sig[7] = (set->sig[3] >> 32); compat->sig[6] = set->sig[3]; - case 3: compat->sig[5] = (set->sig[2] >> 32); compat->sig[4] = set->sig[2]; - case 2: compat->sig[3] = (set->sig[1] >> 32); compat->sig[2] = set->sig[1]; - case 1: compat->sig[1] = (set->sig[0] >> 32); compat->sig[0] = set->sig[0]; + case 4: v.sig[7] = (set->sig[3] >> 32); v.sig[6] = set->sig[3]; + case 3: v.sig[5] = (set->sig[2] >> 32); v.sig[4] = set->sig[2]; + case 2: v.sig[3] = (set->sig[1] >> 32); v.sig[2] = set->sig[1]; + case 1: v.sig[1] = (set->sig[0] >> 32); v.sig[0] = set->sig[0]; } + return copy_to_user(compat, &v, size) ? -EFAULT : 0; +#else + return copy_to_user(compat, set, size) ? -EFAULT : 0; +#endif } #ifdef CONFIG_NUMA diff --git a/kernel/signal.c b/kernel/signal.c index 800a18f77732..14ad6bb90dad 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2621,13 +2621,7 @@ COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset, if (error) return error; } - if (oset) { - compat_sigset_t old32; - sigset_to_compat(&old32, &old_set); - if (copy_to_user(oset, &old32, sizeof(compat_sigset_t))) - return -EFAULT; - } - return 0; + return oset ? put_compat_sigset(oset, &old_set, sizeof(*oset)) : 0; #else return sys_rt_sigprocmask(how, (sigset_t __user *)nset, (sigset_t __user *)oset, sigsetsize); @@ -2669,20 +2663,11 @@ SYSCALL_DEFINE2(rt_sigpending, sigset_t __user *, uset, size_t, sigsetsize) COMPAT_SYSCALL_DEFINE2(rt_sigpending, compat_sigset_t __user *, uset, compat_size_t, sigsetsize) { -#ifdef __BIG_ENDIAN sigset_t set; int err = do_sigpending(&set, sigsetsize); - if (!err) { - compat_sigset_t set32; - sigset_to_compat(&set32, &set); - /* we can get here only if sigsetsize <= sizeof(set) */ - if (copy_to_user(uset, &set32, sigsetsize)) - err = -EFAULT; - } + if (!err) + err = put_compat_sigset(uset, &set, sigsetsize); return err; -#else - return sys_rt_sigpending((sigset_t __user *)uset, sigsetsize); -#endif } #endif @@ -3451,7 +3436,6 @@ COMPAT_SYSCALL_DEFINE4(rt_sigaction, int, sig, compat_size_t, sigsetsize) { struct k_sigaction new_ka, old_ka; - compat_sigset_t mask; #ifdef __ARCH_HAS_SA_RESTORER compat_uptr_t restorer; #endif @@ -3463,6 +3447,7 @@ COMPAT_SYSCALL_DEFINE4(rt_sigaction, int, sig, if (act) { compat_uptr_t handler; + compat_sigset_t mask; ret = get_user(handler, &act->sa_handler); new_ka.sa.sa_handler = compat_ptr(handler); #ifdef __ARCH_HAS_SA_RESTORER @@ -3478,10 +3463,10 @@ COMPAT_SYSCALL_DEFINE4(rt_sigaction, int, sig, ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); if (!ret && oact) { - sigset_to_compat(&mask, &old_ka.sa.sa_mask); ret = put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler); - ret |= copy_to_user(&oact->sa_mask, &mask, sizeof(mask)); + ret |= put_compat_sigset(&oact->sa_mask, &old_ka.sa.sa_mask, + sizeof(oact->sa_mask)); ret |= put_user(old_ka.sa.sa_flags, &oact->sa_flags); #ifdef __ARCH_HAS_SA_RESTORER ret |= put_user(ptr_to_compat(old_ka.sa.sa_restorer), -- cgit v1.2.3-59-g8ed1b From b8e8e1aa9f14110da180569908bbe538c9e9dc63 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 Sep 2017 20:42:54 -0400 Subject: get rid of {get,put}_compat_itimerspec() no users left Signed-off-by: Al Viro --- include/linux/compat.h | 5 ----- kernel/compat.c | 18 ------------------ 2 files changed, 23 deletions(-) (limited to 'include') diff --git a/include/linux/compat.h b/include/linux/compat.h index ab1baa79abe8..21d30be5c0a5 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -443,11 +443,6 @@ static inline int compat_timespec_compare(struct compat_timespec *lhs, return lhs->tv_nsec - rhs->tv_nsec; } -extern int get_compat_itimerspec(struct itimerspec *dst, - const struct compat_itimerspec __user *src); -extern int put_compat_itimerspec(struct compat_itimerspec __user *dst, - const struct itimerspec *src); - asmlinkage long compat_sys_gettimeofday(struct compat_timeval __user *tv, struct timezone __user *tz); asmlinkage long compat_sys_settimeofday(struct compat_timeval __user *tv, diff --git a/kernel/compat.c b/kernel/compat.c index 18dd902c9052..d43b18031116 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -367,24 +367,6 @@ COMPAT_SYSCALL_DEFINE3(sched_getaffinity, compat_pid_t, pid, unsigned int, len, return ret; } -int get_compat_itimerspec(struct itimerspec *dst, - const struct compat_itimerspec __user *src) -{ - if (__compat_get_timespec(&dst->it_interval, &src->it_interval) || - __compat_get_timespec(&dst->it_value, &src->it_value)) - return -EFAULT; - return 0; -} - -int put_compat_itimerspec(struct compat_itimerspec __user *dst, - const struct itimerspec *src) -{ - if (__compat_put_timespec(&src->it_interval, &dst->it_interval) || - __compat_put_timespec(&src->it_value, &dst->it_value)) - return -EFAULT; - return 0; -} - int get_compat_itimerspec64(struct itimerspec64 *its, const struct compat_itimerspec __user *uits) { -- cgit v1.2.3-59-g8ed1b From 3968cf623892d710e651070243fd16af312a9797 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 3 Sep 2017 21:45:17 -0400 Subject: get_compat_sigset() similar to put_compat_sigset() Signed-off-by: Al Viro --- arch/sparc/kernel/sys_sparc32.c | 4 +--- fs/eventpoll.c | 4 +--- fs/select.c | 8 ++------ fs/signalfd.c | 4 +--- include/linux/compat.h | 2 +- kernel/compat.c | 23 ++++++++++++++++------- kernel/signal.c | 27 ++++----------------------- virt/kvm/kvm_main.c | 7 ++----- 8 files changed, 28 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c index 5e2bec9e41b2..34ece61ee970 100644 --- a/arch/sparc/kernel/sys_sparc32.c +++ b/arch/sparc/kernel/sys_sparc32.c @@ -166,13 +166,11 @@ COMPAT_SYSCALL_DEFINE5(rt_sigaction, int, sig, if (act) { u32 u_handler, u_restorer; - compat_sigset_t set32; new_ka.ka_restorer = restorer; ret = get_user(u_handler, &act->sa_handler); new_ka.sa.sa_handler = compat_ptr(u_handler); - ret |= copy_from_user(&set32, &act->sa_mask, sizeof(compat_sigset_t)); - sigset_from_compat(&new_ka.sa.sa_mask, &set32); + ret |= get_compat_sigset(&new_ka.sa.sa_mask, &act->sa_mask); ret |= get_user(new_ka.sa.sa_flags, &act->sa_flags); ret |= get_user(u_restorer, &act->sa_restorer); new_ka.sa.sa_restorer = compat_ptr(u_restorer); diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 2fabd19cdeea..396a3c075fd4 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -2259,7 +2259,6 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd, compat_size_t, sigsetsize) { long err; - compat_sigset_t csigmask; sigset_t ksigmask, sigsaved; /* @@ -2269,9 +2268,8 @@ COMPAT_SYSCALL_DEFINE6(epoll_pwait, int, epfd, if (sigmask) { if (sigsetsize != sizeof(compat_sigset_t)) return -EINVAL; - if (copy_from_user(&csigmask, sigmask, sizeof(csigmask))) + if (get_compat_sigset(&ksigmask, sigmask)) return -EFAULT; - sigset_from_compat(&ksigmask, &csigmask); sigsaved = current->blocked; set_current_blocked(&ksigmask); } diff --git a/fs/select.c b/fs/select.c index 20a7d061904f..9c980162c9fe 100644 --- a/fs/select.c +++ b/fs/select.c @@ -1301,7 +1301,6 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp, struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask, compat_size_t sigsetsize) { - compat_sigset_t ss32; sigset_t ksigmask, sigsaved; struct timespec64 ts, end_time, *to = NULL; int ret; @@ -1318,9 +1317,8 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp, if (sigmask) { if (sigsetsize != sizeof(compat_sigset_t)) return -EINVAL; - if (copy_from_user(&ss32, sigmask, sizeof(ss32))) + if (get_compat_sigset(&ksigmask, sigmask)) return -EFAULT; - sigset_from_compat(&ksigmask, &ss32); sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); @@ -1369,7 +1367,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds, struct compat_timespec __user *, tsp, const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize) { - compat_sigset_t ss32; sigset_t ksigmask, sigsaved; struct timespec64 ts, end_time, *to = NULL; int ret; @@ -1386,9 +1383,8 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, if (sigmask) { if (sigsetsize != sizeof(compat_sigset_t)) return -EINVAL; - if (copy_from_user(&ss32, sigmask, sizeof(ss32))) + if (get_compat_sigset(&ksigmask, sigmask)) return -EFAULT; - sigset_from_compat(&ksigmask, &ss32); sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); diff --git a/fs/signalfd.c b/fs/signalfd.c index d2c434112f42..9de5beeb771d 100644 --- a/fs/signalfd.c +++ b/fs/signalfd.c @@ -312,15 +312,13 @@ COMPAT_SYSCALL_DEFINE4(signalfd4, int, ufd, compat_size_t, sigsetsize, int, flags) { - compat_sigset_t ss32; sigset_t tmp; sigset_t __user *ksigmask; if (sigsetsize != sizeof(compat_sigset_t)) return -EINVAL; - if (copy_from_user(&ss32, sigmask, sizeof(ss32))) + if (get_compat_sigset(&tmp, sigmask)) return -EFAULT; - sigset_from_compat(&tmp, &ss32); ksigmask = compat_alloc_user_space(sizeof(sigset_t)); if (copy_to_user(ksigmask, &tmp, sizeof(sigset_t))) return -EFAULT; diff --git a/include/linux/compat.h b/include/linux/compat.h index 21d30be5c0a5..57cb6ecafa86 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -450,7 +450,7 @@ asmlinkage long compat_sys_settimeofday(struct compat_timeval __user *tv, asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp); -extern void sigset_from_compat(sigset_t *set, const compat_sigset_t *compat); +extern int get_compat_sigset(sigset_t *set, const compat_sigset_t __user *compat); extern int put_compat_sigset(compat_sigset_t __user *compat, const sigset_t *set, unsigned int size); diff --git a/kernel/compat.c b/kernel/compat.c index d43b18031116..a46a4a40bb8b 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -467,17 +467,26 @@ Efault: return -EFAULT; } -void -sigset_from_compat(sigset_t *set, const compat_sigset_t *compat) +int +get_compat_sigset(sigset_t *set, const compat_sigset_t __user *compat) { +#ifdef __BIG_ENDIAN + compat_sigset_t v; + if (copy_from_user(&v, compat, sizeof(compat_sigset_t))) + return -EFAULT; switch (_NSIG_WORDS) { - case 4: set->sig[3] = compat->sig[6] | (((long)compat->sig[7]) << 32 ); - case 3: set->sig[2] = compat->sig[4] | (((long)compat->sig[5]) << 32 ); - case 2: set->sig[1] = compat->sig[2] | (((long)compat->sig[3]) << 32 ); - case 1: set->sig[0] = compat->sig[0] | (((long)compat->sig[1]) << 32 ); + case 4: set->sig[3] = v.sig[6] | (((long)v.sig[7]) << 32 ); + case 3: set->sig[2] = v.sig[4] | (((long)v.sig[5]) << 32 ); + case 2: set->sig[1] = v.sig[2] | (((long)v.sig[3]) << 32 ); + case 1: set->sig[0] = v.sig[0] | (((long)v.sig[1]) << 32 ); } +#else + if (copy_from_user(set, compat, sizeof(compat_sigset_t))) + return -EFAULT; +#endif + return 0; } -EXPORT_SYMBOL_GPL(sigset_from_compat); +EXPORT_SYMBOL_GPL(get_compat_sigset); int put_compat_sigset(compat_sigset_t __user *compat, const sigset_t *set, diff --git a/kernel/signal.c b/kernel/signal.c index 9fbc574ced10..36a523640894 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2600,7 +2600,6 @@ SYSCALL_DEFINE4(rt_sigprocmask, int, how, sigset_t __user *, nset, COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset, compat_sigset_t __user *, oset, compat_size_t, sigsetsize) { -#ifdef __BIG_ENDIAN sigset_t old_set = current->blocked; /* XXX: Don't preclude handling different sized sigset_t's. */ @@ -2608,13 +2607,10 @@ COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset, return -EINVAL; if (nset) { - compat_sigset_t new32; sigset_t new_set; int error; - if (copy_from_user(&new32, nset, sizeof(compat_sigset_t))) + if (get_compat_sigset(&new_set, nset)) return -EFAULT; - - sigset_from_compat(&new_set, &new32); sigdelsetmask(&new_set, sigmask(SIGKILL)|sigmask(SIGSTOP)); error = sigprocmask(how, &new_set, NULL); @@ -2622,10 +2618,6 @@ COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset, return error; } return oset ? put_compat_sigset(oset, &old_set, sizeof(*oset)) : 0; -#else - return sys_rt_sigprocmask(how, (sigset_t __user *)nset, - (sigset_t __user *)oset, sigsetsize); -#endif } #endif @@ -2908,7 +2900,6 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, compat_sigset_t __user *, uthese, struct compat_siginfo __user *, uinfo, struct compat_timespec __user *, uts, compat_size_t, sigsetsize) { - compat_sigset_t s32; sigset_t s; struct timespec t; siginfo_t info; @@ -2917,9 +2908,8 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, compat_sigset_t __user *, uthese, if (sigsetsize != sizeof(sigset_t)) return -EINVAL; - if (copy_from_user(&s32, uthese, sizeof(compat_sigset_t))) + if (get_compat_sigset(&s, uthese)) return -EFAULT; - sigset_from_compat(&s, &s32); if (uts) { if (compat_get_timespec(&t, uts)) @@ -3450,18 +3440,16 @@ COMPAT_SYSCALL_DEFINE4(rt_sigaction, int, sig, if (act) { compat_uptr_t handler; - compat_sigset_t mask; ret = get_user(handler, &act->sa_handler); new_ka.sa.sa_handler = compat_ptr(handler); #ifdef __ARCH_HAS_SA_RESTORER ret |= get_user(restorer, &act->sa_restorer); new_ka.sa.sa_restorer = compat_ptr(restorer); #endif - ret |= copy_from_user(&mask, &act->sa_mask, sizeof(mask)); + ret |= get_compat_sigset(&new_ka.sa.sa_mask, &act->sa_mask); ret |= get_user(new_ka.sa.sa_flags, &act->sa_flags); if (ret) return -EFAULT; - sigset_from_compat(&new_ka.sa.sa_mask, &mask); } ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); @@ -3649,22 +3637,15 @@ SYSCALL_DEFINE2(rt_sigsuspend, sigset_t __user *, unewset, size_t, sigsetsize) #ifdef CONFIG_COMPAT COMPAT_SYSCALL_DEFINE2(rt_sigsuspend, compat_sigset_t __user *, unewset, compat_size_t, sigsetsize) { -#ifdef __BIG_ENDIAN sigset_t newset; - compat_sigset_t newset32; /* XXX: Don't preclude handling different sized sigset_t's. */ if (sigsetsize != sizeof(sigset_t)) return -EINVAL; - if (copy_from_user(&newset32, unewset, sizeof(compat_sigset_t))) + if (get_compat_sigset(&newset, unewset)) return -EFAULT; - sigset_from_compat(&newset, &newset32); return sigsuspend(&newset); -#else - /* on little-endian bitmaps don't care about granularity */ - return sys_rt_sigsuspend((sigset_t __user *)unewset, sigsetsize); -#endif } #endif diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 9deb5a245b83..99bfe50a0589 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2724,7 +2724,6 @@ static long kvm_vcpu_compat_ioctl(struct file *filp, case KVM_SET_SIGNAL_MASK: { struct kvm_signal_mask __user *sigmask_arg = argp; struct kvm_signal_mask kvm_sigmask; - compat_sigset_t csigset; sigset_t sigset; if (argp) { @@ -2733,13 +2732,11 @@ static long kvm_vcpu_compat_ioctl(struct file *filp, sizeof(kvm_sigmask))) goto out; r = -EINVAL; - if (kvm_sigmask.len != sizeof(csigset)) + if (kvm_sigmask.len != sizeof(compat_sigset_t)) goto out; r = -EFAULT; - if (copy_from_user(&csigset, sigmask_arg->sigset, - sizeof(csigset))) + if (get_compat_sigset(&sigset, (void *)sigmask_arg->sigset)) goto out; - sigset_from_compat(&sigset, &csigset); r = kvm_vcpu_ioctl_set_sigmask(vcpu, &sigset); } else r = kvm_vcpu_ioctl_set_sigmask(vcpu, NULL); -- cgit v1.2.3-59-g8ed1b From bffa72cf7f9df842f0016ba03586039296b4caaf Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 19 Sep 2017 05:14:24 -0700 Subject: net: sk_buff rbnode reorg skb->rbnode shares space with skb->next, skb->prev and skb->tstamp Current uses (TCP receive ofo queue and netem) need to save/restore tstamp, while skb->dev is either NULL (TCP) or a constant for a given queue (netem). Since we plan using an RB tree for TCP retransmit queue to speedup SACK processing with large BDP, this patch exchanges skb->dev and skb->tstamp. This saves some overhead in both TCP and netem. v2: removes the swtstamp field from struct tcp_skb_cb Signed-off-by: Eric Dumazet Cc: Soheil Hassas Yeganeh Cc: Wei Wang Cc: Willem de Bruijn Acked-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- include/linux/skbuff.h | 16 ++++++++-------- include/net/tcp.h | 6 ------ net/ipv4/tcp_input.c | 27 +++++---------------------- net/sched/sch_netem.c | 7 ++++--- 4 files changed, 17 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 72299ef00061..492828801acb 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -661,8 +661,12 @@ struct sk_buff { struct sk_buff *prev; union { - ktime_t tstamp; - u64 skb_mstamp; + struct net_device *dev; + /* Some protocols might use this space to store information, + * while device pointer would be NULL. + * UDP receive path is one user. + */ + unsigned long dev_scratch; }; }; struct rb_node rbnode; /* used in netem & tcp stack */ @@ -670,12 +674,8 @@ struct sk_buff { struct sock *sk; union { - struct net_device *dev; - /* Some protocols might use this space to store information, - * while device pointer would be NULL. - * UDP receive path is one user. - */ - unsigned long dev_scratch; + ktime_t tstamp; + u64 skb_mstamp; }; /* * This is the control buffer. It is free to use for every diff --git a/include/net/tcp.h b/include/net/tcp.h index b510f284427a..49a8a46466f3 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -797,12 +797,6 @@ struct tcp_skb_cb { u16 tcp_gso_segs; u16 tcp_gso_size; }; - - /* Used to stash the receive timestamp while this skb is in the - * out of order queue, as skb->tstamp is overwritten by the - * rbnode. - */ - ktime_t swtstamp; }; __u8 tcp_flags; /* TCP header flags. (tcp[13]) */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index bddf724f5c02..db9bb46b5776 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4266,11 +4266,6 @@ static void tcp_sack_remove(struct tcp_sock *tp) tp->rx_opt.num_sacks = num_sacks; } -enum tcp_queue { - OOO_QUEUE, - RCV_QUEUE, -}; - /** * tcp_try_coalesce - try to merge skb to prior one * @sk: socket @@ -4286,7 +4281,6 @@ enum tcp_queue { * Returns true if caller should free @from instead of queueing it */ static bool tcp_try_coalesce(struct sock *sk, - enum tcp_queue dest, struct sk_buff *to, struct sk_buff *from, bool *fragstolen) @@ -4311,10 +4305,7 @@ static bool tcp_try_coalesce(struct sock *sk, if (TCP_SKB_CB(from)->has_rxtstamp) { TCP_SKB_CB(to)->has_rxtstamp = true; - if (dest == OOO_QUEUE) - TCP_SKB_CB(to)->swtstamp = TCP_SKB_CB(from)->swtstamp; - else - to->tstamp = from->tstamp; + to->tstamp = from->tstamp; } return true; @@ -4351,9 +4342,6 @@ static void tcp_ofo_queue(struct sock *sk) } p = rb_next(p); rb_erase(&skb->rbnode, &tp->out_of_order_queue); - /* Replace tstamp which was stomped by rbnode */ - if (TCP_SKB_CB(skb)->has_rxtstamp) - skb->tstamp = TCP_SKB_CB(skb)->swtstamp; if (unlikely(!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt))) { SOCK_DEBUG(sk, "ofo packet was already received\n"); @@ -4365,8 +4353,7 @@ static void tcp_ofo_queue(struct sock *sk) TCP_SKB_CB(skb)->end_seq); tail = skb_peek_tail(&sk->sk_receive_queue); - eaten = tail && tcp_try_coalesce(sk, RCV_QUEUE, - tail, skb, &fragstolen); + eaten = tail && tcp_try_coalesce(sk, tail, skb, &fragstolen); tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq); fin = TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN; if (!eaten) @@ -4420,10 +4407,6 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) return; } - /* Stash tstamp to avoid being stomped on by rbnode */ - if (TCP_SKB_CB(skb)->has_rxtstamp) - TCP_SKB_CB(skb)->swtstamp = skb->tstamp; - /* Disable header prediction. */ tp->pred_flags = 0; inet_csk_schedule_ack(sk); @@ -4451,7 +4434,7 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) /* In the typical case, we are adding an skb to the end of the list. * Use of ooo_last_skb avoids the O(Log(N)) rbtree lookup. */ - if (tcp_try_coalesce(sk, OOO_QUEUE, tp->ooo_last_skb, + if (tcp_try_coalesce(sk, tp->ooo_last_skb, skb, &fragstolen)) { coalesce_done: tcp_grow_window(sk, skb); @@ -4502,7 +4485,7 @@ coalesce_done: __kfree_skb(skb1); goto merge_right; } - } else if (tcp_try_coalesce(sk, OOO_QUEUE, skb1, + } else if (tcp_try_coalesce(sk, skb1, skb, &fragstolen)) { goto coalesce_done; } @@ -4554,7 +4537,7 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int __skb_pull(skb, hdrlen); eaten = (tail && - tcp_try_coalesce(sk, RCV_QUEUE, tail, + tcp_try_coalesce(sk, tail, skb, fragstolen)) ? 1 : 0; tcp_rcv_nxt_update(tcp_sk(sk), TCP_SKB_CB(skb)->end_seq); if (!eaten) { diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index b1266e75ca43..063a4bdb9ee6 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -146,7 +146,6 @@ struct netem_sched_data { */ struct netem_skb_cb { psched_time_t time_to_send; - ktime_t tstamp_save; }; @@ -561,7 +560,6 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, } cb->time_to_send = now + delay; - cb->tstamp_save = skb->tstamp; ++q->counter; tfifo_enqueue(skb, sch); } else { @@ -629,7 +627,10 @@ deliver: qdisc_qstats_backlog_dec(sch, skb); skb->next = NULL; skb->prev = NULL; - skb->tstamp = netem_skb_cb(skb)->tstamp_save; + /* skb->dev shares skb->rbnode area, + * we need to restore its value. + */ + skb->dev = qdisc_dev(sch); #ifdef CONFIG_NET_CLS_ACT /* -- cgit v1.2.3-59-g8ed1b From f5619866592c65adc087364cc1a3ba709201ea26 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 19 Sep 2017 11:56:57 -0400 Subject: net: dsa: remove copy of master ethtool_ops There is no need to store a copy of the master ethtool ops, storing the original pointer in DSA and the new one in the master netdev itself is enough. In the meantime, set orig_ethtool_ops to NULL when restoring the master ethtool ops and check the presence of the master original ethtool ops as well as its needed functions before calling them. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 1 - net/dsa/dsa.c | 8 ++++---- net/dsa/slave.c | 19 +++++++++++-------- 3 files changed, 15 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index dd44d6ce1097..8dee216a5a9b 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -188,7 +188,6 @@ struct dsa_port { /* * Original copy of the master netdev ethtool_ops */ - struct ethtool_ops ethtool_ops; const struct ethtool_ops *orig_ethtool_ops; }; diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 03c58b0eb082..abadf7b49236 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -124,11 +124,10 @@ int dsa_cpu_port_ethtool_setup(struct dsa_port *cpu_dp) if (!cpu_ops) return -ENOMEM; - memcpy(&cpu_dp->ethtool_ops, master->ethtool_ops, - sizeof(struct ethtool_ops)); cpu_dp->orig_ethtool_ops = master->ethtool_ops; - memcpy(cpu_ops, &cpu_dp->ethtool_ops, - sizeof(struct ethtool_ops)); + if (cpu_dp->orig_ethtool_ops) + memcpy(cpu_ops, cpu_dp->orig_ethtool_ops, sizeof(*cpu_ops)); + dsa_cpu_port_ethtool_init(cpu_ops); master->ethtool_ops = cpu_ops; @@ -138,6 +137,7 @@ int dsa_cpu_port_ethtool_setup(struct dsa_port *cpu_dp) void dsa_cpu_port_ethtool_restore(struct dsa_port *cpu_dp) { cpu_dp->netdev->ethtool_ops = cpu_dp->orig_ethtool_ops; + cpu_dp->orig_ethtool_ops = NULL; } void dsa_cpu_dsa_destroy(struct dsa_port *port) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 2afa99506f8b..2ff4f907d137 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -574,12 +574,13 @@ static void dsa_cpu_port_get_ethtool_stats(struct net_device *dev, struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_port *cpu_dp = dsa_get_cpu_port(dst); struct dsa_switch *ds = cpu_dp->ds; + const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; s8 cpu_port = cpu_dp->index; int count = 0; - if (cpu_dp->ethtool_ops.get_sset_count) { - count = cpu_dp->ethtool_ops.get_sset_count(dev, ETH_SS_STATS); - cpu_dp->ethtool_ops.get_ethtool_stats(dev, stats, data); + if (ops && ops->get_sset_count && ops->get_ethtool_stats) { + count = ops->get_sset_count(dev, ETH_SS_STATS); + ops->get_ethtool_stats(dev, stats, data); } if (ds->ops->get_ethtool_stats) @@ -591,10 +592,11 @@ static int dsa_cpu_port_get_sset_count(struct net_device *dev, int sset) struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_port *cpu_dp = dsa_get_cpu_port(dst); struct dsa_switch *ds = cpu_dp->ds; + const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; int count = 0; - if (cpu_dp->ethtool_ops.get_sset_count) - count += cpu_dp->ethtool_ops.get_sset_count(dev, sset); + if (ops && ops->get_sset_count) + count += ops->get_sset_count(dev, sset); if (sset == ETH_SS_STATS && ds->ops->get_sset_count) count += ds->ops->get_sset_count(ds); @@ -608,6 +610,7 @@ static void dsa_cpu_port_get_strings(struct net_device *dev, struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_port *cpu_dp = dsa_get_cpu_port(dst); struct dsa_switch *ds = cpu_dp->ds; + const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; s8 cpu_port = cpu_dp->index; int len = ETH_GSTRING_LEN; int mcount = 0, count; @@ -619,9 +622,9 @@ static void dsa_cpu_port_get_strings(struct net_device *dev, /* We do not want to be NULL-terminated, since this is a prefix */ pfx[sizeof(pfx) - 1] = '_'; - if (cpu_dp->ethtool_ops.get_sset_count) { - mcount = cpu_dp->ethtool_ops.get_sset_count(dev, ETH_SS_STATS); - cpu_dp->ethtool_ops.get_strings(dev, stringset, data); + if (ops && ops->get_sset_count && ops->get_strings) { + mcount = ops->get_sset_count(dev, ETH_SS_STATS); + ops->get_strings(dev, stringset, data); } if (stringset == ETH_SS_STATS && ds->ops->get_strings) { -- cgit v1.2.3-59-g8ed1b From 752fbcc33405d6f8249465e4b2c4e420091bb825 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Tue, 19 Sep 2017 13:15:42 -0700 Subject: net_sched: no need to free qdisc in RCU callback gen estimator has been rewritten in commit 1c0d32fde5bd ("net_sched: gen_estimator: complete rewrite of rate estimators"), the caller no longer needs to wait for a grace period. So this patch gets rid of it. Cc: Jamal Hadi Salim Cc: Eric Dumazet Signed-off-by: Cong Wang Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sch_generic.h | 1 - net/sched/sch_generic.c | 10 ++-------- 2 files changed, 2 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 135f5a2dd931..684d8ed27eaa 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -93,7 +93,6 @@ struct Qdisc { unsigned long state; struct Qdisc *next_sched; struct sk_buff *skb_bad_txq; - struct rcu_head rcu_head; int padded; refcount_t refcnt; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 92237e75dbbc..1fb0c754b7fd 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -688,10 +688,8 @@ void qdisc_reset(struct Qdisc *qdisc) } EXPORT_SYMBOL(qdisc_reset); -static void qdisc_rcu_free(struct rcu_head *head) +static void qdisc_free(struct Qdisc *qdisc) { - struct Qdisc *qdisc = container_of(head, struct Qdisc, rcu_head); - if (qdisc_is_percpu_stats(qdisc)) { free_percpu(qdisc->cpu_bstats); free_percpu(qdisc->cpu_qstats); @@ -724,11 +722,7 @@ void qdisc_destroy(struct Qdisc *qdisc) kfree_skb_list(qdisc->gso_skb); kfree_skb(qdisc->skb_bad_txq); - /* - * gen_estimator est_timer() might access qdisc->q.lock, - * wait a RCU grace period before freeing qdisc. - */ - call_rcu(&qdisc->rcu_head, qdisc_rcu_free); + qdisc_free(qdisc); } EXPORT_SYMBOL(qdisc_destroy); -- cgit v1.2.3-59-g8ed1b From a90c9347e90ed1e9323d71402ed18023bc910cd8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 19 Sep 2017 16:27:06 -0700 Subject: ipv6: addrlabel: per netns list Having a global list of labels do not scale to thousands of netns in the cloud era. This causes quadratic behavior on netns creation and deletion. This is time having a per netns list of ~10 labels. Tested: $ time perf record (for f in `seq 1 3000` ; do ip netns add tast$f; done) [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 3.637 MB perf.data (~158898 samples) ] real 0m20.837s # instead of 0m24.227s user 0m0.328s sys 0m20.338s # instead of 0m23.753s 16.17% ip [kernel.kallsyms] [k] netlink_broadcast_filtered 12.30% ip [kernel.kallsyms] [k] netlink_has_listeners 6.76% ip [kernel.kallsyms] [k] _raw_spin_lock_irqsave 5.78% ip [kernel.kallsyms] [k] memset_erms 5.77% ip [kernel.kallsyms] [k] kobject_uevent_env 5.18% ip [kernel.kallsyms] [k] refcount_sub_and_test 4.96% ip [kernel.kallsyms] [k] _raw_read_lock 3.82% ip [kernel.kallsyms] [k] refcount_inc_not_zero 3.33% ip [kernel.kallsyms] [k] _raw_spin_unlock_irqrestore 2.11% ip [kernel.kallsyms] [k] unmap_page_range 1.77% ip [kernel.kallsyms] [k] __wake_up 1.69% ip [kernel.kallsyms] [k] strlen 1.17% ip [kernel.kallsyms] [k] __wake_up_common 1.09% ip [kernel.kallsyms] [k] insert_header 1.04% ip [kernel.kallsyms] [k] page_remove_rmap 1.01% ip [kernel.kallsyms] [k] consume_skb 0.98% ip [kernel.kallsyms] [k] netlink_trim 0.51% ip [kernel.kallsyms] [k] kernfs_link_sibling 0.51% ip [kernel.kallsyms] [k] filemap_map_pages 0.46% ip [kernel.kallsyms] [k] memcpy_erms Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv6.h | 5 +++ net/ipv6/addrlabel.c | 81 ++++++++++++++++++------------------------------ 2 files changed, 35 insertions(+), 51 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 2544f9760a42..2ea1ed341ef8 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -89,6 +89,11 @@ struct netns_ipv6 { atomic_t fib6_sernum; struct seg6_pernet_data *seg6_data; struct fib_notifier_ops *notifier_ops; + struct { + struct hlist_head head; + spinlock_t lock; + u32 seq; + } ip6addrlbl_table; }; #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index b055bc79f56d..c6311d7108f6 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -30,7 +30,6 @@ * Policy Table */ struct ip6addrlbl_entry { - possible_net_t lbl_net; struct in6_addr prefix; int prefixlen; int ifindex; @@ -41,19 +40,6 @@ struct ip6addrlbl_entry { struct rcu_head rcu; }; -static struct ip6addrlbl_table -{ - struct hlist_head head; - spinlock_t lock; - u32 seq; -} ip6addrlbl_table; - -static inline -struct net *ip6addrlbl_net(const struct ip6addrlbl_entry *lbl) -{ - return read_pnet(&lbl->lbl_net); -} - /* * Default policy table (RFC6724 + extensions) * @@ -148,13 +134,10 @@ static inline void ip6addrlbl_put(struct ip6addrlbl_entry *p) } /* Find label */ -static bool __ip6addrlbl_match(struct net *net, - const struct ip6addrlbl_entry *p, +static bool __ip6addrlbl_match(const struct ip6addrlbl_entry *p, const struct in6_addr *addr, int addrtype, int ifindex) { - if (!net_eq(ip6addrlbl_net(p), net)) - return false; if (p->ifindex && p->ifindex != ifindex) return false; if (p->addrtype && p->addrtype != addrtype) @@ -169,8 +152,9 @@ static struct ip6addrlbl_entry *__ipv6_addr_label(struct net *net, int type, int ifindex) { struct ip6addrlbl_entry *p; - hlist_for_each_entry_rcu(p, &ip6addrlbl_table.head, list) { - if (__ip6addrlbl_match(net, p, addr, type, ifindex)) + + hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) { + if (__ip6addrlbl_match(p, addr, type, ifindex)) return p; } return NULL; @@ -196,8 +180,7 @@ u32 ipv6_addr_label(struct net *net, } /* allocate one entry */ -static struct ip6addrlbl_entry *ip6addrlbl_alloc(struct net *net, - const struct in6_addr *prefix, +static struct ip6addrlbl_entry *ip6addrlbl_alloc(const struct in6_addr *prefix, int prefixlen, int ifindex, u32 label) { @@ -236,24 +219,23 @@ static struct ip6addrlbl_entry *ip6addrlbl_alloc(struct net *net, newp->addrtype = addrtype; newp->label = label; INIT_HLIST_NODE(&newp->list); - write_pnet(&newp->lbl_net, net); refcount_set(&newp->refcnt, 1); return newp; } /* add a label */ -static int __ip6addrlbl_add(struct ip6addrlbl_entry *newp, int replace) +static int __ip6addrlbl_add(struct net *net, struct ip6addrlbl_entry *newp, + int replace) { - struct hlist_node *n; struct ip6addrlbl_entry *last = NULL, *p = NULL; + struct hlist_node *n; int ret = 0; ADDRLABEL(KERN_DEBUG "%s(newp=%p, replace=%d)\n", __func__, newp, replace); - hlist_for_each_entry_safe(p, n, &ip6addrlbl_table.head, list) { + hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { if (p->prefixlen == newp->prefixlen && - net_eq(ip6addrlbl_net(p), ip6addrlbl_net(newp)) && p->ifindex == newp->ifindex && ipv6_addr_equal(&p->prefix, &newp->prefix)) { if (!replace) { @@ -273,10 +255,10 @@ static int __ip6addrlbl_add(struct ip6addrlbl_entry *newp, int replace) if (last) hlist_add_behind_rcu(&newp->list, &last->list); else - hlist_add_head_rcu(&newp->list, &ip6addrlbl_table.head); + hlist_add_head_rcu(&newp->list, &net->ipv6.ip6addrlbl_table.head); out: if (!ret) - ip6addrlbl_table.seq++; + net->ipv6.ip6addrlbl_table.seq++; return ret; } @@ -292,12 +274,12 @@ static int ip6addrlbl_add(struct net *net, __func__, prefix, prefixlen, ifindex, (unsigned int)label, replace); - newp = ip6addrlbl_alloc(net, prefix, prefixlen, ifindex, label); + newp = ip6addrlbl_alloc(prefix, prefixlen, ifindex, label); if (IS_ERR(newp)) return PTR_ERR(newp); - spin_lock(&ip6addrlbl_table.lock); - ret = __ip6addrlbl_add(newp, replace); - spin_unlock(&ip6addrlbl_table.lock); + spin_lock(&net->ipv6.ip6addrlbl_table.lock); + ret = __ip6addrlbl_add(net, newp, replace); + spin_unlock(&net->ipv6.ip6addrlbl_table.lock); if (ret) ip6addrlbl_free(newp); return ret; @@ -315,9 +297,8 @@ static int __ip6addrlbl_del(struct net *net, ADDRLABEL(KERN_DEBUG "%s(prefix=%pI6, prefixlen=%d, ifindex=%d)\n", __func__, prefix, prefixlen, ifindex); - hlist_for_each_entry_safe(p, n, &ip6addrlbl_table.head, list) { + hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { if (p->prefixlen == prefixlen && - net_eq(ip6addrlbl_net(p), net) && p->ifindex == ifindex && ipv6_addr_equal(&p->prefix, prefix)) { hlist_del_rcu(&p->list); @@ -340,9 +321,9 @@ static int ip6addrlbl_del(struct net *net, __func__, prefix, prefixlen, ifindex); ipv6_addr_prefix(&prefix_buf, prefix, prefixlen); - spin_lock(&ip6addrlbl_table.lock); + spin_lock(&net->ipv6.ip6addrlbl_table.lock); ret = __ip6addrlbl_del(net, &prefix_buf, prefixlen, ifindex); - spin_unlock(&ip6addrlbl_table.lock); + spin_unlock(&net->ipv6.ip6addrlbl_table.lock); return ret; } @@ -354,6 +335,9 @@ static int __net_init ip6addrlbl_net_init(struct net *net) ADDRLABEL(KERN_DEBUG "%s\n", __func__); + spin_lock_init(&net->ipv6.ip6addrlbl_table.lock); + INIT_HLIST_HEAD(&net->ipv6.ip6addrlbl_table.head); + for (i = 0; i < ARRAY_SIZE(ip6addrlbl_init_table); i++) { int ret = ip6addrlbl_add(net, ip6addrlbl_init_table[i].prefix, @@ -373,14 +357,12 @@ static void __net_exit ip6addrlbl_net_exit(struct net *net) struct hlist_node *n; /* Remove all labels belonging to the exiting net */ - spin_lock(&ip6addrlbl_table.lock); - hlist_for_each_entry_safe(p, n, &ip6addrlbl_table.head, list) { - if (net_eq(ip6addrlbl_net(p), net)) { - hlist_del_rcu(&p->list); - ip6addrlbl_put(p); - } + spin_lock(&net->ipv6.ip6addrlbl_table.lock); + hlist_for_each_entry_safe(p, n, &net->ipv6.ip6addrlbl_table.head, list) { + hlist_del_rcu(&p->list); + ip6addrlbl_put(p); } - spin_unlock(&ip6addrlbl_table.lock); + spin_unlock(&net->ipv6.ip6addrlbl_table.lock); } static struct pernet_operations ipv6_addr_label_ops = { @@ -390,8 +372,6 @@ static struct pernet_operations ipv6_addr_label_ops = { int __init ipv6_addr_label_init(void) { - spin_lock_init(&ip6addrlbl_table.lock); - return register_pernet_subsys(&ipv6_addr_label_ops); } @@ -510,11 +490,10 @@ static int ip6addrlbl_dump(struct sk_buff *skb, struct netlink_callback *cb) int err; rcu_read_lock(); - hlist_for_each_entry_rcu(p, &ip6addrlbl_table.head, list) { - if (idx >= s_idx && - net_eq(ip6addrlbl_net(p), net)) { + hlist_for_each_entry_rcu(p, &net->ipv6.ip6addrlbl_table.head, list) { + if (idx >= s_idx) { err = ip6addrlbl_fill(skb, p, - ip6addrlbl_table.seq, + net->ipv6.ip6addrlbl_table.seq, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, RTM_NEWADDRLABEL, @@ -571,7 +550,7 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, p = __ipv6_addr_label(net, addr, ipv6_addr_type(addr), ifal->ifal_index); if (p && !ip6addrlbl_hold(p)) p = NULL; - lseq = ip6addrlbl_table.seq; + lseq = net->ipv6.ip6addrlbl_table.seq; rcu_read_unlock(); if (!p) { -- cgit v1.2.3-59-g8ed1b From 64bc17811b72758753e2b64cd8f2a63812c61fe1 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 19 Sep 2017 16:27:09 -0700 Subject: ipv4: speedup ipv6 tunnels dismantle Implement exit_batch() method to dismantle more devices per round. (rtnl_lock() ... unregister_netdevice_many() ... rtnl_unlock()) Tested: $ cat add_del_unshare.sh for i in `seq 1 40` do (for j in `seq 1 100` ; do unshare -n /bin/true >/dev/null ; done) & done wait ; grep net_namespace /proc/slabinfo Before patch : $ time ./add_del_unshare.sh net_namespace 126 282 5504 1 2 : tunables 8 4 0 : slabdata 126 282 0 real 1m38.965s user 0m0.688s sys 0m37.017s After patch: $ time ./add_del_unshare.sh net_namespace 135 291 5504 1 2 : tunables 8 4 0 : slabdata 135 291 0 real 0m22.117s user 0m0.728s sys 0m35.328s Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 3 ++- net/ipv4/ip_gre.c | 22 +++++++++------------- net/ipv4/ip_tunnel.c | 12 +++++++++--- net/ipv4/ip_vti.c | 7 +++---- net/ipv4/ipip.c | 7 +++---- 5 files changed, 26 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 992652856fe8..b41a1e057fce 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -258,7 +258,8 @@ int ip_tunnel_get_iflink(const struct net_device *dev); int ip_tunnel_init_net(struct net *net, unsigned int ip_tnl_net_id, struct rtnl_link_ops *ops, char *devname); -void ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops); +void ip_tunnel_delete_nets(struct list_head *list_net, unsigned int id, + struct rtnl_link_ops *ops); void ip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev, const struct iphdr *tnl_params, const u8 protocol); diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 0162fb955b33..9cee986ac6b8 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1013,15 +1013,14 @@ static int __net_init ipgre_init_net(struct net *net) return ip_tunnel_init_net(net, ipgre_net_id, &ipgre_link_ops, NULL); } -static void __net_exit ipgre_exit_net(struct net *net) +static void __net_exit ipgre_exit_batch_net(struct list_head *list_net) { - struct ip_tunnel_net *itn = net_generic(net, ipgre_net_id); - ip_tunnel_delete_net(itn, &ipgre_link_ops); + ip_tunnel_delete_nets(list_net, ipgre_net_id, &ipgre_link_ops); } static struct pernet_operations ipgre_net_ops = { .init = ipgre_init_net, - .exit = ipgre_exit_net, + .exit_batch = ipgre_exit_batch_net, .id = &ipgre_net_id, .size = sizeof(struct ip_tunnel_net), }; @@ -1540,15 +1539,14 @@ static int __net_init ipgre_tap_init_net(struct net *net) return ip_tunnel_init_net(net, gre_tap_net_id, &ipgre_tap_ops, "gretap0"); } -static void __net_exit ipgre_tap_exit_net(struct net *net) +static void __net_exit ipgre_tap_exit_batch_net(struct list_head *list_net) { - struct ip_tunnel_net *itn = net_generic(net, gre_tap_net_id); - ip_tunnel_delete_net(itn, &ipgre_tap_ops); + ip_tunnel_delete_nets(list_net, gre_tap_net_id, &ipgre_tap_ops); } static struct pernet_operations ipgre_tap_net_ops = { .init = ipgre_tap_init_net, - .exit = ipgre_tap_exit_net, + .exit_batch = ipgre_tap_exit_batch_net, .id = &gre_tap_net_id, .size = sizeof(struct ip_tunnel_net), }; @@ -1559,16 +1557,14 @@ static int __net_init erspan_init_net(struct net *net) &erspan_link_ops, "erspan0"); } -static void __net_exit erspan_exit_net(struct net *net) +static void __net_exit erspan_exit_batch_net(struct list_head *net_list) { - struct ip_tunnel_net *itn = net_generic(net, erspan_net_id); - - ip_tunnel_delete_net(itn, &erspan_link_ops); + ip_tunnel_delete_nets(net_list, erspan_net_id, &erspan_link_ops); } static struct pernet_operations erspan_net_ops = { .init = erspan_init_net, - .exit = erspan_exit_net, + .exit_batch = erspan_exit_batch_net, .id = &erspan_net_id, .size = sizeof(struct ip_tunnel_net), }; diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c index e9805ad664ac..fe6fee728ce4 100644 --- a/net/ipv4/ip_tunnel.c +++ b/net/ipv4/ip_tunnel.c @@ -1061,16 +1061,22 @@ static void ip_tunnel_destroy(struct ip_tunnel_net *itn, struct list_head *head, } } -void ip_tunnel_delete_net(struct ip_tunnel_net *itn, struct rtnl_link_ops *ops) +void ip_tunnel_delete_nets(struct list_head *net_list, unsigned int id, + struct rtnl_link_ops *ops) { + struct ip_tunnel_net *itn; + struct net *net; LIST_HEAD(list); rtnl_lock(); - ip_tunnel_destroy(itn, &list, ops); + list_for_each_entry(net, net_list, exit_list) { + itn = net_generic(net, id); + ip_tunnel_destroy(itn, &list, ops); + } unregister_netdevice_many(&list); rtnl_unlock(); } -EXPORT_SYMBOL_GPL(ip_tunnel_delete_net); +EXPORT_SYMBOL_GPL(ip_tunnel_delete_nets); int ip_tunnel_newlink(struct net_device *dev, struct nlattr *tb[], struct ip_tunnel_parm *p, __u32 fwmark) diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 5ed63d250950..02d70ca99db1 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -452,15 +452,14 @@ static int __net_init vti_init_net(struct net *net) return 0; } -static void __net_exit vti_exit_net(struct net *net) +static void __net_exit vti_exit_batch_net(struct list_head *list_net) { - struct ip_tunnel_net *itn = net_generic(net, vti_net_id); - ip_tunnel_delete_net(itn, &vti_link_ops); + ip_tunnel_delete_nets(list_net, vti_net_id, &vti_link_ops); } static struct pernet_operations vti_net_ops = { .init = vti_init_net, - .exit = vti_exit_net, + .exit_batch = vti_exit_batch_net, .id = &vti_net_id, .size = sizeof(struct ip_tunnel_net), }; diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index fb1ad22b5e29..1e47818e38c7 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -634,15 +634,14 @@ static int __net_init ipip_init_net(struct net *net) return ip_tunnel_init_net(net, ipip_net_id, &ipip_link_ops, "tunl0"); } -static void __net_exit ipip_exit_net(struct net *net) +static void __net_exit ipip_exit_batch_net(struct list_head *list_net) { - struct ip_tunnel_net *itn = net_generic(net, ipip_net_id); - ip_tunnel_delete_net(itn, &ipip_link_ops); + ip_tunnel_delete_nets(list_net, ipip_net_id, &ipip_link_ops); } static struct pernet_operations ipip_net_ops = { .init = ipip_init_net, - .exit = ipip_exit_net, + .exit_batch = ipip_exit_batch_net, .id = &ipip_net_id, .size = sizeof(struct ip_tunnel_net), }; -- cgit v1.2.3-59-g8ed1b From 840bc44849b9d7f8eaae34ac7b434e17c4cf7368 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 20 Sep 2017 06:08:42 +0000 Subject: ASoC: add missing snd_soc_component_set_jack commit 44c07365e9e2 ("ASoC: add Component level set_jack") added new snd_soc_component_set_jack(), but it didn't add definition on soc.h. This patch adds it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d22de9712c45..996bdbc125a5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1487,6 +1487,8 @@ int snd_soc_component_set_sysclk(struct snd_soc_component *component, int snd_soc_component_set_pll(struct snd_soc_component *component, int pll_id, int source, unsigned int freq_in, unsigned int freq_out); +int snd_soc_component_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data); #ifdef CONFIG_REGMAP -- cgit v1.2.3-59-g8ed1b From 85e7dd3f871b988702973c80d9ef128e10dd3dad Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 4 Sep 2017 16:41:53 +0100 Subject: ASoC: arizona: Add support for setting the output volume limits The output volume limits allow signals to be limited to specific levels appropriate for the hardware attached. As this is a property of the hardware itself these will be configured through device tree. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Mark Brown --- include/linux/mfd/arizona/pdata.h | 3 +++ sound/soc/codecs/arizona.c | 25 +++++++++++++++++++++++++ sound/soc/codecs/arizona.h | 1 + sound/soc/codecs/cs47l24.c | 3 +++ sound/soc/codecs/wm5102.c | 3 +++ sound/soc/codecs/wm5110.c | 3 +++ sound/soc/codecs/wm8997.c | 3 +++ 7 files changed, 41 insertions(+) (limited to 'include') diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index bfeecf179895..f72dc53848d7 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -174,6 +174,9 @@ struct arizona_pdata { /** Mode for outputs */ int out_mono[ARIZONA_MAX_OUTPUT]; + /** Limit output volumes */ + unsigned int out_vol_limit[2 * ARIZONA_MAX_OUTPUT]; + /** PDM speaker mute setting */ unsigned int spk_mute[ARIZONA_MAX_PDM_SPK]; diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index e6967385dccb..b3375e19598a 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -372,6 +372,22 @@ int arizona_init_common(struct arizona *arizona) } EXPORT_SYMBOL_GPL(arizona_init_common); +int arizona_init_vol_limit(struct arizona *arizona) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(arizona->pdata.out_vol_limit); ++i) { + if (arizona->pdata.out_vol_limit[i]) + regmap_update_bits(arizona->regmap, + ARIZONA_DAC_VOLUME_LIMIT_1L + i * 4, + ARIZONA_OUT1L_VOL_LIM_MASK, + arizona->pdata.out_vol_limit[i]); + } + + return 0; +} +EXPORT_SYMBOL_GPL(arizona_init_vol_limit); + const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", "Tone Generator 1", @@ -2810,6 +2826,15 @@ int arizona_of_get_audio_pdata(struct arizona *arizona) count++; } + count = 0; + of_property_for_each_u32(np, "wlf,out-volume-limit", prop, cur, val) { + if (count == ARRAY_SIZE(pdata->out_vol_limit)) + break; + + pdata->out_vol_limit[count] = val; + count++; + } + ret = of_property_read_u32_array(np, "wlf,spk-fmt", pdm_val, ARRAY_SIZE(pdm_val)); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 2d198fb2ce97..dfdf6d8c9687 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -315,6 +315,7 @@ int arizona_init_gpio(struct snd_soc_codec *codec); int arizona_init_mono(struct snd_soc_codec *codec); int arizona_init_common(struct arizona *arizona); +int arizona_init_vol_limit(struct arizona *arizona); int arizona_init_spk_irqs(struct arizona *arizona); int arizona_free_spk_irqs(struct arizona *arizona); diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c index 0fe7d7a87ff3..94c0209977d0 100644 --- a/sound/soc/codecs/cs47l24.c +++ b/sound/soc/codecs/cs47l24.c @@ -1297,6 +1297,9 @@ static int cs47l24_probe(struct platform_device *pdev) arizona_init_common(arizona); + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; ret = arizona_init_spk_irqs(arizona); if (ret < 0) goto err_dsp_irq; diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 5a917dd73f32..4f0481d3c7a7 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -2107,6 +2107,9 @@ static int wm5102_probe(struct platform_device *pdev) arizona_init_common(arizona); + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; ret = arizona_init_spk_irqs(arizona); if (ret < 0) goto err_dsp_irq; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index ba1e90ca8be4..6ed1e1f9ce51 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -2463,6 +2463,9 @@ static int wm5110_probe(struct platform_device *pdev) arizona_init_common(arizona); + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + goto err_dsp_irq; ret = arizona_init_spk_irqs(arizona); if (ret < 0) goto err_dsp_irq; diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c index c5aef9ecdecc..77f512767273 100644 --- a/sound/soc/codecs/wm8997.c +++ b/sound/soc/codecs/wm8997.c @@ -1176,6 +1176,9 @@ static int wm8997_probe(struct platform_device *pdev) arizona_init_common(arizona); + ret = arizona_init_vol_limit(arizona); + if (ret < 0) + return ret; ret = arizona_init_spk_irqs(arizona); if (ret < 0) return ret; -- cgit v1.2.3-59-g8ed1b From 2512b1b18d0748d867bb22387db7c86b903291ad Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sat, 5 Aug 2017 11:44:31 +0300 Subject: mac80211: extend ieee80211_ie_split to support EXTENSION Current ieee80211_ie_split() implementation doesn't account for elements that are sub-elements of the EXTENSION IE. To extend support to these IEs as well, treat the WLAN_EID_EXTENSION ids in the %ids array as indicating that the next id in the array is a sub-element of the EXTENSION IE. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 6 ++++-- net/wireless/util.c | 54 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f12fa5245a45..aa9d993e519a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5934,7 +5934,8 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, * @ies: the IE buffer * @ielen: the length of the IE buffer * @ids: an array with element IDs that are allowed before - * the split + * the split. A WLAN_EID_EXTENSION value means that the next + * EID in the list is a sub-element of the EXTENSION IE. * @n_ids: the size of the element ID array * @after_ric: array IE types that come after the RIC element * @n_after_ric: size of the @after_ric array @@ -5965,7 +5966,8 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, * @ies: the IE buffer * @ielen: the length of the IE buffer * @ids: an array with element IDs that are allowed before - * the split + * the split. A WLAN_EID_EXTENSION value means that the next + * EID in the list is a sub-element of the EXTENSION IE. * @n_ids: the size of the element ID array * @offset: offset where to start splitting in the buffer * diff --git a/net/wireless/util.c b/net/wireless/util.c index bcb1284c3415..4aab793c2f00 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1367,13 +1367,29 @@ int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, } EXPORT_SYMBOL(cfg80211_get_p2p_attr); -static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) +static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id, bool id_ext) { int i; - for (i = 0; i < n_ids; i++) - if (ids[i] == id) + /* Make sure array values are legal */ + if (WARN_ON(ids[n_ids - 1] == WLAN_EID_EXTENSION)) + return false; + + i = 0; + while (i < n_ids) { + if (ids[i] == WLAN_EID_EXTENSION) { + if (id_ext && (ids[i + 1] == id)) + return true; + + i += 2; + continue; + } + + if (ids[i] == id && !id_ext) return true; + + i++; + } return false; } @@ -1403,14 +1419,36 @@ size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, { size_t pos = offset; - while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) { + while (pos < ielen) { + u8 ext = 0; + + if (ies[pos] == WLAN_EID_EXTENSION) + ext = 2; + if ((pos + ext) >= ielen) + break; + + if (!ieee80211_id_in_list(ids, n_ids, ies[pos + ext], + ies[pos] == WLAN_EID_EXTENSION)) + break; + if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { pos = skip_ie(ies, ielen, pos); - while (pos < ielen && - !ieee80211_id_in_list(after_ric, n_after_ric, - ies[pos])) - pos = skip_ie(ies, ielen, pos); + while (pos < ielen) { + if (ies[pos] == WLAN_EID_EXTENSION) + ext = 2; + else + ext = 0; + + if ((pos + ext) >= ielen) + break; + + if (!ieee80211_id_in_list(after_ric, + n_after_ric, + ies[pos + ext], + ext == 2)) + pos = skip_ie(ies, ielen, pos); + } } else { pos = skip_ie(ies, ielen, pos); } -- cgit v1.2.3-59-g8ed1b From 2d23d0736e3a4a0fdb92b8e46ea476639f16aae8 Mon Sep 17 00:00:00 2001 From: Roee Zamir Date: Sun, 6 Aug 2017 11:38:22 +0300 Subject: nl80211: add OCE scan and capability flags Add Optimized Connectivity Experience (OCE) scan and capability flags. Some of them unique to OCE and some are stand alone. And add scan flags to enable/disable them. Signed-off-by: Roee Zamir Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 37 ++++++++++-- net/wireless/nl80211.c | 137 ++++++++++++++++++++++++------------------- 2 files changed, 111 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 51626b4175c0..76404d8a8863 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -4914,6 +4914,15 @@ enum nl80211_feature_flags { * handshake with 802.1X in station mode (will pass EAP frames to the host * and accept the set_pmk/del_pmk commands), doing it in the host might not * be supported. + * @NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME: Driver is capable of overriding + * the max channel attribute in the FILS request params IE with the + * actual dwell time. + * @NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP: Driver accepts broadcast probe + * response + * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE: Driver supports sending + * the first probe request in each channel at rate of at least 5.5Mbps. + * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports + * probe request tx deferral and suppression * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4936,6 +4945,10 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_FILS_SK_OFFLOAD, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X, + NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME, + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -5012,12 +5025,28 @@ enum nl80211_timeout_reason { * locally administered 1, multicast 0) is assumed. * This flag must not be requested when the feature isn't supported, check * the nl80211 feature flags for the device. + * @NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME: fill the dwell time in the FILS + * request parameters IE in the probe request + * @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses + * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at + * rate of at least 5.5M. In case non OCE AP is dicovered in the channel, + * only the first probe req in the channel will be sent in high rate. + * @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request + * tx deferral (dot11FILSProbeDelay shall be set to 15ms) + * and suppression (if it has received a broadcast Probe Response frame, + * Beacon frame or FILS Discovery frame from an AP that the STA considers + * a suitable candidate for (re-)association - suitable in terms of + * SSID and/or RSSI */ enum nl80211_scan_flags { - NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, - NL80211_SCAN_FLAG_FLUSH = 1<<1, - NL80211_SCAN_FLAG_AP = 1<<2, - NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3, + NL80211_SCAN_FLAG_LOW_PRIORITY = 1<<0, + NL80211_SCAN_FLAG_FLUSH = 1<<1, + NL80211_SCAN_FLAG_AP = 1<<2, + NL80211_SCAN_FLAG_RANDOM_ADDR = 1<<3, + NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME = 1<<4, + NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP = 1<<5, + NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE = 1<<6, + NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION = 1<<7, }; /** diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 66e97136ab44..2e6f5f4065f9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -6619,6 +6619,77 @@ static bool cfg80211_off_channel_oper_allowed(struct wireless_dev *wdev) return regulatory_pre_cac_allowed(wdev->wiphy); } +static int +nl80211_check_scan_flags(struct wiphy *wiphy, struct wireless_dev *wdev, + void *request, struct nlattr **attrs, + bool is_sched_scan) +{ + u8 *mac_addr, *mac_addr_mask; + u32 *flags; + enum nl80211_feature_flags randomness_flag; + + if (!attrs[NL80211_ATTR_SCAN_FLAGS]) + return 0; + + if (is_sched_scan) { + struct cfg80211_sched_scan_request *req = request; + + randomness_flag = wdev ? + NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR : + NL80211_FEATURE_ND_RANDOM_MAC_ADDR; + flags = &req->flags; + mac_addr = req->mac_addr; + mac_addr_mask = req->mac_addr_mask; + } else { + struct cfg80211_scan_request *req = request; + + randomness_flag = NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + flags = &req->flags; + mac_addr = req->mac_addr; + mac_addr_mask = req->mac_addr_mask; + } + + *flags = nla_get_u32(attrs[NL80211_ATTR_SCAN_FLAGS]); + + if ((*flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && + !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) + return -EOPNOTSUPP; + + if (*flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + int err; + + if (!(wiphy->features & randomness_flag) || + (wdev && wdev->current_bss)) + return -EOPNOTSUPP; + + err = nl80211_parse_random_mac(attrs, mac_addr, mac_addr_mask); + if (err) + return err; + } + + if ((*flags & NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME) && + !wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME)) + return -EOPNOTSUPP; + + if ((*flags & NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP) && + !wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP)) + return -EOPNOTSUPP; + + if ((*flags & NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION) && + !wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION)) + return -EOPNOTSUPP; + + if ((*flags & NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE) && + !wiphy_ext_feature_isset(wiphy, + NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE)) + return -EOPNOTSUPP; + + return 0; +} + static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -6824,34 +6895,10 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) nla_get_flag(info->attrs[NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY]); } - if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) { - request->flags = nla_get_u32( - info->attrs[NL80211_ATTR_SCAN_FLAGS]); - if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && - !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) { - err = -EOPNOTSUPP; - goto out_free; - } - - if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { - if (!(wiphy->features & - NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)) { - err = -EOPNOTSUPP; - goto out_free; - } - - if (wdev->current_bss) { - err = -EOPNOTSUPP; - goto out_free; - } - - err = nl80211_parse_random_mac(info->attrs, - request->mac_addr, - request->mac_addr_mask); - if (err) - goto out_free; - } - } + err = nl80211_check_scan_flags(wiphy, wdev, request, info->attrs, + false); + if (err) + goto out_free; request->no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); @@ -7299,37 +7346,9 @@ nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, request->ie_len); } - if (attrs[NL80211_ATTR_SCAN_FLAGS]) { - request->flags = nla_get_u32( - attrs[NL80211_ATTR_SCAN_FLAGS]); - if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && - !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) { - err = -EOPNOTSUPP; - goto out_free; - } - - if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { - u32 flg = NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; - - if (!wdev) /* must be net-detect */ - flg = NL80211_FEATURE_ND_RANDOM_MAC_ADDR; - - if (!(wiphy->features & flg)) { - err = -EOPNOTSUPP; - goto out_free; - } - - if (wdev && wdev->current_bss) { - err = -EOPNOTSUPP; - goto out_free; - } - - err = nl80211_parse_random_mac(attrs, request->mac_addr, - request->mac_addr_mask); - if (err) - goto out_free; - } - } + err = nl80211_check_scan_flags(wiphy, wdev, request, attrs, true); + if (err) + goto out_free; if (attrs[NL80211_ATTR_SCHED_SCAN_DELAY]) request->delay = -- cgit v1.2.3-59-g8ed1b From 1272c5d89b597995cb10db87dd4a1adc91d36006 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Fri, 18 Aug 2017 15:33:56 +0300 Subject: mac80211: add documentation to ieee80211_rx_ba_offl() Add documentation to ieee80211_rx_ba_offl() function and, while at it, rename the bit argument to tid, for consistency. Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/net/mac80211.h | 8 +++++++- net/mac80211/agg-rx.c | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 885690fa39c8..cc9073e45be9 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -5441,8 +5441,14 @@ void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid, */ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn); +/** + * ieee80211_manage_rx_ba_offl - helper to queue an RX BA work + * @vif: &struct ieee80211_vif pointer from the add_interface callback + * @addr: station mac address + * @tid: the rx tid + */ void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif, const u8 *addr, - unsigned int bit); + unsigned int tid); /** * ieee80211_start_rx_ba_session_offl - start a Rx BA session diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 2849a1fc41c5..88cc1ae935ea 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -459,7 +459,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, } void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif, - const u8 *addr, unsigned int bit) + const u8 *addr, unsigned int tid) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_local *local = sdata->local; @@ -470,7 +470,7 @@ void ieee80211_manage_rx_ba_offl(struct ieee80211_vif *vif, if (!sta) goto unlock; - set_bit(bit, sta->ampdu_mlme.tid_rx_manage_offl); + set_bit(tid, sta->ampdu_mlme.tid_rx_manage_offl); ieee80211_queue_work(&local->hw, &sta->ampdu_mlme.work); unlock: rcu_read_unlock(); -- cgit v1.2.3-59-g8ed1b From a6bcda44843c6dfced0fb973e2607c2a98addfa9 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 19 Sep 2017 11:52:43 +0200 Subject: cfg80211: remove unused function ieee80211_data_from_8023() This function hasn't been used since the removal of iwmc3200wifi in 2012. It also appears to have a bug when qos=True, since then it'll copy uninitialized stack memory to the SKB. Just remove the function entirely. Reported-by: Jouni Malinen Signed-off-by: Johannes Berg --- Documentation/driver-api/80211/cfg80211.rst | 3 - include/net/cfg80211.h | 13 ---- net/wireless/util.c | 115 ---------------------------- 3 files changed, 131 deletions(-) (limited to 'include') diff --git a/Documentation/driver-api/80211/cfg80211.rst b/Documentation/driver-api/80211/cfg80211.rst index 8ffac57e1f5b..eeab91b59457 100644 --- a/Documentation/driver-api/80211/cfg80211.rst +++ b/Documentation/driver-api/80211/cfg80211.rst @@ -299,9 +299,6 @@ Data path helpers .. kernel-doc:: include/net/cfg80211.h :functions: ieee80211_data_to_8023 -.. kernel-doc:: include/net/cfg80211.h - :functions: ieee80211_data_from_8023 - .. kernel-doc:: include/net/cfg80211.h :functions: ieee80211_amsdu_to_8023s diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index aa9d993e519a..cc1996081463 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -4346,19 +4346,6 @@ static inline int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr, return ieee80211_data_to_8023_exthdr(skb, NULL, addr, iftype); } -/** - * ieee80211_data_from_8023 - convert an 802.3 frame to 802.11 - * @skb: the 802.3 frame - * @addr: the device MAC address - * @iftype: the virtual interface type - * @bssid: the network bssid (used only for iftype STATION and ADHOC) - * @qos: build 802.11 QoS data frame - * Return: 0 on success, or a negative error code. - */ -int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype, const u8 *bssid, - bool qos); - /** * ieee80211_amsdu_to_8023s - decode an IEEE 802.11n A-MSDU frame * diff --git a/net/wireless/util.c b/net/wireless/util.c index 4aab793c2f00..7dcdf67cba29 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -529,121 +529,6 @@ int ieee80211_data_to_8023_exthdr(struct sk_buff *skb, struct ethhdr *ehdr, } EXPORT_SYMBOL(ieee80211_data_to_8023_exthdr); -int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, - enum nl80211_iftype iftype, - const u8 *bssid, bool qos) -{ - struct ieee80211_hdr hdr; - u16 hdrlen, ethertype; - __le16 fc; - const u8 *encaps_data; - int encaps_len, skip_header_bytes; - int nh_pos, h_pos; - int head_need; - - if (unlikely(skb->len < ETH_HLEN)) - return -EINVAL; - - nh_pos = skb_network_header(skb) - skb->data; - h_pos = skb_transport_header(skb) - skb->data; - - /* convert Ethernet header to proper 802.11 header (based on - * operation mode) */ - ethertype = (skb->data[12] << 8) | skb->data[13]; - fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA); - - switch (iftype) { - case NL80211_IFTYPE_AP: - case NL80211_IFTYPE_AP_VLAN: - case NL80211_IFTYPE_P2P_GO: - fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS); - /* DA BSSID SA */ - memcpy(hdr.addr1, skb->data, ETH_ALEN); - memcpy(hdr.addr2, addr, ETH_ALEN); - memcpy(hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); - hdrlen = 24; - break; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - fc |= cpu_to_le16(IEEE80211_FCTL_TODS); - /* BSSID SA DA */ - memcpy(hdr.addr1, bssid, ETH_ALEN); - memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); - memcpy(hdr.addr3, skb->data, ETH_ALEN); - hdrlen = 24; - break; - case NL80211_IFTYPE_OCB: - case NL80211_IFTYPE_ADHOC: - /* DA SA BSSID */ - memcpy(hdr.addr1, skb->data, ETH_ALEN); - memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); - memcpy(hdr.addr3, bssid, ETH_ALEN); - hdrlen = 24; - break; - default: - return -EOPNOTSUPP; - } - - if (qos) { - fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); - hdrlen += 2; - } - - hdr.frame_control = fc; - hdr.duration_id = 0; - hdr.seq_ctrl = 0; - - skip_header_bytes = ETH_HLEN; - if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { - encaps_data = bridge_tunnel_header; - encaps_len = sizeof(bridge_tunnel_header); - skip_header_bytes -= 2; - } else if (ethertype >= ETH_P_802_3_MIN) { - encaps_data = rfc1042_header; - encaps_len = sizeof(rfc1042_header); - skip_header_bytes -= 2; - } else { - encaps_data = NULL; - encaps_len = 0; - } - - skb_pull(skb, skip_header_bytes); - nh_pos -= skip_header_bytes; - h_pos -= skip_header_bytes; - - head_need = hdrlen + encaps_len - skb_headroom(skb); - - if (head_need > 0 || skb_cloned(skb)) { - head_need = max(head_need, 0); - if (head_need) - skb_orphan(skb); - - if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) - return -ENOMEM; - } - - if (encaps_data) { - memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); - nh_pos += encaps_len; - h_pos += encaps_len; - } - - memcpy(skb_push(skb, hdrlen), &hdr, hdrlen); - - nh_pos += hdrlen; - h_pos += hdrlen; - - /* Update skb pointers to various headers since this modified frame - * is going to go through Linux networking code that may potentially - * need things like pointer to IP header. */ - skb_reset_mac_header(skb); - skb_set_network_header(skb, nh_pos); - skb_set_transport_header(skb, h_pos); - - return 0; -} -EXPORT_SYMBOL(ieee80211_data_from_8023); - static void __frame_add_frag(struct sk_buff *skb, struct page *page, void *ptr, int len, int size) -- cgit v1.2.3-59-g8ed1b From 65026002d69de006e273749bb799d3b01b757eb0 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Fri, 18 Aug 2017 15:31:41 +0300 Subject: nl80211: add an option to allow MFP without requiring it The user space can now allow the kernel to associate to an AP that requires MFP or that doesn't have MFP enabled in the same NL80211_CMD_CONNECT command, by using a new NL80211_MFP_OPTIONAL flag. The driver / firmware will decide whether to use it or not. Include a feature bit to advertise support for NL80211_MFP_OPTIONAL. This allows new user space to run on old kernels and know that it cannot use the new attribute if it isn't supported. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 13 +++++++++++-- net/wireless/nl80211.c | 8 +++++++- 2 files changed, 18 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 76404d8a8863..59ba6ca66a0d 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -1407,8 +1407,12 @@ enum nl80211_commands { * * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is * used for the association (&enum nl80211_mfp, represented as a u32); - * this attribute can be used - * with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests + * this attribute can be used with %NL80211_CMD_ASSOCIATE and + * %NL80211_CMD_CONNECT requests. %NL80211_MFP_OPTIONAL is not allowed for + * %NL80211_CMD_ASSOCIATE since user space SME is expected and hence, it + * must have decided whether to use management frame protection or not. + * Setting %NL80211_MFP_OPTIONAL with a %NL80211_CMD_CONNECT request will + * let the driver (or the firmware) decide whether to use MFP or not. * * @NL80211_ATTR_STA_FLAGS2: Attribute containing a * &struct nl80211_sta_flag_update. @@ -3947,10 +3951,12 @@ enum nl80211_key_type { * enum nl80211_mfp - Management frame protection state * @NL80211_MFP_NO: Management frame protection not used * @NL80211_MFP_REQUIRED: Management frame protection required + * @NL80211_MFP_OPTIONAL: Management frame protection is optional */ enum nl80211_mfp { NL80211_MFP_NO, NL80211_MFP_REQUIRED, + NL80211_MFP_OPTIONAL, }; enum nl80211_wpa_versions { @@ -4923,6 +4929,8 @@ enum nl80211_feature_flags { * the first probe request in each channel at rate of at least 5.5Mbps. * @NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: Driver supports * probe request tx deferral and suppression + * @NL80211_EXT_FEATURE_MFP_OPTIONAL: Driver supports the %NL80211_MFP_OPTIONAL + * value in %NL80211_ATTR_USE_MFP. * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. @@ -4949,6 +4957,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP, NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE, NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION, + NL80211_EXT_FEATURE_MFP_OPTIONAL, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 2e6f5f4065f9..1e39ba3cfd06 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -8952,8 +8952,14 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) if (info->attrs[NL80211_ATTR_USE_MFP]) { connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]); + if (connect.mfp == NL80211_MFP_OPTIONAL && + !wiphy_ext_feature_isset(&rdev->wiphy, + NL80211_EXT_FEATURE_MFP_OPTIONAL)) + return -EOPNOTSUPP; + if (connect.mfp != NL80211_MFP_REQUIRED && - connect.mfp != NL80211_MFP_NO) + connect.mfp != NL80211_MFP_NO && + connect.mfp != NL80211_MFP_OPTIONAL) return -EINVAL; } else { connect.mfp = NL80211_MFP_NO; -- cgit v1.2.3-59-g8ed1b From 41693fd5237397d3c61b311af0fda1f6f39297c2 Mon Sep 17 00:00:00 2001 From: Anuj Phogat Date: Wed, 20 Sep 2017 13:31:26 -0700 Subject: drm/i915/kbl: Change a KBL pci id to GT2 from GT1.5 See Mesa commit 9c588ff Cc: Matt Turner Cc: Rodrigo Vivi Signed-off-by: Anuj Phogat Reviewed-by: Rodrigo Vivi Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20170920203126.1323-1-anuj.phogat@gmail.com --- include/drm/i915_pciids.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 1257e15c1a03..972a25633525 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -339,7 +339,6 @@ #define INTEL_KBL_GT1_IDS(info) \ INTEL_VGA_DEVICE(0x5913, info), /* ULT GT1.5 */ \ INTEL_VGA_DEVICE(0x5915, info), /* ULX GT1.5 */ \ - INTEL_VGA_DEVICE(0x5917, info), /* DT GT1.5 */ \ INTEL_VGA_DEVICE(0x5906, info), /* ULT GT1 */ \ INTEL_VGA_DEVICE(0x590E, info), /* ULX GT1 */ \ INTEL_VGA_DEVICE(0x5902, info), /* DT GT1 */ \ @@ -349,6 +348,7 @@ #define INTEL_KBL_GT2_IDS(info) \ INTEL_VGA_DEVICE(0x5916, info), /* ULT GT2 */ \ + INTEL_VGA_DEVICE(0x5917, info), /* Mobile GT2 */ \ INTEL_VGA_DEVICE(0x5921, info), /* ULT GT2F */ \ INTEL_VGA_DEVICE(0x591E, info), /* ULX GT2 */ \ INTEL_VGA_DEVICE(0x5912, info), /* DT GT2 */ \ -- cgit v1.2.3-59-g8ed1b From 3f7632e1ba2c6f5ca2499d1ee8acf95599d4b7b6 Mon Sep 17 00:00:00 2001 From: Stefan Brüns Date: Tue, 12 Sep 2017 01:44:44 +0200 Subject: dmaengine: List all allowed values for src/dst_addr_width in kernel doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 93c6ee94c140 ("dma: Support for 3 bytes word size") and commit 534a729866f9 ("dmaengine: Add 16 bytes, 32 bytes and 64 bytes bus widths") added additional values for the allowed word size, but omitted these from the struct dma_slave_config documentation. Signed-off-by: Stefan Brüns Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 8319101170fc..2910e7dadc7f 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -329,7 +329,7 @@ enum dma_slave_buswidth { * @src_addr_width: this is the width in bytes of the source (RX) * register where DMA data shall be read. If the source * is memory this may be ignored depending on architecture. - * Legal values: 1, 2, 4, 8. + * Legal values: 1, 2, 3, 4, 8, 16, 32, 64. * @dst_addr_width: same as src_addr_width but for destination * target (TX) mutatis mutandis. * @src_maxburst: the maximum number of words (note: words, as in -- cgit v1.2.3-59-g8ed1b From c2cbd4276eea1d3b204754653dd74d6e10ca207f Mon Sep 17 00:00:00 2001 From: Stefan Brüns Date: Tue, 12 Sep 2017 01:44:45 +0200 Subject: dmaengine: Mark struct dma_slave_caps kernel-doc correctly, clarify MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct dma_slave_caps documentation omitted the correct kernel-doc opening comment mark. Document byte granularity and interpretation of the src/dst_addr_widths bit flag fields used by struct dma_slave_caps and struct dma_device. Add punctuation to their "directions" member documentations, and cleanup wording of the description. Signed-off-by: Stefan Brüns Signed-off-by: Vinod Koul --- include/linux/dmaengine.h | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index 2910e7dadc7f..f838764993eb 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -404,14 +404,16 @@ enum dma_residue_granularity { DMA_RESIDUE_GRANULARITY_BURST = 2, }; -/* struct dma_slave_caps - expose capabilities of a slave channel only - * - * @src_addr_widths: bit mask of src addr widths the channel supports - * @dst_addr_widths: bit mask of dstn addr widths the channel supports - * @directions: bit mask of slave direction the channel supported - * since the enum dma_transfer_direction is not defined as bits for each - * type of direction, the dma controller should fill (1 << ) and same - * should be checked by controller as well +/** + * struct dma_slave_caps - expose capabilities of a slave channel only + * @src_addr_widths: bit mask of src addr widths the channel supports. + * Width is specified in bytes, e.g. for a channel supporting + * a width of 4 the mask should have BIT(4) set. + * @dst_addr_widths: bit mask of dst addr widths the channel supports + * @directions: bit mask of slave directions the channel supports. + * Since the enum dma_transfer_direction is not defined as bit flag for + * each type, the dma controller should set BIT() and same + * should be checked by controller as well * @max_burst: max burst capability per-transfer * @cmd_pause: true, if pause and thereby resume is supported * @cmd_terminate: true, if terminate cmd is supported @@ -678,11 +680,13 @@ struct dma_filter { * @dev_id: unique device ID * @dev: struct device reference for dma mapping api * @src_addr_widths: bit mask of src addr widths the device supports + * Width is specified in bytes, e.g. for a device supporting + * a width of 4 the mask should have BIT(4) set. * @dst_addr_widths: bit mask of dst addr widths the device supports - * @directions: bit mask of slave direction the device supports since - * the enum dma_transfer_direction is not defined as bits for - * each type of direction, the dma controller should fill (1 << - * ) and same should be checked by controller as well + * @directions: bit mask of slave directions the device supports. + * Since the enum dma_transfer_direction is not defined as bit flag for + * each type, the dma controller should set BIT() and same + * should be checked by controller as well * @max_burst: max burst capability per-transfer * @residue_granularity: granularity of the transfer residue reported * by tx_status -- cgit v1.2.3-59-g8ed1b From 6e617de84e87d626d1e976fc30e1322239fd4d2d Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Wed, 20 Sep 2017 18:26:53 +0200 Subject: net: avoid a full fib lookup when rp_filter is disabled. Since commit 1dced6a85482 ("ipv4: Restore accept_local behaviour in fib_validate_source()") a full fib lookup is needed even if the rp_filter is disabled, if accept_local is false - which is the default. What we really need in the above scenario is just checking that the source IP address is not local, and in most case we can do that is a cheaper way looking up the ifaddr hash table. This commit adds a helper for such lookup, and uses it to validate the src address when rp_filter is disabled and no 'local' routes are created by the user space in the relevant namespace. A new ipv4 netns flag is added to account for such routes. We need that to preserve the same behavior we had before this patch. It also drops the checks to bail early from __fib_validate_source, added by the commit 1dced6a85482 ("ipv4: Restore accept_local behaviour in fib_validate_source()") they do not give any measurable performance improvement: if we do the lookup with are on a slower path. This improves UDP performances for unconnected sockets when rp_filter is disabled by 5% and also gives small but measurable performance improvement for TCP flood scenarios. v1 -> v2: - use the ifaddr lookup helper in __ip_dev_find(), as suggested by Eric - fall-back to full lookup if custom local routes are present Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller --- include/linux/inetdevice.h | 1 + include/net/netns/ipv4.h | 1 + net/ipv4/devinet.c | 30 ++++++++++++++++++------------ net/ipv4/fib_frontend.c | 22 +++++++++++++++++----- 4 files changed, 37 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index fb3f809e34e4..751d051f0bc7 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -179,6 +179,7 @@ __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, __be32 dst, __be32 local, int scope); struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, __be32 mask); +struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr); static __inline__ bool inet_ifa_match(__be32 addr, struct in_ifaddr *ifa) { return !((addr^ifa->ifa_address)&ifa->ifa_mask); diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 20d061c805e3..20720721da4b 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -49,6 +49,7 @@ struct netns_ipv4 { #ifdef CONFIG_IP_MULTIPLE_TABLES struct fib_rules_ops *rules_ops; bool fib_has_custom_rules; + bool fib_has_custom_local_routes; struct fib_table __rcu *fib_main; struct fib_table __rcu *fib_default; #endif diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index d7adc0616599..7ce22a2c07ce 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -137,22 +137,12 @@ static void inet_hash_remove(struct in_ifaddr *ifa) */ struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) { - u32 hash = inet_addr_hash(net, addr); struct net_device *result = NULL; struct in_ifaddr *ifa; rcu_read_lock(); - hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) { - if (ifa->ifa_local == addr) { - struct net_device *dev = ifa->ifa_dev->dev; - - if (!net_eq(dev_net(dev), net)) - continue; - result = dev; - break; - } - } - if (!result) { + ifa = inet_lookup_ifaddr_rcu(net, addr); + if (!ifa) { struct flowi4 fl4 = { .daddr = addr }; struct fib_result res = { 0 }; struct fib_table *local; @@ -165,6 +155,8 @@ struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && res.type == RTN_LOCAL) result = FIB_RES_DEV(res); + } else { + result = ifa->ifa_dev->dev; } if (result && devref) dev_hold(result); @@ -173,6 +165,20 @@ struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) } EXPORT_SYMBOL(__ip_dev_find); +/* called under RCU lock */ +struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr) +{ + u32 hash = inet_addr_hash(net, addr); + struct in_ifaddr *ifa; + + hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) + if (ifa->ifa_local == addr && + net_eq(dev_net(ifa->ifa_dev->dev), net)) + return ifa; + + return NULL; +} + static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 37819ab4cc74..f02819134ba2 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -345,9 +345,6 @@ static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, if (res.type != RTN_UNICAST && (res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev))) goto e_inval; - if (!rpf && !fib_num_tclassid_users(net) && - (dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev))) - goto last_resort; fib_combine_itag(itag, &res); dev_match = false; @@ -402,13 +399,26 @@ int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, struct in_device *idev, u32 *itag) { int r = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev); + struct net *net = dev_net(dev); - if (!r && !fib_num_tclassid_users(dev_net(dev)) && - IN_DEV_ACCEPT_LOCAL(idev) && + if (!r && !fib_num_tclassid_users(net) && (dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev))) { + if (IN_DEV_ACCEPT_LOCAL(idev)) + goto ok; + /* if no local routes are added from user space we can check + * for local addresses looking-up the ifaddr table + */ + if (net->ipv4.fib_has_custom_local_routes) + goto full_check; + if (inet_lookup_ifaddr_rcu(net, src)) + return -EINVAL; + +ok: *itag = 0; return 0; } + +full_check: return __fib_validate_source(skb, src, dst, tos, oif, dev, r, idev, itag); } @@ -759,6 +769,8 @@ static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh, } err = fib_table_insert(net, tb, &cfg, extack); + if (!err && cfg.fc_type == RTN_LOCAL) + net->ipv4.fib_has_custom_local_routes = true; errout: return err; } -- cgit v1.2.3-59-g8ed1b From a1f3316dd7b5ce740c774697c664e2e60d095794 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 21 Sep 2017 18:18:23 -0700 Subject: ipv4: Move fib_has_custom_local_routes outside of IP_MULTIPLE_TABLES. > net/ipv4/fib_frontend.c: In function 'fib_validate_source': > net/ipv4/fib_frontend.c:411:16: error: 'struct netns_ipv4' has no member named 'fib_has_custom_local_routes' > if (net->ipv4.fib_has_custom_local_routes) > ^ > net/ipv4/fib_frontend.c: In function 'inet_rtm_newroute': > net/ipv4/fib_frontend.c:773:12: error: 'struct netns_ipv4' has no member named 'fib_has_custom_local_routes' > net->ipv4.fib_has_custom_local_routes = true; > ^ Fixes: 6e617de84e87 ("net: avoid a full fib lookup when rp_filter is disabled.") Reported-by: Stephen Rothwell Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 20720721da4b..8387f099115e 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -49,10 +49,10 @@ struct netns_ipv4 { #ifdef CONFIG_IP_MULTIPLE_TABLES struct fib_rules_ops *rules_ops; bool fib_has_custom_rules; - bool fib_has_custom_local_routes; struct fib_table __rcu *fib_main; struct fib_table __rcu *fib_default; #endif + bool fib_has_custom_local_routes; #ifdef CONFIG_IP_ROUTE_CLASSID int fib_num_tclassid_users; #endif -- cgit v1.2.3-59-g8ed1b From a9a1d2a7827c9cf780966d0879c73ef5a91380e9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 22 Sep 2017 11:02:10 +0200 Subject: pinctrl/gpio: Unify namespace for cross-calls The pinctrl_request_gpio() and pinctrl_free_gpio() break the nice namespacing in the other cross-calls like pinctrl_gpio_foo(). Just rename them and all references so we have one namespace with all cross-calls under pinctrl_gpio_*(). Signed-off-by: Linus Walleij --- Documentation/driver-api/pinctl.rst | 6 +++--- Documentation/gpio/gpio-legacy.txt | 10 +++++----- Documentation/translations/zh_CN/gpio.txt | 6 +++--- drivers/gpio/gpio-aspeed.c | 4 ++-- drivers/gpio/gpio-em.c | 4 ++-- drivers/gpio/gpio-pxa.c | 4 ++-- drivers/gpio/gpio-rcar.c | 4 ++-- drivers/gpio/gpio-tegra.c | 4 ++-- drivers/gpio/gpio-tz1090.c | 4 ++-- drivers/gpio/gpiolib.c | 4 ++-- drivers/pinctrl/bcm/pinctrl-iproc-gpio.c | 4 ++-- drivers/pinctrl/bcm/pinctrl-nsp-gpio.c | 4 ++-- drivers/pinctrl/core.c | 12 ++++++------ drivers/pinctrl/core.h | 2 +- drivers/pinctrl/meson/pinctrl-meson.c | 4 ++-- drivers/pinctrl/sh-pfc/gpio.c | 4 ++-- drivers/pinctrl/sirf/pinctrl-atlas7.c | 4 ++-- drivers/pinctrl/sirf/pinctrl-sirf.c | 4 ++-- drivers/pinctrl/spear/pinctrl-plgpio.c | 6 +++--- drivers/pinctrl/stm32/pinctrl-stm32.c | 4 ++-- include/linux/pinctrl/consumer.h | 8 ++++---- 21 files changed, 53 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/Documentation/driver-api/pinctl.rst b/Documentation/driver-api/pinctl.rst index 48f15b4f9d3e..6cb68d67fa75 100644 --- a/Documentation/driver-api/pinctl.rst +++ b/Documentation/driver-api/pinctl.rst @@ -757,8 +757,8 @@ that your datasheet calls "GPIO mode", but actually is just an electrical configuration for a certain device. See the section below named "GPIO mode pitfalls" for more details on this scenario. -The public pinmux API contains two functions named pinctrl_request_gpio() -and pinctrl_free_gpio(). These two functions shall *ONLY* be called from +The public pinmux API contains two functions named pinctrl_gpio_request() +and pinctrl_gpio_free(). These two functions shall *ONLY* be called from gpiolib-based drivers as part of their gpio_request() and gpio_free() semantics. Likewise the pinctrl_gpio_direction_[input|output] shall only be called from within respective gpio_direction_[input|output] @@ -790,7 +790,7 @@ gpiolib driver and the affected GPIO range, pin offset and desired direction will be passed along to this function. Alternatively to using these special functions, it is fully allowed to use -named functions for each GPIO pin, the pinctrl_request_gpio() will attempt to +named functions for each GPIO pin, the pinctrl_gpio_request() will attempt to obtain the function "gpioN" where "N" is the global GPIO pin number if no special GPIO-handler is registered. diff --git a/Documentation/gpio/gpio-legacy.txt b/Documentation/gpio/gpio-legacy.txt index 5eacc147ea87..8356d0e78f67 100644 --- a/Documentation/gpio/gpio-legacy.txt +++ b/Documentation/gpio/gpio-legacy.txt @@ -273,8 +273,8 @@ easily, gating off unused clocks. For GPIOs that use pins known to the pinctrl subsystem, that subsystem should be informed of their use; a gpiolib driver's .request() operation may call -pinctrl_request_gpio(), and a gpiolib driver's .free() operation may call -pinctrl_free_gpio(). The pinctrl subsystem allows a pinctrl_request_gpio() +pinctrl_gpio_request(), and a gpiolib driver's .free() operation may call +pinctrl_gpio_free(). The pinctrl subsystem allows a pinctrl_gpio_request() to succeed concurrently with a pin or pingroup being "owned" by a device for pin multiplexing. @@ -448,8 +448,8 @@ together with an optional gpio feature. We have already covered the case where e.g. a GPIO controller need to reserve a pin or set the direction of a pin by calling any of: -pinctrl_request_gpio() -pinctrl_free_gpio() +pinctrl_gpio_request() +pinctrl_gpio_free() pinctrl_gpio_direction_input() pinctrl_gpio_direction_output() @@ -466,7 +466,7 @@ gpio (under gpiolib) is still maintained by gpio drivers. It may happen that different pin ranges in a SoC is managed by different gpio drivers. This makes it logical to let gpio drivers announce their pin ranges to -the pin ctrl subsystem before it will call 'pinctrl_request_gpio' in order +the pin ctrl subsystem before it will call 'pinctrl_gpio_request' in order to request the corresponding pin to be prepared by the pinctrl subsystem before any gpio usage. diff --git a/Documentation/translations/zh_CN/gpio.txt b/Documentation/translations/zh_CN/gpio.txt index bce972521065..4f8bf30a41dc 100644 --- a/Documentation/translations/zh_CN/gpio.txt +++ b/Documentation/translations/zh_CN/gpio.txt @@ -257,9 +257,9 @@ GPIO 值的命令需要等待其信息排到队首才发送命令,再获得其 简单地关闭未使用时钟)。 对于 GPIO 使用 pinctrl 子系统已知的引脚,子系统应该被告知其使用情况; -一个 gpiolib 驱动的 .request()操作应调用 pinctrl_request_gpio(), -而 gpiolib 驱动的 .free()操作应调用 pinctrl_free_gpio()。pinctrl -子系统允许 pinctrl_request_gpio()在某个引脚或引脚组以复用形式“属于” +一个 gpiolib 驱动的 .request()操作应调用 pinctrl_gpio_request(), +而 gpiolib 驱动的 .free()操作应调用 pinctrl_gpio_free()。pinctrl +子系统允许 pinctrl_gpio_request()在某个引脚或引脚组以复用形式“属于” 一个设备时都成功返回。 任何须将 GPIO 信号导向适当引脚的引脚复用硬件的编程应该发生在 GPIO diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index bfc53995064a..c269cc199707 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -536,12 +536,12 @@ static int aspeed_gpio_request(struct gpio_chip *chip, unsigned int offset) if (!have_gpio(gpiochip_get_data(chip), offset)) return -ENODEV; - return pinctrl_request_gpio(chip->base + offset); + return pinctrl_gpio_request(chip->base + offset); } static void aspeed_gpio_free(struct gpio_chip *chip, unsigned int offset) { - pinctrl_free_gpio(chip->base + offset); + pinctrl_gpio_free(chip->base + offset); } static inline void __iomem *bank_debounce_reg(struct aspeed_gpio *gpio, diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index 8d32ccc980d9..b86e09e1b13b 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -239,12 +239,12 @@ static int em_gio_to_irq(struct gpio_chip *chip, unsigned offset) static int em_gio_request(struct gpio_chip *chip, unsigned offset) { - return pinctrl_request_gpio(chip->base + offset); + return pinctrl_gpio_request(chip->base + offset); } static void em_gio_free(struct gpio_chip *chip, unsigned offset) { - pinctrl_free_gpio(chip->base + offset); + pinctrl_gpio_free(chip->base + offset); /* Set the GPIO as an input to ensure that the next GPIO request won't * drive the GPIO pin as an output. diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 6029899789f3..da68d6cbc1e4 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -332,12 +332,12 @@ static int pxa_gpio_of_xlate(struct gpio_chip *gc, static int pxa_gpio_request(struct gpio_chip *chip, unsigned int offset) { - return pinctrl_request_gpio(chip->base + offset); + return pinctrl_gpio_request(chip->base + offset); } static void pxa_gpio_free(struct gpio_chip *chip, unsigned int offset) { - pinctrl_free_gpio(chip->base + offset); + pinctrl_gpio_free(chip->base + offset); } static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio, diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 1f0871553fd2..43b51045aa47 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -249,7 +249,7 @@ static int gpio_rcar_request(struct gpio_chip *chip, unsigned offset) if (error < 0) return error; - error = pinctrl_request_gpio(chip->base + offset); + error = pinctrl_gpio_request(chip->base + offset); if (error) pm_runtime_put(&p->pdev->dev); @@ -260,7 +260,7 @@ static void gpio_rcar_free(struct gpio_chip *chip, unsigned offset) { struct gpio_rcar_priv *p = gpiochip_get_data(chip); - pinctrl_free_gpio(chip->base + offset); + pinctrl_gpio_free(chip->base + offset); /* * Set the GPIO as an input to ensure that the next GPIO request won't diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index fbaf974277df..8db47f671708 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -141,14 +141,14 @@ static void tegra_gpio_disable(struct tegra_gpio_info *tgi, unsigned int gpio) static int tegra_gpio_request(struct gpio_chip *chip, unsigned int offset) { - return pinctrl_request_gpio(offset); + return pinctrl_gpio_request(offset); } static void tegra_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct tegra_gpio_info *tgi = gpiochip_get_data(chip); - pinctrl_free_gpio(offset); + pinctrl_gpio_free(offset); tegra_gpio_disable(tgi, offset); } diff --git a/drivers/gpio/gpio-tz1090.c b/drivers/gpio/gpio-tz1090.c index 22c5be65051f..0bb9bb583889 100644 --- a/drivers/gpio/gpio-tz1090.c +++ b/drivers/gpio/gpio-tz1090.c @@ -232,7 +232,7 @@ static int tz1090_gpio_request(struct gpio_chip *chip, unsigned int offset) struct tz1090_gpio_bank *bank = gpiochip_get_data(chip); int ret; - ret = pinctrl_request_gpio(chip->base + offset); + ret = pinctrl_gpio_request(chip->base + offset); if (ret) return ret; @@ -246,7 +246,7 @@ static void tz1090_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct tz1090_gpio_bank *bank = gpiochip_get_data(chip); - pinctrl_free_gpio(chip->base + offset); + pinctrl_gpio_free(chip->base + offset); tz1090_gpio_clear_bit(bank, REG_GPIO_BIT_EN, offset); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index eb80dac4e26a..a9cb825ef335 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1859,7 +1859,7 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip) */ int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset) { - return pinctrl_request_gpio(chip->gpiodev->base + offset); + return pinctrl_gpio_request(chip->gpiodev->base + offset); } EXPORT_SYMBOL_GPL(gpiochip_generic_request); @@ -1870,7 +1870,7 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); */ void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset) { - pinctrl_free_gpio(chip->gpiodev->base + offset); + pinctrl_gpio_free(chip->gpiodev->base + offset); } EXPORT_SYMBOL_GPL(gpiochip_generic_free); diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c index 85a8c97d9dfe..5d08d989b1d0 100644 --- a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -311,7 +311,7 @@ static int iproc_gpio_request(struct gpio_chip *gc, unsigned offset) if (!chip->pinmux_is_supported) return 0; - return pinctrl_request_gpio(gpio); + return pinctrl_gpio_request(gpio); } static void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) @@ -322,7 +322,7 @@ static void iproc_gpio_free(struct gpio_chip *gc, unsigned offset) if (!chip->pinmux_is_supported) return; - pinctrl_free_gpio(gpio); + pinctrl_gpio_free(gpio); } static int iproc_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) diff --git a/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c index 1cfe45fd391f..c1887072936e 100644 --- a/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c @@ -282,14 +282,14 @@ static int nsp_gpio_request(struct gpio_chip *gc, unsigned offset) { unsigned gpio = gc->base + offset; - return pinctrl_request_gpio(gpio); + return pinctrl_gpio_request(gpio); } static void nsp_gpio_free(struct gpio_chip *gc, unsigned offset) { unsigned gpio = gc->base + offset; - pinctrl_free_gpio(gpio); + pinctrl_gpio_free(gpio); } static int nsp_gpio_direction_input(struct gpio_chip *gc, unsigned gpio) diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 56fbe4c3e800..4c8d5b23e4d0 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -733,14 +733,14 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev, } /** - * pinctrl_request_gpio() - request a single pin to be used as GPIO + * pinctrl_gpio_request() - request a single pin to be used as GPIO * @gpio: the GPIO pin number from the GPIO subsystem number space * * This function should *ONLY* be used from gpiolib-based GPIO drivers, * as part of their gpio_request() semantics, platforms and individual drivers * shall *NOT* request GPIO pins to be muxed in. */ -int pinctrl_request_gpio(unsigned gpio) +int pinctrl_gpio_request(unsigned gpio) { struct pinctrl_dev *pctldev; struct pinctrl_gpio_range *range; @@ -765,17 +765,17 @@ int pinctrl_request_gpio(unsigned gpio) return ret; } -EXPORT_SYMBOL_GPL(pinctrl_request_gpio); +EXPORT_SYMBOL_GPL(pinctrl_gpio_request); /** - * pinctrl_free_gpio() - free control on a single pin, currently used as GPIO + * pinctrl_gpio_free() - free control on a single pin, currently used as GPIO * @gpio: the GPIO pin number from the GPIO subsystem number space * * This function should *ONLY* be used from gpiolib-based GPIO drivers, * as part of their gpio_free() semantics, platforms and individual drivers * shall *NOT* request GPIO pins to be muxed out. */ -void pinctrl_free_gpio(unsigned gpio) +void pinctrl_gpio_free(unsigned gpio) { struct pinctrl_dev *pctldev; struct pinctrl_gpio_range *range; @@ -795,7 +795,7 @@ void pinctrl_free_gpio(unsigned gpio) mutex_unlock(&pctldev->mutex); } -EXPORT_SYMBOL_GPL(pinctrl_free_gpio); +EXPORT_SYMBOL_GPL(pinctrl_gpio_free); static int pinctrl_gpio_direction(unsigned gpio, bool input) { diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h index 7880c3adc450..8cf2eba17c8c 100644 --- a/drivers/pinctrl/core.h +++ b/drivers/pinctrl/core.h @@ -154,7 +154,7 @@ struct pinctrl_setting { * or pin, and each of these will increment the @usecount. * @mux_owner: The name of device that called pinctrl_get(). * @mux_setting: The most recent selected mux setting for this pin, if any. - * @gpio_owner: If pinctrl_request_gpio() was called for this pin, this is + * @gpio_owner: If pinctrl_gpio_request() was called for this pin, this is * the name of the GPIO that "owns" this pin. */ struct pin_desc { diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c index 66ed70c12733..e6e12a7b21e0 100644 --- a/drivers/pinctrl/meson/pinctrl-meson.c +++ b/drivers/pinctrl/meson/pinctrl-meson.c @@ -412,14 +412,14 @@ static const struct pinconf_ops meson_pinconf_ops = { static int meson_gpio_request(struct gpio_chip *chip, unsigned gpio) { - return pinctrl_request_gpio(chip->base + gpio); + return pinctrl_gpio_request(chip->base + gpio); } static void meson_gpio_free(struct gpio_chip *chip, unsigned gpio) { struct meson_pinctrl *pc = gpiochip_get_data(chip); - pinctrl_free_gpio(pc->data->pin_base + gpio); + pinctrl_gpio_free(pc->data->pin_base + gpio); } static int meson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) diff --git a/drivers/pinctrl/sh-pfc/gpio.c b/drivers/pinctrl/sh-pfc/gpio.c index 6b5422766f13..946d9be50b62 100644 --- a/drivers/pinctrl/sh-pfc/gpio.c +++ b/drivers/pinctrl/sh-pfc/gpio.c @@ -139,12 +139,12 @@ static int gpio_pin_request(struct gpio_chip *gc, unsigned offset) if (idx < 0 || pfc->info->pins[idx].enum_id == 0) return -EINVAL; - return pinctrl_request_gpio(offset); + return pinctrl_gpio_request(offset); } static void gpio_pin_free(struct gpio_chip *gc, unsigned offset) { - return pinctrl_free_gpio(offset); + return pinctrl_gpio_free(offset); } static void gpio_pin_set_value(struct sh_pfc_chip *chip, unsigned offset, diff --git a/drivers/pinctrl/sirf/pinctrl-atlas7.c b/drivers/pinctrl/sirf/pinctrl-atlas7.c index 4db9323251e3..f4b192b493a0 100644 --- a/drivers/pinctrl/sirf/pinctrl-atlas7.c +++ b/drivers/pinctrl/sirf/pinctrl-atlas7.c @@ -5860,7 +5860,7 @@ static int atlas7_gpio_request(struct gpio_chip *chip, if (ret < 0) return ret; - if (pinctrl_request_gpio(chip->base + gpio)) + if (pinctrl_gpio_request(chip->base + gpio)) return -ENODEV; raw_spin_lock_irqsave(&a7gc->lock, flags); @@ -5890,7 +5890,7 @@ static void atlas7_gpio_free(struct gpio_chip *chip, raw_spin_unlock_irqrestore(&a7gc->lock, flags); - pinctrl_free_gpio(chip->base + gpio); + pinctrl_gpio_free(chip->base + gpio); } static int atlas7_gpio_direction_input(struct gpio_chip *chip, diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c index d3ef05973901..d64add0b84cc 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -614,7 +614,7 @@ static int sirfsoc_gpio_request(struct gpio_chip *chip, unsigned offset) struct sirfsoc_gpio_bank *bank = sirfsoc_gpio_to_bank(sgpio, offset); unsigned long flags; - if (pinctrl_request_gpio(chip->base + offset)) + if (pinctrl_gpio_request(chip->base + offset)) return -ENODEV; spin_lock_irqsave(&bank->lock, flags); @@ -644,7 +644,7 @@ static void sirfsoc_gpio_free(struct gpio_chip *chip, unsigned offset) spin_unlock_irqrestore(&bank->lock, flags); - pinctrl_free_gpio(chip->base + offset); + pinctrl_gpio_free(chip->base + offset); } static int sirfsoc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) diff --git a/drivers/pinctrl/spear/pinctrl-plgpio.c b/drivers/pinctrl/spear/pinctrl-plgpio.c index cf6d68c7345b..7a33e2e1e3e7 100644 --- a/drivers/pinctrl/spear/pinctrl-plgpio.c +++ b/drivers/pinctrl/spear/pinctrl-plgpio.c @@ -204,7 +204,7 @@ static int plgpio_request(struct gpio_chip *chip, unsigned offset) if (offset >= chip->ngpio) return -EINVAL; - ret = pinctrl_request_gpio(gpio); + ret = pinctrl_gpio_request(gpio); if (ret) return ret; @@ -242,7 +242,7 @@ err1: if (!IS_ERR(plgpio->clk)) clk_disable(plgpio->clk); err0: - pinctrl_free_gpio(gpio); + pinctrl_gpio_free(gpio); return ret; } @@ -273,7 +273,7 @@ disable_clk: if (!IS_ERR(plgpio->clk)) clk_disable(plgpio->clk); - pinctrl_free_gpio(gpio); + pinctrl_gpio_free(gpio); } /* PLGPIO IRQ */ diff --git a/drivers/pinctrl/stm32/pinctrl-stm32.c b/drivers/pinctrl/stm32/pinctrl-stm32.c index 50299ad96659..a954d25bac4e 100644 --- a/drivers/pinctrl/stm32/pinctrl-stm32.c +++ b/drivers/pinctrl/stm32/pinctrl-stm32.c @@ -150,12 +150,12 @@ static int stm32_gpio_request(struct gpio_chip *chip, unsigned offset) return -EINVAL; } - return pinctrl_request_gpio(chip->base + offset); + return pinctrl_gpio_request(chip->base + offset); } static void stm32_gpio_free(struct gpio_chip *chip, unsigned offset) { - pinctrl_free_gpio(chip->base + offset); + pinctrl_gpio_free(chip->base + offset); } static int stm32_gpio_get(struct gpio_chip *chip, unsigned offset) diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index a0f2aba72fa9..0412cc9833e9 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -25,8 +25,8 @@ struct device; #ifdef CONFIG_PINCTRL /* External interface to pin control */ -extern int pinctrl_request_gpio(unsigned gpio); -extern void pinctrl_free_gpio(unsigned gpio); +extern int pinctrl_gpio_request(unsigned gpio); +extern void pinctrl_gpio_free(unsigned gpio); extern int pinctrl_gpio_direction_input(unsigned gpio); extern int pinctrl_gpio_direction_output(unsigned gpio); extern int pinctrl_gpio_set_config(unsigned gpio, unsigned long config); @@ -62,12 +62,12 @@ static inline int pinctrl_pm_select_idle_state(struct device *dev) #else /* !CONFIG_PINCTRL */ -static inline int pinctrl_request_gpio(unsigned gpio) +static inline int pinctrl_gpio_request(unsigned gpio) { return 0; } -static inline void pinctrl_free_gpio(unsigned gpio) +static inline void pinctrl_gpio_free(unsigned gpio) { } -- cgit v1.2.3-59-g8ed1b From 560b1a82ee87288c18aca31ee3053272c3737fe5 Mon Sep 17 00:00:00 2001 From: Kamil Konieczny Date: Thu, 17 Aug 2017 12:11:36 +0200 Subject: crypto: doc - clarify return values for async hash methods * fix documentation of return values for crypto_ahash_init(), crypto_ahash_finup(), crypto_ahash_final(), crypto_ahash_digest() and crypto_ahash_update() Also while at it: * add notes for device driver developers in struct ahash_alg description * fix description of @final method in struct ahash_alg * fix typo in crypto_ahash_finup() description Signed-off-by: Kamil Konieczny Signed-off-by: Herbert Xu --- include/crypto/hash.h | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/crypto/hash.h b/include/crypto/hash.h index b5727bcd2336..0ed31fd80242 100644 --- a/include/crypto/hash.h +++ b/include/crypto/hash.h @@ -75,6 +75,7 @@ struct ahash_request { * state of the HASH transformation at the beginning. This shall fill in * the internal structures used during the entire duration of the whole * transformation. No data processing happens at this point. + * Note: mandatory. * @update: Push a chunk of data into the driver for transformation. This * function actually pushes blocks of data from upper layers into the * driver, which then passes those to the hardware as seen fit. This @@ -84,16 +85,20 @@ struct ahash_request { * context, as this function may be called in parallel with the same * transformation object. Data processing can happen synchronously * [SHASH] or asynchronously [AHASH] at this point. + * Note: mandatory. * @final: Retrieve result from the driver. This function finalizes the * transformation and retrieves the resulting hash from the driver and * pushes it back to upper layers. No data processing happens at this - * point. + * point unless hardware requires it to finish the transformation + * (then the data buffered by the device driver is processed). + * Note: mandatory. * @finup: Combination of @update and @final. This function is effectively a * combination of @update and @final calls issued in sequence. As some * hardware cannot do @update and @final separately, this callback was * added to allow such hardware to be used at least by IPsec. Data * processing can happen synchronously [SHASH] or asynchronously [AHASH] * at this point. + * Note: optional. * @digest: Combination of @init and @update and @final. This function * effectively behaves as the entire chain of operations, @init, * @update and @final issued in sequence. Just like @finup, this was @@ -416,11 +421,10 @@ static inline bool crypto_ahash_has_setkey(struct crypto_ahash *tfm) * needed to perform the cipher operation * * This function is a "short-hand" for the function calls of - * crypto_ahash_update and crypto_shash_final. The parameters have the same + * crypto_ahash_update and crypto_ahash_final. The parameters have the same * meaning as discussed for those separate functions. * - * Return: 0 if the message digest creation was successful; < 0 if an error - * occurred + * Return: see crypto_ahash_final() */ int crypto_ahash_finup(struct ahash_request *req); @@ -433,8 +437,11 @@ int crypto_ahash_finup(struct ahash_request *req); * based on all data added to the cipher handle. The message digest is placed * into the output buffer registered with the ahash_request handle. * - * Return: 0 if the message digest creation was successful; < 0 if an error - * occurred + * Return: + * 0 if the message digest was successfully calculated; + * -EINPROGRESS if data is feeded into hardware (DMA) or queued for later; + * -EBUSY if queue is full and request should be resubmitted later; + * other < 0 if an error occurred */ int crypto_ahash_final(struct ahash_request *req); @@ -447,8 +454,7 @@ int crypto_ahash_final(struct ahash_request *req); * crypto_ahash_update and crypto_ahash_final. The parameters have the same * meaning as discussed for those separate three functions. * - * Return: 0 if the message digest creation was successful; < 0 if an error - * occurred + * Return: see crypto_ahash_final() */ int crypto_ahash_digest(struct ahash_request *req); @@ -493,8 +499,7 @@ static inline int crypto_ahash_import(struct ahash_request *req, const void *in) * handle. Any potentially existing state created by previous operations is * discarded. * - * Return: 0 if the message digest initialization was successful; < 0 if an - * error occurred + * Return: see crypto_ahash_final() */ static inline int crypto_ahash_init(struct ahash_request *req) { @@ -510,8 +515,7 @@ static inline int crypto_ahash_init(struct ahash_request *req) * is pointed to by the scatter/gather list registered in the &ahash_request * handle * - * Return: 0 if the message digest update was successful; < 0 if an error - * occurred + * Return: see crypto_ahash_final() */ static inline int crypto_ahash_update(struct ahash_request *req) { -- cgit v1.2.3-59-g8ed1b From 4f0fc1600edbdb3612b931aa5076ca984f7ee8fe Mon Sep 17 00:00:00 2001 From: Gilad Ben-Yossef Date: Mon, 21 Aug 2017 13:51:28 +0300 Subject: crypto: sm3 - add OSCCA SM3 secure hash Add OSCCA SM3 secure hash (OSCCA GM/T 0004-2012 SM3) generic hash transformation. Signed-off-by: Gilad Ben-Yossef Signed-off-by: Herbert Xu --- crypto/Kconfig | 11 +++ crypto/Makefile | 1 + crypto/sm3_generic.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++ include/crypto/sm3.h | 40 +++++++++ include/crypto/sm3_base.h | 117 ++++++++++++++++++++++++++ 5 files changed, 379 insertions(+) create mode 100644 crypto/sm3_generic.c create mode 100644 include/crypto/sm3.h create mode 100644 include/crypto/sm3_base.h (limited to 'include') diff --git a/crypto/Kconfig b/crypto/Kconfig index 0a121f9ddf8e..97fb7360207b 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -859,6 +859,17 @@ config CRYPTO_SHA3 References: http://keccak.noekeon.org/ +config CRYPTO_SM3 + tristate "SM3 digest algorithm" + select CRYPTO_HASH + help + SM3 secure hash function as defined by OSCCA GM/T 0004-2012 SM3). + It is part of the Chinese Commercial Cryptography suite. + + References: + http://www.oscca.gov.cn/UpFile/20101222141857786.pdf + https://datatracker.ietf.org/doc/html/draft-shen-sm3-hash + config CRYPTO_TGR192 tristate "Tiger digest algorithms" select CRYPTO_HASH diff --git a/crypto/Makefile b/crypto/Makefile index d41f0331b085..723b0b41dab9 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -70,6 +70,7 @@ obj-$(CONFIG_CRYPTO_SHA1) += sha1_generic.o obj-$(CONFIG_CRYPTO_SHA256) += sha256_generic.o obj-$(CONFIG_CRYPTO_SHA512) += sha512_generic.o obj-$(CONFIG_CRYPTO_SHA3) += sha3_generic.o +obj-$(CONFIG_CRYPTO_SM3) += sm3_generic.o obj-$(CONFIG_CRYPTO_WP512) += wp512.o CFLAGS_wp512.o := $(call cc-option,-fno-schedule-insns) # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79149 obj-$(CONFIG_CRYPTO_TGR192) += tgr192.o diff --git a/crypto/sm3_generic.c b/crypto/sm3_generic.c new file mode 100644 index 000000000000..9e823d99f095 --- /dev/null +++ b/crypto/sm3_generic.c @@ -0,0 +1,210 @@ +/* + * SM3 secure hash, as specified by OSCCA GM/T 0004-2012 SM3 and + * described at https://tools.ietf.org/html/draft-shen-sm3-hash-01 + * + * Copyright (C) 2017 ARM Limited or its affiliates. + * Written by Gilad Ben-Yossef + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const u8 sm3_zero_message_hash[SM3_DIGEST_SIZE] = { + 0x1A, 0xB2, 0x1D, 0x83, 0x55, 0xCF, 0xA1, 0x7F, + 0x8e, 0x61, 0x19, 0x48, 0x31, 0xE8, 0x1A, 0x8F, + 0x22, 0xBE, 0xC8, 0xC7, 0x28, 0xFE, 0xFB, 0x74, + 0x7E, 0xD0, 0x35, 0xEB, 0x50, 0x82, 0xAA, 0x2B +}; +EXPORT_SYMBOL_GPL(sm3_zero_message_hash); + +static inline u32 p0(u32 x) +{ + return x ^ rol32(x, 9) ^ rol32(x, 17); +} + +static inline u32 p1(u32 x) +{ + return x ^ rol32(x, 15) ^ rol32(x, 23); +} + +static inline u32 ff(unsigned int n, u32 a, u32 b, u32 c) +{ + return (n < 16) ? (a ^ b ^ c) : ((a & b) | (a & c) | (b & c)); +} + +static inline u32 gg(unsigned int n, u32 e, u32 f, u32 g) +{ + return (n < 16) ? (e ^ f ^ g) : ((e & f) | ((~e) & g)); +} + +static inline u32 t(unsigned int n) +{ + return (n < 16) ? SM3_T1 : SM3_T2; +} + +static void sm3_expand(u32 *t, u32 *w, u32 *wt) +{ + int i; + unsigned int tmp; + + /* load the input */ + for (i = 0; i <= 15; i++) + w[i] = get_unaligned_be32((__u32 *)t + i); + + for (i = 16; i <= 67; i++) { + tmp = w[i - 16] ^ w[i - 9] ^ rol32(w[i - 3], 15); + w[i] = p1(tmp) ^ (rol32(w[i - 13], 7)) ^ w[i - 6]; + } + + for (i = 0; i <= 63; i++) + wt[i] = w[i] ^ w[i + 4]; +} + +static void sm3_compress(u32 *w, u32 *wt, u32 *m) +{ + u32 ss1; + u32 ss2; + u32 tt1; + u32 tt2; + u32 a, b, c, d, e, f, g, h; + int i; + + a = m[0]; + b = m[1]; + c = m[2]; + d = m[3]; + e = m[4]; + f = m[5]; + g = m[6]; + h = m[7]; + + for (i = 0; i <= 63; i++) { + + ss1 = rol32((rol32(a, 12) + e + rol32(t(i), i)), 7); + + ss2 = ss1 ^ rol32(a, 12); + + tt1 = ff(i, a, b, c) + d + ss2 + *wt; + wt++; + + tt2 = gg(i, e, f, g) + h + ss1 + *w; + w++; + + d = c; + c = rol32(b, 9); + b = a; + a = tt1; + h = g; + g = rol32(f, 19); + f = e; + e = p0(tt2); + } + + m[0] = a ^ m[0]; + m[1] = b ^ m[1]; + m[2] = c ^ m[2]; + m[3] = d ^ m[3]; + m[4] = e ^ m[4]; + m[5] = f ^ m[5]; + m[6] = g ^ m[6]; + m[7] = h ^ m[7]; + + a = b = c = d = e = f = g = h = ss1 = ss2 = tt1 = tt2 = 0; +} + +static void sm3_transform(struct sm3_state *sst, u8 const *src) +{ + unsigned int w[68]; + unsigned int wt[64]; + + sm3_expand((u32 *)src, w, wt); + sm3_compress(w, wt, sst->state); + + memzero_explicit(w, sizeof(w)); + memzero_explicit(wt, sizeof(wt)); +} + +static void sm3_generic_block_fn(struct sm3_state *sst, u8 const *src, + int blocks) +{ + while (blocks--) { + sm3_transform(sst, src); + src += SM3_BLOCK_SIZE; + } +} + +int crypto_sm3_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + return sm3_base_do_update(desc, data, len, sm3_generic_block_fn); +} +EXPORT_SYMBOL(crypto_sm3_update); + +static int sm3_final(struct shash_desc *desc, u8 *out) +{ + sm3_base_do_finalize(desc, sm3_generic_block_fn); + return sm3_base_finish(desc, out); +} + +int crypto_sm3_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *hash) +{ + sm3_base_do_update(desc, data, len, sm3_generic_block_fn); + return sm3_final(desc, hash); +} +EXPORT_SYMBOL(crypto_sm3_finup); + +static struct shash_alg sm3_alg = { + .digestsize = SM3_DIGEST_SIZE, + .init = sm3_base_init, + .update = crypto_sm3_update, + .final = sm3_final, + .finup = crypto_sm3_finup, + .descsize = sizeof(struct sm3_state), + .base = { + .cra_name = "sm3", + .cra_driver_name = "sm3-generic", + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SM3_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}; + +static int __init sm3_generic_mod_init(void) +{ + return crypto_register_shash(&sm3_alg); +} + +static void __exit sm3_generic_mod_fini(void) +{ + crypto_unregister_shash(&sm3_alg); +} + +module_init(sm3_generic_mod_init); +module_exit(sm3_generic_mod_fini); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("SM3 Secure Hash Algorithm"); + +MODULE_ALIAS_CRYPTO("sm3"); +MODULE_ALIAS_CRYPTO("sm3-generic"); diff --git a/include/crypto/sm3.h b/include/crypto/sm3.h new file mode 100644 index 000000000000..1438942dc773 --- /dev/null +++ b/include/crypto/sm3.h @@ -0,0 +1,40 @@ +/* + * Common values for SM3 algorithm + */ + +#ifndef _CRYPTO_SM3_H +#define _CRYPTO_SM3_H + +#include + +#define SM3_DIGEST_SIZE 32 +#define SM3_BLOCK_SIZE 64 + +#define SM3_T1 0x79CC4519 +#define SM3_T2 0x7A879D8A + +#define SM3_IVA 0x7380166f +#define SM3_IVB 0x4914b2b9 +#define SM3_IVC 0x172442d7 +#define SM3_IVD 0xda8a0600 +#define SM3_IVE 0xa96f30bc +#define SM3_IVF 0x163138aa +#define SM3_IVG 0xe38dee4d +#define SM3_IVH 0xb0fb0e4e + +extern const u8 sm3_zero_message_hash[SM3_DIGEST_SIZE]; + +struct sm3_state { + u32 state[SM3_DIGEST_SIZE / 4]; + u64 count; + u8 buffer[SM3_BLOCK_SIZE]; +}; + +struct shash_desc; + +extern int crypto_sm3_update(struct shash_desc *desc, const u8 *data, + unsigned int len); + +extern int crypto_sm3_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *hash); +#endif diff --git a/include/crypto/sm3_base.h b/include/crypto/sm3_base.h new file mode 100644 index 000000000000..256948e39296 --- /dev/null +++ b/include/crypto/sm3_base.h @@ -0,0 +1,117 @@ +/* + * sm3_base.h - core logic for SM3 implementations + * + * Copyright (C) 2017 ARM Limited or its affiliates. + * Written by Gilad Ben-Yossef + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include +#include +#include +#include +#include + +typedef void (sm3_block_fn)(struct sm3_state *sst, u8 const *src, int blocks); + +static inline int sm3_base_init(struct shash_desc *desc) +{ + struct sm3_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SM3_IVA; + sctx->state[1] = SM3_IVB; + sctx->state[2] = SM3_IVC; + sctx->state[3] = SM3_IVD; + sctx->state[4] = SM3_IVE; + sctx->state[5] = SM3_IVF; + sctx->state[6] = SM3_IVG; + sctx->state[7] = SM3_IVH; + sctx->count = 0; + + return 0; +} + +static inline int sm3_base_do_update(struct shash_desc *desc, + const u8 *data, + unsigned int len, + sm3_block_fn *block_fn) +{ + struct sm3_state *sctx = shash_desc_ctx(desc); + unsigned int partial = sctx->count % SM3_BLOCK_SIZE; + + sctx->count += len; + + if (unlikely((partial + len) >= SM3_BLOCK_SIZE)) { + int blocks; + + if (partial) { + int p = SM3_BLOCK_SIZE - partial; + + memcpy(sctx->buffer + partial, data, p); + data += p; + len -= p; + + block_fn(sctx, sctx->buffer, 1); + } + + blocks = len / SM3_BLOCK_SIZE; + len %= SM3_BLOCK_SIZE; + + if (blocks) { + block_fn(sctx, data, blocks); + data += blocks * SM3_BLOCK_SIZE; + } + partial = 0; + } + if (len) + memcpy(sctx->buffer + partial, data, len); + + return 0; +} + +static inline int sm3_base_do_finalize(struct shash_desc *desc, + sm3_block_fn *block_fn) +{ + const int bit_offset = SM3_BLOCK_SIZE - sizeof(__be64); + struct sm3_state *sctx = shash_desc_ctx(desc); + __be64 *bits = (__be64 *)(sctx->buffer + bit_offset); + unsigned int partial = sctx->count % SM3_BLOCK_SIZE; + + sctx->buffer[partial++] = 0x80; + if (partial > bit_offset) { + memset(sctx->buffer + partial, 0x0, SM3_BLOCK_SIZE - partial); + partial = 0; + + block_fn(sctx, sctx->buffer, 1); + } + + memset(sctx->buffer + partial, 0x0, bit_offset - partial); + *bits = cpu_to_be64(sctx->count << 3); + block_fn(sctx, sctx->buffer, 1); + + return 0; +} + +static inline int sm3_base_finish(struct shash_desc *desc, u8 *out) +{ + struct sm3_state *sctx = shash_desc_ctx(desc); + __be32 *digest = (__be32 *)out; + int i; + + for (i = 0; i < SM3_DIGEST_SIZE / sizeof(__be32); i++) + put_unaligned_be32(sctx->state[i], digest++); + + *sctx = (struct sm3_state){}; + return 0; +} -- cgit v1.2.3-59-g8ed1b From ef780324592dd639e4bfbc5b9bf8934b234b7c99 Mon Sep 17 00:00:00 2001 From: Corentin LABBE Date: Tue, 22 Aug 2017 10:08:08 +0200 Subject: crypto: gcm - add GCM IV size constant Many GCM users use directly GCM IV size instead of using some constant. This patch add all IV size constant used by GCM. Signed-off-by: Corentin Labbe Signed-off-by: Herbert Xu --- include/crypto/gcm.h | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 include/crypto/gcm.h (limited to 'include') diff --git a/include/crypto/gcm.h b/include/crypto/gcm.h new file mode 100644 index 000000000000..c50e057ea17e --- /dev/null +++ b/include/crypto/gcm.h @@ -0,0 +1,8 @@ +#ifndef _CRYPTO_GCM_H +#define _CRYPTO_GCM_H + +#define GCM_AES_IV_SIZE 12 +#define GCM_RFC4106_IV_SIZE 8 +#define GCM_RFC4543_IV_SIZE 8 + +#endif -- cgit v1.2.3-59-g8ed1b From 242c1a28eb61cb34974e8aa05235d84355940a8a Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Fri, 22 Sep 2017 10:25:22 +0800 Subject: net: Remove useless function skb_header_release There is no one which would invokes the function skb_header_release. So just remove it now. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- drivers/net/usb/asix_common.c | 2 +- include/linux/skbuff.h | 19 ------------------- net/batman-adv/soft-interface.c | 2 +- 3 files changed, 2 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 522d2900cd1d..f4d7362eb325 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -245,7 +245,7 @@ struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, * - We are allowed to put 4 bytes at tail if skb_cloned() * is false (and if we have 4 bytes of tailroom) * - * TCP packets for example are cloned, but skb_header_release() + * TCP packets for example are cloned, but __skb_header_release() * was called in tcp stack, allowing us to use headroom for our needs. */ if (!skb_header_cloned(skb) && diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 492828801acb..f9db5539a6fb 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1456,28 +1456,9 @@ static inline int skb_header_unclone(struct sk_buff *skb, gfp_t pri) return 0; } -/** - * skb_header_release - release reference to header - * @skb: buffer to operate on - * - * Drop a reference to the header part of the buffer. This is done - * by acquiring a payload reference. You must not read from the header - * part of skb->data after this. - * Note : Check if you can use __skb_header_release() instead. - */ -static inline void skb_header_release(struct sk_buff *skb) -{ - BUG_ON(skb->nohdr); - skb->nohdr = 1; - atomic_add(1 << SKB_DATAREF_SHIFT, &skb_shinfo(skb)->dataref); -} - /** * __skb_header_release - release reference to header * @skb: buffer to operate on - * - * Variant of skb_header_release() assuming skb is private to caller. - * We can avoid one atomic operation. */ static inline void __skb_header_release(struct sk_buff *skb) { diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 10f7edfb176e..c2c986746d0b 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -69,7 +69,7 @@ int batadv_skb_head_push(struct sk_buff *skb, unsigned int len) int result; /* TODO: We must check if we can release all references to non-payload - * data using skb_header_release in our skbs to allow skb_cow_header to + * data using __skb_header_release in our skbs to allow skb_cow_header to * work optimally. This means that those skbs are not allowed to read * or write any data which is before the current position of skb->data * after that call and thus allow other skbs with the same data buffer -- cgit v1.2.3-59-g8ed1b From 333ef6bd10c3ffdaf6da94e34dc6cae675ed27fc Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 15 Aug 2017 10:07:25 -0400 Subject: media: cec: add CEC_EVENT_PIN_HPD_LOW/HIGH events Add support for two new low-level events: PIN_HPD_LOW and PIN_HPD_HIGH. This is specifically meant for use with the upcoming cec-gpio driver and makes it possible to trace when the HPD pin changes. Some HDMI sinks do strange things with the HPD and this makes it easy to debug this. Note that this also moves the initialization of a devnode mutex and list to the allocate_adapter function: if the HPD is high, then as soon as the HPD interrupt is created an interrupt occurs and cec_queue_pin_hpd_event() is called which requires that the devnode mutex and list are initialized. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-adap.c | 18 +++++++++++++++++- drivers/media/cec/cec-api.c | 18 ++++++++++++++---- drivers/media/cec/cec-core.c | 8 ++++---- include/media/cec-pin.h | 4 ++++ include/media/cec.h | 12 +++++++++++- include/uapi/linux/cec.h | 2 ++ 6 files changed, 52 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index 84d1b67f850c..dd0c9cacd1d0 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -86,7 +86,7 @@ void cec_queue_event_fh(struct cec_fh *fh, const struct cec_event *new_ev, u64 ts) { static const u8 max_events[CEC_NUM_EVENTS] = { - 1, 1, 64, 64, + 1, 1, 64, 64, 8, 8, }; struct cec_event_entry *entry; unsigned int ev_idx = new_ev->event - 1; @@ -170,6 +170,22 @@ void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, ktime_t ts) } EXPORT_SYMBOL_GPL(cec_queue_pin_cec_event); +/* Notify userspace that the HPD pin changed state at the given time. */ +void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts) +{ + struct cec_event ev = { + .event = is_high ? CEC_EVENT_PIN_HPD_HIGH : + CEC_EVENT_PIN_HPD_LOW, + }; + struct cec_fh *fh; + + mutex_lock(&adap->devnode.lock); + list_for_each_entry(fh, &adap->devnode.fhs, list) + cec_queue_event_fh(fh, &ev, ktime_to_ns(ts)); + mutex_unlock(&adap->devnode.lock); +} +EXPORT_SYMBOL_GPL(cec_queue_pin_hpd_event); + /* * Queue a new message for this filehandle. * diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index a079f7fe018c..465bb3ec21f6 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -529,7 +529,7 @@ static int cec_open(struct inode *inode, struct file *filp) * Initial events that are automatically sent when the cec device is * opened. */ - struct cec_event ev_state = { + struct cec_event ev = { .event = CEC_EVENT_STATE_CHANGE, .flags = CEC_EVENT_FL_INITIAL_STATE, }; @@ -569,9 +569,19 @@ static int cec_open(struct inode *inode, struct file *filp) filp->private_data = fh; /* Queue up initial state events */ - ev_state.state_change.phys_addr = adap->phys_addr; - ev_state.state_change.log_addr_mask = adap->log_addrs.log_addr_mask; - cec_queue_event_fh(fh, &ev_state, 0); + ev.state_change.phys_addr = adap->phys_addr; + ev.state_change.log_addr_mask = adap->log_addrs.log_addr_mask; + cec_queue_event_fh(fh, &ev, 0); +#ifdef CONFIG_CEC_PIN + if (adap->pin && adap->pin->ops->read_hpd) { + err = adap->pin->ops->read_hpd(adap); + if (err >= 0) { + ev.event = err ? CEC_EVENT_PIN_HPD_HIGH : + CEC_EVENT_PIN_HPD_LOW; + cec_queue_event_fh(fh, &ev, 0); + } + } +#endif list_add(&fh->list, &devnode->fhs); mutex_unlock(&devnode->lock); diff --git a/drivers/media/cec/cec-core.c b/drivers/media/cec/cec-core.c index 648136e552d5..e3a1fb6d6690 100644 --- a/drivers/media/cec/cec-core.c +++ b/drivers/media/cec/cec-core.c @@ -112,10 +112,6 @@ static int __must_check cec_devnode_register(struct cec_devnode *devnode, int minor; int ret; - /* Initialization */ - INIT_LIST_HEAD(&devnode->fhs); - mutex_init(&devnode->lock); - /* Part 1: Find a free minor number */ mutex_lock(&cec_devnode_lock); minor = find_next_zero_bit(cec_devnode_nums, CEC_NUM_DEVICES, 0); @@ -242,6 +238,10 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, INIT_LIST_HEAD(&adap->wait_queue); init_waitqueue_head(&adap->kthread_waitq); + /* adap->devnode initialization */ + INIT_LIST_HEAD(&adap->devnode.fhs); + mutex_init(&adap->devnode.lock); + adap->kthread = kthread_run(cec_thread_func, adap, "cec-%s", name); if (IS_ERR(adap->kthread)) { pr_err("cec-%s: kernel_thread() failed\n", name); diff --git a/include/media/cec-pin.h b/include/media/cec-pin.h index f09cc9579d53..ea84b9c9e0c3 100644 --- a/include/media/cec-pin.h +++ b/include/media/cec-pin.h @@ -97,6 +97,9 @@ enum cec_pin_state { * @free: optional. Free any allocated resources. Called when the * adapter is deleted. * @status: optional, log status information. + * @read_hpd: read the HPD pin. Return true if high, false if low or + * an error if negative. If NULL or -ENOTTY is returned, + * then this is not supported. * * These operations are used by the cec pin framework to manipulate * the CEC pin. @@ -109,6 +112,7 @@ struct cec_pin_ops { void (*disable_irq)(struct cec_adapter *adap); void (*free)(struct cec_adapter *adap); void (*status)(struct cec_adapter *adap, struct seq_file *file); + int (*read_hpd)(struct cec_adapter *adap); }; #define CEC_NUM_PIN_EVENTS 128 diff --git a/include/media/cec.h b/include/media/cec.h index df6b3bd31284..9d0f983faea9 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -91,7 +91,7 @@ struct cec_event_entry { }; #define CEC_NUM_CORE_EVENTS 2 -#define CEC_NUM_EVENTS CEC_EVENT_PIN_CEC_HIGH +#define CEC_NUM_EVENTS CEC_EVENT_PIN_HPD_HIGH struct cec_fh { struct list_head list; @@ -296,6 +296,16 @@ static inline void cec_received_msg(struct cec_adapter *adap, void cec_queue_pin_cec_event(struct cec_adapter *adap, bool is_high, ktime_t ts); +/** + * cec_queue_pin_hpd_event() - queue a pin event with a given timestamp. + * + * @adap: pointer to the cec adapter + * @is_high: when true the HPD pin is high, otherwise it is low + * @ts: the timestamp for this event + * + */ +void cec_queue_pin_hpd_event(struct cec_adapter *adap, bool is_high, ktime_t ts); + /** * cec_get_edid_phys_addr() - find and return the physical address * diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h index 4351c3481aea..b9f8df3a0477 100644 --- a/include/uapi/linux/cec.h +++ b/include/uapi/linux/cec.h @@ -410,6 +410,8 @@ struct cec_log_addrs { #define CEC_EVENT_LOST_MSGS 2 #define CEC_EVENT_PIN_CEC_LOW 3 #define CEC_EVENT_PIN_CEC_HIGH 4 +#define CEC_EVENT_PIN_HPD_LOW 5 +#define CEC_EVENT_PIN_HPD_HIGH 6 #define CEC_EVENT_FL_INITIAL_STATE (1 << 0) #define CEC_EVENT_FL_DROPPED_EVENTS (1 << 1) -- cgit v1.2.3-59-g8ed1b From 9267d90c564fc4ac7e51b922fa03107bb8c78739 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 31 Aug 2017 04:12:53 -0400 Subject: media: cec.h: initialize *parent and *port in cec_phys_addr_validate Make sure these values are set to avoid 'uninitialized variable' warnings. Hasn't happened yet, but better safe than sorry. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/cec.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/media/cec.h b/include/media/cec.h index 9d0f983faea9..16341210d3ba 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -427,6 +427,10 @@ static inline u16 cec_phys_addr_for_input(u16 phys_addr, u8 input) static inline int cec_phys_addr_validate(u16 phys_addr, u16 *parent, u16 *port) { + if (parent) + *parent = phys_addr; + if (port) + *port = 0; return 0; } -- cgit v1.2.3-59-g8ed1b From 373b8eeb0c15d4ce58f62afb12f213b1b5bbc3d3 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 21 Sep 2017 23:45:43 +0300 Subject: xfrm: make aead_len() return unsigned int Key lengths can't be negative. Comparison with nla_len() is left signed just in case negative value can sneak in there. Signed-off-by: Alexey Dobriyan Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 2 +- net/xfrm/xfrm_user.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index f002a2c5e33c..0be4c547e383 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1764,7 +1764,7 @@ static inline int xfrm_acquire_is_on(struct net *net) } #endif -static inline int aead_len(struct xfrm_algo_aead *alg) +static inline unsigned int aead_len(struct xfrm_algo_aead *alg) { return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 2bfbd9121e3b..32c67b80c3ce 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -84,7 +84,7 @@ static int verify_aead(struct nlattr **attrs) return 0; algp = nla_data(rt); - if (nla_len(rt) < aead_len(algp)) + if (nla_len(rt) < (int)aead_len(algp)) return -EINVAL; algp->alg_name[sizeof(algp->alg_name) - 1] = '\0'; -- cgit v1.2.3-59-g8ed1b From 06cd22f830f28023b82455c82c7db65fc6cf9c16 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 21 Sep 2017 23:46:30 +0300 Subject: xfrm: make xfrm_alg_len() return unsigned int Key lengths can't be negative. Comparison with nla_len() is left signed just in case negative value can sneak in there. Signed-off-by: Alexey Dobriyan Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 2 +- net/xfrm/xfrm_user.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 0be4c547e383..2abc0e117f11 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1769,7 +1769,7 @@ static inline unsigned int aead_len(struct xfrm_algo_aead *alg) return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); } -static inline int xfrm_alg_len(const struct xfrm_algo *alg) +static inline unsigned int xfrm_alg_len(const struct xfrm_algo *alg) { return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 32c67b80c3ce..09512d90e6a5 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -42,7 +42,7 @@ static int verify_one_alg(struct nlattr **attrs, enum xfrm_attr_type_t type) return 0; algp = nla_data(rt); - if (nla_len(rt) < xfrm_alg_len(algp)) + if (nla_len(rt) < (int)xfrm_alg_len(algp)) return -EINVAL; switch (type) { -- cgit v1.2.3-59-g8ed1b From 1bd963a72e859d194d87a5a2a8839efee7e23102 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 21 Sep 2017 23:47:09 +0300 Subject: xfrm: make xfrm_alg_auth_len() return unsigned int Key lengths can't be negative. Comparison with nla_len() is left signed just in case negative value can sneak in there. Signed-off-by: Alexey Dobriyan Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 2 +- net/xfrm/xfrm_user.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 2abc0e117f11..5d5e11b653eb 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1774,7 +1774,7 @@ static inline unsigned int xfrm_alg_len(const struct xfrm_algo *alg) return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); } -static inline int xfrm_alg_auth_len(const struct xfrm_algo_auth *alg) +static inline unsigned int xfrm_alg_auth_len(const struct xfrm_algo_auth *alg) { return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 09512d90e6a5..465c23d4ea78 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -68,7 +68,7 @@ static int verify_auth_trunc(struct nlattr **attrs) return 0; algp = nla_data(rt); - if (nla_len(rt) < xfrm_alg_auth_len(algp)) + if (nla_len(rt) < (int)xfrm_alg_auth_len(algp)) return -EINVAL; algp->alg_name[sizeof(algp->alg_name) - 1] = '\0'; -- cgit v1.2.3-59-g8ed1b From 5e708e47c44366453c33373940455a75fd33f635 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Thu, 21 Sep 2017 23:47:50 +0300 Subject: xfrm: make xfrm_replay_state_esn_len() return unsigned int Replay detection bitmaps can't have negative length. Comparisons with nla_len() are left signed just in case negative value can sneak in there. Propagate unsignedness for code size savings: add/remove: 0/0 grow/shrink: 0/5 up/down: 0/-38 (-38) function old new delta xfrm_state_construct 1802 1800 -2 xfrm_update_ae_params 295 289 -6 xfrm_state_migrate 1345 1339 -6 xfrm_replay_notify_esn 349 337 -12 xfrm_replay_notify_bmp 345 333 -12 Signed-off-by: Alexey Dobriyan Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 2 +- net/xfrm/xfrm_user.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 5d5e11b653eb..3cb618bbcfa5 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1779,7 +1779,7 @@ static inline unsigned int xfrm_alg_auth_len(const struct xfrm_algo_auth *alg) return sizeof(*alg) + ((alg->alg_key_len + 7) / 8); } -static inline int xfrm_replay_state_esn_len(struct xfrm_replay_state_esn *replay_esn) +static inline unsigned int xfrm_replay_state_esn_len(struct xfrm_replay_state_esn *replay_esn) { return sizeof(*replay_esn) + replay_esn->bmp_len * sizeof(__u32); } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 465c23d4ea78..83718db5ec9c 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -130,7 +130,7 @@ static inline int verify_replay(struct xfrm_usersa_info *p, if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8) return -EINVAL; - if (nla_len(rt) < xfrm_replay_state_esn_len(rs) && + if (nla_len(rt) < (int)xfrm_replay_state_esn_len(rs) && nla_len(rt) != sizeof(*rs)) return -EINVAL; } @@ -404,7 +404,7 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es struct nlattr *rp) { struct xfrm_replay_state_esn *up; - int ulen; + unsigned int ulen; if (!replay_esn || !rp) return 0; @@ -414,7 +414,7 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es /* Check the overall length and the internal bitmap length to avoid * potential overflow. */ - if (nla_len(rp) < ulen || + if (nla_len(rp) < (int)ulen || xfrm_replay_state_esn_len(replay_esn) != ulen || replay_esn->bmp_len != up->bmp_len) return -EINVAL; @@ -430,14 +430,14 @@ static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn struct nlattr *rta) { struct xfrm_replay_state_esn *p, *pp, *up; - int klen, ulen; + unsigned int klen, ulen; if (!rta) return 0; up = nla_data(rta); klen = xfrm_replay_state_esn_len(up); - ulen = nla_len(rta) >= klen ? klen : sizeof(*up); + ulen = nla_len(rta) >= (int)klen ? klen : sizeof(*up); p = kzalloc(klen, GFP_KERNEL); if (!p) -- cgit v1.2.3-59-g8ed1b From cfb766da54d98ceb145e1bb0bd11c559569dcbfc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Sep 2017 08:12:04 -0700 Subject: sched/cputime: Expose cputime_adjust() Will be used by basic cgroup resource stat reporting later. Signed-off-by: Tejun Heo Acked-by: Peter Zijlstra Cc: Ingo Molnar Cc: Li Zefan Cc: Johannes Weiner --- include/linux/sched/cputime.h | 3 ++- kernel/sched/cputime.c | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/sched/cputime.h b/include/linux/sched/cputime.h index 4c5b9735c1ae..9251044335c5 100644 --- a/include/linux/sched/cputime.h +++ b/include/linux/sched/cputime.h @@ -53,7 +53,8 @@ static inline void task_cputime_scaled(struct task_struct *t, extern void task_cputime_adjusted(struct task_struct *p, u64 *ut, u64 *st); extern void thread_group_cputime_adjusted(struct task_struct *p, u64 *ut, u64 *st); - +extern void cputime_adjust(struct task_cputime *curr, struct prev_cputime *prev, + u64 *ut, u64 *st); /* * Thread group CPU time accounting. diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index 14d2dbf97c53..8839b6e8a104 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -585,9 +585,8 @@ drop_precision: * * Assuming that rtime_i+1 >= rtime_i. */ -static void cputime_adjust(struct task_cputime *curr, - struct prev_cputime *prev, - u64 *ut, u64 *st) +void cputime_adjust(struct task_cputime *curr, struct prev_cputime *prev, + u64 *ut, u64 *st) { u64 rtime, stime, utime; unsigned long flags; -- cgit v1.2.3-59-g8ed1b From d2cc5ed6949085cfba30ec5228816cf6eb1d02b9 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Sep 2017 08:12:04 -0700 Subject: cpuacct: Introduce cgroup_account_cputime[_field]() Introduce cgroup_account_cputime[_field]() which wrap cpuacct_charge() and cgroup_account_field(). This doesn't introduce any functional changes and will be used to add cgroup basic resource accounting. Signed-off-by: Tejun Heo Acked-by: Peter Zijlstra Cc: Ingo Molnar --- include/linux/cgroup.h | 38 ++++++++++++++++++++++++++++++++++++++ kernel/sched/cpuacct.h | 17 ----------------- kernel/sched/cputime.c | 2 +- kernel/sched/deadline.c | 2 +- kernel/sched/fair.c | 2 +- kernel/sched/rt.c | 2 +- kernel/sched/sched.h | 2 +- kernel/sched/stop_task.c | 2 +- 8 files changed, 44 insertions(+), 23 deletions(-) delete mode 100644 kernel/sched/cpuacct.h (limited to 'include') diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index d023ac5e377f..6cd579329310 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -688,6 +689,43 @@ static inline void cgroup_path_from_kernfs_id(const union kernfs_node_id *id, char *buf, size_t buflen) {} #endif /* !CONFIG_CGROUPS */ +/* + * Basic resource stats. + */ +#ifdef CONFIG_CGROUPS + +#ifdef CONFIG_CGROUP_CPUACCT +void cpuacct_charge(struct task_struct *tsk, u64 cputime); +void cpuacct_account_field(struct task_struct *tsk, int index, u64 val); +#else +static inline void cpuacct_charge(struct task_struct *tsk, u64 cputime) {} +static inline void cpuacct_account_field(struct task_struct *tsk, int index, + u64 val) {} +#endif + +static inline void cgroup_account_cputime(struct task_struct *task, + u64 delta_exec) +{ + cpuacct_charge(task, delta_exec); +} + +static inline void cgroup_account_cputime_field(struct task_struct *task, + enum cpu_usage_stat index, + u64 delta_exec) +{ + cpuacct_account_field(task, index, delta_exec); +} + +#else /* CONFIG_CGROUPS */ + +static inline void cgroup_account_cputime(struct task_struct *task, + u64 delta_exec) {} +static inline void cgroup_account_cputime_field(struct task_struct *task, + enum cpu_usage_stat index, + u64 delta_exec) {} + +#endif /* CONFIG_CGROUPS */ + /* * sock->sk_cgrp_data handling. For more info, see sock_cgroup_data * definition in cgroup-defs.h. diff --git a/kernel/sched/cpuacct.h b/kernel/sched/cpuacct.h deleted file mode 100644 index ba72807c73d4..000000000000 --- a/kernel/sched/cpuacct.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifdef CONFIG_CGROUP_CPUACCT - -extern void cpuacct_charge(struct task_struct *tsk, u64 cputime); -extern void cpuacct_account_field(struct task_struct *tsk, int index, u64 val); - -#else - -static inline void cpuacct_charge(struct task_struct *tsk, u64 cputime) -{ -} - -static inline void -cpuacct_account_field(struct task_struct *tsk, int index, u64 val) -{ -} - -#endif diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c index 8839b6e8a104..e01b699bbd5b 100644 --- a/kernel/sched/cputime.c +++ b/kernel/sched/cputime.c @@ -109,7 +109,7 @@ static inline void task_group_account_field(struct task_struct *p, int index, */ __this_cpu_add(kernel_cpustat.cpustat[index], tmp); - cpuacct_account_field(p, index, tmp); + cgroup_account_cputime_field(p, index, tmp); } /* diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 0191ec7667c3..abd913c1b99e 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1143,7 +1143,7 @@ static void update_curr_dl(struct rq *rq) account_group_exec_runtime(curr, delta_exec); curr->se.exec_start = rq_clock_task(rq); - cpuacct_charge(curr, delta_exec); + cgroup_account_cputime(curr, delta_exec); sched_rt_avg_update(rq, delta_exec); diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 70ba32e08a23..0ae69af95b8b 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -851,7 +851,7 @@ static void update_curr(struct cfs_rq *cfs_rq) struct task_struct *curtask = task_of(curr); trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime); - cpuacct_charge(curtask, delta_exec); + cgroup_account_cputime(curtask, delta_exec); account_group_exec_runtime(curtask, delta_exec); } diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 0af5ca9e3e3f..fdc2c5d1f82e 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -979,7 +979,7 @@ static void update_curr_rt(struct rq *rq) account_group_exec_runtime(curr, delta_exec); curr->se.exec_start = rq_clock_task(rq); - cpuacct_charge(curr, delta_exec); + cgroup_account_cputime(curr, delta_exec); sched_rt_avg_update(rq, delta_exec); diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h index 14db76cd496f..f0b98f978843 100644 --- a/kernel/sched/sched.h +++ b/kernel/sched/sched.h @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef CONFIG_PARAVIRT #include @@ -36,7 +37,6 @@ #include "cpupri.h" #include "cpudeadline.h" -#include "cpuacct.h" #ifdef CONFIG_SCHED_DEBUG # define SCHED_WARN_ON(x) WARN_ONCE(x, #x) diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c index 9f69fb630853..ec0bb5ab9024 100644 --- a/kernel/sched/stop_task.c +++ b/kernel/sched/stop_task.c @@ -71,7 +71,7 @@ static void put_prev_task_stop(struct rq *rq, struct task_struct *prev) account_group_exec_runtime(curr, delta_exec); curr->se.exec_start = rq_clock_task(rq); - cpuacct_charge(curr, delta_exec); + cgroup_account_cputime(curr, delta_exec); } static void task_tick_stop(struct rq *rq, struct task_struct *curr, int queued) -- cgit v1.2.3-59-g8ed1b From 041cd640b2f3c5607171c59d8712b503659d21f7 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 25 Sep 2017 08:12:05 -0700 Subject: cgroup: Implement cgroup2 basic CPU usage accounting In cgroup1, while cpuacct isn't actually controlling any resources, it is a separate controller due to combination of two factors - 1. enabling cpu controller has significant side effects, and 2. we have to pick one of the hierarchies to account CPU usages on. cpuacct controller is effectively used to designate a hierarchy to track CPU usages on. cgroup2's unified hierarchy removes the second reason and we can account basic CPU usages by default. While we can use cpuacct for this purpose, both its interface and implementation leave a lot to be desired - it collects and exposes two sources of truth which don't agree with each other and some of the exposed statistics don't make much sense. Also, it propagates all the way up the hierarchy on each accounting event which is unnecessary. This patch adds basic resource accounting mechanism to cgroup2's unified hierarchy and accounts CPU usages using it. * All accountings are done per-cpu and don't propagate immediately. It just bumps the per-cgroup per-cpu counters and links to the parent's updated list if not already on it. * On a read, the per-cpu counters are collected into the global ones and then propagated upwards. Only the per-cpu counters which have changed since the last read are propagated. * CPU usage stats are collected and shown in "cgroup.stat" with "cpu." prefix. Total usage is collected from scheduling events. User/sys breakdown is sourced from tick sampling and adjusted to the usage using cputime_adjust(). This keeps the accounting side hot path O(1) and per-cpu and the read side O(nr_updated_since_last_read). v2: Minor changes and documentation updates as suggested by Waiman and Roman. Signed-off-by: Tejun Heo Acked-by: Peter Zijlstra Cc: Ingo Molnar Cc: Li Zefan Cc: Johannes Weiner Cc: Waiman Long Cc: Roman Gushchin --- Documentation/cgroup-v2.txt | 9 ++ include/linux/cgroup-defs.h | 57 +++++++ include/linux/cgroup.h | 22 +++ kernel/cgroup/Makefile | 2 +- kernel/cgroup/cgroup-internal.h | 8 + kernel/cgroup/cgroup.c | 24 ++- kernel/cgroup/stat.c | 334 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 453 insertions(+), 3 deletions(-) create mode 100644 kernel/cgroup/stat.c (limited to 'include') diff --git a/Documentation/cgroup-v2.txt b/Documentation/cgroup-v2.txt index dc44785dc0fa..3f8216912df0 100644 --- a/Documentation/cgroup-v2.txt +++ b/Documentation/cgroup-v2.txt @@ -886,6 +886,15 @@ All cgroup core files are prefixed with "cgroup." A dying cgroup can consume system resources not exceeding limits, which were active at the moment of cgroup deletion. + cpu.usage_usec + CPU time consumed in the subtree. + + cpu.user_usec + User CPU time consumed in the subtree. + + cpu.system_usec + System CPU time consumed in the subtree. + Controllers =========== diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index ade4a78a54c2..3e55bbd31ad1 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -254,6 +255,57 @@ struct css_set { struct rcu_head rcu_head; }; +/* + * cgroup basic resource usage statistics. Accounting is done per-cpu in + * cgroup_cpu_stat which is then lazily propagated up the hierarchy on + * reads. + * + * When a stat gets updated, the cgroup_cpu_stat and its ancestors are + * linked into the updated tree. On the following read, propagation only + * considers and consumes the updated tree. This makes reading O(the + * number of descendants which have been active since last read) instead of + * O(the total number of descendants). + * + * This is important because there can be a lot of (draining) cgroups which + * aren't active and stat may be read frequently. The combination can + * become very expensive. By propagating selectively, increasing reading + * frequency decreases the cost of each read. + */ +struct cgroup_cpu_stat { + /* + * ->sync protects all the current counters. These are the only + * fields which get updated in the hot path. + */ + struct u64_stats_sync sync; + struct task_cputime cputime; + + /* + * Snapshots at the last reading. These are used to calculate the + * deltas to propagate to the global counters. + */ + struct task_cputime last_cputime; + + /* + * Child cgroups with stat updates on this cpu since the last read + * are linked on the parent's ->updated_children through + * ->updated_next. + * + * In addition to being more compact, singly-linked list pointing + * to the cgroup makes it unnecessary for each per-cpu struct to + * point back to the associated cgroup. + * + * Protected by per-cpu cgroup_cpu_stat_lock. + */ + struct cgroup *updated_children; /* terminated by self cgroup */ + struct cgroup *updated_next; /* NULL iff not on the list */ +}; + +struct cgroup_stat { + /* per-cpu statistics are collected into the folowing global counters */ + struct task_cputime cputime; + struct prev_cputime prev_cputime; +}; + struct cgroup { /* self css with NULL ->ss, points back to this cgroup */ struct cgroup_subsys_state self; @@ -353,6 +405,11 @@ struct cgroup { */ struct cgroup *dom_cgrp; + /* cgroup basic resource statistics */ + struct cgroup_cpu_stat __percpu *cpu_stat; + struct cgroup_stat pending_stat; /* pending from children */ + struct cgroup_stat stat; + /* * list of pidlists, up to two for each namespace (one for procs, one * for tasks); created on demand. diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 6cd579329310..328a70ce0e23 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -703,17 +703,39 @@ static inline void cpuacct_account_field(struct task_struct *tsk, int index, u64 val) {} #endif +void cgroup_stat_show_cputime(struct seq_file *seq, const char *prefix); + +void __cgroup_account_cputime(struct cgroup *cgrp, u64 delta_exec); +void __cgroup_account_cputime_field(struct cgroup *cgrp, + enum cpu_usage_stat index, u64 delta_exec); + static inline void cgroup_account_cputime(struct task_struct *task, u64 delta_exec) { + struct cgroup *cgrp; + cpuacct_charge(task, delta_exec); + + rcu_read_lock(); + cgrp = task_dfl_cgroup(task); + if (cgroup_parent(cgrp)) + __cgroup_account_cputime(cgrp, delta_exec); + rcu_read_unlock(); } static inline void cgroup_account_cputime_field(struct task_struct *task, enum cpu_usage_stat index, u64 delta_exec) { + struct cgroup *cgrp; + cpuacct_account_field(task, index, delta_exec); + + rcu_read_lock(); + cgrp = task_dfl_cgroup(task); + if (cgroup_parent(cgrp)) + __cgroup_account_cputime_field(cgrp, index, delta_exec); + rcu_read_unlock(); } #else /* CONFIG_CGROUPS */ diff --git a/kernel/cgroup/Makefile b/kernel/cgroup/Makefile index ce693ccb8c58..0acee616e06c 100644 --- a/kernel/cgroup/Makefile +++ b/kernel/cgroup/Makefile @@ -1,4 +1,4 @@ -obj-y := cgroup.o namespace.o cgroup-v1.o +obj-y := cgroup.o stat.o namespace.o cgroup-v1.o obj-$(CONFIG_CGROUP_FREEZER) += freezer.o obj-$(CONFIG_CGROUP_PIDS) += pids.o diff --git a/kernel/cgroup/cgroup-internal.h b/kernel/cgroup/cgroup-internal.h index 5151ff256c29..fa642c99586a 100644 --- a/kernel/cgroup/cgroup-internal.h +++ b/kernel/cgroup/cgroup-internal.h @@ -199,6 +199,14 @@ int cgroup_show_path(struct seq_file *sf, struct kernfs_node *kf_node, int cgroup_task_count(const struct cgroup *cgrp); +/* + * stat.c + */ +void cgroup_stat_flush(struct cgroup *cgrp); +int cgroup_stat_init(struct cgroup *cgrp); +void cgroup_stat_exit(struct cgroup *cgrp); +void cgroup_stat_boot(void); + /* * namespace.c */ diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index d6551cd45238..d036625556c9 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -142,12 +142,14 @@ static struct static_key_true *cgroup_subsys_on_dfl_key[] = { }; #undef SUBSYS +static DEFINE_PER_CPU(struct cgroup_cpu_stat, cgrp_dfl_root_cpu_stat); + /* * The default hierarchy, reserved for the subsystems that are otherwise * unattached - it never has more than a single cgroup, and all tasks are * part of that cgroup. */ -struct cgroup_root cgrp_dfl_root; +struct cgroup_root cgrp_dfl_root = { .cgrp.cpu_stat = &cgrp_dfl_root_cpu_stat }; EXPORT_SYMBOL_GPL(cgrp_dfl_root); /* @@ -3301,6 +3303,8 @@ static int cgroup_stat_show(struct seq_file *seq, void *v) seq_printf(seq, "nr_dying_descendants %d\n", cgroup->nr_dying_descendants); + cgroup_stat_show_cputime(seq, "cpu."); + return 0; } @@ -4471,6 +4475,8 @@ static void css_free_work_fn(struct work_struct *work) */ cgroup_put(cgroup_parent(cgrp)); kernfs_put(cgrp->kn); + if (cgroup_on_dfl(cgrp)) + cgroup_stat_exit(cgrp); kfree(cgrp); } else { /* @@ -4515,6 +4521,9 @@ static void css_release_work_fn(struct work_struct *work) /* cgroup release path */ trace_cgroup_release(cgrp); + if (cgroup_on_dfl(cgrp)) + cgroup_stat_flush(cgrp); + for (tcgrp = cgroup_parent(cgrp); tcgrp; tcgrp = cgroup_parent(tcgrp)) tcgrp->nr_dying_descendants--; @@ -4698,6 +4707,12 @@ static struct cgroup *cgroup_create(struct cgroup *parent) if (ret) goto out_free_cgrp; + if (cgroup_on_dfl(parent)) { + ret = cgroup_stat_init(cgrp); + if (ret) + goto out_cancel_ref; + } + /* * Temporarily set the pointer to NULL, so idr_find() won't return * a half-baked cgroup. @@ -4705,7 +4720,7 @@ static struct cgroup *cgroup_create(struct cgroup *parent) cgrp->id = cgroup_idr_alloc(&root->cgroup_idr, NULL, 2, 0, GFP_KERNEL); if (cgrp->id < 0) { ret = -ENOMEM; - goto out_cancel_ref; + goto out_stat_exit; } init_cgroup_housekeeping(cgrp); @@ -4754,6 +4769,9 @@ static struct cgroup *cgroup_create(struct cgroup *parent) return cgrp; +out_stat_exit: + if (cgroup_on_dfl(parent)) + cgroup_stat_exit(cgrp); out_cancel_ref: percpu_ref_exit(&cgrp->self.refcnt); out_free_cgrp: @@ -5148,6 +5166,8 @@ int __init cgroup_init(void) BUG_ON(cgroup_init_cftypes(NULL, cgroup_base_files)); BUG_ON(cgroup_init_cftypes(NULL, cgroup1_base_files)); + cgroup_stat_boot(); + /* * The latency of the synchronize_sched() is too high for cgroups, * avoid it at the cost of forcing all readers into the slow path. diff --git a/kernel/cgroup/stat.c b/kernel/cgroup/stat.c new file mode 100644 index 000000000000..9cce79e89320 --- /dev/null +++ b/kernel/cgroup/stat.c @@ -0,0 +1,334 @@ +#include "cgroup-internal.h" + +#include + +static DEFINE_MUTEX(cgroup_stat_mutex); +static DEFINE_PER_CPU(raw_spinlock_t, cgroup_cpu_stat_lock); + +static struct cgroup_cpu_stat *cgroup_cpu_stat(struct cgroup *cgrp, int cpu) +{ + return per_cpu_ptr(cgrp->cpu_stat, cpu); +} + +/** + * cgroup_cpu_stat_updated - keep track of updated cpu_stat + * @cgrp: target cgroup + * @cpu: cpu on which cpu_stat was updated + * + * @cgrp's cpu_stat on @cpu was updated. Put it on the parent's matching + * cpu_stat->updated_children list. See the comment on top of + * cgroup_cpu_stat definition for details. + */ +static void cgroup_cpu_stat_updated(struct cgroup *cgrp, int cpu) +{ + raw_spinlock_t *cpu_lock = per_cpu_ptr(&cgroup_cpu_stat_lock, cpu); + struct cgroup *parent; + unsigned long flags; + + /* + * Speculative already-on-list test. This may race leading to + * temporary inaccuracies, which is fine. + * + * Because @parent's updated_children is terminated with @parent + * instead of NULL, we can tell whether @cgrp is on the list by + * testing the next pointer for NULL. + */ + if (cgroup_cpu_stat(cgrp, cpu)->updated_next) + return; + + raw_spin_lock_irqsave(cpu_lock, flags); + + /* put @cgrp and all ancestors on the corresponding updated lists */ + for (parent = cgroup_parent(cgrp); parent; + cgrp = parent, parent = cgroup_parent(cgrp)) { + struct cgroup_cpu_stat *cstat = cgroup_cpu_stat(cgrp, cpu); + struct cgroup_cpu_stat *pcstat = cgroup_cpu_stat(parent, cpu); + + /* + * Both additions and removals are bottom-up. If a cgroup + * is already in the tree, all ancestors are. + */ + if (cstat->updated_next) + break; + + cstat->updated_next = pcstat->updated_children; + pcstat->updated_children = cgrp; + } + + raw_spin_unlock_irqrestore(cpu_lock, flags); +} + +/** + * cgroup_cpu_stat_pop_updated - iterate and dismantle cpu_stat updated tree + * @pos: current position + * @root: root of the tree to traversal + * @cpu: target cpu + * + * Walks the udpated cpu_stat tree on @cpu from @root. %NULL @pos starts + * the traversal and %NULL return indicates the end. During traversal, + * each returned cgroup is unlinked from the tree. Must be called with the + * matching cgroup_cpu_stat_lock held. + * + * The only ordering guarantee is that, for a parent and a child pair + * covered by a given traversal, if a child is visited, its parent is + * guaranteed to be visited afterwards. + */ +static struct cgroup *cgroup_cpu_stat_pop_updated(struct cgroup *pos, + struct cgroup *root, int cpu) +{ + struct cgroup_cpu_stat *cstat; + struct cgroup *parent; + + if (pos == root) + return NULL; + + /* + * We're gonna walk down to the first leaf and visit/remove it. We + * can pick whatever unvisited node as the starting point. + */ + if (!pos) + pos = root; + else + pos = cgroup_parent(pos); + + /* walk down to the first leaf */ + while (true) { + cstat = cgroup_cpu_stat(pos, cpu); + if (cstat->updated_children == pos) + break; + pos = cstat->updated_children; + } + + /* + * Unlink @pos from the tree. As the updated_children list is + * singly linked, we have to walk it to find the removal point. + * However, due to the way we traverse, @pos will be the first + * child in most cases. The only exception is @root. + */ + parent = cgroup_parent(pos); + if (parent && cstat->updated_next) { + struct cgroup_cpu_stat *pcstat = cgroup_cpu_stat(parent, cpu); + struct cgroup_cpu_stat *ncstat; + struct cgroup **nextp; + + nextp = &pcstat->updated_children; + while (true) { + ncstat = cgroup_cpu_stat(*nextp, cpu); + if (*nextp == pos) + break; + + WARN_ON_ONCE(*nextp == parent); + nextp = &ncstat->updated_next; + } + + *nextp = cstat->updated_next; + cstat->updated_next = NULL; + } + + return pos; +} + +static void cgroup_stat_accumulate(struct cgroup_stat *dst_stat, + struct cgroup_stat *src_stat) +{ + dst_stat->cputime.utime += src_stat->cputime.utime; + dst_stat->cputime.stime += src_stat->cputime.stime; + dst_stat->cputime.sum_exec_runtime += src_stat->cputime.sum_exec_runtime; +} + +static void cgroup_cpu_stat_flush_one(struct cgroup *cgrp, int cpu) +{ + struct cgroup *parent = cgroup_parent(cgrp); + struct cgroup_cpu_stat *cstat = cgroup_cpu_stat(cgrp, cpu); + struct task_cputime *last_cputime = &cstat->last_cputime; + struct task_cputime cputime; + struct cgroup_stat delta; + unsigned seq; + + lockdep_assert_held(&cgroup_stat_mutex); + + /* fetch the current per-cpu values */ + do { + seq = __u64_stats_fetch_begin(&cstat->sync); + cputime = cstat->cputime; + } while (__u64_stats_fetch_retry(&cstat->sync, seq)); + + /* accumulate the deltas to propgate */ + delta.cputime.utime = cputime.utime - last_cputime->utime; + delta.cputime.stime = cputime.stime - last_cputime->stime; + delta.cputime.sum_exec_runtime = cputime.sum_exec_runtime - + last_cputime->sum_exec_runtime; + *last_cputime = cputime; + + /* transfer the pending stat into delta */ + cgroup_stat_accumulate(&delta, &cgrp->pending_stat); + memset(&cgrp->pending_stat, 0, sizeof(cgrp->pending_stat)); + + /* propagate delta into the global stat and the parent's pending */ + cgroup_stat_accumulate(&cgrp->stat, &delta); + if (parent) + cgroup_stat_accumulate(&parent->pending_stat, &delta); +} + +/* see cgroup_stat_flush() */ +static void cgroup_stat_flush_locked(struct cgroup *cgrp) +{ + int cpu; + + lockdep_assert_held(&cgroup_stat_mutex); + + for_each_possible_cpu(cpu) { + raw_spinlock_t *cpu_lock = per_cpu_ptr(&cgroup_cpu_stat_lock, cpu); + struct cgroup *pos = NULL; + + raw_spin_lock_irq(cpu_lock); + while ((pos = cgroup_cpu_stat_pop_updated(pos, cgrp, cpu))) + cgroup_cpu_stat_flush_one(pos, cpu); + raw_spin_unlock_irq(cpu_lock); + } +} + +/** + * cgroup_stat_flush - flush stats in @cgrp's subtree + * @cgrp: target cgroup + * + * Collect all per-cpu stats in @cgrp's subtree into the global counters + * and propagate them upwards. After this function returns, all cgroups in + * the subtree have up-to-date ->stat. + * + * This also gets all cgroups in the subtree including @cgrp off the + * ->updated_children lists. + */ +void cgroup_stat_flush(struct cgroup *cgrp) +{ + mutex_lock(&cgroup_stat_mutex); + cgroup_stat_flush_locked(cgrp); + mutex_unlock(&cgroup_stat_mutex); +} + +static struct cgroup_cpu_stat *cgroup_cpu_stat_account_begin(struct cgroup *cgrp) +{ + struct cgroup_cpu_stat *cstat; + + cstat = get_cpu_ptr(cgrp->cpu_stat); + u64_stats_update_begin(&cstat->sync); + return cstat; +} + +static void cgroup_cpu_stat_account_end(struct cgroup *cgrp, + struct cgroup_cpu_stat *cstat) +{ + u64_stats_update_end(&cstat->sync); + cgroup_cpu_stat_updated(cgrp, smp_processor_id()); + put_cpu_ptr(cstat); +} + +void __cgroup_account_cputime(struct cgroup *cgrp, u64 delta_exec) +{ + struct cgroup_cpu_stat *cstat; + + cstat = cgroup_cpu_stat_account_begin(cgrp); + cstat->cputime.sum_exec_runtime += delta_exec; + cgroup_cpu_stat_account_end(cgrp, cstat); +} + +void __cgroup_account_cputime_field(struct cgroup *cgrp, + enum cpu_usage_stat index, u64 delta_exec) +{ + struct cgroup_cpu_stat *cstat; + + cstat = cgroup_cpu_stat_account_begin(cgrp); + + switch (index) { + case CPUTIME_USER: + case CPUTIME_NICE: + cstat->cputime.utime += delta_exec; + break; + case CPUTIME_SYSTEM: + case CPUTIME_IRQ: + case CPUTIME_SOFTIRQ: + cstat->cputime.stime += delta_exec; + break; + default: + break; + } + + cgroup_cpu_stat_account_end(cgrp, cstat); +} + +void cgroup_stat_show_cputime(struct seq_file *seq, const char *prefix) +{ + struct cgroup *cgrp = seq_css(seq)->cgroup; + u64 usage, utime, stime; + + if (!cgroup_parent(cgrp)) + return; + + mutex_lock(&cgroup_stat_mutex); + + cgroup_stat_flush_locked(cgrp); + + usage = cgrp->stat.cputime.sum_exec_runtime; + cputime_adjust(&cgrp->stat.cputime, &cgrp->stat.prev_cputime, + &utime, &stime); + + mutex_unlock(&cgroup_stat_mutex); + + do_div(usage, NSEC_PER_USEC); + do_div(utime, NSEC_PER_USEC); + do_div(stime, NSEC_PER_USEC); + + seq_printf(seq, "%susage_usec %llu\n" + "%suser_usec %llu\n" + "%ssystem_usec %llu\n", + prefix, usage, prefix, utime, prefix, stime); +} + +int cgroup_stat_init(struct cgroup *cgrp) +{ + int cpu; + + /* the root cgrp has cpu_stat preallocated */ + if (!cgrp->cpu_stat) { + cgrp->cpu_stat = alloc_percpu(struct cgroup_cpu_stat); + if (!cgrp->cpu_stat) + return -ENOMEM; + } + + /* ->updated_children list is self terminated */ + for_each_possible_cpu(cpu) + cgroup_cpu_stat(cgrp, cpu)->updated_children = cgrp; + + prev_cputime_init(&cgrp->stat.prev_cputime); + + return 0; +} + +void cgroup_stat_exit(struct cgroup *cgrp) +{ + int cpu; + + cgroup_stat_flush(cgrp); + + /* sanity check */ + for_each_possible_cpu(cpu) { + struct cgroup_cpu_stat *cstat = cgroup_cpu_stat(cgrp, cpu); + + if (WARN_ON_ONCE(cstat->updated_children != cgrp) || + WARN_ON_ONCE(cstat->updated_next)) + return; + } + + free_percpu(cgrp->cpu_stat); + cgrp->cpu_stat = NULL; +} + +void __init cgroup_stat_boot(void) +{ + int cpu; + + for_each_possible_cpu(cpu) + raw_spin_lock_init(per_cpu_ptr(&cgroup_cpu_stat_lock, cpu)); + + BUG_ON(cgroup_stat_init(&cgrp_dfl_root.cgrp)); +} -- cgit v1.2.3-59-g8ed1b From 1d66af81905a4e2f3d03913f5449a8e9b5d3facd Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Fri, 9 Jun 2017 00:29:00 +0200 Subject: clk: bcm2835: remove remains from stub clk driver This commit removes the fixed clocks introduced as a stub clock driver added with commit 75fabc3f6448 ("ARM: bcm2835: add stub clock driver"). Originally they were used to drive the AMBA bus and PL011 uart driver. Now these clocks are derived by the CPRMAN clock driver and configured in DT. Additionally, get rid of init_machine function in bcm2835 board file as there's nothing to do any longer. Signed-off-by: Danilo Krummrich Acked-by: Eric Anholt Acked-by: Stephen Boyd Signed-off-by: Stefan Wahren --- arch/arm/mach-bcm/board_bcm2835.c | 7 ------- drivers/clk/bcm/clk-bcm2835-aux.c | 1 - drivers/clk/bcm/clk-bcm2835.c | 30 ------------------------------ include/linux/clk/bcm2835.h | 24 ------------------------ 4 files changed, 62 deletions(-) delete mode 100644 include/linux/clk/bcm2835.h (limited to 'include') diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c index 0c1edfc98696..24af33f91705 100644 --- a/arch/arm/mach-bcm/board_bcm2835.c +++ b/arch/arm/mach-bcm/board_bcm2835.c @@ -15,16 +15,10 @@ #include #include #include -#include #include #include -static void __init bcm2835_init(void) -{ - bcm2835_init_clocks(); -} - static const char * const bcm2835_compat[] = { #ifdef CONFIG_ARCH_MULTI_V6 "brcm,bcm2835", @@ -36,6 +30,5 @@ static const char * const bcm2835_compat[] = { }; DT_MACHINE_START(BCM2835, "BCM2835") - .init_machine = bcm2835_init, .dt_compat = bcm2835_compat MACHINE_END diff --git a/drivers/clk/bcm/clk-bcm2835-aux.c b/drivers/clk/bcm/clk-bcm2835-aux.c index bd750cf2238d..77e276d61702 100644 --- a/drivers/clk/bcm/clk-bcm2835-aux.c +++ b/drivers/clk/bcm/clk-bcm2835-aux.c @@ -14,7 +14,6 @@ #include #include -#include #include #include #include diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 58ce6af8452d..44301a3d9963 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -416,35 +415,6 @@ static int bcm2835_debugfs_regset(struct bcm2835_cprman *cprman, u32 base, return regdump ? 0 : -ENOMEM; } -/* - * These are fixed clocks. They're probably not all root clocks and it may - * be possible to turn them on and off but until this is mapped out better - * it's the only way they can be used. - */ -void __init bcm2835_init_clocks(void) -{ - struct clk_hw *hw; - int ret; - - hw = clk_hw_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 126000000); - if (IS_ERR(hw)) - pr_err("apb_pclk not registered\n"); - - hw = clk_hw_register_fixed_rate(NULL, "uart0_pclk", NULL, 0, 3000000); - if (IS_ERR(hw)) - pr_err("uart0_pclk not registered\n"); - ret = clk_hw_register_clkdev(hw, NULL, "20201000.uart"); - if (ret) - pr_err("uart0_pclk alias not registered\n"); - - hw = clk_hw_register_fixed_rate(NULL, "uart1_pclk", NULL, 0, 125000000); - if (IS_ERR(hw)) - pr_err("uart1_pclk not registered\n"); - ret = clk_hw_register_clkdev(hw, NULL, "20215000.uart"); - if (ret) - pr_err("uart1_pclk alias not registered\n"); -} - struct bcm2835_pll_data { const char *name; u32 cm_ctrl_reg; diff --git a/include/linux/clk/bcm2835.h b/include/linux/clk/bcm2835.h deleted file mode 100644 index aa937f6c17da..000000000000 --- a/include/linux/clk/bcm2835.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2010 Broadcom - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef __LINUX_CLK_BCM2835_H_ -#define __LINUX_CLK_BCM2835_H_ - -void __init bcm2835_init_clocks(void); - -#endif -- cgit v1.2.3-59-g8ed1b From 88bbe85dcd37aa2662c1a83962c15009fc12503e Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sun, 6 Aug 2017 17:52:02 +0200 Subject: irqchip: bcm2836: Move SMP startup code to arch/arm (v2) In order to easily provide SMP for BCM2837 on 32-bit and 64-bit the SMP startup code was placed in irq-bcm2836. That's not the right approach. So move this code where it belongs. Signed-off-by: Stefan Wahren Fixes: 41f4988cc287 ("irqchip/bcm2836: Add SMP support for the 2836") Tested-by: Eric Anholt Acked-by: Marc Zyngier --- arch/arm/mach-bcm/Makefile | 5 +++ arch/arm/mach-bcm/board_bcm2835.c | 6 ++- arch/arm/mach-bcm/platsmp.c | 35 ++++++++++++++++ arch/arm/mach-bcm/platsmp.h | 10 +++++ drivers/irqchip/irq-bcm2836.c | 79 +------------------------------------ include/linux/irqchip/irq-bcm2836.h | 70 ++++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 78 deletions(-) create mode 100644 arch/arm/mach-bcm/platsmp.h create mode 100644 include/linux/irqchip/irq-bcm2836.h (limited to 'include') diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile index 980f5850097c..62a59008c5a8 100644 --- a/arch/arm/mach-bcm/Makefile +++ b/arch/arm/mach-bcm/Makefile @@ -43,6 +43,11 @@ endif # BCM2835 obj-$(CONFIG_ARCH_BCM2835) += board_bcm2835.o +ifeq ($(CONFIG_ARCH_BCM2835),y) +ifeq ($(CONFIG_ARM),y) +obj-$(CONFIG_SMP) += platsmp.o +endif +endif # BCM5301X obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o diff --git a/arch/arm/mach-bcm/board_bcm2835.c b/arch/arm/mach-bcm/board_bcm2835.c index 24af33f91705..8cff865ace04 100644 --- a/arch/arm/mach-bcm/board_bcm2835.c +++ b/arch/arm/mach-bcm/board_bcm2835.c @@ -19,16 +19,20 @@ #include #include +#include "platsmp.h" + static const char * const bcm2835_compat[] = { #ifdef CONFIG_ARCH_MULTI_V6 "brcm,bcm2835", #endif #ifdef CONFIG_ARCH_MULTI_V7 "brcm,bcm2836", + "brcm,bcm2837", #endif NULL }; DT_MACHINE_START(BCM2835, "BCM2835") - .dt_compat = bcm2835_compat + .dt_compat = bcm2835_compat, + .smp = smp_ops(bcm2836_smp_ops), MACHINE_END diff --git a/arch/arm/mach-bcm/platsmp.c b/arch/arm/mach-bcm/platsmp.c index 9e3f275934eb..34506a608fea 100644 --- a/arch/arm/mach-bcm/platsmp.c +++ b/arch/arm/mach-bcm/platsmp.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -287,6 +288,35 @@ out: return ret; } +static int bcm2836_boot_secondary(unsigned int cpu, struct task_struct *idle) +{ + void __iomem *intc_base; + struct device_node *dn; + char *name; + + name = "brcm,bcm2836-l1-intc"; + dn = of_find_compatible_node(NULL, NULL, name); + if (!dn) { + pr_err("unable to find intc node\n"); + return -ENODEV; + } + + intc_base = of_iomap(dn, 0); + of_node_put(dn); + + if (!intc_base) { + pr_err("unable to remap intc base register\n"); + return -ENOMEM; + } + + writel(virt_to_phys(secondary_startup), + intc_base + LOCAL_MAILBOX3_SET0 + 16 * cpu); + + iounmap(intc_base); + + return 0; +} + static const struct smp_operations kona_smp_ops __initconst = { .smp_prepare_cpus = bcm_smp_prepare_cpus, .smp_boot_secondary = kona_boot_secondary, @@ -305,3 +335,8 @@ static const struct smp_operations nsp_smp_ops __initconst = { .smp_boot_secondary = nsp_boot_secondary, }; CPU_METHOD_OF_DECLARE(bcm_smp_nsp, "brcm,bcm-nsp-smp", &nsp_smp_ops); + +const struct smp_operations bcm2836_smp_ops __initconst = { + .smp_boot_secondary = bcm2836_boot_secondary, +}; +CPU_METHOD_OF_DECLARE(bcm_smp_bcm2836, "brcm,bcm2836-smp", &bcm2836_smp_ops); diff --git a/arch/arm/mach-bcm/platsmp.h b/arch/arm/mach-bcm/platsmp.h new file mode 100644 index 000000000000..b8b8b3fa350d --- /dev/null +++ b/arch/arm/mach-bcm/platsmp.h @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2017 Stefan Wahren + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + */ + +extern const struct smp_operations bcm2836_smp_ops; diff --git a/drivers/irqchip/irq-bcm2836.c b/drivers/irqchip/irq-bcm2836.c index dc8c1e3eafe7..667b9e14b032 100644 --- a/drivers/irqchip/irq-bcm2836.c +++ b/drivers/irqchip/irq-bcm2836.c @@ -19,62 +19,9 @@ #include #include #include -#include - -#define LOCAL_CONTROL 0x000 -#define LOCAL_PRESCALER 0x008 +#include -/* - * The low 2 bits identify the CPU that the GPU IRQ goes to, and the - * next 2 bits identify the CPU that the GPU FIQ goes to. - */ -#define LOCAL_GPU_ROUTING 0x00c -/* When setting bits 0-3, enables PMU interrupts on that CPU. */ -#define LOCAL_PM_ROUTING_SET 0x010 -/* When setting bits 0-3, disables PMU interrupts on that CPU. */ -#define LOCAL_PM_ROUTING_CLR 0x014 -/* - * The low 4 bits of this are the CPU's timer IRQ enables, and the - * next 4 bits are the CPU's timer FIQ enables (which override the IRQ - * bits). - */ -#define LOCAL_TIMER_INT_CONTROL0 0x040 -/* - * The low 4 bits of this are the CPU's per-mailbox IRQ enables, and - * the next 4 bits are the CPU's per-mailbox FIQ enables (which - * override the IRQ bits). - */ -#define LOCAL_MAILBOX_INT_CONTROL0 0x050 -/* - * The CPU's interrupt status register. Bits are defined by the the - * LOCAL_IRQ_* bits below. - */ -#define LOCAL_IRQ_PENDING0 0x060 -/* Same status bits as above, but for FIQ. */ -#define LOCAL_FIQ_PENDING0 0x070 -/* - * Mailbox write-to-set bits. There are 16 mailboxes, 4 per CPU, and - * these bits are organized by mailbox number and then CPU number. We - * use mailbox 0 for IPIs. The mailbox's interrupt is raised while - * any bit is set. - */ -#define LOCAL_MAILBOX0_SET0 0x080 -#define LOCAL_MAILBOX3_SET0 0x08c -/* Mailbox write-to-clear bits. */ -#define LOCAL_MAILBOX0_CLR0 0x0c0 -#define LOCAL_MAILBOX3_CLR0 0x0cc - -#define LOCAL_IRQ_CNTPSIRQ 0 -#define LOCAL_IRQ_CNTPNSIRQ 1 -#define LOCAL_IRQ_CNTHPIRQ 2 -#define LOCAL_IRQ_CNTVIRQ 3 -#define LOCAL_IRQ_MAILBOX0 4 -#define LOCAL_IRQ_MAILBOX1 5 -#define LOCAL_IRQ_MAILBOX2 6 -#define LOCAL_IRQ_MAILBOX3 7 -#define LOCAL_IRQ_GPU_FAST 8 -#define LOCAL_IRQ_PMU_FAST 9 -#define LAST_IRQ LOCAL_IRQ_PMU_FAST +#include struct bcm2836_arm_irqchip_intc { struct irq_domain *domain; @@ -215,24 +162,6 @@ static int bcm2836_cpu_dying(unsigned int cpu) cpu); return 0; } - -#ifdef CONFIG_ARM -static int __init bcm2836_smp_boot_secondary(unsigned int cpu, - struct task_struct *idle) -{ - unsigned long secondary_startup_phys = - (unsigned long)virt_to_phys((void *)secondary_startup); - - writel(secondary_startup_phys, - intc.base + LOCAL_MAILBOX3_SET0 + 16 * cpu); - - return 0; -} - -static const struct smp_operations bcm2836_smp_ops __initconst = { - .smp_boot_secondary = bcm2836_smp_boot_secondary, -}; -#endif #endif static const struct irq_domain_ops bcm2836_arm_irqchip_intc_ops = { @@ -249,10 +178,6 @@ bcm2836_arm_irqchip_smp_init(void) bcm2836_cpu_dying); set_smp_cross_call(bcm2836_arm_irqchip_send_ipi); - -#ifdef CONFIG_ARM - smp_set_ops(&bcm2836_smp_ops); -#endif #endif } diff --git a/include/linux/irqchip/irq-bcm2836.h b/include/linux/irqchip/irq-bcm2836.h new file mode 100644 index 000000000000..218a6e1b18d8 --- /dev/null +++ b/include/linux/irqchip/irq-bcm2836.h @@ -0,0 +1,70 @@ +/* + * Root interrupt controller for the BCM2836 (Raspberry Pi 2). + * + * Copyright 2015 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#define LOCAL_CONTROL 0x000 +#define LOCAL_PRESCALER 0x008 + +/* + * The low 2 bits identify the CPU that the GPU IRQ goes to, and the + * next 2 bits identify the CPU that the GPU FIQ goes to. + */ +#define LOCAL_GPU_ROUTING 0x00c +/* When setting bits 0-3, enables PMU interrupts on that CPU. */ +#define LOCAL_PM_ROUTING_SET 0x010 +/* When setting bits 0-3, disables PMU interrupts on that CPU. */ +#define LOCAL_PM_ROUTING_CLR 0x014 +/* + * The low 4 bits of this are the CPU's timer IRQ enables, and the + * next 4 bits are the CPU's timer FIQ enables (which override the IRQ + * bits). + */ +#define LOCAL_TIMER_INT_CONTROL0 0x040 +/* + * The low 4 bits of this are the CPU's per-mailbox IRQ enables, and + * the next 4 bits are the CPU's per-mailbox FIQ enables (which + * override the IRQ bits). + */ +#define LOCAL_MAILBOX_INT_CONTROL0 0x050 +/* + * The CPU's interrupt status register. Bits are defined by the the + * LOCAL_IRQ_* bits below. + */ +#define LOCAL_IRQ_PENDING0 0x060 +/* Same status bits as above, but for FIQ. */ +#define LOCAL_FIQ_PENDING0 0x070 +/* + * Mailbox write-to-set bits. There are 16 mailboxes, 4 per CPU, and + * these bits are organized by mailbox number and then CPU number. We + * use mailbox 0 for IPIs. The mailbox's interrupt is raised while + * any bit is set. + */ +#define LOCAL_MAILBOX0_SET0 0x080 +#define LOCAL_MAILBOX3_SET0 0x08c +/* Mailbox write-to-clear bits. */ +#define LOCAL_MAILBOX0_CLR0 0x0c0 +#define LOCAL_MAILBOX3_CLR0 0x0cc + +#define LOCAL_IRQ_CNTPSIRQ 0 +#define LOCAL_IRQ_CNTPNSIRQ 1 +#define LOCAL_IRQ_CNTHPIRQ 2 +#define LOCAL_IRQ_CNTVIRQ 3 +#define LOCAL_IRQ_MAILBOX0 4 +#define LOCAL_IRQ_MAILBOX1 5 +#define LOCAL_IRQ_MAILBOX2 6 +#define LOCAL_IRQ_MAILBOX3 7 +#define LOCAL_IRQ_GPU_FAST 8 +#define LOCAL_IRQ_PMU_FAST 9 +#define LAST_IRQ LOCAL_IRQ_PMU_FAST -- cgit v1.2.3-59-g8ed1b From a380f2edef65b2447a043251bb3c00a9d2153a8b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 25 Sep 2017 14:56:44 +0200 Subject: PM / core: Drop legacy class suspend/resume operations There are no classes using the legacy suspend/resume operations in the tree any more, so drop these operations and update the code referring to them accordingly. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- drivers/base/power/main.c | 32 +++++++++----------------------- include/linux/device.h | 5 ----- 2 files changed, 9 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 770b1539a083..12abcf6084a5 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -848,16 +848,10 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) goto Driver; } - if (dev->class) { - if (dev->class->pm) { - info = "class "; - callback = pm_op(dev->class->pm, state); - goto Driver; - } else if (dev->class->resume) { - info = "legacy class "; - callback = dev->class->resume; - goto End; - } + if (dev->class && dev->class->pm) { + info = "class "; + callback = pm_op(dev->class->pm, state); + goto Driver; } if (dev->bus) { @@ -1508,17 +1502,10 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) goto Run; } - if (dev->class) { - if (dev->class->pm) { - info = "class "; - callback = pm_op(dev->class->pm, state); - goto Run; - } else if (dev->class->suspend) { - pm_dev_dbg(dev, state, "legacy class "); - error = legacy_suspend(dev, state, dev->class->suspend, - "legacy class "); - goto End; - } + if (dev->class && dev->class->pm) { + info = "class "; + callback = pm_op(dev->class->pm, state); + goto Run; } if (dev->bus) { @@ -1862,8 +1849,7 @@ void device_pm_check_callbacks(struct device *dev) dev->power.no_pm_callbacks = (!dev->bus || (pm_ops_is_empty(dev->bus->pm) && !dev->bus->suspend && !dev->bus->resume)) && - (!dev->class || (pm_ops_is_empty(dev->class->pm) && - !dev->class->suspend && !dev->class->resume)) && + (!dev->class || pm_ops_is_empty(dev->class->pm)) && (!dev->type || pm_ops_is_empty(dev->type->pm)) && (!dev->pm_domain || pm_ops_is_empty(&dev->pm_domain->ops)) && (!dev->driver || (pm_ops_is_empty(dev->driver->pm) && diff --git a/include/linux/device.h b/include/linux/device.h index 1d2607923a24..c1527f887050 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -372,9 +372,6 @@ int subsys_virtual_register(struct bus_type *subsys, * @devnode: Callback to provide the devtmpfs. * @class_release: Called to release this class. * @dev_release: Called to release the device. - * @suspend: Used to put the device to sleep mode, usually to a low power - * state. - * @resume: Used to bring the device from the sleep mode. * @shutdown_pre: Called at shut-down time before driver shutdown. * @ns_type: Callbacks so sysfs can detemine namespaces. * @namespace: Namespace of the device belongs to this class. @@ -402,8 +399,6 @@ struct class { void (*class_release)(struct class *class); void (*dev_release)(struct device *dev); - int (*suspend)(struct device *dev, pm_message_t state); - int (*resume)(struct device *dev); int (*shutdown_pre)(struct device *dev); const struct kobj_ns_type_operations *ns_type; -- cgit v1.2.3-59-g8ed1b From b35cd9884fa5d81c9d5e7f57c9d03264ae2bd835 Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 23 May 2017 10:28:26 -0700 Subject: lib: Add shared copies of some GCC library routines Many ports (m32r, microblaze, mips, parisc, score, and sparc) use functionally identical copies of various GCC library routine files, which came up as we were submitting the RISC-V port (which also uses some of these). This patch adds a new copy of these library routine files, which are functionally identical to the various other copies. These are availiable via Kconfig as CONFIG_GENERIC_$ROUTINE, which currently isn't used anywhere. Reviewed-by: Geert Uytterhoeven Signed-off-by: Palmer Dabbelt --- include/lib/libgcc.h | 43 +++++++++++++++++++++++++++++++ lib/Kconfig | 18 +++++++++++++ lib/Makefile | 8 ++++++ lib/ashldi3.c | 44 ++++++++++++++++++++++++++++++++ lib/ashrdi3.c | 46 +++++++++++++++++++++++++++++++++ lib/cmpdi2.c | 42 ++++++++++++++++++++++++++++++ lib/lshrdi3.c | 45 ++++++++++++++++++++++++++++++++ lib/muldi3.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/ucmpdi2.c | 35 +++++++++++++++++++++++++ 9 files changed, 353 insertions(+) create mode 100644 include/lib/libgcc.h create mode 100644 lib/ashldi3.c create mode 100644 lib/ashrdi3.c create mode 100644 lib/cmpdi2.c create mode 100644 lib/lshrdi3.c create mode 100644 lib/muldi3.c create mode 100644 lib/ucmpdi2.c (limited to 'include') diff --git a/include/lib/libgcc.h b/include/lib/libgcc.h new file mode 100644 index 000000000000..32e1e0f4b2d0 --- /dev/null +++ b/include/lib/libgcc.h @@ -0,0 +1,43 @@ +/* + * include/lib/libgcc.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc. + */ + +#ifndef __LIB_LIBGCC_H +#define __LIB_LIBGCC_H + +#include + +typedef int word_type __attribute__ ((mode (__word__))); + +#ifdef __BIG_ENDIAN +struct DWstruct { + int high, low; +}; +#elif defined(__LITTLE_ENDIAN) +struct DWstruct { + int low, high; +}; +#else +#error I feel sick. +#endif + +typedef union { + struct DWstruct s; + long long ll; +} DWunion; + +#endif /* __ASM_LIBGCC_H */ diff --git a/lib/Kconfig b/lib/Kconfig index b1445b22a6de..a2b6745324ab 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -587,3 +587,21 @@ config STRING_SELFTEST bool "Test string functions" endmenu + +config GENERIC_ASHLDI3 + bool + +config GENERIC_ASHRDI3 + bool + +config GENERIC_LSHRDI3 + bool + +config GENERIC_MULDI3 + bool + +config GENERIC_CMPDI2 + bool + +config GENERIC_UCMPDI2 + bool diff --git a/lib/Makefile b/lib/Makefile index dafa79613fb4..5ad444501f2d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -247,3 +247,11 @@ UBSAN_SANITIZE_ubsan.o := n obj-$(CONFIG_SBITMAP) += sbitmap.o obj-$(CONFIG_PARMAN) += parman.o + +# GCC library routines +obj-$(CONFIG_GENERIC_ASHLDI3) += ashldi3.o +obj-$(CONFIG_GENERIC_ASHRDI3) += ashrdi3.o +obj-$(CONFIG_GENERIC_LSHRDI3) += lshrdi3.o +obj-$(CONFIG_GENERIC_MULDI3) += muldi3.o +obj-$(CONFIG_GENERIC_CMPDI2) += cmpdi2.o +obj-$(CONFIG_GENERIC_UCMPDI2) += ucmpdi2.o diff --git a/lib/ashldi3.c b/lib/ashldi3.c new file mode 100644 index 000000000000..1b6087db95a5 --- /dev/null +++ b/lib/ashldi3.c @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc. + */ + +#include + +#include + +long long notrace __ashldi3(long long u, word_type b) +{ + DWunion uu, w; + word_type bm; + + if (b == 0) + return u; + + uu.ll = u; + bm = 32 - b; + + if (bm <= 0) { + w.s.low = 0; + w.s.high = (unsigned int) uu.s.low << -bm; + } else { + const unsigned int carries = (unsigned int) uu.s.low >> bm; + + w.s.low = (unsigned int) uu.s.low << b; + w.s.high = ((unsigned int) uu.s.high << b) | carries; + } + + return w.ll; +} +EXPORT_SYMBOL(__ashldi3); diff --git a/lib/ashrdi3.c b/lib/ashrdi3.c new file mode 100644 index 000000000000..2e67c97ac65a --- /dev/null +++ b/lib/ashrdi3.c @@ -0,0 +1,46 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc. + */ + +#include + +#include + +long long notrace __ashrdi3(long long u, word_type b) +{ + DWunion uu, w; + word_type bm; + + if (b == 0) + return u; + + uu.ll = u; + bm = 32 - b; + + if (bm <= 0) { + /* w.s.high = 1..1 or 0..0 */ + w.s.high = + uu.s.high >> 31; + w.s.low = uu.s.high >> -bm; + } else { + const unsigned int carries = (unsigned int) uu.s.high << bm; + + w.s.high = uu.s.high >> b; + w.s.low = ((unsigned int) uu.s.low >> b) | carries; + } + + return w.ll; +} +EXPORT_SYMBOL(__ashrdi3); diff --git a/lib/cmpdi2.c b/lib/cmpdi2.c new file mode 100644 index 000000000000..6d7ebf6c2b86 --- /dev/null +++ b/lib/cmpdi2.c @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc. + */ + +#include + +#include + +word_type notrace __cmpdi2(long long a, long long b) +{ + const DWunion au = { + .ll = a + }; + const DWunion bu = { + .ll = b + }; + + if (au.s.high < bu.s.high) + return 0; + else if (au.s.high > bu.s.high) + return 2; + + if ((unsigned int) au.s.low < (unsigned int) bu.s.low) + return 0; + else if ((unsigned int) au.s.low > (unsigned int) bu.s.low) + return 2; + + return 1; +} +EXPORT_SYMBOL(__cmpdi2); diff --git a/lib/lshrdi3.c b/lib/lshrdi3.c new file mode 100644 index 000000000000..8e845f4bb65f --- /dev/null +++ b/lib/lshrdi3.c @@ -0,0 +1,45 @@ +/* + * lib/lshrdi3.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc. + */ + +#include +#include + +long long notrace __lshrdi3(long long u, word_type b) +{ + DWunion uu, w; + word_type bm; + + if (b == 0) + return u; + + uu.ll = u; + bm = 32 - b; + + if (bm <= 0) { + w.s.high = 0; + w.s.low = (unsigned int) uu.s.high >> -bm; + } else { + const unsigned int carries = (unsigned int) uu.s.high << bm; + + w.s.high = (unsigned int) uu.s.high >> b; + w.s.low = ((unsigned int) uu.s.low >> b) | carries; + } + + return w.ll; +} +EXPORT_SYMBOL(__lshrdi3); diff --git a/lib/muldi3.c b/lib/muldi3.c new file mode 100644 index 000000000000..88938543e10a --- /dev/null +++ b/lib/muldi3.c @@ -0,0 +1,72 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc. + */ + +#include +#include + +#define W_TYPE_SIZE 32 + +#define __ll_B ((unsigned long) 1 << (W_TYPE_SIZE / 2)) +#define __ll_lowpart(t) ((unsigned long) (t) & (__ll_B - 1)) +#define __ll_highpart(t) ((unsigned long) (t) >> (W_TYPE_SIZE / 2)) + +/* If we still don't have umul_ppmm, define it using plain C. */ +#if !defined(umul_ppmm) +#define umul_ppmm(w1, w0, u, v) \ + do { \ + unsigned long __x0, __x1, __x2, __x3; \ + unsigned short __ul, __vl, __uh, __vh; \ + \ + __ul = __ll_lowpart(u); \ + __uh = __ll_highpart(u); \ + __vl = __ll_lowpart(v); \ + __vh = __ll_highpart(v); \ + \ + __x0 = (unsigned long) __ul * __vl; \ + __x1 = (unsigned long) __ul * __vh; \ + __x2 = (unsigned long) __uh * __vl; \ + __x3 = (unsigned long) __uh * __vh; \ + \ + __x1 += __ll_highpart(__x0); /* this can't give carry */\ + __x1 += __x2; /* but this indeed can */ \ + if (__x1 < __x2) /* did we get it? */ \ + __x3 += __ll_B; /* yes, add it in the proper pos */ \ + \ + (w1) = __x3 + __ll_highpart(__x1); \ + (w0) = __ll_lowpart(__x1) * __ll_B + __ll_lowpart(__x0);\ + } while (0) +#endif + +#if !defined(__umulsidi3) +#define __umulsidi3(u, v) ({ \ + DWunion __w; \ + umul_ppmm(__w.s.high, __w.s.low, u, v); \ + __w.ll; \ + }) +#endif + +long long notrace __muldi3(long long u, long long v) +{ + const DWunion uu = {.ll = u}; + const DWunion vv = {.ll = v}; + DWunion w = {.ll = __umulsidi3(uu.s.low, vv.s.low)}; + + w.s.high += ((unsigned long) uu.s.low * (unsigned long) vv.s.high + + (unsigned long) uu.s.high * (unsigned long) vv.s.low); + + return w.ll; +} +EXPORT_SYMBOL(__muldi3); diff --git a/lib/ucmpdi2.c b/lib/ucmpdi2.c new file mode 100644 index 000000000000..49a53505c8e3 --- /dev/null +++ b/lib/ucmpdi2.c @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see the file COPYING, or write + * to the Free Software Foundation, Inc. + */ + +#include +#include + +word_type __ucmpdi2(unsigned long long a, unsigned long long b) +{ + const DWunion au = {.ll = a}; + const DWunion bu = {.ll = b}; + + if ((unsigned int) au.s.high < (unsigned int) bu.s.high) + return 0; + else if ((unsigned int) au.s.high > (unsigned int) bu.s.high) + return 2; + if ((unsigned int) au.s.low < (unsigned int) bu.s.low) + return 0; + else if ((unsigned int) au.s.low > (unsigned int) bu.s.low) + return 2; + return 1; +} +EXPORT_SYMBOL(__ucmpdi2); -- cgit v1.2.3-59-g8ed1b From 943170998b200190f99d3fe7e771437e2c51f319 Mon Sep 17 00:00:00 2001 From: Petar Penkov Date: Fri, 22 Sep 2017 13:49:14 -0700 Subject: tun: enable NAPI for TUN/TAP driver Changes TUN driver to use napi_gro_receive() upon receiving packets rather than netif_rx_ni(). Adds flag IFF_NAPI that enables these changes and operation is not affected if the flag is disabled. SKBs are constructed upon packet arrival and are queued to be processed later. The new path was evaluated with a benchmark with the following setup: Open two tap devices and a receiver thread that reads in a loop for each device. Start one sender thread and pin all threads to different CPUs. Send 1M minimum UDP packets to each device and measure sending time for each of the sending methods: napi_gro_receive(): 4.90s netif_rx_ni(): 4.90s netif_receive_skb(): 7.20s Signed-off-by: Petar Penkov Cc: Eric Dumazet Cc: Mahesh Bandewar Cc: Willem de Bruijn Cc: davem@davemloft.net Cc: ppenkov@stanford.edu Acked-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/tun.c | 133 +++++++++++++++++++++++++++++++++++++++----- include/uapi/linux/if_tun.h | 1 + 2 files changed, 119 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3c9985f29950..f16407242b18 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -121,7 +121,7 @@ do { \ #define TUN_VNET_BE 0x40000000 #define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \ - IFF_MULTI_QUEUE) + IFF_MULTI_QUEUE | IFF_NAPI) #define GOODCOPY_LEN 128 #define FLT_EXACT_COUNT 8 @@ -172,6 +172,7 @@ struct tun_file { u16 queue_index; unsigned int ifindex; }; + struct napi_struct napi; struct list_head next; struct tun_struct *detached; struct skb_array tx_array; @@ -229,6 +230,68 @@ struct tun_struct { struct bpf_prog __rcu *xdp_prog; }; +static int tun_napi_receive(struct napi_struct *napi, int budget) +{ + struct tun_file *tfile = container_of(napi, struct tun_file, napi); + struct sk_buff_head *queue = &tfile->sk.sk_write_queue; + struct sk_buff_head process_queue; + struct sk_buff *skb; + int received = 0; + + __skb_queue_head_init(&process_queue); + + spin_lock(&queue->lock); + skb_queue_splice_tail_init(queue, &process_queue); + spin_unlock(&queue->lock); + + while (received < budget && (skb = __skb_dequeue(&process_queue))) { + napi_gro_receive(napi, skb); + ++received; + } + + if (!skb_queue_empty(&process_queue)) { + spin_lock(&queue->lock); + skb_queue_splice(&process_queue, queue); + spin_unlock(&queue->lock); + } + + return received; +} + +static int tun_napi_poll(struct napi_struct *napi, int budget) +{ + unsigned int received; + + received = tun_napi_receive(napi, budget); + + if (received < budget) + napi_complete_done(napi, received); + + return received; +} + +static void tun_napi_init(struct tun_struct *tun, struct tun_file *tfile, + bool napi_en) +{ + if (napi_en) { + netif_napi_add(tun->dev, &tfile->napi, tun_napi_poll, + NAPI_POLL_WEIGHT); + napi_enable(&tfile->napi); + } +} + +static void tun_napi_disable(struct tun_struct *tun, struct tun_file *tfile) +{ + if (tun->flags & IFF_NAPI) + napi_disable(&tfile->napi); +} + +static void tun_napi_del(struct tun_struct *tun, struct tun_file *tfile) +{ + if (tun->flags & IFF_NAPI) + netif_napi_del(&tfile->napi); +} + #ifdef CONFIG_TUN_VNET_CROSS_LE static inline bool tun_legacy_is_little_endian(struct tun_struct *tun) { @@ -541,6 +604,11 @@ static void __tun_detach(struct tun_file *tfile, bool clean) tun = rtnl_dereference(tfile->tun); + if (tun && clean) { + tun_napi_disable(tun, tfile); + tun_napi_del(tun, tfile); + } + if (tun && !tfile->detached) { u16 index = tfile->queue_index; BUG_ON(index >= tun->numqueues); @@ -598,6 +666,7 @@ static void tun_detach_all(struct net_device *dev) for (i = 0; i < n; i++) { tfile = rtnl_dereference(tun->tfiles[i]); BUG_ON(!tfile); + tun_napi_disable(tun, tfile); tfile->socket.sk->sk_shutdown = RCV_SHUTDOWN; tfile->socket.sk->sk_data_ready(tfile->socket.sk); RCU_INIT_POINTER(tfile->tun, NULL); @@ -613,6 +682,7 @@ static void tun_detach_all(struct net_device *dev) synchronize_net(); for (i = 0; i < n; i++) { tfile = rtnl_dereference(tun->tfiles[i]); + tun_napi_del(tun, tfile); /* Drop read queue */ tun_queue_purge(tfile); sock_put(&tfile->sk); @@ -631,7 +701,8 @@ static void tun_detach_all(struct net_device *dev) module_put(THIS_MODULE); } -static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filter) +static int tun_attach(struct tun_struct *tun, struct file *file, + bool skip_filter, bool napi) { struct tun_file *tfile = file->private_data; struct net_device *dev = tun->dev; @@ -677,10 +748,12 @@ static int tun_attach(struct tun_struct *tun, struct file *file, bool skip_filte rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile); tun->numqueues++; - if (tfile->detached) + if (tfile->detached) { tun_enable_queue(tfile); - else + } else { sock_hold(&tfile->sk); + tun_napi_init(tun, tfile, napi); + } tun_set_real_num_queues(tun); @@ -956,13 +1029,28 @@ static void tun_poll_controller(struct net_device *dev) * Tun only receives frames when: * 1) the char device endpoint gets data from user space * 2) the tun socket gets a sendmsg call from user space - * Since both of those are synchronous operations, we are guaranteed - * never to have pending data when we poll for it - * so there is nothing to do here but return. + * If NAPI is not enabled, since both of those are synchronous + * operations, we are guaranteed never to have pending data when we poll + * for it so there is nothing to do here but return. * We need this though so netpoll recognizes us as an interface that * supports polling, which enables bridge devices in virt setups to * still use netconsole + * If NAPI is enabled, however, we need to schedule polling for all + * queues. */ + struct tun_struct *tun = netdev_priv(dev); + + if (tun->flags & IFF_NAPI) { + struct tun_file *tfile; + int i; + + rcu_read_lock(); + for (i = 0; i < tun->numqueues; i++) { + tfile = rcu_dereference(tun->tfiles[i]); + napi_schedule(&tfile->napi); + } + rcu_read_unlock(); + } return; } #endif @@ -1549,11 +1637,25 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, } rxhash = __skb_get_hash_symmetric(skb); -#ifndef CONFIG_4KSTACKS - tun_rx_batched(tun, tfile, skb, more); -#else - netif_rx_ni(skb); -#endif + + if (tun->flags & IFF_NAPI) { + struct sk_buff_head *queue = &tfile->sk.sk_write_queue; + int queue_len; + + spin_lock_bh(&queue->lock); + __skb_queue_tail(queue, skb); + queue_len = skb_queue_len(queue); + spin_unlock(&queue->lock); + + if (!more || queue_len > NAPI_POLL_WEIGHT) + napi_schedule(&tfile->napi); + + local_bh_enable(); + } else if (!IS_ENABLED(CONFIG_4KSTACKS)) { + tun_rx_batched(tun, tfile, skb, more); + } else { + netif_rx_ni(skb); + } stats = get_cpu_ptr(tun->pcpu_stats); u64_stats_update_begin(&stats->syncp); @@ -1980,7 +2082,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (err < 0) return err; - err = tun_attach(tun, file, ifr->ifr_flags & IFF_NOFILTER); + err = tun_attach(tun, file, ifr->ifr_flags & IFF_NOFILTER, + ifr->ifr_flags & IFF_NAPI); if (err < 0) return err; @@ -2066,7 +2169,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) NETIF_F_HW_VLAN_STAG_TX); INIT_LIST_HEAD(&tun->disabled); - err = tun_attach(tun, file, false); + err = tun_attach(tun, file, false, ifr->ifr_flags & IFF_NAPI); if (err < 0) goto err_free_flow; @@ -2216,7 +2319,7 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) ret = security_tun_dev_attach_queue(tun->security); if (ret < 0) goto unlock; - ret = tun_attach(tun, file, false); + ret = tun_attach(tun, file, false, tun->flags & IFF_NAPI); } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { tun = rtnl_dereference(tfile->tun); if (!tun || !(tun->flags & IFF_MULTI_QUEUE) || tfile->detached) diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h index 3cb5e1d85ddd..30b6184884eb 100644 --- a/include/uapi/linux/if_tun.h +++ b/include/uapi/linux/if_tun.h @@ -60,6 +60,7 @@ /* TUNSETIFF ifr flags */ #define IFF_TUN 0x0001 #define IFF_TAP 0x0002 +#define IFF_NAPI 0x0010 #define IFF_NO_PI 0x1000 /* This flag has no real effect */ #define IFF_ONE_QUEUE 0x2000 -- cgit v1.2.3-59-g8ed1b From 90e33d45940793def6f773b2d528e9f3c84ffdc7 Mon Sep 17 00:00:00 2001 From: Petar Penkov Date: Fri, 22 Sep 2017 13:49:15 -0700 Subject: tun: enable napi_gro_frags() for TUN/TAP driver Add a TUN/TAP receive mode that exercises the napi_gro_frags() interface. This mode is available only in TAP mode, as the interface expects packets with Ethernet headers. Furthermore, packets follow the layout of the iovec_iter that was received. The first iovec is the linear data, and every one after the first is a fragment. If there are more fragments than the max number, drop the packet. Additionally, invoke eth_get_headlen() to exercise flow dissector code and to verify that the header resides in the linear data. The napi_gro_frags() mode requires setting the IFF_NAPI_FRAGS option. This is imposed because this mode is intended for testing via tools like syzkaller and packetdrill, and the increased flexibility it provides can introduce security vulnerabilities. This flag is accepted only if the device is in TAP mode and has the IFF_NAPI flag set as well. This is done because both of these are explicit requirements for correct operation in this mode. Signed-off-by: Petar Penkov Cc: Eric Dumazet Cc: Mahesh Bandewar Cc: Willem de Bruijn Cc: davem@davemloft.net Cc: ppenkov@stanford.edu Acked-by: Mahesh Bandewar Signed-off-by: David S. Miller --- drivers/net/tun.c | 134 ++++++++++++++++++++++++++++++++++++++++++-- include/uapi/linux/if_tun.h | 1 + 2 files changed, 129 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index f16407242b18..9880b3bc8fa5 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -75,6 +75,7 @@ #include #include #include +#include #include @@ -121,7 +122,8 @@ do { \ #define TUN_VNET_BE 0x40000000 #define TUN_FEATURES (IFF_NO_PI | IFF_ONE_QUEUE | IFF_VNET_HDR | \ - IFF_MULTI_QUEUE | IFF_NAPI) + IFF_MULTI_QUEUE | IFF_NAPI | IFF_NAPI_FRAGS) + #define GOODCOPY_LEN 128 #define FLT_EXACT_COUNT 8 @@ -173,6 +175,7 @@ struct tun_file { unsigned int ifindex; }; struct napi_struct napi; + struct mutex napi_mutex; /* Protects access to the above napi */ struct list_head next; struct tun_struct *detached; struct skb_array tx_array; @@ -277,6 +280,7 @@ static void tun_napi_init(struct tun_struct *tun, struct tun_file *tfile, netif_napi_add(tun->dev, &tfile->napi, tun_napi_poll, NAPI_POLL_WEIGHT); napi_enable(&tfile->napi); + mutex_init(&tfile->napi_mutex); } } @@ -292,6 +296,11 @@ static void tun_napi_del(struct tun_struct *tun, struct tun_file *tfile) netif_napi_del(&tfile->napi); } +static bool tun_napi_frags_enabled(const struct tun_struct *tun) +{ + return READ_ONCE(tun->flags) & IFF_NAPI_FRAGS; +} + #ifdef CONFIG_TUN_VNET_CROSS_LE static inline bool tun_legacy_is_little_endian(struct tun_struct *tun) { @@ -1036,7 +1045,8 @@ static void tun_poll_controller(struct net_device *dev) * supports polling, which enables bridge devices in virt setups to * still use netconsole * If NAPI is enabled, however, we need to schedule polling for all - * queues. + * queues unless we are using napi_gro_frags(), which we call in + * process context and not in NAPI context. */ struct tun_struct *tun = netdev_priv(dev); @@ -1044,6 +1054,9 @@ static void tun_poll_controller(struct net_device *dev) struct tun_file *tfile; int i; + if (tun_napi_frags_enabled(tun)) + return; + rcu_read_lock(); for (i = 0; i < tun->numqueues; i++) { tfile = rcu_dereference(tun->tfiles[i]); @@ -1266,6 +1279,64 @@ static unsigned int tun_chr_poll(struct file *file, poll_table *wait) return mask; } +static struct sk_buff *tun_napi_alloc_frags(struct tun_file *tfile, + size_t len, + const struct iov_iter *it) +{ + struct sk_buff *skb; + size_t linear; + int err; + int i; + + if (it->nr_segs > MAX_SKB_FRAGS + 1) + return ERR_PTR(-ENOMEM); + + local_bh_disable(); + skb = napi_get_frags(&tfile->napi); + local_bh_enable(); + if (!skb) + return ERR_PTR(-ENOMEM); + + linear = iov_iter_single_seg_count(it); + err = __skb_grow(skb, linear); + if (err) + goto free; + + skb->len = len; + skb->data_len = len - linear; + skb->truesize += skb->data_len; + + for (i = 1; i < it->nr_segs; i++) { + size_t fragsz = it->iov[i].iov_len; + unsigned long offset; + struct page *page; + void *data; + + if (fragsz == 0 || fragsz > PAGE_SIZE) { + err = -EINVAL; + goto free; + } + + local_bh_disable(); + data = napi_alloc_frag(fragsz); + local_bh_enable(); + if (!data) { + err = -ENOMEM; + goto free; + } + + page = virt_to_head_page(data); + offset = data - page_address(page); + skb_fill_page_desc(skb, i - 1, page, offset, fragsz); + } + + return skb; +free: + /* frees skb and all frags allocated with napi_alloc_frag() */ + napi_free_frags(&tfile->napi); + return ERR_PTR(err); +} + /* prepad is the amount to reserve at front. len is length after that. * linear is a hint as to how much to copy (usually headers). */ static struct sk_buff *tun_alloc_skb(struct tun_file *tfile, @@ -1478,6 +1549,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, int err; u32 rxhash; int skb_xdp = 1; + bool frags = tun_napi_frags_enabled(tun); if (!(tun->dev->flags & IFF_UP)) return -EIO; @@ -1535,7 +1607,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, zerocopy = true; } - if (tun_can_build_skb(tun, tfile, len, noblock, zerocopy)) { + if (!frags && tun_can_build_skb(tun, tfile, len, noblock, zerocopy)) { /* For the packet that is not easy to be processed * (e.g gso or jumbo packet), we will do it at after * skb was created with generic XDP routine. @@ -1556,10 +1628,24 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, linear = tun16_to_cpu(tun, gso.hdr_len); } - skb = tun_alloc_skb(tfile, align, copylen, linear, noblock); + if (frags) { + mutex_lock(&tfile->napi_mutex); + skb = tun_napi_alloc_frags(tfile, copylen, from); + /* tun_napi_alloc_frags() enforces a layout for the skb. + * If zerocopy is enabled, then this layout will be + * overwritten by zerocopy_sg_from_iter(). + */ + zerocopy = false; + } else { + skb = tun_alloc_skb(tfile, align, copylen, linear, + noblock); + } + if (IS_ERR(skb)) { if (PTR_ERR(skb) != -EAGAIN) this_cpu_inc(tun->pcpu_stats->rx_dropped); + if (frags) + mutex_unlock(&tfile->napi_mutex); return PTR_ERR(skb); } @@ -1571,6 +1657,11 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (err) { this_cpu_inc(tun->pcpu_stats->rx_dropped); kfree_skb(skb); + if (frags) { + tfile->napi.skb = NULL; + mutex_unlock(&tfile->napi_mutex); + } + return -EFAULT; } } @@ -1578,6 +1669,11 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, if (virtio_net_hdr_to_skb(skb, &gso, tun_is_little_endian(tun))) { this_cpu_inc(tun->pcpu_stats->rx_frame_errors); kfree_skb(skb); + if (frags) { + tfile->napi.skb = NULL; + mutex_unlock(&tfile->napi_mutex); + } + return -EINVAL; } @@ -1603,7 +1699,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, skb->dev = tun->dev; break; case IFF_TAP: - skb->protocol = eth_type_trans(skb, tun->dev); + if (!frags) + skb->protocol = eth_type_trans(skb, tun->dev); break; } @@ -1638,7 +1735,23 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, rxhash = __skb_get_hash_symmetric(skb); - if (tun->flags & IFF_NAPI) { + if (frags) { + /* Exercise flow dissector code path. */ + u32 headlen = eth_get_headlen(skb->data, skb_headlen(skb)); + + if (headlen > skb_headlen(skb) || headlen < ETH_HLEN) { + this_cpu_inc(tun->pcpu_stats->rx_dropped); + napi_free_frags(&tfile->napi); + mutex_unlock(&tfile->napi_mutex); + WARN_ON(1); + return -ENOMEM; + } + + local_bh_disable(); + napi_gro_frags(&tfile->napi); + local_bh_enable(); + mutex_unlock(&tfile->napi_mutex); + } else if (tun->flags & IFF_NAPI) { struct sk_buff_head *queue = &tfile->sk.sk_write_queue; int queue_len; @@ -2061,6 +2174,15 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (tfile->detached) return -EINVAL; + if ((ifr->ifr_flags & IFF_NAPI_FRAGS)) { + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (!(ifr->ifr_flags & IFF_NAPI) || + (ifr->ifr_flags & TUN_TYPE_MASK) != IFF_TAP) + return -EINVAL; + } + dev = __dev_get_by_name(net, ifr->ifr_name); if (dev) { if (ifr->ifr_flags & IFF_TUN_EXCL) diff --git a/include/uapi/linux/if_tun.h b/include/uapi/linux/if_tun.h index 30b6184884eb..365ade5685c9 100644 --- a/include/uapi/linux/if_tun.h +++ b/include/uapi/linux/if_tun.h @@ -61,6 +61,7 @@ #define IFF_TUN 0x0001 #define IFF_TAP 0x0002 #define IFF_NAPI 0x0010 +#define IFF_NAPI_FRAGS 0x0020 #define IFF_NO_PI 0x1000 /* This flag has no real effect */ #define IFF_ONE_QUEUE 0x2000 -- cgit v1.2.3-59-g8ed1b From e451ae8e4f6b3f6bd3b83a5595657b5421b3bf69 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 23 Sep 2017 23:01:06 +0300 Subject: neigh: make struct neigh_table::entry_size unsigned int Neigh entry size can't be negative. Space savings: add/remove: 0/0 grow/shrink: 0/5 up/down: 0/-7 (-7) function old new delta lowpan_neigh_construct 25 24 -1 clip_seq_sub_iter 152 151 -1 clip_ioctl 1475 1474 -1 clip_constructor 93 92 -1 __neigh_create 2455 2452 -3 Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- include/net/neighbour.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 9816df225af3..9a25512e0a6e 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -190,7 +190,7 @@ struct neigh_hash_table { struct neigh_table { int family; - int entry_size; + unsigned int entry_size; int key_len; __be16 protocol; __u32 (*hash)(const void *pkey, -- cgit v1.2.3-59-g8ed1b From 01ccdf126ca5f9d4fe0889f65ee67afac910f19c Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sat, 23 Sep 2017 23:03:04 +0300 Subject: neigh: make strucrt neigh_table::entry_size unsigned int Key length can't be negative. Leave comparisons against nla_len() signed just in case truncated attribute can sneak in there. Space savings: add/remove: 0/0 grow/shrink: 0/7 up/down: 0/-7 (-7) function old new delta pneigh_delete 273 272 -1 mlx5e_rep_netevent_event 1415 1414 -1 mlx5e_create_encap_header_ipv6 1194 1193 -1 mlx5e_create_encap_header_ipv4 1071 1070 -1 cxgb4_l2t_get 1104 1103 -1 __pneigh_lookup 69 68 -1 __neigh_create 2452 2451 -1 Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/l2t.c | 4 ++-- include/net/neighbour.h | 2 +- net/core/neighbour.c | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index f7ef8871dd0b..1817a0307d26 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -422,7 +422,7 @@ struct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh, u8 lport; u16 vlan; struct l2t_entry *e; - int addr_len = neigh->tbl->key_len; + unsigned int addr_len = neigh->tbl->key_len; u32 *addr = (u32 *)neigh->primary_key; int ifidx = neigh->dev->ifindex; int hash = addr_hash(d, addr, addr_len, ifidx); @@ -536,7 +536,7 @@ void t4_l2t_update(struct adapter *adap, struct neighbour *neigh) struct l2t_entry *e; struct sk_buff_head *arpq = NULL; struct l2t_data *d = adap->l2t; - int addr_len = neigh->tbl->key_len; + unsigned int addr_len = neigh->tbl->key_len; u32 *addr = (u32 *) neigh->primary_key; int ifidx = neigh->dev->ifindex; int hash = addr_hash(d, addr, addr_len, ifidx); diff --git a/include/net/neighbour.h b/include/net/neighbour.h index 9a25512e0a6e..2492000e1035 100644 --- a/include/net/neighbour.h +++ b/include/net/neighbour.h @@ -191,7 +191,7 @@ struct neigh_hash_table { struct neigh_table { int family; unsigned int entry_size; - int key_len; + unsigned int key_len; __be16 protocol; __u32 (*hash)(const void *pkey, const struct net_device *dev, diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 16a1a4c4eb57..6ea3a1a7f36a 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -457,7 +457,7 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net, const void *pkey) { struct neighbour *n; - int key_len = tbl->key_len; + unsigned int key_len = tbl->key_len; u32 hash_val; struct neigh_hash_table *nht; @@ -488,7 +488,7 @@ struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey, struct net_device *dev, bool want_ref) { u32 hash_val; - int key_len = tbl->key_len; + unsigned int key_len = tbl->key_len; int error; struct neighbour *n1, *rc, *n = neigh_alloc(tbl, dev); struct neigh_hash_table *nht; @@ -572,7 +572,7 @@ out_neigh_release: } EXPORT_SYMBOL(__neigh_create); -static u32 pneigh_hash(const void *pkey, int key_len) +static u32 pneigh_hash(const void *pkey, unsigned int key_len) { u32 hash_val = *(u32 *)(pkey + key_len - 4); hash_val ^= (hash_val >> 16); @@ -585,7 +585,7 @@ static u32 pneigh_hash(const void *pkey, int key_len) static struct pneigh_entry *__pneigh_lookup_1(struct pneigh_entry *n, struct net *net, const void *pkey, - int key_len, + unsigned int key_len, struct net_device *dev) { while (n) { @@ -601,7 +601,7 @@ static struct pneigh_entry *__pneigh_lookup_1(struct pneigh_entry *n, struct pneigh_entry *__pneigh_lookup(struct neigh_table *tbl, struct net *net, const void *pkey, struct net_device *dev) { - int key_len = tbl->key_len; + unsigned int key_len = tbl->key_len; u32 hash_val = pneigh_hash(pkey, key_len); return __pneigh_lookup_1(tbl->phash_buckets[hash_val], @@ -614,7 +614,7 @@ struct pneigh_entry * pneigh_lookup(struct neigh_table *tbl, struct net_device *dev, int creat) { struct pneigh_entry *n; - int key_len = tbl->key_len; + unsigned int key_len = tbl->key_len; u32 hash_val = pneigh_hash(pkey, key_len); read_lock_bh(&tbl->lock); @@ -659,7 +659,7 @@ int pneigh_delete(struct neigh_table *tbl, struct net *net, const void *pkey, struct net_device *dev) { struct pneigh_entry *n, **np; - int key_len = tbl->key_len; + unsigned int key_len = tbl->key_len; u32 hash_val = pneigh_hash(pkey, key_len); write_lock_bh(&tbl->lock); @@ -1662,7 +1662,7 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh, if (tbl == NULL) return -EAFNOSUPPORT; - if (nla_len(dst_attr) < tbl->key_len) + if (nla_len(dst_attr) < (int)tbl->key_len) goto out; if (ndm->ndm_flags & NTF_PROXY) { @@ -1730,7 +1730,7 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh, if (tbl == NULL) return -EAFNOSUPPORT; - if (nla_len(tb[NDA_DST]) < tbl->key_len) + if (nla_len(tb[NDA_DST]) < (int)tbl->key_len) goto out; dst = nla_data(tb[NDA_DST]); lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL; -- cgit v1.2.3-59-g8ed1b From 9a96f55034e41b4e002b767e9218d55f03bdff7d Mon Sep 17 00:00:00 2001 From: Aishwarya Pant Date: Tue, 26 Sep 2017 13:58:49 +0530 Subject: drm: introduce drm_dev_{get/put} functions Reference counting functions in the kernel typically use get/put suffixes. For maintaining coding style consistency, introduce drm_dev_{get/put} functions. All callers of drm_dev_ref() API have been converted in this patch and hence it has been dropped while the drm_dev_unref() API with non-trivial number of users remains for compatibility. The semantic patch scripts/coccinelle/api/drm-get-put.cocci has been updated with the new helper for conversion of drm_dev_unref() to drm_dev_put() Signed-off-by: Aishwarya Pant Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/6babda56134035a98220d5d37a4fd4048df214ce.1506413698.git.aishpant@gmail.com --- drivers/gpu/drm/drm_drv.c | 51 ++++++++++++++++++++------------ drivers/gpu/drm/drm_prime.c | 2 +- include/drm/drm_drv.h | 5 ++-- scripts/coccinelle/api/drm-get-put.cocci | 5 ++++ 4 files changed, 41 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index be38ac7050d4..c0292e5d7281 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -286,13 +286,13 @@ struct drm_minor *drm_minor_acquire(unsigned int minor_id) spin_lock_irqsave(&drm_minor_lock, flags); minor = idr_find(&drm_minors_idr, minor_id); if (minor) - drm_dev_ref(minor->dev); + drm_dev_get(minor->dev); spin_unlock_irqrestore(&drm_minor_lock, flags); if (!minor) { return ERR_PTR(-ENODEV); } else if (drm_dev_is_unplugged(minor->dev)) { - drm_dev_unref(minor->dev); + drm_dev_put(minor->dev); return ERR_PTR(-ENODEV); } @@ -301,7 +301,7 @@ struct drm_minor *drm_minor_acquire(unsigned int minor_id) void drm_minor_release(struct drm_minor *minor) { - drm_dev_unref(minor->dev); + drm_dev_put(minor->dev); } /** @@ -326,11 +326,11 @@ void drm_minor_release(struct drm_minor *minor) * When cleaning up a device instance everything needs to be done in reverse: * First unpublish the device instance with drm_dev_unregister(). Then clean up * any other resources allocated at device initialization and drop the driver's - * reference to &drm_device using drm_dev_unref(). + * reference to &drm_device using drm_dev_put(). * * Note that the lifetime rules for &drm_device instance has still a lot of * historical baggage. Hence use the reference counting provided by - * drm_dev_ref() and drm_dev_unref() only carefully. + * drm_dev_get() and drm_dev_put() only carefully. * * It is recommended that drivers embed &struct drm_device into their own device * structure, which is supported through drm_dev_init(). @@ -345,7 +345,7 @@ void drm_minor_release(struct drm_minor *minor) * Cleans up all DRM device, calling drm_lastclose(). * * Note: Use of this function is deprecated. It will eventually go away - * completely. Please use drm_dev_unregister() and drm_dev_unref() explicitly + * completely. Please use drm_dev_unregister() and drm_dev_put() explicitly * instead to make sure that the device isn't userspace accessible any more * while teardown is in progress, ensuring that userspace can't access an * inconsistent state. @@ -360,7 +360,7 @@ void drm_put_dev(struct drm_device *dev) } drm_dev_unregister(dev); - drm_dev_unref(dev); + drm_dev_put(dev); } EXPORT_SYMBOL(drm_put_dev); @@ -386,7 +386,7 @@ void drm_dev_unplug(struct drm_device *dev) mutex_lock(&drm_global_mutex); drm_device_set_unplugged(dev); if (dev->open_count == 0) - drm_dev_unref(dev); + drm_dev_put(dev); mutex_unlock(&drm_global_mutex); } EXPORT_SYMBOL(drm_dev_unplug); @@ -475,8 +475,8 @@ static void drm_fs_inode_free(struct inode *inode) * initialization sequence to make sure userspace can't access an inconsistent * state. * - * The initial ref-count of the object is 1. Use drm_dev_ref() and - * drm_dev_unref() to take and drop further ref-counts. + * The initial ref-count of the object is 1. Use drm_dev_get() and + * drm_dev_put() to take and drop further ref-counts. * * Note that for purely virtual devices @parent can be NULL. * @@ -626,8 +626,8 @@ EXPORT_SYMBOL(drm_dev_fini); * initialization sequence to make sure userspace can't access an inconsistent * state. * - * The initial ref-count of the object is 1. Use drm_dev_ref() and - * drm_dev_unref() to take and drop further ref-counts. + * The initial ref-count of the object is 1. Use drm_dev_get() and + * drm_dev_put() to take and drop further ref-counts. * * Note that for purely virtual devices @parent can be NULL. * @@ -670,36 +670,49 @@ static void drm_dev_release(struct kref *ref) } /** - * drm_dev_ref - Take reference of a DRM device + * drm_dev_get - Take reference of a DRM device * @dev: device to take reference of or NULL * * This increases the ref-count of @dev by one. You *must* already own a - * reference when calling this. Use drm_dev_unref() to drop this reference + * reference when calling this. Use drm_dev_put() to drop this reference * again. * * This function never fails. However, this function does not provide *any* * guarantee whether the device is alive or running. It only provides a * reference to the object and the memory associated with it. */ -void drm_dev_ref(struct drm_device *dev) +void drm_dev_get(struct drm_device *dev) { if (dev) kref_get(&dev->ref); } -EXPORT_SYMBOL(drm_dev_ref); +EXPORT_SYMBOL(drm_dev_get); /** - * drm_dev_unref - Drop reference of a DRM device + * drm_dev_put - Drop reference of a DRM device * @dev: device to drop reference of or NULL * * This decreases the ref-count of @dev by one. The device is destroyed if the * ref-count drops to zero. */ -void drm_dev_unref(struct drm_device *dev) +void drm_dev_put(struct drm_device *dev) { if (dev) kref_put(&dev->ref, drm_dev_release); } +EXPORT_SYMBOL(drm_dev_put); + +/** + * drm_dev_unref - Drop reference of a DRM device + * @dev: device to drop reference of or NULL + * + * This is a compatibility alias for drm_dev_put() and should not be used by new + * code. + */ +void drm_dev_unref(struct drm_device *dev) +{ + drm_dev_put(dev); +} EXPORT_SYMBOL(drm_dev_unref); static int create_compat_control_link(struct drm_device *dev) @@ -839,7 +852,7 @@ EXPORT_SYMBOL(drm_dev_register); * * Unregister the DRM device from the system. This does the reverse of * drm_dev_register() but does not deallocate the device. The caller must call - * drm_dev_unref() to drop their final reference. + * drm_dev_put() to drop their final reference. * * A special form of unregistering for hotpluggable devices is drm_dev_unplug(), * which can be called while there are still open users of @dev. diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 22408badc617..782b95e72405 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -318,7 +318,7 @@ struct dma_buf *drm_gem_dmabuf_export(struct drm_device *dev, if (IS_ERR(dma_buf)) return dma_buf; - drm_dev_ref(dev); + drm_dev_get(dev); drm_gem_object_get(exp_info->priv); return dma_buf; diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 71bbaaec836d..ee06ecd6c01f 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -155,7 +155,7 @@ struct drm_driver { * reverse order of the initialization. Similarly to the load * hook, this handler is deprecated and its usage should be * dropped in favor of an open-coded teardown function at the - * driver layer. See drm_dev_unregister() and drm_dev_unref() + * driver layer. See drm_dev_unregister() and drm_dev_put() * for the proper way to remove a &struct drm_device. * * The unload() hook is called right after unregistering @@ -611,7 +611,8 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, int drm_dev_register(struct drm_device *dev, unsigned long flags); void drm_dev_unregister(struct drm_device *dev); -void drm_dev_ref(struct drm_device *dev); +void drm_dev_get(struct drm_device *dev); +void drm_dev_put(struct drm_device *dev); void drm_dev_unref(struct drm_device *dev); void drm_put_dev(struct drm_device *dev); void drm_dev_unplug(struct drm_device *dev); diff --git a/scripts/coccinelle/api/drm-get-put.cocci b/scripts/coccinelle/api/drm-get-put.cocci index 0c7a9265c07e..dc10dee356c2 100644 --- a/scripts/coccinelle/api/drm-get-put.cocci +++ b/scripts/coccinelle/api/drm-get-put.cocci @@ -50,6 +50,9 @@ expression object; | - drm_property_unreference_blob(object) + drm_property_blob_put(object) +| +- drm_dev_unref(object) ++ drm_dev_put(object) ) @r depends on report@ @@ -81,6 +84,8 @@ drm_gem_object_unreference_unlocked(object) drm_property_unreference_blob@p(object) | drm_property_reference_blob@p(object) +| +drm_dev_unref@p(object) ) @script:python depends on report@ -- cgit v1.2.3-59-g8ed1b From 003278e431bffa4070d18c821eff1d95867f24db Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Tue, 26 Sep 2017 09:14:07 +0200 Subject: nfs_common: convert int to bool Since __state_in_grace return only true/false, make it return bool instead of int. Same change for the two user of it, locks_in_grace/opens_in_grace Signed-off-by: Corentin Labbe Reviewed-by: Jeff Layton Signed-off-by: J. Bruce Fields --- fs/nfs_common/grace.c | 10 +++++----- include/linux/fs.h | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/fs/nfs_common/grace.c b/fs/nfs_common/grace.c index c030cd618b99..897b299db55e 100644 --- a/fs/nfs_common/grace.c +++ b/fs/nfs_common/grace.c @@ -55,7 +55,7 @@ locks_end_grace(struct lock_manager *lm) } EXPORT_SYMBOL_GPL(locks_end_grace); -static int +static bool __state_in_grace(struct net *net, bool open) { struct list_head *grace_list = net_generic(net, grace_net_id); @@ -78,15 +78,15 @@ __state_in_grace(struct net *net, bool open) * to answer ordinary lock requests, and when they should accept only * lock reclaims. */ -int locks_in_grace(struct net *net) +bool locks_in_grace(struct net *net) { - return __state_in_grace(net, 0); + return __state_in_grace(net, false); } EXPORT_SYMBOL_GPL(locks_in_grace); -int opens_in_grace(struct net *net) +bool opens_in_grace(struct net *net) { - return __state_in_grace(net, 1); + return __state_in_grace(net, true); } EXPORT_SYMBOL_GPL(opens_in_grace); diff --git a/include/linux/fs.h b/include/linux/fs.h index 339e73742e73..8cc0493c7c39 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -970,8 +970,8 @@ struct lock_manager { struct net; void locks_start_grace(struct net *, struct lock_manager *); void locks_end_grace(struct lock_manager *); -int locks_in_grace(struct net *); -int opens_in_grace(struct net *); +bool locks_in_grace(struct net *); +bool opens_in_grace(struct net *); /* that will die - we need it for nfs_lock_info */ #include -- cgit v1.2.3-59-g8ed1b From 05e3db95ebfc5c06a29a1d8c7a3e02f46f3a25a7 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 14 Sep 2017 14:02:04 -0700 Subject: kthread: add a mechanism to store cgroup info kthread usually runs jobs on behalf of other threads. The jobs should be charged to cgroup of original threads. But the jobs run in a kthread, where we lose the cgroup context of original threads. The patch adds a machanism to record cgroup info of original threads in kthread context. Later we can retrieve the cgroup info and attach the cgroup info to jobs. Since this mechanism is only required by kthread, we store the cgroup info in kthread data instead of generic task_struct. Acked-by: Tejun Heo Signed-off-by: Shaohua Li Signed-off-by: Jens Axboe --- include/linux/kthread.h | 11 +++++++++ kernel/kthread.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/kthread.h b/include/linux/kthread.h index 82e197eeac91..bd4369c83dfb 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -3,6 +3,7 @@ /* Simple interface for creating and stopping kernel threads without mess. */ #include #include +#include __printf(4, 5) struct task_struct *kthread_create_on_node(int (*threadfn)(void *data), @@ -198,4 +199,14 @@ bool kthread_cancel_delayed_work_sync(struct kthread_delayed_work *work); void kthread_destroy_worker(struct kthread_worker *worker); +#ifdef CONFIG_CGROUPS +void kthread_associate_blkcg(struct cgroup_subsys_state *css); +struct cgroup_subsys_state *kthread_blkcg(void); +#else +static inline void kthread_associate_blkcg(struct cgroup_subsys_state *css) { } +static inline struct cgroup_subsys_state *kthread_blkcg(void) +{ + return NULL; +} +#endif #endif /* _LINUX_KTHREAD_H */ diff --git a/kernel/kthread.c b/kernel/kthread.c index 1c19edf82427..b011ea08967f 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -20,7 +20,6 @@ #include #include #include -#include #include static DEFINE_SPINLOCK(kthread_create_lock); @@ -47,6 +46,9 @@ struct kthread { void *data; struct completion parked; struct completion exited; +#ifdef CONFIG_CGROUPS + struct cgroup_subsys_state *blkcg_css; +#endif }; enum KTHREAD_BITS { @@ -74,11 +76,17 @@ static inline struct kthread *to_kthread(struct task_struct *k) void free_kthread_struct(struct task_struct *k) { + struct kthread *kthread; + /* * Can be NULL if this kthread was created by kernel_thread() * or if kmalloc() in kthread() failed. */ - kfree(to_kthread(k)); + kthread = to_kthread(k); +#ifdef CONFIG_CGROUPS + WARN_ON_ONCE(kthread && kthread->blkcg_css); +#endif + kfree(kthread); } /** @@ -216,6 +224,9 @@ static int kthread(void *_create) self->data = data; init_completion(&self->exited); init_completion(&self->parked); +#ifdef CONFIG_CGROUPS + self->blkcg_css = NULL; +#endif current->vfork_done = &self->exited; /* OK, tell user we're spawned, wait for stop or wakeup */ @@ -1154,3 +1165,54 @@ void kthread_destroy_worker(struct kthread_worker *worker) kfree(worker); } EXPORT_SYMBOL(kthread_destroy_worker); + +#ifdef CONFIG_CGROUPS +/** + * kthread_associate_blkcg - associate blkcg to current kthread + * @css: the cgroup info + * + * Current thread must be a kthread. The thread is running jobs on behalf of + * other threads. In some cases, we expect the jobs attach cgroup info of + * original threads instead of that of current thread. This function stores + * original thread's cgroup info in current kthread context for later + * retrieval. + */ +void kthread_associate_blkcg(struct cgroup_subsys_state *css) +{ + struct kthread *kthread; + + if (!(current->flags & PF_KTHREAD)) + return; + kthread = to_kthread(current); + if (!kthread) + return; + + if (kthread->blkcg_css) { + css_put(kthread->blkcg_css); + kthread->blkcg_css = NULL; + } + if (css) { + css_get(css); + kthread->blkcg_css = css; + } +} +EXPORT_SYMBOL(kthread_associate_blkcg); + +/** + * kthread_blkcg - get associated blkcg css of current kthread + * + * Current thread must be a kthread. + */ +struct cgroup_subsys_state *kthread_blkcg(void) +{ + struct kthread *kthread; + + if (current->flags & PF_KTHREAD) { + kthread = to_kthread(current); + if (kthread) + return kthread->blkcg_css; + } + return NULL; +} +EXPORT_SYMBOL(kthread_blkcg); +#endif -- cgit v1.2.3-59-g8ed1b From af551fb3be26a22b7a6b345b3b7e7e6acfc41758 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 14 Sep 2017 14:02:05 -0700 Subject: blkcg: delete unused APIs Nobody uses the APIs right now. Acked-by: Tejun Heo Signed-off-by: Shaohua Li Signed-off-by: Jens Axboe --- block/bio.c | 31 ------------------------------- include/linux/bio.h | 2 -- include/linux/blk-cgroup.h | 12 ------------ 3 files changed, 45 deletions(-) (limited to 'include') diff --git a/block/bio.c b/block/bio.c index b38e962fa83e..8338304ea256 100644 --- a/block/bio.c +++ b/block/bio.c @@ -2032,37 +2032,6 @@ int bio_associate_blkcg(struct bio *bio, struct cgroup_subsys_state *blkcg_css) } EXPORT_SYMBOL_GPL(bio_associate_blkcg); -/** - * bio_associate_current - associate a bio with %current - * @bio: target bio - * - * Associate @bio with %current if it hasn't been associated yet. Block - * layer will treat @bio as if it were issued by %current no matter which - * task actually issues it. - * - * This function takes an extra reference of @task's io_context and blkcg - * which will be put when @bio is released. The caller must own @bio, - * ensure %current->io_context exists, and is responsible for synchronizing - * calls to this function. - */ -int bio_associate_current(struct bio *bio) -{ - struct io_context *ioc; - - if (bio->bi_css) - return -EBUSY; - - ioc = current->io_context; - if (!ioc) - return -ENOENT; - - get_io_context_active(ioc); - bio->bi_ioc = ioc; - bio->bi_css = task_get_css(current, io_cgrp_id); - return 0; -} -EXPORT_SYMBOL_GPL(bio_associate_current); - /** * bio_disassociate_task - undo bio_associate_current() * @bio: target bio diff --git a/include/linux/bio.h b/include/linux/bio.h index 275c91c99516..9c75f58f6a50 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -522,13 +522,11 @@ do { \ #ifdef CONFIG_BLK_CGROUP int bio_associate_blkcg(struct bio *bio, struct cgroup_subsys_state *blkcg_css); -int bio_associate_current(struct bio *bio); void bio_disassociate_task(struct bio *bio); void bio_clone_blkcg_association(struct bio *dst, struct bio *src); #else /* CONFIG_BLK_CGROUP */ static inline int bio_associate_blkcg(struct bio *bio, struct cgroup_subsys_state *blkcg_css) { return 0; } -static inline int bio_associate_current(struct bio *bio) { return -ENOENT; } static inline void bio_disassociate_task(struct bio *bio) { } static inline void bio_clone_blkcg_association(struct bio *dst, struct bio *src) { } diff --git a/include/linux/blk-cgroup.h b/include/linux/blk-cgroup.h index 9d92153dd856..0cfa8d2ef4d6 100644 --- a/include/linux/blk-cgroup.h +++ b/include/linux/blk-cgroup.h @@ -235,12 +235,6 @@ static inline struct blkcg *bio_blkcg(struct bio *bio) return task_blkcg(current); } -static inline struct cgroup_subsys_state * -task_get_blkcg_css(struct task_struct *task) -{ - return task_get_css(task, io_cgrp_id); -} - /** * blkcg_parent - get the parent of a blkcg * @blkcg: blkcg of interest @@ -735,12 +729,6 @@ struct blkcg_policy { #define blkcg_root_css ((struct cgroup_subsys_state *)ERR_PTR(-EINVAL)) -static inline struct cgroup_subsys_state * -task_get_blkcg_css(struct task_struct *task) -{ - return NULL; -} - #ifdef CONFIG_BLOCK static inline struct blkcg_gq *blkg_lookup(struct blkcg *blkcg, void *key) { return NULL; } -- cgit v1.2.3-59-g8ed1b From 902ec5b6de0678ac2effba0faaafdd1c3e7fcf2e Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Thu, 14 Sep 2017 14:02:06 -0700 Subject: block: make blkcg aware of kthread stored original cgroup info bio_blkcg is the only API to get cgroup info for a bio right now. If bio_blkcg finds current task is a kthread and has original blkcg associated, it will use the css instead of associating the bio to current task. This makes it possible that kthread dispatches bios on behalf of other threads. Acked-by: Tejun Heo Signed-off-by: Shaohua Li Signed-off-by: Jens Axboe --- include/linux/blk-cgroup.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/blk-cgroup.h b/include/linux/blk-cgroup.h index 0cfa8d2ef4d6..f57e54d64529 100644 --- a/include/linux/blk-cgroup.h +++ b/include/linux/blk-cgroup.h @@ -19,6 +19,7 @@ #include #include #include +#include /* percpu_counter batch for blkg_[rw]stats, per-cpu drift doesn't matter */ #define BLKG_STAT_CPU_BATCH (INT_MAX / 2) @@ -223,16 +224,16 @@ static inline struct blkcg *css_to_blkcg(struct cgroup_subsys_state *css) return css ? container_of(css, struct blkcg, css) : NULL; } -static inline struct blkcg *task_blkcg(struct task_struct *tsk) -{ - return css_to_blkcg(task_css(tsk, io_cgrp_id)); -} - static inline struct blkcg *bio_blkcg(struct bio *bio) { + struct cgroup_subsys_state *css; + if (bio && bio->bi_css) return css_to_blkcg(bio->bi_css); - return task_blkcg(current); + css = kthread_blkcg(); + if (css) + return css_to_blkcg(css); + return css_to_blkcg(task_css(current, io_cgrp_id)); } /** -- cgit v1.2.3-59-g8ed1b From 0b508bc926bdced678febee2a2b8cdba0a19e481 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Tue, 26 Sep 2017 11:02:12 -0700 Subject: block: fix a build error The code is only for blkcg not for all cgroups Fixes: d4478e92d618 ("block/loop: make loop cgroup aware") Reported-by: kbuild test robot Signed-off-by: Shaohua Li Signed-off-by: Jens Axboe --- drivers/block/loop.c | 2 +- include/linux/kthread.h | 2 +- kernel/kthread.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/block/loop.c b/drivers/block/loop.c index fd4eff5f5b76..bc8e61506968 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -1692,7 +1692,7 @@ static blk_status_t loop_queue_rq(struct blk_mq_hw_ctx *hctx, } /* always use the first bio's css */ -#ifdef CONFIG_CGROUPS +#ifdef CONFIG_BLK_CGROUP if (cmd->use_aio && cmd->rq->bio && cmd->rq->bio->bi_css) { cmd->css = cmd->rq->bio->bi_css; css_get(cmd->css); diff --git a/include/linux/kthread.h b/include/linux/kthread.h index bd4369c83dfb..fb201842c635 100644 --- a/include/linux/kthread.h +++ b/include/linux/kthread.h @@ -199,7 +199,7 @@ bool kthread_cancel_delayed_work_sync(struct kthread_delayed_work *work); void kthread_destroy_worker(struct kthread_worker *worker); -#ifdef CONFIG_CGROUPS +#ifdef CONFIG_BLK_CGROUP void kthread_associate_blkcg(struct cgroup_subsys_state *css); struct cgroup_subsys_state *kthread_blkcg(void); #else diff --git a/kernel/kthread.c b/kernel/kthread.c index b011ea08967f..f87cd8b4eb2a 100644 --- a/kernel/kthread.c +++ b/kernel/kthread.c @@ -46,7 +46,7 @@ struct kthread { void *data; struct completion parked; struct completion exited; -#ifdef CONFIG_CGROUPS +#ifdef CONFIG_BLK_CGROUP struct cgroup_subsys_state *blkcg_css; #endif }; @@ -83,7 +83,7 @@ void free_kthread_struct(struct task_struct *k) * or if kmalloc() in kthread() failed. */ kthread = to_kthread(k); -#ifdef CONFIG_CGROUPS +#ifdef CONFIG_BLK_CGROUP WARN_ON_ONCE(kthread && kthread->blkcg_css); #endif kfree(kthread); @@ -224,7 +224,7 @@ static int kthread(void *_create) self->data = data; init_completion(&self->exited); init_completion(&self->parked); -#ifdef CONFIG_CGROUPS +#ifdef CONFIG_BLK_CGROUP self->blkcg_css = NULL; #endif current->vfork_done = &self->exited; @@ -1166,7 +1166,7 @@ void kthread_destroy_worker(struct kthread_worker *worker) } EXPORT_SYMBOL(kthread_destroy_worker); -#ifdef CONFIG_CGROUPS +#ifdef CONFIG_BLK_CGROUP /** * kthread_associate_blkcg - associate blkcg to current kthread * @css: the cgroup info -- cgit v1.2.3-59-g8ed1b From 1e99c497012cd8647972876f1bd18545bc907aea Mon Sep 17 00:00:00 2001 From: Michal Kalderon Date: Sun, 24 Sep 2017 12:09:45 +0300 Subject: qed: iWARP - Add check for errors on a SYN packet A SYN packet which arrives with errors from FW should be dropped. This required adding an additional field to the ll2 rx completion data. Signed-off-by: Michal Kalderon Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_iwarp.c | 8 ++++++++ drivers/net/ethernet/qlogic/qed/qed_ll2.c | 1 + include/linux/qed/qed_ll2_if.h | 1 + 3 files changed, 10 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/qlogic/qed/qed_iwarp.c b/drivers/net/ethernet/qlogic/qed/qed_iwarp.c index 568e9853cc8d..8fc9c811f6e3 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_iwarp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_iwarp.c @@ -1733,6 +1733,14 @@ qed_iwarp_ll2_comp_syn_pkt(void *cxt, struct qed_ll2_comp_rx_data *data) memset(&cm_info, 0, sizeof(cm_info)); ll2_syn_handle = p_hwfn->p_rdma_info->iwarp.ll2_syn_handle; + + /* Check if packet was received with errors... */ + if (data->err_flags) { + DP_NOTICE(p_hwfn, "Error received on SYN packet: 0x%x\n", + data->err_flags); + goto err; + } + if (GET_FIELD(data->parse_flags, PARSING_AND_ERR_FLAGS_L4CHKSMWASCALCULATED) && GET_FIELD(data->parse_flags, PARSING_AND_ERR_FLAGS_L4CHKSMERROR)) { diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index c06ad4f0758e..250afa5486cf 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -413,6 +413,7 @@ static void qed_ll2_rxq_parse_reg(struct qed_hwfn *p_hwfn, struct qed_ll2_comp_rx_data *data) { data->parse_flags = le16_to_cpu(p_cqe->rx_cqe_fp.parse_flags.flags); + data->err_flags = le16_to_cpu(p_cqe->rx_cqe_fp.err_flags.flags); data->length.packet_length = le16_to_cpu(p_cqe->rx_cqe_fp.packet_length); data->vlan = le16_to_cpu(p_cqe->rx_cqe_fp.vlan); diff --git a/include/linux/qed/qed_ll2_if.h b/include/linux/qed/qed_ll2_if.h index dd7a3b86bb9e..89fa0bbd54f3 100644 --- a/include/linux/qed/qed_ll2_if.h +++ b/include/linux/qed/qed_ll2_if.h @@ -101,6 +101,7 @@ struct qed_ll2_comp_rx_data { void *cookie; dma_addr_t rx_buf_addr; u16 parse_flags; + u16 err_flags; u16 vlan; bool b_last_packet; u8 connection_handle; -- cgit v1.2.3-59-g8ed1b From 6aaae2b6c4330a46204bca042f1d2f41e8e18dea Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 25 Sep 2017 02:25:50 +0200 Subject: bpf: rename bpf_compute_data_end into bpf_compute_data_pointers Just do the rename into bpf_compute_data_pointers() as we'll add one more pointer here to recompute. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Acked-by: John Fastabend Signed-off-by: David S. Miller --- include/linux/filter.h | 9 ++++++--- kernel/bpf/sockmap.c | 4 ++-- net/bpf/test_run.c | 2 +- net/core/filter.c | 14 +++++++------- net/core/lwt_bpf.c | 2 +- net/sched/act_bpf.c | 4 ++-- net/sched/cls_bpf.c | 4 ++-- 7 files changed, 21 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/filter.h b/include/linux/filter.h index d29e58fde364..052bab3d62e7 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -496,10 +496,13 @@ struct xdp_buff { void *data_hard_start; }; -/* compute the linear packet data range [data, data_end) which - * will be accessed by cls_bpf, act_bpf and lwt programs +/* Compute the linear packet data range [data, data_end) which + * will be accessed by various program types (cls_bpf, act_bpf, + * lwt, ...). Subsystems allowing direct data access must (!) + * ensure that cb[] area can be written to when BPF program is + * invoked (otherwise cb[] save/restore is necessary). */ -static inline void bpf_compute_data_end(struct sk_buff *skb) +static inline void bpf_compute_data_pointers(struct sk_buff *skb) { struct bpf_skb_data_end *cb = (struct bpf_skb_data_end *)skb->cb; diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c index 6424ce0e4969..a298d6666698 100644 --- a/kernel/bpf/sockmap.c +++ b/kernel/bpf/sockmap.c @@ -102,7 +102,7 @@ static int smap_verdict_func(struct smap_psock *psock, struct sk_buff *skb) skb_orphan(skb); skb->sk = psock->sock; - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); rc = (*prog->bpf_func)(skb, prog->insnsi); skb->sk = NULL; @@ -369,7 +369,7 @@ static int smap_parse_func_strparser(struct strparser *strp, * any socket yet. */ skb->sk = psock->sock; - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); rc = (*prog->bpf_func)(skb, prog->insnsi); skb->sk = NULL; rcu_read_unlock(); diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index 6be41a44d688..df672517b4fd 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -133,7 +133,7 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, if (is_l2) __skb_push(skb, ETH_HLEN); if (is_direct_pkt_access) - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); retval = bpf_test_run(prog, skb, repeat, &duration); if (!is_l2) __skb_push(skb, ETH_HLEN); diff --git a/net/core/filter.c b/net/core/filter.c index 82edad58d066..c468e7cfad19 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1402,7 +1402,7 @@ static inline int bpf_try_make_writable(struct sk_buff *skb, { int err = __bpf_try_make_writable(skb, write_len); - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); return err; } @@ -1962,7 +1962,7 @@ BPF_CALL_3(bpf_skb_vlan_push, struct sk_buff *, skb, __be16, vlan_proto, ret = skb_vlan_push(skb, vlan_proto, vlan_tci); bpf_pull_mac_rcsum(skb); - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); return ret; } @@ -1984,7 +1984,7 @@ BPF_CALL_1(bpf_skb_vlan_pop, struct sk_buff *, skb) ret = skb_vlan_pop(skb); bpf_pull_mac_rcsum(skb); - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); return ret; } @@ -2178,7 +2178,7 @@ BPF_CALL_3(bpf_skb_change_proto, struct sk_buff *, skb, __be16, proto, * need to be verified first. */ ret = bpf_skb_proto_xlat(skb, proto); - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); return ret; } @@ -2303,7 +2303,7 @@ static int bpf_skb_adjust_net(struct sk_buff *skb, s32 len_diff) ret = shrink ? bpf_skb_net_shrink(skb, len_diff_abs) : bpf_skb_net_grow(skb, len_diff_abs); - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); return ret; } @@ -2394,7 +2394,7 @@ BPF_CALL_3(bpf_skb_change_tail, struct sk_buff *, skb, u32, new_len, skb_gso_reset(skb); } - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); return ret; } @@ -2434,7 +2434,7 @@ BPF_CALL_3(bpf_skb_change_head, struct sk_buff *, skb, u32, head_room, skb_reset_mac_header(skb); } - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); return 0; } diff --git a/net/core/lwt_bpf.c b/net/core/lwt_bpf.c index 1307731ddfe4..e7e626fb87bb 100644 --- a/net/core/lwt_bpf.c +++ b/net/core/lwt_bpf.c @@ -51,7 +51,7 @@ static int run_lwt_bpf(struct sk_buff *skb, struct bpf_lwt_prog *lwt, */ preempt_disable(); rcu_read_lock(); - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); ret = bpf_prog_run_save_cb(lwt->prog, skb); rcu_read_unlock(); diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index c0c707eb2c96..5ef8ce8c83d4 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -49,11 +49,11 @@ static int tcf_bpf(struct sk_buff *skb, const struct tc_action *act, filter = rcu_dereference(prog->filter); if (at_ingress) { __skb_push(skb, skb->mac_len); - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); filter_res = BPF_PROG_RUN(filter, skb); __skb_pull(skb, skb->mac_len); } else { - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); filter_res = BPF_PROG_RUN(filter, skb); } rcu_read_unlock(); diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 520c5027646a..36671b0fb125 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -99,11 +99,11 @@ static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, } else if (at_ingress) { /* It is safe to push/pull even if skb_shared() */ __skb_push(skb, skb->mac_len); - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); filter_res = BPF_PROG_RUN(prog->filter, skb); __skb_pull(skb, skb->mac_len); } else { - bpf_compute_data_end(skb); + bpf_compute_data_pointers(skb); filter_res = BPF_PROG_RUN(prog->filter, skb); } -- cgit v1.2.3-59-g8ed1b From de8f3a83b0a0fddb2cf56e7a718127e9619ea3da Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Mon, 25 Sep 2017 02:25:51 +0200 Subject: bpf: add meta pointer for direct access This work enables generic transfer of metadata from XDP into skb. The basic idea is that we can make use of the fact that the resulting skb must be linear and already comes with a larger headroom for supporting bpf_xdp_adjust_head(), which mangles xdp->data. Here, we base our work on a similar principle and introduce a small helper bpf_xdp_adjust_meta() for adjusting a new pointer called xdp->data_meta. Thus, the packet has a flexible and programmable room for meta data, followed by the actual packet data. struct xdp_buff is therefore laid out that we first point to data_hard_start, then data_meta directly prepended to data followed by data_end marking the end of packet. bpf_xdp_adjust_head() takes into account whether we have meta data already prepended and if so, memmove()s this along with the given offset provided there's enough room. xdp->data_meta is optional and programs are not required to use it. The rationale is that when we process the packet in XDP (e.g. as DoS filter), we can push further meta data along with it for the XDP_PASS case, and give the guarantee that a clsact ingress BPF program on the same device can pick this up for further post-processing. Since we work with skb there, we can also set skb->mark, skb->priority or other skb meta data out of BPF, thus having this scratch space generic and programmable allows for more flexibility than defining a direct 1:1 transfer of potentially new XDP members into skb (it's also more efficient as we don't need to initialize/handle each of such new members). The facility also works together with GRO aggregation. The scratch space at the head of the packet can be multiple of 4 byte up to 32 byte large. Drivers not yet supporting xdp->data_meta can simply be set up with xdp->data_meta as xdp->data + 1 as bpf_xdp_adjust_meta() will detect this and bail out, such that the subsequent match against xdp->data for later access is guaranteed to fail. The verifier treats xdp->data_meta/xdp->data the same way as we treat xdp->data/xdp->data_end pointer comparisons. The requirement for doing the compare against xdp->data is that it hasn't been modified from it's original address we got from ctx access. It may have a range marking already from prior successful xdp->data/xdp->data_end pointer comparisons though. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Acked-by: John Fastabend Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 1 + drivers/net/ethernet/cavium/thunder/nicvf_main.c | 1 + drivers/net/ethernet/intel/i40e/i40e_txrx.c | 1 + drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 1 + drivers/net/ethernet/mellanox/mlx4/en_rx.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 1 + .../net/ethernet/netronome/nfp/nfp_net_common.c | 1 + drivers/net/ethernet/qlogic/qede/qede_fp.c | 1 + drivers/net/tun.c | 1 + drivers/net/virtio_net.c | 2 + include/linux/bpf.h | 1 + include/linux/filter.h | 21 +++- include/linux/skbuff.h | 68 +++++++++++- include/uapi/linux/bpf.h | 13 ++- kernel/bpf/verifier.c | 114 ++++++++++++++++----- net/bpf/test_run.c | 1 + net/core/dev.c | 31 +++++- net/core/filter.c | 77 +++++++++++++- net/core/skbuff.c | 2 + 19 files changed, 297 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index d8f0c837b72c..06ce63c00821 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -94,6 +94,7 @@ bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, xdp.data_hard_start = *data_ptr - offset; xdp.data = *data_ptr; + xdp_set_data_meta_invalid(&xdp); xdp.data_end = *data_ptr + *len; orig_data = xdp.data; mapping = rx_buf->mapping - bp->rx_dma_offset; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 49b80da51ba7..d68478afccbf 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -523,6 +523,7 @@ static inline bool nicvf_xdp_rx(struct nicvf *nic, struct bpf_prog *prog, xdp.data_hard_start = page_address(page); xdp.data = (void *)cpu_addr; + xdp_set_data_meta_invalid(&xdp); xdp.data_end = xdp.data + len; orig_data = xdp.data; diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 1519dfb851d0..f426762bd83a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -2107,6 +2107,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) if (!skb) { xdp.data = page_address(rx_buffer->page) + rx_buffer->page_offset; + xdp_set_data_meta_invalid(&xdp); xdp.data_hard_start = xdp.data - i40e_rx_offset(rx_ring); xdp.data_end = xdp.data + size; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index d962368d08d0..04bb03bda1cd 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2326,6 +2326,7 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, if (!skb) { xdp.data = page_address(rx_buffer->page) + rx_buffer->page_offset; + xdp_set_data_meta_invalid(&xdp); xdp.data_hard_start = xdp.data - ixgbe_rx_offset(rx_ring); xdp.data_end = xdp.data + size; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index b97a55c827eb..8f9cb8abc497 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -762,6 +762,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud xdp.data_hard_start = va - frags[0].page_offset; xdp.data = va; + xdp_set_data_meta_invalid(&xdp); xdp.data_end = xdp.data + length; orig_data = xdp.data; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index f1dd638384d3..30b3f3fbd719 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -794,6 +794,7 @@ static inline int mlx5e_xdp_handle(struct mlx5e_rq *rq, return false; xdp.data = va + *rx_headroom; + xdp_set_data_meta_invalid(&xdp); xdp.data_end = xdp.data + *len; xdp.data_hard_start = va; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 1c0187f0af51..e3a38be3600a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -1583,6 +1583,7 @@ static int nfp_net_run_xdp(struct bpf_prog *prog, void *data, void *hard_start, xdp.data_hard_start = hard_start; xdp.data = data + *off; + xdp_set_data_meta_invalid(&xdp); xdp.data_end = data + *off + *len; orig_data = xdp.data; diff --git a/drivers/net/ethernet/qlogic/qede/qede_fp.c b/drivers/net/ethernet/qlogic/qede/qede_fp.c index 6fc854b120b0..48ec4c56cddf 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_fp.c +++ b/drivers/net/ethernet/qlogic/qede/qede_fp.c @@ -1004,6 +1004,7 @@ static bool qede_rx_xdp(struct qede_dev *edev, xdp.data_hard_start = page_address(bd->data); xdp.data = xdp.data_hard_start + *data_offset; + xdp_set_data_meta_invalid(&xdp); xdp.data_end = xdp.data + *len; /* Queues always have a full reset currently, so for the time diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 2c36f6ebad79..a6e0bffe3d29 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1468,6 +1468,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct *tun, xdp.data_hard_start = buf; xdp.data = buf + pad; + xdp_set_data_meta_invalid(&xdp); xdp.data_end = xdp.data + len; orig_data = xdp.data; act = bpf_prog_run_xdp(xdp_prog, &xdp); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index dd14a4547932..fc059f193e7d 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -554,6 +554,7 @@ static struct sk_buff *receive_small(struct net_device *dev, xdp.data_hard_start = buf + VIRTNET_RX_PAD + vi->hdr_len; xdp.data = xdp.data_hard_start + xdp_headroom; + xdp_set_data_meta_invalid(&xdp); xdp.data_end = xdp.data + len; orig_data = xdp.data; act = bpf_prog_run_xdp(xdp_prog, &xdp); @@ -686,6 +687,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, data = page_address(xdp_page) + offset; xdp.data_hard_start = data - VIRTIO_XDP_HEADROOM + vi->hdr_len; xdp.data = data + vi->hdr_len; + xdp_set_data_meta_invalid(&xdp); xdp.data_end = xdp.data + (len - vi->hdr_len); act = bpf_prog_run_xdp(xdp_prog, &xdp); diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 8390859e79e7..2b672c50f160 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -137,6 +137,7 @@ enum bpf_reg_type { PTR_TO_MAP_VALUE, /* reg points to map element value */ PTR_TO_MAP_VALUE_OR_NULL,/* points to map elem value or NULL */ PTR_TO_STACK, /* reg == frame_pointer + offset */ + PTR_TO_PACKET_META, /* skb->data - meta_len */ PTR_TO_PACKET, /* reg points to skb->data */ PTR_TO_PACKET_END, /* skb->data + headlen */ }; diff --git a/include/linux/filter.h b/include/linux/filter.h index 052bab3d62e7..911d454af107 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -487,12 +487,14 @@ struct sk_filter { struct bpf_skb_data_end { struct qdisc_skb_cb qdisc_cb; + void *data_meta; void *data_end; }; struct xdp_buff { void *data; void *data_end; + void *data_meta; void *data_hard_start; }; @@ -507,7 +509,8 @@ static inline void bpf_compute_data_pointers(struct sk_buff *skb) struct bpf_skb_data_end *cb = (struct bpf_skb_data_end *)skb->cb; BUILD_BUG_ON(sizeof(*cb) > FIELD_SIZEOF(struct sk_buff, cb)); - cb->data_end = skb->data + skb_headlen(skb); + cb->data_meta = skb->data - skb_metadata_len(skb); + cb->data_end = skb->data + skb_headlen(skb); } static inline u8 *bpf_skb_cb(struct sk_buff *skb) @@ -728,8 +731,22 @@ int xdp_do_redirect(struct net_device *dev, struct bpf_prog *prog); void xdp_do_flush_map(void); +/* Drivers not supporting XDP metadata can use this helper, which + * rejects any room expansion for metadata as a result. + */ +static __always_inline void +xdp_set_data_meta_invalid(struct xdp_buff *xdp) +{ + xdp->data_meta = xdp->data + 1; +} + +static __always_inline bool +xdp_data_meta_unsupported(const struct xdp_buff *xdp) +{ + return unlikely(xdp->data_meta > xdp->data); +} + void bpf_warn_invalid_xdp_action(u32 act); -void bpf_warn_invalid_xdp_redirect(u32 ifindex); struct sock *do_sk_redirect_map(void); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index f9db5539a6fb..19e64bfb1a66 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -489,8 +489,9 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb, * the end of the header data, ie. at skb->end. */ struct skb_shared_info { - unsigned short _unused; - unsigned char nr_frags; + __u8 __unused; + __u8 meta_len; + __u8 nr_frags; __u8 tx_flags; unsigned short gso_size; /* Warning: this field is not always filled in (UFO)! */ @@ -3400,6 +3401,69 @@ static inline ktime_t net_invalid_timestamp(void) return 0; } +static inline u8 skb_metadata_len(const struct sk_buff *skb) +{ + return skb_shinfo(skb)->meta_len; +} + +static inline void *skb_metadata_end(const struct sk_buff *skb) +{ + return skb_mac_header(skb); +} + +static inline bool __skb_metadata_differs(const struct sk_buff *skb_a, + const struct sk_buff *skb_b, + u8 meta_len) +{ + const void *a = skb_metadata_end(skb_a); + const void *b = skb_metadata_end(skb_b); + /* Using more efficient varaiant than plain call to memcmp(). */ +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && BITS_PER_LONG == 64 + u64 diffs = 0; + + switch (meta_len) { +#define __it(x, op) (x -= sizeof(u##op)) +#define __it_diff(a, b, op) (*(u##op *)__it(a, op)) ^ (*(u##op *)__it(b, op)) + case 32: diffs |= __it_diff(a, b, 64); + case 24: diffs |= __it_diff(a, b, 64); + case 16: diffs |= __it_diff(a, b, 64); + case 8: diffs |= __it_diff(a, b, 64); + break; + case 28: diffs |= __it_diff(a, b, 64); + case 20: diffs |= __it_diff(a, b, 64); + case 12: diffs |= __it_diff(a, b, 64); + case 4: diffs |= __it_diff(a, b, 32); + break; + } + return diffs; +#else + return memcmp(a - meta_len, b - meta_len, meta_len); +#endif +} + +static inline bool skb_metadata_differs(const struct sk_buff *skb_a, + const struct sk_buff *skb_b) +{ + u8 len_a = skb_metadata_len(skb_a); + u8 len_b = skb_metadata_len(skb_b); + + if (!(len_a | len_b)) + return false; + + return len_a != len_b ? + true : __skb_metadata_differs(skb_a, skb_b, len_a); +} + +static inline void skb_metadata_set(struct sk_buff *skb, u8 meta_len) +{ + skb_shinfo(skb)->meta_len = meta_len; +} + +static inline void skb_metadata_clear(struct sk_buff *skb) +{ + skb_metadata_set(skb, 0); +} + struct sk_buff *skb_clone_sk(struct sk_buff *skb); #ifdef CONFIG_NETWORK_PHY_TIMESTAMPING diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 43ab5c402f98..e43491ac4823 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -582,6 +582,12 @@ union bpf_attr { * @map: pointer to sockmap to update * @key: key to insert/update sock in map * @flags: same flags as map update elem + * + * int bpf_xdp_adjust_meta(xdp_md, delta) + * Adjust the xdp_md.data_meta by delta + * @xdp_md: pointer to xdp_md + * @delta: An positive/negative integer to be added to xdp_md.data_meta + * Return: 0 on success or negative on error */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -638,6 +644,7 @@ union bpf_attr { FN(redirect_map), \ FN(sk_redirect_map), \ FN(sock_map_update), \ + FN(xdp_adjust_meta), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -715,7 +722,7 @@ struct __sk_buff { __u32 data_end; __u32 napi_id; - /* accessed by BPF_PROG_TYPE_sk_skb types */ + /* Accessed by BPF_PROG_TYPE_sk_skb types from here to ... */ __u32 family; __u32 remote_ip4; /* Stored in network byte order */ __u32 local_ip4; /* Stored in network byte order */ @@ -723,6 +730,9 @@ struct __sk_buff { __u32 local_ip6[4]; /* Stored in network byte order */ __u32 remote_port; /* Stored in network byte order */ __u32 local_port; /* stored in host byte order */ + /* ... here. */ + + __u32 data_meta; }; struct bpf_tunnel_key { @@ -783,6 +793,7 @@ enum xdp_action { struct xdp_md { __u32 data; __u32 data_end; + __u32 data_meta; }; enum sk_action { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index b914fbe1383e..f849eca36052 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -177,6 +177,12 @@ static __printf(1, 2) void verbose(const char *fmt, ...) va_end(args); } +static bool type_is_pkt_pointer(enum bpf_reg_type type) +{ + return type == PTR_TO_PACKET || + type == PTR_TO_PACKET_META; +} + /* string representation of 'enum bpf_reg_type' */ static const char * const reg_type_str[] = { [NOT_INIT] = "?", @@ -187,6 +193,7 @@ static const char * const reg_type_str[] = { [PTR_TO_MAP_VALUE_OR_NULL] = "map_value_or_null", [PTR_TO_STACK] = "fp", [PTR_TO_PACKET] = "pkt", + [PTR_TO_PACKET_META] = "pkt_meta", [PTR_TO_PACKET_END] = "pkt_end", }; @@ -226,7 +233,7 @@ static void print_verifier_state(struct bpf_verifier_state *state) verbose("(id=%d", reg->id); if (t != SCALAR_VALUE) verbose(",off=%d", reg->off); - if (t == PTR_TO_PACKET) + if (type_is_pkt_pointer(t)) verbose(",r=%d", reg->range); else if (t == CONST_PTR_TO_MAP || t == PTR_TO_MAP_VALUE || @@ -519,6 +526,31 @@ static void mark_reg_known_zero(struct bpf_reg_state *regs, u32 regno) __mark_reg_known_zero(regs + regno); } +static bool reg_is_pkt_pointer(const struct bpf_reg_state *reg) +{ + return type_is_pkt_pointer(reg->type); +} + +static bool reg_is_pkt_pointer_any(const struct bpf_reg_state *reg) +{ + return reg_is_pkt_pointer(reg) || + reg->type == PTR_TO_PACKET_END; +} + +/* Unmodified PTR_TO_PACKET[_META,_END] register from ctx access. */ +static bool reg_is_init_pkt_pointer(const struct bpf_reg_state *reg, + enum bpf_reg_type which) +{ + /* The register can already have a range from prior markings. + * This is fine as long as it hasn't been advanced from its + * origin. + */ + return reg->type == which && + reg->id == 0 && + reg->off == 0 && + tnum_equals_const(reg->var_off, 0); +} + /* Attempts to improve min/max values based on var_off information */ static void __update_reg_bounds(struct bpf_reg_state *reg) { @@ -702,6 +734,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type) case PTR_TO_STACK: case PTR_TO_CTX: case PTR_TO_PACKET: + case PTR_TO_PACKET_META: case PTR_TO_PACKET_END: case CONST_PTR_TO_MAP: return true; @@ -1047,7 +1080,10 @@ static int check_ptr_alignment(struct bpf_verifier_env *env, switch (reg->type) { case PTR_TO_PACKET: - /* special case, because of NET_IP_ALIGN */ + case PTR_TO_PACKET_META: + /* Special case, because of NET_IP_ALIGN. Given metadata sits + * right in front, treat it the very same way. + */ return check_pkt_ptr_alignment(reg, off, size, strict); case PTR_TO_MAP_VALUE: pointer_desc = "value "; @@ -1124,8 +1160,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn err = check_ctx_access(env, insn_idx, off, size, t, ®_type); if (!err && t == BPF_READ && value_regno >= 0) { /* ctx access returns either a scalar, or a - * PTR_TO_PACKET[_END]. In the latter case, we know - * the offset is zero. + * PTR_TO_PACKET[_META,_END]. In the latter + * case, we know the offset is zero. */ if (reg_type == SCALAR_VALUE) mark_reg_unknown(state->regs, value_regno); @@ -1170,7 +1206,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn } else { err = check_stack_read(state, off, size, value_regno); } - } else if (reg->type == PTR_TO_PACKET) { + } else if (reg_is_pkt_pointer(reg)) { if (t == BPF_WRITE && !may_access_direct_pkt_data(env, NULL, t)) { verbose("cannot write into packet\n"); return -EACCES; @@ -1310,6 +1346,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, switch (reg->type) { case PTR_TO_PACKET: + case PTR_TO_PACKET_META: return check_packet_access(env, regno, reg->off, access_size); case PTR_TO_MAP_VALUE: return check_map_access(env, regno, reg->off, access_size); @@ -1342,7 +1379,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, return 0; } - if (type == PTR_TO_PACKET && + if (type_is_pkt_pointer(type) && !may_access_direct_pkt_data(env, meta, BPF_READ)) { verbose("helper access to the packet is not allowed\n"); return -EACCES; @@ -1351,7 +1388,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, if (arg_type == ARG_PTR_TO_MAP_KEY || arg_type == ARG_PTR_TO_MAP_VALUE) { expected_type = PTR_TO_STACK; - if (type != PTR_TO_PACKET && type != expected_type) + if (!type_is_pkt_pointer(type) && + type != expected_type) goto err_type; } else if (arg_type == ARG_CONST_SIZE || arg_type == ARG_CONST_SIZE_OR_ZERO) { @@ -1375,7 +1413,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, */ if (register_is_null(*reg)) /* final test in check_stack_boundary() */; - else if (type != PTR_TO_PACKET && type != PTR_TO_MAP_VALUE && + else if (!type_is_pkt_pointer(type) && + type != PTR_TO_MAP_VALUE && type != expected_type) goto err_type; meta->raw_mode = arg_type == ARG_PTR_TO_UNINIT_MEM; @@ -1401,7 +1440,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, verbose("invalid map_ptr to access map->key\n"); return -EACCES; } - if (type == PTR_TO_PACKET) + if (type_is_pkt_pointer(type)) err = check_packet_access(env, regno, reg->off, meta->map_ptr->key_size); else @@ -1417,7 +1456,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, verbose("invalid map_ptr to access map->value\n"); return -EACCES; } - if (type == PTR_TO_PACKET) + if (type_is_pkt_pointer(type)) err = check_packet_access(env, regno, reg->off, meta->map_ptr->value_size); else @@ -1590,8 +1629,8 @@ static int check_raw_mode(const struct bpf_func_proto *fn) return count > 1 ? -EINVAL : 0; } -/* Packet data might have moved, any old PTR_TO_PACKET[_END] are now invalid, - * so turn them into unknown SCALAR_VALUE. +/* Packet data might have moved, any old PTR_TO_PACKET[_META,_END] + * are now invalid, so turn them into unknown SCALAR_VALUE. */ static void clear_all_pkt_pointers(struct bpf_verifier_env *env) { @@ -1600,18 +1639,15 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env) int i; for (i = 0; i < MAX_BPF_REG; i++) - if (regs[i].type == PTR_TO_PACKET || - regs[i].type == PTR_TO_PACKET_END) + if (reg_is_pkt_pointer_any(®s[i])) mark_reg_unknown(regs, i); for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { if (state->stack_slot_type[i] != STACK_SPILL) continue; reg = &state->spilled_regs[i / BPF_REG_SIZE]; - if (reg->type != PTR_TO_PACKET && - reg->type != PTR_TO_PACKET_END) - continue; - __mark_reg_unknown(reg); + if (reg_is_pkt_pointer_any(reg)) + __mark_reg_unknown(reg); } } @@ -1871,7 +1907,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, } dst_reg->var_off = tnum_add(ptr_reg->var_off, off_reg->var_off); dst_reg->off = ptr_reg->off; - if (ptr_reg->type == PTR_TO_PACKET) { + if (reg_is_pkt_pointer(ptr_reg)) { dst_reg->id = ++env->id_gen; /* something was added to pkt_ptr, set range to zero */ dst_reg->range = 0; @@ -1931,7 +1967,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, } dst_reg->var_off = tnum_sub(ptr_reg->var_off, off_reg->var_off); dst_reg->off = ptr_reg->off; - if (ptr_reg->type == PTR_TO_PACKET) { + if (reg_is_pkt_pointer(ptr_reg)) { dst_reg->id = ++env->id_gen; /* something was added to pkt_ptr, set range to zero */ if (smin_val < 0) @@ -2421,7 +2457,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) } static void find_good_pkt_pointers(struct bpf_verifier_state *state, - struct bpf_reg_state *dst_reg) + struct bpf_reg_state *dst_reg, + enum bpf_reg_type type) { struct bpf_reg_state *regs = state->regs, *reg; int i; @@ -2483,7 +2520,7 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state, * dst_reg->off is known < MAX_PACKET_OFF, therefore it fits in a u16. */ for (i = 0; i < MAX_BPF_REG; i++) - if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id) + if (regs[i].type == type && regs[i].id == dst_reg->id) /* keep the maximum range already checked */ regs[i].range = max_t(u16, regs[i].range, dst_reg->off); @@ -2491,7 +2528,7 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state, if (state->stack_slot_type[i] != STACK_SPILL) continue; reg = &state->spilled_regs[i / BPF_REG_SIZE]; - if (reg->type == PTR_TO_PACKET && reg->id == dst_reg->id) + if (reg->type == type && reg->id == dst_reg->id) reg->range = max_t(u16, reg->range, dst_reg->off); } } @@ -2856,19 +2893,39 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT && dst_reg->type == PTR_TO_PACKET && regs[insn->src_reg].type == PTR_TO_PACKET_END) { - find_good_pkt_pointers(this_branch, dst_reg); + find_good_pkt_pointers(this_branch, dst_reg, PTR_TO_PACKET); } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT && dst_reg->type == PTR_TO_PACKET && regs[insn->src_reg].type == PTR_TO_PACKET_END) { - find_good_pkt_pointers(other_branch, dst_reg); + find_good_pkt_pointers(other_branch, dst_reg, PTR_TO_PACKET); } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE && dst_reg->type == PTR_TO_PACKET_END && regs[insn->src_reg].type == PTR_TO_PACKET) { - find_good_pkt_pointers(other_branch, ®s[insn->src_reg]); + find_good_pkt_pointers(other_branch, ®s[insn->src_reg], + PTR_TO_PACKET); } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE && dst_reg->type == PTR_TO_PACKET_END && regs[insn->src_reg].type == PTR_TO_PACKET) { - find_good_pkt_pointers(this_branch, ®s[insn->src_reg]); + find_good_pkt_pointers(this_branch, ®s[insn->src_reg], + PTR_TO_PACKET); + } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT && + dst_reg->type == PTR_TO_PACKET_META && + reg_is_init_pkt_pointer(®s[insn->src_reg], PTR_TO_PACKET)) { + find_good_pkt_pointers(this_branch, dst_reg, PTR_TO_PACKET_META); + } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT && + dst_reg->type == PTR_TO_PACKET_META && + reg_is_init_pkt_pointer(®s[insn->src_reg], PTR_TO_PACKET)) { + find_good_pkt_pointers(other_branch, dst_reg, PTR_TO_PACKET_META); + } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE && + reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) && + regs[insn->src_reg].type == PTR_TO_PACKET_META) { + find_good_pkt_pointers(other_branch, ®s[insn->src_reg], + PTR_TO_PACKET_META); + } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE && + reg_is_init_pkt_pointer(dst_reg, PTR_TO_PACKET) && + regs[insn->src_reg].type == PTR_TO_PACKET_META) { + find_good_pkt_pointers(this_branch, ®s[insn->src_reg], + PTR_TO_PACKET_META); } else if (is_pointer_value(env, insn->dst_reg)) { verbose("R%d pointer comparison prohibited\n", insn->dst_reg); return -EACCES; @@ -3298,8 +3355,9 @@ static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur, return false; /* Check our ids match any regs they're supposed to */ return check_ids(rold->id, rcur->id, idmap); + case PTR_TO_PACKET_META: case PTR_TO_PACKET: - if (rcur->type != PTR_TO_PACKET) + if (rcur->type != rold->type) return false; /* We must have at least as much range as the old ptr * did, so that any accesses which were safe before are diff --git a/net/bpf/test_run.c b/net/bpf/test_run.c index df672517b4fd..a86e6687026e 100644 --- a/net/bpf/test_run.c +++ b/net/bpf/test_run.c @@ -162,6 +162,7 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, xdp.data_hard_start = data; xdp.data = data + XDP_PACKET_HEADROOM + NET_IP_ALIGN; + xdp.data_meta = xdp.data; xdp.data_end = xdp.data + size; retval = bpf_test_run(prog, &xdp, repeat, &duration); diff --git a/net/core/dev.c b/net/core/dev.c index 97abddd9039a..e350c768d4b5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3864,8 +3864,8 @@ drop: static u32 netif_receive_generic_xdp(struct sk_buff *skb, struct bpf_prog *xdp_prog) { + u32 metalen, act = XDP_DROP; struct xdp_buff xdp; - u32 act = XDP_DROP; void *orig_data; int hlen, off; u32 mac_len; @@ -3876,8 +3876,25 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, if (skb_cloned(skb)) return XDP_PASS; - if (skb_linearize(skb)) - goto do_drop; + /* XDP packets must be linear and must have sufficient headroom + * of XDP_PACKET_HEADROOM bytes. This is the guarantee that also + * native XDP provides, thus we need to do it here as well. + */ + if (skb_is_nonlinear(skb) || + skb_headroom(skb) < XDP_PACKET_HEADROOM) { + int hroom = XDP_PACKET_HEADROOM - skb_headroom(skb); + int troom = skb->tail + skb->data_len - skb->end; + + /* In case we have to go down the path and also linearize, + * then lets do the pskb_expand_head() work just once here. + */ + if (pskb_expand_head(skb, + hroom > 0 ? ALIGN(hroom, NET_SKB_PAD) : 0, + troom > 0 ? troom + 128 : 0, GFP_ATOMIC)) + goto do_drop; + if (troom > 0 && __skb_linearize(skb)) + goto do_drop; + } /* The XDP program wants to see the packet starting at the MAC * header. @@ -3885,6 +3902,7 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, mac_len = skb->data - skb_mac_header(skb); hlen = skb_headlen(skb) + mac_len; xdp.data = skb->data - mac_len; + xdp.data_meta = xdp.data; xdp.data_end = xdp.data + hlen; xdp.data_hard_start = skb->data - skb_headroom(skb); orig_data = xdp.data; @@ -3902,10 +3920,12 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, case XDP_REDIRECT: case XDP_TX: __skb_push(skb, mac_len); - /* fall through */ + break; case XDP_PASS: + metalen = xdp.data - xdp.data_meta; + if (metalen) + skb_metadata_set(skb, metalen); break; - default: bpf_warn_invalid_xdp_action(act); /* fall through */ @@ -4695,6 +4715,7 @@ static void gro_list_prepare(struct napi_struct *napi, struct sk_buff *skb) diffs = (unsigned long)p->dev ^ (unsigned long)skb->dev; diffs |= p->vlan_tci ^ skb->vlan_tci; diffs |= skb_metadata_dst_cmp(p, skb); + diffs |= skb_metadata_differs(p, skb); if (maclen == ETH_HLEN) diffs |= compare_ether_header(skb_mac_header(p), skb_mac_header(skb)); diff --git a/net/core/filter.c b/net/core/filter.c index c468e7cfad19..9b6e7e84aafd 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2447,14 +2447,26 @@ static const struct bpf_func_proto bpf_skb_change_head_proto = { .arg3_type = ARG_ANYTHING, }; +static unsigned long xdp_get_metalen(const struct xdp_buff *xdp) +{ + return xdp_data_meta_unsupported(xdp) ? 0 : + xdp->data - xdp->data_meta; +} + BPF_CALL_2(bpf_xdp_adjust_head, struct xdp_buff *, xdp, int, offset) { + unsigned long metalen = xdp_get_metalen(xdp); + void *data_start = xdp->data_hard_start + metalen; void *data = xdp->data + offset; - if (unlikely(data < xdp->data_hard_start || + if (unlikely(data < data_start || data > xdp->data_end - ETH_HLEN)) return -EINVAL; + if (metalen) + memmove(xdp->data_meta + offset, + xdp->data_meta, metalen); + xdp->data_meta += offset; xdp->data = data; return 0; @@ -2468,6 +2480,33 @@ static const struct bpf_func_proto bpf_xdp_adjust_head_proto = { .arg2_type = ARG_ANYTHING, }; +BPF_CALL_2(bpf_xdp_adjust_meta, struct xdp_buff *, xdp, int, offset) +{ + void *meta = xdp->data_meta + offset; + unsigned long metalen = xdp->data - meta; + + if (xdp_data_meta_unsupported(xdp)) + return -ENOTSUPP; + if (unlikely(meta < xdp->data_hard_start || + meta > xdp->data)) + return -EINVAL; + if (unlikely((metalen & (sizeof(__u32) - 1)) || + (metalen > 32))) + return -EACCES; + + xdp->data_meta = meta; + + return 0; +} + +static const struct bpf_func_proto bpf_xdp_adjust_meta_proto = { + .func = bpf_xdp_adjust_meta, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, +}; + static int __bpf_tx_xdp(struct net_device *dev, struct bpf_map *map, struct xdp_buff *xdp, @@ -2692,7 +2731,8 @@ bool bpf_helper_changes_pkt_data(void *func) func == bpf_clone_redirect || func == bpf_l3_csum_replace || func == bpf_l4_csum_replace || - func == bpf_xdp_adjust_head) + func == bpf_xdp_adjust_head || + func == bpf_xdp_adjust_meta) return true; return false; @@ -3288,6 +3328,8 @@ xdp_func_proto(enum bpf_func_id func_id) return &bpf_get_smp_processor_id_proto; case BPF_FUNC_xdp_adjust_head: return &bpf_xdp_adjust_head_proto; + case BPF_FUNC_xdp_adjust_meta: + return &bpf_xdp_adjust_meta_proto; case BPF_FUNC_redirect: return &bpf_xdp_redirect_proto; case BPF_FUNC_redirect_map: @@ -3418,6 +3460,7 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type case bpf_ctx_range_till(struct __sk_buff, remote_ip4, remote_ip4): case bpf_ctx_range_till(struct __sk_buff, local_ip4, local_ip4): case bpf_ctx_range(struct __sk_buff, data): + case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, data_end): if (size != size_default) return false; @@ -3444,6 +3487,7 @@ static bool sk_filter_is_valid_access(int off, int size, switch (off) { case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range(struct __sk_buff, data): + case bpf_ctx_range(struct __sk_buff, data_meta): case bpf_ctx_range(struct __sk_buff, data_end): case bpf_ctx_range_till(struct __sk_buff, family, local_port): return false; @@ -3468,6 +3512,7 @@ static bool lwt_is_valid_access(int off, int size, switch (off) { case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range_till(struct __sk_buff, family, local_port): + case bpf_ctx_range(struct __sk_buff, data_meta): return false; } @@ -3586,6 +3631,9 @@ static bool tc_cls_act_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data): info->reg_type = PTR_TO_PACKET; break; + case bpf_ctx_range(struct __sk_buff, data_meta): + info->reg_type = PTR_TO_PACKET_META; + break; case bpf_ctx_range(struct __sk_buff, data_end): info->reg_type = PTR_TO_PACKET_END; break; @@ -3619,6 +3667,9 @@ static bool xdp_is_valid_access(int off, int size, case offsetof(struct xdp_md, data): info->reg_type = PTR_TO_PACKET; break; + case offsetof(struct xdp_md, data_meta): + info->reg_type = PTR_TO_PACKET_META; + break; case offsetof(struct xdp_md, data_end): info->reg_type = PTR_TO_PACKET_END; break; @@ -3677,6 +3728,12 @@ static bool sk_skb_is_valid_access(int off, int size, enum bpf_access_type type, struct bpf_insn_access_aux *info) { + switch (off) { + case bpf_ctx_range(struct __sk_buff, tc_classid): + case bpf_ctx_range(struct __sk_buff, data_meta): + return false; + } + if (type == BPF_WRITE) { switch (off) { case bpf_ctx_range(struct __sk_buff, mark): @@ -3689,8 +3746,6 @@ static bool sk_skb_is_valid_access(int off, int size, } switch (off) { - case bpf_ctx_range(struct __sk_buff, tc_classid): - return false; case bpf_ctx_range(struct __sk_buff, data): info->reg_type = PTR_TO_PACKET; break; @@ -3847,6 +3902,15 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, offsetof(struct sk_buff, data)); break; + case offsetof(struct __sk_buff, data_meta): + off = si->off; + off -= offsetof(struct __sk_buff, data_meta); + off += offsetof(struct sk_buff, cb); + off += offsetof(struct bpf_skb_data_end, data_meta); + *insn++ = BPF_LDX_MEM(BPF_SIZEOF(void *), si->dst_reg, + si->src_reg, off); + break; + case offsetof(struct __sk_buff, data_end): off = si->off; off -= offsetof(struct __sk_buff, data_end); @@ -4095,6 +4159,11 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type, si->dst_reg, si->src_reg, offsetof(struct xdp_buff, data)); break; + case offsetof(struct xdp_md, data_meta): + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct xdp_buff, data_meta), + si->dst_reg, si->src_reg, + offsetof(struct xdp_buff, data_meta)); + break; case offsetof(struct xdp_md, data_end): *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct xdp_buff, data_end), si->dst_reg, si->src_reg, diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 000ce735fa8d..d98c2e3ce2bf 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1509,6 +1509,8 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, skb->nohdr = 0; atomic_set(&skb_shinfo(skb)->dataref, 1); + skb_metadata_clear(skb); + /* It is not generally safe to change skb->truesize. * For the moment, we really care of rx path, or * when skb is orphaned (not attached to a socket). -- cgit v1.2.3-59-g8ed1b From ac58fff1551672c925b0ec2879c4653a69771ee2 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Wed, 19 Apr 2017 13:15:18 -0400 Subject: drm/dp-helper: add missing defines needed by AMD display core. These are all the ones required by the AMD display core. Signed-off-by: Dave Airlie Reviewed-by: Harry Wentland Signed-off-by: Alex Deucher --- include/drm/drm_dp_helper.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index b17476a6909c..d9fab1e1818a 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -510,6 +510,8 @@ # define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0 # define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6 +#define DP_ADJUST_REQUEST_POST_CURSOR2 0x20c + #define DP_TEST_REQUEST 0x218 # define DP_TEST_LINK_TRAINING (1 << 0) # define DP_TEST_LINK_VIDEO_PATTERN (1 << 1) @@ -582,6 +584,8 @@ #define DP_TEST_REFRESH_RATE_NUMERATOR 0x234 +#define DP_TEST_MISC0 0x232 + #define DP_TEST_CRC_R_CR 0x240 #define DP_TEST_CRC_G_Y 0x242 #define DP_TEST_CRC_B_CB 0x244 @@ -590,6 +594,18 @@ # define DP_TEST_CRC_SUPPORTED (1 << 5) # define DP_TEST_COUNT_MASK 0xf +#define DP_TEST_PHY_PATTERN 0x248 +#define DP_TEST_80BIT_CUSTOM_PATTERN_7_0 0x250 +#define DP_TEST_80BIT_CUSTOM_PATTERN_15_8 0x251 +#define DP_TEST_80BIT_CUSTOM_PATTERN_23_16 0x252 +#define DP_TEST_80BIT_CUSTOM_PATTERN_31_24 0x253 +#define DP_TEST_80BIT_CUSTOM_PATTERN_39_32 0x254 +#define DP_TEST_80BIT_CUSTOM_PATTERN_47_40 0x255 +#define DP_TEST_80BIT_CUSTOM_PATTERN_55_48 0x256 +#define DP_TEST_80BIT_CUSTOM_PATTERN_63_56 0x257 +#define DP_TEST_80BIT_CUSTOM_PATTERN_71_64 0x258 +#define DP_TEST_80BIT_CUSTOM_PATTERN_79_72 0x259 + #define DP_TEST_RESPONSE 0x260 # define DP_TEST_ACK (1 << 0) # define DP_TEST_NAK (1 << 1) @@ -611,6 +627,7 @@ #define DP_SINK_OUI 0x400 #define DP_BRANCH_OUI 0x500 #define DP_BRANCH_ID 0x503 +#define DP_BRANCH_REVISION_START 0x509 #define DP_BRANCH_HW_REV 0x509 #define DP_BRANCH_SW_REV 0x50A @@ -738,6 +755,9 @@ #define DP_RECEIVER_ALPM_STATUS 0x200b /* eDP 1.4 */ # define DP_ALPM_LOCK_TIMEOUT_ERROR (1 << 0) +#define DP_DP13_DPCD_REV 0x2200 +#define DP_DP13_MAX_LINK_RATE 0x2201 + #define DP_DPRX_FEATURE_ENUMERATION_LIST 0x2210 /* DP 1.3 */ # define DP_GTC_CAP (1 << 0) /* DP 1.3 */ # define DP_SST_SPLIT_SDP_CAP (1 << 1) /* DP 1.4 */ -- cgit v1.2.3-59-g8ed1b From 3b8e9238a8d194e82f0202a5fb68a63686ebe420 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 25 Sep 2017 10:58:21 +0200 Subject: net: sched: introduce helper to identify gact pass action Introduce a helper called is_tcf_gact_pass which could be used to tell if the action is gact pass or not. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/tc_act/tc_gact.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/net/tc_act/tc_gact.h b/include/net/tc_act/tc_gact.h index 41afe1ce7b16..d979a0d48f9e 100644 --- a/include/net/tc_act/tc_gact.h +++ b/include/net/tc_act/tc_gact.h @@ -33,6 +33,11 @@ static inline bool __is_tcf_gact_act(const struct tc_action *a, int act, return false; } +static inline bool is_tcf_gact_ok(const struct tc_action *a) +{ + return __is_tcf_gact_act(a, TC_ACT_OK, false); +} + static inline bool is_tcf_gact_shot(const struct tc_action *a) { return __is_tcf_gact_act(a, TC_ACT_SHOT, false); -- cgit v1.2.3-59-g8ed1b From e60aa7b53845a261dd419652f12ab9f89e668843 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Thu, 21 Sep 2017 16:52:44 +0100 Subject: iommu/iova: Extend rbtree node caching The cached node mechanism provides a significant performance benefit for allocations using a 32-bit DMA mask, but in the case of non-PCI devices or where the 32-bit space is full, the loss of this benefit can be significant - on large systems there can be many thousands of entries in the tree, such that walking all the way down to find free space every time becomes increasingly awful. Maintain a similar cached node for the whole IOVA space as a superset of the 32-bit space so that performance can remain much more consistent. Inspired by work by Zhen Lei . Tested-by: Ard Biesheuvel Tested-by: Zhen Lei Tested-by: Nate Watterson Signed-off-by: Robin Murphy Signed-off-by: Joerg Roedel --- drivers/iommu/iova.c | 60 ++++++++++++++++++++++++---------------------------- include/linux/iova.h | 3 ++- 2 files changed, 30 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index 20be9a8b3188..c6f5a22f8d20 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -48,6 +48,7 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule, spin_lock_init(&iovad->iova_rbtree_lock); iovad->rbroot = RB_ROOT; + iovad->cached_node = NULL; iovad->cached32_node = NULL; iovad->granule = granule; iovad->start_pfn = start_pfn; @@ -110,48 +111,44 @@ EXPORT_SYMBOL_GPL(init_iova_flush_queue); static struct rb_node * __get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn) { - if ((*limit_pfn > iovad->dma_32bit_pfn) || - (iovad->cached32_node == NULL)) + struct rb_node *cached_node = NULL; + struct iova *curr_iova; + + if (*limit_pfn <= iovad->dma_32bit_pfn) + cached_node = iovad->cached32_node; + if (!cached_node) + cached_node = iovad->cached_node; + if (!cached_node) return rb_last(&iovad->rbroot); - else { - struct rb_node *prev_node = rb_prev(iovad->cached32_node); - struct iova *curr_iova = - rb_entry(iovad->cached32_node, struct iova, node); - *limit_pfn = curr_iova->pfn_lo; - return prev_node; - } + + curr_iova = rb_entry(cached_node, struct iova, node); + *limit_pfn = min(*limit_pfn, curr_iova->pfn_lo); + + return rb_prev(cached_node); } static void -__cached_rbnode_insert_update(struct iova_domain *iovad, - unsigned long limit_pfn, struct iova *new) +__cached_rbnode_insert_update(struct iova_domain *iovad, struct iova *new) { - if (limit_pfn != iovad->dma_32bit_pfn) - return; - iovad->cached32_node = &new->node; + if (new->pfn_hi < iovad->dma_32bit_pfn) + iovad->cached32_node = &new->node; + else + iovad->cached_node = &new->node; } static void __cached_rbnode_delete_update(struct iova_domain *iovad, struct iova *free) { struct iova *cached_iova; - struct rb_node *curr; - if (!iovad->cached32_node) - return; - curr = iovad->cached32_node; - cached_iova = rb_entry(curr, struct iova, node); + cached_iova = rb_entry(iovad->cached32_node, struct iova, node); + if (free->pfn_hi < iovad->dma_32bit_pfn && + iovad->cached32_node && free->pfn_lo >= cached_iova->pfn_lo) + iovad->cached32_node = rb_next(&free->node); - if (free->pfn_lo >= cached_iova->pfn_lo) { - struct rb_node *node = rb_next(&free->node); - struct iova *iova = rb_entry(node, struct iova, node); - - /* only cache if it's below 32bit pfn */ - if (node && iova->pfn_lo < iovad->dma_32bit_pfn) - iovad->cached32_node = node; - else - iovad->cached32_node = NULL; - } + cached_iova = rb_entry(iovad->cached_node, struct iova, node); + if (iovad->cached_node && free->pfn_lo >= cached_iova->pfn_lo) + iovad->cached_node = rb_next(&free->node); } /* Insert the iova into domain rbtree by holding writer lock */ @@ -188,7 +185,7 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad, { struct rb_node *prev, *curr = NULL; unsigned long flags; - unsigned long saved_pfn, new_pfn; + unsigned long new_pfn; unsigned long align_mask = ~0UL; if (size_aligned) @@ -196,7 +193,6 @@ static int __alloc_and_insert_iova_range(struct iova_domain *iovad, /* Walk the tree backwards */ spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); - saved_pfn = limit_pfn; curr = __get_cached_rbnode(iovad, &limit_pfn); prev = curr; while (curr) { @@ -226,7 +222,7 @@ move_left: /* If we have 'prev', it's a valid place to start the insertion. */ iova_insert_rbtree(&iovad->rbroot, new, prev); - __cached_rbnode_insert_update(iovad, saved_pfn, new); + __cached_rbnode_insert_update(iovad, new); spin_unlock_irqrestore(&iovad->iova_rbtree_lock, flags); diff --git a/include/linux/iova.h b/include/linux/iova.h index d179b9bf7814..69ea3e258ff2 100644 --- a/include/linux/iova.h +++ b/include/linux/iova.h @@ -70,7 +70,8 @@ struct iova_fq { struct iova_domain { spinlock_t iova_rbtree_lock; /* Lock to protect update of rbtree */ struct rb_root rbroot; /* iova domain rbtree root */ - struct rb_node *cached32_node; /* Save last alloced node */ + struct rb_node *cached_node; /* Save last alloced node */ + struct rb_node *cached32_node; /* Save last 32-bit alloced node */ unsigned long granule; /* pfn granularity for this domain */ unsigned long start_pfn; /* Lower limit for this domain */ unsigned long dma_32bit_pfn; -- cgit v1.2.3-59-g8ed1b From aa3ac9469c1850ed00741955b975c3a19029763a Mon Sep 17 00:00:00 2001 From: Zhen Lei Date: Thu, 21 Sep 2017 16:52:45 +0100 Subject: iommu/iova: Make dma_32bit_pfn implicit Now that the cached node optimisation can apply to all allocations, the couple of users which were playing tricks with dma_32bit_pfn in order to benefit from it can stop doing so. Conversely, there is also no need for all the other users to explicitly calculate a 'real' 32-bit PFN, when init_iova_domain() can happily do that itself from the page granularity. CC: Thierry Reding CC: Jonathan Hunter CC: David Airlie CC: Sudeep Dutt CC: Ashutosh Dixit Signed-off-by: Zhen Lei Tested-by: Ard Biesheuvel Tested-by: Zhen Lei Tested-by: Nate Watterson [rm: use iova_shift(), rewrote commit message] Signed-off-by: Robin Murphy Signed-off-by: Joerg Roedel --- drivers/gpu/drm/tegra/drm.c | 3 +-- drivers/gpu/host1x/dev.c | 3 +-- drivers/iommu/amd_iommu.c | 7 ++----- drivers/iommu/dma-iommu.c | 18 +----------------- drivers/iommu/intel-iommu.c | 11 +++-------- drivers/iommu/iova.c | 4 ++-- drivers/misc/mic/scif/scif_rma.c | 3 +-- include/linux/iova.h | 5 ++--- 8 files changed, 13 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 597d563d636a..b822e484b7e5 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -155,8 +155,7 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) order = __ffs(tegra->domain->pgsize_bitmap); init_iova_domain(&tegra->carveout.domain, 1UL << order, - carveout_start >> order, - carveout_end >> order); + carveout_start >> order); tegra->carveout.shift = iova_shift(&tegra->carveout.domain); tegra->carveout.limit = carveout_end >> tegra->carveout.shift; diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 7f22c5c37660..5267c62e8896 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -198,8 +198,7 @@ static int host1x_probe(struct platform_device *pdev) order = __ffs(host->domain->pgsize_bitmap); init_iova_domain(&host->iova, 1UL << order, - geometry->aperture_start >> order, - geometry->aperture_end >> order); + geometry->aperture_start >> order); host->iova_end = geometry->aperture_end; } diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 51f8215877f5..647ab7691aee 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -63,7 +63,6 @@ /* IO virtual address start page frame number */ #define IOVA_START_PFN (1) #define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) -#define DMA_32BIT_PFN IOVA_PFN(DMA_BIT_MASK(32)) /* Reserved IOVA ranges */ #define MSI_RANGE_START (0xfee00000) @@ -1788,8 +1787,7 @@ static struct dma_ops_domain *dma_ops_domain_alloc(void) if (!dma_dom->domain.pt_root) goto free_dma_dom; - init_iova_domain(&dma_dom->iovad, PAGE_SIZE, - IOVA_START_PFN, DMA_32BIT_PFN); + init_iova_domain(&dma_dom->iovad, PAGE_SIZE, IOVA_START_PFN); if (init_iova_flush_queue(&dma_dom->iovad, iova_domain_flush_tlb, NULL)) goto free_dma_dom; @@ -2696,8 +2694,7 @@ static int init_reserved_iova_ranges(void) struct pci_dev *pdev = NULL; struct iova *val; - init_iova_domain(&reserved_iova_ranges, PAGE_SIZE, - IOVA_START_PFN, DMA_32BIT_PFN); + init_iova_domain(&reserved_iova_ranges, PAGE_SIZE, IOVA_START_PFN); lockdep_set_class(&reserved_iova_ranges.iova_rbtree_lock, &reserved_rbtree_key); diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 9d1cebe7f6cb..191be9c80a8a 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -292,18 +292,7 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, /* ...then finally give it a kicking to make sure it fits */ base_pfn = max_t(unsigned long, base_pfn, domain->geometry.aperture_start >> order); - end_pfn = min_t(unsigned long, end_pfn, - domain->geometry.aperture_end >> order); } - /* - * PCI devices may have larger DMA masks, but still prefer allocating - * within a 32-bit mask to avoid DAC addressing. Such limitations don't - * apply to the typical platform device, so for those we may as well - * leave the cache limit at the top of their range to save an rb_last() - * traversal on every allocation. - */ - if (dev && dev_is_pci(dev)) - end_pfn &= DMA_BIT_MASK(32) >> order; /* start_pfn is always nonzero for an already-initialised domain */ if (iovad->start_pfn) { @@ -312,16 +301,11 @@ int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base, pr_warn("Incompatible range for DMA domain\n"); return -EFAULT; } - /* - * If we have devices with different DMA masks, move the free - * area cache limit down for the benefit of the smaller one. - */ - iovad->dma_32bit_pfn = min(end_pfn + 1, iovad->dma_32bit_pfn); return 0; } - init_iova_domain(iovad, 1UL << order, base_pfn, end_pfn); + init_iova_domain(iovad, 1UL << order, base_pfn); if (!dev) return 0; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 6784a05dd6b2..ebb48353dd39 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -82,8 +82,6 @@ #define IOVA_START_PFN (1) #define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) -#define DMA_32BIT_PFN IOVA_PFN(DMA_BIT_MASK(32)) -#define DMA_64BIT_PFN IOVA_PFN(DMA_BIT_MASK(64)) /* page table handling */ #define LEVEL_STRIDE (9) @@ -1878,8 +1876,7 @@ static int dmar_init_reserved_ranges(void) struct iova *iova; int i; - init_iova_domain(&reserved_iova_list, VTD_PAGE_SIZE, IOVA_START_PFN, - DMA_32BIT_PFN); + init_iova_domain(&reserved_iova_list, VTD_PAGE_SIZE, IOVA_START_PFN); lockdep_set_class(&reserved_iova_list.iova_rbtree_lock, &reserved_rbtree_key); @@ -1938,8 +1935,7 @@ static int domain_init(struct dmar_domain *domain, struct intel_iommu *iommu, unsigned long sagaw; int err; - init_iova_domain(&domain->iovad, VTD_PAGE_SIZE, IOVA_START_PFN, - DMA_32BIT_PFN); + init_iova_domain(&domain->iovad, VTD_PAGE_SIZE, IOVA_START_PFN); err = init_iova_flush_queue(&domain->iovad, iommu_flush_iova, iova_entry_free); @@ -4897,8 +4893,7 @@ static int md_domain_init(struct dmar_domain *domain, int guest_width) { int adjust_width; - init_iova_domain(&domain->iovad, VTD_PAGE_SIZE, IOVA_START_PFN, - DMA_32BIT_PFN); + init_iova_domain(&domain->iovad, VTD_PAGE_SIZE, IOVA_START_PFN); domain_reserve_special_ranges(domain); /* calculate AGAW */ diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index c6f5a22f8d20..65032e60a5d1 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -37,7 +37,7 @@ static void fq_flush_timeout(unsigned long data); void init_iova_domain(struct iova_domain *iovad, unsigned long granule, - unsigned long start_pfn, unsigned long pfn_32bit) + unsigned long start_pfn) { /* * IOVA granularity will normally be equal to the smallest @@ -52,7 +52,7 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule, iovad->cached32_node = NULL; iovad->granule = granule; iovad->start_pfn = start_pfn; - iovad->dma_32bit_pfn = pfn_32bit + 1; + iovad->dma_32bit_pfn = 1UL << (32 - iova_shift(iovad)); iovad->flush_cb = NULL; iovad->fq = NULL; init_iova_rcaches(iovad); diff --git a/drivers/misc/mic/scif/scif_rma.c b/drivers/misc/mic/scif/scif_rma.c index 329727e00e97..c824329f7012 100644 --- a/drivers/misc/mic/scif/scif_rma.c +++ b/drivers/misc/mic/scif/scif_rma.c @@ -39,8 +39,7 @@ void scif_rma_ep_init(struct scif_endpt *ep) struct scif_endpt_rma_info *rma = &ep->rma_info; mutex_init(&rma->rma_lock); - init_iova_domain(&rma->iovad, PAGE_SIZE, SCIF_IOVA_START_PFN, - SCIF_DMA_64BIT_PFN); + init_iova_domain(&rma->iovad, PAGE_SIZE, SCIF_IOVA_START_PFN); spin_lock_init(&rma->tc_lock); mutex_init(&rma->mmn_lock); INIT_LIST_HEAD(&rma->reg_list); diff --git a/include/linux/iova.h b/include/linux/iova.h index 69ea3e258ff2..953cfd20f152 100644 --- a/include/linux/iova.h +++ b/include/linux/iova.h @@ -154,7 +154,7 @@ struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo, unsigned long pfn_hi); void copy_reserved_iova(struct iova_domain *from, struct iova_domain *to); void init_iova_domain(struct iova_domain *iovad, unsigned long granule, - unsigned long start_pfn, unsigned long pfn_32bit); + unsigned long start_pfn); int init_iova_flush_queue(struct iova_domain *iovad, iova_flush_cb flush_cb, iova_entry_dtor entry_dtor); struct iova *find_iova(struct iova_domain *iovad, unsigned long pfn); @@ -230,8 +230,7 @@ static inline void copy_reserved_iova(struct iova_domain *from, static inline void init_iova_domain(struct iova_domain *iovad, unsigned long granule, - unsigned long start_pfn, - unsigned long pfn_32bit) + unsigned long start_pfn) { } -- cgit v1.2.3-59-g8ed1b From bb68b2fbfbd643d4407541f9c7a16a2c9b3a57c7 Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Thu, 21 Sep 2017 16:52:46 +0100 Subject: iommu/iova: Add rbtree anchor node Add a permanent dummy IOVA reservation to the rbtree, such that we can always access the top of the address space instantly. The immediate benefit is that we remove the overhead of the rb_last() traversal when not using the cached node, but it also paves the way for further simplifications. Signed-off-by: Robin Murphy Signed-off-by: Joerg Roedel --- drivers/iommu/iova.c | 15 +++++++++++++-- include/linux/iova.h | 1 + 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index 65032e60a5d1..9e04c1f3e740 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -24,6 +24,9 @@ #include #include +/* The anchor node sits above the top of the usable address space */ +#define IOVA_ANCHOR ~0UL + static bool iova_rcache_insert(struct iova_domain *iovad, unsigned long pfn, unsigned long size); @@ -55,6 +58,9 @@ init_iova_domain(struct iova_domain *iovad, unsigned long granule, iovad->dma_32bit_pfn = 1UL << (32 - iova_shift(iovad)); iovad->flush_cb = NULL; iovad->fq = NULL; + iovad->anchor.pfn_lo = iovad->anchor.pfn_hi = IOVA_ANCHOR; + rb_link_node(&iovad->anchor.node, NULL, &iovad->rbroot.rb_node); + rb_insert_color(&iovad->anchor.node, &iovad->rbroot); init_iova_rcaches(iovad); } EXPORT_SYMBOL_GPL(init_iova_domain); @@ -119,7 +125,7 @@ __get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn) if (!cached_node) cached_node = iovad->cached_node; if (!cached_node) - return rb_last(&iovad->rbroot); + return rb_prev(&iovad->anchor.node); curr_iova = rb_entry(cached_node, struct iova, node); *limit_pfn = min(*limit_pfn, curr_iova->pfn_lo); @@ -242,7 +248,8 @@ EXPORT_SYMBOL(alloc_iova_mem); void free_iova_mem(struct iova *iova) { - kmem_cache_free(iova_cache, iova); + if (iova->pfn_lo != IOVA_ANCHOR) + kmem_cache_free(iova_cache, iova); } EXPORT_SYMBOL(free_iova_mem); @@ -676,6 +683,10 @@ reserve_iova(struct iova_domain *iovad, struct iova *iova; unsigned int overlap = 0; + /* Don't allow nonsensical pfns */ + if (WARN_ON((pfn_hi | pfn_lo) > (ULLONG_MAX >> iova_shift(iovad)))) + return NULL; + spin_lock_irqsave(&iovad->iova_rbtree_lock, flags); for (node = rb_first(&iovad->rbroot); node; node = rb_next(node)) { if (__is_range_overlap(node, pfn_lo, pfn_hi)) { diff --git a/include/linux/iova.h b/include/linux/iova.h index 953cfd20f152..c696ee81054e 100644 --- a/include/linux/iova.h +++ b/include/linux/iova.h @@ -75,6 +75,7 @@ struct iova_domain { unsigned long granule; /* pfn granularity for this domain */ unsigned long start_pfn; /* Lower limit for this domain */ unsigned long dma_32bit_pfn; + struct iova anchor; /* rbtree lookup anchor */ struct iova_rcache rcaches[IOVA_RANGE_CACHE_MAX_SIZE]; /* IOVA range caches */ iova_flush_cb flush_cb; /* Call-Back function to flush IOMMU -- cgit v1.2.3-59-g8ed1b From f523acebbb74f3cf5840d801f2e4856c688bf14a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 26 Sep 2017 01:00:53 +0000 Subject: ASoC: add Component level pcm_new/pcm_free v2 In current ALSA SoC, Platform only has pcm_new/pcm_free feature, but it should be supported on Component level. This patch adds it. The v1 was added commit 99b04f4c4051f7 ("ASoC: add Component level pcm_new/pcm_free") but it called all "card" connected component's pcm_new/free, it was wrong. This patch calls "rtd" connected component. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 6 ++++++ sound/soc/soc-core.c | 42 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/soc-pcm.c | 29 +++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 996bdbc125a5..c2278614e5bf 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -795,6 +795,10 @@ struct snd_soc_component_driver { int (*suspend)(struct snd_soc_component *); int (*resume)(struct snd_soc_component *); + /* pcm creation and destruction */ + int (*pcm_new)(struct snd_soc_pcm_runtime *); + void (*pcm_free)(struct snd_pcm *); + /* component wide operations */ int (*set_sysclk)(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir); @@ -872,6 +876,8 @@ struct snd_soc_component { void (*remove)(struct snd_soc_component *); int (*suspend)(struct snd_soc_component *); int (*resume)(struct snd_soc_component *); + int (*pcm_new)(struct snd_soc_component *, struct snd_soc_pcm_runtime *); + void (*pcm_free)(struct snd_soc_component *, struct snd_pcm *); int (*set_sysclk)(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 1b5b51952718..faed564670cd 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3254,6 +3254,22 @@ static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm, return component->driver->stream_event(component, event); } +static int snd_soc_component_drv_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + if (component->driver->pcm_new) + return component->driver->pcm_new(rtd); + + return 0; +} + +static void snd_soc_component_drv_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + if (component->driver->pcm_free) + component->driver->pcm_free(pcm); +} + static int snd_soc_component_initialize(struct snd_soc_component *component, const struct snd_soc_component_driver *driver, struct device *dev) { @@ -3274,6 +3290,8 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, component->set_sysclk = component->driver->set_sysclk; component->set_pll = component->driver->set_pll; component->set_jack = component->driver->set_jack; + component->pcm_new = snd_soc_component_drv_pcm_new; + component->pcm_free = snd_soc_component_drv_pcm_free; dapm = snd_soc_component_get_dapm(component); dapm->dev = dev; @@ -3466,6 +3484,26 @@ static void snd_soc_platform_drv_remove(struct snd_soc_component *component) platform->driver->remove(platform); } +static int snd_soc_platform_drv_pcm_new(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + if (platform->driver->pcm_new) + return platform->driver->pcm_new(rtd); + + return 0; +} + +static void snd_soc_platform_drv_pcm_free(struct snd_soc_component *component, + struct snd_pcm *pcm) +{ + struct snd_soc_platform *platform = snd_soc_component_to_platform(component); + + if (platform->driver->pcm_free) + platform->driver->pcm_free(pcm); +} + /** * snd_soc_add_platform - Add a platform to the ASoC core * @dev: The parent device for the platform @@ -3489,6 +3527,10 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform, platform->component.probe = snd_soc_platform_drv_probe; if (platform_drv->remove) platform->component.remove = snd_soc_platform_drv_remove; + if (platform_drv->pcm_new) + platform->component.pcm_new = snd_soc_platform_drv_pcm_new; + if (platform_drv->pcm_free) + platform->component.pcm_free = snd_soc_platform_drv_pcm_free; #ifdef CONFIG_DEBUG_FS platform->component.debugfs_prefix = "platform"; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 09fed7014ed8..e5eb0cff790b 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -2633,12 +2633,18 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream) static void soc_pcm_private_free(struct snd_pcm *pcm) { struct snd_soc_pcm_runtime *rtd = pcm->private_data; - struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + /* need to sync the delayed work before releasing resources */ + + flush_delayed_work(&rtd->delayed_work); + component = rtdcom->component; - /* need to sync the delayed work before releasing resources */ - flush_delayed_work(&rtd->delayed_work); - if (platform->driver->pcm_free) - platform->driver->pcm_free(pcm); + if (component->pcm_free) + component->pcm_free(component, pcm); + } } /* create a new pcm */ @@ -2647,6 +2653,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_pcm *pcm; char new_name[64]; int ret = 0, playback = 0, capture = 0; @@ -2756,10 +2764,15 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) if (capture) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops); - if (platform->driver->pcm_new) { - ret = platform->driver->pcm_new(rtd); + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->pcm_new) + continue; + + ret = component->pcm_new(component, rtd); if (ret < 0) { - dev_err(platform->dev, + dev_err(component->dev, "ASoC: pcm constructor failed: %d\n", ret); return ret; -- cgit v1.2.3-59-g8ed1b From 7ba236ce58bd7ac8e360de81f834d9c446f7b063 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 26 Sep 2017 01:01:10 +0000 Subject: ASoC: add Component level set_bias_level In current ALSA SoC, Codec only has set_bias_level feature. Codec will be merged into Component in next generation ALSA SoC, thus current Codec specific feature need to be merged into it. This is glue patch for it. Codec driver has .idle_bias_off for dapm bias. But Component driver doesn't have it, and dapm->idle_bias_off is set as "true". To keep compatibility, this patch adds "idle_bias_on" instead of ".idle_bias_off" on Component driver. dapm->idle_bias_off will be set by inverted idle_bias_on. When we replace Codec to Component, the driver which has ".idle_bias_off = true" is just remove it, and the driver which doesn't have it will have new ".idle_bias_on = true". Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 8 ++++++++ sound/soc/soc-core.c | 13 ++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index c2278614e5bf..d776cdee30d7 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -816,10 +816,16 @@ struct snd_soc_component_driver { void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type, int subseq); int (*stream_event)(struct snd_soc_component *, int event); + int (*set_bias_level)(struct snd_soc_component *component, + enum snd_soc_bias_level level); /* probe ordering - for components with runtime dependencies */ int probe_order; int remove_order; + + /* bits */ + unsigned int idle_bias_on:1; + unsigned int suspend_bias_off:1; }; struct snd_soc_component { @@ -885,6 +891,8 @@ struct snd_soc_component { int source, unsigned int freq_in, unsigned int freq_out); int (*set_jack)(struct snd_soc_component *component, struct snd_soc_jack *jack, void *data); + int (*set_bias_level)(struct snd_soc_component *component, + enum snd_soc_bias_level level); /* machine specific init */ int (*init)(struct snd_soc_component *component); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index faed564670cd..fb3435121172 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3270,6 +3270,14 @@ static void snd_soc_component_drv_pcm_free(struct snd_soc_component *component, component->driver->pcm_free(pcm); } +static int snd_soc_component_set_bias_level(struct snd_soc_dapm_context *dapm, + enum snd_soc_bias_level level) +{ + struct snd_soc_component *component = dapm->component; + + return component->driver->set_bias_level(component, level); +} + static int snd_soc_component_initialize(struct snd_soc_component *component, const struct snd_soc_component_driver *driver, struct device *dev) { @@ -3297,11 +3305,14 @@ static int snd_soc_component_initialize(struct snd_soc_component *component, dapm->dev = dev; dapm->component = component; dapm->bias_level = SND_SOC_BIAS_OFF; - dapm->idle_bias_off = true; + dapm->idle_bias_off = !driver->idle_bias_on; + dapm->suspend_bias_off = driver->suspend_bias_off; if (driver->seq_notifier) dapm->seq_notifier = snd_soc_component_seq_notifier; if (driver->stream_event) dapm->stream_event = snd_soc_component_stream_event; + if (driver->set_bias_level) + dapm->set_bias_level = snd_soc_component_set_bias_level; INIT_LIST_HEAD(&component->dai_list); mutex_init(&component->io_mutex); -- cgit v1.2.3-59-g8ed1b From 85e482285bbbd508483cbe08de69c8fe00cdbbfe Mon Sep 17 00:00:00 2001 From: Yotam Gigi Date: Wed, 27 Sep 2017 08:23:11 +0200 Subject: fib: notifier: Add VIF add and delete event types In order for an interface to forward packets according to the kernel multicast routing table, it must be configured with a VIF index according to the mroute user API. The VIF index is then used to refer to that interface in the mroute user API, for example, to set the iif and oifs of an MFC entry. In order to allow drivers to be aware and offload multicast routes, they have to be aware of the VIF add and delete notifications. Due to the fact that a specific VIF can be deleted and re-added pointing to another netdevice, and the MFC routes that point to it will forward the matching packets to the new netdevice, a driver willing to offload MFC cache entries must be aware of the VIF add and delete events in addition to MFC routes notifications. Signed-off-by: Yotam Gigi Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/net/fib_notifier.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/net/fib_notifier.h b/include/net/fib_notifier.h index 669b9716dc7a..54cd6b839d2f 100644 --- a/include/net/fib_notifier.h +++ b/include/net/fib_notifier.h @@ -20,6 +20,8 @@ enum fib_event_type { FIB_EVENT_RULE_DEL, FIB_EVENT_NH_ADD, FIB_EVENT_NH_DEL, + FIB_EVENT_VIF_ADD, + FIB_EVENT_VIF_DEL, }; struct fib_notifier_ops { -- cgit v1.2.3-59-g8ed1b From 310ebbba3b7396b00bce08a33f1d2de2c74fa257 Mon Sep 17 00:00:00 2001 From: Yotam Gigi Date: Wed, 27 Sep 2017 08:23:12 +0200 Subject: ipmr: Add reference count to MFC entries Next commits will introduce MFC notifications through the atomic fib_notification chain, thus allowing modules to be aware of MFC entries. Due to the fact that modules may need to hold a reference to an MFC entry, add reference count to MFC entries to prevent them from being freed while these modules use them. The reference counting is done only on resolved MFC entries currently. Signed-off-by: Yotam Gigi Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/linux/mroute.h | 21 +++++++++++++++++++++ net/ipv4/ipmr.c | 8 +++++--- 2 files changed, 26 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index d7f63339ef0b..10028f208efb 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -109,6 +109,7 @@ struct mfc_cache_cmp_arg { * @wrong_if: number of wrong source interface hits * @lastuse: time of last use of the group (traffic or update) * @ttls: OIF TTL threshold array + * @refcount: reference count for this entry * @list: global entry list * @rcu: used for entry destruction */ @@ -138,6 +139,7 @@ struct mfc_cache { unsigned long wrong_if; unsigned long lastuse; unsigned char ttls[MAXVIFS]; + refcount_t refcount; } res; } mfc_un; struct list_head list; @@ -148,4 +150,23 @@ struct rtmsg; int ipmr_get_route(struct net *net, struct sk_buff *skb, __be32 saddr, __be32 daddr, struct rtmsg *rtm, u32 portid); + +#ifdef CONFIG_IP_MROUTE +void ipmr_cache_free(struct mfc_cache *mfc_cache); +#else +static inline void ipmr_cache_free(struct mfc_cache *mfc_cache) +{ +} +#endif + +static inline void ipmr_cache_put(struct mfc_cache *c) +{ + if (refcount_dec_and_test(&c->mfc_un.res.refcount)) + ipmr_cache_free(c); +} +static inline void ipmr_cache_hold(struct mfc_cache *c) +{ + refcount_inc(&c->mfc_un.res.refcount); +} + #endif diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index c9b3e6e069ae..86dc5f98c5dd 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -652,10 +652,11 @@ static void ipmr_cache_free_rcu(struct rcu_head *head) kmem_cache_free(mrt_cachep, c); } -static inline void ipmr_cache_free(struct mfc_cache *c) +void ipmr_cache_free(struct mfc_cache *c) { call_rcu(&c->rcu, ipmr_cache_free_rcu); } +EXPORT_SYMBOL(ipmr_cache_free); /* Destroy an unresolved cache entry, killing queued skbs * and reporting error to netlink readers. @@ -949,6 +950,7 @@ static struct mfc_cache *ipmr_cache_alloc(void) if (c) { c->mfc_un.res.last_assert = jiffies - MFC_ASSERT_THRESH - 1; c->mfc_un.res.minvif = MAXVIFS; + refcount_set(&c->mfc_un.res.refcount, 1); } return c; } @@ -1162,7 +1164,7 @@ static int ipmr_mfc_delete(struct mr_table *mrt, struct mfcctl *mfc, int parent) rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params); list_del_rcu(&c->list); mroute_netlink_event(mrt, c, RTM_DELROUTE); - ipmr_cache_free(c); + ipmr_cache_put(c); return 0; } @@ -1264,7 +1266,7 @@ static void mroute_clean_tables(struct mr_table *mrt, bool all) rhltable_remove(&mrt->mfc_hash, &c->mnode, ipmr_rht_params); list_del_rcu(&c->list); mroute_netlink_event(mrt, c, RTM_DELROUTE); - ipmr_cache_free(c); + ipmr_cache_put(c); } if (atomic_read(&mrt->cache_resolve_queue_len) != 0) { -- cgit v1.2.3-59-g8ed1b From 4d65b9487831170e699b2fc64a91b839d729bd78 Mon Sep 17 00:00:00 2001 From: Yotam Gigi Date: Wed, 27 Sep 2017 08:23:13 +0200 Subject: ipmr: Add FIB notification access functions Make the ipmr module register as a FIB notifier. To do that, implement both the ipmr_seq_read and ipmr_dump ops. The ipmr_seq_read op returns a sequence counter that is incremented on every notification related operation done by the ipmr. To implement that, add a sequence counter in the netns_ipv4 struct and increment it whenever a new MFC route or VIF are added or deleted. The sequence operations are protected by the RTNL lock. The ipmr_dump iterates the list of MFC routes and the list of VIF entries and sends notifications about them. The entries dump is done under RCU where the VIF dump uses the mrt_lock too, as the vif->dev field can change under RCU. Signed-off-by: Yotam Gigi Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/linux/mroute.h | 15 ++++++ include/net/netns/ipv4.h | 3 ++ net/ipv4/ipmr.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 153 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index 10028f208efb..54c5cb82ddcb 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #ifdef CONFIG_IP_MROUTE @@ -58,6 +59,14 @@ struct vif_device { int link; /* Physical interface index */ }; +struct vif_entry_notifier_info { + struct fib_notifier_info info; + struct net_device *dev; + vifi_t vif_index; + unsigned short vif_flags; + u32 tb_id; +}; + #define VIFF_STATIC 0x8000 #define VIF_EXISTS(_mrt, _idx) ((_mrt)->vif_table[_idx].dev != NULL) @@ -146,6 +155,12 @@ struct mfc_cache { struct rcu_head rcu; }; +struct mfc_entry_notifier_info { + struct fib_notifier_info info; + struct mfc_cache *mfc; + u32 tb_id; +}; + struct rtmsg; int ipmr_get_route(struct net *net, struct sk_buff *skb, __be32 saddr, __be32 daddr, diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 8387f099115e..abc84d986da4 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -163,6 +163,9 @@ struct netns_ipv4 { struct fib_notifier_ops *notifier_ops; unsigned int fib_seq; /* protected by rtnl_mutex */ + struct fib_notifier_ops *ipmr_notifier_ops; + unsigned int ipmr_seq; /* protected by rtnl_mutex */ + atomic_t rt_genid; }; #endif diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 86dc5f98c5dd..49879c338357 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -264,6 +264,16 @@ static void __net_exit ipmr_rules_exit(struct net *net) fib_rules_unregister(net->ipv4.mr_rules_ops); rtnl_unlock(); } + +static int ipmr_rules_dump(struct net *net, struct notifier_block *nb) +{ + return fib_rules_dump(net, nb, RTNL_FAMILY_IPMR); +} + +static unsigned int ipmr_rules_seq_read(struct net *net) +{ + return fib_rules_seq_read(net, RTNL_FAMILY_IPMR); +} #else #define ipmr_for_each_table(mrt, net) \ for (mrt = net->ipv4.mrt; mrt; mrt = NULL) @@ -298,6 +308,16 @@ static void __net_exit ipmr_rules_exit(struct net *net) net->ipv4.mrt = NULL; rtnl_unlock(); } + +static int ipmr_rules_dump(struct net *net, struct notifier_block *nb) +{ + return 0; +} + +static unsigned int ipmr_rules_seq_read(struct net *net) +{ + return 0; +} #endif static inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg, @@ -587,6 +607,43 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt) } #endif +static int call_ipmr_vif_entry_notifier(struct notifier_block *nb, + struct net *net, + enum fib_event_type event_type, + struct vif_device *vif, + vifi_t vif_index, u32 tb_id) +{ + struct vif_entry_notifier_info info = { + .info = { + .family = RTNL_FAMILY_IPMR, + .net = net, + }, + .dev = vif->dev, + .vif_index = vif_index, + .vif_flags = vif->flags, + .tb_id = tb_id, + }; + + return call_fib_notifier(nb, net, event_type, &info.info); +} + +static int call_ipmr_mfc_entry_notifier(struct notifier_block *nb, + struct net *net, + enum fib_event_type event_type, + struct mfc_cache *mfc, u32 tb_id) +{ + struct mfc_entry_notifier_info info = { + .info = { + .family = RTNL_FAMILY_IPMR, + .net = net, + }, + .mfc = mfc, + .tb_id = tb_id + }; + + return call_fib_notifier(nb, net, event_type, &info.info); +} + /** * vif_delete - Delete a VIF entry * @notify: Set to 1, if the caller is a notifier_call @@ -3050,14 +3107,87 @@ static const struct net_protocol pim_protocol = { }; #endif +static unsigned int ipmr_seq_read(struct net *net) +{ + ASSERT_RTNL(); + + return net->ipv4.ipmr_seq + ipmr_rules_seq_read(net); +} + +static int ipmr_dump(struct net *net, struct notifier_block *nb) +{ + struct mr_table *mrt; + int err; + + err = ipmr_rules_dump(net, nb); + if (err) + return err; + + ipmr_for_each_table(mrt, net) { + struct vif_device *v = &mrt->vif_table[0]; + struct mfc_cache *mfc; + int vifi; + + /* Notifiy on table VIF entries */ + read_lock(&mrt_lock); + for (vifi = 0; vifi < mrt->maxvif; vifi++, v++) { + if (!v->dev) + continue; + + call_ipmr_vif_entry_notifier(nb, net, FIB_EVENT_VIF_ADD, + v, vifi, mrt->id); + } + read_unlock(&mrt_lock); + + /* Notify on table MFC entries */ + list_for_each_entry_rcu(mfc, &mrt->mfc_cache_list, list) + call_ipmr_mfc_entry_notifier(nb, net, + FIB_EVENT_ENTRY_ADD, mfc, + mrt->id); + } + + return 0; +} + +static const struct fib_notifier_ops ipmr_notifier_ops_template = { + .family = RTNL_FAMILY_IPMR, + .fib_seq_read = ipmr_seq_read, + .fib_dump = ipmr_dump, + .owner = THIS_MODULE, +}; + +int __net_init ipmr_notifier_init(struct net *net) +{ + struct fib_notifier_ops *ops; + + net->ipv4.ipmr_seq = 0; + + ops = fib_notifier_ops_register(&ipmr_notifier_ops_template, net); + if (IS_ERR(ops)) + return PTR_ERR(ops); + net->ipv4.ipmr_notifier_ops = ops; + + return 0; +} + +static void __net_exit ipmr_notifier_exit(struct net *net) +{ + fib_notifier_ops_unregister(net->ipv4.ipmr_notifier_ops); + net->ipv4.ipmr_notifier_ops = NULL; +} + /* Setup for IP multicast routing */ static int __net_init ipmr_net_init(struct net *net) { int err; + err = ipmr_notifier_init(net); + if (err) + goto ipmr_notifier_fail; + err = ipmr_rules_init(net); if (err < 0) - goto fail; + goto ipmr_rules_fail; #ifdef CONFIG_PROC_FS err = -ENOMEM; @@ -3074,7 +3204,9 @@ proc_cache_fail: proc_vif_fail: ipmr_rules_exit(net); #endif -fail: +ipmr_rules_fail: + ipmr_notifier_exit(net); +ipmr_notifier_fail: return err; } @@ -3084,6 +3216,7 @@ static void __net_exit ipmr_net_exit(struct net *net) remove_proc_entry("ip_mr_cache", net->proc_net); remove_proc_entry("ip_mr_vif", net->proc_net); #endif + ipmr_notifier_exit(net); ipmr_rules_exit(net); } -- cgit v1.2.3-59-g8ed1b From c7c0bbeae9501a7e42f2fd306d6a6399aca688b6 Mon Sep 17 00:00:00 2001 From: Yotam Gigi Date: Wed, 27 Sep 2017 08:23:15 +0200 Subject: net: ipmr: Add MFC offload indication Allow drivers, registered to the fib notification chain indicate whether a multicast MFC route is offloaded or not, similarly to unicast routes. The indication of whether a route is offloaded is done using the mfc_flags field on an mfc_cache struct, and the information is sent to the userspace via the RTNetlink interface only. Currently, MFC routes are either offloaded or not, thus there is no need to add per-VIF offload indication. Signed-off-by: Yotam Gigi Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/linux/mroute.h | 2 ++ net/ipv4/ipmr.c | 3 +++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index 54c5cb82ddcb..5566580811ce 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -90,9 +90,11 @@ struct mr_table { /* mfc_flags: * MFC_STATIC - the entry was added statically (not by a routing daemon) + * MFC_OFFLOAD - the entry was offloaded to the hardware */ enum { MFC_STATIC = BIT(0), + MFC_OFFLOAD = BIT(1), }; struct mfc_cache_cmp_arg { diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index ba71bc402336..2a795d2c0502 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -2268,6 +2268,9 @@ static int __ipmr_fill_mroute(struct mr_table *mrt, struct sk_buff *skb, nla_put_u32(skb, RTA_IIF, mrt->vif_table[c->mfc_parent].dev->ifindex) < 0) return -EMSGSIZE; + if (c->mfc_flags & MFC_OFFLOAD) + rtm->rtm_flags |= RTNH_F_OFFLOAD; + if (!(mp_attr = nla_nest_start(skb, RTA_MULTIPATH))) return -EMSGSIZE; -- cgit v1.2.3-59-g8ed1b From 478e4c2f0067d57d7c17059caafab026ca32084a Mon Sep 17 00:00:00 2001 From: Yotam Gigi Date: Wed, 27 Sep 2017 08:23:16 +0200 Subject: net: mroute: Check if rule is a default rule When the ipmr starts, it adds one default FIB rule that matches all packets and sends them to the DEFAULT (multicast) FIB table. A more complex rule can be added by user to specify that for a specific interface, a packet should be look up at either an arbitrary table or according to the l3mdev of the interface. For drivers willing to offload the ipmr logic into a hardware but don't want to offload all the FIB rules functionality, provide a function that can indicate whether the FIB rule is the default multicast rule, thus only one routing table is needed. This way, a driver can register to the FIB notification chain, get notifications about FIB rules added and trigger some kind of an internal abort mechanism when a non default rule is added by the user. Signed-off-by: Yotam Gigi Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/linux/mroute.h | 7 +++++++ net/ipv4/ipmr.c | 12 ++++++++++++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index 5566580811ce..b072a84fbe1c 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,7 @@ int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *); int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg); int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg); int ip_mr_init(void); +bool ipmr_rule_default(const struct fib_rule *rule); #else static inline int ip_mroute_setsockopt(struct sock *sock, int optname, char __user *optval, unsigned int optlen) @@ -46,6 +48,11 @@ static inline int ip_mroute_opt(int opt) { return 0; } + +static inline bool ipmr_rule_default(const struct fib_rule *rule) +{ + return true; +} #endif struct vif_device { diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 2a795d2c0502..292a8e80bdfa 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -274,6 +274,12 @@ static unsigned int ipmr_rules_seq_read(struct net *net) { return fib_rules_seq_read(net, RTNL_FAMILY_IPMR); } + +bool ipmr_rule_default(const struct fib_rule *rule) +{ + return fib_rule_matchall(rule) && rule->table == RT_TABLE_DEFAULT; +} +EXPORT_SYMBOL(ipmr_rule_default); #else #define ipmr_for_each_table(mrt, net) \ for (mrt = net->ipv4.mrt; mrt; mrt = NULL) @@ -318,6 +324,12 @@ static unsigned int ipmr_rules_seq_read(struct net *net) { return 0; } + +bool ipmr_rule_default(const struct fib_rule *rule) +{ + return true; +} +EXPORT_SYMBOL(ipmr_rule_default); #endif static inline int ipmr_hash_cmp(struct rhashtable_compare_arg *arg, -- cgit v1.2.3-59-g8ed1b From 511d57dc71a22514e106f79a878e788cb22f73e3 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Wed, 30 Aug 2017 08:04:24 -0500 Subject: ipmi: Get the device id through a function This makes getting the device id consistent, and make it possible to add a function to fetch it dynamically later. Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 155 +++++++++++++++++++++++++++--------- drivers/char/ipmi/ipmi_watchdog.c | 11 ++- include/linux/ipmi.h | 6 +- 3 files changed, 130 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index d92767225b15..89b72425f9ce 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -272,6 +272,9 @@ struct bmc_device { }; #define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev) +static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, + struct ipmi_device_id *id); + /* * Various statistics for IPMI, these index stats[] in the ipmi_smi * structure. @@ -396,10 +399,6 @@ struct ipmi_smi { */ struct list_head users; - /* Information to supply to users. */ - unsigned char ipmi_version_major; - unsigned char ipmi_version_minor; - /* Used for wake ups at startup. */ wait_queue_head_t waitq; @@ -1194,12 +1193,21 @@ int ipmi_destroy_user(ipmi_user_t user) } EXPORT_SYMBOL(ipmi_destroy_user); -void ipmi_get_version(ipmi_user_t user, - unsigned char *major, - unsigned char *minor) +int ipmi_get_version(ipmi_user_t user, + unsigned char *major, + unsigned char *minor) { - *major = user->intf->ipmi_version_major; - *minor = user->intf->ipmi_version_minor; + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(user->intf, NULL, &id); + if (rv) + return rv; + + *major = ipmi_version_major(&id); + *minor = ipmi_version_minor(&id); + + return 0; } EXPORT_SYMBOL(ipmi_get_version); @@ -2072,6 +2080,16 @@ int ipmi_request_supply_msgs(ipmi_user_t user, } EXPORT_SYMBOL(ipmi_request_supply_msgs); +static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, + struct ipmi_device_id *id) +{ + if (!bmc) + bmc = intf->bmc; + + *id = bmc->id; + return 0; +} + #ifdef CONFIG_PROC_FS static int smi_ipmb_proc_show(struct seq_file *m, void *v) { @@ -2101,10 +2119,16 @@ static const struct file_operations smi_ipmb_proc_ops = { static int smi_version_proc_show(struct seq_file *m, void *v) { ipmi_smi_t intf = m->private; + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(intf, NULL, &id); + if (rv) + return rv; seq_printf(m, "%u.%u\n", - ipmi_version_major(&intf->bmc->id), - ipmi_version_minor(&intf->bmc->id)); + ipmi_version_major(&id), + ipmi_version_minor(&id)); return 0; } @@ -2287,8 +2311,14 @@ static ssize_t device_id_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id); + if (rv) + return rv; - return snprintf(buf, 10, "%u\n", bmc->id.device_id); + return snprintf(buf, 10, "%u\n", id.device_id); } static DEVICE_ATTR(device_id, S_IRUGO, device_id_show, NULL); @@ -2297,9 +2327,14 @@ static ssize_t provides_device_sdrs_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; - return snprintf(buf, 10, "%u\n", - (bmc->id.device_revision & 0x80) >> 7); + rv = bmc_get_device_id(NULL, bmc, &id); + if (rv) + return rv; + + return snprintf(buf, 10, "%u\n", (id.device_revision & 0x80) >> 7); } static DEVICE_ATTR(provides_device_sdrs, S_IRUGO, provides_device_sdrs_show, NULL); @@ -2308,9 +2343,14 @@ static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; - return snprintf(buf, 20, "%u\n", - bmc->id.device_revision & 0x0F); + rv = bmc_get_device_id(NULL, bmc, &id); + if (rv) + return rv; + + return snprintf(buf, 20, "%u\n", id.device_revision & 0x0F); } static DEVICE_ATTR(revision, S_IRUGO, revision_show, NULL); @@ -2319,9 +2359,15 @@ static ssize_t firmware_revision_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; - return snprintf(buf, 20, "%u.%x\n", bmc->id.firmware_revision_1, - bmc->id.firmware_revision_2); + rv = bmc_get_device_id(NULL, bmc, &id); + if (rv) + return rv; + + return snprintf(buf, 20, "%u.%x\n", id.firmware_revision_1, + id.firmware_revision_2); } static DEVICE_ATTR(firmware_revision, S_IRUGO, firmware_revision_show, NULL); @@ -2330,10 +2376,16 @@ static ssize_t ipmi_version_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id); + if (rv) + return rv; return snprintf(buf, 20, "%u.%u\n", - ipmi_version_major(&bmc->id), - ipmi_version_minor(&bmc->id)); + ipmi_version_major(&id), + ipmi_version_minor(&id)); } static DEVICE_ATTR(ipmi_version, S_IRUGO, ipmi_version_show, NULL); @@ -2342,9 +2394,14 @@ static ssize_t add_dev_support_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; - return snprintf(buf, 10, "0x%02x\n", - bmc->id.additional_device_support); + rv = bmc_get_device_id(NULL, bmc, &id); + if (rv) + return rv; + + return snprintf(buf, 10, "0x%02x\n", id.additional_device_support); } static DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show, NULL); @@ -2354,8 +2411,14 @@ static ssize_t manufacturer_id_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id); + if (rv) + return rv; - return snprintf(buf, 20, "0x%6.6x\n", bmc->id.manufacturer_id); + return snprintf(buf, 20, "0x%6.6x\n", id.manufacturer_id); } static DEVICE_ATTR(manufacturer_id, S_IRUGO, manufacturer_id_show, NULL); @@ -2364,8 +2427,14 @@ static ssize_t product_id_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id); + if (rv) + return rv; - return snprintf(buf, 10, "0x%4.4x\n", bmc->id.product_id); + return snprintf(buf, 10, "0x%4.4x\n", id.product_id); } static DEVICE_ATTR(product_id, S_IRUGO, product_id_show, NULL); @@ -2374,12 +2443,18 @@ static ssize_t aux_firmware_rev_show(struct device *dev, char *buf) { struct bmc_device *bmc = to_bmc_device(dev); + struct ipmi_device_id id; + int rv; + + rv = bmc_get_device_id(NULL, bmc, &id); + if (rv) + return rv; return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n", - bmc->id.aux_firmware_revision[3], - bmc->id.aux_firmware_revision[2], - bmc->id.aux_firmware_revision[1], - bmc->id.aux_firmware_revision[0]); + id.aux_firmware_revision[3], + id.aux_firmware_revision[2], + id.aux_firmware_revision[1], + id.aux_firmware_revision[0]); } static DEVICE_ATTR(aux_firmware_revision, S_IRUGO, aux_firmware_rev_show, NULL); @@ -2417,9 +2492,13 @@ static umode_t bmc_dev_attr_is_visible(struct kobject *kobj, struct device *dev = kobj_to_dev(kobj); struct bmc_device *bmc = to_bmc_device(dev); umode_t mode = attr->mode; + struct ipmi_device_id id; + int rv; - if (attr == &dev_attr_aux_firmware_revision.attr) - return bmc->id.aux_firmware_revision_set ? mode : 0; + if (attr == &dev_attr_aux_firmware_revision.attr) { + rv = bmc_get_device_id(NULL, bmc, &id); + return (!rv && id.aux_firmware_revision_set) ? mode : 0; + } if (attr == &dev_attr_guid.attr) return bmc->guid_set ? mode : 0; return mode; @@ -2884,6 +2963,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, ipmi_smi_t intf; ipmi_smi_t tintf; struct list_head *link; + struct ipmi_device_id id; /* * Make sure the driver is actually initialized, this handles @@ -2905,9 +2985,6 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, if (!intf) return -ENOMEM; - intf->ipmi_version_major = ipmi_version_major(device_id); - intf->ipmi_version_minor = ipmi_version_minor(device_id); - intf->bmc = kzalloc(sizeof(*intf->bmc), GFP_KERNEL); if (!intf->bmc) { kfree(intf); @@ -2982,13 +3059,19 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, get_guid(intf); + rv = bmc_get_device_id(intf, NULL, &id); + if (rv) { + dev_err(si_dev, "Unable to get the device id: %d\n", rv); + goto out; + } + rv = ipmi_bmc_register(intf, i); if (rv) goto out; - if ((intf->ipmi_version_major > 1) - || ((intf->ipmi_version_major == 1) - && (intf->ipmi_version_minor >= 5))) { + if (ipmi_version_major(&id) > 1 + || (ipmi_version_major(&id) == 1 + && ipmi_version_minor(&id) >= 5)) { /* * Start scanning the channels to see what is * available. diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c index 3d832d0362a4..76b270678b50 100644 --- a/drivers/char/ipmi/ipmi_watchdog.c +++ b/drivers/char/ipmi/ipmi_watchdog.c @@ -1009,9 +1009,14 @@ static void ipmi_register_watchdog(int ipmi_intf) goto out; } - ipmi_get_version(watchdog_user, - &ipmi_version_major, - &ipmi_version_minor); + rv = ipmi_get_version(watchdog_user, + &ipmi_version_major, + &ipmi_version_minor); + if (rv) { + pr_warn(PFX "Unable to get IPMI version, assuming 1.0\n"); + ipmi_version_major = 1; + ipmi_version_minor = 0; + } rv = misc_register(&ipmi_wdog_miscdev); if (rv < 0) { diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index f1045b2c6a00..80fc3f798984 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h @@ -113,9 +113,9 @@ int ipmi_create_user(unsigned int if_num, int ipmi_destroy_user(ipmi_user_t user); /* Get the IPMI version of the BMC we are talking to. */ -void ipmi_get_version(ipmi_user_t user, - unsigned char *major, - unsigned char *minor); +int ipmi_get_version(ipmi_user_t user, + unsigned char *major, + unsigned char *minor); /* Set and get the slave address and LUN that we will use for our source messages. Note that this affects the interface, not just -- cgit v1.2.3-59-g8ed1b From c468f911b73beb39b20f7e5f97a35d41f038b31b Mon Sep 17 00:00:00 2001 From: Jeremy Kerr Date: Fri, 25 Aug 2017 15:47:23 +0800 Subject: ipmi: Make ipmi_demangle_device_id more generic Currently, ipmi_demagle_device_id requires a full response buffer in its data argument. This means we can't use it to parse a response in a struct ipmi_recv_msg, which has the netfn and cmd as separate bytes. This change alters the definition and users of ipmi_demangle_device_id to use a split netfn, cmd and data buffer, so it can be used with non-sequential responses. Signed-off-by: Jeremy Kerr Fixed the ipmi_ssif.c and ipmi_si_intf.c changes to use data from the response, not the data from the message, when passing info to the ipmi_demangle_device_id() function. Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_si_intf.c | 3 ++- drivers/char/ipmi/ipmi_ssif.c | 3 ++- include/linux/ipmi_smi.h | 24 ++++++++++++------------ 3 files changed, 16 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index f8e28bad6d56..bc99369fca49 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2926,7 +2926,8 @@ static int try_get_dev_id(struct smi_info *smi_info) resp, IPMI_MAX_MSG_LENGTH); /* Check and record info from the get device id, in case we need it. */ - rv = ipmi_demangle_device_id(resp, resp_len, &smi_info->device_id); + rv = ipmi_demangle_device_id(resp[0] >> 2, resp[1], + resp + 2, resp_len - 2, &smi_info->device_id); out: kfree(resp); diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 0aea3bcb6158..20ab098cd661 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -1491,7 +1491,8 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) if (rv) goto out; - rv = ipmi_demangle_device_id(resp, len, &ssif_info->device_id); + rv = ipmi_demangle_device_id(resp[0] >> 2, resp[1], + resp + 2, len - 2, &ssif_info->device_id); if (rv) goto out; diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index f8cea14485dd..75542c837c07 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -162,27 +162,27 @@ struct ipmi_device_id { #define ipmi_version_major(v) ((v)->ipmi_version & 0xf) #define ipmi_version_minor(v) ((v)->ipmi_version >> 4) -/* Take a pointer to a raw data buffer and a length and extract device - id information from it. The first byte of data must point to the - netfn << 2, the data should be of the format: - netfn << 2, cmd, completion code, data - as normally comes from a device interface. */ -static inline int ipmi_demangle_device_id(const unsigned char *data, +/* Take a pointer to an IPMI response and extract device id information from + * it. @netfn is in the IPMI_NETFN_ format, so may need to be shifted from + * a SI response. + */ +static inline int ipmi_demangle_device_id(uint8_t netfn, uint8_t cmd, + const unsigned char *data, unsigned int data_len, struct ipmi_device_id *id) { - if (data_len < 9) + if (data_len < 7) return -EINVAL; - if (data[0] != IPMI_NETFN_APP_RESPONSE << 2 || - data[1] != IPMI_GET_DEVICE_ID_CMD) + if (netfn != IPMI_NETFN_APP_RESPONSE || cmd != IPMI_GET_DEVICE_ID_CMD) /* Strange, didn't get the response we expected. */ return -EINVAL; - if (data[2] != 0) + if (data[0] != 0) /* That's odd, it shouldn't be able to fail. */ return -EINVAL; - data += 3; - data_len -= 3; + data++; + data_len--; + id->device_id = data[0]; id->device_revision = data[1]; id->firmware_revision_1 = data[2]; -- cgit v1.2.3-59-g8ed1b From 1e5058ea21010883b1e1d288637f7390bb8d1c61 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 31 Aug 2017 16:45:40 -0500 Subject: ipmi: Remove the device id from ipmi_register_smi() It's no longer used, dynamic device id handling is in place now. Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_msghandler.c | 1 - drivers/char/ipmi/ipmi_powernv.c | 4 +--- drivers/char/ipmi/ipmi_si_intf.c | 1 - drivers/char/ipmi/ipmi_ssif.c | 19 ------------------- include/linux/ipmi_smi.h | 1 - 5 files changed, 1 insertion(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 9157a9e17c36..ee108be13348 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -3112,7 +3112,6 @@ EXPORT_SYMBOL(ipmi_poll_interface); int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, void *send_info, - struct ipmi_device_id *device_id, struct device *si_dev, unsigned char slave_addr) { diff --git a/drivers/char/ipmi/ipmi_powernv.c b/drivers/char/ipmi/ipmi_powernv.c index b338a4becbf8..07fddbefefe4 100644 --- a/drivers/char/ipmi/ipmi_powernv.c +++ b/drivers/char/ipmi/ipmi_powernv.c @@ -23,7 +23,6 @@ struct ipmi_smi_powernv { u64 interface_id; - struct ipmi_device_id ipmi_id; ipmi_smi_t intf; unsigned int irq; @@ -266,8 +265,7 @@ static int ipmi_powernv_probe(struct platform_device *pdev) } /* todo: query actual ipmi_device_id */ - rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi, - &ipmi->ipmi_id, dev, 0); + rc = ipmi_register_smi(&ipmi_powernv_smi_handlers, ipmi, dev, 0); if (rc) { dev_warn(dev, "IPMI SMI registration failed (%d)\n", rc); goto err_free_msg; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index bc99369fca49..4caa793a6765 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -3631,7 +3631,6 @@ static int try_smi_init(struct smi_info *new_smi) rv = ipmi_register_smi(&handlers, new_smi, - &new_smi->device_id, new_smi->dev, new_smi->slave_addr); if (rv) { diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 20ab098cd661..dd716d06ce73 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -267,9 +267,6 @@ struct ssif_info { unsigned char *i2c_data; unsigned int i2c_size; - /* From the device id response. */ - struct ipmi_device_id device_id; - struct timer_list retry_timer; int retries_left; @@ -1481,21 +1478,6 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) ipmi_addr_src_to_str(ssif_info->addr_source), client->addr, client->adapter->name, slave_addr); - /* - * Do a Get Device ID command, since it comes back with some - * useful info. - */ - msg[0] = IPMI_NETFN_APP_REQUEST << 2; - msg[1] = IPMI_GET_DEVICE_ID_CMD; - rv = do_cmd(client, 2, msg, &len, resp); - if (rv) - goto out; - - rv = ipmi_demangle_device_id(resp[0] >> 2, resp[1], - resp + 2, len - 2, &ssif_info->device_id); - if (rv) - goto out; - ssif_info->client = client; i2c_set_clientdata(client, ssif_info); @@ -1685,7 +1667,6 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) rv = ipmi_register_smi(&ssif_info->handlers, ssif_info, - &ssif_info->device_id, &ssif_info->client->dev, slave_addr); if (rv) { diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index 75542c837c07..97771e36b7f0 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -214,7 +214,6 @@ static inline int ipmi_demangle_device_id(uint8_t netfn, uint8_t cmd, call. */ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, void *send_info, - struct ipmi_device_id *device_id, struct device *dev, unsigned char slave_addr); -- cgit v1.2.3-59-g8ed1b From da61e7308df868c20a41861c0a11f6eefd822993 Mon Sep 17 00:00:00 2001 From: Corentin Labbe Date: Mon, 18 Sep 2017 20:10:36 +0200 Subject: sbus: char: Move D7S_MINOR to include/linux/miscdevice.h This patch move the define for D7S_MINOR to include/linux/miscdevice.h. It's better that all minor number are in the same place. Signed-off-by: Corentin Labbe Signed-off-by: David S. Miller --- drivers/sbus/char/display7seg.c | 1 - include/linux/miscdevice.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c index f32765d3cbd8..5c8ed7350a04 100644 --- a/drivers/sbus/char/display7seg.c +++ b/drivers/sbus/char/display7seg.c @@ -22,7 +22,6 @@ #include -#define D7S_MINOR 193 #define DRIVER_NAME "d7s" #define PFX DRIVER_NAME ": " diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 58751eae5f77..05c3892b3e92 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -35,6 +35,7 @@ #define HWRNG_MINOR 183 #define MICROCODE_MINOR 184 #define IRNET_MINOR 187 +#define D7S_MINOR 193 #define VFIO_MINOR 196 #define TUN_MINOR 200 #define CUSE_MINOR 203 -- cgit v1.2.3-59-g8ed1b From ae59e633b52cccf5761ac3012378fec2480c49aa Mon Sep 17 00:00:00 2001 From: vathsala nagaraju Date: Tue, 26 Sep 2017 15:29:12 +0530 Subject: drm/dp: Add defines for latency in sink Add defines for dpcd register 2009 (synchronization latency in sink). v2: - add spec version (Daniel) - use register name as is in spec,only drop excess from end (jani) - add the full register contents (jani) [Rodrigo fixed spec version when merging] Cc: Rodrigo Vivi CC: Puthikorn Voravootivat Reviewed-by: Rodrigo Vivi Signed-off-by: Vathsala Nagaraju Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/1506419953-32605-1-git-send-email-vathsala.nagaraju@intel.com Signed-off-by: Rodrigo Vivi --- include/drm/drm_dp_helper.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 11c39f15f1b3..8b9ac321c3bd 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -735,6 +735,12 @@ # define DP_PSR_SINK_INTERNAL_ERROR 7 # define DP_PSR_SINK_STATE_MASK 0x07 +#define DP_SYNCHRONIZATION_LATENCY_IN_SINK 0x2009 /* edp 1.4 */ +# define DP_MAX_RESYNC_FRAME_COUNT_MASK (0xf << 0) +# define DP_MAX_RESYNC_FRAME_COUNT_SHIFT 0 +# define DP_LAST_ACTUAL_SYNCHRONIZATION_LATENCY_MASK (0xf << 4) +# define DP_LAST_ACTUAL_SYNCHRONIZATION_LATENCY_SHIFT 4 + #define DP_RECEIVER_ALPM_STATUS 0x200b /* eDP 1.4 */ # define DP_ALPM_LOCK_TIMEOUT_ERROR (1 << 0) -- cgit v1.2.3-59-g8ed1b From 55f91cb6f1dfc873359674f35a8ffb1e78429d22 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Sat, 16 Sep 2017 15:51:25 -0500 Subject: ipmi: Make the IPMI proc interface configurable So we can remove it later. Signed-off-by: Corey Minyard --- drivers/char/ipmi/Kconfig | 8 +++++++ drivers/char/ipmi/ipmi_msghandler.c | 43 +++++++++++++++++++------------------ drivers/char/ipmi/ipmi_si_intf.c | 4 ++++ drivers/char/ipmi/ipmi_ssif.c | 6 ++++++ include/linux/ipmi_smi.h | 2 ++ 5 files changed, 42 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig index 152ccefdaecb..3544abc0f9f9 100644 --- a/drivers/char/ipmi/Kconfig +++ b/drivers/char/ipmi/Kconfig @@ -22,6 +22,14 @@ config IPMI_DMI_DECODE if IPMI_HANDLER +config IPMI_PROC_INTERFACE + bool 'Provide an interface for IPMI stats in /proc (deprecated)' + depends on PROC_FS + default y + help + Do not use this any more, use sysfs for this info. It will be + removed in future kernel versions. + config IPMI_PANIC_EVENT bool 'Generate a panic event to all BMCs on a panic' help diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 1c8bef2e1dc1..fd3ac6b50412 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -132,9 +132,9 @@ module_param_cb(panic_op, &panic_op_ops, NULL, 0600); MODULE_PARM_DESC(panic_op, "Sets if the IPMI driver will attempt to store panic information in the event log in the event of a panic. Set to 'none' for no, 'event' for a single event, or 'string' for a generic event and the panic string in IPMI OEM events."); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE static struct proc_dir_entry *proc_ipmi_root; -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_IPMI_PROC_INTERFACE */ /* Remain in auto-maintenance mode for this amount of time (in ms). */ #define IPMI_MAINTENANCE_MODE_TIMEOUT 30000 @@ -267,7 +267,7 @@ struct ipmi_my_addrinfo { unsigned char lun; }; -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE struct ipmi_proc_entry { char *name; struct ipmi_proc_entry *next; @@ -449,10 +449,13 @@ struct ipmi_smi { const struct ipmi_smi_handlers *handlers; void *send_info; -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE /* A list of proc entries for this interface. */ struct mutex proc_entry_lock; struct ipmi_proc_entry *proc_entries; + + struct proc_dir_entry *proc_dir; + char proc_dir_name[10]; #endif /* Driver-model device for the system interface. */ @@ -542,10 +545,6 @@ struct ipmi_smi { struct ipmi_my_addrinfo addrinfo[IPMI_MAX_CHANNELS]; bool channels_ready; - /* Proc FS stuff. */ - struct proc_dir_entry *proc_dir; - char proc_dir_name[10]; - atomic_t stats[IPMI_NUM_STATS]; /* @@ -2363,7 +2362,7 @@ static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc, return __bmc_get_device_id(intf, bmc, id, guid_set, guid, -1); } -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE static int smi_ipmb_proc_show(struct seq_file *m, void *v) { ipmi_smi_t intf = m->private; @@ -2492,14 +2491,12 @@ static const struct file_operations smi_stats_proc_ops = { .llseek = seq_lseek, .release = single_release, }; -#endif /* CONFIG_PROC_FS */ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, const struct file_operations *proc_ops, void *data) { int rv = 0; -#ifdef CONFIG_PROC_FS struct proc_dir_entry *file; struct ipmi_proc_entry *entry; @@ -2525,7 +2522,6 @@ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, smi->proc_entries = entry; mutex_unlock(&smi->proc_entry_lock); } -#endif /* CONFIG_PROC_FS */ return rv; } @@ -2535,7 +2531,6 @@ static int add_proc_entries(ipmi_smi_t smi, int num) { int rv = 0; -#ifdef CONFIG_PROC_FS sprintf(smi->proc_dir_name, "%d", num); smi->proc_dir = proc_mkdir(smi->proc_dir_name, proc_ipmi_root); if (!smi->proc_dir) @@ -2555,14 +2550,12 @@ static int add_proc_entries(ipmi_smi_t smi, int num) rv = ipmi_smi_add_proc_entry(smi, "version", &smi_version_proc_ops, smi); -#endif /* CONFIG_PROC_FS */ return rv; } static void remove_proc_entries(ipmi_smi_t smi) { -#ifdef CONFIG_PROC_FS struct ipmi_proc_entry *entry; mutex_lock(&smi->proc_entry_lock); @@ -2576,8 +2569,8 @@ static void remove_proc_entries(ipmi_smi_t smi) } mutex_unlock(&smi->proc_entry_lock); remove_proc_entry(smi->proc_dir_name, proc_ipmi_root); -#endif /* CONFIG_PROC_FS */ } +#endif /* CONFIG_IPMI_PROC_INTERFACE */ static ssize_t device_id_show(struct device *dev, struct device_attribute *attr, @@ -3419,7 +3412,7 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, intf->seq_table[j].seqid = 0; } intf->curr_seq = 0; -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE mutex_init(&intf->proc_entry_lock); #endif spin_lock_init(&intf->waiting_rcv_msgs_lock); @@ -3443,7 +3436,9 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, for (i = 0; i < IPMI_NUM_STATS; i++) atomic_set(&intf->stats[i], 0); +#ifdef CONFIG_IPMI_PROC_INTERFACE intf->proc_dir = NULL; +#endif mutex_lock(&smi_watchers_mutex); mutex_lock(&ipmi_interfaces_mutex); @@ -3479,13 +3474,17 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers, if (rv) goto out; +#ifdef CONFIG_IPMI_PROC_INTERFACE rv = add_proc_entries(intf, i); +#endif out: if (rv) { ipmi_bmc_unregister(intf); +#ifdef CONFIG_IPMI_PROC_INTERFACE if (intf->proc_dir) remove_proc_entries(intf); +#endif intf->handlers = NULL; list_del_rcu(&intf->link); mutex_unlock(&ipmi_interfaces_mutex); @@ -3590,7 +3589,9 @@ int ipmi_unregister_smi(ipmi_smi_t intf) intf->handlers = NULL; mutex_unlock(&ipmi_interfaces_mutex); +#ifdef CONFIG_IPMI_PROC_INTERFACE remove_proc_entries(intf); +#endif ipmi_bmc_unregister(intf); /* @@ -5170,7 +5171,7 @@ static int ipmi_init_msghandler(void) printk(KERN_INFO "ipmi message handler version " IPMI_DRIVER_VERSION "\n"); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE proc_ipmi_root = proc_mkdir("ipmi", NULL); if (!proc_ipmi_root) { printk(KERN_ERR PFX "Unable to create IPMI proc dir"); @@ -5178,7 +5179,7 @@ static int ipmi_init_msghandler(void) return -ENOMEM; } -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_IPMI_PROC_INTERFACE */ setup_timer(&ipmi_timer, ipmi_timeout, 0); mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES); @@ -5218,9 +5219,9 @@ static void __exit cleanup_ipmi(void) atomic_inc(&stop_operation); del_timer_sync(&ipmi_timer); -#ifdef CONFIG_PROC_FS +#ifdef CONFIG_IPMI_PROC_INTERFACE proc_remove(proc_ipmi_root); -#endif /* CONFIG_PROC_FS */ +#endif /* CONFIG_IPMI_PROC_INTERFACE */ driver_unregister(&ipmidriver.driver); diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 6ce5b7c8cf93..efc8ee9b5071 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1605,6 +1605,7 @@ out: return rv; } +#ifdef CONFIG_IPMI_PROC_INTERFACE static int smi_type_proc_show(struct seq_file *m, void *v) { struct smi_info *smi = m->private; @@ -1698,6 +1699,7 @@ static const struct file_operations smi_params_proc_ops = { .llseek = seq_lseek, .release = single_release, }; +#endif #define IPMI_SI_ATTR(name) \ static ssize_t ipmi_##name##_show(struct device *dev, \ @@ -2191,6 +2193,7 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err_remove_attrs; } +#ifdef CONFIG_IPMI_PROC_INTERFACE rv = ipmi_smi_add_proc_entry(new_smi->intf, "type", &smi_type_proc_ops, new_smi); @@ -2217,6 +2220,7 @@ static int try_smi_init(struct smi_info *new_smi) "Unable to create proc entry: %d\n", rv); goto out_err_stop_timer; } +#endif /* Don't increment till we know we have succeeded. */ smi_num++; diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index 7b2d4000b11e..aadec879d052 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -1344,6 +1344,7 @@ static int ssif_detect(struct i2c_client *client, struct i2c_board_info *info) return rv; } +#ifdef CONFIG_IPMI_PROC_INTERFACE static int smi_type_proc_show(struct seq_file *m, void *v) { seq_puts(m, "ssif\n"); @@ -1407,6 +1408,7 @@ static const struct file_operations smi_stats_proc_ops = { .llseek = seq_lseek, .release = single_release, }; +#endif static int strcmp_nospace(char *s1, char *s2) { @@ -1742,6 +1744,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) goto out_remove_attr; } +#ifdef CONFIG_IPMI_PROC_INTERFACE rv = ipmi_smi_add_proc_entry(ssif_info->intf, "type", &smi_type_proc_ops, ssif_info); @@ -1757,6 +1760,7 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) pr_err(PFX "Unable to create proc entry: %d\n", rv); goto out_err_unreg; } +#endif out: if (rv) { @@ -1775,8 +1779,10 @@ static int ssif_probe(struct i2c_client *client, const struct i2c_device_id *id) kfree(resp); return rv; +#ifdef CONFIG_IPMI_PROC_INTERFACE out_err_unreg: ipmi_unregister_smi(ssif_info->intf); +#endif out_remove_attr: device_remove_group(&ssif_info->client->dev, &ipmi_ssif_dev_attr_group); diff --git a/include/linux/ipmi_smi.h b/include/linux/ipmi_smi.h index 97771e36b7f0..5be51281e14d 100644 --- a/include/linux/ipmi_smi.h +++ b/include/linux/ipmi_smi.h @@ -241,11 +241,13 @@ static inline void ipmi_free_smi_msg(struct ipmi_smi_msg *msg) msg->done(msg); } +#ifdef CONFIG_IPMI_PROC_INTERFACE /* Allow the lower layer to add things to the proc filesystem directory for this interface. Note that the entry will automatically be dstroyed when the interface is destroyed. */ int ipmi_smi_add_proc_entry(ipmi_smi_t smi, char *name, const struct file_operations *proc_ops, void *data); +#endif #endif /* __LINUX_IPMI_SMI_H */ -- cgit v1.2.3-59-g8ed1b From 95e300c052fd9dbb05f289a912c138ed03320ec5 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Mon, 18 Sep 2017 12:38:17 -0500 Subject: ipmi: Make the DMI probe into a generic platform probe Rework the DMI probe function to be a generic platform probe, and then rework the DMI code (and a few other things) to use the more generic information. This is so other things can declare platform IPMI devices. Signed-off-by: Corey Minyard --- drivers/char/ipmi/ipmi_dmi.c | 70 ++++++++++++++++-------- drivers/char/ipmi/ipmi_dmi.h | 8 +-- drivers/char/ipmi/ipmi_msghandler.c | 2 +- drivers/char/ipmi/ipmi_si_intf.c | 2 +- drivers/char/ipmi/ipmi_si_platform.c | 101 ++++++++++++++++------------------- drivers/char/ipmi/ipmi_si_sm.h | 2 +- drivers/char/ipmi/ipmi_ssif.c | 12 ++--- include/linux/ipmi.h | 2 +- 8 files changed, 104 insertions(+), 95 deletions(-) (limited to 'include') diff --git a/drivers/char/ipmi/ipmi_dmi.c b/drivers/char/ipmi/ipmi_dmi.c index 2a84401dea05..d08d41903b0f 100644 --- a/drivers/char/ipmi/ipmi_dmi.c +++ b/drivers/char/ipmi/ipmi_dmi.c @@ -8,10 +8,16 @@ #include #include #include +#include "ipmi_si_sm.h" #include "ipmi_dmi.h" +#define IPMI_DMI_TYPE_KCS 0x01 +#define IPMI_DMI_TYPE_SMIC 0x02 +#define IPMI_DMI_TYPE_BT 0x03 +#define IPMI_DMI_TYPE_SSIF 0x04 + struct ipmi_dmi_info { - int type; + enum si_type si_type; u32 flags; unsigned long addr; u8 slave_addr; @@ -22,6 +28,15 @@ static struct ipmi_dmi_info *ipmi_dmi_infos; static int ipmi_dmi_nr __initdata; +#define set_prop_entry(_p_, _name_, type, val) \ +do { \ + struct property_entry *_p = &_p_; \ + _p->name = _name_; \ + _p->length = sizeof(type); \ + _p->is_string = false; \ + _p->value.type##_data = val; \ +} while(0) + static void __init dmi_add_platform_ipmi(unsigned long base_addr, u32 flags, u8 slave_addr, @@ -32,27 +47,14 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, struct platform_device *pdev; struct resource r[4]; unsigned int num_r = 1, size; - struct property_entry p[4] = { - PROPERTY_ENTRY_U8("slave-addr", slave_addr), - PROPERTY_ENTRY_U8("ipmi-type", type), - PROPERTY_ENTRY_U16("i2c-addr", base_addr), - { } - }; + struct property_entry p[5]; + unsigned int pidx = 0; char *name, *override; int rv; + enum si_type si_type; struct ipmi_dmi_info *info; - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - pr_warn("ipmi:dmi: Could not allocate dmi info\n"); - } else { - info->type = type; - info->flags = flags; - info->addr = base_addr; - info->slave_addr = slave_addr; - info->next = ipmi_dmi_infos; - ipmi_dmi_infos = info; - } + memset(p, 0, sizeof(p)); name = "dmi-ipmi-si"; override = "ipmi_si"; @@ -62,19 +64,42 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, override = "ipmi_ssif"; offset = 1; size = 1; + si_type = SI_TYPE_INVALID; break; case IPMI_DMI_TYPE_BT: size = 3; + si_type = SI_BT; break; case IPMI_DMI_TYPE_KCS: + size = 2; + si_type = SI_KCS; + break; case IPMI_DMI_TYPE_SMIC: size = 2; + si_type = SI_SMIC; break; default: pr_err("ipmi:dmi: Invalid IPMI type: %d", type); return; } + if (si_type != SI_TYPE_INVALID) + set_prop_entry(p[pidx++], "ipmi-type", u8, si_type); + set_prop_entry(p[pidx++], "slave-addr", u8, slave_addr); + set_prop_entry(p[pidx++], "addr-source", u8, SI_SMBIOS); + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + pr_warn("ipmi:dmi: Could not allocate dmi info\n"); + } else { + info->si_type = si_type; + info->flags = flags; + info->addr = base_addr; + info->slave_addr = slave_addr; + info->next = ipmi_dmi_infos; + ipmi_dmi_infos = info; + } + pdev = platform_device_alloc(name, ipmi_dmi_nr); if (!pdev) { pr_err("ipmi:dmi: Error allocation IPMI platform device"); @@ -82,8 +107,10 @@ static void __init dmi_add_platform_ipmi(unsigned long base_addr, } pdev->driver_override = override; - if (type == IPMI_DMI_TYPE_SSIF) + if (type == IPMI_DMI_TYPE_SSIF) { + set_prop_entry(p[pidx++], "i2c-addr", u16, base_addr); goto add_properties; + } memset(r, 0, sizeof(r)); @@ -151,12 +178,13 @@ err: * This function allows an ACPI-specified IPMI device to look up the * slave address from the DMI table. */ -int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr) +int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags, + unsigned long base_addr) { struct ipmi_dmi_info *info = ipmi_dmi_infos; while (info) { - if (info->type == type && + if (info->si_type == si_type && info->flags == flags && info->addr == base_addr) return info->slave_addr; diff --git a/drivers/char/ipmi/ipmi_dmi.h b/drivers/char/ipmi/ipmi_dmi.h index 0a1afe5ceb1e..062015b8f520 100644 --- a/drivers/char/ipmi/ipmi_dmi.h +++ b/drivers/char/ipmi/ipmi_dmi.h @@ -2,11 +2,7 @@ * DMI defines for use by IPMI */ -#define IPMI_DMI_TYPE_KCS 0x01 -#define IPMI_DMI_TYPE_SMIC 0x02 -#define IPMI_DMI_TYPE_BT 0x03 -#define IPMI_DMI_TYPE_SSIF 0x04 - #ifdef CONFIG_IPMI_DMI_DECODE -int ipmi_dmi_get_slave_addr(int type, u32 flags, unsigned long base_addr); +int ipmi_dmi_get_slave_addr(enum si_type si_type, u32 flags, + unsigned long base_addr); #endif diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index fd3ac6b50412..9d1eaf70f406 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -594,7 +594,7 @@ static DEFINE_MUTEX(smi_watchers_mutex); static const char * const addr_src_to_str[] = { "invalid", "hotmod", "hardcoded", "SPMI", "ACPI", "SMBIOS", "PCI", - "device-tree" + "device-tree", "platform" }; const char *ipmi_addr_src_to_str(enum ipmi_addr_src src) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index efc8ee9b5071..55e0c42bee4d 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -89,7 +89,7 @@ enum si_intf_state { #define IPMI_BT_INTMASK_CLEAR_IRQ_BIT 2 #define IPMI_BT_INTMASK_ENABLE_IRQ_BIT 1 -static const char * const si_to_str[] = { "kcs", "smic", "bt" }; +static const char * const si_to_str[] = { "invalid", "kcs", "smic", "bt" }; static int initialized; diff --git a/drivers/char/ipmi/ipmi_si_platform.c b/drivers/char/ipmi/ipmi_si_platform.c index cf5c3e5e72e2..9573f1116450 100644 --- a/drivers/char/ipmi/ipmi_si_platform.c +++ b/drivers/char/ipmi/ipmi_si_platform.c @@ -6,14 +6,13 @@ */ #include #include -#include "ipmi_dmi.h" -#include #include #include #include #include #include #include "ipmi_si.h" +#include "ipmi_dmi.h" #define PFX "ipmi_platform: " @@ -21,19 +20,29 @@ static bool si_tryplatform = true; #ifdef CONFIG_ACPI static bool si_tryacpi = true; #endif +#ifdef CONFIG_OF +static bool si_tryopenfirmware = true; +#endif #ifdef CONFIG_DMI static bool si_trydmi = true; +#else +static bool si_trydmi = false; #endif module_param_named(tryplatform, si_tryplatform, bool, 0); MODULE_PARM_DESC(tryplatform, "Setting this to zero will disable the" " default scan of the interfaces identified via platform" - " interfaces like openfirmware"); + " interfaces besides ACPI, OpenFirmware, and DMI"); #ifdef CONFIG_ACPI module_param_named(tryacpi, si_tryacpi, bool, 0); MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" " default scan of the interfaces identified via ACPI"); #endif +#ifdef CONFIG_OF +module_param_named(tryopenfirmware, si_tryopenfirmware, bool, 0); +MODULE_PARM_DESC(tryacpi, "Setting this to zero will disable the" + " default scan of the interfaces identified via OpenFirmware"); +#endif #ifdef CONFIG_DMI module_param_named(trydmi, si_trydmi, bool, 0); MODULE_PARM_DESC(trydmi, "Setting this to zero will disable the" @@ -235,7 +244,6 @@ static void spmi_find_bmc(void) } #endif -#if defined(CONFIG_DMI) || defined(CONFIG_ACPI) static struct resource * ipmi_get_info_from_resources(struct platform_device *pdev, struct si_sm_io *io) @@ -271,48 +279,52 @@ ipmi_get_info_from_resources(struct platform_device *pdev, return res; } -#endif - -#ifdef CONFIG_DMI -static int dmi_ipmi_probe(struct platform_device *pdev) +static int platform_ipmi_probe(struct platform_device *pdev) { struct si_sm_io io; - u8 type, slave_addr; + u8 type, slave_addr, addr_source; int rv; - if (!si_trydmi) - return -ENODEV; + rv = device_property_read_u8(&pdev->dev, "addr-source", &addr_source); + if (rv) + addr_source = SI_PLATFORM; + if (addr_source >= SI_LAST) + return -EINVAL; + + if (addr_source == SI_SMBIOS) { + if (!si_trydmi) + return -ENODEV; + } else { + if (!si_tryplatform) + return -ENODEV; + } rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type); if (rv) return -ENODEV; memset(&io, 0, sizeof(io)); - io.addr_source = SI_SMBIOS; - pr_info(PFX "probing via SMBIOS\n"); + io.addr_source = addr_source; + dev_info(&pdev->dev, PFX "probing via %s\n", + ipmi_addr_src_to_str(addr_source)); switch (type) { - case IPMI_DMI_TYPE_KCS: - io.si_type = SI_KCS; - break; - case IPMI_DMI_TYPE_SMIC: - io.si_type = SI_SMIC; - break; - case IPMI_DMI_TYPE_BT: - io.si_type = SI_BT; + case SI_KCS: + case SI_SMIC: + case SI_BT: + io.si_type = type; break; default: + dev_err(&pdev->dev, "ipmi-type property is invalid\n"); return -EINVAL; } - if (!ipmi_get_info_from_resources(pdev, &io)) { - rv = -EINVAL; - goto err_free; - } + if (!ipmi_get_info_from_resources(pdev, &io)) + return -EINVAL; rv = device_property_read_u8(&pdev->dev, "slave-addr", &slave_addr); if (rv) { - dev_warn(&pdev->dev, "device has no slave-addr property"); + dev_warn(&pdev->dev, "device has no slave-addr property\n"); io.slave_addr = 0x20; } else { io.slave_addr = slave_addr; @@ -333,16 +345,7 @@ static int dmi_ipmi_probe(struct platform_device *pdev) ipmi_si_add_smi(&io); return 0; - -err_free: - return rv; } -#else -static int dmi_ipmi_probe(struct platform_device *pdev) -{ - return -ENODEV; -} -#endif /* CONFIG_DMI */ #ifdef CONFIG_OF static const struct of_device_id of_ipmi_match[] = { @@ -366,6 +369,9 @@ static int of_ipmi_probe(struct platform_device *pdev) int ret; int proplen; + if (!si_tryopenfirmware) + return -ENODEV; + dev_info(&pdev->dev, "probing via device tree\n"); match = of_match_device(of_ipmi_match, &pdev->dev); @@ -436,25 +442,12 @@ static int find_slave_address(struct si_sm_io *io, int slave_addr) { #ifdef CONFIG_IPMI_DMI_DECODE if (!slave_addr) { - int type = -1; u32 flags = IORESOURCE_IO; - switch (io->si_type) { - case SI_KCS: - type = IPMI_DMI_TYPE_KCS; - break; - case SI_BT: - type = IPMI_DMI_TYPE_BT; - break; - case SI_SMIC: - type = IPMI_DMI_TYPE_SMIC; - break; - } - if (io->addr_type == IPMI_MEM_ADDR_SPACE) flags = IORESOURCE_MEM; - slave_addr = ipmi_dmi_get_slave_addr(type, flags, + slave_addr = ipmi_dmi_get_slave_addr(io->si_type, flags, io->addr_data); } #endif @@ -563,7 +556,7 @@ static int ipmi_probe(struct platform_device *pdev) if (acpi_ipmi_probe(pdev) == 0) return 0; - return dmi_ipmi_probe(pdev); + return platform_ipmi_probe(pdev); } static int ipmi_remove(struct platform_device *pdev) @@ -583,11 +576,9 @@ struct platform_driver ipmi_platform_driver = { void ipmi_si_platform_init(void) { - if (si_tryplatform) { - int rv = platform_driver_register(&ipmi_platform_driver); - if (rv) - pr_err(PFX "Unable to register driver: %d\n", rv); - } + int rv = platform_driver_register(&ipmi_platform_driver); + if (rv) + pr_err(PFX "Unable to register driver: %d\n", rv); #ifdef CONFIG_ACPI if (si_tryacpi) diff --git a/drivers/char/ipmi/ipmi_si_sm.h b/drivers/char/ipmi/ipmi_si_sm.h index fbf5bfccde2e..aa8d88ab4433 100644 --- a/drivers/char/ipmi/ipmi_si_sm.h +++ b/drivers/char/ipmi/ipmi_si_sm.h @@ -43,7 +43,7 @@ struct si_sm_data; enum si_type { - SI_KCS, SI_SMIC, SI_BT + SI_TYPE_INVALID, SI_KCS, SI_SMIC, SI_BT }; /* diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index aadec879d052..466b3a1c0adf 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -53,6 +53,7 @@ #include #include #include +#include "ipmi_si_sm.h" #include "ipmi_dmi.h" #define PFX "ipmi_ssif: " @@ -1482,7 +1483,7 @@ static int find_slave_address(struct i2c_client *client, int slave_addr) #ifdef CONFIG_IPMI_DMI_DECODE if (!slave_addr) slave_addr = ipmi_dmi_get_slave_addr( - IPMI_DMI_TYPE_SSIF, + SI_TYPE_INVALID, i2c_adapter_id(client->adapter), client->addr); #endif @@ -2013,20 +2014,13 @@ static void spmi_find_bmc(void) { } #ifdef CONFIG_DMI static int dmi_ipmi_probe(struct platform_device *pdev) { - u8 type, slave_addr = 0; + u8 slave_addr = 0; u16 i2c_addr; int rv; if (!ssif_trydmi) return -ENODEV; - rv = device_property_read_u8(&pdev->dev, "ipmi-type", &type); - if (rv) - return -ENODEV; - - if (type != IPMI_DMI_TYPE_SSIF) - return -ENODEV; - rv = device_property_read_u16(&pdev->dev, "i2c-addr", &i2c_addr); if (rv) { dev_warn(&pdev->dev, PFX "No i2c-addr property\n"); diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index 80fc3f798984..f4ffacf4fe9d 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h @@ -277,7 +277,7 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len); */ enum ipmi_addr_src { SI_INVALID = 0, SI_HOTMOD, SI_HARDCODED, SI_SPMI, SI_ACPI, SI_SMBIOS, - SI_PCI, SI_DEVICETREE, SI_LAST + SI_PCI, SI_DEVICETREE, SI_PLATFORM, SI_LAST }; const char *ipmi_addr_src_to_str(enum ipmi_addr_src src); -- cgit v1.2.3-59-g8ed1b From 6ade97da601f8af793f6c7a861af754d0f0b6767 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Tue, 26 Sep 2017 23:12:28 +0300 Subject: arp: make arp_hdr_len() return unsigned int Negative ARP header length are not a thing. Constify arguments while I'm at it. Space savings: add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-3 (-3) function old new delta arpt_do_table 1163 1160 -3 Signed-off-by: Alexey Dobriyan Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 3 ++- include/linux/if_arp.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index c99dc59d729b..d2e94b8559f0 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2491,7 +2491,8 @@ int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, struct slave *curr_active_slave, *curr_arp_slave; unsigned char *arp_ptr; __be32 sip, tip; - int alen, is_arp = skb->protocol == __cpu_to_be16(ETH_P_ARP); + int is_arp = skb->protocol == __cpu_to_be16(ETH_P_ARP); + unsigned int alen; if (!slave_do_arp_validate(bond, slave)) { if ((slave_do_arp_validate_only(bond) && is_arp) || diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h index 3355efc89781..6756fea18b69 100644 --- a/include/linux/if_arp.h +++ b/include/linux/if_arp.h @@ -31,7 +31,7 @@ static inline struct arphdr *arp_hdr(const struct sk_buff *skb) return (struct arphdr *)skb_network_header(skb); } -static inline int arp_hdr_len(struct net_device *dev) +static inline unsigned int arp_hdr_len(const struct net_device *dev) { switch (dev->type) { #if IS_ENABLED(CONFIG_FIREWIRE_NET) -- cgit v1.2.3-59-g8ed1b From 5af48b59f35cf712793badabe1a574a0d0ce3bd3 Mon Sep 17 00:00:00 2001 From: Nikolay Aleksandrov Date: Wed, 27 Sep 2017 16:12:44 +0300 Subject: net: bridge: add per-port group_fwd_mask with less restrictions We need to be able to transparently forward most link-local frames via tunnels (e.g. vxlan, qinq). Currently the bridge's group_fwd_mask has a mask which restricts the forwarding of STP and LACP, but we need to be able to forward these over tunnels and control that forwarding on a per-port basis thus add a new per-port group_fwd_mask option which only disallows mac pause frames to be forwarded (they're always dropped anyway). The patch does not change the current default situation - all of the others are still restricted unless configured for forwarding. We have successfully tested this patch with LACP and STP forwarding over VxLAN and qinq tunnels. Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/uapi/linux/if_link.h | 1 + net/bridge/br_input.c | 1 + net/bridge/br_netlink.c | 14 +++++++++++++- net/bridge/br_private.h | 10 +++++++++- net/bridge/br_sysfs_if.c | 18 ++++++++++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 8d062c58d5cb..ea87bd708ee9 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -325,6 +325,7 @@ enum { IFLA_BRPORT_MCAST_TO_UCAST, IFLA_BRPORT_VLAN_TUNNEL, IFLA_BRPORT_BCAST_FLOOD, + IFLA_BRPORT_GROUP_FWD_MASK, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 7637f58c1226..7cb613776b31 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -289,6 +289,7 @@ rx_handler_result_t br_handle_frame(struct sk_buff **pskb) * * Others reserved for future standardization */ + fwd_mask |= p->group_fwd_mask; switch (dest[5]) { case 0x00: /* Bridge Group Address */ /* If STP is turned off, diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 3bc890716c89..dea88a255d26 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -152,6 +152,7 @@ static inline size_t br_port_info_size(void) #ifdef CONFIG_BRIDGE_IGMP_SNOOPING + nla_total_size(sizeof(u8)) /* IFLA_BRPORT_MULTICAST_ROUTER */ #endif + + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_GROUP_FWD_MASK */ + 0; } @@ -208,7 +209,8 @@ static int br_port_fill_attrs(struct sk_buff *skb, p->topology_change_ack) || nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) || nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(p->flags & - BR_VLAN_TUNNEL))) + BR_VLAN_TUNNEL)) || + nla_put_u16(skb, IFLA_BRPORT_GROUP_FWD_MASK, p->group_fwd_mask)) return -EMSGSIZE; timerval = br_timer_value(&p->message_age_timer); @@ -637,6 +639,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NLA_U8 }, [IFLA_BRPORT_MCAST_FLOOD] = { .type = NLA_U8 }, [IFLA_BRPORT_BCAST_FLOOD] = { .type = NLA_U8 }, + [IFLA_BRPORT_GROUP_FWD_MASK] = { .type = NLA_U16 }, }; /* Change the state of the port and notify spanning tree */ @@ -773,6 +776,15 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) return err; } #endif + + if (tb[IFLA_BRPORT_GROUP_FWD_MASK]) { + u16 fwd_mask = nla_get_u16(tb[IFLA_BRPORT_GROUP_FWD_MASK]); + + if (fwd_mask & BR_GROUPFWD_MACPAUSE) + return -EINVAL; + p->group_fwd_mask = fwd_mask; + } + br_port_flags_change(p, old_flags ^ p->flags); return 0; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index e870cfc85b14..020c709a017f 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -36,7 +36,14 @@ /* Control of forwarding link local multicast */ #define BR_GROUPFWD_DEFAULT 0 /* Don't allow forwarding of control protocols like STP, MAC PAUSE and LACP */ -#define BR_GROUPFWD_RESTRICTED 0x0007u +enum { + BR_GROUPFWD_STP = BIT(0), + BR_GROUPFWD_MACPAUSE = BIT(1), + BR_GROUPFWD_LACP = BIT(2), +}; + +#define BR_GROUPFWD_RESTRICTED (BR_GROUPFWD_STP | BR_GROUPFWD_MACPAUSE | \ + BR_GROUPFWD_LACP) /* The Nearest Customer Bridge Group Address, 01-80-C2-00-00-[00,0B,0C,0D,0F] */ #define BR_GROUPFWD_8021AD 0xB801u @@ -268,6 +275,7 @@ struct net_bridge_port { #ifdef CONFIG_NET_SWITCHDEV int offload_fwd_mark; #endif + u16 group_fwd_mask; }; #define br_auto_port(p) ((p)->flags & BR_AUTO_MASK) diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 5d5d413a6cf8..9110d5e56085 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -165,6 +165,23 @@ static int store_flush(struct net_bridge_port *p, unsigned long v) } static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush); +static ssize_t show_group_fwd_mask(struct net_bridge_port *p, char *buf) +{ + return sprintf(buf, "%#x\n", p->group_fwd_mask); +} + +static int store_group_fwd_mask(struct net_bridge_port *p, + unsigned long v) +{ + if (v & BR_GROUPFWD_MACPAUSE) + return -EINVAL; + p->group_fwd_mask = v; + + return 0; +} +static BRPORT_ATTR(group_fwd_mask, S_IRUGO | S_IWUSR, show_group_fwd_mask, + store_group_fwd_mask); + BRPORT_ATTR_FLAG(hairpin_mode, BR_HAIRPIN_MODE); BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD); BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK); @@ -223,6 +240,7 @@ static const struct brport_attribute *brport_attrs[] = { &brport_attr_proxyarp_wifi, &brport_attr_multicast_flood, &brport_attr_broadcast_flood, + &brport_attr_group_fwd_mask, NULL }; -- cgit v1.2.3-59-g8ed1b From cb4d2b3f03d8eed90be3a194e5b54b734ec4bbe9 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 27 Sep 2017 14:37:52 -0700 Subject: bpf: Add name, load_time, uid and map_ids to bpf_prog_info The patch adds name and load_time to struct bpf_prog_aux. They are also exported to bpf_prog_info. The bpf_prog's name is passed by userspace during BPF_PROG_LOAD. The kernel only stores the first (BPF_PROG_NAME_LEN - 1) bytes and the name stored in the kernel is always \0 terminated. The kernel will reject name that contains characters other than isalnum() and '_'. It will also reject name that is not null terminated. The existing 'user->uid' of the bpf_prog_aux is also exported to the bpf_prog_info as created_by_uid. The existing 'used_maps' of the bpf_prog_aux is exported to the newly added members 'nr_map_ids' and 'map_ids' of the bpf_prog_info. On the input, nr_map_ids tells how big the userspace's map_ids buffer is. On the output, nr_map_ids tells the exact user_map_cnt and it will only copy up to the userspace's map_ids buffer is allowed. Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 2 ++ include/uapi/linux/bpf.h | 8 ++++++++ kernel/bpf/syscall.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 2b672c50f160..33ccc474fb04 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -187,6 +187,8 @@ struct bpf_prog_aux { struct bpf_map **used_maps; struct bpf_prog *prog; struct user_struct *user; + u64 load_time; /* ns since boottime */ + u8 name[BPF_OBJ_NAME_LEN]; union { struct work_struct work; struct rcu_head rcu; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index e43491ac4823..bd6348269bf5 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -175,6 +175,8 @@ enum bpf_attach_type { /* Specify numa node during map creation */ #define BPF_F_NUMA_NODE (1U << 2) +#define BPF_OBJ_NAME_LEN 16U + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ @@ -210,6 +212,7 @@ union bpf_attr { __aligned_u64 log_buf; /* user supplied buffer */ __u32 kern_version; /* checked when prog_type=kprobe */ __u32 prog_flags; + __u8 prog_name[BPF_OBJ_NAME_LEN]; }; struct { /* anonymous struct used by BPF_OBJ_* commands */ @@ -812,6 +815,11 @@ struct bpf_prog_info { __u32 xlated_prog_len; __aligned_u64 jited_prog_insns; __aligned_u64 xlated_prog_insns; + __u64 load_time; /* ns since boottime */ + __u32 created_by_uid; + __u32 nr_map_ids; + __aligned_u64 map_ids; + __u8 name[BPF_OBJ_NAME_LEN]; } __attribute__((aligned(8))); struct bpf_map_info { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 25d074920a00..45970df3f820 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -23,6 +23,9 @@ #include #include #include +#include +#include +#include #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \ (map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \ @@ -312,6 +315,30 @@ int bpf_map_new_fd(struct bpf_map *map) offsetof(union bpf_attr, CMD##_LAST_FIELD) - \ sizeof(attr->CMD##_LAST_FIELD)) != NULL +/* dst and src must have at least BPF_OBJ_NAME_LEN number of bytes. + * Return 0 on success and < 0 on error. + */ +static int bpf_obj_name_cpy(char *dst, const char *src) +{ + const char *end = src + BPF_OBJ_NAME_LEN; + + /* Copy all isalnum() and '_' char */ + while (src < end && *src) { + if (!isalnum(*src) && *src != '_') + return -EINVAL; + *dst++ = *src++; + } + + /* No '\0' found in BPF_OBJ_NAME_LEN number of bytes */ + if (src == end) + return -EINVAL; + + /* '\0' terminates dst */ + *dst = 0; + + return 0; +} + #define BPF_MAP_CREATE_LAST_FIELD numa_node /* called via syscall */ static int map_create(union bpf_attr *attr) @@ -973,7 +1000,7 @@ struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type) EXPORT_SYMBOL_GPL(bpf_prog_get_type); /* last field in 'union bpf_attr' used by this command */ -#define BPF_PROG_LOAD_LAST_FIELD prog_flags +#define BPF_PROG_LOAD_LAST_FIELD prog_name static int bpf_prog_load(union bpf_attr *attr) { @@ -1037,6 +1064,11 @@ static int bpf_prog_load(union bpf_attr *attr) if (err < 0) goto free_prog; + prog->aux->load_time = ktime_get_boot_ns(); + err = bpf_obj_name_cpy(prog->aux->name, attr->prog_name); + if (err) + goto free_prog; + /* run eBPF verifier */ err = bpf_check(&prog, attr); if (err < 0) @@ -1358,8 +1390,25 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, info.type = prog->type; info.id = prog->aux->id; + info.load_time = prog->aux->load_time; + info.created_by_uid = from_kuid_munged(current_user_ns(), + prog->aux->user->uid); memcpy(info.tag, prog->tag, sizeof(prog->tag)); + memcpy(info.name, prog->aux->name, sizeof(prog->aux->name)); + + ulen = info.nr_map_ids; + info.nr_map_ids = prog->aux->used_map_cnt; + ulen = min_t(u32, info.nr_map_ids, ulen); + if (ulen) { + u32 *user_map_ids = (u32 *)info.map_ids; + u32 i; + + for (i = 0; i < ulen; i++) + if (put_user(prog->aux->used_maps[i]->id, + &user_map_ids[i])) + return -EFAULT; + } if (!capable(CAP_SYS_ADMIN)) { info.jited_prog_len = 0; -- cgit v1.2.3-59-g8ed1b From ad5b177bd73f5107d97c36f56395c4281fb6f089 Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Wed, 27 Sep 2017 14:37:53 -0700 Subject: bpf: Add map_name to bpf_map_info This patch allows userspace to specify a name for a map during BPF_MAP_CREATE. The map's name can later be exported to user space via BPF_OBJ_GET_INFO_BY_FD. Signed-off-by: Martin KaFai Lau Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 1 + include/uapi/linux/bpf.h | 2 ++ kernel/bpf/syscall.c | 7 ++++++- 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 33ccc474fb04..252f4bc9eb25 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -56,6 +56,7 @@ struct bpf_map { struct work_struct work; atomic_t usercnt; struct bpf_map *inner_map_meta; + u8 name[BPF_OBJ_NAME_LEN]; }; /* function argument constraints */ diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index bd6348269bf5..6d2137b4cf38 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -190,6 +190,7 @@ union bpf_attr { __u32 numa_node; /* numa node (effective only if * BPF_F_NUMA_NODE is set). */ + __u8 map_name[BPF_OBJ_NAME_LEN]; }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ @@ -829,6 +830,7 @@ struct bpf_map_info { __u32 value_size; __u32 max_entries; __u32 map_flags; + __u8 name[BPF_OBJ_NAME_LEN]; } __attribute__((aligned(8))); /* User bpf_sock_ops struct to access socket values and specify request ops diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 45970df3f820..11a7f82a55d1 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -339,7 +339,7 @@ static int bpf_obj_name_cpy(char *dst, const char *src) return 0; } -#define BPF_MAP_CREATE_LAST_FIELD numa_node +#define BPF_MAP_CREATE_LAST_FIELD map_name /* called via syscall */ static int map_create(union bpf_attr *attr) { @@ -361,6 +361,10 @@ static int map_create(union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); + err = bpf_obj_name_cpy(map->name, attr->map_name); + if (err) + goto free_map_nouncharge; + atomic_set(&map->refcnt, 1); atomic_set(&map->usercnt, 1); @@ -1462,6 +1466,7 @@ static int bpf_map_get_info_by_fd(struct bpf_map *map, info.value_size = map->value_size; info.max_entries = map->max_entries; info.map_flags = map->map_flags; + memcpy(info.name, map->name, sizeof(map->name)); if (copy_to_user(uinfo, &info, info_len) || put_user(info_len, &uattr->info.info_len)) -- cgit v1.2.3-59-g8ed1b From c7c3e5913bf18eda3cf38932bebdce48351baac9 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 27 Sep 2017 19:08:00 -0700 Subject: net: ipv4: remove fib_weight fib_weight in fib_info is set but not used. Remove it and the helpers for setting it. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/net/ip_fib.h | 3 --- net/ipv4/fib_semantics.c | 9 --------- 2 files changed, 12 deletions(-) (limited to 'include') diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index 1a7f7e424320..f80524396c06 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -122,9 +122,6 @@ struct fib_info { #define fib_rtt fib_metrics->metrics[RTAX_RTT-1] #define fib_advmss fib_metrics->metrics[RTAX_ADVMSS-1] int fib_nhs; -#ifdef CONFIG_IP_ROUTE_MULTIPATH - int fib_weight; -#endif struct rcu_head rcu; struct fib_nh fib_nh[0]; #define fib_dev fib_nh[0].nh_dev diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 57a5d48acee8..be0874620ecc 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -601,17 +601,9 @@ static void fib_rebalance(struct fib_info *fi) atomic_set(&nexthop_nh->nh_upper_bound, upper_bound); } endfor_nexthops(fi); } - -static inline void fib_add_weight(struct fib_info *fi, - const struct fib_nh *nh) -{ - fi->fib_weight += nh->nh_weight; -} - #else /* CONFIG_IP_ROUTE_MULTIPATH */ #define fib_rebalance(fi) do { } while (0) -#define fib_add_weight(fi, nh) do { } while (0) #endif /* CONFIG_IP_ROUTE_MULTIPATH */ @@ -1275,7 +1267,6 @@ struct fib_info *fib_create_info(struct fib_config *cfg, change_nexthops(fi) { fib_info_update_nh_saddr(net, nexthop_nh); - fib_add_weight(fi, nexthop_nh); } endfor_nexthops(fi) fib_rebalance(fi); -- cgit v1.2.3-59-g8ed1b From 80815004a45fc68b6e34653af4fca47be7fb96ed Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Fri, 29 Sep 2017 16:22:53 +0800 Subject: clk: sunxi-ng: sun6i: Export video PLLs The 2x outputs of the 2 video PLL clocks are directly used by the HDMI controller block. Export them so they can be referenced in the device tree. Fixes: c6e6c96d8fa6 ("clk: sunxi-ng: Add A31/A31s clocks") Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu-sun6i-a31.h | 8 ++++++-- include/dt-bindings/clock/sun6i-a31-ccu.h | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-a31.h b/drivers/clk/sunxi-ng/ccu-sun6i-a31.h index 4e434011e9e7..27e6ad4133ab 100644 --- a/drivers/clk/sunxi-ng/ccu-sun6i-a31.h +++ b/drivers/clk/sunxi-ng/ccu-sun6i-a31.h @@ -27,7 +27,9 @@ #define CLK_PLL_AUDIO_4X 4 #define CLK_PLL_AUDIO_8X 5 #define CLK_PLL_VIDEO0 6 -#define CLK_PLL_VIDEO0_2X 7 + +/* The PLL_VIDEO0_2X clock is exported */ + #define CLK_PLL_VE 8 #define CLK_PLL_DDR 9 @@ -35,7 +37,9 @@ #define CLK_PLL_PERIPH_2X 11 #define CLK_PLL_VIDEO1 12 -#define CLK_PLL_VIDEO1_2X 13 + +/* The PLL_VIDEO1_2X clock is exported */ + #define CLK_PLL_GPU 14 #define CLK_PLL_MIPI 15 #define CLK_PLL9 16 diff --git a/include/dt-bindings/clock/sun6i-a31-ccu.h b/include/dt-bindings/clock/sun6i-a31-ccu.h index 4482530fb6f5..c5d13340184a 100644 --- a/include/dt-bindings/clock/sun6i-a31-ccu.h +++ b/include/dt-bindings/clock/sun6i-a31-ccu.h @@ -43,8 +43,12 @@ #ifndef _DT_BINDINGS_CLK_SUN6I_A31_H_ #define _DT_BINDINGS_CLK_SUN6I_A31_H_ +#define CLK_PLL_VIDEO0_2X 7 + #define CLK_PLL_PERIPH 10 +#define CLK_PLL_VIDEO1_2X 13 + #define CLK_CPU 18 #define CLK_AHB1_MIPIDSI 23 -- cgit v1.2.3-59-g8ed1b From a3f5aa907340b5d7b54223ddbaa90410f168864d Mon Sep 17 00:00:00 2001 From: Alan Brady Date: Fri, 14 Jul 2017 09:27:08 -0400 Subject: i40e: Enable VF to negotiate number of allocated queues Currently the PF allocates a default number of queues for each VF and cannot be changed. This patch enables the VF to request a different number of queues allocated to it. This patch also adds a new virtchnl op and capability flag to facilitate this negotiation. After the PF receives a request message, it will set a requested number of queues for that VF. Then when the VF resets, its VSI will get a new number of queues allocated to it. This is a best effort request and since we only allocate a guaranteed default number, if the VF tries to ask for more than the guaranteed number, there may not be enough in HW to accommodate it unless other queues for other VFs are freed. It should also be noted decreasing the number queues allocated to a VF to below the default will NOT enable the allocation of more than 32 VFs per PF and will not free queues guaranteed to each VF by default. Signed-off-by: Alan Brady Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e.h | 1 + drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 75 ++++++++++++++++++++++ drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h | 1 + include/linux/avf/virtchnl.h | 20 ++++++ 4 files changed, 97 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index b7a539cdca00..439c63cb2a0c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -77,6 +77,7 @@ #define i40e_default_queues_per_vmdq(pf) \ (((pf)->hw_features & I40E_HW_RSS_AQ_CAPABLE) ? 4 : 1) #define I40E_DEFAULT_QUEUES_PER_VF 4 +#define I40E_MAX_VF_QUEUES 16 #define I40E_DEFAULT_QUEUES_PER_TC 1 /* should be a power of 2 */ #define i40e_pf_get_max_q_per_tc(pf) \ (((pf)->hw_features & I40E_HW_128_QP_RSS_CAPABLE) ? 128 : 64) diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 4d1e670f490e..a75396c157d9 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -815,6 +815,14 @@ static void i40e_free_vf_res(struct i40e_vf *vf) */ clear_bit(I40E_VF_STATE_INIT, &vf->vf_states); + /* It's possible the VF had requeuested more queues than the default so + * do the accounting here when we're about to free them. + */ + if (vf->num_queue_pairs > I40E_DEFAULT_QUEUES_PER_VF) { + pf->queues_left += vf->num_queue_pairs - + I40E_DEFAULT_QUEUES_PER_VF; + } + /* free vsi & disconnect it from the parent uplink */ if (vf->lan_vsi_idx) { i40e_vsi_release(pf->vsi[vf->lan_vsi_idx]); @@ -868,12 +876,27 @@ static int i40e_alloc_vf_res(struct i40e_vf *vf) int total_queue_pairs = 0; int ret; + if (vf->num_req_queues && + vf->num_req_queues <= pf->queues_left + I40E_DEFAULT_QUEUES_PER_VF) + pf->num_vf_qps = vf->num_req_queues; + else + pf->num_vf_qps = I40E_DEFAULT_QUEUES_PER_VF; + /* allocate hw vsi context & associated resources */ ret = i40e_alloc_vsi_res(vf, I40E_VSI_SRIOV); if (ret) goto error_alloc; total_queue_pairs += pf->vsi[vf->lan_vsi_idx]->alloc_queue_pairs; + /* We account for each VF to get a default number of queue pairs. If + * the VF has now requested more, we need to account for that to make + * certain we never request more queues than we actually have left in + * HW. + */ + if (total_queue_pairs > I40E_DEFAULT_QUEUES_PER_VF) + pf->queues_left -= + total_queue_pairs - I40E_DEFAULT_QUEUES_PER_VF; + if (vf->trusted) set_bit(I40E_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps); else @@ -1579,6 +1602,9 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) VIRTCHNL_VF_OFFLOAD_WB_ON_ITR; } + if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_REQ_QUEUES) + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_REQ_QUEUES; + vfres->num_vsis = num_vsis; vfres->num_queue_pairs = vf->num_queue_pairs; vfres->max_vectors = pf->hw.func_caps.num_msix_vectors_vf; @@ -1986,6 +2012,52 @@ error_param: aq_ret); } +/** + * i40e_vc_request_queues_msg + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * @msglen: msg length + * + * VFs get a default number of queues but can use this message to request a + * different number. Will respond with either the number requested or the + * maximum we can support. + **/ +static int i40e_vc_request_queues_msg(struct i40e_vf *vf, u8 *msg, int msglen) +{ + struct virtchnl_vf_res_request *vfres = + (struct virtchnl_vf_res_request *)msg; + int req_pairs = vfres->num_queue_pairs; + int cur_pairs = vf->num_queue_pairs; + struct i40e_pf *pf = vf->pf; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) + return -EINVAL; + + if (req_pairs <= 0) { + dev_err(&pf->pdev->dev, + "VF %d tried to request %d queues. Ignoring.\n", + vf->vf_id, req_pairs); + } else if (req_pairs > I40E_MAX_VF_QUEUES) { + dev_err(&pf->pdev->dev, + "VF %d tried to request more than %d queues.\n", + vf->vf_id, + I40E_MAX_VF_QUEUES); + vfres->num_queue_pairs = I40E_MAX_VF_QUEUES; + } else if (req_pairs - cur_pairs > pf->queues_left) { + dev_warn(&pf->pdev->dev, + "VF %d requested %d more queues, but only %d left.\n", + vf->vf_id, + req_pairs - cur_pairs, + pf->queues_left); + vfres->num_queue_pairs = pf->queues_left + cur_pairs; + } else { + vf->num_req_queues = req_pairs; + } + + return i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0, + (u8 *)vfres, sizeof(vfres)); +} + /** * i40e_vc_get_stats_msg * @vf: pointer to the VF info @@ -2708,6 +2780,9 @@ int i40e_vc_process_vf_msg(struct i40e_pf *pf, s16 vf_id, u32 v_opcode, case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING: ret = i40e_vc_disable_vlan_stripping(vf, msg, msglen); break; + case VIRTCHNL_OP_REQUEST_QUEUES: + ret = i40e_vc_request_queues_msg(vf, msg, msglen); + break; case VIRTCHNL_OP_UNKNOWN: default: diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h index 1f4b0c504368..5111d05d5f2f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h @@ -97,6 +97,7 @@ struct i40e_vf { u16 lan_vsi_id; /* ID as used by firmware */ u8 num_queue_pairs; /* num of qps assigned to VF vsis */ + u8 num_req_queues; /* num of requested qps */ u64 num_mdd_events; /* num of mdd events detected */ /* num of continuous malformed or invalid msgs detected */ u64 num_invalid_msgs; diff --git a/include/linux/avf/virtchnl.h b/include/linux/avf/virtchnl.h index 2b038442c352..60e5d90cb18a 100644 --- a/include/linux/avf/virtchnl.h +++ b/include/linux/avf/virtchnl.h @@ -135,6 +135,7 @@ enum virtchnl_ops { VIRTCHNL_OP_SET_RSS_HENA = 26, VIRTCHNL_OP_ENABLE_VLAN_STRIPPING = 27, VIRTCHNL_OP_DISABLE_VLAN_STRIPPING = 28, + VIRTCHNL_OP_REQUEST_QUEUES = 29, }; /* This macro is used to generate a compilation error if a structure @@ -235,6 +236,7 @@ VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_vsi_resource); #define VIRTCHNL_VF_OFFLOAD_RSS_AQ 0x00000008 #define VIRTCHNL_VF_OFFLOAD_RSS_REG 0x00000010 #define VIRTCHNL_VF_OFFLOAD_WB_ON_ITR 0x00000020 +#define VIRTCHNL_VF_OFFLOAD_REQ_QUEUES 0x00000040 #define VIRTCHNL_VF_OFFLOAD_VLAN 0x00010000 #define VIRTCHNL_VF_OFFLOAD_RX_POLLING 0x00020000 #define VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2 0x00040000 @@ -325,6 +327,21 @@ struct virtchnl_vsi_queue_config_info { struct virtchnl_queue_pair_info qpair[1]; }; +/* VIRTCHNL_OP_REQUEST_QUEUES + * VF sends this message to request the PF to allocate additional queues to + * this VF. Each VF gets a guaranteed number of queues on init but asking for + * additional queues must be negotiated. This is a best effort request as it + * is possible the PF does not have enough queues left to support the request. + * If the PF cannot support the number requested it will respond with the + * maximum number it is able to support; otherwise it will respond with the + * number requested. + */ + +/* VF resource request */ +struct virtchnl_vf_res_request { + u16 num_queue_pairs; +}; + VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_vsi_queue_config_info); /* VIRTCHNL_OP_CONFIG_IRQ_MAP @@ -691,6 +708,9 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode, case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING: break; + case VIRTCHNL_OP_REQUEST_QUEUES: + valid_len = sizeof(struct virtchnl_vf_res_request); + break; /* These are always errors coming from the VF. */ case VIRTCHNL_OP_EVENT: case VIRTCHNL_OP_UNKNOWN: -- cgit v1.2.3-59-g8ed1b From 84e14fe353de7624872e582887712079ba0b2d56 Mon Sep 17 00:00:00 2001 From: Maciej Żenczykowski Date: Tue, 26 Sep 2017 21:32:42 -0700 Subject: net-ipv6: add support for sockopt(SOL_IPV6, IPV6_FREEBIND) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far we've been relying on sockopt(SOL_IP, IP_FREEBIND) being usable even on IPv6 sockets. However, it turns out it is perfectly reasonable to want to set freebind on an AF_INET6 SOCK_RAW socket - but there is no way to set any SOL_IP socket option on such a socket (they're all blindly errored out). One use case for this is to allow spoofing src ip on a raw socket via sendmsg cmsg. Tested: built, and booted # python >>> import socket >>> SOL_IP = socket.SOL_IP >>> SOL_IPV6 = socket.IPPROTO_IPV6 >>> IP_FREEBIND = 15 >>> IPV6_FREEBIND = 78 >>> s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, 0) >>> s.getsockopt(SOL_IP, IP_FREEBIND) 0 >>> s.getsockopt(SOL_IPV6, IPV6_FREEBIND) 0 >>> s.setsockopt(SOL_IPV6, IPV6_FREEBIND, 1) >>> s.getsockopt(SOL_IP, IP_FREEBIND) 1 >>> s.getsockopt(SOL_IPV6, IPV6_FREEBIND) 1 Signed-off-by: Maciej Żenczykowski Signed-off-by: David S. Miller --- include/uapi/linux/in6.h | 1 + net/ipv6/ipv6_sockglue.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index 46444f8fbee4..4f8f3eb0699f 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h @@ -284,6 +284,7 @@ struct in6_flowlabel_req { #define IPV6_TRANSPARENT 75 #define IPV6_UNICAST_IF 76 #define IPV6_RECVFRAGSIZE 77 +#define IPV6_FREEBIND 78 /* * Multicast Routing: diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index a5e466d4e093..b9404feabd78 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -377,6 +377,14 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, retv = 0; break; + case IPV6_FREEBIND: + if (optlen < sizeof(int)) + goto e_inval; + /* we also don't have a separate freebind bit for IPV6 */ + inet_sk(sk)->freebind = valbool; + retv = 0; + break; + case IPV6_RECVORIGDSTADDR: if (optlen < sizeof(int)) goto e_inval; @@ -1214,6 +1222,10 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, val = inet_sk(sk)->transparent; break; + case IPV6_FREEBIND: + val = inet_sk(sk)->freebind; + break; + case IPV6_RECVORIGDSTADDR: val = np->rxopt.bits.rxorigdstaddr; break; -- cgit v1.2.3-59-g8ed1b From 152402483ed75b167d5628d414e876ffa7a6d4c4 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 29 Sep 2017 17:19:18 -0400 Subject: net: dsa: add tagging ops to port The DSA tagging protocol operations are specific to each CPU port, thus the dsa_device_ops pointer belongs to the dsa_port structure. >From now on assign a slave's xmit copy from its CPU port tagging operations. This will ease the future support for multiple CPU ports. Also keep the tag_ops at the beginning of the dsa_port structure so that we ensure copies for hot path are in cacheline 1. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 3 +++ net/dsa/dsa2.c | 1 + net/dsa/dsa_priv.h | 2 +- net/dsa/legacy.c | 1 + net/dsa/slave.c | 3 +-- 5 files changed, 7 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 8dee216a5a9b..4d1df2f086e8 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -175,6 +175,9 @@ struct dsa_mall_tc_entry { struct dsa_port { + /* CPU port tagging operations used by master or slave devices */ + const struct dsa_device_ops *tag_ops; + struct dsa_switch *ds; unsigned int index; const char *name; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 6a10c5c1639f..9eac4726dc0c 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -522,6 +522,7 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, return PTR_ERR(tag_ops); } + dst->cpu_dp->tag_ops = tag_ops; dst->tag_ops = tag_ops; dst->rcv = dst->tag_ops->rcv; diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index d429505dc4e7..9397291bb3aa 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -66,7 +66,7 @@ struct dsa_notifier_vlan_info { }; struct dsa_slave_priv { - /* Copy of dp->ds->dst->tag_ops->xmit for faster access in hot path */ + /* Copy of CPU port xmit for faster access in slave transmit hot path */ struct sk_buff * (*xmit)(struct sk_buff *skb, struct net_device *dev); diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 8e849013f69d..4d374541815a 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -152,6 +152,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, if (IS_ERR(tag_ops)) return PTR_ERR(tag_ops); + dst->cpu_dp->tag_ops = tag_ops; dst->tag_ops = tag_ops; dst->rcv = dst->tag_ops->rcv; } diff --git a/net/dsa/slave.c b/net/dsa/slave.c index bf8800de13c1..4b634db05cee 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1117,7 +1117,6 @@ int dsa_slave_resume(struct net_device *slave_dev) int dsa_slave_create(struct dsa_port *port, const char *name) { struct dsa_switch *ds = port->ds; - struct dsa_switch_tree *dst = ds->dst; struct net_device *master; struct net_device *slave_dev; struct dsa_slave_priv *p; @@ -1162,7 +1161,7 @@ int dsa_slave_create(struct dsa_port *port, const char *name) } p->dp = port; INIT_LIST_HEAD(&p->mall_tc_list); - p->xmit = dst->tag_ops->xmit; + p->xmit = cpu_dp->tag_ops->xmit; p->old_pause = -1; p->old_link = -1; -- cgit v1.2.3-59-g8ed1b From 3e41f93b358a8800194b87995ad076fc50919719 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 29 Sep 2017 17:19:19 -0400 Subject: net: dsa: prepare master receive hot path In preparation to make DSA master devices point to their corresponding CPU port instead of the whole tree, add copies of dst and rcv in the dsa_port structure so that we keep fast access in the receive hot path. Also keep the copies at the beginning of the dsa_port structure in order to ensure they are available in cacheline 1. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 5 +++++ net/dsa/dsa2.c | 4 ++++ net/dsa/legacy.c | 4 ++++ 3 files changed, 13 insertions(+) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 4d1df2f086e8..6bda01fa5747 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -178,6 +178,11 @@ struct dsa_port { /* CPU port tagging operations used by master or slave devices */ const struct dsa_device_ops *tag_ops; + /* Copies for faster access in master receive hot path */ + struct dsa_switch_tree *dst; + struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt); + struct dsa_switch *ds; unsigned int index; const char *name; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 9eac4726dc0c..b71e3bb478e4 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -524,7 +524,11 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, dst->cpu_dp->tag_ops = tag_ops; dst->tag_ops = tag_ops; + + /* Make a few copies for faster access in master receive hot path */ + dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv; dst->rcv = dst->tag_ops->rcv; + dst->cpu_dp->dst = dst; return 0; } diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 4d374541815a..96c7e3f8b8bb 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -154,7 +154,11 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, dst->cpu_dp->tag_ops = tag_ops; dst->tag_ops = tag_ops; + + /* Few copies for faster access in master receive hot path */ + dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv; dst->rcv = dst->tag_ops->rcv; + dst->cpu_dp->dst = dst; } memcpy(ds->rtable, cd->rtable, sizeof(ds->rtable)); -- cgit v1.2.3-59-g8ed1b From 2f657a600409f1961d67642fe384a9d4be71d36a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 29 Sep 2017 17:19:20 -0400 Subject: net: dsa: change dsa_ptr for a dsa_port With DSA, a master net device (CPU facing interface) has a dsa_ptr pointer to which hangs a dsa_switch_tree. This is not correct because a master interface is wired to a dedicated switch port, and because we can theoretically have several master interfaces pointing to several CPU ports of the same switch fabric. Change the master interface's dsa_ptr for the CPU dsa_port pointer. This is a step towards supporting multiple CPU ports. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- net/dsa/dsa.c | 6 +++--- net/dsa/dsa2.c | 2 +- net/dsa/dsa_priv.h | 3 ++- net/dsa/legacy.c | 2 +- net/dsa/master.c | 15 +++++---------- 6 files changed, 14 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f535779d9dc1..e1d6ef130611 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -55,7 +55,7 @@ struct netpoll_info; struct device; struct phy_device; -struct dsa_switch_tree; +struct dsa_port; /* 802.11 specific */ struct wireless_dev; @@ -1752,7 +1752,7 @@ struct net_device { struct vlan_info __rcu *vlan_info; #endif #if IS_ENABLED(CONFIG_NET_DSA) - struct dsa_switch_tree *dsa_ptr; + struct dsa_port *dsa_ptr; #endif #if IS_ENABLED(CONFIG_TIPC) struct tipc_bearer __rcu *tipc_ptr; diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 81c852e32821..51ca2a524a27 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -160,12 +160,12 @@ EXPORT_SYMBOL_GPL(dsa_dev_to_net_device); static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *unused) { - struct dsa_switch_tree *dst = dev->dsa_ptr; + struct dsa_port *cpu_dp = dev->dsa_ptr; struct sk_buff *nskb = NULL; struct pcpu_sw_netstats *s; struct dsa_slave_priv *p; - if (unlikely(dst == NULL)) { + if (unlikely(!cpu_dp)) { kfree_skb(skb); return 0; } @@ -174,7 +174,7 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, if (!skb) return 0; - nskb = dst->rcv(skb, dev, pt); + nskb = cpu_dp->rcv(skb, dev, pt); if (!nskb) { kfree_skb(skb); return 0; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index b71e3bb478e4..62302558f38c 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -438,7 +438,7 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst) * sent to the tag format's receive function. */ wmb(); - dst->cpu_dp->netdev->dsa_ptr = dst; + dst->cpu_dp->netdev->dsa_ptr = dst->cpu_dp; err = dsa_master_ethtool_setup(dst->cpu_dp->netdev); if (err) diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 9397291bb3aa..2850077cc9cc 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -116,7 +116,8 @@ void dsa_master_ethtool_restore(struct net_device *dev); static inline struct net_device *dsa_master_get_slave(struct net_device *dev, int device, int port) { - struct dsa_switch_tree *dst = dev->dsa_ptr; + struct dsa_port *cpu_dp = dev->dsa_ptr; + struct dsa_switch_tree *dst = cpu_dp->dst; struct dsa_switch *ds; if (device < 0 || device >= DSA_MAX_SWITCHES) diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 96c7e3f8b8bb..71917505a5cc 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -607,7 +607,7 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, * sent to the tag format's receive function. */ wmb(); - dev->dsa_ptr = dst; + dev->dsa_ptr = dst->cpu_dp; return dsa_master_ethtool_setup(dst->cpu_dp->netdev); } diff --git a/net/dsa/master.c b/net/dsa/master.c index ef15d35f1574..5f3f57e372e0 100644 --- a/net/dsa/master.c +++ b/net/dsa/master.c @@ -16,8 +16,7 @@ static void dsa_master_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, uint64_t *data) { - struct dsa_switch_tree *dst = dev->dsa_ptr; - struct dsa_port *cpu_dp = dst->cpu_dp; + struct dsa_port *cpu_dp = dev->dsa_ptr; const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; int port = cpu_dp->index; @@ -34,8 +33,7 @@ static void dsa_master_get_ethtool_stats(struct net_device *dev, static int dsa_master_get_sset_count(struct net_device *dev, int sset) { - struct dsa_switch_tree *dst = dev->dsa_ptr; - struct dsa_port *cpu_dp = dst->cpu_dp; + struct dsa_port *cpu_dp = dev->dsa_ptr; const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; int count = 0; @@ -52,8 +50,7 @@ static int dsa_master_get_sset_count(struct net_device *dev, int sset) static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, uint8_t *data) { - struct dsa_switch_tree *dst = dev->dsa_ptr; - struct dsa_port *cpu_dp = dst->cpu_dp; + struct dsa_port *cpu_dp = dev->dsa_ptr; const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops; struct dsa_switch *ds = cpu_dp->ds; int port = cpu_dp->index; @@ -90,8 +87,7 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset, int dsa_master_ethtool_setup(struct net_device *dev) { - struct dsa_switch_tree *dst = dev->dsa_ptr; - struct dsa_port *cpu_dp = dst->cpu_dp; + struct dsa_port *cpu_dp = dev->dsa_ptr; struct dsa_switch *ds = cpu_dp->ds; struct ethtool_ops *ops; @@ -114,8 +110,7 @@ int dsa_master_ethtool_setup(struct net_device *dev) void dsa_master_ethtool_restore(struct net_device *dev) { - struct dsa_switch_tree *dst = dev->dsa_ptr; - struct dsa_port *cpu_dp = dst->cpu_dp; + struct dsa_port *cpu_dp = dev->dsa_ptr; dev->ethtool_ops = cpu_dp->orig_ethtool_ops; cpu_dp->orig_ethtool_ops = NULL; -- cgit v1.2.3-59-g8ed1b From aa193d9b1d7ea6893ce24a9d141f676950563987 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 29 Sep 2017 17:19:21 -0400 Subject: net: dsa: remove tag ops from the switch tree Now that the dsa_ptr is a dsa_port instance, there is no need to keep the tag operations in the dsa_switch_tree structure. Remove it. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 11 ----------- net/dsa/dsa2.c | 2 -- net/dsa/legacy.c | 2 -- 3 files changed, 15 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 6bda01fa5747..10dceccd9ce8 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -130,11 +130,6 @@ struct dsa_switch_tree { */ struct dsa_platform_data *pd; - /* Copy of tag_ops->rcv for faster access in hot path */ - struct sk_buff * (*rcv)(struct sk_buff *skb, - struct net_device *dev, - struct packet_type *pt); - /* * The switch port to which the CPU is attached. */ @@ -144,12 +139,6 @@ struct dsa_switch_tree { * Data for the individual switch chips. */ struct dsa_switch *ds[DSA_MAX_SWITCHES]; - - /* - * Tagging protocol operations for adding and removing an - * encapsulation tag. - */ - const struct dsa_device_ops *tag_ops; }; /* TC matchall action types, only mirroring for now */ diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 62302558f38c..54ed054777bd 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -523,11 +523,9 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, } dst->cpu_dp->tag_ops = tag_ops; - dst->tag_ops = tag_ops; /* Make a few copies for faster access in master receive hot path */ dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv; - dst->rcv = dst->tag_ops->rcv; dst->cpu_dp->dst = dst; return 0; diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 71917505a5cc..19ff6e0a21dc 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -153,11 +153,9 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, return PTR_ERR(tag_ops); dst->cpu_dp->tag_ops = tag_ops; - dst->tag_ops = tag_ops; /* Few copies for faster access in master receive hot path */ dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv; - dst->rcv = dst->tag_ops->rcv; dst->cpu_dp->dst = dst; } -- cgit v1.2.3-59-g8ed1b From c0f095f766981750d238744e5a0cb3ef20163c28 Mon Sep 17 00:00:00 2001 From: Noralf Trønnes Date: Sun, 24 Sep 2017 14:26:25 +0200 Subject: drm/fb-cma-helper: Remove unused functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cma drivers use the drm_gem_framebuffer_helper functions now, so remove drm_fb_cma_destroy, drm_fb_cma_create_handle, drm_fb_cma_create_with_funcs, drm_fb_cma_create and drm_fb_cma_prepare_fb. Signed-off-by: Noralf Trønnes Reviewed-by: Eric Anholt Reviewed-by: Linus Walleij Link: https://patchwork.freedesktop.org/patch/msgid/1506255985-61113-11-git-send-email-noralf@tronnes.org --- drivers/gpu/drm/drm_fb_cma_helper.c | 77 ++----------------------------------- include/drm/drm_fb_cma_helper.h | 13 ------- 2 files changed, 3 insertions(+), 87 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index f2ee88363015..0e3c14174d08 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -38,7 +38,7 @@ struct drm_fbdev_cma { * Provides helper functions for creating a cma (contiguous memory allocator) * backed framebuffer. * - * drm_fb_cma_create() is used in the &drm_mode_config_funcs.fb_create + * drm_gem_fb_create() is used in the &drm_mode_config_funcs.fb_create * callback function to create a cma backed framebuffer. * * An fbdev framebuffer backed by cma is also available by calling @@ -61,8 +61,8 @@ struct drm_fbdev_cma { * } * * static struct drm_framebuffer_funcs driver_fb_funcs = { - * .destroy = drm_fb_cma_destroy, - * .create_handle = drm_fb_cma_create_handle, + * .destroy = drm_gem_fb_destroy, + * .create_handle = drm_gem_fb_create_handle, * .dirty = driver_fb_dirty, * }; * @@ -80,57 +80,6 @@ static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper) return container_of(helper, struct drm_fbdev_cma, fb_helper); } -void drm_fb_cma_destroy(struct drm_framebuffer *fb) -{ - drm_gem_fb_destroy(fb); -} -EXPORT_SYMBOL(drm_fb_cma_destroy); - -int drm_fb_cma_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, unsigned int *handle) -{ - return drm_gem_fb_create_handle(fb, file_priv, handle); -} -EXPORT_SYMBOL(drm_fb_cma_create_handle); - -/** - * drm_fb_cma_create_with_funcs() - helper function for the - * &drm_mode_config_funcs.fb_create - * callback - * @dev: DRM device - * @file_priv: drm file for the ioctl call - * @mode_cmd: metadata from the userspace fb creation request - * @funcs: vtable to be used for the new framebuffer object - * - * This can be used to set &drm_framebuffer_funcs for drivers that need the - * &drm_framebuffer_funcs.dirty callback. Use drm_fb_cma_create() if you don't - * need to change &drm_framebuffer_funcs. - */ -struct drm_framebuffer *drm_fb_cma_create_with_funcs(struct drm_device *dev, - struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd, - const struct drm_framebuffer_funcs *funcs) -{ - return drm_gem_fb_create_with_funcs(dev, file_priv, mode_cmd, funcs); -} -EXPORT_SYMBOL_GPL(drm_fb_cma_create_with_funcs); - -/** - * drm_fb_cma_create() - &drm_mode_config_funcs.fb_create callback function - * @dev: DRM device - * @file_priv: drm file for the ioctl call - * @mode_cmd: metadata from the userspace fb creation request - * - * If your hardware has special alignment or pitch requirements these should be - * checked before calling this function. Use drm_fb_cma_create_with_funcs() if - * you need to set &drm_framebuffer_funcs.dirty. - */ -struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev, - struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) -{ - return drm_gem_fb_create(dev, file_priv, mode_cmd); -} -EXPORT_SYMBOL_GPL(drm_fb_cma_create); - /** * drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer * @fb: The framebuffer @@ -181,26 +130,6 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb, } EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr); -/** - * drm_fb_cma_prepare_fb() - Prepare CMA framebuffer - * @plane: Which plane - * @state: Plane state attach fence to - * - * This should be set as the &struct drm_plane_helper_funcs.prepare_fb hook. - * - * This function checks if the plane FB has an dma-buf attached, extracts - * the exclusive fence and attaches it to plane state for the atomic helper - * to wait on. - * - * There is no need for cleanup_fb for CMA based framebuffer drivers. - */ -int drm_fb_cma_prepare_fb(struct drm_plane *plane, - struct drm_plane_state *state) -{ - return drm_gem_fb_prepare_fb(plane, state); -} -EXPORT_SYMBOL_GPL(drm_fb_cma_prepare_fb); - #ifdef CONFIG_DEBUG_FS static void drm_fb_cma_describe(struct drm_framebuffer *fb, struct seq_file *m) { diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h index a323781afc3f..023f052a5873 100644 --- a/include/drm/drm_fb_cma_helper.h +++ b/include/drm/drm_fb_cma_helper.h @@ -28,16 +28,6 @@ void drm_fbdev_cma_set_suspend(struct drm_fbdev_cma *fbdev_cma, bool state); void drm_fbdev_cma_set_suspend_unlocked(struct drm_fbdev_cma *fbdev_cma, bool state); -void drm_fb_cma_destroy(struct drm_framebuffer *fb); -int drm_fb_cma_create_handle(struct drm_framebuffer *fb, - struct drm_file *file_priv, unsigned int *handle); - -struct drm_framebuffer *drm_fb_cma_create_with_funcs(struct drm_device *dev, - struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd, - const struct drm_framebuffer_funcs *funcs); -struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev, - struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd); - struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, unsigned int plane); @@ -45,9 +35,6 @@ dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb, struct drm_plane_state *state, unsigned int plane); -int drm_fb_cma_prepare_fb(struct drm_plane *plane, - struct drm_plane_state *state); - #ifdef CONFIG_DEBUG_FS struct seq_file; -- cgit v1.2.3-59-g8ed1b From 19fe5f643f89f29c1a16bc474d91506b0e9a6232 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Sun, 1 Oct 2017 17:55:54 -0400 Subject: iomap: Switch from blkno to disk offset Replace iomap->blkno, the sector number, with iomap->addr, the disk offset in bytes. For invalid disk offsets, use the special value IOMAP_NULL_ADDR instead of IOMAP_NULL_BLOCK. This allows to use iomap for mappings which are not block aligned, such as inline data on ext4. Signed-off-by: Andreas Gruenbacher Signed-off-by: Theodore Ts'o Reviewed-by: Darrick J. Wong # iomap, xfs Reviewed-by: Jan Kara --- fs/buffer.c | 4 ++-- fs/dax.c | 2 +- fs/ext2/inode.c | 4 ++-- fs/ext4/inode.c | 4 ++-- fs/iomap.c | 11 +++++------ fs/nfsd/blocklayout.c | 4 ++-- fs/xfs/xfs_iomap.c | 6 +++--- include/linux/iomap.h | 10 +++++----- 8 files changed, 22 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/fs/buffer.c b/fs/buffer.c index 170df856bdb9..bd4d0923cdce 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -1978,8 +1978,8 @@ iomap_to_bh(struct inode *inode, sector_t block, struct buffer_head *bh, case IOMAP_MAPPED: if (offset >= i_size_read(inode)) set_buffer_new(bh); - bh->b_blocknr = (iomap->blkno >> (inode->i_blkbits - 9)) + - ((offset - iomap->offset) >> inode->i_blkbits); + bh->b_blocknr = (iomap->addr + offset - iomap->offset) >> + inode->i_blkbits; set_buffer_mapped(bh); break; } diff --git a/fs/dax.c b/fs/dax.c index f001d8c72a06..f3a44a7c14b3 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -938,7 +938,7 @@ EXPORT_SYMBOL_GPL(__dax_zero_page_range); static sector_t dax_iomap_sector(struct iomap *iomap, loff_t pos) { - return iomap->blkno + (((pos & PAGE_MASK) - iomap->offset) >> 9); + return (iomap->addr + (pos & PAGE_MASK) - iomap->offset) >> 9; } static loff_t diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c index 4dca6f348714..1b8fc73de4a1 100644 --- a/fs/ext2/inode.c +++ b/fs/ext2/inode.c @@ -820,11 +820,11 @@ static int ext2_iomap_begin(struct inode *inode, loff_t offset, loff_t length, if (ret == 0) { iomap->type = IOMAP_HOLE; - iomap->blkno = IOMAP_NULL_BLOCK; + iomap->addr = IOMAP_NULL_ADDR; iomap->length = 1 << blkbits; } else { iomap->type = IOMAP_MAPPED; - iomap->blkno = (sector_t)bno << (blkbits - 9); + iomap->addr = (u64)bno << blkbits; iomap->length = (u64)ret << blkbits; iomap->flags |= IOMAP_F_MERGED; } diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 31db875bc7a1..d9e633c12aae 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3472,7 +3472,7 @@ retry: if (ret == 0) { iomap->type = IOMAP_HOLE; - iomap->blkno = IOMAP_NULL_BLOCK; + iomap->addr = IOMAP_NULL_ADDR; iomap->length = (u64)map.m_len << blkbits; } else { if (map.m_flags & EXT4_MAP_MAPPED) { @@ -3483,7 +3483,7 @@ retry: WARN_ON_ONCE(1); return -EIO; } - iomap->blkno = (sector_t)map.m_pblk << (blkbits - 9); + iomap->addr = (u64)map.m_pblk << blkbits; iomap->length = (u64)map.m_len << blkbits; } diff --git a/fs/iomap.c b/fs/iomap.c index be61cf742b5e..d25db039ee13 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -350,8 +350,8 @@ static int iomap_zero(struct inode *inode, loff_t pos, unsigned offset, static int iomap_dax_zero(loff_t pos, unsigned offset, unsigned bytes, struct iomap *iomap) { - sector_t sector = iomap->blkno + - (((pos & ~(PAGE_SIZE - 1)) - iomap->offset) >> 9); + sector_t sector = (iomap->addr + + (pos & PAGE_MASK) - iomap->offset) >> 9; return __dax_zero_page_range(iomap->bdev, iomap->dax_dev, sector, offset, bytes); @@ -512,9 +512,8 @@ static int iomap_to_fiemap(struct fiemap_extent_info *fi, flags |= FIEMAP_EXTENT_SHARED; return fiemap_fill_next_extent(fi, iomap->offset, - iomap->blkno != IOMAP_NULL_BLOCK ? iomap->blkno << 9: 0, + iomap->addr != IOMAP_NULL_ADDR ? iomap->addr : 0, iomap->length, flags); - } static loff_t @@ -823,7 +822,7 @@ iomap_dio_zero(struct iomap_dio *dio, struct iomap *iomap, loff_t pos, bio = bio_alloc(GFP_KERNEL, 1); bio_set_dev(bio, iomap->bdev); bio->bi_iter.bi_sector = - iomap->blkno + ((pos - iomap->offset) >> 9); + (iomap->addr + pos - iomap->offset) >> 9; bio->bi_private = dio; bio->bi_end_io = iomap_dio_bio_end_io; @@ -902,7 +901,7 @@ iomap_dio_actor(struct inode *inode, loff_t pos, loff_t length, bio = bio_alloc(GFP_KERNEL, nr_pages); bio_set_dev(bio, iomap->bdev); bio->bi_iter.bi_sector = - iomap->blkno + ((pos - iomap->offset) >> 9); + (iomap->addr + pos - iomap->offset) >> 9; bio->bi_write_hint = dio->iocb->ki_hint; bio->bi_private = dio; bio->bi_end_io = iomap_dio_bio_end_io; diff --git a/fs/nfsd/blocklayout.c b/fs/nfsd/blocklayout.c index c862c2489df0..2d1d37b27dc7 100644 --- a/fs/nfsd/blocklayout.c +++ b/fs/nfsd/blocklayout.c @@ -65,7 +65,7 @@ nfsd4_block_proc_layoutget(struct inode *inode, const struct svc_fh *fhp, bex->es = PNFS_BLOCK_READ_DATA; else bex->es = PNFS_BLOCK_READWRITE_DATA; - bex->soff = (iomap.blkno << 9); + bex->soff = iomap.addr; break; case IOMAP_UNWRITTEN: if (seg->iomode & IOMODE_RW) { @@ -78,7 +78,7 @@ nfsd4_block_proc_layoutget(struct inode *inode, const struct svc_fh *fhp, } bex->es = PNFS_BLOCK_INVALID_DATA; - bex->soff = (iomap.blkno << 9); + bex->soff = iomap.addr; break; } /*FALLTHRU*/ diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index f179bdf1644d..9744b4819e0d 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -54,13 +54,13 @@ xfs_bmbt_to_iomap( struct xfs_mount *mp = ip->i_mount; if (imap->br_startblock == HOLESTARTBLOCK) { - iomap->blkno = IOMAP_NULL_BLOCK; + iomap->addr = IOMAP_NULL_ADDR; iomap->type = IOMAP_HOLE; } else if (imap->br_startblock == DELAYSTARTBLOCK) { - iomap->blkno = IOMAP_NULL_BLOCK; + iomap->addr = IOMAP_NULL_ADDR; iomap->type = IOMAP_DELALLOC; } else { - iomap->blkno = xfs_fsb_to_db(ip, imap->br_startblock); + iomap->addr = BBTOB(xfs_fsb_to_db(ip, imap->br_startblock)); if (imap->br_state == XFS_EXT_UNWRITTEN) iomap->type = IOMAP_UNWRITTEN; else diff --git a/include/linux/iomap.h b/include/linux/iomap.h index f64dc6ce5161..7b8a615fa021 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -15,8 +15,8 @@ struct vm_fault; */ #define IOMAP_HOLE 0x01 /* no blocks allocated, need allocation */ #define IOMAP_DELALLOC 0x02 /* delayed allocation blocks */ -#define IOMAP_MAPPED 0x03 /* blocks allocated @blkno */ -#define IOMAP_UNWRITTEN 0x04 /* blocks allocated @blkno in unwritten state */ +#define IOMAP_MAPPED 0x03 /* blocks allocated at @addr */ +#define IOMAP_UNWRITTEN 0x04 /* blocks allocated at @addr in unwritten state */ /* * Flags for all iomap mappings: @@ -30,12 +30,12 @@ struct vm_fault; #define IOMAP_F_SHARED 0x20 /* block shared with another file */ /* - * Magic value for blkno: + * Magic value for addr: */ -#define IOMAP_NULL_BLOCK -1LL /* blkno is not valid */ +#define IOMAP_NULL_ADDR -1ULL /* addr is not valid */ struct iomap { - sector_t blkno; /* 1st sector of mapping, 512b units */ + u64 addr; /* disk offset of mapping, bytes */ loff_t offset; /* file offset of mapping, bytes */ u64 length; /* length of mapping, bytes */ u16 type; /* type of mapping */ -- cgit v1.2.3-59-g8ed1b From 9ca250a5137f3df7ffac58b49660beadd5926ace Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Sun, 1 Oct 2017 17:56:54 -0400 Subject: iomap: Add IOMAP_F_DATA_INLINE flag Add a new IOMAP_F_DATA_INLINE flag to indicate that a mapping is in a disk area that contains data as well as metadata. In iomap_fiemap, map this flag to FIEMAP_EXTENT_DATA_INLINE. Signed-off-by: Andreas Gruenbacher Signed-off-by: Theodore Ts'o Reviewed-by: Jan Kara --- fs/iomap.c | 2 ++ include/linux/iomap.h | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/iomap.c b/fs/iomap.c index d25db039ee13..379eb9896093 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -510,6 +510,8 @@ static int iomap_to_fiemap(struct fiemap_extent_info *fi, flags |= FIEMAP_EXTENT_MERGED; if (iomap->flags & IOMAP_F_SHARED) flags |= FIEMAP_EXTENT_SHARED; + if (iomap->flags & IOMAP_F_DATA_INLINE) + flags |= FIEMAP_EXTENT_DATA_INLINE; return fiemap_fill_next_extent(fi, iomap->offset, iomap->addr != IOMAP_NULL_ADDR ? iomap->addr : 0, diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 7b8a615fa021..2b0790dbd6ea 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -26,8 +26,9 @@ struct vm_fault; /* * Flags that only need to be reported for IOMAP_REPORT requests: */ -#define IOMAP_F_MERGED 0x10 /* contains multiple blocks/extents */ -#define IOMAP_F_SHARED 0x20 /* block shared with another file */ +#define IOMAP_F_MERGED 0x10 /* contains multiple blocks/extents */ +#define IOMAP_F_SHARED 0x20 /* block shared with another file */ +#define IOMAP_F_DATA_INLINE 0x40 /* data inline in the inode */ /* * Magic value for addr: -- cgit v1.2.3-59-g8ed1b From e1cfcbe82b4534bd0f99fef92a6d33843fd85e0e Mon Sep 17 00:00:00 2001 From: Haishuang Yan Date: Wed, 27 Sep 2017 11:35:40 +0800 Subject: ipv4: Namespaceify tcp_fastopen knob Different namespace application might require enable TCP Fast Open feature independently of the host. This patch series continues making more of the TCP Fast Open related sysctl knobs be per net-namespace. Reported-by: Luca BRUNO Signed-off-by: Haishuang Yan Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/af_inet.c | 7 ++++--- net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp.c | 4 ++-- net/ipv4/tcp_fastopen.c | 11 +++++------ net/ipv4/tcp_ipv4.c | 2 ++ 7 files changed, 21 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index abc84d986da4..16420ccaef15 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -128,6 +128,7 @@ struct netns_ipv4 { int sysctl_tcp_timestamps; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; + int sysctl_tcp_fastopen; #ifdef CONFIG_NET_L3_MASTER_DEV int sysctl_udp_l3mdev_accept; diff --git a/include/net/tcp.h b/include/net/tcp.h index 770b608c8439..9e414a99034f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -240,7 +240,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* sysctl variables for tcp */ -extern int sysctl_tcp_fastopen; extern int sysctl_tcp_retrans_collapse; extern int sysctl_tcp_stdurg; extern int sysctl_tcp_rfc1337; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index e31108e5ef79..ddd126d120ac 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -195,7 +195,7 @@ int inet_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; unsigned char old_state; - int err; + int err, tcp_fastopen; lock_sock(sk); @@ -217,8 +217,9 @@ int inet_listen(struct socket *sock, int backlog) * because the socket was in TCP_LISTEN state previously but * was shutdown() rather than close(). */ - if ((sysctl_tcp_fastopen & TFO_SERVER_WO_SOCKOPT1) && - (sysctl_tcp_fastopen & TFO_SERVER_ENABLE) && + tcp_fastopen = sock_net(sk)->ipv4.sysctl_tcp_fastopen; + if ((tcp_fastopen & TFO_SERVER_WO_SOCKOPT1) && + (tcp_fastopen & TFO_SERVER_ENABLE) && !inet_csk(sk)->icsk_accept_queue.fastopenq.max_qlen) { fastopen_queue_tune(sk, backlog); tcp_fastopen_init_key_once(true); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 0d3c038d7b04..e31e853cf486 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -400,13 +400,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_fastopen", - .data = &sysctl_tcp_fastopen, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "tcp_fastopen_key", .mode = 0600, @@ -1085,6 +1078,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_fastopen", + .data = &init_net.ipv4.sysctl_tcp_fastopen, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, #ifdef CONFIG_IP_ROUTE_MULTIPATH { .procname = "fib_multipath_use_neigh", diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 5091402720ab..dac56c4ad357 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1126,7 +1126,7 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, struct sockaddr *uaddr = msg->msg_name; int err, flags; - if (!(sysctl_tcp_fastopen & TFO_CLIENT_ENABLE) || + if (!(sock_net(sk)->ipv4.sysctl_tcp_fastopen & TFO_CLIENT_ENABLE) || (uaddr && msg->msg_namelen >= sizeof(uaddr->sa_family) && uaddr->sa_family == AF_UNSPEC)) return -EOPNOTSUPP; @@ -2759,7 +2759,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level, case TCP_FASTOPEN_CONNECT: if (val > 1 || val < 0) { err = -EINVAL; - } else if (sysctl_tcp_fastopen & TFO_CLIENT_ENABLE) { + } else if (net->ipv4.sysctl_tcp_fastopen & TFO_CLIENT_ENABLE) { if (sk->sk_state == TCP_CLOSE) tp->fastopen_connect = val; else diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index e3c33220c418..31b08ec38cb8 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -9,8 +9,6 @@ #include #include -int sysctl_tcp_fastopen __read_mostly = TFO_CLIENT_ENABLE; - struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; static DEFINE_SPINLOCK(tcp_fastopen_ctx_lock); @@ -279,21 +277,22 @@ struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct tcp_fastopen_cookie *foc) { - struct tcp_fastopen_cookie valid_foc = { .len = -1 }; bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1; + int tcp_fastopen = sock_net(sk)->ipv4.sysctl_tcp_fastopen; + struct tcp_fastopen_cookie valid_foc = { .len = -1 }; struct sock *child; if (foc->len == 0) /* Client requests a cookie */ NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFASTOPENCOOKIEREQD); - if (!((sysctl_tcp_fastopen & TFO_SERVER_ENABLE) && + if (!((tcp_fastopen & TFO_SERVER_ENABLE) && (syn_data || foc->len >= 0) && tcp_fastopen_queue_check(sk))) { foc->len = -1; return NULL; } - if (syn_data && (sysctl_tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD)) + if (syn_data && (tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD)) goto fastopen; if (foc->len >= 0 && /* Client presents or requests a cookie */ @@ -347,7 +346,7 @@ bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss, return false; } - if (sysctl_tcp_fastopen & TFO_CLIENT_NO_COOKIE) { + if (sock_net(sk)->ipv4.sysctl_tcp_fastopen & TFO_CLIENT_NO_COOKIE) { cookie->len = -1; return true; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d9416b5162bc..88409b13c9d2 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2472,6 +2472,8 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_window_scaling = 1; net->ipv4.sysctl_tcp_timestamps = 1; + net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; + return 0; fail: tcp_sk_exit(net); -- cgit v1.2.3-59-g8ed1b From dd000598a39b6937fcefdf143720ec9fb5250e72 Mon Sep 17 00:00:00 2001 From: Haishuang Yan Date: Wed, 27 Sep 2017 11:35:41 +0800 Subject: ipv4: Remove the 'publish' logic in tcp_fastopen_init_key_once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'publish' logic is not necessary after commit dfea2aa65424 ("tcp: Do not call tcp_fastopen_reset_cipher from interrupt context"), because in tcp_fastopen_cookie_gen,it wouldn't call tcp_fastopen_init_key_once. Signed-off-by: Haishuang Yan Signed-off-by: David S. Miller --- include/net/tcp.h | 2 +- net/ipv4/af_inet.c | 2 +- net/ipv4/sysctl_net_ipv4.c | 5 ----- net/ipv4/tcp.c | 2 +- net/ipv4/tcp_fastopen.c | 4 ++-- 5 files changed, 5 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 9e414a99034f..d9376e2458e9 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1555,7 +1555,7 @@ void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb); struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct tcp_fastopen_cookie *foc); -void tcp_fastopen_init_key_once(bool publish); +void tcp_fastopen_init_key_once(void); bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss, struct tcp_fastopen_cookie *cookie); bool tcp_fastopen_defer_connect(struct sock *sk, int *err); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index ddd126d120ac..e73ce79d7176 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -222,7 +222,7 @@ int inet_listen(struct socket *sock, int backlog) (tcp_fastopen & TFO_SERVER_ENABLE) && !inet_csk(sk)->icsk_accept_queue.fastopenq.max_qlen) { fastopen_queue_tune(sk, backlog); - tcp_fastopen_init_key_once(true); + tcp_fastopen_init_key_once(); } err = inet_csk_listen_start(sk, backlog); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index e31e853cf486..f6324ead0e19 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -282,11 +282,6 @@ static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write, ret = -EINVAL; goto bad_key; } - /* Generate a dummy secret but don't publish it. This - * is needed so we don't regenerate a new key on the - * first invocation of tcp_fastopen_cookie_gen - */ - tcp_fastopen_init_key_once(false); tcp_fastopen_reset_cipher(user_key, TCP_FASTOPEN_KEY_LENGTH); } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index dac56c4ad357..4e395452d69f 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2749,7 +2749,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level, case TCP_FASTOPEN: if (val >= 0 && ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) { - tcp_fastopen_init_key_once(true); + tcp_fastopen_init_key_once(); fastopen_queue_tune(sk, val); } else { diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 31b08ec38cb8..8c8f0f0af59d 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -13,7 +13,7 @@ struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; static DEFINE_SPINLOCK(tcp_fastopen_ctx_lock); -void tcp_fastopen_init_key_once(bool publish) +void tcp_fastopen_init_key_once(void) { static u8 key[TCP_FASTOPEN_KEY_LENGTH]; @@ -23,7 +23,7 @@ void tcp_fastopen_init_key_once(bool publish) * All call sites of tcp_fastopen_cookie_gen also check * for a valid cookie, so this is an acceptable risk. */ - if (net_get_random_once(key, sizeof(key)) && publish) + if (net_get_random_once(key, sizeof(key))) tcp_fastopen_reset_cipher(key, sizeof(key)); } -- cgit v1.2.3-59-g8ed1b From 437138485656c41e32b8c63c0987cfa0348be0e6 Mon Sep 17 00:00:00 2001 From: Haishuang Yan Date: Wed, 27 Sep 2017 11:35:42 +0800 Subject: ipv4: Namespaceify tcp_fastopen_key knob Different namespace application might require different tcp_fastopen_key independently of the host. David Miller pointed out there is a leak without releasing the context of tcp_fastopen_key during netns teardown. So add the release action in exit_batch path. Tested: 1. Container namespace: # cat /proc/sys/net/ipv4/tcp_fastopen_key: 2817fff2-f803cf97-eadfd1f3-78c0992b cookie key in tcp syn packets: Fast Open Cookie Kind: TCP Fast Open Cookie (34) Length: 10 Fast Open Cookie: 1e5dd82a8c492ca9 2. Host: # cat /proc/sys/net/ipv4/tcp_fastopen_key: 107d7c5f-68eb2ac7-02fb06e6-ed341702 cookie key in tcp syn packets: Fast Open Cookie Kind: TCP Fast Open Cookie (34) Length: 10 Fast Open Cookie: e213c02bf0afbc8a Signed-off-by: Haishuang Yan Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 4 +++ include/net/tcp.h | 6 ++--- net/ipv4/af_inet.c | 2 +- net/ipv4/sysctl_net_ipv4.c | 21 ++++++++------- net/ipv4/tcp.c | 2 +- net/ipv4/tcp_fastopen.c | 64 +++++++++++++++++++++++++++++++--------------- net/ipv4/tcp_ipv4.c | 6 +++++ 7 files changed, 70 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 16420ccaef15..7bb9603ff66c 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -36,6 +36,8 @@ struct inet_timewait_death_row { int sysctl_max_tw_buckets; }; +struct tcp_fastopen_context; + struct netns_ipv4 { #ifdef CONFIG_SYSCTL struct ctl_table_header *forw_hdr; @@ -129,6 +131,8 @@ struct netns_ipv4 { struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; + struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; + spinlock_t tcp_fastopen_ctx_lock; #ifdef CONFIG_NET_L3_MASTER_DEV int sysctl_udp_l3mdev_accept; diff --git a/include/net/tcp.h b/include/net/tcp.h index d9376e2458e9..6d25d8305054 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1549,13 +1549,13 @@ struct tcp_fastopen_request { }; void tcp_free_fastopen_req(struct tcp_sock *tp); -extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; -int tcp_fastopen_reset_cipher(void *key, unsigned int len); +void tcp_fastopen_ctx_destroy(struct net *net); +int tcp_fastopen_reset_cipher(struct net *net, void *key, unsigned int len); void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb); struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct tcp_fastopen_cookie *foc); -void tcp_fastopen_init_key_once(void); +void tcp_fastopen_init_key_once(struct net *net); bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss, struct tcp_fastopen_cookie *cookie); bool tcp_fastopen_defer_connect(struct sock *sk, int *err); diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index e73ce79d7176..43a1bbed7a42 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -222,7 +222,7 @@ int inet_listen(struct socket *sock, int backlog) (tcp_fastopen & TFO_SERVER_ENABLE) && !inet_csk(sk)->icsk_accept_queue.fastopenq.max_qlen) { fastopen_queue_tune(sk, backlog); - tcp_fastopen_init_key_once(); + tcp_fastopen_init_key_once(sock_net(sk)); } err = inet_csk_listen_start(sk, backlog); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index f6324ead0e19..20e19fe78dbd 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -251,10 +251,12 @@ static int proc_allowed_congestion_control(struct ctl_table *ctl, return ret; } -static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write, +static int proc_tcp_fastopen_key(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { + struct net *net = container_of(table->data, struct net, + ipv4.sysctl_tcp_fastopen); struct ctl_table tbl = { .maxlen = (TCP_FASTOPEN_KEY_LENGTH * 2 + 10) }; struct tcp_fastopen_context *ctxt; int ret; @@ -265,7 +267,7 @@ static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write, return -ENOMEM; rcu_read_lock(); - ctxt = rcu_dereference(tcp_fastopen_ctx); + ctxt = rcu_dereference(net->ipv4.tcp_fastopen_ctx); if (ctxt) memcpy(user_key, ctxt->key, TCP_FASTOPEN_KEY_LENGTH); else @@ -282,7 +284,7 @@ static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write, ret = -EINVAL; goto bad_key; } - tcp_fastopen_reset_cipher(user_key, TCP_FASTOPEN_KEY_LENGTH); + tcp_fastopen_reset_cipher(net, user_key, TCP_FASTOPEN_KEY_LENGTH); } bad_key: @@ -395,12 +397,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_fastopen_key", - .mode = 0600, - .maxlen = ((TCP_FASTOPEN_KEY_LENGTH * 2) + 10), - .proc_handler = proc_tcp_fastopen_key, - }, { .procname = "tcp_fastopen_blackhole_timeout_sec", .data = &sysctl_tcp_fastopen_blackhole_timeout, @@ -1080,6 +1076,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "tcp_fastopen_key", + .mode = 0600, + .data = &init_net.ipv4.sysctl_tcp_fastopen, + .maxlen = ((TCP_FASTOPEN_KEY_LENGTH * 2) + 10), + .proc_handler = proc_tcp_fastopen_key, + }, #ifdef CONFIG_IP_ROUTE_MULTIPATH { .procname = "fib_multipath_use_neigh", diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 4e395452d69f..23225c98d287 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2749,7 +2749,7 @@ static int do_tcp_setsockopt(struct sock *sk, int level, case TCP_FASTOPEN: if (val >= 0 && ((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) { - tcp_fastopen_init_key_once(); + tcp_fastopen_init_key_once(net); fastopen_queue_tune(sk, val); } else { diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 8c8f0f0af59d..4eae44ac3cb0 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -9,13 +9,18 @@ #include #include -struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; - -static DEFINE_SPINLOCK(tcp_fastopen_ctx_lock); - -void tcp_fastopen_init_key_once(void) +void tcp_fastopen_init_key_once(struct net *net) { - static u8 key[TCP_FASTOPEN_KEY_LENGTH]; + u8 key[TCP_FASTOPEN_KEY_LENGTH]; + struct tcp_fastopen_context *ctxt; + + rcu_read_lock(); + ctxt = rcu_dereference(net->ipv4.tcp_fastopen_ctx); + if (ctxt) { + rcu_read_unlock(); + return; + } + rcu_read_unlock(); /* tcp_fastopen_reset_cipher publishes the new context * atomically, so we allow this race happening here. @@ -23,8 +28,8 @@ void tcp_fastopen_init_key_once(void) * All call sites of tcp_fastopen_cookie_gen also check * for a valid cookie, so this is an acceptable risk. */ - if (net_get_random_once(key, sizeof(key))) - tcp_fastopen_reset_cipher(key, sizeof(key)); + get_random_bytes(key, sizeof(key)); + tcp_fastopen_reset_cipher(net, key, sizeof(key)); } static void tcp_fastopen_ctx_free(struct rcu_head *head) @@ -35,7 +40,22 @@ static void tcp_fastopen_ctx_free(struct rcu_head *head) kfree(ctx); } -int tcp_fastopen_reset_cipher(void *key, unsigned int len) +void tcp_fastopen_ctx_destroy(struct net *net) +{ + struct tcp_fastopen_context *ctxt; + + spin_lock(&net->ipv4.tcp_fastopen_ctx_lock); + + ctxt = rcu_dereference_protected(net->ipv4.tcp_fastopen_ctx, + lockdep_is_held(&net->ipv4.tcp_fastopen_ctx_lock)); + rcu_assign_pointer(net->ipv4.tcp_fastopen_ctx, NULL); + spin_unlock(&net->ipv4.tcp_fastopen_ctx_lock); + + if (ctxt) + call_rcu(&ctxt->rcu, tcp_fastopen_ctx_free); +} + +int tcp_fastopen_reset_cipher(struct net *net, void *key, unsigned int len) { int err; struct tcp_fastopen_context *ctx, *octx; @@ -59,26 +79,27 @@ error: kfree(ctx); } memcpy(ctx->key, key, len); - spin_lock(&tcp_fastopen_ctx_lock); + spin_lock(&net->ipv4.tcp_fastopen_ctx_lock); - octx = rcu_dereference_protected(tcp_fastopen_ctx, - lockdep_is_held(&tcp_fastopen_ctx_lock)); - rcu_assign_pointer(tcp_fastopen_ctx, ctx); - spin_unlock(&tcp_fastopen_ctx_lock); + octx = rcu_dereference_protected(net->ipv4.tcp_fastopen_ctx, + lockdep_is_held(&net->ipv4.tcp_fastopen_ctx_lock)); + rcu_assign_pointer(net->ipv4.tcp_fastopen_ctx, ctx); + spin_unlock(&net->ipv4.tcp_fastopen_ctx_lock); if (octx) call_rcu(&octx->rcu, tcp_fastopen_ctx_free); return err; } -static bool __tcp_fastopen_cookie_gen(const void *path, +static bool __tcp_fastopen_cookie_gen(struct net *net, + const void *path, struct tcp_fastopen_cookie *foc) { struct tcp_fastopen_context *ctx; bool ok = false; rcu_read_lock(); - ctx = rcu_dereference(tcp_fastopen_ctx); + ctx = rcu_dereference(net->ipv4.tcp_fastopen_ctx); if (ctx) { crypto_cipher_encrypt_one(ctx->tfm, foc->val, path); foc->len = TCP_FASTOPEN_COOKIE_SIZE; @@ -94,7 +115,8 @@ static bool __tcp_fastopen_cookie_gen(const void *path, * * XXX (TFO) - refactor when TCP_FASTOPEN_COOKIE_SIZE != AES_BLOCK_SIZE. */ -static bool tcp_fastopen_cookie_gen(struct request_sock *req, +static bool tcp_fastopen_cookie_gen(struct net *net, + struct request_sock *req, struct sk_buff *syn, struct tcp_fastopen_cookie *foc) { @@ -102,7 +124,7 @@ static bool tcp_fastopen_cookie_gen(struct request_sock *req, const struct iphdr *iph = ip_hdr(syn); __be32 path[4] = { iph->saddr, iph->daddr, 0, 0 }; - return __tcp_fastopen_cookie_gen(path, foc); + return __tcp_fastopen_cookie_gen(net, path, foc); } #if IS_ENABLED(CONFIG_IPV6) @@ -110,13 +132,13 @@ static bool tcp_fastopen_cookie_gen(struct request_sock *req, const struct ipv6hdr *ip6h = ipv6_hdr(syn); struct tcp_fastopen_cookie tmp; - if (__tcp_fastopen_cookie_gen(&ip6h->saddr, &tmp)) { + if (__tcp_fastopen_cookie_gen(net, &ip6h->saddr, &tmp)) { struct in6_addr *buf = &tmp.addr; int i; for (i = 0; i < 4; i++) buf->s6_addr32[i] ^= ip6h->daddr.s6_addr32[i]; - return __tcp_fastopen_cookie_gen(buf, foc); + return __tcp_fastopen_cookie_gen(net, buf, foc); } } #endif @@ -296,7 +318,7 @@ struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, goto fastopen; if (foc->len >= 0 && /* Client presents or requests a cookie */ - tcp_fastopen_cookie_gen(req, skb, &valid_foc) && + tcp_fastopen_cookie_gen(sock_net(sk), req, skb, &valid_foc) && foc->len == TCP_FASTOPEN_COOKIE_SIZE && foc->len == valid_foc.len && !memcmp(foc->val, valid_foc.val, foc->len)) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 88409b13c9d2..49c74c0d0d21 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2473,6 +2473,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_timestamps = 1; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; + spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); return 0; fail: @@ -2483,7 +2484,12 @@ fail: static void __net_exit tcp_sk_exit_batch(struct list_head *net_exit_list) { + struct net *net; + inet_twsk_purge(&tcp_hashinfo, AF_INET); + + list_for_each_entry(net, net_exit_list, exit_list) + tcp_fastopen_ctx_destroy(net); } static struct pernet_operations __net_initdata tcp_sk_ops = { -- cgit v1.2.3-59-g8ed1b From 3733be14a32bae288b61ed28341e593baba983af Mon Sep 17 00:00:00 2001 From: Haishuang Yan Date: Wed, 27 Sep 2017 11:35:43 +0800 Subject: ipv4: Namespaceify tcp_fastopen_blackhole_timeout knob Different namespace application might require different time period in second to disable Fastopen on active TCP sockets. Tested: Simulate following similar situation that the server's data gets dropped after 3WHS. C ---- syn-data ---> S C <--- syn/ack ----- S C ---- ack --------> S S (accept & write) C? X <- data ------ S [retry and timeout] And then print netstat of TCPFastOpenBlackhole, the counter increased as expected when the firewall blackhole issue is detected and active TFO is disabled. # cat /proc/net/netstat | awk '{print $91}' TCPFastOpenBlackhole 1 Signed-off-by: Haishuang Yan Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 3 +++ net/ipv4/sysctl_net_ipv4.c | 20 +++++++++++--------- net/ipv4/tcp_fastopen.c | 30 +++++++++++------------------- net/ipv4/tcp_ipv4.c | 2 ++ 4 files changed, 27 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 7bb9603ff66c..2c4222a5d102 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -133,6 +133,9 @@ struct netns_ipv4 { int sysctl_tcp_fastopen; struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; spinlock_t tcp_fastopen_ctx_lock; + unsigned int sysctl_tcp_fastopen_blackhole_timeout; + atomic_t tfo_active_disable_times; + unsigned long tfo_active_disable_stamp; #ifdef CONFIG_NET_L3_MASTER_DEV int sysctl_udp_l3mdev_accept; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 20e19fe78dbd..cac8dd309f39 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -355,11 +355,13 @@ static int proc_tfo_blackhole_detect_timeout(struct ctl_table *table, void __user *buffer, size_t *lenp, loff_t *ppos) { + struct net *net = container_of(table->data, struct net, + ipv4.sysctl_tcp_fastopen_blackhole_timeout); int ret; ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (write && ret == 0) - tcp_fastopen_active_timeout_reset(); + atomic_set(&net->ipv4.tfo_active_disable_times, 0); return ret; } @@ -397,14 +399,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_fastopen_blackhole_timeout_sec", - .data = &sysctl_tcp_fastopen_blackhole_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_tfo_blackhole_detect_timeout, - .extra1 = &zero, - }, { .procname = "tcp_abort_on_overflow", .data = &sysctl_tcp_abort_on_overflow, @@ -1083,6 +1077,14 @@ static struct ctl_table ipv4_net_table[] = { .maxlen = ((TCP_FASTOPEN_KEY_LENGTH * 2) + 10), .proc_handler = proc_tcp_fastopen_key, }, + { + .procname = "tcp_fastopen_blackhole_timeout_sec", + .data = &init_net.ipv4.sysctl_tcp_fastopen_blackhole_timeout, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_tfo_blackhole_detect_timeout, + .extra1 = &zero, + }, #ifdef CONFIG_IP_ROUTE_MULTIPATH { .procname = "fib_multipath_use_neigh", diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 4eae44ac3cb0..de470e7e586f 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -422,25 +422,16 @@ EXPORT_SYMBOL(tcp_fastopen_defer_connect); * TFO connection with data exchanges. */ -/* Default to 1hr */ -unsigned int sysctl_tcp_fastopen_blackhole_timeout __read_mostly = 60 * 60; -static atomic_t tfo_active_disable_times __read_mostly = ATOMIC_INIT(0); -static unsigned long tfo_active_disable_stamp __read_mostly; - /* Disable active TFO and record current jiffies and * tfo_active_disable_times */ void tcp_fastopen_active_disable(struct sock *sk) { - atomic_inc(&tfo_active_disable_times); - tfo_active_disable_stamp = jiffies; - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPFASTOPENBLACKHOLE); -} + struct net *net = sock_net(sk); -/* Reset tfo_active_disable_times to 0 */ -void tcp_fastopen_active_timeout_reset(void) -{ - atomic_set(&tfo_active_disable_times, 0); + atomic_inc(&net->ipv4.tfo_active_disable_times); + net->ipv4.tfo_active_disable_stamp = jiffies; + NET_INC_STATS(net, LINUX_MIB_TCPFASTOPENBLACKHOLE); } /* Calculate timeout for tfo active disable @@ -449,17 +440,18 @@ void tcp_fastopen_active_timeout_reset(void) */ bool tcp_fastopen_active_should_disable(struct sock *sk) { - int tfo_da_times = atomic_read(&tfo_active_disable_times); - int multiplier; + unsigned int tfo_bh_timeout = sock_net(sk)->ipv4.sysctl_tcp_fastopen_blackhole_timeout; + int tfo_da_times = atomic_read(&sock_net(sk)->ipv4.tfo_active_disable_times); unsigned long timeout; + int multiplier; if (!tfo_da_times) return false; /* Limit timout to max: 2^6 * initial timeout */ multiplier = 1 << min(tfo_da_times - 1, 6); - timeout = multiplier * sysctl_tcp_fastopen_blackhole_timeout * HZ; - if (time_before(jiffies, tfo_active_disable_stamp + timeout)) + timeout = multiplier * tfo_bh_timeout * HZ; + if (time_before(jiffies, sock_net(sk)->ipv4.tfo_active_disable_stamp + timeout)) return true; /* Mark check bit so we can check for successful active TFO @@ -495,10 +487,10 @@ void tcp_fastopen_active_disable_ofo_check(struct sock *sk) } } } else if (tp->syn_fastopen_ch && - atomic_read(&tfo_active_disable_times)) { + atomic_read(&sock_net(sk)->ipv4.tfo_active_disable_times)) { dst = sk_dst_get(sk); if (!(dst && dst->dev && (dst->dev->flags & IFF_LOOPBACK))) - tcp_fastopen_active_timeout_reset(); + atomic_set(&sock_net(sk)->ipv4.tfo_active_disable_times, 0); dst_release(dst); } } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 49c74c0d0d21..ad3b5bbaf942 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2474,6 +2474,8 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); + net->ipv4.sysctl_tcp_fastopen_blackhole_timeout = 60 * 60; + atomic_set(&net->ipv4.tfo_active_disable_times, 0); return 0; fail: -- cgit v1.2.3-59-g8ed1b From b80ccfe9bbcac70e66fdfaef73f0988a27f9a68c Mon Sep 17 00:00:00 2001 From: Maciej Żenczykowski Date: Tue, 26 Sep 2017 20:37:22 -0700 Subject: net-ipv6: remove unused IP6_ECN_clear() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is unused, and furthermore it is buggy since it suffers from the same issue that requires IP6_ECN_set_ce() to take a pointer to the skb so that it may (in case of CHECKSUM_COMPLETE) update skb->csum Instead of fixing it, let's just outright remove it. Tested: builds, and 'git grep IP6_ECN_clear' comes up empty Signed-off-by: Maciej Żenczykowski Signed-off-by: David S. Miller --- include/net/inet_ecn.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/net/inet_ecn.h b/include/net/inet_ecn.h index dce2d586d9ce..f5ff16d72fe6 100644 --- a/include/net/inet_ecn.h +++ b/include/net/inet_ecn.h @@ -133,11 +133,6 @@ static inline int IP6_ECN_set_ce(struct sk_buff *skb, struct ipv6hdr *iph) return 1; } -static inline void IP6_ECN_clear(struct ipv6hdr *iph) -{ - *(__be32*)iph &= ~htonl(INET_ECN_MASK << 20); -} - static inline void ipv6_copy_dscp(unsigned int dscp, struct ipv6hdr *inner) { dscp &= ~INET_ECN_MASK; -- cgit v1.2.3-59-g8ed1b From 4f210c2938053e894ee00500dd0661f7a27a6ff4 Mon Sep 17 00:00:00 2001 From: Jaejoong Kim Date: Wed, 20 Sep 2017 18:40:36 +0900 Subject: HID: add comment for power callback in struct hid_ll_driver There is a missing comment in struct hid_ll_driver. So, add it. Signed-off-by: Jaejoong Kim Signed-off-by: Jiri Kosina --- include/linux/hid.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/hid.h b/include/linux/hid.h index ab05a86269dc..7c3d4a17bbde 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -753,6 +753,7 @@ struct hid_driver { * @stop: called on remove * @open: called by input layer on open * @close: called by input layer on close + * @power: request underlying hardware to enter requested power mode * @parse: this method is called only once to parse the device data, * shouldn't allocate anything to not leak memory * @request: send report request to device (e.g. feature report) -- cgit v1.2.3-59-g8ed1b From 66b1bedf662518e9b6367990a87e9601b35a94c1 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Fri, 29 Sep 2017 14:21:14 +0300 Subject: ieee80211: Add WFA TPC report element OUI type Add Transmit Power Control OUI type definition for WLAN_OUI_MICROSOFT. Signed-off-by: Avraham Stern Signed-off-by: Luca Coelho Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 55a604ad459f..ee6657a0ed69 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -2445,6 +2445,7 @@ enum ieee80211_sa_query_action { #define WLAN_OUI_TYPE_MICROSOFT_WPA 1 #define WLAN_OUI_TYPE_MICROSOFT_WMM 2 #define WLAN_OUI_TYPE_MICROSOFT_WPS 4 +#define WLAN_OUI_TYPE_MICROSOFT_TPC 8 /* * WMM/802.11e Tspec Element -- cgit v1.2.3-59-g8ed1b From 503c1fb98ba3859c13863957c7c65c92371a9e50 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Fri, 29 Sep 2017 14:21:49 +0200 Subject: cfg80211/nl80211: add a port authorized event Add an event that indicates that a connection is authorized (i.e. the 4 way handshake was performed by the driver). This event should be sent by the driver after sending a connect/roamed event. This is useful for networks that require 802.1X authentication. In cases that the driver supports 4 way handshake offload, but the 802.1X authentication is managed by user space, the driver needs to inform user space right after the 802.11 association was completed so user space can initialize its 802.1X state machine etc. However, it is also possible that the AP will choose to skip the 802.1X authentication (e.g. when PMKSA caching is used) and proceed with the 4 way handshake immediately. In this case the driver needs to inform user space that 802.1X authentication is no longer required (e.g. to prevent user space from disconnecting since it did not get any EAPOLs from the AP). This is also useful for roaming, in which case it is possible that the driver used the Fast Transition protocol so 802.1X is not required. Since there will now be a dedicated notification indicating that the connection is authorized, the authorized flag can be removed from the roamed event. Drivers can send the new port authorized event right after sending the roamed event to indicate the new AP is already authorized. This therefore reserves the old PORT_AUTHORIZED attribute. Signed-off-by: Avraham Stern Signed-off-by: Johannes Berg --- include/net/cfg80211.h | 21 +++++++++++++++++---- include/uapi/linux/nl80211.h | 28 ++++++++++++++++----------- net/wireless/core.h | 5 +++++ net/wireless/nl80211.c | 34 ++++++++++++++++++++++++++++++--- net/wireless/nl80211.h | 2 ++ net/wireless/sme.c | 45 +++++++++++++++++++++++++++++++++++++++++++- net/wireless/util.c | 3 +++ 7 files changed, 119 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index cc1996081463..8b8118a7fadb 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -5428,9 +5428,6 @@ cfg80211_connect_timeout(struct net_device *dev, const u8 *bssid, * @req_ie_len: association request IEs length * @resp_ie: association response IEs (may be %NULL) * @resp_ie_len: assoc response IEs length - * @authorized: true if the 802.1X authentication was done by the driver or is - * not needed (e.g., when Fast Transition protocol was used), false - * otherwise. Ignored for networks that don't use 802.1X authentication. */ struct cfg80211_roam_info { struct ieee80211_channel *channel; @@ -5440,7 +5437,6 @@ struct cfg80211_roam_info { size_t req_ie_len; const u8 *resp_ie; size_t resp_ie_len; - bool authorized; }; /** @@ -5464,6 +5460,23 @@ struct cfg80211_roam_info { void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, gfp_t gfp); +/** + * cfg80211_port_authorized - notify cfg80211 of successful security association + * + * @dev: network device + * @bssid: the BSSID of the AP + * @gfp: allocation flags + * + * This function should be called by a driver that supports 4 way handshake + * offload after a security association was successfully established (i.e., + * the 4 way handshake was completed successfully). The call to this function + * should be preceded with a call to cfg80211_connect_result(), + * cfg80211_connect_done(), cfg80211_connect_bss() or cfg80211_roamed() to + * indicate the 802.11 association. + */ +void cfg80211_port_authorized(struct net_device *dev, const u8 *bssid, + gfp_t gfp); + /** * cfg80211_disconnected - notify cfg80211 that connection was dropped * diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 59ba6ca66a0d..95832ce03a44 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -569,13 +569,14 @@ * authentication/association or not receiving a response from the AP. * Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as * well to remain backwards compatible. - * @NL80211_CMD_ROAM: notifcation indicating the card/driver roamed by itself. - * When the driver roamed in a network that requires 802.1X authentication, - * %NL80211_ATTR_PORT_AUTHORIZED should be set if the 802.1X authentication - * was done by the driver or if roaming was done using Fast Transition - * protocol (in which case 802.1X authentication is not needed). If - * %NL80211_ATTR_PORT_AUTHORIZED is not set, user space is responsible for - * the 802.1X authentication. + * When establishing a security association, drivers that support 4 way + * handshake offload should send %NL80211_CMD_PORT_AUTHORIZED event when + * the 4 way handshake is completed successfully. + * @NL80211_CMD_ROAM: Notification indicating the card/driver roamed by itself. + * When a security association was established with the new AP (e.g. if + * the FT protocol was used for roaming or the driver completed the 4 way + * handshake), this event should be followed by an + * %NL80211_CMD_PORT_AUTHORIZED event. * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify * userspace that a connection was dropped by the AP or due to other * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and @@ -982,6 +983,12 @@ * @NL80211_CMD_DEL_PMK: For offloaded 4-Way handshake, delete the previously * configured PMK for the authenticator address identified by * &NL80211_ATTR_MAC. + * @NL80211_CMD_PORT_AUTHORIZED: An event that indicates that the 4 way + * handshake was completed successfully by the driver. The BSSID is + * specified with &NL80211_ATTR_MAC. Drivers that support 4 way handshake + * offload should send this event after indicating 802.11 association with + * &NL80211_CMD_CONNECT or &NL80211_CMD_ROAM. If the 4 way handshake failed + * &NL80211_CMD_DISCONNECT should be indicated instead. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use @@ -1185,6 +1192,8 @@ enum nl80211_commands { NL80211_CMD_SET_PMK, NL80211_CMD_DEL_PMK, + NL80211_CMD_PORT_AUTHORIZED, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2138,10 +2147,7 @@ enum nl80211_commands { * in %NL80211_CMD_CONNECT to indicate that for 802.1X authentication it * wants to use the supported offload of the 4-way handshake. * @NL80211_ATTR_PMKR0_NAME: PMK-R0 Name for offloaded FT. - * @NL80211_ATTR_PORT_AUTHORIZED: flag attribute used in %NL80211_CMD_ROAMED - * notification indicating that that 802.1X authentication was done by - * the driver or is not needed (because roaming used the Fast Transition - * protocol). + * @NL80211_ATTR_PORT_AUTHORIZED: (reserved) * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined diff --git a/net/wireless/core.h b/net/wireless/core.h index 6e809325af3b..35165f42c2a8 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -216,6 +216,7 @@ enum cfg80211_event_type { EVENT_DISCONNECTED, EVENT_IBSS_JOINED, EVENT_STOPPED, + EVENT_PORT_AUTHORIZED, }; struct cfg80211_event { @@ -235,6 +236,9 @@ struct cfg80211_event { u8 bssid[ETH_ALEN]; struct ieee80211_channel *channel; } ij; + struct { + u8 bssid[ETH_ALEN]; + } pa; }; }; @@ -385,6 +389,7 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev, bool wextev); void __cfg80211_roamed(struct wireless_dev *wdev, struct cfg80211_roam_info *info); +void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid); int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); void cfg80211_autodisconnect_wk(struct work_struct *work); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1e39ba3cfd06..90e212db6889 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -13830,9 +13830,7 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, info->req_ie)) || (info->resp_ie && nla_put(msg, NL80211_ATTR_RESP_IE, info->resp_ie_len, - info->resp_ie)) || - (info->authorized && - nla_put_flag(msg, NL80211_ATTR_PORT_AUTHORIZED))) + info->resp_ie))) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -13846,6 +13844,36 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid) +{ + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PORT_AUTHORIZED); + if (!hdr) { + nlmsg_free(msg); + return; + } + + if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, + NL80211_MCGRP_MLME, GFP_KERNEL); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, const u8 *ie, size_t ie_len, bool from_ap) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index b96933322077..bf9e772a30b9 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -58,6 +58,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, void nl80211_send_roamed(struct cfg80211_registered_device *rdev, struct net_device *netdev, struct cfg80211_roam_info *info, gfp_t gfp); +void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *bssid); void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, struct net_device *netdev, u16 reason, const u8 *ie, size_t ie_len, bool from_ap); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 0a49b88070d0..f38ed490e42b 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -960,7 +960,6 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, ev->rm.resp_ie_len = info->resp_ie_len; memcpy((void *)ev->rm.resp_ie, info->resp_ie, info->resp_ie_len); ev->rm.bss = info->bss; - ev->rm.authorized = info->authorized; spin_lock_irqsave(&wdev->event_lock, flags); list_add_tail(&ev->list, &wdev->event_list); @@ -969,6 +968,50 @@ void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, } EXPORT_SYMBOL(cfg80211_roamed); +void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid) +{ + ASSERT_WDEV_LOCK(wdev); + + if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) + return; + + if (WARN_ON(!wdev->current_bss) || + WARN_ON(!ether_addr_equal(wdev->current_bss->pub.bssid, bssid))) + return; + + nl80211_send_port_authorized(wiphy_to_rdev(wdev->wiphy), wdev->netdev, + bssid); +} + +void cfg80211_port_authorized(struct net_device *dev, const u8 *bssid, + gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); + struct cfg80211_event *ev; + unsigned long flags; + + if (WARN_ON(!bssid)) + return; + + ev = kzalloc(sizeof(*ev), gfp); + if (!ev) + return; + + ev->type = EVENT_PORT_AUTHORIZED; + memcpy(ev->pa.bssid, bssid, ETH_ALEN); + + /* + * Use the wdev event list so that if there are pending + * connected/roamed events, they will be reported first. + */ + spin_lock_irqsave(&wdev->event_lock, flags); + list_add_tail(&ev->list, &wdev->event_list); + spin_unlock_irqrestore(&wdev->event_lock, flags); + queue_work(cfg80211_wq, &rdev->event_work); +} +EXPORT_SYMBOL(cfg80211_port_authorized); + void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, size_t ie_len, u16 reason, bool from_ap) { diff --git a/net/wireless/util.c b/net/wireless/util.c index 7a1fcc6ee060..ff21c314a609 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -846,6 +846,9 @@ void cfg80211_process_wdev_events(struct wireless_dev *wdev) case EVENT_STOPPED: __cfg80211_leave(wiphy_to_rdev(wdev->wiphy), wdev); break; + case EVENT_PORT_AUTHORIZED: + __cfg80211_port_authorized(wdev, ev->pa.bssid); + break; } wdev_unlock(wdev); -- cgit v1.2.3-59-g8ed1b From 32f16369e59fcc505c5ed93a6a8cad3d5636b463 Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Mon, 2 Oct 2017 10:41:15 +0200 Subject: net/dst: Make skb parameter of skb{metadata_dst, tunnel_info}() const Make the skb parameter of skb_metadata_dst() and skb_tunnel_info() const as they are not modified. This is in preparation for using them in call-sites where skb is const. Signed-off-by: Simon Horman Reviewed-by: Jakub Kicinski Signed-off-by: David S. Miller --- include/net/dst_metadata.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/dst_metadata.h b/include/net/dst_metadata.h index a803129a4849..9fba2ebf6dda 100644 --- a/include/net/dst_metadata.h +++ b/include/net/dst_metadata.h @@ -24,7 +24,7 @@ struct metadata_dst { } u; }; -static inline struct metadata_dst *skb_metadata_dst(struct sk_buff *skb) +static inline struct metadata_dst *skb_metadata_dst(const struct sk_buff *skb) { struct metadata_dst *md_dst = (struct metadata_dst *) skb_dst(skb); @@ -34,7 +34,8 @@ static inline struct metadata_dst *skb_metadata_dst(struct sk_buff *skb) return NULL; } -static inline struct ip_tunnel_info *skb_tunnel_info(struct sk_buff *skb) +static inline struct ip_tunnel_info * +skb_tunnel_info(const struct sk_buff *skb) { struct metadata_dst *md_dst = skb_metadata_dst(skb); struct dst_entry *dst; -- cgit v1.2.3-59-g8ed1b From f2f2efb807d339513199b1bb771806c90cce83ae Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:28 +0300 Subject: byteorder: Move {cpu_to_be32, be32_to_cpu}_array() from Thunderbolt to core We will be using these when communicating XDomain discovery protocol over Thunderbolt link but they might be useful for other drivers as well. Make them available through byteorder/generic.h. Suggested-by: Andy Shevchenko Signed-off-by: Mika Westerberg Reviewed-by: Michael Jamet Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/thunderbolt/ctl.c | 14 -------------- include/linux/byteorder/generic.h | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index fb40dd0588b9..e6a4c9458c76 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -289,20 +289,6 @@ static void tb_cfg_print_error(struct tb_ctl *ctl, } } -static void cpu_to_be32_array(__be32 *dst, const u32 *src, size_t len) -{ - int i; - for (i = 0; i < len; i++) - dst[i] = cpu_to_be32(src[i]); -} - -static void be32_to_cpu_array(u32 *dst, __be32 *src, size_t len) -{ - int i; - for (i = 0; i < len; i++) - dst[i] = be32_to_cpu(src[i]); -} - static __be32 tb_crc(const void *data, size_t len) { return cpu_to_be32(~__crc32c_le(~0, data, len)); diff --git a/include/linux/byteorder/generic.h b/include/linux/byteorder/generic.h index 89f67c1c3160..805d16654459 100644 --- a/include/linux/byteorder/generic.h +++ b/include/linux/byteorder/generic.h @@ -170,4 +170,20 @@ static inline void be64_add_cpu(__be64 *var, u64 val) *var = cpu_to_be64(be64_to_cpu(*var) + val); } +static inline void cpu_to_be32_array(__be32 *dst, const u32 *src, size_t len) +{ + int i; + + for (i = 0; i < len; i++) + dst[i] = cpu_to_be32(src[i]); +} + +static inline void be32_to_cpu_array(u32 *dst, const __be32 *src, size_t len) +{ + int i; + + for (i = 0; i < len; i++) + dst[i] = be32_to_cpu(src[i]); +} + #endif /* _LINUX_BYTEORDER_GENERIC_H */ -- cgit v1.2.3-59-g8ed1b From cdae7c07e3e3509eaabc18c1640a55dc5b99c179 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:30 +0300 Subject: thunderbolt: Add support for XDomain properties Thunderbolt XDomain discovery protocol uses directories which contain properties and other directories to exchange information about what capabilities the remote host supports. This also includes identification information like device ID and name. This adds support for parsing and formatting these properties and establishes an API drivers can use in addition to the core Thunderbolt driver. This API is exposed in a new header: include/linux/thunderbolt.h. This code is based on the work done by Amir Levy and Michael Jamet. Signed-off-by: Michael Jamet Signed-off-by: Mika Westerberg Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/thunderbolt/Makefile | 2 +- drivers/thunderbolt/property.c | 670 +++++++++++++++++++++++++++++++++++++++++ include/linux/thunderbolt.h | 89 ++++++ 3 files changed, 760 insertions(+), 1 deletion(-) create mode 100644 drivers/thunderbolt/property.c create mode 100644 include/linux/thunderbolt.h (limited to 'include') diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 4900febc6c8a..7afd21f5383a 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -1,3 +1,3 @@ obj-${CONFIG_THUNDERBOLT} := thunderbolt.o thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o -thunderbolt-objs += domain.o dma_port.o icm.o +thunderbolt-objs += domain.o dma_port.o icm.o property.o diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c new file mode 100644 index 000000000000..8fe913a95b4a --- /dev/null +++ b/drivers/thunderbolt/property.c @@ -0,0 +1,670 @@ +/* + * Thunderbolt XDomain property support + * + * Copyright (C) 2017, Intel Corporation + * Authors: Michael Jamet + * Mika Westerberg + * + * 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. + */ + +#include +#include +#include +#include +#include + +struct tb_property_entry { + u32 key_hi; + u32 key_lo; + u16 length; + u8 reserved; + u8 type; + u32 value; +}; + +struct tb_property_rootdir_entry { + u32 magic; + u32 length; + struct tb_property_entry entries[]; +}; + +struct tb_property_dir_entry { + u32 uuid[4]; + struct tb_property_entry entries[]; +}; + +#define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401 + +static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, + size_t block_len, unsigned int dir_offset, size_t dir_len, + bool is_root); + +static inline void parse_dwdata(void *dst, const void *src, size_t dwords) +{ + be32_to_cpu_array(dst, src, dwords); +} + +static inline void format_dwdata(void *dst, const void *src, size_t dwords) +{ + cpu_to_be32_array(dst, src, dwords); +} + +static bool tb_property_entry_valid(const struct tb_property_entry *entry, + size_t block_len) +{ + switch (entry->type) { + case TB_PROPERTY_TYPE_DIRECTORY: + case TB_PROPERTY_TYPE_DATA: + case TB_PROPERTY_TYPE_TEXT: + if (entry->length > block_len) + return false; + if (entry->value + entry->length > block_len) + return false; + break; + + case TB_PROPERTY_TYPE_VALUE: + if (entry->length != 1) + return false; + break; + } + + return true; +} + +static bool tb_property_key_valid(const char *key) +{ + return key && strlen(key) <= TB_PROPERTY_KEY_SIZE; +} + +static struct tb_property * +tb_property_alloc(const char *key, enum tb_property_type type) +{ + struct tb_property *property; + + property = kzalloc(sizeof(*property), GFP_KERNEL); + if (!property) + return NULL; + + strcpy(property->key, key); + property->type = type; + INIT_LIST_HEAD(&property->list); + + return property; +} + +static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, + const struct tb_property_entry *entry) +{ + char key[TB_PROPERTY_KEY_SIZE + 1]; + struct tb_property *property; + struct tb_property_dir *dir; + + if (!tb_property_entry_valid(entry, block_len)) + return NULL; + + parse_dwdata(key, entry, 2); + key[TB_PROPERTY_KEY_SIZE] = '\0'; + + property = tb_property_alloc(key, entry->type); + if (!property) + return NULL; + + property->length = entry->length; + + switch (property->type) { + case TB_PROPERTY_TYPE_DIRECTORY: + dir = __tb_property_parse_dir(block, block_len, entry->value, + entry->length, false); + if (!dir) { + kfree(property); + return NULL; + } + property->value.dir = dir; + break; + + case TB_PROPERTY_TYPE_DATA: + property->value.data = kcalloc(property->length, sizeof(u32), + GFP_KERNEL); + if (!property->value.data) { + kfree(property); + return NULL; + } + parse_dwdata(property->value.data, block + entry->value, + entry->length); + break; + + case TB_PROPERTY_TYPE_TEXT: + property->value.text = kcalloc(property->length, sizeof(u32), + GFP_KERNEL); + if (!property->value.text) { + kfree(property); + return NULL; + } + parse_dwdata(property->value.text, block + entry->value, + entry->length); + /* Force null termination */ + property->value.text[property->length * 4 - 1] = '\0'; + break; + + case TB_PROPERTY_TYPE_VALUE: + property->value.immediate = entry->value; + break; + + default: + property->type = TB_PROPERTY_TYPE_UNKNOWN; + break; + } + + return property; +} + +static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, + size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root) +{ + const struct tb_property_entry *entries; + size_t i, content_len, nentries; + unsigned int content_offset; + struct tb_property_dir *dir; + + dir = kzalloc(sizeof(*dir), GFP_KERNEL); + if (!dir) + return NULL; + + if (is_root) { + content_offset = dir_offset + 2; + content_len = dir_len; + } else { + dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid), + GFP_KERNEL); + content_offset = dir_offset + 4; + content_len = dir_len - 4; /* Length includes UUID */ + } + + entries = (const struct tb_property_entry *)&block[content_offset]; + nentries = content_len / (sizeof(*entries) / 4); + + INIT_LIST_HEAD(&dir->properties); + + for (i = 0; i < nentries; i++) { + struct tb_property *property; + + property = tb_property_parse(block, block_len, &entries[i]); + if (!property) { + tb_property_free_dir(dir); + return NULL; + } + + list_add_tail(&property->list, &dir->properties); + } + + return dir; +} + +/** + * tb_property_parse_dir() - Parses properties from given property block + * @block: Property block to parse + * @block_len: Number of dword elements in the property block + * + * This function parses the XDomain properties data block into format that + * can be traversed using the helper functions provided by this module. + * Upon success returns the parsed directory. In case of error returns + * %NULL. The resulting &struct tb_property_dir needs to be released by + * calling tb_property_free_dir() when not needed anymore. + * + * The @block is expected to be root directory. + */ +struct tb_property_dir *tb_property_parse_dir(const u32 *block, + size_t block_len) +{ + const struct tb_property_rootdir_entry *rootdir = + (const struct tb_property_rootdir_entry *)block; + + if (rootdir->magic != TB_PROPERTY_ROOTDIR_MAGIC) + return NULL; + if (rootdir->length > block_len) + return NULL; + + return __tb_property_parse_dir(block, block_len, 0, rootdir->length, + true); +} + +/** + * tb_property_create_dir() - Creates new property directory + * @uuid: UUID used to identify the particular directory + * + * Creates new, empty property directory. If @uuid is %NULL then the + * directory is assumed to be root directory. + */ +struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid) +{ + struct tb_property_dir *dir; + + dir = kzalloc(sizeof(*dir), GFP_KERNEL); + if (!dir) + return NULL; + + INIT_LIST_HEAD(&dir->properties); + if (uuid) { + dir->uuid = kmemdup(uuid, sizeof(*dir->uuid), GFP_KERNEL); + if (!dir->uuid) { + kfree(dir); + return NULL; + } + } + + return dir; +} +EXPORT_SYMBOL_GPL(tb_property_create_dir); + +static void tb_property_free(struct tb_property *property) +{ + switch (property->type) { + case TB_PROPERTY_TYPE_DIRECTORY: + tb_property_free_dir(property->value.dir); + break; + + case TB_PROPERTY_TYPE_DATA: + kfree(property->value.data); + break; + + case TB_PROPERTY_TYPE_TEXT: + kfree(property->value.text); + break; + + default: + break; + } + + kfree(property); +} + +/** + * tb_property_free_dir() - Release memory allocated for property directory + * @dir: Directory to release + * + * This will release all the memory the directory occupies including all + * descendants. It is OK to pass %NULL @dir, then the function does + * nothing. + */ +void tb_property_free_dir(struct tb_property_dir *dir) +{ + struct tb_property *property, *tmp; + + if (!dir) + return; + + list_for_each_entry_safe(property, tmp, &dir->properties, list) { + list_del(&property->list); + tb_property_free(property); + } + kfree(dir->uuid); + kfree(dir); +} +EXPORT_SYMBOL_GPL(tb_property_free_dir); + +static size_t tb_property_dir_length(const struct tb_property_dir *dir, + bool recurse, size_t *data_len) +{ + const struct tb_property *property; + size_t len = 0; + + if (dir->uuid) + len += sizeof(*dir->uuid) / 4; + else + len += sizeof(struct tb_property_rootdir_entry) / 4; + + list_for_each_entry(property, &dir->properties, list) { + len += sizeof(struct tb_property_entry) / 4; + + switch (property->type) { + case TB_PROPERTY_TYPE_DIRECTORY: + if (recurse) { + len += tb_property_dir_length( + property->value.dir, recurse, data_len); + } + /* Reserve dword padding after each directory */ + if (data_len) + *data_len += 1; + break; + + case TB_PROPERTY_TYPE_DATA: + case TB_PROPERTY_TYPE_TEXT: + if (data_len) + *data_len += property->length; + break; + + default: + break; + } + } + + return len; +} + +static ssize_t __tb_property_format_dir(const struct tb_property_dir *dir, + u32 *block, unsigned int start_offset, size_t block_len) +{ + unsigned int data_offset, dir_end; + const struct tb_property *property; + struct tb_property_entry *entry; + size_t dir_len, data_len = 0; + int ret; + + /* + * The structure of property block looks like following. Leaf + * data/text is included right after the directory and each + * directory follows each other (even nested ones). + * + * +----------+ <-- start_offset + * | header | <-- root directory header + * +----------+ --- + * | entry 0 | -^--------------------. + * +----------+ | | + * | entry 1 | -|--------------------|--. + * +----------+ | | | + * | entry 2 | -|-----------------. | | + * +----------+ | | | | + * : : | dir_len | | | + * . . | | | | + * : : | | | | + * +----------+ | | | | + * | entry n | v | | | + * +----------+ <-- data_offset | | | + * | data 0 | <------------------|--' | + * +----------+ | | + * | data 1 | <------------------|-----' + * +----------+ | + * | 00000000 | padding | + * +----------+ <-- dir_end <------' + * | UUID | <-- directory UUID (child directory) + * +----------+ + * | entry 0 | + * +----------+ + * | entry 1 | + * +----------+ + * : : + * . . + * : : + * +----------+ + * | entry n | + * +----------+ + * | data 0 | + * +----------+ + * + * We use dir_end to hold pointer to the end of the directory. It + * will increase as we add directories and each directory should be + * added starting from previous dir_end. + */ + dir_len = tb_property_dir_length(dir, false, &data_len); + data_offset = start_offset + dir_len; + dir_end = start_offset + data_len + dir_len; + + if (data_offset > dir_end) + return -EINVAL; + if (dir_end > block_len) + return -EINVAL; + + /* Write headers first */ + if (dir->uuid) { + struct tb_property_dir_entry *pe; + + pe = (struct tb_property_dir_entry *)&block[start_offset]; + memcpy(pe->uuid, dir->uuid, sizeof(pe->uuid)); + entry = pe->entries; + } else { + struct tb_property_rootdir_entry *re; + + re = (struct tb_property_rootdir_entry *)&block[start_offset]; + re->magic = TB_PROPERTY_ROOTDIR_MAGIC; + re->length = dir_len - sizeof(*re) / 4; + entry = re->entries; + } + + list_for_each_entry(property, &dir->properties, list) { + const struct tb_property_dir *child; + + format_dwdata(entry, property->key, 2); + entry->type = property->type; + + switch (property->type) { + case TB_PROPERTY_TYPE_DIRECTORY: + child = property->value.dir; + ret = __tb_property_format_dir(child, block, dir_end, + block_len); + if (ret < 0) + return ret; + entry->length = tb_property_dir_length(child, false, + NULL); + entry->value = dir_end; + dir_end = ret; + break; + + case TB_PROPERTY_TYPE_DATA: + format_dwdata(&block[data_offset], property->value.data, + property->length); + entry->length = property->length; + entry->value = data_offset; + data_offset += entry->length; + break; + + case TB_PROPERTY_TYPE_TEXT: + format_dwdata(&block[data_offset], property->value.text, + property->length); + entry->length = property->length; + entry->value = data_offset; + data_offset += entry->length; + break; + + case TB_PROPERTY_TYPE_VALUE: + entry->length = property->length; + entry->value = property->value.immediate; + break; + + default: + break; + } + + entry++; + } + + return dir_end; +} + +/** + * tb_property_format_dir() - Formats directory to the packed XDomain format + * @dir: Directory to format + * @block: Property block where the packed data is placed + * @block_len: Length of the property block + * + * This function formats the directory to the packed format that can be + * then send over the thunderbolt fabric to receiving host. Returns %0 in + * case of success and negative errno on faulure. Passing %NULL in @block + * returns number of entries the block takes. + */ +ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block, + size_t block_len) +{ + ssize_t ret; + + if (!block) { + size_t dir_len, data_len = 0; + + dir_len = tb_property_dir_length(dir, true, &data_len); + return dir_len + data_len; + } + + ret = __tb_property_format_dir(dir, block, 0, block_len); + return ret < 0 ? ret : 0; +} + +/** + * tb_property_add_immediate() - Add immediate property to directory + * @parent: Directory to add the property + * @key: Key for the property + * @value: Immediate value to store with the property + */ +int tb_property_add_immediate(struct tb_property_dir *parent, const char *key, + u32 value) +{ + struct tb_property *property; + + if (!tb_property_key_valid(key)) + return -EINVAL; + + property = tb_property_alloc(key, TB_PROPERTY_TYPE_VALUE); + if (!property) + return -ENOMEM; + + property->length = 1; + property->value.immediate = value; + + list_add_tail(&property->list, &parent->properties); + return 0; +} +EXPORT_SYMBOL_GPL(tb_property_add_immediate); + +/** + * tb_property_add_data() - Adds arbitrary data property to directory + * @parent: Directory to add the property + * @key: Key for the property + * @buf: Data buffer to add + * @buflen: Number of bytes in the data buffer + * + * Function takes a copy of @buf and adds it to the directory. + */ +int tb_property_add_data(struct tb_property_dir *parent, const char *key, + const void *buf, size_t buflen) +{ + /* Need to pad to dword boundary */ + size_t size = round_up(buflen, 4); + struct tb_property *property; + + if (!tb_property_key_valid(key)) + return -EINVAL; + + property = tb_property_alloc(key, TB_PROPERTY_TYPE_DATA); + if (!property) + return -ENOMEM; + + property->length = size / 4; + property->value.data = kzalloc(size, GFP_KERNEL); + memcpy(property->value.data, buf, buflen); + + list_add_tail(&property->list, &parent->properties); + return 0; +} +EXPORT_SYMBOL_GPL(tb_property_add_data); + +/** + * tb_property_add_text() - Adds string property to directory + * @parent: Directory to add the property + * @key: Key for the property + * @text: String to add + * + * Function takes a copy of @text and adds it to the directory. + */ +int tb_property_add_text(struct tb_property_dir *parent, const char *key, + const char *text) +{ + /* Need to pad to dword boundary */ + size_t size = round_up(strlen(text) + 1, 4); + struct tb_property *property; + + if (!tb_property_key_valid(key)) + return -EINVAL; + + property = tb_property_alloc(key, TB_PROPERTY_TYPE_TEXT); + if (!property) + return -ENOMEM; + + property->length = size / 4; + property->value.data = kzalloc(size, GFP_KERNEL); + strcpy(property->value.text, text); + + list_add_tail(&property->list, &parent->properties); + return 0; +} +EXPORT_SYMBOL_GPL(tb_property_add_text); + +/** + * tb_property_add_dir() - Adds a directory to the parent directory + * @parent: Directory to add the property + * @key: Key for the property + * @dir: Directory to add + */ +int tb_property_add_dir(struct tb_property_dir *parent, const char *key, + struct tb_property_dir *dir) +{ + struct tb_property *property; + + if (!tb_property_key_valid(key)) + return -EINVAL; + + property = tb_property_alloc(key, TB_PROPERTY_TYPE_DIRECTORY); + if (!property) + return -ENOMEM; + + property->value.dir = dir; + + list_add_tail(&property->list, &parent->properties); + return 0; +} +EXPORT_SYMBOL_GPL(tb_property_add_dir); + +/** + * tb_property_remove() - Removes property from a parent directory + * @property: Property to remove + * + * Note memory for @property is released as well so it is not allowed to + * touch the object after call to this function. + */ +void tb_property_remove(struct tb_property *property) +{ + list_del(&property->list); + kfree(property); +} +EXPORT_SYMBOL_GPL(tb_property_remove); + +/** + * tb_property_find() - Find a property from a directory + * @dir: Directory where the property is searched + * @key: Key to look for + * @type: Type of the property + * + * Finds and returns property from the given directory. Does not recurse + * into sub-directories. Returns %NULL if the property was not found. + */ +struct tb_property *tb_property_find(struct tb_property_dir *dir, + const char *key, enum tb_property_type type) +{ + struct tb_property *property; + + list_for_each_entry(property, &dir->properties, list) { + if (property->type == type && !strcmp(property->key, key)) + return property; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(tb_property_find); + +/** + * tb_property_get_next() - Get next property from directory + * @dir: Directory holding properties + * @prev: Previous property in the directory (%NULL returns the first) + */ +struct tb_property *tb_property_get_next(struct tb_property_dir *dir, + struct tb_property *prev) +{ + if (prev) { + if (list_is_last(&prev->list, &dir->properties)) + return NULL; + return list_next_entry(prev, list); + } + return list_first_entry_or_null(&dir->properties, struct tb_property, + list); +} +EXPORT_SYMBOL_GPL(tb_property_get_next); diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h new file mode 100644 index 000000000000..96561c1265ae --- /dev/null +++ b/include/linux/thunderbolt.h @@ -0,0 +1,89 @@ +/* + * Thunderbolt service API + * + * Copyright (C) 2017, Intel Corporation + * Authors: Michael Jamet + * Mika Westerberg + * + * 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. + */ + +#ifndef THUNDERBOLT_H_ +#define THUNDERBOLT_H_ + +#include +#include + +/** + * struct tb_property_dir - XDomain property directory + * @uuid: Directory UUID or %NULL if root directory + * @properties: List of properties in this directory + * + * User needs to provide serialization if needed. + */ +struct tb_property_dir { + const uuid_t *uuid; + struct list_head properties; +}; + +enum tb_property_type { + TB_PROPERTY_TYPE_UNKNOWN = 0x00, + TB_PROPERTY_TYPE_DIRECTORY = 0x44, + TB_PROPERTY_TYPE_DATA = 0x64, + TB_PROPERTY_TYPE_TEXT = 0x74, + TB_PROPERTY_TYPE_VALUE = 0x76, +}; + +#define TB_PROPERTY_KEY_SIZE 8 + +/** + * struct tb_property - XDomain property + * @list: Used to link properties together in a directory + * @key: Key for the property (always terminated). + * @type: Type of the property + * @length: Length of the property data in dwords + * @value: Property value + * + * Users use @type to determine which field in @value is filled. + */ +struct tb_property { + struct list_head list; + char key[TB_PROPERTY_KEY_SIZE + 1]; + enum tb_property_type type; + size_t length; + union { + struct tb_property_dir *dir; + u8 *data; + char *text; + u32 immediate; + } value; +}; + +struct tb_property_dir *tb_property_parse_dir(const u32 *block, + size_t block_len); +ssize_t tb_property_format_dir(const struct tb_property_dir *dir, u32 *block, + size_t block_len); +struct tb_property_dir *tb_property_create_dir(const uuid_t *uuid); +void tb_property_free_dir(struct tb_property_dir *dir); +int tb_property_add_immediate(struct tb_property_dir *parent, const char *key, + u32 value); +int tb_property_add_data(struct tb_property_dir *parent, const char *key, + const void *buf, size_t buflen); +int tb_property_add_text(struct tb_property_dir *parent, const char *key, + const char *text); +int tb_property_add_dir(struct tb_property_dir *parent, const char *key, + struct tb_property_dir *dir); +void tb_property_remove(struct tb_property *tb_property); +struct tb_property *tb_property_find(struct tb_property_dir *dir, + const char *key, enum tb_property_type type); +struct tb_property *tb_property_get_next(struct tb_property_dir *dir, + struct tb_property *prev); + +#define tb_property_for_each(dir, property) \ + for (property = tb_property_get_next(dir, NULL); \ + property; \ + property = tb_property_get_next(dir, property)) + +#endif /* THUNDERBOLT_H_ */ -- cgit v1.2.3-59-g8ed1b From eaf8ff35a345449207ad116e2574c19780ec9a98 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:31 +0300 Subject: thunderbolt: Move enum tb_cfg_pkg_type to thunderbolt.h These will be needed by Thunderbolt services when sending and receiving XDomain control messages. While there change TB_CFG_PKG_PREPARE_TO_SLEEP value to be decimal in order to be consistent with other members. Signed-off-by: Mika Westerberg Reviewed-by: Michael Jamet Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/thunderbolt/ctl.h | 1 + drivers/thunderbolt/tb_msgs.h | 17 ----------------- include/linux/thunderbolt.h | 17 +++++++++++++++++ 3 files changed, 18 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h index 36fd28b1c1c5..d0f21e1e0b8b 100644 --- a/drivers/thunderbolt/ctl.h +++ b/drivers/thunderbolt/ctl.h @@ -8,6 +8,7 @@ #define _TB_CFG #include +#include #include "nhi.h" #include "tb_msgs.h" diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index f3adf58a40ce..f2b2550cd97c 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -15,23 +15,6 @@ #include #include -enum tb_cfg_pkg_type { - TB_CFG_PKG_READ = 1, - TB_CFG_PKG_WRITE = 2, - TB_CFG_PKG_ERROR = 3, - TB_CFG_PKG_NOTIFY_ACK = 4, - TB_CFG_PKG_EVENT = 5, - TB_CFG_PKG_XDOMAIN_REQ = 6, - TB_CFG_PKG_XDOMAIN_RESP = 7, - TB_CFG_PKG_OVERRIDE = 8, - TB_CFG_PKG_RESET = 9, - TB_CFG_PKG_ICM_EVENT = 10, - TB_CFG_PKG_ICM_CMD = 11, - TB_CFG_PKG_ICM_RESP = 12, - TB_CFG_PKG_PREPARE_TO_SLEEP = 0xd, - -}; - enum tb_cfg_space { TB_CFG_HOPS = 0, TB_CFG_PORT = 1, diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 96561c1265ae..b512b1e2b4f2 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -1,6 +1,7 @@ /* * Thunderbolt service API * + * Copyright (C) 2014 Andreas Noever * Copyright (C) 2017, Intel Corporation * Authors: Michael Jamet * Mika Westerberg @@ -16,6 +17,22 @@ #include #include +enum tb_cfg_pkg_type { + TB_CFG_PKG_READ = 1, + TB_CFG_PKG_WRITE = 2, + TB_CFG_PKG_ERROR = 3, + TB_CFG_PKG_NOTIFY_ACK = 4, + TB_CFG_PKG_EVENT = 5, + TB_CFG_PKG_XDOMAIN_REQ = 6, + TB_CFG_PKG_XDOMAIN_RESP = 7, + TB_CFG_PKG_OVERRIDE = 8, + TB_CFG_PKG_RESET = 9, + TB_CFG_PKG_ICM_EVENT = 10, + TB_CFG_PKG_ICM_CMD = 11, + TB_CFG_PKG_ICM_RESP = 12, + TB_CFG_PKG_PREPARE_TO_SLEEP = 13, +}; + /** * struct tb_property_dir - XDomain property directory * @uuid: Directory UUID or %NULL if root directory -- cgit v1.2.3-59-g8ed1b From 9e99b9f4d5c36340dabda6d14053195b2a43796b Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:32 +0300 Subject: thunderbolt: Move thunderbolt domain structure to thunderbolt.h These are needed by Thunderbolt services so move them to thunderbolt.h to make sure they are available outside of drivers/thunderbolt. Signed-off-by: Mika Westerberg Reviewed-by: Michael Jamet Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/thunderbolt/tb.h | 42 ------------------------------------------ include/linux/thunderbolt.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index e0deee4f1eb0..2fefe76621ca 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -39,20 +39,6 @@ struct tb_switch_nvm { bool authenticating; }; -/** - * enum tb_security_level - Thunderbolt security level - * @TB_SECURITY_NONE: No security, legacy mode - * @TB_SECURITY_USER: User approval required at minimum - * @TB_SECURITY_SECURE: One time saved key required at minimum - * @TB_SECURITY_DPONLY: Only tunnel Display port (and USB) - */ -enum tb_security_level { - TB_SECURITY_NONE, - TB_SECURITY_USER, - TB_SECURITY_SECURE, - TB_SECURITY_DPONLY, -}; - #define TB_SWITCH_KEY_SIZE 32 /* Each physical port contains 2 links on modern controllers */ #define TB_SWITCH_LINKS_PER_PHY_PORT 2 @@ -223,33 +209,6 @@ struct tb_cm_ops { int (*disconnect_pcie_paths)(struct tb *tb); }; -/** - * struct tb - main thunderbolt bus structure - * @dev: Domain device - * @lock: Big lock. Must be held when accessing any struct - * tb_switch / struct tb_port. - * @nhi: Pointer to the NHI structure - * @ctl: Control channel for this domain - * @wq: Ordered workqueue for all domain specific work - * @root_switch: Root switch of this domain - * @cm_ops: Connection manager specific operations vector - * @index: Linux assigned domain number - * @security_level: Current security level - * @privdata: Private connection manager specific data - */ -struct tb { - struct device dev; - struct mutex lock; - struct tb_nhi *nhi; - struct tb_ctl *ctl; - struct workqueue_struct *wq; - struct tb_switch *root_switch; - const struct tb_cm_ops *cm_ops; - int index; - enum tb_security_level security_level; - unsigned long privdata[0]; -}; - static inline void *tb_priv(struct tb *tb) { return (void *)tb->privdata; @@ -368,7 +327,6 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer, struct tb *icm_probe(struct tb_nhi *nhi); struct tb *tb_probe(struct tb_nhi *nhi); -extern struct bus_type tb_bus_type; extern struct device_type tb_domain_type; extern struct device_type tb_switch_type; diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index b512b1e2b4f2..910b1bf92112 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -14,7 +14,9 @@ #ifndef THUNDERBOLT_H_ #define THUNDERBOLT_H_ +#include #include +#include #include enum tb_cfg_pkg_type { @@ -33,6 +35,49 @@ enum tb_cfg_pkg_type { TB_CFG_PKG_PREPARE_TO_SLEEP = 13, }; +/** + * enum tb_security_level - Thunderbolt security level + * @TB_SECURITY_NONE: No security, legacy mode + * @TB_SECURITY_USER: User approval required at minimum + * @TB_SECURITY_SECURE: One time saved key required at minimum + * @TB_SECURITY_DPONLY: Only tunnel Display port (and USB) + */ +enum tb_security_level { + TB_SECURITY_NONE, + TB_SECURITY_USER, + TB_SECURITY_SECURE, + TB_SECURITY_DPONLY, +}; + +/** + * struct tb - main thunderbolt bus structure + * @dev: Domain device + * @lock: Big lock. Must be held when accessing any struct + * tb_switch / struct tb_port. + * @nhi: Pointer to the NHI structure + * @ctl: Control channel for this domain + * @wq: Ordered workqueue for all domain specific work + * @root_switch: Root switch of this domain + * @cm_ops: Connection manager specific operations vector + * @index: Linux assigned domain number + * @security_level: Current security level + * @privdata: Private connection manager specific data + */ +struct tb { + struct device dev; + struct mutex lock; + struct tb_nhi *nhi; + struct tb_ctl *ctl; + struct workqueue_struct *wq; + struct tb_switch *root_switch; + const struct tb_cm_ops *cm_ops; + int index; + enum tb_security_level security_level; + unsigned long privdata[0]; +}; + +extern struct bus_type tb_bus_type; + /** * struct tb_property_dir - XDomain property directory * @uuid: Directory UUID or %NULL if root directory -- cgit v1.2.3-59-g8ed1b From e69b71f8458b78a2ef44e3d07374a8f46e45123d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:33 +0300 Subject: thunderbolt: Move tb_switch_phy_port_from_link() to thunderbolt.h A Thunderbolt service might need to find the physical port from a link the cable is connected to. For instance networking driver uses this information to generate MAC address according the Apple ThunderboltIP protocol. Move this function to thunderbolt.h and rename it to tb_phy_port_from_link() to reflect the fact that it does not take switch as parameter. Signed-off-by: Mika Westerberg Reviewed-by: Michael Jamet Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/thunderbolt/icm.c | 2 +- drivers/thunderbolt/tb.h | 7 ------- include/linux/thunderbolt.h | 7 +++++++ 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 53250fc057e1..8c22b91ed040 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -89,7 +89,7 @@ static inline struct tb *icm_to_tb(struct icm *icm) static inline u8 phy_port_from_route(u64 route, u8 depth) { - return tb_switch_phy_port_from_link(route >> ((depth - 1) * 8)); + return tb_phy_port_from_link(route >> ((depth - 1) * 8)); } static inline u8 dual_link_from_link(u8 link) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 2fefe76621ca..ea21d927bd09 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -40,8 +40,6 @@ struct tb_switch_nvm { }; #define TB_SWITCH_KEY_SIZE 32 -/* Each physical port contains 2 links on modern controllers */ -#define TB_SWITCH_LINKS_PER_PHY_PORT 2 /** * struct tb_switch - a thunderbolt switch @@ -367,11 +365,6 @@ struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, u8 depth); struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid); -static inline unsigned int tb_switch_phy_port_from_link(unsigned int link) -{ - return (link - 1) / TB_SWITCH_LINKS_PER_PHY_PORT; -} - static inline void tb_switch_put(struct tb_switch *sw) { put_device(&sw->dev); diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 910b1bf92112..43b8d1e09341 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -78,6 +78,13 @@ struct tb { extern struct bus_type tb_bus_type; +#define TB_LINKS_PER_PHY_PORT 2 + +static inline unsigned int tb_phy_port_from_link(unsigned int link) +{ + return (link - 1) / TB_LINKS_PER_PHY_PORT; +} + /** * struct tb_property_dir - XDomain property directory * @uuid: Directory UUID or %NULL if root directory -- cgit v1.2.3-59-g8ed1b From d1ff70241a275133e1a0258b7c23588b122276c8 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:34 +0300 Subject: thunderbolt: Add support for XDomain discovery protocol When two hosts are connected over a Thunderbolt cable, there is a protocol they can use to communicate capabilities supported by the host. The discovery protocol uses automatically configured control channel (ring 0) and is build on top of request/response transactions using special XDomain primitives provided by the Thunderbolt base protocol. The capabilities consists of a root directory block of basic properties used for identification of the host, and then there can be zero or more directories each describing a Thunderbolt service and its capabilities. Once both sides have discovered what is supported the two hosts can setup high-speed DMA paths and transfer data to the other side using whatever protocol was agreed based on the properties. The software protocol used to communicate which DMA paths to enable is service specific. This patch adds support for the XDomain discovery protocol to the Thunderbolt bus. We model each remote host connection as a Linux XDomain device. For each Thunderbolt service found supported on the XDomain device, we create Linux Thunderbolt service device which Thunderbolt service drivers can then bind to based on the protocol identification information retrieved from the property directory describing the service. This code is based on the work done by Amir Levy and Michael Jamet. Signed-off-by: Michael Jamet Signed-off-by: Mika Westerberg Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- Documentation/ABI/testing/sysfs-bus-thunderbolt | 48 + drivers/thunderbolt/Makefile | 2 +- drivers/thunderbolt/ctl.c | 11 +- drivers/thunderbolt/ctl.h | 2 +- drivers/thunderbolt/domain.c | 197 ++- drivers/thunderbolt/icm.c | 218 +++- drivers/thunderbolt/nhi.h | 2 + drivers/thunderbolt/switch.c | 7 +- drivers/thunderbolt/tb.h | 39 +- drivers/thunderbolt/tb_msgs.h | 123 ++ drivers/thunderbolt/xdomain.c | 1576 +++++++++++++++++++++++ include/linux/mod_devicetable.h | 26 + include/linux/thunderbolt.h | 242 ++++ scripts/mod/devicetable-offsets.c | 7 + scripts/mod/file2alias.c | 25 + 15 files changed, 2507 insertions(+), 18 deletions(-) create mode 100644 drivers/thunderbolt/xdomain.c (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index 392bef5bd399..93798c02e28b 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -110,3 +110,51 @@ Description: When new NVM image is written to the non-active NVM is directly the status value from the DMA configuration based mailbox before the device is power cycled. Writing 0 here clears the status. + +What: /sys/bus/thunderbolt/devices/./key +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: This contains name of the property directory the XDomain + service exposes. This entry describes the protocol in + question. Following directories are already reserved by + the Apple XDomain specification: + + network: IP/ethernet over Thunderbolt + targetdm: Target disk mode protocol over Thunderbolt + extdisp: External display mode protocol over Thunderbolt + +What: /sys/bus/thunderbolt/devices/./modalias +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: Stores the same MODALIAS value emitted by uevent for + the XDomain service. Format: tbtsvc:kSpNvNrN + +What: /sys/bus/thunderbolt/devices/./prtcid +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: This contains XDomain protocol identifier the XDomain + service supports. + +What: /sys/bus/thunderbolt/devices/./prtcvers +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: This contains XDomain protocol version the XDomain + service supports. + +What: /sys/bus/thunderbolt/devices/./prtcrevs +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: This contains XDomain software version the XDomain + service supports. + +What: /sys/bus/thunderbolt/devices/./prtcstns +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: This contains XDomain service specific settings as + bitmask. Format: %x diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 7afd21f5383a..f2f0de27252b 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -1,3 +1,3 @@ obj-${CONFIG_THUNDERBOLT} := thunderbolt.o thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o -thunderbolt-objs += domain.o dma_port.o icm.o property.o +thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index e6a4c9458c76..46e393c5fd1d 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -368,10 +368,10 @@ static int tb_ctl_tx(struct tb_ctl *ctl, const void *data, size_t len, /** * tb_ctl_handle_event() - acknowledge a plug event, invoke ctl->callback */ -static void tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type, +static bool tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type, struct ctl_pkg *pkg, size_t size) { - ctl->callback(ctl->callback_data, type, pkg->buffer, size); + return ctl->callback(ctl->callback_data, type, pkg->buffer, size); } static void tb_ctl_rx_submit(struct ctl_pkg *pkg) @@ -444,6 +444,8 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, break; case TB_CFG_PKG_EVENT: + case TB_CFG_PKG_XDOMAIN_RESP: + case TB_CFG_PKG_XDOMAIN_REQ: if (*(__be32 *)(pkg->buffer + frame->size) != crc32) { tb_ctl_err(pkg->ctl, "RX: checksum mismatch, dropping packet\n"); @@ -451,8 +453,9 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, } /* Fall through */ case TB_CFG_PKG_ICM_EVENT: - tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size); - goto rx; + if (tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size)) + goto rx; + break; default: break; diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h index d0f21e1e0b8b..85c49dd301ea 100644 --- a/drivers/thunderbolt/ctl.h +++ b/drivers/thunderbolt/ctl.h @@ -16,7 +16,7 @@ /* control channel */ struct tb_ctl; -typedef void (*event_cb)(void *data, enum tb_cfg_pkg_type type, +typedef bool (*event_cb)(void *data, enum tb_cfg_pkg_type type, const void *buf, size_t size); struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data); diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 9f2dcd48974d..9b90115319ce 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -20,6 +20,98 @@ static DEFINE_IDA(tb_domain_ida); +static bool match_service_id(const struct tb_service_id *id, + const struct tb_service *svc) +{ + if (id->match_flags & TBSVC_MATCH_PROTOCOL_KEY) { + if (strcmp(id->protocol_key, svc->key)) + return false; + } + + if (id->match_flags & TBSVC_MATCH_PROTOCOL_ID) { + if (id->protocol_id != svc->prtcid) + return false; + } + + if (id->match_flags & TBSVC_MATCH_PROTOCOL_VERSION) { + if (id->protocol_version != svc->prtcvers) + return false; + } + + if (id->match_flags & TBSVC_MATCH_PROTOCOL_VERSION) { + if (id->protocol_revision != svc->prtcrevs) + return false; + } + + return true; +} + +static const struct tb_service_id *__tb_service_match(struct device *dev, + struct device_driver *drv) +{ + struct tb_service_driver *driver; + const struct tb_service_id *ids; + struct tb_service *svc; + + svc = tb_to_service(dev); + if (!svc) + return NULL; + + driver = container_of(drv, struct tb_service_driver, driver); + if (!driver->id_table) + return NULL; + + for (ids = driver->id_table; ids->match_flags != 0; ids++) { + if (match_service_id(ids, svc)) + return ids; + } + + return NULL; +} + +static int tb_service_match(struct device *dev, struct device_driver *drv) +{ + return !!__tb_service_match(dev, drv); +} + +static int tb_service_probe(struct device *dev) +{ + struct tb_service *svc = tb_to_service(dev); + struct tb_service_driver *driver; + const struct tb_service_id *id; + + driver = container_of(dev->driver, struct tb_service_driver, driver); + id = __tb_service_match(dev, &driver->driver); + + return driver->probe(svc, id); +} + +static int tb_service_remove(struct device *dev) +{ + struct tb_service *svc = tb_to_service(dev); + struct tb_service_driver *driver; + + driver = container_of(dev->driver, struct tb_service_driver, driver); + if (driver->remove) + driver->remove(svc); + + return 0; +} + +static void tb_service_shutdown(struct device *dev) +{ + struct tb_service_driver *driver; + struct tb_service *svc; + + svc = tb_to_service(dev); + if (!svc || !dev->driver) + return; + + driver = container_of(dev->driver, struct tb_service_driver, driver); + if (driver->shutdown) + driver->shutdown(svc); +} + static const char * const tb_security_names[] = { [TB_SECURITY_NONE] = "none", [TB_SECURITY_USER] = "user", @@ -52,6 +144,10 @@ static const struct attribute_group *domain_attr_groups[] = { struct bus_type tb_bus_type = { .name = "thunderbolt", + .match = tb_service_match, + .probe = tb_service_probe, + .remove = tb_service_remove, + .shutdown = tb_service_shutdown, }; static void tb_domain_release(struct device *dev) @@ -128,17 +224,26 @@ err_free: return NULL; } -static void tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type, +static bool tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type, const void *buf, size_t size) { struct tb *tb = data; if (!tb->cm_ops->handle_event) { tb_warn(tb, "domain does not have event handler\n"); - return; + return true; } - tb->cm_ops->handle_event(tb, type, buf, size); + switch (type) { + case TB_CFG_PKG_XDOMAIN_REQ: + case TB_CFG_PKG_XDOMAIN_RESP: + return tb_xdomain_handle_request(tb, type, buf, size); + + default: + tb->cm_ops->handle_event(tb, type, buf, size); + } + + return true; } /** @@ -443,9 +548,92 @@ int tb_domain_disconnect_pcie_paths(struct tb *tb) return tb->cm_ops->disconnect_pcie_paths(tb); } +/** + * tb_domain_approve_xdomain_paths() - Enable DMA paths for XDomain + * @tb: Domain enabling the DMA paths + * @xd: XDomain DMA paths are created to + * + * Calls connection manager specific method to enable DMA paths to the + * XDomain in question. + * + * Return: 0% in case of success and negative errno otherwise. In + * particular returns %-ENOTSUPP if the connection manager + * implementation does not support XDomains. + */ +int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +{ + if (!tb->cm_ops->approve_xdomain_paths) + return -ENOTSUPP; + + return tb->cm_ops->approve_xdomain_paths(tb, xd); +} + +/** + * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain + * @tb: Domain disabling the DMA paths + * @xd: XDomain whose DMA paths are disconnected + * + * Calls connection manager specific method to disconnect DMA paths to + * the XDomain in question. + * + * Return: 0% in case of success and negative errno otherwise. In + * particular returns %-ENOTSUPP if the connection manager + * implementation does not support XDomains. + */ +int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +{ + if (!tb->cm_ops->disconnect_xdomain_paths) + return -ENOTSUPP; + + return tb->cm_ops->disconnect_xdomain_paths(tb, xd); +} + +static int disconnect_xdomain(struct device *dev, void *data) +{ + struct tb_xdomain *xd; + struct tb *tb = data; + int ret = 0; + + xd = tb_to_xdomain(dev); + if (xd && xd->tb == tb) + ret = tb_xdomain_disable_paths(xd); + + return ret; +} + +/** + * tb_domain_disconnect_all_paths() - Disconnect all paths for the domain + * @tb: Domain whose paths are disconnected + * + * This function can be used to disconnect all paths (PCIe, XDomain) for + * example in preparation for host NVM firmware upgrade. After this is + * called the paths cannot be established without resetting the switch. + * + * Return: %0 in case of success and negative errno otherwise. + */ +int tb_domain_disconnect_all_paths(struct tb *tb) +{ + int ret; + + ret = tb_domain_disconnect_pcie_paths(tb); + if (ret) + return ret; + + return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain); +} + int tb_domain_init(void) { - return bus_register(&tb_bus_type); + int ret; + + ret = tb_xdomain_init(); + if (ret) + return ret; + ret = bus_register(&tb_bus_type); + if (ret) + tb_xdomain_exit(); + + return ret; } void tb_domain_exit(void) @@ -453,4 +641,5 @@ void tb_domain_exit(void) bus_unregister(&tb_bus_type); ida_destroy(&tb_domain_ida); tb_switch_exit(); + tb_xdomain_exit(); } diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 8c22b91ed040..ab02d13f40b7 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -60,6 +60,8 @@ * @get_route: Find a route string for given switch * @device_connected: Handle device connected ICM message * @device_disconnected: Handle device disconnected ICM message + * @xdomain_connected - Handle XDomain connected ICM message + * @xdomain_disconnected - Handle XDomain disconnected ICM message */ struct icm { struct mutex request_lock; @@ -74,6 +76,10 @@ struct icm { const struct icm_pkg_header *hdr); void (*device_disconnected)(struct tb *tb, const struct icm_pkg_header *hdr); + void (*xdomain_connected)(struct tb *tb, + const struct icm_pkg_header *hdr); + void (*xdomain_disconnected)(struct tb *tb, + const struct icm_pkg_header *hdr); }; struct icm_notification { @@ -89,7 +95,10 @@ static inline struct tb *icm_to_tb(struct icm *icm) static inline u8 phy_port_from_route(u64 route, u8 depth) { - return tb_phy_port_from_link(route >> ((depth - 1) * 8)); + u8 link; + + link = depth ? route >> ((depth - 1) * 8) : route; + return tb_phy_port_from_link(link); } static inline u8 dual_link_from_link(u8 link) @@ -320,6 +329,51 @@ static int icm_fr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, return 0; } +static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +{ + struct icm_fr_pkg_approve_xdomain_response reply; + struct icm_fr_pkg_approve_xdomain request; + int ret; + + memset(&request, 0, sizeof(request)); + request.hdr.code = ICM_APPROVE_XDOMAIN; + request.link_info = xd->depth << ICM_LINK_INFO_DEPTH_SHIFT | xd->link; + memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); + + request.transmit_path = xd->transmit_path; + request.transmit_ring = xd->transmit_ring; + request.receive_path = xd->receive_path; + request.receive_ring = xd->receive_ring; + + memset(&reply, 0, sizeof(reply)); + ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), + 1, ICM_TIMEOUT); + if (ret) + return ret; + + if (reply.hdr.flags & ICM_FLAGS_ERROR) + return -EIO; + + return 0; +} + +static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +{ + u8 phy_port; + u8 cmd; + + phy_port = tb_phy_port_from_link(xd->link); + if (phy_port == 0) + cmd = NHI_MAILBOX_DISCONNECT_PA; + else + cmd = NHI_MAILBOX_DISCONNECT_PB; + + nhi_mailbox_cmd(tb->nhi, cmd, 1); + usleep_range(10, 50); + nhi_mailbox_cmd(tb->nhi, cmd, 2); + return 0; +} + static void remove_switch(struct tb_switch *sw) { struct tb_switch *parent_sw; @@ -475,6 +529,141 @@ icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) tb_switch_put(sw); } +static void remove_xdomain(struct tb_xdomain *xd) +{ + struct tb_switch *sw; + + sw = tb_to_switch(xd->dev.parent); + tb_port_at(xd->route, sw)->xdomain = NULL; + tb_xdomain_remove(xd); +} + +static void +icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) +{ + const struct icm_fr_event_xdomain_connected *pkg = + (const struct icm_fr_event_xdomain_connected *)hdr; + struct tb_xdomain *xd; + struct tb_switch *sw; + u8 link, depth; + bool approved; + u64 route; + + /* + * After NVM upgrade adding root switch device fails because we + * initiated reset. During that time ICM might still send + * XDomain connected message which we ignore here. + */ + if (!tb->root_switch) + return; + + link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; + depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> + ICM_LINK_INFO_DEPTH_SHIFT; + approved = pkg->link_info & ICM_LINK_INFO_APPROVED; + + if (link > ICM_MAX_LINK || depth > ICM_MAX_DEPTH) { + tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); + return; + } + + route = get_route(pkg->local_route_hi, pkg->local_route_lo); + + xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); + if (xd) { + u8 xd_phy_port, phy_port; + + xd_phy_port = phy_port_from_route(xd->route, xd->depth); + phy_port = phy_port_from_route(route, depth); + + if (xd->depth == depth && xd_phy_port == phy_port) { + xd->link = link; + xd->route = route; + xd->is_unplugged = false; + tb_xdomain_put(xd); + return; + } + + /* + * If we find an existing XDomain connection remove it + * now. We need to go through login handshake and + * everything anyway to be able to re-establish the + * connection. + */ + remove_xdomain(xd); + tb_xdomain_put(xd); + } + + /* + * Look if there already exists an XDomain in the same place + * than the new one and in that case remove it because it is + * most likely another host that got disconnected. + */ + xd = tb_xdomain_find_by_link_depth(tb, link, depth); + if (!xd) { + u8 dual_link; + + dual_link = dual_link_from_link(link); + if (dual_link) + xd = tb_xdomain_find_by_link_depth(tb, dual_link, + depth); + } + if (xd) { + remove_xdomain(xd); + tb_xdomain_put(xd); + } + + /* + * If the user disconnected a switch during suspend and + * connected another host to the same port, remove the switch + * first. + */ + sw = get_switch_at_route(tb->root_switch, route); + if (sw) + remove_switch(sw); + + sw = tb_switch_find_by_link_depth(tb, link, depth); + if (!sw) { + tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, + depth); + return; + } + + xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, + &pkg->local_uuid, &pkg->remote_uuid); + if (!xd) { + tb_switch_put(sw); + return; + } + + xd->link = link; + xd->depth = depth; + + tb_port_at(route, sw)->xdomain = xd; + + tb_xdomain_add(xd); + tb_switch_put(sw); +} + +static void +icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) +{ + const struct icm_fr_event_xdomain_disconnected *pkg = + (const struct icm_fr_event_xdomain_disconnected *)hdr; + struct tb_xdomain *xd; + + /* + * If the connection is through one or multiple devices, the + * XDomain device is removed along with them so it is fine if we + * cannot find it here. + */ + xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); + if (xd) { + remove_xdomain(xd); + tb_xdomain_put(xd); + } +} + static struct pci_dev *get_upstream_port(struct pci_dev *pdev) { struct pci_dev *parent; @@ -594,6 +783,12 @@ static void icm_handle_notification(struct work_struct *work) case ICM_EVENT_DEVICE_DISCONNECTED: icm->device_disconnected(tb, n->pkg); break; + case ICM_EVENT_XDOMAIN_CONNECTED: + icm->xdomain_connected(tb, n->pkg); + break; + case ICM_EVENT_XDOMAIN_DISCONNECTED: + icm->xdomain_disconnected(tb, n->pkg); + break; } mutex_unlock(&tb->lock); @@ -927,6 +1122,10 @@ static void icm_unplug_children(struct tb_switch *sw) if (tb_is_upstream_port(port)) continue; + if (port->xdomain) { + port->xdomain->is_unplugged = true; + continue; + } if (!port->remote) continue; @@ -943,6 +1142,13 @@ static void icm_free_unplugged_children(struct tb_switch *sw) if (tb_is_upstream_port(port)) continue; + + if (port->xdomain && port->xdomain->is_unplugged) { + tb_xdomain_remove(port->xdomain); + port->xdomain = NULL; + continue; + } + if (!port->remote) continue; @@ -1009,8 +1215,10 @@ static int icm_start(struct tb *tb) tb->root_switch->no_nvm_upgrade = x86_apple_machine; ret = tb_switch_add(tb->root_switch); - if (ret) + if (ret) { tb_switch_put(tb->root_switch); + tb->root_switch = NULL; + } return ret; } @@ -1042,6 +1250,8 @@ static const struct tb_cm_ops icm_fr_ops = { .add_switch_key = icm_fr_add_switch_key, .challenge_switch_key = icm_fr_challenge_switch_key, .disconnect_pcie_paths = icm_disconnect_pcie_paths, + .approve_xdomain_paths = icm_fr_approve_xdomain_paths, + .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, }; struct tb *icm_probe(struct tb_nhi *nhi) @@ -1064,6 +1274,8 @@ struct tb *icm_probe(struct tb_nhi *nhi) icm->get_route = icm_fr_get_route; icm->device_connected = icm_fr_device_connected; icm->device_disconnected = icm_fr_device_disconnected; + icm->xdomain_connected = icm_fr_xdomain_connected; + icm->xdomain_disconnected = icm_fr_xdomain_disconnected; tb->cm_ops = &icm_fr_ops; break; @@ -1077,6 +1289,8 @@ struct tb *icm_probe(struct tb_nhi *nhi) icm->get_route = icm_ar_get_route; icm->device_connected = icm_fr_device_connected; icm->device_disconnected = icm_fr_device_disconnected; + icm->xdomain_connected = icm_fr_xdomain_connected; + icm->xdomain_disconnected = icm_fr_xdomain_disconnected; tb->cm_ops = &icm_fr_ops; break; } diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 5b5bb2c436be..0e05828983db 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -157,6 +157,8 @@ enum nhi_mailbox_cmd { NHI_MAILBOX_SAVE_DEVS = 0x05, NHI_MAILBOX_DISCONNECT_PCIE_PATHS = 0x06, NHI_MAILBOX_DRV_UNLOADS = 0x07, + NHI_MAILBOX_DISCONNECT_PA = 0x10, + NHI_MAILBOX_DISCONNECT_PB = 0x11, NHI_MAILBOX_ALLOW_ALL_DEVS = 0x23, }; diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 53f40c57df59..dfc357d33e1e 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -171,11 +171,11 @@ static int nvm_authenticate_host(struct tb_switch *sw) /* * Root switch NVM upgrade requires that we disconnect the - * existing PCIe paths first (in case it is not in safe mode + * existing paths first (in case it is not in safe mode * already). */ if (!sw->safe_mode) { - ret = tb_domain_disconnect_pcie_paths(sw->tb); + ret = tb_domain_disconnect_all_paths(sw->tb); if (ret) return ret; /* @@ -1363,6 +1363,9 @@ void tb_switch_remove(struct tb_switch *sw) if (sw->ports[i].remote) tb_switch_remove(sw->ports[i].remote->sw); sw->ports[i].remote = NULL; + if (sw->ports[i].xdomain) + tb_xdomain_remove(sw->ports[i].xdomain); + sw->ports[i].xdomain = NULL; } if (!sw->is_unplugged) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index ea21d927bd09..74af9d4929ab 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -9,6 +9,7 @@ #include #include +#include #include #include "tb_regs.h" @@ -109,14 +110,25 @@ struct tb_switch { /** * struct tb_port - a thunderbolt port, part of a tb_switch + * @config: Cached port configuration read from registers + * @sw: Switch the port belongs to + * @remote: Remote port (%NULL if not connected) + * @xdomain: Remote host (%NULL if not connected) + * @cap_phy: Offset, zero if not found + * @port: Port number on switch + * @disabled: Disabled by eeprom + * @dual_link_port: If the switch is connected using two ports, points + * to the other port. + * @link_nr: Is this primary or secondary port on the dual_link. */ struct tb_port { struct tb_regs_port_header config; struct tb_switch *sw; - struct tb_port *remote; /* remote port, NULL if not connected */ - int cap_phy; /* offset, zero if not found */ - u8 port; /* port number on switch */ - bool disabled; /* disabled by eeprom */ + struct tb_port *remote; + struct tb_xdomain *xdomain; + int cap_phy; + u8 port; + bool disabled; struct tb_port *dual_link_port; u8 link_nr:1; }; @@ -189,6 +201,8 @@ struct tb_path { * @add_switch_key: Add key to switch * @challenge_switch_key: Challenge switch using key * @disconnect_pcie_paths: Disconnects PCIe paths before NVM update + * @approve_xdomain_paths: Approve (establish) XDomain DMA paths + * @disconnect_xdomain_paths: Disconnect XDomain DMA paths */ struct tb_cm_ops { int (*driver_ready)(struct tb *tb); @@ -205,6 +219,8 @@ struct tb_cm_ops { int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw, const u8 *challenge, u8 *response); int (*disconnect_pcie_paths)(struct tb *tb); + int (*approve_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd); + int (*disconnect_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd); }; static inline void *tb_priv(struct tb *tb) @@ -331,6 +347,8 @@ extern struct device_type tb_switch_type; int tb_domain_init(void); void tb_domain_exit(void); void tb_switch_exit(void); +int tb_xdomain_init(void); +void tb_xdomain_exit(void); struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize); int tb_domain_add(struct tb *tb); @@ -343,6 +361,9 @@ int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw); int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw); int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw); int tb_domain_disconnect_pcie_paths(struct tb *tb); +int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd); +int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd); +int tb_domain_disconnect_all_paths(struct tb *tb); static inline void tb_domain_put(struct tb *tb) { @@ -422,4 +443,14 @@ static inline u64 tb_downstream_route(struct tb_port *port) | ((u64) port->port << (port->sw->config.depth * 8)); } +bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type, + const void *buf, size_t size); +struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, + u64 route, const uuid_t *local_uuid, + const uuid_t *remote_uuid); +void tb_xdomain_add(struct tb_xdomain *xd); +void tb_xdomain_remove(struct tb_xdomain *xd); +struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link, + u8 depth); + #endif diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index f2b2550cd97c..b0a092baa605 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -101,11 +101,14 @@ enum icm_pkg_code { ICM_CHALLENGE_DEVICE = 0x5, ICM_ADD_DEVICE_KEY = 0x6, ICM_GET_ROUTE = 0xa, + ICM_APPROVE_XDOMAIN = 0x10, }; enum icm_event_code { ICM_EVENT_DEVICE_CONNECTED = 3, ICM_EVENT_DEVICE_DISCONNECTED = 4, + ICM_EVENT_XDOMAIN_CONNECTED = 6, + ICM_EVENT_XDOMAIN_DISCONNECTED = 7, }; struct icm_pkg_header { @@ -188,6 +191,25 @@ struct icm_fr_event_device_disconnected { u16 link_info; }; +struct icm_fr_event_xdomain_connected { + struct icm_pkg_header hdr; + u16 reserved; + u16 link_info; + uuid_t remote_uuid; + uuid_t local_uuid; + u32 local_route_hi; + u32 local_route_lo; + u32 remote_route_hi; + u32 remote_route_lo; +}; + +struct icm_fr_event_xdomain_disconnected { + struct icm_pkg_header hdr; + u16 reserved; + u16 link_info; + uuid_t remote_uuid; +}; + struct icm_fr_pkg_add_device_key { struct icm_pkg_header hdr; uuid_t ep_uuid; @@ -224,6 +246,28 @@ struct icm_fr_pkg_challenge_device_response { u32 response[8]; }; +struct icm_fr_pkg_approve_xdomain { + struct icm_pkg_header hdr; + u16 reserved; + u16 link_info; + uuid_t remote_uuid; + u16 transmit_path; + u16 transmit_ring; + u16 receive_path; + u16 receive_ring; +}; + +struct icm_fr_pkg_approve_xdomain_response { + struct icm_pkg_header hdr; + u16 reserved; + u16 link_info; + uuid_t remote_uuid; + u16 transmit_path; + u16 transmit_ring; + u16 receive_path; + u16 receive_ring; +}; + /* Alpine Ridge only messages */ struct icm_ar_pkg_get_route { @@ -240,4 +284,83 @@ struct icm_ar_pkg_get_route_response { u32 route_lo; }; +/* XDomain messages */ + +struct tb_xdomain_header { + u32 route_hi; + u32 route_lo; + u32 length_sn; +}; + +#define TB_XDOMAIN_LENGTH_MASK GENMASK(5, 0) +#define TB_XDOMAIN_SN_MASK GENMASK(28, 27) +#define TB_XDOMAIN_SN_SHIFT 27 + +enum tb_xdp_type { + UUID_REQUEST_OLD = 1, + UUID_RESPONSE = 2, + PROPERTIES_REQUEST, + PROPERTIES_RESPONSE, + PROPERTIES_CHANGED_REQUEST, + PROPERTIES_CHANGED_RESPONSE, + ERROR_RESPONSE, + UUID_REQUEST = 12, +}; + +struct tb_xdp_header { + struct tb_xdomain_header xd_hdr; + uuid_t uuid; + u32 type; +}; + +struct tb_xdp_properties { + struct tb_xdp_header hdr; + uuid_t src_uuid; + uuid_t dst_uuid; + u16 offset; + u16 reserved; +}; + +struct tb_xdp_properties_response { + struct tb_xdp_header hdr; + uuid_t src_uuid; + uuid_t dst_uuid; + u16 offset; + u16 data_length; + u32 generation; + u32 data[0]; +}; + +/* + * Max length of data array single XDomain property response is allowed + * to carry. + */ +#define TB_XDP_PROPERTIES_MAX_DATA_LENGTH \ + (((256 - 4 - sizeof(struct tb_xdp_properties_response))) / 4) + +/* Maximum size of the total property block in dwords we allow */ +#define TB_XDP_PROPERTIES_MAX_LENGTH 500 + +struct tb_xdp_properties_changed { + struct tb_xdp_header hdr; + uuid_t src_uuid; +}; + +struct tb_xdp_properties_changed_response { + struct tb_xdp_header hdr; +}; + +enum tb_xdp_error { + ERROR_SUCCESS, + ERROR_UNKNOWN_PACKET, + ERROR_UNKNOWN_DOMAIN, + ERROR_NOT_SUPPORTED, + ERROR_NOT_READY, +}; + +struct tb_xdp_error_response { + struct tb_xdp_header hdr; + u32 error; +}; + #endif diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c new file mode 100644 index 000000000000..f2d06f6f7be9 --- /dev/null +++ b/drivers/thunderbolt/xdomain.c @@ -0,0 +1,1576 @@ +/* + * Thunderbolt XDomain discovery protocol support + * + * Copyright (C) 2017, Intel Corporation + * Authors: Michael Jamet + * Mika Westerberg + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "tb.h" + +#define XDOMAIN_DEFAULT_TIMEOUT 5000 /* ms */ +#define XDOMAIN_PROPERTIES_RETRIES 60 +#define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10 + +struct xdomain_request_work { + struct work_struct work; + struct tb_xdp_header *pkg; + struct tb *tb; +}; + +/* Serializes access to the properties and protocol handlers below */ +static DEFINE_MUTEX(xdomain_lock); + +/* Properties exposed to the remote domains */ +static struct tb_property_dir *xdomain_property_dir; +static u32 *xdomain_property_block; +static u32 xdomain_property_block_len; +static u32 xdomain_property_block_gen; + +/* Additional protocol handlers */ +static LIST_HEAD(protocol_handlers); + +/* UUID for XDomain discovery protocol: b638d70e-42ff-40bb-97c2-90e2c0b2ff07 */ +static const uuid_t tb_xdp_uuid = + UUID_INIT(0xb638d70e, 0x42ff, 0x40bb, + 0x97, 0xc2, 0x90, 0xe2, 0xc0, 0xb2, 0xff, 0x07); + +static bool tb_xdomain_match(const struct tb_cfg_request *req, + const struct ctl_pkg *pkg) +{ + switch (pkg->frame.eof) { + case TB_CFG_PKG_ERROR: + return true; + + case TB_CFG_PKG_XDOMAIN_RESP: { + const struct tb_xdp_header *res_hdr = pkg->buffer; + const struct tb_xdp_header *req_hdr = req->request; + u8 req_seq, res_seq; + + if (pkg->frame.size < req->response_size / 4) + return false; + + /* Make sure route matches */ + if ((res_hdr->xd_hdr.route_hi & ~BIT(31)) != + req_hdr->xd_hdr.route_hi) + return false; + if ((res_hdr->xd_hdr.route_lo) != req_hdr->xd_hdr.route_lo) + return false; + + /* Then check that the sequence number matches */ + res_seq = res_hdr->xd_hdr.length_sn & TB_XDOMAIN_SN_MASK; + res_seq >>= TB_XDOMAIN_SN_SHIFT; + req_seq = req_hdr->xd_hdr.length_sn & TB_XDOMAIN_SN_MASK; + req_seq >>= TB_XDOMAIN_SN_SHIFT; + if (res_seq != req_seq) + return false; + + /* Check that the XDomain protocol matches */ + if (!uuid_equal(&res_hdr->uuid, &req_hdr->uuid)) + return false; + + return true; + } + + default: + return false; + } +} + +static bool tb_xdomain_copy(struct tb_cfg_request *req, + const struct ctl_pkg *pkg) +{ + memcpy(req->response, pkg->buffer, req->response_size); + req->result.err = 0; + return true; +} + +static void response_ready(void *data) +{ + tb_cfg_request_put(data); +} + +static int __tb_xdomain_response(struct tb_ctl *ctl, const void *response, + size_t size, enum tb_cfg_pkg_type type) +{ + struct tb_cfg_request *req; + + req = tb_cfg_request_alloc(); + if (!req) + return -ENOMEM; + + req->match = tb_xdomain_match; + req->copy = tb_xdomain_copy; + req->request = response; + req->request_size = size; + req->request_type = type; + + return tb_cfg_request(ctl, req, response_ready, req); +} + +/** + * tb_xdomain_response() - Send a XDomain response message + * @xd: XDomain to send the message + * @response: Response to send + * @size: Size of the response + * @type: PDF type of the response + * + * This can be used to send a XDomain response message to the other + * domain. No response for the message is expected. + * + * Return: %0 in case of success and negative errno in case of failure + */ +int tb_xdomain_response(struct tb_xdomain *xd, const void *response, + size_t size, enum tb_cfg_pkg_type type) +{ + return __tb_xdomain_response(xd->tb->ctl, response, size, type); +} +EXPORT_SYMBOL_GPL(tb_xdomain_response); + +static int __tb_xdomain_request(struct tb_ctl *ctl, const void *request, + size_t request_size, enum tb_cfg_pkg_type request_type, void *response, + size_t response_size, enum tb_cfg_pkg_type response_type, + unsigned int timeout_msec) +{ + struct tb_cfg_request *req; + struct tb_cfg_result res; + + req = tb_cfg_request_alloc(); + if (!req) + return -ENOMEM; + + req->match = tb_xdomain_match; + req->copy = tb_xdomain_copy; + req->request = request; + req->request_size = request_size; + req->request_type = request_type; + req->response = response; + req->response_size = response_size; + req->response_type = response_type; + + res = tb_cfg_request_sync(ctl, req, timeout_msec); + + tb_cfg_request_put(req); + + return res.err == 1 ? -EIO : res.err; +} + +/** + * tb_xdomain_request() - Send a XDomain request + * @xd: XDomain to send the request + * @request: Request to send + * @request_size: Size of the request in bytes + * @request_type: PDF type of the request + * @response: Response is copied here + * @response_size: Expected size of the response in bytes + * @response_type: Expected PDF type of the response + * @timeout_msec: Timeout in milliseconds to wait for the response + * + * This function can be used to send XDomain control channel messages to + * the other domain. The function waits until the response is received + * or when timeout triggers. Whichever comes first. + * + * Return: %0 in case of success and negative errno in case of failure + */ +int tb_xdomain_request(struct tb_xdomain *xd, const void *request, + size_t request_size, enum tb_cfg_pkg_type request_type, + void *response, size_t response_size, + enum tb_cfg_pkg_type response_type, unsigned int timeout_msec) +{ + return __tb_xdomain_request(xd->tb->ctl, request, request_size, + request_type, response, response_size, + response_type, timeout_msec); +} +EXPORT_SYMBOL_GPL(tb_xdomain_request); + +static inline void tb_xdp_fill_header(struct tb_xdp_header *hdr, u64 route, + u8 sequence, enum tb_xdp_type type, size_t size) +{ + u32 length_sn; + + length_sn = (size - sizeof(hdr->xd_hdr)) / 4; + length_sn |= (sequence << TB_XDOMAIN_SN_SHIFT) & TB_XDOMAIN_SN_MASK; + + hdr->xd_hdr.route_hi = upper_32_bits(route); + hdr->xd_hdr.route_lo = lower_32_bits(route); + hdr->xd_hdr.length_sn = length_sn; + hdr->type = type; + memcpy(&hdr->uuid, &tb_xdp_uuid, sizeof(tb_xdp_uuid)); +} + +static int tb_xdp_handle_error(const struct tb_xdp_header *hdr) +{ + const struct tb_xdp_error_response *error; + + if (hdr->type != ERROR_RESPONSE) + return 0; + + error = (const struct tb_xdp_error_response *)hdr; + + switch (error->error) { + case ERROR_UNKNOWN_PACKET: + case ERROR_UNKNOWN_DOMAIN: + return -EIO; + case ERROR_NOT_SUPPORTED: + return -ENOTSUPP; + case ERROR_NOT_READY: + return -EAGAIN; + default: + break; + } + + return 0; +} + +static int tb_xdp_error_response(struct tb_ctl *ctl, u64 route, u8 sequence, + enum tb_xdp_error error) +{ + struct tb_xdp_error_response res; + + memset(&res, 0, sizeof(res)); + tb_xdp_fill_header(&res.hdr, route, sequence, ERROR_RESPONSE, + sizeof(res)); + res.error = error; + + return __tb_xdomain_response(ctl, &res, sizeof(res), + TB_CFG_PKG_XDOMAIN_RESP); +} + +static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route, + const uuid_t *src_uuid, const uuid_t *dst_uuid, int retry, + u32 **block, u32 *generation) +{ + struct tb_xdp_properties_response *res; + struct tb_xdp_properties req; + u16 data_len, len; + size_t total_size; + u32 *data = NULL; + int ret; + + total_size = sizeof(*res) + TB_XDP_PROPERTIES_MAX_DATA_LENGTH * 4; + res = kzalloc(total_size, GFP_KERNEL); + if (!res) + return -ENOMEM; + + memset(&req, 0, sizeof(req)); + tb_xdp_fill_header(&req.hdr, route, retry % 4, PROPERTIES_REQUEST, + sizeof(req)); + memcpy(&req.src_uuid, src_uuid, sizeof(*src_uuid)); + memcpy(&req.dst_uuid, dst_uuid, sizeof(*dst_uuid)); + + len = 0; + data_len = 0; + + do { + ret = __tb_xdomain_request(ctl, &req, sizeof(req), + TB_CFG_PKG_XDOMAIN_REQ, res, + total_size, TB_CFG_PKG_XDOMAIN_RESP, + XDOMAIN_DEFAULT_TIMEOUT); + if (ret) + goto err; + + ret = tb_xdp_handle_error(&res->hdr); + if (ret) + goto err; + + /* + * Package length includes the whole payload without the + * XDomain header. Validate first that the package is at + * least size of the response structure. + */ + len = res->hdr.xd_hdr.length_sn & TB_XDOMAIN_LENGTH_MASK; + if (len < sizeof(*res) / 4) { + ret = -EINVAL; + goto err; + } + + len += sizeof(res->hdr.xd_hdr) / 4; + len -= sizeof(*res) / 4; + + if (res->offset != req.offset) { + ret = -EINVAL; + goto err; + } + + /* + * First time allocate block that has enough space for + * the whole properties block. + */ + if (!data) { + data_len = res->data_length; + if (data_len > TB_XDP_PROPERTIES_MAX_LENGTH) { + ret = -E2BIG; + goto err; + } + + data = kcalloc(data_len, sizeof(u32), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err; + } + } + + memcpy(data + req.offset, res->data, len * 4); + req.offset += len; + } while (!data_len || req.offset < data_len); + + *block = data; + *generation = res->generation; + + kfree(res); + + return data_len; + +err: + kfree(data); + kfree(res); + + return ret; +} + +static int tb_xdp_properties_response(struct tb *tb, struct tb_ctl *ctl, + u64 route, u8 sequence, const uuid_t *src_uuid, + const struct tb_xdp_properties *req) +{ + struct tb_xdp_properties_response *res; + size_t total_size; + u16 len; + int ret; + + /* + * Currently we expect all requests to be directed to us. The + * protocol supports forwarding, though which we might add + * support later on. + */ + if (!uuid_equal(src_uuid, &req->dst_uuid)) { + tb_xdp_error_response(ctl, route, sequence, + ERROR_UNKNOWN_DOMAIN); + return 0; + } + + mutex_lock(&xdomain_lock); + + if (req->offset >= xdomain_property_block_len) { + mutex_unlock(&xdomain_lock); + return -EINVAL; + } + + len = xdomain_property_block_len - req->offset; + len = min_t(u16, len, TB_XDP_PROPERTIES_MAX_DATA_LENGTH); + total_size = sizeof(*res) + len * 4; + + res = kzalloc(total_size, GFP_KERNEL); + if (!res) { + mutex_unlock(&xdomain_lock); + return -ENOMEM; + } + + tb_xdp_fill_header(&res->hdr, route, sequence, PROPERTIES_RESPONSE, + total_size); + res->generation = xdomain_property_block_gen; + res->data_length = xdomain_property_block_len; + res->offset = req->offset; + uuid_copy(&res->src_uuid, src_uuid); + uuid_copy(&res->dst_uuid, &req->src_uuid); + memcpy(res->data, &xdomain_property_block[req->offset], len * 4); + + mutex_unlock(&xdomain_lock); + + ret = __tb_xdomain_response(ctl, res, total_size, + TB_CFG_PKG_XDOMAIN_RESP); + + kfree(res); + return ret; +} + +static int tb_xdp_properties_changed_request(struct tb_ctl *ctl, u64 route, + int retry, const uuid_t *uuid) +{ + struct tb_xdp_properties_changed_response res; + struct tb_xdp_properties_changed req; + int ret; + + memset(&req, 0, sizeof(req)); + tb_xdp_fill_header(&req.hdr, route, retry % 4, + PROPERTIES_CHANGED_REQUEST, sizeof(req)); + uuid_copy(&req.src_uuid, uuid); + + memset(&res, 0, sizeof(res)); + ret = __tb_xdomain_request(ctl, &req, sizeof(req), + TB_CFG_PKG_XDOMAIN_REQ, &res, sizeof(res), + TB_CFG_PKG_XDOMAIN_RESP, + XDOMAIN_DEFAULT_TIMEOUT); + if (ret) + return ret; + + return tb_xdp_handle_error(&res.hdr); +} + +static int +tb_xdp_properties_changed_response(struct tb_ctl *ctl, u64 route, u8 sequence) +{ + struct tb_xdp_properties_changed_response res; + + memset(&res, 0, sizeof(res)); + tb_xdp_fill_header(&res.hdr, route, sequence, + PROPERTIES_CHANGED_RESPONSE, sizeof(res)); + return __tb_xdomain_response(ctl, &res, sizeof(res), + TB_CFG_PKG_XDOMAIN_RESP); +} + +/** + * tb_register_protocol_handler() - Register protocol handler + * @handler: Handler to register + * + * This allows XDomain service drivers to hook into incoming XDomain + * messages. After this function is called the service driver needs to + * be able to handle calls to callback whenever a package with the + * registered protocol is received. + */ +int tb_register_protocol_handler(struct tb_protocol_handler *handler) +{ + if (!handler->uuid || !handler->callback) + return -EINVAL; + if (uuid_equal(handler->uuid, &tb_xdp_uuid)) + return -EINVAL; + + mutex_lock(&xdomain_lock); + list_add_tail(&handler->list, &protocol_handlers); + mutex_unlock(&xdomain_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(tb_register_protocol_handler); + +/** + * tb_unregister_protocol_handler() - Unregister protocol handler + * @handler: Handler to unregister + * + * Removes the previously registered protocol handler. + */ +void tb_unregister_protocol_handler(struct tb_protocol_handler *handler) +{ + mutex_lock(&xdomain_lock); + list_del_init(&handler->list); + mutex_unlock(&xdomain_lock); +} +EXPORT_SYMBOL_GPL(tb_unregister_protocol_handler); + +static void tb_xdp_handle_request(struct work_struct *work) +{ + struct xdomain_request_work *xw = container_of(work, typeof(*xw), work); + const struct tb_xdp_header *pkg = xw->pkg; + const struct tb_xdomain_header *xhdr = &pkg->xd_hdr; + struct tb *tb = xw->tb; + struct tb_ctl *ctl = tb->ctl; + const uuid_t *uuid; + int ret = 0; + u8 sequence; + u64 route; + + route = ((u64)xhdr->route_hi << 32 | xhdr->route_lo) & ~BIT_ULL(63); + sequence = xhdr->length_sn & TB_XDOMAIN_SN_MASK; + sequence >>= TB_XDOMAIN_SN_SHIFT; + + mutex_lock(&tb->lock); + if (tb->root_switch) + uuid = tb->root_switch->uuid; + else + uuid = NULL; + mutex_unlock(&tb->lock); + + if (!uuid) { + tb_xdp_error_response(ctl, route, sequence, ERROR_NOT_READY); + goto out; + } + + switch (pkg->type) { + case PROPERTIES_REQUEST: + ret = tb_xdp_properties_response(tb, ctl, route, sequence, uuid, + (const struct tb_xdp_properties *)pkg); + break; + + case PROPERTIES_CHANGED_REQUEST: { + const struct tb_xdp_properties_changed *xchg = + (const struct tb_xdp_properties_changed *)pkg; + struct tb_xdomain *xd; + + ret = tb_xdp_properties_changed_response(ctl, route, sequence); + + /* + * Since the properties have been changed, let's update + * the xdomain related to this connection as well in + * case there is a change in services it offers. + */ + xd = tb_xdomain_find_by_uuid_locked(tb, &xchg->src_uuid); + if (xd) { + queue_delayed_work(tb->wq, &xd->get_properties_work, + msecs_to_jiffies(50)); + tb_xdomain_put(xd); + } + + break; + } + + default: + break; + } + + if (ret) { + tb_warn(tb, "failed to send XDomain response for %#x\n", + pkg->type); + } + +out: + kfree(xw->pkg); + kfree(xw); +} + +static void +tb_xdp_schedule_request(struct tb *tb, const struct tb_xdp_header *hdr, + size_t size) +{ + struct xdomain_request_work *xw; + + xw = kmalloc(sizeof(*xw), GFP_KERNEL); + if (!xw) + return; + + INIT_WORK(&xw->work, tb_xdp_handle_request); + xw->pkg = kmemdup(hdr, size, GFP_KERNEL); + xw->tb = tb; + + queue_work(tb->wq, &xw->work); +} + +/** + * tb_register_service_driver() - Register XDomain service driver + * @drv: Driver to register + * + * Registers new service driver from @drv to the bus. + */ +int tb_register_service_driver(struct tb_service_driver *drv) +{ + drv->driver.bus = &tb_bus_type; + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(tb_register_service_driver); + +/** + * tb_unregister_service_driver() - Unregister XDomain service driver + * @xdrv: Driver to unregister + * + * Unregisters XDomain service driver from the bus. + */ +void tb_unregister_service_driver(struct tb_service_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(tb_unregister_service_driver); + +static ssize_t key_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + /* + * It should be null terminated but anything else is pretty much + * allowed. + */ + return sprintf(buf, "%*pEp\n", (int)strlen(svc->key), svc->key); +} +static DEVICE_ATTR_RO(key); + +static int get_modalias(struct tb_service *svc, char *buf, size_t size) +{ + return snprintf(buf, size, "tbsvc:k%sp%08Xv%08Xr%08X", svc->key, + svc->prtcid, svc->prtcvers, svc->prtcrevs); +} + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + /* Full buffer size except new line and null termination */ + get_modalias(svc, buf, PAGE_SIZE - 2); + return sprintf(buf, "%s\n", buf); +} +static DEVICE_ATTR_RO(modalias); + +static ssize_t prtcid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + return sprintf(buf, "%u\n", svc->prtcid); +} +static DEVICE_ATTR_RO(prtcid); + +static ssize_t prtcvers_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + return sprintf(buf, "%u\n", svc->prtcvers); +} +static DEVICE_ATTR_RO(prtcvers); + +static ssize_t prtcrevs_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + return sprintf(buf, "%u\n", svc->prtcrevs); +} +static DEVICE_ATTR_RO(prtcrevs); + +static ssize_t prtcstns_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + return sprintf(buf, "0x%08x\n", svc->prtcstns); +} +static DEVICE_ATTR_RO(prtcstns); + +static struct attribute *tb_service_attrs[] = { + &dev_attr_key.attr, + &dev_attr_modalias.attr, + &dev_attr_prtcid.attr, + &dev_attr_prtcvers.attr, + &dev_attr_prtcrevs.attr, + &dev_attr_prtcstns.attr, + NULL, +}; + +static struct attribute_group tb_service_attr_group = { + .attrs = tb_service_attrs, +}; + +static const struct attribute_group *tb_service_attr_groups[] = { + &tb_service_attr_group, + NULL, +}; + +static int tb_service_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + char modalias[64]; + + get_modalias(svc, modalias, sizeof(modalias)); + return add_uevent_var(env, "MODALIAS=%s", modalias); +} + +static void tb_service_release(struct device *dev) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + struct tb_xdomain *xd = tb_service_parent(svc); + + ida_simple_remove(&xd->service_ids, svc->id); + kfree(svc->key); + kfree(svc); +} + +struct device_type tb_service_type = { + .name = "thunderbolt_service", + .groups = tb_service_attr_groups, + .uevent = tb_service_uevent, + .release = tb_service_release, +}; +EXPORT_SYMBOL_GPL(tb_service_type); + +static int remove_missing_service(struct device *dev, void *data) +{ + struct tb_xdomain *xd = data; + struct tb_service *svc; + + svc = tb_to_service(dev); + if (!svc) + return 0; + + if (!tb_property_find(xd->properties, svc->key, + TB_PROPERTY_TYPE_DIRECTORY)) + device_unregister(dev); + + return 0; +} + +static int find_service(struct device *dev, void *data) +{ + const struct tb_property *p = data; + struct tb_service *svc; + + svc = tb_to_service(dev); + if (!svc) + return 0; + + return !strcmp(svc->key, p->key); +} + +static int populate_service(struct tb_service *svc, + struct tb_property *property) +{ + struct tb_property_dir *dir = property->value.dir; + struct tb_property *p; + + /* Fill in standard properties */ + p = tb_property_find(dir, "prtcid", TB_PROPERTY_TYPE_VALUE); + if (p) + svc->prtcid = p->value.immediate; + p = tb_property_find(dir, "prtcvers", TB_PROPERTY_TYPE_VALUE); + if (p) + svc->prtcvers = p->value.immediate; + p = tb_property_find(dir, "prtcrevs", TB_PROPERTY_TYPE_VALUE); + if (p) + svc->prtcrevs = p->value.immediate; + p = tb_property_find(dir, "prtcstns", TB_PROPERTY_TYPE_VALUE); + if (p) + svc->prtcstns = p->value.immediate; + + svc->key = kstrdup(property->key, GFP_KERNEL); + if (!svc->key) + return -ENOMEM; + + return 0; +} + +static void enumerate_services(struct tb_xdomain *xd) +{ + struct tb_service *svc; + struct tb_property *p; + struct device *dev; + + /* + * First remove all services that are not available anymore in + * the updated property block. + */ + device_for_each_child_reverse(&xd->dev, xd, remove_missing_service); + + /* Then re-enumerate properties creating new services as we go */ + tb_property_for_each(xd->properties, p) { + if (p->type != TB_PROPERTY_TYPE_DIRECTORY) + continue; + + /* If the service exists already we are fine */ + dev = device_find_child(&xd->dev, p, find_service); + if (dev) { + put_device(dev); + continue; + } + + svc = kzalloc(sizeof(*svc), GFP_KERNEL); + if (!svc) + break; + + if (populate_service(svc, p)) { + kfree(svc); + break; + } + + svc->id = ida_simple_get(&xd->service_ids, 0, 0, GFP_KERNEL); + svc->dev.bus = &tb_bus_type; + svc->dev.type = &tb_service_type; + svc->dev.parent = &xd->dev; + dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id); + + if (device_register(&svc->dev)) { + put_device(&svc->dev); + break; + } + } +} + +static int populate_properties(struct tb_xdomain *xd, + struct tb_property_dir *dir) +{ + const struct tb_property *p; + + /* Required properties */ + p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_VALUE); + if (!p) + return -EINVAL; + xd->device = p->value.immediate; + + p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_VALUE); + if (!p) + return -EINVAL; + xd->vendor = p->value.immediate; + + kfree(xd->device_name); + xd->device_name = NULL; + kfree(xd->vendor_name); + xd->vendor_name = NULL; + + /* Optional properties */ + p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_TEXT); + if (p) + xd->device_name = kstrdup(p->value.text, GFP_KERNEL); + p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_TEXT); + if (p) + xd->vendor_name = kstrdup(p->value.text, GFP_KERNEL); + + return 0; +} + +/* Called with @xd->lock held */ +static void tb_xdomain_restore_paths(struct tb_xdomain *xd) +{ + if (!xd->resume) + return; + + xd->resume = false; + if (xd->transmit_path) { + dev_dbg(&xd->dev, "re-establishing DMA path\n"); + tb_domain_approve_xdomain_paths(xd->tb, xd); + } +} + +static void tb_xdomain_get_properties(struct work_struct *work) +{ + struct tb_xdomain *xd = container_of(work, typeof(*xd), + get_properties_work.work); + struct tb_property_dir *dir; + struct tb *tb = xd->tb; + bool update = false; + u32 *block = NULL; + u32 gen = 0; + int ret; + + ret = tb_xdp_properties_request(tb->ctl, xd->route, xd->local_uuid, + xd->remote_uuid, xd->properties_retries, + &block, &gen); + if (ret < 0) { + if (xd->properties_retries-- > 0) { + queue_delayed_work(xd->tb->wq, &xd->get_properties_work, + msecs_to_jiffies(1000)); + } else { + /* Give up now */ + dev_err(&xd->dev, + "failed read XDomain properties from %pUb\n", + xd->remote_uuid); + } + return; + } + + xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; + + mutex_lock(&xd->lock); + + /* Only accept newer generation properties */ + if (xd->properties && gen <= xd->property_block_gen) { + /* + * On resume it is likely that the properties block is + * not changed (unless the other end added or removed + * services). However, we need to make sure the existing + * DMA paths are restored properly. + */ + tb_xdomain_restore_paths(xd); + goto err_free_block; + } + + dir = tb_property_parse_dir(block, ret); + if (!dir) { + dev_err(&xd->dev, "failed to parse XDomain properties\n"); + goto err_free_block; + } + + ret = populate_properties(xd, dir); + if (ret) { + dev_err(&xd->dev, "missing XDomain properties in response\n"); + goto err_free_dir; + } + + /* Release the existing one */ + if (xd->properties) { + tb_property_free_dir(xd->properties); + update = true; + } + + xd->properties = dir; + xd->property_block_gen = gen; + + tb_xdomain_restore_paths(xd); + + mutex_unlock(&xd->lock); + + kfree(block); + + /* + * Now the device should be ready enough so we can add it to the + * bus and let userspace know about it. If the device is already + * registered, we notify the userspace that it has changed. + */ + if (!update) { + if (device_add(&xd->dev)) { + dev_err(&xd->dev, "failed to add XDomain device\n"); + return; + } + } else { + kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE); + } + + enumerate_services(xd); + return; + +err_free_dir: + tb_property_free_dir(dir); +err_free_block: + kfree(block); + mutex_unlock(&xd->lock); +} + +static void tb_xdomain_properties_changed(struct work_struct *work) +{ + struct tb_xdomain *xd = container_of(work, typeof(*xd), + properties_changed_work.work); + int ret; + + ret = tb_xdp_properties_changed_request(xd->tb->ctl, xd->route, + xd->properties_changed_retries, xd->local_uuid); + if (ret) { + if (xd->properties_changed_retries-- > 0) + queue_delayed_work(xd->tb->wq, + &xd->properties_changed_work, + msecs_to_jiffies(1000)); + return; + } + + xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES; +} + +static ssize_t device_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%#x\n", xd->device); +} +static DEVICE_ATTR_RO(device); + +static ssize_t +device_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + int ret; + + if (mutex_lock_interruptible(&xd->lock)) + return -ERESTARTSYS; + ret = sprintf(buf, "%s\n", xd->device_name ? xd->device_name : ""); + mutex_unlock(&xd->lock); + + return ret; +} +static DEVICE_ATTR_RO(device_name); + +static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%#x\n", xd->vendor); +} +static DEVICE_ATTR_RO(vendor); + +static ssize_t +vendor_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + int ret; + + if (mutex_lock_interruptible(&xd->lock)) + return -ERESTARTSYS; + ret = sprintf(buf, "%s\n", xd->vendor_name ? xd->vendor_name : ""); + mutex_unlock(&xd->lock); + + return ret; +} +static DEVICE_ATTR_RO(vendor_name); + +static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%pUb\n", xd->remote_uuid); +} +static DEVICE_ATTR_RO(unique_id); + +static struct attribute *xdomain_attrs[] = { + &dev_attr_device.attr, + &dev_attr_device_name.attr, + &dev_attr_unique_id.attr, + &dev_attr_vendor.attr, + &dev_attr_vendor_name.attr, + NULL, +}; + +static struct attribute_group xdomain_attr_group = { + .attrs = xdomain_attrs, +}; + +static const struct attribute_group *xdomain_attr_groups[] = { + &xdomain_attr_group, + NULL, +}; + +static void tb_xdomain_release(struct device *dev) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + put_device(xd->dev.parent); + + tb_property_free_dir(xd->properties); + ida_destroy(&xd->service_ids); + + kfree(xd->local_uuid); + kfree(xd->remote_uuid); + kfree(xd->device_name); + kfree(xd->vendor_name); + kfree(xd); +} + +static void start_handshake(struct tb_xdomain *xd) +{ + xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; + xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES; + + /* Start exchanging properties with the other host */ + queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, + msecs_to_jiffies(100)); + queue_delayed_work(xd->tb->wq, &xd->get_properties_work, + msecs_to_jiffies(1000)); +} + +static void stop_handshake(struct tb_xdomain *xd) +{ + xd->properties_retries = 0; + xd->properties_changed_retries = 0; + + cancel_delayed_work_sync(&xd->get_properties_work); + cancel_delayed_work_sync(&xd->properties_changed_work); +} + +static int __maybe_unused tb_xdomain_suspend(struct device *dev) +{ + stop_handshake(tb_to_xdomain(dev)); + return 0; +} + +static int __maybe_unused tb_xdomain_resume(struct device *dev) +{ + struct tb_xdomain *xd = tb_to_xdomain(dev); + + /* + * Ask tb_xdomain_get_properties() restore any existing DMA + * paths after properties are re-read. + */ + xd->resume = true; + start_handshake(xd); + + return 0; +} + +static const struct dev_pm_ops tb_xdomain_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tb_xdomain_suspend, tb_xdomain_resume) +}; + +struct device_type tb_xdomain_type = { + .name = "thunderbolt_xdomain", + .release = tb_xdomain_release, + .pm = &tb_xdomain_pm_ops, +}; +EXPORT_SYMBOL_GPL(tb_xdomain_type); + +/** + * tb_xdomain_alloc() - Allocate new XDomain object + * @tb: Domain where the XDomain belongs + * @parent: Parent device (the switch through the connection to the + * other domain is reached). + * @route: Route string used to reach the other domain + * @local_uuid: Our local domain UUID + * @remote_uuid: UUID of the other domain + * + * Allocates new XDomain structure and returns pointer to that. The + * object must be released by calling tb_xdomain_put(). + */ +struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, + u64 route, const uuid_t *local_uuid, + const uuid_t *remote_uuid) +{ + struct tb_xdomain *xd; + + xd = kzalloc(sizeof(*xd), GFP_KERNEL); + if (!xd) + return NULL; + + xd->tb = tb; + xd->route = route; + ida_init(&xd->service_ids); + mutex_init(&xd->lock); + INIT_DELAYED_WORK(&xd->get_properties_work, tb_xdomain_get_properties); + INIT_DELAYED_WORK(&xd->properties_changed_work, + tb_xdomain_properties_changed); + + xd->local_uuid = kmemdup(local_uuid, sizeof(uuid_t), GFP_KERNEL); + if (!xd->local_uuid) + goto err_free; + + xd->remote_uuid = kmemdup(remote_uuid, sizeof(uuid_t), GFP_KERNEL); + if (!xd->remote_uuid) + goto err_free_local_uuid; + + device_initialize(&xd->dev); + xd->dev.parent = get_device(parent); + xd->dev.bus = &tb_bus_type; + xd->dev.type = &tb_xdomain_type; + xd->dev.groups = xdomain_attr_groups; + dev_set_name(&xd->dev, "%u-%llx", tb->index, route); + + return xd; + +err_free_local_uuid: + kfree(xd->local_uuid); +err_free: + kfree(xd); + + return NULL; +} + +/** + * tb_xdomain_add() - Add XDomain to the bus + * @xd: XDomain to add + * + * This function starts XDomain discovery protocol handshake and + * eventually adds the XDomain to the bus. After calling this function + * the caller needs to call tb_xdomain_remove() in order to remove and + * release the object regardless whether the handshake succeeded or not. + */ +void tb_xdomain_add(struct tb_xdomain *xd) +{ + /* Start exchanging properties with the other host */ + start_handshake(xd); +} + +static int unregister_service(struct device *dev, void *data) +{ + device_unregister(dev); + return 0; +} + +/** + * tb_xdomain_remove() - Remove XDomain from the bus + * @xd: XDomain to remove + * + * This will stop all ongoing configuration work and remove the XDomain + * along with any services from the bus. When the last reference to @xd + * is released the object will be released as well. + */ +void tb_xdomain_remove(struct tb_xdomain *xd) +{ + stop_handshake(xd); + + device_for_each_child_reverse(&xd->dev, xd, unregister_service); + + if (!device_is_registered(&xd->dev)) + put_device(&xd->dev); + else + device_unregister(&xd->dev); +} + +/** + * tb_xdomain_enable_paths() - Enable DMA paths for XDomain connection + * @xd: XDomain connection + * @transmit_path: HopID of the transmit path the other end is using to + * send packets + * @transmit_ring: DMA ring used to receive packets from the other end + * @receive_path: HopID of the receive path the other end is using to + * receive packets + * @receive_ring: DMA ring used to send packets to the other end + * + * The function enables DMA paths accordingly so that after successful + * return the caller can send and receive packets using high-speed DMA + * path. + * + * Return: %0 in case of success and negative errno in case of error + */ +int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 transmit_path, + u16 transmit_ring, u16 receive_path, + u16 receive_ring) +{ + int ret; + + mutex_lock(&xd->lock); + + if (xd->transmit_path) { + ret = xd->transmit_path == transmit_path ? 0 : -EBUSY; + goto exit_unlock; + } + + xd->transmit_path = transmit_path; + xd->transmit_ring = transmit_ring; + xd->receive_path = receive_path; + xd->receive_ring = receive_ring; + + ret = tb_domain_approve_xdomain_paths(xd->tb, xd); + +exit_unlock: + mutex_unlock(&xd->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(tb_xdomain_enable_paths); + +/** + * tb_xdomain_disable_paths() - Disable DMA paths for XDomain connection + * @xd: XDomain connection + * + * This does the opposite of tb_xdomain_enable_paths(). After call to + * this the caller is not expected to use the rings anymore. + * + * Return: %0 in case of success and negative errno in case of error + */ +int tb_xdomain_disable_paths(struct tb_xdomain *xd) +{ + int ret = 0; + + mutex_lock(&xd->lock); + if (xd->transmit_path) { + xd->transmit_path = 0; + xd->transmit_ring = 0; + xd->receive_path = 0; + xd->receive_ring = 0; + + ret = tb_domain_disconnect_xdomain_paths(xd->tb, xd); + } + mutex_unlock(&xd->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(tb_xdomain_disable_paths); + +struct tb_xdomain_lookup { + const uuid_t *uuid; + u8 link; + u8 depth; +}; + +static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw, + const struct tb_xdomain_lookup *lookup) +{ + int i; + + for (i = 1; i <= sw->config.max_port_number; i++) { + struct tb_port *port = &sw->ports[i]; + struct tb_xdomain *xd; + + if (tb_is_upstream_port(port)) + continue; + + if (port->xdomain) { + xd = port->xdomain; + + if (lookup->uuid) { + if (uuid_equal(xd->remote_uuid, lookup->uuid)) + return xd; + } else if (lookup->link == xd->link && + lookup->depth == xd->depth) { + return xd; + } + } else if (port->remote) { + xd = switch_find_xdomain(port->remote->sw, lookup); + if (xd) + return xd; + } + } + + return NULL; +} + +/** + * tb_xdomain_find_by_uuid() - Find an XDomain by UUID + * @tb: Domain where the XDomain belongs to + * @uuid: UUID to look for + * + * Finds XDomain by walking through the Thunderbolt topology below @tb. + * The returned XDomain will have its reference count increased so the + * caller needs to call tb_xdomain_put() when it is done with the + * object. + * + * This will find all XDomains including the ones that are not yet added + * to the bus (handshake is still in progress). + * + * The caller needs to hold @tb->lock. + */ +struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid) +{ + struct tb_xdomain_lookup lookup; + struct tb_xdomain *xd; + + memset(&lookup, 0, sizeof(lookup)); + lookup.uuid = uuid; + + xd = switch_find_xdomain(tb->root_switch, &lookup); + if (xd) { + get_device(&xd->dev); + return xd; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(tb_xdomain_find_by_uuid); + +/** + * tb_xdomain_find_by_link_depth() - Find an XDomain by link and depth + * @tb: Domain where the XDomain belongs to + * @link: Root switch link number + * @depth: Depth in the link + * + * Finds XDomain by walking through the Thunderbolt topology below @tb. + * The returned XDomain will have its reference count increased so the + * caller needs to call tb_xdomain_put() when it is done with the + * object. + * + * This will find all XDomains including the ones that are not yet added + * to the bus (handshake is still in progress). + * + * The caller needs to hold @tb->lock. + */ +struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link, + u8 depth) +{ + struct tb_xdomain_lookup lookup; + struct tb_xdomain *xd; + + memset(&lookup, 0, sizeof(lookup)); + lookup.link = link; + lookup.depth = depth; + + xd = switch_find_xdomain(tb->root_switch, &lookup); + if (xd) { + get_device(&xd->dev); + return xd; + } + + return NULL; +} + +bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type, + const void *buf, size_t size) +{ + const struct tb_protocol_handler *handler, *tmp; + const struct tb_xdp_header *hdr = buf; + unsigned int length; + int ret = 0; + + /* We expect the packet is at least size of the header */ + length = hdr->xd_hdr.length_sn & TB_XDOMAIN_LENGTH_MASK; + if (length != size / 4 - sizeof(hdr->xd_hdr) / 4) + return true; + if (length < sizeof(*hdr) / 4 - sizeof(hdr->xd_hdr) / 4) + return true; + + /* + * Handle XDomain discovery protocol packets directly here. For + * other protocols (based on their UUID) we call registered + * handlers in turn. + */ + if (uuid_equal(&hdr->uuid, &tb_xdp_uuid)) { + if (type == TB_CFG_PKG_XDOMAIN_REQ) { + tb_xdp_schedule_request(tb, hdr, size); + return true; + } + return false; + } + + mutex_lock(&xdomain_lock); + list_for_each_entry_safe(handler, tmp, &protocol_handlers, list) { + if (!uuid_equal(&hdr->uuid, handler->uuid)) + continue; + + mutex_unlock(&xdomain_lock); + ret = handler->callback(buf, size, handler->data); + mutex_lock(&xdomain_lock); + + if (ret) + break; + } + mutex_unlock(&xdomain_lock); + + return ret > 0; +} + +static int rebuild_property_block(void) +{ + u32 *block, len; + int ret; + + ret = tb_property_format_dir(xdomain_property_dir, NULL, 0); + if (ret < 0) + return ret; + + len = ret; + + block = kcalloc(len, sizeof(u32), GFP_KERNEL); + if (!block) + return -ENOMEM; + + ret = tb_property_format_dir(xdomain_property_dir, block, len); + if (ret) { + kfree(block); + return ret; + } + + kfree(xdomain_property_block); + xdomain_property_block = block; + xdomain_property_block_len = len; + xdomain_property_block_gen++; + + return 0; +} + +static int update_xdomain(struct device *dev, void *data) +{ + struct tb_xdomain *xd; + + xd = tb_to_xdomain(dev); + if (xd) { + queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, + msecs_to_jiffies(50)); + } + + return 0; +} + +static void update_all_xdomains(void) +{ + bus_for_each_dev(&tb_bus_type, NULL, NULL, update_xdomain); +} + +static bool remove_directory(const char *key, const struct tb_property_dir *dir) +{ + struct tb_property *p; + + p = tb_property_find(xdomain_property_dir, key, + TB_PROPERTY_TYPE_DIRECTORY); + if (p && p->value.dir == dir) { + tb_property_remove(p); + return true; + } + return false; +} + +/** + * tb_register_property_dir() - Register property directory to the host + * @key: Key (name) of the directory to add + * @dir: Directory to add + * + * Service drivers can use this function to add new property directory + * to the host available properties. The other connected hosts are + * notified so they can re-read properties of this host if they are + * interested. + * + * Return: %0 on success and negative errno on failure + */ +int tb_register_property_dir(const char *key, struct tb_property_dir *dir) +{ + int ret; + + if (!key || strlen(key) > 8) + return -EINVAL; + + mutex_lock(&xdomain_lock); + if (tb_property_find(xdomain_property_dir, key, + TB_PROPERTY_TYPE_DIRECTORY)) { + ret = -EEXIST; + goto err_unlock; + } + + ret = tb_property_add_dir(xdomain_property_dir, key, dir); + if (ret) + goto err_unlock; + + ret = rebuild_property_block(); + if (ret) { + remove_directory(key, dir); + goto err_unlock; + } + + mutex_unlock(&xdomain_lock); + update_all_xdomains(); + return 0; + +err_unlock: + mutex_unlock(&xdomain_lock); + return ret; +} +EXPORT_SYMBOL_GPL(tb_register_property_dir); + +/** + * tb_unregister_property_dir() - Removes property directory from host + * @key: Key (name) of the directory + * @dir: Directory to remove + * + * This will remove the existing directory from this host and notify the + * connected hosts about the change. + */ +void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir) +{ + int ret = 0; + + mutex_lock(&xdomain_lock); + if (remove_directory(key, dir)) + ret = rebuild_property_block(); + mutex_unlock(&xdomain_lock); + + if (!ret) + update_all_xdomains(); +} +EXPORT_SYMBOL_GPL(tb_unregister_property_dir); + +int tb_xdomain_init(void) +{ + int ret; + + xdomain_property_dir = tb_property_create_dir(NULL); + if (!xdomain_property_dir) + return -ENOMEM; + + /* + * Initialize standard set of properties without any service + * directories. Those will be added by service drivers + * themselves when they are loaded. + */ + tb_property_add_immediate(xdomain_property_dir, "vendorid", + PCI_VENDOR_ID_INTEL); + tb_property_add_text(xdomain_property_dir, "vendorid", "Intel Corp."); + tb_property_add_immediate(xdomain_property_dir, "deviceid", 0x1); + tb_property_add_text(xdomain_property_dir, "deviceid", + utsname()->nodename); + tb_property_add_immediate(xdomain_property_dir, "devicerv", 0x80000100); + + ret = rebuild_property_block(); + if (ret) { + tb_property_free_dir(xdomain_property_dir); + xdomain_property_dir = NULL; + } + + return ret; +} + +void tb_xdomain_exit(void) +{ + kfree(xdomain_property_block); + tb_property_free_dir(xdomain_property_dir); +} diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 694cebb50f72..7625c3b81f84 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -683,5 +683,31 @@ struct fsl_mc_device_id { const char obj_type[16]; }; +/** + * struct tb_service_id - Thunderbolt service identifiers + * @match_flags: Flags used to match the structure + * @protocol_key: Protocol key the service supports + * @protocol_id: Protocol id the service supports + * @protocol_version: Version of the protocol + * @protocol_revision: Revision of the protocol software + * @driver_data: Driver specific data + * + * Thunderbolt XDomain services are exposed as devices where each device + * carries the protocol information the service supports. Thunderbolt + * XDomain service drivers match against that information. + */ +struct tb_service_id { + __u32 match_flags; + char protocol_key[8 + 1]; + __u32 protocol_id; + __u32 protocol_version; + __u32 protocol_revision; + kernel_ulong_t driver_data; +}; + +#define TBSVC_MATCH_PROTOCOL_KEY 0x0001 +#define TBSVC_MATCH_PROTOCOL_ID 0x0002 +#define TBSVC_MATCH_PROTOCOL_VERSION 0x0004 +#define TBSVC_MATCH_PROTOCOL_REVISION 0x0008 #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 43b8d1e09341..18c0e3d5e85c 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -17,6 +17,7 @@ #include #include #include +#include #include enum tb_cfg_pkg_type { @@ -77,6 +78,8 @@ struct tb { }; extern struct bus_type tb_bus_type; +extern struct device_type tb_service_type; +extern struct device_type tb_xdomain_type; #define TB_LINKS_PER_PHY_PORT 2 @@ -155,4 +158,243 @@ struct tb_property *tb_property_get_next(struct tb_property_dir *dir, property; \ property = tb_property_get_next(dir, property)) +int tb_register_property_dir(const char *key, struct tb_property_dir *dir); +void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir); + +/** + * struct tb_xdomain - Cross-domain (XDomain) connection + * @dev: XDomain device + * @tb: Pointer to the domain + * @remote_uuid: UUID of the remote domain (host) + * @local_uuid: Cached local UUID + * @route: Route string the other domain can be reached + * @vendor: Vendor ID of the remote domain + * @device: Device ID of the demote domain + * @lock: Lock to serialize access to the following fields of this structure + * @vendor_name: Name of the vendor (or %NULL if not known) + * @device_name: Name of the device (or %NULL if not known) + * @is_unplugged: The XDomain is unplugged + * @resume: The XDomain is being resumed + * @transmit_path: HopID which the remote end expects us to transmit + * @transmit_ring: Local ring (hop) where outgoing packets are pushed + * @receive_path: HopID which we expect the remote end to transmit + * @receive_ring: Local ring (hop) where incoming packets arrive + * @service_ids: Used to generate IDs for the services + * @properties: Properties exported by the remote domain + * @property_block_gen: Generation of @properties + * @properties_lock: Lock protecting @properties. + * @get_properties_work: Work used to get remote domain properties + * @properties_retries: Number of times left to read properties + * @properties_changed_work: Work used to notify the remote domain that + * our properties have changed + * @properties_changed_retries: Number of times left to send properties + * changed notification + * @link: Root switch link the remote domain is connected (ICM only) + * @depth: Depth in the chain the remote domain is connected (ICM only) + * + * This structure represents connection across two domains (hosts). + * Each XDomain contains zero or more services which are exposed as + * &struct tb_service objects. + * + * Service drivers may access this structure if they need to enumerate + * non-standard properties but they need hold @lock when doing so + * because properties can be changed asynchronously in response to + * changes in the remote domain. + */ +struct tb_xdomain { + struct device dev; + struct tb *tb; + uuid_t *remote_uuid; + const uuid_t *local_uuid; + u64 route; + u16 vendor; + u16 device; + struct mutex lock; + const char *vendor_name; + const char *device_name; + bool is_unplugged; + bool resume; + u16 transmit_path; + u16 transmit_ring; + u16 receive_path; + u16 receive_ring; + struct ida service_ids; + struct tb_property_dir *properties; + u32 property_block_gen; + struct delayed_work get_properties_work; + int properties_retries; + struct delayed_work properties_changed_work; + int properties_changed_retries; + u8 link; + u8 depth; +}; + +int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 transmit_path, + u16 transmit_ring, u16 receive_path, + u16 receive_ring); +int tb_xdomain_disable_paths(struct tb_xdomain *xd); +struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid); + +static inline struct tb_xdomain * +tb_xdomain_find_by_uuid_locked(struct tb *tb, const uuid_t *uuid) +{ + struct tb_xdomain *xd; + + mutex_lock(&tb->lock); + xd = tb_xdomain_find_by_uuid(tb, uuid); + mutex_unlock(&tb->lock); + + return xd; +} + +static inline struct tb_xdomain *tb_xdomain_get(struct tb_xdomain *xd) +{ + if (xd) + get_device(&xd->dev); + return xd; +} + +static inline void tb_xdomain_put(struct tb_xdomain *xd) +{ + if (xd) + put_device(&xd->dev); +} + +static inline bool tb_is_xdomain(const struct device *dev) +{ + return dev->type == &tb_xdomain_type; +} + +static inline struct tb_xdomain *tb_to_xdomain(struct device *dev) +{ + if (tb_is_xdomain(dev)) + return container_of(dev, struct tb_xdomain, dev); + return NULL; +} + +int tb_xdomain_response(struct tb_xdomain *xd, const void *response, + size_t size, enum tb_cfg_pkg_type type); +int tb_xdomain_request(struct tb_xdomain *xd, const void *request, + size_t request_size, enum tb_cfg_pkg_type request_type, + void *response, size_t response_size, + enum tb_cfg_pkg_type response_type, + unsigned int timeout_msec); + +/** + * tb_protocol_handler - Protocol specific handler + * @uuid: XDomain messages with this UUID are dispatched to this handler + * @callback: Callback called with the XDomain message. Returning %1 + * here tells the XDomain core that the message was handled + * by this handler and should not be forwared to other + * handlers. + * @data: Data passed with the callback + * @list: Handlers are linked using this + * + * Thunderbolt services can hook into incoming XDomain requests by + * registering protocol handler. Only limitation is that the XDomain + * discovery protocol UUID cannot be registered since it is handled by + * the core XDomain code. + * + * The @callback must check that the message is really directed to the + * service the driver implements. + */ +struct tb_protocol_handler { + const uuid_t *uuid; + int (*callback)(const void *buf, size_t size, void *data); + void *data; + struct list_head list; +}; + +int tb_register_protocol_handler(struct tb_protocol_handler *handler); +void tb_unregister_protocol_handler(struct tb_protocol_handler *handler); + +/** + * struct tb_service - Thunderbolt service + * @dev: XDomain device + * @id: ID of the service (shown in sysfs) + * @key: Protocol key from the properties directory + * @prtcid: Protocol ID from the properties directory + * @prtcvers: Protocol version from the properties directory + * @prtcrevs: Protocol software revision from the properties directory + * @prtcstns: Protocol settings mask from the properties directory + * + * Each domain exposes set of services it supports as collection of + * properties. For each service there will be one corresponding + * &struct tb_service. Service drivers are bound to these. + */ +struct tb_service { + struct device dev; + int id; + const char *key; + u32 prtcid; + u32 prtcvers; + u32 prtcrevs; + u32 prtcstns; +}; + +static inline struct tb_service *tb_service_get(struct tb_service *svc) +{ + if (svc) + get_device(&svc->dev); + return svc; +} + +static inline void tb_service_put(struct tb_service *svc) +{ + if (svc) + put_device(&svc->dev); +} + +static inline bool tb_is_service(const struct device *dev) +{ + return dev->type == &tb_service_type; +} + +static inline struct tb_service *tb_to_service(struct device *dev) +{ + if (tb_is_service(dev)) + return container_of(dev, struct tb_service, dev); + return NULL; +} + +/** + * tb_service_driver - Thunderbolt service driver + * @driver: Driver structure + * @probe: Called when the driver is probed + * @remove: Called when the driver is removed (optional) + * @shutdown: Called at shutdown time to stop the service (optional) + * @id_table: Table of service identifiers the driver supports + */ +struct tb_service_driver { + struct device_driver driver; + int (*probe)(struct tb_service *svc, const struct tb_service_id *id); + void (*remove)(struct tb_service *svc); + void (*shutdown)(struct tb_service *svc); + const struct tb_service_id *id_table; +}; + +#define TB_SERVICE(key, id) \ + .match_flags = TBSVC_MATCH_PROTOCOL_KEY | \ + TBSVC_MATCH_PROTOCOL_ID, \ + .protocol_key = (key), \ + .protocol_id = (id) + +int tb_register_service_driver(struct tb_service_driver *drv); +void tb_unregister_service_driver(struct tb_service_driver *drv); + +static inline void *tb_service_get_drvdata(const struct tb_service *svc) +{ + return dev_get_drvdata(&svc->dev); +} + +static inline void tb_service_set_drvdata(struct tb_service *svc, void *data) +{ + dev_set_drvdata(&svc->dev, data); +} + +static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc) +{ + return tb_to_xdomain(svc->dev.parent); +} + #endif /* THUNDERBOLT_H_ */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index e4d90e50f6fe..57263f2f8f2f 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -206,5 +206,12 @@ int main(void) DEVID_FIELD(fsl_mc_device_id, vendor); DEVID_FIELD(fsl_mc_device_id, obj_type); + DEVID(tb_service_id); + DEVID_FIELD(tb_service_id, match_flags); + DEVID_FIELD(tb_service_id, protocol_key); + DEVID_FIELD(tb_service_id, protocol_id); + DEVID_FIELD(tb_service_id, protocol_version); + DEVID_FIELD(tb_service_id, protocol_revision); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 29d6699d5a06..6ef6e63f96fd 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1301,6 +1301,31 @@ static int do_fsl_mc_entry(const char *filename, void *symval, } ADD_TO_DEVTABLE("fslmc", fsl_mc_device_id, do_fsl_mc_entry); +/* Looks like: tbsvc:kSpNvNrN */ +static int do_tbsvc_entry(const char *filename, void *symval, char *alias) +{ + DEF_FIELD(symval, tb_service_id, match_flags); + DEF_FIELD_ADDR(symval, tb_service_id, protocol_key); + DEF_FIELD(symval, tb_service_id, protocol_id); + DEF_FIELD(symval, tb_service_id, protocol_version); + DEF_FIELD(symval, tb_service_id, protocol_revision); + + strcpy(alias, "tbsvc:"); + if (match_flags & TBSVC_MATCH_PROTOCOL_KEY) + sprintf(alias + strlen(alias), "k%s", *protocol_key); + else + strcat(alias + strlen(alias), "k*"); + ADD(alias, "p", match_flags & TBSVC_MATCH_PROTOCOL_ID, protocol_id); + ADD(alias, "v", match_flags & TBSVC_MATCH_PROTOCOL_VERSION, + protocol_version); + ADD(alias, "r", match_flags & TBSVC_MATCH_PROTOCOL_REVISION, + protocol_revision); + + add_wildcard(alias); + return 1; +} +ADD_TO_DEVTABLE("tbsvc", tb_service_id, do_tbsvc_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { -- cgit v1.2.3-59-g8ed1b From 3b3d9f4da96493e4f68d0a80ab210763a24f8b33 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:37 +0300 Subject: thunderbolt: Export ring handling functions to modules These are used by Thunderbolt services to send and receive frames over the high-speed DMA rings. We also put the functions to tb_ namespace to make sure we do not collide with others and add missing kernel-doc comments for the exported functions. Signed-off-by: Mika Westerberg Reviewed-by: Michael Jamet Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/thunderbolt/ctl.c | 20 +++--- drivers/thunderbolt/nhi.c | 62 +++++++++++------ drivers/thunderbolt/nhi.h | 147 +---------------------------------------- include/linux/thunderbolt.h | 158 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+), 176 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 05400b77dcd7..dd10789e1dbb 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -359,7 +359,7 @@ static int tb_ctl_tx(struct tb_ctl *ctl, const void *data, size_t len, cpu_to_be32_array(pkg->buffer, data, len / 4); *(__be32 *) (pkg->buffer + len) = tb_crc(pkg->buffer, len); - res = ring_tx(ctl->tx, &pkg->frame); + res = tb_ring_tx(ctl->tx, &pkg->frame); if (res) /* ring is stopped */ tb_ctl_pkg_free(pkg); return res; @@ -376,7 +376,7 @@ static bool tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type, static void tb_ctl_rx_submit(struct ctl_pkg *pkg) { - ring_rx(pkg->ctl->rx, &pkg->frame); /* + tb_ring_rx(pkg->ctl->rx, &pkg->frame); /* * We ignore failures during stop. * All rx packets are referenced * from ctl->rx_packets, so we do @@ -614,11 +614,11 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) if (!ctl->frame_pool) goto err; - ctl->tx = ring_alloc_tx(nhi, 0, 10, RING_FLAG_NO_SUSPEND); + ctl->tx = tb_ring_alloc_tx(nhi, 0, 10, RING_FLAG_NO_SUSPEND); if (!ctl->tx) goto err; - ctl->rx = ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff, + ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff, 0xffff); if (!ctl->rx) goto err; @@ -652,9 +652,9 @@ void tb_ctl_free(struct tb_ctl *ctl) return; if (ctl->rx) - ring_free(ctl->rx); + tb_ring_free(ctl->rx); if (ctl->tx) - ring_free(ctl->tx); + tb_ring_free(ctl->tx); /* free RX packets */ for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++) @@ -673,8 +673,8 @@ void tb_ctl_start(struct tb_ctl *ctl) { int i; tb_ctl_info(ctl, "control channel starting...\n"); - ring_start(ctl->tx); /* is used to ack hotplug packets, start first */ - ring_start(ctl->rx); + tb_ring_start(ctl->tx); /* is used to ack hotplug packets, start first */ + tb_ring_start(ctl->rx); for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++) tb_ctl_rx_submit(ctl->rx_packets[i]); @@ -695,8 +695,8 @@ void tb_ctl_stop(struct tb_ctl *ctl) ctl->running = false; mutex_unlock(&ctl->request_queue_lock); - ring_stop(ctl->rx); - ring_stop(ctl->tx); + tb_ring_stop(ctl->rx); + tb_ring_stop(ctl->tx); if (!list_empty(&ctl->request_queue)) tb_ctl_WARN(ctl, "dangling request in request_queue\n"); diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index bebcad3d2c1f..e0a47f7581cb 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -253,7 +253,7 @@ invoke_callback: } } -int __ring_enqueue(struct tb_ring *ring, struct ring_frame *frame) +int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame) { int ret = 0; mutex_lock(&ring->lock); @@ -266,6 +266,7 @@ int __ring_enqueue(struct tb_ring *ring, struct ring_frame *frame) mutex_unlock(&ring->lock); return ret; } +EXPORT_SYMBOL_GPL(__tb_ring_enqueue); static irqreturn_t ring_msix(int irq, void *data) { @@ -309,9 +310,9 @@ static void ring_release_msix(struct tb_ring *ring) ring->irq = 0; } -static struct tb_ring *ring_alloc(struct tb_nhi *nhi, u32 hop, int size, - bool transmit, unsigned int flags, - u16 sof_mask, u16 eof_mask) +static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, + bool transmit, unsigned int flags, + u16 sof_mask, u16 eof_mask) { struct tb_ring *ring = NULL; dev_info(&nhi->pdev->dev, "allocating %s ring %d of size %d\n", @@ -377,24 +378,42 @@ err: return NULL; } -struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags) +/** + * tb_ring_alloc_tx() - Allocate DMA ring for transmit + * @nhi: Pointer to the NHI the ring is to be allocated + * @hop: HopID (ring) to allocate + * @size: Number of entries in the ring + * @flags: Flags for the ring + */ +struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, + unsigned int flags) { - return ring_alloc(nhi, hop, size, true, flags, 0, 0); + return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0); } +EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); -struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags, u16 sof_mask, u16 eof_mask) +/** + * tb_ring_alloc_rx() - Allocate DMA ring for receive + * @nhi: Pointer to the NHI the ring is to be allocated + * @hop: HopID (ring) to allocate + * @size: Number of entries in the ring + * @flags: Flags for the ring + * @sof_mask: Mask of PDF values that start a frame + * @eof_mask: Mask of PDF values that end a frame + */ +struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, + unsigned int flags, u16 sof_mask, u16 eof_mask) { - return ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask); + return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask); } +EXPORT_SYMBOL_GPL(tb_ring_alloc_rx); /** - * ring_start() - enable a ring + * tb_ring_start() - enable a ring * - * Must not be invoked in parallel with ring_stop(). + * Must not be invoked in parallel with tb_ring_stop(). */ -void ring_start(struct tb_ring *ring) +void tb_ring_start(struct tb_ring *ring) { u16 frame_size; u32 flags; @@ -450,21 +469,22 @@ err: mutex_unlock(&ring->lock); mutex_unlock(&ring->nhi->lock); } - +EXPORT_SYMBOL_GPL(tb_ring_start); /** - * ring_stop() - shutdown a ring + * tb_ring_stop() - shutdown a ring * * Must not be invoked from a callback. * - * This method will disable the ring. Further calls to ring_tx/ring_rx will - * return -ESHUTDOWN until ring_stop has been called. + * This method will disable the ring. Further calls to + * tb_ring_tx/tb_ring_rx will return -ESHUTDOWN until ring_stop has been + * called. * * All enqueued frames will be canceled and their callbacks will be executed * with frame->canceled set to true (on the callback thread). This method * returns only after all callback invocations have finished. */ -void ring_stop(struct tb_ring *ring) +void tb_ring_stop(struct tb_ring *ring) { mutex_lock(&ring->nhi->lock); mutex_lock(&ring->lock); @@ -497,9 +517,10 @@ err: schedule_work(&ring->work); flush_work(&ring->work); } +EXPORT_SYMBOL_GPL(tb_ring_stop); /* - * ring_free() - free ring + * tb_ring_free() - free ring * * When this method returns all invocations of ring->callback will have * finished. @@ -508,7 +529,7 @@ err: * * Must NOT be called from ring_frame->callback! */ -void ring_free(struct tb_ring *ring) +void tb_ring_free(struct tb_ring *ring) { mutex_lock(&ring->nhi->lock); /* @@ -550,6 +571,7 @@ void ring_free(struct tb_ring *ring) mutex_destroy(&ring->lock); kfree(ring); } +EXPORT_SYMBOL_GPL(tb_ring_free); /** * nhi_mailbox_cmd() - Send a command through NHI mailbox diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 4503ddbeccb3..771d09ca5dc5 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -7,152 +7,7 @@ #ifndef DSL3510_H_ #define DSL3510_H_ -#include -#include -#include - -/** - * struct tb_nhi - thunderbolt native host interface - * @lock: Must be held during ring creation/destruction. Is acquired by - * interrupt_work when dispatching interrupts to individual rings. - * @pdev: Pointer to the PCI device - * @iobase: MMIO space of the NHI - * @tx_rings: All Tx rings available on this host controller - * @rx_rings: All Rx rings available on this host controller - * @msix_ida: Used to allocate MSI-X vectors for rings - * @going_away: The host controller device is about to disappear so when - * this flag is set, avoid touching the hardware anymore. - * @interrupt_work: Work scheduled to handle ring interrupt when no - * MSI-X is used. - * @hop_count: Number of rings (end point hops) supported by NHI. - */ -struct tb_nhi { - struct mutex lock; - struct pci_dev *pdev; - void __iomem *iobase; - struct tb_ring **tx_rings; - struct tb_ring **rx_rings; - struct ida msix_ida; - bool going_away; - struct work_struct interrupt_work; - u32 hop_count; -}; - -/** - * struct tb_ring - thunderbolt TX or RX ring associated with a NHI - * @lock: Lock serializing actions to this ring. Must be acquired after - * nhi->lock. - * @nhi: Pointer to the native host controller interface - * @size: Size of the ring - * @hop: Hop (DMA channel) associated with this ring - * @head: Head of the ring (write next descriptor here) - * @tail: Tail of the ring (complete next descriptor here) - * @descriptors: Allocated descriptors for this ring - * @queue: Queue holding frames to be transferred over this ring - * @in_flight: Queue holding frames that are currently in flight - * @work: Interrupt work structure - * @is_tx: Is the ring Tx or Rx - * @running: Is the ring running - * @irq: MSI-X irq number if the ring uses MSI-X. %0 otherwise. - * @vector: MSI-X vector number the ring uses (only set if @irq is > 0) - * @flags: Ring specific flags - * @sof_mask: Bit mask used to detect start of frame PDF - * @eof_mask: Bit mask used to detect end of frame PDF - */ -struct tb_ring { - struct mutex lock; - struct tb_nhi *nhi; - int size; - int hop; - int head; - int tail; - struct ring_desc *descriptors; - dma_addr_t descriptors_dma; - struct list_head queue; - struct list_head in_flight; - struct work_struct work; - bool is_tx:1; - bool running:1; - int irq; - u8 vector; - unsigned int flags; - u16 sof_mask; - u16 eof_mask; -}; - -/* Leave ring interrupt enabled on suspend */ -#define RING_FLAG_NO_SUSPEND BIT(0) -/* Configure the ring to be in frame mode */ -#define RING_FLAG_FRAME BIT(1) -/* Enable end-to-end flow control */ -#define RING_FLAG_E2E BIT(2) - -struct ring_frame; -typedef void (*ring_cb)(struct tb_ring*, struct ring_frame*, bool canceled); - -/** - * struct ring_frame - for use with ring_rx/ring_tx - */ -struct ring_frame { - dma_addr_t buffer_phy; - ring_cb callback; - struct list_head list; - u32 size:12; /* TX: in, RX: out*/ - u32 flags:12; /* RX: out */ - u32 eof:4; /* TX:in, RX: out */ - u32 sof:4; /* TX:in, RX: out */ -}; - -#define TB_FRAME_SIZE 0x100 /* minimum size for ring_rx */ - -struct tb_ring *ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags); -struct tb_ring *ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags, u16 sof_mask, u16 eof_mask); -void ring_start(struct tb_ring *ring); -void ring_stop(struct tb_ring *ring); -void ring_free(struct tb_ring *ring); - -int __ring_enqueue(struct tb_ring *ring, struct ring_frame *frame); - -/** - * ring_rx() - enqueue a frame on an RX ring - * - * frame->buffer, frame->buffer_phy and frame->callback have to be set. The - * buffer must contain at least TB_FRAME_SIZE bytes. - * - * frame->callback will be invoked with frame->size, frame->flags, frame->eof, - * frame->sof set once the frame has been received. - * - * If ring_stop is called after the packet has been enqueued frame->callback - * will be called with canceled set to true. - * - * Return: Returns ESHUTDOWN if ring_stop has been called. Zero otherwise. - */ -static inline int ring_rx(struct tb_ring *ring, struct ring_frame *frame) -{ - WARN_ON(ring->is_tx); - return __ring_enqueue(ring, frame); -} - -/** - * ring_tx() - enqueue a frame on an TX ring - * - * frame->buffer, frame->buffer_phy, frame->callback, frame->size, frame->eof - * and frame->sof have to be set. - * - * frame->callback will be invoked with once the frame has been transmitted. - * - * If ring_stop is called after the packet has been enqueued frame->callback - * will be called with canceled set to true. - * - * Return: Returns ESHUTDOWN if ring_stop has been called. Zero otherwise. - */ -static inline int ring_tx(struct tb_ring *ring, struct ring_frame *frame) -{ - WARN_ON(!ring->is_tx); - return __ring_enqueue(ring, frame); -} +#include enum nhi_fw_mode { NHI_FW_SAFE_MODE, diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 18c0e3d5e85c..9ddb83ad890f 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -15,10 +15,12 @@ #define THUNDERBOLT_H_ #include +#include #include #include #include #include +#include enum tb_cfg_pkg_type { TB_CFG_PKG_READ = 1, @@ -397,4 +399,160 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc) return tb_to_xdomain(svc->dev.parent); } +/** + * struct tb_nhi - thunderbolt native host interface + * @lock: Must be held during ring creation/destruction. Is acquired by + * interrupt_work when dispatching interrupts to individual rings. + * @pdev: Pointer to the PCI device + * @iobase: MMIO space of the NHI + * @tx_rings: All Tx rings available on this host controller + * @rx_rings: All Rx rings available on this host controller + * @msix_ida: Used to allocate MSI-X vectors for rings + * @going_away: The host controller device is about to disappear so when + * this flag is set, avoid touching the hardware anymore. + * @interrupt_work: Work scheduled to handle ring interrupt when no + * MSI-X is used. + * @hop_count: Number of rings (end point hops) supported by NHI. + */ +struct tb_nhi { + struct mutex lock; + struct pci_dev *pdev; + void __iomem *iobase; + struct tb_ring **tx_rings; + struct tb_ring **rx_rings; + struct ida msix_ida; + bool going_away; + struct work_struct interrupt_work; + u32 hop_count; +}; + +/** + * struct tb_ring - thunderbolt TX or RX ring associated with a NHI + * @lock: Lock serializing actions to this ring. Must be acquired after + * nhi->lock. + * @nhi: Pointer to the native host controller interface + * @size: Size of the ring + * @hop: Hop (DMA channel) associated with this ring + * @head: Head of the ring (write next descriptor here) + * @tail: Tail of the ring (complete next descriptor here) + * @descriptors: Allocated descriptors for this ring + * @queue: Queue holding frames to be transferred over this ring + * @in_flight: Queue holding frames that are currently in flight + * @work: Interrupt work structure + * @is_tx: Is the ring Tx or Rx + * @running: Is the ring running + * @irq: MSI-X irq number if the ring uses MSI-X. %0 otherwise. + * @vector: MSI-X vector number the ring uses (only set if @irq is > 0) + * @flags: Ring specific flags + * @sof_mask: Bit mask used to detect start of frame PDF + * @eof_mask: Bit mask used to detect end of frame PDF + */ +struct tb_ring { + struct mutex lock; + struct tb_nhi *nhi; + int size; + int hop; + int head; + int tail; + struct ring_desc *descriptors; + dma_addr_t descriptors_dma; + struct list_head queue; + struct list_head in_flight; + struct work_struct work; + bool is_tx:1; + bool running:1; + int irq; + u8 vector; + unsigned int flags; + u16 sof_mask; + u16 eof_mask; +}; + +/* Leave ring interrupt enabled on suspend */ +#define RING_FLAG_NO_SUSPEND BIT(0) +/* Configure the ring to be in frame mode */ +#define RING_FLAG_FRAME BIT(1) +/* Enable end-to-end flow control */ +#define RING_FLAG_E2E BIT(2) + +struct ring_frame; +typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled); + +/** + * struct ring_frame - For use with ring_rx/ring_tx + * @buffer_phy: DMA mapped address of the frame + * @callback: Callback called when the frame is finished + * @list: Frame is linked to a queue using this + * @size: Size of the frame in bytes (%0 means %4096) + * @flags: Flags for the frame (see &enum ring_desc_flags) + * @eof: End of frame protocol defined field + * @sof: Start of frame protocol defined field + */ +struct ring_frame { + dma_addr_t buffer_phy; + ring_cb callback; + struct list_head list; + u32 size:12; + u32 flags:12; + u32 eof:4; + u32 sof:4; +}; + +/* Minimum size for ring_rx */ +#define TB_FRAME_SIZE 0x100 + +struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, + unsigned int flags); +struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, + unsigned int flags, u16 sof_mask, + u16 eof_mask); +void tb_ring_start(struct tb_ring *ring); +void tb_ring_stop(struct tb_ring *ring); +void tb_ring_free(struct tb_ring *ring); + +int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame); + +/** + * tb_ring_rx() - enqueue a frame on an RX ring + * @ring: Ring to enqueue the frame + * @frame: Frame to enqueue + * + * @frame->buffer, @frame->buffer_phy and @frame->callback have to be set. The + * buffer must contain at least %TB_FRAME_SIZE bytes. + * + * @frame->callback will be invoked with @frame->size, @frame->flags, + * @frame->eof, @frame->sof set once the frame has been received. + * + * If ring_stop() is called after the packet has been enqueued + * @frame->callback will be called with canceled set to true. + * + * Return: Returns %-ESHUTDOWN if ring_stop has been called. Zero otherwise. + */ +static inline int tb_ring_rx(struct tb_ring *ring, struct ring_frame *frame) +{ + WARN_ON(ring->is_tx); + return __tb_ring_enqueue(ring, frame); +} + +/** + * tb_ring_tx() - enqueue a frame on an TX ring + * @ring: Ring the enqueue the frame + * @frame: Frame to enqueue + * + * @frame->buffer, @frame->buffer_phy, @frame->callback, @frame->size, + * @frame->eof and @frame->sof have to be set. + * + * @frame->callback will be invoked with once the frame has been transmitted. + * + * If ring_stop() is called after the packet has been enqueued @frame->callback + * will be called with canceled set to true. + * + * Return: Returns %-ESHUTDOWN if ring_stop has been called. Zero otherwise. + */ +static inline int tb_ring_tx(struct tb_ring *ring, struct ring_frame *frame) +{ + WARN_ON(!ring->is_tx); + return __tb_ring_enqueue(ring, frame); +} + #endif /* THUNDERBOLT_H_ */ -- cgit v1.2.3-59-g8ed1b From 2a91ec63f8a11e70d4b958dd4df867fec0247179 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:38 +0300 Subject: thunderbolt: Move ring descriptor flags to thunderbolt.h A Thunderbolt service driver might need to check if there was an error with the descriptor when in frame mode. We also add two Rx specific error flags RING_DESC_CRC_ERROR and RING_DESC_BUFFER_OVERRUN. Signed-off-by: Mika Westerberg Reviewed-by: Michael Jamet Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/thunderbolt/nhi_regs.h | 7 ------- include/linux/thunderbolt.h | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h index 491a4c0c18fc..5ed6934e31e7 100644 --- a/drivers/thunderbolt/nhi_regs.h +++ b/drivers/thunderbolt/nhi_regs.h @@ -17,13 +17,6 @@ enum ring_flags { RING_FLAG_ENABLE = 1 << 31, }; -enum ring_desc_flags { - RING_DESC_ISOCH = 0x1, /* TX only? */ - RING_DESC_COMPLETED = 0x2, /* set by NHI */ - RING_DESC_POSTED = 0x4, /* always set this */ - RING_DESC_INTERRUPT = 0x8, /* request an interrupt on completion */ -}; - /** * struct ring_desc - TX/RX ring entry * diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 9ddb83ad890f..e3b9af7be0ad 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -478,6 +478,24 @@ struct tb_ring { struct ring_frame; typedef void (*ring_cb)(struct tb_ring *, struct ring_frame *, bool canceled); +/** + * enum ring_desc_flags - Flags for DMA ring descriptor + * %RING_DESC_ISOCH: Enable isonchronous DMA (Tx only) + * %RING_DESC_CRC_ERROR: In frame mode CRC check failed for the frame (Rx only) + * %RING_DESC_COMPLETED: Descriptor completed (set by NHI) + * %RING_DESC_POSTED: Always set this + * %RING_DESC_BUFFER_OVERRUN: RX buffer overrun + * %RING_DESC_INTERRUPT: Request an interrupt on completion + */ +enum ring_desc_flags { + RING_DESC_ISOCH = 0x1, + RING_DESC_CRC_ERROR = 0x1, + RING_DESC_COMPLETED = 0x2, + RING_DESC_POSTED = 0x4, + RING_DESC_BUFFER_OVERRUN = 0x04, + RING_DESC_INTERRUPT = 0x8, +}; + /** * struct ring_frame - For use with ring_rx/ring_tx * @buffer_phy: DMA mapped address of the frame -- cgit v1.2.3-59-g8ed1b From 22b7de1000e66d739c431d6be4e7e97c69fa7c98 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:39 +0300 Subject: thunderbolt: Use spinlock in ring serialization This makes it possible to enqueue frames also from atomic context which is needed for example, when networking packets are sent over a Thunderbolt cable. Signed-off-by: Mika Westerberg Reviewed-by: Michael Jamet Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/thunderbolt/nhi.c | 26 ++++++++++++++------------ include/linux/thunderbolt.h | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index e0a47f7581cb..7d1891ec3c47 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -212,8 +212,10 @@ static void ring_work(struct work_struct *work) struct tb_ring *ring = container_of(work, typeof(*ring), work); struct ring_frame *frame; bool canceled = false; + unsigned long flags; LIST_HEAD(done); - mutex_lock(&ring->lock); + + spin_lock_irqsave(&ring->lock, flags); if (!ring->running) { /* Move all frames to done and mark them as canceled. */ @@ -241,7 +243,8 @@ static void ring_work(struct work_struct *work) ring_write_descriptors(ring); invoke_callback: - mutex_unlock(&ring->lock); /* allow callbacks to schedule new work */ + /* allow callbacks to schedule new work */ + spin_unlock_irqrestore(&ring->lock, flags); while (!list_empty(&done)) { frame = list_first_entry(&done, typeof(*frame), list); /* @@ -255,15 +258,17 @@ invoke_callback: int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame) { + unsigned long flags; int ret = 0; - mutex_lock(&ring->lock); + + spin_lock_irqsave(&ring->lock, flags); if (ring->running) { list_add_tail(&frame->list, &ring->queue); ring_write_descriptors(ring); } else { ret = -ESHUTDOWN; } - mutex_unlock(&ring->lock); + spin_unlock_irqrestore(&ring->lock, flags); return ret; } EXPORT_SYMBOL_GPL(__tb_ring_enqueue); @@ -338,7 +343,7 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, if (!ring) goto err; - mutex_init(&ring->lock); + spin_lock_init(&ring->lock); INIT_LIST_HEAD(&ring->queue); INIT_LIST_HEAD(&ring->in_flight); INIT_WORK(&ring->work, ring_work); @@ -371,8 +376,6 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, return ring; err: - if (ring) - mutex_destroy(&ring->lock); kfree(ring); mutex_unlock(&nhi->lock); return NULL; @@ -419,7 +422,7 @@ void tb_ring_start(struct tb_ring *ring) u32 flags; mutex_lock(&ring->nhi->lock); - mutex_lock(&ring->lock); + spin_lock_irq(&ring->lock); if (ring->nhi->going_away) goto err; if (ring->running) { @@ -466,7 +469,7 @@ void tb_ring_start(struct tb_ring *ring) ring_interrupt_active(ring, true); ring->running = true; err: - mutex_unlock(&ring->lock); + spin_unlock_irq(&ring->lock); mutex_unlock(&ring->nhi->lock); } EXPORT_SYMBOL_GPL(tb_ring_start); @@ -487,7 +490,7 @@ EXPORT_SYMBOL_GPL(tb_ring_start); void tb_ring_stop(struct tb_ring *ring) { mutex_lock(&ring->nhi->lock); - mutex_lock(&ring->lock); + spin_lock_irq(&ring->lock); dev_info(&ring->nhi->pdev->dev, "stopping %s %d\n", RING_TYPE(ring), ring->hop); if (ring->nhi->going_away) @@ -508,7 +511,7 @@ void tb_ring_stop(struct tb_ring *ring) ring->running = false; err: - mutex_unlock(&ring->lock); + spin_unlock_irq(&ring->lock); mutex_unlock(&ring->nhi->lock); /* @@ -568,7 +571,6 @@ void tb_ring_free(struct tb_ring *ring) * to finish before freeing the ring. */ flush_work(&ring->work); - mutex_destroy(&ring->lock); kfree(ring); } EXPORT_SYMBOL_GPL(tb_ring_free); diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index e3b9af7be0ad..cf9e42db780f 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -448,7 +448,7 @@ struct tb_nhi { * @eof_mask: Bit mask used to detect end of frame PDF */ struct tb_ring { - struct mutex lock; + spinlock_t lock; struct tb_nhi *nhi; int size; int hop; -- cgit v1.2.3-59-g8ed1b From 59120e06101db72442acf4c8b364a0c76d8faa68 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:40 +0300 Subject: thunderbolt: Use spinlock in NHI serialization This is needed because ring polling functionality can be called from atomic contexts when networking and other high-speed traffic is transferred over a Thunderbolt cable. Signed-off-by: Mika Westerberg Reviewed-by: Michael Jamet Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/thunderbolt/nhi.c | 75 +++++++++++++++++++++++++-------------------- include/linux/thunderbolt.h | 2 +- 2 files changed, 42 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 7d1891ec3c47..0b3c0640048b 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -327,21 +327,9 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, if (transmit && hop == RING_E2E_UNUSED_HOPID) return NULL; - mutex_lock(&nhi->lock); - if (hop >= nhi->hop_count) { - dev_WARN(&nhi->pdev->dev, "invalid hop: %d\n", hop); - goto err; - } - if (transmit && nhi->tx_rings[hop]) { - dev_WARN(&nhi->pdev->dev, "TX hop %d already allocated\n", hop); - goto err; - } else if (!transmit && nhi->rx_rings[hop]) { - dev_WARN(&nhi->pdev->dev, "RX hop %d already allocated\n", hop); - goto err; - } ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) - goto err; + return NULL; spin_lock_init(&ring->lock); INIT_LIST_HEAD(&ring->queue); @@ -359,25 +347,45 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, ring->tail = 0; ring->running = false; - if (ring_request_msix(ring, flags & RING_FLAG_NO_SUSPEND)) - goto err; - ring->descriptors = dma_alloc_coherent(&ring->nhi->pdev->dev, size * sizeof(*ring->descriptors), &ring->descriptors_dma, GFP_KERNEL | __GFP_ZERO); if (!ring->descriptors) - goto err; + goto err_free_ring; + if (ring_request_msix(ring, flags & RING_FLAG_NO_SUSPEND)) + goto err_free_descs; + + spin_lock_irq(&nhi->lock); + if (hop >= nhi->hop_count) { + dev_WARN(&nhi->pdev->dev, "invalid hop: %d\n", hop); + goto err_release_msix; + } + if (transmit && nhi->tx_rings[hop]) { + dev_WARN(&nhi->pdev->dev, "TX hop %d already allocated\n", hop); + goto err_release_msix; + } else if (!transmit && nhi->rx_rings[hop]) { + dev_WARN(&nhi->pdev->dev, "RX hop %d already allocated\n", hop); + goto err_release_msix; + } if (transmit) nhi->tx_rings[hop] = ring; else nhi->rx_rings[hop] = ring; - mutex_unlock(&nhi->lock); + spin_unlock_irq(&nhi->lock); + return ring; -err: +err_release_msix: + spin_unlock_irq(&nhi->lock); + ring_release_msix(ring); +err_free_descs: + dma_free_coherent(&ring->nhi->pdev->dev, + ring->size * sizeof(*ring->descriptors), + ring->descriptors, ring->descriptors_dma); +err_free_ring: kfree(ring); - mutex_unlock(&nhi->lock); + return NULL; } @@ -421,8 +429,8 @@ void tb_ring_start(struct tb_ring *ring) u16 frame_size; u32 flags; - mutex_lock(&ring->nhi->lock); - spin_lock_irq(&ring->lock); + spin_lock_irq(&ring->nhi->lock); + spin_lock(&ring->lock); if (ring->nhi->going_away) goto err; if (ring->running) { @@ -469,8 +477,8 @@ void tb_ring_start(struct tb_ring *ring) ring_interrupt_active(ring, true); ring->running = true; err: - spin_unlock_irq(&ring->lock); - mutex_unlock(&ring->nhi->lock); + spin_unlock(&ring->lock); + spin_unlock_irq(&ring->nhi->lock); } EXPORT_SYMBOL_GPL(tb_ring_start); @@ -489,8 +497,8 @@ EXPORT_SYMBOL_GPL(tb_ring_start); */ void tb_ring_stop(struct tb_ring *ring) { - mutex_lock(&ring->nhi->lock); - spin_lock_irq(&ring->lock); + spin_lock_irq(&ring->nhi->lock); + spin_lock(&ring->lock); dev_info(&ring->nhi->pdev->dev, "stopping %s %d\n", RING_TYPE(ring), ring->hop); if (ring->nhi->going_away) @@ -511,8 +519,8 @@ void tb_ring_stop(struct tb_ring *ring) ring->running = false; err: - spin_unlock_irq(&ring->lock); - mutex_unlock(&ring->nhi->lock); + spin_unlock(&ring->lock); + spin_unlock_irq(&ring->nhi->lock); /* * schedule ring->work to invoke callbacks on all remaining frames. @@ -534,7 +542,7 @@ EXPORT_SYMBOL_GPL(tb_ring_stop); */ void tb_ring_free(struct tb_ring *ring) { - mutex_lock(&ring->nhi->lock); + spin_lock_irq(&ring->nhi->lock); /* * Dissociate the ring from the NHI. This also ensures that * nhi_interrupt_work cannot reschedule ring->work. @@ -564,7 +572,7 @@ void tb_ring_free(struct tb_ring *ring) RING_TYPE(ring), ring->hop); - mutex_unlock(&ring->nhi->lock); + spin_unlock_irq(&ring->nhi->lock); /** * ring->work can no longer be scheduled (it is scheduled only * by nhi_interrupt_work, ring_stop and ring_msix). Wait for it @@ -639,7 +647,7 @@ static void nhi_interrupt_work(struct work_struct *work) int type = 0; /* current interrupt type 0: TX, 1: RX, 2: RX overflow */ struct tb_ring *ring; - mutex_lock(&nhi->lock); + spin_lock_irq(&nhi->lock); /* * Starting at REG_RING_NOTIFY_BASE there are three status bitfields @@ -677,7 +685,7 @@ static void nhi_interrupt_work(struct work_struct *work) /* we do not check ring->running, this is done in ring->work */ schedule_work(&ring->work); } - mutex_unlock(&nhi->lock); + spin_unlock_irq(&nhi->lock); } static irqreturn_t nhi_msi(int irq, void *data) @@ -767,7 +775,6 @@ static void nhi_shutdown(struct tb_nhi *nhi) devm_free_irq(&nhi->pdev->dev, nhi->pdev->irq, nhi); flush_work(&nhi->interrupt_work); } - mutex_destroy(&nhi->lock); ida_destroy(&nhi->msix_ida); } @@ -856,7 +863,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) return res; } - mutex_init(&nhi->lock); + spin_lock_init(&nhi->lock); pci_set_master(pdev); diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index cf9e42db780f..d59e3f9a35c4 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -415,7 +415,7 @@ static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc) * @hop_count: Number of rings (end point hops) supported by NHI. */ struct tb_nhi { - struct mutex lock; + spinlock_t lock; struct pci_dev *pdev; void __iomem *iobase; struct tb_ring **tx_rings; -- cgit v1.2.3-59-g8ed1b From 4ffe722eefcb07c76701f03e0d759fbaecedf79f Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:41 +0300 Subject: thunderbolt: Add polling mode for rings In order to support things like networking over Thunderbolt cable, there needs to be a way to switch the ring to a mode where it can be polled with the interrupt masked. We implement such mode so that the caller can allocate a ring by passing pointer to a function that is then called when an interrupt is triggered. Completed frames can be fetched using tb_ring_poll() and the interrupt can be re-enabled when the caller is finished with polling by using tb_ring_poll_complete(). Signed-off-by: Mika Westerberg Reviewed-by: Michael Jamet Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- drivers/thunderbolt/ctl.c | 2 +- drivers/thunderbolt/nhi.c | 126 ++++++++++++++++++++++++++++++++++++++++---- include/linux/thunderbolt.h | 23 +++++--- 3 files changed, 134 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index dd10789e1dbb..d079dbba2c03 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -619,7 +619,7 @@ struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data) goto err; ctl->rx = tb_ring_alloc_rx(nhi, 0, 10, RING_FLAG_NO_SUSPEND, 0xffff, - 0xffff); + 0xffff, NULL, NULL); if (!ctl->rx) goto err; diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 0b3c0640048b..af0a80ddf594 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -252,7 +252,8 @@ invoke_callback: * Do not hold on to it. */ list_del_init(&frame->list); - frame->callback(ring, frame, canceled); + if (frame->callback) + frame->callback(ring, frame, canceled); } } @@ -273,11 +274,106 @@ int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame) } EXPORT_SYMBOL_GPL(__tb_ring_enqueue); +/** + * tb_ring_poll() - Poll one completed frame from the ring + * @ring: Ring to poll + * + * This function can be called when @start_poll callback of the @ring + * has been called. It will read one completed frame from the ring and + * return it to the caller. Returns %NULL if there is no more completed + * frames. + */ +struct ring_frame *tb_ring_poll(struct tb_ring *ring) +{ + struct ring_frame *frame = NULL; + unsigned long flags; + + spin_lock_irqsave(&ring->lock, flags); + if (!ring->running) + goto unlock; + if (ring_empty(ring)) + goto unlock; + + if (ring->descriptors[ring->tail].flags & RING_DESC_COMPLETED) { + frame = list_first_entry(&ring->in_flight, typeof(*frame), + list); + list_del_init(&frame->list); + + if (!ring->is_tx) { + frame->size = ring->descriptors[ring->tail].length; + frame->eof = ring->descriptors[ring->tail].eof; + frame->sof = ring->descriptors[ring->tail].sof; + frame->flags = ring->descriptors[ring->tail].flags; + } + + ring->tail = (ring->tail + 1) % ring->size; + } + +unlock: + spin_unlock_irqrestore(&ring->lock, flags); + return frame; +} +EXPORT_SYMBOL_GPL(tb_ring_poll); + +static void __ring_interrupt_mask(struct tb_ring *ring, bool mask) +{ + int idx = ring_interrupt_index(ring); + int reg = REG_RING_INTERRUPT_BASE + idx / 32 * 4; + int bit = idx % 32; + u32 val; + + val = ioread32(ring->nhi->iobase + reg); + if (mask) + val &= ~BIT(bit); + else + val |= BIT(bit); + iowrite32(val, ring->nhi->iobase + reg); +} + +/* Both @nhi->lock and @ring->lock should be held */ +static void __ring_interrupt(struct tb_ring *ring) +{ + if (!ring->running) + return; + + if (ring->start_poll) { + __ring_interrupt_mask(ring, false); + ring->start_poll(ring->poll_data); + } else { + schedule_work(&ring->work); + } +} + +/** + * tb_ring_poll_complete() - Re-start interrupt for the ring + * @ring: Ring to re-start the interrupt + * + * This will re-start (unmask) the ring interrupt once the user is done + * with polling. + */ +void tb_ring_poll_complete(struct tb_ring *ring) +{ + unsigned long flags; + + spin_lock_irqsave(&ring->nhi->lock, flags); + spin_lock(&ring->lock); + if (ring->start_poll) + __ring_interrupt_mask(ring, false); + spin_unlock(&ring->lock); + spin_unlock_irqrestore(&ring->nhi->lock, flags); +} +EXPORT_SYMBOL_GPL(tb_ring_poll_complete); + static irqreturn_t ring_msix(int irq, void *data) { struct tb_ring *ring = data; - schedule_work(&ring->work); + spin_lock(&ring->nhi->lock); + spin_lock(&ring->lock); + __ring_interrupt(ring); + spin_unlock(&ring->lock); + spin_unlock(&ring->nhi->lock); + return IRQ_HANDLED; } @@ -317,7 +413,9 @@ static void ring_release_msix(struct tb_ring *ring) static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, bool transmit, unsigned int flags, - u16 sof_mask, u16 eof_mask) + u16 sof_mask, u16 eof_mask, + void (*start_poll)(void *), + void *poll_data) { struct tb_ring *ring = NULL; dev_info(&nhi->pdev->dev, "allocating %s ring %d of size %d\n", @@ -346,6 +444,8 @@ static struct tb_ring *tb_ring_alloc(struct tb_nhi *nhi, u32 hop, int size, ring->head = 0; ring->tail = 0; ring->running = false; + ring->start_poll = start_poll; + ring->poll_data = poll_data; ring->descriptors = dma_alloc_coherent(&ring->nhi->pdev->dev, size * sizeof(*ring->descriptors), @@ -399,7 +499,7 @@ err_free_ring: struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, unsigned int flags) { - return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0); + return tb_ring_alloc(nhi, hop, size, true, flags, 0, 0, NULL, NULL); } EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); @@ -411,11 +511,17 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_tx); * @flags: Flags for the ring * @sof_mask: Mask of PDF values that start a frame * @eof_mask: Mask of PDF values that end a frame + * @start_poll: If not %NULL the ring will call this function when an + * interrupt is triggered and masked, instead of callback + * in each Rx frame. + * @poll_data: Optional data passed to @start_poll */ struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags, u16 sof_mask, u16 eof_mask) + unsigned int flags, u16 sof_mask, u16 eof_mask, + void (*start_poll)(void *), void *poll_data) { - return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask); + return tb_ring_alloc(nhi, hop, size, false, flags, sof_mask, eof_mask, + start_poll, poll_data); } EXPORT_SYMBOL_GPL(tb_ring_alloc_rx); @@ -556,6 +662,7 @@ void tb_ring_free(struct tb_ring *ring) dev_WARN(&ring->nhi->pdev->dev, "%s %d still running\n", RING_TYPE(ring), ring->hop); } + spin_unlock_irq(&ring->nhi->lock); ring_release_msix(ring); @@ -572,7 +679,6 @@ void tb_ring_free(struct tb_ring *ring) RING_TYPE(ring), ring->hop); - spin_unlock_irq(&ring->nhi->lock); /** * ring->work can no longer be scheduled (it is scheduled only * by nhi_interrupt_work, ring_stop and ring_msix). Wait for it @@ -682,8 +788,10 @@ static void nhi_interrupt_work(struct work_struct *work) hop); continue; } - /* we do not check ring->running, this is done in ring->work */ - schedule_work(&ring->work); + + spin_lock(&ring->lock); + __ring_interrupt(ring); + spin_unlock(&ring->lock); } spin_unlock_irq(&nhi->lock); } diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index d59e3f9a35c4..36925e3aec7c 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -446,6 +446,9 @@ struct tb_nhi { * @flags: Ring specific flags * @sof_mask: Bit mask used to detect start of frame PDF * @eof_mask: Bit mask used to detect end of frame PDF + * @start_poll: Called when ring interrupt is triggered to start + * polling. Passing %NULL keeps the ring in interrupt mode. + * @poll_data: Data passed to @start_poll */ struct tb_ring { spinlock_t lock; @@ -466,6 +469,8 @@ struct tb_ring { unsigned int flags; u16 sof_mask; u16 eof_mask; + void (*start_poll)(void *data); + void *poll_data; }; /* Leave ring interrupt enabled on suspend */ @@ -499,7 +504,7 @@ enum ring_desc_flags { /** * struct ring_frame - For use with ring_rx/ring_tx * @buffer_phy: DMA mapped address of the frame - * @callback: Callback called when the frame is finished + * @callback: Callback called when the frame is finished (optional) * @list: Frame is linked to a queue using this * @size: Size of the frame in bytes (%0 means %4096) * @flags: Flags for the frame (see &enum ring_desc_flags) @@ -522,8 +527,8 @@ struct ring_frame { struct tb_ring *tb_ring_alloc_tx(struct tb_nhi *nhi, int hop, int size, unsigned int flags); struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size, - unsigned int flags, u16 sof_mask, - u16 eof_mask); + unsigned int flags, u16 sof_mask, u16 eof_mask, + void (*start_poll)(void *), void *poll_data); void tb_ring_start(struct tb_ring *ring); void tb_ring_stop(struct tb_ring *ring); void tb_ring_free(struct tb_ring *ring); @@ -535,8 +540,8 @@ int __tb_ring_enqueue(struct tb_ring *ring, struct ring_frame *frame); * @ring: Ring to enqueue the frame * @frame: Frame to enqueue * - * @frame->buffer, @frame->buffer_phy and @frame->callback have to be set. The - * buffer must contain at least %TB_FRAME_SIZE bytes. + * @frame->buffer, @frame->buffer_phy have to be set. The buffer must + * contain at least %TB_FRAME_SIZE bytes. * * @frame->callback will be invoked with @frame->size, @frame->flags, * @frame->eof, @frame->sof set once the frame has been received. @@ -557,8 +562,8 @@ static inline int tb_ring_rx(struct tb_ring *ring, struct ring_frame *frame) * @ring: Ring the enqueue the frame * @frame: Frame to enqueue * - * @frame->buffer, @frame->buffer_phy, @frame->callback, @frame->size, - * @frame->eof and @frame->sof have to be set. + * @frame->buffer, @frame->buffer_phy, @frame->size, @frame->eof and + * @frame->sof have to be set. * * @frame->callback will be invoked with once the frame has been transmitted. * @@ -573,4 +578,8 @@ static inline int tb_ring_tx(struct tb_ring *ring, struct ring_frame *frame) return __tb_ring_enqueue(ring, frame); } +/* Used only when the ring is in polling mode */ +struct ring_frame *tb_ring_poll(struct tb_ring *ring); +void tb_ring_poll_complete(struct tb_ring *ring); + #endif /* THUNDERBOLT_H_ */ -- cgit v1.2.3-59-g8ed1b From 3304559e353f098d7e0ed5ca981e26c406513e12 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:42 +0300 Subject: thunderbolt: Add function to retrieve DMA device for the ring This is needed when Thunderbolt service drivers need to DMA map memory before it is passed down to the ring. Signed-off-by: Mika Westerberg Reviewed-by: Michael Jamet Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- include/linux/thunderbolt.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 36925e3aec7c..7b69853188b1 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -582,4 +583,16 @@ static inline int tb_ring_tx(struct tb_ring *ring, struct ring_frame *frame) struct ring_frame *tb_ring_poll(struct tb_ring *ring); void tb_ring_poll_complete(struct tb_ring *ring); +/** + * tb_ring_dma_device() - Return device used for DMA mapping + * @ring: Ring whose DMA device is retrieved + * + * Use this function when you are mapping DMA for buffers that are + * passed to the ring for sending/receiving. + */ +static inline struct device *tb_ring_dma_device(struct tb_ring *ring) +{ + return &ring->nhi->pdev->dev; +} + #endif /* THUNDERBOLT_H_ */ -- cgit v1.2.3-59-g8ed1b From 130c28daf01c8aad5224abfb39f59d892a436222 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Fri, 29 Sep 2017 11:44:19 -0500 Subject: ARM: dts: am43xx: Introduce additional pinmux definitions for DS0 AM43xx platform pinmux registers contain bits to set state during suspend (DS0), add these bit definitions here. Signed-off-by: Andrew F. Davis Signed-off-by: Tony Lindgren --- include/dt-bindings/pinctrl/am43xx.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/dt-bindings/pinctrl/am43xx.h b/include/dt-bindings/pinctrl/am43xx.h index 344bd1eb3386..fc31ef7ce219 100644 --- a/include/dt-bindings/pinctrl/am43xx.h +++ b/include/dt-bindings/pinctrl/am43xx.h @@ -21,9 +21,21 @@ #define INPUT_EN (1 << 18) #define SLEWCTRL_SLOW (1 << 19) #define SLEWCTRL_FAST 0 +#define DS0_FORCE_OFF_MODE (1 << 24) +#define DS0_INPUT (1 << 25) +#define DS0_FORCE_OUT_HIGH (1 << 26) #define DS0_PULL_UP_DOWN_EN (1 << 27) +#define DS0_PULL_UP_SEL (1 << 28) #define WAKEUP_ENABLE (1 << 29) +#define DS0_PIN_OUTPUT (DS0_FORCE_OFF_MODE) +#define DS0_PIN_OUTPUT_HIGH (DS0_FORCE_OFF_MODE | DS0_FORCE_OUT_HIGH) +#define DS0_PIN_OUTPUT_PULLUP (DS0_FORCE_OFF_MODE | DS0_PULL_UP_DOWN_EN | DS0_PULL_UP_SEL) +#define DS0_PIN_OUTPUT_PULLDOWN (DS0_FORCE_OFF_MODE | DS0_PULL_UP_DOWN_EN) +#define DS0_PIN_INPUT (DS0_FORCE_OFF_MODE | DS0_INPUT) +#define DS0_PIN_INPUT_PULLUP (DS0_FORCE_OFF_MODE | DS0_INPUT | DS0_PULL_UP_DOWN_EN | DS0_PULL_UP_SEL) +#define DS0_PIN_INPUT_PULLDOWN (DS0_FORCE_OFF_MODE | DS0_INPUT | DS0_PULL_UP_DOWN_EN) + #define PIN_OUTPUT (PULL_DISABLE) #define PIN_OUTPUT_PULLUP (PULL_UP) #define PIN_OUTPUT_PULLDOWN 0 -- cgit v1.2.3-59-g8ed1b From e7d5459dfaf613799915e901189d296bdc7534f9 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 26 Sep 2017 17:41:07 +0100 Subject: cpufreq: provide default frequency-invariance setter function Frequency-invariant accounting support based on the ratio of current frequency and maximum supported frequency is an optional feature an arch can implement. Since there are cpufreq drivers (e.g. cpufreq-dt) which can be build for different arch's a default implementation of the frequency-invariance setter function arch_set_freq_scale() is needed. This default implementation is an empty weak function which will be overwritten by a strong function in case the arch provides one. The setter function passes the cpumask of related (to the frequency change) cpus (online and offline cpus), the (new) current frequency and the maximum supported frequency. Signed-off-by: Dietmar Eggemann Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 6 ++++++ include/linux/cpufreq.h | 3 +++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index ea43b147a7fe..41d148af7748 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -161,6 +161,12 @@ u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy) } EXPORT_SYMBOL_GPL(get_cpu_idle_time); +__weak void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, + unsigned long max_freq) +{ +} +EXPORT_SYMBOL_GPL(arch_set_freq_scale); + /* * This is a generic cpufreq init() routine which can be used by cpufreq * drivers of SMP systems. It will do following: diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 537ff842ff73..28734ee185a7 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -919,6 +919,9 @@ static inline bool policy_has_boost_freq(struct cpufreq_policy *policy) extern unsigned int arch_freq_get_on_cpu(int cpu); +extern void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, + unsigned long max_freq); + /* the following are really really optional */ extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs; extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs; -- cgit v1.2.3-59-g8ed1b From 0e27c567d1673137b06aa96bb7aef635fb657dee Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 26 Sep 2017 17:41:10 +0100 Subject: drivers base/arch_topology: provide frequency-invariant accounting support Implements the arch-specific (arm and arm64) frequency-invariance setter function arch_set_freq_scale() which provides the following frequency scaling factor: current_freq(cpu) << SCHED_CAPACITY_SHIFT / max_supported_freq(cpu) One possible consumer of the frequency-invariance getter function topology_get_freq_scale() is the Per-Entity Load Tracking (PELT) mechanism of the task scheduler. Allow inlining of topology_get_freq_scale() into the task scheduler fast path (e.g. __update_load_avg_se()) by coding it as a static inline function in the arch topology header file. Signed-off-by: Dietmar Eggemann Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/arch_topology.c | 14 ++++++++++++++ include/linux/arch_topology.h | 9 +++++++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index e9bb368f32b3..416ec2f5211d 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -22,6 +22,20 @@ #include #include +DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE; + +void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, + unsigned long max_freq) +{ + unsigned long scale; + int i; + + scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq; + + for_each_cpu(i, cpus) + per_cpu(freq_scale, i) = scale; +} + static DEFINE_MUTEX(cpu_scale_mutex); static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE; diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 716ce587247e..f6e490312e4d 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -5,6 +5,7 @@ #define _LINUX_ARCH_TOPOLOGY_H_ #include +#include void topology_normalize_cpu_scale(void); @@ -16,4 +17,12 @@ unsigned long topology_get_cpu_scale(struct sched_domain *sd, int cpu); void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity); +DECLARE_PER_CPU(unsigned long, freq_scale); + +static inline +unsigned long topology_get_freq_scale(struct sched_domain *sd, int cpu) +{ + return per_cpu(freq_scale, cpu); +} + #endif /* _LINUX_ARCH_TOPOLOGY_H_ */ -- cgit v1.2.3-59-g8ed1b From 8216f588b52b61ce36fc0080218e4730435e58b7 Mon Sep 17 00:00:00 2001 From: Dietmar Eggemann Date: Tue, 26 Sep 2017 17:41:11 +0100 Subject: drivers base/arch_topology: allow inlining cpu-invariant accounting support Allow inlining of topology_get_cpu_scale() into the task scheduler fast path (e.g. __update_load_avg_se()) by coding it as a static inline function in the arch topology header file. Signed-off-by: Dietmar Eggemann Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/base/arch_topology.c | 7 +------ include/linux/arch_topology.h | 8 +++++++- 2 files changed, 8 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 416ec2f5211d..aea0b9d521f6 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -37,12 +37,7 @@ void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq, } static DEFINE_MUTEX(cpu_scale_mutex); -static DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE; - -unsigned long topology_get_cpu_scale(struct sched_domain *sd, int cpu) -{ - return per_cpu(cpu_scale, cpu); -} +DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE; void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity) { diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index f6e490312e4d..c189de3ef5df 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -12,8 +12,14 @@ void topology_normalize_cpu_scale(void); struct device_node; bool topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu); +DECLARE_PER_CPU(unsigned long, cpu_scale); + struct sched_domain; -unsigned long topology_get_cpu_scale(struct sched_domain *sd, int cpu); +static inline +unsigned long topology_get_cpu_scale(struct sched_domain *sd, int cpu) +{ + return per_cpu(cpu_scale, cpu); +} void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity); -- cgit v1.2.3-59-g8ed1b From 7f20333fe76780af19ee0d8d2dc6d04d35aa88d2 Mon Sep 17 00:00:00 2001 From: Al Cooper Date: Fri, 22 Sep 2017 15:33:59 -0400 Subject: soc: brcmstb: Add Product ID and Family ID helper functions Add Product ID and Family ID helper functions for brcmstb soc. Signed-off-by: Al Cooper Acked-by: Florian Fainelli Signed-off-by: Kishon Vijay Abraham I --- drivers/soc/bcm/brcmstb/common.c | 12 ++++++++++++ include/linux/soc/brcmstb/brcmstb.h | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'include') diff --git a/drivers/soc/bcm/brcmstb/common.c b/drivers/soc/bcm/brcmstb/common.c index 22e98a90468c..a71730da6385 100644 --- a/drivers/soc/bcm/brcmstb/common.c +++ b/drivers/soc/bcm/brcmstb/common.c @@ -40,6 +40,18 @@ bool soc_is_brcmstb(void) return of_match_node(brcmstb_machine_match, root) != NULL; } +u32 brcmstb_get_family_id(void) +{ + return family_id; +} +EXPORT_SYMBOL(brcmstb_get_family_id); + +u32 brcmstb_get_product_id(void) +{ + return product_id; +} +EXPORT_SYMBOL(brcmstb_get_product_id); + static const struct of_device_id sun_top_ctrl_match[] = { { .compatible = "brcm,bcm7125-sun-top-ctrl", }, { .compatible = "brcm,bcm7346-sun-top-ctrl", }, diff --git a/include/linux/soc/brcmstb/brcmstb.h b/include/linux/soc/brcmstb/brcmstb.h index 337ce414e898..8baafd6908b9 100644 --- a/include/linux/soc/brcmstb/brcmstb.h +++ b/include/linux/soc/brcmstb/brcmstb.h @@ -1,10 +1,27 @@ #ifndef __BRCMSTB_SOC_H #define __BRCMSTB_SOC_H +static inline u32 BRCM_ID(u32 reg) +{ + return reg >> 28 ? reg >> 16 : reg >> 8; +} + +static inline u32 BRCM_REV(u32 reg) +{ + return reg & 0xff; +} + /* * Bus Interface Unit control register setup, must happen early during boot, * before SMP is brought up, called by machine entry point. */ void brcmstb_biuctrl_init(void); +/* + * Helper functions for getting family or product id from the + * SoC driver. + */ +u32 brcmstb_get_family_id(void); +u32 brcmstb_get_product_id(void); + #endif /* __BRCMSTB_SOC_H */ -- cgit v1.2.3-59-g8ed1b From 640ab98fb3629c0f8417b9b2532eca596495f3bb Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 27 Sep 2017 05:40:16 -0600 Subject: buffer: have alloc_page_buffers() use __GFP_NOFAIL Instead of adding weird retry logic in that function, utilize __GFP_NOFAIL to ensure that the vm takes care of handling any potential retries appropriately. This means we don't have to call free_more_memory() from here. Reviewed-by: Nikolay Borisov Reviewed-by: Jan Kara Signed-off-by: Jens Axboe --- drivers/md/bitmap.c | 2 +- fs/buffer.c | 33 ++++++++++----------------------- fs/ntfs/aops.c | 2 +- fs/ntfs/mft.c | 2 +- include/linux/buffer_head.h | 2 +- 5 files changed, 14 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index d2121637b4ab..4d8ed74efadf 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -368,7 +368,7 @@ static int read_page(struct file *file, unsigned long index, pr_debug("read bitmap file (%dB @ %llu)\n", (int)PAGE_SIZE, (unsigned long long)index << PAGE_SHIFT); - bh = alloc_page_buffers(page, 1<i_blkbits, 0); + bh = alloc_page_buffers(page, 1<i_blkbits, false); if (!bh) { ret = -ENOMEM; goto out; diff --git a/fs/buffer.c b/fs/buffer.c index 170df856bdb9..1234ae343aef 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -861,16 +861,19 @@ int remove_inode_buffers(struct inode *inode) * which may not fail from ordinary buffer allocations. */ struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size, - int retry) + bool retry) { struct buffer_head *bh, *head; + gfp_t gfp = GFP_NOFS; long offset; -try_again: + if (retry) + gfp |= __GFP_NOFAIL; + head = NULL; offset = PAGE_SIZE; while ((offset -= size) >= 0) { - bh = alloc_buffer_head(GFP_NOFS); + bh = alloc_buffer_head(gfp); if (!bh) goto no_grow; @@ -896,23 +899,7 @@ no_grow: } while (head); } - /* - * Return failure for non-async IO requests. Async IO requests - * are not allowed to fail, so we have to wait until buffer heads - * become available. But we don't want tasks sleeping with - * partially complete buffers, so all were released above. - */ - if (!retry) - return NULL; - - /* We're _really_ low on memory. Now we just - * wait for old buffer heads to become free due to - * finishing IO. Since this is an async request and - * the reserve list is empty, we're sure there are - * async buffer heads in use. - */ - free_more_memory(); - goto try_again; + return NULL; } EXPORT_SYMBOL_GPL(alloc_page_buffers); @@ -1021,7 +1008,7 @@ grow_dev_page(struct block_device *bdev, sector_t block, /* * Allocate some buffers for this page */ - bh = alloc_page_buffers(page, size, 0); + bh = alloc_page_buffers(page, size, false); if (!bh) goto failed; @@ -1575,7 +1562,7 @@ void create_empty_buffers(struct page *page, { struct buffer_head *bh, *head, *tail; - head = alloc_page_buffers(page, blocksize, 1); + head = alloc_page_buffers(page, blocksize, true); bh = head; do { bh->b_state |= b_state; @@ -2638,7 +2625,7 @@ int nobh_write_begin(struct address_space *mapping, * Be careful: the buffer linked list is a NULL terminated one, rather * than the circular one we're used to. */ - head = alloc_page_buffers(page, blocksize, 0); + head = alloc_page_buffers(page, blocksize, false); if (!head) { ret = -ENOMEM; goto out_release; diff --git a/fs/ntfs/aops.c b/fs/ntfs/aops.c index cc91856b5e2d..3a2e509c77c5 100644 --- a/fs/ntfs/aops.c +++ b/fs/ntfs/aops.c @@ -1739,7 +1739,7 @@ void mark_ntfs_record_dirty(struct page *page, const unsigned int ofs) { spin_lock(&mapping->private_lock); if (unlikely(!page_has_buffers(page))) { spin_unlock(&mapping->private_lock); - bh = head = alloc_page_buffers(page, bh_size, 1); + bh = head = alloc_page_buffers(page, bh_size, true); spin_lock(&mapping->private_lock); if (likely(!page_has_buffers(page))) { struct buffer_head *tail; diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c index b6f402194f02..ee8392aee9f6 100644 --- a/fs/ntfs/mft.c +++ b/fs/ntfs/mft.c @@ -507,7 +507,7 @@ int ntfs_sync_mft_mirror(ntfs_volume *vol, const unsigned long mft_no, if (unlikely(!page_has_buffers(page))) { struct buffer_head *tail; - bh = head = alloc_page_buffers(page, blocksize, 1); + bh = head = alloc_page_buffers(page, blocksize, true); do { set_buffer_uptodate(bh); tail = bh; diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index c8dae555eccf..ae2d25f01b98 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -156,7 +156,7 @@ void set_bh_page(struct buffer_head *bh, struct page *page, unsigned long offset); int try_to_free_buffers(struct page *); struct buffer_head *alloc_page_buffers(struct page *page, unsigned long size, - int retry); + bool retry); void create_empty_buffers(struct page *, unsigned long, unsigned long b_state); void end_buffer_read_sync(struct buffer_head *bh, int uptodate); -- cgit v1.2.3-59-g8ed1b From 9ba4b2dfafaa711b41cc2102b0e9a529f3981218 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 20 Sep 2017 08:58:25 -0600 Subject: fs: kill 'nr_pages' argument from wakeup_flusher_threads() Everybody is passing in 0 now, let's get rid of the argument. Reviewed-by: Jan Kara Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 9 ++++----- fs/sync.c | 2 +- include/linux/writeback.h | 2 +- mm/vmscan.c | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 245c430a2e41..bb6148dc6d24 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1947,12 +1947,12 @@ void wb_workfn(struct work_struct *work) } /* - * Start writeback of `nr_pages' pages. If `nr_pages' is zero, write back - * the whole world. + * Wakeup the flusher threads to start writeback of all currently dirty pages */ -void wakeup_flusher_threads(long nr_pages, enum wb_reason reason) +void wakeup_flusher_threads(enum wb_reason reason) { struct backing_dev_info *bdi; + long nr_pages; /* * If we are expecting writeback progress we must submit plugged IO. @@ -1960,8 +1960,7 @@ void wakeup_flusher_threads(long nr_pages, enum wb_reason reason) if (blk_needs_flush_plug(current)) blk_schedule_flush_plug(current); - if (!nr_pages) - nr_pages = get_nr_dirty_pages(); + nr_pages = get_nr_dirty_pages(); rcu_read_lock(); list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) { diff --git a/fs/sync.c b/fs/sync.c index a576aa2e6b09..09f96a18dd93 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -108,7 +108,7 @@ SYSCALL_DEFINE0(sync) { int nowait = 0, wait = 1; - wakeup_flusher_threads(0, WB_REASON_SYNC); + wakeup_flusher_threads(WB_REASON_SYNC); iterate_supers(sync_inodes_one_sb, NULL); iterate_supers(sync_fs_one_sb, &nowait); iterate_supers(sync_fs_one_sb, &wait); diff --git a/include/linux/writeback.h b/include/linux/writeback.h index d5815794416c..1f9c6db5e29a 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -189,7 +189,7 @@ bool try_to_writeback_inodes_sb(struct super_block *, enum wb_reason reason); bool try_to_writeback_inodes_sb_nr(struct super_block *, unsigned long nr, enum wb_reason reason); void sync_inodes_sb(struct super_block *); -void wakeup_flusher_threads(long nr_pages, enum wb_reason reason); +void wakeup_flusher_threads(enum wb_reason reason); void inode_wait_for_writeback(struct inode *inode); /* writeback.h requires fs.h; it, too, is not included from here. */ diff --git a/mm/vmscan.c b/mm/vmscan.c index 13d711dd8776..42a7fdd52d87 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1867,7 +1867,7 @@ shrink_inactive_list(unsigned long nr_to_scan, struct lruvec *lruvec, * also allow kswapd to start writing pages during reclaim. */ if (stat.nr_unqueued_dirty == nr_taken) { - wakeup_flusher_threads(0, WB_REASON_VMSCAN); + wakeup_flusher_threads(WB_REASON_VMSCAN); set_bit(PGDAT_DIRTY, &pgdat->flags); } -- cgit v1.2.3-59-g8ed1b From 47410d88f665486bf91f02242ab5d5692b8887ac Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 28 Sep 2017 11:25:03 -0600 Subject: writeback: remove 'range_cyclic' argument for wb_start_writeback() All the callers pass in 'true' for range_cyclic, so kill the argument. Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 6 +++--- include/linux/backing-dev.h | 2 +- mm/page-writeback.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 65e6992d8719..fe555bce886c 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -934,7 +934,7 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi, #endif /* CONFIG_CGROUP_WRITEBACK */ void wb_start_writeback(struct bdi_writeback *wb, long nr_pages, - bool range_cyclic, enum wb_reason reason) + enum wb_reason reason) { struct wb_writeback_work *work; @@ -955,7 +955,7 @@ void wb_start_writeback(struct bdi_writeback *wb, long nr_pages, work->sync_mode = WB_SYNC_NONE; work->nr_pages = nr_pages; - work->range_cyclic = range_cyclic; + work->range_cyclic = 1; work->reason = reason; work->auto_free = 1; @@ -1971,7 +1971,7 @@ void wakeup_flusher_threads(enum wb_reason reason) list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node) wb_start_writeback(wb, wb_split_bdi_pages(wb, nr_pages), - true, reason); + reason); } rcu_read_unlock(); } diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 854e1bdd0b2a..0f63493de9e7 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -39,7 +39,7 @@ static inline struct backing_dev_info *bdi_alloc(gfp_t gfp_mask) } void wb_start_writeback(struct bdi_writeback *wb, long nr_pages, - bool range_cyclic, enum wb_reason reason); + enum wb_reason reason); void wb_start_background_writeback(struct bdi_writeback *wb); void wb_workfn(struct work_struct *work); void wb_wakeup_delayed(struct bdi_writeback *wb); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 0b9c5cbe8eba..dede53355123 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -1994,8 +1994,8 @@ void laptop_mode_timer_fn(unsigned long data) rcu_read_lock(); list_for_each_entry_rcu(wb, &q->backing_dev_info->wb_list, bdi_node) if (wb_has_dirty_io(wb)) - wb_start_writeback(wb, nr_pages, true, - WB_REASON_LAPTOP_TIMER); + wb_start_writeback(wb, nr_pages, + WB_REASON_LAPTOP_TIMER); rcu_read_unlock(); } -- cgit v1.2.3-59-g8ed1b From 595043e5f9ef1d8263bd9fda215cade489227491 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 28 Sep 2017 11:26:59 -0600 Subject: writeback: provide a wakeup_flusher_threads_bdi() Similar to wakeup_flusher_threads(), except that we only wake up the flusher threads on the specified backing device. No functional changes in this patch. Acked-by: Johannes Weiner Tested-by: Chris Mason Reviewed-by: Jan Kara Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 39 +++++++++++++++++++++++++++++---------- include/linux/writeback.h | 2 ++ 2 files changed, 31 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index fe555bce886c..9f39829459dc 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -1946,6 +1946,33 @@ void wb_workfn(struct work_struct *work) current->flags &= ~PF_SWAPWRITE; } +/* + * Start writeback of `nr_pages' pages on this bdi. If `nr_pages' is zero, + * write back the whole world. + */ +static void __wakeup_flusher_threads_bdi(struct backing_dev_info *bdi, + long nr_pages, enum wb_reason reason) +{ + struct bdi_writeback *wb; + + if (!bdi_has_dirty_io(bdi)) + return; + + list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node) + wb_start_writeback(wb, wb_split_bdi_pages(wb, nr_pages), + reason); +} + +void wakeup_flusher_threads_bdi(struct backing_dev_info *bdi, + enum wb_reason reason) +{ + long nr_pages = get_nr_dirty_pages(); + + rcu_read_lock(); + __wakeup_flusher_threads_bdi(bdi, nr_pages, reason); + rcu_read_unlock(); +} + /* * Wakeup the flusher threads to start writeback of all currently dirty pages */ @@ -1963,16 +1990,8 @@ void wakeup_flusher_threads(enum wb_reason reason) nr_pages = get_nr_dirty_pages(); rcu_read_lock(); - list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) { - struct bdi_writeback *wb; - - if (!bdi_has_dirty_io(bdi)) - continue; - - list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node) - wb_start_writeback(wb, wb_split_bdi_pages(wb, nr_pages), - reason); - } + list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) + __wakeup_flusher_threads_bdi(bdi, nr_pages, reason); rcu_read_unlock(); } diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 1f9c6db5e29a..9c0091678af4 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -190,6 +190,8 @@ bool try_to_writeback_inodes_sb_nr(struct super_block *, unsigned long nr, enum wb_reason reason); void sync_inodes_sb(struct super_block *); void wakeup_flusher_threads(enum wb_reason reason); +void wakeup_flusher_threads_bdi(struct backing_dev_info *bdi, + enum wb_reason reason); void inode_wait_for_writeback(struct inode *inode); /* writeback.h requires fs.h; it, too, is not included from here. */ -- cgit v1.2.3-59-g8ed1b From 9dfb176fae57a1dea68531fd25e867037e4d9bac Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 28 Sep 2017 11:28:55 -0600 Subject: writeback: make wb_start_writeback() static We don't have any callers outside of fs-writeback.c anymore, make it private. Acked-by: Johannes Weiner Tested-by: Chris Mason Reviewed-by: Jan Kara Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 4 ++-- include/linux/backing-dev.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 9f39829459dc..f1042061eaad 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -933,8 +933,8 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi, #endif /* CONFIG_CGROUP_WRITEBACK */ -void wb_start_writeback(struct bdi_writeback *wb, long nr_pages, - enum wb_reason reason) +static void wb_start_writeback(struct bdi_writeback *wb, long nr_pages, + enum wb_reason reason) { struct wb_writeback_work *work; diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 0f63493de9e7..157e950a70dc 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -38,8 +38,6 @@ static inline struct backing_dev_info *bdi_alloc(gfp_t gfp_mask) return bdi_alloc_node(gfp_mask, NUMA_NO_NODE); } -void wb_start_writeback(struct bdi_writeback *wb, long nr_pages, - enum wb_reason reason); void wb_start_background_writeback(struct bdi_writeback *wb); void wb_workfn(struct work_struct *work); void wb_wakeup_delayed(struct bdi_writeback *wb); -- cgit v1.2.3-59-g8ed1b From aac8d41cd438f25bf3110fc6b98f1d16d7dbc169 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 28 Sep 2017 11:31:55 -0600 Subject: writeback: only allow one inflight and pending full flush When someone calls wakeup_flusher_threads() or wakeup_flusher_threads_bdi(), they schedule writeback of all dirty pages in the system (or on that bdi). If we are tight on memory, we can get tons of these queued from kswapd/vmscan. This causes (at least) two problems: 1) We consume a ton of memory just allocating writeback work items. We've seen as much as 600 million of these writeback work items pending. That's a lot of memory to pointlessly hold hostage, while the box is under memory pressure. 2) We spend so much time processing these work items, that we introduce a softlockup in writeback processing. This is because each of the writeback work items don't end up doing any work (it's hard when you have millions of identical ones coming in to the flush machinery), so we just sit in a tight loop pulling work items and deleting/freeing them. Fix this by adding a 'start_all' bit to the writeback structure, and set that when someone attempts to flush all dirty pages. The bit is cleared when we start writeback on that work item. If the bit is already set when we attempt to queue !nr_pages writeback, then we simply ignore it. This provides us one full flush in flight, with one pending as well, and makes for more efficient handling of this type of writeback. Acked-by: Johannes Weiner Tested-by: Chris Mason Reviewed-by: Jan Kara Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 25 +++++++++++++++++++++++++ include/linux/backing-dev-defs.h | 1 + 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 424577152eb5..399619c97567 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -53,6 +53,7 @@ struct wb_writeback_work { unsigned int for_background:1; unsigned int for_sync:1; /* sync(2) WB_SYNC_ALL writeback */ unsigned int auto_free:1; /* free on completion */ + unsigned int start_all:1; /* nr_pages == 0 (all) writeback */ enum wb_reason reason; /* why was writeback initiated? */ struct list_head list; /* pending work list */ @@ -951,6 +952,20 @@ static void wb_start_writeback(struct bdi_writeback *wb, enum wb_reason reason) if (!wb_has_dirty_io(wb)) return; + /* + * All callers of this function want to start writeback of all + * dirty pages. Places like vmscan can call this at a very + * high frequency, causing pointless allocations of tons of + * work items and keeping the flusher threads busy retrieving + * that work. Ensure that we only allow one of them pending and + * inflight at the time. It doesn't matter if we race a little + * bit on this, so use the faster separate test/set bit variants. + */ + if (test_bit(WB_start_all, &wb->state)) + return; + + set_bit(WB_start_all, &wb->state); + /* * This is WB_SYNC_NONE writeback, so if allocation fails just * wakeup the thread for old dirty data writeback @@ -958,6 +973,7 @@ static void wb_start_writeback(struct bdi_writeback *wb, enum wb_reason reason) work = kzalloc(sizeof(*work), GFP_NOWAIT | __GFP_NOMEMALLOC | __GFP_NOWARN); if (!work) { + clear_bit(WB_start_all, &wb->state); trace_writeback_nowork(wb); wb_wakeup(wb); return; @@ -968,6 +984,7 @@ static void wb_start_writeback(struct bdi_writeback *wb, enum wb_reason reason) work->range_cyclic = 1; work->reason = reason; work->auto_free = 1; + work->start_all = 1; wb_queue_work(wb, work); } @@ -1821,6 +1838,14 @@ static struct wb_writeback_work *get_next_work_item(struct bdi_writeback *wb) list_del_init(&work->list); } spin_unlock_bh(&wb->work_lock); + + /* + * Once we start processing a work item that had !nr_pages, + * clear the wb state bit for that so we can allow more. + */ + if (work && work->start_all) + clear_bit(WB_start_all, &wb->state); + return work; } diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h index 866c433e7d32..420de5c7c7f9 100644 --- a/include/linux/backing-dev-defs.h +++ b/include/linux/backing-dev-defs.h @@ -24,6 +24,7 @@ enum wb_state { WB_shutting_down, /* wb_shutdown() in progress */ WB_writeback_running, /* Writeback is in progress */ WB_has_dirty_io, /* Dirty inodes on ->b_{dirty|io|more_io} */ + WB_start_all, /* nr_pages == 0 (all) work pending */ }; enum wb_congested_state { -- cgit v1.2.3-59-g8ed1b From abf4bb6b63d0a54266f8e7eff3720c1974063971 Mon Sep 17 00:00:00 2001 From: Yotam Gigi Date: Tue, 3 Oct 2017 09:58:06 +0200 Subject: skbuff: Add the offload_mr_fwd_mark field Similarly to the offload_fwd_mark field, the offload_mr_fwd_mark field is used to allow partial offloading of MFC multicast routes. Switchdev drivers can offload MFC multicast routes to the hardware by registering to the FIB notification chain. When one of the route output interfaces is not offload-able, i.e. has different parent ID, the route cannot be fully offloaded by the hardware. Examples to non-offload-able devices are a management NIC, dummy device, pimreg device, etc. Similar problem exists in the bridge module, as one bridge can hold interfaces with different parent IDs. At the bridge, the problem is solved by the offload_fwd_mark skb field. Currently, when a route cannot go through full offload, the only solution for a switchdev driver is not to offload it at all and let the packet go through slow path. Using the offload_mr_fwd_mark field, a driver can indicate that a packet was already forwarded by hardware to all the devices with the same parent ID as the input device. Further patches in this patch-set are going to enhance ipmr to skip multicast forwarding to devices with the same parent ID if a packets is marked with that field. The reason why the already existing "offload_fwd_mark" bit cannot be used is that a switchdev driver would want to make the distinction between a packet that has already gone through L2 forwarding but did not go through multicast forwarding, and a packet that has already gone through both L2 and multicast forwarding. For example: when a packet is ingressing from a switchport enslaved to a bridge, which is configured with multicast forwarding, the following scenarios are possible: - The packet can be trapped to the CPU due to exception while multicast forwarding (for example, MTU error). In that case, it had already gone through L2 forwarding in the hardware, thus A switchdev driver would want to set the skb->offload_fwd_mark and not the skb->offload_mr_fwd_mark. - The packet can also be trapped due to a pimreg/dummy device used as one of the output interfaces. In that case, it can go through both L2 and (partial) multicast forwarding inside the hardware, thus a switchdev driver would want to set both the skb->offload_fwd_mark and skb->offload_mr_fwd_mark. Signed-off-by: Yotam Gigi Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/skbuff.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 19e64bfb1a66..ada821466e88 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -772,6 +772,7 @@ struct sk_buff { __u8 remcsum_offload:1; #ifdef CONFIG_NET_SWITCHDEV __u8 offload_fwd_mark:1; + __u8 offload_mr_fwd_mark:1; #endif #ifdef CONFIG_NET_CLS_ACT __u8 tc_skip_classify:1; -- cgit v1.2.3-59-g8ed1b From 5d8b3e69fc5e5ccafc9db1251bb7c78a8622fddd Mon Sep 17 00:00:00 2001 From: Yotam Gigi Date: Tue, 3 Oct 2017 09:58:07 +0200 Subject: ipv4: ipmr: Add the parent ID field to VIF struct In order to allow the ipmr module to do partial multicast forwarding according to the device parent ID, add the device parent ID field to the VIF struct. This way, the forwarding path can use the parent ID field without invoking switchdev calls, which requires the RTNL lock. When a new VIF is added, set the device parent ID field in it by invoking the switchdev_port_attr_get call. Signed-off-by: Yotam Gigi Reviewed-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/linux/mroute.h | 1 + net/ipv4/ipmr.c | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'include') diff --git a/include/linux/mroute.h b/include/linux/mroute.h index b072a84fbe1c..8242d05df35e 100644 --- a/include/linux/mroute.h +++ b/include/linux/mroute.h @@ -57,6 +57,7 @@ static inline bool ipmr_rule_default(const struct fib_rule *rule) struct vif_device { struct net_device *dev; /* Device we are using */ + struct netdev_phys_item_id dev_parent_id; /* Device parent ID */ unsigned long bytes_in,bytes_out; unsigned long pkt_in,pkt_out; /* Statistics */ unsigned long rate_limit; /* Traffic shaping (NI) */ diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index a844738b38bd..1b161ada7ae6 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -67,6 +67,7 @@ #include #include #include +#include struct ipmr_rule { struct fib_rule common; @@ -868,6 +869,9 @@ static int vif_add(struct net *net, struct mr_table *mrt, struct vifctl *vifc, int mrtsock) { int vifi = vifc->vifc_vifi; + struct switchdev_attr attr = { + .id = SWITCHDEV_ATTR_ID_PORT_PARENT_ID, + }; struct vif_device *v = &mrt->vif_table[vifi]; struct net_device *dev; struct in_device *in_dev; @@ -942,6 +946,13 @@ static int vif_add(struct net *net, struct mr_table *mrt, /* Fill in the VIF structures */ + attr.orig_dev = dev; + if (!switchdev_port_attr_get(dev, &attr)) { + memcpy(v->dev_parent_id.id, attr.u.ppid.id, attr.u.ppid.id_len); + v->dev_parent_id.id_len = attr.u.ppid.id_len; + } else { + v->dev_parent_id.id_len = 0; + } v->rate_limit = vifc->vifc_rate_limit; v->local = vifc->vifc_lcl_addr.s_addr; v->remote = vifc->vifc_rmt_addr.s_addr; -- cgit v1.2.3-59-g8ed1b From 6c5570016b972d9b1f0f6c2dca9cc0422b1f92bf Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 2 Oct 2017 23:50:05 +0200 Subject: net: core: decouple ifalias get/set from rtnl lock Device alias can be set by either rtnetlink (rtnl is held) or sysfs. rtnetlink hold the rtnl mutex, sysfs acquires it for this purpose. Add an extra mutex for it and use rcu to protect concurrent accesses. This allows the sysfs path to not take rtnl and would later allow to not hold it when dumping ifalias. Based on suggestion from Eric Dumazet. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/linux/netdevice.h | 8 +++++++- net/core/dev.c | 52 +++++++++++++++++++++++++++++++++++------------ net/core/net-sysfs.c | 17 ++++++++-------- net/core/rtnetlink.c | 13 ++++++++++-- 4 files changed, 65 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e1d6ef130611..d04424cfffba 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -826,6 +826,11 @@ struct xfrmdev_ops { }; #endif +struct dev_ifalias { + struct rcu_head rcuhead; + char ifalias[]; +}; + /* * This structure defines the management hooks for network devices. * The following hooks can be defined; unless noted otherwise, they are @@ -1632,7 +1637,7 @@ enum netdev_priv_flags { struct net_device { char name[IFNAMSIZ]; struct hlist_node name_hlist; - char *ifalias; + struct dev_ifalias __rcu *ifalias; /* * I/O specific fields * FIXME: Merge these and struct ifmap into one @@ -3275,6 +3280,7 @@ void __dev_notify_flags(struct net_device *, unsigned int old_flags, unsigned int gchanges); int dev_change_name(struct net_device *, const char *); int dev_set_alias(struct net_device *, const char *, size_t); +int dev_get_alias(const struct net_device *, char *, size_t); int dev_change_net_namespace(struct net_device *, struct net *, const char *); int __dev_set_mtu(struct net_device *, int); int dev_set_mtu(struct net_device *, int); diff --git a/net/core/dev.c b/net/core/dev.c index e350c768d4b5..1770097cfd86 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -188,6 +188,8 @@ static struct napi_struct *napi_by_id(unsigned int napi_id); DEFINE_RWLOCK(dev_base_lock); EXPORT_SYMBOL(dev_base_lock); +static DEFINE_MUTEX(ifalias_mutex); + /* protects napi_hash addition/deletion and napi_gen_id */ static DEFINE_SPINLOCK(napi_hash_lock); @@ -1265,29 +1267,53 @@ rollback: */ int dev_set_alias(struct net_device *dev, const char *alias, size_t len) { - char *new_ifalias; - - ASSERT_RTNL(); + struct dev_ifalias *new_alias = NULL; if (len >= IFALIASZ) return -EINVAL; - if (!len) { - kfree(dev->ifalias); - dev->ifalias = NULL; - return 0; + if (len) { + new_alias = kmalloc(sizeof(*new_alias) + len + 1, GFP_KERNEL); + if (!new_alias) + return -ENOMEM; + + memcpy(new_alias->ifalias, alias, len); + new_alias->ifalias[len] = 0; } - new_ifalias = krealloc(dev->ifalias, len + 1, GFP_KERNEL); - if (!new_ifalias) - return -ENOMEM; - dev->ifalias = new_ifalias; - memcpy(dev->ifalias, alias, len); - dev->ifalias[len] = 0; + mutex_lock(&ifalias_mutex); + rcu_swap_protected(dev->ifalias, new_alias, + mutex_is_locked(&ifalias_mutex)); + mutex_unlock(&ifalias_mutex); + + if (new_alias) + kfree_rcu(new_alias, rcuhead); return len; } +/** + * dev_get_alias - get ifalias of a device + * @dev: device + * @alias: buffer to store name of ifalias + * @len: size of buffer + * + * get ifalias for a device. Caller must make sure dev cannot go + * away, e.g. rcu read lock or own a reference count to device. + */ +int dev_get_alias(const struct net_device *dev, char *name, size_t len) +{ + const struct dev_ifalias *alias; + int ret = 0; + + rcu_read_lock(); + alias = rcu_dereference(dev->ifalias); + if (alias) + ret = snprintf(name, len, "%s", alias->ifalias); + rcu_read_unlock(); + + return ret; +} /** * netdev_features_change - device changes features diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 927a6dcbad96..51d5836d8fb9 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -391,10 +391,7 @@ static ssize_t ifalias_store(struct device *dev, struct device_attribute *attr, if (len > 0 && buf[len - 1] == '\n') --count; - if (!rtnl_trylock()) - return restart_syscall(); ret = dev_set_alias(netdev, buf, count); - rtnl_unlock(); return ret < 0 ? ret : len; } @@ -403,13 +400,12 @@ static ssize_t ifalias_show(struct device *dev, struct device_attribute *attr, char *buf) { const struct net_device *netdev = to_net_dev(dev); + char tmp[IFALIASZ]; ssize_t ret = 0; - if (!rtnl_trylock()) - return restart_syscall(); - if (netdev->ifalias) - ret = sprintf(buf, "%s\n", netdev->ifalias); - rtnl_unlock(); + ret = dev_get_alias(netdev, tmp, sizeof(tmp)); + if (ret > 0) + ret = sprintf(buf, "%s\n", tmp); return ret; } static DEVICE_ATTR_RW(ifalias); @@ -1488,7 +1484,10 @@ static void netdev_release(struct device *d) BUG_ON(dev->reg_state != NETREG_RELEASED); - kfree(dev->ifalias); + /* no need to wait for rcu grace period: + * device is dead and about to be freed. + */ + kfree(rcu_access_pointer(dev->ifalias)); netdev_freemem(dev); } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index e6955da0d58d..3961f87cdc76 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1366,6 +1366,16 @@ static int nla_put_iflink(struct sk_buff *skb, const struct net_device *dev) return nla_put_u32(skb, IFLA_LINK, ifindex); } +static noinline_for_stack int nla_put_ifalias(struct sk_buff *skb, + struct net_device *dev) +{ + char buf[IFALIASZ]; + int ret; + + ret = dev_get_alias(dev, buf, sizeof(buf)); + return ret > 0 ? nla_put_string(skb, IFLA_IFALIAS, buf) : 0; +} + static int rtnl_fill_link_netnsid(struct sk_buff *skb, const struct net_device *dev) { @@ -1425,8 +1435,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) || (dev->qdisc && nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) || - (dev->ifalias && - nla_put_string(skb, IFLA_IFALIAS, dev->ifalias)) || + nla_put_ifalias(skb, dev) || nla_put_u32(skb, IFLA_CARRIER_CHANGES, atomic_read(&dev->carrier_changes)) || nla_put_u8(skb, IFLA_PROTO_DOWN, dev->proto_down)) -- cgit v1.2.3-59-g8ed1b From f952be79cebd49d04154781d99408867a069d375 Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Tue, 3 Oct 2017 19:20:11 -0300 Subject: sctp: introduce struct sctp_stream_out_ext With the stream schedulers, sctp_stream_out will become too big to be allocated by kmalloc and as we need to allocate with BH disabled, we cannot use __vmalloc in sctp_stream_init(). This patch moves out the stats from sctp_stream_out to sctp_stream_out_ext, which will be allocated only when the application tries to sendmsg something on it. Just the introduction of sctp_stream_out_ext would already fix the issue described above by splitting the allocation in two. Moving the stats to it also reduces the pressure on the allocator as we will ask for less memory atomically when creating the socket and we will use GFP_KERNEL later. Then, for stream schedulers, we will just use sctp_stream_out_ext. Tested-by: Xin Long Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 10 ++++++++-- net/sctp/chunk.c | 6 +++--- net/sctp/outqueue.c | 4 ++-- net/sctp/socket.c | 27 +++++++++++++++++++++------ net/sctp/stream.c | 16 ++++++++++++++++ 5 files changed, 50 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 0477945de1a3..9b2b30b3ba4d 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -84,6 +84,7 @@ struct sctp_ulpq; struct sctp_ep_common; struct crypto_shash; struct sctp_stream; +struct sctp_stream_out; #include @@ -380,6 +381,7 @@ struct sctp_sender_hb_info { int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, gfp_t gfp); +int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid); void sctp_stream_free(struct sctp_stream *stream); void sctp_stream_clear(struct sctp_stream *stream); void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new); @@ -1315,11 +1317,15 @@ struct sctp_inithdr_host { __u32 initial_tsn; }; +struct sctp_stream_out_ext { + __u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1]; + __u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1]; +}; + struct sctp_stream_out { __u16 ssn; __u8 state; - __u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1]; - __u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1]; + struct sctp_stream_out_ext *ext; }; struct sctp_stream_in { diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 3afac275ee82..7b261afc47b9 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -311,10 +311,10 @@ int sctp_chunk_abandoned(struct sctp_chunk *chunk) if (chunk->sent_count) { chunk->asoc->abandoned_sent[SCTP_PR_INDEX(TTL)]++; - streamout->abandoned_sent[SCTP_PR_INDEX(TTL)]++; + streamout->ext->abandoned_sent[SCTP_PR_INDEX(TTL)]++; } else { chunk->asoc->abandoned_unsent[SCTP_PR_INDEX(TTL)]++; - streamout->abandoned_unsent[SCTP_PR_INDEX(TTL)]++; + streamout->ext->abandoned_unsent[SCTP_PR_INDEX(TTL)]++; } return 1; } else if (SCTP_PR_RTX_ENABLED(chunk->sinfo.sinfo_flags) && @@ -323,7 +323,7 @@ int sctp_chunk_abandoned(struct sctp_chunk *chunk) &chunk->asoc->stream.out[chunk->sinfo.sinfo_stream]; chunk->asoc->abandoned_sent[SCTP_PR_INDEX(RTX)]++; - streamout->abandoned_sent[SCTP_PR_INDEX(RTX)]++; + streamout->ext->abandoned_sent[SCTP_PR_INDEX(RTX)]++; return 1; } else if (!SCTP_PR_POLICY(chunk->sinfo.sinfo_flags) && chunk->msg->expires_at && diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 2966ff400755..746b07b7937d 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -366,7 +366,7 @@ static int sctp_prsctp_prune_sent(struct sctp_association *asoc, streamout = &asoc->stream.out[chk->sinfo.sinfo_stream]; asoc->sent_cnt_removable--; asoc->abandoned_sent[SCTP_PR_INDEX(PRIO)]++; - streamout->abandoned_sent[SCTP_PR_INDEX(PRIO)]++; + streamout->ext->abandoned_sent[SCTP_PR_INDEX(PRIO)]++; if (!chk->tsn_gap_acked) { if (chk->transport) @@ -404,7 +404,7 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc, struct sctp_stream_out *streamout = &asoc->stream.out[chk->sinfo.sinfo_stream]; - streamout->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; + streamout->ext->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; } msg_len -= SCTP_DATA_SNDSIZE(chk) + diff --git a/net/sctp/socket.c b/net/sctp/socket.c index d4730ada7f32..d207734326b0 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1927,6 +1927,13 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) goto out_free; } + /* Allocate sctp_stream_out_ext if not already done */ + if (unlikely(!asoc->stream.out[sinfo->sinfo_stream].ext)) { + err = sctp_stream_init_ext(&asoc->stream, sinfo->sinfo_stream); + if (err) + goto out_free; + } + if (sctp_wspace(asoc) < msg_len) sctp_prsctp_prune(asoc, sinfo, msg_len - sctp_wspace(asoc)); @@ -6645,7 +6652,7 @@ static int sctp_getsockopt_pr_streamstatus(struct sock *sk, int len, char __user *optval, int __user *optlen) { - struct sctp_stream_out *streamout; + struct sctp_stream_out_ext *streamoute; struct sctp_association *asoc; struct sctp_prstatus params; int retval = -EINVAL; @@ -6668,21 +6675,29 @@ static int sctp_getsockopt_pr_streamstatus(struct sock *sk, int len, if (!asoc || params.sprstat_sid >= asoc->stream.outcnt) goto out; - streamout = &asoc->stream.out[params.sprstat_sid]; + streamoute = asoc->stream.out[params.sprstat_sid].ext; + if (!streamoute) { + /* Not allocated yet, means all stats are 0 */ + params.sprstat_abandoned_unsent = 0; + params.sprstat_abandoned_sent = 0; + retval = 0; + goto out; + } + if (policy == SCTP_PR_SCTP_NONE) { params.sprstat_abandoned_unsent = 0; params.sprstat_abandoned_sent = 0; for (policy = 0; policy <= SCTP_PR_INDEX(MAX); policy++) { params.sprstat_abandoned_unsent += - streamout->abandoned_unsent[policy]; + streamoute->abandoned_unsent[policy]; params.sprstat_abandoned_sent += - streamout->abandoned_sent[policy]; + streamoute->abandoned_sent[policy]; } } else { params.sprstat_abandoned_unsent = - streamout->abandoned_unsent[__SCTP_PR_INDEX(policy)]; + streamoute->abandoned_unsent[__SCTP_PR_INDEX(policy)]; params.sprstat_abandoned_sent = - streamout->abandoned_sent[__SCTP_PR_INDEX(policy)]; + streamoute->abandoned_sent[__SCTP_PR_INDEX(policy)]; } if (put_user(len, optlen) || copy_to_user(optval, ¶ms, len)) { diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 952437d656cc..055ca25bbc91 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -121,8 +121,24 @@ in: return 0; } +int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) +{ + struct sctp_stream_out_ext *soute; + + soute = kzalloc(sizeof(*soute), GFP_KERNEL); + if (!soute) + return -ENOMEM; + stream->out[sid].ext = soute; + + return 0; +} + void sctp_stream_free(struct sctp_stream *stream) { + int i; + + for (i = 0; i < stream->outcnt; i++) + kfree(stream->out[i].ext); kfree(stream->out); kfree(stream->in); } -- cgit v1.2.3-59-g8ed1b From 2fc019f790312e703efa1a44204c586112a430dc Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Tue, 3 Oct 2017 19:20:12 -0300 Subject: sctp: introduce sctp_chunk_stream_no Add a helper to fetch the stream number from a given chunk. Tested-by: Xin Long Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 9b2b30b3ba4d..c48f7999fe9b 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -642,6 +642,11 @@ void sctp_init_addrs(struct sctp_chunk *, union sctp_addr *, union sctp_addr *); const union sctp_addr *sctp_source(const struct sctp_chunk *chunk); +static inline __u16 sctp_chunk_stream_no(struct sctp_chunk *ch) +{ + return ntohs(ch->subh.data_hdr->stream); +} + enum { SCTP_ADDR_NEW, /* new address added to assoc/ep */ SCTP_ADDR_SRC, /* address can be used as source */ -- cgit v1.2.3-59-g8ed1b From 5bbbbe32a43199c2b9ea5ea66fab6241c64beb51 Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Tue, 3 Oct 2017 19:20:13 -0300 Subject: sctp: introduce stream scheduler foundations This patch introduces the hooks necessary to do stream scheduling, as per RFC Draft ndata. It also introduces the first scheduler, which is what we do today but now factored out: first come first served (FCFS). With stream scheduling now we have to track which chunk was enqueued on which stream and be able to select another other than the in front of the main outqueue. So we introduce a list on sctp_stream_out_ext structure for this purpose. We reuse sctp_chunk->transmitted_list space for the list above, as the chunk cannot belong to the two lists at the same time. By using the union in there, we can have distinct names for these moments. sctp_sched_ops are the operations expected to be implemented by each scheduler. The dequeueing is a bit particular to this implementation but it is to match how we dequeue packets today. We first dequeue and then check if it fits the packet and if not, we requeue it at head. Thus why we don't have a peek operation but have dequeue_done instead, which is called once the chunk can be safely considered as transmitted. The check removed from sctp_outq_flush is now performed by sctp_stream_outq_migrate, which is only called during assoc setup. (sctp_sendmsg() also checks for it) The only operation that is foreseen but not yet added here is a way to signalize that a new packet is starting or that the packet is done, for round robin scheduler per packet, but is intentionally left to the patch that actually implements it. Support for I-DATA chunks, also described in this RFC, with user message interleaving is straightforward as it just requires the schedulers to probe for the feature and ignore datamsg boundaries when dequeueing. See-also: https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-13 Tested-by: Xin Long Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/stream_sched.h | 72 +++++++++++ include/net/sctp/structs.h | 15 ++- include/uapi/linux/sctp.h | 6 + net/sctp/Makefile | 2 +- net/sctp/outqueue.c | 59 +++++---- net/sctp/sm_sideeffect.c | 3 + net/sctp/stream.c | 88 +++++++++++-- net/sctp/stream_sched.c | 270 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 477 insertions(+), 38 deletions(-) create mode 100644 include/net/sctp/stream_sched.h create mode 100644 net/sctp/stream_sched.c (limited to 'include') diff --git a/include/net/sctp/stream_sched.h b/include/net/sctp/stream_sched.h new file mode 100644 index 000000000000..c676550a4c7d --- /dev/null +++ b/include/net/sctp/stream_sched.h @@ -0,0 +1,72 @@ +/* SCTP kernel implementation + * (C) Copyright Red Hat Inc. 2017 + * + * These are definitions used by the stream schedulers, defined in RFC + * draft ndata (https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-11) + * + * This SCTP implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This SCTP implementation 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. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, see + * . + * + * Please send any bug reports or fixes you make to the + * email addresses: + * lksctp developers + * + * Written or modified by: + * Marcelo Ricardo Leitner + */ + +#ifndef __sctp_stream_sched_h__ +#define __sctp_stream_sched_h__ + +struct sctp_sched_ops { + /* Property handling for a given stream */ + int (*set)(struct sctp_stream *stream, __u16 sid, __u16 value, + gfp_t gfp); + int (*get)(struct sctp_stream *stream, __u16 sid, __u16 *value); + + /* Init the specific scheduler */ + int (*init)(struct sctp_stream *stream); + /* Init a stream */ + int (*init_sid)(struct sctp_stream *stream, __u16 sid, gfp_t gfp); + /* Frees the entire thing */ + void (*free)(struct sctp_stream *stream); + + /* Enqueue a chunk */ + void (*enqueue)(struct sctp_outq *q, struct sctp_datamsg *msg); + /* Dequeue a chunk */ + struct sctp_chunk *(*dequeue)(struct sctp_outq *q); + /* Called only if the chunk fit the packet */ + void (*dequeue_done)(struct sctp_outq *q, struct sctp_chunk *chunk); + /* Sched all chunks already enqueued */ + void (*sched_all)(struct sctp_stream *steam); + /* Unched all chunks already enqueued */ + void (*unsched_all)(struct sctp_stream *steam); +}; + +int sctp_sched_set_sched(struct sctp_association *asoc, + enum sctp_sched_type sched); +int sctp_sched_get_sched(struct sctp_association *asoc); +int sctp_sched_set_value(struct sctp_association *asoc, __u16 sid, + __u16 value, gfp_t gfp); +int sctp_sched_get_value(struct sctp_association *asoc, __u16 sid, + __u16 *value); +void sctp_sched_dequeue_done(struct sctp_outq *q, struct sctp_chunk *ch); + +void sctp_sched_dequeue_common(struct sctp_outq *q, struct sctp_chunk *ch); +int sctp_sched_init_sid(struct sctp_stream *stream, __u16 sid, gfp_t gfp); +struct sctp_sched_ops *sctp_sched_ops_from_stream(struct sctp_stream *stream); + +#endif /* __sctp_stream_sched_h__ */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index c48f7999fe9b..3c22a30fd71b 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -84,7 +84,6 @@ struct sctp_ulpq; struct sctp_ep_common; struct crypto_shash; struct sctp_stream; -struct sctp_stream_out; #include @@ -531,8 +530,12 @@ struct sctp_chunk { /* How many times this chunk have been sent, for prsctp RTX policy */ int sent_count; - /* This is our link to the per-transport transmitted list. */ - struct list_head transmitted_list; + union { + /* This is our link to the per-transport transmitted list. */ + struct list_head transmitted_list; + /* List in specific stream outq */ + struct list_head stream_list; + }; /* This field is used by chunks that hold fragmented data. * For the first fragment this is the list that holds the rest of @@ -1019,6 +1022,9 @@ struct sctp_outq { /* Data pending that has never been transmitted. */ struct list_head out_chunk_list; + /* Stream scheduler being used */ + struct sctp_sched_ops *sched; + unsigned int out_qlen; /* Total length of queued data chunks. */ /* Error of send failed, may used in SCTP_SEND_FAILED event. */ @@ -1325,6 +1331,7 @@ struct sctp_inithdr_host { struct sctp_stream_out_ext { __u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1]; __u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1]; + struct list_head outq; /* chunks enqueued by this stream */ }; struct sctp_stream_out { @@ -1342,6 +1349,8 @@ struct sctp_stream { struct sctp_stream_in *in; __u16 outcnt; __u16 incnt; + /* Current stream being sent, if any */ + struct sctp_stream_out *out_curr; }; #define SCTP_STREAM_CLOSED 0x00 diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 6217ff8500a1..4487e7625ddb 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -1088,4 +1088,10 @@ struct sctp_add_streams { uint16_t sas_outstrms; }; +/* SCTP Stream schedulers */ +enum sctp_sched_type { + SCTP_SS_FCFS, + SCTP_SS_MAX = SCTP_SS_FCFS +}; + #endif /* _UAPI_SCTP_H */ diff --git a/net/sctp/Makefile b/net/sctp/Makefile index 70f1b570bab9..0f6e6d1d69fd 100644 --- a/net/sctp/Makefile +++ b/net/sctp/Makefile @@ -12,7 +12,7 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \ inqueue.o outqueue.o ulpqueue.o \ tsnmap.o bind_addr.o socket.o primitive.o \ output.o input.o debug.o stream.o auth.o \ - offload.o + offload.o stream_sched.o sctp_probe-y := probe.o diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 746b07b7937d..4db012aa25f7 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -50,6 +50,7 @@ #include #include +#include /* Declare internal functions here. */ static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn); @@ -72,32 +73,38 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp); /* Add data to the front of the queue. */ static inline void sctp_outq_head_data(struct sctp_outq *q, - struct sctp_chunk *ch) + struct sctp_chunk *ch) { + struct sctp_stream_out_ext *oute; + __u16 stream; + list_add(&ch->list, &q->out_chunk_list); q->out_qlen += ch->skb->len; + + stream = sctp_chunk_stream_no(ch); + oute = q->asoc->stream.out[stream].ext; + list_add(&ch->stream_list, &oute->outq); } /* Take data from the front of the queue. */ static inline struct sctp_chunk *sctp_outq_dequeue_data(struct sctp_outq *q) { - struct sctp_chunk *ch = NULL; - - if (!list_empty(&q->out_chunk_list)) { - struct list_head *entry = q->out_chunk_list.next; - - ch = list_entry(entry, struct sctp_chunk, list); - list_del_init(entry); - q->out_qlen -= ch->skb->len; - } - return ch; + return q->sched->dequeue(q); } + /* Add data chunk to the end of the queue. */ static inline void sctp_outq_tail_data(struct sctp_outq *q, struct sctp_chunk *ch) { + struct sctp_stream_out_ext *oute; + __u16 stream; + list_add_tail(&ch->list, &q->out_chunk_list); q->out_qlen += ch->skb->len; + + stream = sctp_chunk_stream_no(ch); + oute = q->asoc->stream.out[stream].ext; + list_add_tail(&ch->stream_list, &oute->outq); } /* @@ -207,6 +214,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q) INIT_LIST_HEAD(&q->retransmit); INIT_LIST_HEAD(&q->sacked); INIT_LIST_HEAD(&q->abandoned); + sctp_sched_set_sched(asoc, SCTP_SS_FCFS); } /* Free the outqueue structure and any related pending chunks. @@ -258,6 +266,7 @@ static void __sctp_outq_teardown(struct sctp_outq *q) /* Throw away any leftover data chunks. */ while ((chunk = sctp_outq_dequeue_data(q)) != NULL) { + sctp_sched_dequeue_done(q, chunk); /* Mark as send failure. */ sctp_chunk_fail(chunk, q->error); @@ -391,13 +400,14 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc, struct sctp_outq *q = &asoc->outqueue; struct sctp_chunk *chk, *temp; + q->sched->unsched_all(&asoc->stream); + list_for_each_entry_safe(chk, temp, &q->out_chunk_list, list) { if (!SCTP_PR_PRIO_ENABLED(chk->sinfo.sinfo_flags) || chk->sinfo.sinfo_timetolive <= sinfo->sinfo_timetolive) continue; - list_del_init(&chk->list); - q->out_qlen -= chk->skb->len; + sctp_sched_dequeue_common(q, chk); asoc->sent_cnt_removable--; asoc->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; if (chk->sinfo.sinfo_stream < asoc->stream.outcnt) { @@ -415,6 +425,8 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc, break; } + q->sched->sched_all(&asoc->stream); + return msg_len; } @@ -1033,22 +1045,9 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) while ((chunk = sctp_outq_dequeue_data(q)) != NULL) { __u32 sid = ntohs(chunk->subh.data_hdr->stream); - /* RFC 2960 6.5 Every DATA chunk MUST carry a valid - * stream identifier. - */ - if (chunk->sinfo.sinfo_stream >= asoc->stream.outcnt) { - - /* Mark as failed send. */ - sctp_chunk_fail(chunk, SCTP_ERROR_INV_STRM); - if (asoc->peer.prsctp_capable && - SCTP_PR_PRIO_ENABLED(chunk->sinfo.sinfo_flags)) - asoc->sent_cnt_removable--; - sctp_chunk_free(chunk); - continue; - } - /* Has this chunk expired? */ if (sctp_chunk_abandoned(chunk)) { + sctp_sched_dequeue_done(q, chunk); sctp_chunk_fail(chunk, 0); sctp_chunk_free(chunk); continue; @@ -1070,6 +1069,7 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) new_transport = asoc->peer.active_path; if (new_transport->state == SCTP_UNCONFIRMED) { WARN_ONCE(1, "Attempt to send packet on unconfirmed path."); + sctp_sched_dequeue_done(q, chunk); sctp_chunk_fail(chunk, 0); sctp_chunk_free(chunk); continue; @@ -1133,6 +1133,11 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) else asoc->stats.oodchunks++; + /* Only now it's safe to consider this + * chunk as sent, sched-wise. + */ + sctp_sched_dequeue_done(q, chunk); + break; default: diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index e6a2974e020e..402bfbb888cd 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -50,6 +50,7 @@ #include #include #include +#include static int sctp_cmd_interpreter(enum sctp_event event_type, union sctp_subtype subtype, @@ -1089,6 +1090,8 @@ static void sctp_cmd_send_msg(struct sctp_association *asoc, list_for_each_entry(chunk, &msg->chunks, frag_list) sctp_outq_tail(&asoc->outqueue, chunk, gfp); + + asoc->outqueue.sched->enqueue(&asoc->outqueue, msg); } diff --git a/net/sctp/stream.c b/net/sctp/stream.c index 055ca25bbc91..5ea33a2c453b 100644 --- a/net/sctp/stream.c +++ b/net/sctp/stream.c @@ -32,8 +32,61 @@ * Xin Long */ +#include #include #include +#include + +/* Migrates chunks from stream queues to new stream queues if needed, + * but not across associations. Also, removes those chunks to streams + * higher than the new max. + */ +static void sctp_stream_outq_migrate(struct sctp_stream *stream, + struct sctp_stream *new, __u16 outcnt) +{ + struct sctp_association *asoc; + struct sctp_chunk *ch, *temp; + struct sctp_outq *outq; + int i; + + asoc = container_of(stream, struct sctp_association, stream); + outq = &asoc->outqueue; + + list_for_each_entry_safe(ch, temp, &outq->out_chunk_list, list) { + __u16 sid = sctp_chunk_stream_no(ch); + + if (sid < outcnt) + continue; + + sctp_sched_dequeue_common(outq, ch); + /* No need to call dequeue_done here because + * the chunks are not scheduled by now. + */ + + /* Mark as failed send. */ + sctp_chunk_fail(ch, SCTP_ERROR_INV_STRM); + if (asoc->peer.prsctp_capable && + SCTP_PR_PRIO_ENABLED(ch->sinfo.sinfo_flags)) + asoc->sent_cnt_removable--; + + sctp_chunk_free(ch); + } + + if (new) { + /* Here we actually move the old ext stuff into the new + * buffer, because we want to keep it. Then + * sctp_stream_update will swap ->out pointers. + */ + for (i = 0; i < outcnt; i++) { + kfree(new->out[i].ext); + new->out[i].ext = stream->out[i].ext; + stream->out[i].ext = NULL; + } + } + + for (i = outcnt; i < stream->outcnt; i++) + kfree(stream->out[i].ext); +} static int sctp_stream_alloc_out(struct sctp_stream *stream, __u16 outcnt, gfp_t gfp) @@ -87,7 +140,8 @@ static int sctp_stream_alloc_in(struct sctp_stream *stream, __u16 incnt, int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, gfp_t gfp) { - int i; + struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); + int i, ret = 0; gfp |= __GFP_NOWARN; @@ -97,6 +151,11 @@ int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, if (outcnt == stream->outcnt) goto in; + /* Filter out chunks queued on streams that won't exist anymore */ + sched->unsched_all(stream); + sctp_stream_outq_migrate(stream, NULL, outcnt); + sched->sched_all(stream); + i = sctp_stream_alloc_out(stream, outcnt, gfp); if (i) return i; @@ -105,20 +164,27 @@ int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, for (i = 0; i < stream->outcnt; i++) stream->out[i].state = SCTP_STREAM_OPEN; + sched->init(stream); + in: if (!incnt) - return 0; + goto out; i = sctp_stream_alloc_in(stream, incnt, gfp); if (i) { - kfree(stream->out); - stream->out = NULL; - return -ENOMEM; + ret = -ENOMEM; + goto free; } stream->incnt = incnt; + goto out; - return 0; +free: + sched->free(stream); + kfree(stream->out); + stream->out = NULL; +out: + return ret; } int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) @@ -130,13 +196,15 @@ int sctp_stream_init_ext(struct sctp_stream *stream, __u16 sid) return -ENOMEM; stream->out[sid].ext = soute; - return 0; + return sctp_sched_init_sid(stream, sid, GFP_KERNEL); } void sctp_stream_free(struct sctp_stream *stream) { + struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); int i; + sched->free(stream); for (i = 0; i < stream->outcnt; i++) kfree(stream->out[i].ext); kfree(stream->out); @@ -156,6 +224,10 @@ void sctp_stream_clear(struct sctp_stream *stream) void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new) { + struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); + + sched->unsched_all(stream); + sctp_stream_outq_migrate(stream, new, new->outcnt); sctp_stream_free(stream); stream->out = new->out; @@ -163,6 +235,8 @@ void sctp_stream_update(struct sctp_stream *stream, struct sctp_stream *new) stream->outcnt = new->outcnt; stream->incnt = new->incnt; + sched->sched_all(stream); + new->out = NULL; new->in = NULL; } diff --git a/net/sctp/stream_sched.c b/net/sctp/stream_sched.c new file mode 100644 index 000000000000..40a9a9de2b98 --- /dev/null +++ b/net/sctp/stream_sched.c @@ -0,0 +1,270 @@ +/* SCTP kernel implementation + * (C) Copyright Red Hat Inc. 2017 + * + * This file is part of the SCTP kernel implementation + * + * These functions manipulate sctp stream queue/scheduling. + * + * This SCTP implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This SCTP implementation 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. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, see + * . + * + * Please send any bug reports or fixes you make to the + * email addresched(es): + * lksctp developers + * + * Written or modified by: + * Marcelo Ricardo Leitner + */ + +#include +#include +#include +#include + +/* First Come First Serve (a.k.a. FIFO) + * RFC DRAFT ndata Section 3.1 + */ +static int sctp_sched_fcfs_set(struct sctp_stream *stream, __u16 sid, + __u16 value, gfp_t gfp) +{ + return 0; +} + +static int sctp_sched_fcfs_get(struct sctp_stream *stream, __u16 sid, + __u16 *value) +{ + *value = 0; + return 0; +} + +static int sctp_sched_fcfs_init(struct sctp_stream *stream) +{ + return 0; +} + +static int sctp_sched_fcfs_init_sid(struct sctp_stream *stream, __u16 sid, + gfp_t gfp) +{ + return 0; +} + +static void sctp_sched_fcfs_free(struct sctp_stream *stream) +{ +} + +static void sctp_sched_fcfs_enqueue(struct sctp_outq *q, + struct sctp_datamsg *msg) +{ +} + +static struct sctp_chunk *sctp_sched_fcfs_dequeue(struct sctp_outq *q) +{ + struct sctp_stream *stream = &q->asoc->stream; + struct sctp_chunk *ch = NULL; + struct list_head *entry; + + if (list_empty(&q->out_chunk_list)) + goto out; + + if (stream->out_curr) { + ch = list_entry(stream->out_curr->ext->outq.next, + struct sctp_chunk, stream_list); + } else { + entry = q->out_chunk_list.next; + ch = list_entry(entry, struct sctp_chunk, list); + } + + sctp_sched_dequeue_common(q, ch); + +out: + return ch; +} + +static void sctp_sched_fcfs_dequeue_done(struct sctp_outq *q, + struct sctp_chunk *chunk) +{ +} + +static void sctp_sched_fcfs_sched_all(struct sctp_stream *stream) +{ +} + +static void sctp_sched_fcfs_unsched_all(struct sctp_stream *stream) +{ +} + +static struct sctp_sched_ops sctp_sched_fcfs = { + .set = sctp_sched_fcfs_set, + .get = sctp_sched_fcfs_get, + .init = sctp_sched_fcfs_init, + .init_sid = sctp_sched_fcfs_init_sid, + .free = sctp_sched_fcfs_free, + .enqueue = sctp_sched_fcfs_enqueue, + .dequeue = sctp_sched_fcfs_dequeue, + .dequeue_done = sctp_sched_fcfs_dequeue_done, + .sched_all = sctp_sched_fcfs_sched_all, + .unsched_all = sctp_sched_fcfs_unsched_all, +}; + +/* API to other parts of the stack */ + +struct sctp_sched_ops *sctp_sched_ops[] = { + &sctp_sched_fcfs, +}; + +int sctp_sched_set_sched(struct sctp_association *asoc, + enum sctp_sched_type sched) +{ + struct sctp_sched_ops *n = sctp_sched_ops[sched]; + struct sctp_sched_ops *old = asoc->outqueue.sched; + struct sctp_datamsg *msg = NULL; + struct sctp_chunk *ch; + int i, ret = 0; + + if (old == n) + return ret; + + if (sched > SCTP_SS_MAX) + return -EINVAL; + + if (old) { + old->free(&asoc->stream); + + /* Give the next scheduler a clean slate. */ + for (i = 0; i < asoc->stream.outcnt; i++) { + void *p = asoc->stream.out[i].ext; + + if (!p) + continue; + + p += offsetofend(struct sctp_stream_out_ext, outq); + memset(p, 0, sizeof(struct sctp_stream_out_ext) - + offsetofend(struct sctp_stream_out_ext, outq)); + } + } + + asoc->outqueue.sched = n; + n->init(&asoc->stream); + for (i = 0; i < asoc->stream.outcnt; i++) { + if (!asoc->stream.out[i].ext) + continue; + + ret = n->init_sid(&asoc->stream, i, GFP_KERNEL); + if (ret) + goto err; + } + + /* We have to requeue all chunks already queued. */ + list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) { + if (ch->msg == msg) + continue; + msg = ch->msg; + n->enqueue(&asoc->outqueue, msg); + } + + return ret; + +err: + n->free(&asoc->stream); + asoc->outqueue.sched = &sctp_sched_fcfs; /* Always safe */ + + return ret; +} + +int sctp_sched_get_sched(struct sctp_association *asoc) +{ + int i; + + for (i = 0; i <= SCTP_SS_MAX; i++) + if (asoc->outqueue.sched == sctp_sched_ops[i]) + return i; + + return 0; +} + +int sctp_sched_set_value(struct sctp_association *asoc, __u16 sid, + __u16 value, gfp_t gfp) +{ + if (sid >= asoc->stream.outcnt) + return -EINVAL; + + if (!asoc->stream.out[sid].ext) { + int ret; + + ret = sctp_stream_init_ext(&asoc->stream, sid); + if (ret) + return ret; + } + + return asoc->outqueue.sched->set(&asoc->stream, sid, value, gfp); +} + +int sctp_sched_get_value(struct sctp_association *asoc, __u16 sid, + __u16 *value) +{ + if (sid >= asoc->stream.outcnt) + return -EINVAL; + + if (!asoc->stream.out[sid].ext) + return 0; + + return asoc->outqueue.sched->get(&asoc->stream, sid, value); +} + +void sctp_sched_dequeue_done(struct sctp_outq *q, struct sctp_chunk *ch) +{ + if (!list_is_last(&ch->frag_list, &ch->msg->chunks)) { + struct sctp_stream_out *sout; + __u16 sid; + + /* datamsg is not finish, so save it as current one, + * in case application switch scheduler or a higher + * priority stream comes in. + */ + sid = sctp_chunk_stream_no(ch); + sout = &q->asoc->stream.out[sid]; + q->asoc->stream.out_curr = sout; + return; + } + + q->asoc->stream.out_curr = NULL; + q->sched->dequeue_done(q, ch); +} + +/* Auxiliary functions for the schedulers */ +void sctp_sched_dequeue_common(struct sctp_outq *q, struct sctp_chunk *ch) +{ + list_del_init(&ch->list); + list_del_init(&ch->stream_list); + q->out_qlen -= ch->skb->len; +} + +int sctp_sched_init_sid(struct sctp_stream *stream, __u16 sid, gfp_t gfp) +{ + struct sctp_sched_ops *sched = sctp_sched_ops_from_stream(stream); + + INIT_LIST_HEAD(&stream->out[sid].ext->outq); + return sched->init_sid(stream, sid, gfp); +} + +struct sctp_sched_ops *sctp_sched_ops_from_stream(struct sctp_stream *stream) +{ + struct sctp_association *asoc; + + asoc = container_of(stream, struct sctp_association, stream); + + return asoc->outqueue.sched; +} -- cgit v1.2.3-59-g8ed1b From 13aa8770fe42d246c6f3a8eb814b85bccb428011 Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Tue, 3 Oct 2017 19:20:14 -0300 Subject: sctp: add sockopt to get/set stream scheduler As defined per RFC Draft ndata Section 4.3.2, named as SCTP_STREAM_SCHEDULER. See-also: https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-13 Tested-by: Xin Long Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/uapi/linux/sctp.h | 1 + net/sctp/socket.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 4487e7625ddb..0050f10087d2 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -122,6 +122,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_RESET_ASSOC 120 #define SCTP_ADD_STREAMS 121 #define SCTP_SOCKOPT_PEELOFF_FLAGS 122 +#define SCTP_STREAM_SCHEDULER 123 /* PR-SCTP policies */ #define SCTP_PR_SCTP_NONE 0x0000 diff --git a/net/sctp/socket.c b/net/sctp/socket.c index d207734326b0..ae35dbf2810f 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -79,6 +79,7 @@ #include #include #include +#include /* Forward declarations for internal helper functions. */ static int sctp_writeable(struct sock *sk); @@ -3914,6 +3915,36 @@ out: return retval; } +static int sctp_setsockopt_scheduler(struct sock *sk, + char __user *optval, + unsigned int optlen) +{ + struct sctp_association *asoc; + struct sctp_assoc_value params; + int retval = -EINVAL; + + if (optlen < sizeof(params)) + goto out; + + optlen = sizeof(params); + if (copy_from_user(¶ms, optval, optlen)) { + retval = -EFAULT; + goto out; + } + + if (params.assoc_value > SCTP_SS_MAX) + goto out; + + asoc = sctp_id2assoc(sk, params.assoc_id); + if (!asoc) + goto out; + + retval = sctp_sched_set_sched(asoc, params.assoc_value); + +out: + return retval; +} + /* API 6.2 setsockopt(), getsockopt() * * Applications use setsockopt() and getsockopt() to set or retrieve @@ -4095,6 +4126,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, case SCTP_ADD_STREAMS: retval = sctp_setsockopt_add_streams(sk, optval, optlen); break; + case SCTP_STREAM_SCHEDULER: + retval = sctp_setsockopt_scheduler(sk, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; @@ -6793,6 +6827,43 @@ out: return retval; } +static int sctp_getsockopt_scheduler(struct sock *sk, int len, + char __user *optval, + int __user *optlen) +{ + struct sctp_assoc_value params; + struct sctp_association *asoc; + int retval = -EFAULT; + + if (len < sizeof(params)) { + retval = -EINVAL; + goto out; + } + + len = sizeof(params); + if (copy_from_user(¶ms, optval, len)) + goto out; + + asoc = sctp_id2assoc(sk, params.assoc_id); + if (!asoc) { + retval = -EINVAL; + goto out; + } + + params.assoc_value = sctp_sched_get_sched(asoc); + + if (put_user(len, optlen)) + goto out; + + if (copy_to_user(optval, ¶ms, len)) + goto out; + + retval = 0; + +out: + return retval; +} + static int sctp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { @@ -6975,6 +7046,10 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, retval = sctp_getsockopt_enable_strreset(sk, len, optval, optlen); break; + case SCTP_STREAM_SCHEDULER: + retval = sctp_getsockopt_scheduler(sk, len, optval, + optlen); + break; default: retval = -ENOPROTOOPT; break; -- cgit v1.2.3-59-g8ed1b From 0ccdf3c7fdeda511b10def19505178a9d2d3fccd Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Tue, 3 Oct 2017 19:20:15 -0300 Subject: sctp: add sockopt to get/set stream scheduler parameters As defined per RFC Draft ndata Section 4.3.3, named as SCTP_STREAM_SCHEDULER_VALUE. See-also: https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-13 Tested-by: Xin Long Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/uapi/linux/sctp.h | 7 +++++ net/sctp/socket.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 0050f10087d2..00ac417d2c4f 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -123,6 +123,7 @@ typedef __s32 sctp_assoc_t; #define SCTP_ADD_STREAMS 121 #define SCTP_SOCKOPT_PEELOFF_FLAGS 122 #define SCTP_STREAM_SCHEDULER 123 +#define SCTP_STREAM_SCHEDULER_VALUE 124 /* PR-SCTP policies */ #define SCTP_PR_SCTP_NONE 0x0000 @@ -815,6 +816,12 @@ struct sctp_assoc_value { uint32_t assoc_value; }; +struct sctp_stream_value { + sctp_assoc_t assoc_id; + uint16_t stream_id; + uint16_t stream_value; +}; + /* * 7.2.2 Peer Address Information * diff --git a/net/sctp/socket.c b/net/sctp/socket.c index ae35dbf2810f..88c28421ec15 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3945,6 +3945,34 @@ out: return retval; } +static int sctp_setsockopt_scheduler_value(struct sock *sk, + char __user *optval, + unsigned int optlen) +{ + struct sctp_association *asoc; + struct sctp_stream_value params; + int retval = -EINVAL; + + if (optlen < sizeof(params)) + goto out; + + optlen = sizeof(params); + if (copy_from_user(¶ms, optval, optlen)) { + retval = -EFAULT; + goto out; + } + + asoc = sctp_id2assoc(sk, params.assoc_id); + if (!asoc) + goto out; + + retval = sctp_sched_set_value(asoc, params.stream_id, + params.stream_value, GFP_KERNEL); + +out: + return retval; +} + /* API 6.2 setsockopt(), getsockopt() * * Applications use setsockopt() and getsockopt() to set or retrieve @@ -4129,6 +4157,9 @@ static int sctp_setsockopt(struct sock *sk, int level, int optname, case SCTP_STREAM_SCHEDULER: retval = sctp_setsockopt_scheduler(sk, optval, optlen); break; + case SCTP_STREAM_SCHEDULER_VALUE: + retval = sctp_setsockopt_scheduler_value(sk, optval, optlen); + break; default: retval = -ENOPROTOOPT; break; @@ -6864,6 +6895,48 @@ out: return retval; } +static int sctp_getsockopt_scheduler_value(struct sock *sk, int len, + char __user *optval, + int __user *optlen) +{ + struct sctp_stream_value params; + struct sctp_association *asoc; + int retval = -EFAULT; + + if (len < sizeof(params)) { + retval = -EINVAL; + goto out; + } + + len = sizeof(params); + if (copy_from_user(¶ms, optval, len)) + goto out; + + asoc = sctp_id2assoc(sk, params.assoc_id); + if (!asoc) { + retval = -EINVAL; + goto out; + } + + retval = sctp_sched_get_value(asoc, params.stream_id, + ¶ms.stream_value); + if (retval) + goto out; + + if (put_user(len, optlen)) { + retval = -EFAULT; + goto out; + } + + if (copy_to_user(optval, ¶ms, len)) { + retval = -EFAULT; + goto out; + } + +out: + return retval; +} + static int sctp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen) { @@ -7050,6 +7123,10 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname, retval = sctp_getsockopt_scheduler(sk, len, optval, optlen); break; + case SCTP_STREAM_SCHEDULER_VALUE: + retval = sctp_getsockopt_scheduler_value(sk, len, optval, + optlen); + break; default: retval = -ENOPROTOOPT; break; -- cgit v1.2.3-59-g8ed1b From 637784ade221a3c8a7ecd0f583eddd95d6276b9a Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Tue, 3 Oct 2017 19:20:16 -0300 Subject: sctp: introduce priority based stream scheduler This patch introduces RFC Draft ndata section 3.4 Priority Based Scheduler (SCTP_SS_PRIO). It works by having a struct sctp_stream_priority for each priority configured. This struct is then enlisted on a queue ordered per priority if, and only if, there is a stream with data queued, so that dequeueing is very straightforward: either finish current datamsg or simply dequeue from the highest priority queued, which is the next stream pointed, and that's it. If there are multiple streams assigned with the same priority and with data queued, it will do round robin amongst them while respecting datamsgs boundaries (when not using idata chunks), to be reasonably fair. We intentionally don't maintain a list of priorities nor a list of all streams with the same priority to save memory. The first would mean at least 2 other pointers per priority (which, for 1000 priorities, that can mean 16kB) and the second would also mean 2 other pointers but per stream. As SCTP supports up to 65535 streams on a given asoc, that's 1MB. This impacts when giving a priority to some stream, as we have to find out if the new priority is already being used and if we can free the old one, and also when tearing down. The new fields in struct sctp_stream_out_ext and sctp_stream are added under a union because that memory is to be shared with other schedulers. See-also: https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-13 Tested-by: Xin Long Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 24 +++ include/uapi/linux/sctp.h | 3 +- net/sctp/Makefile | 2 +- net/sctp/stream_sched.c | 3 + net/sctp/stream_sched_prio.c | 347 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 377 insertions(+), 2 deletions(-) create mode 100644 net/sctp/stream_sched_prio.c (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 3c22a30fd71b..40eb8d66a37c 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1328,10 +1328,27 @@ struct sctp_inithdr_host { __u32 initial_tsn; }; +struct sctp_stream_priorities { + /* List of priorities scheduled */ + struct list_head prio_sched; + /* List of streams scheduled */ + struct list_head active; + /* The next stream stream in line */ + struct sctp_stream_out_ext *next; + __u16 prio; +}; + struct sctp_stream_out_ext { __u64 abandoned_unsent[SCTP_PR_INDEX(MAX) + 1]; __u64 abandoned_sent[SCTP_PR_INDEX(MAX) + 1]; struct list_head outq; /* chunks enqueued by this stream */ + union { + struct { + /* Scheduled streams list */ + struct list_head prio_list; + struct sctp_stream_priorities *prio_head; + }; + }; }; struct sctp_stream_out { @@ -1351,6 +1368,13 @@ struct sctp_stream { __u16 incnt; /* Current stream being sent, if any */ struct sctp_stream_out *out_curr; + union { + /* Fields used by priority scheduler */ + struct { + /* List of priorities scheduled */ + struct list_head prio_list; + }; + }; }; #define SCTP_STREAM_CLOSED 0x00 diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 00ac417d2c4f..850fa8b29d7e 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -1099,7 +1099,8 @@ struct sctp_add_streams { /* SCTP Stream schedulers */ enum sctp_sched_type { SCTP_SS_FCFS, - SCTP_SS_MAX = SCTP_SS_FCFS + SCTP_SS_PRIO, + SCTP_SS_MAX = SCTP_SS_PRIO }; #endif /* _UAPI_SCTP_H */ diff --git a/net/sctp/Makefile b/net/sctp/Makefile index 0f6e6d1d69fd..647c9cfd4e95 100644 --- a/net/sctp/Makefile +++ b/net/sctp/Makefile @@ -12,7 +12,7 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \ inqueue.o outqueue.o ulpqueue.o \ tsnmap.o bind_addr.o socket.o primitive.o \ output.o input.o debug.o stream.o auth.o \ - offload.o stream_sched.o + offload.o stream_sched.o stream_sched_prio.o sctp_probe-y := probe.o diff --git a/net/sctp/stream_sched.c b/net/sctp/stream_sched.c index 40a9a9de2b98..115ddb765169 100644 --- a/net/sctp/stream_sched.c +++ b/net/sctp/stream_sched.c @@ -121,8 +121,11 @@ static struct sctp_sched_ops sctp_sched_fcfs = { /* API to other parts of the stack */ +extern struct sctp_sched_ops sctp_sched_prio; + struct sctp_sched_ops *sctp_sched_ops[] = { &sctp_sched_fcfs, + &sctp_sched_prio, }; int sctp_sched_set_sched(struct sctp_association *asoc, diff --git a/net/sctp/stream_sched_prio.c b/net/sctp/stream_sched_prio.c new file mode 100644 index 000000000000..384dbf3c8760 --- /dev/null +++ b/net/sctp/stream_sched_prio.c @@ -0,0 +1,347 @@ +/* SCTP kernel implementation + * (C) Copyright Red Hat Inc. 2017 + * + * This file is part of the SCTP kernel implementation + * + * These functions manipulate sctp stream queue/scheduling. + * + * This SCTP implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This SCTP implementation 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. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, see + * . + * + * Please send any bug reports or fixes you make to the + * email addresched(es): + * lksctp developers + * + * Written or modified by: + * Marcelo Ricardo Leitner + */ + +#include +#include +#include +#include + +/* Priority handling + * RFC DRAFT ndata section 3.4 + */ + +static void sctp_sched_prio_unsched_all(struct sctp_stream *stream); + +static struct sctp_stream_priorities *sctp_sched_prio_new_head( + struct sctp_stream *stream, int prio, gfp_t gfp) +{ + struct sctp_stream_priorities *p; + + p = kmalloc(sizeof(*p), gfp); + if (!p) + return NULL; + + INIT_LIST_HEAD(&p->prio_sched); + INIT_LIST_HEAD(&p->active); + p->next = NULL; + p->prio = prio; + + return p; +} + +static struct sctp_stream_priorities *sctp_sched_prio_get_head( + struct sctp_stream *stream, int prio, gfp_t gfp) +{ + struct sctp_stream_priorities *p; + int i; + + /* Look into scheduled priorities first, as they are sorted and + * we can find it fast IF it's scheduled. + */ + list_for_each_entry(p, &stream->prio_list, prio_sched) { + if (p->prio == prio) + return p; + if (p->prio > prio) + break; + } + + /* No luck. So we search on all streams now. */ + for (i = 0; i < stream->outcnt; i++) { + if (!stream->out[i].ext) + continue; + + p = stream->out[i].ext->prio_head; + if (!p) + /* Means all other streams won't be initialized + * as well. + */ + break; + if (p->prio == prio) + return p; + } + + /* If not even there, allocate a new one. */ + return sctp_sched_prio_new_head(stream, prio, gfp); +} + +static void sctp_sched_prio_next_stream(struct sctp_stream_priorities *p) +{ + struct list_head *pos; + + pos = p->next->prio_list.next; + if (pos == &p->active) + pos = pos->next; + p->next = list_entry(pos, struct sctp_stream_out_ext, prio_list); +} + +static bool sctp_sched_prio_unsched(struct sctp_stream_out_ext *soute) +{ + bool scheduled = false; + + if (!list_empty(&soute->prio_list)) { + struct sctp_stream_priorities *prio_head = soute->prio_head; + + /* Scheduled */ + scheduled = true; + + if (prio_head->next == soute) + /* Try to move to the next stream */ + sctp_sched_prio_next_stream(prio_head); + + list_del_init(&soute->prio_list); + + /* Also unsched the priority if this was the last stream */ + if (list_empty(&prio_head->active)) { + list_del_init(&prio_head->prio_sched); + /* If there is no stream left, clear next */ + prio_head->next = NULL; + } + } + + return scheduled; +} + +static void sctp_sched_prio_sched(struct sctp_stream *stream, + struct sctp_stream_out_ext *soute) +{ + struct sctp_stream_priorities *prio, *prio_head; + + prio_head = soute->prio_head; + + /* Nothing to do if already scheduled */ + if (!list_empty(&soute->prio_list)) + return; + + /* Schedule the stream. If there is a next, we schedule the new + * one before it, so it's the last in round robin order. + * If there isn't, we also have to schedule the priority. + */ + if (prio_head->next) { + list_add(&soute->prio_list, prio_head->next->prio_list.prev); + return; + } + + list_add(&soute->prio_list, &prio_head->active); + prio_head->next = soute; + + list_for_each_entry(prio, &stream->prio_list, prio_sched) { + if (prio->prio > prio_head->prio) { + list_add(&prio_head->prio_sched, prio->prio_sched.prev); + return; + } + } + + list_add_tail(&prio_head->prio_sched, &stream->prio_list); +} + +static int sctp_sched_prio_set(struct sctp_stream *stream, __u16 sid, + __u16 prio, gfp_t gfp) +{ + struct sctp_stream_out *sout = &stream->out[sid]; + struct sctp_stream_out_ext *soute = sout->ext; + struct sctp_stream_priorities *prio_head, *old; + bool reschedule = false; + int i; + + prio_head = sctp_sched_prio_get_head(stream, prio, gfp); + if (!prio_head) + return -ENOMEM; + + reschedule = sctp_sched_prio_unsched(soute); + old = soute->prio_head; + soute->prio_head = prio_head; + if (reschedule) + sctp_sched_prio_sched(stream, soute); + + if (!old) + /* Happens when we set the priority for the first time */ + return 0; + + for (i = 0; i < stream->outcnt; i++) { + soute = stream->out[i].ext; + if (soute && soute->prio_head == old) + /* It's still in use, nothing else to do here. */ + return 0; + } + + /* No hits, we are good to free it. */ + kfree(old); + + return 0; +} + +static int sctp_sched_prio_get(struct sctp_stream *stream, __u16 sid, + __u16 *value) +{ + *value = stream->out[sid].ext->prio_head->prio; + return 0; +} + +static int sctp_sched_prio_init(struct sctp_stream *stream) +{ + INIT_LIST_HEAD(&stream->prio_list); + + return 0; +} + +static int sctp_sched_prio_init_sid(struct sctp_stream *stream, __u16 sid, + gfp_t gfp) +{ + INIT_LIST_HEAD(&stream->out[sid].ext->prio_list); + return sctp_sched_prio_set(stream, sid, 0, gfp); +} + +static void sctp_sched_prio_free(struct sctp_stream *stream) +{ + struct sctp_stream_priorities *prio, *n; + LIST_HEAD(list); + int i; + + /* As we don't keep a list of priorities, to avoid multiple + * frees we have to do it in 3 steps: + * 1. unsched everyone, so the lists are free to use in 2. + * 2. build the list of the priorities + * 3. free the list + */ + sctp_sched_prio_unsched_all(stream); + for (i = 0; i < stream->outcnt; i++) { + if (!stream->out[i].ext) + continue; + prio = stream->out[i].ext->prio_head; + if (prio && list_empty(&prio->prio_sched)) + list_add(&prio->prio_sched, &list); + } + list_for_each_entry_safe(prio, n, &list, prio_sched) { + list_del_init(&prio->prio_sched); + kfree(prio); + } +} + +static void sctp_sched_prio_enqueue(struct sctp_outq *q, + struct sctp_datamsg *msg) +{ + struct sctp_stream *stream; + struct sctp_chunk *ch; + __u16 sid; + + ch = list_first_entry(&msg->chunks, struct sctp_chunk, frag_list); + sid = sctp_chunk_stream_no(ch); + stream = &q->asoc->stream; + sctp_sched_prio_sched(stream, stream->out[sid].ext); +} + +static struct sctp_chunk *sctp_sched_prio_dequeue(struct sctp_outq *q) +{ + struct sctp_stream *stream = &q->asoc->stream; + struct sctp_stream_priorities *prio; + struct sctp_stream_out_ext *soute; + struct sctp_chunk *ch = NULL; + + /* Bail out quickly if queue is empty */ + if (list_empty(&q->out_chunk_list)) + goto out; + + /* Find which chunk is next. It's easy, it's either the current + * one or the first chunk on the next active stream. + */ + if (stream->out_curr) { + soute = stream->out_curr->ext; + } else { + prio = list_entry(stream->prio_list.next, + struct sctp_stream_priorities, prio_sched); + soute = prio->next; + } + ch = list_entry(soute->outq.next, struct sctp_chunk, stream_list); + sctp_sched_dequeue_common(q, ch); + +out: + return ch; +} + +static void sctp_sched_prio_dequeue_done(struct sctp_outq *q, + struct sctp_chunk *ch) +{ + struct sctp_stream_priorities *prio; + struct sctp_stream_out_ext *soute; + __u16 sid; + + /* Last chunk on that msg, move to the next stream on + * this priority. + */ + sid = sctp_chunk_stream_no(ch); + soute = q->asoc->stream.out[sid].ext; + prio = soute->prio_head; + + sctp_sched_prio_next_stream(prio); + + if (list_empty(&soute->outq)) + sctp_sched_prio_unsched(soute); +} + +static void sctp_sched_prio_sched_all(struct sctp_stream *stream) +{ + struct sctp_association *asoc; + struct sctp_stream_out *sout; + struct sctp_chunk *ch; + + asoc = container_of(stream, struct sctp_association, stream); + list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) { + __u16 sid; + + sid = sctp_chunk_stream_no(ch); + sout = &stream->out[sid]; + if (sout->ext) + sctp_sched_prio_sched(stream, sout->ext); + } +} + +static void sctp_sched_prio_unsched_all(struct sctp_stream *stream) +{ + struct sctp_stream_priorities *p, *tmp; + struct sctp_stream_out_ext *soute, *souttmp; + + list_for_each_entry_safe(p, tmp, &stream->prio_list, prio_sched) + list_for_each_entry_safe(soute, souttmp, &p->active, prio_list) + sctp_sched_prio_unsched(soute); +} + +struct sctp_sched_ops sctp_sched_prio = { + .set = sctp_sched_prio_set, + .get = sctp_sched_prio_get, + .init = sctp_sched_prio_init, + .init_sid = sctp_sched_prio_init_sid, + .free = sctp_sched_prio_free, + .enqueue = sctp_sched_prio_enqueue, + .dequeue = sctp_sched_prio_dequeue, + .dequeue_done = sctp_sched_prio_dequeue_done, + .sched_all = sctp_sched_prio_sched_all, + .unsched_all = sctp_sched_prio_unsched_all, +}; -- cgit v1.2.3-59-g8ed1b From ac1ed8b82cd60ba8e7d84103ac1414b8c577c485 Mon Sep 17 00:00:00 2001 From: Marcelo Ricardo Leitner Date: Tue, 3 Oct 2017 19:20:17 -0300 Subject: sctp: introduce round robin stream scheduler This patch introduces RFC Draft ndata section 3.2 Priority Based Scheduler (SCTP_SS_RR). Works by maintaining a list of enqueued streams and tracking the last one used to send data. When the datamsg is done, it switches to the next stream. See-also: https://tools.ietf.org/html/draft-ietf-tsvwg-sctp-ndata-13 Tested-by: Xin Long Signed-off-by: Marcelo Ricardo Leitner Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 11 +++ include/uapi/linux/sctp.h | 3 +- net/sctp/Makefile | 3 +- net/sctp/stream_sched.c | 2 + net/sctp/stream_sched_rr.c | 201 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 net/sctp/stream_sched_rr.c (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 40eb8d66a37c..16f949eef52f 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1348,6 +1348,10 @@ struct sctp_stream_out_ext { struct list_head prio_list; struct sctp_stream_priorities *prio_head; }; + /* Fields used by RR scheduler */ + struct { + struct list_head rr_list; + }; }; }; @@ -1374,6 +1378,13 @@ struct sctp_stream { /* List of priorities scheduled */ struct list_head prio_list; }; + /* Fields used by RR scheduler */ + struct { + /* List of streams scheduled */ + struct list_head rr_list; + /* The next stream stream in line */ + struct sctp_stream_out_ext *rr_next; + }; }; }; diff --git a/include/uapi/linux/sctp.h b/include/uapi/linux/sctp.h index 850fa8b29d7e..6cd7d416ca40 100644 --- a/include/uapi/linux/sctp.h +++ b/include/uapi/linux/sctp.h @@ -1100,7 +1100,8 @@ struct sctp_add_streams { enum sctp_sched_type { SCTP_SS_FCFS, SCTP_SS_PRIO, - SCTP_SS_MAX = SCTP_SS_PRIO + SCTP_SS_RR, + SCTP_SS_MAX = SCTP_SS_RR }; #endif /* _UAPI_SCTP_H */ diff --git a/net/sctp/Makefile b/net/sctp/Makefile index 647c9cfd4e95..bf90c5397719 100644 --- a/net/sctp/Makefile +++ b/net/sctp/Makefile @@ -12,7 +12,8 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \ inqueue.o outqueue.o ulpqueue.o \ tsnmap.o bind_addr.o socket.o primitive.o \ output.o input.o debug.o stream.o auth.o \ - offload.o stream_sched.o stream_sched_prio.o + offload.o stream_sched.o stream_sched_prio.o \ + stream_sched_rr.o sctp_probe-y := probe.o diff --git a/net/sctp/stream_sched.c b/net/sctp/stream_sched.c index 115ddb765169..03513a9fa110 100644 --- a/net/sctp/stream_sched.c +++ b/net/sctp/stream_sched.c @@ -122,10 +122,12 @@ static struct sctp_sched_ops sctp_sched_fcfs = { /* API to other parts of the stack */ extern struct sctp_sched_ops sctp_sched_prio; +extern struct sctp_sched_ops sctp_sched_rr; struct sctp_sched_ops *sctp_sched_ops[] = { &sctp_sched_fcfs, &sctp_sched_prio, + &sctp_sched_rr, }; int sctp_sched_set_sched(struct sctp_association *asoc, diff --git a/net/sctp/stream_sched_rr.c b/net/sctp/stream_sched_rr.c new file mode 100644 index 000000000000..7612a438c5b9 --- /dev/null +++ b/net/sctp/stream_sched_rr.c @@ -0,0 +1,201 @@ +/* SCTP kernel implementation + * (C) Copyright Red Hat Inc. 2017 + * + * This file is part of the SCTP kernel implementation + * + * These functions manipulate sctp stream queue/scheduling. + * + * This SCTP implementation is free software; + * you can redistribute it and/or modify it under the terms of + * the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This SCTP implementation 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. + * + * You should have received a copy of the GNU General Public License + * along with GNU CC; see the file COPYING. If not, see + * . + * + * Please send any bug reports or fixes you make to the + * email addresched(es): + * lksctp developers + * + * Written or modified by: + * Marcelo Ricardo Leitner + */ + +#include +#include +#include +#include + +/* Priority handling + * RFC DRAFT ndata section 3.2 + */ +static void sctp_sched_rr_unsched_all(struct sctp_stream *stream); + +static void sctp_sched_rr_next_stream(struct sctp_stream *stream) +{ + struct list_head *pos; + + pos = stream->rr_next->rr_list.next; + if (pos == &stream->rr_list) + pos = pos->next; + stream->rr_next = list_entry(pos, struct sctp_stream_out_ext, rr_list); +} + +static void sctp_sched_rr_unsched(struct sctp_stream *stream, + struct sctp_stream_out_ext *soute) +{ + if (stream->rr_next == soute) + /* Try to move to the next stream */ + sctp_sched_rr_next_stream(stream); + + list_del_init(&soute->rr_list); + + /* If we have no other stream queued, clear next */ + if (list_empty(&stream->rr_list)) + stream->rr_next = NULL; +} + +static void sctp_sched_rr_sched(struct sctp_stream *stream, + struct sctp_stream_out_ext *soute) +{ + if (!list_empty(&soute->rr_list)) + /* Already scheduled. */ + return; + + /* Schedule the stream */ + list_add_tail(&soute->rr_list, &stream->rr_list); + + if (!stream->rr_next) + stream->rr_next = soute; +} + +static int sctp_sched_rr_set(struct sctp_stream *stream, __u16 sid, + __u16 prio, gfp_t gfp) +{ + return 0; +} + +static int sctp_sched_rr_get(struct sctp_stream *stream, __u16 sid, + __u16 *value) +{ + return 0; +} + +static int sctp_sched_rr_init(struct sctp_stream *stream) +{ + INIT_LIST_HEAD(&stream->rr_list); + stream->rr_next = NULL; + + return 0; +} + +static int sctp_sched_rr_init_sid(struct sctp_stream *stream, __u16 sid, + gfp_t gfp) +{ + INIT_LIST_HEAD(&stream->out[sid].ext->rr_list); + + return 0; +} + +static void sctp_sched_rr_free(struct sctp_stream *stream) +{ + sctp_sched_rr_unsched_all(stream); +} + +static void sctp_sched_rr_enqueue(struct sctp_outq *q, + struct sctp_datamsg *msg) +{ + struct sctp_stream *stream; + struct sctp_chunk *ch; + __u16 sid; + + ch = list_first_entry(&msg->chunks, struct sctp_chunk, frag_list); + sid = sctp_chunk_stream_no(ch); + stream = &q->asoc->stream; + sctp_sched_rr_sched(stream, stream->out[sid].ext); +} + +static struct sctp_chunk *sctp_sched_rr_dequeue(struct sctp_outq *q) +{ + struct sctp_stream *stream = &q->asoc->stream; + struct sctp_stream_out_ext *soute; + struct sctp_chunk *ch = NULL; + + /* Bail out quickly if queue is empty */ + if (list_empty(&q->out_chunk_list)) + goto out; + + /* Find which chunk is next */ + if (stream->out_curr) + soute = stream->out_curr->ext; + else + soute = stream->rr_next; + ch = list_entry(soute->outq.next, struct sctp_chunk, stream_list); + + sctp_sched_dequeue_common(q, ch); + +out: + return ch; +} + +static void sctp_sched_rr_dequeue_done(struct sctp_outq *q, + struct sctp_chunk *ch) +{ + struct sctp_stream_out_ext *soute; + __u16 sid; + + /* Last chunk on that msg, move to the next stream */ + sid = sctp_chunk_stream_no(ch); + soute = q->asoc->stream.out[sid].ext; + + sctp_sched_rr_next_stream(&q->asoc->stream); + + if (list_empty(&soute->outq)) + sctp_sched_rr_unsched(&q->asoc->stream, soute); +} + +static void sctp_sched_rr_sched_all(struct sctp_stream *stream) +{ + struct sctp_association *asoc; + struct sctp_stream_out_ext *soute; + struct sctp_chunk *ch; + + asoc = container_of(stream, struct sctp_association, stream); + list_for_each_entry(ch, &asoc->outqueue.out_chunk_list, list) { + __u16 sid; + + sid = sctp_chunk_stream_no(ch); + soute = stream->out[sid].ext; + if (soute) + sctp_sched_rr_sched(stream, soute); + } +} + +static void sctp_sched_rr_unsched_all(struct sctp_stream *stream) +{ + struct sctp_stream_out_ext *soute, *tmp; + + list_for_each_entry_safe(soute, tmp, &stream->rr_list, rr_list) + sctp_sched_rr_unsched(stream, soute); +} + +struct sctp_sched_ops sctp_sched_rr = { + .set = sctp_sched_rr_set, + .get = sctp_sched_rr_get, + .init = sctp_sched_rr_init, + .init_sid = sctp_sched_rr_init_sid, + .free = sctp_sched_rr_free, + .enqueue = sctp_sched_rr_enqueue, + .dequeue = sctp_sched_rr_dequeue, + .dequeue_done = sctp_sched_rr_dequeue_done, + .sched_all = sctp_sched_rr_sched_all, + .unsched_all = sctp_sched_rr_unsched_all, +}; -- cgit v1.2.3-59-g8ed1b From 81b7cb92958f50da1e0997de3289c09031c044c5 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 20 Sep 2017 10:00:30 +0800 Subject: ACPICA: Header support for the PDTT ACPI table ACPICA commit 9951c78746b52da7d23da4531fcfba6bf8c95b6a This is an ACPI 6.2 table. Link: https://github.com/acpica/acpica/commit/9951c78746b5 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/actbl1.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'include') diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h index 6b8714a428b6..7a89e6de94da 100644 --- a/include/acpi/actbl1.h +++ b/include/acpi/actbl1.h @@ -69,6 +69,7 @@ #define ACPI_SIG_HEST "HEST" /* Hardware Error Source Table */ #define ACPI_SIG_MADT "APIC" /* Multiple APIC Description Table */ #define ACPI_SIG_MSCT "MSCT" /* Maximum System Characteristics Table */ +#define ACPI_SIG_PDTT "PDTT" /* Processor Debug Trigger Table */ #define ACPI_SIG_PPTT "PPTT" /* Processor Properties Topology Table */ #define ACPI_SIG_SBST "SBST" /* Smart Battery Specification Table */ #define ACPI_SIG_SLIT "SLIT" /* System Locality Distance Information Table */ @@ -1280,6 +1281,35 @@ struct acpi_nfit_flush_address { u64 hint_address[1]; /* Variable length */ }; +/******************************************************************************* + * + * PDTT - Processor Debug Trigger Table (ACPI 6.2) + * Version 0 + * + ******************************************************************************/ + +struct acpi_table_pdtt { + struct acpi_table_header header; /* Common ACPI table header */ + u8 trigger_count; + u8 reserved[3]; + u32 array_offset; +}; + +/* + * PDTT Communication Channel Identifier Structure. + * The number of these structures is defined by trigger_count above, + * starting at array_offset. + */ +struct acpi_pdtt_channel { + u16 sub_channel_id; +}; + +/* Mask and Flags for above */ + +#define ACPI_PDTT_SUBCHANNEL_ID_MASK 0x00FF +#define ACPI_PDTT_RUNTIME_TRIGGER (1<<8) +#define ACPI_PPTT_WAIT_COMPLETION (1<<9) + /******************************************************************************* * * PPTT - Processor Properties Topology Table (ACPI 6.2) -- cgit v1.2.3-59-g8ed1b From fe97d28704147ba72f7d7859909f80b4bb5a17d4 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 20 Sep 2017 10:00:36 +0800 Subject: ACPICA: Restructure/cleanup all string-to-integer conversion functions ACPICA commit 610046d444ad781cc36673bf1f030abe50cbc61f Improve adherence to ACPI spec for implicit and explicit conversions Adds octal support for constants in ASL code Adds integer overflow errors for constants during ASL compilation Eliminates most of the existing complex flags parameters Simplify support for implicit/explicit runtime conversions Adds one new file, utilities/utstrsuppt.c Link: https://github.com/acpica/acpica/commit/610046d444ad Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/Makefile | 1 + drivers/acpi/acpica/acinterp.h | 6 +- drivers/acpi/acpica/acutils.h | 31 ++- drivers/acpi/acpica/dbconvert.c | 5 +- drivers/acpi/acpica/dswexec.c | 2 +- drivers/acpi/acpica/exconcat.c | 2 +- drivers/acpi/acpica/exconvrt.c | 30 ++- drivers/acpi/acpica/exmisc.c | 2 +- drivers/acpi/acpica/exresop.c | 2 +- drivers/acpi/acpica/nsconvert.c | 4 +- drivers/acpi/acpica/utstrsuppt.c | 417 ++++++++++++++++++++++++++++++ drivers/acpi/acpica/utstrtoul64.c | 420 +++++++++++++++---------------- include/acpi/acexcep.h | 16 +- tools/power/acpi/tools/acpidump/Makefile | 1 + tools/power/acpi/tools/acpidump/apdump.c | 3 +- tools/power/acpi/tools/acpidump/apmain.c | 4 +- 16 files changed, 690 insertions(+), 256 deletions(-) create mode 100644 drivers/acpi/acpica/utstrsuppt.c (limited to 'include') diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 1709551bc4aa..2d09b0249ac8 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -177,6 +177,7 @@ acpi-y += \ utresrc.o \ utstate.o \ utstring.o \ + utstrsuppt.o \ utstrtoul64.o \ utxface.o \ utxfinit.o \ diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index 29a863c85318..29555c8789a3 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -101,7 +101,8 @@ typedef const struct acpi_exdump_info { */ acpi_status acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, - union acpi_operand_object **result_desc, u32 flags); + union acpi_operand_object **result_desc, + u32 implicit_conversion); acpi_status acpi_ex_convert_to_buffer(union acpi_operand_object *obj_desc, @@ -424,9 +425,6 @@ acpi_ex_store_object_to_node(union acpi_operand_object *source_desc, struct acpi_walk_state *walk_state, u8 implicit_conversion); -#define ACPI_IMPLICIT_CONVERSION TRUE -#define ACPI_NO_IMPLICIT_CONVERSION FALSE - /* * exstoren - resolve/store object */ diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 745134ade35f..7a568c604a75 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -141,6 +141,11 @@ extern const char *acpi_gbl_ptyp_decode[]; #define ACPI_MSG_SUFFIX \ acpi_os_printf (" (%8.8X/%s-%u)\n", ACPI_CA_VERSION, module_name, line_number) +/* Flags to indicate implicit or explicit string-to-integer conversion */ + +#define ACPI_IMPLICIT_CONVERSION TRUE +#define ACPI_NO_IMPLICIT_CONVERSION FALSE + /* Types for Resource descriptor entries */ #define ACPI_INVALID_RESOURCE 0 @@ -197,15 +202,29 @@ void acpi_ut_strlwr(char *src_string); int acpi_ut_stricmp(char *string1, char *string2); -acpi_status acpi_ut_strtoul64(char *string, u32 flags, u64 *ret_integer); +/* + * utstrsuppt - string-to-integer conversion support functions + */ +acpi_status acpi_ut_convert_octal_string(char *string, u64 *return_value); + +acpi_status acpi_ut_convert_decimal_string(char *string, u64 *return_value_ptr); + +acpi_status acpi_ut_convert_hex_string(char *string, u64 *return_value_ptr); + +char acpi_ut_remove_leading_zeros(char **string); + +u8 acpi_ut_detect_hex_prefix(char **string); + +u8 acpi_ut_detect_octal_prefix(char **string); /* - * Values for Flags above - * Note: LIMIT values correspond to acpi_gbl_integer_byte_width values (4/8) + * utstrtoul64 - string-to-integer conversion functions */ -#define ACPI_STRTOUL_32BIT 0x04 /* 4 bytes */ -#define ACPI_STRTOUL_64BIT 0x08 /* 8 bytes */ -#define ACPI_STRTOUL_BASE16 0x10 /* Default: Base10/16 */ +acpi_status acpi_ut_strtoul64(char *string, u64 *ret_integer); + +u64 acpi_ut_explicit_strtoul64(char *string); + +u64 acpi_ut_implicit_strtoul64(char *string); /* * utglobal - Global data structures and procedures diff --git a/drivers/acpi/acpica/dbconvert.c b/drivers/acpi/acpica/dbconvert.c index 857dbc43a9b1..32d546f0db2f 100644 --- a/drivers/acpi/acpica/dbconvert.c +++ b/drivers/acpi/acpica/dbconvert.c @@ -277,10 +277,7 @@ acpi_db_convert_to_object(acpi_object_type type, default: object->type = ACPI_TYPE_INTEGER; - status = acpi_ut_strtoul64(string, - (acpi_gbl_integer_byte_width | - ACPI_STRTOUL_BASE16), - &object->integer.value); + status = acpi_ut_strtoul64(string, &object->integer.value); break; } diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c index 20d7744b06ae..22f45d090733 100644 --- a/drivers/acpi/acpica/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -134,7 +134,7 @@ acpi_ds_get_predicate_value(struct acpi_walk_state *walk_state, * object. Implicitly convert the argument if necessary. */ status = acpi_ex_convert_to_integer(obj_desc, &local_obj_desc, - ACPI_STRTOUL_BASE16); + ACPI_IMPLICIT_CONVERSION); if (ACPI_FAILURE(status)) { goto cleanup; } diff --git a/drivers/acpi/acpica/exconcat.c b/drivers/acpi/acpica/exconcat.c index 76bfb7dcae2f..59b8de2f07d3 100644 --- a/drivers/acpi/acpica/exconcat.c +++ b/drivers/acpi/acpica/exconcat.c @@ -156,7 +156,7 @@ acpi_ex_do_concatenate(union acpi_operand_object *operand0, status = acpi_ex_convert_to_integer(local_operand1, &temp_operand1, - ACPI_STRTOUL_BASE16); + ACPI_IMPLICIT_CONVERSION); break; case ACPI_TYPE_BUFFER: diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index f71028e334ee..23ebadb06a95 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -57,10 +57,10 @@ acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 max_length); * * FUNCTION: acpi_ex_convert_to_integer * - * PARAMETERS: obj_desc - Object to be converted. Must be an - * Integer, Buffer, or String - * result_desc - Where the new Integer object is returned - * flags - Used for string conversion + * PARAMETERS: obj_desc - Object to be converted. Must be an + * Integer, Buffer, or String + * result_desc - Where the new Integer object is returned + * implicit_conversion - Used for string conversion * * RETURN: Status * @@ -70,14 +70,14 @@ acpi_ex_convert_to_ascii(u64 integer, u16 base, u8 *string, u8 max_length); acpi_status acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, - union acpi_operand_object **result_desc, u32 flags) + union acpi_operand_object **result_desc, + u32 implicit_conversion) { union acpi_operand_object *return_desc; u8 *pointer; u64 result; u32 i; u32 count; - acpi_status status; ACPI_FUNCTION_TRACE_PTR(ex_convert_to_integer, obj_desc); @@ -123,12 +123,18 @@ acpi_ex_convert_to_integer(union acpi_operand_object *obj_desc, * hexadecimal as per the ACPI specification. The only exception (as * of ACPI 3.0) is that the to_integer() operator allows both decimal * and hexadecimal strings (hex prefixed with "0x"). + * + * Explicit conversion is used only by to_integer. + * All other string-to-integer conversions are implicit conversions. */ - status = acpi_ut_strtoul64(ACPI_CAST_PTR(char, pointer), - (acpi_gbl_integer_byte_width | - flags), &result); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + if (implicit_conversion) { + result = + acpi_ut_implicit_strtoul64(ACPI_CAST_PTR + (char, pointer)); + } else { + result = + acpi_ut_explicit_strtoul64(ACPI_CAST_PTR + (char, pointer)); } break; @@ -631,7 +637,7 @@ acpi_ex_convert_to_target_type(acpi_object_type destination_type, */ status = acpi_ex_convert_to_integer(source_desc, result_desc, - ACPI_STRTOUL_BASE16); + ACPI_IMPLICIT_CONVERSION); break; case ACPI_TYPE_STRING: diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c index 1e7649ce0a7b..dbad3ebd7df5 100644 --- a/drivers/acpi/acpica/exmisc.c +++ b/drivers/acpi/acpica/exmisc.c @@ -330,7 +330,7 @@ acpi_ex_do_logical_op(u16 opcode, case ACPI_TYPE_INTEGER: status = acpi_ex_convert_to_integer(operand1, &local_operand1, - ACPI_STRTOUL_BASE16); + ACPI_IMPLICIT_CONVERSION); break; case ACPI_TYPE_STRING: diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c index c4852429e2ff..1c7c9962b0de 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -415,7 +415,7 @@ acpi_ex_resolve_operands(u16 opcode, * Known as "Implicit Source Operand Conversion" */ status = acpi_ex_convert_to_integer(obj_desc, stack_ptr, - ACPI_STRTOUL_BASE16); + ACPI_IMPLICIT_CONVERSION); if (ACPI_FAILURE(status)) { if (status == AE_TYPE) { ACPI_ERROR((AE_INFO, diff --git a/drivers/acpi/acpica/nsconvert.c b/drivers/acpi/acpica/nsconvert.c index e4a7da8a11f0..539d775bbc92 100644 --- a/drivers/acpi/acpica/nsconvert.c +++ b/drivers/acpi/acpica/nsconvert.c @@ -78,8 +78,8 @@ acpi_ns_convert_to_integer(union acpi_operand_object *original_object, /* String-to-Integer conversion */ - status = acpi_ut_strtoul64(original_object->string.pointer, - acpi_gbl_integer_byte_width, &value); + status = + acpi_ut_strtoul64(original_object->string.pointer, &value); if (ACPI_FAILURE(status)) { return (status); } diff --git a/drivers/acpi/acpica/utstrsuppt.c b/drivers/acpi/acpica/utstrsuppt.c new file mode 100644 index 000000000000..ca41f037fd9a --- /dev/null +++ b/drivers/acpi/acpica/utstrsuppt.c @@ -0,0 +1,417 @@ +/******************************************************************************* + * + * Module Name: utstrsuppt - string-to-integer conversion support functions + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2017, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utstrsuppt") + +/* Local prototypes */ +static acpi_status +acpi_ut_insert_digit(u64 *accumulated_value, u32 base, int ascii_digit); + +static acpi_status +acpi_ut_strtoul_multiply64(u64 multiplicand, u64 multiplier, u64 *out_product); + +static acpi_status +acpi_ut_strtoul_add64(u64 addend1, u64 addend2, u64 *out_sum); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_convert_octal_string + * + * PARAMETERS: string - Null terminated input string + * return_value_ptr - Where the converted value is returned + * + * RETURN: Status and 64-bit converted integer + * + * DESCRIPTION: Performs a base 8 conversion of the input string to an + * integer value, either 32 or 64 bits. + * + * NOTE: Maximum 64-bit unsigned octal value is 01777777777777777777777 + * Maximum 32-bit unsigned octal value is 037777777777 + * + ******************************************************************************/ + +acpi_status acpi_ut_convert_octal_string(char *string, u64 *return_value_ptr) +{ + u64 accumulated_value = 0; + acpi_status status = AE_OK; + + /* Convert each ASCII byte in the input string */ + + while (*string) { + + /* Must be ASCII 0-7, otherwise terminate with no error */ + + if (!(ACPI_IS_OCTAL_DIGIT(*string))) { + break; + } + + /* Convert and insert this octal digit into the accumulator */ + + status = acpi_ut_insert_digit(&accumulated_value, 8, *string); + if (ACPI_FAILURE(status)) { + status = AE_OCTAL_OVERFLOW; + break; + } + + string++; + } + + /* Always return the value that has been accumulated */ + + *return_value_ptr = accumulated_value; + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_convert_decimal_string + * + * PARAMETERS: string - Null terminated input string + * return_value_ptr - Where the converted value is returned + * + * RETURN: Status and 64-bit converted integer + * + * DESCRIPTION: Performs a base 10 conversion of the input string to an + * integer value, either 32 or 64 bits. + * + * NOTE: Maximum 64-bit unsigned decimal value is 18446744073709551615 + * Maximum 32-bit unsigned decimal value is 4294967295 + * + ******************************************************************************/ + +acpi_status acpi_ut_convert_decimal_string(char *string, u64 *return_value_ptr) +{ + u64 accumulated_value = 0; + acpi_status status = AE_OK; + + /* Convert each ASCII byte in the input string */ + + while (*string) { + + /* Must be ASCII 0-9, otherwise terminate with no error */ + + if (!isdigit(*string)) { + break; + } + + /* Convert and insert this decimal digit into the accumulator */ + + status = acpi_ut_insert_digit(&accumulated_value, 10, *string); + if (ACPI_FAILURE(status)) { + status = AE_DECIMAL_OVERFLOW; + break; + } + + string++; + } + + /* Always return the value that has been accumulated */ + + *return_value_ptr = accumulated_value; + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_convert_hex_string + * + * PARAMETERS: string - Null terminated input string + * return_value_ptr - Where the converted value is returned + * + * RETURN: Status and 64-bit converted integer + * + * DESCRIPTION: Performs a base 16 conversion of the input string to an + * integer value, either 32 or 64 bits. + * + * NOTE: Maximum 64-bit unsigned hex value is 0xFFFFFFFFFFFFFFFF + * Maximum 32-bit unsigned hex value is 0xFFFFFFFF + * + ******************************************************************************/ + +acpi_status acpi_ut_convert_hex_string(char *string, u64 *return_value_ptr) +{ + u64 accumulated_value = 0; + acpi_status status = AE_OK; + + /* Convert each ASCII byte in the input string */ + + while (*string) { + + /* Must be ASCII A-F, a-f, or 0-9, otherwise terminate with no error */ + + if (!isxdigit(*string)) { + break; + } + + /* Convert and insert this hex digit into the accumulator */ + + status = acpi_ut_insert_digit(&accumulated_value, 16, *string); + if (ACPI_FAILURE(status)) { + status = AE_HEX_OVERFLOW; + break; + } + + string++; + } + + /* Always return the value that has been accumulated */ + + *return_value_ptr = accumulated_value; + return (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_remove_leading_zeros + * + * PARAMETERS: string - Pointer to input ASCII string + * + * RETURN: Next character after the leading zeros. This behavior may be + * Used by the caller to detect end-of-string. + * + * DESCRIPTION: Remove all leading zeros in the input string. Return the + * next character after the final zero to check for the end + * of the string (NULL terminator). + * + ******************************************************************************/ + +char acpi_ut_remove_leading_zeros(char **string) +{ + /* Skip all leading zeros */ + + while (**string == ACPI_ASCII_ZERO) { + *string += 1; + } + + return (**string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_detect_hex_prefix + * + * PARAMETERS: string - Pointer to input ASCII string + * + * RETURN: TRUE if a 0x prefix was found + * + * DESCRIPTION: Detect and remove a hex 0x prefix + * + ******************************************************************************/ + +u8 acpi_ut_detect_hex_prefix(char **string) +{ + + if ((**string == ACPI_ASCII_ZERO) && + (tolower((int)*(*string + 1)) == 'x')) { + *string += 2; /* Go past the leading 0x */ + return (TRUE); + } + + return (FALSE); /* Not a hex string */ +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_detect_octal_prefix + * + * PARAMETERS: string - Pointer to input ASCII string + * + * RETURN: True if an octal 0 prefix was found + * + * DESCRIPTION: Detect and remove an octal prefix (zero) + * + ******************************************************************************/ + +u8 acpi_ut_detect_octal_prefix(char **string) +{ + + if (**string == ACPI_ASCII_ZERO) { + *string += 1; /* Go past the leading 0 */ + return (TRUE); + } + + return (FALSE); /* Not an octal string */ +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_insert_digit + * + * PARAMETERS: accumulated_value - Current value of the integer value + * accumulator. The New value is + * returned here. + * base - Radix, either 8/10/16 supported + * ascii_digit - ASCII single digit to be inserted + * + * RETURN: Status and result of convert/insert operation. The only + * exception is numeric overflow of either the multiply or the + * add operations. + * + * DESCRIPTION: Generic conversion and insertion function for all bases: + * + * 1) Multiply the current accumulated converted value by the + * base in order to make room for the new character. + * + * 2) Add the current accumulated/converted value the new + * character (after the character has been converted to a binary + * value). + * + * Note: The only possible exception indicates an integer + * overflow (AE_NUMERIC_OVERFLOW) + * + ******************************************************************************/ + +static acpi_status +acpi_ut_insert_digit(u64 *accumulated_value, u32 base, int ascii_digit) +{ + acpi_status status; + u64 product; + + /* Make room in the accumulated value for the incoming digit */ + + status = acpi_ut_strtoul_multiply64(*accumulated_value, base, &product); + if (ACPI_FAILURE(status)) { + return (status); + } + + /* Add in the new digit, and store to the caller's accumulated value */ + + status = + acpi_ut_strtoul_add64(product, + acpi_ut_ascii_char_to_hex(ascii_digit), + accumulated_value); + if (ACPI_FAILURE(status)) { + return (status); + } + + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_strtoul_multiply64 + * + * PARAMETERS: multiplicand - Current accumulated converted integer + * multiplier - Base/Radix + * out_product - Where the product is returned + * + * RETURN: Status and 64-bit product + * + * DESCRIPTION: Multiply two 64-bit values, with checking for 64-bit overflow as + * well as 32-bit overflow if necessary (if the current global + * integer width is 32). + * + ******************************************************************************/ + +static acpi_status +acpi_ut_strtoul_multiply64(u64 multiplicand, u64 multiplier, u64 *out_product) +{ + u64 val; + + /* Exit if either operand is zero */ + + *out_product = 0; + if (!multiplicand || !multiplier) { + return (AE_OK); + } + + /* Check for 64-bit overflow before the actual multiplication */ + + acpi_ut_short_divide(ACPI_UINT64_MAX, (u32)multiplier, &val, NULL); + if (multiplicand > val) { + return (AE_NUMERIC_OVERFLOW); + } + + val = multiplicand * multiplier; + + /* Check for 32-bit overflow if necessary */ + + if ((acpi_gbl_integer_bit_width == 32) && (val > ACPI_UINT32_MAX)) { + return (AE_NUMERIC_OVERFLOW); + } + + *out_product = val; + return (AE_OK); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_strtoul_add64 + * + * PARAMETERS: addend1 - Current accumulated converted integer + * addend2 - New hex value/char + * out_sum - Where sum is returned (Accumulator) + * + * RETURN: Status and 64-bit sum + * + * DESCRIPTION: Add two 64-bit values, with checking for 64-bit overflow as + * well as 32-bit overflow if necessary (if the current global + * integer width is 32). + * + ******************************************************************************/ + +static acpi_status acpi_ut_strtoul_add64(u64 addend1, u64 addend2, u64 *out_sum) +{ + u64 sum; + + /* Check for 64-bit overflow before the actual addition */ + + if ((addend1 > 0) && (addend2 > (ACPI_UINT64_MAX - addend1))) { + return (AE_NUMERIC_OVERFLOW); + } + + sum = addend1 + addend2; + + /* Check for 32-bit overflow if necessary */ + + if ((acpi_gbl_integer_bit_width == 32) && (sum > ACPI_UINT32_MAX)) { + return (AE_NUMERIC_OVERFLOW); + } + + *out_sum = sum; + return (AE_OK); +} diff --git a/drivers/acpi/acpica/utstrtoul64.c b/drivers/acpi/acpica/utstrtoul64.c index 9633ee142855..e5a2df3f16e5 100644 --- a/drivers/acpi/acpica/utstrtoul64.c +++ b/drivers/acpi/acpica/utstrtoul64.c @@ -1,6 +1,7 @@ /******************************************************************************* * - * Module Name: utstrtoul64 - string to 64-bit integer support + * Module Name: utstrtoul64 - string-to-integer support for both 64-bit + * and 32-bit integers * ******************************************************************************/ @@ -44,152 +45,74 @@ #include #include "accommon.h" -/******************************************************************************* - * - * The functions in this module satisfy the need for 64-bit string-to-integer - * conversions on both 32-bit and 64-bit platforms. - * - ******************************************************************************/ - #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("utstrtoul64") -/* Local prototypes */ -static u64 acpi_ut_strtoul_base10(char *string, u32 flags); - -static u64 acpi_ut_strtoul_base16(char *string, u32 flags); - /******************************************************************************* * - * String conversion rules as written in the ACPI specification. The error - * conditions and behavior are different depending on the type of conversion. - * + * This module contains the external string to 64/32-bit unsigned integer + * conversion functions: * - * Implicit data type conversion: string-to-integer - * -------------------------------------------------- - * - * Base is always 16. This is the ACPI_STRTOUL_BASE16 case. - * - * Example: - * Add ("BA98", Arg0, Local0) - * - * The integer is initialized to the value zero. - * The ASCII string is interpreted as a hexadecimal constant. - * - * 1) A "0x" prefix is not allowed. However, ACPICA allows this for - * compatibility with previous ACPICA. (NO ERROR) - * - * 2) Terminates when the size of an integer is reached (32 or 64 bits). - * (NO ERROR) + * 1) Standard strtoul() function with 64-bit support. This is mostly used by + * the iASL compiler. + * 2) Runtime "Explicit conversion" as defined in the ACPI specification. + * 3) Runtime "Implicit conversion" as defined in the ACPI specification. * - * 3) The first non-hex character terminates the conversion without error. - * (NO ERROR) + * Current users of this module: * - * 4) Conversion of a null (zero-length) string to an integer is not - * allowed. However, ACPICA allows this for compatibility with previous - * ACPICA. This conversion returns the value 0. (NO ERROR) - * - * - * Explicit data type conversion: to_integer() with string operand - * --------------------------------------------------------------- - * - * Base is either 10 (default) or 16 (with 0x prefix) - * - * Examples: - * to_integer ("1000") - * to_integer ("0xABCD") - * - * 1) Can be (must be) either a decimal or hexadecimal numeric string. - * A hex value must be prefixed by "0x" or it is interpreted as a decimal. + * interpreter - Implicit and explicit conversions, GPE method names + * debugger - Command line input string conversion + * iASL - Main parser, conversion of constants to integers + * iASL - Data Table Compiler parser (constant math expressions) + * iASL - Preprocessor (constant math expressions) + * acpi_dump - Input table addresses + * acpi_exec - Testing of the acpi_ut_strtoul64 function * - * 2) The value must not exceed the maximum of an integer value. ACPI spec - * states the behavior is "unpredictable", so ACPICA matches the behavior - * of the implicit conversion case.(NO ERROR) + * Notes concerning users of these interfaces: * - * 3) Behavior on the first non-hex character is not specified by the ACPI - * spec, so ACPICA matches the behavior of the implicit conversion case - * and terminates. (NO ERROR) + * acpi_gbl_integer_byte_width is used to set the 32/64 bit limit. This global + * must be set to the proper width. For the core ACPICA code, the width + * depends on the DSDT version. For iASL, the default width is 64 bits for + * all parsers, but error checking is performed later to flag cases where + * a 64-bit constant is wrongly defined in a 32-bit DSDT/SSDT. * - * 4) A null (zero-length) string is illegal. - * However, ACPICA allows this for compatibility with previous ACPICA. - * This conversion returns the value 0. (NO ERROR) + * In ACPI, the only place where octal numbers are supported is within + * the ASL language itself. There is no runtime support for octal. * ******************************************************************************/ - /******************************************************************************* * * FUNCTION: acpi_ut_strtoul64 * - * PARAMETERS: string - Null terminated input string - * flags - Conversion info, see below + * PARAMETERS: string - Null terminated input string. + * Must be a valid pointer * return_value - Where the converted integer is - * returned - * - * RETURN: Status and Converted value - * - * DESCRIPTION: Convert a string into an unsigned value. Performs either a - * 32-bit or 64-bit conversion, depending on the input integer - * size in Flags (often the current mode of the interpreter). - * - * Values for Flags: - * ACPI_STRTOUL_32BIT - Max integer value is 32 bits - * ACPI_STRTOUL_64BIT - Max integer value is 64 bits - * ACPI_STRTOUL_BASE16 - Input string is hexadecimal. Default - * is 10/16 based on string prefix (0x). + * returned. Must be a valid pointer * - * NOTES: - * Negative numbers are not supported, as they are not supported by ACPI. + * RETURN: Status and converted integer + * Returns an exception on numeric overflow * - * Supports only base 16 or base 10 strings/values. Does not - * support Octal strings, as these are not supported by ACPI. + * DESCRIPTION: Convert a string into an unsigned integer. Performs either a + * 32-bit or 64-bit conversion, depending on the current global + * integer width. Supports Decimal, Hex, and Octal strings. * - * Current users of this support: + * Current users of this function: * - * interpreter - Implicit and explicit conversions, GPE method names - * debugger - Command line input string conversion - * iASL - Main parser, conversion of constants to integers - * iASL - Data Table Compiler parser (constant math expressions) * iASL - Preprocessor (constant math expressions) - * acpi_dump - Input table addresses - * acpi_exec - Testing of the acpi_ut_strtoul64 function - * - * Note concerning callers: - * acpi_gbl_integer_byte_width can be used to set the 32/64 limit. If used, - * this global should be set to the proper width. For the core ACPICA code, - * this width depends on the DSDT version. For iASL, the default byte - * width is always 8 for the parser, but error checking is performed later - * to flag cases where a 64-bit constant is defined in a 32-bit DSDT/SSDT. + * iASL - Main parser, conversion of ASL constants to integers + * iASL - Data Table Compiler parser (constant math expressions) * ******************************************************************************/ - -acpi_status acpi_ut_strtoul64(char *string, u32 flags, u64 *return_value) +acpi_status acpi_ut_strtoul64(char *string, u64 *return_value) { acpi_status status = AE_OK; - u32 base; + u32 base = 10; /* Default is decimal */ ACPI_FUNCTION_TRACE_STR(ut_strtoul64, string); - /* Parameter validation */ - - if (!string || !return_value) { - return_ACPI_STATUS(AE_BAD_PARAMETER); - } - *return_value = 0; - /* Check for zero-length string, returns 0 */ - - if (*string == 0) { - return_ACPI_STATUS(AE_OK); - } - - /* Skip over any white space at start of string */ - - while (isspace((int)*string)) { - string++; - } - - /* End of string? return 0 */ + /* Null return string returns a value of zero */ if (*string == 0) { return_ACPI_STATUS(AE_OK); @@ -198,45 +121,45 @@ acpi_status acpi_ut_strtoul64(char *string, u32 flags, u64 *return_value) /* * 1) The "0x" prefix indicates base 16. Per the ACPI specification, * the "0x" prefix is only allowed for implicit (non-strict) conversions. - * However, we always allow it for compatibility with older ACPICA. + * However, we always allow it for compatibility with older ACPICA and + * just plain on principle. */ - if ((*string == ACPI_ASCII_ZERO) && - (tolower((int)*(string + 1)) == 'x')) { - string += 2; /* Go past the 0x */ - if (*string == 0) { - return_ACPI_STATUS(AE_OK); /* Return value 0 */ - } - - base = 16; - } - - /* 2) Force to base 16 (implicit conversion case) */ - - else if (flags & ACPI_STRTOUL_BASE16) { + if (acpi_ut_detect_hex_prefix(&string)) { base = 16; } - /* 3) Default fallback is to Base 10 */ - - else { - base = 10; + /* + * 2) Check for an octal constant, defined to be a leading zero + * followed by an valid octal digit (0-7) + */ + else if (acpi_ut_detect_octal_prefix(&string)) { + base = 8; } - /* Skip all leading zeros */ - - while (*string == ACPI_ASCII_ZERO) { - string++; - if (*string == 0) { - return_ACPI_STATUS(AE_OK); /* Return value 0 */ - } + if (!acpi_ut_remove_leading_zeros(&string)) { + return_ACPI_STATUS(AE_OK); /* Return value 0 */ } - /* Perform the base 16 or 10 conversion */ - - if (base == 16) { - *return_value = acpi_ut_strtoul_base16(string, flags); - } else { - *return_value = acpi_ut_strtoul_base10(string, flags); + /* + * Perform the base 8, 10, or 16 conversion. A numeric overflow will + * return an exception. + */ + switch (base) { + case 8: + status = acpi_ut_convert_octal_string(string, return_value); + break; + + case 10: + status = acpi_ut_convert_decimal_string(string, return_value); + break; + + case 16: + status = acpi_ut_convert_hex_string(string, return_value); + break; + + default: + status = AE_AML_INTERNAL; /* Should never happen */ + break; } return_ACPI_STATUS(status); @@ -244,104 +167,167 @@ acpi_status acpi_ut_strtoul64(char *string, u32 flags, u64 *return_value) /******************************************************************************* * - * FUNCTION: acpi_ut_strtoul_base10 + * FUNCTION: acpi_ut_implicit_strtoul64 + * + * PARAMETERS: string - Null terminated input string. + * Must be a valid pointer * - * PARAMETERS: string - Null terminated input string - * flags - Conversion info + * RETURN: Converted integer * - * RETURN: 64-bit converted integer + * DESCRIPTION: Perform a 64-bit conversion with restrictions placed upon + * an "implicit conversion" by the ACPI specification. Used by + * many ASL operators that require an integer operand, and support + * an automatic (implicit) conversion from a string operand + * to the final integer operand. The restriction is that only + * hex strings are supported. * - * DESCRIPTION: Performs a base 10 conversion of the input string to an - * integer value, either 32 or 64 bits. - * Note: String must be valid and non-null. + * ----------------------------------------------------------------------------- + * + * Base is always 16, either with or without the 0x prefix. + * + * Examples (both are hex values): + * Add ("BA98", Arg0, Local0) + * Subtract ("0x12345678", Arg1, Local1) + * + * Rules extracted from the ACPI specification: + * + * The converted integer is initialized to the value zero. + * The ASCII string is interpreted as a hexadecimal constant. + * + * 1) A "0x" prefix is not allowed. However, ACPICA allows this as an + * ACPI extension on general principle. (NO ERROR) + * + * 2) Terminates when the size of an integer is reached (32 or 64 bits). + * There are no numeric overflow conditions. (NO ERROR) + * + * 3) The first non-hex character terminates the conversion and returns + * the current accumulated value of the converted integer (NO ERROR). + * + * 4) Conversion of a null (zero-length) string to an integer is + * technically allowed. However, ACPICA allows as an ACPI extension. + * The conversion returns the value 0. (NO ERROR) + * + * Note: there are no error conditions returned by this function. At + * the minimum, a value of zero is returned. + * + * Current users of this function: + * + * interpreter - All runtime implicit conversions, as per ACPI specification + * iASL - Data Table Compiler parser (constant math expressions) * ******************************************************************************/ -static u64 acpi_ut_strtoul_base10(char *string, u32 flags) +u64 acpi_ut_implicit_strtoul64(char *string) { - int ascii_digit; - u64 next_value; - u64 return_value = 0; + u64 converted_integer = 0; - /* Main loop: convert each ASCII byte in the input string */ + ACPI_FUNCTION_TRACE_STR(ut_implicit_strtoul64, string); - while (*string) { - ascii_digit = *string; - if (!isdigit(ascii_digit)) { - - /* Not ASCII 0-9, terminate */ - - goto exit; - } - - /* Convert and insert (add) the decimal digit */ - - acpi_ut_short_multiply(return_value, 10, &next_value); - next_value += (ascii_digit - ACPI_ASCII_ZERO); - - /* Check for overflow (32 or 64 bit) - return current converted value */ - - if (((flags & ACPI_STRTOUL_32BIT) && (next_value > ACPI_UINT32_MAX)) || (next_value < return_value)) { /* 64-bit overflow case */ - goto exit; - } + /* + * Per the ACPI specification, only hexadecimal is supported for + * implicit conversions, and the "0x" prefix is "not allowed". + * However, allow a "0x" prefix as an ACPI extension. + */ + acpi_ut_detect_hex_prefix(&string); - return_value = next_value; - string++; + if (!acpi_ut_remove_leading_zeros(&string)) { + return_VALUE(0); } -exit: - return (return_value); + /* + * Ignore overflow as per the ACPI specification. This is implemented by + * ignoring the return status below. On overflow, the input string is + * simply truncated. + */ + acpi_ut_convert_hex_string(string, &converted_integer); + return_VALUE(converted_integer); } /******************************************************************************* * - * FUNCTION: acpi_ut_strtoul_base16 + * FUNCTION: acpi_ut_explicit_strtoul64 + * + * PARAMETERS: string - Null terminated input string. + * Must be a valid pointer + * + * RETURN: Converted integer + * + * DESCRIPTION: Perform a 64-bit conversion with the restrictions placed upon + * an "explicit conversion" by the ACPI specification. The + * main restriction is that only hex and decimal are supported. + * + * ----------------------------------------------------------------------------- + * + * Base is either 10 (default) or 16 (with 0x prefix). There is no octal + * (base 8), as per the ACPI specification. + * + * Examples: + * to_integer ("1000") Decimal + * to_integer ("0xABCD") Hex + * + * Rules extracted from the ACPI specification: + * + * 1) Thi input string is either a decimal or hexadecimal numeric string. + * A hex value must be prefixed by "0x" or it is interpreted as decimal. + * + * 2) The value must not exceed the maximum of an integer value + * (32 or 64 bits). The ACPI specification states the behavior is + * "unpredictable", so ACPICA matches the behavior of the implicit + * conversion case. There are no numeric overflow conditions. (NO ERROR) + * + * 3) Behavior on the first non-hex character is not specified by the ACPI + * specification (for the to_integer operator), so ACPICA matches the + * behavior of the implicit conversion case. It terminates the + * conversion and returns the current accumulated value of the converted + * integer. (NO ERROR) * - * PARAMETERS: string - Null terminated input string - * flags - conversion info + * 4) Conversion of a null (zero-length) string to an integer is + * technically allowed. However, ACPICA allows as an ACPI extension. + * The conversion returns the value 0. (NO ERROR) * - * RETURN: 64-bit converted integer + * Note: there are no error conditions returned by this function. At + * the minimum, a value of zero is returned. * - * DESCRIPTION: Performs a base 16 conversion of the input string to an - * integer value, either 32 or 64 bits. - * Note: String must be valid and non-null. + * Current users of this function: + * + * interpreter - Runtime ASL to_integer operator, as per the ACPI specification * ******************************************************************************/ -static u64 acpi_ut_strtoul_base16(char *string, u32 flags) +u64 acpi_ut_explicit_strtoul64(char *string) { - int ascii_digit; - u32 valid_digits = 1; - u64 return_value = 0; - - /* Main loop: convert each ASCII byte in the input string */ - - while (*string) { - - /* Check for overflow (32 or 64 bit) - return current converted value */ - - if ((valid_digits > 16) || - ((valid_digits > 8) && (flags & ACPI_STRTOUL_32BIT))) { - goto exit; - } - - ascii_digit = *string; - if (!isxdigit(ascii_digit)) { - - /* Not Hex ASCII A-F, a-f, or 0-9, terminate */ + u64 converted_integer = 0; + u32 base = 10; /* Default is decimal */ - goto exit; - } + ACPI_FUNCTION_TRACE_STR(ut_explicit_strtoul64, string); - /* Convert and insert the hex digit */ + /* + * Only Hex and Decimal are supported, as per the ACPI specification. + * 0x prefix means hex; otherwise decimal is assumed. + */ + if (acpi_ut_detect_hex_prefix(&string)) { + base = 16; + } - acpi_ut_short_shift_left(return_value, 4, &return_value); - return_value |= acpi_ut_ascii_char_to_hex(ascii_digit); + if (!acpi_ut_remove_leading_zeros(&string)) { + return_VALUE(0); + } - string++; - valid_digits++; + /* + * Ignore overflow as per the ACPI specification. This is implemented by + * ignoring the return status below. On overflow, the input string is + * simply truncated. + */ + switch (base) { + case 10: + default: + acpi_ut_convert_decimal_string(string, &converted_integer); + break; + + case 16: + acpi_ut_convert_hex_string(string, &converted_integer); + break; } -exit: - return (return_value); + return_VALUE(converted_integer); } diff --git a/include/acpi/acexcep.h b/include/acpi/acexcep.h index ad54610ea6cd..17d61b1f2511 100644 --- a/include/acpi/acexcep.h +++ b/include/acpi/acexcep.h @@ -126,8 +126,12 @@ struct acpi_exception_info { #define AE_NOT_CONFIGURED EXCEP_ENV (0x001C) #define AE_ACCESS EXCEP_ENV (0x001D) #define AE_IO_ERROR EXCEP_ENV (0x001E) +#define AE_NUMERIC_OVERFLOW EXCEP_ENV (0x001F) +#define AE_HEX_OVERFLOW EXCEP_ENV (0x0020) +#define AE_DECIMAL_OVERFLOW EXCEP_ENV (0x0021) +#define AE_OCTAL_OVERFLOW EXCEP_ENV (0x0022) -#define AE_CODE_ENV_MAX 0x001E +#define AE_CODE_ENV_MAX 0x0022 /* * Programmer exceptions @@ -263,7 +267,15 @@ static const struct acpi_exception_info acpi_gbl_exception_names_env[] = { EXCEP_TXT("AE_NOT_CONFIGURED", "The interface is not part of the current subsystem configuration"), EXCEP_TXT("AE_ACCESS", "Permission denied for the requested operation"), - EXCEP_TXT("AE_IO_ERROR", "An I/O error occurred") + EXCEP_TXT("AE_IO_ERROR", "An I/O error occurred"), + EXCEP_TXT("AE_NUMERIC_OVERFLOW", + "Overflow during string-to-integer conversion"), + EXCEP_TXT("AE_HEX_OVERFLOW", + "Overflow during ASCII hex-to-binary conversion"), + EXCEP_TXT("AE_DECIMAL_OVERFLOW", + "Overflow during ASCII decimal-to-binary conversion"), + EXCEP_TXT("AE_OCTAL_OVERFLOW", + "Overflow during ASCII octal-to-binary conversion") }; static const struct acpi_exception_info acpi_gbl_exception_names_pgm[] = { diff --git a/tools/power/acpi/tools/acpidump/Makefile b/tools/power/acpi/tools/acpidump/Makefile index f7c7af1f9258..b436f8675f6a 100644 --- a/tools/power/acpi/tools/acpidump/Makefile +++ b/tools/power/acpi/tools/acpidump/Makefile @@ -39,6 +39,7 @@ TOOL_OBJS = \ utnonansi.o\ utprint.o\ utstring.o\ + utstrsuppt.o\ utstrtoul64.o\ utxferror.o\ oslinuxtbl.o\ diff --git a/tools/power/acpi/tools/acpidump/apdump.c b/tools/power/acpi/tools/acpidump/apdump.c index 60df1fbd4a77..0634449156d8 100644 --- a/tools/power/acpi/tools/acpidump/apdump.c +++ b/tools/power/acpi/tools/acpidump/apdump.c @@ -287,8 +287,7 @@ int ap_dump_table_by_address(char *ascii_address) /* Convert argument to an integer physical address */ - status = acpi_ut_strtoul64(ascii_address, ACPI_STRTOUL_64BIT, - &long_address); + status = acpi_ut_strtoul64(ascii_address, &long_address); if (ACPI_FAILURE(status)) { fprintf(stderr, "%s: Could not convert to a physical address\n", ascii_address); diff --git a/tools/power/acpi/tools/acpidump/apmain.c b/tools/power/acpi/tools/acpidump/apmain.c index 943b6b614683..22c3b4ee1617 100644 --- a/tools/power/acpi/tools/acpidump/apmain.c +++ b/tools/power/acpi/tools/acpidump/apmain.c @@ -208,9 +208,7 @@ static int ap_do_options(int argc, char **argv) case 'r': /* Dump tables from specified RSDP */ status = - acpi_ut_strtoul64(acpi_gbl_optarg, - ACPI_STRTOUL_64BIT, - &gbl_rsdp_base); + acpi_ut_strtoul64(acpi_gbl_optarg, &gbl_rsdp_base); if (ACPI_FAILURE(status)) { fprintf(stderr, "%s: Could not convert to a physical address\n", -- cgit v1.2.3-59-g8ed1b From d954f94cfa4488f4281db2b698d90e1b03facb4d Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Wed, 20 Sep 2017 10:01:02 +0800 Subject: ACPICA: Update version to 20170831 ACPICA commit faa9fdfbd035a14347e6a875be2a0cddb3e6fc00 Version 20170831. Link: https://github.com/acpica/acpica/commit/faa9fdfbd035 Signed-off-by: Bob Moore Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 53c5e2f7bcec..e1dd1a8d42b6 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -46,7 +46,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20170728 +#define ACPI_CA_VERSION 0x20170831 #include #include -- cgit v1.2.3-59-g8ed1b From eaefd5abf6b095bfc55eb745bdf7c42cf66790eb Mon Sep 17 00:00:00 2001 From: James Smart Date: Thu, 14 Sep 2017 10:38:42 -0700 Subject: nvme-fc: add uevent for auto-connect To support auto-connecting to FC-NVME devices upon their dynamic appearance, add a uevent that can kick off connection scripts. uevent is posted against the fc_udev device. patch set tested with the following rule to kick an nvme-cli connect-all for the FC initiator and FC target ports. This is just an example for testing and not intended for real life use. ACTION=="change", SUBSYSTEM=="fc", ENV{FC_EVENT}=="nvmediscovery", \ ENV{NVMEFC_HOST_TRADDR}=="*", ENV{NVMEFC_TRADDR}=="*", \ RUN+="/bin/sh -c '/usr/local/sbin/nvme connect-all --transport=fc --host-traddr=$env{NVMEFC_HOST_TRADDR} --traddr=$env{NVMEFC_TRADDR} >> /tmp/nvme_fc.log'" I will post proposed udev/systemd scripts for possible kernel support. Signed-off-by: James Smart Signed-off-by: Christoph Hellwig --- drivers/nvme/host/fc.c | 49 ++++++++++++++++++++++++++++++++++++++++++ include/linux/nvme-fc-driver.h | 2 ++ 2 files changed, 51 insertions(+) (limited to 'include') diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index af075e998944..f546eecb1f82 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -452,6 +452,36 @@ nvme_fc_unregister_localport(struct nvme_fc_local_port *portptr) } EXPORT_SYMBOL_GPL(nvme_fc_unregister_localport); +/* + * TRADDR strings, per FC-NVME are fixed format: + * "nn-0x<16hexdigits>:pn-0x<16hexdigits>" - 43 characters + * udev event will only differ by prefix of what field is + * being specified: + * "NVMEFC_HOST_TRADDR=" or "NVMEFC_TRADDR=" - 19 max characters + * 19 + 43 + null_fudge = 64 characters + */ +#define FCNVME_TRADDR_LENGTH 64 + +static void +nvme_fc_signal_discovery_scan(struct nvme_fc_lport *lport, + struct nvme_fc_rport *rport) +{ + char hostaddr[FCNVME_TRADDR_LENGTH]; /* NVMEFC_HOST_TRADDR=...*/ + char tgtaddr[FCNVME_TRADDR_LENGTH]; /* NVMEFC_TRADDR=...*/ + char *envp[4] = { "FC_EVENT=nvmediscovery", hostaddr, tgtaddr, NULL }; + + if (!(rport->remoteport.port_role & FC_PORT_ROLE_NVME_DISCOVERY)) + return; + + snprintf(hostaddr, sizeof(hostaddr), + "NVMEFC_HOST_TRADDR=nn-0x%016llx:pn-0x%016llx", + lport->localport.node_name, lport->localport.port_name); + snprintf(tgtaddr, sizeof(tgtaddr), + "NVMEFC_TRADDR=nn-0x%016llx:pn-0x%016llx", + rport->remoteport.node_name, rport->remoteport.port_name); + kobject_uevent_env(&fc_udev_device->kobj, KOBJ_CHANGE, envp); +} + /** * nvme_fc_register_remoteport - transport entry point called by an * LLDD to register the existence of a NVME @@ -516,6 +546,8 @@ nvme_fc_register_remoteport(struct nvme_fc_local_port *localport, list_add_tail(&newrec->endp_list, &lport->endp_list); spin_unlock_irqrestore(&nvme_fc_lock, flags); + nvme_fc_signal_discovery_scan(lport, newrec); + *portptr = &newrec->remoteport; return 0; @@ -634,6 +666,23 @@ nvme_fc_unregister_remoteport(struct nvme_fc_remote_port *portptr) } EXPORT_SYMBOL_GPL(nvme_fc_unregister_remoteport); +/** + * nvme_fc_rescan_remoteport - transport entry point called by an + * LLDD to request a nvme device rescan. + * @remoteport: pointer to the (registered) remote port that is to be + * rescanned. + * + * Returns: N/A + */ +void +nvme_fc_rescan_remoteport(struct nvme_fc_remote_port *remoteport) +{ + struct nvme_fc_rport *rport = remoteport_to_rport(remoteport); + + nvme_fc_signal_discovery_scan(rport->lport, rport); +} +EXPORT_SYMBOL_GPL(nvme_fc_rescan_remoteport); + /* *********************** FC-NVME DMA Handling **************************** */ diff --git a/include/linux/nvme-fc-driver.h b/include/linux/nvme-fc-driver.h index a726f96010d5..4ea03b9a5c8c 100644 --- a/include/linux/nvme-fc-driver.h +++ b/include/linux/nvme-fc-driver.h @@ -446,6 +446,8 @@ int nvme_fc_register_remoteport(struct nvme_fc_local_port *localport, int nvme_fc_unregister_remoteport(struct nvme_fc_remote_port *remoteport); +void nvme_fc_rescan_remoteport(struct nvme_fc_remote_port *remoteport); + /* -- cgit v1.2.3-59-g8ed1b From 6263368c5b0b758d8639cad37a2a6493c9370425 Mon Sep 17 00:00:00 2001 From: Ed Blake Date: Tue, 26 Sep 2017 11:40:02 +0100 Subject: serial: Add define for max baud rate divisor Add a define for the maximum baud rate divisor, to improve code readability. Signed-off-by: Ed Blake Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mtk.c | 2 +- drivers/tty/serial/8250/8250_omap.c | 4 ++-- drivers/tty/serial/8250/8250_port.c | 2 +- include/uapi/linux/serial_reg.h | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/tty/serial/8250/8250_mtk.c b/drivers/tty/serial/8250/8250_mtk.c index fb45770d47aa..fef9823d7b4c 100644 --- a/drivers/tty/serial/8250/8250_mtk.c +++ b/drivers/tty/serial/8250/8250_mtk.c @@ -61,7 +61,7 @@ mtk8250_set_termios(struct uart_port *port, struct ktermios *termios, * registers to their default values. */ baud = uart_get_baud_rate(port, termios, old, - port->uartclk / 16 / 0xffff, + port->uartclk / 16 / UART_DIV_MAX, port->uartclk); if (baud <= 115200) { diff --git a/drivers/tty/serial/8250/8250_omap.c b/drivers/tty/serial/8250/8250_omap.c index 833771bca0a5..4938d338e01f 100644 --- a/drivers/tty/serial/8250/8250_omap.c +++ b/drivers/tty/serial/8250/8250_omap.c @@ -199,7 +199,7 @@ static void omap_8250_get_divisor(struct uart_port *port, unsigned int baud, * Old custom speed handling. */ if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) { - priv->quot = port->custom_divisor & 0xffff; + priv->quot = port->custom_divisor & UART_DIV_MAX; /* * I assume that nobody is using this. But hey, if somebody * would like to specify the divisor _and_ the mode then the @@ -358,7 +358,7 @@ static void omap_8250_set_termios(struct uart_port *port, * Ask the core to calculate the divisor for us. */ baud = uart_get_baud_rate(port, termios, old, - port->uartclk / 16 / 0xffff, + port->uartclk / 16 / UART_DIV_MAX, port->uartclk / 13); omap_8250_get_divisor(port, baud, priv); diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index f0cc04f62b67..01ab2188a151 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2601,7 +2601,7 @@ static unsigned int serial8250_get_baud_rate(struct uart_port *port, * causing transmission errors. */ return uart_get_baud_rate(port, termios, old, - port->uartclk / 16 / 0xffff, + port->uartclk / 16 / UART_DIV_MAX, port->uartclk); } diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index 5db76880b4ad..dea05724c760 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -157,6 +157,7 @@ */ #define UART_DLL 0 /* Out: Divisor Latch Low */ #define UART_DLM 1 /* Out: Divisor Latch High */ +#define UART_DIV_MAX 0xFFFF /* Max divisor value */ /* * LCR=0xBF (or DLAB=1 for 16C660) -- cgit v1.2.3-59-g8ed1b From eb8470db8bc018fc28901e4e3b0f48e33f1ea7df Mon Sep 17 00:00:00 2001 From: Jan Kandziora Date: Wed, 20 Sep 2017 23:52:45 +0200 Subject: wire: export w1_touch_bit The w1_ds28e17 driver from the next part of this patch needs to emit single-bit read timeslots to the DS28E17. The w1 subsystem already has this function but it is not exported outside drivers/w1/w1_io.c This subpatch exports the w1_touch_bit symbol with EXPORT_SYMBOL_GPL, same as the other exported symbols in drivers/w1/w1_io.c May be also useful later for writing drivers for other Onewire chips which do single-bit communication. Signed-off-by: Jan Kandziora Acked-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/w1_io.c | 3 ++- include/linux/w1.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index d191e1f80579..075d120e7b88 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -58,7 +58,7 @@ static u8 w1_read_bit(struct w1_master *dev); * @dev: the master device * @bit: 0 - write a 0, 1 - write a 0 read the level */ -static u8 w1_touch_bit(struct w1_master *dev, int bit) +u8 w1_touch_bit(struct w1_master *dev, int bit) { if (dev->bus_master->touch_bit) return dev->bus_master->touch_bit(dev->bus_master->data, bit); @@ -69,6 +69,7 @@ static u8 w1_touch_bit(struct w1_master *dev, int bit) return 0; } } +EXPORT_SYMBOL_GPL(w1_touch_bit); /** * w1_write_bit() - Generates a write-0 or write-1 cycle. diff --git a/include/linux/w1.h b/include/linux/w1.h index 5b2972946dda..694101f744c7 100644 --- a/include/linux/w1.h +++ b/include/linux/w1.h @@ -293,6 +293,7 @@ void w1_unregister_family(struct w1_family *family); w1_unregister_family) u8 w1_triplet(struct w1_master *dev, int bdir); +u8 w1_touch_bit(struct w1_master *dev, int bit); void w1_write_8(struct w1_master *, u8); u8 w1_read_8(struct w1_master *); int w1_reset_bus(struct w1_master *); -- cgit v1.2.3-59-g8ed1b From c2e5df616e1ae6c2a074cb241ebb65a318ebaf7c Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 21 Sep 2017 20:58:49 -0700 Subject: vmbus: add per-channel sysfs info This extends existing vmbus related sysfs structure to provide per-channel state information. This is useful when diagnosing issues with multiple queues in networking and storage. The existing sysfs only displayed information about the primary channel. The one place it reported multiple channels was the channel_vp_mapping file which violated the sysfs convention of one value per file. Signed-off-by: Stephen Hemminger Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/stable/sysfs-bus-vmbus | 56 ++++++++++ drivers/hv/channel_mgmt.c | 10 +- drivers/hv/hyperv_vmbus.h | 2 + drivers/hv/vmbus_drv.c | 185 +++++++++++++++++++++++++++++-- include/linux/hyperv.h | 6 + 5 files changed, 246 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/stable/sysfs-bus-vmbus b/Documentation/ABI/stable/sysfs-bus-vmbus index 5d0125f7bcaf..0ebd8a1537a0 100644 --- a/Documentation/ABI/stable/sysfs-bus-vmbus +++ b/Documentation/ABI/stable/sysfs-bus-vmbus @@ -41,3 +41,59 @@ KernelVersion: 4.5 Contact: K. Y. Srinivasan Description: The 16 bit vendor ID of the device Users: tools/hv/lsvmbus and user level RDMA libraries + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/cpu +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger +Description: VCPU (sub)channel is affinitized to +Users: tools/hv/lsvmbus and other debuggig tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/cpu +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger +Description: VCPU (sub)channel is affinitized to +Users: tools/hv/lsvmbus and other debuggig tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/in_mask +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger +Description: Inbound channel signaling state +Users: Debuggig tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/latency +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger +Description: Channel signaling latency +Users: Debuggig tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/out_mask +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger +Description: Outbound channel signaling state +Users: Debuggig tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/pending +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger +Description: Channel interrupt pending state +Users: Debuggig tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/read_avail +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger +Description: Bytes availabble to read +Users: Debuggig tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/write_avail +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger +Description: Bytes availabble to write +Users: Debuggig tools diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 060df71c2e8b..dd2dffe816be 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -350,7 +350,7 @@ static void free_channel(struct vmbus_channel *channel) { tasklet_kill(&channel->callback_event); - kfree_rcu(channel, rcu); + kobject_put(&channel->kobj); } static void percpu_channel_enq(void *arg) @@ -513,6 +513,14 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) newchannel->state = CHANNEL_OPEN_STATE; if (!fnew) { + struct hv_device *dev + = newchannel->primary_channel->device_obj; + + if (vmbus_add_channel_kobj(dev, newchannel)) { + atomic_dec(&vmbus_connection.offer_in_progress); + goto err_free_chan; + } + if (channel->sc_creation_callback != NULL) channel->sc_creation_callback(newchannel); return; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 49569f8fe038..de6f01df9592 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -373,6 +373,8 @@ struct hv_device *vmbus_device_create(const uuid_le *type, int vmbus_device_register(struct hv_device *child_device_obj); void vmbus_device_unregister(struct hv_device *device_obj); +int vmbus_add_channel_kobj(struct hv_device *device_obj, + struct vmbus_channel *channel); struct vmbus_channel *relid2channel(u32 relid); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index a9d49f6f6501..a209527b09f5 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -107,28 +107,30 @@ static void print_alias_name(struct hv_device *hv_dev, char *alias_name) sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]); } -static u8 channel_monitor_group(struct vmbus_channel *channel) +static u8 channel_monitor_group(const struct vmbus_channel *channel) { return (u8)channel->offermsg.monitorid / 32; } -static u8 channel_monitor_offset(struct vmbus_channel *channel) +static u8 channel_monitor_offset(const struct vmbus_channel *channel) { return (u8)channel->offermsg.monitorid % 32; } -static u32 channel_pending(struct vmbus_channel *channel, - struct hv_monitor_page *monitor_page) +static u32 channel_pending(const struct vmbus_channel *channel, + const struct hv_monitor_page *monitor_page) { u8 monitor_group = channel_monitor_group(channel); + return monitor_page->trigger_group[monitor_group].pending; } -static u32 channel_latency(struct vmbus_channel *channel, - struct hv_monitor_page *monitor_page) +static u32 channel_latency(const struct vmbus_channel *channel, + const struct hv_monitor_page *monitor_page) { u8 monitor_group = channel_monitor_group(channel); u8 monitor_offset = channel_monitor_offset(channel); + return monitor_page->latency[monitor_group][monitor_offset]; } @@ -1134,6 +1136,145 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver) } EXPORT_SYMBOL_GPL(vmbus_driver_unregister); + +/* + * Called when last reference to channel is gone. + */ +static void vmbus_chan_release(struct kobject *kobj) +{ + struct vmbus_channel *channel + = container_of(kobj, struct vmbus_channel, kobj); + + kfree_rcu(channel, rcu); +} + +struct vmbus_chan_attribute { + struct attribute attr; + ssize_t (*show)(const struct vmbus_channel *chan, char *buf); + ssize_t (*store)(struct vmbus_channel *chan, + const char *buf, size_t count); +}; +#define VMBUS_CHAN_ATTR(_name, _mode, _show, _store) \ + struct vmbus_chan_attribute chan_attr_##_name \ + = __ATTR(_name, _mode, _show, _store) +#define VMBUS_CHAN_ATTR_RW(_name) \ + struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RW(_name) +#define VMBUS_CHAN_ATTR_RO(_name) \ + struct vmbus_chan_attribute chan_attr_##_name = __ATTR_RO(_name) +#define VMBUS_CHAN_ATTR_WO(_name) \ + struct vmbus_chan_attribute chan_attr_##_name = __ATTR_WO(_name) + +static ssize_t vmbus_chan_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + const struct vmbus_chan_attribute *attribute + = container_of(attr, struct vmbus_chan_attribute, attr); + const struct vmbus_channel *chan + = container_of(kobj, struct vmbus_channel, kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(chan, buf); +} + +static const struct sysfs_ops vmbus_chan_sysfs_ops = { + .show = vmbus_chan_attr_show, +}; + +static ssize_t out_mask_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->outbound; + + return sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask); +} +VMBUS_CHAN_ATTR_RO(out_mask); + +static ssize_t in_mask_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->inbound; + + return sprintf(buf, "%u\n", rbi->ring_buffer->interrupt_mask); +} +VMBUS_CHAN_ATTR_RO(in_mask); + +static ssize_t read_avail_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->inbound; + + return sprintf(buf, "%u\n", hv_get_bytes_to_read(rbi)); +} +VMBUS_CHAN_ATTR_RO(read_avail); + +static ssize_t write_avail_show(const struct vmbus_channel *channel, char *buf) +{ + const struct hv_ring_buffer_info *rbi = &channel->outbound; + + return sprintf(buf, "%u\n", hv_get_bytes_to_write(rbi)); +} +VMBUS_CHAN_ATTR_RO(write_avail); + +static ssize_t show_target_cpu(const struct vmbus_channel *channel, char *buf) +{ + return sprintf(buf, "%u\n", channel->target_cpu); +} +VMBUS_CHAN_ATTR(cpu, S_IRUGO, show_target_cpu, NULL); + +static ssize_t channel_pending_show(const struct vmbus_channel *channel, + char *buf) +{ + return sprintf(buf, "%d\n", + channel_pending(channel, + vmbus_connection.monitor_pages[1])); +} +VMBUS_CHAN_ATTR(pending, S_IRUGO, channel_pending_show, NULL); + +static ssize_t channel_latency_show(const struct vmbus_channel *channel, + char *buf) +{ + return sprintf(buf, "%d\n", + channel_latency(channel, + vmbus_connection.monitor_pages[1])); +} +VMBUS_CHAN_ATTR(latency, S_IRUGO, channel_latency_show, NULL); + +static struct attribute *vmbus_chan_attrs[] = { + &chan_attr_out_mask.attr, + &chan_attr_in_mask.attr, + &chan_attr_read_avail.attr, + &chan_attr_write_avail.attr, + &chan_attr_cpu.attr, + &chan_attr_pending.attr, + &chan_attr_latency.attr, + NULL +}; + +static struct kobj_type vmbus_chan_ktype = { + .sysfs_ops = &vmbus_chan_sysfs_ops, + .release = vmbus_chan_release, + .default_attrs = vmbus_chan_attrs, +}; + +/* + * vmbus_add_channel_kobj - setup a sub-directory under device/channels + */ +int vmbus_add_channel_kobj(struct hv_device *dev, struct vmbus_channel *channel) +{ + struct kobject *kobj = &channel->kobj; + u32 relid = channel->offermsg.child_relid; + int ret; + + kobj->kset = dev->channels_kset; + ret = kobject_init_and_add(kobj, &vmbus_chan_ktype, NULL, + "%u", relid); + if (ret) + return ret; + + kobject_uevent(kobj, KOBJ_ADD); + + return 0; +} + /* * vmbus_device_create - Creates and registers a new child device * on the vmbus. @@ -1165,7 +1306,8 @@ struct hv_device *vmbus_device_create(const uuid_le *type, */ int vmbus_device_register(struct hv_device *child_device_obj) { - int ret = 0; + struct kobject *kobj = &child_device_obj->device.kobj; + int ret; dev_set_name(&child_device_obj->device, "%pUl", child_device_obj->channel->offermsg.offer.if_instance.b); @@ -1179,13 +1321,32 @@ int vmbus_device_register(struct hv_device *child_device_obj) * binding...which will eventually call vmbus_match() and vmbus_probe() */ ret = device_register(&child_device_obj->device); - - if (ret) + if (ret) { pr_err("Unable to register child device\n"); - else - pr_debug("child device %s registered\n", - dev_name(&child_device_obj->device)); + return ret; + } + + child_device_obj->channels_kset = kset_create_and_add("channels", + NULL, kobj); + if (!child_device_obj->channels_kset) { + ret = -ENOMEM; + goto err_dev_unregister; + } + + ret = vmbus_add_channel_kobj(child_device_obj, + child_device_obj->channel); + if (ret) { + pr_err("Unable to register primary channeln"); + goto err_kset_unregister; + } + + return 0; + +err_kset_unregister: + kset_unregister(child_device_obj->channels_kset); +err_dev_unregister: + device_unregister(&child_device_obj->device); return ret; } diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index c458d7b7ad19..ef16ee039850 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -828,6 +828,11 @@ struct vmbus_channel { */ struct rcu_head rcu; + /* + * For sysfs per-channel properties. + */ + struct kobject kobj; + /* * For performance critical channels (storage, networking * etc,), Hyper-V has a mechanism to enhance the throughput @@ -1089,6 +1094,7 @@ struct hv_device { struct device device; struct vmbus_channel *channel; + struct kset *channels_kset; }; -- cgit v1.2.3-59-g8ed1b From 7f4fbf79f4d4c00466e1b4158b17c5a095c83f2c Mon Sep 17 00:00:00 2001 From: Sean Wang Date: Wed, 20 Sep 2017 17:40:16 +0800 Subject: reset: mediatek: add reset controller dt-bindings required header for MT7622 SoC Add the reset controller dt-bindings exported from infracfg, pericfg, hifsys and ethsys which could be found on MT7622 SoC. So that we can reference them from within a device-tree file. Signed-off-by: Sean Wang Signed-off-by: Philipp Zabel --- include/dt-bindings/reset/mt7622-reset.h | 94 ++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 include/dt-bindings/reset/mt7622-reset.h (limited to 'include') diff --git a/include/dt-bindings/reset/mt7622-reset.h b/include/dt-bindings/reset/mt7622-reset.h new file mode 100644 index 000000000000..234052f80417 --- /dev/null +++ b/include/dt-bindings/reset/mt7622-reset.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Sean Wang + * + * 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. + */ + +#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT7622 +#define _DT_BINDINGS_RESET_CONTROLLER_MT7622 + +/* INFRACFG resets */ +#define MT7622_INFRA_EMI_REG_RST 0 +#define MT7622_INFRA_DRAMC0_A0_RST 1 +#define MT7622_INFRA_APCIRQ_EINT_RST 3 +#define MT7622_INFRA_APXGPT_RST 4 +#define MT7622_INFRA_SCPSYS_RST 5 +#define MT7622_INFRA_PMIC_WRAP_RST 7 +#define MT7622_INFRA_IRRX_RST 9 +#define MT7622_INFRA_EMI_RST 16 +#define MT7622_INFRA_WED0_RST 17 +#define MT7622_INFRA_DRAMC_RST 18 +#define MT7622_INFRA_CCI_INTF_RST 19 +#define MT7622_INFRA_TRNG_RST 21 +#define MT7622_INFRA_SYSIRQ_RST 22 +#define MT7622_INFRA_WED1_RST 25 + +/* PERICFG Subsystem resets */ +#define MT7622_PERI_UART0_SW_RST 0 +#define MT7622_PERI_UART1_SW_RST 1 +#define MT7622_PERI_UART2_SW_RST 2 +#define MT7622_PERI_UART3_SW_RST 3 +#define MT7622_PERI_UART4_SW_RST 4 +#define MT7622_PERI_BTIF_SW_RST 6 +#define MT7622_PERI_PWM_SW_RST 8 +#define MT7622_PERI_AUXADC_SW_RST 10 +#define MT7622_PERI_DMA_SW_RST 11 +#define MT7622_PERI_IRTX_SW_RST 13 +#define MT7622_PERI_NFI_SW_RST 14 +#define MT7622_PERI_THERM_SW_RST 16 +#define MT7622_PERI_MSDC0_SW_RST 19 +#define MT7622_PERI_MSDC1_SW_RST 20 +#define MT7622_PERI_I2C0_SW_RST 22 +#define MT7622_PERI_I2C1_SW_RST 23 +#define MT7622_PERI_I2C2_SW_RST 24 +#define MT7622_PERI_SPI0_SW_RST 33 +#define MT7622_PERI_SPI1_SW_RST 34 +#define MT7622_PERI_FLASHIF_SW_RST 36 + +/* TOPRGU resets */ +#define MT7622_TOPRGU_INFRA_RST 0 +#define MT7622_TOPRGU_ETHDMA_RST 1 +#define MT7622_TOPRGU_DDRPHY_RST 6 +#define MT7622_TOPRGU_INFRA_AO_RST 8 +#define MT7622_TOPRGU_CONN_RST 9 +#define MT7622_TOPRGU_APMIXED_RST 10 +#define MT7622_TOPRGU_CONN_MCU_RST 12 + +/* PCIe/SATA Subsystem resets */ +#define MT7622_SATA_PHY_REG_RST 12 +#define MT7622_SATA_PHY_SW_RST 13 +#define MT7622_SATA_AXI_BUS_RST 15 +#define MT7622_PCIE1_CORE_RST 19 +#define MT7622_PCIE1_MMIO_RST 20 +#define MT7622_PCIE1_HRST 21 +#define MT7622_PCIE1_USER_RST 22 +#define MT7622_PCIE1_PIPE_RST 23 +#define MT7622_PCIE0_CORE_RST 27 +#define MT7622_PCIE0_MMIO_RST 28 +#define MT7622_PCIE0_HRST 29 +#define MT7622_PCIE0_USER_RST 30 +#define MT7622_PCIE0_PIPE_RST 31 + +/* SSUSB Subsystem resets */ +#define MT7622_SSUSB_PHY_PWR_RST 3 +#define MT7622_SSUSB_MAC_PWR_RST 4 + +/* ETHSYS Subsystem resets */ +#define MT7622_ETHSYS_SYS_RST 0 +#define MT7622_ETHSYS_MCM_RST 2 +#define MT7622_ETHSYS_HSDMA_RST 5 +#define MT7622_ETHSYS_FE_RST 6 +#define MT7622_ETHSYS_GMAC_RST 23 +#define MT7622_ETHSYS_EPHY_RST 24 +#define MT7622_ETHSYS_CRYPTO_RST 29 +#define MT7622_ETHSYS_PPE_RST 31 + +#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT7622 */ -- cgit v1.2.3-59-g8ed1b From 7dd5d0d954611c05a38bdf843054c639f45ce08b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 2 Oct 2017 05:09:52 +0000 Subject: ASoC: soc-core: add component lookup functions ALSA SoC platform/codec will be replaced to component soon. This means 1 device might have multiple components. But current unregister component function only checks "dev" to find it. This means, unexpected component might be unregistered by current function. But, it is no problem if driver registered only 1 component. To prepare avoid this issue, this patch adds new component lookup function. it finds component by "dev" and "driver name". Here, the reason why it uses "driver name" is that "component name" was created by fmt_single_name() and difficult to use it from driver. Driver of course knows its "driver name", thus, using it is more easy. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-core.c | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d776cdee30d7..11ca867033bd 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -475,6 +475,8 @@ int devm_snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_component(struct device *dev); +struct snd_soc_component *snd_soc_lookup_component(struct device *dev, + const char *driver_name); int snd_soc_cache_init(struct snd_soc_codec *codec); int snd_soc_cache_exit(struct snd_soc_codec *codec); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fb3435121172..6ec127346e9c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3481,6 +3481,32 @@ void snd_soc_unregister_component(struct device *dev) } EXPORT_SYMBOL_GPL(snd_soc_unregister_component); +struct snd_soc_component *snd_soc_lookup_component(struct device *dev, + const char *driver_name) +{ + struct snd_soc_component *component; + struct snd_soc_component *ret; + + ret = NULL; + mutex_lock(&client_mutex); + list_for_each_entry(component, &component_list, list) { + if (dev != component->dev) + continue; + + if (driver_name && + (driver_name != component->driver->name) && + (strcmp(component->driver->name, driver_name) != 0)) + continue; + + ret = component; + break; + } + mutex_unlock(&client_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(snd_soc_lookup_component); + static int snd_soc_platform_drv_probe(struct snd_soc_component *component) { struct snd_soc_platform *platform = snd_soc_component_to_platform(component); -- cgit v1.2.3-59-g8ed1b From c3c6dd750e0b8dcee0306b9c5a45708922debbbe Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Sun, 25 Jun 2017 09:31:24 -0300 Subject: [media] media: lirc_dev: remove support for manually specifying minor number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All users of lirc_register_driver() uses dynamic minor allocation, therefore we can remove the ability to explicitly request a given number. This changes the function prototype of lirc_unregister_driver() to also take a struct lirc_driver pointer as the sole argument. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-lirc-codec.c | 9 ++--- drivers/media/rc/lirc_dev.c | 68 +++++++++------------------------ drivers/staging/media/lirc/lirc_zilog.c | 14 +++---- include/media/lirc_dev.h | 20 +++++----- 4 files changed, 34 insertions(+), 77 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index d2223c04e9ad..58bff7a75d5b 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -382,7 +382,6 @@ static int ir_lirc_register(struct rc_dev *dev) snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)", dev->driver_name); - drv->minor = -1; drv->features = features; drv->data = &dev->raw->lirc; drv->rbuf = NULL; @@ -394,11 +393,9 @@ static int ir_lirc_register(struct rc_dev *dev) drv->rdev = dev; drv->owner = THIS_MODULE; - drv->minor = lirc_register_driver(drv); - if (drv->minor < 0) { - rc = -ENODEV; + rc = lirc_register_driver(drv); + if (rc < 0) goto out; - } dev->raw->lirc.drv = drv; dev->raw->lirc.dev = dev; @@ -413,7 +410,7 @@ static int ir_lirc_unregister(struct rc_dev *dev) { struct lirc_codec *lirc = &dev->raw->lirc; - lirc_unregister_driver(lirc->drv->minor); + lirc_unregister_driver(lirc->drv); kfree(lirc->drv); lirc->drv = NULL; diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 5e3c4779d866..f1d8c1ef072e 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -29,7 +29,6 @@ #include #include -#define NOPLUG -1 #define LOGHEAD "lirc_dev (%s[%d]): " static dev_t lirc_base_dev; @@ -114,7 +113,7 @@ out: int lirc_register_driver(struct lirc_driver *d) { struct irctl *ir; - int minor; + unsigned int minor; int err; if (!d) { @@ -132,12 +131,6 @@ int lirc_register_driver(struct lirc_driver *d) return -EINVAL; } - if (d->minor >= MAX_IRCTL_DEVICES) { - dev_err(d->dev, "minor must be between 0 and %d!\n", - MAX_IRCTL_DEVICES - 1); - return -EBADRQC; - } - if (d->code_length < 1 || d->code_length > (BUFLEN * 8)) { dev_err(d->dev, "code length must be less than %d bits\n", BUFLEN * 8); @@ -152,21 +145,14 @@ int lirc_register_driver(struct lirc_driver *d) mutex_lock(&lirc_dev_lock); - minor = d->minor; + /* find first free slot for driver */ + for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) + if (!irctls[minor]) + break; - if (minor < 0) { - /* find first free slot for driver */ - for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) - if (!irctls[minor]) - break; - if (minor == MAX_IRCTL_DEVICES) { - dev_err(d->dev, "no free slots for drivers!\n"); - err = -ENOMEM; - goto out_lock; - } - } else if (irctls[minor]) { - dev_err(d->dev, "minor (%d) just registered!\n", minor); - err = -EBUSY; + if (minor == MAX_IRCTL_DEVICES) { + dev_err(d->dev, "no free slots for drivers!\n"); + err = -ENOMEM; goto out_lock; } @@ -178,6 +164,7 @@ int lirc_register_driver(struct lirc_driver *d) mutex_init(&ir->irctl_lock); irctls[minor] = ir; + d->irctl = ir; d->minor = minor; /* some safety check 8-) */ @@ -225,7 +212,7 @@ int lirc_register_driver(struct lirc_driver *d) dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", ir->d.name, ir->d.minor); - return minor; + return 0; out_cdev: cdev_del(&ir->cdev); @@ -238,38 +225,24 @@ out_lock: } EXPORT_SYMBOL(lirc_register_driver); -int lirc_unregister_driver(int minor) +void lirc_unregister_driver(struct lirc_driver *d) { struct irctl *ir; - if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { - pr_err("minor (%d) must be between 0 and %d!\n", - minor, MAX_IRCTL_DEVICES - 1); - return -EBADRQC; - } + if (!d || !d->irctl) + return; - ir = irctls[minor]; - if (!ir) { - pr_err("failed to get irctl\n"); - return -ENOENT; - } + ir = d->irctl; mutex_lock(&lirc_dev_lock); - if (ir->d.minor != minor) { - dev_err(ir->d.dev, "lirc_dev: minor %d device not registered\n", - minor); - mutex_unlock(&lirc_dev_lock); - return -ENOENT; - } - dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", - ir->d.name, ir->d.minor); + d->name, d->minor); ir->attached = 0; if (ir->open) { dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", - ir->d.name, ir->d.minor); + d->name, d->minor); wake_up_interruptible(&ir->buf->wait_poll); } @@ -278,8 +251,6 @@ int lirc_unregister_driver(int minor) device_del(&ir->dev); cdev_del(&ir->cdev); put_device(&ir->dev); - - return 0; } EXPORT_SYMBOL(lirc_unregister_driver); @@ -306,11 +277,6 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); - if (ir->d.minor == NOPLUG) { - retval = -ENODEV; - goto error; - } - if (ir->open) { retval = -EBUSY; goto error; @@ -403,7 +369,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", ir->d.name, ir->d.minor, cmd); - if (ir->d.minor == NOPLUG || !ir->attached) { + if (!ir->attached) { dev_err(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n", ir->d.name, ir->d.minor); return -ENODEV; diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 71af13bd0ebd..efcbfef1980e 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -183,10 +183,7 @@ static void release_ir_device(struct kref *ref) * ir->open_count == 0 - happens on final close() * ir_lock, tx_ref_lock, rx_ref_lock, all released */ - if (ir->l.minor >= 0) { - lirc_unregister_driver(ir->l.minor); - ir->l.minor = -1; - } + lirc_unregister_driver(&ir->l); if (kfifo_initialized(&ir->rbuf.fifo)) lirc_buffer_free(&ir->rbuf); @@ -1385,7 +1382,6 @@ static const struct file_operations lirc_fops = { static struct lirc_driver lirc_template = { .name = "lirc_zilog", - .minor = -1, .code_length = 13, .buffer_size = BUFLEN / 2, .chunk_size = 2, @@ -1599,14 +1595,14 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* register with lirc */ - ir->l.minor = lirc_register_driver(&ir->l); - if (ir->l.minor < 0) { + ret = lirc_register_driver(&ir->l); + if (ret < 0) { dev_err(tx->ir->l.dev, "%s: lirc_register_driver() failed: %i\n", - __func__, ir->l.minor); - ret = -EBADRQC; + __func__, ret); goto out_put_xx; } + dev_info(ir->l.dev, "IR unit on %s (i2c-%d) registered as lirc%d and ready\n", adap->name, adap->nr, ir->l.minor); diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 86d15a9b6c01..1bb9890744fa 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -116,10 +116,8 @@ static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, * * @name: this string will be used for logs * - * @minor: indicates minor device (/dev/lirc) number for - * registered driver if caller fills it with negative - * value, then the first free minor number will be used - * (if available). + * @minor: the minor device (/dev/lircX) number for a registered + * driver. * * @code_length: length of the remote control key code expressed in bits. * @@ -157,10 +155,12 @@ static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, * device. * * @owner: the module owning this struct + * + * @irctl: the struct irctl for this LIRC device. */ struct lirc_driver { char name[40]; - int minor; + unsigned int minor; __u32 code_length; unsigned int buffer_size; /* in chunks holding one code each */ __u32 features; @@ -175,19 +175,17 @@ struct lirc_driver { const struct file_operations *fops; struct device *dev; struct module *owner; + struct irctl *irctl; }; /* following functions can be called ONLY from user context * - * returns negative value on error or minor number - * of the registered device if success + * returns negative value on error or zero * contents of the structure pointed by p is copied */ -extern int lirc_register_driver(struct lirc_driver *d); +int lirc_register_driver(struct lirc_driver *d); -/* returns negative value on error or 0 if success -*/ -extern int lirc_unregister_driver(int minor); +void lirc_unregister_driver(struct lirc_driver *d); /* Returns the private data stored in the lirc_driver * associated with the given device file pointer. -- cgit v1.2.3-59-g8ed1b From 615cd3fe6cccb950b46728120009a1805cce908e Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Sun, 25 Jun 2017 09:31:40 -0300 Subject: [media] media: lirc_dev: make better use of file->private_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By making better use of file->private_data in lirc_dev we can avoid digging around in the irctls[] array, thereby simplifying the code. External drivers need to use lirc_get_pdata() instead of mucking around in file->private_data. The newly introduced lirc_init_pdata() function isn't very elegant, but it's a stopgap measure which can be removed once lirc_zilog is converted to rc-core. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 70 ++++++++++----------------------- drivers/staging/media/lirc/lirc_zilog.c | 53 +++++-------------------- include/media/lirc_dev.h | 3 ++ 3 files changed, 33 insertions(+), 93 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 057983b8ec53..ffa203eb2045 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -247,36 +247,18 @@ EXPORT_SYMBOL(lirc_unregister_driver); int lirc_dev_fop_open(struct inode *inode, struct file *file) { - struct irctl *ir; + struct irctl *ir = container_of(inode->i_cdev, struct irctl, cdev); int retval; - if (iminor(inode) >= MAX_IRCTL_DEVICES) { - pr_err("open result for %d is -ENODEV\n", iminor(inode)); - return -ENODEV; - } - - if (mutex_lock_interruptible(&lirc_dev_lock)) - return -ERESTARTSYS; - - ir = irctls[iminor(inode)]; - mutex_unlock(&lirc_dev_lock); - - if (!ir) { - retval = -ENODEV; - goto error; - } - dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); - if (ir->open) { - retval = -EBUSY; - goto error; - } + if (ir->open) + return -EBUSY; if (ir->d.rdev) { retval = rc_open(ir->d.rdev); if (retval) - goto error; + return retval; } if (ir->buf) @@ -284,25 +266,18 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) ir->open++; + lirc_init_pdata(inode, file); nonseekable_open(inode, file); return 0; - -error: - return retval; } EXPORT_SYMBOL(lirc_dev_fop_open); int lirc_dev_fop_close(struct inode *inode, struct file *file) { - struct irctl *ir = irctls[iminor(inode)]; + struct irctl *ir = file->private_data; int ret; - if (!ir) { - pr_err("called with invalid irctl\n"); - return -EINVAL; - } - ret = mutex_lock_killable(&lirc_dev_lock); WARN_ON(ret); @@ -318,14 +293,9 @@ EXPORT_SYMBOL(lirc_dev_fop_close); unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) { - struct irctl *ir = irctls[iminor(file_inode(file))]; + struct irctl *ir = file->private_data; unsigned int ret; - if (!ir) { - pr_err("called with invalid irctl\n"); - return POLLERR; - } - if (!ir->attached) return POLLHUP | POLLERR; @@ -348,14 +318,9 @@ EXPORT_SYMBOL(lirc_dev_fop_poll); long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct irctl *ir = file->private_data; __u32 mode; int result = 0; - struct irctl *ir = irctls[iminor(file_inode(file))]; - - if (!ir) { - pr_err("no irctl found!\n"); - return -ENODEV; - } dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", ir->d.name, ir->d.minor, cmd); @@ -432,16 +397,11 @@ ssize_t lirc_dev_fop_read(struct file *file, size_t length, loff_t *ppos) { - struct irctl *ir = irctls[iminor(file_inode(file))]; + struct irctl *ir = file->private_data; unsigned char *buf; int ret = 0, written = 0; DECLARE_WAITQUEUE(wait, current); - if (!ir) { - pr_err("called with invalid irctl\n"); - return -ENODEV; - } - if (!LIRC_CAN_REC(ir->d.features)) return -EINVAL; @@ -532,9 +492,19 @@ out_unlocked: } EXPORT_SYMBOL(lirc_dev_fop_read); +void lirc_init_pdata(struct inode *inode, struct file *file) +{ + struct irctl *ir = container_of(inode->i_cdev, struct irctl, cdev); + + file->private_data = ir; +} +EXPORT_SYMBOL(lirc_init_pdata); + void *lirc_get_pdata(struct file *file) { - return irctls[iminor(file_inode(file))]->d.data; + struct irctl *ir = file->private_data; + + return ir->d.data; } EXPORT_SYMBOL(lirc_get_pdata); diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index efcbfef1980e..c4a4c2f93ae8 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -879,7 +879,7 @@ out: static ssize_t read(struct file *filep, char __user *outbuf, size_t n, loff_t *ppos) { - struct IR *ir = filep->private_data; + struct IR *ir = lirc_get_pdata(filep); struct IR_rx *rx; struct lirc_buffer *rbuf = ir->l.rbuf; int ret = 0, written = 0, retries = 0; @@ -1089,7 +1089,7 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) static ssize_t write(struct file *filep, const char __user *buf, size_t n, loff_t *ppos) { - struct IR *ir = filep->private_data; + struct IR *ir = lirc_get_pdata(filep); struct IR_tx *tx; size_t i; int failures = 0; @@ -1197,7 +1197,7 @@ static ssize_t write(struct file *filep, const char __user *buf, size_t n, /* copied from lirc_dev */ static unsigned int poll(struct file *filep, poll_table *wait) { - struct IR *ir = filep->private_data; + struct IR *ir = lirc_get_pdata(filep); struct IR_rx *rx; struct lirc_buffer *rbuf = ir->l.rbuf; unsigned int ret; @@ -1230,7 +1230,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { - struct IR *ir = filep->private_data; + struct IR *ir = lirc_get_pdata(filep); unsigned long __user *uptr = (unsigned long __user *)arg; int result; unsigned long mode, features; @@ -1280,46 +1280,18 @@ static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) return result; } -static struct IR *get_ir_device_by_minor(unsigned int minor) -{ - struct IR *ir; - struct IR *ret = NULL; - - mutex_lock(&ir_devices_lock); - - if (!list_empty(&ir_devices_list)) { - list_for_each_entry(ir, &ir_devices_list, list) { - if (ir->l.minor == minor) { - ret = get_ir_device(ir, true); - break; - } - } - } - - mutex_unlock(&ir_devices_lock); - return ret; -} - /* - * Open the IR device. Get hold of our IR structure and - * stash it in private_data for the file + * Open the IR device. */ static int open(struct inode *node, struct file *filep) { struct IR *ir; - unsigned int minor = MINOR(node->i_rdev); - - /* find our IR struct */ - ir = get_ir_device_by_minor(minor); - if (!ir) - return -ENODEV; + lirc_init_pdata(node, filep); + ir = lirc_get_pdata(filep); atomic_inc(&ir->open_count); - /* stash our IR struct */ - filep->private_data = ir; - nonseekable_open(node, filep); return 0; } @@ -1327,14 +1299,7 @@ static int open(struct inode *node, struct file *filep) /* Close the IR device */ static int close(struct inode *node, struct file *filep) { - /* find our IR struct */ - struct IR *ir = filep->private_data; - - if (!ir) { - pr_err("ir: %s: no private_data attached to the file!\n", - __func__); - return -ENODEV; - } + struct IR *ir = lirc_get_pdata(filep); atomic_dec(&ir->open_count); @@ -1489,6 +1454,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) */ ir->l.rbuf = &ir->rbuf; ir->l.dev = &adap->dev; + /* This will be returned by lirc_get_pdata() */ + ir->l.data = ir; ret = lirc_buffer_init(ir->l.rbuf, ir->l.chunk_size, ir->l.buffer_size); if (ret) diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 1bb9890744fa..d07a53232ffc 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -187,6 +187,9 @@ int lirc_register_driver(struct lirc_driver *d); void lirc_unregister_driver(struct lirc_driver *d); +/* Must be called in the open fop before lirc_get_pdata() can be used */ +void lirc_init_pdata(struct inode *inode, struct file *file); + /* Returns the private data stored in the lirc_driver * associated with the given device file pointer. */ -- cgit v1.2.3-59-g8ed1b From bf64e0b00e1ff30449cc68e7bc7498adb6faa343 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 3 Oct 2017 21:34:51 +0100 Subject: drm/i915: Expand I915_PARAM_HAS_SCHEDULER into a capability bitmask In the next few patches, we wish to enable different features for the scheduler, some which may subtlety change ABI (e.g. allow requests to be reordered under different circumstances). So we need to make sure userspace is cognizant of the changes (if they care), by which we employ the usual method of a GETPARAM. We already have an I915_PARAM_HAS_SCHEDULER (which notes the existing ability to reorder requests to avoid bubbles), and now we wish to extend that to be a bitmask to describe the different capabilities implemented. Signed-off-by: Chris Wilson Reviewed-by: Joonas Lahtinen Link: https://patchwork.freedesktop.org/patch/msgid/20171003203453.15692-7-chris@chris-wilson.co.uk --- drivers/gpu/drm/i915/i915_drv.c | 5 +++-- include/uapi/drm/i915_drm.h | 9 ++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 74a456fe487f..006332805702 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -367,8 +367,9 @@ static int i915_getparam(struct drm_device *dev, void *data, value = i915_gem_mmap_gtt_version(); break; case I915_PARAM_HAS_SCHEDULER: - value = dev_priv->engine[RCS] && - dev_priv->engine[RCS]->schedule; + value = 0; + if (dev_priv->engine[RCS] && dev_priv->engine[RCS]->schedule) + value |= I915_SCHEDULER_CAP_ENABLED; break; case I915_PARAM_MMAP_VERSION: /* Remember to bump this if the version changes! */ diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index fe25a01c81f2..aa4a3b20ef6b 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -397,10 +397,17 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_MIN_EU_IN_POOL 39 #define I915_PARAM_MMAP_GTT_VERSION 40 -/* Query whether DRM_I915_GEM_EXECBUFFER2 supports user defined execution +/* + * Query whether DRM_I915_GEM_EXECBUFFER2 supports user defined execution * priorities and the driver will attempt to execute batches in priority order. + * The param returns a capability bitmask, nonzero implies that the scheduler + * is enabled, with different features present according to the mask. */ #define I915_PARAM_HAS_SCHEDULER 41 +#define I915_SCHEDULER_CAP_ENABLED (1ul << 0) +#define I915_SCHEDULER_CAP_PRIORITY (1ul << 1) +#define I915_SCHEDULER_CAP_PREEMPTION (1ul << 2) + #define I915_PARAM_HUC_STATUS 42 /* Query whether DRM_I915_GEM_EXECBUFFER2 supports the ability to opt-out of -- cgit v1.2.3-59-g8ed1b From ac14fbd460d0ec16e7750e40dcd8199b0ff83d0a Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 3 Oct 2017 21:34:53 +0100 Subject: drm/i915/scheduler: Support user-defined priorities Use a priority stored in the context as the initial value when submitting a request. This allows us to change the default priority on a per-context basis, allowing different contexts to be favoured with GPU time at the expense of lower importance work. The user can adjust the context's priority via I915_CONTEXT_PARAM_PRIORITY, with more positive values being higher priority (they will be serviced earlier, after their dependencies have been resolved). Any prerequisite work for an execbuf will have its priority raised to match the new request as required. Normal users can specify any value in the range of -1023 to 0 [default], i.e. they can reduce the priority of their workloads (and temporarily boost it back to normal if so desired). Privileged users can specify any value in the range of -1023 to 1023, [default is 0], i.e. they can raise their priority above all overs and so potentially starve the system. Note that the existing schedulers are not fair, nor load balancing, the execution is strictly by priority on a first-come, first-served basis, and the driver may choose to boost some requests above the range available to users. This priority was originally based around nice(2), but evolved to allow clients to adjust their priority within a small range, and allow for a privileged high priority range. For example, this can be used to implement EGL_IMG_context_priority https://www.khronos.org/registry/egl/extensions/IMG/EGL_IMG_context_priority.txt EGL_CONTEXT_PRIORITY_LEVEL_IMG determines the priority level of the context to be created. This attribute is a hint, as an implementation may not support multiple contexts at some priority levels and system policy may limit access to high priority contexts to appropriate system privilege level. The default value for EGL_CONTEXT_PRIORITY_LEVEL_IMG is EGL_CONTEXT_PRIORITY_MEDIUM_IMG." so we can map PRIORITY_HIGH -> 1023 [privileged, will failback to 0] PRIORITY_MED -> 0 [default] PRIORITY_LOW -> -1023 They also map onto the priorities used by VkQueue (and a VkQueue is essentially a timeline, our i915_gem_context under full-ppgtt). v2: s/CAP_SYS_ADMIN/CAP_SYS_NICE/ v3: Report min/max user priorities as defines in the uapi, and rebase internal priorities on the exposed values. Testcase: igt/gem_exec_schedule Signed-off-by: Chris Wilson Reviewed-by: Tvrtko Ursulin Reviewed-by: Joonas Lahtinen Link: https://patchwork.freedesktop.org/patch/msgid/20171003203453.15692-9-chris@chris-wilson.co.uk --- drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/i915_gem_context.c | 23 +++++++++++++++++++++++ drivers/gpu/drm/i915/i915_gem_request.h | 14 ++++++++++---- include/uapi/drm/i915_drm.h | 7 +++++++ 4 files changed, 41 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 7614880edad8..66fc156b294a 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -370,6 +370,7 @@ static int i915_getparam(struct drm_device *dev, void *data, value = 0; if (dev_priv->engine[RCS] && dev_priv->engine[RCS]->schedule) { value |= I915_SCHEDULER_CAP_ENABLED; + value |= I915_SCHEDULER_CAP_PRIORITY; if (INTEL_INFO(dev_priv)->has_logical_ring_preemption && i915_modparams.enable_execlists && diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 2bb8e58706ba..5bf96a258509 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -1070,6 +1070,9 @@ int i915_gem_context_getparam_ioctl(struct drm_device *dev, void *data, case I915_CONTEXT_PARAM_BANNABLE: args->value = i915_gem_context_is_bannable(ctx); break; + case I915_CONTEXT_PARAM_PRIORITY: + args->value = ctx->priority; + break; default: ret = -EINVAL; break; @@ -1125,6 +1128,26 @@ int i915_gem_context_setparam_ioctl(struct drm_device *dev, void *data, else i915_gem_context_clear_bannable(ctx); break; + + case I915_CONTEXT_PARAM_PRIORITY: + { + int priority = args->value; + + if (args->size) + ret = -EINVAL; + else if (!to_i915(dev)->engine[RCS]->schedule) + ret = -ENODEV; + else if (priority > I915_CONTEXT_MAX_USER_PRIORITY || + priority < I915_CONTEXT_MIN_USER_PRIORITY) + ret = -EINVAL; + else if (priority > I915_CONTEXT_DEFAULT_PRIORITY && + !capable(CAP_SYS_NICE)) + ret = -EPERM; + else + ctx->priority = priority; + } + break; + default: ret = -EINVAL; break; diff --git a/drivers/gpu/drm/i915/i915_gem_request.h b/drivers/gpu/drm/i915/i915_gem_request.h index 6b9e992d01de..26249f39de67 100644 --- a/drivers/gpu/drm/i915/i915_gem_request.h +++ b/drivers/gpu/drm/i915/i915_gem_request.h @@ -30,6 +30,8 @@ #include "i915_gem.h" #include "i915_sw_fence.h" +#include + struct drm_file; struct drm_i915_gem_object; struct drm_i915_gem_request; @@ -69,10 +71,14 @@ struct i915_priotree { struct list_head waiters_list; /* those after us, they depend upon us */ struct list_head link; int priority; -#define I915_PRIORITY_MAX 1024 -#define I915_PRIORITY_NORMAL 0 -#define I915_PRIORITY_MIN (-I915_PRIORITY_MAX) -#define I915_PRIORITY_INVALID INT_MIN +}; + +enum { + I915_PRIORITY_MIN = I915_CONTEXT_MIN_USER_PRIORITY - 1, + I915_PRIORITY_NORMAL = I915_CONTEXT_DEFAULT_PRIORITY, + I915_PRIORITY_MAX = I915_CONTEXT_MAX_USER_PRIORITY + 1, + + I915_PRIORITY_INVALID = INT_MIN }; struct i915_gem_capture_list { diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index aa4a3b20ef6b..7266b53191ee 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -402,6 +402,9 @@ typedef struct drm_i915_irq_wait { * priorities and the driver will attempt to execute batches in priority order. * The param returns a capability bitmask, nonzero implies that the scheduler * is enabled, with different features present according to the mask. + * + * The initial priority for each batch is supplied by the context and is + * controlled via I915_CONTEXT_PARAM_PRIORITY. */ #define I915_PARAM_HAS_SCHEDULER 41 #define I915_SCHEDULER_CAP_ENABLED (1ul << 0) @@ -1367,6 +1370,10 @@ struct drm_i915_gem_context_param { #define I915_CONTEXT_PARAM_GTT_SIZE 0x3 #define I915_CONTEXT_PARAM_NO_ERROR_CAPTURE 0x4 #define I915_CONTEXT_PARAM_BANNABLE 0x5 +#define I915_CONTEXT_PARAM_PRIORITY 0x6 +#define I915_CONTEXT_MAX_USER_PRIORITY 1023 /* inclusive */ +#define I915_CONTEXT_DEFAULT_PRIORITY 0 +#define I915_CONTEXT_MIN_USER_PRIORITY -1023 /* inclusive */ __u64 value; }; -- cgit v1.2.3-59-g8ed1b From b145ef94f63e02c2615ffde61a376b53f3367bc6 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Sun, 25 Jun 2017 09:31:45 -0300 Subject: [media] media: lirc_dev: make chunk_size and buffer_size mandatory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make setting chunk_size and buffer_size mandatory for drivers which expect lirc_dev to allocate the lirc_buffer (i.e. ir-lirc-codec) and don't set them in lirc-zilog (which creates its own buffer). Also remove an unnecessary copy of chunk_size in struct irctl (the same information is already available from struct lirc_buffer). Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 26 +++++++++++++------------- drivers/staging/media/lirc/lirc_zilog.c | 5 +---- include/media/lirc_dev.h | 9 +++++---- 3 files changed, 19 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index ffa203eb2045..1915ffc52955 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -41,7 +41,6 @@ struct irctl { struct mutex irctl_lock; struct lirc_buffer *buf; bool buf_internal; - unsigned int chunk_size; struct device dev; struct cdev cdev; @@ -74,16 +73,8 @@ static void lirc_release(struct device *ld) static int lirc_allocate_buffer(struct irctl *ir) { int err = 0; - int bytes_in_key; - unsigned int chunk_size; - unsigned int buffer_size; struct lirc_driver *d = &ir->d; - bytes_in_key = BITS_TO_LONGS(d->code_length) + - (d->code_length % 8 ? 1 : 0); - buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key; - chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key; - if (d->rbuf) { ir->buf = d->rbuf; ir->buf_internal = false; @@ -94,7 +85,7 @@ static int lirc_allocate_buffer(struct irctl *ir) goto out; } - err = lirc_buffer_init(ir->buf, chunk_size, buffer_size); + err = lirc_buffer_init(ir->buf, d->chunk_size, d->buffer_size); if (err) { kfree(ir->buf); ir->buf = NULL; @@ -104,7 +95,6 @@ static int lirc_allocate_buffer(struct irctl *ir) ir->buf_internal = true; d->rbuf = ir->buf; } - ir->chunk_size = ir->buf->chunk_size; out: return err; @@ -131,6 +121,16 @@ int lirc_register_driver(struct lirc_driver *d) return -EINVAL; } + if (!d->rbuf && d->chunk_size < 1) { + pr_err("chunk_size must be set!\n"); + return -EINVAL; + } + + if (!d->rbuf && d->buffer_size < 1) { + pr_err("buffer_size must be set!\n"); + return -EINVAL; + } + if (d->code_length < 1 || d->code_length > (BUFLEN * 8)) { dev_err(d->dev, "code length must be less than %d bits\n", BUFLEN * 8); @@ -407,7 +407,7 @@ ssize_t lirc_dev_fop_read(struct file *file, dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); - buf = kzalloc(ir->chunk_size, GFP_KERNEL); + buf = kzalloc(ir->buf->chunk_size, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -420,7 +420,7 @@ ssize_t lirc_dev_fop_read(struct file *file, goto out_locked; } - if (length % ir->chunk_size) { + if (length % ir->buf->chunk_size) { ret = -EINVAL; goto out_locked; } diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index c4a4c2f93ae8..780b2d9f2f4b 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -1348,8 +1348,6 @@ static const struct file_operations lirc_fops = { static struct lirc_driver lirc_template = { .name = "lirc_zilog", .code_length = 13, - .buffer_size = BUFLEN / 2, - .chunk_size = 2, .fops = &lirc_fops, .owner = THIS_MODULE, }; @@ -1456,8 +1454,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->l.dev = &adap->dev; /* This will be returned by lirc_get_pdata() */ ir->l.data = ir; - ret = lirc_buffer_init(ir->l.rbuf, - ir->l.chunk_size, ir->l.buffer_size); + ret = lirc_buffer_init(ir->l.rbuf, 2, BUFLEN / 2); if (ret) goto out_put_ir; } diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index d07a53232ffc..8e3894e2d2c8 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -121,13 +121,14 @@ static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, * * @code_length: length of the remote control key code expressed in bits. * - * @buffer_size: Number of FIFO buffers with @chunk_size size. If zero, - * creates a buffer with BUFLEN size (16 bytes). - * * @features: lirc compatible hardware features, like LIRC_MODE_RAW, * LIRC_CAN\_\*, as defined at include/media/lirc.h. * + * @buffer_size: Number of FIFO buffers with @chunk_size size. + * Only used if @rbuf is NULL. + * * @chunk_size: Size of each FIFO buffer. + * Only used if @rbuf is NULL. * * @data: it may point to any driver data and this pointer will * be passed to all callback functions. @@ -162,9 +163,9 @@ struct lirc_driver { char name[40]; unsigned int minor; __u32 code_length; - unsigned int buffer_size; /* in chunks holding one code each */ __u32 features; + unsigned int buffer_size; /* in chunks holding one code each */ unsigned int chunk_size; void *data; -- cgit v1.2.3-59-g8ed1b From 46c8f4771154eb0dc21f5f2bc2640a33e8fe1d02 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Sun, 25 Jun 2017 09:32:05 -0300 Subject: [media] media: lirc_dev: use an IDA instead of an array to keep track of registered devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the kernel-provided IDA simplifies the code and makes it possible to remove the lirc_dev_lock mutex. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 36 ++++++++++++------------------------ include/media/lirc_dev.h | 1 - 2 files changed, 12 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index c83fffec0681..a2c5ed0181c1 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -46,10 +47,9 @@ struct irctl { struct cdev cdev; }; -/* This mutex protects the irctls array */ -static DEFINE_MUTEX(lirc_dev_lock); - -static struct irctl *irctls[MAX_IRCTL_DEVICES]; +/* Used to keep track of allocated lirc devices */ +#define LIRC_MAX_DEVICES 256 +static DEFINE_IDA(lirc_ida); /* Only used for sysfs but defined to void otherwise */ static struct class *lirc_class; @@ -69,9 +69,6 @@ static void lirc_release(struct device *ld) { struct irctl *ir = container_of(ld, struct irctl, dev); - mutex_lock(&lirc_dev_lock); - irctls[ir->d.minor] = NULL; - mutex_unlock(&lirc_dev_lock); lirc_free_buffer(ir); kfree(ir); } @@ -109,7 +106,7 @@ out: int lirc_register_driver(struct lirc_driver *d) { struct irctl *ir; - unsigned int minor; + int minor; int err; if (!d) { @@ -171,28 +168,17 @@ int lirc_register_driver(struct lirc_driver *d) d->rbuf = ir->buf; } - mutex_lock(&lirc_dev_lock); - - /* find first free slot for driver */ - for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) - if (!irctls[minor]) - break; - - if (minor == MAX_IRCTL_DEVICES) { - dev_err(d->dev, "no free slots for drivers!\n"); - mutex_unlock(&lirc_dev_lock); + minor = ida_simple_get(&lirc_ida, 0, LIRC_MAX_DEVICES, GFP_KERNEL); + if (minor < 0) { lirc_free_buffer(ir); kfree(ir); - return -ENOMEM; + return minor; } - irctls[minor] = ir; d->irctl = ir; d->minor = minor; ir->d.minor = minor; - mutex_unlock(&lirc_dev_lock); - device_initialize(&ir->dev); ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor); ir->dev.class = lirc_class; @@ -206,6 +192,7 @@ int lirc_register_driver(struct lirc_driver *d) err = cdev_device_add(&ir->cdev, &ir->dev); if (err) { + ida_simple_remove(&lirc_ida, minor); put_device(&ir->dev); return err; } @@ -244,6 +231,7 @@ void lirc_unregister_driver(struct lirc_driver *d) mutex_unlock(&ir->mutex); + ida_simple_remove(&lirc_ida, d->minor); put_device(&ir->dev); } EXPORT_SYMBOL(lirc_unregister_driver); @@ -540,7 +528,7 @@ static int __init lirc_dev_init(void) return PTR_ERR(lirc_class); } - retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, + retval = alloc_chrdev_region(&lirc_base_dev, 0, LIRC_MAX_DEVICES, "BaseRemoteCtl"); if (retval) { class_destroy(lirc_class); @@ -557,7 +545,7 @@ static int __init lirc_dev_init(void) static void __exit lirc_dev_exit(void) { class_destroy(lirc_class); - unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES); + unregister_chrdev_region(lirc_base_dev, LIRC_MAX_DEVICES); pr_info("module unloaded\n"); } diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 8e3894e2d2c8..51c15c050e85 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -9,7 +9,6 @@ #ifndef _LINUX_LIRC_DEV_H #define _LINUX_LIRC_DEV_H -#define MAX_IRCTL_DEVICES 8 #define BUFLEN 16 #include -- cgit v1.2.3-59-g8ed1b From 5ddc9c098dc3f91243840cec12a2170e9ab9f33a Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Thu, 21 Sep 2017 16:13:34 -0300 Subject: [media] media: rename struct lirc_driver to struct lirc_dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is in preparation for the later patches which do away with struct irctl entirely. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-lirc-codec.c | 52 ++++++++++++++++----------------- drivers/media/rc/lirc_dev.c | 12 ++++---- drivers/media/rc/rc-core-priv.h | 2 +- drivers/staging/media/lirc/lirc_zilog.c | 12 ++++---- include/media/lirc_dev.h | 48 +++++++++--------------------- 5 files changed, 52 insertions(+), 74 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index 58bff7a75d5b..2d591168c991 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -35,7 +35,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) struct lirc_codec *lirc = &dev->raw->lirc; int sample; - if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf) + if (!dev->raw->lirc.ldev || !dev->raw->lirc.ldev->rbuf) return -EINVAL; /* Packet start */ @@ -84,8 +84,8 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) (u64)LIRC_VALUE_MASK); gap_sample = LIRC_SPACE(lirc->gap_duration); - lirc_buffer_write(dev->raw->lirc.drv->rbuf, - (unsigned char *) &gap_sample); + lirc_buffer_write(dev->raw->lirc.ldev->rbuf, + (unsigned char *)&gap_sample); lirc->gap = false; } @@ -95,9 +95,9 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) TO_US(ev.duration), TO_STR(ev.pulse)); } - lirc_buffer_write(dev->raw->lirc.drv->rbuf, + lirc_buffer_write(dev->raw->lirc.ldev->rbuf, (unsigned char *) &sample); - wake_up(&dev->raw->lirc.drv->rbuf->wait_poll); + wake_up(&dev->raw->lirc.ldev->rbuf->wait_poll); return 0; } @@ -343,12 +343,12 @@ static const struct file_operations lirc_fops = { static int ir_lirc_register(struct rc_dev *dev) { - struct lirc_driver *drv; + struct lirc_dev *ldev; int rc = -ENOMEM; unsigned long features = 0; - drv = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); - if (!drv) + ldev = kzalloc(sizeof(*ldev), GFP_KERNEL); + if (!ldev) return rc; if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { @@ -380,29 +380,29 @@ static int ir_lirc_register(struct rc_dev *dev) if (dev->max_timeout) features |= LIRC_CAN_SET_REC_TIMEOUT; - snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)", + snprintf(ldev->name, sizeof(ldev->name), "ir-lirc-codec (%s)", dev->driver_name); - drv->features = features; - drv->data = &dev->raw->lirc; - drv->rbuf = NULL; - drv->code_length = sizeof(struct ir_raw_event) * 8; - drv->chunk_size = sizeof(int); - drv->buffer_size = LIRCBUF_SIZE; - drv->fops = &lirc_fops; - drv->dev = &dev->dev; - drv->rdev = dev; - drv->owner = THIS_MODULE; - - rc = lirc_register_driver(drv); + ldev->features = features; + ldev->data = &dev->raw->lirc; + ldev->rbuf = NULL; + ldev->code_length = sizeof(struct ir_raw_event) * 8; + ldev->chunk_size = sizeof(int); + ldev->buffer_size = LIRCBUF_SIZE; + ldev->fops = &lirc_fops; + ldev->dev = &dev->dev; + ldev->rdev = dev; + ldev->owner = THIS_MODULE; + + rc = lirc_register_device(ldev); if (rc < 0) goto out; - dev->raw->lirc.drv = drv; + dev->raw->lirc.ldev = ldev; dev->raw->lirc.dev = dev; return 0; out: - kfree(drv); + kfree(ldev); return rc; } @@ -410,9 +410,9 @@ static int ir_lirc_unregister(struct rc_dev *dev) { struct lirc_codec *lirc = &dev->raw->lirc; - lirc_unregister_driver(lirc->drv); - kfree(lirc->drv); - lirc->drv = NULL; + lirc_unregister_device(lirc->ldev); + kfree(lirc->ldev); + lirc->ldev = NULL; return 0; } diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index a2c5ed0181c1..e381a1c04bea 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -35,7 +35,7 @@ static dev_t lirc_base_dev; struct irctl { - struct lirc_driver d; + struct lirc_dev d; bool attached; int open; @@ -76,7 +76,7 @@ static void lirc_release(struct device *ld) static int lirc_allocate_buffer(struct irctl *ir) { int err = 0; - struct lirc_driver *d = &ir->d; + struct lirc_dev *d = &ir->d; if (d->rbuf) { ir->buf = d->rbuf; @@ -103,7 +103,7 @@ out: return err; } -int lirc_register_driver(struct lirc_driver *d) +int lirc_register_device(struct lirc_dev *d) { struct irctl *ir; int minor; @@ -204,9 +204,9 @@ int lirc_register_driver(struct lirc_driver *d) return 0; } -EXPORT_SYMBOL(lirc_register_driver); +EXPORT_SYMBOL(lirc_register_device); -void lirc_unregister_driver(struct lirc_driver *d) +void lirc_unregister_device(struct lirc_dev *d) { struct irctl *ir; @@ -234,7 +234,7 @@ void lirc_unregister_driver(struct lirc_driver *d) ida_simple_remove(&lirc_ida, d->minor); put_device(&ir->dev); } -EXPORT_SYMBOL(lirc_unregister_driver); +EXPORT_SYMBOL(lirc_unregister_device); int lirc_dev_fop_open(struct inode *inode, struct file *file) { diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 7da9c96cb058..ae4dd0c27731 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -106,7 +106,7 @@ struct ir_raw_event_ctrl { } mce_kbd; struct lirc_codec { struct rc_dev *dev; - struct lirc_driver *drv; + struct lirc_dev *ldev; int carrier_low; ktime_t gap_start; diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 780b2d9f2f4b..0766e5029bd7 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -100,7 +100,7 @@ struct IR { struct list_head list; /* FIXME spinlock access to l.features */ - struct lirc_driver l; + struct lirc_dev l; struct lirc_buffer rbuf; struct mutex ir_lock; @@ -183,7 +183,7 @@ static void release_ir_device(struct kref *ref) * ir->open_count == 0 - happens on final close() * ir_lock, tx_ref_lock, rx_ref_lock, all released */ - lirc_unregister_driver(&ir->l); + lirc_unregister_device(&ir->l); if (kfifo_initialized(&ir->rbuf.fifo)) lirc_buffer_free(&ir->rbuf); @@ -1345,7 +1345,7 @@ static const struct file_operations lirc_fops = { .release = close }; -static struct lirc_driver lirc_template = { +static struct lirc_dev lirc_template = { .name = "lirc_zilog", .code_length = 13, .fops = &lirc_fops, @@ -1441,7 +1441,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) spin_lock_init(&ir->rx_ref_lock); /* set lirc_dev stuff */ - memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); + memcpy(&ir->l, &lirc_template, sizeof(struct lirc_dev)); /* * FIXME this is a pointer reference to us, but no refcount. * @@ -1559,10 +1559,10 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* register with lirc */ - ret = lirc_register_driver(&ir->l); + ret = lirc_register_device(&ir->l); if (ret < 0) { dev_err(tx->ir->l.dev, - "%s: lirc_register_driver() failed: %i\n", + "%s: lirc_register_device() failed: %i\n", __func__, ret); goto out_put_xx; } diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 51c15c050e85..d16d6e0ef8da 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -111,54 +111,32 @@ static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, } /** - * struct lirc_driver - Defines the parameters on a LIRC driver - * - * @name: this string will be used for logs - * - * @minor: the minor device (/dev/lircX) number for a registered - * driver. - * - * @code_length: length of the remote control key code expressed in bits. + * struct lirc_dev - represents a LIRC device * + * @name: used for logging + * @minor: the minor device (/dev/lircX) number for the device + * @code_length: length of a remote control key code expressed in bits * @features: lirc compatible hardware features, like LIRC_MODE_RAW, * LIRC_CAN\_\*, as defined at include/media/lirc.h. - * * @buffer_size: Number of FIFO buffers with @chunk_size size. * Only used if @rbuf is NULL. - * * @chunk_size: Size of each FIFO buffer. * Only used if @rbuf is NULL. - * - * @data: it may point to any driver data and this pointer will - * be passed to all callback functions. - * + * @data: private per-driver data * @min_timeout: Minimum timeout for record. Valid only if * LIRC_CAN_SET_REC_TIMEOUT is defined. - * * @max_timeout: Maximum timeout for record. Valid only if * LIRC_CAN_SET_REC_TIMEOUT is defined. - * * @rbuf: if not NULL, it will be used as a read buffer, you will * have to write to the buffer by other means, like irq's * (see also lirc_serial.c). - * - * @rdev: Pointed to struct rc_dev associated with the LIRC - * device. - * - * @fops: file_operations for drivers which don't fit the current - * driver model. - * Some ioctl's can be directly handled by lirc_dev if the - * driver's ioctl function is NULL or if it returns - * -ENOIOCTLCMD (see also lirc_serial.c). - * - * @dev: pointer to the struct device associated with the LIRC - * device. - * + * @rdev: &struct rc_dev associated with the device + * @fops: &struct file_operations for the device + * @dev: &struct device assigned to the device * @owner: the module owning this struct - * - * @irctl: the struct irctl for this LIRC device. + * @irctl: &struct irctl assigned to the device */ -struct lirc_driver { +struct lirc_dev { char name[40]; unsigned int minor; __u32 code_length; @@ -183,14 +161,14 @@ struct lirc_driver { * returns negative value on error or zero * contents of the structure pointed by p is copied */ -int lirc_register_driver(struct lirc_driver *d); +int lirc_register_device(struct lirc_dev *d); -void lirc_unregister_driver(struct lirc_driver *d); +void lirc_unregister_device(struct lirc_dev *d); /* Must be called in the open fop before lirc_get_pdata() can be used */ void lirc_init_pdata(struct inode *inode, struct file *file); -/* Returns the private data stored in the lirc_driver +/* Returns the private data stored in the lirc_dev * associated with the given device file pointer. */ void *lirc_get_pdata(struct file *file); -- cgit v1.2.3-59-g8ed1b From 6ecccc379b7334c02f90a401dafea6fce5c91310 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Sun, 25 Jun 2017 09:32:15 -0300 Subject: [media] media: lirc_dev: introduce lirc_allocate_device and lirc_free_device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce two new functions so that the API for lirc_dev matches that of the rc-core and input subsystems. This means that lirc_dev structs are managed using the usual four functions: lirc_allocate_device lirc_free_device lirc_register_device lirc_unregister_device The functions are pretty simplistic at this point, later patches will put more flesh on the bones of both. Signed-off-by: David Härdeman Signed-off-by: Sean Young --- drivers/media/rc/ir-lirc-codec.c | 2 +- drivers/media/rc/lirc_dev.c | 13 +++++++++++++ include/media/lirc_dev.h | 9 ++++----- 3 files changed, 18 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index 2d591168c991..d5c155a5a547 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -347,7 +347,7 @@ static int ir_lirc_register(struct rc_dev *dev) int rc = -ENOMEM; unsigned long features = 0; - ldev = kzalloc(sizeof(*ldev), GFP_KERNEL); + ldev = lirc_allocate_device(); if (!ldev) return rc; diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index e381a1c04bea..a6005f70de5a 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -103,6 +103,19 @@ out: return err; } +struct lirc_dev * +lirc_allocate_device(void) +{ + return kzalloc(sizeof(struct lirc_dev), GFP_KERNEL); +} +EXPORT_SYMBOL(lirc_allocate_device); + +void lirc_free_device(struct lirc_dev *d) +{ + kfree(d); +} +EXPORT_SYMBOL(lirc_free_device); + int lirc_register_device(struct lirc_dev *d) { struct irctl *ir; diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index d16d6e0ef8da..4b0dc640e142 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -156,11 +156,10 @@ struct lirc_dev { struct irctl *irctl; }; -/* following functions can be called ONLY from user context - * - * returns negative value on error or zero - * contents of the structure pointed by p is copied - */ +struct lirc_dev *lirc_allocate_device(void); + +void lirc_free_device(struct lirc_dev *d); + int lirc_register_device(struct lirc_dev *d); void lirc_unregister_device(struct lirc_dev *d); -- cgit v1.2.3-59-g8ed1b From 85009b4f5f0399669a44f07cb9a5622c0e71d419 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sat, 30 Sep 2017 02:09:06 -0600 Subject: writeback: eliminate work item allocation in bd_start_writeback() Handle start-all writeback like we do periodic or kupdate style writeback - by marking the bdi_writeback as needing a full flush, and simply waking the thread. This eliminates the need to allocate and queue a specific work item just for this purpose. After this change, we truly only ever have one of them running at any point in time. We mark the need to start all flushes, and the writeback thread will clear it once it has processed the request. Reviewed-by: Jan Kara Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 71 +++++++++++++++++++--------------------- include/linux/backing-dev-defs.h | 23 +++++++++++++ include/linux/writeback.h | 22 ------------- include/trace/events/writeback.h | 1 - 4 files changed, 57 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 399619c97567..9e24d604c59c 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -53,7 +53,6 @@ struct wb_writeback_work { unsigned int for_background:1; unsigned int for_sync:1; /* sync(2) WB_SYNC_ALL writeback */ unsigned int auto_free:1; /* free on completion */ - unsigned int start_all:1; /* nr_pages == 0 (all) writeback */ enum wb_reason reason; /* why was writeback initiated? */ struct list_head list; /* pending work list */ @@ -947,8 +946,6 @@ static unsigned long get_nr_dirty_pages(void) static void wb_start_writeback(struct bdi_writeback *wb, enum wb_reason reason) { - struct wb_writeback_work *work; - if (!wb_has_dirty_io(wb)) return; @@ -958,35 +955,14 @@ static void wb_start_writeback(struct bdi_writeback *wb, enum wb_reason reason) * high frequency, causing pointless allocations of tons of * work items and keeping the flusher threads busy retrieving * that work. Ensure that we only allow one of them pending and - * inflight at the time. It doesn't matter if we race a little - * bit on this, so use the faster separate test/set bit variants. + * inflight at the time. */ - if (test_bit(WB_start_all, &wb->state)) + if (test_bit(WB_start_all, &wb->state) || + test_and_set_bit(WB_start_all, &wb->state)) return; - set_bit(WB_start_all, &wb->state); - - /* - * This is WB_SYNC_NONE writeback, so if allocation fails just - * wakeup the thread for old dirty data writeback - */ - work = kzalloc(sizeof(*work), - GFP_NOWAIT | __GFP_NOMEMALLOC | __GFP_NOWARN); - if (!work) { - clear_bit(WB_start_all, &wb->state); - trace_writeback_nowork(wb); - wb_wakeup(wb); - return; - } - - work->sync_mode = WB_SYNC_NONE; - work->nr_pages = wb_split_bdi_pages(wb, get_nr_dirty_pages()); - work->range_cyclic = 1; - work->reason = reason; - work->auto_free = 1; - work->start_all = 1; - - wb_queue_work(wb, work); + wb->start_all_reason = reason; + wb_wakeup(wb); } /** @@ -1838,14 +1814,6 @@ static struct wb_writeback_work *get_next_work_item(struct bdi_writeback *wb) list_del_init(&work->list); } spin_unlock_bh(&wb->work_lock); - - /* - * Once we start processing a work item that had !nr_pages, - * clear the wb state bit for that so we can allow more. - */ - if (work && work->start_all) - clear_bit(WB_start_all, &wb->state); - return work; } @@ -1901,6 +1869,30 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb) return 0; } +static long wb_check_start_all(struct bdi_writeback *wb) +{ + long nr_pages; + + if (!test_bit(WB_start_all, &wb->state)) + return 0; + + nr_pages = get_nr_dirty_pages(); + if (nr_pages) { + struct wb_writeback_work work = { + .nr_pages = wb_split_bdi_pages(wb, nr_pages), + .sync_mode = WB_SYNC_NONE, + .range_cyclic = 1, + .reason = wb->start_all_reason, + }; + + nr_pages = wb_writeback(wb, &work); + } + + clear_bit(WB_start_all, &wb->state); + return nr_pages; +} + + /* * Retrieve work items and do the writeback they describe */ @@ -1916,6 +1908,11 @@ static long wb_do_writeback(struct bdi_writeback *wb) finish_writeback_work(wb, work); } + /* + * Check for a flush-everything request + */ + wrote += wb_check_start_all(wb); + /* * Check for periodic writeback, kupdated() style */ diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h index 420de5c7c7f9..b7c7be6f5986 100644 --- a/include/linux/backing-dev-defs.h +++ b/include/linux/backing-dev-defs.h @@ -44,6 +44,28 @@ enum wb_stat_item { #define WB_STAT_BATCH (8*(1+ilog2(nr_cpu_ids))) +/* + * why some writeback work was initiated + */ +enum wb_reason { + WB_REASON_BACKGROUND, + WB_REASON_VMSCAN, + WB_REASON_SYNC, + WB_REASON_PERIODIC, + WB_REASON_LAPTOP_TIMER, + WB_REASON_FREE_MORE_MEM, + WB_REASON_FS_FREE_SPACE, + /* + * There is no bdi forker thread any more and works are done + * by emergency worker, however, this is TPs userland visible + * and we'll be exposing exactly the same information, + * so it has a mismatch name. + */ + WB_REASON_FORKER_THREAD, + + WB_REASON_MAX, +}; + /* * For cgroup writeback, multiple wb's may map to the same blkcg. Those * wb's can operate mostly independently but should share the congested @@ -116,6 +138,7 @@ struct bdi_writeback { struct fprop_local_percpu completions; int dirty_exceeded; + enum wb_reason start_all_reason; spinlock_t work_lock; /* protects work_list & dwork scheduling */ struct list_head work_list; diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 9c0091678af4..dd1d2c23f743 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -41,28 +41,6 @@ enum writeback_sync_modes { WB_SYNC_ALL, /* Wait on every mapping */ }; -/* - * why some writeback work was initiated - */ -enum wb_reason { - WB_REASON_BACKGROUND, - WB_REASON_VMSCAN, - WB_REASON_SYNC, - WB_REASON_PERIODIC, - WB_REASON_LAPTOP_TIMER, - WB_REASON_FREE_MORE_MEM, - WB_REASON_FS_FREE_SPACE, - /* - * There is no bdi forker thread any more and works are done - * by emergency worker, however, this is TPs userland visible - * and we'll be exposing exactly the same information, - * so it has a mismatch name. - */ - WB_REASON_FORKER_THREAD, - - WB_REASON_MAX, -}; - /* * A control structure which tells the writeback code what to do. These are * always on the stack, and hence need no locking. They are always initialised diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h index 9b57f014d79d..19a0ea08e098 100644 --- a/include/trace/events/writeback.h +++ b/include/trace/events/writeback.h @@ -286,7 +286,6 @@ DEFINE_EVENT(writeback_class, name, \ TP_PROTO(struct bdi_writeback *wb), \ TP_ARGS(wb)) -DEFINE_WRITEBACK_EVENT(writeback_nowork); DEFINE_WRITEBACK_EVENT(writeback_wake_background); TRACE_EVENT(writeback_bdi_register, -- cgit v1.2.3-59-g8ed1b From e774d96b7d2c3489bfb5bbdc2b65ed41cd68d3d5 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 4 Oct 2017 15:55:29 +0200 Subject: rtnetlink: remove slave_validate callback no users in the tree. Signed-off-by: Florian Westphal Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/rtnetlink.h | 3 --- net/core/rtnetlink.c | 6 ------ 2 files changed, 9 deletions(-) (limited to 'include') diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 21837ca68ecc..6520993ff449 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -93,9 +93,6 @@ struct rtnl_link_ops { int slave_maxtype; const struct nla_policy *slave_policy; - int (*slave_validate)(struct nlattr *tb[], - struct nlattr *data[], - struct netlink_ext_ack *extack); int (*slave_changelink)(struct net_device *dev, struct net_device *slave_dev, struct nlattr *tb[], diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 3961f87cdc76..b63c5759641f 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2631,12 +2631,6 @@ replay: return err; slave_data = slave_attr; } - if (m_ops->slave_validate) { - err = m_ops->slave_validate(tb, slave_data, - extack); - if (err < 0) - return err; - } } if (dev) { -- cgit v1.2.3-59-g8ed1b From 5c45121dc39026ab2139910e57cf933fd57d30f2 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 4 Oct 2017 15:58:49 +0200 Subject: rtnetlink: remove __rtnl_af_unregister switch the only caller to rtnl_af_unregister. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/rtnetlink.h | 2 -- net/core/rtnetlink.c | 14 +------------- net/ipv6/addrconf.c | 4 ++-- 3 files changed, 3 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 6520993ff449..e3ca8e2e3103 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -151,8 +151,6 @@ struct rtnl_af_ops { size_t (*get_stats_af_size)(const struct net_device *dev); }; -void __rtnl_af_unregister(struct rtnl_af_ops *ops); - void rtnl_af_register(struct rtnl_af_ops *ops); void rtnl_af_unregister(struct rtnl_af_ops *ops); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index b63c5759641f..3fb1ca33cba4 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -475,18 +475,6 @@ void rtnl_af_register(struct rtnl_af_ops *ops) } EXPORT_SYMBOL_GPL(rtnl_af_register); -/** - * __rtnl_af_unregister - Unregister rtnl_af_ops from rtnetlink. - * @ops: struct rtnl_af_ops * to unregister - * - * The caller must hold the rtnl_mutex. - */ -void __rtnl_af_unregister(struct rtnl_af_ops *ops) -{ - list_del(&ops->list); -} -EXPORT_SYMBOL_GPL(__rtnl_af_unregister); - /** * rtnl_af_unregister - Unregister rtnl_af_ops from rtnetlink. * @ops: struct rtnl_af_ops * to unregister @@ -494,7 +482,7 @@ EXPORT_SYMBOL_GPL(__rtnl_af_unregister); void rtnl_af_unregister(struct rtnl_af_ops *ops) { rtnl_lock(); - __rtnl_af_unregister(ops); + list_del(&ops->list); rtnl_unlock(); } EXPORT_SYMBOL_GPL(rtnl_af_unregister); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f553f72d0bee..837418ff2d4b 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6618,9 +6618,9 @@ void addrconf_cleanup(void) unregister_pernet_subsys(&addrconf_ops); ipv6_addr_label_cleanup(); - rtnl_lock(); + rtnl_af_unregister(&inet6_ops); - __rtnl_af_unregister(&inet6_ops); + rtnl_lock(); /* clean dev list */ for_each_netdev(&init_net, dev) { -- cgit v1.2.3-59-g8ed1b From b15e39379fe8700fe0ec849a5c5ee2b44cd16381 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Sun, 25 Jun 2017 09:32:36 -0300 Subject: [media] media: lirc_dev: merge struct irctl into struct lirc_dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The use of two separate structs (lirc_dev aka lirc_driver and irctl) makes it much harder to follow the proper lifetime of the various structs and necessitates hacks such as keeping a copy of struct lirc_dev inside struct irctl. Merging the two structs means that lirc_dev can properly manage the lifetime of the resulting struct and simplifies the code at the same time. [mchehab@s-opensource.com: fix merge conflict] Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/ir-lirc-codec.c | 15 +- drivers/media/rc/lirc_dev.c | 314 ++++++++++++++------------------ drivers/staging/media/lirc/lirc_zilog.c | 20 +- include/media/lirc_dev.h | 26 ++- 4 files changed, 175 insertions(+), 200 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index d5c155a5a547..bd046c41a53a 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -35,7 +35,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) struct lirc_codec *lirc = &dev->raw->lirc; int sample; - if (!dev->raw->lirc.ldev || !dev->raw->lirc.ldev->rbuf) + if (!dev->raw->lirc.ldev || !dev->raw->lirc.ldev->buf) return -EINVAL; /* Packet start */ @@ -84,7 +84,7 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) (u64)LIRC_VALUE_MASK); gap_sample = LIRC_SPACE(lirc->gap_duration); - lirc_buffer_write(dev->raw->lirc.ldev->rbuf, + lirc_buffer_write(dev->raw->lirc.ldev->buf, (unsigned char *)&gap_sample); lirc->gap = false; } @@ -95,9 +95,9 @@ static int ir_lirc_decode(struct rc_dev *dev, struct ir_raw_event ev) TO_US(ev.duration), TO_STR(ev.pulse)); } - lirc_buffer_write(dev->raw->lirc.ldev->rbuf, + lirc_buffer_write(dev->raw->lirc.ldev->buf, (unsigned char *) &sample); - wake_up(&dev->raw->lirc.ldev->rbuf->wait_poll); + wake_up(&dev->raw->lirc.ldev->buf->wait_poll); return 0; } @@ -384,12 +384,12 @@ static int ir_lirc_register(struct rc_dev *dev) dev->driver_name); ldev->features = features; ldev->data = &dev->raw->lirc; - ldev->rbuf = NULL; + ldev->buf = NULL; ldev->code_length = sizeof(struct ir_raw_event) * 8; ldev->chunk_size = sizeof(int); ldev->buffer_size = LIRCBUF_SIZE; ldev->fops = &lirc_fops; - ldev->dev = &dev->dev; + ldev->dev.parent = &dev->dev; ldev->rdev = dev; ldev->owner = THIS_MODULE; @@ -402,7 +402,7 @@ static int ir_lirc_register(struct rc_dev *dev) return 0; out: - kfree(ldev); + lirc_free_device(ldev); return rc; } @@ -411,7 +411,6 @@ static int ir_lirc_unregister(struct rc_dev *dev) struct lirc_codec *lirc = &dev->raw->lirc; lirc_unregister_device(lirc->ldev); - kfree(lirc->ldev); lirc->ldev = NULL; return 0; diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index a6005f70de5a..e9dae8621670 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -34,19 +34,6 @@ static dev_t lirc_base_dev; -struct irctl { - struct lirc_dev d; - bool attached; - int open; - - struct mutex mutex; /* protect from simultaneous accesses */ - struct lirc_buffer *buf; - bool buf_internal; - - struct device dev; - struct cdev cdev; -}; - /* Used to keep track of allocated lirc devices */ #define LIRC_MAX_DEVICES 256 static DEFINE_IDA(lirc_ida); @@ -54,71 +41,74 @@ static DEFINE_IDA(lirc_ida); /* Only used for sysfs but defined to void otherwise */ static struct class *lirc_class; -static void lirc_free_buffer(struct irctl *ir) +static void lirc_release_device(struct device *ld) { - put_device(ir->dev.parent); - - if (ir->buf_internal) { - lirc_buffer_free(ir->buf); - kfree(ir->buf); - ir->buf = NULL; - } -} + struct lirc_dev *d = container_of(ld, struct lirc_dev, dev); -static void lirc_release(struct device *ld) -{ - struct irctl *ir = container_of(ld, struct irctl, dev); + put_device(d->dev.parent); - lirc_free_buffer(ir); - kfree(ir); + if (d->buf_internal) { + lirc_buffer_free(d->buf); + kfree(d->buf); + d->buf = NULL; + } + kfree(d); + module_put(THIS_MODULE); } -static int lirc_allocate_buffer(struct irctl *ir) +static int lirc_allocate_buffer(struct lirc_dev *d) { - int err = 0; - struct lirc_dev *d = &ir->d; + int err; - if (d->rbuf) { - ir->buf = d->rbuf; - ir->buf_internal = false; - } else { - ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); - if (!ir->buf) { - err = -ENOMEM; - goto out; - } + if (d->buf) { + d->buf_internal = false; + return 0; + } - err = lirc_buffer_init(ir->buf, d->chunk_size, d->buffer_size); - if (err) { - kfree(ir->buf); - ir->buf = NULL; - goto out; - } + d->buf = kmalloc(sizeof(*d->buf), GFP_KERNEL); + if (!d->buf) + return -ENOMEM; - ir->buf_internal = true; - d->rbuf = ir->buf; + err = lirc_buffer_init(d->buf, d->chunk_size, d->buffer_size); + if (err) { + kfree(d->buf); + d->buf = NULL; + return err; } -out: - return err; + d->buf_internal = true; + return 0; } struct lirc_dev * lirc_allocate_device(void) { - return kzalloc(sizeof(struct lirc_dev), GFP_KERNEL); + struct lirc_dev *d; + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (d) { + mutex_init(&d->mutex); + device_initialize(&d->dev); + d->dev.class = lirc_class; + d->dev.release = lirc_release_device; + __module_get(THIS_MODULE); + } + + return d; } EXPORT_SYMBOL(lirc_allocate_device); void lirc_free_device(struct lirc_dev *d) { - kfree(d); + if (!d) + return; + + put_device(&d->dev); } EXPORT_SYMBOL(lirc_free_device); int lirc_register_device(struct lirc_dev *d) { - struct irctl *ir; int minor; int err; @@ -127,8 +117,8 @@ int lirc_register_device(struct lirc_dev *d) return -EBADRQC; } - if (!d->dev) { - pr_err("dev pointer not filled in!\n"); + if (!d->dev.parent) { + pr_err("dev parent pointer not filled in!\n"); return -EINVAL; } @@ -137,25 +127,25 @@ int lirc_register_device(struct lirc_dev *d) return -EINVAL; } - if (!d->rbuf && d->chunk_size < 1) { + if (!d->buf && d->chunk_size < 1) { pr_err("chunk_size must be set!\n"); return -EINVAL; } - if (!d->rbuf && d->buffer_size < 1) { + if (!d->buf && d->buffer_size < 1) { pr_err("buffer_size must be set!\n"); return -EINVAL; } if (d->code_length < 1 || d->code_length > (BUFLEN * 8)) { - dev_err(d->dev, "code length must be less than %d bits\n", - BUFLEN * 8); + dev_err(&d->dev, "code length must be less than %d bits\n", + BUFLEN * 8); return -EBADRQC; } - if (!d->rbuf && !(d->fops && d->fops->read && - d->fops->poll && d->fops->unlocked_ioctl)) { - dev_err(d->dev, "undefined read, poll, ioctl\n"); + if (!d->buf && !(d->fops && d->fops->read && + d->fops->poll && d->fops->unlocked_ioctl)) { + dev_err(&d->dev, "undefined read, poll, ioctl\n"); return -EBADRQC; } @@ -165,55 +155,34 @@ int lirc_register_device(struct lirc_dev *d) if (d->features == 0) d->features = LIRC_CAN_REC_LIRCCODE; - ir = kzalloc(sizeof(*ir), GFP_KERNEL); - if (!ir) - return -ENOMEM; - - mutex_init(&ir->mutex); - ir->d = *d; - if (LIRC_CAN_REC(d->features)) { - err = lirc_allocate_buffer(ir); - if (err) { - kfree(ir); + err = lirc_allocate_buffer(d); + if (err) return err; - } - d->rbuf = ir->buf; } minor = ida_simple_get(&lirc_ida, 0, LIRC_MAX_DEVICES, GFP_KERNEL); - if (minor < 0) { - lirc_free_buffer(ir); - kfree(ir); + if (minor < 0) return minor; - } - d->irctl = ir; d->minor = minor; - ir->d.minor = minor; - - device_initialize(&ir->dev); - ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor); - ir->dev.class = lirc_class; - ir->dev.parent = d->dev; - ir->dev.release = lirc_release; - dev_set_name(&ir->dev, "lirc%d", ir->d.minor); + d->dev.devt = MKDEV(MAJOR(lirc_base_dev), d->minor); + dev_set_name(&d->dev, "lirc%d", d->minor); - cdev_init(&ir->cdev, d->fops); - ir->cdev.owner = ir->d.owner; - ir->attached = true; + cdev_init(&d->cdev, d->fops); + d->cdev.owner = d->owner; + d->attached = true; - err = cdev_device_add(&ir->cdev, &ir->dev); + err = cdev_device_add(&d->cdev, &d->dev); if (err) { ida_simple_remove(&lirc_ida, minor); - put_device(&ir->dev); return err; } - get_device(ir->dev.parent); + get_device(d->dev.parent); - dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", - ir->d.name, ir->d.minor); + dev_info(&d->dev, "lirc_dev: driver %s registered at minor = %d\n", + d->name, d->minor); return 0; } @@ -221,88 +190,83 @@ EXPORT_SYMBOL(lirc_register_device); void lirc_unregister_device(struct lirc_dev *d) { - struct irctl *ir; - - if (!d || !d->irctl) + if (!d) return; - ir = d->irctl; - - dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", + dev_dbg(&d->dev, "lirc_dev: driver %s unregistered from minor = %d\n", d->name, d->minor); - cdev_device_del(&ir->cdev, &ir->dev); - - mutex_lock(&ir->mutex); + mutex_lock(&d->mutex); - ir->attached = false; - if (ir->open) { - dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", + d->attached = false; + if (d->open) { + dev_dbg(&d->dev, LOGHEAD "releasing opened driver\n", d->name, d->minor); - wake_up_interruptible(&ir->buf->wait_poll); + wake_up_interruptible(&d->buf->wait_poll); } - mutex_unlock(&ir->mutex); + mutex_unlock(&d->mutex); + cdev_device_del(&d->cdev, &d->dev); ida_simple_remove(&lirc_ida, d->minor); - put_device(&ir->dev); + put_device(&d->dev); } EXPORT_SYMBOL(lirc_unregister_device); int lirc_dev_fop_open(struct inode *inode, struct file *file) { - struct irctl *ir = container_of(inode->i_cdev, struct irctl, cdev); + struct lirc_dev *d = container_of(inode->i_cdev, struct lirc_dev, cdev); int retval; - dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); + dev_dbg(&d->dev, LOGHEAD "open called\n", d->name, d->minor); - retval = mutex_lock_interruptible(&ir->mutex); + retval = mutex_lock_interruptible(&d->mutex); if (retval) return retval; - if (!ir->attached) { + if (!d->attached) { retval = -ENODEV; goto out; } - if (ir->open) { + if (d->open) { retval = -EBUSY; goto out; } - if (ir->d.rdev) { - retval = rc_open(ir->d.rdev); + if (d->rdev) { + retval = rc_open(d->rdev); if (retval) goto out; } - if (ir->buf) - lirc_buffer_clear(ir->buf); + if (d->buf) + lirc_buffer_clear(d->buf); - ir->open++; + d->open++; lirc_init_pdata(inode, file); nonseekable_open(inode, file); - mutex_unlock(&ir->mutex); + mutex_unlock(&d->mutex); return 0; out: - mutex_unlock(&ir->mutex); + mutex_unlock(&d->mutex); return retval; } EXPORT_SYMBOL(lirc_dev_fop_open); int lirc_dev_fop_close(struct inode *inode, struct file *file) { - struct irctl *ir = file->private_data; + struct lirc_dev *d = file->private_data; - mutex_lock(&ir->mutex); + mutex_lock(&d->mutex); - rc_close(ir->d.rdev); - ir->open--; + rc_close(d->rdev); + d->open--; - mutex_unlock(&ir->mutex); + mutex_unlock(&d->mutex); return 0; } @@ -310,24 +274,24 @@ EXPORT_SYMBOL(lirc_dev_fop_close); unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) { - struct irctl *ir = file->private_data; + struct lirc_dev *d = file->private_data; unsigned int ret; - if (!ir->attached) + if (!d->attached) return POLLHUP | POLLERR; - if (ir->buf) { - poll_wait(file, &ir->buf->wait_poll, wait); + if (d->buf) { + poll_wait(file, &d->buf->wait_poll, wait); - if (lirc_buffer_empty(ir->buf)) + if (lirc_buffer_empty(d->buf)) ret = 0; else ret = POLLIN | POLLRDNORM; - } else + } else { ret = POLLERR; + } - dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", - ir->d.name, ir->d.minor, ret); + dev_dbg(&d->dev, LOGHEAD "poll result = %d\n", d->name, d->minor, ret); return ret; } @@ -335,44 +299,44 @@ EXPORT_SYMBOL(lirc_dev_fop_poll); long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct irctl *ir = file->private_data; + struct lirc_dev *d = file->private_data; __u32 mode; int result; - dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", - ir->d.name, ir->d.minor, cmd); + dev_dbg(&d->dev, LOGHEAD "ioctl called (0x%x)\n", + d->name, d->minor, cmd); - result = mutex_lock_interruptible(&ir->mutex); + result = mutex_lock_interruptible(&d->mutex); if (result) return result; - if (!ir->attached) { + if (!d->attached) { result = -ENODEV; goto out; } switch (cmd) { case LIRC_GET_FEATURES: - result = put_user(ir->d.features, (__u32 __user *)arg); + result = put_user(d->features, (__u32 __user *)arg); break; case LIRC_GET_REC_MODE: - if (!LIRC_CAN_REC(ir->d.features)) { + if (!LIRC_CAN_REC(d->features)) { result = -ENOTTY; break; } result = put_user(LIRC_REC2MODE - (ir->d.features & LIRC_CAN_REC_MASK), + (d->features & LIRC_CAN_REC_MASK), (__u32 __user *)arg); break; case LIRC_SET_REC_MODE: - if (!LIRC_CAN_REC(ir->d.features)) { + if (!LIRC_CAN_REC(d->features)) { result = -ENOTTY; break; } result = get_user(mode, (__u32 __user *)arg); - if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) + if (!result && !(LIRC_MODE2REC(mode) & d->features)) result = -EINVAL; /* * FIXME: We should actually set the mode somehow but @@ -380,32 +344,32 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) */ break; case LIRC_GET_LENGTH: - result = put_user(ir->d.code_length, (__u32 __user *)arg); + result = put_user(d->code_length, (__u32 __user *)arg); break; case LIRC_GET_MIN_TIMEOUT: - if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || - ir->d.min_timeout == 0) { + if (!(d->features & LIRC_CAN_SET_REC_TIMEOUT) || + d->min_timeout == 0) { result = -ENOTTY; break; } - result = put_user(ir->d.min_timeout, (__u32 __user *)arg); + result = put_user(d->min_timeout, (__u32 __user *)arg); break; case LIRC_GET_MAX_TIMEOUT: - if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || - ir->d.max_timeout == 0) { + if (!(d->features & LIRC_CAN_SET_REC_TIMEOUT) || + d->max_timeout == 0) { result = -ENOTTY; break; } - result = put_user(ir->d.max_timeout, (__u32 __user *)arg); + result = put_user(d->max_timeout, (__u32 __user *)arg); break; default: result = -ENOTTY; } out: - mutex_unlock(&ir->mutex); + mutex_unlock(&d->mutex); return result; } EXPORT_SYMBOL(lirc_dev_fop_ioctl); @@ -415,34 +379,34 @@ ssize_t lirc_dev_fop_read(struct file *file, size_t length, loff_t *ppos) { - struct irctl *ir = file->private_data; + struct lirc_dev *d = file->private_data; unsigned char *buf; int ret, written = 0; DECLARE_WAITQUEUE(wait, current); - dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); - - buf = kzalloc(ir->buf->chunk_size, GFP_KERNEL); + buf = kzalloc(d->buf->chunk_size, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = mutex_lock_interruptible(&ir->mutex); + dev_dbg(&d->dev, LOGHEAD "read called\n", d->name, d->minor); + + ret = mutex_lock_interruptible(&d->mutex); if (ret) { kfree(buf); return ret; } - if (!ir->attached) { + if (!d->attached) { ret = -ENODEV; goto out_locked; } - if (!LIRC_CAN_REC(ir->d.features)) { + if (!LIRC_CAN_REC(d->features)) { ret = -EINVAL; goto out_locked; } - if (length % ir->buf->chunk_size) { + if (length % d->buf->chunk_size) { ret = -EINVAL; goto out_locked; } @@ -452,14 +416,14 @@ ssize_t lirc_dev_fop_read(struct file *file, * to avoid losing scan code (in case when queue is awaken somewhere * between while condition checking and scheduling) */ - add_wait_queue(&ir->buf->wait_poll, &wait); + add_wait_queue(&d->buf->wait_poll, &wait); /* * while we didn't provide 'length' bytes, device is opened in blocking * mode and 'copy_to_user' is happy, wait for data. */ while (written < length && ret == 0) { - if (lirc_buffer_empty(ir->buf)) { + if (lirc_buffer_empty(d->buf)) { /* According to the read(2) man page, 'written' can be * returned as less than 'length', instead of blocking * again, returning -EWOULDBLOCK, or returning @@ -476,36 +440,36 @@ ssize_t lirc_dev_fop_read(struct file *file, break; } - mutex_unlock(&ir->mutex); + mutex_unlock(&d->mutex); set_current_state(TASK_INTERRUPTIBLE); schedule(); set_current_state(TASK_RUNNING); - ret = mutex_lock_interruptible(&ir->mutex); + ret = mutex_lock_interruptible(&d->mutex); if (ret) { - remove_wait_queue(&ir->buf->wait_poll, &wait); + remove_wait_queue(&d->buf->wait_poll, &wait); goto out_unlocked; } - if (!ir->attached) { + if (!d->attached) { ret = -ENODEV; goto out_locked; } } else { - lirc_buffer_read(ir->buf, buf); + lirc_buffer_read(d->buf, buf); ret = copy_to_user((void __user *)buffer+written, buf, - ir->buf->chunk_size); + d->buf->chunk_size); if (!ret) - written += ir->buf->chunk_size; + written += d->buf->chunk_size; else ret = -EFAULT; } } - remove_wait_queue(&ir->buf->wait_poll, &wait); + remove_wait_queue(&d->buf->wait_poll, &wait); out_locked: - mutex_unlock(&ir->mutex); + mutex_unlock(&d->mutex); out_unlocked: kfree(buf); @@ -516,17 +480,17 @@ EXPORT_SYMBOL(lirc_dev_fop_read); void lirc_init_pdata(struct inode *inode, struct file *file) { - struct irctl *ir = container_of(inode->i_cdev, struct irctl, cdev); + struct lirc_dev *d = container_of(inode->i_cdev, struct lirc_dev, cdev); - file->private_data = ir; + file->private_data = d; } EXPORT_SYMBOL(lirc_init_pdata); void *lirc_get_pdata(struct file *file) { - struct irctl *ir = file->private_data; + struct lirc_dev *d = file->private_data; - return ir->d.data; + return d->data; } EXPORT_SYMBOL(lirc_get_pdata); diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index 00e8c8f224b7..6bd0717bf76e 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -184,10 +184,8 @@ static void release_ir_device(struct kref *ref) * ir->open_count == 0 - happens on final close() * ir_lock, tx_ref_lock, rx_ref_lock, all released */ - if (ir->l) { + if (ir->l) lirc_unregister_device(ir->l); - lirc_free_device(ir->l); - } if (kfifo_initialized(&ir->rbuf.fifo)) lirc_buffer_free(&ir->rbuf); @@ -318,7 +316,7 @@ static int add_to_buf(struct IR *ir) int ret; int failures = 0; unsigned char sendbuf[1] = { 0 }; - struct lirc_buffer *rbuf = ir->l->rbuf; + struct lirc_buffer *rbuf = ir->l->buf; struct IR_rx *rx; struct IR_tx *tx; @@ -464,7 +462,7 @@ static int add_to_buf(struct IR *ir) static int lirc_thread(void *arg) { struct IR *ir = arg; - struct lirc_buffer *rbuf = ir->l->rbuf; + struct lirc_buffer *rbuf = ir->l->buf; dev_dbg(ir->dev, "poll thread started\n"); @@ -885,7 +883,7 @@ static ssize_t read(struct file *filep, char __user *outbuf, size_t n, { struct IR *ir = lirc_get_pdata(filep); struct IR_rx *rx; - struct lirc_buffer *rbuf = ir->l->rbuf; + struct lirc_buffer *rbuf = ir->l->buf; int ret = 0, written = 0, retries = 0; unsigned int m; DECLARE_WAITQUEUE(wait, current); @@ -1203,7 +1201,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) { struct IR *ir = lirc_get_pdata(filep); struct IR_rx *rx; - struct lirc_buffer *rbuf = ir->l->rbuf; + struct lirc_buffer *rbuf = ir->l->buf; unsigned int ret; dev_dbg(ir->dev, "%s called\n", __func__); @@ -1449,6 +1447,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->l->code_length = 13; ir->l->fops = &lirc_fops; ir->l->owner = THIS_MODULE; + ir->l->dev.parent = &adap->dev; /* * FIXME this is a pointer reference to us, but no refcount. @@ -1456,13 +1455,12 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) * This OK for now, since lirc_dev currently won't touch this * buffer as we provide our own lirc_fops. * - * Currently our own lirc_fops rely on this ir->l->rbuf pointer + * Currently our own lirc_fops rely on this ir->l->buf pointer */ - ir->l->rbuf = &ir->rbuf; - ir->l->dev = &adap->dev; + ir->l->buf = &ir->rbuf; /* This will be returned by lirc_get_pdata() */ ir->l->data = ir; - ret = lirc_buffer_init(ir->l->rbuf, 2, BUFLEN / 2); + ret = lirc_buffer_init(ir->l->buf, 2, BUFLEN / 2); if (ret) { lirc_free_device(ir->l); ir->l = NULL; diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 4b0dc640e142..981dcabd5fd5 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -17,6 +17,8 @@ #include #include #include +#include +#include struct lirc_buffer { wait_queue_head_t wait_poll; @@ -127,14 +129,19 @@ static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, * LIRC_CAN_SET_REC_TIMEOUT is defined. * @max_timeout: Maximum timeout for record. Valid only if * LIRC_CAN_SET_REC_TIMEOUT is defined. - * @rbuf: if not NULL, it will be used as a read buffer, you will + * @buf: if %NULL, lirc_dev will allocate and manage the buffer, + * otherwise allocated by the caller which will * have to write to the buffer by other means, like irq's * (see also lirc_serial.c). + * @buf_internal: whether lirc_dev has allocated the read buffer or not * @rdev: &struct rc_dev associated with the device * @fops: &struct file_operations for the device - * @dev: &struct device assigned to the device * @owner: the module owning this struct - * @irctl: &struct irctl assigned to the device + * @attached: if the device is still live + * @open: open count for the device's chardev + * @mutex: serialises file_operations calls + * @dev: &struct device assigned to the device + * @cdev: &struct cdev assigned to the device */ struct lirc_dev { char name[40]; @@ -144,16 +151,23 @@ struct lirc_dev { unsigned int buffer_size; /* in chunks holding one code each */ unsigned int chunk_size; + struct lirc_buffer *buf; + bool buf_internal; void *data; int min_timeout; int max_timeout; - struct lirc_buffer *rbuf; struct rc_dev *rdev; const struct file_operations *fops; - struct device *dev; struct module *owner; - struct irctl *irctl; + + bool attached; + int open; + + struct mutex mutex; /* protect from simultaneous accesses */ + + struct device dev; + struct cdev cdev; }; struct lirc_dev *lirc_allocate_device(void); -- cgit v1.2.3-59-g8ed1b From 6cafbe159416822f6d3dfd711bf4c39050c650ba Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Tue, 20 Jun 2017 10:44:58 -0400 Subject: ftrace: Add a ftrace_free_mem() function for modules to use In order to be able to trace module init functions, the module code needs to tell ftrace what is being freed when the init sections are freed. Use the code that the main init calls to tell ftrace to free the main init sections. This requires passing in a start and end address to free. Signed-off-by: Steven Rostedt (VMware) --- include/linux/ftrace.h | 2 ++ kernel/trace/ftrace.c | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 2e028854bac7..47fc404ad233 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -151,8 +151,10 @@ struct ftrace_ops_hash { }; void ftrace_free_init_mem(void); +void ftrace_free_mem(void *start, void *end); #else static inline void ftrace_free_init_mem(void) { } +static inline void ftrace_free_mem(void *start, void *end) { } #endif /* diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 6abfafd7f173..84cb5928665a 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -5868,10 +5868,10 @@ void ftrace_module_init(struct module *mod) } #endif /* CONFIG_MODULES */ -void __init ftrace_free_init_mem(void) +void ftrace_free_mem(void *start_ptr, void *end_ptr) { - unsigned long start = (unsigned long)(&__init_begin); - unsigned long end = (unsigned long)(&__init_end); + unsigned long start = (unsigned long)(start_ptr); + unsigned long end = (unsigned long)(end_ptr); struct ftrace_page **last_pg = &ftrace_pages_start; struct ftrace_page *pg; struct dyn_ftrace *rec; @@ -5913,6 +5913,14 @@ void __init ftrace_free_init_mem(void) mutex_unlock(&ftrace_lock); } +void __init ftrace_free_init_mem(void) +{ + void *start = (void *)(&__init_begin); + void *end = (void *)(&__init_end); + + ftrace_free_mem(start, end); +} + void __init ftrace_init(void) { extern unsigned long __start_mcount_loc[]; -- cgit v1.2.3-59-g8ed1b From 5c95878f618cf4f3eb0a4c7ff54a09ca6d4d0426 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Thu, 7 Sep 2017 20:39:45 -0300 Subject: [media] media: rc: gpio-ir-recv: remove gpio_ir_recv_platform_data gpio_ir_recv_platform_data are not used anywhere in kernel tree, so remove it. Signed-off-by: Ladislav Michl Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/gpio-ir-recv.c | 92 +++++++----------------- include/linux/platform_data/media/gpio-ir-recv.h | 23 ------ 2 files changed, 26 insertions(+), 89 deletions(-) delete mode 100644 include/linux/platform_data/media/gpio-ir-recv.h (limited to 'include') diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c index b78195f06354..2634b81cbe7e 100644 --- a/drivers/media/rc/gpio-ir-recv.c +++ b/drivers/media/rc/gpio-ir-recv.c @@ -21,7 +21,6 @@ #include #include #include -#include #define GPIO_IR_DEVICE_NAME "gpio_ir_recv" @@ -31,45 +30,6 @@ struct gpio_rc_dev { bool active_low; }; -#ifdef CONFIG_OF -/* - * Translate OpenFirmware node properties into platform_data - */ -static int gpio_ir_recv_get_devtree_pdata(struct device *dev, - struct gpio_ir_recv_platform_data *pdata) -{ - struct device_node *np = dev->of_node; - enum of_gpio_flags flags; - int gpio; - - gpio = of_get_gpio_flags(np, 0, &flags); - if (gpio < 0) { - if (gpio != -EPROBE_DEFER) - dev_err(dev, "Failed to get gpio flags (%d)\n", gpio); - return gpio; - } - - pdata->gpio_nr = gpio; - pdata->active_low = (flags & OF_GPIO_ACTIVE_LOW); - /* probe() takes care of map_name == NULL or allowed_protos == 0 */ - pdata->map_name = of_get_property(np, "linux,rc-map-name", NULL); - pdata->allowed_protos = 0; - - return 0; -} - -static const struct of_device_id gpio_ir_recv_of_match[] = { - { .compatible = "gpio-ir-receiver", }, - { }, -}; -MODULE_DEVICE_TABLE(of, gpio_ir_recv_of_match); - -#else /* !CONFIG_OF */ - -#define gpio_ir_recv_get_devtree_pdata(dev, pdata) (-ENOSYS) - -#endif - static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id) { struct gpio_rc_dev *gpio_dev = dev_id; @@ -95,32 +55,29 @@ err_get_value: static int gpio_ir_recv_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct gpio_rc_dev *gpio_dev; + enum of_gpio_flags flags; struct rc_dev *rcdev; - const struct gpio_ir_recv_platform_data *pdata = dev->platform_data; int rc; - if (pdev->dev.of_node) { - struct gpio_ir_recv_platform_data *dtpdata = - devm_kzalloc(dev, sizeof(*dtpdata), GFP_KERNEL); - if (!dtpdata) - return -ENOMEM; - rc = gpio_ir_recv_get_devtree_pdata(dev, dtpdata); - if (rc) - return rc; - pdata = dtpdata; - } - - if (!pdata) - return -EINVAL; - - if (pdata->gpio_nr < 0) - return -EINVAL; + if (!np) + return -ENODEV; gpio_dev = devm_kzalloc(dev, sizeof(*gpio_dev), GFP_KERNEL); if (!gpio_dev) return -ENOMEM; + rc = of_get_gpio_flags(np, 0, &flags); + if (rc < 0) { + if (rc != -EPROBE_DEFER) + dev_err(dev, "Failed to get gpio flags (%d)\n", rc); + return rc; + } + + gpio_dev->gpio_nr = rc; + gpio_dev->active_low = (flags & OF_GPIO_ACTIVE_LOW); + rcdev = devm_rc_allocate_device(dev, RC_DRIVER_IR_RAW); if (!rcdev) return -ENOMEM; @@ -137,17 +94,14 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) rcdev->min_timeout = 1; rcdev->timeout = IR_DEFAULT_TIMEOUT; rcdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT; - if (pdata->allowed_protos) - rcdev->allowed_protocols = pdata->allowed_protos; - else - rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; - rcdev->map_name = pdata->map_name ?: RC_MAP_EMPTY; + rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; + rcdev->map_name = of_get_property(np, "linux,rc-map-name", NULL); + if (!rcdev->map_name) + rcdev->map_name = RC_MAP_EMPTY; gpio_dev->rcdev = rcdev; - gpio_dev->gpio_nr = pdata->gpio_nr; - gpio_dev->active_low = pdata->active_low; - rc = devm_gpio_request_one(dev, pdata->gpio_nr, GPIOF_DIR_IN, + rc = devm_gpio_request_one(dev, gpio_dev->gpio_nr, GPIOF_DIR_IN, "gpio-ir-recv"); if (rc < 0) return rc; @@ -160,7 +114,7 @@ static int gpio_ir_recv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, gpio_dev); - return devm_request_irq(dev, gpio_to_irq(pdata->gpio_nr), + return devm_request_irq(dev, gpio_to_irq(gpio_dev->gpio_nr), gpio_ir_recv_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "gpio-ir-recv-irq", gpio_dev); @@ -199,6 +153,12 @@ static const struct dev_pm_ops gpio_ir_recv_pm_ops = { }; #endif +static const struct of_device_id gpio_ir_recv_of_match[] = { + { .compatible = "gpio-ir-receiver", }, + { }, +}; +MODULE_DEVICE_TABLE(of, gpio_ir_recv_of_match); + static struct platform_driver gpio_ir_recv_driver = { .probe = gpio_ir_recv_probe, .driver = { diff --git a/include/linux/platform_data/media/gpio-ir-recv.h b/include/linux/platform_data/media/gpio-ir-recv.h deleted file mode 100644 index 0c298f569d5a..000000000000 --- a/include/linux/platform_data/media/gpio-ir-recv.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only 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. - */ - -#ifndef __GPIO_IR_RECV_H__ -#define __GPIO_IR_RECV_H__ - -struct gpio_ir_recv_platform_data { - int gpio_nr; - bool active_low; - u64 allowed_protos; - const char *map_name; -}; - -#endif /* __GPIO_IR_RECV_H__ */ -- cgit v1.2.3-59-g8ed1b From a917374e8a20cdfbaaaaaa2b01331dc063d38848 Mon Sep 17 00:00:00 2001 From: Don Hiatt Date: Mon, 2 Oct 2017 11:04:33 -0700 Subject: IB/core: Use __be32 for LIDs in opa_is_extended_lid The LIDs passed to opa_extended_lid are in __be32 format, change function signature accordingly. This fixes the following sparse warnings: drivers/infiniband/core/cm.c:1181:60: warning: incorrect type in argument 1 (different ba drivers/infiniband/core/cm.c:1182:60: warning: incorrect type in argument 2 (different ba drivers/infiniband/core/cm.c:1242:68: warning: incorrect type in argument 1 (different ba drivers/infiniband/core/cm.c:1243:68: warning: incorrect type in argument 2 (different ba drivers/infiniband/core/cm.c:2922:66: warning: incorrect type in argument 1 (different ba drivers/infiniband/core/cm.c:2923:66: warning: incorrect type in argument 2 (different ba include/rdma/opa_addr.h:102:14: warning: cast to restricted __be32 Fixes: e92aa00a5189 ("IB/CM: Add OPA Path record support to CM") Reviewed-by: Mike Marciniszyn Signed-off-by: Don Hiatt Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- include/rdma/opa_addr.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/rdma/opa_addr.h b/include/rdma/opa_addr.h index e6e90f18e6d5..f68fca296631 100644 --- a/include/rdma/opa_addr.h +++ b/include/rdma/opa_addr.h @@ -97,15 +97,15 @@ static inline u32 opa_get_lid_from_gid(const union ib_gid *gid) * @dlid: The DLID * @slid: The SLID */ -static inline bool opa_is_extended_lid(u32 dlid, u32 slid) +static inline bool opa_is_extended_lid(__be32 dlid, __be32 slid) { if ((be32_to_cpu(dlid) >= be16_to_cpu(IB_MULTICAST_LID_BASE)) || (be32_to_cpu(slid) >= be16_to_cpu(IB_MULTICAST_LID_BASE))) return true; - else - return false; + + return false; } /* Get multicast lid base */ -- cgit v1.2.3-59-g8ed1b From 7ebfc93edcefa0c4c09f194ebdace9ddae15efa6 Mon Sep 17 00:00:00 2001 From: Sebastian Sanchez Date: Mon, 2 Oct 2017 11:04:41 -0700 Subject: IB/rdmavt: Correct issues with read-mostly and send size cache lines The s_ahgpsn was incorrectly placed in the read-mostly section of the QP and the s_curr_size and s_hdrwords are oversized. The misplaced s_ahgpsn will cause the read-mostly cachelines to thrash. Place s_ahgpsn in the send side cache lines and correctly size and s_hdrwords and s_cur_size to keep the send side cachelines at the same size. Reviewed-by: Mike Marciniszyn Signed-off-by: Sebastian Sanchez Signed-off-by: Dennis Dalessandro Signed-off-by: Doug Ledford --- drivers/infiniband/hw/hfi1/chip.c | 4 ++-- include/rdma/rdmavt_qp.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index 98bcd06c82d7..7befba8fceb2 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -8739,8 +8739,8 @@ static int set_physical_link_state(struct hfi1_devdata *dd, u64 state) return do_8051_command(dd, HCMD_CHANGE_PHY_STATE, state, NULL); } -int _load_8051_config(struct hfi1_devdata *dd, u8 field_id, - u8 lane_id, u32 config_data) +static int _load_8051_config(struct hfi1_devdata *dd, u8 field_id, + u8 lane_id, u32 config_data) { u64 data; int ret; diff --git a/include/rdma/rdmavt_qp.h b/include/rdma/rdmavt_qp.h index 0eed3d8752fa..89ab88c342b6 100644 --- a/include/rdma/rdmavt_qp.h +++ b/include/rdma/rdmavt_qp.h @@ -282,7 +282,6 @@ struct rvt_qp { u32 remote_qpn; u32 qkey; /* QKEY for this QP (for UD or RD) */ u32 s_size; /* send work queue size */ - u32 s_ahgpsn; /* set to the psn in the copy of the header */ u16 pmtu; /* decoded from path_mtu */ u8 log_pmtu; /* shift for pmtu */ @@ -344,7 +343,6 @@ struct rvt_qp { struct rvt_swqe *s_wqe; struct rvt_sge_state s_sge; /* current send request data */ struct rvt_mregion *s_rdma_mr; - u32 s_cur_size; /* size of send packet in bytes */ u32 s_len; /* total length of s_sge */ u32 s_rdma_read_len; /* total length of s_rdma_read_sge */ u32 s_last_psn; /* last response PSN processed */ @@ -358,8 +356,10 @@ struct rvt_qp { u32 s_acked; /* last un-ACK'ed entry */ u32 s_last; /* last completed entry */ u32 s_lsn; /* limit sequence number (credit) */ - u16 s_hdrwords; /* size of s_hdr in 32 bit words */ + u32 s_ahgpsn; /* set to the psn in the copy of the header */ + u16 s_cur_size; /* size of send packet in bytes */ u16 s_rdma_ack_cnt; + u8 s_hdrwords; /* size of s_hdr in 32 bit words */ s8 s_ahgidx; u8 s_state; /* opcode of last packet sent */ u8 s_ack_state; /* opcode of packet to ACK */ -- cgit v1.2.3-59-g8ed1b From ad62701720dd5d2a17c62f6c3bbd6ee629efcfaa Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 13 Sep 2017 16:37:50 -0300 Subject: [media] saa7146: make saa7146_use_ops const Make these const as they are not modified in the file referencing them. They are only used when their function pointer fields invokes a function and therefore none of the structure fields are getting modified. Also, add a const to the declaration in the header. Signed-off-by: Bhumika Goyal Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_vbi.c | 2 +- drivers/media/common/saa7146/saa7146_video.c | 2 +- include/media/drv-intf/saa7146_vv.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c index d79e4d7ecd9f..69525ca4f52c 100644 --- a/drivers/media/common/saa7146/saa7146_vbi.c +++ b/drivers/media/common/saa7146/saa7146_vbi.c @@ -488,7 +488,7 @@ static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff return ret; } -struct saa7146_use_ops saa7146_vbi_uops = { +const struct saa7146_use_ops saa7146_vbi_uops = { .init = vbi_init, .open = vbi_open, .release = vbi_close, diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c index 37b4654dc21c..51eeed830de4 100644 --- a/drivers/media/common/saa7146/saa7146_video.c +++ b/drivers/media/common/saa7146/saa7146_video.c @@ -1303,7 +1303,7 @@ out: return ret; } -struct saa7146_use_ops saa7146_video_uops = { +const struct saa7146_use_ops saa7146_video_uops = { .init = video_init, .open = video_open, .release = video_close, diff --git a/include/media/drv-intf/saa7146_vv.h b/include/media/drv-intf/saa7146_vv.h index 0da6ccc0615b..736f4f2d8290 100644 --- a/include/media/drv-intf/saa7146_vv.h +++ b/include/media/drv-intf/saa7146_vv.h @@ -202,14 +202,14 @@ void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data); /* from saa7146_video.c */ extern const struct v4l2_ioctl_ops saa7146_video_ioctl_ops; extern const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops; -extern struct saa7146_use_ops saa7146_video_uops; +extern const struct saa7146_use_ops saa7146_video_uops; int saa7146_start_preview(struct saa7146_fh *fh); int saa7146_stop_preview(struct saa7146_fh *fh); long saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg); int saa7146_s_ctrl(struct v4l2_ctrl *ctrl); /* from saa7146_vbi.c */ -extern struct saa7146_use_ops saa7146_vbi_uops; +extern const struct saa7146_use_ops saa7146_vbi_uops; /* resource management functions */ int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit); -- cgit v1.2.3-59-g8ed1b From 324bda9e6c5add86ba2e1066476481c48132aca0 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Mon, 2 Oct 2017 22:50:21 -0700 Subject: bpf: multi program support for cgroup+bpf introduce BPF_F_ALLOW_MULTI flag that can be used to attach multiple bpf programs to a cgroup. The difference between three possible flags for BPF_PROG_ATTACH command: - NONE(default): No further bpf programs allowed in the subtree. - BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program, the program in this cgroup yields to sub-cgroup program. - BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program, that cgroup program gets run in addition to the program in this cgroup. NONE and BPF_F_ALLOW_OVERRIDE existed before. This patch doesn't change their behavior. It only clarifies the semantics in relation to new flag. Only one program is allowed to be attached to a cgroup with NONE or BPF_F_ALLOW_OVERRIDE flag. Multiple programs are allowed to be attached to a cgroup with BPF_F_ALLOW_MULTI flag. They are executed in FIFO order (those that were attached first, run first) The programs of sub-cgroup are executed first, then programs of this cgroup and then programs of parent cgroup. All eligible programs are executed regardless of return code from earlier programs. To allow efficient execution of multiple programs attached to a cgroup and to avoid penalizing cgroups without any programs attached introduce 'struct bpf_prog_array' which is RCU protected array of pointers to bpf programs. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Acked-by: Martin KaFai Lau for cgroup bits Acked-by: Tejun Heo Signed-off-by: David S. Miller --- include/linux/bpf-cgroup.h | 46 +++-- include/linux/bpf.h | 32 ++++ include/linux/filter.h | 2 +- include/uapi/linux/bpf.h | 42 +++- kernel/bpf/cgroup.c | 467 ++++++++++++++++++++++++++++++++------------- kernel/bpf/core.c | 31 +++ kernel/bpf/syscall.c | 37 ++-- kernel/cgroup/cgroup.c | 28 ++- 8 files changed, 516 insertions(+), 169 deletions(-) (limited to 'include') diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index d41d40ac3efd..102e56fbb6de 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -14,27 +14,42 @@ struct bpf_sock_ops_kern; extern struct static_key_false cgroup_bpf_enabled_key; #define cgroup_bpf_enabled static_branch_unlikely(&cgroup_bpf_enabled_key) +struct bpf_prog_list { + struct list_head node; + struct bpf_prog *prog; +}; + +struct bpf_prog_array; + struct cgroup_bpf { - /* - * Store two sets of bpf_prog pointers, one for programs that are - * pinned directly to this cgroup, and one for those that are effective - * when this cgroup is accessed. + /* array of effective progs in this cgroup */ + struct bpf_prog_array __rcu *effective[MAX_BPF_ATTACH_TYPE]; + + /* attached progs to this cgroup and attach flags + * when flags == 0 or BPF_F_ALLOW_OVERRIDE the progs list will + * have either zero or one element + * when BPF_F_ALLOW_MULTI the list can have up to BPF_CGROUP_MAX_PROGS */ - struct bpf_prog *prog[MAX_BPF_ATTACH_TYPE]; - struct bpf_prog __rcu *effective[MAX_BPF_ATTACH_TYPE]; - bool disallow_override[MAX_BPF_ATTACH_TYPE]; + struct list_head progs[MAX_BPF_ATTACH_TYPE]; + u32 flags[MAX_BPF_ATTACH_TYPE]; + + /* temp storage for effective prog array used by prog_attach/detach */ + struct bpf_prog_array __rcu *inactive; }; void cgroup_bpf_put(struct cgroup *cgrp); -void cgroup_bpf_inherit(struct cgroup *cgrp, struct cgroup *parent); +int cgroup_bpf_inherit(struct cgroup *cgrp); -int __cgroup_bpf_update(struct cgroup *cgrp, struct cgroup *parent, - struct bpf_prog *prog, enum bpf_attach_type type, - bool overridable); +int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, + enum bpf_attach_type type, u32 flags); +int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, + enum bpf_attach_type type, u32 flags); -/* Wrapper for __cgroup_bpf_update() protected by cgroup_mutex */ -int cgroup_bpf_update(struct cgroup *cgrp, struct bpf_prog *prog, - enum bpf_attach_type type, bool overridable); +/* Wrapper for __cgroup_bpf_*() protected by cgroup_mutex */ +int cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, + enum bpf_attach_type type, u32 flags); +int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, + enum bpf_attach_type type, u32 flags); int __cgroup_bpf_run_filter_skb(struct sock *sk, struct sk_buff *skb, @@ -96,8 +111,7 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk, struct cgroup_bpf {}; static inline void cgroup_bpf_put(struct cgroup *cgrp) {} -static inline void cgroup_bpf_inherit(struct cgroup *cgrp, - struct cgroup *parent) {} +static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; } #define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk,skb) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; }) diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 252f4bc9eb25..a6964b75f070 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -241,6 +241,38 @@ int bpf_prog_test_run_xdp(struct bpf_prog *prog, const union bpf_attr *kattr, int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr, union bpf_attr __user *uattr); +/* an array of programs to be executed under rcu_lock. + * + * Typical usage: + * ret = BPF_PROG_RUN_ARRAY(&bpf_prog_array, ctx, BPF_PROG_RUN); + * + * the structure returned by bpf_prog_array_alloc() should be populated + * with program pointers and the last pointer must be NULL. + * The user has to keep refcnt on the program and make sure the program + * is removed from the array before bpf_prog_put(). + * The 'struct bpf_prog_array *' should only be replaced with xchg() + * since other cpus are walking the array of pointers in parallel. + */ +struct bpf_prog_array { + struct rcu_head rcu; + struct bpf_prog *progs[0]; +}; + +struct bpf_prog_array __rcu *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags); +void bpf_prog_array_free(struct bpf_prog_array __rcu *progs); + +#define BPF_PROG_RUN_ARRAY(array, ctx, func) \ + ({ \ + struct bpf_prog **_prog; \ + u32 _ret = 1; \ + rcu_read_lock(); \ + _prog = rcu_dereference(array)->progs; \ + for (; *_prog; _prog++) \ + _ret &= func(*_prog, ctx); \ + rcu_read_unlock(); \ + _ret; \ + }) + #ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU(int, bpf_prog_active); diff --git a/include/linux/filter.h b/include/linux/filter.h index 911d454af107..2d2db394b0ca 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -481,7 +481,7 @@ struct sk_filter { struct bpf_prog *prog; }; -#define BPF_PROG_RUN(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi) +#define BPF_PROG_RUN(filter, ctx) (*(filter)->bpf_func)(ctx, (filter)->insnsi) #define BPF_SKB_CB_LEN QDISC_CB_PRIV_LEN diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 6d2137b4cf38..762f74bc6c47 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -143,11 +143,47 @@ enum bpf_attach_type { #define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE -/* If BPF_F_ALLOW_OVERRIDE flag is used in BPF_PROG_ATTACH command - * to the given target_fd cgroup the descendent cgroup will be able to - * override effective bpf program that was inherited from this cgroup +/* cgroup-bpf attach flags used in BPF_PROG_ATTACH command + * + * NONE(default): No further bpf programs allowed in the subtree. + * + * BPF_F_ALLOW_OVERRIDE: If a sub-cgroup installs some bpf program, + * the program in this cgroup yields to sub-cgroup program. + * + * BPF_F_ALLOW_MULTI: If a sub-cgroup installs some bpf program, + * that cgroup program gets run in addition to the program in this cgroup. + * + * Only one program is allowed to be attached to a cgroup with + * NONE or BPF_F_ALLOW_OVERRIDE flag. + * Attaching another program on top of NONE or BPF_F_ALLOW_OVERRIDE will + * release old program and attach the new one. Attach flags has to match. + * + * Multiple programs are allowed to be attached to a cgroup with + * BPF_F_ALLOW_MULTI flag. They are executed in FIFO order + * (those that were attached first, run first) + * The programs of sub-cgroup are executed first, then programs of + * this cgroup and then programs of parent cgroup. + * When children program makes decision (like picking TCP CA or sock bind) + * parent program has a chance to override it. + * + * A cgroup with MULTI or OVERRIDE flag allows any attach flags in sub-cgroups. + * A cgroup with NONE doesn't allow any programs in sub-cgroups. + * Ex1: + * cgrp1 (MULTI progs A, B) -> + * cgrp2 (OVERRIDE prog C) -> + * cgrp3 (MULTI prog D) -> + * cgrp4 (OVERRIDE prog E) -> + * cgrp5 (NONE prog F) + * the event in cgrp5 triggers execution of F,D,A,B in that order. + * if prog F is detached, the execution is E,D,A,B + * if prog F and D are detached, the execution is E,A,B + * if prog F, E and D are detached, the execution is C,A,B + * + * All eligible programs are executed regardless of return code from + * earlier programs. */ #define BPF_F_ALLOW_OVERRIDE (1U << 0) +#define BPF_F_ALLOW_MULTI (1U << 1) /* If BPF_F_STRICT_ALIGNMENT is used in BPF_PROG_LOAD command, the * verifier will perform strict alignment checking as if the kernel diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 546113430049..6b7500bbdb53 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -27,129 +27,361 @@ void cgroup_bpf_put(struct cgroup *cgrp) { unsigned int type; - for (type = 0; type < ARRAY_SIZE(cgrp->bpf.prog); type++) { - struct bpf_prog *prog = cgrp->bpf.prog[type]; - - if (prog) { - bpf_prog_put(prog); + for (type = 0; type < ARRAY_SIZE(cgrp->bpf.progs); type++) { + struct list_head *progs = &cgrp->bpf.progs[type]; + struct bpf_prog_list *pl, *tmp; + + list_for_each_entry_safe(pl, tmp, progs, node) { + list_del(&pl->node); + bpf_prog_put(pl->prog); + kfree(pl); static_branch_dec(&cgroup_bpf_enabled_key); } + bpf_prog_array_free(cgrp->bpf.effective[type]); + } +} + +/* count number of elements in the list. + * it's slow but the list cannot be long + */ +static u32 prog_list_length(struct list_head *head) +{ + struct bpf_prog_list *pl; + u32 cnt = 0; + + list_for_each_entry(pl, head, node) { + if (!pl->prog) + continue; + cnt++; } + return cnt; +} + +/* if parent has non-overridable prog attached, + * disallow attaching new programs to the descendent cgroup. + * if parent has overridable or multi-prog, allow attaching + */ +static bool hierarchy_allows_attach(struct cgroup *cgrp, + enum bpf_attach_type type, + u32 new_flags) +{ + struct cgroup *p; + + p = cgroup_parent(cgrp); + if (!p) + return true; + do { + u32 flags = p->bpf.flags[type]; + u32 cnt; + + if (flags & BPF_F_ALLOW_MULTI) + return true; + cnt = prog_list_length(&p->bpf.progs[type]); + WARN_ON_ONCE(cnt > 1); + if (cnt == 1) + return !!(flags & BPF_F_ALLOW_OVERRIDE); + p = cgroup_parent(p); + } while (p); + return true; +} + +/* compute a chain of effective programs for a given cgroup: + * start from the list of programs in this cgroup and add + * all parent programs. + * Note that parent's F_ALLOW_OVERRIDE-type program is yielding + * to programs in this cgroup + */ +static int compute_effective_progs(struct cgroup *cgrp, + enum bpf_attach_type type, + struct bpf_prog_array __rcu **array) +{ + struct bpf_prog_array __rcu *progs; + struct bpf_prog_list *pl; + struct cgroup *p = cgrp; + int cnt = 0; + + /* count number of effective programs by walking parents */ + do { + if (cnt == 0 || (p->bpf.flags[type] & BPF_F_ALLOW_MULTI)) + cnt += prog_list_length(&p->bpf.progs[type]); + p = cgroup_parent(p); + } while (p); + + progs = bpf_prog_array_alloc(cnt, GFP_KERNEL); + if (!progs) + return -ENOMEM; + + /* populate the array with effective progs */ + cnt = 0; + p = cgrp; + do { + if (cnt == 0 || (p->bpf.flags[type] & BPF_F_ALLOW_MULTI)) + list_for_each_entry(pl, + &p->bpf.progs[type], node) { + if (!pl->prog) + continue; + rcu_dereference_protected(progs, 1)-> + progs[cnt++] = pl->prog; + } + p = cgroup_parent(p); + } while (p); + + *array = progs; + return 0; +} + +static void activate_effective_progs(struct cgroup *cgrp, + enum bpf_attach_type type, + struct bpf_prog_array __rcu *array) +{ + struct bpf_prog_array __rcu *old_array; + + old_array = xchg(&cgrp->bpf.effective[type], array); + /* free prog array after grace period, since __cgroup_bpf_run_*() + * might be still walking the array + */ + bpf_prog_array_free(old_array); } /** * cgroup_bpf_inherit() - inherit effective programs from parent * @cgrp: the cgroup to modify - * @parent: the parent to inherit from */ -void cgroup_bpf_inherit(struct cgroup *cgrp, struct cgroup *parent) +int cgroup_bpf_inherit(struct cgroup *cgrp) { - unsigned int type; +/* has to use marco instead of const int, since compiler thinks + * that array below is variable length + */ +#define NR ARRAY_SIZE(cgrp->bpf.effective) + struct bpf_prog_array __rcu *arrays[NR] = {}; + int i; - for (type = 0; type < ARRAY_SIZE(cgrp->bpf.effective); type++) { - struct bpf_prog *e; + for (i = 0; i < NR; i++) + INIT_LIST_HEAD(&cgrp->bpf.progs[i]); - e = rcu_dereference_protected(parent->bpf.effective[type], - lockdep_is_held(&cgroup_mutex)); - rcu_assign_pointer(cgrp->bpf.effective[type], e); - cgrp->bpf.disallow_override[type] = parent->bpf.disallow_override[type]; - } + for (i = 0; i < NR; i++) + if (compute_effective_progs(cgrp, i, &arrays[i])) + goto cleanup; + + for (i = 0; i < NR; i++) + activate_effective_progs(cgrp, i, arrays[i]); + + return 0; +cleanup: + for (i = 0; i < NR; i++) + bpf_prog_array_free(arrays[i]); + return -ENOMEM; } +#define BPF_CGROUP_MAX_PROGS 64 + /** - * __cgroup_bpf_update() - Update the pinned program of a cgroup, and + * __cgroup_bpf_attach() - Attach the program to a cgroup, and * propagate the change to descendants * @cgrp: The cgroup which descendants to traverse - * @parent: The parent of @cgrp, or %NULL if @cgrp is the root - * @prog: A new program to pin - * @type: Type of pinning operation (ingress/egress) - * - * Each cgroup has a set of two pointers for bpf programs; one for eBPF - * programs it owns, and which is effective for execution. - * - * If @prog is not %NULL, this function attaches a new program to the cgroup - * and releases the one that is currently attached, if any. @prog is then made - * the effective program of type @type in that cgroup. - * - * If @prog is %NULL, the currently attached program of type @type is released, - * and the effective program of the parent cgroup (if any) is inherited to - * @cgrp. - * - * Then, the descendants of @cgrp are walked and the effective program for - * each of them is set to the effective program of @cgrp unless the - * descendant has its own program attached, in which case the subbranch is - * skipped. This ensures that delegated subcgroups with own programs are left - * untouched. + * @prog: A program to attach + * @type: Type of attach operation * * Must be called with cgroup_mutex held. */ -int __cgroup_bpf_update(struct cgroup *cgrp, struct cgroup *parent, - struct bpf_prog *prog, enum bpf_attach_type type, - bool new_overridable) +int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, + enum bpf_attach_type type, u32 flags) { - struct bpf_prog *old_prog, *effective = NULL; - struct cgroup_subsys_state *pos; - bool overridable = true; - - if (parent) { - overridable = !parent->bpf.disallow_override[type]; - effective = rcu_dereference_protected(parent->bpf.effective[type], - lockdep_is_held(&cgroup_mutex)); - } - - if (prog && effective && !overridable) - /* if parent has non-overridable prog attached, disallow - * attaching new programs to descendent cgroup - */ + struct list_head *progs = &cgrp->bpf.progs[type]; + struct bpf_prog *old_prog = NULL; + struct cgroup_subsys_state *css; + struct bpf_prog_list *pl; + bool pl_was_allocated; + u32 old_flags; + int err; + + if ((flags & BPF_F_ALLOW_OVERRIDE) && (flags & BPF_F_ALLOW_MULTI)) + /* invalid combination */ + return -EINVAL; + + if (!hierarchy_allows_attach(cgrp, type, flags)) return -EPERM; - if (prog && effective && overridable != new_overridable) - /* if parent has overridable prog attached, only - * allow overridable programs in descendent cgroup + if (!list_empty(progs) && cgrp->bpf.flags[type] != flags) + /* Disallow attaching non-overridable on top + * of existing overridable in this cgroup. + * Disallow attaching multi-prog if overridable or none */ return -EPERM; - old_prog = cgrp->bpf.prog[type]; - - if (prog) { - overridable = new_overridable; - effective = prog; - if (old_prog && - cgrp->bpf.disallow_override[type] == new_overridable) - /* disallow attaching non-overridable on top - * of existing overridable in this cgroup - * and vice versa - */ - return -EPERM; + if (prog_list_length(progs) >= BPF_CGROUP_MAX_PROGS) + return -E2BIG; + + if (flags & BPF_F_ALLOW_MULTI) { + list_for_each_entry(pl, progs, node) + if (pl->prog == prog) + /* disallow attaching the same prog twice */ + return -EINVAL; + + pl = kmalloc(sizeof(*pl), GFP_KERNEL); + if (!pl) + return -ENOMEM; + pl_was_allocated = true; + pl->prog = prog; + list_add_tail(&pl->node, progs); + } else { + if (list_empty(progs)) { + pl = kmalloc(sizeof(*pl), GFP_KERNEL); + if (!pl) + return -ENOMEM; + pl_was_allocated = true; + list_add_tail(&pl->node, progs); + } else { + pl = list_first_entry(progs, typeof(*pl), node); + old_prog = pl->prog; + pl_was_allocated = false; + } + pl->prog = prog; } - if (!prog && !old_prog) - /* report error when trying to detach and nothing is attached */ - return -ENOENT; + old_flags = cgrp->bpf.flags[type]; + cgrp->bpf.flags[type] = flags; - cgrp->bpf.prog[type] = prog; + /* allocate and recompute effective prog arrays */ + css_for_each_descendant_pre(css, &cgrp->self) { + struct cgroup *desc = container_of(css, struct cgroup, self); - css_for_each_descendant_pre(pos, &cgrp->self) { - struct cgroup *desc = container_of(pos, struct cgroup, self); - - /* skip the subtree if the descendant has its own program */ - if (desc->bpf.prog[type] && desc != cgrp) { - pos = css_rightmost_descendant(pos); - } else { - rcu_assign_pointer(desc->bpf.effective[type], - effective); - desc->bpf.disallow_override[type] = !overridable; - } + err = compute_effective_progs(desc, type, &desc->bpf.inactive); + if (err) + goto cleanup; } - if (prog) - static_branch_inc(&cgroup_bpf_enabled_key); + /* all allocations were successful. Activate all prog arrays */ + css_for_each_descendant_pre(css, &cgrp->self) { + struct cgroup *desc = container_of(css, struct cgroup, self); + activate_effective_progs(desc, type, desc->bpf.inactive); + desc->bpf.inactive = NULL; + } + + static_branch_inc(&cgroup_bpf_enabled_key); if (old_prog) { bpf_prog_put(old_prog); static_branch_dec(&cgroup_bpf_enabled_key); } return 0; + +cleanup: + /* oom while computing effective. Free all computed effective arrays + * since they were not activated + */ + css_for_each_descendant_pre(css, &cgrp->self) { + struct cgroup *desc = container_of(css, struct cgroup, self); + + bpf_prog_array_free(desc->bpf.inactive); + desc->bpf.inactive = NULL; + } + + /* and cleanup the prog list */ + pl->prog = old_prog; + if (pl_was_allocated) { + list_del(&pl->node); + kfree(pl); + } + return err; +} + +/** + * __cgroup_bpf_detach() - Detach the program from a cgroup, and + * propagate the change to descendants + * @cgrp: The cgroup which descendants to traverse + * @prog: A program to detach or NULL + * @type: Type of detach operation + * + * Must be called with cgroup_mutex held. + */ +int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, + enum bpf_attach_type type, u32 unused_flags) +{ + struct list_head *progs = &cgrp->bpf.progs[type]; + u32 flags = cgrp->bpf.flags[type]; + struct bpf_prog *old_prog = NULL; + struct cgroup_subsys_state *css; + struct bpf_prog_list *pl; + int err; + + if (flags & BPF_F_ALLOW_MULTI) { + if (!prog) + /* to detach MULTI prog the user has to specify valid FD + * of the program to be detached + */ + return -EINVAL; + } else { + if (list_empty(progs)) + /* report error when trying to detach and nothing is attached */ + return -ENOENT; + } + + if (flags & BPF_F_ALLOW_MULTI) { + /* find the prog and detach it */ + list_for_each_entry(pl, progs, node) { + if (pl->prog != prog) + continue; + old_prog = prog; + /* mark it deleted, so it's ignored while + * recomputing effective + */ + pl->prog = NULL; + break; + } + if (!old_prog) + return -ENOENT; + } else { + /* to maintain backward compatibility NONE and OVERRIDE cgroups + * allow detaching with invalid FD (prog==NULL) + */ + pl = list_first_entry(progs, typeof(*pl), node); + old_prog = pl->prog; + pl->prog = NULL; + } + + /* allocate and recompute effective prog arrays */ + css_for_each_descendant_pre(css, &cgrp->self) { + struct cgroup *desc = container_of(css, struct cgroup, self); + + err = compute_effective_progs(desc, type, &desc->bpf.inactive); + if (err) + goto cleanup; + } + + /* all allocations were successful. Activate all prog arrays */ + css_for_each_descendant_pre(css, &cgrp->self) { + struct cgroup *desc = container_of(css, struct cgroup, self); + + activate_effective_progs(desc, type, desc->bpf.inactive); + desc->bpf.inactive = NULL; + } + + /* now can actually delete it from this cgroup list */ + list_del(&pl->node); + kfree(pl); + if (list_empty(progs)) + /* last program was detached, reset flags to zero */ + cgrp->bpf.flags[type] = 0; + + bpf_prog_put(old_prog); + static_branch_dec(&cgroup_bpf_enabled_key); + return 0; + +cleanup: + /* oom while computing effective. Free all computed effective arrays + * since they were not activated + */ + css_for_each_descendant_pre(css, &cgrp->self) { + struct cgroup *desc = container_of(css, struct cgroup, self); + + bpf_prog_array_free(desc->bpf.inactive); + desc->bpf.inactive = NULL; + } + + /* and restore back old_prog */ + pl->prog = old_prog; + return err; } /** @@ -171,36 +403,26 @@ int __cgroup_bpf_run_filter_skb(struct sock *sk, struct sk_buff *skb, enum bpf_attach_type type) { - struct bpf_prog *prog; + unsigned int offset = skb->data - skb_network_header(skb); + struct sock *save_sk; struct cgroup *cgrp; - int ret = 0; + int ret; if (!sk || !sk_fullsock(sk)) return 0; - if (sk->sk_family != AF_INET && - sk->sk_family != AF_INET6) + if (sk->sk_family != AF_INET && sk->sk_family != AF_INET6) return 0; cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); - - rcu_read_lock(); - - prog = rcu_dereference(cgrp->bpf.effective[type]); - if (prog) { - unsigned int offset = skb->data - skb_network_header(skb); - struct sock *save_sk = skb->sk; - - skb->sk = sk; - __skb_push(skb, offset); - ret = bpf_prog_run_save_cb(prog, skb) == 1 ? 0 : -EPERM; - __skb_pull(skb, offset); - skb->sk = save_sk; - } - - rcu_read_unlock(); - - return ret; + save_sk = skb->sk; + skb->sk = sk; + __skb_push(skb, offset); + ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], skb, + bpf_prog_run_save_cb); + __skb_pull(skb, offset); + skb->sk = save_sk; + return ret == 1 ? 0 : -EPERM; } EXPORT_SYMBOL(__cgroup_bpf_run_filter_skb); @@ -221,19 +443,10 @@ int __cgroup_bpf_run_filter_sk(struct sock *sk, enum bpf_attach_type type) { struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); - struct bpf_prog *prog; - int ret = 0; - - - rcu_read_lock(); - - prog = rcu_dereference(cgrp->bpf.effective[type]); - if (prog) - ret = BPF_PROG_RUN(prog, sk) == 1 ? 0 : -EPERM; + int ret; - rcu_read_unlock(); - - return ret; + ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], sk, BPF_PROG_RUN); + return ret == 1 ? 0 : -EPERM; } EXPORT_SYMBOL(__cgroup_bpf_run_filter_sk); @@ -258,18 +471,10 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk, enum bpf_attach_type type) { struct cgroup *cgrp = sock_cgroup_ptr(&sk->sk_cgrp_data); - struct bpf_prog *prog; - int ret = 0; - - - rcu_read_lock(); - - prog = rcu_dereference(cgrp->bpf.effective[type]); - if (prog) - ret = BPF_PROG_RUN(prog, sock_ops) == 1 ? 0 : -EPERM; - - rcu_read_unlock(); + int ret; - return ret; + ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], sock_ops, + BPF_PROG_RUN); + return ret == 1 ? 0 : -EPERM; } EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_ops); diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 917cc04a0a94..6b49e1991ae7 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1381,6 +1381,37 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) } EXPORT_SYMBOL_GPL(bpf_prog_select_runtime); +/* to avoid allocating empty bpf_prog_array for cgroups that + * don't have bpf program attached use one global 'empty_prog_array' + * It will not be modified the caller of bpf_prog_array_alloc() + * (since caller requested prog_cnt == 0) + * that pointer should be 'freed' by bpf_prog_array_free() + */ +static struct { + struct bpf_prog_array hdr; + struct bpf_prog *null_prog; +} empty_prog_array = { + .null_prog = NULL, +}; + +struct bpf_prog_array __rcu *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags) +{ + if (prog_cnt) + return kzalloc(sizeof(struct bpf_prog_array) + + sizeof(struct bpf_prog *) * (prog_cnt + 1), + flags); + + return &empty_prog_array.hdr; +} + +void bpf_prog_array_free(struct bpf_prog_array __rcu *progs) +{ + if (!progs || + progs == (struct bpf_prog_array __rcu *)&empty_prog_array.hdr) + return; + kfree_rcu(progs, rcu); +} + static void bpf_prog_free_deferred(struct work_struct *work) { struct bpf_prog_aux *aux; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b927da66f653..51bee695d32c 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1168,6 +1168,9 @@ static int sockmap_get_from_fd(const union bpf_attr *attr, bool attach) return 0; } +#define BPF_F_ATTACH_MASK \ + (BPF_F_ALLOW_OVERRIDE | BPF_F_ALLOW_MULTI) + static int bpf_prog_attach(const union bpf_attr *attr) { enum bpf_prog_type ptype; @@ -1181,7 +1184,7 @@ static int bpf_prog_attach(const union bpf_attr *attr) if (CHECK_ATTR(BPF_PROG_ATTACH)) return -EINVAL; - if (attr->attach_flags & ~BPF_F_ALLOW_OVERRIDE) + if (attr->attach_flags & ~BPF_F_ATTACH_MASK) return -EINVAL; switch (attr->attach_type) { @@ -1212,8 +1215,8 @@ static int bpf_prog_attach(const union bpf_attr *attr) return PTR_ERR(cgrp); } - ret = cgroup_bpf_update(cgrp, prog, attr->attach_type, - attr->attach_flags & BPF_F_ALLOW_OVERRIDE); + ret = cgroup_bpf_attach(cgrp, prog, attr->attach_type, + attr->attach_flags); if (ret) bpf_prog_put(prog); cgroup_put(cgrp); @@ -1225,6 +1228,8 @@ static int bpf_prog_attach(const union bpf_attr *attr) static int bpf_prog_detach(const union bpf_attr *attr) { + enum bpf_prog_type ptype; + struct bpf_prog *prog; struct cgroup *cgrp; int ret; @@ -1237,23 +1242,33 @@ static int bpf_prog_detach(const union bpf_attr *attr) switch (attr->attach_type) { case BPF_CGROUP_INET_INGRESS: case BPF_CGROUP_INET_EGRESS: + ptype = BPF_PROG_TYPE_CGROUP_SKB; + break; case BPF_CGROUP_INET_SOCK_CREATE: + ptype = BPF_PROG_TYPE_CGROUP_SOCK; + break; case BPF_CGROUP_SOCK_OPS: - cgrp = cgroup_get_from_fd(attr->target_fd); - if (IS_ERR(cgrp)) - return PTR_ERR(cgrp); - - ret = cgroup_bpf_update(cgrp, NULL, attr->attach_type, false); - cgroup_put(cgrp); + ptype = BPF_PROG_TYPE_SOCK_OPS; break; case BPF_SK_SKB_STREAM_PARSER: case BPF_SK_SKB_STREAM_VERDICT: - ret = sockmap_get_from_fd(attr, false); - break; + return sockmap_get_from_fd(attr, false); default: return -EINVAL; } + cgrp = cgroup_get_from_fd(attr->target_fd); + if (IS_ERR(cgrp)) + return PTR_ERR(cgrp); + + prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); + if (IS_ERR(prog)) + prog = NULL; + + ret = cgroup_bpf_detach(cgrp, prog, attr->attach_type, 0); + if (prog) + bpf_prog_put(prog); + cgroup_put(cgrp); return ret; } diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index d6551cd45238..57eb866ae78d 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -1896,6 +1896,9 @@ int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask, int ref_flags) if (ret) goto destroy_root; + ret = cgroup_bpf_inherit(root_cgrp); + WARN_ON_ONCE(ret); + trace_cgroup_setup_root(root); /* @@ -4713,6 +4716,9 @@ static struct cgroup *cgroup_create(struct cgroup *parent) cgrp->self.parent = &parent->self; cgrp->root = root; cgrp->level = level; + ret = cgroup_bpf_inherit(cgrp); + if (ret) + goto out_idr_free; for (tcgrp = cgrp; tcgrp; tcgrp = cgroup_parent(tcgrp)) { cgrp->ancestor_ids[tcgrp->level] = tcgrp->id; @@ -4747,13 +4753,12 @@ static struct cgroup *cgroup_create(struct cgroup *parent) if (!cgroup_on_dfl(cgrp)) cgrp->subtree_control = cgroup_control(cgrp); - if (parent) - cgroup_bpf_inherit(cgrp, parent); - cgroup_propagate_control(cgrp); return cgrp; +out_idr_free: + cgroup_idr_remove(&root->cgroup_idr, cgrp->id); out_cancel_ref: percpu_ref_exit(&cgrp->self.refcnt); out_free_cgrp: @@ -5736,14 +5741,23 @@ void cgroup_sk_free(struct sock_cgroup_data *skcd) #endif /* CONFIG_SOCK_CGROUP_DATA */ #ifdef CONFIG_CGROUP_BPF -int cgroup_bpf_update(struct cgroup *cgrp, struct bpf_prog *prog, - enum bpf_attach_type type, bool overridable) +int cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, + enum bpf_attach_type type, u32 flags) +{ + int ret; + + mutex_lock(&cgroup_mutex); + ret = __cgroup_bpf_attach(cgrp, prog, type, flags); + mutex_unlock(&cgroup_mutex); + return ret; +} +int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, + enum bpf_attach_type type, u32 flags) { - struct cgroup *parent = cgroup_parent(cgrp); int ret; mutex_lock(&cgroup_mutex); - ret = __cgroup_bpf_update(cgrp, parent, prog, type, overridable); + ret = __cgroup_bpf_detach(cgrp, prog, type, flags); mutex_unlock(&cgroup_mutex); return ret; } -- cgit v1.2.3-59-g8ed1b From 468e2f64d220fe2dc11caa2bcb9b3a1e50fc7321 Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Mon, 2 Oct 2017 22:50:22 -0700 Subject: bpf: introduce BPF_PROG_QUERY command introduce BPF_PROG_QUERY command to retrieve a set of either attached programs to given cgroup or a set of effective programs that will execute for events within a cgroup Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Acked-by: Martin KaFai Lau for cgroup bits Acked-by: Tejun Heo Signed-off-by: David S. Miller --- include/linux/bpf-cgroup.h | 4 ++++ include/linux/bpf.h | 3 +++ include/uapi/linux/bpf.h | 13 +++++++++++++ kernel/bpf/cgroup.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/core.c | 38 ++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 34 ++++++++++++++++++++++++++++++++++ kernel/cgroup/cgroup.c | 10 ++++++++++ 7 files changed, 148 insertions(+) (limited to 'include') diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index 102e56fbb6de..359b6f5d3d90 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -44,12 +44,16 @@ int __cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, enum bpf_attach_type type, u32 flags); int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, enum bpf_attach_type type, u32 flags); +int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, + union bpf_attr __user *uattr); /* Wrapper for __cgroup_bpf_*() protected by cgroup_mutex */ int cgroup_bpf_attach(struct cgroup *cgrp, struct bpf_prog *prog, enum bpf_attach_type type, u32 flags); int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, enum bpf_attach_type type, u32 flags); +int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, + union bpf_attr __user *uattr); int __cgroup_bpf_run_filter_skb(struct sock *sk, struct sk_buff *skb, diff --git a/include/linux/bpf.h b/include/linux/bpf.h index a6964b75f070..a67daea731ab 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -260,6 +260,9 @@ struct bpf_prog_array { struct bpf_prog_array __rcu *bpf_prog_array_alloc(u32 prog_cnt, gfp_t flags); void bpf_prog_array_free(struct bpf_prog_array __rcu *progs); +int bpf_prog_array_length(struct bpf_prog_array __rcu *progs); +int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, + __u32 __user *prog_ids, u32 cnt); #define BPF_PROG_RUN_ARRAY(array, ctx, func) \ ({ \ diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 762f74bc6c47..cb2b9f95160a 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -92,6 +92,7 @@ enum bpf_cmd { BPF_PROG_GET_FD_BY_ID, BPF_MAP_GET_FD_BY_ID, BPF_OBJ_GET_INFO_BY_FD, + BPF_PROG_QUERY, }; enum bpf_map_type { @@ -211,6 +212,9 @@ enum bpf_attach_type { /* Specify numa node during map creation */ #define BPF_F_NUMA_NODE (1U << 2) +/* flags for BPF_PROG_QUERY */ +#define BPF_F_QUERY_EFFECTIVE (1U << 0) + #define BPF_OBJ_NAME_LEN 16U union bpf_attr { @@ -289,6 +293,15 @@ union bpf_attr { __u32 info_len; __aligned_u64 info; } info; + + struct { /* anonymous struct used by BPF_PROG_QUERY command */ + __u32 target_fd; /* container object to query */ + __u32 attach_type; + __u32 query_flags; + __u32 attach_flags; + __aligned_u64 prog_ids; + __u32 prog_cnt; + } query; } __attribute__((aligned(8))); /* BPF helper function descriptions: diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 6b7500bbdb53..e88abc0865d5 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -384,6 +384,52 @@ cleanup: return err; } +/* Must be called with cgroup_mutex held to avoid races. */ +int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, + union bpf_attr __user *uattr) +{ + __u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids); + enum bpf_attach_type type = attr->query.attach_type; + struct list_head *progs = &cgrp->bpf.progs[type]; + u32 flags = cgrp->bpf.flags[type]; + int cnt, ret = 0, i; + + if (attr->query.query_flags & BPF_F_QUERY_EFFECTIVE) + cnt = bpf_prog_array_length(cgrp->bpf.effective[type]); + else + cnt = prog_list_length(progs); + + if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags))) + return -EFAULT; + if (copy_to_user(&uattr->query.prog_cnt, &cnt, sizeof(cnt))) + return -EFAULT; + if (attr->query.prog_cnt == 0 || !prog_ids || !cnt) + /* return early if user requested only program count + flags */ + return 0; + if (attr->query.prog_cnt < cnt) { + cnt = attr->query.prog_cnt; + ret = -ENOSPC; + } + + if (attr->query.query_flags & BPF_F_QUERY_EFFECTIVE) { + return bpf_prog_array_copy_to_user(cgrp->bpf.effective[type], + prog_ids, cnt); + } else { + struct bpf_prog_list *pl; + u32 id; + + i = 0; + list_for_each_entry(pl, progs, node) { + id = pl->prog->aux->id; + if (copy_to_user(prog_ids + i, &id, sizeof(id))) + return -EFAULT; + if (++i == cnt) + break; + } + } + return ret; +} + /** * __cgroup_bpf_run_filter_skb() - Run a program for packet filtering * @sk: The socket sending or receiving traffic diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 6b49e1991ae7..eba966c09053 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1412,6 +1412,44 @@ void bpf_prog_array_free(struct bpf_prog_array __rcu *progs) kfree_rcu(progs, rcu); } +int bpf_prog_array_length(struct bpf_prog_array __rcu *progs) +{ + struct bpf_prog **prog; + u32 cnt = 0; + + rcu_read_lock(); + prog = rcu_dereference(progs)->progs; + for (; *prog; prog++) + cnt++; + rcu_read_unlock(); + return cnt; +} + +int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, + __u32 __user *prog_ids, u32 cnt) +{ + struct bpf_prog **prog; + u32 i = 0, id; + + rcu_read_lock(); + prog = rcu_dereference(progs)->progs; + for (; *prog; prog++) { + id = (*prog)->aux->id; + if (copy_to_user(prog_ids + i, &id, sizeof(id))) { + rcu_read_unlock(); + return -EFAULT; + } + if (++i == cnt) { + prog++; + break; + } + } + rcu_read_unlock(); + if (*prog) + return -ENOSPC; + return 0; +} + static void bpf_prog_free_deferred(struct work_struct *work) { struct bpf_prog_aux *aux; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 51bee695d32c..0048cb24ba7b 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1272,6 +1272,37 @@ static int bpf_prog_detach(const union bpf_attr *attr) return ret; } +#define BPF_PROG_QUERY_LAST_FIELD query.prog_cnt + +static int bpf_prog_query(const union bpf_attr *attr, + union bpf_attr __user *uattr) +{ + struct cgroup *cgrp; + int ret; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (CHECK_ATTR(BPF_PROG_QUERY)) + return -EINVAL; + if (attr->query.query_flags & ~BPF_F_QUERY_EFFECTIVE) + return -EINVAL; + + switch (attr->query.attach_type) { + case BPF_CGROUP_INET_INGRESS: + case BPF_CGROUP_INET_EGRESS: + case BPF_CGROUP_INET_SOCK_CREATE: + case BPF_CGROUP_SOCK_OPS: + break; + default: + return -EINVAL; + } + cgrp = cgroup_get_from_fd(attr->query.target_fd); + if (IS_ERR(cgrp)) + return PTR_ERR(cgrp); + ret = cgroup_bpf_query(cgrp, attr, uattr); + cgroup_put(cgrp); + return ret; +} #endif /* CONFIG_CGROUP_BPF */ #define BPF_PROG_TEST_RUN_LAST_FIELD test.duration @@ -1568,6 +1599,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz case BPF_PROG_DETACH: err = bpf_prog_detach(&attr); break; + case BPF_PROG_QUERY: + err = bpf_prog_query(&attr, uattr); + break; #endif case BPF_PROG_TEST_RUN: err = bpf_prog_test_run(&attr, uattr); diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 57eb866ae78d..269512b94a94 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -5761,4 +5761,14 @@ int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog, mutex_unlock(&cgroup_mutex); return ret; } +int cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr, + union bpf_attr __user *uattr) +{ + int ret; + + mutex_lock(&cgroup_mutex); + ret = __cgroup_bpf_query(cgrp, attr, uattr); + mutex_unlock(&cgroup_mutex); + return ret; +} #endif /* CONFIG_CGROUP_BPF */ -- cgit v1.2.3-59-g8ed1b From 6621dd29eb9b5e6774ec7a9a75161352fdea47fc Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Tue, 3 Oct 2017 13:53:23 +0200 Subject: dev: advertise the new nsid when the netns iface changes x-netns interfaces are bound to two netns: the link netns and the upper netns. Usually, this kind of interfaces is created in the link netns and then moved to the upper netns. At the end, the interface is visible only in the upper netns. The link nsid is advertised via netlink in the upper netns, thus the user always knows where is the link part. There is no such mechanism in the link netns. When the interface is moved to another netns, the user cannot "follow" it. This patch adds a new netlink attribute which helps to follow an interface which moves to another netns. When the interface is unregistered, the new nsid is advertised. If the interface is a x-netns interface (ie rtnl_link_ops->get_link_net is defined), the nsid is allocated if needed. CC: Jason A. Donenfeld Signed-off-by: Nicolas Dichtel Signed-off-by: David S. Miller --- include/linux/rtnetlink.h | 4 +++- include/uapi/linux/if_link.h | 1 + net/core/dev.c | 11 ++++++++--- net/core/rtnetlink.c | 31 ++++++++++++++++++++++--------- 4 files changed, 34 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index dea59c8eec54..1251638e60d3 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -17,9 +17,11 @@ extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id, long expires, u32 error); void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change, gfp_t flags); +void rtmsg_ifinfo_newnet(int type, struct net_device *dev, unsigned int change, + gfp_t flags, int *new_nsid); struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, unsigned change, u32 event, - gfp_t flags); + gfp_t flags, int *new_nsid); void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags); diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index ea87bd708ee9..cd580fc0e58f 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -158,6 +158,7 @@ enum { IFLA_PAD, IFLA_XDP, IFLA_EVENT, + IFLA_NEW_NETNSID, __IFLA_MAX }; diff --git a/net/core/dev.c b/net/core/dev.c index 454f05441546..bffc75429184 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -145,6 +145,7 @@ #include #include #include +#include #include "net-sysfs.h" @@ -7204,7 +7205,7 @@ static void rollback_registered_many(struct list_head *head) if (!dev->rtnl_link_ops || dev->rtnl_link_state == RTNL_LINK_INITIALIZED) skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U, 0, - GFP_KERNEL); + GFP_KERNEL, NULL); /* * Flush the unicast and multicast chains @@ -8291,7 +8292,7 @@ EXPORT_SYMBOL(unregister_netdev); int dev_change_net_namespace(struct net_device *dev, struct net *net, const char *pat) { - int err; + int err, new_nsid; ASSERT_RTNL(); @@ -8347,7 +8348,11 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char call_netdevice_notifiers(NETDEV_UNREGISTER, dev); rcu_barrier(); call_netdevice_notifiers(NETDEV_UNREGISTER_FINAL, dev); - rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL); + if (dev->rtnl_link_ops && dev->rtnl_link_ops->get_link_net) + new_nsid = peernet2id_alloc(dev_net(dev), net); + else + new_nsid = peernet2id(dev_net(dev), net); + rtmsg_ifinfo_newnet(RTM_DELLINK, dev, ~0U, GFP_KERNEL, &new_nsid); /* * Flush the unicast and multicast chains diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 3fb1ca33cba4..1ee98b1369d5 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -915,6 +915,7 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(IFNAMSIZ) /* IFLA_PHYS_PORT_NAME */ + rtnl_xdp_size() /* IFLA_XDP */ + nla_total_size(4) /* IFLA_EVENT */ + + nla_total_size(4) /* IFLA_NEW_NETNSID */ + nla_total_size(1); /* IFLA_PROTO_DOWN */ } @@ -1384,7 +1385,7 @@ static int rtnl_fill_link_netnsid(struct sk_buff *skb, static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, int type, u32 pid, u32 seq, u32 change, unsigned int flags, u32 ext_filter_mask, - u32 event) + u32 event, int *new_nsid) { struct ifinfomsg *ifm; struct nlmsghdr *nlh; @@ -1472,6 +1473,10 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, if (rtnl_fill_link_netnsid(skb, dev)) goto nla_put_failure; + if (new_nsid && + nla_put_s32(skb, IFLA_NEW_NETNSID, *new_nsid) < 0) + goto nla_put_failure; + if (!(af_spec = nla_nest_start(skb, IFLA_AF_SPEC))) goto nla_put_failure; @@ -1701,7 +1706,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 0, flags, - ext_filter_mask, 0); + ext_filter_mask, 0, NULL); if (err < 0) { if (likely(skb->len)) @@ -2808,7 +2813,7 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh, return -ENOBUFS; err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, 0, 0, ext_filter_mask, 0); + nlh->nlmsg_seq, 0, 0, ext_filter_mask, 0, NULL); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size */ WARN_ON(err == -EMSGSIZE); @@ -2893,7 +2898,7 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, unsigned int change, - u32 event, gfp_t flags) + u32 event, gfp_t flags, int *new_nsid) { struct net *net = dev_net(dev); struct sk_buff *skb; @@ -2904,7 +2909,8 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, if (skb == NULL) goto errout; - err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0, event); + err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0, event, + new_nsid); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); @@ -2927,14 +2933,14 @@ void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags) static void rtmsg_ifinfo_event(int type, struct net_device *dev, unsigned int change, u32 event, - gfp_t flags) + gfp_t flags, int *new_nsid) { struct sk_buff *skb; if (dev->reg_state != NETREG_REGISTERED) return; - skb = rtmsg_ifinfo_build_skb(type, dev, change, event, flags); + skb = rtmsg_ifinfo_build_skb(type, dev, change, event, flags, new_nsid); if (skb) rtmsg_ifinfo_send(skb, dev, flags); } @@ -2942,10 +2948,17 @@ static void rtmsg_ifinfo_event(int type, struct net_device *dev, void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, gfp_t flags) { - rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags); + rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags, NULL); } EXPORT_SYMBOL(rtmsg_ifinfo); +void rtmsg_ifinfo_newnet(int type, struct net_device *dev, unsigned int change, + gfp_t flags, int *new_nsid) +{ + rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags, + new_nsid); +} + static int nlmsg_populate_fdb_fill(struct sk_buff *skb, struct net_device *dev, u8 *addr, u16 vid, u32 pid, u32 seq, @@ -4321,7 +4334,7 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_RESEND_IGMP: case NETDEV_CHANGEINFODATA: rtmsg_ifinfo_event(RTM_NEWLINK, dev, 0, rtnl_get_event(event), - GFP_KERNEL); + GFP_KERNEL, NULL); break; default: break; -- cgit v1.2.3-59-g8ed1b From 51d0c04795a4b5d9a188336884887a9d394a94b0 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 4 Oct 2017 17:48:45 -0700 Subject: net: Add extack to netdev_notifier_info Add netlink_ext_ack to netdev_notifier_info to allow notifier handlers to return errors to userspace. Clean up the initialization in dev.c such that extack is easily added in subsequent patches where relevant. Specifically, remove the init call in call_netdevice_notifiers_info and have callers initalize on stack when info is declared. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/linux/netdevice.h | 10 +++++- net/core/dev.c | 79 ++++++++++++++++++++++++++++------------------- 2 files changed, 56 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index d04424cfffba..05fcaba4b0d9 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2309,7 +2309,8 @@ int register_netdevice_notifier(struct notifier_block *nb); int unregister_netdevice_notifier(struct notifier_block *nb); struct netdev_notifier_info { - struct net_device *dev; + struct net_device *dev; + struct netlink_ext_ack *extack; }; struct netdev_notifier_change_info { @@ -2334,6 +2335,7 @@ static inline void netdev_notifier_info_init(struct netdev_notifier_info *info, struct net_device *dev) { info->dev = dev; + info->extack = NULL; } static inline struct net_device * @@ -2342,6 +2344,12 @@ netdev_notifier_info_to_dev(const struct netdev_notifier_info *info) return info->dev; } +static inline struct netlink_ext_ack * +netdev_notifier_info_to_extack(const struct netdev_notifier_info *info) +{ + return info->extack; +} + int call_netdevice_notifiers(unsigned long val, struct net_device *dev); diff --git a/net/core/dev.c b/net/core/dev.c index bffc75429184..e27a6bc0ac4d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -163,7 +163,6 @@ static struct list_head offload_base __read_mostly; static int netif_rx_internal(struct sk_buff *skb); static int call_netdevice_notifiers_info(unsigned long val, - struct net_device *dev, struct netdev_notifier_info *info); static struct napi_struct *napi_by_id(unsigned int napi_id); @@ -1339,10 +1338,11 @@ EXPORT_SYMBOL(netdev_features_change); void netdev_state_change(struct net_device *dev) { if (dev->flags & IFF_UP) { - struct netdev_notifier_change_info change_info; + struct netdev_notifier_change_info change_info = { + .info.dev = dev, + }; - change_info.flags_changed = 0; - call_netdevice_notifiers_info(NETDEV_CHANGE, dev, + call_netdevice_notifiers_info(NETDEV_CHANGE, &change_info.info); rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); } @@ -1563,9 +1563,10 @@ EXPORT_SYMBOL(dev_disable_lro); static int call_netdevice_notifier(struct notifier_block *nb, unsigned long val, struct net_device *dev) { - struct netdev_notifier_info info; + struct netdev_notifier_info info = { + .dev = dev, + }; - netdev_notifier_info_init(&info, dev); return nb->notifier_call(nb, val, &info); } @@ -1690,11 +1691,9 @@ EXPORT_SYMBOL(unregister_netdevice_notifier); */ static int call_netdevice_notifiers_info(unsigned long val, - struct net_device *dev, struct netdev_notifier_info *info) { ASSERT_RTNL(); - netdev_notifier_info_init(info, dev); return raw_notifier_call_chain(&netdev_chain, val, info); } @@ -1709,9 +1708,11 @@ static int call_netdevice_notifiers_info(unsigned long val, int call_netdevice_notifiers(unsigned long val, struct net_device *dev) { - struct netdev_notifier_info info; + struct netdev_notifier_info info = { + .dev = dev, + }; - return call_netdevice_notifiers_info(val, dev, &info); + return call_netdevice_notifiers_info(val, &info); } EXPORT_SYMBOL(call_netdevice_notifiers); @@ -6278,7 +6279,15 @@ static int __netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, bool master, void *upper_priv, void *upper_info) { - struct netdev_notifier_changeupper_info changeupper_info; + struct netdev_notifier_changeupper_info changeupper_info = { + .info = { + .dev = dev, + }, + .upper_dev = upper_dev, + .master = master, + .linking = true, + .upper_info = upper_info, + }; int ret = 0; ASSERT_RTNL(); @@ -6296,12 +6305,7 @@ static int __netdev_upper_dev_link(struct net_device *dev, if (master && netdev_master_upper_dev_get(dev)) return -EBUSY; - changeupper_info.upper_dev = upper_dev; - changeupper_info.master = master; - changeupper_info.linking = true; - changeupper_info.upper_info = upper_info; - - ret = call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev, + ret = call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, &changeupper_info.info); ret = notifier_to_errno(ret); if (ret) @@ -6312,7 +6316,7 @@ static int __netdev_upper_dev_link(struct net_device *dev, if (ret) return ret; - ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev, + ret = call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, &changeupper_info.info); ret = notifier_to_errno(ret); if (ret) @@ -6376,20 +6380,24 @@ EXPORT_SYMBOL(netdev_master_upper_dev_link); void netdev_upper_dev_unlink(struct net_device *dev, struct net_device *upper_dev) { - struct netdev_notifier_changeupper_info changeupper_info; + struct netdev_notifier_changeupper_info changeupper_info = { + .info = { + .dev = dev, + }, + .upper_dev = upper_dev, + .linking = false, + }; ASSERT_RTNL(); - changeupper_info.upper_dev = upper_dev; changeupper_info.master = netdev_master_upper_dev_get(dev) == upper_dev; - changeupper_info.linking = false; - call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, dev, + call_netdevice_notifiers_info(NETDEV_PRECHANGEUPPER, &changeupper_info.info); __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); - call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, dev, + call_netdevice_notifiers_info(NETDEV_CHANGEUPPER, &changeupper_info.info); } EXPORT_SYMBOL(netdev_upper_dev_unlink); @@ -6405,11 +6413,13 @@ EXPORT_SYMBOL(netdev_upper_dev_unlink); void netdev_bonding_info_change(struct net_device *dev, struct netdev_bonding_info *bonding_info) { - struct netdev_notifier_bonding_info info; + struct netdev_notifier_bonding_info info = { + .info.dev = dev, + }; memcpy(&info.bonding_info, bonding_info, sizeof(struct netdev_bonding_info)); - call_netdevice_notifiers_info(NETDEV_BONDING_INFO, dev, + call_netdevice_notifiers_info(NETDEV_BONDING_INFO, &info.info); } EXPORT_SYMBOL(netdev_bonding_info_change); @@ -6535,11 +6545,13 @@ EXPORT_SYMBOL(dev_get_nest_level); void netdev_lower_state_changed(struct net_device *lower_dev, void *lower_state_info) { - struct netdev_notifier_changelowerstate_info changelowerstate_info; + struct netdev_notifier_changelowerstate_info changelowerstate_info = { + .info.dev = lower_dev, + }; ASSERT_RTNL(); changelowerstate_info.lower_state_info = lower_state_info; - call_netdevice_notifiers_info(NETDEV_CHANGELOWERSTATE, lower_dev, + call_netdevice_notifiers_info(NETDEV_CHANGELOWERSTATE, &changelowerstate_info.info); } EXPORT_SYMBOL(netdev_lower_state_changed); @@ -6830,11 +6842,14 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, if (dev->flags & IFF_UP && (changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE))) { - struct netdev_notifier_change_info change_info; - - change_info.flags_changed = changes; - call_netdevice_notifiers_info(NETDEV_CHANGE, dev, - &change_info.info); + struct netdev_notifier_change_info change_info = { + .info = { + .dev = dev, + }, + .flags_changed = changes, + }; + + call_netdevice_notifiers_info(NETDEV_CHANGE, &change_info.info); } } -- cgit v1.2.3-59-g8ed1b From 33eaf2a6eb48ebf00374aaaf4b1b43f9950dcbe4 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 4 Oct 2017 17:48:46 -0700 Subject: net: Add extack to ndo_add_slave Pass extack to do_set_master and down to ndo_add_slave Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 5 +++-- drivers/net/bonding/bond_options.c | 2 +- drivers/net/team/team.c | 3 ++- drivers/net/vrf.c | 3 ++- include/linux/netdevice.h | 3 ++- include/net/bonding.h | 3 ++- net/batman-adv/soft-interface.c | 3 ++- net/bridge/br_device.c | 3 ++- net/core/rtnetlink.c | 10 ++++++---- 9 files changed, 22 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index b19dc033fb36..78feb94a36db 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1328,7 +1328,8 @@ void bond_lower_state_changed(struct slave *slave) } /* enslave device to bond device */ -int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) +int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, + struct netlink_ext_ack *extack) { struct bonding *bond = netdev_priv(bond_dev); const struct net_device_ops *slave_ops = slave_dev->netdev_ops; @@ -3492,7 +3493,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd switch (cmd) { case BOND_ENSLAVE_OLD: case SIOCBONDENSLAVE: - res = bond_enslave(bond_dev, slave_dev); + res = bond_enslave(bond_dev, slave_dev, NULL); break; case BOND_RELEASE_OLD: case SIOCBONDRELEASE: diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 5931aa2fe997..8a9b085c2a98 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -1383,7 +1383,7 @@ static int bond_option_slaves_set(struct bonding *bond, switch (command[0]) { case '+': netdev_dbg(bond->dev, "Adding slave %s\n", dev->name); - ret = bond_enslave(bond->dev, dev); + ret = bond_enslave(bond->dev, dev, NULL); break; case '-': diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index ae53e899259f..4359d45aa131 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1914,7 +1914,8 @@ static int team_netpoll_setup(struct net_device *dev, } #endif -static int team_add_slave(struct net_device *dev, struct net_device *port_dev) +static int team_add_slave(struct net_device *dev, struct net_device *port_dev, + struct netlink_ext_ack *extack) { struct team *team = netdev_priv(dev); int err; diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index cc18b7b11612..4a082ef53533 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -788,7 +788,8 @@ err: return ret; } -static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev) +static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev, + struct netlink_ext_ack *extack) { if (netif_is_l3_master(port_dev) || netif_is_l3_slave(port_dev)) return -EINVAL; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 05fcaba4b0d9..368a5064a487 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1246,7 +1246,8 @@ struct net_device_ops { u32 flow_id); #endif int (*ndo_add_slave)(struct net_device *dev, - struct net_device *slave_dev); + struct net_device *slave_dev, + struct netlink_ext_ack *extack); int (*ndo_del_slave)(struct net_device *dev, struct net_device *slave_dev); netdev_features_t (*ndo_fix_features)(struct net_device *dev, diff --git a/include/net/bonding.h b/include/net/bonding.h index b2e68657a216..2860cc66c2bb 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -596,7 +596,8 @@ void bond_destroy_sysfs(struct bond_net *net); void bond_prepare_sysfs_group(struct bonding *bond); int bond_sysfs_slave_add(struct slave *slave); void bond_sysfs_slave_del(struct slave *slave); -int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev); +int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, + struct netlink_ext_ack *extack); int bond_release(struct net_device *bond_dev, struct net_device *slave_dev); u32 bond_xmit_hash(struct bonding *bond, struct sk_buff *skb); int bond_set_carrier(struct bonding *bond); diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index c2c986746d0b..e7d5fbb6ad53 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -867,7 +867,8 @@ free_bat_counters: * Return: 0 if successful or error otherwise. */ static int batadv_softif_slave_add(struct net_device *dev, - struct net_device *slave_dev) + struct net_device *slave_dev, + struct netlink_ext_ack *extack) { struct batadv_hard_iface *hard_iface; struct net *net = dev_net(dev); diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index f6b6a92f1c48..cb0131d70ab1 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -320,7 +320,8 @@ void br_netpoll_disable(struct net_bridge_port *p) #endif -static int br_add_slave(struct net_device *dev, struct net_device *slave_dev) +static int br_add_slave(struct net_device *dev, struct net_device *slave_dev, + struct netlink_ext_ack *extack) { struct net_bridge *br = netdev_priv(dev); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 1ee98b1369d5..c5ee429bcce9 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1957,7 +1957,8 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr **tb) return err; } -static int do_set_master(struct net_device *dev, int ifindex) +static int do_set_master(struct net_device *dev, int ifindex, + struct netlink_ext_ack *extack) { struct net_device *upper_dev = netdev_master_upper_dev_get(dev); const struct net_device_ops *ops; @@ -1982,7 +1983,7 @@ static int do_set_master(struct net_device *dev, int ifindex) return -EINVAL; ops = upper_dev->netdev_ops; if (ops->ndo_add_slave) { - err = ops->ndo_add_slave(upper_dev, dev); + err = ops->ndo_add_slave(upper_dev, dev, extack); if (err) return err; } else { @@ -2115,7 +2116,7 @@ static int do_setlink(const struct sk_buff *skb, } if (tb[IFLA_MASTER]) { - err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER])); + err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), extack); if (err) goto errout; status |= DO_SETLINK_MODIFIED; @@ -2753,7 +2754,8 @@ replay: goto out_unregister; } if (tb[IFLA_MASTER]) { - err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER])); + err = do_set_master(dev, nla_get_u32(tb[IFLA_MASTER]), + extack); if (err) goto out_unregister; } -- cgit v1.2.3-59-g8ed1b From 42ab19ee90292993370a30ad242599d75a3b749e Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 4 Oct 2017 17:48:47 -0700 Subject: net: Add extack to upper device linking Add extack arg to netdev_upper_dev_link and netdev_master_upper_dev_link Signed-off-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/bonding/bond_main.c | 7 ++++--- drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c | 2 +- drivers/net/hyperv/netvsc_drv.c | 2 +- drivers/net/ipvlan/ipvlan_main.c | 2 +- drivers/net/macsec.c | 2 +- drivers/net/macvlan.c | 7 ++++--- drivers/net/macvtap.c | 2 +- drivers/net/team/team.c | 2 +- drivers/net/usb/qmi_wwan.c | 2 +- drivers/net/vrf.c | 7 ++++--- include/linux/if_macvlan.h | 3 ++- include/linux/netdevice.h | 6 ++++-- net/8021q/vlan.c | 6 +++--- net/8021q/vlan.h | 2 +- net/8021q/vlan_netlink.c | 2 +- net/batman-adv/hard-interface.c | 2 +- net/bridge/br_if.c | 2 +- net/core/dev.c | 15 ++++++++++----- net/openvswitch/vport-netdev.c | 3 ++- 19 files changed, 44 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 78feb94a36db..bc92307c2082 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1217,14 +1217,15 @@ static enum netdev_lag_tx_type bond_lag_tx_type(struct bonding *bond) } } -static int bond_master_upper_dev_link(struct bonding *bond, struct slave *slave) +static int bond_master_upper_dev_link(struct bonding *bond, struct slave *slave, + struct netlink_ext_ack *extack) { struct netdev_lag_upper_info lag_upper_info; int err; lag_upper_info.tx_type = bond_lag_tx_type(bond); err = netdev_master_upper_dev_link(slave->dev, bond->dev, slave, - &lag_upper_info); + &lag_upper_info, extack); if (err) return err; rtmsg_ifinfo(RTM_NEWLINK, slave->dev, IFF_SLAVE, GFP_KERNEL); @@ -1710,7 +1711,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev, goto err_detach; } - res = bond_master_upper_dev_link(bond, new_slave); + res = bond_master_upper_dev_link(bond, new_slave, extack); if (res) { netdev_dbg(bond_dev, "Error %d calling bond_master_upper_dev_link\n", res); goto err_unregister; diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c index 98f22551eb45..1af326a60cbb 100644 --- a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -178,7 +178,7 @@ static int rmnet_newlink(struct net *src_net, struct net_device *dev, if (err) goto err1; - err = netdev_master_upper_dev_link(dev, real_dev, NULL, NULL); + err = netdev_master_upper_dev_link(dev, real_dev, NULL, NULL, extack); if (err) goto err2; diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f300ae61c6c6..dfb986421ec6 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -1748,7 +1748,7 @@ static int netvsc_vf_join(struct net_device *vf_netdev, goto rx_handler_failed; } - ret = netdev_upper_dev_link(vf_netdev, ndev); + ret = netdev_upper_dev_link(vf_netdev, ndev, NULL); if (ret != 0) { netdev_err(vf_netdev, "can not set master device %s (err = %d)\n", diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index c74893c1e620..57c3856bab05 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -584,7 +584,7 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, if (err < 0) goto remove_ida; - err = netdev_upper_dev_link(phy_dev, dev); + err = netdev_upper_dev_link(phy_dev, dev, extack); if (err) { goto unregister_netdev; } diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c index 98e4deaa3a6a..ccbe4eaffe4d 100644 --- a/drivers/net/macsec.c +++ b/drivers/net/macsec.c @@ -3244,7 +3244,7 @@ static int macsec_newlink(struct net *net, struct net_device *dev, &macsec_netdev_addr_lock_key, macsec_get_nest_level(dev)); - err = netdev_upper_dev_link(real_dev, dev); + err = netdev_upper_dev_link(real_dev, dev, extack); if (err < 0) goto unregister; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 1ffe77e95d46..858bd66511a2 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -1344,7 +1344,8 @@ static int macvlan_changelink_sources(struct macvlan_dev *vlan, u32 mode, } int macvlan_common_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[]) + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) { struct macvlan_dev *vlan = netdev_priv(dev); struct macvlan_port *port; @@ -1433,7 +1434,7 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, goto destroy_macvlan_port; dev->priv_flags |= IFF_MACVLAN; - err = netdev_upper_dev_link(lowerdev, dev); + err = netdev_upper_dev_link(lowerdev, dev, extack); if (err) goto unregister_netdev; @@ -1456,7 +1457,7 @@ static int macvlan_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { - return macvlan_common_newlink(src_net, dev, tb, data); + return macvlan_common_newlink(src_net, dev, tb, data, extack); } void macvlan_dellink(struct net_device *dev, struct list_head *head) diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index c2d0ea2fb019..f62aea2fcfa9 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -105,7 +105,7 @@ static int macvtap_newlink(struct net *src_net, struct net_device *dev, /* Don't put anything that may fail after macvlan_common_newlink * because we can't undo what it does. */ - err = macvlan_common_newlink(src_net, dev, tb, data); + err = macvlan_common_newlink(src_net, dev, tb, data, extack); if (err) { netdev_rx_handler_unregister(dev); return err; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 4359d45aa131..a468439969df 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1112,7 +1112,7 @@ static int team_upper_dev_link(struct team *team, struct team_port *port) lag_upper_info.tx_type = team->mode->lag_tx_type; err = netdev_master_upper_dev_link(port->dev, team->dev, NULL, - &lag_upper_info); + &lag_upper_info, NULL); if (err) return err; port->dev->priv_flags |= IFF_TEAM_PORT; diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 8c3733608271..db7279d5b250 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -221,7 +221,7 @@ static int qmimux_register_device(struct net_device *real_dev, u8 mux_id) /* Account for reference in struct qmimux_priv_priv */ dev_hold(real_dev); - err = netdev_upper_dev_link(real_dev, new_dev); + err = netdev_upper_dev_link(real_dev, new_dev, NULL); if (err) goto out_unregister_netdev; diff --git a/drivers/net/vrf.c b/drivers/net/vrf.c index 4a082ef53533..77d0655a0250 100644 --- a/drivers/net/vrf.c +++ b/drivers/net/vrf.c @@ -764,7 +764,8 @@ static void cycle_netdev(struct net_device *dev) } } -static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) +static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev, + struct netlink_ext_ack *extack) { int ret; @@ -775,7 +776,7 @@ static int do_vrf_add_slave(struct net_device *dev, struct net_device *port_dev) return -EOPNOTSUPP; port_dev->priv_flags |= IFF_L3MDEV_SLAVE; - ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL); + ret = netdev_master_upper_dev_link(port_dev, dev, NULL, NULL, extack); if (ret < 0) goto err; @@ -794,7 +795,7 @@ static int vrf_add_slave(struct net_device *dev, struct net_device *port_dev, if (netif_is_l3_master(port_dev) || netif_is_l3_slave(port_dev)) return -EINVAL; - return do_vrf_add_slave(dev, port_dev); + return do_vrf_add_slave(dev, port_dev, extack); } /* inverse of do_vrf_add_slave */ diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index c9ec1343d187..10e319f41fb1 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -72,7 +72,8 @@ static inline void macvlan_count_rx(const struct macvlan_dev *vlan, extern void macvlan_common_setup(struct net_device *dev); extern int macvlan_common_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[]); + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack); extern void macvlan_count_rx(const struct macvlan_dev *vlan, unsigned int len, bool success, diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 368a5064a487..31bb3010c69b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3919,10 +3919,12 @@ void *netdev_adjacent_get_private(struct list_head *adj_list); void *netdev_lower_get_first_private_rcu(struct net_device *dev); struct net_device *netdev_master_upper_dev_get(struct net_device *dev); struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev); -int netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev); +int netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, + struct netlink_ext_ack *extack); int netdev_master_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, - void *upper_priv, void *upper_info); + void *upper_priv, void *upper_info, + struct netlink_ext_ack *extack); void netdev_upper_dev_unlink(struct net_device *dev, struct net_device *upper_dev); void netdev_adjacent_rename_links(struct net_device *dev, char *oldname); diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 9649579b5b9f..71c3e045505b 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -138,7 +138,7 @@ int vlan_check_real_dev(struct net_device *real_dev, return 0; } -int register_vlan_dev(struct net_device *dev) +int register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack) { struct vlan_dev_priv *vlan = vlan_dev_priv(dev); struct net_device *real_dev = vlan->real_dev; @@ -174,7 +174,7 @@ int register_vlan_dev(struct net_device *dev) if (err < 0) goto out_uninit_mvrp; - err = netdev_upper_dev_link(real_dev, dev); + err = netdev_upper_dev_link(real_dev, dev, extack); if (err) goto out_unregister_netdev; @@ -270,7 +270,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) vlan->flags = VLAN_FLAG_REORDER_HDR; new_dev->rtnl_link_ops = &vlan_link_ops; - err = register_vlan_dev(new_dev); + err = register_vlan_dev(new_dev, NULL); if (err < 0) goto out_free_newdev; diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index df8bd65dd370..94f8eed9f9b3 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -107,7 +107,7 @@ void vlan_dev_get_realdev_name(const struct net_device *dev, char *result); int vlan_check_real_dev(struct net_device *real_dev, __be16 protocol, u16 vlan_id); void vlan_setup(struct net_device *dev); -int register_vlan_dev(struct net_device *dev); +int register_vlan_dev(struct net_device *dev, struct netlink_ext_ack *extack); void unregister_vlan_dev(struct net_device *dev, struct list_head *head); bool vlan_dev_inherit_address(struct net_device *dev, struct net_device *real_dev); diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c index 5e831de3103e..6e7c5a6a7930 100644 --- a/net/8021q/vlan_netlink.c +++ b/net/8021q/vlan_netlink.c @@ -160,7 +160,7 @@ static int vlan_newlink(struct net *src_net, struct net_device *dev, if (err < 0) return err; - return register_vlan_dev(dev); + return register_vlan_dev(dev, extack); } static inline size_t vlan_qos_map_size(unsigned int n) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index e348f76ea8c1..f7b413b9297e 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -738,7 +738,7 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, bat_priv = netdev_priv(hard_iface->soft_iface); ret = netdev_master_upper_dev_link(hard_iface->net_dev, - soft_iface, NULL, NULL); + soft_iface, NULL, NULL, NULL); if (ret) goto err_dev; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index f3aef22931ab..0a3fd727048d 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -540,7 +540,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) dev->priv_flags |= IFF_BRIDGE_PORT; - err = netdev_master_upper_dev_link(dev, br->dev, NULL, NULL); + err = netdev_master_upper_dev_link(dev, br->dev, NULL, NULL, NULL); if (err) goto err5; diff --git a/net/core/dev.c b/net/core/dev.c index e27a6bc0ac4d..fcddccb6be41 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -6277,11 +6277,13 @@ static void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev, static int __netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, bool master, - void *upper_priv, void *upper_info) + void *upper_priv, void *upper_info, + struct netlink_ext_ack *extack) { struct netdev_notifier_changeupper_info changeupper_info = { .info = { .dev = dev, + .extack = extack, }, .upper_dev = upper_dev, .master = master, @@ -6341,9 +6343,11 @@ rollback: * returns zero. */ int netdev_upper_dev_link(struct net_device *dev, - struct net_device *upper_dev) + struct net_device *upper_dev, + struct netlink_ext_ack *extack) { - return __netdev_upper_dev_link(dev, upper_dev, false, NULL, NULL); + return __netdev_upper_dev_link(dev, upper_dev, false, + NULL, NULL, extack); } EXPORT_SYMBOL(netdev_upper_dev_link); @@ -6362,10 +6366,11 @@ EXPORT_SYMBOL(netdev_upper_dev_link); */ int netdev_master_upper_dev_link(struct net_device *dev, struct net_device *upper_dev, - void *upper_priv, void *upper_info) + void *upper_priv, void *upper_info, + struct netlink_ext_ack *extack) { return __netdev_upper_dev_link(dev, upper_dev, true, - upper_priv, upper_info); + upper_priv, upper_info, extack); } EXPORT_SYMBOL(netdev_master_upper_dev_link); diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 0389398fa4ab..2e5e7a41d8ef 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -108,7 +108,8 @@ struct vport *ovs_netdev_link(struct vport *vport, const char *name) rtnl_lock(); err = netdev_master_upper_dev_link(vport->dev, - get_dpdev(vport->dp), NULL, NULL); + get_dpdev(vport->dp), + NULL, NULL, NULL); if (err) goto error_unlock; -- cgit v1.2.3-59-g8ed1b From 29cc309d8bf19a36c5196bf626662319af6e3c0b Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 22 Aug 2017 09:10:11 +0800 Subject: HID: hid-multitouch: forward MSC_TIMESTAMP Computes and forwards the device timestamp according to the specification. Many devices use a 16-bit timestamp field, with a resolution of 100us, therefore rolling around very frequently (every 6.5 seconds). To make sure there is no ambiguity, the timestamp reported to the input stack reset to 0 whenever the time between 2 received events is greater than MAX_TIMESTAMP_INTERVAL (1 second). Signed-off-by: Nicolas Boichat Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/hid-multitouch.c | 42 ++++++++++++++++++++++++++++++++++++++++++ include/linux/hid.h | 1 + 2 files changed, 43 insertions(+) (limited to 'include') diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 440b999304a5..996bdc9bf0e5 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -136,6 +137,9 @@ struct mt_device { bool serial_maybe; /* need to check for serial protocol */ bool curvalid; /* is the current contact valid? */ unsigned mt_flags; /* flags to pass to input-mt */ + __s32 dev_time; /* the scan time provided by the device */ + unsigned long jiffies; /* the frame's jiffies */ + int timestamp; /* the timestamp to be sent */ }; static void mt_post_parse_default_settings(struct mt_device *td); @@ -177,6 +181,12 @@ static void mt_post_parse(struct mt_device *td); #define MT_DEFAULT_MAXCONTACT 10 #define MT_MAX_MAXCONTACT 250 +/* + * Resync device and local timestamps after that many microseconds without + * receiving data. + */ +#define MAX_TIMESTAMP_INTERVAL 1000000 + #define MT_USB_DEVICE(v, p) HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH, v, p) #define MT_BT_DEVICE(v, p) HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_MULTITOUCH, v, p) @@ -583,6 +593,12 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, cls->sn_pressure); mt_store_field(usage, td, hi); return 1; + case HID_DG_SCANTIME: + hid_map_usage(hi, usage, bit, max, + EV_MSC, MSC_TIMESTAMP); + input_set_capability(hi->input, EV_MSC, MSC_TIMESTAMP); + mt_store_field(usage, td, hi); + return 1; case HID_DG_CONTACTCOUNT: /* Ignore if indexes are out of bounds. */ if (field->index >= field->report->maxfield || @@ -718,6 +734,7 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input) static void mt_sync_frame(struct mt_device *td, struct input_dev *input) { input_mt_sync_frame(input); + input_event(input, EV_MSC, MSC_TIMESTAMP, td->timestamp); input_sync(input); td->num_received = 0; if (test_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags)) @@ -727,6 +744,28 @@ static void mt_sync_frame(struct mt_device *td, struct input_dev *input) clear_bit(MT_IO_FLAGS_ACTIVE_SLOTS, &td->mt_io_flags); } +static int mt_compute_timestamp(struct mt_device *td, struct hid_field *field, + __s32 value) +{ + long delta = value - td->dev_time; + unsigned long jdelta = jiffies_to_usecs(jiffies - td->jiffies); + + td->jiffies = jiffies; + td->dev_time = value; + + if (delta < 0) + delta += field->logical_maximum; + + /* HID_DG_SCANTIME is expressed in 100us, we want it in us. */ + delta *= 100; + + if (jdelta > MAX_TIMESTAMP_INTERVAL) + /* No data received for a while, resync the timestamp. */ + return 0; + else + return td->timestamp + delta; +} + static int mt_touch_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value) { @@ -787,6 +826,9 @@ static void mt_process_mt_event(struct hid_device *hid, struct hid_field *field, case HID_DG_HEIGHT: td->curdata.h = value; break; + case HID_DG_SCANTIME: + td->timestamp = mt_compute_timestamp(td, field, value); + break; case HID_DG_CONTACTCOUNT: break; case HID_DG_TOUCH: diff --git a/include/linux/hid.h b/include/linux/hid.h index ab05a86269dc..47dd962d9a7a 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -289,6 +289,7 @@ struct hid_item { #define HID_DG_DEVICEINDEX 0x000d0053 #define HID_DG_CONTACTCOUNT 0x000d0054 #define HID_DG_CONTACTMAX 0x000d0055 +#define HID_DG_SCANTIME 0x000d0056 #define HID_DG_BUTTONTYPE 0x000d0059 #define HID_DG_BARRELSWITCH2 0x000d005a #define HID_DG_TOOLSERIALNUMBER 0x000d005b -- cgit v1.2.3-59-g8ed1b From a060c2104ef83e62346b7e893947a940471c0d7c Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Tue, 26 Sep 2017 12:22:54 +0200 Subject: of/pci: Add of_pci_dma_range_parser_init() for dma-ranges parsing support Several host bridge drivers duplicate of_pci_range_parser_init() in order to parse their dma-ranges property. Provide of_pci_dma_range_parser_init() for that use case. Signed-off-by: Marc Gonzalez Signed-off-by: Bjorn Helgaas Reviewed-by: Rob Herring Reviewed-by: Linus Walleij --- drivers/of/address.c | 19 ++++++++++++++++--- include/linux/of_address.h | 10 +++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/of/address.c b/drivers/of/address.c index 792722e7d458..fa6cabfc3cb9 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -232,8 +232,8 @@ int of_pci_address_to_resource(struct device_node *dev, int bar, } EXPORT_SYMBOL_GPL(of_pci_address_to_resource); -int of_pci_range_parser_init(struct of_pci_range_parser *parser, - struct device_node *node) +static int parser_init(struct of_pci_range_parser *parser, + struct device_node *node, const char *name) { const int na = 3, ns = 2; int rlen; @@ -242,7 +242,7 @@ int of_pci_range_parser_init(struct of_pci_range_parser *parser, parser->pna = of_n_addr_cells(node); parser->np = parser->pna + na + ns; - parser->range = of_get_property(node, "ranges", &rlen); + parser->range = of_get_property(node, name, &rlen); if (parser->range == NULL) return -ENOENT; @@ -250,8 +250,21 @@ int of_pci_range_parser_init(struct of_pci_range_parser *parser, return 0; } + +int of_pci_range_parser_init(struct of_pci_range_parser *parser, + struct device_node *node) +{ + return parser_init(parser, node, "ranges"); +} EXPORT_SYMBOL_GPL(of_pci_range_parser_init); +int of_pci_dma_range_parser_init(struct of_pci_range_parser *parser, + struct device_node *node) +{ + return parser_init(parser, node, "dma-ranges"); +} +EXPORT_SYMBOL_GPL(of_pci_dma_range_parser_init); + struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, struct of_pci_range *range) { diff --git a/include/linux/of_address.h b/include/linux/of_address.h index 37864734ca50..8beed2de98e9 100644 --- a/include/linux/of_address.h +++ b/include/linux/of_address.h @@ -49,6 +49,8 @@ extern const __be32 *of_get_address(struct device_node *dev, int index, extern int of_pci_range_parser_init(struct of_pci_range_parser *parser, struct device_node *node); +extern int of_pci_dma_range_parser_init(struct of_pci_range_parser *parser, + struct device_node *node); extern struct of_pci_range *of_pci_range_parser_one( struct of_pci_range_parser *parser, struct of_pci_range *range); @@ -85,7 +87,13 @@ static inline const __be32 *of_get_address(struct device_node *dev, int index, static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser, struct device_node *node) { - return -1; + return -ENOSYS; +} + +static inline int of_pci_dma_range_parser_init(struct of_pci_range_parser *parser, + struct device_node *node) +{ + return -ENOSYS; } static inline struct of_pci_range *of_pci_range_parser_one( -- cgit v1.2.3-59-g8ed1b From 753f612471819d3b3abba8c520eb3ce8f9d00fa5 Mon Sep 17 00:00:00 2001 From: "Jan H. Schönherr" Date: Tue, 26 Sep 2017 12:53:23 -0500 Subject: PCI: Remove reset argument from pci_iov_{add,remove}_virtfn() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "reset" argument passed to pci_iov_add_virtfn() and pci_iov_remove_virtfn() is always zero since 46cb7b1bd86f ("PCI: Remove unused SR-IOV VF Migration support") Remove the argument together with the associated code. Signed-off-by: Jan H. Schönherr Signed-off-by: Bjorn Helgaas Acked-by: Russell Currey --- arch/powerpc/kernel/eeh_driver.c | 4 ++-- drivers/pci/iov.c | 18 +++++------------- include/linux/pci.h | 8 ++++---- 3 files changed, 11 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 8b840191df59..4e1b433f6cb5 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -441,7 +441,7 @@ static void *eeh_add_virt_device(void *data, void *userdata) } #ifdef CONFIG_PPC_POWERNV - pci_iov_add_virtfn(edev->physfn, pdn->vf_index, 0); + pci_iov_add_virtfn(edev->physfn, pdn->vf_index); #endif return NULL; } @@ -499,7 +499,7 @@ static void *eeh_rmv_device(void *data, void *userdata) #ifdef CONFIG_PPC_POWERNV struct pci_dn *pdn = eeh_dev_to_pdn(edev); - pci_iov_remove_virtfn(edev->physfn, pdn->vf_index, 0); + pci_iov_remove_virtfn(edev->physfn, pdn->vf_index); edev->pdev = NULL; /* diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index ac41c8be9200..996bf3b3cb70 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -113,7 +113,7 @@ resource_size_t pci_iov_resource_size(struct pci_dev *dev, int resno) return dev->sriov->barsz[resno - PCI_IOV_RESOURCES]; } -int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset) +int pci_iov_add_virtfn(struct pci_dev *dev, int id) { int i; int rc = -ENOMEM; @@ -157,9 +157,6 @@ int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset) BUG_ON(rc); } - if (reset) - __pci_reset_function(virtfn); - pci_device_add(virtfn, virtfn->bus); pci_bus_add_device(virtfn); @@ -187,7 +184,7 @@ failed: return rc; } -void pci_iov_remove_virtfn(struct pci_dev *dev, int id, int reset) +void pci_iov_remove_virtfn(struct pci_dev *dev, int id) { char buf[VIRTFN_ID_LEN]; struct pci_dev *virtfn; @@ -198,11 +195,6 @@ void pci_iov_remove_virtfn(struct pci_dev *dev, int id, int reset) if (!virtfn) return; - if (reset) { - device_release_driver(&virtfn->dev); - __pci_reset_function(virtfn); - } - sprintf(buf, "virtfn%u", id); sysfs_remove_link(&dev->dev.kobj, buf); /* @@ -317,7 +309,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) pci_cfg_access_unlock(dev); for (i = 0; i < initial; i++) { - rc = pci_iov_add_virtfn(dev, i, 0); + rc = pci_iov_add_virtfn(dev, i); if (rc) goto failed; } @@ -329,7 +321,7 @@ static int sriov_enable(struct pci_dev *dev, int nr_virtfn) failed: while (i--) - pci_iov_remove_virtfn(dev, i, 0); + pci_iov_remove_virtfn(dev, i); err_pcibios: iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); @@ -356,7 +348,7 @@ static void sriov_disable(struct pci_dev *dev) return; for (i = 0; i < iov->num_VFs; i++) - pci_iov_remove_virtfn(dev, i, 0); + pci_iov_remove_virtfn(dev, i); iov->ctrl &= ~(PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE); pci_cfg_access_lock(dev); diff --git a/include/linux/pci.h b/include/linux/pci.h index f4f8ee5a7362..75ac2af89e87 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1958,8 +1958,8 @@ int pci_iov_virtfn_devfn(struct pci_dev *dev, int id); int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn); void pci_disable_sriov(struct pci_dev *dev); -int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset); -void pci_iov_remove_virtfn(struct pci_dev *dev, int id, int reset); +int pci_iov_add_virtfn(struct pci_dev *dev, int id); +void pci_iov_remove_virtfn(struct pci_dev *dev, int id); int pci_num_vf(struct pci_dev *dev); int pci_vfs_assigned(struct pci_dev *dev); int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs); @@ -1976,12 +1976,12 @@ static inline int pci_iov_virtfn_devfn(struct pci_dev *dev, int id) } static inline int pci_enable_sriov(struct pci_dev *dev, int nr_virtfn) { return -ENODEV; } -static inline int pci_iov_add_virtfn(struct pci_dev *dev, int id, int reset) +static inline int pci_iov_add_virtfn(struct pci_dev *dev, int id) { return -ENOSYS; } static inline void pci_iov_remove_virtfn(struct pci_dev *dev, - int id, int reset) { } + int id) { } static inline void pci_disable_sriov(struct pci_dev *dev) { } static inline int pci_num_vf(struct pci_dev *dev) { return 0; } static inline int pci_vfs_assigned(struct pci_dev *dev) -- cgit v1.2.3-59-g8ed1b From 79e699b648b93f76d0f6e692499d5c6a2295ef05 Mon Sep 17 00:00:00 2001 From: "Jan H. Schönherr" Date: Wed, 6 Sep 2017 01:21:23 +0200 Subject: PCI: Remove unused function __pci_reset_function() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last caller of __pci_reset_function() has been removed. Remove the function as well. Signed-off-by: Jan H. Schönherr Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 35 +++-------------------------------- include/linux/pci.h | 1 - 2 files changed, 3 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6078dfc11b11..f0d68066c726 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4145,35 +4145,6 @@ static void pci_dev_restore(struct pci_dev *dev) err_handler->reset_done(dev); } -/** - * __pci_reset_function - reset a PCI device function - * @dev: PCI device to reset - * - * Some devices allow an individual function to be reset without affecting - * other functions in the same device. The PCI device must be responsive - * to PCI config space in order to use this function. - * - * The device function is presumed to be unused when this function is called. - * Resetting the device will make the contents of PCI configuration space - * random, so any caller of this must be prepared to reinitialise the - * device including MSI, bus mastering, BARs, decoding IO and memory spaces, - * etc. - * - * Returns 0 if the device function was successfully reset or negative if the - * device doesn't support resetting a single function. - */ -int __pci_reset_function(struct pci_dev *dev) -{ - int ret; - - pci_dev_lock(dev); - ret = __pci_reset_function_locked(dev); - pci_dev_unlock(dev); - - return ret; -} -EXPORT_SYMBOL_GPL(__pci_reset_function); - /** * __pci_reset_function_locked - reset a PCI device function while holding * the @dev mutex lock. @@ -4264,8 +4235,8 @@ int pci_probe_reset_function(struct pci_dev *dev) * * This function does not just reset the PCI portion of a device, but * clears all the state associated with the device. This function differs - * from __pci_reset_function in that it saves and restores device state - * over the reset. + * from __pci_reset_function_locked() in that it saves and restores device state + * over the reset and takes the PCI device lock. * * Returns 0 if the device function was successfully reset or negative if the * device doesn't support resetting a single function. @@ -4300,7 +4271,7 @@ EXPORT_SYMBOL_GPL(pci_reset_function); * * This function does not just reset the PCI portion of a device, but * clears all the state associated with the device. This function differs - * from __pci_reset_function() in that it saves and restores device state + * from __pci_reset_function_locked() in that it saves and restores device state * over the reset. It also differs from pci_reset_function() in that it * requires the PCI device lock to be held. * diff --git a/include/linux/pci.h b/include/linux/pci.h index 75ac2af89e87..fb29d964a057 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1089,7 +1089,6 @@ int pcie_set_mps(struct pci_dev *dev, int mps); int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, enum pcie_link_width *width); void pcie_flr(struct pci_dev *dev); -int __pci_reset_function(struct pci_dev *dev); int __pci_reset_function_locked(struct pci_dev *dev); int pci_reset_function(struct pci_dev *dev); int pci_reset_function_locked(struct pci_dev *dev); -- cgit v1.2.3-59-g8ed1b From e891a5a40103bc887befb21bbee83308e3669309 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Wed, 20 Sep 2017 15:39:26 +0200 Subject: pinctrl: meson-gx: TEST_N belongs to the AO controller On meson-gx platforms, TEST_N has been incorrectly declared in the EE controller while it belongs to AO controller. Move the pin to the appropriate controller Signed-off-by: Jerome Brunet Signed-off-by: Linus Walleij --- drivers/pinctrl/meson/pinctrl-meson-gxbb.c | 8 ++++---- drivers/pinctrl/meson/pinctrl-meson-gxl.c | 8 ++++---- include/dt-bindings/gpio/meson-gxbb-gpio.h | 2 +- include/dt-bindings/gpio/meson-gxl-gpio.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c index 8e0d6e4a31b4..1305d68b0954 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-gxbb.c +++ b/drivers/pinctrl/meson/pinctrl-meson-gxbb.c @@ -141,8 +141,6 @@ static const struct pinctrl_pin_desc meson_gxbb_periphs_pins[] = { MESON_PIN(GPIOCLK_1), MESON_PIN(GPIOCLK_2), MESON_PIN(GPIOCLK_3), - - MESON_PIN(GPIO_TEST_N), }; static const unsigned int emmc_nand_d07_pins[] = { @@ -258,6 +256,8 @@ static const struct pinctrl_pin_desc meson_gxbb_aobus_pins[] = { MESON_PIN(GPIOAO_11), MESON_PIN(GPIOAO_12), MESON_PIN(GPIOAO_13), + + MESON_PIN(GPIO_TEST_N), }; static const unsigned int uart_tx_ao_a_pins[] = { GPIOAO_0 }; @@ -596,8 +596,6 @@ static const char * const gpio_periphs_groups[] = { "GPIOX_10", "GPIOX_11", "GPIOX_12", "GPIOX_13", "GPIOX_14", "GPIOX_15", "GPIOX_16", "GPIOX_17", "GPIOX_18", "GPIOX_19", "GPIOX_20", "GPIOX_21", "GPIOX_22", - - "GPIO_TEST_N", }; static const char * const emmc_groups[] = { @@ -706,6 +704,8 @@ static const char * const gpio_aobus_groups[] = { "GPIOAO_0", "GPIOAO_1", "GPIOAO_2", "GPIOAO_3", "GPIOAO_4", "GPIOAO_5", "GPIOAO_6", "GPIOAO_7", "GPIOAO_8", "GPIOAO_9", "GPIOAO_10", "GPIOAO_11", "GPIOAO_12", "GPIOAO_13", + + "GPIO_TEST_N", }; static const char * const uart_ao_groups[] = { diff --git a/drivers/pinctrl/meson/pinctrl-meson-gxl.c b/drivers/pinctrl/meson/pinctrl-meson-gxl.c index 0d90ddab6ddd..04334e807170 100644 --- a/drivers/pinctrl/meson/pinctrl-meson-gxl.c +++ b/drivers/pinctrl/meson/pinctrl-meson-gxl.c @@ -122,8 +122,6 @@ static const struct pinctrl_pin_desc meson_gxl_periphs_pins[] = { MESON_PIN(GPIOCLK_0), MESON_PIN(GPIOCLK_1), - - MESON_PIN(GPIO_TEST_N), }; static const unsigned int emmc_nand_d07_pins[] = { @@ -263,6 +261,8 @@ static const struct pinctrl_pin_desc meson_gxl_aobus_pins[] = { MESON_PIN(GPIOAO_7), MESON_PIN(GPIOAO_8), MESON_PIN(GPIOAO_9), + + MESON_PIN(GPIO_TEST_N), }; static const unsigned int uart_tx_ao_a_pins[] = { GPIOAO_0 }; @@ -587,8 +587,6 @@ static const char * const gpio_periphs_groups[] = { "GPIOX_5", "GPIOX_6", "GPIOX_7", "GPIOX_8", "GPIOX_9", "GPIOX_10", "GPIOX_11", "GPIOX_12", "GPIOX_13", "GPIOX_14", "GPIOX_15", "GPIOX_16", "GPIOX_17", "GPIOX_18", - - "GPIO_TEST_N", }; static const char * const emmc_groups[] = { @@ -703,6 +701,8 @@ static const char * const tsin_a_groups[] = { static const char * const gpio_aobus_groups[] = { "GPIOAO_0", "GPIOAO_1", "GPIOAO_2", "GPIOAO_3", "GPIOAO_4", "GPIOAO_5", "GPIOAO_6", "GPIOAO_7", "GPIOAO_8", "GPIOAO_9", + + "GPIO_TEST_N", }; static const char * const uart_ao_groups[] = { diff --git a/include/dt-bindings/gpio/meson-gxbb-gpio.h b/include/dt-bindings/gpio/meson-gxbb-gpio.h index 58654fd7aa1e..43a68a1110f0 100644 --- a/include/dt-bindings/gpio/meson-gxbb-gpio.h +++ b/include/dt-bindings/gpio/meson-gxbb-gpio.h @@ -29,6 +29,7 @@ #define GPIOAO_11 11 #define GPIOAO_12 12 #define GPIOAO_13 13 +#define GPIO_TEST_N 14 #define GPIOZ_0 0 #define GPIOZ_1 1 @@ -149,6 +150,5 @@ #define GPIOCLK_1 116 #define GPIOCLK_2 117 #define GPIOCLK_3 118 -#define GPIO_TEST_N 119 #endif diff --git a/include/dt-bindings/gpio/meson-gxl-gpio.h b/include/dt-bindings/gpio/meson-gxl-gpio.h index 684d0d7add1c..01f2a2abd35e 100644 --- a/include/dt-bindings/gpio/meson-gxl-gpio.h +++ b/include/dt-bindings/gpio/meson-gxl-gpio.h @@ -25,6 +25,7 @@ #define GPIOAO_7 7 #define GPIOAO_8 8 #define GPIOAO_9 9 +#define GPIO_TEST_N 10 #define GPIOZ_0 0 #define GPIOZ_1 1 @@ -126,6 +127,5 @@ #define GPIOX_18 97 #define GPIOCLK_0 98 #define GPIOCLK_1 99 -#define GPIO_TEST_N 100 #endif -- cgit v1.2.3-59-g8ed1b From 5fdee2127faa77c9c91862ad5e001dfab7013e92 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 5 Oct 2017 21:22:52 +0200 Subject: block: remove QUEUE_FLAG_STACKABLE We already have a queue_is_rq_based helper to check if a request_queue is request based, so we can remove the flag for it. Acked-by: Mike Snitzer Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-mq-debugfs.c | 1 - block/elevator.c | 2 +- drivers/md/dm-rq.c | 2 +- drivers/md/dm-table.c | 15 +-------------- drivers/md/dm.c | 11 ----------- include/linux/blkdev.h | 5 ----- 6 files changed, 3 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/block/blk-mq-debugfs.c b/block/blk-mq-debugfs.c index 980e73095643..7f4a1ba532af 100644 --- a/block/blk-mq-debugfs.c +++ b/block/blk-mq-debugfs.c @@ -54,7 +54,6 @@ static const char *const blk_queue_flag_name[] = { QUEUE_FLAG_NAME(NOMERGES), QUEUE_FLAG_NAME(SAME_COMP), QUEUE_FLAG_NAME(FAIL_IO), - QUEUE_FLAG_NAME(STACKABLE), QUEUE_FLAG_NAME(NONROT), QUEUE_FLAG_NAME(IO_STAT), QUEUE_FLAG_NAME(DISCARD), diff --git a/block/elevator.c b/block/elevator.c index 153926a90901..7ae50eb2732b 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -1118,7 +1118,7 @@ ssize_t elv_iosched_show(struct request_queue *q, char *name) struct elevator_type *__e; int len = 0; - if (!blk_queue_stackable(q)) + if (!queue_is_rq_based(q)) return sprintf(name, "none\n"); if (!q->elevator) diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c index eadfcfd106ff..9d32f25489c2 100644 --- a/drivers/md/dm-rq.c +++ b/drivers/md/dm-rq.c @@ -56,7 +56,7 @@ static unsigned dm_get_blk_mq_queue_depth(void) int dm_request_based(struct mapped_device *md) { - return blk_queue_stackable(md->queue); + return queue_is_rq_based(md->queue); } static void dm_old_start_queue(struct request_queue *q) diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index ef7b8f201f73..75281828f2cb 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -1000,7 +1000,7 @@ verify_rq_based: list_for_each_entry(dd, devices, list) { struct request_queue *q = bdev_get_queue(dd->dm_dev->bdev); - if (!blk_queue_stackable(q)) { + if (!queue_is_rq_based(q)) { DMERR("table load rejected: including" " non-request-stackable devices"); return -EINVAL; @@ -1847,19 +1847,6 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, */ if (blk_queue_add_random(q) && dm_table_all_devices_attribute(t, device_is_not_random)) queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q); - - /* - * QUEUE_FLAG_STACKABLE must be set after all queue settings are - * visible to other CPUs because, once the flag is set, incoming bios - * are processed by request-based dm, which refers to the queue - * settings. - * Until the flag set, bios are passed to bio-based dm and queued to - * md->deferred where queue settings are not needed yet. - * Those bios are passed to request-based dm at the resume time. - */ - smp_mb(); - if (dm_table_request_based(t)) - queue_flag_set_unlocked(QUEUE_FLAG_STACKABLE, q); } unsigned int dm_table_get_num_targets(struct dm_table *t) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 6e54145969c5..8d07ad61221c 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1612,17 +1612,6 @@ static void dm_wq_work(struct work_struct *work); void dm_init_md_queue(struct mapped_device *md) { - /* - * Request-based dm devices cannot be stacked on top of bio-based dm - * devices. The type of this dm device may not have been decided yet. - * The type is decided at the first table loading time. - * To prevent problematic device stacking, clear the queue flag - * for request stacking support until then. - * - * This queue is new, so no concurrency on the queue_flags. - */ - queue_flag_clear_unlocked(QUEUE_FLAG_STACKABLE, md->queue); - /* * Initialize data that will only be used by a non-blk-mq DM queue * - must do so here (in alloc_dev callchain) before queue is used diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 02fa42d24b52..9fb71fc7d0e8 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -609,7 +609,6 @@ struct request_queue { #define QUEUE_FLAG_NOMERGES 5 /* disable merge attempts */ #define QUEUE_FLAG_SAME_COMP 6 /* complete on same CPU-group */ #define QUEUE_FLAG_FAIL_IO 7 /* fake timeout */ -#define QUEUE_FLAG_STACKABLE 8 /* supports request stacking */ #define QUEUE_FLAG_NONROT 9 /* non-rotational device (SSD) */ #define QUEUE_FLAG_VIRT QUEUE_FLAG_NONROT /* paravirt device */ #define QUEUE_FLAG_IO_STAT 10 /* do IO stats */ @@ -633,12 +632,10 @@ struct request_queue { #define QUEUE_FLAG_QUIESCED 28 /* queue has been quiesced */ #define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \ - (1 << QUEUE_FLAG_STACKABLE) | \ (1 << QUEUE_FLAG_SAME_COMP) | \ (1 << QUEUE_FLAG_ADD_RANDOM)) #define QUEUE_FLAG_MQ_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \ - (1 << QUEUE_FLAG_STACKABLE) | \ (1 << QUEUE_FLAG_SAME_COMP) | \ (1 << QUEUE_FLAG_POLL)) @@ -722,8 +719,6 @@ static inline void queue_flag_clear(unsigned int flag, struct request_queue *q) #define blk_queue_nonrot(q) test_bit(QUEUE_FLAG_NONROT, &(q)->queue_flags) #define blk_queue_io_stat(q) test_bit(QUEUE_FLAG_IO_STAT, &(q)->queue_flags) #define blk_queue_add_random(q) test_bit(QUEUE_FLAG_ADD_RANDOM, &(q)->queue_flags) -#define blk_queue_stackable(q) \ - test_bit(QUEUE_FLAG_STACKABLE, &(q)->queue_flags) #define blk_queue_discard(q) test_bit(QUEUE_FLAG_DISCARD, &(q)->queue_flags) #define blk_queue_secure_erase(q) \ (test_bit(QUEUE_FLAG_SECERASE, &(q)->queue_flags)) -- cgit v1.2.3-59-g8ed1b From 3e234289f86b12985ef8909cd34525fcb66c4efb Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 3 Mar 2017 18:00:22 -0500 Subject: ftrace: Allow module init functions to be traced Allow for module init sections to be traced as well as core kernel init sections. Now that filtering modules functions can be stored, for when they are loaded, it makes sense to be able to trace them. Cc: Jessica Yu Cc: Rusty Russell Signed-off-by: Steven Rostedt (VMware) --- include/linux/init.h | 4 +--- kernel/module.c | 2 ++ kernel/trace/ftrace.c | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/init.h b/include/linux/init.h index 94769d687cf0..a779c1816437 100644 --- a/include/linux/init.h +++ b/include/linux/init.h @@ -39,7 +39,7 @@ /* These are for everybody (although not all archs will actually discard it in modules) */ -#define __init __section(.init.text) __cold __inittrace __latent_entropy +#define __init __section(.init.text) __cold __latent_entropy #define __initdata __section(.init.data) #define __initconst __section(.init.rodata) #define __exitdata __section(.exit.data) @@ -68,10 +68,8 @@ #ifdef MODULE #define __exitused -#define __inittrace notrace #else #define __exitused __used -#define __inittrace #endif #define __exit __section(.exit.text) __exitused __cold notrace diff --git a/kernel/module.c b/kernel/module.c index de66ec825992..58bca427ac3f 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3473,6 +3473,8 @@ static noinline int do_init_module(struct module *mod) if (!mod->async_probe_requested && (current->flags & PF_USED_ASYNC)) async_synchronize_full(); + ftrace_free_mem(mod->init_layout.base, mod->init_layout.base + + mod->init_layout.size); mutex_lock(&module_mutex); /* Drop initial reference. */ module_put(mod); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 84cb5928665a..d7297e866e4a 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -5752,7 +5752,8 @@ void ftrace_release_mod(struct module *mod) last_pg = &ftrace_pages_start; for (pg = ftrace_pages_start; pg; pg = *last_pg) { rec = &pg->records[0]; - if (within_module_core(rec->ip, mod)) { + if (within_module_core(rec->ip, mod) || + within_module_init(rec->ip, mod)) { /* * As core pages are first, the first * page should never be a module page. @@ -5821,7 +5822,8 @@ void ftrace_module_enable(struct module *mod) * not part of this module, then skip this pg, * which the "break" will do. */ - if (!within_module_core(rec->ip, mod)) + if (!within_module_core(rec->ip, mod) && + !within_module_init(rec->ip, mod)) break; cnt = 0; -- cgit v1.2.3-59-g8ed1b From aba4b5c22cbac296f4081a0476d0c55828f135b4 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 1 Sep 2017 08:35:38 -0400 Subject: ftrace: Save module init functions kallsyms symbols for tracing If function tracing is active when the module init functions are freed, then store them to be referenced by kallsyms. As module init functions can now be traced on module load, they were useless: ># echo ':mod:snd_seq' > set_ftrace_filter ># echo function > current_tracer ># modprobe snd_seq ># cat trace # tracer: function # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | modprobe-2786 [000] .... 3189.037874: 0xffffffffa0860000 <-do_one_initcall modprobe-2786 [000] .... 3189.037876: 0xffffffffa086004d <-0xffffffffa086000f modprobe-2786 [000] .... 3189.037876: 0xffffffffa086010d <-0xffffffffa0860018 modprobe-2786 [000] .... 3189.037877: 0xffffffffa086011a <-0xffffffffa0860021 modprobe-2786 [000] .... 3189.037877: 0xffffffffa0860080 <-0xffffffffa086002a modprobe-2786 [000] .... 3189.039523: 0xffffffffa0860400 <-0xffffffffa0860033 modprobe-2786 [000] .... 3189.039523: 0xffffffffa086038a <-0xffffffffa086041c modprobe-2786 [000] .... 3189.039591: 0xffffffffa086038a <-0xffffffffa0860436 modprobe-2786 [000] .... 3189.039657: 0xffffffffa086038a <-0xffffffffa0860450 modprobe-2786 [000] .... 3189.039719: 0xffffffffa0860127 <-0xffffffffa086003c modprobe-2786 [000] .... 3189.039742: snd_seq_create_kernel_client <-0xffffffffa08601f6 When the output is shown, the kallsyms for the module init functions have already been freed, and the output of the trace can not convert them to their function names. Now this looks like this: # tracer: function # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | modprobe-2463 [002] .... 174.243237: alsa_seq_init <-do_one_initcall modprobe-2463 [002] .... 174.243239: client_init_data <-alsa_seq_init modprobe-2463 [002] .... 174.243240: snd_sequencer_memory_init <-alsa_seq_init modprobe-2463 [002] .... 174.243240: snd_seq_queues_init <-alsa_seq_init modprobe-2463 [002] .... 174.243240: snd_sequencer_device_init <-alsa_seq_init modprobe-2463 [002] .... 174.244860: snd_seq_info_init <-alsa_seq_init modprobe-2463 [002] .... 174.244861: create_info_entry <-snd_seq_info_init modprobe-2463 [002] .... 174.244936: create_info_entry <-snd_seq_info_init modprobe-2463 [002] .... 174.245003: create_info_entry <-snd_seq_info_init modprobe-2463 [002] .... 174.245072: snd_seq_system_client_init <-alsa_seq_init modprobe-2463 [002] .... 174.245094: snd_seq_create_kernel_client <-snd_seq_system_client_init Signed-off-by: Steven Rostedt (VMware) --- include/linux/ftrace.h | 20 ++++++- kernel/kallsyms.c | 5 ++ kernel/module.c | 2 +- kernel/trace/ftrace.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 168 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 47fc404ad233..202b40784c4e 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -51,6 +51,21 @@ static inline void early_trace_init(void) { } struct module; struct ftrace_hash; +#if defined(CONFIG_FUNCTION_TRACER) && defined(CONFIG_MODULES) && \ + defined(CONFIG_DYNAMIC_FTRACE) +const char * +ftrace_mod_address_lookup(unsigned long addr, unsigned long *size, + unsigned long *off, char **modname, char *sym); +#else +static inline const char * +ftrace_mod_address_lookup(unsigned long addr, unsigned long *size, + unsigned long *off, char **modname, char *sym) +{ + return NULL; +} +#endif + + #ifdef CONFIG_FUNCTION_TRACER extern int ftrace_enabled; @@ -151,10 +166,10 @@ struct ftrace_ops_hash { }; void ftrace_free_init_mem(void); -void ftrace_free_mem(void *start, void *end); +void ftrace_free_mem(struct module *mod, void *start, void *end); #else static inline void ftrace_free_init_mem(void) { } -static inline void ftrace_free_mem(void *start, void *end) { } +static inline void ftrace_free_mem(struct module *mod, void *start, void *end) { } #endif /* @@ -272,6 +287,7 @@ static inline int ftrace_nr_registered_ops(void) static inline void clear_ftrace_function(void) { } static inline void ftrace_kill(void) { } static inline void ftrace_free_init_mem(void) { } +static inline void ftrace_free_mem(struct module *mod, void *start, void *end) { } #endif /* CONFIG_FUNCTION_TRACER */ #ifdef CONFIG_STACK_TRACER diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 127e7cfafa55..976ecb9275d9 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -337,6 +338,10 @@ const char *kallsyms_lookup(unsigned long addr, if (!ret) ret = bpf_address_lookup(addr, symbolsize, offset, modname, namebuf); + + if (!ret) + ret = ftrace_mod_address_lookup(addr, symbolsize, + offset, modname, namebuf); return ret; } diff --git a/kernel/module.c b/kernel/module.c index 58bca427ac3f..279a469dc375 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -3473,7 +3473,7 @@ static noinline int do_init_module(struct module *mod) if (!mod->async_probe_requested && (current->flags & PF_USED_ASYNC)) async_synchronize_full(); - ftrace_free_mem(mod->init_layout.base, mod->init_layout.base + + ftrace_free_mem(mod, mod->init_layout.base, mod->init_layout.base + mod->init_layout.size); mutex_lock(&module_mutex); /* Drop initial reference. */ diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index d7297e866e4a..86dbbfb353db 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -5675,6 +5675,21 @@ static int ftrace_process_locs(struct module *mod, return ret; } +struct ftrace_mod_func { + struct list_head list; + char *name; + unsigned long ip; + unsigned int size; +}; + +struct ftrace_mod_map { + struct list_head list; + struct module *mod; + unsigned long start_addr; + unsigned long end_addr; + struct list_head funcs; +}; + #ifdef CONFIG_MODULES #define next_to_ftrace_page(p) container_of(p, struct ftrace_page, next) @@ -5868,9 +5883,123 @@ void ftrace_module_init(struct module *mod) ftrace_process_locs(mod, mod->ftrace_callsites, mod->ftrace_callsites + mod->num_ftrace_callsites); } + +static void save_ftrace_mod_rec(struct ftrace_mod_map *mod_map, + struct dyn_ftrace *rec) +{ + struct ftrace_mod_func *mod_func; + unsigned long symsize; + unsigned long offset; + char str[KSYM_SYMBOL_LEN]; + char *modname; + const char *ret; + + ret = kallsyms_lookup(rec->ip, &symsize, &offset, &modname, str); + if (!ret) + return; + + mod_func = kmalloc(sizeof(*mod_func), GFP_KERNEL); + if (!mod_func) + return; + + mod_func->name = kstrdup(str, GFP_KERNEL); + if (!mod_func->name) { + kfree(mod_func); + return; + } + + mod_func->ip = rec->ip - offset; + mod_func->size = symsize; + + list_add_rcu(&mod_func->list, &mod_map->funcs); +} + +static LIST_HEAD(ftrace_mod_maps); + +static struct ftrace_mod_map * +allocate_ftrace_mod_map(struct module *mod, + unsigned long start, unsigned long end) +{ + struct ftrace_mod_map *mod_map; + + mod_map = kmalloc(sizeof(*mod_map), GFP_KERNEL); + if (!mod_map) + return NULL; + + mod_map->mod = mod; + mod_map->start_addr = start; + mod_map->end_addr = end; + + INIT_LIST_HEAD_RCU(&mod_map->funcs); + + list_add_rcu(&mod_map->list, &ftrace_mod_maps); + + return mod_map; +} + +static const char * +ftrace_func_address_lookup(struct ftrace_mod_map *mod_map, + unsigned long addr, unsigned long *size, + unsigned long *off, char *sym) +{ + struct ftrace_mod_func *found_func = NULL; + struct ftrace_mod_func *mod_func; + + list_for_each_entry_rcu(mod_func, &mod_map->funcs, list) { + if (addr >= mod_func->ip && + addr < mod_func->ip + mod_func->size) { + found_func = mod_func; + break; + } + } + + if (found_func) { + if (size) + *size = found_func->size; + if (off) + *off = addr - found_func->ip; + if (sym) + strlcpy(sym, found_func->name, KSYM_NAME_LEN); + + return found_func->name; + } + + return NULL; +} + +const char * +ftrace_mod_address_lookup(unsigned long addr, unsigned long *size, + unsigned long *off, char **modname, char *sym) +{ + struct ftrace_mod_map *mod_map; + const char *ret = NULL; + + preempt_disable(); + list_for_each_entry_rcu(mod_map, &ftrace_mod_maps, list) { + ret = ftrace_func_address_lookup(mod_map, addr, size, off, sym); + if (ret) { + if (modname) + *modname = mod_map->mod->name; + break; + } + } + preempt_enable(); + + return ret; +} + +#else +static void save_ftrace_mod_rec(struct ftrace_mod_map *mod_map, + struct dyn_ftrace *rec) { } +static inline struct ftrace_mod_map * +allocate_ftrace_mod_map(struct module *mod, + unsigned long start, unsigned long end) +{ + return NULL; +} #endif /* CONFIG_MODULES */ -void ftrace_free_mem(void *start_ptr, void *end_ptr) +void ftrace_free_mem(struct module *mod, void *start_ptr, void *end_ptr) { unsigned long start = (unsigned long)(start_ptr); unsigned long end = (unsigned long)(end_ptr); @@ -5878,6 +6007,7 @@ void ftrace_free_mem(void *start_ptr, void *end_ptr) struct ftrace_page *pg; struct dyn_ftrace *rec; struct dyn_ftrace key; + struct ftrace_mod_map *mod_map = NULL; int order; key.ip = start; @@ -5885,6 +6015,14 @@ void ftrace_free_mem(void *start_ptr, void *end_ptr) mutex_lock(&ftrace_lock); + /* + * If we are freeing module init memory, then check if + * any tracer is active. If so, we need to save a mapping of + * the module functions being freed with the address. + */ + if (mod && ftrace_ops_list != &ftrace_list_end) + mod_map = allocate_ftrace_mod_map(mod, start, end); + for (pg = ftrace_pages_start; pg; last_pg = &pg->next, pg = *last_pg) { if (end < pg->records[0].ip || start >= (pg->records[pg->index - 1].ip + MCOUNT_INSN_SIZE)) @@ -5895,6 +6033,10 @@ void ftrace_free_mem(void *start_ptr, void *end_ptr) ftrace_cmp_recs); if (!rec) continue; + + if (mod_map) + save_ftrace_mod_rec(mod_map, rec); + pg->index--; ftrace_update_tot_cnt--; if (!pg->index) { @@ -5920,7 +6062,7 @@ void __init ftrace_free_init_mem(void) void *start = (void *)(&__init_begin); void *end = (void *)(&__init_end); - ftrace_free_mem(start, end); + ftrace_free_mem(NULL, start, end); } void __init ftrace_init(void) -- cgit v1.2.3-59-g8ed1b From 44f209807ee87a5eddf6c0432f3fb63cec27bad8 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 5 Oct 2017 16:46:50 -0400 Subject: VSOCK: export socket tables for sock_diag interface The socket table symbols need to be exported from vsock.ko so that the vsock_diag.ko module will be able to traverse sockets. Signed-off-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- include/net/af_vsock.h | 5 +++++ net/vmw_vsock/af_vsock.c | 10 ++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h index f9fb566e75cf..30cba806e344 100644 --- a/include/net/af_vsock.h +++ b/include/net/af_vsock.h @@ -27,6 +27,11 @@ #define LAST_RESERVED_PORT 1023 +#define VSOCK_HASH_SIZE 251 +extern struct list_head vsock_bind_table[VSOCK_HASH_SIZE + 1]; +extern struct list_head vsock_connected_table[VSOCK_HASH_SIZE]; +extern spinlock_t vsock_table_lock; + #define vsock_sk(__sk) ((struct vsock_sock *)__sk) #define sk_vsock(__vsk) (&(__vsk)->sk) diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index dfc8c51e4d74..9afe4da8c67d 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -153,7 +153,6 @@ EXPORT_SYMBOL_GPL(vm_sockets_get_local_cid); * vsock_bind_table[VSOCK_HASH_SIZE] is for unbound sockets. The hash function * mods with VSOCK_HASH_SIZE to ensure this. */ -#define VSOCK_HASH_SIZE 251 #define MAX_PORT_RETRIES 24 #define VSOCK_HASH(addr) ((addr)->svm_port % VSOCK_HASH_SIZE) @@ -168,9 +167,12 @@ EXPORT_SYMBOL_GPL(vm_sockets_get_local_cid); #define vsock_connected_sockets_vsk(vsk) \ vsock_connected_sockets(&(vsk)->remote_addr, &(vsk)->local_addr) -static struct list_head vsock_bind_table[VSOCK_HASH_SIZE + 1]; -static struct list_head vsock_connected_table[VSOCK_HASH_SIZE]; -static DEFINE_SPINLOCK(vsock_table_lock); +struct list_head vsock_bind_table[VSOCK_HASH_SIZE + 1]; +EXPORT_SYMBOL_GPL(vsock_bind_table); +struct list_head vsock_connected_table[VSOCK_HASH_SIZE]; +EXPORT_SYMBOL_GPL(vsock_connected_table); +DEFINE_SPINLOCK(vsock_table_lock); +EXPORT_SYMBOL_GPL(vsock_table_lock); /* Autobind this socket to the local address if necessary. */ static int vsock_auto_bind(struct vsock_sock *vsk) -- cgit v1.2.3-59-g8ed1b From bf359b8127719535f88494adb3c2b73c06667dcd Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 5 Oct 2017 16:46:51 -0400 Subject: VSOCK: move __vsock_in_bound/connected_table() to af_vsock.h The vsock_diag.ko module will need to check socket table membership. Signed-off-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- include/net/af_vsock.h | 12 ++++++++++++ net/vmw_vsock/af_vsock.c | 10 ---------- 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h index 30cba806e344..3dd217718a2f 100644 --- a/include/net/af_vsock.h +++ b/include/net/af_vsock.h @@ -180,6 +180,18 @@ const struct vsock_transport *vsock_core_get_transport(void); /**** UTILS ****/ +/* vsock_table_lock must be held */ +static inline bool __vsock_in_bound_table(struct vsock_sock *vsk) +{ + return !list_empty(&vsk->bound_table); +} + +/* vsock_table_lock must be held */ +static inline bool __vsock_in_connected_table(struct vsock_sock *vsk) +{ + return !list_empty(&vsk->connected_table); +} + void vsock_release_pending(struct sock *pending); void vsock_add_pending(struct sock *listener, struct sock *pending); void vsock_remove_pending(struct sock *listener, struct sock *pending); diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 9afe4da8c67d..9b179a0081b3 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -250,16 +250,6 @@ static struct sock *__vsock_find_connected_socket(struct sockaddr_vm *src, return NULL; } -static bool __vsock_in_bound_table(struct vsock_sock *vsk) -{ - return !list_empty(&vsk->bound_table); -} - -static bool __vsock_in_connected_table(struct vsock_sock *vsk) -{ - return !list_empty(&vsk->connected_table); -} - static void vsock_insert_unbound(struct vsock_sock *vsk) { spin_lock_bh(&vsock_table_lock); -- cgit v1.2.3-59-g8ed1b From 3b4477d2dcf2709d0be89e2a8dced3d0f4a017f2 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 5 Oct 2017 16:46:52 -0400 Subject: VSOCK: use TCP state constants for sk_state There are two state fields: socket->state and sock->sk_state. The socket->state field uses SS_UNCONNECTED, SS_CONNECTED, etc while the sock->sk_state typically uses values that match TCP state constants (TCP_CLOSE, TCP_ESTABLISHED). AF_VSOCK does not follow this convention and instead uses SS_* constants for both fields. The sk_state field will be exposed to userspace through the vsock_diag interface for ss(8), netstat(8), and other programs. This patch switches sk_state to TCP state constants so that the meaning of this field is consistent with other address families. Not just AF_INET and AF_INET6 use the TCP constants, AF_UNIX and others do too. The following mapping was used to convert the code: SS_FREE -> TCP_CLOSE SS_UNCONNECTED -> TCP_CLOSE SS_CONNECTING -> TCP_SYN_SENT SS_CONNECTED -> TCP_ESTABLISHED SS_DISCONNECTING -> TCP_CLOSING VSOCK_SS_LISTEN -> TCP_LISTEN In __vsock_create() the sk_state initialization was dropped because sock_init_data() already initializes sk_state to TCP_CLOSE. Signed-off-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- include/net/af_vsock.h | 3 -- net/vmw_vsock/af_vsock.c | 46 ++++++++++++++++------------ net/vmw_vsock/hyperv_transport.c | 12 ++++---- net/vmw_vsock/virtio_transport.c | 2 +- net/vmw_vsock/virtio_transport_common.c | 22 ++++++------- net/vmw_vsock/vmci_transport.c | 34 ++++++++++---------- net/vmw_vsock/vmci_transport_notify.c | 2 +- net/vmw_vsock/vmci_transport_notify_qstate.c | 2 +- 8 files changed, 64 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h index 3dd217718a2f..9324ac2d9ff2 100644 --- a/include/net/af_vsock.h +++ b/include/net/af_vsock.h @@ -22,9 +22,6 @@ #include "vsock_addr.h" -/* vsock-specific sock->sk_state constants */ -#define VSOCK_SS_LISTEN 255 - #define LAST_RESERVED_PORT 1023 #define VSOCK_HASH_SIZE 251 diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c index 9b179a0081b3..98359c19522f 100644 --- a/net/vmw_vsock/af_vsock.c +++ b/net/vmw_vsock/af_vsock.c @@ -36,7 +36,7 @@ * not support simultaneous connects (two "client" sockets connecting). * * - "Server" sockets are referred to as listener sockets throughout this - * implementation because they are in the VSOCK_SS_LISTEN state. When a + * implementation because they are in the TCP_LISTEN state. When a * connection request is received (the second kind of socket mentioned above), * we create a new socket and refer to it as a pending socket. These pending * sockets are placed on the pending connection list of the listener socket. @@ -82,6 +82,15 @@ * argument, we must ensure the reference count is increased to ensure the * socket isn't freed before the function is run; the deferred function will * then drop the reference. + * + * - sk->sk_state uses the TCP state constants because they are widely used by + * other address families and exposed to userspace tools like ss(8): + * + * TCP_CLOSE - unconnected + * TCP_SYN_SENT - connecting + * TCP_ESTABLISHED - connected + * TCP_CLOSING - disconnecting + * TCP_LISTEN - listening */ #include @@ -477,7 +486,7 @@ void vsock_pending_work(struct work_struct *work) if (vsock_in_connected_table(vsk)) vsock_remove_connected(vsk); - sk->sk_state = SS_FREE; + sk->sk_state = TCP_CLOSE; out: release_sock(sk); @@ -617,7 +626,6 @@ struct sock *__vsock_create(struct net *net, sk->sk_destruct = vsock_sk_destruct; sk->sk_backlog_rcv = vsock_queue_rcv_skb; - sk->sk_state = 0; sock_reset_flag(sk, SOCK_DONE); INIT_LIST_HEAD(&vsk->bound_table); @@ -891,7 +899,7 @@ static unsigned int vsock_poll(struct file *file, struct socket *sock, /* Listening sockets that have connections in their accept * queue can be read. */ - if (sk->sk_state == VSOCK_SS_LISTEN + if (sk->sk_state == TCP_LISTEN && !vsock_is_accept_queue_empty(sk)) mask |= POLLIN | POLLRDNORM; @@ -920,7 +928,7 @@ static unsigned int vsock_poll(struct file *file, struct socket *sock, } /* Connected sockets that can produce data can be written. */ - if (sk->sk_state == SS_CONNECTED) { + if (sk->sk_state == TCP_ESTABLISHED) { if (!(sk->sk_shutdown & SEND_SHUTDOWN)) { bool space_avail_now = false; int ret = transport->notify_poll_out( @@ -942,7 +950,7 @@ static unsigned int vsock_poll(struct file *file, struct socket *sock, * POLLOUT|POLLWRNORM when peer is closed and nothing to read, * but local send is not shutdown. */ - if (sk->sk_state == SS_UNCONNECTED) { + if (sk->sk_state == TCP_CLOSE) { if (!(sk->sk_shutdown & SEND_SHUTDOWN)) mask |= POLLOUT | POLLWRNORM; @@ -1112,9 +1120,9 @@ static void vsock_connect_timeout(struct work_struct *work) sk = sk_vsock(vsk); lock_sock(sk); - if (sk->sk_state == SS_CONNECTING && + if (sk->sk_state == TCP_SYN_SENT && (sk->sk_shutdown != SHUTDOWN_MASK)) { - sk->sk_state = SS_UNCONNECTED; + sk->sk_state = TCP_CLOSE; sk->sk_err = ETIMEDOUT; sk->sk_error_report(sk); cancel = 1; @@ -1160,7 +1168,7 @@ static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr, err = -EALREADY; break; default: - if ((sk->sk_state == VSOCK_SS_LISTEN) || + if ((sk->sk_state == TCP_LISTEN) || vsock_addr_cast(addr, addr_len, &remote_addr) != 0) { err = -EINVAL; goto out; @@ -1183,7 +1191,7 @@ static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr, if (err) goto out; - sk->sk_state = SS_CONNECTING; + sk->sk_state = TCP_SYN_SENT; err = transport->connect(vsk); if (err < 0) @@ -1203,7 +1211,7 @@ static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr, timeout = vsk->connect_timeout; prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - while (sk->sk_state != SS_CONNECTED && sk->sk_err == 0) { + while (sk->sk_state != TCP_ESTABLISHED && sk->sk_err == 0) { if (flags & O_NONBLOCK) { /* If we're not going to block, we schedule a timeout * function to generate a timeout on the connection @@ -1226,13 +1234,13 @@ static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr, if (signal_pending(current)) { err = sock_intr_errno(timeout); - sk->sk_state = SS_UNCONNECTED; + sk->sk_state = TCP_CLOSE; sock->state = SS_UNCONNECTED; vsock_transport_cancel_pkt(vsk); goto out_wait; } else if (timeout == 0) { err = -ETIMEDOUT; - sk->sk_state = SS_UNCONNECTED; + sk->sk_state = TCP_CLOSE; sock->state = SS_UNCONNECTED; vsock_transport_cancel_pkt(vsk); goto out_wait; @@ -1243,7 +1251,7 @@ static int vsock_stream_connect(struct socket *sock, struct sockaddr *addr, if (sk->sk_err) { err = -sk->sk_err; - sk->sk_state = SS_UNCONNECTED; + sk->sk_state = TCP_CLOSE; sock->state = SS_UNCONNECTED; } else { err = 0; @@ -1276,7 +1284,7 @@ static int vsock_accept(struct socket *sock, struct socket *newsock, int flags, goto out; } - if (listener->sk_state != VSOCK_SS_LISTEN) { + if (listener->sk_state != TCP_LISTEN) { err = -EINVAL; goto out; } @@ -1366,7 +1374,7 @@ static int vsock_listen(struct socket *sock, int backlog) } sk->sk_max_ack_backlog = backlog; - sk->sk_state = VSOCK_SS_LISTEN; + sk->sk_state = TCP_LISTEN; err = 0; @@ -1546,7 +1554,7 @@ static int vsock_stream_sendmsg(struct socket *sock, struct msghdr *msg, /* Callers should not provide a destination with stream sockets. */ if (msg->msg_namelen) { - err = sk->sk_state == SS_CONNECTED ? -EISCONN : -EOPNOTSUPP; + err = sk->sk_state == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP; goto out; } @@ -1557,7 +1565,7 @@ static int vsock_stream_sendmsg(struct socket *sock, struct msghdr *msg, goto out; } - if (sk->sk_state != SS_CONNECTED || + if (sk->sk_state != TCP_ESTABLISHED || !vsock_addr_bound(&vsk->local_addr)) { err = -ENOTCONN; goto out; @@ -1681,7 +1689,7 @@ vsock_stream_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, lock_sock(sk); - if (sk->sk_state != SS_CONNECTED) { + if (sk->sk_state != TCP_ESTABLISHED) { /* Recvmsg is supposed to return 0 if a peer performs an * orderly shutdown. Differentiate between that case and when a * peer has not connected or a local shutdown occured with the diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c index 14ed5a344cdf..bbac023e70d1 100644 --- a/net/vmw_vsock/hyperv_transport.c +++ b/net/vmw_vsock/hyperv_transport.c @@ -310,7 +310,7 @@ static void hvs_close_connection(struct vmbus_channel *chan) struct sock *sk = get_per_channel_state(chan); struct vsock_sock *vsk = vsock_sk(sk); - sk->sk_state = SS_UNCONNECTED; + sk->sk_state = TCP_CLOSE; sock_set_flag(sk, SOCK_DONE); vsk->peer_shutdown |= SEND_SHUTDOWN | RCV_SHUTDOWN; @@ -344,8 +344,8 @@ static void hvs_open_connection(struct vmbus_channel *chan) if (!sk) return; - if ((conn_from_host && sk->sk_state != VSOCK_SS_LISTEN) || - (!conn_from_host && sk->sk_state != SS_CONNECTING)) + if ((conn_from_host && sk->sk_state != TCP_LISTEN) || + (!conn_from_host && sk->sk_state != TCP_SYN_SENT)) goto out; if (conn_from_host) { @@ -357,7 +357,7 @@ static void hvs_open_connection(struct vmbus_channel *chan) if (!new) goto out; - new->sk_state = SS_CONNECTING; + new->sk_state = TCP_SYN_SENT; vnew = vsock_sk(new); hvs_new = vnew->trans; hvs_new->chan = chan; @@ -384,7 +384,7 @@ static void hvs_open_connection(struct vmbus_channel *chan) vmbus_set_chn_rescind_callback(chan, hvs_close_connection); if (conn_from_host) { - new->sk_state = SS_CONNECTED; + new->sk_state = TCP_ESTABLISHED; sk->sk_ack_backlog++; hvs_addr_init(&vnew->local_addr, if_type); @@ -399,7 +399,7 @@ static void hvs_open_connection(struct vmbus_channel *chan) vsock_enqueue_accept(sk, new); release_sock(sk); } else { - sk->sk_state = SS_CONNECTED; + sk->sk_state = TCP_ESTABLISHED; sk->sk_socket->state = SS_CONNECTED; vsock_insert_connected(vsock_sk(sk)); diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index 403d86e80162..8e03bd3f3668 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -414,7 +414,7 @@ static void virtio_vsock_event_fill(struct virtio_vsock *vsock) static void virtio_vsock_reset_sock(struct sock *sk) { lock_sock(sk); - sk->sk_state = SS_UNCONNECTED; + sk->sk_state = TCP_CLOSE; sk->sk_err = ECONNRESET; sk->sk_error_report(sk); release_sock(sk); diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c index edba7ab97563..3ae3a33da70b 100644 --- a/net/vmw_vsock/virtio_transport_common.c +++ b/net/vmw_vsock/virtio_transport_common.c @@ -708,7 +708,7 @@ static void virtio_transport_do_close(struct vsock_sock *vsk, sock_set_flag(sk, SOCK_DONE); vsk->peer_shutdown = SHUTDOWN_MASK; if (vsock_stream_has_data(vsk) <= 0) - sk->sk_state = SS_DISCONNECTING; + sk->sk_state = TCP_CLOSING; sk->sk_state_change(sk); if (vsk->close_work_scheduled && @@ -748,8 +748,8 @@ static bool virtio_transport_close(struct vsock_sock *vsk) { struct sock *sk = &vsk->sk; - if (!(sk->sk_state == SS_CONNECTED || - sk->sk_state == SS_DISCONNECTING)) + if (!(sk->sk_state == TCP_ESTABLISHED || + sk->sk_state == TCP_CLOSING)) return true; /* Already received SHUTDOWN from peer, reply with RST */ @@ -801,7 +801,7 @@ virtio_transport_recv_connecting(struct sock *sk, switch (le16_to_cpu(pkt->hdr.op)) { case VIRTIO_VSOCK_OP_RESPONSE: - sk->sk_state = SS_CONNECTED; + sk->sk_state = TCP_ESTABLISHED; sk->sk_socket->state = SS_CONNECTED; vsock_insert_connected(vsk); sk->sk_state_change(sk); @@ -821,7 +821,7 @@ virtio_transport_recv_connecting(struct sock *sk, destroy: virtio_transport_reset(vsk, pkt); - sk->sk_state = SS_UNCONNECTED; + sk->sk_state = TCP_CLOSE; sk->sk_err = skerr; sk->sk_error_report(sk); return err; @@ -857,7 +857,7 @@ virtio_transport_recv_connected(struct sock *sk, vsk->peer_shutdown |= SEND_SHUTDOWN; if (vsk->peer_shutdown == SHUTDOWN_MASK && vsock_stream_has_data(vsk) <= 0) - sk->sk_state = SS_DISCONNECTING; + sk->sk_state = TCP_CLOSING; if (le32_to_cpu(pkt->hdr.flags)) sk->sk_state_change(sk); break; @@ -928,7 +928,7 @@ virtio_transport_recv_listen(struct sock *sk, struct virtio_vsock_pkt *pkt) lock_sock_nested(child, SINGLE_DEPTH_NESTING); - child->sk_state = SS_CONNECTED; + child->sk_state = TCP_ESTABLISHED; vchild = vsock_sk(child); vsock_addr_init(&vchild->local_addr, le64_to_cpu(pkt->hdr.dst_cid), @@ -1016,18 +1016,18 @@ void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt) sk->sk_write_space(sk); switch (sk->sk_state) { - case VSOCK_SS_LISTEN: + case TCP_LISTEN: virtio_transport_recv_listen(sk, pkt); virtio_transport_free_pkt(pkt); break; - case SS_CONNECTING: + case TCP_SYN_SENT: virtio_transport_recv_connecting(sk, pkt); virtio_transport_free_pkt(pkt); break; - case SS_CONNECTED: + case TCP_ESTABLISHED: virtio_transport_recv_connected(sk, pkt); break; - case SS_DISCONNECTING: + case TCP_CLOSING: virtio_transport_recv_disconnecting(sk, pkt); virtio_transport_free_pkt(pkt); break; diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c index 0206155bff53..391775e3575c 100644 --- a/net/vmw_vsock/vmci_transport.c +++ b/net/vmw_vsock/vmci_transport.c @@ -742,7 +742,7 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg) /* The local context ID may be out of date, update it. */ vsk->local_addr.svm_cid = dst.svm_cid; - if (sk->sk_state == SS_CONNECTED) + if (sk->sk_state == TCP_ESTABLISHED) vmci_trans(vsk)->notify_ops->handle_notify_pkt( sk, pkt, true, &dst, &src, &bh_process_pkt); @@ -800,7 +800,9 @@ static void vmci_transport_handle_detach(struct sock *sk) * left in our consume queue. */ if (vsock_stream_has_data(vsk) <= 0) { - if (sk->sk_state == SS_CONNECTING) { + sk->sk_state = TCP_CLOSE; + + if (sk->sk_state == TCP_SYN_SENT) { /* The peer may detach from a queue pair while * we are still in the connecting state, i.e., * if the peer VM is killed after attaching to @@ -809,12 +811,10 @@ static void vmci_transport_handle_detach(struct sock *sk) * event like a reset. */ - sk->sk_state = SS_UNCONNECTED; sk->sk_err = ECONNRESET; sk->sk_error_report(sk); return; } - sk->sk_state = SS_UNCONNECTED; } sk->sk_state_change(sk); } @@ -882,17 +882,17 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work) vsock_sk(sk)->local_addr.svm_cid = pkt->dg.dst.context; switch (sk->sk_state) { - case VSOCK_SS_LISTEN: + case TCP_LISTEN: vmci_transport_recv_listen(sk, pkt); break; - case SS_CONNECTING: + case TCP_SYN_SENT: /* Processing of pending connections for servers goes through * the listening socket, so see vmci_transport_recv_listen() * for that path. */ vmci_transport_recv_connecting_client(sk, pkt); break; - case SS_CONNECTED: + case TCP_ESTABLISHED: vmci_transport_recv_connected(sk, pkt); break; default: @@ -941,7 +941,7 @@ static int vmci_transport_recv_listen(struct sock *sk, vsock_sk(pending)->local_addr.svm_cid = pkt->dg.dst.context; switch (pending->sk_state) { - case SS_CONNECTING: + case TCP_SYN_SENT: err = vmci_transport_recv_connecting_server(sk, pending, pkt); @@ -1071,7 +1071,7 @@ static int vmci_transport_recv_listen(struct sock *sk, vsock_add_pending(sk, pending); sk->sk_ack_backlog++; - pending->sk_state = SS_CONNECTING; + pending->sk_state = TCP_SYN_SENT; vmci_trans(vpending)->produce_size = vmci_trans(vpending)->consume_size = qp_size; vmci_trans(vpending)->queue_pair_size = qp_size; @@ -1196,11 +1196,11 @@ vmci_transport_recv_connecting_server(struct sock *listener, * the socket will be valid until it is removed from the queue. * * If we fail sending the attach below, we remove the socket from the - * connected list and move the socket to SS_UNCONNECTED before + * connected list and move the socket to TCP_CLOSE before * releasing the lock, so a pending slow path processing of an incoming * packet will not see the socket in the connected state in that case. */ - pending->sk_state = SS_CONNECTED; + pending->sk_state = TCP_ESTABLISHED; vsock_insert_connected(vpending); @@ -1231,7 +1231,7 @@ vmci_transport_recv_connecting_server(struct sock *listener, destroy: pending->sk_err = skerr; - pending->sk_state = SS_UNCONNECTED; + pending->sk_state = TCP_CLOSE; /* As long as we drop our reference, all necessary cleanup will handle * when the cleanup function drops its reference and our destruct * implementation is called. Note that since the listen handler will @@ -1269,7 +1269,7 @@ vmci_transport_recv_connecting_client(struct sock *sk, * accounting (it can already be found since it's in the bound * table). */ - sk->sk_state = SS_CONNECTED; + sk->sk_state = TCP_ESTABLISHED; sk->sk_socket->state = SS_CONNECTED; vsock_insert_connected(vsk); sk->sk_state_change(sk); @@ -1337,7 +1337,7 @@ vmci_transport_recv_connecting_client(struct sock *sk, destroy: vmci_transport_send_reset(sk, pkt); - sk->sk_state = SS_UNCONNECTED; + sk->sk_state = TCP_CLOSE; sk->sk_err = skerr; sk->sk_error_report(sk); return err; @@ -1525,7 +1525,7 @@ static int vmci_transport_recv_connected(struct sock *sk, sock_set_flag(sk, SOCK_DONE); vsk->peer_shutdown = SHUTDOWN_MASK; if (vsock_stream_has_data(vsk) <= 0) - sk->sk_state = SS_DISCONNECTING; + sk->sk_state = TCP_CLOSING; sk->sk_state_change(sk); break; @@ -1789,7 +1789,7 @@ static int vmci_transport_connect(struct vsock_sock *vsk) err = vmci_transport_send_conn_request( sk, vmci_trans(vsk)->queue_pair_size); if (err < 0) { - sk->sk_state = SS_UNCONNECTED; + sk->sk_state = TCP_CLOSE; return err; } } else { @@ -1799,7 +1799,7 @@ static int vmci_transport_connect(struct vsock_sock *vsk) sk, vmci_trans(vsk)->queue_pair_size, supported_proto_versions); if (err < 0) { - sk->sk_state = SS_UNCONNECTED; + sk->sk_state = TCP_CLOSE; return err; } diff --git a/net/vmw_vsock/vmci_transport_notify.c b/net/vmw_vsock/vmci_transport_notify.c index 1406db4d97d1..41fb427f150a 100644 --- a/net/vmw_vsock/vmci_transport_notify.c +++ b/net/vmw_vsock/vmci_transport_notify.c @@ -355,7 +355,7 @@ vmci_transport_notify_pkt_poll_in(struct sock *sk, * queue. Ask for notifications when there is something to * read. */ - if (sk->sk_state == SS_CONNECTED) { + if (sk->sk_state == TCP_ESTABLISHED) { if (!send_waiting_read(sk, 1)) return -1; diff --git a/net/vmw_vsock/vmci_transport_notify_qstate.c b/net/vmw_vsock/vmci_transport_notify_qstate.c index f3a0afc46208..0cc84f2bb05e 100644 --- a/net/vmw_vsock/vmci_transport_notify_qstate.c +++ b/net/vmw_vsock/vmci_transport_notify_qstate.c @@ -176,7 +176,7 @@ vmci_transport_notify_pkt_poll_in(struct sock *sk, * queue. Ask for notifications when there is something to * read. */ - if (sk->sk_state == SS_CONNECTED) + if (sk->sk_state == TCP_ESTABLISHED) vsock_block_update_write_window(sk); *data_ready_now = false; } -- cgit v1.2.3-59-g8ed1b From 413a4317aca7d6367d57a5971b0c461f03851207 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 5 Oct 2017 16:46:53 -0400 Subject: VSOCK: add sock_diag interface This patch adds the sock_diag interface for querying sockets from userspace. Tools like ss(8) and netstat(8) can use this interface to list open sockets. The userspace ABI is defined in and includes netlink request and response structs. The request can query sockets based on their sk_state (e.g. listening sockets only) and the response contains socket information fields including the local/remote addresses, inode number, etc. This patch does not dump VMCI pending sockets because I have only tested the virtio transport, which does not use pending sockets. Support can be added later by extending vsock_diag_dump() if needed by VMCI users. Signed-off-by: Stefan Hajnoczi Signed-off-by: David S. Miller --- MAINTAINERS | 2 + include/uapi/linux/vm_sockets_diag.h | 33 +++++++ net/vmw_vsock/Kconfig | 10 ++ net/vmw_vsock/Makefile | 3 + net/vmw_vsock/diag.c | 186 +++++++++++++++++++++++++++++++++++ 5 files changed, 234 insertions(+) create mode 100644 include/uapi/linux/vm_sockets_diag.h create mode 100644 net/vmw_vsock/diag.c (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index d0cbb3d7a0ca..0fd9121953bb 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14286,6 +14286,8 @@ S: Maintained F: include/linux/virtio_vsock.h F: include/uapi/linux/virtio_vsock.h F: include/uapi/linux/vsockmon.h +F: include/uapi/linux/vm_sockets_diag.h +F: net/vmw_vsock/diag.c F: net/vmw_vsock/af_vsock_tap.c F: net/vmw_vsock/virtio_transport_common.c F: net/vmw_vsock/virtio_transport.c diff --git a/include/uapi/linux/vm_sockets_diag.h b/include/uapi/linux/vm_sockets_diag.h new file mode 100644 index 000000000000..14cd7dc5a187 --- /dev/null +++ b/include/uapi/linux/vm_sockets_diag.h @@ -0,0 +1,33 @@ +/* AF_VSOCK sock_diag(7) interface for querying open sockets */ + +#ifndef _UAPI__VM_SOCKETS_DIAG_H__ +#define _UAPI__VM_SOCKETS_DIAG_H__ + +#include + +/* Request */ +struct vsock_diag_req { + __u8 sdiag_family; /* must be AF_VSOCK */ + __u8 sdiag_protocol; /* must be 0 */ + __u16 pad; /* must be 0 */ + __u32 vdiag_states; /* query bitmap (e.g. 1 << TCP_LISTEN) */ + __u32 vdiag_ino; /* must be 0 (reserved) */ + __u32 vdiag_show; /* must be 0 (reserved) */ + __u32 vdiag_cookie[2]; +}; + +/* Response */ +struct vsock_diag_msg { + __u8 vdiag_family; /* AF_VSOCK */ + __u8 vdiag_type; /* SOCK_STREAM or SOCK_DGRAM */ + __u8 vdiag_state; /* sk_state (e.g. TCP_LISTEN) */ + __u8 vdiag_shutdown; /* local RCV_SHUTDOWN | SEND_SHUTDOWN */ + __u32 vdiag_src_cid; + __u32 vdiag_src_port; + __u32 vdiag_dst_cid; + __u32 vdiag_dst_port; + __u32 vdiag_ino; + __u32 vdiag_cookie[2]; +}; + +#endif /* _UAPI__VM_SOCKETS_DIAG_H__ */ diff --git a/net/vmw_vsock/Kconfig b/net/vmw_vsock/Kconfig index a24369d175fd..970f96489fe7 100644 --- a/net/vmw_vsock/Kconfig +++ b/net/vmw_vsock/Kconfig @@ -15,6 +15,16 @@ config VSOCKETS To compile this driver as a module, choose M here: the module will be called vsock. If unsure, say N. +config VSOCKETS_DIAG + tristate "Virtual Sockets monitoring interface" + depends on VSOCKETS + default y + help + Support for PF_VSOCK sockets monitoring interface used by the ss tool. + If unsure, say Y. + + Enable this module so userspace applications can query open sockets. + config VMWARE_VMCI_VSOCKETS tristate "VMware VMCI transport for Virtual Sockets" depends on VSOCKETS && VMWARE_VMCI diff --git a/net/vmw_vsock/Makefile b/net/vmw_vsock/Makefile index e63d574234a9..64afc06805da 100644 --- a/net/vmw_vsock/Makefile +++ b/net/vmw_vsock/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_VSOCKETS) += vsock.o +obj-$(CONFIG_VSOCKETS_DIAG) += vsock_diag.o obj-$(CONFIG_VMWARE_VMCI_VSOCKETS) += vmw_vsock_vmci_transport.o obj-$(CONFIG_VIRTIO_VSOCKETS) += vmw_vsock_virtio_transport.o obj-$(CONFIG_VIRTIO_VSOCKETS_COMMON) += vmw_vsock_virtio_transport_common.o @@ -6,6 +7,8 @@ obj-$(CONFIG_HYPERV_VSOCKETS) += hv_sock.o vsock-y += af_vsock.o af_vsock_tap.o vsock_addr.o +vsock_diag-y += diag.o + vmw_vsock_vmci_transport-y += vmci_transport.o vmci_transport_notify.o \ vmci_transport_notify_qstate.o diff --git a/net/vmw_vsock/diag.c b/net/vmw_vsock/diag.c new file mode 100644 index 000000000000..31b567652250 --- /dev/null +++ b/net/vmw_vsock/diag.c @@ -0,0 +1,186 @@ +/* + * vsock sock_diag(7) module + * + * Copyright (C) 2017 Red Hat, Inc. + * Author: Stefan Hajnoczi + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation version 2 and no later version. + * + * 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. + */ + +#include +#include +#include +#include + +static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, + u32 portid, u32 seq, u32 flags) +{ + struct vsock_sock *vsk = vsock_sk(sk); + struct vsock_diag_msg *rep; + struct nlmsghdr *nlh; + + nlh = nlmsg_put(skb, portid, seq, SOCK_DIAG_BY_FAMILY, sizeof(*rep), + flags); + if (!nlh) + return -EMSGSIZE; + + rep = nlmsg_data(nlh); + rep->vdiag_family = AF_VSOCK; + + /* Lock order dictates that sk_lock is acquired before + * vsock_table_lock, so we cannot lock here. Simply don't take + * sk_lock; sk is guaranteed to stay alive since vsock_table_lock is + * held. + */ + rep->vdiag_type = sk->sk_type; + rep->vdiag_state = sk->sk_state; + rep->vdiag_shutdown = sk->sk_shutdown; + rep->vdiag_src_cid = vsk->local_addr.svm_cid; + rep->vdiag_src_port = vsk->local_addr.svm_port; + rep->vdiag_dst_cid = vsk->remote_addr.svm_cid; + rep->vdiag_dst_port = vsk->remote_addr.svm_port; + rep->vdiag_ino = sock_i_ino(sk); + + sock_diag_save_cookie(sk, rep->vdiag_cookie); + + return 0; +} + +static int vsock_diag_dump(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct vsock_diag_req *req; + struct vsock_sock *vsk; + unsigned int bucket; + unsigned int last_i; + unsigned int table; + struct net *net; + unsigned int i; + + req = nlmsg_data(cb->nlh); + net = sock_net(skb->sk); + + /* State saved between calls: */ + table = cb->args[0]; + bucket = cb->args[1]; + i = last_i = cb->args[2]; + + /* TODO VMCI pending sockets? */ + + spin_lock_bh(&vsock_table_lock); + + /* Bind table (locally created sockets) */ + if (table == 0) { + while (bucket < ARRAY_SIZE(vsock_bind_table)) { + struct list_head *head = &vsock_bind_table[bucket]; + + i = 0; + list_for_each_entry(vsk, head, bound_table) { + struct sock *sk = sk_vsock(vsk); + + if (!net_eq(sock_net(sk), net)) + continue; + if (i < last_i) + goto next_bind; + if (!(req->vdiag_states & (1 << sk->sk_state))) + goto next_bind; + if (sk_diag_fill(sk, skb, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NLM_F_MULTI) < 0) + goto done; +next_bind: + i++; + } + last_i = 0; + bucket++; + } + + table++; + bucket = 0; + } + + /* Connected table (accepted connections) */ + while (bucket < ARRAY_SIZE(vsock_connected_table)) { + struct list_head *head = &vsock_connected_table[bucket]; + + i = 0; + list_for_each_entry(vsk, head, connected_table) { + struct sock *sk = sk_vsock(vsk); + + /* Skip sockets we've already seen above */ + if (__vsock_in_bound_table(vsk)) + continue; + + if (!net_eq(sock_net(sk), net)) + continue; + if (i < last_i) + goto next_connected; + if (!(req->vdiag_states & (1 << sk->sk_state))) + goto next_connected; + if (sk_diag_fill(sk, skb, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NLM_F_MULTI) < 0) + goto done; +next_connected: + i++; + } + last_i = 0; + bucket++; + } + +done: + spin_unlock_bh(&vsock_table_lock); + + cb->args[0] = table; + cb->args[1] = bucket; + cb->args[2] = i; + + return skb->len; +} + +static int vsock_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h) +{ + int hdrlen = sizeof(struct vsock_diag_req); + struct net *net = sock_net(skb->sk); + + if (nlmsg_len(h) < hdrlen) + return -EINVAL; + + if (h->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = vsock_diag_dump, + }; + return netlink_dump_start(net->diag_nlsk, skb, h, &c); + } + + return -EOPNOTSUPP; +} + +static const struct sock_diag_handler vsock_diag_handler = { + .family = AF_VSOCK, + .dump = vsock_diag_handler_dump, +}; + +static int __init vsock_diag_init(void) +{ + return sock_diag_register(&vsock_diag_handler); +} + +static void __exit vsock_diag_exit(void) +{ + sock_diag_unregister(&vsock_diag_handler); +} + +module_init(vsock_diag_init); +module_exit(vsock_diag_exit); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_NETLINK, NETLINK_SOCK_DIAG, + 40 /* AF_VSOCK */); -- cgit v1.2.3-59-g8ed1b From 6171a0310a06a7a0cb83713fa7068bdd4192de19 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Wed, 6 Sep 2017 08:40:41 -0400 Subject: ftrace/kallsyms: Have /proc/kallsyms show saved mod init functions If a module is loaded while tracing is enabled, then there's a possibility that the module init functions were traced. These functions have their name and address stored by ftrace such that it can translate the function address that is written into the buffer into a human readable function name. As userspace tools may be doing the same, they need a way to map function names to their address as well. This is done through reading /proc/kallsyms. Signed-off-by: Steven Rostedt (VMware) --- include/linux/ftrace.h | 9 +++++++++ kernel/kallsyms.c | 38 ++++++++++++++++++++++++++++++++------ kernel/trace/ftrace.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 202b40784c4e..346f8294e40a 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -56,6 +56,9 @@ struct ftrace_hash; const char * ftrace_mod_address_lookup(unsigned long addr, unsigned long *size, unsigned long *off, char **modname, char *sym); +int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value, + char *type, char *name, + char *module_name, int *exported); #else static inline const char * ftrace_mod_address_lookup(unsigned long addr, unsigned long *size, @@ -63,6 +66,12 @@ ftrace_mod_address_lookup(unsigned long addr, unsigned long *size, { return NULL; } +static inline int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value, + char *type, char *name, + char *module_name, int *exported) +{ + return -1; +} #endif diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 976ecb9275d9..1966fe1c2b57 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -479,6 +479,7 @@ EXPORT_SYMBOL(__print_symbol); struct kallsym_iter { loff_t pos; loff_t pos_mod_end; + loff_t pos_ftrace_mod_end; unsigned long value; unsigned int nameoff; /* If iterating in core kernel symbols. */ char type; @@ -501,11 +502,25 @@ static int get_ksymbol_mod(struct kallsym_iter *iter) return 1; } +static int get_ksymbol_ftrace_mod(struct kallsym_iter *iter) +{ + int ret = ftrace_mod_get_kallsym(iter->pos - iter->pos_mod_end, + &iter->value, &iter->type, + iter->name, iter->module_name, + &iter->exported); + if (ret < 0) { + iter->pos_ftrace_mod_end = iter->pos; + return 0; + } + + return 1; +} + static int get_ksymbol_bpf(struct kallsym_iter *iter) { iter->module_name[0] = '\0'; iter->exported = 0; - return bpf_get_kallsym(iter->pos - iter->pos_mod_end, + return bpf_get_kallsym(iter->pos - iter->pos_ftrace_mod_end, &iter->value, &iter->type, iter->name) < 0 ? 0 : 1; } @@ -530,20 +545,31 @@ static void reset_iter(struct kallsym_iter *iter, loff_t new_pos) iter->name[0] = '\0'; iter->nameoff = get_symbol_offset(new_pos); iter->pos = new_pos; - if (new_pos == 0) + if (new_pos == 0) { iter->pos_mod_end = 0; + iter->pos_ftrace_mod_end = 0; + } } static int update_iter_mod(struct kallsym_iter *iter, loff_t pos) { iter->pos = pos; - if (iter->pos_mod_end > 0 && - iter->pos_mod_end < iter->pos) + if (iter->pos_ftrace_mod_end > 0 && + iter->pos_ftrace_mod_end < iter->pos) return get_ksymbol_bpf(iter); - if (!get_ksymbol_mod(iter)) - return get_ksymbol_bpf(iter); + if (iter->pos_mod_end > 0 && + iter->pos_mod_end < iter->pos) { + if (!get_ksymbol_ftrace_mod(iter)) + return get_ksymbol_bpf(iter); + return 1; + } + + if (!get_ksymbol_mod(iter)) { + if (!get_ksymbol_ftrace_mod(iter)) + return get_ksymbol_bpf(iter); + } return 1; } diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index a5824408bed9..9e99bd55732e 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -5689,6 +5689,7 @@ struct ftrace_mod_map { unsigned long start_addr; unsigned long end_addr; struct list_head funcs; + unsigned int num_funcs; }; #ifdef CONFIG_MODULES @@ -5940,6 +5941,8 @@ static void save_ftrace_mod_rec(struct ftrace_mod_map *mod_map, mod_func->ip = rec->ip - offset; mod_func->size = symsize; + mod_map->num_funcs++; + list_add_rcu(&mod_func->list, &mod_map->funcs); } @@ -5956,6 +5959,7 @@ allocate_ftrace_mod_map(struct module *mod, mod_map->mod = mod; mod_map->start_addr = start; mod_map->end_addr = end; + mod_map->num_funcs = 0; INIT_LIST_HEAD_RCU(&mod_map->funcs); @@ -6016,6 +6020,42 @@ ftrace_mod_address_lookup(unsigned long addr, unsigned long *size, return ret; } +int ftrace_mod_get_kallsym(unsigned int symnum, unsigned long *value, + char *type, char *name, + char *module_name, int *exported) +{ + struct ftrace_mod_map *mod_map; + struct ftrace_mod_func *mod_func; + + preempt_disable(); + list_for_each_entry_rcu(mod_map, &ftrace_mod_maps, list) { + + if (symnum >= mod_map->num_funcs) { + symnum -= mod_map->num_funcs; + continue; + } + + list_for_each_entry_rcu(mod_func, &mod_map->funcs, list) { + if (symnum > 1) { + symnum--; + continue; + } + + *value = mod_func->ip; + *type = 'T'; + strlcpy(name, mod_func->name, KSYM_NAME_LEN); + strlcpy(module_name, mod_map->mod->name, MODULE_NAME_LEN); + *exported = 1; + preempt_enable(); + return 0; + } + WARN_ON(1); + break; + } + preempt_enable(); + return -ERANGE; +} + #else static void save_ftrace_mod_rec(struct ftrace_mod_map *mod_map, struct dyn_ftrace *rec) { } -- cgit v1.2.3-59-g8ed1b From 27204aaa9dc67b833b77179fdac556288ec3a4bf Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 4 Oct 2017 10:03:44 -0700 Subject: tcp: uniform the set up of sockets after successful connection Currently in the TCP code, the initialization sequence for cached metrics, congestion control, BPF, etc, after successful connection is very inconsistent. This introduces inconsistent bevhavior and is prone to bugs. The current call sequence is as follows: (1) for active case (tcp_finish_connect() case): tcp_mtup_init(sk); icsk->icsk_af_ops->rebuild_header(sk); tcp_init_metrics(sk); tcp_call_bpf(sk, BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB); tcp_init_congestion_control(sk); tcp_init_buffer_space(sk); (2) for passive case (tcp_rcv_state_process() TCP_SYN_RECV case): icsk->icsk_af_ops->rebuild_header(sk); tcp_call_bpf(sk, BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB); tcp_init_congestion_control(sk); tcp_mtup_init(sk); tcp_init_buffer_space(sk); tcp_init_metrics(sk); (3) for TFO passive case (tcp_fastopen_create_child()): inet_csk(child)->icsk_af_ops->rebuild_header(child); tcp_init_congestion_control(child); tcp_mtup_init(child); tcp_init_metrics(child); tcp_call_bpf(child, BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB); tcp_init_buffer_space(child); This commit uniforms the above functions to have the following sequence: tcp_mtup_init(sk); icsk->icsk_af_ops->rebuild_header(sk); tcp_init_metrics(sk); tcp_call_bpf(sk, BPF_SOCK_OPS_ACTIVE/PASSIVE_ESTABLISHED_CB); tcp_init_congestion_control(sk); tcp_init_buffer_space(sk); This sequence is the same as the (1) active case. We pick this sequence because this order correctly allows BPF to override the settings including congestion control module and initial cwnd, etc from the route, and then allows the CC module to see those settings. Suggested-by: Neal Cardwell Tested-by: Neal Cardwell Signed-off-by: Wei Wang Acked-by: Neal Cardwell Acked-by: Yuchung Cheng Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 1 + net/ipv4/tcp.c | 12 ++++++++++++ net/ipv4/tcp_fastopen.c | 7 +------ net/ipv4/tcp_input.c | 21 +++------------------ 4 files changed, 17 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 7a3a8af56fd6..426c2e986016 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -416,6 +416,7 @@ bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst); void tcp_disable_fack(struct tcp_sock *tp); void tcp_close(struct sock *sk, long timeout); void tcp_init_sock(struct sock *sk); +void tcp_init_transfer(struct sock *sk, int bpf_op); unsigned int tcp_poll(struct file *file, struct socket *sock, struct poll_table_struct *wait); int tcp_getsockopt(struct sock *sk, int level, int optname, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 23225c98d287..c115e37ca608 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -456,6 +456,18 @@ void tcp_init_sock(struct sock *sk) } EXPORT_SYMBOL(tcp_init_sock); +void tcp_init_transfer(struct sock *sk, int bpf_op) +{ + struct inet_connection_sock *icsk = inet_csk(sk); + + tcp_mtup_init(sk); + icsk->icsk_af_ops->rebuild_header(sk); + tcp_init_metrics(sk); + tcp_call_bpf(sk, bpf_op); + tcp_init_congestion_control(sk); + tcp_init_buffer_space(sk); +} + static void tcp_tx_timestamp(struct sock *sk, u16 tsflags, struct sk_buff *skb) { if (tsflags && skb) { diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index de470e7e586f..29fff14d5a53 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -236,12 +236,7 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk, refcount_set(&req->rsk_refcnt, 2); /* Now finish processing the fastopen child socket. */ - inet_csk(child)->icsk_af_ops->rebuild_header(child); - tcp_init_congestion_control(child); - tcp_mtup_init(child); - tcp_init_metrics(child); - tcp_call_bpf(child, BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB); - tcp_init_buffer_space(child); + tcp_init_transfer(child, BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB); tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index db9bb46b5776..bd3a35f5dbf2 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5513,20 +5513,13 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) security_inet_conn_established(sk, skb); } - /* Make sure socket is routed, for correct metrics. */ - icsk->icsk_af_ops->rebuild_header(sk); - - tcp_init_metrics(sk); - tcp_call_bpf(sk, BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB); - tcp_init_congestion_control(sk); + tcp_init_transfer(sk, BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB); /* Prevent spurious tcp_cwnd_restart() on first data * packet. */ tp->lsndtime = tcp_jiffies32; - tcp_init_buffer_space(sk); - if (sock_flag(sk, SOCK_KEEPOPEN)) inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tp)); @@ -5693,7 +5686,6 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, if (tcp_is_sack(tp) && sysctl_tcp_fack) tcp_enable_fack(tp); - tcp_mtup_init(sk); tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); tcp_initialize_rcv_mss(sk); @@ -5920,14 +5912,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) inet_csk(sk)->icsk_retransmits = 0; reqsk_fastopen_remove(sk, req, false); } else { - /* Make sure socket is routed, for correct metrics. */ - icsk->icsk_af_ops->rebuild_header(sk); - tcp_call_bpf(sk, BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB); - tcp_init_congestion_control(sk); - - tcp_mtup_init(sk); + tcp_init_transfer(sk, BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB); tp->copied_seq = tp->rcv_nxt; - tcp_init_buffer_space(sk); } smp_mb(); tcp_set_state(sk, TCP_ESTABLISHED); @@ -5957,8 +5943,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) * are sent out. */ tcp_rearm_rto(sk); - } else - tcp_init_metrics(sk); + } if (!inet_csk(sk)->icsk_ca_ops->cong_control) tcp_update_pacing_rate(sk); -- cgit v1.2.3-59-g8ed1b From e2080072ed2d98a55ae69d95dea60ff7a17cddd5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 4 Oct 2017 12:59:58 -0700 Subject: tcp: new list for sent but unacked skbs for RACK recovery This patch adds a new queue (list) that tracks the sent but not yet acked or SACKed skbs for a TCP connection. The list is chronologically ordered by skb->skb_mstamp (the head is the oldest sent skb). This list will be used to optimize TCP Rack recovery, which checks an skb's timestamp to judge if it has been lost and needs to be retransmitted. Since TCP write queue is ordered by sequence instead of sent time, RACK has to scan over the write queue to catch all eligible packets to detect lost retransmission, and iterates through SACKed skbs repeatedly. Special cares for rare events: 1. TCP repair fakes skb transmission so the send queue needs adjusted 2. SACK reneging would require re-inserting SACKed skbs into the send queue. For now I believe it's not worth the complexity to make RACK work perfectly on SACK reneging, so we do nothing here. 3. Fast Open: currently for non-TFO, send-queue correctly queues the pure SYN packet. For TFO which queues a pure SYN and then a data packet, send-queue only queues the data packet but not the pure SYN due to the structure of TFO code. This is okay because the SYN receiver would never respond with a SACK on a missing SYN (i.e. SYN is never fast-retransmitted by SACK/RACK). In order to not grow sk_buff, we use an union for the new list and _skb_refdst/destructor fields. This is a bit complicated because we need to make sure _skb_refdst and destructor are properly zeroed before skb is cloned/copied at transmit, and before being freed. Signed-off-by: Eric Dumazet Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/skbuff.h | 11 +++++++++-- include/linux/tcp.h | 1 + include/net/tcp.h | 24 +++++++++++++++++++++++- net/ipv4/tcp.c | 2 ++ net/ipv4/tcp_input.c | 9 +++++++-- net/ipv4/tcp_minisocks.c | 1 + net/ipv4/tcp_output.c | 42 +++++++++++++++++++++++++++++++----------- 7 files changed, 74 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index ada821466e88..01a985937867 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -617,6 +617,7 @@ typedef unsigned char *sk_buff_data_t; * @nf_trace: netfilter packet trace flag * @protocol: Packet protocol from driver * @destructor: Destruct function + * @tcp_tsorted_anchor: list structure for TCP (tp->tsorted_sent_queue) * @_nfct: Associated connection, if any (with nfctinfo bits) * @nf_bridge: Saved data about a bridged frame - see br_netfilter.c * @skb_iif: ifindex of device we arrived on @@ -686,8 +687,14 @@ struct sk_buff { */ char cb[48] __aligned(8); - unsigned long _skb_refdst; - void (*destructor)(struct sk_buff *skb); + union { + struct { + unsigned long _skb_refdst; + void (*destructor)(struct sk_buff *skb); + }; + struct list_head tcp_tsorted_anchor; + }; + #ifdef CONFIG_XFRM struct sec_path *sp; #endif diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 4aa40ef02d32..1d2c44e09e31 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -191,6 +191,7 @@ struct tcp_sock { u32 tsoffset; /* timestamp offset */ struct list_head tsq_node; /* anchor in tsq_tasklet.head list */ + struct list_head tsorted_sent_queue; /* time-sorted sent but un-SACKed skbs */ u32 snd_wl1; /* Sequence for window update */ u32 snd_wnd; /* The window we expect to receive */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 426c2e986016..3b16f353b539 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1589,14 +1589,34 @@ enum tcp_chrono { void tcp_chrono_start(struct sock *sk, const enum tcp_chrono type); void tcp_chrono_stop(struct sock *sk, const enum tcp_chrono type); +/* This helper is needed, because skb->tcp_tsorted_anchor uses + * the same memory storage than skb->destructor/_skb_refdst + */ +static inline void tcp_skb_tsorted_anchor_cleanup(struct sk_buff *skb) +{ + skb->destructor = NULL; + skb->_skb_refdst = 0UL; +} + +#define tcp_skb_tsorted_save(skb) { \ + unsigned long _save = skb->_skb_refdst; \ + skb->_skb_refdst = 0UL; + +#define tcp_skb_tsorted_restore(skb) \ + skb->_skb_refdst = _save; \ +} + /* write queue abstraction */ static inline void tcp_write_queue_purge(struct sock *sk) { struct sk_buff *skb; tcp_chrono_stop(sk, TCP_CHRONO_BUSY); - while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) + while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { + tcp_skb_tsorted_anchor_cleanup(skb); sk_wmem_free_skb(sk, skb); + } + INIT_LIST_HEAD(&tcp_sk(sk)->tsorted_sent_queue); sk_mem_reclaim(sk); tcp_clear_all_retrans_hints(tcp_sk(sk)); } @@ -1711,6 +1731,8 @@ static inline void tcp_insert_write_queue_before(struct sk_buff *new, static inline void tcp_unlink_write_queue(struct sk_buff *skb, struct sock *sk) { + list_del(&skb->tcp_tsorted_anchor); + tcp_skb_tsorted_anchor_cleanup(skb); __skb_unlink(skb, &sk->sk_write_queue); } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c115e37ca608..8cf742fd4f99 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -415,6 +415,7 @@ void tcp_init_sock(struct sock *sk) tp->out_of_order_queue = RB_ROOT; tcp_init_xmit_timers(sk); INIT_LIST_HEAD(&tp->tsq_node); + INIT_LIST_HEAD(&tp->tsorted_sent_queue); icsk->icsk_rto = TCP_TIMEOUT_INIT; tp->mdev_us = jiffies_to_usecs(TCP_TIMEOUT_INIT); @@ -881,6 +882,7 @@ struct sk_buff *sk_stream_alloc_skb(struct sock *sk, int size, gfp_t gfp, * available to the caller, no more, no less. */ skb->reserved_tailroom = skb->end - skb->tail - size; + INIT_LIST_HEAD(&skb->tcp_tsorted_anchor); return skb; } __kfree_skb(skb); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index c5b8d61846c2..fb0d7ed84b94 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1593,6 +1593,8 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk, tcp_skb_pcount(skb), skb->skb_mstamp); tcp_rate_skb_delivered(sk, skb, state->rate); + if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED) + list_del_init(&skb->tcp_tsorted_anchor); if (!before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp))) @@ -3054,8 +3056,11 @@ static void tcp_ack_tstamp(struct sock *sk, struct sk_buff *skb, shinfo = skb_shinfo(skb); if (!before(shinfo->tskey, prior_snd_una) && - before(shinfo->tskey, tcp_sk(sk)->snd_una)) - __skb_tstamp_tx(skb, NULL, sk, SCM_TSTAMP_ACK); + before(shinfo->tskey, tcp_sk(sk)->snd_una)) { + tcp_skb_tsorted_save(skb) { + __skb_tstamp_tx(skb, NULL, sk, SCM_TSTAMP_ACK); + } tcp_skb_tsorted_restore(skb); + } } /* Remove acknowledged frames from the retransmission queue. If our packet diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 188a6f31356d..2341b9f857b6 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -446,6 +446,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, newtp->snd_nxt = newtp->snd_up = treq->snt_isn + 1; INIT_LIST_HEAD(&newtp->tsq_node); + INIT_LIST_HEAD(&newtp->tsorted_sent_queue); tcp_init_wl(newtp, treq->rcv_isn); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 0bc9e46a5369..8162e2880178 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -971,6 +971,12 @@ static void tcp_internal_pacing(struct sock *sk, const struct sk_buff *skb) HRTIMER_MODE_ABS_PINNED); } +static void tcp_update_skb_after_send(struct tcp_sock *tp, struct sk_buff *skb) +{ + skb->skb_mstamp = tp->tcp_mstamp; + list_move_tail(&skb->tcp_tsorted_anchor, &tp->tsorted_sent_queue); +} + /* This routine actually transmits TCP packets queued in by * tcp_do_sendmsg(). This is used by both the initial * transmission and possible later retransmissions. @@ -1003,10 +1009,14 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, TCP_SKB_CB(skb)->tx.in_flight = TCP_SKB_CB(skb)->end_seq - tp->snd_una; oskb = skb; - if (unlikely(skb_cloned(skb))) - skb = pskb_copy(skb, gfp_mask); - else - skb = skb_clone(skb, gfp_mask); + + tcp_skb_tsorted_save(oskb) { + if (unlikely(skb_cloned(oskb))) + skb = pskb_copy(oskb, gfp_mask); + else + skb = skb_clone(oskb, gfp_mask); + } tcp_skb_tsorted_restore(oskb); + if (unlikely(!skb)) return -ENOBUFS; } @@ -1127,7 +1137,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, err = net_xmit_eval(err); } if (!err && oskb) { - oskb->skb_mstamp = tp->tcp_mstamp; + tcp_update_skb_after_send(tp, oskb); tcp_rate_skb_sent(sk, oskb); } return err; @@ -1328,6 +1338,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, /* Link BUFF into the send queue. */ __skb_header_release(buff); tcp_insert_write_queue_after(skb, buff, sk); + list_add(&buff->tcp_tsorted_anchor, &skb->tcp_tsorted_anchor); return 0; } @@ -2260,7 +2271,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, if (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE) { /* "skb_mstamp" is used as a start point for the retransmit timer */ - skb->skb_mstamp = tp->tcp_mstamp; + tcp_update_skb_after_send(tp, skb); goto repair; /* Skip network transmission */ } @@ -2838,11 +2849,14 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs) skb_headroom(skb) >= 0xFFFF)) { struct sk_buff *nskb; - nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC); - err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) : - -ENOBUFS; + tcp_skb_tsorted_save(skb) { + nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC); + err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) : + -ENOBUFS; + } tcp_skb_tsorted_restore(skb); + if (!err) - skb->skb_mstamp = tp->tcp_mstamp; + tcp_update_skb_after_send(tp, skb); } else { err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); } @@ -3023,6 +3037,7 @@ coalesce: goto coalesce; return; } + INIT_LIST_HEAD(&skb->tcp_tsorted_anchor); skb_reserve(skb, MAX_TCP_HEADER); sk_forced_mem_schedule(sk, skb->truesize); /* FIN eats a sequence byte, write_seq advanced by tcp_queue_skb(). */ @@ -3078,9 +3093,14 @@ int tcp_send_synack(struct sock *sk) } if (!(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_ACK)) { if (skb_cloned(skb)) { - struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); + struct sk_buff *nskb; + + tcp_skb_tsorted_save(skb) { + nskb = skb_copy(skb, GFP_ATOMIC); + } tcp_skb_tsorted_restore(skb); if (!nskb) return -ENOMEM; + INIT_LIST_HEAD(&nskb->tcp_tsorted_anchor); tcp_unlink_write_queue(skb, sk); __skb_header_release(nskb); __tcp_add_write_queue_head(sk, nskb); -- cgit v1.2.3-59-g8ed1b From f0d2e86c1b9ecd9cbe967b94e2f0d197f30cb3e7 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 27 Sep 2017 10:35:31 +0200 Subject: drm/atomic: Remove unneeded null check for private objects It can be seen in drm_atomic_get_private_obj_state() that ptr will never be NULL, so skip the check for that case. Signed-off-by: Maarten Lankhorst Link: https://patchwork.freedesktop.org/patch/msgid/20170927083532.5756-1-maarten.lankhorst@linux.intel.com Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_atomic.c | 3 --- include/drm/drm_atomic.h | 9 +++------ 2 files changed, 3 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 366c56fe5f58..6c9c4a8e09af 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -182,9 +182,6 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) for (i = 0; i < state->num_private_objs; i++) { struct drm_private_obj *obj = state->private_objs[i].ptr; - if (!obj) - continue; - obj->funcs->atomic_destroy_state(obj, state->private_objs[i].state); state->private_objs[i].ptr = NULL; diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 5834580d75bc..6fae95f28e10 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -768,8 +768,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); ((obj) = (__state)->private_objs[__i].ptr, \ (old_obj_state) = (__state)->private_objs[__i].old_state, \ (new_obj_state) = (__state)->private_objs[__i].new_state, 1); \ - (__i)++) \ - for_each_if (obj) + (__i)++) /** * for_each_old_private_obj_in_state - iterate over all private objects in an atomic update @@ -787,8 +786,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i) < (__state)->num_private_objs && \ ((obj) = (__state)->private_objs[__i].ptr, \ (old_obj_state) = (__state)->private_objs[__i].old_state, 1); \ - (__i)++) \ - for_each_if (obj) + (__i)++) /** * for_each_new_private_obj_in_state - iterate over all private objects in an atomic update @@ -806,8 +804,7 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); (__i) < (__state)->num_private_objs && \ ((obj) = (__state)->private_objs[__i].ptr, \ (new_obj_state) = (__state)->private_objs[__i].new_state, 1); \ - (__i)++) \ - for_each_if (obj) + (__i)++) /** * drm_atomic_crtc_needs_modeset - compute combined modeset need -- cgit v1.2.3-59-g8ed1b From 331494eb51002d0ee99414e3918e06d5e9a3962d Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Wed, 27 Sep 2017 10:35:32 +0200 Subject: drm/atomic: Make atomic iterators less surprising Commit 669c9215afea ("drm/atomic: Make async plane update checks work as intended, v2.") assumed incorrectly that if only 1 plane is matched in the loop, the variables will be set to that plane. In reality we reset them to NULL every time a new plane was iterated. This behavior is surprising, so fix this by making the for loops only assign the variables on a match. When we have not added all the planes/crtc/connector to the state, and there's a few NULL ones after the last one we iterated, te assumption is broken that the pointers will hold the values from the last loop iteration, which holds true for all other for_each macros we're using. Except of course the iterator pointer itself, but that one really is entirely internal. Cc: Dmitry Osipenko Fixes: 669c9215afea ("drm/atomic: Make async plane update checks work as intended, v2.") Signed-off-by: Maarten Lankhorst Link: https://patchwork.freedesktop.org/patch/msgid/20170927083532.5756-2-maarten.lankhorst@linux.intel.com Reviewed-by: Daniel Vetter Tested-by: Dmitry Osipenko --- include/drm/drm_atomic.h | 85 ++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 6fae95f28e10..5afd6e364fb6 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -585,12 +585,12 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); */ #define for_each_oldnew_connector_in_state(__state, connector, old_connector_state, new_connector_state, __i) \ for ((__i) = 0; \ - (__i) < (__state)->num_connector && \ - ((connector) = (__state)->connectors[__i].ptr, \ - (old_connector_state) = (__state)->connectors[__i].old_state, \ - (new_connector_state) = (__state)->connectors[__i].new_state, 1); \ - (__i)++) \ - for_each_if (connector) + (__i) < (__state)->num_connector; \ + (__i)++) \ + for_each_if ((__state)->connectors[__i].ptr && \ + ((connector) = (__state)->connectors[__i].ptr, \ + (old_connector_state) = (__state)->connectors[__i].old_state, \ + (new_connector_state) = (__state)->connectors[__i].new_state, 1)) /** * for_each_old_connector_in_state - iterate over all connectors in an atomic update @@ -606,11 +606,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); */ #define for_each_old_connector_in_state(__state, connector, old_connector_state, __i) \ for ((__i) = 0; \ - (__i) < (__state)->num_connector && \ - ((connector) = (__state)->connectors[__i].ptr, \ - (old_connector_state) = (__state)->connectors[__i].old_state, 1); \ - (__i)++) \ - for_each_if (connector) + (__i) < (__state)->num_connector; \ + (__i)++) \ + for_each_if ((__state)->connectors[__i].ptr && \ + ((connector) = (__state)->connectors[__i].ptr, \ + (old_connector_state) = (__state)->connectors[__i].old_state, 1)) /** * for_each_new_connector_in_state - iterate over all connectors in an atomic update @@ -626,11 +626,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); */ #define for_each_new_connector_in_state(__state, connector, new_connector_state, __i) \ for ((__i) = 0; \ - (__i) < (__state)->num_connector && \ - ((connector) = (__state)->connectors[__i].ptr, \ - (new_connector_state) = (__state)->connectors[__i].new_state, 1); \ - (__i)++) \ - for_each_if (connector) + (__i) < (__state)->num_connector; \ + (__i)++) \ + for_each_if ((__state)->connectors[__i].ptr && \ + ((connector) = (__state)->connectors[__i].ptr, \ + (new_connector_state) = (__state)->connectors[__i].new_state, 1)) /** * for_each_oldnew_crtc_in_state - iterate over all CRTCs in an atomic update @@ -646,12 +646,12 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); */ #define for_each_oldnew_crtc_in_state(__state, crtc, old_crtc_state, new_crtc_state, __i) \ for ((__i) = 0; \ - (__i) < (__state)->dev->mode_config.num_crtc && \ - ((crtc) = (__state)->crtcs[__i].ptr, \ - (old_crtc_state) = (__state)->crtcs[__i].old_state, \ - (new_crtc_state) = (__state)->crtcs[__i].new_state, 1); \ + (__i) < (__state)->dev->mode_config.num_crtc; \ (__i)++) \ - for_each_if (crtc) + for_each_if ((__state)->crtcs[__i].ptr && \ + ((crtc) = (__state)->crtcs[__i].ptr, \ + (old_crtc_state) = (__state)->crtcs[__i].old_state, \ + (new_crtc_state) = (__state)->crtcs[__i].new_state, 1)) /** * for_each_old_crtc_in_state - iterate over all CRTCs in an atomic update @@ -666,11 +666,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); */ #define for_each_old_crtc_in_state(__state, crtc, old_crtc_state, __i) \ for ((__i) = 0; \ - (__i) < (__state)->dev->mode_config.num_crtc && \ - ((crtc) = (__state)->crtcs[__i].ptr, \ - (old_crtc_state) = (__state)->crtcs[__i].old_state, 1); \ + (__i) < (__state)->dev->mode_config.num_crtc; \ (__i)++) \ - for_each_if (crtc) + for_each_if ((__state)->crtcs[__i].ptr && \ + ((crtc) = (__state)->crtcs[__i].ptr, \ + (old_crtc_state) = (__state)->crtcs[__i].old_state, 1)) /** * for_each_new_crtc_in_state - iterate over all CRTCs in an atomic update @@ -685,11 +685,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); */ #define for_each_new_crtc_in_state(__state, crtc, new_crtc_state, __i) \ for ((__i) = 0; \ - (__i) < (__state)->dev->mode_config.num_crtc && \ - ((crtc) = (__state)->crtcs[__i].ptr, \ - (new_crtc_state) = (__state)->crtcs[__i].new_state, 1); \ + (__i) < (__state)->dev->mode_config.num_crtc; \ (__i)++) \ - for_each_if (crtc) + for_each_if ((__state)->crtcs[__i].ptr && \ + ((crtc) = (__state)->crtcs[__i].ptr, \ + (new_crtc_state) = (__state)->crtcs[__i].new_state, 1)) /** * for_each_oldnew_plane_in_state - iterate over all planes in an atomic update @@ -705,12 +705,12 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); */ #define for_each_oldnew_plane_in_state(__state, plane, old_plane_state, new_plane_state, __i) \ for ((__i) = 0; \ - (__i) < (__state)->dev->mode_config.num_total_plane && \ - ((plane) = (__state)->planes[__i].ptr, \ - (old_plane_state) = (__state)->planes[__i].old_state, \ - (new_plane_state) = (__state)->planes[__i].new_state, 1); \ + (__i) < (__state)->dev->mode_config.num_total_plane; \ (__i)++) \ - for_each_if (plane) + for_each_if ((__state)->planes[__i].ptr && \ + ((plane) = (__state)->planes[__i].ptr, \ + (old_plane_state) = (__state)->planes[__i].old_state,\ + (new_plane_state) = (__state)->planes[__i].new_state, 1)) /** * for_each_old_plane_in_state - iterate over all planes in an atomic update @@ -725,12 +725,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); */ #define for_each_old_plane_in_state(__state, plane, old_plane_state, __i) \ for ((__i) = 0; \ - (__i) < (__state)->dev->mode_config.num_total_plane && \ - ((plane) = (__state)->planes[__i].ptr, \ - (old_plane_state) = (__state)->planes[__i].old_state, 1); \ + (__i) < (__state)->dev->mode_config.num_total_plane; \ (__i)++) \ - for_each_if (plane) - + for_each_if ((__state)->planes[__i].ptr && \ + ((plane) = (__state)->planes[__i].ptr, \ + (old_plane_state) = (__state)->planes[__i].old_state, 1)) /** * for_each_new_plane_in_state - iterate over all planes in an atomic update * @__state: &struct drm_atomic_state pointer @@ -744,11 +743,11 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p); */ #define for_each_new_plane_in_state(__state, plane, new_plane_state, __i) \ for ((__i) = 0; \ - (__i) < (__state)->dev->mode_config.num_total_plane && \ - ((plane) = (__state)->planes[__i].ptr, \ - (new_plane_state) = (__state)->planes[__i].new_state, 1); \ + (__i) < (__state)->dev->mode_config.num_total_plane; \ (__i)++) \ - for_each_if (plane) + for_each_if ((__state)->planes[__i].ptr && \ + ((plane) = (__state)->planes[__i].ptr, \ + (new_plane_state) = (__state)->planes[__i].new_state, 1)) /** * for_each_oldnew_private_obj_in_state - iterate over all private objects in an atomic update -- cgit v1.2.3-59-g8ed1b From 822a4b673284672af697ccd66e8795f8a712a90d Mon Sep 17 00:00:00 2001 From: Joonas Lahtinen Date: Fri, 6 Oct 2017 13:45:59 +0300 Subject: drm/i915: Don't use BIT() in UAPI section Lets not introduce BIT() macro requirement for UAPI for now. Fixes: 3fd3a6ffe279 ("drm/i915: Simplify i915_reg_read_ioctl") Signed-off-by: Joonas Lahtinen Cc: Chris Wilson Reviewed-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20171006104559.17312-1-joonas.lahtinen@linux.intel.com --- include/uapi/drm/i915_drm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/drm/i915_drm.h b/include/uapi/drm/i915_drm.h index 7266b53191ee..125bde7d9504 100644 --- a/include/uapi/drm/i915_drm.h +++ b/include/uapi/drm/i915_drm.h @@ -1318,7 +1318,7 @@ struct drm_i915_reg_read { * be specified */ __u64 offset; -#define I915_REG_READ_8B_WA BIT(0) +#define I915_REG_READ_8B_WA (1ul << 0) __u64 val; /* Return value */ }; -- cgit v1.2.3-59-g8ed1b From ec154bf56b276a0bb36079a5d22a267b5f417801 Mon Sep 17 00:00:00 2001 From: Joerg Roedel Date: Fri, 6 Oct 2017 15:00:53 +0200 Subject: iommu/vt-d: Don't register bus-notifier under dmar_global_lock The notifier function will take the dmar_global_lock too, so lockdep complains about inverse locking order when the notifier is registered under the dmar_global_lock. Reported-by: Jan Kiszka Fixes: 59ce0515cdaf ('iommu/vt-d: Update DRHD/RMRR/ATSR device scope caches when PCI hotplug happens') Signed-off-by: Joerg Roedel --- drivers/iommu/dmar.c | 7 +++++-- drivers/iommu/intel-iommu.c | 10 ++++++++++ include/linux/dmar.h | 1 + 3 files changed, 16 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 57c920c1372d..1ea7cd537873 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -801,13 +801,16 @@ int __init dmar_dev_scope_init(void) dmar_free_pci_notify_info(info); } } - - bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb); } return dmar_dev_scope_status; } +void dmar_register_bus_notifier(void) +{ + bus_register_notifier(&pci_bus_type, &dmar_pci_bus_nb); +} + int __init dmar_table_init(void) { diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 6784a05dd6b2..934cef924461 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4752,6 +4752,16 @@ int __init intel_iommu_init(void) goto out_free_dmar; } + up_write(&dmar_global_lock); + + /* + * The bus notifier takes the dmar_global_lock, so lockdep will + * complain later when we register it under the lock. + */ + dmar_register_bus_notifier(); + + down_write(&dmar_global_lock); + if (no_iommu || dmar_disabled) { /* * We exit the function here to ensure IOMMU's remapping and diff --git a/include/linux/dmar.h b/include/linux/dmar.h index e8ffba1052d3..e2433bc50210 100644 --- a/include/linux/dmar.h +++ b/include/linux/dmar.h @@ -112,6 +112,7 @@ static inline bool dmar_rcu_check(void) extern int dmar_table_init(void); extern int dmar_dev_scope_init(void); +extern void dmar_register_bus_notifier(void); extern int dmar_parse_dev_scope(void *start, void *end, int *cnt, struct dmar_dev_scope **devices, u16 segment); extern void *dmar_alloc_dev_scope(void *start, void *end, int *cnt); -- cgit v1.2.3-59-g8ed1b From 775d3a35dc3e13de55ec0e061c59e36faa7dd7f0 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 6 Oct 2017 08:15:15 -0600 Subject: backing-dev: kill unused pdflush_proc_obsolete() After commit b35bd0d9f8a8, pdflush_proc_obsolete() is no longer used. Kill the function and declaration. Reported-by: Rakesh Pandit Signed-off-by: Jens Axboe --- include/linux/backing-dev.h | 2 -- mm/backing-dev.c | 20 -------------------- 2 files changed, 22 deletions(-) (limited to 'include') diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 157e950a70dc..872afa41abc2 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -172,8 +172,6 @@ static inline int wb_congested(struct bdi_writeback *wb, int cong_bits) long congestion_wait(int sync, long timeout); long wait_iff_congested(struct pglist_data *pgdat, int sync, long timeout); -int pdflush_proc_obsolete(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos); static inline bool bdi_cap_stable_pages_required(struct backing_dev_info *bdi) { diff --git a/mm/backing-dev.c b/mm/backing-dev.c index e19606bb41a0..74b52dfd5852 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -1072,23 +1072,3 @@ out: return ret; } EXPORT_SYMBOL(wait_iff_congested); - -int pdflush_proc_obsolete(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - char kbuf[] = "0\n"; - - if (*ppos || *lenp < sizeof(kbuf)) { - *lenp = 0; - return 0; - } - - if (copy_to_user(buffer, kbuf, sizeof(kbuf))) - return -EFAULT; - pr_warn_once("%s exported in /proc is scheduled for removal\n", - table->procname); - - *lenp = 2; - *ppos += *lenp; - return 2; -} -- cgit v1.2.3-59-g8ed1b From 4e64b1ed15e25b8dcc2819c6d43dab72eb0bea26 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Thu, 5 Oct 2017 23:46:14 -0700 Subject: net/ipv6: Convert icmpv6_push_pending_frames to void commit cc71b7b07119 ("net/ipv6: remove unused err variable on icmpv6_push_pending_frames") exposed icmpv6_push_pending_frames return value not being used. Remove now unnecessary int err declarations and uses. Miscellanea: o Remove unnecessary goto and out: labels o Realign arguments Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- include/net/ipv6.h | 4 ++-- net/ipv6/icmp.c | 43 ++++++++++++++++++------------------------- net/ipv6/ping.c | 5 ++--- 3 files changed, 22 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 6eac5cf8f1e6..3cda3b521c36 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -300,8 +300,8 @@ static inline void fl6_sock_release(struct ip6_flowlabel *fl) void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info); -int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, - struct icmp6hdr *thdr, int len); +void icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, + struct icmp6hdr *thdr, int len); int ip6_ra_control(struct sock *sk, int sel); diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index aeb49b4d8c7d..4e52d52a6752 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -250,15 +250,15 @@ static bool opt_unrec(struct sk_buff *skb, __u32 offset) return (*op & 0xC0) == 0x80; } -int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, - struct icmp6hdr *thdr, int len) +void icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, + struct icmp6hdr *thdr, int len) { struct sk_buff *skb; struct icmp6hdr *icmp6h; skb = skb_peek(&sk->sk_write_queue); if (!skb) - goto out; + return; icmp6h = icmp6_hdr(skb); memcpy(icmp6h, thdr, sizeof(struct icmp6hdr)); @@ -286,8 +286,6 @@ int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, tmp_csum); } ip6_push_pending_frames(sk); -out: - return 0; } struct icmpv6_msg { @@ -437,7 +435,6 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, int iif = 0; int addr_type = 0; int len; - int err = 0; u32 mark = IP6_REPLY_MARK(net, skb->mark); if ((u8 *)hdr < skb->head || @@ -574,17 +571,16 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, rcu_read_lock(); idev = __in6_dev_get(skb->dev); - err = ip6_append_data(sk, icmpv6_getfrag, &msg, - len + sizeof(struct icmp6hdr), - sizeof(struct icmp6hdr), - &ipc6, &fl6, (struct rt6_info *)dst, - MSG_DONTWAIT, &sockc_unused); - if (err) { + if (ip6_append_data(sk, icmpv6_getfrag, &msg, + len + sizeof(struct icmp6hdr), + sizeof(struct icmp6hdr), + &ipc6, &fl6, (struct rt6_info *)dst, + MSG_DONTWAIT, &sockc_unused)) { ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); ip6_flush_pending_frames(sk); } else { - err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, - len + sizeof(struct icmp6hdr)); + icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, + len + sizeof(struct icmp6hdr)); } rcu_read_unlock(); out_dst_release: @@ -681,7 +677,6 @@ static void icmpv6_echo_reply(struct sk_buff *skb) struct icmpv6_msg msg; struct dst_entry *dst; struct ipcm6_cookie ipc6; - int err = 0; u32 mark = IP6_REPLY_MARK(net, skb->mark); struct sockcm_cookie sockc_unused = {0}; @@ -718,8 +713,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) else if (!fl6.flowi6_oif) fl6.flowi6_oif = np->ucast_oif; - err = ip6_dst_lookup(net, sk, &dst, &fl6); - if (err) + if (ip6_dst_lookup(net, sk, &dst, &fl6)) goto out; dst = xfrm_lookup(net, dst, flowi6_to_flowi(&fl6), sk, 0); if (IS_ERR(dst)) @@ -736,17 +730,16 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ipc6.dontfrag = np->dontfrag; ipc6.opt = NULL; - err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr), - sizeof(struct icmp6hdr), &ipc6, &fl6, - (struct rt6_info *)dst, MSG_DONTWAIT, - &sockc_unused); - - if (err) { + if (ip6_append_data(sk, icmpv6_getfrag, &msg, + skb->len + sizeof(struct icmp6hdr), + sizeof(struct icmp6hdr), &ipc6, &fl6, + (struct rt6_info *)dst, MSG_DONTWAIT, + &sockc_unused)) { __ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); ip6_flush_pending_frames(sk); } else { - err = icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, - skb->len + sizeof(struct icmp6hdr)); + icmpv6_push_pending_frames(sk, &fl6, &tmp_hdr, + skb->len + sizeof(struct icmp6hdr)); } dst_release(dst); out: diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index ac826dd338ff..d12c55dad7d1 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -154,9 +154,8 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ICMP6_MIB_OUTERRORS); ip6_flush_pending_frames(sk); } else { - err = icmpv6_push_pending_frames(sk, &fl6, - (struct icmp6hdr *) &pfh.icmph, - len); + icmpv6_push_pending_frames(sk, &fl6, + (struct icmp6hdr *)&pfh.icmph, len); } release_sock(sk); -- cgit v1.2.3-59-g8ed1b From 878e832ade6fc315e6ea59d95824bbb0430c6e8d Mon Sep 17 00:00:00 2001 From: Al Viro Date: Fri, 6 Oct 2017 11:19:24 -0400 Subject: acct.h: get rid of detritus unused forward declarations of structs Signed-off-by: Al Viro --- include/linux/acct.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/acct.h b/include/linux/acct.h index dccc2d4fe7de..3912a45b8c89 100644 --- a/include/linux/acct.h +++ b/include/linux/acct.h @@ -19,9 +19,6 @@ #ifdef CONFIG_BSD_PROCESS_ACCT -struct vfsmount; -struct super_block; -struct pacct_struct; struct pid_namespace; extern int acct_parm[]; /* for sysctl */ extern void acct_collect(long exitcode, int group_dead); -- cgit v1.2.3-59-g8ed1b From 55edd1dad96b760c87f5b9e2c461ba551a625f44 Mon Sep 17 00:00:00 2001 From: David Lin Date: Wed, 13 Sep 2017 10:53:58 -0700 Subject: leds: Replace flags bit shift with BIT() macros This is for readability as well as to avoid checkpatch warnings when adding new bit flag information in the future. Signed-off-by: David Lin Signed-off-by: Jacek Anaszewski --- include/linux/leds.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/linux/leds.h b/include/linux/leds.h index bf6db4fe895b..5579c64c8fd6 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -40,16 +40,16 @@ struct led_classdev { int flags; /* Lower 16 bits reflect status */ -#define LED_SUSPENDED (1 << 0) -#define LED_UNREGISTERING (1 << 1) +#define LED_SUSPENDED BIT(0) +#define LED_UNREGISTERING BIT(1) /* Upper 16 bits reflect control information */ -#define LED_CORE_SUSPENDRESUME (1 << 16) -#define LED_SYSFS_DISABLE (1 << 17) -#define LED_DEV_CAP_FLASH (1 << 18) -#define LED_HW_PLUGGABLE (1 << 19) -#define LED_PANIC_INDICATOR (1 << 20) -#define LED_BRIGHT_HW_CHANGED (1 << 21) -#define LED_RETAIN_AT_SHUTDOWN (1 << 22) +#define LED_CORE_SUSPENDRESUME BIT(16) +#define LED_SYSFS_DISABLE BIT(17) +#define LED_DEV_CAP_FLASH BIT(18) +#define LED_HW_PLUGGABLE BIT(19) +#define LED_PANIC_INDICATOR BIT(20) +#define LED_BRIGHT_HW_CHANGED BIT(21) +#define LED_RETAIN_AT_SHUTDOWN BIT(22) /* set_brightness_work / blink_timer flags, atomic, private. */ unsigned long work_flags; -- cgit v1.2.3-59-g8ed1b From 1321fd2c010e7ccb474cda6b8793747c7a3a775b Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Tue, 12 Sep 2017 22:42:12 +0200 Subject: drm/syncobj: extract two helpers from drm_syncobj_create MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For amdgpu. drm_syncobj_create is renamed to drm_syncobj_create_as_handle, and new helpers drm_syncobj_create and drm_syncobj_get_handle are added. Reviewed-by: Dave Airlie Signed-off-by: Marek Olšák Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_syncobj.c | 49 +++++++++++++++++++++++++++++++++++++++---- include/drm/drm_syncobj.h | 4 ++++ 2 files changed, 49 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 0422b8c2c2e7..0bb174109b96 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -262,8 +262,14 @@ void drm_syncobj_free(struct kref *kref) } EXPORT_SYMBOL(drm_syncobj_free); -static int drm_syncobj_create(struct drm_file *file_private, - u32 *handle, uint32_t flags) +/** + * drm_syncobj_create - create a new syncobj + * @out_syncobj: returned syncobj + * @flags: DRM_SYNCOBJ_* flags + * @fence: if non-NULL, the syncobj will represent this fence + */ +int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, + struct dma_fence *fence) { int ret; struct drm_syncobj *syncobj; @@ -284,6 +290,25 @@ static int drm_syncobj_create(struct drm_file *file_private, } } + if (fence) + drm_syncobj_replace_fence(syncobj, fence); + + *out_syncobj = syncobj; + return 0; +} +EXPORT_SYMBOL(drm_syncobj_create); + +/** + * drm_syncobj_get_handle - get a handle from a syncobj + */ +int drm_syncobj_get_handle(struct drm_file *file_private, + struct drm_syncobj *syncobj, u32 *handle) +{ + int ret; + + /* take a reference to put in the idr */ + drm_syncobj_get(syncobj); + idr_preload(GFP_KERNEL); spin_lock(&file_private->syncobj_table_lock); ret = idr_alloc(&file_private->syncobj_idr, syncobj, 1, 0, GFP_NOWAIT); @@ -299,6 +324,22 @@ static int drm_syncobj_create(struct drm_file *file_private, *handle = ret; return 0; } +EXPORT_SYMBOL(drm_syncobj_get_handle); + +static int drm_syncobj_create_as_handle(struct drm_file *file_private, + u32 *handle, uint32_t flags) +{ + int ret; + struct drm_syncobj *syncobj; + + ret = drm_syncobj_create(&syncobj, flags, NULL); + if (ret) + return ret; + + ret = drm_syncobj_get_handle(file_private, syncobj, handle); + drm_syncobj_put(syncobj); + return ret; +} static int drm_syncobj_destroy(struct drm_file *file_private, u32 handle) @@ -522,8 +563,8 @@ drm_syncobj_create_ioctl(struct drm_device *dev, void *data, if (args->flags & ~DRM_SYNCOBJ_CREATE_SIGNALED) return -EINVAL; - return drm_syncobj_create(file_private, - &args->handle, args->flags); + return drm_syncobj_create_as_handle(file_private, + &args->handle, args->flags); } int diff --git a/include/drm/drm_syncobj.h b/include/drm/drm_syncobj.h index c00fee539822..e7f0035bb8f8 100644 --- a/include/drm/drm_syncobj.h +++ b/include/drm/drm_syncobj.h @@ -136,5 +136,9 @@ int drm_syncobj_find_fence(struct drm_file *file_private, u32 handle, struct dma_fence **fence); void drm_syncobj_free(struct kref *kref); +int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, + struct dma_fence *fence); +int drm_syncobj_get_handle(struct drm_file *file_private, + struct drm_syncobj *syncobj, u32 *handle); #endif -- cgit v1.2.3-59-g8ed1b From 684fd0af4732f373503c6af2973b4faa352db4e2 Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Tue, 12 Sep 2017 22:42:13 +0200 Subject: drm/syncobj: add a new helper drm_syncobj_get_fd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Dave Airlie Signed-off-by: Marek Olšák Signed-off-by: Alex Deucher --- drivers/gpu/drm/drm_syncobj.c | 33 +++++++++++++++++++-------------- include/drm/drm_syncobj.h | 1 + 2 files changed, 20 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c index 0bb174109b96..62adc7acafcc 100644 --- a/drivers/gpu/drm/drm_syncobj.c +++ b/drivers/gpu/drm/drm_syncobj.c @@ -386,33 +386,38 @@ static int drm_syncobj_alloc_file(struct drm_syncobj *syncobj) return 0; } -static int drm_syncobj_handle_to_fd(struct drm_file *file_private, - u32 handle, int *p_fd) +int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd) { - struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle); int ret; int fd; - if (!syncobj) - return -EINVAL; - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) { - drm_syncobj_put(syncobj); + if (fd < 0) return fd; - } if (!syncobj->file) { ret = drm_syncobj_alloc_file(syncobj); - if (ret) - goto out_put_fd; + if (ret) { + put_unused_fd(fd); + return ret; + } } fd_install(fd, syncobj->file); - drm_syncobj_put(syncobj); *p_fd = fd; return 0; -out_put_fd: - put_unused_fd(fd); +} +EXPORT_SYMBOL(drm_syncobj_get_fd); + +static int drm_syncobj_handle_to_fd(struct drm_file *file_private, + u32 handle, int *p_fd) +{ + struct drm_syncobj *syncobj = drm_syncobj_find(file_private, handle); + int ret; + + if (!syncobj) + return -EINVAL; + + ret = drm_syncobj_get_fd(syncobj, p_fd); drm_syncobj_put(syncobj); return ret; } diff --git a/include/drm/drm_syncobj.h b/include/drm/drm_syncobj.h index e7f0035bb8f8..43e2f382d2f0 100644 --- a/include/drm/drm_syncobj.h +++ b/include/drm/drm_syncobj.h @@ -140,5 +140,6 @@ int drm_syncobj_create(struct drm_syncobj **out_syncobj, uint32_t flags, struct dma_fence *fence); int drm_syncobj_get_handle(struct drm_file *file_private, struct drm_syncobj *syncobj, u32 *handle); +int drm_syncobj_get_fd(struct drm_syncobj *syncobj, int *p_fd); #endif -- cgit v1.2.3-59-g8ed1b From 7ca24cf2d2269bde25e21c02a77fe81995a081ae Mon Sep 17 00:00:00 2001 From: Marek Olšák Date: Tue, 12 Sep 2017 22:42:14 +0200 Subject: drm/amdgpu: add FENCE_TO_HANDLE ioctl that returns syncobj or sync_file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit for being able to convert an amdgpu fence into one of the handles. Mesa will use this. Reviewed-by: Dave Airlie Signed-off-by: Marek Olšák Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 61 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 3 +- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 1 + include/uapi/drm/amdgpu_drm.h | 16 +++++++++ 5 files changed, 82 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 0725ab940f49..a23b8af95319 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1311,6 +1311,8 @@ int amdgpu_gem_va_ioctl(struct drm_device *dev, void *data, int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); +int amdgpu_cs_fence_to_handle_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); int amdgpu_cs_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); int amdgpu_cs_wait_fences_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index c6a214f1e991..ab83dfcabb41 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -25,6 +25,7 @@ * Jerome Glisse */ #include +#include #include #include #include @@ -1330,6 +1331,66 @@ static struct dma_fence *amdgpu_cs_get_fence(struct amdgpu_device *adev, return fence; } +int amdgpu_cs_fence_to_handle_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct amdgpu_device *adev = dev->dev_private; + struct amdgpu_fpriv *fpriv = filp->driver_priv; + union drm_amdgpu_fence_to_handle *info = data; + struct dma_fence *fence; + struct drm_syncobj *syncobj; + struct sync_file *sync_file; + int fd, r; + + if (amdgpu_kms_vram_lost(adev, fpriv)) + return -ENODEV; + + fence = amdgpu_cs_get_fence(adev, filp, &info->in.fence); + if (IS_ERR(fence)) + return PTR_ERR(fence); + + switch (info->in.what) { + case AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ: + r = drm_syncobj_create(&syncobj, 0, fence); + dma_fence_put(fence); + if (r) + return r; + r = drm_syncobj_get_handle(filp, syncobj, &info->out.handle); + drm_syncobj_put(syncobj); + return r; + + case AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ_FD: + r = drm_syncobj_create(&syncobj, 0, fence); + dma_fence_put(fence); + if (r) + return r; + r = drm_syncobj_get_fd(syncobj, (int*)&info->out.handle); + drm_syncobj_put(syncobj); + return r; + + case AMDGPU_FENCE_TO_HANDLE_GET_SYNC_FILE_FD: + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + dma_fence_put(fence); + return fd; + } + + sync_file = sync_file_create(fence); + dma_fence_put(fence); + if (!sync_file) { + put_unused_fd(fd); + return -ENOMEM; + } + + fd_install(fd, sync_file->file); + info->out.handle = fd; + return 0; + + default: + return -EINVAL; + } +} + /** * amdgpu_cs_wait_all_fence - wait on all fences to signal * diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 9816f50a7498..ad02d3fbb44c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -70,9 +70,10 @@ * - 3.18.0 - Export gpu always on cu bitmap * - 3.19.0 - Add support for UVD MJPEG decode * - 3.20.0 - Add support for local BOs + * - 3.21.0 - Add DRM_AMDGPU_FENCE_TO_HANDLE ioctl */ #define KMS_DRIVER_MAJOR 3 -#define KMS_DRIVER_MINOR 20 +#define KMS_DRIVER_MINOR 21 #define KMS_DRIVER_PATCHLEVEL 0 int amdgpu_vram_limit = 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 4fd06f8d9768..51841259e23f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1024,6 +1024,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_VM, amdgpu_vm_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_BO_LIST, amdgpu_bo_list_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(AMDGPU_FENCE_TO_HANDLE, amdgpu_cs_fence_to_handle_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), /* KMS */ DRM_IOCTL_DEF_DRV(AMDGPU_GEM_MMAP, amdgpu_gem_mmap_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_GEM_WAIT_IDLE, amdgpu_gem_wait_idle_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index e055776f2f4c..4c6e8c482ee4 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -52,6 +52,7 @@ extern "C" { #define DRM_AMDGPU_GEM_USERPTR 0x11 #define DRM_AMDGPU_WAIT_FENCES 0x12 #define DRM_AMDGPU_VM 0x13 +#define DRM_AMDGPU_FENCE_TO_HANDLE 0x14 #define DRM_IOCTL_AMDGPU_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define DRM_IOCTL_AMDGPU_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) @@ -67,6 +68,7 @@ extern "C" { #define DRM_IOCTL_AMDGPU_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_USERPTR, struct drm_amdgpu_gem_userptr) #define DRM_IOCTL_AMDGPU_WAIT_FENCES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_FENCES, union drm_amdgpu_wait_fences) #define DRM_IOCTL_AMDGPU_VM DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_VM, union drm_amdgpu_vm) +#define DRM_IOCTL_AMDGPU_FENCE_TO_HANDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_FENCE_TO_HANDLE, union drm_amdgpu_fence_to_handle) #define AMDGPU_GEM_DOMAIN_CPU 0x1 #define AMDGPU_GEM_DOMAIN_GTT 0x2 @@ -515,6 +517,20 @@ struct drm_amdgpu_cs_chunk_sem { __u32 handle; }; +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ 0 +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ_FD 1 +#define AMDGPU_FENCE_TO_HANDLE_GET_SYNC_FILE_FD 2 + +union drm_amdgpu_fence_to_handle { + struct { + struct drm_amdgpu_fence fence; + __u32 what; + } in; + struct { + __u32 handle; + } out; +}; + struct drm_amdgpu_cs_chunk_data { union { struct drm_amdgpu_cs_chunk_ib ib_data; -- cgit v1.2.3-59-g8ed1b From f9ebec52b5f115a0b06cdabe3036b858bfbb588a Mon Sep 17 00:00:00 2001 From: Christian König Date: Mon, 18 Sep 2017 15:45:11 +0200 Subject: drm/ttm: remove unsued options from ttm_mem_global_alloc_page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nobody is actually using that, remove it. Signed-off-by: Christian König Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/ttm/ttm_memory.c | 6 ++---- drivers/gpu/drm/ttm/ttm_page_alloc.c | 3 +-- drivers/gpu/drm/ttm/ttm_page_alloc_dma.c | 3 +-- include/drm/ttm/ttm_memory.h | 3 +-- 4 files changed, 5 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c index 29855be96be0..4f9978cbc5bc 100644 --- a/drivers/gpu/drm/ttm/ttm_memory.c +++ b/drivers/gpu/drm/ttm/ttm_memory.c @@ -546,8 +546,7 @@ int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, EXPORT_SYMBOL(ttm_mem_global_alloc); int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, - struct page *page, - bool no_wait, bool interruptible) + struct page *page) { struct ttm_mem_zone *zone = NULL; @@ -564,8 +563,7 @@ int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) zone = glob->zone_kernel; #endif - return ttm_mem_global_alloc_zone(glob, zone, PAGE_SIZE, no_wait, - interruptible); + return ttm_mem_global_alloc_zone(glob, zone, PAGE_SIZE, false, false); } void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page) diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index 052e1f102113..74f465ea914b 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -882,8 +882,7 @@ int ttm_pool_populate(struct ttm_tt *ttm) return -ENOMEM; } - ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i], - false, false); + ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i]); if (unlikely(ret != 0)) { ttm_pool_unpopulate(ttm); return -ENOMEM; diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c index 06bc14b55e66..b8905bdd4143 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c @@ -902,8 +902,7 @@ int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev) return -ENOMEM; } - ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i], - false, false); + ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i]); if (unlikely(ret != 0)) { ttm_dma_unpopulate(ttm_dma, dev); return -ENOMEM; diff --git a/include/drm/ttm/ttm_memory.h b/include/drm/ttm/ttm_memory.h index c4520890f267..8184ff144176 100644 --- a/include/drm/ttm/ttm_memory.h +++ b/include/drm/ttm/ttm_memory.h @@ -150,8 +150,7 @@ extern int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, extern void ttm_mem_global_free(struct ttm_mem_global *glob, uint64_t amount); extern int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, - struct page *page, - bool no_wait, bool interruptible); + struct page *page); extern void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page); extern size_t ttm_round_pot(size_t size); -- cgit v1.2.3-59-g8ed1b From d188bfa5532ce5b426681d8530ff1a9683eea0ad Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 4 Jul 2017 16:56:24 +0200 Subject: drm/ttm: add support for different pool sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Correctly handle different page sizes in the memory accounting. Signed-off-by: Christian König Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/ttm/ttm_memory.c | 10 +++++----- drivers/gpu/drm/ttm/ttm_page_alloc.c | 5 +++-- drivers/gpu/drm/ttm/ttm_page_alloc_dma.c | 7 ++++--- include/drm/ttm/ttm_memory.h | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c index 4f9978cbc5bc..e96374990398 100644 --- a/drivers/gpu/drm/ttm/ttm_memory.c +++ b/drivers/gpu/drm/ttm/ttm_memory.c @@ -546,7 +546,7 @@ int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, EXPORT_SYMBOL(ttm_mem_global_alloc); int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, - struct page *page) + struct page *page, uint64_t size) { struct ttm_mem_zone *zone = NULL; @@ -563,10 +563,11 @@ int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) zone = glob->zone_kernel; #endif - return ttm_mem_global_alloc_zone(glob, zone, PAGE_SIZE, false, false); + return ttm_mem_global_alloc_zone(glob, zone, size, false, false); } -void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page) +void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page, + uint64_t size) { struct ttm_mem_zone *zone = NULL; @@ -577,10 +578,9 @@ void ttm_mem_global_free_page(struct ttm_mem_global *glob, struct page *page) if (glob->zone_dma32 && page_to_pfn(page) > 0x00100000UL) zone = glob->zone_kernel; #endif - ttm_mem_global_free_zone(glob, zone, PAGE_SIZE); + ttm_mem_global_free_zone(glob, zone, size); } - size_t ttm_round_pot(size_t size) { if ((size & (size - 1)) == 0) diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c index 74f465ea914b..e11fd76e06f4 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c @@ -882,7 +882,8 @@ int ttm_pool_populate(struct ttm_tt *ttm) return -ENOMEM; } - ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i]); + ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i], + PAGE_SIZE); if (unlikely(ret != 0)) { ttm_pool_unpopulate(ttm); return -ENOMEM; @@ -909,7 +910,7 @@ void ttm_pool_unpopulate(struct ttm_tt *ttm) for (i = 0; i < ttm->num_pages; ++i) { if (ttm->pages[i]) { ttm_mem_global_free_page(ttm->glob->mem_glob, - ttm->pages[i]); + ttm->pages[i], PAGE_SIZE); ttm_put_pages(&ttm->pages[i], 1, ttm->page_flags, ttm->caching_state); diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c index b8905bdd4143..53626578004c 100644 --- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c +++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c @@ -902,7 +902,8 @@ int ttm_dma_populate(struct ttm_dma_tt *ttm_dma, struct device *dev) return -ENOMEM; } - ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i]); + ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i], + pool->size); if (unlikely(ret != 0)) { ttm_dma_unpopulate(ttm_dma, dev); return -ENOMEM; @@ -967,13 +968,13 @@ void ttm_dma_unpopulate(struct ttm_dma_tt *ttm_dma, struct device *dev) if (is_cached) { list_for_each_entry_safe(d_page, next, &ttm_dma->pages_list, page_list) { ttm_mem_global_free_page(ttm->glob->mem_glob, - d_page->p); + d_page->p, pool->size); ttm_dma_page_put(pool, d_page); } } else { for (i = 0; i < count; i++) { ttm_mem_global_free_page(ttm->glob->mem_glob, - ttm->pages[i]); + ttm->pages[i], pool->size); } } diff --git a/include/drm/ttm/ttm_memory.h b/include/drm/ttm/ttm_memory.h index 8184ff144176..2c1e3598effe 100644 --- a/include/drm/ttm/ttm_memory.h +++ b/include/drm/ttm/ttm_memory.h @@ -150,9 +150,9 @@ extern int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory, extern void ttm_mem_global_free(struct ttm_mem_global *glob, uint64_t amount); extern int ttm_mem_global_alloc_page(struct ttm_mem_global *glob, - struct page *page); + struct page *page, uint64_t size); extern void ttm_mem_global_free_page(struct ttm_mem_global *glob, - struct page *page); + struct page *page, uint64_t size); extern size_t ttm_round_pot(size_t size); extern uint64_t ttm_get_kernel_zone_memory_size(struct ttm_mem_global *glob); #endif -- cgit v1.2.3-59-g8ed1b From 18a4c0eab2623cc95be98a1e6af1ad18e7695977 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 5 Oct 2017 22:21:21 -0700 Subject: net: add rb_to_skb() and other rb tree helpers Geeralize private netem_rb_to_skb() TCP rtx queue will soon be converted to rb-tree, so we will need skb_rbtree_walk() helpers. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/skbuff.h | 18 ++++++++++++++++++ net/ipv4/tcp_fastopen.c | 8 +++----- net/ipv4/tcp_input.c | 33 ++++++++++++--------------------- net/sched/sch_netem.c | 14 ++++---------- 4 files changed, 37 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 01a985937867..03634ec2f918 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3158,6 +3158,12 @@ static inline int __skb_grow_rcsum(struct sk_buff *skb, unsigned int len) return __skb_grow(skb, len); } +#define rb_to_skb(rb) rb_entry_safe(rb, struct sk_buff, rbnode) +#define skb_rb_first(root) rb_to_skb(rb_first(root)) +#define skb_rb_last(root) rb_to_skb(rb_last(root)) +#define skb_rb_next(skb) rb_to_skb(rb_next(&(skb)->rbnode)) +#define skb_rb_prev(skb) rb_to_skb(rb_prev(&(skb)->rbnode)) + #define skb_queue_walk(queue, skb) \ for (skb = (queue)->next; \ skb != (struct sk_buff *)(queue); \ @@ -3172,6 +3178,18 @@ static inline int __skb_grow_rcsum(struct sk_buff *skb, unsigned int len) for (; skb != (struct sk_buff *)(queue); \ skb = skb->next) +#define skb_rbtree_walk(skb, root) \ + for (skb = skb_rb_first(root); skb != NULL; \ + skb = skb_rb_next(skb)) + +#define skb_rbtree_walk_from(skb) \ + for (; skb != NULL; \ + skb = skb_rb_next(skb)) + +#define skb_rbtree_walk_from_safe(skb, tmp) \ + for (; tmp = skb ? skb_rb_next(skb) : NULL, (skb != NULL); \ + skb = tmp) + #define skb_queue_walk_from_safe(queue, skb, tmp) \ for (tmp = skb->next; \ skb != (struct sk_buff *)(queue); \ diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 29fff14d5a53..7ee4aadcdd71 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -465,17 +465,15 @@ bool tcp_fastopen_active_should_disable(struct sock *sk) void tcp_fastopen_active_disable_ofo_check(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); - struct rb_node *p; - struct sk_buff *skb; struct dst_entry *dst; + struct sk_buff *skb; if (!tp->syn_fastopen) return; if (!tp->data_segs_in) { - p = rb_first(&tp->out_of_order_queue); - if (p && !rb_next(p)) { - skb = rb_entry(p, struct sk_buff, rbnode); + skb = skb_rb_first(&tp->out_of_order_queue); + if (skb && !skb_rb_next(skb)) { if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) { tcp_fastopen_active_disable(sk); return; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index fb0d7ed84b94..90afe4143596 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4335,7 +4335,7 @@ static void tcp_ofo_queue(struct sock *sk) p = rb_first(&tp->out_of_order_queue); while (p) { - skb = rb_entry(p, struct sk_buff, rbnode); + skb = rb_to_skb(p); if (after(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) break; @@ -4399,7 +4399,7 @@ static int tcp_try_rmem_schedule(struct sock *sk, struct sk_buff *skb, static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); - struct rb_node **p, *q, *parent; + struct rb_node **p, *parent; struct sk_buff *skb1; u32 seq, end_seq; bool fragstolen; @@ -4458,7 +4458,7 @@ coalesce_done: parent = NULL; while (*p) { parent = *p; - skb1 = rb_entry(parent, struct sk_buff, rbnode); + skb1 = rb_to_skb(parent); if (before(seq, TCP_SKB_CB(skb1)->seq)) { p = &parent->rb_left; continue; @@ -4503,9 +4503,7 @@ insert: merge_right: /* Remove other segments covered by skb. */ - while ((q = rb_next(&skb->rbnode)) != NULL) { - skb1 = rb_entry(q, struct sk_buff, rbnode); - + while ((skb1 = skb_rb_next(skb)) != NULL) { if (!after(end_seq, TCP_SKB_CB(skb1)->seq)) break; if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) { @@ -4520,7 +4518,7 @@ merge_right: tcp_drop(sk, skb1); } /* If there is no skb after us, we are the last_skb ! */ - if (!q) + if (!skb1) tp->ooo_last_skb = skb; add_sack: @@ -4706,7 +4704,7 @@ static struct sk_buff *tcp_skb_next(struct sk_buff *skb, struct sk_buff_head *li if (list) return !skb_queue_is_last(list, skb) ? skb->next : NULL; - return rb_entry_safe(rb_next(&skb->rbnode), struct sk_buff, rbnode); + return skb_rb_next(skb); } static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb, @@ -4735,7 +4733,7 @@ static void tcp_rbtree_insert(struct rb_root *root, struct sk_buff *skb) while (*p) { parent = *p; - skb1 = rb_entry(parent, struct sk_buff, rbnode); + skb1 = rb_to_skb(parent); if (before(TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb1)->seq)) p = &parent->rb_left; else @@ -4854,26 +4852,19 @@ static void tcp_collapse_ofo_queue(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb, *head; - struct rb_node *p; u32 start, end; - p = rb_first(&tp->out_of_order_queue); - skb = rb_entry_safe(p, struct sk_buff, rbnode); + skb = skb_rb_first(&tp->out_of_order_queue); new_range: if (!skb) { - p = rb_last(&tp->out_of_order_queue); - /* Note: This is possible p is NULL here. We do not - * use rb_entry_safe(), as ooo_last_skb is valid only - * if rbtree is not empty. - */ - tp->ooo_last_skb = rb_entry(p, struct sk_buff, rbnode); + tp->ooo_last_skb = skb_rb_last(&tp->out_of_order_queue); return; } start = TCP_SKB_CB(skb)->seq; end = TCP_SKB_CB(skb)->end_seq; for (head = skb;;) { - skb = tcp_skb_next(skb, NULL); + skb = skb_rb_next(skb); /* Range is terminated when we see a gap or when * we are at the queue end. @@ -4916,14 +4907,14 @@ static bool tcp_prune_ofo_queue(struct sock *sk) do { prev = rb_prev(node); rb_erase(node, &tp->out_of_order_queue); - tcp_drop(sk, rb_entry(node, struct sk_buff, rbnode)); + tcp_drop(sk, rb_to_skb(node)); sk_mem_reclaim(sk); if (atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf && !tcp_under_memory_pressure(sk)) break; node = prev; } while (node); - tp->ooo_last_skb = rb_entry(prev, struct sk_buff, rbnode); + tp->ooo_last_skb = rb_to_skb(prev); /* Reset SACK state. A conforming SACK implementation will * do the same at a timeout based retransmit. When a connection diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 5a4f10080290..db0228a65e8c 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -148,12 +148,6 @@ struct netem_skb_cb { psched_time_t time_to_send; }; - -static struct sk_buff *netem_rb_to_skb(struct rb_node *rb) -{ - return rb_entry(rb, struct sk_buff, rbnode); -} - static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb) { /* we assume we can use skb next/prev/tstamp as storage for rb_node */ @@ -364,7 +358,7 @@ static void tfifo_reset(struct Qdisc *sch) struct rb_node *p = rb_first(&q->t_root); while (p) { - struct sk_buff *skb = netem_rb_to_skb(p); + struct sk_buff *skb = rb_to_skb(p); p = rb_next(p); rb_erase(&skb->rbnode, &q->t_root); @@ -382,7 +376,7 @@ static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch) struct sk_buff *skb; parent = *p; - skb = netem_rb_to_skb(parent); + skb = rb_to_skb(parent); if (tnext >= netem_skb_cb(skb)->time_to_send) p = &parent->rb_right; else @@ -538,7 +532,7 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff *t_skb; struct netem_skb_cb *t_last; - t_skb = netem_rb_to_skb(rb_last(&q->t_root)); + t_skb = skb_rb_last(&q->t_root); t_last = netem_skb_cb(t_skb); if (!last || t_last->time_to_send > last->time_to_send) { @@ -617,7 +611,7 @@ deliver: if (p) { psched_time_t time_to_send; - skb = netem_rb_to_skb(p); + skb = rb_to_skb(p); /* if more time remaining? */ time_to_send = netem_skb_cb(skb)->time_to_send; -- cgit v1.2.3-59-g8ed1b From ac3f09ba3e496bd7cc780ead05b1d1bb5f33aedb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 5 Oct 2017 22:21:22 -0700 Subject: tcp: uninline tcp_write_queue_purge() Since the upcoming rtx rbtree will add some extra code, it is time to not inline this fat function anymore. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 15 +-------------- net/ipv4/tcp.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 3b16f353b539..744559b72784 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1606,20 +1606,7 @@ static inline void tcp_skb_tsorted_anchor_cleanup(struct sk_buff *skb) skb->_skb_refdst = _save; \ } -/* write queue abstraction */ -static inline void tcp_write_queue_purge(struct sock *sk) -{ - struct sk_buff *skb; - - tcp_chrono_stop(sk, TCP_CHRONO_BUSY); - while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { - tcp_skb_tsorted_anchor_cleanup(skb); - sk_wmem_free_skb(sk, skb); - } - INIT_LIST_HEAD(&tcp_sk(sk)->tsorted_sent_queue); - sk_mem_reclaim(sk); - tcp_clear_all_retrans_hints(tcp_sk(sk)); -} +void tcp_write_queue_purge(struct sock *sk); static inline struct sk_buff *tcp_write_queue_head(const struct sock *sk) { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8cf742fd4f99..f8ebae62f834 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2318,6 +2318,20 @@ static inline bool tcp_need_reset(int state) TCPF_FIN_WAIT2 | TCPF_SYN_RECV); } +void tcp_write_queue_purge(struct sock *sk) +{ + struct sk_buff *skb; + + tcp_chrono_stop(sk, TCP_CHRONO_BUSY); + while ((skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { + tcp_skb_tsorted_anchor_cleanup(skb); + sk_wmem_free_skb(sk, skb); + } + INIT_LIST_HEAD(&tcp_sk(sk)->tsorted_sent_queue); + sk_mem_reclaim(sk); + tcp_clear_all_retrans_hints(tcp_sk(sk)); +} + int tcp_disconnect(struct sock *sk, int flags) { struct inet_sock *inet = inet_sk(sk); -- cgit v1.2.3-59-g8ed1b From 75c119afe14f74b4dd967d75ed9f57ab6c0ef045 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 5 Oct 2017 22:21:27 -0700 Subject: tcp: implement rb-tree based retransmit queue Using a linear list to store all skbs in write queue has been okay for quite a while : O(N) is not too bad when N < 500. Things get messy when N is the order of 100,000 : Modern TCP stacks want 10Gbit+ of throughput even with 200 ms RTT flows. 40 ns per cache line miss means a full scan can use 4 ms, blowing away CPU caches. SACK processing often can use various hints to avoid parsing whole retransmit queue. But with high packet losses and/or high reordering, hints no longer work. Sender has to process thousands of unfriendly SACK, accumulating a huge socket backlog, burning a cpu and massively dropping packets. Using an rb-tree for retransmit queue has been avoided for years because it added complexity and overhead, but now is the time to be more resistant and say no to quadratic behavior. 1) RTX queue is no longer part of the write queue : already sent skbs are stored in one rb-tree. 2) Since reaching the head of write queue no longer needs sk->sk_send_head, we added an union of sk_send_head and tcp_rtx_queue Tested: On receiver : netem on ingress : delay 150ms 200us loss 1 GRO disabled to force stress and SACK storms. for f in `seq 1 10` do ./netperf -H lpaa6 -l30 -- -K bbr -o THROUGHPUT|tail -1 done | awk '{print $0} {sum += $0} END {printf "%7u\n",sum}' Before patch : 323.87 351.48 339.59 338.62 306.72 204.07 304.93 291.88 202.47 176.88 2840 After patch: 1700.83 2207.98 2070.17 1544.26 2114.76 2124.89 1693.14 1080.91 2216.82 1299.94 18053 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/sock.h | 7 ++- include/net/tcp.h | 89 ++++++++++++++++---------------- net/ipv4/tcp.c | 41 +++++++++++---- net/ipv4/tcp_input.c | 133 +++++++++++++++++++++++++----------------------- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_output.c | 137 +++++++++++++++++++++++++++----------------------- net/ipv4/tcp_timer.c | 24 +++++---- 7 files changed, 245 insertions(+), 188 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index a6b9a8d1a6df..4827094f1db4 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -60,7 +60,7 @@ #include #include #include - +#include #include #include #include @@ -397,7 +397,10 @@ struct sock { int sk_wmem_queued; refcount_t sk_wmem_alloc; unsigned long sk_tsq_flags; - struct sk_buff *sk_send_head; + union { + struct sk_buff *sk_send_head; + struct rb_root tcp_rtx_queue; + }; struct sk_buff_head sk_write_queue; __s32 sk_peek_off; int sk_write_pending; diff --git a/include/net/tcp.h b/include/net/tcp.h index 744559b72784..5a95e5886b55 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -551,7 +551,13 @@ void tcp_xmit_retransmit_queue(struct sock *); void tcp_simple_retransmit(struct sock *); void tcp_enter_recovery(struct sock *sk, bool ece_ack); int tcp_trim_head(struct sock *, struct sk_buff *, u32); -int tcp_fragment(struct sock *, struct sk_buff *, u32, unsigned int, gfp_t); +enum tcp_queue { + TCP_FRAG_IN_WRITE_QUEUE, + TCP_FRAG_IN_RTX_QUEUE, +}; +int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue, + struct sk_buff *skb, u32 len, + unsigned int mss_now, gfp_t gfp); void tcp_send_probe0(struct sock *); void tcp_send_partial(struct sock *); @@ -1608,6 +1614,11 @@ static inline void tcp_skb_tsorted_anchor_cleanup(struct sk_buff *skb) void tcp_write_queue_purge(struct sock *sk); +static inline struct sk_buff *tcp_rtx_queue_head(const struct sock *sk) +{ + return skb_rb_first(&sk->tcp_rtx_queue); +} + static inline struct sk_buff *tcp_write_queue_head(const struct sock *sk) { return skb_peek(&sk->sk_write_queue); @@ -1630,18 +1641,12 @@ static inline struct sk_buff *tcp_write_queue_prev(const struct sock *sk, return skb_queue_prev(&sk->sk_write_queue, skb); } -#define tcp_for_write_queue(skb, sk) \ - skb_queue_walk(&(sk)->sk_write_queue, skb) - -#define tcp_for_write_queue_from(skb, sk) \ - skb_queue_walk_from(&(sk)->sk_write_queue, skb) - #define tcp_for_write_queue_from_safe(skb, tmp, sk) \ skb_queue_walk_from_safe(&(sk)->sk_write_queue, skb, tmp) static inline struct sk_buff *tcp_send_head(const struct sock *sk) { - return sk->sk_send_head; + return skb_peek(&sk->sk_write_queue); } static inline bool tcp_skb_is_last(const struct sock *sk, @@ -1650,29 +1655,30 @@ static inline bool tcp_skb_is_last(const struct sock *sk, return skb_queue_is_last(&sk->sk_write_queue, skb); } -static inline void tcp_advance_send_head(struct sock *sk, const struct sk_buff *skb) +static inline bool tcp_write_queue_empty(const struct sock *sk) { - if (tcp_skb_is_last(sk, skb)) - sk->sk_send_head = NULL; - else - sk->sk_send_head = tcp_write_queue_next(sk, skb); + return skb_queue_empty(&sk->sk_write_queue); +} + +static inline bool tcp_rtx_queue_empty(const struct sock *sk) +{ + return RB_EMPTY_ROOT(&sk->tcp_rtx_queue); +} + +static inline bool tcp_rtx_and_write_queues_empty(const struct sock *sk) +{ + return tcp_rtx_queue_empty(sk) && tcp_write_queue_empty(sk); } static inline void tcp_check_send_head(struct sock *sk, struct sk_buff *skb_unlinked) { - if (sk->sk_send_head == skb_unlinked) { - sk->sk_send_head = NULL; + if (tcp_write_queue_empty(sk)) tcp_chrono_stop(sk, TCP_CHRONO_BUSY); - } + if (tcp_sk(sk)->highest_sack == skb_unlinked) tcp_sk(sk)->highest_sack = NULL; } -static inline void tcp_init_send_head(struct sock *sk) -{ - sk->sk_send_head = NULL; -} - static inline void __tcp_add_write_queue_tail(struct sock *sk, struct sk_buff *skb) { __skb_queue_tail(&sk->sk_write_queue, skb); @@ -1683,8 +1689,7 @@ static inline void tcp_add_write_queue_tail(struct sock *sk, struct sk_buff *skb __tcp_add_write_queue_tail(sk, skb); /* Queue it, remembering where we must start sending. */ - if (sk->sk_send_head == NULL) { - sk->sk_send_head = skb; + if (sk->sk_write_queue.next == skb) { tcp_chrono_start(sk, TCP_CHRONO_BUSY); if (tcp_sk(sk)->highest_sack == NULL) @@ -1697,35 +1702,32 @@ static inline void __tcp_add_write_queue_head(struct sock *sk, struct sk_buff *s __skb_queue_head(&sk->sk_write_queue, skb); } -/* Insert buff after skb on the write queue of sk. */ -static inline void tcp_insert_write_queue_after(struct sk_buff *skb, - struct sk_buff *buff, - struct sock *sk) -{ - __skb_queue_after(&sk->sk_write_queue, skb, buff); -} - /* Insert new before skb on the write queue of sk. */ static inline void tcp_insert_write_queue_before(struct sk_buff *new, struct sk_buff *skb, struct sock *sk) { __skb_queue_before(&sk->sk_write_queue, skb, new); - - if (sk->sk_send_head == skb) - sk->sk_send_head = new; } static inline void tcp_unlink_write_queue(struct sk_buff *skb, struct sock *sk) { - list_del(&skb->tcp_tsorted_anchor); - tcp_skb_tsorted_anchor_cleanup(skb); __skb_unlink(skb, &sk->sk_write_queue); } -static inline bool tcp_write_queue_empty(struct sock *sk) +void tcp_rbtree_insert(struct rb_root *root, struct sk_buff *skb); + +static inline void tcp_rtx_queue_unlink(struct sk_buff *skb, struct sock *sk) { - return skb_queue_empty(&sk->sk_write_queue); + tcp_skb_tsorted_anchor_cleanup(skb); + rb_erase(&skb->rbnode, &sk->tcp_rtx_queue); +} + +static inline void tcp_rtx_queue_unlink_and_free(struct sk_buff *skb, struct sock *sk) +{ + list_del(&skb->tcp_tsorted_anchor); + tcp_rtx_queue_unlink(skb, sk); + sk_wmem_free_skb(sk, skb); } static inline void tcp_push_pending_frames(struct sock *sk) @@ -1754,8 +1756,9 @@ static inline u32 tcp_highest_sack_seq(struct tcp_sock *tp) static inline void tcp_advance_highest_sack(struct sock *sk, struct sk_buff *skb) { - tcp_sk(sk)->highest_sack = tcp_skb_is_last(sk, skb) ? NULL : - tcp_write_queue_next(sk, skb); + struct sk_buff *next = skb_rb_next(skb); + + tcp_sk(sk)->highest_sack = next ?: tcp_send_head(sk); } static inline struct sk_buff *tcp_highest_sack(struct sock *sk) @@ -1765,7 +1768,9 @@ static inline struct sk_buff *tcp_highest_sack(struct sock *sk) static inline void tcp_highest_sack_reset(struct sock *sk) { - tcp_sk(sk)->highest_sack = tcp_write_queue_head(sk); + struct sk_buff *skb = tcp_rtx_queue_head(sk); + + tcp_sk(sk)->highest_sack = skb ?: tcp_send_head(sk); } /* Called when old skb is about to be deleted (to be combined with new skb) */ @@ -1935,7 +1940,7 @@ extern void tcp_rack_reo_timeout(struct sock *sk); /* At how many usecs into the future should the RTO fire? */ static inline s64 tcp_rto_delta_us(const struct sock *sk) { - const struct sk_buff *skb = tcp_write_queue_head(sk); + const struct sk_buff *skb = tcp_rtx_queue_head(sk); u32 rto = inet_csk(sk)->icsk_rto; u64 rto_time_stamp_us = skb->skb_mstamp + jiffies_to_usecs(rto); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index b8d379c80936..3b34850d361f 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -413,6 +413,7 @@ void tcp_init_sock(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); tp->out_of_order_queue = RB_ROOT; + sk->tcp_rtx_queue = RB_ROOT; tcp_init_xmit_timers(sk); INIT_LIST_HEAD(&tp->tsq_node); INIT_LIST_HEAD(&tp->tsorted_sent_queue); @@ -701,10 +702,9 @@ static void tcp_push(struct sock *sk, int flags, int mss_now, struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; - if (!tcp_send_head(sk)) - return; - skb = tcp_write_queue_tail(sk); + if (!skb) + return; if (!(flags & MSG_MORE) || forced_push(tp)) tcp_mark_push(tp, skb); @@ -964,14 +964,14 @@ ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset, int copy, i; bool can_coalesce; - if (!tcp_send_head(sk) || (copy = size_goal - skb->len) <= 0 || + if (!skb || (copy = size_goal - skb->len) <= 0 || !tcp_skb_can_collapse_to(skb)) { new_segment: if (!sk_stream_memory_free(sk)) goto wait_for_sndbuf; skb = sk_stream_alloc_skb(sk, 0, sk->sk_allocation, - skb_queue_empty(&sk->sk_write_queue)); + tcp_rtx_and_write_queues_empty(sk)); if (!skb) goto wait_for_memory; @@ -1199,7 +1199,7 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size) goto out_err; } - skb = tcp_send_head(sk) ? tcp_write_queue_tail(sk) : NULL; + skb = tcp_write_queue_tail(sk); uarg = sock_zerocopy_realloc(sk, size, skb_zcopy(skb)); if (!uarg) { err = -ENOBUFS; @@ -1275,7 +1275,7 @@ restart: int max = size_goal; skb = tcp_write_queue_tail(sk); - if (tcp_send_head(sk)) { + if (skb) { if (skb->ip_summed == CHECKSUM_NONE) max = mss_now; copy = max - skb->len; @@ -1295,7 +1295,7 @@ new_segment: process_backlog = false; goto restart; } - first_skb = skb_queue_empty(&sk->sk_write_queue); + first_skb = tcp_rtx_and_write_queues_empty(sk); skb = sk_stream_alloc_skb(sk, select_size(sk, sg, first_skb), sk->sk_allocation, @@ -1521,6 +1521,13 @@ static int tcp_peek_sndq(struct sock *sk, struct msghdr *msg, int len) /* XXX -- need to support SO_PEEK_OFF */ + skb_rbtree_walk(skb, &sk->tcp_rtx_queue) { + err = skb_copy_datagram_msg(skb, 0, msg, skb->len); + if (err) + return err; + copied += skb->len; + } + skb_queue_walk(&sk->sk_write_queue, skb) { err = skb_copy_datagram_msg(skb, 0, msg, skb->len); if (err) @@ -2320,6 +2327,22 @@ static inline bool tcp_need_reset(int state) TCPF_FIN_WAIT2 | TCPF_SYN_RECV); } +static void tcp_rtx_queue_purge(struct sock *sk) +{ + struct rb_node *p = rb_first(&sk->tcp_rtx_queue); + + while (p) { + struct sk_buff *skb = rb_to_skb(p); + + p = rb_next(p); + /* Since we are deleting whole queue, no need to + * list_del(&skb->tcp_tsorted_anchor) + */ + tcp_rtx_queue_unlink(skb, sk); + sk_wmem_free_skb(sk, skb); + } +} + void tcp_write_queue_purge(struct sock *sk) { struct sk_buff *skb; @@ -2329,6 +2352,7 @@ void tcp_write_queue_purge(struct sock *sk) tcp_skb_tsorted_anchor_cleanup(skb); sk_wmem_free_skb(sk, skb); } + tcp_rtx_queue_purge(sk); INIT_LIST_HEAD(&tcp_sk(sk)->tsorted_sent_queue); sk_mem_reclaim(sk); tcp_clear_all_retrans_hints(tcp_sk(sk)); @@ -2392,7 +2416,6 @@ int tcp_disconnect(struct sock *sk, int flags) * issue in __tcp_select_window() */ icsk->icsk_ack.rcv_mss = TCP_MIN_MSS; - tcp_init_send_head(sk); memset(&tp->rx_opt, 0, sizeof(tp->rx_opt)); __sk_dst_reset(sk); dst_release(sk->sk_rx_dst); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 72c4732ae2da..d0682ce2a5d6 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1142,6 +1142,7 @@ struct tcp_sacktag_state { u64 last_sackt; struct rate_sample *rate; int flag; + unsigned int mss_now; }; /* Check if skb is fully within the SACK block. In presence of GSO skbs, @@ -1191,7 +1192,8 @@ static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb, if (pkt_len >= skb->len && !in_sack) return 0; - err = tcp_fragment(sk, skb, pkt_len, mss, GFP_ATOMIC); + err = tcp_fragment(sk, TCP_FRAG_IN_RTX_QUEUE, skb, + pkt_len, mss, GFP_ATOMIC); if (err < 0) return err; } @@ -1363,8 +1365,7 @@ static bool tcp_shifted_skb(struct sock *sk, struct sk_buff *prev, if (unlikely(TCP_SKB_CB(prev)->tx.delivered_mstamp)) TCP_SKB_CB(prev)->tx.delivered_mstamp = 0; - tcp_unlink_write_queue(skb, sk); - sk_wmem_free_skb(sk, skb); + tcp_rtx_queue_unlink_and_free(skb, sk); NET_INC_STATS(sock_net(sk), LINUX_MIB_SACKMERGED); @@ -1414,9 +1415,9 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb, goto fallback; /* Can only happen with delayed DSACK + discard craziness */ - if (unlikely(skb == tcp_write_queue_head(sk))) + prev = skb_rb_prev(skb); + if (!prev) goto fallback; - prev = tcp_write_queue_prev(sk, skb); if ((TCP_SKB_CB(prev)->sacked & TCPCB_TAGBITS) != TCPCB_SACKED_ACKED) goto fallback; @@ -1501,12 +1502,11 @@ static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb, /* Hole filled allows collapsing with the next as well, this is very * useful when hole on every nth skb pattern happens */ - if (prev == tcp_write_queue_tail(sk)) + skb = skb_rb_next(prev); + if (!skb) goto out; - skb = tcp_write_queue_next(sk, prev); if (!skb_can_shift(skb) || - (skb == tcp_send_head(sk)) || ((TCP_SKB_CB(skb)->sacked & TCPCB_TAGBITS) != TCPCB_SACKED_ACKED) || (mss != tcp_skb_seglen(skb))) goto out; @@ -1539,13 +1539,10 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk, struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *tmp; - tcp_for_write_queue_from(skb, sk) { + skb_rbtree_walk_from(skb) { int in_sack = 0; bool dup_sack = dup_sack_in; - if (skb == tcp_send_head(sk)) - break; - /* queue is in-order => we can short-circuit the walk early */ if (!before(TCP_SKB_CB(skb)->seq, end_seq)) break; @@ -1607,23 +1604,44 @@ static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk, return skb; } -/* Avoid all extra work that is being done by sacktag while walking in - * a normal way - */ +static struct sk_buff *tcp_sacktag_bsearch(struct sock *sk, + struct tcp_sacktag_state *state, + u32 seq) +{ + struct rb_node *parent, **p = &sk->tcp_rtx_queue.rb_node; + struct sk_buff *skb; + int unack_bytes; + + while (*p) { + parent = *p; + skb = rb_to_skb(parent); + if (before(seq, TCP_SKB_CB(skb)->seq)) { + p = &parent->rb_left; + continue; + } + if (!before(seq, TCP_SKB_CB(skb)->end_seq)) { + p = &parent->rb_right; + continue; + } + + state->fack_count = 0; + unack_bytes = TCP_SKB_CB(skb)->seq - tcp_sk(sk)->snd_una; + if (state->mss_now && unack_bytes > 0) + state->fack_count = unack_bytes / state->mss_now; + + return skb; + } + return NULL; +} + static struct sk_buff *tcp_sacktag_skip(struct sk_buff *skb, struct sock *sk, struct tcp_sacktag_state *state, u32 skip_to_seq) { - tcp_for_write_queue_from(skb, sk) { - if (skb == tcp_send_head(sk)) - break; - - if (after(TCP_SKB_CB(skb)->end_seq, skip_to_seq)) - break; + if (skb && after(TCP_SKB_CB(skb)->seq, skip_to_seq)) + return skb; - state->fack_count += tcp_skb_pcount(skb); - } - return skb; + return tcp_sacktag_bsearch(sk, state, skip_to_seq); } static struct sk_buff *tcp_maybe_skipping_dsack(struct sk_buff *skb, @@ -1745,8 +1763,9 @@ tcp_sacktag_write_queue(struct sock *sk, const struct sk_buff *ack_skb, } } - skb = tcp_write_queue_head(sk); + state->mss_now = tcp_current_mss(sk); state->fack_count = 0; + skb = NULL; i = 0; if (!tp->sacked_out) { @@ -1970,7 +1989,7 @@ void tcp_enter_loss(struct sock *sk) if (tcp_is_reno(tp)) tcp_reset_reno_sack(tp); - skb = tcp_write_queue_head(sk); + skb = tcp_rtx_queue_head(sk); is_reneg = skb && (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED); if (is_reneg) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSACKRENEGING); @@ -1979,10 +1998,7 @@ void tcp_enter_loss(struct sock *sk) } tcp_clear_all_retrans_hints(tp); - tcp_for_write_queue(skb, sk) { - if (skb == tcp_send_head(sk)) - break; - + skb_rbtree_walk_from(skb) { mark_lost = (!(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED) || is_reneg); if (mark_lost) @@ -2215,13 +2231,11 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head) return; cnt = tp->lost_cnt_hint; } else { - skb = tcp_write_queue_head(sk); + skb = tcp_rtx_queue_head(sk); cnt = 0; } - tcp_for_write_queue_from(skb, sk) { - if (skb == tcp_send_head(sk)) - break; + skb_rbtree_walk_from(skb) { /* TODO: do this better */ /* this is not the most efficient way to do this... */ tp->lost_skb_hint = skb; @@ -2245,7 +2259,8 @@ static void tcp_mark_head_lost(struct sock *sk, int packets, int mark_head) /* If needed, chop off the prefix to mark as lost. */ lost = (packets - oldcnt) * mss; if (lost < skb->len && - tcp_fragment(sk, skb, lost, mss, GFP_ATOMIC) < 0) + tcp_fragment(sk, TCP_FRAG_IN_RTX_QUEUE, skb, + lost, mss, GFP_ATOMIC) < 0) break; cnt = packets; } @@ -2329,7 +2344,7 @@ static bool tcp_any_retrans_done(const struct sock *sk) if (tp->retrans_out) return true; - skb = tcp_write_queue_head(sk); + skb = tcp_rtx_queue_head(sk); if (unlikely(skb && TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS)) return true; @@ -2370,9 +2385,7 @@ static void tcp_undo_cwnd_reduction(struct sock *sk, bool unmark_loss) if (unmark_loss) { struct sk_buff *skb; - tcp_for_write_queue(skb, sk) { - if (skb == tcp_send_head(sk)) - break; + skb_rbtree_walk(skb, &sk->tcp_rtx_queue) { TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST; } tp->lost_out = 0; @@ -2617,9 +2630,7 @@ void tcp_simple_retransmit(struct sock *sk) unsigned int mss = tcp_current_mss(sk); u32 prior_lost = tp->lost_out; - tcp_for_write_queue(skb, sk) { - if (skb == tcp_send_head(sk)) - break; + skb_rbtree_walk(skb, &sk->tcp_rtx_queue) { if (tcp_skb_seglen(skb) > mss && !(TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED)) { if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_RETRANS) { @@ -2713,7 +2724,7 @@ static void tcp_process_loss(struct sock *sk, int flag, bool is_dupack, * is updated in tcp_ack()). Otherwise fall back to * the conventional recovery. */ - if (tcp_send_head(sk) && + if (!tcp_write_queue_empty(sk) && after(tcp_wnd_end(tp), tp->snd_nxt)) { *rexmit = REXMIT_NEW; return; @@ -3077,11 +3088,11 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, struct tcp_sock *tp = tcp_sk(sk); u32 prior_sacked = tp->sacked_out; u32 reord = tp->packets_out; + struct sk_buff *skb, *next; bool fully_acked = true; long sack_rtt_us = -1L; long seq_rtt_us = -1L; long ca_rtt_us = -1L; - struct sk_buff *skb; u32 pkts_acked = 0; u32 last_in_flight = 0; bool rtt_update; @@ -3089,7 +3100,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, first_ackt = 0; - while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) { + for (skb = skb_rb_first(&sk->tcp_rtx_queue); skb; skb = next) { struct tcp_skb_cb *scb = TCP_SKB_CB(skb); u8 sacked = scb->sacked; u32 acked_pcount; @@ -3107,8 +3118,6 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, break; fully_acked = false; } else { - /* Speedup tcp_unlink_write_queue() and next loop */ - prefetchw(skb->next); acked_pcount = tcp_skb_pcount(skb); } @@ -3160,12 +3169,12 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, if (!fully_acked) break; - tcp_unlink_write_queue(skb, sk); - sk_wmem_free_skb(sk, skb); + next = skb_rb_next(skb); if (unlikely(skb == tp->retransmit_skb_hint)) tp->retransmit_skb_hint = NULL; if (unlikely(skb == tp->lost_skb_hint)) tp->lost_skb_hint = NULL; + tcp_rtx_queue_unlink_and_free(skb, sk); } if (!skb) @@ -3257,12 +3266,14 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, static void tcp_ack_probe(struct sock *sk) { - const struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); + struct sk_buff *head = tcp_send_head(sk); + const struct tcp_sock *tp = tcp_sk(sk); /* Was it a usable window open? */ - - if (!after(TCP_SKB_CB(tcp_send_head(sk))->end_seq, tcp_wnd_end(tp))) { + if (!head) + return; + if (!after(TCP_SKB_CB(head)->end_seq, tcp_wnd_end(tp))) { icsk->icsk_backoff = 0; inet_csk_clear_xmit_timer(sk, ICSK_TIME_PROBE0); /* Socket must be waked up by subsequent tcp_data_snd_check(). @@ -3382,7 +3393,7 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32 tp->pred_flags = 0; tcp_fast_path_check(sk); - if (tcp_send_head(sk)) + if (!tcp_write_queue_empty(sk)) tcp_slow_start_after_idle_check(sk); if (nwin > tp->max_window) { @@ -3567,8 +3578,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) sack_state.first_sackt = 0; sack_state.rate = &rs; - /* We very likely will need to access write queue head. */ - prefetchw(sk->sk_write_queue.next); + /* We very likely will need to access rtx queue. */ + prefetch(sk->tcp_rtx_queue.rb_node); /* If the ack is older than previous acks * then we can probably ignore it. @@ -3682,8 +3693,7 @@ no_queue: * being used to time the probes, and is probably far higher than * it needs to be for normal retransmission. */ - if (tcp_send_head(sk)) - tcp_ack_probe(sk); + tcp_ack_probe(sk); if (tp->tlp_high_seq) tcp_process_tlp_ack(sk, ack, flag); @@ -4726,7 +4736,7 @@ static struct sk_buff *tcp_collapse_one(struct sock *sk, struct sk_buff *skb, } /* Insert skb into rb tree, ordered by TCP_SKB_CB(skb)->seq */ -static void tcp_rbtree_insert(struct rb_root *root, struct sk_buff *skb) +void tcp_rbtree_insert(struct rb_root *root, struct sk_buff *skb) { struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; @@ -5530,7 +5540,7 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, struct tcp_fastopen_cookie *cookie) { struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *data = tp->syn_data ? tcp_write_queue_head(sk) : NULL; + struct sk_buff *data = tp->syn_data ? tcp_rtx_queue_head(sk) : NULL; u16 mss = tp->rx_opt.mss_clamp, try_exp = 0; bool syn_drop = false; @@ -5565,9 +5575,8 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, tcp_fastopen_cache_set(sk, mss, cookie, syn_drop, try_exp); if (data) { /* Retransmit unacked data in SYN */ - tcp_for_write_queue_from(data, sk) { - if (data == tcp_send_head(sk) || - __tcp_retransmit_skb(sk, data, 1)) + skb_rbtree_walk_from(data) { + if (__tcp_retransmit_skb(sk, data, 1)) break; } tcp_rearm_rto(sk); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index c7460fd90884..5418ecf03b78 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -480,7 +480,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) TCP_TIMEOUT_INIT; icsk->icsk_rto = inet_csk_rto_backoff(icsk, TCP_RTO_MAX); - skb = tcp_write_queue_head(sk); + skb = tcp_rtx_queue_head(sk); BUG_ON(!skb); tcp_mstamp_refresh(tp); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 8162e2880178..696b0a168f16 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -66,15 +66,17 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp); /* Account for new data that has been sent to the network. */ -static void tcp_event_new_data_sent(struct sock *sk, const struct sk_buff *skb) +static void tcp_event_new_data_sent(struct sock *sk, struct sk_buff *skb) { struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); unsigned int prior_packets = tp->packets_out; - tcp_advance_send_head(sk, skb); tp->snd_nxt = TCP_SKB_CB(skb)->end_seq; + __skb_unlink(skb, &sk->sk_write_queue); + tcp_rbtree_insert(&sk->tcp_rtx_queue, skb); + tp->packets_out += tcp_skb_pcount(skb); if (!prior_packets || icsk->icsk_pending == ICSK_TIME_LOSS_PROBE) tcp_rearm_rto(sk); @@ -1249,12 +1251,25 @@ static void tcp_skb_fragment_eor(struct sk_buff *skb, struct sk_buff *skb2) TCP_SKB_CB(skb)->eor = 0; } +/* Insert buff after skb on the write or rtx queue of sk. */ +static void tcp_insert_write_queue_after(struct sk_buff *skb, + struct sk_buff *buff, + struct sock *sk, + enum tcp_queue tcp_queue) +{ + if (tcp_queue == TCP_FRAG_IN_WRITE_QUEUE) + __skb_queue_after(&sk->sk_write_queue, skb, buff); + else + tcp_rbtree_insert(&sk->tcp_rtx_queue, buff); +} + /* Function to create two new TCP segments. Shrinks the given segment * to the specified size and appends a new segment with the rest of the * packet to the list. This won't be called frequently, I hope. * Remember, these are still headerless SKBs at this point. */ -int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, +int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue, + struct sk_buff *skb, u32 len, unsigned int mss_now, gfp_t gfp) { struct tcp_sock *tp = tcp_sk(sk); @@ -1337,7 +1352,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len, /* Link BUFF into the send queue. */ __skb_header_release(buff); - tcp_insert_write_queue_after(skb, buff, sk); + tcp_insert_write_queue_after(skb, buff, sk, tcp_queue); list_add(&buff->tcp_tsorted_anchor, &skb->tcp_tsorted_anchor); return 0; @@ -1625,10 +1640,10 @@ static void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited) * is caused by insufficient sender buffer: * 1) just sent some data (see tcp_write_xmit) * 2) not cwnd limited (this else condition) - * 3) no more data to send (null tcp_send_head ) + * 3) no more data to send (tcp_write_queue_empty()) * 4) application is hitting buffer limit (SOCK_NOSPACE) */ - if (!tcp_send_head(sk) && sk->sk_socket && + if (tcp_write_queue_empty(sk) && sk->sk_socket && test_bit(SOCK_NOSPACE, &sk->sk_socket->flags) && (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) tcp_chrono_start(sk, TCP_CHRONO_SNDBUF_LIMITED); @@ -1824,7 +1839,8 @@ static bool tcp_snd_wnd_test(const struct tcp_sock *tp, * know that all the data is in scatter-gather pages, and that the * packet has never been sent out before (and thus is not cloned). */ -static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, +static int tso_fragment(struct sock *sk, enum tcp_queue tcp_queue, + struct sk_buff *skb, unsigned int len, unsigned int mss_now, gfp_t gfp) { struct sk_buff *buff; @@ -1833,7 +1849,7 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, /* All of a TSO frame must be composed of paged data. */ if (skb->len != skb->data_len) - return tcp_fragment(sk, skb, len, mss_now, gfp); + return tcp_fragment(sk, tcp_queue, skb, len, mss_now, gfp); buff = sk_stream_alloc_skb(sk, 0, gfp, true); if (unlikely(!buff)) @@ -1869,7 +1885,7 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len, /* Link BUFF into the send queue. */ __skb_header_release(buff); - tcp_insert_write_queue_after(skb, buff, sk); + tcp_insert_write_queue_after(skb, buff, sk, tcp_queue); return 0; } @@ -1939,8 +1955,10 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, goto send_now; } - head = tcp_write_queue_head(sk); - + /* TODO : use tsorted_sent_queue ? */ + head = tcp_rtx_queue_head(sk); + if (!head) + goto send_now; age = tcp_stamp_us_delta(tp->tcp_mstamp, head->skb_mstamp); /* If next ACK is likely to come too late (half srtt), do not defer */ if (age < (tp->srtt_us >> 4)) @@ -2158,13 +2176,12 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, limit <<= factor; if (refcount_read(&sk->sk_wmem_alloc) > limit) { - /* Always send the 1st or 2nd skb in write queue. + /* Always send skb if rtx queue is empty. * No need to wait for TX completion to call us back, * after softirq/tasklet schedule. * This helps when TX completions are delayed too much. */ - if (skb == sk->sk_write_queue.next || - skb->prev == sk->sk_write_queue.next) + if (tcp_rtx_queue_empty(sk)) return false; set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags); @@ -2215,7 +2232,7 @@ void tcp_chrono_stop(struct sock *sk, const enum tcp_chrono type) * it's the "most interesting" or current chrono we are * tracking and starts busy chrono if we have pending data. */ - if (tcp_write_queue_empty(sk)) + if (tcp_rtx_and_write_queues_empty(sk)) tcp_chrono_set(tp, TCP_CHRONO_UNSPEC); else if (type == tp->chrono_type) tcp_chrono_set(tp, TCP_CHRONO_BUSY); @@ -2310,7 +2327,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, nonagle); if (skb->len > limit && - unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) + unlikely(tso_fragment(sk, TCP_FRAG_IN_WRITE_QUEUE, + skb, limit, mss_now, gfp))) break; if (test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags)) @@ -2350,7 +2368,7 @@ repair: tcp_cwnd_validate(sk, is_cwnd_limited); return false; } - return !tp->packets_out && tcp_send_head(sk); + return !tp->packets_out && !tcp_write_queue_empty(sk); } bool tcp_schedule_loss_probe(struct sock *sk) @@ -2374,7 +2392,7 @@ bool tcp_schedule_loss_probe(struct sock *sk) return false; if ((tp->snd_cwnd > tcp_packets_in_flight(tp)) && - tcp_send_head(sk)) + !tcp_write_queue_empty(sk)) return false; /* Probe timeout is 2*rtt. Add minimum RTO to account @@ -2427,18 +2445,14 @@ void tcp_send_loss_probe(struct sock *sk) int mss = tcp_current_mss(sk); skb = tcp_send_head(sk); - if (skb) { - if (tcp_snd_wnd_test(tp, skb, mss)) { - pcount = tp->packets_out; - tcp_write_xmit(sk, mss, TCP_NAGLE_OFF, 2, GFP_ATOMIC); - if (tp->packets_out > pcount) - goto probe_sent; - goto rearm_timer; - } - skb = tcp_write_queue_prev(sk, skb); - } else { - skb = tcp_write_queue_tail(sk); + if (skb && tcp_snd_wnd_test(tp, skb, mss)) { + pcount = tp->packets_out; + tcp_write_xmit(sk, mss, TCP_NAGLE_OFF, 2, GFP_ATOMIC); + if (tp->packets_out > pcount) + goto probe_sent; + goto rearm_timer; } + skb = skb_rb_last(&sk->tcp_rtx_queue); /* At most one outstanding TLP retransmission. */ if (tp->tlp_high_seq) @@ -2456,10 +2470,11 @@ void tcp_send_loss_probe(struct sock *sk) goto rearm_timer; if ((pcount > 1) && (skb->len > (pcount - 1) * mss)) { - if (unlikely(tcp_fragment(sk, skb, (pcount - 1) * mss, mss, + if (unlikely(tcp_fragment(sk, TCP_FRAG_IN_RTX_QUEUE, skb, + (pcount - 1) * mss, mss, GFP_ATOMIC))) goto rearm_timer; - skb = tcp_write_queue_next(sk, skb); + skb = skb_rb_next(skb); } if (WARN_ON(!skb || !tcp_skb_pcount(skb))) @@ -2659,7 +2674,7 @@ void tcp_skb_collapse_tstamp(struct sk_buff *skb, static bool tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *next_skb = tcp_write_queue_next(sk, skb); + struct sk_buff *next_skb = skb_rb_next(skb); int skb_size, next_skb_size; skb_size = skb->len; @@ -2676,8 +2691,6 @@ static bool tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) } tcp_highest_sack_combine(sk, next_skb, skb); - tcp_unlink_write_queue(next_skb, sk); - if (next_skb->ip_summed == CHECKSUM_PARTIAL) skb->ip_summed = CHECKSUM_PARTIAL; @@ -2705,7 +2718,7 @@ static bool tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb) tcp_skb_collapse_tstamp(skb, next_skb); - sk_wmem_free_skb(sk, next_skb); + tcp_rtx_queue_unlink_and_free(next_skb, sk); return true; } @@ -2716,8 +2729,6 @@ static bool tcp_can_collapse(const struct sock *sk, const struct sk_buff *skb) return false; if (skb_cloned(skb)) return false; - if (skb == tcp_send_head(sk)) - return false; /* Some heuristics for collapsing over SACK'd could be invented */ if (TCP_SKB_CB(skb)->sacked & TCPCB_SACKED_ACKED) return false; @@ -2740,7 +2751,7 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to, if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN) return; - tcp_for_write_queue_from_safe(skb, tmp, sk) { + skb_rbtree_walk_from_safe(skb, tmp) { if (!tcp_can_collapse(sk, skb)) break; @@ -2815,7 +2826,8 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs) len = cur_mss * segs; if (skb->len > len) { - if (tcp_fragment(sk, skb, len, cur_mss, GFP_ATOMIC)) + if (tcp_fragment(sk, TCP_FRAG_IN_RTX_QUEUE, skb, len, + cur_mss, GFP_ATOMIC)) return -ENOMEM; /* We'll try again later. */ } else { if (skb_unclone(skb, GFP_ATOMIC)) @@ -2906,29 +2918,24 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs) void tcp_xmit_retransmit_queue(struct sock *sk) { const struct inet_connection_sock *icsk = inet_csk(sk); + struct sk_buff *skb, *rtx_head = NULL, *hole = NULL; struct tcp_sock *tp = tcp_sk(sk); - struct sk_buff *skb; - struct sk_buff *hole = NULL; u32 max_segs; int mib_idx; if (!tp->packets_out) return; - if (tp->retransmit_skb_hint) { - skb = tp->retransmit_skb_hint; - } else { - skb = tcp_write_queue_head(sk); + skb = tp->retransmit_skb_hint; + if (!skb) { + rtx_head = tcp_rtx_queue_head(sk); + skb = rtx_head; } - max_segs = tcp_tso_segs(sk, tcp_current_mss(sk)); - tcp_for_write_queue_from(skb, sk) { + skb_rbtree_walk_from(skb) { __u8 sacked; int segs; - if (skb == tcp_send_head(sk)) - break; - if (tcp_pacing_check(sk)) break; @@ -2973,7 +2980,7 @@ void tcp_xmit_retransmit_queue(struct sock *sk) if (tcp_in_cwnd_reduction(sk)) tp->prr_out += tcp_skb_pcount(skb); - if (skb == tcp_write_queue_head(sk) && + if (skb == rtx_head && icsk->icsk_pending != ICSK_TIME_REO_TIMEOUT) inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, inet_csk(sk)->icsk_rto, @@ -3015,12 +3022,15 @@ void tcp_send_fin(struct sock *sk) * Note: in the latter case, FIN packet will be sent after a timeout, * as TCP stack thinks it has already been transmitted. */ - if (tskb && (tcp_send_head(sk) || tcp_under_memory_pressure(sk))) { + if (!tskb && tcp_under_memory_pressure(sk)) + tskb = skb_rb_last(&sk->tcp_rtx_queue); + + if (tskb) { coalesce: TCP_SKB_CB(tskb)->tcp_flags |= TCPHDR_FIN; TCP_SKB_CB(tskb)->end_seq++; tp->write_seq++; - if (!tcp_send_head(sk)) { + if (tcp_write_queue_empty(sk)) { /* This means tskb was already sent. * Pretend we included the FIN on previous transmit. * We need to set tp->snd_nxt to the value it would have @@ -3086,9 +3096,9 @@ int tcp_send_synack(struct sock *sk) { struct sk_buff *skb; - skb = tcp_write_queue_head(sk); + skb = tcp_rtx_queue_head(sk); if (!skb || !(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN)) { - pr_debug("%s: wrong queue state\n", __func__); + pr_err("%s: wrong queue state\n", __func__); return -EFAULT; } if (!(TCP_SKB_CB(skb)->tcp_flags & TCPHDR_ACK)) { @@ -3101,10 +3111,9 @@ int tcp_send_synack(struct sock *sk) if (!nskb) return -ENOMEM; INIT_LIST_HEAD(&nskb->tcp_tsorted_anchor); - tcp_unlink_write_queue(skb, sk); + tcp_rtx_queue_unlink_and_free(skb, sk); __skb_header_release(nskb); - __tcp_add_write_queue_head(sk, nskb); - sk_wmem_free_skb(sk, skb); + tcp_rbtree_insert(&sk->tcp_rtx_queue, nskb); sk->sk_wmem_queued += nskb->truesize; sk_mem_charge(sk, nskb->truesize); skb = nskb; @@ -3327,7 +3336,6 @@ static void tcp_connect_queue_skb(struct sock *sk, struct sk_buff *skb) tcb->end_seq += skb->len; __skb_header_release(skb); - __tcp_add_write_queue_tail(sk, skb); sk->sk_wmem_queued += skb->truesize; sk_mem_charge(sk, skb->truesize); tp->write_seq = tcb->end_seq; @@ -3405,12 +3413,13 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn) TCP_SKB_CB(syn_data)->tcp_flags = TCPHDR_ACK | TCPHDR_PSH; if (!err) { tp->syn_data = (fo->copied > 0); + tcp_rbtree_insert(&sk->tcp_rtx_queue, syn_data); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPORIGDATASENT); goto done; } - /* data was not sent, this is our new send_head */ - sk->sk_send_head = syn_data; + /* data was not sent, put it in write_queue */ + __skb_queue_tail(&sk->sk_write_queue, syn_data); tp->packets_out -= tcp_skb_pcount(syn_data); fallback: @@ -3453,6 +3462,7 @@ int tcp_connect(struct sock *sk) tp->retrans_stamp = tcp_time_stamp(tp); tcp_connect_queue_skb(sk, buff); tcp_ecn_send_syn(sk, buff); + tcp_rbtree_insert(&sk->tcp_rtx_queue, buff); /* Send off SYN; include data in Fast Open. */ err = tp->fastopen_req ? tcp_send_syn_data(sk, buff) : @@ -3647,7 +3657,8 @@ int tcp_write_wakeup(struct sock *sk, int mib) skb->len > mss) { seg_size = min(seg_size, mss); TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_PSH; - if (tcp_fragment(sk, skb, seg_size, mss, GFP_ATOMIC)) + if (tcp_fragment(sk, TCP_FRAG_IN_WRITE_QUEUE, + skb, seg_size, mss, GFP_ATOMIC)) return -1; } else if (!tcp_skb_pcount(skb)) tcp_set_skb_tso_segs(skb, mss); @@ -3677,7 +3688,7 @@ void tcp_send_probe0(struct sock *sk) err = tcp_write_wakeup(sk, LINUX_MIB_TCPWINPROBE); - if (tp->packets_out || !tcp_send_head(sk)) { + if (tp->packets_out || tcp_write_queue_empty(sk)) { /* Cancel probe timer, if it is not required. */ icsk->icsk_probes_out = 0; icsk->icsk_backoff = 0; diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 655dd8d7f064..7014cc00c74c 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -156,8 +156,13 @@ static bool retransmits_timed_out(struct sock *sk, return false; start_ts = tcp_sk(sk)->retrans_stamp; - if (unlikely(!start_ts)) - start_ts = tcp_skb_timestamp(tcp_write_queue_head(sk)); + if (unlikely(!start_ts)) { + struct sk_buff *head = tcp_rtx_queue_head(sk); + + if (!head) + return false; + start_ts = tcp_skb_timestamp(head); + } if (likely(timeout == 0)) { linear_backoff_thresh = ilog2(TCP_RTO_MAX/rto_base); @@ -304,11 +309,12 @@ static void tcp_delack_timer(unsigned long data) static void tcp_probe_timer(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); + struct sk_buff *skb = tcp_send_head(sk); struct tcp_sock *tp = tcp_sk(sk); int max_probes; u32 start_ts; - if (tp->packets_out || !tcp_send_head(sk)) { + if (tp->packets_out || !skb) { icsk->icsk_probes_out = 0; return; } @@ -321,9 +327,9 @@ static void tcp_probe_timer(struct sock *sk) * corresponding system limit. We also implement similar policy when * we use RTO to probe window in tcp_retransmit_timer(). */ - start_ts = tcp_skb_timestamp(tcp_send_head(sk)); + start_ts = tcp_skb_timestamp(skb); if (!start_ts) - tcp_send_head(sk)->skb_mstamp = tp->tcp_mstamp; + skb->skb_mstamp = tp->tcp_mstamp; else if (icsk->icsk_user_timeout && (s32)(tcp_time_stamp(tp) - start_ts) > jiffies_to_msecs(icsk->icsk_user_timeout)) @@ -408,7 +414,7 @@ void tcp_retransmit_timer(struct sock *sk) if (!tp->packets_out) goto out; - WARN_ON(tcp_write_queue_empty(sk)); + WARN_ON(tcp_rtx_queue_empty(sk)); tp->tlp_high_seq = 0; @@ -441,7 +447,7 @@ void tcp_retransmit_timer(struct sock *sk) goto out; } tcp_enter_loss(sk); - tcp_retransmit_skb(sk, tcp_write_queue_head(sk), 1); + tcp_retransmit_skb(sk, tcp_rtx_queue_head(sk), 1); __sk_dst_reset(sk); goto out_reset_timer; } @@ -473,7 +479,7 @@ void tcp_retransmit_timer(struct sock *sk) tcp_enter_loss(sk); - if (tcp_retransmit_skb(sk, tcp_write_queue_head(sk), 1) > 0) { + if (tcp_retransmit_skb(sk, tcp_rtx_queue_head(sk), 1) > 0) { /* Retransmission failed because of local congestion, * do not backoff. */ @@ -647,7 +653,7 @@ static void tcp_keepalive_timer (unsigned long data) elapsed = keepalive_time_when(tp); /* It is alive without keepalive 8) */ - if (tp->packets_out || tcp_send_head(sk)) + if (tp->packets_out || !tcp_write_queue_empty(sk)) goto resched; elapsed = keepalive_time_elapsed(tp); -- cgit v1.2.3-59-g8ed1b From cf5868c8a22dc2854b96e9569064bb92365549ca Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Fri, 8 Sep 2017 20:57:10 +0200 Subject: padata: ensure the reorder timer callback runs on the correct CPU The reorder timer function runs on the CPU where the timer interrupt was handled which is not necessarily one of the CPUs of the 'pcpu' CPU mask set. Ensure the padata_reorder() callback runs on the correct CPU, which is one in the 'pcpu' CPU mask set and, preferrably, the next expected one. Do so by comparing the current CPU with the expected target CPU. If they match, call padata_reorder() right away. If they differ, schedule a work item on the target CPU that does the padata_reorder() call for us. Signed-off-by: Mathias Krause Signed-off-by: Herbert Xu --- include/linux/padata.h | 2 ++ kernel/padata.c | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/padata.h b/include/linux/padata.h index 2f9c1f93b1ce..5c0175bbc179 100644 --- a/include/linux/padata.h +++ b/include/linux/padata.h @@ -85,6 +85,7 @@ struct padata_serial_queue { * @swork: work struct for serialization. * @pd: Backpointer to the internal control structure. * @work: work struct for parallelization. + * @reorder_work: work struct for reordering. * @num_obj: Number of objects that are processed by this cpu. * @cpu_index: Index of the cpu. */ @@ -93,6 +94,7 @@ struct padata_parallel_queue { struct padata_list reorder; struct parallel_data *pd; struct work_struct work; + struct work_struct reorder_work; atomic_t num_obj; int cpu_index; }; diff --git a/kernel/padata.c b/kernel/padata.c index 1b9b4bac4a9b..b4066147bce4 100644 --- a/kernel/padata.c +++ b/kernel/padata.c @@ -275,11 +275,51 @@ static void padata_reorder(struct parallel_data *pd) return; } +static void invoke_padata_reorder(struct work_struct *work) +{ + struct padata_parallel_queue *pqueue; + struct parallel_data *pd; + + local_bh_disable(); + pqueue = container_of(work, struct padata_parallel_queue, reorder_work); + pd = pqueue->pd; + padata_reorder(pd); + local_bh_enable(); +} + static void padata_reorder_timer(unsigned long arg) { struct parallel_data *pd = (struct parallel_data *)arg; + unsigned int weight; + int target_cpu, cpu; - padata_reorder(pd); + cpu = get_cpu(); + + /* We don't lock pd here to not interfere with parallel processing + * padata_reorder() calls on other CPUs. We just need any CPU out of + * the cpumask.pcpu set. It would be nice if it's the right one but + * it doesn't matter if we're off to the next one by using an outdated + * pd->processed value. + */ + weight = cpumask_weight(pd->cpumask.pcpu); + target_cpu = padata_index_to_cpu(pd, pd->processed % weight); + + /* ensure to call the reorder callback on the correct CPU */ + if (cpu != target_cpu) { + struct padata_parallel_queue *pqueue; + struct padata_instance *pinst; + + /* The timer function is serialized wrt itself -- no locking + * needed. + */ + pinst = pd->pinst; + pqueue = per_cpu_ptr(pd->pqueue, target_cpu); + queue_work_on(target_cpu, pinst->wq, &pqueue->reorder_work); + } else { + padata_reorder(pd); + } + + put_cpu(); } static void padata_serial_worker(struct work_struct *serial_work) @@ -399,6 +439,7 @@ static void padata_init_pqueues(struct parallel_data *pd) __padata_list_init(&pqueue->reorder); __padata_list_init(&pqueue->parallel); INIT_WORK(&pqueue->work, padata_parallel_worker); + INIT_WORK(&pqueue->reorder_work, invoke_padata_reorder); atomic_set(&pqueue->num_obj, 0); } } -- cgit v1.2.3-59-g8ed1b From 350ef88e7e922354f82a931897ad4a4ce6c686ff Mon Sep 17 00:00:00 2001 From: Mathias Krause Date: Fri, 8 Sep 2017 20:57:11 +0200 Subject: padata: ensure padata_do_serial() runs on the correct CPU If the algorithm we're parallelizing is asynchronous we might change CPUs between padata_do_parallel() and padata_do_serial(). However, we don't expect this to happen as we need to enqueue the padata object into the per-cpu reorder queue we took it from, i.e. the same-cpu's parallel queue. Ensure we're not switching CPUs for a given padata object by tracking the CPU within the padata object. If the serial callback gets called on the wrong CPU, defer invoking padata_reorder() via a kernel worker on the CPU we're expected to run on. Signed-off-by: Mathias Krause Signed-off-by: Herbert Xu --- include/linux/padata.h | 2 ++ kernel/padata.c | 20 +++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/padata.h b/include/linux/padata.h index 5c0175bbc179..5d13d25da2c8 100644 --- a/include/linux/padata.h +++ b/include/linux/padata.h @@ -37,6 +37,7 @@ * @list: List entry, to attach to the padata lists. * @pd: Pointer to the internal control structure. * @cb_cpu: Callback cpu for serializatioon. + * @cpu: Cpu for parallelization. * @seq_nr: Sequence number of the parallelized data object. * @info: Used to pass information from the parallel to the serial function. * @parallel: Parallel execution function. @@ -46,6 +47,7 @@ struct padata_priv { struct list_head list; struct parallel_data *pd; int cb_cpu; + int cpu; int info; void (*parallel)(struct padata_priv *padata); void (*serial)(struct padata_priv *padata); diff --git a/kernel/padata.c b/kernel/padata.c index b4066147bce4..f262c9a4e70a 100644 --- a/kernel/padata.c +++ b/kernel/padata.c @@ -131,6 +131,7 @@ int padata_do_parallel(struct padata_instance *pinst, padata->cb_cpu = cb_cpu; target_cpu = padata_cpu_hash(pd); + padata->cpu = target_cpu; queue = per_cpu_ptr(pd->pqueue, target_cpu); spin_lock(&queue->parallel.lock); @@ -363,10 +364,21 @@ void padata_do_serial(struct padata_priv *padata) int cpu; struct padata_parallel_queue *pqueue; struct parallel_data *pd; + int reorder_via_wq = 0; pd = padata->pd; cpu = get_cpu(); + + /* We need to run on the same CPU padata_do_parallel(.., padata, ..) + * was called on -- or, at least, enqueue the padata object into the + * correct per-cpu queue. + */ + if (cpu != padata->cpu) { + reorder_via_wq = 1; + cpu = padata->cpu; + } + pqueue = per_cpu_ptr(pd->pqueue, cpu); spin_lock(&pqueue->reorder.lock); @@ -376,7 +388,13 @@ void padata_do_serial(struct padata_priv *padata) put_cpu(); - padata_reorder(pd); + /* If we're running on the wrong CPU, call padata_reorder() via a + * kernel worker. + */ + if (reorder_via_wq) + queue_work_on(cpu, pd->pinst->wq, &pqueue->reorder_work); + else + padata_reorder(pd); } EXPORT_SYMBOL(padata_do_serial); -- cgit v1.2.3-59-g8ed1b From 703321b60b605b1f381f5dcf0bca42703b912e3e Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Fri, 6 Oct 2017 23:18:13 +0100 Subject: mm/shmem: introduce shmem_file_setup_with_mnt We are planning to use our own tmpfs mnt in i915 in place of the shm_mnt, such that we can control the mount options, in particular huge=, which we require to support huge-gtt-pages. So rather than roll our own version of __shmem_file_setup, it would be preferred if we could just give shmem our mnt, and let it do the rest. Signed-off-by: Matthew Auld Cc: Joonas Lahtinen Cc: Chris Wilson Cc: Dave Hansen Cc: Kirill A. Shutemov Cc: Hugh Dickins Cc: linux-mm@kvack.org Acked-by: Andrew Morton Acked-by: Kirill A. Shutemov Reviewed-by: Joonas Lahtinen Link: https://patchwork.freedesktop.org/patch/msgid/20171006145041.21673-2-matthew.auld@intel.com Signed-off-by: Chris Wilson Link: https://patchwork.freedesktop.org/patch/msgid/20171006221833.32439-1-chris@chris-wilson.co.uk --- include/linux/shmem_fs.h | 2 ++ mm/shmem.c | 30 ++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index b6c3540e07bc..0937d9a7d8fb 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -53,6 +53,8 @@ extern struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags); extern struct file *shmem_kernel_file_setup(const char *name, loff_t size, unsigned long flags); +extern struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt, + const char *name, loff_t size, unsigned long flags); extern int shmem_zero_setup(struct vm_area_struct *); extern unsigned long shmem_get_unmapped_area(struct file *, unsigned long addr, unsigned long len, unsigned long pgoff, unsigned long flags); diff --git a/mm/shmem.c b/mm/shmem.c index 07a1d22807be..3229d27503ec 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -4183,7 +4183,7 @@ static const struct dentry_operations anon_ops = { .d_dname = simple_dname }; -static struct file *__shmem_file_setup(const char *name, loff_t size, +static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name, loff_t size, unsigned long flags, unsigned int i_flags) { struct file *res; @@ -4192,8 +4192,8 @@ static struct file *__shmem_file_setup(const char *name, loff_t size, struct super_block *sb; struct qstr this; - if (IS_ERR(shm_mnt)) - return ERR_CAST(shm_mnt); + if (IS_ERR(mnt)) + return ERR_CAST(mnt); if (size < 0 || size > MAX_LFS_FILESIZE) return ERR_PTR(-EINVAL); @@ -4205,8 +4205,8 @@ static struct file *__shmem_file_setup(const char *name, loff_t size, this.name = name; this.len = strlen(name); this.hash = 0; /* will go */ - sb = shm_mnt->mnt_sb; - path.mnt = mntget(shm_mnt); + sb = mnt->mnt_sb; + path.mnt = mntget(mnt); path.dentry = d_alloc_pseudo(sb, &this); if (!path.dentry) goto put_memory; @@ -4251,7 +4251,7 @@ put_path: */ struct file *shmem_kernel_file_setup(const char *name, loff_t size, unsigned long flags) { - return __shmem_file_setup(name, size, flags, S_PRIVATE); + return __shmem_file_setup(shm_mnt, name, size, flags, S_PRIVATE); } /** @@ -4262,10 +4262,24 @@ struct file *shmem_kernel_file_setup(const char *name, loff_t size, unsigned lon */ struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags) { - return __shmem_file_setup(name, size, flags, 0); + return __shmem_file_setup(shm_mnt, name, size, flags, 0); } EXPORT_SYMBOL_GPL(shmem_file_setup); +/** + * shmem_file_setup_with_mnt - get an unlinked file living in tmpfs + * @mnt: the tmpfs mount where the file will be created + * @name: name for dentry (to be seen in /proc//maps + * @size: size to be set for the file + * @flags: VM_NORESERVE suppresses pre-accounting of the entire object size + */ +struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt, const char *name, + loff_t size, unsigned long flags) +{ + return __shmem_file_setup(mnt, name, size, flags, 0); +} +EXPORT_SYMBOL_GPL(shmem_file_setup_with_mnt); + /** * shmem_zero_setup - setup a shared anonymous mapping * @vma: the vma to be mmapped is prepared by do_mmap_pgoff @@ -4281,7 +4295,7 @@ int shmem_zero_setup(struct vm_area_struct *vma) * accessible to the user through its mapping, use S_PRIVATE flag to * bypass file security, in the same way as shmem_kernel_file_setup(). */ - file = __shmem_file_setup("dev/zero", size, vma->vm_flags, S_PRIVATE); + file = shmem_kernel_file_setup("dev/zero", size, vma->vm_flags); if (IS_ERR(file)) return PTR_ERR(file); -- cgit v1.2.3-59-g8ed1b From 180ca444b985c42948fa26abd278e616b5ce7eb2 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 6 Oct 2017 12:05:56 -0700 Subject: ipv6: introduce a new function fib6_update_sernum() This function takes a route as input and tries to update the sernum in the fib6_node this route is associated with. It will be used in later commit when adding a cached route into the exception table under that route. Signed-off-by: Wei Wang Signed-off-by: Martin KaFai Lau Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 2 ++ net/ipv6/ip6_fib.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index d060d711a624..152b7b14a5a5 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -358,6 +358,8 @@ void __net_exit fib6_notifier_exit(struct net *net); unsigned int fib6_tables_seq_read(struct net *net); int fib6_tables_dump(struct net *net, struct notifier_block *nb); +void fib6_update_sernum(struct rt6_info *rt); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES int fib6_rules_init(void); void fib6_rules_cleanup(void); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index e5308d7cbd75..0ba4fbb2f855 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -110,6 +110,20 @@ enum { FIB6_NO_SERNUM_CHANGE = 0, }; +void fib6_update_sernum(struct rt6_info *rt) +{ + struct fib6_table *table = rt->rt6i_table; + struct net *net = dev_net(rt->dst.dev); + struct fib6_node *fn; + + write_lock_bh(&table->tb6_lock); + fn = rcu_dereference_protected(rt->rt6i_node, + lockdep_is_held(&table->tb6_lock)); + if (fn) + fn->fn_sernum = fib6_new_sernum(net); + write_unlock_bh(&table->tb6_lock); +} + /* * Auxiliary address test functions for the radix tree. * -- cgit v1.2.3-59-g8ed1b From 35732d01fe311ec13c4e42936878b782b8e7ea85 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 6 Oct 2017 12:05:57 -0700 Subject: ipv6: introduce a hash table to store dst cache Add a hash table into struct rt6_info in order to store dst caches created by pmtu discovery and ip redirect in ipv6 routing code. APIs to add dst cache, delete dst cache, find dst cache and update dst cache in the hash table are implemented and will be used in later commits. This is a preparation work to move all cache routes into the exception table instead of getting inserted into the fib6 tree. Signed-off-by: Wei Wang Signed-off-by: Martin KaFai Lau Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 19 +++ include/net/ip6_route.h | 3 + net/ipv6/route.c | 341 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 363 insertions(+) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 152b7b14a5a5..c4864c1e8f13 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -98,6 +98,22 @@ struct rt6key { struct fib6_table; +struct rt6_exception_bucket { + struct hlist_head chain; + int depth; +}; + +struct rt6_exception { + struct hlist_node hlist; + struct rt6_info *rt6i; + unsigned long stamp; + struct rcu_head rcu; +}; + +#define FIB6_EXCEPTION_BUCKET_SIZE_SHIFT 10 +#define FIB6_EXCEPTION_BUCKET_SIZE (1 << FIB6_EXCEPTION_BUCKET_SIZE_SHIFT) +#define FIB6_MAX_DEPTH 5 + struct rt6_info { struct dst_entry dst; @@ -134,12 +150,15 @@ struct rt6_info { struct inet6_dev *rt6i_idev; struct rt6_info * __percpu *rt6i_pcpu; + struct rt6_exception_bucket __rcu *rt6i_exception_bucket; u32 rt6i_metric; u32 rt6i_pmtu; /* more non-fragment space at head required */ unsigned short rt6i_nfheader_len; u8 rt6i_protocol; + u8 exception_bucket_flushed:1, + unused:7; }; static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst) diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index ee96f402cb75..3315605f34c9 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -95,6 +95,9 @@ int ip6_route_add(struct fib6_config *cfg, struct netlink_ext_ack *extack); int ip6_ins_rt(struct rt6_info *); int ip6_del_rt(struct rt6_info *); +void rt6_flush_exceptions(struct rt6_info *rt); +int rt6_remove_exception_rt(struct rt6_info *rt); + static inline int ip6_route_get_saddr(struct net *net, struct rt6_info *rt, const struct in6_addr *daddr, unsigned int prefs, diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 26cc9f483b6d..dc5e70975966 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -104,6 +105,9 @@ static int rt6_fill_node(struct net *net, struct in6_addr *dst, struct in6_addr *src, int iif, int type, u32 portid, u32 seq, unsigned int flags); +static struct rt6_info *rt6_find_cached_rt(struct rt6_info *rt, + struct in6_addr *daddr, + struct in6_addr *saddr); #ifdef CONFIG_IPV6_ROUTE_INFO static struct rt6_info *rt6_add_route_info(struct net *net, @@ -392,6 +396,7 @@ EXPORT_SYMBOL(ip6_dst_alloc); static void ip6_dst_destroy(struct dst_entry *dst) { struct rt6_info *rt = (struct rt6_info *)dst; + struct rt6_exception_bucket *bucket; struct dst_entry *from = dst->from; struct inet6_dev *idev; @@ -404,6 +409,11 @@ static void ip6_dst_destroy(struct dst_entry *dst) rt->rt6i_idev = NULL; in6_dev_put(idev); } + bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, 1); + if (bucket) { + rt->rt6i_exception_bucket = NULL; + kfree(bucket); + } dst->from = NULL; dst_release(from); @@ -1091,6 +1101,337 @@ static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt) return pcpu_rt; } +/* exception hash table implementation + */ +static DEFINE_SPINLOCK(rt6_exception_lock); + +/* Remove rt6_ex from hash table and free the memory + * Caller must hold rt6_exception_lock + */ +static void rt6_remove_exception(struct rt6_exception_bucket *bucket, + struct rt6_exception *rt6_ex) +{ + if (!bucket || !rt6_ex) + return; + rt6_ex->rt6i->rt6i_node = NULL; + hlist_del_rcu(&rt6_ex->hlist); + rt6_release(rt6_ex->rt6i); + kfree_rcu(rt6_ex, rcu); + WARN_ON_ONCE(!bucket->depth); + bucket->depth--; +} + +/* Remove oldest rt6_ex in bucket and free the memory + * Caller must hold rt6_exception_lock + */ +static void rt6_exception_remove_oldest(struct rt6_exception_bucket *bucket) +{ + struct rt6_exception *rt6_ex, *oldest = NULL; + + if (!bucket) + return; + + hlist_for_each_entry(rt6_ex, &bucket->chain, hlist) { + if (!oldest || time_before(rt6_ex->stamp, oldest->stamp)) + oldest = rt6_ex; + } + rt6_remove_exception(bucket, oldest); +} + +static u32 rt6_exception_hash(const struct in6_addr *dst, + const struct in6_addr *src) +{ + static u32 seed __read_mostly; + u32 val; + + net_get_random_once(&seed, sizeof(seed)); + val = jhash(dst, sizeof(*dst), seed); + +#ifdef CONFIG_IPV6_SUBTREES + if (src) + val = jhash(src, sizeof(*src), val); +#endif + return hash_32(val, FIB6_EXCEPTION_BUCKET_SIZE_SHIFT); +} + +/* Helper function to find the cached rt in the hash table + * and update bucket pointer to point to the bucket for this + * (daddr, saddr) pair + * Caller must hold rt6_exception_lock + */ +static struct rt6_exception * +__rt6_find_exception_spinlock(struct rt6_exception_bucket **bucket, + const struct in6_addr *daddr, + const struct in6_addr *saddr) +{ + struct rt6_exception *rt6_ex; + u32 hval; + + if (!(*bucket) || !daddr) + return NULL; + + hval = rt6_exception_hash(daddr, saddr); + *bucket += hval; + + hlist_for_each_entry(rt6_ex, &(*bucket)->chain, hlist) { + struct rt6_info *rt6 = rt6_ex->rt6i; + bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr); + +#ifdef CONFIG_IPV6_SUBTREES + if (matched && saddr) + matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr); +#endif + if (matched) + return rt6_ex; + } + return NULL; +} + +/* Helper function to find the cached rt in the hash table + * and update bucket pointer to point to the bucket for this + * (daddr, saddr) pair + * Caller must hold rcu_read_lock() + */ +static struct rt6_exception * +__rt6_find_exception_rcu(struct rt6_exception_bucket **bucket, + const struct in6_addr *daddr, + const struct in6_addr *saddr) +{ + struct rt6_exception *rt6_ex; + u32 hval; + + WARN_ON_ONCE(!rcu_read_lock_held()); + + if (!(*bucket) || !daddr) + return NULL; + + hval = rt6_exception_hash(daddr, saddr); + *bucket += hval; + + hlist_for_each_entry_rcu(rt6_ex, &(*bucket)->chain, hlist) { + struct rt6_info *rt6 = rt6_ex->rt6i; + bool matched = ipv6_addr_equal(daddr, &rt6->rt6i_dst.addr); + +#ifdef CONFIG_IPV6_SUBTREES + if (matched && saddr) + matched = ipv6_addr_equal(saddr, &rt6->rt6i_src.addr); +#endif + if (matched) + return rt6_ex; + } + return NULL; +} + +static int rt6_insert_exception(struct rt6_info *nrt, + struct rt6_info *ort) +{ + struct rt6_exception_bucket *bucket; + struct in6_addr *src_key = NULL; + struct rt6_exception *rt6_ex; + int err = 0; + + /* ort can't be a cache or pcpu route */ + if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) + ort = (struct rt6_info *)ort->dst.from; + WARN_ON_ONCE(ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)); + + spin_lock_bh(&rt6_exception_lock); + + if (ort->exception_bucket_flushed) { + err = -EINVAL; + goto out; + } + + bucket = rcu_dereference_protected(ort->rt6i_exception_bucket, + lockdep_is_held(&rt6_exception_lock)); + if (!bucket) { + bucket = kcalloc(FIB6_EXCEPTION_BUCKET_SIZE, sizeof(*bucket), + GFP_ATOMIC); + if (!bucket) { + err = -ENOMEM; + goto out; + } + rcu_assign_pointer(ort->rt6i_exception_bucket, bucket); + } + +#ifdef CONFIG_IPV6_SUBTREES + /* rt6i_src.plen != 0 indicates ort is in subtree + * and exception table is indexed by a hash of + * both rt6i_dst and rt6i_src. + * Otherwise, the exception table is indexed by + * a hash of only rt6i_dst. + */ + if (ort->rt6i_src.plen) + src_key = &nrt->rt6i_src.addr; +#endif + rt6_ex = __rt6_find_exception_spinlock(&bucket, &nrt->rt6i_dst.addr, + src_key); + if (rt6_ex) + rt6_remove_exception(bucket, rt6_ex); + + rt6_ex = kzalloc(sizeof(*rt6_ex), GFP_ATOMIC); + if (!rt6_ex) { + err = -ENOMEM; + goto out; + } + rt6_ex->rt6i = nrt; + rt6_ex->stamp = jiffies; + atomic_inc(&nrt->rt6i_ref); + nrt->rt6i_node = ort->rt6i_node; + hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain); + bucket->depth++; + + if (bucket->depth > FIB6_MAX_DEPTH) + rt6_exception_remove_oldest(bucket); + +out: + spin_unlock_bh(&rt6_exception_lock); + + /* Update fn->fn_sernum to invalidate all cached dst */ + if (!err) + fib6_update_sernum(ort); + + return err; +} + +void rt6_flush_exceptions(struct rt6_info *rt) +{ + struct rt6_exception_bucket *bucket; + struct rt6_exception *rt6_ex; + struct hlist_node *tmp; + int i; + + spin_lock_bh(&rt6_exception_lock); + /* Prevent rt6_insert_exception() to recreate the bucket list */ + rt->exception_bucket_flushed = 1; + + bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, + lockdep_is_held(&rt6_exception_lock)); + if (!bucket) + goto out; + + for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { + hlist_for_each_entry_safe(rt6_ex, tmp, &bucket->chain, hlist) + rt6_remove_exception(bucket, rt6_ex); + WARN_ON_ONCE(bucket->depth); + bucket++; + } + +out: + spin_unlock_bh(&rt6_exception_lock); +} + +/* Find cached rt in the hash table inside passed in rt + * Caller has to hold rcu_read_lock() + */ +static struct rt6_info *rt6_find_cached_rt(struct rt6_info *rt, + struct in6_addr *daddr, + struct in6_addr *saddr) +{ + struct rt6_exception_bucket *bucket; + struct in6_addr *src_key = NULL; + struct rt6_exception *rt6_ex; + struct rt6_info *res = NULL; + + bucket = rcu_dereference(rt->rt6i_exception_bucket); + +#ifdef CONFIG_IPV6_SUBTREES + /* rt6i_src.plen != 0 indicates rt is in subtree + * and exception table is indexed by a hash of + * both rt6i_dst and rt6i_src. + * Otherwise, the exception table is indexed by + * a hash of only rt6i_dst. + */ + if (rt->rt6i_src.plen) + src_key = saddr; +#endif + rt6_ex = __rt6_find_exception_rcu(&bucket, daddr, src_key); + + if (rt6_ex && !rt6_check_expired(rt6_ex->rt6i)) + res = rt6_ex->rt6i; + + return res; +} + +/* Remove the passed in cached rt from the hash table that contains it */ +int rt6_remove_exception_rt(struct rt6_info *rt) +{ + struct rt6_info *from = (struct rt6_info *)rt->dst.from; + struct rt6_exception_bucket *bucket; + struct in6_addr *src_key = NULL; + struct rt6_exception *rt6_ex; + int err; + + if (!from || + !(rt->rt6i_flags | RTF_CACHE)) + return -EINVAL; + + if (!rcu_access_pointer(from->rt6i_exception_bucket)) + return -ENOENT; + + spin_lock_bh(&rt6_exception_lock); + bucket = rcu_dereference_protected(from->rt6i_exception_bucket, + lockdep_is_held(&rt6_exception_lock)); +#ifdef CONFIG_IPV6_SUBTREES + /* rt6i_src.plen != 0 indicates 'from' is in subtree + * and exception table is indexed by a hash of + * both rt6i_dst and rt6i_src. + * Otherwise, the exception table is indexed by + * a hash of only rt6i_dst. + */ + if (from->rt6i_src.plen) + src_key = &rt->rt6i_src.addr; +#endif + rt6_ex = __rt6_find_exception_spinlock(&bucket, + &rt->rt6i_dst.addr, + src_key); + if (rt6_ex) { + rt6_remove_exception(bucket, rt6_ex); + err = 0; + } else { + err = -ENOENT; + } + + spin_unlock_bh(&rt6_exception_lock); + return err; +} + +/* Find rt6_ex which contains the passed in rt cache and + * refresh its stamp + */ +static void rt6_update_exception_stamp_rt(struct rt6_info *rt) +{ + struct rt6_info *from = (struct rt6_info *)rt->dst.from; + struct rt6_exception_bucket *bucket; + struct in6_addr *src_key = NULL; + struct rt6_exception *rt6_ex; + + if (!from || + !(rt->rt6i_flags | RTF_CACHE)) + return; + + rcu_read_lock(); + bucket = rcu_dereference(from->rt6i_exception_bucket); + +#ifdef CONFIG_IPV6_SUBTREES + /* rt6i_src.plen != 0 indicates 'from' is in subtree + * and exception table is indexed by a hash of + * both rt6i_dst and rt6i_src. + * Otherwise, the exception table is indexed by + * a hash of only rt6i_dst. + */ + if (from->rt6i_src.plen) + src_key = &rt->rt6i_src.addr; +#endif + rt6_ex = __rt6_find_exception_rcu(&bucket, + &rt->rt6i_dst.addr, + src_key); + if (rt6_ex) + rt6_ex->stamp = jiffies; + + rcu_read_unlock(); +} + struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, struct flowi6 *fl6, int flags) { -- cgit v1.2.3-59-g8ed1b From c757faa8bfa26a0dd24b41ff783e0da042156887 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 6 Oct 2017 12:06:01 -0700 Subject: ipv6: prepare fib6_age() for exception table If all dst cache entries are stored in the exception table under the main route, we have to go through them during fib6_age() when doing garbage collecting. Introduce a new function rt6_age_exception() which goes through all dst entries in the exception table and remove those entries that are expired. This function is called in fib6_age() so that all dst caches are also garbage collected. Signed-off-by: Wei Wang Signed-off-by: Martin KaFai Lau Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 13 +++++++++++ include/net/ip6_route.h | 2 ++ net/ipv6/ip6_fib.c | 26 ++++++++------------- net/ipv6/route.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index c4864c1e8f13..11a79ef87a28 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -29,6 +29,14 @@ #define FIB6_TABLE_HASHSZ 1 #endif +#define RT6_DEBUG 2 + +#if RT6_DEBUG >= 3 +#define RT6_TRACE(x...) pr_debug(x) +#else +#define RT6_TRACE(x...) do { ; } while (0) +#endif + struct rt6_info; struct fib6_config { @@ -75,6 +83,11 @@ struct fib6_node { struct rcu_head rcu; }; +struct fib6_gc_args { + int timeout; + int more; +}; + #ifndef CONFIG_IPV6_SUBTREES #define FIB6_SUBTREE(fn) NULL #else diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 3315605f34c9..a0087fb9864b 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -97,6 +97,8 @@ int ip6_del_rt(struct rt6_info *); void rt6_flush_exceptions(struct rt6_info *rt); int rt6_remove_exception_rt(struct rt6_info *rt); +void rt6_age_exceptions(struct rt6_info *rt, struct fib6_gc_args *gc_args, + unsigned long now); static inline int ip6_route_get_saddr(struct net *net, struct rt6_info *rt, const struct in6_addr *daddr, diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 0ba4fbb2f855..3afbe50f2779 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -38,14 +38,6 @@ #include #include -#define RT6_DEBUG 2 - -#if RT6_DEBUG >= 3 -#define RT6_TRACE(x...) pr_debug(x) -#else -#define RT6_TRACE(x...) do { ; } while (0) -#endif - static struct kmem_cache *fib6_node_kmem __read_mostly; struct fib6_cleaner { @@ -1890,12 +1882,6 @@ static void fib6_flush_trees(struct net *net) * Garbage collection */ -struct fib6_gc_args -{ - int timeout; - int more; -}; - static int fib6_age(struct rt6_info *rt, void *arg) { struct fib6_gc_args *gc_args = arg; @@ -1904,9 +1890,6 @@ static int fib6_age(struct rt6_info *rt, void *arg) /* * check addrconf expiration here. * Routes are expired even if they are in use. - * - * Also age clones. Note, that clones are aged out - * only if they are not in use now. */ if (rt->rt6i_flags & RTF_EXPIRES && rt->dst.expires) { @@ -1915,6 +1898,9 @@ static int fib6_age(struct rt6_info *rt, void *arg) return -1; } gc_args->more++; + /* The following part will soon be removed when the exception + * table is hooked up to store all cached routes. + */ } else if (rt->rt6i_flags & RTF_CACHE) { if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) rt->dst.obsolete = DST_OBSOLETE_KILL; @@ -1940,6 +1926,12 @@ static int fib6_age(struct rt6_info *rt, void *arg) gc_args->more++; } + /* Also age clones in the exception table. + * Note, that clones are aged out + * only if they are not in use now. + */ + rt6_age_exceptions(rt, gc_args, now); + return 0; } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index e8e901589564..d2dd55f58b5d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1528,6 +1528,66 @@ static void rt6_exceptions_clean_tohost(struct rt6_info *rt, spin_unlock_bh(&rt6_exception_lock); } +static void rt6_age_examine_exception(struct rt6_exception_bucket *bucket, + struct rt6_exception *rt6_ex, + struct fib6_gc_args *gc_args, + unsigned long now) +{ + struct rt6_info *rt = rt6_ex->rt6i; + + if (atomic_read(&rt->dst.__refcnt) == 1 && + time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) { + RT6_TRACE("aging clone %p\n", rt); + rt6_remove_exception(bucket, rt6_ex); + return; + } else if (rt->rt6i_flags & RTF_GATEWAY) { + struct neighbour *neigh; + __u8 neigh_flags = 0; + + neigh = dst_neigh_lookup(&rt->dst, &rt->rt6i_gateway); + if (neigh) { + neigh_flags = neigh->flags; + neigh_release(neigh); + } + if (!(neigh_flags & NTF_ROUTER)) { + RT6_TRACE("purging route %p via non-router but gateway\n", + rt); + rt6_remove_exception(bucket, rt6_ex); + return; + } + } + gc_args->more++; +} + +void rt6_age_exceptions(struct rt6_info *rt, + struct fib6_gc_args *gc_args, + unsigned long now) +{ + struct rt6_exception_bucket *bucket; + struct rt6_exception *rt6_ex; + struct hlist_node *tmp; + int i; + + if (!rcu_access_pointer(rt->rt6i_exception_bucket)) + return; + + spin_lock_bh(&rt6_exception_lock); + bucket = rcu_dereference_protected(rt->rt6i_exception_bucket, + lockdep_is_held(&rt6_exception_lock)); + + if (bucket) { + for (i = 0; i < FIB6_EXCEPTION_BUCKET_SIZE; i++) { + hlist_for_each_entry_safe(rt6_ex, tmp, + &bucket->chain, hlist) { + rt6_age_examine_exception(bucket, rt6_ex, + gc_args, now); + } + bucket++; + } + } + spin_unlock_bh(&rt6_exception_lock); +} + struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, struct flowi6 *fl6, int flags) { -- cgit v1.2.3-59-g8ed1b From 38fbeeeeccdb38d0635398e8e344d245f6d8dc52 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 6 Oct 2017 12:06:02 -0700 Subject: ipv6: prepare fib6_locate() for exception table fib6_locate() is used to find the fib6_node according to the passed in prefix address key. It currently tries to find the fib6_node with the exact match of the passed in key. However, when we move cached routes into the exception table, fib6_locate() will fail to find the fib6_node for it as the cached routes will be stored in the exception table under the fib6_node with the longest prefix match of the cache's dst addr key. This commit adds a new parameter to let the caller specify if it needs exact match or longest prefix match. Right now, all callers still does exact match when calling fib6_locate(). It will be changed in later commit where exception table is hooked up to store cached routes. Signed-off-by: Wei Wang Signed-off-by: Martin KaFai Lau Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 3 ++- net/ipv6/addrconf.c | 2 +- net/ipv6/ip6_fib.c | 30 +++++++++++++++++++++++------- net/ipv6/route.c | 5 +++-- 4 files changed, 29 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 11a79ef87a28..4497a1eb4d41 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -357,7 +357,8 @@ struct fib6_node *fib6_lookup(struct fib6_node *root, struct fib6_node *fib6_locate(struct fib6_node *root, const struct in6_addr *daddr, int dst_len, - const struct in6_addr *saddr, int src_len); + const struct in6_addr *saddr, int src_len, + bool exact_match); void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), void *arg); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 837418ff2d4b..3ccaf52824c9 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2322,7 +2322,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, return NULL; read_lock_bh(&table->tb6_lock); - fn = fib6_locate(&table->tb6_root, pfx, plen, NULL, 0); + fn = fib6_locate(&table->tb6_root, pfx, plen, NULL, 0, true); if (!fn) goto out; diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 3afbe50f2779..b3e4cf0962f8 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1343,14 +1343,21 @@ struct fib6_node *fib6_lookup(struct fib6_node *root, const struct in6_addr *dad /* * Get node with specified destination prefix (and source prefix, * if subtrees are used) + * exact_match == true means we try to find fn with exact match of + * the passed in prefix addr + * exact_match == false means we try to find fn with longest prefix + * match of the passed in prefix addr. This is useful for finding fn + * for cached route as it will be stored in the exception table under + * the node with longest prefix length. */ static struct fib6_node *fib6_locate_1(struct fib6_node *root, const struct in6_addr *addr, - int plen, int offset) + int plen, int offset, + bool exact_match) { - struct fib6_node *fn; + struct fib6_node *fn, *prev = NULL; for (fn = root; fn ; ) { struct rt6key *key = (struct rt6key *)((u8 *)fn->leaf + offset); @@ -1360,11 +1367,13 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root, */ if (plen < fn->fn_bit || !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit)) - return NULL; + goto out; if (plen == fn->fn_bit) return fn; + prev = fn; + /* * We have more bits to go */ @@ -1373,24 +1382,31 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root, else fn = fn->left; } - return NULL; +out: + if (exact_match) + return NULL; + else + return prev; } struct fib6_node *fib6_locate(struct fib6_node *root, const struct in6_addr *daddr, int dst_len, - const struct in6_addr *saddr, int src_len) + const struct in6_addr *saddr, int src_len, + bool exact_match) { struct fib6_node *fn; fn = fib6_locate_1(root, daddr, dst_len, - offsetof(struct rt6_info, rt6i_dst)); + offsetof(struct rt6_info, rt6i_dst), + exact_match); #ifdef CONFIG_IPV6_SUBTREES if (src_len) { WARN_ON(saddr == NULL); if (fn && fn->subtree) fn = fib6_locate_1(fn->subtree, saddr, src_len, - offsetof(struct rt6_info, rt6i_src)); + offsetof(struct rt6_info, rt6i_src), + exact_match); } #endif diff --git a/net/ipv6/route.c b/net/ipv6/route.c index d2dd55f58b5d..855b4ceec349 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2800,7 +2800,8 @@ static int ip6_route_del(struct fib6_config *cfg, fn = fib6_locate(&table->tb6_root, &cfg->fc_dst, cfg->fc_dst_len, - &cfg->fc_src, cfg->fc_src_len); + &cfg->fc_src, cfg->fc_src_len, + true); if (fn) { for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { @@ -3009,7 +3010,7 @@ static struct rt6_info *rt6_get_route_info(struct net *net, return NULL; read_lock_bh(&table->tb6_lock); - fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0); + fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true); if (!fn) goto out; -- cgit v1.2.3-59-g8ed1b From 2b760fcf5cfb34e8610df56d83745b2b74ae1379 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 6 Oct 2017 12:06:03 -0700 Subject: ipv6: hook up exception table to store dst cache This commit makes use of the exception hash table implementation to store dst caches created by pmtu discovery and ip redirect into the hash table under the rt_info and no longer inserts these routes into fib6 tree. This makes the fib6 tree only contain static configured routes and could now be protected by rcu instead of a rw lock. With this change, in the route lookup related functions, after finding the rt6_info with the longest prefix, we also need to search for the exception table before doing backtracking. In the route delete function, if the route being deleted is not a dst cache, deletion of this route also need to flush the whole hash table under it. If it is a dst cache, then only delete the cached dst in the hash table. Note: for fib6_walk_continue() function, w->root now is always pointing to a root node considering that fib6_prune_clones() is removed from the code. So we add a WARN_ON() msg to make sure w->root always points to a root node and also removed the update of w->root in fib6_repair_tree(). This is a prerequisite for later patch because we don't need to make w->root as rcu protected when replacing rwlock with RCU. Also, we remove all prune related variables as it is no longer used. Signed-off-by: Wei Wang Signed-off-by: Martin KaFai Lau Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 1 - net/ipv6/addrconf.c | 1 - net/ipv6/ip6_fib.c | 95 ++++++++------------------------------------ net/ipv6/route.c | 108 +++++++++++++++++++++++++------------------------- 4 files changed, 72 insertions(+), 133 deletions(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 4497a1eb4d41..d0b7283073e3 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -280,7 +280,6 @@ struct fib6_walker { struct fib6_node *root, *node; struct rt6_info *leaf; enum fib6_walk_state state; - bool prune; unsigned int skip; unsigned int count; int (*func)(struct fib6_walker *); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 3ccaf52824c9..873afafddfc4 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2326,7 +2326,6 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, if (!fn) goto out; - noflags |= RTF_CACHE; for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { if (rt->dst.dev->ifindex != dev->ifindex) continue; diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index b3e4cf0962f8..9c8e704e6af7 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -54,7 +54,6 @@ struct fib6_cleaner { #define FWS_INIT FWS_L #endif -static void fib6_prune_clones(struct net *net, struct fib6_node *fn); static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn); static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn); static int fib6_walk(struct net *net, struct fib6_walker *w); @@ -1101,6 +1100,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, if (WARN_ON_ONCE(!atomic_read(&rt->dst.__refcnt))) return -EINVAL; + if (WARN_ON_ONCE(rt->rt6i_flags & RTF_CACHE)) + return -EINVAL; if (info->nlh) { if (!(info->nlh->nlmsg_flags & NLM_F_CREATE)) @@ -1192,11 +1193,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, #endif err = fib6_add_rt2node(fn, rt, info, mxc); - if (!err) { + if (!err) fib6_start_gc(info->nl_net, rt); - if (!(rt->rt6i_flags & RTF_CACHE)) - fib6_prune_clones(info->nl_net, pn); - } out: if (err) { @@ -1511,19 +1509,12 @@ static struct fib6_node *fib6_repair_tree(struct net *net, read_lock(&net->ipv6.fib6_walker_lock); FOR_WALKERS(net, w) { if (!child) { - if (w->root == fn) { - w->root = w->node = NULL; - RT6_TRACE("W %p adjusted by delroot 1\n", w); - } else if (w->node == fn) { + if (w->node == fn) { RT6_TRACE("W %p adjusted by delnode 1, s=%d/%d\n", w, w->state, nstate); w->node = pn; w->state = nstate; } } else { - if (w->root == fn) { - w->root = child; - RT6_TRACE("W %p adjusted by delroot 2\n", w); - } if (w->node == fn) { w->node = child; if (children&2) { @@ -1557,12 +1548,17 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, RT6_TRACE("fib6_del_route\n"); + WARN_ON_ONCE(rt->rt6i_flags & RTF_CACHE); + /* Unlink it */ *rtp = rt->dst.rt6_next; rt->rt6i_node = NULL; net->ipv6.rt6_stats->fib_rt_entries--; net->ipv6.rt6_stats->fib_discarded_routes++; + /* Flush all cached dst in exception table */ + rt6_flush_exceptions(rt); + /* Reset round-robin state, if necessary */ if (fn->rr_ptr == rt) fn->rr_ptr = NULL; @@ -1625,18 +1621,9 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info) WARN_ON(!(fn->fn_flags & RTN_RTINFO)); - if (!(rt->rt6i_flags & RTF_CACHE)) { - struct fib6_node *pn = fn; -#ifdef CONFIG_IPV6_SUBTREES - /* clones of this route might be in another subtree */ - if (rt->rt6i_src.plen) { - while (!(pn->fn_flags & RTN_ROOT)) - pn = pn->parent; - pn = pn->parent; - } -#endif - fib6_prune_clones(info->nl_net, pn); - } + /* remove cached dst from exception table */ + if (rt->rt6i_flags & RTF_CACHE) + return rt6_remove_exception_rt(rt); /* * Walk the leaf entries looking for ourself @@ -1679,16 +1666,14 @@ static int fib6_walk_continue(struct fib6_walker *w) { struct fib6_node *fn, *pn; + /* w->root should always be table->tb6_root */ + WARN_ON_ONCE(!(w->root->fn_flags & RTN_TL_ROOT)); + for (;;) { fn = w->node; if (!fn) return 0; - if (w->prune && fn != w->root && - fn->fn_flags & RTN_RTINFO && w->state < FWS_C) { - w->state = FWS_C; - w->leaf = fn->leaf; - } switch (w->state) { #ifdef CONFIG_IPV6_SUBTREES case FWS_S: @@ -1820,20 +1805,16 @@ static int fib6_clean_node(struct fib6_walker *w) * func is called on each route. * It may return -1 -> delete this route. * 0 -> continue walking - * - * prune==1 -> only immediate children of node (certainly, - * ignoring pure split nodes) will be scanned. */ static void fib6_clean_tree(struct net *net, struct fib6_node *root, int (*func)(struct rt6_info *, void *arg), - bool prune, int sernum, void *arg) + int sernum, void *arg) { struct fib6_cleaner c; c.w.root = root; c.w.func = fib6_clean_node; - c.w.prune = prune; c.w.count = 0; c.w.skip = 0; c.func = func; @@ -1858,7 +1839,7 @@ static void __fib6_clean_all(struct net *net, hlist_for_each_entry_rcu(table, head, tb6_hlist) { write_lock_bh(&table->tb6_lock); fib6_clean_tree(net, &table->tb6_root, - func, false, sernum, arg); + func, sernum, arg); write_unlock_bh(&table->tb6_lock); } } @@ -1871,22 +1852,6 @@ void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *), __fib6_clean_all(net, func, FIB6_NO_SERNUM_CHANGE, arg); } -static int fib6_prune_clone(struct rt6_info *rt, void *arg) -{ - if (rt->rt6i_flags & RTF_CACHE) { - RT6_TRACE("pruning clone %p\n", rt); - return -1; - } - - return 0; -} - -static void fib6_prune_clones(struct net *net, struct fib6_node *fn) -{ - fib6_clean_tree(net, fn, fib6_prune_clone, true, - FIB6_NO_SERNUM_CHANGE, NULL); -} - static void fib6_flush_trees(struct net *net) { int new_sernum = fib6_new_sernum(net); @@ -1914,32 +1879,6 @@ static int fib6_age(struct rt6_info *rt, void *arg) return -1; } gc_args->more++; - /* The following part will soon be removed when the exception - * table is hooked up to store all cached routes. - */ - } else if (rt->rt6i_flags & RTF_CACHE) { - if (time_after_eq(now, rt->dst.lastuse + gc_args->timeout)) - rt->dst.obsolete = DST_OBSOLETE_KILL; - if (atomic_read(&rt->dst.__refcnt) == 1 && - rt->dst.obsolete == DST_OBSOLETE_KILL) { - RT6_TRACE("aging clone %p\n", rt); - return -1; - } else if (rt->rt6i_flags & RTF_GATEWAY) { - struct neighbour *neigh; - __u8 neigh_flags = 0; - - neigh = dst_neigh_lookup(&rt->dst, &rt->rt6i_gateway); - if (neigh) { - neigh_flags = neigh->flags; - neigh_release(neigh); - } - if (!(neigh_flags & NTF_ROUTER)) { - RT6_TRACE("purging route %p via non-router but gateway\n", - rt); - return -1; - } - } - gc_args->more++; } /* Also age clones in the exception table. diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 855b4ceec349..65130dde276a 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -878,8 +878,8 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net, struct fib6_table *table, struct flowi6 *fl6, int flags) { + struct rt6_info *rt, *rt_cache; struct fib6_node *fn; - struct rt6_info *rt; read_lock_bh(&table->tb6_lock); fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); @@ -893,6 +893,11 @@ restart: if (fn) goto restart; } + /* Search through exception table */ + rt_cache = rt6_find_cached_rt(rt, &fl6->daddr, &fl6->saddr); + if (rt_cache) + rt = rt_cache; + dst_use(&rt->dst, jiffies); read_unlock_bh(&table->tb6_lock); @@ -1592,7 +1597,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif, struct flowi6 *fl6, int flags) { struct fib6_node *fn, *saved_fn; - struct rt6_info *rt; + struct rt6_info *rt, *rt_cache; int strict = 0; strict |= flags & RT6_LOOKUP_F_IFACE; @@ -1624,6 +1629,10 @@ redo_rt6_select: } } + /*Search through exception table */ + rt_cache = rt6_find_cached_rt(rt, &fl6->daddr, &fl6->saddr); + if (rt_cache) + rt = rt_cache; if (rt == net->ipv6.ip6_null_entry || (rt->rt6i_flags & RTF_CACHE)) { dst_use(&rt->dst, jiffies); @@ -1988,23 +1997,17 @@ static void __ip6_rt_update_pmtu(struct dst_entry *dst, const struct sock *sk, if (!rt6_cache_allowed_for_pmtu(rt6)) { rt6_do_update_pmtu(rt6, mtu); + /* update rt6_ex->stamp for cache */ + if (rt6->rt6i_flags & RTF_CACHE) + rt6_update_exception_stamp_rt(rt6); } else if (daddr) { struct rt6_info *nrt6; nrt6 = ip6_rt_cache_alloc(rt6, daddr, saddr); if (nrt6) { rt6_do_update_pmtu(nrt6, mtu); - - /* ip6_ins_rt(nrt6) will bump the - * rt6->rt6i_node->fn_sernum - * which will fail the next rt6_check() and - * invalidate the sk->sk_dst_cache. - */ - ip6_ins_rt(nrt6); - /* Release the reference taken in - * ip6_rt_cache_alloc() - */ - dst_release(&nrt6->dst); + if (rt6_insert_exception(nrt6, rt6)) + dst_release_immediate(&nrt6->dst); } } } @@ -2068,7 +2071,7 @@ static struct rt6_info *__ip6_route_redirect(struct net *net, int flags) { struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl6; - struct rt6_info *rt; + struct rt6_info *rt, *rt_cache; struct fib6_node *fn; /* Get the "current" route for this destination and @@ -2093,8 +2096,23 @@ restart: continue; if (fl6->flowi6_oif != rt->dst.dev->ifindex) continue; - if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) + /* rt_cache's gateway might be different from its 'parent' + * in the case of an ip redirect. + * So we keep searching in the exception table if the gateway + * is different. + */ + if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) { + rt_cache = rt6_find_cached_rt(rt, + &fl6->daddr, + &fl6->saddr); + if (rt_cache && + ipv6_addr_equal(&rdfl->gateway, + &rt_cache->rt6i_gateway)) { + rt = rt_cache; + break; + } continue; + } break; } @@ -2785,9 +2803,9 @@ out_put: static int ip6_route_del(struct fib6_config *cfg, struct netlink_ext_ack *extack) { + struct rt6_info *rt, *rt_cache; struct fib6_table *table; struct fib6_node *fn; - struct rt6_info *rt; int err = -ESRCH; table = fib6_get_table(cfg->fc_nlinfo.nl_net, cfg->fc_table); @@ -2801,13 +2819,17 @@ static int ip6_route_del(struct fib6_config *cfg, fn = fib6_locate(&table->tb6_root, &cfg->fc_dst, cfg->fc_dst_len, &cfg->fc_src, cfg->fc_src_len, - true); + !(cfg->fc_flags & RTF_CACHE)); if (fn) { for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { - if ((rt->rt6i_flags & RTF_CACHE) && - !(cfg->fc_flags & RTF_CACHE)) - continue; + if (cfg->fc_flags & RTF_CACHE) { + rt_cache = rt6_find_cached_rt(rt, &cfg->fc_dst, + &cfg->fc_src); + if (!rt_cache) + continue; + rt = rt_cache; + } if (cfg->fc_ifindex && (!rt->dst.dev || rt->dst.dev->ifindex != cfg->fc_ifindex)) @@ -2933,8 +2955,14 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu nrt->rt6i_protocol = RTPROT_REDIRECT; nrt->rt6i_gateway = *(struct in6_addr *)neigh->primary_key; - if (ip6_ins_rt(nrt)) - goto out_release; + /* No need to remove rt from the exception table if rt is + * a cached route because rt6_insert_exception() will + * takes care of it + */ + if (rt6_insert_exception(nrt, rt)) { + dst_release_immediate(&nrt->dst); + goto out; + } netevent.old = &rt->dst; netevent.new = &nrt->dst; @@ -2942,17 +2970,6 @@ static void rt6_do_redirect(struct dst_entry *dst, struct sock *sk, struct sk_bu netevent.neigh = neigh; call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); - if (rt->rt6i_flags & RTF_CACHE) { - rt = (struct rt6_info *) dst_clone(&rt->dst); - ip6_del_rt(rt); - } - -out_release: - /* Release the reference taken in - * ip6_rt_cache_alloc() - */ - dst_release(&nrt->dst); - out: neigh_release(neigh); } @@ -3344,12 +3361,8 @@ static int fib6_clean_tohost(struct rt6_info *rt, void *arg) { struct in6_addr *gateway = (struct in6_addr *)arg; - /* RTF_CACHE_GATEWAY case will be removed once the exception - * table is hooked up to store all cached routes. - */ - if ((((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) || - ((rt->rt6i_flags & RTF_CACHE_GATEWAY) == RTF_CACHE_GATEWAY)) && - ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { + if (((rt->rt6i_flags & RTF_RA_ROUTER) == RTF_RA_ROUTER) && + ipv6_addr_equal(gateway, &rt->rt6i_gateway)) { return -1; } @@ -3438,20 +3451,9 @@ static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg) dst_metric_raw(&rt->dst, RTAX_MTU) && !dst_metric_locked(&rt->dst, RTAX_MTU)) { spin_lock_bh(&rt6_exception_lock); - /* This case will be removed once the exception table - * is hooked up. - */ - if (rt->rt6i_flags & RTF_CACHE) { - /* For RTF_CACHE with rt6i_pmtu == 0 - * (i.e. a redirected route), - * the metrics of its rt->dst.from has already - * been updated. - */ - if (rt->rt6i_pmtu && rt->rt6i_pmtu > arg->mtu) - rt->rt6i_pmtu = arg->mtu; - } else if (dst_mtu(&rt->dst) >= arg->mtu || - (dst_mtu(&rt->dst) < arg->mtu && - dst_mtu(&rt->dst) == idev->cnf.mtu6)) { + if (dst_mtu(&rt->dst) >= arg->mtu || + (dst_mtu(&rt->dst) < arg->mtu && + dst_mtu(&rt->dst) == idev->cnf.mtu6)) { dst_metric_set(&rt->dst, RTAX_MTU, arg->mtu); } rt6_exceptions_update_pmtu(rt, arg->mtu); -- cgit v1.2.3-59-g8ed1b From bbd63f06d114a52be33f6982fc89ca2768cdeb62 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 6 Oct 2017 12:06:07 -0700 Subject: ipv6: update fn_sernum after route is inserted to tree fib6_add() logic currently calls fib6_add_1() to figure out what node should be used for the newly added route and then call fib6_add_rt2node() to insert the route to the node. And during the call of fib6_add_1(), fn_sernum is updated for all nodes that share the same prefix as the new route. This does not have issue in the current code because reader thread will not be able to access the tree while writer thread is inserting new route to it. However, it is not the case once we transition to use RCU. Reader thread could potentially see the new fn_sernum before the new route is inserted. As a result, reader thread's route lookup will return a stale route with the new fn_sernum. In order to solve this issue, we remove all the update of fn_sernum in fib6_add_1(), and instead, introduce a new function that updates fn_sernum for all related nodes and call this functions once the route is successfully inserted to the tree. Also, smp_wmb() is used after a route is successfully inserted into the fib tree and right before the updated of fn->sernum. And smp_rmb() is used right after fn->sernum is accessed in rt6_get_cookie_safe(). This is to guarantee that when the reader thread sees the new fn->sernum, the new route is already inserted in the tree in memory. Signed-off-by: Wei Wang Signed-off-by: Martin KaFai Lau Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 2 ++ net/ipv6/ip6_fib.c | 39 +++++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index d0b7283073e3..6bf929b50951 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -220,6 +220,8 @@ static inline bool rt6_get_cookie_safe(const struct rt6_info *rt, if (fn) { *cookie = fn->fn_sernum; + /* pairs with smp_wmb() in fib6_update_sernum_upto_root() */ + smp_rmb(); status = true; } diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index eee392f7b1f6..f604b311cc3e 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -585,7 +585,7 @@ out: static struct fib6_node *fib6_add_1(struct fib6_node *root, struct in6_addr *addr, int plen, int offset, int allow_create, - int replace_required, int sernum, + int replace_required, struct netlink_ext_ack *extack) { struct fib6_node *fn, *in, *ln; @@ -631,8 +631,6 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root, fn->leaf = NULL; } - fn->fn_sernum = sernum; - return fn; } @@ -641,7 +639,6 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root, */ /* Try to walk down on tree. */ - fn->fn_sernum = sernum; dir = addr_bit_set(addr, fn->fn_bit); pn = fn; fn = dir ? fn->right : fn->left; @@ -677,7 +674,6 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root, ln->fn_bit = plen; ln->parent = pn; - ln->fn_sernum = sernum; if (dir) pn->right = ln; @@ -737,8 +733,6 @@ insert_above: in->leaf = fn->leaf; atomic_inc(&in->leaf->rt6i_ref); - in->fn_sernum = sernum; - /* update parent pointer */ if (dir) pn->right = in; @@ -750,8 +744,6 @@ insert_above: ln->parent = in; fn->parent = in; - ln->fn_sernum = sernum; - if (addr_bit_set(addr, bit)) { in->right = ln; in->left = fn; @@ -776,8 +768,6 @@ insert_above: ln->parent = pn; - ln->fn_sernum = sernum; - if (dir) pn->right = ln; else @@ -1079,6 +1069,20 @@ void fib6_force_start_gc(struct net *net) jiffies + net->ipv6.sysctl.ip6_rt_gc_interval); } +static void fib6_update_sernum_upto_root(struct rt6_info *rt, + int sernum) +{ + struct fib6_node *fn = rcu_dereference_protected(rt->rt6i_node, + lockdep_is_held(&rt->rt6i_table->tb6_lock)); + + /* paired with smp_rmb() in rt6_get_cookie_safe() */ + smp_wmb(); + while (fn) { + fn->fn_sernum = sernum; + fn = fn->parent; + } +} + /* * Add routing information to the routing tree. * / @@ -1111,7 +1115,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, fn = fib6_add_1(root, &rt->rt6i_dst.addr, rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst), allow_create, - replace_required, sernum, extack); + replace_required, extack); if (IS_ERR(fn)) { err = PTR_ERR(fn); fn = NULL; @@ -1145,15 +1149,13 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, sfn->leaf = info->nl_net->ipv6.ip6_null_entry; atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref); sfn->fn_flags = RTN_ROOT; - sfn->fn_sernum = sernum; /* Now add the first leaf node to new subtree */ sn = fib6_add_1(sfn, &rt->rt6i_src.addr, rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src), - allow_create, replace_required, sernum, - extack); + allow_create, replace_required, extack); if (IS_ERR(sn)) { /* If it is failed, discard just allocated @@ -1172,8 +1174,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src), - allow_create, replace_required, sernum, - extack); + allow_create, replace_required, extack); if (IS_ERR(sn)) { err = PTR_ERR(sn); @@ -1190,8 +1191,10 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, #endif err = fib6_add_rt2node(fn, rt, info, mxc); - if (!err) + if (!err) { + fib6_update_sernum_upto_root(rt, sernum); fib6_start_gc(info->nl_net, rt); + } out: if (err) { -- cgit v1.2.3-59-g8ed1b From 66f5d6ce53e665477d2a33e8f539d4fa4ca81c83 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 6 Oct 2017 12:06:10 -0700 Subject: ipv6: replace rwlock with rcu and spinlock in fib6_table With all the preparation work before, we are now ready to replace rwlock with rcu and spinlock in fib6_table. That means now all fib6_node in fib6_table are protected by rcu. And when freeing fib6_node, call_rcu() is used to wait for the rcu grace period before releasing the memory. When accessing fib6_node, corresponding rcu APIs need to be used. And all previous sessions protected by the write lock will now be protected by the spin lock per table. All previous sessions protected by read lock will now be protected by rcu_read_lock(). A couple of things to note here: 1. As part of the work of replacing rwlock with rcu, the linked list of fn->leaf now has to be rcu protected as well. So both fn->leaf and rt->dst.rt6_next are now __rcu tagged and corresponding rcu APIs are used when manipulating them. 2. For fn->rr_ptr, first of all, it also needs to be rcu protected now and is tagged with __rcu and rcu APIs are used in corresponding places. Secondly, fn->rr_ptr is changed in rt6_select() which is a reader thread. This makes the issue a bit complicated. We think a valid solution for it is to let rt6_select() grab the tb6_lock if it decides to change it. As it is not in the normal operation and only happens when there is no valid neighbor cache for the route, we think the performance impact should be low. 3. fib6_walk_continue() has to be called with tb6_lock held even in the route dumping related functions, e.g. inet6_dump_fib(), fib6_tables_dump() and ipv6_route_seq_ops. It is because fib6_walk_continue() makes modifications to the walker structure, and so are fib6_repair_tree() and fib6_del_route(). In order to do proper syncing between them, we need to let fib6_walk_continue() hold the lock. We may be able to do further improvement on the way we do the tree walk to get rid of the need for holding the spin lock. But not for now. 4. When fib6_del_route() removes a route from the tree, we no longer mark rt->dst.rt6_next to NULL to make simultaneous reader be able to further traverse the list with rcu. However, rt->dst.rt6_next is only valid within this same rcu period. No one should access it later. 5. All the operation of atomic_inc(rt->rt6i_ref) is changed to be performed before we publish this route (either by linking it to fn->leaf or insert it in the list pointed by fn->leaf) just to be safe because as soon as we publish the route, some read thread will be able to access it. Signed-off-by: Wei Wang Signed-off-by: Martin KaFai Lau Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/dst.h | 2 +- include/net/ip6_fib.h | 24 ++- net/ipv6/addrconf.c | 11 +- net/ipv6/ip6_fib.c | 405 ++++++++++++++++++++++++++++++-------------------- net/ipv6/route.c | 121 ++++++++------- 5 files changed, 333 insertions(+), 230 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 06a6765da074..204c19e25456 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -101,7 +101,7 @@ struct dst_entry { union { struct dst_entry *next; struct rtable __rcu *rt_next; - struct rt6_info *rt6_next; + struct rt6_info __rcu *rt6_next; struct dn_route __rcu *dn_next; }; }; diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 6bf929b50951..0b438b9bcb10 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -68,18 +68,18 @@ struct fib6_config { }; struct fib6_node { - struct fib6_node *parent; - struct fib6_node *left; - struct fib6_node *right; + struct fib6_node __rcu *parent; + struct fib6_node __rcu *left; + struct fib6_node __rcu *right; #ifdef CONFIG_IPV6_SUBTREES - struct fib6_node *subtree; + struct fib6_node __rcu *subtree; #endif - struct rt6_info *leaf; + struct rt6_info __rcu *leaf; __u16 fn_bit; /* bit key */ __u16 fn_flags; int fn_sernum; - struct rt6_info *rr_ptr; + struct rt6_info __rcu *rr_ptr; struct rcu_head rcu; }; @@ -91,7 +91,7 @@ struct fib6_gc_args { #ifndef CONFIG_IPV6_SUBTREES #define FIB6_SUBTREE(fn) NULL #else -#define FIB6_SUBTREE(fn) ((fn)->subtree) +#define FIB6_SUBTREE(fn) (rcu_dereference_protected((fn)->subtree, 1)) #endif struct mx6_config { @@ -174,6 +174,14 @@ struct rt6_info { unused:7; }; +#define for_each_fib6_node_rt_rcu(fn) \ + for (rt = rcu_dereference((fn)->leaf); rt; \ + rt = rcu_dereference(rt->dst.rt6_next)) + +#define for_each_fib6_walker_rt(w) \ + for (rt = (w)->leaf; rt; \ + rt = rcu_dereference_protected(rt->dst.rt6_next, 1)) + static inline struct inet6_dev *ip6_dst_idev(struct dst_entry *dst) { return ((struct rt6_info *)dst)->rt6i_idev; @@ -310,7 +318,7 @@ struct rt6_statistics { struct fib6_table { struct hlist_node tb6_hlist; u32 tb6_id; - rwlock_t tb6_lock; + spinlock_t tb6_lock; struct fib6_node tb6_root; struct inet_peer_base tb6_peers; unsigned int flags; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index f86e931d555e..9854d93e45bb 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2321,12 +2321,12 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, if (!table) return NULL; - read_lock_bh(&table->tb6_lock); + rcu_read_lock(); fn = fib6_locate(&table->tb6_root, pfx, plen, NULL, 0, true); if (!fn) goto out; - for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { + for_each_fib6_node_rt_rcu(fn) { if (rt->dst.dev->ifindex != dev->ifindex) continue; if ((rt->rt6i_flags & flags) != flags) @@ -2338,7 +2338,7 @@ static struct rt6_info *addrconf_get_prefix_route(const struct in6_addr *pfx, break; } out: - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); return rt; } @@ -5898,10 +5898,9 @@ void addrconf_disable_policy_idev(struct inet6_dev *idev, int val) spin_lock(&ifa->lock); if (ifa->rt) { struct rt6_info *rt = ifa->rt; - struct fib6_table *table = rt->rt6i_table; int cpu; - read_lock(&table->tb6_lock); + rcu_read_lock(); addrconf_set_nopolicy(ifa->rt, val); if (rt->rt6i_pcpu) { for_each_possible_cpu(cpu) { @@ -5911,7 +5910,7 @@ void addrconf_disable_policy_idev(struct inet6_dev *idev, int val) addrconf_set_nopolicy(*rtp, val); } } - read_unlock(&table->tb6_lock); + rcu_read_unlock(); } spin_unlock(&ifa->lock); } diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index cf6137e81408..3f95908b39c3 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -54,8 +54,12 @@ struct fib6_cleaner { #define FWS_INIT FWS_L #endif -static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn); -static struct fib6_node *fib6_repair_tree(struct net *net, struct fib6_node *fn); +static struct rt6_info *fib6_find_prefix(struct net *net, + struct fib6_table *table, + struct fib6_node *fn); +static struct fib6_node *fib6_repair_tree(struct net *net, + struct fib6_table *table, + struct fib6_node *fn); static int fib6_walk(struct net *net, struct fib6_walker *w); static int fib6_walk_continue(struct fib6_walker *w); @@ -107,12 +111,12 @@ void fib6_update_sernum(struct rt6_info *rt) struct net *net = dev_net(rt->dst.dev); struct fib6_node *fn; - write_lock_bh(&table->tb6_lock); + spin_lock_bh(&table->tb6_lock); fn = rcu_dereference_protected(rt->rt6i_node, lockdep_is_held(&table->tb6_lock)); if (fn) fn->fn_sernum = fib6_new_sernum(net); - write_unlock_bh(&table->tb6_lock); + spin_unlock_bh(&table->tb6_lock); } /* @@ -207,8 +211,7 @@ static void fib6_link_table(struct net *net, struct fib6_table *tb) * Initialize table lock at a single place to give lockdep a key, * tables aren't visible prior to being linked to the list. */ - rwlock_init(&tb->tb6_lock); - + spin_lock_init(&tb->tb6_lock); h = tb->tb6_id & (FIB6_TABLE_HASHSZ - 1); /* @@ -227,7 +230,8 @@ static struct fib6_table *fib6_alloc_table(struct net *net, u32 id) table = kzalloc(sizeof(*table), GFP_ATOMIC); if (table) { table->tb6_id = id; - table->tb6_root.leaf = net->ipv6.ip6_null_entry; + rcu_assign_pointer(table->tb6_root.leaf, + net->ipv6.ip6_null_entry); table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; inet_peer_base_init(&table->tb6_peers); } @@ -324,11 +328,8 @@ unsigned int fib6_tables_seq_read(struct net *net) struct hlist_head *head = &net->ipv6.fib_table_hash[h]; struct fib6_table *tb; - hlist_for_each_entry_rcu(tb, head, tb6_hlist) { - read_lock_bh(&tb->tb6_lock); + hlist_for_each_entry_rcu(tb, head, tb6_hlist) fib_seq += tb->fib_seq; - read_unlock_bh(&tb->tb6_lock); - } } rcu_read_unlock(); @@ -374,7 +375,7 @@ static int fib6_node_dump(struct fib6_walker *w) { struct rt6_info *rt; - for (rt = w->leaf; rt; rt = rt->dst.rt6_next) + for_each_fib6_walker_rt(w) fib6_rt_dump(rt, w->args); w->leaf = NULL; return 0; @@ -384,9 +385,9 @@ static void fib6_table_dump(struct net *net, struct fib6_table *tb, struct fib6_walker *w) { w->root = &tb->tb6_root; - read_lock_bh(&tb->tb6_lock); + spin_lock_bh(&tb->tb6_lock); fib6_walk(net, w); - read_unlock_bh(&tb->tb6_lock); + spin_unlock_bh(&tb->tb6_lock); } /* Called with rcu_read_lock() */ @@ -423,7 +424,7 @@ static int fib6_dump_node(struct fib6_walker *w) int res; struct rt6_info *rt; - for (rt = w->leaf; rt; rt = rt->dst.rt6_next) { + for_each_fib6_walker_rt(w) { res = rt6_dump_route(rt, w->args); if (res < 0) { /* Frame is full, suspend walking */ @@ -482,9 +483,9 @@ static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb, w->count = 0; w->skip = 0; - read_lock_bh(&table->tb6_lock); + spin_lock_bh(&table->tb6_lock); res = fib6_walk(net, w); - read_unlock_bh(&table->tb6_lock); + spin_unlock_bh(&table->tb6_lock); if (res > 0) { cb->args[4] = 1; cb->args[5] = w->root->fn_sernum; @@ -499,9 +500,9 @@ static int fib6_dump_table(struct fib6_table *table, struct sk_buff *skb, } else w->skip = 0; - read_lock_bh(&table->tb6_lock); + spin_lock_bh(&table->tb6_lock); res = fib6_walk_continue(w); - read_unlock_bh(&table->tb6_lock); + spin_unlock_bh(&table->tb6_lock); if (res <= 0) { fib6_walker_unlink(net, w); cb->args[4] = 0; @@ -582,11 +583,12 @@ out: * node. */ -static struct fib6_node *fib6_add_1(struct fib6_node *root, - struct in6_addr *addr, int plen, - int offset, int allow_create, - int replace_required, - struct netlink_ext_ack *extack) +static struct fib6_node *fib6_add_1(struct fib6_table *table, + struct fib6_node *root, + struct in6_addr *addr, int plen, + int offset, int allow_create, + int replace_required, + struct netlink_ext_ack *extack) { struct fib6_node *fn, *in, *ln; struct fib6_node *pn = NULL; @@ -601,7 +603,9 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root, fn = root; do { - key = (struct rt6key *)((u8 *)fn->leaf + offset); + struct rt6_info *leaf = rcu_dereference_protected(fn->leaf, + lockdep_is_held(&table->tb6_lock)); + key = (struct rt6key *)((u8 *)leaf + offset); /* * Prefix match @@ -627,8 +631,8 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root, if (plen == fn->fn_bit) { /* clean up an intermediate node */ if (!(fn->fn_flags & RTN_RTINFO)) { - rt6_release(fn->leaf); - fn->leaf = NULL; + RCU_INIT_POINTER(fn->leaf, NULL); + rt6_release(leaf); } return fn; @@ -641,7 +645,11 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root, /* Try to walk down on tree. */ dir = addr_bit_set(addr, fn->fn_bit); pn = fn; - fn = dir ? fn->right : fn->left; + fn = dir ? + rcu_dereference_protected(fn->right, + lockdep_is_held(&table->tb6_lock)) : + rcu_dereference_protected(fn->left, + lockdep_is_held(&table->tb6_lock)); } while (fn); if (!allow_create) { @@ -672,13 +680,12 @@ static struct fib6_node *fib6_add_1(struct fib6_node *root, if (!ln) return ERR_PTR(-ENOMEM); ln->fn_bit = plen; - - ln->parent = pn; + RCU_INIT_POINTER(ln->parent, pn); if (dir) - pn->right = ln; + rcu_assign_pointer(pn->right, ln); else - pn->left = ln; + rcu_assign_pointer(pn->left, ln); return ln; @@ -692,7 +699,8 @@ insert_above: * and the current */ - pn = fn->parent; + pn = rcu_dereference_protected(fn->parent, + lockdep_is_held(&table->tb6_lock)); /* find 1st bit in difference between the 2 addrs. @@ -729,27 +737,28 @@ insert_above: in->fn_bit = bit; - in->parent = pn; + RCU_INIT_POINTER(in->parent, pn); in->leaf = fn->leaf; - atomic_inc(&in->leaf->rt6i_ref); + atomic_inc(&rcu_dereference_protected(in->leaf, + lockdep_is_held(&table->tb6_lock))->rt6i_ref); /* update parent pointer */ if (dir) - pn->right = in; + rcu_assign_pointer(pn->right, in); else - pn->left = in; + rcu_assign_pointer(pn->left, in); ln->fn_bit = plen; - ln->parent = in; - fn->parent = in; + RCU_INIT_POINTER(ln->parent, in); + rcu_assign_pointer(fn->parent, in); if (addr_bit_set(addr, bit)) { - in->right = ln; - in->left = fn; + rcu_assign_pointer(in->right, ln); + rcu_assign_pointer(in->left, fn); } else { - in->left = ln; - in->right = fn; + rcu_assign_pointer(in->left, ln); + rcu_assign_pointer(in->right, fn); } } else { /* plen <= bit */ @@ -766,19 +775,19 @@ insert_above: ln->fn_bit = plen; - ln->parent = pn; - - if (dir) - pn->right = ln; - else - pn->left = ln; + RCU_INIT_POINTER(ln->parent, pn); if (addr_bit_set(&key->addr, plen)) - ln->right = fn; + RCU_INIT_POINTER(ln->right, fn); else - ln->left = fn; + RCU_INIT_POINTER(ln->left, fn); + + rcu_assign_pointer(fn->parent, ln); - fn->parent = ln; + if (dir) + rcu_assign_pointer(pn->right, ln); + else + rcu_assign_pointer(pn->left, ln); } return ln; } @@ -824,6 +833,8 @@ static int fib6_commit_metrics(struct dst_entry *dst, struct mx6_config *mxc) static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn, struct net *net) { + struct fib6_table *table = rt->rt6i_table; + if (atomic_read(&rt->rt6i_ref) != 1) { /* This route is used as dummy address holder in some split * nodes. It is not leaked, but it still holds other resources, @@ -832,12 +843,17 @@ static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn, * to still alive ones. */ while (fn) { - if (!(fn->fn_flags & RTN_RTINFO) && fn->leaf == rt) { - fn->leaf = fib6_find_prefix(net, fn); - atomic_inc(&fn->leaf->rt6i_ref); + struct rt6_info *leaf = rcu_dereference_protected(fn->leaf, + lockdep_is_held(&table->tb6_lock)); + struct rt6_info *new_leaf; + if (!(fn->fn_flags & RTN_RTINFO) && leaf == rt) { + new_leaf = fib6_find_prefix(net, table, fn); + atomic_inc(&new_leaf->rt6i_ref); + rcu_assign_pointer(fn->leaf, new_leaf); rt6_release(rt); } - fn = fn->parent; + fn = rcu_dereference_protected(fn->parent, + lockdep_is_held(&table->tb6_lock)); } } } @@ -849,9 +865,11 @@ static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn, static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, struct nl_info *info, struct mx6_config *mxc) { + struct rt6_info *leaf = rcu_dereference_protected(fn->leaf, + lockdep_is_held(&rt->rt6i_table->tb6_lock)); struct rt6_info *iter = NULL; - struct rt6_info **ins; - struct rt6_info **fallback_ins = NULL; + struct rt6_info __rcu **ins; + struct rt6_info __rcu **fallback_ins = NULL; int replace = (info->nlh && (info->nlh->nlmsg_flags & NLM_F_REPLACE)); int add = (!info->nlh || @@ -866,7 +884,9 @@ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, ins = &fn->leaf; - for (iter = fn->leaf; iter; iter = iter->dst.rt6_next) { + for (iter = leaf; iter; + iter = rcu_dereference_protected(iter->dst.rt6_next, + lockdep_is_held(&rt->rt6i_table->tb6_lock))) { /* * Search for duplicates */ @@ -928,7 +948,8 @@ next_iter: if (fallback_ins && !found) { /* No ECMP-able route found, replace first non-ECMP one */ ins = fallback_ins; - iter = *ins; + iter = rcu_dereference_protected(*ins, + lockdep_is_held(&rt->rt6i_table->tb6_lock)); found++; } @@ -942,7 +963,7 @@ next_iter: struct rt6_info *sibling, *temp_sibling; /* Find the first route that have the same metric */ - sibling = fn->leaf; + sibling = leaf; while (sibling) { if (sibling->rt6i_metric == rt->rt6i_metric && rt6_qualify_for_ecmp(sibling)) { @@ -950,7 +971,8 @@ next_iter: &sibling->rt6i_siblings); break; } - sibling = sibling->dst.rt6_next; + sibling = rcu_dereference_protected(sibling->dst.rt6_next, + lockdep_is_held(&rt->rt6i_table->tb6_lock)); } /* For each sibling in the list, increment the counter of * siblings. BUG() if counters does not match, list of siblings @@ -979,10 +1001,10 @@ add: if (err) return err; - rt->dst.rt6_next = iter; - *ins = rt; - rcu_assign_pointer(rt->rt6i_node, fn); + rcu_assign_pointer(rt->dst.rt6_next, iter); atomic_inc(&rt->rt6i_ref); + rcu_assign_pointer(rt->rt6i_node, fn); + rcu_assign_pointer(*ins, rt); call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_ADD, rt); if (!info->skip_notify) @@ -1008,10 +1030,10 @@ add: if (err) return err; - *ins = rt; + atomic_inc(&rt->rt6i_ref); rcu_assign_pointer(rt->rt6i_node, fn); rt->dst.rt6_next = iter->dst.rt6_next; - atomic_inc(&rt->rt6i_ref); + rcu_assign_pointer(*ins, rt); call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_REPLACE, rt); if (!info->skip_notify) @@ -1023,14 +1045,15 @@ add: nsiblings = iter->rt6i_nsiblings; iter->rt6i_node = NULL; fib6_purge_rt(iter, fn, info->nl_net); - if (fn->rr_ptr == iter) + if (rcu_access_pointer(fn->rr_ptr) == iter) fn->rr_ptr = NULL; rt6_release(iter); if (nsiblings) { /* Replacing an ECMP route, remove all siblings */ ins = &rt->dst.rt6_next; - iter = *ins; + iter = rcu_dereference_protected(*ins, + lockdep_is_held(&rt->rt6i_table->tb6_lock)); while (iter) { if (iter->rt6i_metric > rt->rt6i_metric) break; @@ -1038,14 +1061,15 @@ add: *ins = iter->dst.rt6_next; iter->rt6i_node = NULL; fib6_purge_rt(iter, fn, info->nl_net); - if (fn->rr_ptr == iter) + if (rcu_access_pointer(fn->rr_ptr) == iter) fn->rr_ptr = NULL; rt6_release(iter); nsiblings--; } else { ins = &iter->dst.rt6_next; } - iter = *ins; + iter = rcu_dereference_protected(*ins, + lockdep_is_held(&rt->rt6i_table->tb6_lock)); } WARN_ON(nsiblings != 0); } @@ -1079,7 +1103,8 @@ static void fib6_update_sernum_upto_root(struct rt6_info *rt, smp_wmb(); while (fn) { fn->fn_sernum = sernum; - fn = fn->parent; + fn = rcu_dereference_protected(fn->parent, + lockdep_is_held(&rt->rt6i_table->tb6_lock)); } } @@ -1087,12 +1112,14 @@ static void fib6_update_sernum_upto_root(struct rt6_info *rt, * Add routing information to the routing tree. * / * with source addr info in sub-trees + * Need to own table->tb6_lock */ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info, struct mx6_config *mxc, struct netlink_ext_ack *extack) { + struct fib6_table *table = rt->rt6i_table; struct fib6_node *fn, *pn = NULL; int err = -ENOMEM; int allow_create = 1; @@ -1113,7 +1140,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, if (!allow_create && !replace_required) pr_warn("RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE\n"); - fn = fib6_add_1(root, &rt->rt6i_dst.addr, rt->rt6i_dst.plen, + fn = fib6_add_1(table, root, + &rt->rt6i_dst.addr, rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst), allow_create, replace_required, extack); if (IS_ERR(fn)) { @@ -1128,7 +1156,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, if (rt->rt6i_src.plen) { struct fib6_node *sn; - if (!fn->subtree) { + if (!rcu_access_pointer(fn->subtree)) { struct fib6_node *sfn; /* @@ -1146,13 +1174,14 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, if (!sfn) goto failure; - sfn->leaf = info->nl_net->ipv6.ip6_null_entry; atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref); + rcu_assign_pointer(sfn->leaf, + info->nl_net->ipv6.ip6_null_entry); sfn->fn_flags = RTN_ROOT; /* Now add the first leaf node to new subtree */ - sn = fib6_add_1(sfn, &rt->rt6i_src.addr, + sn = fib6_add_1(table, sfn, &rt->rt6i_src.addr, rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src), allow_create, replace_required, extack); @@ -1168,10 +1197,10 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, } /* Now link new subtree to main tree */ - sfn->parent = fn; - fn->subtree = sfn; + rcu_assign_pointer(sfn->parent, fn); + rcu_assign_pointer(fn->subtree, sfn); } else { - sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, + sn = fib6_add_1(table, FIB6_SUBTREE(fn), &rt->rt6i_src.addr, rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src), allow_create, replace_required, extack); @@ -1182,9 +1211,9 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, } } - if (!fn->leaf) { - fn->leaf = rt; + if (!rcu_access_pointer(fn->leaf)) { atomic_inc(&rt->rt6i_ref); + rcu_assign_pointer(fn->leaf, rt); } fn = sn; } @@ -1203,19 +1232,23 @@ out: * If fib6_add_1 has cleared the old leaf pointer in the * super-tree leaf node we have to find a new one for it. */ - if (pn != fn && pn->leaf == rt) { - pn->leaf = NULL; + struct rt6_info *pn_leaf = rcu_dereference_protected(pn->leaf, + lockdep_is_held(&table->tb6_lock)); + if (pn != fn && pn_leaf == rt) { + pn_leaf = NULL; + RCU_INIT_POINTER(pn->leaf, NULL); atomic_dec(&rt->rt6i_ref); } - if (pn != fn && !pn->leaf && !(pn->fn_flags & RTN_RTINFO)) { - pn->leaf = fib6_find_prefix(info->nl_net, pn); + if (pn != fn && !pn_leaf && !(pn->fn_flags & RTN_RTINFO)) { + pn_leaf = fib6_find_prefix(info->nl_net, table, pn); #if RT6_DEBUG >= 2 - if (!pn->leaf) { - WARN_ON(pn->leaf == NULL); - pn->leaf = info->nl_net->ipv6.ip6_null_entry; + if (!pn_leaf) { + WARN_ON(!pn_leaf); + pn_leaf = info->nl_net->ipv6.ip6_null_entry; } #endif - atomic_inc(&pn->leaf->rt6i_ref); + atomic_inc(&pn_leaf->rt6i_ref); + rcu_assign_pointer(pn->leaf, pn_leaf); } #endif goto failure; @@ -1230,7 +1263,7 @@ failure: * fn->leaf. */ if (fn && !(fn->fn_flags & (RTN_RTINFO|RTN_ROOT))) - fib6_repair_tree(info->nl_net, fn); + fib6_repair_tree(info->nl_net, table, fn); /* Always release dst as dst->__refcnt is guaranteed * to be taken before entering this function */ @@ -1268,7 +1301,8 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root, dir = addr_bit_set(args->addr, fn->fn_bit); - next = dir ? fn->right : fn->left; + next = dir ? rcu_dereference(fn->right) : + rcu_dereference(fn->left); if (next) { fn = next; @@ -1278,8 +1312,10 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root, } while (fn) { - if (FIB6_SUBTREE(fn) || fn->fn_flags & RTN_RTINFO) { - struct rt6_info *leaf = fn->leaf; + struct fib6_node *subtree = FIB6_SUBTREE(fn); + + if (subtree || fn->fn_flags & RTN_RTINFO) { + struct rt6_info *leaf = rcu_dereference(fn->leaf); struct rt6key *key; if (!leaf) @@ -1289,10 +1325,9 @@ static struct fib6_node *fib6_lookup_1(struct fib6_node *root, if (ipv6_prefix_equal(&key->addr, args->addr, key->plen)) { #ifdef CONFIG_IPV6_SUBTREES - if (fn->subtree) { + if (subtree) { struct fib6_node *sfn; - sfn = fib6_lookup_1(fn->subtree, - args + 1); + sfn = fib6_lookup_1(subtree, args + 1); if (!sfn) goto backtrack; fn = sfn; @@ -1306,12 +1341,14 @@ backtrack: if (fn->fn_flags & RTN_ROOT) break; - fn = fn->parent; + fn = rcu_dereference(fn->parent); } return NULL; } +/* called with rcu_read_lock() held + */ struct fib6_node *fib6_lookup(struct fib6_node *root, const struct in6_addr *daddr, const struct in6_addr *saddr) { @@ -1359,7 +1396,7 @@ static struct fib6_node *fib6_locate_1(struct fib6_node *root, struct fib6_node *fn, *prev = NULL; for (fn = root; fn ; ) { - struct rt6_info *leaf = fn->leaf; + struct rt6_info *leaf = rcu_dereference(fn->leaf); struct rt6key *key; /* This node is being deleted */ @@ -1389,9 +1426,9 @@ next: * We have more bits to go */ if (addr_bit_set(addr, fn->fn_bit)) - fn = fn->right; + fn = rcu_dereference(fn->right); else - fn = fn->left; + fn = rcu_dereference(fn->left); } out: if (exact_match) @@ -1413,9 +1450,11 @@ struct fib6_node *fib6_locate(struct fib6_node *root, #ifdef CONFIG_IPV6_SUBTREES if (src_len) { + struct fib6_node *subtree = FIB6_SUBTREE(fn); + WARN_ON(saddr == NULL); - if (fn && fn->subtree) - fn = fib6_locate_1(fn->subtree, saddr, src_len, + if (fn && subtree) + fn = fib6_locate_1(subtree, saddr, src_len, offsetof(struct rt6_info, rt6i_src), exact_match); } @@ -1433,16 +1472,26 @@ struct fib6_node *fib6_locate(struct fib6_node *root, * */ -static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn) +static struct rt6_info *fib6_find_prefix(struct net *net, + struct fib6_table *table, + struct fib6_node *fn) { + struct fib6_node *child_left, *child_right; + if (fn->fn_flags & RTN_ROOT) return net->ipv6.ip6_null_entry; while (fn) { - if (fn->left) - return fn->left->leaf; - if (fn->right) - return fn->right->leaf; + child_left = rcu_dereference_protected(fn->left, + lockdep_is_held(&table->tb6_lock)); + child_right = rcu_dereference_protected(fn->right, + lockdep_is_held(&table->tb6_lock)); + if (child_left) + return rcu_dereference_protected(child_left->leaf, + lockdep_is_held(&table->tb6_lock)); + if (child_right) + return rcu_dereference_protected(child_right->leaf, + lockdep_is_held(&table->tb6_lock)); fn = FIB6_SUBTREE(fn); } @@ -1452,31 +1501,49 @@ static struct rt6_info *fib6_find_prefix(struct net *net, struct fib6_node *fn) /* * Called to trim the tree of intermediate nodes when possible. "fn" * is the node we want to try and remove. + * Need to own table->tb6_lock */ static struct fib6_node *fib6_repair_tree(struct net *net, - struct fib6_node *fn) + struct fib6_table *table, + struct fib6_node *fn) { int children; int nstate; - struct fib6_node *child, *pn; + struct fib6_node *child; struct fib6_walker *w; int iter = 0; for (;;) { + struct fib6_node *fn_r = rcu_dereference_protected(fn->right, + lockdep_is_held(&table->tb6_lock)); + struct fib6_node *fn_l = rcu_dereference_protected(fn->left, + lockdep_is_held(&table->tb6_lock)); + struct fib6_node *pn = rcu_dereference_protected(fn->parent, + lockdep_is_held(&table->tb6_lock)); + struct fib6_node *pn_r = rcu_dereference_protected(pn->right, + lockdep_is_held(&table->tb6_lock)); + struct fib6_node *pn_l = rcu_dereference_protected(pn->left, + lockdep_is_held(&table->tb6_lock)); + struct rt6_info *fn_leaf = rcu_dereference_protected(fn->leaf, + lockdep_is_held(&table->tb6_lock)); + struct rt6_info *pn_leaf = rcu_dereference_protected(pn->leaf, + lockdep_is_held(&table->tb6_lock)); + struct rt6_info *new_fn_leaf; + RT6_TRACE("fixing tree: plen=%d iter=%d\n", fn->fn_bit, iter); iter++; WARN_ON(fn->fn_flags & RTN_RTINFO); WARN_ON(fn->fn_flags & RTN_TL_ROOT); - WARN_ON(fn->leaf); + WARN_ON(fn_leaf); children = 0; child = NULL; - if (fn->right) - child = fn->right, children |= 1; - if (fn->left) - child = fn->left, children |= 2; + if (fn_r) + child = fn_r, children |= 1; + if (fn_l) + child = fn_l, children |= 2; if (children == 3 || FIB6_SUBTREE(fn) #ifdef CONFIG_IPV6_SUBTREES @@ -1484,36 +1551,36 @@ static struct fib6_node *fib6_repair_tree(struct net *net, || (children && fn->fn_flags & RTN_ROOT) #endif ) { - fn->leaf = fib6_find_prefix(net, fn); + new_fn_leaf = fib6_find_prefix(net, table, fn); #if RT6_DEBUG >= 2 - if (!fn->leaf) { - WARN_ON(!fn->leaf); - fn->leaf = net->ipv6.ip6_null_entry; + if (!new_fn_leaf) { + WARN_ON(!new_fn_leaf); + new_fn_leaf = net->ipv6.ip6_null_entry; } #endif - atomic_inc(&fn->leaf->rt6i_ref); - return fn->parent; + atomic_inc(&new_fn_leaf->rt6i_ref); + rcu_assign_pointer(fn->leaf, new_fn_leaf); + return pn; } - pn = fn->parent; #ifdef CONFIG_IPV6_SUBTREES if (FIB6_SUBTREE(pn) == fn) { WARN_ON(!(fn->fn_flags & RTN_ROOT)); - FIB6_SUBTREE(pn) = NULL; + RCU_INIT_POINTER(pn->subtree, NULL); nstate = FWS_L; } else { WARN_ON(fn->fn_flags & RTN_ROOT); #endif - if (pn->right == fn) - pn->right = child; - else if (pn->left == fn) - pn->left = child; + if (pn_r == fn) + rcu_assign_pointer(pn->right, child); + else if (pn_l == fn) + rcu_assign_pointer(pn->left, child); #if RT6_DEBUG >= 2 else WARN_ON(1); #endif if (child) - child->parent = pn; + rcu_assign_pointer(child->parent, pn); nstate = FWS_R; #ifdef CONFIG_IPV6_SUBTREES } @@ -1546,17 +1613,18 @@ static struct fib6_node *fib6_repair_tree(struct net *net, if (pn->fn_flags & RTN_RTINFO || FIB6_SUBTREE(pn)) return pn; - rt6_release(pn->leaf); - pn->leaf = NULL; + RCU_INIT_POINTER(pn->leaf, NULL); + rt6_release(pn_leaf); fn = pn; } } -static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, - struct nl_info *info) +static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn, + struct rt6_info __rcu **rtp, struct nl_info *info) { struct fib6_walker *w; - struct rt6_info *rt = *rtp; + struct rt6_info *rt = rcu_dereference_protected(*rtp, + lockdep_is_held(&table->tb6_lock)); struct net *net = info->nl_net; RT6_TRACE("fib6_del_route\n"); @@ -1573,7 +1641,7 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, rt6_flush_exceptions(rt); /* Reset round-robin state, if necessary */ - if (fn->rr_ptr == rt) + if (rcu_access_pointer(fn->rr_ptr) == rt) fn->rr_ptr = NULL; /* Remove this entry from other siblings */ @@ -1592,20 +1660,19 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, FOR_WALKERS(net, w) { if (w->state == FWS_C && w->leaf == rt) { RT6_TRACE("walker %p adjusted by delroute\n", w); - w->leaf = rt->dst.rt6_next; + w->leaf = rcu_dereference_protected(rt->dst.rt6_next, + lockdep_is_held(&table->tb6_lock)); if (!w->leaf) w->state = FWS_U; } } read_unlock(&net->ipv6.fib6_walker_lock); - rt->dst.rt6_next = NULL; - /* If it was last route, expunge its radix tree node */ - if (!fn->leaf) { + if (!rcu_access_pointer(fn->leaf)) { fn->fn_flags &= ~RTN_RTINFO; net->ipv6.rt6_stats->fib_route_nodes--; - fn = fib6_repair_tree(net, fn); + fn = fib6_repair_tree(net, table, fn); } fib6_purge_rt(rt, fn, net); @@ -1616,12 +1683,15 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, rt6_release(rt); } +/* Need to own table->tb6_lock */ int fib6_del(struct rt6_info *rt, struct nl_info *info) { struct fib6_node *fn = rcu_dereference_protected(rt->rt6i_node, lockdep_is_held(&rt->rt6i_table->tb6_lock)); + struct fib6_table *table = rt->rt6i_table; struct net *net = info->nl_net; - struct rt6_info **rtp; + struct rt6_info __rcu **rtp; + struct rt6_info __rcu **rtp_next; #if RT6_DEBUG >= 2 if (rt->dst.obsolete > 0) { @@ -1642,11 +1712,14 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info) * Walk the leaf entries looking for ourself */ - for (rtp = &fn->leaf; *rtp; rtp = &(*rtp)->dst.rt6_next) { - if (*rtp == rt) { - fib6_del_route(fn, rtp, info); + for (rtp = &fn->leaf; *rtp; rtp = rtp_next) { + struct rt6_info *cur = rcu_dereference_protected(*rtp, + lockdep_is_held(&table->tb6_lock)); + if (rt == cur) { + fib6_del_route(table, fn, rtp, info); return 0; } + rtp_next = &cur->dst.rt6_next; } return -ENOENT; } @@ -1673,11 +1746,13 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info) * 0 -> walk is complete. * >0 -> walk is incomplete (i.e. suspended) * <0 -> walk is terminated by an error. + * + * This function is called with tb6_lock held. */ static int fib6_walk_continue(struct fib6_walker *w) { - struct fib6_node *fn, *pn; + struct fib6_node *fn, *pn, *left, *right; /* w->root should always be table->tb6_root */ WARN_ON_ONCE(!(w->root->fn_flags & RTN_TL_ROOT)); @@ -1697,20 +1772,22 @@ static int fib6_walk_continue(struct fib6_walker *w) w->state = FWS_L; #endif case FWS_L: - if (fn->left) { - w->node = fn->left; + left = rcu_dereference_protected(fn->left, 1); + if (left) { + w->node = left; w->state = FWS_INIT; continue; } w->state = FWS_R; case FWS_R: - if (fn->right) { - w->node = fn->right; + right = rcu_dereference_protected(fn->right, 1); + if (right) { + w->node = right; w->state = FWS_INIT; continue; } w->state = FWS_C; - w->leaf = fn->leaf; + w->leaf = rcu_dereference_protected(fn->leaf, 1); case FWS_C: if (w->leaf && fn->fn_flags & RTN_RTINFO) { int err; @@ -1732,7 +1809,9 @@ skip: case FWS_U: if (fn == w->root) return 0; - pn = fn->parent; + pn = rcu_dereference_protected(fn->parent, 1); + left = rcu_dereference_protected(pn->left, 1); + right = rcu_dereference_protected(pn->right, 1); w->node = pn; #ifdef CONFIG_IPV6_SUBTREES if (FIB6_SUBTREE(pn) == fn) { @@ -1741,13 +1820,13 @@ skip: continue; } #endif - if (pn->left == fn) { + if (left == fn) { w->state = FWS_R; continue; } - if (pn->right == fn) { + if (right == fn) { w->state = FWS_C; - w->leaf = w->node->leaf; + w->leaf = rcu_dereference_protected(w->node->leaf, 1); continue; } #if RT6_DEBUG >= 2 @@ -1790,7 +1869,7 @@ static int fib6_clean_node(struct fib6_walker *w) return 0; } - for (rt = w->leaf; rt; rt = rt->dst.rt6_next) { + for_each_fib6_walker_rt(w) { res = c->func(rt, c->arg); if (res < 0) { w->leaf = rt; @@ -1850,10 +1929,10 @@ static void __fib6_clean_all(struct net *net, for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { head = &net->ipv6.fib_table_hash[h]; hlist_for_each_entry_rcu(table, head, tb6_hlist) { - write_lock_bh(&table->tb6_lock); + spin_lock_bh(&table->tb6_lock); fib6_clean_tree(net, &table->tb6_root, func, sernum, arg); - write_unlock_bh(&table->tb6_lock); + spin_unlock_bh(&table->tb6_lock); } } rcu_read_unlock(); @@ -1967,7 +2046,8 @@ static int __net_init fib6_net_init(struct net *net) goto out_fib_table_hash; net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN; - net->ipv6.fib6_main_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry; + rcu_assign_pointer(net->ipv6.fib6_main_tbl->tb6_root.leaf, + net->ipv6.ip6_null_entry); net->ipv6.fib6_main_tbl->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; inet_peer_base_init(&net->ipv6.fib6_main_tbl->tb6_peers); @@ -1978,7 +2058,8 @@ static int __net_init fib6_net_init(struct net *net) if (!net->ipv6.fib6_local_tbl) goto out_fib6_main_tbl; net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL; - net->ipv6.fib6_local_tbl->tb6_root.leaf = net->ipv6.ip6_null_entry; + rcu_assign_pointer(net->ipv6.fib6_local_tbl->tb6_root.leaf, + net->ipv6.ip6_null_entry); net->ipv6.fib6_local_tbl->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO; inet_peer_base_init(&net->ipv6.fib6_local_tbl->tb6_peers); @@ -2108,7 +2189,9 @@ static int ipv6_route_yield(struct fib6_walker *w) return 1; do { - iter->w.leaf = iter->w.leaf->dst.rt6_next; + iter->w.leaf = rcu_dereference_protected( + iter->w.leaf->dst.rt6_next, + lockdep_is_held(&iter->tbl->tb6_lock)); iter->skip--; if (!iter->skip && iter->w.leaf) return 1; @@ -2173,7 +2256,7 @@ static void *ipv6_route_seq_next(struct seq_file *seq, void *v, loff_t *pos) if (!v) goto iter_table; - n = ((struct rt6_info *)v)->dst.rt6_next; + n = rcu_dereference(((struct rt6_info *)v)->dst.rt6_next); if (n) { ++*pos; return n; @@ -2181,9 +2264,9 @@ static void *ipv6_route_seq_next(struct seq_file *seq, void *v, loff_t *pos) iter_table: ipv6_route_check_sernum(iter); - read_lock(&iter->tbl->tb6_lock); + spin_lock_bh(&iter->tbl->tb6_lock); r = fib6_walk_continue(&iter->w); - read_unlock(&iter->tbl->tb6_lock); + spin_unlock_bh(&iter->tbl->tb6_lock); if (r > 0) { if (v) ++*pos; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 24b80f43bbfb..cf44d0994b1e 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -488,7 +488,7 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match, } /* - * Route lookup. Any table->tb6_lock is implied. + * Route lookup. rcu_read_lock() should be held. */ static inline struct rt6_info *rt6_device_match(struct net *net, @@ -503,7 +503,7 @@ static inline struct rt6_info *rt6_device_match(struct net *net, if (!oif && ipv6_addr_any(saddr)) goto out; - for (sprt = rt; sprt; sprt = sprt->dst.rt6_next) { + for (sprt = rt; sprt; sprt = rcu_dereference(sprt->dst.rt6_next)) { struct net_device *dev = sprt->dst.dev; if (oif) { @@ -722,7 +722,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn, match = NULL; cont = NULL; - for (rt = rr_head; rt; rt = rt->dst.rt6_next) { + for (rt = rr_head; rt; rt = rcu_dereference(rt->dst.rt6_next)) { if (rt->rt6i_metric != metric) { cont = rt; break; @@ -731,7 +731,8 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn, match = find_match(rt, oif, strict, &mpri, match, do_rr); } - for (rt = leaf; rt && rt != rr_head; rt = rt->dst.rt6_next) { + for (rt = leaf; rt && rt != rr_head; + rt = rcu_dereference(rt->dst.rt6_next)) { if (rt->rt6i_metric != metric) { cont = rt; break; @@ -743,7 +744,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn, if (match || !cont) return match; - for (rt = cont; rt; rt = rt->dst.rt6_next) + for (rt = cont; rt; rt = rcu_dereference(rt->dst.rt6_next)) match = find_match(rt, oif, strict, &mpri, match, do_rr); return match; @@ -752,7 +753,7 @@ static struct rt6_info *find_rr_leaf(struct fib6_node *fn, static struct rt6_info *rt6_select(struct net *net, struct fib6_node *fn, int oif, int strict) { - struct rt6_info *leaf = fn->leaf; + struct rt6_info *leaf = rcu_dereference(fn->leaf); struct rt6_info *match, *rt0; bool do_rr = false; int key_plen; @@ -760,9 +761,9 @@ static struct rt6_info *rt6_select(struct net *net, struct fib6_node *fn, if (!leaf) return net->ipv6.ip6_null_entry; - rt0 = fn->rr_ptr; + rt0 = rcu_dereference(fn->rr_ptr); if (!rt0) - fn->rr_ptr = rt0 = leaf; + rt0 = leaf; /* Double check to make sure fn is not an intermediate node * and fn->leaf does not points to its child's leaf @@ -781,14 +782,19 @@ static struct rt6_info *rt6_select(struct net *net, struct fib6_node *fn, &do_rr); if (do_rr) { - struct rt6_info *next = rt0->dst.rt6_next; + struct rt6_info *next = rcu_dereference(rt0->dst.rt6_next); /* no entries matched; do round-robin */ if (!next || next->rt6i_metric != rt0->rt6i_metric) next = leaf; - if (next != rt0) - fn->rr_ptr = next; + if (next != rt0) { + spin_lock_bh(&leaf->rt6i_table->tb6_lock); + /* make sure next is not being deleted from the tree */ + if (next->rt6i_node) + rcu_assign_pointer(fn->rr_ptr, next); + spin_unlock_bh(&leaf->rt6i_table->tb6_lock); + } } return match ? match : net->ipv6.ip6_null_entry; @@ -878,13 +884,14 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, static struct fib6_node* fib6_backtrack(struct fib6_node *fn, struct in6_addr *saddr) { - struct fib6_node *pn; + struct fib6_node *pn, *sn; while (1) { if (fn->fn_flags & RTN_TL_ROOT) return NULL; - pn = fn->parent; - if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) - fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); + pn = rcu_dereference(fn->parent); + sn = FIB6_SUBTREE(pn); + if (sn && sn != fn) + fn = fib6_lookup(sn, NULL, saddr); else fn = pn; if (fn->fn_flags & RTN_RTINFO) @@ -916,13 +923,19 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net, struct rt6_info *rt, *rt_cache; struct fib6_node *fn; - read_lock_bh(&table->tb6_lock); + rcu_read_lock(); fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); restart: - rt = fn->leaf; - rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags); - if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) - rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags); + rt = rcu_dereference(fn->leaf); + if (!rt) { + rt = net->ipv6.ip6_null_entry; + } else { + rt = rt6_device_match(net, rt, &fl6->saddr, + fl6->flowi6_oif, flags); + if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0) + rt = rt6_multipath_select(rt, fl6, + fl6->flowi6_oif, flags); + } if (rt == net->ipv6.ip6_null_entry) { fn = fib6_backtrack(fn, &fl6->saddr); if (fn) @@ -936,7 +949,7 @@ restart: if (ip6_hold_safe(net, &rt, true)) dst_use_noref(&rt->dst, jiffies); - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); @@ -990,9 +1003,9 @@ static int __ip6_ins_rt(struct rt6_info *rt, struct nl_info *info, struct fib6_table *table; table = rt->rt6i_table; - write_lock_bh(&table->tb6_lock); + spin_lock_bh(&table->tb6_lock); err = fib6_add(&table->tb6_root, rt, info, mxc, extack); - write_unlock_bh(&table->tb6_lock); + spin_unlock_bh(&table->tb6_lock); return err; } @@ -1090,7 +1103,7 @@ static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) return pcpu_rt; } -/* It should be called with read_lock_bh(&tb6_lock) acquired */ +/* It should be called with rcu_read_lock() acquired */ static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) { struct rt6_info *pcpu_rt, **p; @@ -1632,7 +1645,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, if (net->ipv6.devconf_all->forwarding == 0) strict |= RT6_LOOKUP_F_REACHABLE; - read_lock_bh(&table->tb6_lock); + rcu_read_lock(); fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); saved_fn = fn; @@ -1662,7 +1675,7 @@ redo_rt6_select: rt = rt_cache; if (rt == net->ipv6.ip6_null_entry) { - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); dst_hold(&rt->dst); trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); return rt; @@ -1671,7 +1684,7 @@ redo_rt6_select: dst_use_noref(&rt->dst, jiffies); rt6_dst_from_metrics_check(rt); } - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); return rt; } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && @@ -1687,11 +1700,11 @@ redo_rt6_select: if (ip6_hold_safe(net, &rt, true)) { dst_use_noref(&rt->dst, jiffies); } else { - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); uncached_rt = rt; goto uncached_rt_out; } - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); uncached_rt = ip6_rt_cache_alloc(rt, &fl6->daddr, NULL); dst_release(&rt->dst); @@ -1719,7 +1732,7 @@ uncached_rt_out: pcpu_rt = rt6_get_pcpu_route(rt); if (pcpu_rt) { - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); } else { /* atomic_inc_not_zero() is needed when using rcu */ if (atomic_inc_not_zero(&rt->rt6i_ref)) { @@ -1730,12 +1743,12 @@ uncached_rt_out: * No dst_hold() on rt is needed because grabbing * rt->rt6i_ref makes sure rt can't be released. */ - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); pcpu_rt = rt6_make_pcpu_route(rt); rt6_release(rt); } else { /* rt is already removed from tree */ - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); pcpu_rt = net->ipv6.ip6_null_entry; dst_hold(&pcpu_rt->dst); } @@ -2131,10 +2144,10 @@ static struct rt6_info *__ip6_route_redirect(struct net *net, * routes. */ - read_lock_bh(&table->tb6_lock); + rcu_read_lock(); fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr); restart: - for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { + for_each_fib6_node_rt_rcu(fn) { if (rt6_check_expired(rt)) continue; if (rt->dst.error) @@ -2179,7 +2192,7 @@ restart: out: ip6_hold_safe(net, &rt, true); - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); return rt; @@ -2778,9 +2791,9 @@ static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info) } table = rt->rt6i_table; - write_lock_bh(&table->tb6_lock); + spin_lock_bh(&table->tb6_lock); err = fib6_del(rt, info); - write_unlock_bh(&table->tb6_lock); + spin_unlock_bh(&table->tb6_lock); out: ip6_rt_put(rt); @@ -2806,7 +2819,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg) if (rt == net->ipv6.ip6_null_entry) goto out_put; table = rt->rt6i_table; - write_lock_bh(&table->tb6_lock); + spin_lock_bh(&table->tb6_lock); if (rt->rt6i_nsiblings && cfg->fc_delete_all_nh) { struct rt6_info *sibling, *next_sibling; @@ -2836,7 +2849,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg) err = fib6_del(rt, info); out_unlock: - write_unlock_bh(&table->tb6_lock); + spin_unlock_bh(&table->tb6_lock); out_put: ip6_rt_put(rt); @@ -2861,7 +2874,7 @@ static int ip6_route_del(struct fib6_config *cfg, return err; } - read_lock_bh(&table->tb6_lock); + rcu_read_lock(); fn = fib6_locate(&table->tb6_root, &cfg->fc_dst, cfg->fc_dst_len, @@ -2869,7 +2882,7 @@ static int ip6_route_del(struct fib6_config *cfg, !(cfg->fc_flags & RTF_CACHE)); if (fn) { - for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { + for_each_fib6_node_rt_rcu(fn) { if (cfg->fc_flags & RTF_CACHE) { rt_cache = rt6_find_cached_rt(rt, &cfg->fc_dst, &cfg->fc_src); @@ -2890,7 +2903,7 @@ static int ip6_route_del(struct fib6_config *cfg, continue; if (!dst_hold_safe(&rt->dst)) break; - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); /* if gateway was specified only delete the one hop */ if (cfg->fc_flags & RTF_GATEWAY) @@ -2899,7 +2912,7 @@ static int ip6_route_del(struct fib6_config *cfg, return __ip6_del_rt_siblings(rt, cfg); } } - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); return err; } @@ -3074,12 +3087,12 @@ static struct rt6_info *rt6_get_route_info(struct net *net, if (!table) return NULL; - read_lock_bh(&table->tb6_lock); + rcu_read_lock(); fn = fib6_locate(&table->tb6_root, prefix, prefixlen, NULL, 0, true); if (!fn) goto out; - for (rt = fn->leaf; rt; rt = rt->dst.rt6_next) { + for_each_fib6_node_rt_rcu(fn) { if (rt->dst.dev->ifindex != ifindex) continue; if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) @@ -3090,7 +3103,7 @@ static struct rt6_info *rt6_get_route_info(struct net *net, break; } out: - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); return rt; } @@ -3136,8 +3149,8 @@ struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_dev if (!table) return NULL; - read_lock_bh(&table->tb6_lock); - for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { + rcu_read_lock(); + for_each_fib6_node_rt_rcu(&table->tb6_root) { if (dev == rt->dst.dev && ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && ipv6_addr_equal(&rt->rt6i_gateway, addr)) @@ -3145,7 +3158,7 @@ struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, struct net_dev } if (rt) ip6_hold_safe(NULL, &rt, false); - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); return rt; } @@ -3183,20 +3196,20 @@ static void __rt6_purge_dflt_routers(struct fib6_table *table) struct rt6_info *rt; restart: - read_lock_bh(&table->tb6_lock); - for (rt = table->tb6_root.leaf; rt; rt = rt->dst.rt6_next) { + rcu_read_lock(); + for_each_fib6_node_rt_rcu(&table->tb6_root) { if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF) && (!rt->rt6i_idev || rt->rt6i_idev->cnf.accept_ra != 2)) { if (dst_hold_safe(&rt->dst)) { - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); ip6_del_rt(rt); } else { - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); } goto restart; } } - read_unlock_bh(&table->tb6_lock); + rcu_read_unlock(); table->flags &= ~RT6_TABLE_HAS_DFLT_ROUTER; } -- cgit v1.2.3-59-g8ed1b From 81eb8447daae3b62247aa66bb17b82f8fef68249 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 6 Oct 2017 12:06:11 -0700 Subject: ipv6: take care of rt6_stats Currently, most of the rt6_stats are not hooked up correctly. As the last part of this patch series, hook up all existing rt6_stats and add one new stat fib_rt_uncache to indicate the number of routes in the uncached list. For details of the stats, please refer to the comments added in include/net/ip6_fib.h. Note: fib_rt_alloc and fib_rt_uncache are not guaranteed to be modified under a lock. So atomic_t is used for them. Signed-off-by: Wei Wang Signed-off-by: Martin KaFai Lau Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 15 +++++++++------ net/ipv6/ip6_fib.c | 42 ++++++++++++++++++++++++------------------ net/ipv6/route.c | 16 ++++++++++++++-- 3 files changed, 47 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 0b438b9bcb10..10c913816032 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -297,12 +297,15 @@ struct fib6_walker { }; struct rt6_statistics { - __u32 fib_nodes; - __u32 fib_route_nodes; - __u32 fib_rt_alloc; /* permanent routes */ - __u32 fib_rt_entries; /* rt entries in table */ - __u32 fib_rt_cache; /* cache routes */ - __u32 fib_discarded_routes; + __u32 fib_nodes; /* all fib6 nodes */ + __u32 fib_route_nodes; /* intermediate nodes */ + __u32 fib_rt_entries; /* rt entries in fib table */ + __u32 fib_rt_cache; /* cached rt entries in exception table */ + __u32 fib_discarded_routes; /* total number of routes delete */ + + /* The following stats are not protected by any lock */ + atomic_t fib_rt_alloc; /* total number of routes alloced */ + atomic_t fib_rt_uncache; /* rt entries in uncached list */ }; #define RTN_TL_ROOT 0x0001 diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 3f95908b39c3..52a29ba32928 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -149,18 +149,21 @@ static __be32 addr_bit_set(const void *token, int fn_bit) addr[fn_bit >> 5]; } -static struct fib6_node *node_alloc(void) +static struct fib6_node *node_alloc(struct net *net) { struct fib6_node *fn; fn = kmem_cache_zalloc(fib6_node_kmem, GFP_ATOMIC); + if (fn) + net->ipv6.rt6_stats->fib_nodes++; return fn; } -static void node_free_immediate(struct fib6_node *fn) +static void node_free_immediate(struct net *net, struct fib6_node *fn) { kmem_cache_free(fib6_node_kmem, fn); + net->ipv6.rt6_stats->fib_nodes--; } static void node_free_rcu(struct rcu_head *head) @@ -170,9 +173,10 @@ static void node_free_rcu(struct rcu_head *head) kmem_cache_free(fib6_node_kmem, fn); } -static void node_free(struct fib6_node *fn) +static void node_free(struct net *net, struct fib6_node *fn) { call_rcu(&fn->rcu, node_free_rcu); + net->ipv6.rt6_stats->fib_nodes--; } void rt6_free_pcpu(struct rt6_info *non_pcpu_rt) @@ -583,7 +587,8 @@ out: * node. */ -static struct fib6_node *fib6_add_1(struct fib6_table *table, +static struct fib6_node *fib6_add_1(struct net *net, + struct fib6_table *table, struct fib6_node *root, struct in6_addr *addr, int plen, int offset, int allow_create, @@ -675,7 +680,7 @@ static struct fib6_node *fib6_add_1(struct fib6_table *table, * Create new leaf node without children. */ - ln = node_alloc(); + ln = node_alloc(net); if (!ln) return ERR_PTR(-ENOMEM); @@ -716,14 +721,14 @@ insert_above: * (new leaf node)[ln] (old node)[fn] */ if (plen > bit) { - in = node_alloc(); - ln = node_alloc(); + in = node_alloc(net); + ln = node_alloc(net); if (!in || !ln) { if (in) - node_free_immediate(in); + node_free_immediate(net, in); if (ln) - node_free_immediate(ln); + node_free_immediate(net, ln); return ERR_PTR(-ENOMEM); } @@ -768,7 +773,7 @@ insert_above: * (old node)[fn] NULL */ - ln = node_alloc(); + ln = node_alloc(net); if (!ln) return ERR_PTR(-ENOMEM); @@ -1065,6 +1070,7 @@ add: fn->rr_ptr = NULL; rt6_release(iter); nsiblings--; + info->nl_net->ipv6.rt6_stats->fib_rt_entries--; } else { ins = &iter->dst.rt6_next; } @@ -1140,7 +1146,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, if (!allow_create && !replace_required) pr_warn("RTM_NEWROUTE with no NLM_F_CREATE or NLM_F_REPLACE\n"); - fn = fib6_add_1(table, root, + fn = fib6_add_1(info->nl_net, table, root, &rt->rt6i_dst.addr, rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst), allow_create, replace_required, extack); @@ -1170,7 +1176,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, */ /* Create subtree root node */ - sfn = node_alloc(); + sfn = node_alloc(info->nl_net); if (!sfn) goto failure; @@ -1181,8 +1187,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, /* Now add the first leaf node to new subtree */ - sn = fib6_add_1(table, sfn, &rt->rt6i_src.addr, - rt->rt6i_src.plen, + sn = fib6_add_1(info->nl_net, table, sfn, + &rt->rt6i_src.addr, rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src), allow_create, replace_required, extack); @@ -1191,7 +1197,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, root, and then (in failure) stale node in main tree. */ - node_free_immediate(sfn); + node_free_immediate(info->nl_net, sfn); err = PTR_ERR(sn); goto failure; } @@ -1200,8 +1206,8 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, rcu_assign_pointer(sfn->parent, fn); rcu_assign_pointer(fn->subtree, sfn); } else { - sn = fib6_add_1(table, FIB6_SUBTREE(fn), &rt->rt6i_src.addr, - rt->rt6i_src.plen, + sn = fib6_add_1(info->nl_net, table, FIB6_SUBTREE(fn), + &rt->rt6i_src.addr, rt->rt6i_src.plen, offsetof(struct rt6_info, rt6i_src), allow_create, replace_required, extack); @@ -1609,7 +1615,7 @@ static struct fib6_node *fib6_repair_tree(struct net *net, } read_unlock(&net->ipv6.fib6_walker_lock); - node_free(fn); + node_free(net, fn); if (pn->fn_flags & RTN_RTINFO || FIB6_SUBTREE(pn)) return pn; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index cf44d0994b1e..399d1bceec4a 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -143,9 +143,11 @@ static void rt6_uncached_list_del(struct rt6_info *rt) { if (!list_empty(&rt->rt6i_uncached)) { struct uncached_list *ul = rt->rt6i_uncached_list; + struct net *net = dev_net(rt->dst.dev); spin_lock_bh(&ul->lock); list_del(&rt->rt6i_uncached); + atomic_dec(&net->ipv6.rt6_stats->fib_rt_uncache); spin_unlock_bh(&ul->lock); } } @@ -359,8 +361,10 @@ static struct rt6_info *__ip6_dst_alloc(struct net *net, struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 1, DST_OBSOLETE_FORCE_CHK, flags); - if (rt) + if (rt) { rt6_info_init(rt); + atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc); + } return rt; } @@ -1156,6 +1160,8 @@ static DEFINE_SPINLOCK(rt6_exception_lock); static void rt6_remove_exception(struct rt6_exception_bucket *bucket, struct rt6_exception *rt6_ex) { + struct net *net = dev_net(rt6_ex->rt6i->dst.dev); + if (!bucket || !rt6_ex) return; rt6_ex->rt6i->rt6i_node = NULL; @@ -1164,6 +1170,7 @@ static void rt6_remove_exception(struct rt6_exception_bucket *bucket, kfree_rcu(rt6_ex, rcu); WARN_ON_ONCE(!bucket->depth); bucket->depth--; + net->ipv6.rt6_stats->fib_rt_cache--; } /* Remove oldest rt6_ex in bucket and free the memory @@ -1270,6 +1277,7 @@ __rt6_find_exception_rcu(struct rt6_exception_bucket **bucket, static int rt6_insert_exception(struct rt6_info *nrt, struct rt6_info *ort) { + struct net *net = dev_net(ort->dst.dev); struct rt6_exception_bucket *bucket; struct in6_addr *src_key = NULL; struct rt6_exception *rt6_ex; @@ -1339,6 +1347,7 @@ static int rt6_insert_exception(struct rt6_info *nrt, nrt->rt6i_node = ort->rt6i_node; hlist_add_head_rcu(&rt6_ex->hlist, &bucket->chain); bucket->depth++; + net->ipv6.rt6_stats->fib_rt_cache++; if (bucket->depth > FIB6_MAX_DEPTH) rt6_exception_remove_oldest(bucket); @@ -1714,6 +1723,7 @@ redo_rt6_select: * No need for another dst_hold() */ rt6_uncached_list_add(uncached_rt); + atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); } else { uncached_rt = net->ipv6.ip6_null_entry; dst_hold(&uncached_rt->dst); @@ -1894,6 +1904,7 @@ struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_ori DST_OBSOLETE_NONE, 0); if (rt) { rt6_info_init(rt); + atomic_inc(&net->ipv6.rt6_stats->fib_rt_alloc); new = &rt->dst; new->__use = 1; @@ -2341,6 +2352,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, * do proper release of the net_device */ rt6_uncached_list_add(rt); + atomic_inc(&net->ipv6.rt6_stats->fib_rt_uncache); dst = xfrm_lookup(net, &rt->dst, flowi6_to_flowi(fl6), NULL, 0); @@ -4422,7 +4434,7 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", net->ipv6.rt6_stats->fib_nodes, net->ipv6.rt6_stats->fib_route_nodes, - net->ipv6.rt6_stats->fib_rt_alloc, + atomic_read(&net->ipv6.rt6_stats->fib_rt_alloc), net->ipv6.rt6_stats->fib_rt_entries, net->ipv6.rt6_stats->fib_rt_cache, dst_entries_get_slow(&net->ipv6.ip6_dst_ops), -- cgit v1.2.3-59-g8ed1b From bdc476413dcdb5c38a7dec90fb2bca327021273a Mon Sep 17 00:00:00 2001 From: Amine Kherbouche Date: Wed, 4 Oct 2017 19:35:57 +0200 Subject: ip_tunnel: add mpls over gre support This commit introduces the MPLSoGRE support (RFC 4023), using ip tunnel API by simply adding ipgre_tunnel_encap_(add|del)_mpls_ops() and the new tunnel type TUNNEL_ENCAP_MPLS. Signed-off-by: Amine Kherbouche Signed-off-by: David S. Miller --- include/uapi/linux/if_tunnel.h | 1 + net/mpls/af_mpls.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h index 2e520883c054..a2f48c01365e 100644 --- a/include/uapi/linux/if_tunnel.h +++ b/include/uapi/linux/if_tunnel.h @@ -84,6 +84,7 @@ enum tunnel_encap_types { TUNNEL_ENCAP_NONE, TUNNEL_ENCAP_FOU, TUNNEL_ENCAP_GUE, + TUNNEL_ENCAP_MPLS, }; #define TUNNEL_ENCAP_FLAG_CSUM (1<<0) diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index c5b9ce41d66f..9745e8f69810 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #if IS_ENABLED(CONFIG_IPV6) #include @@ -39,6 +40,36 @@ static int one = 1; static int label_limit = (1 << 20) - 1; static int ttl_max = 255; +#if IS_ENABLED(CONFIG_NET_IP_TUNNEL) +size_t ipgre_mpls_encap_hlen(struct ip_tunnel_encap *e) +{ + return sizeof(struct mpls_shim_hdr); +} + +static const struct ip_tunnel_encap_ops mpls_iptun_ops = { + .encap_hlen = ipgre_mpls_encap_hlen, +}; + +static int ipgre_tunnel_encap_add_mpls_ops(void) +{ + return ip_tunnel_encap_add_ops(&mpls_iptun_ops, TUNNEL_ENCAP_MPLS); +} + +static void ipgre_tunnel_encap_del_mpls_ops(void) +{ + ip_tunnel_encap_del_ops(&mpls_iptun_ops, TUNNEL_ENCAP_MPLS); +} +#else +static int ipgre_tunnel_encap_add_mpls_ops(void) +{ + return 0; +} + +static void ipgre_tunnel_encap_del_mpls_ops(void) +{ +} +#endif + static void rtmsg_lfib(int event, u32 label, struct mpls_route *rt, struct nlmsghdr *nlh, struct net *net, u32 portid, unsigned int nlm_flags); @@ -2485,6 +2516,10 @@ static int __init mpls_init(void) 0); rtnl_register(PF_MPLS, RTM_GETNETCONF, mpls_netconf_get_devconf, mpls_netconf_dump_devconf, 0); + err = ipgre_tunnel_encap_add_mpls_ops(); + if (err) + pr_err("Can't add mpls over gre tunnel ops\n"); + err = 0; out: return err; @@ -2502,6 +2537,7 @@ static void __exit mpls_exit(void) dev_remove_pack(&mpls_packet_type); unregister_netdevice_notifier(&mpls_dev_notifier); unregister_pernet_subsys(&mpls_net_ops); + ipgre_tunnel_encap_del_mpls_ops(); } module_exit(mpls_exit); -- cgit v1.2.3-59-g8ed1b From 97562633bcbac4a07d605ae628d7655fa71caaf5 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Thu, 5 Oct 2017 09:19:19 -0700 Subject: bpf: perf event change needed for subsequent bpf helpers This patch does not impact existing functionalities. It contains the changes in perf event area needed for subsequent bpf_perf_event_read_value and bpf_perf_prog_read_value helpers. Signed-off-by: Yonghong Song Acked-by: Peter Zijlstra (Intel) Signed-off-by: David S. Miller --- include/linux/perf_event.h | 7 +++++-- kernel/bpf/arraymap.c | 2 +- kernel/events/core.c | 15 +++++++++++++-- kernel/trace/bpf_trace.c | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 8e22f24ded6a..79b18a20cf5d 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -806,6 +806,7 @@ struct perf_output_handle { struct bpf_perf_event_data_kern { struct pt_regs *regs; struct perf_sample_data *data; + struct perf_event *event; }; #ifdef CONFIG_CGROUP_PERF @@ -884,7 +885,8 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, void *context); extern void perf_pmu_migrate_context(struct pmu *pmu, int src_cpu, int dst_cpu); -int perf_event_read_local(struct perf_event *event, u64 *value); +int perf_event_read_local(struct perf_event *event, u64 *value, + u64 *enabled, u64 *running); extern u64 perf_event_read_value(struct perf_event *event, u64 *enabled, u64 *running); @@ -1286,7 +1288,8 @@ static inline const struct perf_event_attr *perf_event_attrs(struct perf_event * { return ERR_PTR(-EINVAL); } -static inline int perf_event_read_local(struct perf_event *event, u64 *value) +static inline int perf_event_read_local(struct perf_event *event, u64 *value, + u64 *enabled, u64 *running) { return -EINVAL; } diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 98c0f00c3f5e..68d866628be0 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -492,7 +492,7 @@ static void *perf_event_fd_array_get_ptr(struct bpf_map *map, ee = ERR_PTR(-EOPNOTSUPP); event = perf_file->private_data; - if (perf_event_read_local(event, &value) == -EOPNOTSUPP) + if (perf_event_read_local(event, &value, NULL, NULL) == -EOPNOTSUPP) goto err_out; ee = bpf_event_entry_gen(perf_file, map_file); diff --git a/kernel/events/core.c b/kernel/events/core.c index 6bc21e202ae4..902149f05381 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3684,10 +3684,12 @@ static inline u64 perf_event_count(struct perf_event *event) * will not be local and we cannot read them atomically * - must not have a pmu::count method */ -int perf_event_read_local(struct perf_event *event, u64 *value) +int perf_event_read_local(struct perf_event *event, u64 *value, + u64 *enabled, u64 *running) { unsigned long flags; int ret = 0; + u64 now; /* * Disabling interrupts avoids all counter scheduling (context @@ -3718,13 +3720,21 @@ int perf_event_read_local(struct perf_event *event, u64 *value) goto out; } + now = event->shadow_ctx_time + perf_clock(); + if (enabled) + *enabled = now - event->tstamp_enabled; /* * If the event is currently on this CPU, its either a per-task event, * or local to this CPU. Furthermore it means its ACTIVE (otherwise * oncpu == -1). */ - if (event->oncpu == smp_processor_id()) + if (event->oncpu == smp_processor_id()) { event->pmu->read(event); + if (running) + *running = now - event->tstamp_running; + } else if (running) { + *running = event->total_time_running; + } *value = local64_read(&event->count); out: @@ -8072,6 +8082,7 @@ static void bpf_overflow_handler(struct perf_event *event, struct bpf_perf_event_data_kern ctx = { .data = data, .regs = regs, + .event = event, }; int ret = 0; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index dc498b605d5d..95888ae6c263 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -275,7 +275,7 @@ BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) if (!ee) return -ENOENT; - err = perf_event_read_local(ee->event, &value); + err = perf_event_read_local(ee->event, &value, NULL, NULL); /* * this api is ugly since we miss [-22..-2] range of valid * counter values, but that's uapi -- cgit v1.2.3-59-g8ed1b From 908432ca84fc229e906ba164219e9ad0fe56f755 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Thu, 5 Oct 2017 09:19:20 -0700 Subject: bpf: add helper bpf_perf_event_read_value for perf event array map Hardware pmu counters are limited resources. When there are more pmu based perf events opened than available counters, kernel will multiplex these events so each event gets certain percentage (but not 100%) of the pmu time. In case that multiplexing happens, the number of samples or counter value will not reflect the case compared to no multiplexing. This makes comparison between different runs difficult. Typically, the number of samples or counter value should be normalized before comparing to other experiments. The typical normalization is done like: normalized_num_samples = num_samples * time_enabled / time_running normalized_counter_value = counter_value * time_enabled / time_running where time_enabled is the time enabled for event and time_running is the time running for event since last normalization. This patch adds helper bpf_perf_event_read_value for kprobed based perf event array map, to read perf counter and enabled/running time. The enabled/running time is accumulated since the perf event open. To achieve scaling factor between two bpf invocations, users can can use cpu_id as the key (which is typical for perf array usage model) to remember the previous value and do the calculation inside the bpf program. Signed-off-by: Yonghong Song Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 21 +++++++++++++++++++-- kernel/bpf/verifier.c | 4 +++- kernel/trace/bpf_trace.c | 45 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 63 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 6082faf5fd2a..7b57a212c7d7 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -641,6 +641,14 @@ union bpf_attr { * @xdp_md: pointer to xdp_md * @delta: An positive/negative integer to be added to xdp_md.data_meta * Return: 0 on success or negative on error + * + * int bpf_perf_event_read_value(map, flags, buf, buf_size) + * read perf event counter value and perf event enabled/running time + * @map: pointer to perf_event_array map + * @flags: index of event in the map or bitmask flags + * @buf: buf to fill + * @buf_size: size of the buf + * Return: 0 on success or negative error code */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -697,7 +705,8 @@ union bpf_attr { FN(redirect_map), \ FN(sk_redirect_map), \ FN(sock_map_update), \ - FN(xdp_adjust_meta), + FN(xdp_adjust_meta), \ + FN(perf_event_read_value), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -741,7 +750,9 @@ enum bpf_func_id { #define BPF_F_ZERO_CSUM_TX (1ULL << 1) #define BPF_F_DONT_FRAGMENT (1ULL << 2) -/* BPF_FUNC_perf_event_output and BPF_FUNC_perf_event_read flags. */ +/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and + * BPF_FUNC_perf_event_read_value flags. + */ #define BPF_F_INDEX_MASK 0xffffffffULL #define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK /* BPF_FUNC_perf_event_output for sk_buff input context. */ @@ -934,4 +945,10 @@ enum { #define TCP_BPF_IW 1001 /* Set TCP initial congestion window */ #define TCP_BPF_SNDCWND_CLAMP 1002 /* Set sndcwnd_clamp */ +struct bpf_perf_event_value { + __u64 counter; + __u64 enabled; + __u64 running; +}; + #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 52b022310f6a..590125e29161 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1552,7 +1552,8 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id) break; case BPF_MAP_TYPE_PERF_EVENT_ARRAY: if (func_id != BPF_FUNC_perf_event_read && - func_id != BPF_FUNC_perf_event_output) + func_id != BPF_FUNC_perf_event_output && + func_id != BPF_FUNC_perf_event_read_value) goto error; break; case BPF_MAP_TYPE_STACK_TRACE: @@ -1595,6 +1596,7 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id) break; case BPF_FUNC_perf_event_read: case BPF_FUNC_perf_event_output: + case BPF_FUNC_perf_event_read_value: if (map->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) goto error; break; diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 95888ae6c263..0be86cc0130e 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -255,14 +255,14 @@ const struct bpf_func_proto *bpf_get_trace_printk_proto(void) return &bpf_trace_printk_proto; } -BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) +static __always_inline int +get_map_perf_counter(struct bpf_map *map, u64 flags, + u64 *value, u64 *enabled, u64 *running) { struct bpf_array *array = container_of(map, struct bpf_array, map); unsigned int cpu = smp_processor_id(); u64 index = flags & BPF_F_INDEX_MASK; struct bpf_event_entry *ee; - u64 value = 0; - int err; if (unlikely(flags & ~(BPF_F_INDEX_MASK))) return -EINVAL; @@ -275,7 +275,15 @@ BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) if (!ee) return -ENOENT; - err = perf_event_read_local(ee->event, &value, NULL, NULL); + return perf_event_read_local(ee->event, value, enabled, running); +} + +BPF_CALL_2(bpf_perf_event_read, struct bpf_map *, map, u64, flags) +{ + u64 value = 0; + int err; + + err = get_map_perf_counter(map, flags, &value, NULL, NULL); /* * this api is ugly since we miss [-22..-2] range of valid * counter values, but that's uapi @@ -293,6 +301,33 @@ static const struct bpf_func_proto bpf_perf_event_read_proto = { .arg2_type = ARG_ANYTHING, }; +BPF_CALL_4(bpf_perf_event_read_value, struct bpf_map *, map, u64, flags, + struct bpf_perf_event_value *, buf, u32, size) +{ + int err = -EINVAL; + + if (unlikely(size != sizeof(struct bpf_perf_event_value))) + goto clear; + err = get_map_perf_counter(map, flags, &buf->counter, &buf->enabled, + &buf->running); + if (unlikely(err)) + goto clear; + return 0; +clear: + memset(buf, 0, size); + return err; +} + +static const struct bpf_func_proto bpf_perf_event_read_value_proto = { + .func = bpf_perf_event_read_value, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_CONST_MAP_PTR, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_PTR_TO_UNINIT_MEM, + .arg4_type = ARG_CONST_SIZE, +}; + static DEFINE_PER_CPU(struct perf_sample_data, bpf_sd); static __always_inline u64 @@ -499,6 +534,8 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id func return &bpf_perf_event_output_proto; case BPF_FUNC_get_stackid: return &bpf_get_stackid_proto; + case BPF_FUNC_perf_event_read_value: + return &bpf_perf_event_read_value_proto; default: return tracing_func_proto(func_id); } -- cgit v1.2.3-59-g8ed1b From 4bebdc7a85aa400c0222b5329861e4ad9252f1e5 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Thu, 5 Oct 2017 09:19:22 -0700 Subject: bpf: add helper bpf_perf_prog_read_value This patch adds helper bpf_perf_prog_read_cvalue for perf event based bpf programs, to read event counter and enabled/running time. The enabled/running time is accumulated since the perf event open. The typical use case for perf event based bpf program is to attach itself to a single event. In such cases, if it is desirable to get scaling factor between two bpf invocations, users can can save the time values in a map, and use the value from the map and the current value to calculate the scaling factor. Signed-off-by: Yonghong Song Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 10 +++++++++- kernel/trace/bpf_trace.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 7b57a212c7d7..5bbbec17aa5a 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -649,6 +649,13 @@ union bpf_attr { * @buf: buf to fill * @buf_size: size of the buf * Return: 0 on success or negative error code + * + * int bpf_perf_prog_read_value(ctx, buf, buf_size) + * read perf prog attached perf event counter and enabled/running time + * @ctx: pointer to ctx + * @buf: buf to fill + * @buf_size: size of the buf + * Return : 0 on success or negative error code */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -706,7 +713,8 @@ union bpf_attr { FN(sk_redirect_map), \ FN(sock_map_update), \ FN(xdp_adjust_meta), \ - FN(perf_event_read_value), + FN(perf_event_read_value), \ + FN(perf_prog_read_value), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 0be86cc0130e..04ea5314f2bc 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -613,6 +613,32 @@ static const struct bpf_func_proto bpf_get_stackid_proto_tp = { .arg3_type = ARG_ANYTHING, }; +BPF_CALL_3(bpf_perf_prog_read_value_tp, struct bpf_perf_event_data_kern *, ctx, + struct bpf_perf_event_value *, buf, u32, size) +{ + int err = -EINVAL; + + if (unlikely(size != sizeof(struct bpf_perf_event_value))) + goto clear; + err = perf_event_read_local(ctx->event, &buf->counter, &buf->enabled, + &buf->running); + if (unlikely(err)) + goto clear; + return 0; +clear: + memset(buf, 0, size); + return err; +} + +static const struct bpf_func_proto bpf_perf_prog_read_value_proto_tp = { + .func = bpf_perf_prog_read_value_tp, + .gpl_only = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_PTR_TO_UNINIT_MEM, + .arg3_type = ARG_CONST_SIZE, +}; + static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id) { switch (func_id) { @@ -620,6 +646,8 @@ static const struct bpf_func_proto *tp_prog_func_proto(enum bpf_func_id func_id) return &bpf_perf_event_output_proto_tp; case BPF_FUNC_get_stackid: return &bpf_get_stackid_proto_tp; + case BPF_FUNC_perf_prog_read_value: + return &bpf_perf_prog_read_value_proto_tp; default: return tracing_func_proto(func_id); } -- cgit v1.2.3-59-g8ed1b From 64237470ddf97b63155fbd272c9e743e01d5f514 Mon Sep 17 00:00:00 2001 From: Lin Zhang Date: Fri, 6 Oct 2017 01:37:29 +0800 Subject: net: phonet: mark header_ops as const Signed-off-by: Lin Zhang Signed-off-by: David S. Miller --- include/linux/if_phonet.h | 2 +- net/phonet/af_phonet.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/if_phonet.h b/include/linux/if_phonet.h index bbcdb0a767d8..a118ee4a8428 100644 --- a/include/linux/if_phonet.h +++ b/include/linux/if_phonet.h @@ -10,5 +10,5 @@ #include -extern struct header_ops phonet_header_ops; +extern const struct header_ops phonet_header_ops; #endif diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index f925753668a7..b12142e55d19 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -149,7 +149,7 @@ static int pn_header_parse(const struct sk_buff *skb, unsigned char *haddr) return 1; } -struct header_ops phonet_header_ops = { +const struct header_ops phonet_header_ops = { .create = pn_header_create, .parse = pn_header_parse, }; -- cgit v1.2.3-59-g8ed1b From 548ec114705bb8f0879a0da12abec17f17a7cc26 Mon Sep 17 00:00:00 2001 From: Lin Zhang Date: Fri, 6 Oct 2017 01:40:35 +0800 Subject: net: phonet: mark phonet_protocol as const The phonet_protocol structs don't need to be written by anyone and so can be marked as const. Signed-off-by: Lin Zhang Signed-off-by: David S. Miller --- include/net/phonet/phonet.h | 6 ++++-- net/phonet/af_phonet.c | 15 ++++++++------- net/phonet/datagram.c | 2 +- net/phonet/pep.c | 2 +- 4 files changed, 14 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/phonet/phonet.h b/include/net/phonet/phonet.h index 039cc29cb4a8..51e1a2a45d02 100644 --- a/include/net/phonet/phonet.h +++ b/include/net/phonet/phonet.h @@ -108,8 +108,10 @@ struct phonet_protocol { int sock_type; }; -int phonet_proto_register(unsigned int protocol, struct phonet_protocol *pp); -void phonet_proto_unregister(unsigned int protocol, struct phonet_protocol *pp); +int phonet_proto_register(unsigned int protocol, + const struct phonet_protocol *pp); +void phonet_proto_unregister(unsigned int protocol, + const struct phonet_protocol *pp); int phonet_sysctl_init(void); void phonet_sysctl_exit(void); diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index b12142e55d19..3b0ef691f5b1 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -35,11 +35,11 @@ #include /* Transport protocol registration */ -static struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly; +static const struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly; -static struct phonet_protocol *phonet_proto_get(unsigned int protocol) +static const struct phonet_protocol *phonet_proto_get(unsigned int protocol) { - struct phonet_protocol *pp; + const struct phonet_protocol *pp; if (protocol >= PHONET_NPROTO) return NULL; @@ -53,7 +53,7 @@ static struct phonet_protocol *phonet_proto_get(unsigned int protocol) return pp; } -static inline void phonet_proto_put(struct phonet_protocol *pp) +static inline void phonet_proto_put(const struct phonet_protocol *pp) { module_put(pp->prot->owner); } @@ -65,7 +65,7 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol, { struct sock *sk; struct pn_sock *pn; - struct phonet_protocol *pnp; + const struct phonet_protocol *pnp; int err; if (!capable(CAP_SYS_ADMIN)) @@ -470,7 +470,7 @@ static struct packet_type phonet_packet_type __read_mostly = { static DEFINE_MUTEX(proto_tab_lock); int __init_or_module phonet_proto_register(unsigned int protocol, - struct phonet_protocol *pp) + const struct phonet_protocol *pp) { int err = 0; @@ -492,7 +492,8 @@ int __init_or_module phonet_proto_register(unsigned int protocol, } EXPORT_SYMBOL(phonet_proto_register); -void phonet_proto_unregister(unsigned int protocol, struct phonet_protocol *pp) +void phonet_proto_unregister(unsigned int protocol, + const struct phonet_protocol *pp) { mutex_lock(&proto_tab_lock); BUG_ON(proto_tab[protocol] != pp); diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c index 5e710435ffa9..b44fb9018fb8 100644 --- a/net/phonet/datagram.c +++ b/net/phonet/datagram.c @@ -195,7 +195,7 @@ static struct proto pn_proto = { .name = "PHONET", }; -static struct phonet_protocol pn_dgram_proto = { +static const struct phonet_protocol pn_dgram_proto = { .ops = &phonet_dgram_ops, .prot = &pn_proto, .sock_type = SOCK_DGRAM, diff --git a/net/phonet/pep.c b/net/phonet/pep.c index e81537991ddf..9fc76b19cd3c 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -1351,7 +1351,7 @@ static struct proto pep_proto = { .name = "PNPIPE", }; -static struct phonet_protocol pep_pn_proto = { +static const struct phonet_protocol pep_pn_proto = { .ops = &phonet_stream_ops, .prot = &pep_proto, .sock_type = SOCK_SEQPACKET, -- cgit v1.2.3-59-g8ed1b From 067cae47771c864604969fd902efe10916e0d79c Mon Sep 17 00:00:00 2001 From: Martin KaFai Lau Date: Thu, 5 Oct 2017 21:52:12 -0700 Subject: bpf: Use char in prog and map name Instead of u8, use char for prog and map name. It can avoid the userspace tool getting compiler's signess warning. The bpf_prog_aux, bpf_map, bpf_attr, bpf_prog_info and bpf_map_info are changed. Signed-off-by: Martin KaFai Lau Cc: Jakub Kicinski Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 4 ++-- include/uapi/linux/bpf.h | 8 ++++---- tools/include/uapi/linux/bpf.h | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index a67daea731ab..bc7da2ddfcaf 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -56,7 +56,7 @@ struct bpf_map { struct work_struct work; atomic_t usercnt; struct bpf_map *inner_map_meta; - u8 name[BPF_OBJ_NAME_LEN]; + char name[BPF_OBJ_NAME_LEN]; }; /* function argument constraints */ @@ -189,7 +189,7 @@ struct bpf_prog_aux { struct bpf_prog *prog; struct user_struct *user; u64 load_time; /* ns since boottime */ - u8 name[BPF_OBJ_NAME_LEN]; + char name[BPF_OBJ_NAME_LEN]; union { struct work_struct work; struct rcu_head rcu; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 5bbbec17aa5a..6db9e1d679cd 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -230,7 +230,7 @@ union bpf_attr { __u32 numa_node; /* numa node (effective only if * BPF_F_NUMA_NODE is set). */ - __u8 map_name[BPF_OBJ_NAME_LEN]; + char map_name[BPF_OBJ_NAME_LEN]; }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ @@ -253,7 +253,7 @@ union bpf_attr { __aligned_u64 log_buf; /* user supplied buffer */ __u32 kern_version; /* checked when prog_type=kprobe */ __u32 prog_flags; - __u8 prog_name[BPF_OBJ_NAME_LEN]; + char prog_name[BPF_OBJ_NAME_LEN]; }; struct { /* anonymous struct used by BPF_OBJ_* commands */ @@ -888,7 +888,7 @@ struct bpf_prog_info { __u32 created_by_uid; __u32 nr_map_ids; __aligned_u64 map_ids; - __u8 name[BPF_OBJ_NAME_LEN]; + char name[BPF_OBJ_NAME_LEN]; } __attribute__((aligned(8))); struct bpf_map_info { @@ -898,7 +898,7 @@ struct bpf_map_info { __u32 value_size; __u32 max_entries; __u32 map_flags; - __u8 name[BPF_OBJ_NAME_LEN]; + char name[BPF_OBJ_NAME_LEN]; } __attribute__((aligned(8))); /* User bpf_sock_ops struct to access socket values and specify request ops diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 0894fd20b12b..fb4fb81ce5b0 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -230,7 +230,7 @@ union bpf_attr { __u32 numa_node; /* numa node (effective only if * BPF_F_NUMA_NODE is set). */ - __u8 map_name[BPF_OBJ_NAME_LEN]; + char map_name[BPF_OBJ_NAME_LEN]; }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ @@ -253,7 +253,7 @@ union bpf_attr { __aligned_u64 log_buf; /* user supplied buffer */ __u32 kern_version; /* checked when prog_type=kprobe */ __u32 prog_flags; - __u8 prog_name[BPF_OBJ_NAME_LEN]; + char prog_name[BPF_OBJ_NAME_LEN]; }; struct { /* anonymous struct used by BPF_OBJ_* commands */ @@ -871,7 +871,7 @@ struct bpf_prog_info { __u32 created_by_uid; __u32 nr_map_ids; __aligned_u64 map_ids; - __u8 name[BPF_OBJ_NAME_LEN]; + char name[BPF_OBJ_NAME_LEN]; } __attribute__((aligned(8))); struct bpf_map_info { @@ -881,7 +881,7 @@ struct bpf_map_info { __u32 value_size; __u32 max_entries; __u32 map_flags; - __u8 name[BPF_OBJ_NAME_LEN]; + char name[BPF_OBJ_NAME_LEN]; } __attribute__((aligned(8))); /* User bpf_sock_ops struct to access socket values and specify request ops -- cgit v1.2.3-59-g8ed1b From 821f1b21cabb46827ce39ddf82e2789680b5042a Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Fri, 6 Oct 2017 22:12:37 -0700 Subject: bridge: add new BR_NEIGH_SUPPRESS port flag to suppress arp and nd flood This patch adds a new bridge port flag BR_NEIGH_SUPPRESS to suppress arp and nd flood on bridge ports. It implements rfc7432, section 10. https://tools.ietf.org/html/rfc7432#section-10 for ethernet VPN deployments. It is similar to the existing BR_PROXYARP* flags but has a few semantic differences to conform to EVPN standard. Unlike the existing flags, this new flag suppresses flood of all neigh discovery packets (arp and nd) to tunnel ports. Supports both vlan filtering and non-vlan filtering bridges. In case of EVPN, it is mainly used to avoid flooding of arp and nd packets to tunnel ports like vxlan. This patch adds netlink and sysfs support to set this bridge port flag. Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller --- include/linux/if_bridge.h | 1 + include/uapi/linux/if_link.h | 1 + net/bridge/Makefile | 2 +- net/bridge/br_arp_nd_proxy.c | 32 ++++++++++++++++++++++++++++++++ net/bridge/br_forward.c | 2 +- net/bridge/br_if.c | 5 +++++ net/bridge/br_netlink.c | 10 +++++++++- net/bridge/br_private.h | 2 ++ net/bridge/br_sysfs_if.c | 2 ++ 9 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 net/bridge/br_arp_nd_proxy.c (limited to 'include') diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 3cd18ac0697f..316ee113a220 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -49,6 +49,7 @@ struct br_ip_list { #define BR_MULTICAST_TO_UNICAST BIT(12) #define BR_VLAN_TUNNEL BIT(13) #define BR_BCAST_FLOOD BIT(14) +#define BR_NEIGH_SUPPRESS BIT(15) #define BR_DEFAULT_AGEING_TIME (300 * HZ) diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index cd580fc0e58f..b037e0ab1975 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -327,6 +327,7 @@ enum { IFLA_BRPORT_VLAN_TUNNEL, IFLA_BRPORT_BCAST_FLOOD, IFLA_BRPORT_GROUP_FWD_MASK, + IFLA_BRPORT_NEIGH_SUPPRESS, __IFLA_BRPORT_MAX }; #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1) diff --git a/net/bridge/Makefile b/net/bridge/Makefile index 40b1ede527ca..4aee55fdcc92 100644 --- a/net/bridge/Makefile +++ b/net/bridge/Makefile @@ -7,7 +7,7 @@ obj-$(CONFIG_BRIDGE) += bridge.o bridge-y := br.o br_device.o br_fdb.o br_forward.o br_if.o br_input.o \ br_ioctl.o br_stp.o br_stp_bpdu.o \ br_stp_if.o br_stp_timer.o br_netlink.o \ - br_netlink_tunnel.o + br_netlink_tunnel.o br_arp_nd_proxy.o bridge-$(CONFIG_SYSFS) += br_sysfs_if.o br_sysfs_br.o diff --git a/net/bridge/br_arp_nd_proxy.c b/net/bridge/br_arp_nd_proxy.c new file mode 100644 index 000000000000..f889ad5f0048 --- /dev/null +++ b/net/bridge/br_arp_nd_proxy.c @@ -0,0 +1,32 @@ +/* + * Handle bridge arp/nd proxy/suppress + * + * Copyright (C) 2017 Cumulus Networks + * Copyright (c) 2017 Roopa Prabhu + * + * Authors: + * Roopa Prabhu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include "br_private.h" + +void br_recalculate_neigh_suppress_enabled(struct net_bridge *br) +{ + struct net_bridge_port *p; + bool neigh_suppress = false; + + list_for_each_entry(p, &br->port_list, list) { + if (p->flags & BR_NEIGH_SUPPRESS) { + neigh_suppress = true; + break; + } + } + + br->neigh_suppress_enabled = neigh_suppress; +} diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 48fb17417fac..b4eed113d2ec 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -204,7 +204,7 @@ void br_flood(struct net_bridge *br, struct sk_buff *skb, /* Do not flood to ports that enable proxy ARP */ if (p->flags & BR_PROXYARP) continue; - if ((p->flags & BR_PROXYARP_WIFI) && + if ((p->flags & (BR_PROXYARP_WIFI | BR_NEIGH_SUPPRESS)) && BR_INPUT_SKB_CB(skb)->proxyarp_replied) continue; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 59a74a414e20..ae38547bbf91 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -310,6 +310,8 @@ void br_dev_delete(struct net_device *dev, struct list_head *head) del_nbp(p); } + br_recalculate_neigh_suppress_enabled(br); + br_fdb_delete_by_port(br, NULL, 0, 1); cancel_delayed_work_sync(&br->gc_work); @@ -660,4 +662,7 @@ void br_port_flags_change(struct net_bridge_port *p, unsigned long mask) if (mask & BR_AUTO_MASK) nbp_update_port_count(br); + + if (mask & BR_NEIGH_SUPPRESS) + br_recalculate_neigh_suppress_enabled(br); } diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index dea88a255d26..f0e82682e071 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -138,6 +138,7 @@ static inline size_t br_port_info_size(void) + nla_total_size(1) /* IFLA_BRPORT_PROXYARP */ + nla_total_size(1) /* IFLA_BRPORT_PROXYARP_WIFI */ + nla_total_size(1) /* IFLA_BRPORT_VLAN_TUNNEL */ + + nla_total_size(1) /* IFLA_BRPORT_NEIGH_SUPPRESS */ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_ROOT_ID */ + nla_total_size(sizeof(struct ifla_bridge_id)) /* IFLA_BRPORT_BRIDGE_ID */ + nla_total_size(sizeof(u16)) /* IFLA_BRPORT_DESIGNATED_PORT */ @@ -210,7 +211,9 @@ static int br_port_fill_attrs(struct sk_buff *skb, nla_put_u8(skb, IFLA_BRPORT_CONFIG_PENDING, p->config_pending) || nla_put_u8(skb, IFLA_BRPORT_VLAN_TUNNEL, !!(p->flags & BR_VLAN_TUNNEL)) || - nla_put_u16(skb, IFLA_BRPORT_GROUP_FWD_MASK, p->group_fwd_mask)) + nla_put_u16(skb, IFLA_BRPORT_GROUP_FWD_MASK, p->group_fwd_mask) || + nla_put_u8(skb, IFLA_BRPORT_NEIGH_SUPPRESS, + !!(p->flags & BR_NEIGH_SUPPRESS))) return -EMSGSIZE; timerval = br_timer_value(&p->message_age_timer); @@ -785,6 +788,11 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) p->group_fwd_mask = fwd_mask; } + err = br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS, + BR_NEIGH_SUPPRESS); + if (err) + return err; + br_port_flags_change(p, old_flags ^ p->flags); return 0; } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index ab4df24f7bba..00fa371b1fb2 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -404,6 +404,7 @@ struct net_bridge { #ifdef CONFIG_NET_SWITCHDEV int offload_fwd_mark; #endif + bool neigh_suppress_enabled; }; struct br_input_skb_cb { @@ -1139,4 +1140,5 @@ static inline void br_switchdev_frame_unmark(struct sk_buff *skb) } #endif /* CONFIG_NET_SWITCHDEV */ +void br_recalculate_neigh_suppress_enabled(struct net_bridge *br); #endif diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 9110d5e56085..0a1fa9ccd8b7 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -191,6 +191,7 @@ BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP); BRPORT_ATTR_FLAG(proxyarp_wifi, BR_PROXYARP_WIFI); BRPORT_ATTR_FLAG(multicast_flood, BR_MCAST_FLOOD); BRPORT_ATTR_FLAG(broadcast_flood, BR_BCAST_FLOOD); +BRPORT_ATTR_FLAG(neigh_suppress, BR_NEIGH_SUPPRESS); #ifdef CONFIG_BRIDGE_IGMP_SNOOPING static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf) @@ -241,6 +242,7 @@ static const struct brport_attribute *brport_attrs[] = { &brport_attr_multicast_flood, &brport_attr_broadcast_flood, &brport_attr_group_fwd_mask, + &brport_attr_neigh_suppress, NULL }; -- cgit v1.2.3-59-g8ed1b From 45d882daf88a57ec681254e8a41dd5ba268a097d Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Tue, 3 Oct 2017 12:00:15 +0200 Subject: clk: samsung: Add explicit MPLL, EPLL clkdev aliases in S3C2443 driver S3C2443 platform still use non-dt based lookup in some of its drivers to get MPLL and EPLL clocks. Till now it worked only because PLL() macro implicitly created aliases for all instantiated clocks. This feature will be removed, so explicitly create aliases for MPLL and EPLL clocks. Signed-off-by: Marek Szyprowski Acked-by: Chanwoo Choi Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-s3c2443.c | 10 ++++++---- include/dt-bindings/clock/s3c2443.h | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c index 45166033f638..d94b85a42356 100644 --- a/drivers/clk/samsung/clk-s3c2443.c +++ b/drivers/clk/samsung/clk-s3c2443.c @@ -191,6 +191,8 @@ struct samsung_gate_clock s3c2443_common_gates[] __initdata = { struct samsung_clock_alias s3c2443_common_aliases[] __initdata = { ALIAS(MSYSCLK, NULL, "msysclk"), ALIAS(ARMCLK, NULL, "armclk"), + ALIAS(MPLL, NULL, "mpll"), + ALIAS(EPLL, NULL, "epll"), ALIAS(HCLK, NULL, "hclk"), ALIAS(HCLK_SSMC, NULL, "nand"), ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"), @@ -223,9 +225,9 @@ struct samsung_clock_alias s3c2443_common_aliases[] __initdata = { /* S3C2416 specific clocks */ static struct samsung_pll_clock s3c2416_pll_clks[] __initdata = { - [mpll] = PLL(pll_6552_s3c2416, 0, "mpll", "mpllref", + [mpll] = PLL(pll_6552_s3c2416, MPLL, "mpll", "mpllref", LOCKCON0, MPLLCON, NULL), - [epll] = PLL(pll_6553, 0, "epll", "epllref", + [epll] = PLL(pll_6553, EPLL, "epll", "epllref", LOCKCON1, EPLLCON, NULL), }; @@ -277,9 +279,9 @@ struct samsung_clock_alias s3c2416_aliases[] __initdata = { /* S3C2443 specific clocks */ static struct samsung_pll_clock s3c2443_pll_clks[] __initdata = { - [mpll] = PLL(pll_3000, 0, "mpll", "mpllref", + [mpll] = PLL(pll_3000, MPLL, "mpll", "mpllref", LOCKCON0, MPLLCON, NULL), - [epll] = PLL(pll_2126, 0, "epll", "epllref", + [epll] = PLL(pll_2126, EPLL, "epll", "epllref", LOCKCON1, EPLLCON, NULL), }; diff --git a/include/dt-bindings/clock/s3c2443.h b/include/dt-bindings/clock/s3c2443.h index 37e66b054d64..f3ba68a25ecb 100644 --- a/include/dt-bindings/clock/s3c2443.h +++ b/include/dt-bindings/clock/s3c2443.h @@ -26,6 +26,8 @@ #define ARMCLK 4 #define HCLK 5 #define PCLK 6 +#define MPLL 7 +#define EPLL 8 /* Special clocks */ #define SCLK_HSSPI0 16 -- cgit v1.2.3-59-g8ed1b From f8e0731db4a02c8c5624e1dce6dc983210a51f64 Mon Sep 17 00:00:00 2001 From: Christian König Date: Fri, 15 Sep 2017 11:53:07 +0200 Subject: dma-fence: fix dma_fence_get_rcu_safe v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When dma_fence_get_rcu() fails to acquire a reference it doesn't necessary mean that there is no fence at all. It usually mean that the fence was replaced by a new one and in this situation we certainly want to have the new one as result and *NOT* NULL. v2: Keep extra check after dma_fence_get_rcu(). Signed-off-by: Christian König Cc: Chris Wilson Cc: Daniel Vetter Cc: Sumit Semwal Cc: linux-media@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Cc: linaro-mm-sig@lists.linaro.org Reviewed-by: Maarten Lankhorst Signed-off-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/1505469187-3565-1-git-send-email-deathsimple@vodafone.de --- include/linux/dma-fence.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h index 171895072435..ca974224d92e 100644 --- a/include/linux/dma-fence.h +++ b/include/linux/dma-fence.h @@ -248,9 +248,12 @@ dma_fence_get_rcu_safe(struct dma_fence * __rcu *fencep) struct dma_fence *fence; fence = rcu_dereference(*fencep); - if (!fence || !dma_fence_get_rcu(fence)) + if (!fence) return NULL; + if (!dma_fence_get_rcu(fence)) + continue; + /* The atomic_inc_not_zero() inside dma_fence_get_rcu() * provides a full memory barrier upon success (such as now). * This is paired with the write barrier from assigning -- cgit v1.2.3-59-g8ed1b From 77041420751fe6d4acf2103b245dcc2b4b7b8360 Mon Sep 17 00:00:00 2001 From: Yotam Gigi Date: Mon, 9 Oct 2017 11:15:31 +0200 Subject: net: bridge: Notify on bridge device mrouter state changes Add the SWITCHDEV_ATTR_ID_BRIDGE_MROUTER switchdev notification type, used to indicate whether the bridge is or isn't mrouter. Notify when the bridge changes its state, similarly to the already existing bridged port mrouter notifications. The notification uses the switchdev_attr.u.mrouter boolean flag to indicate the current bridge mrouter status. Thus, it only indicates whether the bridge is currently used as an mrouter or not, and does not indicate the exact mrouter state of the bridge (learning, permanent, etc.). Signed-off-by: Yotam Gigi Signed-off-by: Jiri Pirko Signed-off-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/net/switchdev.h | 1 + net/bridge/br_multicast.c | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/switchdev.h b/include/net/switchdev.h index d767b7991887..d756fbe46625 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -51,6 +51,7 @@ enum switchdev_attr_id { SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME, SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING, SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED, + SWITCHDEV_ATTR_ID_BRIDGE_MROUTER, }; struct switchdev_attr { diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 8dc5c8d69bcd..bd50550dd4ca 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -859,8 +859,32 @@ out: spin_unlock(&br->multicast_lock); } +static void br_mc_router_state_change(struct net_bridge *p, + bool is_mc_router) +{ + struct switchdev_attr attr = { + .orig_dev = p->dev, + .id = SWITCHDEV_ATTR_ID_BRIDGE_MROUTER, + .flags = SWITCHDEV_F_DEFER, + .u.mrouter = is_mc_router, + }; + + switchdev_port_attr_set(p->dev, &attr); +} + static void br_multicast_local_router_expired(unsigned long data) { + struct net_bridge *br = (struct net_bridge *)data; + + spin_lock(&br->multicast_lock); + if (br->multicast_router == MDB_RTR_TYPE_DISABLED || + br->multicast_router == MDB_RTR_TYPE_PERM || + timer_pending(&br->multicast_router_timer)) + goto out; + + br_mc_router_state_change(br, false); +out: + spin_unlock(&br->multicast_lock); } static void br_multicast_querier_expired(struct net_bridge *br, @@ -1364,9 +1388,12 @@ static void br_multicast_mark_router(struct net_bridge *br, unsigned long now = jiffies; if (!port) { - if (br->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) + if (br->multicast_router == MDB_RTR_TYPE_TEMP_QUERY) { + if (!timer_pending(&br->multicast_router_timer)) + br_mc_router_state_change(br, true); mod_timer(&br->multicast_router_timer, now + br->multicast_querier_interval); + } return; } @@ -1952,7 +1979,7 @@ void br_multicast_init(struct net_bridge *br) spin_lock_init(&br->multicast_lock); setup_timer(&br->multicast_router_timer, - br_multicast_local_router_expired, 0); + br_multicast_local_router_expired, (unsigned long)br); setup_timer(&br->ip4_other_query.timer, br_ip4_multicast_querier_expired, (unsigned long)br); setup_timer(&br->ip4_own_query.timer, br_ip4_multicast_query_expired, @@ -2042,9 +2069,14 @@ int br_multicast_set_router(struct net_bridge *br, unsigned long val) switch (val) { case MDB_RTR_TYPE_DISABLED: case MDB_RTR_TYPE_PERM: + br_mc_router_state_change(br, val == MDB_RTR_TYPE_PERM); del_timer(&br->multicast_router_timer); - /* fall through */ + br->multicast_router = val; + err = 0; + break; case MDB_RTR_TYPE_TEMP_QUERY: + if (br->multicast_router != MDB_RTR_TYPE_TEMP_QUERY) + br_mc_router_state_change(br, false); br->multicast_router = val; err = 0; break; -- cgit v1.2.3-59-g8ed1b From 0912bda436388a02c72164b4b490b578e64c012e Mon Sep 17 00:00:00 2001 From: Yotam Gigi Date: Mon, 9 Oct 2017 11:15:32 +0200 Subject: net: bridge: Export bridge multicast router state Add an access function that, given a bridge netdevice, returns whether the bridge device is currently an mrouter or not. The function uses the already existing br_multicast_is_router function to check that. This function is needed in order to allow ports that join an already existing bridge to know the current mrouter state of the bridge device. Together with the bridge device mrouter ports switchdev notifications, it is possible to have full offloading of the semantics of the bridge device mcast router state. Due to the fact that the bridge multicast router status can change in packet RX path, take the multicast_router bridge spinlock to protect the read. Signed-off-by: Yotam Gigi Reviewed-by: Nogah Frankel Reviewed-by: Nikolay Aleksandrov Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/if_bridge.h | 5 +++++ net/bridge/br_multicast.c | 12 ++++++++++++ 2 files changed, 17 insertions(+) (limited to 'include') diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h index 316ee113a220..02639ebea2f0 100644 --- a/include/linux/if_bridge.h +++ b/include/linux/if_bridge.h @@ -64,6 +64,7 @@ int br_multicast_list_adjacent(struct net_device *dev, bool br_multicast_has_querier_anywhere(struct net_device *dev, int proto); bool br_multicast_has_querier_adjacent(struct net_device *dev, int proto); bool br_multicast_enabled(const struct net_device *dev); +bool br_multicast_router(const struct net_device *dev); #else static inline int br_multicast_list_adjacent(struct net_device *dev, struct list_head *br_ip_list) @@ -84,6 +85,10 @@ static inline bool br_multicast_enabled(const struct net_device *dev) { return false; } +static inline bool br_multicast_router(const struct net_device *dev) +{ + return false; +} #endif #if IS_ENABLED(CONFIG_BRIDGE) && IS_ENABLED(CONFIG_BRIDGE_VLAN_FILTERING) diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index bd50550dd4ca..7947e0436e18 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -2216,6 +2216,18 @@ bool br_multicast_enabled(const struct net_device *dev) } EXPORT_SYMBOL_GPL(br_multicast_enabled); +bool br_multicast_router(const struct net_device *dev) +{ + struct net_bridge *br = netdev_priv(dev); + bool is_router; + + spin_lock_bh(&br->multicast_lock); + is_router = br_multicast_is_router(br); + spin_unlock_bh(&br->multicast_lock); + return is_router; +} +EXPORT_SYMBOL_GPL(br_multicast_router); + int br_multicast_set_querier(struct net_bridge *br, unsigned long val) { unsigned long max_delay; -- cgit v1.2.3-59-g8ed1b From ed468ebee04ffba0231a8f50616bdb250752a891 Mon Sep 17 00:00:00 2001 From: Michal Kalderon Date: Mon, 9 Oct 2017 12:37:44 +0300 Subject: qed: Add ll2 ability of opening a secondary queue When more than one ll2 queue is opened ( that is not an OOO queue ) ll2 code does not have enough information to determine whether the queue is the main one or not, so a new field is added to the acquire input data to expose the control of determining whether the queue is the main queue or a secondary queue. Signed-off-by: Michal Kalderon Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_ll2.c | 7 ++++++- drivers/net/ethernet/qlogic/qed/qed_ll2.h | 1 + include/linux/qed/qed_ll2_if.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index 75af40a7690a..3c695da890df 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -894,7 +894,7 @@ static int qed_sp_ll2_rx_queue_start(struct qed_hwfn *p_hwfn, p_ramrod->drop_ttl0_flg = p_ll2_conn->input.rx_drop_ttl0_flg; p_ramrod->inner_vlan_removal_en = p_ll2_conn->input.rx_vlan_removal_en; p_ramrod->queue_id = p_ll2_conn->queue_id; - p_ramrod->main_func_queue = (conn_type == QED_LL2_TYPE_OOO) ? 0 : 1; + p_ramrod->main_func_queue = p_ll2_conn->main_func_queue ? 1 : 0; if ((IS_MF_DEFAULT(p_hwfn) || IS_MF_SI(p_hwfn)) && p_ramrod->main_func_queue && (conn_type != QED_LL2_TYPE_ROCE) && @@ -1265,6 +1265,11 @@ int qed_ll2_acquire_connection(void *cxt, struct qed_ll2_acquire_data *data) p_ll2_info->tx_dest = (data->input.tx_dest == QED_LL2_TX_DEST_NW) ? CORE_TX_DEST_NW : CORE_TX_DEST_LB; + if (data->input.conn_type == QED_LL2_TYPE_OOO || + data->input.secondary_queue) + p_ll2_info->main_func_queue = false; + else + p_ll2_info->main_func_queue = true; /* Correct maximum number of Tx BDs */ p_tx_max = &p_ll2_info->input.tx_max_bds_per_packet; diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.h b/drivers/net/ethernet/qlogic/qed/qed_ll2.h index 9bdd08f15c79..f65817012e97 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.h +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.h @@ -121,6 +121,7 @@ struct qed_ll2_info { bool b_active; enum core_tx_dest tx_dest; u8 tx_stats_en; + bool main_func_queue; struct qed_ll2_rx_queue rx_queue; struct qed_ll2_tx_queue tx_queue; struct qed_ll2_cbs cbs; diff --git a/include/linux/qed/qed_ll2_if.h b/include/linux/qed/qed_ll2_if.h index 89fa0bbd54f3..d7cca590b743 100644 --- a/include/linux/qed/qed_ll2_if.h +++ b/include/linux/qed/qed_ll2_if.h @@ -171,6 +171,7 @@ struct qed_ll2_acquire_data_inputs { enum qed_ll2_tx_dest tx_dest; enum qed_ll2_error_handle ai_err_packet_too_big; enum qed_ll2_error_handle ai_err_no_buf; + bool secondary_queue; u8 gsi_enable; }; -- cgit v1.2.3-59-g8ed1b From 77caa792f5d8e4ecc88eb1cf4b9c478c07e0ec57 Mon Sep 17 00:00:00 2001 From: Michal Kalderon Date: Mon, 9 Oct 2017 12:37:45 +0300 Subject: qed: Add ll2 option for dropping a tx packet The option of sending a packet on the ll2 and dropping it exists in hardware and was not used until now, thus not exposed. The iWARP unaligned MPA flow requires this functionality for flushing the tx queue. Signed-off-by: Michal Kalderon Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_ll2.c | 16 ++++++++++++++-- include/linux/qed/qed_ll2_if.h | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index 3c695da890df..ad67d36956e8 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -1597,8 +1597,20 @@ qed_ll2_prepare_tx_packet_set_bd(struct qed_hwfn *p_hwfn, roce_flavor = (pkt->qed_roce_flavor == QED_LL2_ROCE) ? CORE_ROCE : CORE_RROCE; - tx_dest = (pkt->tx_dest == QED_LL2_TX_DEST_NW) ? CORE_TX_DEST_NW - : CORE_TX_DEST_LB; + switch (pkt->tx_dest) { + case QED_LL2_TX_DEST_NW: + tx_dest = CORE_TX_DEST_NW; + break; + case QED_LL2_TX_DEST_LB: + tx_dest = CORE_TX_DEST_LB; + break; + case QED_LL2_TX_DEST_DROP: + tx_dest = CORE_TX_DEST_DROP; + break; + default: + tx_dest = CORE_TX_DEST_LB; + break; + } start_bd = (struct core_tx_bd *)qed_chain_produce(p_tx_chain); start_bd->nw_vlan_or_lb_echo = cpu_to_le16(pkt->vlan); diff --git a/include/linux/qed/qed_ll2_if.h b/include/linux/qed/qed_ll2_if.h index d7cca590b743..95fdf02a3bbe 100644 --- a/include/linux/qed/qed_ll2_if.h +++ b/include/linux/qed/qed_ll2_if.h @@ -64,6 +64,7 @@ enum qed_ll2_roce_flavor_type { enum qed_ll2_tx_dest { QED_LL2_TX_DEST_NW, /* Light L2 TX Destination to the Network */ QED_LL2_TX_DEST_LB, /* Light L2 TX Destination to the Loopback */ + QED_LL2_TX_DEST_DROP, /* Light L2 Drop the TX packet */ QED_LL2_TX_DEST_MAX }; -- cgit v1.2.3-59-g8ed1b From 6f34a284f36399501fcc034dc4522a2d8d9fa6c9 Mon Sep 17 00:00:00 2001 From: Michal Kalderon Date: Mon, 9 Oct 2017 12:37:48 +0300 Subject: qed: Add LL2 slowpath handling For iWARP unaligned MPA flow, a slowpath event of flushing an MPA connection that entered an unaligned state is required. The flush ramrod is received on the ll2 queue, and a pre-registered callback function is called to handle the flush event. Signed-off-by: Michal Kalderon Signed-off-by: Ariel Elior Signed-off-by: David S. Miller --- drivers/net/ethernet/qlogic/qed/qed_ll2.c | 40 +++++++++++++++++++++++++++++-- include/linux/qed/qed_ll2_if.h | 5 ++++ 2 files changed, 43 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/qlogic/qed/qed_ll2.c b/drivers/net/ethernet/qlogic/qed/qed_ll2.c index 8eb9645c880d..047f556ca62e 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_ll2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_ll2.c @@ -422,6 +422,41 @@ static void qed_ll2_rxq_parse_reg(struct qed_hwfn *p_hwfn, data->u.placement_offset = p_cqe->rx_cqe_fp.placement_offset; } +static int +qed_ll2_handle_slowpath(struct qed_hwfn *p_hwfn, + struct qed_ll2_info *p_ll2_conn, + union core_rx_cqe_union *p_cqe, + unsigned long *p_lock_flags) +{ + struct qed_ll2_rx_queue *p_rx = &p_ll2_conn->rx_queue; + struct core_rx_slow_path_cqe *sp_cqe; + + sp_cqe = &p_cqe->rx_cqe_sp; + if (sp_cqe->ramrod_cmd_id != CORE_RAMROD_RX_QUEUE_FLUSH) { + DP_NOTICE(p_hwfn, + "LL2 - unexpected Rx CQE slowpath ramrod_cmd_id:%d\n", + sp_cqe->ramrod_cmd_id); + return -EINVAL; + } + + if (!p_ll2_conn->cbs.slowpath_cb) { + DP_NOTICE(p_hwfn, + "LL2 - received RX_QUEUE_FLUSH but no callback was provided\n"); + return -EINVAL; + } + + spin_unlock_irqrestore(&p_rx->lock, *p_lock_flags); + + p_ll2_conn->cbs.slowpath_cb(p_ll2_conn->cbs.cookie, + p_ll2_conn->my_id, + le32_to_cpu(sp_cqe->opaque_data.data[0]), + le32_to_cpu(sp_cqe->opaque_data.data[1])); + + spin_lock_irqsave(&p_rx->lock, *p_lock_flags); + + return 0; +} + static int qed_ll2_rxq_handle_completion(struct qed_hwfn *p_hwfn, struct qed_ll2_info *p_ll2_conn, @@ -495,8 +530,8 @@ static int qed_ll2_rxq_completion(struct qed_hwfn *p_hwfn, void *cookie) switch (cqe->rx_cqe_sp.type) { case CORE_RX_CQE_TYPE_SLOW_PATH: - DP_NOTICE(p_hwfn, "LL2 - unexpected Rx CQE slowpath\n"); - rc = -EINVAL; + rc = qed_ll2_handle_slowpath(p_hwfn, p_ll2_conn, + cqe, &flags); break; case CORE_RX_CQE_TYPE_GSI_OFFLOAD: case CORE_RX_CQE_TYPE_REGULAR: @@ -1214,6 +1249,7 @@ qed_ll2_set_cbs(struct qed_ll2_info *p_ll2_info, const struct qed_ll2_cbs *cbs) p_ll2_info->cbs.rx_release_cb = cbs->rx_release_cb; p_ll2_info->cbs.tx_comp_cb = cbs->tx_comp_cb; p_ll2_info->cbs.tx_release_cb = cbs->tx_release_cb; + p_ll2_info->cbs.slowpath_cb = cbs->slowpath_cb; p_ll2_info->cbs.cookie = cbs->cookie; return 0; diff --git a/include/linux/qed/qed_ll2_if.h b/include/linux/qed/qed_ll2_if.h index 95fdf02a3bbe..e755954d85fd 100644 --- a/include/linux/qed/qed_ll2_if.h +++ b/include/linux/qed/qed_ll2_if.h @@ -151,11 +151,16 @@ void (*qed_ll2_release_tx_packet_cb)(void *cxt, dma_addr_t first_frag_addr, bool b_last_fragment, bool b_last_packet); +typedef +void (*qed_ll2_slowpath_cb)(void *cxt, u8 connection_handle, + u32 opaque_data_0, u32 opaque_data_1); + struct qed_ll2_cbs { qed_ll2_complete_rx_packet_cb rx_comp_cb; qed_ll2_release_rx_packet_cb rx_release_cb; qed_ll2_complete_tx_packet_cb tx_comp_cb; qed_ll2_release_tx_packet_cb tx_release_cb; + qed_ll2_slowpath_cb slowpath_cb; void *cookie; }; -- cgit v1.2.3-59-g8ed1b From e72a060151e5bb673af24993665e270fc4f674a7 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 30 Aug 2017 13:50:39 +0200 Subject: iio: st_sensors: add register mask for status register Introduce register mask for data-ready status register since pressure sensors (e.g. LPS22HB) export just two channels (BIT(0) and BIT(1)) and BIT(2) is marked reserved while in st_sensors_new_samples_available() value read from status register is masked using 0x7. Moreover do not mask status register using active_scan_mask since now status value is properly masked and if the result is not zero the interrupt has to be consumed by the driver. This fix an issue on LPS25H and LPS331AP where channel definition is swapped respect to status register. Furthermore that change allows to properly support new devices (e.g LIS2DW12) that report just ZYXDA (data-ready) field in status register to figure out if the interrupt has been generated by the device. Fixes: 97865fe41322 (iio: st_sensors: verify interrupt event to status) Signed-off-by: Lorenzo Bianconi Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 35 +++++++++++++++++----- drivers/iio/common/st_sensors/st_sensors_core.c | 2 +- drivers/iio/common/st_sensors/st_sensors_trigger.c | 16 +++------- drivers/iio/gyro/st_gyro_core.c | 15 ++++++++-- drivers/iio/magnetometer/st_magn_core.c | 10 +++++-- drivers/iio/pressure/st_pressure_core.c | 15 ++++++++-- include/linux/iio/common/st_sensors.h | 7 +++-- 7 files changed, 70 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 1a2e54ff473a..140ba26f6131 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -164,7 +164,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int2 = 0x00, .addr_ihl = 0x25, .mask_ihl = 0x02, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, }, .sim = { .addr = 0x23, @@ -236,7 +239,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_ihl = 0x80, .addr_od = 0x22, .mask_od = 0x40, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, }, .sim = { .addr = 0x23, @@ -318,7 +324,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int2 = 0x00, .addr_ihl = 0x23, .mask_ihl = 0x40, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, .ig1 = { .en_addr = 0x23, .en_mask = 0x08, @@ -389,7 +398,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .drdy_irq = { .addr = 0x21, .mask_int1 = 0x04, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, }, .sim = { .addr = 0x21, @@ -451,7 +463,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_ihl = 0x80, .addr_od = 0x22, .mask_od = 0x40, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, }, .sim = { .addr = 0x21, @@ -569,7 +584,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .drdy_irq = { .addr = 0x21, .mask_int1 = 0x04, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, }, .sim = { .addr = 0x21, @@ -640,7 +658,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int2 = 0x00, .addr_ihl = 0x25, .mask_ihl = 0x02, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, }, .sim = { .addr = 0x23, diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 02e833b14db0..34115f05d5c4 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -470,7 +470,7 @@ int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable) * different one. Take into account irq status register * to understand if irq trigger can be properly supported */ - if (sdata->sensor_settings->drdy_irq.addr_stat_drdy) + if (sdata->sensor_settings->drdy_irq.stat_drdy.addr) sdata->hw_irq_trigger = enable; return 0; } diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index fa73e6795359..fdcc5a891958 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -31,7 +31,7 @@ static int st_sensors_new_samples_available(struct iio_dev *indio_dev, int ret; /* How would I know if I can't check it? */ - if (!sdata->sensor_settings->drdy_irq.addr_stat_drdy) + if (!sdata->sensor_settings->drdy_irq.stat_drdy.addr) return -EINVAL; /* No scan mask, no interrupt */ @@ -39,23 +39,15 @@ static int st_sensors_new_samples_available(struct iio_dev *indio_dev, return 0; ret = sdata->tf->read_byte(&sdata->tb, sdata->dev, - sdata->sensor_settings->drdy_irq.addr_stat_drdy, + sdata->sensor_settings->drdy_irq.stat_drdy.addr, &status); if (ret < 0) { dev_err(sdata->dev, "error checking samples available\n"); return ret; } - /* - * the lower bits of .active_scan_mask[0] is directly mapped - * to the channels on the sensor: either bit 0 for - * one-dimensional sensors, or e.g. x,y,z for accelerometers, - * gyroscopes or magnetometers. No sensor use more than 3 - * channels, so cut the other status bits here. - */ - status &= 0x07; - if (status & (u8)indio_dev->active_scan_mask[0]) + if (status & sdata->sensor_settings->drdy_irq.stat_drdy.mask) return 1; return 0; @@ -212,7 +204,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, * it was "our" interrupt. */ if (sdata->int_pin_open_drain && - sdata->sensor_settings->drdy_irq.addr_stat_drdy) + sdata->sensor_settings->drdy_irq.stat_drdy.addr) irq_trig |= IRQF_SHARED; err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev), diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index 4cf85aa01dde..22c0c1732996 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -118,7 +118,10 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { * drain settings, but only for INT1 and not * for the DRDY line on INT2. */ - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, }, .multi_read_bit = true, .bootime = 2, @@ -188,7 +191,10 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { * drain settings, but only for INT1 and not * for the DRDY line on INT2. */ - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, }, .multi_read_bit = true, .bootime = 2, @@ -253,7 +259,10 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { * drain settings, but only for INT1 and not * for the DRDY line on INT2. */ - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, }, .multi_read_bit = true, .bootime = 2, diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 703de313c418..ace72c57f53c 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -317,7 +317,10 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { }, .drdy_irq = { /* drdy line is routed drdy pin */ - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x07, + }, }, .multi_read_bit = true, .bootime = 2, @@ -361,7 +364,10 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .drdy_irq = { .addr = 0x62, .mask_int1 = 0x01, - .addr_stat_drdy = 0x67, + .stat_drdy = { + .addr = 0x67, + .mask = 0x07, + }, }, .multi_read_bit = false, .bootime = 2, diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 86120715913b..5f8358e23f5b 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -287,7 +287,10 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask_ihl = 0x80, .addr_od = 0x22, .mask_od = 0x40, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x03, + }, }, .multi_read_bit = true, .bootime = 2, @@ -395,7 +398,10 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask_ihl = 0x80, .addr_od = 0x22, .mask_od = 0x40, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x03, + }, }, .multi_read_bit = true, .bootime = 2, @@ -456,7 +462,10 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask_ihl = 0x80, .addr_od = 0x12, .mask_od = 0x40, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = 0x03, + }, }, .multi_read_bit = false, .bootime = 2, diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 7b0fa8b5c120..ce0ef1c0a30a 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -139,7 +139,7 @@ struct st_sensor_das { * @mask_ihl: mask to enable/disable active low on the INT lines. * @addr_od: address to enable/disable Open Drain on the INT lines. * @mask_od: mask to enable/disable Open Drain on the INT lines. - * @addr_stat_drdy: address to read status of DRDY (data ready) interrupt + * struct stat_drdy - status register of DRDY (data ready) interrupt. * struct ig1 - represents the Interrupt Generator 1 of sensors. * @en_addr: address of the enable ig1 register. * @en_mask: mask to write the on/off value for enable. @@ -152,7 +152,10 @@ struct st_sensor_data_ready_irq { u8 mask_ihl; u8 addr_od; u8 mask_od; - u8 addr_stat_drdy; + struct { + u8 addr; + u8 mask; + } stat_drdy; struct { u8 en_addr; u8 en_mask; -- cgit v1.2.3-59-g8ed1b From 75d4c6d2e15d4455dfd5995c27e6e6ad6f214e39 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 30 Aug 2017 13:50:40 +0200 Subject: iio: st_sensors: decouple irq1 configuration parameters from the irq2 ones Separate data-ready configuration parameters for INT1 and INT2 pins in st_sensor_data_ready_irq data structure. That change will be use to properly support LIS2DW12 accel sensor. Signed-off-by: Lorenzo Bianconi Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 66 ++++++++++++++++--------- drivers/iio/common/st_sensors/st_sensors_core.c | 28 ++++++----- drivers/iio/gyro/st_gyro_core.c | 18 ++++--- drivers/iio/magnetometer/st_magn_core.c | 6 ++- drivers/iio/pressure/st_pressure_core.c | 31 +++++++----- include/linux/iio/common/st_sensors.h | 16 +++--- 6 files changed, 104 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 140ba26f6131..a6eac959dfe8 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -159,9 +159,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask = 0x80, }, .drdy_irq = { - .addr = 0x22, - .mask_int1 = 0x10, - .mask_int2 = 0x00, + .int1 = { + .addr = 0x22, + .mask = 0x10, + }, .addr_ihl = 0x25, .mask_ihl = 0x02, .stat_drdy = { @@ -232,9 +233,14 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask = 0x80, }, .drdy_irq = { - .addr = 0x22, - .mask_int1 = 0x02, - .mask_int2 = 0x10, + .int1 = { + .addr = 0x22, + .mask = 0x02, + }, + .int2 = { + .addr = 0x22, + .mask = 0x10, + }, .addr_ihl = 0x22, .mask_ihl = 0x80, .addr_od = 0x22, @@ -319,9 +325,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask = 0x08, }, .drdy_irq = { - .addr = 0x23, - .mask_int1 = 0x80, - .mask_int2 = 0x00, + .int1 = { + .addr = 0x23, + .mask = 0x80, + }, .addr_ihl = 0x23, .mask_ihl = 0x40, .stat_drdy = { @@ -396,8 +403,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask = 0x01, }, .drdy_irq = { - .addr = 0x21, - .mask_int1 = 0x04, + .int1 = { + .addr = 0x21, + .mask = 0x04, + }, .stat_drdy = { .addr = ST_SENSORS_DEFAULT_STAT_ADDR, .mask = 0x07, @@ -456,9 +465,14 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { }, }, .drdy_irq = { - .addr = 0x22, - .mask_int1 = 0x04, - .mask_int2 = 0x20, + .int1 = { + .addr = 0x22, + .mask = 0x04, + }, + .int2 = { + .addr = 0x22, + .mask = 0x20, + }, .addr_ihl = 0x22, .mask_ihl = 0x80, .addr_od = 0x22, @@ -528,9 +542,14 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask = 0x80, }, .drdy_irq = { - .addr = 0x22, - .mask_int1 = 0x02, - .mask_int2 = 0x10, + .int1 = { + .addr = 0x22, + .mask = 0x02, + }, + .int2 = { + .addr = 0x22, + .mask = 0x10, + }, .addr_ihl = 0x22, .mask_ihl = 0x80, }, @@ -582,8 +601,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .bdu = { }, .drdy_irq = { - .addr = 0x21, - .mask_int1 = 0x04, + .int1 = { + .addr = 0x21, + .mask = 0x04, + }, .stat_drdy = { .addr = ST_SENSORS_DEFAULT_STAT_ADDR, .mask = 0x07, @@ -653,9 +674,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { }, }, .drdy_irq = { - .addr = 0x22, - .mask_int1 = 0x10, - .mask_int2 = 0x00, + .int1 = { + .addr = 0x22, + .mask = 0x10, + }, .addr_ihl = 0x25, .mask_ihl = 0x02, .stat_drdy = { diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 34115f05d5c4..af702fa8fa84 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -283,7 +283,8 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, struct st_sensor_data *sdata = iio_priv(indio_dev); /* Sensor does not support interrupts */ - if (sdata->sensor_settings->drdy_irq.addr == 0) { + if (!sdata->sensor_settings->drdy_irq.int1.addr && + !sdata->sensor_settings->drdy_irq.int2.addr) { if (pdata->drdy_int_pin) dev_info(&indio_dev->dev, "DRDY on pin INT%d specified, but sensor " @@ -294,7 +295,7 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, switch (pdata->drdy_int_pin) { case 1: - if (sdata->sensor_settings->drdy_irq.mask_int1 == 0) { + if (!sdata->sensor_settings->drdy_irq.int1.mask) { dev_err(&indio_dev->dev, "DRDY on INT1 not available.\n"); return -EINVAL; @@ -302,7 +303,7 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, sdata->drdy_int_pin = 1; break; case 2: - if (sdata->sensor_settings->drdy_irq.mask_int2 == 0) { + if (!sdata->sensor_settings->drdy_irq.int2.mask) { dev_err(&indio_dev->dev, "DRDY on INT2 not available.\n"); return -EINVAL; @@ -460,10 +461,11 @@ EXPORT_SYMBOL(st_sensors_init_sensor); int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable) { int err; - u8 drdy_mask; + u8 drdy_addr, drdy_mask; struct st_sensor_data *sdata = iio_priv(indio_dev); - if (!sdata->sensor_settings->drdy_irq.addr) { + if (!sdata->sensor_settings->drdy_irq.int1.addr && + !sdata->sensor_settings->drdy_irq.int2.addr) { /* * there are some devices (e.g. LIS3MDL) where drdy line is * routed to a given pin and it is not possible to select a @@ -485,18 +487,20 @@ int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable) goto st_accel_set_dataready_irq_error; } - if (sdata->drdy_int_pin == 1) - drdy_mask = sdata->sensor_settings->drdy_irq.mask_int1; - else - drdy_mask = sdata->sensor_settings->drdy_irq.mask_int2; + if (sdata->drdy_int_pin == 1) { + drdy_addr = sdata->sensor_settings->drdy_irq.int1.addr; + drdy_mask = sdata->sensor_settings->drdy_irq.int1.mask; + } else { + drdy_addr = sdata->sensor_settings->drdy_irq.int2.addr; + drdy_mask = sdata->sensor_settings->drdy_irq.int2.mask; + } /* Flag to the poll function that the hardware trigger is in use */ sdata->hw_irq_trigger = enable; /* Enable/Disable the interrupt generator for data ready. */ - err = st_sensors_write_data_with_mask(indio_dev, - sdata->sensor_settings->drdy_irq.addr, - drdy_mask, (int)enable); + err = st_sensors_write_data_with_mask(indio_dev, drdy_addr, + drdy_mask, (int)enable); st_accel_set_dataready_irq_error: return err; diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index 22c0c1732996..46991f7131d1 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -111,8 +111,10 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .mask = 0x80, }, .drdy_irq = { - .addr = 0x22, - .mask_int2 = 0x08, + .int2 = { + .addr = 0x22, + .mask = 0x08, + }, /* * The sensor has IHL (active low) and open * drain settings, but only for INT1 and not @@ -184,8 +186,10 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .mask = 0x80, }, .drdy_irq = { - .addr = 0x22, - .mask_int2 = 0x08, + .int2 = { + .addr = 0x22, + .mask = 0x08, + }, /* * The sensor has IHL (active low) and open * drain settings, but only for INT1 and not @@ -252,8 +256,10 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .mask = 0x80, }, .drdy_irq = { - .addr = 0x22, - .mask_int2 = 0x08, + .int2 = { + .addr = 0x22, + .mask = 0x08, + }, /* * The sensor has IHL (active low) and open * drain settings, but only for INT1 and not diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index ace72c57f53c..8745686f233c 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -362,8 +362,10 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .mask = 0x10, }, .drdy_irq = { - .addr = 0x62, - .mask_int1 = 0x01, + .int1 = { + .addr = 0x62, + .mask = 0x01, + }, .stat_drdy = { .addr = 0x67, .mask = 0x07, diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 5f8358e23f5b..b8890e37a2d2 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -280,9 +280,14 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask = 0x04, }, .drdy_irq = { - .addr = 0x22, - .mask_int1 = 0x04, - .mask_int2 = 0x20, + .int1 = { + .addr = 0x22, + .mask = 0x04, + }, + .int2 = { + .addr = 0x22, + .mask = 0x20, + }, .addr_ihl = 0x22, .mask_ihl = 0x80, .addr_od = 0x22, @@ -338,9 +343,6 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .addr = 0x20, .mask = 0x04, }, - .drdy_irq = { - .addr = 0, - }, .multi_read_bit = true, .bootime = 2, }, @@ -391,9 +393,10 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask = 0x04, }, .drdy_irq = { - .addr = 0x23, - .mask_int1 = 0x01, - .mask_int2 = 0x00, + .int1 = { + .addr = 0x23, + .mask = 0x01, + }, .addr_ihl = 0x22, .mask_ihl = 0x80, .addr_od = 0x22, @@ -455,9 +458,10 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask = 0x02, }, .drdy_irq = { - .addr = 0x12, - .mask_int1 = 0x04, - .mask_int2 = 0x00, + .int1 = { + .addr = 0x12, + .mask = 0x04, + }, .addr_ihl = 0x12, .mask_ihl = 0x80, .addr_od = 0x12, @@ -614,7 +618,8 @@ int st_press_common_probe(struct iio_dev *indio_dev) press_data->odr = press_data->sensor_settings->odr.odr_avl[0].hz; /* Some devices don't support a data ready pin. */ - if (!pdata && press_data->sensor_settings->drdy_irq.addr) + if (!pdata && (press_data->sensor_settings->drdy_irq.int1.addr || + press_data->sensor_settings->drdy_irq.int2.addr)) pdata = (struct st_sensors_platform_data *)&default_press_pdata; err = st_sensors_init_sensor(indio_dev, press_data->dev->platform_data); diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index ce0ef1c0a30a..e6c646d5d6d4 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -132,9 +132,8 @@ struct st_sensor_das { /** * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt - * @addr: address of the register. - * @mask_int1: mask to enable/disable IRQ on INT1 pin. - * @mask_int2: mask to enable/disable IRQ on INT2 pin. + * struct int1 - data-ready configuration register for INT1 pin. + * struct int2 - data-ready configuration register for INT2 pin. * @addr_ihl: address to enable/disable active low on the INT lines. * @mask_ihl: mask to enable/disable active low on the INT lines. * @addr_od: address to enable/disable Open Drain on the INT lines. @@ -145,9 +144,14 @@ struct st_sensor_das { * @en_mask: mask to write the on/off value for enable. */ struct st_sensor_data_ready_irq { - u8 addr; - u8 mask_int1; - u8 mask_int2; + struct { + u8 addr; + u8 mask; + } int1; + struct { + u8 addr; + u8 mask; + } int2; u8 addr_ihl; u8 mask_ihl; u8 addr_od; -- cgit v1.2.3-59-g8ed1b From 177ae09b5d699a5ebd1cafcee78889db968abf54 Mon Sep 17 00:00:00 2001 From: Andres Rodriguez Date: Fri, 15 Sep 2017 20:44:06 -0400 Subject: drm/amdgpu: introduce AMDGPU_GEM_CREATE_EXPLICIT_SYNC v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a flag to signal that access to a BO will be synchronized through an external mechanism. Currently all buffers shared between contexts are subject to implicit synchronization. However, this is only required for protocols that currently don't support an explicit synchronization mechanism (DRI2/3). This patch introduces the AMDGPU_GEM_CREATE_EXPLICIT_SYNC, so that users can specify when it is safe to disable implicit sync. v2: only disable explicit sync in amdgpu_cs_ioctl Reviewed-by: Christian König Signed-off-by: Andres Rodriguez Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 3 ++- drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c | 4 +++- drivers/gpu/drm/amd/amdgpu/amdgpu_object.h | 8 ++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c | 7 +++++-- drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h | 3 ++- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 5 +++-- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 8 ++++---- include/uapi/drm/amdgpu_drm.h | 2 ++ 8 files changed, 29 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index ab83dfcabb41..38027a00f8ab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -705,7 +705,8 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p) list_for_each_entry(e, &p->validated, tv.head) { struct reservation_object *resv = e->robj->tbo.resv; - r = amdgpu_sync_resv(p->adev, &p->job->sync, resv, p->filp); + r = amdgpu_sync_resv(p->adev, &p->job->sync, resv, p->filp, + amdgpu_bo_explicit_sync(e->robj)); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index b0d45c8e6bb3..21e99366cab3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -212,7 +212,9 @@ int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, AMDGPU_GEM_CREATE_NO_CPU_ACCESS | AMDGPU_GEM_CREATE_CPU_GTT_USWC | AMDGPU_GEM_CREATE_VRAM_CLEARED | - AMDGPU_GEM_CREATE_VM_ALWAYS_VALID)) + AMDGPU_GEM_CREATE_VM_ALWAYS_VALID | + AMDGPU_GEM_CREATE_EXPLICIT_SYNC)) + return -EINVAL; /* reject invalid gem domains */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h index c26ef53604af..428aae048f4b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h @@ -193,6 +193,14 @@ static inline bool amdgpu_bo_gpu_accessible(struct amdgpu_bo *bo) } } +/** + * amdgpu_bo_explicit_sync - return whether the bo is explicitly synced + */ +static inline bool amdgpu_bo_explicit_sync(struct amdgpu_bo *bo) +{ + return bo->flags & AMDGPU_GEM_CREATE_EXPLICIT_SYNC; +} + int amdgpu_bo_create(struct amdgpu_device *adev, unsigned long size, int byte_align, bool kernel, u32 domain, u64 flags, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c index c586f44312f9..a4bf21f8f1c1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c @@ -169,14 +169,14 @@ int amdgpu_sync_fence(struct amdgpu_device *adev, struct amdgpu_sync *sync, * * @sync: sync object to add fences from reservation object to * @resv: reservation object with embedded fence - * @shared: true if we should only sync to the exclusive fence + * @explicit_sync: true if we should only sync to the exclusive fence * * Sync to the fence */ int amdgpu_sync_resv(struct amdgpu_device *adev, struct amdgpu_sync *sync, struct reservation_object *resv, - void *owner) + void *owner, bool explicit_sync) { struct reservation_object_list *flist; struct dma_fence *f; @@ -191,6 +191,9 @@ int amdgpu_sync_resv(struct amdgpu_device *adev, f = reservation_object_get_excl(resv); r = amdgpu_sync_fence(adev, sync, f); + if (explicit_sync) + return r; + flist = reservation_object_get_list(resv); if (!flist || r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h index dc7687993317..70d7e3a279a0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.h @@ -45,7 +45,8 @@ int amdgpu_sync_fence(struct amdgpu_device *adev, struct amdgpu_sync *sync, int amdgpu_sync_resv(struct amdgpu_device *adev, struct amdgpu_sync *sync, struct reservation_object *resv, - void *owner); + void *owner, + bool explicit_sync); struct dma_fence *amdgpu_sync_peek_fence(struct amdgpu_sync *sync, struct amdgpu_ring *ring); struct dma_fence *amdgpu_sync_get_fence(struct amdgpu_sync *sync); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 10952c3e5eb6..a2282bacf960 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -1489,7 +1489,8 @@ int amdgpu_copy_buffer(struct amdgpu_ring *ring, uint64_t src_offset, job->vm_needs_flush = vm_needs_flush; if (resv) { r = amdgpu_sync_resv(adev, &job->sync, resv, - AMDGPU_FENCE_OWNER_UNDEFINED); + AMDGPU_FENCE_OWNER_UNDEFINED, + false); if (r) { DRM_ERROR("sync failed (%d).\n", r); goto error_free; @@ -1581,7 +1582,7 @@ int amdgpu_fill_buffer(struct amdgpu_bo *bo, if (resv) { r = amdgpu_sync_resv(adev, &job->sync, resv, - AMDGPU_FENCE_OWNER_UNDEFINED); + AMDGPU_FENCE_OWNER_UNDEFINED, false); if (r) { DRM_ERROR("sync failed (%d).\n", r); goto error_free; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index eb4a01c14eee..c559d76ff695 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1035,7 +1035,7 @@ static int amdgpu_vm_wait_pd(struct amdgpu_device *adev, struct amdgpu_vm *vm, int r; amdgpu_sync_create(&sync); - amdgpu_sync_resv(adev, &sync, vm->root.base.bo->tbo.resv, owner); + amdgpu_sync_resv(adev, &sync, vm->root.base.bo->tbo.resv, owner, false); r = amdgpu_sync_wait(&sync, true); amdgpu_sync_free(&sync); @@ -1176,11 +1176,11 @@ static int amdgpu_vm_update_level(struct amdgpu_device *adev, amdgpu_ring_pad_ib(ring, params.ib); amdgpu_sync_resv(adev, &job->sync, parent->base.bo->tbo.resv, - AMDGPU_FENCE_OWNER_VM); + AMDGPU_FENCE_OWNER_VM, false); if (shadow) amdgpu_sync_resv(adev, &job->sync, shadow->tbo.resv, - AMDGPU_FENCE_OWNER_VM); + AMDGPU_FENCE_OWNER_VM, false); WARN_ON(params.ib->length_dw > ndw); r = amdgpu_job_submit(job, ring, &vm->entity, @@ -1644,7 +1644,7 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev, goto error_free; r = amdgpu_sync_resv(adev, &job->sync, vm->root.base.bo->tbo.resv, - owner); + owner, false); if (r) goto error_free; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 4c6e8c482ee4..b62484af8ccb 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -91,6 +91,8 @@ extern "C" { #define AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS (1 << 5) /* Flag that BO is always valid in this VM */ #define AMDGPU_GEM_CREATE_VM_ALWAYS_VALID (1 << 6) +/* Flag that BO sharing will be explicitly synchronized */ +#define AMDGPU_GEM_CREATE_EXPLICIT_SYNC (1 << 7) struct drm_amdgpu_gem_create_in { /** the requested memory size */ -- cgit v1.2.3-59-g8ed1b From c2636dc53abd8269a0930bccd564f2f195dba729 Mon Sep 17 00:00:00 2001 From: Andres Rodriguez Date: Thu, 22 Dec 2016 17:06:50 -0500 Subject: drm/amdgpu: add parameter to allocate high priority contexts v11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new context creation parameter to express a global context priority. The priority ranking in descending order is as follows: * AMDGPU_CTX_PRIORITY_HIGH_HW * AMDGPU_CTX_PRIORITY_HIGH_SW * AMDGPU_CTX_PRIORITY_NORMAL * AMDGPU_CTX_PRIORITY_LOW_SW * AMDGPU_CTX_PRIORITY_LOW_HW The driver will attempt to schedule work to the hardware according to the priorities. No latency or throughput guarantees are provided by this patch. This interface intends to service the EGL_IMG_context_priority extension, and vulkan equivalents. Setting a priority above NORMAL requires CAP_SYS_NICE or DRM_MASTER. v2: Instead of using flags, repurpose __pad v3: Swap enum values of _NORMAL _HIGH for backwards compatibility v4: Validate usermode priority and store it v5: Move priority validation into amdgpu_ctx_ioctl(), headline reword v6: add UAPI note regarding priorities requiring CAP_SYS_ADMIN v7: remove ctx->priority v8: added AMDGPU_CTX_PRIORITY_LOW, s/CAP_SYS_ADMIN/CAP_SYS_NICE v9: change the priority parameter to __s32 v10: split priorities into _SW and _HW v11: Allow DRM_MASTER without CAP_SYS_NICE Reviewed-by: Emil Velikov Reviewed-by: Christian König Signed-off-by: Andres Rodriguez Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c | 61 +++++++++++++++++++++++++-- drivers/gpu/drm/amd/scheduler/gpu_scheduler.h | 5 ++- include/uapi/drm/amdgpu_drm.h | 10 ++++- 3 files changed, 70 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index 75c933b1a432..52388b1b52c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -23,13 +23,40 @@ */ #include +#include #include "amdgpu.h" -static int amdgpu_ctx_init(struct amdgpu_device *adev, struct amdgpu_ctx *ctx) +static int amdgpu_ctx_priority_permit(struct drm_file *filp, + enum amd_sched_priority priority) +{ + /* NORMAL and below are accessible by everyone */ + if (priority <= AMD_SCHED_PRIORITY_NORMAL) + return 0; + + if (capable(CAP_SYS_NICE)) + return 0; + + if (drm_is_current_master(filp)) + return 0; + + return -EACCES; +} + +static int amdgpu_ctx_init(struct amdgpu_device *adev, + enum amd_sched_priority priority, + struct drm_file *filp, + struct amdgpu_ctx *ctx) { unsigned i, j; int r; + if (priority < 0 || priority >= AMD_SCHED_PRIORITY_MAX) + return -EINVAL; + + r = amdgpu_ctx_priority_permit(filp, priority); + if (r) + return r; + memset(ctx, 0, sizeof(*ctx)); ctx->adev = adev; kref_init(&ctx->refcount); @@ -51,7 +78,7 @@ static int amdgpu_ctx_init(struct amdgpu_device *adev, struct amdgpu_ctx *ctx) struct amdgpu_ring *ring = adev->rings[i]; struct amd_sched_rq *rq; - rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_NORMAL]; + rq = &ring->sched.sched_rq[priority]; if (ring == &adev->gfx.kiq.ring) continue; @@ -100,6 +127,8 @@ static void amdgpu_ctx_fini(struct amdgpu_ctx *ctx) static int amdgpu_ctx_alloc(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, + struct drm_file *filp, + enum amd_sched_priority priority, uint32_t *id) { struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; @@ -117,8 +146,9 @@ static int amdgpu_ctx_alloc(struct amdgpu_device *adev, kfree(ctx); return r; } + *id = (uint32_t)r; - r = amdgpu_ctx_init(adev, ctx); + r = amdgpu_ctx_init(adev, priority, filp, ctx); if (r) { idr_remove(&mgr->ctx_handles, *id); *id = 0; @@ -188,11 +218,30 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev, return 0; } +static enum amd_sched_priority amdgpu_to_sched_priority(int amdgpu_priority) +{ + switch (amdgpu_priority) { + case AMDGPU_CTX_PRIORITY_HIGH_HW: + return AMD_SCHED_PRIORITY_HIGH_HW; + case AMDGPU_CTX_PRIORITY_HIGH_SW: + return AMD_SCHED_PRIORITY_HIGH_SW; + case AMDGPU_CTX_PRIORITY_NORMAL: + return AMD_SCHED_PRIORITY_NORMAL; + case AMDGPU_CTX_PRIORITY_LOW_SW: + case AMDGPU_CTX_PRIORITY_LOW_HW: + return AMD_SCHED_PRIORITY_LOW; + default: + WARN(1, "Invalid context priority %d\n", amdgpu_priority); + return AMD_SCHED_PRIORITY_NORMAL; + } +} + int amdgpu_ctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { int r; uint32_t id; + enum amd_sched_priority priority; union drm_amdgpu_ctx *args = data; struct amdgpu_device *adev = dev->dev_private; @@ -200,10 +249,14 @@ int amdgpu_ctx_ioctl(struct drm_device *dev, void *data, r = 0; id = args->in.ctx_id; + priority = amdgpu_to_sched_priority(args->in.priority); + + if (priority >= AMD_SCHED_PRIORITY_MAX) + return -EINVAL; switch (args->in.op) { case AMDGPU_CTX_OP_ALLOC_CTX: - r = amdgpu_ctx_alloc(adev, fpriv, &id); + r = amdgpu_ctx_alloc(adev, fpriv, filp, priority, &id); args->out.alloc.ctx_id = id; break; case AMDGPU_CTX_OP_FREE_CTX: diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h index f9d8f28efd16..38e622ce06de 100644 --- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h +++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h @@ -115,7 +115,10 @@ struct amd_sched_backend_ops { enum amd_sched_priority { AMD_SCHED_PRIORITY_MIN, - AMD_SCHED_PRIORITY_NORMAL = AMD_SCHED_PRIORITY_MIN, + AMD_SCHED_PRIORITY_LOW = AMD_SCHED_PRIORITY_MIN, + AMD_SCHED_PRIORITY_NORMAL, + AMD_SCHED_PRIORITY_HIGH_SW, + AMD_SCHED_PRIORITY_HIGH_HW, AMD_SCHED_PRIORITY_KERNEL, AMD_SCHED_PRIORITY_MAX }; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index b62484af8ccb..94ef0af492dc 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -168,13 +168,21 @@ union drm_amdgpu_bo_list { /* unknown cause */ #define AMDGPU_CTX_UNKNOWN_RESET 3 +/* Context priority level */ +#define AMDGPU_CTX_PRIORITY_LOW_HW -1023 +#define AMDGPU_CTX_PRIORITY_LOW_SW -512 +#define AMDGPU_CTX_PRIORITY_NORMAL 0 +/* Selecting a priority above NORMAL requires CAP_SYS_NICE or DRM_MASTER */ +#define AMDGPU_CTX_PRIORITY_HIGH_SW 512 +#define AMDGPU_CTX_PRIORITY_HIGH_HW 1023 + struct drm_amdgpu_ctx_in { /** AMDGPU_CTX_OP_* */ __u32 op; /** For future use, no flags defined so far */ __u32 flags; __u32 ctx_id; - __u32 _pad; + __s32 priority; }; union drm_amdgpu_ctx_out { -- cgit v1.2.3-59-g8ed1b From f3d19bf80d6c7bfe5922c09604a402ef176da41f Mon Sep 17 00:00:00 2001 From: Andres Rodriguez Date: Mon, 26 Jun 2017 16:12:10 -0400 Subject: drm/amdgpu: introduce AMDGPU_CTX_PRIORITY_UNSET MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use _INVALID to identify bad parameters and _UNSET to represent the lack of interest in a specific value. Signed-off-by: Andres Rodriguez Acked-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c | 2 ++ drivers/gpu/drm/amd/scheduler/gpu_scheduler.h | 3 ++- include/uapi/drm/amdgpu_drm.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index d3d63f78bec9..29eebdc30a4c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -230,6 +230,8 @@ static enum amd_sched_priority amdgpu_to_sched_priority(int amdgpu_priority) case AMDGPU_CTX_PRIORITY_LOW_SW: case AMDGPU_CTX_PRIORITY_LOW_HW: return AMD_SCHED_PRIORITY_LOW; + case AMDGPU_CTX_PRIORITY_UNSET: + return AMD_SCHED_PRIORITY_UNSET; default: WARN(1, "Invalid context priority %d\n", amdgpu_priority); return AMD_SCHED_PRIORITY_INVALID; diff --git a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h index 4b528f7abbfe..52c8e5447624 100644 --- a/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h +++ b/drivers/gpu/drm/amd/scheduler/gpu_scheduler.h @@ -122,7 +122,8 @@ enum amd_sched_priority { AMD_SCHED_PRIORITY_HIGH_HW, AMD_SCHED_PRIORITY_KERNEL, AMD_SCHED_PRIORITY_MAX, - AMD_SCHED_PRIORITY_INVALID = -1 + AMD_SCHED_PRIORITY_INVALID = -1, + AMD_SCHED_PRIORITY_UNSET = -2 }; /** diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 94ef0af492dc..7ea33b57893a 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -169,6 +169,7 @@ union drm_amdgpu_bo_list { #define AMDGPU_CTX_UNKNOWN_RESET 3 /* Context priority level */ +#define AMDGPU_CTX_PRIORITY_UNSET -2048 #define AMDGPU_CTX_PRIORITY_LOW_HW -1023 #define AMDGPU_CTX_PRIORITY_LOW_SW -512 #define AMDGPU_CTX_PRIORITY_NORMAL 0 -- cgit v1.2.3-59-g8ed1b From 52c6a62c64fac03a434cdacf6ef671c6a9e9000f Mon Sep 17 00:00:00 2001 From: Andres Rodriguez Date: Mon, 26 Jun 2017 16:17:13 -0400 Subject: drm/amdgpu: add interface for editing a foreign process's priority v3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AMDGPU_SCHED_OP_PROCESS_PRIORITY_OVERRIDE ioctls are used to set the priority of a different process in the current system. When a request is dropped, the process's contexts will be restored to the priority specified at context creation time. A request can be dropped by setting the override priority to AMDGPU_CTX_PRIORITY_UNSET. An fd is used to identify the remote process. This is simpler than passing a pid number, which is vulnerable to re-use, etc. This functionality is limited to DRM_MASTER since abuse of this interface can have a negative impact on the system's performance. v2: removed unused output structure v3: change refcounted interface for a regular set operation Signed-off-by: Andres Rodriguez Acked-by: Christian König Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/Makefile | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c | 21 +----- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 2 + drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c | 109 ++++++++++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_sched.h | 34 ++++++++++ include/uapi/drm/amdgpu_drm.h | 17 +++++ 6 files changed, 164 insertions(+), 21 deletions(-) create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c create mode 100644 drivers/gpu/drm/amd/amdgpu/amdgpu_sched.h (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile index 25a95c95df14..ef9a3b6d7b62 100644 --- a/drivers/gpu/drm/amd/amdgpu/Makefile +++ b/drivers/gpu/drm/amd/amdgpu/Makefile @@ -25,7 +25,7 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \ amdgpu_prime.o amdgpu_vm.o amdgpu_ib.o amdgpu_pll.o \ amdgpu_ucode.o amdgpu_bo_list.o amdgpu_ctx.o amdgpu_sync.o \ amdgpu_gtt_mgr.o amdgpu_vram_mgr.o amdgpu_virt.o amdgpu_atomfirmware.o \ - amdgpu_queue_mgr.o amdgpu_vf_error.o + amdgpu_queue_mgr.o amdgpu_vf_error.o amdgpu_sched.o # add asic specific block amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o cik_ih.o kv_smc.o kv_dpm.o \ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index d2ef24f4b56d..a78b03f65c69 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -25,6 +25,7 @@ #include #include #include "amdgpu.h" +#include "amdgpu_sched.h" static int amdgpu_ctx_priority_permit(struct drm_file *filp, enum amd_sched_priority priority) @@ -220,26 +221,6 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev, return 0; } -static enum amd_sched_priority amdgpu_to_sched_priority(int amdgpu_priority) -{ - switch (amdgpu_priority) { - case AMDGPU_CTX_PRIORITY_HIGH_HW: - return AMD_SCHED_PRIORITY_HIGH_HW; - case AMDGPU_CTX_PRIORITY_HIGH_SW: - return AMD_SCHED_PRIORITY_HIGH_SW; - case AMDGPU_CTX_PRIORITY_NORMAL: - return AMD_SCHED_PRIORITY_NORMAL; - case AMDGPU_CTX_PRIORITY_LOW_SW: - case AMDGPU_CTX_PRIORITY_LOW_HW: - return AMD_SCHED_PRIORITY_LOW; - case AMDGPU_CTX_PRIORITY_UNSET: - return AMD_SCHED_PRIORITY_UNSET; - default: - WARN(1, "Invalid context priority %d\n", amdgpu_priority); - return AMD_SCHED_PRIORITY_INVALID; - } -} - int amdgpu_ctx_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 51841259e23f..82e8d43b235a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -28,6 +28,7 @@ #include #include "amdgpu.h" #include +#include "amdgpu_sched.h" #include "amdgpu_uvd.h" #include "amdgpu_vce.h" @@ -1023,6 +1024,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(AMDGPU_GEM_CREATE, amdgpu_gem_create_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_CTX, amdgpu_ctx_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_VM, amdgpu_vm_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(AMDGPU_SCHED, amdgpu_sched_ioctl, DRM_MASTER), DRM_IOCTL_DEF_DRV(AMDGPU_BO_LIST, amdgpu_bo_list_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_FENCE_TO_HANDLE, amdgpu_cs_fence_to_handle_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), /* KMS */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c new file mode 100644 index 000000000000..cd123306eda7 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c @@ -0,0 +1,109 @@ +/* + * Copyright 2017 Valve Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Andres Rodriguez + */ + +#include +#include +#include +#include "amdgpu.h" + +#include "amdgpu_vm.h" + +enum amd_sched_priority amdgpu_to_sched_priority(int amdgpu_priority) +{ + switch (amdgpu_priority) { + case AMDGPU_CTX_PRIORITY_HIGH_HW: + return AMD_SCHED_PRIORITY_HIGH_HW; + case AMDGPU_CTX_PRIORITY_HIGH_SW: + return AMD_SCHED_PRIORITY_HIGH_SW; + case AMDGPU_CTX_PRIORITY_NORMAL: + return AMD_SCHED_PRIORITY_NORMAL; + case AMDGPU_CTX_PRIORITY_LOW_SW: + case AMDGPU_CTX_PRIORITY_LOW_HW: + return AMD_SCHED_PRIORITY_LOW; + case AMDGPU_CTX_PRIORITY_UNSET: + return AMD_SCHED_PRIORITY_UNSET; + default: + WARN(1, "Invalid context priority %d\n", amdgpu_priority); + return AMD_SCHED_PRIORITY_INVALID; + } +} + +static int amdgpu_sched_process_priority_override(struct amdgpu_device *adev, + int fd, + enum amd_sched_priority priority) +{ + struct file *filp = fcheck(fd); + struct drm_file *file; + struct pid *pid; + struct amdgpu_fpriv *fpriv; + struct amdgpu_ctx *ctx; + uint32_t id; + + if (!filp) + return -EINVAL; + + pid = get_pid(((struct drm_file *)filp->private_data)->pid); + + mutex_lock(&adev->ddev->filelist_mutex); + list_for_each_entry(file, &adev->ddev->filelist, lhead) { + if (file->pid != pid) + continue; + + fpriv = file->driver_priv; + idr_for_each_entry(&fpriv->ctx_mgr.ctx_handles, ctx, id) + amdgpu_ctx_priority_override(ctx, priority); + } + mutex_unlock(&adev->ddev->filelist_mutex); + + put_pid(pid); + + return 0; +} + +int amdgpu_sched_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + union drm_amdgpu_sched *args = data; + struct amdgpu_device *adev = dev->dev_private; + enum amd_sched_priority priority; + int r; + + priority = amdgpu_to_sched_priority(args->in.priority); + if (args->in.flags || priority == AMD_SCHED_PRIORITY_INVALID) + return -EINVAL; + + switch (args->in.op) { + case AMDGPU_SCHED_OP_PROCESS_PRIORITY_OVERRIDE: + r = amdgpu_sched_process_priority_override(adev, + args->in.fd, + priority); + break; + default: + DRM_ERROR("Invalid sched op specified: %d\n", args->in.op); + r = -EINVAL; + break; + } + + return r; +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.h new file mode 100644 index 000000000000..b28c067d3822 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.h @@ -0,0 +1,34 @@ +/* + * Copyright 2017 Valve Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Andres Rodriguez + */ + +#ifndef __AMDGPU_SCHED_H__ +#define __AMDGPU_SCHED_H__ + +#include + +enum amd_sched_priority amdgpu_to_sched_priority(int amdgpu_priority); +int amdgpu_sched_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + +#endif // __AMDGPU_SCHED_H__ diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index 7ea33b57893a..a332de1aeb42 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -53,6 +53,7 @@ extern "C" { #define DRM_AMDGPU_WAIT_FENCES 0x12 #define DRM_AMDGPU_VM 0x13 #define DRM_AMDGPU_FENCE_TO_HANDLE 0x14 +#define DRM_AMDGPU_SCHED 0x15 #define DRM_IOCTL_AMDGPU_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_CREATE, union drm_amdgpu_gem_create) #define DRM_IOCTL_AMDGPU_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_GEM_MMAP, union drm_amdgpu_gem_mmap) @@ -69,6 +70,7 @@ extern "C" { #define DRM_IOCTL_AMDGPU_WAIT_FENCES DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_WAIT_FENCES, union drm_amdgpu_wait_fences) #define DRM_IOCTL_AMDGPU_VM DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_VM, union drm_amdgpu_vm) #define DRM_IOCTL_AMDGPU_FENCE_TO_HANDLE DRM_IOWR(DRM_COMMAND_BASE + DRM_AMDGPU_FENCE_TO_HANDLE, union drm_amdgpu_fence_to_handle) +#define DRM_IOCTL_AMDGPU_SCHED DRM_IOW(DRM_COMMAND_BASE + DRM_AMDGPU_SCHED, union drm_amdgpu_sched) #define AMDGPU_GEM_DOMAIN_CPU 0x1 #define AMDGPU_GEM_DOMAIN_GTT 0x2 @@ -227,6 +229,21 @@ union drm_amdgpu_vm { struct drm_amdgpu_vm_out out; }; +/* sched ioctl */ +#define AMDGPU_SCHED_OP_PROCESS_PRIORITY_OVERRIDE 1 + +struct drm_amdgpu_sched_in { + /* AMDGPU_SCHED_OP_* */ + __u32 op; + __u32 fd; + __s32 priority; + __u32 flags; +}; + +union drm_amdgpu_sched { + struct drm_amdgpu_sched_in in; +}; + /* * This is not a reliable API and you should expect it to fail for any * number of reasons and have fallback path that do not use userptr to -- cgit v1.2.3-59-g8ed1b From 8055af0a4fddb45a8cd925fb9bc71f4b52628c9a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 6 Oct 2017 09:08:34 +0200 Subject: ACPI / PM: Remove stale function header acpi_dev_pm_get_node() isn't used or implemented, so remove it. Signed-off-by: Ulf Hansson Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- include/linux/acpi.h | 5 ----- 1 file changed, 5 deletions(-) (limited to 'include') diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 502af53ec012..3b89b4fe6812 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -868,17 +868,12 @@ int acpi_dev_runtime_suspend(struct device *dev); int acpi_dev_runtime_resume(struct device *dev); int acpi_subsys_runtime_suspend(struct device *dev); int acpi_subsys_runtime_resume(struct device *dev); -struct acpi_device *acpi_dev_pm_get_node(struct device *dev); int acpi_dev_pm_attach(struct device *dev, bool power_on); #else static inline int acpi_dev_runtime_suspend(struct device *dev) { return 0; } static inline int acpi_dev_runtime_resume(struct device *dev) { return 0; } static inline int acpi_subsys_runtime_suspend(struct device *dev) { return 0; } static inline int acpi_subsys_runtime_resume(struct device *dev) { return 0; } -static inline struct acpi_device *acpi_dev_pm_get_node(struct device *dev) -{ - return NULL; -} static inline int acpi_dev_pm_attach(struct device *dev, bool power_on) { return -ENODEV; -- cgit v1.2.3-59-g8ed1b From cf4c950b87ee2f547ad3abd3aca6ae3f3eb3443f Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 14:30:52 -0700 Subject: once: switch to new jump label API Switch the DO_ONCE() macro from the deprecated jump label API to the new one. The new one is more readable, and for DO_ONCE() it also makes the generated code more icache-friendly: now the one-time initialization code is placed out-of-line at the jump target, rather than at the inline fallthrough case. Acked-by: Hannes Frederic Sowa Signed-off-by: Eric Biggers Signed-off-by: David S. Miller --- include/linux/once.h | 6 +++--- lib/once.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/linux/once.h b/include/linux/once.h index 9c98aaa87cbc..724724918e8b 100644 --- a/include/linux/once.h +++ b/include/linux/once.h @@ -5,7 +5,7 @@ #include bool __do_once_start(bool *done, unsigned long *flags); -void __do_once_done(bool *done, struct static_key *once_key, +void __do_once_done(bool *done, struct static_key_true *once_key, unsigned long *flags); /* Call a function exactly once. The idea of DO_ONCE() is to perform @@ -38,8 +38,8 @@ void __do_once_done(bool *done, struct static_key *once_key, ({ \ bool ___ret = false; \ static bool ___done = false; \ - static struct static_key ___once_key = STATIC_KEY_INIT_TRUE; \ - if (static_key_true(&___once_key)) { \ + static DEFINE_STATIC_KEY_TRUE(___once_key); \ + if (static_branch_unlikely(&___once_key)) { \ unsigned long ___flags; \ ___ret = __do_once_start(&___done, &___flags); \ if (unlikely(___ret)) { \ diff --git a/lib/once.c b/lib/once.c index 05c8604627eb..831c5a6b0bb2 100644 --- a/lib/once.c +++ b/lib/once.c @@ -5,7 +5,7 @@ struct once_work { struct work_struct work; - struct static_key *key; + struct static_key_true *key; }; static void once_deferred(struct work_struct *w) @@ -14,11 +14,11 @@ static void once_deferred(struct work_struct *w) work = container_of(w, struct once_work, work); BUG_ON(!static_key_enabled(work->key)); - static_key_slow_dec(work->key); + static_branch_disable(work->key); kfree(work); } -static void once_disable_jump(struct static_key *key) +static void once_disable_jump(struct static_key_true *key) { struct once_work *w; @@ -51,7 +51,7 @@ bool __do_once_start(bool *done, unsigned long *flags) } EXPORT_SYMBOL(__do_once_start); -void __do_once_done(bool *done, struct static_key *once_key, +void __do_once_done(bool *done, struct static_key_true *once_key, unsigned long *flags) __releases(once_lock) { -- cgit v1.2.3-59-g8ed1b From ceaa001a170e43608854d5290a48064f57b565ed Mon Sep 17 00:00:00 2001 From: William Tu Date: Wed, 4 Oct 2017 17:03:12 -0700 Subject: openvswitch: Add erspan tunnel support. Add erspan netlink interface for OVS. Signed-off-by: William Tu Cc: Pravin B Shelar Signed-off-by: David S. Miller --- include/uapi/linux/openvswitch.h | 1 + net/openvswitch/flow_netlink.c | 51 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 156ee4cab82e..efdbfbfd3ee2 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -359,6 +359,7 @@ enum ovs_tunnel_key_attr { OVS_TUNNEL_KEY_ATTR_IPV6_SRC, /* struct in6_addr src IPv6 address. */ OVS_TUNNEL_KEY_ATTR_IPV6_DST, /* struct in6_addr dst IPv6 address. */ OVS_TUNNEL_KEY_ATTR_PAD, + OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS, /* be32 ERSPAN index. */ __OVS_TUNNEL_KEY_ATTR_MAX }; diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index e8eb427ce6d1..fc0ca9a89b8e 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -48,6 +48,7 @@ #include #include #include +#include #include "flow_netlink.h" @@ -319,7 +320,8 @@ size_t ovs_tun_key_attr_size(void) * OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS and covered by it. */ + nla_total_size(2) /* OVS_TUNNEL_KEY_ATTR_TP_SRC */ - + nla_total_size(2); /* OVS_TUNNEL_KEY_ATTR_TP_DST */ + + nla_total_size(2) /* OVS_TUNNEL_KEY_ATTR_TP_DST */ + + nla_total_size(4); /* OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS */ } size_t ovs_key_attr_size(void) @@ -371,6 +373,7 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] .next = ovs_vxlan_ext_key_lens }, [OVS_TUNNEL_KEY_ATTR_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, [OVS_TUNNEL_KEY_ATTR_IPV6_DST] = { .len = sizeof(struct in6_addr) }, + [OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS] = { .len = sizeof(u32) }, }; /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */ @@ -593,6 +596,33 @@ static int vxlan_tun_opt_from_nlattr(const struct nlattr *attr, return 0; } +static int erspan_tun_opt_from_nlattr(const struct nlattr *attr, + struct sw_flow_match *match, bool is_mask, + bool log) +{ + unsigned long opt_key_offset; + struct erspan_metadata opts; + + BUILD_BUG_ON(sizeof(opts) > sizeof(match->key->tun_opts)); + + memset(&opts, 0, sizeof(opts)); + opts.index = nla_get_be32(attr); + + /* Index has only 20-bit */ + if (ntohl(opts.index) & ~INDEX_MASK) { + OVS_NLERR(log, "ERSPAN index number %x too large.", + ntohl(opts.index)); + return -EINVAL; + } + + SW_FLOW_KEY_PUT(match, tun_opts_len, sizeof(opts), is_mask); + opt_key_offset = TUN_METADATA_OFFSET(sizeof(opts)); + SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset, &opts, sizeof(opts), + is_mask); + + return 0; +} + static int ip_tun_from_nlattr(const struct nlattr *attr, struct sw_flow_match *match, bool is_mask, bool log) @@ -700,6 +730,19 @@ static int ip_tun_from_nlattr(const struct nlattr *attr, break; case OVS_TUNNEL_KEY_ATTR_PAD: break; + case OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS: + if (opts_type) { + OVS_NLERR(log, "Multiple metadata blocks provided"); + return -EINVAL; + } + + err = erspan_tun_opt_from_nlattr(a, match, is_mask, log); + if (err) + return err; + + tun_flags |= TUNNEL_ERSPAN_OPT; + opts_type = type; + break; default: OVS_NLERR(log, "Unknown IP tunnel attribute %d", type); @@ -824,6 +867,10 @@ static int __ip_tun_to_nlattr(struct sk_buff *skb, else if (output->tun_flags & TUNNEL_VXLAN_OPT && vxlan_opt_to_nlattr(skb, tun_opts, swkey_tun_opts_len)) return -EMSGSIZE; + else if (output->tun_flags & TUNNEL_ERSPAN_OPT && + nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS, + ((struct erspan_metadata *)tun_opts)->index)) + return -EMSGSIZE; } return 0; @@ -2195,6 +2242,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, break; case OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS: break; + case OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS: + break; } }; -- cgit v1.2.3-59-g8ed1b From e0dac41b8c21d41534b04d520c1927c2b0ac9b5e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 2 Oct 2017 05:10:17 +0000 Subject: ASoC: soc-core: add snd_soc_add_component() ALSA SoC platform/codec will be replaced to component soon. But, some function exist in "platform" doesn't exist in "component". Current soc-core has snd_soc_register_component(), but doesn't have snd_soc_add_component() like snd_soc_add_platform(). This patch adds it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 5 +++++ sound/soc/soc-core.c | 34 +++++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 996bdbc125a5..580da1e4f141 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -468,6 +468,11 @@ int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver *codec_drv, struct snd_soc_dai_driver *dai_drv, int num_dai); void snd_soc_unregister_codec(struct device *dev); +int snd_soc_add_component(struct device *dev, + struct snd_soc_component *component, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai); int snd_soc_register_component(struct device *dev, const struct snd_soc_component_driver *component_driver, struct snd_soc_dai_driver *dai_drv, int num_dai); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index fee4b0ef5566..3a1c3b44de5e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3371,20 +3371,14 @@ static void snd_soc_component_del_unlocked(struct snd_soc_component *component) list_del(&component->list); } -int snd_soc_register_component(struct device *dev, - const struct snd_soc_component_driver *component_driver, - struct snd_soc_dai_driver *dai_drv, - int num_dai) +int snd_soc_add_component(struct device *dev, + struct snd_soc_component *component, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai) { - struct snd_soc_component *component; int ret; - component = kzalloc(sizeof(*component), GFP_KERNEL); - if (!component) { - dev_err(dev, "ASoC: Failed to allocate memory\n"); - return -ENOMEM; - } - ret = snd_soc_component_initialize(component, component_driver, dev); if (ret) goto err_free; @@ -3408,6 +3402,24 @@ err_free: kfree(component); return ret; } +EXPORT_SYMBOL_GPL(snd_soc_add_component); + +int snd_soc_register_component(struct device *dev, + const struct snd_soc_component_driver *component_driver, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + struct snd_soc_component *component; + + component = kzalloc(sizeof(*component), GFP_KERNEL); + if (!component) { + dev_err(dev, "ASoC: Failed to allocate memory\n"); + return -ENOMEM; + } + + return snd_soc_add_component(dev, component, component_driver, + dai_drv, num_dai); +} EXPORT_SYMBOL_GPL(snd_soc_register_component); /** -- cgit v1.2.3-59-g8ed1b From c70087e8f16f1dfe703d223aadd95ede1cde8e30 Mon Sep 17 00:00:00 2001 From: "benjamin.gaignard@linaro.org" Date: Mon, 2 Oct 2017 11:34:45 +0200 Subject: drm/drm_of: add drm_of_panel_bridge_remove function This function is the pendant of drm_of_find_panel_or_bridge() to remove a previously allocated panel_bridge. Given a specific port and endpoint it remove the panel bridge. Since drm_panel_bridge_remove() will check that bridge parameter is not NULL and is a real drm_panel_bridge and no a simple bridge it is safe to call it directly. Signed-off-by: Benjamin Gaignard Reviewed-by: Philippe Cornu Tested-by: Philippe Cornu Link: https://patchwork.freedesktop.org/patch/msgid/1506936888-23844-3-git-send-email-benjamin.gaignard@linaro.org --- drivers/gpu/drm/drm_of.c | 33 +++++++++++++++++++++++++++++++++ include/drm/drm_of.h | 8 ++++++++ 2 files changed, 41 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 4c191c050e7d..7a36934ea5db 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -262,3 +262,36 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, return ret; } EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); + +#ifdef CONFIG_DRM_PANEL_BRIDGE +/* + * drm_of_panel_bridge_remove - remove panel bridge + * @np: device tree node containing panel bridge output ports + * + * Remove the panel bridge of a given DT node's port and endpoint number + * + * Returns zero if successful, or one of the standard error codes if it fails. + */ +int drm_of_panel_bridge_remove(const struct device_node *np, + int port, int endpoint) +{ + struct drm_bridge *bridge; + struct device_node *remote; + + remote = of_graph_get_remote_node(np, port, endpoint); + if (!remote) + return -ENODEV; + + bridge = of_drm_find_bridge(remote); + drm_panel_bridge_remove(bridge); + + return 0; +} +#else +int drm_of_panel_bridge_remove(const struct device_node *np, + int port, int endpoint) +{ + return -EINVAL; +} +#endif +EXPORT_SYMBOL_GPL(drm_of_panel_bridge_remove); diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index 104dd517fdbe..390966e4a308 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -29,6 +29,8 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, int port, int endpoint, struct drm_panel **panel, struct drm_bridge **bridge); +int drm_of_panel_bridge_remove(const struct device_node *np, + int port, int endpoint); #else static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, struct device_node *port) @@ -65,6 +67,12 @@ static inline int drm_of_find_panel_or_bridge(const struct device_node *np, { return -EINVAL; } + +static inline int drm_of_panel_bridge_remove(const struct device_node *np, + int port, int endpoint) +{ + return -EINVAL; +} #endif static inline int drm_of_encoder_active_endpoint_id(struct device_node *node, -- cgit v1.2.3-59-g8ed1b From 9e2c2e27301266dee805dda2e6d171931d752087 Mon Sep 17 00:00:00 2001 From: Christian Gmeiner Date: Sun, 24 Sep 2017 15:15:21 +0200 Subject: drm/etnaviv: add infrastructure to query perf counter Make it possible that userspace can query all performance domains and its signals. This information is needed to sample those signals via submit ioctl. At the moment no performance domain is available. Changes from v1 -> v2: - use a 16 bit value for signals - fix padding issues - add id member to domain and signal struct Changes v4 -> v5 - provide for each pipe an own set of pm domains Signed-off-by: Christian Gmeiner Signed-off-by: Lucas Stach --- drivers/gpu/drm/etnaviv/Makefile | 3 +- drivers/gpu/drm/etnaviv/etnaviv_drv.c | 37 ++++++++++ drivers/gpu/drm/etnaviv/etnaviv_perfmon.c | 113 ++++++++++++++++++++++++++++++ drivers/gpu/drm/etnaviv/etnaviv_perfmon.h | 31 ++++++++ include/uapi/drm/etnaviv_drm.h | 27 ++++++- 5 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_perfmon.c create mode 100644 drivers/gpu/drm/etnaviv/etnaviv_perfmon.h (limited to 'include') diff --git a/drivers/gpu/drm/etnaviv/Makefile b/drivers/gpu/drm/etnaviv/Makefile index 4f76c992043f..15c3bfa89a79 100644 --- a/drivers/gpu/drm/etnaviv/Makefile +++ b/drivers/gpu/drm/etnaviv/Makefile @@ -10,6 +10,7 @@ etnaviv-y := \ etnaviv_gpu.o \ etnaviv_iommu_v2.o \ etnaviv_iommu.o \ - etnaviv_mmu.o + etnaviv_mmu.o \ + etnaviv_perfmon.o obj-$(CONFIG_DRM_ETNAVIV) += etnaviv.o diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 2cb4773823c2..8721dc23312f 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -23,6 +23,7 @@ #include "etnaviv_gpu.h" #include "etnaviv_gem.h" #include "etnaviv_mmu.h" +#include "etnaviv_perfmon.h" #ifdef CONFIG_DRM_ETNAVIV_REGISTER_LOGGING static bool reglog; @@ -451,6 +452,40 @@ static int etnaviv_ioctl_gem_wait(struct drm_device *dev, void *data, return ret; } +static int etnaviv_ioctl_pm_query_dom(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct etnaviv_drm_private *priv = dev->dev_private; + struct drm_etnaviv_pm_domain *args = data; + struct etnaviv_gpu *gpu; + + if (args->pipe >= ETNA_MAX_PIPES) + return -EINVAL; + + gpu = priv->gpu[args->pipe]; + if (!gpu) + return -ENXIO; + + return etnaviv_pm_query_dom(gpu, args); +} + +static int etnaviv_ioctl_pm_query_sig(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct etnaviv_drm_private *priv = dev->dev_private; + struct drm_etnaviv_pm_signal *args = data; + struct etnaviv_gpu *gpu; + + if (args->pipe >= ETNA_MAX_PIPES) + return -EINVAL; + + gpu = priv->gpu[args->pipe]; + if (!gpu) + return -ENXIO; + + return etnaviv_pm_query_sig(gpu, args); +} + static const struct drm_ioctl_desc etnaviv_ioctls[] = { #define ETNA_IOCTL(n, func, flags) \ DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags) @@ -463,6 +498,8 @@ static const struct drm_ioctl_desc etnaviv_ioctls[] = { ETNA_IOCTL(WAIT_FENCE, wait_fence, DRM_AUTH|DRM_RENDER_ALLOW), ETNA_IOCTL(GEM_USERPTR, gem_userptr, DRM_AUTH|DRM_RENDER_ALLOW), ETNA_IOCTL(GEM_WAIT, gem_wait, DRM_AUTH|DRM_RENDER_ALLOW), + ETNA_IOCTL(PM_QUERY_DOM, pm_query_dom, DRM_AUTH|DRM_RENDER_ALLOW), + ETNA_IOCTL(PM_QUERY_SIG, pm_query_sig, DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct vm_operations_struct vm_ops = { diff --git a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c new file mode 100644 index 000000000000..08ef01c2a0ef --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 Etnaviv Project + * Copyright (C) 2017 Zodiac Inflight Innovations + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "etnaviv_gpu.h" + +struct etnaviv_pm_domain; + +struct etnaviv_pm_signal { + char name[64]; + u32 data; + + u32 (*sample)(struct etnaviv_gpu *gpu, + const struct etnaviv_pm_domain *domain, + const struct etnaviv_pm_signal *signal); +}; + +struct etnaviv_pm_domain { + char name[64]; + u8 nr_signals; + const struct etnaviv_pm_signal *signal; +}; + +struct etnaviv_pm_domain_meta { + const struct etnaviv_pm_domain *domains; + u32 nr_domains; +}; + +static const struct etnaviv_pm_domain doms_3d[] = { +}; + +static const struct etnaviv_pm_domain doms_2d[] = { +}; + +static const struct etnaviv_pm_domain doms_vg[] = { +}; + +static const struct etnaviv_pm_domain_meta doms_meta[] = { + { + .nr_domains = ARRAY_SIZE(doms_3d), + .domains = &doms_3d[0] + }, + { + .nr_domains = ARRAY_SIZE(doms_2d), + .domains = &doms_2d[0] + }, + { + .nr_domains = ARRAY_SIZE(doms_vg), + .domains = &doms_vg[0] + } +}; + +int etnaviv_pm_query_dom(struct etnaviv_gpu *gpu, + struct drm_etnaviv_pm_domain *domain) +{ + const struct etnaviv_pm_domain_meta *meta = &doms_meta[domain->pipe]; + const struct etnaviv_pm_domain *dom; + + if (domain->iter >= meta->nr_domains) + return -EINVAL; + + dom = meta->domains + domain->iter; + + domain->id = domain->iter; + domain->nr_signals = dom->nr_signals; + strncpy(domain->name, dom->name, sizeof(domain->name)); + + domain->iter++; + if (domain->iter == meta->nr_domains) + domain->iter = 0xff; + + return 0; +} + +int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu, + struct drm_etnaviv_pm_signal *signal) +{ + const struct etnaviv_pm_domain_meta *meta = &doms_meta[signal->pipe]; + const struct etnaviv_pm_domain *dom; + const struct etnaviv_pm_signal *sig; + + if (signal->domain >= meta->nr_domains) + return -EINVAL; + + dom = meta->domains + signal->domain; + + if (signal->iter > dom->nr_signals) + return -EINVAL; + + sig = &dom->signal[signal->iter]; + + signal->id = signal->iter; + strncpy(signal->name, sig->name, sizeof(signal->name)); + + signal->iter++; + if (signal->iter == dom->nr_signals) + signal->iter = 0xffff; + + return 0; +} diff --git a/drivers/gpu/drm/etnaviv/etnaviv_perfmon.h b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.h new file mode 100644 index 000000000000..4589cadc3a4f --- /dev/null +++ b/drivers/gpu/drm/etnaviv/etnaviv_perfmon.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 Etnaviv Project + * Copyright (C) 2017 Zodiac Inflight Innovations + * + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __ETNAVIV_PERFMON_H__ +#define __ETNAVIV_PERFMON_H__ + +struct etnaviv_gpu; +struct drm_etnaviv_pm_domain; +struct drm_etnaviv_pm_signal; + +int etnaviv_pm_query_dom(struct etnaviv_gpu *gpu, + struct drm_etnaviv_pm_domain *domain); + +int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu, + struct drm_etnaviv_pm_signal *signal); + +#endif /* __ETNAVIV_PERFMON_H__ */ diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h index 76f6f78a352b..c065c8308bbd 100644 --- a/include/uapi/drm/etnaviv_drm.h +++ b/include/uapi/drm/etnaviv_drm.h @@ -210,6 +210,27 @@ struct drm_etnaviv_gem_wait { struct drm_etnaviv_timespec timeout; /* in */ }; +/* + * Performance Monitor (PM): + */ + +struct drm_etnaviv_pm_domain { + __u32 pipe; /* in */ + __u8 iter; /* in/out, select pm domain at index iter */ + __u8 id; /* out, id of domain */ + __u16 nr_signals; /* out, how many signals does this domain provide */ + char name[64]; /* out, name of domain */ +}; + +struct drm_etnaviv_pm_signal { + __u32 pipe; /* in */ + __u8 domain; /* in, pm domain index */ + __u8 pad; + __u16 iter; /* in/out, select pm source at index iter */ + __u16 id; /* out, id of signal */ + char name[64]; /* out, name of domain */ +}; + #define DRM_ETNAVIV_GET_PARAM 0x00 /* placeholder: #define DRM_ETNAVIV_SET_PARAM 0x01 @@ -222,7 +243,9 @@ struct drm_etnaviv_gem_wait { #define DRM_ETNAVIV_WAIT_FENCE 0x07 #define DRM_ETNAVIV_GEM_USERPTR 0x08 #define DRM_ETNAVIV_GEM_WAIT 0x09 -#define DRM_ETNAVIV_NUM_IOCTLS 0x0a +#define DRM_ETNAVIV_PM_QUERY_DOM 0x0a +#define DRM_ETNAVIV_PM_QUERY_SIG 0x0b +#define DRM_ETNAVIV_NUM_IOCTLS 0x0c #define DRM_IOCTL_ETNAVIV_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GET_PARAM, struct drm_etnaviv_param) #define DRM_IOCTL_ETNAVIV_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_NEW, struct drm_etnaviv_gem_new) @@ -233,6 +256,8 @@ struct drm_etnaviv_gem_wait { #define DRM_IOCTL_ETNAVIV_WAIT_FENCE DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_WAIT_FENCE, struct drm_etnaviv_wait_fence) #define DRM_IOCTL_ETNAVIV_GEM_USERPTR DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_USERPTR, struct drm_etnaviv_gem_userptr) #define DRM_IOCTL_ETNAVIV_GEM_WAIT DRM_IOW(DRM_COMMAND_BASE + DRM_ETNAVIV_GEM_WAIT, struct drm_etnaviv_gem_wait) +#define DRM_IOCTL_ETNAVIV_PM_QUERY_DOM DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_DOM, struct drm_etnaviv_pm_domain) +#define DRM_IOCTL_ETNAVIV_PM_QUERY_SIG DRM_IOWR(DRM_COMMAND_BASE + DRM_ETNAVIV_PM_QUERY_SIG, struct drm_etnaviv_pm_signal) #if defined(__cplusplus) } -- cgit v1.2.3-59-g8ed1b From 05916bed11b6d4c61b473a76220151a7d0547164 Mon Sep 17 00:00:00 2001 From: Christian Gmeiner Date: Sun, 24 Sep 2017 15:15:22 +0200 Subject: drm/etnaviv: add uapi for perfmon feature Sadly we can not read any registers via command stream so we need to extend the drm_etnaviv_gem_submit struct with performance monitor requests. Those requests gets process before or after the actual submitted command stream. The Vivante kernel driver has a special ioctl to read all perfmon registers at once and return it. Changes from v1 -> v2: - use a 16 bit value for signals - fix padding issues Signed-off-by: Christian Gmeiner Signed-off-by: Lucas Stach --- include/uapi/drm/etnaviv_drm.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include') diff --git a/include/uapi/drm/etnaviv_drm.h b/include/uapi/drm/etnaviv_drm.h index c065c8308bbd..110cc73bf549 100644 --- a/include/uapi/drm/etnaviv_drm.h +++ b/include/uapi/drm/etnaviv_drm.h @@ -150,6 +150,19 @@ struct drm_etnaviv_gem_submit_bo { __u64 presumed; /* in/out, presumed buffer address */ }; +/* performance monitor request (pmr) */ +#define ETNA_PM_PROCESS_PRE 0x0001 +#define ETNA_PM_PROCESS_POST 0x0002 +struct drm_etnaviv_gem_submit_pmr { + __u32 flags; /* in, when to process request (ETNA_PM_PROCESS_x) */ + __u8 domain; /* in, pm domain */ + __u8 pad; + __u16 signal; /* in, pm signal */ + __u32 sequence; /* in, sequence number */ + __u32 read_offset; /* in, offset from read_bo */ + __u32 read_idx; /* in, index of read_bo buffer */ +}; + /* Each cmdstream submit consists of a table of buffers involved, and * one or more cmdstream buffers. This allows for conditional execution * (context-restore), and IB buffers needed for per tile/bin draw cmds. @@ -175,6 +188,9 @@ struct drm_etnaviv_gem_submit { __u64 stream; /* in, ptr to cmdstream */ __u32 flags; /* in, mask of ETNA_SUBMIT_x */ __s32 fence_fd; /* in/out, fence fd (see ETNA_SUBMIT_FENCE_FD_x) */ + __u64 pmrs; /* in, ptr to array of submit_pmr's */ + __u32 nr_pmrs; /* in, number of submit_pmr's */ + __u32 pad; }; /* The normal way to synchronize with the GPU is just to CPU_PREP on -- cgit v1.2.3-59-g8ed1b From de8cd83e91bc3ee212b3e6ec6e4283af9e4ab269 Mon Sep 17 00:00:00 2001 From: Steve Grubb Date: Mon, 2 Oct 2017 20:21:39 -0400 Subject: audit: Record fanotify access control decisions The fanotify interface allows user space daemons to make access control decisions. Under common criteria requirements, we need to optionally record decisions based on policy. This patch adds a bit mask, FAN_AUDIT, that a user space daemon can 'or' into the response decision which will tell the kernel that it made a decision and record it. It would be used something like this in user space code: response.response = FAN_DENY | FAN_AUDIT; write(fd, &response, sizeof(struct fanotify_response)); When the syscall ends, the audit system will record the decision as a AUDIT_FANOTIFY auxiliary record to denote that the reason this event occurred is the result of an access control decision from fanotify rather than DAC or MAC policy. A sample event looks like this: type=PATH msg=audit(1504310584.332:290): item=0 name="./evil-ls" inode=1319561 dev=fc:03 mode=0100755 ouid=1000 ogid=1000 rdev=00:00 obj=unconfined_u:object_r:user_home_t:s0 nametype=NORMAL type=CWD msg=audit(1504310584.332:290): cwd="/home/sgrubb" type=SYSCALL msg=audit(1504310584.332:290): arch=c000003e syscall=2 success=no exit=-1 a0=32cb3fca90 a1=0 a2=43 a3=8 items=1 ppid=901 pid=959 auid=1000 uid=1000 gid=1000 euid=1000 suid=1000 fsuid=1000 egid=1000 sgid=1000 fsgid=1000 tty=pts1 ses=3 comm="bash" exe="/usr/bin/bash" subj=unconfined_u:unconfined_r:unconfined_t: s0-s0:c0.c1023 key=(null) type=FANOTIFY msg=audit(1504310584.332:290): resp=2 Prior to using the audit flag, the developer needs to call fanotify_init or'ing in FAN_ENABLE_AUDIT to ensure that the kernel supports auditing. The calling process must also have the CAP_AUDIT_WRITE capability. Signed-off-by: sgrubb Reviewed-by: Amir Goldstein Signed-off-by: Jan Kara --- fs/notify/fanotify/fanotify.c | 8 +++++++- fs/notify/fanotify/fanotify_user.c | 16 +++++++++++++++- fs/notify/fdinfo.c | 3 +++ include/linux/audit.h | 10 ++++++++++ include/linux/fsnotify_backend.h | 1 + include/uapi/linux/audit.h | 1 + include/uapi/linux/fanotify.h | 3 +++ kernel/auditsc.c | 6 ++++++ 8 files changed, 46 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 2fa99aeaa095..1968d21a3f37 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "fanotify.h" @@ -78,7 +79,7 @@ static int fanotify_get_response(struct fsnotify_group *group, fsnotify_finish_user_wait(iter_info); out: /* userspace responded, convert to something usable */ - switch (event->response) { + switch (event->response & ~FAN_AUDIT) { case FAN_ALLOW: ret = 0; break; @@ -86,6 +87,11 @@ out: default: ret = -EPERM; } + + /* Check if the response should be audited */ + if (event->response & FAN_AUDIT) + audit_fanotify(event->response & ~FAN_AUDIT); + event->response = 0; pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 907a481ac781..0455ea729384 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -179,7 +179,7 @@ static int process_access_response(struct fsnotify_group *group, * userspace can send a valid response or we will clean it up after the * timeout */ - switch (response) { + switch (response & ~FAN_AUDIT) { case FAN_ALLOW: case FAN_DENY: break; @@ -190,6 +190,9 @@ static int process_access_response(struct fsnotify_group *group, if (fd < 0) return -EINVAL; + if ((response & FAN_AUDIT) && !group->fanotify_data.audit) + return -EINVAL; + event = dequeue_event(group, fd); if (!event) return -ENOENT; @@ -721,7 +724,11 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) if (!capable(CAP_SYS_ADMIN)) return -EPERM; +#ifdef CONFIG_AUDITSYSCALL + if (flags & ~(FAN_ALL_INIT_FLAGS | FAN_ENABLE_AUDIT)) +#else if (flags & ~FAN_ALL_INIT_FLAGS) +#endif return -EINVAL; if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS) @@ -805,6 +812,13 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) group->fanotify_data.max_marks = FANOTIFY_DEFAULT_MAX_MARKS; } + if (flags & FAN_ENABLE_AUDIT) { + fd = -EPERM; + if (!capable(CAP_AUDIT_WRITE)) + goto out_destroy_group; + group->fanotify_data.audit = true; + } + fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); if (fd < 0) goto out_destroy_group; diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c index dd63aa9a6f9a..645ab561e790 100644 --- a/fs/notify/fdinfo.c +++ b/fs/notify/fdinfo.c @@ -156,6 +156,9 @@ void fanotify_show_fdinfo(struct seq_file *m, struct file *f) if (group->fanotify_data.max_marks == UINT_MAX) flags |= FAN_UNLIMITED_MARKS; + if (group->fanotify_data.audit) + flags |= FAN_ENABLE_AUDIT; + seq_printf(m, "fanotify flags:%x event-flags:%x\n", flags, group->fanotify_data.f_flags); diff --git a/include/linux/audit.h b/include/linux/audit.h index cb708eb8accc..d66220dac364 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -356,6 +356,7 @@ extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, extern void __audit_log_capset(const struct cred *new, const struct cred *old); extern void __audit_mmap_fd(int fd, int flags); extern void __audit_log_kern_module(char *name); +extern void __audit_fanotify(unsigned int response); static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp) { @@ -452,6 +453,12 @@ static inline void audit_log_kern_module(char *name) __audit_log_kern_module(name); } +static inline void audit_fanotify(unsigned int response) +{ + if (!audit_dummy_context()) + __audit_fanotify(response); +} + extern int audit_n_rules; extern int audit_signals; #else /* CONFIG_AUDITSYSCALL */ @@ -568,6 +575,9 @@ static inline void audit_log_kern_module(char *name) { } +static inline void audit_fanotify(unsigned int response) +{ } + static inline void audit_ptrace(struct task_struct *t) { } #define audit_n_rules 0 diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index c6c69318752b..4a474f972910 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -190,6 +190,7 @@ struct fsnotify_group { int f_flags; unsigned int max_marks; struct user_struct *user; + bool audit; } fanotify_data; #endif /* CONFIG_FANOTIFY */ }; diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index 0714a66f0e0c..221f8b7f01b2 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -112,6 +112,7 @@ #define AUDIT_FEATURE_CHANGE 1328 /* audit log listing feature changes */ #define AUDIT_REPLACE 1329 /* Replace auditd if this packet unanswerd */ #define AUDIT_KERN_MODULE 1330 /* Kernel Module events */ +#define AUDIT_FANOTIFY 1331 /* Fanotify access decision */ #define AUDIT_AVC 1400 /* SE Linux avc denial or grant */ #define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */ diff --git a/include/uapi/linux/fanotify.h b/include/uapi/linux/fanotify.h index 030508d195d3..5dda19a9a947 100644 --- a/include/uapi/linux/fanotify.h +++ b/include/uapi/linux/fanotify.h @@ -35,6 +35,7 @@ #define FAN_UNLIMITED_QUEUE 0x00000010 #define FAN_UNLIMITED_MARKS 0x00000020 +#define FAN_ENABLE_AUDIT 0x00000040 #define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \ FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |\ @@ -99,6 +100,8 @@ struct fanotify_response { /* Legit userspace responses to a _PERM event */ #define FAN_ALLOW 0x01 #define FAN_DENY 0x02 +#define FAN_AUDIT 0x10 /* Bit mask to create audit record for result */ + /* No fd set in event */ #define FAN_NOFD -1 diff --git a/kernel/auditsc.c b/kernel/auditsc.c index ecc23e25c9eb..9c723e978245 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -2390,6 +2390,12 @@ void __audit_log_kern_module(char *name) context->type = AUDIT_KERN_MODULE; } +void __audit_fanotify(unsigned int response) +{ + audit_log(current->audit_context, GFP_KERNEL, + AUDIT_FANOTIFY, "resp=%u", response); +} + static void audit_log_task(struct audit_buffer *ab) { kuid_t auid, uid; -- cgit v1.2.3-59-g8ed1b From 8264c3214f28b52b399d9e03bfa7feec275a0d71 Mon Sep 17 00:00:00 2001 From: Rakesh Pandit Date: Mon, 9 Oct 2017 13:34:41 +0300 Subject: writeback: merge try_to_writeback_inodes_sb_nr() into caller Since commit 925a6efb8ff0c ("Btrfs: stop using try_to_writeback_inodes_sb_nr to flush delalloc") this function hasn't been used outside so stop exporting it. In addition we merge it into try_to_writeback_inodes_sb() which is the only caller. Also change return type of try_to_writeback_inodes_sb to void as the only user ext4 doesn't care. Reviewed-by: Jan Kara Signed-off-by: Rakesh Pandit Signed-off-by: Jens Axboe --- fs/fs-writeback.c | 30 ++++++------------------------ include/linux/writeback.h | 4 +--- 2 files changed, 7 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 9e24d604c59c..08f5debd07d1 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -2376,37 +2376,19 @@ void writeback_inodes_sb(struct super_block *sb, enum wb_reason reason) EXPORT_SYMBOL(writeback_inodes_sb); /** - * try_to_writeback_inodes_sb_nr - try to start writeback if none underway + * try_to_writeback_inodes_sb - try to start writeback if none underway * @sb: the superblock - * @nr: the number of pages to write - * @reason: the reason of writeback + * @reason: reason why some writeback work was initiated * - * Invoke writeback_inodes_sb_nr if no writeback is currently underway. - * Returns 1 if writeback was started, 0 if not. + * Invoke __writeback_inodes_sb_nr if no writeback is currently underway. */ -bool try_to_writeback_inodes_sb_nr(struct super_block *sb, unsigned long nr, - enum wb_reason reason) +void try_to_writeback_inodes_sb(struct super_block *sb, enum wb_reason reason) { if (!down_read_trylock(&sb->s_umount)) - return false; + return; - __writeback_inodes_sb_nr(sb, nr, reason, true); + __writeback_inodes_sb_nr(sb, get_nr_dirty_pages(), reason, true); up_read(&sb->s_umount); - return true; -} -EXPORT_SYMBOL(try_to_writeback_inodes_sb_nr); - -/** - * try_to_writeback_inodes_sb - try to start writeback if none underway - * @sb: the superblock - * @reason: reason why some writeback work was initiated - * - * Implement by try_to_writeback_inodes_sb_nr() - * Returns 1 if writeback was started, 0 if not. - */ -bool try_to_writeback_inodes_sb(struct super_block *sb, enum wb_reason reason) -{ - return try_to_writeback_inodes_sb_nr(sb, get_nr_dirty_pages(), reason); } EXPORT_SYMBOL(try_to_writeback_inodes_sb); diff --git a/include/linux/writeback.h b/include/linux/writeback.h index dd1d2c23f743..e15ec14085ad 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -163,9 +163,7 @@ struct bdi_writeback; void writeback_inodes_sb(struct super_block *, enum wb_reason reason); void writeback_inodes_sb_nr(struct super_block *, unsigned long nr, enum wb_reason reason); -bool try_to_writeback_inodes_sb(struct super_block *, enum wb_reason reason); -bool try_to_writeback_inodes_sb_nr(struct super_block *, unsigned long nr, - enum wb_reason reason); +void try_to_writeback_inodes_sb(struct super_block *sb, enum wb_reason reason); void sync_inodes_sb(struct super_block *); void wakeup_flusher_threads(enum wb_reason reason); void wakeup_flusher_threads_bdi(struct backing_dev_info *bdi, -- cgit v1.2.3-59-g8ed1b From 9d18717790c43c904fabe9da7a4c6b2ebed2c4d8 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Fri, 6 Oct 2017 13:06:17 -0700 Subject: IB/core: Simplify sa_path_set_[sd]lid() calls Instead of making every caller convert the second argument of sa_path_set_slid() and sa_path_set_dlid() to big endian format, make these two functions accept LIDs in CPU endian format. This patch does not change any functionality. Signed-off-by: Bart Van Assche Cc: Sean Hefty Cc: Dasaratharaman Chandramouli Cc: Don Hiatt Cc: Ira Weiny Signed-off-by: Doug Ledford --- drivers/infiniband/core/cm.c | 26 ++++++++++++-------------- drivers/infiniband/core/uverbs_marshall.c | 10 +++++----- drivers/infiniband/ulp/srp/ib_srp.c | 2 +- include/rdma/ib_sa.h | 12 ++++++------ 4 files changed, 24 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 4c4b46586af2..d80911d4abb7 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -1472,31 +1472,29 @@ static void cm_format_path_lid_from_req(struct cm_req_msg *req_msg, if (primary_path->rec_type != SA_PATH_REC_TYPE_OPA) { sa_path_set_dlid(primary_path, - htonl(ntohs(req_msg->primary_local_lid))); + ntohs(req_msg->primary_local_lid)); sa_path_set_slid(primary_path, - htonl(ntohs(req_msg->primary_remote_lid))); + ntohs(req_msg->primary_remote_lid)); } else { lid = opa_get_lid_from_gid(&req_msg->primary_local_gid); - sa_path_set_dlid(primary_path, cpu_to_be32(lid)); + sa_path_set_dlid(primary_path, lid); lid = opa_get_lid_from_gid(&req_msg->primary_remote_gid); - sa_path_set_slid(primary_path, cpu_to_be32(lid)); + sa_path_set_slid(primary_path, lid); } if (!cm_req_has_alt_path(req_msg)) return; if (alt_path->rec_type != SA_PATH_REC_TYPE_OPA) { - sa_path_set_dlid(alt_path, - htonl(ntohs(req_msg->alt_local_lid))); - sa_path_set_slid(alt_path, - htonl(ntohs(req_msg->alt_remote_lid))); + sa_path_set_dlid(alt_path, ntohs(req_msg->alt_local_lid)); + sa_path_set_slid(alt_path, ntohs(req_msg->alt_remote_lid)); } else { lid = opa_get_lid_from_gid(&req_msg->alt_local_gid); - sa_path_set_dlid(alt_path, cpu_to_be32(lid)); + sa_path_set_dlid(alt_path, lid); lid = opa_get_lid_from_gid(&req_msg->alt_remote_gid); - sa_path_set_slid(alt_path, cpu_to_be32(lid)); + sa_path_set_slid(alt_path, lid); } } @@ -3037,14 +3035,14 @@ static void cm_format_path_lid_from_lap(struct cm_lap_msg *lap_msg, u32 lid; if (path->rec_type != SA_PATH_REC_TYPE_OPA) { - sa_path_set_dlid(path, htonl(ntohs(lap_msg->alt_local_lid))); - sa_path_set_slid(path, htonl(ntohs(lap_msg->alt_remote_lid))); + sa_path_set_dlid(path, ntohs(lap_msg->alt_local_lid)); + sa_path_set_slid(path, ntohs(lap_msg->alt_remote_lid)); } else { lid = opa_get_lid_from_gid(&lap_msg->alt_local_gid); - sa_path_set_dlid(path, cpu_to_be32(lid)); + sa_path_set_dlid(path, lid); lid = opa_get_lid_from_gid(&lap_msg->alt_remote_gid); - sa_path_set_slid(path, cpu_to_be32(lid)); + sa_path_set_slid(path, lid); } } diff --git a/drivers/infiniband/core/uverbs_marshall.c b/drivers/infiniband/core/uverbs_marshall.c index bd0acf376af0..aeb2824efbc7 100644 --- a/drivers/infiniband/core/uverbs_marshall.c +++ b/drivers/infiniband/core/uverbs_marshall.c @@ -176,18 +176,18 @@ EXPORT_SYMBOL(ib_copy_path_rec_to_user); void ib_copy_path_rec_from_user(struct sa_path_rec *dst, struct ib_user_path_rec *src) { - __be32 slid, dlid; + u32 slid, dlid; memset(dst, 0, sizeof(*dst)); if ((ib_is_opa_gid((union ib_gid *)src->sgid)) || (ib_is_opa_gid((union ib_gid *)src->dgid))) { dst->rec_type = SA_PATH_REC_TYPE_OPA; - slid = htonl(opa_get_lid_from_gid((union ib_gid *)src->sgid)); - dlid = htonl(opa_get_lid_from_gid((union ib_gid *)src->dgid)); + slid = opa_get_lid_from_gid((union ib_gid *)src->sgid); + dlid = opa_get_lid_from_gid((union ib_gid *)src->dgid); } else { dst->rec_type = SA_PATH_REC_TYPE_IB; - slid = htonl(ntohs(src->slid)); - dlid = htonl(ntohs(src->dlid)); + slid = ntohs(src->slid); + dlid = ntohs(src->dlid); } memcpy(dst->dgid.raw, src->dgid, sizeof dst->dgid); memcpy(dst->sgid.raw, src->sgid, sizeof dst->sgid); diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index fa5ccdb3bb2a..778a08250f16 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -2403,7 +2403,7 @@ static void srp_cm_rej_handler(struct ib_cm_id *cm_id, switch (event->param.rej_rcvd.reason) { case IB_CM_REJ_PORT_CM_REDIRECT: cpi = event->param.rej_rcvd.ari; - sa_path_set_dlid(&ch->path, htonl(ntohs(cpi->redirect_lid))); + sa_path_set_dlid(&ch->path, ntohs(cpi->redirect_lid)); ch->path.pkey = cpi->redirect_pkey; cm_id->remote_cm_qpn = be32_to_cpu(cpi->redirect_qp) & 0x00ffffff; memcpy(ch->path.dgid.raw, cpi->redirect_gid, 16); diff --git a/include/rdma/ib_sa.h b/include/rdma/ib_sa.h index 355b81f4242d..1f7f604db5aa 100644 --- a/include/rdma/ib_sa.h +++ b/include/rdma/ib_sa.h @@ -590,20 +590,20 @@ static inline bool sa_path_is_roce(struct sa_path_rec *rec) (rec->rec_type == SA_PATH_REC_TYPE_ROCE_V2)); } -static inline void sa_path_set_slid(struct sa_path_rec *rec, __be32 slid) +static inline void sa_path_set_slid(struct sa_path_rec *rec, u32 slid) { if (rec->rec_type == SA_PATH_REC_TYPE_IB) - rec->ib.slid = htons(ntohl(slid)); + rec->ib.slid = cpu_to_be16(slid); else if (rec->rec_type == SA_PATH_REC_TYPE_OPA) - rec->opa.slid = slid; + rec->opa.slid = cpu_to_be32(slid); } -static inline void sa_path_set_dlid(struct sa_path_rec *rec, __be32 dlid) +static inline void sa_path_set_dlid(struct sa_path_rec *rec, u32 dlid) { if (rec->rec_type == SA_PATH_REC_TYPE_IB) - rec->ib.dlid = htons(ntohl(dlid)); + rec->ib.dlid = cpu_to_be16(dlid); else if (rec->rec_type == SA_PATH_REC_TYPE_OPA) - rec->opa.dlid = dlid; + rec->opa.dlid = cpu_to_be32(dlid); } static inline void sa_path_set_raw_traffic(struct sa_path_rec *rec, -- cgit v1.2.3-59-g8ed1b From e7bf8249e8f1bac64885eeccb55bcf6111901a81 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 9 Oct 2017 10:30:10 -0700 Subject: bpf: encapsulate verifier log state into a structure Put the loose log_* variables into a structure. This will make it simpler to remove the global verifier state in following patches. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf_verifier.h | 13 ++++++++++ kernel/bpf/verifier.c | 57 +++++++++++++++++++++++--------------------- 2 files changed, 43 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index b8d200f60a40..163541ba70d9 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -115,6 +115,19 @@ struct bpf_insn_aux_data { #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ +struct bpf_verifer_log { + u32 level; + char *kbuf; + char __user *ubuf; + u32 len_used; + u32 len_total; +}; + +static inline bool bpf_verifier_log_full(const struct bpf_verifer_log *log) +{ + return log->len_used >= log->len_total - 1; +} + struct bpf_verifier_env; struct bpf_ext_analyzer_ops { int (*insn_hook)(struct bpf_verifier_env *env, diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 6352a88ca6d1..e53458b02249 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -156,8 +156,7 @@ struct bpf_call_arg_meta { /* verbose verifier prints what it's seeing * bpf_check() is called under lock, so no race to access these global vars */ -static u32 log_level, log_size, log_len; -static char *log_buf; +static struct bpf_verifer_log verifier_log; static DEFINE_MUTEX(bpf_verifier_lock); @@ -167,13 +166,15 @@ static DEFINE_MUTEX(bpf_verifier_lock); */ static __printf(1, 2) void verbose(const char *fmt, ...) { + struct bpf_verifer_log *log = &verifier_log; va_list args; - if (log_level == 0 || log_len >= log_size - 1) + if (!log->level || bpf_verifier_log_full(log)) return; va_start(args, fmt); - log_len += vscnprintf(log_buf + log_len, log_size - log_len, fmt, args); + log->len_used += vscnprintf(log->kbuf + log->len_used, + log->len_total - log->len_used, fmt, args); va_end(args); } @@ -886,7 +887,7 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, * need to try adding each of min_value and max_value to off * to make sure our theoretical access will be safe. */ - if (log_level) + if (verifier_log.level) print_verifier_state(state); /* The minimum value is only important with signed * comparisons where we can't assume the floor of a @@ -2956,7 +2957,7 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, verbose("R%d pointer comparison prohibited\n", insn->dst_reg); return -EACCES; } - if (log_level) + if (verifier_log.level) print_verifier_state(this_branch); return 0; } @@ -3712,7 +3713,7 @@ static int do_check(struct bpf_verifier_env *env) return err; if (err == 1) { /* found equivalent state, can prune the search */ - if (log_level) { + if (verifier_log.level) { if (do_print_state) verbose("\nfrom %d to %d: safe\n", prev_insn_idx, insn_idx); @@ -3725,8 +3726,9 @@ static int do_check(struct bpf_verifier_env *env) if (need_resched()) cond_resched(); - if (log_level > 1 || (log_level && do_print_state)) { - if (log_level > 1) + if (verifier_log.level > 1 || + (verifier_log.level && do_print_state)) { + if (verifier_log.level > 1) verbose("%d:", insn_idx); else verbose("\nfrom %d to %d:", @@ -3735,7 +3737,7 @@ static int do_check(struct bpf_verifier_env *env) do_print_state = false; } - if (log_level) { + if (verifier_log.level) { verbose("%d: ", insn_idx); print_bpf_insn(env, insn); } @@ -4389,7 +4391,7 @@ static void free_states(struct bpf_verifier_env *env) int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) { - char __user *log_ubuf = NULL; + struct bpf_verifer_log *log = &verifier_log; struct bpf_verifier_env *env; int ret = -EINVAL; @@ -4414,23 +4416,23 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) /* user requested verbose verifier output * and supplied buffer to store the verification trace */ - log_level = attr->log_level; - log_ubuf = (char __user *) (unsigned long) attr->log_buf; - log_size = attr->log_size; - log_len = 0; + log->level = attr->log_level; + log->ubuf = (char __user *) (unsigned long) attr->log_buf; + log->len_total = attr->log_size; + log->len_used = 0; ret = -EINVAL; - /* log_* values have to be sane */ - if (log_size < 128 || log_size > UINT_MAX >> 8 || - log_level == 0 || log_ubuf == NULL) + /* log attributes have to be sane */ + if (log->len_total < 128 || log->len_total > UINT_MAX >> 8 || + !log->level || !log->ubuf) goto err_unlock; ret = -ENOMEM; - log_buf = vmalloc(log_size); - if (!log_buf) + log->kbuf = vmalloc(log->len_total); + if (!log->kbuf) goto err_unlock; } else { - log_level = 0; + log->level = 0; } env->strict_alignment = !!(attr->prog_flags & BPF_F_STRICT_ALIGNMENT); @@ -4467,15 +4469,16 @@ skip_full_check: if (ret == 0) ret = fixup_bpf_calls(env); - if (log_level && log_len >= log_size - 1) { - BUG_ON(log_len >= log_size); + if (log->level && bpf_verifier_log_full(log)) { + BUG_ON(log->len_used >= log->len_total); /* verifier log exceeded user supplied buffer */ ret = -ENOSPC; /* fall through to return what was recorded */ } /* copy verifier log back to user space including trailing zero */ - if (log_level && copy_to_user(log_ubuf, log_buf, log_len + 1) != 0) { + if (log->level && copy_to_user(log->ubuf, log->kbuf, + log->len_used + 1) != 0) { ret = -EFAULT; goto free_log_buf; } @@ -4502,8 +4505,8 @@ skip_full_check: } free_log_buf: - if (log_level) - vfree(log_buf); + if (log->level) + vfree(log->kbuf); if (!env->prog->aux->used_maps) /* if we didn't copy map pointers into bpf_prog_info, release * them now. Otherwise free_bpf_prog_info() will release them. @@ -4540,7 +4543,7 @@ int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, /* grab the mutex to protect few globals used by verifier */ mutex_lock(&bpf_verifier_lock); - log_level = 0; + verifier_log.level = 0; env->strict_alignment = false; if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) -- cgit v1.2.3-59-g8ed1b From 61bd5218eef349fcacc4976a251bc83a4748b4af Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 9 Oct 2017 10:30:11 -0700 Subject: bpf: move global verifier log into verifier environment The biggest piece of global state protected by the verifier lock is the verifier_log. Move that log to struct bpf_verifier_env. struct bpf_verifier_env has to be passed now to all invocations of verbose(). Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf_verifier.h | 2 + kernel/bpf/verifier.c | 491 +++++++++++++++++++++++-------------------- 2 files changed, 261 insertions(+), 232 deletions(-) (limited to 'include') diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 163541ba70d9..5ddb9a626a51 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -152,6 +152,8 @@ struct bpf_verifier_env { bool allow_ptr_leaks; bool seen_direct_write; struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */ + + struct bpf_verifer_log log; }; int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e53458b02249..a352f93cd4b2 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -153,20 +153,16 @@ struct bpf_call_arg_meta { int access_size; }; -/* verbose verifier prints what it's seeing - * bpf_check() is called under lock, so no race to access these global vars - */ -static struct bpf_verifer_log verifier_log; - static DEFINE_MUTEX(bpf_verifier_lock); /* log_level controls verbosity level of eBPF verifier. * verbose() is used to dump the verification trace to the log, so the user * can figure out what's wrong with the program */ -static __printf(1, 2) void verbose(const char *fmt, ...) +static __printf(2, 3) void verbose(struct bpf_verifier_env *env, + const char *fmt, ...) { - struct bpf_verifer_log *log = &verifier_log; + struct bpf_verifer_log *log = &env->log; va_list args; if (!log->level || bpf_verifier_log_full(log)) @@ -214,7 +210,8 @@ static const char *func_id_name(int id) return "unknown"; } -static void print_verifier_state(struct bpf_verifier_state *state) +static void print_verifier_state(struct bpf_verifier_env *env, + struct bpf_verifier_state *state) { struct bpf_reg_state *reg; enum bpf_reg_type t; @@ -225,21 +222,21 @@ static void print_verifier_state(struct bpf_verifier_state *state) t = reg->type; if (t == NOT_INIT) continue; - verbose(" R%d=%s", i, reg_type_str[t]); + verbose(env, " R%d=%s", i, reg_type_str[t]); if ((t == SCALAR_VALUE || t == PTR_TO_STACK) && tnum_is_const(reg->var_off)) { /* reg->off should be 0 for SCALAR_VALUE */ - verbose("%lld", reg->var_off.value + reg->off); + verbose(env, "%lld", reg->var_off.value + reg->off); } else { - verbose("(id=%d", reg->id); + verbose(env, "(id=%d", reg->id); if (t != SCALAR_VALUE) - verbose(",off=%d", reg->off); + verbose(env, ",off=%d", reg->off); if (type_is_pkt_pointer(t)) - verbose(",r=%d", reg->range); + verbose(env, ",r=%d", reg->range); else if (t == CONST_PTR_TO_MAP || t == PTR_TO_MAP_VALUE || t == PTR_TO_MAP_VALUE_OR_NULL) - verbose(",ks=%d,vs=%d", + verbose(env, ",ks=%d,vs=%d", reg->map_ptr->key_size, reg->map_ptr->value_size); if (tnum_is_const(reg->var_off)) { @@ -247,38 +244,38 @@ static void print_verifier_state(struct bpf_verifier_state *state) * could be a pointer whose offset is too big * for reg->off */ - verbose(",imm=%llx", reg->var_off.value); + verbose(env, ",imm=%llx", reg->var_off.value); } else { if (reg->smin_value != reg->umin_value && reg->smin_value != S64_MIN) - verbose(",smin_value=%lld", + verbose(env, ",smin_value=%lld", (long long)reg->smin_value); if (reg->smax_value != reg->umax_value && reg->smax_value != S64_MAX) - verbose(",smax_value=%lld", + verbose(env, ",smax_value=%lld", (long long)reg->smax_value); if (reg->umin_value != 0) - verbose(",umin_value=%llu", + verbose(env, ",umin_value=%llu", (unsigned long long)reg->umin_value); if (reg->umax_value != U64_MAX) - verbose(",umax_value=%llu", + verbose(env, ",umax_value=%llu", (unsigned long long)reg->umax_value); if (!tnum_is_unknown(reg->var_off)) { char tn_buf[48]; tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); - verbose(",var_off=%s", tn_buf); + verbose(env, ",var_off=%s", tn_buf); } } - verbose(")"); + verbose(env, ")"); } } for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { if (state->stack_slot_type[i] == STACK_SPILL) - verbose(" fp%d=%s", -MAX_BPF_STACK + i, + verbose(env, " fp%d=%s", -MAX_BPF_STACK + i, reg_type_str[state->spilled_regs[i / BPF_REG_SIZE].type]); } - verbose("\n"); + verbose(env, "\n"); } static const char *const bpf_class_string[] = { @@ -333,15 +330,15 @@ static const char *const bpf_jmp_string[16] = { [BPF_EXIT >> 4] = "exit", }; -static void print_bpf_end_insn(const struct bpf_verifier_env *env, +static void print_bpf_end_insn(struct bpf_verifier_env *env, const struct bpf_insn *insn) { - verbose("(%02x) r%d = %s%d r%d\n", insn->code, insn->dst_reg, + verbose(env, "(%02x) r%d = %s%d r%d\n", insn->code, insn->dst_reg, BPF_SRC(insn->code) == BPF_TO_BE ? "be" : "le", insn->imm, insn->dst_reg); } -static void print_bpf_insn(const struct bpf_verifier_env *env, +static void print_bpf_insn(struct bpf_verifier_env *env, const struct bpf_insn *insn) { u8 class = BPF_CLASS(insn->code); @@ -349,23 +346,23 @@ static void print_bpf_insn(const struct bpf_verifier_env *env, if (class == BPF_ALU || class == BPF_ALU64) { if (BPF_OP(insn->code) == BPF_END) { if (class == BPF_ALU64) - verbose("BUG_alu64_%02x\n", insn->code); + verbose(env, "BUG_alu64_%02x\n", insn->code); else print_bpf_end_insn(env, insn); } else if (BPF_OP(insn->code) == BPF_NEG) { - verbose("(%02x) r%d = %s-r%d\n", + verbose(env, "(%02x) r%d = %s-r%d\n", insn->code, insn->dst_reg, class == BPF_ALU ? "(u32) " : "", insn->dst_reg); } else if (BPF_SRC(insn->code) == BPF_X) { - verbose("(%02x) %sr%d %s %sr%d\n", + verbose(env, "(%02x) %sr%d %s %sr%d\n", insn->code, class == BPF_ALU ? "(u32) " : "", insn->dst_reg, bpf_alu_string[BPF_OP(insn->code) >> 4], class == BPF_ALU ? "(u32) " : "", insn->src_reg); } else { - verbose("(%02x) %sr%d %s %s%d\n", + verbose(env, "(%02x) %sr%d %s %s%d\n", insn->code, class == BPF_ALU ? "(u32) " : "", insn->dst_reg, bpf_alu_string[BPF_OP(insn->code) >> 4], @@ -374,46 +371,46 @@ static void print_bpf_insn(const struct bpf_verifier_env *env, } } else if (class == BPF_STX) { if (BPF_MODE(insn->code) == BPF_MEM) - verbose("(%02x) *(%s *)(r%d %+d) = r%d\n", + verbose(env, "(%02x) *(%s *)(r%d %+d) = r%d\n", insn->code, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->dst_reg, insn->off, insn->src_reg); else if (BPF_MODE(insn->code) == BPF_XADD) - verbose("(%02x) lock *(%s *)(r%d %+d) += r%d\n", + verbose(env, "(%02x) lock *(%s *)(r%d %+d) += r%d\n", insn->code, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->dst_reg, insn->off, insn->src_reg); else - verbose("BUG_%02x\n", insn->code); + verbose(env, "BUG_%02x\n", insn->code); } else if (class == BPF_ST) { if (BPF_MODE(insn->code) != BPF_MEM) { - verbose("BUG_st_%02x\n", insn->code); + verbose(env, "BUG_st_%02x\n", insn->code); return; } - verbose("(%02x) *(%s *)(r%d %+d) = %d\n", + verbose(env, "(%02x) *(%s *)(r%d %+d) = %d\n", insn->code, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->dst_reg, insn->off, insn->imm); } else if (class == BPF_LDX) { if (BPF_MODE(insn->code) != BPF_MEM) { - verbose("BUG_ldx_%02x\n", insn->code); + verbose(env, "BUG_ldx_%02x\n", insn->code); return; } - verbose("(%02x) r%d = *(%s *)(r%d %+d)\n", + verbose(env, "(%02x) r%d = *(%s *)(r%d %+d)\n", insn->code, insn->dst_reg, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->src_reg, insn->off); } else if (class == BPF_LD) { if (BPF_MODE(insn->code) == BPF_ABS) { - verbose("(%02x) r0 = *(%s *)skb[%d]\n", + verbose(env, "(%02x) r0 = *(%s *)skb[%d]\n", insn->code, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->imm); } else if (BPF_MODE(insn->code) == BPF_IND) { - verbose("(%02x) r0 = *(%s *)skb[r%d + %d]\n", + verbose(env, "(%02x) r0 = *(%s *)skb[r%d + %d]\n", insn->code, bpf_ldst_string[BPF_SIZE(insn->code) >> 3], insn->src_reg, insn->imm); @@ -428,36 +425,37 @@ static void print_bpf_insn(const struct bpf_verifier_env *env, if (map_ptr && !env->allow_ptr_leaks) imm = 0; - verbose("(%02x) r%d = 0x%llx\n", insn->code, + verbose(env, "(%02x) r%d = 0x%llx\n", insn->code, insn->dst_reg, (unsigned long long)imm); } else { - verbose("BUG_ld_%02x\n", insn->code); + verbose(env, "BUG_ld_%02x\n", insn->code); return; } } else if (class == BPF_JMP) { u8 opcode = BPF_OP(insn->code); if (opcode == BPF_CALL) { - verbose("(%02x) call %s#%d\n", insn->code, + verbose(env, "(%02x) call %s#%d\n", insn->code, func_id_name(insn->imm), insn->imm); } else if (insn->code == (BPF_JMP | BPF_JA)) { - verbose("(%02x) goto pc%+d\n", + verbose(env, "(%02x) goto pc%+d\n", insn->code, insn->off); } else if (insn->code == (BPF_JMP | BPF_EXIT)) { - verbose("(%02x) exit\n", insn->code); + verbose(env, "(%02x) exit\n", insn->code); } else if (BPF_SRC(insn->code) == BPF_X) { - verbose("(%02x) if r%d %s r%d goto pc%+d\n", + verbose(env, "(%02x) if r%d %s r%d goto pc%+d\n", insn->code, insn->dst_reg, bpf_jmp_string[BPF_OP(insn->code) >> 4], insn->src_reg, insn->off); } else { - verbose("(%02x) if r%d %s 0x%x goto pc%+d\n", + verbose(env, "(%02x) if r%d %s 0x%x goto pc%+d\n", insn->code, insn->dst_reg, bpf_jmp_string[BPF_OP(insn->code) >> 4], insn->imm, insn->off); } } else { - verbose("(%02x) %s\n", insn->code, bpf_class_string[class]); + verbose(env, "(%02x) %s\n", + insn->code, bpf_class_string[class]); } } @@ -496,7 +494,7 @@ static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env, env->head = elem; env->stack_size++; if (env->stack_size > BPF_COMPLEXITY_LIMIT_STACK) { - verbose("BPF program is too complex\n"); + verbose(env, "BPF program is too complex\n"); goto err; } return &elem->st; @@ -534,10 +532,11 @@ static void __mark_reg_known_zero(struct bpf_reg_state *reg) __mark_reg_known(reg, 0); } -static void mark_reg_known_zero(struct bpf_reg_state *regs, u32 regno) +static void mark_reg_known_zero(struct bpf_verifier_env *env, + struct bpf_reg_state *regs, u32 regno) { if (WARN_ON(regno >= MAX_BPF_REG)) { - verbose("mark_reg_known_zero(regs, %u)\n", regno); + verbose(env, "mark_reg_known_zero(regs, %u)\n", regno); /* Something bad happened, let's kill all regs */ for (regno = 0; regno < MAX_BPF_REG; regno++) __mark_reg_not_init(regs + regno); @@ -647,10 +646,11 @@ static void __mark_reg_unknown(struct bpf_reg_state *reg) __mark_reg_unbounded(reg); } -static void mark_reg_unknown(struct bpf_reg_state *regs, u32 regno) +static void mark_reg_unknown(struct bpf_verifier_env *env, + struct bpf_reg_state *regs, u32 regno) { if (WARN_ON(regno >= MAX_BPF_REG)) { - verbose("mark_reg_unknown(regs, %u)\n", regno); + verbose(env, "mark_reg_unknown(regs, %u)\n", regno); /* Something bad happened, let's kill all regs */ for (regno = 0; regno < MAX_BPF_REG; regno++) __mark_reg_not_init(regs + regno); @@ -665,10 +665,11 @@ static void __mark_reg_not_init(struct bpf_reg_state *reg) reg->type = NOT_INIT; } -static void mark_reg_not_init(struct bpf_reg_state *regs, u32 regno) +static void mark_reg_not_init(struct bpf_verifier_env *env, + struct bpf_reg_state *regs, u32 regno) { if (WARN_ON(regno >= MAX_BPF_REG)) { - verbose("mark_reg_not_init(regs, %u)\n", regno); + verbose(env, "mark_reg_not_init(regs, %u)\n", regno); /* Something bad happened, let's kill all regs */ for (regno = 0; regno < MAX_BPF_REG; regno++) __mark_reg_not_init(regs + regno); @@ -677,22 +678,23 @@ static void mark_reg_not_init(struct bpf_reg_state *regs, u32 regno) __mark_reg_not_init(regs + regno); } -static void init_reg_state(struct bpf_reg_state *regs) +static void init_reg_state(struct bpf_verifier_env *env, + struct bpf_reg_state *regs) { int i; for (i = 0; i < MAX_BPF_REG; i++) { - mark_reg_not_init(regs, i); + mark_reg_not_init(env, regs, i); regs[i].live = REG_LIVE_NONE; } /* frame pointer */ regs[BPF_REG_FP].type = PTR_TO_STACK; - mark_reg_known_zero(regs, BPF_REG_FP); + mark_reg_known_zero(env, regs, BPF_REG_FP); /* 1st arg to a function */ regs[BPF_REG_1].type = PTR_TO_CTX; - mark_reg_known_zero(regs, BPF_REG_1); + mark_reg_known_zero(env, regs, BPF_REG_1); } enum reg_arg_type { @@ -726,26 +728,26 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, struct bpf_reg_state *regs = env->cur_state.regs; if (regno >= MAX_BPF_REG) { - verbose("R%d is invalid\n", regno); + verbose(env, "R%d is invalid\n", regno); return -EINVAL; } if (t == SRC_OP) { /* check whether register used as source operand can be read */ if (regs[regno].type == NOT_INIT) { - verbose("R%d !read_ok\n", regno); + verbose(env, "R%d !read_ok\n", regno); return -EACCES; } mark_reg_read(&env->cur_state, regno); } else { /* check whether register used as dest operand can be written to */ if (regno == BPF_REG_FP) { - verbose("frame pointer is read only\n"); + verbose(env, "frame pointer is read only\n"); return -EACCES; } regs[regno].live |= REG_LIVE_WRITTEN; if (t == DST_OP) - mark_reg_unknown(regs, regno); + mark_reg_unknown(env, regs, regno); } return 0; } @@ -770,7 +772,8 @@ static bool is_spillable_regtype(enum bpf_reg_type type) /* check_stack_read/write functions track spill/fill of registers, * stack boundary and alignment are checked in check_mem_access() */ -static int check_stack_write(struct bpf_verifier_state *state, int off, +static int check_stack_write(struct bpf_verifier_env *env, + struct bpf_verifier_state *state, int off, int size, int value_regno) { int i, spi = (MAX_BPF_STACK + off) / BPF_REG_SIZE; @@ -783,7 +786,7 @@ static int check_stack_write(struct bpf_verifier_state *state, int off, /* register containing pointer is being spilled into stack */ if (size != BPF_REG_SIZE) { - verbose("invalid size of register spill\n"); + verbose(env, "invalid size of register spill\n"); return -EACCES; } @@ -818,7 +821,8 @@ static void mark_stack_slot_read(const struct bpf_verifier_state *state, int slo } } -static int check_stack_read(struct bpf_verifier_state *state, int off, int size, +static int check_stack_read(struct bpf_verifier_env *env, + struct bpf_verifier_state *state, int off, int size, int value_regno) { u8 *slot_type; @@ -828,12 +832,12 @@ static int check_stack_read(struct bpf_verifier_state *state, int off, int size, if (slot_type[0] == STACK_SPILL) { if (size != BPF_REG_SIZE) { - verbose("invalid size of register spill\n"); + verbose(env, "invalid size of register spill\n"); return -EACCES; } for (i = 1; i < BPF_REG_SIZE; i++) { if (slot_type[i] != STACK_SPILL) { - verbose("corrupted spill memory\n"); + verbose(env, "corrupted spill memory\n"); return -EACCES; } } @@ -849,14 +853,14 @@ static int check_stack_read(struct bpf_verifier_state *state, int off, int size, } else { for (i = 0; i < size; i++) { if (slot_type[i] != STACK_MISC) { - verbose("invalid read from stack off %d+%d size %d\n", + verbose(env, "invalid read from stack off %d+%d size %d\n", off, i, size); return -EACCES; } } if (value_regno >= 0) /* have read misc data from the stack */ - mark_reg_unknown(state->regs, value_regno); + mark_reg_unknown(env, state->regs, value_regno); return 0; } } @@ -868,7 +872,7 @@ static int __check_map_access(struct bpf_verifier_env *env, u32 regno, int off, struct bpf_map *map = env->cur_state.regs[regno].map_ptr; if (off < 0 || size <= 0 || off + size > map->value_size) { - verbose("invalid access to map value, value_size=%d off=%d size=%d\n", + verbose(env, "invalid access to map value, value_size=%d off=%d size=%d\n", map->value_size, off, size); return -EACCES; } @@ -887,8 +891,8 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, * need to try adding each of min_value and max_value to off * to make sure our theoretical access will be safe. */ - if (verifier_log.level) - print_verifier_state(state); + if (env->log.level) + print_verifier_state(env, state); /* The minimum value is only important with signed * comparisons where we can't assume the floor of a * value is 0. If we are using signed variables for our @@ -896,13 +900,14 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, * will have a set floor within our range. */ if (reg->smin_value < 0) { - verbose("R%d min value is negative, either use unsigned index or do a if (index >=0) check.\n", + verbose(env, "R%d min value is negative, either use unsigned index or do a if (index >=0) check.\n", regno); return -EACCES; } err = __check_map_access(env, regno, reg->smin_value + off, size); if (err) { - verbose("R%d min value is outside of the array range\n", regno); + verbose(env, "R%d min value is outside of the array range\n", + regno); return err; } @@ -911,13 +916,14 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno, * If reg->umax_value + off could overflow, treat that as unbounded too. */ if (reg->umax_value >= BPF_MAX_VAR_OFF) { - verbose("R%d unbounded memory access, make sure to bounds check any array access into a map\n", + verbose(env, "R%d unbounded memory access, make sure to bounds check any array access into a map\n", regno); return -EACCES; } err = __check_map_access(env, regno, reg->umax_value + off, size); if (err) - verbose("R%d max value is outside of the array range\n", regno); + verbose(env, "R%d max value is outside of the array range\n", + regno); return err; } @@ -956,7 +962,7 @@ static int __check_packet_access(struct bpf_verifier_env *env, u32 regno, struct bpf_reg_state *reg = ®s[regno]; if (off < 0 || size <= 0 || (u64)off + size > reg->range) { - verbose("invalid access to packet, off=%d size=%d, R%d(id=%d,off=%d,r=%d)\n", + verbose(env, "invalid access to packet, off=%d size=%d, R%d(id=%d,off=%d,r=%d)\n", off, size, regno, reg->id, reg->off, reg->range); return -EACCES; } @@ -979,13 +985,13 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, * detail to prove they're safe. */ if (reg->smin_value < 0) { - verbose("R%d min value is negative, either use unsigned index or do a if (index >=0) check.\n", + verbose(env, "R%d min value is negative, either use unsigned index or do a if (index >=0) check.\n", regno); return -EACCES; } err = __check_packet_access(env, regno, off, size); if (err) { - verbose("R%d offset is outside of the packet\n", regno); + verbose(env, "R%d offset is outside of the packet\n", regno); return err; } return err; @@ -1021,7 +1027,7 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, return 0; } - verbose("invalid bpf_context access off=%d size=%d\n", off, size); + verbose(env, "invalid bpf_context access off=%d size=%d\n", off, size); return -EACCES; } @@ -1039,7 +1045,8 @@ static bool is_pointer_value(struct bpf_verifier_env *env, int regno) return __is_pointer_value(env->allow_ptr_leaks, &env->cur_state.regs[regno]); } -static int check_pkt_ptr_alignment(const struct bpf_reg_state *reg, +static int check_pkt_ptr_alignment(struct bpf_verifier_env *env, + const struct bpf_reg_state *reg, int off, int size, bool strict) { struct tnum reg_off; @@ -1064,7 +1071,8 @@ static int check_pkt_ptr_alignment(const struct bpf_reg_state *reg, char tn_buf[48]; tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); - verbose("misaligned packet access off %d+%s+%d+%d size %d\n", + verbose(env, + "misaligned packet access off %d+%s+%d+%d size %d\n", ip_align, tn_buf, reg->off, off, size); return -EACCES; } @@ -1072,7 +1080,8 @@ static int check_pkt_ptr_alignment(const struct bpf_reg_state *reg, return 0; } -static int check_generic_ptr_alignment(const struct bpf_reg_state *reg, +static int check_generic_ptr_alignment(struct bpf_verifier_env *env, + const struct bpf_reg_state *reg, const char *pointer_desc, int off, int size, bool strict) { @@ -1087,7 +1096,7 @@ static int check_generic_ptr_alignment(const struct bpf_reg_state *reg, char tn_buf[48]; tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); - verbose("misaligned %saccess off %s+%d+%d size %d\n", + verbose(env, "misaligned %saccess off %s+%d+%d size %d\n", pointer_desc, tn_buf, reg->off, off, size); return -EACCES; } @@ -1108,7 +1117,7 @@ static int check_ptr_alignment(struct bpf_verifier_env *env, /* Special case, because of NET_IP_ALIGN. Given metadata sits * right in front, treat it the very same way. */ - return check_pkt_ptr_alignment(reg, off, size, strict); + return check_pkt_ptr_alignment(env, reg, off, size, strict); case PTR_TO_MAP_VALUE: pointer_desc = "value "; break; @@ -1121,7 +1130,8 @@ static int check_ptr_alignment(struct bpf_verifier_env *env, default: break; } - return check_generic_ptr_alignment(reg, pointer_desc, off, size, strict); + return check_generic_ptr_alignment(env, reg, pointer_desc, off, size, + strict); } /* check whether memory at (regno + off) is accessible for t = (read | write) @@ -1153,20 +1163,20 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn if (reg->type == PTR_TO_MAP_VALUE) { if (t == BPF_WRITE && value_regno >= 0 && is_pointer_value(env, value_regno)) { - verbose("R%d leaks addr into map\n", value_regno); + verbose(env, "R%d leaks addr into map\n", value_regno); return -EACCES; } err = check_map_access(env, regno, off, size); if (!err && t == BPF_READ && value_regno >= 0) - mark_reg_unknown(state->regs, value_regno); + mark_reg_unknown(env, state->regs, value_regno); } else if (reg->type == PTR_TO_CTX) { enum bpf_reg_type reg_type = SCALAR_VALUE; if (t == BPF_WRITE && value_regno >= 0 && is_pointer_value(env, value_regno)) { - verbose("R%d leaks addr into ctx\n", value_regno); + verbose(env, "R%d leaks addr into ctx\n", value_regno); return -EACCES; } /* ctx accesses must be at a fixed offset, so that we can @@ -1176,7 +1186,8 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn char tn_buf[48]; tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); - verbose("variable ctx access var_off=%s off=%d size=%d", + verbose(env, + "variable ctx access var_off=%s off=%d size=%d", tn_buf, off, size); return -EACCES; } @@ -1188,9 +1199,10 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn * case, we know the offset is zero. */ if (reg_type == SCALAR_VALUE) - mark_reg_unknown(state->regs, value_regno); + mark_reg_unknown(env, state->regs, value_regno); else - mark_reg_known_zero(state->regs, value_regno); + mark_reg_known_zero(env, state->regs, + value_regno); state->regs[value_regno].id = 0; state->regs[value_regno].off = 0; state->regs[value_regno].range = 0; @@ -1206,13 +1218,14 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn char tn_buf[48]; tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); - verbose("variable stack access var_off=%s off=%d size=%d", + verbose(env, "variable stack access var_off=%s off=%d size=%d", tn_buf, off, size); return -EACCES; } off += reg->var_off.value; if (off >= 0 || off < -MAX_BPF_STACK) { - verbose("invalid stack off=%d size=%d\n", off, size); + verbose(env, "invalid stack off=%d size=%d\n", off, + size); return -EACCES; } @@ -1223,29 +1236,32 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn if (!env->allow_ptr_leaks && state->stack_slot_type[MAX_BPF_STACK + off] == STACK_SPILL && size != BPF_REG_SIZE) { - verbose("attempt to corrupt spilled pointer on stack\n"); + verbose(env, "attempt to corrupt spilled pointer on stack\n"); return -EACCES; } - err = check_stack_write(state, off, size, value_regno); + err = check_stack_write(env, state, off, size, + value_regno); } else { - err = check_stack_read(state, off, size, value_regno); + err = check_stack_read(env, state, off, size, + value_regno); } } else if (reg_is_pkt_pointer(reg)) { if (t == BPF_WRITE && !may_access_direct_pkt_data(env, NULL, t)) { - verbose("cannot write into packet\n"); + verbose(env, "cannot write into packet\n"); return -EACCES; } if (t == BPF_WRITE && value_regno >= 0 && is_pointer_value(env, value_regno)) { - verbose("R%d leaks addr into packet\n", value_regno); + verbose(env, "R%d leaks addr into packet\n", + value_regno); return -EACCES; } err = check_packet_access(env, regno, off, size); if (!err && t == BPF_READ && value_regno >= 0) - mark_reg_unknown(state->regs, value_regno); + mark_reg_unknown(env, state->regs, value_regno); } else { - verbose("R%d invalid mem access '%s'\n", - regno, reg_type_str[reg->type]); + verbose(env, "R%d invalid mem access '%s'\n", regno, + reg_type_str[reg->type]); return -EACCES; } @@ -1265,7 +1281,7 @@ static int check_xadd(struct bpf_verifier_env *env, int insn_idx, struct bpf_ins if ((BPF_SIZE(insn->code) != BPF_W && BPF_SIZE(insn->code) != BPF_DW) || insn->imm != 0) { - verbose("BPF_XADD uses reserved fields\n"); + verbose(env, "BPF_XADD uses reserved fields\n"); return -EINVAL; } @@ -1280,7 +1296,7 @@ static int check_xadd(struct bpf_verifier_env *env, int insn_idx, struct bpf_ins return err; if (is_pointer_value(env, insn->src_reg)) { - verbose("R%d leaks addr into mem\n", insn->src_reg); + verbose(env, "R%d leaks addr into mem\n", insn->src_reg); return -EACCES; } @@ -1321,7 +1337,7 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno, register_is_null(regs[regno])) return 0; - verbose("R%d type=%s expected=%s\n", regno, + verbose(env, "R%d type=%s expected=%s\n", regno, reg_type_str[regs[regno].type], reg_type_str[PTR_TO_STACK]); return -EACCES; @@ -1332,13 +1348,13 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno, char tn_buf[48]; tnum_strn(tn_buf, sizeof(tn_buf), regs[regno].var_off); - verbose("invalid variable stack read R%d var_off=%s\n", + verbose(env, "invalid variable stack read R%d var_off=%s\n", regno, tn_buf); } off = regs[regno].off + regs[regno].var_off.value; if (off >= 0 || off < -MAX_BPF_STACK || off + access_size > 0 || access_size <= 0) { - verbose("invalid stack type R%d off=%d access_size=%d\n", + verbose(env, "invalid stack type R%d off=%d access_size=%d\n", regno, off, access_size); return -EACCES; } @@ -1354,7 +1370,7 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno, for (i = 0; i < access_size; i++) { if (state->stack_slot_type[MAX_BPF_STACK + off + i] != STACK_MISC) { - verbose("invalid indirect read from stack off %d+%d size %d\n", + verbose(env, "invalid indirect read from stack off %d+%d size %d\n", off, i, access_size); return -EACCES; } @@ -1397,7 +1413,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, if (arg_type == ARG_ANYTHING) { if (is_pointer_value(env, regno)) { - verbose("R%d leaks addr into helper function\n", regno); + verbose(env, "R%d leaks addr into helper function\n", + regno); return -EACCES; } return 0; @@ -1405,7 +1422,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, if (type_is_pkt_pointer(type) && !may_access_direct_pkt_data(env, meta, BPF_READ)) { - verbose("helper access to the packet is not allowed\n"); + verbose(env, "helper access to the packet is not allowed\n"); return -EACCES; } @@ -1443,7 +1460,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, goto err_type; meta->raw_mode = arg_type == ARG_PTR_TO_UNINIT_MEM; } else { - verbose("unsupported arg_type %d\n", arg_type); + verbose(env, "unsupported arg_type %d\n", arg_type); return -EFAULT; } @@ -1461,7 +1478,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, * we have to check map_key here. Otherwise it means * that kernel subsystem misconfigured verifier */ - verbose("invalid map_ptr to access map->key\n"); + verbose(env, "invalid map_ptr to access map->key\n"); return -EACCES; } if (type_is_pkt_pointer(type)) @@ -1477,7 +1494,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, */ if (!meta->map_ptr) { /* kernel subsystem misconfigured verifier */ - verbose("invalid map_ptr to access map->value\n"); + verbose(env, "invalid map_ptr to access map->value\n"); return -EACCES; } if (type_is_pkt_pointer(type)) @@ -1497,7 +1514,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, */ if (regno == 0) { /* kernel subsystem misconfigured verifier */ - verbose("ARG_CONST_SIZE cannot be first argument\n"); + verbose(env, + "ARG_CONST_SIZE cannot be first argument\n"); return -EACCES; } @@ -1514,7 +1532,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, meta = NULL; if (reg->smin_value < 0) { - verbose("R%d min value is negative, either use unsigned or 'var &= const'\n", + verbose(env, "R%d min value is negative, either use unsigned or 'var &= const'\n", regno); return -EACCES; } @@ -1528,7 +1546,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, } if (reg->umax_value >= BPF_MAX_VAR_SIZ) { - verbose("R%d unbounded memory access, use 'var &= const' or 'if (var < const)'\n", + verbose(env, "R%d unbounded memory access, use 'var &= const' or 'if (var < const)'\n", regno); return -EACCES; } @@ -1539,12 +1557,13 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, return err; err_type: - verbose("R%d type=%s expected=%s\n", regno, + verbose(env, "R%d type=%s expected=%s\n", regno, reg_type_str[type], reg_type_str[expected_type]); return -EACCES; } -static int check_map_func_compatibility(struct bpf_map *map, int func_id) +static int check_map_func_compatibility(struct bpf_verifier_env *env, + struct bpf_map *map, int func_id) { if (!map) return 0; @@ -1632,7 +1651,7 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id) return 0; error: - verbose("cannot pass map_type %d into func %s#%d\n", + verbose(env, "cannot pass map_type %d into func %s#%d\n", map->map_type, func_id_name(func_id), func_id); return -EINVAL; } @@ -1666,7 +1685,7 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env) for (i = 0; i < MAX_BPF_REG; i++) if (reg_is_pkt_pointer_any(®s[i])) - mark_reg_unknown(regs, i); + mark_reg_unknown(env, regs, i); for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { if (state->stack_slot_type[i] != STACK_SPILL) @@ -1688,7 +1707,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) /* find function prototype */ if (func_id < 0 || func_id >= __BPF_FUNC_MAX_ID) { - verbose("invalid func %s#%d\n", func_id_name(func_id), func_id); + verbose(env, "invalid func %s#%d\n", func_id_name(func_id), + func_id); return -EINVAL; } @@ -1696,13 +1716,14 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) fn = env->prog->aux->ops->get_func_proto(func_id); if (!fn) { - verbose("unknown func %s#%d\n", func_id_name(func_id), func_id); + verbose(env, "unknown func %s#%d\n", func_id_name(func_id), + func_id); return -EINVAL; } /* eBPF programs must be GPL compatible to use GPL-ed functions */ if (!env->prog->gpl_compatible && fn->gpl_only) { - verbose("cannot call GPL only function from proprietary program\n"); + verbose(env, "cannot call GPL only function from proprietary program\n"); return -EINVAL; } @@ -1716,7 +1737,7 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) */ err = check_raw_mode(fn); if (err) { - verbose("kernel subsystem misconfigured func %s#%d\n", + verbose(env, "kernel subsystem misconfigured func %s#%d\n", func_id_name(func_id), func_id); return err; } @@ -1749,14 +1770,14 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) /* reset caller saved regs */ for (i = 0; i < CALLER_SAVED_REGS; i++) { - mark_reg_not_init(regs, caller_saved[i]); + mark_reg_not_init(env, regs, caller_saved[i]); check_reg_arg(env, caller_saved[i], DST_OP_NO_MARK); } /* update return register (already marked as written above) */ if (fn->ret_type == RET_INTEGER) { /* sets type to SCALAR_VALUE */ - mark_reg_unknown(regs, BPF_REG_0); + mark_reg_unknown(env, regs, BPF_REG_0); } else if (fn->ret_type == RET_VOID) { regs[BPF_REG_0].type = NOT_INIT; } else if (fn->ret_type == RET_PTR_TO_MAP_VALUE_OR_NULL) { @@ -1764,14 +1785,15 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) regs[BPF_REG_0].type = PTR_TO_MAP_VALUE_OR_NULL; /* There is no offset yet applied, variable or fixed */ - mark_reg_known_zero(regs, BPF_REG_0); + mark_reg_known_zero(env, regs, BPF_REG_0); regs[BPF_REG_0].off = 0; /* remember map_ptr, so that check_map_access() * can check 'value_size' boundary of memory access * to map element returned from bpf_map_lookup_elem() */ if (meta.map_ptr == NULL) { - verbose("kernel subsystem misconfigured verifier\n"); + verbose(env, + "kernel subsystem misconfigured verifier\n"); return -EINVAL; } regs[BPF_REG_0].map_ptr = meta.map_ptr; @@ -1782,12 +1804,12 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) else if (insn_aux->map_ptr != meta.map_ptr) insn_aux->map_ptr = BPF_MAP_PTR_POISON; } else { - verbose("unknown return type %d of func %s#%d\n", + verbose(env, "unknown return type %d of func %s#%d\n", fn->ret_type, func_id_name(func_id), func_id); return -EINVAL; } - err = check_map_func_compatibility(meta.map_ptr, func_id); + err = check_map_func_compatibility(env, meta.map_ptr, func_id); if (err) return err; @@ -1846,39 +1868,42 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, dst_reg = ®s[dst]; if (WARN_ON_ONCE(known && (smin_val != smax_val))) { - print_verifier_state(&env->cur_state); - verbose("verifier internal error: known but bad sbounds\n"); + print_verifier_state(env, &env->cur_state); + verbose(env, + "verifier internal error: known but bad sbounds\n"); return -EINVAL; } if (WARN_ON_ONCE(known && (umin_val != umax_val))) { - print_verifier_state(&env->cur_state); - verbose("verifier internal error: known but bad ubounds\n"); + print_verifier_state(env, &env->cur_state); + verbose(env, + "verifier internal error: known but bad ubounds\n"); return -EINVAL; } if (BPF_CLASS(insn->code) != BPF_ALU64) { /* 32-bit ALU ops on pointers produce (meaningless) scalars */ if (!env->allow_ptr_leaks) - verbose("R%d 32-bit pointer arithmetic prohibited\n", + verbose(env, + "R%d 32-bit pointer arithmetic prohibited\n", dst); return -EACCES; } if (ptr_reg->type == PTR_TO_MAP_VALUE_OR_NULL) { if (!env->allow_ptr_leaks) - verbose("R%d pointer arithmetic on PTR_TO_MAP_VALUE_OR_NULL prohibited, null-check it first\n", + verbose(env, "R%d pointer arithmetic on PTR_TO_MAP_VALUE_OR_NULL prohibited, null-check it first\n", dst); return -EACCES; } if (ptr_reg->type == CONST_PTR_TO_MAP) { if (!env->allow_ptr_leaks) - verbose("R%d pointer arithmetic on CONST_PTR_TO_MAP prohibited\n", + verbose(env, "R%d pointer arithmetic on CONST_PTR_TO_MAP prohibited\n", dst); return -EACCES; } if (ptr_reg->type == PTR_TO_PACKET_END) { if (!env->allow_ptr_leaks) - verbose("R%d pointer arithmetic on PTR_TO_PACKET_END prohibited\n", + verbose(env, "R%d pointer arithmetic on PTR_TO_PACKET_END prohibited\n", dst); return -EACCES; } @@ -1943,7 +1968,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, if (dst_reg == off_reg) { /* scalar -= pointer. Creates an unknown scalar */ if (!env->allow_ptr_leaks) - verbose("R%d tried to subtract pointer from scalar\n", + verbose(env, "R%d tried to subtract pointer from scalar\n", dst); return -EACCES; } @@ -1953,7 +1978,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, */ if (ptr_reg->type == PTR_TO_STACK) { if (!env->allow_ptr_leaks) - verbose("R%d subtraction from stack pointer prohibited\n", + verbose(env, "R%d subtraction from stack pointer prohibited\n", dst); return -EACCES; } @@ -2008,13 +2033,13 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, * ptr &= ~3 which would reduce min_value by 3.) */ if (!env->allow_ptr_leaks) - verbose("R%d bitwise operator %s on pointer prohibited\n", + verbose(env, "R%d bitwise operator %s on pointer prohibited\n", dst, bpf_alu_string[opcode >> 4]); return -EACCES; default: /* other operators (e.g. MUL,LSH) produce non-pointer results */ if (!env->allow_ptr_leaks) - verbose("R%d pointer arithmetic with %s operator prohibited\n", + verbose(env, "R%d pointer arithmetic with %s operator prohibited\n", dst, bpf_alu_string[opcode >> 4]); return -EACCES; } @@ -2180,7 +2205,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, /* Shifts greater than 63 are undefined. This includes * shifts by a negative number. */ - mark_reg_unknown(regs, insn->dst_reg); + mark_reg_unknown(env, regs, insn->dst_reg); break; } /* We lose all sign bit information (except what we can pick @@ -2208,7 +2233,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, /* Shifts greater than 63 are undefined. This includes * shifts by a negative number. */ - mark_reg_unknown(regs, insn->dst_reg); + mark_reg_unknown(env, regs, insn->dst_reg); break; } /* BPF_RSH is an unsigned shift, so make the appropriate casts */ @@ -2236,7 +2261,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, __update_reg_bounds(dst_reg); break; default: - mark_reg_unknown(regs, insn->dst_reg); + mark_reg_unknown(env, regs, insn->dst_reg); break; } @@ -2268,12 +2293,12 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, * an arbitrary scalar. */ if (!env->allow_ptr_leaks) { - verbose("R%d pointer %s pointer prohibited\n", + verbose(env, "R%d pointer %s pointer prohibited\n", insn->dst_reg, bpf_alu_string[opcode >> 4]); return -EACCES; } - mark_reg_unknown(regs, insn->dst_reg); + mark_reg_unknown(env, regs, insn->dst_reg); return 0; } else { /* scalar += pointer @@ -2325,13 +2350,13 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, /* Got here implies adding two SCALAR_VALUEs */ if (WARN_ON_ONCE(ptr_reg)) { - print_verifier_state(&env->cur_state); - verbose("verifier internal error: unexpected ptr_reg\n"); + print_verifier_state(env, &env->cur_state); + verbose(env, "verifier internal error: unexpected ptr_reg\n"); return -EINVAL; } if (WARN_ON(!src_reg)) { - print_verifier_state(&env->cur_state); - verbose("verifier internal error: no src_reg\n"); + print_verifier_state(env, &env->cur_state); + verbose(env, "verifier internal error: no src_reg\n"); return -EINVAL; } return adjust_scalar_min_max_vals(env, insn, dst_reg, *src_reg); @@ -2349,14 +2374,14 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) if (BPF_SRC(insn->code) != 0 || insn->src_reg != BPF_REG_0 || insn->off != 0 || insn->imm != 0) { - verbose("BPF_NEG uses reserved fields\n"); + verbose(env, "BPF_NEG uses reserved fields\n"); return -EINVAL; } } else { if (insn->src_reg != BPF_REG_0 || insn->off != 0 || (insn->imm != 16 && insn->imm != 32 && insn->imm != 64) || BPF_CLASS(insn->code) == BPF_ALU64) { - verbose("BPF_END uses reserved fields\n"); + verbose(env, "BPF_END uses reserved fields\n"); return -EINVAL; } } @@ -2367,7 +2392,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) return err; if (is_pointer_value(env, insn->dst_reg)) { - verbose("R%d pointer arithmetic prohibited\n", + verbose(env, "R%d pointer arithmetic prohibited\n", insn->dst_reg); return -EACCES; } @@ -2381,7 +2406,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) if (BPF_SRC(insn->code) == BPF_X) { if (insn->imm != 0 || insn->off != 0) { - verbose("BPF_MOV uses reserved fields\n"); + verbose(env, "BPF_MOV uses reserved fields\n"); return -EINVAL; } @@ -2391,7 +2416,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) return err; } else { if (insn->src_reg != BPF_REG_0 || insn->off != 0) { - verbose("BPF_MOV uses reserved fields\n"); + verbose(env, "BPF_MOV uses reserved fields\n"); return -EINVAL; } } @@ -2411,11 +2436,12 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) } else { /* R1 = (u32) R2 */ if (is_pointer_value(env, insn->src_reg)) { - verbose("R%d partial copy of pointer\n", + verbose(env, + "R%d partial copy of pointer\n", insn->src_reg); return -EACCES; } - mark_reg_unknown(regs, insn->dst_reg); + mark_reg_unknown(env, regs, insn->dst_reg); /* high 32 bits are known zero. */ regs[insn->dst_reg].var_off = tnum_cast( regs[insn->dst_reg].var_off, 4); @@ -2430,14 +2456,14 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) } } else if (opcode > BPF_END) { - verbose("invalid BPF_ALU opcode %x\n", opcode); + verbose(env, "invalid BPF_ALU opcode %x\n", opcode); return -EINVAL; } else { /* all other ALU ops: and, sub, xor, add, ... */ if (BPF_SRC(insn->code) == BPF_X) { if (insn->imm != 0 || insn->off != 0) { - verbose("BPF_ALU uses reserved fields\n"); + verbose(env, "BPF_ALU uses reserved fields\n"); return -EINVAL; } /* check src1 operand */ @@ -2446,7 +2472,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) return err; } else { if (insn->src_reg != BPF_REG_0 || insn->off != 0) { - verbose("BPF_ALU uses reserved fields\n"); + verbose(env, "BPF_ALU uses reserved fields\n"); return -EINVAL; } } @@ -2458,7 +2484,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) if ((opcode == BPF_MOD || opcode == BPF_DIV) && BPF_SRC(insn->code) == BPF_K && insn->imm == 0) { - verbose("div by zero\n"); + verbose(env, "div by zero\n"); return -EINVAL; } @@ -2467,7 +2493,7 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) int size = BPF_CLASS(insn->code) == BPF_ALU64 ? 64 : 32; if (insn->imm < 0 || insn->imm >= size) { - verbose("invalid shift %d\n", insn->imm); + verbose(env, "invalid shift %d\n", insn->imm); return -EINVAL; } } @@ -2820,13 +2846,13 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, int err; if (opcode > BPF_JSLE) { - verbose("invalid BPF_JMP opcode %x\n", opcode); + verbose(env, "invalid BPF_JMP opcode %x\n", opcode); return -EINVAL; } if (BPF_SRC(insn->code) == BPF_X) { if (insn->imm != 0) { - verbose("BPF_JMP uses reserved fields\n"); + verbose(env, "BPF_JMP uses reserved fields\n"); return -EINVAL; } @@ -2836,13 +2862,13 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, return err; if (is_pointer_value(env, insn->src_reg)) { - verbose("R%d pointer comparison prohibited\n", + verbose(env, "R%d pointer comparison prohibited\n", insn->src_reg); return -EACCES; } } else { if (insn->src_reg != BPF_REG_0) { - verbose("BPF_JMP uses reserved fields\n"); + verbose(env, "BPF_JMP uses reserved fields\n"); return -EINVAL; } } @@ -2954,11 +2980,12 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, find_good_pkt_pointers(this_branch, ®s[insn->src_reg], PTR_TO_PACKET_META); } else if (is_pointer_value(env, insn->dst_reg)) { - verbose("R%d pointer comparison prohibited\n", insn->dst_reg); + verbose(env, "R%d pointer comparison prohibited\n", + insn->dst_reg); return -EACCES; } - if (verifier_log.level) - print_verifier_state(this_branch); + if (env->log.level) + print_verifier_state(env, this_branch); return 0; } @@ -2977,11 +3004,11 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn) int err; if (BPF_SIZE(insn->code) != BPF_DW) { - verbose("invalid BPF_LD_IMM insn\n"); + verbose(env, "invalid BPF_LD_IMM insn\n"); return -EINVAL; } if (insn->off != 0) { - verbose("BPF_LD_IMM64 uses reserved fields\n"); + verbose(env, "BPF_LD_IMM64 uses reserved fields\n"); return -EINVAL; } @@ -3039,14 +3066,14 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) int i, err; if (!may_access_skb(env->prog->type)) { - verbose("BPF_LD_[ABS|IND] instructions not allowed for this program type\n"); + verbose(env, "BPF_LD_[ABS|IND] instructions not allowed for this program type\n"); return -EINVAL; } if (insn->dst_reg != BPF_REG_0 || insn->off != 0 || BPF_SIZE(insn->code) == BPF_DW || (mode == BPF_ABS && insn->src_reg != BPF_REG_0)) { - verbose("BPF_LD_[ABS|IND] uses reserved fields\n"); + verbose(env, "BPF_LD_[ABS|IND] uses reserved fields\n"); return -EINVAL; } @@ -3056,7 +3083,8 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) return err; if (regs[BPF_REG_6].type != PTR_TO_CTX) { - verbose("at the time of BPF_LD_ABS|IND R6 != pointer to skb\n"); + verbose(env, + "at the time of BPF_LD_ABS|IND R6 != pointer to skb\n"); return -EINVAL; } @@ -3069,7 +3097,7 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) /* reset caller saved regs to unreadable */ for (i = 0; i < CALLER_SAVED_REGS; i++) { - mark_reg_not_init(regs, caller_saved[i]); + mark_reg_not_init(env, regs, caller_saved[i]); check_reg_arg(env, caller_saved[i], DST_OP_NO_MARK); } @@ -3077,7 +3105,7 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) * the value fetched from the packet. * Already marked as written above. */ - mark_reg_unknown(regs, BPF_REG_0); + mark_reg_unknown(env, regs, BPF_REG_0); return 0; } @@ -3097,22 +3125,22 @@ static int check_return_code(struct bpf_verifier_env *env) reg = &env->cur_state.regs[BPF_REG_0]; if (reg->type != SCALAR_VALUE) { - verbose("At program exit the register R0 is not a known value (%s)\n", + verbose(env, "At program exit the register R0 is not a known value (%s)\n", reg_type_str[reg->type]); return -EINVAL; } if (!tnum_in(range, reg->var_off)) { - verbose("At program exit the register R0 "); + verbose(env, "At program exit the register R0 "); if (!tnum_is_unknown(reg->var_off)) { char tn_buf[48]; tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); - verbose("has value %s", tn_buf); + verbose(env, "has value %s", tn_buf); } else { - verbose("has unknown scalar value"); + verbose(env, "has unknown scalar value"); } - verbose(" should have been 0 or 1\n"); + verbose(env, " should have been 0 or 1\n"); return -EINVAL; } return 0; @@ -3178,7 +3206,7 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env) return 0; if (w < 0 || w >= env->prog->len) { - verbose("jump out of range from insn %d to %d\n", t, w); + verbose(env, "jump out of range from insn %d to %d\n", t, w); return -EINVAL; } @@ -3195,13 +3223,13 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env) insn_stack[cur_stack++] = w; return 1; } else if ((insn_state[w] & 0xF0) == DISCOVERED) { - verbose("back-edge from insn %d to %d\n", t, w); + verbose(env, "back-edge from insn %d to %d\n", t, w); return -EINVAL; } else if (insn_state[w] == EXPLORED) { /* forward- or cross-edge */ insn_state[t] = DISCOVERED | e; } else { - verbose("insn state internal bug\n"); + verbose(env, "insn state internal bug\n"); return -EFAULT; } return 0; @@ -3295,7 +3323,7 @@ peek_stack: mark_explored: insn_state[t] = EXPLORED; if (cur_stack-- <= 0) { - verbose("pop stack internal bug\n"); + verbose(env, "pop stack internal bug\n"); ret = -EFAULT; goto err_free; } @@ -3304,7 +3332,7 @@ mark_explored: check_state: for (i = 0; i < insn_cnt; i++) { if (insn_state[i] != EXPLORED) { - verbose("unreachable insn %d\n", i); + verbose(env, "unreachable insn %d\n", i); ret = -EINVAL; goto err_free; } @@ -3685,7 +3713,7 @@ static int do_check(struct bpf_verifier_env *env) int insn_processed = 0; bool do_print_state = false; - init_reg_state(regs); + init_reg_state(env, regs); state->parent = NULL; insn_idx = 0; for (;;) { @@ -3694,7 +3722,7 @@ static int do_check(struct bpf_verifier_env *env) int err; if (insn_idx >= insn_cnt) { - verbose("invalid insn idx %d insn_cnt %d\n", + verbose(env, "invalid insn idx %d insn_cnt %d\n", insn_idx, insn_cnt); return -EFAULT; } @@ -3703,7 +3731,8 @@ static int do_check(struct bpf_verifier_env *env) class = BPF_CLASS(insn->code); if (++insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) { - verbose("BPF program is too large. Processed %d insn\n", + verbose(env, + "BPF program is too large. Processed %d insn\n", insn_processed); return -E2BIG; } @@ -3713,12 +3742,12 @@ static int do_check(struct bpf_verifier_env *env) return err; if (err == 1) { /* found equivalent state, can prune the search */ - if (verifier_log.level) { + if (env->log.level) { if (do_print_state) - verbose("\nfrom %d to %d: safe\n", + verbose(env, "\nfrom %d to %d: safe\n", prev_insn_idx, insn_idx); else - verbose("%d: safe\n", insn_idx); + verbose(env, "%d: safe\n", insn_idx); } goto process_bpf_exit; } @@ -3726,19 +3755,18 @@ static int do_check(struct bpf_verifier_env *env) if (need_resched()) cond_resched(); - if (verifier_log.level > 1 || - (verifier_log.level && do_print_state)) { - if (verifier_log.level > 1) - verbose("%d:", insn_idx); + if (env->log.level > 1 || (env->log.level && do_print_state)) { + if (env->log.level > 1) + verbose(env, "%d:", insn_idx); else - verbose("\nfrom %d to %d:", + verbose(env, "\nfrom %d to %d:", prev_insn_idx, insn_idx); - print_verifier_state(&env->cur_state); + print_verifier_state(env, &env->cur_state); do_print_state = false; } - if (verifier_log.level) { - verbose("%d: ", insn_idx); + if (env->log.level) { + verbose(env, "%d: ", insn_idx); print_bpf_insn(env, insn); } @@ -3795,7 +3823,7 @@ static int do_check(struct bpf_verifier_env *env) * src_reg == stack|map in some other branch. * Reject it. */ - verbose("same insn cannot be used with different pointers\n"); + verbose(env, "same insn cannot be used with different pointers\n"); return -EINVAL; } @@ -3835,14 +3863,14 @@ static int do_check(struct bpf_verifier_env *env) } else if (dst_reg_type != *prev_dst_type && (dst_reg_type == PTR_TO_CTX || *prev_dst_type == PTR_TO_CTX)) { - verbose("same insn cannot be used with different pointers\n"); + verbose(env, "same insn cannot be used with different pointers\n"); return -EINVAL; } } else if (class == BPF_ST) { if (BPF_MODE(insn->code) != BPF_MEM || insn->src_reg != BPF_REG_0) { - verbose("BPF_ST uses reserved fields\n"); + verbose(env, "BPF_ST uses reserved fields\n"); return -EINVAL; } /* check src operand */ @@ -3865,7 +3893,7 @@ static int do_check(struct bpf_verifier_env *env) insn->off != 0 || insn->src_reg != BPF_REG_0 || insn->dst_reg != BPF_REG_0) { - verbose("BPF_CALL uses reserved fields\n"); + verbose(env, "BPF_CALL uses reserved fields\n"); return -EINVAL; } @@ -3878,7 +3906,7 @@ static int do_check(struct bpf_verifier_env *env) insn->imm != 0 || insn->src_reg != BPF_REG_0 || insn->dst_reg != BPF_REG_0) { - verbose("BPF_JA uses reserved fields\n"); + verbose(env, "BPF_JA uses reserved fields\n"); return -EINVAL; } @@ -3890,7 +3918,7 @@ static int do_check(struct bpf_verifier_env *env) insn->imm != 0 || insn->src_reg != BPF_REG_0 || insn->dst_reg != BPF_REG_0) { - verbose("BPF_EXIT uses reserved fields\n"); + verbose(env, "BPF_EXIT uses reserved fields\n"); return -EINVAL; } @@ -3905,7 +3933,7 @@ static int do_check(struct bpf_verifier_env *env) return err; if (is_pointer_value(env, BPF_REG_0)) { - verbose("R0 leaks addr as return value\n"); + verbose(env, "R0 leaks addr as return value\n"); return -EACCES; } @@ -3940,19 +3968,19 @@ process_bpf_exit: insn_idx++; } else { - verbose("invalid BPF_LD mode\n"); + verbose(env, "invalid BPF_LD mode\n"); return -EINVAL; } } else { - verbose("unknown insn class %d\n", class); + verbose(env, "unknown insn class %d\n", class); return -EINVAL; } insn_idx++; } - verbose("processed %d insns, stack depth %d\n", - insn_processed, env->prog->aux->stack_depth); + verbose(env, "processed %d insns, stack depth %d\n", insn_processed, + env->prog->aux->stack_depth); return 0; } @@ -3964,7 +3992,8 @@ static int check_map_prealloc(struct bpf_map *map) !(map->map_flags & BPF_F_NO_PREALLOC); } -static int check_map_prog_compatibility(struct bpf_map *map, +static int check_map_prog_compatibility(struct bpf_verifier_env *env, + struct bpf_map *map, struct bpf_prog *prog) { @@ -3975,12 +4004,12 @@ static int check_map_prog_compatibility(struct bpf_map *map, */ if (prog->type == BPF_PROG_TYPE_PERF_EVENT) { if (!check_map_prealloc(map)) { - verbose("perf_event programs can only use preallocated hash map\n"); + verbose(env, "perf_event programs can only use preallocated hash map\n"); return -EINVAL; } if (map->inner_map_meta && !check_map_prealloc(map->inner_map_meta)) { - verbose("perf_event programs can only use preallocated inner hash map\n"); + verbose(env, "perf_event programs can only use preallocated inner hash map\n"); return -EINVAL; } } @@ -4003,14 +4032,14 @@ static int replace_map_fd_with_map_ptr(struct bpf_verifier_env *env) for (i = 0; i < insn_cnt; i++, insn++) { if (BPF_CLASS(insn->code) == BPF_LDX && (BPF_MODE(insn->code) != BPF_MEM || insn->imm != 0)) { - verbose("BPF_LDX uses reserved fields\n"); + verbose(env, "BPF_LDX uses reserved fields\n"); return -EINVAL; } if (BPF_CLASS(insn->code) == BPF_STX && ((BPF_MODE(insn->code) != BPF_MEM && BPF_MODE(insn->code) != BPF_XADD) || insn->imm != 0)) { - verbose("BPF_STX uses reserved fields\n"); + verbose(env, "BPF_STX uses reserved fields\n"); return -EINVAL; } @@ -4021,7 +4050,7 @@ static int replace_map_fd_with_map_ptr(struct bpf_verifier_env *env) if (i == insn_cnt - 1 || insn[1].code != 0 || insn[1].dst_reg != 0 || insn[1].src_reg != 0 || insn[1].off != 0) { - verbose("invalid bpf_ld_imm64 insn\n"); + verbose(env, "invalid bpf_ld_imm64 insn\n"); return -EINVAL; } @@ -4030,19 +4059,20 @@ static int replace_map_fd_with_map_ptr(struct bpf_verifier_env *env) goto next_insn; if (insn->src_reg != BPF_PSEUDO_MAP_FD) { - verbose("unrecognized bpf_ld_imm64 insn\n"); + verbose(env, + "unrecognized bpf_ld_imm64 insn\n"); return -EINVAL; } f = fdget(insn->imm); map = __bpf_map_get(f); if (IS_ERR(map)) { - verbose("fd %d is not pointing to valid bpf_map\n", + verbose(env, "fd %d is not pointing to valid bpf_map\n", insn->imm); return PTR_ERR(map); } - err = check_map_prog_compatibility(map, env->prog); + err = check_map_prog_compatibility(env, map, env->prog); if (err) { fdput(f); return err; @@ -4164,7 +4194,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) cnt = ops->gen_prologue(insn_buf, env->seen_direct_write, env->prog); if (cnt >= ARRAY_SIZE(insn_buf)) { - verbose("bpf verifier is misconfigured\n"); + verbose(env, "bpf verifier is misconfigured\n"); return -EINVAL; } else if (cnt) { new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt); @@ -4212,7 +4242,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) u8 size_code; if (type == BPF_WRITE) { - verbose("bpf verifier narrow ctx access misconfigured\n"); + verbose(env, "bpf verifier narrow ctx access misconfigured\n"); return -EINVAL; } @@ -4231,7 +4261,7 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env) &target_size); if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf) || (ctx_field_size && !target_size)) { - verbose("bpf verifier is misconfigured\n"); + verbose(env, "bpf verifier is misconfigured\n"); return -EINVAL; } @@ -4313,7 +4343,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) cnt = map_ptr->ops->map_gen_lookup(map_ptr, insn_buf); if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) { - verbose("bpf verifier is misconfigured\n"); + verbose(env, "bpf verifier is misconfigured\n"); return -EINVAL; } @@ -4357,7 +4387,8 @@ patch_call_imm: * programs to call them, must be real in-kernel functions */ if (!fn->func) { - verbose("kernel subsystem misconfigured func %s#%d\n", + verbose(env, + "kernel subsystem misconfigured func %s#%d\n", func_id_name(insn->imm), insn->imm); return -EFAULT; } @@ -4391,8 +4422,8 @@ static void free_states(struct bpf_verifier_env *env) int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) { - struct bpf_verifer_log *log = &verifier_log; struct bpf_verifier_env *env; + struct bpf_verifer_log *log; int ret = -EINVAL; /* 'struct bpf_verifier_env' can be global, but since it's not small, @@ -4401,6 +4432,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) env = kzalloc(sizeof(struct bpf_verifier_env), GFP_KERNEL); if (!env) return -ENOMEM; + log = &env->log; env->insn_aux_data = vzalloc(sizeof(struct bpf_insn_aux_data) * (*prog)->len); @@ -4419,7 +4451,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) log->level = attr->log_level; log->ubuf = (char __user *) (unsigned long) attr->log_buf; log->len_total = attr->log_size; - log->len_used = 0; ret = -EINVAL; /* log attributes have to be sane */ @@ -4431,8 +4462,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) log->kbuf = vmalloc(log->len_total); if (!log->kbuf) goto err_unlock; - } else { - log->level = 0; } env->strict_alignment = !!(attr->prog_flags & BPF_F_STRICT_ALIGNMENT); @@ -4543,8 +4572,6 @@ int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, /* grab the mutex to protect few globals used by verifier */ mutex_lock(&bpf_verifier_lock); - verifier_log.level = 0; - env->strict_alignment = false; if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) env->strict_alignment = true; -- cgit v1.2.3-59-g8ed1b From d66f2b91f95b56e31772b9faa0d036cd2e53cb02 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 9 Oct 2017 10:30:14 -0700 Subject: bpf: don't rely on the verifier lock for metadata_dst allocation bpf_skb_set_tunnel_*() functions require allocation of per-cpu metadata_dst. The allocation happens upon verification of the first program using those helpers. In preparation for removing the verifier lock, use cmpxchg() to make sure we only allocate the metadata_dsts once. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/net/dst_metadata.h | 1 + net/core/dst.c | 16 ++++++++++++++++ net/core/filter.c | 16 +++++++++------- 3 files changed, 26 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/dst_metadata.h b/include/net/dst_metadata.h index 9fba2ebf6dda..87a0bb8d449f 100644 --- a/include/net/dst_metadata.h +++ b/include/net/dst_metadata.h @@ -87,6 +87,7 @@ static inline int skb_metadata_dst_cmp(const struct sk_buff *skb_a, void metadata_dst_free(struct metadata_dst *); struct metadata_dst *metadata_dst_alloc(u8 optslen, enum metadata_type type, gfp_t flags); +void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst); struct metadata_dst __percpu * metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags); diff --git a/net/core/dst.c b/net/core/dst.c index a6c47da7d0f8..8b2eafac984d 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -322,3 +322,19 @@ metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags) return md_dst; } EXPORT_SYMBOL_GPL(metadata_dst_alloc_percpu); + +void metadata_dst_free_percpu(struct metadata_dst __percpu *md_dst) +{ + int cpu; + +#ifdef CONFIG_DST_CACHE + for_each_possible_cpu(cpu) { + struct metadata_dst *one_md_dst = per_cpu_ptr(md_dst, cpu); + + if (one_md_dst->type == METADATA_IP_TUNNEL) + dst_cache_destroy(&one_md_dst->u.tun_info.dst_cache); + } +#endif + free_percpu(md_dst); +} +EXPORT_SYMBOL_GPL(metadata_dst_free_percpu); diff --git a/net/core/filter.c b/net/core/filter.c index b7e8caa1e790..140fa9f9c0f4 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -2987,14 +2988,15 @@ static const struct bpf_func_proto * bpf_get_skb_set_tunnel_proto(enum bpf_func_id which) { if (!md_dst) { - /* Race is not possible, since it's called from verifier - * that is holding verifier mutex. - */ - md_dst = metadata_dst_alloc_percpu(IP_TUNNEL_OPTS_MAX, - METADATA_IP_TUNNEL, - GFP_KERNEL); - if (!md_dst) + struct metadata_dst __percpu *tmp; + + tmp = metadata_dst_alloc_percpu(IP_TUNNEL_OPTS_MAX, + METADATA_IP_TUNNEL, + GFP_KERNEL); + if (!tmp) return NULL; + if (cmpxchg(&md_dst, NULL, tmp)) + metadata_dst_free_percpu(tmp); } switch (which) { -- cgit v1.2.3-59-g8ed1b From a2a7d5701052542cd2260e7659b12443e0a74733 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 9 Oct 2017 10:30:15 -0700 Subject: bpf: write back the verifier log buffer as it gets filled Verifier log buffer can be quite large (up to 16MB currently). As Eric Dumazet points out if we allow multiple verification requests to proceed simultaneously, malicious user may use the verifier as a way of allocating large amounts of unswappable memory to OOM the host. Switch to a strategy of allocating a smaller buffer (1024B) and writing it out into the user buffer after every print. While at it remove the old BUG_ON(). This is in preparation of the global verifier lock removal. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf_verifier.h | 4 +++- kernel/bpf/verifier.c | 41 +++++++++++++++++++---------------------- 2 files changed, 22 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 5ddb9a626a51..f00ef751c1c5 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -115,9 +115,11 @@ struct bpf_insn_aux_data { #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ +#define BPF_VERIFIER_TMP_LOG_SIZE 1024 + struct bpf_verifer_log { u32 level; - char *kbuf; + char kbuf[BPF_VERIFIER_TMP_LOG_SIZE]; char __user *ubuf; u32 len_used; u32 len_total; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 274c6582ec39..2cdbcc4f8f6b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -165,15 +165,26 @@ static __printf(2, 3) void verbose(struct bpf_verifier_env *env, const char *fmt, ...) { struct bpf_verifer_log *log = &env->log; + unsigned int n; va_list args; - if (!log->level || bpf_verifier_log_full(log)) + if (!log->level || !log->ubuf || bpf_verifier_log_full(log)) return; va_start(args, fmt); - log->len_used += vscnprintf(log->kbuf + log->len_used, - log->len_total - log->len_used, fmt, args); + n = vscnprintf(log->kbuf, BPF_VERIFIER_TMP_LOG_SIZE, fmt, args); va_end(args); + + WARN_ONCE(n >= BPF_VERIFIER_TMP_LOG_SIZE - 1, + "verifier log line truncated - local buffer too short\n"); + + n = min(log->len_total - log->len_used - 1, n); + log->kbuf[n] = '\0'; + + if (!copy_to_user(log->ubuf + log->len_used, log->kbuf, n + 1)) + log->len_used += n; + else + log->ubuf = NULL; } static bool type_is_pkt_pointer(enum bpf_reg_type type) @@ -4263,11 +4274,6 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) if (log->len_total < 128 || log->len_total > UINT_MAX >> 8 || !log->level || !log->ubuf) goto err_unlock; - - ret = -ENOMEM; - log->kbuf = vmalloc(log->len_total); - if (!log->kbuf) - goto err_unlock; } env->strict_alignment = !!(attr->prog_flags & BPF_F_STRICT_ALIGNMENT); @@ -4304,18 +4310,11 @@ skip_full_check: if (ret == 0) ret = fixup_bpf_calls(env); - if (log->level && bpf_verifier_log_full(log)) { - BUG_ON(log->len_used >= log->len_total); - /* verifier log exceeded user supplied buffer */ + if (log->level && bpf_verifier_log_full(log)) ret = -ENOSPC; - /* fall through to return what was recorded */ - } - - /* copy verifier log back to user space including trailing zero */ - if (log->level && copy_to_user(log->ubuf, log->kbuf, - log->len_used + 1) != 0) { + if (log->level && !log->ubuf) { ret = -EFAULT; - goto free_log_buf; + goto err_release_maps; } if (ret == 0 && env->used_map_cnt) { @@ -4326,7 +4325,7 @@ skip_full_check: if (!env->prog->aux->used_maps) { ret = -ENOMEM; - goto free_log_buf; + goto err_release_maps; } memcpy(env->prog->aux->used_maps, env->used_maps, @@ -4339,9 +4338,7 @@ skip_full_check: convert_pseudo_ld_imm64(env); } -free_log_buf: - if (log->level) - vfree(log->kbuf); +err_release_maps: if (!env->prog->aux->used_maps) /* if we didn't copy map pointers into bpf_prog_info, release * them now. Otherwise free_bpf_prog_info() will release them. -- cgit v1.2.3-59-g8ed1b From a542f9a04d30570d367770e34f2c5d0c1d313337 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sun, 17 Sep 2017 18:17:09 +0200 Subject: iio: st_sensors: split open-drain parameters for irq1 and irq2 Define st_sensor_int_drdy structure in st_sensor_data_ready_irq in order to contain irq line parameters of the device. Moreover separate data-ready open-drain configuration parameters for INT1 and INT2 pins in st_sensor_data_ready_irq data structure. That change will be used to properly support LIS3DHH accel sensor. Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 18 ++++++++++------ drivers/iio/common/st_sensors/st_sensors_core.c | 21 ++++++++++++++----- drivers/iio/pressure/st_pressure_core.c | 14 +++++++------ include/linux/iio/common/st_sensors.h | 28 ++++++++++++++----------- 4 files changed, 52 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 731ec3a4c82b..c88db25a1aff 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -236,15 +236,17 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .int1 = { .addr = 0x22, .mask = 0x02, + .addr_od = 0x22, + .mask_od = 0x40, }, .int2 = { .addr = 0x22, .mask = 0x10, + .addr_od = 0x22, + .mask_od = 0x40, }, .addr_ihl = 0x22, .mask_ihl = 0x80, - .addr_od = 0x22, - .mask_od = 0x40, .stat_drdy = { .addr = ST_SENSORS_DEFAULT_STAT_ADDR, .mask = 0x07, @@ -468,15 +470,17 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .int1 = { .addr = 0x22, .mask = 0x04, + .addr_od = 0x22, + .mask_od = 0x40, }, .int2 = { .addr = 0x22, .mask = 0x20, + .addr_od = 0x22, + .mask_od = 0x40, }, .addr_ihl = 0x22, .mask_ihl = 0x80, - .addr_od = 0x22, - .mask_od = 0x40, .stat_drdy = { .addr = ST_SENSORS_DEFAULT_STAT_ADDR, .mask = 0x07, @@ -750,15 +754,17 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .int1 = { .addr = 0x23, .mask = 0x01, + .addr_od = 0x22, + .mask_od = 0x20, }, .int2 = { .addr = 0x24, .mask = 0x01, + .addr_od = 0x22, + .mask_od = 0x20, }, .addr_ihl = 0x22, .mask_ihl = 0x08, - .addr_od = 0x22, - .mask_od = 0x20, .stat_drdy = { .addr = ST_SENSORS_DEFAULT_STAT_ADDR, .mask = 0x01, diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 6657160b5a73..40dfdfc0906b 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -319,7 +319,8 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, } if (pdata->open_drain) { - if (!sdata->sensor_settings->drdy_irq.addr_od) + if (!sdata->sensor_settings->drdy_irq.int1.addr_od && + !sdata->sensor_settings->drdy_irq.int2.addr_od) dev_err(&indio_dev->dev, "open drain requested but unsupported.\n"); else @@ -446,11 +447,21 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev, } if (sdata->int_pin_open_drain) { + u8 addr, mask; + + if (sdata->drdy_int_pin == 1) { + addr = sdata->sensor_settings->drdy_irq.int1.addr_od; + mask = sdata->sensor_settings->drdy_irq.int1.mask_od; + } else { + addr = sdata->sensor_settings->drdy_irq.int2.addr_od; + mask = sdata->sensor_settings->drdy_irq.int2.mask_od; + } + dev_info(&indio_dev->dev, - "set interrupt line to open drain mode\n"); - err = st_sensors_write_data_with_mask(indio_dev, - sdata->sensor_settings->drdy_irq.addr_od, - sdata->sensor_settings->drdy_irq.mask_od, 1); + "set interrupt line to open drain mode on pin %d\n", + sdata->drdy_int_pin); + err = st_sensors_write_data_with_mask(indio_dev, addr, + mask, 1); if (err < 0) return err; } diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 15ad6054d9f6..349e5c713c03 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -283,15 +283,17 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .int1 = { .addr = 0x22, .mask = 0x04, + .addr_od = 0x22, + .mask_od = 0x40, }, .int2 = { .addr = 0x22, .mask = 0x20, + .addr_od = 0x22, + .mask_od = 0x40, }, .addr_ihl = 0x22, .mask_ihl = 0x80, - .addr_od = 0x22, - .mask_od = 0x40, .stat_drdy = { .addr = ST_SENSORS_DEFAULT_STAT_ADDR, .mask = 0x03, @@ -404,11 +406,11 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .int1 = { .addr = 0x23, .mask = 0x01, + .addr_od = 0x22, + .mask_od = 0x40, }, .addr_ihl = 0x22, .mask_ihl = 0x80, - .addr_od = 0x22, - .mask_od = 0x40, .stat_drdy = { .addr = ST_SENSORS_DEFAULT_STAT_ADDR, .mask = 0x03, @@ -473,11 +475,11 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .int1 = { .addr = 0x12, .mask = 0x04, + .addr_od = 0x12, + .mask_od = 0x40, }, .addr_ihl = 0x12, .mask_ihl = 0x80, - .addr_od = 0x12, - .mask_od = 0x40, .stat_drdy = { .addr = ST_SENSORS_DEFAULT_STAT_ADDR, .mask = 0x03, diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index e6c646d5d6d4..f9bd6e8ab138 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -130,32 +130,36 @@ struct st_sensor_das { u8 mask; }; +/** + * struct st_sensor_int_drdy - ST sensor device drdy line parameters + * @addr: address of INT drdy register. + * @mask: mask to enable drdy line. + * @addr_od: address to enable/disable Open Drain on the INT line. + * @mask_od: mask to enable/disable Open Drain on the INT line. + */ +struct st_sensor_int_drdy { + u8 addr; + u8 mask; + u8 addr_od; + u8 mask_od; +}; + /** * struct st_sensor_data_ready_irq - ST sensor device data-ready interrupt * struct int1 - data-ready configuration register for INT1 pin. * struct int2 - data-ready configuration register for INT2 pin. * @addr_ihl: address to enable/disable active low on the INT lines. * @mask_ihl: mask to enable/disable active low on the INT lines. - * @addr_od: address to enable/disable Open Drain on the INT lines. - * @mask_od: mask to enable/disable Open Drain on the INT lines. * struct stat_drdy - status register of DRDY (data ready) interrupt. * struct ig1 - represents the Interrupt Generator 1 of sensors. * @en_addr: address of the enable ig1 register. * @en_mask: mask to write the on/off value for enable. */ struct st_sensor_data_ready_irq { - struct { - u8 addr; - u8 mask; - } int1; - struct { - u8 addr; - u8 mask; - } int2; + struct st_sensor_int_drdy int1; + struct st_sensor_int_drdy int2; u8 addr_ihl; u8 mask_ihl; - u8 addr_od; - u8 mask_od; struct { u8 addr; u8 mask; -- cgit v1.2.3-59-g8ed1b From eca8b53a6769e60d6d8240d71202d73b0af81901 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Fri, 6 Oct 2017 17:55:59 -0700 Subject: blk-stat: delete useless code Fix two issues: - the per-cpu stat flush is unnecessary, nobody uses per-cpu stat except sum it to global stat. We can do the calculation there. The flush just wastes cpu time. - some fields are signed int/s64. I don't see the point. Reviewed-by: Omar Sandoval Signed-off-by: Shaohua Li Signed-off-by: Jens Axboe --- block/blk-stat.c | 45 +++++++-------------------------------------- include/linux/blk_types.h | 5 ++--- 2 files changed, 9 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/block/blk-stat.c b/block/blk-stat.c index c52356d90fe3..3a2f3c96f367 100644 --- a/block/blk-stat.c +++ b/block/blk-stat.c @@ -11,8 +11,6 @@ #include "blk-mq.h" #include "blk.h" -#define BLK_RQ_STAT_BATCH 64 - struct blk_queue_stats { struct list_head callbacks; spinlock_t lock; @@ -23,45 +21,21 @@ static void blk_stat_init(struct blk_rq_stat *stat) { stat->min = -1ULL; stat->max = stat->nr_samples = stat->mean = 0; - stat->batch = stat->nr_batch = 0; -} - -static void blk_stat_flush_batch(struct blk_rq_stat *stat) -{ - const s32 nr_batch = READ_ONCE(stat->nr_batch); - const s32 nr_samples = READ_ONCE(stat->nr_samples); - - if (!nr_batch) - return; - if (!nr_samples) - stat->mean = div64_s64(stat->batch, nr_batch); - else { - stat->mean = div64_s64((stat->mean * nr_samples) + - stat->batch, - nr_batch + nr_samples); - } - - stat->nr_samples += nr_batch; - stat->nr_batch = stat->batch = 0; + stat->batch = 0; } +/* src is a per-cpu stat, mean isn't initialized */ static void blk_stat_sum(struct blk_rq_stat *dst, struct blk_rq_stat *src) { - blk_stat_flush_batch(src); - if (!src->nr_samples) return; dst->min = min(dst->min, src->min); dst->max = max(dst->max, src->max); - if (!dst->nr_samples) - dst->mean = src->mean; - else { - dst->mean = div64_s64((src->mean * src->nr_samples) + - (dst->mean * dst->nr_samples), - dst->nr_samples + src->nr_samples); - } + dst->mean = div_u64(src->batch + dst->mean * dst->nr_samples, + dst->nr_samples + src->nr_samples); + dst->nr_samples += src->nr_samples; } @@ -69,13 +43,8 @@ static void __blk_stat_add(struct blk_rq_stat *stat, u64 value) { stat->min = min(stat->min, value); stat->max = max(stat->max, value); - - if (stat->batch + value < stat->batch || - stat->nr_batch + 1 == BLK_RQ_STAT_BATCH) - blk_stat_flush_batch(stat); - stat->batch += value; - stat->nr_batch++; + stat->nr_samples++; } void blk_stat_add(struct request *rq) @@ -84,7 +53,7 @@ void blk_stat_add(struct request *rq) struct blk_stat_callback *cb; struct blk_rq_stat *stat; int bucket; - s64 now, value; + u64 now, value; now = __blk_stat_time(ktime_to_ns(ktime_get())); if (now < blk_stat_time(&rq->issue_stat)) diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index a2d2aa709cef..3385c89f402e 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -329,11 +329,10 @@ static inline bool blk_qc_t_is_internal(blk_qc_t cookie) } struct blk_rq_stat { - s64 mean; + u64 mean; u64 min; u64 max; - s32 nr_samples; - s32 nr_batch; + u32 nr_samples; u64 batch; }; -- cgit v1.2.3-59-g8ed1b From d59158162e032917a428704160a2063a02405ec6 Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Tue, 10 Oct 2017 15:51:37 -0700 Subject: tracing: Add support for preempt and irq enable/disable events Preempt and irq trace events can be used for tracing the start and end of an atomic section which can be used by a trace viewer like systrace to graphically view the start and end of an atomic section and correlate them with latencies and scheduling issues. This also serves as a prelude to using synthetic events or probes to rewrite the preempt and irqsoff tracers, along with numerous benefits of using trace events features for these events. Link: http://lkml.kernel.org/r/20171006005432.14244-3-joelaf@google.com Link: http://lkml.kernel.org/r/20171010225137.17370-1-joelaf@google.com Cc: Peter Zilstra Cc: kernel-team@android.com Signed-off-by: Joel Fernandes Signed-off-by: Steven Rostedt (VMware) --- include/linux/ftrace.h | 3 +- include/trace/events/preemptirq.h | 70 +++++++++++++++++++++++++++++++++++++++ kernel/trace/Kconfig | 11 ++++++ kernel/trace/Makefile | 1 + kernel/trace/trace_irqsoff.c | 35 +++++++++++++++++++- 5 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 include/trace/events/preemptirq.h (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 346f8294e40a..1f8545caa691 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -769,7 +769,8 @@ static inline unsigned long get_lock_parent_ip(void) static inline void time_hardirqs_off(unsigned long a0, unsigned long a1) { } #endif -#ifdef CONFIG_PREEMPT_TRACER +#if defined(CONFIG_PREEMPT_TRACER) || \ + (defined(CONFIG_DEBUG_PREEMPT) && defined(CONFIG_PREEMPTIRQ_EVENTS)) extern void trace_preempt_on(unsigned long a0, unsigned long a1); extern void trace_preempt_off(unsigned long a0, unsigned long a1); #else diff --git a/include/trace/events/preemptirq.h b/include/trace/events/preemptirq.h new file mode 100644 index 000000000000..f5024c560d8f --- /dev/null +++ b/include/trace/events/preemptirq.h @@ -0,0 +1,70 @@ +#ifdef CONFIG_PREEMPTIRQ_EVENTS + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM preemptirq + +#if !defined(_TRACE_PREEMPTIRQ_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_PREEMPTIRQ_H + +#include +#include +#include +#include + +DECLARE_EVENT_CLASS(preemptirq_template, + + TP_PROTO(unsigned long ip, unsigned long parent_ip), + + TP_ARGS(ip, parent_ip), + + TP_STRUCT__entry( + __field(u32, caller_offs) + __field(u32, parent_offs) + ), + + TP_fast_assign( + __entry->caller_offs = (u32)(ip - (unsigned long)_stext); + __entry->parent_offs = (u32)(parent_ip - (unsigned long)_stext); + ), + + TP_printk("caller=%pF parent=%pF", + (void *)((unsigned long)(_stext) + __entry->caller_offs), + (void *)((unsigned long)(_stext) + __entry->parent_offs)) +); + +#ifndef CONFIG_PROVE_LOCKING +DEFINE_EVENT(preemptirq_template, irq_disable, + TP_PROTO(unsigned long ip, unsigned long parent_ip), + TP_ARGS(ip, parent_ip)); + +DEFINE_EVENT(preemptirq_template, irq_enable, + TP_PROTO(unsigned long ip, unsigned long parent_ip), + TP_ARGS(ip, parent_ip)); +#endif + +#ifdef CONFIG_DEBUG_PREEMPT +DEFINE_EVENT(preemptirq_template, preempt_disable, + TP_PROTO(unsigned long ip, unsigned long parent_ip), + TP_ARGS(ip, parent_ip)); + +DEFINE_EVENT(preemptirq_template, preempt_enable, + TP_PROTO(unsigned long ip, unsigned long parent_ip), + TP_ARGS(ip, parent_ip)); +#endif + +#endif /* _TRACE_PREEMPTIRQ_H */ + +#include + +#else /* !CONFIG_PREEMPTIRQ_EVENTS */ + +#define trace_irq_enable(...) +#define trace_irq_disable(...) +#define trace_preempt_enable(...) +#define trace_preempt_disable(...) +#define trace_irq_enable_rcuidle(...) +#define trace_irq_disable_rcuidle(...) +#define trace_preempt_enable_rcuidle(...) +#define trace_preempt_disable_rcuidle(...) + +#endif diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 434c840e2d82..b8395a020821 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -160,6 +160,17 @@ config FUNCTION_GRAPH_TRACER address on the current task structure into a stack of calls. +config PREEMPTIRQ_EVENTS + bool "Enable trace events for preempt and irq disable/enable" + select TRACE_IRQFLAGS + depends on DEBUG_PREEMPT || !PROVE_LOCKING + default n + help + Enable tracing of disable and enable events for preemption and irqs. + For tracing preempt disable/enable events, DEBUG_PREEMPT must be + enabled. For tracing irq disable/enable events, PROVE_LOCKING must + be disabled. + config IRQSOFF_TRACER bool "Interrupts-off Latency Tracer" default n diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index 90f2701d92a7..9f62eee61f14 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_TRACING) += trace_printk.o obj-$(CONFIG_TRACING_MAP) += tracing_map.o obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o +obj-$(CONFIG_PREEMPTIRQ_EVENTS) += trace_irqsoff.o obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c index 0e3033c00474..03ecb4465ee4 100644 --- a/kernel/trace/trace_irqsoff.c +++ b/kernel/trace/trace_irqsoff.c @@ -16,6 +16,9 @@ #include "trace.h" +#define CREATE_TRACE_POINTS +#include + #if defined(CONFIG_IRQSOFF_TRACER) || defined(CONFIG_PREEMPT_TRACER) static struct trace_array *irqsoff_trace __read_mostly; static int tracer_enabled __read_mostly; @@ -777,26 +780,53 @@ static inline void tracer_preempt_off(unsigned long a0, unsigned long a1) { } #endif #if defined(CONFIG_TRACE_IRQFLAGS) && !defined(CONFIG_PROVE_LOCKING) +/* Per-cpu variable to prevent redundant calls when IRQs already off */ +static DEFINE_PER_CPU(int, tracing_irq_cpu); + void trace_hardirqs_on(void) { + if (!this_cpu_read(tracing_irq_cpu)) + return; + + trace_irq_enable_rcuidle(CALLER_ADDR0, CALLER_ADDR1); tracer_hardirqs_on(); + + this_cpu_write(tracing_irq_cpu, 0); } EXPORT_SYMBOL(trace_hardirqs_on); void trace_hardirqs_off(void) { + if (this_cpu_read(tracing_irq_cpu)) + return; + + this_cpu_write(tracing_irq_cpu, 1); + + trace_irq_disable_rcuidle(CALLER_ADDR0, CALLER_ADDR1); tracer_hardirqs_off(); } EXPORT_SYMBOL(trace_hardirqs_off); __visible void trace_hardirqs_on_caller(unsigned long caller_addr) { + if (!this_cpu_read(tracing_irq_cpu)) + return; + + trace_irq_enable_rcuidle(CALLER_ADDR0, caller_addr); tracer_hardirqs_on_caller(caller_addr); + + this_cpu_write(tracing_irq_cpu, 0); } EXPORT_SYMBOL(trace_hardirqs_on_caller); __visible void trace_hardirqs_off_caller(unsigned long caller_addr) { + if (this_cpu_read(tracing_irq_cpu)) + return; + + this_cpu_write(tracing_irq_cpu, 1); + + trace_irq_disable_rcuidle(CALLER_ADDR0, caller_addr); tracer_hardirqs_off_caller(caller_addr); } EXPORT_SYMBOL(trace_hardirqs_off_caller); @@ -818,14 +848,17 @@ inline void print_irqtrace_events(struct task_struct *curr) } #endif -#ifdef CONFIG_PREEMPT_TRACER +#if defined(CONFIG_PREEMPT_TRACER) || \ + (defined(CONFIG_DEBUG_PREEMPT) && defined(CONFIG_PREEMPTIRQ_EVENTS)) void trace_preempt_on(unsigned long a0, unsigned long a1) { + trace_preempt_enable_rcuidle(a0, a1); tracer_preempt_on(a0, a1); } void trace_preempt_off(unsigned long a0, unsigned long a1) { + trace_preempt_disable_rcuidle(a0, a1); tracer_preempt_off(a0, a1); } #endif -- cgit v1.2.3-59-g8ed1b From b8226962b1c49c784aeddb9d2fafbf53dfdc2190 Mon Sep 17 00:00:00 2001 From: Eric Garver Date: Tue, 10 Oct 2017 16:54:44 -0400 Subject: openvswitch: add ct_clear action This adds a ct_clear action for clearing conntrack state. ct_clear is currently implemented in OVS userspace, but is not backed by an action in the kernel datapath. This is useful for flows that may modify a packet tuple after a ct lookup has already occurred. Signed-off-by: Eric Garver Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- include/uapi/linux/openvswitch.h | 2 ++ net/openvswitch/actions.c | 4 ++++ net/openvswitch/conntrack.c | 11 +++++++++++ net/openvswitch/conntrack.h | 7 +++++++ net/openvswitch/flow_netlink.c | 5 +++++ 5 files changed, 29 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index efdbfbfd3ee2..0cd6f8833147 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -807,6 +807,7 @@ struct ovs_action_push_eth { * packet. * @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the * packet. + * @OVS_ACTION_ATTR_CT_CLEAR: Clear conntrack state from the packet. * * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment @@ -836,6 +837,7 @@ enum ovs_action_attr { OVS_ACTION_ATTR_TRUNC, /* u32 struct ovs_action_trunc. */ OVS_ACTION_ATTR_PUSH_ETH, /* struct ovs_action_push_eth. */ OVS_ACTION_ATTR_POP_ETH, /* No argument. */ + OVS_ACTION_ATTR_CT_CLEAR, /* No argument. */ __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index a54a556fcdb5..a551232daf61 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -1203,6 +1203,10 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, return err == -EINPROGRESS ? 0 : err; break; + case OVS_ACTION_ATTR_CT_CLEAR: + err = ovs_ct_clear(skb, key); + break; + case OVS_ACTION_ATTR_PUSH_ETH: err = push_eth(skb, key, nla_data(a)); break; diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index d558e882ca0c..fe861e2f0deb 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -1129,6 +1129,17 @@ int ovs_ct_execute(struct net *net, struct sk_buff *skb, return err; } +int ovs_ct_clear(struct sk_buff *skb, struct sw_flow_key *key) +{ + if (skb_nfct(skb)) { + nf_conntrack_put(skb_nfct(skb)); + nf_ct_set(skb, NULL, IP_CT_UNTRACKED); + ovs_ct_fill_key(skb, key); + } + + return 0; +} + static int ovs_ct_add_helper(struct ovs_conntrack_info *info, const char *name, const struct sw_flow_key *key, bool log) { diff --git a/net/openvswitch/conntrack.h b/net/openvswitch/conntrack.h index bc7efd1867ab..399dfdd2c4f9 100644 --- a/net/openvswitch/conntrack.h +++ b/net/openvswitch/conntrack.h @@ -30,6 +30,7 @@ int ovs_ct_action_to_attr(const struct ovs_conntrack_info *, struct sk_buff *); int ovs_ct_execute(struct net *, struct sk_buff *, struct sw_flow_key *, const struct ovs_conntrack_info *); +int ovs_ct_clear(struct sk_buff *skb, struct sw_flow_key *key); void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key); int ovs_ct_put_key(const struct sw_flow_key *swkey, @@ -73,6 +74,12 @@ static inline int ovs_ct_execute(struct net *net, struct sk_buff *skb, return -ENOTSUPP; } +static inline int ovs_ct_clear(struct sk_buff *skb, + struct sw_flow_key *key) +{ + return -ENOTSUPP; +} + static inline void ovs_ct_fill_key(const struct sk_buff *skb, struct sw_flow_key *key) { diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index fc0ca9a89b8e..dc0d79092e74 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -76,6 +76,7 @@ static bool actions_may_change_flow(const struct nlattr *actions) break; case OVS_ACTION_ATTR_CT: + case OVS_ACTION_ATTR_CT_CLEAR: case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_POP_ETH: case OVS_ACTION_ATTR_POP_MPLS: @@ -2528,6 +2529,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, [OVS_ACTION_ATTR_SAMPLE] = (u32)-1, [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash), [OVS_ACTION_ATTR_CT] = (u32)-1, + [OVS_ACTION_ATTR_CT_CLEAR] = 0, [OVS_ACTION_ATTR_TRUNC] = sizeof(struct ovs_action_trunc), [OVS_ACTION_ATTR_PUSH_ETH] = sizeof(struct ovs_action_push_eth), [OVS_ACTION_ATTR_POP_ETH] = 0, @@ -2669,6 +2671,9 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, skip_copy = true; break; + case OVS_ACTION_ATTR_CT_CLEAR: + break; + case OVS_ACTION_ATTR_PUSH_ETH: /* Disallow pushing an Ethernet header if one * is already present */ -- cgit v1.2.3-59-g8ed1b From 604a7aeb4325b8ecb23df163c89fc12248302a4e Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 5 Oct 2017 17:26:21 +0530 Subject: PM / OPP: Rename dev_pm_opp_register_put_opp_helper() The routine is named incorrectly since the first attempt as there is nothing like a put_opp() helper. We wanted to unregister the set_opp() helper here and so it should rather be named as dev_pm_opp_unregister_set_opp_helper(). Signed-off-by: Viresh Kumar Reviewed-by: Stephen Boyd Signed-off-by: Rafael J. Wysocki --- drivers/opp/core.c | 6 +++--- include/linux/pm_opp.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 0459b1204694..80c21207e48c 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1476,13 +1476,13 @@ err: EXPORT_SYMBOL_GPL(dev_pm_opp_register_set_opp_helper); /** - * dev_pm_opp_register_put_opp_helper() - Releases resources blocked for + * dev_pm_opp_unregister_set_opp_helper() - Releases resources blocked for * set_opp helper * @opp_table: OPP table returned from dev_pm_opp_register_set_opp_helper(). * * Release resources blocked for platform specific set_opp helper. */ -void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table) +void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) { if (!opp_table->set_opp) { pr_err("%s: Doesn't have custom set_opp helper set\n", @@ -1497,7 +1497,7 @@ void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table) dev_pm_opp_put_opp_table(opp_table); } -EXPORT_SYMBOL_GPL(dev_pm_opp_register_put_opp_helper); +EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper); /** * dev_pm_opp_add() - Add an OPP table from a table definitions diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 51ec727b4824..849d21dc4ca7 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -124,7 +124,7 @@ void dev_pm_opp_put_regulators(struct opp_table *opp_table); struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name); void dev_pm_opp_put_clkname(struct opp_table *opp_table); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); -void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table); +void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); @@ -243,7 +243,7 @@ static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct device return ERR_PTR(-ENOTSUPP); } -static inline void dev_pm_opp_register_put_opp_helper(struct opp_table *opp_table) {} +static inline void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) {} static inline struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) { -- cgit v1.2.3-59-g8ed1b From 8c418b5b15747eda05d086e80fa0a767982fbf37 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 6 Oct 2017 11:53:32 +0200 Subject: fq: support filtering a given tin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add to the FQ API a way to filter a given tin, in order to remove frames that fulfil certain criteria according to a filter function. This will be used by mac80211 to remove frames belonging to an AP VLAN interface that's being removed. Signed-off-by: Johannes Berg Acked-by: Toke Høiland-Jørgensen Signed-off-by: Johannes Berg --- include/net/fq.h | 7 +++++ include/net/fq_impl.h | 72 ++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 69 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/fq.h b/include/net/fq.h index 6d8521a30c5c..ac944a686840 100644 --- a/include/net/fq.h +++ b/include/net/fq.h @@ -90,6 +90,13 @@ typedef void fq_skb_free_t(struct fq *, struct fq_flow *, struct sk_buff *); +/* Return %true to filter (drop) the frame. */ +typedef bool fq_skb_filter_t(struct fq *, + struct fq_tin *, + struct fq_flow *, + struct sk_buff *, + void *); + typedef struct fq_flow *fq_flow_get_default_t(struct fq *, struct fq_tin *, int idx, diff --git a/include/net/fq_impl.h b/include/net/fq_impl.h index 4e6131cd3f43..8b237e4afee6 100644 --- a/include/net/fq_impl.h +++ b/include/net/fq_impl.h @@ -12,24 +12,22 @@ /* functions that are embedded into includer */ -static struct sk_buff *fq_flow_dequeue(struct fq *fq, - struct fq_flow *flow) +static void fq_adjust_removal(struct fq *fq, + struct fq_flow *flow, + struct sk_buff *skb) { struct fq_tin *tin = flow->tin; - struct fq_flow *i; - struct sk_buff *skb; - - lockdep_assert_held(&fq->lock); - - skb = __skb_dequeue(&flow->queue); - if (!skb) - return NULL; tin->backlog_bytes -= skb->len; tin->backlog_packets--; flow->backlog -= skb->len; fq->backlog--; fq->memory_usage -= skb->truesize; +} + +static void fq_rejigger_backlog(struct fq *fq, struct fq_flow *flow) +{ + struct fq_flow *i; if (flow->backlog == 0) { list_del_init(&flow->backlogchain); @@ -43,6 +41,21 @@ static struct sk_buff *fq_flow_dequeue(struct fq *fq, list_move_tail(&flow->backlogchain, &i->backlogchain); } +} + +static struct sk_buff *fq_flow_dequeue(struct fq *fq, + struct fq_flow *flow) +{ + struct sk_buff *skb; + + lockdep_assert_held(&fq->lock); + + skb = __skb_dequeue(&flow->queue); + if (!skb) + return NULL; + + fq_adjust_removal(fq, flow, skb); + fq_rejigger_backlog(fq, flow); return skb; } @@ -188,6 +201,45 @@ static void fq_tin_enqueue(struct fq *fq, } } +static void fq_flow_filter(struct fq *fq, + struct fq_flow *flow, + fq_skb_filter_t filter_func, + void *filter_data, + fq_skb_free_t free_func) +{ + struct fq_tin *tin = flow->tin; + struct sk_buff *skb, *tmp; + + lockdep_assert_held(&fq->lock); + + skb_queue_walk_safe(&flow->queue, skb, tmp) { + if (!filter_func(fq, tin, flow, skb, filter_data)) + continue; + + __skb_unlink(skb, &flow->queue); + fq_adjust_removal(fq, flow, skb); + free_func(fq, tin, flow, skb); + } + + fq_rejigger_backlog(fq, flow); +} + +static void fq_tin_filter(struct fq *fq, + struct fq_tin *tin, + fq_skb_filter_t filter_func, + void *filter_data, + fq_skb_free_t free_func) +{ + struct fq_flow *flow; + + lockdep_assert_held(&fq->lock); + + list_for_each_entry(flow, &tin->new_flows, flowchain) + fq_flow_filter(fq, flow, filter_func, filter_data, free_func); + list_for_each_entry(flow, &tin->old_flows, flowchain) + fq_flow_filter(fq, flow, filter_func, filter_data, free_func); +} + static void fq_flow_reset(struct fq *fq, struct fq_flow *flow, fq_skb_free_t free_func) -- cgit v1.2.3-59-g8ed1b From 1ea4ff3e9f0b8d53e680a2bb9e8e644bf03aeb4d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Sep 2017 16:07:22 +0200 Subject: cfg80211: support reloading regulatory database If the regulatory database is loaded, and then updated, it may be necessary to reload it. Add an nl80211 command to do this. Note that this just reloads the database, it doesn't re-apply the rules from it immediately. Signed-off-by: Johannes Berg --- include/uapi/linux/nl80211.h | 4 +++ net/wireless/nl80211.c | 11 ++++++ net/wireless/reg.c | 80 +++++++++++++++++++++++++++++++++----------- net/wireless/reg.h | 6 ++++ 4 files changed, 81 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 95832ce03a44..f882fe1f9709 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -990,6 +990,8 @@ * &NL80211_CMD_CONNECT or &NL80211_CMD_ROAM. If the 4 way handshake failed * &NL80211_CMD_DISCONNECT should be indicated instead. * + * @NL80211_CMD_RELOAD_REGDB: Request that the regdb firmware file is reloaded. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1194,6 +1196,8 @@ enum nl80211_commands { NL80211_CMD_PORT_AUTHORIZED, + NL80211_CMD_RELOAD_REGDB, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5129342151e6..67a03f2885a4 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -5678,6 +5678,11 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) } } +static int nl80211_reload_regdb(struct sk_buff *skb, struct genl_info *info) +{ + return reg_reload_regdb(); +} + static int nl80211_get_mesh_config(struct sk_buff *skb, struct genl_info *info) { @@ -12708,6 +12713,12 @@ static const struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_RELOAD_REGDB, + .doit = nl80211_reload_regdb, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, { .cmd = NL80211_CMD_GET_MESH_CONFIG, .doit = nl80211_get_mesh_config, diff --git a/net/wireless/reg.c b/net/wireless/reg.c index e9aeb05aaf3e..180addda52af 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -781,6 +781,8 @@ static int query_regdb(const char *alpha2) const struct fwdb_header *hdr = regdb; const struct fwdb_country *country; + ASSERT_RTNL(); + if (IS_ERR(regdb)) return PTR_ERR(regdb); @@ -796,41 +798,47 @@ static int query_regdb(const char *alpha2) static void regdb_fw_cb(const struct firmware *fw, void *context) { + int set_error = 0; + bool restore = true; void *db; if (!fw) { pr_info("failed to load regulatory.db\n"); - regdb = ERR_PTR(-ENODATA); - goto restore; - } - - if (!valid_regdb(fw->data, fw->size)) { + set_error = -ENODATA; + } else if (!valid_regdb(fw->data, fw->size)) { pr_info("loaded regulatory.db is malformed\n"); - release_firmware(fw); - regdb = ERR_PTR(-EINVAL); - goto restore; + set_error = -EINVAL; } - db = kmemdup(fw->data, fw->size, GFP_KERNEL); - release_firmware(fw); + rtnl_lock(); + if (WARN_ON(regdb && !IS_ERR(regdb))) { + /* just restore and free new db */ + } else if (set_error) { + regdb = ERR_PTR(set_error); + } else if (fw) { + db = kmemdup(fw->data, fw->size, GFP_KERNEL); + if (db) { + regdb = db; + restore = context && query_regdb(context); + } else { + restore = true; + } + } - if (!db) - goto restore; - regdb = db; + if (restore) + restore_regulatory_settings(true); - if (query_regdb(context)) - goto restore; - goto free; - restore: - rtnl_lock(); - restore_regulatory_settings(true); rtnl_unlock(); - free: + kfree(context); + + release_firmware(fw); } static int query_regdb_file(const char *alpha2) { + ASSERT_RTNL(); + if (regdb) return query_regdb(alpha2); @@ -843,6 +851,38 @@ static int query_regdb_file(const char *alpha2) (void *)alpha2, regdb_fw_cb); } +int reg_reload_regdb(void) +{ + const struct firmware *fw; + void *db; + int err; + + err = request_firmware(&fw, "regulatory.db", ®_pdev->dev); + if (err) + return err; + + if (!valid_regdb(fw->data, fw->size)) { + err = -ENODATA; + goto out; + } + + db = kmemdup(fw->data, fw->size, GFP_KERNEL); + if (!db) { + err = -ENOMEM; + goto out; + } + + rtnl_lock(); + if (!IS_ERR_OR_NULL(regdb)) + kfree(regdb); + regdb = db; + rtnl_unlock(); + + out: + release_firmware(fw); + return err; +} + static bool reg_query_database(struct regulatory_request *request) { /* query internal regulatory database (if it exists) */ diff --git a/net/wireless/reg.h b/net/wireless/reg.h index ca7fedf2e7a1..9529c522611a 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -179,4 +179,10 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy, * @wiphy2 - wiphy it's dfs_region to be checked against that of wiphy1 */ bool reg_dfs_domain_same(struct wiphy *wiphy1, struct wiphy *wiphy2); + +/** + * reg_reload_regdb - reload the regulatory.db firmware file + */ +int reg_reload_regdb(void); + #endif /* __NET_WIRELESS_REG_H */ -- cgit v1.2.3-59-g8ed1b From e25f1f7c94e16d4493398300efa03e5b6c77fcbf Mon Sep 17 00:00:00 2001 From: Maciej Purski Date: Thu, 24 Aug 2017 10:58:07 +0200 Subject: drm/bridge/sii8620: add remote control support MHL specification defines Remote Control Protocol(RCP) to send input events between MHL devices. The driver now recognizes RCP messages and reacts to them by reporting key events to input subsystem, allowing a user to control a device using TV remote control. Signed-off-by: Maciej Purski Acked-by: Sean Young Acked-by: Mauro Carvalho Chehab Signed-off-by: Andrzej Hajda Link: https://patchwork.freedesktop.org/patch/msgid/1503565087-19730-1-git-send-email-m.purski@samsung.com --- drivers/gpu/drm/bridge/Kconfig | 2 +- drivers/gpu/drm/bridge/sil-sii8620.c | 96 ++++++++++++++++++++++++++++++++++-- include/drm/bridge/mhl.h | 4 ++ 3 files changed, 96 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 9dba16fd56c3..3b99d5a06c16 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -71,7 +71,7 @@ config DRM_PARADE_PS8622 config DRM_SIL_SII8620 tristate "Silicon Image SII8620 HDMI/MHL bridge" - depends on OF + depends on OF && RC_CORE select DRM_KMS_HELPER help Silicon Image SII8620 HDMI/MHL bridge chip driver. diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 5131bfb94f06..b7eb704d0a8a 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -28,6 +28,8 @@ #include #include +#include + #include "sil-sii8620.h" #define SII8620_BURST_BUF_LEN 288 @@ -58,6 +60,7 @@ enum sii8620_mt_state { struct sii8620 { struct drm_bridge bridge; struct device *dev; + struct rc_dev *rc_dev; struct clk *clk_xtal; struct gpio_desc *gpio_reset; struct gpio_desc *gpio_int; @@ -431,6 +434,16 @@ static void sii8620_mt_rap(struct sii8620 *ctx, u8 code) sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RAP, code); } +static void sii8620_mt_rcpk(struct sii8620 *ctx, u8 code) +{ + sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPK, code); +} + +static void sii8620_mt_rcpe(struct sii8620 *ctx, u8 code) +{ + sii8620_mt_msc_msg(ctx, MHL_MSC_MSG_RCPE, code); +} + static void sii8620_mt_read_devcap_send(struct sii8620 *ctx, struct sii8620_mt_msg *msg) { @@ -1753,6 +1766,25 @@ static void sii8620_send_features(struct sii8620 *ctx) sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf)); } +static bool sii8620_rcp_consume(struct sii8620 *ctx, u8 scancode) +{ + bool pressed = !(scancode & MHL_RCP_KEY_RELEASED_MASK); + + scancode &= MHL_RCP_KEY_ID_MASK; + + if (!ctx->rc_dev) { + dev_dbg(ctx->dev, "RCP input device not initialized\n"); + return false; + } + + if (pressed) + rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0); + else + rc_keyup(ctx->rc_dev); + + return true; +} + static void sii8620_msc_mr_set_int(struct sii8620 *ctx) { u8 ints[MHL_INT_SIZE]; @@ -1804,19 +1836,25 @@ static void sii8620_msc_mt_done(struct sii8620 *ctx) static void sii8620_msc_mr_msc_msg(struct sii8620 *ctx) { - struct sii8620_mt_msg *msg = sii8620_msc_msg_first(ctx); + struct sii8620_mt_msg *msg; u8 buf[2]; - if (!msg) - return; - sii8620_read_buf(ctx, REG_MSC_MR_MSC_MSG_RCVD_1ST_DATA, buf, 2); switch (buf[0]) { case MHL_MSC_MSG_RAPK: + msg = sii8620_msc_msg_first(ctx); + if (!msg) + return; msg->ret = buf[1]; ctx->mt_state = MT_STATE_DONE; break; + case MHL_MSC_MSG_RCP: + if (!sii8620_rcp_consume(ctx, buf[1])) + sii8620_mt_rcpe(ctx, + MHL_RCPE_STATUS_INEFFECTIVE_KEY_CODE); + sii8620_mt_rcpk(ctx, buf[1]); + break; default: dev_err(ctx->dev, "%s message type %d,%d not supported", __func__, buf[0], buf[1]); @@ -2102,11 +2140,57 @@ static void sii8620_cable_in(struct sii8620 *ctx) enable_irq(to_i2c_client(ctx->dev)->irq); } +static void sii8620_init_rcp_input_dev(struct sii8620 *ctx) +{ + struct rc_dev *rc_dev; + int ret; + + rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE); + if (!rc_dev) { + dev_err(ctx->dev, "Failed to allocate RC device\n"); + ctx->error = -ENOMEM; + return; + } + + rc_dev->input_phys = "sii8620/input0"; + rc_dev->input_id.bustype = BUS_VIRTUAL; + rc_dev->map_name = RC_MAP_CEC; + rc_dev->allowed_protocols = RC_PROTO_BIT_CEC; + rc_dev->driver_name = "sii8620"; + rc_dev->device_name = "sii8620"; + + ret = rc_register_device(rc_dev); + + if (ret) { + dev_err(ctx->dev, "Failed to register RC device\n"); + ctx->error = ret; + rc_free_device(ctx->rc_dev); + return; + } + ctx->rc_dev = rc_dev; +} + static inline struct sii8620 *bridge_to_sii8620(struct drm_bridge *bridge) { return container_of(bridge, struct sii8620, bridge); } +static int sii8620_attach(struct drm_bridge *bridge) +{ + struct sii8620 *ctx = bridge_to_sii8620(bridge); + + sii8620_init_rcp_input_dev(ctx); + + return sii8620_clear_error(ctx); +} + +static void sii8620_detach(struct drm_bridge *bridge) +{ + struct sii8620 *ctx = bridge_to_sii8620(bridge); + + rc_unregister_device(ctx->rc_dev); +} + static bool sii8620_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -2151,6 +2235,8 @@ end: } static const struct drm_bridge_funcs sii8620_bridge_funcs = { + .attach = sii8620_attach, + .detach = sii8620_detach, .mode_fixup = sii8620_mode_fixup, }; @@ -2217,8 +2303,8 @@ static int sii8620_remove(struct i2c_client *client) struct sii8620 *ctx = i2c_get_clientdata(client); disable_irq(to_i2c_client(ctx->dev)->irq); - drm_bridge_remove(&ctx->bridge); sii8620_hw_off(ctx); + drm_bridge_remove(&ctx->bridge); return 0; } diff --git a/include/drm/bridge/mhl.h b/include/drm/bridge/mhl.h index fbdfc8d7f3c7..96a5e0f6ff12 100644 --- a/include/drm/bridge/mhl.h +++ b/include/drm/bridge/mhl.h @@ -262,6 +262,10 @@ enum { #define MHL_RAPK_UNSUPPORTED 0x02 /* Rcvd RAP action code not supported */ #define MHL_RAPK_BUSY 0x03 /* Responder too busy to respond */ +/* Bit masks for RCP messages */ +#define MHL_RCP_KEY_RELEASED_MASK 0x80 +#define MHL_RCP_KEY_ID_MASK 0x7F + /* * Error status codes for RCPE messages */ -- cgit v1.2.3-59-g8ed1b From c44027c89e19adafccd404bbe6f9686722ff4217 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 11 Oct 2017 06:36:13 +0000 Subject: ALSA: add snd_card_disconnect_sync() In case of user unbind ALSA driver during playing back / capturing, each driver needs to stop and remove it correctly. One note here is that we can't cancel from remove function in such case, because unbind operation doesn't check return value from remove function. So, we *must* stop and remove in this case. For this purpose, we need to sync (= wait) until the all top-level operations are canceled at remove function. For example, snd_card_free() processes the disconnection procedure at first, then waits for the completion. That's how the hot-unplug works safely. It's implemented, at least, in the top-level driver removal. Now for the lower level driver, we need a similar strategy. Notify to the toplevel for hot-unplug (disconnect in ALSA), and sync with the stop operation, then continue the rest of its own remove procedure. This patch adds snd_card_disconnect_sync(), and driver can use it from remove function. Note: the "lower level" driver here refers to a middle layer driver (e.g. ASoC components) that can be unbound freely during operation. Most of legacy ALSA helper drivers don't have such a problem because they can't be unbound. Note#2: snd_card_disconnect_sync() merely calls snd_card_disconnect() and syncs with closing all pending files. It takes only the files opened by user-space into account, and doesn't care about object refcounts. (The latter is handled by snd_card_free() completion call, BTW.) Also, the function doesn't free resources by itself. Tested-by: Kuninori Morimoto Signed-off-by: Takashi Iwai --- include/sound/core.h | 2 ++ sound/core/init.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'include') diff --git a/include/sound/core.h b/include/sound/core.h index 4104a9d1001f..5f181b875c2f 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -133,6 +133,7 @@ struct snd_card { struct device card_dev; /* cardX object for sysfs */ const struct attribute_group *dev_groups[4]; /* assigned sysfs attr */ bool registered; /* card_dev is registered? */ + wait_queue_head_t remove_sleep; #ifdef CONFIG_PM unsigned int power_state; /* power state */ @@ -240,6 +241,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid, struct snd_card **card_ret); int snd_card_disconnect(struct snd_card *card); +void snd_card_disconnect_sync(struct snd_card *card); int snd_card_free(struct snd_card *card); int snd_card_free_when_closed(struct snd_card *card); void snd_card_set_id(struct snd_card *card, const char *id); diff --git a/sound/core/init.c b/sound/core/init.c index 32ebe2f6bc59..168ae03d3a1c 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -255,6 +255,7 @@ int snd_card_new(struct device *parent, int idx, const char *xid, #ifdef CONFIG_PM init_waitqueue_head(&card->power_sleep); #endif + init_waitqueue_head(&card->remove_sleep); device_initialize(&card->card_dev); card->card_dev.parent = parent; @@ -452,6 +453,35 @@ int snd_card_disconnect(struct snd_card *card) } EXPORT_SYMBOL(snd_card_disconnect); +/** + * snd_card_disconnect_sync - disconnect card and wait until files get closed + * @card: card object to disconnect + * + * This calls snd_card_disconnect() for disconnecting all belonging components + * and waits until all pending files get closed. + * It assures that all accesses from user-space finished so that the driver + * can release its resources gracefully. + */ +void snd_card_disconnect_sync(struct snd_card *card) +{ + int err; + + err = snd_card_disconnect(card); + if (err < 0) { + dev_err(card->dev, + "snd_card_disconnect error (%d), skipping sync\n", + err); + return; + } + + spin_lock_irq(&card->files_lock); + wait_event_lock_irq(card->remove_sleep, + list_empty(&card->files_list), + card->files_lock); + spin_unlock_irq(&card->files_lock); +} +EXPORT_SYMBOL_GPL(snd_card_disconnect_sync); + static int snd_card_do_free(struct snd_card *card) { #if IS_ENABLED(CONFIG_SND_MIXER_OSS) @@ -957,6 +987,8 @@ int snd_card_file_remove(struct snd_card *card, struct file *file) break; } } + if (list_empty(&card->files_list)) + wake_up_all(&card->remove_sleep); spin_unlock(&card->files_lock); if (!found) { dev_err(card->dev, "card file remove problem (%p)\n", file); -- cgit v1.2.3-59-g8ed1b From e901b9873876ca30a09253731bd3a6b00c44b5b0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 4 Oct 2017 16:15:59 +0200 Subject: usb: core: Add a helper function to check the validity of EP type in URB This patch adds a new helper function to perform a sanity check of the given URB to see whether it contains a valid endpoint. It's a light- weight version of what usb_submit_urb() does, but without the kernel warning followed by the stack trace, just returns an error code. Especially for a driver that doesn't parse the descriptor but fills the URB with the fixed endpoint (e.g. some quirks for non-compliant devices), this kind of check is preferable at the probe phase before actually submitting the urb. Tested-by: Andrey Konovalov Acked-by: Greg Kroah-Hartman Signed-off-by: Takashi Iwai --- drivers/usb/core/urb.c | 30 ++++++++++++++++++++++++++---- include/linux/usb.h | 2 ++ 2 files changed, 28 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 47903d510955..8b800e34407b 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -187,6 +187,31 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); /*-------------------------------------------------------------------*/ +static const int pipetypes[4] = { + PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT +}; + +/** + * usb_urb_ep_type_check - sanity check of endpoint in the given urb + * @urb: urb to be checked + * + * This performs a light-weight sanity check for the endpoint in the + * given urb. It returns 0 if the urb contains a valid endpoint, otherwise + * a negative error code. + */ +int usb_urb_ep_type_check(const struct urb *urb) +{ + const struct usb_host_endpoint *ep; + + ep = usb_pipe_endpoint(urb->dev, urb->pipe); + if (!ep) + return -EINVAL; + if (usb_pipetype(urb->pipe) != pipetypes[usb_endpoint_type(&ep->desc)]) + return -EINVAL; + return 0; +} +EXPORT_SYMBOL_GPL(usb_urb_ep_type_check); + /** * usb_submit_urb - issue an asynchronous transfer request for an endpoint * @urb: pointer to the urb describing the request @@ -326,9 +351,6 @@ EXPORT_SYMBOL_GPL(usb_unanchor_urb); */ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) { - static int pipetypes[4] = { - PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT - }; int xfertype, max; struct usb_device *dev; struct usb_host_endpoint *ep; @@ -444,7 +466,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) */ /* Check that the pipe's type matches the endpoint's type */ - if (usb_pipetype(urb->pipe) != pipetypes[xfertype]) + if (usb_urb_ep_type_check(urb)) dev_WARN(&dev->dev, "BOGUS urb xfer, pipe %x != type %x\n", usb_pipetype(urb->pipe), pipetypes[xfertype]); diff --git a/include/linux/usb.h b/include/linux/usb.h index cb9fbd54386e..2b861804fffa 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1728,6 +1728,8 @@ static inline int usb_urb_dir_out(struct urb *urb) return (urb->transfer_flags & URB_DIR_MASK) == URB_DIR_OUT; } +int usb_urb_ep_type_check(const struct urb *urb); + void *usb_alloc_coherent(struct usb_device *dev, size_t size, gfp_t mem_flags, dma_addr_t *dma); void usb_free_coherent(struct usb_device *dev, size_t size, -- cgit v1.2.3-59-g8ed1b From 63705c406a8adbd6f26691148b09d466dd4d8d2f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 10 Oct 2017 18:49:22 +0200 Subject: ACPI / PM: Combine two identical device resume routines Notice that acpi_dev_runtime_resume() and acpi_dev_resume_early() are actually literally identical after some more-or-less recent changes, so rename acpi_dev_runtime_resume() to acpi_dev_resume(), use it everywhere instead of acpi_dev_resume_early() and drop the latter. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson --- drivers/acpi/acpi_lpss.c | 6 +++--- drivers/acpi/device_pm.c | 35 ++++++----------------------------- include/linux/acpi.h | 3 +-- 3 files changed, 10 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 032ae44710e5..81b6096c4177 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -693,7 +693,7 @@ static int acpi_lpss_activate(struct device *dev) struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); int ret; - ret = acpi_dev_runtime_resume(dev); + ret = acpi_dev_resume(dev); if (ret) return ret; @@ -737,7 +737,7 @@ static int acpi_lpss_resume_early(struct device *dev) struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); int ret; - ret = acpi_dev_resume_early(dev); + ret = acpi_dev_resume(dev); if (ret) return ret; @@ -872,7 +872,7 @@ static int acpi_lpss_runtime_resume(struct device *dev) if (lpss_quirks & LPSS_QUIRK_ALWAYS_POWER_ON && iosf_mbi_available()) lpss_iosf_exit_d3_state(); - ret = acpi_dev_runtime_resume(dev); + ret = acpi_dev_resume(dev); if (ret) return ret; diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index fbcc73f7a099..6eb51145dcf7 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -882,14 +882,13 @@ int acpi_dev_runtime_suspend(struct device *dev) EXPORT_SYMBOL_GPL(acpi_dev_runtime_suspend); /** - * acpi_dev_runtime_resume - Put device into the full-power state using ACPI. + * acpi_dev_resume - Put device into the full-power state using ACPI. * @dev: Device to put into the full-power state. * * Put the given device into the full-power state using the standard ACPI - * mechanism at run time. Set the power state of the device to ACPI D0 and - * disable remote wakeup. + * mechanism. Set the power state of the device to ACPI D0 and disable wakeup. */ -int acpi_dev_runtime_resume(struct device *dev) +int acpi_dev_resume(struct device *dev) { struct acpi_device *adev = ACPI_COMPANION(dev); int error; @@ -901,7 +900,7 @@ int acpi_dev_runtime_resume(struct device *dev) acpi_device_wakeup_disable(adev); return error; } -EXPORT_SYMBOL_GPL(acpi_dev_runtime_resume); +EXPORT_SYMBOL_GPL(acpi_dev_resume); /** * acpi_subsys_runtime_suspend - Suspend device using ACPI. @@ -926,7 +925,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend); */ int acpi_subsys_runtime_resume(struct device *dev) { - int ret = acpi_dev_runtime_resume(dev); + int ret = acpi_dev_resume(dev); return ret ? ret : pm_generic_runtime_resume(dev); } EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume); @@ -967,28 +966,6 @@ int acpi_dev_suspend_late(struct device *dev) } EXPORT_SYMBOL_GPL(acpi_dev_suspend_late); -/** - * acpi_dev_resume_early - Put device into the full-power state using ACPI. - * @dev: Device to put into the full-power state. - * - * Put the given device into the full-power state using the standard ACPI - * mechanism during system transition to the working state. Set the power - * state of the device to ACPI D0 and disable remote wakeup. - */ -int acpi_dev_resume_early(struct device *dev) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - int error; - - if (!adev) - return 0; - - error = acpi_dev_pm_full_power(adev); - acpi_device_wakeup_disable(adev); - return error; -} -EXPORT_SYMBOL_GPL(acpi_dev_resume_early); - /** * acpi_subsys_prepare - Prepare device for system transition to a sleep state. * @dev: Device to prepare. @@ -1057,7 +1034,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late); */ int acpi_subsys_resume_early(struct device *dev) { - int ret = acpi_dev_resume_early(dev); + int ret = acpi_dev_resume(dev); return ret ? ret : pm_generic_resume_early(dev); } EXPORT_SYMBOL_GPL(acpi_subsys_resume_early); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 3b89b4fe6812..d18c92d4ba19 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -865,7 +865,7 @@ static inline void arch_reserve_mem_area(acpi_physical_address addr, #if defined(CONFIG_ACPI) && defined(CONFIG_PM) int acpi_dev_runtime_suspend(struct device *dev); -int acpi_dev_runtime_resume(struct device *dev); +int acpi_dev_resume(struct device *dev); int acpi_subsys_runtime_suspend(struct device *dev); int acpi_subsys_runtime_resume(struct device *dev); int acpi_dev_pm_attach(struct device *dev, bool power_on); @@ -882,7 +882,6 @@ static inline int acpi_dev_pm_attach(struct device *dev, bool power_on) #if defined(CONFIG_ACPI) && defined(CONFIG_PM_SLEEP) int acpi_dev_suspend_late(struct device *dev); -int acpi_dev_resume_early(struct device *dev); int acpi_subsys_prepare(struct device *dev); void acpi_subsys_complete(struct device *dev); int acpi_subsys_suspend_late(struct device *dev); -- cgit v1.2.3-59-g8ed1b From eeb2d80d502af28e5660ff4bbe00f90ceb82c2db Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 5 Oct 2017 16:24:03 -0700 Subject: ACPI / LPIT: Add Low Power Idle Table (LPIT) support Add functionality to read LPIT table, which provides: - Sysfs interface to read residency counters via /sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us /sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us Here the count "low_power_idle_cpu_residency_us" shows the time spent by CPU package in low power state. This is read via MSR interface, which points to MSR for PKG C10. Here the count "low_power_idle_system_residency_us" show the count the system was in low power state. This is read via MMIO interface. This is mapped to SLP_S0 residency on modern Intel systems. This residency is achieved only when CPU is in PKG C10 and all functional blocks are in low power state. It is possible that none of the above counters present or anyone of the counter present or all counters present. For example: On my Kabylake system both of the above counters present. After suspend to idle these counts updated and prints: 6916179 6998564 This counter can be read by tools like turbostat to display. Or it can be used to debug, if modern systems are reaching desired low power state. - Provides an interface to read residency counter memory address This address can be used to get the base address of PMC memory mapped IO. This is utilized by intel_pmc_core driver to print more debug information. In addition, to avoid code duplication to read iomem, removed the read of iomem from acpi_os_read_memory() in osl.c and made a common function acpi_os_read_iomem(). This new function is used for reading iomem in in both osl.c and acpi_lpit.c. Link: http://www.uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- Documentation/acpi/lpit.txt | 25 +++++++ drivers/acpi/Kconfig | 5 ++ drivers/acpi/Makefile | 1 + drivers/acpi/acpi_lpit.c | 162 ++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/internal.h | 6 ++ drivers/acpi/osl.c | 42 +++++++----- drivers/acpi/scan.c | 1 + include/acpi/acpiosxf.h | 2 + include/linux/acpi.h | 9 +++ 9 files changed, 237 insertions(+), 16 deletions(-) create mode 100644 Documentation/acpi/lpit.txt create mode 100644 drivers/acpi/acpi_lpit.c (limited to 'include') diff --git a/Documentation/acpi/lpit.txt b/Documentation/acpi/lpit.txt new file mode 100644 index 000000000000..b426398d2e97 --- /dev/null +++ b/Documentation/acpi/lpit.txt @@ -0,0 +1,25 @@ +To enumerate platform Low Power Idle states, Intel platforms are using +“Low Power Idle Table” (LPIT). More details about this table can be +downloaded from: +http://www.uefi.org/sites/default/files/resources/Intel_ACPI_Low_Power_S0_Idle.pdf + +Residencies for each low power state can be read via FFH +(Function fixed hardware) or a memory mapped interface. + +On platforms supporting S0ix sleep states, there can be two types of +residencies: +- CPU PKG C10 (Read via FFH interface) +- Platform Controller Hub (PCH) SLP_S0 (Read via memory mapped interface) + +The following attributes are added dynamically to the cpuidle +sysfs attribute group: + /sys/devices/system/cpu/cpuidle/low_power_idle_cpu_residency_us + /sys/devices/system/cpu/cpuidle/low_power_idle_system_residency_us + +The "low_power_idle_cpu_residency_us" attribute shows time spent +by the CPU package in PKG C10 + +The "low_power_idle_system_residency_us" attribute shows SLP_S0 +residency, or system time spent with the SLP_S0# signal asserted. +This is the lowest possible system power state, achieved only when CPU is in +PKG C10 and all functional blocks in PCH are in a low power state. diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 1ce52f84dc23..4bfef0f78cde 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -80,6 +80,11 @@ endif config ACPI_SPCR_TABLE bool +config ACPI_LPIT + bool + depends on X86_64 + default y + config ACPI_SLEEP bool depends on SUSPEND || HIBERNATION diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 90265ab4437a..6a19bd7aba21 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -56,6 +56,7 @@ acpi-$(CONFIG_DEBUG_FS) += debugfs.o acpi-$(CONFIG_ACPI_NUMA) += numa.o acpi-$(CONFIG_ACPI_PROCFS_POWER) += cm_sbs.o acpi-y += acpi_lpat.o +acpi-$(CONFIG_ACPI_LPIT) += acpi_lpit.o acpi-$(CONFIG_ACPI_GENERIC_GSI) += irq.o acpi-$(CONFIG_ACPI_WATCHDOG) += acpi_watchdog.o diff --git a/drivers/acpi/acpi_lpit.c b/drivers/acpi/acpi_lpit.c new file mode 100644 index 000000000000..e94e478dd18b --- /dev/null +++ b/drivers/acpi/acpi_lpit.c @@ -0,0 +1,162 @@ + +/* + * acpi_lpit.c - LPIT table processing functions + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * 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. + */ + +#include +#include +#include +#include + +struct lpit_residency_info { + struct acpi_generic_address gaddr; + u64 frequency; + void __iomem *iomem_addr; +}; + +/* Storage for an memory mapped and FFH based entries */ +static struct lpit_residency_info residency_info_mem; +static struct lpit_residency_info residency_info_ffh; + +static int lpit_read_residency_counter_us(u64 *counter, bool io_mem) +{ + int err; + + if (io_mem) { + u64 count = 0; + int error; + + error = acpi_os_read_iomem(residency_info_mem.iomem_addr, &count, + residency_info_mem.gaddr.bit_width); + if (error) + return error; + + *counter = div64_u64(count * 1000000ULL, residency_info_mem.frequency); + return 0; + } + + err = rdmsrl_safe(residency_info_ffh.gaddr.address, counter); + if (!err) { + u64 mask = GENMASK_ULL(residency_info_ffh.gaddr.bit_offset + + residency_info_ffh.gaddr. bit_width - 1, + residency_info_ffh.gaddr.bit_offset); + + *counter &= mask; + *counter >>= residency_info_ffh.gaddr.bit_offset; + *counter = div64_u64(*counter * 1000000ULL, residency_info_ffh.frequency); + return 0; + } + + return -ENODATA; +} + +static ssize_t low_power_idle_system_residency_us_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u64 counter; + int ret; + + ret = lpit_read_residency_counter_us(&counter, true); + if (ret) + return ret; + + return sprintf(buf, "%llu\n", counter); +} +static DEVICE_ATTR_RO(low_power_idle_system_residency_us); + +static ssize_t low_power_idle_cpu_residency_us_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u64 counter; + int ret; + + ret = lpit_read_residency_counter_us(&counter, false); + if (ret) + return ret; + + return sprintf(buf, "%llu\n", counter); +} +static DEVICE_ATTR_RO(low_power_idle_cpu_residency_us); + +int lpit_read_residency_count_address(u64 *address) +{ + if (!residency_info_mem.gaddr.address) + return -EINVAL; + + *address = residency_info_mem.gaddr.address; + + return 0; +} + +static void lpit_update_residency(struct lpit_residency_info *info, + struct acpi_lpit_native *lpit_native) +{ + info->frequency = lpit_native->counter_frequency ? + lpit_native->counter_frequency : tsc_khz * 1000; + if (!info->frequency) + info->frequency = 1; + + info->gaddr = lpit_native->residency_counter; + if (info->gaddr.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) { + info->iomem_addr = ioremap_nocache(info->gaddr.address, + info->gaddr.bit_width / 8); + if (!info->iomem_addr) + return; + + /* Silently fail, if cpuidle attribute group is not present */ + sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj, + &dev_attr_low_power_idle_system_residency_us.attr, + "cpuidle"); + } else if (info->gaddr.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) { + /* Silently fail, if cpuidle attribute group is not present */ + sysfs_add_file_to_group(&cpu_subsys.dev_root->kobj, + &dev_attr_low_power_idle_cpu_residency_us.attr, + "cpuidle"); + } +} + +static void lpit_process(u64 begin, u64 end) +{ + while (begin + sizeof(struct acpi_lpit_native) < end) { + struct acpi_lpit_native *lpit_native = (struct acpi_lpit_native *)begin; + + if (!lpit_native->header.type && !lpit_native->header.flags) { + if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY && + !residency_info_mem.gaddr.address) { + lpit_update_residency(&residency_info_mem, lpit_native); + } else if (lpit_native->residency_counter.space_id == ACPI_ADR_SPACE_FIXED_HARDWARE && + !residency_info_ffh.gaddr.address) { + lpit_update_residency(&residency_info_ffh, lpit_native); + } + } + begin += lpit_native->header.length; + } +} + +void acpi_init_lpit(void) +{ + acpi_status status; + u64 lpit_begin; + struct acpi_table_lpit *lpit; + + status = acpi_get_table(ACPI_SIG_LPIT, 0, (struct acpi_table_header **)&lpit); + + if (ACPI_FAILURE(status)) + return; + + lpit_begin = (u64)lpit + sizeof(*lpit); + lpit_process(lpit_begin, lpit_begin + lpit->header.length); +} diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 4361c4415b4f..fc8c43e76707 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -248,4 +248,10 @@ void acpi_watchdog_init(void); static inline void acpi_watchdog_init(void) {} #endif +#ifdef CONFIG_ACPI_LPIT +void acpi_init_lpit(void); +#else +static inline void acpi_init_lpit(void) { } +#endif + #endif /* _ACPI_INTERNAL_H_ */ diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index db78d353bab1..3bb46cb24a99 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -663,6 +663,29 @@ acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width) EXPORT_SYMBOL(acpi_os_write_port); +int acpi_os_read_iomem(void __iomem *virt_addr, u64 *value, u32 width) +{ + + switch (width) { + case 8: + *(u8 *) value = readb(virt_addr); + break; + case 16: + *(u16 *) value = readw(virt_addr); + break; + case 32: + *(u32 *) value = readl(virt_addr); + break; + case 64: + *(u64 *) value = readq(virt_addr); + break; + default: + return -EINVAL; + } + + return 0; +} + acpi_status acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width) { @@ -670,6 +693,7 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width) unsigned int size = width / 8; bool unmap = false; u64 dummy; + int error; rcu_read_lock(); virt_addr = acpi_map_vaddr_lookup(phys_addr, size); @@ -684,22 +708,8 @@ acpi_os_read_memory(acpi_physical_address phys_addr, u64 *value, u32 width) if (!value) value = &dummy; - switch (width) { - case 8: - *(u8 *) value = readb(virt_addr); - break; - case 16: - *(u16 *) value = readw(virt_addr); - break; - case 32: - *(u32 *) value = readl(virt_addr); - break; - case 64: - *(u64 *) value = readq(virt_addr); - break; - default: - BUG(); - } + error = acpi_os_read_iomem(virt_addr, value, width); + BUG_ON(error); if (unmap) iounmap(virt_addr); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 602f8ff212f2..81367edc8a10 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2122,6 +2122,7 @@ int __init acpi_scan_init(void) acpi_int340x_thermal_init(); acpi_amba_init(); acpi_watchdog_init(); + acpi_init_lpit(); acpi_scan_add_handler(&generic_device_handler); diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h index c66eb8ffa454..d5c0f5153c4e 100644 --- a/include/acpi/acpiosxf.h +++ b/include/acpi/acpiosxf.h @@ -287,6 +287,8 @@ acpi_status acpi_os_write_port(acpi_io_address address, u32 value, u32 width); /* * Platform and hardware-independent physical memory interfaces */ +int acpi_os_read_iomem(void __iomem *virt_addr, u64 *value, u32 width); + #ifndef ACPI_USE_ALTERNATE_PROTOTYPE_acpi_os_read_memory acpi_status acpi_os_read_memory(acpi_physical_address address, u64 *value, u32 width); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index d18c92d4ba19..2b1738f840ab 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1248,4 +1248,13 @@ int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res) } #endif +#ifdef CONFIG_ACPI_LPIT +int lpit_read_residency_count_address(u64 *address); +#else +static inline int lpit_read_residency_count_address(u64 *address) +{ + return -EINVAL; +} +#endif + #endif /*_LINUX_ACPI_H*/ -- cgit v1.2.3-59-g8ed1b From 0e708fc602531b8355b5de6ea7c98f09129b223f Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 3 Oct 2017 09:11:07 +0200 Subject: PM / sleep: Remove pm_complete_with_resume_check() According to recent changes for ACPI, the are longer any users of pm_complete_with_resume_check(), thus let's drop it. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/generic_ops.c | 23 ----------------------- include/linux/pm.h | 1 - 2 files changed, 24 deletions(-) (limited to 'include') diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index 07c3c4a9522d..b2ed606265a8 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -9,7 +9,6 @@ #include #include #include -#include #ifdef CONFIG_PM /** @@ -298,26 +297,4 @@ void pm_generic_complete(struct device *dev) if (drv && drv->pm && drv->pm->complete) drv->pm->complete(dev); } - -/** - * pm_complete_with_resume_check - Complete a device power transition. - * @dev: Device to handle. - * - * Complete a device power transition during a system-wide power transition and - * optionally schedule a runtime resume of the device if the system resume in - * progress has been initated by the platform firmware and the device had its - * power.direct_complete flag set. - */ -void pm_complete_with_resume_check(struct device *dev) -{ - pm_generic_complete(dev); - /* - * If the device had been runtime-suspended before the system went into - * the sleep state it is going out of and it has never been resumed till - * now, resume it in case the firmware powered it up. - */ - if (dev->power.direct_complete && pm_resume_via_firmware()) - pm_request_resume(dev); -} -EXPORT_SYMBOL_GPL(pm_complete_with_resume_check); #endif /* CONFIG_PM_SLEEP */ diff --git a/include/linux/pm.h b/include/linux/pm.h index 47ded8aa8a5d..a0ceeccf2846 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -736,7 +736,6 @@ extern int pm_generic_poweroff_noirq(struct device *dev); extern int pm_generic_poweroff_late(struct device *dev); extern int pm_generic_poweroff(struct device *dev); extern void pm_generic_complete(struct device *dev); -extern void pm_complete_with_resume_check(struct device *dev); #else /* !CONFIG_PM_SLEEP */ -- cgit v1.2.3-59-g8ed1b From 2265425fd9c512cc9977516b5fe78d03ad9311a7 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Sun, 25 Jun 2017 08:31:30 -0400 Subject: media: lirc_dev: remove min_timeout and max_timeout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no users of this functionality (ir-lirc-codec.c has its own implementation and lirc_zilog.c doesn't use it) so remove it. This only affects users of the lirc kapi, not rc-core drivers. Signed-off-by: David Härdeman Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/lirc_dev.c | 18 ------------------ include/media/lirc_dev.h | 6 ------ 2 files changed, 24 deletions(-) (limited to 'include') diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index e9dae8621670..e16d1138ca48 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -346,24 +346,6 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case LIRC_GET_LENGTH: result = put_user(d->code_length, (__u32 __user *)arg); break; - case LIRC_GET_MIN_TIMEOUT: - if (!(d->features & LIRC_CAN_SET_REC_TIMEOUT) || - d->min_timeout == 0) { - result = -ENOTTY; - break; - } - - result = put_user(d->min_timeout, (__u32 __user *)arg); - break; - case LIRC_GET_MAX_TIMEOUT: - if (!(d->features & LIRC_CAN_SET_REC_TIMEOUT) || - d->max_timeout == 0) { - result = -ENOTTY; - break; - } - - result = put_user(d->max_timeout, (__u32 __user *)arg); - break; default: result = -ENOTTY; } diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h index 981dcabd5fd5..857da67bd931 100644 --- a/include/media/lirc_dev.h +++ b/include/media/lirc_dev.h @@ -125,10 +125,6 @@ static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, * @chunk_size: Size of each FIFO buffer. * Only used if @rbuf is NULL. * @data: private per-driver data - * @min_timeout: Minimum timeout for record. Valid only if - * LIRC_CAN_SET_REC_TIMEOUT is defined. - * @max_timeout: Maximum timeout for record. Valid only if - * LIRC_CAN_SET_REC_TIMEOUT is defined. * @buf: if %NULL, lirc_dev will allocate and manage the buffer, * otherwise allocated by the caller which will * have to write to the buffer by other means, like irq's @@ -155,8 +151,6 @@ struct lirc_dev { bool buf_internal; void *data; - int min_timeout; - int max_timeout; struct rc_dev *rdev; const struct file_operations *fops; struct module *owner; -- cgit v1.2.3-59-g8ed1b From 5248e34b3fa39cdd80ab41f73c545d0bf5428b47 Mon Sep 17 00:00:00 2001 From: Marc Gonzalez Date: Fri, 6 Oct 2017 08:33:41 -0400 Subject: media: rc: Add tango keymap Add a keymap for the Sigma Designs Vantage (dev board) remote control. Signed-off-by: Marc Gonzalez Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-tango.c | 92 +++++++++++++++++++++++++++++++++++++ include/media/rc-map.h | 1 + 3 files changed, 94 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-tango.c (limited to 'include') diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index af6496d709fb..3c1e31321e21 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -88,6 +88,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-reddo.o \ rc-snapstream-firefly.o \ rc-streamzap.o \ + rc-tango.o \ rc-tbs-nec.o \ rc-technisat-ts35.o \ rc-technisat-usb2.o \ diff --git a/drivers/media/rc/keymaps/rc-tango.c b/drivers/media/rc/keymaps/rc-tango.c new file mode 100644 index 000000000000..1c6e8875d46f --- /dev/null +++ b/drivers/media/rc/keymaps/rc-tango.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2017 Sigma Designs + * + * 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. + */ + +#include +#include + +static struct rc_map_table tango_table[] = { + { 0x4cb4a, KEY_POWER }, + { 0x4cb48, KEY_FILE }, + { 0x4cb0f, KEY_SETUP }, + { 0x4cb4d, KEY_SUSPEND }, + { 0x4cb4e, KEY_VOLUMEUP }, + { 0x4cb44, KEY_EJECTCD }, + { 0x4cb13, KEY_TV }, + { 0x4cb51, KEY_MUTE }, + { 0x4cb52, KEY_VOLUMEDOWN }, + + { 0x4cb41, KEY_1 }, + { 0x4cb03, KEY_2 }, + { 0x4cb42, KEY_3 }, + { 0x4cb45, KEY_4 }, + { 0x4cb07, KEY_5 }, + { 0x4cb46, KEY_6 }, + { 0x4cb55, KEY_7 }, + { 0x4cb17, KEY_8 }, + { 0x4cb56, KEY_9 }, + { 0x4cb1b, KEY_0 }, + { 0x4cb59, KEY_DELETE }, + { 0x4cb5a, KEY_CAPSLOCK }, + + { 0x4cb47, KEY_BACK }, + { 0x4cb05, KEY_SWITCHVIDEOMODE }, + { 0x4cb06, KEY_UP }, + { 0x4cb43, KEY_LEFT }, + { 0x4cb01, KEY_RIGHT }, + { 0x4cb0a, KEY_DOWN }, + { 0x4cb02, KEY_ENTER }, + { 0x4cb4b, KEY_INFO }, + { 0x4cb09, KEY_HOME }, + + { 0x4cb53, KEY_MENU }, + { 0x4cb12, KEY_PREVIOUS }, + { 0x4cb50, KEY_PLAY }, + { 0x4cb11, KEY_NEXT }, + { 0x4cb4f, KEY_TITLE }, + { 0x4cb0e, KEY_REWIND }, + { 0x4cb4c, KEY_STOP }, + { 0x4cb0d, KEY_FORWARD }, + { 0x4cb57, KEY_MEDIA_REPEAT }, + { 0x4cb16, KEY_ANGLE }, + { 0x4cb54, KEY_PAUSE }, + { 0x4cb15, KEY_SLOW }, + { 0x4cb5b, KEY_TIME }, + { 0x4cb1a, KEY_AUDIO }, + { 0x4cb58, KEY_SUBTITLE }, + { 0x4cb19, KEY_ZOOM }, + + { 0x4cb5f, KEY_RED }, + { 0x4cb1e, KEY_GREEN }, + { 0x4cb5c, KEY_YELLOW }, + { 0x4cb1d, KEY_BLUE }, +}; + +static struct rc_map_list tango_map = { + .map = { + .scan = tango_table, + .size = ARRAY_SIZE(tango_table), + .rc_proto = RC_PROTO_NECX, + .name = RC_MAP_TANGO, + } +}; + +static int __init init_rc_map_tango(void) +{ + return rc_map_register(&tango_map); +} + +static void __exit exit_rc_map_tango(void) +{ + rc_map_unregister(&tango_map); +} + +module_init(init_rc_map_tango) +module_exit(exit_rc_map_tango) + +MODULE_AUTHOR("Sigma Designs"); +MODULE_LICENSE("GPL"); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 2a160e6e823c..b4ddcb62c993 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -300,6 +300,7 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_REDDO "rc-reddo" #define RC_MAP_SNAPSTREAM_FIREFLY "rc-snapstream-firefly" #define RC_MAP_STREAMZAP "rc-streamzap" +#define RC_MAP_TANGO "rc-tango" #define RC_MAP_TBS_NEC "rc-tbs-nec" #define RC_MAP_TECHNISAT_TS35 "rc-technisat-ts35" #define RC_MAP_TECHNISAT_USB2 "rc-technisat-usb2" -- cgit v1.2.3-59-g8ed1b From 259a41d9ae8f3689742267f340ad2b159d00b302 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 18 Sep 2017 08:21:37 -0400 Subject: media: dvb_frontend: fix return values for FE_SET_PROPERTY There are several problems with regards to the return of FE_SET_PROPERTY. The original idea were to return per-property return codes via tvp->result field, and to return an updated set of values. However, that never worked. What's actually implemented is: - the FE_SET_PROPERTY implementation doesn't call .get_frontend callback in order to get the actual parameters after return; - the tvp->result field is only filled if there's no error. So, it is always filled with zero; - FE_SET_PROPERTY doesn't call memdup_user() nor any other copy_to_user() function. So, any changes to the properties will be lost; - FE_SET_PROPERTY is declared as a write-only ioctl (IOW). While we could fix the above, it could cause regressions. So, let's just assume what the code really does, updating the documentation accordingly and removing the logic that would update the discarded tvp->result. Reviewed-by: Shuah Khan Signed-off-by: Mauro Carvalho Chehab --- Documentation/media/uapi/dvb/fe-get-property.rst | 7 +++++-- drivers/media/dvb-core/dvb_frontend.c | 2 -- include/uapi/linux/dvb/frontend.h | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/media/uapi/dvb/fe-get-property.rst b/Documentation/media/uapi/dvb/fe-get-property.rst index 948d2ba84f2c..b69741d9cedf 100644 --- a/Documentation/media/uapi/dvb/fe-get-property.rst +++ b/Documentation/media/uapi/dvb/fe-get-property.rst @@ -48,8 +48,11 @@ depends on the delivery system and on the device: - This call requires read/write access to the device. - - At return, the values are updated to reflect the actual parameters - used. +.. note:: + + At return, the values aren't updated to reflect the actual + parameters used. If the actual parameters are needed, an explicit + call to ``FE_GET_PROPERTY`` is needed. - ``FE_GET_PROPERTY:`` diff --git a/drivers/media/dvb-core/dvb_frontend.c b/drivers/media/dvb-core/dvb_frontend.c index b19f40be0ab2..5e3bcae477d2 100644 --- a/drivers/media/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb-core/dvb_frontend.c @@ -2125,7 +2125,6 @@ static int dvb_frontend_handle_ioctl(struct file *file, kfree(tvp); return err; } - (tvp + i)->result = err; } kfree(tvp); break; @@ -2170,7 +2169,6 @@ static int dvb_frontend_handle_ioctl(struct file *file, kfree(tvp); return err; } - (tvp + i)->result = err; } if (copy_to_user((void __user *)tvps->props, tvp, diff --git a/include/uapi/linux/dvb/frontend.h b/include/uapi/linux/dvb/frontend.h index 861cacd5711f..6bc26f35217b 100644 --- a/include/uapi/linux/dvb/frontend.h +++ b/include/uapi/linux/dvb/frontend.h @@ -830,7 +830,7 @@ struct dtv_fe_stats { * @cmd: Digital TV command. * @reserved: Not used. * @u: Union with the values for the command. - * @result: Result of the command set (currently unused). + * @result: Unused * * The @u union may have either one of the values below: * -- cgit v1.2.3-59-g8ed1b From 4a269818a7eb8577d32d8b2879099c689ddbd856 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 11 Oct 2017 13:27:29 -0700 Subject: tcp: fix tcp_unlink_write_queue() Yury reported crash with this signature : [ 554.034021] [] 0xffff80003ccd5a58 [ 554.034156] [] skb_release_all+0x14/0x30 [ 554.034288] [] __kfree_skb+0x14/0x28 [ 554.034409] [] tcp_sendmsg_locked+0x4dc/0xcc8 [ 554.034541] [] tcp_sendmsg+0x34/0x58 [ 554.034659] [] inet_sendmsg+0x2c/0xf8 [ 554.034783] [] sock_sendmsg+0x18/0x30 [ 554.034928] [] SyS_sendto+0x84/0xf8 Problem is that skb->destructor contains garbage, and this is because I accidentally removed tcp_skb_tsorted_anchor_cleanup() from tcp_unlink_write_queue() This would trigger with a write(fd, , len) attempt, and we will add to packetdrill this capability to avoid future regressions. Fixes: 75c119afe14f ("tcp: implement rb-tree based retransmit queue") Reported-by: Yury Norov Tested-by: Yury Norov Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 5a95e5886b55..15163454174b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1712,6 +1712,7 @@ static inline void tcp_insert_write_queue_before(struct sk_buff *new, static inline void tcp_unlink_write_queue(struct sk_buff *skb, struct sock *sk) { + tcp_skb_tsorted_anchor_cleanup(skb); __skb_unlink(skb, &sk->sk_write_queue); } -- cgit v1.2.3-59-g8ed1b From e9d4bf219c83d09579bc62512fea2ca10f025d93 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 10 Oct 2017 17:31:42 -0400 Subject: SUNRPC: Fix tracepoint storage issues with svc_recv and svc_rqst_status There is no guarantee that either the request or the svc_xprt exist by the time we get round to printing the trace message. Signed-off-by: Trond Myklebust Cc: stable@vger.kernel.org Signed-off-by: J. Bruce Fields --- include/trace/events/sunrpc.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/trace/events/sunrpc.h b/include/trace/events/sunrpc.h index 8a707f8a41c3..8a13e3903839 100644 --- a/include/trace/events/sunrpc.h +++ b/include/trace/events/sunrpc.h @@ -455,20 +455,22 @@ TRACE_EVENT(svc_recv, TP_ARGS(rqst, status), TP_STRUCT__entry( - __field(struct sockaddr *, addr) __field(__be32, xid) __field(int, status) __field(unsigned long, flags) + __dynamic_array(unsigned char, addr, rqst->rq_addrlen) ), TP_fast_assign( - __entry->addr = (struct sockaddr *)&rqst->rq_addr; __entry->xid = status > 0 ? rqst->rq_xid : 0; __entry->status = status; __entry->flags = rqst->rq_flags; + memcpy(__get_dynamic_array(addr), + &rqst->rq_addr, rqst->rq_addrlen); ), - TP_printk("addr=%pIScp xid=0x%x status=%d flags=%s", __entry->addr, + TP_printk("addr=%pIScp xid=0x%x status=%d flags=%s", + (struct sockaddr *)__get_dynamic_array(addr), be32_to_cpu(__entry->xid), __entry->status, show_rqstp_flags(__entry->flags)) ); @@ -513,22 +515,23 @@ DECLARE_EVENT_CLASS(svc_rqst_status, TP_ARGS(rqst, status), TP_STRUCT__entry( - __field(struct sockaddr *, addr) __field(__be32, xid) - __field(int, dropme) __field(int, status) __field(unsigned long, flags) + __dynamic_array(unsigned char, addr, rqst->rq_addrlen) ), TP_fast_assign( - __entry->addr = (struct sockaddr *)&rqst->rq_addr; __entry->xid = rqst->rq_xid; __entry->status = status; __entry->flags = rqst->rq_flags; + memcpy(__get_dynamic_array(addr), + &rqst->rq_addr, rqst->rq_addrlen); ), TP_printk("addr=%pIScp rq_xid=0x%x status=%d flags=%s", - __entry->addr, be32_to_cpu(__entry->xid), + (struct sockaddr *)__get_dynamic_array(addr), + be32_to_cpu(__entry->xid), __entry->status, show_rqstp_flags(__entry->flags)) ); -- cgit v1.2.3-59-g8ed1b From e81cef5d3001501350b4e596b4bd6dfd26187afa Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 24 Sep 2017 09:25:39 -0400 Subject: blk_rq_map_user_iov(): move iov_iter_advance() down ... into bio_{map,copy}_user_iov() Signed-off-by: Al Viro --- block/bio.c | 6 ++++-- block/blk-map.c | 1 - include/linux/bio.h | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/block/bio.c b/block/bio.c index d1ca7eecc8aa..cd1282db03cb 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1195,7 +1195,7 @@ int bio_uncopy_user(struct bio *bio) */ struct bio *bio_copy_user_iov(struct request_queue *q, struct rq_map_data *map_data, - const struct iov_iter *iter, + struct iov_iter *iter, gfp_t gfp_mask) { struct bio_map_data *bmd; @@ -1298,6 +1298,7 @@ struct bio *bio_copy_user_iov(struct request_queue *q, if (ret) goto cleanup; } + iov_iter_advance(iter, bio->bi_iter.bi_size); bio->bi_private = bmd; return bio; @@ -1320,7 +1321,7 @@ out_bmd: * device. Returns an error pointer in case of error. */ struct bio *bio_map_user_iov(struct request_queue *q, - const struct iov_iter *iter, + struct iov_iter *iter, gfp_t gfp_mask) { int j; @@ -1399,6 +1400,7 @@ struct bio *bio_map_user_iov(struct request_queue *q, * reference to it */ bio_get(bio); + iov_iter_advance(iter, bio->bi_iter.bi_size); return bio; out_unmap: diff --git a/block/blk-map.c b/block/blk-map.c index 2547016aa7aa..891eea11f68e 100644 --- a/block/blk-map.c +++ b/block/blk-map.c @@ -69,7 +69,6 @@ static int __blk_rq_map_user_iov(struct request *rq, if (map_data && map_data->null_mapped) bio_set_flag(bio, BIO_NULL_MAPPED); - iov_iter_advance(iter, bio->bi_iter.bi_size); if (map_data) map_data->offset += bio->bi_iter.bi_size; diff --git a/include/linux/bio.h b/include/linux/bio.h index 275c91c99516..6050c0caa4e1 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -462,7 +462,7 @@ extern int bio_add_pc_page(struct request_queue *, struct bio *, struct page *, int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter); struct rq_map_data; extern struct bio *bio_map_user_iov(struct request_queue *, - const struct iov_iter *, gfp_t); + struct iov_iter *, gfp_t); extern void bio_unmap_user(struct bio *); extern struct bio *bio_map_kern(struct request_queue *, void *, unsigned int, gfp_t); @@ -494,7 +494,7 @@ extern void bio_free_pages(struct bio *bio); extern struct bio *bio_copy_user_iov(struct request_queue *, struct rq_map_data *, - const struct iov_iter *, + struct iov_iter *, gfp_t); extern int bio_uncopy_user(struct bio *); void zero_fill_bio(struct bio *bio); -- cgit v1.2.3-59-g8ed1b From faea13297ea739f94913d56d7b865134b4fc8726 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 24 Sep 2017 14:03:29 -0400 Subject: kill iov_shorten() Signed-off-by: Al Viro --- fs/read_write.c | 21 --------------------- include/linux/uio.h | 2 -- 2 files changed, 23 deletions(-) (limited to 'include') diff --git a/fs/read_write.c b/fs/read_write.c index f0d4b16873e8..b8bf2cb3298a 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -634,27 +634,6 @@ SYSCALL_DEFINE4(pwrite64, unsigned int, fd, const char __user *, buf, return ret; } -/* - * Reduce an iovec's length in-place. Return the resulting number of segments - */ -unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to) -{ - unsigned long seg = 0; - size_t len = 0; - - while (seg < nr_segs) { - seg++; - if (len + iov->iov_len >= to) { - iov->iov_len = to - len; - break; - } - len += iov->iov_len; - iov++; - } - return seg; -} -EXPORT_SYMBOL(iov_shorten); - static ssize_t do_iter_readv_writev(struct file *filp, struct iov_iter *iter, loff_t *ppos, int type, rwf_t flags) { diff --git a/include/linux/uio.h b/include/linux/uio.h index 8a642cda641c..5885daeae721 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -80,8 +80,6 @@ static inline struct iovec iov_iter_iovec(const struct iov_iter *iter) ((iov = iov_iter_iovec(&(iter))), 1); \ iov_iter_advance(&(iter), (iov).iov_len)) -unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to); - size_t iov_iter_copy_from_user_atomic(struct page *page, struct iov_iter *i, unsigned long offset, size_t bytes); void iov_iter_advance(struct iov_iter *i, size_t bytes); -- cgit v1.2.3-59-g8ed1b From 28978713c51b0a70acf748f76f9d6d2d20dcf980 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 10 Oct 2017 23:45:18 -0700 Subject: net: qrtr: Move constants to header file The constants are used by both the name server and clients, so clarify their value and move them to the uapi header. Signed-off-by: Bjorn Andersson Signed-off-by: David S. Miller --- include/uapi/linux/qrtr.h | 3 +++ net/qrtr/qrtr.c | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/qrtr.h b/include/uapi/linux/qrtr.h index 9d76c566f66e..63e8803e4d90 100644 --- a/include/uapi/linux/qrtr.h +++ b/include/uapi/linux/qrtr.h @@ -4,6 +4,9 @@ #include #include +#define QRTR_NODE_BCAST 0xffffffffu +#define QRTR_PORT_CTRL 0xfffffffeu + struct sockaddr_qrtr { __kernel_sa_family_t sq_family; __u32 sq_node; diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c index 7e4b49a8349e..15981abc042c 100644 --- a/net/qrtr/qrtr.c +++ b/net/qrtr/qrtr.c @@ -61,8 +61,6 @@ struct qrtr_hdr { } __packed; #define QRTR_HDR_SIZE sizeof(struct qrtr_hdr) -#define QRTR_NODE_BCAST ((unsigned int)-1) -#define QRTR_PORT_CTRL ((unsigned int)-2) struct qrtr_sock { /* WARNING: sk must be the first member */ -- cgit v1.2.3-59-g8ed1b From da7653f0faabbe45eb2d3fd6e4b400fe003e81ae Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 10 Oct 2017 23:45:19 -0700 Subject: net: qrtr: Add control packet definition to uapi The QMUX protocol specification defines structure of the special control packet messages being sent between handlers of the control port. Add these to the uapi header, as this structure and the associated types are shared between the kernel and all userspace handlers of control messages. Signed-off-by: Bjorn Andersson Signed-off-by: David S. Miller --- include/uapi/linux/qrtr.h | 32 ++++++++++++++++++++++++++++++++ net/qrtr/qrtr.c | 12 ------------ 2 files changed, 32 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/qrtr.h b/include/uapi/linux/qrtr.h index 63e8803e4d90..179af64846e0 100644 --- a/include/uapi/linux/qrtr.h +++ b/include/uapi/linux/qrtr.h @@ -13,4 +13,36 @@ struct sockaddr_qrtr { __u32 sq_port; }; +enum qrtr_pkt_type { + QRTR_TYPE_DATA = 1, + QRTR_TYPE_HELLO = 2, + QRTR_TYPE_BYE = 3, + QRTR_TYPE_NEW_SERVER = 4, + QRTR_TYPE_DEL_SERVER = 5, + QRTR_TYPE_DEL_CLIENT = 6, + QRTR_TYPE_RESUME_TX = 7, + QRTR_TYPE_EXIT = 8, + QRTR_TYPE_PING = 9, + QRTR_TYPE_NEW_LOOKUP = 10, + QRTR_TYPE_DEL_LOOKUP = 11, +}; + +struct qrtr_ctrl_pkt { + __le32 cmd; + + union { + struct { + __le32 service; + __le32 instance; + __le32 node; + __le32 port; + } server; + + struct { + __le32 node; + __le32 port; + } client; + }; +} __packed; + #endif /* _LINUX_QRTR_H */ diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c index 15981abc042c..d85ca7170b8f 100644 --- a/net/qrtr/qrtr.c +++ b/net/qrtr/qrtr.c @@ -26,18 +26,6 @@ #define QRTR_MIN_EPH_SOCKET 0x4000 #define QRTR_MAX_EPH_SOCKET 0x7fff -enum qrtr_pkt_type { - QRTR_TYPE_DATA = 1, - QRTR_TYPE_HELLO = 2, - QRTR_TYPE_BYE = 3, - QRTR_TYPE_NEW_SERVER = 4, - QRTR_TYPE_DEL_SERVER = 5, - QRTR_TYPE_DEL_CLIENT = 6, - QRTR_TYPE_RESUME_TX = 7, - QRTR_TYPE_EXIT = 8, - QRTR_TYPE_PING = 9, -}; - /** * struct qrtr_hdr - (I|R)PCrouter packet header * @version: protocol version -- cgit v1.2.3-59-g8ed1b From 418da17214aca5ef5f0b6f7588905ee7df92f98f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 14 Mar 2017 23:25:07 -0700 Subject: drm: Pass struct drm_file * to __drm_mode_object_find [v2] This will allow __drm_mode_object_file to be extended to perform access control checks based on the file in use. v2: Also fix up vboxvideo driver in staging [airlied: merging early as this is an API change] Suggested-by: Daniel Vetter Signed-off-by: Keith Packard Signed-off-by: Dave Airlie --- drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | 16 ++++++++-------- drivers/gpu/drm/amd/amdgpu/dce_virtual.c | 4 ++-- drivers/gpu/drm/ast/ast_mode.c | 2 +- drivers/gpu/drm/bochs/bochs_kms.c | 2 +- drivers/gpu/drm/cirrus/cirrus_mode.c | 2 +- drivers/gpu/drm/drm_atomic.c | 8 ++++---- drivers/gpu/drm/drm_atomic_helper.c | 2 +- drivers/gpu/drm/drm_color_mgmt.c | 4 ++-- drivers/gpu/drm/drm_connector.c | 2 +- drivers/gpu/drm/drm_crtc.c | 8 ++++---- drivers/gpu/drm/drm_crtc_internal.h | 1 + drivers/gpu/drm/drm_encoder.c | 2 +- drivers/gpu/drm/drm_framebuffer.c | 9 +++++---- drivers/gpu/drm/drm_mode_object.c | 10 ++++++---- drivers/gpu/drm/drm_plane.c | 14 +++++++------- drivers/gpu/drm/drm_probe_helper.c | 2 +- drivers/gpu/drm/drm_property.c | 6 +++--- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 2 +- drivers/gpu/drm/i915/intel_display.c | 2 +- drivers/gpu/drm/i915/intel_overlay.c | 2 +- drivers/gpu/drm/i915/intel_sprite.c | 2 +- drivers/gpu/drm/mgag200/mgag200_mode.c | 2 +- drivers/gpu/drm/nouveau/nouveau_connector.c | 4 ++-- drivers/gpu/drm/radeon/r100.c | 2 +- drivers/gpu/drm/radeon/r600_cs.c | 2 +- drivers/gpu/drm/radeon/radeon_connectors.c | 16 ++++++++-------- drivers/gpu/drm/udl/udl_connector.c | 2 +- drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 4 ++-- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 +- drivers/staging/vboxvideo/vbox_mode.c | 2 +- include/drm/drm_connector.h | 3 ++- include/drm/drm_crtc.h | 5 +++-- include/drm/drm_encoder.h | 3 ++- include/drm/drm_framebuffer.h | 1 + include/drm/drm_mode_object.h | 2 ++ include/drm/drm_plane.h | 3 ++- include/drm/drm_property.h | 3 ++- 37 files changed, 85 insertions(+), 73 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c index f51b41f094ef..df9cbc78e168 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c @@ -231,7 +231,7 @@ amdgpu_connector_update_scratch_regs(struct drm_connector *connector, if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -256,7 +256,7 @@ amdgpu_connector_find_encoder(struct drm_connector *connector, for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -372,7 +372,7 @@ amdgpu_connector_best_single_encoder(struct drm_connector *connector) /* pick the encoder ids */ if (enc_id) - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } @@ -1077,7 +1077,7 @@ amdgpu_connector_dvi_detect(struct drm_connector *connector, bool force) if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -1134,7 +1134,7 @@ amdgpu_connector_dvi_encoder(struct drm_connector *connector) if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -1153,7 +1153,7 @@ amdgpu_connector_dvi_encoder(struct drm_connector *connector) /* then check use digitial */ /* pick the first one */ if (enc_id) - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } @@ -1294,7 +1294,7 @@ u16 amdgpu_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -1323,7 +1323,7 @@ static bool amdgpu_connector_encoder_is_hbr2(struct drm_connector *connector) for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c index b9ee9073cb0d..a8829af120c1 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c @@ -288,7 +288,7 @@ dce_virtual_encoder(struct drm_connector *connector) if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -298,7 +298,7 @@ dce_virtual_encoder(struct drm_connector *connector) /* pick the first one */ if (enc_id) - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 6f3849ec0c1d..9555a3542022 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -713,7 +713,7 @@ static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connect int enc_id = connector->encoder_ids[0]; /* pick the encoder ids */ if (enc_id) - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } diff --git a/drivers/gpu/drm/bochs/bochs_kms.c b/drivers/gpu/drm/bochs/bochs_kms.c index 6a91e62da2f4..a24a18fbd65a 100644 --- a/drivers/gpu/drm/bochs/bochs_kms.c +++ b/drivers/gpu/drm/bochs/bochs_kms.c @@ -213,7 +213,7 @@ bochs_connector_best_encoder(struct drm_connector *connector) int enc_id = connector->encoder_ids[0]; /* pick the encoder ids */ if (enc_id) - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index a4c4a465b385..cd23b1b28259 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -457,7 +457,7 @@ static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector int enc_id = connector->encoder_ids[0]; /* pick the encoder ids */ if (enc_id) - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 366c56fe5f58..268969fecee7 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -718,7 +718,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, struct drm_mode_config *config = &dev->mode_config; if (property == config->prop_fb_id) { - struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, val); + struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, NULL, val); drm_atomic_set_fb_for_plane(state, fb); if (fb) drm_framebuffer_put(fb); @@ -734,7 +734,7 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane, return -EINVAL; } else if (property == config->prop_crtc_id) { - struct drm_crtc *crtc = drm_crtc_find(dev, val); + struct drm_crtc *crtc = drm_crtc_find(dev, NULL, val); return drm_atomic_set_crtc_for_plane(state, crtc); } else if (property == config->prop_crtc_x) { state->crtc_x = U642I64(val); @@ -1149,7 +1149,7 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, struct drm_mode_config *config = &dev->mode_config; if (property == config->prop_crtc_id) { - struct drm_crtc *crtc = drm_crtc_find(dev, val); + struct drm_crtc *crtc = drm_crtc_find(dev, NULL, val); return drm_atomic_set_crtc_for_connector(state, crtc); } else if (property == config->dpms_property) { /* setting DPMS property requires special handling, which @@ -2259,7 +2259,7 @@ retry: goto out; } - obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY); + obj = drm_mode_object_find(dev, file_priv, obj_id, DRM_MODE_OBJECT_ANY); if (!obj) { ret = -ENOENT; goto out; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 01c34bc5b5b0..c49fbc4db3b5 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3206,7 +3206,7 @@ struct drm_encoder * drm_atomic_helper_best_encoder(struct drm_connector *connector) { WARN_ON(connector->encoder_ids[1]); - return drm_encoder_find(connector->dev, connector->encoder_ids[0]); + return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]); } EXPORT_SYMBOL(drm_atomic_helper_best_encoder); diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index fe0982708e95..0d002b045bd2 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -230,7 +230,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - crtc = drm_crtc_find(dev, crtc_lut->crtc_id); + crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id); if (!crtc) return -ENOENT; @@ -308,7 +308,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - crtc = drm_crtc_find(dev, crtc_lut->crtc_id); + crtc = drm_crtc_find(dev, file_priv, crtc_lut->crtc_id); if (!crtc) return -ENOENT; diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index d8ca526ca4ee..704fc8934616 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -1310,7 +1310,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); - connector = drm_connector_lookup(dev, out_resp->connector_id); + connector = drm_connector_lookup(dev, file_priv, out_resp->connector_id); if (!connector) return -ENOENT; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 68b4e976d5e0..f0556e654116 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -402,7 +402,7 @@ int drm_mode_getcrtc(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - crtc = drm_crtc_find(dev, crtc_resp->crtc_id); + crtc = drm_crtc_find(dev, file_priv, crtc_resp->crtc_id); if (!crtc) return -ENOENT; @@ -569,7 +569,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000) return -ERANGE; - crtc = drm_crtc_find(dev, crtc_req->crtc_id); + crtc = drm_crtc_find(dev, file_priv, crtc_req->crtc_id); if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); return -ENOENT; @@ -595,7 +595,7 @@ retry: /* Make refcounting symmetric with the lookup path. */ drm_framebuffer_get(fb); } else { - fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); + fb = drm_framebuffer_lookup(dev, file_priv, crtc_req->fb_id); if (!fb) { DRM_DEBUG_KMS("Unknown FB ID%d\n", crtc_req->fb_id); @@ -680,7 +680,7 @@ retry: goto out; } - connector = drm_connector_lookup(dev, out_id); + connector = drm_connector_lookup(dev, file_priv, out_id); if (!connector) { DRM_DEBUG_KMS("Connector id %d unknown\n", out_id); diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index a43582076b20..9ebb8841778c 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -106,6 +106,7 @@ int drm_mode_object_add(struct drm_device *dev, struct drm_mode_object *obj, void drm_mode_object_register(struct drm_device *dev, struct drm_mode_object *obj); struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, + struct drm_file *file_priv, uint32_t id, uint32_t type); void drm_mode_object_unregister(struct drm_device *dev, struct drm_mode_object *object); diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 0708779840d2..43f644844b83 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -220,7 +220,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - encoder = drm_encoder_find(dev, enc_resp->encoder_id); + encoder = drm_encoder_find(dev, file_priv, enc_resp->encoder_id); if (!encoder) return -ENOENT; diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index af279844d7ce..2affe53f3fda 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -381,7 +381,7 @@ int drm_mode_rmfb(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - fb = drm_framebuffer_lookup(dev, *id); + fb = drm_framebuffer_lookup(dev, file_priv, *id); if (!fb) return -ENOENT; @@ -450,7 +450,7 @@ int drm_mode_getfb(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - fb = drm_framebuffer_lookup(dev, r->fb_id); + fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); if (!fb) return -ENOENT; @@ -515,7 +515,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - fb = drm_framebuffer_lookup(dev, r->fb_id); + fb = drm_framebuffer_lookup(dev, file_priv, r->fb_id); if (!fb) return -ENOENT; @@ -688,12 +688,13 @@ EXPORT_SYMBOL(drm_framebuffer_init); * again, using drm_framebuffer_put(). */ struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, + struct drm_file *file_priv, uint32_t id) { struct drm_mode_object *obj; struct drm_framebuffer *fb = NULL; - obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_FB); + obj = __drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_FB); if (obj) fb = obj_to_fb(obj); return fb; diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c index 7a1ea91d3343..240a05d91a53 100644 --- a/drivers/gpu/drm/drm_mode_object.c +++ b/drivers/gpu/drm/drm_mode_object.c @@ -105,6 +105,7 @@ void drm_mode_object_unregister(struct drm_device *dev, } struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, + struct drm_file *file_priv, uint32_t id, uint32_t type) { struct drm_mode_object *obj = NULL; @@ -127,7 +128,7 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, /** * drm_mode_object_find - look up a drm object with static lifetime - * @dev: drm device + * @file_priv: drm file * @id: id of the mode object * @type: type of the mode object * @@ -136,11 +137,12 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, * by callind drm_mode_object_put(). */ struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, + struct drm_file *file_priv, uint32_t id, uint32_t type) { struct drm_mode_object *obj = NULL; - obj = __drm_mode_object_find(dev, id, type); + obj = __drm_mode_object_find(dev, file_priv, id, type); return obj; } EXPORT_SYMBOL(drm_mode_object_find); @@ -359,7 +361,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); - obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); + obj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type); if (!obj) { ret = -ENOENT; goto out; @@ -481,7 +483,7 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); + arg_obj = drm_mode_object_find(dev, file_priv, arg->obj_id, arg->obj_type); if (!arg_obj) return -ENOENT; diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 72cba9805edc..6af02c7b5da3 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -513,7 +513,7 @@ int drm_mode_getplane(struct drm_device *dev, void *data, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - plane = drm_plane_find(dev, plane_resp->plane_id); + plane = drm_plane_find(dev, file_priv, plane_resp->plane_id); if (!plane) return -ENOENT; @@ -703,7 +703,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, * First, find the plane, crtc, and fb objects. If not available, * we don't bother to call the driver. */ - plane = drm_plane_find(dev, plane_req->plane_id); + plane = drm_plane_find(dev, file_priv, plane_req->plane_id); if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n", plane_req->plane_id); @@ -711,14 +711,14 @@ int drm_mode_setplane(struct drm_device *dev, void *data, } if (plane_req->fb_id) { - fb = drm_framebuffer_lookup(dev, plane_req->fb_id); + fb = drm_framebuffer_lookup(dev, file_priv, plane_req->fb_id); if (!fb) { DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", plane_req->fb_id); return -ENOENT; } - crtc = drm_crtc_find(dev, plane_req->crtc_id); + crtc = drm_crtc_find(dev, file_priv, plane_req->crtc_id); if (!crtc) { drm_framebuffer_put(fb); DRM_DEBUG_KMS("Unknown crtc ID %d\n", @@ -829,7 +829,7 @@ static int drm_mode_cursor_common(struct drm_device *dev, if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; - crtc = drm_crtc_find(dev, req->crtc_id); + crtc = drm_crtc_find(dev, file_priv, req->crtc_id); if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); return -ENOENT; @@ -944,7 +944,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) return -EINVAL; - crtc = drm_crtc_find(dev, page_flip->crtc_id); + crtc = drm_crtc_find(dev, file_priv, page_flip->crtc_id); if (!crtc) return -ENOENT; @@ -1005,7 +1005,7 @@ retry: goto out; } - fb = drm_framebuffer_lookup(dev, page_flip->fb_id); + fb = drm_framebuffer_lookup(dev, file_priv, page_flip->fb_id); if (!fb) { ret = -ENOENT; goto out; diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 5840aabbf24e..6dc2dde5b672 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -99,7 +99,7 @@ drm_mode_validate_pipeline(struct drm_display_mode *mode, /* Step 2: Validate against encoders and crtcs */ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - struct drm_encoder *encoder = drm_encoder_find(dev, ids[i]); + struct drm_encoder *encoder = drm_encoder_find(dev, NULL, ids[i]); struct drm_crtc *crtc; if (!encoder) diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c index bc5128203056..bae50e6b819d 100644 --- a/drivers/gpu/drm/drm_property.c +++ b/drivers/gpu/drm/drm_property.c @@ -450,7 +450,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - property = drm_property_find(dev, out_resp->prop_id); + property = drm_property_find(dev, file_priv, out_resp->prop_id); if (!property) return -ENOENT; @@ -634,7 +634,7 @@ struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, struct drm_mode_object *obj; struct drm_property_blob *blob = NULL; - obj = __drm_mode_object_find(dev, id, DRM_MODE_OBJECT_BLOB); + obj = __drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_BLOB); if (obj) blob = obj_to_blob(obj); return blob; @@ -897,7 +897,7 @@ bool drm_property_change_valid_get(struct drm_property *property, if (value == 0) return true; - *ref = __drm_mode_object_find(property->dev, value, + *ref = __drm_mode_object_find(property->dev, NULL, value, property->values[0]); return *ref != NULL; } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index ec4dd9df9150..f4eba87c96f3 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -36,7 +36,7 @@ static int hibmc_connector_mode_valid(struct drm_connector *connector, static struct drm_encoder * hibmc_connector_best_encoder(struct drm_connector *connector) { - return drm_encoder_find(connector->dev, connector->encoder_ids[0]); + return drm_encoder_find(connector->dev, NULL, connector->encoder_ids[0]); } static const struct drm_connector_helper_funcs diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f780f39e0758..0bec6deaae4f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -13511,7 +13511,7 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, struct drm_crtc *drmmode_crtc; struct intel_crtc *crtc; - drmmode_crtc = drm_crtc_find(dev, pipe_from_crtc_id->crtc_id); + drmmode_crtc = drm_crtc_find(dev, file, pipe_from_crtc_id->crtc_id); if (!drmmode_crtc) return -ENOENT; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index aace22e7ccac..1b397b41cb4f 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1134,7 +1134,7 @@ int intel_overlay_put_image_ioctl(struct drm_device *dev, void *data, if (!params) return -ENOMEM; - drmmode_crtc = drm_crtc_find(dev, put_image_rec->crtc_id); + drmmode_crtc = drm_crtc_find(dev, file_priv, put_image_rec->crtc_id); if (!drmmode_crtc) { ret = -ENOENT; goto out_free; diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c index b0d6e3e28d07..28a1209d87e2 100644 --- a/drivers/gpu/drm/i915/intel_sprite.c +++ b/drivers/gpu/drm/i915/intel_sprite.c @@ -994,7 +994,7 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data, set->flags & I915_SET_COLORKEY_DESTINATION) return -EINVAL; - plane = drm_plane_find(dev, set->plane_id); + plane = drm_plane_find(dev, file_priv, set->plane_id); if (!plane || plane->type != DRM_PLANE_TYPE_OVERLAY) return -ENOENT; diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index 5e9cd4c0e8b6..68e5d9c94475 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1670,7 +1670,7 @@ static struct drm_encoder *mga_connector_best_encoder(struct drm_connector int enc_id = connector->encoder_ids[0]; /* pick the encoder ids */ if (enc_id) - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 70d8e0d69ad5..69d6e61a01ec 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -373,7 +373,7 @@ find_encoder(struct drm_connector *connector, int type) if (!id) break; - enc = drm_encoder_find(dev, id); + enc = drm_encoder_find(dev, NULL, id); if (!enc) continue; nv_encoder = nouveau_encoder(enc); @@ -441,7 +441,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) if (id == 0) break; - encoder = drm_encoder_find(dev, id); + encoder = drm_encoder_find(dev, NULL, id); if (!encoder) continue; nv_encoder = nouveau_encoder(encoder); diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index c31e660e35db..7d39ed63e5be 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -1456,7 +1456,7 @@ int r100_cs_packet_parse_vline(struct radeon_cs_parser *p) header = radeon_get_ib_value(p, h_idx); crtc_id = radeon_get_ib_value(p, h_idx + 5); reg = R100_CP_PACKET0_GET_REG(header); - crtc = drm_crtc_find(p->rdev->ddev, crtc_id); + crtc = drm_crtc_find(p->rdev->ddev, p->filp, crtc_id); if (!crtc) { DRM_ERROR("cannot find crtc %d\n", crtc_id); return -ENOENT; diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c index 97fd58e97043..c96b31950ca7 100644 --- a/drivers/gpu/drm/radeon/r600_cs.c +++ b/drivers/gpu/drm/radeon/r600_cs.c @@ -887,7 +887,7 @@ int r600_cs_common_vline_parse(struct radeon_cs_parser *p, crtc_id = radeon_get_ib_value(p, h_idx + 2 + 7 + 1); reg = R600_CP_PACKET0_GET_REG(header); - crtc = drm_crtc_find(p->rdev->ddev, crtc_id); + crtc = drm_crtc_find(p->rdev->ddev, p->filp, crtc_id); if (!crtc) { DRM_ERROR("cannot find crtc %d\n", crtc_id); return -ENOENT; diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 2f642cbefd8e..59dcefb2df3b 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -263,7 +263,7 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -290,7 +290,7 @@ static struct drm_encoder *radeon_find_encoder(struct drm_connector *connector, if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -404,7 +404,7 @@ static struct drm_encoder *radeon_best_single_encoder(struct drm_connector *conn int enc_id = connector->encoder_ids[0]; /* pick the encoder ids */ if (enc_id) - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } @@ -1368,7 +1368,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -1454,7 +1454,7 @@ static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -1473,7 +1473,7 @@ static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) /* then check use digitial */ /* pick the first one */ if (enc_id) - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } @@ -1620,7 +1620,7 @@ u16 radeon_connector_encoder_get_dp_bridge_encoder_id(struct drm_connector *conn if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; @@ -1649,7 +1649,7 @@ static bool radeon_connector_encoder_is_hbr2(struct drm_connector *connector) if (connector->encoder_ids[i] == 0) break; - encoder = drm_encoder_find(connector->dev, connector->encoder_ids[i]); + encoder = drm_encoder_find(connector->dev, NULL, connector->encoder_ids[i]); if (!encoder) continue; diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index 9f9a49748d17..091ca81658eb 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -105,7 +105,7 @@ static struct drm_encoder* udl_best_single_encoder(struct drm_connector *connector) { int enc_id = connector->encoder_ids[0]; - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); } static int udl_connector_set_property(struct drm_connector *connector, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 5ec24fd801cd..01be355525e4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -286,7 +286,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); - fb = drm_framebuffer_lookup(dev, arg->fb_id); + fb = drm_framebuffer_lookup(dev, file_priv, arg->fb_id); if (!fb) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -ENOENT; @@ -369,7 +369,7 @@ int vmw_present_readback_ioctl(struct drm_device *dev, void *data, drm_modeset_lock_all(dev); - fb = drm_framebuffer_lookup(dev, arg->fb_id); + fb = drm_framebuffer_lookup(dev, file_priv, arg->fb_id); if (!fb) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -ENOENT; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index b850562fbdd6..0545740b3724 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1726,7 +1726,7 @@ int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data, return 0; } - crtc = drm_crtc_find(dev, arg->crtc_id); + crtc = drm_crtc_find(dev, file_priv, arg->crtc_id); if (!crtc) { ret = -ENOENT; goto out; diff --git a/drivers/staging/vboxvideo/vbox_mode.c b/drivers/staging/vboxvideo/vbox_mode.c index 257a77830410..c745a0402c68 100644 --- a/drivers/staging/vboxvideo/vbox_mode.c +++ b/drivers/staging/vboxvideo/vbox_mode.c @@ -377,7 +377,7 @@ static struct drm_encoder *vbox_best_single_encoder(struct drm_connector /* pick the encoder ids */ if (enc_id) - return drm_encoder_find(connector->dev, enc_id); + return drm_encoder_find(connector->dev, NULL, enc_id); return NULL; } diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index b34904dc8b9b..b4285c40e1e4 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -939,10 +939,11 @@ static inline unsigned drm_connector_index(struct drm_connector *connector) * add takes a reference to it. */ static inline struct drm_connector *drm_connector_lookup(struct drm_device *dev, + struct drm_file *file_priv, uint32_t id) { struct drm_mode_object *mo; - mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CONNECTOR); + mo = drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_CONNECTOR); return mo ? obj_to_connector(mo) : NULL; } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 80c97210eda5..f7fcceef46d9 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -959,10 +959,11 @@ struct drm_crtc *drm_crtc_from_index(struct drm_device *dev, int idx); * userspace interface should be done using &drm_property. */ static inline struct drm_crtc *drm_crtc_find(struct drm_device *dev, - uint32_t id) + struct drm_file *file_priv, + uint32_t id) { struct drm_mode_object *mo; - mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_CRTC); + mo = drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_CRTC); return mo ? obj_to_crtc(mo) : NULL; } diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 8d8245ec0181..86db0da8bdcb 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -214,11 +214,12 @@ static inline bool drm_encoder_crtc_ok(struct drm_encoder *encoder, * drm_mode_object_find(). */ static inline struct drm_encoder *drm_encoder_find(struct drm_device *dev, + struct drm_file *file_priv, uint32_t id) { struct drm_mode_object *mo; - mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); + mo = drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_ENCODER); return mo ? obj_to_encoder(mo) : NULL; } diff --git a/include/drm/drm_framebuffer.h b/include/drm/drm_framebuffer.h index b6996ddb19d6..4c5ee4ae54df 100644 --- a/include/drm/drm_framebuffer.h +++ b/include/drm/drm_framebuffer.h @@ -205,6 +205,7 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs); struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, + struct drm_file *file_priv, uint32_t id); void drm_framebuffer_remove(struct drm_framebuffer *fb); void drm_framebuffer_cleanup(struct drm_framebuffer *fb); diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h index a767b4a30a6d..b2f920b518e3 100644 --- a/include/drm/drm_mode_object.h +++ b/include/drm/drm_mode_object.h @@ -27,6 +27,7 @@ struct drm_object_properties; struct drm_property; struct drm_device; +struct drm_file; /** * struct drm_mode_object - base structure for modeset objects @@ -113,6 +114,7 @@ struct drm_object_properties { } struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, + struct drm_file *file_priv, uint32_t id, uint32_t type); void drm_mode_object_get(struct drm_mode_object *obj); void drm_mode_object_put(struct drm_mode_object *obj); diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 82a217bd77f0..069c4c8ce360 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -597,10 +597,11 @@ int drm_mode_plane_set_obj_prop(struct drm_plane *plane, * drm_mode_object_find(). */ static inline struct drm_plane *drm_plane_find(struct drm_device *dev, + struct drm_file *file_priv, uint32_t id) { struct drm_mode_object *mo; - mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PLANE); + mo = drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_PLANE); return mo ? obj_to_plane(mo) : NULL; } diff --git a/include/drm/drm_property.h b/include/drm/drm_property.h index 37355c623e6c..429d8218f740 100644 --- a/include/drm/drm_property.h +++ b/include/drm/drm_property.h @@ -312,10 +312,11 @@ drm_property_unreference_blob(struct drm_property_blob *blob) * This function looks up the property object specified by id and returns it. */ static inline struct drm_property *drm_property_find(struct drm_device *dev, + struct drm_file *file_priv, uint32_t id) { struct drm_mode_object *mo; - mo = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_PROPERTY); + mo = drm_mode_object_find(dev, file_priv, id, DRM_MODE_OBJECT_PROPERTY); return mo ? obj_to_property(mo) : NULL; } -- cgit v1.2.3-59-g8ed1b From 09cf698a594276139b7dfafb232af3fe4fbc4438 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 18 Feb 2017 01:44:03 -0500 Subject: new primitive: iov_iter_for_each_range() For kvec and bvec: feeds segments to given callback as long as it returns 0. For iovec and pipe: fails. Signed-off-by: Al Viro --- include/linux/uio.h | 4 ++++ lib/iov_iter.c | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/include/linux/uio.h b/include/linux/uio.h index 5885daeae721..e67e12adb136 100644 --- a/include/linux/uio.h +++ b/include/linux/uio.h @@ -244,4 +244,8 @@ int compat_import_iovec(int type, const struct compat_iovec __user * uvector, int import_single_range(int type, void __user *buf, size_t len, struct iovec *iov, struct iov_iter *i); +int iov_iter_for_each_range(struct iov_iter *i, size_t bytes, + int (*f)(struct kvec *vec, void *context), + void *context); + #endif diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 1c1c06ddc20a..970212670b6a 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1446,3 +1446,25 @@ int import_single_range(int rw, void __user *buf, size_t len, return 0; } EXPORT_SYMBOL(import_single_range); + +int iov_iter_for_each_range(struct iov_iter *i, size_t bytes, + int (*f)(struct kvec *vec, void *context), + void *context) +{ + struct kvec w; + int err = -EINVAL; + if (!bytes) + return 0; + + iterate_all_kinds(i, bytes, v, -EINVAL, ({ + w.iov_base = kmap(v.bv_page) + v.bv_offset; + w.iov_len = v.bv_len; + err = f(&w, context); + kunmap(v.bv_page); + err;}), ({ + w = v; + err = f(&w, context);}) + ) + return err; +} +EXPORT_SYMBOL(iov_iter_for_each_range); -- cgit v1.2.3-59-g8ed1b From 843e79d05addd8eb06992cd6dfafc7b9d53f2bc8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 11 Oct 2017 09:41:07 +0200 Subject: net: sched: make tc_action_ops->get_dev return dev and avoid passing net Return dev directly, NULL if not possible. That is enough. Makes no sense to pass struct net * to get_dev op, as there is only one net possible, the one the action was created in. So just store it in mirred priv and use directly. Rename the mirred op callback function. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/act_api.h | 3 +-- include/net/tc_act/tc_mirred.h | 1 + net/sched/act_mirred.c | 13 +++++-------- net/sched/cls_api.c | 6 ++---- 4 files changed, 9 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/act_api.h b/include/net/act_api.h index b944e0eb93be..900168a9901e 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -93,8 +93,7 @@ struct tc_action_ops { int (*walk)(struct net *, struct sk_buff *, struct netlink_callback *, int, const struct tc_action_ops *); void (*stats_update)(struct tc_action *, u64, u32, u64); - int (*get_dev)(const struct tc_action *a, struct net *net, - struct net_device **mirred_dev); + struct net_device *(*get_dev)(const struct tc_action *a); }; struct tc_action_net { diff --git a/include/net/tc_act/tc_mirred.h b/include/net/tc_act/tc_mirred.h index 604bc31e23ab..21a656569840 100644 --- a/include/net/tc_act/tc_mirred.h +++ b/include/net/tc_act/tc_mirred.h @@ -10,6 +10,7 @@ struct tcf_mirred { int tcfm_ifindex; bool tcfm_mac_header_xmit; struct net_device __rcu *tcfm_dev; + struct net *net; struct list_head tcfm_list; }; #define to_mirred(a) ((struct tcf_mirred *)a) diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 416627c66f08..8b3e59388480 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -140,6 +140,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, m->tcfm_eaction = parm->eaction; if (dev != NULL) { m->tcfm_ifindex = parm->ifindex; + m->net = net; if (ret != ACT_P_CREATED) dev_put(rcu_dereference_protected(m->tcfm_dev, 1)); dev_hold(dev); @@ -313,15 +314,11 @@ static struct notifier_block mirred_device_notifier = { .notifier_call = mirred_device_event, }; -static int tcf_mirred_device(const struct tc_action *a, struct net *net, - struct net_device **mirred_dev) +static struct net_device *tcf_mirred_get_dev(const struct tc_action *a) { - int ifindex = tcf_mirred_ifindex(a); + struct tcf_mirred *m = to_mirred(a); - *mirred_dev = __dev_get_by_index(net, ifindex); - if (!*mirred_dev) - return -EINVAL; - return 0; + return __dev_get_by_index(m->net, m->tcfm_ifindex); } static struct tc_action_ops act_mirred_ops = { @@ -336,7 +333,7 @@ static struct tc_action_ops act_mirred_ops = { .walk = tcf_mirred_walker, .lookup = tcf_mirred_search, .size = sizeof(struct tcf_mirred), - .get_dev = tcf_mirred_device, + .get_dev = tcf_mirred_get_dev, }; static __net_init int mirred_init_net(struct net *net) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 0b2219adf520..450873b0c4b9 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1016,10 +1016,8 @@ int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, tcf_exts_to_list(exts, &actions); list_for_each_entry(a, &actions, list) { - if (a->ops->get_dev) { - a->ops->get_dev(a, dev_net(dev), hw_dev); - break; - } + if (a->ops->get_dev) + *hw_dev = a->ops->get_dev(a); } if (*hw_dev) return 0; -- cgit v1.2.3-59-g8ed1b From b3f55bdda8df55a563005e00b1b71212d8546541 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 11 Oct 2017 09:41:08 +0200 Subject: net: sched: introduce per-egress action device callbacks Introduce infrastructure that allows drivers to register callbacks that are called whenever tc would offload inserted rule and specified device acts as tc action egress device. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/act_api.h | 34 ++++++++ include/net/pkt_cls.h | 2 + net/sched/act_api.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++ net/sched/cls_api.c | 30 +++++++ 4 files changed, 286 insertions(+) (limited to 'include') diff --git a/include/net/act_api.h b/include/net/act_api.h index 900168a9901e..f5e8c9048fb0 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -174,4 +174,38 @@ static inline void tcf_action_stats_update(struct tc_action *a, u64 bytes, #endif } +typedef int tc_setup_cb_t(enum tc_setup_type type, + void *type_data, void *cb_priv); + +#ifdef CONFIG_NET_CLS_ACT +int tc_setup_cb_egdev_register(const struct net_device *dev, + tc_setup_cb_t *cb, void *cb_priv); +void tc_setup_cb_egdev_unregister(const struct net_device *dev, + tc_setup_cb_t *cb, void *cb_priv); +int tc_setup_cb_egdev_call(const struct net_device *dev, + enum tc_setup_type type, void *type_data, + bool err_stop); +#else +static inline +int tc_setup_cb_egdev_register(const struct net_device *dev, + tc_setup_cb_t *cb, void *cb_priv) +{ + return 0; +} + +static inline +void tc_setup_cb_egdev_unregister(const struct net_device *dev, + tc_setup_cb_t *cb, void *cb_priv) +{ +} + +static inline +int tc_setup_cb_egdev_call(const struct net_device *dev, + enum tc_setup_type type, void *type_data, + bool err_stop) +{ + return 0; +} +#endif + #endif diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index e80edd8879ef..6f8149c82571 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -206,6 +206,8 @@ int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts); int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts); int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, struct net_device **hw_dev); +int tcf_exts_egdev_cb_call(struct tcf_exts *exts, enum tc_setup_type type, + void *type_data, bool err_stop); /** * struct tcf_pkt_info - packet information diff --git a/net/sched/act_api.c b/net/sched/act_api.c index da6fa82c98a8..ac97db92ab68 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -1249,8 +1251,226 @@ out_module_put: return skb->len; } +struct tcf_action_net { + struct rhashtable egdev_ht; +}; + +static unsigned int tcf_action_net_id; + +struct tcf_action_egdev_cb { + struct list_head list; + tc_setup_cb_t *cb; + void *cb_priv; +}; + +struct tcf_action_egdev { + struct rhash_head ht_node; + const struct net_device *dev; + unsigned int refcnt; + struct list_head cb_list; +}; + +static const struct rhashtable_params tcf_action_egdev_ht_params = { + .key_offset = offsetof(struct tcf_action_egdev, dev), + .head_offset = offsetof(struct tcf_action_egdev, ht_node), + .key_len = sizeof(const struct net_device *), +}; + +static struct tcf_action_egdev * +tcf_action_egdev_lookup(const struct net_device *dev) +{ + struct net *net = dev_net(dev); + struct tcf_action_net *tan = net_generic(net, tcf_action_net_id); + + return rhashtable_lookup_fast(&tan->egdev_ht, &dev, + tcf_action_egdev_ht_params); +} + +static struct tcf_action_egdev * +tcf_action_egdev_get(const struct net_device *dev) +{ + struct tcf_action_egdev *egdev; + struct tcf_action_net *tan; + + egdev = tcf_action_egdev_lookup(dev); + if (egdev) + goto inc_ref; + + egdev = kzalloc(sizeof(*egdev), GFP_KERNEL); + if (!egdev) + return NULL; + INIT_LIST_HEAD(&egdev->cb_list); + tan = net_generic(dev_net(dev), tcf_action_net_id); + rhashtable_insert_fast(&tan->egdev_ht, &egdev->ht_node, + tcf_action_egdev_ht_params); + +inc_ref: + egdev->refcnt++; + return egdev; +} + +static void tcf_action_egdev_put(struct tcf_action_egdev *egdev) +{ + struct tcf_action_net *tan; + + if (--egdev->refcnt) + return; + tan = net_generic(dev_net(egdev->dev), tcf_action_net_id); + rhashtable_remove_fast(&tan->egdev_ht, &egdev->ht_node, + tcf_action_egdev_ht_params); + kfree(egdev); +} + +static struct tcf_action_egdev_cb * +tcf_action_egdev_cb_lookup(struct tcf_action_egdev *egdev, + tc_setup_cb_t *cb, void *cb_priv) +{ + struct tcf_action_egdev_cb *egdev_cb; + + list_for_each_entry(egdev_cb, &egdev->cb_list, list) + if (egdev_cb->cb == cb && egdev_cb->cb_priv == cb_priv) + return egdev_cb; + return NULL; +} + +static int tcf_action_egdev_cb_call(struct tcf_action_egdev *egdev, + enum tc_setup_type type, + void *type_data, bool err_stop) +{ + struct tcf_action_egdev_cb *egdev_cb; + int ok_count = 0; + int err; + + list_for_each_entry(egdev_cb, &egdev->cb_list, list) { + err = egdev_cb->cb(type, type_data, egdev_cb->cb_priv); + if (err) { + if (err_stop) + return err; + } else { + ok_count++; + } + } + return ok_count; +} + +static int tcf_action_egdev_cb_add(struct tcf_action_egdev *egdev, + tc_setup_cb_t *cb, void *cb_priv) +{ + struct tcf_action_egdev_cb *egdev_cb; + + egdev_cb = tcf_action_egdev_cb_lookup(egdev, cb, cb_priv); + if (WARN_ON(egdev_cb)) + return -EEXIST; + egdev_cb = kzalloc(sizeof(*egdev_cb), GFP_KERNEL); + if (!egdev_cb) + return -ENOMEM; + egdev_cb->cb = cb; + egdev_cb->cb_priv = cb_priv; + list_add(&egdev_cb->list, &egdev->cb_list); + return 0; +} + +static void tcf_action_egdev_cb_del(struct tcf_action_egdev *egdev, + tc_setup_cb_t *cb, void *cb_priv) +{ + struct tcf_action_egdev_cb *egdev_cb; + + egdev_cb = tcf_action_egdev_cb_lookup(egdev, cb, cb_priv); + if (WARN_ON(!egdev_cb)) + return; + list_del(&egdev_cb->list); + kfree(egdev_cb); +} + +static int __tc_setup_cb_egdev_register(const struct net_device *dev, + tc_setup_cb_t *cb, void *cb_priv) +{ + struct tcf_action_egdev *egdev = tcf_action_egdev_get(dev); + int err; + + if (!egdev) + return -ENOMEM; + err = tcf_action_egdev_cb_add(egdev, cb, cb_priv); + if (err) + goto err_cb_add; + return 0; + +err_cb_add: + tcf_action_egdev_put(egdev); + return err; +} +int tc_setup_cb_egdev_register(const struct net_device *dev, + tc_setup_cb_t *cb, void *cb_priv) +{ + int err; + + rtnl_lock(); + err = __tc_setup_cb_egdev_register(dev, cb, cb_priv); + rtnl_unlock(); + return err; +} +EXPORT_SYMBOL_GPL(tc_setup_cb_egdev_register); + +static void __tc_setup_cb_egdev_unregister(const struct net_device *dev, + tc_setup_cb_t *cb, void *cb_priv) +{ + struct tcf_action_egdev *egdev = tcf_action_egdev_lookup(dev); + + if (WARN_ON(!egdev)) + return; + tcf_action_egdev_cb_del(egdev, cb, cb_priv); + tcf_action_egdev_put(egdev); +} +void tc_setup_cb_egdev_unregister(const struct net_device *dev, + tc_setup_cb_t *cb, void *cb_priv) +{ + rtnl_lock(); + __tc_setup_cb_egdev_unregister(dev, cb, cb_priv); + rtnl_unlock(); +} +EXPORT_SYMBOL_GPL(tc_setup_cb_egdev_unregister); + +int tc_setup_cb_egdev_call(const struct net_device *dev, + enum tc_setup_type type, void *type_data, + bool err_stop) +{ + struct tcf_action_egdev *egdev = tcf_action_egdev_lookup(dev); + + if (!egdev) + return 0; + return tcf_action_egdev_cb_call(egdev, type, type_data, err_stop); +} +EXPORT_SYMBOL_GPL(tc_setup_cb_egdev_call); + +static __net_init int tcf_action_net_init(struct net *net) +{ + struct tcf_action_net *tan = net_generic(net, tcf_action_net_id); + + return rhashtable_init(&tan->egdev_ht, &tcf_action_egdev_ht_params); +} + +static void __net_exit tcf_action_net_exit(struct net *net) +{ + struct tcf_action_net *tan = net_generic(net, tcf_action_net_id); + + rhashtable_destroy(&tan->egdev_ht); +} + +static struct pernet_operations tcf_action_net_ops = { + .init = tcf_action_net_init, + .exit = tcf_action_net_exit, + .id = &tcf_action_net_id, + .size = sizeof(struct tcf_action_net), +}; + static int __init tc_action_init(void) { + int err; + + err = register_pernet_subsys(&tcf_action_net_ops); + if (err) + return err; + rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, 0); rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, 0); rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action, diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 450873b0c4b9..99f9432f63cf 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1026,6 +1026,36 @@ int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, } EXPORT_SYMBOL(tcf_exts_get_dev); +int tcf_exts_egdev_cb_call(struct tcf_exts *exts, enum tc_setup_type type, + void *type_data, bool err_stop) +{ + int ok_count = 0; +#ifdef CONFIG_NET_CLS_ACT + const struct tc_action *a; + struct net_device *dev; + LIST_HEAD(actions); + int ret; + + if (!tcf_exts_has_actions(exts)) + return 0; + + tcf_exts_to_list(exts, &actions); + list_for_each_entry(a, &actions, list) { + if (!a->ops->get_dev) + continue; + dev = a->ops->get_dev(a); + if (!dev || !tc_can_offload(dev)) + continue; + ret = tc_setup_cb_egdev_call(dev, type, type_data, err_stop); + if (ret < 0) + return ret; + ok_count += ret; + } +#endif + return ok_count; +} +EXPORT_SYMBOL(tcf_exts_egdev_cb_call); + static int __init tc_filter_init(void) { rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, 0); -- cgit v1.2.3-59-g8ed1b From 717503b9cf57c0bb7ea4d3a9f5699c9a04adf988 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 11 Oct 2017 09:41:09 +0200 Subject: net: sched: convert cls_flower->egress_dev users to tc_setup_cb_egdev infra The only user of cls_flower->egress_dev is mlx5. So do the conversion there alongside with the code originating the call in cls_flower function fl_hw_replace_filter to the newly introduced egress device callback infrastucture. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 3 ++ drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 +- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 31 +++++++---- include/net/pkt_cls.h | 5 +- net/sched/cls_api.c | 13 +++-- net/sched/cls_flower.c | 63 ++++++++++++----------- 6 files changed, 73 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index cc13d3dbd366..5ec6d3e8dc89 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -1081,6 +1081,9 @@ int mlx5e_ethtool_get_ts_info(struct mlx5e_priv *priv, int mlx5e_ethtool_flash_device(struct mlx5e_priv *priv, struct ethtool_flash *flash); +int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data); + /* mlx5e generic netdev management API */ struct net_device* mlx5e_create_netdev(struct mlx5_core_dev *mdev, const struct mlx5e_profile *profile, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index cc11bbbd0309..2a32102e7648 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3108,8 +3108,8 @@ static int mlx5e_setup_tc_cls_flower(struct net_device *dev, } #endif -static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, - void *type_data) +int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) { switch (type) { #ifdef CONFIG_MLX5_ESWITCH diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 45e03c427faf..765fc74fbb1b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -667,14 +668,6 @@ mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, cls_flower->common.chain_index) return -EOPNOTSUPP; - if (cls_flower->egress_dev) { - struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - - dev = mlx5_eswitch_get_uplink_netdev(esw); - return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, - cls_flower); - } - switch (cls_flower->command) { case TC_CLSFLOWER_REPLACE: return mlx5e_configure_flower(priv, cls_flower); @@ -698,6 +691,14 @@ static int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, } } +static int mlx5e_rep_setup_tc_cb(enum tc_setup_type type, void *type_data, + void *cb_priv) +{ + struct net_device *dev = cb_priv; + + return mlx5e_setup_tc(dev, type, type_data); +} + bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; @@ -1017,15 +1018,24 @@ mlx5e_vport_rep_load(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) goto err_detach_netdev; } + err = tc_setup_cb_egdev_register(netdev, mlx5e_rep_setup_tc_cb, + mlx5_eswitch_get_uplink_netdev(esw)); + if (err) + goto err_neigh_cleanup; + err = register_netdev(netdev); if (err) { pr_warn("Failed to register representor netdev for vport %d\n", rep->vport); - goto err_neigh_cleanup; + goto err_egdev_cleanup; } return 0; +err_egdev_cleanup: + tc_setup_cb_egdev_unregister(netdev, mlx5e_rep_setup_tc_cb, + mlx5_eswitch_get_uplink_netdev(esw)); + err_neigh_cleanup: mlx5e_rep_neigh_cleanup(rpriv); @@ -1047,7 +1057,8 @@ mlx5e_vport_rep_unload(struct mlx5_eswitch *esw, struct mlx5_eswitch_rep *rep) void *ppriv = priv->ppriv; unregister_netdev(rep->netdev); - + tc_setup_cb_egdev_unregister(netdev, mlx5e_rep_setup_tc_cb, + mlx5_eswitch_get_uplink_netdev(esw)); mlx5e_rep_neigh_cleanup(rpriv); mlx5e_detach_netdev(priv); mlx5e_destroy_netdev(priv); diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 6f8149c82571..c0bdf5cad727 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -206,8 +206,6 @@ int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts); int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts); int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, struct net_device **hw_dev); -int tcf_exts_egdev_cb_call(struct tcf_exts *exts, enum tc_setup_type type, - void *type_data, bool err_stop); /** * struct tcf_pkt_info - packet information @@ -407,6 +405,9 @@ tcf_match_indev(struct sk_buff *skb, int ifindex) } #endif /* CONFIG_NET_CLS_IND */ +int tc_setup_cb_call(struct tcf_exts *exts, enum tc_setup_type type, + void *type_data, bool err_stop); + struct tc_cls_common_offload { u32 chain_index; __be16 protocol; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 99f9432f63cf..51994a202585 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1026,8 +1026,9 @@ int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, } EXPORT_SYMBOL(tcf_exts_get_dev); -int tcf_exts_egdev_cb_call(struct tcf_exts *exts, enum tc_setup_type type, - void *type_data, bool err_stop) +static int tc_exts_setup_cb_egdev_call(struct tcf_exts *exts, + enum tc_setup_type type, + void *type_data, bool err_stop) { int ok_count = 0; #ifdef CONFIG_NET_CLS_ACT @@ -1054,7 +1055,13 @@ int tcf_exts_egdev_cb_call(struct tcf_exts *exts, enum tc_setup_type type, #endif return ok_count; } -EXPORT_SYMBOL(tcf_exts_egdev_cb_call); + +int tc_setup_cb_call(struct tcf_exts *exts, enum tc_setup_type type, + void *type_data, bool err_stop) +{ + return tc_exts_setup_cb_egdev_call(exts, type, type_data, err_stop); +} +EXPORT_SYMBOL(tc_setup_cb_call); static int __init tc_filter_init(void) { diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index db831ac708f6..5b7bb968d1d4 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -88,7 +88,6 @@ struct cls_fl_filter { u32 handle; u32 flags; struct rcu_head rcu; - struct net_device *hw_dev; }; static unsigned short int fl_mask_range(const struct fl_flow_mask *mask) @@ -201,16 +200,17 @@ static void fl_destroy_filter(struct rcu_head *head) static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) { struct tc_cls_flower_offload cls_flower = {}; - struct net_device *dev = f->hw_dev; - - if (!tc_can_offload(dev)) - return; + struct net_device *dev = tp->q->dev_queue->dev; tc_cls_common_offload_init(&cls_flower.common, tp); cls_flower.command = TC_CLSFLOWER_DESTROY; cls_flower.cookie = (unsigned long) f; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, &cls_flower); + if (tc_can_offload(dev)) + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, + &cls_flower); + tc_setup_cb_call(&f->exts, TC_SETUP_CLSFLOWER, + &cls_flower, false); } static int fl_hw_replace_filter(struct tcf_proto *tp, @@ -220,20 +220,9 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, { struct net_device *dev = tp->q->dev_queue->dev; struct tc_cls_flower_offload cls_flower = {}; + bool skip_sw = tc_skip_sw(f->flags); int err; - if (!tc_can_offload(dev)) { - if (tcf_exts_get_dev(dev, &f->exts, &f->hw_dev) || - (f->hw_dev && !tc_can_offload(f->hw_dev))) { - f->hw_dev = dev; - return tc_skip_sw(f->flags) ? -EINVAL : 0; - } - dev = f->hw_dev; - cls_flower.egress_dev = true; - } else { - f->hw_dev = dev; - } - tc_cls_common_offload_init(&cls_flower.common, tp); cls_flower.command = TC_CLSFLOWER_REPLACE; cls_flower.cookie = (unsigned long) f; @@ -242,31 +231,47 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, cls_flower.key = &f->mkey; cls_flower.exts = &f->exts; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, - &cls_flower); - if (!err) - f->flags |= TCA_CLS_FLAGS_IN_HW; + if (tc_can_offload(dev)) { + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, + &cls_flower); + if (err) { + if (skip_sw) + return err; + } else { + f->flags |= TCA_CLS_FLAGS_IN_HW; + } + } - if (tc_skip_sw(f->flags)) + err = tc_setup_cb_call(&f->exts, TC_SETUP_CLSFLOWER, + &cls_flower, skip_sw); + if (err < 0) { + fl_hw_destroy_filter(tp, f); return err; + } else if (err > 0) { + f->flags |= TCA_CLS_FLAGS_IN_HW; + } + + if (skip_sw && !(f->flags & TCA_CLS_FLAGS_IN_HW)) + return -EINVAL; + return 0; } static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) { struct tc_cls_flower_offload cls_flower = {}; - struct net_device *dev = f->hw_dev; - - if (!tc_can_offload(dev)) - return; + struct net_device *dev = tp->q->dev_queue->dev; tc_cls_common_offload_init(&cls_flower.common, tp); cls_flower.command = TC_CLSFLOWER_STATS; cls_flower.cookie = (unsigned long) f; cls_flower.exts = &f->exts; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, - &cls_flower); + if (tc_can_offload(dev)) + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, + &cls_flower); + tc_setup_cb_call(&f->exts, TC_SETUP_CLSFLOWER, + &cls_flower, false); } static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) -- cgit v1.2.3-59-g8ed1b From 7578d7b45ed870b13a8ace57e32feaed623c2a94 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 11 Oct 2017 09:41:10 +0200 Subject: net: sched: remove unused tcf_exts_get_dev helper and cls_flower->egress_dev The helper and the struct field ares no longer used by any code, so remove them. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 3 --- net/sched/cls_api.c | 22 ---------------------- 2 files changed, 25 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index c0bdf5cad727..f5263743076b 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -204,8 +204,6 @@ void tcf_exts_destroy(struct tcf_exts *exts); void tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src); int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts); int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts); -int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, - struct net_device **hw_dev); /** * struct tcf_pkt_info - packet information @@ -517,7 +515,6 @@ struct tc_cls_flower_offload { struct fl_flow_key *mask; struct fl_flow_key *key; struct tcf_exts *exts; - bool egress_dev; }; enum tc_matchall_command { diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 51994a202585..2977b8a90851 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1004,28 +1004,6 @@ int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts) } EXPORT_SYMBOL(tcf_exts_dump_stats); -int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, - struct net_device **hw_dev) -{ -#ifdef CONFIG_NET_CLS_ACT - const struct tc_action *a; - LIST_HEAD(actions); - - if (!tcf_exts_has_actions(exts)) - return -EINVAL; - - tcf_exts_to_list(exts, &actions); - list_for_each_entry(a, &actions, list) { - if (a->ops->get_dev) - *hw_dev = a->ops->get_dev(a); - } - if (*hw_dev) - return 0; -#endif - return -EOPNOTSUPP; -} -EXPORT_SYMBOL(tcf_exts_get_dev); - static int tc_exts_setup_cb_egdev_call(struct tcf_exts *exts, enum tc_setup_type type, void *type_data, bool err_stop) -- cgit v1.2.3-59-g8ed1b From 4e659dbe2d02a56ca0df25c77e099760252a329c Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Mon, 14 Aug 2017 15:46:17 -0700 Subject: firmware: qcom: scm: Expose secure IO service The secure IO service provides operations for reading and writing secure memory from non-secure mode, expose this API through SCM. Reviewed-by: Stephen Boyd Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/firmware/qcom_scm-32.c | 18 ++++++++++++++++++ drivers/firmware/qcom_scm-64.c | 31 +++++++++++++++++++++++++++++++ drivers/firmware/qcom_scm.c | 12 ++++++++++++ drivers/firmware/qcom_scm.h | 6 ++++++ include/linux/qcom_scm.h | 4 ++++ 5 files changed, 71 insertions(+) (limited to 'include') diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 93e3b96b6dfa..11fdb1584823 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -596,3 +596,21 @@ int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, { return -ENODEV; } + +int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, + unsigned int *val) +{ + int ret; + + ret = qcom_scm_call_atomic1(QCOM_SCM_SVC_IO, QCOM_SCM_IO_READ, addr); + if (ret >= 0) + *val = ret; + + return ret < 0 ? ret : 0; +} + +int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) +{ + return qcom_scm_call_atomic2(QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE, + addr, val); +} diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 6e6d561708e2..bf50fb59852e 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -439,3 +439,34 @@ int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, return ret; } + +int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, + unsigned int *val) +{ + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + int ret; + + desc.args[0] = addr; + desc.arginfo = QCOM_SCM_ARGS(1); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_IO, QCOM_SCM_IO_READ, + &desc, &res); + if (ret >= 0) + *val = res.a1; + + return ret < 0 ? ret : 0; +} + +int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val) +{ + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = addr; + desc.args[1] = val; + desc.arginfo = QCOM_SCM_ARGS(2); + + return qcom_scm_call(dev, QCOM_SCM_SVC_IO, QCOM_SCM_IO_WRITE, + &desc, &res); +} diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index bb16510d75ba..e18d63935648 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -333,6 +333,18 @@ int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) } EXPORT_SYMBOL(qcom_scm_iommu_secure_ptbl_init); +int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) +{ + return __qcom_scm_io_readl(__scm->dev, addr, val); +} +EXPORT_SYMBOL(qcom_scm_io_readl); + +int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) +{ + return __qcom_scm_io_writel(__scm->dev, addr, val); +} +EXPORT_SYMBOL(qcom_scm_io_writel); + /** * qcom_scm_is_available() - Checks if SCM is available */ diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 9bea691f30fb..a60e4b9b1394 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -30,6 +30,12 @@ extern int __qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); #define QCOM_SCM_CMD_CORE_HOTPLUGGED 0x10 extern void __qcom_scm_cpu_power_down(u32 flags); +#define QCOM_SCM_SVC_IO 0x5 +#define QCOM_SCM_IO_READ 0x1 +#define QCOM_SCM_IO_WRITE 0x2 +extern int __qcom_scm_io_readl(struct device *dev, phys_addr_t addr, unsigned int *val); +extern int __qcom_scm_io_writel(struct device *dev, phys_addr_t addr, unsigned int val); + #define QCOM_SCM_SVC_INFO 0x6 #define QCOM_IS_CALL_AVAIL_CMD 0x1 extern int __qcom_scm_is_call_available(struct device *dev, u32 svc_id, diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index e5380471c2cd..e8357f570695 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -43,6 +43,8 @@ extern int qcom_scm_set_remote_state(u32 state, u32 id); extern int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare); extern int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size); extern int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare); +extern int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val); +extern int qcom_scm_io_writel(phys_addr_t addr, unsigned int val); #else static inline int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) @@ -73,5 +75,7 @@ qcom_scm_set_remote_state(u32 state,u32 id) { return -ENODEV; } static inline int qcom_scm_restore_sec_cfg(u32 device_id, u32 spare) { return -ENODEV; } static inline int qcom_scm_iommu_secure_ptbl_size(u32 spare, size_t *size) { return -ENODEV; } static inline int qcom_scm_iommu_secure_ptbl_init(u64 addr, u32 size, u32 spare) { return -ENODEV; } +static inline int qcom_scm_io_readl(phys_addr_t addr, unsigned int *val) { return -ENODEV; } +static inline int qcom_scm_io_writel(phys_addr_t addr, unsigned int val) { return -ENODEV; } #endif #endif -- cgit v1.2.3-59-g8ed1b From 538d5b333216c3daa7a5821307164f10af73ec8c Mon Sep 17 00:00:00 2001 From: Tomasz Nowicki Date: Wed, 20 Sep 2017 10:52:02 +0200 Subject: iommu/iova: Make rcache flush optional on IOVA allocation failure Since IOVA allocation failure is not unusual case we need to flush CPUs' rcache in hope we will succeed in next round. However, it is useful to decide whether we need rcache flush step because of two reasons: - Not scalability. On large system with ~100 CPUs iterating and flushing rcache for each CPU becomes serious bottleneck so we may want to defer it. - free_cpu_cached_iovas() does not care about max PFN we are interested in. Thus we may flush our rcaches and still get no new IOVA like in the commonly used scenario: if (dma_limit > DMA_BIT_MASK(32) && dev_is_pci(dev)) iova = alloc_iova_fast(iovad, iova_len, DMA_BIT_MASK(32) >> shift); if (!iova) iova = alloc_iova_fast(iovad, iova_len, dma_limit >> shift); 1. First alloc_iova_fast() call is limited to DMA_BIT_MASK(32) to get PCI devices a SAC address 2. alloc_iova() fails due to full 32-bit space 3. rcaches contain PFNs out of 32-bit space so free_cpu_cached_iovas() throws entries away for nothing and alloc_iova() fails again 4. Next alloc_iova_fast() call cannot take advantage of rcache since we have just defeated caches. In this case we pick the slowest option to proceed. This patch reworks flushed_rcache local flag to be additional function argument instead and control rcache flush step. Also, it updates all users to do the flush as the last chance. Signed-off-by: Tomasz Nowicki Reviewed-by: Robin Murphy Tested-by: Nate Watterson Signed-off-by: Joerg Roedel --- drivers/iommu/amd_iommu.c | 5 +++-- drivers/iommu/dma-iommu.c | 6 ++++-- drivers/iommu/intel-iommu.c | 5 +++-- drivers/iommu/iova.c | 11 ++++++----- include/linux/iova.h | 5 +++-- 5 files changed, 19 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 647ab7691aee..3d64c844d8b1 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -1546,10 +1546,11 @@ static unsigned long dma_ops_alloc_iova(struct device *dev, if (dma_mask > DMA_BIT_MASK(32)) pfn = alloc_iova_fast(&dma_dom->iovad, pages, - IOVA_PFN(DMA_BIT_MASK(32))); + IOVA_PFN(DMA_BIT_MASK(32)), false); if (!pfn) - pfn = alloc_iova_fast(&dma_dom->iovad, pages, IOVA_PFN(dma_mask)); + pfn = alloc_iova_fast(&dma_dom->iovad, pages, + IOVA_PFN(dma_mask), true); return (pfn << PAGE_SHIFT); } diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 191be9c80a8a..25914d36c5ac 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -370,10 +370,12 @@ static dma_addr_t iommu_dma_alloc_iova(struct iommu_domain *domain, /* Try to get PCI devices a SAC address */ if (dma_limit > DMA_BIT_MASK(32) && dev_is_pci(dev)) - iova = alloc_iova_fast(iovad, iova_len, DMA_BIT_MASK(32) >> shift); + iova = alloc_iova_fast(iovad, iova_len, + DMA_BIT_MASK(32) >> shift, false); if (!iova) - iova = alloc_iova_fast(iovad, iova_len, dma_limit >> shift); + iova = alloc_iova_fast(iovad, iova_len, dma_limit >> shift, + true); return (dma_addr_t)iova << shift; } diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index ebb48353dd39..b3914fce8254 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3469,11 +3469,12 @@ static unsigned long intel_alloc_iova(struct device *dev, * from higher range */ iova_pfn = alloc_iova_fast(&domain->iovad, nrpages, - IOVA_PFN(DMA_BIT_MASK(32))); + IOVA_PFN(DMA_BIT_MASK(32)), false); if (iova_pfn) return iova_pfn; } - iova_pfn = alloc_iova_fast(&domain->iovad, nrpages, IOVA_PFN(dma_mask)); + iova_pfn = alloc_iova_fast(&domain->iovad, nrpages, + IOVA_PFN(dma_mask), true); if (unlikely(!iova_pfn)) { pr_err("Allocating %ld-page iova for %s failed", nrpages, dev_name(dev)); diff --git a/drivers/iommu/iova.c b/drivers/iommu/iova.c index 3aee64b99df1..84bda3a4dafc 100644 --- a/drivers/iommu/iova.c +++ b/drivers/iommu/iova.c @@ -395,14 +395,15 @@ EXPORT_SYMBOL_GPL(free_iova); * @iovad: - iova domain in question * @size: - size of page frames to allocate * @limit_pfn: - max limit address + * @flush_rcache: - set to flush rcache on regular allocation failure * This function tries to satisfy an iova allocation from the rcache, - * and falls back to regular allocation on failure. + * and falls back to regular allocation on failure. If regular allocation + * fails too and the flush_rcache flag is set then the rcache will be flushed. */ unsigned long alloc_iova_fast(struct iova_domain *iovad, unsigned long size, - unsigned long limit_pfn) + unsigned long limit_pfn, bool flush_rcache) { - bool flushed_rcache = false; unsigned long iova_pfn; struct iova *new_iova; @@ -415,11 +416,11 @@ retry: if (!new_iova) { unsigned int cpu; - if (flushed_rcache) + if (!flush_rcache) return 0; /* Try replenishing IOVAs by flushing rcache. */ - flushed_rcache = true; + flush_rcache = false; for_each_online_cpu(cpu) free_cpu_cached_iovas(cpu, iovad); goto retry; diff --git a/include/linux/iova.h b/include/linux/iova.h index c696ee81054e..928442dda565 100644 --- a/include/linux/iova.h +++ b/include/linux/iova.h @@ -150,7 +150,7 @@ void queue_iova(struct iova_domain *iovad, unsigned long pfn, unsigned long pages, unsigned long data); unsigned long alloc_iova_fast(struct iova_domain *iovad, unsigned long size, - unsigned long limit_pfn); + unsigned long limit_pfn, bool flush_rcache); struct iova *reserve_iova(struct iova_domain *iovad, unsigned long pfn_lo, unsigned long pfn_hi); void copy_reserved_iova(struct iova_domain *from, struct iova_domain *to); @@ -212,7 +212,8 @@ static inline void queue_iova(struct iova_domain *iovad, static inline unsigned long alloc_iova_fast(struct iova_domain *iovad, unsigned long size, - unsigned long limit_pfn) + unsigned long limit_pfn, + bool flush_rcache) { return 0; } -- cgit v1.2.3-59-g8ed1b From 24d654fadd70884a719c45ffabb0fe6033996f92 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 23 Sep 2017 12:08:16 +0200 Subject: kfifo: Fix comments Fix some typo. Signed-off-by: Christophe JAILLET Signed-off-by: Jiri Kosina --- include/linux/kfifo.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h index 41eb6fdf87a8..7b45959ebd92 100644 --- a/include/linux/kfifo.h +++ b/include/linux/kfifo.h @@ -325,7 +325,7 @@ __kfifo_uint_must_check_helper( \ * * This macro dynamically allocates a new fifo buffer. * - * The numer of elements will be rounded-up to a power of 2. + * The number of elements will be rounded-up to a power of 2. * The fifo will be release with kfifo_free(). * Return 0 if no error, otherwise an error code. */ @@ -358,9 +358,9 @@ __kfifo_int_must_check_helper( \ * @buffer: the preallocated buffer to be used * @size: the size of the internal buffer, this have to be a power of 2 * - * This macro initialize a fifo using a preallocated buffer. + * This macro initializes a fifo using a preallocated buffer. * - * The numer of elements will be rounded-up to a power of 2. + * The number of elements will be rounded-up to a power of 2. * Return 0 if no error, otherwise an error code. */ #define kfifo_init(fifo, buffer, size) \ -- cgit v1.2.3-59-g8ed1b From 6e97e08d4311d2413df0e891a10301bf4258fe3e Mon Sep 17 00:00:00 2001 From: Tudor-Dan Ambarus Date: Fri, 29 Sep 2017 12:13:08 +0300 Subject: crypto: ecdh - return unsigned value for crypto_ecdh_key_len() ECDH_KPP_SECRET_MIN_SIZE and params->key_size are both returning unsigned values. Signed-off-by: Tudor Ambarus Signed-off-by: Herbert Xu --- crypto/ecdh_helper.c | 2 +- include/crypto/ecdh.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/crypto/ecdh_helper.c b/crypto/ecdh_helper.c index f05bea5fd257..d3af8e8b0b5e 100644 --- a/crypto/ecdh_helper.c +++ b/crypto/ecdh_helper.c @@ -28,7 +28,7 @@ static inline const u8 *ecdh_unpack_data(void *dst, const void *src, size_t sz) return src + sz; } -int crypto_ecdh_key_len(const struct ecdh *params) +unsigned int crypto_ecdh_key_len(const struct ecdh *params) { return ECDH_KPP_SECRET_MIN_SIZE + params->key_size; } diff --git a/include/crypto/ecdh.h b/include/crypto/ecdh.h index 1aff2a8a3a68..d696317c43a8 100644 --- a/include/crypto/ecdh.h +++ b/include/crypto/ecdh.h @@ -54,7 +54,7 @@ struct ecdh { * * Return: size of the key in bytes */ -int crypto_ecdh_key_len(const struct ecdh *params); +unsigned int crypto_ecdh_key_len(const struct ecdh *params); /** * crypto_ecdh_encode_key() - encode the private key -- cgit v1.2.3-59-g8ed1b From 5b3f3a8bede7d3af2dc9ea20b0ad971d4e5e83cc Mon Sep 17 00:00:00 2001 From: Tudor-Dan Ambarus Date: Fri, 29 Sep 2017 12:21:05 +0300 Subject: crypto: dh - return unsigned value for crypto_dh_key_len() DH_KPP_SECRET_MIN_SIZE and dh_data_size() are both returning unsigned values. Signed-off-by: Tudor Ambarus Signed-off-by: Herbert Xu --- crypto/dh_helper.c | 2 +- include/crypto/dh.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/crypto/dh_helper.c b/crypto/dh_helper.c index 69869dad344a..a413b311e568 100644 --- a/crypto/dh_helper.c +++ b/crypto/dh_helper.c @@ -33,7 +33,7 @@ static inline unsigned int dh_data_size(const struct dh *p) return p->key_size + p->p_size + p->g_size; } -int crypto_dh_key_len(const struct dh *p) +unsigned int crypto_dh_key_len(const struct dh *p) { return DH_KPP_SECRET_MIN_SIZE + dh_data_size(p); } diff --git a/include/crypto/dh.h b/include/crypto/dh.h index f638998fb6d0..71e1bb24d79f 100644 --- a/include/crypto/dh.h +++ b/include/crypto/dh.h @@ -53,7 +53,7 @@ struct dh { * * Return: size of the key in bytes */ -int crypto_dh_key_len(const struct dh *params); +unsigned int crypto_dh_key_len(const struct dh *params); /** * crypto_dh_encode_key() - encode the private key -- cgit v1.2.3-59-g8ed1b From 437d2762ba07f0fc639d5a09acb323fe4106a61f Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 11 Oct 2017 20:45:40 -0700 Subject: tcp: remove obsolete helpers Remove three inline helpers that are no longer needed. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 15163454174b..3b3b9b968e2d 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1629,18 +1629,6 @@ static inline struct sk_buff *tcp_write_queue_tail(const struct sock *sk) return skb_peek_tail(&sk->sk_write_queue); } -static inline struct sk_buff *tcp_write_queue_next(const struct sock *sk, - const struct sk_buff *skb) -{ - return skb_queue_next(&sk->sk_write_queue, skb); -} - -static inline struct sk_buff *tcp_write_queue_prev(const struct sock *sk, - const struct sk_buff *skb) -{ - return skb_queue_prev(&sk->sk_write_queue, skb); -} - #define tcp_for_write_queue_from_safe(skb, tmp, sk) \ skb_queue_walk_from_safe(&(sk)->sk_write_queue, skb, tmp) @@ -1697,11 +1685,6 @@ static inline void tcp_add_write_queue_tail(struct sock *sk, struct sk_buff *skb } } -static inline void __tcp_add_write_queue_head(struct sock *sk, struct sk_buff *skb) -{ - __skb_queue_head(&sk->sk_write_queue, skb); -} - /* Insert new before skb on the write queue of sk. */ static inline void tcp_insert_write_queue_before(struct sk_buff *new, struct sk_buff *skb, -- cgit v1.2.3-59-g8ed1b From 7f66721a7d5bf6251f9c49aada9200c61ddcecc5 Mon Sep 17 00:00:00 2001 From: Rakesh Pandit Date: Thu, 12 Oct 2017 19:58:10 +0300 Subject: fs/block_dev: remove vfs_msg() interface Replaced by pr_err usage in commit ef51042472f5 ("block, dax: move "select DAX" from BLOCK to FS_DAX") Signed-off-by: Rakesh Pandit Acked-by: Ross Zwisler Signed-off-by: Jens Axboe --- fs/block_dev.c | 12 ------------ include/linux/blkdev.h | 11 ----------- 2 files changed, 23 deletions(-) (limited to 'include') diff --git a/fs/block_dev.c b/fs/block_dev.c index 93d088ffc05c..07ddccd17801 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -54,18 +54,6 @@ struct block_device *I_BDEV(struct inode *inode) } EXPORT_SYMBOL(I_BDEV); -void __vfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - va_start(args, fmt); - vaf.fmt = fmt; - vaf.va = &args; - printk_ratelimited("%sVFS (%s): %pV\n", prefix, sb->s_id, &vaf); - va_end(args); -} - static void bdev_write_inode(struct block_device *bdev) { struct inode *inode = bdev->bd_inode; diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 9fb71fc7d0e8..72637028f3c9 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -917,17 +917,6 @@ static inline void rq_flush_dcache_pages(struct request *rq) } #endif -#ifdef CONFIG_PRINTK -#define vfs_msg(sb, level, fmt, ...) \ - __vfs_msg(sb, level, fmt, ##__VA_ARGS__) -#else -#define vfs_msg(sb, level, fmt, ...) \ -do { \ - no_printk(fmt, ##__VA_ARGS__); \ - __vfs_msg(sb, "", " "); \ -} while (0) -#endif - extern int blk_register_queue(struct gendisk *disk); extern void blk_unregister_queue(struct gendisk *disk); extern blk_qc_t generic_make_request(struct bio *bio); -- cgit v1.2.3-59-g8ed1b From 60724d4bae14cd295b27b1610cad9a2720eb0860 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 11 Oct 2017 10:57:48 -0700 Subject: net: dsa: Add support for DSA specific notifiers In preparation for communicating a given DSA network device's port number and switch index, create a specialized DSA notifier and two events: DSA_PORT_REGISTER and DSA_PORT_UNREGISTER that communicate: the slave network device (slave_dev), port number and switch number in the tree. This will be later used for network device drivers like bcmsysport which needs to cooperate with its DSA network devices to set-up queue mapping and scheduling. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ net/dsa/dsa.c | 23 +++++++++++++++++++++++ net/dsa/slave.c | 13 +++++++++++++ 3 files changed, 81 insertions(+) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 10dceccd9ce8..40a709a0754d 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -471,4 +471,49 @@ static inline int dsa_switch_resume(struct dsa_switch *ds) } #endif /* CONFIG_PM_SLEEP */ +enum dsa_notifier_type { + DSA_PORT_REGISTER, + DSA_PORT_UNREGISTER, +}; + +struct dsa_notifier_info { + struct net_device *dev; +}; + +struct dsa_notifier_register_info { + struct dsa_notifier_info info; /* must be first */ + struct net_device *master; + unsigned int port_number; + unsigned int switch_number; +}; + +static inline struct net_device * +dsa_notifier_info_to_dev(const struct dsa_notifier_info *info) +{ + return info->dev; +} + +#if IS_ENABLED(CONFIG_NET_DSA) +int register_dsa_notifier(struct notifier_block *nb); +int unregister_dsa_notifier(struct notifier_block *nb); +int call_dsa_notifiers(unsigned long val, struct net_device *dev, + struct dsa_notifier_info *info); +#else +static inline int register_dsa_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline int unregister_dsa_notifier(struct notifier_block *nb) +{ + return 0; +} + +static inline int call_dsa_notifiers(unsigned long val, struct net_device *dev, + struct dsa_notifier_info *info) +{ + return NOTIFY_DONE; +} +#endif + #endif diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 51ca2a524a27..832c659ff993 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -261,6 +262,28 @@ bool dsa_schedule_work(struct work_struct *work) return queue_work(dsa_owq, work); } +static ATOMIC_NOTIFIER_HEAD(dsa_notif_chain); + +int register_dsa_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&dsa_notif_chain, nb); +} +EXPORT_SYMBOL_GPL(register_dsa_notifier); + +int unregister_dsa_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&dsa_notif_chain, nb); +} +EXPORT_SYMBOL_GPL(unregister_dsa_notifier); + +int call_dsa_notifiers(unsigned long val, struct net_device *dev, + struct dsa_notifier_info *info) +{ + info->dev = dev; + return atomic_notifier_call_chain(&dsa_notif_chain, val, info); +} +EXPORT_SYMBOL_GPL(call_dsa_notifiers); + static int __init dsa_init_module(void) { int rc; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index fb2954ff198c..45f4ea845c07 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1116,6 +1116,7 @@ int dsa_slave_resume(struct net_device *slave_dev) int dsa_slave_create(struct dsa_port *port, const char *name) { + struct dsa_notifier_register_info rinfo = { }; struct dsa_switch *ds = port->ds; struct net_device *master; struct net_device *slave_dev; @@ -1177,6 +1178,12 @@ int dsa_slave_create(struct dsa_port *port, const char *name) goto out_free; } + rinfo.info.dev = slave_dev; + rinfo.master = master; + rinfo.port_number = p->dp->index; + rinfo.switch_number = p->dp->ds->index; + call_dsa_notifiers(DSA_PORT_REGISTER, slave_dev, &rinfo.info); + ret = register_netdev(slave_dev); if (ret) { netdev_err(master, "error %d registering interface %s\n", @@ -1200,6 +1207,7 @@ out_free: void dsa_slave_destroy(struct net_device *slave_dev) { struct dsa_slave_priv *p = netdev_priv(slave_dev); + struct dsa_notifier_register_info rinfo = { }; struct device_node *port_dn; port_dn = p->dp->dn; @@ -1211,6 +1219,11 @@ void dsa_slave_destroy(struct net_device *slave_dev) if (of_phy_is_fixed_link(port_dn)) of_phy_deregister_fixed_link(port_dn); } + rinfo.info.dev = slave_dev; + rinfo.master = p->dp->cpu_dp->netdev; + rinfo.port_number = p->dp->index; + rinfo.switch_number = p->dp->ds->index; + call_dsa_notifiers(DSA_PORT_UNREGISTER, slave_dev, &rinfo.info); unregister_netdev(slave_dev); free_percpu(p->stats64); free_netdev(slave_dev); -- cgit v1.2.3-59-g8ed1b From 0a5f14ce67a6e093e651d3cd75e6ac281123d93a Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 11 Oct 2017 10:57:49 -0700 Subject: net: dsa: tag_brcm: Indicate to master netdevice port + queue We need to tell the DSA master network device doing the actual transmission what the desired switch port and queue number is for it to resolve that to the internal transmit queue it is mapped to. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 5 +++++ net/dsa/tag_brcm.c | 6 ++++++ 2 files changed, 11 insertions(+) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 40a709a0754d..ce1d622734d7 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -516,4 +516,9 @@ static inline int call_dsa_notifiers(unsigned long val, struct net_device *dev, } #endif +/* Broadcom tag specific helpers to insert and extract queue/port number */ +#define BRCM_TAG_SET_PORT_QUEUE(p, q) ((p) << 8 | q) +#define BRCM_TAG_GET_PORT(v) ((v) >> 8) +#define BRCM_TAG_GET_QUEUE(v) ((v) & 0xff) + #endif diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index 8e4bdb9d9ae3..cc4f472fbd77 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -86,6 +86,12 @@ static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev brcm_tag[2] = BRCM_IG_DSTMAP2_MASK; brcm_tag[3] = (1 << p->dp->index) & BRCM_IG_DSTMAP1_MASK; + /* Now tell the master network device about the desired output queue + * as well + */ + skb_set_queue_mapping(skb, BRCM_TAG_SET_PORT_QUEUE(p->dp->index, + queue)); + return skb; } -- cgit v1.2.3-59-g8ed1b From ad2d116c5242875bba27522682ec5ba7f0df75f0 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Wed, 11 Oct 2017 11:14:49 -0700 Subject: sched: tc_mirred: Remove whitespaces This file contains unnecessary whitespaces as newlines, remove them, found by looking at what struct tc_mirred looks like. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/uapi/linux/tc_act/tc_mirred.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/tc_act/tc_mirred.h b/include/uapi/linux/tc_act/tc_mirred.h index 3d7a2b352a62..69038c29e8a9 100644 --- a/include/uapi/linux/tc_act/tc_mirred.h +++ b/include/uapi/linux/tc_act/tc_mirred.h @@ -9,13 +9,13 @@ #define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */ #define TCA_INGRESS_REDIR 3 /* packet redirect to INGRESS*/ #define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */ - + struct tc_mirred { tc_gen; int eaction; /* one of IN/EGRESS_MIRROR/REDIR */ __u32 ifindex; /* ifindex of egress port */ }; - + enum { TCA_MIRRED_UNSPEC, TCA_MIRRED_TM, @@ -24,5 +24,5 @@ enum { __TCA_MIRRED_MAX }; #define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1) - + #endif -- cgit v1.2.3-59-g8ed1b From 67680d3c046450b3901aa4e5a9cf2f8fbd7ed9a2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 11 Oct 2017 17:20:12 +0200 Subject: drm: vblank: use ktime_t instead of timeval The drm vblank handling uses 'timeval' to store timestamps in either monotonic or wall-clock time base. In either case, it reads the current time as a ktime_t in get_drm_timestamp() and converts it from there. This is a bit suspicious, as users of 'timeval' often suffer from the time_t overflow in y2038. I have gone through this code and found that it is unlikely to cause problems here: - The user space ABI does not use time_t or timeval, but uses 'u32' and 'long' as the types. This means at least that rebuilding user programs against a new libc with 64-bit time_t does not change the ABI. - As of commit c61eef726a78 ("drm: add support for monotonic vblank timestamps") in linux-3.8, the monotonic timestamp is the default and can only get reverted to wall-clock through a module-parameter. - With the default monotonic timestamps, there is no problem at all. - The drm_wait_vblank_ioctl() interface is alway safe on 64-bit architectures, on 32-bit it might overflow the 'long' timestamps in 2038 with wall-clock timestamps. - The event handling uses 'u32' seconds, which overflow in 2106 on both 32-bit and 64-bit machines, when wall-clock timestamps are used. - The effect of overflowing either of the two is only temporary (during the overflow, and is likely to keep working again afterwards. It is likely the same problem as observing a 'settimeofday()' call, which was the reason for moving to the monotonic timestamps in the first place. Overall, this seems good enough, so my patch removes the use of 'timeval' from the vblank handling altogether and uses ktime_t consistently, except for the part where we copy the data to user space structures in the existing format. Signed-off-by: Arnd Bergmann Reviewed-by: Sean Paul Reviewed-by: Keith Packard Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_vblank.c | 123 +++++++++++++++++++++++-------------------- include/drm/drm_drv.h | 2 +- include/drm/drm_vblank.h | 6 +-- 3 files changed, 71 insertions(+), 60 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 70f2b9593edc..c605c3ad6b6e 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -78,7 +78,7 @@ static bool drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, - struct timeval *tvblank, bool in_vblank_irq); + ktime_t *tvblank, bool in_vblank_irq); static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ @@ -99,7 +99,7 @@ MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); static void store_vblank(struct drm_device *dev, unsigned int pipe, u32 vblank_count_inc, - struct timeval *t_vblank, u32 last) + ktime_t t_vblank, u32 last) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; @@ -108,7 +108,7 @@ static void store_vblank(struct drm_device *dev, unsigned int pipe, vblank->last = last; write_seqlock(&vblank->seqlock); - vblank->time = *t_vblank; + vblank->time = t_vblank; vblank->count += vblank_count_inc; write_sequnlock(&vblank->seqlock); } @@ -151,7 +151,7 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe { u32 cur_vblank; bool rc; - struct timeval t_vblank; + ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; spin_lock(&dev->vblank_time_lock); @@ -171,13 +171,13 @@ static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid. */ if (!rc) - t_vblank = (struct timeval) {0, 0}; + t_vblank = 0; /* * +1 to make sure user will never see the same * vblank counter value before and after a modeset */ - store_vblank(dev, pipe, 1, &t_vblank, cur_vblank); + store_vblank(dev, pipe, 1, t_vblank, cur_vblank); spin_unlock(&dev->vblank_time_lock); } @@ -200,7 +200,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; u32 cur_vblank, diff; bool rc; - struct timeval t_vblank; + ktime_t t_vblank; int count = DRM_TIMESTAMP_MAXRETRIES; int framedur_ns = vblank->framedur_ns; @@ -225,11 +225,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, /* trust the hw counter when it's around */ diff = (cur_vblank - vblank->last) & dev->max_vblank_count; } else if (rc && framedur_ns) { - const struct timeval *t_old; - u64 diff_ns; - - t_old = &vblank->time; - diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old); + u64 diff_ns = ktime_to_ns(ktime_sub(t_vblank, vblank->time)); /* * Figure out how many vblanks we've missed based @@ -278,9 +274,9 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, * for now, to mark the vblanktimestamp as invalid. */ if (!rc && !in_vblank_irq) - t_vblank = (struct timeval) {0, 0}; + t_vblank = 0; - store_vblank(dev, pipe, diff, &t_vblank, cur_vblank); + store_vblank(dev, pipe, diff, t_vblank, cur_vblank); } static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe) @@ -556,7 +552,7 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants); * @pipe: index of CRTC whose vblank timestamp to retrieve * @max_error: Desired maximum allowable error in timestamps (nanosecs) * On return contains true maximum error of timestamp - * @vblank_time: Pointer to struct timeval which should receive the timestamp + * @vblank_time: Pointer to time which should receive the timestamp * @in_vblank_irq: * True when called from drm_crtc_handle_vblank(). Some drivers * need to apply some workarounds for gpu-specific vblank irq quirks @@ -584,10 +580,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants); bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, unsigned int pipe, int *max_error, - struct timeval *vblank_time, + ktime_t *vblank_time, bool in_vblank_irq) { - struct timeval tv_etime; + struct timespec64 ts_etime, ts_vblank_time; ktime_t stime, etime; bool vbl_status; struct drm_crtc *crtc; @@ -680,29 +676,27 @@ bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, etime = ktime_mono_to_real(etime); /* save this only for debugging purposes */ - tv_etime = ktime_to_timeval(etime); + ts_etime = ktime_to_timespec64(etime); + ts_vblank_time = ktime_to_timespec64(*vblank_time); /* Subtract time delta from raw timestamp to get final * vblank_time timestamp for end of vblank. */ etime = ktime_sub_ns(etime, delta_ns); - *vblank_time = ktime_to_timeval(etime); + *vblank_time = etime; - DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n", + DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %lld.%06ld -> %lld.%06ld [e %d us, %d rep]\n", pipe, hpos, vpos, - (long)tv_etime.tv_sec, (long)tv_etime.tv_usec, - (long)vblank_time->tv_sec, (long)vblank_time->tv_usec, - duration_ns/1000, i); + (u64)ts_etime.tv_sec, ts_etime.tv_nsec / 1000, + (u64)ts_vblank_time.tv_sec, ts_vblank_time.tv_nsec / 1000, + duration_ns / 1000, i); return true; } EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); -static struct timeval get_drm_timestamp(void) +static ktime_t get_drm_timestamp(void) { - ktime_t now; - - now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real(); - return ktime_to_timeval(now); + return drm_timestamp_monotonic ? ktime_get() : ktime_get_real(); } /** @@ -710,7 +704,7 @@ static struct timeval get_drm_timestamp(void) * vblank interval * @dev: DRM device * @pipe: index of CRTC whose vblank timestamp to retrieve - * @tvblank: Pointer to target struct timeval which should receive the timestamp + * @tvblank: Pointer to target time which should receive the timestamp * @in_vblank_irq: * True when called from drm_crtc_handle_vblank(). Some drivers * need to apply some workarounds for gpu-specific vblank irq quirks @@ -728,7 +722,7 @@ static struct timeval get_drm_timestamp(void) */ static bool drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, - struct timeval *tvblank, bool in_vblank_irq) + ktime_t *tvblank, bool in_vblank_irq) { bool ret = false; @@ -769,14 +763,14 @@ u32 drm_crtc_vblank_count(struct drm_crtc *crtc) EXPORT_SYMBOL(drm_crtc_vblank_count); static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, - struct timeval *vblanktime) + ktime_t *vblanktime) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; u32 vblank_count; unsigned int seq; if (WARN_ON(pipe >= dev->num_crtcs)) { - *vblanktime = (struct timeval) { 0 }; + *vblanktime = 0; return 0; } @@ -793,7 +787,7 @@ static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value * and the system timestamp corresponding to that vblank counter value * @crtc: which counter to retrieve - * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. + * @vblanktime: Pointer to time to receive the vblank timestamp. * * Fetches the "cooked" vblank count value that represents the number of * vblank events since the system was booted, including lost events due to @@ -801,7 +795,7 @@ static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, * of the vblank interval that corresponds to the current vblank counter value. */ u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, - struct timeval *vblanktime) + ktime_t *vblanktime) { return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc), vblanktime); @@ -810,11 +804,18 @@ EXPORT_SYMBOL(drm_crtc_vblank_count_and_time); static void send_vblank_event(struct drm_device *dev, struct drm_pending_vblank_event *e, - unsigned long seq, struct timeval *now) + unsigned long seq, ktime_t now) { + struct timespec64 tv = ktime_to_timespec64(now); + e->event.sequence = seq; - e->event.tv_sec = now->tv_sec; - e->event.tv_usec = now->tv_usec; + /* + * e->event is a user space structure, with hardcoded unsigned + * 32-bit seconds/microseconds. This will overflow in 2106 for + * drm_timestamp_monotonic==0, but not with drm_timestamp_monotonic==1 + */ + e->event.tv_sec = tv.tv_sec; + e->event.tv_usec = tv.tv_nsec / 1000; trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, e->event.sequence); @@ -891,7 +892,7 @@ void drm_crtc_send_vblank_event(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; unsigned int seq, pipe = drm_crtc_index(crtc); - struct timeval now; + ktime_t now; if (dev->num_crtcs > 0) { seq = drm_vblank_count_and_time(dev, pipe, &now); @@ -902,7 +903,7 @@ void drm_crtc_send_vblank_event(struct drm_crtc *crtc, } e->pipe = pipe; e->event.crtc_id = crtc->base.id; - send_vblank_event(dev, e, seq, &now); + send_vblank_event(dev, e, seq, now); } EXPORT_SYMBOL(drm_crtc_send_vblank_event); @@ -1100,7 +1101,8 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) unsigned int pipe = drm_crtc_index(crtc); struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_pending_vblank_event *e, *t; - struct timeval now; + + ktime_t now; unsigned long irqflags; unsigned int seq; @@ -1141,7 +1143,7 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) e->event.sequence, seq); list_del(&e->base.link); drm_vblank_put(dev, pipe); - send_vblank_event(dev, e, seq, &now); + send_vblank_event(dev, e, seq, now); } spin_unlock_irqrestore(&dev->event_lock, irqflags); @@ -1321,7 +1323,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_pending_vblank_event *e; - struct timeval now; + ktime_t now; unsigned long flags; unsigned int seq; int ret; @@ -1367,7 +1369,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, e->event.sequence = vblwait->request.sequence; if (vblank_passed(seq, vblwait->request.sequence)) { drm_vblank_put(dev, pipe); - send_vblank_event(dev, e, seq, &now); + send_vblank_event(dev, e, seq, now); vblwait->reply.sequence = seq; } else { /* drm_handle_vblank_events will call drm_vblank_put */ @@ -1398,6 +1400,24 @@ static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait) _DRM_VBLANK_NEXTONMISS)); } +static void drm_wait_vblank_reply(struct drm_device *dev, unsigned int pipe, + struct drm_wait_vblank_reply *reply) +{ + ktime_t now; + struct timespec64 ts; + + /* + * drm_wait_vblank_reply is a UAPI structure that uses 'long' + * to store the seconds. This will overflow in y2038 on 32-bit + * architectures with drm_timestamp_monotonic==0, but not with + * drm_timestamp_monotonic==1 (the default). + */ + reply->sequence = drm_vblank_count_and_time(dev, pipe, &now); + ts = ktime_to_timespec64(now); + reply->tval_sec = (u32)ts.tv_sec; + reply->tval_usec = ts.tv_nsec / 1000; +} + int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -1439,12 +1459,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, if (dev->vblank_disable_immediate && drm_wait_vblank_is_query(vblwait) && READ_ONCE(vblank->enabled)) { - struct timeval now; - - vblwait->reply.sequence = - drm_vblank_count_and_time(dev, pipe, &now); - vblwait->reply.tval_sec = now.tv_sec; - vblwait->reply.tval_usec = now.tv_usec; + drm_wait_vblank_reply(dev, pipe, &vblwait->reply); return 0; } @@ -1487,11 +1502,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, } if (ret != -EINTR) { - struct timeval now; - - vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now); - vblwait->reply.tval_sec = now.tv_sec; - vblwait->reply.tval_usec = now.tv_usec; + drm_wait_vblank_reply(dev, pipe, &vblwait->reply); DRM_DEBUG("crtc %d returning %u to client\n", pipe, vblwait->reply.sequence); @@ -1507,7 +1518,7 @@ done: static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) { struct drm_pending_vblank_event *e, *t; - struct timeval now; + ktime_t now; unsigned int seq; assert_spin_locked(&dev->event_lock); @@ -1525,7 +1536,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) list_del(&e->base.link); drm_vblank_put(dev, pipe); - send_vblank_event(dev, e, seq, &now); + send_vblank_event(dev, e, seq, now); } trace_drm_vblank_event(pipe, seq); diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index ee06ecd6c01f..412e83a4d3db 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -324,7 +324,7 @@ struct drm_driver { */ bool (*get_vblank_timestamp) (struct drm_device *dev, unsigned int pipe, int *max_error, - struct timeval *vblank_time, + ktime_t *vblank_time, bool in_vblank_irq); /** diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 7fba9efe4951..6a58e2e91a0f 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -92,7 +92,7 @@ struct drm_vblank_crtc { /** * @time: Vblank timestamp corresponding to @count. */ - struct timeval time; + ktime_t time; /** * @refcount: Number of users/waiters of the vblank interrupt. Only when @@ -154,7 +154,7 @@ struct drm_vblank_crtc { int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs); u32 drm_crtc_vblank_count(struct drm_crtc *crtc); u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, - struct timeval *vblanktime); + ktime_t *vblanktime); void drm_crtc_send_vblank_event(struct drm_crtc *crtc, struct drm_pending_vblank_event *e); void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, @@ -172,7 +172,7 @@ u32 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc); bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, unsigned int pipe, int *max_error, - struct timeval *vblank_time, + ktime_t *vblank_time, bool in_vblank_irq); void drm_calc_timestamping_constants(struct drm_crtc *crtc, const struct drm_display_mode *mode); -- cgit v1.2.3-59-g8ed1b From 8f04748016f3b583e675e0f649d42cfc10812a8b Mon Sep 17 00:00:00 2001 From: Roman Mashak Date: Wed, 11 Oct 2017 10:50:29 -0400 Subject: net sched actions: change IFE modules alias names Make style of module alias name consistent with other subsystems in kernel, for example net devices. Fixes: 084e2f6566d2 ("Support to encoding decoding skb mark on IFE action") Fixes: 200e10f46936 ("Support to encoding decoding skb prio on IFE action") Fixes: 408fbc22ef1e ("net sched ife action: Introduce skb tcindex metadata encap decap") Signed-off-by: Roman Mashak Signed-off-by: David S. Miller --- include/net/tc_act/tc_ife.h | 2 +- net/sched/act_ife.c | 2 +- net/sched/act_meta_mark.c | 2 +- net/sched/act_meta_skbprio.c | 2 +- net/sched/act_meta_skbtcindex.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/tc_act/tc_ife.h b/include/net/tc_act/tc_ife.h index 30ba459ddd34..104578f16062 100644 --- a/include/net/tc_act/tc_ife.h +++ b/include/net/tc_act/tc_ife.h @@ -40,7 +40,7 @@ struct tcf_meta_ops { struct module *owner; }; -#define MODULE_ALIAS_IFE_META(metan) MODULE_ALIAS("ifemeta" __stringify_1(metan)) +#define MODULE_ALIAS_IFE_META(metan) MODULE_ALIAS("ife-meta-" metan) int ife_get_meta_u32(struct sk_buff *skb, struct tcf_meta_info *mi); int ife_get_meta_u16(struct sk_buff *skb, struct tcf_meta_info *mi); diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index 8ccd35825b6b..791aeee11c7e 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c @@ -263,7 +263,7 @@ static int load_metaops_and_vet(struct tcf_ife_info *ife, u32 metaid, if (exists) spin_unlock_bh(&ife->tcf_lock); rtnl_unlock(); - request_module("ifemeta%u", metaid); + request_module("ife-meta-%u", metaid); rtnl_lock(); if (exists) spin_lock_bh(&ife->tcf_lock); diff --git a/net/sched/act_meta_mark.c b/net/sched/act_meta_mark.c index 82892170ce4f..1e3f10e5da99 100644 --- a/net/sched/act_meta_mark.c +++ b/net/sched/act_meta_mark.c @@ -76,4 +76,4 @@ module_exit(ifemark_cleanup_module); MODULE_AUTHOR("Jamal Hadi Salim(2015)"); MODULE_DESCRIPTION("Inter-FE skb mark metadata module"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_IFE_META(IFE_META_SKBMARK); +MODULE_ALIAS_IFE_META("skbmark"); diff --git a/net/sched/act_meta_skbprio.c b/net/sched/act_meta_skbprio.c index 26bf4d86030b..4033f9fc4d4a 100644 --- a/net/sched/act_meta_skbprio.c +++ b/net/sched/act_meta_skbprio.c @@ -73,4 +73,4 @@ module_exit(ifeprio_cleanup_module); MODULE_AUTHOR("Jamal Hadi Salim(2015)"); MODULE_DESCRIPTION("Inter-FE skb prio metadata action"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_IFE_META(IFE_META_PRIO); +MODULE_ALIAS_IFE_META("skbprio"); diff --git a/net/sched/act_meta_skbtcindex.c b/net/sched/act_meta_skbtcindex.c index 3b35774ce890..2ea1f26c9e96 100644 --- a/net/sched/act_meta_skbtcindex.c +++ b/net/sched/act_meta_skbtcindex.c @@ -76,4 +76,4 @@ module_exit(ifetc_index_cleanup_module); MODULE_AUTHOR("Jamal Hadi Salim(2016)"); MODULE_DESCRIPTION("Inter-FE skb tc_index metadata module"); MODULE_LICENSE("GPL"); -MODULE_ALIAS_IFE_META(IFE_META_SKBTCINDEX); +MODULE_ALIAS_IFE_META("tcindex"); -- cgit v1.2.3-59-g8ed1b From aa9fd9a325d51fa0b11153b03b8fefff569fa955 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Wed, 11 Oct 2017 17:16:08 -0400 Subject: sched: act: ife: update parameters via rcu handling This patch changes the parameter updating via RCU and not protected by a spinlock anymore. This reduce the time that the spinlock is being held. Signed-off-by: Alexander Aring Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/tc_act/tc_ife.h | 10 ++++-- net/sched/act_ife.c | 87 ++++++++++++++++++++++++++++++--------------- 2 files changed, 67 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/tc_act/tc_ife.h b/include/net/tc_act/tc_ife.h index 104578f16062..c7fb99c3f76c 100644 --- a/include/net/tc_act/tc_ife.h +++ b/include/net/tc_act/tc_ife.h @@ -6,12 +6,18 @@ #include #include -struct tcf_ife_info { - struct tc_action common; +struct tcf_ife_params { u8 eth_dst[ETH_ALEN]; u8 eth_src[ETH_ALEN]; u16 eth_type; u16 flags; + + struct rcu_head rcu; +}; + +struct tcf_ife_info { + struct tc_action common; + struct tcf_ife_params __rcu *params; /* list of metaids allowed */ struct list_head metalist; }; diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index f59d78918cf9..252ee7d8c731 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c @@ -406,10 +406,14 @@ static void _tcf_ife_cleanup(struct tc_action *a, int bind) static void tcf_ife_cleanup(struct tc_action *a, int bind) { struct tcf_ife_info *ife = to_ife(a); + struct tcf_ife_params *p; spin_lock_bh(&ife->tcf_lock); _tcf_ife_cleanup(a, bind); spin_unlock_bh(&ife->tcf_lock); + + p = rcu_dereference_protected(ife->params, 1); + kfree_rcu(p, rcu); } /* under ife->tcf_lock for existing action */ @@ -446,6 +450,7 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, struct tc_action_net *tn = net_generic(net, ife_net_id); struct nlattr *tb[TCA_IFE_MAX + 1]; struct nlattr *tb2[IFE_META_MAX + 1]; + struct tcf_ife_params *p, *p_old; struct tcf_ife_info *ife; u16 ife_type = ETH_P_IFE; struct tc_ife *parm; @@ -471,24 +476,34 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, if (parm->flags & ~IFE_ENCODE) return -EINVAL; + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + exists = tcf_idr_check(tn, parm->index, a, bind); - if (exists && bind) + if (exists && bind) { + kfree(p); return 0; + } if (!exists) { ret = tcf_idr_create(tn, parm->index, est, a, &act_ife_ops, bind, true); - if (ret) + if (ret) { + kfree(p); return ret; + } ret = ACT_P_CREATED; } else { tcf_idr_release(*a, bind); - if (!ovr) + if (!ovr) { + kfree(p); return -EEXIST; + } } ife = to_ife(*a); - ife->flags = parm->flags; + p->flags = parm->flags; if (parm->flags & IFE_ENCODE) { if (tb[TCA_IFE_TYPE]) @@ -499,24 +514,25 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, saddr = nla_data(tb[TCA_IFE_SMAC]); } - if (exists) - spin_lock_bh(&ife->tcf_lock); ife->tcf_action = parm->action; if (parm->flags & IFE_ENCODE) { if (daddr) - ether_addr_copy(ife->eth_dst, daddr); + ether_addr_copy(p->eth_dst, daddr); else - eth_zero_addr(ife->eth_dst); + eth_zero_addr(p->eth_dst); if (saddr) - ether_addr_copy(ife->eth_src, saddr); + ether_addr_copy(p->eth_src, saddr); else - eth_zero_addr(ife->eth_src); + eth_zero_addr(p->eth_src); - ife->eth_type = ife_type; + p->eth_type = ife_type; } + if (exists) + spin_lock_bh(&ife->tcf_lock); + if (ret == ACT_P_CREATED) INIT_LIST_HEAD(&ife->metalist); @@ -532,6 +548,7 @@ metadata_parse_err: if (exists) spin_unlock_bh(&ife->tcf_lock); + kfree(p); return err; } @@ -552,6 +569,7 @@ metadata_parse_err: if (exists) spin_unlock_bh(&ife->tcf_lock); + kfree(p); return err; } } @@ -559,6 +577,11 @@ metadata_parse_err: if (exists) spin_unlock_bh(&ife->tcf_lock); + p_old = rtnl_dereference(ife->params); + rcu_assign_pointer(ife->params, p); + if (p_old) + kfree_rcu(p_old, rcu); + if (ret == ACT_P_CREATED) tcf_idr_insert(tn, *a); @@ -570,12 +593,13 @@ static int tcf_ife_dump(struct sk_buff *skb, struct tc_action *a, int bind, { unsigned char *b = skb_tail_pointer(skb); struct tcf_ife_info *ife = to_ife(a); + struct tcf_ife_params *p = rtnl_dereference(ife->params); struct tc_ife opt = { .index = ife->tcf_index, .refcnt = ife->tcf_refcnt - ref, .bindcnt = ife->tcf_bindcnt - bind, .action = ife->tcf_action, - .flags = ife->flags, + .flags = p->flags, }; struct tcf_t t; @@ -586,17 +610,17 @@ static int tcf_ife_dump(struct sk_buff *skb, struct tc_action *a, int bind, if (nla_put_64bit(skb, TCA_IFE_TM, sizeof(t), &t, TCA_IFE_PAD)) goto nla_put_failure; - if (!is_zero_ether_addr(ife->eth_dst)) { - if (nla_put(skb, TCA_IFE_DMAC, ETH_ALEN, ife->eth_dst)) + if (!is_zero_ether_addr(p->eth_dst)) { + if (nla_put(skb, TCA_IFE_DMAC, ETH_ALEN, p->eth_dst)) goto nla_put_failure; } - if (!is_zero_ether_addr(ife->eth_src)) { - if (nla_put(skb, TCA_IFE_SMAC, ETH_ALEN, ife->eth_src)) + if (!is_zero_ether_addr(p->eth_src)) { + if (nla_put(skb, TCA_IFE_SMAC, ETH_ALEN, p->eth_src)) goto nla_put_failure; } - if (nla_put(skb, TCA_IFE_TYPE, 2, &ife->eth_type)) + if (nla_put(skb, TCA_IFE_TYPE, 2, &p->eth_type)) goto nla_put_failure; if (dump_metalist(skb, ife)) { @@ -698,7 +722,7 @@ static int ife_get_sz(struct sk_buff *skb, struct tcf_ife_info *ife) } static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a, - struct tcf_result *res) + struct tcf_result *res, struct tcf_ife_params *p) { struct tcf_ife_info *ife = to_ife(a); int action = ife->tcf_action; @@ -762,19 +786,18 @@ static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a, } skboff += err; } + spin_unlock(&ife->tcf_lock); oethh = (struct ethhdr *)skb->data; - if (!is_zero_ether_addr(ife->eth_src)) - ether_addr_copy(oethh->h_source, ife->eth_src); - if (!is_zero_ether_addr(ife->eth_dst)) - ether_addr_copy(oethh->h_dest, ife->eth_dst); - oethh->h_proto = htons(ife->eth_type); + if (!is_zero_ether_addr(p->eth_src)) + ether_addr_copy(oethh->h_source, p->eth_src); + if (!is_zero_ether_addr(p->eth_dst)) + ether_addr_copy(oethh->h_dest, p->eth_dst); + oethh->h_proto = htons(p->eth_type); if (skb_at_tc_ingress(skb)) skb_pull(skb, skb->dev->hard_header_len); - spin_unlock(&ife->tcf_lock); - return action; } @@ -782,9 +805,17 @@ static int tcf_ife_act(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { struct tcf_ife_info *ife = to_ife(a); - - if (ife->flags & IFE_ENCODE) - return tcf_ife_encode(skb, a, res); + struct tcf_ife_params *p; + int ret; + + rcu_read_lock(); + p = rcu_dereference(ife->params); + if (p->flags & IFE_ENCODE) { + ret = tcf_ife_encode(skb, a, res, p); + rcu_read_unlock(); + return ret; + } + rcu_read_unlock(); return tcf_ife_decode(skb, a, res); } -- cgit v1.2.3-59-g8ed1b From 2355a6546a053b1c16ebefd6ce1f0cccc00e1da5 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Thu, 12 Oct 2017 10:21:25 +0200 Subject: net: phy: broadcom: support new device flag for setting master mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of Broadcom's PHYs run by default in slave mode with Automatic Slave/Master configuration disabled. It stops them from working properly with some devices. So far it has been verified for BCM54210E and BCM50212E which don't work well with Intel's I217-LM and I218-LM: http://ark.intel.com/products/60019/Intel-Ethernet-Connection-I217-LM http://ark.intel.com/products/71307/Intel-Ethernet-Connection-I218-LM I was told there is massive ping loss. This commit adds support for a new flag which can be set by an ethernet driver to fixup PHY setup. Signed-off-by: Rafał Miłecki Signed-off-by: David S. Miller --- drivers/net/phy/broadcom.c | 6 ++++++ include/linux/brcmphy.h | 1 + 2 files changed, 7 insertions(+) (limited to 'include') diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c index 1e9ad30a35c8..d7ed69deabfb 100644 --- a/drivers/net/phy/broadcom.c +++ b/drivers/net/phy/broadcom.c @@ -43,6 +43,12 @@ static int bcm54210e_config_init(struct phy_device *phydev) val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN; bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val); + if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) { + val = phy_read(phydev, MII_CTRL1000); + val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER; + phy_write(phydev, MII_CTRL1000, val); + } + return 0; } diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h index abcda9b458ab..9ac9e3e3d1e5 100644 --- a/include/linux/brcmphy.h +++ b/include/linux/brcmphy.h @@ -63,6 +63,7 @@ #define PHY_BRCM_EXT_IBND_TX_ENABLE 0x00002000 #define PHY_BRCM_CLEAR_RGMII_MODE 0x00004000 #define PHY_BRCM_DIS_TXCRXC_NOENRGY 0x00008000 +#define PHY_BRCM_EN_MASTER_MODE 0x00010000 /* Broadcom BCM7xxx specific workarounds */ #define PHY_BRCM_7XXX_REV(x) (((x) >> 8) & 0xff) -- cgit v1.2.3-59-g8ed1b From 511cb17448d95e4277451cdee882e72b6a9a3099 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 30 Aug 2017 14:59:10 +0530 Subject: mfd: tps65217: Introduce dependency on CONFIG_OF Currently the driver boots only via device tree hence add a dependency on CONFIG_OF. This leaves with a bunch of unused code so clean that up. This patch also makes use of probe_new function in place of the probe function so as to avoid passing i2c_device_id. Signed-off-by: Keerthy Reviewed-by: Javier Martinez Canillas Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 2 +- drivers/mfd/tps65217.c | 28 +++++----------------------- drivers/regulator/tps65217-regulator.c | 5 ----- drivers/video/backlight/tps65217_bl.c | 14 +++----------- include/linux/mfd/tps65217.h | 6 ------ 5 files changed, 9 insertions(+), 46 deletions(-) (limited to 'include') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index fc5e4fef89d2..b47876877c9c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1338,7 +1338,7 @@ config MFD_TPS65090 config MFD_TPS65217 tristate "TI TPS65217 Power Management / White LED chips" - depends on I2C + depends on I2C && OF select MFD_CORE select REGMAP_I2C select IRQ_DOMAIN diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index f769c7d4e335..7566ce4457a0 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -311,37 +311,20 @@ static const struct regmap_config tps65217_regmap_config = { }; static const struct of_device_id tps65217_of_match[] = { - { .compatible = "ti,tps65217", .data = (void *)TPS65217 }, + { .compatible = "ti,tps65217"}, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, tps65217_of_match); -static int tps65217_probe(struct i2c_client *client, - const struct i2c_device_id *ids) +static int tps65217_probe(struct i2c_client *client) { struct tps65217 *tps; unsigned int version; - unsigned long chip_id = ids->driver_data; - const struct of_device_id *match; bool status_off = false; int ret; - if (client->dev.of_node) { - match = of_match_device(tps65217_of_match, &client->dev); - if (!match) { - dev_err(&client->dev, - "Failed to find matching dt id\n"); - return -EINVAL; - } - chip_id = (unsigned long)match->data; - status_off = of_property_read_bool(client->dev.of_node, - "ti,pmic-shutdown-controller"); - } - - if (!chip_id) { - dev_err(&client->dev, "id is null.\n"); - return -ENODEV; - } + status_off = of_property_read_bool(client->dev.of_node, + "ti,pmic-shutdown-controller"); tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); if (!tps) @@ -349,7 +332,6 @@ static int tps65217_probe(struct i2c_client *client, i2c_set_clientdata(client, tps); tps->dev = &client->dev; - tps->id = chip_id; tps->regmap = devm_regmap_init_i2c(client, &tps65217_regmap_config); if (IS_ERR(tps->regmap)) { @@ -430,7 +412,7 @@ static struct i2c_driver tps65217_driver = { .of_match_table = tps65217_of_match, }, .id_table = tps65217_id_table, - .probe = tps65217_probe, + .probe_new = tps65217_probe, .remove = tps65217_remove, }; diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 5324dc9e6d6e..7b12e880d1ea 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -228,11 +228,6 @@ static int tps65217_regulator_probe(struct platform_device *pdev) int i, ret; unsigned int val; - if (tps65217_chip_id(tps) != TPS65217) { - dev_err(&pdev->dev, "Invalid tps chip version\n"); - return -ENODEV; - } - /* Allocate memory for strobes */ tps->strobes = devm_kzalloc(&pdev->dev, sizeof(u8) * TPS65217_NUM_REGULATOR, GFP_KERNEL); diff --git a/drivers/video/backlight/tps65217_bl.c b/drivers/video/backlight/tps65217_bl.c index fd524ad860a5..5ce8791f4523 100644 --- a/drivers/video/backlight/tps65217_bl.c +++ b/drivers/video/backlight/tps65217_bl.c @@ -275,17 +275,9 @@ static int tps65217_bl_probe(struct platform_device *pdev) struct tps65217_bl_pdata *pdata; struct backlight_properties bl_props; - if (tps->dev->of_node) { - pdata = tps65217_bl_parse_dt(pdev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - } else { - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(&pdev->dev, "no platform data provided\n"); - return -EINVAL; - } - } + pdata = tps65217_bl_parse_dt(pdev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); tps65217_bl = devm_kzalloc(&pdev->dev, sizeof(*tps65217_bl), GFP_KERNEL); diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index eac285756b37..b5dd108421c8 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -263,7 +263,6 @@ struct tps65217_board { struct tps65217 { struct device *dev; struct tps65217_board *pdata; - unsigned long id; struct regulator_desc desc[TPS65217_NUM_REGULATOR]; struct regmap *regmap; u8 *strobes; @@ -278,11 +277,6 @@ static inline struct tps65217 *dev_to_tps65217(struct device *dev) return dev_get_drvdata(dev); } -static inline unsigned long tps65217_chip_id(struct tps65217 *tps65217) -{ - return tps65217->id; -} - int tps65217_reg_read(struct tps65217 *tps, unsigned int reg, unsigned int *val); int tps65217_reg_write(struct tps65217 *tps, unsigned int reg, -- cgit v1.2.3-59-g8ed1b From 8275b77a15131673f955f738f0704e1d40a8edae Mon Sep 17 00:00:00 2001 From: Rui Feng Date: Thu, 7 Sep 2017 16:26:39 +0800 Subject: mfd: rts5249: Add support for RTS5250S power saving Enable power saving for RTS5250S as following steps: 1.Set 0xFE58 to enable clock power management. 2.Check cfg space whether support L1SS or not. 3.If support L1SS, set 0xFF03 to free clkreq. 4.When entering idle status, enable aspm and set parameters for L1SS and LTR. 5.Wnen entering run status, disable aspm and set parameters for L1SS and LTR. If entering L1SS mode successfully, electric current will be below 2mA. Signed-off-by: Rui Feng Signed-off-by: Lee Jones --- drivers/mfd/rts5249.c | 155 ++++++++++++++++++++++++++++++++++++++++++- drivers/mfd/rtsx_pcr.c | 142 +++++++++++++++++++++++++++++++++++++-- drivers/mfd/rtsx_pcr.h | 14 ++++ include/linux/mfd/rtsx_pci.h | 84 +++++++++++++++++++++++ 4 files changed, 389 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/mfd/rts5249.c b/drivers/mfd/rts5249.c index 40f8bb14fc59..7fcf37ba922c 100644 --- a/drivers/mfd/rts5249.c +++ b/drivers/mfd/rts5249.c @@ -103,8 +103,64 @@ static void rtsx_base_force_power_down(struct rtsx_pcr *pcr, u8 pm_state) rtsx_pci_write_register(pcr, FPDCTL, 0x03, 0x03); } +static void rts5249_init_from_cfg(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &(pcr->option); + u32 lval; + + if (CHK_PCI_PID(pcr, PID_524A)) + rtsx_pci_read_config_dword(pcr, + PCR_ASPM_SETTING_REG1, &lval); + else + rtsx_pci_read_config_dword(pcr, + PCR_ASPM_SETTING_REG2, &lval); + + if (lval & ASPM_L1_1_EN_MASK) + rtsx_set_dev_flag(pcr, ASPM_L1_1_EN); + + if (lval & ASPM_L1_2_EN_MASK) + rtsx_set_dev_flag(pcr, ASPM_L1_2_EN); + + if (lval & PM_L1_1_EN_MASK) + rtsx_set_dev_flag(pcr, PM_L1_1_EN); + + if (lval & PM_L1_2_EN_MASK) + rtsx_set_dev_flag(pcr, PM_L1_2_EN); + + if (option->ltr_en) { + u16 val; + + pcie_capability_read_word(pcr->pci, PCI_EXP_DEVCTL2, &val); + if (val & PCI_EXP_DEVCTL2_LTR_EN) { + option->ltr_enabled = true; + option->ltr_active = true; + rtsx_set_ltr_latency(pcr, option->ltr_active_latency); + } else { + option->ltr_enabled = false; + } + } +} + +static int rts5249_init_from_hw(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &(pcr->option); + + if (rtsx_check_dev_flag(pcr, ASPM_L1_1_EN | ASPM_L1_2_EN + | PM_L1_1_EN | PM_L1_2_EN)) + option->force_clkreq_0 = false; + else + option->force_clkreq_0 = true; + + return 0; +} + static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) { + struct rtsx_cr_option *option = &(pcr->option); + + rts5249_init_from_cfg(pcr); + rts5249_init_from_hw(pcr); + rtsx_pci_init_cmd(pcr); /* Rest L1SUB Config */ @@ -125,7 +181,18 @@ static int rts5249_extra_init_hw(struct rtsx_pcr *pcr) else rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, 0xB0, 0x80); - return rtsx_pci_send_cmd(pcr, 100); + /* + * If u_force_clkreq_0 is enabled, CLKREQ# PIN will be forced + * to drive low, and we forcibly request clock. + */ + if (option->force_clkreq_0) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_LOW); + else + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PETXCFG, + FORCE_CLKREQ_DELINK_MASK, FORCE_CLKREQ_HIGH); + + return rtsx_pci_send_cmd(pcr, CMD_TIMEOUT_DEF); } static int rts5249_optimize_phy(struct rtsx_pcr *pcr) @@ -285,6 +352,31 @@ static int rtsx_base_switch_output_voltage(struct rtsx_pcr *pcr, u8 voltage) return rtsx_pci_send_cmd(pcr, 100); } +static void rts5249_set_aspm(struct rtsx_pcr *pcr, bool enable) +{ + struct rtsx_cr_option *option = &pcr->option; + u8 val = 0; + + if (pcr->aspm_enabled == enable) + return; + + if (option->dev_aspm_mode == DEV_ASPM_DYNAMIC) { + if (enable) + val = pcr->aspm_en; + rtsx_pci_update_cfg_byte(pcr, + pcr->pcie_cap + PCI_EXP_LNKCTL, + ASPM_MASK_NEG, val); + } else if (option->dev_aspm_mode == DEV_ASPM_BACKDOOR) { + u8 mask = FORCE_ASPM_VAL_MASK | FORCE_ASPM_CTL0; + + if (!enable) + val = FORCE_ASPM_CTL0; + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); + } + + pcr->aspm_enabled = enable; +} + static const struct pcr_ops rts5249_pcr_ops = { .fetch_vendor_settings = rtsx_base_fetch_vendor_settings, .extra_init_hw = rts5249_extra_init_hw, @@ -297,6 +389,7 @@ static const struct pcr_ops rts5249_pcr_ops = { .card_power_off = rtsx_base_card_power_off, .switch_output_voltage = rtsx_base_switch_output_voltage, .force_power_down = rtsx_base_force_power_down, + .set_aspm = rts5249_set_aspm, }; /* SD Pull Control Enable: @@ -353,6 +446,8 @@ static const u32 rts5249_ms_pull_ctl_disable_tbl[] = { void rts5249_init_params(struct rtsx_pcr *pcr) { + struct rtsx_cr_option *option = &(pcr->option); + pcr->extra_caps = EXTRA_CAPS_SD_SDR50 | EXTRA_CAPS_SD_SDR104; pcr->num_slots = 2; pcr->ops = &rts5249_pcr_ops; @@ -372,6 +467,20 @@ void rts5249_init_params(struct rtsx_pcr *pcr) pcr->ms_pull_ctl_disable_tbl = rts5249_ms_pull_ctl_disable_tbl; pcr->reg_pm_ctrl3 = PM_CTRL3; + + option->dev_flags = (LTR_L1SS_PWR_GATE_CHECK_CARD_EN + | LTR_L1SS_PWR_GATE_EN); + option->ltr_en = true; + + /* Init latency of active, idle, L1OFF to 60us, 300us, 3ms */ + option->ltr_active_latency = LTR_ACTIVE_LATENCY_DEF; + option->ltr_idle_latency = LTR_IDLE_LATENCY_DEF; + option->ltr_l1off_latency = LTR_L1OFF_LATENCY_DEF; + option->dev_aspm_mode = DEV_ASPM_DYNAMIC; + option->l1_snooze_delay = L1_SNOOZE_DELAY_DEF; + option->ltr_l1off_sspwrgate = LTR_L1OFF_SSPWRGATE_5249_DEF; + option->ltr_l1off_snooze_sspwrgate = + LTR_L1OFF_SNOOZE_SSPWRGATE_5249_DEF; } static int rts524a_write_phy(struct rtsx_pcr *pcr, u8 addr, u16 val) @@ -459,6 +568,40 @@ static int rts524a_extra_init_hw(struct rtsx_pcr *pcr) return 0; } +static void rts5250_set_l1off_cfg_sub_d0(struct rtsx_pcr *pcr, int active) +{ + struct rtsx_cr_option *option = &(pcr->option); + + u32 interrupt = rtsx_pci_readl(pcr, RTSX_BIPR); + int card_exist = (interrupt & SD_EXIST) | (interrupt & MS_EXIST); + int aspm_L1_1, aspm_L1_2; + u8 val = 0; + + aspm_L1_1 = rtsx_check_dev_flag(pcr, ASPM_L1_1_EN); + aspm_L1_2 = rtsx_check_dev_flag(pcr, ASPM_L1_2_EN); + + if (active) { + /* Run, latency: 60us */ + if (aspm_L1_1) + val = option->ltr_l1off_snooze_sspwrgate; + } else { + /* L1off, latency: 300us */ + if (aspm_L1_2) + val = option->ltr_l1off_sspwrgate; + } + + if (aspm_L1_1 || aspm_L1_2) { + if (rtsx_check_dev_flag(pcr, + LTR_L1SS_PWR_GATE_CHECK_CARD_EN)) { + if (card_exist) + val &= ~L1OFF_MBIAS2_EN_5250; + else + val |= L1OFF_MBIAS2_EN_5250; + } + } + rtsx_set_l1off_sub(pcr, val); +} + static const struct pcr_ops rts524a_pcr_ops = { .write_phy = rts524a_write_phy, .read_phy = rts524a_read_phy, @@ -473,11 +616,16 @@ static const struct pcr_ops rts524a_pcr_ops = { .card_power_off = rtsx_base_card_power_off, .switch_output_voltage = rtsx_base_switch_output_voltage, .force_power_down = rtsx_base_force_power_down, + .set_l1off_cfg_sub_d0 = rts5250_set_l1off_cfg_sub_d0, + .set_aspm = rts5249_set_aspm, }; void rts524a_init_params(struct rtsx_pcr *pcr) { rts5249_init_params(pcr); + pcr->option.ltr_l1off_sspwrgate = LTR_L1OFF_SSPWRGATE_5250_DEF; + pcr->option.ltr_l1off_snooze_sspwrgate = + LTR_L1OFF_SNOOZE_SSPWRGATE_5250_DEF; pcr->reg_pm_ctrl3 = RTS524A_PM_CTRL3; pcr->ops = &rts524a_pcr_ops; @@ -576,11 +724,16 @@ static const struct pcr_ops rts525a_pcr_ops = { .card_power_off = rtsx_base_card_power_off, .switch_output_voltage = rts525a_switch_output_voltage, .force_power_down = rtsx_base_force_power_down, + .set_l1off_cfg_sub_d0 = rts5250_set_l1off_cfg_sub_d0, + .set_aspm = rts5249_set_aspm, }; void rts525a_init_params(struct rtsx_pcr *pcr) { rts5249_init_params(pcr); + pcr->option.ltr_l1off_sspwrgate = LTR_L1OFF_SSPWRGATE_5250_DEF; + pcr->option.ltr_l1off_snooze_sspwrgate = + LTR_L1OFF_SNOOZE_SSPWRGATE_5250_DEF; pcr->reg_pm_ctrl3 = RTS524A_PM_CTRL3; pcr->ops = &rts525a_pcr_ops; diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c index 3cf69e5c5703..590fb9aad77d 100644 --- a/drivers/mfd/rtsx_pcr.c +++ b/drivers/mfd/rtsx_pcr.c @@ -79,6 +79,96 @@ static inline void rtsx_pci_disable_aspm(struct rtsx_pcr *pcr) 0xFC, 0); } +int rtsx_comm_set_ltr_latency(struct rtsx_pcr *pcr, u32 latency) +{ + rtsx_pci_write_register(pcr, MSGTXDATA0, + MASK_8_BIT_DEF, (u8) (latency & 0xFF)); + rtsx_pci_write_register(pcr, MSGTXDATA1, + MASK_8_BIT_DEF, (u8)((latency >> 8) & 0xFF)); + rtsx_pci_write_register(pcr, MSGTXDATA2, + MASK_8_BIT_DEF, (u8)((latency >> 16) & 0xFF)); + rtsx_pci_write_register(pcr, MSGTXDATA3, + MASK_8_BIT_DEF, (u8)((latency >> 24) & 0xFF)); + rtsx_pci_write_register(pcr, LTR_CTL, LTR_TX_EN_MASK | + LTR_LATENCY_MODE_MASK, LTR_TX_EN_1 | LTR_LATENCY_MODE_SW); + + return 0; +} + +int rtsx_set_ltr_latency(struct rtsx_pcr *pcr, u32 latency) +{ + if (pcr->ops->set_ltr_latency) + return pcr->ops->set_ltr_latency(pcr, latency); + else + return rtsx_comm_set_ltr_latency(pcr, latency); +} + +static void rtsx_comm_set_aspm(struct rtsx_pcr *pcr, bool enable) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (pcr->aspm_enabled == enable) + return; + + if (option->dev_aspm_mode == DEV_ASPM_DYNAMIC) { + if (enable) + rtsx_pci_enable_aspm(pcr); + else + rtsx_pci_disable_aspm(pcr); + } else if (option->dev_aspm_mode == DEV_ASPM_BACKDOOR) { + u8 mask = FORCE_ASPM_VAL_MASK; + u8 val = 0; + + if (enable) + val = pcr->aspm_en; + rtsx_pci_write_register(pcr, ASPM_FORCE_CTL, mask, val); + } + + pcr->aspm_enabled = enable; +} + +static void rtsx_disable_aspm(struct rtsx_pcr *pcr) +{ + if (pcr->ops->set_aspm) + pcr->ops->set_aspm(pcr, false); + else + rtsx_comm_set_aspm(pcr, false); +} + +int rtsx_set_l1off_sub(struct rtsx_pcr *pcr, u8 val) +{ + rtsx_pci_write_register(pcr, L1SUB_CONFIG3, 0xFF, val); + + return 0; +} + +void rtsx_set_l1off_sub_cfg_d0(struct rtsx_pcr *pcr, int active) +{ + if (pcr->ops->set_l1off_cfg_sub_d0) + pcr->ops->set_l1off_cfg_sub_d0(pcr, active); +} + +static void rtsx_comm_pm_full_on(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + rtsx_disable_aspm(pcr); + + if (option->ltr_enabled) + rtsx_set_ltr_latency(pcr, option->ltr_active_latency); + + if (rtsx_check_dev_flag(pcr, LTR_L1SS_PWR_GATE_EN)) + rtsx_set_l1off_sub_cfg_d0(pcr, 1); +} + +void rtsx_pm_full_on(struct rtsx_pcr *pcr) +{ + if (pcr->ops->full_on) + pcr->ops->full_on(pcr); + else + rtsx_comm_pm_full_on(pcr); +} + void rtsx_pci_start_run(struct rtsx_pcr *pcr) { /* If pci device removed, don't queue idle work any more */ @@ -89,9 +179,7 @@ void rtsx_pci_start_run(struct rtsx_pcr *pcr) pcr->state = PDEV_STAT_RUN; if (pcr->ops->enable_auto_blink) pcr->ops->enable_auto_blink(pcr); - - if (pcr->aspm_en) - rtsx_pci_disable_aspm(pcr); + rtsx_pm_full_on(pcr); } mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200)); @@ -958,6 +1046,41 @@ static int rtsx_pci_acquire_irq(struct rtsx_pcr *pcr) return 0; } +static void rtsx_enable_aspm(struct rtsx_pcr *pcr) +{ + if (pcr->ops->set_aspm) + pcr->ops->set_aspm(pcr, true); + else + rtsx_comm_set_aspm(pcr, true); +} + +static void rtsx_comm_pm_power_saving(struct rtsx_pcr *pcr) +{ + struct rtsx_cr_option *option = &pcr->option; + + if (option->ltr_enabled) { + u32 latency = option->ltr_l1off_latency; + + if (rtsx_check_dev_flag(pcr, L1_SNOOZE_TEST_EN)) + mdelay(option->l1_snooze_delay); + + rtsx_set_ltr_latency(pcr, latency); + } + + if (rtsx_check_dev_flag(pcr, LTR_L1SS_PWR_GATE_EN)) + rtsx_set_l1off_sub_cfg_d0(pcr, 0); + + rtsx_enable_aspm(pcr); +} + +void rtsx_pm_power_saving(struct rtsx_pcr *pcr) +{ + if (pcr->ops->power_saving) + pcr->ops->power_saving(pcr); + else + rtsx_comm_pm_power_saving(pcr); +} + static void rtsx_pci_idle_work(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); @@ -974,8 +1097,7 @@ static void rtsx_pci_idle_work(struct work_struct *work) if (pcr->ops->turn_off_led) pcr->ops->turn_off_led(pcr); - if (pcr->aspm_en) - rtsx_pci_enable_aspm(pcr); + rtsx_pm_power_saving(pcr); mutex_unlock(&pcr->pcr_mutex); } @@ -1063,6 +1185,16 @@ static int rtsx_pci_init_hw(struct rtsx_pcr *pcr) if (err < 0) return err; + switch (PCI_PID(pcr)) { + case PID_5250: + case PID_524A: + case PID_525A: + rtsx_pci_write_register(pcr, PM_CLK_FORCE_CTL, 1, 1); + break; + default: + break; + } + /* Enable clk_request_n to enable clock power management */ rtsx_pci_write_config_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL + 1, 1); /* Enter L1 when host tx idle */ diff --git a/drivers/mfd/rtsx_pcr.h b/drivers/mfd/rtsx_pcr.h index 931d1ae3ce32..ec784e04fe20 100644 --- a/drivers/mfd/rtsx_pcr.h +++ b/drivers/mfd/rtsx_pcr.h @@ -32,6 +32,18 @@ #define RTS524A_PME_FORCE_CTL 0xFF78 #define RTS524A_PM_CTRL3 0xFF7E +#define LTR_ACTIVE_LATENCY_DEF 0x883C +#define LTR_IDLE_LATENCY_DEF 0x892C +#define LTR_L1OFF_LATENCY_DEF 0x9003 +#define L1_SNOOZE_DELAY_DEF 1 +#define LTR_L1OFF_SSPWRGATE_5249_DEF 0xAF +#define LTR_L1OFF_SSPWRGATE_5250_DEF 0xFF +#define LTR_L1OFF_SNOOZE_SSPWRGATE_5249_DEF 0xAC +#define LTR_L1OFF_SNOOZE_SSPWRGATE_5250_DEF 0xF8 +#define CMD_TIMEOUT_DEF 100 +#define ASPM_MASK_NEG 0xFC +#define MASK_8_BIT_DEF 0xFF + int __rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val); int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val); @@ -85,5 +97,7 @@ do { \ /* generic operations */ int rtsx_gops_pm_reset(struct rtsx_pcr *pcr); +int rtsx_set_ltr_latency(struct rtsx_pcr *pcr, u32 latency); +int rtsx_set_l1off_sub(struct rtsx_pcr *pcr, u8 val); #endif diff --git a/include/linux/mfd/rtsx_pci.h b/include/linux/mfd/rtsx_pci.h index 116816fb9110..57e23ad1c2bc 100644 --- a/include/linux/mfd/rtsx_pci.h +++ b/include/linux/mfd/rtsx_pci.h @@ -573,6 +573,12 @@ #define MSGTXDATA3 0xFE47 #define MSGTXCTL 0xFE48 #define LTR_CTL 0xFE4A +#define LTR_TX_EN_MASK BIT(7) +#define LTR_TX_EN_1 BIT(7) +#define LTR_TX_EN_0 0 +#define LTR_LATENCY_MODE_MASK BIT(6) +#define LTR_LATENCY_MODE_HW 0 +#define LTR_LATENCY_MODE_SW BIT(6) #define OBFF_CFG 0xFE4C #define CDRESUMECTL 0xFE52 @@ -616,11 +622,15 @@ #define L1SUB_CONFIG2 0xFE8E #define L1SUB_AUTO_CFG 0x02 #define L1SUB_CONFIG3 0xFE8F +#define L1OFF_MBIAS2_EN_5250 BIT(7) #define DUMMY_REG_RESET_0 0xFE90 #define AUTOLOAD_CFG_BASE 0xFF00 #define PETXCFG 0xFF03 +#define FORCE_CLKREQ_DELINK_MASK BIT(7) +#define FORCE_CLKREQ_LOW 0x80 +#define FORCE_CLKREQ_HIGH 0x00 #define PM_CTRL1 0xFF44 #define CD_RESUME_EN_MASK 0xF0 @@ -844,6 +854,9 @@ #define PHY_DIG1E_RX_EN_KEEP 0x0001 #define PHY_DUM_REG 0x1F +#define PCR_ASPM_SETTING_REG1 0x160 +#define PCR_ASPM_SETTING_REG2 0x168 + #define PCR_SETTING_REG1 0x724 #define PCR_SETTING_REG2 0x814 #define PCR_SETTING_REG3 0x747 @@ -876,14 +889,79 @@ struct pcr_ops { int (*conv_clk_and_div_n)(int clk, int dir); void (*fetch_vendor_settings)(struct rtsx_pcr *pcr); void (*force_power_down)(struct rtsx_pcr *pcr, u8 pm_state); + + void (*set_aspm)(struct rtsx_pcr *pcr, bool enable); + int (*set_ltr_latency)(struct rtsx_pcr *pcr, u32 latency); + int (*set_l1off_sub)(struct rtsx_pcr *pcr, u8 val); + void (*set_l1off_cfg_sub_d0)(struct rtsx_pcr *pcr, int active); + void (*full_on)(struct rtsx_pcr *pcr); + void (*power_saving)(struct rtsx_pcr *pcr); }; enum PDEV_STAT {PDEV_STAT_IDLE, PDEV_STAT_RUN}; +#define ASPM_L1_1_EN_MASK BIT(3) +#define ASPM_L1_2_EN_MASK BIT(2) +#define PM_L1_1_EN_MASK BIT(1) +#define PM_L1_2_EN_MASK BIT(0) + +#define ASPM_L1_1_EN BIT(0) +#define ASPM_L1_2_EN BIT(1) +#define PM_L1_1_EN BIT(2) +#define PM_L1_2_EN BIT(3) +#define LTR_L1SS_PWR_GATE_EN BIT(4) +#define L1_SNOOZE_TEST_EN BIT(5) +#define LTR_L1SS_PWR_GATE_CHECK_CARD_EN BIT(6) + +enum dev_aspm_mode { + DEV_ASPM_DISABLE = 0, + DEV_ASPM_DYNAMIC, + DEV_ASPM_BACKDOOR, + DEV_ASPM_STATIC, +}; + +/* + * struct rtsx_cr_option - card reader option + * @dev_flags: device flags + * @force_clkreq_0: force clock request + * @ltr_en: enable ltr mode flag + * @ltr_enabled: ltr mode in configure space flag + * @ltr_active: ltr mode status + * @ltr_active_latency: ltr mode active latency + * @ltr_idle_latency: ltr mode idle latency + * @ltr_l1off_latency: ltr mode l1off latency + * @dev_aspm_mode: device aspm mode + * @l1_snooze_delay: l1 snooze delay + * @ltr_l1off_sspwrgate: ltr l1off sspwrgate + * @ltr_l1off_snooze_sspwrgate: ltr l1off snooze sspwrgate + */ +struct rtsx_cr_option { + u32 dev_flags; + bool force_clkreq_0; + bool ltr_en; + bool ltr_enabled; + bool ltr_active; + u32 ltr_active_latency; + u32 ltr_idle_latency; + u32 ltr_l1off_latency; + enum dev_aspm_mode dev_aspm_mode; + u32 l1_snooze_delay; + u8 ltr_l1off_sspwrgate; + u8 ltr_l1off_snooze_sspwrgate; +}; + +#define rtsx_set_dev_flag(cr, flag) \ + ((cr)->option.dev_flags |= (flag)) +#define rtsx_clear_dev_flag(cr, flag) \ + ((cr)->option.dev_flags &= ~(flag)) +#define rtsx_check_dev_flag(cr, flag) \ + ((cr)->option.dev_flags & (flag)) + struct rtsx_pcr { struct pci_dev *pci; unsigned int id; int pcie_cap; + struct rtsx_cr_option option; /* pci resources */ unsigned long addr; @@ -940,6 +1018,7 @@ struct rtsx_pcr { u8 card_drive_sel; #define ASPM_L1_EN 0x02 u8 aspm_en; + bool aspm_enabled; #define PCR_MS_PMOS (1 << 0) #define PCR_REVERSE_SOCKET (1 << 1) @@ -964,6 +1043,11 @@ struct rtsx_pcr { u8 dma_error_count; }; +#define PID_524A 0x524A +#define PID_5249 0x5249 +#define PID_5250 0x5250 +#define PID_525A 0x525A + #define CHK_PCI_PID(pcr, pid) ((pcr)->pci->device == (pid)) #define PCI_VID(pcr) ((pcr)->pci->vendor) #define PCI_PID(pcr) ((pcr)->pci->device) -- cgit v1.2.3-59-g8ed1b From 900148296b78c61aa8c443dc594c0da968c3be53 Mon Sep 17 00:00:00 2001 From: Rakesh Pandit Date: Fri, 13 Oct 2017 14:45:50 +0200 Subject: lightnvm: prevent target type module removal when in use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If target type module e.g. pblk here is unloaded (rmmod) while module is in use (after creating target) system crashes. We fix this by using module API refcnt. Signed-off-by: Rakesh Pandit Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/core.c | 4 ++++ drivers/lightnvm/pblk-init.c | 1 + include/linux/lightnvm.h | 1 + 3 files changed, 6 insertions(+) (limited to 'include') diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c index ddae430b6eae..60e163be5a89 100644 --- a/drivers/lightnvm/core.c +++ b/drivers/lightnvm/core.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -316,6 +317,8 @@ static int nvm_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create) list_add_tail(&t->list, &dev->targets); mutex_unlock(&dev->mlock); + __module_get(tt->owner); + return 0; err_sysfs: if (tt->exit) @@ -351,6 +354,7 @@ static void __nvm_remove_target(struct nvm_target *t) nvm_remove_tgt_dev(t->dev, 1); put_disk(tdisk); + module_put(t->type->owner); list_del(&t->list); kfree(t); diff --git a/drivers/lightnvm/pblk-init.c b/drivers/lightnvm/pblk-init.c index 1b0f61233c21..6df65d14a2c5 100644 --- a/drivers/lightnvm/pblk-init.c +++ b/drivers/lightnvm/pblk-init.c @@ -1044,6 +1044,7 @@ static struct nvm_tgt_type tt_pblk = { .sysfs_init = pblk_sysfs_init, .sysfs_exit = pblk_sysfs_exit, + .owner = THIS_MODULE, }; static int __init pblk_module_init(void) diff --git a/include/linux/lightnvm.h b/include/linux/lightnvm.h index 7dfa56ebbc6d..7b80ac911d26 100644 --- a/include/linux/lightnvm.h +++ b/include/linux/lightnvm.h @@ -460,6 +460,7 @@ struct nvm_tgt_type { /* For internal use */ struct list_head list; + struct module *owner; }; extern struct nvm_tgt_type *nvm_find_target_type(const char *, int); -- cgit v1.2.3-59-g8ed1b From ef56b9ce562753cacf518f081a4ff3227efdab25 Mon Sep 17 00:00:00 2001 From: Rakesh Pandit Date: Fri, 13 Oct 2017 14:46:30 +0200 Subject: lightnvm: remove unused argument from nvm_set_tgt_bb_tbl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vblk isn't being used anyway and if we ever have a usecase we can introduce this again. This makes the logic easier and removes unnecessary checks. Signed-off-by: Rakesh Pandit Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/core.c | 29 ++++++++++++----------------- include/linux/lightnvm.h | 2 +- 2 files changed, 13 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c index 798964f511cd..231c92899431 100644 --- a/drivers/lightnvm/core.c +++ b/drivers/lightnvm/core.c @@ -633,7 +633,7 @@ int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas, memset(&rqd, 0, sizeof(struct nvm_rq)); - nvm_set_rqd_ppalist(tgt_dev, &rqd, ppas, nr_ppas, 1); + nvm_set_rqd_ppalist(tgt_dev, &rqd, ppas, nr_ppas); nvm_rq_tgt_to_dev(tgt_dev, &rqd); ret = dev->ops->set_bb_tbl(dev, &rqd.ppa_addr, rqd.nr_ppas, type); @@ -697,7 +697,7 @@ int nvm_erase_sync(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas, rqd.private = &wait; rqd.flags = geo->plane_mode >> 1; - ret = nvm_set_rqd_ppalist(tgt_dev, &rqd, ppas, nr_ppas, 1); + ret = nvm_set_rqd_ppalist(tgt_dev, &rqd, ppas, nr_ppas); if (ret) return ret; @@ -793,14 +793,14 @@ void nvm_put_area(struct nvm_tgt_dev *tgt_dev, sector_t begin) EXPORT_SYMBOL(nvm_put_area); int nvm_set_rqd_ppalist(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd, - const struct ppa_addr *ppas, int nr_ppas, int vblk) + const struct ppa_addr *ppas, int nr_ppas) { struct nvm_dev *dev = tgt_dev->parent; struct nvm_geo *geo = &tgt_dev->geo; int i, plane_cnt, pl_idx; struct ppa_addr ppa; - if ((!vblk || geo->plane_mode == NVM_PLANE_SINGLE) && nr_ppas == 1) { + if (geo->plane_mode == NVM_PLANE_SINGLE && nr_ppas == 1) { rqd->nr_ppas = nr_ppas; rqd->ppa_addr = ppas[0]; @@ -814,19 +814,14 @@ int nvm_set_rqd_ppalist(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd, return -ENOMEM; } - if (!vblk) { - for (i = 0; i < nr_ppas; i++) - rqd->ppa_list[i] = ppas[i]; - } else { - plane_cnt = geo->plane_mode; - rqd->nr_ppas *= plane_cnt; - - for (i = 0; i < nr_ppas; i++) { - for (pl_idx = 0; pl_idx < plane_cnt; pl_idx++) { - ppa = ppas[i]; - ppa.g.pl = pl_idx; - rqd->ppa_list[(pl_idx * nr_ppas) + i] = ppa; - } + plane_cnt = geo->plane_mode; + rqd->nr_ppas *= plane_cnt; + + for (i = 0; i < nr_ppas; i++) { + for (pl_idx = 0; pl_idx < plane_cnt; pl_idx++) { + ppa = ppas[i]; + ppa.g.pl = pl_idx; + rqd->ppa_list[(pl_idx * nr_ppas) + i] = ppa; } } diff --git a/include/linux/lightnvm.h b/include/linux/lightnvm.h index 7b80ac911d26..32ec35b5e18b 100644 --- a/include/linux/lightnvm.h +++ b/include/linux/lightnvm.h @@ -481,7 +481,7 @@ extern int nvm_max_phys_sects(struct nvm_tgt_dev *); extern int nvm_submit_io(struct nvm_tgt_dev *, struct nvm_rq *); extern int nvm_erase_sync(struct nvm_tgt_dev *, struct ppa_addr *, int); extern int nvm_set_rqd_ppalist(struct nvm_tgt_dev *, struct nvm_rq *, - const struct ppa_addr *, int, int); + const struct ppa_addr *, int); extern void nvm_free_rqd_ppalist(struct nvm_tgt_dev *, struct nvm_rq *); extern int nvm_get_l2p_tbl(struct nvm_tgt_dev *, u64, u32, nvm_l2p_update_fn *, void *); -- cgit v1.2.3-59-g8ed1b From eb6f168f97438bf1cac8b9b1301c662eace9e39f Mon Sep 17 00:00:00 2001 From: Rakesh Pandit Date: Fri, 13 Oct 2017 14:46:31 +0200 Subject: lightnvm: remove stale extern and unused exported symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not all exported symbols are being used outside core and there were some stale entries in lightnvm.h Signed-off-by: Rakesh Pandit Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/core.c | 129 +++++++++++++++++++++++------------------------ include/linux/lightnvm.h | 7 --- 2 files changed, 64 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c index 231c92899431..0e5f77234c79 100644 --- a/drivers/lightnvm/core.c +++ b/drivers/lightnvm/core.c @@ -226,6 +226,24 @@ static const struct block_device_operations nvm_fops = { .owner = THIS_MODULE, }; +static struct nvm_tgt_type *nvm_find_target_type(const char *name, int lock) +{ + struct nvm_tgt_type *tmp, *tt = NULL; + + if (lock) + down_write(&nvm_tgtt_lock); + + list_for_each_entry(tmp, &nvm_tgt_types, list) + if (!strcmp(name, tmp->name)) { + tt = tmp; + break; + } + + if (lock) + up_write(&nvm_tgtt_lock); + return tt; +} + static int nvm_create_tgt(struct nvm_dev *dev, struct nvm_ioctl_create *create) { struct nvm_ioctl_create_simple *s = &create->conf.s; @@ -549,25 +567,6 @@ void nvm_part_to_tgt(struct nvm_dev *dev, sector_t *entries, } EXPORT_SYMBOL(nvm_part_to_tgt); -struct nvm_tgt_type *nvm_find_target_type(const char *name, int lock) -{ - struct nvm_tgt_type *tmp, *tt = NULL; - - if (lock) - down_write(&nvm_tgtt_lock); - - list_for_each_entry(tmp, &nvm_tgt_types, list) - if (!strcmp(name, tmp->name)) { - tt = tmp; - break; - } - - if (lock) - up_write(&nvm_tgtt_lock); - return tt; -} -EXPORT_SYMBOL(nvm_find_target_type); - int nvm_register_tgt_type(struct nvm_tgt_type *tt) { int ret = 0; @@ -619,6 +618,52 @@ static struct nvm_dev *nvm_find_nvm_dev(const char *name) return NULL; } +static int nvm_set_rqd_ppalist(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd, + const struct ppa_addr *ppas, int nr_ppas) +{ + struct nvm_dev *dev = tgt_dev->parent; + struct nvm_geo *geo = &tgt_dev->geo; + int i, plane_cnt, pl_idx; + struct ppa_addr ppa; + + if (geo->plane_mode == NVM_PLANE_SINGLE && nr_ppas == 1) { + rqd->nr_ppas = nr_ppas; + rqd->ppa_addr = ppas[0]; + + return 0; + } + + rqd->nr_ppas = nr_ppas; + rqd->ppa_list = nvm_dev_dma_alloc(dev, GFP_KERNEL, &rqd->dma_ppa_list); + if (!rqd->ppa_list) { + pr_err("nvm: failed to allocate dma memory\n"); + return -ENOMEM; + } + + plane_cnt = geo->plane_mode; + rqd->nr_ppas *= plane_cnt; + + for (i = 0; i < nr_ppas; i++) { + for (pl_idx = 0; pl_idx < plane_cnt; pl_idx++) { + ppa = ppas[i]; + ppa.g.pl = pl_idx; + rqd->ppa_list[(pl_idx * nr_ppas) + i] = ppa; + } + } + + return 0; +} + +static void nvm_free_rqd_ppalist(struct nvm_tgt_dev *tgt_dev, + struct nvm_rq *rqd) +{ + if (!rqd->ppa_list) + return; + + nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list); +} + + int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas, int nr_ppas, int type) { @@ -792,52 +837,6 @@ void nvm_put_area(struct nvm_tgt_dev *tgt_dev, sector_t begin) } EXPORT_SYMBOL(nvm_put_area); -int nvm_set_rqd_ppalist(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd, - const struct ppa_addr *ppas, int nr_ppas) -{ - struct nvm_dev *dev = tgt_dev->parent; - struct nvm_geo *geo = &tgt_dev->geo; - int i, plane_cnt, pl_idx; - struct ppa_addr ppa; - - if (geo->plane_mode == NVM_PLANE_SINGLE && nr_ppas == 1) { - rqd->nr_ppas = nr_ppas; - rqd->ppa_addr = ppas[0]; - - return 0; - } - - rqd->nr_ppas = nr_ppas; - rqd->ppa_list = nvm_dev_dma_alloc(dev, GFP_KERNEL, &rqd->dma_ppa_list); - if (!rqd->ppa_list) { - pr_err("nvm: failed to allocate dma memory\n"); - return -ENOMEM; - } - - plane_cnt = geo->plane_mode; - rqd->nr_ppas *= plane_cnt; - - for (i = 0; i < nr_ppas; i++) { - for (pl_idx = 0; pl_idx < plane_cnt; pl_idx++) { - ppa = ppas[i]; - ppa.g.pl = pl_idx; - rqd->ppa_list[(pl_idx * nr_ppas) + i] = ppa; - } - } - - return 0; -} -EXPORT_SYMBOL(nvm_set_rqd_ppalist); - -void nvm_free_rqd_ppalist(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd) -{ - if (!rqd->ppa_list) - return; - - nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list); -} -EXPORT_SYMBOL(nvm_free_rqd_ppalist); - void nvm_end_io(struct nvm_rq *rqd) { struct nvm_tgt_dev *tgt_dev = rqd->dev; diff --git a/include/linux/lightnvm.h b/include/linux/lightnvm.h index 32ec35b5e18b..4f0e4a0fd204 100644 --- a/include/linux/lightnvm.h +++ b/include/linux/lightnvm.h @@ -463,8 +463,6 @@ struct nvm_tgt_type { struct module *owner; }; -extern struct nvm_tgt_type *nvm_find_target_type(const char *, int); - extern int nvm_register_tgt_type(struct nvm_tgt_type *); extern void nvm_unregister_tgt_type(struct nvm_tgt_type *); @@ -480,9 +478,6 @@ extern int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *, struct ppa_addr *, extern int nvm_max_phys_sects(struct nvm_tgt_dev *); extern int nvm_submit_io(struct nvm_tgt_dev *, struct nvm_rq *); extern int nvm_erase_sync(struct nvm_tgt_dev *, struct ppa_addr *, int); -extern int nvm_set_rqd_ppalist(struct nvm_tgt_dev *, struct nvm_rq *, - const struct ppa_addr *, int); -extern void nvm_free_rqd_ppalist(struct nvm_tgt_dev *, struct nvm_rq *); extern int nvm_get_l2p_tbl(struct nvm_tgt_dev *, u64, u32, nvm_l2p_update_fn *, void *); extern int nvm_get_area(struct nvm_tgt_dev *, sector_t *, sector_t); @@ -491,8 +486,6 @@ extern void nvm_end_io(struct nvm_rq *); extern int nvm_bb_tbl_fold(struct nvm_dev *, u8 *, int); extern int nvm_get_tgt_bb_tbl(struct nvm_tgt_dev *, struct ppa_addr, u8 *); -extern int nvm_dev_factory(struct nvm_dev *, int flags); - extern void nvm_part_to_tgt(struct nvm_dev *, sector_t *, int); #else /* CONFIG_NVM */ -- cgit v1.2.3-59-g8ed1b From 1a94b2d484677dc559c96251dd0e7c7b8811c378 Mon Sep 17 00:00:00 2001 From: Javier González Date: Fri, 13 Oct 2017 14:46:47 +0200 Subject: lightnvm: implement generic path for sync I/O MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement a generic path for sending sync I/O on LightNVM. This allows to reuse the standard synchronous path trough blk_execute_rq(), instead of implementing a wait_for_completion on the target side (e.g., pblk). Signed-off-by: Javier González Signed-off-by: Matias Bjørling Signed-off-by: Jens Axboe --- drivers/lightnvm/core.c | 25 +++++++++----- drivers/lightnvm/pblk-core.c | 74 ++++++++++++---------------------------- drivers/lightnvm/pblk-read.c | 21 ++---------- drivers/lightnvm/pblk-recovery.c | 31 ++--------------- drivers/lightnvm/pblk.h | 42 +++++++++++++++++++++-- drivers/nvme/host/lightnvm.c | 70 +++++++++++++++++++++++++++++-------- include/linux/lightnvm.h | 3 ++ 7 files changed, 143 insertions(+), 123 deletions(-) (limited to 'include') diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c index 0e5f77234c79..fe21f4dd33e9 100644 --- a/drivers/lightnvm/core.c +++ b/drivers/lightnvm/core.c @@ -720,12 +720,25 @@ int nvm_submit_io(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd) } EXPORT_SYMBOL(nvm_submit_io); -static void nvm_end_io_sync(struct nvm_rq *rqd) +int nvm_submit_io_sync(struct nvm_tgt_dev *tgt_dev, struct nvm_rq *rqd) { - struct completion *waiting = rqd->private; + struct nvm_dev *dev = tgt_dev->parent; + int ret; + + if (!dev->ops->submit_io_sync) + return -ENODEV; + + nvm_rq_tgt_to_dev(tgt_dev, rqd); - complete(waiting); + rqd->dev = tgt_dev; + + /* In case of error, fail with right address format */ + ret = dev->ops->submit_io_sync(dev, rqd); + nvm_rq_dev_to_tgt(tgt_dev, rqd); + + return ret; } +EXPORT_SYMBOL(nvm_submit_io_sync); int nvm_erase_sync(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas, int nr_ppas) @@ -733,25 +746,21 @@ int nvm_erase_sync(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas, struct nvm_geo *geo = &tgt_dev->geo; struct nvm_rq rqd; int ret; - DECLARE_COMPLETION_ONSTACK(wait); memset(&rqd, 0, sizeof(struct nvm_rq)); rqd.opcode = NVM_OP_ERASE; - rqd.end_io = nvm_end_io_sync; - rqd.private = &wait; rqd.flags = geo->plane_mode >> 1; ret = nvm_set_rqd_ppalist(tgt_dev, &rqd, ppas, nr_ppas); if (ret) return ret; - ret = nvm_submit_io(tgt_dev, &rqd); + ret = nvm_submit_io_sync(tgt_dev, &rqd); if (ret) { pr_err("rrpr: erase I/O submission failed: %d\n", ret); goto free_ppa_list; } - wait_for_completion_io(&wait); free_ppa_list: nvm_free_rqd_ppalist(tgt_dev, &rqd); diff --git a/drivers/lightnvm/pblk-core.c b/drivers/lightnvm/pblk-core.c index 4199119a0754..ce90213a42fa 100644 --- a/drivers/lightnvm/pblk-core.c +++ b/drivers/lightnvm/pblk-core.c @@ -412,39 +412,33 @@ int pblk_submit_io(struct pblk *pblk, struct nvm_rq *rqd) struct nvm_tgt_dev *dev = pblk->dev; #ifdef CONFIG_NVM_DEBUG - struct ppa_addr *ppa_list; + int ret; - ppa_list = (rqd->nr_ppas > 1) ? rqd->ppa_list : &rqd->ppa_addr; - if (pblk_boundary_ppa_checks(dev, ppa_list, rqd->nr_ppas)) { - WARN_ON(1); - return -EINVAL; - } + ret = pblk_check_io(pblk, rqd); + if (ret) + return ret; +#endif - if (rqd->opcode == NVM_OP_PWRITE) { - struct pblk_line *line; - struct ppa_addr ppa; - int i; + atomic_inc(&pblk->inflight_io); - for (i = 0; i < rqd->nr_ppas; i++) { - ppa = ppa_list[i]; - line = &pblk->lines[pblk_dev_ppa_to_line(ppa)]; + return nvm_submit_io(dev, rqd); +} - spin_lock(&line->lock); - if (line->state != PBLK_LINESTATE_OPEN) { - pr_err("pblk: bad ppa: line:%d,state:%d\n", - line->id, line->state); - WARN_ON(1); - spin_unlock(&line->lock); - return -EINVAL; - } - spin_unlock(&line->lock); - } - } +int pblk_submit_io_sync(struct pblk *pblk, struct nvm_rq *rqd) +{ + struct nvm_tgt_dev *dev = pblk->dev; + +#ifdef CONFIG_NVM_DEBUG + int ret; + + ret = pblk_check_io(pblk, rqd); + if (ret) + return ret; #endif atomic_inc(&pblk->inflight_io); - return nvm_submit_io(dev, rqd); + return nvm_submit_io_sync(dev, rqd); } static void pblk_bio_map_addr_endio(struct bio *bio) @@ -597,7 +591,6 @@ static int pblk_line_submit_emeta_io(struct pblk *pblk, struct pblk_line *line, int cmd_op, bio_op; int i, j; int ret; - DECLARE_COMPLETION_ONSTACK(wait); if (dir == PBLK_WRITE) { bio_op = REQ_OP_WRITE; @@ -639,8 +632,6 @@ next_rq: rqd.dma_ppa_list = dma_ppa_list; rqd.opcode = cmd_op; rqd.nr_ppas = rq_ppas; - rqd.end_io = pblk_end_io_sync; - rqd.private = &wait; if (dir == PBLK_WRITE) { struct pblk_sec_meta *meta_list = rqd.meta_list; @@ -694,19 +685,14 @@ next_rq: } } - ret = pblk_submit_io(pblk, &rqd); + ret = pblk_submit_io_sync(pblk, &rqd); if (ret) { pr_err("pblk: emeta I/O submission failed: %d\n", ret); bio_put(bio); goto free_rqd_dma; } - if (!wait_for_completion_io_timeout(&wait, - msecs_to_jiffies(PBLK_COMMAND_TIMEOUT_MS))) { - pr_err("pblk: emeta I/O timed out\n"); - } atomic_dec(&pblk->inflight_io); - reinit_completion(&wait); if (rqd.error) { if (dir == PBLK_WRITE) @@ -750,7 +736,6 @@ static int pblk_line_submit_smeta_io(struct pblk *pblk, struct pblk_line *line, int i, ret; int cmd_op, bio_op; int flags; - DECLARE_COMPLETION_ONSTACK(wait); if (dir == PBLK_WRITE) { bio_op = REQ_OP_WRITE; @@ -787,8 +772,6 @@ static int pblk_line_submit_smeta_io(struct pblk *pblk, struct pblk_line *line, rqd.opcode = cmd_op; rqd.flags = flags; rqd.nr_ppas = lm->smeta_sec; - rqd.end_io = pblk_end_io_sync; - rqd.private = &wait; for (i = 0; i < lm->smeta_sec; i++, paddr++) { struct pblk_sec_meta *meta_list = rqd.meta_list; @@ -807,17 +790,13 @@ static int pblk_line_submit_smeta_io(struct pblk *pblk, struct pblk_line *line, * the write thread is the only one sending write and erase commands, * there is no need to take the LUN semaphore. */ - ret = pblk_submit_io(pblk, &rqd); + ret = pblk_submit_io_sync(pblk, &rqd); if (ret) { pr_err("pblk: smeta I/O submission failed: %d\n", ret); bio_put(bio); goto free_ppa_list; } - if (!wait_for_completion_io_timeout(&wait, - msecs_to_jiffies(PBLK_COMMAND_TIMEOUT_MS))) { - pr_err("pblk: smeta I/O timed out\n"); - } atomic_dec(&pblk->inflight_io); if (rqd.error) { @@ -861,19 +840,15 @@ static int pblk_blk_erase_sync(struct pblk *pblk, struct ppa_addr ppa) { struct nvm_rq rqd; int ret = 0; - DECLARE_COMPLETION_ONSTACK(wait); memset(&rqd, 0, sizeof(struct nvm_rq)); pblk_setup_e_rq(pblk, &rqd, ppa); - rqd.end_io = pblk_end_io_sync; - rqd.private = &wait; - /* The write thread schedules erases so that it minimizes disturbances * with writes. Thus, there is no need to take the LUN semaphore. */ - ret = pblk_submit_io(pblk, &rqd); + ret = pblk_submit_io_sync(pblk, &rqd); if (ret) { struct nvm_tgt_dev *dev = pblk->dev; struct nvm_geo *geo = &dev->geo; @@ -886,11 +861,6 @@ static int pblk_blk_erase_sync(struct pblk *pblk, struct ppa_addr ppa) goto out; } - if (!wait_for_completion_io_timeout(&wait, - msecs_to_jiffies(PBLK_COMMAND_TIMEOUT_MS))) { - pr_err("pblk: sync erase timed out\n"); - } - out: rqd.private = pblk; __pblk_end_io_erase(pblk, &rqd); diff --git a/drivers/lightnvm/pblk-read.c b/drivers/lightnvm/pblk-read.c index 71c58503f1a4..ca79d8fb3e60 100644 --- a/drivers/lightnvm/pblk-read.c +++ b/drivers/lightnvm/pblk-read.c @@ -207,7 +207,6 @@ static int pblk_fill_partial_read_bio(struct pblk *pblk, struct nvm_rq *rqd, int nr_secs = rqd->nr_ppas; int nr_holes = nr_secs - bitmap_weight(read_bitmap, nr_secs); int i, ret, hole; - DECLARE_COMPLETION_ONSTACK(wait); /* Re-use allocated memory for intermediate lbas */ lba_list_mem = (((void *)rqd->ppa_list) + pblk_dma_ppa_size); @@ -232,8 +231,6 @@ static int pblk_fill_partial_read_bio(struct pblk *pblk, struct nvm_rq *rqd, rqd->bio = new_bio; rqd->nr_ppas = nr_holes; rqd->flags = pblk_set_read_mode(pblk, PBLK_READ_RANDOM); - rqd->end_io = pblk_end_io_sync; - rqd->private = &wait; if (unlikely(nr_holes == 1)) { ppa_ptr = rqd->ppa_list; @@ -241,18 +238,13 @@ static int pblk_fill_partial_read_bio(struct pblk *pblk, struct nvm_rq *rqd, rqd->ppa_addr = rqd->ppa_list[0]; } - ret = pblk_submit_read_io(pblk, rqd); + ret = pblk_submit_io_sync(pblk, rqd); if (ret) { bio_put(rqd->bio); - pr_err("pblk: read IO submission failed\n"); + pr_err("pblk: sync read IO submission failed\n"); goto err; } - if (!wait_for_completion_io_timeout(&wait, - msecs_to_jiffies(PBLK_COMMAND_TIMEOUT_MS))) { - pr_err("pblk: partial read I/O timed out\n"); - } - if (rqd->error) { atomic_long_inc(&pblk->read_failed); #ifdef CONFIG_NVM_DEBUG @@ -537,7 +529,6 @@ int pblk_submit_read_gc(struct pblk *pblk, struct pblk_gc_rq *gc_rq) struct nvm_rq rqd; int data_len; int ret = NVM_IO_OK; - DECLARE_COMPLETION_ONSTACK(wait); memset(&rqd, 0, sizeof(struct nvm_rq)); @@ -577,22 +568,16 @@ int pblk_submit_read_gc(struct pblk *pblk, struct pblk_gc_rq *gc_rq) bio_set_op_attrs(bio, REQ_OP_READ, 0); rqd.opcode = NVM_OP_PREAD; - rqd.end_io = pblk_end_io_sync; - rqd.private = &wait; rqd.nr_ppas = gc_rq->secs_to_gc; rqd.flags = pblk_set_read_mode(pblk, PBLK_READ_RANDOM); rqd.bio = bio; - if (pblk_submit_read_io(pblk, &rqd)) { + if (pblk_submit_io_sync(pblk, &rqd)) { ret = -EIO; pr_err("pblk: GC read request failed\n"); goto err_free_bio; } - if (!wait_for_completion_io_timeout(&wait, - msecs_to_jiffies(PBLK_COMMAND_TIMEOUT_MS))) { - pr_err("pblk: GC read I/O timed out\n"); - } atomic_dec(&pblk->inflight_io); if (rqd.error) { diff --git a/drivers/lightnvm/pblk-recovery.c b/drivers/lightnvm/pblk-recovery.c index 9772a947ca4f..eadb3eb5d4dc 100644 --- a/drivers/lightnvm/pblk-recovery.c +++ b/drivers/lightnvm/pblk-recovery.c @@ -216,7 +216,6 @@ static int pblk_recov_read_oob(struct pblk *pblk, struct pblk_line *line, int rq_ppas, rq_len; int i, j; int ret = 0; - DECLARE_COMPLETION_ONSTACK(wait); ppa_list = p.ppa_list; meta_list = p.meta_list; @@ -253,8 +252,6 @@ next_read_rq: rqd->ppa_list = ppa_list; rqd->dma_ppa_list = dma_ppa_list; rqd->dma_meta_list = dma_meta_list; - rqd->end_io = pblk_end_io_sync; - rqd->private = &wait; if (pblk_io_aligned(pblk, rq_ppas)) rqd->flags = pblk_set_read_mode(pblk, PBLK_READ_SEQUENTIAL); @@ -280,19 +277,13 @@ next_read_rq: } /* If read fails, more padding is needed */ - ret = pblk_submit_io(pblk, rqd); + ret = pblk_submit_io_sync(pblk, rqd); if (ret) { pr_err("pblk: I/O submission failed: %d\n", ret); return ret; } - if (!wait_for_completion_io_timeout(&wait, - msecs_to_jiffies(PBLK_COMMAND_TIMEOUT_MS))) { - pr_err("pblk: L2P recovery read timed out\n"); - return -EINTR; - } atomic_dec(&pblk->inflight_io); - reinit_completion(&wait); /* At this point, the read should not fail. If it does, it is a problem * we cannot recover from here. Need FTL log. @@ -504,7 +495,6 @@ static int pblk_recov_scan_all_oob(struct pblk *pblk, struct pblk_line *line, int ret = 0; int rec_round; int left_ppas = pblk_calc_sec_in_line(pblk, line) - line->cur_sec; - DECLARE_COMPLETION_ONSTACK(wait); ppa_list = p.ppa_list; meta_list = p.meta_list; @@ -539,8 +529,6 @@ next_rq: rqd->ppa_list = ppa_list; rqd->dma_ppa_list = dma_ppa_list; rqd->dma_meta_list = dma_meta_list; - rqd->end_io = pblk_end_io_sync; - rqd->private = &wait; if (pblk_io_aligned(pblk, rq_ppas)) rqd->flags = pblk_set_read_mode(pblk, PBLK_READ_SEQUENTIAL); @@ -566,18 +554,13 @@ next_rq: addr_to_gen_ppa(pblk, w_ptr, line->id); } - ret = pblk_submit_io(pblk, rqd); + ret = pblk_submit_io_sync(pblk, rqd); if (ret) { pr_err("pblk: I/O submission failed: %d\n", ret); return ret; } - if (!wait_for_completion_io_timeout(&wait, - msecs_to_jiffies(PBLK_COMMAND_TIMEOUT_MS))) { - pr_err("pblk: L2P recovery read timed out\n"); - } atomic_dec(&pblk->inflight_io); - reinit_completion(&wait); /* This should not happen since the read failed during normal recovery, * but the media works funny sometimes... @@ -645,7 +628,6 @@ static int pblk_recov_scan_oob(struct pblk *pblk, struct pblk_line *line, int i, j; int ret = 0; int left_ppas = pblk_calc_sec_in_line(pblk, line); - DECLARE_COMPLETION_ONSTACK(wait); ppa_list = p.ppa_list; meta_list = p.meta_list; @@ -678,8 +660,6 @@ next_rq: rqd->ppa_list = ppa_list; rqd->dma_ppa_list = dma_ppa_list; rqd->dma_meta_list = dma_meta_list; - rqd->end_io = pblk_end_io_sync; - rqd->private = &wait; if (pblk_io_aligned(pblk, rq_ppas)) rqd->flags = pblk_set_read_mode(pblk, PBLK_READ_SEQUENTIAL); @@ -705,19 +685,14 @@ next_rq: addr_to_gen_ppa(pblk, paddr, line->id); } - ret = pblk_submit_io(pblk, rqd); + ret = pblk_submit_io_sync(pblk, rqd); if (ret) { pr_err("pblk: I/O submission failed: %d\n", ret); bio_put(bio); return ret; } - if (!wait_for_completion_io_timeout(&wait, - msecs_to_jiffies(PBLK_COMMAND_TIMEOUT_MS))) { - pr_err("pblk: L2P recovery read timed out\n"); - } atomic_dec(&pblk->inflight_io); - reinit_completion(&wait); /* Reached the end of the written line */ if (rqd->error) { diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h index 6c9ea9a93704..6b64288de6f7 100644 --- a/drivers/lightnvm/pblk.h +++ b/drivers/lightnvm/pblk.h @@ -714,6 +714,7 @@ void pblk_discard(struct pblk *pblk, struct bio *bio); void pblk_log_write_err(struct pblk *pblk, struct nvm_rq *rqd); void pblk_log_read_err(struct pblk *pblk, struct nvm_rq *rqd); int pblk_submit_io(struct pblk *pblk, struct nvm_rq *rqd); +int pblk_submit_io_sync(struct pblk *pblk, struct nvm_rq *rqd); int pblk_submit_meta_io(struct pblk *pblk, struct pblk_line *meta_line); struct bio *pblk_bio_map_addr(struct pblk *pblk, void *data, unsigned int nr_secs, unsigned int len, @@ -1203,7 +1204,6 @@ static inline void pblk_print_failed_rqd(struct pblk *pblk, struct nvm_rq *rqd, pr_err("error:%d, ppa_status:%llx\n", error, rqd->ppa_status); } -#endif static inline int pblk_boundary_ppa_checks(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas, int nr_ppas) @@ -1224,14 +1224,50 @@ static inline int pblk_boundary_ppa_checks(struct nvm_tgt_dev *tgt_dev, ppa->g.sec < geo->sec_per_pg) continue; -#ifdef CONFIG_NVM_DEBUG print_ppa(ppa, "boundary", i); -#endif + return 1; } return 0; } +static inline int pblk_check_io(struct pblk *pblk, struct nvm_rq *rqd) +{ + struct nvm_tgt_dev *dev = pblk->dev; + struct ppa_addr *ppa_list; + + ppa_list = (rqd->nr_ppas > 1) ? rqd->ppa_list : &rqd->ppa_addr; + + if (pblk_boundary_ppa_checks(dev, ppa_list, rqd->nr_ppas)) { + WARN_ON(1); + return -EINVAL; + } + + if (rqd->opcode == NVM_OP_PWRITE) { + struct pblk_line *line; + struct ppa_addr ppa; + int i; + + for (i = 0; i < rqd->nr_ppas; i++) { + ppa = ppa_list[i]; + line = &pblk->lines[pblk_dev_ppa_to_line(ppa)]; + + spin_lock(&line->lock); + if (line->state != PBLK_LINESTATE_OPEN) { + pr_err("pblk: bad ppa: line:%d,state:%d\n", + line->id, line->state); + WARN_ON(1); + spin_unlock(&line->lock); + return -EINVAL; + } + spin_unlock(&line->lock); + } + } + + return 0; +} +#endif + static inline int pblk_boundary_paddr_checks(struct pblk *pblk, u64 paddr) { struct pblk_line_meta *lm = &pblk->lm; diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c index 6017153c2439..8fc949c5b49b 100644 --- a/drivers/nvme/host/lightnvm.c +++ b/drivers/nvme/host/lightnvm.c @@ -492,34 +492,47 @@ static void nvme_nvm_end_io(struct request *rq, blk_status_t status) blk_mq_free_request(rq); } -static int nvme_nvm_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd) +static struct request *nvme_nvm_alloc_request(struct request_queue *q, + struct nvm_rq *rqd, + struct nvme_nvm_command *cmd) { - struct request_queue *q = dev->q; struct nvme_ns *ns = q->queuedata; struct request *rq; - struct bio *bio = rqd->bio; - struct nvme_nvm_command *cmd; - - cmd = kzalloc(sizeof(struct nvme_nvm_command), GFP_KERNEL); - if (!cmd) - return -ENOMEM; nvme_nvm_rqtocmd(rqd, ns, cmd); rq = nvme_alloc_request(q, (struct nvme_command *)cmd, 0, NVME_QID_ANY); - if (IS_ERR(rq)) { - kfree(cmd); - return PTR_ERR(rq); - } + if (IS_ERR(rq)) + return rq; + rq->cmd_flags &= ~REQ_FAILFAST_DRIVER; - if (bio) { - blk_init_request_from_bio(rq, bio); + if (rqd->bio) { + blk_init_request_from_bio(rq, rqd->bio); } else { rq->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, IOPRIO_NORM); rq->__data_len = 0; } + return rq; +} + +static int nvme_nvm_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd) +{ + struct request_queue *q = dev->q; + struct nvme_nvm_command *cmd; + struct request *rq; + + cmd = kzalloc(sizeof(struct nvme_nvm_command), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + rq = nvme_nvm_alloc_request(q, rqd, cmd); + if (IS_ERR(rq)) { + kfree(cmd); + return PTR_ERR(rq); + } + rq->end_io_data = rqd; blk_execute_rq_nowait(q, NULL, rq, 0, nvme_nvm_end_io); @@ -527,6 +540,34 @@ static int nvme_nvm_submit_io(struct nvm_dev *dev, struct nvm_rq *rqd) return 0; } +static int nvme_nvm_submit_io_sync(struct nvm_dev *dev, struct nvm_rq *rqd) +{ + struct request_queue *q = dev->q; + struct request *rq; + struct nvme_nvm_command cmd; + int ret = 0; + + memset(&cmd, 0, sizeof(struct nvme_nvm_command)); + + rq = nvme_nvm_alloc_request(q, rqd, &cmd); + if (IS_ERR(rq)) + return PTR_ERR(rq); + + /* I/Os can fail and the error is signaled through rqd. Callers must + * handle the error accordingly. + */ + blk_execute_rq(q, NULL, rq, 0); + if (nvme_req(rq)->flags & NVME_REQ_CANCELLED) + ret = -EINTR; + + rqd->ppa_status = le64_to_cpu(nvme_req(rq)->result.u64); + rqd->error = nvme_req(rq)->status; + + blk_mq_free_request(rq); + + return ret; +} + static void *nvme_nvm_create_dma_pool(struct nvm_dev *nvmdev, char *name) { struct nvme_ns *ns = nvmdev->q->queuedata; @@ -562,6 +603,7 @@ static struct nvm_dev_ops nvme_nvm_dev_ops = { .set_bb_tbl = nvme_nvm_set_bb_tbl, .submit_io = nvme_nvm_submit_io, + .submit_io_sync = nvme_nvm_submit_io_sync, .create_dma_pool = nvme_nvm_create_dma_pool, .destroy_dma_pool = nvme_nvm_destroy_dma_pool, diff --git a/include/linux/lightnvm.h b/include/linux/lightnvm.h index 4f0e4a0fd204..b7f111ff4d3b 100644 --- a/include/linux/lightnvm.h +++ b/include/linux/lightnvm.h @@ -56,6 +56,7 @@ typedef int (nvm_get_l2p_tbl_fn)(struct nvm_dev *, u64, u32, typedef int (nvm_op_bb_tbl_fn)(struct nvm_dev *, struct ppa_addr, u8 *); typedef int (nvm_op_set_bb_fn)(struct nvm_dev *, struct ppa_addr *, int, int); typedef int (nvm_submit_io_fn)(struct nvm_dev *, struct nvm_rq *); +typedef int (nvm_submit_io_sync_fn)(struct nvm_dev *, struct nvm_rq *); typedef void *(nvm_create_dma_pool_fn)(struct nvm_dev *, char *); typedef void (nvm_destroy_dma_pool_fn)(void *); typedef void *(nvm_dev_dma_alloc_fn)(struct nvm_dev *, void *, gfp_t, @@ -69,6 +70,7 @@ struct nvm_dev_ops { nvm_op_set_bb_fn *set_bb_tbl; nvm_submit_io_fn *submit_io; + nvm_submit_io_sync_fn *submit_io_sync; nvm_create_dma_pool_fn *create_dma_pool; nvm_destroy_dma_pool_fn *destroy_dma_pool; @@ -477,6 +479,7 @@ extern int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *, struct ppa_addr *, int, int); extern int nvm_max_phys_sects(struct nvm_tgt_dev *); extern int nvm_submit_io(struct nvm_tgt_dev *, struct nvm_rq *); +extern int nvm_submit_io_sync(struct nvm_tgt_dev *, struct nvm_rq *); extern int nvm_erase_sync(struct nvm_tgt_dev *, struct ppa_addr *, int); extern int nvm_get_l2p_tbl(struct nvm_tgt_dev *, u64, u32, nvm_l2p_update_fn *, void *); -- cgit v1.2.3-59-g8ed1b From 512721a14a2a94b48ca0863d48f9829922a816b8 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Fri, 13 Oct 2017 16:08:53 +0200 Subject: drm/drm_of: Move drm_of_panel_bridge_remove_function into header. Core drm shouldn't depend on anything in drm-kms-helper, or the drm module will fail to load. insmod drm fails with [ 6087.674390] drm: Unknown symbol drm_panel_bridge_remove (err 0) which is defined in drm_kms_helper.ko This call was added by commit c70087e8f16f ("drm/drm_of: add drm_of_panel_bridge_remove function"), and the fix is defining it in the drm_of.h header, to break the circular dependency. Signed-off-by: Maarten Lankhorst Link: https://patchwork.freedesktop.org/patch/msgid/8f95e623-9480-97dc-2414-77086d8aa49d@linux.intel.com Reviewed-by: Daniel Vetter #irc Fixes: c70087e8f16f ("drm/drm_of: add drm_of_panel_bridge_remove function") Acked-by: Benjamin Gaignard --- drivers/gpu/drm/drm_of.c | 33 --------------------------------- include/drm/drm_of.h | 29 ++++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index 7a36934ea5db..4c191c050e7d 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -262,36 +262,3 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, return ret; } EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); - -#ifdef CONFIG_DRM_PANEL_BRIDGE -/* - * drm_of_panel_bridge_remove - remove panel bridge - * @np: device tree node containing panel bridge output ports - * - * Remove the panel bridge of a given DT node's port and endpoint number - * - * Returns zero if successful, or one of the standard error codes if it fails. - */ -int drm_of_panel_bridge_remove(const struct device_node *np, - int port, int endpoint) -{ - struct drm_bridge *bridge; - struct device_node *remote; - - remote = of_graph_get_remote_node(np, port, endpoint); - if (!remote) - return -ENODEV; - - bridge = of_drm_find_bridge(remote); - drm_panel_bridge_remove(bridge); - - return 0; -} -#else -int drm_of_panel_bridge_remove(const struct device_node *np, - int port, int endpoint) -{ - return -EINVAL; -} -#endif -EXPORT_SYMBOL_GPL(drm_of_panel_bridge_remove); diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index 390966e4a308..d20ec4e0431d 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -2,6 +2,9 @@ #define __DRM_OF_H__ #include +#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE) +#include +#endif struct component_master_ops; struct component_match; @@ -29,8 +32,6 @@ int drm_of_find_panel_or_bridge(const struct device_node *np, int port, int endpoint, struct drm_panel **panel, struct drm_bridge **bridge); -int drm_of_panel_bridge_remove(const struct device_node *np, - int port, int endpoint); #else static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, struct device_node *port) @@ -67,13 +68,35 @@ static inline int drm_of_find_panel_or_bridge(const struct device_node *np, { return -EINVAL; } +#endif +/* + * drm_of_panel_bridge_remove - remove panel bridge + * @np: device tree node containing panel bridge output ports + * + * Remove the panel bridge of a given DT node's port and endpoint number + * + * Returns zero if successful, or one of the standard error codes if it fails. + */ static inline int drm_of_panel_bridge_remove(const struct device_node *np, int port, int endpoint) { +#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE) + struct drm_bridge *bridge; + struct device_node *remote; + + remote = of_graph_get_remote_node(np, port, endpoint); + if (!remote) + return -ENODEV; + + bridge = of_drm_find_bridge(remote); + drm_panel_bridge_remove(bridge); + + return 0; +#else return -EINVAL; -} #endif +} static inline int drm_of_encoder_active_endpoint_id(struct device_node *node, struct drm_encoder *encoder) -- cgit v1.2.3-59-g8ed1b From e83543b495598233c3741b23bed7c82161c93e24 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Tue, 10 Oct 2017 19:12:56 -0400 Subject: tracing/xen: Hide events that are not used when X86_PAE is not defined TRACE_EVENTS() take up memory. If they are defined but not used, then they simply waste space. If their use case is behind a define, then the trace events should be as well. The trace events xen_mmu_set_pte_atomic, xen_mmu_pte_clear, and xen_mmu_pmd_clear are not used when CONFIG_X86_PAE is not defined. Link: http://lkml.kernel.org/r/20171010191256.3d6d72cb@gandalf.local.home Reviewed-by: Boris Ostrovsky Reviewed-by: Jeremy Linton Signed-off-by: Steven Rostedt (VMware) --- include/trace/events/xen.h | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/trace/events/xen.h b/include/trace/events/xen.h index 1b4fed72f573..6118d82334ec 100644 --- a/include/trace/events/xen.h +++ b/include/trace/events/xen.h @@ -147,7 +147,6 @@ DECLARE_EVENT_CLASS(xen_mmu__set_pte, TP_ARGS(ptep, pteval)) DEFINE_XEN_MMU_SET_PTE(xen_mmu_set_pte); -DEFINE_XEN_MMU_SET_PTE(xen_mmu_set_pte_atomic); TRACE_EVENT(xen_mmu_set_pte_at, TP_PROTO(struct mm_struct *mm, unsigned long addr, @@ -169,21 +168,6 @@ TRACE_EVENT(xen_mmu_set_pte_at, (int)sizeof(pteval_t) * 2, (unsigned long long)__entry->pteval) ); -TRACE_EVENT(xen_mmu_pte_clear, - TP_PROTO(struct mm_struct *mm, unsigned long addr, pte_t *ptep), - TP_ARGS(mm, addr, ptep), - TP_STRUCT__entry( - __field(struct mm_struct *, mm) - __field(unsigned long, addr) - __field(pte_t *, ptep) - ), - TP_fast_assign(__entry->mm = mm; - __entry->addr = addr; - __entry->ptep = ptep), - TP_printk("mm %p addr %lx ptep %p", - __entry->mm, __entry->addr, __entry->ptep) - ); - TRACE_DEFINE_SIZEOF(pmdval_t); TRACE_EVENT(xen_mmu_set_pmd, @@ -201,6 +185,24 @@ TRACE_EVENT(xen_mmu_set_pmd, (int)sizeof(pmdval_t) * 2, (unsigned long long)__entry->pmdval) ); +#ifdef CONFIG_X86_PAE +DEFINE_XEN_MMU_SET_PTE(xen_mmu_set_pte_atomic); + +TRACE_EVENT(xen_mmu_pte_clear, + TP_PROTO(struct mm_struct *mm, unsigned long addr, pte_t *ptep), + TP_ARGS(mm, addr, ptep), + TP_STRUCT__entry( + __field(struct mm_struct *, mm) + __field(unsigned long, addr) + __field(pte_t *, ptep) + ), + TP_fast_assign(__entry->mm = mm; + __entry->addr = addr; + __entry->ptep = ptep), + TP_printk("mm %p addr %lx ptep %p", + __entry->mm, __entry->addr, __entry->ptep) + ); + TRACE_EVENT(xen_mmu_pmd_clear, TP_PROTO(pmd_t *pmdp), TP_ARGS(pmdp), @@ -210,6 +212,7 @@ TRACE_EVENT(xen_mmu_pmd_clear, TP_fast_assign(__entry->pmdp = pmdp), TP_printk("pmdp %p", __entry->pmdp) ); +#endif #if CONFIG_PGTABLE_LEVELS >= 4 -- cgit v1.2.3-59-g8ed1b From f40a37cb4916f17806b8a89d8eb76f6943c69189 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Thu, 12 Oct 2017 18:46:32 -0400 Subject: tracing, memcg, vmscan: Hide trace events when not in use When trace events are defined but not used they still create data structures and functions for their use, even though nothing may be using them. The trace events mm_vmscan_memcg_reclaim_begin, mm_vmscan_memcg_softlimit_reclaim_begin, mm_vmscan_memcg_reclaim_end, and mm_vmscan_memcg_softlimit_reclaim_end are not used if CONFIG_MEMCG is not defined. Do not create these trace events unless CONFIG_MEMCG is defined. Link: http://lkml.kernel.org/r/20171012184632.2bd247cd@gandalf.local.home Acked-by: Michal Hocko Signed-off-by: Steven Rostedt (VMware) --- include/trace/events/vmscan.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/trace/events/vmscan.h b/include/trace/events/vmscan.h index 27e8a5c77579..cde5fea0a179 100644 --- a/include/trace/events/vmscan.h +++ b/include/trace/events/vmscan.h @@ -133,6 +133,7 @@ DEFINE_EVENT(mm_vmscan_direct_reclaim_begin_template, mm_vmscan_direct_reclaim_b TP_ARGS(order, may_writepage, gfp_flags, classzone_idx) ); +#ifdef CONFIG_MEMCG DEFINE_EVENT(mm_vmscan_direct_reclaim_begin_template, mm_vmscan_memcg_reclaim_begin, TP_PROTO(int order, int may_writepage, gfp_t gfp_flags, int classzone_idx), @@ -146,6 +147,7 @@ DEFINE_EVENT(mm_vmscan_direct_reclaim_begin_template, mm_vmscan_memcg_softlimit_ TP_ARGS(order, may_writepage, gfp_flags, classzone_idx) ); +#endif /* CONFIG_MEMCG */ DECLARE_EVENT_CLASS(mm_vmscan_direct_reclaim_end_template, @@ -171,6 +173,7 @@ DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_direct_reclaim_end TP_ARGS(nr_reclaimed) ); +#ifdef CONFIG_MEMCG DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_memcg_reclaim_end, TP_PROTO(unsigned long nr_reclaimed), @@ -184,6 +187,7 @@ DEFINE_EVENT(mm_vmscan_direct_reclaim_end_template, mm_vmscan_memcg_softlimit_re TP_ARGS(nr_reclaimed) ); +#endif /* CONFIG_MEMCG */ TRACE_EVENT(mm_shrink_slab_start, TP_PROTO(struct shrinker *shr, struct shrink_control *sc, -- cgit v1.2.3-59-g8ed1b From 75da2163dbb6af9f2dce1d80056d11d290dd19a5 Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Fri, 13 Oct 2017 11:04:23 +0200 Subject: tipc: introduce communication groups As a preparation for introducing flow control for multicast and datagram messaging we need a more strictly defined framework than we have now. A socket must be able keep track of exactly how many and which other sockets it is allowed to communicate with at any moment, and keep the necessary state for those. We therefore introduce a new concept we have named Communication Group. Sockets can join a group via a new setsockopt() call TIPC_GROUP_JOIN. The call takes four parameters: 'type' serves as group identifier, 'instance' serves as an logical member identifier, and 'scope' indicates the visibility of the group (node/cluster/zone). Finally, 'flags' makes it possible to set certain properties for the member. For now, there is only one flag, indicating if the creator of the socket wants to receive a copy of broadcast or multicast messages it is sending via the socket, and if wants to be eligible as destination for its own anycasts. A group is closed, i.e., sockets which have not joined a group will not be able to send messages to or receive messages from members of the group, and vice versa. Any member of a group can send multicast ('group broadcast') messages to all group members, optionally including itself, using the primitive send(). The messages are received via the recvmsg() primitive. A socket can only be member of one group at a time. Signed-off-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: David S. Miller --- include/uapi/linux/tipc.h | 14 ++ net/tipc/Makefile | 2 +- net/tipc/group.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++ net/tipc/group.h | 64 ++++++++ net/tipc/link.c | 3 +- net/tipc/msg.h | 50 +++++- net/tipc/name_table.c | 44 +++-- net/tipc/name_table.h | 3 + net/tipc/node.h | 3 +- net/tipc/socket.c | 209 ++++++++++++++++++++---- 10 files changed, 748 insertions(+), 48 deletions(-) create mode 100644 net/tipc/group.c create mode 100644 net/tipc/group.h (limited to 'include') diff --git a/include/uapi/linux/tipc.h b/include/uapi/linux/tipc.h index 5351b08c897a..5f7b2c4a09ab 100644 --- a/include/uapi/linux/tipc.h +++ b/include/uapi/linux/tipc.h @@ -231,6 +231,20 @@ struct sockaddr_tipc { #define TIPC_SOCK_RECVQ_DEPTH 132 /* Default: none (read only) */ #define TIPC_MCAST_BROADCAST 133 /* Default: TIPC selects. No arg */ #define TIPC_MCAST_REPLICAST 134 /* Default: TIPC selects. No arg */ +#define TIPC_GROUP_JOIN 135 /* Takes struct tipc_group_req* */ +#define TIPC_GROUP_LEAVE 136 /* No argument */ + +/* + * Flag values + */ +#define TIPC_GROUP_LOOPBACK 0x1 /* Receive copy of sent msg when match */ + +struct tipc_group_req { + __u32 type; /* group id */ + __u32 instance; /* member id */ + __u32 scope; /* zone/cluster/node */ + __u32 flags; +}; /* * Maximum sizes of TIPC bearer-related names (including terminating NULL) diff --git a/net/tipc/Makefile b/net/tipc/Makefile index 31b9f9c52974..a3af73ec0b78 100644 --- a/net/tipc/Makefile +++ b/net/tipc/Makefile @@ -8,7 +8,7 @@ tipc-y += addr.o bcast.o bearer.o \ core.o link.o discover.o msg.o \ name_distr.o subscr.o monitor.o name_table.o net.o \ netlink.o netlink_compat.o node.o socket.o eth_media.o \ - server.o socket.o + server.o socket.o group.o tipc-$(CONFIG_TIPC_MEDIA_UDP) += udp_media.o tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o diff --git a/net/tipc/group.c b/net/tipc/group.c new file mode 100644 index 000000000000..3f0e1ce1e3b9 --- /dev/null +++ b/net/tipc/group.c @@ -0,0 +1,404 @@ +/* + * net/tipc/group.c: TIPC group messaging code + * + * Copyright (c) 2017, Ericsson AB + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core.h" +#include "addr.h" +#include "group.h" +#include "bcast.h" +#include "server.h" +#include "msg.h" +#include "socket.h" +#include "node.h" +#include "name_table.h" +#include "subscr.h" + +#define ADV_UNIT (((MAX_MSG_SIZE + MAX_H_SIZE) / FLOWCTL_BLK_SZ) + 1) +#define ADV_IDLE ADV_UNIT + +enum mbr_state { + MBR_QUARANTINED, + MBR_DISCOVERED, + MBR_JOINING, + MBR_PUBLISHED, + MBR_JOINED, + MBR_LEAVING +}; + +struct tipc_member { + struct rb_node tree_node; + struct list_head list; + u32 node; + u32 port; + enum mbr_state state; + u16 bc_rcv_nxt; +}; + +struct tipc_group { + struct rb_root members; + struct tipc_nlist dests; + struct net *net; + int subid; + u32 type; + u32 instance; + u32 domain; + u32 scope; + u32 portid; + u16 member_cnt; + u16 bc_snd_nxt; + bool loopback; +}; + +static void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m, + int mtyp, struct sk_buff_head *xmitq); + +u16 tipc_group_bc_snd_nxt(struct tipc_group *grp) +{ + return grp->bc_snd_nxt; +} + +static bool tipc_group_is_receiver(struct tipc_member *m) +{ + return m && m->state >= MBR_JOINED; +} + +int tipc_group_size(struct tipc_group *grp) +{ + return grp->member_cnt; +} + +struct tipc_group *tipc_group_create(struct net *net, u32 portid, + struct tipc_group_req *mreq) +{ + struct tipc_group *grp; + u32 type = mreq->type; + + grp = kzalloc(sizeof(*grp), GFP_ATOMIC); + if (!grp) + return NULL; + tipc_nlist_init(&grp->dests, tipc_own_addr(net)); + grp->members = RB_ROOT; + grp->net = net; + grp->portid = portid; + grp->domain = addr_domain(net, mreq->scope); + grp->type = type; + grp->instance = mreq->instance; + grp->scope = mreq->scope; + grp->loopback = mreq->flags & TIPC_GROUP_LOOPBACK; + if (tipc_topsrv_kern_subscr(net, portid, type, 0, ~0, &grp->subid)) + return grp; + kfree(grp); + return NULL; +} + +void tipc_group_delete(struct net *net, struct tipc_group *grp) +{ + struct rb_root *tree = &grp->members; + struct tipc_member *m, *tmp; + struct sk_buff_head xmitq; + + __skb_queue_head_init(&xmitq); + + rbtree_postorder_for_each_entry_safe(m, tmp, tree, tree_node) { + tipc_group_proto_xmit(grp, m, GRP_LEAVE_MSG, &xmitq); + list_del(&m->list); + kfree(m); + } + tipc_node_distr_xmit(net, &xmitq); + tipc_nlist_purge(&grp->dests); + tipc_topsrv_kern_unsubscr(net, grp->subid); + kfree(grp); +} + +struct tipc_member *tipc_group_find_member(struct tipc_group *grp, + u32 node, u32 port) +{ + struct rb_node *n = grp->members.rb_node; + u64 nkey, key = (u64)node << 32 | port; + struct tipc_member *m; + + while (n) { + m = container_of(n, struct tipc_member, tree_node); + nkey = (u64)m->node << 32 | m->port; + if (key < nkey) + n = n->rb_left; + else if (key > nkey) + n = n->rb_right; + else + return m; + } + return NULL; +} + +static struct tipc_member *tipc_group_find_node(struct tipc_group *grp, + u32 node) +{ + struct tipc_member *m; + struct rb_node *n; + + for (n = rb_first(&grp->members); n; n = rb_next(n)) { + m = container_of(n, struct tipc_member, tree_node); + if (m->node == node) + return m; + } + return NULL; +} + +static void tipc_group_add_to_tree(struct tipc_group *grp, + struct tipc_member *m) +{ + u64 nkey, key = (u64)m->node << 32 | m->port; + struct rb_node **n, *parent = NULL; + struct tipc_member *tmp; + + n = &grp->members.rb_node; + while (*n) { + tmp = container_of(*n, struct tipc_member, tree_node); + parent = *n; + tmp = container_of(parent, struct tipc_member, tree_node); + nkey = (u64)tmp->node << 32 | tmp->port; + if (key < nkey) + n = &(*n)->rb_left; + else if (key > nkey) + n = &(*n)->rb_right; + else + return; + } + rb_link_node(&m->tree_node, parent, n); + rb_insert_color(&m->tree_node, &grp->members); +} + +static struct tipc_member *tipc_group_create_member(struct tipc_group *grp, + u32 node, u32 port, + int state) +{ + struct tipc_member *m; + + m = kzalloc(sizeof(*m), GFP_ATOMIC); + if (!m) + return NULL; + INIT_LIST_HEAD(&m->list); + m->node = node; + m->port = port; + grp->member_cnt++; + tipc_group_add_to_tree(grp, m); + tipc_nlist_add(&grp->dests, m->node); + m->state = state; + return m; +} + +void tipc_group_add_member(struct tipc_group *grp, u32 node, u32 port) +{ + tipc_group_create_member(grp, node, port, MBR_DISCOVERED); +} + +static void tipc_group_delete_member(struct tipc_group *grp, + struct tipc_member *m) +{ + rb_erase(&m->tree_node, &grp->members); + grp->member_cnt--; + list_del_init(&m->list); + + /* If last member on a node, remove node from dest list */ + if (!tipc_group_find_node(grp, m->node)) + tipc_nlist_del(&grp->dests, m->node); + + kfree(m); +} + +struct tipc_nlist *tipc_group_dests(struct tipc_group *grp) +{ + return &grp->dests; +} + +void tipc_group_self(struct tipc_group *grp, struct tipc_name_seq *seq, + int *scope) +{ + seq->type = grp->type; + seq->lower = grp->instance; + seq->upper = grp->instance; + *scope = grp->scope; +} + +void tipc_group_update_bc_members(struct tipc_group *grp) +{ + grp->bc_snd_nxt++; +} + +/* tipc_group_filter_msg() - determine if we should accept arriving message + */ +void tipc_group_filter_msg(struct tipc_group *grp, struct sk_buff_head *inputq, + struct sk_buff_head *xmitq) +{ + struct sk_buff *skb = __skb_dequeue(inputq); + struct tipc_member *m; + struct tipc_msg *hdr; + u32 node, port; + int mtyp; + + if (!skb) + return; + + hdr = buf_msg(skb); + mtyp = msg_type(hdr); + node = msg_orignode(hdr); + port = msg_origport(hdr); + + if (!msg_in_group(hdr)) + goto drop; + + m = tipc_group_find_member(grp, node, port); + if (!tipc_group_is_receiver(m)) + goto drop; + + __skb_queue_tail(inputq, skb); + + m->bc_rcv_nxt = msg_grp_bc_seqno(hdr) + 1; + return; +drop: + kfree_skb(skb); +} + +static void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m, + int mtyp, struct sk_buff_head *xmitq) +{ + struct tipc_msg *hdr; + struct sk_buff *skb; + + skb = tipc_msg_create(GROUP_PROTOCOL, mtyp, INT_H_SIZE, 0, + m->node, tipc_own_addr(grp->net), + m->port, grp->portid, 0); + if (!skb) + return; + + hdr = buf_msg(skb); + if (mtyp == GRP_JOIN_MSG) + msg_set_grp_bc_syncpt(hdr, grp->bc_snd_nxt); + __skb_queue_tail(xmitq, skb); +} + +void tipc_group_proto_rcv(struct tipc_group *grp, struct tipc_msg *hdr, + struct sk_buff_head *xmitq) +{ + u32 node = msg_orignode(hdr); + u32 port = msg_origport(hdr); + struct tipc_member *m; + + if (!grp) + return; + + m = tipc_group_find_member(grp, node, port); + + switch (msg_type(hdr)) { + case GRP_JOIN_MSG: + if (!m) + m = tipc_group_create_member(grp, node, port, + MBR_QUARANTINED); + if (!m) + return; + m->bc_rcv_nxt = msg_grp_bc_syncpt(hdr); + + /* Wait until PUBLISH event is received */ + if (m->state == MBR_DISCOVERED) + m->state = MBR_JOINING; + else if (m->state == MBR_PUBLISHED) + m->state = MBR_JOINED; + return; + case GRP_LEAVE_MSG: + if (!m) + return; + + /* Wait until WITHDRAW event is received */ + if (m->state != MBR_LEAVING) { + m->state = MBR_LEAVING; + return; + } + /* Otherwise deliver already received WITHDRAW event */ + tipc_group_delete_member(grp, m); + return; + default: + pr_warn("Received unknown GROUP_PROTO message\n"); + } +} + +/* tipc_group_member_evt() - receive and handle a member up/down event + */ +void tipc_group_member_evt(struct tipc_group *grp, + struct sk_buff *skb, + struct sk_buff_head *xmitq) +{ + struct tipc_msg *hdr = buf_msg(skb); + struct tipc_event *evt = (void *)msg_data(hdr); + u32 node = evt->port.node; + u32 port = evt->port.ref; + struct tipc_member *m; + struct net *net; + u32 self; + + if (!grp) + goto drop; + + net = grp->net; + self = tipc_own_addr(net); + if (!grp->loopback && node == self && port == grp->portid) + goto drop; + + m = tipc_group_find_member(grp, node, port); + + if (evt->event == TIPC_PUBLISHED) { + if (!m) + m = tipc_group_create_member(grp, node, port, + MBR_DISCOVERED); + if (!m) + goto drop; + + /* Wait if JOIN message not yet received */ + if (m->state == MBR_DISCOVERED) + m->state = MBR_PUBLISHED; + else + m->state = MBR_JOINED; + tipc_group_proto_xmit(grp, m, GRP_JOIN_MSG, xmitq); + } else if (evt->event == TIPC_WITHDRAWN) { + if (!m) + goto drop; + + /* Keep back event if more messages might be expected */ + if (m->state != MBR_LEAVING && tipc_node_is_up(net, node)) + m->state = MBR_LEAVING; + else + tipc_group_delete_member(grp, m); + } +drop: + kfree_skb(skb); +} diff --git a/net/tipc/group.h b/net/tipc/group.h new file mode 100644 index 000000000000..9bdf4479fc03 --- /dev/null +++ b/net/tipc/group.h @@ -0,0 +1,64 @@ +/* + * net/tipc/group.h: Include file for TIPC group unicast/multicast functions + * + * Copyright (c) 2017, Ericsson AB + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TIPC_GROUP_H +#define _TIPC_GROUP_H + +#include "core.h" + +struct tipc_group; +struct tipc_member; +struct tipc_msg; + +struct tipc_group *tipc_group_create(struct net *net, u32 portid, + struct tipc_group_req *mreq); +void tipc_group_delete(struct net *net, struct tipc_group *grp); +void tipc_group_add_member(struct tipc_group *grp, u32 node, u32 port); +struct tipc_nlist *tipc_group_dests(struct tipc_group *grp); +void tipc_group_self(struct tipc_group *grp, struct tipc_name_seq *seq, + int *scope); +void tipc_group_filter_msg(struct tipc_group *grp, + struct sk_buff_head *inputq, + struct sk_buff_head *xmitq); +void tipc_group_member_evt(struct tipc_group *grp, + struct sk_buff *skb, + struct sk_buff_head *xmitq); +void tipc_group_proto_rcv(struct tipc_group *grp, + struct tipc_msg *hdr, + struct sk_buff_head *xmitq); +void tipc_group_update_bc_members(struct tipc_group *grp); +u16 tipc_group_bc_snd_nxt(struct tipc_group *grp); +int tipc_group_size(struct tipc_group *grp); +#endif diff --git a/net/tipc/link.c b/net/tipc/link.c index ac0144f532aa..bd25bff63925 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -1046,11 +1046,12 @@ static bool tipc_data_input(struct tipc_link *l, struct sk_buff *skb, case TIPC_MEDIUM_IMPORTANCE: case TIPC_HIGH_IMPORTANCE: case TIPC_CRITICAL_IMPORTANCE: - if (unlikely(msg_type(hdr) == TIPC_MCAST_MSG)) { + if (unlikely(msg_mcast(hdr))) { skb_queue_tail(l->bc_rcvlink->inputq, skb); return true; } case CONN_MANAGER: + case GROUP_PROTOCOL: skb_queue_tail(inputq, skb); return true; case NAME_DISTRIBUTOR: diff --git a/net/tipc/msg.h b/net/tipc/msg.h index be3e38aa9dd2..dad400935405 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -1,7 +1,7 @@ /* * net/tipc/msg.h: Include file for TIPC message header routines * - * Copyright (c) 2000-2007, 2014-2015 Ericsson AB + * Copyright (c) 2000-2007, 2014-2017 Ericsson AB * Copyright (c) 2005-2008, 2010-2011, Wind River Systems * All rights reserved. * @@ -61,10 +61,11 @@ struct plist; /* * Payload message types */ -#define TIPC_CONN_MSG 0 -#define TIPC_MCAST_MSG 1 -#define TIPC_NAMED_MSG 2 -#define TIPC_DIRECT_MSG 3 +#define TIPC_CONN_MSG 0 +#define TIPC_MCAST_MSG 1 +#define TIPC_NAMED_MSG 2 +#define TIPC_DIRECT_MSG 3 +#define TIPC_GRP_BCAST_MSG 4 /* * Internal message users @@ -73,6 +74,7 @@ struct plist; #define MSG_BUNDLER 6 #define LINK_PROTOCOL 7 #define CONN_MANAGER 8 +#define GROUP_PROTOCOL 9 #define TUNNEL_PROTOCOL 10 #define NAME_DISTRIBUTOR 11 #define MSG_FRAGMENTER 12 @@ -87,6 +89,7 @@ struct plist; #define BASIC_H_SIZE 32 /* Basic payload message */ #define NAMED_H_SIZE 40 /* Named payload message */ #define MCAST_H_SIZE 44 /* Multicast payload message */ +#define GROUP_H_SIZE 44 /* Group payload message */ #define INT_H_SIZE 40 /* Internal messages */ #define MIN_H_SIZE 24 /* Smallest legal TIPC header size */ #define MAX_H_SIZE 60 /* Largest possible TIPC header size */ @@ -252,6 +255,11 @@ static inline void msg_set_type(struct tipc_msg *m, u32 n) msg_set_bits(m, 1, 29, 0x7, n); } +static inline int msg_in_group(struct tipc_msg *m) +{ + return (msg_type(m) == TIPC_GRP_BCAST_MSG); +} + static inline u32 msg_named(struct tipc_msg *m) { return msg_type(m) == TIPC_NAMED_MSG; @@ -259,7 +267,9 @@ static inline u32 msg_named(struct tipc_msg *m) static inline u32 msg_mcast(struct tipc_msg *m) { - return msg_type(m) == TIPC_MCAST_MSG; + int mtyp = msg_type(m); + + return ((mtyp == TIPC_MCAST_MSG) || (mtyp == TIPC_GRP_BCAST_MSG)); } static inline u32 msg_connected(struct tipc_msg *m) @@ -514,6 +524,12 @@ static inline void msg_set_nameupper(struct tipc_msg *m, u32 n) #define DSC_REQ_MSG 0 #define DSC_RESP_MSG 1 +/* + * Group protocol message types + */ +#define GRP_JOIN_MSG 0 +#define GRP_LEAVE_MSG 1 + /* * Word 1 */ @@ -795,6 +811,28 @@ static inline void msg_set_link_tolerance(struct tipc_msg *m, u32 n) msg_set_bits(m, 9, 0, 0xffff, n); } +static inline u16 msg_grp_bc_syncpt(struct tipc_msg *m) +{ + return msg_bits(m, 9, 16, 0xffff); +} + +static inline void msg_set_grp_bc_syncpt(struct tipc_msg *m, u16 n) +{ + msg_set_bits(m, 9, 16, 0xffff, n); +} + +/* Word 10 + */ +static inline u16 msg_grp_bc_seqno(struct tipc_msg *m) +{ + return msg_bits(m, 10, 16, 0xffff); +} + +static inline void msg_set_grp_bc_seqno(struct tipc_msg *m, u32 n) +{ + msg_set_bits(m, 10, 16, 0xffff, n); +} + static inline bool msg_peer_link_is_up(struct tipc_msg *m) { if (likely(msg_user(m) != LINK_PROTOCOL)) diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c index 76bd2777baaf..114d72bab827 100644 --- a/net/tipc/name_table.c +++ b/net/tipc/name_table.c @@ -43,6 +43,7 @@ #include "bcast.h" #include "addr.h" #include "node.h" +#include "group.h" #include #define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */ @@ -596,18 +597,6 @@ not_found: return ref; } -/** - * tipc_nametbl_mc_translate - find multicast destinations - * - * Creates list of all local ports that overlap the given multicast address; - * also determines if any off-node ports overlap. - * - * Note: Publications with a scope narrower than 'limit' are ignored. - * (i.e. local node-scope publications mustn't receive messages arriving - * from another node, even if the multcast link brought it here) - * - * Returns non-zero if any off-node ports overlap - */ int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper, u32 limit, struct list_head *dports) { @@ -679,6 +668,37 @@ exit: rcu_read_unlock(); } +/* tipc_nametbl_build_group - build list of communication group members + */ +void tipc_nametbl_build_group(struct net *net, struct tipc_group *grp, + u32 type, u32 domain) +{ + struct sub_seq *sseq, *stop; + struct name_info *info; + struct publication *p; + struct name_seq *seq; + + rcu_read_lock(); + seq = nametbl_find_seq(net, type); + if (!seq) + goto exit; + + spin_lock_bh(&seq->lock); + sseq = seq->sseqs; + stop = seq->sseqs + seq->first_free; + for (; sseq != stop; sseq++) { + info = sseq->info; + list_for_each_entry(p, &info->zone_list, zone_list) { + if (!tipc_in_scope(domain, p->node)) + continue; + tipc_group_add_member(grp, p->node, p->ref); + } + } + spin_unlock_bh(&seq->lock); +exit: + rcu_read_unlock(); +} + /* * tipc_nametbl_publish - add name publication to network name tables */ diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h index d121175a92b5..97646b17a4a2 100644 --- a/net/tipc/name_table.h +++ b/net/tipc/name_table.h @@ -40,6 +40,7 @@ struct tipc_subscription; struct tipc_plist; struct tipc_nlist; +struct tipc_group; /* * TIPC name types reserved for internal TIPC use (both current and planned) @@ -101,6 +102,8 @@ int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb); u32 tipc_nametbl_translate(struct net *net, u32 type, u32 instance, u32 *node); int tipc_nametbl_mc_translate(struct net *net, u32 type, u32 lower, u32 upper, u32 limit, struct list_head *dports); +void tipc_nametbl_build_group(struct net *net, struct tipc_group *grp, + u32 type, u32 domain); void tipc_nametbl_lookup_dst_nodes(struct net *net, u32 type, u32 lower, u32 upper, u32 domain, struct tipc_nlist *nodes); diff --git a/net/tipc/node.h b/net/tipc/node.h index df2f2197c4ad..acd58d23a70e 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -48,7 +48,8 @@ enum { TIPC_BCAST_SYNCH = (1 << 1), TIPC_BCAST_STATE_NACK = (1 << 2), TIPC_BLOCK_FLOWCTL = (1 << 3), - TIPC_BCAST_RCAST = (1 << 4) + TIPC_BCAST_RCAST = (1 << 4), + TIPC_MCAST_GROUPS = (1 << 5) }; #define TIPC_NODE_CAPABILITIES (TIPC_BCAST_SYNCH | \ diff --git a/net/tipc/socket.c b/net/tipc/socket.c index daf7c4df4531..64bbf9d03629 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -1,7 +1,7 @@ /* * net/tipc/socket.c: TIPC socket API * - * Copyright (c) 2001-2007, 2012-2016, Ericsson AB + * Copyright (c) 2001-2007, 2012-2017, Ericsson AB * Copyright (c) 2004-2008, 2010-2013, Wind River Systems * All rights reserved. * @@ -45,6 +45,7 @@ #include "socket.h" #include "bcast.h" #include "netlink.h" +#include "group.h" #define CONN_TIMEOUT_DEFAULT 8000 /* default connect timeout = 8s */ #define CONN_PROBING_INTERVAL msecs_to_jiffies(3600000) /* [ms] => 1 h */ @@ -78,7 +79,7 @@ enum { * @conn_timeout: the time we can wait for an unresponded setup request * @dupl_rcvcnt: number of bytes counted twice, in both backlog and rcv queue * @cong_link_cnt: number of congested links - * @sent_unacked: # messages sent by socket, and not yet acked by peer + * @snt_unacked: # messages sent by socket, and not yet acked by peer * @rcv_unacked: # messages read by user, but not yet acked back to peer * @peer: 'connected' peer for dgram/rdm * @node: hash table node @@ -109,6 +110,7 @@ struct tipc_sock { struct rhash_head node; struct tipc_mc_method mc_method; struct rcu_head rcu; + struct tipc_group *group; }; static int tipc_sk_backlog_rcv(struct sock *sk, struct sk_buff *skb); @@ -123,6 +125,7 @@ static int tipc_sk_publish(struct tipc_sock *tsk, uint scope, struct tipc_name_seq const *seq); static int tipc_sk_withdraw(struct tipc_sock *tsk, uint scope, struct tipc_name_seq const *seq); +static int tipc_sk_leave(struct tipc_sock *tsk); static struct tipc_sock *tipc_sk_lookup(struct net *net, u32 portid); static int tipc_sk_insert(struct tipc_sock *tsk); static void tipc_sk_remove(struct tipc_sock *tsk); @@ -559,6 +562,7 @@ static int tipc_release(struct socket *sock) __tipc_shutdown(sock, TIPC_ERR_NO_PORT); sk->sk_shutdown = SHUTDOWN_MASK; + tipc_sk_leave(tsk); tipc_sk_withdraw(tsk, 0, NULL); sk_stop_timer(sk, &sk->sk_timer); tipc_sk_remove(tsk); @@ -601,7 +605,10 @@ static int tipc_bind(struct socket *sock, struct sockaddr *uaddr, res = tipc_sk_withdraw(tsk, 0, NULL); goto exit; } - + if (tsk->group) { + res = -EACCES; + goto exit; + } if (uaddr_len < sizeof(struct sockaddr_tipc)) { res = -EINVAL; goto exit; @@ -698,6 +705,7 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); + struct tipc_group *grp = tsk->group; u32 mask = 0; sock_poll_wait(file, sk_sleep(sk), wait); @@ -718,8 +726,9 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, mask |= (POLLIN | POLLRDNORM); break; case TIPC_OPEN: - if (!tsk->cong_link_cnt) - mask |= POLLOUT; + if (!grp || tipc_group_size(grp)) + if (!tsk->cong_link_cnt) + mask |= POLLOUT; if (tipc_sk_type_connectionless(sk) && (!skb_queue_empty(&sk->sk_receive_queue))) mask |= (POLLIN | POLLRDNORM); @@ -757,6 +766,9 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, struct tipc_nlist dsts; int rc; + if (tsk->group) + return -EACCES; + /* Block or return if any destination link is congested */ rc = tipc_wait_for_cond(sock, &timeout, !tsk->cong_link_cnt); if (unlikely(rc)) @@ -793,6 +805,64 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, return rc ? rc : dlen; } +/** + * tipc_send_group_bcast - send message to all members in communication group + * @sk: socket structure + * @m: message to send + * @dlen: total length of message data + * @timeout: timeout to wait for wakeup + * + * Called from function tipc_sendmsg(), which has done all sanity checks + * Returns the number of bytes sent on success, or errno + */ +static int tipc_send_group_bcast(struct socket *sock, struct msghdr *m, + int dlen, long timeout) +{ + struct sock *sk = sock->sk; + struct net *net = sock_net(sk); + struct tipc_sock *tsk = tipc_sk(sk); + struct tipc_group *grp = tsk->group; + struct tipc_nlist *dsts = tipc_group_dests(grp); + struct tipc_mc_method *method = &tsk->mc_method; + struct tipc_msg *hdr = &tsk->phdr; + int mtu = tipc_bcast_get_mtu(net); + struct sk_buff_head pkts; + int rc = -EHOSTUNREACH; + + if (!dsts->local && !dsts->remote) + return -EHOSTUNREACH; + + /* Block or return if any destination link is congested */ + rc = tipc_wait_for_cond(sock, &timeout, !tsk->cong_link_cnt); + if (unlikely(rc)) + return rc; + + /* Complete message header */ + msg_set_type(hdr, TIPC_GRP_BCAST_MSG); + msg_set_hdr_sz(hdr, MCAST_H_SIZE); + msg_set_destport(hdr, 0); + msg_set_destnode(hdr, 0); + msg_set_nameinst(hdr, 0); + msg_set_grp_bc_seqno(hdr, tipc_group_bc_snd_nxt(grp)); + + /* Build message as chain of buffers */ + skb_queue_head_init(&pkts); + rc = tipc_msg_build(hdr, m, 0, dlen, mtu, &pkts); + if (unlikely(rc != dlen)) + return rc; + + /* Send message */ + rc = tipc_mcast_xmit(net, &pkts, method, dsts, + &tsk->cong_link_cnt); + if (unlikely(rc)) + return rc; + + /* Update broadcast sequence number */ + tipc_group_update_bc_members(tsk->group); + + return dlen; +} + /** * tipc_sk_mcast_rcv - Deliver multicast messages to all destination sockets * @arrvq: queue with arriving messages, to be cloned after destination lookup @@ -803,13 +873,15 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq, void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq, struct sk_buff_head *inputq) { - struct tipc_msg *msg; - struct list_head dports; - u32 portid; u32 scope = TIPC_CLUSTER_SCOPE; - struct sk_buff_head tmpq; - uint hsz; + u32 self = tipc_own_addr(net); struct sk_buff *skb, *_skb; + u32 lower = 0, upper = ~0; + struct sk_buff_head tmpq; + u32 portid, oport, onode; + struct list_head dports; + struct tipc_msg *msg; + int hsz; __skb_queue_head_init(&tmpq); INIT_LIST_HEAD(&dports); @@ -818,14 +890,18 @@ void tipc_sk_mcast_rcv(struct net *net, struct sk_buff_head *arrvq, for (; skb; skb = tipc_skb_peek(arrvq, &inputq->lock)) { msg = buf_msg(skb); hsz = skb_headroom(skb) + msg_hdr_sz(msg); - - if (in_own_node(net, msg_orignode(msg))) + oport = msg_origport(msg); + onode = msg_orignode(msg); + if (onode == self) scope = TIPC_NODE_SCOPE; /* Create destination port list and message clones: */ - tipc_nametbl_mc_translate(net, - msg_nametype(msg), msg_namelower(msg), - msg_nameupper(msg), scope, &dports); + if (!msg_in_group(msg)) { + lower = msg_namelower(msg); + upper = msg_nameupper(msg); + } + tipc_nametbl_mc_translate(net, msg_nametype(msg), lower, upper, + scope, &dports); while (tipc_dest_pop(&dports, NULL, &portid)) { _skb = __pskb_copy(skb, hsz, GFP_ATOMIC); if (_skb) { @@ -895,10 +971,6 @@ exit: kfree_skb(skb); } -static void tipc_sk_top_evt(struct tipc_sock *tsk, struct tipc_event *evt) -{ -} - /** * tipc_sendmsg - send message in connectionless manner * @sock: socket structure @@ -934,6 +1006,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) long timeout = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT); struct list_head *clinks = &tsk->cong_links; bool syn = !tipc_sk_type_connectionless(sk); + struct tipc_group *grp = tsk->group; struct tipc_msg *hdr = &tsk->phdr; struct tipc_name_seq *seq; struct sk_buff_head pkts; @@ -944,6 +1017,9 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen) if (unlikely(dlen > TIPC_MAX_USER_MSG_SIZE)) return -EMSGSIZE; + if (unlikely(grp)) + return tipc_send_group_bcast(sock, m, dlen, timeout); + if (unlikely(!dest)) { dest = &tsk->peer; if (!syn || dest->family != AF_TIPC) @@ -1543,6 +1619,7 @@ static void tipc_sk_proto_rcv(struct sock *sk, struct sk_buff *skb = __skb_dequeue(inputq); struct tipc_sock *tsk = tipc_sk(sk); struct tipc_msg *hdr = buf_msg(skb); + struct tipc_group *grp = tsk->group; switch (msg_user(hdr)) { case CONN_MANAGER: @@ -1553,8 +1630,12 @@ static void tipc_sk_proto_rcv(struct sock *sk, tsk->cong_link_cnt--; sk->sk_write_space(sk); break; + case GROUP_PROTOCOL: + tipc_group_proto_rcv(grp, hdr, xmitq); + break; case TOP_SRV: - tipc_sk_top_evt(tsk, (void *)msg_data(hdr)); + tipc_group_member_evt(tsk->group, skb, xmitq); + skb = NULL; break; default: break; @@ -1699,6 +1780,7 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb, { bool sk_conn = !tipc_sk_type_connectionless(sk); struct tipc_sock *tsk = tipc_sk(sk); + struct tipc_group *grp = tsk->group; struct tipc_msg *hdr = buf_msg(skb); struct net *net = sock_net(sk); struct sk_buff_head inputq; @@ -1710,15 +1792,19 @@ static void tipc_sk_filter_rcv(struct sock *sk, struct sk_buff *skb, if (unlikely(!msg_isdata(hdr))) tipc_sk_proto_rcv(sk, &inputq, xmitq); - else if (unlikely(msg_type(hdr) > TIPC_DIRECT_MSG)) + else if (unlikely(msg_type(hdr) > TIPC_GRP_BCAST_MSG)) return kfree_skb(skb); + if (unlikely(grp)) + tipc_group_filter_msg(grp, &inputq, xmitq); + /* Validate and add to receive buffer if there is space */ while ((skb = __skb_dequeue(&inputq))) { hdr = buf_msg(skb); limit = rcvbuf_limit(sk, skb); if ((sk_conn && !tipc_sk_filter_connect(tsk, skb)) || - (!sk_conn && msg_connected(hdr))) + (!sk_conn && msg_connected(hdr)) || + (!grp && msg_in_group(hdr))) err = TIPC_ERR_NO_PORT; else if (sk_rmem_alloc_get(sk) + skb->truesize >= limit) err = TIPC_ERR_OVERLOAD; @@ -1837,7 +1923,6 @@ void tipc_sk_rcv(struct net *net, struct sk_buff_head *inputq) sock_put(sk); continue; } - /* No destination socket => dequeue skb if still there */ skb = tipc_skb_dequeue(inputq, dport); if (!skb) @@ -1905,6 +1990,11 @@ static int tipc_connect(struct socket *sock, struct sockaddr *dest, lock_sock(sk); + if (tsk->group) { + res = -EINVAL; + goto exit; + } + if (dst->family == AF_UNSPEC) { memset(&tsk->peer, 0, sizeof(struct sockaddr_tipc)); if (!tipc_sk_type_connectionless(sk)) @@ -2341,6 +2431,52 @@ void tipc_sk_rht_destroy(struct net *net) rhashtable_destroy(&tn->sk_rht); } +static int tipc_sk_join(struct tipc_sock *tsk, struct tipc_group_req *mreq) +{ + struct net *net = sock_net(&tsk->sk); + u32 domain = addr_domain(net, mreq->scope); + struct tipc_group *grp = tsk->group; + struct tipc_msg *hdr = &tsk->phdr; + struct tipc_name_seq seq; + int rc; + + if (mreq->type < TIPC_RESERVED_TYPES) + return -EACCES; + if (grp) + return -EACCES; + grp = tipc_group_create(net, tsk->portid, mreq); + if (!grp) + return -ENOMEM; + tsk->group = grp; + msg_set_lookup_scope(hdr, mreq->scope); + msg_set_nametype(hdr, mreq->type); + msg_set_dest_droppable(hdr, true); + seq.type = mreq->type; + seq.lower = mreq->instance; + seq.upper = seq.lower; + tipc_nametbl_build_group(net, grp, mreq->type, domain); + rc = tipc_sk_publish(tsk, mreq->scope, &seq); + if (rc) + tipc_group_delete(net, grp); + return rc; +} + +static int tipc_sk_leave(struct tipc_sock *tsk) +{ + struct net *net = sock_net(&tsk->sk); + struct tipc_group *grp = tsk->group; + struct tipc_name_seq seq; + int scope; + + if (!grp) + return -EINVAL; + tipc_group_self(grp, &seq, &scope); + tipc_group_delete(net, grp); + tsk->group = NULL; + tipc_sk_withdraw(tsk, scope, &seq); + return 0; +} + /** * tipc_setsockopt - set socket option * @sock: socket structure @@ -2359,6 +2495,7 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt, { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); + struct tipc_group_req mreq; u32 value = 0; int res = 0; @@ -2374,9 +2511,14 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt, case TIPC_CONN_TIMEOUT: if (ol < sizeof(value)) return -EINVAL; - res = get_user(value, (u32 __user *)ov); - if (res) - return res; + if (get_user(value, (u32 __user *)ov)) + return -EFAULT; + break; + case TIPC_GROUP_JOIN: + if (ol < sizeof(mreq)) + return -EINVAL; + if (copy_from_user(&mreq, ov, sizeof(mreq))) + return -EFAULT; break; default: if (ov || ol) @@ -2409,6 +2551,12 @@ static int tipc_setsockopt(struct socket *sock, int lvl, int opt, tsk->mc_method.rcast = true; tsk->mc_method.mandatory = true; break; + case TIPC_GROUP_JOIN: + res = tipc_sk_join(tsk, &mreq); + break; + case TIPC_GROUP_LEAVE: + res = tipc_sk_leave(tsk); + break; default: res = -EINVAL; } @@ -2436,7 +2584,8 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, { struct sock *sk = sock->sk; struct tipc_sock *tsk = tipc_sk(sk); - int len; + struct tipc_name_seq seq; + int len, scope; u32 value; int res; @@ -2470,6 +2619,12 @@ static int tipc_getsockopt(struct socket *sock, int lvl, int opt, case TIPC_SOCK_RECVQ_DEPTH: value = skb_queue_len(&sk->sk_receive_queue); break; + case TIPC_GROUP_JOIN: + seq.type = 0; + if (tsk->group) + tipc_group_self(tsk->group, &seq, &scope); + value = seq.type; + break; default: res = -EINVAL; } -- cgit v1.2.3-59-g8ed1b From ae236fb208a6fbbd2e7a6913385e8fb78ac807f8 Mon Sep 17 00:00:00 2001 From: Jon Maloy Date: Fri, 13 Oct 2017 11:04:25 +0200 Subject: tipc: receive group membership events via member socket Like with any other service, group members' availability can be subscribed for by connecting to be topology server. However, because the events arrive via a different socket than the member socket, there is a real risk that membership events my arrive out of synch with the actual JOIN/LEAVE action. I.e., it is possible to receive the first messages from a new member before the corresponding JOIN event arrives, just as it is possible to receive the last messages from a leaving member after the LEAVE event has already been received. Since each member socket is internally also subscribing for membership events, we now fix this problem by passing those events on to the user via the member socket. We leverage the already present member synch- ronization protocol to guarantee correct message/event order. An event is delivered to the user as an empty message where the two source addresses identify the new/lost member. Furthermore, we set the MSG_OOB bit in the message flags to mark it as an event. If the event is an indication about a member loss we also set the MSG_EOR bit, so it can be distinguished from a member addition event. Signed-off-by: Jon Maloy Acked-by: Ying Xue Signed-off-by: David S. Miller --- include/uapi/linux/tipc.h | 1 + net/tipc/group.c | 60 +++++++++++++++++++++++++++++++++++++---------- net/tipc/group.h | 2 ++ net/tipc/msg.h | 22 +++++++++++++++-- net/tipc/socket.c | 49 ++++++++++++++++++++++++-------------- 5 files changed, 101 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/tipc.h b/include/uapi/linux/tipc.h index 5f7b2c4a09ab..ef41c11a7f38 100644 --- a/include/uapi/linux/tipc.h +++ b/include/uapi/linux/tipc.h @@ -238,6 +238,7 @@ struct sockaddr_tipc { * Flag values */ #define TIPC_GROUP_LOOPBACK 0x1 /* Receive copy of sent msg when match */ +#define TIPC_GROUP_MEMBER_EVTS 0x2 /* Receive membership events in socket */ struct tipc_group_req { __u32 type; /* group id */ diff --git a/net/tipc/group.c b/net/tipc/group.c index beb214a3420c..1bfa9348b26d 100644 --- a/net/tipc/group.c +++ b/net/tipc/group.c @@ -59,6 +59,7 @@ enum mbr_state { struct tipc_member { struct rb_node tree_node; struct list_head list; + struct sk_buff *event_msg; u32 node; u32 port; u32 instance; @@ -79,6 +80,7 @@ struct tipc_group { u16 member_cnt; u16 bc_snd_nxt; bool loopback; + bool events; }; static void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m, @@ -117,6 +119,7 @@ struct tipc_group *tipc_group_create(struct net *net, u32 portid, grp->instance = mreq->instance; grp->scope = mreq->scope; grp->loopback = mreq->flags & TIPC_GROUP_LOOPBACK; + grp->events = mreq->flags & TIPC_GROUP_MEMBER_EVTS; if (tipc_topsrv_kern_subscr(net, portid, type, 0, ~0, &grp->subid)) return grp; kfree(grp); @@ -279,6 +282,13 @@ void tipc_group_filter_msg(struct tipc_group *grp, struct sk_buff_head *inputq, if (!msg_in_group(hdr)) goto drop; + if (mtyp == TIPC_GRP_MEMBER_EVT) { + if (!grp->events) + goto drop; + __skb_queue_tail(inputq, skb); + return; + } + m = tipc_group_find_member(grp, node, port); if (!tipc_group_is_receiver(m)) goto drop; @@ -311,6 +321,7 @@ static void tipc_group_proto_xmit(struct tipc_group *grp, struct tipc_member *m, } void tipc_group_proto_rcv(struct tipc_group *grp, struct tipc_msg *hdr, + struct sk_buff_head *inputq, struct sk_buff_head *xmitq) { u32 node = msg_orignode(hdr); @@ -332,10 +343,12 @@ void tipc_group_proto_rcv(struct tipc_group *grp, struct tipc_msg *hdr, m->bc_rcv_nxt = msg_grp_bc_syncpt(hdr); /* Wait until PUBLISH event is received */ - if (m->state == MBR_DISCOVERED) + if (m->state == MBR_DISCOVERED) { m->state = MBR_JOINING; - else if (m->state == MBR_PUBLISHED) + } else if (m->state == MBR_PUBLISHED) { m->state = MBR_JOINED; + __skb_queue_tail(inputq, m->event_msg); + } return; case GRP_LEAVE_MSG: if (!m) @@ -347,6 +360,7 @@ void tipc_group_proto_rcv(struct tipc_group *grp, struct tipc_msg *hdr, return; } /* Otherwise deliver already received WITHDRAW event */ + __skb_queue_tail(inputq, m->event_msg); tipc_group_delete_member(grp, m); return; default: @@ -354,16 +368,17 @@ void tipc_group_proto_rcv(struct tipc_group *grp, struct tipc_msg *hdr, } } -/* tipc_group_member_evt() - receive and handle a member up/down event - */ void tipc_group_member_evt(struct tipc_group *grp, struct sk_buff *skb, + struct sk_buff_head *inputq, struct sk_buff_head *xmitq) { struct tipc_msg *hdr = buf_msg(skb); struct tipc_event *evt = (void *)msg_data(hdr); + u32 instance = evt->found_lower; u32 node = evt->port.node; u32 port = evt->port.ref; + int event = evt->event; struct tipc_member *m; struct net *net; u32 self; @@ -376,32 +391,51 @@ void tipc_group_member_evt(struct tipc_group *grp, if (!grp->loopback && node == self && port == grp->portid) goto drop; + /* Convert message before delivery to user */ + msg_set_hdr_sz(hdr, GROUP_H_SIZE); + msg_set_user(hdr, TIPC_CRITICAL_IMPORTANCE); + msg_set_type(hdr, TIPC_GRP_MEMBER_EVT); + msg_set_origport(hdr, port); + msg_set_orignode(hdr, node); + msg_set_nametype(hdr, grp->type); + msg_set_grp_evt(hdr, event); + m = tipc_group_find_member(grp, node, port); - if (evt->event == TIPC_PUBLISHED) { + if (event == TIPC_PUBLISHED) { if (!m) m = tipc_group_create_member(grp, node, port, MBR_DISCOVERED); if (!m) goto drop; - /* Wait if JOIN message not yet received */ - if (m->state == MBR_DISCOVERED) + /* Hold back event if JOIN message not yet received */ + if (m->state == MBR_DISCOVERED) { + m->event_msg = skb; m->state = MBR_PUBLISHED; - else + } else { + __skb_queue_tail(inputq, skb); m->state = MBR_JOINED; - m->instance = evt->found_lower; + } + m->instance = instance; + TIPC_SKB_CB(skb)->orig_member = m->instance; tipc_group_proto_xmit(grp, m, GRP_JOIN_MSG, xmitq); - } else if (evt->event == TIPC_WITHDRAWN) { + } else if (event == TIPC_WITHDRAWN) { if (!m) goto drop; - /* Keep back event if more messages might be expected */ - if (m->state != MBR_LEAVING && tipc_node_is_up(net, node)) + TIPC_SKB_CB(skb)->orig_member = m->instance; + + /* Hold back event if more messages might be expected */ + if (m->state != MBR_LEAVING && tipc_node_is_up(net, node)) { + m->event_msg = skb; m->state = MBR_LEAVING; - else + } else { + __skb_queue_tail(inputq, skb); tipc_group_delete_member(grp, m); + } } + return; drop: kfree_skb(skb); } diff --git a/net/tipc/group.h b/net/tipc/group.h index 9bdf4479fc03..5d3f10d28967 100644 --- a/net/tipc/group.h +++ b/net/tipc/group.h @@ -54,9 +54,11 @@ void tipc_group_filter_msg(struct tipc_group *grp, struct sk_buff_head *xmitq); void tipc_group_member_evt(struct tipc_group *grp, struct sk_buff *skb, + struct sk_buff_head *inputq, struct sk_buff_head *xmitq); void tipc_group_proto_rcv(struct tipc_group *grp, struct tipc_msg *hdr, + struct sk_buff_head *inputq, struct sk_buff_head *xmitq); void tipc_group_update_bc_members(struct tipc_group *grp); u16 tipc_group_bc_snd_nxt(struct tipc_group *grp); diff --git a/net/tipc/msg.h b/net/tipc/msg.h index e438716d2372..1b527b154e46 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -65,7 +65,8 @@ struct plist; #define TIPC_MCAST_MSG 1 #define TIPC_NAMED_MSG 2 #define TIPC_DIRECT_MSG 3 -#define TIPC_GRP_BCAST_MSG 4 +#define TIPC_GRP_MEMBER_EVT 4 +#define TIPC_GRP_BCAST_MSG 5 /* * Internal message users @@ -258,7 +259,14 @@ static inline void msg_set_type(struct tipc_msg *m, u32 n) static inline int msg_in_group(struct tipc_msg *m) { - return (msg_type(m) == TIPC_GRP_BCAST_MSG); + int mtyp = msg_type(m); + + return (mtyp == TIPC_GRP_BCAST_MSG) || (mtyp == TIPC_GRP_MEMBER_EVT); +} + +static inline bool msg_is_grp_evt(struct tipc_msg *m) +{ + return msg_type(m) == TIPC_GRP_MEMBER_EVT; } static inline u32 msg_named(struct tipc_msg *m) @@ -824,6 +832,16 @@ static inline void msg_set_grp_bc_syncpt(struct tipc_msg *m, u16 n) /* Word 10 */ +static inline u16 msg_grp_evt(struct tipc_msg *m) +{ + return msg_bits(m, 10, 0, 0x3); +} + +static inline void msg_set_grp_evt(struct tipc_msg *m, int n) +{ + msg_set_bits(m, 10, 0, 0x3, n); +} + static inline u16 msg_grp_bc_seqno(struct tipc_msg *m) { return msg_bits(m, 10, 16, 0xffff); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 25ecf1201527..0a2eac309177 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -709,41 +709,43 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock, poll_table *wait) { struct sock *sk = sock->sk; + struct sk_buff *skb = skb_peek(&sk->sk_receive_queue); struct tipc_sock *tsk = tipc_sk(sk); struct tipc_group *grp = tsk->group; - u32 mask = 0; + u32 revents = 0; sock_poll_wait(file, sk_sleep(sk), wait); if (sk->sk_shutdown & RCV_SHUTDOWN) - mask |= POLLRDHUP | POLLIN | POLLRDNORM; + revents |= POLLRDHUP | POLLIN | POLLRDNORM; if (sk->sk_shutdown == SHUTDOWN_MASK) - mask |= POLLHUP; + revents |= POLLHUP; switch (sk->sk_state) { case TIPC_ESTABLISHED: if (!tsk->cong_link_cnt && !tsk_conn_cong(tsk)) - mask |= POLLOUT; + revents |= POLLOUT; /* fall thru' */ case TIPC_LISTEN: case TIPC_CONNECTING: - if (!skb_queue_empty(&sk->sk_receive_queue)) - mask |= (POLLIN | POLLRDNORM); + if (skb) + revents |= POLLIN | POLLRDNORM; break; case TIPC_OPEN: if (!grp || tipc_group_size(grp)) if (!tsk->cong_link_cnt) - mask |= POLLOUT; - if (tipc_sk_type_connectionless(sk) && - (!skb_queue_empty(&sk->sk_receive_queue))) - mask |= (POLLIN | POLLRDNORM); + revents |= POLLOUT; + if (!tipc_sk_type_connectionless(sk)) + break; + if (!skb) + break; + revents |= POLLIN | POLLRDNORM; break; case TIPC_DISCONNECTING: - mask = (POLLIN | POLLRDNORM | POLLHUP); + revents = POLLIN | POLLRDNORM | POLLHUP; break; } - - return mask; + return revents; } /** @@ -1415,11 +1417,12 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, size_t buflen, int flags) { struct sock *sk = sock->sk; - struct tipc_sock *tsk = tipc_sk(sk); - struct sk_buff *skb; - struct tipc_msg *hdr; bool connected = !tipc_sk_type_connectionless(sk); + struct tipc_sock *tsk = tipc_sk(sk); int rc, err, hlen, dlen, copy; + struct tipc_msg *hdr; + struct sk_buff *skb; + bool grp_evt; long timeout; /* Catch invalid receive requests */ @@ -1443,6 +1446,7 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, dlen = msg_data_sz(hdr); hlen = msg_hdr_sz(hdr); err = msg_errcode(hdr); + grp_evt = msg_is_grp_evt(hdr); if (likely(dlen || err)) break; tsk_advance_rx_queue(sk); @@ -1469,11 +1473,20 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m, if (unlikely(rc)) goto exit; + /* Mark message as group event if applicable */ + if (unlikely(grp_evt)) { + if (msg_grp_evt(hdr) == TIPC_WITHDRAWN) + m->msg_flags |= MSG_EOR; + m->msg_flags |= MSG_OOB; + copy = 0; + } + /* Caption of data or error code/rejected data was successful */ if (unlikely(flags & MSG_PEEK)) goto exit; tsk_advance_rx_queue(sk); + if (likely(!connected)) goto exit; @@ -1648,10 +1661,10 @@ static void tipc_sk_proto_rcv(struct sock *sk, sk->sk_write_space(sk); break; case GROUP_PROTOCOL: - tipc_group_proto_rcv(grp, hdr, xmitq); + tipc_group_proto_rcv(grp, hdr, inputq, xmitq); break; case TOP_SRV: - tipc_group_member_evt(tsk->group, skb, xmitq); + tipc_group_member_evt(tsk->group, skb, inputq, xmitq); skb = NULL; break; default: -- cgit v1.2.3-59-g8ed1b From ec5c8e429d07737ee94ee1fd2ae5029547484194 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Fri, 13 Oct 2017 14:32:55 +0100 Subject: arm_arch_timer: Expose event stream status The arch timer configuration for a CPU might get reset after suspending said CPU. In order to reliably use the event stream in the kernel (e.g. for delays), we keep track of the state where we can safely consider the event stream as properly configured. After writing to cntkctl, we issue an ISB to ensure that subsequent delay loops can rely on the event stream being enabled. Signed-off-by: Julien Thierry Acked-by: Mark Rutland Cc: Marc Zyngier Cc: Russell King Cc: Catalin Marinas Cc: Will Deacon Signed-off-by: Will Deacon --- arch/arm/include/asm/arch_timer.h | 1 + arch/arm64/include/asm/arch_timer.h | 1 + drivers/clocksource/arm_arch_timer.c | 25 ++++++++++++++++++++++--- include/clocksource/arm_arch_timer.h | 6 ++++++ 4 files changed, 30 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h index d4ebf5679f1f..0b6e1040f9e7 100644 --- a/arch/arm/include/asm/arch_timer.h +++ b/arch/arm/include/asm/arch_timer.h @@ -106,6 +106,7 @@ static inline u32 arch_timer_get_cntkctl(void) static inline void arch_timer_set_cntkctl(u32 cntkctl) { asm volatile("mcr p15, 0, %0, c14, c1, 0" : : "r" (cntkctl)); + isb(); } #endif diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h index a652ce0a5cb2..bdedd8f748d1 100644 --- a/arch/arm64/include/asm/arch_timer.h +++ b/arch/arm64/include/asm/arch_timer.h @@ -144,6 +144,7 @@ static inline u32 arch_timer_get_cntkctl(void) static inline void arch_timer_set_cntkctl(u32 cntkctl) { write_sysreg(cntkctl, cntkctl_el1); + isb(); } static inline u64 arch_counter_get_cntpct(void) diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index fd4b7f684bd0..13e6baa13a89 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -77,6 +77,7 @@ static bool arch_timer_mem_use_virtual; static bool arch_counter_suspend_stop; static bool vdso_default = true; +static cpumask_t evtstrm_available = CPU_MASK_NONE; static bool evtstrm_enable = IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM); static int __init early_evtstrm_cfg(char *buf) @@ -740,6 +741,7 @@ static void arch_timer_evtstrm_enable(int divider) #ifdef CONFIG_COMPAT compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM; #endif + cpumask_set_cpu(smp_processor_id(), &evtstrm_available); } static void arch_timer_configure_evtstream(void) @@ -864,6 +866,16 @@ u32 arch_timer_get_rate(void) return arch_timer_rate; } +bool arch_timer_evtstrm_available(void) +{ + /* + * We might get called from a preemptible context. This is fine + * because availability of the event stream should be always the same + * for a preemptible context and context where we might resume a task. + */ + return cpumask_test_cpu(raw_smp_processor_id(), &evtstrm_available); +} + static u64 arch_counter_get_cntvct_mem(void) { u32 vct_lo, vct_hi, tmp_hi; @@ -929,6 +941,8 @@ static int arch_timer_dying_cpu(unsigned int cpu) { struct clock_event_device *clk = this_cpu_ptr(arch_timer_evt); + cpumask_clear_cpu(smp_processor_id(), &evtstrm_available); + arch_timer_stop(clk); return 0; } @@ -938,10 +952,16 @@ static DEFINE_PER_CPU(unsigned long, saved_cntkctl); static int arch_timer_cpu_pm_notify(struct notifier_block *self, unsigned long action, void *hcpu) { - if (action == CPU_PM_ENTER) + if (action == CPU_PM_ENTER) { __this_cpu_write(saved_cntkctl, arch_timer_get_cntkctl()); - else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) + + cpumask_clear_cpu(smp_processor_id(), &evtstrm_available); + } else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) { arch_timer_set_cntkctl(__this_cpu_read(saved_cntkctl)); + + if (elf_hwcap & HWCAP_EVTSTRM) + cpumask_set_cpu(smp_processor_id(), &evtstrm_available); + } return NOTIFY_OK; } @@ -1017,7 +1037,6 @@ static int __init arch_timer_register(void) if (err) goto out_unreg_notify; - /* Register and immediately configure the timer on the boot CPU */ err = cpuhp_setup_state(CPUHP_AP_ARM_ARCH_TIMER_STARTING, "clockevents/arm/arch_timer:starting", diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index cc805b72994a..4e28283e2ec6 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -93,6 +93,7 @@ struct arch_timer_mem { extern u32 arch_timer_get_rate(void); extern u64 (*arch_timer_read_counter)(void); extern struct arch_timer_kvm_info *arch_timer_get_kvm_info(void); +extern bool arch_timer_evtstrm_available(void); #else @@ -106,6 +107,11 @@ static inline u64 arch_timer_read_counter(void) return 0; } +static inline bool arch_timer_evtstrm_available(void) +{ + return false; +} + #endif #endif -- cgit v1.2.3-59-g8ed1b From 7b77452ec524fa845efdec28ae16e2202cf797dc Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Fri, 13 Oct 2017 14:32:56 +0100 Subject: arm64: use WFE for long delays The current delay implementation uses the yield instruction, which is a hint that it is beneficial to schedule another thread. As this is a hint, it may be implemented as a NOP, causing all delays to be busy loops. This is the case for many existing CPUs. Taking advantage of the generic timer sending periodic events to all cores, we can use WFE during delays to reduce power consumption. This is beneficial only for delays longer than the period of the timer event stream. If timer event stream is not enabled, delays will behave as yield/busy loops. Signed-off-by: Julien Thierry Cc: Catalin Marinas Cc: Will Deacon Cc: Mark Rutland Signed-off-by: Will Deacon --- arch/arm64/lib/delay.c | 23 +++++++++++++++++++---- include/clocksource/arm_arch_timer.h | 4 +++- 2 files changed, 22 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/arch/arm64/lib/delay.c b/arch/arm64/lib/delay.c index dad4ec9bbfd1..e48ac402e7be 100644 --- a/arch/arm64/lib/delay.c +++ b/arch/arm64/lib/delay.c @@ -24,10 +24,28 @@ #include #include +#include + +#define USECS_TO_CYCLES(time_usecs) \ + xloops_to_cycles((time_usecs) * 0x10C7UL) + +static inline unsigned long xloops_to_cycles(unsigned long xloops) +{ + return (xloops * loops_per_jiffy * HZ) >> 32; +} + void __delay(unsigned long cycles) { cycles_t start = get_cycles(); + if (arch_timer_evtstrm_available()) { + const cycles_t timer_evt_period = + USECS_TO_CYCLES(ARCH_TIMER_EVT_STREAM_PERIOD_US); + + while ((get_cycles() - start + timer_evt_period) < cycles) + wfe(); + } + while ((get_cycles() - start) < cycles) cpu_relax(); } @@ -35,10 +53,7 @@ EXPORT_SYMBOL(__delay); inline void __const_udelay(unsigned long xloops) { - unsigned long loops; - - loops = xloops * loops_per_jiffy * HZ; - __delay(loops >> 32); + __delay(xloops_to_cycles(xloops)); } EXPORT_SYMBOL(__const_udelay); diff --git a/include/clocksource/arm_arch_timer.h b/include/clocksource/arm_arch_timer.h index 4e28283e2ec6..349e5957c949 100644 --- a/include/clocksource/arm_arch_timer.h +++ b/include/clocksource/arm_arch_timer.h @@ -67,7 +67,9 @@ enum arch_timer_spi_nr { #define ARCH_TIMER_USR_VT_ACCESS_EN (1 << 8) /* virtual timer registers */ #define ARCH_TIMER_USR_PT_ACCESS_EN (1 << 9) /* physical timer registers */ -#define ARCH_TIMER_EVT_STREAM_FREQ 10000 /* 100us */ +#define ARCH_TIMER_EVT_STREAM_PERIOD_US 100 +#define ARCH_TIMER_EVT_STREAM_FREQ \ + (USEC_PER_SEC / ARCH_TIMER_EVT_STREAM_PERIOD_US) struct arch_timer_kvm_info { struct timecounter timecounter; -- cgit v1.2.3-59-g8ed1b From 4e8b86c062695454df0b76f3fee4fab8dc4bb716 Mon Sep 17 00:00:00 2001 From: Amritha Nambiar Date: Thu, 7 Sep 2017 04:00:06 -0700 Subject: mqprio: Introduce new hardware offload mode and shaper in mqprio The offload types currently supported in mqprio are 0 (no offload) and 1 (offload only TCs) by setting these values for the 'hw' option. If offloads are supported by setting the 'hw' option to 1, the default offload mode is 'dcb' where only the TC values are offloaded to the device. This patch introduces a new hardware offload mode called 'channel' with 'hw' set to 1 in mqprio which makes full use of the mqprio options, the TCs, the queue configurations and the QoS parameters for the TCs. This is achieved through a new netlink attribute for the 'mode' option which takes values such as 'dcb' (default) and 'channel'. The 'channel' mode also supports QoS attributes for traffic class such as minimum and maximum values for bandwidth rate limits. This patch enables configuring additional HW shaper attributes associated with a traffic class. Currently the shaper for bandwidth rate limiting is supported which takes options such as minimum and maximum bandwidth rates and are offloaded to the hardware in the 'channel' mode. The min and max limits for bandwidth rates are provided by the user along with the TCs and the queue configurations when creating the mqprio qdisc. The interface can be extended to support new HW shapers in future through the 'shaper' attribute. Introduces a new data structure 'tc_mqprio_qopt_offload' for offloading mqprio queue options and use this to be shared between the kernel and device driver. This contains a copy of the existing data structure for mqprio queue options. This new data structure can be extended when adding new attributes for traffic class such as mode, shaper, shaper parameters (bandwidth rate limits). The existing data structure for mqprio queue options will be shared between the kernel and userspace. Example: queues 4@0 4@4 hw 1 mode channel shaper bw_rlimit\ min_rate 1Gbit 2Gbit max_rate 4Gbit 5Gbit To dump the bandwidth rates: qdisc mqprio 804a: root tc 2 map 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 queues:(0:3) (4:7) mode:channel shaper:bw_rlimit min_rate:1Gbit 2Gbit max_rate:4Gbit 5Gbit Signed-off-by: Amritha Nambiar Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- include/net/pkt_cls.h | 9 ++ include/uapi/linux/pkt_sched.h | 32 +++++++ net/sched/sch_mqprio.c | 183 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 215 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index f5263743076b..60d39789e4f0 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -546,6 +546,15 @@ struct tc_cls_bpf_offload { u32 gen_flags; }; +struct tc_mqprio_qopt_offload { + /* struct tc_mqprio_qopt must always be the first element */ + struct tc_mqprio_qopt qopt; + u16 mode; + u16 shaper; + u32 flags; + u64 min_rate[TC_QOPT_MAX_QUEUE]; + u64 max_rate[TC_QOPT_MAX_QUEUE]; +}; /* This structure holds cookie structure that is passed from user * to the kernel for actions and classifiers diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index 099bf5528fed..e95b5c9b9fad 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -625,6 +625,22 @@ enum { #define TC_MQPRIO_HW_OFFLOAD_MAX (__TC_MQPRIO_HW_OFFLOAD_MAX - 1) +enum { + TC_MQPRIO_MODE_DCB, + TC_MQPRIO_MODE_CHANNEL, + __TC_MQPRIO_MODE_MAX +}; + +#define __TC_MQPRIO_MODE_MAX (__TC_MQPRIO_MODE_MAX - 1) + +enum { + TC_MQPRIO_SHAPER_DCB, + TC_MQPRIO_SHAPER_BW_RATE, /* Add new shapers below */ + __TC_MQPRIO_SHAPER_MAX +}; + +#define __TC_MQPRIO_SHAPER_MAX (__TC_MQPRIO_SHAPER_MAX - 1) + struct tc_mqprio_qopt { __u8 num_tc; __u8 prio_tc_map[TC_QOPT_BITMASK + 1]; @@ -633,6 +649,22 @@ struct tc_mqprio_qopt { __u16 offset[TC_QOPT_MAX_QUEUE]; }; +#define TC_MQPRIO_F_MODE 0x1 +#define TC_MQPRIO_F_SHAPER 0x2 +#define TC_MQPRIO_F_MIN_RATE 0x4 +#define TC_MQPRIO_F_MAX_RATE 0x8 + +enum { + TCA_MQPRIO_UNSPEC, + TCA_MQPRIO_MODE, + TCA_MQPRIO_SHAPER, + TCA_MQPRIO_MIN_RATE64, + TCA_MQPRIO_MAX_RATE64, + __TCA_MQPRIO_MAX, +}; + +#define TCA_MQPRIO_MAX (__TCA_MQPRIO_MAX - 1) + /* SFB */ enum { diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 6bcdfe6e7b63..f1ae9be83934 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -18,10 +18,16 @@ #include #include #include +#include struct mqprio_sched { struct Qdisc **qdiscs; + u16 mode; + u16 shaper; int hw_offload; + u32 flags; + u64 min_rate[TC_QOPT_MAX_QUEUE]; + u64 max_rate[TC_QOPT_MAX_QUEUE]; }; static void mqprio_destroy(struct Qdisc *sch) @@ -39,9 +45,17 @@ static void mqprio_destroy(struct Qdisc *sch) } if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) { - struct tc_mqprio_qopt mqprio = {}; + struct tc_mqprio_qopt_offload mqprio = { { 0 } }; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &mqprio); + switch (priv->mode) { + case TC_MQPRIO_MODE_DCB: + case TC_MQPRIO_MODE_CHANNEL: + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, + &mqprio); + break; + default: + return; + } } else { netdev_set_num_tc(dev, 0); } @@ -97,6 +111,26 @@ static int mqprio_parse_opt(struct net_device *dev, struct tc_mqprio_qopt *qopt) return 0; } +static const struct nla_policy mqprio_policy[TCA_MQPRIO_MAX + 1] = { + [TCA_MQPRIO_MODE] = { .len = sizeof(u16) }, + [TCA_MQPRIO_SHAPER] = { .len = sizeof(u16) }, + [TCA_MQPRIO_MIN_RATE64] = { .type = NLA_NESTED }, + [TCA_MQPRIO_MAX_RATE64] = { .type = NLA_NESTED }, +}; + +static int parse_attr(struct nlattr *tb[], int maxtype, struct nlattr *nla, + const struct nla_policy *policy, int len) +{ + int nested_len = nla_len(nla) - NLA_ALIGN(len); + + if (nested_len >= nla_attr_size(0)) + return nla_parse(tb, maxtype, nla_data(nla) + NLA_ALIGN(len), + nested_len, policy, NULL); + + memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); + return 0; +} + static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) { struct net_device *dev = qdisc_dev(sch); @@ -105,6 +139,10 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) struct Qdisc *qdisc; int i, err = -EOPNOTSUPP; struct tc_mqprio_qopt *qopt = NULL; + struct nlattr *tb[TCA_MQPRIO_MAX + 1]; + struct nlattr *attr; + int rem; + int len = nla_len(opt) - NLA_ALIGN(sizeof(*qopt)); BUILD_BUG_ON(TC_MAX_QUEUE != TC_QOPT_MAX_QUEUE); BUILD_BUG_ON(TC_BITMASK != TC_QOPT_BITMASK); @@ -122,6 +160,58 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) if (mqprio_parse_opt(dev, qopt)) return -EINVAL; + if (len > 0) { + err = parse_attr(tb, TCA_MQPRIO_MAX, opt, mqprio_policy, + sizeof(*qopt)); + if (err < 0) + return err; + + if (!qopt->hw) + return -EINVAL; + + if (tb[TCA_MQPRIO_MODE]) { + priv->flags |= TC_MQPRIO_F_MODE; + priv->mode = *(u16 *)nla_data(tb[TCA_MQPRIO_MODE]); + } + + if (tb[TCA_MQPRIO_SHAPER]) { + priv->flags |= TC_MQPRIO_F_SHAPER; + priv->shaper = *(u16 *)nla_data(tb[TCA_MQPRIO_SHAPER]); + } + + if (tb[TCA_MQPRIO_MIN_RATE64]) { + if (priv->shaper != TC_MQPRIO_SHAPER_BW_RATE) + return -EINVAL; + i = 0; + nla_for_each_nested(attr, tb[TCA_MQPRIO_MIN_RATE64], + rem) { + if (nla_type(attr) != TCA_MQPRIO_MIN_RATE64) + return -EINVAL; + if (i >= qopt->num_tc) + break; + priv->min_rate[i] = *(u64 *)nla_data(attr); + i++; + } + priv->flags |= TC_MQPRIO_F_MIN_RATE; + } + + if (tb[TCA_MQPRIO_MAX_RATE64]) { + if (priv->shaper != TC_MQPRIO_SHAPER_BW_RATE) + return -EINVAL; + i = 0; + nla_for_each_nested(attr, tb[TCA_MQPRIO_MAX_RATE64], + rem) { + if (nla_type(attr) != TCA_MQPRIO_MAX_RATE64) + return -EINVAL; + if (i >= qopt->num_tc) + break; + priv->max_rate[i] = *(u64 *)nla_data(attr); + i++; + } + priv->flags |= TC_MQPRIO_F_MAX_RATE; + } + } + /* pre-allocate qdisc, attachment can't fail */ priv->qdiscs = kcalloc(dev->num_tx_queues, sizeof(priv->qdiscs[0]), GFP_KERNEL); @@ -146,14 +236,36 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) * supplied and verified mapping */ if (qopt->hw) { - struct tc_mqprio_qopt mqprio = *qopt; + struct tc_mqprio_qopt_offload mqprio = {.qopt = *qopt}; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, + switch (priv->mode) { + case TC_MQPRIO_MODE_DCB: + if (priv->shaper != TC_MQPRIO_SHAPER_DCB) + return -EINVAL; + break; + case TC_MQPRIO_MODE_CHANNEL: + mqprio.flags = priv->flags; + if (priv->flags & TC_MQPRIO_F_MODE) + mqprio.mode = priv->mode; + if (priv->flags & TC_MQPRIO_F_SHAPER) + mqprio.shaper = priv->shaper; + if (priv->flags & TC_MQPRIO_F_MIN_RATE) + for (i = 0; i < mqprio.qopt.num_tc; i++) + mqprio.min_rate[i] = priv->min_rate[i]; + if (priv->flags & TC_MQPRIO_F_MAX_RATE) + for (i = 0; i < mqprio.qopt.num_tc; i++) + mqprio.max_rate[i] = priv->max_rate[i]; + break; + default: + return -EINVAL; + } + err = dev->netdev_ops->ndo_setup_tc(dev, + TC_SETUP_MQPRIO, &mqprio); if (err) return err; - priv->hw_offload = mqprio.hw; + priv->hw_offload = mqprio.qopt.hw; } else { netdev_set_num_tc(dev, qopt->num_tc); for (i = 0; i < qopt->num_tc; i++) @@ -223,11 +335,51 @@ static int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new, return 0; } +static int dump_rates(struct mqprio_sched *priv, + struct tc_mqprio_qopt *opt, struct sk_buff *skb) +{ + struct nlattr *nest; + int i; + + if (priv->flags & TC_MQPRIO_F_MIN_RATE) { + nest = nla_nest_start(skb, TCA_MQPRIO_MIN_RATE64); + if (!nest) + goto nla_put_failure; + + for (i = 0; i < opt->num_tc; i++) { + if (nla_put(skb, TCA_MQPRIO_MIN_RATE64, + sizeof(priv->min_rate[i]), + &priv->min_rate[i])) + goto nla_put_failure; + } + nla_nest_end(skb, nest); + } + + if (priv->flags & TC_MQPRIO_F_MAX_RATE) { + nest = nla_nest_start(skb, TCA_MQPRIO_MAX_RATE64); + if (!nest) + goto nla_put_failure; + + for (i = 0; i < opt->num_tc; i++) { + if (nla_put(skb, TCA_MQPRIO_MAX_RATE64, + sizeof(priv->max_rate[i]), + &priv->max_rate[i])) + goto nla_put_failure; + } + nla_nest_end(skb, nest); + } + return 0; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -1; +} + static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) { struct net_device *dev = qdisc_dev(sch); struct mqprio_sched *priv = qdisc_priv(sch); - unsigned char *b = skb_tail_pointer(skb); + struct nlattr *nla = (struct nlattr *)skb_tail_pointer(skb); struct tc_mqprio_qopt opt = { 0 }; struct Qdisc *qdisc; unsigned int i; @@ -258,12 +410,25 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb) opt.offset[i] = dev->tc_to_txq[i].offset; } - if (nla_put(skb, TCA_OPTIONS, sizeof(opt), &opt)) + if (nla_put(skb, TCA_OPTIONS, NLA_ALIGN(sizeof(opt)), &opt)) + goto nla_put_failure; + + if ((priv->flags & TC_MQPRIO_F_MODE) && + nla_put_u16(skb, TCA_MQPRIO_MODE, priv->mode)) + goto nla_put_failure; + + if ((priv->flags & TC_MQPRIO_F_SHAPER) && + nla_put_u16(skb, TCA_MQPRIO_SHAPER, priv->shaper)) + goto nla_put_failure; + + if ((priv->flags & TC_MQPRIO_F_MIN_RATE || + priv->flags & TC_MQPRIO_F_MAX_RATE) && + (dump_rates(priv, &opt, skb) != 0)) goto nla_put_failure; - return skb->len; + return nla_nest_end(skb, nla); nla_put_failure: - nlmsg_trim(skb, b); + nlmsg_trim(skb, nla); return -1; } -- cgit v1.2.3-59-g8ed1b From 17a9422de78c3a59b490b400f555635c477f1476 Mon Sep 17 00:00:00 2001 From: Alan Brady Date: Wed, 11 Oct 2017 14:49:43 -0700 Subject: i40e/i40evf: don't trust VF to reset itself When using 'ethtool -L' on a VF to change number of requested queues from PF, we shouldn't trust the VF to reset itself after making the request. Doing it that way opens the door for a potentially malicious VF to do nasty things to the PF which should never be the case. This makes it such that after VF makes a successful request, PF will then reset the VF to institute required changes. Only if the request fails will PF send a message back to VF letting it know the request was unsuccessful. Testing-hints: There should be no real functional changes. This is simply hardening against a potentially malicious VF. Signed-off-by: Alan Brady Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c | 9 +++++++-- drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c | 7 +++---- include/linux/avf/virtchnl.h | 4 ++-- 3 files changed, 12 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index ce0981e2f605..f8a794b72462 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -2045,8 +2045,9 @@ error_param: * @msglen: msg length * * VFs get a default number of queues but can use this message to request a - * different number. Will respond with either the number requested or the - * maximum we can support. + * different number. If the request is successful, PF will reset the VF and + * return 0. If unsuccessful, PF will send message informing VF of number of + * available queues and return result of sending VF a message. **/ static int i40e_vc_request_queues_msg(struct i40e_vf *vf, u8 *msg, int msglen) { @@ -2077,7 +2078,11 @@ static int i40e_vc_request_queues_msg(struct i40e_vf *vf, u8 *msg, int msglen) pf->queues_left); vfres->num_queue_pairs = pf->queues_left + cur_pairs; } else { + /* successful request */ vf->num_req_queues = req_pairs; + i40e_vc_notify_vf_reset(vf); + i40e_reset_vf(vf, false); + return 0; } return i40e_vc_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES, 0, diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 2bb81c39d85f..46c8b8a3907c 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -407,6 +407,7 @@ int i40evf_request_queues(struct i40evf_adapter *adapter, int num) vfres.num_queue_pairs = num; adapter->current_op = VIRTCHNL_OP_REQUEST_QUEUES; + adapter->flags |= I40EVF_FLAG_REINIT_ITR_NEEDED; return i40evf_send_pf_msg(adapter, VIRTCHNL_OP_REQUEST_QUEUES, (u8 *)&vfres, sizeof(vfres)); } @@ -1098,15 +1099,13 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, case VIRTCHNL_OP_REQUEST_QUEUES: { struct virtchnl_vf_res_request *vfres = (struct virtchnl_vf_res_request *)msg; - if (vfres->num_queue_pairs == adapter->num_req_queues) { - adapter->flags |= I40EVF_FLAG_REINIT_ITR_NEEDED; - i40evf_schedule_reset(adapter); - } else { + if (vfres->num_queue_pairs != adapter->num_req_queues) { dev_info(&adapter->pdev->dev, "Requested %d queues, PF can support %d\n", adapter->num_req_queues, vfres->num_queue_pairs); adapter->num_req_queues = 0; + adapter->flags &= ~I40EVF_FLAG_REINIT_ITR_NEEDED; } } break; diff --git a/include/linux/avf/virtchnl.h b/include/linux/avf/virtchnl.h index 60e5d90cb18a..3ce61342fa31 100644 --- a/include/linux/avf/virtchnl.h +++ b/include/linux/avf/virtchnl.h @@ -333,8 +333,8 @@ struct virtchnl_vsi_queue_config_info { * additional queues must be negotiated. This is a best effort request as it * is possible the PF does not have enough queues left to support the request. * If the PF cannot support the number requested it will respond with the - * maximum number it is able to support; otherwise it will respond with the - * number requested. + * maximum number it is able to support. If the request is successful, PF will + * then reset the VF to institute required changes. */ /* VF resource request */ -- cgit v1.2.3-59-g8ed1b From 42f6284ae602469762ee721ec31ddfc6170e00bc Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 12 Oct 2017 15:07:23 +0530 Subject: PM / Domains: Add support to select performance-state of domains Some platforms have the capability to configure the performance state of PM domains. This patch enhances the genpd core to support such platforms. The performance levels (within the genpd core) are identified by positive integer values, a lower value represents lower performance state. This patch adds a new genpd API, which is called by user drivers (like OPP framework): - int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state); This updates the performance state constraint of the device on its PM domain. On success, the genpd will have its performance state set to a value which is >= "state" passed to this routine. The genpd core calls the genpd->set_performance_state() callback, if implemented, else -ENODEV is returned to the caller. The PM domain drivers need to implement the following callback if they want to support performance states. - int (*set_performance_state)(struct generic_pm_domain *genpd, unsigned int state); This is called internally by the genpd core on several occasions. The genpd core passes the genpd pointer and the aggregate of the performance states of the devices supported by that genpd to this callback. This callback must update the performance state of the genpd (in a platform dependent way). The power domains can avoid supplying above callback, if they don't support setting performance-states. Currently we aren't propagating performance state changes of a subdomain to its masters as we don't have hardware that needs it right now. Over that, the performance states of subdomain and its masters may not have one-to-one mapping and would require additional information. We can get back to this once we have hardware that needs it. Tested-by: Rajendra Nayak Signed-off-by: Viresh Kumar Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_domain.h | 12 ++++++ 2 files changed, 110 insertions(+) (limited to 'include') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index a6e4c8d7d837..7e01ae364d78 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -237,6 +237,95 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd) static inline void genpd_update_accounting(struct generic_pm_domain *genpd) {} #endif +/** + * dev_pm_genpd_set_performance_state- Set performance state of device's power + * domain. + * + * @dev: Device for which the performance-state needs to be set. + * @state: Target performance state of the device. This can be set as 0 when the + * device doesn't have any performance state constraints left (And so + * the device wouldn't participate anymore to find the target + * performance state of the genpd). + * + * It is assumed that the users guarantee that the genpd wouldn't be detached + * while this routine is getting called. + * + * Returns 0 on success and negative error values on failures. + */ +int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state) +{ + struct generic_pm_domain *genpd; + struct generic_pm_domain_data *gpd_data, *pd_data; + struct pm_domain_data *pdd; + unsigned int prev; + int ret = 0; + + genpd = dev_to_genpd(dev); + if (IS_ERR(genpd)) + return -ENODEV; + + if (unlikely(!genpd->set_performance_state)) + return -EINVAL; + + if (unlikely(!dev->power.subsys_data || + !dev->power.subsys_data->domain_data)) { + WARN_ON(1); + return -EINVAL; + } + + genpd_lock(genpd); + + gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); + prev = gpd_data->performance_state; + gpd_data->performance_state = state; + + /* New requested state is same as Max requested state */ + if (state == genpd->performance_state) + goto unlock; + + /* New requested state is higher than Max requested state */ + if (state > genpd->performance_state) + goto update_state; + + /* Traverse all devices within the domain */ + list_for_each_entry(pdd, &genpd->dev_list, list_node) { + pd_data = to_gpd_data(pdd); + + if (pd_data->performance_state > state) + state = pd_data->performance_state; + } + + if (state == genpd->performance_state) + goto unlock; + + /* + * We aren't propagating performance state changes of a subdomain to its + * masters as we don't have hardware that needs it. Over that, the + * performance states of subdomain and its masters may not have + * one-to-one mapping and would require additional information. We can + * get back to this once we have hardware that needs it. For that + * reason, we don't have to consider performance state of the subdomains + * of genpd here. + */ + +update_state: + if (genpd_status_on(genpd)) { + ret = genpd->set_performance_state(genpd, state); + if (ret) { + gpd_data->performance_state = prev; + goto unlock; + } + } + + genpd->performance_state = state; + +unlock: + genpd_unlock(genpd); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state); + static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) { unsigned int state_idx = genpd->state_idx; @@ -256,6 +345,15 @@ static int _genpd_power_on(struct generic_pm_domain *genpd, bool timed) return ret; elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start)); + + if (unlikely(genpd->set_performance_state)) { + ret = genpd->set_performance_state(genpd, genpd->performance_state); + if (ret) { + pr_warn("%s: Failed to set performance state %d (%d)\n", + genpd->name, genpd->performance_state, ret); + } + } + if (elapsed_ns <= genpd->states[state_idx].power_on_latency_ns) return ret; diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 84f423d5633e..9af0356bd69c 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -64,8 +64,11 @@ struct generic_pm_domain { unsigned int device_count; /* Number of devices */ unsigned int suspended_count; /* System suspend device counter */ unsigned int prepared_count; /* Suspend counter of prepared devices */ + unsigned int performance_state; /* Aggregated max performance state */ int (*power_off)(struct generic_pm_domain *domain); int (*power_on)(struct generic_pm_domain *domain); + int (*set_performance_state)(struct generic_pm_domain *genpd, + unsigned int state); struct gpd_dev_ops dev_ops; s64 max_off_time_ns; /* Maximum allowed "suspended" time. */ bool max_off_time_changed; @@ -121,6 +124,7 @@ struct generic_pm_domain_data { struct pm_domain_data base; struct gpd_timing_data td; struct notifier_block nb; + unsigned int performance_state; void *data; }; @@ -148,6 +152,8 @@ extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, extern int pm_genpd_init(struct generic_pm_domain *genpd, struct dev_power_governor *gov, bool is_off); extern int pm_genpd_remove(struct generic_pm_domain *genpd); +extern int dev_pm_genpd_set_performance_state(struct device *dev, + unsigned int state); extern struct dev_power_governor simple_qos_governor; extern struct dev_power_governor pm_domain_always_on_gov; @@ -188,6 +194,12 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd) return -ENOTSUPP; } +static inline int dev_pm_genpd_set_performance_state(struct device *dev, + unsigned int state) +{ + return -ENOTSUPP; +} + #define simple_qos_governor (*(struct dev_power_governor *)(NULL)) #define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL)) #endif -- cgit v1.2.3-59-g8ed1b From b6aa98364f842f943495408895627702ad7ad44b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 11 Oct 2017 12:54:15 +0530 Subject: PM / OPP: Add dev_pm_opp_{un}register_get_pstate_helper() This adds the dev_pm_opp_{un}register_get_pstate_helper() helper routines which will be used to set the get_pstate() callback for a device. This callback will be later called internally by the OPP core to get performance state corresponding to an OPP. This is required temporarily until the time we have proper DT bindings to include the performance state information. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/opp/core.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/opp/opp.h | 2 ++ include/linux/pm_opp.h | 10 +++++++ 3 files changed, 90 insertions(+) (limited to 'include') diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 0ce8069d6843..92fa94a6dcc1 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1036,6 +1036,9 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, return ret; } + if (opp_table->get_pstate) + new_opp->pstate = opp_table->get_pstate(dev, new_opp->rate); + list_add(&new_opp->node, head); mutex_unlock(&opp_table->lock); @@ -1547,6 +1550,81 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) } EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper); +/** + * dev_pm_opp_register_get_pstate_helper() - Register get_pstate() helper. + * @dev: Device for which the helper is getting registered. + * @get_pstate: Helper. + * + * TODO: Remove this callback after the same information is available via Device + * Tree. + * + * This allows a platform to initialize the performance states of individual + * OPPs for its devices, until we get similar information directly from DT. + * + * This must be called before the OPPs are initialized for the device. + */ +struct opp_table *dev_pm_opp_register_get_pstate_helper(struct device *dev, + int (*get_pstate)(struct device *dev, unsigned long rate)) +{ + struct opp_table *opp_table; + int ret; + + if (!get_pstate) + return ERR_PTR(-EINVAL); + + opp_table = dev_pm_opp_get_opp_table(dev); + if (!opp_table) + return ERR_PTR(-ENOMEM); + + /* This should be called before OPPs are initialized */ + if (WARN_ON(!list_empty(&opp_table->opp_list))) { + ret = -EBUSY; + goto err; + } + + /* Already have genpd_performance_state set */ + if (WARN_ON(opp_table->genpd_performance_state)) { + ret = -EBUSY; + goto err; + } + + opp_table->genpd_performance_state = true; + opp_table->get_pstate = get_pstate; + + return opp_table; + +err: + dev_pm_opp_put_opp_table(opp_table); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_register_get_pstate_helper); + +/** + * dev_pm_opp_unregister_get_pstate_helper() - Releases resources blocked for + * get_pstate() helper + * @opp_table: OPP table returned from dev_pm_opp_register_get_pstate_helper(). + * + * Release resources blocked for platform specific get_pstate() helper. + */ +void dev_pm_opp_unregister_get_pstate_helper(struct opp_table *opp_table) +{ + if (!opp_table->genpd_performance_state) { + pr_err("%s: Doesn't have performance states set\n", + __func__); + return; + } + + /* Make sure there are no concurrent readers while updating opp_table */ + WARN_ON(!list_empty(&opp_table->opp_list)); + + opp_table->genpd_performance_state = false; + opp_table->get_pstate = NULL; + + dev_pm_opp_put_opp_table(opp_table); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_get_pstate_helper); + /** * dev_pm_opp_add() - Add an OPP table from a table definitions * @dev: device for which we do this operation diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h index e8f767ab5814..4d00061648a3 100644 --- a/drivers/opp/opp.h +++ b/drivers/opp/opp.h @@ -140,6 +140,7 @@ enum opp_table_access { * @genpd_performance_state: Device's power domain support performance state. * @set_opp: Platform specific set_opp callback * @set_opp_data: Data to be passed to set_opp callback + * @get_pstate: Platform specific get_pstate callback * @dentry: debugfs dentry pointer of the real device directory (not links). * @dentry_name: Name of the real dentry. * @@ -177,6 +178,7 @@ struct opp_table { int (*set_opp)(struct dev_pm_set_opp_data *data); struct dev_pm_set_opp_data *set_opp_data; + int (*get_pstate)(struct device *dev, unsigned long rate); #ifdef CONFIG_DEBUG_FS struct dentry *dentry; diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 849d21dc4ca7..6c2d2e88f066 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -125,6 +125,8 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name); void dev_pm_opp_put_clkname(struct opp_table *opp_table); struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data)); void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table); +struct opp_table *dev_pm_opp_register_get_pstate_helper(struct device *dev, int (*get_pstate)(struct device *dev, unsigned long rate)); +void dev_pm_opp_unregister_get_pstate_helper(struct opp_table *opp_table); int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq); int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask); int dev_pm_opp_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpumask); @@ -245,6 +247,14 @@ static inline struct opp_table *dev_pm_opp_register_set_opp_helper(struct device static inline void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table) {} +static inline struct opp_table *dev_pm_opp_register_get_pstate_helper(struct device *dev, + int (*get_pstate)(struct device *dev, unsigned long rate)) +{ + return ERR_PTR(-ENOTSUPP); +} + +static inline void dev_pm_opp_unregister_get_pstate_helper(struct opp_table *opp_table) {} + static inline struct opp_table *dev_pm_opp_set_prop_name(struct device *dev, const char *name) { return ERR_PTR(-ENOTSUPP); -- cgit v1.2.3-59-g8ed1b From 20f97caf1120bd02e8ff4adbad3b44b63626feb5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 13 Oct 2017 15:27:24 +0200 Subject: PM / QoS: Drop PM_QOS_FLAG_REMOTE_WAKEUP The PM QoS flag PM_QOS_FLAG_REMOTE_WAKEUP is not used consistently and the vast majority of code simply assumes that remote wakeup should be enabled for devices in runtime suspend if they can generate wakeup signals, so drop it. Signed-off-by: Rafael J. Wysocki Acked-by: Ulf Hansson Reviewed-by: Mika Westerberg --- Documentation/ABI/testing/sysfs-devices-power | 16 --------------- Documentation/power/pm_qos_interface.txt | 13 ++++++------- drivers/acpi/device_pm.c | 6 ++---- drivers/base/power/domain.c | 4 +--- drivers/base/power/sysfs.c | 28 --------------------------- include/linux/pm_qos.h | 1 - 6 files changed, 9 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 676fdf5f2a99..f4b24c327665 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -258,19 +258,3 @@ Description: This attribute has no effect on system-wide suspend/resume and hibernation. - -What: /sys/devices/.../power/pm_qos_remote_wakeup -Date: September 2012 -Contact: Rafael J. Wysocki -Description: - The /sys/devices/.../power/pm_qos_remote_wakeup attribute - is used for manipulating the PM QoS "remote wakeup required" - flag. If set, this flag indicates to the kernel that the - device is a source of user events that have to be signaled from - its low-power states. - - Not all drivers support this attribute. If it isn't supported, - it is not present. - - This attribute has no effect on system-wide suspend/resume and - hibernation. diff --git a/Documentation/power/pm_qos_interface.txt b/Documentation/power/pm_qos_interface.txt index 21d2d48f87a2..19c5f7b1a7ba 100644 --- a/Documentation/power/pm_qos_interface.txt +++ b/Documentation/power/pm_qos_interface.txt @@ -98,8 +98,7 @@ Values are updated in response to changes of the request list. The target values of resume latency and active state latency tolerance are simply the minimum of the request values held in the parameter list elements. The PM QoS flags aggregate value is a gather (bitwise OR) of all list elements' -values. Two device PM QoS flags are defined currently: PM_QOS_FLAG_NO_POWER_OFF -and PM_QOS_FLAG_REMOTE_WAKEUP. +values. One device PM QoS flag is defined currently: PM_QOS_FLAG_NO_POWER_OFF. Note: The aggregated target values are implemented in such a way that reading the aggregated value does not require any locking mechanism. @@ -153,14 +152,14 @@ PM QoS list of resume latency constraints and remove sysfs attribute pm_qos_resume_latency_us from the device's power directory. int dev_pm_qos_expose_flags(device, value) -Add a request to the device's PM QoS list of flags and create sysfs attributes -pm_qos_no_power_off and pm_qos_remote_wakeup under the device's power directory -allowing user space to change these flags' value. +Add a request to the device's PM QoS list of flags and create sysfs attribute +pm_qos_no_power_off under the device's power directory allowing user space to +change the value of the PM_QOS_FLAG_NO_POWER_OFF flag. void dev_pm_qos_hide_flags(device) Drop the request added by dev_pm_qos_expose_flags() from the device's PM QoS list -of flags and remove sysfs attributes pm_qos_no_power_off and pm_qos_remote_wakeup -under the device's power directory. +of flags and remove sysfs attribute pm_qos_no_power_off from the device's power +directory. Notification mechanisms: The per-device PM QoS framework has a per-device notification tree. diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index fbcc73f7a099..e8c820129797 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -581,8 +581,7 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, d_min = ret; wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid && adev->wakeup.sleep_state >= target_state; - } else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) != - PM_QOS_FLAGS_NONE) { + } else { wakeup = adev->wakeup.flags.valid; } @@ -865,8 +864,7 @@ int acpi_dev_runtime_suspend(struct device *dev) if (!adev) return 0; - remote_wakeup = dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) > - PM_QOS_FLAGS_NONE; + remote_wakeup = acpi_device_can_wakeup(adev); if (remote_wakeup) { error = acpi_device_wakeup_enable(adev, ACPI_STATE_S0); if (error) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index e8ca5e2cf1e5..e6414e9998bb 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -346,9 +346,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, list_for_each_entry(pdd, &genpd->dev_list, list_node) { enum pm_qos_flags_status stat; - stat = dev_pm_qos_flags(pdd->dev, - PM_QOS_FLAG_NO_POWER_OFF - | PM_QOS_FLAG_REMOTE_WAKEUP); + stat = dev_pm_qos_flags(pdd->dev, PM_QOS_FLAG_NO_POWER_OFF); if (stat > PM_QOS_FLAGS_NONE) return -EBUSY; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 156ab57bca77..29bf28fef136 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -309,33 +309,6 @@ static ssize_t pm_qos_no_power_off_store(struct device *dev, static DEVICE_ATTR(pm_qos_no_power_off, 0644, pm_qos_no_power_off_show, pm_qos_no_power_off_store); -static ssize_t pm_qos_remote_wakeup_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - return sprintf(buf, "%d\n", !!(dev_pm_qos_requested_flags(dev) - & PM_QOS_FLAG_REMOTE_WAKEUP)); -} - -static ssize_t pm_qos_remote_wakeup_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t n) -{ - int ret; - - if (kstrtoint(buf, 0, &ret)) - return -EINVAL; - - if (ret != 0 && ret != 1) - return -EINVAL; - - ret = dev_pm_qos_update_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP, ret); - return ret < 0 ? ret : n; -} - -static DEVICE_ATTR(pm_qos_remote_wakeup, 0644, - pm_qos_remote_wakeup_show, pm_qos_remote_wakeup_store); - #ifdef CONFIG_PM_SLEEP static const char _enabled[] = "enabled"; static const char _disabled[] = "disabled"; @@ -671,7 +644,6 @@ static const struct attribute_group pm_qos_latency_tolerance_attr_group = { static struct attribute *pm_qos_flags_attrs[] = { &dev_attr_pm_qos_no_power_off.attr, - &dev_attr_pm_qos_remote_wakeup.attr, NULL, }; static const struct attribute_group pm_qos_flags_attr_group = { diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 032b55909145..51f0d7e0b15f 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -39,7 +39,6 @@ enum pm_qos_flags_status { #define PM_QOS_LATENCY_ANY ((s32)(~(__u32)0 >> 1)) #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) -#define PM_QOS_FLAG_REMOTE_WAKEUP (1 << 1) struct pm_qos_request { struct plist_node node; -- cgit v1.2.3-59-g8ed1b From e0005bd93ab5f7a80b08a7bdff9b2ade436f9482 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Tue, 10 Oct 2017 18:15:34 +0200 Subject: iio: Drop duplicate forward declaration Commit 5f420b42079c ("staging:iio: Add extended IIO channel info") added a forward declaration for struct iio_dev to but forgot to remove an existing forward declaration further down originating from commit 7ae8cf627558 ("staging: iio: chrdev.h rationalization"). Cc: Lars-Peter Clausen Signed-off-by: Lukas Wunner Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 50cae8504256..20b61347ea58 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -365,7 +365,6 @@ unsigned int iio_get_time_res(const struct iio_dev *indio_dev); #define INDIO_MAX_RAW_ELEMENTS 4 struct iio_trigger; /* forward declaration */ -struct iio_dev; /** * struct iio_info - constant information about device -- cgit v1.2.3-59-g8ed1b From 7c39afb394c79e72c3795b4a42d55155b34ee073 Mon Sep 17 00:00:00 2001 From: Feras Daoud Date: Tue, 15 Aug 2017 13:46:04 +0300 Subject: net/mlx5: PTP code migration to driver core section PTP code is moved to core section of mlx5 driver in order to share it between ethernet and infiniband. This movement involves the following changes: - Change mlx5e_ prefix to be mlx5_ - Add clock structs to Core - Add clock object to mlx5_core_dev - Call Init/Uninit clock from core init/cleanup - Rename mlx5e_tstamp to be mlx5_clock Signed-off-by: Feras Daoud Signed-off-by: Eitan Rabin Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en.h | 39 +- .../net/ethernet/mellanox/mlx5/core/en_ethtool.c | 7 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 95 +++- drivers/net/ethernet/mellanox/mlx5/core/en_rx.c | 17 +- drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 6 +- drivers/net/ethernet/mellanox/mlx5/core/eq.c | 3 +- .../net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c | 3 +- .../net/ethernet/mellanox/mlx5/core/lib/clock.c | 548 +++++++++------------ .../net/ethernet/mellanox/mlx5/core/lib/clock.h | 51 ++ drivers/net/ethernet/mellanox/mlx5/core/main.c | 4 + .../net/ethernet/mellanox/mlx5/core/mlx5_core.h | 1 + include/linux/mlx5/driver.h | 24 + 12 files changed, 416 insertions(+), 382 deletions(-) create mode 100644 drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index cc13d3dbd366..2059122eb089 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -267,28 +267,6 @@ struct mlx5e_dcbx { }; #endif -#define MAX_PIN_NUM 8 -struct mlx5e_pps { - u8 pin_caps[MAX_PIN_NUM]; - struct work_struct out_work; - u64 start[MAX_PIN_NUM]; - u8 enabled; -}; - -struct mlx5e_tstamp { - rwlock_t lock; - struct cyclecounter cycles; - struct timecounter clock; - struct hwtstamp_config hwtstamp_config; - u32 nominal_c_mult; - unsigned long overflow_period; - struct delayed_work overflow_work; - struct mlx5_core_dev *mdev; - struct ptp_clock *ptp; - struct ptp_clock_info ptp_info; - struct mlx5e_pps pps_info; -}; - enum { MLX5E_RQ_STATE_ENABLED, MLX5E_RQ_STATE_AM, @@ -375,9 +353,10 @@ struct mlx5e_txqsq { u8 min_inline_mode; u16 edge; struct device *pdev; - struct mlx5e_tstamp *tstamp; __be32 mkey_be; unsigned long state; + struct hwtstamp_config *tstamp; + struct mlx5_clock *clock; /* control path */ struct mlx5_wq_ctrl wq_ctrl; @@ -543,10 +522,11 @@ struct mlx5e_rq { struct mlx5e_channel *channel; struct device *pdev; struct net_device *netdev; - struct mlx5e_tstamp *tstamp; struct mlx5e_rq_stats stats; struct mlx5e_cq cq; struct mlx5e_page_cache page_cache; + struct hwtstamp_config *tstamp; + struct mlx5_clock *clock; mlx5e_fp_handle_rx_cqe handle_rx_cqe; mlx5e_fp_post_rx_wqes post_wqes; @@ -588,7 +568,7 @@ struct mlx5e_channel { /* control */ struct mlx5e_priv *priv; struct mlx5_core_dev *mdev; - struct mlx5e_tstamp *tstamp; + struct hwtstamp_config *tstamp; int ix; }; @@ -789,7 +769,7 @@ struct mlx5e_priv { struct mlx5_core_dev *mdev; struct net_device *netdev; struct mlx5e_stats stats; - struct mlx5e_tstamp tstamp; + struct hwtstamp_config tstamp; u16 q_counter; #ifdef CONFIG_MLX5_CORE_EN_DCB struct mlx5e_dcbx dcbx; @@ -873,12 +853,6 @@ void mlx5e_ethtool_init_steering(struct mlx5e_priv *priv); void mlx5e_ethtool_cleanup_steering(struct mlx5e_priv *priv); void mlx5e_set_rx_mode_work(struct work_struct *work); -void mlx5e_fill_hwstamp(struct mlx5e_tstamp *clock, u64 timestamp, - struct skb_shared_hwtstamps *hwts); -void mlx5e_timestamp_init(struct mlx5e_priv *priv); -void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv); -void mlx5e_pps_event_handler(struct mlx5e_priv *priv, - struct ptp_clock_event *event); int mlx5e_hwstamp_set(struct mlx5e_priv *priv, struct ifreq *ifr); int mlx5e_hwstamp_get(struct mlx5e_priv *priv, struct ifreq *ifr); int mlx5e_modify_rx_cqe_compression_locked(struct mlx5e_priv *priv, bool val); @@ -889,6 +863,7 @@ int mlx5e_vlan_rx_kill_vid(struct net_device *dev, __always_unused __be16 proto, u16 vid); void mlx5e_enable_vlan_filter(struct mlx5e_priv *priv); void mlx5e_disable_vlan_filter(struct mlx5e_priv *priv); +void mlx5e_timestamp_set(struct mlx5e_priv *priv); struct mlx5e_redirect_rqt_param { bool is_rss; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index d12e9fc0d76b..81a112e40fe3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -1417,14 +1417,15 @@ static int mlx5e_set_pauseparam(struct net_device *netdev, int mlx5e_ethtool_get_ts_info(struct mlx5e_priv *priv, struct ethtool_ts_info *info) { + struct mlx5_core_dev *mdev = priv->mdev; int ret; ret = ethtool_op_get_ts_info(priv->netdev, info); if (ret) return ret; - info->phc_index = priv->tstamp.ptp ? - ptp_clock_index(priv->tstamp.ptp) : -1; + info->phc_index = mdev->clock.ptp ? + ptp_clock_index(mdev->clock.ptp) : -1; if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) return 0; @@ -1754,7 +1755,7 @@ static int set_pflag_rx_cqe_compress(struct net_device *netdev, if (!MLX5_CAP_GEN(mdev, cqe_compression)) return -EOPNOTSUPP; - if (enable && priv->tstamp.hwtstamp_config.rx_filter != HWTSTAMP_FILTER_NONE) { + if (enable && priv->tstamp.rx_filter != HWTSTAMP_FILTER_NONE) { netdev_err(netdev, "Can't enable cqe compression while timestamping is enabled.\n"); return -EINVAL; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index cc11bbbd0309..6df00dd9745a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -373,8 +373,6 @@ static void mlx5e_async_event(struct mlx5_core_dev *mdev, void *vpriv, enum mlx5_dev_event event, unsigned long param) { struct mlx5e_priv *priv = vpriv; - struct ptp_clock_event ptp_event; - struct mlx5_eqe *eqe = NULL; if (!test_bit(MLX5E_STATE_ASYNC_EVENTS_ENABLED, &priv->state)) return; @@ -384,14 +382,6 @@ static void mlx5e_async_event(struct mlx5_core_dev *mdev, void *vpriv, case MLX5_DEV_EVENT_PORT_DOWN: queue_work(priv->wq, &priv->update_carrier_work); break; - case MLX5_DEV_EVENT_PPS: - eqe = (struct mlx5_eqe *)param; - ptp_event.index = eqe->data.pps.pin; - ptp_event.timestamp = - timecounter_cyc2time(&priv->tstamp.clock, - be64_to_cpu(eqe->data.pps.time_stamp)); - mlx5e_pps_event_handler(vpriv, &ptp_event); - break; default: break; } @@ -585,6 +575,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, rq->pdev = c->pdev; rq->netdev = c->netdev; rq->tstamp = c->tstamp; + rq->clock = &mdev->clock; rq->channel = c; rq->ix = c->ix; rq->mdev = mdev; @@ -1123,6 +1114,7 @@ static int mlx5e_alloc_txqsq(struct mlx5e_channel *c, sq->pdev = c->pdev; sq->tstamp = c->tstamp; + sq->clock = &mdev->clock; sq->mkey_be = c->mkey_be; sq->channel = c; sq->txq_ix = txq_ix; @@ -2678,6 +2670,12 @@ void mlx5e_switch_priv_channels(struct mlx5e_priv *priv, netif_carrier_on(netdev); } +void mlx5e_timestamp_set(struct mlx5e_priv *priv) +{ + priv->tstamp.tx_type = HWTSTAMP_TX_OFF; + priv->tstamp.rx_filter = HWTSTAMP_FILTER_NONE; +} + int mlx5e_open_locked(struct net_device *netdev) { struct mlx5e_priv *priv = netdev_priv(netdev); @@ -2693,7 +2691,7 @@ int mlx5e_open_locked(struct net_device *netdev) mlx5e_activate_priv_channels(priv); if (priv->profile->update_carrier) priv->profile->update_carrier(priv); - mlx5e_timestamp_init(priv); + mlx5e_timestamp_set(priv); if (priv->profile->update_stats) queue_delayed_work(priv->wq, &priv->update_stats_work, 0); @@ -2731,7 +2729,6 @@ int mlx5e_close_locked(struct net_device *netdev) clear_bit(MLX5E_STATE_OPENED, &priv->state); - mlx5e_timestamp_cleanup(priv); netif_carrier_off(priv->netdev); mlx5e_deactivate_priv_channels(priv); mlx5e_close_channels(&priv->channels); @@ -3403,6 +3400,80 @@ out: return err; } +int mlx5e_hwstamp_set(struct mlx5e_priv *priv, struct ifreq *ifr) +{ + struct hwtstamp_config config; + int err; + + if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) + return -EOPNOTSUPP; + + if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) + return -EFAULT; + + /* TX HW timestamp */ + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + case HWTSTAMP_TX_ON: + break; + default: + return -ERANGE; + } + + mutex_lock(&priv->state_lock); + /* RX HW timestamp */ + switch (config.rx_filter) { + case HWTSTAMP_FILTER_NONE: + /* Reset CQE compression to Admin default */ + mlx5e_modify_rx_cqe_compression_locked(priv, priv->channels.params.rx_cqe_compress_def); + break; + case HWTSTAMP_FILTER_ALL: + case HWTSTAMP_FILTER_SOME: + case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: + case HWTSTAMP_FILTER_PTP_V2_EVENT: + case HWTSTAMP_FILTER_PTP_V2_SYNC: + case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: + case HWTSTAMP_FILTER_NTP_ALL: + /* Disable CQE compression */ + netdev_warn(priv->netdev, "Disabling cqe compression"); + err = mlx5e_modify_rx_cqe_compression_locked(priv, false); + if (err) { + netdev_err(priv->netdev, "Failed disabling cqe compression err=%d\n", err); + mutex_unlock(&priv->state_lock); + return err; + } + config.rx_filter = HWTSTAMP_FILTER_ALL; + break; + default: + mutex_unlock(&priv->state_lock); + return -ERANGE; + } + + memcpy(&priv->tstamp, &config, sizeof(config)); + mutex_unlock(&priv->state_lock); + + return copy_to_user(ifr->ifr_data, &config, + sizeof(config)) ? -EFAULT : 0; +} + +int mlx5e_hwstamp_get(struct mlx5e_priv *priv, struct ifreq *ifr) +{ + struct hwtstamp_config *cfg = &priv->tstamp; + + if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) + return -EOPNOTSUPP; + + return copy_to_user(ifr->ifr_data, cfg, sizeof(*cfg)) ? -EFAULT : 0; +} + static int mlx5e_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct mlx5e_priv *priv = netdev_priv(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 15a1687483cc..7e3bfe62ef6e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -42,10 +42,11 @@ #include "en_rep.h" #include "ipoib/ipoib.h" #include "en_accel/ipsec_rxtx.h" +#include "lib/clock.h" -static inline bool mlx5e_rx_hw_stamp(struct mlx5e_tstamp *tstamp) +static inline bool mlx5e_rx_hw_stamp(struct hwtstamp_config *config) { - return tstamp->hwtstamp_config.rx_filter == HWTSTAMP_FILTER_ALL; + return config->rx_filter == HWTSTAMP_FILTER_ALL; } static inline void mlx5e_read_cqe_slot(struct mlx5e_cq *cq, u32 cqcc, @@ -661,7 +662,6 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe, struct sk_buff *skb) { struct net_device *netdev = rq->netdev; - struct mlx5e_tstamp *tstamp = rq->tstamp; int lro_num_seg; lro_num_seg = be32_to_cpu(cqe->srqn) >> 24; @@ -676,8 +676,9 @@ static inline void mlx5e_build_rx_skb(struct mlx5_cqe64 *cqe, rq->stats.lro_bytes += cqe_bcnt; } - if (unlikely(mlx5e_rx_hw_stamp(tstamp))) - mlx5e_fill_hwstamp(tstamp, get_cqe_ts(cqe), skb_hwtstamps(skb)); + if (unlikely(mlx5e_rx_hw_stamp(rq->tstamp))) + skb_hwtstamps(skb)->hwtstamp = + mlx5_timecounter_cyc2time(rq->clock, get_cqe_ts(cqe)); skb_record_rx_queue(skb, rq->ix); @@ -1163,7 +1164,6 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq, struct sk_buff *skb) { struct net_device *netdev = rq->netdev; - struct mlx5e_tstamp *tstamp = rq->tstamp; char *pseudo_header; u8 *dgid; u8 g; @@ -1188,8 +1188,9 @@ static inline void mlx5i_complete_rx_cqe(struct mlx5e_rq *rq, skb->ip_summed = CHECKSUM_COMPLETE; skb->csum = csum_unfold((__force __sum16)cqe->check_sum); - if (unlikely(mlx5e_rx_hw_stamp(tstamp))) - mlx5e_fill_hwstamp(tstamp, get_cqe_ts(cqe), skb_hwtstamps(skb)); + if (unlikely(mlx5e_rx_hw_stamp(rq->tstamp))) + skb_hwtstamps(skb)->hwtstamp = + mlx5_timecounter_cyc2time(rq->clock, get_cqe_ts(cqe)); skb_record_rx_queue(skb, rq->ix); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 1d6925d4369a..a7c208a1ad83 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -35,6 +35,7 @@ #include "en.h" #include "ipoib/ipoib.h" #include "en_accel/ipsec_rxtx.h" +#include "lib/clock.h" #define MLX5E_SQ_NOPS_ROOM MLX5_SEND_WQE_MAX_WQEBBS #define MLX5E_SQ_STOP_ROOM (MLX5_SEND_WQE_MAX_WQEBBS +\ @@ -452,8 +453,9 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) SKBTX_HW_TSTAMP)) { struct skb_shared_hwtstamps hwts = {}; - mlx5e_fill_hwstamp(sq->tstamp, - get_cqe_ts(cqe), &hwts); + hwts.hwtstamp = + mlx5_timecounter_cyc2time(sq->clock, + get_cqe_ts(cqe)); skb_tstamp_tx(skb, &hwts); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index fc606bfd1d6e..60771865c99c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -491,8 +491,7 @@ static irqreturn_t mlx5_eq_int(int irq, void *eq_ptr) break; case MLX5_EVENT_TYPE_PPS_EVENT: - if (dev->event) - dev->event(dev, MLX5_DEV_EVENT_PPS, (unsigned long)eqe); + mlx5_pps_event(dev, eqe); break; case MLX5_EVENT_TYPE_FPGA_ERROR: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index 145e392ab849..14dfb577691b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -404,7 +404,7 @@ static int mlx5i_open(struct net_device *netdev) mlx5e_refresh_tirs(priv, false); mlx5e_activate_priv_channels(priv); - mlx5e_timestamp_init(priv); + mlx5e_timestamp_set(priv); mutex_unlock(&priv->state_lock); return 0; @@ -429,7 +429,6 @@ static int mlx5i_close(struct net_device *netdev) clear_bit(MLX5E_STATE_OPENED, &priv->state); - mlx5e_timestamp_cleanup(priv); netif_carrier_off(priv->netdev); mlx5e_deactivate_priv_channels(priv); mlx5e_close_channels(&priv->channels); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c index 84dd63e74041..fa8aed62b231 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.c @@ -34,250 +34,164 @@ #include "en.h" enum { - MLX5E_CYCLES_SHIFT = 23 + MLX5_CYCLES_SHIFT = 23 }; enum { - MLX5E_PIN_MODE_IN = 0x0, - MLX5E_PIN_MODE_OUT = 0x1, + MLX5_PIN_MODE_IN = 0x0, + MLX5_PIN_MODE_OUT = 0x1, }; enum { - MLX5E_OUT_PATTERN_PULSE = 0x0, - MLX5E_OUT_PATTERN_PERIODIC = 0x1, + MLX5_OUT_PATTERN_PULSE = 0x0, + MLX5_OUT_PATTERN_PERIODIC = 0x1, }; enum { - MLX5E_EVENT_MODE_DISABLE = 0x0, - MLX5E_EVENT_MODE_REPETETIVE = 0x1, - MLX5E_EVENT_MODE_ONCE_TILL_ARM = 0x2, + MLX5_EVENT_MODE_DISABLE = 0x0, + MLX5_EVENT_MODE_REPETETIVE = 0x1, + MLX5_EVENT_MODE_ONCE_TILL_ARM = 0x2, }; enum { - MLX5E_MTPPS_FS_ENABLE = BIT(0x0), - MLX5E_MTPPS_FS_PATTERN = BIT(0x2), - MLX5E_MTPPS_FS_PIN_MODE = BIT(0x3), - MLX5E_MTPPS_FS_TIME_STAMP = BIT(0x4), - MLX5E_MTPPS_FS_OUT_PULSE_DURATION = BIT(0x5), - MLX5E_MTPPS_FS_ENH_OUT_PER_ADJ = BIT(0x7), + MLX5_MTPPS_FS_ENABLE = BIT(0x0), + MLX5_MTPPS_FS_PATTERN = BIT(0x2), + MLX5_MTPPS_FS_PIN_MODE = BIT(0x3), + MLX5_MTPPS_FS_TIME_STAMP = BIT(0x4), + MLX5_MTPPS_FS_OUT_PULSE_DURATION = BIT(0x5), + MLX5_MTPPS_FS_ENH_OUT_PER_ADJ = BIT(0x7), }; -void mlx5e_fill_hwstamp(struct mlx5e_tstamp *tstamp, u64 timestamp, - struct skb_shared_hwtstamps *hwts) +static u64 read_internal_timer(const struct cyclecounter *cc) { - u64 nsec; + struct mlx5_clock *clock = container_of(cc, struct mlx5_clock, cycles); + struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev, + clock); - read_lock(&tstamp->lock); - nsec = timecounter_cyc2time(&tstamp->clock, timestamp); - read_unlock(&tstamp->lock); - - hwts->hwtstamp = ns_to_ktime(nsec); -} - -static u64 mlx5e_read_internal_timer(const struct cyclecounter *cc) -{ - struct mlx5e_tstamp *tstamp = container_of(cc, struct mlx5e_tstamp, - cycles); - - return mlx5_read_internal_timer(tstamp->mdev) & cc->mask; + return mlx5_read_internal_timer(mdev) & cc->mask; } -static void mlx5e_pps_out(struct work_struct *work) +static void mlx5_pps_out(struct work_struct *work) { - struct mlx5e_pps *pps_info = container_of(work, struct mlx5e_pps, - out_work); - struct mlx5e_tstamp *tstamp = container_of(pps_info, struct mlx5e_tstamp, - pps_info); + struct mlx5_pps *pps_info = container_of(work, struct mlx5_pps, + out_work); + struct mlx5_clock *clock = container_of(pps_info, struct mlx5_clock, + pps_info); + struct mlx5_core_dev *mdev = container_of(clock, struct mlx5_core_dev, + clock); u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; unsigned long flags; int i; - for (i = 0; i < tstamp->ptp_info.n_pins; i++) { + for (i = 0; i < clock->ptp_info.n_pins; i++) { u64 tstart; - write_lock_irqsave(&tstamp->lock, flags); - tstart = tstamp->pps_info.start[i]; - tstamp->pps_info.start[i] = 0; - write_unlock_irqrestore(&tstamp->lock, flags); + write_lock_irqsave(&clock->lock, flags); + tstart = clock->pps_info.start[i]; + clock->pps_info.start[i] = 0; + write_unlock_irqrestore(&clock->lock, flags); if (!tstart) continue; MLX5_SET(mtpps_reg, in, pin, i); MLX5_SET64(mtpps_reg, in, time_stamp, tstart); - MLX5_SET(mtpps_reg, in, field_select, MLX5E_MTPPS_FS_TIME_STAMP); - mlx5_set_mtpps(tstamp->mdev, in, sizeof(in)); + MLX5_SET(mtpps_reg, in, field_select, MLX5_MTPPS_FS_TIME_STAMP); + mlx5_set_mtpps(mdev, in, sizeof(in)); } } -static void mlx5e_timestamp_overflow(struct work_struct *work) +static void mlx5_timestamp_overflow(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); - struct mlx5e_tstamp *tstamp = container_of(dwork, struct mlx5e_tstamp, - overflow_work); - struct mlx5e_priv *priv = container_of(tstamp, struct mlx5e_priv, tstamp); + struct mlx5_clock *clock = container_of(dwork, struct mlx5_clock, + overflow_work); unsigned long flags; - write_lock_irqsave(&tstamp->lock, flags); - timecounter_read(&tstamp->clock); - write_unlock_irqrestore(&tstamp->lock, flags); - queue_delayed_work(priv->wq, &tstamp->overflow_work, - msecs_to_jiffies(tstamp->overflow_period * 1000)); -} - -int mlx5e_hwstamp_set(struct mlx5e_priv *priv, struct ifreq *ifr) -{ - struct hwtstamp_config config; - int err; - - if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) - return -EOPNOTSUPP; - - if (copy_from_user(&config, ifr->ifr_data, sizeof(config))) - return -EFAULT; - - /* TX HW timestamp */ - switch (config.tx_type) { - case HWTSTAMP_TX_OFF: - case HWTSTAMP_TX_ON: - break; - default: - return -ERANGE; - } - - mutex_lock(&priv->state_lock); - /* RX HW timestamp */ - switch (config.rx_filter) { - case HWTSTAMP_FILTER_NONE: - /* Reset CQE compression to Admin default */ - mlx5e_modify_rx_cqe_compression_locked(priv, priv->channels.params.rx_cqe_compress_def); - break; - case HWTSTAMP_FILTER_ALL: - case HWTSTAMP_FILTER_SOME: - case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: - case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: - case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: - case HWTSTAMP_FILTER_PTP_V2_L4_EVENT: - case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: - case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: - case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: - case HWTSTAMP_FILTER_PTP_V2_L2_SYNC: - case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ: - case HWTSTAMP_FILTER_PTP_V2_EVENT: - case HWTSTAMP_FILTER_PTP_V2_SYNC: - case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: - case HWTSTAMP_FILTER_NTP_ALL: - /* Disable CQE compression */ - netdev_warn(priv->netdev, "Disabling cqe compression"); - err = mlx5e_modify_rx_cqe_compression_locked(priv, false); - if (err) { - netdev_err(priv->netdev, "Failed disabling cqe compression err=%d\n", err); - mutex_unlock(&priv->state_lock); - return err; - } - config.rx_filter = HWTSTAMP_FILTER_ALL; - break; - default: - mutex_unlock(&priv->state_lock); - return -ERANGE; - } - - memcpy(&priv->tstamp.hwtstamp_config, &config, sizeof(config)); - mutex_unlock(&priv->state_lock); - - return copy_to_user(ifr->ifr_data, &config, - sizeof(config)) ? -EFAULT : 0; -} - -int mlx5e_hwstamp_get(struct mlx5e_priv *priv, struct ifreq *ifr) -{ - struct hwtstamp_config *cfg = &priv->tstamp.hwtstamp_config; - - if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) - return -EOPNOTSUPP; - - return copy_to_user(ifr->ifr_data, cfg, sizeof(*cfg)) ? -EFAULT : 0; + write_lock_irqsave(&clock->lock, flags); + timecounter_read(&clock->tc); + write_unlock_irqrestore(&clock->lock, flags); + schedule_delayed_work(&clock->overflow_work, clock->overflow_period); } -static int mlx5e_ptp_settime(struct ptp_clock_info *ptp, - const struct timespec64 *ts) +static int mlx5_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) { - struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, - ptp_info); + struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, + ptp_info); u64 ns = timespec64_to_ns(ts); unsigned long flags; - write_lock_irqsave(&tstamp->lock, flags); - timecounter_init(&tstamp->clock, &tstamp->cycles, ns); - write_unlock_irqrestore(&tstamp->lock, flags); + write_lock_irqsave(&clock->lock, flags); + timecounter_init(&clock->tc, &clock->cycles, ns); + write_unlock_irqrestore(&clock->lock, flags); return 0; } -static int mlx5e_ptp_gettime(struct ptp_clock_info *ptp, - struct timespec64 *ts) +static int mlx5_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) { - struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, - ptp_info); + struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, + ptp_info); u64 ns; unsigned long flags; - write_lock_irqsave(&tstamp->lock, flags); - ns = timecounter_read(&tstamp->clock); - write_unlock_irqrestore(&tstamp->lock, flags); + write_lock_irqsave(&clock->lock, flags); + ns = timecounter_read(&clock->tc); + write_unlock_irqrestore(&clock->lock, flags); *ts = ns_to_timespec64(ns); return 0; } -static int mlx5e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +static int mlx5_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { - struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, - ptp_info); + struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, + ptp_info); unsigned long flags; - write_lock_irqsave(&tstamp->lock, flags); - timecounter_adjtime(&tstamp->clock, delta); - write_unlock_irqrestore(&tstamp->lock, flags); + write_lock_irqsave(&clock->lock, flags); + timecounter_adjtime(&clock->tc, delta); + write_unlock_irqrestore(&clock->lock, flags); return 0; } -static int mlx5e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) +static int mlx5_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) { u64 adj; u32 diff; unsigned long flags; int neg_adj = 0; - struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, - ptp_info); + struct mlx5_clock *clock = container_of(ptp, struct mlx5_clock, + ptp_info); if (delta < 0) { neg_adj = 1; delta = -delta; } - adj = tstamp->nominal_c_mult; + adj = clock->nominal_c_mult; adj *= delta; diff = div_u64(adj, 1000000000ULL); - write_lock_irqsave(&tstamp->lock, flags); - timecounter_read(&tstamp->clock); - tstamp->cycles.mult = neg_adj ? tstamp->nominal_c_mult - diff : - tstamp->nominal_c_mult + diff; - write_unlock_irqrestore(&tstamp->lock, flags); + write_lock_irqsave(&clock->lock, flags); + timecounter_read(&clock->tc); + clock->cycles.mult = neg_adj ? clock->nominal_c_mult - diff : + clock->nominal_c_mult + diff; + write_unlock_irqrestore(&clock->lock, flags); return 0; } -static int mlx5e_extts_configure(struct ptp_clock_info *ptp, - struct ptp_clock_request *rq, - int on) +static int mlx5_extts_configure(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, + int on) { - struct mlx5e_tstamp *tstamp = - container_of(ptp, struct mlx5e_tstamp, ptp_info); - struct mlx5e_priv *priv = - container_of(tstamp, struct mlx5e_priv, tstamp); + struct mlx5_clock *clock = + container_of(ptp, struct mlx5_clock, ptp_info); + struct mlx5_core_dev *mdev = + container_of(clock, struct mlx5_core_dev, clock); u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; u32 field_select = 0; u8 pin_mode = 0; @@ -285,24 +199,24 @@ static int mlx5e_extts_configure(struct ptp_clock_info *ptp, int pin = -1; int err = 0; - if (!MLX5_PPS_CAP(priv->mdev)) + if (!MLX5_PPS_CAP(mdev)) return -EOPNOTSUPP; - if (rq->extts.index >= tstamp->ptp_info.n_pins) + if (rq->extts.index >= clock->ptp_info.n_pins) return -EINVAL; if (on) { - pin = ptp_find_pin(tstamp->ptp, PTP_PF_EXTTS, rq->extts.index); + pin = ptp_find_pin(clock->ptp, PTP_PF_EXTTS, rq->extts.index); if (pin < 0) return -EBUSY; - pin_mode = MLX5E_PIN_MODE_IN; + pin_mode = MLX5_PIN_MODE_IN; pattern = !!(rq->extts.flags & PTP_FALLING_EDGE); - field_select = MLX5E_MTPPS_FS_PIN_MODE | - MLX5E_MTPPS_FS_PATTERN | - MLX5E_MTPPS_FS_ENABLE; + field_select = MLX5_MTPPS_FS_PIN_MODE | + MLX5_MTPPS_FS_PATTERN | + MLX5_MTPPS_FS_ENABLE; } else { pin = rq->extts.index; - field_select = MLX5E_MTPPS_FS_ENABLE; + field_select = MLX5_MTPPS_FS_ENABLE; } MLX5_SET(mtpps_reg, in, pin, pin); @@ -311,22 +225,22 @@ static int mlx5e_extts_configure(struct ptp_clock_info *ptp, MLX5_SET(mtpps_reg, in, enable, on); MLX5_SET(mtpps_reg, in, field_select, field_select); - err = mlx5_set_mtpps(priv->mdev, in, sizeof(in)); + err = mlx5_set_mtpps(mdev, in, sizeof(in)); if (err) return err; - return mlx5_set_mtppse(priv->mdev, pin, 0, - MLX5E_EVENT_MODE_REPETETIVE & on); + return mlx5_set_mtppse(mdev, pin, 0, + MLX5_EVENT_MODE_REPETETIVE & on); } -static int mlx5e_perout_configure(struct ptp_clock_info *ptp, - struct ptp_clock_request *rq, - int on) +static int mlx5_perout_configure(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, + int on) { - struct mlx5e_tstamp *tstamp = - container_of(ptp, struct mlx5e_tstamp, ptp_info); - struct mlx5e_priv *priv = - container_of(tstamp, struct mlx5e_priv, tstamp); + struct mlx5_clock *clock = + container_of(ptp, struct mlx5_clock, ptp_info); + struct mlx5_core_dev *mdev = + container_of(clock, struct mlx5_core_dev, clock); u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; u64 nsec_now, nsec_delta, time_stamp = 0; u64 cycles_now, cycles_delta; @@ -339,20 +253,20 @@ static int mlx5e_perout_configure(struct ptp_clock_info *ptp, int err = 0; s64 ns; - if (!MLX5_PPS_CAP(priv->mdev)) + if (!MLX5_PPS_CAP(mdev)) return -EOPNOTSUPP; - if (rq->perout.index >= tstamp->ptp_info.n_pins) + if (rq->perout.index >= clock->ptp_info.n_pins) return -EINVAL; if (on) { - pin = ptp_find_pin(tstamp->ptp, PTP_PF_PEROUT, + pin = ptp_find_pin(clock->ptp, PTP_PF_PEROUT, rq->perout.index); if (pin < 0) return -EBUSY; - pin_mode = MLX5E_PIN_MODE_OUT; - pattern = MLX5E_OUT_PATTERN_PERIODIC; + pin_mode = MLX5_PIN_MODE_OUT; + pattern = MLX5_OUT_PATTERN_PERIODIC; ts.tv_sec = rq->perout.period.sec; ts.tv_nsec = rq->perout.period.nsec; ns = timespec64_to_ns(&ts); @@ -363,21 +277,21 @@ static int mlx5e_perout_configure(struct ptp_clock_info *ptp, ts.tv_sec = rq->perout.start.sec; ts.tv_nsec = rq->perout.start.nsec; ns = timespec64_to_ns(&ts); - cycles_now = mlx5_read_internal_timer(tstamp->mdev); - write_lock_irqsave(&tstamp->lock, flags); - nsec_now = timecounter_cyc2time(&tstamp->clock, cycles_now); + cycles_now = mlx5_read_internal_timer(mdev); + write_lock_irqsave(&clock->lock, flags); + nsec_now = timecounter_cyc2time(&clock->tc, cycles_now); nsec_delta = ns - nsec_now; - cycles_delta = div64_u64(nsec_delta << tstamp->cycles.shift, - tstamp->cycles.mult); - write_unlock_irqrestore(&tstamp->lock, flags); + cycles_delta = div64_u64(nsec_delta << clock->cycles.shift, + clock->cycles.mult); + write_unlock_irqrestore(&clock->lock, flags); time_stamp = cycles_now + cycles_delta; - field_select = MLX5E_MTPPS_FS_PIN_MODE | - MLX5E_MTPPS_FS_PATTERN | - MLX5E_MTPPS_FS_ENABLE | - MLX5E_MTPPS_FS_TIME_STAMP; + field_select = MLX5_MTPPS_FS_PIN_MODE | + MLX5_MTPPS_FS_PATTERN | + MLX5_MTPPS_FS_ENABLE | + MLX5_MTPPS_FS_TIME_STAMP; } else { pin = rq->perout.index; - field_select = MLX5E_MTPPS_FS_ENABLE; + field_select = MLX5_MTPPS_FS_ENABLE; } MLX5_SET(mtpps_reg, in, pin, pin); @@ -387,233 +301,225 @@ static int mlx5e_perout_configure(struct ptp_clock_info *ptp, MLX5_SET64(mtpps_reg, in, time_stamp, time_stamp); MLX5_SET(mtpps_reg, in, field_select, field_select); - err = mlx5_set_mtpps(priv->mdev, in, sizeof(in)); + err = mlx5_set_mtpps(mdev, in, sizeof(in)); if (err) return err; - return mlx5_set_mtppse(priv->mdev, pin, 0, - MLX5E_EVENT_MODE_REPETETIVE & on); + return mlx5_set_mtppse(mdev, pin, 0, + MLX5_EVENT_MODE_REPETETIVE & on); } -static int mlx5e_pps_configure(struct ptp_clock_info *ptp, - struct ptp_clock_request *rq, - int on) +static int mlx5_pps_configure(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, + int on) { - struct mlx5e_tstamp *tstamp = - container_of(ptp, struct mlx5e_tstamp, ptp_info); + struct mlx5_clock *clock = + container_of(ptp, struct mlx5_clock, ptp_info); - tstamp->pps_info.enabled = !!on; + clock->pps_info.enabled = !!on; return 0; } -static int mlx5e_ptp_enable(struct ptp_clock_info *ptp, - struct ptp_clock_request *rq, - int on) +static int mlx5_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, + int on) { switch (rq->type) { case PTP_CLK_REQ_EXTTS: - return mlx5e_extts_configure(ptp, rq, on); + return mlx5_extts_configure(ptp, rq, on); case PTP_CLK_REQ_PEROUT: - return mlx5e_perout_configure(ptp, rq, on); + return mlx5_perout_configure(ptp, rq, on); case PTP_CLK_REQ_PPS: - return mlx5e_pps_configure(ptp, rq, on); + return mlx5_pps_configure(ptp, rq, on); default: return -EOPNOTSUPP; } return 0; } -static int mlx5e_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, - enum ptp_pin_function func, unsigned int chan) +static int mlx5_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin, + enum ptp_pin_function func, unsigned int chan) { return (func == PTP_PF_PHYSYNC) ? -EOPNOTSUPP : 0; } -static const struct ptp_clock_info mlx5e_ptp_clock_info = { +static const struct ptp_clock_info mlx5_ptp_clock_info = { .owner = THIS_MODULE, + .name = "mlx5_p2p", .max_adj = 100000000, .n_alarm = 0, .n_ext_ts = 0, .n_per_out = 0, .n_pins = 0, .pps = 0, - .adjfreq = mlx5e_ptp_adjfreq, - .adjtime = mlx5e_ptp_adjtime, - .gettime64 = mlx5e_ptp_gettime, - .settime64 = mlx5e_ptp_settime, + .adjfreq = mlx5_ptp_adjfreq, + .adjtime = mlx5_ptp_adjtime, + .gettime64 = mlx5_ptp_gettime, + .settime64 = mlx5_ptp_settime, .enable = NULL, .verify = NULL, }; -static void mlx5e_timestamp_init_config(struct mlx5e_tstamp *tstamp) -{ - tstamp->hwtstamp_config.tx_type = HWTSTAMP_TX_OFF; - tstamp->hwtstamp_config.rx_filter = HWTSTAMP_FILTER_NONE; -} - -static int mlx5e_init_pin_config(struct mlx5e_tstamp *tstamp) +static int mlx5_init_pin_config(struct mlx5_clock *clock) { int i; - tstamp->ptp_info.pin_config = - kzalloc(sizeof(*tstamp->ptp_info.pin_config) * - tstamp->ptp_info.n_pins, GFP_KERNEL); - if (!tstamp->ptp_info.pin_config) + clock->ptp_info.pin_config = + kzalloc(sizeof(*clock->ptp_info.pin_config) * + clock->ptp_info.n_pins, GFP_KERNEL); + if (!clock->ptp_info.pin_config) return -ENOMEM; - tstamp->ptp_info.enable = mlx5e_ptp_enable; - tstamp->ptp_info.verify = mlx5e_ptp_verify; - tstamp->ptp_info.pps = 1; + clock->ptp_info.enable = mlx5_ptp_enable; + clock->ptp_info.verify = mlx5_ptp_verify; + clock->ptp_info.pps = 1; - for (i = 0; i < tstamp->ptp_info.n_pins; i++) { - snprintf(tstamp->ptp_info.pin_config[i].name, - sizeof(tstamp->ptp_info.pin_config[i].name), + for (i = 0; i < clock->ptp_info.n_pins; i++) { + snprintf(clock->ptp_info.pin_config[i].name, + sizeof(clock->ptp_info.pin_config[i].name), "mlx5_pps%d", i); - tstamp->ptp_info.pin_config[i].index = i; - tstamp->ptp_info.pin_config[i].func = PTP_PF_NONE; - tstamp->ptp_info.pin_config[i].chan = i; + clock->ptp_info.pin_config[i].index = i; + clock->ptp_info.pin_config[i].func = PTP_PF_NONE; + clock->ptp_info.pin_config[i].chan = i; } return 0; } -static void mlx5e_get_pps_caps(struct mlx5e_priv *priv, - struct mlx5e_tstamp *tstamp) +static void mlx5_get_pps_caps(struct mlx5_core_dev *mdev) { + struct mlx5_clock *clock = &mdev->clock; u32 out[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; - mlx5_query_mtpps(priv->mdev, out, sizeof(out)); - - tstamp->ptp_info.n_pins = MLX5_GET(mtpps_reg, out, - cap_number_of_pps_pins); - tstamp->ptp_info.n_ext_ts = MLX5_GET(mtpps_reg, out, - cap_max_num_of_pps_in_pins); - tstamp->ptp_info.n_per_out = MLX5_GET(mtpps_reg, out, - cap_max_num_of_pps_out_pins); - - tstamp->pps_info.pin_caps[0] = MLX5_GET(mtpps_reg, out, cap_pin_0_mode); - tstamp->pps_info.pin_caps[1] = MLX5_GET(mtpps_reg, out, cap_pin_1_mode); - tstamp->pps_info.pin_caps[2] = MLX5_GET(mtpps_reg, out, cap_pin_2_mode); - tstamp->pps_info.pin_caps[3] = MLX5_GET(mtpps_reg, out, cap_pin_3_mode); - tstamp->pps_info.pin_caps[4] = MLX5_GET(mtpps_reg, out, cap_pin_4_mode); - tstamp->pps_info.pin_caps[5] = MLX5_GET(mtpps_reg, out, cap_pin_5_mode); - tstamp->pps_info.pin_caps[6] = MLX5_GET(mtpps_reg, out, cap_pin_6_mode); - tstamp->pps_info.pin_caps[7] = MLX5_GET(mtpps_reg, out, cap_pin_7_mode); + mlx5_query_mtpps(mdev, out, sizeof(out)); + + clock->ptp_info.n_pins = MLX5_GET(mtpps_reg, out, + cap_number_of_pps_pins); + clock->ptp_info.n_ext_ts = MLX5_GET(mtpps_reg, out, + cap_max_num_of_pps_in_pins); + clock->ptp_info.n_per_out = MLX5_GET(mtpps_reg, out, + cap_max_num_of_pps_out_pins); + + clock->pps_info.pin_caps[0] = MLX5_GET(mtpps_reg, out, cap_pin_0_mode); + clock->pps_info.pin_caps[1] = MLX5_GET(mtpps_reg, out, cap_pin_1_mode); + clock->pps_info.pin_caps[2] = MLX5_GET(mtpps_reg, out, cap_pin_2_mode); + clock->pps_info.pin_caps[3] = MLX5_GET(mtpps_reg, out, cap_pin_3_mode); + clock->pps_info.pin_caps[4] = MLX5_GET(mtpps_reg, out, cap_pin_4_mode); + clock->pps_info.pin_caps[5] = MLX5_GET(mtpps_reg, out, cap_pin_5_mode); + clock->pps_info.pin_caps[6] = MLX5_GET(mtpps_reg, out, cap_pin_6_mode); + clock->pps_info.pin_caps[7] = MLX5_GET(mtpps_reg, out, cap_pin_7_mode); } -void mlx5e_pps_event_handler(struct mlx5e_priv *priv, - struct ptp_clock_event *event) +void mlx5_pps_event(struct mlx5_core_dev *mdev, + struct mlx5_eqe *eqe) { - struct net_device *netdev = priv->netdev; - struct mlx5e_tstamp *tstamp = &priv->tstamp; + struct mlx5_clock *clock = &mdev->clock; + struct ptp_clock_event ptp_event; struct timespec64 ts; u64 nsec_now, nsec_delta; u64 cycles_now, cycles_delta; - int pin = event->index; + int pin = eqe->data.pps.pin; s64 ns; unsigned long flags; - switch (tstamp->ptp_info.pin_config[pin].func) { + switch (clock->ptp_info.pin_config[pin].func) { case PTP_PF_EXTTS: - if (tstamp->pps_info.enabled) { - event->type = PTP_CLOCK_PPSUSR; - event->pps_times.ts_real = ns_to_timespec64(event->timestamp); + if (clock->pps_info.enabled) { + ptp_event.type = PTP_CLOCK_PPSUSR; + ptp_event.pps_times.ts_real = ns_to_timespec64(eqe->data.pps.time_stamp); } else { - event->type = PTP_CLOCK_EXTTS; + ptp_event.type = PTP_CLOCK_EXTTS; } - ptp_clock_event(tstamp->ptp, event); + ptp_clock_event(clock->ptp, &ptp_event); break; case PTP_PF_PEROUT: - mlx5e_ptp_gettime(&tstamp->ptp_info, &ts); - cycles_now = mlx5_read_internal_timer(tstamp->mdev); + mlx5_ptp_gettime(&clock->ptp_info, &ts); + cycles_now = mlx5_read_internal_timer(mdev); ts.tv_sec += 1; ts.tv_nsec = 0; ns = timespec64_to_ns(&ts); - write_lock_irqsave(&tstamp->lock, flags); - nsec_now = timecounter_cyc2time(&tstamp->clock, cycles_now); + write_lock_irqsave(&clock->lock, flags); + nsec_now = timecounter_cyc2time(&clock->tc, cycles_now); nsec_delta = ns - nsec_now; - cycles_delta = div64_u64(nsec_delta << tstamp->cycles.shift, - tstamp->cycles.mult); - tstamp->pps_info.start[pin] = cycles_now + cycles_delta; - queue_work(priv->wq, &tstamp->pps_info.out_work); - write_unlock_irqrestore(&tstamp->lock, flags); + cycles_delta = div64_u64(nsec_delta << clock->cycles.shift, + clock->cycles.mult); + clock->pps_info.start[pin] = cycles_now + cycles_delta; + schedule_work(&clock->pps_info.out_work); + write_unlock_irqrestore(&clock->lock, flags); break; default: - netdev_err(netdev, "%s: Unhandled event\n", __func__); + mlx5_core_err(mdev, " Unhandled event\n"); } } -void mlx5e_timestamp_init(struct mlx5e_priv *priv) +void mlx5_init_clock(struct mlx5_core_dev *mdev) { - struct mlx5e_tstamp *tstamp = &priv->tstamp; + struct mlx5_clock *clock = &mdev->clock; u64 ns; u64 frac = 0; u32 dev_freq; - mlx5e_timestamp_init_config(tstamp); - dev_freq = MLX5_CAP_GEN(priv->mdev, device_frequency_khz); + dev_freq = MLX5_CAP_GEN(mdev, device_frequency_khz); if (!dev_freq) { - mlx5_core_warn(priv->mdev, "invalid device_frequency_khz, aborting HW clock init\n"); + mlx5_core_warn(mdev, "invalid device_frequency_khz, aborting HW clock init\n"); return; } - rwlock_init(&tstamp->lock); - tstamp->cycles.read = mlx5e_read_internal_timer; - tstamp->cycles.shift = MLX5E_CYCLES_SHIFT; - tstamp->cycles.mult = clocksource_khz2mult(dev_freq, - tstamp->cycles.shift); - tstamp->nominal_c_mult = tstamp->cycles.mult; - tstamp->cycles.mask = CLOCKSOURCE_MASK(41); - tstamp->mdev = priv->mdev; - - timecounter_init(&tstamp->clock, &tstamp->cycles, + rwlock_init(&clock->lock); + clock->cycles.read = read_internal_timer; + clock->cycles.shift = MLX5_CYCLES_SHIFT; + clock->cycles.mult = clocksource_khz2mult(dev_freq, + clock->cycles.shift); + clock->nominal_c_mult = clock->cycles.mult; + clock->cycles.mask = CLOCKSOURCE_MASK(41); + + timecounter_init(&clock->tc, &clock->cycles, ktime_to_ns(ktime_get_real())); /* Calculate period in seconds to call the overflow watchdog - to make * sure counter is checked at least once every wrap around. */ - ns = cyclecounter_cyc2ns(&tstamp->cycles, tstamp->cycles.mask, + ns = cyclecounter_cyc2ns(&clock->cycles, clock->cycles.mask, frac, &frac); do_div(ns, NSEC_PER_SEC / 2 / HZ); - tstamp->overflow_period = ns; + clock->overflow_period = ns; - INIT_WORK(&tstamp->pps_info.out_work, mlx5e_pps_out); - INIT_DELAYED_WORK(&tstamp->overflow_work, mlx5e_timestamp_overflow); - if (tstamp->overflow_period) - queue_delayed_work(priv->wq, &tstamp->overflow_work, 0); + INIT_WORK(&clock->pps_info.out_work, mlx5_pps_out); + INIT_DELAYED_WORK(&clock->overflow_work, mlx5_timestamp_overflow); + if (clock->overflow_period) + schedule_delayed_work(&clock->overflow_work, 0); else - mlx5_core_warn(priv->mdev, "invalid overflow period, overflow_work is not scheduled\n"); + mlx5_core_warn(mdev, "invalid overflow period, overflow_work is not scheduled\n"); /* Configure the PHC */ - tstamp->ptp_info = mlx5e_ptp_clock_info; - snprintf(tstamp->ptp_info.name, 16, "mlx5 ptp"); + clock->ptp_info = mlx5_ptp_clock_info; /* Initialize 1PPS data structures */ - if (MLX5_PPS_CAP(priv->mdev)) - mlx5e_get_pps_caps(priv, tstamp); - if (tstamp->ptp_info.n_pins) - mlx5e_init_pin_config(tstamp); - - tstamp->ptp = ptp_clock_register(&tstamp->ptp_info, - &priv->mdev->pdev->dev); - if (IS_ERR(tstamp->ptp)) { - mlx5_core_warn(priv->mdev, "ptp_clock_register failed %ld\n", - PTR_ERR(tstamp->ptp)); - tstamp->ptp = NULL; + if (MLX5_PPS_CAP(mdev)) + mlx5_get_pps_caps(mdev); + if (clock->ptp_info.n_pins) + mlx5_init_pin_config(clock); + + clock->ptp = ptp_clock_register(&clock->ptp_info, + &mdev->pdev->dev); + if (IS_ERR(clock->ptp)) { + mlx5_core_warn(mdev, "ptp_clock_register failed %ld\n", + PTR_ERR(clock->ptp)); + clock->ptp = NULL; } } -void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv) +void mlx5_cleanup_clock(struct mlx5_core_dev *mdev) { - struct mlx5e_tstamp *tstamp = &priv->tstamp; + struct mlx5_clock *clock = &mdev->clock; - if (!MLX5_CAP_GEN(priv->mdev, device_frequency_khz)) + if (!MLX5_CAP_GEN(mdev, device_frequency_khz)) return; - if (priv->tstamp.ptp) { - ptp_clock_unregister(priv->tstamp.ptp); - priv->tstamp.ptp = NULL; + if (clock->ptp) { + ptp_clock_unregister(clock->ptp); + clock->ptp = NULL; } - cancel_work_sync(&tstamp->pps_info.out_work); - cancel_delayed_work_sync(&tstamp->overflow_work); - kfree(tstamp->ptp_info.pin_config); + cancel_work_sync(&clock->pps_info.out_work); + cancel_delayed_work_sync(&clock->overflow_work); + kfree(clock->ptp_info.pin_config); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h new file mode 100644 index 000000000000..a8eecedd46c2 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/clock.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2017, Mellanox Technologies, Ltd. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __LIB_CLOCK_H__ +#define __LIB_CLOCK_H__ + +void mlx5_init_clock(struct mlx5_core_dev *mdev); +void mlx5_cleanup_clock(struct mlx5_core_dev *mdev); + +static inline ktime_t mlx5_timecounter_cyc2time(struct mlx5_clock *clock, + u64 timestamp) +{ + u64 nsec; + + read_lock(&clock->lock); + nsec = timecounter_cyc2time(&clock->tc, timestamp); + read_unlock(&clock->lock); + + return ns_to_ktime(nsec); +} + +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 0d2c8dcd6eae..ecbe9fad22d8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -59,6 +59,7 @@ #include "lib/mlx5.h" #include "fpga/core.h" #include "accel/ipsec.h" +#include "lib/clock.h" MODULE_AUTHOR("Eli Cohen "); MODULE_DESCRIPTION("Mellanox Connect-IB, ConnectX-4 core driver"); @@ -889,6 +890,8 @@ static int mlx5_init_once(struct mlx5_core_dev *dev, struct mlx5_priv *priv) mlx5_init_reserved_gids(dev); + mlx5_init_clock(dev); + err = mlx5_init_rl_table(dev); if (err) { dev_err(&pdev->dev, "Failed to init rate limiting\n"); @@ -949,6 +952,7 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev) mlx5_eswitch_cleanup(dev->priv.eswitch); mlx5_mpfs_cleanup(dev); mlx5_cleanup_rl_table(dev); + mlx5_cleanup_clock(dev); mlx5_cleanup_reserved_gids(dev); mlx5_cleanup_mkey_table(dev); mlx5_cleanup_srq_table(dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index b7c2900b75f9..8f00de2fe283 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -93,6 +93,7 @@ void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, unsigned long param); void mlx5_core_page_fault(struct mlx5_core_dev *dev, struct mlx5_pagefault *pfault); +void mlx5_pps_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe); void mlx5_port_module_event(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe); void mlx5_enter_error_state(struct mlx5_core_dev *dev, bool force); void mlx5_disable_device(struct mlx5_core_dev *dev); diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 401c8972cc3a..08c77b7e59cb 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -49,6 +49,8 @@ #include #include #include +#include +#include enum { MLX5_BOARD_ID_LEN = 64, @@ -760,6 +762,27 @@ struct mlx5_rsvd_gids { struct ida ida; }; +#define MAX_PIN_NUM 8 +struct mlx5_pps { + u8 pin_caps[MAX_PIN_NUM]; + struct work_struct out_work; + u64 start[MAX_PIN_NUM]; + u8 enabled; +}; + +struct mlx5_clock { + rwlock_t lock; + struct cyclecounter cycles; + struct timecounter tc; + struct hwtstamp_config hwtstamp_config; + u32 nominal_c_mult; + unsigned long overflow_period; + struct delayed_work overflow_work; + struct ptp_clock *ptp; + struct ptp_clock_info ptp_info; + struct mlx5_pps pps_info; +}; + struct mlx5_core_dev { struct pci_dev *pdev; /* sync pci state */ @@ -800,6 +823,7 @@ struct mlx5_core_dev { #ifdef CONFIG_RFS_ACCEL struct cpu_rmap *rmap; #endif + struct mlx5_clock clock; }; struct mlx5_db { -- cgit v1.2.3-59-g8ed1b From ad7532cefd11d11a0814a75fb814c205ee3d9d4c Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Wed, 11 Oct 2017 09:35:01 -0700 Subject: iio: hid-sensor-trigger: Don't touch sensors unless user space requests One of the user complained that on his system Thinkpad Yoga S1, with commit f1664eaacec3 ("iio: hid-sensor-trigger: Fix the race with user space powering up sensors") causes the system to resume immediately on suspend (S3 operation). On this system the sensor hub is on USB and is a wake up device from S3. So if any sensor sends data on motion, the system will wake up. This can be a legitimate use case to wake up device motion, but that needs proper user space support to set right thresholds. In fact the above commit didn't cause this regression, but any operation which cause sensors to wake up would have caused the same issue. So if user reads the raw sensor data, same issue occurs, with or without this commit. Only difference is that the above commit by default will trigger a power up and power down of sensors as part of runtime pm enable (runtime enable will cause a runtime resume callback followed by runtime_suspend callback). Previously user has to do some action on sensors. On investigation it was observed that the current driver correctly changing the state of all sensors to power off but then also some sensor will still send some data. Only option is to never power up any sensor. Only good option is to: - Using sysfs interface disable USB as a wakeup device (This will not need any driver change) Since some user don't care about sensors. So for those users this change brings back old functionality. As long as they don't cause any operation to power up sensors (like raw read or start iio-sensor-proxy service), the sensors will not be to touched. This is done by delaying run time enable till user space does some operation with sensors. Link: https://bugzilla.kernel.org/show_bug.cgi?id=196853 Signed-off-by: Srinivas Pandruvada Signed-off-by: Jonathan Cameron --- drivers/iio/common/hid-sensors/hid-sensor-trigger.c | 12 +++++++++--- include/linux/hid-sensor-hub.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 55dfd6288d18..cfb6588565ba 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -179,6 +179,10 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state) int ret; atomic_set(&st->user_requested_state, state); + + if (atomic_add_unless(&st->runtime_pm_enable, 1, 1)) + pm_runtime_enable(&st->pdev->dev); + if (state) ret = pm_runtime_get_sync(&st->pdev->dev); else { @@ -221,7 +225,8 @@ static void hid_sensor_set_power_work(struct work_struct *work) if (attrb->latency_ms > 0) hid_sensor_set_report_latency(attrb, attrb->latency_ms); - _hid_sensor_power_state(attrb, true); + if (atomic_read(&attrb->user_requested_state)) + _hid_sensor_power_state(attrb, true); } static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, @@ -232,7 +237,9 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, void hid_sensor_remove_trigger(struct hid_sensor_common *attrb) { - pm_runtime_disable(&attrb->pdev->dev); + if (atomic_read(&attrb->runtime_pm_enable)) + pm_runtime_disable(&attrb->pdev->dev); + pm_runtime_set_suspended(&attrb->pdev->dev); pm_runtime_put_noidle(&attrb->pdev->dev); @@ -282,7 +289,6 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, INIT_WORK(&attrb->work, hid_sensor_set_power_work); pm_suspend_ignore_children(&attrb->pdev->dev, true); - pm_runtime_enable(&attrb->pdev->dev); /* Default to 3 seconds, but can be changed from sysfs */ pm_runtime_set_autosuspend_delay(&attrb->pdev->dev, 3000); diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h index fc7aae64dcde..331dc377c275 100644 --- a/include/linux/hid-sensor-hub.h +++ b/include/linux/hid-sensor-hub.h @@ -231,6 +231,7 @@ struct hid_sensor_common { unsigned usage_id; atomic_t data_ready; atomic_t user_requested_state; + atomic_t runtime_pm_enable; int poll_interval; int raw_hystersis; int latency_ms; -- cgit v1.2.3-59-g8ed1b From 8c04f7a3e347cb2a00074d197f5cd5b25c6fe383 Mon Sep 17 00:00:00 2001 From: Romain Perier Date: Mon, 4 Sep 2017 10:51:16 +0200 Subject: clk: rockchip: add clock id for PCLK_EFUSE256 of RK3368 SoCs Signed-off-by: Romain Perier Signed-off-by: Heiko Stuebner --- include/dt-bindings/clock/rk3368-cru.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/dt-bindings/clock/rk3368-cru.h b/include/dt-bindings/clock/rk3368-cru.h index aeb83e581a11..a0063ed7284a 100644 --- a/include/dt-bindings/clock/rk3368-cru.h +++ b/include/dt-bindings/clock/rk3368-cru.h @@ -156,6 +156,7 @@ #define PCLK_ISP 366 #define PCLK_VIP 367 #define PCLK_WDT 368 +#define PCLK_EFUSE256 369 /* hclk gates */ #define HCLK_SFC 448 -- cgit v1.2.3-59-g8ed1b From 4e07533f30e8e2d4447fc4e159d34b4068d96efc Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Fri, 15 Sep 2017 10:33:49 +0200 Subject: clk: rockchip: add more rk3188 graphics clock ids Add ids for cif, v{d/e}pu clocks on rk3188. ACLK_CIF does get a needed 1 at it's end but that should be safe because no driver for the camera interface has surfaced so far and the old vendor kernels for these socs are based on linux-3.0 and still used board files then, so there really are no previous users anywhere to be found. Signed-off-by: Heiko Stuebner --- include/dt-bindings/clock/rk3188-cru-common.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/dt-bindings/clock/rk3188-cru-common.h b/include/dt-bindings/clock/rk3188-cru-common.h index eff4319d008b..b9462b7d3dfe 100644 --- a/include/dt-bindings/clock/rk3188-cru-common.h +++ b/include/dt-bindings/clock/rk3188-cru-common.h @@ -68,12 +68,14 @@ #define ACLK_LCDC1 196 #define ACLK_GPU 197 #define ACLK_SMC 198 -#define ACLK_CIF 199 +#define ACLK_CIF1 199 #define ACLK_IPP 200 #define ACLK_RGA 201 #define ACLK_CIF0 202 #define ACLK_CPU 203 #define ACLK_PERI 204 +#define ACLK_VEPU 205 +#define ACLK_VDPU 206 /* pclk gates */ #define PCLK_GRF 320 @@ -134,8 +136,11 @@ #define HCLK_NANDC0 467 #define HCLK_CPU 468 #define HCLK_PERI 469 +#define HCLK_CIF1 470 +#define HCLK_VEPU 471 +#define HCLK_VDPU 472 -#define CLK_NR_CLKS (HCLK_PERI + 1) +#define CLK_NR_CLKS (HCLK_VDPU + 1) /* soft-reset indices */ #define SRST_MCORE 2 -- cgit v1.2.3-59-g8ed1b From 1c3aea2bc8f0b2e5b57375ead40457ff75a3a2ec Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 11 Oct 2017 10:48:43 -0700 Subject: IB/core: Fix endianness annotation in rdma_is_multicast_addr() Since ipv4_addr is a big endian 32-bit number, annotate it as such. Fixes: commit be1d325a3358 ("IB/core: Set RoCEv2 MGID according to spec") Signed-off-by: Bart Van Assche Reviewed-by: Leon Romanovsky Signed-off-by: Doug Ledford --- include/rdma/ib_addr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h index ec5008cf5d51..cfa82d16573d 100644 --- a/include/rdma/ib_addr.h +++ b/include/rdma/ib_addr.h @@ -305,12 +305,12 @@ static inline void rdma_get_ll_mac(struct in6_addr *addr, u8 *mac) static inline int rdma_is_multicast_addr(struct in6_addr *addr) { - u32 ipv4_addr; + __be32 ipv4_addr; if (addr->s6_addr[0] == 0xff) return 1; - memcpy(&ipv4_addr, addr->s6_addr + 12, 4); + ipv4_addr = addr->s6_addr32[3]; return (ipv6_addr_v4mapped(addr) && ipv4_is_multicast(ipv4_addr)); } -- cgit v1.2.3-59-g8ed1b From 841f4f24053acad69240c6ab7427a1d24bc29491 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 13 Oct 2017 14:18:09 -0400 Subject: net: dsa: remove .set_addr Now that there is no user for the .set_addr function, remove it from DSA. If a switch supports this feature (like mv88e6xxx), the implementation can be done in the driver setup. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 1 - net/dsa/dsa2.c | 6 ------ net/dsa/legacy.c | 6 ------ 3 files changed, 13 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index ce1d622734d7..2746741f74cf 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -291,7 +291,6 @@ struct dsa_switch_ops { enum dsa_tag_protocol (*get_tag_protocol)(struct dsa_switch *ds); int (*setup)(struct dsa_switch *ds); - int (*set_addr)(struct dsa_switch *ds, u8 *addr); u32 (*get_phy_flags)(struct dsa_switch *ds, int port); /* diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 54ed054777bd..6ac9e11d385c 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -336,12 +336,6 @@ static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds) if (err) return err; - if (ds->ops->set_addr) { - err = ds->ops->set_addr(ds, dst->cpu_dp->netdev->dev_addr); - if (err < 0) - return err; - } - if (!ds->slave_mii_bus && ds->ops->phy_read) { ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev); if (!ds->slave_mii_bus) diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 19ff6e0a21dc..b0fefbffe082 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -172,12 +172,6 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, if (ret) return ret; - if (ops->set_addr) { - ret = ops->set_addr(ds, master->dev_addr); - if (ret < 0) - return ret; - } - if (!ds->slave_mii_bus && ops->phy_read) { ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev); if (!ds->slave_mii_bus) -- cgit v1.2.3-59-g8ed1b From e086101b150ae8e99e54ab26101ef3835fa9f48d Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 13 Oct 2017 13:03:16 -0700 Subject: tcp: add a tracepoint for tcp retransmission We need a real-time notification for tcp retransmission for monitoring. Of course we could use ftrace to dynamically instrument this kernel function too, however we can't retrieve the connection information at the same time, for example perf-tools [1] reads /proc/net/tcp for socket details, which is slow when we have a lots of connections. Therefore, this patch adds a tracepoint for __tcp_retransmit_skb() and exposes src/dst IP addresses and ports of the connection. This also makes it easier to integrate into perf. Note, I expose both IPv4 and IPv6 addresses at the same time: for a IPv4 socket, v4 mapped address is used as IPv6 addresses, for a IPv6 socket, LOOPBACK4_IPV6 is already filled by kernel. Also, add sk and skb pointers as they are useful for BPF. 1. https://github.com/brendangregg/perf-tools/blob/master/net/tcpretrans Cc: Eric Dumazet Cc: Alexei Starovoitov Cc: Hannes Frederic Sowa Cc: Brendan Gregg Cc: Neal Cardwell Signed-off-by: Cong Wang Acked-by: Alexei Starovoitov Acked-by: Brendan Gregg Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 68 ++++++++++++++++++++++++++++++++++++++++++++++ net/core/net-traces.c | 1 + net/ipv4/tcp_output.c | 3 ++ 3 files changed, 72 insertions(+) create mode 100644 include/trace/events/tcp.h (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h new file mode 100644 index 000000000000..3d1cbd072b7e --- /dev/null +++ b/include/trace/events/tcp.h @@ -0,0 +1,68 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM tcp + +#if !defined(_TRACE_TCP_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_TCP_H + +#include +#include +#include +#include + +TRACE_EVENT(tcp_retransmit_skb, + + TP_PROTO(struct sock *sk, struct sk_buff *skb), + + TP_ARGS(sk, skb), + + TP_STRUCT__entry( + __field(void *, skbaddr) + __field(void *, skaddr) + __field(__u16, sport) + __field(__u16, dport) + __array(__u8, saddr, 4) + __array(__u8, daddr, 4) + __array(__u8, saddr_v6, 16) + __array(__u8, daddr_v6, 16) + ), + + TP_fast_assign( + struct ipv6_pinfo *np = inet6_sk(sk); + struct inet_sock *inet = inet_sk(sk); + struct in6_addr *pin6; + __be32 *p32; + + __entry->skbaddr = skb; + __entry->skaddr = sk; + + __entry->sport = ntohs(inet->inet_sport); + __entry->dport = ntohs(inet->inet_dport); + + p32 = (__be32 *) __entry->saddr; + *p32 = inet->inet_saddr; + + p32 = (__be32 *) __entry->daddr; + *p32 = inet->inet_daddr; + + if (np) { + pin6 = (struct in6_addr *)__entry->saddr_v6; + *pin6 = np->saddr; + pin6 = (struct in6_addr *)__entry->daddr_v6; + *pin6 = *(np->daddr_cache); + } else { + pin6 = (struct in6_addr *)__entry->saddr_v6; + ipv6_addr_set_v4mapped(inet->inet_saddr, pin6); + pin6 = (struct in6_addr *)__entry->daddr_v6; + ipv6_addr_set_v4mapped(inet->inet_daddr, pin6); + } + ), + + TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6 daddrv6=%pI6", + __entry->sport, __entry->dport, __entry->saddr, __entry->daddr, + __entry->saddr_v6, __entry->daddr_v6) +); + +#endif /* _TRACE_TCP_H */ + +/* This part must be outside protection */ +#include diff --git a/net/core/net-traces.c b/net/core/net-traces.c index 1132820c8e62..f4e4fa2db505 100644 --- a/net/core/net-traces.c +++ b/net/core/net-traces.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #if IS_ENABLED(CONFIG_IPV6) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 696b0a168f16..6c74f2a39778 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -42,6 +42,8 @@ #include #include +#include + /* People can turn this off for buggy TCP's found in printers etc. */ int sysctl_tcp_retrans_collapse __read_mostly = 1; @@ -2875,6 +2877,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs) if (likely(!err)) { TCP_SKB_CB(skb)->sacked |= TCPCB_EVER_RETRANS; + trace_tcp_retransmit_skb(sk, skb); } else if (err != -EBUSY) { NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPRETRANSFAIL); } -- cgit v1.2.3-59-g8ed1b From 53fd88ab61948f711147204c1c5017c7301979e9 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 14 Oct 2017 23:00:54 -0400 Subject: make vfs_ustat() static Signed-off-by: Al Viro --- fs/statfs.c | 2 +- include/linux/fs.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/fs/statfs.c b/fs/statfs.c index fab9b6a3c116..6327edf79e0f 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -216,7 +216,7 @@ SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user return error; } -int vfs_ustat(dev_t dev, struct kstatfs *sbuf) +static int vfs_ustat(dev_t dev, struct kstatfs *sbuf) { struct super_block *s = user_get_super(dev); int err; diff --git a/include/linux/fs.h b/include/linux/fs.h index 339e73742e73..89323e03e648 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2169,7 +2169,6 @@ extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *, extern int vfs_statfs(const struct path *, struct kstatfs *); extern int user_statfs(const char __user *, struct kstatfs *); extern int fd_statfs(int, struct kstatfs *); -extern int vfs_ustat(dev_t, struct kstatfs *); extern int freeze_super(struct super_block *super); extern int thaw_super(struct super_block *super); extern bool our_mnt(struct vfsmount *mnt); -- cgit v1.2.3-59-g8ed1b From f175f307dd0bd1ca3825d244f9b870ff12981d3c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sun, 15 Oct 2017 00:38:00 -0400 Subject: stubs for mount_bdev() and kill_block_super() in !CONFIG_BLOCK case Signed-off-by: Al Viro --- include/linux/fs.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index 89323e03e648..31f8b2ea358c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2094,9 +2094,18 @@ struct file_system_type { extern struct dentry *mount_ns(struct file_system_type *fs_type, int flags, void *data, void *ns, struct user_namespace *user_ns, int (*fill_super)(struct super_block *, void *, int)); +#ifdef CONFIG_BLOCK extern struct dentry *mount_bdev(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, int (*fill_super)(struct super_block *, void *, int)); +#else +static inline struct dentry *mount_bdev(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data, + int (*fill_super)(struct super_block *, void *, int)) +{ + return ERR_PTR(-ENODEV); +} +#endif extern struct dentry *mount_single(struct file_system_type *fs_type, int flags, void *data, int (*fill_super)(struct super_block *, void *, int)); @@ -2105,7 +2114,14 @@ extern struct dentry *mount_nodev(struct file_system_type *fs_type, int (*fill_super)(struct super_block *, void *, int)); extern struct dentry *mount_subtree(struct vfsmount *mnt, const char *path); void generic_shutdown_super(struct super_block *sb); +#ifdef CONFIG_BLOCK void kill_block_super(struct super_block *sb); +#else +static inline void kill_block_super(struct super_block *sb) +{ + BUG(); +} +#endif void kill_anon_super(struct super_block *sb); void kill_litter_super(struct super_block *sb); void deactivate_super(struct super_block *sb); -- cgit v1.2.3-59-g8ed1b From fd4f6f2a78aeaebb7094c1bb9b30623d18a86e4c Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Thu, 12 Oct 2017 02:16:11 -0400 Subject: cramfs: implement uncompressed and arbitrary data block positioning Two new capabilities are introduced here: - The ability to store some blocks uncompressed. - The ability to locate blocks anywhere. Those capabilities can be used independently, but the combination opens the possibility for execute-in-place (XIP) of program text segments that must remain uncompressed, and in the MMU case, must have a specific alignment. It is even possible to still have the writable data segments from the same file compressed as they have to be copied into RAM anyway. This is achieved by giving special meanings to some unused block pointer bits while remaining compatible with legacy cramfs images. Signed-off-by: Nicolas Pitre Tested-by: Chris Brandt Signed-off-by: Al Viro --- fs/cramfs/README | 31 ++++++++++++++- fs/cramfs/inode.c | 90 +++++++++++++++++++++++++++++++++--------- include/uapi/linux/cramfs_fs.h | 26 +++++++++++- 3 files changed, 126 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/fs/cramfs/README b/fs/cramfs/README index 9d4e7ea311f4..d71b27e0ff15 100644 --- a/fs/cramfs/README +++ b/fs/cramfs/README @@ -49,17 +49,46 @@ same as the start of the (i+1)'th if there is one). The first immediately follows the last for the file. s are each 32 bits long. +When the CRAMFS_FLAG_EXT_BLOCK_POINTERS capability bit is set, each +'s top bits may contain special flags as follows: + +CRAMFS_BLK_FLAG_UNCOMPRESSED (bit 31): + The block data is not compressed and should be copied verbatim. + +CRAMFS_BLK_FLAG_DIRECT_PTR (bit 30): + The stores the actual block start offset and not + its end, shifted right by 2 bits. The block must therefore be + aligned to a 4-byte boundary. The block size is either blksize + if CRAMFS_BLK_FLAG_UNCOMPRESSED is also specified, otherwise + the compressed data length is included in the first 2 bytes of + the block data. This is used to allow discontiguous data layout + and specific data block alignments e.g. for XIP applications. + + The order of 's is a depth-first descent of the directory tree, i.e. the same order as `find -size +0 \( -type f -o -type l \) -print'. : The i'th is the output of zlib's compress function -applied to the i'th blksize-sized chunk of the input data. +applied to the i'th blksize-sized chunk of the input data if the +corresponding CRAMFS_BLK_FLAG_UNCOMPRESSED bit is not set, +otherwise it is the input data directly. (For the last of the file, the input may of course be smaller.) Each may be a different size. (See above.) + s are merely byte-aligned, not generally u32-aligned. +When CRAMFS_BLK_FLAG_DIRECT_PTR is specified then the corresponding + may be located anywhere and not necessarily contiguous with +the previous/next blocks. In that case it is minimally u32-aligned. +If CRAMFS_BLK_FLAG_UNCOMPRESSED is also specified then the size is always +blksize except for the last block which is limited by the file length. +If CRAMFS_BLK_FLAG_DIRECT_PTR is set and CRAMFS_BLK_FLAG_UNCOMPRESSED +is not set then the first 2 bytes of the block contains the size of the +remaining block data as this cannot be determined from the placement of +logically adjacent blocks. + Holes ----- diff --git a/fs/cramfs/inode.c b/fs/cramfs/inode.c index bcdccb7a820b..19045453a8f3 100644 --- a/fs/cramfs/inode.c +++ b/fs/cramfs/inode.c @@ -618,34 +618,86 @@ static int cramfs_readpage(struct file *file, struct page *page) if (page->index < maxblock) { struct super_block *sb = inode->i_sb; - u32 blkptr_offset = OFFSET(inode) + page->index*4; - u32 start_offset, compr_len; + u32 blkptr_offset = OFFSET(inode) + page->index * 4; + u32 block_ptr, block_start, block_len; + bool uncompressed, direct; - start_offset = OFFSET(inode) + maxblock*4; mutex_lock(&read_mutex); - if (page->index) - start_offset = *(u32 *) cramfs_read(sb, blkptr_offset-4, - 4); - compr_len = (*(u32 *) cramfs_read(sb, blkptr_offset, 4) - - start_offset); - mutex_unlock(&read_mutex); + block_ptr = *(u32 *) cramfs_read(sb, blkptr_offset, 4); + uncompressed = (block_ptr & CRAMFS_BLK_FLAG_UNCOMPRESSED); + direct = (block_ptr & CRAMFS_BLK_FLAG_DIRECT_PTR); + block_ptr &= ~CRAMFS_BLK_FLAGS; + + if (direct) { + /* + * The block pointer is an absolute start pointer, + * shifted by 2 bits. The size is included in the + * first 2 bytes of the data block when compressed, + * or PAGE_SIZE otherwise. + */ + block_start = block_ptr << CRAMFS_BLK_DIRECT_PTR_SHIFT; + if (uncompressed) { + block_len = PAGE_SIZE; + /* if last block: cap to file length */ + if (page->index == maxblock - 1) + block_len = + offset_in_page(inode->i_size); + } else { + block_len = *(u16 *) + cramfs_read(sb, block_start, 2); + block_start += 2; + } + } else { + /* + * The block pointer indicates one past the end of + * the current block (start of next block). If this + * is the first block then it starts where the block + * pointer table ends, otherwise its start comes + * from the previous block's pointer. + */ + block_start = OFFSET(inode) + maxblock * 4; + if (page->index) + block_start = *(u32 *) + cramfs_read(sb, blkptr_offset - 4, 4); + /* Beware... previous ptr might be a direct ptr */ + if (unlikely(block_start & CRAMFS_BLK_FLAG_DIRECT_PTR)) { + /* See comments on earlier code. */ + u32 prev_start = block_start; + block_start = prev_start & ~CRAMFS_BLK_FLAGS; + block_start <<= CRAMFS_BLK_DIRECT_PTR_SHIFT; + if (prev_start & CRAMFS_BLK_FLAG_UNCOMPRESSED) { + block_start += PAGE_SIZE; + } else { + block_len = *(u16 *) + cramfs_read(sb, block_start, 2); + block_start += 2 + block_len; + } + } + block_start &= ~CRAMFS_BLK_FLAGS; + block_len = block_ptr - block_start; + } - if (compr_len == 0) + if (block_len == 0) ; /* hole */ - else if (unlikely(compr_len > (PAGE_SIZE << 1))) { - pr_err("bad compressed blocksize %u\n", - compr_len); + else if (unlikely(block_len > 2*PAGE_SIZE || + (uncompressed && block_len > PAGE_SIZE))) { + mutex_unlock(&read_mutex); + pr_err("bad data blocksize %u\n", block_len); goto err; + } else if (uncompressed) { + memcpy(pgdata, + cramfs_read(sb, block_start, block_len), + block_len); + bytes_filled = block_len; } else { - mutex_lock(&read_mutex); bytes_filled = cramfs_uncompress_block(pgdata, PAGE_SIZE, - cramfs_read(sb, start_offset, compr_len), - compr_len); - mutex_unlock(&read_mutex); - if (unlikely(bytes_filled < 0)) - goto err; + cramfs_read(sb, block_start, block_len), + block_len); } + mutex_unlock(&read_mutex); + if (unlikely(bytes_filled < 0)) + goto err; } memset(pgdata + bytes_filled, 0, PAGE_SIZE - bytes_filled); diff --git a/include/uapi/linux/cramfs_fs.h b/include/uapi/linux/cramfs_fs.h index e4611a9b9243..ce2c885133e2 100644 --- a/include/uapi/linux/cramfs_fs.h +++ b/include/uapi/linux/cramfs_fs.h @@ -73,6 +73,7 @@ struct cramfs_super { #define CRAMFS_FLAG_HOLES 0x00000100 /* support for holes */ #define CRAMFS_FLAG_WRONG_SIGNATURE 0x00000200 /* reserved */ #define CRAMFS_FLAG_SHIFTED_ROOT_OFFSET 0x00000400 /* shifted root fs */ +#define CRAMFS_FLAG_EXT_BLOCK_POINTERS 0x00000800 /* block pointer extensions */ /* * Valid values in super.flags. Currently we refuse to mount @@ -82,7 +83,30 @@ struct cramfs_super { #define CRAMFS_SUPPORTED_FLAGS ( 0x000000ff \ | CRAMFS_FLAG_HOLES \ | CRAMFS_FLAG_WRONG_SIGNATURE \ - | CRAMFS_FLAG_SHIFTED_ROOT_OFFSET ) + | CRAMFS_FLAG_SHIFTED_ROOT_OFFSET \ + | CRAMFS_FLAG_EXT_BLOCK_POINTERS ) +/* + * Block pointer flags + * + * The maximum block offset that needs to be represented is roughly: + * + * (1 << CRAMFS_OFFSET_WIDTH) * 4 + + * (1 << CRAMFS_SIZE_WIDTH) / PAGE_SIZE * (4 + PAGE_SIZE) + * = 0x11004000 + * + * That leaves room for 3 flag bits in the block pointer table. + */ +#define CRAMFS_BLK_FLAG_UNCOMPRESSED (1 << 31) +#define CRAMFS_BLK_FLAG_DIRECT_PTR (1 << 30) + +#define CRAMFS_BLK_FLAGS ( CRAMFS_BLK_FLAG_UNCOMPRESSED \ + | CRAMFS_BLK_FLAG_DIRECT_PTR ) + +/* + * Direct blocks are at least 4-byte aligned. + * Pointers to direct blocks are shifted down by 2 bits. + */ +#define CRAMFS_BLK_DIRECT_PTR_SHIFT 2 #endif /* _UAPI__CRAMFS_H */ -- cgit v1.2.3-59-g8ed1b From 8ca8ac1024841781f544427b7ca5684e4c5637a9 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 11 Oct 2017 11:25:12 +0200 Subject: clk: samsung: Add dt bindings for Exynos4412 ISP clock controller Some registers for the Exynos 4412 ISP (Camera subsystem) clocks are located in the ISP power domain. Because those registers are also located in a different memory region than the main clock controller, support for them can be provided by a separate clock controller. Signed-off-by: Marek Szyprowski Acked-by: Krzysztof Kozlowski Acked-by: Rob Herring Signed-off-by: Sylwester Nawrocki --- .../devicetree/bindings/clock/exynos4-clock.txt | 43 ++++++++++++++++++++++ include/dt-bindings/clock/exynos4.h | 35 ++++++++++++++++++ 2 files changed, 78 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/exynos4-clock.txt b/Documentation/devicetree/bindings/clock/exynos4-clock.txt index f5a5b19ed3b2..bc61c952cb0b 100644 --- a/Documentation/devicetree/bindings/clock/exynos4-clock.txt +++ b/Documentation/devicetree/bindings/clock/exynos4-clock.txt @@ -41,3 +41,46 @@ Example 2: UART controller node that consumes the clock generated by the clock clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>; clock-names = "uart", "clk_uart_baud0"; }; + +Exynos4412 SoC contains some additional clocks for FIMC-ISP (Camera ISP) +subsystem. Registers for those clocks are located in the ISP power domain. +Because those registers are also located in a different memory region than +the main clock controller, a separate clock controller has to be defined for +handling them. + +Required Properties: + +- compatible: should be "samsung,exynos4412-isp-clock". + +- reg: physical base address of the ISP clock controller and length of memory + mapped region. + +- #clock-cells: should be 1. + +- clocks: list of the clock controller input clock identifiers, + from common clock bindings, should point to CLK_ACLK200 and + CLK_ACLK400_MCUISP clocks from the main clock controller. + +- clock-names: list of the clock controller input clock names, + as described in clock-bindings.txt, should be "aclk200" and + "aclk400_mcuisp". + +- power-domains: a phandle to ISP power domain node as described by + generic PM domain bindings. + +Example 3: The clock controllers bindings for Exynos4412 SoCs. + + clock: clock-controller@10030000 { + compatible = "samsung,exynos4412-clock"; + reg = <0x10030000 0x18000>; + #clock-cells = <1>; + }; + + isp_clock: clock-controller@10048000 { + compatible = "samsung,exynos4412-isp-clock"; + reg = <0x10048000 0x1000>; + #clock-cells = <1>; + power-domains = <&pd_isp>; + clocks = <&clock CLK_ACLK200>, <&clock CLK_ACLK400_MCUISP>; + clock-names = "aclk200", "aclk400_mcuisp"; + }; diff --git a/include/dt-bindings/clock/exynos4.h b/include/dt-bindings/clock/exynos4.h index c40111f36d5e..e9f9d400c322 100644 --- a/include/dt-bindings/clock/exynos4.h +++ b/include/dt-bindings/clock/exynos4.h @@ -272,4 +272,39 @@ /* must be greater than maximal clock id */ #define CLK_NR_CLKS 461 +/* Exynos4x12 ISP clocks */ +#define CLK_ISP_FIMC_ISP 1 +#define CLK_ISP_FIMC_DRC 2 +#define CLK_ISP_FIMC_FD 3 +#define CLK_ISP_FIMC_LITE0 4 +#define CLK_ISP_FIMC_LITE1 5 +#define CLK_ISP_MCUISP 6 +#define CLK_ISP_GICISP 7 +#define CLK_ISP_SMMU_ISP 8 +#define CLK_ISP_SMMU_DRC 9 +#define CLK_ISP_SMMU_FD 10 +#define CLK_ISP_SMMU_LITE0 11 +#define CLK_ISP_SMMU_LITE1 12 +#define CLK_ISP_PPMUISPMX 13 +#define CLK_ISP_PPMUISPX 14 +#define CLK_ISP_MCUCTL_ISP 15 +#define CLK_ISP_MPWM_ISP 16 +#define CLK_ISP_I2C0_ISP 17 +#define CLK_ISP_I2C1_ISP 18 +#define CLK_ISP_MTCADC_ISP 19 +#define CLK_ISP_PWM_ISP 20 +#define CLK_ISP_WDT_ISP 21 +#define CLK_ISP_UART_ISP 22 +#define CLK_ISP_ASYNCAXIM 23 +#define CLK_ISP_SMMU_ISPCX 24 +#define CLK_ISP_SPI0_ISP 25 +#define CLK_ISP_SPI1_ISP 26 + +#define CLK_ISP_DIV_ISP0 27 +#define CLK_ISP_DIV_ISP1 28 +#define CLK_ISP_DIV_MCUISP0 29 +#define CLK_ISP_DIV_MCUISP1 30 + +#define CLK_NR_ISP_CLKS 31 + #endif /* _DT_BINDINGS_CLOCK_EXYNOS_4_H */ -- cgit v1.2.3-59-g8ed1b From 44842cc8a89aa7742bc47737aa75da5910aa5f33 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 12 Oct 2017 11:35:04 +0200 Subject: dt-bindings: clk: r7s72100: Add missing I and G clocks Add the missing definitions for the I (CPU) and G (Image Processing) clocks, so these clocks can be referred to from device nodes in DT. Note that these clocks are already fully supported otherwise (DT bindings, Linux driver, r7s72100.dtsi), they were just omitted from the header file. Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- include/dt-bindings/clock/r7s72100-clock.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/dt-bindings/clock/r7s72100-clock.h b/include/dt-bindings/clock/r7s72100-clock.h index 7dd8bc0c3cd0..0dcb3e87d44c 100644 --- a/include/dt-bindings/clock/r7s72100-clock.h +++ b/include/dt-bindings/clock/r7s72100-clock.h @@ -11,6 +11,8 @@ #define __DT_BINDINGS_CLOCK_R7S72100_H__ #define R7S72100_CLK_PLL 0 +#define R7S72100_CLK_I 1 +#define R7S72100_CLK_G 2 /* MSTP2 */ #define R7S72100_CLK_CORESIGHT 0 -- cgit v1.2.3-59-g8ed1b From 162d58c26d65e1486d18436eb151148a7f9886e5 Mon Sep 17 00:00:00 2001 From: Alexandre Torgue Date: Mon, 16 Oct 2017 13:45:58 +0200 Subject: ARM: dts: stm32: change pinctrl bindings definition Initially each pin was declared in "include/dt-bindings/stm32-pinfunc.h" and each definition contained SOC names (ex: STM32F429_PA9_FUNC_USART1_TX). Since this approach was approved, the number of supported MCU has increased (STM32F429/STM32F469/STM32f746/STM32H743). To avoid to add a new file in "include/dt-bindings" each time a new STM32 SOC arrives I propose a new approach which consist to use a macro to define pin muxing in device tree. All STM32 will use the common macro to define pinmux. Furthermore, it will make STM32 maintenance and integration of new SOC easier . Signed-off-by: Alexandre TORGUE Reviewed-by: Vikas MANOCHA Reviewed-by: Benjamin Gaignard Acked-by: Linus Walleij Acked-by: Rob Herring --- .../bindings/pinctrl/st,stm32-pinctrl.txt | 22 +- arch/arm/boot/dts/stm32f4-pinctrl.dtsi | 176 +-- arch/arm/boot/dts/stm32f746.dtsi | 16 +- arch/arm/boot/dts/stm32h743-pinctrl.dtsi | 10 +- include/dt-bindings/pinctrl/stm32-pinfunc.h | 30 + include/dt-bindings/pinctrl/stm32f429-pinfunc.h | 1239 --------------- include/dt-bindings/pinctrl/stm32f746-pinfunc.h | 1324 ---------------- include/dt-bindings/pinctrl/stm32h7-pinfunc.h | 1612 -------------------- 8 files changed, 151 insertions(+), 4278 deletions(-) create mode 100644 include/dt-bindings/pinctrl/stm32-pinfunc.h delete mode 100644 include/dt-bindings/pinctrl/stm32f429-pinfunc.h delete mode 100644 include/dt-bindings/pinctrl/stm32f746-pinfunc.h delete mode 100644 include/dt-bindings/pinctrl/stm32h7-pinfunc.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt index 33e3d3c47552..58c2a4c229db 100644 --- a/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt +++ b/Documentation/devicetree/bindings/pinctrl/st,stm32-pinctrl.txt @@ -143,6 +143,24 @@ Required properties: * 16 : Alternate Function 15 * 17 : Analog + To simplify the usage, macro is available to generate "pinmux" field. + This macro is available here: + - include/dt-bindings/pinctrl/stm32-pinfunc.h + + Some examples of using macro: + /* GPIO A9 set as alernate function 2 */ + ... { + pinmux = ; + }; + /* GPIO A9 set as GPIO */ + ... { + pinmux = ; + }; + /* GPIO A9 set as analog */ + ... { + pinmux = ; + }; + Optional properties: - GENERIC_PINCONFIG: is the generic pinconfig options to use. Available options are: @@ -165,13 +183,13 @@ pin-controller { ... usart1_pins_a: usart1@0 { pins1 { - pinmux = ; + pinmux = ; bias-disable; drive-push-pull; slew-rate = <0>; }; pins2 { - pinmux = ; + pinmux = ; bias-disable; }; }; diff --git a/arch/arm/boot/dts/stm32f4-pinctrl.dtsi b/arch/arm/boot/dts/stm32f4-pinctrl.dtsi index 7f3560c0211d..ae94d86c53c4 100644 --- a/arch/arm/boot/dts/stm32f4-pinctrl.dtsi +++ b/arch/arm/boot/dts/stm32f4-pinctrl.dtsi @@ -40,7 +40,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include +#include #include / { @@ -165,35 +165,35 @@ usart1_pins_a: usart1@0 { pins1 { - pinmux = ; + pinmux = ; /* USART1_TX */ bias-disable; drive-push-pull; slew-rate = <0>; }; pins2 { - pinmux = ; + pinmux = ; /* USART1_RX */ bias-disable; }; }; usart3_pins_a: usart3@0 { pins1 { - pinmux = ; + pinmux = ; /* USART3_TX */ bias-disable; drive-push-pull; slew-rate = <0>; }; pins2 { - pinmux = ; + pinmux = ; /* USART3_RX */ bias-disable; }; }; usbotg_fs_pins_a: usbotg_fs@0 { pins { - pinmux = , - , - ; + pinmux = , /* OTG_FS_ID */ + , /* OTG_FS_DM */ + ; /* OTG_FS_DP */ bias-disable; drive-push-pull; slew-rate = <2>; @@ -202,9 +202,9 @@ usbotg_fs_pins_b: usbotg_fs@1 { pins { - pinmux = , - , - ; + pinmux = , /* OTG_HS_ID */ + , /* OTG_HS_DM */ + ; /* OTG_HS_DP */ bias-disable; drive-push-pull; slew-rate = <2>; @@ -213,18 +213,18 @@ usbotg_hs_pins_a: usbotg_hs@0 { pins { - pinmux = , - , - , - , - , - , - , - , - , - , - , - ; + pinmux = , /* OTG_HS_ULPI_NXT*/ + , /* OTG_HS_ULPI_DIR */ + , /* OTG_HS_ULPI_STP */ + , /* OTG_HS_ULPI_CK */ + , /* OTG_HS_ULPI_D0 */ + , /* OTG_HS_ULPI_D1 */ + , /* OTG_HS_ULPI_D2 */ + , /* OTG_HS_ULPI_D3 */ + , /* OTG_HS_ULPI_D4 */ + , /* OTG_HS_ULPI_D5 */ + , /* OTG_HS_ULPI_D6 */ + ; /* OTG_HS_ULPI_D7 */ bias-disable; drive-push-pull; slew-rate = <2>; @@ -233,49 +233,49 @@ ethernet_mii: mii@0 { pins { - pinmux = , - , - , - , - , - , - , - , - , - , - , - , - , - ; + pinmux = , /* ETH_MII_TXD0_ETH_RMII_TXD0 */ + , /* ETH_MII_TXD1_ETH_RMII_TXD1 */ + , /* ETH_MII_TXD2 */ + , /* ETH_MII_TXD3 */ + , /* ETH_MII_TX_CLK */ + , /* ETH_MII_TX_EN_ETH_RMII_TX_EN */ + , /* ETH_MDIO */ + , /* ETH_MDC */ + , /* ETH_MII_RX_CLK_ETH_RMII_REF_CLK */ + , /* ETH_MII_RX_DV_ETH_RMII_CRS_DV */ + , /* ETH_MII_RXD0_ETH_RMII_RXD0 */ + , /* ETH_MII_RXD1_ETH_RMII_RXD1 */ + , /* ETH_MII_RXD2 */ + ; /* ETH_MII_RXD3 */ slew-rate = <2>; }; }; adc3_in8_pin: adc@200 { pins { - pinmux = ; + pinmux = ; }; }; pwm1_pins: pwm@1 { pins { - pinmux = , - , - ; + pinmux = , /* TIM1_CH1 */ + , /* TIM1_CH1N */ + ; /* TIM1_BKIN */ }; }; pwm3_pins: pwm@3 { pins { - pinmux = , - ; + pinmux = , /* TIM3_CH1 */ + ; /* TIM3_CH2 */ }; }; i2c1_pins: i2c1@0 { pins { - pinmux = , - ; + pinmux = , /* I2C1_SDA */ + ; /* I2C1_SCL */ bias-disable; drive-open-drain; slew-rate = <3>; @@ -284,55 +284,55 @@ ltdc_pins: ltdc@0 { pins { - pinmux = , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - ; + pinmux = , /* LCD_HSYNC */ + , /* LCD_VSYNC */ + , /* LCD_CLK */ + , /* LCD_R0 */ + , /* LCD_R1 */ + , /* LCD_R2 */ + , /* LCD_R3 */ + , /* LCD_R4 */ + , /* LCD_R5 */ + , /* LCD_R6*/ + , /* LCD_R7 */ + , /* LCD_G0 */ + , /* LCD_G1 */ + , /* LCD_G2 */ + , /* LCD_G3 */ + , /* LCD_G4 */ + , /* LCD_B0 */ + , /* LCD_B1 */ + , /* LCD_B2 */ + , /* LCD_B3*/ + , /* LCD_G5 */ + , /* LCD_G6 */ + , /* LCD_G7 */ + , /* LCD_B4 */ + , /* LCD_B5 */ + , /* LCD_B6 */ + , /* LCD_B7 */ + ; /* LCD_DE */ slew-rate = <2>; }; }; dcmi_pins: dcmi@0 { pins { - pinmux = , - , - , - , - , - , - , - , - , - , - , - , - , - , - ; + pinmux = , /* DCMI_HSYNC */ + , /* DCMI_VSYNC */ + , /* DCMI_PIXCLK */ + , /* DCMI_D0 */ + , /* DCMI_D1 */ + , /* DCMI_D2 */ + , /* DCMI_D3 */ + , /*DCMI_D4 */ + , /* DCMI_D5 */ + , /* DCMI_D6 */ + , /* DCMI_D7 */ + , /* DCMI_D8 */ + , /* DCMI_D9 */ + , /* DCMI_D10 */ + ; /* DCMI_D11 */ bias-disable; drive-push-pull; slew-rate = <3>; diff --git a/arch/arm/boot/dts/stm32f746.dtsi b/arch/arm/boot/dts/stm32f746.dtsi index 73bf8c1529a1..faaeca803943 100644 --- a/arch/arm/boot/dts/stm32f746.dtsi +++ b/arch/arm/boot/dts/stm32f746.dtsi @@ -42,7 +42,7 @@ #include "skeleton.dtsi" #include "armv7-m.dtsi" -#include +#include #include #include @@ -619,7 +619,7 @@ cec_pins_a: cec@0 { pins { - pinmux = ; + pinmux = ; /* HDMI CEC */ slew-rate = <0>; drive-open-drain; bias-disable; @@ -628,34 +628,34 @@ usart1_pins_a: usart1@0 { pins1 { - pinmux = ; + pinmux = ; /* USART1_TX */ bias-disable; drive-push-pull; slew-rate = <0>; }; pins2 { - pinmux = ; + pinmux = ; /* USART1_RX */ bias-disable; }; }; usart1_pins_b: usart1@1 { pins1 { - pinmux = ; + pinmux = ; /* USART1_TX */ bias-disable; drive-push-pull; slew-rate = <0>; }; pins2 { - pinmux = ; + pinmux = ; /* USART1_RX */ bias-disable; }; }; i2c1_pins_b: i2c1@0 { pins { - pinmux = , - ; + pinmux = , /* I2C1 SDA */ + ; /* I2C1 SCL */ bias-disable; drive-open-drain; slew-rate = <0>; diff --git a/arch/arm/boot/dts/stm32h743-pinctrl.dtsi b/arch/arm/boot/dts/stm32h743-pinctrl.dtsi index d438818155b2..65c1cd043987 100644 --- a/arch/arm/boot/dts/stm32h743-pinctrl.dtsi +++ b/arch/arm/boot/dts/stm32h743-pinctrl.dtsi @@ -40,7 +40,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -#include +#include / { soc { @@ -141,26 +141,26 @@ usart1_pins: usart1@0 { pins1 { - pinmux = ; + pinmux = ; /* USART1_TX */ bias-disable; drive-push-pull; slew-rate = <0>; }; pins2 { - pinmux = ; + pinmux = ; /* USART1_RX */ bias-disable; }; }; usart2_pins: usart2@0 { pins1 { - pinmux = ; + pinmux = ; /* USART2_TX */ bias-disable; drive-push-pull; slew-rate = <0>; }; pins2 { - pinmux = ; + pinmux = ; /* USART2_RX */ bias-disable; }; }; diff --git a/include/dt-bindings/pinctrl/stm32-pinfunc.h b/include/dt-bindings/pinctrl/stm32-pinfunc.h new file mode 100644 index 000000000000..b8dfe31821e6 --- /dev/null +++ b/include/dt-bindings/pinctrl/stm32-pinfunc.h @@ -0,0 +1,30 @@ +#ifndef _DT_BINDINGS_STM32_PINFUNC_H +#define _DT_BINDINGS_STM32_PINFUNC_H + +/* define PIN modes */ +#define GPIO 0x0 +#define AF0 0x1 +#define AF1 0x2 +#define AF2 0x3 +#define AF3 0x4 +#define AF4 0x5 +#define AF5 0x6 +#define AF6 0x7 +#define AF7 0x8 +#define AF8 0x9 +#define AF9 0xa +#define AF10 0xb +#define AF11 0xc +#define AF12 0xd +#define AF13 0xe +#define AF14 0xf +#define AF15 0x10 +#define ANALOG 0x11 + +/* define Pins number*/ +#define PIN_NO(port, line) (((port) - 'A') * 0x10 + (line)) + +#define STM32_PINMUX(port, line, mode) (((PIN_NO(port, line)) << 8) | (mode)) + +#endif /* _DT_BINDINGS_STM32_PINFUNC_H */ + diff --git a/include/dt-bindings/pinctrl/stm32f429-pinfunc.h b/include/dt-bindings/pinctrl/stm32f429-pinfunc.h deleted file mode 100644 index 26f18798d949..000000000000 --- a/include/dt-bindings/pinctrl/stm32f429-pinfunc.h +++ /dev/null @@ -1,1239 +0,0 @@ -#ifndef _DT_BINDINGS_STM32F429_PINFUNC_H -#define _DT_BINDINGS_STM32F429_PINFUNC_H - -#define STM32F429_PA0_FUNC_GPIO 0x0 -#define STM32F429_PA0_FUNC_TIM2_CH1_TIM2_ETR 0x2 -#define STM32F429_PA0_FUNC_TIM5_CH1 0x3 -#define STM32F429_PA0_FUNC_TIM8_ETR 0x4 -#define STM32F429_PA0_FUNC_USART2_CTS 0x8 -#define STM32F429_PA0_FUNC_UART4_TX 0x9 -#define STM32F429_PA0_FUNC_ETH_MII_CRS 0xc -#define STM32F429_PA0_FUNC_EVENTOUT 0x10 -#define STM32F429_PA0_FUNC_ANALOG 0x11 - -#define STM32F429_PA1_FUNC_GPIO 0x100 -#define STM32F429_PA1_FUNC_TIM2_CH2 0x102 -#define STM32F429_PA1_FUNC_TIM5_CH2 0x103 -#define STM32F429_PA1_FUNC_USART2_RTS 0x108 -#define STM32F429_PA1_FUNC_UART4_RX 0x109 -#define STM32F429_PA1_FUNC_ETH_MII_RX_CLK_ETH_RMII_REF_CLK 0x10c -#define STM32F429_PA1_FUNC_EVENTOUT 0x110 -#define STM32F429_PA1_FUNC_ANALOG 0x111 - -#define STM32F429_PA2_FUNC_GPIO 0x200 -#define STM32F429_PA2_FUNC_TIM2_CH3 0x202 -#define STM32F429_PA2_FUNC_TIM5_CH3 0x203 -#define STM32F429_PA2_FUNC_TIM9_CH1 0x204 -#define STM32F429_PA2_FUNC_USART2_TX 0x208 -#define STM32F429_PA2_FUNC_ETH_MDIO 0x20c -#define STM32F429_PA2_FUNC_EVENTOUT 0x210 -#define STM32F429_PA2_FUNC_ANALOG 0x211 - -#define STM32F429_PA3_FUNC_GPIO 0x300 -#define STM32F429_PA3_FUNC_TIM2_CH4 0x302 -#define STM32F429_PA3_FUNC_TIM5_CH4 0x303 -#define STM32F429_PA3_FUNC_TIM9_CH2 0x304 -#define STM32F429_PA3_FUNC_USART2_RX 0x308 -#define STM32F429_PA3_FUNC_OTG_HS_ULPI_D0 0x30b -#define STM32F429_PA3_FUNC_ETH_MII_COL 0x30c -#define STM32F429_PA3_FUNC_LCD_B5 0x30f -#define STM32F429_PA3_FUNC_EVENTOUT 0x310 -#define STM32F429_PA3_FUNC_ANALOG 0x311 - -#define STM32F429_PA4_FUNC_GPIO 0x400 -#define STM32F429_PA4_FUNC_SPI1_NSS 0x406 -#define STM32F429_PA4_FUNC_SPI3_NSS_I2S3_WS 0x407 -#define STM32F429_PA4_FUNC_USART2_CK 0x408 -#define STM32F429_PA4_FUNC_OTG_HS_SOF 0x40d -#define STM32F429_PA4_FUNC_DCMI_HSYNC 0x40e -#define STM32F429_PA4_FUNC_LCD_VSYNC 0x40f -#define STM32F429_PA4_FUNC_EVENTOUT 0x410 -#define STM32F429_PA4_FUNC_ANALOG 0x411 - -#define STM32F429_PA5_FUNC_GPIO 0x500 -#define STM32F429_PA5_FUNC_TIM2_CH1_TIM2_ETR 0x502 -#define STM32F429_PA5_FUNC_TIM8_CH1N 0x504 -#define STM32F429_PA5_FUNC_SPI1_SCK 0x506 -#define STM32F429_PA5_FUNC_OTG_HS_ULPI_CK 0x50b -#define STM32F429_PA5_FUNC_EVENTOUT 0x510 -#define STM32F429_PA5_FUNC_ANALOG 0x511 - -#define STM32F429_PA6_FUNC_GPIO 0x600 -#define STM32F429_PA6_FUNC_TIM1_BKIN 0x602 -#define STM32F429_PA6_FUNC_TIM3_CH1 0x603 -#define STM32F429_PA6_FUNC_TIM8_BKIN 0x604 -#define STM32F429_PA6_FUNC_SPI1_MISO 0x606 -#define STM32F429_PA6_FUNC_TIM13_CH1 0x60a -#define STM32F429_PA6_FUNC_DCMI_PIXCLK 0x60e -#define STM32F429_PA6_FUNC_LCD_G2 0x60f -#define STM32F429_PA6_FUNC_EVENTOUT 0x610 -#define STM32F429_PA6_FUNC_ANALOG 0x611 - -#define STM32F429_PA7_FUNC_GPIO 0x700 -#define STM32F429_PA7_FUNC_TIM1_CH1N 0x702 -#define STM32F429_PA7_FUNC_TIM3_CH2 0x703 -#define STM32F429_PA7_FUNC_TIM8_CH1N 0x704 -#define STM32F429_PA7_FUNC_SPI1_MOSI 0x706 -#define STM32F429_PA7_FUNC_TIM14_CH1 0x70a -#define STM32F429_PA7_FUNC_ETH_MII_RX_DV_ETH_RMII_CRS_DV 0x70c -#define STM32F429_PA7_FUNC_EVENTOUT 0x710 -#define STM32F429_PA7_FUNC_ANALOG 0x711 - -#define STM32F429_PA8_FUNC_GPIO 0x800 -#define STM32F429_PA8_FUNC_MCO1 0x801 -#define STM32F429_PA8_FUNC_TIM1_CH1 0x802 -#define STM32F429_PA8_FUNC_I2C3_SCL 0x805 -#define STM32F429_PA8_FUNC_USART1_CK 0x808 -#define STM32F429_PA8_FUNC_OTG_FS_SOF 0x80b -#define STM32F429_PA8_FUNC_LCD_R6 0x80f -#define STM32F429_PA8_FUNC_EVENTOUT 0x810 -#define STM32F429_PA8_FUNC_ANALOG 0x811 - -#define STM32F429_PA9_FUNC_GPIO 0x900 -#define STM32F429_PA9_FUNC_TIM1_CH2 0x902 -#define STM32F429_PA9_FUNC_I2C3_SMBA 0x905 -#define STM32F429_PA9_FUNC_USART1_TX 0x908 -#define STM32F429_PA9_FUNC_DCMI_D0 0x90e -#define STM32F429_PA9_FUNC_EVENTOUT 0x910 -#define STM32F429_PA9_FUNC_ANALOG 0x911 - -#define STM32F429_PA10_FUNC_GPIO 0xa00 -#define STM32F429_PA10_FUNC_TIM1_CH3 0xa02 -#define STM32F429_PA10_FUNC_USART1_RX 0xa08 -#define STM32F429_PA10_FUNC_OTG_FS_ID 0xa0b -#define STM32F429_PA10_FUNC_DCMI_D1 0xa0e -#define STM32F429_PA10_FUNC_EVENTOUT 0xa10 -#define STM32F429_PA10_FUNC_ANALOG 0xa11 - -#define STM32F429_PA11_FUNC_GPIO 0xb00 -#define STM32F429_PA11_FUNC_TIM1_CH4 0xb02 -#define STM32F429_PA11_FUNC_USART1_CTS 0xb08 -#define STM32F429_PA11_FUNC_CAN1_RX 0xb0a -#define STM32F429_PA11_FUNC_OTG_FS_DM 0xb0b -#define STM32F429_PA11_FUNC_LCD_R4 0xb0f -#define STM32F429_PA11_FUNC_EVENTOUT 0xb10 -#define STM32F429_PA11_FUNC_ANALOG 0xb11 - -#define STM32F429_PA12_FUNC_GPIO 0xc00 -#define STM32F429_PA12_FUNC_TIM1_ETR 0xc02 -#define STM32F429_PA12_FUNC_USART1_RTS 0xc08 -#define STM32F429_PA12_FUNC_CAN1_TX 0xc0a -#define STM32F429_PA12_FUNC_OTG_FS_DP 0xc0b -#define STM32F429_PA12_FUNC_LCD_R5 0xc0f -#define STM32F429_PA12_FUNC_EVENTOUT 0xc10 -#define STM32F429_PA12_FUNC_ANALOG 0xc11 - -#define STM32F429_PA13_FUNC_GPIO 0xd00 -#define STM32F429_PA13_FUNC_JTMS_SWDIO 0xd01 -#define STM32F429_PA13_FUNC_EVENTOUT 0xd10 -#define STM32F429_PA13_FUNC_ANALOG 0xd11 - -#define STM32F429_PA14_FUNC_GPIO 0xe00 -#define STM32F429_PA14_FUNC_JTCK_SWCLK 0xe01 -#define STM32F429_PA14_FUNC_EVENTOUT 0xe10 -#define STM32F429_PA14_FUNC_ANALOG 0xe11 - -#define STM32F429_PA15_FUNC_GPIO 0xf00 -#define STM32F429_PA15_FUNC_JTDI 0xf01 -#define STM32F429_PA15_FUNC_TIM2_CH1_TIM2_ETR 0xf02 -#define STM32F429_PA15_FUNC_SPI1_NSS 0xf06 -#define STM32F429_PA15_FUNC_SPI3_NSS_I2S3_WS 0xf07 -#define STM32F429_PA15_FUNC_EVENTOUT 0xf10 -#define STM32F429_PA15_FUNC_ANALOG 0xf11 - - - -#define STM32F429_PB0_FUNC_GPIO 0x1000 -#define STM32F429_PB0_FUNC_TIM1_CH2N 0x1002 -#define STM32F429_PB0_FUNC_TIM3_CH3 0x1003 -#define STM32F429_PB0_FUNC_TIM8_CH2N 0x1004 -#define STM32F429_PB0_FUNC_LCD_R3 0x100a -#define STM32F429_PB0_FUNC_OTG_HS_ULPI_D1 0x100b -#define STM32F429_PB0_FUNC_ETH_MII_RXD2 0x100c -#define STM32F429_PB0_FUNC_EVENTOUT 0x1010 -#define STM32F429_PB0_FUNC_ANALOG 0x1011 - -#define STM32F429_PB1_FUNC_GPIO 0x1100 -#define STM32F429_PB1_FUNC_TIM1_CH3N 0x1102 -#define STM32F429_PB1_FUNC_TIM3_CH4 0x1103 -#define STM32F429_PB1_FUNC_TIM8_CH3N 0x1104 -#define STM32F429_PB1_FUNC_LCD_R6 0x110a -#define STM32F429_PB1_FUNC_OTG_HS_ULPI_D2 0x110b -#define STM32F429_PB1_FUNC_ETH_MII_RXD3 0x110c -#define STM32F429_PB1_FUNC_EVENTOUT 0x1110 -#define STM32F429_PB1_FUNC_ANALOG 0x1111 - -#define STM32F429_PB2_FUNC_GPIO 0x1200 -#define STM32F429_PB2_FUNC_EVENTOUT 0x1210 -#define STM32F429_PB2_FUNC_ANALOG 0x1211 - -#define STM32F429_PB3_FUNC_GPIO 0x1300 -#define STM32F429_PB3_FUNC_JTDO_TRACESWO 0x1301 -#define STM32F429_PB3_FUNC_TIM2_CH2 0x1302 -#define STM32F429_PB3_FUNC_SPI1_SCK 0x1306 -#define STM32F429_PB3_FUNC_SPI3_SCK_I2S3_CK 0x1307 -#define STM32F429_PB3_FUNC_EVENTOUT 0x1310 -#define STM32F429_PB3_FUNC_ANALOG 0x1311 - -#define STM32F429_PB4_FUNC_GPIO 0x1400 -#define STM32F429_PB4_FUNC_NJTRST 0x1401 -#define STM32F429_PB4_FUNC_TIM3_CH1 0x1403 -#define STM32F429_PB4_FUNC_SPI1_MISO 0x1406 -#define STM32F429_PB4_FUNC_SPI3_MISO 0x1407 -#define STM32F429_PB4_FUNC_I2S3EXT_SD 0x1408 -#define STM32F429_PB4_FUNC_EVENTOUT 0x1410 -#define STM32F429_PB4_FUNC_ANALOG 0x1411 - -#define STM32F429_PB5_FUNC_GPIO 0x1500 -#define STM32F429_PB5_FUNC_TIM3_CH2 0x1503 -#define STM32F429_PB5_FUNC_I2C1_SMBA 0x1505 -#define STM32F429_PB5_FUNC_SPI1_MOSI 0x1506 -#define STM32F429_PB5_FUNC_SPI3_MOSI_I2S3_SD 0x1507 -#define STM32F429_PB5_FUNC_CAN2_RX 0x150a -#define STM32F429_PB5_FUNC_OTG_HS_ULPI_D7 0x150b -#define STM32F429_PB5_FUNC_ETH_PPS_OUT 0x150c -#define STM32F429_PB5_FUNC_FMC_SDCKE1 0x150d -#define STM32F429_PB5_FUNC_DCMI_D10 0x150e -#define STM32F429_PB5_FUNC_EVENTOUT 0x1510 -#define STM32F429_PB5_FUNC_ANALOG 0x1511 - -#define STM32F429_PB6_FUNC_GPIO 0x1600 -#define STM32F429_PB6_FUNC_TIM4_CH1 0x1603 -#define STM32F429_PB6_FUNC_I2C1_SCL 0x1605 -#define STM32F429_PB6_FUNC_USART1_TX 0x1608 -#define STM32F429_PB6_FUNC_CAN2_TX 0x160a -#define STM32F429_PB6_FUNC_FMC_SDNE1 0x160d -#define STM32F429_PB6_FUNC_DCMI_D5 0x160e -#define STM32F429_PB6_FUNC_EVENTOUT 0x1610 -#define STM32F429_PB6_FUNC_ANALOG 0x1611 - -#define STM32F429_PB7_FUNC_GPIO 0x1700 -#define STM32F429_PB7_FUNC_TIM4_CH2 0x1703 -#define STM32F429_PB7_FUNC_I2C1_SDA 0x1705 -#define STM32F429_PB7_FUNC_USART1_RX 0x1708 -#define STM32F429_PB7_FUNC_FMC_NL 0x170d -#define STM32F429_PB7_FUNC_DCMI_VSYNC 0x170e -#define STM32F429_PB7_FUNC_EVENTOUT 0x1710 -#define STM32F429_PB7_FUNC_ANALOG 0x1711 - -#define STM32F429_PB8_FUNC_GPIO 0x1800 -#define STM32F429_PB8_FUNC_TIM4_CH3 0x1803 -#define STM32F429_PB8_FUNC_TIM10_CH1 0x1804 -#define STM32F429_PB8_FUNC_I2C1_SCL 0x1805 -#define STM32F429_PB8_FUNC_CAN1_RX 0x180a -#define STM32F429_PB8_FUNC_ETH_MII_TXD3 0x180c -#define STM32F429_PB8_FUNC_SDIO_D4 0x180d -#define STM32F429_PB8_FUNC_DCMI_D6 0x180e -#define STM32F429_PB8_FUNC_LCD_B6 0x180f -#define STM32F429_PB8_FUNC_EVENTOUT 0x1810 -#define STM32F429_PB8_FUNC_ANALOG 0x1811 - -#define STM32F429_PB9_FUNC_GPIO 0x1900 -#define STM32F429_PB9_FUNC_TIM4_CH4 0x1903 -#define STM32F429_PB9_FUNC_TIM11_CH1 0x1904 -#define STM32F429_PB9_FUNC_I2C1_SDA 0x1905 -#define STM32F429_PB9_FUNC_SPI2_NSS_I2S2_WS 0x1906 -#define STM32F429_PB9_FUNC_CAN1_TX 0x190a -#define STM32F429_PB9_FUNC_SDIO_D5 0x190d -#define STM32F429_PB9_FUNC_DCMI_D7 0x190e -#define STM32F429_PB9_FUNC_LCD_B7 0x190f -#define STM32F429_PB9_FUNC_EVENTOUT 0x1910 -#define STM32F429_PB9_FUNC_ANALOG 0x1911 - -#define STM32F429_PB10_FUNC_GPIO 0x1a00 -#define STM32F429_PB10_FUNC_TIM2_CH3 0x1a02 -#define STM32F429_PB10_FUNC_I2C2_SCL 0x1a05 -#define STM32F429_PB10_FUNC_SPI2_SCK_I2S2_CK 0x1a06 -#define STM32F429_PB10_FUNC_USART3_TX 0x1a08 -#define STM32F429_PB10_FUNC_OTG_HS_ULPI_D3 0x1a0b -#define STM32F429_PB10_FUNC_ETH_MII_RX_ER 0x1a0c -#define STM32F429_PB10_FUNC_LCD_G4 0x1a0f -#define STM32F429_PB10_FUNC_EVENTOUT 0x1a10 -#define STM32F429_PB10_FUNC_ANALOG 0x1a11 - -#define STM32F429_PB11_FUNC_GPIO 0x1b00 -#define STM32F429_PB11_FUNC_TIM2_CH4 0x1b02 -#define STM32F429_PB11_FUNC_I2C2_SDA 0x1b05 -#define STM32F429_PB11_FUNC_USART3_RX 0x1b08 -#define STM32F429_PB11_FUNC_OTG_HS_ULPI_D4 0x1b0b -#define STM32F429_PB11_FUNC_ETH_MII_TX_EN_ETH_RMII_TX_EN 0x1b0c -#define STM32F429_PB11_FUNC_LCD_G5 0x1b0f -#define STM32F429_PB11_FUNC_EVENTOUT 0x1b10 -#define STM32F429_PB11_FUNC_ANALOG 0x1b11 - -#define STM32F429_PB12_FUNC_GPIO 0x1c00 -#define STM32F429_PB12_FUNC_TIM1_BKIN 0x1c02 -#define STM32F429_PB12_FUNC_I2C2_SMBA 0x1c05 -#define STM32F429_PB12_FUNC_SPI2_NSS_I2S2_WS 0x1c06 -#define STM32F429_PB12_FUNC_USART3_CK 0x1c08 -#define STM32F429_PB12_FUNC_CAN2_RX 0x1c0a -#define STM32F429_PB12_FUNC_OTG_HS_ULPI_D5 0x1c0b -#define STM32F429_PB12_FUNC_ETH_MII_TXD0_ETH_RMII_TXD0 0x1c0c -#define STM32F429_PB12_FUNC_OTG_HS_ID 0x1c0d -#define STM32F429_PB12_FUNC_EVENTOUT 0x1c10 -#define STM32F429_PB12_FUNC_ANALOG 0x1c11 - -#define STM32F429_PB13_FUNC_GPIO 0x1d00 -#define STM32F429_PB13_FUNC_TIM1_CH1N 0x1d02 -#define STM32F429_PB13_FUNC_SPI2_SCK_I2S2_CK 0x1d06 -#define STM32F429_PB13_FUNC_USART3_CTS 0x1d08 -#define STM32F429_PB13_FUNC_CAN2_TX 0x1d0a -#define STM32F429_PB13_FUNC_OTG_HS_ULPI_D6 0x1d0b -#define STM32F429_PB13_FUNC_ETH_MII_TXD1_ETH_RMII_TXD1 0x1d0c -#define STM32F429_PB13_FUNC_EVENTOUT 0x1d10 -#define STM32F429_PB13_FUNC_ANALOG 0x1d11 - -#define STM32F429_PB14_FUNC_GPIO 0x1e00 -#define STM32F429_PB14_FUNC_TIM1_CH2N 0x1e02 -#define STM32F429_PB14_FUNC_TIM8_CH2N 0x1e04 -#define STM32F429_PB14_FUNC_SPI2_MISO 0x1e06 -#define STM32F429_PB14_FUNC_I2S2EXT_SD 0x1e07 -#define STM32F429_PB14_FUNC_USART3_RTS 0x1e08 -#define STM32F429_PB14_FUNC_TIM12_CH1 0x1e0a -#define STM32F429_PB14_FUNC_OTG_HS_DM 0x1e0d -#define STM32F429_PB14_FUNC_EVENTOUT 0x1e10 -#define STM32F429_PB14_FUNC_ANALOG 0x1e11 - -#define STM32F429_PB15_FUNC_GPIO 0x1f00 -#define STM32F429_PB15_FUNC_RTC_REFIN 0x1f01 -#define STM32F429_PB15_FUNC_TIM1_CH3N 0x1f02 -#define STM32F429_PB15_FUNC_TIM8_CH3N 0x1f04 -#define STM32F429_PB15_FUNC_SPI2_MOSI_I2S2_SD 0x1f06 -#define STM32F429_PB15_FUNC_TIM12_CH2 0x1f0a -#define STM32F429_PB15_FUNC_OTG_HS_DP 0x1f0d -#define STM32F429_PB15_FUNC_EVENTOUT 0x1f10 -#define STM32F429_PB15_FUNC_ANALOG 0x1f11 - - - -#define STM32F429_PC0_FUNC_GPIO 0x2000 -#define STM32F429_PC0_FUNC_OTG_HS_ULPI_STP 0x200b -#define STM32F429_PC0_FUNC_FMC_SDNWE 0x200d -#define STM32F429_PC0_FUNC_EVENTOUT 0x2010 -#define STM32F429_PC0_FUNC_ANALOG 0x2011 - -#define STM32F429_PC1_FUNC_GPIO 0x2100 -#define STM32F429_PC1_FUNC_ETH_MDC 0x210c -#define STM32F429_PC1_FUNC_EVENTOUT 0x2110 -#define STM32F429_PC1_FUNC_ANALOG 0x2111 - -#define STM32F429_PC2_FUNC_GPIO 0x2200 -#define STM32F429_PC2_FUNC_SPI2_MISO 0x2206 -#define STM32F429_PC2_FUNC_I2S2EXT_SD 0x2207 -#define STM32F429_PC2_FUNC_OTG_HS_ULPI_DIR 0x220b -#define STM32F429_PC2_FUNC_ETH_MII_TXD2 0x220c -#define STM32F429_PC2_FUNC_FMC_SDNE0 0x220d -#define STM32F429_PC2_FUNC_EVENTOUT 0x2210 -#define STM32F429_PC2_FUNC_ANALOG 0x2211 - -#define STM32F429_PC3_FUNC_GPIO 0x2300 -#define STM32F429_PC3_FUNC_SPI2_MOSI_I2S2_SD 0x2306 -#define STM32F429_PC3_FUNC_OTG_HS_ULPI_NXT 0x230b -#define STM32F429_PC3_FUNC_ETH_MII_TX_CLK 0x230c -#define STM32F429_PC3_FUNC_FMC_SDCKE0 0x230d -#define STM32F429_PC3_FUNC_EVENTOUT 0x2310 -#define STM32F429_PC3_FUNC_ANALOG 0x2311 - -#define STM32F429_PC4_FUNC_GPIO 0x2400 -#define STM32F429_PC4_FUNC_ETH_MII_RXD0_ETH_RMII_RXD0 0x240c -#define STM32F429_PC4_FUNC_EVENTOUT 0x2410 -#define STM32F429_PC4_FUNC_ANALOG 0x2411 - -#define STM32F429_PC5_FUNC_GPIO 0x2500 -#define STM32F429_PC5_FUNC_ETH_MII_RXD1_ETH_RMII_RXD1 0x250c -#define STM32F429_PC5_FUNC_EVENTOUT 0x2510 -#define STM32F429_PC5_FUNC_ANALOG 0x2511 - -#define STM32F429_PC6_FUNC_GPIO 0x2600 -#define STM32F429_PC6_FUNC_TIM3_CH1 0x2603 -#define STM32F429_PC6_FUNC_TIM8_CH1 0x2604 -#define STM32F429_PC6_FUNC_I2S2_MCK 0x2606 -#define STM32F429_PC6_FUNC_USART6_TX 0x2609 -#define STM32F429_PC6_FUNC_SDIO_D6 0x260d -#define STM32F429_PC6_FUNC_DCMI_D0 0x260e -#define STM32F429_PC6_FUNC_LCD_HSYNC 0x260f -#define STM32F429_PC6_FUNC_EVENTOUT 0x2610 -#define STM32F429_PC6_FUNC_ANALOG 0x2611 - -#define STM32F429_PC7_FUNC_GPIO 0x2700 -#define STM32F429_PC7_FUNC_TIM3_CH2 0x2703 -#define STM32F429_PC7_FUNC_TIM8_CH2 0x2704 -#define STM32F429_PC7_FUNC_I2S3_MCK 0x2707 -#define STM32F429_PC7_FUNC_USART6_RX 0x2709 -#define STM32F429_PC7_FUNC_SDIO_D7 0x270d -#define STM32F429_PC7_FUNC_DCMI_D1 0x270e -#define STM32F429_PC7_FUNC_LCD_G6 0x270f -#define STM32F429_PC7_FUNC_EVENTOUT 0x2710 -#define STM32F429_PC7_FUNC_ANALOG 0x2711 - -#define STM32F429_PC8_FUNC_GPIO 0x2800 -#define STM32F429_PC8_FUNC_TIM3_CH3 0x2803 -#define STM32F429_PC8_FUNC_TIM8_CH3 0x2804 -#define STM32F429_PC8_FUNC_USART6_CK 0x2809 -#define STM32F429_PC8_FUNC_SDIO_D0 0x280d -#define STM32F429_PC8_FUNC_DCMI_D2 0x280e -#define STM32F429_PC8_FUNC_EVENTOUT 0x2810 -#define STM32F429_PC8_FUNC_ANALOG 0x2811 - -#define STM32F429_PC9_FUNC_GPIO 0x2900 -#define STM32F429_PC9_FUNC_MCO2 0x2901 -#define STM32F429_PC9_FUNC_TIM3_CH4 0x2903 -#define STM32F429_PC9_FUNC_TIM8_CH4 0x2904 -#define STM32F429_PC9_FUNC_I2C3_SDA 0x2905 -#define STM32F429_PC9_FUNC_I2S_CKIN 0x2906 -#define STM32F429_PC9_FUNC_SDIO_D1 0x290d -#define STM32F429_PC9_FUNC_DCMI_D3 0x290e -#define STM32F429_PC9_FUNC_EVENTOUT 0x2910 -#define STM32F429_PC9_FUNC_ANALOG 0x2911 - -#define STM32F429_PC10_FUNC_GPIO 0x2a00 -#define STM32F429_PC10_FUNC_SPI3_SCK_I2S3_CK 0x2a07 -#define STM32F429_PC10_FUNC_USART3_TX 0x2a08 -#define STM32F429_PC10_FUNC_UART4_TX 0x2a09 -#define STM32F429_PC10_FUNC_SDIO_D2 0x2a0d -#define STM32F429_PC10_FUNC_DCMI_D8 0x2a0e -#define STM32F429_PC10_FUNC_LCD_R2 0x2a0f -#define STM32F429_PC10_FUNC_EVENTOUT 0x2a10 -#define STM32F429_PC10_FUNC_ANALOG 0x2a11 - -#define STM32F429_PC11_FUNC_GPIO 0x2b00 -#define STM32F429_PC11_FUNC_I2S3EXT_SD 0x2b06 -#define STM32F429_PC11_FUNC_SPI3_MISO 0x2b07 -#define STM32F429_PC11_FUNC_USART3_RX 0x2b08 -#define STM32F429_PC11_FUNC_UART4_RX 0x2b09 -#define STM32F429_PC11_FUNC_SDIO_D3 0x2b0d -#define STM32F429_PC11_FUNC_DCMI_D4 0x2b0e -#define STM32F429_PC11_FUNC_EVENTOUT 0x2b10 -#define STM32F429_PC11_FUNC_ANALOG 0x2b11 - -#define STM32F429_PC12_FUNC_GPIO 0x2c00 -#define STM32F429_PC12_FUNC_SPI3_MOSI_I2S3_SD 0x2c07 -#define STM32F429_PC12_FUNC_USART3_CK 0x2c08 -#define STM32F429_PC12_FUNC_UART5_TX 0x2c09 -#define STM32F429_PC12_FUNC_SDIO_CK 0x2c0d -#define STM32F429_PC12_FUNC_DCMI_D9 0x2c0e -#define STM32F429_PC12_FUNC_EVENTOUT 0x2c10 -#define STM32F429_PC12_FUNC_ANALOG 0x2c11 - -#define STM32F429_PC13_FUNC_GPIO 0x2d00 -#define STM32F429_PC13_FUNC_EVENTOUT 0x2d10 -#define STM32F429_PC13_FUNC_ANALOG 0x2d11 - -#define STM32F429_PC14_FUNC_GPIO 0x2e00 -#define STM32F429_PC14_FUNC_EVENTOUT 0x2e10 -#define STM32F429_PC14_FUNC_ANALOG 0x2e11 - -#define STM32F429_PC15_FUNC_GPIO 0x2f00 -#define STM32F429_PC15_FUNC_EVENTOUT 0x2f10 -#define STM32F429_PC15_FUNC_ANALOG 0x2f11 - - - -#define STM32F429_PD0_FUNC_GPIO 0x3000 -#define STM32F429_PD0_FUNC_CAN1_RX 0x300a -#define STM32F429_PD0_FUNC_FMC_D2 0x300d -#define STM32F429_PD0_FUNC_EVENTOUT 0x3010 -#define STM32F429_PD0_FUNC_ANALOG 0x3011 - -#define STM32F429_PD1_FUNC_GPIO 0x3100 -#define STM32F429_PD1_FUNC_CAN1_TX 0x310a -#define STM32F429_PD1_FUNC_FMC_D3 0x310d -#define STM32F429_PD1_FUNC_EVENTOUT 0x3110 -#define STM32F429_PD1_FUNC_ANALOG 0x3111 - -#define STM32F429_PD2_FUNC_GPIO 0x3200 -#define STM32F429_PD2_FUNC_TIM3_ETR 0x3203 -#define STM32F429_PD2_FUNC_UART5_RX 0x3209 -#define STM32F429_PD2_FUNC_SDIO_CMD 0x320d -#define STM32F429_PD2_FUNC_DCMI_D11 0x320e -#define STM32F429_PD2_FUNC_EVENTOUT 0x3210 -#define STM32F429_PD2_FUNC_ANALOG 0x3211 - -#define STM32F429_PD3_FUNC_GPIO 0x3300 -#define STM32F429_PD3_FUNC_SPI2_SCK_I2S2_CK 0x3306 -#define STM32F429_PD3_FUNC_USART2_CTS 0x3308 -#define STM32F429_PD3_FUNC_FMC_CLK 0x330d -#define STM32F429_PD3_FUNC_DCMI_D5 0x330e -#define STM32F429_PD3_FUNC_LCD_G7 0x330f -#define STM32F429_PD3_FUNC_EVENTOUT 0x3310 -#define STM32F429_PD3_FUNC_ANALOG 0x3311 - -#define STM32F429_PD4_FUNC_GPIO 0x3400 -#define STM32F429_PD4_FUNC_USART2_RTS 0x3408 -#define STM32F429_PD4_FUNC_FMC_NOE 0x340d -#define STM32F429_PD4_FUNC_EVENTOUT 0x3410 -#define STM32F429_PD4_FUNC_ANALOG 0x3411 - -#define STM32F429_PD5_FUNC_GPIO 0x3500 -#define STM32F429_PD5_FUNC_USART2_TX 0x3508 -#define STM32F429_PD5_FUNC_FMC_NWE 0x350d -#define STM32F429_PD5_FUNC_EVENTOUT 0x3510 -#define STM32F429_PD5_FUNC_ANALOG 0x3511 - -#define STM32F429_PD6_FUNC_GPIO 0x3600 -#define STM32F429_PD6_FUNC_SPI3_MOSI_I2S3_SD 0x3606 -#define STM32F429_PD6_FUNC_SAI1_SD_A 0x3607 -#define STM32F429_PD6_FUNC_USART2_RX 0x3608 -#define STM32F429_PD6_FUNC_FMC_NWAIT 0x360d -#define STM32F429_PD6_FUNC_DCMI_D10 0x360e -#define STM32F429_PD6_FUNC_LCD_B2 0x360f -#define STM32F429_PD6_FUNC_EVENTOUT 0x3610 -#define STM32F429_PD6_FUNC_ANALOG 0x3611 - -#define STM32F429_PD7_FUNC_GPIO 0x3700 -#define STM32F429_PD7_FUNC_USART2_CK 0x3708 -#define STM32F429_PD7_FUNC_FMC_NE1_FMC_NCE2 0x370d -#define STM32F429_PD7_FUNC_EVENTOUT 0x3710 -#define STM32F429_PD7_FUNC_ANALOG 0x3711 - -#define STM32F429_PD8_FUNC_GPIO 0x3800 -#define STM32F429_PD8_FUNC_USART3_TX 0x3808 -#define STM32F429_PD8_FUNC_FMC_D13 0x380d -#define STM32F429_PD8_FUNC_EVENTOUT 0x3810 -#define STM32F429_PD8_FUNC_ANALOG 0x3811 - -#define STM32F429_PD9_FUNC_GPIO 0x3900 -#define STM32F429_PD9_FUNC_USART3_RX 0x3908 -#define STM32F429_PD9_FUNC_FMC_D14 0x390d -#define STM32F429_PD9_FUNC_EVENTOUT 0x3910 -#define STM32F429_PD9_FUNC_ANALOG 0x3911 - -#define STM32F429_PD10_FUNC_GPIO 0x3a00 -#define STM32F429_PD10_FUNC_USART3_CK 0x3a08 -#define STM32F429_PD10_FUNC_FMC_D15 0x3a0d -#define STM32F429_PD10_FUNC_LCD_B3 0x3a0f -#define STM32F429_PD10_FUNC_EVENTOUT 0x3a10 -#define STM32F429_PD10_FUNC_ANALOG 0x3a11 - -#define STM32F429_PD11_FUNC_GPIO 0x3b00 -#define STM32F429_PD11_FUNC_USART3_CTS 0x3b08 -#define STM32F429_PD11_FUNC_FMC_A16 0x3b0d -#define STM32F429_PD11_FUNC_EVENTOUT 0x3b10 -#define STM32F429_PD11_FUNC_ANALOG 0x3b11 - -#define STM32F429_PD12_FUNC_GPIO 0x3c00 -#define STM32F429_PD12_FUNC_TIM4_CH1 0x3c03 -#define STM32F429_PD12_FUNC_USART3_RTS 0x3c08 -#define STM32F429_PD12_FUNC_FMC_A17 0x3c0d -#define STM32F429_PD12_FUNC_EVENTOUT 0x3c10 -#define STM32F429_PD12_FUNC_ANALOG 0x3c11 - -#define STM32F429_PD13_FUNC_GPIO 0x3d00 -#define STM32F429_PD13_FUNC_TIM4_CH2 0x3d03 -#define STM32F429_PD13_FUNC_FMC_A18 0x3d0d -#define STM32F429_PD13_FUNC_EVENTOUT 0x3d10 -#define STM32F429_PD13_FUNC_ANALOG 0x3d11 - -#define STM32F429_PD14_FUNC_GPIO 0x3e00 -#define STM32F429_PD14_FUNC_TIM4_CH3 0x3e03 -#define STM32F429_PD14_FUNC_FMC_D0 0x3e0d -#define STM32F429_PD14_FUNC_EVENTOUT 0x3e10 -#define STM32F429_PD14_FUNC_ANALOG 0x3e11 - -#define STM32F429_PD15_FUNC_GPIO 0x3f00 -#define STM32F429_PD15_FUNC_TIM4_CH4 0x3f03 -#define STM32F429_PD15_FUNC_FMC_D1 0x3f0d -#define STM32F429_PD15_FUNC_EVENTOUT 0x3f10 -#define STM32F429_PD15_FUNC_ANALOG 0x3f11 - - - -#define STM32F429_PE0_FUNC_GPIO 0x4000 -#define STM32F429_PE0_FUNC_TIM4_ETR 0x4003 -#define STM32F429_PE0_FUNC_UART8_RX 0x4009 -#define STM32F429_PE0_FUNC_FMC_NBL0 0x400d -#define STM32F429_PE0_FUNC_DCMI_D2 0x400e -#define STM32F429_PE0_FUNC_EVENTOUT 0x4010 -#define STM32F429_PE0_FUNC_ANALOG 0x4011 - -#define STM32F429_PE1_FUNC_GPIO 0x4100 -#define STM32F429_PE1_FUNC_UART8_TX 0x4109 -#define STM32F429_PE1_FUNC_FMC_NBL1 0x410d -#define STM32F429_PE1_FUNC_DCMI_D3 0x410e -#define STM32F429_PE1_FUNC_EVENTOUT 0x4110 -#define STM32F429_PE1_FUNC_ANALOG 0x4111 - -#define STM32F429_PE2_FUNC_GPIO 0x4200 -#define STM32F429_PE2_FUNC_TRACECLK 0x4201 -#define STM32F429_PE2_FUNC_SPI4_SCK 0x4206 -#define STM32F429_PE2_FUNC_SAI1_MCLK_A 0x4207 -#define STM32F429_PE2_FUNC_ETH_MII_TXD3 0x420c -#define STM32F429_PE2_FUNC_FMC_A23 0x420d -#define STM32F429_PE2_FUNC_EVENTOUT 0x4210 -#define STM32F429_PE2_FUNC_ANALOG 0x4211 - -#define STM32F429_PE3_FUNC_GPIO 0x4300 -#define STM32F429_PE3_FUNC_TRACED0 0x4301 -#define STM32F429_PE3_FUNC_SAI1_SD_B 0x4307 -#define STM32F429_PE3_FUNC_FMC_A19 0x430d -#define STM32F429_PE3_FUNC_EVENTOUT 0x4310 -#define STM32F429_PE3_FUNC_ANALOG 0x4311 - -#define STM32F429_PE4_FUNC_GPIO 0x4400 -#define STM32F429_PE4_FUNC_TRACED1 0x4401 -#define STM32F429_PE4_FUNC_SPI4_NSS 0x4406 -#define STM32F429_PE4_FUNC_SAI1_FS_A 0x4407 -#define STM32F429_PE4_FUNC_FMC_A20 0x440d -#define STM32F429_PE4_FUNC_DCMI_D4 0x440e -#define STM32F429_PE4_FUNC_LCD_B0 0x440f -#define STM32F429_PE4_FUNC_EVENTOUT 0x4410 -#define STM32F429_PE4_FUNC_ANALOG 0x4411 - -#define STM32F429_PE5_FUNC_GPIO 0x4500 -#define STM32F429_PE5_FUNC_TRACED2 0x4501 -#define STM32F429_PE5_FUNC_TIM9_CH1 0x4504 -#define STM32F429_PE5_FUNC_SPI4_MISO 0x4506 -#define STM32F429_PE5_FUNC_SAI1_SCK_A 0x4507 -#define STM32F429_PE5_FUNC_FMC_A21 0x450d -#define STM32F429_PE5_FUNC_DCMI_D6 0x450e -#define STM32F429_PE5_FUNC_LCD_G0 0x450f -#define STM32F429_PE5_FUNC_EVENTOUT 0x4510 -#define STM32F429_PE5_FUNC_ANALOG 0x4511 - -#define STM32F429_PE6_FUNC_GPIO 0x4600 -#define STM32F429_PE6_FUNC_TRACED3 0x4601 -#define STM32F429_PE6_FUNC_TIM9_CH2 0x4604 -#define STM32F429_PE6_FUNC_SPI4_MOSI 0x4606 -#define STM32F429_PE6_FUNC_SAI1_SD_A 0x4607 -#define STM32F429_PE6_FUNC_FMC_A22 0x460d -#define STM32F429_PE6_FUNC_DCMI_D7 0x460e -#define STM32F429_PE6_FUNC_LCD_G1 0x460f -#define STM32F429_PE6_FUNC_EVENTOUT 0x4610 -#define STM32F429_PE6_FUNC_ANALOG 0x4611 - -#define STM32F429_PE7_FUNC_GPIO 0x4700 -#define STM32F429_PE7_FUNC_TIM1_ETR 0x4702 -#define STM32F429_PE7_FUNC_UART7_RX 0x4709 -#define STM32F429_PE7_FUNC_FMC_D4 0x470d -#define STM32F429_PE7_FUNC_EVENTOUT 0x4710 -#define STM32F429_PE7_FUNC_ANALOG 0x4711 - -#define STM32F429_PE8_FUNC_GPIO 0x4800 -#define STM32F429_PE8_FUNC_TIM1_CH1N 0x4802 -#define STM32F429_PE8_FUNC_UART7_TX 0x4809 -#define STM32F429_PE8_FUNC_FMC_D5 0x480d -#define STM32F429_PE8_FUNC_EVENTOUT 0x4810 -#define STM32F429_PE8_FUNC_ANALOG 0x4811 - -#define STM32F429_PE9_FUNC_GPIO 0x4900 -#define STM32F429_PE9_FUNC_TIM1_CH1 0x4902 -#define STM32F429_PE9_FUNC_FMC_D6 0x490d -#define STM32F429_PE9_FUNC_EVENTOUT 0x4910 -#define STM32F429_PE9_FUNC_ANALOG 0x4911 - -#define STM32F429_PE10_FUNC_GPIO 0x4a00 -#define STM32F429_PE10_FUNC_TIM1_CH2N 0x4a02 -#define STM32F429_PE10_FUNC_FMC_D7 0x4a0d -#define STM32F429_PE10_FUNC_EVENTOUT 0x4a10 -#define STM32F429_PE10_FUNC_ANALOG 0x4a11 - -#define STM32F429_PE11_FUNC_GPIO 0x4b00 -#define STM32F429_PE11_FUNC_TIM1_CH2 0x4b02 -#define STM32F429_PE11_FUNC_SPI4_NSS 0x4b06 -#define STM32F429_PE11_FUNC_FMC_D8 0x4b0d -#define STM32F429_PE11_FUNC_LCD_G3 0x4b0f -#define STM32F429_PE11_FUNC_EVENTOUT 0x4b10 -#define STM32F429_PE11_FUNC_ANALOG 0x4b11 - -#define STM32F429_PE12_FUNC_GPIO 0x4c00 -#define STM32F429_PE12_FUNC_TIM1_CH3N 0x4c02 -#define STM32F429_PE12_FUNC_SPI4_SCK 0x4c06 -#define STM32F429_PE12_FUNC_FMC_D9 0x4c0d -#define STM32F429_PE12_FUNC_LCD_B4 0x4c0f -#define STM32F429_PE12_FUNC_EVENTOUT 0x4c10 -#define STM32F429_PE12_FUNC_ANALOG 0x4c11 - -#define STM32F429_PE13_FUNC_GPIO 0x4d00 -#define STM32F429_PE13_FUNC_TIM1_CH3 0x4d02 -#define STM32F429_PE13_FUNC_SPI4_MISO 0x4d06 -#define STM32F429_PE13_FUNC_FMC_D10 0x4d0d -#define STM32F429_PE13_FUNC_LCD_DE 0x4d0f -#define STM32F429_PE13_FUNC_EVENTOUT 0x4d10 -#define STM32F429_PE13_FUNC_ANALOG 0x4d11 - -#define STM32F429_PE14_FUNC_GPIO 0x4e00 -#define STM32F429_PE14_FUNC_TIM1_CH4 0x4e02 -#define STM32F429_PE14_FUNC_SPI4_MOSI 0x4e06 -#define STM32F429_PE14_FUNC_FMC_D11 0x4e0d -#define STM32F429_PE14_FUNC_LCD_CLK 0x4e0f -#define STM32F429_PE14_FUNC_EVENTOUT 0x4e10 -#define STM32F429_PE14_FUNC_ANALOG 0x4e11 - -#define STM32F429_PE15_FUNC_GPIO 0x4f00 -#define STM32F429_PE15_FUNC_TIM1_BKIN 0x4f02 -#define STM32F429_PE15_FUNC_FMC_D12 0x4f0d -#define STM32F429_PE15_FUNC_LCD_R7 0x4f0f -#define STM32F429_PE15_FUNC_EVENTOUT 0x4f10 -#define STM32F429_PE15_FUNC_ANALOG 0x4f11 - - - -#define STM32F429_PF0_FUNC_GPIO 0x5000 -#define STM32F429_PF0_FUNC_I2C2_SDA 0x5005 -#define STM32F429_PF0_FUNC_FMC_A0 0x500d -#define STM32F429_PF0_FUNC_EVENTOUT 0x5010 -#define STM32F429_PF0_FUNC_ANALOG 0x5011 - -#define STM32F429_PF1_FUNC_GPIO 0x5100 -#define STM32F429_PF1_FUNC_I2C2_SCL 0x5105 -#define STM32F429_PF1_FUNC_FMC_A1 0x510d -#define STM32F429_PF1_FUNC_EVENTOUT 0x5110 -#define STM32F429_PF1_FUNC_ANALOG 0x5111 - -#define STM32F429_PF2_FUNC_GPIO 0x5200 -#define STM32F429_PF2_FUNC_I2C2_SMBA 0x5205 -#define STM32F429_PF2_FUNC_FMC_A2 0x520d -#define STM32F429_PF2_FUNC_EVENTOUT 0x5210 -#define STM32F429_PF2_FUNC_ANALOG 0x5211 - -#define STM32F429_PF3_FUNC_GPIO 0x5300 -#define STM32F429_PF3_FUNC_FMC_A3 0x530d -#define STM32F429_PF3_FUNC_EVENTOUT 0x5310 -#define STM32F429_PF3_FUNC_ANALOG 0x5311 - -#define STM32F429_PF4_FUNC_GPIO 0x5400 -#define STM32F429_PF4_FUNC_FMC_A4 0x540d -#define STM32F429_PF4_FUNC_EVENTOUT 0x5410 -#define STM32F429_PF4_FUNC_ANALOG 0x5411 - -#define STM32F429_PF5_FUNC_GPIO 0x5500 -#define STM32F429_PF5_FUNC_FMC_A5 0x550d -#define STM32F429_PF5_FUNC_EVENTOUT 0x5510 -#define STM32F429_PF5_FUNC_ANALOG 0x5511 - -#define STM32F429_PF6_FUNC_GPIO 0x5600 -#define STM32F429_PF6_FUNC_TIM10_CH1 0x5604 -#define STM32F429_PF6_FUNC_SPI5_NSS 0x5606 -#define STM32F429_PF6_FUNC_SAI1_SD_B 0x5607 -#define STM32F429_PF6_FUNC_UART7_RX 0x5609 -#define STM32F429_PF6_FUNC_FMC_NIORD 0x560d -#define STM32F429_PF6_FUNC_EVENTOUT 0x5610 -#define STM32F429_PF6_FUNC_ANALOG 0x5611 - -#define STM32F429_PF7_FUNC_GPIO 0x5700 -#define STM32F429_PF7_FUNC_TIM11_CH1 0x5704 -#define STM32F429_PF7_FUNC_SPI5_SCK 0x5706 -#define STM32F429_PF7_FUNC_SAI1_MCLK_B 0x5707 -#define STM32F429_PF7_FUNC_UART7_TX 0x5709 -#define STM32F429_PF7_FUNC_FMC_NREG 0x570d -#define STM32F429_PF7_FUNC_EVENTOUT 0x5710 -#define STM32F429_PF7_FUNC_ANALOG 0x5711 - -#define STM32F429_PF8_FUNC_GPIO 0x5800 -#define STM32F429_PF8_FUNC_SPI5_MISO 0x5806 -#define STM32F429_PF8_FUNC_SAI1_SCK_B 0x5807 -#define STM32F429_PF8_FUNC_TIM13_CH1 0x580a -#define STM32F429_PF8_FUNC_FMC_NIOWR 0x580d -#define STM32F429_PF8_FUNC_EVENTOUT 0x5810 -#define STM32F429_PF8_FUNC_ANALOG 0x5811 - -#define STM32F429_PF9_FUNC_GPIO 0x5900 -#define STM32F429_PF9_FUNC_SPI5_MOSI 0x5906 -#define STM32F429_PF9_FUNC_SAI1_FS_B 0x5907 -#define STM32F429_PF9_FUNC_TIM14_CH1 0x590a -#define STM32F429_PF9_FUNC_FMC_CD 0x590d -#define STM32F429_PF9_FUNC_EVENTOUT 0x5910 -#define STM32F429_PF9_FUNC_ANALOG 0x5911 - -#define STM32F429_PF10_FUNC_GPIO 0x5a00 -#define STM32F429_PF10_FUNC_FMC_INTR 0x5a0d -#define STM32F429_PF10_FUNC_DCMI_D11 0x5a0e -#define STM32F429_PF10_FUNC_LCD_DE 0x5a0f -#define STM32F429_PF10_FUNC_EVENTOUT 0x5a10 -#define STM32F429_PF10_FUNC_ANALOG 0x5a11 - -#define STM32F429_PF11_FUNC_GPIO 0x5b00 -#define STM32F429_PF11_FUNC_SPI5_MOSI 0x5b06 -#define STM32F429_PF11_FUNC_FMC_SDNRAS 0x5b0d -#define STM32F429_PF11_FUNC_DCMI_D12 0x5b0e -#define STM32F429_PF11_FUNC_EVENTOUT 0x5b10 -#define STM32F429_PF11_FUNC_ANALOG 0x5b11 - -#define STM32F429_PF12_FUNC_GPIO 0x5c00 -#define STM32F429_PF12_FUNC_FMC_A6 0x5c0d -#define STM32F429_PF12_FUNC_EVENTOUT 0x5c10 -#define STM32F429_PF12_FUNC_ANALOG 0x5c11 - -#define STM32F429_PF13_FUNC_GPIO 0x5d00 -#define STM32F429_PF13_FUNC_FMC_A7 0x5d0d -#define STM32F429_PF13_FUNC_EVENTOUT 0x5d10 -#define STM32F429_PF13_FUNC_ANALOG 0x5d11 - -#define STM32F429_PF14_FUNC_GPIO 0x5e00 -#define STM32F429_PF14_FUNC_FMC_A8 0x5e0d -#define STM32F429_PF14_FUNC_EVENTOUT 0x5e10 -#define STM32F429_PF14_FUNC_ANALOG 0x5e11 - -#define STM32F429_PF15_FUNC_GPIO 0x5f00 -#define STM32F429_PF15_FUNC_FMC_A9 0x5f0d -#define STM32F429_PF15_FUNC_EVENTOUT 0x5f10 -#define STM32F429_PF15_FUNC_ANALOG 0x5f11 - - - -#define STM32F429_PG0_FUNC_GPIO 0x6000 -#define STM32F429_PG0_FUNC_FMC_A10 0x600d -#define STM32F429_PG0_FUNC_EVENTOUT 0x6010 -#define STM32F429_PG0_FUNC_ANALOG 0x6011 - -#define STM32F429_PG1_FUNC_GPIO 0x6100 -#define STM32F429_PG1_FUNC_FMC_A11 0x610d -#define STM32F429_PG1_FUNC_EVENTOUT 0x6110 -#define STM32F429_PG1_FUNC_ANALOG 0x6111 - -#define STM32F429_PG2_FUNC_GPIO 0x6200 -#define STM32F429_PG2_FUNC_FMC_A12 0x620d -#define STM32F429_PG2_FUNC_EVENTOUT 0x6210 -#define STM32F429_PG2_FUNC_ANALOG 0x6211 - -#define STM32F429_PG3_FUNC_GPIO 0x6300 -#define STM32F429_PG3_FUNC_FMC_A13 0x630d -#define STM32F429_PG3_FUNC_EVENTOUT 0x6310 -#define STM32F429_PG3_FUNC_ANALOG 0x6311 - -#define STM32F429_PG4_FUNC_GPIO 0x6400 -#define STM32F429_PG4_FUNC_FMC_A14_FMC_BA0 0x640d -#define STM32F429_PG4_FUNC_EVENTOUT 0x6410 -#define STM32F429_PG4_FUNC_ANALOG 0x6411 - -#define STM32F429_PG5_FUNC_GPIO 0x6500 -#define STM32F429_PG5_FUNC_FMC_A15_FMC_BA1 0x650d -#define STM32F429_PG5_FUNC_EVENTOUT 0x6510 -#define STM32F429_PG5_FUNC_ANALOG 0x6511 - -#define STM32F429_PG6_FUNC_GPIO 0x6600 -#define STM32F429_PG6_FUNC_FMC_INT2 0x660d -#define STM32F429_PG6_FUNC_DCMI_D12 0x660e -#define STM32F429_PG6_FUNC_LCD_R7 0x660f -#define STM32F429_PG6_FUNC_EVENTOUT 0x6610 -#define STM32F429_PG6_FUNC_ANALOG 0x6611 - -#define STM32F429_PG7_FUNC_GPIO 0x6700 -#define STM32F429_PG7_FUNC_USART6_CK 0x6709 -#define STM32F429_PG7_FUNC_FMC_INT3 0x670d -#define STM32F429_PG7_FUNC_DCMI_D13 0x670e -#define STM32F429_PG7_FUNC_LCD_CLK 0x670f -#define STM32F429_PG7_FUNC_EVENTOUT 0x6710 -#define STM32F429_PG7_FUNC_ANALOG 0x6711 - -#define STM32F429_PG8_FUNC_GPIO 0x6800 -#define STM32F429_PG8_FUNC_SPI6_NSS 0x6806 -#define STM32F429_PG8_FUNC_USART6_RTS 0x6809 -#define STM32F429_PG8_FUNC_ETH_PPS_OUT 0x680c -#define STM32F429_PG8_FUNC_FMC_SDCLK 0x680d -#define STM32F429_PG8_FUNC_EVENTOUT 0x6810 -#define STM32F429_PG8_FUNC_ANALOG 0x6811 - -#define STM32F429_PG9_FUNC_GPIO 0x6900 -#define STM32F429_PG9_FUNC_USART6_RX 0x6909 -#define STM32F429_PG9_FUNC_FMC_NE2_FMC_NCE3 0x690d -#define STM32F429_PG9_FUNC_DCMI_VSYNC 0x690e -#define STM32F429_PG9_FUNC_EVENTOUT 0x6910 -#define STM32F429_PG9_FUNC_ANALOG 0x6911 - -#define STM32F429_PG10_FUNC_GPIO 0x6a00 -#define STM32F429_PG10_FUNC_LCD_G3 0x6a0a -#define STM32F429_PG10_FUNC_FMC_NCE4_1_FMC_NE3 0x6a0d -#define STM32F429_PG10_FUNC_DCMI_D2 0x6a0e -#define STM32F429_PG10_FUNC_LCD_B2 0x6a0f -#define STM32F429_PG10_FUNC_EVENTOUT 0x6a10 -#define STM32F429_PG10_FUNC_ANALOG 0x6a11 - -#define STM32F429_PG11_FUNC_GPIO 0x6b00 -#define STM32F429_PG11_FUNC_ETH_MII_TX_EN_ETH_RMII_TX_EN 0x6b0c -#define STM32F429_PG11_FUNC_FMC_NCE4_2 0x6b0d -#define STM32F429_PG11_FUNC_DCMI_D3 0x6b0e -#define STM32F429_PG11_FUNC_LCD_B3 0x6b0f -#define STM32F429_PG11_FUNC_EVENTOUT 0x6b10 -#define STM32F429_PG11_FUNC_ANALOG 0x6b11 - -#define STM32F429_PG12_FUNC_GPIO 0x6c00 -#define STM32F429_PG12_FUNC_SPI6_MISO 0x6c06 -#define STM32F429_PG12_FUNC_USART6_RTS 0x6c09 -#define STM32F429_PG12_FUNC_LCD_B4 0x6c0a -#define STM32F429_PG12_FUNC_FMC_NE4 0x6c0d -#define STM32F429_PG12_FUNC_LCD_B1 0x6c0f -#define STM32F429_PG12_FUNC_EVENTOUT 0x6c10 -#define STM32F429_PG12_FUNC_ANALOG 0x6c11 - -#define STM32F429_PG13_FUNC_GPIO 0x6d00 -#define STM32F429_PG13_FUNC_SPI6_SCK 0x6d06 -#define STM32F429_PG13_FUNC_USART6_CTS 0x6d09 -#define STM32F429_PG13_FUNC_ETH_MII_TXD0_ETH_RMII_TXD0 0x6d0c -#define STM32F429_PG13_FUNC_FMC_A24 0x6d0d -#define STM32F429_PG13_FUNC_EVENTOUT 0x6d10 -#define STM32F429_PG13_FUNC_ANALOG 0x6d11 - -#define STM32F429_PG14_FUNC_GPIO 0x6e00 -#define STM32F429_PG14_FUNC_SPI6_MOSI 0x6e06 -#define STM32F429_PG14_FUNC_USART6_TX 0x6e09 -#define STM32F429_PG14_FUNC_ETH_MII_TXD1_ETH_RMII_TXD1 0x6e0c -#define STM32F429_PG14_FUNC_FMC_A25 0x6e0d -#define STM32F429_PG14_FUNC_EVENTOUT 0x6e10 -#define STM32F429_PG14_FUNC_ANALOG 0x6e11 - -#define STM32F429_PG15_FUNC_GPIO 0x6f00 -#define STM32F429_PG15_FUNC_USART6_CTS 0x6f09 -#define STM32F429_PG15_FUNC_FMC_SDNCAS 0x6f0d -#define STM32F429_PG15_FUNC_DCMI_D13 0x6f0e -#define STM32F429_PG15_FUNC_EVENTOUT 0x6f10 -#define STM32F429_PG15_FUNC_ANALOG 0x6f11 - - - -#define STM32F429_PH0_FUNC_GPIO 0x7000 -#define STM32F429_PH0_FUNC_EVENTOUT 0x7010 -#define STM32F429_PH0_FUNC_ANALOG 0x7011 - -#define STM32F429_PH1_FUNC_GPIO 0x7100 -#define STM32F429_PH1_FUNC_EVENTOUT 0x7110 -#define STM32F429_PH1_FUNC_ANALOG 0x7111 - -#define STM32F429_PH2_FUNC_GPIO 0x7200 -#define STM32F429_PH2_FUNC_ETH_MII_CRS 0x720c -#define STM32F429_PH2_FUNC_FMC_SDCKE0 0x720d -#define STM32F429_PH2_FUNC_LCD_R0 0x720f -#define STM32F429_PH2_FUNC_EVENTOUT 0x7210 -#define STM32F429_PH2_FUNC_ANALOG 0x7211 - -#define STM32F429_PH3_FUNC_GPIO 0x7300 -#define STM32F429_PH3_FUNC_ETH_MII_COL 0x730c -#define STM32F429_PH3_FUNC_FMC_SDNE0 0x730d -#define STM32F429_PH3_FUNC_LCD_R1 0x730f -#define STM32F429_PH3_FUNC_EVENTOUT 0x7310 -#define STM32F429_PH3_FUNC_ANALOG 0x7311 - -#define STM32F429_PH4_FUNC_GPIO 0x7400 -#define STM32F429_PH4_FUNC_I2C2_SCL 0x7405 -#define STM32F429_PH4_FUNC_OTG_HS_ULPI_NXT 0x740b -#define STM32F429_PH4_FUNC_EVENTOUT 0x7410 -#define STM32F429_PH4_FUNC_ANALOG 0x7411 - -#define STM32F429_PH5_FUNC_GPIO 0x7500 -#define STM32F429_PH5_FUNC_I2C2_SDA 0x7505 -#define STM32F429_PH5_FUNC_SPI5_NSS 0x7506 -#define STM32F429_PH5_FUNC_FMC_SDNWE 0x750d -#define STM32F429_PH5_FUNC_EVENTOUT 0x7510 -#define STM32F429_PH5_FUNC_ANALOG 0x7511 - -#define STM32F429_PH6_FUNC_GPIO 0x7600 -#define STM32F429_PH6_FUNC_I2C2_SMBA 0x7605 -#define STM32F429_PH6_FUNC_SPI5_SCK 0x7606 -#define STM32F429_PH6_FUNC_TIM12_CH1 0x760a -#define STM32F429_PH6_FUNC_ETH_MII_RXD2 0x760c -#define STM32F429_PH6_FUNC_FMC_SDNE1 0x760d -#define STM32F429_PH6_FUNC_DCMI_D8 0x760e -#define STM32F429_PH6_FUNC_EVENTOUT 0x7610 -#define STM32F429_PH6_FUNC_ANALOG 0x7611 - -#define STM32F429_PH7_FUNC_GPIO 0x7700 -#define STM32F429_PH7_FUNC_I2C3_SCL 0x7705 -#define STM32F429_PH7_FUNC_SPI5_MISO 0x7706 -#define STM32F429_PH7_FUNC_ETH_MII_RXD3 0x770c -#define STM32F429_PH7_FUNC_FMC_SDCKE1 0x770d -#define STM32F429_PH7_FUNC_DCMI_D9 0x770e -#define STM32F429_PH7_FUNC_EVENTOUT 0x7710 -#define STM32F429_PH7_FUNC_ANALOG 0x7711 - -#define STM32F429_PH8_FUNC_GPIO 0x7800 -#define STM32F429_PH8_FUNC_I2C3_SDA 0x7805 -#define STM32F429_PH8_FUNC_FMC_D16 0x780d -#define STM32F429_PH8_FUNC_DCMI_HSYNC 0x780e -#define STM32F429_PH8_FUNC_LCD_R2 0x780f -#define STM32F429_PH8_FUNC_EVENTOUT 0x7810 -#define STM32F429_PH8_FUNC_ANALOG 0x7811 - -#define STM32F429_PH9_FUNC_GPIO 0x7900 -#define STM32F429_PH9_FUNC_I2C3_SMBA 0x7905 -#define STM32F429_PH9_FUNC_TIM12_CH2 0x790a -#define STM32F429_PH9_FUNC_FMC_D17 0x790d -#define STM32F429_PH9_FUNC_DCMI_D0 0x790e -#define STM32F429_PH9_FUNC_LCD_R3 0x790f -#define STM32F429_PH9_FUNC_EVENTOUT 0x7910 -#define STM32F429_PH9_FUNC_ANALOG 0x7911 - -#define STM32F429_PH10_FUNC_GPIO 0x7a00 -#define STM32F429_PH10_FUNC_TIM5_CH1 0x7a03 -#define STM32F429_PH10_FUNC_FMC_D18 0x7a0d -#define STM32F429_PH10_FUNC_DCMI_D1 0x7a0e -#define STM32F429_PH10_FUNC_LCD_R4 0x7a0f -#define STM32F429_PH10_FUNC_EVENTOUT 0x7a10 -#define STM32F429_PH10_FUNC_ANALOG 0x7a11 - -#define STM32F429_PH11_FUNC_GPIO 0x7b00 -#define STM32F429_PH11_FUNC_TIM5_CH2 0x7b03 -#define STM32F429_PH11_FUNC_FMC_D19 0x7b0d -#define STM32F429_PH11_FUNC_DCMI_D2 0x7b0e -#define STM32F429_PH11_FUNC_LCD_R5 0x7b0f -#define STM32F429_PH11_FUNC_EVENTOUT 0x7b10 -#define STM32F429_PH11_FUNC_ANALOG 0x7b11 - -#define STM32F429_PH12_FUNC_GPIO 0x7c00 -#define STM32F429_PH12_FUNC_TIM5_CH3 0x7c03 -#define STM32F429_PH12_FUNC_FMC_D20 0x7c0d -#define STM32F429_PH12_FUNC_DCMI_D3 0x7c0e -#define STM32F429_PH12_FUNC_LCD_R6 0x7c0f -#define STM32F429_PH12_FUNC_EVENTOUT 0x7c10 -#define STM32F429_PH12_FUNC_ANALOG 0x7c11 - -#define STM32F429_PH13_FUNC_GPIO 0x7d00 -#define STM32F429_PH13_FUNC_TIM8_CH1N 0x7d04 -#define STM32F429_PH13_FUNC_CAN1_TX 0x7d0a -#define STM32F429_PH13_FUNC_FMC_D21 0x7d0d -#define STM32F429_PH13_FUNC_LCD_G2 0x7d0f -#define STM32F429_PH13_FUNC_EVENTOUT 0x7d10 -#define STM32F429_PH13_FUNC_ANALOG 0x7d11 - -#define STM32F429_PH14_FUNC_GPIO 0x7e00 -#define STM32F429_PH14_FUNC_TIM8_CH2N 0x7e04 -#define STM32F429_PH14_FUNC_FMC_D22 0x7e0d -#define STM32F429_PH14_FUNC_DCMI_D4 0x7e0e -#define STM32F429_PH14_FUNC_LCD_G3 0x7e0f -#define STM32F429_PH14_FUNC_EVENTOUT 0x7e10 -#define STM32F429_PH14_FUNC_ANALOG 0x7e11 - -#define STM32F429_PH15_FUNC_GPIO 0x7f00 -#define STM32F429_PH15_FUNC_TIM8_CH3N 0x7f04 -#define STM32F429_PH15_FUNC_FMC_D23 0x7f0d -#define STM32F429_PH15_FUNC_DCMI_D11 0x7f0e -#define STM32F429_PH15_FUNC_LCD_G4 0x7f0f -#define STM32F429_PH15_FUNC_EVENTOUT 0x7f10 -#define STM32F429_PH15_FUNC_ANALOG 0x7f11 - - - -#define STM32F429_PI0_FUNC_GPIO 0x8000 -#define STM32F429_PI0_FUNC_TIM5_CH4 0x8003 -#define STM32F429_PI0_FUNC_SPI2_NSS_I2S2_WS 0x8006 -#define STM32F429_PI0_FUNC_FMC_D24 0x800d -#define STM32F429_PI0_FUNC_DCMI_D13 0x800e -#define STM32F429_PI0_FUNC_LCD_G5 0x800f -#define STM32F429_PI0_FUNC_EVENTOUT 0x8010 -#define STM32F429_PI0_FUNC_ANALOG 0x8011 - -#define STM32F429_PI1_FUNC_GPIO 0x8100 -#define STM32F429_PI1_FUNC_SPI2_SCK_I2S2_CK 0x8106 -#define STM32F429_PI1_FUNC_FMC_D25 0x810d -#define STM32F429_PI1_FUNC_DCMI_D8 0x810e -#define STM32F429_PI1_FUNC_LCD_G6 0x810f -#define STM32F429_PI1_FUNC_EVENTOUT 0x8110 -#define STM32F429_PI1_FUNC_ANALOG 0x8111 - -#define STM32F429_PI2_FUNC_GPIO 0x8200 -#define STM32F429_PI2_FUNC_TIM8_CH4 0x8204 -#define STM32F429_PI2_FUNC_SPI2_MISO 0x8206 -#define STM32F429_PI2_FUNC_I2S2EXT_SD 0x8207 -#define STM32F429_PI2_FUNC_FMC_D26 0x820d -#define STM32F429_PI2_FUNC_DCMI_D9 0x820e -#define STM32F429_PI2_FUNC_LCD_G7 0x820f -#define STM32F429_PI2_FUNC_EVENTOUT 0x8210 -#define STM32F429_PI2_FUNC_ANALOG 0x8211 - -#define STM32F429_PI3_FUNC_GPIO 0x8300 -#define STM32F429_PI3_FUNC_TIM8_ETR 0x8304 -#define STM32F429_PI3_FUNC_SPI2_MOSI_I2S2_SD 0x8306 -#define STM32F429_PI3_FUNC_FMC_D27 0x830d -#define STM32F429_PI3_FUNC_DCMI_D10 0x830e -#define STM32F429_PI3_FUNC_EVENTOUT 0x8310 -#define STM32F429_PI3_FUNC_ANALOG 0x8311 - -#define STM32F429_PI4_FUNC_GPIO 0x8400 -#define STM32F429_PI4_FUNC_TIM8_BKIN 0x8404 -#define STM32F429_PI4_FUNC_FMC_NBL2 0x840d -#define STM32F429_PI4_FUNC_DCMI_D5 0x840e -#define STM32F429_PI4_FUNC_LCD_B4 0x840f -#define STM32F429_PI4_FUNC_EVENTOUT 0x8410 -#define STM32F429_PI4_FUNC_ANALOG 0x8411 - -#define STM32F429_PI5_FUNC_GPIO 0x8500 -#define STM32F429_PI5_FUNC_TIM8_CH1 0x8504 -#define STM32F429_PI5_FUNC_FMC_NBL3 0x850d -#define STM32F429_PI5_FUNC_DCMI_VSYNC 0x850e -#define STM32F429_PI5_FUNC_LCD_B5 0x850f -#define STM32F429_PI5_FUNC_EVENTOUT 0x8510 -#define STM32F429_PI5_FUNC_ANALOG 0x8511 - -#define STM32F429_PI6_FUNC_GPIO 0x8600 -#define STM32F429_PI6_FUNC_TIM8_CH2 0x8604 -#define STM32F429_PI6_FUNC_FMC_D28 0x860d -#define STM32F429_PI6_FUNC_DCMI_D6 0x860e -#define STM32F429_PI6_FUNC_LCD_B6 0x860f -#define STM32F429_PI6_FUNC_EVENTOUT 0x8610 -#define STM32F429_PI6_FUNC_ANALOG 0x8611 - -#define STM32F429_PI7_FUNC_GPIO 0x8700 -#define STM32F429_PI7_FUNC_TIM8_CH3 0x8704 -#define STM32F429_PI7_FUNC_FMC_D29 0x870d -#define STM32F429_PI7_FUNC_DCMI_D7 0x870e -#define STM32F429_PI7_FUNC_LCD_B7 0x870f -#define STM32F429_PI7_FUNC_EVENTOUT 0x8710 -#define STM32F429_PI7_FUNC_ANALOG 0x8711 - -#define STM32F429_PI8_FUNC_GPIO 0x8800 -#define STM32F429_PI8_FUNC_EVENTOUT 0x8810 -#define STM32F429_PI8_FUNC_ANALOG 0x8811 - -#define STM32F429_PI9_FUNC_GPIO 0x8900 -#define STM32F429_PI9_FUNC_CAN1_RX 0x890a -#define STM32F429_PI9_FUNC_FMC_D30 0x890d -#define STM32F429_PI9_FUNC_LCD_VSYNC 0x890f -#define STM32F429_PI9_FUNC_EVENTOUT 0x8910 -#define STM32F429_PI9_FUNC_ANALOG 0x8911 - -#define STM32F429_PI10_FUNC_GPIO 0x8a00 -#define STM32F429_PI10_FUNC_ETH_MII_RX_ER 0x8a0c -#define STM32F429_PI10_FUNC_FMC_D31 0x8a0d -#define STM32F429_PI10_FUNC_LCD_HSYNC 0x8a0f -#define STM32F429_PI10_FUNC_EVENTOUT 0x8a10 -#define STM32F429_PI10_FUNC_ANALOG 0x8a11 - -#define STM32F429_PI11_FUNC_GPIO 0x8b00 -#define STM32F429_PI11_FUNC_OTG_HS_ULPI_DIR 0x8b0b -#define STM32F429_PI11_FUNC_EVENTOUT 0x8b10 -#define STM32F429_PI11_FUNC_ANALOG 0x8b11 - -#define STM32F429_PI12_FUNC_GPIO 0x8c00 -#define STM32F429_PI12_FUNC_LCD_HSYNC 0x8c0f -#define STM32F429_PI12_FUNC_EVENTOUT 0x8c10 -#define STM32F429_PI12_FUNC_ANALOG 0x8c11 - -#define STM32F429_PI13_FUNC_GPIO 0x8d00 -#define STM32F429_PI13_FUNC_LCD_VSYNC 0x8d0f -#define STM32F429_PI13_FUNC_EVENTOUT 0x8d10 -#define STM32F429_PI13_FUNC_ANALOG 0x8d11 - -#define STM32F429_PI14_FUNC_GPIO 0x8e00 -#define STM32F429_PI14_FUNC_LCD_CLK 0x8e0f -#define STM32F429_PI14_FUNC_EVENTOUT 0x8e10 -#define STM32F429_PI14_FUNC_ANALOG 0x8e11 - -#define STM32F429_PI15_FUNC_GPIO 0x8f00 -#define STM32F429_PI15_FUNC_LCD_R0 0x8f0f -#define STM32F429_PI15_FUNC_EVENTOUT 0x8f10 -#define STM32F429_PI15_FUNC_ANALOG 0x8f11 - - - -#define STM32F429_PJ0_FUNC_GPIO 0x9000 -#define STM32F429_PJ0_FUNC_LCD_R1 0x900f -#define STM32F429_PJ0_FUNC_EVENTOUT 0x9010 -#define STM32F429_PJ0_FUNC_ANALOG 0x9011 - -#define STM32F429_PJ1_FUNC_GPIO 0x9100 -#define STM32F429_PJ1_FUNC_LCD_R2 0x910f -#define STM32F429_PJ1_FUNC_EVENTOUT 0x9110 -#define STM32F429_PJ1_FUNC_ANALOG 0x9111 - -#define STM32F429_PJ2_FUNC_GPIO 0x9200 -#define STM32F429_PJ2_FUNC_LCD_R3 0x920f -#define STM32F429_PJ2_FUNC_EVENTOUT 0x9210 -#define STM32F429_PJ2_FUNC_ANALOG 0x9211 - -#define STM32F429_PJ3_FUNC_GPIO 0x9300 -#define STM32F429_PJ3_FUNC_LCD_R4 0x930f -#define STM32F429_PJ3_FUNC_EVENTOUT 0x9310 -#define STM32F429_PJ3_FUNC_ANALOG 0x9311 - -#define STM32F429_PJ4_FUNC_GPIO 0x9400 -#define STM32F429_PJ4_FUNC_LCD_R5 0x940f -#define STM32F429_PJ4_FUNC_EVENTOUT 0x9410 -#define STM32F429_PJ4_FUNC_ANALOG 0x9411 - -#define STM32F429_PJ5_FUNC_GPIO 0x9500 -#define STM32F429_PJ5_FUNC_LCD_R6 0x950f -#define STM32F429_PJ5_FUNC_EVENTOUT 0x9510 -#define STM32F429_PJ5_FUNC_ANALOG 0x9511 - -#define STM32F429_PJ6_FUNC_GPIO 0x9600 -#define STM32F429_PJ6_FUNC_LCD_R7 0x960f -#define STM32F429_PJ6_FUNC_EVENTOUT 0x9610 -#define STM32F429_PJ6_FUNC_ANALOG 0x9611 - -#define STM32F429_PJ7_FUNC_GPIO 0x9700 -#define STM32F429_PJ7_FUNC_LCD_G0 0x970f -#define STM32F429_PJ7_FUNC_EVENTOUT 0x9710 -#define STM32F429_PJ7_FUNC_ANALOG 0x9711 - -#define STM32F429_PJ8_FUNC_GPIO 0x9800 -#define STM32F429_PJ8_FUNC_LCD_G1 0x980f -#define STM32F429_PJ8_FUNC_EVENTOUT 0x9810 -#define STM32F429_PJ8_FUNC_ANALOG 0x9811 - -#define STM32F429_PJ9_FUNC_GPIO 0x9900 -#define STM32F429_PJ9_FUNC_LCD_G2 0x990f -#define STM32F429_PJ9_FUNC_EVENTOUT 0x9910 -#define STM32F429_PJ9_FUNC_ANALOG 0x9911 - -#define STM32F429_PJ10_FUNC_GPIO 0x9a00 -#define STM32F429_PJ10_FUNC_LCD_G3 0x9a0f -#define STM32F429_PJ10_FUNC_EVENTOUT 0x9a10 -#define STM32F429_PJ10_FUNC_ANALOG 0x9a11 - -#define STM32F429_PJ11_FUNC_GPIO 0x9b00 -#define STM32F429_PJ11_FUNC_LCD_G4 0x9b0f -#define STM32F429_PJ11_FUNC_EVENTOUT 0x9b10 -#define STM32F429_PJ11_FUNC_ANALOG 0x9b11 - -#define STM32F429_PJ12_FUNC_GPIO 0x9c00 -#define STM32F429_PJ12_FUNC_LCD_B0 0x9c0f -#define STM32F429_PJ12_FUNC_EVENTOUT 0x9c10 -#define STM32F429_PJ12_FUNC_ANALOG 0x9c11 - -#define STM32F429_PJ13_FUNC_GPIO 0x9d00 -#define STM32F429_PJ13_FUNC_LCD_B1 0x9d0f -#define STM32F429_PJ13_FUNC_EVENTOUT 0x9d10 -#define STM32F429_PJ13_FUNC_ANALOG 0x9d11 - -#define STM32F429_PJ14_FUNC_GPIO 0x9e00 -#define STM32F429_PJ14_FUNC_LCD_B2 0x9e0f -#define STM32F429_PJ14_FUNC_EVENTOUT 0x9e10 -#define STM32F429_PJ14_FUNC_ANALOG 0x9e11 - -#define STM32F429_PJ15_FUNC_GPIO 0x9f00 -#define STM32F429_PJ15_FUNC_LCD_B3 0x9f0f -#define STM32F429_PJ15_FUNC_EVENTOUT 0x9f10 -#define STM32F429_PJ15_FUNC_ANALOG 0x9f11 - - - -#define STM32F429_PK0_FUNC_GPIO 0xa000 -#define STM32F429_PK0_FUNC_LCD_G5 0xa00f -#define STM32F429_PK0_FUNC_EVENTOUT 0xa010 -#define STM32F429_PK0_FUNC_ANALOG 0xa011 - -#define STM32F429_PK1_FUNC_GPIO 0xa100 -#define STM32F429_PK1_FUNC_LCD_G6 0xa10f -#define STM32F429_PK1_FUNC_EVENTOUT 0xa110 -#define STM32F429_PK1_FUNC_ANALOG 0xa111 - -#define STM32F429_PK2_FUNC_GPIO 0xa200 -#define STM32F429_PK2_FUNC_LCD_G7 0xa20f -#define STM32F429_PK2_FUNC_EVENTOUT 0xa210 -#define STM32F429_PK2_FUNC_ANALOG 0xa211 - -#define STM32F429_PK3_FUNC_GPIO 0xa300 -#define STM32F429_PK3_FUNC_LCD_B4 0xa30f -#define STM32F429_PK3_FUNC_EVENTOUT 0xa310 -#define STM32F429_PK3_FUNC_ANALOG 0xa311 - -#define STM32F429_PK4_FUNC_GPIO 0xa400 -#define STM32F429_PK4_FUNC_LCD_B5 0xa40f -#define STM32F429_PK4_FUNC_EVENTOUT 0xa410 -#define STM32F429_PK4_FUNC_ANALOG 0xa411 - -#define STM32F429_PK5_FUNC_GPIO 0xa500 -#define STM32F429_PK5_FUNC_LCD_B6 0xa50f -#define STM32F429_PK5_FUNC_EVENTOUT 0xa510 -#define STM32F429_PK5_FUNC_ANALOG 0xa511 - -#define STM32F429_PK6_FUNC_GPIO 0xa600 -#define STM32F429_PK6_FUNC_LCD_B7 0xa60f -#define STM32F429_PK6_FUNC_EVENTOUT 0xa610 -#define STM32F429_PK6_FUNC_ANALOG 0xa611 - -#define STM32F429_PK7_FUNC_GPIO 0xa700 -#define STM32F429_PK7_FUNC_LCD_DE 0xa70f -#define STM32F429_PK7_FUNC_EVENTOUT 0xa710 -#define STM32F429_PK7_FUNC_ANALOG 0xa711 - -#endif /* _DT_BINDINGS_STM32F429_PINFUNC_H */ diff --git a/include/dt-bindings/pinctrl/stm32f746-pinfunc.h b/include/dt-bindings/pinctrl/stm32f746-pinfunc.h deleted file mode 100644 index 6348c6a830e9..000000000000 --- a/include/dt-bindings/pinctrl/stm32f746-pinfunc.h +++ /dev/null @@ -1,1324 +0,0 @@ -#ifndef _DT_BINDINGS_STM32F746_PINFUNC_H -#define _DT_BINDINGS_STM32F746_PINFUNC_H - -#define STM32F746_PA0_FUNC_GPIO 0x0 -#define STM32F746_PA0_FUNC_TIM2_CH1_TIM2_ETR 0x2 -#define STM32F746_PA0_FUNC_TIM5_CH1 0x3 -#define STM32F746_PA0_FUNC_TIM8_ETR 0x4 -#define STM32F746_PA0_FUNC_USART2_CTS 0x8 -#define STM32F746_PA0_FUNC_UART4_TX 0x9 -#define STM32F746_PA0_FUNC_SAI2_SD_B 0xb -#define STM32F746_PA0_FUNC_ETH_MII_CRS 0xc -#define STM32F746_PA0_FUNC_EVENTOUT 0x10 -#define STM32F746_PA0_FUNC_ANALOG 0x11 - -#define STM32F746_PA1_FUNC_GPIO 0x100 -#define STM32F746_PA1_FUNC_TIM2_CH2 0x102 -#define STM32F746_PA1_FUNC_TIM5_CH2 0x103 -#define STM32F746_PA1_FUNC_USART2_RTS 0x108 -#define STM32F746_PA1_FUNC_UART4_RX 0x109 -#define STM32F746_PA1_FUNC_QUADSPI_BK1_IO3 0x10a -#define STM32F746_PA1_FUNC_SAI2_MCLK_B 0x10b -#define STM32F746_PA1_FUNC_ETH_MII_RX_CLK_ETH_RMII_REF_CLK 0x10c -#define STM32F746_PA1_FUNC_LCD_R2 0x10f -#define STM32F746_PA1_FUNC_EVENTOUT 0x110 -#define STM32F746_PA1_FUNC_ANALOG 0x111 - -#define STM32F746_PA2_FUNC_GPIO 0x200 -#define STM32F746_PA2_FUNC_TIM2_CH3 0x202 -#define STM32F746_PA2_FUNC_TIM5_CH3 0x203 -#define STM32F746_PA2_FUNC_TIM9_CH1 0x204 -#define STM32F746_PA2_FUNC_USART2_TX 0x208 -#define STM32F746_PA2_FUNC_SAI2_SCK_B 0x209 -#define STM32F746_PA2_FUNC_ETH_MDIO 0x20c -#define STM32F746_PA2_FUNC_LCD_R1 0x20f -#define STM32F746_PA2_FUNC_EVENTOUT 0x210 -#define STM32F746_PA2_FUNC_ANALOG 0x211 - -#define STM32F746_PA3_FUNC_GPIO 0x300 -#define STM32F746_PA3_FUNC_TIM2_CH4 0x302 -#define STM32F746_PA3_FUNC_TIM5_CH4 0x303 -#define STM32F746_PA3_FUNC_TIM9_CH2 0x304 -#define STM32F746_PA3_FUNC_USART2_RX 0x308 -#define STM32F746_PA3_FUNC_OTG_HS_ULPI_D0 0x30b -#define STM32F746_PA3_FUNC_ETH_MII_COL 0x30c -#define STM32F746_PA3_FUNC_LCD_B5 0x30f -#define STM32F746_PA3_FUNC_EVENTOUT 0x310 -#define STM32F746_PA3_FUNC_ANALOG 0x311 - -#define STM32F746_PA4_FUNC_GPIO 0x400 -#define STM32F746_PA4_FUNC_SPI1_NSS_I2S1_WS 0x406 -#define STM32F746_PA4_FUNC_SPI3_NSS_I2S3_WS 0x407 -#define STM32F746_PA4_FUNC_USART2_CK 0x408 -#define STM32F746_PA4_FUNC_OTG_HS_SOF 0x40d -#define STM32F746_PA4_FUNC_DCMI_HSYNC 0x40e -#define STM32F746_PA4_FUNC_LCD_VSYNC 0x40f -#define STM32F746_PA4_FUNC_EVENTOUT 0x410 -#define STM32F746_PA4_FUNC_ANALOG 0x411 - -#define STM32F746_PA5_FUNC_GPIO 0x500 -#define STM32F746_PA5_FUNC_TIM2_CH1_TIM2_ETR 0x502 -#define STM32F746_PA5_FUNC_TIM8_CH1N 0x504 -#define STM32F746_PA5_FUNC_SPI1_SCK_I2S1_CK 0x506 -#define STM32F746_PA5_FUNC_OTG_HS_ULPI_CK 0x50b -#define STM32F746_PA5_FUNC_LCD_R4 0x50f -#define STM32F746_PA5_FUNC_EVENTOUT 0x510 -#define STM32F746_PA5_FUNC_ANALOG 0x511 - -#define STM32F746_PA6_FUNC_GPIO 0x600 -#define STM32F746_PA6_FUNC_TIM1_BKIN 0x602 -#define STM32F746_PA6_FUNC_TIM3_CH1 0x603 -#define STM32F746_PA6_FUNC_TIM8_BKIN 0x604 -#define STM32F746_PA6_FUNC_SPI1_MISO 0x606 -#define STM32F746_PA6_FUNC_TIM13_CH1 0x60a -#define STM32F746_PA6_FUNC_DCMI_PIXCLK 0x60e -#define STM32F746_PA6_FUNC_LCD_G2 0x60f -#define STM32F746_PA6_FUNC_EVENTOUT 0x610 -#define STM32F746_PA6_FUNC_ANALOG 0x611 - -#define STM32F746_PA7_FUNC_GPIO 0x700 -#define STM32F746_PA7_FUNC_TIM1_CH1N 0x702 -#define STM32F746_PA7_FUNC_TIM3_CH2 0x703 -#define STM32F746_PA7_FUNC_TIM8_CH1N 0x704 -#define STM32F746_PA7_FUNC_SPI1_MOSI_I2S1_SD 0x706 -#define STM32F746_PA7_FUNC_TIM14_CH1 0x70a -#define STM32F746_PA7_FUNC_ETH_MII_RX_DV_ETH_RMII_CRS_DV 0x70c -#define STM32F746_PA7_FUNC_FMC_SDNWE 0x70d -#define STM32F746_PA7_FUNC_EVENTOUT 0x710 -#define STM32F746_PA7_FUNC_ANALOG 0x711 - -#define STM32F746_PA8_FUNC_GPIO 0x800 -#define STM32F746_PA8_FUNC_MCO1 0x801 -#define STM32F746_PA8_FUNC_TIM1_CH1 0x802 -#define STM32F746_PA8_FUNC_TIM8_BKIN2 0x804 -#define STM32F746_PA8_FUNC_I2C3_SCL 0x805 -#define STM32F746_PA8_FUNC_USART1_CK 0x808 -#define STM32F746_PA8_FUNC_OTG_FS_SOF 0x80b -#define STM32F746_PA8_FUNC_LCD_R6 0x80f -#define STM32F746_PA8_FUNC_EVENTOUT 0x810 -#define STM32F746_PA8_FUNC_ANALOG 0x811 - -#define STM32F746_PA9_FUNC_GPIO 0x900 -#define STM32F746_PA9_FUNC_TIM1_CH2 0x902 -#define STM32F746_PA9_FUNC_I2C3_SMBA 0x905 -#define STM32F746_PA9_FUNC_SPI2_SCK_I2S2_CK 0x906 -#define STM32F746_PA9_FUNC_USART1_TX 0x908 -#define STM32F746_PA9_FUNC_DCMI_D0 0x90e -#define STM32F746_PA9_FUNC_EVENTOUT 0x910 -#define STM32F746_PA9_FUNC_ANALOG 0x911 - -#define STM32F746_PA10_FUNC_GPIO 0xa00 -#define STM32F746_PA10_FUNC_TIM1_CH3 0xa02 -#define STM32F746_PA10_FUNC_USART1_RX 0xa08 -#define STM32F746_PA10_FUNC_OTG_FS_ID 0xa0b -#define STM32F746_PA10_FUNC_DCMI_D1 0xa0e -#define STM32F746_PA10_FUNC_EVENTOUT 0xa10 -#define STM32F746_PA10_FUNC_ANALOG 0xa11 - -#define STM32F746_PA11_FUNC_GPIO 0xb00 -#define STM32F746_PA11_FUNC_TIM1_CH4 0xb02 -#define STM32F746_PA11_FUNC_USART1_CTS 0xb08 -#define STM32F746_PA11_FUNC_CAN1_RX 0xb0a -#define STM32F746_PA11_FUNC_OTG_FS_DM 0xb0b -#define STM32F746_PA11_FUNC_LCD_R4 0xb0f -#define STM32F746_PA11_FUNC_EVENTOUT 0xb10 -#define STM32F746_PA11_FUNC_ANALOG 0xb11 - -#define STM32F746_PA12_FUNC_GPIO 0xc00 -#define STM32F746_PA12_FUNC_TIM1_ETR 0xc02 -#define STM32F746_PA12_FUNC_USART1_RTS 0xc08 -#define STM32F746_PA12_FUNC_SAI2_FS_B 0xc09 -#define STM32F746_PA12_FUNC_CAN1_TX 0xc0a -#define STM32F746_PA12_FUNC_OTG_FS_DP 0xc0b -#define STM32F746_PA12_FUNC_LCD_R5 0xc0f -#define STM32F746_PA12_FUNC_EVENTOUT 0xc10 -#define STM32F746_PA12_FUNC_ANALOG 0xc11 - -#define STM32F746_PA13_FUNC_GPIO 0xd00 -#define STM32F746_PA13_FUNC_JTMS_SWDIO 0xd01 -#define STM32F746_PA13_FUNC_EVENTOUT 0xd10 -#define STM32F746_PA13_FUNC_ANALOG 0xd11 - -#define STM32F746_PA14_FUNC_GPIO 0xe00 -#define STM32F746_PA14_FUNC_JTCK_SWCLK 0xe01 -#define STM32F746_PA14_FUNC_EVENTOUT 0xe10 -#define STM32F746_PA14_FUNC_ANALOG 0xe11 - -#define STM32F746_PA15_FUNC_GPIO 0xf00 -#define STM32F746_PA15_FUNC_JTDI 0xf01 -#define STM32F746_PA15_FUNC_TIM2_CH1_TIM2_ETR 0xf02 -#define STM32F746_PA15_FUNC_HDMI_CEC 0xf05 -#define STM32F746_PA15_FUNC_SPI1_NSS_I2S1_WS 0xf06 -#define STM32F746_PA15_FUNC_SPI3_NSS_I2S3_WS 0xf07 -#define STM32F746_PA15_FUNC_UART4_RTS 0xf09 -#define STM32F746_PA15_FUNC_EVENTOUT 0xf10 -#define STM32F746_PA15_FUNC_ANALOG 0xf11 - - -#define STM32F746_PB0_FUNC_GPIO 0x1000 -#define STM32F746_PB0_FUNC_TIM1_CH2N 0x1002 -#define STM32F746_PB0_FUNC_TIM3_CH3 0x1003 -#define STM32F746_PB0_FUNC_TIM8_CH2N 0x1004 -#define STM32F746_PB0_FUNC_UART4_CTS 0x1009 -#define STM32F746_PB0_FUNC_LCD_R3 0x100a -#define STM32F746_PB0_FUNC_OTG_HS_ULPI_D1 0x100b -#define STM32F746_PB0_FUNC_ETH_MII_RXD2 0x100c -#define STM32F746_PB0_FUNC_EVENTOUT 0x1010 -#define STM32F746_PB0_FUNC_ANALOG 0x1011 - -#define STM32F746_PB1_FUNC_GPIO 0x1100 -#define STM32F746_PB1_FUNC_TIM1_CH3N 0x1102 -#define STM32F746_PB1_FUNC_TIM3_CH4 0x1103 -#define STM32F746_PB1_FUNC_TIM8_CH3N 0x1104 -#define STM32F746_PB1_FUNC_LCD_R6 0x110a -#define STM32F746_PB1_FUNC_OTG_HS_ULPI_D2 0x110b -#define STM32F746_PB1_FUNC_ETH_MII_RXD3 0x110c -#define STM32F746_PB1_FUNC_EVENTOUT 0x1110 -#define STM32F746_PB1_FUNC_ANALOG 0x1111 - -#define STM32F746_PB2_FUNC_GPIO 0x1200 -#define STM32F746_PB2_FUNC_SAI1_SD_A 0x1207 -#define STM32F746_PB2_FUNC_SPI3_MOSI_I2S3_SD 0x1208 -#define STM32F746_PB2_FUNC_QUADSPI_CLK 0x120a -#define STM32F746_PB2_FUNC_EVENTOUT 0x1210 -#define STM32F746_PB2_FUNC_ANALOG 0x1211 - -#define STM32F746_PB3_FUNC_GPIO 0x1300 -#define STM32F746_PB3_FUNC_JTDO_TRACESWO 0x1301 -#define STM32F746_PB3_FUNC_TIM2_CH2 0x1302 -#define STM32F746_PB3_FUNC_SPI1_SCK_I2S1_CK 0x1306 -#define STM32F746_PB3_FUNC_SPI3_SCK_I2S3_CK 0x1307 -#define STM32F746_PB3_FUNC_EVENTOUT 0x1310 -#define STM32F746_PB3_FUNC_ANALOG 0x1311 - -#define STM32F746_PB4_FUNC_GPIO 0x1400 -#define STM32F746_PB4_FUNC_NJTRST 0x1401 -#define STM32F746_PB4_FUNC_TIM3_CH1 0x1403 -#define STM32F746_PB4_FUNC_SPI1_MISO 0x1406 -#define STM32F746_PB4_FUNC_SPI3_MISO 0x1407 -#define STM32F746_PB4_FUNC_SPI2_NSS_I2S2_WS 0x1408 -#define STM32F746_PB4_FUNC_EVENTOUT 0x1410 -#define STM32F746_PB4_FUNC_ANALOG 0x1411 - -#define STM32F746_PB5_FUNC_GPIO 0x1500 -#define STM32F746_PB5_FUNC_TIM3_CH2 0x1503 -#define STM32F746_PB5_FUNC_I2C1_SMBA 0x1505 -#define STM32F746_PB5_FUNC_SPI1_MOSI_I2S1_SD 0x1506 -#define STM32F746_PB5_FUNC_SPI3_MOSI_I2S3_SD 0x1507 -#define STM32F746_PB5_FUNC_CAN2_RX 0x150a -#define STM32F746_PB5_FUNC_OTG_HS_ULPI_D7 0x150b -#define STM32F746_PB5_FUNC_ETH_PPS_OUT 0x150c -#define STM32F746_PB5_FUNC_FMC_SDCKE1 0x150d -#define STM32F746_PB5_FUNC_DCMI_D10 0x150e -#define STM32F746_PB5_FUNC_EVENTOUT 0x1510 -#define STM32F746_PB5_FUNC_ANALOG 0x1511 - -#define STM32F746_PB6_FUNC_GPIO 0x1600 -#define STM32F746_PB6_FUNC_TIM4_CH1 0x1603 -#define STM32F746_PB6_FUNC_HDMI_CEC 0x1604 -#define STM32F746_PB6_FUNC_I2C1_SCL 0x1605 -#define STM32F746_PB6_FUNC_USART1_TX 0x1608 -#define STM32F746_PB6_FUNC_CAN2_TX 0x160a -#define STM32F746_PB6_FUNC_QUADSPI_BK1_NCS 0x160b -#define STM32F746_PB6_FUNC_FMC_SDNE1 0x160d -#define STM32F746_PB6_FUNC_DCMI_D5 0x160e -#define STM32F746_PB6_FUNC_EVENTOUT 0x1610 -#define STM32F746_PB6_FUNC_ANALOG 0x1611 - -#define STM32F746_PB7_FUNC_GPIO 0x1700 -#define STM32F746_PB7_FUNC_TIM4_CH2 0x1703 -#define STM32F746_PB7_FUNC_I2C1_SDA 0x1705 -#define STM32F746_PB7_FUNC_USART1_RX 0x1708 -#define STM32F746_PB7_FUNC_FMC_NL 0x170d -#define STM32F746_PB7_FUNC_DCMI_VSYNC 0x170e -#define STM32F746_PB7_FUNC_EVENTOUT 0x1710 -#define STM32F746_PB7_FUNC_ANALOG 0x1711 - -#define STM32F746_PB8_FUNC_GPIO 0x1800 -#define STM32F746_PB8_FUNC_TIM4_CH3 0x1803 -#define STM32F746_PB8_FUNC_TIM10_CH1 0x1804 -#define STM32F746_PB8_FUNC_I2C1_SCL 0x1805 -#define STM32F746_PB8_FUNC_CAN1_RX 0x180a -#define STM32F746_PB8_FUNC_ETH_MII_TXD3 0x180c -#define STM32F746_PB8_FUNC_SDMMC1_D4 0x180d -#define STM32F746_PB8_FUNC_DCMI_D6 0x180e -#define STM32F746_PB8_FUNC_LCD_B6 0x180f -#define STM32F746_PB8_FUNC_EVENTOUT 0x1810 -#define STM32F746_PB8_FUNC_ANALOG 0x1811 - -#define STM32F746_PB9_FUNC_GPIO 0x1900 -#define STM32F746_PB9_FUNC_TIM4_CH4 0x1903 -#define STM32F746_PB9_FUNC_TIM11_CH1 0x1904 -#define STM32F746_PB9_FUNC_I2C1_SDA 0x1905 -#define STM32F746_PB9_FUNC_SPI2_NSS_I2S2_WS 0x1906 -#define STM32F746_PB9_FUNC_CAN1_TX 0x190a -#define STM32F746_PB9_FUNC_SDMMC1_D5 0x190d -#define STM32F746_PB9_FUNC_DCMI_D7 0x190e -#define STM32F746_PB9_FUNC_LCD_B7 0x190f -#define STM32F746_PB9_FUNC_EVENTOUT 0x1910 -#define STM32F746_PB9_FUNC_ANALOG 0x1911 - -#define STM32F746_PB10_FUNC_GPIO 0x1a00 -#define STM32F746_PB10_FUNC_TIM2_CH3 0x1a02 -#define STM32F746_PB10_FUNC_I2C2_SCL 0x1a05 -#define STM32F746_PB10_FUNC_SPI2_SCK_I2S2_CK 0x1a06 -#define STM32F746_PB10_FUNC_USART3_TX 0x1a08 -#define STM32F746_PB10_FUNC_OTG_HS_ULPI_D3 0x1a0b -#define STM32F746_PB10_FUNC_ETH_MII_RX_ER 0x1a0c -#define STM32F746_PB10_FUNC_LCD_G4 0x1a0f -#define STM32F746_PB10_FUNC_EVENTOUT 0x1a10 -#define STM32F746_PB10_FUNC_ANALOG 0x1a11 - -#define STM32F746_PB11_FUNC_GPIO 0x1b00 -#define STM32F746_PB11_FUNC_TIM2_CH4 0x1b02 -#define STM32F746_PB11_FUNC_I2C2_SDA 0x1b05 -#define STM32F746_PB11_FUNC_USART3_RX 0x1b08 -#define STM32F746_PB11_FUNC_OTG_HS_ULPI_D4 0x1b0b -#define STM32F746_PB11_FUNC_ETH_MII_TX_EN_ETH_RMII_TX_EN 0x1b0c -#define STM32F746_PB11_FUNC_LCD_G5 0x1b0f -#define STM32F746_PB11_FUNC_EVENTOUT 0x1b10 -#define STM32F746_PB11_FUNC_ANALOG 0x1b11 - -#define STM32F746_PB12_FUNC_GPIO 0x1c00 -#define STM32F746_PB12_FUNC_TIM1_BKIN 0x1c02 -#define STM32F746_PB12_FUNC_I2C2_SMBA 0x1c05 -#define STM32F746_PB12_FUNC_SPI2_NSS_I2S2_WS 0x1c06 -#define STM32F746_PB12_FUNC_USART3_CK 0x1c08 -#define STM32F746_PB12_FUNC_CAN2_RX 0x1c0a -#define STM32F746_PB12_FUNC_OTG_HS_ULPI_D5 0x1c0b -#define STM32F746_PB12_FUNC_ETH_MII_TXD0_ETH_RMII_TXD0 0x1c0c -#define STM32F746_PB12_FUNC_OTG_HS_ID 0x1c0d -#define STM32F746_PB12_FUNC_EVENTOUT 0x1c10 -#define STM32F746_PB12_FUNC_ANALOG 0x1c11 - -#define STM32F746_PB13_FUNC_GPIO 0x1d00 -#define STM32F746_PB13_FUNC_TIM1_CH1N 0x1d02 -#define STM32F746_PB13_FUNC_SPI2_SCK_I2S2_CK 0x1d06 -#define STM32F746_PB13_FUNC_USART3_CTS 0x1d08 -#define STM32F746_PB13_FUNC_CAN2_TX 0x1d0a -#define STM32F746_PB13_FUNC_OTG_HS_ULPI_D6 0x1d0b -#define STM32F746_PB13_FUNC_ETH_MII_TXD1_ETH_RMII_TXD1 0x1d0c -#define STM32F746_PB13_FUNC_EVENTOUT 0x1d10 -#define STM32F746_PB13_FUNC_ANALOG 0x1d11 - -#define STM32F746_PB14_FUNC_GPIO 0x1e00 -#define STM32F746_PB14_FUNC_TIM1_CH2N 0x1e02 -#define STM32F746_PB14_FUNC_TIM8_CH2N 0x1e04 -#define STM32F746_PB14_FUNC_SPI2_MISO 0x1e06 -#define STM32F746_PB14_FUNC_USART3_RTS 0x1e08 -#define STM32F746_PB14_FUNC_TIM12_CH1 0x1e0a -#define STM32F746_PB14_FUNC_OTG_HS_DM 0x1e0d -#define STM32F746_PB14_FUNC_EVENTOUT 0x1e10 -#define STM32F746_PB14_FUNC_ANALOG 0x1e11 - -#define STM32F746_PB15_FUNC_GPIO 0x1f00 -#define STM32F746_PB15_FUNC_RTC_REFIN 0x1f01 -#define STM32F746_PB15_FUNC_TIM1_CH3N 0x1f02 -#define STM32F746_PB15_FUNC_TIM8_CH3N 0x1f04 -#define STM32F746_PB15_FUNC_SPI2_MOSI_I2S2_SD 0x1f06 -#define STM32F746_PB15_FUNC_TIM12_CH2 0x1f0a -#define STM32F746_PB15_FUNC_OTG_HS_DP 0x1f0d -#define STM32F746_PB15_FUNC_EVENTOUT 0x1f10 -#define STM32F746_PB15_FUNC_ANALOG 0x1f11 - - -#define STM32F746_PC0_FUNC_GPIO 0x2000 -#define STM32F746_PC0_FUNC_SAI2_FS_B 0x2009 -#define STM32F746_PC0_FUNC_OTG_HS_ULPI_STP 0x200b -#define STM32F746_PC0_FUNC_FMC_SDNWE 0x200d -#define STM32F746_PC0_FUNC_LCD_R5 0x200f -#define STM32F746_PC0_FUNC_EVENTOUT 0x2010 -#define STM32F746_PC0_FUNC_ANALOG 0x2011 - -#define STM32F746_PC1_FUNC_GPIO 0x2100 -#define STM32F746_PC1_FUNC_TRACED0 0x2101 -#define STM32F746_PC1_FUNC_SPI2_MOSI_I2S2_SD 0x2106 -#define STM32F746_PC1_FUNC_SAI1_SD_A 0x2107 -#define STM32F746_PC1_FUNC_ETH_MDC 0x210c -#define STM32F746_PC1_FUNC_EVENTOUT 0x2110 -#define STM32F746_PC1_FUNC_ANALOG 0x2111 - -#define STM32F746_PC2_FUNC_GPIO 0x2200 -#define STM32F746_PC2_FUNC_SPI2_MISO 0x2206 -#define STM32F746_PC2_FUNC_OTG_HS_ULPI_DIR 0x220b -#define STM32F746_PC2_FUNC_ETH_MII_TXD2 0x220c -#define STM32F746_PC2_FUNC_FMC_SDNE0 0x220d -#define STM32F746_PC2_FUNC_EVENTOUT 0x2210 -#define STM32F746_PC2_FUNC_ANALOG 0x2211 - -#define STM32F746_PC3_FUNC_GPIO 0x2300 -#define STM32F746_PC3_FUNC_SPI2_MOSI_I2S2_SD 0x2306 -#define STM32F746_PC3_FUNC_OTG_HS_ULPI_NXT 0x230b -#define STM32F746_PC3_FUNC_ETH_MII_TX_CLK 0x230c -#define STM32F746_PC3_FUNC_FMC_SDCKE0 0x230d -#define STM32F746_PC3_FUNC_EVENTOUT 0x2310 -#define STM32F746_PC3_FUNC_ANALOG 0x2311 - -#define STM32F746_PC4_FUNC_GPIO 0x2400 -#define STM32F746_PC4_FUNC_I2S1_MCK 0x2406 -#define STM32F746_PC4_FUNC_SPDIFRX_IN2 0x2409 -#define STM32F746_PC4_FUNC_ETH_MII_RXD0_ETH_RMII_RXD0 0x240c -#define STM32F746_PC4_FUNC_FMC_SDNE0 0x240d -#define STM32F746_PC4_FUNC_EVENTOUT 0x2410 -#define STM32F746_PC4_FUNC_ANALOG 0x2411 - -#define STM32F746_PC5_FUNC_GPIO 0x2500 -#define STM32F746_PC5_FUNC_SPDIFRX_IN3 0x2509 -#define STM32F746_PC5_FUNC_ETH_MII_RXD1_ETH_RMII_RXD1 0x250c -#define STM32F746_PC5_FUNC_FMC_SDCKE0 0x250d -#define STM32F746_PC5_FUNC_EVENTOUT 0x2510 -#define STM32F746_PC5_FUNC_ANALOG 0x2511 - -#define STM32F746_PC6_FUNC_GPIO 0x2600 -#define STM32F746_PC6_FUNC_TIM3_CH1 0x2603 -#define STM32F746_PC6_FUNC_TIM8_CH1 0x2604 -#define STM32F746_PC6_FUNC_I2S2_MCK 0x2606 -#define STM32F746_PC6_FUNC_USART6_TX 0x2609 -#define STM32F746_PC6_FUNC_SDMMC1_D6 0x260d -#define STM32F746_PC6_FUNC_DCMI_D0 0x260e -#define STM32F746_PC6_FUNC_LCD_HSYNC 0x260f -#define STM32F746_PC6_FUNC_EVENTOUT 0x2610 -#define STM32F746_PC6_FUNC_ANALOG 0x2611 - -#define STM32F746_PC7_FUNC_GPIO 0x2700 -#define STM32F746_PC7_FUNC_TIM3_CH2 0x2703 -#define STM32F746_PC7_FUNC_TIM8_CH2 0x2704 -#define STM32F746_PC7_FUNC_I2S3_MCK 0x2707 -#define STM32F746_PC7_FUNC_USART6_RX 0x2709 -#define STM32F746_PC7_FUNC_SDMMC1_D7 0x270d -#define STM32F746_PC7_FUNC_DCMI_D1 0x270e -#define STM32F746_PC7_FUNC_LCD_G6 0x270f -#define STM32F746_PC7_FUNC_EVENTOUT 0x2710 -#define STM32F746_PC7_FUNC_ANALOG 0x2711 - -#define STM32F746_PC8_FUNC_GPIO 0x2800 -#define STM32F746_PC8_FUNC_TRACED1 0x2801 -#define STM32F746_PC8_FUNC_TIM3_CH3 0x2803 -#define STM32F746_PC8_FUNC_TIM8_CH3 0x2804 -#define STM32F746_PC8_FUNC_UART5_RTS 0x2808 -#define STM32F746_PC8_FUNC_USART6_CK 0x2809 -#define STM32F746_PC8_FUNC_SDMMC1_D0 0x280d -#define STM32F746_PC8_FUNC_DCMI_D2 0x280e -#define STM32F746_PC8_FUNC_EVENTOUT 0x2810 -#define STM32F746_PC8_FUNC_ANALOG 0x2811 - -#define STM32F746_PC9_FUNC_GPIO 0x2900 -#define STM32F746_PC9_FUNC_MCO2 0x2901 -#define STM32F746_PC9_FUNC_TIM3_CH4 0x2903 -#define STM32F746_PC9_FUNC_TIM8_CH4 0x2904 -#define STM32F746_PC9_FUNC_I2C3_SDA 0x2905 -#define STM32F746_PC9_FUNC_I2S_CKIN 0x2906 -#define STM32F746_PC9_FUNC_UART5_CTS 0x2908 -#define STM32F746_PC9_FUNC_QUADSPI_BK1_IO0 0x290a -#define STM32F746_PC9_FUNC_SDMMC1_D1 0x290d -#define STM32F746_PC9_FUNC_DCMI_D3 0x290e -#define STM32F746_PC9_FUNC_EVENTOUT 0x2910 -#define STM32F746_PC9_FUNC_ANALOG 0x2911 - -#define STM32F746_PC10_FUNC_GPIO 0x2a00 -#define STM32F746_PC10_FUNC_SPI3_SCK_I2S3_CK 0x2a07 -#define STM32F746_PC10_FUNC_USART3_TX 0x2a08 -#define STM32F746_PC10_FUNC_UART4_TX 0x2a09 -#define STM32F746_PC10_FUNC_QUADSPI_BK1_IO1 0x2a0a -#define STM32F746_PC10_FUNC_SDMMC1_D2 0x2a0d -#define STM32F746_PC10_FUNC_DCMI_D8 0x2a0e -#define STM32F746_PC10_FUNC_LCD_R2 0x2a0f -#define STM32F746_PC10_FUNC_EVENTOUT 0x2a10 -#define STM32F746_PC10_FUNC_ANALOG 0x2a11 - -#define STM32F746_PC11_FUNC_GPIO 0x2b00 -#define STM32F746_PC11_FUNC_SPI3_MISO 0x2b07 -#define STM32F746_PC11_FUNC_USART3_RX 0x2b08 -#define STM32F746_PC11_FUNC_UART4_RX 0x2b09 -#define STM32F746_PC11_FUNC_QUADSPI_BK2_NCS 0x2b0a -#define STM32F746_PC11_FUNC_SDMMC1_D3 0x2b0d -#define STM32F746_PC11_FUNC_DCMI_D4 0x2b0e -#define STM32F746_PC11_FUNC_EVENTOUT 0x2b10 -#define STM32F746_PC11_FUNC_ANALOG 0x2b11 - -#define STM32F746_PC12_FUNC_GPIO 0x2c00 -#define STM32F746_PC12_FUNC_TRACED3 0x2c01 -#define STM32F746_PC12_FUNC_SPI3_MOSI_I2S3_SD 0x2c07 -#define STM32F746_PC12_FUNC_USART3_CK 0x2c08 -#define STM32F746_PC12_FUNC_UART5_TX 0x2c09 -#define STM32F746_PC12_FUNC_SDMMC1_CK 0x2c0d -#define STM32F746_PC12_FUNC_DCMI_D9 0x2c0e -#define STM32F746_PC12_FUNC_EVENTOUT 0x2c10 -#define STM32F746_PC12_FUNC_ANALOG 0x2c11 - -#define STM32F746_PC13_FUNC_GPIO 0x2d00 -#define STM32F746_PC13_FUNC_EVENTOUT 0x2d10 -#define STM32F746_PC13_FUNC_ANALOG 0x2d11 - -#define STM32F746_PC14_FUNC_GPIO 0x2e00 -#define STM32F746_PC14_FUNC_EVENTOUT 0x2e10 -#define STM32F746_PC14_FUNC_ANALOG 0x2e11 - -#define STM32F746_PC15_FUNC_GPIO 0x2f00 -#define STM32F746_PC15_FUNC_EVENTOUT 0x2f10 -#define STM32F746_PC15_FUNC_ANALOG 0x2f11 - - -#define STM32F746_PD0_FUNC_GPIO 0x3000 -#define STM32F746_PD0_FUNC_CAN1_RX 0x300a -#define STM32F746_PD0_FUNC_FMC_D2 0x300d -#define STM32F746_PD0_FUNC_EVENTOUT 0x3010 -#define STM32F746_PD0_FUNC_ANALOG 0x3011 - -#define STM32F746_PD1_FUNC_GPIO 0x3100 -#define STM32F746_PD1_FUNC_CAN1_TX 0x310a -#define STM32F746_PD1_FUNC_FMC_D3 0x310d -#define STM32F746_PD1_FUNC_EVENTOUT 0x3110 -#define STM32F746_PD1_FUNC_ANALOG 0x3111 - -#define STM32F746_PD2_FUNC_GPIO 0x3200 -#define STM32F746_PD2_FUNC_TRACED2 0x3201 -#define STM32F746_PD2_FUNC_TIM3_ETR 0x3203 -#define STM32F746_PD2_FUNC_UART5_RX 0x3209 -#define STM32F746_PD2_FUNC_SDMMC1_CMD 0x320d -#define STM32F746_PD2_FUNC_DCMI_D11 0x320e -#define STM32F746_PD2_FUNC_EVENTOUT 0x3210 -#define STM32F746_PD2_FUNC_ANALOG 0x3211 - -#define STM32F746_PD3_FUNC_GPIO 0x3300 -#define STM32F746_PD3_FUNC_SPI2_SCK_I2S2_CK 0x3306 -#define STM32F746_PD3_FUNC_USART2_CTS 0x3308 -#define STM32F746_PD3_FUNC_FMC_CLK 0x330d -#define STM32F746_PD3_FUNC_DCMI_D5 0x330e -#define STM32F746_PD3_FUNC_LCD_G7 0x330f -#define STM32F746_PD3_FUNC_EVENTOUT 0x3310 -#define STM32F746_PD3_FUNC_ANALOG 0x3311 - -#define STM32F746_PD4_FUNC_GPIO 0x3400 -#define STM32F746_PD4_FUNC_USART2_RTS 0x3408 -#define STM32F746_PD4_FUNC_FMC_NOE 0x340d -#define STM32F746_PD4_FUNC_EVENTOUT 0x3410 -#define STM32F746_PD4_FUNC_ANALOG 0x3411 - -#define STM32F746_PD5_FUNC_GPIO 0x3500 -#define STM32F746_PD5_FUNC_USART2_TX 0x3508 -#define STM32F746_PD5_FUNC_FMC_NWE 0x350d -#define STM32F746_PD5_FUNC_EVENTOUT 0x3510 -#define STM32F746_PD5_FUNC_ANALOG 0x3511 - -#define STM32F746_PD6_FUNC_GPIO 0x3600 -#define STM32F746_PD6_FUNC_SPI3_MOSI_I2S3_SD 0x3606 -#define STM32F746_PD6_FUNC_SAI1_SD_A 0x3607 -#define STM32F746_PD6_FUNC_USART2_RX 0x3608 -#define STM32F746_PD6_FUNC_FMC_NWAIT 0x360d -#define STM32F746_PD6_FUNC_DCMI_D10 0x360e -#define STM32F746_PD6_FUNC_LCD_B2 0x360f -#define STM32F746_PD6_FUNC_EVENTOUT 0x3610 -#define STM32F746_PD6_FUNC_ANALOG 0x3611 - -#define STM32F746_PD7_FUNC_GPIO 0x3700 -#define STM32F746_PD7_FUNC_USART2_CK 0x3708 -#define STM32F746_PD7_FUNC_SPDIFRX_IN0 0x3709 -#define STM32F746_PD7_FUNC_FMC_NE1 0x370d -#define STM32F746_PD7_FUNC_EVENTOUT 0x3710 -#define STM32F746_PD7_FUNC_ANALOG 0x3711 - -#define STM32F746_PD8_FUNC_GPIO 0x3800 -#define STM32F746_PD8_FUNC_USART3_TX 0x3808 -#define STM32F746_PD8_FUNC_SPDIFRX_IN1 0x3809 -#define STM32F746_PD8_FUNC_FMC_D13 0x380d -#define STM32F746_PD8_FUNC_EVENTOUT 0x3810 -#define STM32F746_PD8_FUNC_ANALOG 0x3811 - -#define STM32F746_PD9_FUNC_GPIO 0x3900 -#define STM32F746_PD9_FUNC_USART3_RX 0x3908 -#define STM32F746_PD9_FUNC_FMC_D14 0x390d -#define STM32F746_PD9_FUNC_EVENTOUT 0x3910 -#define STM32F746_PD9_FUNC_ANALOG 0x3911 - -#define STM32F746_PD10_FUNC_GPIO 0x3a00 -#define STM32F746_PD10_FUNC_USART3_CK 0x3a08 -#define STM32F746_PD10_FUNC_FMC_D15 0x3a0d -#define STM32F746_PD10_FUNC_LCD_B3 0x3a0f -#define STM32F746_PD10_FUNC_EVENTOUT 0x3a10 -#define STM32F746_PD10_FUNC_ANALOG 0x3a11 - -#define STM32F746_PD11_FUNC_GPIO 0x3b00 -#define STM32F746_PD11_FUNC_I2C4_SMBA 0x3b05 -#define STM32F746_PD11_FUNC_USART3_CTS 0x3b08 -#define STM32F746_PD11_FUNC_QUADSPI_BK1_IO0 0x3b0a -#define STM32F746_PD11_FUNC_SAI2_SD_A 0x3b0b -#define STM32F746_PD11_FUNC_FMC_A16_FMC_CLE 0x3b0d -#define STM32F746_PD11_FUNC_EVENTOUT 0x3b10 -#define STM32F746_PD11_FUNC_ANALOG 0x3b11 - -#define STM32F746_PD12_FUNC_GPIO 0x3c00 -#define STM32F746_PD12_FUNC_TIM4_CH1 0x3c03 -#define STM32F746_PD12_FUNC_LPTIM1_IN1 0x3c04 -#define STM32F746_PD12_FUNC_I2C4_SCL 0x3c05 -#define STM32F746_PD12_FUNC_USART3_RTS 0x3c08 -#define STM32F746_PD12_FUNC_QUADSPI_BK1_IO1 0x3c0a -#define STM32F746_PD12_FUNC_SAI2_FS_A 0x3c0b -#define STM32F746_PD12_FUNC_FMC_A17_FMC_ALE 0x3c0d -#define STM32F746_PD12_FUNC_EVENTOUT 0x3c10 -#define STM32F746_PD12_FUNC_ANALOG 0x3c11 - -#define STM32F746_PD13_FUNC_GPIO 0x3d00 -#define STM32F746_PD13_FUNC_TIM4_CH2 0x3d03 -#define STM32F746_PD13_FUNC_LPTIM1_OUT 0x3d04 -#define STM32F746_PD13_FUNC_I2C4_SDA 0x3d05 -#define STM32F746_PD13_FUNC_QUADSPI_BK1_IO3 0x3d0a -#define STM32F746_PD13_FUNC_SAI2_SCK_A 0x3d0b -#define STM32F746_PD13_FUNC_FMC_A18 0x3d0d -#define STM32F746_PD13_FUNC_EVENTOUT 0x3d10 -#define STM32F746_PD13_FUNC_ANALOG 0x3d11 - -#define STM32F746_PD14_FUNC_GPIO 0x3e00 -#define STM32F746_PD14_FUNC_TIM4_CH3 0x3e03 -#define STM32F746_PD14_FUNC_UART8_CTS 0x3e09 -#define STM32F746_PD14_FUNC_FMC_D0 0x3e0d -#define STM32F746_PD14_FUNC_EVENTOUT 0x3e10 -#define STM32F746_PD14_FUNC_ANALOG 0x3e11 - -#define STM32F746_PD15_FUNC_GPIO 0x3f00 -#define STM32F746_PD15_FUNC_TIM4_CH4 0x3f03 -#define STM32F746_PD15_FUNC_UART8_RTS 0x3f09 -#define STM32F746_PD15_FUNC_FMC_D1 0x3f0d -#define STM32F746_PD15_FUNC_EVENTOUT 0x3f10 -#define STM32F746_PD15_FUNC_ANALOG 0x3f11 - - -#define STM32F746_PE0_FUNC_GPIO 0x4000 -#define STM32F746_PE0_FUNC_TIM4_ETR 0x4003 -#define STM32F746_PE0_FUNC_LPTIM1_ETR 0x4004 -#define STM32F746_PE0_FUNC_UART8_RX 0x4009 -#define STM32F746_PE0_FUNC_SAI2_MCLK_A 0x400b -#define STM32F746_PE0_FUNC_FMC_NBL0 0x400d -#define STM32F746_PE0_FUNC_DCMI_D2 0x400e -#define STM32F746_PE0_FUNC_EVENTOUT 0x4010 -#define STM32F746_PE0_FUNC_ANALOG 0x4011 - -#define STM32F746_PE1_FUNC_GPIO 0x4100 -#define STM32F746_PE1_FUNC_LPTIM1_IN2 0x4104 -#define STM32F746_PE1_FUNC_UART8_TX 0x4109 -#define STM32F746_PE1_FUNC_FMC_NBL1 0x410d -#define STM32F746_PE1_FUNC_DCMI_D3 0x410e -#define STM32F746_PE1_FUNC_EVENTOUT 0x4110 -#define STM32F746_PE1_FUNC_ANALOG 0x4111 - -#define STM32F746_PE2_FUNC_GPIO 0x4200 -#define STM32F746_PE2_FUNC_TRACECLK 0x4201 -#define STM32F746_PE2_FUNC_SPI4_SCK 0x4206 -#define STM32F746_PE2_FUNC_SAI1_MCLK_A 0x4207 -#define STM32F746_PE2_FUNC_QUADSPI_BK1_IO2 0x420a -#define STM32F746_PE2_FUNC_ETH_MII_TXD3 0x420c -#define STM32F746_PE2_FUNC_FMC_A23 0x420d -#define STM32F746_PE2_FUNC_EVENTOUT 0x4210 -#define STM32F746_PE2_FUNC_ANALOG 0x4211 - -#define STM32F746_PE3_FUNC_GPIO 0x4300 -#define STM32F746_PE3_FUNC_TRACED0 0x4301 -#define STM32F746_PE3_FUNC_SAI1_SD_B 0x4307 -#define STM32F746_PE3_FUNC_FMC_A19 0x430d -#define STM32F746_PE3_FUNC_EVENTOUT 0x4310 -#define STM32F746_PE3_FUNC_ANALOG 0x4311 - -#define STM32F746_PE4_FUNC_GPIO 0x4400 -#define STM32F746_PE4_FUNC_TRACED1 0x4401 -#define STM32F746_PE4_FUNC_SPI4_NSS 0x4406 -#define STM32F746_PE4_FUNC_SAI1_FS_A 0x4407 -#define STM32F746_PE4_FUNC_FMC_A20 0x440d -#define STM32F746_PE4_FUNC_DCMI_D4 0x440e -#define STM32F746_PE4_FUNC_LCD_B0 0x440f -#define STM32F746_PE4_FUNC_EVENTOUT 0x4410 -#define STM32F746_PE4_FUNC_ANALOG 0x4411 - -#define STM32F746_PE5_FUNC_GPIO 0x4500 -#define STM32F746_PE5_FUNC_TRACED2 0x4501 -#define STM32F746_PE5_FUNC_TIM9_CH1 0x4504 -#define STM32F746_PE5_FUNC_SPI4_MISO 0x4506 -#define STM32F746_PE5_FUNC_SAI1_SCK_A 0x4507 -#define STM32F746_PE5_FUNC_FMC_A21 0x450d -#define STM32F746_PE5_FUNC_DCMI_D6 0x450e -#define STM32F746_PE5_FUNC_LCD_G0 0x450f -#define STM32F746_PE5_FUNC_EVENTOUT 0x4510 -#define STM32F746_PE5_FUNC_ANALOG 0x4511 - -#define STM32F746_PE6_FUNC_GPIO 0x4600 -#define STM32F746_PE6_FUNC_TRACED3 0x4601 -#define STM32F746_PE6_FUNC_TIM1_BKIN2 0x4602 -#define STM32F746_PE6_FUNC_TIM9_CH2 0x4604 -#define STM32F746_PE6_FUNC_SPI4_MOSI 0x4606 -#define STM32F746_PE6_FUNC_SAI1_SD_A 0x4607 -#define STM32F746_PE6_FUNC_SAI2_MCLK_B 0x460b -#define STM32F746_PE6_FUNC_FMC_A22 0x460d -#define STM32F746_PE6_FUNC_DCMI_D7 0x460e -#define STM32F746_PE6_FUNC_LCD_G1 0x460f -#define STM32F746_PE6_FUNC_EVENTOUT 0x4610 -#define STM32F746_PE6_FUNC_ANALOG 0x4611 - -#define STM32F746_PE7_FUNC_GPIO 0x4700 -#define STM32F746_PE7_FUNC_TIM1_ETR 0x4702 -#define STM32F746_PE7_FUNC_UART7_RX 0x4709 -#define STM32F746_PE7_FUNC_QUADSPI_BK2_IO0 0x470b -#define STM32F746_PE7_FUNC_FMC_D4 0x470d -#define STM32F746_PE7_FUNC_EVENTOUT 0x4710 -#define STM32F746_PE7_FUNC_ANALOG 0x4711 - -#define STM32F746_PE8_FUNC_GPIO 0x4800 -#define STM32F746_PE8_FUNC_TIM1_CH1N 0x4802 -#define STM32F746_PE8_FUNC_UART7_TX 0x4809 -#define STM32F746_PE8_FUNC_QUADSPI_BK2_IO1 0x480b -#define STM32F746_PE8_FUNC_FMC_D5 0x480d -#define STM32F746_PE8_FUNC_EVENTOUT 0x4810 -#define STM32F746_PE8_FUNC_ANALOG 0x4811 - -#define STM32F746_PE9_FUNC_GPIO 0x4900 -#define STM32F746_PE9_FUNC_TIM1_CH1 0x4902 -#define STM32F746_PE9_FUNC_UART7_RTS 0x4909 -#define STM32F746_PE9_FUNC_QUADSPI_BK2_IO2 0x490b -#define STM32F746_PE9_FUNC_FMC_D6 0x490d -#define STM32F746_PE9_FUNC_EVENTOUT 0x4910 -#define STM32F746_PE9_FUNC_ANALOG 0x4911 - -#define STM32F746_PE10_FUNC_GPIO 0x4a00 -#define STM32F746_PE10_FUNC_TIM1_CH2N 0x4a02 -#define STM32F746_PE10_FUNC_UART7_CTS 0x4a09 -#define STM32F746_PE10_FUNC_QUADSPI_BK2_IO3 0x4a0b -#define STM32F746_PE10_FUNC_FMC_D7 0x4a0d -#define STM32F746_PE10_FUNC_EVENTOUT 0x4a10 -#define STM32F746_PE10_FUNC_ANALOG 0x4a11 - -#define STM32F746_PE11_FUNC_GPIO 0x4b00 -#define STM32F746_PE11_FUNC_TIM1_CH2 0x4b02 -#define STM32F746_PE11_FUNC_SPI4_NSS 0x4b06 -#define STM32F746_PE11_FUNC_SAI2_SD_B 0x4b0b -#define STM32F746_PE11_FUNC_FMC_D8 0x4b0d -#define STM32F746_PE11_FUNC_LCD_G3 0x4b0f -#define STM32F746_PE11_FUNC_EVENTOUT 0x4b10 -#define STM32F746_PE11_FUNC_ANALOG 0x4b11 - -#define STM32F746_PE12_FUNC_GPIO 0x4c00 -#define STM32F746_PE12_FUNC_TIM1_CH3N 0x4c02 -#define STM32F746_PE12_FUNC_SPI4_SCK 0x4c06 -#define STM32F746_PE12_FUNC_SAI2_SCK_B 0x4c0b -#define STM32F746_PE12_FUNC_FMC_D9 0x4c0d -#define STM32F746_PE12_FUNC_LCD_B4 0x4c0f -#define STM32F746_PE12_FUNC_EVENTOUT 0x4c10 -#define STM32F746_PE12_FUNC_ANALOG 0x4c11 - -#define STM32F746_PE13_FUNC_GPIO 0x4d00 -#define STM32F746_PE13_FUNC_TIM1_CH3 0x4d02 -#define STM32F746_PE13_FUNC_SPI4_MISO 0x4d06 -#define STM32F746_PE13_FUNC_SAI2_FS_B 0x4d0b -#define STM32F746_PE13_FUNC_FMC_D10 0x4d0d -#define STM32F746_PE13_FUNC_LCD_DE 0x4d0f -#define STM32F746_PE13_FUNC_EVENTOUT 0x4d10 -#define STM32F746_PE13_FUNC_ANALOG 0x4d11 - -#define STM32F746_PE14_FUNC_GPIO 0x4e00 -#define STM32F746_PE14_FUNC_TIM1_CH4 0x4e02 -#define STM32F746_PE14_FUNC_SPI4_MOSI 0x4e06 -#define STM32F746_PE14_FUNC_SAI2_MCLK_B 0x4e0b -#define STM32F746_PE14_FUNC_FMC_D11 0x4e0d -#define STM32F746_PE14_FUNC_LCD_CLK 0x4e0f -#define STM32F746_PE14_FUNC_EVENTOUT 0x4e10 -#define STM32F746_PE14_FUNC_ANALOG 0x4e11 - -#define STM32F746_PE15_FUNC_GPIO 0x4f00 -#define STM32F746_PE15_FUNC_TIM1_BKIN 0x4f02 -#define STM32F746_PE15_FUNC_FMC_D12 0x4f0d -#define STM32F746_PE15_FUNC_LCD_R7 0x4f0f -#define STM32F746_PE15_FUNC_EVENTOUT 0x4f10 -#define STM32F746_PE15_FUNC_ANALOG 0x4f11 - - -#define STM32F746_PF0_FUNC_GPIO 0x5000 -#define STM32F746_PF0_FUNC_I2C2_SDA 0x5005 -#define STM32F746_PF0_FUNC_FMC_A0 0x500d -#define STM32F746_PF0_FUNC_EVENTOUT 0x5010 -#define STM32F746_PF0_FUNC_ANALOG 0x5011 - -#define STM32F746_PF1_FUNC_GPIO 0x5100 -#define STM32F746_PF1_FUNC_I2C2_SCL 0x5105 -#define STM32F746_PF1_FUNC_FMC_A1 0x510d -#define STM32F746_PF1_FUNC_EVENTOUT 0x5110 -#define STM32F746_PF1_FUNC_ANALOG 0x5111 - -#define STM32F746_PF2_FUNC_GPIO 0x5200 -#define STM32F746_PF2_FUNC_I2C2_SMBA 0x5205 -#define STM32F746_PF2_FUNC_FMC_A2 0x520d -#define STM32F746_PF2_FUNC_EVENTOUT 0x5210 -#define STM32F746_PF2_FUNC_ANALOG 0x5211 - -#define STM32F746_PF3_FUNC_GPIO 0x5300 -#define STM32F746_PF3_FUNC_FMC_A3 0x530d -#define STM32F746_PF3_FUNC_EVENTOUT 0x5310 -#define STM32F746_PF3_FUNC_ANALOG 0x5311 - -#define STM32F746_PF4_FUNC_GPIO 0x5400 -#define STM32F746_PF4_FUNC_FMC_A4 0x540d -#define STM32F746_PF4_FUNC_EVENTOUT 0x5410 -#define STM32F746_PF4_FUNC_ANALOG 0x5411 - -#define STM32F746_PF5_FUNC_GPIO 0x5500 -#define STM32F746_PF5_FUNC_FMC_A5 0x550d -#define STM32F746_PF5_FUNC_EVENTOUT 0x5510 -#define STM32F746_PF5_FUNC_ANALOG 0x5511 - -#define STM32F746_PF6_FUNC_GPIO 0x5600 -#define STM32F746_PF6_FUNC_TIM10_CH1 0x5604 -#define STM32F746_PF6_FUNC_SPI5_NSS 0x5606 -#define STM32F746_PF6_FUNC_SAI1_SD_B 0x5607 -#define STM32F746_PF6_FUNC_UART7_RX 0x5609 -#define STM32F746_PF6_FUNC_QUADSPI_BK1_IO3 0x560a -#define STM32F746_PF6_FUNC_EVENTOUT 0x5610 -#define STM32F746_PF6_FUNC_ANALOG 0x5611 - -#define STM32F746_PF7_FUNC_GPIO 0x5700 -#define STM32F746_PF7_FUNC_TIM11_CH1 0x5704 -#define STM32F746_PF7_FUNC_SPI5_SCK 0x5706 -#define STM32F746_PF7_FUNC_SAI1_MCLK_B 0x5707 -#define STM32F746_PF7_FUNC_UART7_TX 0x5709 -#define STM32F746_PF7_FUNC_QUADSPI_BK1_IO2 0x570a -#define STM32F746_PF7_FUNC_EVENTOUT 0x5710 -#define STM32F746_PF7_FUNC_ANALOG 0x5711 - -#define STM32F746_PF8_FUNC_GPIO 0x5800 -#define STM32F746_PF8_FUNC_SPI5_MISO 0x5806 -#define STM32F746_PF8_FUNC_SAI1_SCK_B 0x5807 -#define STM32F746_PF8_FUNC_UART7_RTS 0x5809 -#define STM32F746_PF8_FUNC_TIM13_CH1 0x580a -#define STM32F746_PF8_FUNC_QUADSPI_BK1_IO0 0x580b -#define STM32F746_PF8_FUNC_EVENTOUT 0x5810 -#define STM32F746_PF8_FUNC_ANALOG 0x5811 - -#define STM32F746_PF9_FUNC_GPIO 0x5900 -#define STM32F746_PF9_FUNC_SPI5_MOSI 0x5906 -#define STM32F746_PF9_FUNC_SAI1_FS_B 0x5907 -#define STM32F746_PF9_FUNC_UART7_CTS 0x5909 -#define STM32F746_PF9_FUNC_TIM14_CH1 0x590a -#define STM32F746_PF9_FUNC_QUADSPI_BK1_IO1 0x590b -#define STM32F746_PF9_FUNC_EVENTOUT 0x5910 -#define STM32F746_PF9_FUNC_ANALOG 0x5911 - -#define STM32F746_PF10_FUNC_GPIO 0x5a00 -#define STM32F746_PF10_FUNC_DCMI_D11 0x5a0e -#define STM32F746_PF10_FUNC_LCD_DE 0x5a0f -#define STM32F746_PF10_FUNC_EVENTOUT 0x5a10 -#define STM32F746_PF10_FUNC_ANALOG 0x5a11 - -#define STM32F746_PF11_FUNC_GPIO 0x5b00 -#define STM32F746_PF11_FUNC_SPI5_MOSI 0x5b06 -#define STM32F746_PF11_FUNC_SAI2_SD_B 0x5b0b -#define STM32F746_PF11_FUNC_FMC_SDNRAS 0x5b0d -#define STM32F746_PF11_FUNC_DCMI_D12 0x5b0e -#define STM32F746_PF11_FUNC_EVENTOUT 0x5b10 -#define STM32F746_PF11_FUNC_ANALOG 0x5b11 - -#define STM32F746_PF12_FUNC_GPIO 0x5c00 -#define STM32F746_PF12_FUNC_FMC_A6 0x5c0d -#define STM32F746_PF12_FUNC_EVENTOUT 0x5c10 -#define STM32F746_PF12_FUNC_ANALOG 0x5c11 - -#define STM32F746_PF13_FUNC_GPIO 0x5d00 -#define STM32F746_PF13_FUNC_I2C4_SMBA 0x5d05 -#define STM32F746_PF13_FUNC_FMC_A7 0x5d0d -#define STM32F746_PF13_FUNC_EVENTOUT 0x5d10 -#define STM32F746_PF13_FUNC_ANALOG 0x5d11 - -#define STM32F746_PF14_FUNC_GPIO 0x5e00 -#define STM32F746_PF14_FUNC_I2C4_SCL 0x5e05 -#define STM32F746_PF14_FUNC_FMC_A8 0x5e0d -#define STM32F746_PF14_FUNC_EVENTOUT 0x5e10 -#define STM32F746_PF14_FUNC_ANALOG 0x5e11 - -#define STM32F746_PF15_FUNC_GPIO 0x5f00 -#define STM32F746_PF15_FUNC_I2C4_SDA 0x5f05 -#define STM32F746_PF15_FUNC_FMC_A9 0x5f0d -#define STM32F746_PF15_FUNC_EVENTOUT 0x5f10 -#define STM32F746_PF15_FUNC_ANALOG 0x5f11 - - -#define STM32F746_PG0_FUNC_GPIO 0x6000 -#define STM32F746_PG0_FUNC_FMC_A10 0x600d -#define STM32F746_PG0_FUNC_EVENTOUT 0x6010 -#define STM32F746_PG0_FUNC_ANALOG 0x6011 - -#define STM32F746_PG1_FUNC_GPIO 0x6100 -#define STM32F746_PG1_FUNC_FMC_A11 0x610d -#define STM32F746_PG1_FUNC_EVENTOUT 0x6110 -#define STM32F746_PG1_FUNC_ANALOG 0x6111 - -#define STM32F746_PG2_FUNC_GPIO 0x6200 -#define STM32F746_PG2_FUNC_FMC_A12 0x620d -#define STM32F746_PG2_FUNC_EVENTOUT 0x6210 -#define STM32F746_PG2_FUNC_ANALOG 0x6211 - -#define STM32F746_PG3_FUNC_GPIO 0x6300 -#define STM32F746_PG3_FUNC_FMC_A13 0x630d -#define STM32F746_PG3_FUNC_EVENTOUT 0x6310 -#define STM32F746_PG3_FUNC_ANALOG 0x6311 - -#define STM32F746_PG4_FUNC_GPIO 0x6400 -#define STM32F746_PG4_FUNC_FMC_A14_FMC_BA0 0x640d -#define STM32F746_PG4_FUNC_EVENTOUT 0x6410 -#define STM32F746_PG4_FUNC_ANALOG 0x6411 - -#define STM32F746_PG5_FUNC_GPIO 0x6500 -#define STM32F746_PG5_FUNC_FMC_A15_FMC_BA1 0x650d -#define STM32F746_PG5_FUNC_EVENTOUT 0x6510 -#define STM32F746_PG5_FUNC_ANALOG 0x6511 - -#define STM32F746_PG6_FUNC_GPIO 0x6600 -#define STM32F746_PG6_FUNC_DCMI_D12 0x660e -#define STM32F746_PG6_FUNC_LCD_R7 0x660f -#define STM32F746_PG6_FUNC_EVENTOUT 0x6610 -#define STM32F746_PG6_FUNC_ANALOG 0x6611 - -#define STM32F746_PG7_FUNC_GPIO 0x6700 -#define STM32F746_PG7_FUNC_USART6_CK 0x6709 -#define STM32F746_PG7_FUNC_FMC_INT 0x670d -#define STM32F746_PG7_FUNC_DCMI_D13 0x670e -#define STM32F746_PG7_FUNC_LCD_CLK 0x670f -#define STM32F746_PG7_FUNC_EVENTOUT 0x6710 -#define STM32F746_PG7_FUNC_ANALOG 0x6711 - -#define STM32F746_PG8_FUNC_GPIO 0x6800 -#define STM32F746_PG8_FUNC_SPI6_NSS 0x6806 -#define STM32F746_PG8_FUNC_SPDIFRX_IN2 0x6808 -#define STM32F746_PG8_FUNC_USART6_RTS 0x6809 -#define STM32F746_PG8_FUNC_ETH_PPS_OUT 0x680c -#define STM32F746_PG8_FUNC_FMC_SDCLK 0x680d -#define STM32F746_PG8_FUNC_EVENTOUT 0x6810 -#define STM32F746_PG8_FUNC_ANALOG 0x6811 - -#define STM32F746_PG9_FUNC_GPIO 0x6900 -#define STM32F746_PG9_FUNC_SPDIFRX_IN3 0x6908 -#define STM32F746_PG9_FUNC_USART6_RX 0x6909 -#define STM32F746_PG9_FUNC_QUADSPI_BK2_IO2 0x690a -#define STM32F746_PG9_FUNC_SAI2_FS_B 0x690b -#define STM32F746_PG9_FUNC_FMC_NE2_FMC_NCE 0x690d -#define STM32F746_PG9_FUNC_DCMI_VSYNC 0x690e -#define STM32F746_PG9_FUNC_EVENTOUT 0x6910 -#define STM32F746_PG9_FUNC_ANALOG 0x6911 - -#define STM32F746_PG10_FUNC_GPIO 0x6a00 -#define STM32F746_PG10_FUNC_LCD_G3 0x6a0a -#define STM32F746_PG10_FUNC_SAI2_SD_B 0x6a0b -#define STM32F746_PG10_FUNC_FMC_NE3 0x6a0d -#define STM32F746_PG10_FUNC_DCMI_D2 0x6a0e -#define STM32F746_PG10_FUNC_LCD_B2 0x6a0f -#define STM32F746_PG10_FUNC_EVENTOUT 0x6a10 -#define STM32F746_PG10_FUNC_ANALOG 0x6a11 - -#define STM32F746_PG11_FUNC_GPIO 0x6b00 -#define STM32F746_PG11_FUNC_SPDIFRX_IN0 0x6b08 -#define STM32F746_PG11_FUNC_ETH_MII_TX_EN_ETH_RMII_TX_EN 0x6b0c -#define STM32F746_PG11_FUNC_DCMI_D3 0x6b0e -#define STM32F746_PG11_FUNC_LCD_B3 0x6b0f -#define STM32F746_PG11_FUNC_EVENTOUT 0x6b10 -#define STM32F746_PG11_FUNC_ANALOG 0x6b11 - -#define STM32F746_PG12_FUNC_GPIO 0x6c00 -#define STM32F746_PG12_FUNC_LPTIM1_IN1 0x6c04 -#define STM32F746_PG12_FUNC_SPI6_MISO 0x6c06 -#define STM32F746_PG12_FUNC_SPDIFRX_IN1 0x6c08 -#define STM32F746_PG12_FUNC_USART6_RTS 0x6c09 -#define STM32F746_PG12_FUNC_LCD_B4 0x6c0a -#define STM32F746_PG12_FUNC_FMC_NE4 0x6c0d -#define STM32F746_PG12_FUNC_LCD_B1 0x6c0f -#define STM32F746_PG12_FUNC_EVENTOUT 0x6c10 -#define STM32F746_PG12_FUNC_ANALOG 0x6c11 - -#define STM32F746_PG13_FUNC_GPIO 0x6d00 -#define STM32F746_PG13_FUNC_TRACED0 0x6d01 -#define STM32F746_PG13_FUNC_LPTIM1_OUT 0x6d04 -#define STM32F746_PG13_FUNC_SPI6_SCK 0x6d06 -#define STM32F746_PG13_FUNC_USART6_CTS 0x6d09 -#define STM32F746_PG13_FUNC_ETH_MII_TXD0_ETH_RMII_TXD0 0x6d0c -#define STM32F746_PG13_FUNC_FMC_A24 0x6d0d -#define STM32F746_PG13_FUNC_LCD_R0 0x6d0f -#define STM32F746_PG13_FUNC_EVENTOUT 0x6d10 -#define STM32F746_PG13_FUNC_ANALOG 0x6d11 - -#define STM32F746_PG14_FUNC_GPIO 0x6e00 -#define STM32F746_PG14_FUNC_TRACED1 0x6e01 -#define STM32F746_PG14_FUNC_LPTIM1_ETR 0x6e04 -#define STM32F746_PG14_FUNC_SPI6_MOSI 0x6e06 -#define STM32F746_PG14_FUNC_USART6_TX 0x6e09 -#define STM32F746_PG14_FUNC_QUADSPI_BK2_IO3 0x6e0a -#define STM32F746_PG14_FUNC_ETH_MII_TXD1_ETH_RMII_TXD1 0x6e0c -#define STM32F746_PG14_FUNC_FMC_A25 0x6e0d -#define STM32F746_PG14_FUNC_LCD_B0 0x6e0f -#define STM32F746_PG14_FUNC_EVENTOUT 0x6e10 -#define STM32F746_PG14_FUNC_ANALOG 0x6e11 - -#define STM32F746_PG15_FUNC_GPIO 0x6f00 -#define STM32F746_PG15_FUNC_USART6_CTS 0x6f09 -#define STM32F746_PG15_FUNC_FMC_SDNCAS 0x6f0d -#define STM32F746_PG15_FUNC_DCMI_D13 0x6f0e -#define STM32F746_PG15_FUNC_EVENTOUT 0x6f10 -#define STM32F746_PG15_FUNC_ANALOG 0x6f11 - - -#define STM32F746_PH0_FUNC_GPIO 0x7000 -#define STM32F746_PH0_FUNC_EVENTOUT 0x7010 -#define STM32F746_PH0_FUNC_ANALOG 0x7011 - -#define STM32F746_PH1_FUNC_GPIO 0x7100 -#define STM32F746_PH1_FUNC_EVENTOUT 0x7110 -#define STM32F746_PH1_FUNC_ANALOG 0x7111 - -#define STM32F746_PH2_FUNC_GPIO 0x7200 -#define STM32F746_PH2_FUNC_LPTIM1_IN2 0x7204 -#define STM32F746_PH2_FUNC_QUADSPI_BK2_IO0 0x720a -#define STM32F746_PH2_FUNC_SAI2_SCK_B 0x720b -#define STM32F746_PH2_FUNC_ETH_MII_CRS 0x720c -#define STM32F746_PH2_FUNC_FMC_SDCKE0 0x720d -#define STM32F746_PH2_FUNC_LCD_R0 0x720f -#define STM32F746_PH2_FUNC_EVENTOUT 0x7210 -#define STM32F746_PH2_FUNC_ANALOG 0x7211 - -#define STM32F746_PH3_FUNC_GPIO 0x7300 -#define STM32F746_PH3_FUNC_QUADSPI_BK2_IO1 0x730a -#define STM32F746_PH3_FUNC_SAI2_MCLK_B 0x730b -#define STM32F746_PH3_FUNC_ETH_MII_COL 0x730c -#define STM32F746_PH3_FUNC_FMC_SDNE0 0x730d -#define STM32F746_PH3_FUNC_LCD_R1 0x730f -#define STM32F746_PH3_FUNC_EVENTOUT 0x7310 -#define STM32F746_PH3_FUNC_ANALOG 0x7311 - -#define STM32F746_PH4_FUNC_GPIO 0x7400 -#define STM32F746_PH4_FUNC_I2C2_SCL 0x7405 -#define STM32F746_PH4_FUNC_OTG_HS_ULPI_NXT 0x740b -#define STM32F746_PH4_FUNC_EVENTOUT 0x7410 -#define STM32F746_PH4_FUNC_ANALOG 0x7411 - -#define STM32F746_PH5_FUNC_GPIO 0x7500 -#define STM32F746_PH5_FUNC_I2C2_SDA 0x7505 -#define STM32F746_PH5_FUNC_SPI5_NSS 0x7506 -#define STM32F746_PH5_FUNC_FMC_SDNWE 0x750d -#define STM32F746_PH5_FUNC_EVENTOUT 0x7510 -#define STM32F746_PH5_FUNC_ANALOG 0x7511 - -#define STM32F746_PH6_FUNC_GPIO 0x7600 -#define STM32F746_PH6_FUNC_I2C2_SMBA 0x7605 -#define STM32F746_PH6_FUNC_SPI5_SCK 0x7606 -#define STM32F746_PH6_FUNC_TIM12_CH1 0x760a -#define STM32F746_PH6_FUNC_ETH_MII_RXD2 0x760c -#define STM32F746_PH6_FUNC_FMC_SDNE1 0x760d -#define STM32F746_PH6_FUNC_DCMI_D8 0x760e -#define STM32F746_PH6_FUNC_EVENTOUT 0x7610 -#define STM32F746_PH6_FUNC_ANALOG 0x7611 - -#define STM32F746_PH7_FUNC_GPIO 0x7700 -#define STM32F746_PH7_FUNC_I2C3_SCL 0x7705 -#define STM32F746_PH7_FUNC_SPI5_MISO 0x7706 -#define STM32F746_PH7_FUNC_ETH_MII_RXD3 0x770c -#define STM32F746_PH7_FUNC_FMC_SDCKE1 0x770d -#define STM32F746_PH7_FUNC_DCMI_D9 0x770e -#define STM32F746_PH7_FUNC_EVENTOUT 0x7710 -#define STM32F746_PH7_FUNC_ANALOG 0x7711 - -#define STM32F746_PH8_FUNC_GPIO 0x7800 -#define STM32F746_PH8_FUNC_I2C3_SDA 0x7805 -#define STM32F746_PH8_FUNC_FMC_D16 0x780d -#define STM32F746_PH8_FUNC_DCMI_HSYNC 0x780e -#define STM32F746_PH8_FUNC_LCD_R2 0x780f -#define STM32F746_PH8_FUNC_EVENTOUT 0x7810 -#define STM32F746_PH8_FUNC_ANALOG 0x7811 - -#define STM32F746_PH9_FUNC_GPIO 0x7900 -#define STM32F746_PH9_FUNC_I2C3_SMBA 0x7905 -#define STM32F746_PH9_FUNC_TIM12_CH2 0x790a -#define STM32F746_PH9_FUNC_FMC_D17 0x790d -#define STM32F746_PH9_FUNC_DCMI_D0 0x790e -#define STM32F746_PH9_FUNC_LCD_R3 0x790f -#define STM32F746_PH9_FUNC_EVENTOUT 0x7910 -#define STM32F746_PH9_FUNC_ANALOG 0x7911 - -#define STM32F746_PH10_FUNC_GPIO 0x7a00 -#define STM32F746_PH10_FUNC_TIM5_CH1 0x7a03 -#define STM32F746_PH10_FUNC_I2C4_SMBA 0x7a05 -#define STM32F746_PH10_FUNC_FMC_D18 0x7a0d -#define STM32F746_PH10_FUNC_DCMI_D1 0x7a0e -#define STM32F746_PH10_FUNC_LCD_R4 0x7a0f -#define STM32F746_PH10_FUNC_EVENTOUT 0x7a10 -#define STM32F746_PH10_FUNC_ANALOG 0x7a11 - -#define STM32F746_PH11_FUNC_GPIO 0x7b00 -#define STM32F746_PH11_FUNC_TIM5_CH2 0x7b03 -#define STM32F746_PH11_FUNC_I2C4_SCL 0x7b05 -#define STM32F746_PH11_FUNC_FMC_D19 0x7b0d -#define STM32F746_PH11_FUNC_DCMI_D2 0x7b0e -#define STM32F746_PH11_FUNC_LCD_R5 0x7b0f -#define STM32F746_PH11_FUNC_EVENTOUT 0x7b10 -#define STM32F746_PH11_FUNC_ANALOG 0x7b11 - -#define STM32F746_PH12_FUNC_GPIO 0x7c00 -#define STM32F746_PH12_FUNC_TIM5_CH3 0x7c03 -#define STM32F746_PH12_FUNC_I2C4_SDA 0x7c05 -#define STM32F746_PH12_FUNC_FMC_D20 0x7c0d -#define STM32F746_PH12_FUNC_DCMI_D3 0x7c0e -#define STM32F746_PH12_FUNC_LCD_R6 0x7c0f -#define STM32F746_PH12_FUNC_EVENTOUT 0x7c10 -#define STM32F746_PH12_FUNC_ANALOG 0x7c11 - -#define STM32F746_PH13_FUNC_GPIO 0x7d00 -#define STM32F746_PH13_FUNC_TIM8_CH1N 0x7d04 -#define STM32F746_PH13_FUNC_CAN1_TX 0x7d0a -#define STM32F746_PH13_FUNC_FMC_D21 0x7d0d -#define STM32F746_PH13_FUNC_LCD_G2 0x7d0f -#define STM32F746_PH13_FUNC_EVENTOUT 0x7d10 -#define STM32F746_PH13_FUNC_ANALOG 0x7d11 - -#define STM32F746_PH14_FUNC_GPIO 0x7e00 -#define STM32F746_PH14_FUNC_TIM8_CH2N 0x7e04 -#define STM32F746_PH14_FUNC_FMC_D22 0x7e0d -#define STM32F746_PH14_FUNC_DCMI_D4 0x7e0e -#define STM32F746_PH14_FUNC_LCD_G3 0x7e0f -#define STM32F746_PH14_FUNC_EVENTOUT 0x7e10 -#define STM32F746_PH14_FUNC_ANALOG 0x7e11 - -#define STM32F746_PH15_FUNC_GPIO 0x7f00 -#define STM32F746_PH15_FUNC_TIM8_CH3N 0x7f04 -#define STM32F746_PH15_FUNC_FMC_D23 0x7f0d -#define STM32F746_PH15_FUNC_DCMI_D11 0x7f0e -#define STM32F746_PH15_FUNC_LCD_G4 0x7f0f -#define STM32F746_PH15_FUNC_EVENTOUT 0x7f10 -#define STM32F746_PH15_FUNC_ANALOG 0x7f11 - - -#define STM32F746_PI0_FUNC_GPIO 0x8000 -#define STM32F746_PI0_FUNC_TIM5_CH4 0x8003 -#define STM32F746_PI0_FUNC_SPI2_NSS_I2S2_WS 0x8006 -#define STM32F746_PI0_FUNC_FMC_D24 0x800d -#define STM32F746_PI0_FUNC_DCMI_D13 0x800e -#define STM32F746_PI0_FUNC_LCD_G5 0x800f -#define STM32F746_PI0_FUNC_EVENTOUT 0x8010 -#define STM32F746_PI0_FUNC_ANALOG 0x8011 - -#define STM32F746_PI1_FUNC_GPIO 0x8100 -#define STM32F746_PI1_FUNC_TIM8_BKIN2 0x8104 -#define STM32F746_PI1_FUNC_SPI2_SCK_I2S2_CK 0x8106 -#define STM32F746_PI1_FUNC_FMC_D25 0x810d -#define STM32F746_PI1_FUNC_DCMI_D8 0x810e -#define STM32F746_PI1_FUNC_LCD_G6 0x810f -#define STM32F746_PI1_FUNC_EVENTOUT 0x8110 -#define STM32F746_PI1_FUNC_ANALOG 0x8111 - -#define STM32F746_PI2_FUNC_GPIO 0x8200 -#define STM32F746_PI2_FUNC_TIM8_CH4 0x8204 -#define STM32F746_PI2_FUNC_SPI2_MISO 0x8206 -#define STM32F746_PI2_FUNC_FMC_D26 0x820d -#define STM32F746_PI2_FUNC_DCMI_D9 0x820e -#define STM32F746_PI2_FUNC_LCD_G7 0x820f -#define STM32F746_PI2_FUNC_EVENTOUT 0x8210 -#define STM32F746_PI2_FUNC_ANALOG 0x8211 - -#define STM32F746_PI3_FUNC_GPIO 0x8300 -#define STM32F746_PI3_FUNC_TIM8_ETR 0x8304 -#define STM32F746_PI3_FUNC_SPI2_MOSI_I2S2_SD 0x8306 -#define STM32F746_PI3_FUNC_FMC_D27 0x830d -#define STM32F746_PI3_FUNC_DCMI_D10 0x830e -#define STM32F746_PI3_FUNC_EVENTOUT 0x8310 -#define STM32F746_PI3_FUNC_ANALOG 0x8311 - -#define STM32F746_PI4_FUNC_GPIO 0x8400 -#define STM32F746_PI4_FUNC_TIM8_BKIN 0x8404 -#define STM32F746_PI4_FUNC_SAI2_MCLK_A 0x840b -#define STM32F746_PI4_FUNC_FMC_NBL2 0x840d -#define STM32F746_PI4_FUNC_DCMI_D5 0x840e -#define STM32F746_PI4_FUNC_LCD_B4 0x840f -#define STM32F746_PI4_FUNC_EVENTOUT 0x8410 -#define STM32F746_PI4_FUNC_ANALOG 0x8411 - -#define STM32F746_PI5_FUNC_GPIO 0x8500 -#define STM32F746_PI5_FUNC_TIM8_CH1 0x8504 -#define STM32F746_PI5_FUNC_SAI2_SCK_A 0x850b -#define STM32F746_PI5_FUNC_FMC_NBL3 0x850d -#define STM32F746_PI5_FUNC_DCMI_VSYNC 0x850e -#define STM32F746_PI5_FUNC_LCD_B5 0x850f -#define STM32F746_PI5_FUNC_EVENTOUT 0x8510 -#define STM32F746_PI5_FUNC_ANALOG 0x8511 - -#define STM32F746_PI6_FUNC_GPIO 0x8600 -#define STM32F746_PI6_FUNC_TIM8_CH2 0x8604 -#define STM32F746_PI6_FUNC_SAI2_SD_A 0x860b -#define STM32F746_PI6_FUNC_FMC_D28 0x860d -#define STM32F746_PI6_FUNC_DCMI_D6 0x860e -#define STM32F746_PI6_FUNC_LCD_B6 0x860f -#define STM32F746_PI6_FUNC_EVENTOUT 0x8610 -#define STM32F746_PI6_FUNC_ANALOG 0x8611 - -#define STM32F746_PI7_FUNC_GPIO 0x8700 -#define STM32F746_PI7_FUNC_TIM8_CH3 0x8704 -#define STM32F746_PI7_FUNC_SAI2_FS_A 0x870b -#define STM32F746_PI7_FUNC_FMC_D29 0x870d -#define STM32F746_PI7_FUNC_DCMI_D7 0x870e -#define STM32F746_PI7_FUNC_LCD_B7 0x870f -#define STM32F746_PI7_FUNC_EVENTOUT 0x8710 -#define STM32F746_PI7_FUNC_ANALOG 0x8711 - -#define STM32F746_PI8_FUNC_GPIO 0x8800 -#define STM32F746_PI8_FUNC_EVENTOUT 0x8810 -#define STM32F746_PI8_FUNC_ANALOG 0x8811 - -#define STM32F746_PI9_FUNC_GPIO 0x8900 -#define STM32F746_PI9_FUNC_CAN1_RX 0x890a -#define STM32F746_PI9_FUNC_FMC_D30 0x890d -#define STM32F746_PI9_FUNC_LCD_VSYNC 0x890f -#define STM32F746_PI9_FUNC_EVENTOUT 0x8910 -#define STM32F746_PI9_FUNC_ANALOG 0x8911 - -#define STM32F746_PI10_FUNC_GPIO 0x8a00 -#define STM32F746_PI10_FUNC_ETH_MII_RX_ER 0x8a0c -#define STM32F746_PI10_FUNC_FMC_D31 0x8a0d -#define STM32F746_PI10_FUNC_LCD_HSYNC 0x8a0f -#define STM32F746_PI10_FUNC_EVENTOUT 0x8a10 -#define STM32F746_PI10_FUNC_ANALOG 0x8a11 - -#define STM32F746_PI11_FUNC_GPIO 0x8b00 -#define STM32F746_PI11_FUNC_OTG_HS_ULPI_DIR 0x8b0b -#define STM32F746_PI11_FUNC_EVENTOUT 0x8b10 -#define STM32F746_PI11_FUNC_ANALOG 0x8b11 - -#define STM32F746_PI12_FUNC_GPIO 0x8c00 -#define STM32F746_PI12_FUNC_LCD_HSYNC 0x8c0f -#define STM32F746_PI12_FUNC_EVENTOUT 0x8c10 -#define STM32F746_PI12_FUNC_ANALOG 0x8c11 - -#define STM32F746_PI13_FUNC_GPIO 0x8d00 -#define STM32F746_PI13_FUNC_LCD_VSYNC 0x8d0f -#define STM32F746_PI13_FUNC_EVENTOUT 0x8d10 -#define STM32F746_PI13_FUNC_ANALOG 0x8d11 - -#define STM32F746_PI14_FUNC_GPIO 0x8e00 -#define STM32F746_PI14_FUNC_LCD_CLK 0x8e0f -#define STM32F746_PI14_FUNC_EVENTOUT 0x8e10 -#define STM32F746_PI14_FUNC_ANALOG 0x8e11 - -#define STM32F746_PI15_FUNC_GPIO 0x8f00 -#define STM32F746_PI15_FUNC_LCD_R0 0x8f0f -#define STM32F746_PI15_FUNC_EVENTOUT 0x8f10 -#define STM32F746_PI15_FUNC_ANALOG 0x8f11 - - -#define STM32F746_PJ0_FUNC_GPIO 0x9000 -#define STM32F746_PJ0_FUNC_LCD_R1 0x900f -#define STM32F746_PJ0_FUNC_EVENTOUT 0x9010 -#define STM32F746_PJ0_FUNC_ANALOG 0x9011 - -#define STM32F746_PJ1_FUNC_GPIO 0x9100 -#define STM32F746_PJ1_FUNC_LCD_R2 0x910f -#define STM32F746_PJ1_FUNC_EVENTOUT 0x9110 -#define STM32F746_PJ1_FUNC_ANALOG 0x9111 - -#define STM32F746_PJ2_FUNC_GPIO 0x9200 -#define STM32F746_PJ2_FUNC_LCD_R3 0x920f -#define STM32F746_PJ2_FUNC_EVENTOUT 0x9210 -#define STM32F746_PJ2_FUNC_ANALOG 0x9211 - -#define STM32F746_PJ3_FUNC_GPIO 0x9300 -#define STM32F746_PJ3_FUNC_LCD_R4 0x930f -#define STM32F746_PJ3_FUNC_EVENTOUT 0x9310 -#define STM32F746_PJ3_FUNC_ANALOG 0x9311 - -#define STM32F746_PJ4_FUNC_GPIO 0x9400 -#define STM32F746_PJ4_FUNC_LCD_R5 0x940f -#define STM32F746_PJ4_FUNC_EVENTOUT 0x9410 -#define STM32F746_PJ4_FUNC_ANALOG 0x9411 - -#define STM32F746_PJ5_FUNC_GPIO 0x9500 -#define STM32F746_PJ5_FUNC_LCD_R6 0x950f -#define STM32F746_PJ5_FUNC_EVENTOUT 0x9510 -#define STM32F746_PJ5_FUNC_ANALOG 0x9511 - -#define STM32F746_PJ6_FUNC_GPIO 0x9600 -#define STM32F746_PJ6_FUNC_LCD_R7 0x960f -#define STM32F746_PJ6_FUNC_EVENTOUT 0x9610 -#define STM32F746_PJ6_FUNC_ANALOG 0x9611 - -#define STM32F746_PJ7_FUNC_GPIO 0x9700 -#define STM32F746_PJ7_FUNC_LCD_G0 0x970f -#define STM32F746_PJ7_FUNC_EVENTOUT 0x9710 -#define STM32F746_PJ7_FUNC_ANALOG 0x9711 - -#define STM32F746_PJ8_FUNC_GPIO 0x9800 -#define STM32F746_PJ8_FUNC_LCD_G1 0x980f -#define STM32F746_PJ8_FUNC_EVENTOUT 0x9810 -#define STM32F746_PJ8_FUNC_ANALOG 0x9811 - -#define STM32F746_PJ9_FUNC_GPIO 0x9900 -#define STM32F746_PJ9_FUNC_LCD_G2 0x990f -#define STM32F746_PJ9_FUNC_EVENTOUT 0x9910 -#define STM32F746_PJ9_FUNC_ANALOG 0x9911 - -#define STM32F746_PJ10_FUNC_GPIO 0x9a00 -#define STM32F746_PJ10_FUNC_LCD_G3 0x9a0f -#define STM32F746_PJ10_FUNC_EVENTOUT 0x9a10 -#define STM32F746_PJ10_FUNC_ANALOG 0x9a11 - -#define STM32F746_PJ11_FUNC_GPIO 0x9b00 -#define STM32F746_PJ11_FUNC_LCD_G4 0x9b0f -#define STM32F746_PJ11_FUNC_EVENTOUT 0x9b10 -#define STM32F746_PJ11_FUNC_ANALOG 0x9b11 - -#define STM32F746_PJ12_FUNC_GPIO 0x9c00 -#define STM32F746_PJ12_FUNC_LCD_B0 0x9c0f -#define STM32F746_PJ12_FUNC_EVENTOUT 0x9c10 -#define STM32F746_PJ12_FUNC_ANALOG 0x9c11 - -#define STM32F746_PJ13_FUNC_GPIO 0x9d00 -#define STM32F746_PJ13_FUNC_LCD_B1 0x9d0f -#define STM32F746_PJ13_FUNC_EVENTOUT 0x9d10 -#define STM32F746_PJ13_FUNC_ANALOG 0x9d11 - -#define STM32F746_PJ14_FUNC_GPIO 0x9e00 -#define STM32F746_PJ14_FUNC_LCD_B2 0x9e0f -#define STM32F746_PJ14_FUNC_EVENTOUT 0x9e10 -#define STM32F746_PJ14_FUNC_ANALOG 0x9e11 - -#define STM32F746_PJ15_FUNC_GPIO 0x9f00 -#define STM32F746_PJ15_FUNC_LCD_B3 0x9f0f -#define STM32F746_PJ15_FUNC_EVENTOUT 0x9f10 -#define STM32F746_PJ15_FUNC_ANALOG 0x9f11 - - -#define STM32F746_PK0_FUNC_GPIO 0xa000 -#define STM32F746_PK0_FUNC_LCD_G5 0xa00f -#define STM32F746_PK0_FUNC_EVENTOUT 0xa010 -#define STM32F746_PK0_FUNC_ANALOG 0xa011 - -#define STM32F746_PK1_FUNC_GPIO 0xa100 -#define STM32F746_PK1_FUNC_LCD_G6 0xa10f -#define STM32F746_PK1_FUNC_EVENTOUT 0xa110 -#define STM32F746_PK1_FUNC_ANALOG 0xa111 - -#define STM32F746_PK2_FUNC_GPIO 0xa200 -#define STM32F746_PK2_FUNC_LCD_G7 0xa20f -#define STM32F746_PK2_FUNC_EVENTOUT 0xa210 -#define STM32F746_PK2_FUNC_ANALOG 0xa211 - -#define STM32F746_PK3_FUNC_GPIO 0xa300 -#define STM32F746_PK3_FUNC_LCD_B4 0xa30f -#define STM32F746_PK3_FUNC_EVENTOUT 0xa310 -#define STM32F746_PK3_FUNC_ANALOG 0xa311 - -#define STM32F746_PK4_FUNC_GPIO 0xa400 -#define STM32F746_PK4_FUNC_LCD_B5 0xa40f -#define STM32F746_PK4_FUNC_EVENTOUT 0xa410 -#define STM32F746_PK4_FUNC_ANALOG 0xa411 - -#define STM32F746_PK5_FUNC_GPIO 0xa500 -#define STM32F746_PK5_FUNC_LCD_B6 0xa50f -#define STM32F746_PK5_FUNC_EVENTOUT 0xa510 -#define STM32F746_PK5_FUNC_ANALOG 0xa511 - -#define STM32F746_PK6_FUNC_GPIO 0xa600 -#define STM32F746_PK6_FUNC_LCD_B7 0xa60f -#define STM32F746_PK6_FUNC_EVENTOUT 0xa610 -#define STM32F746_PK6_FUNC_ANALOG 0xa611 - -#define STM32F746_PK7_FUNC_GPIO 0xa700 -#define STM32F746_PK7_FUNC_LCD_DE 0xa70f -#define STM32F746_PK7_FUNC_EVENTOUT 0xa710 -#define STM32F746_PK7_FUNC_ANALOG 0xa711 - -#endif /* _DT_BINDINGS_STM32F746_PINFUNC_H */ diff --git a/include/dt-bindings/pinctrl/stm32h7-pinfunc.h b/include/dt-bindings/pinctrl/stm32h7-pinfunc.h deleted file mode 100644 index cb673b5e8e1e..000000000000 --- a/include/dt-bindings/pinctrl/stm32h7-pinfunc.h +++ /dev/null @@ -1,1612 +0,0 @@ -#ifndef _DT_BINDINGS_STM32H7_PINFUNC_H -#define _DT_BINDINGS_STM32H7_PINFUNC_H - -#define STM32H7_PA0_FUNC_GPIO 0x0 -#define STM32H7_PA0_FUNC_TIM2_CH1_TIM2_ETR 0x2 -#define STM32H7_PA0_FUNC_TIM5_CH1 0x3 -#define STM32H7_PA0_FUNC_TIM8_ETR 0x4 -#define STM32H7_PA0_FUNC_TIM15_BKIN 0x5 -#define STM32H7_PA0_FUNC_USART2_CTS_NSS 0x8 -#define STM32H7_PA0_FUNC_UART4_TX 0x9 -#define STM32H7_PA0_FUNC_SDMMC2_CMD 0xa -#define STM32H7_PA0_FUNC_SAI2_SD_B 0xb -#define STM32H7_PA0_FUNC_ETH_MII_CRS 0xc -#define STM32H7_PA0_FUNC_EVENTOUT 0x10 -#define STM32H7_PA0_FUNC_ANALOG 0x11 - -#define STM32H7_PA1_FUNC_GPIO 0x100 -#define STM32H7_PA1_FUNC_TIM2_CH2 0x102 -#define STM32H7_PA1_FUNC_TIM5_CH2 0x103 -#define STM32H7_PA1_FUNC_LPTIM3_OUT 0x104 -#define STM32H7_PA1_FUNC_TIM15_CH1N 0x105 -#define STM32H7_PA1_FUNC_USART2_RTS 0x108 -#define STM32H7_PA1_FUNC_UART4_RX 0x109 -#define STM32H7_PA1_FUNC_QUADSPI_BK1_IO3 0x10a -#define STM32H7_PA1_FUNC_SAI2_MCK_B 0x10b -#define STM32H7_PA1_FUNC_ETH_MII_RX_CLK_ETH_RMII_REF_CLK 0x10c -#define STM32H7_PA1_FUNC_LCD_R2 0x10f -#define STM32H7_PA1_FUNC_EVENTOUT 0x110 -#define STM32H7_PA1_FUNC_ANALOG 0x111 - -#define STM32H7_PA2_FUNC_GPIO 0x200 -#define STM32H7_PA2_FUNC_TIM2_CH3 0x202 -#define STM32H7_PA2_FUNC_TIM5_CH3 0x203 -#define STM32H7_PA2_FUNC_LPTIM4_OUT 0x204 -#define STM32H7_PA2_FUNC_TIM15_CH1 0x205 -#define STM32H7_PA2_FUNC_USART2_TX 0x208 -#define STM32H7_PA2_FUNC_SAI2_SCK_B 0x209 -#define STM32H7_PA2_FUNC_ETH_MDIO 0x20c -#define STM32H7_PA2_FUNC_MDIOS_MDIO 0x20d -#define STM32H7_PA2_FUNC_LCD_R1 0x20f -#define STM32H7_PA2_FUNC_EVENTOUT 0x210 -#define STM32H7_PA2_FUNC_ANALOG 0x211 - -#define STM32H7_PA3_FUNC_GPIO 0x300 -#define STM32H7_PA3_FUNC_TIM2_CH4 0x302 -#define STM32H7_PA3_FUNC_TIM5_CH4 0x303 -#define STM32H7_PA3_FUNC_LPTIM5_OUT 0x304 -#define STM32H7_PA3_FUNC_TIM15_CH2 0x305 -#define STM32H7_PA3_FUNC_USART2_RX 0x308 -#define STM32H7_PA3_FUNC_LCD_B2 0x30a -#define STM32H7_PA3_FUNC_OTG_HS_ULPI_D0 0x30b -#define STM32H7_PA3_FUNC_ETH_MII_COL 0x30c -#define STM32H7_PA3_FUNC_LCD_B5 0x30f -#define STM32H7_PA3_FUNC_EVENTOUT 0x310 -#define STM32H7_PA3_FUNC_ANALOG 0x311 - -#define STM32H7_PA4_FUNC_GPIO 0x400 -#define STM32H7_PA4_FUNC_TIM5_ETR 0x403 -#define STM32H7_PA4_FUNC_SPI1_NSS_I2S1_WS 0x406 -#define STM32H7_PA4_FUNC_SPI3_NSS_I2S3_WS 0x407 -#define STM32H7_PA4_FUNC_USART2_CK 0x408 -#define STM32H7_PA4_FUNC_SPI6_NSS 0x409 -#define STM32H7_PA4_FUNC_OTG_HS_SOF 0x40d -#define STM32H7_PA4_FUNC_DCMI_HSYNC 0x40e -#define STM32H7_PA4_FUNC_LCD_VSYNC 0x40f -#define STM32H7_PA4_FUNC_EVENTOUT 0x410 -#define STM32H7_PA4_FUNC_ANALOG 0x411 - -#define STM32H7_PA5_FUNC_GPIO 0x500 -#define STM32H7_PA5_FUNC_TIM2_CH1_TIM2_ETR 0x502 -#define STM32H7_PA5_FUNC_TIM8_CH1N 0x504 -#define STM32H7_PA5_FUNC_SPI1_SCK_I2S1_CK 0x506 -#define STM32H7_PA5_FUNC_SPI6_SCK 0x509 -#define STM32H7_PA5_FUNC_OTG_HS_ULPI_CK 0x50b -#define STM32H7_PA5_FUNC_LCD_R4 0x50f -#define STM32H7_PA5_FUNC_EVENTOUT 0x510 -#define STM32H7_PA5_FUNC_ANALOG 0x511 - -#define STM32H7_PA6_FUNC_GPIO 0x600 -#define STM32H7_PA6_FUNC_TIM1_BKIN 0x602 -#define STM32H7_PA6_FUNC_TIM3_CH1 0x603 -#define STM32H7_PA6_FUNC_TIM8_BKIN 0x604 -#define STM32H7_PA6_FUNC_SPI1_MISO_I2S1_SDI 0x606 -#define STM32H7_PA6_FUNC_SPI6_MISO 0x609 -#define STM32H7_PA6_FUNC_TIM13_CH1 0x60a -#define STM32H7_PA6_FUNC_TIM8_BKIN_COMP12 0x60b -#define STM32H7_PA6_FUNC_MDIOS_MDC 0x60c -#define STM32H7_PA6_FUNC_TIM1_BKIN_COMP12 0x60d -#define STM32H7_PA6_FUNC_DCMI_PIXCLK 0x60e -#define STM32H7_PA6_FUNC_LCD_G2 0x60f -#define STM32H7_PA6_FUNC_EVENTOUT 0x610 -#define STM32H7_PA6_FUNC_ANALOG 0x611 - -#define STM32H7_PA7_FUNC_GPIO 0x700 -#define STM32H7_PA7_FUNC_TIM1_CH1N 0x702 -#define STM32H7_PA7_FUNC_TIM3_CH2 0x703 -#define STM32H7_PA7_FUNC_TIM8_CH1N 0x704 -#define STM32H7_PA7_FUNC_SPI1_MOSI_I2S1_SDO 0x706 -#define STM32H7_PA7_FUNC_SPI6_MOSI 0x709 -#define STM32H7_PA7_FUNC_TIM14_CH1 0x70a -#define STM32H7_PA7_FUNC_ETH_MII_RX_DV_ETH_RMII_CRS_DV 0x70c -#define STM32H7_PA7_FUNC_FMC_SDNWE 0x70d -#define STM32H7_PA7_FUNC_EVENTOUT 0x710 -#define STM32H7_PA7_FUNC_ANALOG 0x711 - -#define STM32H7_PA8_FUNC_GPIO 0x800 -#define STM32H7_PA8_FUNC_MCO1 0x801 -#define STM32H7_PA8_FUNC_TIM1_CH1 0x802 -#define STM32H7_PA8_FUNC_HRTIM_CHB2 0x803 -#define STM32H7_PA8_FUNC_TIM8_BKIN2 0x804 -#define STM32H7_PA8_FUNC_I2C3_SCL 0x805 -#define STM32H7_PA8_FUNC_USART1_CK 0x808 -#define STM32H7_PA8_FUNC_OTG_FS_SOF 0x80b -#define STM32H7_PA8_FUNC_UART7_RX 0x80c -#define STM32H7_PA8_FUNC_TIM8_BKIN2_COMP12 0x80d -#define STM32H7_PA8_FUNC_LCD_B3 0x80e -#define STM32H7_PA8_FUNC_LCD_R6 0x80f -#define STM32H7_PA8_FUNC_EVENTOUT 0x810 -#define STM32H7_PA8_FUNC_ANALOG 0x811 - -#define STM32H7_PA9_FUNC_GPIO 0x900 -#define STM32H7_PA9_FUNC_TIM1_CH2 0x902 -#define STM32H7_PA9_FUNC_HRTIM_CHC1 0x903 -#define STM32H7_PA9_FUNC_LPUART1_TX 0x904 -#define STM32H7_PA9_FUNC_I2C3_SMBA 0x905 -#define STM32H7_PA9_FUNC_SPI2_SCK_I2S2_CK 0x906 -#define STM32H7_PA9_FUNC_USART1_TX 0x908 -#define STM32H7_PA9_FUNC_CAN1_RXFD 0x90a -#define STM32H7_PA9_FUNC_ETH_TX_ER 0x90c -#define STM32H7_PA9_FUNC_DCMI_D0 0x90e -#define STM32H7_PA9_FUNC_LCD_R5 0x90f -#define STM32H7_PA9_FUNC_EVENTOUT 0x910 -#define STM32H7_PA9_FUNC_ANALOG 0x911 - -#define STM32H7_PA10_FUNC_GPIO 0xa00 -#define STM32H7_PA10_FUNC_TIM1_CH3 0xa02 -#define STM32H7_PA10_FUNC_HRTIM_CHC2 0xa03 -#define STM32H7_PA10_FUNC_LPUART1_RX 0xa04 -#define STM32H7_PA10_FUNC_USART1_RX 0xa08 -#define STM32H7_PA10_FUNC_CAN1_TXFD 0xa0a -#define STM32H7_PA10_FUNC_OTG_FS_ID 0xa0b -#define STM32H7_PA10_FUNC_MDIOS_MDIO 0xa0c -#define STM32H7_PA10_FUNC_LCD_B4 0xa0d -#define STM32H7_PA10_FUNC_DCMI_D1 0xa0e -#define STM32H7_PA10_FUNC_LCD_B1 0xa0f -#define STM32H7_PA10_FUNC_EVENTOUT 0xa10 -#define STM32H7_PA10_FUNC_ANALOG 0xa11 - -#define STM32H7_PA11_FUNC_GPIO 0xb00 -#define STM32H7_PA11_FUNC_TIM1_CH4 0xb02 -#define STM32H7_PA11_FUNC_HRTIM_CHD1 0xb03 -#define STM32H7_PA11_FUNC_LPUART1_CTS 0xb04 -#define STM32H7_PA11_FUNC_SPI2_NSS_I2S2_WS 0xb06 -#define STM32H7_PA11_FUNC_UART4_RX 0xb07 -#define STM32H7_PA11_FUNC_USART1_CTS_NSS 0xb08 -#define STM32H7_PA11_FUNC_CAN1_RX 0xb0a -#define STM32H7_PA11_FUNC_OTG_FS_DM 0xb0b -#define STM32H7_PA11_FUNC_LCD_R4 0xb0f -#define STM32H7_PA11_FUNC_EVENTOUT 0xb10 -#define STM32H7_PA11_FUNC_ANALOG 0xb11 - -#define STM32H7_PA12_FUNC_GPIO 0xc00 -#define STM32H7_PA12_FUNC_TIM1_ETR 0xc02 -#define STM32H7_PA12_FUNC_HRTIM_CHD2 0xc03 -#define STM32H7_PA12_FUNC_LPUART1_RTS 0xc04 -#define STM32H7_PA12_FUNC_SPI2_SCK_I2S2_CK 0xc06 -#define STM32H7_PA12_FUNC_UART4_TX 0xc07 -#define STM32H7_PA12_FUNC_USART1_RTS 0xc08 -#define STM32H7_PA12_FUNC_SAI2_FS_B 0xc09 -#define STM32H7_PA12_FUNC_CAN1_TX 0xc0a -#define STM32H7_PA12_FUNC_OTG_FS_DP 0xc0b -#define STM32H7_PA12_FUNC_LCD_R5 0xc0f -#define STM32H7_PA12_FUNC_EVENTOUT 0xc10 -#define STM32H7_PA12_FUNC_ANALOG 0xc11 - -#define STM32H7_PA13_FUNC_GPIO 0xd00 -#define STM32H7_PA13_FUNC_JTMS_SWDIO 0xd01 -#define STM32H7_PA13_FUNC_EVENTOUT 0xd10 -#define STM32H7_PA13_FUNC_ANALOG 0xd11 - -#define STM32H7_PA14_FUNC_GPIO 0xe00 -#define STM32H7_PA14_FUNC_JTCK_SWCLK 0xe01 -#define STM32H7_PA14_FUNC_EVENTOUT 0xe10 -#define STM32H7_PA14_FUNC_ANALOG 0xe11 - -#define STM32H7_PA15_FUNC_GPIO 0xf00 -#define STM32H7_PA15_FUNC_JTDI 0xf01 -#define STM32H7_PA15_FUNC_TIM2_CH1_TIM2_ETR 0xf02 -#define STM32H7_PA15_FUNC_HRTIM_FLT1 0xf03 -#define STM32H7_PA15_FUNC_HDMI_CEC 0xf05 -#define STM32H7_PA15_FUNC_SPI1_NSS_I2S1_WS 0xf06 -#define STM32H7_PA15_FUNC_SPI3_NSS_I2S3_WS 0xf07 -#define STM32H7_PA15_FUNC_SPI6_NSS 0xf08 -#define STM32H7_PA15_FUNC_UART4_RTS 0xf09 -#define STM32H7_PA15_FUNC_UART7_TX 0xf0c -#define STM32H7_PA15_FUNC_DSI_TE 0xf0e -#define STM32H7_PA15_FUNC_EVENTOUT 0xf10 -#define STM32H7_PA15_FUNC_ANALOG 0xf11 - -#define STM32H7_PB0_FUNC_GPIO 0x1000 -#define STM32H7_PB0_FUNC_TIM1_CH2N 0x1002 -#define STM32H7_PB0_FUNC_TIM3_CH3 0x1003 -#define STM32H7_PB0_FUNC_TIM8_CH2N 0x1004 -#define STM32H7_PB0_FUNC_DFSDM_CKOUT 0x1007 -#define STM32H7_PB0_FUNC_UART4_CTS 0x1009 -#define STM32H7_PB0_FUNC_LCD_R3 0x100a -#define STM32H7_PB0_FUNC_OTG_HS_ULPI_D1 0x100b -#define STM32H7_PB0_FUNC_ETH_MII_RXD2 0x100c -#define STM32H7_PB0_FUNC_LCD_G1 0x100f -#define STM32H7_PB0_FUNC_EVENTOUT 0x1010 -#define STM32H7_PB0_FUNC_ANALOG 0x1011 - -#define STM32H7_PB1_FUNC_GPIO 0x1100 -#define STM32H7_PB1_FUNC_TIM1_CH3N 0x1102 -#define STM32H7_PB1_FUNC_TIM3_CH4 0x1103 -#define STM32H7_PB1_FUNC_TIM8_CH3N 0x1104 -#define STM32H7_PB1_FUNC_DFSDM_DATIN1 0x1107 -#define STM32H7_PB1_FUNC_LCD_R6 0x110a -#define STM32H7_PB1_FUNC_OTG_HS_ULPI_D2 0x110b -#define STM32H7_PB1_FUNC_ETH_MII_RXD3 0x110c -#define STM32H7_PB1_FUNC_LCD_G0 0x110f -#define STM32H7_PB1_FUNC_EVENTOUT 0x1110 -#define STM32H7_PB1_FUNC_ANALOG 0x1111 - -#define STM32H7_PB2_FUNC_GPIO 0x1200 -#define STM32H7_PB2_FUNC_SAI1_D1 0x1203 -#define STM32H7_PB2_FUNC_DFSDM_CKIN1 0x1205 -#define STM32H7_PB2_FUNC_SAI1_SD_A 0x1207 -#define STM32H7_PB2_FUNC_SPI3_MOSI_I2S3_SDO 0x1208 -#define STM32H7_PB2_FUNC_SAI4_SD_A 0x1209 -#define STM32H7_PB2_FUNC_QUADSPI_CLK 0x120a -#define STM32H7_PB2_FUNC_SAI4_D1 0x120b -#define STM32H7_PB2_FUNC_ETH_TX_ER 0x120c -#define STM32H7_PB2_FUNC_EVENTOUT 0x1210 -#define STM32H7_PB2_FUNC_ANALOG 0x1211 - -#define STM32H7_PB3_FUNC_GPIO 0x1300 -#define STM32H7_PB3_FUNC_JTDO_TRACESWO 0x1301 -#define STM32H7_PB3_FUNC_TIM2_CH2 0x1302 -#define STM32H7_PB3_FUNC_HRTIM_FLT4 0x1303 -#define STM32H7_PB3_FUNC_SPI1_SCK_I2S1_CK 0x1306 -#define STM32H7_PB3_FUNC_SPI3_SCK_I2S3_CK 0x1307 -#define STM32H7_PB3_FUNC_SPI6_SCK 0x1309 -#define STM32H7_PB3_FUNC_SDMMC2_D2 0x130a -#define STM32H7_PB3_FUNC_UART7_RX 0x130c -#define STM32H7_PB3_FUNC_EVENTOUT 0x1310 -#define STM32H7_PB3_FUNC_ANALOG 0x1311 - -#define STM32H7_PB4_FUNC_GPIO 0x1400 -#define STM32H7_PB4_FUNC_NJTRST 0x1401 -#define STM32H7_PB4_FUNC_TIM16_BKIN 0x1402 -#define STM32H7_PB4_FUNC_TIM3_CH1 0x1403 -#define STM32H7_PB4_FUNC_HRTIM_EEV6 0x1404 -#define STM32H7_PB4_FUNC_SPI1_MISO_I2S1_SDI 0x1406 -#define STM32H7_PB4_FUNC_SPI3_MISO_I2S3_SDI 0x1407 -#define STM32H7_PB4_FUNC_SPI2_NSS_I2S2_WS 0x1408 -#define STM32H7_PB4_FUNC_SPI6_MISO 0x1409 -#define STM32H7_PB4_FUNC_SDMMC2_D3 0x140a -#define STM32H7_PB4_FUNC_UART7_TX 0x140c -#define STM32H7_PB4_FUNC_EVENTOUT 0x1410 -#define STM32H7_PB4_FUNC_ANALOG 0x1411 - -#define STM32H7_PB5_FUNC_GPIO 0x1500 -#define STM32H7_PB5_FUNC_TIM17_BKIN 0x1502 -#define STM32H7_PB5_FUNC_TIM3_CH2 0x1503 -#define STM32H7_PB5_FUNC_HRTIM_EEV7 0x1504 -#define STM32H7_PB5_FUNC_I2C1_SMBA 0x1505 -#define STM32H7_PB5_FUNC_SPI1_MOSI_I2S1_SDO 0x1506 -#define STM32H7_PB5_FUNC_I2C4_SMBA 0x1507 -#define STM32H7_PB5_FUNC_SPI3_MOSI_I2S3_SDO 0x1508 -#define STM32H7_PB5_FUNC_SPI6_MOSI 0x1509 -#define STM32H7_PB5_FUNC_CAN2_RX 0x150a -#define STM32H7_PB5_FUNC_OTG_HS_ULPI_D7 0x150b -#define STM32H7_PB5_FUNC_ETH_PPS_OUT 0x150c -#define STM32H7_PB5_FUNC_FMC_SDCKE1 0x150d -#define STM32H7_PB5_FUNC_DCMI_D10 0x150e -#define STM32H7_PB5_FUNC_UART5_RX 0x150f -#define STM32H7_PB5_FUNC_EVENTOUT 0x1510 -#define STM32H7_PB5_FUNC_ANALOG 0x1511 - -#define STM32H7_PB6_FUNC_GPIO 0x1600 -#define STM32H7_PB6_FUNC_TIM16_CH1N 0x1602 -#define STM32H7_PB6_FUNC_TIM4_CH1 0x1603 -#define STM32H7_PB6_FUNC_HRTIM_EEV8 0x1604 -#define STM32H7_PB6_FUNC_I2C1_SCL 0x1605 -#define STM32H7_PB6_FUNC_HDMI_CEC 0x1606 -#define STM32H7_PB6_FUNC_I2C4_SCL 0x1607 -#define STM32H7_PB6_FUNC_USART1_TX 0x1608 -#define STM32H7_PB6_FUNC_LPUART1_TX 0x1609 -#define STM32H7_PB6_FUNC_CAN2_TX 0x160a -#define STM32H7_PB6_FUNC_QUADSPI_BK1_NCS 0x160b -#define STM32H7_PB6_FUNC_DFSDM_DATIN5 0x160c -#define STM32H7_PB6_FUNC_FMC_SDNE1 0x160d -#define STM32H7_PB6_FUNC_DCMI_D5 0x160e -#define STM32H7_PB6_FUNC_UART5_TX 0x160f -#define STM32H7_PB6_FUNC_EVENTOUT 0x1610 -#define STM32H7_PB6_FUNC_ANALOG 0x1611 - -#define STM32H7_PB7_FUNC_GPIO 0x1700 -#define STM32H7_PB7_FUNC_TIM17_CH1N 0x1702 -#define STM32H7_PB7_FUNC_TIM4_CH2 0x1703 -#define STM32H7_PB7_FUNC_HRTIM_EEV9 0x1704 -#define STM32H7_PB7_FUNC_I2C1_SDA 0x1705 -#define STM32H7_PB7_FUNC_I2C4_SDA 0x1707 -#define STM32H7_PB7_FUNC_USART1_RX 0x1708 -#define STM32H7_PB7_FUNC_LPUART1_RX 0x1709 -#define STM32H7_PB7_FUNC_CAN2_TXFD 0x170a -#define STM32H7_PB7_FUNC_DFSDM_CKIN5 0x170c -#define STM32H7_PB7_FUNC_FMC_NL 0x170d -#define STM32H7_PB7_FUNC_DCMI_VSYNC 0x170e -#define STM32H7_PB7_FUNC_EVENTOUT 0x1710 -#define STM32H7_PB7_FUNC_ANALOG 0x1711 - -#define STM32H7_PB8_FUNC_GPIO 0x1800 -#define STM32H7_PB8_FUNC_TIM16_CH1 0x1802 -#define STM32H7_PB8_FUNC_TIM4_CH3 0x1803 -#define STM32H7_PB8_FUNC_DFSDM_CKIN7 0x1804 -#define STM32H7_PB8_FUNC_I2C1_SCL 0x1805 -#define STM32H7_PB8_FUNC_I2C4_SCL 0x1807 -#define STM32H7_PB8_FUNC_SDMMC1_CKIN 0x1808 -#define STM32H7_PB8_FUNC_UART4_RX 0x1809 -#define STM32H7_PB8_FUNC_CAN1_RX 0x180a -#define STM32H7_PB8_FUNC_SDMMC2_D4 0x180b -#define STM32H7_PB8_FUNC_ETH_MII_TXD3 0x180c -#define STM32H7_PB8_FUNC_SDMMC1_D4 0x180d -#define STM32H7_PB8_FUNC_DCMI_D6 0x180e -#define STM32H7_PB8_FUNC_LCD_B6 0x180f -#define STM32H7_PB8_FUNC_EVENTOUT 0x1810 -#define STM32H7_PB8_FUNC_ANALOG 0x1811 - -#define STM32H7_PB9_FUNC_GPIO 0x1900 -#define STM32H7_PB9_FUNC_TIM17_CH1 0x1902 -#define STM32H7_PB9_FUNC_TIM4_CH4 0x1903 -#define STM32H7_PB9_FUNC_DFSDM_DATIN7 0x1904 -#define STM32H7_PB9_FUNC_I2C1_SDA 0x1905 -#define STM32H7_PB9_FUNC_SPI2_NSS_I2S2_WS 0x1906 -#define STM32H7_PB9_FUNC_I2C4_SDA 0x1907 -#define STM32H7_PB9_FUNC_SDMMC1_CDIR 0x1908 -#define STM32H7_PB9_FUNC_UART4_TX 0x1909 -#define STM32H7_PB9_FUNC_CAN1_TX 0x190a -#define STM32H7_PB9_FUNC_SDMMC2_D5 0x190b -#define STM32H7_PB9_FUNC_I2C4_SMBA 0x190c -#define STM32H7_PB9_FUNC_SDMMC1_D5 0x190d -#define STM32H7_PB9_FUNC_DCMI_D7 0x190e -#define STM32H7_PB9_FUNC_LCD_B7 0x190f -#define STM32H7_PB9_FUNC_EVENTOUT 0x1910 -#define STM32H7_PB9_FUNC_ANALOG 0x1911 - -#define STM32H7_PB10_FUNC_GPIO 0x1a00 -#define STM32H7_PB10_FUNC_TIM2_CH3 0x1a02 -#define STM32H7_PB10_FUNC_HRTIM_SCOUT 0x1a03 -#define STM32H7_PB10_FUNC_LPTIM2_IN1 0x1a04 -#define STM32H7_PB10_FUNC_I2C2_SCL 0x1a05 -#define STM32H7_PB10_FUNC_SPI2_SCK_I2S2_CK 0x1a06 -#define STM32H7_PB10_FUNC_DFSDM_DATIN7 0x1a07 -#define STM32H7_PB10_FUNC_USART3_TX 0x1a08 -#define STM32H7_PB10_FUNC_QUADSPI_BK1_NCS 0x1a0a -#define STM32H7_PB10_FUNC_OTG_HS_ULPI_D3 0x1a0b -#define STM32H7_PB10_FUNC_ETH_MII_RX_ER 0x1a0c -#define STM32H7_PB10_FUNC_LCD_G4 0x1a0f -#define STM32H7_PB10_FUNC_EVENTOUT 0x1a10 -#define STM32H7_PB10_FUNC_ANALOG 0x1a11 - -#define STM32H7_PB11_FUNC_GPIO 0x1b00 -#define STM32H7_PB11_FUNC_TIM2_CH4 0x1b02 -#define STM32H7_PB11_FUNC_HRTIM_SCIN 0x1b03 -#define STM32H7_PB11_FUNC_LPTIM2_ETR 0x1b04 -#define STM32H7_PB11_FUNC_I2C2_SDA 0x1b05 -#define STM32H7_PB11_FUNC_DFSDM_CKIN7 0x1b07 -#define STM32H7_PB11_FUNC_USART3_RX 0x1b08 -#define STM32H7_PB11_FUNC_OTG_HS_ULPI_D4 0x1b0b -#define STM32H7_PB11_FUNC_ETH_MII_TX_EN_ETH_RMII_TX_EN 0x1b0c -#define STM32H7_PB11_FUNC_DSI_TE 0x1b0e -#define STM32H7_PB11_FUNC_LCD_G5 0x1b0f -#define STM32H7_PB11_FUNC_EVENTOUT 0x1b10 -#define STM32H7_PB11_FUNC_ANALOG 0x1b11 - -#define STM32H7_PB12_FUNC_GPIO 0x1c00 -#define STM32H7_PB12_FUNC_TIM1_BKIN 0x1c02 -#define STM32H7_PB12_FUNC_I2C2_SMBA 0x1c05 -#define STM32H7_PB12_FUNC_SPI2_NSS_I2S2_WS 0x1c06 -#define STM32H7_PB12_FUNC_DFSDM_DATIN1 0x1c07 -#define STM32H7_PB12_FUNC_USART3_CK 0x1c08 -#define STM32H7_PB12_FUNC_CAN2_RX 0x1c0a -#define STM32H7_PB12_FUNC_OTG_HS_ULPI_D5 0x1c0b -#define STM32H7_PB12_FUNC_ETH_MII_TXD0_ETH_RMII_TXD0 0x1c0c -#define STM32H7_PB12_FUNC_OTG_HS_ID 0x1c0d -#define STM32H7_PB12_FUNC_TIM1_BKIN_COMP12 0x1c0e -#define STM32H7_PB12_FUNC_UART5_RX 0x1c0f -#define STM32H7_PB12_FUNC_EVENTOUT 0x1c10 -#define STM32H7_PB12_FUNC_ANALOG 0x1c11 - -#define STM32H7_PB13_FUNC_GPIO 0x1d00 -#define STM32H7_PB13_FUNC_TIM1_CH1N 0x1d02 -#define STM32H7_PB13_FUNC_LPTIM2_OUT 0x1d04 -#define STM32H7_PB13_FUNC_SPI2_SCK_I2S2_CK 0x1d06 -#define STM32H7_PB13_FUNC_DFSDM_CKIN1 0x1d07 -#define STM32H7_PB13_FUNC_USART3_CTS_NSS 0x1d08 -#define STM32H7_PB13_FUNC_CAN2_TX 0x1d0a -#define STM32H7_PB13_FUNC_OTG_HS_ULPI_D6 0x1d0b -#define STM32H7_PB13_FUNC_ETH_MII_TXD1_ETH_RMII_TXD1 0x1d0c -#define STM32H7_PB13_FUNC_UART5_TX 0x1d0f -#define STM32H7_PB13_FUNC_EVENTOUT 0x1d10 -#define STM32H7_PB13_FUNC_ANALOG 0x1d11 - -#define STM32H7_PB14_FUNC_GPIO 0x1e00 -#define STM32H7_PB14_FUNC_TIM1_CH2N 0x1e02 -#define STM32H7_PB14_FUNC_TIM8_CH2N 0x1e04 -#define STM32H7_PB14_FUNC_USART1_TX 0x1e05 -#define STM32H7_PB14_FUNC_SPI2_MISO_I2S2_SDI 0x1e06 -#define STM32H7_PB14_FUNC_DFSDM_DATIN2 0x1e07 -#define STM32H7_PB14_FUNC_USART3_RTS 0x1e08 -#define STM32H7_PB14_FUNC_UART4_RTS 0x1e09 -#define STM32H7_PB14_FUNC_SDMMC2_D0 0x1e0a -#define STM32H7_PB14_FUNC_OTG_HS_DM 0x1e0d -#define STM32H7_PB14_FUNC_EVENTOUT 0x1e10 -#define STM32H7_PB14_FUNC_ANALOG 0x1e11 - -#define STM32H7_PB15_FUNC_GPIO 0x1f00 -#define STM32H7_PB15_FUNC_RTC_REFIN 0x1f01 -#define STM32H7_PB15_FUNC_TIM1_CH3N 0x1f02 -#define STM32H7_PB15_FUNC_TIM8_CH3N 0x1f04 -#define STM32H7_PB15_FUNC_USART1_RX 0x1f05 -#define STM32H7_PB15_FUNC_SPI2_MOSI_I2S2_SDO 0x1f06 -#define STM32H7_PB15_FUNC_DFSDM_CKIN2 0x1f07 -#define STM32H7_PB15_FUNC_UART4_CTS 0x1f09 -#define STM32H7_PB15_FUNC_SDMMC2_D1 0x1f0a -#define STM32H7_PB15_FUNC_OTG_HS_DP 0x1f0d -#define STM32H7_PB15_FUNC_EVENTOUT 0x1f10 -#define STM32H7_PB15_FUNC_ANALOG 0x1f11 - -#define STM32H7_PC0_FUNC_GPIO 0x2000 -#define STM32H7_PC0_FUNC_DFSDM_CKIN0 0x2004 -#define STM32H7_PC0_FUNC_DFSDM_DATIN4 0x2007 -#define STM32H7_PC0_FUNC_SAI2_FS_B 0x2009 -#define STM32H7_PC0_FUNC_OTG_HS_ULPI_STP 0x200b -#define STM32H7_PC0_FUNC_FMC_SDNWE 0x200d -#define STM32H7_PC0_FUNC_LCD_R5 0x200f -#define STM32H7_PC0_FUNC_EVENTOUT 0x2010 -#define STM32H7_PC0_FUNC_ANALOG 0x2011 - -#define STM32H7_PC1_FUNC_GPIO 0x2100 -#define STM32H7_PC1_FUNC_TRACED0 0x2101 -#define STM32H7_PC1_FUNC_SAI1_D1 0x2103 -#define STM32H7_PC1_FUNC_DFSDM_DATIN0 0x2104 -#define STM32H7_PC1_FUNC_DFSDM_CKIN4 0x2105 -#define STM32H7_PC1_FUNC_SPI2_MOSI_I2S2_SDO 0x2106 -#define STM32H7_PC1_FUNC_SAI1_SD_A 0x2107 -#define STM32H7_PC1_FUNC_SAI4_SD_A 0x2109 -#define STM32H7_PC1_FUNC_SDMMC2_CK 0x210a -#define STM32H7_PC1_FUNC_SAI4_D1 0x210b -#define STM32H7_PC1_FUNC_ETH_MDC 0x210c -#define STM32H7_PC1_FUNC_MDIOS_MDC 0x210d -#define STM32H7_PC1_FUNC_EVENTOUT 0x2110 -#define STM32H7_PC1_FUNC_ANALOG 0x2111 - -#define STM32H7_PC2_FUNC_GPIO 0x2200 -#define STM32H7_PC2_FUNC_DFSDM_CKIN1 0x2204 -#define STM32H7_PC2_FUNC_SPI2_MISO_I2S2_SDI 0x2206 -#define STM32H7_PC2_FUNC_DFSDM_CKOUT 0x2207 -#define STM32H7_PC2_FUNC_OTG_HS_ULPI_DIR 0x220b -#define STM32H7_PC2_FUNC_ETH_MII_TXD2 0x220c -#define STM32H7_PC2_FUNC_FMC_SDNE0 0x220d -#define STM32H7_PC2_FUNC_EVENTOUT 0x2210 -#define STM32H7_PC2_FUNC_ANALOG 0x2211 - -#define STM32H7_PC3_FUNC_GPIO 0x2300 -#define STM32H7_PC3_FUNC_DFSDM_DATIN1 0x2304 -#define STM32H7_PC3_FUNC_SPI2_MOSI_I2S2_SDO 0x2306 -#define STM32H7_PC3_FUNC_OTG_HS_ULPI_NXT 0x230b -#define STM32H7_PC3_FUNC_ETH_MII_TX_CLK 0x230c -#define STM32H7_PC3_FUNC_FMC_SDCKE0 0x230d -#define STM32H7_PC3_FUNC_EVENTOUT 0x2310 -#define STM32H7_PC3_FUNC_ANALOG 0x2311 - -#define STM32H7_PC4_FUNC_GPIO 0x2400 -#define STM32H7_PC4_FUNC_DFSDM_CKIN2 0x2404 -#define STM32H7_PC4_FUNC_I2S1_MCK 0x2406 -#define STM32H7_PC4_FUNC_SPDIFRX_IN2 0x240a -#define STM32H7_PC4_FUNC_ETH_MII_RXD0_ETH_RMII_RXD0 0x240c -#define STM32H7_PC4_FUNC_FMC_SDNE0 0x240d -#define STM32H7_PC4_FUNC_EVENTOUT 0x2410 -#define STM32H7_PC4_FUNC_ANALOG 0x2411 - -#define STM32H7_PC5_FUNC_GPIO 0x2500 -#define STM32H7_PC5_FUNC_SAI1_D3 0x2503 -#define STM32H7_PC5_FUNC_DFSDM_DATIN2 0x2504 -#define STM32H7_PC5_FUNC_SPDIFRX_IN3 0x250a -#define STM32H7_PC5_FUNC_SAI4_D3 0x250b -#define STM32H7_PC5_FUNC_ETH_MII_RXD1_ETH_RMII_RXD1 0x250c -#define STM32H7_PC5_FUNC_FMC_SDCKE0 0x250d -#define STM32H7_PC5_FUNC_COMP_1_OUT 0x250e -#define STM32H7_PC5_FUNC_EVENTOUT 0x2510 -#define STM32H7_PC5_FUNC_ANALOG 0x2511 - -#define STM32H7_PC6_FUNC_GPIO 0x2600 -#define STM32H7_PC6_FUNC_HRTIM_CHA1 0x2602 -#define STM32H7_PC6_FUNC_TIM3_CH1 0x2603 -#define STM32H7_PC6_FUNC_TIM8_CH1 0x2604 -#define STM32H7_PC6_FUNC_DFSDM_CKIN3 0x2605 -#define STM32H7_PC6_FUNC_I2S2_MCK 0x2606 -#define STM32H7_PC6_FUNC_USART6_TX 0x2608 -#define STM32H7_PC6_FUNC_SDMMC1_D0DIR 0x2609 -#define STM32H7_PC6_FUNC_FMC_NWAIT 0x260a -#define STM32H7_PC6_FUNC_SDMMC2_D6 0x260b -#define STM32H7_PC6_FUNC_SDMMC1_D6 0x260d -#define STM32H7_PC6_FUNC_DCMI_D0 0x260e -#define STM32H7_PC6_FUNC_LCD_HSYNC 0x260f -#define STM32H7_PC6_FUNC_EVENTOUT 0x2610 -#define STM32H7_PC6_FUNC_ANALOG 0x2611 - -#define STM32H7_PC7_FUNC_GPIO 0x2700 -#define STM32H7_PC7_FUNC_TRGIO 0x2701 -#define STM32H7_PC7_FUNC_HRTIM_CHA2 0x2702 -#define STM32H7_PC7_FUNC_TIM3_CH2 0x2703 -#define STM32H7_PC7_FUNC_TIM8_CH2 0x2704 -#define STM32H7_PC7_FUNC_DFSDM_DATIN3 0x2705 -#define STM32H7_PC7_FUNC_I2S3_MCK 0x2707 -#define STM32H7_PC7_FUNC_USART6_RX 0x2708 -#define STM32H7_PC7_FUNC_SDMMC1_D123DIR 0x2709 -#define STM32H7_PC7_FUNC_FMC_NE1 0x270a -#define STM32H7_PC7_FUNC_SDMMC2_D7 0x270b -#define STM32H7_PC7_FUNC_SWPMI_TX 0x270c -#define STM32H7_PC7_FUNC_SDMMC1_D7 0x270d -#define STM32H7_PC7_FUNC_DCMI_D1 0x270e -#define STM32H7_PC7_FUNC_LCD_G6 0x270f -#define STM32H7_PC7_FUNC_EVENTOUT 0x2710 -#define STM32H7_PC7_FUNC_ANALOG 0x2711 - -#define STM32H7_PC8_FUNC_GPIO 0x2800 -#define STM32H7_PC8_FUNC_TRACED1 0x2801 -#define STM32H7_PC8_FUNC_HRTIM_CHB1 0x2802 -#define STM32H7_PC8_FUNC_TIM3_CH3 0x2803 -#define STM32H7_PC8_FUNC_TIM8_CH3 0x2804 -#define STM32H7_PC8_FUNC_USART6_CK 0x2808 -#define STM32H7_PC8_FUNC_UART5_RTS 0x2809 -#define STM32H7_PC8_FUNC_FMC_NE2_FMC_NCE 0x280a -#define STM32H7_PC8_FUNC_SWPMI_RX 0x280c -#define STM32H7_PC8_FUNC_SDMMC1_D0 0x280d -#define STM32H7_PC8_FUNC_DCMI_D2 0x280e -#define STM32H7_PC8_FUNC_EVENTOUT 0x2810 -#define STM32H7_PC8_FUNC_ANALOG 0x2811 - -#define STM32H7_PC9_FUNC_GPIO 0x2900 -#define STM32H7_PC9_FUNC_MCO2 0x2901 -#define STM32H7_PC9_FUNC_TIM3_CH4 0x2903 -#define STM32H7_PC9_FUNC_TIM8_CH4 0x2904 -#define STM32H7_PC9_FUNC_I2C3_SDA 0x2905 -#define STM32H7_PC9_FUNC_I2S_CKIN 0x2906 -#define STM32H7_PC9_FUNC_UART5_CTS 0x2909 -#define STM32H7_PC9_FUNC_QUADSPI_BK1_IO0 0x290a -#define STM32H7_PC9_FUNC_LCD_G3 0x290b -#define STM32H7_PC9_FUNC_SWPMI_SUSPEND 0x290c -#define STM32H7_PC9_FUNC_SDMMC1_D1 0x290d -#define STM32H7_PC9_FUNC_DCMI_D3 0x290e -#define STM32H7_PC9_FUNC_LCD_B2 0x290f -#define STM32H7_PC9_FUNC_EVENTOUT 0x2910 -#define STM32H7_PC9_FUNC_ANALOG 0x2911 - -#define STM32H7_PC10_FUNC_GPIO 0x2a00 -#define STM32H7_PC10_FUNC_HRTIM_EEV1 0x2a03 -#define STM32H7_PC10_FUNC_DFSDM_CKIN5 0x2a04 -#define STM32H7_PC10_FUNC_SPI3_SCK_I2S3_CK 0x2a07 -#define STM32H7_PC10_FUNC_USART3_TX 0x2a08 -#define STM32H7_PC10_FUNC_UART4_TX 0x2a09 -#define STM32H7_PC10_FUNC_QUADSPI_BK1_IO1 0x2a0a -#define STM32H7_PC10_FUNC_SDMMC1_D2 0x2a0d -#define STM32H7_PC10_FUNC_DCMI_D8 0x2a0e -#define STM32H7_PC10_FUNC_LCD_R2 0x2a0f -#define STM32H7_PC10_FUNC_EVENTOUT 0x2a10 -#define STM32H7_PC10_FUNC_ANALOG 0x2a11 - -#define STM32H7_PC11_FUNC_GPIO 0x2b00 -#define STM32H7_PC11_FUNC_HRTIM_FLT2 0x2b03 -#define STM32H7_PC11_FUNC_DFSDM_DATIN5 0x2b04 -#define STM32H7_PC11_FUNC_SPI3_MISO_I2S3_SDI 0x2b07 -#define STM32H7_PC11_FUNC_USART3_RX 0x2b08 -#define STM32H7_PC11_FUNC_UART4_RX 0x2b09 -#define STM32H7_PC11_FUNC_QUADSPI_BK2_NCS 0x2b0a -#define STM32H7_PC11_FUNC_SDMMC1_D3 0x2b0d -#define STM32H7_PC11_FUNC_DCMI_D4 0x2b0e -#define STM32H7_PC11_FUNC_EVENTOUT 0x2b10 -#define STM32H7_PC11_FUNC_ANALOG 0x2b11 - -#define STM32H7_PC12_FUNC_GPIO 0x2c00 -#define STM32H7_PC12_FUNC_TRACED3 0x2c01 -#define STM32H7_PC12_FUNC_HRTIM_EEV2 0x2c03 -#define STM32H7_PC12_FUNC_SPI3_MOSI_I2S3_SDO 0x2c07 -#define STM32H7_PC12_FUNC_USART3_CK 0x2c08 -#define STM32H7_PC12_FUNC_UART5_TX 0x2c09 -#define STM32H7_PC12_FUNC_SDMMC1_CK 0x2c0d -#define STM32H7_PC12_FUNC_DCMI_D9 0x2c0e -#define STM32H7_PC12_FUNC_EVENTOUT 0x2c10 -#define STM32H7_PC12_FUNC_ANALOG 0x2c11 - -#define STM32H7_PC13_FUNC_GPIO 0x2d00 -#define STM32H7_PC13_FUNC_EVENTOUT 0x2d10 -#define STM32H7_PC13_FUNC_ANALOG 0x2d11 - -#define STM32H7_PC14_FUNC_GPIO 0x2e00 -#define STM32H7_PC14_FUNC_EVENTOUT 0x2e10 -#define STM32H7_PC14_FUNC_ANALOG 0x2e11 - -#define STM32H7_PC15_FUNC_GPIO 0x2f00 -#define STM32H7_PC15_FUNC_EVENTOUT 0x2f10 -#define STM32H7_PC15_FUNC_ANALOG 0x2f11 - -#define STM32H7_PD0_FUNC_GPIO 0x3000 -#define STM32H7_PD0_FUNC_DFSDM_CKIN6 0x3004 -#define STM32H7_PD0_FUNC_SAI3_SCK_A 0x3007 -#define STM32H7_PD0_FUNC_UART4_RX 0x3009 -#define STM32H7_PD0_FUNC_CAN1_RX 0x300a -#define STM32H7_PD0_FUNC_FMC_D2_FMC_DA2 0x300d -#define STM32H7_PD0_FUNC_EVENTOUT 0x3010 -#define STM32H7_PD0_FUNC_ANALOG 0x3011 - -#define STM32H7_PD1_FUNC_GPIO 0x3100 -#define STM32H7_PD1_FUNC_DFSDM_DATIN6 0x3104 -#define STM32H7_PD1_FUNC_SAI3_SD_A 0x3107 -#define STM32H7_PD1_FUNC_UART4_TX 0x3109 -#define STM32H7_PD1_FUNC_CAN1_TX 0x310a -#define STM32H7_PD1_FUNC_FMC_D3_FMC_DA3 0x310d -#define STM32H7_PD1_FUNC_EVENTOUT 0x3110 -#define STM32H7_PD1_FUNC_ANALOG 0x3111 - -#define STM32H7_PD2_FUNC_GPIO 0x3200 -#define STM32H7_PD2_FUNC_TRACED2 0x3201 -#define STM32H7_PD2_FUNC_TIM3_ETR 0x3203 -#define STM32H7_PD2_FUNC_UART5_RX 0x3209 -#define STM32H7_PD2_FUNC_SDMMC1_CMD 0x320d -#define STM32H7_PD2_FUNC_DCMI_D11 0x320e -#define STM32H7_PD2_FUNC_EVENTOUT 0x3210 -#define STM32H7_PD2_FUNC_ANALOG 0x3211 - -#define STM32H7_PD3_FUNC_GPIO 0x3300 -#define STM32H7_PD3_FUNC_DFSDM_CKOUT 0x3304 -#define STM32H7_PD3_FUNC_SPI2_SCK_I2S2_CK 0x3306 -#define STM32H7_PD3_FUNC_USART2_CTS_NSS 0x3308 -#define STM32H7_PD3_FUNC_FMC_CLK 0x330d -#define STM32H7_PD3_FUNC_DCMI_D5 0x330e -#define STM32H7_PD3_FUNC_LCD_G7 0x330f -#define STM32H7_PD3_FUNC_EVENTOUT 0x3310 -#define STM32H7_PD3_FUNC_ANALOG 0x3311 - -#define STM32H7_PD4_FUNC_GPIO 0x3400 -#define STM32H7_PD4_FUNC_HRTIM_FLT3 0x3403 -#define STM32H7_PD4_FUNC_SAI3_FS_A 0x3407 -#define STM32H7_PD4_FUNC_USART2_RTS 0x3408 -#define STM32H7_PD4_FUNC_CAN1_RXFD 0x340a -#define STM32H7_PD4_FUNC_FMC_NOE 0x340d -#define STM32H7_PD4_FUNC_EVENTOUT 0x3410 -#define STM32H7_PD4_FUNC_ANALOG 0x3411 - -#define STM32H7_PD5_FUNC_GPIO 0x3500 -#define STM32H7_PD5_FUNC_HRTIM_EEV3 0x3503 -#define STM32H7_PD5_FUNC_USART2_TX 0x3508 -#define STM32H7_PD5_FUNC_CAN1_TXFD 0x350a -#define STM32H7_PD5_FUNC_FMC_NWE 0x350d -#define STM32H7_PD5_FUNC_EVENTOUT 0x3510 -#define STM32H7_PD5_FUNC_ANALOG 0x3511 - -#define STM32H7_PD6_FUNC_GPIO 0x3600 -#define STM32H7_PD6_FUNC_SAI1_D1 0x3603 -#define STM32H7_PD6_FUNC_DFSDM_CKIN4 0x3604 -#define STM32H7_PD6_FUNC_DFSDM_DATIN1 0x3605 -#define STM32H7_PD6_FUNC_SPI3_MOSI_I2S3_SDO 0x3606 -#define STM32H7_PD6_FUNC_SAI1_SD_A 0x3607 -#define STM32H7_PD6_FUNC_USART2_RX 0x3608 -#define STM32H7_PD6_FUNC_SAI4_SD_A 0x3609 -#define STM32H7_PD6_FUNC_CAN2_RXFD 0x360a -#define STM32H7_PD6_FUNC_SAI4_D1 0x360b -#define STM32H7_PD6_FUNC_SDMMC2_CK 0x360c -#define STM32H7_PD6_FUNC_FMC_NWAIT 0x360d -#define STM32H7_PD6_FUNC_DCMI_D10 0x360e -#define STM32H7_PD6_FUNC_LCD_B2 0x360f -#define STM32H7_PD6_FUNC_EVENTOUT 0x3610 -#define STM32H7_PD6_FUNC_ANALOG 0x3611 - -#define STM32H7_PD7_FUNC_GPIO 0x3700 -#define STM32H7_PD7_FUNC_DFSDM_DATIN4 0x3704 -#define STM32H7_PD7_FUNC_SPI1_MOSI_I2S1_SDO 0x3706 -#define STM32H7_PD7_FUNC_DFSDM_CKIN1 0x3707 -#define STM32H7_PD7_FUNC_USART2_CK 0x3708 -#define STM32H7_PD7_FUNC_SPDIFRX_IN0 0x370a -#define STM32H7_PD7_FUNC_SDMMC2_CMD 0x370c -#define STM32H7_PD7_FUNC_FMC_NE1 0x370d -#define STM32H7_PD7_FUNC_EVENTOUT 0x3710 -#define STM32H7_PD7_FUNC_ANALOG 0x3711 - -#define STM32H7_PD8_FUNC_GPIO 0x3800 -#define STM32H7_PD8_FUNC_DFSDM_CKIN3 0x3804 -#define STM32H7_PD8_FUNC_SAI3_SCK_B 0x3807 -#define STM32H7_PD8_FUNC_USART3_TX 0x3808 -#define STM32H7_PD8_FUNC_SPDIFRX_IN1 0x380a -#define STM32H7_PD8_FUNC_FMC_D13_FMC_DA13 0x380d -#define STM32H7_PD8_FUNC_EVENTOUT 0x3810 -#define STM32H7_PD8_FUNC_ANALOG 0x3811 - -#define STM32H7_PD9_FUNC_GPIO 0x3900 -#define STM32H7_PD9_FUNC_DFSDM_DATIN3 0x3904 -#define STM32H7_PD9_FUNC_SAI3_SD_B 0x3907 -#define STM32H7_PD9_FUNC_USART3_RX 0x3908 -#define STM32H7_PD9_FUNC_CAN2_RXFD 0x390a -#define STM32H7_PD9_FUNC_FMC_D14_FMC_DA14 0x390d -#define STM32H7_PD9_FUNC_EVENTOUT 0x3910 -#define STM32H7_PD9_FUNC_ANALOG 0x3911 - -#define STM32H7_PD10_FUNC_GPIO 0x3a00 -#define STM32H7_PD10_FUNC_DFSDM_CKOUT 0x3a04 -#define STM32H7_PD10_FUNC_SAI3_FS_B 0x3a07 -#define STM32H7_PD10_FUNC_USART3_CK 0x3a08 -#define STM32H7_PD10_FUNC_CAN2_TXFD 0x3a0a -#define STM32H7_PD10_FUNC_FMC_D15_FMC_DA15 0x3a0d -#define STM32H7_PD10_FUNC_LCD_B3 0x3a0f -#define STM32H7_PD10_FUNC_EVENTOUT 0x3a10 -#define STM32H7_PD10_FUNC_ANALOG 0x3a11 - -#define STM32H7_PD11_FUNC_GPIO 0x3b00 -#define STM32H7_PD11_FUNC_LPTIM2_IN2 0x3b04 -#define STM32H7_PD11_FUNC_I2C4_SMBA 0x3b05 -#define STM32H7_PD11_FUNC_USART3_CTS_NSS 0x3b08 -#define STM32H7_PD11_FUNC_QUADSPI_BK1_IO0 0x3b0a -#define STM32H7_PD11_FUNC_SAI2_SD_A 0x3b0b -#define STM32H7_PD11_FUNC_FMC_A16 0x3b0d -#define STM32H7_PD11_FUNC_EVENTOUT 0x3b10 -#define STM32H7_PD11_FUNC_ANALOG 0x3b11 - -#define STM32H7_PD12_FUNC_GPIO 0x3c00 -#define STM32H7_PD12_FUNC_LPTIM1_IN1 0x3c02 -#define STM32H7_PD12_FUNC_TIM4_CH1 0x3c03 -#define STM32H7_PD12_FUNC_LPTIM2_IN1 0x3c04 -#define STM32H7_PD12_FUNC_I2C4_SCL 0x3c05 -#define STM32H7_PD12_FUNC_USART3_RTS 0x3c08 -#define STM32H7_PD12_FUNC_QUADSPI_BK1_IO1 0x3c0a -#define STM32H7_PD12_FUNC_SAI2_FS_A 0x3c0b -#define STM32H7_PD12_FUNC_FMC_A17 0x3c0d -#define STM32H7_PD12_FUNC_EVENTOUT 0x3c10 -#define STM32H7_PD12_FUNC_ANALOG 0x3c11 - -#define STM32H7_PD13_FUNC_GPIO 0x3d00 -#define STM32H7_PD13_FUNC_LPTIM1_OUT 0x3d02 -#define STM32H7_PD13_FUNC_TIM4_CH2 0x3d03 -#define STM32H7_PD13_FUNC_I2C4_SDA 0x3d05 -#define STM32H7_PD13_FUNC_QUADSPI_BK1_IO3 0x3d0a -#define STM32H7_PD13_FUNC_SAI2_SCK_A 0x3d0b -#define STM32H7_PD13_FUNC_FMC_A18 0x3d0d -#define STM32H7_PD13_FUNC_EVENTOUT 0x3d10 -#define STM32H7_PD13_FUNC_ANALOG 0x3d11 - -#define STM32H7_PD14_FUNC_GPIO 0x3e00 -#define STM32H7_PD14_FUNC_TIM4_CH3 0x3e03 -#define STM32H7_PD14_FUNC_SAI3_MCLK_B 0x3e07 -#define STM32H7_PD14_FUNC_UART8_CTS 0x3e09 -#define STM32H7_PD14_FUNC_FMC_D0_FMC_DA0 0x3e0d -#define STM32H7_PD14_FUNC_EVENTOUT 0x3e10 -#define STM32H7_PD14_FUNC_ANALOG 0x3e11 - -#define STM32H7_PD15_FUNC_GPIO 0x3f00 -#define STM32H7_PD15_FUNC_TIM4_CH4 0x3f03 -#define STM32H7_PD15_FUNC_SAI3_MCLK_A 0x3f07 -#define STM32H7_PD15_FUNC_UART8_RTS 0x3f09 -#define STM32H7_PD15_FUNC_FMC_D1_FMC_DA1 0x3f0d -#define STM32H7_PD15_FUNC_EVENTOUT 0x3f10 -#define STM32H7_PD15_FUNC_ANALOG 0x3f11 - -#define STM32H7_PE0_FUNC_GPIO 0x4000 -#define STM32H7_PE0_FUNC_LPTIM1_ETR 0x4002 -#define STM32H7_PE0_FUNC_TIM4_ETR 0x4003 -#define STM32H7_PE0_FUNC_HRTIM_SCIN 0x4004 -#define STM32H7_PE0_FUNC_LPTIM2_ETR 0x4005 -#define STM32H7_PE0_FUNC_UART8_RX 0x4009 -#define STM32H7_PE0_FUNC_CAN1_RXFD 0x400a -#define STM32H7_PE0_FUNC_SAI2_MCK_A 0x400b -#define STM32H7_PE0_FUNC_FMC_NBL0 0x400d -#define STM32H7_PE0_FUNC_DCMI_D2 0x400e -#define STM32H7_PE0_FUNC_EVENTOUT 0x4010 -#define STM32H7_PE0_FUNC_ANALOG 0x4011 - -#define STM32H7_PE1_FUNC_GPIO 0x4100 -#define STM32H7_PE1_FUNC_LPTIM1_IN2 0x4102 -#define STM32H7_PE1_FUNC_HRTIM_SCOUT 0x4104 -#define STM32H7_PE1_FUNC_UART8_TX 0x4109 -#define STM32H7_PE1_FUNC_CAN1_TXFD 0x410a -#define STM32H7_PE1_FUNC_FMC_NBL1 0x410d -#define STM32H7_PE1_FUNC_DCMI_D3 0x410e -#define STM32H7_PE1_FUNC_EVENTOUT 0x4110 -#define STM32H7_PE1_FUNC_ANALOG 0x4111 - -#define STM32H7_PE2_FUNC_GPIO 0x4200 -#define STM32H7_PE2_FUNC_TRACECLK 0x4201 -#define STM32H7_PE2_FUNC_SAI1_CK1 0x4203 -#define STM32H7_PE2_FUNC_SPI4_SCK 0x4206 -#define STM32H7_PE2_FUNC_SAI1_MCLK_A 0x4207 -#define STM32H7_PE2_FUNC_SAI4_MCLK_A 0x4209 -#define STM32H7_PE2_FUNC_QUADSPI_BK1_IO2 0x420a -#define STM32H7_PE2_FUNC_SAI4_CK1 0x420b -#define STM32H7_PE2_FUNC_ETH_MII_TXD3 0x420c -#define STM32H7_PE2_FUNC_FMC_A23 0x420d -#define STM32H7_PE2_FUNC_EVENTOUT 0x4210 -#define STM32H7_PE2_FUNC_ANALOG 0x4211 - -#define STM32H7_PE3_FUNC_GPIO 0x4300 -#define STM32H7_PE3_FUNC_TRACED0 0x4301 -#define STM32H7_PE3_FUNC_TIM15_BKIN 0x4305 -#define STM32H7_PE3_FUNC_SAI1_SD_B 0x4307 -#define STM32H7_PE3_FUNC_SAI4_SD_B 0x4309 -#define STM32H7_PE3_FUNC_FMC_A19 0x430d -#define STM32H7_PE3_FUNC_EVENTOUT 0x4310 -#define STM32H7_PE3_FUNC_ANALOG 0x4311 - -#define STM32H7_PE4_FUNC_GPIO 0x4400 -#define STM32H7_PE4_FUNC_TRACED1 0x4401 -#define STM32H7_PE4_FUNC_SAI1_D2 0x4403 -#define STM32H7_PE4_FUNC_DFSDM_DATIN3 0x4404 -#define STM32H7_PE4_FUNC_TIM15_CH1N 0x4405 -#define STM32H7_PE4_FUNC_SPI4_NSS 0x4406 -#define STM32H7_PE4_FUNC_SAI1_FS_A 0x4407 -#define STM32H7_PE4_FUNC_SAI4_FS_A 0x4409 -#define STM32H7_PE4_FUNC_SAI4_D2 0x440b -#define STM32H7_PE4_FUNC_FMC_A20 0x440d -#define STM32H7_PE4_FUNC_DCMI_D4 0x440e -#define STM32H7_PE4_FUNC_LCD_B0 0x440f -#define STM32H7_PE4_FUNC_EVENTOUT 0x4410 -#define STM32H7_PE4_FUNC_ANALOG 0x4411 - -#define STM32H7_PE5_FUNC_GPIO 0x4500 -#define STM32H7_PE5_FUNC_TRACED2 0x4501 -#define STM32H7_PE5_FUNC_SAI1_CK2 0x4503 -#define STM32H7_PE5_FUNC_DFSDM_CKIN3 0x4504 -#define STM32H7_PE5_FUNC_TIM15_CH1 0x4505 -#define STM32H7_PE5_FUNC_SPI4_MISO 0x4506 -#define STM32H7_PE5_FUNC_SAI1_SCK_A 0x4507 -#define STM32H7_PE5_FUNC_SAI4_SCK_A 0x4509 -#define STM32H7_PE5_FUNC_SAI4_CK2 0x450b -#define STM32H7_PE5_FUNC_FMC_A21 0x450d -#define STM32H7_PE5_FUNC_DCMI_D6 0x450e -#define STM32H7_PE5_FUNC_LCD_G0 0x450f -#define STM32H7_PE5_FUNC_EVENTOUT 0x4510 -#define STM32H7_PE5_FUNC_ANALOG 0x4511 - -#define STM32H7_PE6_FUNC_GPIO 0x4600 -#define STM32H7_PE6_FUNC_TRACED3 0x4601 -#define STM32H7_PE6_FUNC_TIM1_BKIN2 0x4602 -#define STM32H7_PE6_FUNC_SAI1_D1 0x4603 -#define STM32H7_PE6_FUNC_TIM15_CH2 0x4605 -#define STM32H7_PE6_FUNC_SPI4_MOSI 0x4606 -#define STM32H7_PE6_FUNC_SAI1_SD_A 0x4607 -#define STM32H7_PE6_FUNC_SAI4_SD_A 0x4609 -#define STM32H7_PE6_FUNC_SAI4_D1 0x460a -#define STM32H7_PE6_FUNC_SAI2_MCK_B 0x460b -#define STM32H7_PE6_FUNC_TIM1_BKIN2_COMP12 0x460c -#define STM32H7_PE6_FUNC_FMC_A22 0x460d -#define STM32H7_PE6_FUNC_DCMI_D7 0x460e -#define STM32H7_PE6_FUNC_LCD_G1 0x460f -#define STM32H7_PE6_FUNC_EVENTOUT 0x4610 -#define STM32H7_PE6_FUNC_ANALOG 0x4611 - -#define STM32H7_PE7_FUNC_GPIO 0x4700 -#define STM32H7_PE7_FUNC_TIM1_ETR 0x4702 -#define STM32H7_PE7_FUNC_DFSDM_DATIN2 0x4704 -#define STM32H7_PE7_FUNC_UART7_RX 0x4708 -#define STM32H7_PE7_FUNC_QUADSPI_BK2_IO0 0x470b -#define STM32H7_PE7_FUNC_FMC_D4_FMC_DA4 0x470d -#define STM32H7_PE7_FUNC_EVENTOUT 0x4710 -#define STM32H7_PE7_FUNC_ANALOG 0x4711 - -#define STM32H7_PE8_FUNC_GPIO 0x4800 -#define STM32H7_PE8_FUNC_TIM1_CH1N 0x4802 -#define STM32H7_PE8_FUNC_DFSDM_CKIN2 0x4804 -#define STM32H7_PE8_FUNC_UART7_TX 0x4808 -#define STM32H7_PE8_FUNC_QUADSPI_BK2_IO1 0x480b -#define STM32H7_PE8_FUNC_FMC_D5_FMC_DA5 0x480d -#define STM32H7_PE8_FUNC_COMP_2_OUT 0x480e -#define STM32H7_PE8_FUNC_EVENTOUT 0x4810 -#define STM32H7_PE8_FUNC_ANALOG 0x4811 - -#define STM32H7_PE9_FUNC_GPIO 0x4900 -#define STM32H7_PE9_FUNC_TIM1_CH1 0x4902 -#define STM32H7_PE9_FUNC_DFSDM_CKOUT 0x4904 -#define STM32H7_PE9_FUNC_UART7_RTS 0x4908 -#define STM32H7_PE9_FUNC_QUADSPI_BK2_IO2 0x490b -#define STM32H7_PE9_FUNC_FMC_D6_FMC_DA6 0x490d -#define STM32H7_PE9_FUNC_EVENTOUT 0x4910 -#define STM32H7_PE9_FUNC_ANALOG 0x4911 - -#define STM32H7_PE10_FUNC_GPIO 0x4a00 -#define STM32H7_PE10_FUNC_TIM1_CH2N 0x4a02 -#define STM32H7_PE10_FUNC_DFSDM_DATIN4 0x4a04 -#define STM32H7_PE10_FUNC_UART7_CTS 0x4a08 -#define STM32H7_PE10_FUNC_QUADSPI_BK2_IO3 0x4a0b -#define STM32H7_PE10_FUNC_FMC_D7_FMC_DA7 0x4a0d -#define STM32H7_PE10_FUNC_EVENTOUT 0x4a10 -#define STM32H7_PE10_FUNC_ANALOG 0x4a11 - -#define STM32H7_PE11_FUNC_GPIO 0x4b00 -#define STM32H7_PE11_FUNC_TIM1_CH2 0x4b02 -#define STM32H7_PE11_FUNC_DFSDM_CKIN4 0x4b04 -#define STM32H7_PE11_FUNC_SPI4_NSS 0x4b06 -#define STM32H7_PE11_FUNC_SAI2_SD_B 0x4b0b -#define STM32H7_PE11_FUNC_FMC_D8_FMC_DA8 0x4b0d -#define STM32H7_PE11_FUNC_LCD_G3 0x4b0f -#define STM32H7_PE11_FUNC_EVENTOUT 0x4b10 -#define STM32H7_PE11_FUNC_ANALOG 0x4b11 - -#define STM32H7_PE12_FUNC_GPIO 0x4c00 -#define STM32H7_PE12_FUNC_TIM1_CH3N 0x4c02 -#define STM32H7_PE12_FUNC_DFSDM_DATIN5 0x4c04 -#define STM32H7_PE12_FUNC_SPI4_SCK 0x4c06 -#define STM32H7_PE12_FUNC_SAI2_SCK_B 0x4c0b -#define STM32H7_PE12_FUNC_FMC_D9_FMC_DA9 0x4c0d -#define STM32H7_PE12_FUNC_COMP_1_OUT 0x4c0e -#define STM32H7_PE12_FUNC_LCD_B4 0x4c0f -#define STM32H7_PE12_FUNC_EVENTOUT 0x4c10 -#define STM32H7_PE12_FUNC_ANALOG 0x4c11 - -#define STM32H7_PE13_FUNC_GPIO 0x4d00 -#define STM32H7_PE13_FUNC_TIM1_CH3 0x4d02 -#define STM32H7_PE13_FUNC_DFSDM_CKIN5 0x4d04 -#define STM32H7_PE13_FUNC_SPI4_MISO 0x4d06 -#define STM32H7_PE13_FUNC_SAI2_FS_B 0x4d0b -#define STM32H7_PE13_FUNC_FMC_D10_FMC_DA10 0x4d0d -#define STM32H7_PE13_FUNC_COMP_2_OUT 0x4d0e -#define STM32H7_PE13_FUNC_LCD_DE 0x4d0f -#define STM32H7_PE13_FUNC_EVENTOUT 0x4d10 -#define STM32H7_PE13_FUNC_ANALOG 0x4d11 - -#define STM32H7_PE14_FUNC_GPIO 0x4e00 -#define STM32H7_PE14_FUNC_TIM1_CH4 0x4e02 -#define STM32H7_PE14_FUNC_SPI4_MOSI 0x4e06 -#define STM32H7_PE14_FUNC_SAI2_MCK_B 0x4e0b -#define STM32H7_PE14_FUNC_FMC_D11_FMC_DA11 0x4e0d -#define STM32H7_PE14_FUNC_LCD_CLK 0x4e0f -#define STM32H7_PE14_FUNC_EVENTOUT 0x4e10 -#define STM32H7_PE14_FUNC_ANALOG 0x4e11 - -#define STM32H7_PE15_FUNC_GPIO 0x4f00 -#define STM32H7_PE15_FUNC_TIM1_BKIN 0x4f02 -#define STM32H7_PE15_FUNC_HDMI__TIM1_BKIN 0x4f06 -#define STM32H7_PE15_FUNC_FMC_D12_FMC_DA12 0x4f0d -#define STM32H7_PE15_FUNC_TIM1_BKIN_COMP12 0x4f0e -#define STM32H7_PE15_FUNC_LCD_R7 0x4f0f -#define STM32H7_PE15_FUNC_EVENTOUT 0x4f10 -#define STM32H7_PE15_FUNC_ANALOG 0x4f11 - -#define STM32H7_PF0_FUNC_GPIO 0x5000 -#define STM32H7_PF0_FUNC_I2C2_SDA 0x5005 -#define STM32H7_PF0_FUNC_FMC_A0 0x500d -#define STM32H7_PF0_FUNC_EVENTOUT 0x5010 -#define STM32H7_PF0_FUNC_ANALOG 0x5011 - -#define STM32H7_PF1_FUNC_GPIO 0x5100 -#define STM32H7_PF1_FUNC_I2C2_SCL 0x5105 -#define STM32H7_PF1_FUNC_FMC_A1 0x510d -#define STM32H7_PF1_FUNC_EVENTOUT 0x5110 -#define STM32H7_PF1_FUNC_ANALOG 0x5111 - -#define STM32H7_PF2_FUNC_GPIO 0x5200 -#define STM32H7_PF2_FUNC_I2C2_SMBA 0x5205 -#define STM32H7_PF2_FUNC_FMC_A2 0x520d -#define STM32H7_PF2_FUNC_EVENTOUT 0x5210 -#define STM32H7_PF2_FUNC_ANALOG 0x5211 - -#define STM32H7_PF3_FUNC_GPIO 0x5300 -#define STM32H7_PF3_FUNC_FMC_A3 0x530d -#define STM32H7_PF3_FUNC_EVENTOUT 0x5310 -#define STM32H7_PF3_FUNC_ANALOG 0x5311 - -#define STM32H7_PF4_FUNC_GPIO 0x5400 -#define STM32H7_PF4_FUNC_FMC_A4 0x540d -#define STM32H7_PF4_FUNC_EVENTOUT 0x5410 -#define STM32H7_PF4_FUNC_ANALOG 0x5411 - -#define STM32H7_PF5_FUNC_GPIO 0x5500 -#define STM32H7_PF5_FUNC_FMC_A5 0x550d -#define STM32H7_PF5_FUNC_EVENTOUT 0x5510 -#define STM32H7_PF5_FUNC_ANALOG 0x5511 - -#define STM32H7_PF6_FUNC_GPIO 0x5600 -#define STM32H7_PF6_FUNC_TIM16_CH1 0x5602 -#define STM32H7_PF6_FUNC_SPI5_NSS 0x5606 -#define STM32H7_PF6_FUNC_SAI1_SD_B 0x5607 -#define STM32H7_PF6_FUNC_UART7_RX 0x5608 -#define STM32H7_PF6_FUNC_SAI4_SD_B 0x5609 -#define STM32H7_PF6_FUNC_QUADSPI_BK1_IO3 0x560a -#define STM32H7_PF6_FUNC_EVENTOUT 0x5610 -#define STM32H7_PF6_FUNC_ANALOG 0x5611 - -#define STM32H7_PF7_FUNC_GPIO 0x5700 -#define STM32H7_PF7_FUNC_TIM17_CH1 0x5702 -#define STM32H7_PF7_FUNC_SPI5_SCK 0x5706 -#define STM32H7_PF7_FUNC_SAI1_MCLK_B 0x5707 -#define STM32H7_PF7_FUNC_UART7_TX 0x5708 -#define STM32H7_PF7_FUNC_SAI4_MCLK_B 0x5709 -#define STM32H7_PF7_FUNC_QUADSPI_BK1_IO2 0x570a -#define STM32H7_PF7_FUNC_EVENTOUT 0x5710 -#define STM32H7_PF7_FUNC_ANALOG 0x5711 - -#define STM32H7_PF8_FUNC_GPIO 0x5800 -#define STM32H7_PF8_FUNC_TIM16_CH1N 0x5802 -#define STM32H7_PF8_FUNC_SPI5_MISO 0x5806 -#define STM32H7_PF8_FUNC_SAI1_SCK_B 0x5807 -#define STM32H7_PF8_FUNC_UART7_RTS 0x5808 -#define STM32H7_PF8_FUNC_SAI4_SCK_B 0x5809 -#define STM32H7_PF8_FUNC_TIM13_CH1 0x580a -#define STM32H7_PF8_FUNC_QUADSPI_BK1_IO0 0x580b -#define STM32H7_PF8_FUNC_EVENTOUT 0x5810 -#define STM32H7_PF8_FUNC_ANALOG 0x5811 - -#define STM32H7_PF9_FUNC_GPIO 0x5900 -#define STM32H7_PF9_FUNC_TIM17_CH1N 0x5902 -#define STM32H7_PF9_FUNC_SPI5_MOSI 0x5906 -#define STM32H7_PF9_FUNC_SAI1_FS_B 0x5907 -#define STM32H7_PF9_FUNC_UART7_CTS 0x5908 -#define STM32H7_PF9_FUNC_SAI4_FS_B 0x5909 -#define STM32H7_PF9_FUNC_TIM14_CH1 0x590a -#define STM32H7_PF9_FUNC_QUADSPI_BK1_IO1 0x590b -#define STM32H7_PF9_FUNC_EVENTOUT 0x5910 -#define STM32H7_PF9_FUNC_ANALOG 0x5911 - -#define STM32H7_PF10_FUNC_GPIO 0x5a00 -#define STM32H7_PF10_FUNC_TIM16_BKIN 0x5a02 -#define STM32H7_PF10_FUNC_SAI1_D3 0x5a03 -#define STM32H7_PF10_FUNC_QUADSPI_CLK 0x5a0a -#define STM32H7_PF10_FUNC_SAI4_D3 0x5a0b -#define STM32H7_PF10_FUNC_DCMI_D11 0x5a0e -#define STM32H7_PF10_FUNC_LCD_DE 0x5a0f -#define STM32H7_PF10_FUNC_EVENTOUT 0x5a10 -#define STM32H7_PF10_FUNC_ANALOG 0x5a11 - -#define STM32H7_PF11_FUNC_GPIO 0x5b00 -#define STM32H7_PF11_FUNC_SPI5_MOSI 0x5b06 -#define STM32H7_PF11_FUNC_SAI2_SD_B 0x5b0b -#define STM32H7_PF11_FUNC_FMC_SDNRAS 0x5b0d -#define STM32H7_PF11_FUNC_DCMI_D12 0x5b0e -#define STM32H7_PF11_FUNC_EVENTOUT 0x5b10 -#define STM32H7_PF11_FUNC_ANALOG 0x5b11 - -#define STM32H7_PF12_FUNC_GPIO 0x5c00 -#define STM32H7_PF12_FUNC_FMC_A6 0x5c0d -#define STM32H7_PF12_FUNC_EVENTOUT 0x5c10 -#define STM32H7_PF12_FUNC_ANALOG 0x5c11 - -#define STM32H7_PF13_FUNC_GPIO 0x5d00 -#define STM32H7_PF13_FUNC_DFSDM_DATIN6 0x5d04 -#define STM32H7_PF13_FUNC_I2C4_SMBA 0x5d05 -#define STM32H7_PF13_FUNC_FMC_A7 0x5d0d -#define STM32H7_PF13_FUNC_EVENTOUT 0x5d10 -#define STM32H7_PF13_FUNC_ANALOG 0x5d11 - -#define STM32H7_PF14_FUNC_GPIO 0x5e00 -#define STM32H7_PF14_FUNC_DFSDM_CKIN6 0x5e04 -#define STM32H7_PF14_FUNC_I2C4_SCL 0x5e05 -#define STM32H7_PF14_FUNC_FMC_A8 0x5e0d -#define STM32H7_PF14_FUNC_EVENTOUT 0x5e10 -#define STM32H7_PF14_FUNC_ANALOG 0x5e11 - -#define STM32H7_PF15_FUNC_GPIO 0x5f00 -#define STM32H7_PF15_FUNC_I2C4_SDA 0x5f05 -#define STM32H7_PF15_FUNC_FMC_A9 0x5f0d -#define STM32H7_PF15_FUNC_EVENTOUT 0x5f10 -#define STM32H7_PF15_FUNC_ANALOG 0x5f11 - -#define STM32H7_PG0_FUNC_GPIO 0x6000 -#define STM32H7_PG0_FUNC_FMC_A10 0x600d -#define STM32H7_PG0_FUNC_EVENTOUT 0x6010 -#define STM32H7_PG0_FUNC_ANALOG 0x6011 - -#define STM32H7_PG1_FUNC_GPIO 0x6100 -#define STM32H7_PG1_FUNC_FMC_A11 0x610d -#define STM32H7_PG1_FUNC_EVENTOUT 0x6110 -#define STM32H7_PG1_FUNC_ANALOG 0x6111 - -#define STM32H7_PG2_FUNC_GPIO 0x6200 -#define STM32H7_PG2_FUNC_TIM8_BKIN 0x6204 -#define STM32H7_PG2_FUNC_TIM8_BKIN_COMP12 0x620c -#define STM32H7_PG2_FUNC_FMC_A12 0x620d -#define STM32H7_PG2_FUNC_EVENTOUT 0x6210 -#define STM32H7_PG2_FUNC_ANALOG 0x6211 - -#define STM32H7_PG3_FUNC_GPIO 0x6300 -#define STM32H7_PG3_FUNC_TIM8_BKIN2 0x6304 -#define STM32H7_PG3_FUNC_TIM8_BKIN2_COMP12 0x630c -#define STM32H7_PG3_FUNC_FMC_A13 0x630d -#define STM32H7_PG3_FUNC_EVENTOUT 0x6310 -#define STM32H7_PG3_FUNC_ANALOG 0x6311 - -#define STM32H7_PG4_FUNC_GPIO 0x6400 -#define STM32H7_PG4_FUNC_TIM1_BKIN2 0x6402 -#define STM32H7_PG4_FUNC_TIM1_BKIN2_COMP12 0x640c -#define STM32H7_PG4_FUNC_FMC_A14_FMC_BA0 0x640d -#define STM32H7_PG4_FUNC_EVENTOUT 0x6410 -#define STM32H7_PG4_FUNC_ANALOG 0x6411 - -#define STM32H7_PG5_FUNC_GPIO 0x6500 -#define STM32H7_PG5_FUNC_TIM1_ETR 0x6502 -#define STM32H7_PG5_FUNC_FMC_A15_FMC_BA1 0x650d -#define STM32H7_PG5_FUNC_EVENTOUT 0x6510 -#define STM32H7_PG5_FUNC_ANALOG 0x6511 - -#define STM32H7_PG6_FUNC_GPIO 0x6600 -#define STM32H7_PG6_FUNC_TIM17_BKIN 0x6602 -#define STM32H7_PG6_FUNC_HRTIM_CHE1 0x6603 -#define STM32H7_PG6_FUNC_QUADSPI_BK1_NCS 0x660b -#define STM32H7_PG6_FUNC_FMC_NE3 0x660d -#define STM32H7_PG6_FUNC_DCMI_D12 0x660e -#define STM32H7_PG6_FUNC_LCD_R7 0x660f -#define STM32H7_PG6_FUNC_EVENTOUT 0x6610 -#define STM32H7_PG6_FUNC_ANALOG 0x6611 - -#define STM32H7_PG7_FUNC_GPIO 0x6700 -#define STM32H7_PG7_FUNC_HRTIM_CHE2 0x6703 -#define STM32H7_PG7_FUNC_SAI1_MCLK_A 0x6707 -#define STM32H7_PG7_FUNC_USART6_CK 0x6708 -#define STM32H7_PG7_FUNC_FMC_INT 0x670d -#define STM32H7_PG7_FUNC_DCMI_D13 0x670e -#define STM32H7_PG7_FUNC_LCD_CLK 0x670f -#define STM32H7_PG7_FUNC_EVENTOUT 0x6710 -#define STM32H7_PG7_FUNC_ANALOG 0x6711 - -#define STM32H7_PG8_FUNC_GPIO 0x6800 -#define STM32H7_PG8_FUNC_TIM8_ETR 0x6804 -#define STM32H7_PG8_FUNC_SPI6_NSS 0x6806 -#define STM32H7_PG8_FUNC_USART6_RTS 0x6808 -#define STM32H7_PG8_FUNC_SPDIFRX_IN2 0x6809 -#define STM32H7_PG8_FUNC_ETH_PPS_OUT 0x680c -#define STM32H7_PG8_FUNC_FMC_SDCLK 0x680d -#define STM32H7_PG8_FUNC_LCD_G7 0x680f -#define STM32H7_PG8_FUNC_EVENTOUT 0x6810 -#define STM32H7_PG8_FUNC_ANALOG 0x6811 - -#define STM32H7_PG9_FUNC_GPIO 0x6900 -#define STM32H7_PG9_FUNC_SPI1_MISO_I2S1_SDI 0x6906 -#define STM32H7_PG9_FUNC_USART6_RX 0x6908 -#define STM32H7_PG9_FUNC_SPDIFRX_IN3 0x6909 -#define STM32H7_PG9_FUNC_QUADSPI_BK2_IO2 0x690a -#define STM32H7_PG9_FUNC_SAI2_FS_B 0x690b -#define STM32H7_PG9_FUNC_FMC_NE2_FMC_NCE 0x690d -#define STM32H7_PG9_FUNC_DCMI_VSYNC 0x690e -#define STM32H7_PG9_FUNC_EVENTOUT 0x6910 -#define STM32H7_PG9_FUNC_ANALOG 0x6911 - -#define STM32H7_PG10_FUNC_GPIO 0x6a00 -#define STM32H7_PG10_FUNC_HRTIM_FLT5 0x6a03 -#define STM32H7_PG10_FUNC_SPI1_NSS_I2S1_WS 0x6a06 -#define STM32H7_PG10_FUNC_LCD_G3 0x6a0a -#define STM32H7_PG10_FUNC_SAI2_SD_B 0x6a0b -#define STM32H7_PG10_FUNC_FMC_NE3 0x6a0d -#define STM32H7_PG10_FUNC_DCMI_D2 0x6a0e -#define STM32H7_PG10_FUNC_LCD_B2 0x6a0f -#define STM32H7_PG10_FUNC_EVENTOUT 0x6a10 -#define STM32H7_PG10_FUNC_ANALOG 0x6a11 - -#define STM32H7_PG11_FUNC_GPIO 0x6b00 -#define STM32H7_PG11_FUNC_HRTIM_EEV4 0x6b03 -#define STM32H7_PG11_FUNC_SPI1_SCK_I2S1_CK 0x6b06 -#define STM32H7_PG11_FUNC_SPDIFRX_IN0 0x6b09 -#define STM32H7_PG11_FUNC_SDMMC2_D2 0x6b0b -#define STM32H7_PG11_FUNC_ETH_MII_TX_EN_ETH_RMII_TX_EN 0x6b0c -#define STM32H7_PG11_FUNC_DCMI_D3 0x6b0e -#define STM32H7_PG11_FUNC_LCD_B3 0x6b0f -#define STM32H7_PG11_FUNC_EVENTOUT 0x6b10 -#define STM32H7_PG11_FUNC_ANALOG 0x6b11 - -#define STM32H7_PG12_FUNC_GPIO 0x6c00 -#define STM32H7_PG12_FUNC_LPTIM1_IN1 0x6c02 -#define STM32H7_PG12_FUNC_HRTIM_EEV5 0x6c03 -#define STM32H7_PG12_FUNC_SPI6_MISO 0x6c06 -#define STM32H7_PG12_FUNC_USART6_RTS 0x6c08 -#define STM32H7_PG12_FUNC_SPDIFRX_IN1 0x6c09 -#define STM32H7_PG12_FUNC_LCD_B4 0x6c0a -#define STM32H7_PG12_FUNC_ETH_MII_TXD1_ETH_RMII_TXD1 0x6c0c -#define STM32H7_PG12_FUNC_FMC_NE4 0x6c0d -#define STM32H7_PG12_FUNC_LCD_B1 0x6c0f -#define STM32H7_PG12_FUNC_EVENTOUT 0x6c10 -#define STM32H7_PG12_FUNC_ANALOG 0x6c11 - -#define STM32H7_PG13_FUNC_GPIO 0x6d00 -#define STM32H7_PG13_FUNC_TRACED0 0x6d01 -#define STM32H7_PG13_FUNC_LPTIM1_OUT 0x6d02 -#define STM32H7_PG13_FUNC_HRTIM_EEV10 0x6d03 -#define STM32H7_PG13_FUNC_SPI6_SCK 0x6d06 -#define STM32H7_PG13_FUNC_USART6_CTS_NSS 0x6d08 -#define STM32H7_PG13_FUNC_ETH_MII_TXD0_ETH_RMII_TXD0 0x6d0c -#define STM32H7_PG13_FUNC_FMC_A24 0x6d0d -#define STM32H7_PG13_FUNC_LCD_R0 0x6d0f -#define STM32H7_PG13_FUNC_EVENTOUT 0x6d10 -#define STM32H7_PG13_FUNC_ANALOG 0x6d11 - -#define STM32H7_PG14_FUNC_GPIO 0x6e00 -#define STM32H7_PG14_FUNC_TRACED1 0x6e01 -#define STM32H7_PG14_FUNC_LPTIM1_ETR 0x6e02 -#define STM32H7_PG14_FUNC_SPI6_MOSI 0x6e06 -#define STM32H7_PG14_FUNC_USART6_TX 0x6e08 -#define STM32H7_PG14_FUNC_QUADSPI_BK2_IO3 0x6e0a -#define STM32H7_PG14_FUNC_ETH_MII_TXD1_ETH_RMII_TXD1 0x6e0c -#define STM32H7_PG14_FUNC_FMC_A25 0x6e0d -#define STM32H7_PG14_FUNC_LCD_B0 0x6e0f -#define STM32H7_PG14_FUNC_EVENTOUT 0x6e10 -#define STM32H7_PG14_FUNC_ANALOG 0x6e11 - -#define STM32H7_PG15_FUNC_GPIO 0x6f00 -#define STM32H7_PG15_FUNC_USART6_CTS_NSS 0x6f08 -#define STM32H7_PG15_FUNC_FMC_SDNCAS 0x6f0d -#define STM32H7_PG15_FUNC_DCMI_D13 0x6f0e -#define STM32H7_PG15_FUNC_EVENTOUT 0x6f10 -#define STM32H7_PG15_FUNC_ANALOG 0x6f11 - -#define STM32H7_PH0_FUNC_GPIO 0x7000 -#define STM32H7_PH0_FUNC_EVENTOUT 0x7010 -#define STM32H7_PH0_FUNC_ANALOG 0x7011 - -#define STM32H7_PH1_FUNC_GPIO 0x7100 -#define STM32H7_PH1_FUNC_EVENTOUT 0x7110 -#define STM32H7_PH1_FUNC_ANALOG 0x7111 - -#define STM32H7_PH2_FUNC_GPIO 0x7200 -#define STM32H7_PH2_FUNC_LPTIM1_IN2 0x7202 -#define STM32H7_PH2_FUNC_QUADSPI_BK2_IO0 0x720a -#define STM32H7_PH2_FUNC_SAI2_SCK_B 0x720b -#define STM32H7_PH2_FUNC_ETH_MII_CRS 0x720c -#define STM32H7_PH2_FUNC_FMC_SDCKE0 0x720d -#define STM32H7_PH2_FUNC_LCD_R0 0x720f -#define STM32H7_PH2_FUNC_EVENTOUT 0x7210 -#define STM32H7_PH2_FUNC_ANALOG 0x7211 - -#define STM32H7_PH3_FUNC_GPIO 0x7300 -#define STM32H7_PH3_FUNC_QUADSPI_BK2_IO1 0x730a -#define STM32H7_PH3_FUNC_SAI2_MCK_B 0x730b -#define STM32H7_PH3_FUNC_ETH_MII_COL 0x730c -#define STM32H7_PH3_FUNC_FMC_SDNE0 0x730d -#define STM32H7_PH3_FUNC_LCD_R1 0x730f -#define STM32H7_PH3_FUNC_EVENTOUT 0x7310 -#define STM32H7_PH3_FUNC_ANALOG 0x7311 - -#define STM32H7_PH4_FUNC_GPIO 0x7400 -#define STM32H7_PH4_FUNC_I2C2_SCL 0x7405 -#define STM32H7_PH4_FUNC_LCD_G5 0x740a -#define STM32H7_PH4_FUNC_OTG_HS_ULPI_NXT 0x740b -#define STM32H7_PH4_FUNC_LCD_G4 0x740f -#define STM32H7_PH4_FUNC_EVENTOUT 0x7410 -#define STM32H7_PH4_FUNC_ANALOG 0x7411 - -#define STM32H7_PH5_FUNC_GPIO 0x7500 -#define STM32H7_PH5_FUNC_I2C2_SDA 0x7505 -#define STM32H7_PH5_FUNC_SPI5_NSS 0x7506 -#define STM32H7_PH5_FUNC_FMC_SDNWE 0x750d -#define STM32H7_PH5_FUNC_EVENTOUT 0x7510 -#define STM32H7_PH5_FUNC_ANALOG 0x7511 - -#define STM32H7_PH6_FUNC_GPIO 0x7600 -#define STM32H7_PH6_FUNC_I2C2_SMBA 0x7605 -#define STM32H7_PH6_FUNC_SPI5_SCK 0x7606 -#define STM32H7_PH6_FUNC_ETH_MII_RXD2 0x760c -#define STM32H7_PH6_FUNC_FMC_SDNE1 0x760d -#define STM32H7_PH6_FUNC_DCMI_D8 0x760e -#define STM32H7_PH6_FUNC_EVENTOUT 0x7610 -#define STM32H7_PH6_FUNC_ANALOG 0x7611 - -#define STM32H7_PH7_FUNC_GPIO 0x7700 -#define STM32H7_PH7_FUNC_I2C3_SCL 0x7705 -#define STM32H7_PH7_FUNC_SPI5_MISO 0x7706 -#define STM32H7_PH7_FUNC_ETH_MII_RXD3 0x770c -#define STM32H7_PH7_FUNC_FMC_SDCKE1 0x770d -#define STM32H7_PH7_FUNC_DCMI_D9 0x770e -#define STM32H7_PH7_FUNC_EVENTOUT 0x7710 -#define STM32H7_PH7_FUNC_ANALOG 0x7711 - -#define STM32H7_PH8_FUNC_GPIO 0x7800 -#define STM32H7_PH8_FUNC_TIM5_ETR 0x7803 -#define STM32H7_PH8_FUNC_I2C3_SDA 0x7805 -#define STM32H7_PH8_FUNC_FMC_D16 0x780d -#define STM32H7_PH8_FUNC_DCMI_HSYNC 0x780e -#define STM32H7_PH8_FUNC_LCD_R2 0x780f -#define STM32H7_PH8_FUNC_EVENTOUT 0x7810 -#define STM32H7_PH8_FUNC_ANALOG 0x7811 - -#define STM32H7_PH9_FUNC_GPIO 0x7900 -#define STM32H7_PH9_FUNC_I2C3_SMBA 0x7905 -#define STM32H7_PH9_FUNC_FMC_D17 0x790d -#define STM32H7_PH9_FUNC_DCMI_D0 0x790e -#define STM32H7_PH9_FUNC_LCD_R3 0x790f -#define STM32H7_PH9_FUNC_EVENTOUT 0x7910 -#define STM32H7_PH9_FUNC_ANALOG 0x7911 - -#define STM32H7_PH10_FUNC_GPIO 0x7a00 -#define STM32H7_PH10_FUNC_TIM5_CH1 0x7a03 -#define STM32H7_PH10_FUNC_I2C4_SMBA 0x7a05 -#define STM32H7_PH10_FUNC_FMC_D18 0x7a0d -#define STM32H7_PH10_FUNC_DCMI_D1 0x7a0e -#define STM32H7_PH10_FUNC_LCD_R4 0x7a0f -#define STM32H7_PH10_FUNC_EVENTOUT 0x7a10 -#define STM32H7_PH10_FUNC_ANALOG 0x7a11 - -#define STM32H7_PH11_FUNC_GPIO 0x7b00 -#define STM32H7_PH11_FUNC_TIM5_CH2 0x7b03 -#define STM32H7_PH11_FUNC_I2C4_SCL 0x7b05 -#define STM32H7_PH11_FUNC_FMC_D19 0x7b0d -#define STM32H7_PH11_FUNC_DCMI_D2 0x7b0e -#define STM32H7_PH11_FUNC_LCD_R5 0x7b0f -#define STM32H7_PH11_FUNC_EVENTOUT 0x7b10 -#define STM32H7_PH11_FUNC_ANALOG 0x7b11 - -#define STM32H7_PH12_FUNC_GPIO 0x7c00 -#define STM32H7_PH12_FUNC_TIM5_CH3 0x7c03 -#define STM32H7_PH12_FUNC_I2C4_SDA 0x7c05 -#define STM32H7_PH12_FUNC_FMC_D20 0x7c0d -#define STM32H7_PH12_FUNC_DCMI_D3 0x7c0e -#define STM32H7_PH12_FUNC_LCD_R6 0x7c0f -#define STM32H7_PH12_FUNC_EVENTOUT 0x7c10 -#define STM32H7_PH12_FUNC_ANALOG 0x7c11 - -#define STM32H7_PH13_FUNC_GPIO 0x7d00 -#define STM32H7_PH13_FUNC_TIM8_CH1N 0x7d04 -#define STM32H7_PH13_FUNC_UART4_TX 0x7d09 -#define STM32H7_PH13_FUNC_CAN1_TX 0x7d0a -#define STM32H7_PH13_FUNC_FMC_D21 0x7d0d -#define STM32H7_PH13_FUNC_LCD_G2 0x7d0f -#define STM32H7_PH13_FUNC_EVENTOUT 0x7d10 -#define STM32H7_PH13_FUNC_ANALOG 0x7d11 - -#define STM32H7_PH14_FUNC_GPIO 0x7e00 -#define STM32H7_PH14_FUNC_TIM8_CH2N 0x7e04 -#define STM32H7_PH14_FUNC_UART4_RX 0x7e09 -#define STM32H7_PH14_FUNC_CAN1_RX 0x7e0a -#define STM32H7_PH14_FUNC_FMC_D22 0x7e0d -#define STM32H7_PH14_FUNC_DCMI_D4 0x7e0e -#define STM32H7_PH14_FUNC_LCD_G3 0x7e0f -#define STM32H7_PH14_FUNC_EVENTOUT 0x7e10 -#define STM32H7_PH14_FUNC_ANALOG 0x7e11 - -#define STM32H7_PH15_FUNC_GPIO 0x7f00 -#define STM32H7_PH15_FUNC_TIM8_CH3N 0x7f04 -#define STM32H7_PH15_FUNC_CAN1_TXFD 0x7f0a -#define STM32H7_PH15_FUNC_FMC_D23 0x7f0d -#define STM32H7_PH15_FUNC_DCMI_D11 0x7f0e -#define STM32H7_PH15_FUNC_LCD_G4 0x7f0f -#define STM32H7_PH15_FUNC_EVENTOUT 0x7f10 -#define STM32H7_PH15_FUNC_ANALOG 0x7f11 - -#define STM32H7_PI0_FUNC_GPIO 0x8000 -#define STM32H7_PI0_FUNC_TIM5_CH4 0x8003 -#define STM32H7_PI0_FUNC_SPI2_NSS_I2S2_WS 0x8006 -#define STM32H7_PI0_FUNC_CAN1_RXFD 0x800a -#define STM32H7_PI0_FUNC_FMC_D24 0x800d -#define STM32H7_PI0_FUNC_DCMI_D13 0x800e -#define STM32H7_PI0_FUNC_LCD_G5 0x800f -#define STM32H7_PI0_FUNC_EVENTOUT 0x8010 -#define STM32H7_PI0_FUNC_ANALOG 0x8011 - -#define STM32H7_PI1_FUNC_GPIO 0x8100 -#define STM32H7_PI1_FUNC_TIM8_BKIN2 0x8104 -#define STM32H7_PI1_FUNC_SPI2_SCK_I2S2_CK 0x8106 -#define STM32H7_PI1_FUNC_TIM8_BKIN2_COMP12 0x810c -#define STM32H7_PI1_FUNC_FMC_D25 0x810d -#define STM32H7_PI1_FUNC_DCMI_D8 0x810e -#define STM32H7_PI1_FUNC_LCD_G6 0x810f -#define STM32H7_PI1_FUNC_EVENTOUT 0x8110 -#define STM32H7_PI1_FUNC_ANALOG 0x8111 - -#define STM32H7_PI2_FUNC_GPIO 0x8200 -#define STM32H7_PI2_FUNC_TIM8_CH4 0x8204 -#define STM32H7_PI2_FUNC_SPI2_MISO_I2S2_SDI 0x8206 -#define STM32H7_PI2_FUNC_FMC_D26 0x820d -#define STM32H7_PI2_FUNC_DCMI_D9 0x820e -#define STM32H7_PI2_FUNC_LCD_G7 0x820f -#define STM32H7_PI2_FUNC_EVENTOUT 0x8210 -#define STM32H7_PI2_FUNC_ANALOG 0x8211 - -#define STM32H7_PI3_FUNC_GPIO 0x8300 -#define STM32H7_PI3_FUNC_TIM8_ETR 0x8304 -#define STM32H7_PI3_FUNC_SPI2_MOSI_I2S2_SDO 0x8306 -#define STM32H7_PI3_FUNC_FMC_D27 0x830d -#define STM32H7_PI3_FUNC_DCMI_D10 0x830e -#define STM32H7_PI3_FUNC_EVENTOUT 0x8310 -#define STM32H7_PI3_FUNC_ANALOG 0x8311 - -#define STM32H7_PI4_FUNC_GPIO 0x8400 -#define STM32H7_PI4_FUNC_TIM8_BKIN 0x8404 -#define STM32H7_PI4_FUNC_SAI2_MCK_A 0x840b -#define STM32H7_PI4_FUNC_TIM8_BKIN_COMP12 0x840c -#define STM32H7_PI4_FUNC_FMC_NBL2 0x840d -#define STM32H7_PI4_FUNC_DCMI_D5 0x840e -#define STM32H7_PI4_FUNC_LCD_B4 0x840f -#define STM32H7_PI4_FUNC_EVENTOUT 0x8410 -#define STM32H7_PI4_FUNC_ANALOG 0x8411 - -#define STM32H7_PI5_FUNC_GPIO 0x8500 -#define STM32H7_PI5_FUNC_TIM8_CH1 0x8504 -#define STM32H7_PI5_FUNC_SAI2_SCK_A 0x850b -#define STM32H7_PI5_FUNC_FMC_NBL3 0x850d -#define STM32H7_PI5_FUNC_DCMI_VSYNC 0x850e -#define STM32H7_PI5_FUNC_LCD_B5 0x850f -#define STM32H7_PI5_FUNC_EVENTOUT 0x8510 -#define STM32H7_PI5_FUNC_ANALOG 0x8511 - -#define STM32H7_PI6_FUNC_GPIO 0x8600 -#define STM32H7_PI6_FUNC_TIM8_CH2 0x8604 -#define STM32H7_PI6_FUNC_SAI2_SD_A 0x860b -#define STM32H7_PI6_FUNC_FMC_D28 0x860d -#define STM32H7_PI6_FUNC_DCMI_D6 0x860e -#define STM32H7_PI6_FUNC_LCD_B6 0x860f -#define STM32H7_PI6_FUNC_EVENTOUT 0x8610 -#define STM32H7_PI6_FUNC_ANALOG 0x8611 - -#define STM32H7_PI7_FUNC_GPIO 0x8700 -#define STM32H7_PI7_FUNC_TIM8_CH3 0x8704 -#define STM32H7_PI7_FUNC_SAI2_FS_A 0x870b -#define STM32H7_PI7_FUNC_FMC_D29 0x870d -#define STM32H7_PI7_FUNC_DCMI_D7 0x870e -#define STM32H7_PI7_FUNC_LCD_B7 0x870f -#define STM32H7_PI7_FUNC_EVENTOUT 0x8710 -#define STM32H7_PI7_FUNC_ANALOG 0x8711 - -#define STM32H7_PI8_FUNC_GPIO 0x8800 -#define STM32H7_PI8_FUNC_EVENTOUT 0x8810 -#define STM32H7_PI8_FUNC_ANALOG 0x8811 - -#define STM32H7_PI9_FUNC_GPIO 0x8900 -#define STM32H7_PI9_FUNC_UART4_RX 0x8909 -#define STM32H7_PI9_FUNC_CAN1_RX 0x890a -#define STM32H7_PI9_FUNC_FMC_D30 0x890d -#define STM32H7_PI9_FUNC_LCD_VSYNC 0x890f -#define STM32H7_PI9_FUNC_EVENTOUT 0x8910 -#define STM32H7_PI9_FUNC_ANALOG 0x8911 - -#define STM32H7_PI10_FUNC_GPIO 0x8a00 -#define STM32H7_PI10_FUNC_CAN1_RXFD 0x8a0a -#define STM32H7_PI10_FUNC_ETH_MII_RX_ER 0x8a0c -#define STM32H7_PI10_FUNC_FMC_D31 0x8a0d -#define STM32H7_PI10_FUNC_LCD_HSYNC 0x8a0f -#define STM32H7_PI10_FUNC_EVENTOUT 0x8a10 -#define STM32H7_PI10_FUNC_ANALOG 0x8a11 - -#define STM32H7_PI11_FUNC_GPIO 0x8b00 -#define STM32H7_PI11_FUNC_LCD_G6 0x8b0a -#define STM32H7_PI11_FUNC_OTG_HS_ULPI_DIR 0x8b0b -#define STM32H7_PI11_FUNC_EVENTOUT 0x8b10 -#define STM32H7_PI11_FUNC_ANALOG 0x8b11 - -#define STM32H7_PI12_FUNC_GPIO 0x8c00 -#define STM32H7_PI12_FUNC_ETH_TX_ER 0x8c0c -#define STM32H7_PI12_FUNC_LCD_HSYNC 0x8c0f -#define STM32H7_PI12_FUNC_EVENTOUT 0x8c10 -#define STM32H7_PI12_FUNC_ANALOG 0x8c11 - -#define STM32H7_PI13_FUNC_GPIO 0x8d00 -#define STM32H7_PI13_FUNC_LCD_VSYNC 0x8d0f -#define STM32H7_PI13_FUNC_EVENTOUT 0x8d10 -#define STM32H7_PI13_FUNC_ANALOG 0x8d11 - -#define STM32H7_PI14_FUNC_GPIO 0x8e00 -#define STM32H7_PI14_FUNC_LCD_CLK 0x8e0f -#define STM32H7_PI14_FUNC_EVENTOUT 0x8e10 -#define STM32H7_PI14_FUNC_ANALOG 0x8e11 - -#define STM32H7_PI15_FUNC_GPIO 0x8f00 -#define STM32H7_PI15_FUNC_LCD_G2 0x8f0a -#define STM32H7_PI15_FUNC_LCD_R0 0x8f0f -#define STM32H7_PI15_FUNC_EVENTOUT 0x8f10 -#define STM32H7_PI15_FUNC_ANALOG 0x8f11 - -#define STM32H7_PJ0_FUNC_GPIO 0x9000 -#define STM32H7_PJ0_FUNC_LCD_R7 0x900a -#define STM32H7_PJ0_FUNC_LCD_R1 0x900f -#define STM32H7_PJ0_FUNC_EVENTOUT 0x9010 -#define STM32H7_PJ0_FUNC_ANALOG 0x9011 - -#define STM32H7_PJ1_FUNC_GPIO 0x9100 -#define STM32H7_PJ1_FUNC_LCD_R2 0x910f -#define STM32H7_PJ1_FUNC_EVENTOUT 0x9110 -#define STM32H7_PJ1_FUNC_ANALOG 0x9111 - -#define STM32H7_PJ2_FUNC_GPIO 0x9200 -#define STM32H7_PJ2_FUNC_DSI_TE 0x920e -#define STM32H7_PJ2_FUNC_LCD_R3 0x920f -#define STM32H7_PJ2_FUNC_EVENTOUT 0x9210 -#define STM32H7_PJ2_FUNC_ANALOG 0x9211 - -#define STM32H7_PJ3_FUNC_GPIO 0x9300 -#define STM32H7_PJ3_FUNC_LCD_R4 0x930f -#define STM32H7_PJ3_FUNC_EVENTOUT 0x9310 -#define STM32H7_PJ3_FUNC_ANALOG 0x9311 - -#define STM32H7_PJ4_FUNC_GPIO 0x9400 -#define STM32H7_PJ4_FUNC_LCD_R5 0x940f -#define STM32H7_PJ4_FUNC_EVENTOUT 0x9410 -#define STM32H7_PJ4_FUNC_ANALOG 0x9411 - -#define STM32H7_PJ5_FUNC_GPIO 0x9500 -#define STM32H7_PJ5_FUNC_LCD_R6 0x950f -#define STM32H7_PJ5_FUNC_EVENTOUT 0x9510 -#define STM32H7_PJ5_FUNC_ANALOG 0x9511 - -#define STM32H7_PJ6_FUNC_GPIO 0x9600 -#define STM32H7_PJ6_FUNC_TIM8_CH2 0x9604 -#define STM32H7_PJ6_FUNC_LCD_R7 0x960f -#define STM32H7_PJ6_FUNC_EVENTOUT 0x9610 -#define STM32H7_PJ6_FUNC_ANALOG 0x9611 - -#define STM32H7_PJ7_FUNC_GPIO 0x9700 -#define STM32H7_PJ7_FUNC_TRGIN 0x9701 -#define STM32H7_PJ7_FUNC_TIM8_CH2N 0x9704 -#define STM32H7_PJ7_FUNC_LCD_G0 0x970f -#define STM32H7_PJ7_FUNC_EVENTOUT 0x9710 -#define STM32H7_PJ7_FUNC_ANALOG 0x9711 - -#define STM32H7_PJ8_FUNC_GPIO 0x9800 -#define STM32H7_PJ8_FUNC_TIM1_CH3N 0x9802 -#define STM32H7_PJ8_FUNC_TIM8_CH1 0x9804 -#define STM32H7_PJ8_FUNC_UART8_TX 0x9809 -#define STM32H7_PJ8_FUNC_LCD_G1 0x980f -#define STM32H7_PJ8_FUNC_EVENTOUT 0x9810 -#define STM32H7_PJ8_FUNC_ANALOG 0x9811 - -#define STM32H7_PJ9_FUNC_GPIO 0x9900 -#define STM32H7_PJ9_FUNC_TIM1_CH3 0x9902 -#define STM32H7_PJ9_FUNC_TIM8_CH1N 0x9904 -#define STM32H7_PJ9_FUNC_UART8_RX 0x9909 -#define STM32H7_PJ9_FUNC_LCD_G2 0x990f -#define STM32H7_PJ9_FUNC_EVENTOUT 0x9910 -#define STM32H7_PJ9_FUNC_ANALOG 0x9911 - -#define STM32H7_PJ10_FUNC_GPIO 0x9a00 -#define STM32H7_PJ10_FUNC_TIM1_CH2N 0x9a02 -#define STM32H7_PJ10_FUNC_TIM8_CH2 0x9a04 -#define STM32H7_PJ10_FUNC_SPI5_MOSI 0x9a06 -#define STM32H7_PJ10_FUNC_LCD_G3 0x9a0f -#define STM32H7_PJ10_FUNC_EVENTOUT 0x9a10 -#define STM32H7_PJ10_FUNC_ANALOG 0x9a11 - -#define STM32H7_PJ11_FUNC_GPIO 0x9b00 -#define STM32H7_PJ11_FUNC_TIM1_CH2 0x9b02 -#define STM32H7_PJ11_FUNC_TIM8_CH2N 0x9b04 -#define STM32H7_PJ11_FUNC_SPI5_MISO 0x9b06 -#define STM32H7_PJ11_FUNC_LCD_G4 0x9b0f -#define STM32H7_PJ11_FUNC_EVENTOUT 0x9b10 -#define STM32H7_PJ11_FUNC_ANALOG 0x9b11 - -#define STM32H7_PJ12_FUNC_GPIO 0x9c00 -#define STM32H7_PJ12_FUNC_TRGOUT 0x9c01 -#define STM32H7_PJ12_FUNC_LCD_G3 0x9c0a -#define STM32H7_PJ12_FUNC_LCD_B0 0x9c0f -#define STM32H7_PJ12_FUNC_EVENTOUT 0x9c10 -#define STM32H7_PJ12_FUNC_ANALOG 0x9c11 - -#define STM32H7_PJ13_FUNC_GPIO 0x9d00 -#define STM32H7_PJ13_FUNC_LCD_B4 0x9d0a -#define STM32H7_PJ13_FUNC_LCD_B1 0x9d0f -#define STM32H7_PJ13_FUNC_EVENTOUT 0x9d10 -#define STM32H7_PJ13_FUNC_ANALOG 0x9d11 - -#define STM32H7_PJ14_FUNC_GPIO 0x9e00 -#define STM32H7_PJ14_FUNC_LCD_B2 0x9e0f -#define STM32H7_PJ14_FUNC_EVENTOUT 0x9e10 -#define STM32H7_PJ14_FUNC_ANALOG 0x9e11 - -#define STM32H7_PJ15_FUNC_GPIO 0x9f00 -#define STM32H7_PJ15_FUNC_LCD_B3 0x9f0f -#define STM32H7_PJ15_FUNC_EVENTOUT 0x9f10 -#define STM32H7_PJ15_FUNC_ANALOG 0x9f11 - -#define STM32H7_PK0_FUNC_GPIO 0xa000 -#define STM32H7_PK0_FUNC_TIM1_CH1N 0xa002 -#define STM32H7_PK0_FUNC_TIM8_CH3 0xa004 -#define STM32H7_PK0_FUNC_SPI5_SCK 0xa006 -#define STM32H7_PK0_FUNC_LCD_G5 0xa00f -#define STM32H7_PK0_FUNC_EVENTOUT 0xa010 -#define STM32H7_PK0_FUNC_ANALOG 0xa011 - -#define STM32H7_PK1_FUNC_GPIO 0xa100 -#define STM32H7_PK1_FUNC_TIM1_CH1 0xa102 -#define STM32H7_PK1_FUNC_TIM8_CH3N 0xa104 -#define STM32H7_PK1_FUNC_SPI5_NSS 0xa106 -#define STM32H7_PK1_FUNC_LCD_G6 0xa10f -#define STM32H7_PK1_FUNC_EVENTOUT 0xa110 -#define STM32H7_PK1_FUNC_ANALOG 0xa111 - -#define STM32H7_PK2_FUNC_GPIO 0xa200 -#define STM32H7_PK2_FUNC_TIM1_BKIN 0xa202 -#define STM32H7_PK2_FUNC_TIM8_BKIN 0xa204 -#define STM32H7_PK2_FUNC_TIM8_BKIN_COMP12 0xa20b -#define STM32H7_PK2_FUNC_TIM1_BKIN_COMP12 0xa20c -#define STM32H7_PK2_FUNC_LCD_G7 0xa20f -#define STM32H7_PK2_FUNC_EVENTOUT 0xa210 -#define STM32H7_PK2_FUNC_ANALOG 0xa211 - -#define STM32H7_PK3_FUNC_GPIO 0xa300 -#define STM32H7_PK3_FUNC_LCD_B4 0xa30f -#define STM32H7_PK3_FUNC_EVENTOUT 0xa310 -#define STM32H7_PK3_FUNC_ANALOG 0xa311 - -#define STM32H7_PK4_FUNC_GPIO 0xa400 -#define STM32H7_PK4_FUNC_LCD_B5 0xa40f -#define STM32H7_PK4_FUNC_EVENTOUT 0xa410 -#define STM32H7_PK4_FUNC_ANALOG 0xa411 - -#define STM32H7_PK5_FUNC_GPIO 0xa500 -#define STM32H7_PK5_FUNC_LCD_B6 0xa50f -#define STM32H7_PK5_FUNC_EVENTOUT 0xa510 -#define STM32H7_PK5_FUNC_ANALOG 0xa511 - -#define STM32H7_PK6_FUNC_GPIO 0xa600 -#define STM32H7_PK6_FUNC_LCD_B7 0xa60f -#define STM32H7_PK6_FUNC_EVENTOUT 0xa610 -#define STM32H7_PK6_FUNC_ANALOG 0xa611 - -#define STM32H7_PK7_FUNC_GPIO 0xa700 -#define STM32H7_PK7_FUNC_LCD_DE 0xa70f -#define STM32H7_PK7_FUNC_EVENTOUT 0xa710 -#define STM32H7_PK7_FUNC_ANALOG 0xa711 - -#endif /* _DT_BINDINGS_STM32H7_PINFUNC_H */ -- cgit v1.2.3-59-g8ed1b From e3d4939267925ab66f39123744ffb4bc74a13149 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Thu, 28 Sep 2017 14:03:33 +0100 Subject: ACPI/IORT: Improve functions return type/storage class specifier indentation Some functions definition indentations are using a style that is frowned upon with return value type/storage class specifier in a separate line. Reindent the function definitions to fix them. Signed-off-by: Lorenzo Pieralisi Acked-by: Hanjun Guo Cc: Hanjun Guo Cc: Sudeep Holla --- drivers/acpi/arm64/iort.c | 36 +++++++++++++++++------------------- include/linux/acpi_iort.h | 4 ++-- 2 files changed, 19 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index 89c42aa71858..5b5630e52281 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -88,8 +88,8 @@ static inline int iort_set_fwnode(struct acpi_iort_node *iort_node, * * Returns: fwnode_handle pointer on success, NULL on failure */ -static inline -struct fwnode_handle *iort_get_fwnode(struct acpi_iort_node *node) +static inline struct fwnode_handle *iort_get_fwnode( + struct acpi_iort_node *node) { struct iort_fwnode *curr; struct fwnode_handle *fwnode = NULL; @@ -306,9 +306,8 @@ static int iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, return 0; } -static -struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, - u32 *id_out, int index) +static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, + u32 *id_out, int index) { struct acpi_iort_node *parent; struct acpi_iort_id_mapping *map; @@ -392,10 +391,9 @@ fail_map: return NULL; } -static -struct acpi_iort_node *iort_node_map_platform_id(struct acpi_iort_node *node, - u32 *id_out, u8 type_mask, - int index) +static struct acpi_iort_node *iort_node_map_platform_id( + struct acpi_iort_node *node, u32 *id_out, u8 type_mask, + int index) { struct acpi_iort_node *parent; u32 id; @@ -623,14 +621,14 @@ static inline bool iort_iommu_driver_enabled(u8 type) } #ifdef CONFIG_IOMMU_API -static inline -const struct iommu_ops *iort_fwspec_iommu_ops(struct iommu_fwspec *fwspec) +static inline const struct iommu_ops *iort_fwspec_iommu_ops( + struct iommu_fwspec *fwspec) { return (fwspec && fwspec->ops) ? fwspec->ops : NULL; } -static inline -int iort_add_device_replay(const struct iommu_ops *ops, struct device *dev) +static inline int iort_add_device_replay(const struct iommu_ops *ops, + struct device *dev) { int err = 0; @@ -640,11 +638,11 @@ int iort_add_device_replay(const struct iommu_ops *ops, struct device *dev) return err; } #else -static inline -const struct iommu_ops *iort_fwspec_iommu_ops(struct iommu_fwspec *fwspec) +static inline const struct iommu_ops *iort_fwspec_iommu_ops( + struct iommu_fwspec *fwspec) { return NULL; } -static inline -int iort_add_device_replay(const struct iommu_ops *ops, struct device *dev) +static inline int iort_add_device_replay(const struct iommu_ops *ops, + struct device *dev) { return 0; } #endif @@ -1077,8 +1075,8 @@ static const struct iort_iommu_config iort_arm_smmu_cfg __initconst = { .iommu_init_resources = arm_smmu_init_resources }; -static __init -const struct iort_iommu_config *iort_get_iommu_cfg(struct acpi_iort_node *node) +static __init const struct iort_iommu_config *iort_get_iommu_cfg( + struct acpi_iort_node *node) { switch (node->type) { case ACPI_IORT_NODE_SMMU_V3: diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h index 8d3f0bf80379..2f7a29242b87 100644 --- a/include/linux/acpi_iort.h +++ b/include/linux/acpi_iort.h @@ -49,8 +49,8 @@ static inline void acpi_configure_pmsi_domain(struct device *dev) { } /* IOMMU interface */ static inline void iort_dma_setup(struct device *dev, u64 *dma_addr, u64 *size) { } -static inline -const struct iommu_ops *iort_iommu_configure(struct device *dev) +static inline const struct iommu_ops *iort_iommu_configure( + struct device *dev) { return NULL; } #endif -- cgit v1.2.3-59-g8ed1b From 3c1818275cc65c6d53e0adfde0c989bfe89ab8d7 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Wed, 26 Jul 2017 10:14:55 -0400 Subject: NFS: Create NFS_ACCESS_* flags Passing the NFS v4 flags into the v3 code seems weird to me, even if they are defined to the same values. This patch adds in generic flags to help me feel better Signed-off-by: Anna Schumaker --- fs/nfs/dir.c | 16 ++++++++-------- include/linux/nfs_fs.h | 10 ++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 5ceaeb1f6fb6..80f397dc96bb 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -2369,15 +2369,15 @@ void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set) } EXPORT_SYMBOL_GPL(nfs_access_add_cache); -#define NFS_MAY_READ (NFS4_ACCESS_READ) -#define NFS_MAY_WRITE (NFS4_ACCESS_MODIFY | \ - NFS4_ACCESS_EXTEND | \ - NFS4_ACCESS_DELETE) -#define NFS_FILE_MAY_WRITE (NFS4_ACCESS_MODIFY | \ - NFS4_ACCESS_EXTEND) +#define NFS_MAY_READ (NFS_ACCESS_READ) +#define NFS_MAY_WRITE (NFS_ACCESS_MODIFY | \ + NFS_ACCESS_EXTEND | \ + NFS_ACCESS_DELETE) +#define NFS_FILE_MAY_WRITE (NFS_ACCESS_MODIFY | \ + NFS_ACCESS_EXTEND) #define NFS_DIR_MAY_WRITE NFS_MAY_WRITE -#define NFS_MAY_LOOKUP (NFS4_ACCESS_LOOKUP) -#define NFS_MAY_EXECUTE (NFS4_ACCESS_EXECUTE) +#define NFS_MAY_LOOKUP (NFS_ACCESS_LOOKUP) +#define NFS_MAY_EXECUTE (NFS_ACCESS_EXECUTE) static int nfs_access_calc_mask(u32 access_result, umode_t umode) { diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index a0282ceaa48b..453f491a5fda 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -183,6 +183,16 @@ struct nfs_inode { struct inode vfs_inode; }; +/* + * Access bit flags + */ +#define NFS_ACCESS_READ 0x0001 +#define NFS_ACCESS_LOOKUP 0x0002 +#define NFS_ACCESS_MODIFY 0x0004 +#define NFS_ACCESS_EXTEND 0x0008 +#define NFS_ACCESS_DELETE 0x0010 +#define NFS_ACCESS_EXECUTE 0x0020 + /* * Cache validity bit flags */ -- cgit v1.2.3-59-g8ed1b From 36689ecd2c065a8879035e5bf1b4a0f4d5b65160 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 29 Sep 2017 20:08:28 -0500 Subject: of: remove struct property.unique_id for FDT Only Sparc uses unique_id, so remove it for FDT builds and shrink struct property a bit making the unflattened DT less of a memory hog. Tested-by: Nicolas Pitre Reviewed-by: Frank Rowand Acked-by: Grant Likely Signed-off-by: Rob Herring --- include/linux/of.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index cfc34117fc92..8f9e96752837 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -38,7 +38,9 @@ struct property { void *value; struct property *next; unsigned long _flags; +#if defined(CONFIG_OF_PROMTREE) unsigned int unique_id; +#endif struct bin_attribute attr; }; -- cgit v1.2.3-59-g8ed1b From 16bba30eab137aaa37538349c0a7496720e90c66 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 4 Oct 2017 14:30:02 -0500 Subject: of: make struct property _flags field configurable Only Sparc and CONFIG_OF_DYNAMIC use the struct property._flags field, so make it conditional shrinking struct property a bit. Tested-by: Nicolas Pitre Reviewed-by: Frank Rowand Acked-by: Grant Likely Signed-off-by: Rob Herring --- include/linux/of.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index 8f9e96752837..7eb94b7fbcf3 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -37,7 +37,9 @@ struct property { int length; void *value; struct property *next; +#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC) unsigned long _flags; +#endif #if defined(CONFIG_OF_PROMTREE) unsigned int unique_id; #endif @@ -205,6 +207,7 @@ static inline void of_node_clear_flag(struct device_node *n, unsigned long flag) clear_bit(flag, &n->_flags); } +#if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC) static inline int of_property_check_flag(struct property *p, unsigned long flag) { return test_bit(flag, &p->_flags); @@ -219,6 +222,7 @@ static inline void of_property_clear_flag(struct property *p, unsigned long flag { clear_bit(flag, &p->_flags); } +#endif extern struct device_node *__of_find_all_nodes(struct device_node *prev); extern struct device_node *of_find_all_nodes(struct device_node *prev); -- cgit v1.2.3-59-g8ed1b From 0c3c234b95fa7f1dfa19e1456a47ebafc300dd6b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 4 Oct 2017 14:04:01 -0500 Subject: of: wrap accesses to device_node kobject In preparation to make kobject element in struct device_node optional, provide and use a macro to return the kobject pointer. The only user outside the DT core is the driver core. Acked-by: Greg Kroah-Hartman Tested-by: Nicolas Pitre Reviewed-by: Frank Rowand Acked-by: Grant Likely Signed-off-by: Rob Herring --- drivers/base/core.c | 2 +- include/linux/of.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/base/core.c b/drivers/base/core.c index 12ebd055724c..c07b47059538 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1571,7 +1571,7 @@ static int device_add_class_symlinks(struct device *dev) int error; if (of_node) { - error = sysfs_create_link(&dev->kobj, &of_node->kobj,"of_node"); + error = sysfs_create_link(&dev->kobj, of_node_kobj(of_node), "of_node"); if (error) dev_warn(dev, "Error %d creating of_node link\n",error); /* An error here doesn't warrant bringing down the device */ diff --git a/include/linux/of.h b/include/linux/of.h index 7eb94b7fbcf3..2d685e769409 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -111,6 +111,8 @@ static inline void of_node_init(struct device_node *node) node->fwnode.ops = &of_fwnode_ops; } +#define of_node_kobj(n) (&(n)->kobj) + /* true when node is initialized */ static inline int of_node_is_initialized(struct device_node *node) { -- cgit v1.2.3-59-g8ed1b From b56b5528f5b3c3d47e7c0ca67318c45e980d93f0 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 4 Oct 2017 14:09:40 -0500 Subject: of: make kobject and bin_attribute support configurable Having device_nodes be kobjects is only needed if sysfs or OF_DYNAMIC is enabled. Otherwise, having a kobject in struct device_node is unnecessary bloat in minimal kernel configurations. Likewise, bin_attribute is only needed in struct property when sysfs is enabled, so we can make it configurable too. Tested-by: Nicolas Pitre Reviewed-by: Frank Rowand Acked-by: Grant Likely Signed-off-by: Rob Herring --- drivers/of/Kconfig | 4 ++ drivers/of/Makefile | 1 + drivers/of/base.c | 130 -------------------------------------- drivers/of/dynamic.c | 22 ------- drivers/of/kobj.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/of/of_private.h | 23 +++++++ include/linux/of.h | 22 +++---- 7 files changed, 202 insertions(+), 164 deletions(-) create mode 100644 drivers/of/kobj.c (limited to 'include') diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index ba7b034b2b91..ad9a9578f9c4 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -46,10 +46,14 @@ config OF_EARLY_FLATTREE config OF_PROMTREE bool +config OF_KOBJ + def_bool SYSFS + # Hardly any platforms need this. It is safe to select, but only do so if you # need it. config OF_DYNAMIC bool "Support for dynamic device trees" if OF_UNITTEST + select OF_KOBJ help On some platforms, the device tree can be manipulated at runtime. While this option is selected automatically on such platforms, you diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 97dc01c81438..83d61a7c5e82 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,4 +1,5 @@ obj-y = base.o device.o platform.o property.o +obj-$(CONFIG_OF_KOBJ) += kobj.o obj-$(CONFIG_OF_DYNAMIC) += dynamic.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o diff --git a/drivers/of/base.c b/drivers/of/base.c index 9e9bd17121fb..b98f3adffbb0 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -95,105 +95,6 @@ int __weak of_node_to_nid(struct device_node *np) } #endif -#ifndef CONFIG_OF_DYNAMIC -static void of_node_release(struct kobject *kobj) -{ - /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */ -} -#endif /* CONFIG_OF_DYNAMIC */ - -struct kobj_type of_node_ktype = { - .release = of_node_release, -}; - -static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *bin_attr, char *buf, - loff_t offset, size_t count) -{ - struct property *pp = container_of(bin_attr, struct property, attr); - return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length); -} - -/* always return newly allocated name, caller must free after use */ -static const char *safe_name(struct kobject *kobj, const char *orig_name) -{ - const char *name = orig_name; - struct kernfs_node *kn; - int i = 0; - - /* don't be a hero. After 16 tries give up */ - while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) { - sysfs_put(kn); - if (name != orig_name) - kfree(name); - name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i); - } - - if (name == orig_name) { - name = kstrdup(orig_name, GFP_KERNEL); - } else { - pr_warn("Duplicate name in %s, renamed to \"%s\"\n", - kobject_name(kobj), name); - } - return name; -} - -int __of_add_property_sysfs(struct device_node *np, struct property *pp) -{ - int rc; - - /* Important: Don't leak passwords */ - bool secure = strncmp(pp->name, "security-", 9) == 0; - - if (!IS_ENABLED(CONFIG_SYSFS)) - return 0; - - if (!of_kset || !of_node_is_attached(np)) - return 0; - - sysfs_bin_attr_init(&pp->attr); - pp->attr.attr.name = safe_name(&np->kobj, pp->name); - pp->attr.attr.mode = secure ? 0400 : 0444; - pp->attr.size = secure ? 0 : pp->length; - pp->attr.read = of_node_property_read; - - rc = sysfs_create_bin_file(&np->kobj, &pp->attr); - WARN(rc, "error adding attribute %s to node %pOF\n", pp->name, np); - return rc; -} - -int __of_attach_node_sysfs(struct device_node *np) -{ - const char *name; - struct kobject *parent; - struct property *pp; - int rc; - - if (!of_kset) - return 0; - - np->kobj.kset = of_kset; - if (!np->parent) { - /* Nodes without parents are new top level trees */ - name = safe_name(&of_kset->kobj, "base"); - parent = NULL; - } else { - name = safe_name(&np->parent->kobj, kbasename(np->full_name)); - parent = &np->parent->kobj; - } - if (!name) - return -ENOMEM; - rc = kobject_add(&np->kobj, parent, "%s", name); - kfree(name); - if (rc) - return rc; - - for_each_property_of_node(np, pp) - __of_add_property_sysfs(np, pp); - - return 0; -} - void __init of_core_init(void) { struct device_node *np; @@ -1501,22 +1402,6 @@ int __of_remove_property(struct device_node *np, struct property *prop) return 0; } -void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop) -{ - sysfs_remove_bin_file(&np->kobj, &prop->attr); - kfree(prop->attr.attr.name); -} - -void __of_remove_property_sysfs(struct device_node *np, struct property *prop) -{ - if (!IS_ENABLED(CONFIG_SYSFS)) - return; - - /* at early boot, bail here and defer setup to of_init() */ - if (of_kset && of_node_is_attached(np)) - __of_sysfs_remove_bin_file(np, prop); -} - /** * of_remove_property - Remove a property from a node. * @@ -1576,21 +1461,6 @@ int __of_update_property(struct device_node *np, struct property *newprop, return 0; } -void __of_update_property_sysfs(struct device_node *np, struct property *newprop, - struct property *oldprop) -{ - if (!IS_ENABLED(CONFIG_SYSFS)) - return; - - /* At early boot, bail out and defer setup to of_init() */ - if (!of_kset) - return; - - if (oldprop) - __of_sysfs_remove_bin_file(np, oldprop); - __of_add_property_sysfs(np, newprop); -} - /* * of_update_property - Update a property in a node, if the property does * not exist, add it. diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 9d6ba18c529f..39e8cf731764 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -48,28 +48,6 @@ void of_node_put(struct device_node *node) } EXPORT_SYMBOL(of_node_put); -void __of_detach_node_sysfs(struct device_node *np) -{ - struct property *pp; - - if (!IS_ENABLED(CONFIG_SYSFS)) - return; - - BUG_ON(!of_node_is_initialized(np)); - if (!of_kset) - return; - - /* only remove properties if on sysfs */ - if (of_node_is_attached(np)) { - for_each_property_of_node(np, pp) - __of_sysfs_remove_bin_file(np, pp); - kobject_del(&np->kobj); - } - - /* finally remove the kobj_init ref */ - of_node_put(np); -} - static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain); int of_reconfig_notifier_register(struct notifier_block *nb) diff --git a/drivers/of/kobj.c b/drivers/of/kobj.c new file mode 100644 index 000000000000..250fc7bb550f --- /dev/null +++ b/drivers/of/kobj.c @@ -0,0 +1,164 @@ +#include +#include + +#include "of_private.h" + +/* true when node is initialized */ +static int of_node_is_initialized(struct device_node *node) +{ + return node && node->kobj.state_initialized; +} + +/* true when node is attached (i.e. present on sysfs) */ +int of_node_is_attached(struct device_node *node) +{ + return node && node->kobj.state_in_sysfs; +} + + +#ifndef CONFIG_OF_DYNAMIC +static void of_node_release(struct kobject *kobj) +{ + /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */ +} +#endif /* CONFIG_OF_DYNAMIC */ + +struct kobj_type of_node_ktype = { + .release = of_node_release, +}; + +static ssize_t of_node_property_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, char *buf, + loff_t offset, size_t count) +{ + struct property *pp = container_of(bin_attr, struct property, attr); + return memory_read_from_buffer(buf, count, &offset, pp->value, pp->length); +} + +/* always return newly allocated name, caller must free after use */ +static const char *safe_name(struct kobject *kobj, const char *orig_name) +{ + const char *name = orig_name; + struct kernfs_node *kn; + int i = 0; + + /* don't be a hero. After 16 tries give up */ + while (i < 16 && (kn = sysfs_get_dirent(kobj->sd, name))) { + sysfs_put(kn); + if (name != orig_name) + kfree(name); + name = kasprintf(GFP_KERNEL, "%s#%i", orig_name, ++i); + } + + if (name == orig_name) { + name = kstrdup(orig_name, GFP_KERNEL); + } else { + pr_warn("Duplicate name in %s, renamed to \"%s\"\n", + kobject_name(kobj), name); + } + return name; +} + +int __of_add_property_sysfs(struct device_node *np, struct property *pp) +{ + int rc; + + /* Important: Don't leak passwords */ + bool secure = strncmp(pp->name, "security-", 9) == 0; + + if (!IS_ENABLED(CONFIG_SYSFS)) + return 0; + + if (!of_kset || !of_node_is_attached(np)) + return 0; + + sysfs_bin_attr_init(&pp->attr); + pp->attr.attr.name = safe_name(&np->kobj, pp->name); + pp->attr.attr.mode = secure ? 0400 : 0444; + pp->attr.size = secure ? 0 : pp->length; + pp->attr.read = of_node_property_read; + + rc = sysfs_create_bin_file(&np->kobj, &pp->attr); + WARN(rc, "error adding attribute %s to node %pOF\n", pp->name, np); + return rc; +} + +void __of_sysfs_remove_bin_file(struct device_node *np, struct property *prop) +{ + if (!IS_ENABLED(CONFIG_SYSFS)) + return; + + sysfs_remove_bin_file(&np->kobj, &prop->attr); + kfree(prop->attr.attr.name); +} + +void __of_remove_property_sysfs(struct device_node *np, struct property *prop) +{ + /* at early boot, bail here and defer setup to of_init() */ + if (of_kset && of_node_is_attached(np)) + __of_sysfs_remove_bin_file(np, prop); +} + +void __of_update_property_sysfs(struct device_node *np, struct property *newprop, + struct property *oldprop) +{ + /* At early boot, bail out and defer setup to of_init() */ + if (!of_kset) + return; + + if (oldprop) + __of_sysfs_remove_bin_file(np, oldprop); + __of_add_property_sysfs(np, newprop); +} + +int __of_attach_node_sysfs(struct device_node *np) +{ + const char *name; + struct kobject *parent; + struct property *pp; + int rc; + + if (!of_kset) + return 0; + + np->kobj.kset = of_kset; + if (!np->parent) { + /* Nodes without parents are new top level trees */ + name = safe_name(&of_kset->kobj, "base"); + parent = NULL; + } else { + name = safe_name(&np->parent->kobj, kbasename(np->full_name)); + parent = &np->parent->kobj; + } + if (!name) + return -ENOMEM; + rc = kobject_add(&np->kobj, parent, "%s", name); + kfree(name); + if (rc) + return rc; + + for_each_property_of_node(np, pp) + __of_add_property_sysfs(np, pp); + + return 0; +} + +void __of_detach_node_sysfs(struct device_node *np) +{ + struct property *pp; + + BUG_ON(!of_node_is_initialized(np)); + if (!of_kset) + return; + + /* only remove properties if on sysfs */ + if (of_node_is_attached(np)) { + for_each_property_of_node(np, pp) + __of_sysfs_remove_bin_file(np, pp); + kobject_del(&np->kobj); + } + + /* finally remove the kobj_init ref */ + of_node_put(np); +} + diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index cc86a974f35f..43df14f0cbce 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -49,6 +49,29 @@ static inline int of_property_notify(int action, struct device_node *np, } #endif /* CONFIG_OF_DYNAMIC */ +#if defined(CONFIG_OF_KOBJ) +int of_node_is_attached(struct device_node *node); +int __of_add_property_sysfs(struct device_node *np, struct property *pp); +void __of_remove_property_sysfs(struct device_node *np, struct property *prop); +void __of_update_property_sysfs(struct device_node *np, struct property *newprop, + struct property *oldprop); +int __of_attach_node_sysfs(struct device_node *np); +void __of_detach_node_sysfs(struct device_node *np); +#else +static inline int __of_add_property_sysfs(struct device_node *np, struct property *pp) +{ + return 0; +} +static inline void __of_remove_property_sysfs(struct device_node *np, struct property *prop) {} +static inline void __of_update_property_sysfs(struct device_node *np, + struct property *newprop, struct property *oldprop) {} +static inline int __of_attach_node_sysfs(struct device_node *np) +{ + return 0; +} +static inline void __of_detach_node_sysfs(struct device_node *np) {} +#endif + #if defined(CONFIG_OF_UNITTEST) && defined(CONFIG_OF_OVERLAY) extern void __init unittest_unflatten_overlay_base(void); #else diff --git a/include/linux/of.h b/include/linux/of.h index 2d685e769409..7b0f17be7830 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -43,7 +43,9 @@ struct property { #if defined(CONFIG_OF_PROMTREE) unsigned int unique_id; #endif +#if defined(CONFIG_OF_KOBJ) struct bin_attribute attr; +#endif }; #if defined(CONFIG_SPARC) @@ -62,7 +64,9 @@ struct device_node { struct device_node *parent; struct device_node *child; struct device_node *sibling; +#if defined(CONFIG_OF_KOBJ) struct kobject kobj; +#endif unsigned long _flags; void *data; #if defined(CONFIG_SPARC) @@ -107,23 +111,17 @@ extern struct kobj_type of_node_ktype; extern const struct fwnode_operations of_fwnode_ops; static inline void of_node_init(struct device_node *node) { +#if defined(CONFIG_OF_KOBJ) kobject_init(&node->kobj, &of_node_ktype); +#endif node->fwnode.ops = &of_fwnode_ops; } +#if defined(CONFIG_OF_KOBJ) #define of_node_kobj(n) (&(n)->kobj) - -/* true when node is initialized */ -static inline int of_node_is_initialized(struct device_node *node) -{ - return node && node->kobj.state_initialized; -} - -/* true when node is attached (i.e. present on sysfs) */ -static inline int of_node_is_attached(struct device_node *node) -{ - return node && node->kobj.state_in_sysfs; -} +#else +#define of_node_kobj(n) NULL +#endif #ifdef CONFIG_OF_DYNAMIC extern struct device_node *of_node_get(struct device_node *node); -- cgit v1.2.3-59-g8ed1b From 32302902ff093891d8e64439cbb8ceae83e21ef8 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 12 Oct 2017 11:38:45 -0700 Subject: mqprio: Reserve last 32 classid values for HW traffic classes and misc IDs This patch makes a slight tweak to mqprio in order to bring the classid values used back in line with what is used for mq. The general idea is to reserve values :ffe0 - :ffef to identify hardware traffic classes normally reported via dev->num_tc. By doing this we can maintain a consistent behavior with mq for classid where :1 - :ffdf will represent a physical qdisc mapped onto a Tx queue represented by classid - 1, and the traffic classes will be mapped onto a known subset of classid values reserved for our virtual qdiscs. Note I reserved the range from :fff0 - :ffff since this way we might be able to reuse these classid values with clsact and ingress which would mean that for mq, mqprio, ingress, and clsact we should be able to maintain a similar classid layout. Signed-off-by: Alexander Duyck Tested-by: Jesus Sanchez-Palencia Signed-off-by: David S. Miller --- include/uapi/linux/pkt_sched.h | 1 + net/sched/sch_mqprio.c | 79 ++++++++++++++++++++++++------------------ 2 files changed, 47 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index e95b5c9b9fad..e7cc3d3c7421 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -74,6 +74,7 @@ struct tc_estimator { #define TC_H_INGRESS (0xFFFFFFF1U) #define TC_H_CLSACT TC_H_INGRESS +#define TC_H_MIN_PRIORITY 0xFFE0U #define TC_H_MIN_INGRESS 0xFFF2U #define TC_H_MIN_EGRESS 0xFFF3U diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index f1ae9be83934..cae91b4b08a6 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -153,6 +153,10 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) if (!netif_is_multiqueue(dev)) return -EOPNOTSUPP; + /* make certain can allocate enough classids to handle queues */ + if (dev->num_tx_queues >= TC_H_MIN_PRIORITY) + return -ENOMEM; + if (!opt || nla_len(opt) < sizeof(*qopt)) return -EINVAL; @@ -305,7 +309,7 @@ static struct netdev_queue *mqprio_queue_get(struct Qdisc *sch, unsigned long cl) { struct net_device *dev = qdisc_dev(sch); - unsigned long ntx = cl - 1 - netdev_get_num_tc(dev); + unsigned long ntx = cl - 1; if (ntx >= dev->num_tx_queues) return NULL; @@ -447,38 +451,35 @@ static unsigned long mqprio_find(struct Qdisc *sch, u32 classid) struct net_device *dev = qdisc_dev(sch); unsigned int ntx = TC_H_MIN(classid); - if (ntx > dev->num_tx_queues + netdev_get_num_tc(dev)) - return 0; - return ntx; + /* There are essentially two regions here that have valid classid + * values. The first region will have a classid value of 1 through + * num_tx_queues. All of these are backed by actual Qdiscs. + */ + if (ntx < TC_H_MIN_PRIORITY) + return (ntx <= dev->num_tx_queues) ? ntx : 0; + + /* The second region represents the hardware traffic classes. These + * are represented by classid values of TC_H_MIN_PRIORITY through + * TC_H_MIN_PRIORITY + netdev_get_num_tc - 1 + */ + return ((ntx - TC_H_MIN_PRIORITY) < netdev_get_num_tc(dev)) ? ntx : 0; } static int mqprio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm) { - struct net_device *dev = qdisc_dev(sch); + if (cl < TC_H_MIN_PRIORITY) { + struct netdev_queue *dev_queue = mqprio_queue_get(sch, cl); + struct net_device *dev = qdisc_dev(sch); + int tc = netdev_txq_to_tc(dev, cl - 1); - if (cl <= netdev_get_num_tc(dev)) { + tcm->tcm_parent = (tc < 0) ? 0 : + TC_H_MAKE(TC_H_MAJ(sch->handle), + TC_H_MIN(tc + TC_H_MIN_PRIORITY)); + tcm->tcm_info = dev_queue->qdisc_sleeping->handle; + } else { tcm->tcm_parent = TC_H_ROOT; tcm->tcm_info = 0; - } else { - int i; - struct netdev_queue *dev_queue; - - dev_queue = mqprio_queue_get(sch, cl); - tcm->tcm_parent = 0; - for (i = 0; i < netdev_get_num_tc(dev); i++) { - struct netdev_tc_txq tc = dev->tc_to_txq[i]; - int q_idx = cl - netdev_get_num_tc(dev); - - if (q_idx > tc.offset && - q_idx <= tc.offset + tc.count) { - tcm->tcm_parent = - TC_H_MAKE(TC_H_MAJ(sch->handle), - TC_H_MIN(i + 1)); - break; - } - } - tcm->tcm_info = dev_queue->qdisc_sleeping->handle; } tcm->tcm_handle |= TC_H_MIN(cl); return 0; @@ -489,15 +490,14 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl, __releases(d->lock) __acquires(d->lock) { - struct net_device *dev = qdisc_dev(sch); - - if (cl <= netdev_get_num_tc(dev)) { + if (cl >= TC_H_MIN_PRIORITY) { int i; __u32 qlen = 0; struct Qdisc *qdisc; struct gnet_stats_queue qstats = {0}; struct gnet_stats_basic_packed bstats = {0}; - struct netdev_tc_txq tc = dev->tc_to_txq[cl - 1]; + struct net_device *dev = qdisc_dev(sch); + struct netdev_tc_txq tc = dev->tc_to_txq[cl & TC_BITMASK]; /* Drop lock here it will be reclaimed before touching * statistics this is required because the d->lock we @@ -550,12 +550,25 @@ static void mqprio_walk(struct Qdisc *sch, struct qdisc_walker *arg) /* Walk hierarchy with a virtual class per tc */ arg->count = arg->skip; - for (ntx = arg->skip; - ntx < dev->num_tx_queues + netdev_get_num_tc(dev); - ntx++) { + for (ntx = arg->skip; ntx < netdev_get_num_tc(dev); ntx++) { + if (arg->fn(sch, ntx + TC_H_MIN_PRIORITY, arg) < 0) { + arg->stop = 1; + return; + } + arg->count++; + } + + /* Pad the values and skip over unused traffic classes */ + if (ntx < TC_MAX_QUEUE) { + arg->count = TC_MAX_QUEUE; + ntx = TC_MAX_QUEUE; + } + + /* Reset offset, sort out remaining per-queue qdiscs */ + for (ntx -= TC_MAX_QUEUE; ntx < dev->num_tx_queues; ntx++) { if (arg->fn(sch, ntx + 1, arg) < 0) { arg->stop = 1; - break; + return; } arg->count++; } -- cgit v1.2.3-59-g8ed1b From 69d78ef25c7b0058674145500efb12255738ba8a Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 13 Oct 2017 14:00:57 +0200 Subject: net: sched: store Qdisc pointer in struct block Prepare for removal of tp->q and store Qdisc pointer in the block structure. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 4 ++-- include/net/sch_generic.h | 1 + net/sched/cls_api.c | 3 ++- net/sched/sch_atm.c | 4 ++-- net/sched/sch_cbq.c | 2 +- net/sched/sch_drr.c | 2 +- net/sched/sch_dsmark.c | 2 +- net/sched/sch_fq_codel.c | 2 +- net/sched/sch_hfsc.c | 4 ++-- net/sched/sch_htb.c | 4 ++-- net/sched/sch_ingress.c | 6 +++--- net/sched/sch_multiq.c | 2 +- net/sched/sch_prio.c | 2 +- net/sched/sch_qfq.c | 2 +- net/sched/sch_sfb.c | 2 +- net/sched/sch_sfq.c | 2 +- 16 files changed, 23 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 60d39789e4f0..e6c9e1e4d711 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -22,7 +22,7 @@ struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, bool create); void tcf_chain_put(struct tcf_chain *chain); int tcf_block_get(struct tcf_block **p_block, - struct tcf_proto __rcu **p_filter_chain); + struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q); void tcf_block_put(struct tcf_block *block); int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res, bool compat_mode); @@ -30,7 +30,7 @@ int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, #else static inline int tcf_block_get(struct tcf_block **p_block, - struct tcf_proto __rcu **p_filter_chain) + struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q) { return 0; } diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 684d8ed27eaa..df4032ca1b7f 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -270,6 +270,7 @@ struct tcf_chain { struct tcf_block { struct list_head chain_list; + struct Qdisc *q; }; static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 2977b8a90851..f7d3f1f539b7 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -241,7 +241,7 @@ tcf_chain_filter_chain_ptr_set(struct tcf_chain *chain, } int tcf_block_get(struct tcf_block **p_block, - struct tcf_proto __rcu **p_filter_chain) + struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q) { struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL); struct tcf_chain *chain; @@ -257,6 +257,7 @@ int tcf_block_get(struct tcf_block **p_block, goto err_chain_create; } tcf_chain_filter_chain_ptr_set(chain, p_filter_chain); + block->q = q; *p_block = block; return 0; diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index c5fcdf1a58a0..2dbd249c0b2f 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -281,7 +281,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, goto err_out; } - error = tcf_block_get(&flow->block, &flow->filter_list); + error = tcf_block_get(&flow->block, &flow->filter_list, sch); if (error) { kfree(flow); goto err_out; @@ -546,7 +546,7 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt) p->link.q = &noop_qdisc; pr_debug("atm_tc_init: link (%p) qdisc %p\n", &p->link, p->link.q); - err = tcf_block_get(&p->link.block, &p->link.filter_list); + err = tcf_block_get(&p->link.block, &p->link.filter_list, sch); if (err) return err; diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index dcef97fa8047..c3b92d62190e 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1566,7 +1566,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t if (cl == NULL) goto failure; - err = tcf_block_get(&cl->block, &cl->filter_list); + err = tcf_block_get(&cl->block, &cl->filter_list, sch); if (err) { kfree(cl); return err; diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 2d0e8d4bdc29..753dc7a77b60 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -412,7 +412,7 @@ static int drr_init_qdisc(struct Qdisc *sch, struct nlattr *opt) struct drr_sched *q = qdisc_priv(sch); int err; - err = tcf_block_get(&q->block, &q->filter_list); + err = tcf_block_get(&q->block, &q->filter_list, sch); if (err) return err; err = qdisc_class_hash_init(&q->clhash); diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index 2836c80c7aa5..fb4fb71c68cf 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -344,7 +344,7 @@ static int dsmark_init(struct Qdisc *sch, struct nlattr *opt) if (!opt) goto errout; - err = tcf_block_get(&p->block, &p->filter_list); + err = tcf_block_get(&p->block, &p->filter_list, sch); if (err) return err; diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index de3b57ceca7b..3c40edeff1e8 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -481,7 +481,7 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt) return err; } - err = tcf_block_get(&q->block, &q->filter_list); + err = tcf_block_get(&q->block, &q->filter_list, sch); if (err) return err; diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 3f88b75488b0..a692184bd333 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1033,7 +1033,7 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (cl == NULL) return -ENOBUFS; - err = tcf_block_get(&cl->block, &cl->filter_list); + err = tcf_block_get(&cl->block, &cl->filter_list, sch); if (err) { kfree(cl); return err; @@ -1405,7 +1405,7 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt) return err; q->eligible = RB_ROOT; - err = tcf_block_get(&q->root.block, &q->root.filter_list); + err = tcf_block_get(&q->root.block, &q->root.filter_list, sch); if (err) return err; diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index c6d7ae81b41f..57be73c0e1d2 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1030,7 +1030,7 @@ static int htb_init(struct Qdisc *sch, struct nlattr *opt) if (!opt) return -EINVAL; - err = tcf_block_get(&q->block, &q->filter_list); + err = tcf_block_get(&q->block, &q->filter_list, sch); if (err) return err; @@ -1393,7 +1393,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, if (!cl) goto failure; - err = tcf_block_get(&cl->block, &cl->filter_list); + err = tcf_block_get(&cl->block, &cl->filter_list, sch); if (err) { kfree(cl); goto failure; diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 44de4ee51ce9..9ccc1b89b0d9 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -59,7 +59,7 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt) struct net_device *dev = qdisc_dev(sch); int err; - err = tcf_block_get(&q->block, &dev->ingress_cl_list); + err = tcf_block_get(&q->block, &dev->ingress_cl_list, sch); if (err) return err; @@ -153,11 +153,11 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt) struct net_device *dev = qdisc_dev(sch); int err; - err = tcf_block_get(&q->ingress_block, &dev->ingress_cl_list); + err = tcf_block_get(&q->ingress_block, &dev->ingress_cl_list, sch); if (err) return err; - err = tcf_block_get(&q->egress_block, &dev->egress_cl_list); + err = tcf_block_get(&q->egress_block, &dev->egress_cl_list, sch); if (err) return err; diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index ff4fc3e0facd..31e0a284eeff 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -245,7 +245,7 @@ static int multiq_init(struct Qdisc *sch, struct nlattr *opt) if (opt == NULL) return -EINVAL; - err = tcf_block_get(&q->block, &q->filter_list); + err = tcf_block_get(&q->block, &q->filter_list, sch); if (err) return err; diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index 2dd6c68ae91e..95fad348c8d7 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -212,7 +212,7 @@ static int prio_init(struct Qdisc *sch, struct nlattr *opt) if (!opt) return -EINVAL; - err = tcf_block_get(&q->block, &q->filter_list); + err = tcf_block_get(&q->block, &q->filter_list, sch); if (err) return err; diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index 6ddfd4991108..8694c7b6d2b1 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -1419,7 +1419,7 @@ static int qfq_init_qdisc(struct Qdisc *sch, struct nlattr *opt) int i, j, err; u32 max_cl_shift, maxbudg_shift, max_classes; - err = tcf_block_get(&q->block, &q->filter_list); + err = tcf_block_get(&q->block, &q->filter_list, sch); if (err) return err; diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index cc39e170b4aa..487d375f5a06 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -553,7 +553,7 @@ static int sfb_init(struct Qdisc *sch, struct nlattr *opt) struct sfb_sched_data *q = qdisc_priv(sch); int err; - err = tcf_block_get(&q->block, &q->filter_list); + err = tcf_block_get(&q->block, &q->filter_list, sch); if (err) return err; diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 74ea863b8240..123a53af2506 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -725,7 +725,7 @@ static int sfq_init(struct Qdisc *sch, struct nlattr *opt) setup_deferrable_timer(&q->perturb_timer, sfq_perturbation, (unsigned long)sch); - err = tcf_block_get(&q->block, &q->filter_list); + err = tcf_block_get(&q->block, &q->filter_list, sch); if (err) return err; -- cgit v1.2.3-59-g8ed1b From 855319becbcffec6988a4e781a861b69a71c5b58 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 13 Oct 2017 14:00:58 +0200 Subject: net: sched: store net pointer in block and introduce qdisc_net helper Store net pointer in the block structure. Along the way, introduce qdisc_net helper which allows to easily obtain net pointer for qdisc instance. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_sched.h | 7 +++++++ include/net/sch_generic.h | 1 + net/sched/cls_api.c | 1 + 3 files changed, 9 insertions(+) (limited to 'include') diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 259bc191ba59..2d234af15f3e 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #include #define DEFAULT_TX_QUEUE_LEN 1000 @@ -146,4 +148,9 @@ static inline bool is_classid_clsact_egress(u32 classid) TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_EGRESS); } +static inline struct net *qdisc_net(struct Qdisc *q) +{ + return dev_net(q->dev_queue->dev); +} + #endif diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index df4032ca1b7f..9b2cb91dc0d9 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -270,6 +270,7 @@ struct tcf_chain { struct tcf_block { struct list_head chain_list; + struct net *net; struct Qdisc *q; }; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index f7d3f1f539b7..856003caa3bb 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -257,6 +257,7 @@ int tcf_block_get(struct tcf_block **p_block, goto err_chain_create; } tcf_chain_filter_chain_ptr_set(chain, p_filter_chain); + block->net = qdisc_net(q); block->q = q; *p_block = block; return 0; -- cgit v1.2.3-59-g8ed1b From 44186460c85a0121562db7cfef132d63c869118f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 13 Oct 2017 14:00:59 +0200 Subject: net: sched: introduce tcf_block_q and tcf_block_dev helpers These helpers allows to get a q and netdev pointers for given block easily. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index e6c9e1e4d711..7bed674ba29a 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -24,6 +24,17 @@ void tcf_chain_put(struct tcf_chain *chain); int tcf_block_get(struct tcf_block **p_block, struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q); void tcf_block_put(struct tcf_block *block); + +static inline struct Qdisc *tcf_block_q(struct tcf_block *block) +{ + return block->q; +} + +static inline struct net_device *tcf_block_dev(struct tcf_block *block) +{ + return tcf_block_q(block)->dev_queue->dev; +} + int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res, bool compat_mode); @@ -39,6 +50,16 @@ static inline void tcf_block_put(struct tcf_block *block) { } +static inline struct Qdisc *tcf_block_q(struct tcf_block *block) +{ + return NULL; +} + +static inline struct net_device *tcf_block_dev(struct tcf_block *block) +{ + return NULL; +} + static inline int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res, bool compat_mode) { -- cgit v1.2.3-59-g8ed1b From 34e3759cf86a3e75463e34c1bb9691777406a175 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 13 Oct 2017 14:01:00 +0200 Subject: net: sched: teach tcf_bind/unbind_filter to use block->q Whenever the block->q is set, it can be used instead of tp->q as it contains the same value. When it is not set, which can't happen now but it might happen with the follow-up shared blocks introduction, the class is not set in the result. That would lead to a class lookup instead of direct class pointer use for classful qdiscs. However, it is not planned to support classful qdisqs sharing filter blocks, so that may never happen. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 7bed674ba29a..49a143e0fe65 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -74,36 +74,43 @@ __cls_set_class(unsigned long *clp, unsigned long cl) } static inline unsigned long -cls_set_class(struct tcf_proto *tp, unsigned long *clp, - unsigned long cl) +cls_set_class(struct Qdisc *q, unsigned long *clp, unsigned long cl) { unsigned long old_cl; - - tcf_tree_lock(tp); + + sch_tree_lock(q); old_cl = __cls_set_class(clp, cl); - tcf_tree_unlock(tp); - + sch_tree_unlock(q); return old_cl; } static inline void tcf_bind_filter(struct tcf_proto *tp, struct tcf_result *r, unsigned long base) { + struct Qdisc *q = tp->chain->block->q; unsigned long cl; - cl = tp->q->ops->cl_ops->bind_tcf(tp->q, base, r->classid); - cl = cls_set_class(tp, &r->class, cl); + /* Check q as it is not set for shared blocks. In that case, + * setting class is not supported. + */ + if (!q) + return; + cl = q->ops->cl_ops->bind_tcf(q, base, r->classid); + cl = cls_set_class(q, &r->class, cl); if (cl) - tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); + q->ops->cl_ops->unbind_tcf(q, cl); } static inline void tcf_unbind_filter(struct tcf_proto *tp, struct tcf_result *r) { + struct Qdisc *q = tp->chain->block->q; unsigned long cl; + if (!q) + return; if ((cl = __cls_set_class(&r->class, 0)) != 0) - tp->q->ops->cl_ops->unbind_tcf(tp->q, cl); + q->ops->cl_ops->unbind_tcf(q, cl); } struct tcf_exts { -- cgit v1.2.3-59-g8ed1b From 74e3be6021d22df2ffcb691eae1affeb2bd0128e Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 13 Oct 2017 14:01:04 +0200 Subject: net: sched: use tcf_block_q helper to get q pointer for sch_tree_lock Use tcf_block_q helper to get q pointer to be used for direct call of sch_tree_lock/unlock instead of tcf_tree_lock/unlock. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/sch_generic.h | 3 --- net/sched/sch_api.c | 6 ++++-- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 9b2cb91dc0d9..0aea9e23e97a 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -359,9 +359,6 @@ static inline void sch_tree_unlock(const struct Qdisc *q) spin_unlock_bh(qdisc_root_sleeping_lock(q)); } -#define tcf_tree_lock(tp) sch_tree_lock((tp)->q) -#define tcf_tree_unlock(tp) sch_tree_unlock((tp)->q) - extern struct Qdisc noop_qdisc; extern struct Qdisc_ops noop_qdisc_ops; extern struct Qdisc_ops pfifo_fast_ops; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index aa82116ed10c..a9ac912f1d67 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1661,9 +1661,11 @@ static int tcf_node_bind(struct tcf_proto *tp, void *n, struct tcf_walker *arg) struct tcf_bind_args *a = (void *)arg; if (tp->ops->bind_class) { - tcf_tree_lock(tp); + struct Qdisc *q = tcf_block_q(tp->chain->block); + + sch_tree_lock(q); tp->ops->bind_class(n, a->classid, a->cl); - tcf_tree_unlock(tp); + sch_tree_unlock(q); } return 0; } -- cgit v1.2.3-59-g8ed1b From 0da4af00b2ed3dbe46788623a696c4169447eadc Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 13 Oct 2017 15:08:07 -0700 Subject: ipv6: only update __use and lastusetime once per jiffy at most In order to not dirty the cacheline too often, we try to only update dst->__use and dst->lastusetime at most once per jiffy. As dst->lastusetime is only used by ipv6 garbage collector, it should be good enough time resolution. And __use is only used in ipv6_route_seq_show() to show how many times a dst has been used. And as __use is not atomic_t right now, it does not show the precise number of usage times anyway. So we think it should be OK to only update it at most once per jiffy. According to my latest syn flood test on a machine with intel Xeon 6th gen processor and 2 10G mlx nics bonded together, each with 8 rx queues on 2 NUMA nodes: With this patch, the packet process rate increases from ~3.49Mpps to ~3.75Mpps with a 7% increase rate. Note: dst_use() is being renamed to dst_hold_and_use() to better specify the purpose of the function. Signed-off-by: Wei Wang Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/dst.h | 15 ++++++++------- net/decnet/dn_route.c | 8 ++++---- 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 204c19e25456..5047e8053d6c 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -255,17 +255,18 @@ static inline void dst_hold(struct dst_entry *dst) WARN_ON(atomic_inc_not_zero(&dst->__refcnt) == 0); } -static inline void dst_use(struct dst_entry *dst, unsigned long time) +static inline void dst_use_noref(struct dst_entry *dst, unsigned long time) { - dst_hold(dst); - dst->__use++; - dst->lastuse = time; + if (time != dst->lastuse) { + dst->__use++; + dst->lastuse = time; + } } -static inline void dst_use_noref(struct dst_entry *dst, unsigned long time) +static inline void dst_hold_and_use(struct dst_entry *dst, unsigned long time) { - dst->__use++; - dst->lastuse = time; + dst_hold(dst); + dst_use_noref(dst, time); } static inline struct dst_entry *dst_clone(struct dst_entry *dst) diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 0bd3afd01dd2..bff5ab88cdbb 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -338,7 +338,7 @@ static int dn_insert_route(struct dn_route *rt, unsigned int hash, struct dn_rou dn_rt_hash_table[hash].chain); rcu_assign_pointer(dn_rt_hash_table[hash].chain, rth); - dst_use(&rth->dst, now); + dst_hold_and_use(&rth->dst, now); spin_unlock_bh(&dn_rt_hash_table[hash].lock); dst_release_immediate(&rt->dst); @@ -351,7 +351,7 @@ static int dn_insert_route(struct dn_route *rt, unsigned int hash, struct dn_rou rcu_assign_pointer(rt->dst.dn_next, dn_rt_hash_table[hash].chain); rcu_assign_pointer(dn_rt_hash_table[hash].chain, rt); - dst_use(&rt->dst, now); + dst_hold_and_use(&rt->dst, now); spin_unlock_bh(&dn_rt_hash_table[hash].lock); *rp = rt; return 0; @@ -1258,7 +1258,7 @@ static int __dn_route_output_key(struct dst_entry **pprt, const struct flowidn * (flp->flowidn_mark == rt->fld.flowidn_mark) && dn_is_output_route(rt) && (rt->fld.flowidn_oif == flp->flowidn_oif)) { - dst_use(&rt->dst, jiffies); + dst_hold_and_use(&rt->dst, jiffies); rcu_read_unlock_bh(); *pprt = &rt->dst; return 0; @@ -1535,7 +1535,7 @@ static int dn_route_input(struct sk_buff *skb) (rt->fld.flowidn_oif == 0) && (rt->fld.flowidn_mark == skb->mark) && (rt->fld.flowidn_iif == cb->iif)) { - dst_use(&rt->dst, jiffies); + dst_hold_and_use(&rt->dst, jiffies); rcu_read_unlock(); skb_dst_set(skb, (struct dst_entry *)rt); return 0; -- cgit v1.2.3-59-g8ed1b From 9185a610f8f7f1b4e4d28c9de27d1969cf58e0f1 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Thu, 12 Oct 2017 18:40:02 -0400 Subject: tracing: bpf: Hide bpf trace events when they are not used All the trace events defined in include/trace/events/bpf.h are only used when CONFIG_BPF_SYSCALL is defined. But this file gets included by include/linux/bpf_trace.h which is included by the networking code with CREATE_TRACE_POINTS defined. If a trace event is created but not used it still has data structures and functions created for its use, even though nothing is using them. To not waste space, do not define the BPF trace events in bpf.h unless CONFIG_BPF_SYSCALL is defined. Signed-off-by: Steven Rostedt (VMware) Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/trace/events/bpf.h | 5 ++++- kernel/bpf/core.c | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/events/bpf.h b/include/trace/events/bpf.h index 52c8425d144b..1fb58faa4a44 100644 --- a/include/trace/events/bpf.h +++ b/include/trace/events/bpf.h @@ -4,6 +4,9 @@ #if !defined(_TRACE_BPF_H) || defined(TRACE_HEADER_MULTI_READ) #define _TRACE_BPF_H +/* These are only used within the BPF_SYSCALL code */ +#ifdef CONFIG_BPF_SYSCALL + #include #include #include @@ -345,7 +348,7 @@ TRACE_EVENT(bpf_map_next_key, __print_hex(__get_dynamic_array(nxt), __entry->key_len), __entry->key_trunc ? " ..." : "") ); - +#endif /* CONFIG_BPF_SYSCALL */ #endif /* _TRACE_BPF_H */ #include diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 248961af2421..8e7c8bf2b687 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1580,5 +1580,8 @@ int __weak skb_copy_bits(const struct sk_buff *skb, int offset, void *to, EXPORT_TRACEPOINT_SYMBOL_GPL(xdp_exception); +/* These are only used within the BPF_SYSCALL code */ +#ifdef CONFIG_BPF_SYSCALL EXPORT_TRACEPOINT_SYMBOL_GPL(bpf_prog_get_type); EXPORT_TRACEPOINT_SYMBOL_GPL(bpf_prog_put_rcu); +#endif -- cgit v1.2.3-59-g8ed1b From 136412474b77c14a26e018a7f2c1e5fcee700d3d Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 13 Oct 2017 10:06:25 -0400 Subject: tracing, dma-buf: Remove unused trace event dma_fence_annotate_wait_on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit e941759c74 ("fence: dma-buf cross-device synchronization") added trace event fence_annotate_wait_on, but never used it. It was renamed to dma_fence_annotate_wait_on by commit f54d186700 ("dma-buf: Rename struct fence to dma_fence") but still not used. As defined trace events have data structures and functions created for them, it is a waste of memory if they are not used. Remove the unused trace event. Link: http://lkml.kernel.org/r/20171013100625.6c820059@gandalf.local.home Reviewed-by: Christian König Acked-by: Sumit Semwal Signed-off-by: Steven Rostedt (VMware) --- drivers/dma-buf/dma-fence.c | 1 - include/trace/events/dma_fence.h | 40 ---------------------------------------- 2 files changed, 41 deletions(-) (limited to 'include') diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 9a302799040e..5d101c4053e0 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -27,7 +27,6 @@ #define CREATE_TRACE_POINTS #include -EXPORT_TRACEPOINT_SYMBOL(dma_fence_annotate_wait_on); EXPORT_TRACEPOINT_SYMBOL(dma_fence_emit); EXPORT_TRACEPOINT_SYMBOL(dma_fence_enable_signal); diff --git a/include/trace/events/dma_fence.h b/include/trace/events/dma_fence.h index 1157cb4c3c6f..50fbdefe6c81 100644 --- a/include/trace/events/dma_fence.h +++ b/include/trace/events/dma_fence.h @@ -8,46 +8,6 @@ struct dma_fence; -TRACE_EVENT(dma_fence_annotate_wait_on, - - /* fence: the fence waiting on f1, f1: the fence to be waited on. */ - TP_PROTO(struct dma_fence *fence, struct dma_fence *f1), - - TP_ARGS(fence, f1), - - TP_STRUCT__entry( - __string(driver, fence->ops->get_driver_name(fence)) - __string(timeline, fence->ops->get_timeline_name(fence)) - __field(unsigned int, context) - __field(unsigned int, seqno) - - __string(waiting_driver, f1->ops->get_driver_name(f1)) - __string(waiting_timeline, f1->ops->get_timeline_name(f1)) - __field(unsigned int, waiting_context) - __field(unsigned int, waiting_seqno) - ), - - TP_fast_assign( - __assign_str(driver, fence->ops->get_driver_name(fence)) - __assign_str(timeline, fence->ops->get_timeline_name(fence)) - __entry->context = fence->context; - __entry->seqno = fence->seqno; - - __assign_str(waiting_driver, f1->ops->get_driver_name(f1)) - __assign_str(waiting_timeline, f1->ops->get_timeline_name(f1)) - __entry->waiting_context = f1->context; - __entry->waiting_seqno = f1->seqno; - - ), - - TP_printk("driver=%s timeline=%s context=%u seqno=%u " \ - "waits on driver=%s timeline=%s context=%u seqno=%u", - __get_str(driver), __get_str(timeline), __entry->context, - __entry->seqno, - __get_str(waiting_driver), __get_str(waiting_timeline), - __entry->waiting_context, __entry->waiting_seqno) -); - DECLARE_EVENT_CLASS(dma_fence, TP_PROTO(struct dma_fence *fence), -- cgit v1.2.3-59-g8ed1b From 8fd0fbbe8888f295eb34172a7e47bf7d3a0a4687 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 11 Oct 2017 09:45:29 +0200 Subject: perf/ftrace: Revert ("perf/ftrace: Fix double traces of perf on ftrace:function") Revert commit: 75e8387685f6 ("perf/ftrace: Fix double traces of perf on ftrace:function") The reason I instantly stumbled on that patch is that it only addresses the ftrace situation and doesn't mention the other _5_ places that use this interface. It doesn't explain why those don't have the problem and if not, why their solution doesn't work for ftrace. It doesn't, but this is just putting more duct tape on. Link: http://lkml.kernel.org/r/20171011080224.200565770@infradead.org Cc: Zhou Chengming Cc: Jiri Olsa Cc: Ingo Molnar Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Steven Rostedt (VMware) --- include/linux/perf_event.h | 2 +- include/linux/trace_events.h | 4 ++-- kernel/events/core.c | 13 ++++--------- kernel/trace/trace_event_perf.c | 4 +--- kernel/trace/trace_kprobe.c | 4 ++-- kernel/trace/trace_syscalls.c | 4 ++-- kernel/trace/trace_uprobe.c | 2 +- 7 files changed, 13 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 8e22f24ded6a..569d1b54e201 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1184,7 +1184,7 @@ extern void perf_event_init(void); extern void perf_tp_event(u16 event_type, u64 count, void *record, int entry_size, struct pt_regs *regs, struct hlist_head *head, int rctx, - struct task_struct *task, struct perf_event *event); + struct task_struct *task); extern void perf_bp_event(struct perf_event *event, void *data); #ifndef perf_misc_flags diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 2e0f22298fe9..a6349b76fd39 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -507,9 +507,9 @@ void perf_trace_run_bpf_submit(void *raw_data, int size, int rctx, static inline void perf_trace_buf_submit(void *raw_data, int size, int rctx, u16 type, u64 count, struct pt_regs *regs, void *head, - struct task_struct *task, struct perf_event *event) + struct task_struct *task) { - perf_tp_event(type, count, raw_data, size, regs, head, rctx, task, event); + perf_tp_event(type, count, raw_data, size, regs, head, rctx, task); } #endif diff --git a/kernel/events/core.c b/kernel/events/core.c index 6bc21e202ae4..b8db80c5513b 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7954,15 +7954,16 @@ void perf_trace_run_bpf_submit(void *raw_data, int size, int rctx, } } perf_tp_event(call->event.type, count, raw_data, size, regs, head, - rctx, task, NULL); + rctx, task); } EXPORT_SYMBOL_GPL(perf_trace_run_bpf_submit); void perf_tp_event(u16 event_type, u64 count, void *record, int entry_size, struct pt_regs *regs, struct hlist_head *head, int rctx, - struct task_struct *task, struct perf_event *event) + struct task_struct *task) { struct perf_sample_data data; + struct perf_event *event; struct perf_raw_record raw = { .frag = { @@ -7976,15 +7977,9 @@ void perf_tp_event(u16 event_type, u64 count, void *record, int entry_size, perf_trace_buf_update(record, event_type); - /* Use the given event instead of the hlist */ - if (event) { + hlist_for_each_entry_rcu(event, head, hlist_entry) { if (perf_tp_event_match(event, &data, regs)) perf_swevent_event(event, count, &data, regs); - } else { - hlist_for_each_entry_rcu(event, head, hlist_entry) { - if (perf_tp_event_match(event, &data, regs)) - perf_swevent_event(event, count, &data, regs); - } } /* diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index 13ba2d3f6a91..562fa69df5d3 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -306,7 +306,6 @@ static void perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *pt_regs) { - struct perf_event *event; struct ftrace_entry *entry; struct hlist_head *head; struct pt_regs regs; @@ -330,9 +329,8 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip, entry->ip = ip; entry->parent_ip = parent_ip; - event = container_of(ops, struct perf_event, ftrace_ops); perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, TRACE_FN, - 1, ®s, head, NULL, event); + 1, ®s, head, NULL); #undef ENTRY_SIZE } diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index af6134f2e597..996902a526d4 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1200,7 +1200,7 @@ kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs) memset(&entry[1], 0, dsize); store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, - head, NULL, NULL); + head, NULL); } NOKPROBE_SYMBOL(kprobe_perf_func); @@ -1236,7 +1236,7 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, entry->ret_ip = (unsigned long)ri->ret_addr; store_trace_args(sizeof(*entry), &tk->tp, regs, (u8 *)&entry[1], dsize); perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, - head, NULL, NULL); + head, NULL); } NOKPROBE_SYMBOL(kretprobe_perf_func); #endif /* CONFIG_PERF_EVENTS */ diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 696afe72d3b1..934b0da72679 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -622,7 +622,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) perf_trace_buf_submit(rec, size, rctx, sys_data->enter_event->event.type, 1, regs, - head, NULL, NULL); + head, NULL); } static int perf_sysenter_enable(struct trace_event_call *call) @@ -716,7 +716,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) } perf_trace_buf_submit(rec, size, rctx, sys_data->exit_event->event.type, - 1, regs, head, NULL, NULL); + 1, regs, head, NULL); } static int perf_sysexit_enable(struct trace_event_call *call) diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index b34965e62fb9..402120ba4594 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -1156,7 +1156,7 @@ static void __uprobe_perf_func(struct trace_uprobe *tu, } perf_trace_buf_submit(entry, size, rctx, call->event.type, 1, regs, - head, NULL, NULL); + head, NULL); out: preempt_enable(); } -- cgit v1.2.3-59-g8ed1b From 466c81c45b650deca213fda3d0ec4761667379a9 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 10 Oct 2017 17:15:47 +0200 Subject: perf/ftrace: Fix function trace events The function-trace <-> perf interface is a tad messed up. Where all the other trace <-> perf interfaces use a single trace hook registration and use per-cpu RCU based hlist to iterate the events, function-trace actually needs multiple hook registrations in order to minimize function entry patching when filters are present. The end result is that we iterate events both on the trace hook and on the hlist, which results in reporting events multiple times. Since function-trace cannot use the regular scheme, fix it the other way around, use singleton hlists. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Steven Rostedt (VMware) --- include/linux/trace_events.h | 5 +++ kernel/trace/trace_event_perf.c | 80 +++++++++++++++++++++++++---------------- 2 files changed, 54 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index a6349b76fd39..ca4e67e466a7 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -173,6 +173,11 @@ enum trace_reg { TRACE_REG_PERF_UNREGISTER, TRACE_REG_PERF_OPEN, TRACE_REG_PERF_CLOSE, + /* + * These (ADD/DEL) use a 'boolean' return value, where 1 (true) means a + * custom action was taken and the default action is not to be + * performed. + */ TRACE_REG_PERF_ADD, TRACE_REG_PERF_DEL, #endif diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index 562fa69df5d3..e73f9ab15939 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -240,27 +240,41 @@ void perf_trace_destroy(struct perf_event *p_event) int perf_trace_add(struct perf_event *p_event, int flags) { struct trace_event_call *tp_event = p_event->tp_event; - struct hlist_head __percpu *pcpu_list; - struct hlist_head *list; - - pcpu_list = tp_event->perf_events; - if (WARN_ON_ONCE(!pcpu_list)) - return -EINVAL; if (!(flags & PERF_EF_START)) p_event->hw.state = PERF_HES_STOPPED; - list = this_cpu_ptr(pcpu_list); - hlist_add_head_rcu(&p_event->hlist_entry, list); + /* + * If TRACE_REG_PERF_ADD returns false; no custom action was performed + * and we need to take the default action of enqueueing our event on + * the right per-cpu hlist. + */ + if (!tp_event->class->reg(tp_event, TRACE_REG_PERF_ADD, p_event)) { + struct hlist_head __percpu *pcpu_list; + struct hlist_head *list; + + pcpu_list = tp_event->perf_events; + if (WARN_ON_ONCE(!pcpu_list)) + return -EINVAL; + + list = this_cpu_ptr(pcpu_list); + hlist_add_head_rcu(&p_event->hlist_entry, list); + } - return tp_event->class->reg(tp_event, TRACE_REG_PERF_ADD, p_event); + return 0; } void perf_trace_del(struct perf_event *p_event, int flags) { struct trace_event_call *tp_event = p_event->tp_event; - hlist_del_rcu(&p_event->hlist_entry); - tp_event->class->reg(tp_event, TRACE_REG_PERF_DEL, p_event); + + /* + * If TRACE_REG_PERF_DEL returns false; no custom action was performed + * and we need to take the default action of dequeueing our event from + * the right per-cpu hlist. + */ + if (!tp_event->class->reg(tp_event, TRACE_REG_PERF_DEL, p_event)) + hlist_del_rcu(&p_event->hlist_entry); } void *perf_trace_buf_alloc(int size, struct pt_regs **regs, int *rctxp) @@ -307,14 +321,24 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *ops, struct pt_regs *pt_regs) { struct ftrace_entry *entry; - struct hlist_head *head; + struct perf_event *event; + struct hlist_head head; struct pt_regs regs; int rctx; - head = this_cpu_ptr(event_function.perf_events); - if (hlist_empty(head)) + if ((unsigned long)ops->private != smp_processor_id()) return; + event = container_of(ops, struct perf_event, ftrace_ops); + + /* + * @event->hlist entry is NULL (per INIT_HLIST_NODE), and all + * the perf code does is hlist_for_each_entry_rcu(), so we can + * get away with simply setting the @head.first pointer in order + * to create a singular list. + */ + head.first = &event->hlist_entry; + #define ENTRY_SIZE (ALIGN(sizeof(struct ftrace_entry) + sizeof(u32), \ sizeof(u64)) - sizeof(u32)) @@ -330,7 +354,7 @@ perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip, entry->ip = ip; entry->parent_ip = parent_ip; perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, TRACE_FN, - 1, ®s, head, NULL); + 1, ®s, &head, NULL); #undef ENTRY_SIZE } @@ -339,8 +363,10 @@ static int perf_ftrace_function_register(struct perf_event *event) { struct ftrace_ops *ops = &event->ftrace_ops; - ops->flags |= FTRACE_OPS_FL_PER_CPU | FTRACE_OPS_FL_RCU; - ops->func = perf_ftrace_function_call; + ops->flags |= FTRACE_OPS_FL_RCU; + ops->func = perf_ftrace_function_call; + ops->private = (void *)(unsigned long)nr_cpu_ids; + return register_ftrace_function(ops); } @@ -352,19 +378,11 @@ static int perf_ftrace_function_unregister(struct perf_event *event) return ret; } -static void perf_ftrace_function_enable(struct perf_event *event) -{ - ftrace_function_local_enable(&event->ftrace_ops); -} - -static void perf_ftrace_function_disable(struct perf_event *event) -{ - ftrace_function_local_disable(&event->ftrace_ops); -} - int perf_ftrace_event_register(struct trace_event_call *call, enum trace_reg type, void *data) { + struct perf_event *event = data; + switch (type) { case TRACE_REG_REGISTER: case TRACE_REG_UNREGISTER: @@ -377,11 +395,11 @@ int perf_ftrace_event_register(struct trace_event_call *call, case TRACE_REG_PERF_CLOSE: return perf_ftrace_function_unregister(data); case TRACE_REG_PERF_ADD: - perf_ftrace_function_enable(data); - return 0; + event->ftrace_ops.private = (void *)(unsigned long)smp_processor_id(); + return 1; case TRACE_REG_PERF_DEL: - perf_ftrace_function_disable(data); - return 0; + event->ftrace_ops.private = (void *)(unsigned long)nr_cpu_ids; + return 1; } return -EINVAL; -- cgit v1.2.3-59-g8ed1b From b3a88803ac5b4bda26017b485c8722a8487fefb7 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 11 Oct 2017 09:45:32 +0200 Subject: ftrace: Kill FTRACE_OPS_FL_PER_CPU The one and only user of FTRACE_OPS_FL_PER_CPU is gone, remove the lot. Link: http://lkml.kernel.org/r/20171011080224.372422809@infradead.org Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Steven Rostedt (VMware) --- include/linux/ftrace.h | 83 +++++++++----------------------------------------- kernel/trace/ftrace.c | 55 ++++----------------------------- 2 files changed, 20 insertions(+), 118 deletions(-) (limited to 'include') diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 1f8545caa691..252e334e7b5f 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -102,10 +102,6 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops); * ENABLED - set/unset when ftrace_ops is registered/unregistered * DYNAMIC - set when ftrace_ops is registered to denote dynamically * allocated ftrace_ops which need special care - * PER_CPU - set manualy by ftrace_ops user to denote the ftrace_ops - * could be controlled by following calls: - * ftrace_function_local_enable - * ftrace_function_local_disable * SAVE_REGS - The ftrace_ops wants regs saved at each function called * and passed to the callback. If this flag is set, but the * architecture does not support passing regs @@ -149,21 +145,20 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops); enum { FTRACE_OPS_FL_ENABLED = 1 << 0, FTRACE_OPS_FL_DYNAMIC = 1 << 1, - FTRACE_OPS_FL_PER_CPU = 1 << 2, - FTRACE_OPS_FL_SAVE_REGS = 1 << 3, - FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 4, - FTRACE_OPS_FL_RECURSION_SAFE = 1 << 5, - FTRACE_OPS_FL_STUB = 1 << 6, - FTRACE_OPS_FL_INITIALIZED = 1 << 7, - FTRACE_OPS_FL_DELETED = 1 << 8, - FTRACE_OPS_FL_ADDING = 1 << 9, - FTRACE_OPS_FL_REMOVING = 1 << 10, - FTRACE_OPS_FL_MODIFYING = 1 << 11, - FTRACE_OPS_FL_ALLOC_TRAMP = 1 << 12, - FTRACE_OPS_FL_IPMODIFY = 1 << 13, - FTRACE_OPS_FL_PID = 1 << 14, - FTRACE_OPS_FL_RCU = 1 << 15, - FTRACE_OPS_FL_TRACE_ARRAY = 1 << 16, + FTRACE_OPS_FL_SAVE_REGS = 1 << 2, + FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 3, + FTRACE_OPS_FL_RECURSION_SAFE = 1 << 4, + FTRACE_OPS_FL_STUB = 1 << 5, + FTRACE_OPS_FL_INITIALIZED = 1 << 6, + FTRACE_OPS_FL_DELETED = 1 << 7, + FTRACE_OPS_FL_ADDING = 1 << 8, + FTRACE_OPS_FL_REMOVING = 1 << 9, + FTRACE_OPS_FL_MODIFYING = 1 << 10, + FTRACE_OPS_FL_ALLOC_TRAMP = 1 << 11, + FTRACE_OPS_FL_IPMODIFY = 1 << 12, + FTRACE_OPS_FL_PID = 1 << 13, + FTRACE_OPS_FL_RCU = 1 << 14, + FTRACE_OPS_FL_TRACE_ARRAY = 1 << 15, }; #ifdef CONFIG_DYNAMIC_FTRACE @@ -198,7 +193,6 @@ struct ftrace_ops { unsigned long flags; void *private; ftrace_func_t saved_func; - int __percpu *disabled; #ifdef CONFIG_DYNAMIC_FTRACE struct ftrace_ops_hash local_hash; struct ftrace_ops_hash *func_hash; @@ -230,55 +224,6 @@ int register_ftrace_function(struct ftrace_ops *ops); int unregister_ftrace_function(struct ftrace_ops *ops); void clear_ftrace_function(void); -/** - * ftrace_function_local_enable - enable ftrace_ops on current cpu - * - * This function enables tracing on current cpu by decreasing - * the per cpu control variable. - * It must be called with preemption disabled and only on ftrace_ops - * registered with FTRACE_OPS_FL_PER_CPU. If called without preemption - * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. - */ -static inline void ftrace_function_local_enable(struct ftrace_ops *ops) -{ - if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU))) - return; - - (*this_cpu_ptr(ops->disabled))--; -} - -/** - * ftrace_function_local_disable - disable ftrace_ops on current cpu - * - * This function disables tracing on current cpu by increasing - * the per cpu control variable. - * It must be called with preemption disabled and only on ftrace_ops - * registered with FTRACE_OPS_FL_PER_CPU. If called without preemption - * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. - */ -static inline void ftrace_function_local_disable(struct ftrace_ops *ops) -{ - if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU))) - return; - - (*this_cpu_ptr(ops->disabled))++; -} - -/** - * ftrace_function_local_disabled - returns ftrace_ops disabled value - * on current cpu - * - * This function returns value of ftrace_ops::disabled on current cpu. - * It must be called with preemption disabled and only on ftrace_ops - * registered with FTRACE_OPS_FL_PER_CPU. If called without preemption - * disabled, this_cpu_ptr will complain when CONFIG_DEBUG_PREEMPT is enabled. - */ -static inline int ftrace_function_local_disabled(struct ftrace_ops *ops) -{ - WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU)); - return *this_cpu_ptr(ops->disabled); -} - extern void ftrace_stub(unsigned long a0, unsigned long a1, struct ftrace_ops *op, struct pt_regs *regs); diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index e0a98225666b..2fd3edaec6de 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -203,30 +203,6 @@ void clear_ftrace_function(void) ftrace_trace_function = ftrace_stub; } -static void per_cpu_ops_disable_all(struct ftrace_ops *ops) -{ - int cpu; - - for_each_possible_cpu(cpu) - *per_cpu_ptr(ops->disabled, cpu) = 1; -} - -static int per_cpu_ops_alloc(struct ftrace_ops *ops) -{ - int __percpu *disabled; - - if (WARN_ON_ONCE(!(ops->flags & FTRACE_OPS_FL_PER_CPU))) - return -EINVAL; - - disabled = alloc_percpu(int); - if (!disabled) - return -ENOMEM; - - ops->disabled = disabled; - per_cpu_ops_disable_all(ops); - return 0; -} - static void ftrace_sync(struct work_struct *work) { /* @@ -262,8 +238,8 @@ static ftrace_func_t ftrace_ops_get_list_func(struct ftrace_ops *ops) * If this is a dynamic, RCU, or per CPU ops, or we force list func, * then it needs to call the list anyway. */ - if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU | - FTRACE_OPS_FL_RCU) || FTRACE_FORCE_LIST_FUNC) + if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_RCU) || + FTRACE_FORCE_LIST_FUNC) return ftrace_ops_list_func; return ftrace_ops_get_func(ops); @@ -422,11 +398,6 @@ static int __register_ftrace_function(struct ftrace_ops *ops) if (!core_kernel_data((unsigned long)ops)) ops->flags |= FTRACE_OPS_FL_DYNAMIC; - if (ops->flags & FTRACE_OPS_FL_PER_CPU) { - if (per_cpu_ops_alloc(ops)) - return -ENOMEM; - } - add_ftrace_ops(&ftrace_ops_list, ops); /* Always save the function, and reset at unregistering */ @@ -2727,11 +2698,6 @@ void __weak arch_ftrace_trampoline_free(struct ftrace_ops *ops) { } -static void per_cpu_ops_free(struct ftrace_ops *ops) -{ - free_percpu(ops->disabled); -} - static void ftrace_startup_enable(int command) { if (saved_ftrace_func != ftrace_trace_function) { @@ -2833,7 +2799,7 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command) * not currently active, we can just free them * without synchronizing all CPUs. */ - if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU)) + if (ops->flags & FTRACE_OPS_FL_DYNAMIC) goto free_ops; return 0; @@ -2880,7 +2846,7 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command) * The same goes for freeing the per_cpu data of the per_cpu * ops. */ - if (ops->flags & (FTRACE_OPS_FL_DYNAMIC | FTRACE_OPS_FL_PER_CPU)) { + if (ops->flags & FTRACE_OPS_FL_DYNAMIC) { /* * We need to do a hard force of sched synchronization. * This is because we use preempt_disable() to do RCU, but @@ -2903,9 +2869,6 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command) free_ops: arch_ftrace_trampoline_free(ops); - - if (ops->flags & FTRACE_OPS_FL_PER_CPU) - per_cpu_ops_free(ops); } return 0; @@ -6355,10 +6318,7 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, * If any of the above fails then the op->func() is not executed. */ if ((!(op->flags & FTRACE_OPS_FL_RCU) || rcu_is_watching()) && - (!(op->flags & FTRACE_OPS_FL_PER_CPU) || - !ftrace_function_local_disabled(op)) && ftrace_ops_test(op, ip, regs)) { - if (FTRACE_WARN_ON(!op->func)) { pr_warn("op=%p %pS\n", op, op); goto out; @@ -6416,10 +6376,7 @@ static void ftrace_ops_assist_func(unsigned long ip, unsigned long parent_ip, preempt_disable_notrace(); - if (!(op->flags & FTRACE_OPS_FL_PER_CPU) || - !ftrace_function_local_disabled(op)) { - op->func(ip, parent_ip, op, regs); - } + op->func(ip, parent_ip, op, regs); preempt_enable_notrace(); trace_clear_recursion(bit); @@ -6443,7 +6400,7 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops) * or does per cpu logic, then we need to call the assist handler. */ if (!(ops->flags & FTRACE_OPS_FL_RECURSION_SAFE) || - ops->flags & (FTRACE_OPS_FL_RCU | FTRACE_OPS_FL_PER_CPU)) + ops->flags & FTRACE_OPS_FL_RCU) return ftrace_ops_assist_func; return ops->func; -- cgit v1.2.3-59-g8ed1b From cbe25ce37d6c2623b5ac09128987e98848a54c6c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 14 Oct 2017 17:43:15 +0200 Subject: ACPI / PM: Combine device suspend routines On top of a previous change getting rid of the PM QoS flag PM_QOS_FLAG_REMOTE_WAKEUP, combine two ACPI device suspend routines, acpi_dev_runtime_suspend() and acpi_dev_suspend_late(), into one, acpi_dev_suspend(), to eliminate some code duplication. It also avoids enabling wakeup for devices handled by the ACPI LPSS middle layer on driver removal. Signed-off-by: Rafael J. Wysocki Reviewed-by: Ulf Hansson --- drivers/acpi/acpi_lpss.c | 6 ++--- drivers/acpi/device_pm.c | 61 +++++++++++------------------------------------- include/linux/acpi.h | 3 +-- 3 files changed, 18 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 97b753dd2e6e..8ec19e7c7b61 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -713,7 +713,7 @@ static int acpi_lpss_activate(struct device *dev) static void acpi_lpss_dismiss(struct device *dev) { - acpi_dev_runtime_suspend(dev); + acpi_dev_suspend(dev, false); } #ifdef CONFIG_PM_SLEEP @@ -729,7 +729,7 @@ static int acpi_lpss_suspend_late(struct device *dev) if (pdata->dev_desc->flags & LPSS_SAVE_CTX) acpi_lpss_save_ctx(dev, pdata); - return acpi_dev_suspend_late(dev); + return acpi_dev_suspend(dev, device_may_wakeup(dev)); } static int acpi_lpss_resume_early(struct device *dev) @@ -847,7 +847,7 @@ static int acpi_lpss_runtime_suspend(struct device *dev) if (pdata->dev_desc->flags & LPSS_SAVE_CTX) acpi_lpss_save_ctx(dev, pdata); - ret = acpi_dev_runtime_suspend(dev); + ret = acpi_dev_suspend(dev, true); /* * This call must be last in the sequence, otherwise PMC will return diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index d74000acb658..17e8eb93a76c 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -847,37 +847,39 @@ static int acpi_dev_pm_full_power(struct acpi_device *adev) } /** - * acpi_dev_runtime_suspend - Put device into a low-power state using ACPI. + * acpi_dev_suspend - Put device into a low-power state using ACPI. * @dev: Device to put into a low-power state. + * @wakeup: Whether or not to enable wakeup for the device. * - * Put the given device into a runtime low-power state using the standard ACPI + * Put the given device into a low-power state using the standard ACPI * mechanism. Set up remote wakeup if desired, choose the state to put the * device into (this checks if remote wakeup is expected to work too), and set * the power state of the device. */ -int acpi_dev_runtime_suspend(struct device *dev) +int acpi_dev_suspend(struct device *dev, bool wakeup) { struct acpi_device *adev = ACPI_COMPANION(dev); - bool remote_wakeup; + u32 target_state = acpi_target_system_state(); int error; if (!adev) return 0; - remote_wakeup = acpi_device_can_wakeup(adev); - if (remote_wakeup) { - error = acpi_device_wakeup_enable(adev, ACPI_STATE_S0); + if (wakeup && acpi_device_can_wakeup(adev)) { + error = acpi_device_wakeup_enable(adev, target_state); if (error) return -EAGAIN; + } else { + wakeup = false; } - error = acpi_dev_pm_low_power(dev, adev, ACPI_STATE_S0); - if (error && remote_wakeup) + error = acpi_dev_pm_low_power(dev, adev, target_state); + if (error && wakeup) acpi_device_wakeup_disable(adev); return error; } -EXPORT_SYMBOL_GPL(acpi_dev_runtime_suspend); +EXPORT_SYMBOL_GPL(acpi_dev_suspend); /** * acpi_dev_resume - Put device into the full-power state using ACPI. @@ -910,7 +912,7 @@ EXPORT_SYMBOL_GPL(acpi_dev_resume); int acpi_subsys_runtime_suspend(struct device *dev) { int ret = pm_generic_runtime_suspend(dev); - return ret ? ret : acpi_dev_runtime_suspend(dev); + return ret ? ret : acpi_dev_suspend(dev, true); } EXPORT_SYMBOL_GPL(acpi_subsys_runtime_suspend); @@ -929,41 +931,6 @@ int acpi_subsys_runtime_resume(struct device *dev) EXPORT_SYMBOL_GPL(acpi_subsys_runtime_resume); #ifdef CONFIG_PM_SLEEP -/** - * acpi_dev_suspend_late - Put device into a low-power state using ACPI. - * @dev: Device to put into a low-power state. - * - * Put the given device into a low-power state during system transition to a - * sleep state using the standard ACPI mechanism. Set up system wakeup if - * desired, choose the state to put the device into (this checks if system - * wakeup is expected to work too), and set the power state of the device. - */ -int acpi_dev_suspend_late(struct device *dev) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - u32 target_state; - bool wakeup; - int error; - - if (!adev) - return 0; - - target_state = acpi_target_system_state(); - wakeup = device_may_wakeup(dev) && acpi_device_can_wakeup(adev); - if (wakeup) { - error = acpi_device_wakeup_enable(adev, target_state); - if (error) - return error; - } - - error = acpi_dev_pm_low_power(dev, adev, target_state); - if (error && wakeup) - acpi_device_wakeup_disable(adev); - - return error; -} -EXPORT_SYMBOL_GPL(acpi_dev_suspend_late); - static bool acpi_dev_needs_resume(struct device *dev, struct acpi_device *adev) { u32 sys_target = acpi_target_system_state(); @@ -1046,7 +1013,7 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend); int acpi_subsys_suspend_late(struct device *dev) { int ret = pm_generic_suspend_late(dev); - return ret ? ret : acpi_dev_suspend_late(dev); + return ret ? ret : acpi_dev_suspend(dev, device_may_wakeup(dev)); } EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 2b1738f840ab..0ada2a948b44 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -864,7 +864,7 @@ static inline void arch_reserve_mem_area(acpi_physical_address addr, #endif #if defined(CONFIG_ACPI) && defined(CONFIG_PM) -int acpi_dev_runtime_suspend(struct device *dev); +int acpi_dev_suspend(struct device *dev, bool wakeup); int acpi_dev_resume(struct device *dev); int acpi_subsys_runtime_suspend(struct device *dev); int acpi_subsys_runtime_resume(struct device *dev); @@ -889,7 +889,6 @@ int acpi_subsys_resume_early(struct device *dev); int acpi_subsys_suspend(struct device *dev); int acpi_subsys_freeze(struct device *dev); #else -static inline int acpi_dev_suspend_late(struct device *dev) { return 0; } static inline int acpi_dev_resume_early(struct device *dev) { return 0; } static inline int acpi_subsys_prepare(struct device *dev) { return 0; } static inline void acpi_subsys_complete(struct device *dev) {} -- cgit v1.2.3-59-g8ed1b From 40d86701a625eed9e644281b9af228d6a52d8ed9 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 13 Oct 2017 00:52:01 +0100 Subject: drm/plane: drop num_overlay_planes (v3) In order to implement plane leasing we need to count things, just make the code consistent with the counting code currently used for counting crtcs/encoders/connectors and drop the need for num_overlay_planes. v2: don't forget to assign plane_ptr. (keithp) v3: use correct bounds check, found by igt. Reviewed-by: Daniel Vetter Reviewed-by: Sean Paul Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_mode_config.c | 1 - drivers/gpu/drm/drm_plane.c | 46 ++++++++++++++------------------------- include/drm/drm_mode_config.h | 13 ----------- 3 files changed, 16 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 74f6ff5df656..919e78d45ab0 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -385,7 +385,6 @@ void drm_mode_config_init(struct drm_device *dev) dev->mode_config.num_connector = 0; dev->mode_config.num_crtc = 0; dev->mode_config.num_encoder = 0; - dev->mode_config.num_overlay_plane = 0; dev->mode_config.num_total_plane = 0; } EXPORT_SYMBOL(drm_mode_config_init); diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 6af02c7b5da3..8090e50607fa 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -241,8 +241,6 @@ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, list_add_tail(&plane->head, &config->plane_list); plane->index = config->num_total_plane++; - if (plane->type == DRM_PLANE_TYPE_OVERLAY) - config->num_overlay_plane++; drm_object_attach_property(&plane->base, config->plane_type_property, @@ -353,8 +351,6 @@ void drm_plane_cleanup(struct drm_plane *plane) list_del(&plane->head); dev->mode_config.num_total_plane--; - if (plane->type == DRM_PLANE_TYPE_OVERLAY) - dev->mode_config.num_overlay_plane--; WARN_ON(plane->state && !plane->funcs->atomic_destroy_state); if (plane->state && plane->funcs->atomic_destroy_state) @@ -462,43 +458,33 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, struct drm_mode_config *config; struct drm_plane *plane; uint32_t __user *plane_ptr; - int copied = 0; - unsigned num_planes; + int count = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; config = &dev->mode_config; - - if (file_priv->universal_planes) - num_planes = config->num_total_plane; - else - num_planes = config->num_overlay_plane; + plane_ptr = u64_to_user_ptr(plane_resp->plane_id_ptr); /* * This ioctl is called twice, once to determine how much space is * needed, and the 2nd time to fill it. */ - if (num_planes && - (plane_resp->count_planes >= num_planes)) { - plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; - - /* Plane lists are invariant, no locking needed. */ - drm_for_each_plane(plane, dev) { - /* - * Unless userspace set the 'universal planes' - * capability bit, only advertise overlays. - */ - if (plane->type != DRM_PLANE_TYPE_OVERLAY && - !file_priv->universal_planes) - continue; - - if (put_user(plane->base.id, plane_ptr + copied)) - return -EFAULT; - copied++; - } + drm_for_each_plane(plane, dev) { + /* + * Unless userspace set the 'universal planes' + * capability bit, only advertise overlays. + */ + if (plane->type != DRM_PLANE_TYPE_OVERLAY && + !file_priv->universal_planes) + continue; + + if (count < plane_resp->count_planes && + put_user(plane->base.id, plane_ptr + count)) + return -EFAULT; + count++; } - plane_resp->count_planes = num_planes; + plane_resp->count_planes = count; return 0; } diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 1b37368416c8..0b4ac2ebc610 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -429,19 +429,6 @@ struct drm_mode_config { */ struct list_head encoder_list; - /** - * @num_overlay_plane: - * - * Number of overlay planes on this device, excluding primary and cursor - * planes. - * - * Track number of overlay planes separately from number of total - * planes. By default we only advertise overlay planes to userspace; if - * userspace sets the "universal plane" capability bit, we'll go ahead - * and expose all planes. This is invariant over the lifetime of a - * device and hence doesn't need any locks. - */ - int num_overlay_plane; /** * @num_total_plane: * -- cgit v1.2.3-59-g8ed1b From aa8a84566282ac47bd861c3cc2538e1014228345 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Wed, 11 Oct 2017 05:54:21 +0900 Subject: scsi: sd_zbc: Move ZBC declarations to scsi_proto.h Move standard macro definitions for the zone types and zone conditions to scsi_proto.h together with the definitions related to the REPORT ZONES command. While at it, define all values in the enums to be clear. Also remove unnecessary includes in sd_zbc.c. No functional change is introduced by this patch. Signed-off-by: Damien Le Moal Reviewed-by: Bart Van Assche Reviewed-by: Johannes Thumshirn Reviewed-by: Christoph Hellwig Signed-off-by: Martin K. Petersen --- drivers/scsi/sd_zbc.c | 24 ------------------------ include/scsi/scsi_proto.h | 45 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 34 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index 8aa54779aac1..692c8cbc7ed8 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -28,32 +28,8 @@ #include #include -#include -#include -#include -#include -#include #include "sd.h" -#include "scsi_priv.h" - -enum zbc_zone_type { - ZBC_ZONE_TYPE_CONV = 0x1, - ZBC_ZONE_TYPE_SEQWRITE_REQ, - ZBC_ZONE_TYPE_SEQWRITE_PREF, - ZBC_ZONE_TYPE_RESERVED, -}; - -enum zbc_zone_cond { - ZBC_ZONE_COND_NO_WP, - ZBC_ZONE_COND_EMPTY, - ZBC_ZONE_COND_IMP_OPEN, - ZBC_ZONE_COND_EXP_OPEN, - ZBC_ZONE_COND_CLOSED, - ZBC_ZONE_COND_READONLY = 0xd, - ZBC_ZONE_COND_FULL, - ZBC_ZONE_COND_OFFLINE, -}; /** * Convert a zone descriptor to a zone struct. diff --git a/include/scsi/scsi_proto.h b/include/scsi/scsi_proto.h index 8c285d9a06d8..39130a9c05bf 100644 --- a/include/scsi/scsi_proto.h +++ b/include/scsi/scsi_proto.h @@ -301,19 +301,42 @@ struct scsi_lun { /* Reporting options for REPORT ZONES */ enum zbc_zone_reporting_options { - ZBC_ZONE_REPORTING_OPTION_ALL = 0, - ZBC_ZONE_REPORTING_OPTION_EMPTY, - ZBC_ZONE_REPORTING_OPTION_IMPLICIT_OPEN, - ZBC_ZONE_REPORTING_OPTION_EXPLICIT_OPEN, - ZBC_ZONE_REPORTING_OPTION_CLOSED, - ZBC_ZONE_REPORTING_OPTION_FULL, - ZBC_ZONE_REPORTING_OPTION_READONLY, - ZBC_ZONE_REPORTING_OPTION_OFFLINE, - ZBC_ZONE_REPORTING_OPTION_NEED_RESET_WP = 0x10, - ZBC_ZONE_REPORTING_OPTION_NON_SEQWRITE, - ZBC_ZONE_REPORTING_OPTION_NON_WP = 0x3f, + ZBC_ZONE_REPORTING_OPTION_ALL = 0x00, + ZBC_ZONE_REPORTING_OPTION_EMPTY = 0x01, + ZBC_ZONE_REPORTING_OPTION_IMPLICIT_OPEN = 0x02, + ZBC_ZONE_REPORTING_OPTION_EXPLICIT_OPEN = 0x03, + ZBC_ZONE_REPORTING_OPTION_CLOSED = 0x04, + ZBC_ZONE_REPORTING_OPTION_FULL = 0x05, + ZBC_ZONE_REPORTING_OPTION_READONLY = 0x06, + ZBC_ZONE_REPORTING_OPTION_OFFLINE = 0x07, + /* 0x08 to 0x0f are reserved */ + ZBC_ZONE_REPORTING_OPTION_NEED_RESET_WP = 0x10, + ZBC_ZONE_REPORTING_OPTION_NON_SEQWRITE = 0x11, + /* 0x12 to 0x3e are reserved */ + ZBC_ZONE_REPORTING_OPTION_NON_WP = 0x3f, }; #define ZBC_REPORT_ZONE_PARTIAL 0x80 +/* Zone types of REPORT ZONES zone descriptors */ +enum zbc_zone_type { + ZBC_ZONE_TYPE_CONV = 0x1, + ZBC_ZONE_TYPE_SEQWRITE_REQ = 0x2, + ZBC_ZONE_TYPE_SEQWRITE_PREF = 0x3, + /* 0x4 to 0xf are reserved */ +}; + +/* Zone conditions of REPORT ZONES zone descriptors */ +enum zbc_zone_cond { + ZBC_ZONE_COND_NO_WP = 0x0, + ZBC_ZONE_COND_EMPTY = 0x1, + ZBC_ZONE_COND_IMP_OPEN = 0x2, + ZBC_ZONE_COND_EXP_OPEN = 0x3, + ZBC_ZONE_COND_CLOSED = 0x4, + /* 0x5 to 0xc are reserved */ + ZBC_ZONE_COND_READONLY = 0xd, + ZBC_ZONE_COND_FULL = 0xe, + ZBC_ZONE_COND_OFFLINE = 0xf, +}; + #endif /* _SCSI_PROTO_H_ */ -- cgit v1.2.3-59-g8ed1b From f26aeada04938b99d6abbae9cd2de4ffa393e5d5 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 2 Oct 2017 16:26:35 +0200 Subject: scsi: scsi_devinfo: Reformat blacklist flags Reformat blacklist flags to make the values easier to read and to enhance error checking. Signed-off-by: Hannes Reinecke Reviewed-by: Bart van Assche Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen --- include/scsi/scsi_devinfo.h | 76 +++++++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/scsi/scsi_devinfo.h b/include/scsi/scsi_devinfo.h index 9592570e092a..7a2329c026a4 100644 --- a/include/scsi/scsi_devinfo.h +++ b/include/scsi/scsi_devinfo.h @@ -3,31 +3,55 @@ /* * Flags for SCSI devices that need special treatment */ -#define BLIST_NOLUN 0x001 /* Only scan LUN 0 */ -#define BLIST_FORCELUN 0x002 /* Known to have LUNs, force scanning, - deprecated: Use max_luns=N */ -#define BLIST_BORKEN 0x004 /* Flag for broken handshaking */ -#define BLIST_KEY 0x008 /* unlock by special command */ -#define BLIST_SINGLELUN 0x010 /* Do not use LUNs in parallel */ -#define BLIST_NOTQ 0x020 /* Buggy Tagged Command Queuing */ -#define BLIST_SPARSELUN 0x040 /* Non consecutive LUN numbering */ -#define BLIST_MAX5LUN 0x080 /* Avoid LUNS >= 5 */ -#define BLIST_ISROM 0x100 /* Treat as (removable) CD-ROM */ -#define BLIST_LARGELUN 0x200 /* LUNs past 7 on a SCSI-2 device */ -#define BLIST_INQUIRY_36 0x400 /* override additional length field */ -#define BLIST_NOSTARTONADD 0x1000 /* do not do automatic start on add */ -#define BLIST_REPORTLUN2 0x20000 /* try REPORT_LUNS even for SCSI-2 devs - (if HBA supports more than 8 LUNs) */ -#define BLIST_NOREPORTLUN 0x40000 /* don't try REPORT_LUNS scan (SCSI-3 devs) */ -#define BLIST_NOT_LOCKABLE 0x80000 /* don't use PREVENT-ALLOW commands */ -#define BLIST_NO_ULD_ATTACH 0x100000 /* device is actually for RAID config */ -#define BLIST_SELECT_NO_ATN 0x200000 /* select without ATN */ -#define BLIST_RETRY_HWERROR 0x400000 /* retry HARDWARE_ERROR */ -#define BLIST_MAX_512 0x800000 /* maximum 512 sector cdb length */ -#define BLIST_NO_DIF 0x2000000 /* Disable T10 PI (DIF) */ -#define BLIST_SKIP_VPD_PAGES 0x4000000 /* Ignore SBC-3 VPD pages */ -#define BLIST_TRY_VPD_PAGES 0x10000000 /* Attempt to read VPD pages */ -#define BLIST_NO_RSOC 0x20000000 /* don't try to issue RSOC */ -#define BLIST_MAX_1024 0x40000000 /* maximum 1024 sector cdb length */ + +/* Only scan LUN 0 */ +#define BLIST_NOLUN ((__force __u32 __bitwise)(1 << 0)) +/* Known to have LUNs, force scanning. + * DEPRECATED: Use max_luns=N */ +#define BLIST_FORCELUN ((__force __u32 __bitwise)(1 << 1)) +/* Flag for broken handshaking */ +#define BLIST_BORKEN ((__force __u32 __bitwise)(1 << 2)) +/* unlock by special command */ +#define BLIST_KEY ((__force __u32 __bitwise)(1 << 3)) +/* Do not use LUNs in parallel */ +#define BLIST_SINGLELUN ((__force __u32 __bitwise)(1 << 4)) +/* Buggy Tagged Command Queuing */ +#define BLIST_NOTQ ((__force __u32 __bitwise)(1 << 5)) +/* Non consecutive LUN numbering */ +#define BLIST_SPARSELUN ((__force __u32 __bitwise)(1 << 6)) +/* Avoid LUNS >= 5 */ +#define BLIST_MAX5LUN ((__force __u32 __bitwise)(1 << 7)) +/* Treat as (removable) CD-ROM */ +#define BLIST_ISROM ((__force __u32 __bitwise)(1 << 8)) +/* LUNs past 7 on a SCSI-2 device */ +#define BLIST_LARGELUN ((__force __u32 __bitwise)(1 << 9)) +/* override additional length field */ +#define BLIST_INQUIRY_36 ((__force __u32 __bitwise)(1 << 10)) +/* do not do automatic start on add */ +#define BLIST_NOSTARTONADD ((__force __u32 __bitwise)(1 << 12)) +/* try REPORT_LUNS even for SCSI-2 devs (if HBA supports more than 8 LUNs) */ +#define BLIST_REPORTLUN2 ((__force __u32 __bitwise)(1 << 17)) +/* don't try REPORT_LUNS scan (SCSI-3 devs) */ +#define BLIST_NOREPORTLUN ((__force __u32 __bitwise)(1 << 18)) +/* don't use PREVENT-ALLOW commands */ +#define BLIST_NOT_LOCKABLE ((__force __u32 __bitwise)(1 << 19)) +/* device is actually for RAID config */ +#define BLIST_NO_ULD_ATTACH ((__force __u32 __bitwise)(1 << 20)) +/* select without ATN */ +#define BLIST_SELECT_NO_ATN ((__force __u32 __bitwise)(1 << 21)) +/* retry HARDWARE_ERROR */ +#define BLIST_RETRY_HWERROR ((__force __u32 __bitwise)(1 << 22)) +/* maximum 512 sector cdb length */ +#define BLIST_MAX_512 ((__force __u32 __bitwise)(1 << 23)) +/* Disable T10 PI (DIF) */ +#define BLIST_NO_DIF ((__force __u32 __bitwise)(1 << 25)) +/* Ignore SBC-3 VPD pages */ +#define BLIST_SKIP_VPD_PAGES ((__force __u32 __bitwise)(1 << 26)) +/* Attempt to read VPD pages */ +#define BLIST_TRY_VPD_PAGES ((__force __u32 __bitwise)(1 << 28)) +/* don't try to issue RSOC */ +#define BLIST_NO_RSOC ((__force __u32 __bitwise)(1 << 29)) +/* maximum 1024 sector cdb length */ +#define BLIST_MAX_1024 ((__force __u32 __bitwise)(1 << 30)) #endif -- cgit v1.2.3-59-g8ed1b From 023b7b07ccb57317bee4d971be546cc4469f4e7e Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Thu, 31 Aug 2017 11:30:45 +0530 Subject: thermal : Remove const to make same prototype Here, prototype of thermal_zone_device_register is not matching with static inline thermal_zone_device_register. One is using const thermal_zone_params. Other is using non-const. Signed-off-by: Arvind Yadav Signed-off-by: Zhang Rui --- include/linux/thermal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/thermal.h b/include/linux/thermal.h index fd5b959c753c..8c5302374eaa 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -488,7 +488,7 @@ static inline int power_actor_set_power(struct thermal_cooling_device *cdev, static inline struct thermal_zone_device *thermal_zone_device_register( const char *type, int trips, int mask, void *devdata, struct thermal_zone_device_ops *ops, - const struct thermal_zone_params *tzp, + struct thermal_zone_params *tzp, int passive_delay, int polling_delay) { return ERR_PTR(-ENODEV); } static inline void thermal_zone_device_unregister( -- cgit v1.2.3-59-g8ed1b From 370d010f0ef09db7ab157a6b5d6d9a737b148f2a Mon Sep 17 00:00:00 2001 From: Timo Alho Date: Thu, 7 Sep 2017 12:31:01 +0300 Subject: firmware: tegra: Propagate error code to caller Response messages from Tegra BPMP firmware contain an error return code as the first word of payload. The error code is used to indicate incorrectly formatted request message or use of non-existing resource (clk, reset, powergate) identifier. Current implementation of tegra_bpmp_transfer() ignores this code and does not pass it to caller. Fix this by adding an extra struct member to tegra_bpmp_message and populate that with return code. Signed-off-by: Timo Alho Acked-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/firmware/tegra/bpmp.c | 22 ++++++++++++++++------ include/soc/tegra/bpmp.h | 1 + 2 files changed, 17 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 73ca55b7b7ec..33683b5a0d48 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -194,16 +194,24 @@ static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel) } static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, - void *data, size_t size) + void *data, size_t size, int *ret) { + int err; + if (data && size > 0) memcpy(data, channel->ib->data, size); - return tegra_ivc_read_advance(channel->ivc); + err = tegra_ivc_read_advance(channel->ivc); + if (err < 0) + return err; + + *ret = channel->ib->code; + + return 0; } static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, - void *data, size_t size) + void *data, size_t size, int *ret) { struct tegra_bpmp *bpmp = channel->bpmp; unsigned long flags; @@ -217,7 +225,7 @@ static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, } spin_lock_irqsave(&bpmp->lock, flags); - err = __tegra_bpmp_channel_read(channel, data, size); + err = __tegra_bpmp_channel_read(channel, data, size, ret); clear_bit(index, bpmp->threaded.allocated); spin_unlock_irqrestore(&bpmp->lock, flags); @@ -337,7 +345,8 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, if (err < 0) return err; - return __tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size); + return __tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size, + &msg->rx.ret); } EXPORT_SYMBOL_GPL(tegra_bpmp_transfer_atomic); @@ -371,7 +380,8 @@ int tegra_bpmp_transfer(struct tegra_bpmp *bpmp, if (err == 0) return -ETIMEDOUT; - return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size); + return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size, + &msg->rx.ret); } EXPORT_SYMBOL_GPL(tegra_bpmp_transfer); diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h index 9ba65222bd3f..57519f4c126a 100644 --- a/include/soc/tegra/bpmp.h +++ b/include/soc/tegra/bpmp.h @@ -110,6 +110,7 @@ struct tegra_bpmp_message { struct { void *data; size_t size; + int ret; } rx; }; -- cgit v1.2.3-59-g8ed1b From 4d1dc40185735e285576d7ed865b065c5cabe40c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 12:05:26 +0200 Subject: dt-bindings: clock: tegra: Add sor1_out clock The sor1_src clock implemented on Tegra210 is modelled the wrong way around, which causes some issues with HDMI and DP support. This clock implementation is provided by BPMP on Tegra186, which models this in a more correct way. Since this introduces incompatibilities between the two SoC generations which we want to avoid, the Tegra210 will be fixed in subsequent patches. This change adds sor1_out as an alias for sor1_src. Signed-off-by: Thierry Reding --- include/dt-bindings/clock/tegra210-car.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/dt-bindings/clock/tegra210-car.h b/include/dt-bindings/clock/tegra210-car.h index 46689cd3750b..43c4a8407333 100644 --- a/include/dt-bindings/clock/tegra210-car.h +++ b/include/dt-bindings/clock/tegra210-car.h @@ -309,6 +309,7 @@ #define TEGRA210_CLK_BLINK 280 /* 281 */ #define TEGRA210_CLK_SOR1_SRC 282 +#define TEGRA210_CLK_SOR1_OUT 282 /* 283 */ #define TEGRA210_CLK_XUSB_HOST_SRC 284 #define TEGRA210_CLK_XUSB_FALCON_SRC 285 -- cgit v1.2.3-59-g8ed1b From 0d96a4f6a03797b73bee465cada39133b7972e8d Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 6 Oct 2017 13:45:53 +0200 Subject: memory: omap-gpmc: Drop gpmc_status This field is no longer used, drop it. Signed-off-by: Ladislav Michl Signed-off-by: Roger Quadros --- drivers/memory/omap-gpmc.c | 1 - include/linux/platform_data/mtd-nand-omap2.h | 2 -- 2 files changed, 3 deletions(-) (limited to 'include') diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 7059bbda2fac..f2aef0b87bc6 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -1079,7 +1079,6 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) { int i; - reg->gpmc_status = NULL; /* deprecated */ reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET + GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs; reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET + diff --git a/include/linux/platform_data/mtd-nand-omap2.h b/include/linux/platform_data/mtd-nand-omap2.h index 17d57a18bac5..25e267f1970c 100644 --- a/include/linux/platform_data/mtd-nand-omap2.h +++ b/include/linux/platform_data/mtd-nand-omap2.h @@ -63,8 +63,6 @@ struct gpmc_nand_regs { void __iomem *gpmc_bch_result4[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER]; void __iomem *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER]; - /* Deprecated. Do not use */ - void __iomem *gpmc_status; }; struct omap_nand_platform_data { -- cgit v1.2.3-59-g8ed1b From 4328a2186e5120cfd34c4f04c6e4b7e74fb8b7b4 Mon Sep 17 00:00:00 2001 From: Jonathan Liu Date: Tue, 17 Oct 2017 20:18:03 +0800 Subject: clk: sunxi-ng: sun4i: Export video PLLs The video PLLs are used directly by the HDMI controller. Export them so that we can use them in our DT node. Signed-off-by: Jonathan Liu Signed-off-by: Chen-Yu Tsai Signed-off-by: Maxime Ripard --- drivers/clk/sunxi-ng/ccu-sun4i-a10.h | 4 ++-- include/dt-bindings/clock/sun4i-a10-ccu.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/clk/sunxi-ng/ccu-sun4i-a10.h b/drivers/clk/sunxi-ng/ccu-sun4i-a10.h index c5947c7c050e..23c908ad509f 100644 --- a/drivers/clk/sunxi-ng/ccu-sun4i-a10.h +++ b/drivers/clk/sunxi-ng/ccu-sun4i-a10.h @@ -29,7 +29,7 @@ #define CLK_PLL_AUDIO_4X 6 #define CLK_PLL_AUDIO_8X 7 #define CLK_PLL_VIDEO0 8 -#define CLK_PLL_VIDEO0_2X 9 +/* The PLL_VIDEO0_2X clock is exported */ #define CLK_PLL_VE 10 #define CLK_PLL_DDR_BASE 11 #define CLK_PLL_DDR 12 @@ -38,7 +38,7 @@ #define CLK_PLL_PERIPH 15 #define CLK_PLL_PERIPH_SATA 16 #define CLK_PLL_VIDEO1 17 -#define CLK_PLL_VIDEO1_2X 18 +/* The PLL_VIDEO1_2X clock is exported */ #define CLK_PLL_GPU 19 /* The CPU clock is exported */ diff --git a/include/dt-bindings/clock/sun4i-a10-ccu.h b/include/dt-bindings/clock/sun4i-a10-ccu.h index c5a53f38d654..e4fa61be5c75 100644 --- a/include/dt-bindings/clock/sun4i-a10-ccu.h +++ b/include/dt-bindings/clock/sun4i-a10-ccu.h @@ -43,6 +43,8 @@ #define _DT_BINDINGS_CLK_SUN4I_A10_H_ #define CLK_HOSC 1 +#define CLK_PLL_VIDEO0_2X 9 +#define CLK_PLL_VIDEO1_2X 18 #define CLK_CPU 20 /* AHB Gates */ -- cgit v1.2.3-59-g8ed1b From 5a6cd6de76ae78b651e7c36eba8b1da465d65f06 Mon Sep 17 00:00:00 2001 From: Alan Brady Date: Thu, 5 Oct 2017 14:53:40 -0700 Subject: ethtool: add ethtool_intersect_link_masks This function provides a way to intersect two link masks together to find the common ground between them. For example in i40e, the driver first generates link masks for what is supported by the PHY type. The driver then gets the link masks for what the NVM supports. The resulting intersection between them yields what can truly be supported. Signed-off-by: Alan Brady Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- include/linux/ethtool.h | 10 ++++++++++ net/core/ethtool.c | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'include') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 4587a4c36923..c77fa3529e15 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -163,6 +163,16 @@ extern int __ethtool_get_link_ksettings(struct net_device *dev, struct ethtool_link_ksettings *link_ksettings); +/** + * ethtool_intersect_link_masks - Given two link masks, AND them together + * @dst: first mask and where result is stored + * @src: second mask to intersect with + * + * Given two link mode masks, AND them together and save the result in dst. + */ +void ethtool_intersect_link_masks(struct ethtool_link_ksettings *dst, + struct ethtool_link_ksettings *src); + void ethtool_convert_legacy_u32_to_link_mode(unsigned long *dst, u32 legacy_u32); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 3228411ada0f..0c406306792a 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -403,6 +403,22 @@ static int __ethtool_set_flags(struct net_device *dev, u32 data) return 0; } +/* Given two link masks, AND them together and save the result in dst. */ +void ethtool_intersect_link_masks(struct ethtool_link_ksettings *dst, + struct ethtool_link_ksettings *src) +{ + unsigned int size = BITS_TO_LONGS(__ETHTOOL_LINK_MODE_MASK_NBITS); + unsigned int idx = 0; + + for (; idx < size; idx++) { + dst->link_modes.supported[idx] &= + src->link_modes.supported[idx]; + dst->link_modes.advertising[idx] &= + src->link_modes.advertising[idx]; + } +} +EXPORT_SYMBOL(ethtool_intersect_link_masks); + void ethtool_convert_legacy_u32_to_link_mode(unsigned long *dst, u32 legacy_u32) { -- cgit v1.2.3-59-g8ed1b From b5ca66f9c0cf8c1cfeca12f001b86486c670279d Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 13 Oct 2017 10:21:50 -0400 Subject: tracing, thermal: Hide devfreq trace events when not in use As trace events when defined create data structures and functions to process them, defining trace events when not using them is a waste of memory. The trace events thermal_power_devfreq_get_power and thermal_power_devfreq_limit are only used when CONFIG_DEVFREQ_THERMAL is set. Make those events only defined when that is set as well. Link: http://lkml.kernel.org/r/20171013102150.0050cb74@gandalf.local.home Acked-by: Javi Merino Signed-off-by: Steven Rostedt (VMware) --- include/trace/events/thermal.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/trace/events/thermal.h b/include/trace/events/thermal.h index 6cde5b3514c2..1fdacdb94e77 100644 --- a/include/trace/events/thermal.h +++ b/include/trace/events/thermal.h @@ -148,6 +148,7 @@ TRACE_EVENT(thermal_power_cpu_limit, __entry->power) ); +#ifdef CONFIG_DEVFREQ_THERMAL TRACE_EVENT(thermal_power_devfreq_get_power, TP_PROTO(struct thermal_cooling_device *cdev, struct devfreq_dev_status *status, unsigned long freq, @@ -203,6 +204,7 @@ TRACE_EVENT(thermal_power_devfreq_limit, __get_str(type), __entry->freq, __entry->cdev_state, __entry->power) ); +#endif /* CONFIG_DEVFREQ_THERMAL */ #endif /* _TRACE_THERMAL_H */ /* This part must be outside protection */ -- cgit v1.2.3-59-g8ed1b From a96a5037ed0f52e2d86739f4a1ef985bd036e575 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (VMware)" Date: Fri, 13 Oct 2017 10:23:09 -0400 Subject: tracing, thermal: Hide cpu cooling trace events when not in use As trace events when defined create data structures and functions to process them, defining trace events when not using them is a waste of memory. The trace events thermal_power_cpu_get_power and thermal_power_cpu_limit are only used when CONFIG_CPU_THERMAL is set. Make those events only defined when that is set as well. Link: http://lkml.kernel.org/r/20171013102309.2c4ef81a@gandalf.local.home Cc: Eduardo Valentin Acked-by: Javi Merino Signed-off-by: Steven Rostedt (VMware) --- include/trace/events/thermal.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/trace/events/thermal.h b/include/trace/events/thermal.h index 1fdacdb94e77..8af8f130950e 100644 --- a/include/trace/events/thermal.h +++ b/include/trace/events/thermal.h @@ -90,6 +90,7 @@ TRACE_EVENT(thermal_zone_trip, show_tzt_type(__entry->trip_type)) ); +#ifdef CONFIG_CPU_THERMAL TRACE_EVENT(thermal_power_cpu_get_power, TP_PROTO(const struct cpumask *cpus, unsigned long freq, u32 *load, size_t load_len, u32 dynamic_power, u32 static_power), @@ -147,6 +148,7 @@ TRACE_EVENT(thermal_power_cpu_limit, __get_bitmask(cpumask), __entry->freq, __entry->cdev_state, __entry->power) ); +#endif /* CONFIG_CPU_THERMAL */ #ifdef CONFIG_DEVFREQ_THERMAL TRACE_EVENT(thermal_power_devfreq_get_power, -- cgit v1.2.3-59-g8ed1b From 0290c4ca2536a35e55c53cfb9058465b1f987b17 Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Tue, 17 Oct 2017 16:36:23 -0700 Subject: of: overlay: rename identifiers to more reflect what they do This patch is aimed primarily at drivers/of/overlay.c, but those changes also have a small impact in a few other files. overlay.c is difficult to read and maintain. Improve readability: - Rename functions, types and variables to better reflect what they do and to be consistent with names in other places, such as the device tree overlay FDT (flattened device tree), and make the algorithms more clear - Use the same names consistently throughout the file - Update comments for name changes - Fix incorrect comments This patch is intended to not introduce any functional change. Signed-off-by: Frank Rowand Signed-off-by: Rob Herring --- Documentation/devicetree/overlay-notes.txt | 12 +- drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c | 5 +- drivers/of/dynamic.c | 2 +- drivers/of/overlay.c | 530 ++++++++++++++------------- drivers/of/unittest.c | 20 +- include/linux/of.h | 12 +- 6 files changed, 310 insertions(+), 271 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/overlay-notes.txt b/Documentation/devicetree/overlay-notes.txt index eb7f2685fda1..c4aa0adf13ec 100644 --- a/Documentation/devicetree/overlay-notes.txt +++ b/Documentation/devicetree/overlay-notes.txt @@ -87,15 +87,15 @@ Overlay in-kernel API The API is quite easy to use. -1. Call of_overlay_create() to create and apply an overlay. The return value -is a cookie identifying this overlay. +1. Call of_overlay_apply() to create and apply an overlay changeset. The return +value is an error or a cookie identifying this overlay. -2. Call of_overlay_destroy() to remove and cleanup the overlay previously -created via the call to of_overlay_create(). Removal of an overlay that -is stacked by another will not be permitted. +2. Call of_overlay_remove() to remove and cleanup the overlay changeset +previously created via the call to of_overlay_apply(). Removal of an overlay +changeset that is stacked by another will not be permitted. Finally, if you need to remove all overlays in one-go, just call -of_overlay_destroy_all() which will remove every single one in the correct +of_overlay_remove_all() which will remove every single one in the correct order. Overlay DTS Format diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c index 623a9140493c..5f5b7ba35f1d 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c @@ -247,9 +247,10 @@ static void __init tilcdc_convert_slave_node(void) tilcdc_node_disable(slave); - ret = of_overlay_create(overlay); + ret = of_overlay_apply(overlay); if (ret) - pr_err("%s: Creating overlay failed: %d\n", __func__, ret); + pr_err("%s: Applying overlay changeset failed: %d\n", + __func__, ret); else pr_info("%s: ti,tilcdc,slave node successfully converted\n", __func__); diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 39e8cf731764..4bc96af4d8e2 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -758,7 +758,7 @@ int of_changeset_revert(struct of_changeset *ocs) EXPORT_SYMBOL_GPL(of_changeset_revert); /** - * of_changeset_action - Perform a changeset action + * of_changeset_action - Add an action to the tail of the changeset list * * @ocs: changeset pointer * @action: action to perform diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index d3f4a5974a11..69610637af88 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -25,67 +25,63 @@ #include "of_private.h" /** - * struct of_overlay_info - Holds a single overlay info + * struct fragment - info about fragment nodes in overlay expanded device tree * @target: target of the overlay operation - * @overlay: pointer to the overlay contents node - * - * Holds a single overlay state, including all the overlay logs & - * records. + * @overlay: pointer to the __overlay__ node */ -struct of_overlay_info { +struct fragment { struct device_node *target; struct device_node *overlay; bool is_symbols_node; }; /** - * struct of_overlay - Holds a complete overlay transaction - * @node: List on which we are located - * @count: Count of ovinfo structures - * @ovinfo_tab: Overlay info table (count sized) - * @cset: Changeset to be used - * - * Holds a complete overlay transaction + * struct overlay_changeset + * @ovcs_list: list on which we are located + * @count: count of @fragments structures + * @fragments: info about fragment nodes in overlay expanded device tree + * @cset: changeset to apply fragments to live device tree */ -struct of_overlay { +struct overlay_changeset { int id; - struct list_head node; + struct list_head ovcs_list; int count; - struct of_overlay_info *ovinfo_tab; + struct fragment *fragments; struct of_changeset cset; }; -static int of_overlay_apply_one(struct of_overlay *ov, - struct device_node *target, const struct device_node *overlay, +static int build_changeset_next_level(struct overlay_changeset *ovcs, + struct device_node *target_node, + const struct device_node *overlay_node, bool is_symbols_node); -static BLOCKING_NOTIFIER_HEAD(of_overlay_chain); +static BLOCKING_NOTIFIER_HEAD(overlay_notify_chain); int of_overlay_notifier_register(struct notifier_block *nb) { - return blocking_notifier_chain_register(&of_overlay_chain, nb); + return blocking_notifier_chain_register(&overlay_notify_chain, nb); } EXPORT_SYMBOL_GPL(of_overlay_notifier_register); int of_overlay_notifier_unregister(struct notifier_block *nb) { - return blocking_notifier_chain_unregister(&of_overlay_chain, nb); + return blocking_notifier_chain_unregister(&overlay_notify_chain, nb); } EXPORT_SYMBOL_GPL(of_overlay_notifier_unregister); -static int of_overlay_notify(struct of_overlay *ov, - enum of_overlay_notify_action action) +static int overlay_notify(struct overlay_changeset *ovcs, + enum of_overlay_notify_action action) { struct of_overlay_notify_data nd; int i, ret; - for (i = 0; i < ov->count; i++) { - struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i]; + for (i = 0; i < ovcs->count; i++) { + struct fragment *fragment = &ovcs->fragments[i]; - nd.target = ovinfo->target; - nd.overlay = ovinfo->overlay; + nd.target = fragment->target; + nd.overlay = fragment->overlay; - ret = blocking_notifier_call_chain(&of_overlay_chain, + ret = blocking_notifier_call_chain(&overlay_notify_chain, action, &nd); if (ret) return notifier_to_errno(ret); @@ -94,10 +90,10 @@ static int of_overlay_notify(struct of_overlay *ov, return 0; } -static struct property *dup_and_fixup_symbol_prop(struct of_overlay *ov, - const struct property *prop) +static struct property *dup_and_fixup_symbol_prop( + struct overlay_changeset *ovcs, const struct property *prop) { - struct of_overlay_info *ovinfo; + struct fragment *fragment; struct property *new; const char *overlay_name; char *label_path; @@ -116,18 +112,18 @@ static struct property *dup_and_fixup_symbol_prop(struct of_overlay *ov, if (!new) return NULL; - for (k = 0; k < ov->count; k++) { - ovinfo = &ov->ovinfo_tab[k]; - overlay_name = ovinfo->overlay->full_name; + for (k = 0; k < ovcs->count; k++) { + fragment = &ovcs->fragments[k]; + overlay_name = fragment->overlay->full_name; overlay_name_len = strlen(overlay_name); if (!strncasecmp(symbol_path, overlay_name, overlay_name_len)) break; } - if (k >= ov->count) + if (k >= ovcs->count) goto err_free; - target_path = ovinfo->target->full_name; + target_path = fragment->target->full_name; target_path_len = strlen(target_path); label_path = symbol_path + overlay_name_len; @@ -156,82 +152,119 @@ static struct property *dup_and_fixup_symbol_prop(struct of_overlay *ov, } -/* +/** + * add_changeset_property() - add @overlay_prop to overlay changeset + * @ovcs: overlay changeset + * @target_node: where to place @overlay_prop in live tree + * @overlay_prop: property to add or update, from overlay tree + * is_symbols_node: 1 if @target_node is "/__symbols__" + * + * If @overlay_prop does not already exist in @target_node, add changeset entry + * to add @overlay_prop in @target_node, else add changeset entry to update + * value of @overlay_prop. + * * Some special properties are not updated (no error returned). + * * Update of property in symbols node is not allowed. + * + * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if + * invalid @overlay. */ -static int of_overlay_apply_single_property(struct of_overlay *ov, - struct device_node *target, struct property *prop, +static int add_changeset_property(struct overlay_changeset *ovcs, + struct device_node *target_node, + struct property *overlay_prop, bool is_symbols_node) { - struct property *propn = NULL, *tprop; + struct property *new_prop = NULL, *prop; int ret = 0; - tprop = of_find_property(target, prop->name, NULL); + prop = of_find_property(target_node, overlay_prop->name, NULL); - if (!of_prop_cmp(prop->name, "name") || - !of_prop_cmp(prop->name, "phandle") || - !of_prop_cmp(prop->name, "linux,phandle")) + if (!of_prop_cmp(overlay_prop->name, "name") || + !of_prop_cmp(overlay_prop->name, "phandle") || + !of_prop_cmp(overlay_prop->name, "linux,phandle")) return 0; if (is_symbols_node) { - if (tprop) + if (prop) return -EINVAL; - propn = dup_and_fixup_symbol_prop(ov, prop); + new_prop = dup_and_fixup_symbol_prop(ovcs, overlay_prop); } else { - propn = __of_prop_dup(prop, GFP_KERNEL); + new_prop = __of_prop_dup(overlay_prop, GFP_KERNEL); } - if (!propn) + if (!new_prop) return -ENOMEM; - if (!tprop) - ret = of_changeset_add_property(&ov->cset, target, propn); + if (!prop) + ret = of_changeset_add_property(&ovcs->cset, target_node, + new_prop); else - ret = of_changeset_update_property(&ov->cset, target, propn); + ret = of_changeset_update_property(&ovcs->cset, target_node, + new_prop); if (ret) { - kfree(propn->name); - kfree(propn->value); - kfree(propn); + kfree(new_prop->name); + kfree(new_prop->value); + kfree(new_prop); } return ret; } -static int of_overlay_apply_single_device_node(struct of_overlay *ov, - struct device_node *target, struct device_node *child) +/** + * add_changeset_node() - add @node (and children) to overlay changeset + * @ovcs: overlay changeset + * @target_node: where to place @node in live tree + * @node: node from within overlay device tree fragment + * + * If @node does not already exist in @target_node, add changeset entry + * to add @node in @target_node. + * + * If @node already exists in @target_node, and the existing node has + * a phandle, the overlay node is not allowed to have a phandle. + * + * If @node has child nodes, add the children recursively via + * build_changeset_next_level(). + * + * NOTE: Multiple mods of created nodes not supported. + * + * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if + * invalid @overlay. + */ +static int add_changeset_node(struct overlay_changeset *ovcs, + struct device_node *target_node, struct device_node *node) { - const char *cname; + const char *node_kbasename; struct device_node *tchild; int ret = 0; - cname = kbasename(child->full_name); - if (!cname) + node_kbasename = kbasename(node->full_name); + if (!node_kbasename) return -ENOMEM; - for_each_child_of_node(target, tchild) - if (!of_node_cmp(cname, kbasename(tchild->full_name))) + for_each_child_of_node(target_node, tchild) + if (!of_node_cmp(node_kbasename, kbasename(tchild->full_name))) break; if (tchild) { - if (child->phandle) + if (node->phandle) return -EINVAL; - /* apply overlay recursively */ - ret = of_overlay_apply_one(ov, tchild, child, 0); + ret = build_changeset_next_level(ovcs, tchild, node, 0); of_node_put(tchild); } else { - tchild = __of_node_dup(child, "%pOF/%s", target, cname); + tchild = __of_node_dup(node, "%pOF/%s", + target_node, node_kbasename); if (!tchild) return -ENOMEM; - tchild->parent = target; + tchild->parent = target_node; - ret = of_changeset_attach_node(&ov->cset, tchild); + ret = of_changeset_attach_node(&ovcs->cset, tchild); if (ret) return ret; - ret = of_overlay_apply_one(ov, tchild, child, 0); + ret = build_changeset_next_level(ovcs, tchild, node, 0); if (ret) return ret; } @@ -239,29 +272,37 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov, return ret; } -/* - * Apply a single overlay node recursively. +/** + * build_changeset_next_level() - add level of overlay changeset + * @ovcs: overlay changeset + * @target_node: where to place @overlay_node in live tree + * @overlay_node: node from within an overlay device tree fragment + * @is_symbols_node: @overlay_node is node "/__symbols__" * - * Note that the in case of an error the target node is left - * in a inconsistent state. Error recovery should be performed - * by using the changeset. + * Add the properties (if any) and nodes (if any) from @overlay_node to the + * @ovcs->cset changeset. If an added node has child nodes, they will + * be added recursively. * * Do not allow symbols node to have any children. + * + * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if + * invalid @overlay_node. */ -static int of_overlay_apply_one(struct of_overlay *ov, - struct device_node *target, const struct device_node *overlay, +static int build_changeset_next_level(struct overlay_changeset *ovcs, + struct device_node *target_node, + const struct device_node *overlay_node, bool is_symbols_node) { struct device_node *child; struct property *prop; int ret; - for_each_property_of_node(overlay, prop) { - ret = of_overlay_apply_single_property(ov, target, prop, - is_symbols_node); + for_each_property_of_node(overlay_node, prop) { + ret = add_changeset_property(ovcs, target_node, prop, + is_symbols_node); if (ret) { pr_err("Failed to apply prop @%pOF/%s\n", - target, prop->name); + target_node, prop->name); return ret; } } @@ -269,11 +310,11 @@ static int of_overlay_apply_one(struct of_overlay *ov, if (is_symbols_node) return 0; - for_each_child_of_node(overlay, child) { - ret = of_overlay_apply_single_device_node(ov, target, child); + for_each_child_of_node(overlay_node, child) { + ret = add_changeset_node(ovcs, target_node, child); if (ret) { - pr_err("Failed to apply single node @%pOF/%s\n", - target, child->name); + pr_err("Failed to apply node @%pOF/%s\n", + target_node, child->name); of_node_put(child); return ret; } @@ -283,26 +324,30 @@ static int of_overlay_apply_one(struct of_overlay *ov, } /** - * of_overlay_apply() - Apply @count overlays pointed at by @ovinfo_tab - * @ov: Overlay to apply + * build_changeset() - populate overlay changeset in @ovcs from @ovcs->fragments + * @ovcs: Overlay changeset * - * Applies the overlays given, while handling all error conditions - * appropriately. Either the operation succeeds, or if it fails the - * live tree is reverted to the state before the attempt. - * Returns 0, or an error if the overlay attempt failed. + * Create changeset @ovcs->cset to contain the nodes and properties of the + * overlay device tree fragments in @ovcs->fragments[]. If an error occurs, + * any portions of the changeset that were successfully created will remain + * in @ovcs->cset. + * + * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if + * invalid overlay in @ovcs->fragments[]. */ -static int of_overlay_apply(struct of_overlay *ov) +static int build_changeset(struct overlay_changeset *ovcs) { - int i, err; + int i, ret; - for (i = 0; i < ov->count; i++) { - struct of_overlay_info *ovinfo = &ov->ovinfo_tab[i]; + for (i = 0; i < ovcs->count; i++) { + struct fragment *fragment = &ovcs->fragments[i]; - err = of_overlay_apply_one(ov, ovinfo->target, ovinfo->overlay, - ovinfo->is_symbols_node); - if (err) { - pr_err("apply failed '%pOF'\n", ovinfo->target); - return err; + ret = build_changeset_next_level(ovcs, fragment->target, + fragment->overlay, + fragment->is_symbols_node); + if (ret) { + pr_err("apply failed '%pOF'\n", fragment->target); + return ret; } } @@ -350,45 +395,46 @@ static struct device_node *find_target_node(struct device_node *info_node) * * Returns 0 on success, or a negative error value. */ -static int of_fill_overlay_info(struct of_overlay *ov, - struct device_node *info_node, struct of_overlay_info *ovinfo) +static int of_fill_overlay_info(struct overlay_changeset *ovcset, + struct device_node *info_node, struct fragment *fragment) { - ovinfo->overlay = of_get_child_by_name(info_node, "__overlay__"); - if (!ovinfo->overlay) + fragment->overlay = of_get_child_by_name(info_node, "__overlay__"); + if (!fragment->overlay) goto err_fail; - ovinfo->target = find_target_node(info_node); - if (!ovinfo->target) + fragment->target = find_target_node(info_node); + if (!fragment->target) goto err_fail; return 0; err_fail: - of_node_put(ovinfo->target); - of_node_put(ovinfo->overlay); + of_node_put(fragment->target); + of_node_put(fragment->overlay); - memset(ovinfo, 0, sizeof(*ovinfo)); + memset(fragment, 0, sizeof(*fragment)); return -EINVAL; } /** - * of_build_overlay_info() - Build an overlay info array - * @ov Overlay to build - * @tree: Device node containing all the overlays + * init_overlay_changeset() - initialize overlay changeset from overlay tree + * @ovcs Overlay changeset to build + * @tree: Contains all the overlay fragments and overlay fixup nodes * - * Helper function that given a tree containing overlay information, - * allocates and builds an overlay info array containing it, ready - * for use using of_overlay_apply. + * Initialize @ovcs. Populate @ovcs->fragments with node information from + * the top level of @tree. The relevant top level nodes are the fragment + * nodes and the __symbols__ node. Any other top level node will be ignored. * - * Returns 0 on success with the @cntp @ovinfop pointers valid, - * while on error a negative error value is returned. + * Returns 0 on success, -ENOMEM if memory allocation failure, -EINVAL if error + * detected in @tree, or -ENODEV if no valid nodes found. */ -static int of_build_overlay_info(struct of_overlay *ov, +static int init_overlay_changeset(struct overlay_changeset *ovcs, struct device_node *tree) { struct device_node *node; - struct of_overlay_info *ovinfo; - int cnt, err; + struct fragment *fragment; + struct fragment *fragments; + int cnt, ret; cnt = 0; for_each_child_of_node(tree, node) @@ -397,24 +443,25 @@ static int of_build_overlay_info(struct of_overlay *ov, if (of_get_child_by_name(tree, "__symbols__")) cnt++; - ovinfo = kcalloc(cnt, sizeof(*ovinfo), GFP_KERNEL); - if (!ovinfo) + fragments = kcalloc(cnt, sizeof(*fragments), GFP_KERNEL); + if (!fragments) return -ENOMEM; cnt = 0; for_each_child_of_node(tree, node) { - err = of_fill_overlay_info(ov, node, &ovinfo[cnt]); - if (!err) + ret = of_fill_overlay_info(ovcs, node, &fragments[cnt]); + if (!ret) cnt++; } node = of_get_child_by_name(tree, "__symbols__"); if (node) { - ovinfo[cnt].overlay = node; - ovinfo[cnt].target = of_find_node_by_path("/__symbols__"); - ovinfo[cnt].is_symbols_node = 1; + fragment = &fragments[cnt]; + fragment->overlay = node; + fragment->target = of_find_node_by_path("/__symbols__"); + fragment->is_symbols_node = 1; - if (!ovinfo[cnt].target) { + if (!fragment->target) { pr_err("no symbols in root of device tree.\n"); return -EINVAL; } @@ -423,137 +470,127 @@ static int of_build_overlay_info(struct of_overlay *ov, } if (!cnt) { - kfree(ovinfo); + kfree(fragments); return -ENODEV; } - ov->count = cnt; - ov->ovinfo_tab = ovinfo; + ovcs->count = cnt; + ovcs->fragments = fragments; return 0; } /** - * of_free_overlay_info() - Free an overlay info array - * @ov Overlay to free the overlay info from - * @ovinfo_tab: Array of overlay_info's to free + * free_overlay_fragments() - Free a fragments array + * @ovcs Overlay to free the overlay info from * - * Releases the memory of a previously allocated ovinfo array - * by of_build_overlay_info. - * Returns 0, or an error if the arguments are bogus. + * Frees the memory of an ovcs->fragments[] array. */ -static int of_free_overlay_info(struct of_overlay *ov) +static void free_overlay_fragments(struct overlay_changeset *ovcs) { - struct of_overlay_info *ovinfo; int i; /* do it in reverse */ - for (i = ov->count - 1; i >= 0; i--) { - ovinfo = &ov->ovinfo_tab[i]; - - of_node_put(ovinfo->target); - of_node_put(ovinfo->overlay); + for (i = ovcs->count - 1; i >= 0; i--) { + of_node_put(ovcs->fragments[i].target); + of_node_put(ovcs->fragments[i].overlay); } - kfree(ov->ovinfo_tab); - return 0; + kfree(ovcs->fragments); } -static LIST_HEAD(ov_list); -static DEFINE_IDR(ov_idr); +static LIST_HEAD(ovcs_list); +static DEFINE_IDR(ovcs_idr); /** - * of_overlay_create() - Create and apply an overlay - * @tree: Device node containing all the overlays + * of_overlay_apply() - Create and apply an overlay changeset + * @tree: Expanded overlay device tree * - * Creates and applies an overlay while also keeping track - * of the overlay in a list. This list can be used to prevent - * illegal overlay removals. + * Creates and applies an overlay changeset. If successful, the overlay + * changeset is added to the overlay changeset list. * - * Returns the id of the created overlay, or a negative error number + * Returns the id of the created overlay changeset, or a negative error number */ -int of_overlay_create(struct device_node *tree) +int of_overlay_apply(struct device_node *tree) { - struct of_overlay *ov; - int err, id; + struct overlay_changeset *ovcs; + int id, ret; - ov = kzalloc(sizeof(*ov), GFP_KERNEL); - if (!ov) + ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL); + if (!ovcs) return -ENOMEM; - ov->id = -1; + ovcs->id = -1; - INIT_LIST_HEAD(&ov->node); + INIT_LIST_HEAD(&ovcs->ovcs_list); - of_changeset_init(&ov->cset); + of_changeset_init(&ovcs->cset); mutex_lock(&of_mutex); - id = idr_alloc(&ov_idr, ov, 0, 0, GFP_KERNEL); + id = idr_alloc(&ovcs_idr, ovcs, 0, 0, GFP_KERNEL); if (id < 0) { - err = id; + ret = id; goto err_destroy_trans; } - ov->id = id; + ovcs->id = id; - err = of_build_overlay_info(ov, tree); - if (err) { - pr_err("of_build_overlay_info() failed for tree@%pOF\n", + ret = init_overlay_changeset(ovcs, tree); + if (ret) { + pr_err("init_overlay_changeset() failed for tree@%pOF\n", tree); goto err_free_idr; } - err = of_overlay_notify(ov, OF_OVERLAY_PRE_APPLY); - if (err < 0) { - pr_err("%s: Pre-apply notifier failed (err=%d)\n", - __func__, err); - goto err_free_idr; + ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY); + if (ret < 0) { + pr_err("%s: Pre-apply notifier failed (ret=%d)\n", + __func__, ret); + goto err_free_overlay_fragments; } - err = of_overlay_apply(ov); - if (err) - goto err_abort_trans; - - err = __of_changeset_apply(&ov->cset); - if (err) - goto err_revert_overlay; + ret = build_changeset(ovcs); + if (ret) + goto err_free_overlay_fragments; + ret = __of_changeset_apply(&ovcs->cset); + if (ret) + goto err_free_overlay_fragments; - list_add_tail(&ov->node, &ov_list); + list_add_tail(&ovcs->ovcs_list, &ovcs_list); - of_overlay_notify(ov, OF_OVERLAY_POST_APPLY); + overlay_notify(ovcs, OF_OVERLAY_POST_APPLY); mutex_unlock(&of_mutex); return id; -err_revert_overlay: -err_abort_trans: - of_free_overlay_info(ov); +err_free_overlay_fragments: + free_overlay_fragments(ovcs); err_free_idr: - idr_remove(&ov_idr, ov->id); + idr_remove(&ovcs_idr, ovcs->id); err_destroy_trans: - of_changeset_destroy(&ov->cset); - kfree(ov); + of_changeset_destroy(&ovcs->cset); + kfree(ovcs); mutex_unlock(&of_mutex); - return err; + return ret; } -EXPORT_SYMBOL_GPL(of_overlay_create); +EXPORT_SYMBOL_GPL(of_overlay_apply); /* - * check whether the given node, lies under the given tree - * return 1 if under tree, else 0 + * Find @np in @tree. + * + * Returns 1 if @np is @tree or is contained in @tree, else 0 */ -static int overlay_subtree_check(struct device_node *tree, - struct device_node *dn) +static int find_node(struct device_node *tree, struct device_node *np) { struct device_node *child; - if (tree == dn) + if (tree == np) return 1; for_each_child_of_node(tree, child) { - if (overlay_subtree_check(child, dn)) { + if (find_node(child, np)) { of_node_put(child); return 1; } @@ -563,30 +600,32 @@ static int overlay_subtree_check(struct device_node *tree, } /* - * check whether this overlay is the topmost - * return 1 if topmost, else 0 + * Is @remove_ce_np a child of or the same as any + * node in an overlay changeset more topmost than @remove_ovcs? + * + * Returns 1 if found, else 0 */ -static int overlay_is_topmost(struct of_overlay *ov, struct device_node *dn) +static int node_in_later_cs(struct overlay_changeset *remove_ovcs, + struct device_node *remove_ce_np) { - struct of_overlay *ovt; + struct overlay_changeset *ovcs; struct of_changeset_entry *ce; - list_for_each_entry_reverse(ovt, &ov_list, node) { - /* if we hit ourselves, we're done */ - if (ovt == ov) + list_for_each_entry_reverse(ovcs, &ovcs_list, ovcs_list) { + if (ovcs == remove_ovcs) break; - /* check against each subtree affected by this overlay */ - list_for_each_entry(ce, &ovt->cset.entries, node) { - if (overlay_subtree_check(ce->np, dn)) { + list_for_each_entry(ce, &ovcs->cset.entries, node) { + if (find_node(ce->np, remove_ce_np)) { pr_err("%s: #%d clashes #%d @%pOF\n", - __func__, ov->id, ovt->id, dn); - return 0; + __func__, remove_ovcs->id, ovcs->id, + remove_ce_np); + return 1; } } } - return 1; + return 0; } /* @@ -599,13 +638,13 @@ static int overlay_is_topmost(struct of_overlay *ov, struct device_node *dn) * the one closest to the tail. If another overlay has affected this * device node and is closest to the tail, then removal is not permited. */ -static int overlay_removal_is_ok(struct of_overlay *ov) +static int overlay_removal_is_ok(struct overlay_changeset *remove_ovcs) { - struct of_changeset_entry *ce; + struct of_changeset_entry *remove_ce; - list_for_each_entry(ce, &ov->cset.entries, node) { - if (!overlay_is_topmost(ov, ce->np)) { - pr_err("overlay #%d is not topmost\n", ov->id); + list_for_each_entry(remove_ce, &remove_ovcs->cset.entries, node) { + if (node_in_later_cs(remove_ovcs, remove_ce->np)) { + pr_err("overlay #%d is not topmost\n", remove_ovcs->id); return 0; } } @@ -614,74 +653,73 @@ static int overlay_removal_is_ok(struct of_overlay *ov) } /** - * of_overlay_destroy() - Removes an overlay - * @id: Overlay id number returned by a previous call to of_overlay_create + * of_overlay_remove() - Revert and free an overlay changeset + * @ovcs_id: Overlay changeset id number * - * Removes an overlay if it is permissible. + * Removes an overlay if it is permissible. ovcs_id was previously returned + * by of_overlay_apply(). * * Returns 0 on success, or a negative error number */ -int of_overlay_destroy(int id) +int of_overlay_remove(int ovcs_id) { - struct of_overlay *ov; - int err; + struct overlay_changeset *ovcs; + int ret = 0; mutex_lock(&of_mutex); - ov = idr_find(&ov_idr, id); - if (!ov) { - err = -ENODEV; - pr_err("destroy: Could not find overlay #%d\n", id); + ovcs = idr_find(&ovcs_idr, ovcs_id); + if (!ovcs) { + ret = -ENODEV; + pr_err("remove: Could not find overlay #%d\n", ovcs_id); goto out; } - if (!overlay_removal_is_ok(ov)) { - err = -EBUSY; + if (!overlay_removal_is_ok(ovcs)) { + ret = -EBUSY; goto out; } - of_overlay_notify(ov, OF_OVERLAY_PRE_REMOVE); - list_del(&ov->node); - __of_changeset_revert(&ov->cset); - of_overlay_notify(ov, OF_OVERLAY_POST_REMOVE); - of_free_overlay_info(ov); - idr_remove(&ov_idr, id); - of_changeset_destroy(&ov->cset); - kfree(ov); - - err = 0; + overlay_notify(ovcs, OF_OVERLAY_PRE_REMOVE); + list_del(&ovcs->ovcs_list); + __of_changeset_revert(&ovcs->cset); + overlay_notify(ovcs, OF_OVERLAY_POST_REMOVE); + free_overlay_fragments(ovcs); + idr_remove(&ovcs_idr, ovcs_id); + of_changeset_destroy(&ovcs->cset); + kfree(ovcs); out: mutex_unlock(&of_mutex); - return err; + return ret; } -EXPORT_SYMBOL_GPL(of_overlay_destroy); +EXPORT_SYMBOL_GPL(of_overlay_remove); /** - * of_overlay_destroy_all() - Removes all overlays from the system + * of_overlay_remove_all() - Reverts and frees all overlay changesets * * Removes all overlays from the system in the correct order. * * Returns 0 on success, or a negative error number */ -int of_overlay_destroy_all(void) +int of_overlay_remove_all(void) { - struct of_overlay *ov, *ovn; + struct overlay_changeset *ovcs, *ovcs_n; mutex_lock(&of_mutex); /* the tail of list is guaranteed to be safe to remove */ - list_for_each_entry_safe_reverse(ov, ovn, &ov_list, node) { - list_del(&ov->node); - __of_changeset_revert(&ov->cset); - of_free_overlay_info(ov); - idr_remove(&ov_idr, ov->id); - kfree(ov); + list_for_each_entry_safe_reverse(ovcs, ovcs_n, &ovcs_list, ovcs_list) { + list_del(&ovcs->ovcs_list); + __of_changeset_revert(&ovcs->cset); + free_overlay_fragments(ovcs); + idr_remove(&ovcs_idr, ovcs->id); + kfree(ovcs); } mutex_unlock(&of_mutex); return 0; } -EXPORT_SYMBOL_GPL(of_overlay_destroy_all); +EXPORT_SYMBOL_GPL(of_overlay_remove_all); diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index add02cfbfb88..bbdaf5606820 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1230,7 +1230,7 @@ static void of_unittest_destroy_tracked_overlays(void) if (!(overlay_id_bits[BIT_WORD(id)] & BIT_MASK(id))) continue; - ret = of_overlay_destroy(id + overlay_first_id); + ret = of_overlay_remove(id + overlay_first_id); if (ret == -ENODEV) { pr_warn("%s: no overlay to destroy for #%d\n", __func__, id + overlay_first_id); @@ -1262,7 +1262,7 @@ static int of_unittest_apply_overlay(int overlay_nr, int unittest_nr, goto out; } - ret = of_overlay_create(np); + ret = of_overlay_apply(np); if (ret < 0) { unittest(0, "could not create overlay from \"%s\"\n", overlay_path(overlay_nr)); @@ -1347,7 +1347,7 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr, return -EINVAL; } - ret = of_overlay_destroy(ov_id); + ret = of_overlay_remove(ov_id); if (ret != 0) { unittest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n", overlay_path(overlay_nr), @@ -1476,7 +1476,7 @@ static void of_unittest_overlay_6(void) return; } - ret = of_overlay_create(np); + ret = of_overlay_apply(np); if (ret < 0) { unittest(0, "could not create overlay from \"%s\"\n", overlay_path(overlay_nr + i)); @@ -1500,7 +1500,7 @@ static void of_unittest_overlay_6(void) } for (i = 1; i >= 0; i--) { - ret = of_overlay_destroy(ov_id[i]); + ret = of_overlay_remove(ov_id[i]); if (ret != 0) { unittest(0, "overlay @\"%s\" failed destroy @\"%s\"\n", overlay_path(overlay_nr + i), @@ -1546,7 +1546,7 @@ static void of_unittest_overlay_8(void) return; } - ret = of_overlay_create(np); + ret = of_overlay_apply(np); if (ret < 0) { unittest(0, "could not create overlay from \"%s\"\n", overlay_path(overlay_nr + i)); @@ -1557,7 +1557,7 @@ static void of_unittest_overlay_8(void) } /* now try to remove first overlay (it should fail) */ - ret = of_overlay_destroy(ov_id[0]); + ret = of_overlay_remove(ov_id[0]); if (ret == 0) { unittest(0, "overlay @\"%s\" was destroyed @\"%s\"\n", overlay_path(overlay_nr + 0), @@ -1568,7 +1568,7 @@ static void of_unittest_overlay_8(void) /* removing them in order should work */ for (i = 1; i >= 0; i--) { - ret = of_overlay_destroy(ov_id[i]); + ret = of_overlay_remove(ov_id[i]); if (ret != 0) { unittest(0, "overlay @\"%s\" not destroyed @\"%s\"\n", overlay_path(overlay_nr + i), @@ -2149,9 +2149,9 @@ static int __init overlay_data_add(int onum) goto out_free_np_overlay; } - ret = of_overlay_create(info->np_overlay); + ret = of_overlay_apply(info->np_overlay); if (ret < 0) { - pr_err("of_overlay_create() (ret=%d), %d\n", ret, onum); + pr_err("of_overlay_apply() (ret=%d), %d\n", ret, onum); goto out_free_np_overlay; } else { info->overlay_id = ret; diff --git a/include/linux/of.h b/include/linux/of.h index 7b0f17be7830..7569e9cc45de 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -1312,26 +1312,26 @@ struct of_overlay_notify_data { #ifdef CONFIG_OF_OVERLAY /* ID based overlays; the API for external users */ -int of_overlay_create(struct device_node *tree); -int of_overlay_destroy(int id); -int of_overlay_destroy_all(void); +int of_overlay_apply(struct device_node *tree); +int of_overlay_remove(int id); +int of_overlay_remove_all(void); int of_overlay_notifier_register(struct notifier_block *nb); int of_overlay_notifier_unregister(struct notifier_block *nb); #else -static inline int of_overlay_create(struct device_node *tree) +static inline int of_overlay_apply(struct device_node *tree) { return -ENOTSUPP; } -static inline int of_overlay_destroy(int id) +static inline int of_overlay_remove(int id) { return -ENOTSUPP; } -static inline int of_overlay_destroy_all(void) +static inline int of_overlay_remove_all(void) { return -ENOTSUPP; } -- cgit v1.2.3-59-g8ed1b From 24789c5ce5a373dd55640f9cd79117fcc3ccc46d Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Tue, 17 Oct 2017 16:36:26 -0700 Subject: of: overlay: detect cases where device tree may become corrupt When an attempt to apply an overlay changeset fails, an effort is made to revert any partial application of the changeset. When an attempt to remove an overlay changeset fails, an effort is made to re-apply any partial reversion of the changeset. The existing code does not check for failure to recover a failed overlay changeset application or overlay changeset revert. Add the missing checks and flag the devicetree as corrupt if the state of the devicetree can not be determined. Improve and expand the returned errors to more fully reflect the result of the effort to undo the partial effects of a failed attempt to apply or remove an overlay changeset. If the device tree might be corrupt, do not allow further attempts to apply or remove an overlay changeset. When creating an overlay changeset from an overlay device tree, add some additional warnings if the state of the overlay device tree is not as expected. Signed-off-by: Frank Rowand Signed-off-by: Rob Herring --- drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c | 5 +- drivers/of/dynamic.c | 135 +++++++++++--- drivers/of/of_private.h | 8 +- drivers/of/overlay.c | 253 ++++++++++++++++++++++----- drivers/of/unittest.c | 57 +++--- include/linux/of.h | 10 +- 6 files changed, 372 insertions(+), 96 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c index 5f5b7ba35f1d..7a7be0515bfd 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c @@ -204,7 +204,7 @@ static void __init tilcdc_convert_slave_node(void) /* For all memory needed for the overlay tree. This memory can be freed after the overlay has been applied. */ struct kfree_table kft; - int ret; + int ovcs_id, ret; if (kfree_table_init(&kft)) return; @@ -247,7 +247,8 @@ static void __init tilcdc_convert_slave_node(void) tilcdc_node_disable(slave); - ret = of_overlay_apply(overlay); + ovcs_id = 0; + ret = of_overlay_apply(overlay, &ovcs_id); if (ret) pr_err("%s: Applying overlay changeset failed: %d\n", __func__, ret); diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 4bc96af4d8e2..747d87619faf 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -491,11 +491,12 @@ static void __of_changeset_entry_invert(struct of_changeset_entry *ce, } } -static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert) +static int __of_changeset_entry_notify(struct of_changeset_entry *ce, + bool revert) { struct of_reconfig_data rd; struct of_changeset_entry ce_inverted; - int ret; + int ret = 0; if (revert) { __of_changeset_entry_invert(ce, &ce_inverted); @@ -517,11 +518,12 @@ static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool reve default: pr_err("invalid devicetree changeset action: %i\n", (int)ce->action); - return; + ret = -EINVAL; } if (ret) pr_err("changeset notifier error @%pOF\n", ce->np); + return ret; } static int __of_changeset_entry_apply(struct of_changeset_entry *ce) @@ -655,32 +657,82 @@ void of_changeset_destroy(struct of_changeset *ocs) } EXPORT_SYMBOL_GPL(of_changeset_destroy); -int __of_changeset_apply(struct of_changeset *ocs) +/* + * Apply the changeset entries in @ocs. + * If apply fails, an attempt is made to revert the entries that were + * successfully applied. + * + * If multiple revert errors occur then only the final revert error is reported. + * + * Returns 0 on success, a negative error value in case of an error. + * If a revert error occurs, it is returned in *ret_revert. + */ +int __of_changeset_apply_entries(struct of_changeset *ocs, int *ret_revert) { struct of_changeset_entry *ce; - int ret; + int ret, ret_tmp; - /* perform the rest of the work */ pr_debug("changeset: applying...\n"); list_for_each_entry(ce, &ocs->entries, node) { ret = __of_changeset_entry_apply(ce); if (ret) { pr_err("Error applying changeset (%d)\n", ret); - list_for_each_entry_continue_reverse(ce, &ocs->entries, node) - __of_changeset_entry_revert(ce); + list_for_each_entry_continue_reverse(ce, &ocs->entries, + node) { + ret_tmp = __of_changeset_entry_revert(ce); + if (ret_tmp) + *ret_revert = ret_tmp; + } return ret; } } - pr_debug("changeset: applied, emitting notifiers.\n"); + + return 0; +} + +/* + * Returns 0 on success, a negative error value in case of an error. + * + * If multiple changset entry notification errors occur then only the + * final notification error is reported. + */ +int __of_changeset_apply_notify(struct of_changeset *ocs) +{ + struct of_changeset_entry *ce; + int ret = 0, ret_tmp; + + pr_debug("changeset: emitting notifiers.\n"); /* drop the global lock while emitting notifiers */ mutex_unlock(&of_mutex); - list_for_each_entry(ce, &ocs->entries, node) - __of_changeset_entry_notify(ce, 0); + list_for_each_entry(ce, &ocs->entries, node) { + ret_tmp = __of_changeset_entry_notify(ce, 0); + if (ret_tmp) + ret = ret_tmp; + } mutex_lock(&of_mutex); pr_debug("changeset: notifiers sent.\n"); - return 0; + return ret; +} + +/* + * Returns 0 on success, a negative error value in case of an error. + * + * If a changeset entry apply fails, an attempt is made to revert any + * previous entries in the changeset. If any of the reverts fails, + * that failure is not reported. Thus the state of the device tree + * is unknown if an apply error occurs. + */ +static int __of_changeset_apply(struct of_changeset *ocs) +{ + int ret, ret_revert = 0; + + ret = __of_changeset_apply_entries(ocs, &ret_revert); + if (!ret) + ret = __of_changeset_apply_notify(ocs); + + return ret; } /** @@ -707,31 +759,74 @@ int of_changeset_apply(struct of_changeset *ocs) } EXPORT_SYMBOL_GPL(of_changeset_apply); -int __of_changeset_revert(struct of_changeset *ocs) +/* + * Revert the changeset entries in @ocs. + * If revert fails, an attempt is made to re-apply the entries that were + * successfully removed. + * + * If multiple re-apply errors occur then only the final apply error is + * reported. + * + * Returns 0 on success, a negative error value in case of an error. + * If an apply error occurs, it is returned in *ret_apply. + */ +int __of_changeset_revert_entries(struct of_changeset *ocs, int *ret_apply) { struct of_changeset_entry *ce; - int ret; + int ret, ret_tmp; pr_debug("changeset: reverting...\n"); list_for_each_entry_reverse(ce, &ocs->entries, node) { ret = __of_changeset_entry_revert(ce); if (ret) { pr_err("Error reverting changeset (%d)\n", ret); - list_for_each_entry_continue(ce, &ocs->entries, node) - __of_changeset_entry_apply(ce); + list_for_each_entry_continue(ce, &ocs->entries, node) { + ret_tmp = __of_changeset_entry_apply(ce); + if (ret_tmp) + *ret_apply = ret_tmp; + } return ret; } } - pr_debug("changeset: reverted, emitting notifiers.\n"); + + return 0; +} + +/* + * If multiple changset entry notification errors occur then only the + * final notification error is reported. + */ +int __of_changeset_revert_notify(struct of_changeset *ocs) +{ + struct of_changeset_entry *ce; + int ret = 0, ret_tmp; + + pr_debug("changeset: emitting notifiers.\n"); /* drop the global lock while emitting notifiers */ mutex_unlock(&of_mutex); - list_for_each_entry_reverse(ce, &ocs->entries, node) - __of_changeset_entry_notify(ce, 1); + list_for_each_entry_reverse(ce, &ocs->entries, node) { + ret_tmp = __of_changeset_entry_notify(ce, 1); + if (ret_tmp) + ret = ret_tmp; + } mutex_lock(&of_mutex); pr_debug("changeset: notifiers sent.\n"); - return 0; + return ret; +} + +static int __of_changeset_revert(struct of_changeset *ocs) +{ + int ret, ret_reply; + + ret_reply = 0; + ret = __of_changeset_revert_entries(ocs, &ret_reply); + + if (!ret) + ret = __of_changeset_revert_notify(ocs); + + return ret; } /** diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 43df14f0cbce..36357f571df2 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -39,8 +39,12 @@ extern struct kset *of_kset; extern int of_property_notify(int action, struct device_node *np, struct property *prop, struct property *old_prop); extern void of_node_release(struct kobject *kobj); -extern int __of_changeset_apply(struct of_changeset *ocs); -extern int __of_changeset_revert(struct of_changeset *ocs); +extern int __of_changeset_apply_entries(struct of_changeset *ocs, + int *ret_revert); +extern int __of_changeset_apply_notify(struct of_changeset *ocs); +extern int __of_changeset_revert_entries(struct of_changeset *ocs, + int *ret_apply); +extern int __of_changeset_revert_notify(struct of_changeset *ocs); #else /* CONFIG_OF_DYNAMIC */ static inline int of_property_notify(int action, struct device_node *np, struct property *prop, struct property *old_prop) diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 905916e17eec..78c50fd57750 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -50,6 +50,22 @@ struct overlay_changeset { struct of_changeset cset; }; +/* flags are sticky - once set, do not reset */ +static int devicetree_state_flags; +#define DTSF_APPLY_FAIL 0x01 +#define DTSF_REVERT_FAIL 0x02 + +/* + * If a changeset apply or revert encounters an error, an attempt will + * be made to undo partial changes, but may fail. If the undo fails + * we do not know the state of the devicetree. + */ +static int devicetree_corrupt(void) +{ + return devicetree_state_flags & + (DTSF_APPLY_FAIL | DTSF_REVERT_FAIL); +} + static int build_changeset_next_level(struct overlay_changeset *ovcs, struct device_node *target_node, const struct device_node *overlay_node, @@ -72,6 +88,13 @@ int of_overlay_notifier_unregister(struct notifier_block *nb) } EXPORT_SYMBOL_GPL(of_overlay_notifier_unregister); +static char *of_overlay_action_name[] = { + "pre-apply", + "post-apply", + "pre-remove", + "post-remove", +}; + static int overlay_notify(struct overlay_changeset *ovcs, enum of_overlay_notify_action action) { @@ -86,8 +109,14 @@ static int overlay_notify(struct overlay_changeset *ovcs, ret = blocking_notifier_call_chain(&overlay_notify_chain, action, &nd); - if (ret) - return notifier_to_errno(ret); + if (ret == NOTIFY_STOP) + return 0; + if (ret) { + ret = notifier_to_errno(ret); + pr_err("overlay changeset %s notifier error %d, target: %pOF\n", + of_overlay_action_name[action], ret, nd.target); + return ret; + } } return 0; @@ -240,6 +269,14 @@ static int add_changeset_property(struct overlay_changeset *ovcs, * build_changeset_next_level(). * * NOTE: Multiple mods of created nodes not supported. + * If more than one fragment contains a node that does not already exist + * in the live tree, then for each fragment of_changeset_attach_node() + * will add a changeset entry to add the node. When the changeset is + * applied, __of_attach_node() will attach the node twice (once for + * each fragment). At this point the device tree will be corrupted. + * + * TODO: add integrity check to ensure that multiple fragments do not + * create the same node. * * Returns 0 on success, -ENOMEM if memory allocation failure, or -EINVAL if * invalid @overlay. @@ -312,8 +349,8 @@ static int build_changeset_next_level(struct overlay_changeset *ovcs, ret = add_changeset_property(ovcs, target_node, prop, is_symbols_node); if (ret) { - pr_err("Failed to apply prop @%pOF/%s\n", - target_node, prop->name); + pr_debug("Failed to apply prop @%pOF/%s, err=%d\n", + target_node, prop->name, ret); return ret; } } @@ -324,8 +361,8 @@ static int build_changeset_next_level(struct overlay_changeset *ovcs, for_each_child_of_node(overlay_node, child) { ret = add_changeset_node(ovcs, target_node, child); if (ret) { - pr_err("Failed to apply node @%pOF/%s\n", - target_node, child->name); + pr_debug("Failed to apply node @%pOF/%s, err=%d\n", + target_node, child->name, ret); of_node_put(child); return ret; } @@ -357,7 +394,7 @@ static int build_changeset(struct overlay_changeset *ovcs) fragment->overlay, fragment->is_symbols_node); if (ret) { - pr_err("apply failed '%pOF'\n", fragment->target); + pr_debug("apply failed '%pOF'\n", fragment->target); return ret; } } @@ -412,6 +449,19 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, struct fragment *fragments; int cnt, ret; + /* + * Warn for some issues. Can not return -EINVAL for these until + * of_unittest_apply_overlay() is fixed to pass these checks. + */ + if (!of_node_check_flag(tree, OF_DYNAMIC)) + pr_debug("%s() tree is not dynamic\n", __func__); + + if (!of_node_check_flag(tree, OF_DETACHED)) + pr_debug("%s() tree is not detached\n", __func__); + + if (!of_node_is_root(tree)) + pr_debug("%s() tree is not root\n", __func__); + INIT_LIST_HEAD(&ovcs->ovcs_list); of_changeset_init(&ovcs->cset); @@ -485,12 +535,13 @@ static int init_overlay_changeset(struct overlay_changeset *ovcs, return 0; - err_free_fragments: kfree(fragments); err_free_idr: idr_remove(&ovcs_idr, ovcs->id); + pr_err("%s() failed, ret = %d\n", __func__, ret); + return ret; } @@ -517,33 +568,71 @@ static void free_overlay_changeset(struct overlay_changeset *ovcs) /** * of_overlay_apply() - Create and apply an overlay changeset * @tree: Expanded overlay device tree + * @ovcs_id: Pointer to overlay changeset id + * + * Creates and applies an overlay changeset. * - * Creates and applies an overlay changeset. If successful, the overlay - * changeset is added to the overlay changeset list. + * If an error occurs in a pre-apply notifier, then no changes are made + * to the device tree. * - * Returns the id of the created overlay changeset, or a negative error number + + * A non-zero return value will not have created the changeset if error is from: + * - parameter checks + * - building the changeset + * - overlay changset pre-apply notifier + * + * If an error is returned by an overlay changeset pre-apply notifier + * then no further overlay changeset pre-apply notifier will be called. + * + * A non-zero return value will have created the changeset if error is from: + * - overlay changeset entry notifier + * - overlay changset post-apply notifier + * + * If an error is returned by an overlay changeset post-apply notifier + * then no further overlay changeset post-apply notifier will be called. + * + * If more than one notifier returns an error, then the last notifier + * error to occur is returned. + * + * If an error occurred while applying the overlay changeset, then an + * attempt is made to revert any changes that were made to the + * device tree. If there were any errors during the revert attempt + * then the state of the device tree can not be determined, and any + * following attempt to apply or remove an overlay changeset will be + * refused. + * + * Returns 0 on success, or a negative error number. Overlay changeset + * id is returned to *ovcs_id. */ -int of_overlay_apply(struct device_node *tree) + +int of_overlay_apply(struct device_node *tree, int *ovcs_id) { struct overlay_changeset *ovcs; - int ret; + int ret = 0, ret_revert, ret_tmp; + + *ovcs_id = 0; + + if (devicetree_corrupt()) { + pr_err("devicetree state suspect, refuse to apply overlay\n"); + ret = -EBUSY; + goto out; + } ovcs = kzalloc(sizeof(*ovcs), GFP_KERNEL); - if (!ovcs) - return -ENOMEM; + if (!ovcs) { + ret = -ENOMEM; + goto out; + } mutex_lock(&of_mutex); ret = init_overlay_changeset(ovcs, tree); - if (ret) { - pr_err("init_overlay_changeset() failed, ret = %d\n", ret); + if (ret) goto err_free_overlay_changeset; - } ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY); - if (ret < 0) { - pr_err("%s: Pre-apply notifier failed (ret=%d)\n", - __func__, ret); + if (ret) { + pr_err("overlay changeset pre-apply notify error %d\n", ret); goto err_free_overlay_changeset; } @@ -551,23 +640,46 @@ int of_overlay_apply(struct device_node *tree) if (ret) goto err_free_overlay_changeset; - ret = __of_changeset_apply(&ovcs->cset); - if (ret) + ret_revert = 0; + ret = __of_changeset_apply_entries(&ovcs->cset, &ret_revert); + if (ret) { + if (ret_revert) { + pr_debug("overlay changeset revert error %d\n", + ret_revert); + devicetree_state_flags |= DTSF_APPLY_FAIL; + } goto err_free_overlay_changeset; + } else { + ret = __of_changeset_apply_notify(&ovcs->cset); + if (ret) + pr_err("overlay changeset entry notify error %d\n", + ret); + /* fall through */ + } list_add_tail(&ovcs->ovcs_list, &ovcs_list); - - overlay_notify(ovcs, OF_OVERLAY_POST_APPLY); + *ovcs_id = ovcs->id; + + ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_APPLY); + if (ret_tmp) { + pr_err("overlay changeset post-apply notify error %d\n", + ret_tmp); + if (!ret) + ret = ret_tmp; + } mutex_unlock(&of_mutex); - return ovcs->id; + goto out; err_free_overlay_changeset: free_overlay_changeset(ovcs); mutex_unlock(&of_mutex); +out: + pr_debug("%s() err=%d\n", __func__, ret); + return ret; } EXPORT_SYMBOL_GPL(of_overlay_apply); @@ -649,45 +761,106 @@ static int overlay_removal_is_ok(struct overlay_changeset *remove_ovcs) /** * of_overlay_remove() - Revert and free an overlay changeset - * @ovcs_id: Overlay changeset id number + * @ovcs_id: Pointer to overlay changeset id * - * Removes an overlay if it is permissible. ovcs_id was previously returned + * Removes an overlay if it is permissible. @ovcs_id was previously returned * by of_overlay_apply(). * - * Returns 0 on success, or a negative error number + * If an error occurred while attempting to revert the overlay changeset, + * then an attempt is made to re-apply any changeset entry that was + * reverted. If an error occurs on re-apply then the state of the device + * tree can not be determined, and any following attempt to apply or remove + * an overlay changeset will be refused. + * + * A non-zero return value will not revert the changeset if error is from: + * - parameter checks + * - overlay changset pre-remove notifier + * - overlay changeset entry revert + * + * If an error is returned by an overlay changeset pre-remove notifier + * then no further overlay changeset pre-remove notifier will be called. + * + * If more than one notifier returns an error, then the last notifier + * error to occur is returned. + * + * A non-zero return value will revert the changeset if error is from: + * - overlay changeset entry notifier + * - overlay changset post-remove notifier + * + * If an error is returned by an overlay changeset post-remove notifier + * then no further overlay changeset post-remove notifier will be called. + * + * Returns 0 on success, or a negative error number. *ovcs_id is set to + * zero after reverting the changeset, even if a subsequent error occurs. */ -int of_overlay_remove(int ovcs_id) +int of_overlay_remove(int *ovcs_id) { struct overlay_changeset *ovcs; - int ret = 0; + int ret, ret_apply, ret_tmp; + + ret = 0; + + if (devicetree_corrupt()) { + pr_err("suspect devicetree state, refuse to remove overlay\n"); + ret = -EBUSY; + goto out; + } mutex_lock(&of_mutex); - ovcs = idr_find(&ovcs_idr, ovcs_id); + ovcs = idr_find(&ovcs_idr, *ovcs_id); if (!ovcs) { ret = -ENODEV; - pr_err("remove: Could not find overlay #%d\n", ovcs_id); - goto out; + pr_err("remove: Could not find overlay #%d\n", *ovcs_id); + goto out_unlock; } if (!overlay_removal_is_ok(ovcs)) { ret = -EBUSY; - goto out; + goto out_unlock; } - overlay_notify(ovcs, OF_OVERLAY_PRE_REMOVE); + ret = overlay_notify(ovcs, OF_OVERLAY_PRE_REMOVE); + if (ret) { + pr_err("overlay changeset pre-remove notify error %d\n", ret); + goto out_unlock; + } list_del(&ovcs->ovcs_list); - __of_changeset_revert(&ovcs->cset); + ret_apply = 0; + ret = __of_changeset_revert_entries(&ovcs->cset, &ret_apply); + if (ret) { + if (ret_apply) + devicetree_state_flags |= DTSF_REVERT_FAIL; + goto out_unlock; + } else { + ret = __of_changeset_revert_notify(&ovcs->cset); + if (ret) { + pr_err("overlay changeset entry notify error %d\n", + ret); + /* fall through - changeset was reverted */ + } + } - overlay_notify(ovcs, OF_OVERLAY_POST_REMOVE); + *ovcs_id = 0; + + ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_REMOVE); + if (ret_tmp) { + pr_err("overlay changeset post-remove notify error %d\n", + ret_tmp); + if (!ret) + ret = ret_tmp; + } free_overlay_changeset(ovcs); -out: +out_unlock: mutex_unlock(&of_mutex); +out: + pr_debug("%s() err=%d\n", __func__, ret); + return ret; } EXPORT_SYMBOL_GPL(of_overlay_remove); @@ -706,7 +879,7 @@ int of_overlay_remove_all(void) /* the tail of list is guaranteed to be safe to remove */ list_for_each_entry_safe_reverse(ovcs, ovcs_n, &ovcs_list, ovcs_list) { - ret = of_overlay_remove(ovcs->id); + ret = of_overlay_remove(&ovcs->id); if (ret) return ret; } diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index bbdaf5606820..3640dae4b9b2 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1217,7 +1217,7 @@ static void of_unittest_untrack_overlay(int id) static void of_unittest_destroy_tracked_overlays(void) { - int id, ret, defers; + int id, ret, defers, ovcs_id; if (overlay_first_id < 0) return; @@ -1230,7 +1230,8 @@ static void of_unittest_destroy_tracked_overlays(void) if (!(overlay_id_bits[BIT_WORD(id)] & BIT_MASK(id))) continue; - ret = of_overlay_remove(id + overlay_first_id); + ovcs_id = id + overlay_first_id; + ret = of_overlay_remove(&ovcs_id); if (ret == -ENODEV) { pr_warn("%s: no overlay to destroy for #%d\n", __func__, id + overlay_first_id); @@ -1252,7 +1253,7 @@ static int of_unittest_apply_overlay(int overlay_nr, int unittest_nr, int *overlay_id) { struct device_node *np = NULL; - int ret, id = -1; + int ret; np = of_find_node_by_path(overlay_path(overlay_nr)); if (np == NULL) { @@ -1262,23 +1263,20 @@ static int of_unittest_apply_overlay(int overlay_nr, int unittest_nr, goto out; } - ret = of_overlay_apply(np); + *overlay_id = 0; + ret = of_overlay_apply(np, overlay_id); if (ret < 0) { unittest(0, "could not create overlay from \"%s\"\n", overlay_path(overlay_nr)); goto out; } - id = ret; - of_unittest_track_overlay(id); + of_unittest_track_overlay(*overlay_id); ret = 0; out: of_node_put(np); - if (overlay_id) - *overlay_id = id; - return ret; } @@ -1286,7 +1284,7 @@ out: static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr, int before, int after, enum overlay_type ovtype) { - int ret; + int ret, ovcs_id; /* unittest device must not be in before state */ if (of_unittest_device_exists(unittest_nr, ovtype) != before) { @@ -1297,7 +1295,8 @@ static int of_unittest_apply_overlay_check(int overlay_nr, int unittest_nr, return -EINVAL; } - ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, NULL); + ovcs_id = 0; + ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ovcs_id); if (ret != 0) { /* of_unittest_apply_overlay already called unittest() */ return ret; @@ -1320,7 +1319,7 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr, int unittest_nr, int before, int after, enum overlay_type ovtype) { - int ret, ov_id; + int ret, ovcs_id; /* unittest device must be in before state */ if (of_unittest_device_exists(unittest_nr, ovtype) != before) { @@ -1332,7 +1331,8 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr, } /* apply the overlay */ - ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ov_id); + ovcs_id = 0; + ret = of_unittest_apply_overlay(overlay_nr, unittest_nr, &ovcs_id); if (ret != 0) { /* of_unittest_apply_overlay already called unittest() */ return ret; @@ -1347,7 +1347,7 @@ static int of_unittest_apply_revert_overlay_check(int overlay_nr, return -EINVAL; } - ret = of_overlay_remove(ov_id); + ret = of_overlay_remove(&ovcs_id); if (ret != 0) { unittest(0, "overlay @\"%s\" failed to be destroyed @\"%s\"\n", overlay_path(overlay_nr), @@ -1449,7 +1449,7 @@ static void of_unittest_overlay_5(void) static void of_unittest_overlay_6(void) { struct device_node *np; - int ret, i, ov_id[2]; + int ret, i, ov_id[2], ovcs_id; int overlay_nr = 6, unittest_nr = 6; int before = 0, after = 1; @@ -1476,13 +1476,14 @@ static void of_unittest_overlay_6(void) return; } - ret = of_overlay_apply(np); + ovcs_id = 0; + ret = of_overlay_apply(np, &ovcs_id); if (ret < 0) { unittest(0, "could not create overlay from \"%s\"\n", overlay_path(overlay_nr + i)); return; } - ov_id[i] = ret; + ov_id[i] = ovcs_id; of_unittest_track_overlay(ov_id[i]); } @@ -1500,7 +1501,8 @@ static void of_unittest_overlay_6(void) } for (i = 1; i >= 0; i--) { - ret = of_overlay_remove(ov_id[i]); + ovcs_id = ov_id[i]; + ret = of_overlay_remove(&ovcs_id); if (ret != 0) { unittest(0, "overlay @\"%s\" failed destroy @\"%s\"\n", overlay_path(overlay_nr + i), @@ -1531,7 +1533,7 @@ static void of_unittest_overlay_6(void) static void of_unittest_overlay_8(void) { struct device_node *np; - int ret, i, ov_id[2]; + int ret, i, ov_id[2], ovcs_id; int overlay_nr = 8, unittest_nr = 8; /* we don't care about device state in this test */ @@ -1546,18 +1548,20 @@ static void of_unittest_overlay_8(void) return; } - ret = of_overlay_apply(np); + ovcs_id = 0; + ret = of_overlay_apply(np, &ovcs_id); if (ret < 0) { unittest(0, "could not create overlay from \"%s\"\n", overlay_path(overlay_nr + i)); return; } - ov_id[i] = ret; + ov_id[i] = ovcs_id; of_unittest_track_overlay(ov_id[i]); } /* now try to remove first overlay (it should fail) */ - ret = of_overlay_remove(ov_id[0]); + ovcs_id = ov_id[0]; + ret = of_overlay_remove(&ovcs_id); if (ret == 0) { unittest(0, "overlay @\"%s\" was destroyed @\"%s\"\n", overlay_path(overlay_nr + 0), @@ -1568,7 +1572,8 @@ static void of_unittest_overlay_8(void) /* removing them in order should work */ for (i = 1; i >= 0; i--) { - ret = of_overlay_remove(ov_id[i]); + ovcs_id = ov_id[i]; + ret = of_overlay_remove(&ovcs_id); if (ret != 0) { unittest(0, "overlay @\"%s\" not destroyed @\"%s\"\n", overlay_path(overlay_nr + i), @@ -2149,13 +2154,11 @@ static int __init overlay_data_add(int onum) goto out_free_np_overlay; } - ret = of_overlay_apply(info->np_overlay); + info->overlay_id = 0; + ret = of_overlay_apply(info->np_overlay, &info->overlay_id); if (ret < 0) { pr_err("of_overlay_apply() (ret=%d), %d\n", ret, onum); goto out_free_np_overlay; - } else { - info->overlay_id = ret; - ret = 0; } pr_debug("__dtb_overlay_begin applied, overlay id %d\n", ret); diff --git a/include/linux/of.h b/include/linux/of.h index 7569e9cc45de..96edda95c6b0 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -1298,7 +1298,7 @@ static inline bool of_device_is_system_power_controller(const struct device_node */ enum of_overlay_notify_action { - OF_OVERLAY_PRE_APPLY, + OF_OVERLAY_PRE_APPLY = 0, OF_OVERLAY_POST_APPLY, OF_OVERLAY_PRE_REMOVE, OF_OVERLAY_POST_REMOVE, @@ -1312,8 +1312,8 @@ struct of_overlay_notify_data { #ifdef CONFIG_OF_OVERLAY /* ID based overlays; the API for external users */ -int of_overlay_apply(struct device_node *tree); -int of_overlay_remove(int id); +int of_overlay_apply(struct device_node *tree, int *ovcs_id); +int of_overlay_remove(int *ovcs_id); int of_overlay_remove_all(void); int of_overlay_notifier_register(struct notifier_block *nb); @@ -1321,12 +1321,12 @@ int of_overlay_notifier_unregister(struct notifier_block *nb); #else -static inline int of_overlay_apply(struct device_node *tree) +static inline int of_overlay_apply(struct device_node *tree, int *ovcs_id) { return -ENOTSUPP; } -static inline int of_overlay_remove(int id) +static inline int of_overlay_remove(int *ovcs_id) { return -ENOTSUPP; } -- cgit v1.2.3-59-g8ed1b From f948d6d8b792bb90041edc12eac35faf83030994 Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Tue, 17 Oct 2017 16:36:29 -0700 Subject: of: overlay: avoid race condition between applying multiple overlays The process of applying an overlay consists of: - unflatten an overlay FDT (flattened device tree) into an EDT (expanded device tree) - fixup the phandle values in the overlay EDT to fit in a range above the phandle values in the live device tree - create the overlay changeset to reflect the contents of the overlay EDT - apply the overlay changeset, to modify the live device tree, potentially changing the maximum phandle value in the live device tree There is currently no protection against two overlay applies concurrently determining what range of phandle values are in use in the live device tree, and subsequently changing that range. Add a mutex to prevent multiple overlay applies from occurring simultaneously. Move of_resolve_phandles() into of_overlay_apply() so that it does not have to be duplicated by each caller of of_overlay_apply(). The test in of_resolve_phandles() that the overlay tree is detached is temporarily disabled so that old style overlay unittests do not fail. Signed-off-by: Frank Rowand Signed-off-by: Rob Herring --- drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c | 6 ------ drivers/of/of_private.h | 12 +++++++++++ drivers/of/overlay.c | 32 ++++++++++++++++++++++++++++ drivers/of/resolver.c | 7 ++++++ drivers/of/unittest.c | 22 +++++++++++++------ include/linux/of.h | 3 --- 6 files changed, 67 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c index 7a7be0515bfd..54025af534d4 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c @@ -145,7 +145,6 @@ static struct device_node * __init tilcdc_get_overlay(struct kfree_table *kft) __dtb_tilcdc_slave_compat_begin; static void *overlay_data; struct device_node *overlay; - int ret; if (!size) { pr_warn("%s: No overlay data\n", __func__); @@ -164,11 +163,6 @@ static struct device_node * __init tilcdc_get_overlay(struct kfree_table *kft) } of_node_set_flag(overlay, OF_DETACHED); - ret = of_resolve_phandles(overlay); - if (ret) { - pr_err("%s: Failed to resolve phandles: %d\n", __func__, ret); - return NULL; - } return overlay; } diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 36357f571df2..248730567dbe 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -76,6 +76,18 @@ static inline int __of_attach_node_sysfs(struct device_node *np) static inline void __of_detach_node_sysfs(struct device_node *np) {} #endif +#if defined(CONFIG_OF_RESOLVE) +int of_resolve_phandles(struct device_node *tree); +#endif + +#if defined(CONFIG_OF_OVERLAY) +void of_overlay_mutex_lock(void); +void of_overlay_mutex_unlock(void); +#else +static inline void of_overlay_mutex_lock(void) {}; +static inline void of_overlay_mutex_unlock(void) {}; +#endif + #if defined(CONFIG_OF_UNITTEST) && defined(CONFIG_OF_OVERLAY) extern void __init unittest_unflatten_overlay_base(void); #else diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 791753321ed2..d164f86e5541 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -71,6 +71,28 @@ static int build_changeset_next_level(struct overlay_changeset *ovcs, const struct device_node *overlay_node, bool is_symbols_node); +/* + * of_resolve_phandles() finds the largest phandle in the live tree. + * of_overlay_apply() may add a larger phandle to the live tree. + * Do not allow race between two overlays being applied simultaneously: + * mutex_lock(&of_overlay_phandle_mutex) + * of_resolve_phandles() + * of_overlay_apply() + * mutex_unlock(&of_overlay_phandle_mutex) + */ +static DEFINE_MUTEX(of_overlay_phandle_mutex); + +void of_overlay_mutex_lock(void) +{ + mutex_lock(&of_overlay_phandle_mutex); +} + +void of_overlay_mutex_unlock(void) +{ + mutex_unlock(&of_overlay_phandle_mutex); +} + + static LIST_HEAD(ovcs_list); static DEFINE_IDR(ovcs_idr); @@ -624,6 +646,12 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id) goto out; } + of_overlay_mutex_lock(); + + ret = of_resolve_phandles(tree); + if (ret) + goto err_overlay_unlock; + mutex_lock(&of_mutex); ret = init_overlay_changeset(ovcs, tree); @@ -669,9 +697,13 @@ int of_overlay_apply(struct device_node *tree, int *ovcs_id) } mutex_unlock(&of_mutex); + of_overlay_mutex_unlock(); goto out; +err_overlay_unlock: + of_overlay_mutex_unlock(); + err_free_overlay_changeset: free_overlay_changeset(ovcs); diff --git a/drivers/of/resolver.c b/drivers/of/resolver.c index bd21a66f6930..cfaeef5f6cb1 100644 --- a/drivers/of/resolver.c +++ b/drivers/of/resolver.c @@ -271,11 +271,18 @@ int of_resolve_phandles(struct device_node *overlay) err = -EINVAL; goto out; } + +#if 0 + Temporarily disable check so that old style overlay unittests + do not fail when of_resolve_phandles() is moved into + of_overlay_apply(). + if (!of_node_check_flag(overlay, OF_DETACHED)) { pr_err("overlay not detached\n"); err = -EINVAL; goto out; } +#endif phandle_delta = live_tree_max_phandle() + 1; adjust_overlay_phandles(overlay, phandle_delta); diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 3640dae4b9b2..273d78c1520b 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -993,9 +993,17 @@ static int __init unittest_data_add(void) pr_warn("%s: No tree to attach; not running tests\n", __func__); return -ENODATA; } + + /* + * This lock normally encloses of_overlay_apply() as well as + * of_resolve_phandles(). + */ + of_overlay_mutex_lock(); + rc = of_resolve_phandles(unittest_data_node); if (rc) { pr_err("%s: Failed to resolve phandles (rc=%i)\n", __func__, rc); + of_overlay_mutex_unlock(); return -EINVAL; } @@ -1005,6 +1013,7 @@ static int __init unittest_data_add(void) __of_attach_node_sysfs(np); of_aliases = of_find_node_by_path("/aliases"); of_chosen = of_find_node_by_path("/chosen"); + of_overlay_mutex_unlock(); return 0; } @@ -1017,6 +1026,9 @@ static int __init unittest_data_add(void) attach_node_and_children(np); np = next; } + + of_overlay_mutex_unlock(); + return 0; } @@ -2148,16 +2160,11 @@ static int __init overlay_data_add(int onum) goto out_free_data; } - ret = of_resolve_phandles(info->np_overlay); - if (ret) { - pr_err("resolve ot phandles (ret=%d), %d\n", ret, onum); - goto out_free_np_overlay; - } - info->overlay_id = 0; ret = of_overlay_apply(info->np_overlay, &info->overlay_id); if (ret < 0) { pr_err("of_overlay_apply() (ret=%d), %d\n", ret, onum); + of_overlay_mutex_unlock(); goto out_free_np_overlay; } @@ -2207,7 +2214,10 @@ static __init void of_unittest_overlay_high_level(void) * Could not fixup phandles in unittest_unflatten_overlay_base() * because kmalloc() was not yet available. */ + of_overlay_mutex_lock(); of_resolve_phandles(overlay_base_root); + of_overlay_mutex_unlock(); + /* * do not allow overlay_base to duplicate any node already in diff --git a/include/linux/of.h b/include/linux/of.h index 96edda95c6b0..ef4c9ff5955a 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -1279,9 +1279,6 @@ static inline int of_reconfig_get_state_change(unsigned long action, } #endif /* CONFIG_OF_DYNAMIC */ -/* CONFIG_OF_RESOLVE api */ -extern int of_resolve_phandles(struct device_node *tree); - /** * of_device_is_system_power_controller - Tells if system-power-controller is found for device_node * @np: Pointer to the given device_node -- cgit v1.2.3-59-g8ed1b From a68f4a27f55f1d54e35c270aff89383da4b1b656 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 18 Oct 2017 11:36:39 +0100 Subject: rxrpc: Support service upgrade from a kernel service Provide support for a kernel service to make use of the service upgrade facility. This involves: (1) Pass an upgrade request flag to rxrpc_kernel_begin_call(). (2) Make rxrpc_kernel_recv_data() return the call's current service ID so that the caller can detect service upgrade and see what the service was upgraded to. Signed-off-by: David Howells --- Documentation/networking/rxrpc.txt | 17 +++++++++++++++-- fs/afs/internal.h | 1 + fs/afs/rxrpc.c | 11 +++++++---- include/net/af_rxrpc.h | 5 +++-- net/rxrpc/af_rxrpc.c | 5 ++++- net/rxrpc/recvmsg.c | 5 ++++- 6 files changed, 34 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt index 810620153a44..9fb61a6bc7cf 100644 --- a/Documentation/networking/rxrpc.txt +++ b/Documentation/networking/rxrpc.txt @@ -782,7 +782,9 @@ The kernel interface functions are as follows: struct key *key, unsigned long user_call_ID, s64 tx_total_len, - gfp_t gfp); + gfp_t gfp, + rxrpc_notify_rx_t notify_rx, + bool upgrade); This allocates the infrastructure to make a new RxRPC call and assigns call and connection numbers. The call will be made on the UDP port that @@ -803,6 +805,13 @@ The kernel interface functions are as follows: allows the kernel to encrypt directly to the packet buffers, thereby saving a copy. The value may not be less than -1. + notify_rx is a pointer to a function to be called when events such as + incoming data packets or remote aborts happen. + + upgrade should be set to true if a client operation should request that + the server upgrade the service to a better one. The resultant service ID + is returned by rxrpc_kernel_recv_data(). + If this function is successful, an opaque reference to the RxRPC call is returned. The caller now holds a reference on this and it must be properly ended. @@ -850,7 +859,8 @@ The kernel interface functions are as follows: size_t size, size_t *_offset, bool want_more, - u32 *_abort) + u32 *_abort, + u16 *_service) This is used to receive data from either the reply part of a client call or the request part of a service call. buf and size specify how much @@ -873,6 +883,9 @@ The kernel interface functions are as follows: If a remote ABORT is detected, the abort code received will be stored in *_abort and ECONNABORTED will be returned. + The service ID that the call ended up with is returned into *_service. + This can be used to see if a call got a service upgrade. + (*) Abort a call. void rxrpc_kernel_abort_call(struct socket *sock, diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 82e16556afea..3f03f7888302 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -100,6 +100,7 @@ struct afs_call { bool send_pages; /* T if data from mapping should be sent */ bool need_attention; /* T if RxRPC poked us */ bool async; /* T if asynchronous */ + bool upgrade; /* T to request service upgrade */ u16 service_id; /* RxRPC service ID to call */ __be16 port; /* target UDP port */ u32 operation_ID; /* operation ID for an incoming call */ diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 0bf191f0dbaf..172a4f9747ac 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -387,7 +387,8 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp, tx_total_len, gfp, (async ? afs_wake_up_async_call : - afs_wake_up_call_waiter)); + afs_wake_up_call_waiter), + call->upgrade); call->key = NULL; if (IS_ERR(rxcall)) { ret = PTR_ERR(rxcall); @@ -443,7 +444,7 @@ error_do_abort: abort_code = 0; offset = 0; rxrpc_kernel_recv_data(afs_socket, rxcall, NULL, 0, &offset, - false, &abort_code); + false, &abort_code, &call->service_id); ret = call->type->abort_to_error(abort_code); } error_kill_call: @@ -471,7 +472,8 @@ static void afs_deliver_to_call(struct afs_call *call) size_t offset = 0; ret = rxrpc_kernel_recv_data(afs_socket, call->rxcall, NULL, 0, &offset, false, - &call->abort_code); + &call->abort_code, + &call->service_id); trace_afs_recv_data(call, 0, offset, false, ret); if (ret == -EINPROGRESS || ret == -EAGAIN) @@ -851,7 +853,8 @@ int afs_extract_data(struct afs_call *call, void *buf, size_t count, ret = rxrpc_kernel_recv_data(afs_socket, call->rxcall, buf, count, &call->offset, - want_more, &call->abort_code); + want_more, &call->abort_code, + &call->service_id); trace_afs_recv_data(call, count, call->offset, want_more, ret); if (ret == 0 || ret == -EAGAIN) return ret; diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index 3ac79150291f..820dd365a08e 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -49,12 +49,13 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *, unsigned long, s64, gfp_t, - rxrpc_notify_rx_t); + rxrpc_notify_rx_t, + bool); int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *, struct msghdr *, size_t, rxrpc_notify_end_tx_t); int rxrpc_kernel_recv_data(struct socket *, struct rxrpc_call *, - void *, size_t, size_t *, bool, u32 *); + void *, size_t, size_t *, bool, u32 *, u16 *); bool rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *, u32, int, const char *); void rxrpc_kernel_end_call(struct socket *, struct rxrpc_call *); diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index fb17552fd292..481f7dc90ba2 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -265,6 +265,7 @@ static int rxrpc_listen(struct socket *sock, int backlog) * @tx_total_len: Total length of data to transmit during the call (or -1) * @gfp: The allocation constraints * @notify_rx: Where to send notifications instead of socket queue + * @upgrade: Request service upgrade for call * * Allow a kernel service to begin a call on the nominated socket. This just * sets up all the internal tracking structures and allocates connection and @@ -279,7 +280,8 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, unsigned long user_call_ID, s64 tx_total_len, gfp_t gfp, - rxrpc_notify_rx_t notify_rx) + rxrpc_notify_rx_t notify_rx, + bool upgrade) { struct rxrpc_conn_parameters cp; struct rxrpc_call *call; @@ -304,6 +306,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, cp.key = key; cp.security_level = 0; cp.exclusive = false; + cp.upgrade = upgrade; cp.service_id = srx->srx_service; call = rxrpc_new_client_call(rx, &cp, srx, user_call_ID, tx_total_len, gfp); diff --git a/net/rxrpc/recvmsg.c b/net/rxrpc/recvmsg.c index bdece21f313d..e4937b3f3685 100644 --- a/net/rxrpc/recvmsg.c +++ b/net/rxrpc/recvmsg.c @@ -607,6 +607,7 @@ wait_error: * @_offset: The running offset into the buffer. * @want_more: True if more data is expected to be read * @_abort: Where the abort code is stored if -ECONNABORTED is returned + * @_service: Where to store the actual service ID (may be upgraded) * * Allow a kernel service to receive data and pick up information about the * state of a call. Returns 0 if got what was asked for and there's more @@ -624,7 +625,7 @@ wait_error: */ int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call, void *buf, size_t size, size_t *_offset, - bool want_more, u32 *_abort) + bool want_more, u32 *_abort, u16 *_service) { struct iov_iter iter; struct kvec iov; @@ -680,6 +681,8 @@ int rxrpc_kernel_recv_data(struct socket *sock, struct rxrpc_call *call, read_phase_complete: ret = 1; out: + if (_service) + *_service = call->service_id; mutex_unlock(&call->user_mutex); _leave(" = %d [%zu,%d]", ret, *_offset, *_abort); return ret; -- cgit v1.2.3-59-g8ed1b From f4d15fb6f99af9b99f688bd87579137be44f85ee Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 18 Oct 2017 11:07:31 +0100 Subject: rxrpc: Provide functions for allowing cleaner handling of signals Provide a couple of functions to allow cleaner handling of signals in a kernel service. They are: (1) rxrpc_kernel_get_rtt() This allows the kernel service to find out the RTT time for a call, so as to better judge how large a timeout to employ. Note, though, that whilst this returns a value in nanoseconds, the timeouts can only actually be in jiffies. (2) rxrpc_kernel_check_life() This returns a number that is updated when ACKs are received from the peer (notably including PING RESPONSE ACKs which we can elicit by sending PING ACKs to see if the call still exists on the server). The caller should compare the numbers of two calls to see if the call is still alive. These can be used to provide an extending timeout rather than returning immediately in the case that a signal occurs that would otherwise abort an RPC operation. The timeout would be extended if the server is still responsive and the call is still apparently alive on the server. For most operations this isn't that necessary - but for FS.StoreData it is: OpenAFS writes the data to storage as it comes in without making a backup, so if we immediately abort it when partially complete on a CTRL+C, say, we have no idea of the state of the file after the abort. Signed-off-by: David Howells --- Documentation/networking/rxrpc.txt | 24 ++++++++++++++++++++++++ include/net/af_rxrpc.h | 2 ++ net/rxrpc/af_rxrpc.c | 19 +++++++++++++++++++ net/rxrpc/peer_object.c | 13 +++++++++++++ 4 files changed, 58 insertions(+) (limited to 'include') diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt index 9fb61a6bc7cf..1fb5c553aedd 100644 --- a/Documentation/networking/rxrpc.txt +++ b/Documentation/networking/rxrpc.txt @@ -1033,6 +1033,30 @@ The kernel interface functions are as follows: It returns 0 if the call was requeued and an error otherwise. + (*) Get call RTT. + + u64 rxrpc_kernel_get_rtt(struct socket *sock, struct rxrpc_call *call); + + Get the RTT time to the peer in use by a call. The value returned is in + nanoseconds. + + (*) Check call still alive. + + u32 rxrpc_kernel_check_life(struct socket *sock, + struct rxrpc_call *call); + + This returns a number that is updated when ACKs are received from the peer + (notably including PING RESPONSE ACKs which we can elicit by sending PING + ACKs to see if the call still exists on the server). The caller should + compare the numbers of two calls to see if the call is still alive after + waiting for a suitable interval. + + This allows the caller to work out if the server is still contactable and + if the call is still alive on the server whilst waiting for the server to + process a client operation. + + This function may transmit a PING ACK. + ======================= CONFIGURABLE PARAMETERS diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index 820dd365a08e..2b3a6eec4570 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -61,6 +61,7 @@ bool rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *, void rxrpc_kernel_end_call(struct socket *, struct rxrpc_call *); void rxrpc_kernel_get_peer(struct socket *, struct rxrpc_call *, struct sockaddr_rxrpc *); +u64 rxrpc_kernel_get_rtt(struct socket *, struct rxrpc_call *); int rxrpc_kernel_charge_accept(struct socket *, rxrpc_notify_rx_t, rxrpc_user_attach_call_t, unsigned long, gfp_t); void rxrpc_kernel_set_tx_length(struct socket *, struct rxrpc_call *, s64); @@ -68,5 +69,6 @@ int rxrpc_kernel_retry_call(struct socket *, struct rxrpc_call *, struct sockaddr_rxrpc *, struct key *); int rxrpc_kernel_check_call(struct socket *, struct rxrpc_call *, enum rxrpc_call_completion *, u32 *); +u32 rxrpc_kernel_check_life(struct socket *, struct rxrpc_call *); #endif /* _NET_RXRPC_H */ diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 481f7dc90ba2..73c980e26581 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -339,6 +339,25 @@ void rxrpc_kernel_end_call(struct socket *sock, struct rxrpc_call *call) } EXPORT_SYMBOL(rxrpc_kernel_end_call); +/** + * rxrpc_kernel_check_life - Check to see whether a call is still alive + * @sock: The socket the call is on + * @call: The call to check + * + * Allow a kernel service to find out whether a call is still alive - ie. we're + * getting ACKs from the server. Returns a number representing the life state + * which can be compared to that returned by a previous call. + * + * If this is a client call, ping ACKs will be sent to the server to find out + * whether it's still responsive and whether the call is still alive on the + * server. + */ +u32 rxrpc_kernel_check_life(struct socket *sock, struct rxrpc_call *call) +{ + return call->acks_latest; +} +EXPORT_SYMBOL(rxrpc_kernel_check_life); + /** * rxrpc_kernel_check_call - Check a call's state * @sock: The socket the call is on diff --git a/net/rxrpc/peer_object.c b/net/rxrpc/peer_object.c index 5787f97f5330..d02a99f37f5f 100644 --- a/net/rxrpc/peer_object.c +++ b/net/rxrpc/peer_object.c @@ -411,3 +411,16 @@ void rxrpc_kernel_get_peer(struct socket *sock, struct rxrpc_call *call, *_srx = call->peer->srx; } EXPORT_SYMBOL(rxrpc_kernel_get_peer); + +/** + * rxrpc_kernel_get_rtt - Get a call's peer RTT + * @sock: The socket on which the call is in progress. + * @call: The call to query + * + * Get the call's peer RTT. + */ +u64 rxrpc_kernel_get_rtt(struct socket *sock, struct rxrpc_call *call) +{ + return call->peer->rtt; +} +EXPORT_SYMBOL(rxrpc_kernel_get_rtt); -- cgit v1.2.3-59-g8ed1b From 6710e1126934d8b4372b4d2f9ae1646cd3f151bf Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 16 Oct 2017 12:19:28 +0200 Subject: bpf: introduce new bpf cpu map type BPF_MAP_TYPE_CPUMAP The 'cpumap' is primarily used as a backend map for XDP BPF helper call bpf_redirect_map() and XDP_REDIRECT action, like 'devmap'. This patch implement the main part of the map. It is not connected to the XDP redirect system yet, and no SKB allocation are done yet. The main concern in this patch is to ensure the datapath can run without any locking. This adds complexity to the setup and tear-down procedure, which assumptions are extra carefully documented in the code comments. V2: - make sure array isn't larger than NR_CPUS - make sure CPUs added is a valid possible CPU V3: fix nitpicks from Jakub Kicinski V5: - Restrict map allocation to root / CAP_SYS_ADMIN - WARN_ON_ONCE if queue is not empty on tear-down - Return -EPERM on memlock limit instead of -ENOMEM - Error code in __cpu_map_entry_alloc() also handle ptr_ring_cleanup() - Moved cpu_map_enqueue() to next patch V6: all notice by Daniel Borkmann - Fix err return code in cpu_map_alloc() introduced in V5 - Move cpu_possible() check after max_entries boundary check - Forbid usage initially in check_map_func_compatibility() V7: - Fix alloc error path spotted by Daniel Borkmann - Did stress test adding+removing CPUs from the map concurrently - Fixed refcnt issue on cpu_map_entry, kthread started too soon - Make sure packets are flushed during tear-down, involved use of rcu_barrier() and kthread_run only exit after queue is empty - Fix alloc error path in __cpu_map_entry_alloc() for ptr_ring V8: - Nitpicking comments and gramma by Edward Cree - Fix missing semi-colon introduced in V7 due to rebasing - Move struct bpf_cpu_map_entry members cpu+map_id to tracepoint patch Signed-off-by: Jesper Dangaard Brouer Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf_types.h | 1 + include/uapi/linux/bpf.h | 1 + kernel/bpf/Makefile | 1 + kernel/bpf/cpumap.c | 560 +++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 8 +- kernel/bpf/verifier.c | 5 + tools/include/uapi/linux/bpf.h | 1 + 7 files changed, 576 insertions(+), 1 deletion(-) create mode 100644 kernel/bpf/cpumap.c (limited to 'include') diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 6f1a567667b8..814c1081a4a9 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -41,4 +41,5 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_DEVMAP, dev_map_ops) #ifdef CONFIG_STREAM_PARSER BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKMAP, sock_map_ops) #endif +BPF_MAP_TYPE(BPF_MAP_TYPE_CPUMAP, cpu_map_ops) #endif diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 6db9e1d679cd..4303fb6c3817 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -112,6 +112,7 @@ enum bpf_map_type { BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_DEVMAP, BPF_MAP_TYPE_SOCKMAP, + BPF_MAP_TYPE_CPUMAP, }; enum bpf_prog_type { diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 53fb09f92e3f..e597daae6120 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list obj-$(CONFIG_BPF_SYSCALL) += disasm.o ifeq ($(CONFIG_NET),y) obj-$(CONFIG_BPF_SYSCALL) += devmap.o +obj-$(CONFIG_BPF_SYSCALL) += cpumap.o ifeq ($(CONFIG_STREAM_PARSER),y) obj-$(CONFIG_BPF_SYSCALL) += sockmap.o endif diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c new file mode 100644 index 000000000000..e1e25ddba038 --- /dev/null +++ b/kernel/bpf/cpumap.c @@ -0,0 +1,560 @@ +/* bpf/cpumap.c + * + * Copyright (c) 2017 Jesper Dangaard Brouer, Red Hat Inc. + * Released under terms in GPL version 2. See COPYING. + */ + +/* The 'cpumap' is primarily used as a backend map for XDP BPF helper + * call bpf_redirect_map() and XDP_REDIRECT action, like 'devmap'. + * + * Unlike devmap which redirects XDP frames out another NIC device, + * this map type redirects raw XDP frames to another CPU. The remote + * CPU will do SKB-allocation and call the normal network stack. + * + * This is a scalability and isolation mechanism, that allow + * separating the early driver network XDP layer, from the rest of the + * netstack, and assigning dedicated CPUs for this stage. This + * basically allows for 10G wirespeed pre-filtering via bpf. + */ +#include +#include +#include + +#include +#include +#include +#include + +/* General idea: XDP packets getting XDP redirected to another CPU, + * will maximum be stored/queued for one driver ->poll() call. It is + * guaranteed that setting flush bit and flush operation happen on + * same CPU. Thus, cpu_map_flush operation can deduct via this_cpu_ptr() + * which queue in bpf_cpu_map_entry contains packets. + */ + +#define CPU_MAP_BULK_SIZE 8 /* 8 == one cacheline on 64-bit archs */ +struct xdp_bulk_queue { + void *q[CPU_MAP_BULK_SIZE]; + unsigned int count; +}; + +/* Struct for every remote "destination" CPU in map */ +struct bpf_cpu_map_entry { + u32 qsize; /* Queue size placeholder for map lookup */ + + /* XDP can run multiple RX-ring queues, need __percpu enqueue store */ + struct xdp_bulk_queue __percpu *bulkq; + + /* Queue with potential multi-producers, and single-consumer kthread */ + struct ptr_ring *queue; + struct task_struct *kthread; + struct work_struct kthread_stop_wq; + + atomic_t refcnt; /* Control when this struct can be free'ed */ + struct rcu_head rcu; +}; + +struct bpf_cpu_map { + struct bpf_map map; + /* Below members specific for map type */ + struct bpf_cpu_map_entry **cpu_map; + unsigned long __percpu *flush_needed; +}; + +static int bq_flush_to_queue(struct bpf_cpu_map_entry *rcpu, + struct xdp_bulk_queue *bq); + +static u64 cpu_map_bitmap_size(const union bpf_attr *attr) +{ + return BITS_TO_LONGS(attr->max_entries) * sizeof(unsigned long); +} + +static struct bpf_map *cpu_map_alloc(union bpf_attr *attr) +{ + struct bpf_cpu_map *cmap; + int err = -ENOMEM; + u64 cost; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return ERR_PTR(-EPERM); + + /* check sanity of attributes */ + if (attr->max_entries == 0 || attr->key_size != 4 || + attr->value_size != 4 || attr->map_flags & ~BPF_F_NUMA_NODE) + return ERR_PTR(-EINVAL); + + cmap = kzalloc(sizeof(*cmap), GFP_USER); + if (!cmap) + return ERR_PTR(-ENOMEM); + + /* mandatory map attributes */ + cmap->map.map_type = attr->map_type; + cmap->map.key_size = attr->key_size; + cmap->map.value_size = attr->value_size; + cmap->map.max_entries = attr->max_entries; + cmap->map.map_flags = attr->map_flags; + cmap->map.numa_node = bpf_map_attr_numa_node(attr); + + /* Pre-limit array size based on NR_CPUS, not final CPU check */ + if (cmap->map.max_entries > NR_CPUS) { + err = -E2BIG; + goto free_cmap; + } + + /* make sure page count doesn't overflow */ + cost = (u64) cmap->map.max_entries * sizeof(struct bpf_cpu_map_entry *); + cost += cpu_map_bitmap_size(attr) * num_possible_cpus(); + if (cost >= U32_MAX - PAGE_SIZE) + goto free_cmap; + cmap->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT; + + /* Notice returns -EPERM on if map size is larger than memlock limit */ + ret = bpf_map_precharge_memlock(cmap->map.pages); + if (ret) { + err = ret; + goto free_cmap; + } + + /* A per cpu bitfield with a bit per possible CPU in map */ + cmap->flush_needed = __alloc_percpu(cpu_map_bitmap_size(attr), + __alignof__(unsigned long)); + if (!cmap->flush_needed) + goto free_cmap; + + /* Alloc array for possible remote "destination" CPUs */ + cmap->cpu_map = bpf_map_area_alloc(cmap->map.max_entries * + sizeof(struct bpf_cpu_map_entry *), + cmap->map.numa_node); + if (!cmap->cpu_map) + goto free_percpu; + + return &cmap->map; +free_percpu: + free_percpu(cmap->flush_needed); +free_cmap: + kfree(cmap); + return ERR_PTR(err); +} + +void __cpu_map_queue_destructor(void *ptr) +{ + /* The tear-down procedure should have made sure that queue is + * empty. See __cpu_map_entry_replace() and work-queue + * invoked cpu_map_kthread_stop(). Catch any broken behaviour + * gracefully and warn once. + */ + if (WARN_ON_ONCE(ptr)) + page_frag_free(ptr); +} + +static void put_cpu_map_entry(struct bpf_cpu_map_entry *rcpu) +{ + if (atomic_dec_and_test(&rcpu->refcnt)) { + /* The queue should be empty at this point */ + ptr_ring_cleanup(rcpu->queue, __cpu_map_queue_destructor); + kfree(rcpu->queue); + kfree(rcpu); + } +} + +static void get_cpu_map_entry(struct bpf_cpu_map_entry *rcpu) +{ + atomic_inc(&rcpu->refcnt); +} + +/* called from workqueue, to workaround syscall using preempt_disable */ +static void cpu_map_kthread_stop(struct work_struct *work) +{ + struct bpf_cpu_map_entry *rcpu; + + rcpu = container_of(work, struct bpf_cpu_map_entry, kthread_stop_wq); + + /* Wait for flush in __cpu_map_entry_free(), via full RCU barrier, + * as it waits until all in-flight call_rcu() callbacks complete. + */ + rcu_barrier(); + + /* kthread_stop will wake_up_process and wait for it to complete */ + kthread_stop(rcpu->kthread); +} + +static int cpu_map_kthread_run(void *data) +{ + struct bpf_cpu_map_entry *rcpu = data; + + set_current_state(TASK_INTERRUPTIBLE); + + /* When kthread gives stop order, then rcpu have been disconnected + * from map, thus no new packets can enter. Remaining in-flight + * per CPU stored packets are flushed to this queue. Wait honoring + * kthread_stop signal until queue is empty. + */ + while (!kthread_should_stop() || !__ptr_ring_empty(rcpu->queue)) { + struct xdp_pkt *xdp_pkt; + + schedule(); + /* Do work */ + while ((xdp_pkt = ptr_ring_consume(rcpu->queue))) { + /* For now just "refcnt-free" */ + page_frag_free(xdp_pkt); + } + __set_current_state(TASK_INTERRUPTIBLE); + } + __set_current_state(TASK_RUNNING); + + put_cpu_map_entry(rcpu); + return 0; +} + +struct bpf_cpu_map_entry *__cpu_map_entry_alloc(u32 qsize, u32 cpu, int map_id) +{ + gfp_t gfp = GFP_ATOMIC|__GFP_NOWARN; + struct bpf_cpu_map_entry *rcpu; + int numa, err; + + /* Have map->numa_node, but choose node of redirect target CPU */ + numa = cpu_to_node(cpu); + + rcpu = kzalloc_node(sizeof(*rcpu), gfp, numa); + if (!rcpu) + return NULL; + + /* Alloc percpu bulkq */ + rcpu->bulkq = __alloc_percpu_gfp(sizeof(*rcpu->bulkq), + sizeof(void *), gfp); + if (!rcpu->bulkq) + goto free_rcu; + + /* Alloc queue */ + rcpu->queue = kzalloc_node(sizeof(*rcpu->queue), gfp, numa); + if (!rcpu->queue) + goto free_bulkq; + + err = ptr_ring_init(rcpu->queue, qsize, gfp); + if (err) + goto free_queue; + + rcpu->qsize = qsize; + + /* Setup kthread */ + rcpu->kthread = kthread_create_on_node(cpu_map_kthread_run, rcpu, numa, + "cpumap/%d/map:%d", cpu, map_id); + if (IS_ERR(rcpu->kthread)) + goto free_ptr_ring; + + get_cpu_map_entry(rcpu); /* 1-refcnt for being in cmap->cpu_map[] */ + get_cpu_map_entry(rcpu); /* 1-refcnt for kthread */ + + /* Make sure kthread runs on a single CPU */ + kthread_bind(rcpu->kthread, cpu); + wake_up_process(rcpu->kthread); + + return rcpu; + +free_ptr_ring: + ptr_ring_cleanup(rcpu->queue, NULL); +free_queue: + kfree(rcpu->queue); +free_bulkq: + free_percpu(rcpu->bulkq); +free_rcu: + kfree(rcpu); + return NULL; +} + +void __cpu_map_entry_free(struct rcu_head *rcu) +{ + struct bpf_cpu_map_entry *rcpu; + int cpu; + + /* This cpu_map_entry have been disconnected from map and one + * RCU graze-period have elapsed. Thus, XDP cannot queue any + * new packets and cannot change/set flush_needed that can + * find this entry. + */ + rcpu = container_of(rcu, struct bpf_cpu_map_entry, rcu); + + /* Flush remaining packets in percpu bulkq */ + for_each_online_cpu(cpu) { + struct xdp_bulk_queue *bq = per_cpu_ptr(rcpu->bulkq, cpu); + + /* No concurrent bq_enqueue can run at this point */ + bq_flush_to_queue(rcpu, bq); + } + free_percpu(rcpu->bulkq); + /* Cannot kthread_stop() here, last put free rcpu resources */ + put_cpu_map_entry(rcpu); +} + +/* After xchg pointer to bpf_cpu_map_entry, use the call_rcu() to + * ensure any driver rcu critical sections have completed, but this + * does not guarantee a flush has happened yet. Because driver side + * rcu_read_lock/unlock only protects the running XDP program. The + * atomic xchg and NULL-ptr check in __cpu_map_flush() makes sure a + * pending flush op doesn't fail. + * + * The bpf_cpu_map_entry is still used by the kthread, and there can + * still be pending packets (in queue and percpu bulkq). A refcnt + * makes sure to last user (kthread_stop vs. call_rcu) free memory + * resources. + * + * The rcu callback __cpu_map_entry_free flush remaining packets in + * percpu bulkq to queue. Due to caller map_delete_elem() disable + * preemption, cannot call kthread_stop() to make sure queue is empty. + * Instead a work_queue is started for stopping kthread, + * cpu_map_kthread_stop, which waits for an RCU graze period before + * stopping kthread, emptying the queue. + */ +void __cpu_map_entry_replace(struct bpf_cpu_map *cmap, + u32 key_cpu, struct bpf_cpu_map_entry *rcpu) +{ + struct bpf_cpu_map_entry *old_rcpu; + + old_rcpu = xchg(&cmap->cpu_map[key_cpu], rcpu); + if (old_rcpu) { + call_rcu(&old_rcpu->rcu, __cpu_map_entry_free); + INIT_WORK(&old_rcpu->kthread_stop_wq, cpu_map_kthread_stop); + schedule_work(&old_rcpu->kthread_stop_wq); + } +} + +int cpu_map_delete_elem(struct bpf_map *map, void *key) +{ + struct bpf_cpu_map *cmap = container_of(map, struct bpf_cpu_map, map); + u32 key_cpu = *(u32 *)key; + + if (key_cpu >= map->max_entries) + return -EINVAL; + + /* notice caller map_delete_elem() use preempt_disable() */ + __cpu_map_entry_replace(cmap, key_cpu, NULL); + return 0; +} + +int cpu_map_update_elem(struct bpf_map *map, void *key, void *value, + u64 map_flags) +{ + struct bpf_cpu_map *cmap = container_of(map, struct bpf_cpu_map, map); + struct bpf_cpu_map_entry *rcpu; + + /* Array index key correspond to CPU number */ + u32 key_cpu = *(u32 *)key; + /* Value is the queue size */ + u32 qsize = *(u32 *)value; + + if (unlikely(map_flags > BPF_EXIST)) + return -EINVAL; + if (unlikely(key_cpu >= cmap->map.max_entries)) + return -E2BIG; + if (unlikely(map_flags == BPF_NOEXIST)) + return -EEXIST; + if (unlikely(qsize > 16384)) /* sanity limit on qsize */ + return -EOVERFLOW; + + /* Make sure CPU is a valid possible cpu */ + if (!cpu_possible(key_cpu)) + return -ENODEV; + + if (qsize == 0) { + rcpu = NULL; /* Same as deleting */ + } else { + /* Updating qsize cause re-allocation of bpf_cpu_map_entry */ + rcpu = __cpu_map_entry_alloc(qsize, key_cpu, map->id); + if (!rcpu) + return -ENOMEM; + } + rcu_read_lock(); + __cpu_map_entry_replace(cmap, key_cpu, rcpu); + rcu_read_unlock(); + return 0; +} + +void cpu_map_free(struct bpf_map *map) +{ + struct bpf_cpu_map *cmap = container_of(map, struct bpf_cpu_map, map); + int cpu; + u32 i; + + /* At this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, + * so the bpf programs (can be more than one that used this map) were + * disconnected from events. Wait for outstanding critical sections in + * these programs to complete. The rcu critical section only guarantees + * no further "XDP/bpf-side" reads against bpf_cpu_map->cpu_map. + * It does __not__ ensure pending flush operations (if any) are + * complete. + */ + synchronize_rcu(); + + /* To ensure all pending flush operations have completed wait for flush + * bitmap to indicate all flush_needed bits to be zero on _all_ cpus. + * Because the above synchronize_rcu() ensures the map is disconnected + * from the program we can assume no new bits will be set. + */ + for_each_online_cpu(cpu) { + unsigned long *bitmap = per_cpu_ptr(cmap->flush_needed, cpu); + + while (!bitmap_empty(bitmap, cmap->map.max_entries)) + cond_resched(); + } + + /* For cpu_map the remote CPUs can still be using the entries + * (struct bpf_cpu_map_entry). + */ + for (i = 0; i < cmap->map.max_entries; i++) { + struct bpf_cpu_map_entry *rcpu; + + rcpu = READ_ONCE(cmap->cpu_map[i]); + if (!rcpu) + continue; + + /* bq flush and cleanup happens after RCU graze-period */ + __cpu_map_entry_replace(cmap, i, NULL); /* call_rcu */ + } + free_percpu(cmap->flush_needed); + bpf_map_area_free(cmap->cpu_map); + kfree(cmap); +} + +struct bpf_cpu_map_entry *__cpu_map_lookup_elem(struct bpf_map *map, u32 key) +{ + struct bpf_cpu_map *cmap = container_of(map, struct bpf_cpu_map, map); + struct bpf_cpu_map_entry *rcpu; + + if (key >= map->max_entries) + return NULL; + + rcpu = READ_ONCE(cmap->cpu_map[key]); + return rcpu; +} + +static void *cpu_map_lookup_elem(struct bpf_map *map, void *key) +{ + struct bpf_cpu_map_entry *rcpu = + __cpu_map_lookup_elem(map, *(u32 *)key); + + return rcpu ? &rcpu->qsize : NULL; +} + +static int cpu_map_get_next_key(struct bpf_map *map, void *key, void *next_key) +{ + struct bpf_cpu_map *cmap = container_of(map, struct bpf_cpu_map, map); + u32 index = key ? *(u32 *)key : U32_MAX; + u32 *next = next_key; + + if (index >= cmap->map.max_entries) { + *next = 0; + return 0; + } + + if (index == cmap->map.max_entries - 1) + return -ENOENT; + *next = index + 1; + return 0; +} + +const struct bpf_map_ops cpu_map_ops = { + .map_alloc = cpu_map_alloc, + .map_free = cpu_map_free, + .map_delete_elem = cpu_map_delete_elem, + .map_update_elem = cpu_map_update_elem, + .map_lookup_elem = cpu_map_lookup_elem, + .map_get_next_key = cpu_map_get_next_key, +}; + +static int bq_flush_to_queue(struct bpf_cpu_map_entry *rcpu, + struct xdp_bulk_queue *bq) +{ + struct ptr_ring *q; + int i; + + if (unlikely(!bq->count)) + return 0; + + q = rcpu->queue; + spin_lock(&q->producer_lock); + + for (i = 0; i < bq->count; i++) { + void *xdp_pkt = bq->q[i]; + int err; + + err = __ptr_ring_produce(q, xdp_pkt); + if (err) { + /* Free xdp_pkt */ + page_frag_free(xdp_pkt); + } + } + bq->count = 0; + spin_unlock(&q->producer_lock); + + return 0; +} + +/* Notice: Will change in later patch */ +struct xdp_pkt { + void *data; + u16 len; + u16 headroom; +}; + +/* Runs under RCU-read-side, plus in softirq under NAPI protection. + * Thus, safe percpu variable access. + */ +int bq_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_pkt *xdp_pkt) +{ + struct xdp_bulk_queue *bq = this_cpu_ptr(rcpu->bulkq); + + if (unlikely(bq->count == CPU_MAP_BULK_SIZE)) + bq_flush_to_queue(rcpu, bq); + + /* Notice, xdp_buff/page MUST be queued here, long enough for + * driver to code invoking us to finished, due to driver + * (e.g. ixgbe) recycle tricks based on page-refcnt. + * + * Thus, incoming xdp_pkt is always queued here (else we race + * with another CPU on page-refcnt and remaining driver code). + * Queue time is very short, as driver will invoke flush + * operation, when completing napi->poll call. + */ + bq->q[bq->count++] = xdp_pkt; + return 0; +} + +void __cpu_map_insert_ctx(struct bpf_map *map, u32 bit) +{ + struct bpf_cpu_map *cmap = container_of(map, struct bpf_cpu_map, map); + unsigned long *bitmap = this_cpu_ptr(cmap->flush_needed); + + __set_bit(bit, bitmap); +} + +void __cpu_map_flush(struct bpf_map *map) +{ + struct bpf_cpu_map *cmap = container_of(map, struct bpf_cpu_map, map); + unsigned long *bitmap = this_cpu_ptr(cmap->flush_needed); + u32 bit; + + /* The napi->poll softirq makes sure __cpu_map_insert_ctx() + * and __cpu_map_flush() happen on same CPU. Thus, the percpu + * bitmap indicate which percpu bulkq have packets. + */ + for_each_set_bit(bit, bitmap, map->max_entries) { + struct bpf_cpu_map_entry *rcpu = READ_ONCE(cmap->cpu_map[bit]); + struct xdp_bulk_queue *bq; + + /* This is possible if entry is removed by user space + * between xdp redirect and flush op. + */ + if (unlikely(!rcpu)) + continue; + + __clear_bit(bit, bitmap); + + /* Flush all frames in bulkq to real queue */ + bq = this_cpu_ptr(rcpu->bulkq); + bq_flush_to_queue(rcpu, bq); + + /* If already running, costs spin_lock_irqsave + smb_mb */ + wake_up_process(rcpu->kthread); + } +} diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index d124e702e040..54fba06942f5 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -592,6 +592,12 @@ static int map_update_elem(union bpf_attr *attr) if (copy_from_user(value, uvalue, value_size) != 0) goto free_value; + /* Need to create a kthread, thus must support schedule */ + if (map->map_type == BPF_MAP_TYPE_CPUMAP) { + err = map->ops->map_update_elem(map, key, value, attr->flags); + goto out; + } + /* must increment bpf_prog_active to avoid kprobe+bpf triggering from * inside bpf map update or delete otherwise deadlocks are possible */ @@ -622,7 +628,7 @@ static int map_update_elem(union bpf_attr *attr) } __this_cpu_dec(bpf_prog_active); preempt_enable(); - +out: if (!err) trace_bpf_map_update_elem(map, ufd, key, value); free_value: diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 9755279d94cb..cefa64be9a2f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1444,6 +1444,11 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, if (func_id != BPF_FUNC_redirect_map) goto error; break; + /* Restrict bpf side of cpumap, open when use-cases appear */ + case BPF_MAP_TYPE_CPUMAP: + if (func_id != BPF_FUNC_redirect_map) + goto error; + break; case BPF_MAP_TYPE_ARRAY_OF_MAPS: case BPF_MAP_TYPE_HASH_OF_MAPS: if (func_id != BPF_FUNC_map_lookup_elem) diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index fb4fb81ce5b0..fa93033dc521 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -112,6 +112,7 @@ enum bpf_map_type { BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_DEVMAP, BPF_MAP_TYPE_SOCKMAP, + BPF_MAP_TYPE_CPUMAP, }; enum bpf_prog_type { -- cgit v1.2.3-59-g8ed1b From 9c270af37bb62e708e3e4415d653ce73e713df02 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 16 Oct 2017 12:19:34 +0200 Subject: bpf: XDP_REDIRECT enable use of cpumap This patch connects cpumap to the xdp_do_redirect_map infrastructure. Still no SKB allocation are done yet. The XDP frames are transferred to the other CPU, but they are simply refcnt decremented on the remote CPU. This served as a good benchmark for measuring the overhead of remote refcnt decrement. If driver page recycle cache is not efficient then this, exposes a bottleneck in the page allocator. A shout-out to MST's ptr_ring, which is the secret behind is being so efficient to transfer memory pointers between CPUs, without constantly bouncing cache-lines between CPUs. V3: Handle !CONFIG_BPF_SYSCALL pointed out by kbuild test robot. V4: Make Generic-XDP aware of cpumap type, but don't allow redirect yet, as implementation require a separate upstream discussion. V5: - Fix a maybe-uninitialized pointed out by kbuild test robot. - Restrict bpf-prog side access to cpumap, open when use-cases appear - Implement cpu_map_enqueue() as a more simple void pointer enqueue V6: - Allow cpumap type for usage in helper bpf_redirect_map, general bpf-prog side restriction moved to earlier patch. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/linux/bpf.h | 31 +++++++++- include/trace/events/xdp.h | 10 +++- kernel/bpf/cpumap.c | 22 ++++++- kernel/bpf/verifier.c | 3 +- net/core/filter.c | 140 +++++++++++++++++++++++++++++++++++---------- 5 files changed, 172 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 4373125de1f3..6d4dd844828a 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -355,6 +355,13 @@ struct net_device *__dev_map_lookup_elem(struct bpf_map *map, u32 key); void __dev_map_insert_ctx(struct bpf_map *map, u32 index); void __dev_map_flush(struct bpf_map *map); +struct bpf_cpu_map_entry *__cpu_map_lookup_elem(struct bpf_map *map, u32 key); +void __cpu_map_insert_ctx(struct bpf_map *map, u32 index); +void __cpu_map_flush(struct bpf_map *map); +struct xdp_buff; +int cpu_map_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_buff *xdp, + struct net_device *dev_rx); + /* Return map's numa specified by userspace */ static inline int bpf_map_attr_numa_node(const union bpf_attr *attr) { @@ -362,7 +369,7 @@ static inline int bpf_map_attr_numa_node(const union bpf_attr *attr) attr->numa_node : NUMA_NO_NODE; } -#else +#else /* !CONFIG_BPF_SYSCALL */ static inline struct bpf_prog *bpf_prog_get(u32 ufd) { return ERR_PTR(-EOPNOTSUPP); @@ -425,6 +432,28 @@ static inline void __dev_map_insert_ctx(struct bpf_map *map, u32 index) static inline void __dev_map_flush(struct bpf_map *map) { } + +static inline +struct bpf_cpu_map_entry *__cpu_map_lookup_elem(struct bpf_map *map, u32 key) +{ + return NULL; +} + +static inline void __cpu_map_insert_ctx(struct bpf_map *map, u32 index) +{ +} + +static inline void __cpu_map_flush(struct bpf_map *map) +{ +} + +struct xdp_buff; +static inline int cpu_map_enqueue(struct bpf_cpu_map_entry *rcpu, + struct xdp_buff *xdp, + struct net_device *dev_rx) +{ + return 0; +} #endif /* CONFIG_BPF_SYSCALL */ #if defined(CONFIG_STREAM_PARSER) && defined(CONFIG_BPF_SYSCALL) diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h index 4e16c43fba10..eb2ece96c1a2 100644 --- a/include/trace/events/xdp.h +++ b/include/trace/events/xdp.h @@ -136,12 +136,18 @@ DEFINE_EVENT_PRINT(xdp_redirect_template, xdp_redirect_map_err, __entry->map_id, __entry->map_index) ); +#define devmap_ifindex(fwd, map) \ + (!fwd ? 0 : \ + (!map ? 0 : \ + ((map->map_type == BPF_MAP_TYPE_DEVMAP) ? \ + ((struct net_device *)fwd)->ifindex : 0))) + #define _trace_xdp_redirect_map(dev, xdp, fwd, map, idx) \ - trace_xdp_redirect_map(dev, xdp, fwd ? fwd->ifindex : 0, \ + trace_xdp_redirect_map(dev, xdp, devmap_ifindex(fwd, map), \ 0, map, idx) #define _trace_xdp_redirect_map_err(dev, xdp, fwd, map, idx, err) \ - trace_xdp_redirect_map_err(dev, xdp, fwd ? fwd->ifindex : 0, \ + trace_xdp_redirect_map_err(dev, xdp, devmap_ifindex(fwd, map), \ err, map, idx) #endif /* _TRACE_XDP_H */ diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index e1e25ddba038..768da6a2c265 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -500,7 +500,7 @@ struct xdp_pkt { /* Runs under RCU-read-side, plus in softirq under NAPI protection. * Thus, safe percpu variable access. */ -int bq_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_pkt *xdp_pkt) +static int bq_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_pkt *xdp_pkt) { struct xdp_bulk_queue *bq = this_cpu_ptr(rcpu->bulkq); @@ -520,6 +520,26 @@ int bq_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_pkt *xdp_pkt) return 0; } +int cpu_map_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_buff *xdp, + struct net_device *dev_rx) +{ + struct xdp_pkt *xdp_pkt; + int headroom; + + /* For now this is just used as a void pointer to data_hard_start. + * Followup patch will generalize this. + */ + xdp_pkt = xdp->data_hard_start; + + /* Fake writing into xdp_pkt->data to measure overhead */ + headroom = xdp->data - xdp->data_hard_start; + if (headroom < sizeof(*xdp_pkt)) + xdp_pkt->data = xdp->data; + + bq_enqueue(rcpu, xdp_pkt); + return 0; +} + void __cpu_map_insert_ctx(struct bpf_map *map, u32 bit) { struct bpf_cpu_map *cmap = container_of(map, struct bpf_cpu_map, map); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index cefa64be9a2f..e4d5136725a2 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1486,7 +1486,8 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env, goto error; break; case BPF_FUNC_redirect_map: - if (map->map_type != BPF_MAP_TYPE_DEVMAP) + if (map->map_type != BPF_MAP_TYPE_DEVMAP && + map->map_type != BPF_MAP_TYPE_CPUMAP) goto error; break; case BPF_FUNC_sk_redirect_map: diff --git a/net/core/filter.c b/net/core/filter.c index 140fa9f9c0f4..4d88e0665c41 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2526,10 +2526,36 @@ static int __bpf_tx_xdp(struct net_device *dev, err = dev->netdev_ops->ndo_xdp_xmit(dev, xdp); if (err) return err; - if (map) + dev->netdev_ops->ndo_xdp_flush(dev); + return 0; +} + +static int __bpf_tx_xdp_map(struct net_device *dev_rx, void *fwd, + struct bpf_map *map, + struct xdp_buff *xdp, + u32 index) +{ + int err; + + if (map->map_type == BPF_MAP_TYPE_DEVMAP) { + struct net_device *dev = fwd; + + if (!dev->netdev_ops->ndo_xdp_xmit) + return -EOPNOTSUPP; + + err = dev->netdev_ops->ndo_xdp_xmit(dev, xdp); + if (err) + return err; __dev_map_insert_ctx(map, index); - else - dev->netdev_ops->ndo_xdp_flush(dev); + + } else if (map->map_type == BPF_MAP_TYPE_CPUMAP) { + struct bpf_cpu_map_entry *rcpu = fwd; + + err = cpu_map_enqueue(rcpu, xdp, dev_rx); + if (err) + return err; + __cpu_map_insert_ctx(map, index); + } return 0; } @@ -2539,11 +2565,33 @@ void xdp_do_flush_map(void) struct bpf_map *map = ri->map_to_flush; ri->map_to_flush = NULL; - if (map) - __dev_map_flush(map); + if (map) { + switch (map->map_type) { + case BPF_MAP_TYPE_DEVMAP: + __dev_map_flush(map); + break; + case BPF_MAP_TYPE_CPUMAP: + __cpu_map_flush(map); + break; + default: + break; + } + } } EXPORT_SYMBOL_GPL(xdp_do_flush_map); +static void *__xdp_map_lookup_elem(struct bpf_map *map, u32 index) +{ + switch (map->map_type) { + case BPF_MAP_TYPE_DEVMAP: + return __dev_map_lookup_elem(map, index); + case BPF_MAP_TYPE_CPUMAP: + return __cpu_map_lookup_elem(map, index); + default: + return NULL; + } +} + static inline bool xdp_map_invalid(const struct bpf_prog *xdp_prog, unsigned long aux) { @@ -2556,8 +2604,8 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, struct redirect_info *ri = this_cpu_ptr(&redirect_info); unsigned long map_owner = ri->map_owner; struct bpf_map *map = ri->map; - struct net_device *fwd = NULL; u32 index = ri->ifindex; + void *fwd = NULL; int err; ri->ifindex = 0; @@ -2570,7 +2618,7 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, goto err; } - fwd = __dev_map_lookup_elem(map, index); + fwd = __xdp_map_lookup_elem(map, index); if (!fwd) { err = -EINVAL; goto err; @@ -2578,7 +2626,7 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, if (ri->map_to_flush && ri->map_to_flush != map) xdp_do_flush_map(); - err = __bpf_tx_xdp(fwd, map, xdp, index); + err = __bpf_tx_xdp_map(dev, fwd, map, xdp, index); if (unlikely(err)) goto err; @@ -2620,54 +2668,88 @@ err: } EXPORT_SYMBOL_GPL(xdp_do_redirect); -int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb, - struct bpf_prog *xdp_prog) +static int __xdp_generic_ok_fwd_dev(struct sk_buff *skb, struct net_device *fwd) +{ + unsigned int len; + + if (unlikely(!(fwd->flags & IFF_UP))) + return -ENETDOWN; + + len = fwd->mtu + fwd->hard_header_len + VLAN_HLEN; + if (skb->len > len) + return -EMSGSIZE; + + return 0; +} + +int xdp_do_generic_redirect_map(struct net_device *dev, struct sk_buff *skb, + struct bpf_prog *xdp_prog) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); unsigned long map_owner = ri->map_owner; struct bpf_map *map = ri->map; struct net_device *fwd = NULL; u32 index = ri->ifindex; - unsigned int len; int err = 0; ri->ifindex = 0; ri->map = NULL; ri->map_owner = 0; - if (map) { - if (unlikely(xdp_map_invalid(xdp_prog, map_owner))) { - err = -EFAULT; - map = NULL; - goto err; - } - fwd = __dev_map_lookup_elem(map, index); - } else { - fwd = dev_get_by_index_rcu(dev_net(dev), index); + if (unlikely(xdp_map_invalid(xdp_prog, map_owner))) { + err = -EFAULT; + map = NULL; + goto err; } + fwd = __xdp_map_lookup_elem(map, index); if (unlikely(!fwd)) { err = -EINVAL; goto err; } - if (unlikely(!(fwd->flags & IFF_UP))) { - err = -ENETDOWN; + if (map->map_type == BPF_MAP_TYPE_DEVMAP) { + if (unlikely((err = __xdp_generic_ok_fwd_dev(skb, fwd)))) + goto err; + skb->dev = fwd; + } else { + /* TODO: Handle BPF_MAP_TYPE_CPUMAP */ + err = -EBADRQC; goto err; } - len = fwd->mtu + fwd->hard_header_len + VLAN_HLEN; - if (skb->len > len) { - err = -EMSGSIZE; + _trace_xdp_redirect_map(dev, xdp_prog, fwd, map, index); + return 0; +err: + _trace_xdp_redirect_map_err(dev, xdp_prog, fwd, map, index, err); + return err; +} + +int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb, + struct bpf_prog *xdp_prog) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + u32 index = ri->ifindex; + struct net_device *fwd; + int err = 0; + + if (ri->map) + return xdp_do_generic_redirect_map(dev, skb, xdp_prog); + + ri->ifindex = 0; + fwd = dev_get_by_index_rcu(dev_net(dev), index); + if (unlikely(!fwd)) { + err = -EINVAL; goto err; } + if (unlikely((err = __xdp_generic_ok_fwd_dev(skb, fwd)))) + goto err; + skb->dev = fwd; - map ? _trace_xdp_redirect_map(dev, xdp_prog, fwd, map, index) - : _trace_xdp_redirect(dev, xdp_prog, index); + _trace_xdp_redirect(dev, xdp_prog, index); return 0; err: - map ? _trace_xdp_redirect_map_err(dev, xdp_prog, fwd, map, index, err) - : _trace_xdp_redirect_err(dev, xdp_prog, index, err); + _trace_xdp_redirect_err(dev, xdp_prog, index, err); return err; } EXPORT_SYMBOL_GPL(xdp_do_generic_redirect); -- cgit v1.2.3-59-g8ed1b From 1c601d829ab0d7ac3ac44853f83db2206afe67fc Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 16 Oct 2017 12:19:39 +0200 Subject: bpf: cpumap xdp_buff to skb conversion and allocation This patch makes cpumap functional, by adding SKB allocation and invoking the network stack on the dequeuing CPU. For constructing the SKB on the remote CPU, the xdp_buff in converted into a struct xdp_pkt, and it mapped into the top headroom of the packet, to avoid allocating separate mem. For now, struct xdp_pkt is just a cpumap internal data structure, with info carried between enqueue to dequeue. If a driver doesn't have enough headroom it is simply dropped, with return code -EOVERFLOW. This will be picked up the xdp tracepoint infrastructure, to allow users to catch this. V2: take into account xdp->data_meta V4: - Drop busypoll tricks, keeping it more simple. - Skip RPS and Generic-XDP-recursive-reinjection, suggested by Alexei V5: correct RCU read protection around __netif_receive_skb_core. V6: Setting TASK_RUNNING vs TASK_INTERRUPTIBLE based on talk with Rik van Riel Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + kernel/bpf/cpumap.c | 152 +++++++++++++++++++++++++++++++++++++++------- net/core/dev.c | 27 ++++++++ 3 files changed, 158 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 31bb3010c69b..bf014afcb914 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3260,6 +3260,7 @@ int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb); int netif_rx(struct sk_buff *skb); int netif_rx_ni(struct sk_buff *skb); int netif_receive_skb(struct sk_buff *skb); +int netif_receive_skb_core(struct sk_buff *skb); gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb); void napi_gro_flush(struct napi_struct *napi, bool flush_old); struct sk_buff *napi_get_frags(struct napi_struct *napi); diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index 768da6a2c265..ee7adf4352dd 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -25,6 +25,9 @@ #include #include +#include /* netif_receive_skb_core */ +#include /* eth_type_trans */ + /* General idea: XDP packets getting XDP redirected to another CPU, * will maximum be stored/queued for one driver ->poll() call. It is * guaranteed that setting flush bit and flush operation happen on @@ -179,6 +182,92 @@ static void cpu_map_kthread_stop(struct work_struct *work) kthread_stop(rcpu->kthread); } +/* For now, xdp_pkt is a cpumap internal data structure, with info + * carried between enqueue to dequeue. It is mapped into the top + * headroom of the packet, to avoid allocating separate mem. + */ +struct xdp_pkt { + void *data; + u16 len; + u16 headroom; + u16 metasize; + struct net_device *dev_rx; +}; + +/* Convert xdp_buff to xdp_pkt */ +static struct xdp_pkt *convert_to_xdp_pkt(struct xdp_buff *xdp) +{ + struct xdp_pkt *xdp_pkt; + int metasize; + int headroom; + + /* Assure headroom is available for storing info */ + headroom = xdp->data - xdp->data_hard_start; + metasize = xdp->data - xdp->data_meta; + metasize = metasize > 0 ? metasize : 0; + if ((headroom - metasize) < sizeof(*xdp_pkt)) + return NULL; + + /* Store info in top of packet */ + xdp_pkt = xdp->data_hard_start; + + xdp_pkt->data = xdp->data; + xdp_pkt->len = xdp->data_end - xdp->data; + xdp_pkt->headroom = headroom - sizeof(*xdp_pkt); + xdp_pkt->metasize = metasize; + + return xdp_pkt; +} + +struct sk_buff *cpu_map_build_skb(struct bpf_cpu_map_entry *rcpu, + struct xdp_pkt *xdp_pkt) +{ + unsigned int frame_size; + void *pkt_data_start; + struct sk_buff *skb; + + /* build_skb need to place skb_shared_info after SKB end, and + * also want to know the memory "truesize". Thus, need to + * know the memory frame size backing xdp_buff. + * + * XDP was designed to have PAGE_SIZE frames, but this + * assumption is not longer true with ixgbe and i40e. It + * would be preferred to set frame_size to 2048 or 4096 + * depending on the driver. + * frame_size = 2048; + * frame_len = frame_size - sizeof(*xdp_pkt); + * + * Instead, with info avail, skb_shared_info in placed after + * packet len. This, unfortunately fakes the truesize. + * Another disadvantage of this approach, the skb_shared_info + * is not at a fixed memory location, with mixed length + * packets, which is bad for cache-line hotness. + */ + frame_size = SKB_DATA_ALIGN(xdp_pkt->len) + xdp_pkt->headroom + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + pkt_data_start = xdp_pkt->data - xdp_pkt->headroom; + skb = build_skb(pkt_data_start, frame_size); + if (!skb) + return NULL; + + skb_reserve(skb, xdp_pkt->headroom); + __skb_put(skb, xdp_pkt->len); + if (xdp_pkt->metasize) + skb_metadata_set(skb, xdp_pkt->metasize); + + /* Essential SKB info: protocol and skb->dev */ + skb->protocol = eth_type_trans(skb, xdp_pkt->dev_rx); + + /* Optional SKB info, currently missing: + * - HW checksum info (skb->ip_summed) + * - HW RX hash (skb_set_hash) + * - RX ring dev queue index (skb_record_rx_queue) + */ + + return skb; +} + static int cpu_map_kthread_run(void *data) { struct bpf_cpu_map_entry *rcpu = data; @@ -191,15 +280,45 @@ static int cpu_map_kthread_run(void *data) * kthread_stop signal until queue is empty. */ while (!kthread_should_stop() || !__ptr_ring_empty(rcpu->queue)) { + unsigned int processed = 0, drops = 0; struct xdp_pkt *xdp_pkt; - schedule(); - /* Do work */ - while ((xdp_pkt = ptr_ring_consume(rcpu->queue))) { - /* For now just "refcnt-free" */ - page_frag_free(xdp_pkt); + /* Release CPU reschedule checks */ + if (__ptr_ring_empty(rcpu->queue)) { + __set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } else { + cond_resched(); + } + __set_current_state(TASK_RUNNING); + + /* Process packets in rcpu->queue */ + local_bh_disable(); + /* + * The bpf_cpu_map_entry is single consumer, with this + * kthread CPU pinned. Lockless access to ptr_ring + * consume side valid as no-resize allowed of queue. + */ + while ((xdp_pkt = __ptr_ring_consume(rcpu->queue))) { + struct sk_buff *skb; + int ret; + + skb = cpu_map_build_skb(rcpu, xdp_pkt); + if (!skb) { + page_frag_free(xdp_pkt); + continue; + } + + /* Inject into network stack */ + ret = netif_receive_skb_core(skb); + if (ret == NET_RX_DROP) + drops++; + + /* Limit BH-disable period */ + if (++processed == 8) + break; } - __set_current_state(TASK_INTERRUPTIBLE); + local_bh_enable(); /* resched point, may call do_softirq() */ } __set_current_state(TASK_RUNNING); @@ -490,13 +609,6 @@ static int bq_flush_to_queue(struct bpf_cpu_map_entry *rcpu, return 0; } -/* Notice: Will change in later patch */ -struct xdp_pkt { - void *data; - u16 len; - u16 headroom; -}; - /* Runs under RCU-read-side, plus in softirq under NAPI protection. * Thus, safe percpu variable access. */ @@ -524,17 +636,13 @@ int cpu_map_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_buff *xdp, struct net_device *dev_rx) { struct xdp_pkt *xdp_pkt; - int headroom; - /* For now this is just used as a void pointer to data_hard_start. - * Followup patch will generalize this. - */ - xdp_pkt = xdp->data_hard_start; + xdp_pkt = convert_to_xdp_pkt(xdp); + if (!xdp_pkt) + return -EOVERFLOW; - /* Fake writing into xdp_pkt->data to measure overhead */ - headroom = xdp->data - xdp->data_hard_start; - if (headroom < sizeof(*xdp_pkt)) - xdp_pkt->data = xdp->data; + /* Info needed when constructing SKB on remote CPU */ + xdp_pkt->dev_rx = dev_rx; bq_enqueue(rcpu, xdp_pkt); return 0; diff --git a/net/core/dev.c b/net/core/dev.c index d2b20e73080e..cf5894f0e6eb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4492,6 +4492,33 @@ out: return ret; } +/** + * netif_receive_skb_core - special purpose version of netif_receive_skb + * @skb: buffer to process + * + * More direct receive version of netif_receive_skb(). It should + * only be used by callers that have a need to skip RPS and Generic XDP. + * Caller must also take care of handling if (page_is_)pfmemalloc. + * + * This function may only be called from softirq context and interrupts + * should be enabled. + * + * Return values (usually ignored): + * NET_RX_SUCCESS: no congestion + * NET_RX_DROP: packet was dropped + */ +int netif_receive_skb_core(struct sk_buff *skb) +{ + int ret; + + rcu_read_lock(); + ret = __netif_receive_skb_core(skb, false); + rcu_read_unlock(); + + return ret; +} +EXPORT_SYMBOL(netif_receive_skb_core); + static int __netif_receive_skb(struct sk_buff *skb) { int ret; -- cgit v1.2.3-59-g8ed1b From f9419f7bd7a5318b636a941a0214c5cdfa6f6530 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Mon, 16 Oct 2017 12:19:44 +0200 Subject: bpf: cpumap add tracepoints This adds two tracepoint to the cpumap. One for the enqueue side trace_xdp_cpumap_enqueue() and one for the kthread dequeue side trace_xdp_cpumap_kthread(). To mitigate the tracepoint overhead, these are invoked during the enqueue/dequeue bulking phases, thus amortizing the cost. The obvious use-cases are for debugging and monitoring. The non-intuitive use-case is using these as a feedback loop to know the system load. One can imagine auto-scaling by reducing, adding or activating more worker CPUs on demand. V4: tracepoint remove time_limit info, instead add sched info V8: intro struct bpf_cpu_map_entry members cpu+map_id in this patch Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/trace/events/xdp.h | 70 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/cpumap.c | 24 ++++++++++++---- 2 files changed, 89 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h index eb2ece96c1a2..0c8dec61987e 100644 --- a/include/trace/events/xdp.h +++ b/include/trace/events/xdp.h @@ -150,6 +150,76 @@ DEFINE_EVENT_PRINT(xdp_redirect_template, xdp_redirect_map_err, trace_xdp_redirect_map_err(dev, xdp, devmap_ifindex(fwd, map), \ err, map, idx) +TRACE_EVENT(xdp_cpumap_kthread, + + TP_PROTO(int map_id, unsigned int processed, unsigned int drops, + int sched), + + TP_ARGS(map_id, processed, drops, sched), + + TP_STRUCT__entry( + __field(int, map_id) + __field(u32, act) + __field(int, cpu) + __field(unsigned int, drops) + __field(unsigned int, processed) + __field(int, sched) + ), + + TP_fast_assign( + __entry->map_id = map_id; + __entry->act = XDP_REDIRECT; + __entry->cpu = smp_processor_id(); + __entry->drops = drops; + __entry->processed = processed; + __entry->sched = sched; + ), + + TP_printk("kthread" + " cpu=%d map_id=%d action=%s" + " processed=%u drops=%u" + " sched=%d", + __entry->cpu, __entry->map_id, + __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB), + __entry->processed, __entry->drops, + __entry->sched) +); + +TRACE_EVENT(xdp_cpumap_enqueue, + + TP_PROTO(int map_id, unsigned int processed, unsigned int drops, + int to_cpu), + + TP_ARGS(map_id, processed, drops, to_cpu), + + TP_STRUCT__entry( + __field(int, map_id) + __field(u32, act) + __field(int, cpu) + __field(unsigned int, drops) + __field(unsigned int, processed) + __field(int, to_cpu) + ), + + TP_fast_assign( + __entry->map_id = map_id; + __entry->act = XDP_REDIRECT; + __entry->cpu = smp_processor_id(); + __entry->drops = drops; + __entry->processed = processed; + __entry->to_cpu = to_cpu; + ), + + TP_printk("enqueue" + " cpu=%d map_id=%d action=%s" + " processed=%u drops=%u" + " to_cpu=%d", + __entry->cpu, __entry->map_id, + __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB), + __entry->processed, __entry->drops, + __entry->to_cpu) +); + #endif /* _TRACE_XDP_H */ #include diff --git a/kernel/bpf/cpumap.c b/kernel/bpf/cpumap.c index ee7adf4352dd..b4358d84ddf1 100644 --- a/kernel/bpf/cpumap.c +++ b/kernel/bpf/cpumap.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /* netif_receive_skb_core */ #include /* eth_type_trans */ @@ -43,6 +44,8 @@ struct xdp_bulk_queue { /* Struct for every remote "destination" CPU in map */ struct bpf_cpu_map_entry { + u32 cpu; /* kthread CPU and map index */ + int map_id; /* Back reference to map */ u32 qsize; /* Queue size placeholder for map lookup */ /* XDP can run multiple RX-ring queues, need __percpu enqueue store */ @@ -280,15 +283,16 @@ static int cpu_map_kthread_run(void *data) * kthread_stop signal until queue is empty. */ while (!kthread_should_stop() || !__ptr_ring_empty(rcpu->queue)) { - unsigned int processed = 0, drops = 0; + unsigned int processed = 0, drops = 0, sched = 0; struct xdp_pkt *xdp_pkt; /* Release CPU reschedule checks */ if (__ptr_ring_empty(rcpu->queue)) { __set_current_state(TASK_INTERRUPTIBLE); schedule(); + sched = 1; } else { - cond_resched(); + sched = cond_resched(); } __set_current_state(TASK_RUNNING); @@ -318,6 +322,9 @@ static int cpu_map_kthread_run(void *data) if (++processed == 8) break; } + /* Feedback loop via tracepoint */ + trace_xdp_cpumap_kthread(rcpu->map_id, processed, drops, sched); + local_bh_enable(); /* resched point, may call do_softirq() */ } __set_current_state(TASK_RUNNING); @@ -354,7 +361,9 @@ struct bpf_cpu_map_entry *__cpu_map_entry_alloc(u32 qsize, u32 cpu, int map_id) if (err) goto free_queue; - rcpu->qsize = qsize; + rcpu->cpu = cpu; + rcpu->map_id = map_id; + rcpu->qsize = qsize; /* Setup kthread */ rcpu->kthread = kthread_create_on_node(cpu_map_kthread_run, rcpu, numa, @@ -584,6 +593,8 @@ const struct bpf_map_ops cpu_map_ops = { static int bq_flush_to_queue(struct bpf_cpu_map_entry *rcpu, struct xdp_bulk_queue *bq) { + unsigned int processed = 0, drops = 0; + const int to_cpu = rcpu->cpu; struct ptr_ring *q; int i; @@ -599,13 +610,16 @@ static int bq_flush_to_queue(struct bpf_cpu_map_entry *rcpu, err = __ptr_ring_produce(q, xdp_pkt); if (err) { - /* Free xdp_pkt */ - page_frag_free(xdp_pkt); + drops++; + page_frag_free(xdp_pkt); /* Free xdp_pkt */ } + processed++; } bq->count = 0; spin_unlock(&q->producer_lock); + /* Feedback loop via tracepoints */ + trace_xdp_cpumap_enqueue(rcpu->map_id, processed, drops, to_cpu); return 0; } -- cgit v1.2.3-59-g8ed1b From f8b8b1cd5aadd221742b45eb0ee3c8a80abf036a Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 16 Oct 2017 11:12:18 -0400 Subject: net: dsa: split dsa_port's netdev member The dsa_port structure has a "netdev" member, which can be used for either the master device, or the slave device, depending on its type. It is true that today, CPU port are not exposed to userspace, thus the port's netdev member can be used to point to its master interface. But it is still slightly confusing, so split it into more explicit "master" and "slave" members inside an anonymous union. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 6 +++--- drivers/net/dsa/mt7530.c | 2 +- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- include/net/dsa.h | 9 ++++++++- net/dsa/dsa.c | 6 +++--- net/dsa/dsa2.c | 20 ++++++++++---------- net/dsa/dsa_priv.h | 4 ++-- net/dsa/legacy.c | 14 +++++++------- net/dsa/slave.c | 6 +++--- 9 files changed, 38 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 32025b990437..b43c063b9634 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -601,7 +601,7 @@ static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, * state machine and make it go in PHY_FORCING state instead. */ if (!status->link) - netif_carrier_off(ds->ports[port].netdev); + netif_carrier_off(ds->ports[port].slave); status->duplex = 1; } else { status->link = 1; @@ -690,7 +690,7 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol) { - struct net_device *p = ds->ports[port].cpu_dp->netdev; + struct net_device *p = ds->ports[port].cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_wolinfo pwol; @@ -713,7 +713,7 @@ static void bcm_sf2_sw_get_wol(struct dsa_switch *ds, int port, static int bcm_sf2_sw_set_wol(struct dsa_switch *ds, int port, struct ethtool_wolinfo *wol) { - struct net_device *p = ds->ports[port].cpu_dp->netdev; + struct net_device *p = ds->ports[port].cpu_dp->master; struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); s8 cpu_port = ds->ports[port].cpu_dp->index; struct ethtool_wolinfo pwol; diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 034241696ce2..fea2e665d0cb 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -933,7 +933,7 @@ mt7530_setup(struct dsa_switch *ds) * controller also is the container for two GMACs nodes representing * as two netdev instances. */ - dn = ds->ports[MT7530_CPU_PORT].netdev->dev.of_node->parent; + dn = ds->ports[MT7530_CPU_PORT].master->dev.of_node->parent; priv->ethernet = syscon_node_to_regmap(dn); if (IS_ERR(priv->ethernet)) return PTR_ERR(priv->ethernet); diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 76cf383dcf90..88b47fa09b41 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1137,7 +1137,7 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i)) continue; - if (!ds->ports[port].netdev) + if (!ds->ports[port].slave) continue; if (vlan.member[i] == diff --git a/include/net/dsa.h b/include/net/dsa.h index 2746741f74cf..6ed1a17ed1bd 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -164,6 +164,14 @@ struct dsa_mall_tc_entry { struct dsa_port { + /* A CPU port is physically connected to a master device. + * A user port exposed to userspace has a slave device. + */ + union { + struct net_device *master; + struct net_device *slave; + }; + /* CPU port tagging operations used by master or slave devices */ const struct dsa_device_ops *tag_ops; @@ -176,7 +184,6 @@ struct dsa_port { unsigned int index; const char *name; struct dsa_port *cpu_dp; - struct net_device *netdev; struct device_node *dn; unsigned int ageing_time; u8 stp_state; diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 832c659ff993..a3abf7a7b9a2 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -201,7 +201,7 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, #ifdef CONFIG_PM_SLEEP static bool dsa_is_port_initialized(struct dsa_switch *ds, int p) { - return ds->enabled_port_mask & (1 << p) && ds->ports[p].netdev; + return ds->enabled_port_mask & (1 << p) && ds->ports[p].slave; } int dsa_switch_suspend(struct dsa_switch *ds) @@ -213,7 +213,7 @@ int dsa_switch_suspend(struct dsa_switch *ds) if (!dsa_is_port_initialized(ds, i)) continue; - ret = dsa_slave_suspend(ds->ports[i].netdev); + ret = dsa_slave_suspend(ds->ports[i].slave); if (ret) return ret; } @@ -240,7 +240,7 @@ int dsa_switch_resume(struct dsa_switch *ds) if (!dsa_is_port_initialized(ds, i)) continue; - ret = dsa_slave_resume(ds->ports[i].netdev); + ret = dsa_slave_resume(ds->ports[i].slave); if (ret) return ret; } diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 6ac9e11d385c..9e8b8aab049d 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -279,7 +279,7 @@ static int dsa_user_port_apply(struct dsa_port *port) if (err) { dev_warn(ds->dev, "Failed to create slave %d: %d\n", port->index, err); - port->netdev = NULL; + port->slave = NULL; return err; } @@ -289,7 +289,7 @@ static int dsa_user_port_apply(struct dsa_port *port) if (err) return err; - devlink_port_type_eth_set(&port->devlink_port, port->netdev); + devlink_port_type_eth_set(&port->devlink_port, port->slave); return 0; } @@ -297,9 +297,9 @@ static int dsa_user_port_apply(struct dsa_port *port) static void dsa_user_port_unapply(struct dsa_port *port) { devlink_port_unregister(&port->devlink_port); - if (port->netdev) { - dsa_slave_destroy(port->netdev); - port->netdev = NULL; + if (port->slave) { + dsa_slave_destroy(port->slave); + port->slave = NULL; port->ds->enabled_port_mask &= ~(1 << port->index); } } @@ -432,9 +432,9 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst) * sent to the tag format's receive function. */ wmb(); - dst->cpu_dp->netdev->dsa_ptr = dst->cpu_dp; + dst->cpu_dp->master->dsa_ptr = dst->cpu_dp; - err = dsa_master_ethtool_setup(dst->cpu_dp->netdev); + err = dsa_master_ethtool_setup(dst->cpu_dp->master); if (err) return err; @@ -451,9 +451,9 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) if (!dst->applied) return; - dsa_master_ethtool_restore(dst->cpu_dp->netdev); + dsa_master_ethtool_restore(dst->cpu_dp->master); - dst->cpu_dp->netdev->dsa_ptr = NULL; + dst->cpu_dp->master->dsa_ptr = NULL; /* If we used a tagging format that doesn't have an ethertype * field, make sure that all packets from this point get sent @@ -499,7 +499,7 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, if (!dst->cpu_dp) { dst->cpu_dp = port; - dst->cpu_dp->netdev = ethernet_dev; + dst->cpu_dp->master = ethernet_dev; } /* Initialize cpu_port_mask now for drv->setup() diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 623c22b75e81..1e9914062d0b 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -130,7 +130,7 @@ static inline struct net_device *dsa_master_find_slave(struct net_device *dev, if (port < 0 || port >= ds->num_ports) return NULL; - return ds->ports[port].netdev; + return ds->ports[port].slave; } /* port.c */ @@ -181,7 +181,7 @@ dsa_slave_to_master(const struct net_device *dev) { struct dsa_port *dp = dsa_slave_to_port(dev); - return dp->cpu_dp->netdev; + return dp->cpu_dp->master; } /* switch.c */ diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index cc28c6f792a3..b6c88fd33d4f 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -120,7 +120,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, return -EINVAL; } dst->cpu_dp = &ds->ports[i]; - dst->cpu_dp->netdev = master; + dst->cpu_dp->master = master; ds->cpu_port_mask |= 1 << i; } else if (!strcmp(name, "dsa")) { ds->dsa_port_mask |= 1 << i; @@ -261,10 +261,10 @@ static void dsa_switch_destroy(struct dsa_switch *ds) if (!(ds->enabled_port_mask & (1 << port))) continue; - if (!ds->ports[port].netdev) + if (!ds->ports[port].slave) continue; - dsa_slave_destroy(ds->ports[port].netdev); + dsa_slave_destroy(ds->ports[port].slave); } /* Disable configuration of the CPU and DSA ports */ @@ -601,7 +601,7 @@ static int dsa_setup_dst(struct dsa_switch_tree *dst, struct net_device *dev, wmb(); dev->dsa_ptr = dst->cpu_dp; - return dsa_master_ethtool_setup(dst->cpu_dp->netdev); + return dsa_master_ethtool_setup(dst->cpu_dp->master); } static int dsa_probe(struct platform_device *pdev) @@ -666,9 +666,9 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst) { int i; - dsa_master_ethtool_restore(dst->cpu_dp->netdev); + dsa_master_ethtool_restore(dst->cpu_dp->master); - dst->cpu_dp->netdev->dsa_ptr = NULL; + dst->cpu_dp->master->dsa_ptr = NULL; /* If we used a tagging format that doesn't have an ethertype * field, make sure that all packets from this point get sent @@ -683,7 +683,7 @@ static void dsa_remove_dst(struct dsa_switch_tree *dst) dsa_switch_destroy(ds); } - dev_put(dst->cpu_dp->netdev); + dev_put(dst->cpu_dp->master); } static int dsa_remove(struct platform_device *pdev) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index b72e07503a40..6906de0f0050 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1123,7 +1123,7 @@ static void dsa_slave_notify(struct net_device *dev, unsigned long val) int dsa_slave_create(struct dsa_port *port, const char *name) { struct dsa_port *cpu_dp = port->cpu_dp; - struct net_device *master = cpu_dp->netdev; + struct net_device *master = cpu_dp->master; struct dsa_switch *ds = port->ds; struct net_device *slave_dev; struct dsa_slave_priv *p; @@ -1170,7 +1170,7 @@ int dsa_slave_create(struct dsa_port *port, const char *name) p->old_link = -1; p->old_duplex = -1; - port->netdev = slave_dev; + port->slave = slave_dev; netif_carrier_off(slave_dev); @@ -1198,7 +1198,7 @@ out_phy: out_free: free_percpu(p->stats64); free_netdev(slave_dev); - port->netdev = NULL; + port->slave = NULL; return ret; } -- cgit v1.2.3-59-g8ed1b From c8652c83bc84ac8db44060ced0036de7628aa5e5 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 16 Oct 2017 11:12:19 -0400 Subject: net: dsa: add dsa_to_port helper The dsa_port structure is part of DSA core data and must only be updated by the later. It is OK and sometimes necessary for the DSA drivers to access this data, but this has to be read only. For that purpose, add a dsa_to_port() helper which returns a const pointer to a dsa_port structure which must be used by DSA drivers from now on instead of digging into ds->ports[] themselves. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 4 ++-- drivers/net/dsa/lan9303-core.c | 2 +- drivers/net/dsa/mt7530.c | 4 ++-- drivers/net/dsa/mv88e6060.c | 2 +- drivers/net/dsa/mv88e6xxx/chip.c | 8 ++++---- drivers/net/dsa/qca8k.c | 4 ++-- include/net/dsa.h | 5 +++++ 7 files changed, 17 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index d4ce092def83..b48cf0487b43 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1354,7 +1354,7 @@ int b53_br_join(struct dsa_switch *ds, int port, struct net_device *br) b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(port), &pvlan); b53_for_each_port(dev, i) { - if (ds->ports[i].bridge_dev != br) + if (dsa_to_port(ds, i)->bridge_dev != br) continue; /* Add this local port to the remote port VLAN control @@ -1390,7 +1390,7 @@ void b53_br_leave(struct dsa_switch *ds, int port, struct net_device *br) b53_for_each_port(dev, i) { /* Don't touch the remaining ports */ - if (ds->ports[i].bridge_dev != br) + if (dsa_to_port(ds, i)->bridge_dev != br) continue; b53_read16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i), ®); diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index fecfe1fe67ea..09a748327fc6 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -863,7 +863,7 @@ static int lan9303_port_bridge_join(struct dsa_switch *ds, int port, struct lan9303 *chip = ds->priv; dev_dbg(chip->dev, "%s(port %d)\n", __func__, port); - if (ds->ports[1].bridge_dev == ds->ports[2].bridge_dev) { + if (dsa_to_port(ds, 1)->bridge_dev == dsa_to_port(ds, 2)->bridge_dev) { lan9303_bridge_ports(chip); chip->is_bridged = true; /* unleash stp_state_set() */ } diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index fea2e665d0cb..21431be2831e 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -782,7 +782,7 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port, * and not being setup until the port becomes enabled. */ if (ds->enabled_port_mask & BIT(i) && i != port) { - if (ds->ports[i].bridge_dev != bridge) + if (dsa_to_port(ds, i)->bridge_dev != bridge) continue; if (priv->ports[i].enable) mt7530_set(priv, MT7530_PCR_P(i), @@ -819,7 +819,7 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port, * is kept and not being setup until the port becomes enabled. */ if (ds->enabled_port_mask & BIT(i) && i != port) { - if (ds->ports[i].bridge_dev != bridge) + if (dsa_to_port(ds, i)->bridge_dev != bridge) continue; if (priv->ports[i].enable) mt7530_clear(priv, MT7530_PCR_P(i), diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index 6173be889d95..f78b9e13be1c 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -177,7 +177,7 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p) ((p & 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT) | (dsa_is_cpu_port(ds, p) ? ds->enabled_port_mask : - BIT(ds->ports[p].cpu_dp->index))); + BIT(dsa_to_port(ds, p)->cpu_dp->index))); /* Port Association Vector: when learning source addresses * of packets, add the address to the address database using diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 88b47fa09b41..677d6902807e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -851,7 +851,7 @@ static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port) for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) if (dsa_is_cpu_port(chip->ds, i) || dsa_is_dsa_port(chip->ds, i) || - (br && chip->ds->ports[i].bridge_dev == br)) + (br && dsa_to_port(chip->ds, i)->bridge_dev == br)) pvlan |= BIT(i); return pvlan; @@ -1144,16 +1144,16 @@ static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, MV88E6XXX_G1_VTU_DATA_MEMBER_TAG_NON_MEMBER) continue; - if (ds->ports[i].bridge_dev == + if (dsa_to_port(ds, i)->bridge_dev == ds->ports[port].bridge_dev) break; /* same bridge, check next VLAN */ - if (!ds->ports[i].bridge_dev) + if (!dsa_to_port(ds, i)->bridge_dev) continue; dev_err(ds->dev, "p%d: hw VLAN %d already used by %s\n", port, vlan.vid, - netdev_name(ds->ports[i].bridge_dev)); + netdev_name(dsa_to_port(ds, i)->bridge_dev)); err = -EOPNOTSUPP; goto unlock; } diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 82f09711ac1a..d1b0b1fb632f 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -700,7 +700,7 @@ qca8k_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *br) int i; for (i = 1; i < QCA8K_NUM_PORTS; i++) { - if (ds->ports[i].bridge_dev != br) + if (dsa_to_port(ds, i)->bridge_dev != br) continue; /* Add this port to the portvlan mask of the other ports * in the bridge @@ -725,7 +725,7 @@ qca8k_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *br) int i; for (i = 1; i < QCA8K_NUM_PORTS; i++) { - if (ds->ports[i].bridge_dev != br) + if (dsa_to_port(ds, i)->bridge_dev != br) continue; /* Remove this port to the portvlan mask of the other ports * in the bridge diff --git a/include/net/dsa.h b/include/net/dsa.h index 6ed1a17ed1bd..38961ef91d3d 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -269,6 +269,11 @@ static inline bool dsa_is_normal_port(struct dsa_switch *ds, int p) return !dsa_is_cpu_port(ds, p) && !dsa_is_dsa_port(ds, p); } +static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) +{ + return &ds->ports[p]; +} + static inline u8 dsa_upstream_port(struct dsa_switch *ds) { struct dsa_switch_tree *dst = ds->dst; -- cgit v1.2.3-59-g8ed1b From 7c761b593e2c1dc6bc6c0c15ec338af1f00cabd7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 12 Oct 2017 18:38:02 -0500 Subject: ASoC: Add helper to find codec_dai from dai_name Create a helper function to remove duplicate code used in machine drivers Suggested-by: Takashi Iwai Signed-off-by: Pierre-Louis Bossart Acked-by: Liam Girdwood Reviewed-by: Andy Shevchenko Signed-off-by: Mark Brown --- include/sound/soc.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d22de9712c45..deacbafbae17 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1720,6 +1720,20 @@ struct snd_soc_dai *snd_soc_find_dai( #include +static inline +struct snd_soc_dai *snd_soc_card_get_codec_dai(struct snd_soc_card *card, + const char *dai_name) +{ + struct snd_soc_pcm_runtime *rtd; + + list_for_each_entry(rtd, &card->rtd_list, list) { + if (!strcmp(rtd->codec_dai->name, dai_name)) + return rtd->codec_dai; + } + + return NULL; +} + #ifdef CONFIG_DEBUG_FS extern struct dentry *snd_soc_debugfs_root; #endif -- cgit v1.2.3-59-g8ed1b From eb4ddaf474285a4c6986f4a1c3205bdb0bed2da9 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 16 Oct 2017 17:28:45 -0700 Subject: net/decnet: Convert timers to use timer_setup() In preparation for unconditionally passing the struct timer_list pointer to all timer callbacks, switch to using the new timer_setup() and from_timer() to pass the timer pointer explicitly. Cc: "David S. Miller" Cc: Johannes Berg Cc: David Ahern Cc: linux-decnet-user@lists.sourceforge.net Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Signed-off-by: David S. Miller --- include/net/dn.h | 7 ------- include/net/dn_nsp.h | 1 - net/decnet/af_decnet.c | 4 ---- net/decnet/dn_dev.c | 12 +++++------- net/decnet/dn_nsp_out.c | 11 ----------- 5 files changed, 5 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/dn.h b/include/net/dn.h index 913b73d239f5..4394f7d5cfe8 100644 --- a/include/net/dn.h +++ b/include/net/dn.h @@ -122,13 +122,6 @@ struct dn_scp /* Session Control Port */ unsigned long keepalive; void (*keepalive_fxn)(struct sock *sk); - /* - * This stuff is for the fast timer for delayed acks - */ - struct timer_list delack_timer; - int delack_pending; - void (*delack_fxn)(struct sock *sk); - }; static inline struct dn_scp *DN_SK(struct sock *sk) diff --git a/include/net/dn_nsp.h b/include/net/dn_nsp.h index 3a3e33d18456..413a15e5339c 100644 --- a/include/net/dn_nsp.h +++ b/include/net/dn_nsp.h @@ -17,7 +17,6 @@ void dn_nsp_send_data_ack(struct sock *sk); void dn_nsp_send_oth_ack(struct sock *sk); -void dn_nsp_delayed_ack(struct sock *sk); void dn_send_conn_ack(struct sock *sk); void dn_send_conn_conf(struct sock *sk, gfp_t gfp); void dn_nsp_send_disc(struct sock *sk, unsigned char type, diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c index 73a0399dc7a2..d4c9a8bbad3e 100644 --- a/net/decnet/af_decnet.c +++ b/net/decnet/af_decnet.c @@ -533,10 +533,6 @@ static struct sock *dn_alloc_sock(struct net *net, struct socket *sock, gfp_t gf scp->keepalive = 10 * HZ; scp->keepalive_fxn = dn_keepalive; - init_timer(&scp->delack_timer); - scp->delack_pending = 0; - scp->delack_fxn = dn_nsp_delayed_ack; - dn_start_slow_timer(sk); out: return sk; diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 4d339de56862..92dbaa3f1eae 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -1038,14 +1038,14 @@ static void dn_eth_down(struct net_device *dev) static void dn_dev_set_timer(struct net_device *dev); -static void dn_dev_timer_func(unsigned long arg) +static void dn_dev_timer_func(struct timer_list *t) { - struct net_device *dev = (struct net_device *)arg; - struct dn_dev *dn_db; + struct dn_dev *dn_db = from_timer(dn_db, t, timer); + struct net_device *dev; struct dn_ifaddr *ifa; rcu_read_lock(); - dn_db = rcu_dereference(dev->dn_ptr); + dev = dn_db->dev; if (dn_db->t3 <= dn_db->parms.t2) { if (dn_db->parms.timer3) { for (ifa = rcu_dereference(dn_db->ifa_list); @@ -1070,8 +1070,6 @@ static void dn_dev_set_timer(struct net_device *dev) if (dn_db->parms.t2 > dn_db->parms.t3) dn_db->parms.t2 = dn_db->parms.t3; - dn_db->timer.data = (unsigned long)dev; - dn_db->timer.function = dn_dev_timer_func; dn_db->timer.expires = jiffies + (dn_db->parms.t2 * HZ); add_timer(&dn_db->timer); @@ -1100,7 +1098,7 @@ static struct dn_dev *dn_dev_create(struct net_device *dev, int *err) rcu_assign_pointer(dev->dn_ptr, dn_db); dn_db->dev = dev; - init_timer(&dn_db->timer); + timer_setup(&dn_db->timer, dn_dev_timer_func, 0); dn_db->uptime = jiffies; diff --git a/net/decnet/dn_nsp_out.c b/net/decnet/dn_nsp_out.c index 66f035e476ea..e50a4adfcf7e 100644 --- a/net/decnet/dn_nsp_out.c +++ b/net/decnet/dn_nsp_out.c @@ -491,17 +491,6 @@ void dn_send_conn_ack (struct sock *sk) dn_nsp_send(skb); } -void dn_nsp_delayed_ack(struct sock *sk) -{ - struct dn_scp *scp = DN_SK(sk); - - if (scp->ackxmt_oth != scp->numoth_rcv) - dn_nsp_send_oth_ack(sk); - - if (scp->ackxmt_dat != scp->numdat_rcv) - dn_nsp_send_data_ack(sk); -} - static int dn_nsp_retrans_conn_conf(struct sock *sk) { struct dn_scp *scp = DN_SK(sk); -- cgit v1.2.3-59-g8ed1b From 59f379f9046a9e0532ffd19b44e3c32fe79ec51b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 16 Oct 2017 17:29:19 -0700 Subject: inet/connection_sock: Convert timers to use timer_setup() In preparation for unconditionally passing the struct timer_list pointer to all timer callbacks, switch to using the new timer_setup() and from_timer() to pass the timer pointer explicitly. Cc: "David S. Miller" Cc: Gerrit Renker Cc: Alexey Kuznetsov Cc: Hideaki YOSHIFUJI Cc: netdev@vger.kernel.org Cc: dccp@vger.kernel.org Signed-off-by: Kees Cook Signed-off-by: David S. Miller --- include/net/inet_connection_sock.h | 6 +++--- net/dccp/timer.c | 18 ++++++++++-------- net/ipv4/inet_connection_sock.c | 21 +++++++++------------ net/ipv4/tcp_timer.c | 18 +++++++++++------- 4 files changed, 33 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index 13e4c89a8231..0358745ea059 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -169,9 +169,9 @@ enum inet_csk_ack_state_t { }; void inet_csk_init_xmit_timers(struct sock *sk, - void (*retransmit_handler)(unsigned long), - void (*delack_handler)(unsigned long), - void (*keepalive_handler)(unsigned long)); + void (*retransmit_handler)(struct timer_list *), + void (*delack_handler)(struct timer_list *), + void (*keepalive_handler)(struct timer_list *)); void inet_csk_clear_xmit_timers(struct sock *sk); static inline void inet_csk_schedule_ack(struct sock *sk) diff --git a/net/dccp/timer.c b/net/dccp/timer.c index 3a2c34027758..1e35526bf436 100644 --- a/net/dccp/timer.c +++ b/net/dccp/timer.c @@ -125,10 +125,11 @@ static void dccp_retransmit_timer(struct sock *sk) __sk_dst_reset(sk); } -static void dccp_write_timer(unsigned long data) +static void dccp_write_timer(struct timer_list *t) { - struct sock *sk = (struct sock *)data; - struct inet_connection_sock *icsk = inet_csk(sk); + struct inet_connection_sock *icsk = + from_timer(icsk, t, icsk_retransmit_timer); + struct sock *sk = &icsk->icsk_inet.sk; int event = 0; bh_lock_sock(sk); @@ -161,19 +162,20 @@ out: sock_put(sk); } -static void dccp_keepalive_timer(unsigned long data) +static void dccp_keepalive_timer(struct timer_list *t) { - struct sock *sk = (struct sock *)data; + struct sock *sk = from_timer(sk, t, sk_timer); pr_err("dccp should not use a keepalive timer !\n"); sock_put(sk); } /* This is the same as tcp_delack_timer, sans prequeue & mem_reclaim stuff */ -static void dccp_delack_timer(unsigned long data) +static void dccp_delack_timer(struct timer_list *t) { - struct sock *sk = (struct sock *)data; - struct inet_connection_sock *icsk = inet_csk(sk); + struct inet_connection_sock *icsk = + from_timer(icsk, t, icsk_delack_timer); + struct sock *sk = &icsk->icsk_inet.sk; bh_lock_sock(sk); if (sock_owned_by_user(sk)) { diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 8a91ebbf0c01..5c965ecc96a0 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -494,17 +494,15 @@ EXPORT_SYMBOL(inet_csk_accept); * to optimize. */ void inet_csk_init_xmit_timers(struct sock *sk, - void (*retransmit_handler)(unsigned long), - void (*delack_handler)(unsigned long), - void (*keepalive_handler)(unsigned long)) + void (*retransmit_handler)(struct timer_list *t), + void (*delack_handler)(struct timer_list *t), + void (*keepalive_handler)(struct timer_list *t)) { struct inet_connection_sock *icsk = inet_csk(sk); - setup_timer(&icsk->icsk_retransmit_timer, retransmit_handler, - (unsigned long)sk); - setup_timer(&icsk->icsk_delack_timer, delack_handler, - (unsigned long)sk); - setup_timer(&sk->sk_timer, keepalive_handler, (unsigned long)sk); + timer_setup(&icsk->icsk_retransmit_timer, retransmit_handler, 0); + timer_setup(&icsk->icsk_delack_timer, delack_handler, 0); + timer_setup(&sk->sk_timer, keepalive_handler, 0); icsk->icsk_pending = icsk->icsk_ack.pending = 0; } EXPORT_SYMBOL(inet_csk_init_xmit_timers); @@ -676,9 +674,9 @@ void inet_csk_reqsk_queue_drop_and_put(struct sock *sk, struct request_sock *req } EXPORT_SYMBOL(inet_csk_reqsk_queue_drop_and_put); -static void reqsk_timer_handler(unsigned long data) +static void reqsk_timer_handler(struct timer_list *t) { - struct request_sock *req = (struct request_sock *)data; + struct request_sock *req = from_timer(req, t, rsk_timer); struct sock *sk_listener = req->rsk_listener; struct net *net = sock_net(sk_listener); struct inet_connection_sock *icsk = inet_csk(sk_listener); @@ -749,8 +747,7 @@ static void reqsk_queue_hash_req(struct request_sock *req, req->num_timeout = 0; req->sk = NULL; - setup_pinned_timer(&req->rsk_timer, reqsk_timer_handler, - (unsigned long)req); + timer_setup(&req->rsk_timer, reqsk_timer_handler, TIMER_PINNED); mod_timer(&req->rsk_timer, jiffies + timeout); inet_ehash_insert(req_to_sk(req), NULL); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 7014cc00c74c..804a8d34ce86 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -288,15 +288,17 @@ out: * * Returns: Nothing (void) */ -static void tcp_delack_timer(unsigned long data) +static void tcp_delack_timer(struct timer_list *t) { - struct sock *sk = (struct sock *)data; + struct inet_connection_sock *icsk = + from_timer(icsk, t, icsk_delack_timer); + struct sock *sk = &icsk->icsk_inet.sk; bh_lock_sock(sk); if (!sock_owned_by_user(sk)) { tcp_delack_timer_handler(sk); } else { - inet_csk(sk)->icsk_ack.blocked = 1; + icsk->icsk_ack.blocked = 1; __NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED); /* deleguate our work to tcp_release_cb() */ if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &sk->sk_tsq_flags)) @@ -576,9 +578,11 @@ out: sk_mem_reclaim(sk); } -static void tcp_write_timer(unsigned long data) +static void tcp_write_timer(struct timer_list *t) { - struct sock *sk = (struct sock *)data; + struct inet_connection_sock *icsk = + from_timer(icsk, t, icsk_retransmit_timer); + struct sock *sk = &icsk->icsk_inet.sk; bh_lock_sock(sk); if (!sock_owned_by_user(sk)) { @@ -613,9 +617,9 @@ void tcp_set_keepalive(struct sock *sk, int val) EXPORT_SYMBOL_GPL(tcp_set_keepalive); -static void tcp_keepalive_timer (unsigned long data) +static void tcp_keepalive_timer (struct timer_list *t) { - struct sock *sk = (struct sock *) data; + struct sock *sk = from_timer(sk, t, sk_timer); struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); u32 elapsed; -- cgit v1.2.3-59-g8ed1b From 78802011fbe34331bdef6f2dfb1634011f0e4c32 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 16 Oct 2017 17:29:20 -0700 Subject: inet: frags: Convert timers to use timer_setup() In preparation for unconditionally passing the struct timer_list pointer to all timer callbacks, switch to using the new timer_setup() and from_timer() to pass the timer pointer explicitly. Cc: Alexander Aring Cc: Stefan Schmidt Cc: "David S. Miller" Cc: Alexey Kuznetsov Cc: Hideaki YOSHIFUJI Cc: Pablo Neira Ayuso Cc: Jozsef Kadlecsik Cc: Florian Westphal Cc: linux-wpan@vger.kernel.org Cc: netdev@vger.kernel.org Cc: netfilter-devel@vger.kernel.org Cc: coreteam@netfilter.org Signed-off-by: Kees Cook Acked-by: Stefan Schmidt # for ieee802154 Signed-off-by: David S. Miller --- include/net/inet_frag.h | 2 +- net/ieee802154/6lowpan/reassembly.c | 5 +++-- net/ipv4/inet_fragment.c | 4 ++-- net/ipv4/ip_fragment.c | 5 +++-- net/ipv6/netfilter/nf_conntrack_reasm.c | 5 +++-- net/ipv6/reassembly.c | 5 +++-- 6 files changed, 15 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index fc59e0775e00..c695807ca707 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -95,7 +95,7 @@ struct inet_frags { void (*constructor)(struct inet_frag_queue *q, const void *arg); void (*destructor)(struct inet_frag_queue *); - void (*frag_expire)(unsigned long data); + void (*frag_expire)(struct timer_list *t); struct kmem_cache *frags_cachep; const char *frags_cache_name; }; diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c index f85b08baff16..85bf86ad6b18 100644 --- a/net/ieee802154/6lowpan/reassembly.c +++ b/net/ieee802154/6lowpan/reassembly.c @@ -80,12 +80,13 @@ static void lowpan_frag_init(struct inet_frag_queue *q, const void *a) fq->daddr = *arg->dst; } -static void lowpan_frag_expire(unsigned long data) +static void lowpan_frag_expire(struct timer_list *t) { + struct inet_frag_queue *frag = from_timer(frag, t, timer); struct frag_queue *fq; struct net *net; - fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); + fq = container_of(frag, struct frag_queue, q); net = container_of(fq->q.net, struct net, ieee802154_lowpan.frags); spin_lock(&fq->q.lock); diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index af74d0433453..7f3ef5c287a1 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -147,7 +147,7 @@ inet_evict_bucket(struct inet_frags *f, struct inet_frag_bucket *hb) spin_unlock(&hb->chain_lock); hlist_for_each_entry_safe(fq, n, &expired, list_evictor) - f->frag_expire((unsigned long) fq); + f->frag_expire(&fq->timer); return evicted; } @@ -366,7 +366,7 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, f->constructor(q, arg); add_frag_mem_limit(nf, f->qsize); - setup_timer(&q->timer, f->frag_expire, (unsigned long)q); + timer_setup(&q->timer, f->frag_expire, 0); spin_lock_init(&q->lock); refcount_set(&q->refcnt, 1); diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 46408c220d9d..9215654a401f 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -190,12 +190,13 @@ static bool frag_expire_skip_icmp(u32 user) /* * Oops, a fragment queue timed out. Kill it and send an ICMP reply. */ -static void ip_expire(unsigned long arg) +static void ip_expire(struct timer_list *t) { + struct inet_frag_queue *frag = from_timer(frag, t, timer); struct ipq *qp; struct net *net; - qp = container_of((struct inet_frag_queue *) arg, struct ipq, q); + qp = container_of(frag, struct ipq, q); net = container_of(qp->q.net, struct net, ipv4.frags); rcu_read_lock(); diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index b263bf3a19f7..977d8900cfd1 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -169,12 +169,13 @@ static unsigned int nf_hashfn(const struct inet_frag_queue *q) return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr); } -static void nf_ct_frag6_expire(unsigned long data) +static void nf_ct_frag6_expire(struct timer_list *t) { + struct inet_frag_queue *frag = from_timer(frag, t, timer); struct frag_queue *fq; struct net *net; - fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); + fq = container_of(frag, struct frag_queue, q); net = container_of(fq->q.net, struct net, nf_frag.frags); ip6_expire_frag_queue(net, fq, &nf_frags); diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 846012eae526..afbc000ad4f2 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -170,12 +170,13 @@ out: } EXPORT_SYMBOL(ip6_expire_frag_queue); -static void ip6_frag_expire(unsigned long data) +static void ip6_frag_expire(struct timer_list *t) { + struct inet_frag_queue *frag = from_timer(frag, t, timer); struct frag_queue *fq; struct net *net; - fq = container_of((struct inet_frag_queue *)data, struct frag_queue, q); + fq = container_of(frag, struct frag_queue, q); net = container_of(fq->q.net, struct net, ipv6.frags); ip6_expire_frag_queue(net, fq, &ip6_frags); -- cgit v1.2.3-59-g8ed1b From 085b30625e39df67d7320f22269796276c6b0c11 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Fri, 23 Sep 2016 14:05:12 +0100 Subject: perf/core: Add PERF_AUX_FLAG_COLLISION to report colliding samples The ARM SPE architecture permits an implementation to ignore a sample if the sample is due to be taken whilst another sample is already being produced. In this case, it is desirable to report the collision to userspace, as they may want to lower the sample period. This patch adds a PERF_AUX_FLAG_COLLISION flag, so that such events can be relayed to userspace. Acked-by: Peter Zijlstra Signed-off-by: Will Deacon --- include/uapi/linux/perf_event.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 140ae638cfd6..7ca1b22ea417 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -941,6 +941,7 @@ enum perf_callchain_context { #define PERF_AUX_FLAG_TRUNCATED 0x01 /* record was truncated to fit */ #define PERF_AUX_FLAG_OVERWRITE 0x02 /* snapshot from overwrite mode */ #define PERF_AUX_FLAG_PARTIAL 0x04 /* record contains gaps */ +#define PERF_AUX_FLAG_COLLISION 0x08 /* sample collided with another */ #define PERF_FLAG_FD_NO_GROUP (1UL << 0) #define PERF_FLAG_FD_OUTPUT (1UL << 1) -- cgit v1.2.3-59-g8ed1b From fb6ff75e18937a20dbec1eb47b5f893f38eabae4 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 16 Oct 2017 14:24:02 -0700 Subject: tcp: Use pI6c in tcp tracepoint The compact form for IPv6 addresses is more user friendly than the full version. For example: compact: 2001:db8:1::1 full: 2001:0db8:0001:0000:0000:0000:0000:0004i Update the tcp tracepoint to show the compact form. Signed-off-by: David Ahern Acked-by: Cong Wang Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index 3d1cbd072b7e..1ffab6d96e94 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -57,7 +57,7 @@ TRACE_EVENT(tcp_retransmit_skb, } ), - TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6 daddrv6=%pI6", + TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c", __entry->sport, __entry->dport, __entry->saddr, __entry->daddr, __entry->saddr_v6, __entry->daddr_v6) ); -- cgit v1.2.3-59-g8ed1b From 386fd5da401dc6c4b0ab6a54d333609876b699fe Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 16 Oct 2017 15:32:07 -0700 Subject: tcp: Check daddr_cache before use in tracepoint Running perf in one window to capture tcp_retransmit_skb tracepoint: $ perf record -e tcp:tcp_retransmit_skb -a And causing a retransmission on an active TCP session (e.g., dropping packets in the receiver, changing MTU on the interface to 500 and back to 1500) triggers a panic: [ 58.543144] BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 [ 58.545300] IP: perf_trace_tcp_retransmit_skb+0xd0/0x145 [ 58.546770] PGD 0 P4D 0 [ 58.547472] Oops: 0000 [#1] SMP [ 58.548328] Modules linked in: vrf [ 58.549262] CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.14.0-rc4+ #26 [ 58.551004] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014 [ 58.554560] task: ffffffff81a0e540 task.stack: ffffffff81a00000 [ 58.555817] RIP: 0010:perf_trace_tcp_retransmit_skb+0xd0/0x145 [ 58.557137] RSP: 0018:ffff88003fc03d68 EFLAGS: 00010282 [ 58.558292] RAX: 0000000000000000 RBX: ffffe8ffffc0ec80 RCX: ffff880038543098 [ 58.559850] RDX: 0400000000000000 RSI: ffff88003fc03d70 RDI: ffff88003fc14b68 [ 58.561099] RBP: ffff88003fc03da8 R08: 0000000000000000 R09: ffffea0000d3224a [ 58.562005] R10: ffff88003fc03db8 R11: 0000000000000010 R12: ffff8800385428c0 [ 58.562930] R13: ffffe8ffffc0e478 R14: ffffffff81a93a40 R15: ffff88003d4f0c00 [ 58.563845] FS: 0000000000000000(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000 [ 58.564873] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 58.565613] CR2: 0000000000000008 CR3: 000000003d68f004 CR4: 00000000000606f0 [ 58.566538] Call Trace: [ 58.566865] [ 58.567140] __tcp_retransmit_skb+0x4ab/0x4c6 [ 58.567704] ? tcp_set_ca_state+0x22/0x3f [ 58.568231] tcp_retransmit_skb+0x14/0xa3 [ 58.568754] tcp_retransmit_timer+0x472/0x5e3 [ 58.569324] ? tcp_write_timer_handler+0x1e9/0x1e9 [ 58.569946] tcp_write_timer_handler+0x95/0x1e9 [ 58.570548] tcp_write_timer+0x2a/0x58 Check that daddr_cache is non-NULL before de-referencing. Fixes: e086101b150a ("tcp: add a tracepoint for tcp retransmission") Signed-off-by: David Ahern Acked-by: Cong Wang Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index 1ffab6d96e94..f51c130f1e0f 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -27,7 +27,6 @@ TRACE_EVENT(tcp_retransmit_skb, ), TP_fast_assign( - struct ipv6_pinfo *np = inet6_sk(sk); struct inet_sock *inet = inet_sk(sk); struct in6_addr *pin6; __be32 *p32; @@ -44,11 +43,12 @@ TRACE_EVENT(tcp_retransmit_skb, p32 = (__be32 *) __entry->daddr; *p32 = inet->inet_daddr; - if (np) { + /* IPv6 socket ? */ + if (inet6_sk(sk)) { pin6 = (struct in6_addr *)__entry->saddr_v6; - *pin6 = np->saddr; + *pin6 = sk->sk_v6_rcv_saddr; pin6 = (struct in6_addr *)__entry->daddr_v6; - *pin6 = *(np->daddr_cache); + *pin6 = sk->sk_v6_daddr; } else { pin6 = (struct in6_addr *)__entry->saddr_v6; ipv6_addr_set_v4mapped(inet->inet_saddr, pin6); -- cgit v1.2.3-59-g8ed1b From 7de16e3a35578f4f5accc6f5f23970310483d0a2 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 16 Oct 2017 16:40:53 -0700 Subject: bpf: split verifier and program ops struct bpf_verifier_ops contains both verifier ops and operations used later during program's lifetime (test_run). Split the runtime ops into a different structure. BPF_PROG_TYPE() will now append ## _prog_ops or ## _verifier_ops to the names. Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 15 ++++++++++----- include/linux/bpf_types.h | 28 ++++++++++++++-------------- kernel/bpf/syscall.c | 16 +++++++++++++--- kernel/bpf/verifier.c | 12 ++++++------ kernel/trace/bpf_trace.c | 15 ++++++++++++--- net/core/filter.c | 45 ++++++++++++++++++++++++++++++++++++--------- 6 files changed, 91 insertions(+), 40 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 6d4dd844828a..e1fba5504ca5 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -157,6 +157,11 @@ bpf_ctx_record_field_size(struct bpf_insn_access_aux *aux, u32 size) aux->ctx_field_size = size; } +struct bpf_prog_ops { + int (*test_run)(struct bpf_prog *prog, const union bpf_attr *kattr, + union bpf_attr __user *uattr); +}; + struct bpf_verifier_ops { /* return eBPF function prototype for verification */ const struct bpf_func_proto *(*get_func_proto)(enum bpf_func_id func_id); @@ -172,8 +177,6 @@ struct bpf_verifier_ops { const struct bpf_insn *src, struct bpf_insn *dst, struct bpf_prog *prog, u32 *target_size); - int (*test_run)(struct bpf_prog *prog, const union bpf_attr *kattr, - union bpf_attr __user *uattr); }; struct bpf_prog_aux { @@ -184,7 +187,8 @@ struct bpf_prog_aux { u32 id; struct latch_tree_node ksym_tnode; struct list_head ksym_lnode; - const struct bpf_verifier_ops *ops; + const struct bpf_prog_ops *ops; + const struct bpf_verifier_ops *vops; struct bpf_map **used_maps; struct bpf_prog *prog; struct user_struct *user; @@ -279,8 +283,9 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, #ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU(int, bpf_prog_active); -#define BPF_PROG_TYPE(_id, _ops) \ - extern const struct bpf_verifier_ops _ops; +#define BPF_PROG_TYPE(_id, _name) \ + extern const struct bpf_prog_ops _name ## _prog_ops; \ + extern const struct bpf_verifier_ops _name ## _verifier_ops; #define BPF_MAP_TYPE(_id, _ops) \ extern const struct bpf_map_ops _ops; #include diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 814c1081a4a9..36418ad43245 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -1,22 +1,22 @@ /* internal file - do not include directly */ #ifdef CONFIG_NET -BPF_PROG_TYPE(BPF_PROG_TYPE_SOCKET_FILTER, sk_filter_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_CLS, tc_cls_act_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_ACT, tc_cls_act_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_XDP, xdp_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SKB, cg_skb_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK, cg_sock_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_IN, lwt_inout_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_inout_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_SOCK_OPS, sock_ops_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_SK_SKB, sk_skb_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_SOCKET_FILTER, sk_filter) +BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_CLS, tc_cls_act) +BPF_PROG_TYPE(BPF_PROG_TYPE_SCHED_ACT, tc_cls_act) +BPF_PROG_TYPE(BPF_PROG_TYPE_XDP, xdp) +BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SKB, cg_skb) +BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SOCK, cg_sock) +BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_IN, lwt_inout) +BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_inout) +BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit) +BPF_PROG_TYPE(BPF_PROG_TYPE_SOCK_OPS, sock_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_SK_SKB, sk_skb) #endif #ifdef CONFIG_BPF_EVENTS -BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint_prog_ops) -BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe) +BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint) +BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event) #endif BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 54fba06942f5..444902b5a30d 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -739,9 +739,18 @@ err_put: return err; } -static const struct bpf_verifier_ops * const bpf_prog_types[] = { -#define BPF_PROG_TYPE(_id, _ops) \ - [_id] = &_ops, +static const struct bpf_prog_ops * const bpf_prog_types[] = { +#define BPF_PROG_TYPE(_id, _name) \ + [_id] = & _name ## _prog_ops, +#define BPF_MAP_TYPE(_id, _ops) +#include +#undef BPF_PROG_TYPE +#undef BPF_MAP_TYPE +}; + +static const struct bpf_verifier_ops * const bpf_verifier_ops[] = { +#define BPF_PROG_TYPE(_id, _name) \ + [_id] = & _name ## _verifier_ops, #define BPF_MAP_TYPE(_id, _ops) #include #undef BPF_PROG_TYPE @@ -754,6 +763,7 @@ static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog) return -EINVAL; prog->aux->ops = bpf_prog_types[type]; + prog->aux->vops = bpf_verifier_ops[type]; prog->type = type; return 0; } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index e4d5136725a2..38e24d69fc95 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -856,8 +856,8 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, *reg_type = info.reg_type; return 0; } - } else if (env->prog->aux->ops->is_valid_access && - env->prog->aux->ops->is_valid_access(off, size, t, &info)) { + } else if (env->prog->aux->vops->is_valid_access && + env->prog->aux->vops->is_valid_access(off, size, t, &info)) { /* A non zero info.ctx_field_size indicates that this field is a * candidate for later verifier transformation to load the whole * field and then apply a mask when accessed with a narrower @@ -1565,8 +1565,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) return -EINVAL; } - if (env->prog->aux->ops->get_func_proto) - fn = env->prog->aux->ops->get_func_proto(func_id); + if (env->prog->aux->vops->get_func_proto) + fn = env->prog->aux->vops->get_func_proto(func_id); if (!fn) { verbose(env, "unknown func %s#%d\n", func_id_name(func_id), @@ -4035,7 +4035,7 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of */ static int convert_ctx_accesses(struct bpf_verifier_env *env) { - const struct bpf_verifier_ops *ops = env->prog->aux->ops; + const struct bpf_verifier_ops *ops = env->prog->aux->vops; int i, cnt, size, ctx_field_size, delta = 0; const int insn_cnt = env->prog->len; struct bpf_insn insn_buf[16], *insn; @@ -4236,7 +4236,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) insn = new_prog->insnsi + i + delta; } patch_call_imm: - fn = prog->aux->ops->get_func_proto(insn->imm); + fn = prog->aux->vops->get_func_proto(insn->imm); /* all functions that have prototype and verifier allowed * programs to call them, must be real in-kernel functions */ diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 04ea5314f2bc..3126da2f468a 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -561,11 +561,14 @@ static bool kprobe_prog_is_valid_access(int off, int size, enum bpf_access_type return true; } -const struct bpf_verifier_ops kprobe_prog_ops = { +const struct bpf_verifier_ops kprobe_verifier_ops = { .get_func_proto = kprobe_prog_func_proto, .is_valid_access = kprobe_prog_is_valid_access, }; +const struct bpf_prog_ops kprobe_prog_ops = { +}; + BPF_CALL_5(bpf_perf_event_output_tp, void *, tp_buff, struct bpf_map *, map, u64, flags, void *, data, u64, size) { @@ -667,11 +670,14 @@ static bool tp_prog_is_valid_access(int off, int size, enum bpf_access_type type return true; } -const struct bpf_verifier_ops tracepoint_prog_ops = { +const struct bpf_verifier_ops tracepoint_verifier_ops = { .get_func_proto = tp_prog_func_proto, .is_valid_access = tp_prog_is_valid_access, }; +const struct bpf_prog_ops tracepoint_prog_ops = { +}; + static bool pe_prog_is_valid_access(int off, int size, enum bpf_access_type type, struct bpf_insn_access_aux *info) { @@ -727,8 +733,11 @@ static u32 pe_prog_convert_ctx_access(enum bpf_access_type type, return insn - insn_buf; } -const struct bpf_verifier_ops perf_event_prog_ops = { +const struct bpf_verifier_ops perf_event_verifier_ops = { .get_func_proto = tp_prog_func_proto, .is_valid_access = pe_prog_is_valid_access, .convert_ctx_access = pe_prog_convert_ctx_access, }; + +const struct bpf_prog_ops perf_event_prog_ops = { +}; diff --git a/net/core/filter.c b/net/core/filter.c index 4d88e0665c41..1dd3034f846f 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -4395,68 +4395,95 @@ static u32 sock_ops_convert_ctx_access(enum bpf_access_type type, return insn - insn_buf; } -const struct bpf_verifier_ops sk_filter_prog_ops = { +const struct bpf_verifier_ops sk_filter_verifier_ops = { .get_func_proto = sk_filter_func_proto, .is_valid_access = sk_filter_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, }; -const struct bpf_verifier_ops tc_cls_act_prog_ops = { +const struct bpf_prog_ops sk_filter_prog_ops = { +}; + +const struct bpf_verifier_ops tc_cls_act_verifier_ops = { .get_func_proto = tc_cls_act_func_proto, .is_valid_access = tc_cls_act_is_valid_access, .convert_ctx_access = tc_cls_act_convert_ctx_access, .gen_prologue = tc_cls_act_prologue, +}; + +const struct bpf_prog_ops tc_cls_act_prog_ops = { .test_run = bpf_prog_test_run_skb, }; -const struct bpf_verifier_ops xdp_prog_ops = { +const struct bpf_verifier_ops xdp_verifier_ops = { .get_func_proto = xdp_func_proto, .is_valid_access = xdp_is_valid_access, .convert_ctx_access = xdp_convert_ctx_access, +}; + +const struct bpf_prog_ops xdp_prog_ops = { .test_run = bpf_prog_test_run_xdp, }; -const struct bpf_verifier_ops cg_skb_prog_ops = { +const struct bpf_verifier_ops cg_skb_verifier_ops = { .get_func_proto = sk_filter_func_proto, .is_valid_access = sk_filter_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, +}; + +const struct bpf_prog_ops cg_skb_prog_ops = { .test_run = bpf_prog_test_run_skb, }; -const struct bpf_verifier_ops lwt_inout_prog_ops = { +const struct bpf_verifier_ops lwt_inout_verifier_ops = { .get_func_proto = lwt_inout_func_proto, .is_valid_access = lwt_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, +}; + +const struct bpf_prog_ops lwt_inout_prog_ops = { .test_run = bpf_prog_test_run_skb, }; -const struct bpf_verifier_ops lwt_xmit_prog_ops = { +const struct bpf_verifier_ops lwt_xmit_verifier_ops = { .get_func_proto = lwt_xmit_func_proto, .is_valid_access = lwt_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, .gen_prologue = tc_cls_act_prologue, +}; + +const struct bpf_prog_ops lwt_xmit_prog_ops = { .test_run = bpf_prog_test_run_skb, }; -const struct bpf_verifier_ops cg_sock_prog_ops = { +const struct bpf_verifier_ops cg_sock_verifier_ops = { .get_func_proto = sock_filter_func_proto, .is_valid_access = sock_filter_is_valid_access, .convert_ctx_access = sock_filter_convert_ctx_access, }; -const struct bpf_verifier_ops sock_ops_prog_ops = { +const struct bpf_prog_ops cg_sock_prog_ops = { +}; + +const struct bpf_verifier_ops sock_ops_verifier_ops = { .get_func_proto = sock_ops_func_proto, .is_valid_access = sock_ops_is_valid_access, .convert_ctx_access = sock_ops_convert_ctx_access, }; -const struct bpf_verifier_ops sk_skb_prog_ops = { +const struct bpf_prog_ops sock_ops_prog_ops = { +}; + +const struct bpf_verifier_ops sk_skb_verifier_ops = { .get_func_proto = sk_skb_func_proto, .is_valid_access = sk_skb_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, .gen_prologue = sk_skb_prologue, }; +const struct bpf_prog_ops sk_skb_prog_ops = { +}; + int sk_detach_filter(struct sock *sk) { int ret = -ENOENT; -- cgit v1.2.3-59-g8ed1b From 00176a34d9e27ab1e77db75fe13abc005cffe0ca Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 16 Oct 2017 16:40:54 -0700 Subject: bpf: remove the verifier ops from program structure Since the verifier ops don't have to be associated with the program for its entire lifetime we can move it to verifier's struct bpf_verifier_env. Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 1 - include/linux/bpf_verifier.h | 1 + kernel/bpf/syscall.c | 10 ---------- kernel/bpf/verifier.c | 23 +++++++++++++++++------ 4 files changed, 18 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index e1fba5504ca5..cf91977e8719 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -188,7 +188,6 @@ struct bpf_prog_aux { struct latch_tree_node ksym_tnode; struct list_head ksym_lnode; const struct bpf_prog_ops *ops; - const struct bpf_verifier_ops *vops; struct bpf_map **used_maps; struct bpf_prog *prog; struct user_struct *user; diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index f00ef751c1c5..feeaea93d959 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -141,6 +141,7 @@ struct bpf_ext_analyzer_ops { */ struct bpf_verifier_env { struct bpf_prog *prog; /* eBPF program being verified */ + const struct bpf_verifier_ops *ops; struct bpf_verifier_stack_elem *head; /* stack of verifier states to be processed */ int stack_size; /* number of states to be processed */ bool strict_alignment; /* perform strict pointer alignment checks */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 444902b5a30d..0e893cac6795 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -748,22 +748,12 @@ static const struct bpf_prog_ops * const bpf_prog_types[] = { #undef BPF_MAP_TYPE }; -static const struct bpf_verifier_ops * const bpf_verifier_ops[] = { -#define BPF_PROG_TYPE(_id, _name) \ - [_id] = & _name ## _verifier_ops, -#define BPF_MAP_TYPE(_id, _ops) -#include -#undef BPF_PROG_TYPE -#undef BPF_MAP_TYPE -}; - static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog) { if (type >= ARRAY_SIZE(bpf_prog_types) || !bpf_prog_types[type]) return -EINVAL; prog->aux->ops = bpf_prog_types[type]; - prog->aux->vops = bpf_verifier_ops[type]; prog->type = type; return 0; } diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 38e24d69fc95..3b6e2c550e96 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -23,6 +23,15 @@ #include "disasm.h" +static const struct bpf_verifier_ops * const bpf_verifier_ops[] = { +#define BPF_PROG_TYPE(_id, _name) \ + [_id] = & _name ## _verifier_ops, +#define BPF_MAP_TYPE(_id, _ops) +#include +#undef BPF_PROG_TYPE +#undef BPF_MAP_TYPE +}; + /* bpf_check() is a static code analyzer that walks eBPF program * instruction by instruction and updates register/stack state. * All paths of conditional branches are analyzed until 'bpf_exit' insn. @@ -856,8 +865,8 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, *reg_type = info.reg_type; return 0; } - } else if (env->prog->aux->vops->is_valid_access && - env->prog->aux->vops->is_valid_access(off, size, t, &info)) { + } else if (env->ops->is_valid_access && + env->ops->is_valid_access(off, size, t, &info)) { /* A non zero info.ctx_field_size indicates that this field is a * candidate for later verifier transformation to load the whole * field and then apply a mask when accessed with a narrower @@ -1565,8 +1574,8 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) return -EINVAL; } - if (env->prog->aux->vops->get_func_proto) - fn = env->prog->aux->vops->get_func_proto(func_id); + if (env->ops->get_func_proto) + fn = env->ops->get_func_proto(func_id); if (!fn) { verbose(env, "unknown func %s#%d\n", func_id_name(func_id), @@ -4035,7 +4044,7 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of */ static int convert_ctx_accesses(struct bpf_verifier_env *env) { - const struct bpf_verifier_ops *ops = env->prog->aux->vops; + const struct bpf_verifier_ops *ops = env->ops; int i, cnt, size, ctx_field_size, delta = 0; const int insn_cnt = env->prog->len; struct bpf_insn insn_buf[16], *insn; @@ -4236,7 +4245,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env) insn = new_prog->insnsi + i + delta; } patch_call_imm: - fn = prog->aux->vops->get_func_proto(insn->imm); + fn = env->ops->get_func_proto(insn->imm); /* all functions that have prototype and verifier allowed * programs to call them, must be real in-kernel functions */ @@ -4294,6 +4303,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) if (!env->insn_aux_data) goto err_free_env; env->prog = *prog; + env->ops = bpf_verifier_ops[env->prog->type]; /* grab the mutex to protect few globals used by verifier */ mutex_lock(&bpf_verifier_lock); @@ -4406,6 +4416,7 @@ int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, if (!env->insn_aux_data) goto err_free_env; env->prog = prog; + env->ops = bpf_verifier_ops[env->prog->type]; env->analyzer_ops = ops; env->analyzer_priv = priv; -- cgit v1.2.3-59-g8ed1b From 4f9218aaf8a463f76cac40aa08d859d065f8cc9e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 16 Oct 2017 16:40:55 -0700 Subject: bpf: move knowledge about post-translation offsets out of verifier Use the fact that verifier ops are now separate from program ops to define a separate set of callbacks for verification of already translated programs. Since we expect the analyzer ops to be defined only for a small subset of all program types initialize their array by hand (don't use linux/bpf_types.h). Signed-off-by: Jakub Kicinski Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 3 +++ kernel/bpf/verifier.c | 55 +++++++++++++++------------------------------------ net/core/filter.c | 40 +++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 39 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index cf91977e8719..d67ccdc0099f 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -291,6 +291,9 @@ DECLARE_PER_CPU(int, bpf_prog_active); #undef BPF_PROG_TYPE #undef BPF_MAP_TYPE +extern const struct bpf_verifier_ops tc_cls_act_analyzer_ops; +extern const struct bpf_verifier_ops xdp_analyzer_ops; + struct bpf_prog *bpf_prog_get(u32 ufd); struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type); struct bpf_prog * __must_check bpf_prog_add(struct bpf_prog *prog, int i); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 3b6e2c550e96..545b8c45a578 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -822,36 +822,6 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, return err; } -static bool analyzer_is_valid_access(struct bpf_verifier_env *env, int off, - struct bpf_insn_access_aux *info) -{ - switch (env->prog->type) { - case BPF_PROG_TYPE_XDP: - switch (off) { - case offsetof(struct xdp_buff, data): - info->reg_type = PTR_TO_PACKET; - return true; - case offsetof(struct xdp_buff, data_end): - info->reg_type = PTR_TO_PACKET_END; - return true; - } - return false; - case BPF_PROG_TYPE_SCHED_CLS: - switch (off) { - case offsetof(struct sk_buff, data): - info->reg_type = PTR_TO_PACKET; - return true; - case offsetof(struct sk_buff, cb) + - offsetof(struct bpf_skb_data_end, data_end): - info->reg_type = PTR_TO_PACKET_END; - return true; - } - return false; - default: - return false; - } -} - /* check access to 'struct bpf_context' fields. Supports fixed offsets only */ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, int size, enum bpf_access_type t, enum bpf_reg_type *reg_type) @@ -860,13 +830,8 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, .reg_type = *reg_type, }; - if (env->analyzer_ops) { - if (analyzer_is_valid_access(env, off, &info)) { - *reg_type = info.reg_type; - return 0; - } - } else if (env->ops->is_valid_access && - env->ops->is_valid_access(off, size, t, &info)) { + if (env->ops->is_valid_access && + env->ops->is_valid_access(off, size, t, &info)) { /* A non zero info.ctx_field_size indicates that this field is a * candidate for later verifier transformation to load the whole * field and then apply a mask when accessed with a narrower @@ -874,9 +839,12 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, * will only allow for whole field access and rejects any other * type of narrower access. */ - env->insn_aux_data[insn_idx].ctx_field_size = info.ctx_field_size; *reg_type = info.reg_type; + if (env->analyzer_ops) + return 0; + + env->insn_aux_data[insn_idx].ctx_field_size = info.ctx_field_size; /* remember the offset of last byte accessed in ctx */ if (env->prog->aux->max_ctx_offset < off + size) env->prog->aux->max_ctx_offset = off + size; @@ -4400,12 +4368,21 @@ err_free_env: return ret; } +static const struct bpf_verifier_ops * const bpf_analyzer_ops[] = { + [BPF_PROG_TYPE_XDP] = &xdp_analyzer_ops, + [BPF_PROG_TYPE_SCHED_CLS] = &tc_cls_act_analyzer_ops, +}; + int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, void *priv) { struct bpf_verifier_env *env; int ret; + if (prog->type >= ARRAY_SIZE(bpf_analyzer_ops) || + !bpf_analyzer_ops[prog->type]) + return -EOPNOTSUPP; + env = kzalloc(sizeof(struct bpf_verifier_env), GFP_KERNEL); if (!env) return -ENOMEM; @@ -4416,7 +4393,7 @@ int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, if (!env->insn_aux_data) goto err_free_env; env->prog = prog; - env->ops = bpf_verifier_ops[env->prog->type]; + env->ops = bpf_analyzer_ops[env->prog->type]; env->analyzer_ops = ops; env->analyzer_priv = priv; diff --git a/net/core/filter.c b/net/core/filter.c index 1dd3034f846f..7373a08fbef7 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3732,6 +3732,23 @@ static bool tc_cls_act_is_valid_access(int off, int size, return bpf_skb_is_valid_access(off, size, type, info); } +static bool +tc_cls_act_is_valid_access_analyzer(int off, int size, + enum bpf_access_type type, + struct bpf_insn_access_aux *info) +{ + switch (off) { + case offsetof(struct sk_buff, data): + info->reg_type = PTR_TO_PACKET; + return true; + case offsetof(struct sk_buff, cb) + + offsetof(struct bpf_skb_data_end, data_end): + info->reg_type = PTR_TO_PACKET_END; + return true; + } + return false; +} + static bool __is_valid_xdp_access(int off, int size) { if (off < 0 || off >= sizeof(struct xdp_md)) @@ -3766,6 +3783,21 @@ static bool xdp_is_valid_access(int off, int size, return __is_valid_xdp_access(off, size); } +static bool xdp_is_valid_access_analyzer(int off, int size, + enum bpf_access_type type, + struct bpf_insn_access_aux *info) +{ + switch (off) { + case offsetof(struct xdp_buff, data): + info->reg_type = PTR_TO_PACKET; + return true; + case offsetof(struct xdp_buff, data_end): + info->reg_type = PTR_TO_PACKET_END; + return true; + } + return false; +} + void bpf_warn_invalid_xdp_action(u32 act) { const u32 act_max = XDP_REDIRECT; @@ -4411,6 +4443,10 @@ const struct bpf_verifier_ops tc_cls_act_verifier_ops = { .gen_prologue = tc_cls_act_prologue, }; +const struct bpf_verifier_ops tc_cls_act_analyzer_ops = { + .is_valid_access = tc_cls_act_is_valid_access_analyzer, +}; + const struct bpf_prog_ops tc_cls_act_prog_ops = { .test_run = bpf_prog_test_run_skb, }; @@ -4421,6 +4457,10 @@ const struct bpf_verifier_ops xdp_verifier_ops = { .convert_ctx_access = xdp_convert_ctx_access, }; +const struct bpf_verifier_ops xdp_analyzer_ops = { + .is_valid_access = xdp_is_valid_access_analyzer, +}; + const struct bpf_prog_ops xdp_prog_ops = { .test_run = bpf_prog_test_run_xdp, }; -- cgit v1.2.3-59-g8ed1b From f674bd281460451e8784f3bc917a650e8937a8ed Mon Sep 17 00:00:00 2001 From: Akshu Agrawal Date: Wed, 28 Jun 2017 14:08:09 +0530 Subject: drm/amdgpu Moving amdgpu asic types to a separate file Amdgpu asic types will be required for other drivers too. Hence, its better to keep it in a separate include file. Reviewed-by: Alex Deucher Signed-off-by: Akshu Agrawal Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/include/amd_shared.h | 29 ++---------------- include/drm/amd_asic_type.h | 52 ++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 27 deletions(-) create mode 100644 include/drm/amd_asic_type.h (limited to 'include') diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index 70e8c20acb2f..3a49fbd8baf8 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -23,34 +23,9 @@ #ifndef __AMD_SHARED_H__ #define __AMD_SHARED_H__ -#define AMD_MAX_USEC_TIMEOUT 200000 /* 200 ms */ +#include -/* - * Supported ASIC types - */ -enum amd_asic_type { - CHIP_TAHITI = 0, - CHIP_PITCAIRN, - CHIP_VERDE, - CHIP_OLAND, - CHIP_HAINAN, - CHIP_BONAIRE, - CHIP_KAVERI, - CHIP_KABINI, - CHIP_HAWAII, - CHIP_MULLINS, - CHIP_TOPAZ, - CHIP_TONGA, - CHIP_FIJI, - CHIP_CARRIZO, - CHIP_STONEY, - CHIP_POLARIS10, - CHIP_POLARIS11, - CHIP_POLARIS12, - CHIP_VEGA10, - CHIP_RAVEN, - CHIP_LAST, -}; +#define AMD_MAX_USEC_TIMEOUT 200000 /* 200 ms */ /* * Chip flags diff --git a/include/drm/amd_asic_type.h b/include/drm/amd_asic_type.h new file mode 100644 index 000000000000..599028f66585 --- /dev/null +++ b/include/drm/amd_asic_type.h @@ -0,0 +1,52 @@ +/* + * Copyright 2017 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef __AMD_ASIC_TYPE_H__ +#define __AMD_ASIC_TYPE_H__ +/* + * Supported ASIC types + */ +enum amd_asic_type { + CHIP_TAHITI = 0, + CHIP_PITCAIRN, + CHIP_VERDE, + CHIP_OLAND, + CHIP_HAINAN, + CHIP_BONAIRE, + CHIP_KAVERI, + CHIP_KABINI, + CHIP_HAWAII, + CHIP_MULLINS, + CHIP_TOPAZ, + CHIP_TONGA, + CHIP_FIJI, + CHIP_CARRIZO, + CHIP_STONEY, + CHIP_POLARIS10, + CHIP_POLARIS11, + CHIP_POLARIS12, + CHIP_VEGA10, + CHIP_RAVEN, + CHIP_LAST, +}; + +#endif /*__AMD_ASIC_TYPE_H__ */ -- cgit v1.2.3-59-g8ed1b From 5cda6587feec790a089703dde2e6e1f82de50bbd Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Mon, 16 Oct 2017 08:45:12 +0300 Subject: IB/core: Introduce and use rdma_create_user_ah Introduce rdma_create_user_ah API which allows passing udata to provider driver and additionally which resolves DMAC for RoCE. ib_resolve_eth_dmac() resolves destination mac address for unicast, multicast, link local ipv4 mapped ipv6 and ipv6 destination gid entry. This allows all RoCE provider drivers to avoid duplicating such code. Such change brings consistency where IB core always resolves dmac and pass it to RoCE provider drivers for user and kernel consumers, with this ah_attr->roce.dmac is always an input field for provider drivers. This uniformity avoids exporting ib_resolve_eth_dmac symbol to providers or other modules. Therefore its removed as exported symbol at later in the patch series. Now uverbs and umad both makes use of rdma_create_user_ah API which fixes the issue where umad has invalid DMAC for address. Signed-off-by: Parav Pandit Reviewed-by: Daniel Jurgens Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- drivers/infiniband/core/user_mad.c | 2 +- drivers/infiniband/core/uverbs_cmd.c | 10 +-------- drivers/infiniband/core/verbs.c | 40 ++++++++++++++++++++++++++++++++++-- include/rdma/ib_verbs.h | 15 ++++++++++++++ 4 files changed, 55 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index c1696e6084b2..38809962c105 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -506,7 +506,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf, rdma_ah_set_dgid_raw(&ah_attr, packet->mad.hdr.gid); } - ah = rdma_create_ah(agent->qp->pd, &ah_attr); + ah = rdma_create_user_ah(agent->qp->pd, &ah_attr, NULL); if (IS_ERR(ah)) { ret = PTR_ERR(ah); goto err_up; diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 52a2cf2d83aa..0cef1863e2dc 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -2537,7 +2537,6 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file, struct rdma_ah_attr attr; int ret; struct ib_udata udata; - u8 *dmac; if (out_len < sizeof resp) return -ENOSPC; @@ -2580,20 +2579,13 @@ ssize_t ib_uverbs_create_ah(struct ib_uverbs_file *file, } else { rdma_ah_set_ah_flags(&attr, 0); } - dmac = rdma_ah_retrieve_dmac(&attr); - if (dmac) - memset(dmac, 0, ETH_ALEN); - - ah = pd->device->create_ah(pd, &attr, &udata); + ah = rdma_create_user_ah(pd, &attr, &udata); if (IS_ERR(ah)) { ret = PTR_ERR(ah); goto err_put; } - ah->device = pd->device; - ah->pd = pd; - atomic_inc(&pd->usecnt); ah->uobject = uobj; uobj->user_handle = cmd.user_handle; uobj->object = ah; diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index de57d6c11a25..4dcfe47c479d 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -302,11 +302,13 @@ EXPORT_SYMBOL(ib_dealloc_pd); /* Address handles */ -struct ib_ah *rdma_create_ah(struct ib_pd *pd, struct rdma_ah_attr *ah_attr) +static struct ib_ah *_rdma_create_ah(struct ib_pd *pd, + struct rdma_ah_attr *ah_attr, + struct ib_udata *udata) { struct ib_ah *ah; - ah = pd->device->create_ah(pd, ah_attr, NULL); + ah = pd->device->create_ah(pd, ah_attr, udata); if (!IS_ERR(ah)) { ah->device = pd->device; @@ -318,8 +320,42 @@ struct ib_ah *rdma_create_ah(struct ib_pd *pd, struct rdma_ah_attr *ah_attr) return ah; } + +struct ib_ah *rdma_create_ah(struct ib_pd *pd, struct rdma_ah_attr *ah_attr) +{ + return _rdma_create_ah(pd, ah_attr, NULL); +} EXPORT_SYMBOL(rdma_create_ah); +/** + * rdma_create_user_ah - Creates an address handle for the + * given address vector. + * It resolves destination mac address for ah attribute of RoCE type. + * @pd: The protection domain associated with the address handle. + * @ah_attr: The attributes of the address vector. + * @udata: pointer to user's input output buffer information need by + * provider driver. + * + * It returns 0 on success and returns appropriate error code on error. + * The address handle is used to reference a local or global destination + * in all UD QP post sends. + */ +struct ib_ah *rdma_create_user_ah(struct ib_pd *pd, + struct rdma_ah_attr *ah_attr, + struct ib_udata *udata) +{ + int err; + + if (ah_attr->type == RDMA_AH_ATTR_TYPE_ROCE) { + err = ib_resolve_eth_dmac(pd->device, ah_attr); + if (err) + return ERR_PTR(err); + } + + return _rdma_create_ah(pd, ah_attr, udata); +} +EXPORT_SYMBOL(rdma_create_user_ah); + int ib_get_rdma_header_version(const union rdma_network_hdr *hdr) { const struct iphdr *ip4h = (struct iphdr *)&hdr->roce4grh; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index e8608b2dc844..09c4a695155e 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -2857,6 +2857,21 @@ void ib_dealloc_pd(struct ib_pd *pd); */ struct ib_ah *rdma_create_ah(struct ib_pd *pd, struct rdma_ah_attr *ah_attr); +/** + * rdma_create_user_ah - Creates an address handle for the given address vector. + * It resolves destination mac address for ah attribute of RoCE type. + * @pd: The protection domain associated with the address handle. + * @ah_attr: The attributes of the address vector. + * @udata: pointer to user's input output buffer information need by + * provider driver. + * + * It returns 0 on success and returns appropriate error code on error. + * The address handle is used to reference a local or global destination + * in all UD QP post sends. + */ +struct ib_ah *rdma_create_user_ah(struct ib_pd *pd, + struct rdma_ah_attr *ah_attr, + struct ib_udata *udata); /** * ib_get_gids_from_rdma_hdr - Get sgid and dgid from GRH or IPv4 header * work completion. -- cgit v1.2.3-59-g8ed1b From c0348eb069687a2f27c0cd23dafb35918edf9e75 Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Mon, 16 Oct 2017 08:45:13 +0300 Subject: IB: Let ib_core resolve destination mac address Since IB/core resolves the destination mac address for user and kernel consumers, avoid resolving in multiple provider drivers. Only ib_core resolves DMAC now, therefore resolve_eth_dmac is removed as exported symbol. Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- drivers/infiniband/core/verbs.c | 8 +++++--- drivers/infiniband/hw/bnxt_re/ib_verbs.c | 8 -------- drivers/infiniband/hw/hns/hns_roce_ah.c | 14 +------------- drivers/infiniband/hw/mlx4/ah.c | 8 +++----- drivers/infiniband/hw/mlx5/ah.c | 4 ---- drivers/infiniband/hw/ocrdma/ocrdma_ah.c | 15 --------------- include/rdma/ib_verbs.h | 2 -- 7 files changed, 9 insertions(+), 50 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 4dcfe47c479d..d8f1a5d34f4f 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -53,6 +53,9 @@ #include "core_priv.h" +static int ib_resolve_eth_dmac(struct ib_device *device, + struct rdma_ah_attr *ah_attr); + static const char * const ib_events[] = { [IB_EVENT_CQ_ERR] = "CQ error", [IB_EVENT_QP_FATAL] = "QP fatal error", @@ -1257,8 +1260,8 @@ int ib_modify_qp_is_ok(enum ib_qp_state cur_state, enum ib_qp_state next_state, } EXPORT_SYMBOL(ib_modify_qp_is_ok); -int ib_resolve_eth_dmac(struct ib_device *device, - struct rdma_ah_attr *ah_attr) +static int ib_resolve_eth_dmac(struct ib_device *device, + struct rdma_ah_attr *ah_attr) { int ret = 0; struct ib_global_route *grh; @@ -1317,7 +1320,6 @@ int ib_resolve_eth_dmac(struct ib_device *device, out: return ret; } -EXPORT_SYMBOL(ib_resolve_eth_dmac); /** * ib_modify_qp_with_udata - Modifies the attributes for the specified QP. diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index 0d89621d9fe8..20e5eb9290ad 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -729,14 +729,6 @@ struct ib_ah *bnxt_re_create_ah(struct ib_pd *ib_pd, ah->qplib_ah.nw_type = CMDQ_CREATE_AH_TYPE_V1; break; } - rc = rdma_addr_find_l2_eth_by_grh(&sgid, &grh->dgid, - ah_attr->roce.dmac, &vlan_tag, - &sgid_attr.ndev->ifindex, - NULL); - if (rc) { - dev_err(rdev_to_dev(rdev), "Failed to get dmac\n"); - goto fail; - } } memcpy(ah->qplib_ah.dmac, ah_attr->roce.dmac, ETH_ALEN); diff --git a/drivers/infiniband/hw/hns/hns_roce_ah.c b/drivers/infiniband/hw/hns/hns_roce_ah.c index d545302b8ef8..05bab4a39d91 100644 --- a/drivers/infiniband/hw/hns/hns_roce_ah.c +++ b/drivers/infiniband/hw/hns/hns_roce_ah.c @@ -48,7 +48,6 @@ struct ib_ah *hns_roce_create_ah(struct ib_pd *ibpd, struct ib_gid_attr gid_attr; struct hns_roce_ah *ah; u16 vlan_tag = 0xffff; - struct in6_addr in6; const struct ib_global_route *grh = rdma_ah_read_grh(ah_attr); union ib_gid sgid; int ret; @@ -58,18 +57,7 @@ struct ib_ah *hns_roce_create_ah(struct ib_pd *ibpd, return ERR_PTR(-ENOMEM); /* Get mac address */ - memcpy(&in6, grh->dgid.raw, sizeof(grh->dgid.raw)); - if (rdma_is_multicast_addr(&in6)) { - rdma_get_mcast_mac(&in6, ah->av.mac); - } else { - u8 *dmac = rdma_ah_retrieve_dmac(ah_attr); - - if (!dmac) { - kfree(ah); - return ERR_PTR(-EINVAL); - } - memcpy(ah->av.mac, dmac, ETH_ALEN); - } + memcpy(ah->av.mac, ah_attr->roce.dmac, ETH_ALEN); /* Get source gid */ ret = ib_get_cached_gid(ibpd->device, rdma_ah_get_port_num(ah_attr), diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c index 538c46a73248..6dee4fdc5d67 100644 --- a/drivers/infiniband/hw/mlx4/ah.c +++ b/drivers/infiniband/hw/mlx4/ah.c @@ -92,12 +92,10 @@ static struct ib_ah *create_iboe_ah(struct ib_pd *pd, int ret; memcpy(&in6, grh->dgid.raw, sizeof(in6)); - if (rdma_is_multicast_addr(&in6)) { + if (rdma_is_multicast_addr(&in6)) is_mcast = 1; - rdma_get_mcast_mac(&in6, ah->av.eth.mac); - } else { - memcpy(ah->av.eth.mac, ah_attr->roce.dmac, ETH_ALEN); - } + + memcpy(ah->av.eth.mac, ah_attr->roce.dmac, ETH_ALEN); ret = ib_get_cached_gid(pd->device, rdma_ah_get_port_num(ah_attr), grh->sgid_index, &sgid, &gid_attr); if (ret) diff --git a/drivers/infiniband/hw/mlx5/ah.c b/drivers/infiniband/hw/mlx5/ah.c index 3363e29157f6..fe269f680103 100644 --- a/drivers/infiniband/hw/mlx5/ah.c +++ b/drivers/infiniband/hw/mlx5/ah.c @@ -89,10 +89,6 @@ struct ib_ah *mlx5_ib_create_ah(struct ib_pd *pd, struct rdma_ah_attr *ah_attr, resp.response_length = min_resp_len; - err = ib_resolve_eth_dmac(pd->device, ah_attr); - if (err) - return ERR_PTR(err); - memcpy(resp.dmac, ah_attr->roce.dmac, ETH_ALEN); err = ib_copy_to_udata(udata, &resp, resp.response_length); if (err) diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_ah.c b/drivers/infiniband/hw/ocrdma/ocrdma_ah.c index d0249e463338..dec650930ca6 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_ah.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_ah.c @@ -201,21 +201,6 @@ struct ib_ah *ocrdma_create_ah(struct ib_pd *ibpd, struct rdma_ah_attr *attr, /* Get network header type for this GID */ ah->hdr_type = ib_gid_to_network_type(sgid_attr.gid_type, &sgid); - if ((pd->uctx) && - (!rdma_is_multicast_addr((struct in6_addr *)grh->dgid.raw)) && - (!rdma_link_local_addr((struct in6_addr *)grh->dgid.raw))) { - status = rdma_addr_find_l2_eth_by_grh(&sgid, &grh->dgid, - attr->roce.dmac, - &vlan_tag, - &sgid_attr.ndev->ifindex, - NULL); - if (status) { - pr_err("%s(): Failed to resolve dmac from gid." - "status = %d\n", __func__, status); - goto av_conf_err; - } - } - status = set_av_attr(dev, ah, attr, &sgid, pd->id, &isvlan, vlan_tag); if (status) goto av_conf_err; diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 09c4a695155e..9810e4568635 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -3622,8 +3622,6 @@ void ib_drain_rq(struct ib_qp *qp); void ib_drain_sq(struct ib_qp *qp); void ib_drain_qp(struct ib_qp *qp); -int ib_resolve_eth_dmac(struct ib_device *device, - struct rdma_ah_attr *ah_attr); int ib_get_eth_speed(struct ib_device *dev, u8 port_num, u8 *speed, u8 *width); static inline u8 *rdma_ah_retrieve_dmac(struct rdma_ah_attr *attr) -- cgit v1.2.3-59-g8ed1b From 99260132fde7bddc6e0132ce53da94d1c9ccabcb Mon Sep 17 00:00:00 2001 From: Parav Pandit Date: Mon, 16 Oct 2017 08:45:16 +0300 Subject: IB/core: Fix calculation of maximum RoCE MTU The original code only took into consideration the largest header possible after the IB_BTH_BYTES. This was incorrect, as the largest possible header size is the largest possible combination of headers we might run into. The new code accounts for all possible headers in the largest possible combination and subtracts that from the MTU to make sure that all packets will fit on the wire. Link: https://www.spinics.net/lists/linux-rdma/msg54558.html Fixes: 3c86aa70bf67 ("RDMA/cm: Add RDMA CM support for IBoE devices") Signed-off-by: Parav Pandit Reviewed-by: Daniel Jurgens Reported-by: Roland Dreier Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- include/rdma/ib_addr.h | 7 ++++--- include/rdma/ib_pack.h | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h index ec5008cf5d51..8815989301ab 100644 --- a/include/rdma/ib_addr.h +++ b/include/rdma/ib_addr.h @@ -245,10 +245,11 @@ static inline void rdma_addr_set_dgid(struct rdma_dev_addr *dev_addr, union ib_g static inline enum ib_mtu iboe_get_mtu(int mtu) { /* - * reduce IB headers from effective IBoE MTU. 28 stands for - * atomic header which is the biggest possible header after BTH + * Reduce IB headers from effective IBoE MTU. */ - mtu = mtu - IB_GRH_BYTES - IB_BTH_BYTES - 28; + mtu = mtu - (IB_GRH_BYTES + IB_UDP_BYTES + IB_BTH_BYTES + + IB_EXT_XRC_BYTES + IB_EXT_ATOMICETH_BYTES + + IB_ICRC_BYTES); if (mtu >= ib_mtu_enum_to_int(IB_MTU_4096)) return IB_MTU_4096; diff --git a/include/rdma/ib_pack.h b/include/rdma/ib_pack.h index 36655899ee02..7ea1382ad0e5 100644 --- a/include/rdma/ib_pack.h +++ b/include/rdma/ib_pack.h @@ -37,14 +37,17 @@ #include enum { - IB_LRH_BYTES = 8, - IB_ETH_BYTES = 14, - IB_VLAN_BYTES = 4, - IB_GRH_BYTES = 40, - IB_IP4_BYTES = 20, - IB_UDP_BYTES = 8, - IB_BTH_BYTES = 12, - IB_DETH_BYTES = 8 + IB_LRH_BYTES = 8, + IB_ETH_BYTES = 14, + IB_VLAN_BYTES = 4, + IB_GRH_BYTES = 40, + IB_IP4_BYTES = 20, + IB_UDP_BYTES = 8, + IB_BTH_BYTES = 12, + IB_DETH_BYTES = 8, + IB_EXT_ATOMICETH_BYTES = 28, + IB_EXT_XRC_BYTES = 4, + IB_ICRC_BYTES = 4 }; struct ib_field { -- cgit v1.2.3-59-g8ed1b From 448956d619a35538e71e8a2d8f1e4bc274037767 Mon Sep 17 00:00:00 2001 From: Frank Binns Date: Wed, 18 Oct 2017 14:30:23 +0100 Subject: dma-fence: remove duplicate word in comment Signed-off-by: Frank Binns Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/1508333423-5394-1-git-send-email-frank.binns@imgtec.com --- include/linux/dma-fence.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h index ca974224d92e..efdabbb64e3c 100644 --- a/include/linux/dma-fence.h +++ b/include/linux/dma-fence.h @@ -128,7 +128,7 @@ struct dma_fence_cb { * implementation know that there is another driver waiting on * the signal (ie. hw->sw case). * - * This function can be called called from atomic context, but not + * This function can be called from atomic context, but not * from irq context, so normal spinlocks can be used. * * A return value of false indicates the fence already passed, -- cgit v1.2.3-59-g8ed1b From 149e10f8ff71439014dff97987e90e87e2684a16 Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Wed, 11 Oct 2017 12:53:06 +0300 Subject: block: introduce blk_mq_tagset_iter Iterator helper to apply a function on all the tags in a given tagset. export it as it will be used outside the block layer later on. Reviewed-by: Bart Van Assche Reviewed-by: Jens Axboe Reviewed-by: Max Gurtovoy Reviewed-by: Johannes Thumshirn Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- block/blk-mq-tag.c | 16 +++++++++++----- include/linux/blk-mq.h | 2 ++ 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index 6714507aa6c7..bce1c76fc768 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -298,12 +298,12 @@ void blk_mq_tagset_busy_iter(struct blk_mq_tag_set *tagset, } EXPORT_SYMBOL(blk_mq_tagset_busy_iter); -int blk_mq_reinit_tagset(struct blk_mq_tag_set *set, - int (reinit_request)(void *, struct request *)) +int blk_mq_tagset_iter(struct blk_mq_tag_set *set, void *data, + int (fn)(void *, struct request *)) { int i, j, ret = 0; - if (WARN_ON_ONCE(!reinit_request)) + if (WARN_ON_ONCE(!fn)) goto out; for (i = 0; i < set->nr_hw_queues; i++) { @@ -316,8 +316,7 @@ int blk_mq_reinit_tagset(struct blk_mq_tag_set *set, if (!tags->static_rqs[j]) continue; - ret = reinit_request(set->driver_data, - tags->static_rqs[j]); + ret = fn(data, tags->static_rqs[j]); if (ret) goto out; } @@ -326,6 +325,13 @@ int blk_mq_reinit_tagset(struct blk_mq_tag_set *set, out: return ret; } +EXPORT_SYMBOL_GPL(blk_mq_tagset_iter); + +int blk_mq_reinit_tagset(struct blk_mq_tag_set *set, + int (reinit_request)(void *, struct request *)) +{ + return blk_mq_tagset_iter(set, set->driver_data, reinit_request); +} EXPORT_SYMBOL_GPL(blk_mq_reinit_tagset); void blk_mq_queue_tag_busy_iter(struct request_queue *q, busy_iter_fn *fn, diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 50c6485cb04f..6bc29f73a9aa 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -259,6 +259,8 @@ void blk_freeze_queue_start(struct request_queue *q); void blk_mq_freeze_queue_wait(struct request_queue *q); int blk_mq_freeze_queue_wait_timeout(struct request_queue *q, unsigned long timeout); +int blk_mq_tagset_iter(struct blk_mq_tag_set *set, void *data, + int (reinit_request)(void *, struct request *)); int blk_mq_reinit_tagset(struct blk_mq_tag_set *set, int (reinit_request)(void *, struct request *)); -- cgit v1.2.3-59-g8ed1b From dab7487bdf63c6f2d70aac604494d023b189a9fd Mon Sep 17 00:00:00 2001 From: Sagi Grimberg Date: Wed, 11 Oct 2017 12:53:08 +0300 Subject: block: remove blk_mq_reinit_tagset No callers left. Reviewed-by: Jens Axboe Reviewed-by: Bart Van Assche Reviewed-by: Max Gurtovoy Reviewed-by: Johannes Thumshirn Signed-off-by: Sagi Grimberg Signed-off-by: Christoph Hellwig --- block/blk-mq-tag.c | 7 ------- include/linux/blk-mq.h | 2 -- 2 files changed, 9 deletions(-) (limited to 'include') diff --git a/block/blk-mq-tag.c b/block/blk-mq-tag.c index bce1c76fc768..c81b40ecd3f1 100644 --- a/block/blk-mq-tag.c +++ b/block/blk-mq-tag.c @@ -327,13 +327,6 @@ out: } EXPORT_SYMBOL_GPL(blk_mq_tagset_iter); -int blk_mq_reinit_tagset(struct blk_mq_tag_set *set, - int (reinit_request)(void *, struct request *)) -{ - return blk_mq_tagset_iter(set, set->driver_data, reinit_request); -} -EXPORT_SYMBOL_GPL(blk_mq_reinit_tagset); - void blk_mq_queue_tag_busy_iter(struct request_queue *q, busy_iter_fn *fn, void *priv) { diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 6bc29f73a9aa..cfd64e500d82 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -261,8 +261,6 @@ int blk_mq_freeze_queue_wait_timeout(struct request_queue *q, unsigned long timeout); int blk_mq_tagset_iter(struct blk_mq_tag_set *set, void *data, int (reinit_request)(void *, struct request *)); -int blk_mq_reinit_tagset(struct blk_mq_tag_set *set, - int (reinit_request)(void *, struct request *)); int blk_mq_map_queues(struct blk_mq_tag_set *set); void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues); -- cgit v1.2.3-59-g8ed1b From 734f0d241d2b4e47383bd0d16e21e06f6cb8d2c3 Mon Sep 17 00:00:00 2001 From: Dave Chinner Date: Mon, 9 Oct 2017 12:15:34 -0700 Subject: fscrypt: clean up include file mess Filesystems have to include different header files based on whether they are compiled with encryption support or not. That's nasty and messy. Instead, rationalise the headers so we have a single include fscrypt.h and let it decide what internal implementation to include based on the __FS_HAS_ENCRYPTION define. Filesystems set __FS_HAS_ENCRYPTION to 1 before including linux/fscrypt.h if they are built with encryption support. Otherwise, they must set __FS_HAS_ENCRYPTION to 0. Add guards to prevent fscrypt_supp.h and fscrypt_notsupp.h from being directly included by filesystems. Signed-off-by: Dave Chinner [EB: use 1 and 0 rather than defined/undefined] Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/fscrypt_private.h | 3 +- fs/ext4/ext4.h | 8 +-- fs/f2fs/f2fs.h | 8 +-- fs/ubifs/ubifs.h | 9 ++- include/linux/fscrypt.h | 156 ++++++++++++++++++++++++++++++++++++++++ include/linux/fscrypt_common.h | 141 ------------------------------------ include/linux/fscrypt_notsupp.h | 7 +- include/linux/fscrypt_supp.h | 7 +- 8 files changed, 177 insertions(+), 162 deletions(-) create mode 100644 include/linux/fscrypt.h delete mode 100644 include/linux/fscrypt_common.h (limited to 'include') diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index a1d5021c31ef..a180981ee6d7 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -11,7 +11,8 @@ #ifndef _FSCRYPT_PRIVATE_H #define _FSCRYPT_PRIVATE_H -#include +#define __FS_HAS_ENCRYPTION 1 +#include #include /* Encryption parameters */ diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index e2abe01c8c6b..ca6d6166b85c 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -33,17 +33,15 @@ #include #include #include -#ifdef CONFIG_EXT4_FS_ENCRYPTION -#include -#else -#include -#endif #include #include #ifdef __KERNEL__ #include #endif +#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_EXT4_FS_ENCRYPTION) +#include + /* * The fourth extended filesystem constants/structures */ diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9a7c90386947..fc53aebaf3ae 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -23,13 +23,11 @@ #include #include #include -#ifdef CONFIG_F2FS_FS_ENCRYPTION -#include -#else -#include -#endif #include +#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_F2FS_FS_ENCRYPTION) +#include + #ifdef CONFIG_F2FS_CHECK_FS #define f2fs_bug_on(sbi, condition) BUG_ON(condition) #else diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index cd43651f1731..6a346d4af98f 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -38,12 +38,11 @@ #include #include #include -#ifdef CONFIG_UBIFS_FS_ENCRYPTION -#include -#else -#include -#endif #include + +#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_UBIFS_FS_ENCRYPTION) +#include + #include "ubifs-media.h" /* Version of this UBIFS implementation */ diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h new file mode 100644 index 000000000000..bfc962e62078 --- /dev/null +++ b/include/linux/fscrypt.h @@ -0,0 +1,156 @@ +/* + * fscrypt.h: declarations for per-file encryption + * + * Filesystems that implement per-file encryption include this header + * file with the __FS_HAS_ENCRYPTION set according to whether that filesystem + * is being built with encryption support or not. + * + * Copyright (C) 2015, Google, Inc. + * + * Written by Michael Halcrow, 2015. + * Modified by Jaegeuk Kim, 2015. + */ +#ifndef _LINUX_FSCRYPT_H +#define _LINUX_FSCRYPT_H + +#include +#include +#include +#include +#include +#include +#include + +#define FS_CRYPTO_BLOCK_SIZE 16 + +struct fscrypt_info; + +struct fscrypt_ctx { + union { + struct { + struct page *bounce_page; /* Ciphertext page */ + struct page *control_page; /* Original page */ + } w; + struct { + struct bio *bio; + struct work_struct work; + } r; + struct list_head free_list; /* Free list */ + }; + u8 flags; /* Flags */ +}; + +/** + * For encrypted symlinks, the ciphertext length is stored at the beginning + * of the string in little-endian format. + */ +struct fscrypt_symlink_data { + __le16 len; + char encrypted_path[1]; +} __packed; + +struct fscrypt_str { + unsigned char *name; + u32 len; +}; + +struct fscrypt_name { + const struct qstr *usr_fname; + struct fscrypt_str disk_name; + u32 hash; + u32 minor_hash; + struct fscrypt_str crypto_buf; +}; + +#define FSTR_INIT(n, l) { .name = n, .len = l } +#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) +#define fname_name(p) ((p)->disk_name.name) +#define fname_len(p) ((p)->disk_name.len) + +/* + * fscrypt superblock flags + */ +#define FS_CFLG_OWN_PAGES (1U << 1) + +/* + * crypto opertions for filesystems + */ +struct fscrypt_operations { + unsigned int flags; + const char *key_prefix; + int (*get_context)(struct inode *, void *, size_t); + int (*set_context)(struct inode *, const void *, size_t, void *); + bool (*dummy_context)(struct inode *); + bool (*is_encrypted)(struct inode *); + bool (*empty_dir)(struct inode *); + unsigned (*max_namelen)(struct inode *); +}; + +/* Maximum value for the third parameter of fscrypt_operations.set_context(). */ +#define FSCRYPT_SET_CONTEXT_MAX_SIZE 28 + +static inline bool fscrypt_dummy_context_enabled(struct inode *inode) +{ + if (inode->i_sb->s_cop->dummy_context && + inode->i_sb->s_cop->dummy_context(inode)) + return true; + return false; +} + +static inline bool fscrypt_valid_enc_modes(u32 contents_mode, + u32 filenames_mode) +{ + if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC && + filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS) + return true; + + if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS && + filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) + return true; + + return false; +} + +static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) +{ + if (str->len == 1 && str->name[0] == '.') + return true; + + if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') + return true; + + return false; +} + +#if __FS_HAS_ENCRYPTION + +static inline struct page *fscrypt_control_page(struct page *page) +{ + return ((struct fscrypt_ctx *)page_private(page))->w.control_page; +} + +static inline bool fscrypt_has_encryption_key(const struct inode *inode) +{ + return (inode->i_crypt_info != NULL); +} + +#include + +#else /* !__FS_HAS_ENCRYPTION */ + +static inline struct page *fscrypt_control_page(struct page *page) +{ + WARN_ON_ONCE(1); + return ERR_PTR(-EINVAL); +} + +static inline bool fscrypt_has_encryption_key(const struct inode *inode) +{ + return 0; +} + +#include +#endif /* __FS_HAS_ENCRYPTION */ + + +#endif /* _LINUX_FSCRYPT_H */ diff --git a/include/linux/fscrypt_common.h b/include/linux/fscrypt_common.h deleted file mode 100644 index 97f738628b36..000000000000 --- a/include/linux/fscrypt_common.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * fscrypt_common.h: common declarations for per-file encryption - * - * Copyright (C) 2015, Google, Inc. - * - * Written by Michael Halcrow, 2015. - * Modified by Jaegeuk Kim, 2015. - */ - -#ifndef _LINUX_FSCRYPT_COMMON_H -#define _LINUX_FSCRYPT_COMMON_H - -#include -#include -#include -#include -#include -#include -#include - -#define FS_CRYPTO_BLOCK_SIZE 16 - -struct fscrypt_info; - -struct fscrypt_ctx { - union { - struct { - struct page *bounce_page; /* Ciphertext page */ - struct page *control_page; /* Original page */ - } w; - struct { - struct bio *bio; - struct work_struct work; - } r; - struct list_head free_list; /* Free list */ - }; - u8 flags; /* Flags */ -}; - -/** - * For encrypted symlinks, the ciphertext length is stored at the beginning - * of the string in little-endian format. - */ -struct fscrypt_symlink_data { - __le16 len; - char encrypted_path[1]; -} __packed; - -struct fscrypt_str { - unsigned char *name; - u32 len; -}; - -struct fscrypt_name { - const struct qstr *usr_fname; - struct fscrypt_str disk_name; - u32 hash; - u32 minor_hash; - struct fscrypt_str crypto_buf; -}; - -#define FSTR_INIT(n, l) { .name = n, .len = l } -#define FSTR_TO_QSTR(f) QSTR_INIT((f)->name, (f)->len) -#define fname_name(p) ((p)->disk_name.name) -#define fname_len(p) ((p)->disk_name.len) - -/* - * fscrypt superblock flags - */ -#define FS_CFLG_OWN_PAGES (1U << 1) - -/* - * crypto opertions for filesystems - */ -struct fscrypt_operations { - unsigned int flags; - const char *key_prefix; - int (*get_context)(struct inode *, void *, size_t); - int (*set_context)(struct inode *, const void *, size_t, void *); - bool (*dummy_context)(struct inode *); - bool (*is_encrypted)(struct inode *); - bool (*empty_dir)(struct inode *); - unsigned (*max_namelen)(struct inode *); -}; - -/* Maximum value for the third parameter of fscrypt_operations.set_context(). */ -#define FSCRYPT_SET_CONTEXT_MAX_SIZE 28 - -static inline bool fscrypt_dummy_context_enabled(struct inode *inode) -{ - if (inode->i_sb->s_cop->dummy_context && - inode->i_sb->s_cop->dummy_context(inode)) - return true; - return false; -} - -static inline bool fscrypt_valid_enc_modes(u32 contents_mode, - u32 filenames_mode) -{ - if (contents_mode == FS_ENCRYPTION_MODE_AES_128_CBC && - filenames_mode == FS_ENCRYPTION_MODE_AES_128_CTS) - return true; - - if (contents_mode == FS_ENCRYPTION_MODE_AES_256_XTS && - filenames_mode == FS_ENCRYPTION_MODE_AES_256_CTS) - return true; - - return false; -} - -static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) -{ - if (str->len == 1 && str->name[0] == '.') - return true; - - if (str->len == 2 && str->name[0] == '.' && str->name[1] == '.') - return true; - - return false; -} - -static inline struct page *fscrypt_control_page(struct page *page) -{ -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) - return ((struct fscrypt_ctx *)page_private(page))->w.control_page; -#else - WARN_ON_ONCE(1); - return ERR_PTR(-EINVAL); -#endif -} - -static inline int fscrypt_has_encryption_key(const struct inode *inode) -{ -#if IS_ENABLED(CONFIG_FS_ENCRYPTION) - return (inode->i_crypt_info != NULL); -#else - return 0; -#endif -} - -#endif /* _LINUX_FSCRYPT_COMMON_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index ec406aed2f2f..2d0b6960831e 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -3,13 +3,16 @@ * * This stubs out the fscrypt functions for filesystems configured without * encryption support. + * + * Do not include this file directly. Use fscrypt.h instead! */ +#ifndef _LINUX_FSCRYPT_H +#error "Incorrect include of linux/fscrypt_notsupp.h!" +#endif #ifndef _LINUX_FSCRYPT_NOTSUPP_H #define _LINUX_FSCRYPT_NOTSUPP_H -#include - /* crypto.c */ static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags) diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 32e2fcf13b01..5a90e5ef4687 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -1,14 +1,15 @@ /* * fscrypt_supp.h * - * This is included by filesystems configured with encryption support. + * Do not include this file directly. Use fscrypt.h instead! */ +#ifndef _LINUX_FSCRYPT_H +#error "Incorrect include of linux/fscrypt_supp.h!" +#endif #ifndef _LINUX_FSCRYPT_SUPP_H #define _LINUX_FSCRYPT_SUPP_H -#include - /* crypto.c */ extern struct kmem_cache *fscrypt_info_cachep; extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t); -- cgit v1.2.3-59-g8ed1b From 2ee6a576be56427209d370d8a511d49340c84139 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:35 -0700 Subject: fs, fscrypt: add an S_ENCRYPTED inode flag Introduce a flag S_ENCRYPTED which can be set in ->i_flags to indicate that the inode is encrypted using the fscrypt (fs/crypto/) mechanism. Checking this flag will give the same information that inode->i_sb->s_cop->is_encrypted(inode) currently does, but will be more efficient. This will be useful for adding higher-level helper functions for filesystems to use. For example we'll be able to replace this: if (ext4_encrypted_inode(inode)) { ret = fscrypt_get_encryption_info(inode); if (ret) return ret; if (!fscrypt_has_encryption_key(inode)) return -ENOKEY; } with this: ret = fscrypt_require_key(inode); if (ret) return ret; ... since we'll be able to retain the fast path for unencrypted files as a single flag check, using an inline function. This wasn't possible before because we'd have had to frequently call through the ->i_sb->s_cop->is_encrypted function pointer, even when the encryption support was disabled or not being used. Note: we don't define S_ENCRYPTED to 0 if CONFIG_FS_ENCRYPTION is disabled because we want to continue to return an error if an encrypted file is accessed without encryption support, rather than pretending that it is unencrypted. Reviewed-by: Chao Yu Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/ext4/inode.c | 7 +++++-- fs/ext4/super.c | 8 ++++++-- fs/f2fs/f2fs.h | 1 + fs/f2fs/inode.c | 5 ++++- fs/ubifs/ioctl.c | 5 ++++- fs/ubifs/xattr.c | 1 + include/linux/fs.h | 2 ++ 7 files changed, 23 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 31db875bc7a1..d5a471939fbc 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4589,10 +4589,13 @@ void ext4_set_inode_flags(struct inode *inode) new_fl |= S_DIRSYNC; if (test_opt(inode->i_sb, DAX) && S_ISREG(inode->i_mode) && !ext4_should_journal_data(inode) && !ext4_has_inline_data(inode) && - !ext4_encrypted_inode(inode)) + !(flags & EXT4_ENCRYPT_FL)) new_fl |= S_DAX; + if (flags & EXT4_ENCRYPT_FL) + new_fl |= S_ENCRYPTED; inode_set_flags(inode, new_fl, - S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_DAX); + S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC|S_DAX| + S_ENCRYPTED); } static blkcnt_t ext4_inode_blocks(struct ext4_inode *raw_inode, diff --git a/fs/ext4/super.c b/fs/ext4/super.c index b104096fce9e..dcfb19539871 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1181,7 +1181,8 @@ static int ext4_set_context(struct inode *inode, const void *ctx, size_t len, ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA); /* - * Update inode->i_flags - e.g. S_DAX may get disabled + * Update inode->i_flags - S_ENCRYPTED will be enabled, + * S_DAX may be disabled */ ext4_set_inode_flags(inode); } @@ -1206,7 +1207,10 @@ retry: ctx, len, 0); if (!res) { ext4_set_inode_flag(inode, EXT4_INODE_ENCRYPT); - /* Update inode->i_flags - e.g. S_DAX may get disabled */ + /* + * Update inode->i_flags - S_ENCRYPTED will be enabled, + * S_DAX may be disabled + */ ext4_set_inode_flags(inode); res = ext4_mark_inode_dirty(handle, inode); if (res) diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index fc53aebaf3ae..34d6e9ae8422 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -2947,6 +2947,7 @@ static inline void f2fs_set_encrypted_inode(struct inode *inode) { #ifdef CONFIG_F2FS_FS_ENCRYPTION file_set_encrypt(inode); + inode->i_flags |= S_ENCRYPTED; #endif } diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 50c88e37ed66..53fb08810ee9 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -43,8 +43,11 @@ void f2fs_set_inode_flags(struct inode *inode) new_fl |= S_NOATIME; if (flags & FS_DIRSYNC_FL) new_fl |= S_DIRSYNC; + if (f2fs_encrypted_inode(inode)) + new_fl |= S_ENCRYPTED; inode_set_flags(inode, new_fl, - S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); + S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC| + S_ENCRYPTED); } static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index fdc311246807..0164bcc827f8 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -38,7 +38,8 @@ void ubifs_set_inode_flags(struct inode *inode) { unsigned int flags = ubifs_inode(inode)->flags; - inode->i_flags &= ~(S_SYNC | S_APPEND | S_IMMUTABLE | S_DIRSYNC); + inode->i_flags &= ~(S_SYNC | S_APPEND | S_IMMUTABLE | S_DIRSYNC | + S_ENCRYPTED); if (flags & UBIFS_SYNC_FL) inode->i_flags |= S_SYNC; if (flags & UBIFS_APPEND_FL) @@ -47,6 +48,8 @@ void ubifs_set_inode_flags(struct inode *inode) inode->i_flags |= S_IMMUTABLE; if (flags & UBIFS_DIRSYNC_FL) inode->i_flags |= S_DIRSYNC; + if (flags & UBIFS_CRYPT_FL) + inode->i_flags |= S_ENCRYPTED; } /* diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index c13eae819cbc..5ddc89d564fd 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c @@ -170,6 +170,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host, err = ubifs_jnl_update(c, host, nm, inode, 0, 1); if (err) goto out_cancel; + ubifs_set_inode_flags(host); mutex_unlock(&host_ui->ui_mutex); ubifs_release_budget(c, &req); diff --git a/include/linux/fs.h b/include/linux/fs.h index 339e73742e73..055d2fbf8eca 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1853,6 +1853,7 @@ struct super_operations { #else #define S_DAX 0 /* Make all the DAX code disappear */ #endif +#define S_ENCRYPTED 16384 /* Encrypted file (using fs/crypto/) */ /* * Note that nosuid etc flags are inode-specific: setting some file-system @@ -1892,6 +1893,7 @@ static inline bool sb_rdonly(const struct super_block *sb) { return sb->s_flags #define IS_AUTOMOUNT(inode) ((inode)->i_flags & S_AUTOMOUNT) #define IS_NOSEC(inode) ((inode)->i_flags & S_NOSEC) #define IS_DAX(inode) ((inode)->i_flags & S_DAX) +#define IS_ENCRYPTED(inode) ((inode)->i_flags & S_ENCRYPTED) #define IS_WHITEOUT(inode) (S_ISCHR(inode->i_mode) && \ (inode)->i_rdev == WHITEOUT_DEV) -- cgit v1.2.3-59-g8ed1b From e0428a266d5a29a3c2eec287fcd49be9e8e2468e Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:36 -0700 Subject: fscrypt: switch from ->is_encrypted() to IS_ENCRYPTED() IS_ENCRYPTED() now gives the same information as i_sb->s_cop->is_encrypted() but is more efficient, since IS_ENCRYPTED() is just a simple flag check. Prepare to remove ->is_encrypted() by switching all callers to IS_ENCRYPTED(). Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/crypto.c | 2 +- fs/crypto/fname.c | 3 +-- fs/crypto/keyinfo.c | 2 +- fs/crypto/policy.c | 6 +++--- include/linux/fscrypt_notsupp.h | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index c7835df7e7b8..608f6bbe0f31 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -340,7 +340,7 @@ static int fscrypt_d_revalidate(struct dentry *dentry, unsigned int flags) return -ECHILD; dir = dget_parent(dentry); - if (!d_inode(dir)->i_sb->s_cop->is_encrypted(d_inode(dir))) { + if (!IS_ENCRYPTED(d_inode(dir))) { dput(dir); return 0; } diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index ad9f814fdead..2878289b3ed2 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -382,8 +382,7 @@ int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, memset(fname, 0, sizeof(struct fscrypt_name)); fname->usr_fname = iname; - if (!dir->i_sb->s_cop->is_encrypted(dir) || - fscrypt_is_dot_dotdot(iname)) { + if (!IS_ENCRYPTED(dir) || fscrypt_is_dot_dotdot(iname)) { fname->disk_name.name = (unsigned char *)iname->name; fname->disk_name.len = iname->len; return 0; diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c index 018c588c7ac3..236a68d4ea72 100644 --- a/fs/crypto/keyinfo.c +++ b/fs/crypto/keyinfo.c @@ -268,7 +268,7 @@ int fscrypt_get_encryption_info(struct inode *inode) res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); if (res < 0) { if (!fscrypt_dummy_context_enabled(inode) || - inode->i_sb->s_cop->is_encrypted(inode)) + IS_ENCRYPTED(inode)) return res; /* Fake up a context for an unencrypted directory */ memset(&ctx, 0, sizeof(ctx)); diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index ce07a86200f3..6a63b8a0d46c 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -109,7 +109,7 @@ int fscrypt_ioctl_get_policy(struct file *filp, void __user *arg) struct fscrypt_policy policy; int res; - if (!inode->i_sb->s_cop->is_encrypted(inode)) + if (!IS_ENCRYPTED(inode)) return -ENODATA; res = inode->i_sb->s_cop->get_context(inode, &ctx, sizeof(ctx)); @@ -166,11 +166,11 @@ int fscrypt_has_permitted_context(struct inode *parent, struct inode *child) return 1; /* No restrictions if the parent directory is unencrypted */ - if (!cops->is_encrypted(parent)) + if (!IS_ENCRYPTED(parent)) return 1; /* Encrypted directories must not contain unencrypted files */ - if (!cops->is_encrypted(child)) + if (!IS_ENCRYPTED(child)) return 0; /* diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 2d0b6960831e..7b390e356f7f 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -100,7 +100,7 @@ static inline int fscrypt_setup_filename(struct inode *dir, const struct qstr *iname, int lookup, struct fscrypt_name *fname) { - if (dir->i_sb->s_cop->is_encrypted(dir)) + if (IS_ENCRYPTED(dir)) return -EOPNOTSUPP; memset(fname, 0, sizeof(struct fscrypt_name)); -- cgit v1.2.3-59-g8ed1b From f7293e48bb1d0c482cd706deb1256a6be718f4f5 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:37 -0700 Subject: fscrypt: remove ->is_encrypted() Now that all callers of fscrypt_operations.is_encrypted() have been switched to IS_ENCRYPTED(), remove ->is_encrypted(). Reviewed-by: Chao Yu Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/ext4/super.c | 2 -- fs/f2fs/super.c | 2 -- fs/ubifs/crypto.c | 1 - fs/ubifs/super.c | 1 - fs/ubifs/ubifs.h | 9 ++------- include/linux/fscrypt.h | 1 - 6 files changed, 2 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index dcfb19539871..bc63cdf194e3 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1241,13 +1241,11 @@ static const struct fscrypt_operations ext4_cryptops = { .get_context = ext4_get_context, .set_context = ext4_set_context, .dummy_context = ext4_dummy_context, - .is_encrypted = ext4_encrypted_inode, .empty_dir = ext4_empty_dir, .max_namelen = ext4_max_namelen, }; #else static const struct fscrypt_operations ext4_cryptops = { - .is_encrypted = ext4_encrypted_inode, }; #endif diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 89f61eb3d167..1cb41f711ab8 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -1594,13 +1594,11 @@ static const struct fscrypt_operations f2fs_cryptops = { .key_prefix = "f2fs:", .get_context = f2fs_get_context, .set_context = f2fs_set_context, - .is_encrypted = f2fs_encrypted_inode, .empty_dir = f2fs_empty_dir, .max_namelen = f2fs_max_namelen, }; #else static const struct fscrypt_operations f2fs_cryptops = { - .is_encrypted = f2fs_encrypted_inode, }; #endif diff --git a/fs/ubifs/crypto.c b/fs/ubifs/crypto.c index 114ba455bac3..8880fa7733d8 100644 --- a/fs/ubifs/crypto.c +++ b/fs/ubifs/crypto.c @@ -87,7 +87,6 @@ const struct fscrypt_operations ubifs_crypt_operations = { .key_prefix = "ubifs:", .get_context = ubifs_crypt_get_context, .set_context = ubifs_crypt_set_context, - .is_encrypted = __ubifs_crypt_is_encrypted, .empty_dir = ubifs_crypt_empty_dir, .max_namelen = ubifs_crypt_max_namelen, }; diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 5496b17b959c..adaca6088836 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -2009,7 +2009,6 @@ static struct ubifs_info *alloc_ubifs_info(struct ubi_volume_desc *ubi) #ifndef CONFIG_UBIFS_FS_ENCRYPTION const struct fscrypt_operations ubifs_crypt_operations = { - .is_encrypted = __ubifs_crypt_is_encrypted, }; #endif diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index 6a346d4af98f..63c7468147eb 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -1834,18 +1834,13 @@ int ubifs_decrypt(const struct inode *inode, struct ubifs_data_node *dn, extern const struct fscrypt_operations ubifs_crypt_operations; -static inline bool __ubifs_crypt_is_encrypted(struct inode *inode) +static inline bool ubifs_crypt_is_encrypted(const struct inode *inode) { - struct ubifs_inode *ui = ubifs_inode(inode); + const struct ubifs_inode *ui = ubifs_inode(inode); return ui->flags & UBIFS_CRYPT_FL; } -static inline bool ubifs_crypt_is_encrypted(const struct inode *inode) -{ - return __ubifs_crypt_is_encrypted((struct inode *)inode); -} - /* Normal UBIFS messages */ __printf(2, 3) void ubifs_msg(const struct ubifs_info *c, const char *fmt, ...); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index bfc962e62078..d58e6a90cfe4 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -81,7 +81,6 @@ struct fscrypt_operations { int (*get_context)(struct inode *, void *, size_t); int (*set_context)(struct inode *, const void *, size_t, void *); bool (*dummy_context)(struct inode *); - bool (*is_encrypted)(struct inode *); bool (*empty_dir)(struct inode *); unsigned (*max_namelen)(struct inode *); }; -- cgit v1.2.3-59-g8ed1b From d293c3e4e07334d761c88df9d68b3aa800a83dd9 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:39 -0700 Subject: fscrypt: new helper function - fscrypt_require_key() Add a helper function which checks if an inode is encrypted, and if so, tries to set up its encryption key. This is a pattern which is duplicated in multiple places in each of ext4, f2fs, and ubifs --- for example, when a regular file is asked to be opened or truncated. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- include/linux/fscrypt.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'include') diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index d58e6a90cfe4..b3e2a5f93415 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -151,5 +151,30 @@ static inline bool fscrypt_has_encryption_key(const struct inode *inode) #include #endif /* __FS_HAS_ENCRYPTION */ +/** + * fscrypt_require_key - require an inode's encryption key + * @inode: the inode we need the key for + * + * If the inode is encrypted, set up its encryption key if not already done. + * Then require that the key be present and return -ENOKEY otherwise. + * + * No locks are needed, and the key will live as long as the struct inode --- so + * it won't go away from under you. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + * if a problem occurred while setting up the encryption key. + */ +static inline int fscrypt_require_key(struct inode *inode) +{ + if (IS_ENCRYPTED(inode)) { + int err = fscrypt_get_encryption_info(inode); + + if (err) + return err; + if (!fscrypt_has_encryption_key(inode)) + return -ENOKEY; + } + return 0; +} #endif /* _LINUX_FSCRYPT_H */ -- cgit v1.2.3-59-g8ed1b From efcc7ae2c9172d9a7ae94afdaf066a7abf0b9a90 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:40 -0700 Subject: fscrypt: new helper function - fscrypt_file_open() Add a helper function which prepares to open a regular file which may be encrypted. It handles setting up the file's encryption key, then checking that the file's encryption policy matches that of its parent directory (if the parent directory is encrypted). It may be set as the ->open() method or it can be called from another ->open() method. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/Makefile | 2 +- fs/crypto/hooks.c | 49 +++++++++++++++++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 9 ++++++++ include/linux/fscrypt_supp.h | 3 +++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 fs/crypto/hooks.c (limited to 'include') diff --git a/fs/crypto/Makefile b/fs/crypto/Makefile index 9f6607f17b53..cb496989a6b6 100644 --- a/fs/crypto/Makefile +++ b/fs/crypto/Makefile @@ -1,4 +1,4 @@ obj-$(CONFIG_FS_ENCRYPTION) += fscrypto.o -fscrypto-y := crypto.o fname.o policy.o keyinfo.o +fscrypto-y := crypto.o fname.o hooks.o keyinfo.o policy.o fscrypto-$(CONFIG_BLOCK) += bio.o diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c new file mode 100644 index 000000000000..069088e91ea9 --- /dev/null +++ b/fs/crypto/hooks.c @@ -0,0 +1,49 @@ +/* + * fs/crypto/hooks.c + * + * Encryption hooks for higher-level filesystem operations. + */ + +#include +#include "fscrypt_private.h" + +/** + * fscrypt_file_open - prepare to open a possibly-encrypted regular file + * @inode: the inode being opened + * @filp: the struct file being set up + * + * Currently, an encrypted regular file can only be opened if its encryption key + * is available; access to the raw encrypted contents is not supported. + * Therefore, we first set up the inode's encryption key (if not already done) + * and return an error if it's unavailable. + * + * We also verify that if the parent directory (from the path via which the file + * is being opened) is encrypted, then the inode being opened uses the same + * encryption policy. This is needed as part of the enforcement that all files + * in an encrypted directory tree use the same encryption policy, as a + * protection against certain types of offline attacks. Note that this check is + * needed even when opening an *unencrypted* file, since it's forbidden to have + * an unencrypted file in an encrypted directory. + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + */ +int fscrypt_file_open(struct inode *inode, struct file *filp) +{ + int err; + struct dentry *dir; + + err = fscrypt_require_key(inode); + if (err) + return err; + + dir = dget_parent(file_dentry(filp)); + if (IS_ENCRYPTED(d_inode(dir)) && + !fscrypt_has_permitted_context(d_inode(dir), inode)) { + pr_warn_ratelimited("fscrypt: inconsistent encryption contexts: %lu/%lu", + d_inode(dir)->i_ino, inode->i_ino); + err = -EPERM; + } + dput(dir); + return err; +} +EXPORT_SYMBOL_GPL(fscrypt_file_open); diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 7b390e356f7f..162da6517ac4 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -177,4 +177,13 @@ static inline int fscrypt_zeroout_range(const struct inode *inode, pgoff_t lblk, return -EOPNOTSUPP; } +/* hooks.c */ + +static inline int fscrypt_file_open(struct inode *inode, struct file *filp) +{ + if (IS_ENCRYPTED(inode)) + return -EOPNOTSUPP; + return 0; +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 5a90e5ef4687..fd2f6decaee4 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -143,4 +143,7 @@ extern void fscrypt_pullback_bio_page(struct page **, bool); extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, unsigned int); +/* hooks.c */ +extern int fscrypt_file_open(struct inode *inode, struct file *filp); + #endif /* _LINUX_FSCRYPT_SUPP_H */ -- cgit v1.2.3-59-g8ed1b From 0ea87a9644ebb5c9a3b100585d10533366de3269 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:41 -0700 Subject: fscrypt: new helper function - fscrypt_prepare_link() Introduce a helper function which prepares to link an inode into a possibly-encrypted directory. It handles setting up the target directory's encryption key, then verifying that the link won't violate the constraint that all files in an encrypted directory tree use the same encryption policy. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/hooks.c | 15 +++++++++++++++ include/linux/fscrypt.h | 27 +++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 6 ++++++ include/linux/fscrypt_supp.h | 1 + 4 files changed, 49 insertions(+) (limited to 'include') diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 069088e91ea9..8b90217320dd 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -47,3 +47,18 @@ int fscrypt_file_open(struct inode *inode, struct file *filp) return err; } EXPORT_SYMBOL_GPL(fscrypt_file_open); + +int __fscrypt_prepare_link(struct inode *inode, struct inode *dir) +{ + int err; + + err = fscrypt_require_key(dir); + if (err) + return err; + + if (!fscrypt_has_permitted_context(dir, inode)) + return -EPERM; + + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_link); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index b3e2a5f93415..9c9a53f99327 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -177,4 +177,31 @@ static inline int fscrypt_require_key(struct inode *inode) return 0; } +/** + * fscrypt_prepare_link - prepare to link an inode into a possibly-encrypted directory + * @old_dentry: an existing dentry for the inode being linked + * @dir: the target directory + * @dentry: negative dentry for the target filename + * + * A new link can only be added to an encrypted directory if the directory's + * encryption key is available --- since otherwise we'd have no way to encrypt + * the filename. Therefore, we first set up the directory's encryption key (if + * not already done) and return an error if it's unavailable. + * + * We also verify that the link will not violate the constraint that all files + * in an encrypted directory tree use the same encryption policy. + * + * Return: 0 on success, -ENOKEY if the directory's encryption key is missing, + * -EPERM if the link would result in an inconsistent encryption policy, or + * another -errno code. + */ +static inline int fscrypt_prepare_link(struct dentry *old_dentry, + struct inode *dir, + struct dentry *dentry) +{ + if (IS_ENCRYPTED(dir)) + return __fscrypt_prepare_link(d_inode(old_dentry), dir); + return 0; +} + #endif /* _LINUX_FSCRYPT_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 162da6517ac4..d7d1039eb6b5 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -186,4 +186,10 @@ static inline int fscrypt_file_open(struct inode *inode, struct file *filp) return 0; } +static inline int __fscrypt_prepare_link(struct inode *inode, + struct inode *dir) +{ + return -EOPNOTSUPP; +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index fd2f6decaee4..80706283da75 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -145,5 +145,6 @@ extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, /* hooks.c */ extern int fscrypt_file_open(struct inode *inode, struct file *filp); +extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir); #endif /* _LINUX_FSCRYPT_SUPP_H */ -- cgit v1.2.3-59-g8ed1b From 94b26f3672a0e41025104c7e46943917544e1c87 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:42 -0700 Subject: fscrypt: new helper function - fscrypt_prepare_rename() Introduce a helper function which prepares to rename a file into a possibly encrypted directory. It handles loading the encryption keys for the source and target directories if needed, and it handles enforcing that if the target directory (and the source directory for a cross-rename) is encrypted, then the file being moved into the directory has the same encryption policy as its containing directory. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/hooks.c | 30 ++++++++++++++++++++++++++++++ include/linux/fscrypt.h | 33 +++++++++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 9 +++++++++ include/linux/fscrypt_supp.h | 5 +++++ 4 files changed, 77 insertions(+) (limited to 'include') diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 8b90217320dd..822cb78f9b45 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -62,3 +62,33 @@ int __fscrypt_prepare_link(struct inode *inode, struct inode *dir) return 0; } EXPORT_SYMBOL_GPL(__fscrypt_prepare_link); + +int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + int err; + + err = fscrypt_require_key(old_dir); + if (err) + return err; + + err = fscrypt_require_key(new_dir); + if (err) + return err; + + if (old_dir != new_dir) { + if (IS_ENCRYPTED(new_dir) && + !fscrypt_has_permitted_context(new_dir, + d_inode(old_dentry))) + return -EPERM; + + if ((flags & RENAME_EXCHANGE) && + IS_ENCRYPTED(old_dir) && + !fscrypt_has_permitted_context(old_dir, + d_inode(new_dentry))) + return -EPERM; + } + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_rename); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 9c9a53f99327..c422367baed9 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -204,4 +204,37 @@ static inline int fscrypt_prepare_link(struct dentry *old_dentry, return 0; } +/** + * fscrypt_prepare_rename - prepare for a rename between possibly-encrypted directories + * @old_dir: source directory + * @old_dentry: dentry for source file + * @new_dir: target directory + * @new_dentry: dentry for target location (may be negative unless exchanging) + * @flags: rename flags (we care at least about %RENAME_EXCHANGE) + * + * Prepare for ->rename() where the source and/or target directories may be + * encrypted. A new link can only be added to an encrypted directory if the + * directory's encryption key is available --- since otherwise we'd have no way + * to encrypt the filename. A rename to an existing name, on the other hand, + * *is* cryptographically possible without the key. However, we take the more + * conservative approach and just forbid all no-key renames. + * + * We also verify that the rename will not violate the constraint that all files + * in an encrypted directory tree use the same encryption policy. + * + * Return: 0 on success, -ENOKEY if an encryption key is missing, -EPERM if the + * rename would cause inconsistent encryption policies, or another -errno code. + */ +static inline int fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags) +{ + if (IS_ENCRYPTED(old_dir) || IS_ENCRYPTED(new_dir)) + return __fscrypt_prepare_rename(old_dir, old_dentry, + new_dir, new_dentry, flags); + return 0; +} + #endif /* _LINUX_FSCRYPT_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index d7d1039eb6b5..6af378d8126e 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -192,4 +192,13 @@ static inline int __fscrypt_prepare_link(struct inode *inode, return -EOPNOTSUPP; } +static inline int __fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags) +{ + return -EOPNOTSUPP; +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 80706283da75..40f35073145f 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -146,5 +146,10 @@ extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, /* hooks.c */ extern int fscrypt_file_open(struct inode *inode, struct file *filp); extern int __fscrypt_prepare_link(struct inode *inode, struct inode *dir); +extern int __fscrypt_prepare_rename(struct inode *old_dir, + struct dentry *old_dentry, + struct inode *new_dir, + struct dentry *new_dentry, + unsigned int flags); #endif /* _LINUX_FSCRYPT_SUPP_H */ -- cgit v1.2.3-59-g8ed1b From 32c3cf028e747d1e9cf0679b16dcbe3794fe4853 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:43 -0700 Subject: fscrypt: new helper function - fscrypt_prepare_lookup() Introduce a helper function which prepares to look up the given dentry in the given directory. If the directory is encrypted, it handles loading the directory's encryption key, setting the dentry's ->d_op to fscrypt_d_ops, and setting DCACHE_ENCRYPTED_WITH_KEY if the directory's encryption key is available. Note: once all filesystems switch over to this, we'll be able to move fscrypt_d_ops and fscrypt_set_encrypted_dentry() to fscrypt_private.h. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- fs/crypto/hooks.c | 18 ++++++++++++++++++ include/linux/fscrypt.h | 28 ++++++++++++++++++++++++++++ include/linux/fscrypt_notsupp.h | 6 ++++++ include/linux/fscrypt_supp.h | 1 + 4 files changed, 53 insertions(+) (limited to 'include') diff --git a/fs/crypto/hooks.c b/fs/crypto/hooks.c index 822cb78f9b45..9f5fb2eb9cf7 100644 --- a/fs/crypto/hooks.c +++ b/fs/crypto/hooks.c @@ -92,3 +92,21 @@ int __fscrypt_prepare_rename(struct inode *old_dir, struct dentry *old_dentry, return 0; } EXPORT_SYMBOL_GPL(__fscrypt_prepare_rename); + +int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry) +{ + int err = fscrypt_get_encryption_info(dir); + + if (err) + return err; + + if (fscrypt_has_encryption_key(dir)) { + spin_lock(&dentry->d_lock); + dentry->d_flags |= DCACHE_ENCRYPTED_WITH_KEY; + spin_unlock(&dentry->d_lock); + } + + d_set_d_op(dentry, &fscrypt_d_ops); + return 0; +} +EXPORT_SYMBOL_GPL(__fscrypt_prepare_lookup); diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index c422367baed9..2327859c8cd2 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -237,4 +237,32 @@ static inline int fscrypt_prepare_rename(struct inode *old_dir, return 0; } +/** + * fscrypt_prepare_lookup - prepare to lookup a name in a possibly-encrypted directory + * @dir: directory being searched + * @dentry: filename being looked up + * @flags: lookup flags + * + * Prepare for ->lookup() in a directory which may be encrypted. Lookups can be + * done with or without the directory's encryption key; without the key, + * filenames are presented in encrypted form. Therefore, we'll try to set up + * the directory's encryption key, but even without it the lookup can continue. + * + * To allow invalidating stale dentries if the directory's encryption key is + * added later, we also install a custom ->d_revalidate() method and use the + * DCACHE_ENCRYPTED_WITH_KEY flag to indicate whether a given dentry is a + * plaintext name (flag set) or a ciphertext name (flag cleared). + * + * Return: 0 on success, -errno if a problem occurred while setting up the + * encryption key + */ +static inline int fscrypt_prepare_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags) +{ + if (IS_ENCRYPTED(dir)) + return __fscrypt_prepare_lookup(dir, dentry); + return 0; +} + #endif /* _LINUX_FSCRYPT_H */ diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 6af378d8126e..c4c6bf2c390e 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -201,4 +201,10 @@ static inline int __fscrypt_prepare_rename(struct inode *old_dir, return -EOPNOTSUPP; } +static inline int __fscrypt_prepare_lookup(struct inode *dir, + struct dentry *dentry) +{ + return -EOPNOTSUPP; +} + #endif /* _LINUX_FSCRYPT_NOTSUPP_H */ diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 40f35073145f..2db5e9706f60 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -151,5 +151,6 @@ extern int __fscrypt_prepare_rename(struct inode *old_dir, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags); +extern int __fscrypt_prepare_lookup(struct inode *dir, struct dentry *dentry); #endif /* _LINUX_FSCRYPT_SUPP_H */ -- cgit v1.2.3-59-g8ed1b From 815dac33b27da9efce0f6008272e69f2a1ba1a33 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 9 Oct 2017 12:15:44 -0700 Subject: fscrypt: new helper function - fscrypt_prepare_setattr() Introduce a helper function for filesystems to call when processing ->setattr() on a possibly-encrypted inode. It handles enforcing that an encrypted file can only be truncated if its encryption key is available. Acked-by: Dave Chinner Signed-off-by: Eric Biggers Signed-off-by: Theodore Ts'o --- include/linux/fscrypt.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'include') diff --git a/include/linux/fscrypt.h b/include/linux/fscrypt.h index 2327859c8cd2..53437bfdfcbc 100644 --- a/include/linux/fscrypt.h +++ b/include/linux/fscrypt.h @@ -265,4 +265,29 @@ static inline int fscrypt_prepare_lookup(struct inode *dir, return 0; } +/** + * fscrypt_prepare_setattr - prepare to change a possibly-encrypted inode's attributes + * @dentry: dentry through which the inode is being changed + * @attr: attributes to change + * + * Prepare for ->setattr() on a possibly-encrypted inode. On an encrypted file, + * most attribute changes are allowed even without the encryption key. However, + * without the encryption key we do have to forbid truncates. This is needed + * because the size being truncated to may not be a multiple of the filesystem + * block size, and in that case we'd have to decrypt the final block, zero the + * portion past i_size, and re-encrypt it. (We *could* allow truncating to a + * filesystem block boundary, but it's simpler to just forbid all truncates --- + * and we already forbid all other contents modifications without the key.) + * + * Return: 0 on success, -ENOKEY if the key is missing, or another -errno code + * if a problem occurred while setting up the encryption key. + */ +static inline int fscrypt_prepare_setattr(struct dentry *dentry, + struct iattr *attr) +{ + if (attr->ia_valid & ATTR_SIZE) + return fscrypt_require_key(d_inode(dentry)); + return 0; +} + #endif /* _LINUX_FSCRYPT_H */ -- cgit v1.2.3-59-g8ed1b From cf3431bba1df3b4f25b04900cb804d7e6e5eb6a9 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 17 Oct 2017 09:11:24 +0200 Subject: scsi: scsi_error: Handle power-on reset unit attention As per SAM there is a status precedence, with any sense code 29/XX taking second place just after an ACA ACTIVE status. Additionally, each target might prefer to not queue any unit attention conditions, but just report one. Due to the above, this will be that one with the highest precedence. This results in the sense code 29/XX effectively overwriting any other unit attention. Hence we should report the power-on reset to userland so that it can take appropriate action. Signed-off-by: Hannes Reinecke Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_error.c | 6 ++++++ drivers/scsi/scsi_lib.c | 4 ++++ include/scsi/scsi_device.h | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 5086489dac97..d670027f598f 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -403,6 +403,12 @@ static void scsi_report_sense(struct scsi_device *sdev, "threshold.\n"); } + if (sshdr->asc == 0x29) { + evt_type = SDEV_EVT_POWER_ON_RESET_OCCURRED; + sdev_printk(KERN_WARNING, sdev, + "Power-on or device reset occurred\n"); + } + if (sshdr->asc == 0x2a && sshdr->ascq == 0x01) { evt_type = SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED; sdev_printk(KERN_WARNING, sdev, diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index c72b97a74906..e5fcfa867045 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -2743,6 +2743,9 @@ static void scsi_evt_emit(struct scsi_device *sdev, struct scsi_event *evt) case SDEV_EVT_ALUA_STATE_CHANGE_REPORTED: envp[idx++] = "SDEV_UA=ASYMMETRIC_ACCESS_STATE_CHANGED"; break; + case SDEV_EVT_POWER_ON_RESET_OCCURRED: + envp[idx++] = "SDEV_UA=POWER_ON_RESET_OCCURRED"; + break; default: /* do nothing */ break; @@ -2847,6 +2850,7 @@ struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type, case SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED: case SDEV_EVT_LUN_CHANGE_REPORTED: case SDEV_EVT_ALUA_STATE_CHANGE_REPORTED: + case SDEV_EVT_POWER_ON_RESET_OCCURRED: default: /* do nothing */ break; diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index 82e93ee94708..d68985c366b0 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -64,9 +64,10 @@ enum scsi_device_event { SDEV_EVT_MODE_PARAMETER_CHANGE_REPORTED, /* 2A 01 UA reported */ SDEV_EVT_LUN_CHANGE_REPORTED, /* 3F 0E UA reported */ SDEV_EVT_ALUA_STATE_CHANGE_REPORTED, /* 2A 06 UA reported */ + SDEV_EVT_POWER_ON_RESET_OCCURRED, /* 29 00 UA reported */ SDEV_EVT_FIRST = SDEV_EVT_MEDIA_CHANGE, - SDEV_EVT_LAST = SDEV_EVT_ALUA_STATE_CHANGE_REPORTED, + SDEV_EVT_LAST = SDEV_EVT_POWER_ON_RESET_OCCURRED, SDEV_EVT_MAXBITS = SDEV_EVT_LAST + 1 }; -- cgit v1.2.3-59-g8ed1b From bbb1cc050890dad2ad8747a23b9f22a53e726c9a Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 24 Aug 2017 16:13:18 -0600 Subject: usb: gadget: Add kerneldoc for some neglected structure fields A couple of structures in have incomplete kerneldoc comments, leading to these warnings in the docs build: ./include/linux/usb/gadget.h:230: warning: No description found for parameter 'claimed' ./include/linux/usb/gadget.h:230: warning: No description found for parameter 'enabled' ./include/linux/usb/gadget.h:412: warning: No description found for parameter 'quirk_altset_not_supp' ./include/linux/usb/gadget.h:412: warning: No description found for parameter 'quirk_stall_not_supp' ./include/linux/usb/gadget.h:412: warning: No description found for parameter 'quirk_zlp_not_supp' Document those fields to make the warnings go away. Signed-off-by: Jonathan Corbet Signed-off-by: Felipe Balbi --- include/linux/usb/gadget.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 21468a722c4a..523e5a2b5015 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -188,6 +188,8 @@ struct usb_ep_caps { * @ops: Function pointers used to access hardware-specific operations. * @ep_list:the gadget's ep_list holds all of its endpoints * @caps:The structure describing types and directions supported by endoint. + * @enabled: The current endpoint enabled/disabled state. + * @claimed: True if this endpoint is claimed by a function. * @maxpacket:The maximum packet size used on this endpoint. The initial * value can sometimes be reduced (hardware allowing), according to * the endpoint descriptor used to configure the endpoint. @@ -349,6 +351,9 @@ struct usb_gadget_ops { * or B-Peripheral wants to take host role. * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to * MaxPacketSize. + * @quirk_altset_not_supp: UDC controller doesn't support alt settings. + * @quirk_stall_not_supp: UDC controller doesn't support stalling. + * @quirk_zlp_not_supp: UDC controller doesn't support ZLP. * @quirk_avoids_skb_reserve: udc/platform wants to avoid skb_reserve() in * u_ether.c to improve performance. * @is_selfpowered: if the gadget is self-powered. -- cgit v1.2.3-59-g8ed1b From 0f38672c629b79fa2b929d2c391bc063a08279eb Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 3 Oct 2017 20:09:14 +0900 Subject: usb: renesas_usbhs: add support for R-Car D3 This patch adds support for R-Car D3. This SoC needs to release the PLL reset by the UGCTRL register. So, since this is not the same as other R-Car Gen3 SoCs, this patch adds a new type as "USBHS_TYPE_RCAR_GEN3_WITH_PLL". Acked-by: Rob Herring Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- .../devicetree/bindings/usb/renesas_usbhs.txt | 1 + drivers/usb/renesas_usbhs/common.c | 10 ++++- drivers/usb/renesas_usbhs/rcar3.c | 48 ++++++++++++++++++++++ drivers/usb/renesas_usbhs/rcar3.h | 1 + include/linux/usb/renesas_usbhs.h | 5 ++- 5 files changed, 62 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt index 9e18e000339e..e79f6e43061a 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt @@ -10,6 +10,7 @@ Required properties: - "renesas,usbhs-r8a7794" for r8a7794 (R-Car E2) compatible device - "renesas,usbhs-r8a7795" for r8a7795 (R-Car H3) compatible device - "renesas,usbhs-r8a7796" for r8a7796 (R-Car M3-W) compatible device + - "renesas,usbhs-r8a77995" for r8a77995 (R-Car D3) compatible device - "renesas,rcar-gen2-usbhs" for R-Car Gen2 compatible device - "renesas,rcar-gen3-usbhs" for R-Car Gen3 compatible device diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 2a860e496b4f..3e92a784c5c3 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -485,6 +485,10 @@ static const struct of_device_id usbhs_of_match[] = { .compatible = "renesas,usbhs-r8a7796", .data = (void *)USBHS_TYPE_RCAR_GEN3, }, + { + .compatible = "renesas,usbhs-r8a77995", + .data = (void *)USBHS_TYPE_RCAR_GEN3_WITH_PLL, + }, { .compatible = "renesas,rcar-gen2-usbhs", .data = (void *)USBHS_TYPE_RCAR_GEN2, @@ -519,7 +523,8 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) dparam->enable_gpio = gpio; if (dparam->type == USBHS_TYPE_RCAR_GEN2 || - dparam->type == USBHS_TYPE_RCAR_GEN3) { + dparam->type == USBHS_TYPE_RCAR_GEN3 || + dparam->type == USBHS_TYPE_RCAR_GEN3_WITH_PLL) { dparam->has_usb_dmac = 1; dparam->pipe_configs = usbhsc_new_pipe; dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe); @@ -584,6 +589,9 @@ static int usbhs_probe(struct platform_device *pdev) case USBHS_TYPE_RCAR_GEN3: priv->pfunc = usbhs_rcar3_ops; break; + case USBHS_TYPE_RCAR_GEN3_WITH_PLL: + priv->pfunc = usbhs_rcar3_with_pll_ops; + break; default: if (!info->platform_callback.get_id) { dev_err(&pdev->dev, "no platform callbacks"); diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c index 02b67abfc2a1..f436e9d51127 100644 --- a/drivers/usb/renesas_usbhs/rcar3.c +++ b/drivers/usb/renesas_usbhs/rcar3.c @@ -15,24 +15,39 @@ #include "rcar3.h" #define LPSTS 0x102 +#define UGCTRL 0x180 /* 32-bit register */ #define UGCTRL2 0x184 /* 32-bit register */ +#define UGSTS 0x188 /* 32-bit register */ /* Low Power Status register (LPSTS) */ #define LPSTS_SUSPM 0x4000 +/* R-Car D3 only: USB General control register (UGCTRL) */ +#define UGCTRL_PLLRESET 0x00000001 +#define UGCTRL_CONNECT 0x00000004 + /* * USB General control register 2 (UGCTRL2) * Remarks: bit[31:11] and bit[9:6] should be 0 */ #define UGCTRL2_RESERVED_3 0x00000001 /* bit[3:0] should be B'0001 */ +#define UGCTRL2_USB0SEL_HSUSB 0x00000020 #define UGCTRL2_USB0SEL_OTG 0x00000030 #define UGCTRL2_VBUSSEL 0x00000400 +/* R-Car D3 only: USB General status register (UGSTS) */ +#define UGSTS_LOCK 0x00000100 + static void usbhs_write32(struct usbhs_priv *priv, u32 reg, u32 data) { iowrite32(data, priv->base + reg); } +static u32 usbhs_read32(struct usbhs_priv *priv, u32 reg) +{ + return ioread32(priv->base + reg); +} + static int usbhs_rcar3_power_ctrl(struct platform_device *pdev, void __iomem *base, int enable) { @@ -52,6 +67,34 @@ static int usbhs_rcar3_power_ctrl(struct platform_device *pdev, return 0; } +/* R-Car D3 needs to release UGCTRL.PLLRESET */ +static int usbhs_rcar3_power_and_pll_ctrl(struct platform_device *pdev, + void __iomem *base, int enable) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + u32 val; + int timeout = 1000; + + if (enable) { + usbhs_write32(priv, UGCTRL, 0); /* release PLLRESET */ + usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | + UGCTRL2_USB0SEL_HSUSB); + + usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM); + do { + val = usbhs_read32(priv, UGSTS); + udelay(1); + } while (!(val & UGSTS_LOCK) && timeout--); + usbhs_write32(priv, UGCTRL, UGCTRL_CONNECT); + } else { + usbhs_write32(priv, UGCTRL, 0); + usbhs_bset(priv, LPSTS, LPSTS_SUSPM, 0); + usbhs_write32(priv, UGCTRL, UGCTRL_PLLRESET); + } + + return 0; +} + static int usbhs_rcar3_get_id(struct platform_device *pdev) { return USBHS_GADGET; @@ -61,3 +104,8 @@ const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = { .power_ctrl = usbhs_rcar3_power_ctrl, .get_id = usbhs_rcar3_get_id, }; + +const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops = { + .power_ctrl = usbhs_rcar3_power_and_pll_ctrl, + .get_id = usbhs_rcar3_get_id, +}; diff --git a/drivers/usb/renesas_usbhs/rcar3.h b/drivers/usb/renesas_usbhs/rcar3.h index 5f850b23ff18..7fe98175f94f 100644 --- a/drivers/usb/renesas_usbhs/rcar3.h +++ b/drivers/usb/renesas_usbhs/rcar3.h @@ -1,3 +1,4 @@ #include "common.h" extern const struct renesas_usbhs_platform_callback usbhs_rcar3_ops; +extern const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops; diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index 00a47d058d83..d2f41e4dd7a8 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -183,8 +183,9 @@ struct renesas_usbhs_driver_param { #define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */ }; -#define USBHS_TYPE_RCAR_GEN2 1 -#define USBHS_TYPE_RCAR_GEN3 2 +#define USBHS_TYPE_RCAR_GEN2 1 +#define USBHS_TYPE_RCAR_GEN3 2 +#define USBHS_TYPE_RCAR_GEN3_WITH_PLL 3 /* * option: -- cgit v1.2.3-59-g8ed1b From 93862e385ded7c60351e09fcd2a541d273650905 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Fri, 13 Oct 2017 15:08:41 -0400 Subject: livepatch: add (un)patch callbacks Provide livepatch modules a klp_object (un)patching notification mechanism. Pre and post-(un)patch callbacks allow livepatch modules to setup or synchronize changes that would be difficult to support in only patched-or-unpatched code contexts. Callbacks can be registered for target module or vmlinux klp_objects, but each implementation is klp_object specific. - Pre-(un)patch callbacks run before any (un)patching transition starts. - Post-(un)patch callbacks run once an object has been (un)patched and the klp_patch fully transitioned to its target state. Example use cases include modification of global data and registration of newly available services/handlers. See Documentation/livepatch/callbacks.txt for details and samples/livepatch/ for examples. Signed-off-by: Joe Lawrence Acked-by: Josh Poimboeuf Acked-by: Miroslav Benes Signed-off-by: Jiri Kosina --- Documentation/livepatch/callbacks.txt | 605 ++++++++++++++++++++++++ include/linux/livepatch.h | 26 + kernel/livepatch/core.c | 51 +- kernel/livepatch/core.h | 38 ++ kernel/livepatch/patch.c | 1 + kernel/livepatch/transition.c | 21 +- samples/livepatch/Makefile | 3 + samples/livepatch/livepatch-callbacks-busymod.c | 72 +++ samples/livepatch/livepatch-callbacks-demo.c | 234 +++++++++ samples/livepatch/livepatch-callbacks-mod.c | 53 +++ 10 files changed, 1091 insertions(+), 13 deletions(-) create mode 100644 Documentation/livepatch/callbacks.txt create mode 100644 samples/livepatch/livepatch-callbacks-busymod.c create mode 100644 samples/livepatch/livepatch-callbacks-demo.c create mode 100644 samples/livepatch/livepatch-callbacks-mod.c (limited to 'include') diff --git a/Documentation/livepatch/callbacks.txt b/Documentation/livepatch/callbacks.txt new file mode 100644 index 000000000000..c9776f48e458 --- /dev/null +++ b/Documentation/livepatch/callbacks.txt @@ -0,0 +1,605 @@ +====================== +(Un)patching Callbacks +====================== + +Livepatch (un)patch-callbacks provide a mechanism for livepatch modules +to execute callback functions when a kernel object is (un)patched. They +can be considered a "power feature" that extends livepatching abilities +to include: + + - Safe updates to global data + + - "Patches" to init and probe functions + + - Patching otherwise unpatchable code (i.e. assembly) + +In most cases, (un)patch callbacks will need to be used in conjunction +with memory barriers and kernel synchronization primitives, like +mutexes/spinlocks, or even stop_machine(), to avoid concurrency issues. + +Callbacks differ from existing kernel facilities: + + - Module init/exit code doesn't run when disabling and re-enabling a + patch. + + - A module notifier can't stop a to-be-patched module from loading. + +Callbacks are part of the klp_object structure and their implementation +is specific to that klp_object. Other livepatch objects may or may not +be patched, irrespective of the target klp_object's current state. + +Callbacks can be registered for the following livepatch actions: + + * Pre-patch - before a klp_object is patched + + * Post-patch - after a klp_object has been patched and is active + across all tasks + + * Pre-unpatch - before a klp_object is unpatched (ie, patched code is + active), used to clean up post-patch callback + resources + + * Post-unpatch - after a klp_object has been patched, all code has + been restored and no tasks are running patched code, + used to cleanup pre-patch callback resources + +Each callback is optional, omitting one does not preclude specifying any +other. However, the livepatching core executes the handlers in +symmetry: pre-patch callbacks have a post-unpatch counterpart and +post-patch callbacks have a pre-unpatch counterpart. An unpatch +callback will only be executed if its corresponding patch callback was +executed. Typical use cases pair a patch handler that acquires and +configures resources with an unpatch handler tears down and releases +those same resources. + +A callback is only executed if its host klp_object is loaded. For +in-kernel vmlinux targets, this means that callbacks will always execute +when a livepatch is enabled/disabled. For patch target kernel modules, +callbacks will only execute if the target module is loaded. When a +module target is (un)loaded, its callbacks will execute only if the +livepatch module is enabled. + +The pre-patch callback, if specified, is expected to return a status +code (0 for success, -ERRNO on error). An error status code indicates +to the livepatching core that patching of the current klp_object is not +safe and to stop the current patching request. (When no pre-patch +callback is provided, the transition is assumed to be safe.) If a +pre-patch callback returns failure, the kernel's module loader will: + + - Refuse to load a livepatch, if the livepatch is loaded after + targeted code. + + or: + + - Refuse to load a module, if the livepatch was already successfully + loaded. + +No post-patch, pre-unpatch, or post-unpatch callbacks will be executed +for a given klp_object if the object failed to patch, due to a failed +pre_patch callback or for any other reason. + +If a patch transition is reversed, no pre-unpatch handlers will be run +(this follows the previously mentioned symmetry -- pre-unpatch callbacks +will only occur if their corresponding post-patch callback executed). + +If the object did successfully patch, but the patch transition never +started for some reason (e.g., if another object failed to patch), +only the post-unpatch callback will be called. + + +Example Use-cases +================= + +Update global data +------------------ + +A pre-patch callback can be useful to update a global variable. For +example, 75ff39ccc1bd ("tcp: make challenge acks less predictable") +changes a global sysctl, as well as patches the tcp_send_challenge_ack() +function. + +In this case, if we're being super paranoid, it might make sense to +patch the data *after* patching is complete with a post-patch callback, +so that tcp_send_challenge_ack() could first be changed to read +sysctl_tcp_challenge_ack_limit with READ_ONCE. + + +Support __init and probe function patches +----------------------------------------- + +Although __init and probe functions are not directly livepatch-able, it +may be possible to implement similar updates via pre/post-patch +callbacks. + +48900cb6af42 ("virtio-net: drop NETIF_F_FRAGLIST") change the way that +virtnet_probe() initialized its driver's net_device features. A +pre/post-patch callback could iterate over all such devices, making a +similar change to their hw_features value. (Client functions of the +value may need to be updated accordingly.) + + +Test cases +========== + +What follows is not an exhaustive test suite of every possible livepatch +pre/post-(un)patch combination, but a selection that demonstrates a few +important concepts. Each test case uses the kernel modules located in +the samples/livepatch/ and assumes that no livepatches are loaded at the +beginning of the test. + + +Test 1 +------ + +Test a combination of loading a kernel module and a livepatch that +patches a function in the first module. (Un)load the target module +before the livepatch module: + +- load target module +- load livepatch +- disable livepatch +- unload target module +- unload livepatch + +First load a target module: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 34.475708] livepatch_callbacks_mod: livepatch_callbacks_mod_init + +On livepatch enable, before the livepatch transition starts, pre-patch +callbacks are executed for vmlinux and livepatch_callbacks_mod (those +klp_objects currently loaded). After klp_objects are patched according +to the klp_patch, their post-patch callbacks run and the transition +completes: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 36.503719] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 36.504213] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 36.504238] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 36.504721] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 36.505849] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 37.727133] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 37.727232] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 37.727860] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 37.728792] livepatch: 'livepatch_callbacks_demo': patching complete + +Similarly, on livepatch disable, pre-patch callbacks run before the +unpatching transition starts. klp_objects are reverted, post-patch +callbacks execute and the transition completes: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 38.510209] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 38.510234] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 38.510982] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 38.512209] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 39.711132] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 39.711210] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 39.711779] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 39.712735] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 42.534183] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + + +Test 2 +------ + +This test is similar to the previous test, but (un)load the livepatch +module before the target kernel module. This tests the livepatch core's +module_coming handler: + +- load livepatch +- load target module +- disable livepatch +- unload livepatch +- unload target module + + +On livepatch enable, only pre/post-patch callbacks are executed for +currently loaded klp_objects, in this case, vmlinux: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 44.553328] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 44.553997] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 44.554049] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 44.554845] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 45.727128] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 45.727212] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 45.727961] livepatch: 'livepatch_callbacks_demo': patching complete + +When a targeted module is subsequently loaded, only its pre/post-patch +callbacks are executed: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 46.560845] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' + [ 46.561988] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 46.563452] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 46.565495] livepatch_callbacks_mod: livepatch_callbacks_mod_init + +On livepatch disable, all currently loaded klp_objects' (vmlinux and +livepatch_callbacks_mod) pre/post-unpatch callbacks are executed: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 48.568885] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 48.568910] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 48.569441] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 48.570502] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 49.759091] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 49.759171] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 49.759742] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 49.760690] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 52.592283] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + + +Test 3 +------ + +Test loading the livepatch after a targeted kernel module, then unload +the kernel module before disabling the livepatch. This tests the +livepatch core's module_going handler: + +- load target module +- load livepatch +- unload target module +- disable livepatch +- unload livepatch + +First load a target module, then the livepatch: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 54.607948] livepatch_callbacks_mod: livepatch_callbacks_mod_init + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 56.613919] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 56.614411] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 56.614436] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 56.614818] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 56.615656] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 57.759070] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 57.759147] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 57.759621] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_LIVE] Normal state + [ 57.760307] livepatch: 'livepatch_callbacks_demo': patching complete + +When a target module is unloaded, the livepatch is only reverted from +that klp_object (livepatch_callbacks_mod). As such, only its pre and +post-unpatch callbacks are executed when this occurs: + + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 58.623409] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + [ 58.623903] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + [ 58.624658] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' + [ 58.625305] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + +When the livepatch is disabled, pre and post-unpatch callbacks are run +for the remaining klp_object, vmlinux: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 60.638420] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 60.638444] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 60.638996] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 61.727088] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 61.727165] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 61.727985] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + + +Test 4 +------ + +This test is similar to the previous test, however the livepatch is +loaded first. This tests the livepatch core's module_coming and +module_going handlers: + +- load livepatch +- load target module +- unload target module +- disable livepatch +- unload livepatch + +First load the livepatch: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 64.661552] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 64.662147] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 64.662175] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 64.662850] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 65.695056] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 65.695147] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 65.695561] livepatch: 'livepatch_callbacks_demo': patching complete + +When a targeted kernel module is subsequently loaded, only its +pre/post-patch callbacks are executed: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 66.669196] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' + [ 66.669882] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 66.670744] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 66.672873] livepatch_callbacks_mod: livepatch_callbacks_mod_init + +When the target module is unloaded, the livepatch is only reverted from +the livepatch_callbacks_mod klp_object. As such, only pre and +post-unpatch callbacks are executed when this occurs: + + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 68.680065] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + [ 68.680688] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + [ 68.681452] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' + [ 68.682094] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 70.689225] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 70.689256] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 70.689882] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 71.711080] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 71.711481] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 71.711988] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + + +Test 5 +------ + +A simple test of loading a livepatch without one of its patch target +klp_objects ever loaded (livepatch_callbacks_mod): + +- load livepatch +- disable livepatch +- unload livepatch + +Load the livepatch: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 74.711081] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 74.711595] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 74.711639] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 74.712272] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 75.743137] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 75.743219] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 75.743867] livepatch: 'livepatch_callbacks_demo': patching complete + +As expected, only pre/post-(un)patch handlers are executed for vmlinux: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 76.716254] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 76.716278] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 76.716666] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 77.727089] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 77.727194] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 77.727907] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + + +Test 6 +------ + +Test a scenario where a vmlinux pre-patch callback returns a non-zero +status (ie, failure): + +- load target module +- load livepatch -ENODEV +- unload target module + +First load a target module: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 80.740520] livepatch_callbacks_mod: livepatch_callbacks_mod_init + +Load the livepatch module, setting its 'pre_patch_ret' value to -19 +(-ENODEV). When its vmlinux pre-patch callback executed, this status +code will propagate back to the module-loading subsystem. The result is +that the insmod command refuses to load the livepatch module: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko pre_patch_ret=-19 + [ 82.747326] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 82.747743] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 82.747767] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 82.748237] livepatch: pre-patch callback failed for object 'vmlinux' + [ 82.748637] livepatch: failed to enable patch 'livepatch_callbacks_demo' + [ 82.749059] livepatch: 'livepatch_callbacks_demo': canceling transition, going to unpatch + [ 82.749060] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 82.749868] livepatch: 'livepatch_callbacks_demo': unpatching complete + [ 82.765809] insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-demo.ko: No such device + + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 84.774238] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + + +Test 7 +------ + +Similar to the previous test, setup a livepatch such that its vmlinux +pre-patch callback returns success. However, when a targeted kernel +module is later loaded, have the livepatch return a failing status code: + +- load livepatch +- setup -ENODEV +- load target module +- disable livepatch +- unload livepatch + +Load the livepatch, notice vmlinux pre-patch callback succeeds: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 86.787845] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 86.788325] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 86.788427] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 86.788821] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 87.711069] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 87.711143] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 87.711886] livepatch: 'livepatch_callbacks_demo': patching complete + +Set a trap so subsequent pre-patch callbacks to this livepatch will +return -ENODEV: + + % echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret + +The livepatch pre-patch callback for subsequently loaded target modules +will return failure, so the module loader refuses to load the kernel +module. Notice that no post-patch or pre/post-unpatch callbacks are +executed for this klp_object: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 90.796976] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' + [ 90.797834] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 90.798900] livepatch: pre-patch callback failed for object 'livepatch_callbacks_mod' + [ 90.799652] livepatch: patch 'livepatch_callbacks_demo' failed for module 'livepatch_callbacks_mod', refusing to load module 'livepatch_callbacks_mod' + [ 90.819737] insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device + +However, pre/post-unpatch callbacks run for the vmlinux klp_object: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 92.823547] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 92.823573] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 92.824331] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 93.727128] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 93.727327] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 93.727861] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + + +Test 8 +------ + +Test loading multiple targeted kernel modules. This test-case is +mainly for comparing with the next test-case. + +- load busy target module (0s sleep), +- load livepatch +- load target module +- unload target module +- disable livepatch +- unload livepatch +- unload busy target module + + +Load a target "busy" kernel module which kicks off a worker function +that immediately exits: + + % insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=0 + [ 96.910107] livepatch_callbacks_busymod: livepatch_callbacks_mod_init + [ 96.910600] livepatch_callbacks_busymod: busymod_work_func, sleeping 0 seconds ... + [ 96.913024] livepatch_callbacks_busymod: busymod_work_func exit + +Proceed with loading the livepatch and another ordinary target module, +notice that the post-patch callbacks are executed and the transition +completes quickly: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 98.917892] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 98.918426] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 98.918453] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 98.918955] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 98.923835] livepatch: 'livepatch_callbacks_demo': starting patching transition + [ 99.743104] livepatch: 'livepatch_callbacks_demo': completing patching transition + [ 99.743156] livepatch_callbacks_demo: post_patch_callback: vmlinux + [ 99.743679] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 99.744616] livepatch: 'livepatch_callbacks_demo': patching complete + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 100.930955] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' + [ 100.931668] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 100.932645] livepatch_callbacks_demo: post_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 100.934125] livepatch_callbacks_mod: livepatch_callbacks_mod_init + + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 102.942805] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + [ 102.943640] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + [ 102.944585] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' + [ 102.945455] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 104.953815] livepatch: 'livepatch_callbacks_demo': initializing unpatching transition + [ 104.953838] livepatch_callbacks_demo: pre_unpatch_callback: vmlinux + [ 104.954431] livepatch_callbacks_demo: pre_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 104.955426] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 106.719073] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 106.722633] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 106.723282] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 106.724279] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + % rmmod samples/livepatch/livepatch-callbacks-busymod.ko + [ 108.975660] livepatch_callbacks_busymod: livepatch_callbacks_mod_exit + + +Test 9 +------ + +A similar test as the previous one, but force the "busy" kernel module +to do longer work. + +The livepatching core will refuse to patch a task that is currently +executing a to-be-patched function -- the consistency model stalls the +current patch transition until this safety-check is met. Test a +scenario where one of a livepatch's target klp_objects sits on such a +function for a long time. Meanwhile, load and unload other target +kernel modules while the livepatch transition is in progress. + +- load busy target module (30s sleep) +- load livepatch +- load target module +- unload target module +- disable livepatch +- unload livepatch +- unload busy target module + + +Load the "busy" kernel module, this time make it do 30 seconds worth of +work: + + % insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30 + [ 110.993362] livepatch_callbacks_busymod: livepatch_callbacks_mod_init + [ 110.994059] livepatch_callbacks_busymod: busymod_work_func, sleeping 30 seconds ... + +Meanwhile, the livepatch is loaded. Notice that the patch transition +does not complete as the targeted "busy" module is sitting on a +to-be-patched function: + + % insmod samples/livepatch/livepatch-callbacks-demo.ko + [ 113.000309] livepatch: enabling patch 'livepatch_callbacks_demo' + [ 113.000764] livepatch: 'livepatch_callbacks_demo': initializing patching transition + [ 113.000791] livepatch_callbacks_demo: pre_patch_callback: vmlinux + [ 113.001289] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 113.005208] livepatch: 'livepatch_callbacks_demo': starting patching transition + +Load a second target module (this one is an ordinary idle kernel +module). Note that *no* post-patch callbacks will be executed while the +livepatch is still in transition: + + % insmod samples/livepatch/livepatch-callbacks-mod.ko + [ 115.012740] livepatch: applying patch 'livepatch_callbacks_demo' to loading module 'livepatch_callbacks_mod' + [ 115.013406] livepatch_callbacks_demo: pre_patch_callback: livepatch_callbacks_mod -> [MODULE_STATE_COMING] Full formed, running module_init + [ 115.015315] livepatch_callbacks_mod: livepatch_callbacks_mod_init + +Request an unload of the simple kernel module. The patch is still +transitioning, so its pre-unpatch callbacks are skipped: + + % rmmod samples/livepatch/livepatch-callbacks-mod.ko + [ 117.022626] livepatch_callbacks_mod: livepatch_callbacks_mod_exit + [ 117.023376] livepatch: reverting patch 'livepatch_callbacks_demo' on unloading module 'livepatch_callbacks_mod' + [ 117.024533] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_mod -> [MODULE_STATE_GOING] Going away + +Finally the livepatch is disabled. Since none of the patch's +klp_object's post-patch callbacks executed, the remaining klp_object's +pre-unpatch callbacks are skipped: + + % echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + [ 119.035408] livepatch: 'livepatch_callbacks_demo': reversing transition from patching to unpatching + [ 119.035485] livepatch: 'livepatch_callbacks_demo': starting unpatching transition + [ 119.711166] livepatch: 'livepatch_callbacks_demo': completing unpatching transition + [ 119.714179] livepatch_callbacks_demo: post_unpatch_callback: vmlinux + [ 119.714653] livepatch_callbacks_demo: post_unpatch_callback: livepatch_callbacks_busymod -> [MODULE_STATE_LIVE] Normal state + [ 119.715437] livepatch: 'livepatch_callbacks_demo': unpatching complete + + % rmmod samples/livepatch/livepatch-callbacks-demo.ko + % rmmod samples/livepatch/livepatch-callbacks-busymod.ko + [ 141.279111] livepatch_callbacks_busymod: busymod_work_func exit + [ 141.279760] livepatch_callbacks_busymod: livepatch_callbacks_mod_exit diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h index d08eddc00497..fc5c1be3f6f4 100644 --- a/include/linux/livepatch.h +++ b/include/linux/livepatch.h @@ -87,10 +87,35 @@ struct klp_func { bool transition; }; +struct klp_object; + +/** + * struct klp_callbacks - pre/post live-(un)patch callback structure + * @pre_patch: executed before code patching + * @post_patch: executed after code patching + * @pre_unpatch: executed before code unpatching + * @post_unpatch: executed after code unpatching + * @post_unpatch_enabled: flag indicating if post-unpatch callback + * should run + * + * All callbacks are optional. Only the pre-patch callback, if provided, + * will be unconditionally executed. If the parent klp_object fails to + * patch for any reason, including a non-zero error status returned from + * the pre-patch callback, no further callbacks will be executed. + */ +struct klp_callbacks { + int (*pre_patch)(struct klp_object *obj); + void (*post_patch)(struct klp_object *obj); + void (*pre_unpatch)(struct klp_object *obj); + void (*post_unpatch)(struct klp_object *obj); + bool post_unpatch_enabled; +}; + /** * struct klp_object - kernel object structure for live patching * @name: module name (or NULL for vmlinux) * @funcs: function entries for functions to be patched in the object + * @callbacks: functions to be executed pre/post (un)patching * @kobj: kobject for sysfs resources * @mod: kernel module associated with the patched object * (NULL for vmlinux) @@ -100,6 +125,7 @@ struct klp_object { /* external */ const char *name; struct klp_func *funcs; + struct klp_callbacks callbacks; /* internal */ struct kobject kobj; diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index b9628e43c78f..cafb5a84417d 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c @@ -54,11 +54,6 @@ static bool klp_is_module(struct klp_object *obj) return obj->name; } -static bool klp_is_object_loaded(struct klp_object *obj) -{ - return !obj->name || obj->mod; -} - /* sets obj->mod if object is not vmlinux and module is found */ static void klp_find_object_module(struct klp_object *obj) { @@ -285,6 +280,8 @@ static int klp_write_object_relocations(struct module *pmod, static int __klp_disable_patch(struct klp_patch *patch) { + struct klp_object *obj; + if (klp_transition_patch) return -EBUSY; @@ -295,6 +292,10 @@ static int __klp_disable_patch(struct klp_patch *patch) klp_init_transition(patch, KLP_UNPATCHED); + klp_for_each_object(patch, obj) + if (patch->enabled && obj->patched) + klp_pre_unpatch_callback(obj); + /* * Enforce the order of the func->transition writes in * klp_init_transition() and the TIF_PATCH_PENDING writes in @@ -388,13 +389,18 @@ static int __klp_enable_patch(struct klp_patch *patch) if (!klp_is_object_loaded(obj)) continue; - ret = klp_patch_object(obj); + ret = klp_pre_patch_callback(obj); if (ret) { - pr_warn("failed to enable patch '%s'\n", - patch->mod->name); + pr_warn("pre-patch callback failed for object '%s'\n", + klp_is_module(obj) ? obj->name : "vmlinux"); + goto err; + } - klp_cancel_transition(); - return ret; + ret = klp_patch_object(obj); + if (ret) { + pr_warn("failed to patch object '%s'\n", + klp_is_module(obj) ? obj->name : "vmlinux"); + goto err; } } @@ -403,6 +409,11 @@ static int __klp_enable_patch(struct klp_patch *patch) patch->enabled = true; return 0; +err: + pr_warn("failed to enable patch '%s'\n", patch->mod->name); + + klp_cancel_transition(); + return ret; } /** @@ -871,13 +882,27 @@ int klp_module_coming(struct module *mod) pr_notice("applying patch '%s' to loading module '%s'\n", patch->mod->name, obj->mod->name); + ret = klp_pre_patch_callback(obj); + if (ret) { + pr_warn("pre-patch callback failed for object '%s'\n", + obj->name); + goto err; + } + ret = klp_patch_object(obj); if (ret) { pr_warn("failed to apply patch '%s' to module '%s' (%d)\n", patch->mod->name, obj->mod->name, ret); + + if (patch != klp_transition_patch) + klp_post_unpatch_callback(obj); + goto err; } + if (patch != klp_transition_patch) + klp_post_patch_callback(obj); + break; } } @@ -927,9 +952,15 @@ void klp_module_going(struct module *mod) * is in transition. */ if (patch->enabled || patch == klp_transition_patch) { + + if (patch != klp_transition_patch) + klp_pre_unpatch_callback(obj); + pr_notice("reverting patch '%s' on unloading module '%s'\n", patch->mod->name, obj->mod->name); klp_unpatch_object(obj); + + klp_post_unpatch_callback(obj); } klp_free_object_loaded(obj); diff --git a/kernel/livepatch/core.h b/kernel/livepatch/core.h index c74f24c47837..6fc907b54e71 100644 --- a/kernel/livepatch/core.h +++ b/kernel/livepatch/core.h @@ -1,6 +1,44 @@ #ifndef _LIVEPATCH_CORE_H #define _LIVEPATCH_CORE_H +#include + extern struct mutex klp_mutex; +static inline bool klp_is_object_loaded(struct klp_object *obj) +{ + return !obj->name || obj->mod; +} + +static inline int klp_pre_patch_callback(struct klp_object *obj) +{ + int ret; + + ret = (obj->callbacks.pre_patch) ? + (*obj->callbacks.pre_patch)(obj) : 0; + + obj->callbacks.post_unpatch_enabled = !ret; + + return ret; +} + +static inline void klp_post_patch_callback(struct klp_object *obj) +{ + if (obj->callbacks.post_patch) + (*obj->callbacks.post_patch)(obj); +} + +static inline void klp_pre_unpatch_callback(struct klp_object *obj) +{ + if (obj->callbacks.pre_unpatch) + (*obj->callbacks.pre_unpatch)(obj); +} + +static inline void klp_post_unpatch_callback(struct klp_object *obj) +{ + if (obj->callbacks.post_unpatch_enabled && + obj->callbacks.post_unpatch) + (*obj->callbacks.post_unpatch)(obj); +} + #endif /* _LIVEPATCH_CORE_H */ diff --git a/kernel/livepatch/patch.c b/kernel/livepatch/patch.c index 52c4e907c14b..82d584225dc6 100644 --- a/kernel/livepatch/patch.c +++ b/kernel/livepatch/patch.c @@ -28,6 +28,7 @@ #include #include #include +#include "core.h" #include "patch.h" #include "transition.h" diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c index b004a1fb6032..7bf55b7f3687 100644 --- a/kernel/livepatch/transition.c +++ b/kernel/livepatch/transition.c @@ -109,9 +109,6 @@ static void klp_complete_transition(void) } } - if (klp_target_state == KLP_UNPATCHED && !immediate_func) - module_put(klp_transition_patch->mod); - /* Prevent klp_ftrace_handler() from seeing KLP_UNDEFINED state */ if (klp_target_state == KLP_PATCHED) klp_synchronize_transition(); @@ -130,6 +127,24 @@ static void klp_complete_transition(void) } done: + klp_for_each_object(klp_transition_patch, obj) { + if (!klp_is_object_loaded(obj)) + continue; + if (klp_target_state == KLP_PATCHED) + klp_post_patch_callback(obj); + else if (klp_target_state == KLP_UNPATCHED) + klp_post_unpatch_callback(obj); + } + + /* + * See complementary comment in __klp_enable_patch() for why we + * keep the module reference for immediate patches. + */ + if (!klp_transition_patch->immediate && !immediate_func && + klp_target_state == KLP_UNPATCHED) { + module_put(klp_transition_patch->mod); + } + klp_target_state = KLP_UNDEFINED; klp_transition_patch = NULL; } diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile index 539e81d433cd..2472ce39a18d 100644 --- a/samples/livepatch/Makefile +++ b/samples/livepatch/Makefile @@ -2,3 +2,6 @@ obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o +obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-demo.o +obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-mod.o +obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-busymod.o diff --git a/samples/livepatch/livepatch-callbacks-busymod.c b/samples/livepatch/livepatch-callbacks-busymod.c new file mode 100644 index 000000000000..80d06e103f1b --- /dev/null +++ b/samples/livepatch/livepatch-callbacks-busymod.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 Joe Lawrence + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * livepatch-callbacks-busymod.c - (un)patching callbacks demo support module + * + * + * Purpose + * ------- + * + * Simple module to demonstrate livepatch (un)patching callbacks. + * + * + * Usage + * ----- + * + * This module is not intended to be standalone. See the "Usage" + * section of livepatch-callbacks-mod.c. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +static int sleep_secs; +module_param(sleep_secs, int, 0644); +MODULE_PARM_DESC(sleep_secs, "sleep_secs (default=0)"); + +static void busymod_work_func(struct work_struct *work); +static DECLARE_DELAYED_WORK(work, busymod_work_func); + +static void busymod_work_func(struct work_struct *work) +{ + pr_info("%s, sleeping %d seconds ...\n", __func__, sleep_secs); + msleep(sleep_secs * 1000); + pr_info("%s exit\n", __func__); +} + +static int livepatch_callbacks_mod_init(void) +{ + pr_info("%s\n", __func__); + schedule_delayed_work(&work, + msecs_to_jiffies(1000 * 0)); + return 0; +} + +static void livepatch_callbacks_mod_exit(void) +{ + cancel_delayed_work_sync(&work); + pr_info("%s\n", __func__); +} + +module_init(livepatch_callbacks_mod_init); +module_exit(livepatch_callbacks_mod_exit); +MODULE_LICENSE("GPL"); diff --git a/samples/livepatch/livepatch-callbacks-demo.c b/samples/livepatch/livepatch-callbacks-demo.c new file mode 100644 index 000000000000..3d115bd68442 --- /dev/null +++ b/samples/livepatch/livepatch-callbacks-demo.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2017 Joe Lawrence + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo + * + * + * Purpose + * ------- + * + * Demonstration of registering livepatch (un)patching callbacks. + * + * + * Usage + * ----- + * + * Step 1 - load the simple module + * + * insmod samples/livepatch/livepatch-callbacks-mod.ko + * + * + * Step 2 - load the demonstration livepatch (with callbacks) + * + * insmod samples/livepatch/livepatch-callbacks-demo.ko + * + * + * Step 3 - cleanup + * + * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + * rmmod livepatch_callbacks_demo + * rmmod livepatch_callbacks_mod + * + * Watch dmesg output to see livepatch enablement, callback execution + * and patching operations for both vmlinux and module targets. + * + * NOTE: swap the insmod order of livepatch-callbacks-mod.ko and + * livepatch-callbacks-demo.ko to observe what happens when a + * target module is loaded after a livepatch with callbacks. + * + * NOTE: 'pre_patch_ret' is a module parameter that sets the pre-patch + * callback return status. Try setting up a non-zero status + * such as -19 (-ENODEV): + * + * # Load demo livepatch, vmlinux is patched + * insmod samples/livepatch/livepatch-callbacks-demo.ko + * + * # Setup next pre-patch callback to return -ENODEV + * echo -19 > /sys/module/livepatch_callbacks_demo/parameters/pre_patch_ret + * + * # Module loader refuses to load the target module + * insmod samples/livepatch/livepatch-callbacks-mod.ko + * insmod: ERROR: could not insert module samples/livepatch/livepatch-callbacks-mod.ko: No such device + * + * NOTE: There is a second target module, + * livepatch-callbacks-busymod.ko, available for experimenting + * with livepatch (un)patch callbacks. This module contains + * a 'sleep_secs' parameter that parks the module on one of the + * functions that the livepatch demo module wants to patch. + * Modifying this value and tweaking the order of module loads can + * effectively demonstrate stalled patch transitions: + * + * # Load a target module, let it park on 'busymod_work_func' for + * # thirty seconds + * insmod samples/livepatch/livepatch-callbacks-busymod.ko sleep_secs=30 + * + * # Meanwhile load the livepatch + * insmod samples/livepatch/livepatch-callbacks-demo.ko + * + * # ... then load and unload another target module while the + * # transition is in progress + * insmod samples/livepatch/livepatch-callbacks-mod.ko + * rmmod samples/livepatch/livepatch-callbacks-mod.ko + * + * # Finally cleanup + * echo 0 > /sys/kernel/livepatch/livepatch_callbacks_demo/enabled + * rmmod samples/livepatch/livepatch-callbacks-demo.ko + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include + +static int pre_patch_ret; +module_param(pre_patch_ret, int, 0644); +MODULE_PARM_DESC(pre_patch_ret, "pre_patch_ret (default=0)"); + +static const char *const module_state[] = { + [MODULE_STATE_LIVE] = "[MODULE_STATE_LIVE] Normal state", + [MODULE_STATE_COMING] = "[MODULE_STATE_COMING] Full formed, running module_init", + [MODULE_STATE_GOING] = "[MODULE_STATE_GOING] Going away", + [MODULE_STATE_UNFORMED] = "[MODULE_STATE_UNFORMED] Still setting it up", +}; + +static void callback_info(const char *callback, struct klp_object *obj) +{ + if (obj->mod) + pr_info("%s: %s -> %s\n", callback, obj->mod->name, + module_state[obj->mod->state]); + else + pr_info("%s: vmlinux\n", callback); +} + +/* Executed on object patching (ie, patch enablement) */ +static int pre_patch_callback(struct klp_object *obj) +{ + callback_info(__func__, obj); + return pre_patch_ret; +} + +/* Executed on object unpatching (ie, patch disablement) */ +static void post_patch_callback(struct klp_object *obj) +{ + callback_info(__func__, obj); +} + +/* Executed on object unpatching (ie, patch disablement) */ +static void pre_unpatch_callback(struct klp_object *obj) +{ + callback_info(__func__, obj); +} + +/* Executed on object unpatching (ie, patch disablement) */ +static void post_unpatch_callback(struct klp_object *obj) +{ + callback_info(__func__, obj); +} + +static void patched_work_func(struct work_struct *work) +{ + pr_info("%s\n", __func__); +} + +static struct klp_func no_funcs[] = { + { } +}; + +static struct klp_func busymod_funcs[] = { + { + .old_name = "busymod_work_func", + .new_func = patched_work_func, + }, { } +}; + +static struct klp_object objs[] = { + { + .name = NULL, /* vmlinux */ + .funcs = no_funcs, + .callbacks = { + .pre_patch = pre_patch_callback, + .post_patch = post_patch_callback, + .pre_unpatch = pre_unpatch_callback, + .post_unpatch = post_unpatch_callback, + }, + }, { + .name = "livepatch_callbacks_mod", + .funcs = no_funcs, + .callbacks = { + .pre_patch = pre_patch_callback, + .post_patch = post_patch_callback, + .pre_unpatch = pre_unpatch_callback, + .post_unpatch = post_unpatch_callback, + }, + }, { + .name = "livepatch_callbacks_busymod", + .funcs = busymod_funcs, + .callbacks = { + .pre_patch = pre_patch_callback, + .post_patch = post_patch_callback, + .pre_unpatch = pre_unpatch_callback, + .post_unpatch = post_unpatch_callback, + }, + }, { } +}; + +static struct klp_patch patch = { + .mod = THIS_MODULE, + .objs = objs, +}; + +static int livepatch_callbacks_demo_init(void) +{ + int ret; + + if (!klp_have_reliable_stack() && !patch.immediate) { + /* + * WARNING: Be very careful when using 'patch.immediate' in + * your patches. It's ok to use it for simple patches like + * this, but for more complex patches which change function + * semantics, locking semantics, or data structures, it may not + * be safe. Use of this option will also prevent removal of + * the patch. + * + * See Documentation/livepatch/livepatch.txt for more details. + */ + patch.immediate = true; + pr_notice("The consistency model isn't supported for your architecture. Bypassing safety mechanisms and applying the patch immediately.\n"); + } + + ret = klp_register_patch(&patch); + if (ret) + return ret; + ret = klp_enable_patch(&patch); + if (ret) { + WARN_ON(klp_unregister_patch(&patch)); + return ret; + } + return 0; +} + +static void livepatch_callbacks_demo_exit(void) +{ + WARN_ON(klp_unregister_patch(&patch)); +} + +module_init(livepatch_callbacks_demo_init); +module_exit(livepatch_callbacks_demo_exit); +MODULE_LICENSE("GPL"); +MODULE_INFO(livepatch, "Y"); diff --git a/samples/livepatch/livepatch-callbacks-mod.c b/samples/livepatch/livepatch-callbacks-mod.c new file mode 100644 index 000000000000..e610ce29ba44 --- /dev/null +++ b/samples/livepatch/livepatch-callbacks-mod.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 Joe Lawrence + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +/* + * livepatch-callbacks-mod.c - (un)patching callbacks demo support module + * + * + * Purpose + * ------- + * + * Simple module to demonstrate livepatch (un)patching callbacks. + * + * + * Usage + * ----- + * + * This module is not intended to be standalone. See the "Usage" + * section of livepatch-callbacks-demo.c. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include + +static int livepatch_callbacks_mod_init(void) +{ + pr_info("%s\n", __func__); + return 0; +} + +static void livepatch_callbacks_mod_exit(void) +{ + pr_info("%s\n", __func__); +} + +module_init(livepatch_callbacks_mod_init); +module_exit(livepatch_callbacks_mod_exit); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 9780ded39bef5d22a84bdc39112df93f70a58bdd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 18 Oct 2017 15:51:59 +0200 Subject: ALSA: hda: Avoid racy recreation of widget kobjects The refresh of HD-audio widget sysfs kobjects via snd_hdac_refresh_widget_sysfs() is slightly racy. The driver recreates the whole tree from scratch after deleting the whole. When CONFIG_DEBUG_KOBJECT_RELEASE option is used, kobject release doesn't happen immediately but delayed, while the re-creation of the same named kobject happens soon after invoking kobject_put(). This may end up with the conflicts of duplicated kobjects, as found in the bug report below. In this patch, we take another approach to refresh the tree: instead of recreating the whole tree, just add the new nodes and delete the non-existing nodes. Since the refresh happens only once at initialization, no longer race would happen. Along with the code change, merge snd_hdac_refresh_widget_sysfs() with the existing snd_hdac_refresh_widgets() with an additional bool flag for simplifying the code. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=197307 Signed-off-by: Takashi Iwai --- include/sound/hdaudio.h | 3 +-- sound/hda/hdac_device.c | 43 ++++++++++--------------------------------- sound/hda/hdac_sysfs.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ sound/hda/local.h | 2 ++ sound/pci/hda/hda_codec.c | 2 +- 5 files changed, 61 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h index 96546b30e900..6f1118545bb8 100644 --- a/include/sound/hdaudio.h +++ b/include/sound/hdaudio.h @@ -111,8 +111,7 @@ void snd_hdac_device_unregister(struct hdac_device *codec); int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name); int snd_hdac_codec_modalias(struct hdac_device *hdac, char *buf, size_t size); -int snd_hdac_refresh_widgets(struct hdac_device *codec); -int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec); +int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs); unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, unsigned int verb, unsigned int parm); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index 19deb306facb..06f845e293cb 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -87,7 +87,7 @@ int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus, fg = codec->afg ? codec->afg : codec->mfg; - err = snd_hdac_refresh_widgets(codec); + err = snd_hdac_refresh_widgets(codec, false); if (err < 0) goto error; @@ -388,11 +388,12 @@ static void setup_fg_nodes(struct hdac_device *codec) /** * snd_hdac_refresh_widgets - Reset the widget start/end nodes * @codec: the codec object + * @sysfs: re-initialize sysfs tree, too */ -int snd_hdac_refresh_widgets(struct hdac_device *codec) +int snd_hdac_refresh_widgets(struct hdac_device *codec, bool sysfs) { hda_nid_t start_nid; - int nums; + int nums, err; nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid); if (!start_nid || nums <= 0 || nums >= 0xff) { @@ -401,6 +402,12 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec) return -EINVAL; } + if (sysfs) { + err = hda_widget_sysfs_reinit(codec, start_nid, nums); + if (err < 0) + return err; + } + codec->num_nodes = nums; codec->start_nid = start_nid; codec->end_nid = start_nid + nums; @@ -408,36 +415,6 @@ int snd_hdac_refresh_widgets(struct hdac_device *codec) } EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets); -/** - * snd_hdac_refresh_widget_sysfs - Reset the codec widgets and reinit the - * codec sysfs - * @codec: the codec object - * - * first we need to remove sysfs, then refresh widgets and lastly - * recreate it - */ -int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec) -{ - int ret; - - if (device_is_registered(&codec->dev)) - hda_widget_sysfs_exit(codec); - ret = snd_hdac_refresh_widgets(codec); - if (ret) { - dev_err(&codec->dev, "failed to refresh widget: %d\n", ret); - return ret; - } - if (device_is_registered(&codec->dev)) { - ret = hda_widget_sysfs_init(codec); - if (ret) { - dev_err(&codec->dev, "failed to init sysfs: %d\n", ret); - return ret; - } - } - return ret; -} -EXPORT_SYMBOL_GPL(snd_hdac_refresh_widget_sysfs); - /* return CONNLIST_LEN parameter of the given widget */ static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid) { diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c index 42d61bf41969..e123ba6b2e81 100644 --- a/sound/hda/hdac_sysfs.c +++ b/sound/hda/hdac_sysfs.c @@ -414,3 +414,50 @@ void hda_widget_sysfs_exit(struct hdac_device *codec) { widget_tree_free(codec); } + +int hda_widget_sysfs_reinit(struct hdac_device *codec, + hda_nid_t start_nid, int num_nodes) +{ + struct hdac_widget_tree *tree; + hda_nid_t end_nid = start_nid + num_nodes; + hda_nid_t nid; + int i; + + if (!codec->widgets) + return hda_widget_sysfs_init(codec); + + tree = kmemdup(codec->widgets, sizeof(*tree), GFP_KERNEL); + if (!tree) + return -ENOMEM; + + tree->nodes = kcalloc(num_nodes + 1, sizeof(*tree->nodes), GFP_KERNEL); + if (!tree->nodes) { + kfree(tree); + return -ENOMEM; + } + + /* prune non-existing nodes */ + for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) { + if (nid < start_nid || nid >= end_nid) + free_widget_node(codec->widgets->nodes[i], + &widget_node_group); + } + + /* add new nodes */ + for (i = 0, nid = start_nid; i < num_nodes; i++, nid++) { + if (nid < codec->start_nid || nid >= codec->end_nid) + add_widget_node(tree->root, nid, &widget_node_group, + &tree->nodes[i]); + else + tree->nodes[i] = + codec->widgets->nodes[nid - codec->start_nid]; + } + + /* replace with the new tree */ + kfree(codec->widgets->nodes); + kfree(codec->widgets); + codec->widgets = tree; + + kobject_uevent(tree->root, KOBJ_CHANGE); + return 0; +} diff --git a/sound/hda/local.h b/sound/hda/local.h index 0d5bb159d538..c586ea62d49e 100644 --- a/sound/hda/local.h +++ b/sound/hda/local.h @@ -28,6 +28,8 @@ static inline unsigned int get_wcaps_channels(u32 wcaps) extern const struct attribute_group *hdac_dev_attr_groups[]; int hda_widget_sysfs_init(struct hdac_device *codec); +int hda_widget_sysfs_reinit(struct hdac_device *codec, hda_nid_t start_nid, + int num_nodes); void hda_widget_sysfs_exit(struct hdac_device *codec); #endif /* __HDAC_LOCAL_H */ diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3db26c451837..bb086665c3e0 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -977,7 +977,7 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec) hda_nid_t fg; int err; - err = snd_hdac_refresh_widget_sysfs(&codec->core); + err = snd_hdac_refresh_widgets(&codec->core, true); if (err < 0) return err; -- cgit v1.2.3-59-g8ed1b From 7a0947e755084b918e33242fd558e55cb443408e Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Tue, 17 Oct 2017 17:16:52 -0700 Subject: dql: make dql_init return void dql_init always returned 0, and the only place that uses it in network core code didn't care about the return value anyway. Signed-off-by: Stephen Hemminger Acked-by: Hiroaki SHIMODA Signed-off-by: David S. Miller --- include/linux/dynamic_queue_limits.h | 2 +- lib/dynamic_queue_limits.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/dynamic_queue_limits.h b/include/linux/dynamic_queue_limits.h index a4be70398ce1..f69f98541953 100644 --- a/include/linux/dynamic_queue_limits.h +++ b/include/linux/dynamic_queue_limits.h @@ -98,7 +98,7 @@ void dql_completed(struct dql *dql, unsigned int count); void dql_reset(struct dql *dql); /* Initialize dql state */ -int dql_init(struct dql *dql, unsigned hold_time); +void dql_init(struct dql *dql, unsigned int hold_time); #endif /* _KERNEL_ */ diff --git a/lib/dynamic_queue_limits.c b/lib/dynamic_queue_limits.c index f346715e2255..dbe61c4c2a97 100644 --- a/lib/dynamic_queue_limits.c +++ b/lib/dynamic_queue_limits.c @@ -127,12 +127,11 @@ void dql_reset(struct dql *dql) } EXPORT_SYMBOL(dql_reset); -int dql_init(struct dql *dql, unsigned hold_time) +void dql_init(struct dql *dql, unsigned int hold_time) { dql->max_limit = DQL_MAX_LIMIT; dql->min_limit = 0; dql->slack_hold_time = hold_time; dql_reset(dql); - return 0; } EXPORT_SYMBOL(dql_init); -- cgit v1.2.3-59-g8ed1b From 93b138dd9d6e01cd360f347512b6c5bf6f0c4ce2 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 18 Sep 2017 00:01:44 +0900 Subject: printk: simplify no_printk() Commit 069f0cd00df0 ("printk: Make the printk*once() variants return a value") surrounded the macro implementation with ({ ... }). Now, the inner do { ... } while (0); is redundant. Link: http://lkml.kernel.org/r/1505660504-11059-1-git-send-email-yamada.masahiro@socionext.com Cc: Andrew Morton Cc: Ingo Molnar Cc: Masahiro Yamada Cc: Steven Rostedt Cc: linux-kernel@vger.kernel.org Signed-off-by: Masahiro Yamada Reviewed-by: Sergey Senozhatsky Signed-off-by: Petr Mladek --- include/linux/printk.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/printk.h b/include/linux/printk.h index e10f27468322..7911f7364346 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -131,10 +131,8 @@ struct va_format { */ #define no_printk(fmt, ...) \ ({ \ - do { \ - if (0) \ - printk(fmt, ##__VA_ARGS__); \ - } while (0); \ + if (0) \ + printk(fmt, ##__VA_ARGS__); \ 0; \ }) -- cgit v1.2.3-59-g8ed1b From aa293583f0fe8b1634aeadbea06b4d0d04c30a95 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Mon, 16 Oct 2017 17:18:40 +0200 Subject: configfs: make ci_type field, some pointers and function arguments const The ci_type field of the config_item structure do not modify the fields of the config_item_type structure it points to. And the other pointers initialized with ci_type do not modify the fields as well. So, make the ci_type field and the pointers initialized with ci_type as const. Make the struct config_item_type *type function argument of functions config_{item/group}_init_type_name const as the argument in both the functions is only stored in the ci_type field of a config_item structure which is now made const. Make the argument of configfs_register_default_group const as it is only passed to the argument of the function config_group_init_type_name which is now const. Signed-off-by: Bhumika Goyal Acked-by: Greg Kroah-Hartman Signed-off-by: Christoph Hellwig --- fs/configfs/dir.c | 10 +++++----- fs/configfs/item.c | 6 +++--- fs/configfs/symlink.c | 4 ++-- include/linux/configfs.h | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 56fb26127fef..577cff24707b 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c @@ -584,7 +584,7 @@ static void detach_attrs(struct config_item * item) static int populate_attrs(struct config_item *item) { - struct config_item_type *t = item->ci_type; + const struct config_item_type *t = item->ci_type; struct configfs_attribute *attr; struct configfs_bin_attribute *bin_attr; int error = 0; @@ -901,7 +901,7 @@ static void configfs_detach_group(struct config_item *item) static void client_disconnect_notify(struct config_item *parent_item, struct config_item *item) { - struct config_item_type *type; + const struct config_item_type *type; type = parent_item->ci_type; BUG_ON(!type); @@ -920,7 +920,7 @@ static void client_disconnect_notify(struct config_item *parent_item, static void client_drop_item(struct config_item *parent_item, struct config_item *item) { - struct config_item_type *type; + const struct config_item_type *type; type = parent_item->ci_type; BUG_ON(!type); @@ -1260,7 +1260,7 @@ static int configfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode struct config_item *parent_item; struct configfs_subsystem *subsys; struct configfs_dirent *sd; - struct config_item_type *type; + const struct config_item_type *type; struct module *subsys_owner = NULL, *new_item_owner = NULL; char *name; @@ -1810,7 +1810,7 @@ EXPORT_SYMBOL(configfs_unregister_group); struct config_group * configfs_register_default_group(struct config_group *parent_group, const char *name, - struct config_item_type *item_type) + const struct config_item_type *item_type) { int ret; struct config_group *group; diff --git a/fs/configfs/item.c b/fs/configfs/item.c index a66f6624d899..88f266efc09b 100644 --- a/fs/configfs/item.c +++ b/fs/configfs/item.c @@ -113,7 +113,7 @@ EXPORT_SYMBOL(config_item_set_name); void config_item_init_type_name(struct config_item *item, const char *name, - struct config_item_type *type) + const struct config_item_type *type) { config_item_set_name(item, "%s", name); item->ci_type = type; @@ -122,7 +122,7 @@ void config_item_init_type_name(struct config_item *item, EXPORT_SYMBOL(config_item_init_type_name); void config_group_init_type_name(struct config_group *group, const char *name, - struct config_item_type *type) + const struct config_item_type *type) { config_item_set_name(&group->cg_item, "%s", name); group->cg_item.ci_type = type; @@ -148,7 +148,7 @@ EXPORT_SYMBOL(config_item_get_unless_zero); static void config_item_cleanup(struct config_item *item) { - struct config_item_type *t = item->ci_type; + const struct config_item_type *t = item->ci_type; struct config_group *s = item->ci_group; struct config_item *parent = item->ci_parent; diff --git a/fs/configfs/symlink.c b/fs/configfs/symlink.c index c8aabba502f6..78ffc2699993 100644 --- a/fs/configfs/symlink.c +++ b/fs/configfs/symlink.c @@ -138,7 +138,7 @@ int configfs_symlink(struct inode *dir, struct dentry *dentry, const char *symna struct configfs_dirent *sd; struct config_item *parent_item; struct config_item *target_item = NULL; - struct config_item_type *type; + const struct config_item_type *type; sd = dentry->d_parent->d_fsdata; /* @@ -186,7 +186,7 @@ int configfs_unlink(struct inode *dir, struct dentry *dentry) struct configfs_dirent *sd = dentry->d_fsdata; struct configfs_symlink *sl; struct config_item *parent_item; - struct config_item_type *type; + const struct config_item_type *type; int ret; ret = -EPERM; /* What lack-of-symlink returns */ diff --git a/include/linux/configfs.h b/include/linux/configfs.h index c96709049683..90b90f8baf99 100644 --- a/include/linux/configfs.h +++ b/include/linux/configfs.h @@ -58,7 +58,7 @@ struct config_item { struct list_head ci_entry; struct config_item *ci_parent; struct config_group *ci_group; - struct config_item_type *ci_type; + const struct config_item_type *ci_type; struct dentry *ci_dentry; }; @@ -72,7 +72,7 @@ static inline char *config_item_name(struct config_item * item) extern void config_item_init_type_name(struct config_item *item, const char *name, - struct config_item_type *type); + const struct config_item_type *type); extern struct config_item *config_item_get(struct config_item *); extern struct config_item *config_item_get_unless_zero(struct config_item *); @@ -101,7 +101,7 @@ struct config_group { extern void config_group_init(struct config_group *group); extern void config_group_init_type_name(struct config_group *group, const char *name, - struct config_item_type *type); + const struct config_item_type *type); static inline struct config_group *to_config_group(struct config_item *item) { @@ -261,7 +261,7 @@ void configfs_remove_default_groups(struct config_group *group); struct config_group * configfs_register_default_group(struct config_group *parent_group, const char *name, - struct config_item_type *item_type); + const struct config_item_type *item_type); void configfs_unregister_default_group(struct config_group *group); /* These functions can sleep and can alloc with GFP_KERNEL */ -- cgit v1.2.3-59-g8ed1b From ece550b5758645809876e6e7862bf485c3a8d15c Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Mon, 16 Oct 2017 17:18:42 +0200 Subject: target: make config_item_type const Make these structures const as they are either passed to the functions having the argument as const or stored as a reference in the "ci_type" const field of a config_item structure. Done using Coccinelle. Signed-off-by: Bhumika Goyal Signed-off-by: Christoph Hellwig --- drivers/target/iscsi/iscsi_target_stat.c | 12 ++++++------ drivers/target/target_core_configfs.c | 14 +++++++------- drivers/target/target_core_stat.c | 16 ++++++++-------- include/target/iscsi/iscsi_target_stat.h | 12 ++++++------ 4 files changed, 27 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/target/iscsi/iscsi_target_stat.c b/drivers/target/iscsi/iscsi_target_stat.c index 411cb266a47d..df0a39811dc2 100644 --- a/drivers/target/iscsi/iscsi_target_stat.c +++ b/drivers/target/iscsi/iscsi_target_stat.c @@ -187,7 +187,7 @@ static struct configfs_attribute *iscsi_stat_instance_attrs[] = { NULL, }; -struct config_item_type iscsi_stat_instance_cit = { +const struct config_item_type iscsi_stat_instance_cit = { .ct_attrs = iscsi_stat_instance_attrs, .ct_owner = THIS_MODULE, }; @@ -249,7 +249,7 @@ static struct configfs_attribute *iscsi_stat_sess_err_attrs[] = { NULL, }; -struct config_item_type iscsi_stat_sess_err_cit = { +const struct config_item_type iscsi_stat_sess_err_cit = { .ct_attrs = iscsi_stat_sess_err_attrs, .ct_owner = THIS_MODULE, }; @@ -390,7 +390,7 @@ static struct configfs_attribute *iscsi_stat_tgt_attr_attrs[] = { NULL, }; -struct config_item_type iscsi_stat_tgt_attr_cit = { +const struct config_item_type iscsi_stat_tgt_attr_cit = { .ct_attrs = iscsi_stat_tgt_attr_attrs, .ct_owner = THIS_MODULE, }; @@ -522,7 +522,7 @@ static struct configfs_attribute *iscsi_stat_login_stats_attrs[] = { NULL, }; -struct config_item_type iscsi_stat_login_cit = { +const struct config_item_type iscsi_stat_login_cit = { .ct_attrs = iscsi_stat_login_stats_attrs, .ct_owner = THIS_MODULE, }; @@ -579,7 +579,7 @@ static struct configfs_attribute *iscsi_stat_logout_stats_attrs[] = { NULL, }; -struct config_item_type iscsi_stat_logout_cit = { +const struct config_item_type iscsi_stat_logout_cit = { .ct_attrs = iscsi_stat_logout_stats_attrs, .ct_owner = THIS_MODULE, }; @@ -801,7 +801,7 @@ static struct configfs_attribute *iscsi_stat_sess_stats_attrs[] = { NULL, }; -struct config_item_type iscsi_stat_sess_cit = { +const struct config_item_type iscsi_stat_sess_cit = { .ct_attrs = iscsi_stat_sess_stats_attrs, .ct_owner = THIS_MODULE, }; diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 7e87d952bb7a..bd87cc26c6e5 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -307,7 +307,7 @@ static struct configfs_attribute *target_core_fabric_item_attrs[] = { /* * Provides Fabrics Groups and Item Attributes for /sys/kernel/config/target/ */ -static struct config_item_type target_core_fabrics_item = { +static const struct config_item_type target_core_fabrics_item = { .ct_group_ops = &target_core_fabric_group_ops, .ct_attrs = target_core_fabric_item_attrs, .ct_owner = THIS_MODULE, @@ -2376,7 +2376,7 @@ static struct configfs_item_operations target_core_alua_lu_gp_ops = { .release = target_core_alua_lu_gp_release, }; -static struct config_item_type target_core_alua_lu_gp_cit = { +static const struct config_item_type target_core_alua_lu_gp_cit = { .ct_item_ops = &target_core_alua_lu_gp_ops, .ct_attrs = target_core_alua_lu_gp_attrs, .ct_owner = THIS_MODULE, @@ -2434,7 +2434,7 @@ static struct configfs_group_operations target_core_alua_lu_gps_group_ops = { .drop_item = &target_core_alua_drop_lu_gp, }; -static struct config_item_type target_core_alua_lu_gps_cit = { +static const struct config_item_type target_core_alua_lu_gps_cit = { .ct_item_ops = NULL, .ct_group_ops = &target_core_alua_lu_gps_group_ops, .ct_owner = THIS_MODULE, @@ -2813,7 +2813,7 @@ static struct configfs_item_operations target_core_alua_tg_pt_gp_ops = { .release = target_core_alua_tg_pt_gp_release, }; -static struct config_item_type target_core_alua_tg_pt_gp_cit = { +static const struct config_item_type target_core_alua_tg_pt_gp_cit = { .ct_item_ops = &target_core_alua_tg_pt_gp_ops, .ct_attrs = target_core_alua_tg_pt_gp_attrs, .ct_owner = THIS_MODULE, @@ -2884,7 +2884,7 @@ TB_CIT_SETUP(dev_alua_tg_pt_gps, NULL, &target_core_alua_tg_pt_gps_group_ops, NU * core/alua/lu_gps and core/alua/tg_pt_gps that are attached to * target_core_alua_cit in target_core_init_configfs() below. */ -static struct config_item_type target_core_alua_cit = { +static const struct config_item_type target_core_alua_cit = { .ct_item_ops = NULL, .ct_attrs = NULL, .ct_owner = THIS_MODULE, @@ -3105,7 +3105,7 @@ static struct configfs_item_operations target_core_hba_item_ops = { .release = target_core_hba_release, }; -static struct config_item_type target_core_hba_cit = { +static const struct config_item_type target_core_hba_cit = { .ct_item_ops = &target_core_hba_item_ops, .ct_group_ops = &target_core_hba_group_ops, .ct_attrs = target_core_hba_attrs, @@ -3188,7 +3188,7 @@ static struct configfs_group_operations target_core_group_ops = { .drop_item = target_core_call_delhbafromtarget, }; -static struct config_item_type target_core_cit = { +static const struct config_item_type target_core_cit = { .ct_item_ops = NULL, .ct_group_ops = &target_core_group_ops, .ct_attrs = NULL, diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c index 8038255b21e8..f0db91ebd735 100644 --- a/drivers/target/target_core_stat.c +++ b/drivers/target/target_core_stat.c @@ -96,7 +96,7 @@ static struct configfs_attribute *target_stat_scsi_dev_attrs[] = { NULL, }; -static struct config_item_type target_stat_scsi_dev_cit = { +static const struct config_item_type target_stat_scsi_dev_cit = { .ct_attrs = target_stat_scsi_dev_attrs, .ct_owner = THIS_MODULE, }; @@ -193,7 +193,7 @@ static struct configfs_attribute *target_stat_scsi_tgt_dev_attrs[] = { NULL, }; -static struct config_item_type target_stat_scsi_tgt_dev_cit = { +static const struct config_item_type target_stat_scsi_tgt_dev_cit = { .ct_attrs = target_stat_scsi_tgt_dev_attrs, .ct_owner = THIS_MODULE, }; @@ -414,7 +414,7 @@ static struct configfs_attribute *target_stat_scsi_lu_attrs[] = { NULL, }; -static struct config_item_type target_stat_scsi_lu_cit = { +static const struct config_item_type target_stat_scsi_lu_cit = { .ct_attrs = target_stat_scsi_lu_attrs, .ct_owner = THIS_MODULE, }; @@ -540,7 +540,7 @@ static struct configfs_attribute *target_stat_scsi_port_attrs[] = { NULL, }; -static struct config_item_type target_stat_scsi_port_cit = { +static const struct config_item_type target_stat_scsi_port_cit = { .ct_attrs = target_stat_scsi_port_attrs, .ct_owner = THIS_MODULE, }; @@ -724,7 +724,7 @@ static struct configfs_attribute *target_stat_scsi_tgt_port_attrs[] = { NULL, }; -static struct config_item_type target_stat_scsi_tgt_port_cit = { +static const struct config_item_type target_stat_scsi_tgt_port_cit = { .ct_attrs = target_stat_scsi_tgt_port_attrs, .ct_owner = THIS_MODULE, }; @@ -844,7 +844,7 @@ static struct configfs_attribute *target_stat_scsi_transport_attrs[] = { NULL, }; -static struct config_item_type target_stat_scsi_transport_cit = { +static const struct config_item_type target_stat_scsi_transport_cit = { .ct_attrs = target_stat_scsi_transport_attrs, .ct_owner = THIS_MODULE, }; @@ -1206,7 +1206,7 @@ static struct configfs_attribute *target_stat_scsi_auth_intr_attrs[] = { NULL, }; -static struct config_item_type target_stat_scsi_auth_intr_cit = { +static const struct config_item_type target_stat_scsi_auth_intr_cit = { .ct_attrs = target_stat_scsi_auth_intr_attrs, .ct_owner = THIS_MODULE, }; @@ -1378,7 +1378,7 @@ static struct configfs_attribute *target_stat_scsi_ath_intr_port_attrs[] = { NULL, }; -static struct config_item_type target_stat_scsi_att_intr_port_cit = { +static const struct config_item_type target_stat_scsi_att_intr_port_cit = { .ct_attrs = target_stat_scsi_ath_intr_port_attrs, .ct_owner = THIS_MODULE, }; diff --git a/include/target/iscsi/iscsi_target_stat.h b/include/target/iscsi/iscsi_target_stat.h index c27dd471656d..36e9c9a65adf 100644 --- a/include/target/iscsi/iscsi_target_stat.h +++ b/include/target/iscsi/iscsi_target_stat.h @@ -8,16 +8,16 @@ /* * For struct iscsi_tiqn->tiqn_wwn default groups */ -extern struct config_item_type iscsi_stat_instance_cit; -extern struct config_item_type iscsi_stat_sess_err_cit; -extern struct config_item_type iscsi_stat_tgt_attr_cit; -extern struct config_item_type iscsi_stat_login_cit; -extern struct config_item_type iscsi_stat_logout_cit; +extern const struct config_item_type iscsi_stat_instance_cit; +extern const struct config_item_type iscsi_stat_sess_err_cit; +extern const struct config_item_type iscsi_stat_tgt_attr_cit; +extern const struct config_item_type iscsi_stat_login_cit; +extern const struct config_item_type iscsi_stat_logout_cit; /* * For struct iscsi_session->se_sess default groups */ -extern struct config_item_type iscsi_stat_sess_cit; +extern const struct config_item_type iscsi_stat_sess_cit; /* iSCSI session error types */ #define ISCSI_SESS_ERR_UNKNOWN 0 -- cgit v1.2.3-59-g8ed1b From 612a462acbb9f3bfc7c8433b44118b0a156560da Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Mon, 16 Oct 2017 17:18:43 +0200 Subject: iio: make function argument and some structures const Make the argument of the functions iio_sw{d/t}_group_init_type_name const as they are only passed to the function config_group_init_type_name having the argument as const. Make the config_item_type structures const as they are either passed to the functions having the argument as const or they are stored in the const "ci_type" field of a config_item structure. Signed-off-by: Bhumika Goyal Acked-by: Jonathan Cameron Signed-off-by: Christoph Hellwig --- drivers/iio/dummy/iio_simple_dummy.c | 2 +- drivers/iio/industrialio-configfs.c | 2 +- drivers/iio/industrialio-sw-device.c | 6 +++--- drivers/iio/industrialio-sw-trigger.c | 6 +++--- drivers/iio/trigger/iio-trig-hrtimer.c | 2 +- drivers/iio/trigger/iio-trig-loop.c | 2 +- include/linux/iio/sw_device.h | 2 +- include/linux/iio/sw_trigger.h | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/iio/dummy/iio_simple_dummy.c b/drivers/iio/dummy/iio_simple_dummy.c index ad3410e528b6..e177a0862b2b 100644 --- a/drivers/iio/dummy/iio_simple_dummy.c +++ b/drivers/iio/dummy/iio_simple_dummy.c @@ -26,7 +26,7 @@ #include #include "iio_simple_dummy.h" -static struct config_item_type iio_dummy_type = { +static const struct config_item_type iio_dummy_type = { .ct_owner = THIS_MODULE, }; diff --git a/drivers/iio/industrialio-configfs.c b/drivers/iio/industrialio-configfs.c index 45ce2bc47180..5a0aae119369 100644 --- a/drivers/iio/industrialio-configfs.c +++ b/drivers/iio/industrialio-configfs.c @@ -17,7 +17,7 @@ #include #include -static struct config_item_type iio_root_group_type = { +static const struct config_item_type iio_root_group_type = { .ct_owner = THIS_MODULE, }; diff --git a/drivers/iio/industrialio-sw-device.c b/drivers/iio/industrialio-sw-device.c index 81b49cfca452..90df97c542f6 100644 --- a/drivers/iio/industrialio-sw-device.c +++ b/drivers/iio/industrialio-sw-device.c @@ -19,9 +19,9 @@ #include static struct config_group *iio_devices_group; -static struct config_item_type iio_device_type_group_type; +static const struct config_item_type iio_device_type_group_type; -static struct config_item_type iio_devices_group_type = { +static const struct config_item_type iio_devices_group_type = { .ct_owner = THIS_MODULE, }; @@ -156,7 +156,7 @@ static struct configfs_group_operations device_ops = { .drop_item = &device_drop_group, }; -static struct config_item_type iio_device_type_group_type = { +static const struct config_item_type iio_device_type_group_type = { .ct_group_ops = &device_ops, .ct_owner = THIS_MODULE, }; diff --git a/drivers/iio/industrialio-sw-trigger.c b/drivers/iio/industrialio-sw-trigger.c index 8d24fb159cc9..bc6b7fb43e3a 100644 --- a/drivers/iio/industrialio-sw-trigger.c +++ b/drivers/iio/industrialio-sw-trigger.c @@ -19,9 +19,9 @@ #include static struct config_group *iio_triggers_group; -static struct config_item_type iio_trigger_type_group_type; +static const struct config_item_type iio_trigger_type_group_type; -static struct config_item_type iio_triggers_group_type = { +static const struct config_item_type iio_triggers_group_type = { .ct_owner = THIS_MODULE, }; @@ -156,7 +156,7 @@ static struct configfs_group_operations trigger_ops = { .drop_item = &trigger_drop_group, }; -static struct config_item_type iio_trigger_type_group_type = { +static const struct config_item_type iio_trigger_type_group_type = { .ct_group_ops = &trigger_ops, .ct_owner = THIS_MODULE, }; diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c index a1cad6cc2e0f..ce7d120b348e 100644 --- a/drivers/iio/trigger/iio-trig-hrtimer.c +++ b/drivers/iio/trigger/iio-trig-hrtimer.c @@ -30,7 +30,7 @@ struct iio_hrtimer_info { ktime_t period; }; -static struct config_item_type iio_hrtimer_type = { +static const struct config_item_type iio_hrtimer_type = { .ct_owner = THIS_MODULE, }; diff --git a/drivers/iio/trigger/iio-trig-loop.c b/drivers/iio/trigger/iio-trig-loop.c index dc6be28f96fe..745a01f04a27 100644 --- a/drivers/iio/trigger/iio-trig-loop.c +++ b/drivers/iio/trigger/iio-trig-loop.c @@ -36,7 +36,7 @@ struct iio_loop_info { struct task_struct *task; }; -static struct config_item_type iio_loop_type = { +static const struct config_item_type iio_loop_type = { .ct_owner = THIS_MODULE, }; diff --git a/include/linux/iio/sw_device.h b/include/linux/iio/sw_device.h index fa7931933067..8642b91a7577 100644 --- a/include/linux/iio/sw_device.h +++ b/include/linux/iio/sw_device.h @@ -60,7 +60,7 @@ void iio_sw_device_type_configfs_unregister(struct iio_sw_device_type *dt); static inline void iio_swd_group_init_type_name(struct iio_sw_device *d, const char *name, - struct config_item_type *type) + const struct config_item_type *type) { #if IS_ENABLED(CONFIG_CONFIGFS_FS) config_group_init_type_name(&d->group, name, type); diff --git a/include/linux/iio/sw_trigger.h b/include/linux/iio/sw_trigger.h index c97eab67558f..0c43738a9e24 100644 --- a/include/linux/iio/sw_trigger.h +++ b/include/linux/iio/sw_trigger.h @@ -60,7 +60,7 @@ void iio_sw_trigger_type_configfs_unregister(struct iio_sw_trigger_type *tt); static inline void iio_swt_group_init_type_name(struct iio_sw_trigger *t, const char *name, - struct config_item_type *type) + const struct config_item_type *type) { #if IS_ENABLED(CONFIG_CONFIGFS_FS) config_group_init_type_name(&t->group, name, type); -- cgit v1.2.3-59-g8ed1b From 3e09b155d58e44e33a699650128f8c6c833cb320 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Mon, 24 Jul 2017 19:29:15 +0300 Subject: dt-bindings: Add bindings for nvidia,tegra186-bpmp-thermal In Tegra186, the BPMP (Boot and Power Management Processor) implements an interface that is used to read system temperatures, including CPU cluster and GPU temperatures. This binding describes the thermal sensor that is exposed by BPMP. Signed-off-by: Mikko Perttunen Acked-by: Rob Herring Signed-off-by: Thierry Reding --- .../thermal/nvidia,tegra186-bpmp-thermal.txt | 32 ++++++++++++++++++++++ .../dt-bindings/thermal/tegra186-bpmp-thermal.h | 14 ++++++++++ 2 files changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/nvidia,tegra186-bpmp-thermal.txt create mode 100644 include/dt-bindings/thermal/tegra186-bpmp-thermal.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/thermal/nvidia,tegra186-bpmp-thermal.txt b/Documentation/devicetree/bindings/thermal/nvidia,tegra186-bpmp-thermal.txt new file mode 100644 index 000000000000..276387dd6815 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/nvidia,tegra186-bpmp-thermal.txt @@ -0,0 +1,32 @@ +NVIDIA Tegra186 BPMP thermal sensor + +In Tegra186, the BPMP (Boot and Power Management Processor) implements an +interface that is used to read system temperatures, including CPU cluster +and GPU temperatures. This binding describes the thermal sensor that is +exposed by BPMP. + +The BPMP thermal node must be located directly inside the main BPMP node. See +../firmware/nvidia,tegra186-bpmp.txt for details of the BPMP binding. + +This node represents a thermal sensor. See thermal.txt for details of the +core thermal binding. + +Required properties: +- compatible: + Array of strings. + One of: + - "nvidia,tegra186-bpmp-thermal". +- #thermal-sensor-cells: Cell for sensor index. + Single-cell integer. + Must be <1>. + +Example: + +bpmp { + ... + + bpmp_thermal: thermal { + compatible = "nvidia,tegra186-bpmp-thermal"; + #thermal-sensor-cells = <1>; + }; +}; diff --git a/include/dt-bindings/thermal/tegra186-bpmp-thermal.h b/include/dt-bindings/thermal/tegra186-bpmp-thermal.h new file mode 100644 index 000000000000..a96b8fa31aab --- /dev/null +++ b/include/dt-bindings/thermal/tegra186-bpmp-thermal.h @@ -0,0 +1,14 @@ +/* + * This header provides constants for binding nvidia,tegra186-bpmp-thermal. + */ + +#ifndef _DT_BINDINGS_THERMAL_TEGRA186_BPMP_THERMAL_H +#define _DT_BINDINGS_THERMAL_TEGRA186_BPMP_THERMAL_H + +#define TEGRA186_BPMP_THERMAL_ZONE_CPU 2 +#define TEGRA186_BPMP_THERMAL_ZONE_GPU 3 +#define TEGRA186_BPMP_THERMAL_ZONE_AUX 4 +#define TEGRA186_BPMP_THERMAL_ZONE_PLLX 5 +#define TEGRA186_BPMP_THERMAL_ZONE_AO 6 + +#endif -- cgit v1.2.3-59-g8ed1b From 2e1e09ed423781d209c1fea773b1c3e614550ea5 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Mon, 24 Jul 2017 19:29:16 +0300 Subject: firmware: tegra: Expose tegra_bpmp_mrq_return() Expose and export the tegra_bpmp_mrq_return() function for use by drivers outside the core BPMP driver. This function is used to reply to messages originating from the BPMP, which is required in the thermal driver. Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding --- drivers/firmware/tegra/bpmp.c | 5 +++-- include/soc/tegra/bpmp.h | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 33683b5a0d48..aa4bbbd7bdb4 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -397,8 +397,8 @@ static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp, return NULL; } -static void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, - int code, const void *data, size_t size) +void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code, + const void *data, size_t size) { unsigned long flags = channel->ib->flags; struct tegra_bpmp *bpmp = channel->bpmp; @@ -436,6 +436,7 @@ static void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, mbox_client_txdone(bpmp->mbox.channel, 0); } } +EXPORT_SYMBOL_GPL(tegra_bpmp_mrq_return); static void tegra_bpmp_handle_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h index 57519f4c126a..b382ed01af09 100644 --- a/include/soc/tegra/bpmp.h +++ b/include/soc/tegra/bpmp.h @@ -118,6 +118,8 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, struct tegra_bpmp_message *msg); int tegra_bpmp_transfer(struct tegra_bpmp *bpmp, struct tegra_bpmp_message *msg); +void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code, + const void *data, size_t size); int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, tegra_bpmp_mrq_handler_t handler, void *data); -- cgit v1.2.3-59-g8ed1b From 80d47a91e5db96e5db391aaad414fb6ceb40e7c0 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Mon, 24 Jul 2017 19:29:17 +0300 Subject: firmware: tegra: Add stubs when BPMP not enabled Add static inline stubs to bpmp.h when CONFIG_BPMP is not enabled. This allows building BPMP-related drivers with COMPILE_TEST. Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding --- include/soc/tegra/bpmp.h | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h index b382ed01af09..1cf210504814 100644 --- a/include/soc/tegra/bpmp.h +++ b/include/soc/tegra/bpmp.h @@ -96,9 +96,6 @@ struct tegra_bpmp { struct genpd_onecell_data genpd; }; -struct tegra_bpmp *tegra_bpmp_get(struct device *dev); -void tegra_bpmp_put(struct tegra_bpmp *bpmp); - struct tegra_bpmp_message { unsigned int mrq; @@ -114,6 +111,9 @@ struct tegra_bpmp_message { } rx; }; +#if IS_ENABLED(CONFIG_TEGRA_BPMP) +struct tegra_bpmp *tegra_bpmp_get(struct device *dev); +void tegra_bpmp_put(struct tegra_bpmp *bpmp); int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, struct tegra_bpmp_message *msg); int tegra_bpmp_transfer(struct tegra_bpmp *bpmp, @@ -125,6 +125,42 @@ int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, tegra_bpmp_mrq_handler_t handler, void *data); void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void *data); +#else +static inline struct tegra_bpmp *tegra_bpmp_get(struct device *dev) +{ + return ERR_PTR(-ENOTSUPP); +} +static inline void tegra_bpmp_put(struct tegra_bpmp *bpmp) +{ +} +static inline int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, + struct tegra_bpmp_message *msg) +{ + return -ENOTSUPP; +} +static inline int tegra_bpmp_transfer(struct tegra_bpmp *bpmp, + struct tegra_bpmp_message *msg) +{ + return -ENOTSUPP; +} +static inline void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, + int code, const void *data, + size_t size) +{ +} + +static inline int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, + unsigned int mrq, + tegra_bpmp_mrq_handler_t handler, + void *data) +{ + return -ENOTSUPP; +} +static inline void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, + unsigned int mrq, void *data) +{ +} +#endif #if IS_ENABLED(CONFIG_CLK_TEGRA_BPMP) int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp); -- cgit v1.2.3-59-g8ed1b From f2381f652266fabfb7a8f5c4b2a05de36cad3a73 Mon Sep 17 00:00:00 2001 From: Timo Alho Date: Tue, 3 Oct 2017 09:12:13 +0300 Subject: firmware: tegra: Add BPMP debugfs support Tegra power management firmware running on the co-processor (BPMP) implements a simple pseudo file system akin to debugfs. The file system can be used for debugging purposes to examine and change the status of selected resources controlled by the firmware (such as clocks, resets, voltages, powergates, ...). Add support to "mirror" the firmware's file system to debugfs. At boot, query firmware for a list of all possible files and create corresponding debugfs entries. Read/write of individual files is implemented by sending a Message ReQuest (MRQ) that passes the full file path name and data to firmware via DRAM. Signed-off-by: Timo Alho Reviewed-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/firmware/tegra/Makefile | 4 +- drivers/firmware/tegra/bpmp-debugfs.c | 444 ++++++++++++++++++++++++++++++++++ drivers/firmware/tegra/bpmp.c | 4 + include/soc/tegra/bpmp.h | 14 ++ 4 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/tegra/bpmp-debugfs.c (limited to 'include') diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index e34a2f79e1ad..1b826dcca719 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile @@ -1,2 +1,4 @@ -obj-$(CONFIG_TEGRA_BPMP) += bpmp.o +tegra-bpmp-y = bpmp.o +tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o +obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o obj-$(CONFIG_TEGRA_IVC) += ivc.o diff --git a/drivers/firmware/tegra/bpmp-debugfs.c b/drivers/firmware/tegra/bpmp-debugfs.c new file mode 100644 index 000000000000..f7f6a0a5cb07 --- /dev/null +++ b/drivers/firmware/tegra/bpmp-debugfs.c @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + * + * 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 + +struct seqbuf { + char *buf; + size_t pos; + size_t size; +}; + +static void seqbuf_init(struct seqbuf *seqbuf, void *buf, size_t size) +{ + seqbuf->buf = buf; + seqbuf->size = size; + seqbuf->pos = 0; +} + +static size_t seqbuf_avail(struct seqbuf *seqbuf) +{ + return seqbuf->pos < seqbuf->size ? seqbuf->size - seqbuf->pos : 0; +} + +static size_t seqbuf_status(struct seqbuf *seqbuf) +{ + return seqbuf->pos <= seqbuf->size ? 0 : -EOVERFLOW; +} + +static int seqbuf_eof(struct seqbuf *seqbuf) +{ + return seqbuf->pos >= seqbuf->size; +} + +static int seqbuf_read(struct seqbuf *seqbuf, void *buf, size_t nbyte) +{ + nbyte = min(nbyte, seqbuf_avail(seqbuf)); + memcpy(buf, seqbuf->buf + seqbuf->pos, nbyte); + seqbuf->pos += nbyte; + return seqbuf_status(seqbuf); +} + +static int seqbuf_read_u32(struct seqbuf *seqbuf, uint32_t *v) +{ + int err; + + err = seqbuf_read(seqbuf, v, 4); + *v = le32_to_cpu(*v); + return err; +} + +static int seqbuf_read_str(struct seqbuf *seqbuf, const char **str) +{ + *str = seqbuf->buf + seqbuf->pos; + seqbuf->pos += strnlen(*str, seqbuf_avail(seqbuf)); + seqbuf->pos++; + return seqbuf_status(seqbuf); +} + +static void seqbuf_seek(struct seqbuf *seqbuf, ssize_t offset) +{ + seqbuf->pos += offset; +} + +/* map filename in Linux debugfs to corresponding entry in BPMP */ +static const char *get_filename(struct tegra_bpmp *bpmp, + const struct file *file, char *buf, int size) +{ + char root_path_buf[512]; + const char *root_path; + const char *filename; + size_t root_len; + + root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf, + sizeof(root_path_buf)); + if (IS_ERR(root_path)) + return NULL; + + root_len = strlen(root_path); + + filename = dentry_path(file->f_path.dentry, buf, size); + if (IS_ERR(filename)) + return NULL; + + if (strlen(filename) < root_len || + strncmp(filename, root_path, root_len)) + return NULL; + + filename += root_len; + + return filename; +} + +static int mrq_debugfs_read(struct tegra_bpmp *bpmp, + dma_addr_t name, size_t sz_name, + dma_addr_t data, size_t sz_data, + size_t *nbytes) +{ + struct mrq_debugfs_request req = { + .cmd = cpu_to_le32(CMD_DEBUGFS_READ), + .fop = { + .fnameaddr = cpu_to_le32((uint32_t)name), + .fnamelen = cpu_to_le32((uint32_t)sz_name), + .dataaddr = cpu_to_le32((uint32_t)data), + .datalen = cpu_to_le32((uint32_t)sz_data), + }, + }; + struct mrq_debugfs_response resp; + struct tegra_bpmp_message msg = { + .mrq = MRQ_DEBUGFS, + .tx = { + .data = &req, + .size = sizeof(req), + }, + .rx = { + .data = &resp, + .size = sizeof(resp), + }, + }; + int err; + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return err; + + *nbytes = (size_t)resp.fop.nbytes; + + return 0; +} + +static int mrq_debugfs_write(struct tegra_bpmp *bpmp, + dma_addr_t name, size_t sz_name, + dma_addr_t data, size_t sz_data) +{ + const struct mrq_debugfs_request req = { + .cmd = cpu_to_le32(CMD_DEBUGFS_WRITE), + .fop = { + .fnameaddr = cpu_to_le32((uint32_t)name), + .fnamelen = cpu_to_le32((uint32_t)sz_name), + .dataaddr = cpu_to_le32((uint32_t)data), + .datalen = cpu_to_le32((uint32_t)sz_data), + }, + }; + struct tegra_bpmp_message msg = { + .mrq = MRQ_DEBUGFS, + .tx = { + .data = &req, + .size = sizeof(req), + }, + }; + + return tegra_bpmp_transfer(bpmp, &msg); +} + +static int mrq_debugfs_dumpdir(struct tegra_bpmp *bpmp, dma_addr_t addr, + size_t size, size_t *nbytes) +{ + const struct mrq_debugfs_request req = { + .cmd = cpu_to_le32(CMD_DEBUGFS_DUMPDIR), + .dumpdir = { + .dataaddr = cpu_to_le32((uint32_t)addr), + .datalen = cpu_to_le32((uint32_t)size), + }, + }; + struct mrq_debugfs_response resp; + struct tegra_bpmp_message msg = { + .mrq = MRQ_DEBUGFS, + .tx = { + .data = &req, + .size = sizeof(req), + }, + .rx = { + .data = &resp, + .size = sizeof(resp), + }, + }; + int err; + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return err; + + *nbytes = (size_t)resp.dumpdir.nbytes; + + return 0; +} + +static int debugfs_show(struct seq_file *m, void *p) +{ + struct file *file = m->private; + struct inode *inode = file_inode(file); + struct tegra_bpmp *bpmp = inode->i_private; + const size_t datasize = m->size; + const size_t namesize = SZ_256; + void *datavirt, *namevirt; + dma_addr_t dataphys, namephys; + char buf[256]; + const char *filename; + size_t len, nbytes; + int ret; + + filename = get_filename(bpmp, file, buf, sizeof(buf)); + if (!filename) + return -ENOENT; + + namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys, + GFP_KERNEL | GFP_DMA32); + if (!namevirt) + return -ENOMEM; + + datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys, + GFP_KERNEL | GFP_DMA32); + if (!datavirt) { + ret = -ENOMEM; + goto free_namebuf; + } + + len = strlen(filename); + strncpy(namevirt, filename, namesize); + + ret = mrq_debugfs_read(bpmp, namephys, len, dataphys, datasize, + &nbytes); + + if (!ret) + seq_write(m, datavirt, nbytes); + + dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys); +free_namebuf: + dma_free_coherent(bpmp->dev, namesize, namevirt, namephys); + + return ret; +} + +static int debugfs_open(struct inode *inode, struct file *file) +{ + return single_open_size(file, debugfs_show, file, SZ_128K); +} + +static ssize_t debugfs_store(struct file *file, const char __user *buf, + size_t count, loff_t *f_pos) +{ + struct inode *inode = file_inode(file); + struct tegra_bpmp *bpmp = inode->i_private; + const size_t datasize = count; + const size_t namesize = SZ_256; + void *datavirt, *namevirt; + dma_addr_t dataphys, namephys; + char fnamebuf[256]; + const char *filename; + size_t len; + int ret; + + filename = get_filename(bpmp, file, fnamebuf, sizeof(fnamebuf)); + if (!filename) + return -ENOENT; + + namevirt = dma_alloc_coherent(bpmp->dev, namesize, &namephys, + GFP_KERNEL | GFP_DMA32); + if (!namevirt) + return -ENOMEM; + + datavirt = dma_alloc_coherent(bpmp->dev, datasize, &dataphys, + GFP_KERNEL | GFP_DMA32); + if (!datavirt) { + ret = -ENOMEM; + goto free_namebuf; + } + + len = strlen(filename); + strncpy(namevirt, filename, namesize); + + if (copy_from_user(datavirt, buf, count)) { + ret = -EFAULT; + goto free_databuf; + } + + ret = mrq_debugfs_write(bpmp, namephys, len, dataphys, + count); + +free_databuf: + dma_free_coherent(bpmp->dev, datasize, datavirt, dataphys); +free_namebuf: + dma_free_coherent(bpmp->dev, namesize, namevirt, namephys); + + return ret ?: count; +} + +static const struct file_operations debugfs_fops = { + .open = debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .write = debugfs_store, + .release = single_release, +}; + +static int bpmp_populate_dir(struct tegra_bpmp *bpmp, struct seqbuf *seqbuf, + struct dentry *parent, uint32_t depth) +{ + int err; + uint32_t d, t; + const char *name; + struct dentry *dentry; + + while (!seqbuf_eof(seqbuf)) { + err = seqbuf_read_u32(seqbuf, &d); + if (err < 0) + return err; + + if (d < depth) { + seqbuf_seek(seqbuf, -4); + /* go up a level */ + return 0; + } else if (d != depth) { + /* malformed data received from BPMP */ + return -EIO; + } + + err = seqbuf_read_u32(seqbuf, &t); + if (err < 0) + return err; + err = seqbuf_read_str(seqbuf, &name); + if (err < 0) + return err; + + if (t & DEBUGFS_S_ISDIR) { + dentry = debugfs_create_dir(name, parent); + if (!dentry) + return -ENOMEM; + err = bpmp_populate_dir(bpmp, seqbuf, dentry, depth+1); + if (err < 0) + return err; + } else { + umode_t mode; + + mode = t & DEBUGFS_S_IRUSR ? S_IRUSR : 0; + mode |= t & DEBUGFS_S_IWUSR ? S_IWUSR : 0; + dentry = debugfs_create_file(name, mode, + parent, bpmp, + &debugfs_fops); + if (!dentry) + return -ENOMEM; + } + } + + return 0; +} + +static int create_debugfs_mirror(struct tegra_bpmp *bpmp, void *buf, + size_t bufsize, struct dentry *root) +{ + struct seqbuf seqbuf; + int err; + + bpmp->debugfs_mirror = debugfs_create_dir("debug", root); + if (!bpmp->debugfs_mirror) + return -ENOMEM; + + seqbuf_init(&seqbuf, buf, bufsize); + err = bpmp_populate_dir(bpmp, &seqbuf, bpmp->debugfs_mirror, 0); + if (err < 0) { + debugfs_remove_recursive(bpmp->debugfs_mirror); + bpmp->debugfs_mirror = NULL; + } + + return err; +} + +static int mrq_is_supported(struct tegra_bpmp *bpmp, unsigned int mrq) +{ + struct mrq_query_abi_request req = { .mrq = cpu_to_le32(mrq) }; + struct mrq_query_abi_response resp; + struct tegra_bpmp_message msg = { + .mrq = MRQ_QUERY_ABI, + .tx = { + .data = &req, + .size = sizeof(req), + }, + .rx = { + .data = &resp, + .size = sizeof(resp), + }, + }; + int ret; + + ret = tegra_bpmp_transfer(bpmp, &msg); + if (ret < 0) { + /* something went wrong; assume not supported */ + dev_warn(bpmp->dev, "tegra_bpmp_transfer failed (%d)\n", ret); + return 0; + } + + return resp.status ? 0 : 1; +} + +int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp) +{ + dma_addr_t phys; + void *virt; + const size_t sz = SZ_256K; + size_t nbytes; + int ret; + struct dentry *root; + + if (!mrq_is_supported(bpmp, MRQ_DEBUGFS)) + return 0; + + root = debugfs_create_dir("bpmp", NULL); + if (!root) + return -ENOMEM; + + virt = dma_alloc_coherent(bpmp->dev, sz, &phys, + GFP_KERNEL | GFP_DMA32); + if (!virt) { + ret = -ENOMEM; + goto out; + } + + ret = mrq_debugfs_dumpdir(bpmp, phys, sz, &nbytes); + if (ret < 0) + goto free; + + ret = create_debugfs_mirror(bpmp, virt, nbytes, root); +free: + dma_free_coherent(bpmp->dev, sz, virt, phys); +out: + if (ret < 0) + debugfs_remove(root); + + return ret; +} diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index aa4bbbd7bdb4..a7f461f2e650 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -835,6 +835,10 @@ static int tegra_bpmp_probe(struct platform_device *pdev) if (err < 0) goto free_mrq; + err = tegra_bpmp_init_debugfs(bpmp); + if (err < 0) + dev_err(&pdev->dev, "debugfs initialization failed: %d\n", err); + return 0; free_mrq: diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h index 1cf210504814..aeae4466dd25 100644 --- a/include/soc/tegra/bpmp.h +++ b/include/soc/tegra/bpmp.h @@ -94,6 +94,10 @@ struct tegra_bpmp { struct reset_controller_dev rstc; struct genpd_onecell_data genpd; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_mirror; +#endif }; struct tegra_bpmp_message { @@ -189,4 +193,14 @@ static inline int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp) } #endif +#if IS_ENABLED(CONFIG_DEBUG_FS) +int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp); +#else +static inline int tegra_bpmp_init_debugfs(struct tegra_bpmp *bpmp) +{ + return 0; +} +#endif + + #endif /* __SOC_TEGRA_BPMP_H */ -- cgit v1.2.3-59-g8ed1b From d89e2378a97fafdc74cbf997e7c88af75b81610a Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Thu, 12 Oct 2017 16:56:14 +0100 Subject: drivers: flag buses which demand DMA configuration We do not want the common dma_configure() pathway to apply indiscriminately to all devices, since there are plenty of buses which do not have DMA capability, and if their child devices were used for DMA API calls it would only be indicative of a driver bug. However, there are a number of buses for which DMA is implicitly expected even when not described by firmware - those we whitelist with an automatic opt-in to dma_configure(), assuming that the DMA address space and the physical address space are equivalent if not otherwise specified. Commit 723288836628 ("of: restrict DMA configuration") introduced a short-term fix by comparing explicit bus types, but this approach is far from pretty, doesn't scale well, and fails to cope at all with bus drivers which may be built as modules, like host1x. Let's refine things by making that opt-in a property of the bus type, which neatly addresses those problems and lets the decision of whether firmware description of DMA capability should be optional or mandatory stay internal to the bus drivers themselves. Signed-off-by: Robin Murphy Acked-by: Rob Herring Acked-by: Greg Kroah-Hartman Acked-by: Thierry Reding Signed-off-by: Christoph Hellwig --- drivers/amba/bus.c | 1 + drivers/base/platform.c | 1 + drivers/gpu/host1x/bus.c | 1 + drivers/of/device.c | 8 +------- drivers/pci/pci-driver.c | 1 + include/linux/device.h | 4 ++++ 6 files changed, 9 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index e0f74ddc22b7..594c228d2f02 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -195,6 +195,7 @@ struct bus_type amba_bustype = { .match = amba_match, .uevent = amba_uevent, .pm = &amba_pm, + .force_dma = true, }; static int __init amba_init(void) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 9045c5f3734e..c203fb90c1a0 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1143,6 +1143,7 @@ struct bus_type platform_bus_type = { .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, + .force_dma = true, }; EXPORT_SYMBOL_GPL(platform_bus_type); diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c index f9cde03030fd..ed03b3243195 100644 --- a/drivers/gpu/host1x/bus.c +++ b/drivers/gpu/host1x/bus.c @@ -320,6 +320,7 @@ struct bus_type host1x_bus_type = { .name = "host1x", .match = host1x_device_match, .pm = &host1x_device_pm_ops, + .force_dma = true, }; static void __host1x_device_del(struct host1x_device *device) diff --git a/drivers/of/device.c b/drivers/of/device.c index 64b710265d39..25bddf9c9fe1 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -9,9 +9,7 @@ #include #include #include -#include #include -#include #include #include "of_private.h" @@ -101,11 +99,7 @@ int of_dma_configure(struct device *dev, struct device_node *np) * DMA configuration regardless of whether "dma-ranges" is * correctly specified or not. */ - if (!dev_is_pci(dev) && -#ifdef CONFIG_ARM_AMBA - dev->bus != &amba_bustype && -#endif - dev->bus != &platform_bus_type) + if (!dev->bus->force_dma) return ret == -ENODEV ? 0 : ret; dma_addr = offset = 0; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 11bd267fc137..38bdb97b6dc6 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1466,6 +1466,7 @@ struct bus_type pci_bus_type = { .drv_groups = pci_drv_groups, .pm = PCI_PM_OPS_PTR, .num_vf = pci_bus_num_vf, + .force_dma = true, }; EXPORT_SYMBOL(pci_bus_type); diff --git a/include/linux/device.h b/include/linux/device.h index 66fe271c2544..d60f7701e245 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -97,6 +97,8 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * @p: The private data of the driver core, only the driver core can * touch this. * @lock_key: Lock class key for use by the lock validator + * @force_dma: Assume devices on this bus should be set up by dma_configure() + * even if DMA capability is not explicitly described by firmware. * * A bus is a channel between the processor and one or more devices. For the * purposes of the device model, all devices are connected via a bus, even if @@ -135,6 +137,8 @@ struct bus_type { struct subsys_private *p; struct lock_class_key lock_key; + + bool force_dma; }; extern int __must_check bus_register(struct bus_type *bus); -- cgit v1.2.3-59-g8ed1b From c9eb6172c328dde7e14812f94f8da87b691e41b5 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Sun, 27 Aug 2017 10:37:15 +0200 Subject: dma-mapping: turn dma_cache_sync into a dma_map_ops method After we removed all the dead wood it turns out only two architectures actually implement dma_cache_sync as a real op: mips and parisc. Add a cache_sync method to struct dma_map_ops and implement it for the mips defualt DMA ops, and the parisc pa11 ops. Note that arm, arc and openrisc support DMA_ATTR_NON_CONSISTENT, but never provided a functional dma_cache_sync implementations, which seems somewhat odd. Signed-off-by: Christoph Hellwig Reviewed-by: Robin Murphy --- arch/alpha/include/asm/dma-mapping.h | 2 -- arch/cris/include/asm/dma-mapping.h | 6 ------ arch/frv/include/asm/dma-mapping.h | 6 ------ arch/hexagon/include/asm/dma-mapping.h | 3 --- arch/ia64/include/asm/dma-mapping.h | 6 ------ arch/m32r/include/asm/dma-mapping.h | 5 ----- arch/m68k/include/asm/dma-mapping.h | 6 ------ arch/metag/include/asm/dma-mapping.h | 10 ---------- arch/microblaze/include/asm/dma-mapping.h | 5 ----- arch/mips/include/asm/dma-mapping.h | 3 --- arch/mips/mm/dma-default.c | 7 +++---- arch/mn10300/include/asm/dma-mapping.h | 6 ------ arch/nios2/include/asm/dma-mapping.h | 9 --------- arch/parisc/include/asm/dma-mapping.h | 8 -------- arch/parisc/kernel/pci-dma.c | 8 ++++++++ arch/powerpc/include/asm/dma-mapping.h | 5 ----- arch/s390/include/asm/dma-mapping.h | 5 ----- arch/sh/include/asm/dma-mapping.h | 6 ------ arch/sparc/include/asm/dma-mapping.h | 8 -------- arch/tile/include/asm/dma-mapping.h | 9 --------- arch/unicore32/include/asm/dma-mapping.h | 5 ----- arch/x86/include/asm/dma-mapping.h | 6 ------ arch/xtensa/include/asm/dma-mapping.h | 5 ----- include/linux/dma-mapping.h | 13 +++++++++++++ 24 files changed, 24 insertions(+), 128 deletions(-) (limited to 'include') diff --git a/arch/alpha/include/asm/dma-mapping.h b/arch/alpha/include/asm/dma-mapping.h index 5d53666935e6..399a4f49355e 100644 --- a/arch/alpha/include/asm/dma-mapping.h +++ b/arch/alpha/include/asm/dma-mapping.h @@ -8,6 +8,4 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return dma_ops; } -#define dma_cache_sync(dev, va, size, dir) ((void)0) - #endif /* _ALPHA_DMA_MAPPING_H */ diff --git a/arch/cris/include/asm/dma-mapping.h b/arch/cris/include/asm/dma-mapping.h index 256169de3743..e30adde42beb 100644 --- a/arch/cris/include/asm/dma-mapping.h +++ b/arch/cris/include/asm/dma-mapping.h @@ -16,10 +16,4 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) } #endif -static inline void -dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - #endif diff --git a/arch/frv/include/asm/dma-mapping.h b/arch/frv/include/asm/dma-mapping.h index da0e5c9744c4..da24ae943f02 100644 --- a/arch/frv/include/asm/dma-mapping.h +++ b/arch/frv/include/asm/dma-mapping.h @@ -14,10 +14,4 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return &frv_dma_ops; } -static inline -void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - #endif /* _ASM_DMA_MAPPING_H */ diff --git a/arch/hexagon/include/asm/dma-mapping.h b/arch/hexagon/include/asm/dma-mapping.h index 463dbc18f853..5208de242e79 100644 --- a/arch/hexagon/include/asm/dma-mapping.h +++ b/arch/hexagon/include/asm/dma-mapping.h @@ -37,9 +37,6 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return dma_ops; } -extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction); - static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) { if (!dev->dma_mask) diff --git a/arch/ia64/include/asm/dma-mapping.h b/arch/ia64/include/asm/dma-mapping.h index 99dfc1aa9d3c..9e5b5df76ff8 100644 --- a/arch/ia64/include/asm/dma-mapping.h +++ b/arch/ia64/include/asm/dma-mapping.h @@ -44,10 +44,4 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) return daddr; } -static inline void -dma_cache_sync (struct device *dev, void *vaddr, size_t size, - enum dma_data_direction dir) -{ -} - #endif /* _ASM_IA64_DMA_MAPPING_H */ diff --git a/arch/m32r/include/asm/dma-mapping.h b/arch/m32r/include/asm/dma-mapping.h index aff3ae8b62f7..9e993daed7a0 100644 --- a/arch/m32r/include/asm/dma-mapping.h +++ b/arch/m32r/include/asm/dma-mapping.h @@ -13,11 +13,6 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return &dma_noop_ops; } -static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) { if (!dev->dma_mask) diff --git a/arch/m68k/include/asm/dma-mapping.h b/arch/m68k/include/asm/dma-mapping.h index 9210e470771b..9a0d559fcc13 100644 --- a/arch/m68k/include/asm/dma-mapping.h +++ b/arch/m68k/include/asm/dma-mapping.h @@ -8,10 +8,4 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return &m68k_dma_ops; } -static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction dir) -{ - /* we use coherent allocation, so not much to do here. */ -} - #endif /* _M68K_DMA_MAPPING_H */ diff --git a/arch/metag/include/asm/dma-mapping.h b/arch/metag/include/asm/dma-mapping.h index ea573be2b6d0..340265dcf839 100644 --- a/arch/metag/include/asm/dma-mapping.h +++ b/arch/metag/include/asm/dma-mapping.h @@ -8,14 +8,4 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return &metag_dma_ops; } -/* - * dma_alloc_attrs() always returns non-cacheable memory, so there's no need to - * do any flushing here. - */ -static inline void -dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - #endif diff --git a/arch/microblaze/include/asm/dma-mapping.h b/arch/microblaze/include/asm/dma-mapping.h index ad448e4aedb6..6b9ea39405b8 100644 --- a/arch/microblaze/include/asm/dma-mapping.h +++ b/arch/microblaze/include/asm/dma-mapping.h @@ -25,9 +25,4 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return &dma_direct_ops; } -static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - #endif /* _ASM_MICROBLAZE_DMA_MAPPING_H */ diff --git a/arch/mips/include/asm/dma-mapping.h b/arch/mips/include/asm/dma-mapping.h index aba71385f9d1..6ea1439430a2 100644 --- a/arch/mips/include/asm/dma-mapping.h +++ b/arch/mips/include/asm/dma-mapping.h @@ -26,9 +26,6 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) static inline void dma_mark_clean(void *addr, size_t size) {} -extern void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction); - #define arch_setup_dma_ops arch_setup_dma_ops static inline void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, const struct iommu_ops *iommu, diff --git a/arch/mips/mm/dma-default.c b/arch/mips/mm/dma-default.c index c01bd20d0208..2e2514e00720 100644 --- a/arch/mips/mm/dma-default.c +++ b/arch/mips/mm/dma-default.c @@ -383,7 +383,7 @@ static int mips_dma_supported(struct device *dev, u64 mask) return plat_dma_supported(dev, mask); } -void dma_cache_sync(struct device *dev, void *vaddr, size_t size, +static void mips_dma_cache_sync(struct device *dev, void *vaddr, size_t size, enum dma_data_direction direction) { BUG_ON(direction == DMA_NONE); @@ -392,8 +392,6 @@ void dma_cache_sync(struct device *dev, void *vaddr, size_t size, __dma_sync_virtual(vaddr, size, direction); } -EXPORT_SYMBOL(dma_cache_sync); - static const struct dma_map_ops mips_default_dma_map_ops = { .alloc = mips_dma_alloc_coherent, .free = mips_dma_free_coherent, @@ -407,7 +405,8 @@ static const struct dma_map_ops mips_default_dma_map_ops = { .sync_sg_for_cpu = mips_dma_sync_sg_for_cpu, .sync_sg_for_device = mips_dma_sync_sg_for_device, .mapping_error = mips_dma_mapping_error, - .dma_supported = mips_dma_supported + .dma_supported = mips_dma_supported, + .cache_sync = mips_dma_cache_sync, }; const struct dma_map_ops *mips_dma_map_ops = &mips_default_dma_map_ops; diff --git a/arch/mn10300/include/asm/dma-mapping.h b/arch/mn10300/include/asm/dma-mapping.h index dc24163b190f..439e474ed6d7 100644 --- a/arch/mn10300/include/asm/dma-mapping.h +++ b/arch/mn10300/include/asm/dma-mapping.h @@ -18,10 +18,4 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return &mn10300_dma_ops; } -static inline -void dma_cache_sync(void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - #endif diff --git a/arch/nios2/include/asm/dma-mapping.h b/arch/nios2/include/asm/dma-mapping.h index f8dc62222741..6ceb92251da0 100644 --- a/arch/nios2/include/asm/dma-mapping.h +++ b/arch/nios2/include/asm/dma-mapping.h @@ -17,13 +17,4 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return &nios2_dma_ops; } -/* - * dma_alloc_attrs() always returns non-cacheable memory, so there's no need to - * do any flushing here. - */ -static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - #endif /* _ASM_NIOS2_DMA_MAPPING_H */ diff --git a/arch/parisc/include/asm/dma-mapping.h b/arch/parisc/include/asm/dma-mapping.h index 2b16282add69..cb26bbd71d8a 100644 --- a/arch/parisc/include/asm/dma-mapping.h +++ b/arch/parisc/include/asm/dma-mapping.h @@ -32,14 +32,6 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return hppa_dma_ops; } -static inline void -dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ - if (hppa_dma_ops->sync_single_for_cpu) - flush_kernel_dcache_range((unsigned long)vaddr, size); -} - static inline void * parisc_walk_tree(struct device *dev) { diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c index bd4c0a7471d3..f87c34cb3b43 100644 --- a/arch/parisc/kernel/pci-dma.c +++ b/arch/parisc/kernel/pci-dma.c @@ -571,6 +571,12 @@ static void pa11_dma_sync_sg_for_device(struct device *dev, struct scatterlist * flush_kernel_vmap_range(sg_virt(sg), sg->length); } +static void pa11_dma_cache_sync(struct device *dev, void *vaddr, size_t size, + enum dma_data_direction direction) +{ + flush_kernel_dcache_range((unsigned long)vaddr, size); +} + const struct dma_map_ops pcxl_dma_ops = { .dma_supported = pa11_dma_supported, .alloc = pa11_dma_alloc, @@ -583,6 +589,7 @@ const struct dma_map_ops pcxl_dma_ops = { .sync_single_for_device = pa11_dma_sync_single_for_device, .sync_sg_for_cpu = pa11_dma_sync_sg_for_cpu, .sync_sg_for_device = pa11_dma_sync_sg_for_device, + .cache_sync = pa11_dma_cache_sync, }; static void *pcx_dma_alloc(struct device *dev, size_t size, @@ -619,4 +626,5 @@ const struct dma_map_ops pcx_dma_ops = { .sync_single_for_device = pa11_dma_sync_single_for_device, .sync_sg_for_cpu = pa11_dma_sync_sg_for_cpu, .sync_sg_for_device = pa11_dma_sync_sg_for_device, + .cache_sync = pa11_dma_cache_sync, }; diff --git a/arch/powerpc/include/asm/dma-mapping.h b/arch/powerpc/include/asm/dma-mapping.h index 320846442bfb..2e43c2ef7632 100644 --- a/arch/powerpc/include/asm/dma-mapping.h +++ b/arch/powerpc/include/asm/dma-mapping.h @@ -141,10 +141,5 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) #define ARCH_HAS_DMA_MMAP_COHERENT -static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - #endif /* __KERNEL__ */ #endif /* _ASM_DMA_MAPPING_H */ diff --git a/arch/s390/include/asm/dma-mapping.h b/arch/s390/include/asm/dma-mapping.h index 512ad0eaa11a..b17304b13de5 100644 --- a/arch/s390/include/asm/dma-mapping.h +++ b/arch/s390/include/asm/dma-mapping.h @@ -15,11 +15,6 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return &dma_noop_ops; } -static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) { if (!dev->dma_mask) diff --git a/arch/sh/include/asm/dma-mapping.h b/arch/sh/include/asm/dma-mapping.h index b46194ecef17..e89df111c017 100644 --- a/arch/sh/include/asm/dma-mapping.h +++ b/arch/sh/include/asm/dma-mapping.h @@ -9,12 +9,6 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return dma_ops; } -static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction dir) -{ -} - -/* arch/sh/mm/consistent.c */ extern void *dma_generic_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr, gfp_t flag, unsigned long attrs); diff --git a/arch/sparc/include/asm/dma-mapping.h b/arch/sparc/include/asm/dma-mapping.h index 60bf1633d554..b298ed45cb23 100644 --- a/arch/sparc/include/asm/dma-mapping.h +++ b/arch/sparc/include/asm/dma-mapping.h @@ -5,14 +5,6 @@ #include #include -static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction dir) -{ - /* Since dma_{alloc,free}_noncoherent() allocated coherent memory, this - * routine can be a nop. - */ -} - extern const struct dma_map_ops *dma_ops; extern const struct dma_map_ops pci32_dma_ops; diff --git a/arch/tile/include/asm/dma-mapping.h b/arch/tile/include/asm/dma-mapping.h index 7061dc8af43a..97ad62878290 100644 --- a/arch/tile/include/asm/dma-mapping.h +++ b/arch/tile/include/asm/dma-mapping.h @@ -67,13 +67,4 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) #define HAVE_ARCH_DMA_SET_MASK 1 int dma_set_mask(struct device *dev, u64 mask); -/* - * dma_alloc_attrs() always returns non-cacheable memory, so there's no need to - * do any flushing here. - */ -static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - #endif /* _ASM_TILE_DMA_MAPPING_H */ diff --git a/arch/unicore32/include/asm/dma-mapping.h b/arch/unicore32/include/asm/dma-mapping.h index e949855bb794..ac608c2f6af6 100644 --- a/arch/unicore32/include/asm/dma-mapping.h +++ b/arch/unicore32/include/asm/dma-mapping.h @@ -45,10 +45,5 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) static inline void dma_mark_clean(void *addr, size_t size) {} -static inline void dma_cache_sync(struct device *dev, void *vaddr, - size_t size, enum dma_data_direction direction) -{ -} - #endif /* __KERNEL__ */ #endif diff --git a/arch/x86/include/asm/dma-mapping.h b/arch/x86/include/asm/dma-mapping.h index c6d3367be916..c26747340ba5 100644 --- a/arch/x86/include/asm/dma-mapping.h +++ b/arch/x86/include/asm/dma-mapping.h @@ -67,12 +67,6 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) } #endif /* CONFIG_X86_DMA_REMAP */ -static inline void -dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction dir) -{ -} - static inline unsigned long dma_alloc_coherent_mask(struct device *dev, gfp_t gfp) { diff --git a/arch/xtensa/include/asm/dma-mapping.h b/arch/xtensa/include/asm/dma-mapping.h index 353e0314d6ba..153bf2370988 100644 --- a/arch/xtensa/include/asm/dma-mapping.h +++ b/arch/xtensa/include/asm/dma-mapping.h @@ -23,11 +23,6 @@ static inline const struct dma_map_ops *get_arch_dma_ops(struct bus_type *bus) return &xtensa_dma_map_ops; } -static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, - enum dma_data_direction direction) -{ -} - static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) { return (dma_addr_t)paddr; diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h index 29ce9815da87..028a375d240d 100644 --- a/include/linux/dma-mapping.h +++ b/include/linux/dma-mapping.h @@ -126,6 +126,8 @@ struct dma_map_ops { void (*sync_sg_for_device)(struct device *dev, struct scatterlist *sg, int nents, enum dma_data_direction dir); + void (*cache_sync)(struct device *dev, void *vaddr, size_t size, + enum dma_data_direction direction); int (*mapping_error)(struct device *dev, dma_addr_t dma_addr); int (*dma_supported)(struct device *dev, u64 mask); #ifdef ARCH_HAS_DMA_GET_REQUIRED_MASK @@ -436,6 +438,17 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, #define dma_map_page(d, p, o, s, r) dma_map_page_attrs(d, p, o, s, r, 0) #define dma_unmap_page(d, a, s, r) dma_unmap_page_attrs(d, a, s, r, 0) +static inline void +dma_cache_sync(struct device *dev, void *vaddr, size_t size, + enum dma_data_direction dir) +{ + const struct dma_map_ops *ops = get_dma_ops(dev); + + BUG_ON(!valid_dma_direction(dir)); + if (ops->cache_sync) + ops->cache_sync(dev, vaddr, size, dir); +} + extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, void *cpu_addr, dma_addr_t dma_addr, size_t size); -- cgit v1.2.3-59-g8ed1b From 2940bc4333707a05e69b3ffd737bda0dc0c3004f Mon Sep 17 00:00:00 2001 From: Shaokun Zhang Date: Thu, 19 Oct 2017 19:05:18 +0800 Subject: perf: hisi: Add support for HiSilicon SoC L3C PMU driver This patch adds support for L3C PMU driver in HiSilicon SoC chip, Each L3C has own control, counter and interrupt registers and is an separate PMU. For each L3C PMU, it has 8-programable counters and each counter is free-running. Interrupt is supported to handle counter (48-bits) overflow. Acked-by: Mark Rutland Reviewed-by: Jonathan Cameron Signed-off-by: Shaokun Zhang Signed-off-by: Anurup M Signed-off-by: Will Deacon --- drivers/perf/hisilicon/Makefile | 2 +- drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 463 +++++++++++++++++++++++++++ include/linux/cpuhotplug.h | 1 + 3 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c (limited to 'include') diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile index 2783bb31e72b..4a3d3e6002a6 100644 --- a/drivers/perf/hisilicon/Makefile +++ b/drivers/perf/hisilicon/Makefile @@ -1 +1 @@ -obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o +obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c new file mode 100644 index 000000000000..0bde5d919b2e --- /dev/null +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -0,0 +1,463 @@ +/* + * HiSilicon SoC L3C uncore Hardware event counters support + * + * Copyright (C) 2017 Hisilicon Limited + * Author: Anurup M + * Shaokun Zhang + * + * This code is based on the uncore PMUs like arm-cci and arm-ccn. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hisi_uncore_pmu.h" + +/* L3C register definition */ +#define L3C_PERF_CTRL 0x0408 +#define L3C_INT_MASK 0x0800 +#define L3C_INT_STATUS 0x0808 +#define L3C_INT_CLEAR 0x080c +#define L3C_EVENT_CTRL 0x1c00 +#define L3C_EVENT_TYPE0 0x1d00 +/* + * Each counter is 48-bits and [48:63] are reserved + * which are Read-As-Zero and Writes-Ignored. + */ +#define L3C_CNTR0_LOWER 0x1e00 + +/* L3C has 8-counters */ +#define L3C_NR_COUNTERS 0x8 + +#define L3C_PERF_CTRL_EN 0x20000 +#define L3C_EVTYPE_NONE 0xff + +/* + * Select the counter register offset using the counter index + */ +static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx) +{ + return (L3C_CNTR0_LOWER + (cntr_idx * 8)); +} + +static u64 hisi_l3c_pmu_read_counter(struct hisi_pmu *l3c_pmu, + struct hw_perf_event *hwc) +{ + u32 idx = hwc->idx; + + if (!hisi_uncore_pmu_counter_valid(l3c_pmu, idx)) { + dev_err(l3c_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + /* Read 64-bits and the upper 16 bits are RAZ */ + return readq(l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(idx)); +} + +static void hisi_l3c_pmu_write_counter(struct hisi_pmu *l3c_pmu, + struct hw_perf_event *hwc, u64 val) +{ + u32 idx = hwc->idx; + + if (!hisi_uncore_pmu_counter_valid(l3c_pmu, idx)) { + dev_err(l3c_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + + /* Write 64-bits and the upper 16 bits are WI */ + writeq(val, l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(idx)); +} + +static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx, + u32 type) +{ + u32 reg, reg_idx, shift, val; + + /* + * Select the appropriate event select register(L3C_EVENT_TYPE0/1). + * There are 2 event select registers for the 8 hardware counters. + * Event code is 8-bits and for the former 4 hardware counters, + * L3C_EVENT_TYPE0 is chosen. For the latter 4 hardware counters, + * L3C_EVENT_TYPE1 is chosen. + */ + reg = L3C_EVENT_TYPE0 + (idx / 4) * 4; + reg_idx = idx % 4; + shift = 8 * reg_idx; + + /* Write event code to L3C_EVENT_TYPEx Register */ + val = readl(l3c_pmu->base + reg); + val &= ~(L3C_EVTYPE_NONE << shift); + val |= (type << shift); + writel(val, l3c_pmu->base + reg); +} + +static void hisi_l3c_pmu_start_counters(struct hisi_pmu *l3c_pmu) +{ + u32 val; + + /* + * Set perf_enable bit in L3C_PERF_CTRL register to start counting + * for all enabled counters. + */ + val = readl(l3c_pmu->base + L3C_PERF_CTRL); + val |= L3C_PERF_CTRL_EN; + writel(val, l3c_pmu->base + L3C_PERF_CTRL); +} + +static void hisi_l3c_pmu_stop_counters(struct hisi_pmu *l3c_pmu) +{ + u32 val; + + /* + * Clear perf_enable bit in L3C_PERF_CTRL register to stop counting + * for all enabled counters. + */ + val = readl(l3c_pmu->base + L3C_PERF_CTRL); + val &= ~(L3C_PERF_CTRL_EN); + writel(val, l3c_pmu->base + L3C_PERF_CTRL); +} + +static void hisi_l3c_pmu_enable_counter(struct hisi_pmu *l3c_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Enable counter index in L3C_EVENT_CTRL register */ + val = readl(l3c_pmu->base + L3C_EVENT_CTRL); + val |= (1 << hwc->idx); + writel(val, l3c_pmu->base + L3C_EVENT_CTRL); +} + +static void hisi_l3c_pmu_disable_counter(struct hisi_pmu *l3c_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Clear counter index in L3C_EVENT_CTRL register */ + val = readl(l3c_pmu->base + L3C_EVENT_CTRL); + val &= ~(1 << hwc->idx); + writel(val, l3c_pmu->base + L3C_EVENT_CTRL); +} + +static void hisi_l3c_pmu_enable_counter_int(struct hisi_pmu *l3c_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + val = readl(l3c_pmu->base + L3C_INT_MASK); + /* Write 0 to enable interrupt */ + val &= ~(1 << hwc->idx); + writel(val, l3c_pmu->base + L3C_INT_MASK); +} + +static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu *l3c_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + val = readl(l3c_pmu->base + L3C_INT_MASK); + /* Write 1 to mask interrupt */ + val |= (1 << hwc->idx); + writel(val, l3c_pmu->base + L3C_INT_MASK); +} + +static irqreturn_t hisi_l3c_pmu_isr(int irq, void *dev_id) +{ + struct hisi_pmu *l3c_pmu = dev_id; + struct perf_event *event; + unsigned long overflown; + int idx; + + /* Read L3C_INT_STATUS register */ + overflown = readl(l3c_pmu->base + L3C_INT_STATUS); + if (!overflown) + return IRQ_NONE; + + /* + * Find the counter index which overflowed if the bit was set + * and handle it. + */ + for_each_set_bit(idx, &overflown, L3C_NR_COUNTERS) { + /* Write 1 to clear the IRQ status flag */ + writel((1 << idx), l3c_pmu->base + L3C_INT_CLEAR); + + /* Get the corresponding event struct */ + event = l3c_pmu->pmu_events.hw_events[idx]; + if (!event) + continue; + + hisi_uncore_pmu_event_update(event); + hisi_uncore_pmu_set_event_period(event); + } + + return IRQ_HANDLED; +} + +static int hisi_l3c_pmu_init_irq(struct hisi_pmu *l3c_pmu, + struct platform_device *pdev) +{ + int irq, ret; + + /* Read and init IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "L3C PMU get irq fail; irq:%d\n", irq); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, hisi_l3c_pmu_isr, + IRQF_NOBALANCING | IRQF_NO_THREAD, + dev_name(&pdev->dev), l3c_pmu); + if (ret < 0) { + dev_err(&pdev->dev, + "Fail to request IRQ:%d ret:%d\n", irq, ret); + return ret; + } + + l3c_pmu->irq = irq; + + return 0; +} + +static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = { + { "HISI0213", }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match); + +static int hisi_l3c_pmu_init_data(struct platform_device *pdev, + struct hisi_pmu *l3c_pmu) +{ + unsigned long long id; + struct resource *res; + acpi_status status; + + status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), + "_UID", NULL, &id); + if (ACPI_FAILURE(status)) + return -EINVAL; + + l3c_pmu->index_id = id; + + /* + * Use the SCCL_ID and CCL_ID to identify the L3C PMU, while + * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1]. + */ + if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", + &l3c_pmu->sccl_id)) { + dev_err(&pdev->dev, "Can not read l3c sccl-id!\n"); + return -EINVAL; + } + + if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id", + &l3c_pmu->ccl_id)) { + dev_err(&pdev->dev, "Can not read l3c ccl-id!\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + l3c_pmu->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(l3c_pmu->base)) { + dev_err(&pdev->dev, "ioremap failed for l3c_pmu resource\n"); + return PTR_ERR(l3c_pmu->base); + } + + return 0; +} + +static struct attribute *hisi_l3c_pmu_format_attr[] = { + HISI_PMU_FORMAT_ATTR(event, "config:0-7"), + NULL, +}; + +static const struct attribute_group hisi_l3c_pmu_format_group = { + .name = "format", + .attrs = hisi_l3c_pmu_format_attr, +}; + +static struct attribute *hisi_l3c_pmu_events_attr[] = { + HISI_PMU_EVENT_ATTR(rd_cpipe, 0x00), + HISI_PMU_EVENT_ATTR(wr_cpipe, 0x01), + HISI_PMU_EVENT_ATTR(rd_hit_cpipe, 0x02), + HISI_PMU_EVENT_ATTR(wr_hit_cpipe, 0x03), + HISI_PMU_EVENT_ATTR(victim_num, 0x04), + HISI_PMU_EVENT_ATTR(rd_spipe, 0x20), + HISI_PMU_EVENT_ATTR(wr_spipe, 0x21), + HISI_PMU_EVENT_ATTR(rd_hit_spipe, 0x22), + HISI_PMU_EVENT_ATTR(wr_hit_spipe, 0x23), + HISI_PMU_EVENT_ATTR(back_invalid, 0x29), + HISI_PMU_EVENT_ATTR(retry_cpu, 0x40), + HISI_PMU_EVENT_ATTR(retry_ring, 0x41), + HISI_PMU_EVENT_ATTR(prefetch_drop, 0x42), + NULL, +}; + +static const struct attribute_group hisi_l3c_pmu_events_group = { + .name = "events", + .attrs = hisi_l3c_pmu_events_attr, +}; + +static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); + +static struct attribute *hisi_l3c_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static const struct attribute_group hisi_l3c_pmu_cpumask_attr_group = { + .attrs = hisi_l3c_pmu_cpumask_attrs, +}; + +static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = { + &hisi_l3c_pmu_format_group, + &hisi_l3c_pmu_events_group, + &hisi_l3c_pmu_cpumask_attr_group, + NULL, +}; + +static const struct hisi_uncore_ops hisi_uncore_l3c_ops = { + .write_evtype = hisi_l3c_pmu_write_evtype, + .get_event_idx = hisi_uncore_pmu_get_event_idx, + .start_counters = hisi_l3c_pmu_start_counters, + .stop_counters = hisi_l3c_pmu_stop_counters, + .enable_counter = hisi_l3c_pmu_enable_counter, + .disable_counter = hisi_l3c_pmu_disable_counter, + .enable_counter_int = hisi_l3c_pmu_enable_counter_int, + .disable_counter_int = hisi_l3c_pmu_disable_counter_int, + .write_counter = hisi_l3c_pmu_write_counter, + .read_counter = hisi_l3c_pmu_read_counter, +}; + +static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev, + struct hisi_pmu *l3c_pmu) +{ + int ret; + + ret = hisi_l3c_pmu_init_data(pdev, l3c_pmu); + if (ret) + return ret; + + ret = hisi_l3c_pmu_init_irq(l3c_pmu, pdev); + if (ret) + return ret; + + l3c_pmu->num_counters = L3C_NR_COUNTERS; + l3c_pmu->counter_bits = 48; + l3c_pmu->ops = &hisi_uncore_l3c_ops; + l3c_pmu->dev = &pdev->dev; + l3c_pmu->on_cpu = -1; + l3c_pmu->check_event = 0x59; + + return 0; +} + +static int hisi_l3c_pmu_probe(struct platform_device *pdev) +{ + struct hisi_pmu *l3c_pmu; + char *name; + int ret; + + l3c_pmu = devm_kzalloc(&pdev->dev, sizeof(*l3c_pmu), GFP_KERNEL); + if (!l3c_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, l3c_pmu); + + ret = hisi_l3c_pmu_dev_probe(pdev, l3c_pmu); + if (ret) + return ret; + + ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, + &l3c_pmu->node); + if (ret) { + dev_err(&pdev->dev, "Error %d registering hotplug\n", ret); + return ret; + } + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_l3c%u", + l3c_pmu->sccl_id, l3c_pmu->index_id); + l3c_pmu->pmu = (struct pmu) { + .name = name, + .task_ctx_nr = perf_invalid_context, + .event_init = hisi_uncore_pmu_event_init, + .pmu_enable = hisi_uncore_pmu_enable, + .pmu_disable = hisi_uncore_pmu_disable, + .add = hisi_uncore_pmu_add, + .del = hisi_uncore_pmu_del, + .start = hisi_uncore_pmu_start, + .stop = hisi_uncore_pmu_stop, + .read = hisi_uncore_pmu_read, + .attr_groups = hisi_l3c_pmu_attr_groups, + }; + + ret = perf_pmu_register(&l3c_pmu->pmu, name, -1); + if (ret) { + dev_err(l3c_pmu->dev, "L3C PMU register failed!\n"); + cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, + &l3c_pmu->node); + } + + return ret; +} + +static int hisi_l3c_pmu_remove(struct platform_device *pdev) +{ + struct hisi_pmu *l3c_pmu = platform_get_drvdata(pdev); + + perf_pmu_unregister(&l3c_pmu->pmu); + cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, + &l3c_pmu->node); + + return 0; +} + +static struct platform_driver hisi_l3c_pmu_driver = { + .driver = { + .name = "hisi_l3c_pmu", + .acpi_match_table = ACPI_PTR(hisi_l3c_pmu_acpi_match), + }, + .probe = hisi_l3c_pmu_probe, + .remove = hisi_l3c_pmu_remove, +}; + +static int __init hisi_l3c_pmu_module_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, + "AP_PERF_ARM_HISI_L3_ONLINE", + hisi_uncore_pmu_online_cpu, + hisi_uncore_pmu_offline_cpu); + if (ret) { + pr_err("L3C PMU: Error setup hotplug, ret = %d\n", ret); + return ret; + } + + ret = platform_driver_register(&hisi_l3c_pmu_driver); + if (ret) + cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE); + + return ret; +} +module_init(hisi_l3c_pmu_module_init); + +static void __exit hisi_l3c_pmu_module_exit(void) +{ + platform_driver_unregister(&hisi_l3c_pmu_driver); + cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE); +} +module_exit(hisi_l3c_pmu_module_exit); + +MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Anurup M "); +MODULE_AUTHOR("Shaokun Zhang "); diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 6d508767e144..9621efdbb3bd 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -153,6 +153,7 @@ enum cpuhp_state { CPUHP_AP_PERF_S390_SF_ONLINE, CPUHP_AP_PERF_ARM_CCI_ONLINE, CPUHP_AP_PERF_ARM_CCN_ONLINE, + CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, CPUHP_AP_PERF_ARM_L2X0_ONLINE, CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE, CPUHP_AP_PERF_ARM_QCOM_L3_ONLINE, -- cgit v1.2.3-59-g8ed1b From 2bab3cf9104c5ab80a1b9c706d81d997548401e4 Mon Sep 17 00:00:00 2001 From: Shaokun Zhang Date: Thu, 19 Oct 2017 19:05:19 +0800 Subject: perf: hisi: Add support for HiSilicon SoC HHA PMU driver L3 cache coherence is maintained by Hydra Home Agent (HHA) in HiSilicon SoC. This patch adds support for HHA PMU driver, Each HHA has own control, counter and interrupt registers and is an separate PMU. For each HHA PMU, it has 16-programable counters and each counter is free-running. Interrupt is supported to handle counter (48-bits) overflow. Acked-by: Mark Rutland Reviewed-by: Jonathan Cameron Signed-off-by: Shaokun Zhang Signed-off-by: Anurup M Signed-off-by: Will Deacon --- drivers/perf/hisilicon/Makefile | 2 +- drivers/perf/hisilicon/hisi_uncore_hha_pmu.c | 473 +++++++++++++++++++++++++++ include/linux/cpuhotplug.h | 1 + 3 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 drivers/perf/hisilicon/hisi_uncore_hha_pmu.c (limited to 'include') diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile index 4a3d3e6002a6..a72afe84d034 100644 --- a/drivers/perf/hisilicon/Makefile +++ b/drivers/perf/hisilicon/Makefile @@ -1 +1 @@ -obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o +obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o hisi_uncore_hha_pmu.o diff --git a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c new file mode 100644 index 000000000000..443906e0aff3 --- /dev/null +++ b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c @@ -0,0 +1,473 @@ +/* + * HiSilicon SoC HHA uncore Hardware event counters support + * + * Copyright (C) 2017 Hisilicon Limited + * Author: Shaokun Zhang + * Anurup M + * + * This code is based on the uncore PMUs like arm-cci and arm-ccn. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hisi_uncore_pmu.h" + +/* HHA register definition */ +#define HHA_INT_MASK 0x0804 +#define HHA_INT_STATUS 0x0808 +#define HHA_INT_CLEAR 0x080C +#define HHA_PERF_CTRL 0x1E00 +#define HHA_EVENT_CTRL 0x1E04 +#define HHA_EVENT_TYPE0 0x1E80 +/* + * Each counter is 48-bits and [48:63] are reserved + * which are Read-As-Zero and Writes-Ignored. + */ +#define HHA_CNT0_LOWER 0x1F00 + +/* HHA has 16-counters */ +#define HHA_NR_COUNTERS 0x10 + +#define HHA_PERF_CTRL_EN 0x1 +#define HHA_EVTYPE_NONE 0xff + +/* + * Select the counter register offset using the counter index + * each counter is 48-bits. + */ +static u32 hisi_hha_pmu_get_counter_offset(int cntr_idx) +{ + return (HHA_CNT0_LOWER + (cntr_idx * 8)); +} + +static u64 hisi_hha_pmu_read_counter(struct hisi_pmu *hha_pmu, + struct hw_perf_event *hwc) +{ + u32 idx = hwc->idx; + + if (!hisi_uncore_pmu_counter_valid(hha_pmu, idx)) { + dev_err(hha_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + /* Read 64 bits and like L3C, top 16 bits are RAZ */ + return readq(hha_pmu->base + hisi_hha_pmu_get_counter_offset(idx)); +} + +static void hisi_hha_pmu_write_counter(struct hisi_pmu *hha_pmu, + struct hw_perf_event *hwc, u64 val) +{ + u32 idx = hwc->idx; + + if (!hisi_uncore_pmu_counter_valid(hha_pmu, idx)) { + dev_err(hha_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + + /* Write 64 bits and like L3C, top 16 bits are WI */ + writeq(val, hha_pmu->base + hisi_hha_pmu_get_counter_offset(idx)); +} + +static void hisi_hha_pmu_write_evtype(struct hisi_pmu *hha_pmu, int idx, + u32 type) +{ + u32 reg, reg_idx, shift, val; + + /* + * Select the appropriate event select register(HHA_EVENT_TYPEx). + * There are 4 event select registers for the 16 hardware counters. + * Event code is 8-bits and for the first 4 hardware counters, + * HHA_EVENT_TYPE0 is chosen. For the next 4 hardware counters, + * HHA_EVENT_TYPE1 is chosen and so on. + */ + reg = HHA_EVENT_TYPE0 + 4 * (idx / 4); + reg_idx = idx % 4; + shift = 8 * reg_idx; + + /* Write event code to HHA_EVENT_TYPEx register */ + val = readl(hha_pmu->base + reg); + val &= ~(HHA_EVTYPE_NONE << shift); + val |= (type << shift); + writel(val, hha_pmu->base + reg); +} + +static void hisi_hha_pmu_start_counters(struct hisi_pmu *hha_pmu) +{ + u32 val; + + /* + * Set perf_enable bit in HHA_PERF_CTRL to start event + * counting for all enabled counters. + */ + val = readl(hha_pmu->base + HHA_PERF_CTRL); + val |= HHA_PERF_CTRL_EN; + writel(val, hha_pmu->base + HHA_PERF_CTRL); +} + +static void hisi_hha_pmu_stop_counters(struct hisi_pmu *hha_pmu) +{ + u32 val; + + /* + * Clear perf_enable bit in HHA_PERF_CTRL to stop event + * counting for all enabled counters. + */ + val = readl(hha_pmu->base + HHA_PERF_CTRL); + val &= ~(HHA_PERF_CTRL_EN); + writel(val, hha_pmu->base + HHA_PERF_CTRL); +} + +static void hisi_hha_pmu_enable_counter(struct hisi_pmu *hha_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Enable counter index in HHA_EVENT_CTRL register */ + val = readl(hha_pmu->base + HHA_EVENT_CTRL); + val |= (1 << hwc->idx); + writel(val, hha_pmu->base + HHA_EVENT_CTRL); +} + +static void hisi_hha_pmu_disable_counter(struct hisi_pmu *hha_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Clear counter index in HHA_EVENT_CTRL register */ + val = readl(hha_pmu->base + HHA_EVENT_CTRL); + val &= ~(1 << hwc->idx); + writel(val, hha_pmu->base + HHA_EVENT_CTRL); +} + +static void hisi_hha_pmu_enable_counter_int(struct hisi_pmu *hha_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Write 0 to enable interrupt */ + val = readl(hha_pmu->base + HHA_INT_MASK); + val &= ~(1 << hwc->idx); + writel(val, hha_pmu->base + HHA_INT_MASK); +} + +static void hisi_hha_pmu_disable_counter_int(struct hisi_pmu *hha_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Write 1 to mask interrupt */ + val = readl(hha_pmu->base + HHA_INT_MASK); + val |= (1 << hwc->idx); + writel(val, hha_pmu->base + HHA_INT_MASK); +} + +static irqreturn_t hisi_hha_pmu_isr(int irq, void *dev_id) +{ + struct hisi_pmu *hha_pmu = dev_id; + struct perf_event *event; + unsigned long overflown; + int idx; + + /* Read HHA_INT_STATUS register */ + overflown = readl(hha_pmu->base + HHA_INT_STATUS); + if (!overflown) + return IRQ_NONE; + + /* + * Find the counter index which overflowed if the bit was set + * and handle it + */ + for_each_set_bit(idx, &overflown, HHA_NR_COUNTERS) { + /* Write 1 to clear the IRQ status flag */ + writel((1 << idx), hha_pmu->base + HHA_INT_CLEAR); + + /* Get the corresponding event struct */ + event = hha_pmu->pmu_events.hw_events[idx]; + if (!event) + continue; + + hisi_uncore_pmu_event_update(event); + hisi_uncore_pmu_set_event_period(event); + } + + return IRQ_HANDLED; +} + +static int hisi_hha_pmu_init_irq(struct hisi_pmu *hha_pmu, + struct platform_device *pdev) +{ + int irq, ret; + + /* Read and init IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "HHA PMU get irq fail; irq:%d\n", irq); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, hisi_hha_pmu_isr, + IRQF_NOBALANCING | IRQF_NO_THREAD, + dev_name(&pdev->dev), hha_pmu); + if (ret < 0) { + dev_err(&pdev->dev, + "Fail to request IRQ:%d ret:%d\n", irq, ret); + return ret; + } + + hha_pmu->irq = irq; + + return 0; +} + +static const struct acpi_device_id hisi_hha_pmu_acpi_match[] = { + { "HISI0243", }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, hisi_hha_pmu_acpi_match); + +static int hisi_hha_pmu_init_data(struct platform_device *pdev, + struct hisi_pmu *hha_pmu) +{ + unsigned long long id; + struct resource *res; + acpi_status status; + + status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), + "_UID", NULL, &id); + if (ACPI_FAILURE(status)) + return -EINVAL; + + hha_pmu->index_id = id; + + /* + * Use SCCL_ID and UID to identify the HHA PMU, while + * SCCL_ID is in MPIDR[aff2]. + */ + if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", + &hha_pmu->sccl_id)) { + dev_err(&pdev->dev, "Can not read hha sccl-id!\n"); + return -EINVAL; + } + /* HHA PMUs only share the same SCCL */ + hha_pmu->ccl_id = -1; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hha_pmu->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hha_pmu->base)) { + dev_err(&pdev->dev, "ioremap failed for hha_pmu resource\n"); + return PTR_ERR(hha_pmu->base); + } + + return 0; +} + +static struct attribute *hisi_hha_pmu_format_attr[] = { + HISI_PMU_FORMAT_ATTR(event, "config:0-7"), + NULL, +}; + +static const struct attribute_group hisi_hha_pmu_format_group = { + .name = "format", + .attrs = hisi_hha_pmu_format_attr, +}; + +static struct attribute *hisi_hha_pmu_events_attr[] = { + HISI_PMU_EVENT_ATTR(rx_ops_num, 0x00), + HISI_PMU_EVENT_ATTR(rx_outer, 0x01), + HISI_PMU_EVENT_ATTR(rx_sccl, 0x02), + HISI_PMU_EVENT_ATTR(rx_ccix, 0x03), + HISI_PMU_EVENT_ATTR(rx_wbi, 0x04), + HISI_PMU_EVENT_ATTR(rx_wbip, 0x05), + HISI_PMU_EVENT_ATTR(rx_wtistash, 0x11), + HISI_PMU_EVENT_ATTR(rd_ddr_64b, 0x1c), + HISI_PMU_EVENT_ATTR(wr_dr_64b, 0x1d), + HISI_PMU_EVENT_ATTR(rd_ddr_128b, 0x1e), + HISI_PMU_EVENT_ATTR(wr_ddr_128b, 0x1f), + HISI_PMU_EVENT_ATTR(spill_num, 0x20), + HISI_PMU_EVENT_ATTR(spill_success, 0x21), + HISI_PMU_EVENT_ATTR(bi_num, 0x23), + HISI_PMU_EVENT_ATTR(mediated_num, 0x32), + HISI_PMU_EVENT_ATTR(tx_snp_num, 0x33), + HISI_PMU_EVENT_ATTR(tx_snp_outer, 0x34), + HISI_PMU_EVENT_ATTR(tx_snp_ccix, 0x35), + HISI_PMU_EVENT_ATTR(rx_snprspdata, 0x38), + HISI_PMU_EVENT_ATTR(rx_snprsp_outer, 0x3c), + HISI_PMU_EVENT_ATTR(sdir-lookup, 0x40), + HISI_PMU_EVENT_ATTR(edir-lookup, 0x41), + HISI_PMU_EVENT_ATTR(sdir-hit, 0x42), + HISI_PMU_EVENT_ATTR(edir-hit, 0x43), + HISI_PMU_EVENT_ATTR(sdir-home-migrate, 0x4c), + HISI_PMU_EVENT_ATTR(edir-home-migrate, 0x4d), + NULL, +}; + +static const struct attribute_group hisi_hha_pmu_events_group = { + .name = "events", + .attrs = hisi_hha_pmu_events_attr, +}; + +static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); + +static struct attribute *hisi_hha_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static const struct attribute_group hisi_hha_pmu_cpumask_attr_group = { + .attrs = hisi_hha_pmu_cpumask_attrs, +}; + +static const struct attribute_group *hisi_hha_pmu_attr_groups[] = { + &hisi_hha_pmu_format_group, + &hisi_hha_pmu_events_group, + &hisi_hha_pmu_cpumask_attr_group, + NULL, +}; + +static const struct hisi_uncore_ops hisi_uncore_hha_ops = { + .write_evtype = hisi_hha_pmu_write_evtype, + .get_event_idx = hisi_uncore_pmu_get_event_idx, + .start_counters = hisi_hha_pmu_start_counters, + .stop_counters = hisi_hha_pmu_stop_counters, + .enable_counter = hisi_hha_pmu_enable_counter, + .disable_counter = hisi_hha_pmu_disable_counter, + .enable_counter_int = hisi_hha_pmu_enable_counter_int, + .disable_counter_int = hisi_hha_pmu_disable_counter_int, + .write_counter = hisi_hha_pmu_write_counter, + .read_counter = hisi_hha_pmu_read_counter, +}; + +static int hisi_hha_pmu_dev_probe(struct platform_device *pdev, + struct hisi_pmu *hha_pmu) +{ + int ret; + + ret = hisi_hha_pmu_init_data(pdev, hha_pmu); + if (ret) + return ret; + + ret = hisi_hha_pmu_init_irq(hha_pmu, pdev); + if (ret) + return ret; + + hha_pmu->num_counters = HHA_NR_COUNTERS; + hha_pmu->counter_bits = 48; + hha_pmu->ops = &hisi_uncore_hha_ops; + hha_pmu->dev = &pdev->dev; + hha_pmu->on_cpu = -1; + hha_pmu->check_event = 0x65; + + return 0; +} + +static int hisi_hha_pmu_probe(struct platform_device *pdev) +{ + struct hisi_pmu *hha_pmu; + char *name; + int ret; + + hha_pmu = devm_kzalloc(&pdev->dev, sizeof(*hha_pmu), GFP_KERNEL); + if (!hha_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, hha_pmu); + + ret = hisi_hha_pmu_dev_probe(pdev, hha_pmu); + if (ret) + return ret; + + ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, + &hha_pmu->node); + if (ret) { + dev_err(&pdev->dev, "Error %d registering hotplug\n", ret); + return ret; + } + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_hha%u", + hha_pmu->sccl_id, hha_pmu->index_id); + hha_pmu->pmu = (struct pmu) { + .name = name, + .task_ctx_nr = perf_invalid_context, + .event_init = hisi_uncore_pmu_event_init, + .pmu_enable = hisi_uncore_pmu_enable, + .pmu_disable = hisi_uncore_pmu_disable, + .add = hisi_uncore_pmu_add, + .del = hisi_uncore_pmu_del, + .start = hisi_uncore_pmu_start, + .stop = hisi_uncore_pmu_stop, + .read = hisi_uncore_pmu_read, + .attr_groups = hisi_hha_pmu_attr_groups, + }; + + ret = perf_pmu_register(&hha_pmu->pmu, name, -1); + if (ret) { + dev_err(hha_pmu->dev, "HHA PMU register failed!\n"); + cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, + &hha_pmu->node); + } + + return ret; +} + +static int hisi_hha_pmu_remove(struct platform_device *pdev) +{ + struct hisi_pmu *hha_pmu = platform_get_drvdata(pdev); + + perf_pmu_unregister(&hha_pmu->pmu); + cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, + &hha_pmu->node); + + return 0; +} + +static struct platform_driver hisi_hha_pmu_driver = { + .driver = { + .name = "hisi_hha_pmu", + .acpi_match_table = ACPI_PTR(hisi_hha_pmu_acpi_match), + }, + .probe = hisi_hha_pmu_probe, + .remove = hisi_hha_pmu_remove, +}; + +static int __init hisi_hha_pmu_module_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, + "AP_PERF_ARM_HISI_HHA_ONLINE", + hisi_uncore_pmu_online_cpu, + hisi_uncore_pmu_offline_cpu); + if (ret) { + pr_err("HHA PMU: Error setup hotplug, ret = %d;\n", ret); + return ret; + } + + ret = platform_driver_register(&hisi_hha_pmu_driver); + if (ret) + cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE); + + return ret; +} +module_init(hisi_hha_pmu_module_init); + +static void __exit hisi_hha_pmu_module_exit(void) +{ + platform_driver_unregister(&hisi_hha_pmu_driver); + cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE); +} +module_exit(hisi_hha_pmu_module_exit); + +MODULE_DESCRIPTION("HiSilicon SoC HHA uncore PMU driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Shaokun Zhang "); +MODULE_AUTHOR("Anurup M "); diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index 9621efdbb3bd..cd63c52e7d93 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -153,6 +153,7 @@ enum cpuhp_state { CPUHP_AP_PERF_S390_SF_ONLINE, CPUHP_AP_PERF_ARM_CCI_ONLINE, CPUHP_AP_PERF_ARM_CCN_ONLINE, + CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, CPUHP_AP_PERF_ARM_L2X0_ONLINE, CPUHP_AP_PERF_ARM_QCOM_L2_ONLINE, -- cgit v1.2.3-59-g8ed1b From 904dcf03f086a2e3b9d1e02cb57c43ea2e588c8c Mon Sep 17 00:00:00 2001 From: Shaokun Zhang Date: Thu, 19 Oct 2017 19:05:20 +0800 Subject: perf: hisi: Add support for HiSilicon SoC DDRC PMU driver This patch adds support for DDRC PMU driver in HiSilicon SoC chip, Each DDRC has own control, counter and interrupt registers and is an separate PMU. For each DDRC PMU, it has 8-fixed-purpose counters which have been mapped to 8-events by hardware, it assumes that counter index is equal to event code (0 - 7) in DDRC PMU driver. Interrupt is supported to handle counter (32-bits) overflow. Acked-by: Mark Rutland Reviewed-by: Jonathan Cameron Signed-off-by: Shaokun Zhang Signed-off-by: Anurup M Signed-off-by: Will Deacon --- drivers/perf/hisilicon/Makefile | 2 +- drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 463 ++++++++++++++++++++++++++ include/linux/cpuhotplug.h | 1 + 3 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c (limited to 'include') diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile index a72afe84d034..2621d51ae87a 100644 --- a/drivers/perf/hisilicon/Makefile +++ b/drivers/perf/hisilicon/Makefile @@ -1 +1 @@ -obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o hisi_uncore_hha_pmu.o +obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o hisi_uncore_hha_pmu.o hisi_uncore_ddrc_pmu.o diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c new file mode 100644 index 000000000000..1b10ea05a914 --- /dev/null +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -0,0 +1,463 @@ +/* + * HiSilicon SoC DDRC uncore Hardware event counters support + * + * Copyright (C) 2017 Hisilicon Limited + * Author: Shaokun Zhang + * Anurup M + * + * This code is based on the uncore PMUs like arm-cci and arm-ccn. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hisi_uncore_pmu.h" + +/* DDRC register definition */ +#define DDRC_PERF_CTRL 0x010 +#define DDRC_FLUX_WR 0x380 +#define DDRC_FLUX_RD 0x384 +#define DDRC_FLUX_WCMD 0x388 +#define DDRC_FLUX_RCMD 0x38c +#define DDRC_PRE_CMD 0x3c0 +#define DDRC_ACT_CMD 0x3c4 +#define DDRC_BNK_CHG 0x3c8 +#define DDRC_RNK_CHG 0x3cc +#define DDRC_EVENT_CTRL 0x6C0 +#define DDRC_INT_MASK 0x6c8 +#define DDRC_INT_STATUS 0x6cc +#define DDRC_INT_CLEAR 0x6d0 + +/* DDRC has 8-counters */ +#define DDRC_NR_COUNTERS 0x8 +#define DDRC_PERF_CTRL_EN 0x2 + +/* + * For DDRC PMU, there are eight-events and every event has been mapped + * to fixed-purpose counters which register offset is not consistent. + * Therefore there is no write event type and we assume that event + * code (0 to 7) is equal to counter index in PMU driver. + */ +#define GET_DDRC_EVENTID(hwc) (hwc->config_base & 0x7) + +static const u32 ddrc_reg_off[] = { + DDRC_FLUX_WR, DDRC_FLUX_RD, DDRC_FLUX_WCMD, DDRC_FLUX_RCMD, + DDRC_PRE_CMD, DDRC_ACT_CMD, DDRC_BNK_CHG, DDRC_RNK_CHG +}; + +/* + * Select the counter register offset using the counter index. + * In DDRC there are no programmable counter, the count + * is readed form the statistics counter register itself. + */ +static u32 hisi_ddrc_pmu_get_counter_offset(int cntr_idx) +{ + return ddrc_reg_off[cntr_idx]; +} + +static u64 hisi_ddrc_pmu_read_counter(struct hisi_pmu *ddrc_pmu, + struct hw_perf_event *hwc) +{ + /* Use event code as counter index */ + u32 idx = GET_DDRC_EVENTID(hwc); + + if (!hisi_uncore_pmu_counter_valid(ddrc_pmu, idx)) { + dev_err(ddrc_pmu->dev, "Unsupported event index:%d!\n", idx); + return 0; + } + + return readl(ddrc_pmu->base + hisi_ddrc_pmu_get_counter_offset(idx)); +} + +static void hisi_ddrc_pmu_write_counter(struct hisi_pmu *ddrc_pmu, + struct hw_perf_event *hwc, u64 val) +{ + u32 idx = GET_DDRC_EVENTID(hwc); + + if (!hisi_uncore_pmu_counter_valid(ddrc_pmu, idx)) { + dev_err(ddrc_pmu->dev, "Unsupported event index:%d!\n", idx); + return; + } + + writel((u32)val, + ddrc_pmu->base + hisi_ddrc_pmu_get_counter_offset(idx)); +} + +/* + * For DDRC PMU, event has been mapped to fixed-purpose counter by hardware, + * so there is no need to write event type. + */ +static void hisi_ddrc_pmu_write_evtype(struct hisi_pmu *hha_pmu, int idx, + u32 type) +{ +} + +static void hisi_ddrc_pmu_start_counters(struct hisi_pmu *ddrc_pmu) +{ + u32 val; + + /* Set perf_enable in DDRC_PERF_CTRL to start event counting */ + val = readl(ddrc_pmu->base + DDRC_PERF_CTRL); + val |= DDRC_PERF_CTRL_EN; + writel(val, ddrc_pmu->base + DDRC_PERF_CTRL); +} + +static void hisi_ddrc_pmu_stop_counters(struct hisi_pmu *ddrc_pmu) +{ + u32 val; + + /* Clear perf_enable in DDRC_PERF_CTRL to stop event counting */ + val = readl(ddrc_pmu->base + DDRC_PERF_CTRL); + val &= ~DDRC_PERF_CTRL_EN; + writel(val, ddrc_pmu->base + DDRC_PERF_CTRL); +} + +static void hisi_ddrc_pmu_enable_counter(struct hisi_pmu *ddrc_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Set counter index(event code) in DDRC_EVENT_CTRL register */ + val = readl(ddrc_pmu->base + DDRC_EVENT_CTRL); + val |= (1 << GET_DDRC_EVENTID(hwc)); + writel(val, ddrc_pmu->base + DDRC_EVENT_CTRL); +} + +static void hisi_ddrc_pmu_disable_counter(struct hisi_pmu *ddrc_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Clear counter index(event code) in DDRC_EVENT_CTRL register */ + val = readl(ddrc_pmu->base + DDRC_EVENT_CTRL); + val &= ~(1 << GET_DDRC_EVENTID(hwc)); + writel(val, ddrc_pmu->base + DDRC_EVENT_CTRL); +} + +static int hisi_ddrc_pmu_get_event_idx(struct perf_event *event) +{ + struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu); + unsigned long *used_mask = ddrc_pmu->pmu_events.used_mask; + struct hw_perf_event *hwc = &event->hw; + /* For DDRC PMU, we use event code as counter index */ + int idx = GET_DDRC_EVENTID(hwc); + + if (test_bit(idx, used_mask)) + return -EAGAIN; + + set_bit(idx, used_mask); + + return idx; +} + +static void hisi_ddrc_pmu_enable_counter_int(struct hisi_pmu *ddrc_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Write 0 to enable interrupt */ + val = readl(ddrc_pmu->base + DDRC_INT_MASK); + val &= ~(1 << GET_DDRC_EVENTID(hwc)); + writel(val, ddrc_pmu->base + DDRC_INT_MASK); +} + +static void hisi_ddrc_pmu_disable_counter_int(struct hisi_pmu *ddrc_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Write 1 to mask interrupt */ + val = readl(ddrc_pmu->base + DDRC_INT_MASK); + val |= (1 << GET_DDRC_EVENTID(hwc)); + writel(val, ddrc_pmu->base + DDRC_INT_MASK); +} + +static irqreturn_t hisi_ddrc_pmu_isr(int irq, void *dev_id) +{ + struct hisi_pmu *ddrc_pmu = dev_id; + struct perf_event *event; + unsigned long overflown; + int idx; + + /* Read the DDRC_INT_STATUS register */ + overflown = readl(ddrc_pmu->base + DDRC_INT_STATUS); + if (!overflown) + return IRQ_NONE; + + /* + * Find the counter index which overflowed if the bit was set + * and handle it + */ + for_each_set_bit(idx, &overflown, DDRC_NR_COUNTERS) { + /* Write 1 to clear the IRQ status flag */ + writel((1 << idx), ddrc_pmu->base + DDRC_INT_CLEAR); + + /* Get the corresponding event struct */ + event = ddrc_pmu->pmu_events.hw_events[idx]; + if (!event) + continue; + + hisi_uncore_pmu_event_update(event); + hisi_uncore_pmu_set_event_period(event); + } + + return IRQ_HANDLED; +} + +static int hisi_ddrc_pmu_init_irq(struct hisi_pmu *ddrc_pmu, + struct platform_device *pdev) +{ + int irq, ret; + + /* Read and init IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "DDRC PMU get irq fail; irq:%d\n", irq); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, hisi_ddrc_pmu_isr, + IRQF_NOBALANCING | IRQF_NO_THREAD, + dev_name(&pdev->dev), ddrc_pmu); + if (ret < 0) { + dev_err(&pdev->dev, + "Fail to request IRQ:%d ret:%d\n", irq, ret); + return ret; + } + + ddrc_pmu->irq = irq; + + return 0; +} + +static const struct acpi_device_id hisi_ddrc_pmu_acpi_match[] = { + { "HISI0233", }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, hisi_ddrc_pmu_acpi_match); + +static int hisi_ddrc_pmu_init_data(struct platform_device *pdev, + struct hisi_pmu *ddrc_pmu) +{ + struct resource *res; + + /* + * Use the SCCL_ID and DDRC channel ID to identify the + * DDRC PMU, while SCCL_ID is in MPIDR[aff2]. + */ + if (device_property_read_u32(&pdev->dev, "hisilicon,ch-id", + &ddrc_pmu->index_id)) { + dev_err(&pdev->dev, "Can not read ddrc channel-id!\n"); + return -EINVAL; + } + + if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", + &ddrc_pmu->sccl_id)) { + dev_err(&pdev->dev, "Can not read ddrc sccl-id!\n"); + return -EINVAL; + } + /* DDRC PMUs only share the same SCCL */ + ddrc_pmu->ccl_id = -1; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ddrc_pmu->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ddrc_pmu->base)) { + dev_err(&pdev->dev, "ioremap failed for ddrc_pmu resource\n"); + return PTR_ERR(ddrc_pmu->base); + } + + return 0; +} + +static struct attribute *hisi_ddrc_pmu_format_attr[] = { + HISI_PMU_FORMAT_ATTR(event, "config:0-4"), + NULL, +}; + +static const struct attribute_group hisi_ddrc_pmu_format_group = { + .name = "format", + .attrs = hisi_ddrc_pmu_format_attr, +}; + +static struct attribute *hisi_ddrc_pmu_events_attr[] = { + HISI_PMU_EVENT_ATTR(flux_wr, 0x00), + HISI_PMU_EVENT_ATTR(flux_rd, 0x01), + HISI_PMU_EVENT_ATTR(flux_wcmd, 0x02), + HISI_PMU_EVENT_ATTR(flux_rcmd, 0x03), + HISI_PMU_EVENT_ATTR(pre_cmd, 0x04), + HISI_PMU_EVENT_ATTR(act_cmd, 0x05), + HISI_PMU_EVENT_ATTR(rnk_chg, 0x06), + HISI_PMU_EVENT_ATTR(rw_chg, 0x07), + NULL, +}; + +static const struct attribute_group hisi_ddrc_pmu_events_group = { + .name = "events", + .attrs = hisi_ddrc_pmu_events_attr, +}; + +static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); + +static struct attribute *hisi_ddrc_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static const struct attribute_group hisi_ddrc_pmu_cpumask_attr_group = { + .attrs = hisi_ddrc_pmu_cpumask_attrs, +}; + +static const struct attribute_group *hisi_ddrc_pmu_attr_groups[] = { + &hisi_ddrc_pmu_format_group, + &hisi_ddrc_pmu_events_group, + &hisi_ddrc_pmu_cpumask_attr_group, + NULL, +}; + +static const struct hisi_uncore_ops hisi_uncore_ddrc_ops = { + .write_evtype = hisi_ddrc_pmu_write_evtype, + .get_event_idx = hisi_ddrc_pmu_get_event_idx, + .start_counters = hisi_ddrc_pmu_start_counters, + .stop_counters = hisi_ddrc_pmu_stop_counters, + .enable_counter = hisi_ddrc_pmu_enable_counter, + .disable_counter = hisi_ddrc_pmu_disable_counter, + .enable_counter_int = hisi_ddrc_pmu_enable_counter_int, + .disable_counter_int = hisi_ddrc_pmu_disable_counter_int, + .write_counter = hisi_ddrc_pmu_write_counter, + .read_counter = hisi_ddrc_pmu_read_counter, +}; + +static int hisi_ddrc_pmu_dev_probe(struct platform_device *pdev, + struct hisi_pmu *ddrc_pmu) +{ + int ret; + + ret = hisi_ddrc_pmu_init_data(pdev, ddrc_pmu); + if (ret) + return ret; + + ret = hisi_ddrc_pmu_init_irq(ddrc_pmu, pdev); + if (ret) + return ret; + + ddrc_pmu->num_counters = DDRC_NR_COUNTERS; + ddrc_pmu->counter_bits = 32; + ddrc_pmu->ops = &hisi_uncore_ddrc_ops; + ddrc_pmu->dev = &pdev->dev; + ddrc_pmu->on_cpu = -1; + ddrc_pmu->check_event = 7; + + return 0; +} + +static int hisi_ddrc_pmu_probe(struct platform_device *pdev) +{ + struct hisi_pmu *ddrc_pmu; + char *name; + int ret; + + ddrc_pmu = devm_kzalloc(&pdev->dev, sizeof(*ddrc_pmu), GFP_KERNEL); + if (!ddrc_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, ddrc_pmu); + + ret = hisi_ddrc_pmu_dev_probe(pdev, ddrc_pmu); + if (ret) + return ret; + + ret = cpuhp_state_add_instance(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE, + &ddrc_pmu->node); + if (ret) { + dev_err(&pdev->dev, "Error %d registering hotplug;\n", ret); + return ret; + } + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_ddrc%u", + ddrc_pmu->sccl_id, ddrc_pmu->index_id); + ddrc_pmu->pmu = (struct pmu) { + .name = name, + .task_ctx_nr = perf_invalid_context, + .event_init = hisi_uncore_pmu_event_init, + .pmu_enable = hisi_uncore_pmu_enable, + .pmu_disable = hisi_uncore_pmu_disable, + .add = hisi_uncore_pmu_add, + .del = hisi_uncore_pmu_del, + .start = hisi_uncore_pmu_start, + .stop = hisi_uncore_pmu_stop, + .read = hisi_uncore_pmu_read, + .attr_groups = hisi_ddrc_pmu_attr_groups, + }; + + ret = perf_pmu_register(&ddrc_pmu->pmu, name, -1); + if (ret) { + dev_err(ddrc_pmu->dev, "DDRC PMU register failed!\n"); + cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE, + &ddrc_pmu->node); + } + + return ret; +} + +static int hisi_ddrc_pmu_remove(struct platform_device *pdev) +{ + struct hisi_pmu *ddrc_pmu = platform_get_drvdata(pdev); + + perf_pmu_unregister(&ddrc_pmu->pmu); + cpuhp_state_remove_instance(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE, + &ddrc_pmu->node); + + return 0; +} + +static struct platform_driver hisi_ddrc_pmu_driver = { + .driver = { + .name = "hisi_ddrc_pmu", + .acpi_match_table = ACPI_PTR(hisi_ddrc_pmu_acpi_match), + }, + .probe = hisi_ddrc_pmu_probe, + .remove = hisi_ddrc_pmu_remove, +}; + +static int __init hisi_ddrc_pmu_module_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE, + "AP_PERF_ARM_HISI_DDRC_ONLINE", + hisi_uncore_pmu_online_cpu, + hisi_uncore_pmu_offline_cpu); + if (ret) { + pr_err("DDRC PMU: setup hotplug, ret = %d\n", ret); + return ret; + } + + ret = platform_driver_register(&hisi_ddrc_pmu_driver); + if (ret) + cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE); + + return ret; +} +module_init(hisi_ddrc_pmu_module_init); + +static void __exit hisi_ddrc_pmu_module_exit(void) +{ + platform_driver_unregister(&hisi_ddrc_pmu_driver); + cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE); + +} +module_exit(hisi_ddrc_pmu_module_exit); + +MODULE_DESCRIPTION("HiSilicon SoC DDRC uncore PMU driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Shaokun Zhang "); +MODULE_AUTHOR("Anurup M "); diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h index cd63c52e7d93..587006de6f82 100644 --- a/include/linux/cpuhotplug.h +++ b/include/linux/cpuhotplug.h @@ -153,6 +153,7 @@ enum cpuhp_state { CPUHP_AP_PERF_S390_SF_ONLINE, CPUHP_AP_PERF_ARM_CCI_ONLINE, CPUHP_AP_PERF_ARM_CCN_ONLINE, + CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE, CPUHP_AP_PERF_ARM_HISI_HHA_ONLINE, CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, CPUHP_AP_PERF_ARM_L2X0_ONLINE, -- cgit v1.2.3-59-g8ed1b From b9f19259b84dc648f207a46f3581d15eeaedf4b6 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 19 Oct 2017 14:57:48 +0200 Subject: drm/vc4: Add the DRM_IOCTL_VC4_GEM_MADVISE ioctl This ioctl will allow us to purge inactive userspace buffers when the system is running out of contiguous memory. For now, the purge logic is rather dumb in that it does not try to release only the amount of BO needed to meet the last CMA alloc request but instead purges all objects placed in the purgeable pool as soon as we experience a CMA allocation failure. Note that the in-kernel BO cache is always purged before the purgeable cache because those objects are known to be unused while objects marked as purgeable by a userspace application/library might have to be restored when they are marked back as unpurgeable, which can be expensive. Signed-off-by: Boris Brezillon Signed-off-by: Eric Anholt Reviewed-by: Eric Anholt Link: https://patchwork.freedesktop.org/patch/msgid/20171019125748.3152-1-boris.brezillon@free-electrons.com --- drivers/gpu/drm/vc4/vc4_bo.c | 287 ++++++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/vc4/vc4_drv.c | 10 +- drivers/gpu/drm/vc4/vc4_drv.h | 30 +++++ drivers/gpu/drm/vc4/vc4_gem.c | 156 +++++++++++++++++++++- drivers/gpu/drm/vc4/vc4_plane.c | 20 +++ include/uapi/drm/vc4_drm.h | 19 +++ 6 files changed, 507 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c index 3afdbf4bc10b..01a53ba304f8 100644 --- a/drivers/gpu/drm/vc4/vc4_bo.c +++ b/drivers/gpu/drm/vc4/vc4_bo.c @@ -53,6 +53,17 @@ static void vc4_bo_stats_dump(struct vc4_dev *vc4) vc4->bo_labels[i].size_allocated / 1024, vc4->bo_labels[i].num_allocated); } + + mutex_lock(&vc4->purgeable.lock); + if (vc4->purgeable.num) + DRM_INFO("%30s: %6zdkb BOs (%d)\n", "userspace BO cache", + vc4->purgeable.size / 1024, vc4->purgeable.num); + + if (vc4->purgeable.purged_num) + DRM_INFO("%30s: %6zdkb BOs (%d)\n", "total purged BO", + vc4->purgeable.purged_size / 1024, + vc4->purgeable.purged_num); + mutex_unlock(&vc4->purgeable.lock); } #ifdef CONFIG_DEBUG_FS @@ -75,6 +86,17 @@ int vc4_bo_stats_debugfs(struct seq_file *m, void *unused) } mutex_unlock(&vc4->bo_lock); + mutex_lock(&vc4->purgeable.lock); + if (vc4->purgeable.num) + seq_printf(m, "%30s: %6dkb BOs (%d)\n", "userspace BO cache", + vc4->purgeable.size / 1024, vc4->purgeable.num); + + if (vc4->purgeable.purged_num) + seq_printf(m, "%30s: %6dkb BOs (%d)\n", "total purged BO", + vc4->purgeable.purged_size / 1024, + vc4->purgeable.purged_num); + mutex_unlock(&vc4->purgeable.lock); + return 0; } #endif @@ -247,6 +269,109 @@ static void vc4_bo_cache_purge(struct drm_device *dev) mutex_unlock(&vc4->bo_lock); } +void vc4_bo_add_to_purgeable_pool(struct vc4_bo *bo) +{ + struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); + + mutex_lock(&vc4->purgeable.lock); + list_add_tail(&bo->size_head, &vc4->purgeable.list); + vc4->purgeable.num++; + vc4->purgeable.size += bo->base.base.size; + mutex_unlock(&vc4->purgeable.lock); +} + +static void vc4_bo_remove_from_purgeable_pool_locked(struct vc4_bo *bo) +{ + struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); + + /* list_del_init() is used here because the caller might release + * the purgeable lock in order to acquire the madv one and update the + * madv status. + * During this short period of time a user might decide to mark + * the BO as unpurgeable, and if bo->madv is set to + * VC4_MADV_DONTNEED it will try to remove the BO from the + * purgeable list which will fail if the ->next/prev fields + * are set to LIST_POISON1/LIST_POISON2 (which is what + * list_del() does). + * Re-initializing the list element guarantees that list_del() + * will work correctly even if it's a NOP. + */ + list_del_init(&bo->size_head); + vc4->purgeable.num--; + vc4->purgeable.size -= bo->base.base.size; +} + +void vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo) +{ + struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev); + + mutex_lock(&vc4->purgeable.lock); + vc4_bo_remove_from_purgeable_pool_locked(bo); + mutex_unlock(&vc4->purgeable.lock); +} + +static void vc4_bo_purge(struct drm_gem_object *obj) +{ + struct vc4_bo *bo = to_vc4_bo(obj); + struct drm_device *dev = obj->dev; + + WARN_ON(!mutex_is_locked(&bo->madv_lock)); + WARN_ON(bo->madv != VC4_MADV_DONTNEED); + + drm_vma_node_unmap(&obj->vma_node, dev->anon_inode->i_mapping); + + dma_free_wc(dev->dev, obj->size, bo->base.vaddr, bo->base.paddr); + bo->base.vaddr = NULL; + bo->madv = __VC4_MADV_PURGED; +} + +static void vc4_bo_userspace_cache_purge(struct drm_device *dev) +{ + struct vc4_dev *vc4 = to_vc4_dev(dev); + + mutex_lock(&vc4->purgeable.lock); + while (!list_empty(&vc4->purgeable.list)) { + struct vc4_bo *bo = list_first_entry(&vc4->purgeable.list, + struct vc4_bo, size_head); + struct drm_gem_object *obj = &bo->base.base; + size_t purged_size = 0; + + vc4_bo_remove_from_purgeable_pool_locked(bo); + + /* Release the purgeable lock while we're purging the BO so + * that other people can continue inserting things in the + * purgeable pool without having to wait for all BOs to be + * purged. + */ + mutex_unlock(&vc4->purgeable.lock); + mutex_lock(&bo->madv_lock); + + /* Since we released the purgeable pool lock before acquiring + * the BO madv one, the user may have marked the BO as WILLNEED + * and re-used it in the meantime. + * Before purging the BO we need to make sure + * - it is still marked as DONTNEED + * - it has not been re-inserted in the purgeable list + * - it is not used by HW blocks + * If one of these conditions is not met, just skip the entry. + */ + if (bo->madv == VC4_MADV_DONTNEED && + list_empty(&bo->size_head) && + !refcount_read(&bo->usecnt)) { + purged_size = bo->base.base.size; + vc4_bo_purge(obj); + } + mutex_unlock(&bo->madv_lock); + mutex_lock(&vc4->purgeable.lock); + + if (purged_size) { + vc4->purgeable.purged_size += purged_size; + vc4->purgeable.purged_num++; + } + } + mutex_unlock(&vc4->purgeable.lock); +} + static struct vc4_bo *vc4_bo_get_from_cache(struct drm_device *dev, uint32_t size, enum vc4_kernel_bo_type type) @@ -293,6 +418,9 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size) if (!bo) return ERR_PTR(-ENOMEM); + bo->madv = VC4_MADV_WILLNEED; + refcount_set(&bo->usecnt, 0); + mutex_init(&bo->madv_lock); mutex_lock(&vc4->bo_lock); bo->label = VC4_BO_TYPE_KERNEL; vc4->bo_labels[VC4_BO_TYPE_KERNEL].num_allocated++; @@ -330,16 +458,38 @@ struct vc4_bo *vc4_bo_create(struct drm_device *dev, size_t unaligned_size, * CMA allocations we've got laying around and try again. */ vc4_bo_cache_purge(dev); + cma_obj = drm_gem_cma_create(dev, size); + } + if (IS_ERR(cma_obj)) { + /* + * Still not enough CMA memory, purge the userspace BO + * cache and retry. + * This is sub-optimal since we purge the whole userspace + * BO cache which forces user that want to re-use the BO to + * restore its initial content. + * Ideally, we should purge entries one by one and retry + * after each to see if CMA allocation succeeds. Or even + * better, try to find an entry with at least the same + * size. + */ + vc4_bo_userspace_cache_purge(dev); cma_obj = drm_gem_cma_create(dev, size); - if (IS_ERR(cma_obj)) { - DRM_ERROR("Failed to allocate from CMA:\n"); - vc4_bo_stats_dump(vc4); - return ERR_PTR(-ENOMEM); - } + } + + if (IS_ERR(cma_obj)) { + DRM_ERROR("Failed to allocate from CMA:\n"); + vc4_bo_stats_dump(vc4); + return ERR_PTR(-ENOMEM); } bo = to_vc4_bo(&cma_obj->base); + /* By default, BOs do not support the MADV ioctl. This will be enabled + * only on BOs that are exposed to userspace (V3D, V3D_SHADER and DUMB + * BOs). + */ + bo->madv = __VC4_MADV_NOTSUPP; + mutex_lock(&vc4->bo_lock); vc4_bo_set_label(&cma_obj->base, type); mutex_unlock(&vc4->bo_lock); @@ -365,6 +515,8 @@ int vc4_dumb_create(struct drm_file *file_priv, if (IS_ERR(bo)) return PTR_ERR(bo); + bo->madv = VC4_MADV_WILLNEED; + ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); drm_gem_object_put_unlocked(&bo->base.base); @@ -403,6 +555,12 @@ void vc4_free_object(struct drm_gem_object *gem_bo) struct vc4_bo *bo = to_vc4_bo(gem_bo); struct list_head *cache_list; + /* Remove the BO from the purgeable list. */ + mutex_lock(&bo->madv_lock); + if (bo->madv == VC4_MADV_DONTNEED && !refcount_read(&bo->usecnt)) + vc4_bo_remove_from_purgeable_pool(bo); + mutex_unlock(&bo->madv_lock); + mutex_lock(&vc4->bo_lock); /* If the object references someone else's memory, we can't cache it. */ @@ -418,7 +576,8 @@ void vc4_free_object(struct drm_gem_object *gem_bo) } /* If this object was partially constructed but CMA allocation - * had failed, just free it. + * had failed, just free it. Can also happen when the BO has been + * purged. */ if (!bo->base.vaddr) { vc4_bo_destroy(bo); @@ -437,6 +596,10 @@ void vc4_free_object(struct drm_gem_object *gem_bo) bo->validated_shader = NULL; } + /* Reset madv and usecnt before adding the BO to the cache. */ + bo->madv = __VC4_MADV_NOTSUPP; + refcount_set(&bo->usecnt, 0); + bo->t_format = false; bo->free_time = jiffies; list_add(&bo->size_head, cache_list); @@ -461,6 +624,56 @@ static void vc4_bo_cache_time_work(struct work_struct *work) mutex_unlock(&vc4->bo_lock); } +int vc4_bo_inc_usecnt(struct vc4_bo *bo) +{ + int ret; + + /* Fast path: if the BO is already retained by someone, no need to + * check the madv status. + */ + if (refcount_inc_not_zero(&bo->usecnt)) + return 0; + + mutex_lock(&bo->madv_lock); + switch (bo->madv) { + case VC4_MADV_WILLNEED: + refcount_inc(&bo->usecnt); + ret = 0; + break; + case VC4_MADV_DONTNEED: + /* We shouldn't use a BO marked as purgeable if at least + * someone else retained its content by incrementing usecnt. + * Luckily the BO hasn't been purged yet, but something wrong + * is happening here. Just throw an error instead of + * authorizing this use case. + */ + case __VC4_MADV_PURGED: + /* We can't use a purged BO. */ + default: + /* Invalid madv value. */ + ret = -EINVAL; + break; + } + mutex_unlock(&bo->madv_lock); + + return ret; +} + +void vc4_bo_dec_usecnt(struct vc4_bo *bo) +{ + /* Fast path: if the BO is still retained by someone, no need to test + * the madv value. + */ + if (refcount_dec_not_one(&bo->usecnt)) + return; + + mutex_lock(&bo->madv_lock); + if (refcount_dec_and_test(&bo->usecnt) && + bo->madv == VC4_MADV_DONTNEED) + vc4_bo_add_to_purgeable_pool(bo); + mutex_unlock(&bo->madv_lock); +} + static void vc4_bo_cache_time_timer(unsigned long data) { struct drm_device *dev = (struct drm_device *)data; @@ -480,18 +693,52 @@ struct dma_buf * vc4_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags) { struct vc4_bo *bo = to_vc4_bo(obj); + struct dma_buf *dmabuf; + int ret; if (bo->validated_shader) { DRM_DEBUG("Attempting to export shader BO\n"); return ERR_PTR(-EINVAL); } - return drm_gem_prime_export(dev, obj, flags); + /* Note: as soon as the BO is exported it becomes unpurgeable, because + * noone ever decrements the usecnt even if the reference held by the + * exported BO is released. This shouldn't be a problem since we don't + * expect exported BOs to be marked as purgeable. + */ + ret = vc4_bo_inc_usecnt(bo); + if (ret) { + DRM_ERROR("Failed to increment BO usecnt\n"); + return ERR_PTR(ret); + } + + dmabuf = drm_gem_prime_export(dev, obj, flags); + if (IS_ERR(dmabuf)) + vc4_bo_dec_usecnt(bo); + + return dmabuf; +} + +int vc4_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + struct drm_gem_object *obj = vma->vm_private_data; + struct vc4_bo *bo = to_vc4_bo(obj); + + /* The only reason we would end up here is when user-space accesses + * BO's memory after it's been purged. + */ + mutex_lock(&bo->madv_lock); + WARN_ON(bo->madv != __VC4_MADV_PURGED); + mutex_unlock(&bo->madv_lock); + + return VM_FAULT_SIGBUS; } int vc4_mmap(struct file *filp, struct vm_area_struct *vma) { struct drm_gem_object *gem_obj; + unsigned long vm_pgoff; struct vc4_bo *bo; int ret; @@ -507,16 +754,36 @@ int vc4_mmap(struct file *filp, struct vm_area_struct *vma) return -EINVAL; } + if (bo->madv != VC4_MADV_WILLNEED) { + DRM_DEBUG("mmaping of %s BO not allowed\n", + bo->madv == VC4_MADV_DONTNEED ? + "purgeable" : "purged"); + return -EINVAL; + } + /* * Clear the VM_PFNMAP flag that was set by drm_gem_mmap(), and set the * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map * the whole buffer. */ vma->vm_flags &= ~VM_PFNMAP; - vma->vm_pgoff = 0; + /* This ->vm_pgoff dance is needed to make all parties happy: + * - dma_mmap_wc() uses ->vm_pgoff as an offset within the allocated + * mem-region, hence the need to set it to zero (the value set by + * the DRM core is a virtual offset encoding the GEM object-id) + * - the mmap() core logic needs ->vm_pgoff to be restored to its + * initial value before returning from this function because it + * encodes the offset of this GEM in the dev->anon_inode pseudo-file + * and this information will be used when we invalidate userspace + * mappings with drm_vma_node_unmap() (called from vc4_gem_purge()). + */ + vm_pgoff = vma->vm_pgoff; + vma->vm_pgoff = 0; ret = dma_mmap_wc(bo->base.base.dev->dev, vma, bo->base.vaddr, bo->base.paddr, vma->vm_end - vma->vm_start); + vma->vm_pgoff = vm_pgoff; + if (ret) drm_gem_vm_close(vma); @@ -580,6 +847,8 @@ int vc4_create_bo_ioctl(struct drm_device *dev, void *data, if (IS_ERR(bo)) return PTR_ERR(bo); + bo->madv = VC4_MADV_WILLNEED; + ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle); drm_gem_object_put_unlocked(&bo->base.base); @@ -633,6 +902,8 @@ vc4_create_shader_bo_ioctl(struct drm_device *dev, void *data, if (IS_ERR(bo)) return PTR_ERR(bo); + bo->madv = VC4_MADV_WILLNEED; + if (copy_from_user(bo->base.vaddr, (void __user *)(uintptr_t)args->data, args->size)) { diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 1c96edcb302b..e3c29729da2e 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -100,6 +100,7 @@ static int vc4_get_param_ioctl(struct drm_device *dev, void *data, case DRM_VC4_PARAM_SUPPORTS_ETC1: case DRM_VC4_PARAM_SUPPORTS_THREADED_FS: case DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER: + case DRM_VC4_PARAM_SUPPORTS_MADVISE: args->value = true; break; default: @@ -117,6 +118,12 @@ static void vc4_lastclose(struct drm_device *dev) drm_fbdev_cma_restore_mode(vc4->fbdev); } +static const struct vm_operations_struct vc4_vm_ops = { + .fault = vc4_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + static const struct file_operations vc4_drm_fops = { .owner = THIS_MODULE, .open = drm_open, @@ -142,6 +149,7 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = { DRM_IOCTL_DEF_DRV(VC4_SET_TILING, vc4_set_tiling_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(VC4_GET_TILING, vc4_get_tiling_ioctl, DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(VC4_LABEL_BO, vc4_label_bo_ioctl, DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(VC4_GEM_MADVISE, vc4_gem_madvise_ioctl, DRM_RENDER_ALLOW), }; static struct drm_driver vc4_drm_driver = { @@ -166,7 +174,7 @@ static struct drm_driver vc4_drm_driver = { .gem_create_object = vc4_create_object, .gem_free_object_unlocked = vc4_free_object, - .gem_vm_ops = &drm_gem_cma_vm_ops, + .gem_vm_ops = &vc4_vm_ops, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 87f2d8e5c134..9c0d380c96f2 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -74,6 +74,19 @@ struct vc4_dev { /* Protects bo_cache and bo_labels. */ struct mutex bo_lock; + /* Purgeable BO pool. All BOs in this pool can have their memory + * reclaimed if the driver is unable to allocate new BOs. We also + * keep stats related to the purge mechanism here. + */ + struct { + struct list_head list; + unsigned int num; + size_t size; + unsigned int purged_num; + size_t purged_size; + struct mutex lock; + } purgeable; + uint64_t dma_fence_context; /* Sequence number for the last job queued in bin_job_list. @@ -192,6 +205,16 @@ struct vc4_bo { * for user-allocated labels. */ int label; + + /* Count the number of active users. This is needed to determine + * whether we can move the BO to the purgeable list or not (when the BO + * is used by the GPU or the display engine we can't purge it). + */ + refcount_t usecnt; + + /* Store purgeable/purged state here */ + u32 madv; + struct mutex madv_lock; }; static inline struct vc4_bo * @@ -503,6 +526,7 @@ int vc4_get_hang_state_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int vc4_label_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int vc4_fault(struct vm_fault *vmf); int vc4_mmap(struct file *filp, struct vm_area_struct *vma); struct reservation_object *vc4_prime_res_obj(struct drm_gem_object *obj); int vc4_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); @@ -513,6 +537,10 @@ void *vc4_prime_vmap(struct drm_gem_object *obj); int vc4_bo_cache_init(struct drm_device *dev); void vc4_bo_cache_destroy(struct drm_device *dev); int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); +int vc4_bo_inc_usecnt(struct vc4_bo *bo); +void vc4_bo_dec_usecnt(struct vc4_bo *bo); +void vc4_bo_add_to_purgeable_pool(struct vc4_bo *bo); +void vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo); /* vc4_crtc.c */ extern struct platform_driver vc4_crtc_driver; @@ -557,6 +585,8 @@ void vc4_job_handle_completed(struct vc4_dev *vc4); int vc4_queue_seqno_cb(struct drm_device *dev, struct vc4_seqno_cb *cb, uint64_t seqno, void (*func)(struct vc4_seqno_cb *cb)); +int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); /* vc4_hdmi.c */ extern struct platform_driver vc4_hdmi_driver; diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index d0c6bfb68c4e..e00ac2f3a264 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -188,11 +188,22 @@ vc4_save_hang_state(struct drm_device *dev) continue; for (j = 0; j < exec[i]->bo_count; j++) { + bo = to_vc4_bo(&exec[i]->bo[j]->base); + + /* Retain BOs just in case they were marked purgeable. + * This prevents the BO from being purged before + * someone had a chance to dump the hang state. + */ + WARN_ON(!refcount_read(&bo->usecnt)); + refcount_inc(&bo->usecnt); drm_gem_object_get(&exec[i]->bo[j]->base); kernel_state->bo[j + prev_idx] = &exec[i]->bo[j]->base; } list_for_each_entry(bo, &exec[i]->unref_list, unref_head) { + /* No need to retain BOs coming from the ->unref_list + * because they are naturally unpurgeable. + */ drm_gem_object_get(&bo->base.base); kernel_state->bo[j + prev_idx] = &bo->base.base; j++; @@ -233,6 +244,26 @@ vc4_save_hang_state(struct drm_device *dev) state->fdbgs = V3D_READ(V3D_FDBGS); state->errstat = V3D_READ(V3D_ERRSTAT); + /* We need to turn purgeable BOs into unpurgeable ones so that + * userspace has a chance to dump the hang state before the kernel + * decides to purge those BOs. + * Note that BO consistency at dump time cannot be guaranteed. For + * example, if the owner of these BOs decides to re-use them or mark + * them purgeable again there's nothing we can do to prevent it. + */ + for (i = 0; i < kernel_state->user_state.bo_count; i++) { + struct vc4_bo *bo = to_vc4_bo(kernel_state->bo[i]); + + if (bo->madv == __VC4_MADV_NOTSUPP) + continue; + + mutex_lock(&bo->madv_lock); + if (!WARN_ON(bo->madv == __VC4_MADV_PURGED)) + bo->madv = VC4_MADV_WILLNEED; + refcount_dec(&bo->usecnt); + mutex_unlock(&bo->madv_lock); + } + spin_lock_irqsave(&vc4->job_lock, irqflags); if (vc4->hang_state) { spin_unlock_irqrestore(&vc4->job_lock, irqflags); @@ -639,9 +670,6 @@ vc4_queue_submit(struct drm_device *dev, struct vc4_exec_info *exec, * The command validator needs to reference BOs by their index within * the submitted job's BO list. This does the validation of the job's * BO list and reference counting for the lifetime of the job. - * - * Note that this function doesn't need to unreference the BOs on - * failure, because that will happen at vc4_complete_exec() time. */ static int vc4_cl_lookup_bos(struct drm_device *dev, @@ -693,16 +721,47 @@ vc4_cl_lookup_bos(struct drm_device *dev, DRM_DEBUG("Failed to look up GEM BO %d: %d\n", i, handles[i]); ret = -EINVAL; - spin_unlock(&file_priv->table_lock); - goto fail; + break; } + drm_gem_object_get(bo); exec->bo[i] = (struct drm_gem_cma_object *)bo; } spin_unlock(&file_priv->table_lock); + if (ret) + goto fail_put_bo; + + for (i = 0; i < exec->bo_count; i++) { + ret = vc4_bo_inc_usecnt(to_vc4_bo(&exec->bo[i]->base)); + if (ret) + goto fail_dec_usecnt; + } + + kvfree(handles); + return 0; + +fail_dec_usecnt: + /* Decrease usecnt on acquired objects. + * We cannot rely on vc4_complete_exec() to release resources here, + * because vc4_complete_exec() has no information about which BO has + * had its ->usecnt incremented. + * To make things easier we just free everything explicitly and set + * exec->bo to NULL so that vc4_complete_exec() skips the 'BO release' + * step. + */ + for (i-- ; i >= 0; i--) + vc4_bo_dec_usecnt(to_vc4_bo(&exec->bo[i]->base)); + +fail_put_bo: + /* Release any reference to acquired objects. */ + for (i = 0; i < exec->bo_count && exec->bo[i]; i++) + drm_gem_object_put_unlocked(&exec->bo[i]->base); + fail: kvfree(handles); + kvfree(exec->bo); + exec->bo = NULL; return ret; } @@ -833,8 +892,12 @@ vc4_complete_exec(struct drm_device *dev, struct vc4_exec_info *exec) dma_fence_signal(exec->fence); if (exec->bo) { - for (i = 0; i < exec->bo_count; i++) + for (i = 0; i < exec->bo_count; i++) { + struct vc4_bo *bo = to_vc4_bo(&exec->bo[i]->base); + + vc4_bo_dec_usecnt(bo); drm_gem_object_put_unlocked(&exec->bo[i]->base); + } kvfree(exec->bo); } @@ -1098,6 +1161,9 @@ vc4_gem_init(struct drm_device *dev) INIT_WORK(&vc4->job_done_work, vc4_job_done_work); mutex_init(&vc4->power_lock); + + INIT_LIST_HEAD(&vc4->purgeable.list); + mutex_init(&vc4->purgeable.lock); } void @@ -1121,3 +1187,81 @@ vc4_gem_destroy(struct drm_device *dev) if (vc4->hang_state) vc4_free_hang_state(dev, vc4->hang_state); } + +int vc4_gem_madvise_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vc4_gem_madvise *args = data; + struct drm_gem_object *gem_obj; + struct vc4_bo *bo; + int ret; + + switch (args->madv) { + case VC4_MADV_DONTNEED: + case VC4_MADV_WILLNEED: + break; + default: + return -EINVAL; + } + + if (args->pad != 0) + return -EINVAL; + + gem_obj = drm_gem_object_lookup(file_priv, args->handle); + if (!gem_obj) { + DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); + return -ENOENT; + } + + bo = to_vc4_bo(gem_obj); + + /* Only BOs exposed to userspace can be purged. */ + if (bo->madv == __VC4_MADV_NOTSUPP) { + DRM_DEBUG("madvise not supported on this BO\n"); + ret = -EINVAL; + goto out_put_gem; + } + + /* Not sure it's safe to purge imported BOs. Let's just assume it's + * not until proven otherwise. + */ + if (gem_obj->import_attach) { + DRM_DEBUG("madvise not supported on imported BOs\n"); + ret = -EINVAL; + goto out_put_gem; + } + + mutex_lock(&bo->madv_lock); + + if (args->madv == VC4_MADV_DONTNEED && bo->madv == VC4_MADV_WILLNEED && + !refcount_read(&bo->usecnt)) { + /* If the BO is about to be marked as purgeable, is not used + * and is not already purgeable or purged, add it to the + * purgeable list. + */ + vc4_bo_add_to_purgeable_pool(bo); + } else if (args->madv == VC4_MADV_WILLNEED && + bo->madv == VC4_MADV_DONTNEED && + !refcount_read(&bo->usecnt)) { + /* The BO has not been purged yet, just remove it from + * the purgeable list. + */ + vc4_bo_remove_from_purgeable_pool(bo); + } + + /* Save the purged state. */ + args->retained = bo->madv != __VC4_MADV_PURGED; + + /* Update internal madv state only if the bo was not purged. */ + if (bo->madv != __VC4_MADV_PURGED) + bo->madv = args->madv; + + mutex_unlock(&bo->madv_lock); + + ret = 0; + +out_put_gem: + drm_gem_object_put_unlocked(gem_obj); + + return ret; +} diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 3a767a038f72..423a23ed8fc2 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -23,6 +23,7 @@ #include #include +#include "uapi/drm/vc4_drm.h" #include "vc4_drv.h" #include "vc4_regs.h" @@ -774,21 +775,40 @@ static int vc4_prepare_fb(struct drm_plane *plane, { struct vc4_bo *bo; struct dma_fence *fence; + int ret; if ((plane->state->fb == state->fb) || !state->fb) return 0; bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); + + ret = vc4_bo_inc_usecnt(bo); + if (ret) + return ret; + fence = reservation_object_get_excl_rcu(bo->resv); drm_atomic_set_fence_for_plane(state, fence); return 0; } +static void vc4_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct vc4_bo *bo; + + if (plane->state->fb == state->fb || !state->fb) + return; + + bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base); + vc4_bo_dec_usecnt(bo); +} + static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { .atomic_check = vc4_plane_atomic_check, .atomic_update = vc4_plane_atomic_update, .prepare_fb = vc4_prepare_fb, + .cleanup_fb = vc4_cleanup_fb, }; static void vc4_plane_destroy(struct drm_plane *plane) diff --git a/include/uapi/drm/vc4_drm.h b/include/uapi/drm/vc4_drm.h index afae87004963..52263b575bdc 100644 --- a/include/uapi/drm/vc4_drm.h +++ b/include/uapi/drm/vc4_drm.h @@ -41,6 +41,7 @@ extern "C" { #define DRM_VC4_SET_TILING 0x08 #define DRM_VC4_GET_TILING 0x09 #define DRM_VC4_LABEL_BO 0x0a +#define DRM_VC4_GEM_MADVISE 0x0b #define DRM_IOCTL_VC4_SUBMIT_CL DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SUBMIT_CL, struct drm_vc4_submit_cl) #define DRM_IOCTL_VC4_WAIT_SEQNO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_WAIT_SEQNO, struct drm_vc4_wait_seqno) @@ -53,6 +54,7 @@ extern "C" { #define DRM_IOCTL_VC4_SET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_SET_TILING, struct drm_vc4_set_tiling) #define DRM_IOCTL_VC4_GET_TILING DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GET_TILING, struct drm_vc4_get_tiling) #define DRM_IOCTL_VC4_LABEL_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_LABEL_BO, struct drm_vc4_label_bo) +#define DRM_IOCTL_VC4_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_VC4_GEM_MADVISE, struct drm_vc4_gem_madvise) struct drm_vc4_submit_rcl_surface { __u32 hindex; /* Handle index, or ~0 if not present. */ @@ -305,6 +307,7 @@ struct drm_vc4_get_hang_state { #define DRM_VC4_PARAM_SUPPORTS_ETC1 4 #define DRM_VC4_PARAM_SUPPORTS_THREADED_FS 5 #define DRM_VC4_PARAM_SUPPORTS_FIXED_RCL_ORDER 6 +#define DRM_VC4_PARAM_SUPPORTS_MADVISE 7 struct drm_vc4_get_param { __u32 param; @@ -333,6 +336,22 @@ struct drm_vc4_label_bo { __u64 name; }; +/* + * States prefixed with '__' are internal states and cannot be passed to the + * DRM_IOCTL_VC4_GEM_MADVISE ioctl. + */ +#define VC4_MADV_WILLNEED 0 +#define VC4_MADV_DONTNEED 1 +#define __VC4_MADV_PURGED 2 +#define __VC4_MADV_NOTSUPP 3 + +struct drm_vc4_gem_madvise { + __u32 handle; + __u32 madv; + __u32 retained; + __u32 pad; +}; + #if defined(__cplusplus) } #endif -- cgit v1.2.3-59-g8ed1b From 8bc4c256f4995d315eb9cce6e47b4885c79ff661 Mon Sep 17 00:00:00 2001 From: Andres Rodriguez Date: Fri, 13 Oct 2017 14:58:14 -0400 Subject: drm/amdgpu: rename context priority levels Don't leak implementation details about how each priority behaves to usermode. This allows greater flexibility in the future. Squash into c2636dc53abd8269a0930bccd564f2f195dba729 Signed-off-by: Andres Rodriguez Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c | 8 ++++---- include/uapi/drm/amdgpu_drm.h | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c index cd123306eda7..290cc3f9c433 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c @@ -32,14 +32,14 @@ enum amd_sched_priority amdgpu_to_sched_priority(int amdgpu_priority) { switch (amdgpu_priority) { - case AMDGPU_CTX_PRIORITY_HIGH_HW: + case AMDGPU_CTX_PRIORITY_VERY_HIGH: return AMD_SCHED_PRIORITY_HIGH_HW; - case AMDGPU_CTX_PRIORITY_HIGH_SW: + case AMDGPU_CTX_PRIORITY_HIGH: return AMD_SCHED_PRIORITY_HIGH_SW; case AMDGPU_CTX_PRIORITY_NORMAL: return AMD_SCHED_PRIORITY_NORMAL; - case AMDGPU_CTX_PRIORITY_LOW_SW: - case AMDGPU_CTX_PRIORITY_LOW_HW: + case AMDGPU_CTX_PRIORITY_LOW: + case AMDGPU_CTX_PRIORITY_VERY_LOW: return AMD_SCHED_PRIORITY_LOW; case AMDGPU_CTX_PRIORITY_UNSET: return AMD_SCHED_PRIORITY_UNSET; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index a332de1aeb42..d0a3ea69eab6 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -172,12 +172,12 @@ union drm_amdgpu_bo_list { /* Context priority level */ #define AMDGPU_CTX_PRIORITY_UNSET -2048 -#define AMDGPU_CTX_PRIORITY_LOW_HW -1023 -#define AMDGPU_CTX_PRIORITY_LOW_SW -512 +#define AMDGPU_CTX_PRIORITY_VERY_LOW -1023 +#define AMDGPU_CTX_PRIORITY_LOW -512 #define AMDGPU_CTX_PRIORITY_NORMAL 0 /* Selecting a priority above NORMAL requires CAP_SYS_NICE or DRM_MASTER */ -#define AMDGPU_CTX_PRIORITY_HIGH_SW 512 -#define AMDGPU_CTX_PRIORITY_HIGH_HW 1023 +#define AMDGPU_CTX_PRIORITY_HIGH 512 +#define AMDGPU_CTX_PRIORITY_VERY_HIGH 1023 struct drm_amdgpu_ctx_in { /** AMDGPU_CTX_OP_* */ -- cgit v1.2.3-59-g8ed1b From 1f7251b73e08395dbf03602a57ca67cf7da7f9db Mon Sep 17 00:00:00 2001 From: Christian König Date: Mon, 9 Oct 2017 17:53:06 +0200 Subject: drm/amdgpu: add VRAM lost query MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows userspace to figure out if VRAM was lost. Signed-off-by: Christian König Reviewed-by: Nicolai Hähnle Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 3 +++ include/uapi/drm/amdgpu_drm.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index ff1a416a66c9..6f0b26dae3b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -763,6 +763,9 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file } return copy_to_user(out, &ui32, min(size, 4u)) ? -EFAULT : 0; } + case AMDGPU_INFO_VRAM_LOST_COUNTER: + ui32 = atomic_read(&adev->vram_lost_counter); + return copy_to_user(out, &ui32, min(size, 4u)) ? -EFAULT : 0; default: DRM_DEBUG_KMS("Invalid request %d\n", info->query); return -EINVAL; diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index d0a3ea69eab6..ff0181829f3d 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -657,6 +657,7 @@ struct drm_amdgpu_cs_chunk_data { #define AMDGPU_INFO_SENSOR_VDDGFX 0x7 /* Number of VRAM page faults on CPU access. */ #define AMDGPU_INFO_NUM_VRAM_CPU_PAGE_FAULTS 0x1E +#define AMDGPU_INFO_VRAM_LOST_COUNTER 0x1F #define AMDGPU_INFO_MMR_SE_INDEX_SHIFT 0 #define AMDGPU_INFO_MMR_SE_INDEX_MASK 0xff -- cgit v1.2.3-59-g8ed1b From f628ba9e22e0fa32ac6270a0d696fe1509889540 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Mon, 9 Oct 2017 17:16:05 -0500 Subject: gpiolib: drop irq_base field from gpio_chip struct Hence, the last user of irq_base field was removed by commit b4c495f03ae3 ("gpio: mockup: use irq_sim") it can be removed safely. Signed-off-by: Grygorii Strashko Signed-off-by: Linus Walleij --- include/linux/gpio/driver.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index c97f8325e8bf..6bbda879fb8b 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -83,7 +83,6 @@ struct module; * @irqchip: GPIO IRQ chip impl, provided by GPIO driver * @irqdomain: Interrupt translation domain; responsible for mapping * between GPIO hwirq number and linux irq number - * @irq_base: first linux IRQ number assigned to GPIO IRQ chip (deprecated) * @irq_handler: the irq handler to use (often a predefined irq core function) * for GPIO IRQs, provided by GPIO driver * @irq_default_type: default IRQ triggering type applied during GPIO driver @@ -165,7 +164,6 @@ struct gpio_chip { */ struct irq_chip *irqchip; struct irq_domain *irqdomain; - unsigned int irq_base; irq_flow_handler_t irq_handler; unsigned int irq_default_type; unsigned int irq_chained_parent; -- cgit v1.2.3-59-g8ed1b From 5307e2ad69ab3b0e0622fdf8b254c1d4565eb924 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 12 Oct 2017 12:40:10 +0200 Subject: bitops: Introduce assign_bit() A common idiom is to assign a value to a bit with: if (value) set_bit(nr, addr); else clear_bit(nr, addr); Likewise common is the one-line expression variant: value ? set_bit(nr, addr) : clear_bit(nr, addr); Commit 9a8ac3ae682e ("dm mpath: cleanup QUEUE_IF_NO_PATH bit manipulation by introducing assign_bit()") introduced assign_bit() to the md subsystem for brevity. Make it available to others, specifically gpiolib and the upcoming driver for Maxim MAX3191x industrial serializer chips. As requested by Peter Zijlstra, change the argument order to reflect traditional "dst = src" in C, hence "assign_bit(nr, addr, value)". Cc: Bart Van Assche Cc: Alasdair Kergon Cc: Mike Snitzer Cc: Linus Walleij Cc: Neil Brown Cc: Peter Zijlstra Cc: Ingo Molnar Cc: Theodore Ts'o Cc: Borislav Petkov Cc: "H. Peter Anvin" Cc: Denys Vlasenko Acked-by: Andrew Morton Signed-off-by: Lukas Wunner Signed-off-by: Linus Walleij --- drivers/md/dm-mpath.c | 22 +++++++--------------- include/linux/bitops.h | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 11f273d2f018..0e211de9bc54 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -641,14 +641,6 @@ static void process_queued_bios(struct work_struct *work) blk_finish_plug(&plug); } -static void assign_bit(bool value, long nr, unsigned long *addr) -{ - if (value) - set_bit(nr, addr); - else - clear_bit(nr, addr); -} - /* * If we run out of usable paths, should we queue I/O or error it? */ @@ -658,11 +650,11 @@ static int queue_if_no_path(struct multipath *m, bool queue_if_no_path, unsigned long flags; spin_lock_irqsave(&m->lock, flags); - assign_bit((save_old_value && test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) || - (!save_old_value && queue_if_no_path), - MPATHF_SAVED_QUEUE_IF_NO_PATH, &m->flags); - assign_bit(queue_if_no_path || dm_noflush_suspending(m->ti), - MPATHF_QUEUE_IF_NO_PATH, &m->flags); + assign_bit(MPATHF_SAVED_QUEUE_IF_NO_PATH, &m->flags, + (save_old_value && test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags)) || + (!save_old_value && queue_if_no_path)); + assign_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags, + queue_if_no_path || dm_noflush_suspending(m->ti)); spin_unlock_irqrestore(&m->lock, flags); if (!queue_if_no_path) { @@ -1588,8 +1580,8 @@ static void multipath_resume(struct dm_target *ti) unsigned long flags; spin_lock_irqsave(&m->lock, flags); - assign_bit(test_bit(MPATHF_SAVED_QUEUE_IF_NO_PATH, &m->flags), - MPATHF_QUEUE_IF_NO_PATH, &m->flags); + assign_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags, + test_bit(MPATHF_SAVED_QUEUE_IF_NO_PATH, &m->flags)); spin_unlock_irqrestore(&m->lock, flags); } diff --git a/include/linux/bitops.h b/include/linux/bitops.h index 8fbe259b197c..9a874deee6e2 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -227,6 +227,30 @@ static inline unsigned long __ffs64(u64 word) return __ffs((unsigned long)word); } +/** + * assign_bit - Assign value to a bit in memory + * @nr: the bit to set + * @addr: the address to start counting from + * @value: the value to assign + */ +static __always_inline void assign_bit(long nr, volatile unsigned long *addr, + bool value) +{ + if (value) + set_bit(nr, addr); + else + clear_bit(nr, addr); +} + +static __always_inline void __assign_bit(long nr, volatile unsigned long *addr, + bool value) +{ + if (value) + __set_bit(nr, addr); + else + __clear_bit(nr, addr); +} + #ifdef __KERNEL__ #ifndef set_mask_bits -- cgit v1.2.3-59-g8ed1b From eec1d566cdf94b57e8f5ba9fe60eea214929bcfc Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Thu, 12 Oct 2017 12:40:10 +0200 Subject: gpio: Introduce ->get_multiple callback SPI-attached GPIO controllers typically read out all inputs in one go. If callers desire the values of multipe inputs, ideally a single readout should take place to return the desired values. However the current driver API only offers a ->get callback but no ->get_multiple (unlike ->set_multiple, which is present). Thus, to read multiple inputs, a full readout needs to be performed for every single value (barring driver-internal caching), which is inefficient. In fact, the lack of a ->get_multiple callback has been bemoaned repeatedly by the gpio subsystem maintainer: http://www.spinics.net/lists/linux-gpio/msg10571.html http://www.spinics.net/lists/devicetree/msg121734.html Introduce the missing callback. Add corresponding consumer functions such as gpiod_get_array_value(). Amend linehandle_ioctl() to take advantage of the newly added infrastructure. Update the documentation. Cc: Rojhalat Ibrahim Signed-off-by: Lukas Wunner Signed-off-by: Linus Walleij --- Documentation/gpio/consumer.txt | 41 ++++++--- drivers/gpio/gpiolib.c | 179 +++++++++++++++++++++++++++++++++++++--- drivers/gpio/gpiolib.h | 4 + include/linux/gpio/consumer.h | 43 ++++++++++ include/linux/gpio/driver.h | 5 ++ 5 files changed, 250 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/Documentation/gpio/consumer.txt b/Documentation/gpio/consumer.txt index ddbfa775a78a..63e1bd1d88e3 100644 --- a/Documentation/gpio/consumer.txt +++ b/Documentation/gpio/consumer.txt @@ -295,9 +295,22 @@ as possible, especially by drivers which should not care about the actual physical line level and worry about the logical value instead. -Set multiple GPIO outputs with a single function call ------------------------------------------------------ -The following functions set the output values of an array of GPIOs: +Access multiple GPIOs with a single function call +------------------------------------------------- +The following functions get or set the values of an array of GPIOs: + + int gpiod_get_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); + int gpiod_get_raw_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); + int gpiod_get_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); + int gpiod_get_raw_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); void gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, @@ -312,34 +325,40 @@ The following functions set the output values of an array of GPIOs: struct gpio_desc **desc_array, int *value_array) -The array can be an arbitrary set of GPIOs. The functions will try to set +The array can be an arbitrary set of GPIOs. The functions will try to access GPIOs belonging to the same bank or chip simultaneously if supported by the corresponding chip driver. In that case a significantly improved performance -can be expected. If simultaneous setting is not possible the GPIOs will be set -sequentially. +can be expected. If simultaneous access is not possible the GPIOs will be +accessed sequentially. -The gpiod_set_array() functions take three arguments: +The functions take three arguments: * array_size - the number of array elements * desc_array - an array of GPIO descriptors - * value_array - an array of values to assign to the GPIOs + * value_array - an array to store the GPIOs' values (get) or + an array of values to assign to the GPIOs (set) The descriptor array can be obtained using the gpiod_get_array() function or one of its variants. If the group of descriptors returned by that function -matches the desired group of GPIOs, those GPIOs can be set by simply using +matches the desired group of GPIOs, those GPIOs can be accessed by simply using the struct gpio_descs returned by gpiod_get_array(): struct gpio_descs *my_gpio_descs = gpiod_get_array(...); gpiod_set_array_value(my_gpio_descs->ndescs, my_gpio_descs->desc, my_gpio_values); -It is also possible to set a completely arbitrary array of descriptors. The +It is also possible to access a completely arbitrary array of descriptors. The descriptors may be obtained using any combination of gpiod_get() and gpiod_get_array(). Afterwards the array of descriptors has to be setup -manually before it can be used with gpiod_set_array(). +manually before it can be passed to one of the above functions. Note that for optimal performance GPIOs belonging to the same chip should be contiguous within the array of descriptors. +The return value of gpiod_get_array_value() and its variants is 0 on success +or negative on error. Note the difference to gpiod_get_value(), which returns +0 or 1 on success to convey the GPIO value. With the array functions, the GPIO +values are stored in value_array rather than passed back as return value. + GPIOs mapped to IRQs -------------------- diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8113929eb2bd..ff46a1b6299e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -365,28 +365,28 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, struct linehandle_state *lh = filep->private_data; void __user *ip = (void __user *)arg; struct gpiohandle_data ghd; + int vals[GPIOHANDLES_MAX]; int i; if (cmd == GPIOHANDLE_GET_LINE_VALUES_IOCTL) { - int val; + /* TODO: check if descriptors are really input */ + int ret = gpiod_get_array_value_complex(false, + true, + lh->numdescs, + lh->descs, + vals); + if (ret) + return ret; memset(&ghd, 0, sizeof(ghd)); - - /* TODO: check if descriptors are really input */ - for (i = 0; i < lh->numdescs; i++) { - val = gpiod_get_value_cansleep(lh->descs[i]); - if (val < 0) - return val; - ghd.values[i] = val; - } + for (i = 0; i < lh->numdescs; i++) + ghd.values[i] = vals[i]; if (copy_to_user(ip, &ghd, sizeof(ghd))) return -EFAULT; return 0; } else if (cmd == GPIOHANDLE_SET_LINE_VALUES_IOCTL) { - int vals[GPIOHANDLES_MAX]; - /* TODO: check if descriptors are really output */ if (copy_from_user(&ghd, ip, sizeof(ghd))) return -EFAULT; @@ -2466,6 +2466,71 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) return value; } +static int gpio_chip_get_multiple(struct gpio_chip *chip, + unsigned long *mask, unsigned long *bits) +{ + if (chip->get_multiple) { + return chip->get_multiple(chip, mask, bits); + } else if (chip->get) { + int i, value; + + for_each_set_bit(i, mask, chip->ngpio) { + value = chip->get(chip, i); + if (value < 0) + return value; + __assign_bit(i, bits, value); + } + return 0; + } + return -EIO; +} + +int gpiod_get_array_value_complex(bool raw, bool can_sleep, + unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) +{ + int i = 0; + + while (i < array_size) { + struct gpio_chip *chip = desc_array[i]->gdev->chip; + unsigned long mask[BITS_TO_LONGS(chip->ngpio)]; + unsigned long bits[BITS_TO_LONGS(chip->ngpio)]; + int first, j, ret; + + if (!can_sleep) + WARN_ON(chip->can_sleep); + + /* collect all inputs belonging to the same chip */ + first = i; + memset(mask, 0, sizeof(mask)); + do { + const struct gpio_desc *desc = desc_array[i]; + int hwgpio = gpio_chip_hwgpio(desc); + + __set_bit(hwgpio, mask); + i++; + } while ((i < array_size) && + (desc_array[i]->gdev->chip == chip)); + + ret = gpio_chip_get_multiple(chip, mask, bits); + if (ret) + return ret; + + for (j = first; j < i; j++) { + const struct gpio_desc *desc = desc_array[j]; + int hwgpio = gpio_chip_hwgpio(desc); + int value = test_bit(hwgpio, bits); + + if (!raw && test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + value = !value; + value_array[j] = value; + trace_gpio_value(desc_to_gpio(desc), 1, value); + } + } + return 0; +} + /** * gpiod_get_raw_value() - return a gpio's raw value * @desc: gpio whose value will be returned @@ -2514,6 +2579,51 @@ int gpiod_get_value(const struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_get_value); +/** + * gpiod_get_raw_array_value() - read raw values from an array of GPIOs + * @array_size: number of elements in the descriptor / value arrays + * @desc_array: array of GPIO descriptors whose values will be read + * @value_array: array to store the read values + * + * Read the raw values of the GPIOs, i.e. the values of the physical lines + * without regard for their ACTIVE_LOW status. Return 0 in case of success, + * else an error code. + * + * This function should be called from contexts where we cannot sleep, + * and it will complain if the GPIO chip functions potentially sleep. + */ +int gpiod_get_raw_array_value(unsigned int array_size, + struct gpio_desc **desc_array, int *value_array) +{ + if (!desc_array) + return -EINVAL; + return gpiod_get_array_value_complex(true, false, array_size, + desc_array, value_array); +} +EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value); + +/** + * gpiod_get_array_value() - read values from an array of GPIOs + * @array_size: number of elements in the descriptor / value arrays + * @desc_array: array of GPIO descriptors whose values will be read + * @value_array: array to store the read values + * + * Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status + * into account. Return 0 in case of success, else an error code. + * + * This function should be called from contexts where we cannot sleep, + * and it will complain if the GPIO chip functions potentially sleep. + */ +int gpiod_get_array_value(unsigned int array_size, + struct gpio_desc **desc_array, int *value_array) +{ + if (!desc_array) + return -EINVAL; + return gpiod_get_array_value_complex(false, false, array_size, + desc_array, value_array); +} +EXPORT_SYMBOL_GPL(gpiod_get_array_value); + /* * gpio_set_open_drain_value_commit() - Set the open drain gpio's value. * @desc: gpio descriptor whose state need to be set. @@ -2942,6 +3052,53 @@ int gpiod_get_value_cansleep(const struct gpio_desc *desc) } EXPORT_SYMBOL_GPL(gpiod_get_value_cansleep); +/** + * gpiod_get_raw_array_value_cansleep() - read raw values from an array of GPIOs + * @array_size: number of elements in the descriptor / value arrays + * @desc_array: array of GPIO descriptors whose values will be read + * @value_array: array to store the read values + * + * Read the raw values of the GPIOs, i.e. the values of the physical lines + * without regard for their ACTIVE_LOW status. Return 0 in case of success, + * else an error code. + * + * This function is to be called from contexts that can sleep. + */ +int gpiod_get_raw_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) +{ + might_sleep_if(extra_checks); + if (!desc_array) + return -EINVAL; + return gpiod_get_array_value_complex(true, true, array_size, + desc_array, value_array); +} +EXPORT_SYMBOL_GPL(gpiod_get_raw_array_value_cansleep); + +/** + * gpiod_get_array_value_cansleep() - read values from an array of GPIOs + * @array_size: number of elements in the descriptor / value arrays + * @desc_array: array of GPIO descriptors whose values will be read + * @value_array: array to store the read values + * + * Read the logical values of the GPIOs, i.e. taking their ACTIVE_LOW status + * into account. Return 0 in case of success, else an error code. + * + * This function is to be called from contexts that can sleep. + */ +int gpiod_get_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) +{ + might_sleep_if(extra_checks); + if (!desc_array) + return -EINVAL; + return gpiod_get_array_value_complex(false, true, array_size, + desc_array, value_array); +} +EXPORT_SYMBOL_GPL(gpiod_get_array_value_cansleep); + /** * gpiod_set_raw_value_cansleep() - assign a gpio's raw value * @desc: gpio whose value will be assigned diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index d003ccb12781..10a48caf8477 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -180,6 +180,10 @@ static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev, #endif struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip, u16 hwnum); +int gpiod_get_array_value_complex(bool raw, bool can_sleep, + unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); void gpiod_set_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 8f702fcbe485..d4920ec1f1da 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -99,10 +99,15 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value); /* Value get/set from non-sleeping context */ int gpiod_get_value(const struct gpio_desc *desc); +int gpiod_get_array_value(unsigned int array_size, + struct gpio_desc **desc_array, int *value_array); void gpiod_set_value(struct gpio_desc *desc, int value); void gpiod_set_array_value(unsigned int array_size, struct gpio_desc **desc_array, int *value_array); int gpiod_get_raw_value(const struct gpio_desc *desc); +int gpiod_get_raw_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); void gpiod_set_raw_value(struct gpio_desc *desc, int value); void gpiod_set_raw_array_value(unsigned int array_size, struct gpio_desc **desc_array, @@ -110,11 +115,17 @@ void gpiod_set_raw_array_value(unsigned int array_size, /* Value get/set from sleeping context */ int gpiod_get_value_cansleep(const struct gpio_desc *desc); +int gpiod_get_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); void gpiod_set_value_cansleep(struct gpio_desc *desc, int value); void gpiod_set_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, int *value_array); int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc); +int gpiod_get_raw_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array); void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value); void gpiod_set_raw_array_value_cansleep(unsigned int array_size, struct gpio_desc **desc_array, @@ -305,6 +316,14 @@ static inline int gpiod_get_value(const struct gpio_desc *desc) WARN_ON(1); return 0; } +static inline int gpiod_get_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return 0; +} static inline void gpiod_set_value(struct gpio_desc *desc, int value) { /* GPIO can never have been requested */ @@ -323,6 +342,14 @@ static inline int gpiod_get_raw_value(const struct gpio_desc *desc) WARN_ON(1); return 0; } +static inline int gpiod_get_raw_array_value(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return 0; +} static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value) { /* GPIO can never have been requested */ @@ -342,6 +369,14 @@ static inline int gpiod_get_value_cansleep(const struct gpio_desc *desc) WARN_ON(1); return 0; } +static inline int gpiod_get_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return 0; +} static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value) { /* GPIO can never have been requested */ @@ -360,6 +395,14 @@ static inline int gpiod_get_raw_value_cansleep(const struct gpio_desc *desc) WARN_ON(1); return 0; } +static inline int gpiod_get_raw_array_value_cansleep(unsigned int array_size, + struct gpio_desc **desc_array, + int *value_array) +{ + /* GPIO can never have been requested */ + WARN_ON(1); + return 0; +} static inline void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value) { diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 6bbda879fb8b..bda95a9b7b8c 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -35,6 +35,8 @@ struct module; * @direction_input: configures signal "offset" as input, or returns error * @direction_output: configures signal "offset" as output, or returns error * @get: returns value for signal "offset", 0=low, 1=high, or negative error + * @get_multiple: reads values for multiple signals defined by "mask" and + * stores them in "bits", returns 0 on success or negative error * @set: assigns output value for signal "offset" * @set_multiple: assigns output values for multiple signals defined by "mask" * @set_config: optional hook for all kinds of settings. Uses the same @@ -125,6 +127,9 @@ struct gpio_chip { unsigned offset, int value); int (*get)(struct gpio_chip *chip, unsigned offset); + int (*get_multiple)(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits); void (*set)(struct gpio_chip *chip, unsigned offset, int value); void (*set_multiple)(struct gpio_chip *chip, -- cgit v1.2.3-59-g8ed1b From 7c950b9e53732f574e3a46d37c62f1f33d0b218c Mon Sep 17 00:00:00 2001 From: Dongdong Liu Date: Wed, 11 Oct 2017 18:52:58 +0800 Subject: PCI/portdrv: Add #defines for AER and DPC Interrupt Message Number masks In the AER case, the mask isn't strictly necessary because there are no higher-order bits above the Interrupt Message Number, but using a #define will make it possible to grep for it. Suggested-by: Bjorn Helgaas Signed-off-by: Dongdong Liu Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig --- drivers/pci/pcie/portdrv_core.c | 4 ++-- include/uapi/linux/pci_regs.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 313a21df1692..72fcbe5567dd 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -114,7 +114,7 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) */ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32); - entry = reg32 >> 27; + entry = (reg32 & PCI_ERR_ROOT_AER_IRQ) >> 27; if (entry >= nr_entries) goto out_free_irqs; @@ -141,7 +141,7 @@ static int pcie_port_enable_irq_vec(struct pci_dev *dev, int *irqs, int mask) */ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DPC); pci_read_config_word(dev, pos + PCI_EXP_DPC_CAP, ®16); - entry = reg16 & 0x1f; + entry = reg16 & PCI_EXP_DPC_IRQ; if (entry >= nr_entries) goto out_free_irqs; diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index f8d58045926f..f7c09a4c494a 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -746,6 +746,7 @@ #define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First UNC is Fatal */ #define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ #define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ +#define PCI_ERR_ROOT_AER_IRQ 0xf8000000 /* Advanced Error Interrupt Message Number */ #define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */ /* Virtual Channel */ @@ -960,6 +961,7 @@ /* Downstream Port Containment */ #define PCI_EXP_DPC_CAP 4 /* DPC Capability */ +#define PCI_EXP_DPC_IRQ 0x1f /* DPC Interrupt Message Number */ #define PCI_EXP_DPC_CAP_RP_EXT 0x20 /* Root Port Extensions for DPC */ #define PCI_EXP_DPC_CAP_POISONED_TLP 0x40 /* Poisoned TLP Egress Blocking Supported */ #define PCI_EXP_DPC_CAP_SW_TRIGGER 0x80 /* Software Triggering Supported */ -- cgit v1.2.3-59-g8ed1b From c5053e695d621f8a8a3992c5e93dd3be088fa267 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 19 Oct 2017 16:19:03 -0700 Subject: Input: gpio_mouse - kill off platform data This is not used much: git grep gpio_mouse_platform_data shows that absolutely nothing in the kernel defines this platform data. It could be argued that the driver should be deleted. But that is a bit harsh I think since it seems generally useful. So this patch starts a series which repurposes it to be used with hardware nodes from device tree or ACPI. This first patch simply localize the platform data header and allocates a dummy platform data. Yes: this patch leaves the driver in a pretty useless state, but since nothing is instantiating this driver, it doesn't make it more useless than it already is. Later patches makes use of the driver. Acked-by: Hans-Christian Noren Egtvedt Signed-off-by: Linus Walleij Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/gpio_mouse.c | 59 +++++++++++++++++++++++++++++++++----- include/linux/gpio_mouse.h | 61 ---------------------------------------- 2 files changed, 52 insertions(+), 68 deletions(-) delete mode 100644 include/linux/gpio_mouse.h (limited to 'include') diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index ced07391304b..dcaba1e4fffb 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -12,8 +12,54 @@ #include #include #include -#include +#define GPIO_MOUSE_POLARITY_ACT_HIGH 0x00 +#define GPIO_MOUSE_POLARITY_ACT_LOW 0x01 + +#define GPIO_MOUSE_PIN_UP 0 +#define GPIO_MOUSE_PIN_DOWN 1 +#define GPIO_MOUSE_PIN_LEFT 2 +#define GPIO_MOUSE_PIN_RIGHT 3 +#define GPIO_MOUSE_PIN_BLEFT 4 +#define GPIO_MOUSE_PIN_BMIDDLE 5 +#define GPIO_MOUSE_PIN_BRIGHT 6 +#define GPIO_MOUSE_PIN_MAX 7 + +/** + * struct gpio_mouse_platform_data + * @scan_ms: integer in ms specifying the scan periode. + * @polarity: Pin polarity, active high or low. + * @up: GPIO line for up value. + * @down: GPIO line for down value. + * @left: GPIO line for left value. + * @right: GPIO line for right value. + * @bleft: GPIO line for left button. + * @bmiddle: GPIO line for middle button. + * @bright: GPIO line for right button. + * @pins: GPIO line numbers used for the mouse. + * + * This struct must be added to the platform_device in the board code. + * It is used by the gpio_mouse driver to setup GPIO lines and to + * calculate mouse movement. + */ +struct gpio_mouse_platform_data { + int scan_ms; + int polarity; + + union { + struct { + int up; + int down; + int left; + int right; + + int bleft; + int bmiddle; + int bright; + }; + int pins[GPIO_MOUSE_PIN_MAX]; + }; +}; /* * Timer function which is run every scan_ms ms when the device is opened. @@ -47,17 +93,16 @@ static void gpio_mouse_scan(struct input_polled_dev *dev) static int gpio_mouse_probe(struct platform_device *pdev) { - struct gpio_mouse_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; + struct gpio_mouse_platform_data *pdata; struct input_polled_dev *input_poll; struct input_dev *input; int pin, i; int error; - if (!pdata) { - dev_err(&pdev->dev, "no platform data\n"); - error = -ENXIO; - goto out; - } + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; if (pdata->scan_ms < 0) { dev_err(&pdev->dev, "invalid scan time\n"); diff --git a/include/linux/gpio_mouse.h b/include/linux/gpio_mouse.h deleted file mode 100644 index 44ed7aa14d85..000000000000 --- a/include/linux/gpio_mouse.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Driver for simulating a mouse on GPIO lines. - * - * Copyright (C) 2007 Atmel 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. - */ - -#ifndef _GPIO_MOUSE_H -#define _GPIO_MOUSE_H - -#define GPIO_MOUSE_POLARITY_ACT_HIGH 0x00 -#define GPIO_MOUSE_POLARITY_ACT_LOW 0x01 - -#define GPIO_MOUSE_PIN_UP 0 -#define GPIO_MOUSE_PIN_DOWN 1 -#define GPIO_MOUSE_PIN_LEFT 2 -#define GPIO_MOUSE_PIN_RIGHT 3 -#define GPIO_MOUSE_PIN_BLEFT 4 -#define GPIO_MOUSE_PIN_BMIDDLE 5 -#define GPIO_MOUSE_PIN_BRIGHT 6 -#define GPIO_MOUSE_PIN_MAX 7 - -/** - * struct gpio_mouse_platform_data - * @scan_ms: integer in ms specifying the scan periode. - * @polarity: Pin polarity, active high or low. - * @up: GPIO line for up value. - * @down: GPIO line for down value. - * @left: GPIO line for left value. - * @right: GPIO line for right value. - * @bleft: GPIO line for left button. - * @bmiddle: GPIO line for middle button. - * @bright: GPIO line for right button. - * - * This struct must be added to the platform_device in the board code. - * It is used by the gpio_mouse driver to setup GPIO lines and to - * calculate mouse movement. - */ -struct gpio_mouse_platform_data { - int scan_ms; - int polarity; - - union { - struct { - int up; - int down; - int left; - int right; - - int bleft; - int bmiddle; - int bright; - }; - int pins[GPIO_MOUSE_PIN_MAX]; - }; -}; - -#endif /* _GPIO_MOUSE_H */ -- cgit v1.2.3-59-g8ed1b From 2cbfca66ba5e00606bb0f24aba1e9cd8efe58849 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 20 Oct 2017 13:27:58 +1030 Subject: gpio: Fix loose spelling Literally. I expect "lose" was meant here, rather than "loose", though you could feasibly use a somewhat uncommon definition of "loose" to mean what would be meant by "lose": "Loose the hounds" for instance, as in "Release the hounds". Substituting in "value" for "hounds" gives "release the value", and makes some sense, but futher substituting back to loose gives "loose the value" which overall just seems a bit anachronistic. Instead, use modern, pragmatic English and save a character. Cc: Russell Currey Signed-off-by: Andrew Jeffery Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-of.c | 4 ++-- drivers/gpio/gpiolib.c | 6 +++--- drivers/gpio/gpiolib.h | 2 +- include/dt-bindings/gpio/gpio.h | 2 +- include/linux/gpio/machine.h | 2 +- include/linux/of_gpio.h | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index bfcd20699ec8..e0d59e61b52f 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -153,8 +153,8 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, *flags |= GPIO_OPEN_SOURCE; } - if (of_flags & OF_GPIO_SLEEP_MAY_LOOSE_VALUE) - *flags |= GPIO_SLEEP_MAY_LOOSE_VALUE; + if (of_flags & OF_GPIO_SLEEP_MAY_LOSE_VALUE) + *flags |= GPIO_SLEEP_MAY_LOSE_VALUE; return desc; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e7372093d968..3827f0767101 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3010,7 +3010,7 @@ bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset) if (offset >= chip->ngpio) return false; - return !test_bit(FLAG_SLEEP_MAY_LOOSE_VALUE, + return !test_bit(FLAG_SLEEP_MAY_LOSE_VALUE, &chip->gpiodev->descs[offset].flags); } EXPORT_SYMBOL_GPL(gpiochip_line_is_persistent); @@ -3435,8 +3435,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, set_bit(FLAG_OPEN_DRAIN, &desc->flags); if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); - if (lflags & GPIO_SLEEP_MAY_LOOSE_VALUE) - set_bit(FLAG_SLEEP_MAY_LOOSE_VALUE, &desc->flags); + if (lflags & GPIO_SLEEP_MAY_LOSE_VALUE) + set_bit(FLAG_SLEEP_MAY_LOSE_VALUE, &desc->flags); /* No particular flag request, return here... */ if (!(dflags & GPIOD_FLAGS_BIT_DIR_SET)) { diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 10a48caf8477..af48322839c3 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -205,7 +205,7 @@ struct gpio_desc { #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ #define FLAG_IS_HOGGED 11 /* GPIO is hogged */ -#define FLAG_SLEEP_MAY_LOOSE_VALUE 12 /* GPIO may loose value in sleep */ +#define FLAG_SLEEP_MAY_LOSE_VALUE 12 /* GPIO may lose value in sleep */ /* Connection label */ const char *label; diff --git a/include/dt-bindings/gpio/gpio.h b/include/dt-bindings/gpio/gpio.h index c5074584561d..70de5b7a6c9b 100644 --- a/include/dt-bindings/gpio/gpio.h +++ b/include/dt-bindings/gpio/gpio.h @@ -30,6 +30,6 @@ /* Bit 3 express GPIO suspend/resume persistence */ #define GPIO_SLEEP_MAINTAIN_VALUE 0 -#define GPIO_SLEEP_MAY_LOOSE_VALUE 8 +#define GPIO_SLEEP_MAY_LOSE_VALUE 8 #endif diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index ba4ccfd900f9..5e9f294c29eb 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -10,7 +10,7 @@ enum gpio_lookup_flags { GPIO_OPEN_DRAIN = (1 << 1), GPIO_OPEN_SOURCE = (1 << 2), GPIO_SLEEP_MAINTAIN_VALUE = (0 << 3), - GPIO_SLEEP_MAY_LOOSE_VALUE = (1 << 3), + GPIO_SLEEP_MAY_LOSE_VALUE = (1 << 3), }; /** diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index ca10f43564de..1fe205582111 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -31,7 +31,7 @@ enum of_gpio_flags { OF_GPIO_ACTIVE_LOW = 0x1, OF_GPIO_SINGLE_ENDED = 0x2, OF_GPIO_OPEN_DRAIN = 0x4, - OF_GPIO_SLEEP_MAY_LOOSE_VALUE = 0x8, + OF_GPIO_SLEEP_MAY_LOSE_VALUE = 0x8, }; #ifdef CONFIG_OF_GPIO -- cgit v1.2.3-59-g8ed1b From 4cf8f811c64ab570d40cb2f4bfc723750aec122f Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 16 Oct 2017 17:34:44 +0200 Subject: clk: meson: gxbb: Add VPU and VAPB clockids Add the clkids for the clocks feeding the Video Processing Unit. Acked-by: Jerome Brunet Signed-off-by: Neil Armstrong --- drivers/clk/meson/gxbb.h | 6 +++++- include/dt-bindings/clock/gxbb-clkc.h | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/clk/meson/gxbb.h b/drivers/clk/meson/gxbb.h index 5b1d4b374d1c..aee6fbba2004 100644 --- a/drivers/clk/meson/gxbb.h +++ b/drivers/clk/meson/gxbb.h @@ -190,8 +190,12 @@ #define CLKID_SD_EMMC_B_CLK0_DIV 121 #define CLKID_SD_EMMC_C_CLK0_SEL 123 #define CLKID_SD_EMMC_C_CLK0_DIV 124 +#define CLKID_VPU_0_DIV 127 +#define CLKID_VPU_1_DIV 130 +#define CLKID_VAPB_0_DIV 134 +#define CLKID_VAPB_1_DIV 137 -#define NR_CLKS 126 +#define NR_CLKS 141 /* include the CLKIDs that have been made part of the DT binding */ #include diff --git a/include/dt-bindings/clock/gxbb-clkc.h b/include/dt-bindings/clock/gxbb-clkc.h index c04a76d8facf..7c5a6f3825a5 100644 --- a/include/dt-bindings/clock/gxbb-clkc.h +++ b/include/dt-bindings/clock/gxbb-clkc.h @@ -113,5 +113,16 @@ #define CLKID_SD_EMMC_A_CLK0 119 #define CLKID_SD_EMMC_B_CLK0 122 #define CLKID_SD_EMMC_C_CLK0 125 +#define CLKID_VPU_0_SEL 126 +#define CLKID_VPU_0 128 +#define CLKID_VPU_1_SEL 129 +#define CLKID_VPU_1 131 +#define CLKID_VPU 132 +#define CLKID_VAPB_0_SEL 133 +#define CLKID_VAPB_0 135 +#define CLKID_VAPB_1_SEL 136 +#define CLKID_VAPB_1 138 +#define CLKID_VAPB_SEL 139 +#define CLKID_VAPB 140 #endif /* __GXBB_CLKC_H */ -- cgit v1.2.3-59-g8ed1b From 890056783c60ad9d0789774af2bc10fe4f27dd9d Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 18 Oct 2017 08:17:29 -0700 Subject: tcp: Remove use of inet6_sk and add IPv6 checks to tracepoint 386fd5da401d ("tcp: Check daddr_cache before use in tracepoint") was the second version of the tracepoint fixup patch. This patch is the delta between v2 and v3. Specifically, remove the use of inet6_sk and check sk_family as requested by Eric and add IS_ENABLED(CONFIG_IPV6) around the use of sk_v6_rcv_saddr and sk_v6_daddr as done in sock_common (noted by Cong). Signed-off-by: David Ahern Reviewed-by: Eric Dumazet Tested-by: Song Liu Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index f51c130f1e0f..c3220d914475 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -43,13 +43,15 @@ TRACE_EVENT(tcp_retransmit_skb, p32 = (__be32 *) __entry->daddr; *p32 = inet->inet_daddr; - /* IPv6 socket ? */ - if (inet6_sk(sk)) { +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family == AF_INET6) { pin6 = (struct in6_addr *)__entry->saddr_v6; *pin6 = sk->sk_v6_rcv_saddr; pin6 = (struct in6_addr *)__entry->daddr_v6; *pin6 = sk->sk_v6_daddr; - } else { + } else +#endif + { pin6 = (struct in6_addr *)__entry->saddr_v6; ipv6_addr_set_v4mapped(inet->inet_saddr, pin6); pin6 = (struct in6_addr *)__entry->daddr_v6; -- cgit v1.2.3-59-g8ed1b From de95e04791a03de5cb681980a3880db6919e3b4a Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 18 Oct 2017 09:56:54 -0700 Subject: net: Add extack to validator_info structs used for address notifier Add extack to in_validator_info and in6_validator_info. Update the one user of each, ipvlan, to return an error message for failures. Only manual configuration of an address is plumbed in the IPv6 code path. Signed-off-by: David Ahern Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ipvlan/ipvlan_main.c | 10 ++++++++-- include/linux/inetdevice.h | 1 + include/net/addrconf.h | 1 + net/ipv4/devinet.c | 8 +++++--- net/ipv6/addrconf.c | 22 ++++++++++++---------- 5 files changed, 27 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 6842739b6679..f0ab55df57f1 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -847,8 +847,11 @@ static int ipvlan_addr6_validator_event(struct notifier_block *unused, switch (event) { case NETDEV_UP: - if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true)) + if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true)) { + NL_SET_ERR_MSG(i6vi->extack, + "Address already assigned to an ipvlan device"); return notifier_from_errno(-EADDRINUSE); + } break; } @@ -917,8 +920,11 @@ static int ipvlan_addr4_validator_event(struct notifier_block *unused, switch (event) { case NETDEV_UP: - if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false)) + if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false)) { + NL_SET_ERR_MSG(ivi->extack, + "Address already assigned to an ipvlan device"); return notifier_from_errno(-EADDRINUSE); + } break; } diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 751d051f0bc7..681dff30940b 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -154,6 +154,7 @@ struct in_ifaddr { struct in_validator_info { __be32 ivi_addr; struct in_device *ivi_dev; + struct netlink_ext_ack *extack; }; int register_inetaddr_notifier(struct notifier_block *nb); diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 87981cd63180..b8b16437c6d5 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -55,6 +55,7 @@ struct prefix_info { struct in6_validator_info { struct in6_addr i6vi_addr; struct inet6_dev *i6vi_dev; + struct netlink_ext_ack *extack; }; #define IN6_ADDR_HSIZE_SHIFT 4 diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index e1e2ec0525e6..a4573bccd6da 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -444,7 +444,7 @@ static void check_lifetime(struct work_struct *work); static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, - u32 portid) + u32 portid, struct netlink_ext_ack *extack) { struct in_device *in_dev = ifa->ifa_dev; struct in_ifaddr *ifa1, **ifap, **last_primary; @@ -489,6 +489,7 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, */ ivi.ivi_addr = ifa->ifa_address; ivi.ivi_dev = ifa->ifa_dev; + ivi.extack = extack; ret = blocking_notifier_call_chain(&inetaddr_validator_chain, NETDEV_UP, &ivi); ret = notifier_to_errno(ret); @@ -521,7 +522,7 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, static int inet_insert_ifa(struct in_ifaddr *ifa) { - return __inet_insert_ifa(ifa, NULL, 0); + return __inet_insert_ifa(ifa, NULL, 0, NULL); } static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) @@ -902,7 +903,8 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, return ret; } } - return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); + return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid, + extack); } else { inet_free_ifa(ifa); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index dd9c0c435f71..93f9c0a61911 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -987,7 +987,7 @@ static struct inet6_ifaddr * ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, const struct in6_addr *peer_addr, int pfxlen, int scope, u32 flags, u32 valid_lft, u32 prefered_lft, - bool can_block) + bool can_block, struct netlink_ext_ack *extack) { gfp_t gfp_flags = can_block ? GFP_KERNEL : GFP_ATOMIC; struct net *net = dev_net(idev->dev); @@ -1019,6 +1019,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, struct in6_validator_info i6vi = { .i6vi_addr = *addr, .i6vi_dev = idev, + .extack = extack, }; err = inet6addr_validator_notifier_call_chain(NETDEV_UP, &i6vi); @@ -1356,7 +1357,7 @@ retry: ift = ipv6_add_addr(idev, &addr, NULL, tmp_plen, ipv6_addr_scope(&addr), addr_flags, - tmp_valid_lft, tmp_prefered_lft, true); + tmp_valid_lft, tmp_prefered_lft, true, NULL); if (IS_ERR(ift)) { in6_ifa_put(ifp); in6_dev_put(idev); @@ -2040,7 +2041,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) ifp2 = ipv6_add_addr(idev, &new_addr, NULL, pfxlen, scope, flags, valid_lft, - preferred_lft, false); + preferred_lft, false, NULL); if (IS_ERR(ifp2)) goto lock_errdad; @@ -2498,7 +2499,7 @@ int addrconf_prefix_rcv_add_addr(struct net *net, struct net_device *dev, pinfo->prefix_len, addr_type&IPV6_ADDR_SCOPE_MASK, addr_flags, valid_lft, - prefered_lft, false); + prefered_lft, false, NULL); if (IS_ERR_OR_NULL(ifp)) return -1; @@ -2808,7 +2809,8 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx, const struct in6_addr *peer_pfx, unsigned int plen, __u32 ifa_flags, - __u32 prefered_lft, __u32 valid_lft) + __u32 prefered_lft, __u32 valid_lft, + struct netlink_ext_ack *extack) { struct inet6_ifaddr *ifp; struct inet6_dev *idev; @@ -2867,7 +2869,7 @@ static int inet6_addr_add(struct net *net, int ifindex, } ifp = ipv6_add_addr(idev, pfx, peer_pfx, plen, scope, ifa_flags, - valid_lft, prefered_lft, true); + valid_lft, prefered_lft, true, extack); if (!IS_ERR(ifp)) { if (!(ifa_flags & IFA_F_NOPREFIXROUTE)) { @@ -2952,7 +2954,7 @@ int addrconf_add_ifaddr(struct net *net, void __user *arg) rtnl_lock(); err = inet6_addr_add(net, ireq.ifr6_ifindex, &ireq.ifr6_addr, NULL, ireq.ifr6_prefixlen, IFA_F_PERMANENT, - INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); + INFINITY_LIFE_TIME, INFINITY_LIFE_TIME, NULL); rtnl_unlock(); return err; } @@ -2983,7 +2985,7 @@ static void add_addr(struct inet6_dev *idev, const struct in6_addr *addr, ifp = ipv6_add_addr(idev, addr, NULL, plen, scope, IFA_F_PERMANENT, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME, - true); + true, NULL); if (!IS_ERR(ifp)) { spin_lock_bh(&ifp->lock); ifp->flags &= ~IFA_F_TENTATIVE; @@ -3083,7 +3085,7 @@ void addrconf_add_linklocal(struct inet6_dev *idev, #endif ifp = ipv6_add_addr(idev, addr, NULL, 64, IFA_LINK, addr_flags, - INFINITY_LIFE_TIME, INFINITY_LIFE_TIME, true); + INFINITY_LIFE_TIME, INFINITY_LIFE_TIME, true, NULL); if (!IS_ERR(ifp)) { addrconf_prefix_route(&ifp->addr, ifp->prefix_len, idev->dev, 0, 0); addrconf_dad_start(ifp); @@ -4586,7 +4588,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, */ return inet6_addr_add(net, ifm->ifa_index, pfx, peer_pfx, ifm->ifa_prefixlen, ifa_flags, - preferred_lft, valid_lft); + preferred_lft, valid_lft, extack); } if (nlh->nlmsg_flags & NLM_F_EXCL || -- cgit v1.2.3-59-g8ed1b From 617dd7cc490b72345277e2666c8ed34d4f47f0da Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 30 Aug 2017 12:48:31 +0200 Subject: gpu: host1x: syncpt: Request syncpoints per client Rather than request syncpoints for a struct device *, request them for a struct host1x_client *. This is important because subsequent patches are going to break the assumption that host1x will always be the parent for devices requesting a syncpoint. It's also a more natural choice because host1x clients are really the only ones that will know how to deal with syncpoints. Note that host1x clients are always guaranteed to be children of host1x, regardless of their location in the device tree. Signed-off-by: Thierry Reding --- drivers/gpu/drm/tegra/dc.c | 2 +- drivers/gpu/drm/tegra/gr2d.c | 2 +- drivers/gpu/drm/tegra/gr3d.c | 2 +- drivers/gpu/drm/tegra/vic.c | 2 +- drivers/gpu/host1x/syncpt.c | 16 ++++++++-------- drivers/gpu/host1x/syncpt.h | 2 +- include/linux/host1x.h | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 4df39112e38e..bc51eb855bf1 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1756,7 +1756,7 @@ static int tegra_dc_init(struct host1x_client *client) struct drm_plane *cursor = NULL; int err; - dc->syncpt = host1x_syncpt_request(dc->dev, flags); + dc->syncpt = host1x_syncpt_request(client, flags); if (!dc->syncpt) dev_warn(dc->dev, "failed to allocate syncpoint\n"); diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index 6ea070da7718..9a8ea93016a9 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -36,7 +36,7 @@ static int gr2d_init(struct host1x_client *client) if (!gr2d->channel) return -ENOMEM; - client->syncpts[0] = host1x_syncpt_request(client->dev, flags); + client->syncpts[0] = host1x_syncpt_request(client, flags); if (!client->syncpts[0]) { host1x_channel_put(gr2d->channel); return -ENOMEM; diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index cee2ab645cde..28c4ef63065b 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -46,7 +46,7 @@ static int gr3d_init(struct host1x_client *client) if (!gr3d->channel) return -ENOMEM; - client->syncpts[0] = host1x_syncpt_request(client->dev, flags); + client->syncpts[0] = host1x_syncpt_request(client, flags); if (!client->syncpts[0]) { host1x_channel_put(gr3d->channel); return -ENOMEM; diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c index 2448229fa653..52899fba15e6 100644 --- a/drivers/gpu/drm/tegra/vic.c +++ b/drivers/gpu/drm/tegra/vic.c @@ -167,7 +167,7 @@ static int vic_init(struct host1x_client *client) goto detach_device; } - client->syncpts[0] = host1x_syncpt_request(client->dev, 0); + client->syncpts[0] = host1x_syncpt_request(client, 0); if (!client->syncpts[0]) { err = -ENOMEM; goto free_channel; diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index 048ac9e344ce..fcba94cbf4ed 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c @@ -54,7 +54,7 @@ static void host1x_syncpt_base_free(struct host1x_syncpt_base *base) } static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, - struct device *dev, + struct host1x_client *client, unsigned long flags) { int i; @@ -76,11 +76,11 @@ static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, } name = kasprintf(GFP_KERNEL, "%02u-%s", sp->id, - dev ? dev_name(dev) : NULL); + client ? dev_name(client->dev) : NULL); if (!name) goto free_base; - sp->dev = dev; + sp->client = client; sp->name = name; if (flags & HOST1X_SYNCPT_CLIENT_MANAGED) @@ -419,7 +419,7 @@ int host1x_syncpt_init(struct host1x *host) /** * host1x_syncpt_request() - request a syncpoint - * @dev: device requesting the syncpoint + * @client: client requesting the syncpoint * @flags: flags * * host1x client drivers can use this function to allocate a syncpoint for @@ -427,12 +427,12 @@ int host1x_syncpt_init(struct host1x *host) * use by the client exclusively. When no longer using a syncpoint, a host1x * client driver needs to release it using host1x_syncpt_free(). */ -struct host1x_syncpt *host1x_syncpt_request(struct device *dev, +struct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client, unsigned long flags) { - struct host1x *host = dev_get_drvdata(dev->parent); + struct host1x *host = dev_get_drvdata(client->parent->parent); - return host1x_syncpt_alloc(host, dev, flags); + return host1x_syncpt_alloc(host, client, flags); } EXPORT_SYMBOL(host1x_syncpt_request); @@ -456,7 +456,7 @@ void host1x_syncpt_free(struct host1x_syncpt *sp) host1x_syncpt_base_free(sp->base); kfree(sp->name); sp->base = NULL; - sp->dev = NULL; + sp->client = NULL; sp->name = NULL; sp->client_managed = false; diff --git a/drivers/gpu/host1x/syncpt.h b/drivers/gpu/host1x/syncpt.h index f719205105ac..9d88d37c2397 100644 --- a/drivers/gpu/host1x/syncpt.h +++ b/drivers/gpu/host1x/syncpt.h @@ -44,7 +44,7 @@ struct host1x_syncpt { const char *name; bool client_managed; struct host1x *host; - struct device *dev; + struct host1x_client *client; struct host1x_syncpt_base *base; /* interrupt data */ diff --git a/include/linux/host1x.h b/include/linux/host1x.h index 630b1a98ab58..ddf7f9ca86cc 100644 --- a/include/linux/host1x.h +++ b/include/linux/host1x.h @@ -157,7 +157,7 @@ int host1x_syncpt_incr(struct host1x_syncpt *sp); u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs); int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, u32 *value); -struct host1x_syncpt *host1x_syncpt_request(struct device *dev, +struct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client, unsigned long flags); void host1x_syncpt_free(struct host1x_syncpt *sp); -- cgit v1.2.3-59-g8ed1b From 1fba70e5b6bed53496ba1f1f16127f5be01b5fb6 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Wed, 18 Oct 2017 11:22:51 -0700 Subject: tcp: socket option to set TCP fast open key New socket option TCP_FASTOPEN_KEY to allow different keys per listener. The listener by default uses the global key until the socket option is set. The key is a 16 bytes long binary data. This option has no effect on regular non-listener TCP sockets. Signed-off-by: Yuchung Cheng Reviewed-by: Eric Dumazet Reviewed-by: Christoph Paasch Signed-off-by: David S. Miller --- include/net/request_sock.h | 2 ++ include/net/tcp.h | 5 +++-- include/uapi/linux/tcp.h | 1 + net/ipv4/sysctl_net_ipv4.c | 3 ++- net/ipv4/tcp.c | 33 +++++++++++++++++++++++++++ net/ipv4/tcp_fastopen.c | 56 +++++++++++++++++++++++++++++++++------------- net/ipv4/tcp_ipv4.c | 1 + 7 files changed, 82 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 23e22054aa60..347015515a7d 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -150,6 +150,8 @@ struct fastopen_queue { spinlock_t lock; int qlen; /* # of pending (TCP_SYN_RECV) reqs */ int max_qlen; /* != 0 iff TFO is currently enabled */ + + struct tcp_fastopen_context __rcu *ctx; /* cipher context for cookie */ }; /** struct request_sock_queue - queue of request_socks diff --git a/include/net/tcp.h b/include/net/tcp.h index 3b3b9b968e2d..1efe8365cb28 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1555,9 +1555,10 @@ struct tcp_fastopen_request { int copied; /* queued in tcp_connect() */ }; void tcp_free_fastopen_req(struct tcp_sock *tp); - +void tcp_fastopen_destroy_cipher(struct sock *sk); void tcp_fastopen_ctx_destroy(struct net *net); -int tcp_fastopen_reset_cipher(struct net *net, void *key, unsigned int len); +int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk, + void *key, unsigned int len); void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb); struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, struct request_sock *req, diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 15c25eccab2b..69c7493e42f8 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -119,6 +119,7 @@ enum { #define TCP_FASTOPEN_CONNECT 30 /* Attempt FastOpen with connect */ #define TCP_ULP 31 /* Attach a ULP to a TCP connection */ #define TCP_MD5SIG_EXT 32 /* TCP MD5 Signature with extensions */ +#define TCP_FASTOPEN_KEY 33 /* Set the key for Fast Open (cookie) */ struct tcp_repair_opt { __u32 opt_code; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index cac8dd309f39..81d218346cf7 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -284,7 +284,8 @@ static int proc_tcp_fastopen_key(struct ctl_table *table, int write, ret = -EINVAL; goto bad_key; } - tcp_fastopen_reset_cipher(net, user_key, TCP_FASTOPEN_KEY_LENGTH); + tcp_fastopen_reset_cipher(net, NULL, user_key, + TCP_FASTOPEN_KEY_LENGTH); } bad_key: diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 3b34850d361f..8b1fa4dd4538 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2571,6 +2571,17 @@ static int do_tcp_setsockopt(struct sock *sk, int level, release_sock(sk); return err; } + case TCP_FASTOPEN_KEY: { + __u8 key[TCP_FASTOPEN_KEY_LENGTH]; + + if (optlen != sizeof(key)) + return -EINVAL; + + if (copy_from_user(key, optval, optlen)) + return -EFAULT; + + return tcp_fastopen_reset_cipher(net, sk, key, sizeof(key)); + } default: /* fallthru */ break; @@ -3157,6 +3168,28 @@ static int do_tcp_getsockopt(struct sock *sk, int level, return -EFAULT; return 0; + case TCP_FASTOPEN_KEY: { + __u8 key[TCP_FASTOPEN_KEY_LENGTH]; + struct tcp_fastopen_context *ctx; + + if (get_user(len, optlen)) + return -EFAULT; + + rcu_read_lock(); + ctx = rcu_dereference(icsk->icsk_accept_queue.fastopenq.ctx); + if (ctx) + memcpy(key, ctx->key, sizeof(key)); + else + len = 0; + rcu_read_unlock(); + + len = min_t(unsigned int, len, sizeof(key)); + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, key, len)) + return -EFAULT; + return 0; + } case TCP_THIN_LINEAR_TIMEOUTS: val = tp->thin_lto; break; diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 7ee4aadcdd71..21075ce19cb6 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -29,7 +29,7 @@ void tcp_fastopen_init_key_once(struct net *net) * for a valid cookie, so this is an acceptable risk. */ get_random_bytes(key, sizeof(key)); - tcp_fastopen_reset_cipher(net, key, sizeof(key)); + tcp_fastopen_reset_cipher(net, NULL, key, sizeof(key)); } static void tcp_fastopen_ctx_free(struct rcu_head *head) @@ -40,6 +40,16 @@ static void tcp_fastopen_ctx_free(struct rcu_head *head) kfree(ctx); } +void tcp_fastopen_destroy_cipher(struct sock *sk) +{ + struct tcp_fastopen_context *ctx; + + ctx = rcu_dereference_protected( + inet_csk(sk)->icsk_accept_queue.fastopenq.ctx, 1); + if (ctx) + call_rcu(&ctx->rcu, tcp_fastopen_ctx_free); +} + void tcp_fastopen_ctx_destroy(struct net *net) { struct tcp_fastopen_context *ctxt; @@ -55,10 +65,12 @@ void tcp_fastopen_ctx_destroy(struct net *net) call_rcu(&ctxt->rcu, tcp_fastopen_ctx_free); } -int tcp_fastopen_reset_cipher(struct net *net, void *key, unsigned int len) +int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk, + void *key, unsigned int len) { - int err; struct tcp_fastopen_context *ctx, *octx; + struct fastopen_queue *q; + int err; ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -79,27 +91,39 @@ error: kfree(ctx); } memcpy(ctx->key, key, len); - spin_lock(&net->ipv4.tcp_fastopen_ctx_lock); - octx = rcu_dereference_protected(net->ipv4.tcp_fastopen_ctx, - lockdep_is_held(&net->ipv4.tcp_fastopen_ctx_lock)); - rcu_assign_pointer(net->ipv4.tcp_fastopen_ctx, ctx); - spin_unlock(&net->ipv4.tcp_fastopen_ctx_lock); + if (sk) { + q = &inet_csk(sk)->icsk_accept_queue.fastopenq; + spin_lock_bh(&q->lock); + octx = rcu_dereference_protected(q->ctx, + lockdep_is_held(&q->lock)); + rcu_assign_pointer(q->ctx, ctx); + spin_unlock_bh(&q->lock); + } else { + spin_lock(&net->ipv4.tcp_fastopen_ctx_lock); + octx = rcu_dereference_protected(net->ipv4.tcp_fastopen_ctx, + lockdep_is_held(&net->ipv4.tcp_fastopen_ctx_lock)); + rcu_assign_pointer(net->ipv4.tcp_fastopen_ctx, ctx); + spin_unlock(&net->ipv4.tcp_fastopen_ctx_lock); + } if (octx) call_rcu(&octx->rcu, tcp_fastopen_ctx_free); return err; } -static bool __tcp_fastopen_cookie_gen(struct net *net, - const void *path, +static bool __tcp_fastopen_cookie_gen(struct sock *sk, const void *path, struct tcp_fastopen_cookie *foc) { struct tcp_fastopen_context *ctx; bool ok = false; rcu_read_lock(); - ctx = rcu_dereference(net->ipv4.tcp_fastopen_ctx); + + ctx = rcu_dereference(inet_csk(sk)->icsk_accept_queue.fastopenq.ctx); + if (!ctx) + ctx = rcu_dereference(sock_net(sk)->ipv4.tcp_fastopen_ctx); + if (ctx) { crypto_cipher_encrypt_one(ctx->tfm, foc->val, path); foc->len = TCP_FASTOPEN_COOKIE_SIZE; @@ -115,7 +139,7 @@ static bool __tcp_fastopen_cookie_gen(struct net *net, * * XXX (TFO) - refactor when TCP_FASTOPEN_COOKIE_SIZE != AES_BLOCK_SIZE. */ -static bool tcp_fastopen_cookie_gen(struct net *net, +static bool tcp_fastopen_cookie_gen(struct sock *sk, struct request_sock *req, struct sk_buff *syn, struct tcp_fastopen_cookie *foc) @@ -124,7 +148,7 @@ static bool tcp_fastopen_cookie_gen(struct net *net, const struct iphdr *iph = ip_hdr(syn); __be32 path[4] = { iph->saddr, iph->daddr, 0, 0 }; - return __tcp_fastopen_cookie_gen(net, path, foc); + return __tcp_fastopen_cookie_gen(sk, path, foc); } #if IS_ENABLED(CONFIG_IPV6) @@ -132,13 +156,13 @@ static bool tcp_fastopen_cookie_gen(struct net *net, const struct ipv6hdr *ip6h = ipv6_hdr(syn); struct tcp_fastopen_cookie tmp; - if (__tcp_fastopen_cookie_gen(net, &ip6h->saddr, &tmp)) { + if (__tcp_fastopen_cookie_gen(sk, &ip6h->saddr, &tmp)) { struct in6_addr *buf = &tmp.addr; int i; for (i = 0; i < 4; i++) buf->s6_addr32[i] ^= ip6h->daddr.s6_addr32[i]; - return __tcp_fastopen_cookie_gen(net, buf, foc); + return __tcp_fastopen_cookie_gen(sk, buf, foc); } } #endif @@ -313,7 +337,7 @@ struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, goto fastopen; if (foc->len >= 0 && /* Client presents or requests a cookie */ - tcp_fastopen_cookie_gen(sock_net(sk), req, skb, &valid_foc) && + tcp_fastopen_cookie_gen(sk, req, skb, &valid_foc) && foc->len == TCP_FASTOPEN_COOKIE_SIZE && foc->len == valid_foc.len && !memcmp(foc->val, valid_foc.val, foc->len)) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ecee4ddb24c5..28ca4e177047 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1893,6 +1893,7 @@ void tcp_v4_destroy_sock(struct sock *sk) /* If socket is aborted during connect operation */ tcp_free_fastopen_req(tp); + tcp_fastopen_destroy_cipher(sk); tcp_saved_syn_free(tp); sk_sockets_allocated_dec(sk); -- cgit v1.2.3-59-g8ed1b From e361d1f85855ded967bd4803e8293445a6ce301a Mon Sep 17 00:00:00 2001 From: Frédéric Danis Date: Wed, 11 Oct 2017 10:32:14 +0200 Subject: ACPI / scan: Fix enumeration for special UART devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UART devices is expected to be enumerated by SerDev subsystem. During ACPI scan, serial devices behind SPI, I2C or UART buses are not enumerated, allowing them to be enumerated by their respective parents. Rename *spi_i2c_slave* to *serial_bus_slave* as this will be used for serial devices on serial buses (SPI, I2C or UART). On Macs an empty ResourceTemplate is returned for uart slaves. Instead the device properties "baud", "parity", "dataBits", "stopBits" are provided. Add a check for "baud" in acpi_is_serial_bus_slave(). Signed-off-by: Frédéric Danis Reviewed-by: Sebastian Reichel Tested-by: Ronald Tschalär Tested-by: Peter Y. Chuang Acked-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/scan.c | 37 +++++++++++++++++-------------------- include/acpi/acpi_bus.h | 2 +- 2 files changed, 18 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 602f8ff212f2..860b6987c2d1 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1505,41 +1505,38 @@ static void acpi_init_coherency(struct acpi_device *adev) adev->flags.coherent_dma = cca; } -static int acpi_check_spi_i2c_slave(struct acpi_resource *ares, void *data) +static int acpi_check_serial_bus_slave(struct acpi_resource *ares, void *data) { - bool *is_spi_i2c_slave_p = data; + bool *is_serial_bus_slave_p = data; if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS) return 1; - /* - * devices that are connected to UART still need to be enumerated to - * platform bus - */ - if (ares->data.common_serial_bus.type != ACPI_RESOURCE_SERIAL_TYPE_UART) - *is_spi_i2c_slave_p = true; + *is_serial_bus_slave_p = true; /* no need to do more checking */ return -1; } -static bool acpi_is_spi_i2c_slave(struct acpi_device *device) +static bool acpi_is_serial_bus_slave(struct acpi_device *device) { struct list_head resource_list; - bool is_spi_i2c_slave = false; + bool is_serial_bus_slave = false; /* Macs use device properties in lieu of _CRS resources */ if (x86_apple_machine && (fwnode_property_present(&device->fwnode, "spiSclkPeriod") || - fwnode_property_present(&device->fwnode, "i2cAddress"))) + fwnode_property_present(&device->fwnode, "i2cAddress") || + fwnode_property_present(&device->fwnode, "baud"))) return true; INIT_LIST_HEAD(&resource_list); - acpi_dev_get_resources(device, &resource_list, acpi_check_spi_i2c_slave, - &is_spi_i2c_slave); + acpi_dev_get_resources(device, &resource_list, + acpi_check_serial_bus_slave, + &is_serial_bus_slave); acpi_dev_free_resource_list(&resource_list); - return is_spi_i2c_slave; + return is_serial_bus_slave; } void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, @@ -1557,7 +1554,7 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle, acpi_bus_get_flags(device); device->flags.match_driver = false; device->flags.initialized = true; - device->flags.spi_i2c_slave = acpi_is_spi_i2c_slave(device); + device->flags.serial_bus_slave = acpi_is_serial_bus_slave(device); acpi_device_clear_enumerated(device); device_initialize(&device->dev); dev_set_uevent_suppress(&device->dev, true); @@ -1841,10 +1838,10 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl_not_used, static void acpi_default_enumeration(struct acpi_device *device) { /* - * Do not enumerate SPI/I2C slaves as they will be enumerated by their - * respective parents. + * Do not enumerate SPI/I2C/UART slaves as they will be enumerated by + * their respective parents. */ - if (!device->flags.spi_i2c_slave) { + if (!device->flags.serial_bus_slave) { acpi_create_platform_device(device, NULL); acpi_device_set_enumerated(device); } else { @@ -1941,7 +1938,7 @@ static void acpi_bus_attach(struct acpi_device *device) return; device->flags.match_driver = true; - if (ret > 0 && !device->flags.spi_i2c_slave) { + if (ret > 0 && !device->flags.serial_bus_slave) { acpi_device_set_enumerated(device); goto ok; } @@ -1950,7 +1947,7 @@ static void acpi_bus_attach(struct acpi_device *device) if (ret < 0) return; - if (!device->pnp.type.platform_id && !device->flags.spi_i2c_slave) + if (!device->pnp.type.platform_id && !device->flags.serial_bus_slave) acpi_device_set_enumerated(device); else acpi_default_enumeration(device); diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index fa1505292f6c..f849be28e082 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -211,7 +211,7 @@ struct acpi_device_flags { u32 of_compatible_ok:1; u32 coherent_dma:1; u32 cca_seen:1; - u32 spi_i2c_slave:1; + u32 serial_bus_slave:1; u32 reserved:19; }; -- cgit v1.2.3-59-g8ed1b From 6e71b04a82248ccf13a94b85cbc674a9fefe53f5 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 18 Oct 2017 13:00:22 -0700 Subject: bpf: Add file mode configuration into bpf maps Introduce the map read/write flags to the eBPF syscalls that returns the map fd. The flags is used to set up the file mode when construct a new file descriptor for bpf maps. To not break the backward capability, the f_flags is set to O_RDWR if the flag passed by syscall is 0. Otherwise it should be O_RDONLY or O_WRONLY. When the userspace want to modify or read the map content, it will check the file mode to see if it is allowed to make the change. Signed-off-by: Chenbo Feng Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 8 +++-- include/uapi/linux/bpf.h | 6 ++++ kernel/bpf/arraymap.c | 6 +++- kernel/bpf/devmap.c | 5 ++- kernel/bpf/hashtab.c | 5 +-- kernel/bpf/inode.c | 15 ++++++--- kernel/bpf/lpm_trie.c | 3 +- kernel/bpf/sockmap.c | 5 ++- kernel/bpf/stackmap.c | 5 ++- kernel/bpf/syscall.c | 88 ++++++++++++++++++++++++++++++++++++++++++------ net/netfilter/xt_bpf.c | 2 +- 11 files changed, 122 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index d67ccdc0099f..3e5508f2fa87 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -315,11 +315,11 @@ void bpf_map_area_free(void *base); extern int sysctl_unprivileged_bpf_disabled; -int bpf_map_new_fd(struct bpf_map *map); +int bpf_map_new_fd(struct bpf_map *map, int flags); int bpf_prog_new_fd(struct bpf_prog *prog); int bpf_obj_pin_user(u32 ufd, const char __user *pathname); -int bpf_obj_get_user(const char __user *pathname); +int bpf_obj_get_user(const char __user *pathname, int flags); int bpf_percpu_hash_copy(struct bpf_map *map, void *key, void *value); int bpf_percpu_array_copy(struct bpf_map *map, void *key, void *value); @@ -338,6 +338,8 @@ int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file, void *key, void *value, u64 map_flags); int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value); +int bpf_get_file_flag(int flags); + /* memcpy that is used with 8-byte aligned pointers, power-of-8 size and * forced to use 'long' read/writes to try to atomically copy long counters. * Best-effort only. No barriers here, since it _will_ race with concurrent @@ -421,7 +423,7 @@ static inline void __bpf_prog_uncharge(struct user_struct *user, u32 pages) { } -static inline int bpf_obj_get_user(const char __user *pathname) +static inline int bpf_obj_get_user(const char __user *pathname, int flags) { return -EOPNOTSUPP; } diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 4303fb6c3817..d83f95ea6a1b 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -218,6 +218,10 @@ enum bpf_attach_type { #define BPF_OBJ_NAME_LEN 16U +/* Flags for accessing BPF object */ +#define BPF_F_RDONLY (1U << 3) +#define BPF_F_WRONLY (1U << 4) + union bpf_attr { struct { /* anonymous struct used by BPF_MAP_CREATE command */ __u32 map_type; /* one of enum bpf_map_type */ @@ -260,6 +264,7 @@ union bpf_attr { struct { /* anonymous struct used by BPF_OBJ_* commands */ __aligned_u64 pathname; __u32 bpf_fd; + __u32 file_flags; }; struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */ @@ -287,6 +292,7 @@ union bpf_attr { __u32 map_id; }; __u32 next_id; + __u32 open_flags; }; struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */ diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index 68d866628be0..988c04c91e10 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c @@ -19,6 +19,9 @@ #include "map_in_map.h" +#define ARRAY_CREATE_FLAG_MASK \ + (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) + static void bpf_array_free_percpu(struct bpf_array *array) { int i; @@ -56,7 +59,8 @@ static struct bpf_map *array_map_alloc(union bpf_attr *attr) /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 || - attr->value_size == 0 || attr->map_flags & ~BPF_F_NUMA_NODE || + attr->value_size == 0 || + attr->map_flags & ~ARRAY_CREATE_FLAG_MASK || (percpu && numa_node != NUMA_NO_NODE)) return ERR_PTR(-EINVAL); diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index e093d9a2c4dd..e5d3de7cff2e 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -50,6 +50,9 @@ #include #include +#define DEV_CREATE_FLAG_MASK \ + (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) + struct bpf_dtab_netdev { struct net_device *dev; struct bpf_dtab *dtab; @@ -80,7 +83,7 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 || - attr->value_size != 4 || attr->map_flags & ~BPF_F_NUMA_NODE) + attr->value_size != 4 || attr->map_flags & ~DEV_CREATE_FLAG_MASK) return ERR_PTR(-EINVAL); dtab = kzalloc(sizeof(*dtab), GFP_USER); diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c index 431126f31ea3..919955236e63 100644 --- a/kernel/bpf/hashtab.c +++ b/kernel/bpf/hashtab.c @@ -18,8 +18,9 @@ #include "bpf_lru_list.h" #include "map_in_map.h" -#define HTAB_CREATE_FLAG_MASK \ - (BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE) +#define HTAB_CREATE_FLAG_MASK \ + (BPF_F_NO_PREALLOC | BPF_F_NO_COMMON_LRU | BPF_F_NUMA_NODE | \ + BPF_F_RDONLY | BPF_F_WRONLY) struct bucket { struct hlist_nulls_head head; diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index be1dde967208..01aaef1a77c5 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -295,7 +295,7 @@ out: } static void *bpf_obj_do_get(const struct filename *pathname, - enum bpf_type *type) + enum bpf_type *type, int flags) { struct inode *inode; struct path path; @@ -307,7 +307,7 @@ static void *bpf_obj_do_get(const struct filename *pathname, return ERR_PTR(ret); inode = d_backing_inode(path.dentry); - ret = inode_permission(inode, MAY_WRITE); + ret = inode_permission(inode, ACC_MODE(flags)); if (ret) goto out; @@ -326,18 +326,23 @@ out: return ERR_PTR(ret); } -int bpf_obj_get_user(const char __user *pathname) +int bpf_obj_get_user(const char __user *pathname, int flags) { enum bpf_type type = BPF_TYPE_UNSPEC; struct filename *pname; int ret = -ENOENT; + int f_flags; void *raw; + f_flags = bpf_get_file_flag(flags); + if (f_flags < 0) + return f_flags; + pname = getname(pathname); if (IS_ERR(pname)) return PTR_ERR(pname); - raw = bpf_obj_do_get(pname, &type); + raw = bpf_obj_do_get(pname, &type, f_flags); if (IS_ERR(raw)) { ret = PTR_ERR(raw); goto out; @@ -346,7 +351,7 @@ int bpf_obj_get_user(const char __user *pathname) if (type == BPF_TYPE_PROG) ret = bpf_prog_new_fd(raw); else if (type == BPF_TYPE_MAP) - ret = bpf_map_new_fd(raw); + ret = bpf_map_new_fd(raw, f_flags); else goto out; diff --git a/kernel/bpf/lpm_trie.c b/kernel/bpf/lpm_trie.c index 34d8a690ea05..885e45479680 100644 --- a/kernel/bpf/lpm_trie.c +++ b/kernel/bpf/lpm_trie.c @@ -495,7 +495,8 @@ out: #define LPM_KEY_SIZE_MAX LPM_KEY_SIZE(LPM_DATA_SIZE_MAX) #define LPM_KEY_SIZE_MIN LPM_KEY_SIZE(LPM_DATA_SIZE_MIN) -#define LPM_CREATE_FLAG_MASK (BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE) +#define LPM_CREATE_FLAG_MASK (BPF_F_NO_PREALLOC | BPF_F_NUMA_NODE | \ + BPF_F_RDONLY | BPF_F_WRONLY) static struct bpf_map *trie_alloc(union bpf_attr *attr) { diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c index a298d6666698..86ec846f2d5e 100644 --- a/kernel/bpf/sockmap.c +++ b/kernel/bpf/sockmap.c @@ -40,6 +40,9 @@ #include #include +#define SOCK_CREATE_FLAG_MASK \ + (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) + struct bpf_stab { struct bpf_map map; struct sock **sock_map; @@ -489,7 +492,7 @@ static struct bpf_map *sock_map_alloc(union bpf_attr *attr) /* check sanity of attributes */ if (attr->max_entries == 0 || attr->key_size != 4 || - attr->value_size != 4 || attr->map_flags & ~BPF_F_NUMA_NODE) + attr->value_size != 4 || attr->map_flags & ~SOCK_CREATE_FLAG_MASK) return ERR_PTR(-EINVAL); if (attr->value_size > KMALLOC_MAX_SIZE) diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c index 135be433e9a0..a15bc636cc98 100644 --- a/kernel/bpf/stackmap.c +++ b/kernel/bpf/stackmap.c @@ -11,6 +11,9 @@ #include #include "percpu_freelist.h" +#define STACK_CREATE_FLAG_MASK \ + (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) + struct stack_map_bucket { struct pcpu_freelist_node fnode; u32 hash; @@ -60,7 +63,7 @@ static struct bpf_map *stack_map_alloc(union bpf_attr *attr) if (!capable(CAP_SYS_ADMIN)) return ERR_PTR(-EPERM); - if (attr->map_flags & ~BPF_F_NUMA_NODE) + if (attr->map_flags & ~STACK_CREATE_FLAG_MASK) return ERR_PTR(-EINVAL); /* check sanity of attributes */ diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 0e893cac6795..676a06e6b322 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -34,6 +34,8 @@ #define IS_FD_HASH(map) ((map)->map_type == BPF_MAP_TYPE_HASH_OF_MAPS) #define IS_FD_MAP(map) (IS_FD_ARRAY(map) || IS_FD_HASH(map)) +#define BPF_OBJ_FLAG_MASK (BPF_F_RDONLY | BPF_F_WRONLY) + DEFINE_PER_CPU(int, bpf_prog_active); static DEFINE_IDR(prog_idr); static DEFINE_SPINLOCK(prog_idr_lock); @@ -294,17 +296,48 @@ static void bpf_map_show_fdinfo(struct seq_file *m, struct file *filp) } #endif +static ssize_t bpf_dummy_read(struct file *filp, char __user *buf, size_t siz, + loff_t *ppos) +{ + /* We need this handler such that alloc_file() enables + * f_mode with FMODE_CAN_READ. + */ + return -EINVAL; +} + +static ssize_t bpf_dummy_write(struct file *filp, const char __user *buf, + size_t siz, loff_t *ppos) +{ + /* We need this handler such that alloc_file() enables + * f_mode with FMODE_CAN_WRITE. + */ + return -EINVAL; +} + static const struct file_operations bpf_map_fops = { #ifdef CONFIG_PROC_FS .show_fdinfo = bpf_map_show_fdinfo, #endif .release = bpf_map_release, + .read = bpf_dummy_read, + .write = bpf_dummy_write, }; -int bpf_map_new_fd(struct bpf_map *map) +int bpf_map_new_fd(struct bpf_map *map, int flags) { return anon_inode_getfd("bpf-map", &bpf_map_fops, map, - O_RDWR | O_CLOEXEC); + flags | O_CLOEXEC); +} + +int bpf_get_file_flag(int flags) +{ + if ((flags & BPF_F_RDONLY) && (flags & BPF_F_WRONLY)) + return -EINVAL; + if (flags & BPF_F_RDONLY) + return O_RDONLY; + if (flags & BPF_F_WRONLY) + return O_WRONLY; + return O_RDWR; } /* helper macro to check that unused fields 'union bpf_attr' are zero */ @@ -344,12 +377,17 @@ static int map_create(union bpf_attr *attr) { int numa_node = bpf_map_attr_numa_node(attr); struct bpf_map *map; + int f_flags; int err; err = CHECK_ATTR(BPF_MAP_CREATE); if (err) return -EINVAL; + f_flags = bpf_get_file_flag(attr->map_flags); + if (f_flags < 0) + return f_flags; + if (numa_node != NUMA_NO_NODE && ((unsigned int)numa_node >= nr_node_ids || !node_online(numa_node))) @@ -375,7 +413,7 @@ static int map_create(union bpf_attr *attr) if (err) goto free_map; - err = bpf_map_new_fd(map); + err = bpf_map_new_fd(map, f_flags); if (err < 0) { /* failed to allocate fd. * bpf_map_put() is needed because the above @@ -490,6 +528,11 @@ static int map_lookup_elem(union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); + if (!(f.file->f_mode & FMODE_CAN_READ)) { + err = -EPERM; + goto err_put; + } + key = memdup_user(ukey, map->key_size); if (IS_ERR(key)) { err = PTR_ERR(key); @@ -570,6 +613,11 @@ static int map_update_elem(union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); + if (!(f.file->f_mode & FMODE_CAN_WRITE)) { + err = -EPERM; + goto err_put; + } + key = memdup_user(ukey, map->key_size); if (IS_ERR(key)) { err = PTR_ERR(key); @@ -659,6 +707,11 @@ static int map_delete_elem(union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); + if (!(f.file->f_mode & FMODE_CAN_WRITE)) { + err = -EPERM; + goto err_put; + } + key = memdup_user(ukey, map->key_size); if (IS_ERR(key)) { err = PTR_ERR(key); @@ -702,6 +755,11 @@ static int map_get_next_key(union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); + if (!(f.file->f_mode & FMODE_CAN_READ)) { + err = -EPERM; + goto err_put; + } + if (ukey) { key = memdup_user(ukey, map->key_size); if (IS_ERR(key)) { @@ -908,6 +966,8 @@ static const struct file_operations bpf_prog_fops = { .show_fdinfo = bpf_prog_show_fdinfo, #endif .release = bpf_prog_release, + .read = bpf_dummy_read, + .write = bpf_dummy_write, }; int bpf_prog_new_fd(struct bpf_prog *prog) @@ -1117,11 +1177,11 @@ free_prog_nouncharge: return err; } -#define BPF_OBJ_LAST_FIELD bpf_fd +#define BPF_OBJ_LAST_FIELD file_flags static int bpf_obj_pin(const union bpf_attr *attr) { - if (CHECK_ATTR(BPF_OBJ)) + if (CHECK_ATTR(BPF_OBJ) || attr->file_flags != 0) return -EINVAL; return bpf_obj_pin_user(attr->bpf_fd, u64_to_user_ptr(attr->pathname)); @@ -1129,10 +1189,12 @@ static int bpf_obj_pin(const union bpf_attr *attr) static int bpf_obj_get(const union bpf_attr *attr) { - if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0) + if (CHECK_ATTR(BPF_OBJ) || attr->bpf_fd != 0 || + attr->file_flags & ~BPF_OBJ_FLAG_MASK) return -EINVAL; - return bpf_obj_get_user(u64_to_user_ptr(attr->pathname)); + return bpf_obj_get_user(u64_to_user_ptr(attr->pathname), + attr->file_flags); } #ifdef CONFIG_CGROUP_BPF @@ -1392,20 +1454,26 @@ static int bpf_prog_get_fd_by_id(const union bpf_attr *attr) return fd; } -#define BPF_MAP_GET_FD_BY_ID_LAST_FIELD map_id +#define BPF_MAP_GET_FD_BY_ID_LAST_FIELD open_flags static int bpf_map_get_fd_by_id(const union bpf_attr *attr) { struct bpf_map *map; u32 id = attr->map_id; + int f_flags; int fd; - if (CHECK_ATTR(BPF_MAP_GET_FD_BY_ID)) + if (CHECK_ATTR(BPF_MAP_GET_FD_BY_ID) || + attr->open_flags & ~BPF_OBJ_FLAG_MASK) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; + f_flags = bpf_get_file_flag(attr->open_flags); + if (f_flags < 0) + return f_flags; + spin_lock_bh(&map_idr_lock); map = idr_find(&map_idr, id); if (map) @@ -1417,7 +1485,7 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr) if (IS_ERR(map)) return PTR_ERR(map); - fd = bpf_map_new_fd(map); + fd = bpf_map_new_fd(map, f_flags); if (fd < 0) bpf_map_put(map); diff --git a/net/netfilter/xt_bpf.c b/net/netfilter/xt_bpf.c index 29123934887b..041da0d9c06f 100644 --- a/net/netfilter/xt_bpf.c +++ b/net/netfilter/xt_bpf.c @@ -56,7 +56,7 @@ static int __bpf_mt_check_path(const char *path, struct bpf_prog **ret) int retval, fd; set_fs(KERNEL_DS); - fd = bpf_obj_get_user(path); + fd = bpf_obj_get_user(path, 0); set_fs(oldfs); if (fd < 0) return fd; -- cgit v1.2.3-59-g8ed1b From afdb09c720b62b8090584c11151d856df330e57d Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 18 Oct 2017 13:00:24 -0700 Subject: security: bpf: Add LSM hooks for bpf object related syscall Introduce several LSM hooks for the syscalls that will allow the userspace to access to eBPF object such as eBPF programs and eBPF maps. The security check is aimed to enforce a per object security protection for eBPF object so only processes with the right priviliges can read/write to a specific map or use a specific eBPF program. Besides that, a general security hook is added before the multiplexer of bpf syscall to check the cmd and the attribute used for the command. The actual security module can decide which command need to be checked and how the cmd should be checked. Signed-off-by: Chenbo Feng Acked-by: James Morris Signed-off-by: David S. Miller --- include/linux/bpf.h | 6 ++++++ include/linux/lsm_hooks.h | 54 +++++++++++++++++++++++++++++++++++++++++++++++ include/linux/security.h | 45 +++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 34 +++++++++++++++++++++++++++-- security/security.c | 32 ++++++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 3e5508f2fa87..84c192da3e0b 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -57,6 +57,9 @@ struct bpf_map { atomic_t usercnt; struct bpf_map *inner_map_meta; char name[BPF_OBJ_NAME_LEN]; +#ifdef CONFIG_SECURITY + void *security; +#endif }; /* function argument constraints */ @@ -193,6 +196,9 @@ struct bpf_prog_aux { struct user_struct *user; u64 load_time; /* ns since boottime */ char name[BPF_OBJ_NAME_LEN]; +#ifdef CONFIG_SECURITY + void *security; +#endif union { struct work_struct work; struct rcu_head rcu; diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index c9258124e417..7161d8e7ee79 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1351,6 +1351,40 @@ * @inode we wish to get the security context of. * @ctx is a pointer in which to place the allocated security context. * @ctxlen points to the place to put the length of @ctx. + * + * Security hooks for using the eBPF maps and programs functionalities through + * eBPF syscalls. + * + * @bpf: + * Do a initial check for all bpf syscalls after the attribute is copied + * into the kernel. The actual security module can implement their own + * rules to check the specific cmd they need. + * + * @bpf_map: + * Do a check when the kernel generate and return a file descriptor for + * eBPF maps. + * + * @map: bpf map that we want to access + * @mask: the access flags + * + * @bpf_prog: + * Do a check when the kernel generate and return a file descriptor for + * eBPF programs. + * + * @prog: bpf prog that userspace want to use. + * + * @bpf_map_alloc_security: + * Initialize the security field inside bpf map. + * + * @bpf_map_free_security: + * Clean up the security information stored inside bpf map. + * + * @bpf_prog_alloc_security: + * Initialize the security field inside bpf program. + * + * @bpf_prog_free_security: + * Clean up the security information stored inside bpf prog. + * */ union security_list_options { int (*binder_set_context_mgr)(struct task_struct *mgr); @@ -1682,6 +1716,17 @@ union security_list_options { struct audit_context *actx); void (*audit_rule_free)(void *lsmrule); #endif /* CONFIG_AUDIT */ + +#ifdef CONFIG_BPF_SYSCALL + int (*bpf)(int cmd, union bpf_attr *attr, + unsigned int size); + int (*bpf_map)(struct bpf_map *map, fmode_t fmode); + int (*bpf_prog)(struct bpf_prog *prog); + int (*bpf_map_alloc_security)(struct bpf_map *map); + void (*bpf_map_free_security)(struct bpf_map *map); + int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux); + void (*bpf_prog_free_security)(struct bpf_prog_aux *aux); +#endif /* CONFIG_BPF_SYSCALL */ }; struct security_hook_heads { @@ -1901,6 +1946,15 @@ struct security_hook_heads { struct list_head audit_rule_match; struct list_head audit_rule_free; #endif /* CONFIG_AUDIT */ +#ifdef CONFIG_BPF_SYSCALL + struct list_head bpf; + struct list_head bpf_map; + struct list_head bpf_prog; + struct list_head bpf_map_alloc_security; + struct list_head bpf_map_free_security; + struct list_head bpf_prog_alloc_security; + struct list_head bpf_prog_free_security; +#endif /* CONFIG_BPF_SYSCALL */ } __randomize_layout; /* diff --git a/include/linux/security.h b/include/linux/security.h index ce6265960d6c..18800b0911e5 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -31,6 +31,7 @@ #include #include #include +#include struct linux_binprm; struct cred; @@ -1730,6 +1731,50 @@ static inline void securityfs_remove(struct dentry *dentry) #endif +#ifdef CONFIG_BPF_SYSCALL +#ifdef CONFIG_SECURITY +extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size); +extern int security_bpf_map(struct bpf_map *map, fmode_t fmode); +extern int security_bpf_prog(struct bpf_prog *prog); +extern int security_bpf_map_alloc(struct bpf_map *map); +extern void security_bpf_map_free(struct bpf_map *map); +extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux); +extern void security_bpf_prog_free(struct bpf_prog_aux *aux); +#else +static inline int security_bpf(int cmd, union bpf_attr *attr, + unsigned int size) +{ + return 0; +} + +static inline int security_bpf_map(struct bpf_map *map, fmode_t fmode) +{ + return 0; +} + +static inline int security_bpf_prog(struct bpf_prog *prog) +{ + return 0; +} + +static inline int security_bpf_map_alloc(struct bpf_map *map) +{ + return 0; +} + +static inline void security_bpf_map_free(struct bpf_map *map) +{ } + +static inline int security_bpf_prog_alloc(struct bpf_prog_aux *aux) +{ + return 0; +} + +static inline void security_bpf_prog_free(struct bpf_prog_aux *aux) +{ } +#endif /* CONFIG_SECURITY */ +#endif /* CONFIG_BPF_SYSCALL */ + #ifdef CONFIG_SECURITY static inline char *alloc_secdata(void) diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 676a06e6b322..5cb56d06b48d 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -212,6 +212,7 @@ static void bpf_map_free_deferred(struct work_struct *work) struct bpf_map *map = container_of(work, struct bpf_map, work); bpf_map_uncharge_memlock(map); + security_bpf_map_free(map); /* implementation dependent freeing */ map->ops->map_free(map); } @@ -325,6 +326,12 @@ static const struct file_operations bpf_map_fops = { int bpf_map_new_fd(struct bpf_map *map, int flags) { + int ret; + + ret = security_bpf_map(map, OPEN_FMODE(flags)); + if (ret < 0) + return ret; + return anon_inode_getfd("bpf-map", &bpf_map_fops, map, flags | O_CLOEXEC); } @@ -405,10 +412,14 @@ static int map_create(union bpf_attr *attr) atomic_set(&map->refcnt, 1); atomic_set(&map->usercnt, 1); - err = bpf_map_charge_memlock(map); + err = security_bpf_map_alloc(map); if (err) goto free_map_nouncharge; + err = bpf_map_charge_memlock(map); + if (err) + goto free_map_sec; + err = bpf_map_alloc_id(map); if (err) goto free_map; @@ -430,6 +441,8 @@ static int map_create(union bpf_attr *attr) free_map: bpf_map_uncharge_memlock(map); +free_map_sec: + security_bpf_map_free(map); free_map_nouncharge: map->ops->map_free(map); return err; @@ -914,6 +927,7 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu) free_used_maps(aux); bpf_prog_uncharge_memlock(aux->prog); + security_bpf_prog_free(aux); bpf_prog_free(aux->prog); } @@ -972,6 +986,12 @@ static const struct file_operations bpf_prog_fops = { int bpf_prog_new_fd(struct bpf_prog *prog) { + int ret; + + ret = security_bpf_prog(prog); + if (ret < 0) + return ret; + return anon_inode_getfd("bpf-prog", &bpf_prog_fops, prog, O_RDWR | O_CLOEXEC); } @@ -1111,10 +1131,14 @@ static int bpf_prog_load(union bpf_attr *attr) if (!prog) return -ENOMEM; - err = bpf_prog_charge_memlock(prog); + err = security_bpf_prog_alloc(prog->aux); if (err) goto free_prog_nouncharge; + err = bpf_prog_charge_memlock(prog); + if (err) + goto free_prog_sec; + prog->len = attr->insn_cnt; err = -EFAULT; @@ -1172,6 +1196,8 @@ free_used_maps: free_used_maps(prog->aux); free_prog: bpf_prog_uncharge_memlock(prog); +free_prog_sec: + security_bpf_prog_free(prog->aux); free_prog_nouncharge: bpf_prog_free(prog); return err; @@ -1640,6 +1666,10 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz if (copy_from_user(&attr, uattr, size) != 0) return -EFAULT; + err = security_bpf(cmd, &attr, size); + if (err < 0) + return err; + switch (cmd) { case BPF_MAP_CREATE: err = map_create(&attr); diff --git a/security/security.c b/security/security.c index 4bf0f571b4ef..1cd8526cb0b7 100644 --- a/security/security.c +++ b/security/security.c @@ -12,6 +12,7 @@ * (at your option) any later version. */ +#include #include #include #include @@ -1703,3 +1704,34 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule, actx); } #endif /* CONFIG_AUDIT */ + +#ifdef CONFIG_BPF_SYSCALL +int security_bpf(int cmd, union bpf_attr *attr, unsigned int size) +{ + return call_int_hook(bpf, 0, cmd, attr, size); +} +int security_bpf_map(struct bpf_map *map, fmode_t fmode) +{ + return call_int_hook(bpf_map, 0, map, fmode); +} +int security_bpf_prog(struct bpf_prog *prog) +{ + return call_int_hook(bpf_prog, 0, prog); +} +int security_bpf_map_alloc(struct bpf_map *map) +{ + return call_int_hook(bpf_map_alloc_security, 0, map); +} +int security_bpf_prog_alloc(struct bpf_prog_aux *aux) +{ + return call_int_hook(bpf_prog_alloc_security, 0, aux); +} +void security_bpf_map_free(struct bpf_map *map) +{ + call_void_hook(bpf_map_free_security, map); +} +void security_bpf_prog_free(struct bpf_prog_aux *aux) +{ + call_void_hook(bpf_prog_free_security, aux); +} +#endif /* CONFIG_BPF_SYSCALL */ -- cgit v1.2.3-59-g8ed1b From f66e448cfda021b0bcd884f26709796fe19c7cc1 Mon Sep 17 00:00:00 2001 From: Chenbo Feng Date: Wed, 18 Oct 2017 13:00:26 -0700 Subject: selinux: bpf: Add addtional check for bpf object file receive Introduce a bpf object related check when sending and receiving files through unix domain socket as well as binder. It checks if the receiving process have privilege to read/write the bpf map or use the bpf program. This check is necessary because the bpf maps and programs are using a anonymous inode as their shared inode so the normal way of checking the files and sockets when passing between processes cannot work properly on eBPF object. This check only works when the BPF_SYSCALL is configured. Signed-off-by: Chenbo Feng Acked-by: Stephen Smalley Reviewed-by: James Morris Signed-off-by: David S. Miller --- include/linux/bpf.h | 3 +++ kernel/bpf/syscall.c | 4 ++-- security/selinux/hooks.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 84c192da3e0b..1e334b248ff6 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -288,6 +288,9 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, #ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU(int, bpf_prog_active); +extern const struct file_operations bpf_map_fops; +extern const struct file_operations bpf_prog_fops; + #define BPF_PROG_TYPE(_id, _name) \ extern const struct bpf_prog_ops _name ## _prog_ops; \ extern const struct bpf_verifier_ops _name ## _verifier_ops; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 5cb56d06b48d..323be2473c4b 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -315,7 +315,7 @@ static ssize_t bpf_dummy_write(struct file *filp, const char __user *buf, return -EINVAL; } -static const struct file_operations bpf_map_fops = { +const struct file_operations bpf_map_fops = { #ifdef CONFIG_PROC_FS .show_fdinfo = bpf_map_show_fdinfo, #endif @@ -975,7 +975,7 @@ static void bpf_prog_show_fdinfo(struct seq_file *m, struct file *filp) } #endif -static const struct file_operations bpf_prog_fops = { +const struct file_operations bpf_prog_fops = { #ifdef CONFIG_PROC_FS .show_fdinfo = bpf_prog_show_fdinfo, #endif diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 12cf7de8cbed..2e3a627fc0b1 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1815,6 +1815,10 @@ static inline int file_path_has_perm(const struct cred *cred, return inode_has_perm(cred, file_inode(file), av, &ad); } +#ifdef CONFIG_BPF_SYSCALL +static int bpf_fd_pass(struct file *file, u32 sid); +#endif + /* Check whether a task can use an open file descriptor to access an inode in a given way. Check access to the descriptor itself, and then use dentry_has_perm to @@ -1845,6 +1849,12 @@ static int file_has_perm(const struct cred *cred, goto out; } +#ifdef CONFIG_BPF_SYSCALL + rc = bpf_fd_pass(file, cred_sid(cred)); + if (rc) + return rc; +#endif + /* av is zero if only checking access to the descriptor. */ rc = 0; if (av) @@ -2165,6 +2175,12 @@ static int selinux_binder_transfer_file(struct task_struct *from, return rc; } +#ifdef CONFIG_BPF_SYSCALL + rc = bpf_fd_pass(file, sid); + if (rc) + return rc; +#endif + if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return 0; @@ -6288,6 +6304,39 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode) return av; } +/* This function will check the file pass through unix socket or binder to see + * if it is a bpf related object. And apply correspinding checks on the bpf + * object based on the type. The bpf maps and programs, not like other files and + * socket, are using a shared anonymous inode inside the kernel as their inode. + * So checking that inode cannot identify if the process have privilege to + * access the bpf object and that's why we have to add this additional check in + * selinux_file_receive and selinux_binder_transfer_files. + */ +static int bpf_fd_pass(struct file *file, u32 sid) +{ + struct bpf_security_struct *bpfsec; + struct bpf_prog *prog; + struct bpf_map *map; + int ret; + + if (file->f_op == &bpf_map_fops) { + map = file->private_data; + bpfsec = map->security; + ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + bpf_map_fmode_to_av(file->f_mode), NULL); + if (ret) + return ret; + } else if (file->f_op == &bpf_prog_fops) { + prog = file->private_data; + bpfsec = prog->aux->security; + ret = avc_has_perm(sid, bpfsec->sid, SECCLASS_BPF, + BPF__PROG_RUN, NULL); + if (ret) + return ret; + } + return 0; +} + static int selinux_bpf_map(struct bpf_map *map, fmode_t fmode) { u32 sid = current_sid(); -- cgit v1.2.3-59-g8ed1b From 56e0349f384cbadf3b939cbcebe6e1560513246e Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 20 Oct 2017 10:52:18 +1000 Subject: amdgpu: add padding to the fence to handle ioctl. I don't think this ioctl is in a Linus release yet. Signed-off-by: Dave Airlie Signed-off-by: Alex Deucher Reviewed-by: Alex Deucher --- include/uapi/drm/amdgpu_drm.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/drm/amdgpu_drm.h b/include/uapi/drm/amdgpu_drm.h index ff0181829f3d..919248fb4028 100644 --- a/include/uapi/drm/amdgpu_drm.h +++ b/include/uapi/drm/amdgpu_drm.h @@ -553,6 +553,7 @@ union drm_amdgpu_fence_to_handle { struct { struct drm_amdgpu_fence fence; __u32 what; + __u32 pad; } in; struct { __u32 handle; -- cgit v1.2.3-59-g8ed1b From 570e86963a511c1b404e81c72d2c42169faf9324 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 12 Oct 2017 11:57:49 -0700 Subject: drm: Widen vblank count to 64-bits [v3] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This modifies the datatypes used by the vblank code to provide 64 bits of vblank count. The driver interfaces have been left using 32 bits of vblank count; all of the code necessary to widen that value for the user API was already included to handle devices returning fewer than 32-bits. This will provide the necessary datatypes for the Vulkan API. v2: * Re-write wait_vblank ioctl to ABSOLUTE sequence When an application uses the WAIT_VBLANK ioctl with RELATIVE or NEXTONMISS bits set, the target vblank interval is updated within the kernel. We need to write that target back to the ioctl buffer and update the flags bits so that if the wait is interrupted by a signal, when it is re-started, it will target precisely the same vblank count as before. * Leave driver API with 32-bit vblank count v3: * Rebase on top of Arnd Bergmann's patch which had the switch to ktime_t parts. [airlied: fix conflict with Ville vblank change]. Suggested-by: Michel Dänzer Suggested-by: Daniel Vetter Signed-off-by: Keith Packard Signed-off-by: Dave Airlie (cherry picked from commit 2affbc16983e4fc90960bc7f70e7615f4228199b) --- drivers/gpu/drm/drm_vblank.c | 104 +++++++++++++++++++++++++++++-------------- include/drm/drm_vblank.h | 10 +++-- 2 files changed, 78 insertions(+), 36 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 3af6c20ba03b..c7e5a274f419 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -251,7 +251,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, } DRM_DEBUG_VBL("updating vblank count on crtc %u:" - " current=%u, diff=%u, hw=%u hw_last=%u\n", + " current=%llu, diff=%u, hw=%u hw_last=%u\n", pipe, vblank->count, diff, cur_vblank, vblank->last); if (diff == 0) { @@ -740,17 +740,31 @@ drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, * Returns: * The software vblank counter. */ -u32 drm_crtc_vblank_count(struct drm_crtc *crtc) +u64 drm_crtc_vblank_count(struct drm_crtc *crtc) { return drm_vblank_count(crtc->dev, drm_crtc_index(crtc)); } EXPORT_SYMBOL(drm_crtc_vblank_count); -static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, +/** + * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the + * system timestamp corresponding to that vblank counter value. + * @dev: DRM device + * @pipe: index of CRTC whose counter to retrieve + * @vblanktime: Pointer to ktime_t to receive the vblank timestamp. + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. Returns corresponding system timestamp of the time + * of the vblank interval that corresponds to the current vblank counter value. + * + * This is the legacy version of drm_crtc_vblank_count_and_time(). + */ +static u64 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, ktime_t *vblanktime) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - u32 vblank_count; + u64 vblank_count; unsigned int seq; if (WARN_ON(pipe >= dev->num_crtcs)) { @@ -778,7 +792,7 @@ static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, * modesetting activity. Returns corresponding system timestamp of the time * of the vblank interval that corresponds to the current vblank counter value. */ -u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, +u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, ktime_t *vblanktime) { return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc), @@ -788,7 +802,7 @@ EXPORT_SYMBOL(drm_crtc_vblank_count_and_time); static void send_vblank_event(struct drm_device *dev, struct drm_pending_vblank_event *e, - unsigned long seq, ktime_t now) + u64 seq, ktime_t now) { struct timespec64 tv = ktime_to_timespec64(now); @@ -854,7 +868,7 @@ void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, assert_spin_locked(&dev->event_lock); e->pipe = pipe; - e->event.sequence = drm_crtc_accurate_vblank_count(crtc) + 1; + e->sequence = drm_crtc_accurate_vblank_count(crtc) + 1; e->event.crtc_id = crtc->base.id; list_add_tail(&e->base.link, &dev->vblank_event_list); } @@ -875,7 +889,8 @@ void drm_crtc_send_vblank_event(struct drm_crtc *crtc, struct drm_pending_vblank_event *e) { struct drm_device *dev = crtc->dev; - unsigned int seq, pipe = drm_crtc_index(crtc); + u64 seq; + unsigned int pipe = drm_crtc_index(crtc); ktime_t now; if (dev->num_crtcs > 0) { @@ -1088,7 +1103,7 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) ktime_t now; unsigned long irqflags; - unsigned int seq; + u64 seq; if (WARN_ON(pipe >= dev->num_crtcs)) return; @@ -1123,8 +1138,8 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) if (e->pipe != pipe) continue; DRM_DEBUG("Sending premature vblank event on disable: " - "wanted %u, current %u\n", - e->event.sequence, seq); + "wanted %llu, current %llu\n", + e->sequence, seq); list_del(&e->base.link); drm_vblank_put(dev, pipe); send_vblank_event(dev, e, seq, now); @@ -1296,12 +1311,13 @@ int drm_legacy_modeset_ctl_ioctl(struct drm_device *dev, void *data, return 0; } -static inline bool vblank_passed(u32 seq, u32 ref) +static inline bool vblank_passed(u64 seq, u64 ref) { return (seq - ref) <= (1 << 23); } static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, + u64 req_seq, union drm_wait_vblank *vblwait, struct drm_file *file_priv) { @@ -1309,7 +1325,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, struct drm_pending_vblank_event *e; ktime_t now; unsigned long flags; - unsigned int seq; + u64 seq; int ret; e = kzalloc(sizeof(*e), GFP_KERNEL); @@ -1344,21 +1360,20 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, seq = drm_vblank_count_and_time(dev, pipe, &now); - DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n", - vblwait->request.sequence, seq, pipe); + DRM_DEBUG("event on vblank count %llu, current %llu, crtc %u\n", + req_seq, seq, pipe); - trace_drm_vblank_event_queued(file_priv, pipe, - vblwait->request.sequence); + trace_drm_vblank_event_queued(file_priv, pipe, req_seq); - e->event.sequence = vblwait->request.sequence; - if (vblank_passed(seq, vblwait->request.sequence)) { + e->sequence = req_seq; + if (vblank_passed(seq, req_seq)) { drm_vblank_put(dev, pipe); send_vblank_event(dev, e, seq, now); vblwait->reply.sequence = seq; } else { /* drm_handle_vblank_events will call drm_vblank_put */ list_add_tail(&e->base.link, &dev->vblank_event_list); - vblwait->reply.sequence = vblwait->request.sequence; + vblwait->reply.sequence = req_seq; } spin_unlock_irqrestore(&dev->event_lock, flags); @@ -1384,6 +1399,22 @@ static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait) _DRM_VBLANK_NEXTONMISS)); } +/* + * Widen a 32-bit param to 64-bits. + * + * \param narrow 32-bit value (missing upper 32 bits) + * \param near 64-bit value that should be 'close' to near + * + * This function returns a 64-bit value using the lower 32-bits from + * 'narrow' and constructing the upper 32-bits so that the result is + * as close as possible to 'near'. + */ + +static u64 widen_32_to_64(u32 narrow, u64 near) +{ + return near + (s32) (narrow - near); +} + static void drm_wait_vblank_reply(struct drm_device *dev, unsigned int pipe, struct drm_wait_vblank_reply *reply) { @@ -1407,7 +1438,8 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, struct drm_vblank_crtc *vblank; union drm_wait_vblank *vblwait = data; int ret; - unsigned int flags, seq, pipe, high_pipe; + u64 req_seq, seq; + unsigned int flags, pipe, high_pipe; if (!dev->irq_enabled) return -EINVAL; @@ -1455,9 +1487,12 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { case _DRM_VBLANK_RELATIVE: - vblwait->request.sequence += seq; + req_seq = seq + vblwait->request.sequence; + vblwait->request.sequence = req_seq; vblwait->request.type &= ~_DRM_VBLANK_RELATIVE; + break; case _DRM_VBLANK_ABSOLUTE: + req_seq = widen_32_to_64(vblwait->request.sequence, seq); break; default: ret = -EINVAL; @@ -1465,22 +1500,25 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, } if ((flags & _DRM_VBLANK_NEXTONMISS) && - vblank_passed(seq, vblwait->request.sequence)) - vblwait->request.sequence = seq + 1; + vblank_passed(seq, req_seq)) { + req_seq = seq + 1; + vblwait->request.type &= ~_DRM_VBLANK_NEXTONMISS; + vblwait->request.sequence = req_seq; + } if (flags & _DRM_VBLANK_EVENT) { /* must hold on to the vblank ref until the event fires * drm_vblank_put will be called asynchronously */ - return drm_queue_vblank_event(dev, pipe, vblwait, file_priv); + return drm_queue_vblank_event(dev, pipe, req_seq, vblwait, file_priv); } - if (vblwait->request.sequence != seq) { - DRM_DEBUG("waiting on vblank count %u, crtc %u\n", - vblwait->request.sequence, pipe); + if (req_seq != seq) { + DRM_DEBUG("waiting on vblank count %llu, crtc %u\n", + req_seq, pipe); DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, vblank_passed(drm_vblank_count(dev, pipe), - vblwait->request.sequence) || + req_seq) || !READ_ONCE(vblank->enabled)); } @@ -1502,7 +1540,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) { struct drm_pending_vblank_event *e, *t; ktime_t now; - unsigned int seq; + u64 seq; assert_spin_locked(&dev->event_lock); @@ -1511,11 +1549,11 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { if (e->pipe != pipe) continue; - if (!vblank_passed(seq, e->event.sequence)) + if (!vblank_passed(seq, e->sequence)) continue; - DRM_DEBUG("vblank event on %u, current %u\n", - e->event.sequence, seq); + DRM_DEBUG("vblank event on %llu, current %llu\n", + e->sequence, seq); list_del(&e->base.link); drm_vblank_put(dev, pipe); diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 6a58e2e91a0f..cce53130510f 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -47,6 +47,10 @@ struct drm_pending_vblank_event { * @pipe: drm_crtc_index() of the &drm_crtc this event is for. */ unsigned int pipe; + /** + * @sequence: frame event should be triggered at + */ + u64 sequence; /** * @event: Actual event which will be sent to userspace. */ @@ -88,7 +92,7 @@ struct drm_vblank_crtc { /** * @count: Current software vblank counter. */ - u32 count; + u64 count; /** * @time: Vblank timestamp corresponding to @count. */ @@ -152,8 +156,8 @@ struct drm_vblank_crtc { }; int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs); -u32 drm_crtc_vblank_count(struct drm_crtc *crtc); -u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, +u64 drm_crtc_vblank_count(struct drm_crtc *crtc); +u64 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, ktime_t *vblanktime); void drm_crtc_send_vblank_event(struct drm_crtc *crtc, struct drm_pending_vblank_event *e); -- cgit v1.2.3-59-g8ed1b From bd386e51805632abed4a0873a84af35f0c6461e3 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Jul 2017 14:34:23 -0700 Subject: drm: Reorganize drm_pending_event to support future event types [v2] Place drm_event_vblank in a new union that includes that and a bare drm_event structure. This will allow new members of that union to be added in the future without changing code related to the existing vbl event type. Assignments to the crtc_id field are now done when the event is allocated, rather than when delievered. This way, delivery doesn't need to have the crtc ID available. v2: * Remove 'dev' argument from create_vblank_event It wasn't being used anyways, and if we need it in the future, we can always get it from crtc->dev. * Check for MODESETTING before looking for crtc in queue_vblank_event UMS drivers will oops if we try to get a crtc, so make sure we're modesetting before we try to find a crtc_id to fill into the event. Signed-off-by: Keith Packard Reviewed-by: Sean Paul Signed-off-by: Dave Airlie (cherry picked from commit dc695b85fde88eca3ef3b03fcd82f15b6bc6e462) --- drivers/gpu/drm/drm_atomic.c | 7 +++--- drivers/gpu/drm/drm_plane.c | 2 +- drivers/gpu/drm/drm_vblank.c | 41 +++++++++++++++++++++--------------- drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 4 ++-- drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 4 ++-- include/drm/drm_vblank.h | 8 ++++++- 6 files changed, 40 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 562494873ca5..c2da5585e201 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1812,7 +1812,7 @@ int drm_atomic_debugfs_init(struct drm_minor *minor) */ static struct drm_pending_vblank_event *create_vblank_event( - struct drm_device *dev, uint64_t user_data) + struct drm_crtc *crtc, uint64_t user_data) { struct drm_pending_vblank_event *e = NULL; @@ -1822,7 +1822,8 @@ static struct drm_pending_vblank_event *create_vblank_event( e->event.base.type = DRM_EVENT_FLIP_COMPLETE; e->event.base.length = sizeof(e->event); - e->event.user_data = user_data; + e->event.vbl.crtc_id = crtc->base.id; + e->event.vbl.user_data = user_data; return e; } @@ -2076,7 +2077,7 @@ static int prepare_crtc_signaling(struct drm_device *dev, if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT || fence_ptr) { struct drm_pending_vblank_event *e; - e = create_vblank_event(dev, arg->user_data); + e = create_vblank_event(crtc, arg->user_data); if (!e) return -ENOMEM; diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 8090e50607fa..8d9824804b0c 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -1025,7 +1025,7 @@ retry: } e->event.base.type = DRM_EVENT_FLIP_COMPLETE; e->event.base.length = sizeof(e->event); - e->event.user_data = page_flip->user_data; + e->event.vbl.user_data = page_flip->user_data; ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base); if (ret) { kfree(e); diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index c7e5a274f419..27b6db073a5c 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -804,20 +804,23 @@ static void send_vblank_event(struct drm_device *dev, struct drm_pending_vblank_event *e, u64 seq, ktime_t now) { - struct timespec64 tv = ktime_to_timespec64(now); - - e->event.sequence = seq; - /* - * e->event is a user space structure, with hardcoded unsigned - * 32-bit seconds/microseconds. This is safe as we always use - * monotonic timestamps since linux-4.15 - */ - e->event.tv_sec = tv.tv_sec; - e->event.tv_usec = tv.tv_nsec / 1000; - - trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, - e->event.sequence); + struct timespec64 tv; + switch (e->event.base.type) { + case DRM_EVENT_VBLANK: + case DRM_EVENT_FLIP_COMPLETE: + tv = ktime_to_timespec64(now); + e->event.vbl.sequence = seq; + /* + * e->event is a user space structure, with hardcoded unsigned + * 32-bit seconds/microseconds. This is safe as we always use + * monotonic timestamps since linux-4.15 + */ + e->event.vbl.tv_sec = tv.tv_sec; + e->event.vbl.tv_usec = tv.tv_nsec / 1000; + break; + } + trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, seq); drm_send_event_locked(dev, &e->base); } @@ -869,7 +872,6 @@ void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, e->pipe = pipe; e->sequence = drm_crtc_accurate_vblank_count(crtc) + 1; - e->event.crtc_id = crtc->base.id; list_add_tail(&e->base.link, &dev->vblank_event_list); } EXPORT_SYMBOL(drm_crtc_arm_vblank_event); @@ -901,7 +903,6 @@ void drm_crtc_send_vblank_event(struct drm_crtc *crtc, now = ktime_get(); } e->pipe = pipe; - e->event.crtc_id = crtc->base.id; send_vblank_event(dev, e, seq, now); } EXPORT_SYMBOL(drm_crtc_send_vblank_event); @@ -1336,8 +1337,14 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, e->pipe = pipe; e->event.base.type = DRM_EVENT_VBLANK; - e->event.base.length = sizeof(e->event); - e->event.user_data = vblwait->request.signal; + e->event.base.length = sizeof(e->event.vbl); + e->event.vbl.user_data = vblwait->request.signal; + e->event.vbl.crtc_id = 0; + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); + if (crtc) + e->event.vbl.crtc_id = crtc->base.id; + } spin_lock_irqsave(&dev->event_lock, flags); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index d1552d3e0652..bc5f6026573d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -360,8 +360,8 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, ret = vmw_event_fence_action_queue(file_priv, fence, &event->base, - &event->event.tv_sec, - &event->event.tv_usec, + &event->event.vbl.tv_sec, + &event->event.vbl.tv_usec, true); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index ca3afae2db1f..90b5437fd787 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -549,8 +549,8 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, ret = vmw_event_fence_action_queue(file_priv, fence, &event->base, - &event->event.tv_sec, - &event->event.tv_usec, + &event->event.vbl.tv_sec, + &event->event.vbl.tv_usec, true); vmw_fence_obj_unreference(&fence); } else { diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index cce53130510f..bf8e07035a0a 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -54,7 +54,10 @@ struct drm_pending_vblank_event { /** * @event: Actual event which will be sent to userspace. */ - struct drm_event_vblank event; + union { + struct drm_event base; + struct drm_event_vblank vbl; + } event; }; /** @@ -163,6 +166,9 @@ void drm_crtc_send_vblank_event(struct drm_crtc *crtc, struct drm_pending_vblank_event *e); void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, struct drm_pending_vblank_event *e); +void drm_vblank_set_event(struct drm_pending_vblank_event *e, + u64 *seq, + ktime_t *now); bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe); bool drm_crtc_handle_vblank(struct drm_crtc *crtc); int drm_crtc_vblank_get(struct drm_crtc *crtc); -- cgit v1.2.3-59-g8ed1b From b65f164d37cf6d4aac59b0e13c2e5c4cfe293fd2 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 19 Oct 2017 09:31:43 +0200 Subject: ipv6: let trace_fib6_table_lookup() dereference the fib table The perf traces for ipv6 routing code show a relevant cost around trace_fib6_table_lookup(), even if no trace is enabled. This is due to the fib6_table de-referencing currently performed by the caller. Let's the tracing code pay this overhead, passing to the trace helper the table pointer. This gives small but measurable performance improvement under UDP flood. Signed-off-by: Paolo Abeni Acked-by: Steven Rostedt (VMware) Acked-by: David Ahern Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- include/trace/events/fib6.h | 6 +++--- net/ipv6/route.c | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/trace/events/fib6.h b/include/trace/events/fib6.h index d60096cddb2a..b34bed17abc7 100644 --- a/include/trace/events/fib6.h +++ b/include/trace/events/fib6.h @@ -12,9 +12,9 @@ TRACE_EVENT(fib6_table_lookup, TP_PROTO(const struct net *net, const struct rt6_info *rt, - u32 tb_id, const struct flowi6 *flp), + struct fib6_table *table, const struct flowi6 *flp), - TP_ARGS(net, rt, tb_id, flp), + TP_ARGS(net, rt, table, flp), TP_STRUCT__entry( __field( u32, tb_id ) @@ -34,7 +34,7 @@ TRACE_EVENT(fib6_table_lookup, TP_fast_assign( struct in6_addr *in6; - __entry->tb_id = tb_id; + __entry->tb_id = table->tb6_id; __entry->oif = flp->flowi6_oif; __entry->iif = flp->flowi6_iif; __entry->tos = ip6_tclass(flp->flowlabel); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 074fac966018..46c59a53c53f 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -945,7 +945,7 @@ restart: rcu_read_unlock(); - trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); + trace_fib6_table_lookup(net, rt, table, fl6); return rt; @@ -1682,7 +1682,7 @@ redo_rt6_select: if (rt == net->ipv6.ip6_null_entry) { rcu_read_unlock(); dst_hold(&rt->dst); - trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); + trace_fib6_table_lookup(net, rt, table, fl6); return rt; } else if (rt->rt6i_flags & RTF_CACHE) { if (ip6_hold_safe(net, &rt, true)) { @@ -1690,7 +1690,7 @@ redo_rt6_select: rt6_dst_from_metrics_check(rt); } rcu_read_unlock(); - trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); + trace_fib6_table_lookup(net, rt, table, fl6); return rt; } else if (unlikely((fl6->flowi6_flags & FLOWI_FLAG_KNOWN_NH) && !(rt->rt6i_flags & RTF_GATEWAY))) { @@ -1726,7 +1726,7 @@ redo_rt6_select: } uncached_rt_out: - trace_fib6_table_lookup(net, uncached_rt, table->tb6_id, fl6); + trace_fib6_table_lookup(net, uncached_rt, table, fl6); return uncached_rt; } else { @@ -1754,7 +1754,7 @@ uncached_rt_out: } local_bh_enable(); rcu_read_unlock(); - trace_fib6_table_lookup(net, pcpu_rt, table->tb6_id, fl6); + trace_fib6_table_lookup(net, pcpu_rt, table, fl6); return pcpu_rt; } } @@ -2195,7 +2195,7 @@ out: rcu_read_unlock(); - trace_fib6_table_lookup(net, rt, table->tb6_id, fl6); + trace_fib6_table_lookup(net, rt, table, fl6); return rt; }; -- cgit v1.2.3-59-g8ed1b From 8c4083b30e56fc71b0e94c26374b32d95d5ea461 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 19 Oct 2017 15:50:29 +0200 Subject: net: sched: add block bind/unbind notif. and extended block_get/put Introduce new type of ndo_setup_tc message to propage binding/unbinding of a block to driver. Call this ndo whenever qdisc gets/puts a block. Alongside with this, there's need to propagate binder type from qdisc code down to the notifier. So introduce extended variants of block_get/put in order to pass this info. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + include/net/pkt_cls.h | 40 +++++++++++++++++++++++++++++++++ net/sched/cls_api.c | 56 ++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 94 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index bf014afcb914..4de5b08ee0fb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -775,6 +775,7 @@ enum tc_setup_type { TC_SETUP_CLSFLOWER, TC_SETUP_CLSMATCHALL, TC_SETUP_CLSBPF, + TC_SETUP_BLOCK, }; /* These structures hold the attributes of xdp state that are being passed diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 49a143e0fe65..41bc7d774047 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -17,13 +17,27 @@ struct tcf_walker { int register_tcf_proto_ops(struct tcf_proto_ops *ops); int unregister_tcf_proto_ops(struct tcf_proto_ops *ops); +enum tcf_block_binder_type { + TCF_BLOCK_BINDER_TYPE_UNSPEC, +}; + +struct tcf_block_ext_info { + enum tcf_block_binder_type binder_type; +}; + #ifdef CONFIG_NET_CLS struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, bool create); void tcf_chain_put(struct tcf_chain *chain); int tcf_block_get(struct tcf_block **p_block, struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q); +int tcf_block_get_ext(struct tcf_block **p_block, + struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, + struct tcf_block_ext_info *ei); void tcf_block_put(struct tcf_block *block); +void tcf_block_put_ext(struct tcf_block *block, + struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, + struct tcf_block_ext_info *ei); static inline struct Qdisc *tcf_block_q(struct tcf_block *block) { @@ -46,10 +60,25 @@ int tcf_block_get(struct tcf_block **p_block, return 0; } +static inline +int tcf_block_get_ext(struct tcf_block **p_block, + struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, + struct tcf_block_ext_info *ei) +{ + return 0; +} + static inline void tcf_block_put(struct tcf_block *block) { } +static inline +void tcf_block_put_ext(struct tcf_block *block, + struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, + struct tcf_block_ext_info *ei) +{ +} + static inline struct Qdisc *tcf_block_q(struct tcf_block *block) { return NULL; @@ -434,6 +463,17 @@ tcf_match_indev(struct sk_buff *skb, int ifindex) int tc_setup_cb_call(struct tcf_exts *exts, enum tc_setup_type type, void *type_data, bool err_stop); +enum tc_block_command { + TC_BLOCK_BIND, + TC_BLOCK_UNBIND, +}; + +struct tc_block_offload { + enum tc_block_command command; + enum tcf_block_binder_type binder_type; + struct tcf_block *block; +}; + struct tc_cls_common_offload { u32 chain_index; __be16 protocol; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 2e8e87fd9d97..92dce26d10e3 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -240,8 +240,36 @@ tcf_chain_filter_chain_ptr_set(struct tcf_chain *chain, chain->p_filter_chain = p_filter_chain; } -int tcf_block_get(struct tcf_block **p_block, - struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q) +static void tcf_block_offload_cmd(struct tcf_block *block, struct Qdisc *q, + struct tcf_block_ext_info *ei, + enum tc_block_command command) +{ + struct net_device *dev = q->dev_queue->dev; + struct tc_block_offload bo = {}; + + if (!tc_can_offload(dev)) + return; + bo.command = command; + bo.binder_type = ei->binder_type; + bo.block = block; + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo); +} + +static void tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q, + struct tcf_block_ext_info *ei) +{ + tcf_block_offload_cmd(block, q, ei, TC_BLOCK_BIND); +} + +static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q, + struct tcf_block_ext_info *ei) +{ + tcf_block_offload_cmd(block, q, ei, TC_BLOCK_UNBIND); +} + +int tcf_block_get_ext(struct tcf_block **p_block, + struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, + struct tcf_block_ext_info *ei) { struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL); struct tcf_chain *chain; @@ -259,6 +287,7 @@ int tcf_block_get(struct tcf_block **p_block, tcf_chain_filter_chain_ptr_set(chain, p_filter_chain); block->net = qdisc_net(q); block->q = q; + tcf_block_offload_bind(block, q, ei); *p_block = block; return 0; @@ -266,15 +295,28 @@ err_chain_create: kfree(block); return err; } +EXPORT_SYMBOL(tcf_block_get_ext); + +int tcf_block_get(struct tcf_block **p_block, + struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q) +{ + struct tcf_block_ext_info ei = {0, }; + + return tcf_block_get_ext(p_block, p_filter_chain, q, &ei); +} EXPORT_SYMBOL(tcf_block_get); -void tcf_block_put(struct tcf_block *block) +void tcf_block_put_ext(struct tcf_block *block, + struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, + struct tcf_block_ext_info *ei) { struct tcf_chain *chain, *tmp; if (!block) return; + tcf_block_offload_unbind(block, q, ei); + /* XXX: Standalone actions are not allowed to jump to any chain, and * bound actions should be all removed after flushing. However, * filters are destroyed in RCU callbacks, we have to hold the chains @@ -302,6 +344,14 @@ void tcf_block_put(struct tcf_block *block) tcf_chain_put(chain); kfree(block); } +EXPORT_SYMBOL(tcf_block_put_ext); + +void tcf_block_put(struct tcf_block *block) +{ + struct tcf_block_ext_info ei = {0, }; + + tcf_block_put_ext(block, NULL, block->q, &ei); +} EXPORT_SYMBOL(tcf_block_put); /* Main classifier routine: scans classifier chain attached -- cgit v1.2.3-59-g8ed1b From 6e40cf2d4dee9dc22ff398041ce876bef8172dea Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 19 Oct 2017 15:50:30 +0200 Subject: net: sched: use extended variants of block_get/put in ingress and clsact qdiscs Use previously introduced extended variants of block get and put functions. This allows to specify a binder types specific to clsact ingress/egress which is useful for drivers to distinguish who actually got the block. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 2 ++ net/sched/sch_ingress.c | 36 +++++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 41bc7d774047..5c50af8f7183 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -19,6 +19,8 @@ int unregister_tcf_proto_ops(struct tcf_proto_ops *ops); enum tcf_block_binder_type { TCF_BLOCK_BINDER_TYPE_UNSPEC, + TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS, + TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS, }; struct tcf_block_ext_info { diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 9ccc1b89b0d9..b599db26d34b 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -20,6 +20,7 @@ struct ingress_sched_data { struct tcf_block *block; + struct tcf_block_ext_info block_info; }; static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg) @@ -59,7 +60,10 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt) struct net_device *dev = qdisc_dev(sch); int err; - err = tcf_block_get(&q->block, &dev->ingress_cl_list, sch); + q->block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; + + err = tcf_block_get_ext(&q->block, &dev->ingress_cl_list, + sch, &q->block_info); if (err) return err; @@ -72,8 +76,10 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt) static void ingress_destroy(struct Qdisc *sch) { struct ingress_sched_data *q = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); - tcf_block_put(q->block); + tcf_block_put_ext(q->block, &dev->ingress_cl_list, + sch, &q->block_info); net_dec_ingress_queue(); } @@ -114,6 +120,8 @@ static struct Qdisc_ops ingress_qdisc_ops __read_mostly = { struct clsact_sched_data { struct tcf_block *ingress_block; struct tcf_block *egress_block; + struct tcf_block_ext_info ingress_block_info; + struct tcf_block_ext_info egress_block_info; }; static unsigned long clsact_find(struct Qdisc *sch, u32 classid) @@ -153,13 +161,19 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt) struct net_device *dev = qdisc_dev(sch); int err; - err = tcf_block_get(&q->ingress_block, &dev->ingress_cl_list, sch); + q->ingress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; + + err = tcf_block_get_ext(&q->ingress_block, &dev->ingress_cl_list, + sch, &q->ingress_block_info); if (err) return err; - err = tcf_block_get(&q->egress_block, &dev->egress_cl_list, sch); + q->egress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS; + + err = tcf_block_get_ext(&q->egress_block, &dev->egress_cl_list, + sch, &q->egress_block_info); if (err) - return err; + goto err_egress_block_get; net_inc_ingress_queue(); net_inc_egress_queue(); @@ -167,14 +181,22 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt) sch->flags |= TCQ_F_CPUSTATS; return 0; + +err_egress_block_get: + tcf_block_put_ext(q->ingress_block, &dev->ingress_cl_list, + sch, &q->ingress_block_info); + return err; } static void clsact_destroy(struct Qdisc *sch) { struct clsact_sched_data *q = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); - tcf_block_put(q->egress_block); - tcf_block_put(q->ingress_block); + tcf_block_put_ext(q->egress_block, &dev->egress_cl_list, + sch, &q->egress_block_info); + tcf_block_put_ext(q->ingress_block, &dev->ingress_cl_list, + sch, &q->ingress_block_info); net_dec_ingress_queue(); net_dec_egress_queue(); -- cgit v1.2.3-59-g8ed1b From acb674428c3d57bccbe3f4a1a7a009f6d73e9f41 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 19 Oct 2017 15:50:31 +0200 Subject: net: sched: introduce per-block callbacks Introduce infrastructure that allows drivers to register callbacks that are called whenever tc would offload inserted rule for a specific block. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 81 +++++++++++++++++++++++++++++++++++ include/net/sch_generic.h | 1 + net/sched/cls_api.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 187 insertions(+) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 5c50af8f7183..4bc6b1cc245d 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -27,6 +27,8 @@ struct tcf_block_ext_info { enum tcf_block_binder_type binder_type; }; +struct tcf_block_cb; + #ifdef CONFIG_NET_CLS struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, bool create); @@ -51,6 +53,21 @@ static inline struct net_device *tcf_block_dev(struct tcf_block *block) return tcf_block_q(block)->dev_queue->dev; } +void *tcf_block_cb_priv(struct tcf_block_cb *block_cb); +struct tcf_block_cb *tcf_block_cb_lookup(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident); +void tcf_block_cb_incref(struct tcf_block_cb *block_cb); +unsigned int tcf_block_cb_decref(struct tcf_block_cb *block_cb); +struct tcf_block_cb *__tcf_block_cb_register(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident, + void *cb_priv); +int tcf_block_cb_register(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident, + void *cb_priv); +void __tcf_block_cb_unregister(struct tcf_block_cb *block_cb); +void tcf_block_cb_unregister(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident); + int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res, bool compat_mode); @@ -91,6 +108,70 @@ static inline struct net_device *tcf_block_dev(struct tcf_block *block) return NULL; } +static inline +int tc_setup_cb_block_register(struct tcf_block *block, tc_setup_cb_t *cb, + void *cb_priv) +{ + return 0; +} + +static inline +void tc_setup_cb_block_unregister(struct tcf_block *block, tc_setup_cb_t *cb, + void *cb_priv) +{ +} + +static inline +void *tcf_block_cb_priv(struct tcf_block_cb *block_cb) +{ + return NULL; +} + +static inline +struct tcf_block_cb *tcf_block_cb_lookup(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident) +{ + return NULL; +} + +static inline +void tcf_block_cb_incref(struct tcf_block_cb *block_cb) +{ +} + +static inline +unsigned int tcf_block_cb_decref(struct tcf_block_cb *block_cb) +{ + return 0; +} + +static inline +struct tcf_block_cb *__tcf_block_cb_register(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident, + void *cb_priv) +{ + return NULL; +} + +static inline +int tcf_block_cb_register(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident, + void *cb_priv) +{ + return 0; +} + +static inline +void __tcf_block_cb_unregister(struct tcf_block_cb *block_cb) +{ +} + +static inline +void tcf_block_cb_unregister(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident) +{ +} + static inline int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp, struct tcf_result *res, bool compat_mode) { diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 0aea9e23e97a..031dffd5836c 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -272,6 +272,7 @@ struct tcf_block { struct list_head chain_list; struct net *net; struct Qdisc *q; + struct list_head cb_list; }; static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 92dce26d10e3..b16c79c47be5 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -278,6 +278,8 @@ int tcf_block_get_ext(struct tcf_block **p_block, if (!block) return -ENOMEM; INIT_LIST_HEAD(&block->chain_list); + INIT_LIST_HEAD(&block->cb_list); + /* Create chain 0 by default, it has to be always present. */ chain = tcf_chain_create(block, 0); if (!chain) { @@ -354,6 +356,109 @@ void tcf_block_put(struct tcf_block *block) } EXPORT_SYMBOL(tcf_block_put); +struct tcf_block_cb { + struct list_head list; + tc_setup_cb_t *cb; + void *cb_ident; + void *cb_priv; + unsigned int refcnt; +}; + +void *tcf_block_cb_priv(struct tcf_block_cb *block_cb) +{ + return block_cb->cb_priv; +} +EXPORT_SYMBOL(tcf_block_cb_priv); + +struct tcf_block_cb *tcf_block_cb_lookup(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident) +{ struct tcf_block_cb *block_cb; + + list_for_each_entry(block_cb, &block->cb_list, list) + if (block_cb->cb == cb && block_cb->cb_ident == cb_ident) + return block_cb; + return NULL; +} +EXPORT_SYMBOL(tcf_block_cb_lookup); + +void tcf_block_cb_incref(struct tcf_block_cb *block_cb) +{ + block_cb->refcnt++; +} +EXPORT_SYMBOL(tcf_block_cb_incref); + +unsigned int tcf_block_cb_decref(struct tcf_block_cb *block_cb) +{ + return --block_cb->refcnt; +} +EXPORT_SYMBOL(tcf_block_cb_decref); + +struct tcf_block_cb *__tcf_block_cb_register(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident, + void *cb_priv) +{ + struct tcf_block_cb *block_cb; + + block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL); + if (!block_cb) + return NULL; + block_cb->cb = cb; + block_cb->cb_ident = cb_ident; + block_cb->cb_priv = cb_priv; + list_add(&block_cb->list, &block->cb_list); + return block_cb; +} +EXPORT_SYMBOL(__tcf_block_cb_register); + +int tcf_block_cb_register(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident, + void *cb_priv) +{ + struct tcf_block_cb *block_cb; + + block_cb = __tcf_block_cb_register(block, cb, cb_ident, cb_priv); + return block_cb ? 0 : -ENOMEM; +} +EXPORT_SYMBOL(tcf_block_cb_register); + +void __tcf_block_cb_unregister(struct tcf_block_cb *block_cb) +{ + list_del(&block_cb->list); + kfree(block_cb); +} +EXPORT_SYMBOL(__tcf_block_cb_unregister); + +void tcf_block_cb_unregister(struct tcf_block *block, + tc_setup_cb_t *cb, void *cb_ident) +{ + struct tcf_block_cb *block_cb; + + block_cb = tcf_block_cb_lookup(block, cb, cb_ident); + if (!block_cb) + return; + __tcf_block_cb_unregister(block_cb); +} +EXPORT_SYMBOL(tcf_block_cb_unregister); + +static int tcf_block_cb_call(struct tcf_block *block, enum tc_setup_type type, + void *type_data, bool err_stop) +{ + struct tcf_block_cb *block_cb; + int ok_count = 0; + int err; + + list_for_each_entry(block_cb, &block->cb_list, list) { + err = block_cb->cb(type, type_data, block_cb->cb_priv); + if (err) { + if (err_stop) + return err; + } else { + ok_count++; + } + } + return ok_count; +} + /* Main classifier routine: scans classifier chain attached * to this qdisc, (optionally) tests for protocol and asks * specific classifiers. -- cgit v1.2.3-59-g8ed1b From 208c0f4b5237f1d6611b2c679a8022d6901577d6 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 19 Oct 2017 15:50:32 +0200 Subject: net: sched: use tc_setup_cb_call to call per-block callbacks Extend the tc_setup_cb_call entrypoint function originally used only for action egress devices callbacks to call per-block callbacks as well. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 4 ++-- net/sched/cls_api.c | 21 ++++++++++++++++++--- net/sched/cls_flower.c | 9 ++++++--- 3 files changed, 26 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 4bc6b1cc245d..fcca5a9d9880 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -543,8 +543,8 @@ tcf_match_indev(struct sk_buff *skb, int ifindex) } #endif /* CONFIG_NET_CLS_IND */ -int tc_setup_cb_call(struct tcf_exts *exts, enum tc_setup_type type, - void *type_data, bool err_stop); +int tc_setup_cb_call(struct tcf_block *block, struct tcf_exts *exts, + enum tc_setup_type type, void *type_data, bool err_stop); enum tc_block_command { TC_BLOCK_BIND, diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index b16c79c47be5..cdfdc24b89cf 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1206,10 +1206,25 @@ static int tc_exts_setup_cb_egdev_call(struct tcf_exts *exts, return ok_count; } -int tc_setup_cb_call(struct tcf_exts *exts, enum tc_setup_type type, - void *type_data, bool err_stop) +int tc_setup_cb_call(struct tcf_block *block, struct tcf_exts *exts, + enum tc_setup_type type, void *type_data, bool err_stop) { - return tc_exts_setup_cb_egdev_call(exts, type, type_data, err_stop); + int ok_count; + int ret; + + ret = tcf_block_cb_call(block, type, type_data, err_stop); + if (ret < 0) + return ret; + ok_count = ret; + + if (!exts) + return ok_count; + ret = tc_exts_setup_cb_egdev_call(exts, type, type_data, err_stop); + if (ret < 0) + return ret; + ok_count += ret; + + return ok_count; } EXPORT_SYMBOL(tc_setup_cb_call); diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 5b7bb968d1d4..76b4e0a1c92f 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -201,6 +201,7 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) { struct tc_cls_flower_offload cls_flower = {}; struct net_device *dev = tp->q->dev_queue->dev; + struct tcf_block *block = tp->chain->block; tc_cls_common_offload_init(&cls_flower.common, tp); cls_flower.command = TC_CLSFLOWER_DESTROY; @@ -209,7 +210,7 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) if (tc_can_offload(dev)) dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, &cls_flower); - tc_setup_cb_call(&f->exts, TC_SETUP_CLSFLOWER, + tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER, &cls_flower, false); } @@ -220,6 +221,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, { struct net_device *dev = tp->q->dev_queue->dev; struct tc_cls_flower_offload cls_flower = {}; + struct tcf_block *block = tp->chain->block; bool skip_sw = tc_skip_sw(f->flags); int err; @@ -242,7 +244,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, } } - err = tc_setup_cb_call(&f->exts, TC_SETUP_CLSFLOWER, + err = tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER, &cls_flower, skip_sw); if (err < 0) { fl_hw_destroy_filter(tp, f); @@ -261,6 +263,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) { struct tc_cls_flower_offload cls_flower = {}; struct net_device *dev = tp->q->dev_queue->dev; + struct tcf_block *block = tp->chain->block; tc_cls_common_offload_init(&cls_flower.common, tp); cls_flower.command = TC_CLSFLOWER_STATS; @@ -270,7 +273,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) if (tc_can_offload(dev)) dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, &cls_flower); - tc_setup_cb_call(&f->exts, TC_SETUP_CLSFLOWER, + tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER, &cls_flower, false); } -- cgit v1.2.3-59-g8ed1b From d58d31a118690b578897749feda48416ac10ca43 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 19 Oct 2017 15:50:47 +0200 Subject: net: sched: remove unused classid field from tc_cls_common_offload It is no longer used by the drivers, so remove it. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index fcca5a9d9880..04caa246e747 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -561,7 +561,6 @@ struct tc_cls_common_offload { u32 chain_index; __be16 protocol; u32 prio; - u32 classid; }; static inline void @@ -571,7 +570,6 @@ tc_cls_common_offload_init(struct tc_cls_common_offload *cls_common, cls_common->chain_index = tp->chain->index; cls_common->protocol = tp->protocol; cls_common->prio = tp->prio; - cls_common->classid = tp->classid; } struct tc_cls_u32_knode { -- cgit v1.2.3-59-g8ed1b From fa71212e91811ac67014ad19d4fc3b3c3446ccf7 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Thu, 19 Oct 2017 15:50:48 +0200 Subject: net: sched: remove unused is_classid_clsact_ingress/egress helpers These helpers are no longer in use by drivers, so remove them. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_sched.h | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'include') diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 2d234af15f3e..b8ecafce4ba1 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -135,19 +135,6 @@ static inline unsigned int psched_mtu(const struct net_device *dev) return dev->mtu + dev->hard_header_len; } -static inline bool is_classid_clsact_ingress(u32 classid) -{ - /* This also returns true for ingress qdisc */ - return TC_H_MAJ(classid) == TC_H_MAJ(TC_H_CLSACT) && - TC_H_MIN(classid) != TC_H_MIN(TC_H_MIN_EGRESS); -} - -static inline bool is_classid_clsact_egress(u32 classid) -{ - return TC_H_MAJ(classid) == TC_H_MAJ(TC_H_CLSACT) && - TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_EGRESS); -} - static inline struct net *qdisc_net(struct Qdisc *q) { return dev_net(q->dev_queue->dev); -- cgit v1.2.3-59-g8ed1b From 7feb2f786a46d34cac760d5e246dfbdafafa0e96 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 12 Oct 2017 18:49:38 -0500 Subject: ASoC: move ACPI common code out of Intel/sst tree ACPI support is not specific to the Intel/SST driver. Move the enumeration and matching code which is not hardware-dependent to sound/soc and rename relevant sst_acpi_ structures and functions with snd_soc_acpi_ prefix soc-acpi.h is protected by a #ifndef __LINUX_SND_SOC_ACPI_H for consistency with all other SoC .h files: grep -L __LINUX include/sound/soc* | wc -l 0 grep __LINUX include/sound/soc* | wc -l 14 Signed-off-by: Pierre-Louis Bossart Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc-acpi.h | 100 ++++++++++++++++++ sound/soc/Kconfig | 3 + sound/soc/Makefile | 6 ++ sound/soc/intel/Kconfig | 1 + sound/soc/intel/atom/sst/sst_acpi.c | 24 ++--- sound/soc/intel/boards/bytcht_da7213.c | 6 +- sound/soc/intel/boards/bytcht_es8316.c | 2 +- sound/soc/intel/boards/bytcr_rt5640.c | 11 +- sound/soc/intel/boards/bytcr_rt5651.c | 6 +- sound/soc/intel/boards/cht_bsw_rt5645.c | 11 +- sound/soc/intel/boards/cht_bsw_rt5672.c | 7 +- sound/soc/intel/common/Makefile | 2 - sound/soc/intel/common/sst-acpi.c | 18 ++-- sound/soc/intel/common/sst-acpi.h | 82 --------------- sound/soc/intel/common/sst-match-acpi.c | 173 ------------------------------- sound/soc/intel/skylake/skl.c | 40 ++++---- sound/soc/soc-acpi.c | 175 ++++++++++++++++++++++++++++++++ 17 files changed, 349 insertions(+), 318 deletions(-) create mode 100644 include/sound/soc-acpi.h delete mode 100644 sound/soc/intel/common/sst-acpi.h delete mode 100644 sound/soc/intel/common/sst-match-acpi.c create mode 100644 sound/soc/soc-acpi.c (limited to 'include') diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h new file mode 100644 index 000000000000..918d78e8c8d6 --- /dev/null +++ b/include/sound/soc-acpi.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2013-15, Intel Corporation. All rights reserved. + * + * 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. + * + */ + +#ifndef __LINUX_SND_SOC_ACPI_H +#define __LINUX_SND_SOC_ACPI_H + +#include +#include + +struct snd_soc_acpi_package_context { + char *name; /* package name */ + int length; /* number of elements */ + struct acpi_buffer *format; + struct acpi_buffer *state; + bool data_valid; +}; + +#if IS_ENABLED(CONFIG_ACPI) +/* translation fron HID to I2C name, needed for DAI codec_name */ +const char *snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); +bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], + struct snd_soc_acpi_package_context *ctx); +#else +static inline const char * +snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) +{ + return NULL; +} +static inline bool +snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], + struct snd_soc_acpi_package_context *ctx) +{ + return false; +} +#endif + +/* acpi match */ +struct snd_soc_acpi_mach * +snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines); + +/* acpi check hid */ +bool snd_soc_acpi_check_hid(const u8 hid[ACPI_ID_LEN]); + +/** + * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are + * related to the hardware, except for the firmware and topology file names. + * + * @id: ACPI ID (usually the codec's) used to find a matching machine driver. + * @drv_name: machine driver name + * @fw_filename: firmware file name. + * @board: board name + * @machine_quirk: pointer to quirk, usually based on DMI information when + * ACPI ID alone is not sufficient, wrong or misleading + * @quirk_data: data used to uniquely identify a machine, usually a list of + * audio codecs whose presence if checked with ACPI + * @pdata: intended for platform data or machine specific-ops. This structure + * is not constant since this field may be updated at run-time + */ +/* Descriptor for SST ASoC machine driver */ +struct snd_soc_acpi_mach { + const u8 id[ACPI_ID_LEN]; + const char *drv_name; + const char *fw_filename; + const char *board; + struct snd_soc_acpi_mach * (*machine_quirk)(void *arg); + const void *quirk_data; + void *pdata; +}; + +#define SND_SOC_ACPI_MAX_CODECS 3 + +/** + * struct snd_soc_acpi_codecs: Structure to hold secondary codec information + * apart from the matched one, this data will be passed to the quirk function + * to match with the ACPI detected devices + * + * @num_codecs: number of secondary codecs used in the platform + * @codecs: holds the codec IDs + * + */ +struct snd_soc_acpi_codecs { + int num_codecs; + u8 codecs[SND_SOC_ACPI_MAX_CODECS][ACPI_ID_LEN]; +}; + +/* check all codecs */ +struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg); + +#endif diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index c0abad2067e1..d22758165496 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -36,6 +36,9 @@ config SND_SOC_COMPRESS config SND_SOC_TOPOLOGY bool +config SND_SOC_ACPI + tristate + # All the supported SoCs source "sound/soc/adi/Kconfig" source "sound/soc/amd/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 39c27a58158d..95f2a73f9aed 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -14,6 +14,12 @@ ifneq ($(CONFIG_SND_SOC_AC97_BUS),) snd-soc-core-objs += soc-ac97.o endif +ifneq ($(CONFIG_SND_SOC_ACPI),) +snd-soc-acpi-objs := soc-acpi.o +endif + +obj-$(CONFIG_SND_SOC_ACPI) += snd-soc-acpi.o + obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ obj-$(CONFIG_SND_SOC) += generic/ diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index b3c7f554ec30..d03199cc61e0 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -41,6 +41,7 @@ config SND_SOC_INTEL_SST_ACPI config SND_SOC_INTEL_SST_MATCH tristate + select SND_SOC_ACPI if ACPI config SND_SOC_INTEL_HASWELL tristate diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index 4d85ead243f1..8d82bc8dc4ac 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -40,9 +40,9 @@ #include #include #include +#include #include "../sst-mfld-platform.h" #include "../../common/sst-dsp.h" -#include "../../common/sst-acpi.h" #include "sst.h" /* LPE viewpoint addresses */ @@ -284,7 +284,7 @@ static int sst_acpi_probe(struct platform_device *pdev) int ret = 0; struct intel_sst_drv *ctx; const struct acpi_device_id *id; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; struct platform_device *mdev; struct platform_device *plat_dev; struct sst_platform_info *pdata; @@ -296,8 +296,8 @@ static int sst_acpi_probe(struct platform_device *pdev) return -ENODEV; dev_dbg(dev, "for %s\n", id->id); - mach = (struct sst_acpi_mach *)id->driver_data; - mach = sst_acpi_find_machine(mach); + mach = (struct snd_soc_acpi_mach *)id->driver_data; + mach = snd_soc_acpi_find_machine(mach); if (mach == NULL) { dev_err(dev, "No matching machine driver found\n"); return -ENODEV; @@ -435,7 +435,7 @@ static const struct dmi_system_id cht_table[] = { }; -static struct sst_acpi_mach cht_surface_mach = { +static struct snd_soc_acpi_mach cht_surface_mach = { .id = "10EC5640", .drv_name = "cht-bsw-rt5645", .fw_filename = "intel/fw_sst_22a8.bin", @@ -443,7 +443,7 @@ static struct sst_acpi_mach cht_surface_mach = { .pdata = &chv_platform_data, }; -static struct sst_acpi_mach byt_thinkpad_10 = { +static struct snd_soc_acpi_mach byt_thinkpad_10 = { .id = "10EC5640", .drv_name = "cht-bsw-rt5672", .fw_filename = "intel/fw_sst_0f28.bin", @@ -451,9 +451,9 @@ static struct sst_acpi_mach byt_thinkpad_10 = { .pdata = &byt_rvp_platform_data, }; -static struct sst_acpi_mach *cht_quirk(void *arg) +static struct snd_soc_acpi_mach *cht_quirk(void *arg) { - struct sst_acpi_mach *mach = arg; + struct snd_soc_acpi_mach *mach = arg; dmi_check_system(cht_table); @@ -463,9 +463,9 @@ static struct sst_acpi_mach *cht_quirk(void *arg) return mach; } -static struct sst_acpi_mach *byt_quirk(void *arg) +static struct snd_soc_acpi_mach *byt_quirk(void *arg) { - struct sst_acpi_mach *mach = arg; + struct snd_soc_acpi_mach *mach = arg; dmi_check_system(byt_table); @@ -476,7 +476,7 @@ static struct sst_acpi_mach *byt_quirk(void *arg) } -static struct sst_acpi_mach sst_acpi_bytcr[] = { +static struct snd_soc_acpi_mach sst_acpi_bytcr[] = { { .id = "10EC5640", .drv_name = "bytcr_rt5640", @@ -560,7 +560,7 @@ static struct sst_acpi_mach sst_acpi_bytcr[] = { }; /* Cherryview-based platforms: CherryTrail and Braswell */ -static struct sst_acpi_mach sst_acpi_chv[] = { +static struct snd_soc_acpi_mach sst_acpi_chv[] = { { .id = "10EC5670", .drv_name = "cht-bsw-rt5672", diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c index 296b50640f46..c4d82ad41bd7 100644 --- a/sound/soc/intel/boards/bytcht_da7213.c +++ b/sound/soc/intel/boards/bytcht_da7213.c @@ -27,9 +27,9 @@ #include #include #include +#include #include "../../codecs/da7213.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" static const struct snd_kcontrol_new controls[] = { SOC_DAPM_PIN_SWITCH("Headphone Jack"), @@ -224,7 +224,7 @@ static char codec_name[16]; /* i2c-:00 with HID being 8 chars */ static int bytcht_da7213_probe(struct platform_device *pdev) { struct snd_soc_card *card; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; const char *i2c_name = NULL; int dai_index = 0; int ret_val = 0; @@ -243,7 +243,7 @@ static int bytcht_da7213_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = sst_acpi_find_name_from_hid(mach->id); + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); if (i2c_name) { snprintf(codec_name, sizeof(codec_name), "%s%s", "i2c-", i2c_name); diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 6acacc0aeedb..8088396717e3 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -29,8 +29,8 @@ #include #include #include +#include #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" #include "../common/sst-dsp.h" struct byt_cht_es8316_private { diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 3eeb02036aa1..f2c0fc415e52 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -32,9 +32,9 @@ #include #include #include +#include #include "../../codecs/rt5640.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" #include "../common/sst-dsp.h" enum { @@ -737,7 +737,7 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) { struct byt_rt5640_private *priv; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; const char *i2c_name = NULL; int ret_val = 0; int dai_index = 0; @@ -762,7 +762,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = sst_acpi_find_name_from_hid(mach->id); + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); if (i2c_name) { snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name), "%s%s", "i2c-", i2c_name); @@ -796,7 +796,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) /* format specified: 2 64-bit integers */ struct acpi_buffer format = {sizeof("NN"), "NN"}; struct acpi_buffer state = {0, NULL}; - struct sst_acpi_package_context pkg_ctx; + struct snd_soc_acpi_package_context pkg_ctx; bool pkg_found = false; state.length = sizeof(chan_package); @@ -808,7 +808,8 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) pkg_ctx.state = &state; pkg_ctx.data_valid = false; - pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx); + pkg_found = snd_soc_acpi_find_package_from_hid(mach->id, + &pkg_ctx); if (pkg_found) { if (chan_package.aif_value == 1) { dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index 1dad5c98c9ef..51de1bbe8ecd 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -30,9 +30,9 @@ #include #include #include +#include #include "../../codecs/rt5651.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" enum { BYT_RT5651_DMIC_MAP, @@ -443,7 +443,7 @@ static char byt_rt5651_codec_name[16]; /* i2c-:00 with HID being 8 chars */ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) { struct byt_rt5651_private *priv; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; const char *i2c_name = NULL; int ret_val = 0; int dai_index = 0; @@ -468,7 +468,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = sst_acpi_find_name_from_hid(mach->id); + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); if (i2c_name) { snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name), "%s%s", "i2c-", i2c_name); diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index 83c923c39393..18d129caa974 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -32,9 +32,9 @@ #include #include #include +#include #include "../../codecs/rt5645.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI1 "rt5645-aif1" @@ -523,7 +523,7 @@ struct acpi_chan_package { /* ACPICA seems to require 64 bit integers */ static int snd_cht_mc_probe(struct platform_device *pdev) { struct snd_soc_card *card = snd_soc_cards[0].soc_card; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; struct cht_mc_private *drv; const char *i2c_name = NULL; bool found = false; @@ -566,7 +566,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) } /* fixup codec name based on HID */ - i2c_name = sst_acpi_find_name_from_hid(mach->id); + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); if (i2c_name) { snprintf(cht_rt5645_codec_name, sizeof(cht_rt5645_codec_name), "%s%s", "i2c-", i2c_name); @@ -599,7 +599,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* format specified: 2 64-bit integers */ struct acpi_buffer format = {sizeof("NN"), "NN"}; struct acpi_buffer state = {0, NULL}; - struct sst_acpi_package_context pkg_ctx; + struct snd_soc_acpi_package_context pkg_ctx; bool pkg_found = false; state.length = sizeof(chan_package); @@ -611,7 +611,8 @@ static int snd_cht_mc_probe(struct platform_device *pdev) pkg_ctx.state = &state; pkg_ctx.data_valid = false; - pkg_found = sst_acpi_find_package_from_hid(mach->id, &pkg_ctx); + pkg_found = snd_soc_acpi_find_package_from_hid(mach->id, + &pkg_ctx); if (pkg_found) { if (chan_package.aif_value == 1) { dev_info(&pdev->dev, "BIOS Routing: AIF1 connected\n"); diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index d135640702e9..f8f21eee9b2d 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -24,9 +24,10 @@ #include #include #include +#include #include "../../codecs/rt5670.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-acpi.h" + /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ #define CHT_PLAT_CLK_3_HZ 19200000 @@ -383,7 +384,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) { int ret_val = 0; struct cht_mc_private *drv; - struct sst_acpi_mach *mach = pdev->dev.platform_data; + struct snd_soc_acpi_mach *mach = pdev->dev.platform_data; const char *i2c_name; int i; @@ -395,7 +396,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) /* fixup codec name based on HID */ if (mach) { - i2c_name = sst_acpi_find_name_from_hid(mach->id); + i2c_name = snd_soc_acpi_find_name_from_hid(mach->id); if (i2c_name) { snprintf(drv->codec_name, sizeof(drv->codec_name), "i2c-%s", i2c_name); diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 1a35149bcad7..2c958ec3f4fd 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,10 +1,8 @@ snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o -snd-soc-sst-match-objs := sst-match-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-firmware-objs := sst-firmware.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o -obj-$(CONFIG_SND_SOC_INTEL_SST_MATCH) += snd-soc-sst-match.o obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 1285cc597b6b..9866998de8be 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -21,7 +21,7 @@ #include #include "sst-dsp.h" -#include "sst-acpi.h" +#include #define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000 #define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000 @@ -30,7 +30,7 @@ /* Descriptor for setting up SST platform data */ struct sst_acpi_desc { const char *drv_name; - struct sst_acpi_mach *machines; + struct snd_soc_acpi_mach *machines; /* Platform resource indexes. Must set to -1 if not used */ int resindex_lpe_base; int resindex_pcicfg_base; @@ -49,7 +49,7 @@ struct sst_acpi_priv { struct platform_device *pdev_pcm; struct sst_pdata sst_pdata; struct sst_acpi_desc *desc; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; }; static void sst_acpi_fw_cb(const struct firmware *fw, void *context) @@ -59,7 +59,7 @@ static void sst_acpi_fw_cb(const struct firmware *fw, void *context) struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev); struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata; struct sst_acpi_desc *desc = sst_acpi->desc; - struct sst_acpi_mach *mach = sst_acpi->mach; + struct snd_soc_acpi_mach *mach = sst_acpi->mach; sst_pdata->fw = fw; if (!fw) { @@ -85,7 +85,7 @@ static int sst_acpi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct sst_acpi_priv *sst_acpi; struct sst_pdata *sst_pdata; - struct sst_acpi_mach *mach; + struct snd_soc_acpi_mach *mach; struct sst_acpi_desc *desc; struct resource *mmio; int ret = 0; @@ -99,7 +99,7 @@ static int sst_acpi_probe(struct platform_device *pdev) return -ENODEV; desc = (struct sst_acpi_desc *)id->driver_data; - mach = sst_acpi_find_machine(desc->machines); + mach = snd_soc_acpi_find_machine(desc->machines); if (mach == NULL) { dev_err(dev, "No matching ASoC machine driver found\n"); return -ENODEV; @@ -179,7 +179,7 @@ static int sst_acpi_remove(struct platform_device *pdev) return 0; } -static struct sst_acpi_mach haswell_machines[] = { +static struct snd_soc_acpi_mach haswell_machines[] = { { "INT33CA", "haswell-audio", "intel/IntcSST1.bin", NULL, NULL, NULL }, {} }; @@ -197,7 +197,7 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = { .dma_size = SST_LPT_DSP_DMA_SIZE, }; -static struct sst_acpi_mach broadwell_machines[] = { +static struct snd_soc_acpi_mach broadwell_machines[] = { { "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL }, { "RT5677CE", "bdw-rt5677", "intel/IntcSST2.bin", NULL, NULL, NULL }, {} @@ -217,7 +217,7 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = { }; #if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI) -static struct sst_acpi_mach baytrail_machines[] = { +static struct snd_soc_acpi_mach baytrail_machines[] = { { "10EC5640", "byt-rt5640", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, { "193C9890", "byt-max98090", "intel/fw_sst_0f28.bin-48kHz_i2s_master", NULL, NULL, NULL }, {} diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h deleted file mode 100644 index afe9b87b8bd5..000000000000 --- a/sound/soc/intel/common/sst-acpi.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2013-15, Intel Corporation. All rights reserved. - * - * 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. - * - */ - -#include -#include - -struct sst_acpi_package_context { - char *name; /* package name */ - int length; /* number of elements */ - struct acpi_buffer *format; - struct acpi_buffer *state; - bool data_valid; -}; - -#if IS_ENABLED(CONFIG_ACPI) -/* translation fron HID to I2C name, needed for DAI codec_name */ -const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]); -bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], - struct sst_acpi_package_context *ctx); -#else -static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) -{ - return NULL; -} -static inline bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], - struct sst_acpi_package_context *ctx) -{ - return false; -} -#endif - -/* acpi match */ -struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines); - -/* acpi check hid */ -bool sst_acpi_check_hid(const u8 hid[ACPI_ID_LEN]); - -/* Descriptor for SST ASoC machine driver */ -struct sst_acpi_mach { - /* ACPI ID for the matching machine driver. Audio codec for instance */ - const u8 id[ACPI_ID_LEN]; - /* machine driver name */ - const char *drv_name; - /* firmware file name */ - const char *fw_filename; - - /* board name */ - const char *board; - struct sst_acpi_mach * (*machine_quirk)(void *arg); - const void *quirk_data; - void *pdata; -}; - -#define SST_ACPI_MAX_CODECS 3 - -/** - * struct sst_codecs: Structure to hold secondary codec information apart from - * the matched one, this data will be passed to the quirk function to match - * with the ACPI detected devices - * - * @num_codecs: number of secondary codecs used in the platform - * @codecs: holds the codec IDs - * - */ -struct sst_codecs { - int num_codecs; - u8 codecs[SST_ACPI_MAX_CODECS][ACPI_ID_LEN]; -}; - -/* check all codecs */ -struct sst_acpi_mach *sst_acpi_codec_list(void *arg); diff --git a/sound/soc/intel/common/sst-match-acpi.c b/sound/soc/intel/common/sst-match-acpi.c deleted file mode 100644 index 56d26f36a3cb..000000000000 --- a/sound/soc/intel/common/sst-match-acpi.c +++ /dev/null @@ -1,173 +0,0 @@ -/* - * sst_match_apci.c - SST (LPE) match for ACPI enumeration. - * - * Copyright (c) 2013-15, 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 "sst-acpi.h" - -static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level, - void *context, void **ret) -{ - struct acpi_device *adev; - const char *name = NULL; - - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - - if (adev->status.present && adev->status.functional) { - name = acpi_dev_name(adev); - *(const char **)ret = name; - return AE_CTRL_TERMINATE; - } - - return AE_OK; -} - -const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) -{ - const char *name = NULL; - acpi_status status; - - status = acpi_get_devices(hid, sst_acpi_find_name, NULL, - (void **)&name); - - if (ACPI_FAILURE(status) || name[0] == '\0') - return NULL; - - return name; -} -EXPORT_SYMBOL_GPL(sst_acpi_find_name_from_hid); - -static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level, - void *context, void **ret) -{ - unsigned long long sta; - acpi_status status; - - *(bool *)context = true; - status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) - *(bool *)context = false; - - return AE_OK; -} - -bool sst_acpi_check_hid(const u8 hid[ACPI_ID_LEN]) -{ - acpi_status status; - bool found = false; - - status = acpi_get_devices(hid, sst_acpi_mach_match, &found, NULL); - - if (ACPI_FAILURE(status)) - return false; - - return found; -} -EXPORT_SYMBOL_GPL(sst_acpi_check_hid); - -struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines) -{ - struct sst_acpi_mach *mach; - - for (mach = machines; mach->id[0]; mach++) { - if (sst_acpi_check_hid(mach->id) == true) { - if (mach->machine_quirk == NULL) - return mach; - - if (mach->machine_quirk(mach) != NULL) - return mach; - } - } - return NULL; -} -EXPORT_SYMBOL_GPL(sst_acpi_find_machine); - -static acpi_status sst_acpi_find_package(acpi_handle handle, u32 level, - void *context, void **ret) -{ - struct acpi_device *adev; - acpi_status status = AE_OK; - struct sst_acpi_package_context *pkg_ctx = context; - - pkg_ctx->data_valid = false; - - if (acpi_bus_get_device(handle, &adev)) - return AE_OK; - - if (adev->status.present && adev->status.functional) { - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; - union acpi_object *myobj = NULL; - - status = acpi_evaluate_object_typed(handle, pkg_ctx->name, - NULL, &buffer, - ACPI_TYPE_PACKAGE); - if (ACPI_FAILURE(status)) - return AE_OK; - - myobj = buffer.pointer; - if (!myobj || myobj->package.count != pkg_ctx->length) { - kfree(buffer.pointer); - return AE_OK; - } - - status = acpi_extract_package(myobj, - pkg_ctx->format, pkg_ctx->state); - if (ACPI_FAILURE(status)) { - kfree(buffer.pointer); - return AE_OK; - } - - kfree(buffer.pointer); - pkg_ctx->data_valid = true; - return AE_CTRL_TERMINATE; - } - - return AE_OK; -} - -bool sst_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], - struct sst_acpi_package_context *ctx) -{ - acpi_status status; - - status = acpi_get_devices(hid, sst_acpi_find_package, ctx, NULL); - - if (ACPI_FAILURE(status) || !ctx->data_valid) - return false; - - return true; -} -EXPORT_SYMBOL_GPL(sst_acpi_find_package_from_hid); - -struct sst_acpi_mach *sst_acpi_codec_list(void *arg) -{ - struct sst_acpi_mach *mach = arg; - struct sst_codecs *codec_list = (struct sst_codecs *) mach->quirk_data; - int i; - - if (mach->quirk_data == NULL) - return mach; - - for (i = 0; i < codec_list->num_codecs; i++) { - if (sst_acpi_check_hid(codec_list->codecs[i]) != true) - return NULL; - } - - return mach; -} -EXPORT_SYMBOL_GPL(sst_acpi_codec_list); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 4abbd90e394c..31d8634e8aa1 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -28,7 +28,7 @@ #include #include #include -#include "../common/sst-acpi.h" +#include #include #include #include @@ -439,10 +439,10 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data) { struct hdac_bus *bus = ebus_to_hbus(&skl->ebus); struct platform_device *pdev; - struct sst_acpi_mach *mach = driver_data; + struct snd_soc_acpi_mach *mach = driver_data; int ret; - mach = sst_acpi_find_machine(mach); + mach = snd_soc_acpi_find_machine(mach); if (mach == NULL) { dev_err(bus->dev, "No matching machine driver found\n"); return -ENODEV; @@ -878,27 +878,27 @@ static void skl_remove(struct pci_dev *pci) dev_set_drvdata(&pci->dev, NULL); } -static struct sst_codecs skl_codecs = { +static struct snd_soc_acpi_codecs skl_codecs = { .num_codecs = 1, .codecs = {"10508825"} }; -static struct sst_codecs kbl_codecs = { +static struct snd_soc_acpi_codecs kbl_codecs = { .num_codecs = 1, .codecs = {"10508825"} }; -static struct sst_codecs bxt_codecs = { +static struct snd_soc_acpi_codecs bxt_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} }; -static struct sst_codecs kbl_poppy_codecs = { +static struct snd_soc_acpi_codecs kbl_poppy_codecs = { .num_codecs = 1, .codecs = {"10EC5663"} }; -static struct sst_codecs kbl_5663_5514_codecs = { +static struct snd_soc_acpi_codecs kbl_5663_5514_codecs = { .num_codecs = 2, .codecs = {"10EC5663", "10EC5514"} }; @@ -907,7 +907,7 @@ static struct skl_machine_pdata cnl_pdata = { .use_tplg_pcm = true, }; -static struct sst_acpi_mach sst_skl_devdata[] = { +static struct snd_soc_acpi_mach sst_skl_devdata[] = { { .id = "INT343A", .drv_name = "skl_alc286s_i2s", @@ -917,7 +917,7 @@ static struct sst_acpi_mach sst_skl_devdata[] = { .id = "INT343B", .drv_name = "skl_n88l25_s4567", .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &skl_codecs, .pdata = &skl_dmic_data }, @@ -925,14 +925,14 @@ static struct sst_acpi_mach sst_skl_devdata[] = { .id = "MX98357A", .drv_name = "skl_n88l25_m98357a", .fw_filename = "intel/dsp_fw_release.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &skl_codecs, .pdata = &skl_dmic_data }, {} }; -static struct sst_acpi_mach sst_bxtp_devdata[] = { +static struct snd_soc_acpi_mach sst_bxtp_devdata[] = { { .id = "INT343A", .drv_name = "bxt_alc298s_i2s", @@ -942,13 +942,13 @@ static struct sst_acpi_mach sst_bxtp_devdata[] = { .id = "DLGS7219", .drv_name = "bxt_da7219_max98357a_i2s", .fw_filename = "intel/dsp_fw_bxtn.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &bxt_codecs, }, {} }; -static struct sst_acpi_mach sst_kbl_devdata[] = { +static struct snd_soc_acpi_mach sst_kbl_devdata[] = { { .id = "INT343A", .drv_name = "kbl_alc286s_i2s", @@ -958,7 +958,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "INT343B", .drv_name = "kbl_n88l25_s4567", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_codecs, .pdata = &skl_dmic_data }, @@ -966,7 +966,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "MX98357A", .drv_name = "kbl_n88l25_m98357a", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_codecs, .pdata = &skl_dmic_data }, @@ -974,7 +974,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "MX98927", .drv_name = "kbl_r5514_5663_max", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_5663_5514_codecs, .pdata = &skl_dmic_data }, @@ -982,7 +982,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { .id = "MX98927", .drv_name = "kbl_rt5663_m98927", .fw_filename = "intel/dsp_fw_kbl.bin", - .machine_quirk = sst_acpi_codec_list, + .machine_quirk = snd_soc_acpi_codec_list, .quirk_data = &kbl_poppy_codecs, .pdata = &skl_dmic_data }, @@ -995,7 +995,7 @@ static struct sst_acpi_mach sst_kbl_devdata[] = { {} }; -static struct sst_acpi_mach sst_glk_devdata[] = { +static struct snd_soc_acpi_mach sst_glk_devdata[] = { { .id = "INT343A", .drv_name = "glk_alc298s_i2s", @@ -1004,7 +1004,7 @@ static struct sst_acpi_mach sst_glk_devdata[] = { {} }; -static const struct sst_acpi_mach sst_cnl_devdata[] = { +static const struct snd_soc_acpi_mach sst_cnl_devdata[] = { { .id = "INT34C2", .drv_name = "cnl_rt274", diff --git a/sound/soc/soc-acpi.c b/sound/soc/soc-acpi.c new file mode 100644 index 000000000000..f21df28bc28e --- /dev/null +++ b/sound/soc/soc-acpi.c @@ -0,0 +1,175 @@ +/* + * soc-apci.c - support for ACPI enumeration. + * + * Copyright (c) 2013-15, 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 + +static acpi_status snd_soc_acpi_find_name(acpi_handle handle, u32 level, + void *context, void **ret) +{ + struct acpi_device *adev; + const char *name = NULL; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (adev->status.present && adev->status.functional) { + name = acpi_dev_name(adev); + *(const char **)ret = name; + return AE_CTRL_TERMINATE; + } + + return AE_OK; +} + +const char *snd_soc_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]) +{ + const char *name = NULL; + acpi_status status; + + status = acpi_get_devices(hid, snd_soc_acpi_find_name, NULL, + (void **)&name); + + if (ACPI_FAILURE(status) || name[0] == '\0') + return NULL; + + return name; +} +EXPORT_SYMBOL_GPL(snd_soc_acpi_find_name_from_hid); + +static acpi_status snd_soc_acpi_mach_match(acpi_handle handle, u32 level, + void *context, void **ret) +{ + unsigned long long sta; + acpi_status status; + + *(bool *)context = true; + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) + *(bool *)context = false; + + return AE_OK; +} + +bool snd_soc_acpi_check_hid(const u8 hid[ACPI_ID_LEN]) +{ + acpi_status status; + bool found = false; + + status = acpi_get_devices(hid, snd_soc_acpi_mach_match, &found, NULL); + + if (ACPI_FAILURE(status)) + return false; + + return found; +} +EXPORT_SYMBOL_GPL(snd_soc_acpi_check_hid); + +struct snd_soc_acpi_mach * +snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines) +{ + struct snd_soc_acpi_mach *mach; + + for (mach = machines; mach->id[0]; mach++) { + if (snd_soc_acpi_check_hid(mach->id) == true) { + if (mach->machine_quirk == NULL) + return mach; + + if (mach->machine_quirk(mach) != NULL) + return mach; + } + } + return NULL; +} +EXPORT_SYMBOL_GPL(snd_soc_acpi_find_machine); + +static acpi_status snd_soc_acpi_find_package(acpi_handle handle, u32 level, + void *context, void **ret) +{ + struct acpi_device *adev; + acpi_status status = AE_OK; + struct snd_soc_acpi_package_context *pkg_ctx = context; + + pkg_ctx->data_valid = false; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (adev->status.present && adev->status.functional) { + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *myobj = NULL; + + status = acpi_evaluate_object_typed(handle, pkg_ctx->name, + NULL, &buffer, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return AE_OK; + + myobj = buffer.pointer; + if (!myobj || myobj->package.count != pkg_ctx->length) { + kfree(buffer.pointer); + return AE_OK; + } + + status = acpi_extract_package(myobj, + pkg_ctx->format, pkg_ctx->state); + if (ACPI_FAILURE(status)) { + kfree(buffer.pointer); + return AE_OK; + } + + kfree(buffer.pointer); + pkg_ctx->data_valid = true; + return AE_CTRL_TERMINATE; + } + + return AE_OK; +} + +bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN], + struct snd_soc_acpi_package_context *ctx) +{ + acpi_status status; + + status = acpi_get_devices(hid, snd_soc_acpi_find_package, ctx, NULL); + + if (ACPI_FAILURE(status) || !ctx->data_valid) + return false; + + return true; +} +EXPORT_SYMBOL_GPL(snd_soc_acpi_find_package_from_hid); + +struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg) +{ + struct snd_soc_acpi_mach *mach = arg; + struct snd_soc_acpi_codecs *codec_list = + (struct snd_soc_acpi_codecs *) mach->quirk_data; + int i; + + if (mach->quirk_data == NULL) + return mach; + + for (i = 0; i < codec_list->num_codecs; i++) { + if (snd_soc_acpi_check_hid(codec_list->codecs[i]) != true) + return NULL; + } + + return mach; +} +EXPORT_SYMBOL_GPL(snd_soc_acpi_codec_list); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ALSA SoC ACPI module"); -- cgit v1.2.3-59-g8ed1b From 976b5a0ee9c49c3c0112596d2e26bd6aa77c5cd5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 12 Oct 2017 18:49:40 -0500 Subject: ASoC: ACPI: add new fields for SOF support To prepare for SOF integration, we need new fields in the machine table. It is intended that the same table is used for both closed-source and open-source firmware to avoid repeating ACPI-related information multiple times No functional change Signed-off-by: Pierre-Louis Bossart Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc-acpi.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 918d78e8c8d6..a7d8d335b043 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -55,10 +55,12 @@ bool snd_soc_acpi_check_hid(const u8 hid[ACPI_ID_LEN]); /** * snd_soc_acpi_mach: ACPI-based machine descriptor. Most of the fields are * related to the hardware, except for the firmware and topology file names. + * A platform supported by legacy and Sound Open Firmware (SOF) would expose + * all firmware/topology related fields. * * @id: ACPI ID (usually the codec's) used to find a matching machine driver. * @drv_name: machine driver name - * @fw_filename: firmware file name. + * @fw_filename: firmware file name. Used when SOF is not enabled. * @board: board name * @machine_quirk: pointer to quirk, usually based on DMI information when * ACPI ID alone is not sufficient, wrong or misleading @@ -66,6 +68,11 @@ bool snd_soc_acpi_check_hid(const u8 hid[ACPI_ID_LEN]); * audio codecs whose presence if checked with ACPI * @pdata: intended for platform data or machine specific-ops. This structure * is not constant since this field may be updated at run-time + * @sof_fw_filename: Sound Open Firmware file name, if enabled + * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled + * @asoc_plat_name: ASoC platform name, used for binding machine drivers + * if non NULL + * @new_mach_data: machine driver private data fixup */ /* Descriptor for SST ASoC machine driver */ struct snd_soc_acpi_mach { @@ -76,6 +83,10 @@ struct snd_soc_acpi_mach { struct snd_soc_acpi_mach * (*machine_quirk)(void *arg); const void *quirk_data; void *pdata; + const char *sof_fw_filename; + const char *sof_tplg_filename; + const char *asoc_plat_name; + struct platform_device * (*new_mach_data)(void *pdata); }; #define SND_SOC_ACPI_MAX_CODECS 3 -- cgit v1.2.3-59-g8ed1b From 6004640638a2cb81c00c17ddf21ad572af964bbe Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 12 Oct 2017 18:49:41 -0500 Subject: ASoC: Intel: move all ACPI match tables to common module First step of cleaning, move all tables to soc-acpi-intel-match module. The tables remain in separate files per platform to keep them manageable. Skylake+ platforms are still handled elsewhere since there is no conflict with SOF for now, but this will have to be handled at a later point. Signed-off-by: Pierre-Louis Bossart Acked-by: Liam Girdwood Signed-off-by: Mark Brown --- include/sound/soc-acpi-intel-match.h | 32 +++ sound/soc/intel/Kconfig | 24 +- sound/soc/intel/Makefile | 2 +- sound/soc/intel/atom/sst/sst_acpi.c | 311 ++------------------- sound/soc/intel/common/Makefile | 2 + sound/soc/intel/common/soc-acpi-intel-byt-match.c | 166 +++++++++++ sound/soc/intel/common/soc-acpi-intel-cht-match.c | 156 +++++++++++ .../intel/common/soc-acpi-intel-hsw-bdw-match.c | 52 ++++ sound/soc/intel/common/sst-acpi.c | 44 +-- 9 files changed, 446 insertions(+), 343 deletions(-) create mode 100644 include/sound/soc-acpi-intel-match.h create mode 100644 sound/soc/intel/common/soc-acpi-intel-byt-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-cht-match.c create mode 100644 sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c (limited to 'include') diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h new file mode 100644 index 000000000000..1a9191cd4bb3 --- /dev/null +++ b/include/sound/soc-acpi-intel-match.h @@ -0,0 +1,32 @@ + +/* + * Copyright (C) 2017, Intel Corporation. All rights reserved. + * + * 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. + * + */ + +#ifndef __LINUX_SND_SOC_ACPI_INTEL_MATCH_H +#define __LINUX_SND_SOC_ACPI_INTEL_MATCH_H + +#include +#include + +/* + * these tables are not constants, some fields can be used for + * pdata or machine ops + */ +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[]; + +#endif diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index d03199cc61e0..ec74d1d023d6 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -27,10 +27,14 @@ config SND_SST_IPC_ACPI select SND_SOC_INTEL_SST select IOSF_MBI +config SND_SOC_INTEL_COMMON + tristate + config SND_SOC_INTEL_SST tristate + select SND_SOC_INTEL_COMMON select SND_SOC_INTEL_SST_ACPI if ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI + select SND_SOC_ACPI_INTEL_MATCH if ACPI config SND_SOC_INTEL_SST_FIRMWARE tristate @@ -39,7 +43,7 @@ config SND_SOC_INTEL_SST_FIRMWARE config SND_SOC_INTEL_SST_ACPI tristate -config SND_SOC_INTEL_SST_MATCH +config SND_SOC_ACPI_INTEL_MATCH tristate select SND_SOC_ACPI if ACPI @@ -145,7 +149,7 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH select SND_SOC_RT5640 select SND_SST_ATOM_HIFI2_PLATFORM select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI + select SND_SOC_ACPI_INTEL_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR platforms with RT5640 audio codec. @@ -158,7 +162,7 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH select SND_SOC_RT5651 select SND_SST_ATOM_HIFI2_PLATFORM select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI + select SND_SOC_ACPI_INTEL_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR platforms with RT5651 audio codec. @@ -171,7 +175,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH select SND_SOC_RT5670 select SND_SST_ATOM_HIFI2_PLATFORM select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI + select SND_SOC_ACPI_INTEL_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with RT5672 audio codec. @@ -184,7 +188,7 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH select SND_SOC_RT5645 select SND_SST_ATOM_HIFI2_PLATFORM select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI + select SND_SOC_ACPI_INTEL_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with RT5645/5650 audio codec. @@ -197,7 +201,7 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH select SND_SOC_TS3A227E select SND_SST_ATOM_HIFI2_PLATFORM select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI + select SND_SOC_ACPI_INTEL_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell platforms with MAX98090 audio codec it also can support TI jack chip as aux device. @@ -209,7 +213,7 @@ config SND_SOC_INTEL_BYT_CHT_DA7213_MACH select SND_SOC_DA7213 select SND_SST_ATOM_HIFI2_PLATFORM select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI + select SND_SOC_ACPI_INTEL_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Baytrail & CherryTrail platforms with DA7212/7213 audio codec. @@ -221,7 +225,7 @@ config SND_SOC_INTEL_BYT_CHT_ES8316_MACH select SND_SOC_ES8316 select SND_SST_ATOM_HIFI2_PLATFORM select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI + select SND_SOC_ACPI_INTEL_MATCH if ACPI help This adds support for ASoC machine driver for Intel(R) Baytrail & Cherrytrail platforms with ES8316 audio codec. @@ -232,7 +236,7 @@ config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH depends on X86_INTEL_LPSS && I2C && ACPI select SND_SST_ATOM_HIFI2_PLATFORM select SND_SST_IPC_ACPI - select SND_SOC_INTEL_SST_MATCH if ACPI + select SND_SOC_ACPI_INTEL_MATCH if ACPI help This adds support for ASoC machine driver for the MinnowBoard Max or Up boards and provides access to I2S signals on the Low-Speed diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index cdd495f7ee2c..9105594436f0 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -1,5 +1,5 @@ # Core support -obj-$(CONFIG_SND_SOC_INTEL_SST) += common/ +obj-$(CONFIG_SND_SOC_INTEL_COMMON) += common/ # Platform Support obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/ diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index 8d82bc8dc4ac..32d6e02e2104 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "../sst-mfld-platform.h" #include "../../common/sst-dsp.h" #include "sst.h" @@ -238,19 +239,26 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx) return 0; } +static int is_byt(void) +{ + bool status = false; + static const struct x86_cpu_id cpu_ids[] = { + { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ + {} + }; + if (x86_match_cpu(cpu_ids)) + status = true; + return status; +} static int is_byt_cr(struct device *dev, bool *bytcr) { int status = 0; if (IS_ENABLED(CONFIG_IOSF_MBI)) { - static const struct x86_cpu_id cpu_ids[] = { - { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */ - {} - }; u32 bios_status; - if (!x86_match_cpu(cpu_ids) || !iosf_mbi_available()) { + if (!is_byt() || !iosf_mbi_available()) { /* bail silently */ return status; } @@ -303,6 +311,10 @@ static int sst_acpi_probe(struct platform_device *pdev) return -ENODEV; } + if (is_byt()) + mach->pdata = &byt_rvp_platform_data; + else + mach->pdata = &chv_platform_data; pdata = mach->pdata; ret = kstrtouint(id->id, 16, &dev_id); @@ -380,294 +392,9 @@ static int sst_acpi_remove(struct platform_device *pdev) return 0; } -static unsigned long cht_machine_id; - -#define CHT_SURFACE_MACH 1 -#define BYT_THINKPAD_10 2 - -static int cht_surface_quirk_cb(const struct dmi_system_id *id) -{ - cht_machine_id = CHT_SURFACE_MACH; - return 1; -} - -static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id) -{ - cht_machine_id = BYT_THINKPAD_10; - return 1; -} - - -static const struct dmi_system_id byt_table[] = { - { - .callback = byt_thinkpad10_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), - }, - }, - { - .callback = byt_thinkpad10_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), - }, - }, - { - .callback = byt_thinkpad10_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), - }, - }, - { } -}; - -static const struct dmi_system_id cht_table[] = { - { - .callback = cht_surface_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), - DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), - }, - }, - { } -}; - - -static struct snd_soc_acpi_mach cht_surface_mach = { - .id = "10EC5640", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data, -}; - -static struct snd_soc_acpi_mach byt_thinkpad_10 = { - .id = "10EC5640", - .drv_name = "cht-bsw-rt5672", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .pdata = &byt_rvp_platform_data, -}; - -static struct snd_soc_acpi_mach *cht_quirk(void *arg) -{ - struct snd_soc_acpi_mach *mach = arg; - - dmi_check_system(cht_table); - - if (cht_machine_id == CHT_SURFACE_MACH) - return &cht_surface_mach; - else - return mach; -} - -static struct snd_soc_acpi_mach *byt_quirk(void *arg) -{ - struct snd_soc_acpi_mach *mach = arg; - - dmi_check_system(byt_table); - - if (cht_machine_id == BYT_THINKPAD_10) - return &byt_thinkpad_10; - else - return mach; -} - - -static struct snd_soc_acpi_mach sst_acpi_bytcr[] = { - { - .id = "10EC5640", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .machine_quirk = byt_quirk, - .pdata = &byt_rvp_platform_data, - }, - { - .id = "10EC5642", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .pdata = &byt_rvp_platform_data - }, - { - .id = "INTCCFFD", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5640", - .pdata = &byt_rvp_platform_data - }, - { - .id = "10EC5651", - .drv_name = "bytcr_rt5651", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcr_rt5651", - .pdata = &byt_rvp_platform_data - }, - { - .id = "DLGS7212", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcht_da7213", - .pdata = &byt_rvp_platform_data - }, - { - .id = "DLGS7213", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcht_da7213", - .pdata = &byt_rvp_platform_data - }, - /* some Baytrail platforms rely on RT5645, use CHT machine driver */ - { - .id = "10EC5645", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .pdata = &byt_rvp_platform_data - }, - { - .id = "10EC5648", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .pdata = &byt_rvp_platform_data - }, - /* use CHT driver to Baytrail Chromebooks */ - { - .id = "193C9890", - .drv_name = "cht-bsw-max98090", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "cht-bsw", - .pdata = &byt_rvp_platform_data - }, -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) - /* - * This is always last in the table so that it is selected only when - * enabled explicitly and there is no codec-related information in SSDT - */ - { - .id = "80860F28", - .drv_name = "bytcht_nocodec", - .fw_filename = "intel/fw_sst_0f28.bin", - .board = "bytcht_nocodec", - .pdata = &byt_rvp_platform_data - }, -#endif - {}, -}; - -/* Cherryview-based platforms: CherryTrail and Braswell */ -static struct snd_soc_acpi_mach sst_acpi_chv[] = { - { - .id = "10EC5670", - .drv_name = "cht-bsw-rt5672", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC5672", - .drv_name = "cht-bsw-rt5672", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC5645", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC5650", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "10EC3270", - .drv_name = "cht-bsw-rt5645", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - - { - .id = "193C9890", - .drv_name = "cht-bsw-max98090", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "cht-bsw", - .pdata = &chv_platform_data - }, - { - .id = "DLGS7212", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_da7213", - .pdata = &chv_platform_data - }, - { - .id = "DLGS7213", - .drv_name = "bytcht_da7213", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_da7213", - .pdata = &chv_platform_data - }, - { - .id = "ESSX8316", - .drv_name = "bytcht_es8316", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_es8316", - .pdata = &chv_platform_data - }, - /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ - { - .id = "10EC5640", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcr_rt5640", - .machine_quirk = cht_quirk, - .pdata = &chv_platform_data - }, - { - .id = "10EC3276", - .drv_name = "bytcr_rt5640", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcr_rt5640", - .pdata = &chv_platform_data - }, - /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ - { - .id = "10EC5651", - .drv_name = "bytcr_rt5651", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcr_rt5651", - .pdata = &chv_platform_data - }, -#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) - /* - * This is always last in the table so that it is selected only when - * enabled explicitly and there is no codec-related information in SSDT - */ - { - .id = "808622A8", - .drv_name = "bytcht_nocodec", - .fw_filename = "intel/fw_sst_22a8.bin", - .board = "bytcht_nocodec", - .pdata = &chv_platform_data - }, -#endif - {}, -}; - static const struct acpi_device_id sst_acpi_ids[] = { - { "80860F28", (unsigned long)&sst_acpi_bytcr}, - { "808622A8", (unsigned long) &sst_acpi_chv}, + { "80860F28", (unsigned long)&snd_soc_acpi_intel_baytrail_machines}, + { "808622A8", (unsigned long)&snd_soc_acpi_intel_cherrytrail_machines}, { }, }; diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 2c958ec3f4fd..cb29653319d9 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -2,7 +2,9 @@ snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-firmware-objs := sst-firmware.o +snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o soc-acpi-intel-hsw-bdw-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o +obj-$(CONFIG_SND_SOC_ACPI_INTEL_MATCH) += snd-soc-acpi-intel-match.o diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c new file mode 100644 index 000000000000..da80b4871a55 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -0,0 +1,166 @@ +/* + * soc-apci-intel-byt-match.c - tables and support for BYT ACPI enumeration. + * + * Copyright (c) 2017, 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 + +static unsigned long byt_machine_id; + +#define BYT_THINKPAD_10 1 + +static int byt_thinkpad10_quirk_cb(const struct dmi_system_id *id) +{ + byt_machine_id = BYT_THINKPAD_10; + return 1; +} + + +static const struct dmi_system_id byt_table[] = { + { + .callback = byt_thinkpad10_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), + }, + }, + { + .callback = byt_thinkpad10_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"), + }, + }, + { + .callback = byt_thinkpad10_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"), + }, + }, + { } +}; + +static struct snd_soc_acpi_mach byt_thinkpad_10 = { + .id = "10EC5640", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", +}; + +static struct snd_soc_acpi_mach *byt_quirk(void *arg) +{ + struct snd_soc_acpi_mach *mach = arg; + + dmi_check_system(byt_table); + + if (byt_machine_id == BYT_THINKPAD_10) + return &byt_thinkpad_10; + else + return mach; +} + +struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[] = { + { + .id = "10EC5640", + .drv_name = "byt-rt5640", + .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", + }, + { + .id = "193C9890", + .drv_name = "byt-max98090", + .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_baytrail_legacy_machines); + +struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { + { + .id = "10EC5640", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + .machine_quirk = byt_quirk, + }, + { + .id = "10EC5642", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + }, + { + .id = "INTCCFFD", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5640", + }, + { + .id = "10EC5651", + .drv_name = "bytcr_rt5651", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcr_rt5651", + }, + { + .id = "DLGS7212", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_da7213", + }, + { + .id = "DLGS7213", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_da7213", + }, + /* some Baytrail platforms rely on RT5645, use CHT machine driver */ + { + .id = "10EC5645", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + }, + { + .id = "10EC5648", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + }, + /* use CHT driver to Baytrail Chromebooks */ + { + .id = "193C9890", + .drv_name = "cht-bsw-max98090", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "cht-bsw", + }, +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) + /* + * This is always last in the table so that it is selected only when + * enabled explicitly and there is no codec-related information in SSDT + */ + { + .id = "80860F28", + .drv_name = "bytcht_nocodec", + .fw_filename = "intel/fw_sst_0f28.bin", + .board = "bytcht_nocodec", + }, +#endif + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_baytrail_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-cht-match.c b/sound/soc/intel/common/soc-acpi-intel-cht-match.c new file mode 100644 index 000000000000..73eb57706654 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-cht-match.c @@ -0,0 +1,156 @@ +/* + * soc-apci-intel-cht-match.c - tables and support for CHT ACPI enumeration. + * + * Copyright (c) 2017, 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 + +static unsigned long cht_machine_id; + +#define CHT_SURFACE_MACH 1 + +static int cht_surface_quirk_cb(const struct dmi_system_id *id) +{ + cht_machine_id = CHT_SURFACE_MACH; + return 1; +} + +static const struct dmi_system_id cht_table[] = { + { + .callback = cht_surface_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"), + }, + }, + { } +}; + + +static struct snd_soc_acpi_mach cht_surface_mach = { + .id = "10EC5640", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", +}; + +static struct snd_soc_acpi_mach *cht_quirk(void *arg) +{ + struct snd_soc_acpi_mach *mach = arg; + + dmi_check_system(cht_table); + + if (cht_machine_id == CHT_SURFACE_MACH) + return &cht_surface_mach; + else + return mach; +} + +/* Cherryview-based platforms: CherryTrail and Braswell */ +struct snd_soc_acpi_mach snd_soc_acpi_intel_cherrytrail_machines[] = { + { + .id = "10EC5670", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + }, + { + .id = "10EC5672", + .drv_name = "cht-bsw-rt5672", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + }, + { + .id = "10EC5645", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + }, + { + .id = "10EC5650", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + }, + { + .id = "10EC3270", + .drv_name = "cht-bsw-rt5645", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + }, + { + .id = "193C9890", + .drv_name = "cht-bsw-max98090", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "cht-bsw", + }, + { + .id = "DLGS7212", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_da7213", + }, + { + .id = "DLGS7213", + .drv_name = "bytcht_da7213", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_da7213", + }, + { + .id = "ESSX8316", + .drv_name = "bytcht_es8316", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_es8316", + }, + /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */ + { + .id = "10EC5640", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5640", + .machine_quirk = cht_quirk, + }, + { + .id = "10EC3276", + .drv_name = "bytcr_rt5640", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5640", + }, + /* some CHT-T platforms rely on RT5651, use Baytrail machine driver */ + { + .id = "10EC5651", + .drv_name = "bytcr_rt5651", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcr_rt5651", + }, +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) + /* + * This is always last in the table so that it is selected only when + * enabled explicitly and there is no codec-related information in SSDT + */ + { + .id = "808622A8", + .drv_name = "bytcht_nocodec", + .fw_filename = "intel/fw_sst_22a8.bin", + .board = "bytcht_nocodec", + }, +#endif + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cherrytrail_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c new file mode 100644 index 000000000000..23329f2a802d --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c @@ -0,0 +1,52 @@ +/* + * soc-apci-intel-hsw-bdw-match.c - tables and support for ACPI enumeration. + * + * Copyright (c) 2017, 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 + +struct snd_soc_acpi_mach snd_soc_acpi_intel_haswell_machines[] = { + { + .id = "INT33CA", + .drv_name = "haswell-audio", + .fw_filename = "intel/IntcSST1.bin", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_haswell_machines); + +struct snd_soc_acpi_mach snd_soc_acpi_intel_broadwell_machines[] = { + { + .id = "INT343A", + .drv_name = "broadwell-audio", + .fw_filename = "intel/IntcSST2.bin", + }, + { + .id = "RT5677CE", + .drv_name = "bdw-rt5677", + .fw_filename = "intel/IntcSST2.bin", + }, + { + .id = "INT33CA", + .drv_name = "haswell-audio", + .fw_filename = "intel/IntcSST2.bin", + }, + {} +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_broadwell_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index 32d74ba70e4a..cf6fbbd4e378 100644 --- a/sound/soc/intel/common/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -22,6 +22,7 @@ #include "sst-dsp.h" #include +#include #define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000 #define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000 @@ -179,18 +180,9 @@ static int sst_acpi_remove(struct platform_device *pdev) return 0; } -static struct snd_soc_acpi_mach haswell_machines[] = { - { - .id = "INT33CA", - .drv_name = "haswell-audio", - .fw_filename = "intel/IntcSST1.bin", - }, - {} -}; - static struct sst_acpi_desc sst_acpi_haswell_desc = { .drv_name = "haswell-pcm-audio", - .machines = haswell_machines, + .machines = snd_soc_acpi_intel_haswell_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = 1, .resindex_fw_base = -1, @@ -201,23 +193,9 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = { .dma_size = SST_LPT_DSP_DMA_SIZE, }; -static struct snd_soc_acpi_mach broadwell_machines[] = { - { - .id = "INT343A", - .drv_name = "broadwell-audio", - .fw_filename = "intel/IntcSST2.bin", - }, - { - .id = "RT5677CE", - .drv_name = "bdw-rt5677", - .fw_filename = "intel/IntcSST2.bin", - }, - {} -}; - static struct sst_acpi_desc sst_acpi_broadwell_desc = { .drv_name = "haswell-pcm-audio", - .machines = broadwell_machines, + .machines = snd_soc_acpi_intel_broadwell_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = 1, .resindex_fw_base = -1, @@ -229,23 +207,9 @@ static struct sst_acpi_desc sst_acpi_broadwell_desc = { }; #if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI) -static struct snd_soc_acpi_mach baytrail_machines[] = { - { - .id = "10EC5640", - .drv_name = "byt-rt5640", - .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", - }, - { - .id = "193C9890", - .drv_name = "byt-max98090", - .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", - }, - {} -}; - static struct sst_acpi_desc sst_acpi_baytrail_desc = { .drv_name = "baytrail-pcm-audio", - .machines = baytrail_machines, + .machines = snd_soc_acpi_intel_baytrail_legacy_machines, .resindex_lpe_base = 0, .resindex_pcicfg_base = 1, .resindex_fw_base = 2, -- cgit v1.2.3-59-g8ed1b From ff61b5e3f041c2f1aa8d7c700af3007889973889 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 20 Oct 2017 10:23:37 +0300 Subject: drivers, net, mlx4: convert mlx4_cq.refcount from atomic_t to refcount_t atomic_t variables are currently used to implement reference counters with the following properties: - counter is initialized to 1 using atomic_set() - a resource is freed upon counter reaching zero - once counter reaches zero, its further increments aren't allowed - counter schema uses basic atomic operations (set, inc, inc_not_zero, dec_and_test, etc.) Such atomic variables should be converted to a newly provided refcount_t type and API that prevents accidental counter overflows and underflows. This is important since overflows and underflows can lead to use-after-free situation and be exploitable. The variable mlx4_cq.refcount is used as pure reference counter. Convert it to refcount_t and fix up the operations. Suggested-by: Kees Cook Reviewed-by: David Windsor Reviewed-by: Hans Liljestrand Signed-off-by: Elena Reshetova Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/cq.c | 8 ++++---- include/linux/mlx4/device.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index 72eb50cd5ecd..d8e9a323122e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -69,7 +69,7 @@ void mlx4_cq_tasklet_cb(unsigned long data) list_for_each_entry_safe(mcq, temp, &ctx->process_list, tasklet_ctx.list) { list_del_init(&mcq->tasklet_ctx.list); mcq->tasklet_ctx.comp(mcq); - if (atomic_dec_and_test(&mcq->refcount)) + if (refcount_dec_and_test(&mcq->refcount)) complete(&mcq->free); if (time_after(jiffies, end)) break; @@ -92,7 +92,7 @@ static void mlx4_add_cq_to_tasklet(struct mlx4_cq *cq) * still arrive. */ if (list_empty_careful(&cq->tasklet_ctx.list)) { - atomic_inc(&cq->refcount); + refcount_inc(&cq->refcount); kick = list_empty(&tasklet_ctx->list); list_add_tail(&cq->tasklet_ctx.list, &tasklet_ctx->list); if (kick) @@ -344,7 +344,7 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, cq->cons_index = 0; cq->arm_sn = 1; cq->uar = uar; - atomic_set(&cq->refcount, 1); + refcount_set(&cq->refcount, 1); init_completion(&cq->free); cq->comp = mlx4_add_cq_to_tasklet; cq->tasklet_ctx.priv = @@ -386,7 +386,7 @@ void mlx4_cq_free(struct mlx4_dev *dev, struct mlx4_cq *cq) priv->eq_table.eq[MLX4_EQ_ASYNC].irq) synchronize_irq(priv->eq_table.eq[MLX4_EQ_ASYNC].irq); - if (atomic_dec_and_test(&cq->refcount)) + if (refcount_dec_and_test(&cq->refcount)) complete(&cq->free); wait_for_completion(&cq->free); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index b0a57e043fa3..daac2e3a1a58 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -40,7 +40,7 @@ #include #include -#include +#include #include @@ -751,7 +751,7 @@ struct mlx4_cq { int cqn; unsigned vector; - atomic_t refcount; + refcount_t refcount; struct completion free; struct { struct list_head list; -- cgit v1.2.3-59-g8ed1b From 0068895ff845c38e9e2b65c002c53c623379e436 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 20 Oct 2017 10:23:38 +0300 Subject: drivers, net, mlx4: convert mlx4_qp.refcount from atomic_t to refcount_t atomic_t variables are currently used to implement reference counters with the following properties: - counter is initialized to 1 using atomic_set() - a resource is freed upon counter reaching zero - once counter reaches zero, its further increments aren't allowed - counter schema uses basic atomic operations (set, inc, inc_not_zero, dec_and_test, etc.) Such atomic variables should be converted to a newly provided refcount_t type and API that prevents accidental counter overflows and underflows. This is important since overflows and underflows can lead to use-after-free situation and be exploitable. The variable mlx4_qp.refcount is used as pure reference counter. Convert it to refcount_t and fix up the operations. Suggested-by: Kees Cook Reviewed-by: David Windsor Reviewed-by: Hans Liljestrand Signed-off-by: Elena Reshetova Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/qp.c | 8 ++++---- include/linux/mlx4/device.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 203320923340..769598f7b6c8 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -55,7 +55,7 @@ void mlx4_qp_event(struct mlx4_dev *dev, u32 qpn, int event_type) qp = __mlx4_qp_lookup(dev, qpn); if (qp) - atomic_inc(&qp->refcount); + refcount_inc(&qp->refcount); spin_unlock(&qp_table->lock); @@ -66,7 +66,7 @@ void mlx4_qp_event(struct mlx4_dev *dev, u32 qpn, int event_type) qp->event(qp, event_type); - if (atomic_dec_and_test(&qp->refcount)) + if (refcount_dec_and_test(&qp->refcount)) complete(&qp->free); } @@ -420,7 +420,7 @@ int mlx4_qp_alloc(struct mlx4_dev *dev, int qpn, struct mlx4_qp *qp) if (err) goto err_icm; - atomic_set(&qp->refcount, 1); + refcount_set(&qp->refcount, 1); init_completion(&qp->free); return 0; @@ -520,7 +520,7 @@ EXPORT_SYMBOL_GPL(mlx4_qp_remove); void mlx4_qp_free(struct mlx4_dev *dev, struct mlx4_qp *qp) { - if (atomic_dec_and_test(&qp->refcount)) + if (refcount_dec_and_test(&qp->refcount)) complete(&qp->free); wait_for_completion(&qp->free); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index daac2e3a1a58..b8e19c4d6caa 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -768,7 +768,7 @@ struct mlx4_qp { int qpn; - atomic_t refcount; + refcount_t refcount; struct completion free; u8 usage; }; -- cgit v1.2.3-59-g8ed1b From 17ac99b2b8d08ed40f4525491d6eff330329a6d2 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 20 Oct 2017 10:23:39 +0300 Subject: drivers, net, mlx4: convert mlx4_srq.refcount from atomic_t to refcount_t atomic_t variables are currently used to implement reference counters with the following properties: - counter is initialized to 1 using atomic_set() - a resource is freed upon counter reaching zero - once counter reaches zero, its further increments aren't allowed - counter schema uses basic atomic operations (set, inc, inc_not_zero, dec_and_test, etc.) Such atomic variables should be converted to a newly provided refcount_t type and API that prevents accidental counter overflows and underflows. This is important since overflows and underflows can lead to use-after-free situation and be exploitable. The variable mlx4_srq.refcount is used as pure reference counter. Convert it to refcount_t and fix up the operations. Suggested-by: Kees Cook Reviewed-by: David Windsor Reviewed-by: Hans Liljestrand Signed-off-by: Elena Reshetova Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx4/srq.c | 8 ++++---- include/linux/mlx4/device.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c index bedf52126824..cbe4d9746ddf 100644 --- a/drivers/net/ethernet/mellanox/mlx4/srq.c +++ b/drivers/net/ethernet/mellanox/mlx4/srq.c @@ -49,7 +49,7 @@ void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type) srq = radix_tree_lookup(&srq_table->tree, srqn & (dev->caps.num_srqs - 1)); rcu_read_unlock(); if (srq) - atomic_inc(&srq->refcount); + refcount_inc(&srq->refcount); else { mlx4_warn(dev, "Async event for bogus SRQ %08x\n", srqn); return; @@ -57,7 +57,7 @@ void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type) srq->event(srq, event_type); - if (atomic_dec_and_test(&srq->refcount)) + if (refcount_dec_and_test(&srq->refcount)) complete(&srq->free); } @@ -203,7 +203,7 @@ int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, u32 cqn, u16 xrcd, if (err) goto err_radix; - atomic_set(&srq->refcount, 1); + refcount_set(&srq->refcount, 1); init_completion(&srq->free); return 0; @@ -232,7 +232,7 @@ void mlx4_srq_free(struct mlx4_dev *dev, struct mlx4_srq *srq) radix_tree_delete(&srq_table->tree, srq->srqn); spin_unlock_irq(&srq_table->lock); - if (atomic_dec_and_test(&srq->refcount)) + if (refcount_dec_and_test(&srq->refcount)) complete(&srq->free); wait_for_completion(&srq->free); diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index b8e19c4d6caa..a9b5fed8f7c6 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -781,7 +781,7 @@ struct mlx4_srq { int max_gs; int wqe_shift; - atomic_t refcount; + refcount_t refcount; struct completion free; }; -- cgit v1.2.3-59-g8ed1b From a4b51a9f83c6d359ff8fc0c66009283b6fdeeaf8 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 20 Oct 2017 10:23:40 +0300 Subject: drivers, net, mlx5: convert mlx5_cq.refcount from atomic_t to refcount_t atomic_t variables are currently used to implement reference counters with the following properties: - counter is initialized to 1 using atomic_set() - a resource is freed upon counter reaching zero - once counter reaches zero, its further increments aren't allowed - counter schema uses basic atomic operations (set, inc, inc_not_zero, dec_and_test, etc.) Such atomic variables should be converted to a newly provided refcount_t type and API that prevents accidental counter overflows and underflows. This is important since overflows and underflows can lead to use-after-free situation and be exploitable. The variable mlx5_cq.refcount is used as pure reference counter. Convert it to refcount_t and fix up the operations. Suggested-by: Kees Cook Reviewed-by: David Windsor Reviewed-by: Hans Liljestrand Signed-off-by: Elena Reshetova Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/cq.c | 16 ++++++++-------- include/linux/mlx5/cq.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cq.c b/drivers/net/ethernet/mellanox/mlx5/core/cq.c index 336d4738b807..1016e05c7ec7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cq.c @@ -58,7 +58,7 @@ void mlx5_cq_tasklet_cb(unsigned long data) tasklet_ctx.list) { list_del_init(&mcq->tasklet_ctx.list); mcq->tasklet_ctx.comp(mcq); - if (atomic_dec_and_test(&mcq->refcount)) + if (refcount_dec_and_test(&mcq->refcount)) complete(&mcq->free); if (time_after(jiffies, end)) break; @@ -80,7 +80,7 @@ static void mlx5_add_cq_to_tasklet(struct mlx5_core_cq *cq) * still arrive. */ if (list_empty_careful(&cq->tasklet_ctx.list)) { - atomic_inc(&cq->refcount); + refcount_inc(&cq->refcount); list_add_tail(&cq->tasklet_ctx.list, &tasklet_ctx->list); } spin_unlock_irqrestore(&tasklet_ctx->lock, flags); @@ -94,7 +94,7 @@ void mlx5_cq_completion(struct mlx5_core_dev *dev, u32 cqn) spin_lock(&table->lock); cq = radix_tree_lookup(&table->tree, cqn); if (likely(cq)) - atomic_inc(&cq->refcount); + refcount_inc(&cq->refcount); spin_unlock(&table->lock); if (!cq) { @@ -106,7 +106,7 @@ void mlx5_cq_completion(struct mlx5_core_dev *dev, u32 cqn) cq->comp(cq); - if (atomic_dec_and_test(&cq->refcount)) + if (refcount_dec_and_test(&cq->refcount)) complete(&cq->free); } @@ -119,7 +119,7 @@ void mlx5_cq_event(struct mlx5_core_dev *dev, u32 cqn, int event_type) cq = radix_tree_lookup(&table->tree, cqn); if (cq) - atomic_inc(&cq->refcount); + refcount_inc(&cq->refcount); spin_unlock(&table->lock); @@ -130,7 +130,7 @@ void mlx5_cq_event(struct mlx5_core_dev *dev, u32 cqn, int event_type) cq->event(cq, event_type); - if (atomic_dec_and_test(&cq->refcount)) + if (refcount_dec_and_test(&cq->refcount)) complete(&cq->free); } @@ -159,7 +159,7 @@ int mlx5_core_create_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq, cq->cqn = MLX5_GET(create_cq_out, out, cqn); cq->cons_index = 0; cq->arm_sn = 0; - atomic_set(&cq->refcount, 1); + refcount_set(&cq->refcount, 1); init_completion(&cq->free); if (!cq->comp) cq->comp = mlx5_add_cq_to_tasklet; @@ -222,7 +222,7 @@ int mlx5_core_destroy_cq(struct mlx5_core_dev *dev, struct mlx5_core_cq *cq) synchronize_irq(cq->irqn); mlx5_debug_cq_remove(dev, cq); - if (atomic_dec_and_test(&cq->refcount)) + if (refcount_dec_and_test(&cq->refcount)) complete(&cq->free); wait_for_completion(&cq->free); diff --git a/include/linux/mlx5/cq.h b/include/linux/mlx5/cq.h index 95898847c7d4..6a57ec2f1ef7 100644 --- a/include/linux/mlx5/cq.h +++ b/include/linux/mlx5/cq.h @@ -35,7 +35,7 @@ #include #include - +#include struct mlx5_core_cq { u32 cqn; @@ -43,7 +43,7 @@ struct mlx5_core_cq { __be32 *set_ci_db; __be32 *arm_db; struct mlx5_uars_page *uar; - atomic_t refcount; + refcount_t refcount; struct completion free; unsigned vector; unsigned int irqn; -- cgit v1.2.3-59-g8ed1b From e65f7ee39b4d7604a78b03ed35d723e1001fc241 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 20 Oct 2017 10:23:49 +0300 Subject: drivers, connector: convert cn_callback_entry.refcnt from atomic_t to refcount_t atomic_t variables are currently used to implement reference counters with the following properties: - counter is initialized to 1 using atomic_set() - a resource is freed upon counter reaching zero - once counter reaches zero, its further increments aren't allowed - counter schema uses basic atomic operations (set, inc, inc_not_zero, dec_and_test, etc.) Such atomic variables should be converted to a newly provided refcount_t type and API that prevents accidental counter overflows and underflows. This is important since overflows and underflows can lead to use-after-free situation and be exploitable. The variable cn_callback_entry.refcnt is used as pure reference counter. Convert it to refcount_t and fix up the operations. Suggested-by: Kees Cook Reviewed-by: David Windsor Reviewed-by: Hans Liljestrand Signed-off-by: Elena Reshetova Signed-off-by: David S. Miller --- drivers/connector/cn_queue.c | 4 ++-- drivers/connector/connector.c | 2 +- include/linux/connector.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/connector/cn_queue.c b/drivers/connector/cn_queue.c index 1f8bf054d11c..9c54fdf7acea 100644 --- a/drivers/connector/cn_queue.c +++ b/drivers/connector/cn_queue.c @@ -45,7 +45,7 @@ cn_queue_alloc_callback_entry(struct cn_queue_dev *dev, const char *name, return NULL; } - atomic_set(&cbq->refcnt, 1); + refcount_set(&cbq->refcnt, 1); atomic_inc(&dev->refcnt); cbq->pdev = dev; @@ -58,7 +58,7 @@ cn_queue_alloc_callback_entry(struct cn_queue_dev *dev, const char *name, void cn_queue_release_callback(struct cn_callback_entry *cbq) { - if (!atomic_dec_and_test(&cbq->refcnt)) + if (!refcount_dec_and_test(&cbq->refcnt)) return; atomic_dec(&cbq->pdev->refcnt); diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 25693b045371..8615594bd065 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -157,7 +157,7 @@ static int cn_call_callback(struct sk_buff *skb) spin_lock_bh(&dev->cbdev->queue_lock); list_for_each_entry(i, &dev->cbdev->queue_list, callback_entry) { if (cn_cb_equal(&i->id.id, &msg->id)) { - atomic_inc(&i->refcnt); + refcount_inc(&i->refcnt); cbq = i; break; } diff --git a/include/linux/connector.h b/include/linux/connector.h index f8fe8637d771..032102b19645 100644 --- a/include/linux/connector.h +++ b/include/linux/connector.h @@ -22,7 +22,7 @@ #define __CONNECTOR_H -#include +#include #include #include @@ -49,7 +49,7 @@ struct cn_callback_id { struct cn_callback_entry { struct list_head callback_entry; - atomic_t refcnt; + refcount_t refcnt; struct cn_queue_dev *pdev; struct cn_callback_id id; -- cgit v1.2.3-59-g8ed1b From e6546ef6d86d0fc38e0e84ccae80e641f3fc0087 Mon Sep 17 00:00:00 2001 From: Lawrence Brakmo Date: Fri, 20 Oct 2017 11:05:39 -0700 Subject: bpf: add support for BPF_SOCK_OPS_BASE_RTT A congestion control algorithm can make a call to the BPF socket_ops program to request the base RTT. The base RTT can be congestion control dependent and is meant to represent a congestion threshold such that RTTs above it indicate congestion. This is especially useful for flows within a DC where the base RTT is easy to obtain. Being provided a base RTT solves a basic problem in RTT based congestion avoidance algorithms (such as Vegas, NV and BBR). Although it is easy to get the base RTT when the network is not congested, it is very diffcult to do when it is very congested. Newer connections get an inflated value of the base RTT leading to unfariness (newer flows with a larger base RTT get more bandwidth). As a result, RTT based congestion avoidance algorithms tend to update their base RTTs to improve fairness. In very congested networks this can lead to base RTT inflation, reducing the ability of these RTT based congestion control algorithms to prevent congestion. Note that in my experiments with TCP-NV, the base RTT provided can be much larger than the actual hardware RTT. For example, experimenting with hosts within a rack where the hardware RTT is 16-20us, I've used base RTTs up to 150us. The effect of using a larger base RTT is that the congestion avoidance algorithm will allow more queueing. When there are only a few flows the main effect is larger measured RTTs and RPC latencies due to the increased queueing. When there are a lot of flows, a larger base RTT can lead to more congestion and more packet drops. For this case, where the hardware RTT is 20us, a base RTT of 80us produces good results. This patch only introduces BPF_SOCK_OPS_BASE_RTT, a later patch in this set adds support for using it in TCP-NV. Further study and testing is needed before support can be added to other delay based congestion avoidance algorithms. Signed-off-by: Lawrence Brakmo Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index d83f95ea6a1b..1aca744c220f 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -955,6 +955,13 @@ enum { BPF_SOCK_OPS_NEEDS_ECN, /* If connection's congestion control * needs ECN */ + BPF_SOCK_OPS_BASE_RTT, /* Get base RTT. The correct value is + * based on the path and may be + * dependent on the congestion control + * algorithm. In general it indicates + * a congestion threshold. RTTs above + * this indicate congestion + */ }; #define TCP_BPF_IW 1001 /* Set TCP initial congestion window */ -- cgit v1.2.3-59-g8ed1b From cd86d1fd21025fdd6daf23d1288da405e7ad0ec6 Mon Sep 17 00:00:00 2001 From: Lawrence Brakmo Date: Fri, 20 Oct 2017 11:05:40 -0700 Subject: bpf: Adding helper function bpf_getsockops Adding support for helper function bpf_getsockops to socket_ops BPF programs. This patch only supports TCP_CONGESTION. Signed-off-by: Vlad Vysotsky Acked-by: Lawrence Brakmo Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 19 ++++++++++--- net/core/filter.c | 46 ++++++++++++++++++++++++++++++- tools/testing/selftests/bpf/bpf_helpers.h | 3 ++ 3 files changed, 63 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1aca744c220f..f650346aaa1a 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -613,12 +613,22 @@ union bpf_attr { * int bpf_setsockopt(bpf_socket, level, optname, optval, optlen) * Calls setsockopt. Not all opts are available, only those with * integer optvals plus TCP_CONGESTION. - * Supported levels: SOL_SOCKET and IPROTO_TCP + * Supported levels: SOL_SOCKET and IPPROTO_TCP * @bpf_socket: pointer to bpf_socket - * @level: SOL_SOCKET or IPROTO_TCP + * @level: SOL_SOCKET or IPPROTO_TCP * @optname: option name * @optval: pointer to option value - * @optlen: length of optval in byes + * @optlen: length of optval in bytes + * Return: 0 or negative error + * + * int bpf_getsockopt(bpf_socket, level, optname, optval, optlen) + * Calls getsockopt. Not all opts are available. + * Supported levels: IPPROTO_TCP + * @bpf_socket: pointer to bpf_socket + * @level: IPPROTO_TCP + * @optname: option name + * @optval: pointer to option value + * @optlen: length of optval in bytes * Return: 0 or negative error * * int bpf_skb_adjust_room(skb, len_diff, mode, flags) @@ -721,7 +731,8 @@ union bpf_attr { FN(sock_map_update), \ FN(xdp_adjust_meta), \ FN(perf_event_read_value), \ - FN(perf_prog_read_value), + FN(perf_prog_read_value), \ + FN(getsockopt), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/net/core/filter.c b/net/core/filter.c index 09e011f20291..ccf62f44140a 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3273,7 +3273,7 @@ BPF_CALL_5(bpf_setsockopt, struct bpf_sock_ops_kern *, bpf_sock, static const struct bpf_func_proto bpf_setsockopt_proto = { .func = bpf_setsockopt, - .gpl_only = true, + .gpl_only = false, .ret_type = RET_INTEGER, .arg1_type = ARG_PTR_TO_CTX, .arg2_type = ARG_ANYTHING, @@ -3282,6 +3282,48 @@ static const struct bpf_func_proto bpf_setsockopt_proto = { .arg5_type = ARG_CONST_SIZE, }; +BPF_CALL_5(bpf_getsockopt, struct bpf_sock_ops_kern *, bpf_sock, + int, level, int, optname, char *, optval, int, optlen) +{ + struct sock *sk = bpf_sock->sk; + int ret = 0; + + if (!sk_fullsock(sk)) + goto err_clear; + +#ifdef CONFIG_INET + if (level == SOL_TCP && sk->sk_prot->getsockopt == tcp_getsockopt) { + if (optname == TCP_CONGESTION) { + struct inet_connection_sock *icsk = inet_csk(sk); + + if (!icsk->icsk_ca_ops || optlen <= 1) + goto err_clear; + strncpy(optval, icsk->icsk_ca_ops->name, optlen); + optval[optlen - 1] = 0; + } else { + goto err_clear; + } + } else { + goto err_clear; + } + return ret; +#endif +err_clear: + memset(optval, 0, optlen); + return -EINVAL; +} + +static const struct bpf_func_proto bpf_getsockopt_proto = { + .func = bpf_getsockopt, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, + .arg4_type = ARG_PTR_TO_UNINIT_MEM, + .arg5_type = ARG_CONST_SIZE, +}; + static const struct bpf_func_proto * bpf_base_func_proto(enum bpf_func_id func_id) { @@ -3460,6 +3502,8 @@ static const struct bpf_func_proto * switch (func_id) { case BPF_FUNC_setsockopt: return &bpf_setsockopt_proto; + case BPF_FUNC_getsockopt: + return &bpf_getsockopt_proto; case BPF_FUNC_sock_map_update: return &bpf_sock_map_update_proto; default: diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index e25dbf6038cf..609514f74482 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -67,6 +67,9 @@ static int (*bpf_xdp_adjust_meta)(void *ctx, int offset) = static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval, int optlen) = (void *) BPF_FUNC_setsockopt; +static int (*bpf_getsockopt)(void *ctx, int level, int optname, void *optval, + int optlen) = + (void *) BPF_FUNC_getsockopt; static int (*bpf_sk_redirect_map)(void *map, int key, int flags) = (void *) BPF_FUNC_sk_redirect_map; static int (*bpf_sock_map_update)(void *map, void *key, void *value, -- cgit v1.2.3-59-g8ed1b From eb297bc716ec2cb3147e6e99002e37058c65cba3 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 10 Oct 2017 22:08:54 -0700 Subject: of: reserved_mem: Accessor for acquiring reserved_mem In some cases drivers referencing a reserved-memory region might want to remap the entire region, but when defining the reserved-memory by "size" the client driver has no means to know the associated base address of the reserved memory region. This patch adds an accessor for such drivers to acquire a handle to their associated reserved-memory for this purpose. A complicating factor for the implementation is that the reserved_mem objects are created from the flattened DeviceTree, as such we can't use the device_node address for comparison. Fortunately the name of the node will be used as "name" of the reserved_mem and will be used when building the full_name, so we can compare the "name" with the basename of the full_name to find the match. Reviewed-by: Rob Herring Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/of/of_reserved_mem.c | 26 ++++++++++++++++++++++++++ include/linux/of_reserved_mem.h | 5 +++++ 2 files changed, 31 insertions(+) (limited to 'include') diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index d507c3569a88..b108c7a1f74c 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -397,3 +397,29 @@ void of_reserved_mem_device_release(struct device *dev) rmem->ops->device_release(rmem, dev); } EXPORT_SYMBOL_GPL(of_reserved_mem_device_release); + +/** + * of_reserved_mem_lookup() - acquire reserved_mem from a device node + * @np: node pointer of the desired reserved-memory region + * + * This function allows drivers to acquire a reference to the reserved_mem + * struct based on a device node handle. + * + * Returns a reserved_mem reference, or NULL on error. + */ +struct reserved_mem *of_reserved_mem_lookup(struct device_node *np) +{ + const char *name; + int i; + + if (!np->full_name) + return NULL; + + name = kbasename(np->full_name); + for (i = 0; i < reserved_mem_count; i++) + if (!strcmp(reserved_mem[i].name, name)) + return &reserved_mem[i]; + + return NULL; +} +EXPORT_SYMBOL_GPL(of_reserved_mem_lookup); diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h index f8e1992d6423..c58f780104f9 100644 --- a/include/linux/of_reserved_mem.h +++ b/include/linux/of_reserved_mem.h @@ -44,6 +44,7 @@ int early_init_dt_alloc_reserved_memory_arch(phys_addr_t size, void fdt_init_reserved_mem(void); void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size); +struct reserved_mem *of_reserved_mem_lookup(struct device_node *np); #else static inline int of_reserved_mem_device_init_by_idx(struct device *dev, struct device_node *np, int idx) @@ -55,6 +56,10 @@ static inline void of_reserved_mem_device_release(struct device *pdev) { } static inline void fdt_init_reserved_mem(void) { } static inline void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size) { } +static inline struct reserved_mem *of_reserved_mem_lookup(struct device_node *np) +{ + return NULL; +} #endif /** -- cgit v1.2.3-59-g8ed1b From 3064abfa932bd09faf8da01741d171d476cf7193 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 29 Jun 2017 22:49:31 -0700 Subject: drm: Add CRTC_GET_SEQUENCE and CRTC_QUEUE_SEQUENCE ioctls [v3] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These provide crtc-id based functions instead of pipe-number, while also offering higher resolution time (ns) and wider frame count (64) as required by the Vulkan API. v2: * Check for DRIVER_MODESET in new crtc-based vblank ioctls Failing to check this will oops the driver. * Ensure vblank interupt is running in crtc_get_sequence ioctl The sequence and timing values are not correct while the interrupt is off, so make sure it's running before asking for them. * Short-circuit get_sequence if the counter is enabled and accurate Steal the idea from the code in wait_vblank to avoid the expense of drm_vblank_get/put * Return active state of crtc in crtc_get_sequence ioctl Might be useful for applications that aren't in charge of modesetting? * Use drm_crtc_vblank_get/put in new crtc-based vblank sequence ioctls Daniel Vetter prefers these over the old drm_vblank_put/get APIs. * Return s64 ns instead of u64 in new sequence event Suggested-by: Daniel Vetter Suggested-by: Ville Syrjälä v3: * Removed FIRST_PIXEL_OUT_FLAG * Document that the timestamp in the query and event are that of the first pixel leaving the display engine for the display (using the same wording as the Vulkan spec). Suggested-by: Michel Dänzer Acked-by: Dave Airlie [airlied: left->leaves (Michel)] Signed-off-by: Keith Packard Reviewed-by: Sean Paul Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_internal.h | 6 ++ drivers/gpu/drm/drm_ioctl.c | 2 + drivers/gpu/drm/drm_vblank.c | 168 +++++++++++++++++++++++++++++++++++++++++ include/drm/drm_vblank.h | 1 + include/uapi/drm/drm.h | 36 +++++++++ 5 files changed, 213 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index edd921adcf33..c9d5a6cd4d41 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -70,6 +70,12 @@ int drm_legacy_modeset_ctl_ioctl(struct drm_device *dev, void *data, int drm_legacy_irq_control(struct drm_device *dev, void *data, struct drm_file *file_priv); +int drm_crtc_get_sequence_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + +int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); + /* drm_auth.c */ int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index a78f03155466..9c435a4c0c82 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -663,6 +663,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_SIGNAL, drm_syncobj_signal_ioctl, DRM_UNLOCKED|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF(DRM_IOCTL_CRTC_GET_SEQUENCE, drm_crtc_get_sequence_ioctl, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_CRTC_QUEUE_SEQUENCE, drm_crtc_queue_sequence_ioctl, DRM_UNLOCKED), }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 27b6db073a5c..13722c373a6a 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -819,6 +819,11 @@ static void send_vblank_event(struct drm_device *dev, e->event.vbl.tv_sec = tv.tv_sec; e->event.vbl.tv_usec = tv.tv_nsec / 1000; break; + case DRM_EVENT_CRTC_SEQUENCE: + if (seq) + e->event.seq.sequence = seq; + e->event.seq.time_ns = ktime_to_ns(now); + break; } trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, seq); drm_send_event_locked(dev, &e->base); @@ -1650,3 +1655,166 @@ bool drm_crtc_handle_vblank(struct drm_crtc *crtc) return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc)); } EXPORT_SYMBOL(drm_crtc_handle_vblank); + +/* + * Get crtc VBLANK count. + * + * \param dev DRM device + * \param data user arguement, pointing to a drm_crtc_get_sequence structure. + * \param file_priv drm file private for the user's open file descriptor + */ + +int drm_crtc_get_sequence_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_crtc *crtc; + struct drm_vblank_crtc *vblank; + int pipe; + struct drm_crtc_get_sequence *get_seq = data; + ktime_t now; + bool vblank_enabled; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + if (!dev->irq_enabled) + return -EINVAL; + + crtc = drm_crtc_find(dev, file_priv, get_seq->crtc_id); + if (!crtc) + return -ENOENT; + + pipe = drm_crtc_index(crtc); + + vblank = &dev->vblank[pipe]; + vblank_enabled = dev->vblank_disable_immediate && READ_ONCE(vblank->enabled); + + if (!vblank_enabled) { + ret = drm_crtc_vblank_get(crtc); + if (ret) { + DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret); + return ret; + } + } + drm_modeset_lock(&crtc->mutex, NULL); + if (crtc->state) + get_seq->active = crtc->state->enable; + else + get_seq->active = crtc->enabled; + drm_modeset_unlock(&crtc->mutex); + get_seq->sequence = drm_vblank_count_and_time(dev, pipe, &now); + get_seq->sequence_ns = ktime_to_ns(now); + if (!vblank_enabled) + drm_crtc_vblank_put(crtc); + return 0; +} + +/* + * Queue a event for VBLANK sequence + * + * \param dev DRM device + * \param data user arguement, pointing to a drm_crtc_queue_sequence structure. + * \param file_priv drm file private for the user's open file descriptor + */ + +int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_crtc *crtc; + struct drm_vblank_crtc *vblank; + int pipe; + struct drm_crtc_queue_sequence *queue_seq = data; + ktime_t now; + struct drm_pending_vblank_event *e; + u32 flags; + u64 seq; + u64 req_seq; + int ret; + unsigned long spin_flags; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + if (!dev->irq_enabled) + return -EINVAL; + + crtc = drm_crtc_find(dev, file_priv, queue_seq->crtc_id); + if (!crtc) + return -ENOENT; + + flags = queue_seq->flags; + /* Check valid flag bits */ + if (flags & ~(DRM_CRTC_SEQUENCE_RELATIVE| + DRM_CRTC_SEQUENCE_NEXT_ON_MISS)) + return -EINVAL; + + pipe = drm_crtc_index(crtc); + + vblank = &dev->vblank[pipe]; + + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (e == NULL) + return -ENOMEM; + + ret = drm_crtc_vblank_get(crtc); + if (ret) { + DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret); + goto err_free; + } + + seq = drm_vblank_count_and_time(dev, pipe, &now); + req_seq = queue_seq->sequence; + + if (flags & DRM_CRTC_SEQUENCE_RELATIVE) + req_seq += seq; + + if ((flags & DRM_CRTC_SEQUENCE_NEXT_ON_MISS) && vblank_passed(seq, req_seq)) + req_seq = seq + 1; + + e->pipe = pipe; + e->event.base.type = DRM_EVENT_CRTC_SEQUENCE; + e->event.base.length = sizeof(e->event.seq); + e->event.seq.user_data = queue_seq->user_data; + + spin_lock_irqsave(&dev->event_lock, spin_flags); + + /* + * drm_crtc_vblank_off() might have been called after we called + * drm_crtc_vblank_get(). drm_crtc_vblank_off() holds event_lock around the + * vblank disable, so no need for further locking. The reference from + * drm_crtc_vblank_get() protects against vblank disable from another source. + */ + if (!READ_ONCE(vblank->enabled)) { + ret = -EINVAL; + goto err_unlock; + } + + ret = drm_event_reserve_init_locked(dev, file_priv, &e->base, + &e->event.base); + + if (ret) + goto err_unlock; + + e->sequence = req_seq; + + if (vblank_passed(seq, req_seq)) { + drm_crtc_vblank_put(crtc); + send_vblank_event(dev, e, seq, now); + queue_seq->sequence = seq; + } else { + /* drm_handle_vblank_events will call drm_vblank_put */ + list_add_tail(&e->base.link, &dev->vblank_event_list); + queue_seq->sequence = req_seq; + } + + spin_unlock_irqrestore(&dev->event_lock, spin_flags); + return 0; + +err_unlock: + spin_unlock_irqrestore(&dev->event_lock, spin_flags); + drm_crtc_vblank_put(crtc); +err_free: + kfree(e); + return ret; +} diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index bf8e07035a0a..848b463a0af5 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -57,6 +57,7 @@ struct drm_pending_vblank_event { union { struct drm_event base; struct drm_event_vblank vbl; + struct drm_event_crtc_sequence seq; } event; }; diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 97677cd6964d..91d83c1747c0 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -737,6 +737,28 @@ struct drm_syncobj_array { __u32 pad; }; +/* Query current scanout sequence number */ +struct drm_crtc_get_sequence { + __u32 crtc_id; /* requested crtc_id */ + __u32 active; /* return: crtc output is active */ + __u64 sequence; /* return: most recent vblank sequence */ + __s64 sequence_ns; /* return: most recent time of first pixel out */ +}; + +/* Queue event to be delivered at specified sequence. Time stamp marks + * when the first pixel of the refresh cycle leaves the display engine + * for the display + */ +#define DRM_CRTC_SEQUENCE_RELATIVE 0x00000001 /* sequence is relative to current */ +#define DRM_CRTC_SEQUENCE_NEXT_ON_MISS 0x00000002 /* Use next sequence if we've missed */ + +struct drm_crtc_queue_sequence { + __u32 crtc_id; + __u32 flags; + __u64 sequence; /* on input, target sequence. on output, actual sequence */ + __u64 user_data; /* user data passed to event */ +}; + #if defined(__cplusplus) } #endif @@ -819,6 +841,9 @@ extern "C" { #define DRM_IOCTL_WAIT_VBLANK DRM_IOWR(0x3a, union drm_wait_vblank) +#define DRM_IOCTL_CRTC_GET_SEQUENCE DRM_IOWR(0x3b, struct drm_crtc_get_sequence) +#define DRM_IOCTL_CRTC_QUEUE_SEQUENCE DRM_IOWR(0x3c, struct drm_crtc_queue_sequence) + #define DRM_IOCTL_UPDATE_DRAW DRM_IOW(0x3f, struct drm_update_draw) #define DRM_IOCTL_MODE_GETRESOURCES DRM_IOWR(0xA0, struct drm_mode_card_res) @@ -893,6 +918,7 @@ struct drm_event { #define DRM_EVENT_VBLANK 0x01 #define DRM_EVENT_FLIP_COMPLETE 0x02 +#define DRM_EVENT_CRTC_SEQUENCE 0x03 struct drm_event_vblank { struct drm_event base; @@ -903,6 +929,16 @@ struct drm_event_vblank { __u32 crtc_id; /* 0 on older kernels that do not support this */ }; +/* Event delivered at sequence. Time stamp marks when the first pixel + * of the refresh cycle leaves the display engine for the display + */ +struct drm_event_crtc_sequence { + struct drm_event base; + __u64 user_data; + __s64 time_ns; + __u64 sequence; +}; + /* typedef area */ #ifndef __KERNEL__ typedef struct drm_clip_rect drm_clip_rect_t; -- cgit v1.2.3-59-g8ed1b From 176aa36012135d172394a928a03fb03dfecd83f9 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 21 Sep 2017 12:11:24 +0900 Subject: extcon: Split out extcon header file for consumer and provider device The extcon has two type of extcon devices as following. - 'extcon provider deivce' adds new extcon device and detect the state/properties of external connector. Also, it notifies the state/properties to the extcon consumer device. - 'extcon consumer device' gets the change state/properties from extcon provider device. Prior to that, include/linux/extcon.h contains all exported API for both provider and consumer device driver. To clarify the meaning of header file and to remove the wrong use-case on consumer device, this patch separates into extcon.h and extcon-provider.h. [Description for include/linux/{extcon.h|extcon-provider.h}] - extcon.h includes the extcon API and data structure for extcon consumer device driver. This header file contains the following APIs: : Register/unregister the notifier to catch the change of extcon device : Get the extcon device instance : Get the extcon device name : Get the state of each external connector : Get the property value of each external connector : Get the property capability of each external connector - extcon-provider.h includes the extcon API and data structure for extcon provider device driver. This header file contains the following APIs: : Include 'include/linux/extcon.h' : Allocate the memory for extcon device instance : Register/unregister extcon device : Set the state of each external connector : Set the property value of each external connector : Set the property capability of each external connector Signed-off-by: Chanwoo Choi Acked-by: Sebastian Reichel Acked-by: Chen-Yu Tsai Acked-by: Charles Keepax Acked-by: Lee Jones Acked-by: Felipe Balbi Acked-by: Yoshihiro Shimoda Acked-by: Kishon Vijay Abraham I --- drivers/extcon/extcon-adc-jack.c | 2 +- drivers/extcon/extcon-arizona.c | 2 +- drivers/extcon/extcon-axp288.c | 2 +- drivers/extcon/extcon-gpio.c | 2 +- drivers/extcon/extcon-intel-cht-wc.c | 2 +- drivers/extcon/extcon-intel-int3496.c | 2 +- drivers/extcon/extcon-max14577.c | 2 +- drivers/extcon/extcon-max3355.c | 2 +- drivers/extcon/extcon-max77693.c | 2 +- drivers/extcon/extcon-max77843.c | 2 +- drivers/extcon/extcon-max8997.c | 2 +- drivers/extcon/extcon-qcom-spmi-misc.c | 2 +- drivers/extcon/extcon-rt8973a.c | 2 +- drivers/extcon/extcon-sm5502.c | 2 +- drivers/extcon/extcon-usb-gpio.c | 2 +- drivers/extcon/extcon-usbc-cros-ec.c | 2 +- drivers/extcon/extcon.h | 2 +- drivers/phy/allwinner/phy-sun4i-usb.c | 2 +- drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c | 2 +- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 2 +- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 2 +- drivers/power/supply/qcom_smbb.c | 2 +- drivers/usb/gadget/udc/renesas_usb3.c | 2 +- drivers/usb/phy/phy-tahvo.c | 2 +- include/linux/extcon-provider.h | 142 ++++++++++++++++++++++++++ include/linux/extcon.h | 109 +------------------- include/linux/mfd/palmas.h | 2 +- 27 files changed, 172 insertions(+), 129 deletions(-) create mode 100644 include/linux/extcon-provider.h (limited to 'include') diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c index 6f6537ab0a79..3877d86c746a 100644 --- a/drivers/extcon/extcon-adc-jack.c +++ b/drivers/extcon/extcon-adc-jack.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include /** * struct adc_jack_data - internal data for adc_jack device driver diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index f84da4a17724..da0e9bc4262f 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include diff --git a/drivers/extcon/extcon-axp288.c b/drivers/extcon/extcon-axp288.c index f4fd03e58e37..981fba56bc18 100644 --- a/drivers/extcon/extcon-axp288.c +++ b/drivers/extcon/extcon-axp288.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index ebed22f22d75..ab770adcca7e 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -17,7 +17,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include diff --git a/drivers/extcon/extcon-intel-cht-wc.c b/drivers/extcon/extcon-intel-cht-wc.c index 91a0023074af..7c4bc8c44c3f 100644 --- a/drivers/extcon/extcon-intel-cht-wc.c +++ b/drivers/extcon/extcon-intel-cht-wc.c @@ -15,7 +15,7 @@ * more details. */ -#include +#include #include #include #include diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c index 1a45e745717d..c8691b5a9cb0 100644 --- a/drivers/extcon/extcon-intel-int3496.c +++ b/drivers/extcon/extcon-intel-int3496.c @@ -19,7 +19,7 @@ */ #include -#include +#include #include #include #include diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c index f6414b7fa5bc..6c2c9996eb71 100644 --- a/drivers/extcon/extcon-max14577.c +++ b/drivers/extcon/extcon-max14577.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #define DELAY_MS_DEFAULT 17000 /* unit: millisecond */ diff --git a/drivers/extcon/extcon-max3355.c b/drivers/extcon/extcon-max3355.c index 533e16a952b8..0aa410836f4e 100644 --- a/drivers/extcon/extcon-max3355.c +++ b/drivers/extcon/extcon-max3355.c @@ -9,7 +9,7 @@ * may be copied, distributed, and modified under those terms. */ -#include +#include #include #include #include diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c index 7a5856809047..643411066ad9 100644 --- a/drivers/extcon/extcon-max77693.c +++ b/drivers/extcon/extcon-max77693.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index 6e722d552cf1..28f251ff0fa2 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -11,7 +11,7 @@ * (at your option) any later version. */ -#include +#include #include #include #include diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c index 4a0612fb9c07..8152790d72e1 100644 --- a/drivers/extcon/extcon-max8997.c +++ b/drivers/extcon/extcon-max8997.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #define DEV_NAME "max8997-muic" diff --git a/drivers/extcon/extcon-qcom-spmi-misc.c b/drivers/extcon/extcon-qcom-spmi-misc.c index b8cde096a808..660bbf163bf5 100644 --- a/drivers/extcon/extcon-qcom-spmi-misc.c +++ b/drivers/extcon/extcon-qcom-spmi-misc.c @@ -15,7 +15,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c index eaa355e7d9e4..e059bd5f2041 100644 --- a/drivers/extcon/extcon-rt8973a.c +++ b/drivers/extcon/extcon-rt8973a.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include "extcon-rt8973a.h" diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index 106ef0297b53..0cfb5a3efdf6 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include "extcon-sm5502.h" diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index 9c925b05b7aa..53762864a9f7 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -14,7 +14,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include diff --git a/drivers/extcon/extcon-usbc-cros-ec.c b/drivers/extcon/extcon-usbc-cros-ec.c index 598956f1dcae..6187f731b29d 100644 --- a/drivers/extcon/extcon-usbc-cros-ec.c +++ b/drivers/extcon/extcon-usbc-cros-ec.c @@ -14,7 +14,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include diff --git a/drivers/extcon/extcon.h b/drivers/extcon/extcon.h index dddddcfa0587..cc1b436eb66a 100644 --- a/drivers/extcon/extcon.h +++ b/drivers/extcon/extcon.h @@ -1,7 +1,7 @@ #ifndef __LINUX_EXTCON_INTERNAL_H__ #define __LINUX_EXTCON_INTERNAL_H__ -#include +#include /** * struct extcon_dev - An extcon device represents one external connector. diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 1161e11fb3cf..ef34f97f214b 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c index d099a0c8cee5..7ceea5ae2704 100644 --- a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c +++ b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c @@ -12,7 +12,7 @@ */ #include -#include +#include #include #include #include diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 54c34298a000..b33e2994ccce 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -12,7 +12,7 @@ * published by the Free Software Foundation. */ -#include +#include #include #include #include diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index ee7ce5ee53f9..5049dac79bd0 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/power/supply/qcom_smbb.c b/drivers/power/supply/qcom_smbb.c index f6a0d245731d..11de691b9a71 100644 --- a/drivers/power/supply/qcom_smbb.c +++ b/drivers/power/supply/qcom_smbb.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #define SMBB_CHG_VMAX 0x040 diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index df37c1e6e9d5..8b4d051fdbed 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index 8babd318c0ed..354e8c98af05 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/linux/extcon-provider.h b/include/linux/extcon-provider.h new file mode 100644 index 000000000000..2feca5881fa7 --- /dev/null +++ b/include/linux/extcon-provider.h @@ -0,0 +1,142 @@ +/* + * External Connector (extcon) framework + * - linux/include/linux/extcon-provider.h for extcon provider device driver. + * + * Copyright (C) 2017 Samsung Electronics + * Author: Chanwoo Choi + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#ifndef __LINUX_EXTCON_PROVIDER_H__ +#define __LINUX_EXTCON_PROVIDER_H__ + +#include + +struct extcon_dev; + +#if IS_ENABLED(CONFIG_EXTCON) + +/* Following APIs register/unregister the extcon device. */ +extern int extcon_dev_register(struct extcon_dev *edev); +extern void extcon_dev_unregister(struct extcon_dev *edev); +extern int devm_extcon_dev_register(struct device *dev, + struct extcon_dev *edev); +extern void devm_extcon_dev_unregister(struct device *dev, + struct extcon_dev *edev); + +/* Following APIs allocate/free the memory of the extcon device. */ +extern struct extcon_dev *extcon_dev_allocate(const unsigned int *cable); +extern void extcon_dev_free(struct extcon_dev *edev); +extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, + const unsigned int *cable); +extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev); + +/* Synchronize the state and property value for each external connector. */ +extern int extcon_sync(struct extcon_dev *edev, unsigned int id); + +/* + * Following APIs set the connected state of each external connector. + * The 'id' argument indicates the defined external connector. + */ +extern int extcon_set_state(struct extcon_dev *edev, unsigned int id, + bool state); +extern int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, + bool state); + +/* + * Following APIs set the property of each external connector. + * The 'id' argument indicates the defined external connector + * and the 'prop' indicates the extcon property. + * + * And extcon_set_property_capability() set the capability of the property + * for each external connector. They are used to set the capability of the + * property of each external connector based on the id and property. + */ +extern int extcon_set_property(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value prop_val); +extern int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value prop_val); +extern int extcon_set_property_capability(struct extcon_dev *edev, + unsigned int id, unsigned int prop); + +#else /* CONFIG_EXTCON */ +static inline int extcon_dev_register(struct extcon_dev *edev) +{ + return 0; +} + +static inline void extcon_dev_unregister(struct extcon_dev *edev) { } + +static inline int devm_extcon_dev_register(struct device *dev, + struct extcon_dev *edev) +{ + return -EINVAL; +} + +static inline void devm_extcon_dev_unregister(struct device *dev, + struct extcon_dev *edev) { } + +static inline struct extcon_dev *extcon_dev_allocate(const unsigned int *cable) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void extcon_dev_free(struct extcon_dev *edev) { } + +static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, + const unsigned int *cable) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void devm_extcon_dev_free(struct extcon_dev *edev) { } + + +static inline int extcon_set_state(struct extcon_dev *edev, unsigned int id, + bool state) +{ + return 0; +} + +static inline int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, + bool state) +{ + return 0; +} + +static inline int extcon_sync(struct extcon_dev *edev, unsigned int id) +{ + return 0; +} + +static inline int extcon_set_property(struct extcon_dev *edev, unsigned int id, + unsigned int prop, + union extcon_property_value prop_val) +{ + return 0; +} + +static inline int extcon_set_property_sync(struct extcon_dev *edev, + unsigned int id, unsigned int prop, + union extcon_property_value prop_val) +{ + return 0; +} + +static inline int extcon_set_property_capability(struct extcon_dev *edev, + unsigned int id, unsigned int prop) +{ + return 0; +} +#endif /* CONFIG_EXTCON */ +#endif /* __LINUX_EXTCON_PROVIDER_H__ */ diff --git a/include/linux/extcon.h b/include/linux/extcon.h index 744d60ca80c3..6d94e82c8ad9 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -1,5 +1,6 @@ /* * External Connector (extcon) framework + * - linux/include/linux/extcon.h for extcon consumer device driver. * * Copyright (C) 2015 Samsung Electronics * Author: Chanwoo Choi @@ -170,61 +171,29 @@ union extcon_property_value { int intval; /* type : integer (intval) */ }; -struct extcon_cable; struct extcon_dev; #if IS_ENABLED(CONFIG_EXTCON) - -/* Following APIs register/unregister the extcon device. */ -extern int extcon_dev_register(struct extcon_dev *edev); -extern void extcon_dev_unregister(struct extcon_dev *edev); -extern int devm_extcon_dev_register(struct device *dev, - struct extcon_dev *edev); -extern void devm_extcon_dev_unregister(struct device *dev, - struct extcon_dev *edev); - -/* Following APIs allocate/free the memory of the extcon device. */ -extern struct extcon_dev *extcon_dev_allocate(const unsigned int *cable); -extern void extcon_dev_free(struct extcon_dev *edev); -extern struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, - const unsigned int *cable); -extern void devm_extcon_dev_free(struct device *dev, struct extcon_dev *edev); - -/* Synchronize the state and property value for each external connector. */ -extern int extcon_sync(struct extcon_dev *edev, unsigned int id); - /* - * Following APIs get/set the connected state of each external connector. + * Following APIs get the connected state of each external connector. * The 'id' argument indicates the defined external connector. */ extern int extcon_get_state(struct extcon_dev *edev, unsigned int id); -extern int extcon_set_state(struct extcon_dev *edev, unsigned int id, - bool state); -extern int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, - bool state); /* - * Following APIs get/set the property of each external connector. + * Following APIs get the property of each external connector. * The 'id' argument indicates the defined external connector * and the 'prop' indicates the extcon property. * - * And extcon_get/set_property_capability() set the capability of the property - * for each external connector. They are used to set the capability of the + * And extcon_get_property_capability() get the capability of the property + * for each external connector. They are used to get the capability of the * property of each external connector based on the id and property. */ extern int extcon_get_property(struct extcon_dev *edev, unsigned int id, unsigned int prop, union extcon_property_value *prop_val); -extern int extcon_set_property(struct extcon_dev *edev, unsigned int id, - unsigned int prop, - union extcon_property_value prop_val); -extern int extcon_set_property_sync(struct extcon_dev *edev, unsigned int id, - unsigned int prop, - union extcon_property_value prop_val); extern int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, unsigned int prop); -extern int extcon_set_property_capability(struct extcon_dev *edev, - unsigned int id, unsigned int prop); /* * Following APIs register the notifier block in order to detect @@ -268,79 +237,17 @@ extern struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, extern const char *extcon_get_edev_name(struct extcon_dev *edev); #else /* CONFIG_EXTCON */ -static inline int extcon_dev_register(struct extcon_dev *edev) -{ - return 0; -} - -static inline void extcon_dev_unregister(struct extcon_dev *edev) { } - -static inline int devm_extcon_dev_register(struct device *dev, - struct extcon_dev *edev) -{ - return -EINVAL; -} - -static inline void devm_extcon_dev_unregister(struct device *dev, - struct extcon_dev *edev) { } - -static inline struct extcon_dev *extcon_dev_allocate(const unsigned int *cable) -{ - return ERR_PTR(-ENOSYS); -} - -static inline void extcon_dev_free(struct extcon_dev *edev) { } - -static inline struct extcon_dev *devm_extcon_dev_allocate(struct device *dev, - const unsigned int *cable) -{ - return ERR_PTR(-ENOSYS); -} - -static inline void devm_extcon_dev_free(struct extcon_dev *edev) { } - - static inline int extcon_get_state(struct extcon_dev *edev, unsigned int id) { return 0; } -static inline int extcon_set_state(struct extcon_dev *edev, unsigned int id, - bool state) -{ - return 0; -} - -static inline int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, - bool state) -{ - return 0; -} - -static inline int extcon_sync(struct extcon_dev *edev, unsigned int id) -{ - return 0; -} - static inline int extcon_get_property(struct extcon_dev *edev, unsigned int id, unsigned int prop, union extcon_property_value *prop_val) { return 0; } -static inline int extcon_set_property(struct extcon_dev *edev, unsigned int id, - unsigned int prop, - union extcon_property_value prop_val) -{ - return 0; -} - -static inline int extcon_set_property_sync(struct extcon_dev *edev, - unsigned int id, unsigned int prop, - union extcon_property_value prop_val) -{ - return 0; -} static inline int extcon_get_property_capability(struct extcon_dev *edev, unsigned int id, unsigned int prop) @@ -348,12 +255,6 @@ static inline int extcon_get_property_capability(struct extcon_dev *edev, return 0; } -static inline int extcon_set_property_capability(struct extcon_dev *edev, - unsigned int id, unsigned int prop) -{ - return 0; -} - static inline int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, struct notifier_block *nb) { diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 6dec43826303..3c8568aa82a5 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3-59-g8ed1b From 11a6e41c0ee503ffcb971d260bd07dc99b77f13a Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 4 Sep 2017 14:53:13 +0200 Subject: phy: Return NULL if the phy is optional If we're trying to get a handle to an optional phy, then the phy framework being disabled shouldn't return an hard error. Instead, return NULL just like phy_optional_get does when there's no phy provided in the DT. Signed-off-by: Maxime Ripard Signed-off-by: Kishon Vijay Abraham I --- include/linux/phy/phy.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index e694d4008c4a..10888a717860 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -291,7 +291,7 @@ static inline struct phy *devm_phy_get(struct device *dev, const char *string) static inline struct phy *devm_phy_optional_get(struct device *dev, const char *string) { - return ERR_PTR(-ENOSYS); + return NULL; } static inline struct phy *devm_of_phy_get(struct device *dev, -- cgit v1.2.3-59-g8ed1b From 55b20e8de9b25da3c2017131afef7e7e3e0e0202 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 12 Oct 2017 11:49:32 +0530 Subject: dt-bindings: phy: Add PHY_TYPE_UFS definition Add definition for UFS phy type. Signed-off-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- include/dt-bindings/phy/phy.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/dt-bindings/phy/phy.h b/include/dt-bindings/phy/phy.h index 6c901930eb3e..d16e8755f6a9 100644 --- a/include/dt-bindings/phy/phy.h +++ b/include/dt-bindings/phy/phy.h @@ -15,5 +15,6 @@ #define PHY_TYPE_PCIE 2 #define PHY_TYPE_USB2 3 #define PHY_TYPE_USB3 4 +#define PHY_TYPE_UFS 5 #endif /* _DT_BINDINGS_PHY */ -- cgit v1.2.3-59-g8ed1b From fd3e4c98e6e7a12dc47ef98ee12c0c3c024b5ee9 Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 12 Oct 2017 11:49:33 +0530 Subject: phy: Add UFS PHY modes UFS phy has two modes for each High speed generation. These modes are identified by two rates of operations - Rate A, and Rate B. Add these UFS phy modes to phy framework. Signed-off-by: Vivek Gautam Signed-off-by: Kishon Vijay Abraham I --- include/linux/phy/phy.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 10888a717860..194d08174516 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -29,6 +29,8 @@ enum phy_mode { PHY_MODE_USB_OTG, PHY_MODE_SGMII, PHY_MODE_10GKR, + PHY_MODE_UFS_HS_A, + PHY_MODE_UFS_HS_B, }; /** -- cgit v1.2.3-59-g8ed1b From 052553af6a31b459adfdc6fd1eebced75de332fc Mon Sep 17 00:00:00 2001 From: Vivek Gautam Date: Thu, 12 Oct 2017 11:49:36 +0530 Subject: ufs/phy: qcom: Refactor to use phy_init call Refactor ufs_qcom_power_up_sequence() to get rid of ugly exported phy APIs and use the phy_init() and phy_power_on() to do the phy initialization. Signed-off-by: Vivek Gautam Reviewed-by: Subhash Jadavani Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/qualcomm/phy-qcom-ufs-i.h | 3 +- drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c | 15 ++++++++-- drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c | 15 ++++++++-- drivers/phy/qualcomm/phy-qcom-ufs.c | 42 ++++++++++------------------ drivers/scsi/ufs/ufs-qcom.c | 36 ++++++++++-------------- include/linux/phy/phy-qcom-ufs.h | 3 -- 6 files changed, 55 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h index 94326ed107c3..822c83b8efcd 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h +++ b/drivers/phy/qualcomm/phy-qcom-ufs-i.h @@ -114,6 +114,7 @@ struct ufs_qcom_phy { struct ufs_qcom_phy_calibration *cached_regs; int cached_regs_table_size; bool is_powered_on; + bool is_started; struct ufs_qcom_phy_specific_ops *phy_spec_ops; enum phy_mode mode; @@ -123,7 +124,6 @@ struct ufs_qcom_phy { * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a * specific implementation per phy. Each UFS phy, should implement * those functions according to its spec and requirements - * @calibrate_phy: pointer to a function that calibrate the phy * @start_serdes: pointer to a function that starts the serdes * @is_physical_coding_sublayer_ready: pointer to a function that * checks pcs readiness. returns 0 for success and non-zero for error. @@ -132,7 +132,6 @@ struct ufs_qcom_phy { * and writes to QSERDES_RX_SIGDET_CNTRL attribute */ struct ufs_qcom_phy_specific_ops { - int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B); void (*start_serdes)(struct ufs_qcom_phy *phy); int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy); void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val); diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c index af65785230b5..ba1895b76a5d 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c @@ -44,7 +44,19 @@ void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common) static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy) { - return 0; + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + bool is_rate_B = false; + int ret; + + if (phy_common->mode == PHY_MODE_UFS_HS_B) + is_rate_B = true; + + ret = ufs_qcom_phy_qmp_14nm_phy_calibrate(phy_common, is_rate_B); + if (!ret) + /* phy calibrated, but yet to be started */ + phy_common->is_started = false; + + return ret; } static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy) @@ -120,7 +132,6 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = { }; static struct ufs_qcom_phy_specific_ops phy_14nm_ops = { - .calibrate_phy = ufs_qcom_phy_qmp_14nm_phy_calibrate, .start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready, .set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c index 5c18c41dbdb4..49f435c71147 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c @@ -63,7 +63,19 @@ void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common) static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy) { - return 0; + struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy); + bool is_rate_B = false; + int ret; + + if (phy_common->mode == PHY_MODE_UFS_HS_B) + is_rate_B = true; + + ret = ufs_qcom_phy_qmp_20nm_phy_calibrate(phy_common, is_rate_B); + if (!ret) + /* phy calibrated, but yet to be started */ + phy_common->is_started = false; + + return ret; } static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy) @@ -178,7 +190,6 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = { }; static struct ufs_qcom_phy_specific_ops phy_20nm_ops = { - .calibrate_phy = ufs_qcom_phy_qmp_20nm_phy_calibrate, .start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes, .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready, .set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable, diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c index 43865ef340e2..c5ff4525edef 100644 --- a/drivers/phy/qualcomm/phy-qcom-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-ufs.c @@ -518,9 +518,8 @@ void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy) } } -int ufs_qcom_phy_start_serdes(struct phy *generic_phy) +static int ufs_qcom_phy_start_serdes(struct ufs_qcom_phy *ufs_qcom_phy) { - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); int ret = 0; if (!ufs_qcom_phy->phy_spec_ops->start_serdes) { @@ -533,7 +532,6 @@ int ufs_qcom_phy_start_serdes(struct phy *generic_phy) return ret; } -EXPORT_SYMBOL_GPL(ufs_qcom_phy_start_serdes); int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes) { @@ -564,31 +562,8 @@ void ufs_qcom_phy_save_controller_version(struct phy *generic_phy, } EXPORT_SYMBOL_GPL(ufs_qcom_phy_save_controller_version); -int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B) -{ - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - int ret = 0; - - if (!ufs_qcom_phy->phy_spec_ops->calibrate_phy) { - dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() callback is not supported\n", - __func__); - ret = -ENOTSUPP; - } else { - ret = ufs_qcom_phy->phy_spec_ops-> - calibrate_phy(ufs_qcom_phy, is_rate_B); - if (ret) - dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n", - __func__, ret); - } - - return ret; -} -EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate_phy); - -int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) +static int ufs_qcom_phy_is_pcs_ready(struct ufs_qcom_phy *ufs_qcom_phy) { - struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy); - if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) { dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n", __func__); @@ -598,7 +573,6 @@ int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy) return ufs_qcom_phy->phy_spec_ops-> is_physical_coding_sublayer_ready(ufs_qcom_phy); } -EXPORT_SYMBOL_GPL(ufs_qcom_phy_is_pcs_ready); int ufs_qcom_phy_power_on(struct phy *generic_phy) { @@ -609,6 +583,18 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy) if (phy_common->is_powered_on) return 0; + if (!phy_common->is_started) { + err = ufs_qcom_phy_start_serdes(phy_common); + if (err) + return err; + + err = ufs_qcom_phy_is_pcs_ready(phy_common); + if (err) + return err; + + phy_common->is_started = true; + } + err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy); if (err) { dev_err(dev, "%s enable vdda_phy failed, err=%d\n", diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 44c21d5818ee..890eafeb8ad4 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -281,10 +281,10 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) /* provide 1ms delay to let the reset pulse propagate */ usleep_range(1000, 1100); - ret = ufs_qcom_phy_calibrate_phy(phy, is_rate_B); - + /* phy initialization - calibrate the phy */ + ret = phy_init(phy); if (ret) { - dev_err(hba->dev, "%s: ufs_qcom_phy_calibrate_phy() failed, ret = %d\n", + dev_err(hba->dev, "%s: phy init failed, ret = %d\n", __func__, ret); goto out; } @@ -297,21 +297,22 @@ static int ufs_qcom_power_up_sequence(struct ufs_hba *hba) * voltage, current to settle down before starting serdes. */ usleep_range(1000, 1100); - ret = ufs_qcom_phy_start_serdes(phy); + + /* power on phy - start serdes and phy's power and clocks */ + ret = phy_power_on(phy); if (ret) { - dev_err(hba->dev, "%s: ufs_qcom_phy_start_serdes() failed, ret = %d\n", + dev_err(hba->dev, "%s: phy power on failed, ret = %d\n", __func__, ret); - goto out; + goto out_disable_phy; } - ret = ufs_qcom_phy_is_pcs_ready(phy); - if (ret) - dev_err(hba->dev, - "%s: is_physical_coding_sublayer_ready() failed, ret = %d\n", - __func__, ret); - ufs_qcom_select_unipro_mode(host); + return 0; + +out_disable_phy: + ufs_qcom_assert_reset(hba); + phy_exit(phy); out: return ret; } @@ -1276,14 +1277,9 @@ static int ufs_qcom_init(struct ufs_hba *hba) ufs_qcom_phy_save_controller_version(host->generic_phy, host->hw_ver.major, host->hw_ver.minor, host->hw_ver.step); - phy_init(host->generic_phy); - err = phy_power_on(host->generic_phy); - if (err) - goto out_unregister_bus; - err = ufs_qcom_init_lane_clks(host); if (err) - goto out_disable_phy; + goto out_variant_clear; ufs_qcom_set_caps(hba); ufs_qcom_advertise_quirks(hba); @@ -1304,10 +1300,6 @@ static int ufs_qcom_init(struct ufs_hba *hba) goto out; -out_disable_phy: - phy_power_off(host->generic_phy); -out_unregister_bus: - phy_exit(host->generic_phy); out_variant_clear: ufshcd_set_variant(hba, NULL); out: diff --git a/include/linux/phy/phy-qcom-ufs.h b/include/linux/phy/phy-qcom-ufs.h index 35c070ea6ea3..0a2c18a9771d 100644 --- a/include/linux/phy/phy-qcom-ufs.h +++ b/include/linux/phy/phy-qcom-ufs.h @@ -31,10 +31,7 @@ void ufs_qcom_phy_enable_dev_ref_clk(struct phy *phy); */ void ufs_qcom_phy_disable_dev_ref_clk(struct phy *phy); -int ufs_qcom_phy_start_serdes(struct phy *phy); int ufs_qcom_phy_set_tx_lane_enable(struct phy *phy, u32 tx_lanes); -int ufs_qcom_phy_calibrate_phy(struct phy *phy, bool is_rate_B); -int ufs_qcom_phy_is_pcs_ready(struct phy *phy); void ufs_qcom_phy_save_controller_version(struct phy *phy, u8 major, u16 minor, u16 step); -- cgit v1.2.3-59-g8ed1b From 36914111e6829be36b23d1109214250b5ee1ee9c Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Mon, 9 Oct 2017 14:00:50 +0200 Subject: drivers: phy: add calibrate method Some quirky UDCs (like dwc3 on Exynos) need to have their phys calibrated e.g. for using super speed. This patch adds a new phy_calibrate() method. When the calibration should be used is dependent on actual chip. In case of dwc3 on Exynos the calibration must happen after usb_add_hcd() (while in host mode), because certain phy parameters like Tx LOS levels and boost levels need to be calibrated further post initialization of xHCI controller, to get SuperSpeed operations working. But an hcd must be prepared first in order to pass it to usb_add_hcd(), so, in particular, dwc3 registers must be available first, and in order for the latter to happen the phys must be initialized. This poses a chicken and egg problem if the calibration were to be performed in phy_init(). To break the circular dependency a separate method is added which can be called at a desired moment after phy intialization. Signed-off-by: Andrzej Pietrasiewicz Signed-off-by: Kishon Vijay Abraham I --- drivers/phy/phy-core.c | 15 +++++++++++++++ include/linux/phy/phy.h | 10 ++++++++++ 2 files changed, 25 insertions(+) (limited to 'include') diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index a268f4d6f3e9..b4964b067aec 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -372,6 +372,21 @@ int phy_reset(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_reset); +int phy_calibrate(struct phy *phy) +{ + int ret; + + if (!phy || !phy->ops->calibrate) + return 0; + + mutex_lock(&phy->mutex); + ret = phy->ops->calibrate(phy); + mutex_unlock(&phy->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(phy_calibrate); + /** * _of_phy_get() - lookup and obtain a reference to a phy by phandle * @np: device_node for which to get the phy diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 194d08174516..4f8423a948d5 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -41,6 +41,7 @@ enum phy_mode { * @power_off: powering off the phy * @set_mode: set the mode of the phy * @reset: resetting the phy + * @calibrate: calibrate the phy * @owner: the module owner containing the ops */ struct phy_ops { @@ -50,6 +51,7 @@ struct phy_ops { int (*power_off)(struct phy *phy); int (*set_mode)(struct phy *phy, enum phy_mode mode); int (*reset)(struct phy *phy); + int (*calibrate)(struct phy *phy); struct module *owner; }; @@ -143,6 +145,7 @@ int phy_power_on(struct phy *phy); int phy_power_off(struct phy *phy); int phy_set_mode(struct phy *phy, enum phy_mode mode); int phy_reset(struct phy *phy); +int phy_calibrate(struct phy *phy); static inline int phy_get_bus_width(struct phy *phy) { return phy->attrs.bus_width; @@ -264,6 +267,13 @@ static inline int phy_reset(struct phy *phy) return -ENOSYS; } +static inline int phy_calibrate(struct phy *phy) +{ + if (!phy) + return 0; + return -ENOSYS; +} + static inline int phy_get_bus_width(struct phy *phy) { return -ENOSYS; -- cgit v1.2.3-59-g8ed1b From 1c59d045058750904926d5883ce8acdedbc52df3 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 12 Oct 2017 20:36:15 +0900 Subject: dt-bindings: gpio: uniphier: add UniPhier GPIO binding This GPIO controller is used on UniPhier SoC family. The vendor specific property "socionext,interrupt-ranges" is for specifying interrupt mapping to the parent interrupt controller because the mapping is not contiguous. It works like "ranges", but transforms "interrupts" instead of "reg". Signed-off-by: Masahiro Yamada Acked-by: Rob Herring Signed-off-by: Linus Walleij --- .../devicetree/bindings/gpio/gpio-uniphier.txt | 52 ++++++++++++++++++++++ MAINTAINERS | 1 + include/dt-bindings/gpio/uniphier-gpio.h | 18 ++++++++ 3 files changed, 71 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-uniphier.txt create mode 100644 include/dt-bindings/gpio/uniphier-gpio.h (limited to 'include') diff --git a/Documentation/devicetree/bindings/gpio/gpio-uniphier.txt b/Documentation/devicetree/bindings/gpio/gpio-uniphier.txt new file mode 100644 index 000000000000..fed9158dd913 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-uniphier.txt @@ -0,0 +1,52 @@ +UniPhier GPIO controller + +Required properties: +- compatible: Should be "socionext,uniphier-gpio". +- reg: Specifies offset and length of the register set for the device. +- gpio-controller: Marks the device node as a GPIO controller. +- #gpio-cells: Should be 2. The first cell is the pin number and the second + cell is used to specify optional parameters. +- interrupt-parent: Specifies the parent interrupt controller. +- interrupt-controller: Marks the device node as an interrupt controller. +- #interrupt-cells: Should be 2. The first cell defines the interrupt number. + The second cell bits[3:0] is used to specify trigger type as follows: + 1 = low-to-high edge triggered + 2 = high-to-low edge triggered + 4 = active high level-sensitive + 8 = active low level-sensitive + Valid combinations are 1, 2, 3, 4, 8. +- ngpios: Specifies the number of GPIO lines. +- gpio-ranges: Mapping to pin controller pins (as described in gpio.txt) +- socionext,interrupt-ranges: Specifies an interrupt number mapping between + this GPIO controller and its interrupt parent, in the form of arbitrary + number of triplets. + +Optional properties: +- gpio-ranges-group-names: Used for named gpio ranges (as described in gpio.txt) + +Example: + gpio: gpio@55000000 { + compatible = "socionext,uniphier-gpio"; + reg = <0x55000000 0x200>; + interrupt-parent = <&aidet>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pinctrl 0 0 0>; + gpio-ranges-group-names = "gpio_range"; + ngpios = <248>; + socionext,interrupt-ranges = <0 48 16>, <16 154 5>, <21 217 3>; + }; + +Consumer Example: + + sdhci0_pwrseq { + compatible = "mmc-pwrseq-emmc"; + reset-gpios = <&gpio UNIPHIER_GPIO_PORT(29, 4) GPIO_ACTIVE_LOW>; + }; + +Please note UNIPHIER_GPIO_PORT(29, 4) represents PORT294 in the SoC document. +Unfortunately, only the one's place is octal in the port numbering. (That is, +PORT 8, 9, 18, 19, 28, 29, ... are missing.) UNIPHIER_GPIO_PORT() is a helper +macro to calculate 29 * 8 + 4. diff --git a/MAINTAINERS b/MAINTAINERS index 00c8be748ed2..193b86116cd1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2017,6 +2017,7 @@ M: Masahiro Yamada L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) T: git git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-uniphier.git S: Maintained +F: Documentation/devicetree/bindings/gpio/gpio-uniphier.txt F: arch/arm/boot/dts/uniphier* F: arch/arm/include/asm/hardware/cache-uniphier.h F: arch/arm/mach-uniphier/ diff --git a/include/dt-bindings/gpio/uniphier-gpio.h b/include/dt-bindings/gpio/uniphier-gpio.h new file mode 100644 index 000000000000..9f0ad174f61c --- /dev/null +++ b/include/dt-bindings/gpio/uniphier-gpio.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2017 Socionext Inc. + * Author: Masahiro Yamada + */ + +#ifndef _DT_BINDINGS_GPIO_UNIPHIER_H +#define _DT_BINDINGS_GPIO_UNIPHIER_H + +#define UNIPHIER_GPIO_LINES_PER_BANK 8 + +#define UNIPHIER_GPIO_IRQ_OFFSET ((UNIPHIER_GPIO_LINES_PER_BANK) * 15) + +#define UNIPHIER_GPIO_PORT(bank, line) \ + ((UNIPHIER_GPIO_LINES_PER_BANK) * (bank) + (line)) + +#define UNIPHIER_GPIO_IRQ(n) ((UNIPHIER_GPIO_IRQ_OFFSET) + (n)) + +#endif /* _DT_BINDINGS_GPIO_UNIPHIER_H */ -- cgit v1.2.3-59-g8ed1b From b8135864d4d33d0c35516ec758b9b73226935c5d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 11 Oct 2017 01:37:23 +0000 Subject: ASoC: snd_soc_component_driver has snd_pcm_ops Platform will be replaced into Component in the future. snd_soc_platform_driver has snd_pcm_ops, but snd_soc_component_driver doesn't have it. To prepare for replacing, this patch adds snd_pcm_ops on component driver. platform will be replaced into component, and its code will be removed. But during replacing, both platform and component process code exists. To keep compatibility, to avoid platform NULL access and to avoid platform/component duplicate operation during replacing process, this patch has such code. Some of this code will be removed when platform was removed. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 2 + sound/soc/soc-pcm.c | 367 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 355 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index eea3007a28f1..2acb56673bab 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -826,6 +826,8 @@ struct snd_soc_component_driver { int (*set_bias_level)(struct snd_soc_component *component, enum snd_soc_bias_level level); + const struct snd_pcm_ops *ops; + /* probe ordering - for components with runtime dependencies */ int probe_order; int remove_order; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index e5eb0cff790b..daaa670ee9b7 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -459,7 +459,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; const char *codec_dai_name = "multicodec"; - int i, ret = 0; + int i, ret = 0, __ret; pinctrl_pm_select_default_state(cpu_dai->dev); for (i = 0; i < rtd->num_codecs; i++) @@ -483,7 +483,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } - if (platform->driver->ops && platform->driver->ops->open) { + if (platform && platform->driver->ops && platform->driver->ops->open) { ret = platform->driver->ops->open(substream); if (ret < 0) { dev_err(platform->dev, "ASoC: can't open platform" @@ -492,6 +492,29 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) } } + ret = 0; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->open) + continue; + + __ret = component->driver->ops->open(substream); + if (__ret < 0) { + dev_err(component->dev, + "ASoC: can't open component %s: %d\n", + component->name, ret); + ret = __ret; + } + } + if (ret < 0) + goto component_err; + for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; if (codec_dai->driver->ops->startup) { @@ -598,7 +621,22 @@ codec_dai_err: codec_dai->driver->ops->shutdown(substream, codec_dai); } - if (platform->driver->ops && platform->driver->ops->close) +component_err: + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->close) + continue; + + component->driver->ops->close(substream); + } + + if (platform && platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); platform_err: @@ -695,9 +733,23 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) if (rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); - if (platform->driver->ops && platform->driver->ops->close) + if (platform && platform->driver->ops && platform->driver->ops->close) platform->driver->ops->close(substream); + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->close) + continue; + + component->driver->ops->close(substream); + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (snd_soc_runtime_ignore_pmdown_time(rtd)) { /* powered down playback stream now */ @@ -745,6 +797,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; int i, ret = 0; @@ -760,7 +814,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } - if (platform->driver->ops && platform->driver->ops->prepare) { + if (platform && platform->driver->ops && platform->driver->ops->prepare) { ret = platform->driver->ops->prepare(substream); if (ret < 0) { dev_err(platform->dev, "ASoC: platform prepare error:" @@ -769,6 +823,25 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->prepare) + continue; + + ret = component->driver->ops->prepare(substream); + if (ret < 0) { + dev_err(component->dev, + "ASoC: platform prepare error: %d\n", ret); + goto out; + } + } + for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; if (codec_dai->driver->ops->prepare) { @@ -851,8 +924,10 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, ret = 0; + int i, ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); if (rtd->dai_link->ops->hw_params) { @@ -910,7 +985,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, if (ret < 0) goto interface_err; - if (platform->driver->ops && platform->driver->ops->hw_params) { + if (platform && platform->driver->ops && platform->driver->ops->hw_params) { ret = platform->driver->ops->hw_params(substream, params); if (ret < 0) { dev_err(platform->dev, "ASoC: %s hw params failed: %d\n", @@ -919,20 +994,60 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, } } + ret = 0; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_params) + continue; + + __ret = component->driver->ops->hw_params(substream, params); + if (__ret < 0) { + dev_err(component->dev, + "ASoC: %s hw params failed: %d\n", + component->name, ret); + ret = __ret; + } + } + if (ret < 0) + goto component_err; + /* store the parameters for each DAIs */ cpu_dai->rate = params_rate(params); cpu_dai->channels = params_channels(params); cpu_dai->sample_bits = snd_pcm_format_physical_width(params_format(params)); - ret = soc_pcm_params_symmetry(substream, params); if (ret) - goto platform_err; + goto component_err; out: mutex_unlock(&rtd->pcm_mutex); return ret; +component_err: + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_free) + continue; + + component->driver->ops->hw_free(substream); + } + + if (platform && platform->driver->ops && platform->driver->ops->hw_free) + platform->driver->ops->hw_free(substream); + platform_err: if (cpu_dai->driver->ops->hw_free) cpu_dai->driver->ops->hw_free(substream, cpu_dai); @@ -962,6 +1077,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; @@ -998,9 +1115,24 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) rtd->dai_link->ops->hw_free(substream); /* free any DMA resources */ - if (platform->driver->ops && platform->driver->ops->hw_free) + if (platform && platform->driver->ops && platform->driver->ops->hw_free) platform->driver->ops->hw_free(substream); + /* free any component resources */ + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->hw_free) + continue; + + component->driver->ops->hw_free(substream); + } + /* now free hw params for the DAIs */ for (i = 0; i < rtd->num_codecs; i++) { codec_dai = rtd->codec_dais[i]; @@ -1019,6 +1151,8 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; int i, ret; @@ -1033,12 +1167,28 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) } } - if (platform->driver->ops && platform->driver->ops->trigger) { + if (platform && platform->driver->ops && platform->driver->ops->trigger) { ret = platform->driver->ops->trigger(substream, cmd); if (ret < 0) return ret; } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->trigger) + continue; + + ret = component->driver->ops->trigger(substream, cmd); + if (ret < 0) + return ret; + } + if (cpu_dai->driver->ops->trigger) { ret = cpu_dai->driver->ops->trigger(substream, cmd, cpu_dai); if (ret < 0) @@ -1088,6 +1238,8 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai; struct snd_pcm_runtime *runtime = substream->runtime; @@ -1096,9 +1248,25 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream) snd_pcm_sframes_t codec_delay = 0; int i; - if (platform->driver->ops && platform->driver->ops->pointer) + if (platform && platform->driver->ops && platform->driver->ops->pointer) offset = platform->driver->ops->pointer(substream); + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->pointer) + continue; + + /* FIXME: use 1st pointer */ + offset = component->driver->ops->pointer(substream); + break; + } + if (cpu_dai->driver->ops->delay) delay += cpu_dai->driver->ops->delay(substream, cpu_dai); @@ -2283,9 +2451,27 @@ static int soc_pcm_ioctl(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; - if (platform->driver->ops && platform->driver->ops->ioctl) + if (platform && platform->driver->ops && platform->driver->ops->ioctl) return platform->driver->ops->ioctl(substream, cmd, arg); + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->ops || + !component->driver->ops->ioctl) + continue; + + /* FIXME: use 1st ioctl */ + return component->driver->ops->ioctl(substream, cmd, arg); + } + return snd_pcm_lib_ioctl(substream, cmd, arg); } @@ -2647,6 +2833,138 @@ static void soc_pcm_private_free(struct snd_pcm *pcm) } } +static int soc_rtdcom_ack(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->ack) + continue; + + /* FIXME. it returns 1st ask now */ + return component->driver->ops->ack(substream); + } + + return -EINVAL; +} + +static int soc_rtdcom_copy_user(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void __user *buf, + unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->copy_user) + continue; + + /* FIXME. it returns 1st copy now */ + return component->driver->ops->copy_user(substream, channel, + pos, buf, bytes); + } + + return -EINVAL; +} + +static int soc_rtdcom_copy_kernel(struct snd_pcm_substream *substream, int channel, + unsigned long pos, void *buf, unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->copy_kernel) + continue; + + /* FIXME. it returns 1st copy now */ + return component->driver->ops->copy_kernel(substream, channel, + pos, buf, bytes); + } + + return -EINVAL; +} + +static int soc_rtdcom_fill_silence(struct snd_pcm_substream *substream, int channel, + unsigned long pos, unsigned long bytes) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->fill_silence) + continue; + + /* FIXME. it returns 1st silence now */ + return component->driver->ops->fill_silence(substream, channel, + pos, bytes); + } + + return -EINVAL; +} + +static struct page *soc_rtdcom_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + struct page *page; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->page) + continue; + + /* FIXME. it returns 1st page now */ + page = component->driver->ops->page(substream, offset); + if (page) + return page; + } + + return NULL; +} + +static int soc_rtdcom_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + if (!component->driver->ops || + !component->driver->ops->mmap) + continue; + + /* FIXME. it returns 1st mmap now */ + return component->driver->ops->mmap(substream, vma); + } + + return -EINVAL; +} + /* create a new pcm */ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) { @@ -2749,7 +3067,28 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) rtd->ops.ioctl = soc_pcm_ioctl; } - if (platform->driver->ops) { + for_each_rtdcom(rtd, rtdcom) { + const struct snd_pcm_ops *ops = rtdcom->component->driver->ops; + + if (!ops) + continue; + + if (ops->ack) + rtd->ops.ack = soc_rtdcom_ack; + if (ops->copy_user) + rtd->ops.copy_user = soc_rtdcom_copy_user; + if (ops->copy_kernel) + rtd->ops.copy_kernel = soc_rtdcom_copy_kernel; + if (ops->fill_silence) + rtd->ops.fill_silence = soc_rtdcom_fill_silence; + if (ops->page) + rtd->ops.page = soc_rtdcom_page; + if (ops->mmap) + rtd->ops.mmap = soc_rtdcom_mmap; + } + + /* overwrite */ + if (platform && platform->driver->ops) { rtd->ops.ack = platform->driver->ops->ack; rtd->ops.copy_user = platform->driver->ops->copy_user; rtd->ops.copy_kernel = platform->driver->ops->copy_kernel; -- cgit v1.2.3-59-g8ed1b From 9e7e3738ab0e908c72dae4ff45481a0568924e62 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 11 Oct 2017 01:37:45 +0000 Subject: ASoC: snd_soc_component_driver has snd_compr_ops Platform will be replaced into Component in the future. snd_soc_platform_driver has snd_compr_ops, but snd_soc_component_driver doesn't have. To prepare for replacing, this patch adds snd_compr_ops on component driver. platform will be replaced into component, and its code will be removed. But during replacing, both platform and component process code exists. To keep compatibility, to avoid platform NULL access and to avoid platform/component duplicate operation during replacing process, this patch has such code. Some of this code will be removed when platform was removed. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-compress.c | 461 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 428 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 2acb56673bab..ae0a27fdc016 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -827,6 +827,7 @@ struct snd_soc_component_driver { enum snd_soc_bias_level level); const struct snd_pcm_ops *ops; + const struct snd_compr_ops *compr_ops; /* probe ordering - for components with runtime dependencies */ int probe_order; diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 2cb8d3b55fbc..d9b1e6417fb9 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -30,8 +30,10 @@ static int soc_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -44,7 +46,7 @@ static int soc_compr_open(struct snd_compr_stream *cstream) } } - if (platform->driver->compr_ops && platform->driver->compr_ops->open) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { pr_err("compress asoc: can't open platform %s\n", @@ -53,6 +55,27 @@ static int soc_compr_open(struct snd_compr_stream *cstream) } } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->open) + continue; + + __ret = component->driver->compr_ops->open(cstream); + if (__ret < 0) { + pr_err("compress asoc: can't open platform %s\n", + component->name); + ret = __ret; + } + } + if (ret < 0) + goto machine_err; + if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->startup) { ret = rtd->dai_link->compr_ops->startup(cstream); if (ret < 0) { @@ -68,7 +91,21 @@ static int soc_compr_open(struct snd_compr_stream *cstream) return 0; machine_err: - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); plat_err: if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) @@ -84,11 +121,13 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) struct snd_pcm_substream *fe_substream = fe->pcm->streams[cstream->direction].substream; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; struct snd_soc_dapm_widget_list *list; int stream; - int ret = 0; + int ret = 0, __ret; if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -107,7 +146,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) } - if (platform->driver->compr_ops && platform->driver->compr_ops->open) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { pr_err("compress asoc: can't open platform %s\n", @@ -116,6 +155,27 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream) } } + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->open) + continue; + + __ret = component->driver->compr_ops->open(cstream); + if (__ret < 0) { + pr_err("compress asoc: can't open platform %s\n", + component->name); + ret = __ret; + } + } + if (ret < 0) + goto machine_err; + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->startup) { ret = fe->dai_link->compr_ops->startup(cstream); if (ret < 0) { @@ -167,7 +227,21 @@ fe_err: if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) fe->dai_link->compr_ops->shutdown(cstream); machine_err: - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); plat_err: if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) @@ -210,6 +284,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai; int stream; @@ -235,7 +311,21 @@ static int soc_compr_free(struct snd_compr_stream *cstream) if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->shutdown) rtd->dai_link->compr_ops->shutdown(cstream); - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) @@ -267,6 +357,8 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; struct snd_soc_dpcm *dpcm; int stream, ret; @@ -304,9 +396,23 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream) if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown) fe->dai_link->compr_ops->shutdown(cstream); - if (platform->driver->compr_ops && platform->driver->compr_ops->free) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->free) + continue; + + component->driver->compr_ops->free(cstream); + } + if (cpu_dai->driver->cops && cpu_dai->driver->cops->shutdown) cpu_dai->driver->cops->shutdown(cstream, cpu_dai); @@ -319,18 +425,38 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) goto out; } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->trigger) + continue; + + __ret = component->driver->compr_ops->trigger(cstream, cmd); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto out; + if (cpu_dai->driver->cops && cpu_dai->driver->cops->trigger) cpu_dai->driver->cops->trigger(cstream, cmd, cpu_dai); @@ -353,16 +479,36 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) { struct snd_soc_pcm_runtime *fe = cstream->private_data; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; - int ret = 0, stream; + int ret = 0, __ret, stream; if (cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN || cmd == SND_COMPR_TRIGGER_DRAIN) { - if (platform->driver->compr_ops && + if (platform && + platform->driver->compr_ops && platform->driver->compr_ops->trigger) return platform->driver->compr_ops->trigger(cstream, cmd); + + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->trigger) + continue; + + __ret = component->driver->compr_ops->trigger(cstream, cmd); + if (__ret < 0) + ret = __ret; + } + return ret; } if (cstream->direction == SND_COMPRESS_PLAYBACK) @@ -379,12 +525,30 @@ static int soc_compr_trigger_fe(struct snd_compr_stream *cstream, int cmd) goto out; } - if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) goto out; } + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->trigger) + continue; + + __ret = component->driver->compr_ops->trigger(cstream, cmd); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto out; + fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE; ret = dpcm_be_dai_trigger(fe, stream, cmd); @@ -415,8 +579,10 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -432,12 +598,30 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, goto err; } - if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) goto err; } + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->set_params) + continue; + + __ret = component->driver->compr_ops->set_params(cstream, params); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto err; + if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { ret = rtd->dai_link->compr_ops->set_params(cstream); if (ret < 0) @@ -471,8 +655,10 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, struct snd_pcm_substream *fe_substream = fe->pcm->streams[cstream->direction].substream; struct snd_soc_platform *platform = fe->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = fe->cpu_dai; - int ret = 0, stream; + int ret = 0, __ret, stream; if (cstream->direction == SND_COMPRESS_PLAYBACK) stream = SNDRV_PCM_STREAM_PLAYBACK; @@ -487,12 +673,30 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream, goto out; } - if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) goto out; } + for_each_rtdcom(fe, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->set_params) + continue; + + __ret = component->driver->compr_ops->set_params(cstream, params); + if (__ret < 0) + ret = __ret; + } + if (ret < 0) + goto out; + if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->set_params) { ret = fe->dai_link->compr_ops->set_params(cstream); if (ret < 0) @@ -531,8 +735,10 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -542,8 +748,27 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, goto err; } - if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_params) { ret = platform->driver->compr_ops->get_params(cstream, params); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_params) + continue; + + __ret = component->driver->compr_ops->get_params(cstream, params); + if (__ret < 0) + ret = __ret; + } err: mutex_unlock(&rtd->pcm_mutex); @@ -555,13 +780,35 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_caps) { ret = platform->driver->compr_ops->get_caps(cstream, caps); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_caps) + continue; + + __ret = component->driver->compr_ops->get_caps(cstream, caps); + if (__ret < 0) + ret = __ret; + } + +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -571,13 +818,35 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) { ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_codec_caps) + continue; + + __ret = component->driver->compr_ops->get_codec_caps(cstream, codec); + if (__ret < 0) + ret = __ret; + } + +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -586,8 +855,10 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -597,8 +868,27 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) goto err; } - if (platform->driver->compr_ops && platform->driver->compr_ops->ack) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->ack) { ret = platform->driver->compr_ops->ack(cstream, bytes); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->ack) + continue; + + __ret = component->driver->compr_ops->ack(cstream, bytes); + if (__ret < 0) + ret = __ret; + } err: mutex_unlock(&rtd->pcm_mutex); @@ -610,7 +900,9 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); @@ -618,9 +910,29 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, if (cpu_dai->driver->cops && cpu_dai->driver->cops->pointer) cpu_dai->driver->cops->pointer(cstream, tstamp, cpu_dai); - if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->pointer) { ret = platform->driver->compr_ops->pointer(cstream, tstamp); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->pointer) + continue; + __ret = component->driver->compr_ops->pointer(cstream, tstamp); + if (__ret < 0) + ret = __ret; + } + +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -630,13 +942,34 @@ static int soc_compr_copy(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; - int ret = 0; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; + int ret = 0, __ret; mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); - if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->copy) { ret = platform->driver->compr_ops->copy(cstream, buf, count); + if (ret < 0) + goto err; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->copy) + continue; + __ret = component->driver->compr_ops->copy(cstream, buf, count); + if (__ret < 0) + ret = __ret; + } +err: mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -646,8 +979,10 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; if (cpu_dai->driver->cops && cpu_dai->driver->cops->set_metadata) { ret = cpu_dai->driver->cops->set_metadata(cstream, metadata, cpu_dai); @@ -655,8 +990,27 @@ static int soc_compr_set_metadata(struct snd_compr_stream *cstream, return ret; } - if (platform->driver->compr_ops && platform->driver->compr_ops->set_metadata) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->set_metadata) { ret = platform->driver->compr_ops->set_metadata(cstream, metadata); + if (ret < 0) + return ret; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->set_metadata) + continue; + + __ret = component->driver->compr_ops->set_metadata(cstream, metadata); + if (__ret < 0) + ret = __ret; + } return ret; } @@ -666,8 +1020,10 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, { struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, __ret; if (cpu_dai->driver->cops && cpu_dai->driver->cops->get_metadata) { ret = cpu_dai->driver->cops->get_metadata(cstream, metadata, cpu_dai); @@ -675,8 +1031,27 @@ static int soc_compr_get_metadata(struct snd_compr_stream *cstream, return ret; } - if (platform->driver->compr_ops && platform->driver->compr_ops->get_metadata) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->get_metadata) { ret = platform->driver->compr_ops->get_metadata(cstream, metadata); + if (ret < 0) + return ret; + } + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->get_metadata) + continue; + + __ret = component->driver->compr_ops->get_metadata(cstream, metadata); + if (__ret < 0) + ret = __ret; + } return ret; } @@ -723,6 +1098,8 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_platform *platform = rtd->platform; + struct snd_soc_component *component; + struct snd_soc_rtdcom_list *rtdcom; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; @@ -798,9 +1175,25 @@ int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); } + /* Add copy callback for not memory mapped DSPs */ - if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + if (platform && platform->driver->compr_ops && platform->driver->compr_ops->copy) + compr->ops->copy = soc_compr_copy; + + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + /* ignore duplication for now */ + if (platform && (component == &platform->component)) + continue; + + if (!component->driver->compr_ops || + !component->driver->compr_ops->copy) + continue; + compr->ops->copy = soc_compr_copy; + } + mutex_init(&compr->lock); ret = snd_compress_new(rtd->card->snd_card, num, direction, -- cgit v1.2.3-59-g8ed1b From fbb16563c6c2b7fc4944adc49f93c1dc6fe25770 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 11 Oct 2017 01:38:08 +0000 Subject: ASoC: snd_soc_component_driver has pmdown_time Current snd_soc_runtime_ignore_pmdown_time() tallys all Codec and CPU's "ignore_pmdown_time". Now, CPU (= via compoent) ignore_pmdown_time is fixed as "true". Codec's one is copied from Codec driver. This means Codec side default is "false". Current all Codec driver will be replaced into Component, thus, we can use for_each_rtdcom() for this totalization. This patch adds new "pmdown_time" on Component driver. Its inverted value will be used for this "ignore" totalizaton. Of course all existing Component driver doesn't have its settings now, thus, all existing "pmdown_time" is "false". This means all Components will ignore pmdown time. This is current CPU behavior. To keep compatibility, snd_soc_runtime_ignore_pmdown_time() totalize Component's inverted "pmdown_time" (= total will be true) and Codec's "ignore_pmdown_time" (= depends on Codec driver settings). Because It is using AND operation, its result is based on Codec driver settings only. This means this operation can keep compatibility and doesn't have nonconformity. When we replace Codec to Component, the driver which has ".ignore_pmdown_time = true" will be just removed, and the driver which doesn't have it will have new ".pmdown_time = true". Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-pcm.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index ae0a27fdc016..44fab951b686 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -836,6 +836,7 @@ struct snd_soc_component_driver { /* bits */ unsigned int idle_bias_on:1; unsigned int suspend_bias_off:1; + unsigned int pmdown_time:1; /* care pmdown_time at stop */ }; struct snd_soc_component { diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index daaa670ee9b7..8075856668c2 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -133,16 +133,25 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream) */ bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd) { + struct snd_soc_rtdcom_list *rtdcom; + struct snd_soc_component *component; int i; bool ignore = true; if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time) return true; + for_each_rtdcom(rtd, rtdcom) { + component = rtdcom->component; + + ignore &= !component->driver->pmdown_time; + } + + /* this will be removed */ for (i = 0; i < rtd->num_codecs; i++) ignore &= rtd->codec_dais[i]->component->ignore_pmdown_time; - return rtd->cpu_dai->component->ignore_pmdown_time && ignore; + return ignore; } /** -- cgit v1.2.3-59-g8ed1b From 8dd8d2c95d0252bc64aa8e061a5fb4fbcd05f826 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Wed, 18 Oct 2017 15:15:01 +0800 Subject: USB: Force disconnect Huawei 4G modem during suspend When going into S3 suspend, the Acer TravelMate P648-M and P648-G3 laptops immediately wake up 3-4 seconds later for no obvious reason. Unbinding the integrated Huawei 4G LTE modem before suspend avoids the issue, even though we are not using the modem at all (checked from rescue.target/runlevel1). The problem also occurs when the option and cdc-ether modem drivers aren't loaded; it reproduces just with the base usb driver. Under Windows the system can suspend fine. Seeking a better fix, we've tried a lot of things, including: - Check that the device's power/wakeup is disabled - Check that remote wakeup is off at the USB level - All the quirks in drivers/usb/core/quirks.c e.g. USB_QUIRK_RESET_RESUME, USB_QUIRK_RESET, USB_QUIRK_IGNORE_REMOTE_WAKEUP, USB_QUIRK_NO_LPM. but none of that makes any difference. There are no errors in the logs showing any suspend/resume-related issues. When the system wakes up due to the modem, log-wise it appears to be a normal resume. Introduce a quirk to disable the port during suspend when the modem is detected. The modem from the P648-G3 model is: T: Bus=01 Lev=01 Prnt=01 Port=08 Cnt=04 Dev#= 5 Spd=480 MxCh= 0 D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=ff MxPS=64 #Cfgs= 3 P: Vendor=12d1 ProdID=15c3 Rev= 1.02 S: Manufacturer=Huawei Technologies Co., Ltd. S: Product=HUAWEI Mobile S: SerialNumber=0123456789ABCDEF C: #Ifs= 5 Cfg#= 1 Atr=a0 MxPwr= 2mA I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=06 Prot=10 Driver= E: Ad=82(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=13 Driver= E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=12 Driver= E: Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 3 Alt= 0 #EPs= 1 Cls=ff(vend.) Sub=06 Prot=16 Driver= E: Ad=86(I) Atr=03(Int.) MxPS= 16 Ivl=2ms I: If#= 3 Alt= 1 #EPs= 3 Cls=ff(vend.) Sub=06 Prot=16 Driver= E: Ad=86(I) Atr=03(Int.) MxPS= 16 Ivl=2ms E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I: If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=1b Driver= E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms C:* #Ifs= 6 Cfg#= 2 Atr=a0 MxPwr= 2mA I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=06 Prot=00 Driver=cdc_ether E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=2ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=06 Prot=00 Driver=cdc_ether E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 2 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=06 Prot=10 Driver=option E: Ad=84(I) Atr=03(Int.) MxPS= 10 Ivl=32ms E: Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=13 Driver=option E: Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=12 Driver=option E: Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=1b Driver=option E: Ad=87(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms C: #Ifs= 2 Cfg#= 3 Atr=a0 MxPwr= 2mA A: FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0e Prot=00 I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0e Prot=00 Driver= E: Ad=82(I) Atr=03(Int.) MxPS= 16 Ivl=2ms I: If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=02 Driver= I: If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=02 Driver= E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms Based on an earlier patch by Chris Chiu. Signed-off-by: Daniel Drake Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 10 +++++++++- drivers/usb/core/hub.c | 13 +++++++++++++ drivers/usb/core/quirks.c | 6 ++++++ drivers/usb/core/usb.h | 1 + include/linux/usb/quirks.h | 6 ++++++ 5 files changed, 35 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index eb87a259d55c..353993f983c8 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1461,6 +1461,7 @@ static void choose_wakeup(struct usb_device *udev, pm_message_t msg) int usb_suspend(struct device *dev, pm_message_t msg) { struct usb_device *udev = to_usb_device(dev); + int r; unbind_no_pm_drivers_interfaces(udev); @@ -1469,7 +1470,14 @@ int usb_suspend(struct device *dev, pm_message_t msg) * so we may still need to unbind and rebind upon resume */ choose_wakeup(udev, msg); - return usb_suspend_both(udev, msg); + r = usb_suspend_both(udev, msg); + if (r) + return r; + + if (udev->quirks & USB_QUIRK_DISCONNECT_SUSPEND) + usb_port_disable(udev); + + return 0; } /* The device lock is held by the PM core */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b5c733613823..941968f011df 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4180,6 +4180,19 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) return ret; } +/* + * usb_port_disable - disable a usb device's upstream port + * @udev: device to disable + * Context: @udev locked, must be able to sleep. + * + * Disables a USB device that isn't in active use. + */ +int usb_port_disable(struct usb_device *udev) +{ + struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent); + + return hub_port_disable(hub, udev->portnum, 0); +} /* USB 2.0 spec, 7.1.7.3 / fig 7-29: * diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 82806e311202..746d2b19109c 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -203,6 +203,12 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x10d6, 0x2200), .driver_info = USB_QUIRK_STRING_FETCH_255 }, + /* Huawei 4G LTE module */ + { USB_DEVICE(0x12d1, 0x15bb), .driver_info = + USB_QUIRK_DISCONNECT_SUSPEND }, + { USB_DEVICE(0x12d1, 0x15c3), .driver_info = + USB_QUIRK_DISCONNECT_SUSPEND }, + /* SKYMEDI USB_DRIVE */ { USB_DEVICE(0x1516, 0x8628), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index dc6949248823..f71890a2db4e 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -73,6 +73,7 @@ extern void usb_hub_cleanup(void); extern int usb_major_init(void); extern void usb_major_cleanup(void); extern int usb_device_supports_lpm(struct usb_device *udev); +extern int usb_port_disable(struct usb_device *udev); #ifdef CONFIG_PM diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h index de2a722fe3cf..bdc639cc80b4 100644 --- a/include/linux/usb/quirks.h +++ b/include/linux/usb/quirks.h @@ -56,4 +56,10 @@ */ #define USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL BIT(11) +/* + * Device needs to be disconnected before suspend to prevent spurious + * wakeup. + */ +#define USB_QUIRK_DISCONNECT_SUSPEND BIT(12) + #endif /* __LINUX_USB_QUIRKS_H */ -- cgit v1.2.3-59-g8ed1b From 273d778ef38a8861f880e9df5799029dc82bd55d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 11 Oct 2017 01:38:29 +0000 Subject: ASoC: snd_soc_component_driver has endianness Codec will be replaced into Component, then Codec side only needs to call fixup_codec_formats() at this point. This patch adds new endianness flag on Component driver and call convert_endianness_formats() (= was fixup_codec_format()) if endianness was true. When Codec is replaced into Component, Codec driver needs to have endianness = 1 flags. Existing CPU side of course doesn't have this flag, thus CPU doesn't call it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 80 +++++++++++++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 44fab951b686..747d080a0853 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -837,6 +837,7 @@ struct snd_soc_component_driver { unsigned int idle_bias_on:1; unsigned int suspend_bias_off:1; unsigned int pmdown_time:1; /* care pmdown_time at stop */ + unsigned int endianness:1; }; struct snd_soc_component { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a3dcf14befd8..61c3d3649914 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3404,6 +3404,41 @@ static void snd_soc_component_del_unlocked(struct snd_soc_component *component) list_del(&component->list); } +#define ENDIANNESS_MAP(name) \ + (SNDRV_PCM_FMTBIT_##name##LE | SNDRV_PCM_FMTBIT_##name##BE) +static u64 endianness_format_map[] = { + ENDIANNESS_MAP(S16_), + ENDIANNESS_MAP(U16_), + ENDIANNESS_MAP(S24_), + ENDIANNESS_MAP(U24_), + ENDIANNESS_MAP(S32_), + ENDIANNESS_MAP(U32_), + ENDIANNESS_MAP(S24_3), + ENDIANNESS_MAP(U24_3), + ENDIANNESS_MAP(S20_3), + ENDIANNESS_MAP(U20_3), + ENDIANNESS_MAP(S18_3), + ENDIANNESS_MAP(U18_3), + ENDIANNESS_MAP(FLOAT_), + ENDIANNESS_MAP(FLOAT64_), + ENDIANNESS_MAP(IEC958_SUBFRAME_), +}; + +/* + * Fix up the DAI formats for endianness: codecs don't actually see + * the endianness of the data but we're using the CPU format + * definitions which do need to include endianness so we ensure that + * codec DAIs always have both big and little endian variants set. + */ +static void convert_endianness_formats(struct snd_soc_pcm_stream *stream) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(endianness_format_map); i++) + if (stream->formats & endianness_format_map[i]) + stream->formats |= endianness_format_map[i]; +} + int snd_soc_add_component(struct device *dev, struct snd_soc_component *component, const struct snd_soc_component_driver *component_driver, @@ -3411,6 +3446,7 @@ int snd_soc_add_component(struct device *dev, int num_dai) { int ret; + int i; ret = snd_soc_component_initialize(component, component_driver, dev); if (ret) @@ -3419,6 +3455,13 @@ int snd_soc_add_component(struct device *dev, component->ignore_pmdown_time = true; component->registered_as_component = true; + if (component_driver->endianness) { + for (i = 0; i < num_dai; i++) { + convert_endianness_formats(&dai_drv[i].playback); + convert_endianness_formats(&dai_drv[i].capture); + } + } + ret = snd_soc_register_dais(component, dai_drv, num_dai, true); if (ret < 0) { dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); @@ -3675,39 +3718,6 @@ void snd_soc_unregister_platform(struct device *dev) } EXPORT_SYMBOL_GPL(snd_soc_unregister_platform); -static u64 codec_format_map[] = { - SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE, - SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE, - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE, - SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE, - SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE, - SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE, - SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3BE, - SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_U24_3BE, - SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE, - SNDRV_PCM_FMTBIT_U20_3LE | SNDRV_PCM_FMTBIT_U20_3BE, - SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE, - SNDRV_PCM_FMTBIT_U18_3LE | SNDRV_PCM_FMTBIT_U18_3BE, - SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE, - SNDRV_PCM_FMTBIT_FLOAT64_LE | SNDRV_PCM_FMTBIT_FLOAT64_BE, - SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE - | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE, -}; - -/* Fix up the DAI formats for endianness: codecs don't actually see - * the endianness of the data but we're using the CPU format - * definitions which do need to include endianness so we ensure that - * codec DAIs always have both big and little endian variants set. - */ -static void fixup_codec_formats(struct snd_soc_pcm_stream *stream) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(codec_format_map); i++) - if (stream->formats & codec_format_map[i]) - stream->formats |= codec_format_map[i]; -} - static int snd_soc_codec_drv_probe(struct snd_soc_component *component) { struct snd_soc_codec *codec = snd_soc_component_to_codec(component); @@ -3858,8 +3868,8 @@ int snd_soc_register_codec(struct device *dev, codec->component.regmap = codec_drv->get_regmap(dev); for (i = 0; i < num_dai; i++) { - fixup_codec_formats(&dai_drv[i].playback); - fixup_codec_formats(&dai_drv[i].capture); + convert_endianness_formats(&dai_drv[i].playback); + convert_endianness_formats(&dai_drv[i].capture); } ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false); -- cgit v1.2.3-59-g8ed1b From 69941bab7c7aeaa7bf7e84397e294c17f0b7c6df Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 11 Oct 2017 01:38:51 +0000 Subject: ASoC: snd_soc_component_driver has non_legacy_dai_naming Codec will be replaced into Component, then Codec side doesn't use legacy_dai_naming on snd_soc_register_dais(). This patch adds new non_legacy_dai_naming flag on Component driver and use converted its value for snd_soc_register_dais(). When Codec is replaced into Component, Codec driver needs to have non_legacy_dai_naming = 1 flags. Existing CPU side of course doesn't have this flag, thus CPU calls it as true. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 747d080a0853..6c808527a4f6 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -838,6 +838,7 @@ struct snd_soc_component_driver { unsigned int suspend_bias_off:1; unsigned int pmdown_time:1; /* care pmdown_time at stop */ unsigned int endianness:1; + unsigned int non_legacy_dai_naming:1; }; struct snd_soc_component { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 61c3d3649914..533c822ca6e6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3462,7 +3462,8 @@ int snd_soc_add_component(struct device *dev, } } - ret = snd_soc_register_dais(component, dai_drv, num_dai, true); + ret = snd_soc_register_dais(component, dai_drv, num_dai, + !component_driver->non_legacy_dai_naming); if (ret < 0) { dev_err(dev, "ASoC: Failed to register DAIs: %d\n", ret); goto err_cleanup; -- cgit v1.2.3-59-g8ed1b From 40b16b9be5773a314948656c96adf7bf7cfdbd0b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 21 Oct 2017 11:45:46 +0200 Subject: batman-adv: use inline kernel-doc for uapi constants The enums of constants for netlink tends to become rather large over time. Documenting them is easier when the kernel-doc is actually next to constant and not in a different block above the enum. Also inline kernel-doc allows multi-paragraph description. This could be required to better document the netlink command types and the expected return values. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- include/uapi/linux/batman_adv.h | 369 +++++++++++++++++++++++++++++++--------- 1 file changed, 290 insertions(+), 79 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/batman_adv.h b/include/uapi/linux/batman_adv.h index a83ddb7b63db..efd641c8a5d6 100644 --- a/include/uapi/linux/batman_adv.h +++ b/include/uapi/linux/batman_adv.h @@ -24,20 +24,6 @@ /** * enum batadv_tt_client_flags - TT client specific flags - * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table - * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and the new - * update telling its new real location has not been received/sent yet - * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi interface. - * This information is used by the "AP Isolation" feature - * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This - * information is used by the Extended Isolation feature - * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from the table - * @BATADV_TT_CLIENT_NEW: this client has been added to the local table but has - * not been announced yet - * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it is kept - * in the table for one more originator interval for consistency purposes - * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be part of - * the network but no nnode has already announced it * * Bits from 0 to 7 are called _remote flags_ because they are sent on the wire. * Bits from 8 to 15 are called _local flags_ because they are used for local @@ -48,160 +34,385 @@ * in the TT CRC computation. */ enum batadv_tt_client_flags { + /** + * @BATADV_TT_CLIENT_DEL: the client has to be deleted from the table + */ BATADV_TT_CLIENT_DEL = (1 << 0), + + /** + * @BATADV_TT_CLIENT_ROAM: the client roamed to/from another node and + * the new update telling its new real location has not been + * received/sent yet + */ BATADV_TT_CLIENT_ROAM = (1 << 1), + + /** + * @BATADV_TT_CLIENT_WIFI: this client is connected through a wifi + * interface. This information is used by the "AP Isolation" feature + */ BATADV_TT_CLIENT_WIFI = (1 << 4), + + /** + * @BATADV_TT_CLIENT_ISOLA: this client is considered "isolated". This + * information is used by the Extended Isolation feature + */ BATADV_TT_CLIENT_ISOLA = (1 << 5), + + /** + * @BATADV_TT_CLIENT_NOPURGE: this client should never be removed from + * the table + */ BATADV_TT_CLIENT_NOPURGE = (1 << 8), + + /** + * @BATADV_TT_CLIENT_NEW: this client has been added to the local table + * but has not been announced yet + */ BATADV_TT_CLIENT_NEW = (1 << 9), + + /** + * @BATADV_TT_CLIENT_PENDING: this client is marked for removal but it + * is kept in the table for one more originator interval for consistency + * purposes + */ BATADV_TT_CLIENT_PENDING = (1 << 10), + + /** + * @BATADV_TT_CLIENT_TEMP: this global client has been detected to be + * part of the network but no nnode has already announced it + */ BATADV_TT_CLIENT_TEMP = (1 << 11), }; /** * enum batadv_nl_attrs - batman-adv netlink attributes - * - * @BATADV_ATTR_UNSPEC: unspecified attribute to catch errors - * @BATADV_ATTR_VERSION: batman-adv version string - * @BATADV_ATTR_ALGO_NAME: name of routing algorithm - * @BATADV_ATTR_MESH_IFINDEX: index of the batman-adv interface - * @BATADV_ATTR_MESH_IFNAME: name of the batman-adv interface - * @BATADV_ATTR_MESH_ADDRESS: mac address of the batman-adv interface - * @BATADV_ATTR_HARD_IFINDEX: index of the non-batman-adv interface - * @BATADV_ATTR_HARD_IFNAME: name of the non-batman-adv interface - * @BATADV_ATTR_HARD_ADDRESS: mac address of the non-batman-adv interface - * @BATADV_ATTR_ORIG_ADDRESS: originator mac address - * @BATADV_ATTR_TPMETER_RESULT: result of run (see batadv_tp_meter_status) - * @BATADV_ATTR_TPMETER_TEST_TIME: time (msec) the run took - * @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run - * @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session - * @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment - * @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active - * @BATADV_ATTR_TT_ADDRESS: Client MAC address - * @BATADV_ATTR_TT_TTVN: Translation table version - * @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version - * @BATADV_ATTR_TT_CRC32: CRC32 over translation table - * @BATADV_ATTR_TT_VID: VLAN ID - * @BATADV_ATTR_TT_FLAGS: Translation table client flags - * @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best - * @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen - * @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address - * @BATADV_ATTR_TQ: TQ to neighbour - * @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour - * @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth - * @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth - * @BATADV_ATTR_ROUTER: Gateway router MAC address - * @BATADV_ATTR_BLA_OWN: Flag indicating own originator - * @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address - * @BATADV_ATTR_BLA_VID: BLA VLAN ID - * @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address - * @BATADV_ATTR_BLA_CRC: BLA CRC - * @__BATADV_ATTR_AFTER_LAST: internal use - * @NUM_BATADV_ATTR: total number of batadv_nl_attrs available - * @BATADV_ATTR_MAX: highest attribute number currently defined */ enum batadv_nl_attrs { + /** + * @BATADV_ATTR_UNSPEC: unspecified attribute to catch errors + */ BATADV_ATTR_UNSPEC, + + /** + * @BATADV_ATTR_VERSION: batman-adv version string + */ BATADV_ATTR_VERSION, + + /** + * @BATADV_ATTR_ALGO_NAME: name of routing algorithm + */ BATADV_ATTR_ALGO_NAME, + + /** + * @BATADV_ATTR_MESH_IFINDEX: index of the batman-adv interface + */ BATADV_ATTR_MESH_IFINDEX, + + /** + * @BATADV_ATTR_MESH_IFNAME: name of the batman-adv interface + */ BATADV_ATTR_MESH_IFNAME, + + /** + * @BATADV_ATTR_MESH_ADDRESS: mac address of the batman-adv interface + */ BATADV_ATTR_MESH_ADDRESS, + + /** + * @BATADV_ATTR_HARD_IFINDEX: index of the non-batman-adv interface + */ BATADV_ATTR_HARD_IFINDEX, + + /** + * @BATADV_ATTR_HARD_IFNAME: name of the non-batman-adv interface + */ BATADV_ATTR_HARD_IFNAME, + + /** + * @BATADV_ATTR_HARD_ADDRESS: mac address of the non-batman-adv + * interface + */ BATADV_ATTR_HARD_ADDRESS, + + /** + * @BATADV_ATTR_ORIG_ADDRESS: originator mac address + */ BATADV_ATTR_ORIG_ADDRESS, + + /** + * @BATADV_ATTR_TPMETER_RESULT: result of run (see + * batadv_tp_meter_status) + */ BATADV_ATTR_TPMETER_RESULT, + + /** + * @BATADV_ATTR_TPMETER_TEST_TIME: time (msec) the run took + */ BATADV_ATTR_TPMETER_TEST_TIME, + + /** + * @BATADV_ATTR_TPMETER_BYTES: amount of acked bytes during run + */ BATADV_ATTR_TPMETER_BYTES, + + /** + * @BATADV_ATTR_TPMETER_COOKIE: session cookie to match tp_meter session + */ BATADV_ATTR_TPMETER_COOKIE, + + /** + * @BATADV_ATTR_PAD: attribute used for padding for 64-bit alignment + */ BATADV_ATTR_PAD, + + /** + * @BATADV_ATTR_ACTIVE: Flag indicating if the hard interface is active + */ BATADV_ATTR_ACTIVE, + + /** + * @BATADV_ATTR_TT_ADDRESS: Client MAC address + */ BATADV_ATTR_TT_ADDRESS, + + /** + * @BATADV_ATTR_TT_TTVN: Translation table version + */ BATADV_ATTR_TT_TTVN, + + /** + * @BATADV_ATTR_TT_LAST_TTVN: Previous translation table version + */ BATADV_ATTR_TT_LAST_TTVN, + + /** + * @BATADV_ATTR_TT_CRC32: CRC32 over translation table + */ BATADV_ATTR_TT_CRC32, + + /** + * @BATADV_ATTR_TT_VID: VLAN ID + */ BATADV_ATTR_TT_VID, + + /** + * @BATADV_ATTR_TT_FLAGS: Translation table client flags + */ BATADV_ATTR_TT_FLAGS, + + /** + * @BATADV_ATTR_FLAG_BEST: Flags indicating entry is the best + */ BATADV_ATTR_FLAG_BEST, + + /** + * @BATADV_ATTR_LAST_SEEN_MSECS: Time in milliseconds since last seen + */ BATADV_ATTR_LAST_SEEN_MSECS, + + /** + * @BATADV_ATTR_NEIGH_ADDRESS: Neighbour MAC address + */ BATADV_ATTR_NEIGH_ADDRESS, + + /** + * @BATADV_ATTR_TQ: TQ to neighbour + */ BATADV_ATTR_TQ, + + /** + * @BATADV_ATTR_THROUGHPUT: Estimated throughput to Neighbour + */ BATADV_ATTR_THROUGHPUT, + + /** + * @BATADV_ATTR_BANDWIDTH_UP: Reported uplink bandwidth + */ BATADV_ATTR_BANDWIDTH_UP, + + /** + * @BATADV_ATTR_BANDWIDTH_DOWN: Reported downlink bandwidth + */ BATADV_ATTR_BANDWIDTH_DOWN, + + /** + * @BATADV_ATTR_ROUTER: Gateway router MAC address + */ BATADV_ATTR_ROUTER, + + /** + * @BATADV_ATTR_BLA_OWN: Flag indicating own originator + */ BATADV_ATTR_BLA_OWN, + + /** + * @BATADV_ATTR_BLA_ADDRESS: Bridge loop avoidance claim MAC address + */ BATADV_ATTR_BLA_ADDRESS, + + /** + * @BATADV_ATTR_BLA_VID: BLA VLAN ID + */ BATADV_ATTR_BLA_VID, + + /** + * @BATADV_ATTR_BLA_BACKBONE: BLA gateway originator MAC address + */ BATADV_ATTR_BLA_BACKBONE, + + /** + * @BATADV_ATTR_BLA_CRC: BLA CRC + */ BATADV_ATTR_BLA_CRC, + /* add attributes above here, update the policy in netlink.c */ + + /** + * @__BATADV_ATTR_AFTER_LAST: internal use + */ __BATADV_ATTR_AFTER_LAST, + + /** + * @NUM_BATADV_ATTR: total number of batadv_nl_attrs available + */ NUM_BATADV_ATTR = __BATADV_ATTR_AFTER_LAST, + + /** + * @BATADV_ATTR_MAX: highest attribute number currently defined + */ BATADV_ATTR_MAX = __BATADV_ATTR_AFTER_LAST - 1 }; /** * enum batadv_nl_commands - supported batman-adv netlink commands - * - * @BATADV_CMD_UNSPEC: unspecified command to catch errors - * @BATADV_CMD_GET_MESH_INFO: Query basic information about batman-adv device - * @BATADV_CMD_TP_METER: Start a tp meter session - * @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session - * @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms. - * @BATADV_CMD_GET_HARDIFS: Query list of hard interfaces - * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations - * @BATADV_CMD_GET_TRANSTABLE_GLOBAL Query list of global translations - * @BATADV_CMD_GET_ORIGINATORS: Query list of originators - * @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours - * @BATADV_CMD_GET_GATEWAYS: Query list of gateways - * @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims - * @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance backbones - * @__BATADV_CMD_AFTER_LAST: internal use - * @BATADV_CMD_MAX: highest used command number */ enum batadv_nl_commands { + /** + * @BATADV_CMD_UNSPEC: unspecified command to catch errors + */ BATADV_CMD_UNSPEC, + + /** + * @BATADV_CMD_GET_MESH_INFO: Query basic information about batman-adv + * device + */ BATADV_CMD_GET_MESH_INFO, + + /** + * @BATADV_CMD_TP_METER: Start a tp meter session + */ BATADV_CMD_TP_METER, + + /** + * @BATADV_CMD_TP_METER_CANCEL: Cancel a tp meter session + */ BATADV_CMD_TP_METER_CANCEL, + + /** + * @BATADV_CMD_GET_ROUTING_ALGOS: Query the list of routing algorithms. + */ BATADV_CMD_GET_ROUTING_ALGOS, + + /** + * @BATADV_CMD_GET_HARDIFS: Query list of hard interfaces + */ BATADV_CMD_GET_HARDIFS, + + /** + * @BATADV_CMD_GET_TRANSTABLE_LOCAL: Query list of local translations + */ BATADV_CMD_GET_TRANSTABLE_LOCAL, + + /** + * @BATADV_CMD_GET_TRANSTABLE_GLOBAL: Query list of global translations + */ BATADV_CMD_GET_TRANSTABLE_GLOBAL, + + /** + * @BATADV_CMD_GET_ORIGINATORS: Query list of originators + */ BATADV_CMD_GET_ORIGINATORS, + + /** + * @BATADV_CMD_GET_NEIGHBORS: Query list of neighbours + */ BATADV_CMD_GET_NEIGHBORS, + + /** + * @BATADV_CMD_GET_GATEWAYS: Query list of gateways + */ BATADV_CMD_GET_GATEWAYS, + + /** + * @BATADV_CMD_GET_BLA_CLAIM: Query list of bridge loop avoidance claims + */ BATADV_CMD_GET_BLA_CLAIM, + + /** + * @BATADV_CMD_GET_BLA_BACKBONE: Query list of bridge loop avoidance + * backbones + */ BATADV_CMD_GET_BLA_BACKBONE, + /* add new commands above here */ + + /** + * @__BATADV_CMD_AFTER_LAST: internal use + */ __BATADV_CMD_AFTER_LAST, + + /** + * @BATADV_CMD_MAX: highest used command number + */ BATADV_CMD_MAX = __BATADV_CMD_AFTER_LAST - 1 }; /** * enum batadv_tp_meter_reason - reason of a tp meter test run stop - * @BATADV_TP_REASON_COMPLETE: sender finished tp run - * @BATADV_TP_REASON_CANCEL: sender was stopped during run - * @BATADV_TP_REASON_DST_UNREACHABLE: receiver could not be reached or didn't - * answer - * @BATADV_TP_REASON_RESEND_LIMIT: (unused) sender retry reached limit - * @BATADV_TP_REASON_ALREADY_ONGOING: test to or from the same node already - * ongoing - * @BATADV_TP_REASON_MEMORY_ERROR: test was stopped due to low memory - * @BATADV_TP_REASON_CANT_SEND: failed to send via outgoing interface - * @BATADV_TP_REASON_TOO_MANY: too many ongoing sessions */ enum batadv_tp_meter_reason { + /** + * @BATADV_TP_REASON_COMPLETE: sender finished tp run + */ BATADV_TP_REASON_COMPLETE = 3, + + /** + * @BATADV_TP_REASON_CANCEL: sender was stopped during run + */ BATADV_TP_REASON_CANCEL = 4, + /* error status >= 128 */ + + /** + * @BATADV_TP_REASON_DST_UNREACHABLE: receiver could not be reached or + * didn't answer + */ BATADV_TP_REASON_DST_UNREACHABLE = 128, + + /** + * @BATADV_TP_REASON_RESEND_LIMIT: (unused) sender retry reached limit + */ BATADV_TP_REASON_RESEND_LIMIT = 129, + + /** + * @BATADV_TP_REASON_ALREADY_ONGOING: test to or from the same node + * already ongoing + */ BATADV_TP_REASON_ALREADY_ONGOING = 130, + + /** + * @BATADV_TP_REASON_MEMORY_ERROR: test was stopped due to low memory + */ BATADV_TP_REASON_MEMORY_ERROR = 131, + + /** + * @BATADV_TP_REASON_CANT_SEND: failed to send via outgoing interface + */ BATADV_TP_REASON_CANT_SEND = 132, + + /** + * @BATADV_TP_REASON_TOO_MANY: too many ongoing sessions + */ BATADV_TP_REASON_TOO_MANY = 133, }; -- cgit v1.2.3-59-g8ed1b From a11bc476b987925654369411dd8281a60cb5a175 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 4 Sep 2017 12:19:07 -0700 Subject: Input: uinput - fold header into the driver proper There is nothing in the uinput kernel header that is of use to anyone in the kernel besides the uinput driver itself, so let's fold it into the driver code (leaving uapi part intact). Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 40 +++++++++++++++++++++- include/linux/uinput.h | 81 --------------------------------------------- 2 files changed, 39 insertions(+), 82 deletions(-) delete mode 100644 include/linux/uinput.h (limited to 'include') diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 06f3ac67dde1..7b41aad7ec27 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -31,6 +31,7 @@ * 0.1 20/06/2002 * - first public version */ +#include #include #include #include @@ -38,10 +39,47 @@ #include #include #include -#include #include #include "../input-compat.h" +#define UINPUT_NAME "uinput" +#define UINPUT_BUFFER_SIZE 16 +#define UINPUT_NUM_REQUESTS 16 + +enum uinput_state { UIST_NEW_DEVICE, UIST_SETUP_COMPLETE, UIST_CREATED }; + +struct uinput_request { + unsigned int id; + unsigned int code; /* UI_FF_UPLOAD, UI_FF_ERASE */ + + int retval; + struct completion done; + + union { + unsigned int effect_id; + struct { + struct ff_effect *effect; + struct ff_effect *old; + } upload; + } u; +}; + +struct uinput_device { + struct input_dev *dev; + struct mutex mutex; + enum uinput_state state; + wait_queue_head_t waitq; + unsigned char ready; + unsigned char head; + unsigned char tail; + struct input_event buff[UINPUT_BUFFER_SIZE]; + unsigned int ff_effects_max; + + struct uinput_request *requests[UINPUT_NUM_REQUESTS]; + wait_queue_head_t requests_waitq; + spinlock_t requests_lock; +}; + static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { diff --git a/include/linux/uinput.h b/include/linux/uinput.h deleted file mode 100644 index 75de43da2301..000000000000 --- a/include/linux/uinput.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * User level driver support for input subsystem - * - * Heavily based on evdev.c by Vojtech Pavlik - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Author: Aristeu Sergio Rozanski Filho - * - * Changes/Revisions: - * 0.5 08/13/2015 (David Herrmann & - * Benjamin Tissoires ) - * - add UI_DEV_SETUP ioctl - * - add UI_ABS_SETUP ioctl - * - add UI_GET_VERSION ioctl - * 0.4 01/09/2014 (Benjamin Tissoires ) - * - add UI_GET_SYSNAME ioctl - * 0.3 24/05/2006 (Anssi Hannula ) - * - update ff support for the changes in kernel interface - * - add UINPUT_VERSION - * 0.2 16/10/2004 (Micah Dowty ) - * - added force feedback support - * - added UI_SET_PHYS - * 0.1 20/06/2002 - * - first public version - */ -#ifndef __UINPUT_H_ -#define __UINPUT_H_ - -#include - -#define UINPUT_NAME "uinput" -#define UINPUT_BUFFER_SIZE 16 -#define UINPUT_NUM_REQUESTS 16 - -enum uinput_state { UIST_NEW_DEVICE, UIST_SETUP_COMPLETE, UIST_CREATED }; - -struct uinput_request { - unsigned int id; - unsigned int code; /* UI_FF_UPLOAD, UI_FF_ERASE */ - - int retval; - struct completion done; - - union { - unsigned int effect_id; - struct { - struct ff_effect *effect; - struct ff_effect *old; - } upload; - } u; -}; - -struct uinput_device { - struct input_dev *dev; - struct mutex mutex; - enum uinput_state state; - wait_queue_head_t waitq; - unsigned char ready; - unsigned char head; - unsigned char tail; - struct input_event buff[UINPUT_BUFFER_SIZE]; - unsigned int ff_effects_max; - - struct uinput_request *requests[UINPUT_NUM_REQUESTS]; - wait_queue_head_t requests_waitq; - spinlock_t requests_lock; -}; -#endif /* __UINPUT_H_ */ -- cgit v1.2.3-59-g8ed1b From f6e37b25413cf636369668652e9752ee77c7d9f7 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Mon, 23 Oct 2017 09:20:22 -0700 Subject: tcp: add trace event class tcp_event_sk_skb Introduce event class tcp_event_sk_skb for tcp tracepoints that have arguments sk and skb. Existing tracepoint trace_tcp_retransmit_skb() falls into this class. This patch rewrites the definition of trace_tcp_retransmit_skb() with tcp_event_sk_skb. Signed-off-by: Song Liu Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index c3220d914475..14b0a7083f1d 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -9,7 +9,13 @@ #include #include -TRACE_EVENT(tcp_retransmit_skb, +/* + * tcp event with arguments sk and skb + * + * Note: this class requires a valid sk pointer; while skb pointer could + * be NULL. + */ +DECLARE_EVENT_CLASS(tcp_event_sk_skb, TP_PROTO(struct sock *sk, struct sk_buff *skb), @@ -64,6 +70,13 @@ TRACE_EVENT(tcp_retransmit_skb, __entry->saddr_v6, __entry->daddr_v6) ); +DEFINE_EVENT(tcp_event_sk_skb, tcp_retransmit_skb, + + TP_PROTO(struct sock *sk, struct sk_buff *skb), + + TP_ARGS(sk, skb) +); + #endif /* _TRACE_TCP_H */ /* This part must be outside protection */ -- cgit v1.2.3-59-g8ed1b From 7344e29f285a94b965075599731811c352f3ab40 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Mon, 23 Oct 2017 09:20:23 -0700 Subject: tcp: mark trace event arguments sk and skb as const Some functions that we plan to add trace points require const sk and/or skb. So we mark these fields as const in the tracepoint. Signed-off-by: Song Liu Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index 14b0a7083f1d..2b6fe72c6781 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -17,13 +17,13 @@ */ DECLARE_EVENT_CLASS(tcp_event_sk_skb, - TP_PROTO(struct sock *sk, struct sk_buff *skb), + TP_PROTO(const struct sock *sk, const struct sk_buff *skb), TP_ARGS(sk, skb), TP_STRUCT__entry( - __field(void *, skbaddr) - __field(void *, skaddr) + __field(const void *, skbaddr) + __field(const void *, skaddr) __field(__u16, sport) __field(__u16, dport) __array(__u8, saddr, 4) @@ -72,7 +72,7 @@ DECLARE_EVENT_CLASS(tcp_event_sk_skb, DEFINE_EVENT(tcp_event_sk_skb, tcp_retransmit_skb, - TP_PROTO(struct sock *sk, struct sk_buff *skb), + TP_PROTO(const struct sock *sk, const struct sk_buff *skb), TP_ARGS(sk, skb) ); -- cgit v1.2.3-59-g8ed1b From c24b14c46bb88d844275de5c4024c8745ae89d42 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Mon, 23 Oct 2017 09:20:24 -0700 Subject: tcp: add tracepoint trace_tcp_send_reset New tracepoint trace_tcp_send_reset is added and called from tcp_v4_send_reset(), tcp_v6_send_reset() and tcp_send_active_reset(). Signed-off-by: Song Liu Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 11 +++++++++++ net/core/net-traces.c | 2 ++ net/ipv4/tcp_ipv4.c | 6 +++++- net/ipv4/tcp_output.c | 5 +++++ net/ipv6/tcp_ipv6.c | 10 ++++++++-- 5 files changed, 31 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index 2b6fe72c6781..3e57e1ae1c6b 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -77,6 +77,17 @@ DEFINE_EVENT(tcp_event_sk_skb, tcp_retransmit_skb, TP_ARGS(sk, skb) ); +/* + * skb of trace_tcp_send_reset is the skb that caused RST. In case of + * active reset, skb should be NULL + */ +DEFINE_EVENT(tcp_event_sk_skb, tcp_send_reset, + + TP_PROTO(const struct sock *sk, const struct sk_buff *skb), + + TP_ARGS(sk, skb) +); + #endif /* _TRACE_TCP_H */ /* This part must be outside protection */ diff --git a/net/core/net-traces.c b/net/core/net-traces.c index f4e4fa2db505..8dcd9b0be04a 100644 --- a/net/core/net-traces.c +++ b/net/core/net-traces.c @@ -49,3 +49,5 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(br_fdb_update); EXPORT_TRACEPOINT_SYMBOL_GPL(kfree_skb); EXPORT_TRACEPOINT_SYMBOL_GPL(napi_poll); + +EXPORT_TRACEPOINT_SYMBOL_GPL(tcp_send_reset); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index e22439f05e46..eb3f3b8e1e4b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -85,6 +85,8 @@ #include #include +#include + #ifdef CONFIG_TCP_MD5SIG static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, __be32 daddr, __be32 saddr, const struct tcphdr *th); @@ -701,8 +703,10 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) * routing might fail in this case. No choice here, if we choose to force * input interface, we will misroute in case of asymmetric route. */ - if (sk) + if (sk) { arg.bound_dev_if = sk->sk_bound_dev_if; + trace_tcp_send_reset(sk, skb); + } BUILD_BUG_ON(offsetof(struct sock, sk_bound_dev_if) != offsetof(struct inet_timewait_sock, tw_bound_dev_if)); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 988733f289c8..1f01f4c9c738 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3084,6 +3084,11 @@ void tcp_send_active_reset(struct sock *sk, gfp_t priority) /* Send it off. */ if (tcp_transmit_skb(sk, skb, 0, priority)) NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTFAILED); + + /* skb of trace_tcp_send_reset() keeps the skb that caused RST, + * skb here is different to the troublesome skb, so use NULL + */ + trace_tcp_send_reset(sk, NULL); } /* Send a crossed SYN-ACK during socket establishment. diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index ae83615b7f6d..0e2529958b52 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -69,6 +69,8 @@ #include #include +#include + static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb); static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb, struct request_sock *req); @@ -890,7 +892,7 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) int genhash; struct sock *sk1 = NULL; #endif - int oif; + int oif = 0; if (th->rst) return; @@ -939,7 +941,11 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) ack_seq = ntohl(th->seq) + th->syn + th->fin + skb->len - (th->doff << 2); - oif = sk ? sk->sk_bound_dev_if : 0; + if (sk) { + oif = sk->sk_bound_dev_if; + trace_tcp_send_reset(sk, skb); + } + tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0); #ifdef CONFIG_TCP_MD5SIG -- cgit v1.2.3-59-g8ed1b From 5941521c05d69cf3f2b1293eefd21207e083b70f Mon Sep 17 00:00:00 2001 From: Song Liu Date: Mon, 23 Oct 2017 09:20:25 -0700 Subject: tcp: add tracepoint trace_tcp_receive_reset New tracepoint trace_tcp_receive_reset is added and called from tcp_reset(). This tracepoint is define with a new class tcp_event_sk. Signed-off-by: Song Liu Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 66 ++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_input.c | 3 +++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index 3e57e1ae1c6b..c83c71187719 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -88,6 +88,72 @@ DEFINE_EVENT(tcp_event_sk_skb, tcp_send_reset, TP_ARGS(sk, skb) ); +/* + * tcp event with arguments sk + * + * Note: this class requires a valid sk pointer. + */ +DECLARE_EVENT_CLASS(tcp_event_sk, + + TP_PROTO(const struct sock *sk), + + TP_ARGS(sk), + + TP_STRUCT__entry( + __field(const void *, skaddr) + __field(__u16, sport) + __field(__u16, dport) + __array(__u8, saddr, 4) + __array(__u8, daddr, 4) + __array(__u8, saddr_v6, 16) + __array(__u8, daddr_v6, 16) + ), + + TP_fast_assign( + struct inet_sock *inet = inet_sk(sk); + struct in6_addr *pin6; + __be32 *p32; + + __entry->skaddr = sk; + + __entry->sport = ntohs(inet->inet_sport); + __entry->dport = ntohs(inet->inet_dport); + + p32 = (__be32 *) __entry->saddr; + *p32 = inet->inet_saddr; + + p32 = (__be32 *) __entry->daddr; + *p32 = inet->inet_daddr; + +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family == AF_INET6) { + pin6 = (struct in6_addr *)__entry->saddr_v6; + *pin6 = sk->sk_v6_rcv_saddr; + pin6 = (struct in6_addr *)__entry->daddr_v6; + *pin6 = sk->sk_v6_daddr; + } else +#endif + { + pin6 = (struct in6_addr *)__entry->saddr_v6; + ipv6_addr_set_v4mapped(inet->inet_saddr, pin6); + pin6 = (struct in6_addr *)__entry->daddr_v6; + ipv6_addr_set_v4mapped(inet->inet_daddr, pin6); + } + ), + + TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c", + __entry->sport, __entry->dport, + __entry->saddr, __entry->daddr, + __entry->saddr_v6, __entry->daddr_v6) +); + +DEFINE_EVENT(tcp_event_sk, tcp_receive_reset, + + TP_PROTO(const struct sock *sk), + + TP_ARGS(sk) +); + #endif /* _TRACE_TCP_H */ /* This part must be outside protection */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ab3f12898245..c5e64d4b5839 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -75,6 +75,7 @@ #include #include #include +#include int sysctl_tcp_fack __read_mostly; int sysctl_tcp_max_reordering __read_mostly = 300; @@ -4010,6 +4011,8 @@ static inline bool tcp_sequence(const struct tcp_sock *tp, u32 seq, u32 end_seq) /* When we get a reset we do this. */ void tcp_reset(struct sock *sk) { + trace_tcp_receive_reset(sk); + /* We want the right error as BSD sees it (and indeed as we do). */ switch (sk->sk_state) { case TCP_SYN_SENT: -- cgit v1.2.3-59-g8ed1b From e1a4aa50f47303ebb3ca0cfd01687884551ce03d Mon Sep 17 00:00:00 2001 From: Song Liu Date: Mon, 23 Oct 2017 09:20:26 -0700 Subject: tcp: add tracepoint trace_tcp_destroy_sock This patch adds trace event trace_tcp_destroy_sock. Signed-off-by: Song Liu Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 7 +++++++ net/ipv4/tcp_ipv4.c | 2 ++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index c83c71187719..1724c12c25cf 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -154,6 +154,13 @@ DEFINE_EVENT(tcp_event_sk, tcp_receive_reset, TP_ARGS(sk) ); +DEFINE_EVENT(tcp_event_sk, tcp_destroy_sock, + + TP_PROTO(const struct sock *sk), + + TP_ARGS(sk) +); + #endif /* _TRACE_TCP_H */ /* This part must be outside protection */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index eb3f3b8e1e4b..23a8100af5ad 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1869,6 +1869,8 @@ void tcp_v4_destroy_sock(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); + trace_tcp_destroy_sock(sk); + tcp_clear_xmit_timers(sk); tcp_cleanup_congestion_control(sk); -- cgit v1.2.3-59-g8ed1b From e8fce23946b7e7eadf25ad78d8207c22903dfe27 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Mon, 23 Oct 2017 09:20:27 -0700 Subject: tcp: add tracepoint trace_tcp_set_state() This patch adds tracepoint trace_tcp_set_state. Besides usual fields (s/d ports, IP addresses), old and new state of the socket is also printed with TP_printk, with __print_symbolic(). Signed-off-by: Song Liu Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 76 ++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp.c | 4 +++ 2 files changed, 80 insertions(+) (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index 1724c12c25cf..03699ba71623 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -9,6 +9,22 @@ #include #include +#define tcp_state_name(state) { state, #state } +#define show_tcp_state_name(val) \ + __print_symbolic(val, \ + tcp_state_name(TCP_ESTABLISHED), \ + tcp_state_name(TCP_SYN_SENT), \ + tcp_state_name(TCP_SYN_RECV), \ + tcp_state_name(TCP_FIN_WAIT1), \ + tcp_state_name(TCP_FIN_WAIT2), \ + tcp_state_name(TCP_TIME_WAIT), \ + tcp_state_name(TCP_CLOSE), \ + tcp_state_name(TCP_CLOSE_WAIT), \ + tcp_state_name(TCP_LAST_ACK), \ + tcp_state_name(TCP_LISTEN), \ + tcp_state_name(TCP_CLOSING), \ + tcp_state_name(TCP_NEW_SYN_RECV)) + /* * tcp event with arguments sk and skb * @@ -161,6 +177,66 @@ DEFINE_EVENT(tcp_event_sk, tcp_destroy_sock, TP_ARGS(sk) ); +TRACE_EVENT(tcp_set_state, + + TP_PROTO(const struct sock *sk, const int oldstate, const int newstate), + + TP_ARGS(sk, oldstate, newstate), + + TP_STRUCT__entry( + __field(const void *, skaddr) + __field(int, oldstate) + __field(int, newstate) + __field(__u16, sport) + __field(__u16, dport) + __array(__u8, saddr, 4) + __array(__u8, daddr, 4) + __array(__u8, saddr_v6, 16) + __array(__u8, daddr_v6, 16) + ), + + TP_fast_assign( + struct inet_sock *inet = inet_sk(sk); + struct in6_addr *pin6; + __be32 *p32; + + __entry->skaddr = sk; + __entry->oldstate = oldstate; + __entry->newstate = newstate; + + __entry->sport = ntohs(inet->inet_sport); + __entry->dport = ntohs(inet->inet_dport); + + p32 = (__be32 *) __entry->saddr; + *p32 = inet->inet_saddr; + + p32 = (__be32 *) __entry->daddr; + *p32 = inet->inet_daddr; + +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family == AF_INET6) { + pin6 = (struct in6_addr *)__entry->saddr_v6; + *pin6 = sk->sk_v6_rcv_saddr; + pin6 = (struct in6_addr *)__entry->daddr_v6; + *pin6 = sk->sk_v6_daddr; + } else +#endif + { + pin6 = (struct in6_addr *)__entry->saddr_v6; + ipv6_addr_set_v4mapped(inet->inet_saddr, pin6); + pin6 = (struct in6_addr *)__entry->daddr_v6; + ipv6_addr_set_v4mapped(inet->inet_daddr, pin6); + } + ), + + TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c oldstate=%s newstate=%s", + __entry->sport, __entry->dport, + __entry->saddr, __entry->daddr, + __entry->saddr_v6, __entry->daddr_v6, + show_tcp_state_name(__entry->oldstate), + show_tcp_state_name(__entry->newstate)) +); + #endif /* _TRACE_TCP_H */ /* This part must be outside protection */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8b1fa4dd4538..be07e9b6dbdd 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -282,6 +282,8 @@ #include #include +#include + int sysctl_tcp_min_tso_segs __read_mostly = 2; int sysctl_tcp_autocorking __read_mostly = 1; @@ -2040,6 +2042,8 @@ void tcp_set_state(struct sock *sk, int state) { int oldstate = sk->sk_state; + trace_tcp_set_state(sk, oldstate, state); + switch (state) { case TCP_ESTABLISHED: if (oldstate != TCP_ESTABLISHED) -- cgit v1.2.3-59-g8ed1b From 7b9651103b64c8a55eb6cec4c2240584e968354c Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 18 Oct 2017 11:56:21 +0200 Subject: extcon: max77843: Add OTG power control to the MUIC driver Enabling power on VBUS micro-usb pin is required only when passive OTG cable is connected. Initially OTG VBUS power control was planned to be done in charger driver. However such information is not really available from the extcon notifications, so VBUS power control has to be done directly in MUIC driver, which has all information about the attached accessory. For example SmartDock is externally powered accessory, provides OTG (USB HOST) functionality and use VBUS pin for charging a device battery, so the VBUS charging pump should be disabled in such case. Signed-off-by: Marek Szyprowski Acked-by: Chanwoo Choi Acked-by: Lee Jones Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max77843.c | 16 ++++++++++++++++ include/linux/mfd/max77843-private.h | 3 +++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index 28f251ff0fa2..17d6bd76edb4 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -240,6 +240,21 @@ static int max77843_muic_set_path(struct max77843_muic_info *info, return 0; } +static void max77843_charger_set_otg_vbus(struct max77843_muic_info *info, + bool on) +{ + struct max77693_dev *max77843 = info->max77843; + unsigned int cnfg00; + + if (on) + cnfg00 = MAX77843_CHG_OTG_MASK | MAX77843_CHG_BOOST_MASK; + else + cnfg00 = MAX77843_CHG_ENABLE | MAX77843_CHG_BUCK_MASK; + + regmap_update_bits(max77843->regmap_chg, MAX77843_CHG_REG_CHG_CNFG_00, + MAX77843_CHG_MODE_MASK, cnfg00); +} + static int max77843_muic_get_cable_type(struct max77843_muic_info *info, enum max77843_muic_cable_group group, bool *attached) { @@ -355,6 +370,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info) return ret; extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached); + max77843_charger_set_otg_vbus(info, attached); break; case MAX77843_MUIC_GND_MHL_VB: case MAX77843_MUIC_GND_MHL: diff --git a/include/linux/mfd/max77843-private.h b/include/linux/mfd/max77843-private.h index c19303b0ccfd..0223cd5941c8 100644 --- a/include/linux/mfd/max77843-private.h +++ b/include/linux/mfd/max77843-private.h @@ -245,10 +245,13 @@ enum max77843_irq_muic { #define MAX77843_CHG_OVER_CURRENT_BAT (0x06 << 4) /* MAX77843 CHG_CNFG_00 register */ +#define MAX77843_CHG_MODE_MASK 0x0f #define MAX77843_CHG_DISABLE 0x00 #define MAX77843_CHG_ENABLE 0x05 #define MAX77843_CHG_MASK 0x01 +#define MAX77843_CHG_OTG_MASK 0x02 #define MAX77843_CHG_BUCK_MASK 0x04 +#define MAX77843_CHG_BOOST_MASK 0x08 /* MAX77843 CHG_CNFG_01 register */ #define MAX77843_CHG_RESTART_THRESHOLD_100 0x00 -- cgit v1.2.3-59-g8ed1b From 4a4a87146a07c866ad2ef49cc32296e6583b1cee Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 18 Oct 2017 11:56:22 +0200 Subject: extcon: max77843: Add support for SmartDock accessory SmartDock uses ADC_RESERVED_ACC_3 (0x10) ADC ID type and provides following features: 1. USB host with embedded USB hub (2-4 ports) for mice, keyboard, etc, 2. MHL for video output, 3. charging. Tested with Unitek Y-2165 MHL+OTG Hub Smart Phone Dock. Signed-off-by: Marek Szyprowski Acked-by: Chanwoo Choi Acked-by: Lee Jones Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-max77843.c | 77 +++++++++++++++++++++++++++++------- include/linux/mfd/max77843-private.h | 2 + 2 files changed, 65 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index 17d6bd76edb4..c9fcd6cd41cb 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -80,7 +80,7 @@ enum max77843_muic_accessory_type { MAX77843_MUIC_ADC_REMOTE_S12_BUTTON, MAX77843_MUIC_ADC_RESERVED_ACC_1, MAX77843_MUIC_ADC_RESERVED_ACC_2, - MAX77843_MUIC_ADC_RESERVED_ACC_3, + MAX77843_MUIC_ADC_RESERVED_ACC_3, /* SmartDock */ MAX77843_MUIC_ADC_RESERVED_ACC_4, MAX77843_MUIC_ADC_RESERVED_ACC_5, MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2, @@ -119,6 +119,7 @@ enum max77843_muic_charger_type { MAX77843_MUIC_CHG_SPECIAL_BIAS, MAX77843_MUIC_CHG_RESERVED, MAX77843_MUIC_CHG_GND, + MAX77843_MUIC_CHG_DOCK, }; static const unsigned int max77843_extcon_cable[] = { @@ -130,6 +131,7 @@ static const unsigned int max77843_extcon_cable[] = { EXTCON_CHG_USB_FAST, EXTCON_CHG_USB_SLOW, EXTCON_DISP_MHL, + EXTCON_DOCK, EXTCON_JIG, EXTCON_NONE, }; @@ -200,7 +202,7 @@ static const struct regmap_irq_chip max77843_muic_irq_chip = { }; static int max77843_muic_set_path(struct max77843_muic_info *info, - u8 val, bool attached) + u8 val, bool attached, bool nobccomp) { struct max77693_dev *max77843 = info->max77843; int ret = 0; @@ -210,10 +212,16 @@ static int max77843_muic_set_path(struct max77843_muic_info *info, ctrl1 = val; else ctrl1 = MAX77843_MUIC_CONTROL1_SW_OPEN; + if (nobccomp) { + /* Disable BC1.2 protocol and force manual switch control */ + ctrl1 |= MAX77843_MUIC_CONTROL1_NOBCCOMP_MASK; + } ret = regmap_update_bits(max77843->regmap_muic, MAX77843_MUIC_REG_CONTROL1, - MAX77843_MUIC_CONTROL1_COM_SW, ctrl1); + MAX77843_MUIC_CONTROL1_COM_SW | + MAX77843_MUIC_CONTROL1_NOBCCOMP_MASK, + ctrl1); if (ret < 0) { dev_err(info->dev, "Cannot switch MUIC port\n"); return ret; @@ -303,6 +311,19 @@ static int max77843_muic_get_cable_type(struct max77843_muic_info *info, break; } + if (adc == MAX77843_MUIC_ADC_RESERVED_ACC_3) { /* SmartDock */ + if (chg_type == MAX77843_MUIC_CHG_NONE) { + *attached = false; + cable_type = info->prev_chg_type; + info->prev_chg_type = MAX77843_MUIC_CHG_NONE; + } else { + *attached = true; + cable_type = MAX77843_MUIC_CHG_DOCK; + info->prev_chg_type = MAX77843_MUIC_CHG_DOCK; + } + break; + } + if (chg_type == MAX77843_MUIC_CHG_NONE) { *attached = false; cable_type = info->prev_chg_type; @@ -365,7 +386,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info) case MAX77843_MUIC_GND_USB_HOST_VB: ret = max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_USB, - attached); + attached, false); if (ret < 0) return ret; @@ -376,7 +397,7 @@ static int max77843_muic_adc_gnd_handler(struct max77843_muic_info *info) case MAX77843_MUIC_GND_MHL: ret = max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_OPEN, - attached); + attached, false); if (ret < 0) return ret; @@ -412,7 +433,7 @@ static int max77843_muic_jig_handler(struct max77843_muic_info *info, return -EINVAL; } - ret = max77843_muic_set_path(info, path, attached); + ret = max77843_muic_set_path(info, path, attached, false); if (ret < 0) return ret; @@ -421,6 +442,26 @@ static int max77843_muic_jig_handler(struct max77843_muic_info *info, return 0; } +static int max77843_muic_dock_handler(struct max77843_muic_info *info, + bool attached) +{ + int ret; + + dev_dbg(info->dev, "external connector is %s (adc: 0x10)\n", + attached ? "attached" : "detached"); + + ret = max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_USB, + attached, attached); + if (ret < 0) + return ret; + + extcon_set_state_sync(info->edev, EXTCON_DISP_MHL, attached); + extcon_set_state_sync(info->edev, EXTCON_USB_HOST, attached); + extcon_set_state_sync(info->edev, EXTCON_DOCK, attached); + + return 0; +} + static int max77843_muic_adc_handler(struct max77843_muic_info *info) { int ret, cable_type; @@ -435,6 +476,11 @@ static int max77843_muic_adc_handler(struct max77843_muic_info *info) info->prev_cable_type); switch (cable_type) { + case MAX77843_MUIC_ADC_RESERVED_ACC_3: /* SmartDock */ + ret = max77843_muic_dock_handler(info, attached); + if (ret < 0) + return ret; + break; case MAX77843_MUIC_ADC_GROUND: ret = max77843_muic_adc_gnd_handler(info); if (ret < 0) @@ -462,7 +508,6 @@ static int max77843_muic_adc_handler(struct max77843_muic_info *info) case MAX77843_MUIC_ADC_REMOTE_S12_BUTTON: case MAX77843_MUIC_ADC_RESERVED_ACC_1: case MAX77843_MUIC_ADC_RESERVED_ACC_2: - case MAX77843_MUIC_ADC_RESERVED_ACC_3: case MAX77843_MUIC_ADC_RESERVED_ACC_4: case MAX77843_MUIC_ADC_RESERVED_ACC_5: case MAX77843_MUIC_ADC_AUDIO_DEVICE_TYPE2: @@ -506,7 +551,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) case MAX77843_MUIC_CHG_USB: ret = max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_USB, - attached); + attached, false); if (ret < 0) return ret; @@ -517,7 +562,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) case MAX77843_MUIC_CHG_DOWNSTREAM: ret = max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_OPEN, - attached); + attached, false); if (ret < 0) return ret; @@ -527,7 +572,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) case MAX77843_MUIC_CHG_DEDICATED: ret = max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_OPEN, - attached); + attached, false); if (ret < 0) return ret; @@ -537,7 +582,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) case MAX77843_MUIC_CHG_SPECIAL_500MA: ret = max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_OPEN, - attached); + attached, false); if (ret < 0) return ret; @@ -547,7 +592,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) case MAX77843_MUIC_CHG_SPECIAL_1A: ret = max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_OPEN, - attached); + attached, false); if (ret < 0) return ret; @@ -566,6 +611,9 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, false); break; + case MAX77843_MUIC_CHG_DOCK: + extcon_set_state_sync(info->edev, EXTCON_CHG_USB_DCP, attached); + break; case MAX77843_MUIC_CHG_NONE: break; default: @@ -574,7 +622,7 @@ static int max77843_muic_chg_handler(struct max77843_muic_info *info) attached ? "attached" : "detached", chg_type); max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_OPEN, - attached); + attached, false); return -EINVAL; } @@ -814,7 +862,8 @@ static int max77843_muic_probe(struct platform_device *pdev) max77843_muic_set_debounce_time(info, MAX77843_DEBOUNCE_TIME_25MS); /* Set initial path for UART */ - max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_UART, true); + max77843_muic_set_path(info, MAX77843_MUIC_CONTROL1_SW_UART, true, + false); /* Check revision number of MUIC device */ ret = regmap_read(max77843->regmap_muic, MAX77843_MUIC_REG_ID, &id); diff --git a/include/linux/mfd/max77843-private.h b/include/linux/mfd/max77843-private.h index 0223cd5941c8..b8908bf8d315 100644 --- a/include/linux/mfd/max77843-private.h +++ b/include/linux/mfd/max77843-private.h @@ -350,6 +350,7 @@ enum max77843_irq_muic { /* MAX77843 CONTROL register */ #define MAX77843_MUIC_CONTROL1_COMP1SW_SHIFT 0 #define MAX77843_MUIC_CONTROL1_COMP2SW_SHIFT 3 +#define MAX77843_MUIC_CONTROL1_NOBCCOMP_SHIFT 6 #define MAX77843_MUIC_CONTROL1_IDBEN_SHIFT 7 #define MAX77843_MUIC_CONTROL2_LOWPWR_SHIFT 0 #define MAX77843_MUIC_CONTROL2_ADCEN_SHIFT 1 @@ -366,6 +367,7 @@ enum max77843_irq_muic { #define MAX77843_MUIC_CONTROL1_COMP1SW_MASK (0x7 << MAX77843_MUIC_CONTROL1_COMP1SW_SHIFT) #define MAX77843_MUIC_CONTROL1_COMP2SW_MASK (0x7 << MAX77843_MUIC_CONTROL1_COMP2SW_SHIFT) #define MAX77843_MUIC_CONTROL1_IDBEN_MASK BIT(MAX77843_MUIC_CONTROL1_IDBEN_SHIFT) +#define MAX77843_MUIC_CONTROL1_NOBCCOMP_MASK BIT(MAX77843_MUIC_CONTROL1_NOBCCOMP_SHIFT) #define MAX77843_MUIC_CONTROL2_LOWPWR_MASK BIT(MAX77843_MUIC_CONTROL2_LOWPWR_SHIFT) #define MAX77843_MUIC_CONTROL2_ADCEN_MASK BIT(MAX77843_MUIC_CONTROL2_ADCEN_SHIFT) #define MAX77843_MUIC_CONTROL2_CPEN_MASK BIT(MAX77843_MUIC_CONTROL2_CPEN_SHIFT) -- cgit v1.2.3-59-g8ed1b From 3f27fb23219e75343b094366f2358bff34300493 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 23 Oct 2017 16:17:47 -0700 Subject: ipv6: addrconf: add per netns perturbation in inet6_addr_hash() Bring IPv6 in par with IPv4 : - Use net_hash_mix() to spread addresses a bit more. - Use 256 slots hash table instead of 16 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/addrconf.h | 2 +- net/ipv6/addrconf.c | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index b8b16437c6d5..15b5ffd7253d 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -58,7 +58,7 @@ struct in6_validator_info { struct netlink_ext_ack *extack; }; -#define IN6_ADDR_HSIZE_SHIFT 4 +#define IN6_ADDR_HSIZE_SHIFT 8 #define IN6_ADDR_HSIZE (1 << IN6_ADDR_HSIZE_SHIFT) int addrconf_init(void); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index c1a5028f394c..d70d98122053 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -950,9 +950,11 @@ ipv6_link_dev_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) list_add_tail_rcu(&ifp->if_list, p); } -static u32 inet6_addr_hash(const struct in6_addr *addr) +static u32 inet6_addr_hash(const struct net *net, const struct in6_addr *addr) { - return hash_32(ipv6_addr_hash(addr), IN6_ADDR_HSIZE_SHIFT); + u32 val = ipv6_addr_hash(addr) ^ net_hash_mix(net); + + return hash_32(val, IN6_ADDR_HSIZE_SHIFT); } static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, @@ -973,7 +975,7 @@ static bool ipv6_chk_same_addr(struct net *net, const struct in6_addr *addr, static int ipv6_add_addr_hash(struct net_device *dev, struct inet6_ifaddr *ifa) { - unsigned int hash = inet6_addr_hash(&ifa->addr); + unsigned int hash = inet6_addr_hash(dev_net(dev), &ifa->addr); int err = 0; spin_lock(&addrconf_hash_lock); @@ -1838,8 +1840,8 @@ int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, const struct net_device *dev, int strict, u32 banned_flags) { + unsigned int hash = inet6_addr_hash(net, addr); struct inet6_ifaddr *ifp; - unsigned int hash = inet6_addr_hash(addr); u32 ifp_flags; rcu_read_lock_bh(); @@ -1917,8 +1919,8 @@ EXPORT_SYMBOL(ipv6_chk_prefix); struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *addr, struct net_device *dev, int strict) { + unsigned int hash = inet6_addr_hash(net, addr); struct inet6_ifaddr *ifp, *result = NULL; - unsigned int hash = inet6_addr_hash(addr); rcu_read_lock_bh(); hlist_for_each_entry_rcu_bh(ifp, &inet6_addr_lst[hash], addr_lst) { @@ -4242,9 +4244,9 @@ void if6_proc_exit(void) /* Check if address is a home address configured on any interface. */ int ipv6_chk_home_addr(struct net *net, const struct in6_addr *addr) { - int ret = 0; + unsigned int hash = inet6_addr_hash(net, addr); struct inet6_ifaddr *ifp = NULL; - unsigned int hash = inet6_addr_hash(addr); + int ret = 0; rcu_read_lock_bh(); hlist_for_each_entry_rcu_bh(ifp, &inet6_addr_lst[hash], addr_lst) { -- cgit v1.2.3-59-g8ed1b From b6f4f8484d88b69f700907200a9a9ec73806355f Mon Sep 17 00:00:00 2001 From: Tim Hansen Date: Mon, 23 Oct 2017 15:35:58 -0400 Subject: net/sock: Update sk rcu iterator macro. Mark hlist node in sk rcu iterator as protected by the rcu. hlist_next_rcu accomplishes this and silences the warnings sparse throws. Found with make C=1 net/ipv4/udp.o on linux-next tag next-20171009. Signed-off-by: Tim Hansen Signed-off-by: David S. Miller --- include/net/sock.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 4827094f1db4..6f1be9726e02 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -737,10 +737,10 @@ static inline void sk_add_bind_node(struct sock *sk, * */ #define sk_for_each_entry_offset_rcu(tpos, pos, head, offset) \ - for (pos = rcu_dereference((head)->first); \ + for (pos = rcu_dereference(hlist_first_rcu(head)); \ pos != NULL && \ ({ tpos = (typeof(*tpos) *)((void *)pos - offset); 1;}); \ - pos = rcu_dereference(pos->next)) + pos = rcu_dereference(hlist_next_rcu(pos))) static inline struct user_namespace *sk_user_ns(struct sock *sk) { -- cgit v1.2.3-59-g8ed1b From 71c02379c762cb616c00fd5c4ed253fbf6bbe11b Mon Sep 17 00:00:00 2001 From: Christoph Paasch Date: Mon, 23 Oct 2017 13:22:23 -0700 Subject: tcp: Configure TFO without cookie per socket and/or per route We already allow to enable TFO without a cookie by using the fastopen-sysctl and setting it to TFO_SERVER_COOKIE_NOT_REQD (or TFO_CLIENT_NO_COOKIE). This is safe to do in certain environments where we know that there isn't a malicous host (aka., data-centers) or when the application-protocol already provides an authentication mechanism in the first flight of data. A server however might be providing multiple services or talking to both sides (public Internet and data-center). So, this server would want to enable cookie-less TFO for certain services and/or for connections that go to the data-center. This patch exposes a socket-option and a per-route attribute to enable such fine-grained configurations. Signed-off-by: Christoph Paasch Reviewed-by: Yuchung Cheng Signed-off-by: David S. Miller --- include/linux/tcp.h | 3 ++- include/net/tcp.h | 3 ++- include/uapi/linux/rtnetlink.h | 2 ++ include/uapi/linux/tcp.h | 1 + net/ipv4/tcp.c | 12 ++++++++++++ net/ipv4/tcp_fastopen.c | 20 +++++++++++++++++--- net/ipv4/tcp_input.c | 2 +- 7 files changed, 37 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 1d2c44e09e31..173a7c2f9636 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -215,7 +215,8 @@ struct tcp_sock { u8 chrono_type:2, /* current chronograph type */ rate_app_limited:1, /* rate_{delivered,interval_us} limited? */ fastopen_connect:1, /* FASTOPEN_CONNECT sockopt */ - unused:4; + fastopen_no_cookie:1, /* Allow send/recv SYN+data without a cookie */ + unused:3; u8 nonagle : 4,/* Disable Nagle algorithm? */ thin_lto : 1,/* Use linear timeouts for thin streams */ unused1 : 1, diff --git a/include/net/tcp.h b/include/net/tcp.h index 2c13484704cb..2392f74074e7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1567,7 +1567,8 @@ int tcp_fastopen_reset_cipher(struct net *net, struct sock *sk, void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb); struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, struct request_sock *req, - struct tcp_fastopen_cookie *foc); + struct tcp_fastopen_cookie *foc, + const struct dst_entry *dst); void tcp_fastopen_init_key_once(struct net *net); bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss, struct tcp_fastopen_cookie *cookie); diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index dab7dad9e01a..fe6679268901 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -430,6 +430,8 @@ enum { #define RTAX_QUICKACK RTAX_QUICKACK RTAX_CC_ALGO, #define RTAX_CC_ALGO RTAX_CC_ALGO + RTAX_FASTOPEN_NO_COOKIE, +#define RTAX_FASTOPEN_NO_COOKIE RTAX_FASTOPEN_NO_COOKIE __RTAX_MAX }; diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 69c7493e42f8..d67e1d40c6d6 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -120,6 +120,7 @@ enum { #define TCP_ULP 31 /* Attach a ULP to a TCP connection */ #define TCP_MD5SIG_EXT 32 /* TCP MD5 Signature with extensions */ #define TCP_FASTOPEN_KEY 33 /* Set the key for Fast Open (cookie) */ +#define TCP_FASTOPEN_NO_COOKIE 34 /* Enable TFO without a TFO cookie */ struct tcp_repair_opt { __u32 opt_code; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index be07e9b6dbdd..8f36277e82e9 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2836,6 +2836,14 @@ static int do_tcp_setsockopt(struct sock *sk, int level, err = -EOPNOTSUPP; } break; + case TCP_FASTOPEN_NO_COOKIE: + if (val > 1 || val < 0) + err = -EINVAL; + else if (!((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) + err = -EINVAL; + else + tp->fastopen_no_cookie = val; + break; case TCP_TIMESTAMP: if (!tp->repair) err = -EPERM; @@ -3256,6 +3264,10 @@ static int do_tcp_getsockopt(struct sock *sk, int level, val = tp->fastopen_connect; break; + case TCP_FASTOPEN_NO_COOKIE: + val = tp->fastopen_no_cookie; + break; + case TCP_TIMESTAMP: val = tcp_time_stamp_raw() + tp->tsoffset; break; diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index 21075ce19cb6..e0a4b56644aa 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -310,13 +310,23 @@ static bool tcp_fastopen_queue_check(struct sock *sk) return true; } +static bool tcp_fastopen_no_cookie(const struct sock *sk, + const struct dst_entry *dst, + int flag) +{ + return (sock_net(sk)->ipv4.sysctl_tcp_fastopen & flag) || + tcp_sk(sk)->fastopen_no_cookie || + (dst && dst_metric(dst, RTAX_FASTOPEN_NO_COOKIE)); +} + /* Returns true if we should perform Fast Open on the SYN. The cookie (foc) * may be updated and return the client in the SYN-ACK later. E.g., Fast Open * cookie request (foc->len == 0). */ struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, struct request_sock *req, - struct tcp_fastopen_cookie *foc) + struct tcp_fastopen_cookie *foc, + const struct dst_entry *dst) { bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1; int tcp_fastopen = sock_net(sk)->ipv4.sysctl_tcp_fastopen; @@ -333,7 +343,8 @@ struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, return NULL; } - if (syn_data && (tcp_fastopen & TFO_SERVER_COOKIE_NOT_REQD)) + if (syn_data && + tcp_fastopen_no_cookie(sk, dst, TFO_SERVER_COOKIE_NOT_REQD)) goto fastopen; if (foc->len >= 0 && /* Client presents or requests a cookie */ @@ -370,6 +381,7 @@ bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss, struct tcp_fastopen_cookie *cookie) { unsigned long last_syn_loss = 0; + const struct dst_entry *dst; int syn_loss = 0; tcp_fastopen_cache_get(sk, mss, cookie, &syn_loss, &last_syn_loss); @@ -387,7 +399,9 @@ bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss, return false; } - if (sock_net(sk)->ipv4.sysctl_tcp_fastopen & TFO_CLIENT_NO_COOKIE) { + dst = __sk_dst_get(sk); + + if (tcp_fastopen_no_cookie(sk, dst, TFO_CLIENT_NO_COOKIE)) { cookie->len = -1; return true; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index c5e64d4b5839..893286db4623 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6332,7 +6332,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, tcp_openreq_init_rwin(req, sk, dst); if (!want_cookie) { tcp_reqsk_record_syn(sk, req, skb); - fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc); + fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst); } if (fastopen_sk) { af_ops->send_synack(fastopen_sk, dst, &fl, req, -- cgit v1.2.3-59-g8ed1b From 08395c7f4d9f5808b5754a0dbed969f378bde0c3 Mon Sep 17 00:00:00 2001 From: Julien Thierry Date: Fri, 13 Oct 2017 12:26:44 +0100 Subject: irqdesc: Add function to identify percpu_devid irqs irq_is_percpu indicates whether an irq should only target a single cpu. PERCPU_DEVID flag indicates that an irq can be configured differently on each cpu it can target. Provide a function to check whether an irq is PERCPU_DEVID. Reviewed-by: Thomas Gleixner Reviewed-by: Marc Zyngier Signed-off-by: Will Deacon Signed-off-by: Julien Thierry --- include/linux/irqdesc.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include') diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h index 3e90a094798d..93960cf36e23 100644 --- a/include/linux/irqdesc.h +++ b/include/linux/irqdesc.h @@ -244,6 +244,14 @@ static inline int irq_is_percpu(unsigned int irq) return desc->status_use_accessors & IRQ_PER_CPU; } +static inline int irq_is_percpu_devid(unsigned int irq) +{ + struct irq_desc *desc; + + desc = irq_to_desc(irq); + return desc->status_use_accessors & IRQ_PER_CPU_DEVID; +} + static inline void irq_set_lockdep_class(unsigned int irq, struct lock_class_key *class) { -- cgit v1.2.3-59-g8ed1b From c4f3db15958277c03d1c324894255ea3ecbf86e1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 11 Oct 2017 10:47:40 +0200 Subject: netfilter: conntrack: add and use nf_l4proto_log_invalid We currently pass down the l4 protocol to the conntrack ->packet() function, but the only user of this is the debug info decision. Same information can be derived from struct nf_conn. As a first step, add and use a new log function for this, similar to nf_ct_helper_log(). Add __cold annotation -- invalid packets should be infrequent so gcc can consider all call paths that lead to such a function as unlikely. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 10 +++++++ net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 18 ++++++------ net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 14 +++++---- net/netfilter/nf_conntrack_proto.c | 24 ++++++++++++++++ net/netfilter/nf_conntrack_proto_dccp.c | 3 +- net/netfilter/nf_conntrack_proto_sctp.c | 3 +- net/netfilter/nf_conntrack_proto_tcp.c | 22 +++++++------- net/netfilter/nf_conntrack_proto_udp.c | 40 ++++++++++++-------------- 8 files changed, 82 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 738a0307a96b..6d79a061d360 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -152,8 +152,18 @@ extern const struct nla_policy nf_ct_port_nla_policy[]; #define LOG_INVALID(net, proto) \ ((net)->ct.sysctl_log_invalid == (proto) || \ (net)->ct.sysctl_log_invalid == IPPROTO_RAW) + +__printf(5, 6) __cold +void nf_l4proto_log_invalid(const struct sk_buff *skb, + struct net *net, + u16 pf, u8 protonum, + const char *fmt, ...); #else static inline int LOG_INVALID(struct net *net, int proto) { return 0; } + +static inline __printf(5, 6) __cold +void nf_l4proto_log_invalid(const struct sk_buff *skb, struct net *net, + u16 pf, u8 protonum, const char *fmt, ...) {} #endif /* CONFIG_SYSCTL */ #endif /*_NF_CONNTRACK_PROTOCOL_H*/ diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index a046c298413a..7281a7b77a0e 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -165,6 +165,12 @@ icmp_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, return NF_ACCEPT; } +static void icmp_error_log(const struct sk_buff *skb, struct net *net, + u8 pf, const char *msg) +{ + nf_l4proto_log_invalid(skb, net, pf, IPPROTO_ICMP, "%s", msg); +} + /* Small and modified version of icmp_rcv */ static int icmp_error(struct net *net, struct nf_conn *tmpl, @@ -177,18 +183,14 @@ icmp_error(struct net *net, struct nf_conn *tmpl, /* Not enough header? */ icmph = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_ih), &_ih); if (icmph == NULL) { - if (LOG_INVALID(net, IPPROTO_ICMP)) - nf_log_packet(net, PF_INET, 0, skb, NULL, NULL, - NULL, "nf_ct_icmp: short packet "); + icmp_error_log(skb, net, pf, "short packet"); return -NF_ACCEPT; } /* See ip_conntrack_proto_tcp.c */ if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && nf_ip_checksum(skb, hooknum, dataoff, 0)) { - if (LOG_INVALID(net, IPPROTO_ICMP)) - nf_log_packet(net, PF_INET, 0, skb, NULL, NULL, NULL, - "nf_ct_icmp: bad HW ICMP checksum "); + icmp_error_log(skb, net, pf, "bad hw icmp checksum"); return -NF_ACCEPT; } @@ -199,9 +201,7 @@ icmp_error(struct net *net, struct nf_conn *tmpl, * discarded. */ if (icmph->type > NR_ICMP_TYPES) { - if (LOG_INVALID(net, IPPROTO_ICMP)) - nf_log_packet(net, PF_INET, 0, skb, NULL, NULL, NULL, - "nf_ct_icmp: invalid ICMP type "); + icmp_error_log(skb, net, pf, "invalid icmp type"); return -NF_ACCEPT; } diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index a9e1fd1a8536..0f227ca4a5a2 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -176,6 +176,12 @@ icmpv6_error_message(struct net *net, struct nf_conn *tmpl, return NF_ACCEPT; } +static void icmpv6_error_log(const struct sk_buff *skb, struct net *net, + u8 pf, const char *msg) +{ + nf_l4proto_log_invalid(skb, net, pf, IPPROTO_ICMPV6, "%s", msg); +} + static int icmpv6_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int dataoff, @@ -187,17 +193,13 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl, icmp6h = skb_header_pointer(skb, dataoff, sizeof(_ih), &_ih); if (icmp6h == NULL) { - if (LOG_INVALID(net, IPPROTO_ICMPV6)) - nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL, - "nf_ct_icmpv6: short packet "); + icmpv6_error_log(skb, net, pf, "short packet"); return -NF_ACCEPT; } if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && nf_ip6_checksum(skb, hooknum, dataoff, IPPROTO_ICMPV6)) { - if (LOG_INVALID(net, IPPROTO_ICMPV6)) - nf_log_packet(net, PF_INET6, 0, skb, NULL, NULL, NULL, - "nf_ct_icmpv6: ICMPv6 checksum failed "); + icmpv6_error_log(skb, net, pf, "ICMPv6 checksum failed"); return -NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index b3e489c859ec..bcd3ee270d75 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -27,6 +27,7 @@ #include #include #include +#include static struct nf_conntrack_l4proto __rcu **nf_ct_protos[NFPROTO_NUMPROTO] __read_mostly; struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[NFPROTO_NUMPROTO] __read_mostly; @@ -63,6 +64,29 @@ nf_ct_unregister_sysctl(struct ctl_table_header **header, *header = NULL; *table = NULL; } + +__printf(5, 6) +void nf_l4proto_log_invalid(const struct sk_buff *skb, + struct net *net, + u16 pf, u8 protonum, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (net->ct.sysctl_log_invalid != protonum || + net->ct.sysctl_log_invalid != IPPROTO_RAW) + return; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, + "nf_ct_proto_%d: %pV ", protonum, &vaf); + va_end(args); +} +EXPORT_SYMBOL_GPL(nf_l4proto_log_invalid); #endif const struct nf_conntrack_l4proto * diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 0f5a4d79f6b8..ef501c7edb96 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -604,8 +604,7 @@ static int dccp_error(struct net *net, struct nf_conn *tmpl, return NF_ACCEPT; out_invalid: - if (LOG_INVALID(net, IPPROTO_DCCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, "%s", msg); + nf_l4proto_log_invalid(skb, net, pf, IPPROTO_DCCP, "%s", msg); return -NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 6303a88af12b..aa630c561361 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -522,8 +522,7 @@ static int sctp_error(struct net *net, struct nf_conn *tpl, struct sk_buff *skb, } return NF_ACCEPT; out_invalid: - if (LOG_INVALID(net, IPPROTO_SCTP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, "%s", logmsg); + nf_l4proto_log_invalid(skb, net, pf, IPPROTO_SCTP, "%s", logmsg); return -NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index cba1c6ffe51a..14198b2a2e2c 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -738,6 +738,12 @@ static const u8 tcp_valid_flags[(TCPHDR_FIN|TCPHDR_SYN|TCPHDR_RST|TCPHDR_ACK| [TCPHDR_ACK|TCPHDR_URG] = 1, }; +static void tcp_error_log(const struct sk_buff *skb, struct net *net, + u8 pf, const char *msg) +{ + nf_l4proto_log_invalid(skb, net, pf, IPPROTO_TCP, "%s", msg); +} + /* Protect conntrack agaist broken packets. Code taken from ipt_unclean.c. */ static int tcp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, @@ -753,17 +759,13 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl, /* Smaller that minimal TCP header? */ th = skb_header_pointer(skb, dataoff, sizeof(_tcph), &_tcph); if (th == NULL) { - if (LOG_INVALID(net, IPPROTO_TCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_tcp: short packet "); + tcp_error_log(skb, net, pf, "short packet"); return -NF_ACCEPT; } /* Not whole TCP header or malformed packet */ if (th->doff*4 < sizeof(struct tcphdr) || tcplen < th->doff*4) { - if (LOG_INVALID(net, IPPROTO_TCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_tcp: truncated/malformed packet "); + tcp_error_log(skb, net, pf, "truncated packet"); return -NF_ACCEPT; } @@ -774,18 +776,14 @@ static int tcp_error(struct net *net, struct nf_conn *tmpl, /* FIXME: Source route IP option packets --RR */ if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && nf_checksum(skb, hooknum, dataoff, IPPROTO_TCP, pf)) { - if (LOG_INVALID(net, IPPROTO_TCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_tcp: bad TCP checksum "); + tcp_error_log(skb, net, pf, "bad checksum"); return -NF_ACCEPT; } /* Check TCP flags. */ tcpflags = (tcp_flag_byte(th) & ~(TCPHDR_ECE|TCPHDR_CWR|TCPHDR_PSH)); if (!tcp_valid_flags[tcpflags]) { - if (LOG_INVALID(net, IPPROTO_TCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_tcp: invalid TCP flag combination "); + tcp_error_log(skb, net, pf, "invalid tcp flag combination"); return -NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index 8af734cd1a94..fc20cf430251 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -99,6 +99,12 @@ static bool udp_new(struct nf_conn *ct, const struct sk_buff *skb, } #ifdef CONFIG_NF_CT_PROTO_UDPLITE +static void udplite_error_log(const struct sk_buff *skb, struct net *net, + u8 pf, const char *msg) +{ + nf_l4proto_log_invalid(skb, net, pf, IPPROTO_UDPLITE, "%s", msg); +} + static int udplite_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int dataoff, @@ -112,9 +118,7 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl, /* Header is too small? */ hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); if (!hdr) { - if (LOG_INVALID(net, IPPROTO_UDPLITE)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_udplite: short packet "); + udplite_error_log(skb, net, pf, "short packet"); return -NF_ACCEPT; } @@ -122,17 +126,13 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl, if (cscov == 0) { cscov = udplen; } else if (cscov < sizeof(*hdr) || cscov > udplen) { - if (LOG_INVALID(net, IPPROTO_UDPLITE)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_udplite: invalid checksum coverage "); + udplite_error_log(skb, net, pf, "invalid checksum coverage"); return -NF_ACCEPT; } /* UDPLITE mandates checksums */ if (!hdr->check) { - if (LOG_INVALID(net, IPPROTO_UDPLITE)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_udplite: checksum missing "); + udplite_error_log(skb, net, pf, "checksum missing"); return -NF_ACCEPT; } @@ -140,9 +140,7 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl, if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && nf_checksum_partial(skb, hooknum, dataoff, cscov, IPPROTO_UDP, pf)) { - if (LOG_INVALID(net, IPPROTO_UDPLITE)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_udplite: bad UDPLite checksum "); + udplite_error_log(skb, net, pf, "bad checksum"); return -NF_ACCEPT; } @@ -150,6 +148,12 @@ static int udplite_error(struct net *net, struct nf_conn *tmpl, } #endif +static void udp_error_log(const struct sk_buff *skb, struct net *net, + u8 pf, const char *msg) +{ + nf_l4proto_log_invalid(skb, net, pf, IPPROTO_UDP, "%s", msg); +} + static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, unsigned int dataoff, u_int8_t pf, @@ -162,17 +166,13 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, /* Header is too small? */ hdr = skb_header_pointer(skb, dataoff, sizeof(_hdr), &_hdr); if (hdr == NULL) { - if (LOG_INVALID(net, IPPROTO_UDP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_udp: short packet "); + udp_error_log(skb, net, pf, "short packet"); return -NF_ACCEPT; } /* Truncated/malformed packets */ if (ntohs(hdr->len) > udplen || ntohs(hdr->len) < sizeof(*hdr)) { - if (LOG_INVALID(net, IPPROTO_UDP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_udp: truncated/malformed packet "); + udp_error_log(skb, net, pf, "truncated/malformed packet"); return -NF_ACCEPT; } @@ -186,9 +186,7 @@ static int udp_error(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, * FIXME: Source route IP option packets --RR */ if (net->ct.sysctl_checksum && hooknum == NF_INET_PRE_ROUTING && nf_checksum(skb, hooknum, dataoff, IPPROTO_UDP, pf)) { - if (LOG_INVALID(net, IPPROTO_UDP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_udp: bad UDP checksum "); + udp_error_log(skb, net, pf, "bad checksum"); return -NF_ACCEPT; } -- cgit v1.2.3-59-g8ed1b From 3d0b527bc9dc0e8c4428eb1a98d4cd27bd1114c7 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 11 Oct 2017 10:47:41 +0200 Subject: netfilter: conntrack: add and use nf_ct_l4proto_log_invalid We currently pass down the l4 protocol to the conntrack ->packet() function, but the only user of this is the debug info decision. Same information can be derived from struct nf_conn. Add a wrapper for the previous patch that extracs the information from nf_conn and passes it to nf_l4proto_log_invalid(). Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 14 ++++++++------ net/netfilter/nf_conntrack_proto.c | 23 +++++++++++++++++++++++ net/netfilter/nf_conntrack_proto_dccp.c | 17 +++++------------ net/netfilter/nf_conntrack_proto_tcp.c | 25 +++++++++---------------- 4 files changed, 45 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 6d79a061d360..5d51255b5bfb 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -149,21 +149,23 @@ int nf_ct_port_nlattr_tuple_size(void); extern const struct nla_policy nf_ct_port_nla_policy[]; #ifdef CONFIG_SYSCTL -#define LOG_INVALID(net, proto) \ - ((net)->ct.sysctl_log_invalid == (proto) || \ - (net)->ct.sysctl_log_invalid == IPPROTO_RAW) - +__printf(3, 4) __cold +void nf_ct_l4proto_log_invalid(const struct sk_buff *skb, + const struct nf_conn *ct, + const char *fmt, ...); __printf(5, 6) __cold void nf_l4proto_log_invalid(const struct sk_buff *skb, struct net *net, u16 pf, u8 protonum, const char *fmt, ...); #else -static inline int LOG_INVALID(struct net *net, int proto) { return 0; } - static inline __printf(5, 6) __cold void nf_l4proto_log_invalid(const struct sk_buff *skb, struct net *net, u16 pf, u8 protonum, const char *fmt, ...) {} +static inline __printf(3, 4) __cold +void nf_ct_l4proto_log_invalid(const struct sk_buff *skb, + const struct nf_conn *ct, + const char *fmt, ...) { } #endif /* CONFIG_SYSCTL */ #endif /*_NF_CONNTRACK_PROTOCOL_H*/ diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index bcd3ee270d75..83f739e9dc08 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -87,6 +87,29 @@ void nf_l4proto_log_invalid(const struct sk_buff *skb, va_end(args); } EXPORT_SYMBOL_GPL(nf_l4proto_log_invalid); + +__printf(3, 4) +void nf_ct_l4proto_log_invalid(const struct sk_buff *skb, + const struct nf_conn *ct, + const char *fmt, ...) +{ + struct va_format vaf; + struct net *net; + va_list args; + + net = nf_ct_net(ct); + if (likely(net->ct.sysctl_log_invalid == 0)) + return; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + + nf_l4proto_log_invalid(skb, net, nf_ct_l3num(ct), + nf_ct_protonum(ct), "%pV", &vaf); + va_end(args); +} +EXPORT_SYMBOL_GPL(nf_ct_l4proto_log_invalid); #endif const struct nf_conntrack_l4proto * diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index ef501c7edb96..49e0abcdc6f4 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -428,13 +428,13 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb, default: dn = dccp_pernet(net); if (dn->dccp_loose == 0) { - msg = "nf_ct_dccp: not picking up existing connection "; + msg = "not picking up existing connection "; goto out_invalid; } case CT_DCCP_REQUEST: break; case CT_DCCP_INVALID: - msg = "nf_ct_dccp: invalid state transition "; + msg = "invalid state transition "; goto out_invalid; } @@ -447,9 +447,7 @@ static bool dccp_new(struct nf_conn *ct, const struct sk_buff *skb, return true; out_invalid: - if (LOG_INVALID(net, IPPROTO_DCCP)) - nf_log_packet(net, nf_ct_l3num(ct), 0, skb, NULL, NULL, - NULL, "%s", msg); + nf_ct_l4proto_log_invalid(skb, ct, "%s", msg); return false; } @@ -472,7 +470,6 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, u_int8_t pf, unsigned int *timeouts) { - struct net *net = nf_ct_net(ct); enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); struct dccp_hdr _dh, *dh; u_int8_t type, old_state, new_state; @@ -534,15 +531,11 @@ static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, ct->proto.dccp.last_pkt = type; spin_unlock_bh(&ct->lock); - if (LOG_INVALID(net, IPPROTO_DCCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_dccp: invalid packet ignored "); + nf_ct_l4proto_log_invalid(skb, ct, "%s", "invalid packet"); return NF_ACCEPT; case CT_DCCP_INVALID: spin_unlock_bh(&ct->lock); - if (LOG_INVALID(net, IPPROTO_DCCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_dccp: invalid state transition "); + nf_ct_l4proto_log_invalid(skb, ct, "%s", "invalid state transition"); return -NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 14198b2a2e2c..dced574f6006 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -702,9 +702,9 @@ static bool tcp_in_window(const struct nf_conn *ct, if (sender->flags & IP_CT_TCP_FLAG_BE_LIBERAL || tn->tcp_be_liberal) res = true; - if (!res && LOG_INVALID(net, IPPROTO_TCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_tcp: %s ", + if (!res) { + nf_ct_l4proto_log_invalid(skb, ct, + "%s", before(seq, sender->td_maxend + 1) ? in_recv_win ? before(sack, receiver->td_end + 1) ? @@ -713,6 +713,7 @@ static bool tcp_in_window(const struct nf_conn *ct, : "ACK is over the upper bound (ACKed data not seen yet)" : "SEQ is under the lower bound (already ACKed data retransmitted)" : "SEQ is over the upper bound (over the window of the receiver)"); + } } pr_debug("tcp_in_window: res=%u sender end=%u maxend=%u maxwin=%u " @@ -937,10 +938,8 @@ static int tcp_packet(struct nf_conn *ct, IP_CT_EXP_CHALLENGE_ACK; } spin_unlock_bh(&ct->lock); - if (LOG_INVALID(net, IPPROTO_TCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_tcp: invalid packet ignored in " - "state %s ", tcp_conntrack_names[old_state]); + nf_ct_l4proto_log_invalid(skb, ct, "invalid packet ignored in " + "state %s ", tcp_conntrack_names[old_state]); return NF_ACCEPT; case TCP_CONNTRACK_MAX: /* Special case for SYN proxy: when the SYN to the server or @@ -962,9 +961,7 @@ static int tcp_packet(struct nf_conn *ct, pr_debug("nf_ct_tcp: Invalid dir=%i index=%u ostate=%u\n", dir, get_conntrack_index(th), old_state); spin_unlock_bh(&ct->lock); - if (LOG_INVALID(net, IPPROTO_TCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_tcp: invalid state "); + nf_ct_l4proto_log_invalid(skb, ct, "invalid state"); return -NF_ACCEPT; case TCP_CONNTRACK_TIME_WAIT: /* RFC5961 compliance cause stack to send "challenge-ACK" @@ -979,9 +976,7 @@ static int tcp_packet(struct nf_conn *ct, /* Detected RFC5961 challenge ACK */ ct->proto.tcp.last_flags &= ~IP_CT_EXP_CHALLENGE_ACK; spin_unlock_bh(&ct->lock); - if (LOG_INVALID(net, IPPROTO_TCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, NULL, - "nf_ct_tcp: challenge-ACK ignored "); + nf_ct_l4proto_log_invalid(skb, ct, "challenge-ack ignored"); return NF_ACCEPT; /* Don't change state */ } break; @@ -991,9 +986,7 @@ static int tcp_packet(struct nf_conn *ct, && before(ntohl(th->seq), ct->proto.tcp.seen[!dir].td_maxack)) { /* Invalid RST */ spin_unlock_bh(&ct->lock); - if (LOG_INVALID(net, IPPROTO_TCP)) - nf_log_packet(net, pf, 0, skb, NULL, NULL, - NULL, "nf_ct_tcp: invalid RST "); + nf_ct_l4proto_log_invalid(skb, ct, "invalid rst"); return -NF_ACCEPT; } if (index == TCP_RST_SET -- cgit v1.2.3-59-g8ed1b From eb6fad5a4a328b85d3faa8b301b522e3f316b49d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 11 Oct 2017 10:47:42 +0200 Subject: netfilter: conntrack: remove pf argument from l4 packet functions not needed/used anymore. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 1 - net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 1 - net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 1 - net/netfilter/nf_conntrack_core.c | 2 +- net/netfilter/nf_conntrack_proto_dccp.c | 1 - net/netfilter/nf_conntrack_proto_generic.c | 1 - net/netfilter/nf_conntrack_proto_gre.c | 1 - net/netfilter/nf_conntrack_proto_sctp.c | 1 - net/netfilter/nf_conntrack_proto_tcp.c | 6 ++---- net/netfilter/nf_conntrack_proto_udp.c | 1 - 10 files changed, 3 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 5d51255b5bfb..e06518874144 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -42,7 +42,6 @@ struct nf_conntrack_l4proto { const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int *timeouts); /* Called when a new connection for this protocol found; diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 7281a7b77a0e..8969420cecc3 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -81,7 +81,6 @@ static int icmp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int *timeout) { /* Do not immediately delete the connection after the first diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 0f227ca4a5a2..dca921df28e1 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -94,7 +94,6 @@ static int icmpv6_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int *timeout) { /* Do not immediately delete the connection after the first diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 01130392b7c0..28e675150853 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1419,7 +1419,7 @@ repeat: /* Decide what timeout policy we want to apply to this flow. */ timeouts = nf_ct_timeout_lookup(net, ct, l4proto); - ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, timeouts); + ret = l4proto->packet(ct, skb, dataoff, ctinfo, timeouts); if (ret <= 0) { /* Invalid: inverse of the return code tells * the netfilter core what to do */ diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 49e0abcdc6f4..2a446f4a554c 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -467,7 +467,6 @@ static unsigned int *dccp_get_timeouts(struct net *net) static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int *timeouts) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index 9cd40700842e..1f86ddf6649a 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -60,7 +60,6 @@ static int generic_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int *timeout) { nf_ct_refresh_acct(ct, ctinfo, skb, *timeout); diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 09a90484c27d..a2503005d80b 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -244,7 +244,6 @@ static int gre_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int *timeouts) { /* If we've seen traffic both ways, this is a GRE connection. diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index aa630c561361..80faf04ddf15 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -306,7 +306,6 @@ static int sctp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int *timeouts) { enum sctp_conntrack new_state, old_state; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index dced574f6006..8f283294d70f 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -493,8 +493,7 @@ static bool tcp_in_window(const struct nf_conn *ct, unsigned int index, const struct sk_buff *skb, unsigned int dataoff, - const struct tcphdr *tcph, - u_int8_t pf) + const struct tcphdr *tcph) { struct net *net = nf_ct_net(ct); struct nf_tcp_net *tn = tcp_pernet(net); @@ -801,7 +800,6 @@ static int tcp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int *timeouts) { struct net *net = nf_ct_net(ct); @@ -1013,7 +1011,7 @@ static int tcp_packet(struct nf_conn *ct, } if (!tcp_in_window(ct, &ct->proto.tcp, dir, index, - skb, dataoff, th, pf)) { + skb, dataoff, th)) { spin_unlock_bh(&ct->lock); return -NF_ACCEPT; } diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index fc20cf430251..3a5f727103af 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -73,7 +73,6 @@ static int udp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int *timeouts) { /* If we've seen traffic both ways, this is some kind of UDP -- cgit v1.2.3-59-g8ed1b From 28efb0046512e8a13ed9f9bdf0d68d10bbfbe9cf Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 12 Oct 2017 09:38:30 +0200 Subject: netfilter: conntrack: make l3proto trackers const previous patches removed all writes to them. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/ipv4/nf_conntrack_ipv4.h | 2 +- include/net/netfilter/ipv6/nf_conntrack_ipv6.h | 2 +- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 2 +- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h index 919e4e8af327..5534ecca7a5d 100644 --- a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h +++ b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h @@ -10,7 +10,7 @@ #define _NF_CONNTRACK_IPV4_H -extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4; +const extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4; diff --git a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h index eaea968f8657..30dc57980866 100644 --- a/include/net/netfilter/ipv6/nf_conntrack_ipv6.h +++ b/include/net/netfilter/ipv6/nf_conntrack_ipv6.h @@ -1,7 +1,7 @@ #ifndef _NF_CONNTRACK_IPV6_H #define _NF_CONNTRACK_IPV6_H -extern struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6; +extern const struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6; diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index fe374da4bc13..89af9d88ca21 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -344,7 +344,7 @@ static void ipv4_hooks_unregister(struct net *net) mutex_unlock(®ister_ipv4_hooks); } -struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { +const struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 = { .l3proto = PF_INET, .pkt_to_tuple = ipv4_pkt_to_tuple, .invert_tuple = ipv4_invert_tuple, diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index fe01dc953c56..3b80a38f62b8 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -339,7 +339,7 @@ static void ipv6_hooks_unregister(struct net *net) mutex_unlock(®ister_ipv6_hooks); } -struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { +const struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 = { .l3proto = PF_INET6, .pkt_to_tuple = ipv6_pkt_to_tuple, .invert_tuple = ipv6_invert_tuple, -- cgit v1.2.3-59-g8ed1b From 57e69e2f06e8fe4949c54e438c9faae0731e92f8 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 24 Oct 2017 08:35:09 -0700 Subject: ALSA: wavefront: Convert timers to use timer_setup() In preparation for unconditionally passing the struct timer_list pointer to all timer callbacks, switch to using the new timer_setup() and from_timer() to pass the timer pointer explicitly. Signed-off-by: Kees Cook Signed-off-by: Takashi Iwai --- include/sound/snd_wavefront.h | 1 + sound/isa/wavefront/wavefront_midi.c | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/sound/snd_wavefront.h b/include/sound/snd_wavefront.h index cd0bab1ef6f1..fcd770fdcda5 100644 --- a/include/sound/snd_wavefront.h +++ b/include/sound/snd_wavefront.h @@ -28,6 +28,7 @@ struct _snd_wavefront_midi { struct snd_rawmidi_substream *substream_output[2]; struct snd_rawmidi_substream *substream_input[2]; struct timer_list timer; + snd_wavefront_card_t *timer_card; spinlock_t open; spinlock_t virtual; /* protects isvirtual */ }; diff --git a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c index 2aa05f3aaa38..556b14738970 100644 --- a/sound/isa/wavefront/wavefront_midi.c +++ b/sound/isa/wavefront/wavefront_midi.c @@ -349,10 +349,10 @@ static void snd_wavefront_midi_input_trigger(struct snd_rawmidi_substream *subst spin_unlock_irqrestore (&midi->virtual, flags); } -static void snd_wavefront_midi_output_timer(unsigned long data) +static void snd_wavefront_midi_output_timer(struct timer_list *t) { - snd_wavefront_card_t *card = (snd_wavefront_card_t *)data; - snd_wavefront_midi_t *midi = &card->wavefront.midi; + snd_wavefront_midi_t *midi = from_timer(midi, t, timer); + snd_wavefront_card_t *card = midi->timer_card; unsigned long flags; spin_lock_irqsave (&midi->virtual, flags); @@ -383,9 +383,9 @@ static void snd_wavefront_midi_output_trigger(struct snd_rawmidi_substream *subs if (up) { if ((midi->mode[mpu] & MPU401_MODE_OUTPUT_TRIGGER) == 0) { if (!midi->istimer) { - setup_timer(&midi->timer, + timer_setup(&midi->timer, snd_wavefront_midi_output_timer, - (unsigned long) substream->rmidi->card->private_data); + 0); mod_timer(&midi->timer, 1 + jiffies); } midi->istimer++; -- cgit v1.2.3-59-g8ed1b From 276b738deb5bf856b9f6049fcd92a967f52643d7 Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 24 Oct 2017 14:40:20 -0500 Subject: PCI: Add resizable BAR infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add resizable BAR infrastructure, including defines and helper functions to read the possible sizes of a BAR and update its size. See PCIe r3.1, sec 7.22. Link: https://pcisig.com/sites/default/files/specification_documents/ECN_Resizable-BAR_24Apr2008.pdf Signed-off-by: Christian König [bhelgaas: rename to functions with "rebar" (to match #defines), drop shift #defines, drop "_MASK" suffixes, fix typos, fix kerneldoc] Signed-off-by: Bjorn Helgaas Reviewed-by: Andy Shevchenko --- drivers/pci/pci.c | 101 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 8 ++++ include/uapi/linux/pci_regs.h | 8 +++- 3 files changed, 115 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6078dfc11b11..832b96756e83 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2965,6 +2965,107 @@ bool pci_acs_path_enabled(struct pci_dev *start, return true; } +/** + * pci_rebar_find_pos - find position of resize ctrl reg for BAR + * @pdev: PCI device + * @bar: BAR to find + * + * Helper to find the position of the ctrl register for a BAR. + * Returns -ENOTSUPP if resizable BARs are not supported at all. + * Returns -ENOENT if no ctrl register for the BAR could be found. + */ +static int pci_rebar_find_pos(struct pci_dev *pdev, int bar) +{ + unsigned int pos, nbars, i; + u32 ctrl; + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_REBAR); + if (!pos) + return -ENOTSUPP; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + nbars = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >> + PCI_REBAR_CTRL_NBAR_SHIFT; + + for (i = 0; i < nbars; i++, pos += 8) { + int bar_idx; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + bar_idx = ctrl & PCI_REBAR_CTRL_BAR_IDX; + if (bar_idx == bar) + return pos; + } + + return -ENOENT; +} + +/** + * pci_rebar_get_possible_sizes - get possible sizes for BAR + * @pdev: PCI device + * @bar: BAR to query + * + * Get the possible sizes of a resizable BAR as bitmask defined in the spec + * (bit 0=1MB, bit 19=512GB). Returns 0 if BAR isn't resizable. + */ +u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar) +{ + int pos; + u32 cap; + + pos = pci_rebar_find_pos(pdev, bar); + if (pos < 0) + return 0; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CAP, &cap); + return (cap & PCI_REBAR_CAP_SIZES) >> 4; +} + +/** + * pci_rebar_get_current_size - get the current size of a BAR + * @pdev: PCI device + * @bar: BAR to set size to + * + * Read the size of a BAR from the resizable BAR config. + * Returns size if found or negative error code. + */ +int pci_rebar_get_current_size(struct pci_dev *pdev, int bar) +{ + int pos; + u32 ctrl; + + pos = pci_rebar_find_pos(pdev, bar); + if (pos < 0) + return pos; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + return (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> 8; +} + +/** + * pci_rebar_set_size - set a new size for a BAR + * @pdev: PCI device + * @bar: BAR to set size to + * @size: new size as defined in the spec (0=1MB, 19=512GB) + * + * Set the new size of a BAR as defined in the spec. + * Returns zero if resizing was successful, error code otherwise. + */ +int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size) +{ + int pos; + u32 ctrl; + + pos = pci_rebar_find_pos(pdev, bar); + if (pos < 0) + return pos; + + pci_read_config_dword(pdev, pos + PCI_REBAR_CTRL, &ctrl); + ctrl &= ~PCI_REBAR_CTRL_BAR_SIZE; + ctrl |= size << 8; + pci_write_config_dword(pdev, pos + PCI_REBAR_CTRL, ctrl); + return 0; +} + /** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index a6560c9baa52..33469a33738d 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -366,4 +366,12 @@ int acpi_get_rc_resources(struct device *dev, const char *hid, u16 segment, struct resource *res); #endif +u32 pci_rebar_get_possible_sizes(struct pci_dev *pdev, int bar); +int pci_rebar_get_current_size(struct pci_dev *pdev, int bar); +int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size); +static inline u64 pci_rebar_size_to_bytes(int size) +{ + return 1ULL << (size + 20); +} + #endif /* DRIVERS_PCI_H */ diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index f8d58045926f..d34000a59f24 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -939,9 +939,13 @@ #define PCI_SATA_SIZEOF_LONG 16 /* Resizable BARs */ +#define PCI_REBAR_CAP 4 /* capability register */ +#define PCI_REBAR_CAP_SIZES 0x00FFFFF0 /* supported BAR sizes */ #define PCI_REBAR_CTRL 8 /* control register */ -#define PCI_REBAR_CTRL_NBAR_MASK (7 << 5) /* mask for # bars */ -#define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # bars */ +#define PCI_REBAR_CTRL_BAR_IDX 0x00000007 /* BAR index */ +#define PCI_REBAR_CTRL_NBAR_MASK 0x000000E0 /* # of resizable BARs */ +#define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # of BARs */ +#define PCI_REBAR_CTRL_BAR_SIZE 0x00001F00 /* BAR size */ /* Dynamic Power Allocation */ #define PCI_DPA_CAP 4 /* capability register */ -- cgit v1.2.3-59-g8ed1b From 908d140a87a794bf89717ceae54aba5ce86c52e4 Mon Sep 17 00:00:00 2001 From: Shmulik Ladkani Date: Sat, 21 Oct 2017 00:25:15 +0300 Subject: ip6_tunnel: Allow rcv/xmit even if remote address is a local address Currently, ip6_tnl_xmit_ctl drops tunneled packets if the remote address (outer v6 destination) is one of host's locally configured addresses. Same applies to ip6_tnl_rcv_ctl: it drops packets if the remote address (outer v6 source) is a local address. This prevents using ipxip6 (and ip6_gre) tunnels whose local/remote endpoints are on same host; OTOH v4 tunnels (ipip or gre) allow such configurations. An example where this proves useful is a system where entities are identified by their unique v6 addresses, and use tunnels to encapsulate traffic between them. The limitation prevents placing several entities on same host. Introduce IP6_TNL_F_ALLOW_LOCAL_REMOTE which allows to bypass this restriction. Signed-off-by: Shmulik Ladkani Signed-off-by: David S. Miller --- include/uapi/linux/ip6_tunnel.h | 2 ++ net/ipv6/ip6_tunnel.c | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/ip6_tunnel.h b/include/uapi/linux/ip6_tunnel.h index 425926c467d7..ffebbe365478 100644 --- a/include/uapi/linux/ip6_tunnel.h +++ b/include/uapi/linux/ip6_tunnel.h @@ -20,6 +20,8 @@ #define IP6_TNL_F_RCV_DSCP_COPY 0x10 /* copy fwmark from inner packet */ #define IP6_TNL_F_USE_ORIG_FWMARK 0x20 +/* allow remote endpoint on the local node */ +#define IP6_TNL_F_ALLOW_LOCAL_REMOTE 0x40 struct ip6_tnl_parm { char name[IFNAMSIZ]; /* name of tunnel device */ diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 4212879ff35e..439d65f7e094 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -770,7 +770,8 @@ int ip6_tnl_rcv_ctl(struct ip6_tnl *t, if ((ipv6_addr_is_multicast(laddr) || likely(ipv6_chk_addr(net, laddr, ldev, 0))) && - likely(!ipv6_chk_addr(net, raddr, NULL, 0))) + ((p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) || + likely(!ipv6_chk_addr(net, raddr, NULL, 0)))) ret = 1; } return ret; @@ -1000,7 +1001,8 @@ int ip6_tnl_xmit_ctl(struct ip6_tnl *t, if (unlikely(!ipv6_chk_addr(net, laddr, ldev, 0))) pr_warn("%s xmit: Local address not yet configured!\n", p->name); - else if (!ipv6_addr_is_multicast(raddr) && + else if (!(p->flags & IP6_TNL_F_ALLOW_LOCAL_REMOTE) && + !ipv6_addr_is_multicast(raddr) && unlikely(ipv6_chk_addr(net, raddr, NULL, 0))) pr_warn("%s xmit: Routing loop! Remote address found on this node!\n", p->name); -- cgit v1.2.3-59-g8ed1b From e87c6bc3852b981e71c757be20771546ce9f76f3 Mon Sep 17 00:00:00 2001 From: Yonghong Song Date: Mon, 23 Oct 2017 23:53:08 -0700 Subject: bpf: permit multiple bpf attachments for a single perf event This patch enables multiple bpf attachments for a kprobe/uprobe/tracepoint single trace event. Each trace_event keeps a list of attached perf events. When an event happens, all attached bpf programs will be executed based on the order of attachment. A global bpf_event_mutex lock is introduced to protect prog_array attaching and detaching. An alternative will be introduce a mutex lock in every trace_event_call structure, but it takes a lot of extra memory. So a global bpf_event_mutex lock is a good compromise. The bpf prog detachment involves allocation of memory. If the allocation fails, a dummy do-nothing program will replace to-be-detached program in-place. Signed-off-by: Yonghong Song Acked-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- include/linux/bpf.h | 30 +++++++++++++--- include/linux/trace_events.h | 43 ++++++++++++++++++++--- include/trace/perf.h | 6 ++-- kernel/bpf/core.c | 81 ++++++++++++++++++++++++++++++++++++++++++ kernel/events/core.c | 26 +++++--------- kernel/trace/bpf_trace.c | 82 ++++++++++++++++++++++++++++++++++++++++--- kernel/trace/trace_kprobe.c | 6 ++-- kernel/trace/trace_syscalls.c | 34 ++++++++++-------- kernel/trace/trace_uprobe.c | 3 +- 9 files changed, 255 insertions(+), 56 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 1e334b248ff6..172be7faf7ba 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -273,18 +273,38 @@ int bpf_prog_array_length(struct bpf_prog_array __rcu *progs); int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, __u32 __user *prog_ids, u32 cnt); -#define BPF_PROG_RUN_ARRAY(array, ctx, func) \ +void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs, + struct bpf_prog *old_prog); +int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array, + struct bpf_prog *exclude_prog, + struct bpf_prog *include_prog, + struct bpf_prog_array **new_array); + +#define __BPF_PROG_RUN_ARRAY(array, ctx, func, check_non_null) \ ({ \ - struct bpf_prog **_prog; \ + struct bpf_prog **_prog, *__prog; \ + struct bpf_prog_array *_array; \ u32 _ret = 1; \ rcu_read_lock(); \ - _prog = rcu_dereference(array)->progs; \ - for (; *_prog; _prog++) \ - _ret &= func(*_prog, ctx); \ + _array = rcu_dereference(array); \ + if (unlikely(check_non_null && !_array))\ + goto _out; \ + _prog = _array->progs; \ + while ((__prog = READ_ONCE(*_prog))) { \ + _ret &= func(__prog, ctx); \ + _prog++; \ + } \ +_out: \ rcu_read_unlock(); \ _ret; \ }) +#define BPF_PROG_RUN_ARRAY(array, ctx, func) \ + __BPF_PROG_RUN_ARRAY(array, ctx, func, false) + +#define BPF_PROG_RUN_ARRAY_CHECK(array, ctx, func) \ + __BPF_PROG_RUN_ARRAY(array, ctx, func, true) + #ifdef CONFIG_BPF_SYSCALL DECLARE_PER_CPU(int, bpf_prog_active); diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 2e0f22298fe9..fc6aeca945db 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -271,14 +271,37 @@ struct trace_event_call { #ifdef CONFIG_PERF_EVENTS int perf_refcount; struct hlist_head __percpu *perf_events; - struct bpf_prog *prog; - struct perf_event *bpf_prog_owner; + struct bpf_prog_array __rcu *prog_array; int (*perf_perm)(struct trace_event_call *, struct perf_event *); #endif }; +#ifdef CONFIG_PERF_EVENTS +static inline bool bpf_prog_array_valid(struct trace_event_call *call) +{ + /* + * This inline function checks whether call->prog_array + * is valid or not. The function is called in various places, + * outside rcu_read_lock/unlock, as a heuristic to speed up execution. + * + * If this function returns true, and later call->prog_array + * becomes false inside rcu_read_lock/unlock region, + * we bail out then. If this function return false, + * there is a risk that we might miss a few events if the checking + * were delayed until inside rcu_read_lock/unlock region and + * call->prog_array happened to become non-NULL then. + * + * Here, READ_ONCE() is used instead of rcu_access_pointer(). + * rcu_access_pointer() requires the actual definition of + * "struct bpf_prog_array" while READ_ONCE() only needs + * a declaration of the same type. + */ + return !!READ_ONCE(call->prog_array); +} +#endif + static inline const char * trace_event_name(struct trace_event_call *call) { @@ -435,12 +458,23 @@ trace_trigger_soft_disabled(struct trace_event_file *file) } #ifdef CONFIG_BPF_EVENTS -unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx); +unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx); +int perf_event_attach_bpf_prog(struct perf_event *event, struct bpf_prog *prog); +void perf_event_detach_bpf_prog(struct perf_event *event); #else -static inline unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx) +static inline unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx) { return 1; } + +static inline int +perf_event_attach_bpf_prog(struct perf_event *event, struct bpf_prog *prog) +{ + return -EOPNOTSUPP; +} + +static inline void perf_event_detach_bpf_prog(struct perf_event *event) { } + #endif enum { @@ -511,6 +545,7 @@ perf_trace_buf_submit(void *raw_data, int size, int rctx, u16 type, { perf_tp_event(type, count, raw_data, size, regs, head, rctx, task, event); } + #endif #endif /* _LINUX_TRACE_EVENT_H */ diff --git a/include/trace/perf.h b/include/trace/perf.h index 04fe68bbe767..14f127b6acf5 100644 --- a/include/trace/perf.h +++ b/include/trace/perf.h @@ -34,7 +34,6 @@ perf_trace_##call(void *__data, proto) \ struct trace_event_call *event_call = __data; \ struct trace_event_data_offsets_##call __maybe_unused __data_offsets;\ struct trace_event_raw_##call *entry; \ - struct bpf_prog *prog = event_call->prog; \ struct pt_regs *__regs; \ u64 __count = 1; \ struct task_struct *__task = NULL; \ @@ -46,8 +45,9 @@ perf_trace_##call(void *__data, proto) \ __data_size = trace_event_get_offsets_##call(&__data_offsets, args); \ \ head = this_cpu_ptr(event_call->perf_events); \ - if (!prog && __builtin_constant_p(!__task) && !__task && \ - hlist_empty(head)) \ + if (!bpf_prog_array_valid(event_call) && \ + __builtin_constant_p(!__task) && !__task && \ + hlist_empty(head)) \ return; \ \ __entry_size = ALIGN(__data_size + sizeof(*entry) + sizeof(u32),\ diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 8e7c8bf2b687..7fe448799d76 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1394,6 +1394,20 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) } EXPORT_SYMBOL_GPL(bpf_prog_select_runtime); +static unsigned int __bpf_prog_ret1(const void *ctx, + const struct bpf_insn *insn) +{ + return 1; +} + +static struct bpf_prog_dummy { + struct bpf_prog prog; +} dummy_bpf_prog = { + .prog = { + .bpf_func = __bpf_prog_ret1, + }, +}; + /* to avoid allocating empty bpf_prog_array for cgroups that * don't have bpf program attached use one global 'empty_prog_array' * It will not be modified the caller of bpf_prog_array_alloc() @@ -1463,6 +1477,73 @@ int bpf_prog_array_copy_to_user(struct bpf_prog_array __rcu *progs, return 0; } +void bpf_prog_array_delete_safe(struct bpf_prog_array __rcu *progs, + struct bpf_prog *old_prog) +{ + struct bpf_prog **prog = progs->progs; + + for (; *prog; prog++) + if (*prog == old_prog) { + WRITE_ONCE(*prog, &dummy_bpf_prog.prog); + break; + } +} + +int bpf_prog_array_copy(struct bpf_prog_array __rcu *old_array, + struct bpf_prog *exclude_prog, + struct bpf_prog *include_prog, + struct bpf_prog_array **new_array) +{ + int new_prog_cnt, carry_prog_cnt = 0; + struct bpf_prog **existing_prog; + struct bpf_prog_array *array; + int new_prog_idx = 0; + + /* Figure out how many existing progs we need to carry over to + * the new array. + */ + if (old_array) { + existing_prog = old_array->progs; + for (; *existing_prog; existing_prog++) { + if (*existing_prog != exclude_prog && + *existing_prog != &dummy_bpf_prog.prog) + carry_prog_cnt++; + if (*existing_prog == include_prog) + return -EEXIST; + } + } + + /* How many progs (not NULL) will be in the new array? */ + new_prog_cnt = carry_prog_cnt; + if (include_prog) + new_prog_cnt += 1; + + /* Do we have any prog (not NULL) in the new array? */ + if (!new_prog_cnt) { + *new_array = NULL; + return 0; + } + + /* +1 as the end of prog_array is marked with NULL */ + array = bpf_prog_array_alloc(new_prog_cnt + 1, GFP_KERNEL); + if (!array) + return -ENOMEM; + + /* Fill in the new prog array */ + if (carry_prog_cnt) { + existing_prog = old_array->progs; + for (; *existing_prog; existing_prog++) + if (*existing_prog != exclude_prog && + *existing_prog != &dummy_bpf_prog.prog) + array->progs[new_prog_idx++] = *existing_prog; + } + if (include_prog) + array->progs[new_prog_idx++] = include_prog; + array->progs[new_prog_idx] = NULL; + *new_array = array; + return 0; +} + static void bpf_prog_free_deferred(struct work_struct *work) { struct bpf_prog_aux *aux; diff --git a/kernel/events/core.c b/kernel/events/core.c index 9f78a6825bbe..9660ee65fbef 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7954,11 +7954,9 @@ void perf_trace_run_bpf_submit(void *raw_data, int size, int rctx, struct pt_regs *regs, struct hlist_head *head, struct task_struct *task) { - struct bpf_prog *prog = call->prog; - - if (prog) { + if (bpf_prog_array_valid(call)) { *(struct pt_regs **)raw_data = regs; - if (!trace_call_bpf(prog, raw_data) || hlist_empty(head)) { + if (!trace_call_bpf(call, raw_data) || hlist_empty(head)) { perf_swevent_put_recursion_context(rctx); return; } @@ -8147,13 +8145,11 @@ static int perf_event_set_bpf_prog(struct perf_event *event, u32 prog_fd) { bool is_kprobe, is_tracepoint, is_syscall_tp; struct bpf_prog *prog; + int ret; if (event->attr.type != PERF_TYPE_TRACEPOINT) return perf_event_set_bpf_handler(event, prog_fd); - if (event->tp_event->prog) - return -EEXIST; - is_kprobe = event->tp_event->flags & TRACE_EVENT_FL_UKPROBE; is_tracepoint = event->tp_event->flags & TRACE_EVENT_FL_TRACEPOINT; is_syscall_tp = is_syscall_trace_event(event->tp_event); @@ -8181,26 +8177,20 @@ static int perf_event_set_bpf_prog(struct perf_event *event, u32 prog_fd) return -EACCES; } } - event->tp_event->prog = prog; - event->tp_event->bpf_prog_owner = event; - return 0; + ret = perf_event_attach_bpf_prog(event, prog); + if (ret) + bpf_prog_put(prog); + return ret; } static void perf_event_free_bpf_prog(struct perf_event *event) { - struct bpf_prog *prog; - if (event->attr.type != PERF_TYPE_TRACEPOINT) { perf_event_free_bpf_handler(event); return; } - - prog = event->tp_event->prog; - if (prog && event->tp_event->bpf_prog_owner == event) { - event->tp_event->prog = NULL; - bpf_prog_put(prog); - } + perf_event_detach_bpf_prog(event); } #else diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 3126da2f468a..b65011d320e3 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -17,7 +17,7 @@ /** * trace_call_bpf - invoke BPF program - * @prog: BPF program + * @call: tracepoint event * @ctx: opaque context pointer * * kprobe handlers execute BPF programs via this helper. @@ -29,7 +29,7 @@ * 1 - store kprobe event into ring buffer * Other values are reserved and currently alias to 1 */ -unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx) +unsigned int trace_call_bpf(struct trace_event_call *call, void *ctx) { unsigned int ret; @@ -49,9 +49,22 @@ unsigned int trace_call_bpf(struct bpf_prog *prog, void *ctx) goto out; } - rcu_read_lock(); - ret = BPF_PROG_RUN(prog, ctx); - rcu_read_unlock(); + /* + * Instead of moving rcu_read_lock/rcu_dereference/rcu_read_unlock + * to all call sites, we did a bpf_prog_array_valid() there to check + * whether call->prog_array is empty or not, which is + * a heurisitc to speed up execution. + * + * If bpf_prog_array_valid() fetched prog_array was + * non-NULL, we go into trace_call_bpf() and do the actual + * proper rcu_dereference() under RCU lock. + * If it turns out that prog_array is NULL then, we bail out. + * For the opposite, if the bpf_prog_array_valid() fetched pointer + * was NULL, you'll skip the prog_array with the risk of missing + * out of events when it was updated in between this and the + * rcu_dereference() which is accepted risk. + */ + ret = BPF_PROG_RUN_ARRAY_CHECK(call->prog_array, ctx, BPF_PROG_RUN); out: __this_cpu_dec(bpf_prog_active); @@ -741,3 +754,62 @@ const struct bpf_verifier_ops perf_event_verifier_ops = { const struct bpf_prog_ops perf_event_prog_ops = { }; + +static DEFINE_MUTEX(bpf_event_mutex); + +int perf_event_attach_bpf_prog(struct perf_event *event, + struct bpf_prog *prog) +{ + struct bpf_prog_array __rcu *old_array; + struct bpf_prog_array *new_array; + int ret = -EEXIST; + + mutex_lock(&bpf_event_mutex); + + if (event->prog) + goto out; + + old_array = rcu_dereference_protected(event->tp_event->prog_array, + lockdep_is_held(&bpf_event_mutex)); + ret = bpf_prog_array_copy(old_array, NULL, prog, &new_array); + if (ret < 0) + goto out; + + /* set the new array to event->tp_event and set event->prog */ + event->prog = prog; + rcu_assign_pointer(event->tp_event->prog_array, new_array); + bpf_prog_array_free(old_array); + +out: + mutex_unlock(&bpf_event_mutex); + return ret; +} + +void perf_event_detach_bpf_prog(struct perf_event *event) +{ + struct bpf_prog_array __rcu *old_array; + struct bpf_prog_array *new_array; + int ret; + + mutex_lock(&bpf_event_mutex); + + if (!event->prog) + goto out; + + old_array = rcu_dereference_protected(event->tp_event->prog_array, + lockdep_is_held(&bpf_event_mutex)); + + ret = bpf_prog_array_copy(old_array, event->prog, NULL, &new_array); + if (ret < 0) { + bpf_prog_array_delete_safe(old_array, event->prog); + } else { + rcu_assign_pointer(event->tp_event->prog_array, new_array); + bpf_prog_array_free(old_array); + } + + bpf_prog_put(event->prog); + event->prog = NULL; + +out: + mutex_unlock(&bpf_event_mutex); +} diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c index 8a907e12b6b9..abf92e478cfb 100644 --- a/kernel/trace/trace_kprobe.c +++ b/kernel/trace/trace_kprobe.c @@ -1174,13 +1174,12 @@ static void kprobe_perf_func(struct trace_kprobe *tk, struct pt_regs *regs) { struct trace_event_call *call = &tk->tp.call; - struct bpf_prog *prog = call->prog; struct kprobe_trace_entry_head *entry; struct hlist_head *head; int size, __size, dsize; int rctx; - if (prog && !trace_call_bpf(prog, regs)) + if (bpf_prog_array_valid(call) && !trace_call_bpf(call, regs)) return; head = this_cpu_ptr(call->perf_events); @@ -1210,13 +1209,12 @@ kretprobe_perf_func(struct trace_kprobe *tk, struct kretprobe_instance *ri, struct pt_regs *regs) { struct trace_event_call *call = &tk->tp.call; - struct bpf_prog *prog = call->prog; struct kretprobe_trace_entry_head *entry; struct hlist_head *head; int size, __size, dsize; int rctx; - if (prog && !trace_call_bpf(prog, regs)) + if (bpf_prog_array_valid(call) && !trace_call_bpf(call, regs)) return; head = this_cpu_ptr(call->perf_events); diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 696afe72d3b1..71a6af34d7a9 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -559,9 +559,10 @@ static DECLARE_BITMAP(enabled_perf_exit_syscalls, NR_syscalls); static int sys_perf_refcount_enter; static int sys_perf_refcount_exit; -static int perf_call_bpf_enter(struct bpf_prog *prog, struct pt_regs *regs, - struct syscall_metadata *sys_data, - struct syscall_trace_enter *rec) { +static int perf_call_bpf_enter(struct trace_event_call *call, struct pt_regs *regs, + struct syscall_metadata *sys_data, + struct syscall_trace_enter *rec) +{ struct syscall_tp_t { unsigned long long regs; unsigned long syscall_nr; @@ -573,7 +574,7 @@ static int perf_call_bpf_enter(struct bpf_prog *prog, struct pt_regs *regs, param.syscall_nr = rec->nr; for (i = 0; i < sys_data->nb_args; i++) param.args[i] = rec->args[i]; - return trace_call_bpf(prog, ¶m); + return trace_call_bpf(call, ¶m); } static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) @@ -581,7 +582,7 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) struct syscall_metadata *sys_data; struct syscall_trace_enter *rec; struct hlist_head *head; - struct bpf_prog *prog; + bool valid_prog_array; int syscall_nr; int rctx; int size; @@ -596,9 +597,9 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) if (!sys_data) return; - prog = READ_ONCE(sys_data->enter_event->prog); head = this_cpu_ptr(sys_data->enter_event->perf_events); - if (!prog && hlist_empty(head)) + valid_prog_array = bpf_prog_array_valid(sys_data->enter_event); + if (!valid_prog_array && hlist_empty(head)) return; /* get the size after alignment with the u32 buffer size field */ @@ -614,7 +615,8 @@ static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id) syscall_get_arguments(current, regs, 0, sys_data->nb_args, (unsigned long *)&rec->args); - if ((prog && !perf_call_bpf_enter(prog, regs, sys_data, rec)) || + if ((valid_prog_array && + !perf_call_bpf_enter(sys_data->enter_event, regs, sys_data, rec)) || hlist_empty(head)) { perf_swevent_put_recursion_context(rctx); return; @@ -659,8 +661,9 @@ static void perf_sysenter_disable(struct trace_event_call *call) mutex_unlock(&syscall_trace_lock); } -static int perf_call_bpf_exit(struct bpf_prog *prog, struct pt_regs *regs, - struct syscall_trace_exit *rec) { +static int perf_call_bpf_exit(struct trace_event_call *call, struct pt_regs *regs, + struct syscall_trace_exit *rec) +{ struct syscall_tp_t { unsigned long long regs; unsigned long syscall_nr; @@ -670,7 +673,7 @@ static int perf_call_bpf_exit(struct bpf_prog *prog, struct pt_regs *regs, *(struct pt_regs **)¶m = regs; param.syscall_nr = rec->nr; param.ret = rec->ret; - return trace_call_bpf(prog, ¶m); + return trace_call_bpf(call, ¶m); } static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) @@ -678,7 +681,7 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) struct syscall_metadata *sys_data; struct syscall_trace_exit *rec; struct hlist_head *head; - struct bpf_prog *prog; + bool valid_prog_array; int syscall_nr; int rctx; int size; @@ -693,9 +696,9 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) if (!sys_data) return; - prog = READ_ONCE(sys_data->exit_event->prog); head = this_cpu_ptr(sys_data->exit_event->perf_events); - if (!prog && hlist_empty(head)) + valid_prog_array = bpf_prog_array_valid(sys_data->exit_event); + if (!valid_prog_array && hlist_empty(head)) return; /* We can probably do that at build time */ @@ -709,7 +712,8 @@ static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret) rec->nr = syscall_nr; rec->ret = syscall_get_return_value(current, regs); - if ((prog && !perf_call_bpf_exit(prog, regs, rec)) || + if ((valid_prog_array && + !perf_call_bpf_exit(sys_data->exit_event, regs, rec)) || hlist_empty(head)) { perf_swevent_put_recursion_context(rctx); return; diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 4525e0271a53..153c0e411461 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -1113,13 +1113,12 @@ static void __uprobe_perf_func(struct trace_uprobe *tu, { struct trace_event_call *call = &tu->tp.call; struct uprobe_trace_entry_head *entry; - struct bpf_prog *prog = call->prog; struct hlist_head *head; void *data; int size, esize; int rctx; - if (prog && !trace_call_bpf(prog, regs)) + if (bpf_prog_array_valid(call) && !trace_call_bpf(call, regs)) return; esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu)); -- cgit v1.2.3-59-g8ed1b From ef5201c83d1400570a3b6f004ad7a23d71934411 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Tue, 24 Oct 2017 13:54:20 +0800 Subject: bonding: remove rtmsg_ifinfo called after bond_lower_state_changed After the patch 'rtnetlink: bring NETDEV_CHANGELOWERSTATE event process back to rtnetlink_event', bond_lower_state_changed would generate NETDEV_CHANGEUPPER event which would send a notification to userspace in rtnetlink_event. There's no need to call rtmsg_ifinfo to send the notification any more. So this patch is to remove it from these places after bond_lower_state_changed. Besides, after this, rtmsg_ifinfo is not needed to be exported. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/bonding.h | 4 ---- net/core/rtnetlink.c | 1 - 2 files changed, 5 deletions(-) (limited to 'include') diff --git a/include/net/bonding.h b/include/net/bonding.h index 2860cc66c2bb..f801fc940b29 100644 --- a/include/net/bonding.h +++ b/include/net/bonding.h @@ -330,7 +330,6 @@ static inline void bond_set_active_slave(struct slave *slave) slave->backup = 0; bond_queue_slave_event(slave); bond_lower_state_changed(slave); - rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC); } } @@ -340,7 +339,6 @@ static inline void bond_set_backup_slave(struct slave *slave) slave->backup = 1; bond_queue_slave_event(slave); bond_lower_state_changed(slave); - rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC); } } @@ -353,7 +351,6 @@ static inline void bond_set_slave_state(struct slave *slave, slave->backup = slave_state; if (notify) { bond_lower_state_changed(slave); - rtmsg_ifinfo(RTM_NEWLINK, slave->dev, 0, GFP_ATOMIC); bond_queue_slave_event(slave); slave->should_notify = 0; } else { @@ -385,7 +382,6 @@ static inline void bond_slave_state_notify(struct bonding *bond) bond_for_each_slave(bond, tmp, iter) { if (tmp->should_notify) { bond_lower_state_changed(tmp); - rtmsg_ifinfo(RTM_NEWLINK, tmp->dev, 0, GFP_ATOMIC); tmp->should_notify = 0; } } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 854a848842ea..de24d394c69e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -2989,7 +2989,6 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, { rtmsg_ifinfo_event(type, dev, change, rtnl_get_event(0), flags, NULL); } -EXPORT_SYMBOL(rtmsg_ifinfo); void rtmsg_ifinfo_newnet(int type, struct net_device *dev, unsigned int change, gfp_t flags, int *new_nsid) -- cgit v1.2.3-59-g8ed1b From 9c3b57518363577d4e2ea1964ef4fa03e100beaa Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 24 Oct 2017 01:45:31 -0700 Subject: net: sctp: Convert timers to use timer_setup() In preparation for unconditionally passing the struct timer_list pointer to all timer callbacks, switch to using the new timer_setup() and from_timer() to pass the timer pointer explicitly. Cc: Vlad Yasevich Cc: Neil Horman Cc: "David S. Miller" Cc: linux-sctp@vger.kernel.org Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Signed-off-by: David S. Miller --- include/net/sctp/sm.h | 10 +++--- net/sctp/associola.c | 3 +- net/sctp/protocol.c | 7 ++-- net/sctp/sm_sideeffect.c | 85 +++++++++++++++++++++++++++++------------------- net/sctp/transport.c | 13 +++----- 5 files changed, 66 insertions(+), 52 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 2db3d3a9ce1d..13cc4963e905 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -72,7 +72,7 @@ typedef enum sctp_disposition (sctp_state_fn_t) ( const union sctp_subtype type, void *arg, struct sctp_cmd_seq *commands); -typedef void (sctp_timer_event_t) (unsigned long); +typedef void (sctp_timer_event_t) (struct timer_list *); struct sctp_sm_table_entry { sctp_state_fn_t *fn; const char *name; @@ -314,10 +314,10 @@ int sctp_do_sm(struct net *net, enum sctp_event event_type, void *event_arg, gfp_t gfp); /* 2nd level prototypes */ -void sctp_generate_t3_rtx_event(unsigned long peer); -void sctp_generate_heartbeat_event(unsigned long peer); -void sctp_generate_reconf_event(unsigned long peer); -void sctp_generate_proto_unreach_event(unsigned long peer); +void sctp_generate_t3_rtx_event(struct timer_list *t); +void sctp_generate_heartbeat_event(struct timer_list *t); +void sctp_generate_reconf_event(struct timer_list *t); +void sctp_generate_proto_unreach_event(struct timer_list *t); void sctp_ootb_pkt_free(struct sctp_packet *packet); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index dfb9651e818b..69394f4d6091 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -149,8 +149,7 @@ static struct sctp_association *sctp_association_init( /* Initializes the timers */ for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) - setup_timer(&asoc->timers[i], sctp_timer_events[i], - (unsigned long)asoc); + timer_setup(&asoc->timers[i], sctp_timer_events[i], 0); /* Pull default initialization values from the sock options. * Note: This assumes that the values have already been diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index fcd80feb293f..f5172c21349b 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -622,9 +622,9 @@ static void sctp_v4_ecn_capable(struct sock *sk) INET_ECN_xmit(sk); } -static void sctp_addr_wq_timeout_handler(unsigned long arg) +static void sctp_addr_wq_timeout_handler(struct timer_list *t) { - struct net *net = (struct net *)arg; + struct net *net = from_timer(net, t, sctp.addr_wq_timer); struct sctp_sockaddr_entry *addrw, *temp; struct sctp_sock *sp; @@ -1304,8 +1304,7 @@ static int __net_init sctp_defaults_init(struct net *net) INIT_LIST_HEAD(&net->sctp.auto_asconf_splist); spin_lock_init(&net->sctp.addr_wq_lock); net->sctp.addr_wq_timer.expires = 0; - setup_timer(&net->sctp.addr_wq_timer, sctp_addr_wq_timeout_handler, - (unsigned long)net); + timer_setup(&net->sctp.addr_wq_timer, sctp_addr_wq_timeout_handler, 0); return 0; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 402bfbb888cd..1c2699b424af 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -243,9 +243,10 @@ nomem: /* When the T3-RTX timer expires, it calls this function to create the * relevant state machine event. */ -void sctp_generate_t3_rtx_event(unsigned long peer) +void sctp_generate_t3_rtx_event(struct timer_list *t) { - struct sctp_transport *transport = (struct sctp_transport *) peer; + struct sctp_transport *transport = + from_timer(transport, t, T3_rtx_timer); struct sctp_association *asoc = transport->asoc; struct sock *sk = asoc->base.sk; struct net *net = sock_net(sk); @@ -319,50 +320,63 @@ out_unlock: sctp_association_put(asoc); } -static void sctp_generate_t1_cookie_event(unsigned long data) +static void sctp_generate_t1_cookie_event(struct timer_list *t) { - struct sctp_association *asoc = (struct sctp_association *) data; + struct sctp_association *asoc = + from_timer(asoc, t, timers[SCTP_EVENT_TIMEOUT_T1_COOKIE]); + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_COOKIE); } -static void sctp_generate_t1_init_event(unsigned long data) +static void sctp_generate_t1_init_event(struct timer_list *t) { - struct sctp_association *asoc = (struct sctp_association *) data; + struct sctp_association *asoc = + from_timer(asoc, t, timers[SCTP_EVENT_TIMEOUT_T1_INIT]); + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_INIT); } -static void sctp_generate_t2_shutdown_event(unsigned long data) +static void sctp_generate_t2_shutdown_event(struct timer_list *t) { - struct sctp_association *asoc = (struct sctp_association *) data; + struct sctp_association *asoc = + from_timer(asoc, t, timers[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN]); + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T2_SHUTDOWN); } -static void sctp_generate_t4_rto_event(unsigned long data) +static void sctp_generate_t4_rto_event(struct timer_list *t) { - struct sctp_association *asoc = (struct sctp_association *) data; + struct sctp_association *asoc = + from_timer(asoc, t, timers[SCTP_EVENT_TIMEOUT_T4_RTO]); + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T4_RTO); } -static void sctp_generate_t5_shutdown_guard_event(unsigned long data) +static void sctp_generate_t5_shutdown_guard_event(struct timer_list *t) { - struct sctp_association *asoc = (struct sctp_association *)data; + struct sctp_association *asoc = + from_timer(asoc, t, + timers[SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD]); + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD); } /* sctp_generate_t5_shutdown_guard_event() */ -static void sctp_generate_autoclose_event(unsigned long data) +static void sctp_generate_autoclose_event(struct timer_list *t) { - struct sctp_association *asoc = (struct sctp_association *) data; + struct sctp_association *asoc = + from_timer(asoc, t, timers[SCTP_EVENT_TIMEOUT_AUTOCLOSE]); + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_AUTOCLOSE); } /* Generate a heart beat event. If the sock is busy, reschedule. Make * sure that the transport is still valid. */ -void sctp_generate_heartbeat_event(unsigned long data) +void sctp_generate_heartbeat_event(struct timer_list *t) { - struct sctp_transport *transport = (struct sctp_transport *) data; + struct sctp_transport *transport = from_timer(transport, t, hb_timer); struct sctp_association *asoc = transport->asoc; struct sock *sk = asoc->base.sk; struct net *net = sock_net(sk); @@ -405,9 +419,10 @@ out_unlock: /* Handle the timeout of the ICMP protocol unreachable timer. Trigger * the correct state machine transition that will close the association. */ -void sctp_generate_proto_unreach_event(unsigned long data) +void sctp_generate_proto_unreach_event(struct timer_list *t) { - struct sctp_transport *transport = (struct sctp_transport *)data; + struct sctp_transport *transport = + from_timer(transport, t, proto_unreach_timer); struct sctp_association *asoc = transport->asoc; struct sock *sk = asoc->base.sk; struct net *net = sock_net(sk); @@ -439,9 +454,10 @@ out_unlock: } /* Handle the timeout of the RE-CONFIG timer. */ -void sctp_generate_reconf_event(unsigned long data) +void sctp_generate_reconf_event(struct timer_list *t) { - struct sctp_transport *transport = (struct sctp_transport *)data; + struct sctp_transport *transport = + from_timer(transport, t, reconf_timer); struct sctp_association *asoc = transport->asoc; struct sock *sk = asoc->base.sk; struct net *net = sock_net(sk); @@ -471,24 +487,27 @@ out_unlock: } /* Inject a SACK Timeout event into the state machine. */ -static void sctp_generate_sack_event(unsigned long data) +static void sctp_generate_sack_event(struct timer_list *t) { - struct sctp_association *asoc = (struct sctp_association *)data; + struct sctp_association *asoc = + from_timer(asoc, t, timers[SCTP_EVENT_TIMEOUT_SACK]); + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK); } sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = { - NULL, - sctp_generate_t1_cookie_event, - sctp_generate_t1_init_event, - sctp_generate_t2_shutdown_event, - NULL, - sctp_generate_t4_rto_event, - sctp_generate_t5_shutdown_guard_event, - NULL, - NULL, - sctp_generate_sack_event, - sctp_generate_autoclose_event, + [SCTP_EVENT_TIMEOUT_NONE] = NULL, + [SCTP_EVENT_TIMEOUT_T1_COOKIE] = sctp_generate_t1_cookie_event, + [SCTP_EVENT_TIMEOUT_T1_INIT] = sctp_generate_t1_init_event, + [SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = sctp_generate_t2_shutdown_event, + [SCTP_EVENT_TIMEOUT_T3_RTX] = NULL, + [SCTP_EVENT_TIMEOUT_T4_RTO] = sctp_generate_t4_rto_event, + [SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD] = + sctp_generate_t5_shutdown_guard_event, + [SCTP_EVENT_TIMEOUT_HEARTBEAT] = NULL, + [SCTP_EVENT_TIMEOUT_RECONF] = NULL, + [SCTP_EVENT_TIMEOUT_SACK] = sctp_generate_sack_event, + [SCTP_EVENT_TIMEOUT_AUTOCLOSE] = sctp_generate_autoclose_event, }; diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 2d9bd3776bc8..1e5a22430cf5 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -87,14 +87,11 @@ static struct sctp_transport *sctp_transport_init(struct net *net, INIT_LIST_HEAD(&peer->send_ready); INIT_LIST_HEAD(&peer->transports); - setup_timer(&peer->T3_rtx_timer, sctp_generate_t3_rtx_event, - (unsigned long)peer); - setup_timer(&peer->hb_timer, sctp_generate_heartbeat_event, - (unsigned long)peer); - setup_timer(&peer->reconf_timer, sctp_generate_reconf_event, - (unsigned long)peer); - setup_timer(&peer->proto_unreach_timer, - sctp_generate_proto_unreach_event, (unsigned long)peer); + timer_setup(&peer->T3_rtx_timer, sctp_generate_t3_rtx_event, 0); + timer_setup(&peer->hb_timer, sctp_generate_heartbeat_event, 0); + timer_setup(&peer->reconf_timer, sctp_generate_reconf_event, 0); + timer_setup(&peer->proto_unreach_timer, + sctp_generate_proto_unreach_event, 0); /* Initialize the 64-bit random nonce sent with heartbeat. */ get_random_bytes(&peer->hb_nonce, sizeof(peer->hb_nonce)); -- cgit v1.2.3-59-g8ed1b From fc8bcaa05160528d56432e4612f522e3ceafc513 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 24 Oct 2017 01:45:48 -0700 Subject: net: LLC: Convert timers to use timer_setup() In preparation for unconditionally passing the struct timer_list pointer to all timer callbacks, switch to using the new timer_setup() and from_timer() to pass the timer pointer explicitly. Cc: "David S. Miller" Cc: Eric Dumazet Cc: Hans Liljestrand Cc: "Paul E. McKenney" Cc: "Reshetova, Elena" Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Signed-off-by: David S. Miller --- include/net/llc_c_ac.h | 8 ++++---- net/llc/llc_c_ac.c | 27 +++++++++++++++++---------- net/llc/llc_conn.c | 12 ++++-------- 3 files changed, 25 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/net/llc_c_ac.h b/include/net/llc_c_ac.h index f3be818e73c1..e766300b3e99 100644 --- a/include/net/llc_c_ac.h +++ b/include/net/llc_c_ac.h @@ -171,10 +171,10 @@ int llc_conn_ac_rst_sendack_flag(struct sock *sk, struct sk_buff *skb); int llc_conn_ac_send_i_rsp_as_ack(struct sock *sk, struct sk_buff *skb); int llc_conn_ac_send_i_as_ack(struct sock *sk, struct sk_buff *skb); -void llc_conn_busy_tmr_cb(unsigned long timeout_data); -void llc_conn_pf_cycle_tmr_cb(unsigned long timeout_data); -void llc_conn_ack_tmr_cb(unsigned long timeout_data); -void llc_conn_rej_tmr_cb(unsigned long timeout_data); +void llc_conn_busy_tmr_cb(struct timer_list *t); +void llc_conn_pf_cycle_tmr_cb(struct timer_list *t); +void llc_conn_ack_tmr_cb(struct timer_list *t); +void llc_conn_rej_tmr_cb(struct timer_list *t); void llc_conn_set_p_flag(struct sock *sk, u8 value); #endif /* LLC_C_AC_H */ diff --git a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c index ea225bd2672c..f59648018060 100644 --- a/net/llc/llc_c_ac.c +++ b/net/llc/llc_c_ac.c @@ -1318,9 +1318,8 @@ static int llc_conn_ac_inc_vs_by_1(struct sock *sk, struct sk_buff *skb) return 0; } -static void llc_conn_tmr_common_cb(unsigned long timeout_data, u8 type) +static void llc_conn_tmr_common_cb(struct sock *sk, u8 type) { - struct sock *sk = (struct sock *)timeout_data; struct sk_buff *skb = alloc_skb(0, GFP_ATOMIC); bh_lock_sock(sk); @@ -1334,24 +1333,32 @@ static void llc_conn_tmr_common_cb(unsigned long timeout_data, u8 type) bh_unlock_sock(sk); } -void llc_conn_pf_cycle_tmr_cb(unsigned long timeout_data) +void llc_conn_pf_cycle_tmr_cb(struct timer_list *t) { - llc_conn_tmr_common_cb(timeout_data, LLC_CONN_EV_TYPE_P_TMR); + struct llc_sock *llc = from_timer(llc, t, pf_cycle_timer.timer); + + llc_conn_tmr_common_cb(&llc->sk, LLC_CONN_EV_TYPE_P_TMR); } -void llc_conn_busy_tmr_cb(unsigned long timeout_data) +void llc_conn_busy_tmr_cb(struct timer_list *t) { - llc_conn_tmr_common_cb(timeout_data, LLC_CONN_EV_TYPE_BUSY_TMR); + struct llc_sock *llc = from_timer(llc, t, busy_state_timer.timer); + + llc_conn_tmr_common_cb(&llc->sk, LLC_CONN_EV_TYPE_BUSY_TMR); } -void llc_conn_ack_tmr_cb(unsigned long timeout_data) +void llc_conn_ack_tmr_cb(struct timer_list *t) { - llc_conn_tmr_common_cb(timeout_data, LLC_CONN_EV_TYPE_ACK_TMR); + struct llc_sock *llc = from_timer(llc, t, ack_timer.timer); + + llc_conn_tmr_common_cb(&llc->sk, LLC_CONN_EV_TYPE_ACK_TMR); } -void llc_conn_rej_tmr_cb(unsigned long timeout_data) +void llc_conn_rej_tmr_cb(struct timer_list *t) { - llc_conn_tmr_common_cb(timeout_data, LLC_CONN_EV_TYPE_REJ_TMR); + struct llc_sock *llc = from_timer(llc, t, rej_sent_timer.timer); + + llc_conn_tmr_common_cb(&llc->sk, LLC_CONN_EV_TYPE_REJ_TMR); } int llc_conn_ac_rst_vs(struct sock *sk, struct sk_buff *skb) diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c index 5e91b47f0d2a..9177dbb16dce 100644 --- a/net/llc/llc_conn.c +++ b/net/llc/llc_conn.c @@ -902,20 +902,16 @@ static void llc_sk_init(struct sock *sk) llc->inc_cntr = llc->dec_cntr = 2; llc->dec_step = llc->connect_step = 1; - setup_timer(&llc->ack_timer.timer, llc_conn_ack_tmr_cb, - (unsigned long)sk); + timer_setup(&llc->ack_timer.timer, llc_conn_ack_tmr_cb, 0); llc->ack_timer.expire = sysctl_llc2_ack_timeout; - setup_timer(&llc->pf_cycle_timer.timer, llc_conn_pf_cycle_tmr_cb, - (unsigned long)sk); + timer_setup(&llc->pf_cycle_timer.timer, llc_conn_pf_cycle_tmr_cb, 0); llc->pf_cycle_timer.expire = sysctl_llc2_p_timeout; - setup_timer(&llc->rej_sent_timer.timer, llc_conn_rej_tmr_cb, - (unsigned long)sk); + timer_setup(&llc->rej_sent_timer.timer, llc_conn_rej_tmr_cb, 0); llc->rej_sent_timer.expire = sysctl_llc2_rej_timeout; - setup_timer(&llc->busy_state_timer.timer, llc_conn_busy_tmr_cb, - (unsigned long)sk); + timer_setup(&llc->busy_state_timer.timer, llc_conn_busy_tmr_cb, 0); llc->busy_state_timer.expire = sysctl_llc2_busy_timeout; llc->n2 = 2; /* max retransmit */ -- cgit v1.2.3-59-g8ed1b From e7646f84ad4f654e1ee503b03a12e520d947884f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Fri, 17 Mar 2017 15:33:22 -0700 Subject: drm: Add new LEASE debug level Separate out lease debugging from the core. Signed-off-by: Keith Packard Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_drv.c | 3 ++- include/drm/drmP.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index c0292e5d7281..a934fd5e7e55 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -57,7 +57,8 @@ MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug cat "\t\tBit 2 (0x04) will enable KMS messages (modesetting code)\n" "\t\tBit 3 (0x08) will enable PRIME messages (prime code)\n" "\t\tBit 4 (0x10) will enable ATOMIC messages (atomic code)\n" -"\t\tBit 5 (0x20) will enable VBL messages (vblank code)"); +"\t\tBit 5 (0x20) will enable VBL messages (vblank code)\n" +"\t\tBit 7 (0x80) will enable LEASE messages (leasing code)"); module_param_named(debug, drm_debug, int, 0600); static DEFINE_SPINLOCK(drm_minor_lock); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 7277783a4ff0..59be1232d005 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -136,6 +136,7 @@ struct pci_controller; #define DRM_UT_ATOMIC 0x10 #define DRM_UT_VBL 0x20 #define DRM_UT_STATE 0x40 +#define DRM_UT_LEASE 0x80 /***********************************************************************/ /** \name DRM template customization defaults */ @@ -250,6 +251,9 @@ struct pci_controller; #define DRM_DEBUG_VBL(fmt, ...) \ drm_printk(KERN_DEBUG, DRM_UT_VBL, fmt, ##__VA_ARGS__) +#define DRM_DEBUG_LEASE(fmt, ...) \ + drm_printk(KERN_DEBUG, DRM_UT_LEASE, fmt, ##__VA_ARGS__) + #define _DRM_DEV_DEFINE_DEBUG_RATELIMITED(dev, level, fmt, args...) \ ({ \ static DEFINE_RATELIMIT_STATE(_rs, \ -- cgit v1.2.3-59-g8ed1b From 2ed077e467eedb033032bc4b6e349365517662d6 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 14 Mar 2017 22:26:41 -0700 Subject: drm: Add drm_object lease infrastructure [v5] This provides new data structures to hold "lease" information about drm mode setting objects, and provides for creating new drm_masters which have access to a subset of the available drm resources. An 'owner' is a drm_master which is not leasing the objects from another drm_master, and hence 'owns' them. A 'lessee' is a drm_master which is leasing objects from some other drm_master. Each lessee holds the set of objects which it is leasing from the lessor. A 'lessor' is a drm_master which is leasing objects to another drm_master. This is the same as the owner in the current code. The set of objects any drm_master 'controls' is limited to the set of objects it leases (for lessees) or all objects (for owners). Objects not controlled by a drm_master cannot be modified through the various state manipulating ioctls, and any state reported back to user space will be edited to make them appear idle and/or unusable. For instance, connectors always report 'disconnected', while encoders report no possible crtcs or clones. The full list of lessees leasing objects from an owner (either directly, or indirectly through another lessee), can be searched from an idr in the drm_master of the owner. Changes for v2 as suggested by Daniel Vetter : * Sub-leasing has been disabled. * BUG_ON for lock checking replaced with lockdep_assert_held * 'change' ioctl has been removed. * Leased objects can always be controlled by the lessor; the 'mask_lease' flag has been removed * Checking for leased status has been simplified, replacing the drm_lease_check function with drm_lease_held. Changes in v3, some suggested by Dave Airlie * Add revocation. This allows leases to be effectively revoked by removing all of the objects they have access to. The lease itself hangs around as it's hanging off a file. * Free the leases IDR when the master is destroyed * _drm_lease_held should look at lessees, not lessor * Allow non-master files to check for lease status Changes in v4, suggested by Dave Airlie * Formatting and whitespace changes Changes in v5 (airlied) * check DRIVER_MODESET before lease destroy call * check DRIVER_MODESET for lease revoke (Chris) * Use idr_mutex uniformly for all lease elements of struct drm_master. (Keith) Signed-off-by: Keith Packard --- drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_auth.c | 30 +++- drivers/gpu/drm/drm_lease.c | 355 ++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_auth.h | 21 +++ include/drm/drm_lease.h | 36 +++++ include/drm/drm_mode_object.h | 1 + 6 files changed, 443 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/drm_lease.c create mode 100644 include/drm/drm_lease.h (limited to 'include') diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index a3fdc5a68dff..81ff79336623 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -17,7 +17,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_encoder.o drm_mode_object.o drm_property.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ - drm_syncobj.o + drm_syncobj.o drm_lease.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o drm-$(CONFIG_DRM_VM) += drm_vm.o diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 7ff697389d74..4f0e274f4111 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -31,6 +31,7 @@ #include #include "drm_internal.h" #include "drm_legacy.h" +#include /** * DOC: master and authentication @@ -93,7 +94,7 @@ int drm_authmagic(struct drm_device *dev, void *data, return file ? 0 : -EINVAL; } -static struct drm_master *drm_master_create(struct drm_device *dev) +struct drm_master *drm_master_create(struct drm_device *dev) { struct drm_master *master; @@ -107,6 +108,14 @@ static struct drm_master *drm_master_create(struct drm_device *dev) idr_init(&master->magic_map); master->dev = dev; + /* initialize the tree of output resource lessees */ + master->lessor = NULL; + master->lessee_id = 0; + INIT_LIST_HEAD(&master->lessees); + INIT_LIST_HEAD(&master->lessee_list); + idr_init(&master->leases); + idr_init(&master->lessee_idr); + return master; } @@ -189,6 +198,12 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data, goto out_unlock; } + if (file_priv->master->lessor != NULL) { + DRM_DEBUG_LEASE("Attempt to set lessee %d as master\n", file_priv->master->lessee_id); + ret = -EINVAL; + goto out_unlock; + } + ret = drm_set_master(dev, file_priv, false); out_unlock: mutex_unlock(&dev->master_mutex); @@ -270,6 +285,13 @@ void drm_master_release(struct drm_file *file_priv) if (dev->master == file_priv->master) drm_drop_master(dev, file_priv); out: + if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) { + /* Revoke any leases held by this or lessees, but only if + * this is the "real" master + */ + drm_lease_revoke(master); + } + /* drop the master reference held by the file priv */ if (file_priv->master) drm_master_put(&file_priv->master); @@ -310,12 +332,18 @@ static void drm_master_destroy(struct kref *kref) struct drm_master *master = container_of(kref, struct drm_master, refcount); struct drm_device *dev = master->dev; + if (drm_core_check_feature(dev, DRIVER_MODESET)) + drm_lease_destroy(master); + if (dev->driver->master_destroy) dev->driver->master_destroy(dev, master); drm_legacy_master_rmmaps(dev, master); idr_destroy(&master->magic_map); + idr_destroy(&master->leases); + idr_destroy(&master->lessee_idr); + kfree(master->unique); kfree(master); } diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c new file mode 100644 index 000000000000..030db1170570 --- /dev/null +++ b/drivers/gpu/drm/drm_lease.c @@ -0,0 +1,355 @@ +/* + * Copyright © 2017 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#include +#include "drm_internal.h" +#include "drm_legacy.h" +#include "drm_crtc_internal.h" +#include +#include +#include + +#define drm_for_each_lessee(lessee, lessor) \ + list_for_each_entry((lessee), &(lessor)->lessees, lessee_list) + +/** + * drm_lease_owner - return ancestor owner drm_master + * @master: drm_master somewhere within tree of lessees and lessors + * + * RETURN: + * + * drm_master at the top of the tree (i.e, with lessor NULL + */ +struct drm_master *drm_lease_owner(struct drm_master *master) +{ + while (master->lessor != NULL) + master = master->lessor; + return master; +} +EXPORT_SYMBOL(drm_lease_owner); + +/** + * _drm_find_lessee - find lessee by id (idr_mutex held) + * @master: drm_master of lessor + * @id: lessee_id + * + * RETURN: + * + * drm_master of the lessee if valid, NULL otherwise + */ + +static struct drm_master* +_drm_find_lessee(struct drm_master *master, int lessee_id) +{ + lockdep_assert_held(&master->dev->mode_config.idr_mutex); + return idr_find(&drm_lease_owner(master)->lessee_idr, lessee_id); +} + +/** + * _drm_lease_held_master - check to see if an object is leased (or owned) by master (idr_mutex held) + * @master: the master to check the lease status of + * @id: the id to check + * + * Checks if the specified master holds a lease on the object. Return + * value: + * + * true 'master' holds a lease on (or owns) the object + * false 'master' does not hold a lease. + */ +static int _drm_lease_held_master(struct drm_master *master, int id) +{ + lockdep_assert_held(&master->dev->mode_config.idr_mutex); + if (master->lessor) + return idr_find(&master->leases, id) != NULL; + return true; +} + +/** + * _drm_has_leased - check to see if an object has been leased (idr_mutex held) + * @master: the master to check the lease status of + * @id: the id to check + * + * Checks if any lessee of 'master' holds a lease on 'id'. Return + * value: + * + * true Some lessee holds a lease on the object. + * false No lessee has a lease on the object. + */ +static bool _drm_has_leased(struct drm_master *master, int id) +{ + struct drm_master *lessee; + + lockdep_assert_held(&master->dev->mode_config.idr_mutex); + drm_for_each_lessee(lessee, master) + if (_drm_lease_held_master(lessee, id)) + return true; + return false; +} + +/** + * _drm_lease_held - check drm_mode_object lease status (idr_mutex held) + * @master: the drm_master + * @id: the object id + * + * Checks if the specified master holds a lease on the object. Return + * value: + * + * true 'master' holds a lease on (or owns) the object + * false 'master' does not hold a lease. + */ +bool _drm_lease_held(struct drm_file *file_priv, int id) +{ + if (file_priv == NULL || file_priv->master == NULL) + return true; + + return _drm_lease_held_master(file_priv->master, id); +} +EXPORT_SYMBOL(_drm_lease_held); + +/** + * drm_lease_held - check drm_mode_object lease status (idr_mutex not held) + * @master: the drm_master + * @id: the object id + * + * Checks if the specified master holds a lease on the object. Return + * value: + * + * true 'master' holds a lease on (or owns) the object + * false 'master' does not hold a lease. + */ +bool drm_lease_held(struct drm_file *file_priv, int id) +{ + struct drm_master *master; + bool ret; + + if (file_priv == NULL || file_priv->master == NULL) + return true; + + master = file_priv->master; + mutex_lock(&master->dev->mode_config.idr_mutex); + ret = _drm_lease_held_master(master, id); + mutex_unlock(&master->dev->mode_config.idr_mutex); + return ret; +} +EXPORT_SYMBOL(drm_lease_held); + +/** + * drm_lease_filter_crtcs - restricted crtc set to leased values (idr_mutex not held) + * @file_priv: requestor file + * @crtcs: bitmask of crtcs to check + * + * Reconstructs a crtc mask based on the crtcs which are visible + * through the specified file. + */ +uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs_in) +{ + struct drm_master *master; + struct drm_device *dev; + struct drm_crtc *crtc; + int count_in, count_out; + uint32_t crtcs_out = 0; + + if (file_priv == NULL || file_priv->master == NULL) + return crtcs_in; + + master = file_priv->master; + dev = master->dev; + + count_in = count_out = 0; + mutex_lock(&master->dev->mode_config.idr_mutex); + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (_drm_lease_held_master(master, crtc->base.id)) { + uint32_t mask_in = 1ul << count_in; + if ((crtcs_in & mask_in) != 0) { + uint32_t mask_out = 1ul << count_out; + crtcs_out |= mask_out; + } + count_out++; + } + count_in++; + } + mutex_unlock(&master->dev->mode_config.idr_mutex); + return crtcs_out; +} +EXPORT_SYMBOL(drm_lease_filter_crtcs); + +/* + * drm_lease_create - create a new drm_master with leased objects (idr_mutex not held) + * @lessor: lease holder (or owner) of objects + * @leases: objects to lease to the new drm_master + * + * Uses drm_master_create to allocate a new drm_master, then checks to + * make sure all of the desired objects can be leased, atomically + * leasing them to the new drmmaster. + * + * ERR_PTR(-EACCESS) some other master holds the title to any object + * ERR_PTR(-ENOENT) some object is not a valid DRM object for this device + * ERR_PTR(-EBUSY) some other lessee holds title to this object + * ERR_PTR(-EEXIST) same object specified more than once in the provided list + * ERR_PTR(-ENOMEM) allocation failed + */ +static struct drm_master *drm_lease_create(struct drm_master *lessor, struct idr *leases) +{ + struct drm_device *dev = lessor->dev; + int error; + struct drm_master *lessee; + int object; + int id; + void *entry; + + DRM_DEBUG_LEASE("lessor %d\n", lessor->lessee_id); + + lessee = drm_master_create(lessor->dev); + if (!lessee) { + DRM_DEBUG_LEASE("drm_master_create failed\n"); + return ERR_PTR(-ENOMEM); + } + + mutex_lock(&dev->mode_config.idr_mutex); + + /* Insert the new lessee into the tree */ + id = idr_alloc(&(drm_lease_owner(lessor)->lessee_idr), lessee, 1, 0, GFP_KERNEL); + if (id < 0) { + error = id; + goto out_lessee; + } + + lessee->lessee_id = id; + lessee->lessor = drm_master_get(lessor); + list_add_tail(&lessee->lessee_list, &lessor->lessees); + + idr_for_each_entry(leases, entry, object) { + error = 0; + if (!idr_find(&dev->mode_config.crtc_idr, object)) + error = -ENOENT; + else if (!_drm_lease_held_master(lessor, object)) + error = -EACCES; + else if (_drm_has_leased(lessor, object)) + error = -EBUSY; + + if (error != 0) { + DRM_DEBUG_LEASE("object %d failed %d\n", object, error); + goto out_lessee; + } + } + + /* Move the leases over */ + lessee->leases = *leases; + DRM_DEBUG_LEASE("new lessee %d %p, lessor %d %p\n", lessee->lessee_id, lessee, lessor->lessee_id, lessor); + + mutex_unlock(&dev->mode_config.idr_mutex); + return lessee; + +out_lessee: + drm_master_put(&lessee); + + mutex_unlock(&dev->mode_config.idr_mutex); + + return ERR_PTR(error); +} + +/** + * drm_lease_destroy - a master is going away (idr_mutex not held) + * @master: the drm_master being destroyed + * + * All lessees will have been destroyed as they + * hold a reference on their lessor. Notify any + * lessor for this master so that it can check + * the list of lessees. + */ +void drm_lease_destroy(struct drm_master *master) +{ + struct drm_device *dev = master->dev; + + mutex_lock(&dev->mode_config.idr_mutex); + + DRM_DEBUG_LEASE("drm_lease_destroy %d\n", master->lessee_id); + + /* This master is referenced by all lessees, hence it cannot be destroyed + * until all of them have been + */ + WARN_ON(!list_empty(&master->lessees)); + + /* Remove this master from the lessee idr in the owner */ + if (master->lessee_id != 0) { + DRM_DEBUG_LEASE("remove master %d from device list of lessees\n", master->lessee_id); + idr_remove(&(drm_lease_owner(master)->lessee_idr), master->lessee_id); + } + + /* Remove this master from any lessee list it may be on */ + list_del(&master->lessee_list); + + mutex_unlock(&dev->mode_config.idr_mutex); + + if (master->lessor) { + /* Tell the master to check the lessee list */ + drm_sysfs_hotplug_event(dev); + drm_master_put(&master->lessor); + } + + DRM_DEBUG_LEASE("drm_lease_destroy done %d\n", master->lessee_id); +} + +/** + * _drm_lease_revoke - revoke access to all leased objects (idr_mutex held) + * @master: the master losing its lease + */ +static void _drm_lease_revoke(struct drm_master *top) +{ + int object; + void *entry; + struct drm_master *master = top; + + lockdep_assert_held(&top->dev->mode_config.idr_mutex); + + /* + * Walk the tree starting at 'top' emptying all leases. Because + * the tree is fully connected, we can do this without recursing + */ + for (;;) { + DRM_DEBUG_LEASE("revoke leases for %p %d\n", master, master->lessee_id); + + /* Evacuate the lease */ + idr_for_each_entry(&master->leases, entry, object) + idr_remove(&master->leases, object); + + /* Depth-first list walk */ + + /* Down */ + if (!list_empty(&master->lessees)) { + master = list_first_entry(&master->lessees, struct drm_master, lessee_list); + } else { + /* Up */ + while (master != top && master == list_last_entry(&master->lessor->lessees, struct drm_master, lessee_list)) + master = master->lessor; + + if (master == top) + break; + + /* Over */ + master = list_entry(master->lessee_list.next, struct drm_master, lessee_list); + } + } +} + +/** + * drm_lease_revoke - revoke access to all leased objects (idr_mutex not held) + * @top: the master losing its lease + */ +void drm_lease_revoke(struct drm_master *top) +{ + mutex_lock(&top->dev->mode_config.idr_mutex); + _drm_lease_revoke(top); + mutex_unlock(&top->dev->mode_config.idr_mutex); +} diff --git a/include/drm/drm_auth.h b/include/drm/drm_auth.h index 81a40c2a9a3e..86bff9841b54 100644 --- a/include/drm/drm_auth.h +++ b/include/drm/drm_auth.h @@ -52,6 +52,12 @@ struct drm_lock_data { * @dev: Link back to the DRM device * @lock: DRI1 lock information. * @driver_priv: Pointer to driver-private information. + * @lessor: Lease holder + * @lessee_id: id for lessees. Owners always have id 0 + * @lessee_list: other lessees of the same master + * @lessees: drm_masters leasing from this one + * @leases: Objects leased to this drm_master. + * @lessee_idr: All lessees under this owner (only used where lessor == NULL) * * Note that master structures are only relevant for the legacy/primary device * nodes, hence there can only be one per device, not one per drm_minor. @@ -76,10 +82,25 @@ struct drm_master { struct idr magic_map; struct drm_lock_data lock; void *driver_priv; + + /* Tree of display resource leases, each of which is a drm_master struct + * All of these get activated simultaneously, so drm_device master points + * at the top of the tree (for which lessor is NULL). Protected by + * &drm_device.mode_config.idr_mutex. + */ + + struct drm_master *lessor; + int lessee_id; + struct list_head lessee_list; + struct list_head lessees; + struct idr leases; + struct idr lessee_idr; }; struct drm_master *drm_master_get(struct drm_master *master); void drm_master_put(struct drm_master **master); bool drm_is_current_master(struct drm_file *fpriv); +struct drm_master *drm_master_create(struct drm_device *dev); + #endif diff --git a/include/drm/drm_lease.h b/include/drm/drm_lease.h new file mode 100644 index 000000000000..890018976a3c --- /dev/null +++ b/include/drm/drm_lease.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2017 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef _DRM_LEASE_H_ +#define _DRM_LEASE_H_ + +struct drm_file; +struct drm_device; +struct drm_master; + +struct drm_master *drm_lease_owner(struct drm_master *master); + +void drm_lease_destroy(struct drm_master *lessee); + +bool drm_lease_held(struct drm_file *file_priv, int id); + +bool _drm_lease_held(struct drm_file *file_priv, int id); + +void drm_lease_revoke(struct drm_master *master); + +uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs); + +uint32_t drm_lease_filter_encoders(struct drm_file *file_priv, uint32_t encoders); + +#endif /* _DRM_LEASE_H_ */ diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h index b2f920b518e3..c8155cb5a932 100644 --- a/include/drm/drm_mode_object.h +++ b/include/drm/drm_mode_object.h @@ -24,6 +24,7 @@ #define __DRM_MODESET_H__ #include +#include struct drm_object_properties; struct drm_property; struct drm_device; -- cgit v1.2.3-59-g8ed1b From 7de440db20d4531e4b740bf17b56afc426c54377 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 9 Apr 2017 22:35:34 -0600 Subject: drm: Check mode object lease status in all master ioctl paths [v4] Attempts to modify un-leased objects are rejected with an error. Information returned about unleased objects is modified to make them appear unusable and/or disconnected. Changes for v2 as suggested by Daniel Vetter : * With the change in the __drm_mode_object_find API to pass the file_priv along, we can now centralize most of the lease-based access checks in that function. * A few places skip that API and require in-line checks. Changes for v3 provided by Dave Airlie * remove support for leasing encoders. * add support for leasing planes. Changes for v4 * Only call drm_lease_held if DRIVER_MODESET. Signed-off-by: Keith Packard Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_auth.c | 2 +- drivers/gpu/drm/drm_encoder.c | 5 +++-- drivers/gpu/drm/drm_mode_config.c | 22 +++++++++++++--------- drivers/gpu/drm/drm_mode_object.c | 22 ++++++++++++++++++++++ drivers/gpu/drm/drm_plane.c | 18 +++++++++++------- drivers/gpu/drm/drm_vblank.c | 22 ++++++++++++++++++++-- include/drm/drm_lease.h | 2 -- 7 files changed, 70 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index 4f0e274f4111..aad468d170a7 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -310,7 +310,7 @@ out: */ bool drm_is_current_master(struct drm_file *fpriv) { - return fpriv->is_master && fpriv->master == fpriv->minor->dev->master; + return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master; } EXPORT_SYMBOL(drm_is_current_master); diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 43f644844b83..59e0ebe733f8 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -226,7 +226,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); crtc = drm_encoder_get_crtc(encoder); - if (crtc) + if (crtc && drm_lease_held(file_priv, crtc->base.id)) enc_resp->crtc_id = crtc->base.id; else enc_resp->crtc_id = 0; @@ -234,7 +234,8 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, enc_resp->encoder_type = encoder->encoder_type; enc_resp->encoder_id = encoder->base.id; - enc_resp->possible_crtcs = encoder->possible_crtcs; + enc_resp->possible_crtcs = drm_lease_filter_crtcs(file_priv, + encoder->possible_crtcs); enc_resp->possible_clones = encoder->possible_clones; return 0; diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 919e78d45ab0..cda8bfab6d3b 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -122,10 +122,12 @@ int drm_mode_getresources(struct drm_device *dev, void *data, count = 0; crtc_id = u64_to_user_ptr(card_res->crtc_id_ptr); drm_for_each_crtc(crtc, dev) { - if (count < card_res->count_crtcs && - put_user(crtc->base.id, crtc_id + count)) - return -EFAULT; - count++; + if (drm_lease_held(file_priv, crtc->base.id)) { + if (count < card_res->count_crtcs && + put_user(crtc->base.id, crtc_id + count)) + return -EFAULT; + count++; + } } card_res->count_crtcs = count; @@ -143,12 +145,14 @@ int drm_mode_getresources(struct drm_device *dev, void *data, count = 0; connector_id = u64_to_user_ptr(card_res->connector_id_ptr); drm_for_each_connector_iter(connector, &conn_iter) { - if (count < card_res->count_connectors && - put_user(connector->base.id, connector_id + count)) { - drm_connector_list_iter_end(&conn_iter); - return -EFAULT; + if (drm_lease_held(file_priv, connector->base.id)) { + if (count < card_res->count_connectors && + put_user(connector->base.id, connector_id + count)) { + drm_connector_list_iter_end(&conn_iter); + return -EFAULT; + } + count++; } - count++; } card_res->count_connectors = count; drm_connector_list_iter_end(&conn_iter); diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c index 240a05d91a53..d1599f36b605 100644 --- a/drivers/gpu/drm/drm_mode_object.c +++ b/drivers/gpu/drm/drm_mode_object.c @@ -104,6 +104,25 @@ void drm_mode_object_unregister(struct drm_device *dev, mutex_unlock(&dev->mode_config.idr_mutex); } +/** + * drm_lease_required - check types which must be leased to be used + * @type: type of object + * + * Returns whether the provided type of drm_mode_object must + * be owned or leased to be used by a process. + */ +static bool drm_lease_required(uint32_t type) +{ + switch(type) { + case DRM_MODE_OBJECT_CRTC: + case DRM_MODE_OBJECT_CONNECTOR: + case DRM_MODE_OBJECT_PLANE: + return true; + default: + return false; + } +} + struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, struct drm_file *file_priv, uint32_t id, uint32_t type) @@ -117,6 +136,9 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, if (obj && obj->id != id) obj = NULL; + if (obj && drm_lease_required(obj->type) && !_drm_lease_held(file_priv, obj->id)) + obj = NULL; + if (obj && obj->free_cb) { if (!kref_get_unless_zero(&obj->refcount)) obj = NULL; diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 8d9824804b0c..19404e34cd59 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -479,10 +479,12 @@ int drm_mode_getplane_res(struct drm_device *dev, void *data, !file_priv->universal_planes) continue; - if (count < plane_resp->count_planes && - put_user(plane->base.id, plane_ptr + count)) - return -EFAULT; - count++; + if (drm_lease_held(file_priv, plane->base.id)) { + if (count < plane_resp->count_planes && + put_user(plane->base.id, plane_ptr + count)) + return -EFAULT; + count++; + } } plane_resp->count_planes = count; @@ -504,9 +506,9 @@ int drm_mode_getplane(struct drm_device *dev, void *data, return -ENOENT; drm_modeset_lock(&plane->mutex, NULL); - if (plane->state && plane->state->crtc) + if (plane->state && plane->state->crtc && drm_lease_held(file_priv, plane->state->crtc->base.id)) plane_resp->crtc_id = plane->state->crtc->base.id; - else if (!plane->state && plane->crtc) + else if (!plane->state && plane->crtc && drm_lease_held(file_priv, plane->crtc->base.id)) plane_resp->crtc_id = plane->crtc->base.id; else plane_resp->crtc_id = 0; @@ -520,7 +522,9 @@ int drm_mode_getplane(struct drm_device *dev, void *data, drm_modeset_unlock(&plane->mutex); plane_resp->plane_id = plane->base.id; - plane_resp->possible_crtcs = plane->possible_crtcs; + plane_resp->possible_crtcs = drm_lease_filter_crtcs(file_priv, + plane->possible_crtcs); + plane_resp->gamma_size = 0; /* diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 13722c373a6a..57cc6e37c810 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -1447,10 +1447,12 @@ static void drm_wait_vblank_reply(struct drm_device *dev, unsigned int pipe, int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct drm_crtc *crtc; struct drm_vblank_crtc *vblank; union drm_wait_vblank *vblwait = data; int ret; u64 req_seq, seq; + unsigned int pipe_index; unsigned int flags, pipe, high_pipe; if (!dev->irq_enabled) @@ -1472,9 +1474,25 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK); if (high_pipe) - pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT; + pipe_index = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT; else - pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; + pipe_index = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; + + /* Convert lease-relative crtc index into global crtc index */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + pipe = 0; + drm_for_each_crtc(crtc, dev) { + if (drm_lease_held(file_priv, crtc->base.id)) { + if (pipe_index == 0) + break; + pipe_index--; + } + pipe++; + } + } else { + pipe = pipe_index; + } + if (pipe >= dev->num_crtcs) return -EINVAL; diff --git a/include/drm/drm_lease.h b/include/drm/drm_lease.h index 890018976a3c..6149e56ddbf3 100644 --- a/include/drm/drm_lease.h +++ b/include/drm/drm_lease.h @@ -31,6 +31,4 @@ void drm_lease_revoke(struct drm_master *master); uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs); -uint32_t drm_lease_filter_encoders(struct drm_file *file_priv, uint32_t encoders); - #endif /* _DRM_LEASE_H_ */ -- cgit v1.2.3-59-g8ed1b From 62884cd386b876638720ef88374b31a84ca7ee5f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 16 Mar 2017 17:56:28 -0700 Subject: drm: Add four ioctls for managing drm mode object leases [v7] drm_mode_create_lease Creates a lease for a list of drm mode objects, returning an fd for the new drm_master and a 64-bit identifier for the lessee drm_mode_list_lesees List the identifiers of the lessees for a master file drm_mode_get_lease List the leased objects for a master file drm_mode_revoke_lease Erase the set of objects managed by a lease. This should suffice to at least create and query leases. Changes for v2 as suggested by Daniel Vetter : * query ioctls only query the master associated with the provided file. * 'mask_lease' value has been removed * change ioctl has been removed. Changes for v3 suggested in part by Dave Airlie * Add revoke ioctl. Changes for v4 suggested by Dave Airlie * Expand on the comment about the magic use of &drm_lease_idr_object * Pad lease ioctl structures to align on 64-bit boundaries Changes for v5 suggested by Dave Airlie * Check for non-negative object_id in create_lease to avoid debug output from the kernel. Changes for v6 provided by Dave Airlie * For non-universal planes add primary/cursor planes to lease If we aren't exposing universal planes to this userspace client, and it requests a lease on a crtc, we should implicitly export the primary and cursor planes for the crtc. If the lessee doesn't request universal planes, it will just see the crtc, but if it does request them it will then see the plane objects as well. This also moves the object look ups earlier as a side effect, so we'd exit the ioctl quicker for non-existant objects. * Restrict leases to crtc/connector/planes. This only allows leasing for objects we wish to allow. Changes for v7 provided by Dave Airlie * Check pad args are 0 * Check create flags and object count are valid. * Check return from fd allocation * Refactor lease idr setup and add some simple validation * Use idr_mutex uniformly (Keith) Signed-off-by: Keith Packard Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_ioctl.c | 4 + drivers/gpu/drm/drm_lease.c | 412 ++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_mode_object.c | 5 +- include/drm/drm_lease.h | 12 ++ include/drm/drm_mode_object.h | 2 + include/uapi/drm/drm.h | 5 + include/uapi/drm/drm_mode.h | 66 ++++++ 7 files changed, 504 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 9c435a4c0c82..4aafe4802099 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -665,6 +665,10 @@ static const struct drm_ioctl_desc drm_ioctls[] = { DRM_UNLOCKED|DRM_RENDER_ALLOW), DRM_IOCTL_DEF(DRM_IOCTL_CRTC_GET_SEQUENCE, drm_crtc_get_sequence_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_CRTC_QUEUE_SEQUENCE, drm_crtc_queue_sequence_ioctl, DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_LEASE, drm_mode_create_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_LIST_LESSEES, drm_mode_list_lessees_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_GET_LEASE, drm_mode_get_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), }; #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) diff --git a/drivers/gpu/drm/drm_lease.c b/drivers/gpu/drm/drm_lease.c index 030db1170570..d1eb56a1eff4 100644 --- a/drivers/gpu/drm/drm_lease.c +++ b/drivers/gpu/drm/drm_lease.c @@ -23,6 +23,8 @@ #define drm_for_each_lessee(lessee, lessor) \ list_for_each_entry((lessee), &(lessor)->lessees, lessee_list) +static uint64_t drm_lease_idr_object; + /** * drm_lease_owner - return ancestor owner drm_master * @master: drm_master somewhere within tree of lessees and lessors @@ -353,3 +355,413 @@ void drm_lease_revoke(struct drm_master *top) _drm_lease_revoke(top); mutex_unlock(&top->dev->mode_config.idr_mutex); } + +static int validate_lease(struct drm_device *dev, + struct drm_file *lessor_priv, + int object_count, + struct drm_mode_object **objects) +{ + int o; + int has_crtc = -1; + int has_connector = -1; + int has_plane = -1; + + /* we want to confirm that there is at least one crtc, plane + connector object. */ + + for (o = 0; o < object_count; o++) { + if (objects[o]->type == DRM_MODE_OBJECT_CRTC && has_crtc == -1) { + has_crtc = o; + } + if (objects[o]->type == DRM_MODE_OBJECT_CONNECTOR && has_connector == -1) + has_connector = o; + + if (lessor_priv->universal_planes) { + if (objects[o]->type == DRM_MODE_OBJECT_PLANE && has_plane == -1) + has_plane = o; + } + } + if (has_crtc == -1 || has_connector == -1) + return -EINVAL; + if (lessor_priv->universal_planes && has_plane == -1) + return -EINVAL; + return 0; +} + +static int fill_object_idr(struct drm_device *dev, + struct drm_file *lessor_priv, + struct idr *leases, + int object_count, + u32 *object_ids) +{ + struct drm_mode_object **objects; + u32 o; + int ret; + objects = kcalloc(object_count, sizeof(struct drm_mode_object *), + GFP_KERNEL); + if (!objects) + return -ENOMEM; + + /* step one - get references to all the mode objects + and check for validity. */ + for (o = 0; o < object_count; o++) { + if ((int) object_ids[o] < 0) { + ret = -EINVAL; + goto out_free_objects; + } + + objects[o] = drm_mode_object_find(dev, lessor_priv, + object_ids[o], + DRM_MODE_OBJECT_ANY); + if (!objects[o]) { + ret = -ENOENT; + goto out_free_objects; + } + + if (!drm_mode_object_lease_required(objects[o]->type)) { + ret = -EINVAL; + goto out_free_objects; + } + } + + ret = validate_lease(dev, lessor_priv, object_count, objects); + if (ret) + goto out_free_objects; + + /* add their IDs to the lease request - taking into account + universal planes */ + for (o = 0; o < object_count; o++) { + struct drm_mode_object *obj = objects[o]; + u32 object_id = objects[o]->id; + DRM_DEBUG_LEASE("Adding object %d to lease\n", object_id); + + /* + * We're using an IDR to hold the set of leased + * objects, but we don't need to point at the object's + * data structure from the lease as the main crtc_idr + * will be used to actually find that. Instead, all we + * really want is a 'leased/not-leased' result, for + * which any non-NULL pointer will work fine. + */ + ret = idr_alloc(leases, &drm_lease_idr_object , object_id, object_id + 1, GFP_KERNEL); + if (ret < 0) { + DRM_DEBUG_LEASE("Object %d cannot be inserted into leases (%d)\n", + object_id, ret); + goto out_free_objects; + } + if (obj->type == DRM_MODE_OBJECT_CRTC && !lessor_priv->universal_planes) { + struct drm_crtc *crtc = obj_to_crtc(obj); + ret = idr_alloc(leases, &drm_lease_idr_object, crtc->primary->base.id, crtc->primary->base.id + 1, GFP_KERNEL); + if (ret < 0) { + DRM_DEBUG_LEASE("Object primary plane %d cannot be inserted into leases (%d)\n", + object_id, ret); + goto out_free_objects; + } + if (crtc->cursor) { + ret = idr_alloc(leases, &drm_lease_idr_object, crtc->cursor->base.id, crtc->cursor->base.id + 1, GFP_KERNEL); + if (ret < 0) { + DRM_DEBUG_LEASE("Object cursor plane %d cannot be inserted into leases (%d)\n", + object_id, ret); + goto out_free_objects; + } + } + } + } + + ret = 0; +out_free_objects: + for (o = 0; o < object_count; o++) { + if (objects[o]) + drm_mode_object_put(objects[o]); + } + kfree(objects); + return ret; +} + +/** + * drm_mode_create_lease_ioctl - create a new lease + * @dev: the drm device + * @data: pointer to struct drm_mode_create_lease + * @file_priv: the file being manipulated + * + * The master associated with the specified file will have a lease + * created containing the objects specified in the ioctl structure. + * A file descriptor will be allocated for that and returned to the + * application. + */ +int drm_mode_create_lease_ioctl(struct drm_device *dev, + void *data, struct drm_file *lessor_priv) +{ + struct drm_mode_create_lease *cl = data; + size_t object_count; + int ret = 0; + struct idr leases; + struct drm_master *lessor = lessor_priv->master; + struct drm_master *lessee = NULL; + struct file *lessee_file = NULL; + struct file *lessor_file = lessor_priv->filp; + struct drm_file *lessee_priv; + int fd = -1; + uint32_t *object_ids; + + /* Can't lease without MODESET */ + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + /* Do not allow sub-leases */ + if (lessor->lessor) + return -EINVAL; + + /* need some objects */ + if (cl->object_count == 0) + return -EINVAL; + + if (cl->flags && (cl->flags & ~(O_CLOEXEC | O_NONBLOCK))) + return -EINVAL; + + object_count = cl->object_count; + + object_ids = memdup_user(u64_to_user_ptr(cl->object_ids), object_count * sizeof(__u32)); + if (IS_ERR(object_ids)) + return PTR_ERR(object_ids); + + idr_init(&leases); + + /* fill and validate the object idr */ + ret = fill_object_idr(dev, lessor_priv, &leases, + object_count, object_ids); + kfree(object_ids); + if (ret) { + idr_destroy(&leases); + return ret; + } + + /* Allocate a file descriptor for the lease */ + fd = get_unused_fd_flags(cl->flags & (O_CLOEXEC | O_NONBLOCK)); + if (fd < 0) { + idr_destroy(&leases); + return fd; + } + + DRM_DEBUG_LEASE("Creating lease\n"); + lessee = drm_lease_create(lessor, &leases); + + if (IS_ERR(lessee)) { + ret = PTR_ERR(lessee); + goto out_leases; + } + + /* Clone the lessor file to create a new file for us */ + DRM_DEBUG_LEASE("Allocating lease file\n"); + path_get(&lessor_file->f_path); + lessee_file = alloc_file(&lessor_file->f_path, + lessor_file->f_mode, + fops_get(lessor_file->f_inode->i_fop)); + + if (IS_ERR(lessee_file)) { + ret = PTR_ERR(lessee_file); + goto out_lessee; + } + + /* Initialize the new file for DRM */ + DRM_DEBUG_LEASE("Initializing the file with %p\n", lessee_file->f_op->open); + ret = lessee_file->f_op->open(lessee_file->f_inode, lessee_file); + if (ret) + goto out_lessee_file; + + lessee_priv = lessee_file->private_data; + + /* Change the file to a master one */ + drm_master_put(&lessee_priv->master); + lessee_priv->master = lessee; + lessee_priv->is_master = 1; + lessee_priv->authenticated = 1; + + /* Hook up the fd */ + fd_install(fd, lessee_file); + + /* Pass fd back to userspace */ + DRM_DEBUG_LEASE("Returning fd %d id %d\n", fd, lessee->lessee_id); + cl->fd = fd; + cl->lessee_id = lessee->lessee_id; + + DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl succeeded\n"); + return 0; + +out_lessee_file: + fput(lessee_file); + +out_lessee: + drm_master_put(&lessee); + +out_leases: + put_unused_fd(fd); + idr_destroy(&leases); + + DRM_DEBUG_LEASE("drm_mode_create_lease_ioctl failed: %d\n", ret); + return ret; +} + +/** + * drm_mode_list_lessees_ioctl - list lessee ids + * @dev: the drm device + * @data: pointer to struct drm_mode_list_lessees + * @lessor_priv: the file being manipulated + * + * Starting from the master associated with the specified file, + * the master with the provided lessee_id is found, and then + * an array of lessee ids associated with leases from that master + * are returned. + */ + +int drm_mode_list_lessees_ioctl(struct drm_device *dev, + void *data, struct drm_file *lessor_priv) +{ + struct drm_mode_list_lessees *arg = data; + __u32 __user *lessee_ids = (__u32 __user *) (uintptr_t) (arg->lessees_ptr); + __u32 count_lessees = arg->count_lessees; + struct drm_master *lessor = lessor_priv->master, *lessee; + int count; + int ret = 0; + + if (arg->pad) + return -EINVAL; + + /* Can't lease without MODESET */ + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + DRM_DEBUG_LEASE("List lessees for %d\n", lessor->lessee_id); + + mutex_lock(&dev->mode_config.idr_mutex); + + count = 0; + drm_for_each_lessee(lessee, lessor) { + /* Only list un-revoked leases */ + if (!idr_is_empty(&lessee->leases)) { + if (count_lessees > count) { + DRM_DEBUG_LEASE("Add lessee %d\n", lessee->lessee_id); + ret = put_user(lessee->lessee_id, lessee_ids + count); + if (ret) + break; + } + count++; + } + } + + DRM_DEBUG_LEASE("Lessor leases to %d\n", count); + if (ret == 0) + arg->count_lessees = count; + + mutex_unlock(&dev->mode_config.idr_mutex); + + return ret; +} + +/** + * drm_mode_get_lease_ioctl - list leased objects + * @dev: the drm device + * @data: pointer to struct drm_mode_get_lease + * @file_priv: the file being manipulated + * + * Return the list of leased objects for the specified lessee + */ + +int drm_mode_get_lease_ioctl(struct drm_device *dev, + void *data, struct drm_file *lessee_priv) +{ + struct drm_mode_get_lease *arg = data; + __u32 __user *object_ids = (__u32 __user *) (uintptr_t) (arg->objects_ptr); + __u32 count_objects = arg->count_objects; + struct drm_master *lessee = lessee_priv->master; + struct idr *object_idr; + int count; + void *entry; + int object; + int ret = 0; + + if (arg->pad) + return -EINVAL; + + /* Can't lease without MODESET */ + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + DRM_DEBUG_LEASE("get lease for %d\n", lessee->lessee_id); + + mutex_lock(&dev->mode_config.idr_mutex); + + if (lessee->lessor == NULL) + /* owner can use all objects */ + object_idr = &lessee->dev->mode_config.crtc_idr; + else + /* lessee can only use allowed object */ + object_idr = &lessee->leases; + + count = 0; + idr_for_each_entry(object_idr, entry, object) { + if (count_objects > count) { + DRM_DEBUG_LEASE("adding object %d\n", object); + ret = put_user(object, object_ids + count); + if (ret) + break; + } + count++; + } + + DRM_DEBUG("lease holds %d objects\n", count); + if (ret == 0) + arg->count_objects = count; + + mutex_unlock(&dev->mode_config.idr_mutex); + + return ret; +} + +/** + * drm_mode_revoke_lease_ioctl - revoke lease + * @dev: the drm device + * @data: pointer to struct drm_mode_revoke_lease + * @file_priv: the file being manipulated + * + * This removes all of the objects from the lease without + * actually getting rid of the lease itself; that way all + * references to it still work correctly + */ +int drm_mode_revoke_lease_ioctl(struct drm_device *dev, + void *data, struct drm_file *lessor_priv) +{ + struct drm_mode_revoke_lease *arg = data; + struct drm_master *lessor = lessor_priv->master; + struct drm_master *lessee; + int ret = 0; + + DRM_DEBUG_LEASE("revoke lease for %d\n", arg->lessee_id); + + /* Can't lease without MODESET */ + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return -EINVAL; + + mutex_lock(&dev->mode_config.idr_mutex); + + lessee = _drm_find_lessee(lessor, arg->lessee_id); + + /* No such lessee */ + if (!lessee) { + ret = -ENOENT; + goto fail; + } + + /* Lease is not held by lessor */ + if (lessee->lessor != lessor) { + ret = -EACCES; + goto fail; + } + + _drm_lease_revoke(lessee); + +fail: + mutex_unlock(&dev->mode_config.idr_mutex); + + return ret; +} diff --git a/drivers/gpu/drm/drm_mode_object.c b/drivers/gpu/drm/drm_mode_object.c index d1599f36b605..7c8b2698c6a7 100644 --- a/drivers/gpu/drm/drm_mode_object.c +++ b/drivers/gpu/drm/drm_mode_object.c @@ -111,7 +111,7 @@ void drm_mode_object_unregister(struct drm_device *dev, * Returns whether the provided type of drm_mode_object must * be owned or leased to be used by a process. */ -static bool drm_lease_required(uint32_t type) +bool drm_mode_object_lease_required(uint32_t type) { switch(type) { case DRM_MODE_OBJECT_CRTC: @@ -136,7 +136,8 @@ struct drm_mode_object *__drm_mode_object_find(struct drm_device *dev, if (obj && obj->id != id) obj = NULL; - if (obj && drm_lease_required(obj->type) && !_drm_lease_held(file_priv, obj->id)) + if (obj && drm_mode_object_lease_required(obj->type) && + !_drm_lease_held(file_priv, obj->id)) obj = NULL; if (obj && obj->free_cb) { diff --git a/include/drm/drm_lease.h b/include/drm/drm_lease.h index 6149e56ddbf3..fbc0ab54855b 100644 --- a/include/drm/drm_lease.h +++ b/include/drm/drm_lease.h @@ -31,4 +31,16 @@ void drm_lease_revoke(struct drm_master *master); uint32_t drm_lease_filter_crtcs(struct drm_file *file_priv, uint32_t crtcs); +int drm_mode_create_lease_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +int drm_mode_list_lessees_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +int drm_mode_get_lease_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + +int drm_mode_revoke_lease_ioctl(struct drm_device *dev, + void *data, struct drm_file *file_priv); + #endif /* _DRM_LEASE_H_ */ diff --git a/include/drm/drm_mode_object.h b/include/drm/drm_mode_object.h index c8155cb5a932..7ba3913f30b5 100644 --- a/include/drm/drm_mode_object.h +++ b/include/drm/drm_mode_object.h @@ -154,4 +154,6 @@ int drm_object_property_get_value(struct drm_mode_object *obj, void drm_object_attach_property(struct drm_mode_object *obj, struct drm_property *property, uint64_t init_val); + +bool drm_mode_object_lease_required(uint32_t type); #endif diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 91d83c1747c0..6fdff5945c8a 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -888,6 +888,11 @@ extern "C" { #define DRM_IOCTL_SYNCOBJ_RESET DRM_IOWR(0xC4, struct drm_syncobj_array) #define DRM_IOCTL_SYNCOBJ_SIGNAL DRM_IOWR(0xC5, struct drm_syncobj_array) +#define DRM_IOCTL_MODE_CREATE_LEASE DRM_IOWR(0xC6, struct drm_mode_create_lease) +#define DRM_IOCTL_MODE_LIST_LESSEES DRM_IOWR(0xC7, struct drm_mode_list_lessees) +#define DRM_IOCTL_MODE_GET_LEASE DRM_IOWR(0xC8, struct drm_mode_get_lease) +#define DRM_IOCTL_MODE_REVOKE_LEASE DRM_IOWR(0xC9, struct drm_mode_revoke_lease) + /** * Device specific ioctls should only be in their respective headers * The device specific ioctl range is from 0x40 to 0x9f. diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 34b6bb34b002..5597a87154e5 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -782,6 +782,72 @@ struct drm_mode_destroy_blob { __u32 blob_id; }; +/** + * Lease mode resources, creating another drm_master. + */ +struct drm_mode_create_lease { + /** Pointer to array of object ids (__u32) */ + __u64 object_ids; + /** Number of object ids */ + __u32 object_count; + /** flags for new FD (O_CLOEXEC, etc) */ + __u32 flags; + + /** Return: unique identifier for lessee. */ + __u32 lessee_id; + /** Return: file descriptor to new drm_master file */ + __u32 fd; +}; + +/** + * List lesses from a drm_master + */ +struct drm_mode_list_lessees { + /** Number of lessees. + * On input, provides length of the array. + * On output, provides total number. No + * more than the input number will be written + * back, so two calls can be used to get + * the size and then the data. + */ + __u32 count_lessees; + __u32 pad; + + /** Pointer to lessees. + * pointer to __u64 array of lessee ids + */ + __u64 lessees_ptr; +}; + +/** + * Get leased objects + */ +struct drm_mode_get_lease { + /** Number of leased objects. + * On input, provides length of the array. + * On output, provides total number. No + * more than the input number will be written + * back, so two calls can be used to get + * the size and then the data. + */ + __u32 count_objects; + __u32 pad; + + /** Pointer to objects. + * pointer to __u32 array of object ids + */ + __u64 objects_ptr; +}; + +/** + * Revoke lease + */ +struct drm_mode_revoke_lease { + /** Unique ID of lessee + */ + __u32 lessee_id; +}; + #if defined(__cplusplus) } #endif -- cgit v1.2.3-59-g8ed1b From 24efd94bc38290dc1d9775a1e767ed4685d8a79b Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 20 Oct 2017 16:31:27 +0200 Subject: gpio: mmio: Make pin2mask() a private business The vtable call pin2mask() was introducing a vtable function call in every gpiochip callback for a generic MMIO GPIO chip. This was not exactly efficient. (Maybe link-time optimization could get rid of it, I don't know.) After removing all external calls into this API we can make it a boolean flag in the struct gpio_chip call and sink the function into the gpio-mmio driver yielding encapsulation and potential speedups. Cc: Anton Vorontsov Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mmio.c | 45 +++++++++++++++++++-------------------------- include/linux/gpio/driver.h | 8 ++++---- 2 files changed, 23 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index f7da40e46c55..63bb48878de1 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -126,20 +126,16 @@ static unsigned long bgpio_read32be(void __iomem *reg) return ioread32be(reg); } -static unsigned long bgpio_pin2mask(struct gpio_chip *gc, unsigned int pin) +static unsigned long bgpio_line2mask(struct gpio_chip *gc, unsigned int line) { - return BIT(pin); -} - -static unsigned long bgpio_pin2mask_be(struct gpio_chip *gc, - unsigned int pin) -{ - return BIT(gc->bgpio_bits - 1 - pin); + if (gc->be_bits) + return BIT(gc->bgpio_bits - 1 - line); + return BIT(line); } static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio) { - unsigned long pinmask = gc->pin2mask(gc, gpio); + unsigned long pinmask = bgpio_line2mask(gc, gpio); if (gc->bgpio_dir & pinmask) return !!(gc->read_reg(gc->reg_set) & pinmask); @@ -149,7 +145,7 @@ static int bgpio_get_set(struct gpio_chip *gc, unsigned int gpio) static int bgpio_get(struct gpio_chip *gc, unsigned int gpio) { - return !!(gc->read_reg(gc->reg_dat) & gc->pin2mask(gc, gpio)); + return !!(gc->read_reg(gc->reg_dat) & bgpio_line2mask(gc, gpio)); } static void bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) @@ -158,7 +154,7 @@ static void bgpio_set_none(struct gpio_chip *gc, unsigned int gpio, int val) static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) { - unsigned long mask = gc->pin2mask(gc, gpio); + unsigned long mask = bgpio_line2mask(gc, gpio); unsigned long flags; spin_lock_irqsave(&gc->bgpio_lock, flags); @@ -176,7 +172,7 @@ static void bgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, int val) { - unsigned long mask = gc->pin2mask(gc, gpio); + unsigned long mask = bgpio_line2mask(gc, gpio); if (val) gc->write_reg(gc->reg_set, mask); @@ -186,7 +182,7 @@ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio, static void bgpio_set_set(struct gpio_chip *gc, unsigned int gpio, int val) { - unsigned long mask = gc->pin2mask(gc, gpio); + unsigned long mask = bgpio_line2mask(gc, gpio); unsigned long flags; spin_lock_irqsave(&gc->bgpio_lock, flags); @@ -216,9 +212,9 @@ static void bgpio_multiple_get_masks(struct gpio_chip *gc, break; if (__test_and_clear_bit(i, mask)) { if (test_bit(i, bits)) - *set_mask |= gc->pin2mask(gc, i); + *set_mask |= bgpio_line2mask(gc, i); else - *clear_mask |= gc->pin2mask(gc, i); + *clear_mask |= bgpio_line2mask(gc, i); } } } @@ -294,7 +290,7 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) spin_lock_irqsave(&gc->bgpio_lock, flags); - gc->bgpio_dir &= ~gc->pin2mask(gc, gpio); + gc->bgpio_dir &= ~bgpio_line2mask(gc, gpio); gc->write_reg(gc->reg_dir, gc->bgpio_dir); spin_unlock_irqrestore(&gc->bgpio_lock, flags); @@ -305,7 +301,7 @@ static int bgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio) { /* Return 0 if output, 1 of input */ - return !(gc->read_reg(gc->reg_dir) & gc->pin2mask(gc, gpio)); + return !(gc->read_reg(gc->reg_dir) & bgpio_line2mask(gc, gpio)); } static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) @@ -316,7 +312,7 @@ static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) spin_lock_irqsave(&gc->bgpio_lock, flags); - gc->bgpio_dir |= gc->pin2mask(gc, gpio); + gc->bgpio_dir |= bgpio_line2mask(gc, gpio); gc->write_reg(gc->reg_dir, gc->bgpio_dir); spin_unlock_irqrestore(&gc->bgpio_lock, flags); @@ -330,7 +326,7 @@ static int bgpio_dir_in_inv(struct gpio_chip *gc, unsigned int gpio) spin_lock_irqsave(&gc->bgpio_lock, flags); - gc->bgpio_dir |= gc->pin2mask(gc, gpio); + gc->bgpio_dir |= bgpio_line2mask(gc, gpio); gc->write_reg(gc->reg_dir, gc->bgpio_dir); spin_unlock_irqrestore(&gc->bgpio_lock, flags); @@ -346,7 +342,7 @@ static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val) spin_lock_irqsave(&gc->bgpio_lock, flags); - gc->bgpio_dir &= ~gc->pin2mask(gc, gpio); + gc->bgpio_dir &= ~bgpio_line2mask(gc, gpio); gc->write_reg(gc->reg_dir, gc->bgpio_dir); spin_unlock_irqrestore(&gc->bgpio_lock, flags); @@ -357,12 +353,11 @@ static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val) static int bgpio_get_dir_inv(struct gpio_chip *gc, unsigned int gpio) { /* Return 0 if output, 1 if input */ - return !!(gc->read_reg(gc->reg_dir) & gc->pin2mask(gc, gpio)); + return !!(gc->read_reg(gc->reg_dir) & bgpio_line2mask(gc, gpio)); } static int bgpio_setup_accessors(struct device *dev, struct gpio_chip *gc, - bool bit_be, bool byte_be) { @@ -406,8 +401,6 @@ static int bgpio_setup_accessors(struct device *dev, return -EINVAL; } - gc->pin2mask = bit_be ? bgpio_pin2mask_be : bgpio_pin2mask; - return 0; } @@ -531,8 +524,8 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev, if (ret) return ret; - ret = bgpio_setup_accessors(dev, gc, flags & BGPIOF_BIG_ENDIAN, - flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER); + gc->be_bits = !!(flags & BGPIOF_BIG_ENDIAN); + ret = bgpio_setup_accessors(dev, gc, flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER); if (ret) return ret; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index bda95a9b7b8c..ed04fa2a00a8 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -67,9 +67,9 @@ struct module; * registers. * @read_reg: reader function for generic GPIO * @write_reg: writer function for generic GPIO - * @pin2mask: some generic GPIO controllers work with the big-endian bits - * notation, e.g. in a 8-bits register, GPIO7 is the least significant - * bit. This callback assigns the right bit mask. + * @be_bits: if the generic GPIO has big endian bit order (bit 31 is representing + * line 0, bit 30 is line 1 ... bit 0 is line 31) this is set to true by the + * generic GPIO core. It is for internal housekeeping only. * @reg_dat: data (in) register for generic GPIO * @reg_set: output set register (out=high) for generic GPIO * @reg_clr: output clear register (out=low) for generic GPIO @@ -151,7 +151,7 @@ struct gpio_chip { #if IS_ENABLED(CONFIG_GPIO_GENERIC) unsigned long (*read_reg)(void __iomem *reg); void (*write_reg)(void __iomem *reg, unsigned long data); - unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin); + bool be_bits; void __iomem *reg_dat; void __iomem *reg_set; void __iomem *reg_clr; -- cgit v1.2.3-59-g8ed1b From b4f34597a5ce148b88a47da621037537c384d565 Mon Sep 17 00:00:00 2001 From: Noa Osherovich Date: Tue, 17 Oct 2017 18:01:12 +0300 Subject: IB/mlx5: Expose multi-packet RQ capabilities This patch reports the device's striding RQ capabilities to the user-space: - min/max_single_stride_log_num_of_bytes: Log of min/max number of bytes in a single stride. - min/max_single_wqe_log_num_of_strides: Log of min/max number of strides in a single WQE. - supported_qpts: A bit mask to know which QP types support multi- packet RQ, for now only Raw Packet QPs. Signed-off-by: Noa Osherovich Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- drivers/infiniband/hw/mlx5/main.c | 16 ++++++++++++++++ drivers/infiniband/hw/mlx5/mlx5_ib.h | 5 +++++ include/uapi/rdma/mlx5-abi.h | 14 ++++++++++++++ 3 files changed, 35 insertions(+) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 552f7bd4ecc3..02da3f58f296 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -848,6 +848,22 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, } } + if (field_avail(typeof(resp), striding_rq_caps, uhw->outlen)) { + resp.response_length += sizeof(resp.striding_rq_caps); + if (MLX5_CAP_GEN(mdev, striding_rq)) { + resp.striding_rq_caps.min_single_stride_log_num_of_bytes = + MLX5_MIN_SINGLE_STRIDE_LOG_NUM_BYTES; + resp.striding_rq_caps.max_single_stride_log_num_of_bytes = + MLX5_MAX_SINGLE_STRIDE_LOG_NUM_BYTES; + resp.striding_rq_caps.min_single_wqe_log_num_of_strides = + MLX5_MIN_SINGLE_WQE_LOG_NUM_STRIDES; + resp.striding_rq_caps.max_single_wqe_log_num_of_strides = + MLX5_MAX_SINGLE_WQE_LOG_NUM_STRIDES; + resp.striding_rq_caps.supported_qpts = + BIT(IB_QPT_RAW_PACKET); + } + } + if (uhw->outlen) { err = ib_copy_to_udata(uhw, &resp, resp.response_length); diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 189e80cd6b2f..8de40852818b 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -256,6 +256,11 @@ enum mlx5_ib_wq_flags { MLX5_IB_WQ_FLAGS_DELAY_DROP = 0x1, }; +#define MLX5_MIN_SINGLE_WQE_LOG_NUM_STRIDES 9 +#define MLX5_MAX_SINGLE_WQE_LOG_NUM_STRIDES 16 +#define MLX5_MIN_SINGLE_STRIDE_LOG_NUM_BYTES 6 +#define MLX5_MAX_SINGLE_STRIDE_LOG_NUM_BYTES 13 + struct mlx5_ib_rwq { struct ib_wq ibwq; struct mlx5_core_qp core_qp; diff --git a/include/uapi/rdma/mlx5-abi.h b/include/uapi/rdma/mlx5-abi.h index 1791bf123ba9..0832d9502200 100644 --- a/include/uapi/rdma/mlx5-abi.h +++ b/include/uapi/rdma/mlx5-abi.h @@ -190,6 +190,19 @@ struct mlx5_ib_sw_parsing_caps { __u32 supported_qpts; }; +struct mlx5_ib_striding_rq_caps { + __u32 min_single_stride_log_num_of_bytes; + __u32 max_single_stride_log_num_of_bytes; + __u32 min_single_wqe_log_num_of_strides; + __u32 max_single_wqe_log_num_of_strides; + + /* Corresponding bit will be set if qp type from + * 'enum ib_qp_type' is supported, e.g. + * supported_qpts |= 1 << IB_QPT_RAW_PACKET + */ + __u32 supported_qpts; +}; + struct mlx5_ib_query_device_resp { __u32 comp_mask; __u32 response_length; @@ -200,6 +213,7 @@ struct mlx5_ib_query_device_resp { __u32 mlx5_ib_support_multi_pkt_send_wqes; __u32 reserved; struct mlx5_ib_sw_parsing_caps sw_parsing_caps; + struct mlx5_ib_striding_rq_caps striding_rq_caps; }; struct mlx5_ib_create_cq { -- cgit v1.2.3-59-g8ed1b From ccc8708790273811db24676223b040710793cba7 Mon Sep 17 00:00:00 2001 From: Noa Osherovich Date: Tue, 17 Oct 2017 18:01:13 +0300 Subject: IB/mlx5: Allow creation of a multi-packet RQ Allow creation of a multi-packet receive queue. In order to create a multi-packet RQ, the following fields in the mlx5_ib_rwq should be set: - log_num_strides: Log of number of strides per WQE - single_stride_log_num_of_bytes: Log of a single stride size - two_byte_shift_en: When enabled, hardware pads 2 bytes of zeros before writing the message to memory (e.g. for the IP alignment). Signed-off-by: Noa Osherovich Reviewed-by: Majd Dibbiny Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- drivers/infiniband/hw/mlx5/mlx5_ib.h | 4 +++ drivers/infiniband/hw/mlx5/qp.c | 52 ++++++++++++++++++++++++++++++------ include/linux/mlx5/mlx5_ifc.h | 1 + include/uapi/rdma/mlx5-abi.h | 8 +++++- 4 files changed, 56 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 8de40852818b..e7deaa08535b 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -254,6 +254,7 @@ struct mlx5_ib_wq { enum mlx5_ib_wq_flags { MLX5_IB_WQ_FLAGS_DELAY_DROP = 0x1, + MLX5_IB_WQ_FLAGS_STRIDING_RQ = 0x2, }; #define MLX5_MIN_SINGLE_WQE_LOG_NUM_STRIDES 9 @@ -269,6 +270,9 @@ struct mlx5_ib_rwq { u32 log_rq_size; u32 rq_page_offset; u32 log_page_size; + u32 log_num_strides; + u32 two_byte_shift_en; + u32 single_stride_log_num_of_bytes; struct ib_umem *umem; size_t buf_size; unsigned int page_shift; diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 37a0976240fd..d209c684d729 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -4706,9 +4706,19 @@ static int create_rq(struct mlx5_ib_rwq *rwq, struct ib_pd *pd, MLX5_SET(rqc, rqc, state, MLX5_RQC_STATE_RST); MLX5_SET(rqc, rqc, flush_in_error_en, 1); wq = MLX5_ADDR_OF(rqc, rqc, wq); - MLX5_SET(wq, wq, wq_type, MLX5_WQ_TYPE_CYCLIC); + MLX5_SET(wq, wq, wq_type, + rwq->create_flags & MLX5_IB_WQ_FLAGS_STRIDING_RQ ? + MLX5_WQ_TYPE_CYCLIC_STRIDING_RQ : MLX5_WQ_TYPE_CYCLIC); MLX5_SET(wq, wq, end_padding_mode, MLX5_WQ_END_PAD_MODE_ALIGN); MLX5_SET(wq, wq, log_wq_stride, rwq->log_rq_stride); + if (rwq->create_flags & MLX5_IB_WQ_FLAGS_STRIDING_RQ) { + MLX5_SET(wq, wq, two_byte_shift_en, rwq->two_byte_shift_en); + MLX5_SET(wq, wq, log_wqe_stride_size, + rwq->single_stride_log_num_of_bytes - + MLX5_MIN_SINGLE_STRIDE_LOG_NUM_BYTES); + MLX5_SET(wq, wq, log_wqe_num_of_strides, rwq->log_num_strides - + MLX5_MIN_SINGLE_WQE_LOG_NUM_STRIDES); + } MLX5_SET(wq, wq, log_wq_sz, rwq->log_rq_size); MLX5_SET(wq, wq, pd, to_mpd(pd)->pdn); MLX5_SET(wq, wq, page_offset, rwq->rq_page_offset); @@ -4790,7 +4800,8 @@ static int prepare_user_rq(struct ib_pd *pd, int err; size_t required_cmd_sz; - required_cmd_sz = offsetof(typeof(ucmd), reserved) + sizeof(ucmd.reserved); + required_cmd_sz = offsetof(typeof(ucmd), single_stride_log_num_of_bytes) + + sizeof(ucmd.single_stride_log_num_of_bytes); if (udata->inlen < required_cmd_sz) { mlx5_ib_dbg(dev, "invalid inlen\n"); return -EINVAL; @@ -4808,14 +4819,39 @@ static int prepare_user_rq(struct ib_pd *pd, return -EFAULT; } - if (ucmd.comp_mask) { + if (ucmd.comp_mask & (~MLX5_IB_CREATE_WQ_STRIDING_RQ)) { mlx5_ib_dbg(dev, "invalid comp mask\n"); return -EOPNOTSUPP; - } - - if (ucmd.reserved) { - mlx5_ib_dbg(dev, "invalid reserved\n"); - return -EOPNOTSUPP; + } else if (ucmd.comp_mask & MLX5_IB_CREATE_WQ_STRIDING_RQ) { + if (!MLX5_CAP_GEN(dev->mdev, striding_rq)) { + mlx5_ib_dbg(dev, "Striding RQ is not supported\n"); + return -EOPNOTSUPP; + } + if ((ucmd.single_stride_log_num_of_bytes < + MLX5_MIN_SINGLE_STRIDE_LOG_NUM_BYTES) || + (ucmd.single_stride_log_num_of_bytes > + MLX5_MAX_SINGLE_STRIDE_LOG_NUM_BYTES)) { + mlx5_ib_dbg(dev, "Invalid log stride size (%u. Range is %u - %u)\n", + ucmd.single_stride_log_num_of_bytes, + MLX5_MIN_SINGLE_STRIDE_LOG_NUM_BYTES, + MLX5_MAX_SINGLE_STRIDE_LOG_NUM_BYTES); + return -EINVAL; + } + if ((ucmd.single_wqe_log_num_of_strides > + MLX5_MAX_SINGLE_WQE_LOG_NUM_STRIDES) || + (ucmd.single_wqe_log_num_of_strides < + MLX5_MIN_SINGLE_WQE_LOG_NUM_STRIDES)) { + mlx5_ib_dbg(dev, "Invalid log num strides (%u. Range is %u - %u)\n", + ucmd.single_wqe_log_num_of_strides, + MLX5_MIN_SINGLE_WQE_LOG_NUM_STRIDES, + MLX5_MAX_SINGLE_WQE_LOG_NUM_STRIDES); + return -EINVAL; + } + rwq->single_stride_log_num_of_bytes = + ucmd.single_stride_log_num_of_bytes; + rwq->log_num_strides = ucmd.single_wqe_log_num_of_strides; + rwq->two_byte_shift_en = !!ucmd.two_byte_shift_en; + rwq->create_flags |= MLX5_IB_WQ_FLAGS_STRIDING_RQ; } err = set_user_rq_size(dev, init_attr, &ucmd, rwq); diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 69772347f866..db655db45b77 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -744,6 +744,7 @@ enum { MLX5_WQ_TYPE_LINKED_LIST = 0x0, MLX5_WQ_TYPE_CYCLIC = 0x1, MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ = 0x2, + MLX5_WQ_TYPE_CYCLIC_STRIDING_RQ = 0x3, }; enum { diff --git a/include/uapi/rdma/mlx5-abi.h b/include/uapi/rdma/mlx5-abi.h index 0832d9502200..b1d5b87ba3fd 100644 --- a/include/uapi/rdma/mlx5-abi.h +++ b/include/uapi/rdma/mlx5-abi.h @@ -308,6 +308,10 @@ struct mlx5_ib_alloc_mw { __u16 reserved2; }; +enum mlx5_ib_create_wq_mask { + MLX5_IB_CREATE_WQ_STRIDING_RQ = (1 << 0), +}; + struct mlx5_ib_create_wq { __u64 buf_addr; __u64 db_addr; @@ -316,7 +320,9 @@ struct mlx5_ib_create_wq { __u32 user_index; __u32 flags; __u32 comp_mask; - __u32 reserved; + __u32 single_stride_log_num_of_bytes; + __u32 single_wqe_log_num_of_strides; + __u32 two_byte_shift_en; }; struct mlx5_ib_create_ah_resp { -- cgit v1.2.3-59-g8ed1b From 0ff8e79ca7c81fafa3f5c91a1b58efc85cbc2302 Mon Sep 17 00:00:00 2001 From: Guy Levi Date: Thu, 19 Oct 2017 08:25:51 +0300 Subject: IB/mlx5: Add 128B CQE compression and padding HW bits Adding new bits in mlx5_ifc_cmd_hca_cap to get the hardware capabilities for: - compression_128: Support 128B CQE compression - cqe_128_always: Support 128B CQE padding Signed-off-by: Guy Levi Reviewed-by: Mark Bloch Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- include/linux/mlx5/mlx5_ifc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index db655db45b77..d49928c314ed 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -1047,7 +1047,9 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 num_of_uars_per_page[0x20]; u8 reserved_at_540[0x40]; - u8 reserved_at_580[0x3f]; + u8 reserved_at_580[0x3d]; + u8 cqe_128_always[0x1]; + u8 cqe_compression_128[0x1]; u8 cqe_compression[0x1]; u8 cqe_compression_timeout[0x10]; -- cgit v1.2.3-59-g8ed1b From de57f2ad06d5bf01015b955600cbfc77059b2b6e Mon Sep 17 00:00:00 2001 From: Guy Levi Date: Thu, 19 Oct 2017 08:25:52 +0300 Subject: IB/mlx5: Support 128B CQE compression feature In commit 1cbe6fc86ccf ("IB/mlx5: Add support for CQE compressing") the concept of CQE compression was introduced and added a support for 64B CQE size. This change update the code to support 128B CQE size as well. Signed-off-by: Guy Levi Reviewed-by: Mark Bloch Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- drivers/infiniband/hw/mlx5/cq.c | 6 ++++-- drivers/infiniband/hw/mlx5/main.c | 8 ++++++-- include/uapi/rdma/mlx5-abi.h | 7 ++++++- 3 files changed, 16 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index b8a116d0e063..51871f049c57 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -804,8 +804,10 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata, *index = to_mucontext(context)->bfregi.sys_pages[0]; if (ucmd.cqe_comp_en == 1) { - if (unlikely((*cqe_size != 64) || - !MLX5_CAP_GEN(dev->mdev, cqe_compression))) { + if (!((*cqe_size == 128 && + MLX5_CAP_GEN(dev->mdev, cqe_compression_128)) || + (*cqe_size == 64 && + MLX5_CAP_GEN(dev->mdev, cqe_compression)))) { err = -EOPNOTSUPP; mlx5_ib_warn(dev, "CQE compression is not supported for size %d!\n", *cqe_size); diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 02da3f58f296..b9337562aa90 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -824,8 +824,12 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, sizeof(resp.mlx5_ib_support_multi_pkt_send_wqes); } - if (field_avail(typeof(resp), reserved, uhw->outlen)) - resp.response_length += sizeof(resp.reserved); + if (field_avail(typeof(resp), flags, uhw->outlen)) { + resp.response_length += sizeof(resp.flags); + if (MLX5_CAP_GEN(mdev, cqe_compression_128)) + resp.flags |= + MLX5_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_COMP; + } if (field_avail(typeof(resp), sw_parsing_caps, uhw->outlen)) { diff --git a/include/uapi/rdma/mlx5-abi.h b/include/uapi/rdma/mlx5-abi.h index b1d5b87ba3fd..a8fc1f0956d0 100644 --- a/include/uapi/rdma/mlx5-abi.h +++ b/include/uapi/rdma/mlx5-abi.h @@ -203,6 +203,11 @@ struct mlx5_ib_striding_rq_caps { __u32 supported_qpts; }; +enum mlx5_ib_query_dev_resp_flags { + /* Support 128B CQE compression */ + MLX5_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_COMP = 1 << 0, +}; + struct mlx5_ib_query_device_resp { __u32 comp_mask; __u32 response_length; @@ -211,7 +216,7 @@ struct mlx5_ib_query_device_resp { struct mlx5_ib_cqe_comp_caps cqe_comp_caps; struct mlx5_packet_pacing_caps packet_pacing_caps; __u32 mlx5_ib_support_multi_pkt_send_wqes; - __u32 reserved; + __u32 flags; /* Use enum mlx5_ib_query_dev_resp_flags */ struct mlx5_ib_sw_parsing_caps sw_parsing_caps; struct mlx5_ib_striding_rq_caps striding_rq_caps; }; -- cgit v1.2.3-59-g8ed1b From 7a0c8f4244e9ec7a630563d294b211342b46223d Mon Sep 17 00:00:00 2001 From: Guy Levi Date: Thu, 19 Oct 2017 08:25:53 +0300 Subject: IB/mlx5: Support padded 128B CQE feature In some benchmarks and some CPU architectures, writing the CQE on a full cache line size improves performance by saving memory access operations (read-modify-write) relative to partial cache line change. This patch lets the user to configure the device to pad the CQE up to 128B in case its content is less than 128B. Currently the driver supports only padding for a CQE size of 128B. Signed-off-by: Guy Levi Reviewed-by: Mark Bloch Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- drivers/infiniband/hw/mlx5/cq.c | 27 +++++++++++++++++++++++---- drivers/infiniband/hw/mlx5/main.c | 4 ++++ drivers/infiniband/hw/mlx5/mlx5_ib.h | 5 +++++ include/linux/mlx5/cq.h | 6 ++++-- include/uapi/rdma/mlx5-abi.h | 7 ++++++- 5 files changed, 42 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index 51871f049c57..01b218a3c277 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -754,13 +754,13 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata, int err; ucmdlen = udata->inlen < sizeof(ucmd) ? - (sizeof(ucmd) - sizeof(ucmd.reserved)) : sizeof(ucmd); + (sizeof(ucmd) - sizeof(ucmd.flags)) : sizeof(ucmd); if (ib_copy_from_udata(&ucmd, udata, ucmdlen)) return -EFAULT; if (ucmdlen == sizeof(ucmd) && - ucmd.reserved != 0) + (ucmd.flags & ~(MLX5_IB_CREATE_CQ_FLAGS_CQE_128B_PAD))) return -EINVAL; if (ucmd.cqe_size != 64 && ucmd.cqe_size != 128) @@ -830,6 +830,19 @@ static int create_cq_user(struct mlx5_ib_dev *dev, struct ib_udata *udata, ilog2(ucmd.cqe_comp_res_format)); } + if (ucmd.flags & MLX5_IB_CREATE_CQ_FLAGS_CQE_128B_PAD) { + if (*cqe_size != 128 || + !MLX5_CAP_GEN(dev->mdev, cqe_128_always)) { + err = -EOPNOTSUPP; + mlx5_ib_warn(dev, + "CQE padding is not supported for CQE size of %dB!\n", + *cqe_size); + goto err_cqb; + } + + cq->private_flags |= MLX5_IB_CQ_PR_FLAGS_CQE_128_PAD; + } + return 0; err_cqb: @@ -989,7 +1002,10 @@ struct ib_cq *mlx5_ib_create_cq(struct ib_device *ibdev, cq->cqe_size = cqe_size; cqc = MLX5_ADDR_OF(create_cq_in, cqb, cq_context); - MLX5_SET(cqc, cqc, cqe_sz, cqe_sz_to_mlx_sz(cqe_size)); + MLX5_SET(cqc, cqc, cqe_sz, + cqe_sz_to_mlx_sz(cqe_size, + cq->private_flags & + MLX5_IB_CQ_PR_FLAGS_CQE_128_PAD)); MLX5_SET(cqc, cqc, log_cq_size, ilog2(entries)); MLX5_SET(cqc, cqc, uar_page, index); MLX5_SET(cqc, cqc, c_eqn, eqn); @@ -1339,7 +1355,10 @@ int mlx5_ib_resize_cq(struct ib_cq *ibcq, int entries, struct ib_udata *udata) MLX5_SET(cqc, cqc, log_page_size, page_shift - MLX5_ADAPTER_PAGE_SHIFT); - MLX5_SET(cqc, cqc, cqe_sz, cqe_sz_to_mlx_sz(cqe_size)); + MLX5_SET(cqc, cqc, cqe_sz, + cqe_sz_to_mlx_sz(cqe_size, + cq->private_flags & + MLX5_IB_CQ_PR_FLAGS_CQE_128_PAD)); MLX5_SET(cqc, cqc, log_cq_size, ilog2(entries)); MLX5_SET(modify_cq_in, in, op_mod, MLX5_CQ_OPMOD_RESIZE); diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index b9337562aa90..1edd41e3be1b 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -826,9 +826,13 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, if (field_avail(typeof(resp), flags, uhw->outlen)) { resp.response_length += sizeof(resp.flags); + if (MLX5_CAP_GEN(mdev, cqe_compression_128)) resp.flags |= MLX5_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_COMP; + + if (MLX5_CAP_GEN(mdev, cqe_128_always)) + resp.flags |= MLX5_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_PAD; } if (field_avail(typeof(resp), sw_parsing_caps, diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index e7deaa08535b..137f2116911f 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -444,6 +444,10 @@ struct mlx5_shared_mr_info { struct ib_umem *umem; }; +enum mlx5_ib_cq_pr_flags { + MLX5_IB_CQ_PR_FLAGS_CQE_128_PAD = 1 << 0, +}; + struct mlx5_ib_cq { struct ib_cq ibcq; struct mlx5_core_cq mcq; @@ -466,6 +470,7 @@ struct mlx5_ib_cq { struct list_head wc_list; enum ib_cq_notify_flags notify_flags; struct work_struct notify_work; + u16 private_flags; /* Use mlx5_ib_cq_pr_flags */ }; struct mlx5_ib_wc { diff --git a/include/linux/mlx5/cq.h b/include/linux/mlx5/cq.h index 95898847c7d4..cc718e245b1e 100644 --- a/include/linux/mlx5/cq.h +++ b/include/linux/mlx5/cq.h @@ -125,11 +125,13 @@ struct mlx5_cq_modify_params { enum { CQE_SIZE_64 = 0, CQE_SIZE_128 = 1, + CQE_SIZE_128_PAD = 2, }; -static inline int cqe_sz_to_mlx_sz(u8 size) +static inline int cqe_sz_to_mlx_sz(u8 size, int padding_128_en) { - return size == 64 ? CQE_SIZE_64 : CQE_SIZE_128; + return padding_128_en ? CQE_SIZE_128_PAD : + size == 64 ? CQE_SIZE_64 : CQE_SIZE_128; } static inline void mlx5_cq_set_ci(struct mlx5_core_cq *cq) diff --git a/include/uapi/rdma/mlx5-abi.h b/include/uapi/rdma/mlx5-abi.h index a8fc1f0956d0..201a60f032dd 100644 --- a/include/uapi/rdma/mlx5-abi.h +++ b/include/uapi/rdma/mlx5-abi.h @@ -206,6 +206,7 @@ struct mlx5_ib_striding_rq_caps { enum mlx5_ib_query_dev_resp_flags { /* Support 128B CQE compression */ MLX5_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_COMP = 1 << 0, + MLX5_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_PAD = 1 << 1, }; struct mlx5_ib_query_device_resp { @@ -221,13 +222,17 @@ struct mlx5_ib_query_device_resp { struct mlx5_ib_striding_rq_caps striding_rq_caps; }; +enum mlx5_ib_create_cq_flags { + MLX5_IB_CREATE_CQ_FLAGS_CQE_128B_PAD = 1 << 0, +}; + struct mlx5_ib_create_cq { __u64 buf_addr; __u64 db_addr; __u32 cqe_size; __u8 cqe_comp_en; __u8 cqe_comp_res_format; - __u16 reserved; /* explicit padding (optional on i386) */ + __u16 flags; }; struct mlx5_ib_create_cq_resp { -- cgit v1.2.3-59-g8ed1b From 4d350f1f89ee8ea84be1ef09717bf392fa5a3b45 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 19 Oct 2017 08:25:54 +0300 Subject: IB/mlx5: Update tunnel offloads bits This patch updates the mlx5_ifc with the following: - Fix tunnel_stateless_gre typo. - max_geneve_opt_len - Maximum geneve options length. - tunnel_stateless_geneve_rx - If set, receive Stateless Offloads for Geneve tunneled (inner) packets are supported. Signed-off-by: Maor Gottlieb Reviewed-by: Mark Bloch Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- include/linux/mlx5/mlx5_ifc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index d49928c314ed..d4c29c183d28 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -614,7 +614,9 @@ struct mlx5_ifc_per_protocol_networking_offload_caps_bits { u8 swp[0x1]; u8 swp_csum[0x1]; u8 swp_lso[0x1]; - u8 reserved_at_23[0x1d]; + u8 reserved_at_23[0x1b]; + u8 max_geneve_opt_len[0x1]; + u8 tunnel_stateless_geneve_rx[0x1]; u8 reserved_at_40[0x10]; u8 lro_min_mss_size[0x10]; -- cgit v1.2.3-59-g8ed1b From f95ef6cbae61fa1dd563f5c0f6a0e5b512fda5ba Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 19 Oct 2017 08:25:55 +0300 Subject: IB/mlx5: Add tunneling offloads support The device can support receive Stateless Offloads for the inner packet's fields only when the packet is processed by TIR which is enabled to support tunneling. Otherwise, the device treats the packet as an ordinary non-tunneling packet and receive offloads can be done only for the outer packet's field. In order to enable receive Stateless Offloading support for incoming tunneling traffic the TIR should be created with tunneled_offload_en. Tunneling offloads is supported only be raw ethernet QP. This patch includes: * New QP creation flag for tunneling offloads. * Reports device capabilities. Signed-off-by: Maor Gottlieb Reviewed-by: Mark Bloch Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- drivers/infiniband/hw/mlx5/main.c | 14 +++++++++++++ drivers/infiniband/hw/mlx5/mlx5_ib.h | 3 +++ drivers/infiniband/hw/mlx5/qp.c | 39 +++++++++++++++++++++++++++++++----- include/uapi/rdma/mlx5-abi.h | 11 +++++++++- 4 files changed, 61 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 1edd41e3be1b..260f8be1d0ed 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -872,6 +872,20 @@ static int mlx5_ib_query_device(struct ib_device *ibdev, } } + if (field_avail(typeof(resp), tunnel_offloads_caps, + uhw->outlen)) { + resp.response_length += sizeof(resp.tunnel_offloads_caps); + if (MLX5_CAP_ETH(mdev, tunnel_stateless_vxlan)) + resp.tunnel_offloads_caps |= + MLX5_IB_TUNNELED_OFFLOADS_VXLAN; + if (MLX5_CAP_ETH(mdev, tunnel_stateless_geneve_rx)) + resp.tunnel_offloads_caps |= + MLX5_IB_TUNNELED_OFFLOADS_GENEVE; + if (MLX5_CAP_ETH(mdev, tunnel_stateless_gre)) + resp.tunnel_offloads_caps |= + MLX5_IB_TUNNELED_OFFLOADS_GRE; + } + if (uhw->outlen) { err = ib_copy_to_udata(uhw, &resp, resp.response_length); diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 137f2116911f..0a328d6c6494 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -398,6 +398,7 @@ struct mlx5_ib_qp { struct list_head cq_send_list; u32 rate_limit; u32 underlay_qpn; + bool tunnel_offload_en; }; struct mlx5_ib_cq_buf { @@ -420,6 +421,8 @@ enum mlx5_ib_qp_flags { MLX5_IB_QP_RSS = 1 << 8, MLX5_IB_QP_CVLAN_STRIPPING = 1 << 9, MLX5_IB_QP_UNDERLAY = 1 << 10, + /* Reserved for PCI_WRITE_PAD = 1 << 11, */ + MLX5_IB_QP_TUNNEL_OFFLOAD = 1 << 12, }; struct mlx5_umr_wr { diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index d209c684d729..53bb0d5cad3d 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -1204,8 +1204,16 @@ static void destroy_raw_packet_qp_rq(struct mlx5_ib_dev *dev, mlx5_core_destroy_rq_tracked(dev->mdev, &rq->base.mqp); } +static bool tunnel_offload_supported(struct mlx5_core_dev *dev) +{ + return (MLX5_CAP_ETH(dev, tunnel_stateless_vxlan) || + MLX5_CAP_ETH(dev, tunnel_stateless_gre) || + MLX5_CAP_ETH(dev, tunnel_stateless_geneve_rx)); +} + static int create_raw_packet_qp_tir(struct mlx5_ib_dev *dev, - struct mlx5_ib_rq *rq, u32 tdn) + struct mlx5_ib_rq *rq, u32 tdn, + bool tunnel_offload_en) { u32 *in; void *tirc; @@ -1221,6 +1229,8 @@ static int create_raw_packet_qp_tir(struct mlx5_ib_dev *dev, MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_DIRECT); MLX5_SET(tirc, tirc, inline_rqn, rq->base.mqp.qpn); MLX5_SET(tirc, tirc, transport_domain, tdn); + if (tunnel_offload_en) + MLX5_SET(tirc, tirc, tunneled_offload_en, 1); err = mlx5_core_create_tir(dev->mdev, in, inlen, &rq->tirn); @@ -1271,7 +1281,8 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, goto err_destroy_sq; - err = create_raw_packet_qp_tir(dev, rq, tdn); + err = create_raw_packet_qp_tir(dev, rq, tdn, + qp->tunnel_offload_en); if (err) goto err_destroy_rq; } @@ -1358,7 +1369,7 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, if (udata->outlen < min_resp_len) return -EINVAL; - required_cmd_sz = offsetof(typeof(ucmd), reserved1) + sizeof(ucmd.reserved1); + required_cmd_sz = offsetof(typeof(ucmd), flags) + sizeof(ucmd.flags); if (udata->inlen < required_cmd_sz) { mlx5_ib_dbg(dev, "invalid inlen\n"); return -EINVAL; @@ -1381,8 +1392,14 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, return -EOPNOTSUPP; } - if (memchr_inv(ucmd.reserved, 0, sizeof(ucmd.reserved)) || ucmd.reserved1) { - mlx5_ib_dbg(dev, "invalid reserved\n"); + if (ucmd.flags & ~MLX5_QP_FLAG_TUNNEL_OFFLOADS) { + mlx5_ib_dbg(dev, "invalid flags\n"); + return -EOPNOTSUPP; + } + + if (ucmd.flags & MLX5_QP_FLAG_TUNNEL_OFFLOADS && + !tunnel_offload_supported(dev->mdev)) { + mlx5_ib_dbg(dev, "tunnel offloads isn't supported\n"); return -EOPNOTSUPP; } @@ -1405,6 +1422,10 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, MLX5_SET(tirc, tirc, transport_domain, tdn); hfso = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer); + + if (ucmd.flags & MLX5_QP_FLAG_TUNNEL_OFFLOADS) + MLX5_SET(tirc, tirc, tunneled_offload_en, 1); + switch (ucmd.rx_hash_function) { case MLX5_RX_HASH_FUNC_TOEPLITZ: { @@ -1604,6 +1625,14 @@ static int create_qp_common(struct mlx5_ib_dev *dev, struct ib_pd *pd, qp->wq_sig = !!(ucmd.flags & MLX5_QP_FLAG_SIGNATURE); qp->scat_cqe = !!(ucmd.flags & MLX5_QP_FLAG_SCATTER_CQE); + if (ucmd.flags & MLX5_QP_FLAG_TUNNEL_OFFLOADS) { + if (init_attr->qp_type != IB_QPT_RAW_PACKET || + !tunnel_offload_supported(mdev)) { + mlx5_ib_dbg(dev, "Tunnel offload isn't supported\n"); + return -EOPNOTSUPP; + } + qp->tunnel_offload_en = true; + } if (init_attr->create_flags & IB_QP_CREATE_SOURCE_QPN) { if (init_attr->qp_type != IB_QPT_UD || diff --git a/include/uapi/rdma/mlx5-abi.h b/include/uapi/rdma/mlx5-abi.h index 201a60f032dd..791655ec4aff 100644 --- a/include/uapi/rdma/mlx5-abi.h +++ b/include/uapi/rdma/mlx5-abi.h @@ -39,6 +39,7 @@ enum { MLX5_QP_FLAG_SIGNATURE = 1 << 0, MLX5_QP_FLAG_SCATTER_CQE = 1 << 1, + MLX5_QP_FLAG_TUNNEL_OFFLOADS = 1 << 2, }; enum { @@ -209,6 +210,12 @@ enum mlx5_ib_query_dev_resp_flags { MLX5_IB_QUERY_DEV_RESP_FLAGS_CQE_128B_PAD = 1 << 1, }; +enum mlx5_ib_tunnel_offloads { + MLX5_IB_TUNNELED_OFFLOADS_VXLAN = 1 << 0, + MLX5_IB_TUNNELED_OFFLOADS_GRE = 1 << 1, + MLX5_IB_TUNNELED_OFFLOADS_GENEVE = 1 << 2 +}; + struct mlx5_ib_query_device_resp { __u32 comp_mask; __u32 response_length; @@ -220,6 +227,8 @@ struct mlx5_ib_query_device_resp { __u32 flags; /* Use enum mlx5_ib_query_dev_resp_flags */ struct mlx5_ib_sw_parsing_caps sw_parsing_caps; struct mlx5_ib_striding_rq_caps striding_rq_caps; + __u32 tunnel_offloads_caps; /* enum mlx5_ib_tunnel_offloads */ + __u32 reserved; }; enum mlx5_ib_create_cq_flags { @@ -304,7 +313,7 @@ struct mlx5_ib_create_qp_rss { __u8 reserved[6]; __u8 rx_hash_key[128]; /* valid only for Toeplitz */ __u32 comp_mask; - __u32 reserved1; + __u32 flags; }; struct mlx5_ib_create_qp_resp { -- cgit v1.2.3-59-g8ed1b From 309fa3470fcaf96b295d2106ab17c00dbf7f3920 Mon Sep 17 00:00:00 2001 From: Maor Gottlieb Date: Thu, 19 Oct 2017 08:25:56 +0300 Subject: IB/mlx5: Add support for RSS on the inner packet Some user space application would like to do RSS on the inner packet fields instead on the outer. When MLX5_RX_HASH_INNER is set with one or more of the other hash fields, then the RSS will be done using the inner packet. Signed-off-by: Maor Gottlieb Reviewed-by: Mark Bloch Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford --- drivers/infiniband/hw/mlx5/qp.c | 11 +++++++++++ include/uapi/rdma/mlx5-abi.h | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 53bb0d5cad3d..9e22dead259a 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -1403,6 +1403,12 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, return -EOPNOTSUPP; } + if (ucmd.rx_hash_fields_mask & MLX5_RX_HASH_INNER && + !(ucmd.flags & MLX5_QP_FLAG_TUNNEL_OFFLOADS)) { + mlx5_ib_dbg(dev, "Tunnel offloads must be set for inner RSS\n"); + return -EOPNOTSUPP; + } + err = ib_copy_to_udata(udata, &resp, min_resp_len); if (err) { mlx5_ib_dbg(dev, "copy failed\n"); @@ -1426,6 +1432,11 @@ static int create_rss_raw_qp_tir(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, if (ucmd.flags & MLX5_QP_FLAG_TUNNEL_OFFLOADS) MLX5_SET(tirc, tirc, tunneled_offload_en, 1); + if (ucmd.rx_hash_fields_mask & MLX5_RX_HASH_INNER) + hfso = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_inner); + else + hfso = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer); + switch (ucmd.rx_hash_function) { case MLX5_RX_HASH_FUNC_TOEPLITZ: { diff --git a/include/uapi/rdma/mlx5-abi.h b/include/uapi/rdma/mlx5-abi.h index 791655ec4aff..442b46b74a3a 100644 --- a/include/uapi/rdma/mlx5-abi.h +++ b/include/uapi/rdma/mlx5-abi.h @@ -303,7 +303,9 @@ enum mlx5_rx_hash_fields { MLX5_RX_HASH_SRC_PORT_TCP = 1 << 4, MLX5_RX_HASH_DST_PORT_TCP = 1 << 5, MLX5_RX_HASH_SRC_PORT_UDP = 1 << 6, - MLX5_RX_HASH_DST_PORT_UDP = 1 << 7 + MLX5_RX_HASH_DST_PORT_UDP = 1 << 7, + /* Save bits for future fields */ + MLX5_RX_HASH_INNER = 1 << 31 }; struct mlx5_ib_create_qp_rss { -- cgit v1.2.3-59-g8ed1b From 8ac0d9a81edf2ef4a2268b65b802a6b856dc77e6 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 25 Oct 2017 12:35:02 -0600 Subject: elevator: allow name aliases Since we now lookup elevator types with the appropriate multiqueue capability, allow schedulers to register with an alias alongside the real name. This is in preparation for allowing 'mq-deadline' to register an alias of 'deadline' as well. Reviewed-by: Omar Sandoval Signed-off-by: Jens Axboe --- block/elevator.c | 23 +++++++++++++++++------ include/linux/elevator.h | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/block/elevator.c b/block/elevator.c index 77856bf29568..7bda083d5968 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -83,6 +83,16 @@ bool elv_bio_merge_ok(struct request *rq, struct bio *bio) } EXPORT_SYMBOL(elv_bio_merge_ok); +static bool elevator_match(const struct elevator_type *e, const char *name) +{ + if (!strcmp(e->elevator_name, name)) + return true; + if (e->elevator_alias && !strcmp(e->elevator_alias, name)) + return true; + + return false; +} + /* * Return scheduler with name 'name' and with matching 'mq capability */ @@ -91,7 +101,7 @@ static struct elevator_type *elevator_find(const char *name, bool mq) struct elevator_type *e; list_for_each_entry(e, &elv_list, list) { - if (!strcmp(e->elevator_name, name) && (mq == e->uses_mq)) + if (elevator_match(e, name) && (mq == e->uses_mq)) return e; } @@ -922,9 +932,9 @@ int elv_register(struct elevator_type *e) spin_unlock(&elv_list_lock); /* print pretty message */ - if (!strcmp(e->elevator_name, chosen_elevator) || + if (elevator_match(e, chosen_elevator) || (!*chosen_elevator && - !strcmp(e->elevator_name, CONFIG_DEFAULT_IOSCHED))) + elevator_match(e, CONFIG_DEFAULT_IOSCHED))) def = " (default)"; printk(KERN_INFO "io scheduler %s registered%s\n", e->elevator_name, @@ -1077,8 +1087,7 @@ static int __elevator_change(struct request_queue *q, const char *name) if (!e) return -EINVAL; - if (q->elevator && - !strcmp(elevator_name, q->elevator->type->elevator_name)) { + if (q->elevator && elevator_match(q->elevator->type, elevator_name)) { elevator_put(e); return 0; } @@ -1114,6 +1123,7 @@ ssize_t elv_iosched_show(struct request_queue *q, char *name) struct elevator_queue *e = q->elevator; struct elevator_type *elv = NULL; struct elevator_type *__e; + bool uses_mq = q->mq_ops != NULL; int len = 0; if (!queue_is_rq_based(q)) @@ -1126,7 +1136,8 @@ ssize_t elv_iosched_show(struct request_queue *q, char *name) spin_lock(&elv_list_lock); list_for_each_entry(__e, &elv_list, list) { - if (elv && !strcmp(elv->elevator_name, __e->elevator_name)) { + if (elv && elevator_match(elv, __e->elevator_name) && + (__e->uses_mq == uses_mq)) { len += sprintf(name+len, "[%s] ", elv->elevator_name); continue; } diff --git a/include/linux/elevator.h b/include/linux/elevator.h index 5bc8f8682a3e..6df8b14f1f6a 100644 --- a/include/linux/elevator.h +++ b/include/linux/elevator.h @@ -144,6 +144,7 @@ struct elevator_type size_t icq_align; /* ditto */ struct elv_fs_entry *elevator_attrs; char elevator_name[ELV_NAME_MAX]; + const char *elevator_alias; struct module *elevator_owner; bool uses_mq; #ifdef CONFIG_BLK_DEBUG_FS -- cgit v1.2.3-59-g8ed1b From 8bb705e3e79d84e77edd4499e74483dd96a4626c Mon Sep 17 00:00:00 2001 From: Christian König Date: Tue, 24 Oct 2017 14:40:26 -0500 Subject: PCI: Add pci_resize_resource() for resizing BARs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a pci_resize_resource() interface to allow device drivers to resize BARs of their devices. This is useful for devices with large local storage, e.g., graphics devices. These devices often only expose 256MB BARs initially to be compatible with 32-bit systems. This function only tries to reprogram the windows of the bridge directly above the requesting device and only the BAR of the same type (usually mem, 64bit, prefetchable). This is done to avoid disturbing other drivers by changing the BARs of their devices. Drivers should use the following sequence to resize their BARs: 1. Disable memory decoding of the device using the PCI cfg dword. 2. Use pci_release_resource() to release all BARs which can move during the resize, including the one you want to resize. 3. Call pci_resize_resource() for each BAR you want to resize. 4. Call pci_assign_unassigned_bus_resources() to reassign new locations for all BARs which are not resized, but could move. 5. If everything worked as expected, enable memory decoding in the device again using the PCI cfg dword. Signed-off-by: Christian König Signed-off-by: Bjorn Helgaas --- drivers/pci/setup-bus.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/setup-res.c | 58 +++++++++++++++++++++++++++++ include/linux/pci.h | 3 ++ 3 files changed, 159 insertions(+) (limited to 'include') diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 73dda0d59bbc..6826a893288a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1913,6 +1913,104 @@ enable_all: } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); +int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) +{ + struct pci_dev_resource *dev_res; + struct pci_dev *next; + LIST_HEAD(saved); + LIST_HEAD(added); + LIST_HEAD(failed); + unsigned int i; + int ret; + + /* Walk to the root hub, releasing bridge BARs when possible */ + next = bridge; + do { + bridge = next; + for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END; + i++) { + struct resource *res = &bridge->resource[i]; + + if ((res->flags ^ type) & PCI_RES_TYPE_MASK) + continue; + + /* Ignore BARs which are still in use */ + if (res->child) + continue; + + ret = add_to_list(&saved, bridge, res, 0, 0); + if (ret) + goto cleanup; + + dev_info(&bridge->dev, "BAR %d: releasing %pR\n", + i, res); + + if (res->parent) + release_resource(res); + res->start = 0; + res->end = 0; + break; + } + if (i == PCI_BRIDGE_RESOURCE_END) + break; + + next = bridge->bus ? bridge->bus->self : NULL; + } while (next); + + if (list_empty(&saved)) + return -ENOENT; + + __pci_bus_size_bridges(bridge->subordinate, &added); + __pci_bridge_assign_resources(bridge, &added, &failed); + BUG_ON(!list_empty(&added)); + + if (!list_empty(&failed)) { + ret = -ENOSPC; + goto cleanup; + } + + list_for_each_entry(dev_res, &saved, list) { + /* Skip the bridge we just assigned resources for. */ + if (bridge == dev_res->dev) + continue; + + bridge = dev_res->dev; + pci_setup_bridge(bridge->subordinate); + } + + free_list(&saved); + return 0; + +cleanup: + /* restore size and flags */ + list_for_each_entry(dev_res, &failed, list) { + struct resource *res = dev_res->res; + + res->start = dev_res->start; + res->end = dev_res->end; + res->flags = dev_res->flags; + } + free_list(&failed); + + /* Revert to the old configuration */ + list_for_each_entry(dev_res, &saved, list) { + struct resource *res = dev_res->res; + + bridge = dev_res->dev; + i = res - bridge->resource; + + res->start = dev_res->start; + res->end = dev_res->end; + res->flags = dev_res->flags; + + pci_claim_resource(bridge, i); + pci_setup_bridge(bridge->subordinate); + } + free_list(&saved); + + return ret; +} + void pci_assign_unassigned_bus_resources(struct pci_bus *bus) { struct pci_dev *dev; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index e576e1a8d978..bf0089ef2177 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -396,6 +396,64 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz return 0; } +void pci_release_resource(struct pci_dev *dev, int resno) +{ + struct resource *res = dev->resource + resno; + + dev_info(&dev->dev, "BAR %d: releasing %pR\n", resno, res); + release_resource(res); + res->end = resource_size(res) - 1; + res->start = 0; + res->flags |= IORESOURCE_UNSET; +} +EXPORT_SYMBOL(pci_release_resource); + +int pci_resize_resource(struct pci_dev *dev, int resno, int size) +{ + struct resource *res = dev->resource + resno; + int old, ret; + u32 sizes; + u16 cmd; + + /* Make sure the resource isn't assigned before resizing it. */ + if (!(res->flags & IORESOURCE_UNSET)) + return -EBUSY; + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (cmd & PCI_COMMAND_MEMORY) + return -EBUSY; + + sizes = pci_rebar_get_possible_sizes(dev, resno); + if (!sizes) + return -ENOTSUPP; + + if (!(sizes & BIT(size))) + return -EINVAL; + + old = pci_rebar_get_current_size(dev, resno); + if (old < 0) + return old; + + ret = pci_rebar_set_size(dev, resno, size); + if (ret) + return ret; + + res->end = res->start + pci_rebar_size_to_bytes(size) - 1; + + /* Check if the new config works by trying to assign everything. */ + ret = pci_reassign_bridge_resources(dev->bus->self, res->flags); + if (ret) + goto error_resize; + + return 0; + +error_resize: + pci_rebar_set_size(dev, resno, old); + res->end = res->start + pci_rebar_size_to_bytes(old) - 1; + return ret; +} +EXPORT_SYMBOL(pci_resize_resource); + int pci_enable_resources(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; diff --git a/include/linux/pci.h b/include/linux/pci.h index f4f8ee5a7362..55e9fe6dd577 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1106,6 +1106,8 @@ void pci_reset_bridge_secondary_bus(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); +void pci_release_resource(struct pci_dev *dev, int resno); +int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size); int pci_select_bars(struct pci_dev *dev, unsigned long flags); bool pci_device_is_present(struct pci_dev *pdev); void pci_ignore_hotplug(struct pci_dev *dev); @@ -1185,6 +1187,7 @@ void pci_assign_unassigned_resources(void); void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge); void pci_assign_unassigned_bus_resources(struct pci_bus *bus); void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus); +int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type); void pdev_enable_device(struct pci_dev *); int pci_enable_resources(struct pci_dev *, int mask); void pci_assign_irq(struct pci_dev *dev); -- cgit v1.2.3-59-g8ed1b From 88ca59d1aaf28c25b47a9f933090e480ba6dc92a Mon Sep 17 00:00:00 2001 From: Girish Moodalbail Date: Wed, 25 Oct 2017 12:26:43 -0700 Subject: macvlan: remove unused fields in struct macvlan_dev commit 635b8c8ecdd2 ("tap: Renaming tap related APIs, data structures, macros") captured all the tap related fields into a new struct tap_dev. However, it failed to remove those fields from struct macvlan_dev. Those fields are currently unused and must be removed. While there I moved the comment for MAX_TAP_QUEUES to the right place. Fixes: 635b8c8ecdd27142 (tap: Renaming tap related APIs, data structures, macros) Signed-off-by: Girish Moodalbail Signed-off-by: David S. Miller --- include/linux/if_macvlan.h | 15 --------------- include/linux/if_tap.h | 4 ++++ 2 files changed, 4 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index 10e319f41fb1..e13b369df02b 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -10,13 +10,6 @@ #include struct macvlan_port; -struct macvtap_queue; - -/* - * Maximum times a macvtap device can be opened. This can be used to - * configure the number of receive queue, e.g. for multiqueue virtio. - */ -#define MAX_TAP_QUEUES 256 #define MACVLAN_MC_FILTER_BITS 8 #define MACVLAN_MC_FILTER_SZ (1 << MACVLAN_MC_FILTER_BITS) @@ -35,14 +28,6 @@ struct macvlan_dev { netdev_features_t set_features; enum macvlan_mode mode; u16 flags; - /* This array tracks active taps. */ - struct tap_queue __rcu *taps[MAX_TAP_QUEUES]; - /* This list tracks all taps (both enabled and disabled) */ - struct list_head queue_list; - int numvtaps; - int numqueues; - netdev_features_t tap_features; - int minor; int nest_level; #ifdef CONFIG_NET_POLL_CONTROLLER struct netpoll *netpoll; diff --git a/include/linux/if_tap.h b/include/linux/if_tap.h index 4837157da0dc..d1b5173ad8f0 100644 --- a/include/linux/if_tap.h +++ b/include/linux/if_tap.h @@ -22,6 +22,10 @@ static inline struct skb_array *tap_get_skb_array(struct file *f) #include #include +/* + * Maximum times a tap device can be opened. This can be used to + * configure the number of receive queue, e.g. for multiqueue virtio. + */ #define MAX_TAP_QUEUES 256 struct tap_queue; -- cgit v1.2.3-59-g8ed1b From f1d981eaecf8ace68ec1d15bf05f28a4887ea6fb Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 23 Oct 2017 10:32:08 +0900 Subject: PM / devfreq: Use the available min/max frequency The commit a76caf55e5b35 ("thermal: Add devfreq cooling") is able to disable OPP as a cooling device. In result, both update_devfreq() and {min|max}_freq_show() have to consider the 'opp->available' status of each OPP. So, this patch adds the 'scaling_{min|max}_freq' to struct devfreq in order to indicate the available mininum and maximum frequency by adjusting OPP interface such as dev_pm_opp_{disable|enable}(). The 'scaling_{min|max}_freq' are used for on both update_devfreq() and {min|max}_freq_show(). Signed-off-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq.c | 40 ++++++++++++++++++++++++++++++++-------- include/linux/devfreq.h | 4 ++++ 2 files changed, 36 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index b6ba24e5db0d..ee3e7cee30b6 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -28,6 +28,9 @@ #include #include "governor.h" +#define MAX(a,b) ((a > b) ? a : b) +#define MIN(a,b) ((a < b) ? a : b) + static struct class *devfreq_class; /* @@ -255,7 +258,7 @@ static int devfreq_notify_transition(struct devfreq *devfreq, int update_devfreq(struct devfreq *devfreq) { struct devfreq_freqs freqs; - unsigned long freq, cur_freq; + unsigned long freq, cur_freq, min_freq, max_freq; int err = 0; u32 flags = 0; @@ -273,19 +276,21 @@ int update_devfreq(struct devfreq *devfreq) return err; /* - * Adjust the frequency with user freq and QoS. + * Adjust the frequency with user freq, QoS and available freq. * * List from the highest priority * max_freq * min_freq */ + max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq); + min_freq = MAX(devfreq->scaling_min_freq, devfreq->min_freq); - if (devfreq->min_freq && freq < devfreq->min_freq) { - freq = devfreq->min_freq; + if (min_freq && freq < min_freq) { + freq = min_freq; flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */ } - if (devfreq->max_freq && freq > devfreq->max_freq) { - freq = devfreq->max_freq; + if (max_freq && freq > max_freq) { + freq = max_freq; flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ } @@ -494,6 +499,19 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, int ret; mutex_lock(&devfreq->lock); + + devfreq->scaling_min_freq = find_available_min_freq(devfreq); + if (!devfreq->scaling_min_freq) { + mutex_unlock(&devfreq->lock); + return -EINVAL; + } + + devfreq->scaling_max_freq = find_available_max_freq(devfreq); + if (!devfreq->scaling_max_freq) { + mutex_unlock(&devfreq->lock); + return -EINVAL; + } + ret = update_devfreq(devfreq); mutex_unlock(&devfreq->lock); @@ -593,6 +611,7 @@ struct devfreq *devfreq_add_device(struct device *dev, err = -EINVAL; goto err_dev; } + devfreq->scaling_min_freq = devfreq->min_freq; devfreq->max_freq = find_available_max_freq(devfreq); if (!devfreq->max_freq) { @@ -600,6 +619,7 @@ struct devfreq *devfreq_add_device(struct device *dev, err = -EINVAL; goto err_dev; } + devfreq->scaling_max_freq = devfreq->max_freq; dev_set_name(&devfreq->dev, "devfreq%d", atomic_inc_return(&devfreq_no)); @@ -1127,7 +1147,9 @@ unlock: static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", to_devfreq(dev)->min_freq); + struct devfreq *df = to_devfreq(dev); + + return sprintf(buf, "%lu\n", MAX(df->scaling_min_freq, df->min_freq)); } static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, @@ -1161,7 +1183,9 @@ static DEVICE_ATTR_RW(min_freq); static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%lu\n", to_devfreq(dev)->max_freq); + struct devfreq *df = to_devfreq(dev); + + return sprintf(buf, "%lu\n", MIN(df->scaling_max_freq, df->max_freq)); } static DEVICE_ATTR_RW(max_freq); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 597294e0cc40..997a9eb34191 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -120,6 +120,8 @@ struct devfreq_dev_profile { * touch this. * @min_freq: Limit minimum frequency requested by user (0: none) * @max_freq: Limit maximum frequency requested by user (0: none) + * @scaling_min_freq: Limit minimum frequency requested by OPP interface + * @scaling_max_freq: Limit maximum frequency requested by OPP interface * @stop_polling: devfreq polling status of a device. * @total_trans: Number of devfreq transitions * @trans_table: Statistics of devfreq transitions @@ -153,6 +155,8 @@ struct devfreq { unsigned long min_freq; unsigned long max_freq; + unsigned long scaling_min_freq; + unsigned long scaling_max_freq; bool stop_polling; /* information for device frequency transition */ -- cgit v1.2.3-59-g8ed1b From 416b46a2627ae8de1466f90787dede6f9c5a1bfa Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 23 Oct 2017 10:32:10 +0900 Subject: PM / devfreq: Show the all available frequencies The commit a76caf55e5b35 ("thermal: Add devfreq cooling") allows the devfreq device to use the cooling device. When the cooling down are required, the devfreq_cooling.c disables the OPP entry with the dev_pm_opp_disable(). In result, 'available_frequencies'[1] sysfs node never came to show the all available frequencies. [1] /sys/class/devfreq/.../available_frequencies So, this patch uses the 'freq_table' in the 'struct devfreq_dev_profile' in order to show the all available frequencies. - If 'freq_table' is NULL, devfreq core initializes them by using OPP values. - If 'freq_table' is initialized, devfreq core just uses the 'freq_table'. And this patch adds some comment about the sort way of 'freq_table'. Signed-off-by: Chanwoo Choi Signed-off-by: MyungJoo Ham --- drivers/devfreq/devfreq.c | 16 +++++----------- include/linux/devfreq.h | 5 +++-- 2 files changed, 8 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index b2920cd2b78e..381f92e5e794 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -1194,22 +1194,16 @@ static ssize_t available_frequencies_show(struct device *d, char *buf) { struct devfreq *df = to_devfreq(d); - struct device *dev = df->dev.parent; - struct dev_pm_opp *opp; ssize_t count = 0; - unsigned long freq = 0; + int i; - do { - opp = dev_pm_opp_find_freq_ceil(dev, &freq); - if (IS_ERR(opp)) - break; + mutex_lock(&df->lock); - dev_pm_opp_put(opp); + for (i = 0; i < df->profile->max_state; i++) count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), - "%lu ", freq); - freq++; - } while (1); + "%lu ", df->profile->freq_table[i]); + mutex_unlock(&df->lock); /* Truncate the trailing space */ if (count) count--; diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 997a9eb34191..19520625ea94 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -84,8 +84,9 @@ struct devfreq_dev_status { * from devfreq_remove_device() call. If the user * has registered devfreq->nb at a notifier-head, * this is the time to unregister it. - * @freq_table: Optional list of frequencies to support statistics. - * @max_state: The size of freq_table. + * @freq_table: Optional list of frequencies to support statistics + * and freq_table must be generated in ascending order. + * @max_state: The size of freq_table. */ struct devfreq_dev_profile { unsigned long initial_freq; -- cgit v1.2.3-59-g8ed1b From aa7c352f9841ab3fee5bf1de127a45e6310124a6 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 23 Oct 2017 10:32:12 +0900 Subject: PM / devfreq: Define the constant governor name Prior to that, the devfreq device uses the governor name when adding the itself. In order to prevent the mistake used the wrong governor name, this patch defines the governor name as a constant and then uses them instead of using the string directly. Signed-off-by: Chanwoo Choi Signed-off-by: MyungJoo Ham Cc: Kukjin Kim Cc: Krzysztof Kozlowski Cc: linux-samsung-soc@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org --- drivers/devfreq/exynos-bus.c | 5 +++-- drivers/devfreq/governor_passive.c | 2 +- drivers/devfreq/governor_performance.c | 2 +- drivers/devfreq/governor_powersave.c | 2 +- drivers/devfreq/governor_simpleondemand.c | 2 +- drivers/devfreq/governor_userspace.c | 2 +- drivers/devfreq/rk3399_dmc.c | 2 +- include/linux/devfreq.h | 7 +++++++ 8 files changed, 16 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c index 49f68929e024..c25658b26598 100644 --- a/drivers/devfreq/exynos-bus.c +++ b/drivers/devfreq/exynos-bus.c @@ -436,7 +436,8 @@ static int exynos_bus_probe(struct platform_device *pdev) ondemand_data->downdifferential = 5; /* Add devfreq device to monitor and handle the exynos bus */ - bus->devfreq = devm_devfreq_add_device(dev, profile, "simple_ondemand", + bus->devfreq = devm_devfreq_add_device(dev, profile, + DEVFREQ_GOV_SIMPLE_ONDEMAND, ondemand_data); if (IS_ERR(bus->devfreq)) { dev_err(dev, "failed to add devfreq device\n"); @@ -488,7 +489,7 @@ passive: passive_data->parent = parent_devfreq; /* Add devfreq device for exynos bus with passive governor */ - bus->devfreq = devm_devfreq_add_device(dev, profile, "passive", + bus->devfreq = devm_devfreq_add_device(dev, profile, DEVFREQ_GOV_PASSIVE, passive_data); if (IS_ERR(bus->devfreq)) { dev_err(dev, diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c index 673ad8cc9a1d..3bc29acbd54e 100644 --- a/drivers/devfreq/governor_passive.c +++ b/drivers/devfreq/governor_passive.c @@ -183,7 +183,7 @@ static int devfreq_passive_event_handler(struct devfreq *devfreq, } static struct devfreq_governor devfreq_passive = { - .name = "passive", + .name = DEVFREQ_GOV_PASSIVE, .immutable = 1, .get_target_freq = devfreq_passive_get_target_freq, .event_handler = devfreq_passive_event_handler, diff --git a/drivers/devfreq/governor_performance.c b/drivers/devfreq/governor_performance.c index c72f942f30a8..4d23ecfbd948 100644 --- a/drivers/devfreq/governor_performance.c +++ b/drivers/devfreq/governor_performance.c @@ -42,7 +42,7 @@ static int devfreq_performance_handler(struct devfreq *devfreq, } static struct devfreq_governor devfreq_performance = { - .name = "performance", + .name = DEVFREQ_GOV_PERFORMANCE, .get_target_freq = devfreq_performance_func, .event_handler = devfreq_performance_handler, }; diff --git a/drivers/devfreq/governor_powersave.c b/drivers/devfreq/governor_powersave.c index 0c6bed567e6d..0c42f23249ef 100644 --- a/drivers/devfreq/governor_powersave.c +++ b/drivers/devfreq/governor_powersave.c @@ -39,7 +39,7 @@ static int devfreq_powersave_handler(struct devfreq *devfreq, } static struct devfreq_governor devfreq_powersave = { - .name = "powersave", + .name = DEVFREQ_GOV_POWERSAVE, .get_target_freq = devfreq_powersave_func, .event_handler = devfreq_powersave_handler, }; diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index ae72ba5e78df..28e0f2de7100 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -125,7 +125,7 @@ static int devfreq_simple_ondemand_handler(struct devfreq *devfreq, } static struct devfreq_governor devfreq_simple_ondemand = { - .name = "simple_ondemand", + .name = DEVFREQ_GOV_SIMPLE_ONDEMAND, .get_target_freq = devfreq_simple_ondemand_func, .event_handler = devfreq_simple_ondemand_handler, }; diff --git a/drivers/devfreq/governor_userspace.c b/drivers/devfreq/governor_userspace.c index 77028c27593c..080607c3f34d 100644 --- a/drivers/devfreq/governor_userspace.c +++ b/drivers/devfreq/governor_userspace.c @@ -87,7 +87,7 @@ static struct attribute *dev_entries[] = { NULL, }; static const struct attribute_group dev_attr_group = { - .name = "userspace", + .name = DEVFREQ_GOV_USERSPACE, .attrs = dev_entries, }; diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index 1b89ebbad02c..5dfbfa3cc878 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -431,7 +431,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) data->devfreq = devm_devfreq_add_device(dev, &rk3399_devfreq_dmc_profile, - "simple_ondemand", + DEVFREQ_GOV_SIMPLE_ONDEMAND, &data->ondemand_data); if (IS_ERR(data->devfreq)) return PTR_ERR(data->devfreq); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 19520625ea94..3aae5b3af87c 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -19,6 +19,13 @@ #define DEVFREQ_NAME_LEN 16 +/* DEVFREQ governor name */ +#define DEVFREQ_GOV_SIMPLE_ONDEMAND "simple_ondemand" +#define DEVFREQ_GOV_PERFORMANCE "performance" +#define DEVFREQ_GOV_POWERSAVE "powersave" +#define DEVFREQ_GOV_USERSPACE "userspace" +#define DEVFREQ_GOV_PASSIVE "passive" + /* DEVFREQ notifier interface */ #define DEVFREQ_TRANSITION_NOTIFIER (0) -- cgit v1.2.3-59-g8ed1b From 2ec6f2ef799b94e6e99868ef586cafceaa7c0475 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 4 Oct 2017 09:08:36 +0800 Subject: f2fs: trace f2fs_remove_discard This patch adds tracepoint to trace f2fs_remove_discard. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/segment.c | 2 ++ include/trace/events/f2fs.h | 7 +++++++ 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 1bb7417b4ebb..d6614037e04e 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -822,6 +822,8 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi, { struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info; + trace_f2fs_remove_discard(dc->bdev, dc->start, dc->len); + f2fs_bug_on(sbi, dc->ref); if (dc->error == -EOPNOTSUPP) diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 5d216f7fb05a..0e7a31694ff5 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -1286,6 +1286,13 @@ DEFINE_EVENT(f2fs_discard, f2fs_issue_discard, TP_ARGS(dev, blkstart, blklen) ); +DEFINE_EVENT(f2fs_discard, f2fs_remove_discard, + + TP_PROTO(struct block_device *dev, block_t blkstart, block_t blklen), + + TP_ARGS(dev, blkstart, blklen) +); + TRACE_EVENT(f2fs_issue_reset_zone, TP_PROTO(struct block_device *dev, block_t blkstart), -- cgit v1.2.3-59-g8ed1b From 0c5e36db17f5fe3dda8c5584f5f530152409a3f3 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Tue, 17 Oct 2017 17:33:41 +0800 Subject: f2fs: trace f2fs_lookup This patch adds trace for f2fs_lookup. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/namei.c | 49 +++++++++++++++++++++++++-------------- include/trace/events/f2fs.h | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index a4dab98c4b7b..6ab8ac9fe043 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -331,12 +331,15 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, struct inode *inode = NULL; struct f2fs_dir_entry *de; struct page *page; - nid_t ino; + struct dentry *new; + nid_t ino = -1; int err = 0; unsigned int root_ino = F2FS_ROOT_INO(F2FS_I_SB(dir)); + trace_f2fs_lookup_start(dir, dentry, flags); + if (f2fs_encrypted_inode(dir)) { - int res = fscrypt_get_encryption_info(dir); + err = fscrypt_get_encryption_info(dir); /* * DCACHE_ENCRYPTED_WITH_KEY is set if the dentry is @@ -346,18 +349,22 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, if (fscrypt_has_encryption_key(dir)) fscrypt_set_encrypted_dentry(dentry); fscrypt_set_d_op(dentry); - if (res && res != -ENOKEY) - return ERR_PTR(res); + if (err && err != -ENOKEY) + goto out; } - if (dentry->d_name.len > F2FS_NAME_LEN) - return ERR_PTR(-ENAMETOOLONG); + if (dentry->d_name.len > F2FS_NAME_LEN) { + err = -ENAMETOOLONG; + goto out; + } de = f2fs_find_entry(dir, &dentry->d_name, &page); if (!de) { - if (IS_ERR(page)) - return (struct dentry *)page; - return d_splice_alias(inode, dentry); + if (IS_ERR(page)) { + err = PTR_ERR(page); + goto out; + } + goto out_splice; } ino = le32_to_cpu(de->ino); @@ -365,19 +372,21 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, f2fs_put_page(page, 0); inode = f2fs_iget(dir->i_sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } if ((dir->i_ino == root_ino) && f2fs_has_inline_dots(dir)) { err = __recover_dot_dentries(dir, root_ino); if (err) - goto err_out; + goto out_iput; } if (f2fs_has_inline_dots(inode)) { err = __recover_dot_dentries(inode, dir->i_ino); if (err) - goto err_out; + goto out_iput; } if (f2fs_encrypted_inode(dir) && (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) && @@ -386,12 +395,18 @@ static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, "Inconsistent encryption contexts: %lu/%lu", dir->i_ino, inode->i_ino); err = -EPERM; - goto err_out; + goto out_iput; } - return d_splice_alias(inode, dentry); - -err_out: +out_splice: + new = d_splice_alias(inode, dentry); + if (IS_ERR(new)) + err = PTR_ERR(new); + trace_f2fs_lookup_end(dir, dentry, ino, err); + return new; +out_iput: iput(inode); +out: + trace_f2fs_lookup_end(dir, dentry, ino, err); return ERR_PTR(err); } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 0e7a31694ff5..dcbbe6dcca9c 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -728,6 +728,62 @@ TRACE_EVENT(f2fs_get_victim, __entry->free) ); +TRACE_EVENT(f2fs_lookup_start, + + TP_PROTO(struct inode *dir, struct dentry *dentry, unsigned int flags), + + TP_ARGS(dir, dentry, flags), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(const char *, name) + __field(unsigned int, flags) + ), + + TP_fast_assign( + __entry->dev = dir->i_sb->s_dev; + __entry->ino = dir->i_ino; + __entry->name = dentry->d_name.name; + __entry->flags = flags; + ), + + TP_printk("dev = (%d,%d), pino = %lu, name:%s, flags:%u", + show_dev_ino(__entry), + __entry->name, + __entry->flags) +); + +TRACE_EVENT(f2fs_lookup_end, + + TP_PROTO(struct inode *dir, struct dentry *dentry, nid_t ino, + int err), + + TP_ARGS(dir, dentry, ino, err), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(const char *, name) + __field(nid_t, cino) + __field(int, err) + ), + + TP_fast_assign( + __entry->dev = dir->i_sb->s_dev; + __entry->ino = dir->i_ino; + __entry->name = dentry->d_name.name; + __entry->cino = ino; + __entry->err = err; + ), + + TP_printk("dev = (%d,%d), pino = %lu, name:%s, ino:%u, err:%d", + show_dev_ino(__entry), + __entry->name, + __entry->cino, + __entry->err) +); + TRACE_EVENT(f2fs_fallocate, TP_PROTO(struct inode *inode, int mode, -- cgit v1.2.3-59-g8ed1b From e97a3c4c6f0af13fa48e251348f98881d82b5135 Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Fri, 13 Oct 2017 18:01:33 +0800 Subject: f2fs: trace f2fs_readdir This patch adds trace for f2fs_readdir. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/dir.c | 14 +++++++++----- include/trace/events/f2fs.h | 29 +++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index c0c933ad43c8..148927f5bc93 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -14,6 +14,7 @@ #include "node.h" #include "acl.h" #include "xattr.h" +#include static unsigned long dir_blocks(struct inode *inode) { @@ -847,6 +848,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) struct f2fs_dentry_block *dentry_blk = NULL; struct page *dentry_page = NULL; struct file_ra_state *ra = &file->f_ra; + loff_t start_pos = ctx->pos; unsigned int n = ((unsigned long)ctx->pos / NR_DENTRY_IN_BLOCK); struct f2fs_dentry_ptr d; struct fscrypt_str fstr = FSTR_INIT(NULL, 0); @@ -855,16 +857,16 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) if (f2fs_encrypted_inode(inode)) { err = fscrypt_get_encryption_info(inode); if (err && err != -ENOKEY) - return err; + goto out; err = fscrypt_fname_alloc_buffer(inode, F2FS_NAME_LEN, &fstr); if (err < 0) - return err; + goto out; } if (f2fs_has_inline_dentry(inode)) { err = f2fs_read_inline_dir(file, ctx, &fstr); - goto out; + goto out_free; } /* readahead for multi pages of dir */ @@ -880,7 +882,7 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) err = 0; continue; } else { - goto out; + goto out_free; } } @@ -900,8 +902,10 @@ static int f2fs_readdir(struct file *file, struct dir_context *ctx) kunmap(dentry_page); f2fs_put_page(dentry_page, 1); } -out: +out_free: fscrypt_fname_free_buffer(&fstr); +out: + trace_f2fs_readdir(inode, start_pos, ctx->pos, err); return err < 0 ? err : 0; } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index dcbbe6dcca9c..009745564214 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -784,6 +784,35 @@ TRACE_EVENT(f2fs_lookup_end, __entry->err) ); +TRACE_EVENT(f2fs_readdir, + + TP_PROTO(struct inode *dir, loff_t start_pos, loff_t end_pos, int err), + + TP_ARGS(dir, start_pos, end_pos, err), + + TP_STRUCT__entry( + __field(dev_t, dev) + __field(ino_t, ino) + __field(loff_t, start) + __field(loff_t, end) + __field(int, err) + ), + + TP_fast_assign( + __entry->dev = dir->i_sb->s_dev; + __entry->ino = dir->i_ino; + __entry->start = start_pos; + __entry->end = end_pos; + __entry->err = err; + ), + + TP_printk("dev = (%d,%d), ino = %lu, start_pos:%llu, end_pos:%llu, err:%d", + show_dev_ino(__entry), + __entry->start, + __entry->end, + __entry->err) +); + TRACE_EVENT(f2fs_fallocate, TP_PROTO(struct inode *inode, int mode, -- cgit v1.2.3-59-g8ed1b From 60e2a7780793bae0debc275a9ccd57f7da0cf195 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Wed, 25 Oct 2017 11:01:45 +0200 Subject: tcp: TCP experimental option for SMC The SMC protocol [1] relies on the use of a new TCP experimental option [2, 3]. With this option, SMC capabilities are exchanged between peers during the TCP three way handshake. This patch adds support for this experimental option to TCP. References: [1] SMC-R Informational RFC: http://www.rfc-editor.org/info/rfc7609 [2] Shared Use of TCP Experimental Options RFC 6994: https://tools.ietf.org/rfc/rfc6994.txt [3] IANA ExID SMCR: http://www.iana.org/assignments/tcp-parameters/tcp-parameters.xhtml#tcp-exids Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- include/linux/tcp.h | 9 +++++-- include/net/inet_sock.h | 3 ++- include/net/tcp.h | 7 ++++++ net/ipv4/tcp.c | 6 +++++ net/ipv4/tcp_input.c | 35 +++++++++++++++++++++++++++ net/ipv4/tcp_minisocks.c | 19 +++++++++++++++ net/ipv4/tcp_output.c | 63 +++++++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 136 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 173a7c2f9636..8c431385b272 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -98,7 +98,8 @@ struct tcp_options_received { tstamp_ok : 1, /* TIMESTAMP seen on SYN packet */ dsack : 1, /* D-SACK is scheduled */ wscale_ok : 1, /* Wscale seen on SYN packet */ - sack_ok : 4, /* SACK seen on SYN packet */ + sack_ok : 3, /* SACK seen on SYN packet */ + smc_ok : 1, /* SMC seen on SYN packet */ snd_wscale : 4, /* Window scaling received from sender */ rcv_wscale : 4; /* Window scaling to send to receiver */ u8 num_sacks; /* Number of SACK blocks */ @@ -110,6 +111,9 @@ static inline void tcp_clear_options(struct tcp_options_received *rx_opt) { rx_opt->tstamp_ok = rx_opt->sack_ok = 0; rx_opt->wscale_ok = rx_opt->snd_wscale = 0; +#if IS_ENABLED(CONFIG_SMC) + rx_opt->smc_ok = 0; +#endif } /* This is the max number of SACKS that we'll generate and process. It's safe @@ -229,7 +233,8 @@ struct tcp_sock { syn_fastopen_ch:1, /* Active TFO re-enabling probe */ syn_data_acked:1,/* data in SYN is acked by SYN-ACK */ save_syn:1, /* Save headers of SYN packet */ - is_cwnd_limited:1;/* forward progress limited by snd_cwnd? */ + is_cwnd_limited:1,/* forward progress limited by snd_cwnd? */ + syn_smc:1; /* SYN includes SMC */ u32 tlp_high_seq; /* snd_nxt at the time of TLP retransmit. */ /* RTT measurement */ diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 425752f768d2..c49938d1481a 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -92,7 +92,8 @@ struct inet_request_sock { wscale_ok : 1, ecn_ok : 1, acked : 1, - no_srccheck: 1; + no_srccheck: 1, + smc_ok : 1; kmemcheck_bitfield_end(flags); u32 ir_mark; union { diff --git a/include/net/tcp.h b/include/net/tcp.h index 2392f74074e7..285bc82dea41 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -191,6 +191,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); * experimental options. See draft-ietf-tcpm-experimental-options-00.txt */ #define TCPOPT_FASTOPEN_MAGIC 0xF989 +#define TCPOPT_SMC_MAGIC 0xE2D4C3D9 /* * TCP option lengths @@ -203,6 +204,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOLEN_MD5SIG 18 #define TCPOLEN_FASTOPEN_BASE 2 #define TCPOLEN_EXP_FASTOPEN_BASE 4 +#define TCPOLEN_EXP_SMC_BASE 6 /* But this is what stacks really send out. */ #define TCPOLEN_TSTAMP_ALIGNED 12 @@ -213,6 +215,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCPOLEN_SACK_PERBLOCK 8 #define TCPOLEN_MD5SIG_ALIGNED 20 #define TCPOLEN_MSS_ALIGNED 4 +#define TCPOLEN_EXP_SMC_BASE_ALIGNED 8 /* Flags in tp->nonagle */ #define TCP_NAGLE_OFF 1 /* Nagle's algo is disabled */ @@ -2108,4 +2111,8 @@ static inline bool tcp_bpf_ca_needs_ecn(struct sock *sk) { return (tcp_call_bpf(sk, BPF_SOCK_OPS_NEEDS_ECN) == 1); } + +#if IS_ENABLED(CONFIG_SMC) +extern struct static_key_false tcp_have_smc; +#endif #endif /* _TCP_H */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 8f36277e82e9..f6e1c00e300e 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -270,6 +270,7 @@ #include #include #include +#include #include #include @@ -302,6 +303,11 @@ EXPORT_SYMBOL(sysctl_tcp_wmem); atomic_long_t tcp_memory_allocated; /* Current allocated memory. */ EXPORT_SYMBOL(tcp_memory_allocated); +#if IS_ENABLED(CONFIG_SMC) +DEFINE_STATIC_KEY_FALSE(tcp_have_smc); +EXPORT_SYMBOL(tcp_have_smc); +#endif + /* * Current number of TCP sockets. */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 893286db4623..337f6011528a 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -76,6 +76,8 @@ #include #include #include +#include +#include int sysctl_tcp_fack __read_mostly; int sysctl_tcp_max_reordering __read_mostly = 300; @@ -3737,6 +3739,21 @@ static void tcp_parse_fastopen_option(int len, const unsigned char *cookie, foc->exp = exp_opt; } +static void smc_parse_options(const struct tcphdr *th, + struct tcp_options_received *opt_rx, + const unsigned char *ptr, + int opsize) +{ +#if IS_ENABLED(CONFIG_SMC) + if (static_branch_unlikely(&tcp_have_smc)) { + if (th->syn && !(opsize & 1) && + opsize >= TCPOLEN_EXP_SMC_BASE && + get_unaligned_be32(ptr) == TCPOPT_SMC_MAGIC) + opt_rx->smc_ok = 1; + } +#endif +} + /* Look for tcp options. Normally only called on SYN and SYNACK packets. * But, this can also be called on packets in the established flow when * the fast version below fails. @@ -3844,6 +3861,9 @@ void tcp_parse_options(const struct net *net, tcp_parse_fastopen_option(opsize - TCPOLEN_EXP_FASTOPEN_BASE, ptr + 2, th->syn, foc, true); + else + smc_parse_options(th, opt_rx, ptr, + opsize); break; } @@ -5598,6 +5618,16 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, return false; } +static void smc_check_reset_syn(struct tcp_sock *tp) +{ +#if IS_ENABLED(CONFIG_SMC) + if (static_branch_unlikely(&tcp_have_smc)) { + if (tp->syn_smc && !tp->rx_opt.smc_ok) + tp->syn_smc = 0; + } +#endif +} + static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th) { @@ -5704,6 +5734,8 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, * is initialized. */ tp->copied_seq = tp->rcv_nxt; + smc_check_reset_syn(tp); + smp_mb(); tcp_finish_connect(sk, skb); @@ -6157,6 +6189,9 @@ static void tcp_openreq_init(struct request_sock *req, ireq->ir_rmt_port = tcp_hdr(skb)->source; ireq->ir_num = ntohs(tcp_hdr(skb)->dest); ireq->ir_mark = inet_request_mark(sk, skb); +#if IS_ENABLED(CONFIG_SMC) + ireq->smc_ok = rx_opt->smc_ok; +#endif } struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index a952357054f4..056009f1c14f 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -416,6 +417,21 @@ void tcp_ca_openreq_child(struct sock *sk, const struct dst_entry *dst) } EXPORT_SYMBOL_GPL(tcp_ca_openreq_child); +static void smc_check_reset_syn_req(struct tcp_sock *oldtp, + struct request_sock *req, + struct tcp_sock *newtp) +{ +#if IS_ENABLED(CONFIG_SMC) + struct inet_request_sock *ireq; + + if (static_branch_unlikely(&tcp_have_smc)) { + ireq = inet_rsk(req); + if (oldtp->syn_smc && !ireq->smc_ok) + newtp->syn_smc = 0; + } +#endif +} + /* This is not only more efficient than what we used to do, it eliminates * a lot of code duplication between IPv4/IPv6 SYN recv processing. -DaveM * @@ -433,6 +449,9 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, struct tcp_request_sock *treq = tcp_rsk(req); struct inet_connection_sock *newicsk = inet_csk(newsk); struct tcp_sock *newtp = tcp_sk(newsk); + struct tcp_sock *oldtp = tcp_sk(sk); + + smc_check_reset_syn_req(oldtp, req, newtp); /* Now setup tcp_sock */ newtp->pred_flags = 0; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 1f01f4c9c738..c8fc512e0bbb 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -422,6 +423,22 @@ static inline bool tcp_urg_mode(const struct tcp_sock *tp) #define OPTION_MD5 (1 << 2) #define OPTION_WSCALE (1 << 3) #define OPTION_FAST_OPEN_COOKIE (1 << 8) +#define OPTION_SMC (1 << 9) + +static void smc_options_write(__be32 *ptr, u16 *options) +{ +#if IS_ENABLED(CONFIG_SMC) + if (static_branch_unlikely(&tcp_have_smc)) { + if (unlikely(OPTION_SMC & *options)) { + *ptr++ = htonl((TCPOPT_NOP << 24) | + (TCPOPT_NOP << 16) | + (TCPOPT_EXP << 8) | + (TCPOLEN_EXP_SMC_BASE)); + *ptr++ = htonl(TCPOPT_SMC_MAGIC); + } + } +#endif +} struct tcp_out_options { u16 options; /* bit field of OPTION_* */ @@ -540,6 +557,41 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp, } ptr += (len + 3) >> 2; } + + smc_options_write(ptr, &options); +} + +static void smc_set_option(const struct tcp_sock *tp, + struct tcp_out_options *opts, + unsigned int *remaining) +{ +#if IS_ENABLED(CONFIG_SMC) + if (static_branch_unlikely(&tcp_have_smc)) { + if (tp->syn_smc) { + if (*remaining >= TCPOLEN_EXP_SMC_BASE_ALIGNED) { + opts->options |= OPTION_SMC; + *remaining -= TCPOLEN_EXP_SMC_BASE_ALIGNED; + } + } + } +#endif +} + +static void smc_set_option_cond(const struct tcp_sock *tp, + const struct inet_request_sock *ireq, + struct tcp_out_options *opts, + unsigned int *remaining) +{ +#if IS_ENABLED(CONFIG_SMC) + if (static_branch_unlikely(&tcp_have_smc)) { + if (tp->syn_smc && ireq->smc_ok) { + if (*remaining >= TCPOLEN_EXP_SMC_BASE_ALIGNED) { + opts->options |= OPTION_SMC; + *remaining -= TCPOLEN_EXP_SMC_BASE_ALIGNED; + } + } + } +#endif } /* Compute TCP options for SYN packets. This is not the final @@ -607,11 +659,14 @@ static unsigned int tcp_syn_options(struct sock *sk, struct sk_buff *skb, } } + smc_set_option(tp, opts, &remaining); + return MAX_TCP_OPTION_SPACE - remaining; } /* Set up TCP options for SYN-ACKs. */ -static unsigned int tcp_synack_options(struct request_sock *req, +static unsigned int tcp_synack_options(const struct sock *sk, + struct request_sock *req, unsigned int mss, struct sk_buff *skb, struct tcp_out_options *opts, const struct tcp_md5sig_key *md5, @@ -667,6 +722,8 @@ static unsigned int tcp_synack_options(struct request_sock *req, } } + smc_set_option_cond(tcp_sk(sk), ireq, opts, &remaining); + return MAX_TCP_OPTION_SPACE - remaining; } @@ -3195,8 +3252,8 @@ struct sk_buff *tcp_make_synack(const struct sock *sk, struct dst_entry *dst, md5 = tcp_rsk(req)->af_specific->req_md5_lookup(sk, req_to_sk(req)); #endif skb_set_hash(skb, tcp_rsk(req)->txhash, PKT_HASH_TYPE_L4); - tcp_header_size = tcp_synack_options(req, mss, skb, &opts, md5, foc) + - sizeof(*th); + tcp_header_size = tcp_synack_options(sk, req, mss, skb, &opts, md5, + foc) + sizeof(*th); skb_push(skb, tcp_header_size); skb_reset_transport_header(skb); -- cgit v1.2.3-59-g8ed1b From 80bbe4a30bc6b119df86c280c91cde2034309bf1 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Fri, 20 Oct 2017 12:18:55 +0100 Subject: ASoC: rt5651: Enable jack detection on JD* pins Enable jack detection for the RT5651 codec on the JD* pins. Signed-off-by: Carlo Caione Signed-off-by: Mark Brown --- include/sound/rt5651.h | 8 +++ sound/soc/codecs/rt5651.c | 168 +++++++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/rt5651.h | 4 ++ 3 files changed, 178 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/rt5651.h b/include/sound/rt5651.h index d35de758dfb5..18b79a761f10 100644 --- a/include/sound/rt5651.h +++ b/include/sound/rt5651.h @@ -11,11 +11,19 @@ #ifndef __LINUX_SND_RT5651_H #define __LINUX_SND_RT5651_H +enum rt5651_jd_src { + RT5651_JD_NULL, + RT5651_JD1_1, + RT5651_JD1_2, + RT5651_JD2, +}; + struct rt5651_platform_data { /* IN2 can optionally be differential */ bool in2_diff; bool dmic_en; + enum rt5651_jd_src jd_src; }; #endif diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 28f7210cec91..91f254391452 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "rl6231.h" #include "rt5651.h" @@ -880,6 +881,9 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("PLL1", RT5651_PWR_ANLG2, RT5651_PWR_PLL_BIT, 0, NULL, 0), /* Input Side */ + SND_SOC_DAPM_SUPPLY("JD Power", RT5651_PWR_ANLG2, + RT5651_PWM_JD_M_BIT, 0, NULL, 0), + /* micbias */ SND_SOC_DAPM_SUPPLY("LDO", RT5651_PWR_ANLG1, RT5651_PWR_LDO_BIT, 0, NULL, 0), @@ -1528,6 +1532,8 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, static int rt5651_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + switch (level) { case SND_SOC_BIAS_PREPARE: if (SND_SOC_BIAS_STANDBY == snd_soc_codec_get_bias_level(codec)) { @@ -1556,8 +1562,13 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, RT5651_PWR_DIG2, 0x0000); snd_soc_write(codec, RT5651_PWR_VOL, 0x0000); snd_soc_write(codec, RT5651_PWR_MIXER, 0x0000); - snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000); - snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000); + if (rt5651->pdata.jd_src) { + snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0204); + snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0002); + } else { + snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000); + snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000); + } break; default: @@ -1570,6 +1581,7 @@ static int rt5651_set_bias_level(struct snd_soc_codec *codec, static int rt5651_probe(struct snd_soc_codec *codec) { struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); rt5651->codec = codec; @@ -1585,6 +1597,15 @@ static int rt5651_probe(struct snd_soc_codec *codec) snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF); + if (rt5651->pdata.jd_src) { + snd_soc_dapm_force_enable_pin(dapm, "JD Power"); + snd_soc_dapm_force_enable_pin(dapm, "LDO"); + snd_soc_dapm_sync(dapm); + + regmap_update_bits(rt5651->regmap, RT5651_MICBIAS, + 0x38, 0x38); + } + return 0; } @@ -1728,6 +1749,93 @@ static int rt5651_parse_dt(struct rt5651_priv *rt5651, struct device_node *np) return 0; } +static irqreturn_t rt5651_irq(int irq, void *data) +{ + struct rt5651_priv *rt5651 = data; + + queue_delayed_work(system_power_efficient_wq, + &rt5651->jack_detect_work, msecs_to_jiffies(250)); + + return IRQ_HANDLED; +} + +static int rt5651_jack_detect(struct snd_soc_codec *codec, int jack_insert) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int jack_type; + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(dapm, "LDO"); + snd_soc_dapm_sync(dapm); + + snd_soc_update_bits(codec, RT5651_MICBIAS, + RT5651_MIC1_OVCD_MASK | + RT5651_MIC1_OVTH_MASK | + RT5651_PWR_CLK12M_MASK | + RT5651_PWR_MB_MASK, + RT5651_MIC1_OVCD_EN | + RT5651_MIC1_OVTH_600UA | + RT5651_PWR_MB_PU | + RT5651_PWR_CLK12M_PU); + msleep(100); + if (snd_soc_read(codec, RT5651_IRQ_CTRL2) & RT5651_MB1_OC_CLR) + jack_type = SND_JACK_HEADPHONE; + else + jack_type = SND_JACK_HEADSET; + snd_soc_update_bits(codec, RT5651_IRQ_CTRL2, + RT5651_MB1_OC_CLR, 0); + } else { /* jack out */ + jack_type = 0; + + snd_soc_update_bits(codec, RT5651_MICBIAS, + RT5651_MIC1_OVCD_MASK, + RT5651_MIC1_OVCD_DIS); + } + + return jack_type; +} + +static void rt5651_jack_detect_work(struct work_struct *work) +{ + struct rt5651_priv *rt5651 = + container_of(work, struct rt5651_priv, jack_detect_work.work); + + int report, val = 0; + + if (!rt5651->codec) + return; + + switch (rt5651->pdata.jd_src) { + case RT5651_JD1_1: + val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x1000; + break; + case RT5651_JD1_2: + val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x2000; + break; + case RT5651_JD2: + val = snd_soc_read(rt5651->codec, RT5651_INT_IRQ_ST) & 0x4000; + break; + default: + break; + } + + report = rt5651_jack_detect(rt5651->codec, !val); + + snd_soc_jack_report(rt5651->hp_jack, report, SND_JACK_HEADSET); +} + +int rt5651_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hp_jack) +{ + struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec); + + rt5651->hp_jack = hp_jack; + rt5651_irq(0, rt5651); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5651_set_jack_detect); + static int rt5651_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1779,6 +1887,59 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, rt5651->hp_mute = 1; + if (rt5651->pdata.jd_src) { + + /* IRQ output on GPIO1 */ + regmap_update_bits(rt5651->regmap, RT5651_GPIO_CTRL1, + RT5651_GP1_PIN_MASK, RT5651_GP1_PIN_IRQ); + + switch (rt5651->pdata.jd_src) { + case RT5651_JD1_1: + regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, + RT5651_JD_TRG_SEL_JD1_1); + regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1, + RT5651_JD1_1_IRQ_EN, + RT5651_JD1_1_IRQ_EN); + break; + case RT5651_JD1_2: + regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, + RT5651_JD_TRG_SEL_JD1_2); + regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1, + RT5651_JD1_2_IRQ_EN, + RT5651_JD1_2_IRQ_EN); + break; + case RT5651_JD2: + regmap_update_bits(rt5651->regmap, RT5651_JD_CTRL2, + RT5651_JD_TRG_SEL_MASK, + RT5651_JD_TRG_SEL_JD2); + regmap_update_bits(rt5651->regmap, RT5651_IRQ_CTRL1, + RT5651_JD2_IRQ_EN, + RT5651_JD2_IRQ_EN); + break; + case RT5651_JD_NULL: + break; + default: + dev_warn(&i2c->dev, "Currently only JD1_1 / JD1_2 / JD2 are supported\n"); + break; + } + } + + INIT_DELAYED_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work); + + if (i2c->irq) { + ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL, + rt5651_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, "rt5651", rt5651); + if (ret) { + dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret); + return ret; + } + } + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651, rt5651_dai, ARRAY_SIZE(rt5651_dai)); @@ -1787,6 +1948,9 @@ static int rt5651_i2c_probe(struct i2c_client *i2c, static int rt5651_i2c_remove(struct i2c_client *i2c) { + struct rt5651_priv *rt5651 = i2c_get_clientdata(i2c); + + cancel_delayed_work_sync(&rt5651->jack_detect_work); snd_soc_unregister_codec(&i2c->dev); return 0; diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h index 1bd33cfa6411..4f8b202121d7 100644 --- a/sound/soc/codecs/rt5651.h +++ b/sound/soc/codecs/rt5651.h @@ -2062,6 +2062,8 @@ struct rt5651_priv { struct snd_soc_codec *codec; struct rt5651_platform_data pdata; struct regmap *regmap; + struct snd_soc_jack *hp_jack; + struct delayed_work jack_detect_work; int sysclk; int sysclk_src; @@ -2077,4 +2079,6 @@ struct rt5651_priv { bool hp_mute; }; +int rt5651_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *hp_jack); #endif /* __RT5651_H__ */ -- cgit v1.2.3-59-g8ed1b From d41bf8c9deaed1a90b18d3ffc5639d4c19f0259a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Mon, 23 Oct 2017 16:18:27 -0700 Subject: cgroup, sched: Move basic cpu stats from cgroup.stat to cpu.stat The basic cpu stat is currently shown with "cpu." prefix in cgroup.stat, and the same information is duplicated in cpu.stat when cpu controller is enabled. This is ugly and not very scalable as we want to expand the coverage of stat information which is always available. This patch makes cgroup core always create "cpu.stat" file and show the basic cpu stat there and calls the cpu controller to show the extra stats when enabled. This ensures that the same information isn't presented in multiple places and makes future expansion of basic stats easier. Signed-off-by: Tejun Heo Acked-by: Peter Zijlstra (Intel) --- Documentation/cgroup-v2.txt | 15 ++++------- include/linux/cgroup-defs.h | 2 ++ include/linux/cgroup.h | 2 -- kernel/cgroup/cgroup-internal.h | 1 + kernel/cgroup/cgroup.c | 60 +++++++++++++++++++++++++++++++++++++++-- kernel/cgroup/stat.c | 10 +++---- kernel/sched/core.c | 13 +++------ 7 files changed, 75 insertions(+), 28 deletions(-) (limited to 'include') diff --git a/Documentation/cgroup-v2.txt b/Documentation/cgroup-v2.txt index 0bbdc720dd7c..779211fbb69f 100644 --- a/Documentation/cgroup-v2.txt +++ b/Documentation/cgroup-v2.txt @@ -886,15 +886,6 @@ All cgroup core files are prefixed with "cgroup." A dying cgroup can consume system resources not exceeding limits, which were active at the moment of cgroup deletion. - cpu.usage_usec - CPU time consumed in the subtree. - - cpu.user_usec - User CPU time consumed in the subtree. - - cpu.system_usec - System CPU time consumed in the subtree. - Controllers =========== @@ -915,12 +906,16 @@ All time durations are in microseconds. cpu.stat A read-only flat-keyed file which exists on non-root cgroups. + This file exists whether the controller is enabled or not. - It reports the following six stats: + It always reports the following three stats: - usage_usec - user_usec - system_usec + + and the following three when the controller is enabled: + - nr_periods - nr_throttled - throttled_usec diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h index 3e55bbd31ad1..ada6df7b1f55 100644 --- a/include/linux/cgroup-defs.h +++ b/include/linux/cgroup-defs.h @@ -569,6 +569,8 @@ struct cgroup_subsys { void (*css_released)(struct cgroup_subsys_state *css); void (*css_free)(struct cgroup_subsys_state *css); void (*css_reset)(struct cgroup_subsys_state *css); + int (*css_extra_stat_show)(struct seq_file *seq, + struct cgroup_subsys_state *css); int (*can_attach)(struct cgroup_taskset *tset); void (*cancel_attach)(struct cgroup_taskset *tset); diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h index 328a70ce0e23..03cad08b09d1 100644 --- a/include/linux/cgroup.h +++ b/include/linux/cgroup.h @@ -703,8 +703,6 @@ static inline void cpuacct_account_field(struct task_struct *tsk, int index, u64 val) {} #endif -void cgroup_stat_show_cputime(struct seq_file *seq, const char *prefix); - void __cgroup_account_cputime(struct cgroup *cgrp, u64 delta_exec); void __cgroup_account_cputime_field(struct cgroup *cgrp, enum cpu_usage_stat index, u64 delta_exec); diff --git a/kernel/cgroup/cgroup-internal.h b/kernel/cgroup/cgroup-internal.h index fa642c99586a..4dc317090920 100644 --- a/kernel/cgroup/cgroup-internal.h +++ b/kernel/cgroup/cgroup-internal.h @@ -205,6 +205,7 @@ int cgroup_task_count(const struct cgroup *cgrp); void cgroup_stat_flush(struct cgroup *cgrp); int cgroup_stat_init(struct cgroup *cgrp); void cgroup_stat_exit(struct cgroup *cgrp); +void cgroup_stat_show_cputime(struct seq_file *seq); void cgroup_stat_boot(void); /* diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 7975b20f1fd1..d9773e49a1b4 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -463,6 +463,28 @@ static struct cgroup_subsys_state *cgroup_css(struct cgroup *cgrp, return &cgrp->self; } +/** + * cgroup_tryget_css - try to get a cgroup's css for the specified subsystem + * @cgrp: the cgroup of interest + * @ss: the subsystem of interest + * + * Find and get @cgrp's css assocaited with @ss. If the css doesn't exist + * or is offline, %NULL is returned. + */ +static struct cgroup_subsys_state *cgroup_tryget_css(struct cgroup *cgrp, + struct cgroup_subsys *ss) +{ + struct cgroup_subsys_state *css; + + rcu_read_lock(); + css = cgroup_css(cgrp, ss); + if (!css || !css_tryget_online(css)) + css = NULL; + rcu_read_unlock(); + + return css; +} + /** * cgroup_e_css - obtain a cgroup's effective css for the specified subsystem * @cgrp: the cgroup of interest @@ -3311,11 +3333,40 @@ static int cgroup_stat_show(struct seq_file *seq, void *v) seq_printf(seq, "nr_dying_descendants %d\n", cgroup->nr_dying_descendants); - cgroup_stat_show_cputime(seq, "cpu."); - return 0; } +static int __maybe_unused cgroup_extra_stat_show(struct seq_file *seq, + struct cgroup *cgrp, int ssid) +{ + struct cgroup_subsys *ss = cgroup_subsys[ssid]; + struct cgroup_subsys_state *css; + int ret; + + if (!ss->css_extra_stat_show) + return 0; + + css = cgroup_tryget_css(cgrp, ss); + if (!css) + return 0; + + ret = ss->css_extra_stat_show(seq, css); + css_put(css); + return ret; +} + +static int cpu_stat_show(struct seq_file *seq, void *v) +{ + struct cgroup *cgrp = seq_css(seq)->cgroup; + int ret = 0; + + cgroup_stat_show_cputime(seq); +#ifdef CONFIG_CGROUP_SCHED + ret = cgroup_extra_stat_show(seq, cgrp, cpu_cgrp_id); +#endif + return ret; +} + static int cgroup_file_open(struct kernfs_open_file *of) { struct cftype *cft = of->kn->priv; @@ -4423,6 +4474,11 @@ static struct cftype cgroup_base_files[] = { .name = "cgroup.stat", .seq_show = cgroup_stat_show, }, + { + .name = "cpu.stat", + .flags = CFTYPE_NOT_ON_ROOT, + .seq_show = cpu_stat_show, + }, { } /* terminate */ }; diff --git a/kernel/cgroup/stat.c b/kernel/cgroup/stat.c index 9cce79e89320..133b465691d6 100644 --- a/kernel/cgroup/stat.c +++ b/kernel/cgroup/stat.c @@ -256,7 +256,7 @@ void __cgroup_account_cputime_field(struct cgroup *cgrp, cgroup_cpu_stat_account_end(cgrp, cstat); } -void cgroup_stat_show_cputime(struct seq_file *seq, const char *prefix) +void cgroup_stat_show_cputime(struct seq_file *seq) { struct cgroup *cgrp = seq_css(seq)->cgroup; u64 usage, utime, stime; @@ -278,10 +278,10 @@ void cgroup_stat_show_cputime(struct seq_file *seq, const char *prefix) do_div(utime, NSEC_PER_USEC); do_div(stime, NSEC_PER_USEC); - seq_printf(seq, "%susage_usec %llu\n" - "%suser_usec %llu\n" - "%ssystem_usec %llu\n", - prefix, usage, prefix, utime, prefix, stime); + seq_printf(seq, "usage_usec %llu\n" + "user_usec %llu\n" + "system_usec %llu\n", + usage, utime, stime); } int cgroup_stat_init(struct cgroup *cgrp) diff --git a/kernel/sched/core.c b/kernel/sched/core.c index ad255162a830..0b3eec389552 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -6678,13 +6678,12 @@ static struct cftype cpu_legacy_files[] = { { } /* Terminate */ }; -static int cpu_stat_show(struct seq_file *sf, void *v) +static int cpu_extra_stat_show(struct seq_file *sf, + struct cgroup_subsys_state *css) { - cgroup_stat_show_cputime(sf, ""); - #ifdef CONFIG_CFS_BANDWIDTH { - struct task_group *tg = css_tg(seq_css(sf)); + struct task_group *tg = css_tg(css); struct cfs_bandwidth *cfs_b = &tg->cfs_bandwidth; u64 throttled_usec; @@ -6817,11 +6816,6 @@ static ssize_t cpu_max_write(struct kernfs_open_file *of, #endif static struct cftype cpu_files[] = { - { - .name = "stat", - .flags = CFTYPE_NOT_ON_ROOT, - .seq_show = cpu_stat_show, - }, #ifdef CONFIG_FAIR_GROUP_SCHED { .name = "weight", @@ -6852,6 +6846,7 @@ struct cgroup_subsys cpu_cgrp_subsys = { .css_online = cpu_cgroup_css_online, .css_released = cpu_cgroup_css_released, .css_free = cpu_cgroup_css_free, + .css_extra_stat_show = cpu_extra_stat_show, .fork = cpu_cgroup_fork, .can_attach = cpu_cgroup_can_attach, .attach = cpu_cgroup_attach, -- cgit v1.2.3-59-g8ed1b From 32d18ab1d44166cbb1dcaf8b359183636734ddb1 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 24 Oct 2017 12:41:01 +0200 Subject: net: updating dst lastusage is an unlikely event. Since commit 0da4af00b2ed ("ipv6: only update __use and lastusetime once per jiffy at most"), updating the dst lastuse field is an unlikely action: it happens at most once per jiffy, out of potentially millions of calls per second. Mark explicitly the code as such, and let the compiler generate better code. Note: gcc 7.2 and several older versions do actually generate different - better - code when the unlikely() hint is in place, avoid jump in the fast path and keeping better code locality. Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller --- include/net/dst.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/dst.h b/include/net/dst.h index 5047e8053d6c..2f53ecc2c296 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -257,7 +257,7 @@ static inline void dst_hold(struct dst_entry *dst) static inline void dst_use_noref(struct dst_entry *dst, unsigned long time) { - if (time != dst->lastuse) { + if (unlikely(time != dst->lastuse)) { dst->__use++; dst->lastuse = time; } -- cgit v1.2.3-59-g8ed1b From 032cfd66afcc2dd2c7be89c71b020fcb15bcc37d Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 25 Oct 2017 03:53:59 -0700 Subject: drivers/net: wan/sdla: Convert timers to use timer_setup() In preparation for unconditionally passing the struct timer_list pointer to all timer callbacks, switch to using the new timer_setup() and from_timer() to pass the timer pointer explicitly. Cc: Allen Pais Cc: "David S. Miller" Cc: Tobias Klauser Cc: netdev@vger.kernel.org Signed-off-by: Kees Cook Signed-off-by: David S. Miller --- drivers/net/wan/sdla.c | 12 +++++------- include/linux/if_frad.h | 1 + 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/net/wan/sdla.c b/drivers/net/wan/sdla.c index 0cc48902dbb9..57ed259c8208 100644 --- a/drivers/net/wan/sdla.c +++ b/drivers/net/wan/sdla.c @@ -927,13 +927,10 @@ static irqreturn_t sdla_isr(int dummy, void *dev_id) return IRQ_HANDLED; } -static void sdla_poll(unsigned long device) +static void sdla_poll(struct timer_list *t) { - struct net_device *dev; - struct frad_local *flp; - - dev = (struct net_device *) device; - flp = netdev_priv(dev); + struct frad_local *flp = from_timer(flp, t, timer); + struct net_device *dev = flp->dev; if (sdla_byte(dev, SDLA_502_RCV_BUF)) sdla_receive(dev); @@ -1616,8 +1613,9 @@ static void setup_sdla(struct net_device *dev) flp->assoc = sdla_assoc; flp->deassoc = sdla_deassoc; flp->dlci_conf = sdla_dlci_conf; + flp->dev = dev; - setup_timer(&flp->timer, sdla_poll, (unsigned long)dev); + timer_setup(&flp->timer, sdla_poll, 0); flp->timer.expires = 1; } diff --git a/include/linux/if_frad.h b/include/linux/if_frad.h index 46df7e565d6f..82a1b4e93570 100644 --- a/include/linux/if_frad.h +++ b/include/linux/if_frad.h @@ -83,6 +83,7 @@ struct frad_local /* fields that are used by the Sangoma SDLA cards */ struct timer_list timer; + struct net_device *dev; int type; /* adapter type */ int state; /* state of the S502/8 control latch */ int buffer; /* current buffer for S508 firmware */ -- cgit v1.2.3-59-g8ed1b From ecad0d2cb8a7997afdc95031ee3328b997aba5c4 Mon Sep 17 00:00:00 2001 From: James Smart Date: Mon, 23 Oct 2017 15:11:36 -0700 Subject: nvme-fc: remove NVME_FC_MAX_SEGMENTS The define is an arbitrary limit to the io size on the initiator, capping the io to 1MB-4KB. Remove the define from the transport. I/O size will solely be limited by the LLDD sg limits. Signed-off-by: James Smart Signed-off-by: Christoph Hellwig --- drivers/nvme/host/fc.c | 6 ++---- include/linux/nvme-fc-driver.h | 2 -- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index d12775d12983..b600c07803cf 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -2435,7 +2435,6 @@ static int nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) { struct nvmf_ctrl_options *opts = ctrl->ctrl.opts; - u32 segs; int ret; bool changed; @@ -2486,9 +2485,8 @@ nvme_fc_create_association(struct nvme_fc_ctrl *ctrl) if (ret) goto out_disconnect_admin_queue; - segs = min_t(u32, NVME_FC_MAX_SEGMENTS, - ctrl->lport->ops->max_sgl_segments); - ctrl->ctrl.max_hw_sectors = (segs - 1) << (PAGE_SHIFT - 9); + ctrl->ctrl.max_hw_sectors = + (ctrl->lport->ops->max_sgl_segments - 1) << (PAGE_SHIFT - 9); ret = nvme_init_identify(&ctrl->ctrl); if (ret) diff --git a/include/linux/nvme-fc-driver.h b/include/linux/nvme-fc-driver.h index 4ea03b9a5c8c..2be4db353937 100644 --- a/include/linux/nvme-fc-driver.h +++ b/include/linux/nvme-fc-driver.h @@ -102,8 +102,6 @@ enum nvmefc_fcp_datadir { }; -#define NVME_FC_MAX_SEGMENTS 256 - /** * struct nvmefc_fcp_req - Request structure passed from NVME-FC transport * to LLDD in order to perform a NVME FCP IO operation. -- cgit v1.2.3-59-g8ed1b From 2ae21cf527da0e5cf9d7ee14bd5b0909bb9d1a75 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:54:56 -0700 Subject: tcp: Namespace-ify sysctl_tcp_early_retrans Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 18 +++++++++--------- net/ipv4/tcp_input.c | 1 - net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 4 +++- 6 files changed, 14 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 2c4222a5d102..a7f39e3ea666 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -128,6 +128,7 @@ struct netns_ipv4 { int sysctl_tcp_sack; int sysctl_tcp_window_scaling; int sysctl_tcp_timestamps; + int sysctl_tcp_early_retrans; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 285bc82dea41..a12b71d4118b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -265,7 +265,6 @@ extern int sysctl_tcp_workaround_signed_windows; extern int sysctl_tcp_slow_start_after_idle; extern int sysctl_tcp_thin_linear_timeouts; extern int sysctl_tcp_thin_dupack; -extern int sysctl_tcp_early_retrans; extern int sysctl_tcp_recovery; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 81d218346cf7..f0f650f020af 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -634,15 +634,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_early_retrans", - .data = &sysctl_tcp_early_retrans, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &four, - }, { .procname = "tcp_min_tso_segs", .data = &sysctl_tcp_min_tso_segs, @@ -1145,6 +1136,15 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_early_retrans", + .data = &init_net.ipv4.sysctl_tcp_early_retrans, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &four, + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 337f6011528a..7656b1e6d504 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -95,7 +95,6 @@ int sysctl_tcp_max_orphans __read_mostly = NR_FILE; int sysctl_tcp_frto __read_mostly = 2; int sysctl_tcp_min_rtt_wlen __read_mostly = 300; int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; -int sysctl_tcp_early_retrans __read_mostly = 3; int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 23a8100af5ad..7ab313f6768e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2484,6 +2484,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_sack = 1; net->ipv4.sysctl_tcp_window_scaling = 1; net->ipv4.sysctl_tcp_timestamps = 1; + net->ipv4.sysctl_tcp_early_retrans = 3; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index c8fc512e0bbb..21713836d46a 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2435,6 +2435,7 @@ bool tcp_schedule_loss_probe(struct sock *sk) struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); u32 timeout, rto_delta_us; + int early_retrans; /* Don't do any loss probe on a Fast Open connection before 3WHS * finishes. @@ -2442,10 +2443,11 @@ bool tcp_schedule_loss_probe(struct sock *sk) if (tp->fastopen_rsk) return false; + early_retrans = sock_net(sk)->ipv4.sysctl_tcp_early_retrans; /* Schedule a loss probe in 2*RTT for SACK capable connections * in Open state, that are either limited by cwnd or application. */ - if ((sysctl_tcp_early_retrans != 3 && sysctl_tcp_early_retrans != 4) || + if ((early_retrans != 3 && early_retrans != 4) || !tp->packets_out || !tcp_is_sack(tp) || icsk->icsk_ca_state != TCP_CA_Open) return false; -- cgit v1.2.3-59-g8ed1b From e20223f1962831d1b1c416d59d259879d0639d68 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:54:57 -0700 Subject: tcp: Namespace-ify sysctl_tcp_recovery Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 2 +- net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 2 +- net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_recovery.c | 2 -- 6 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index a7f39e3ea666..d6ed718075d4 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -129,6 +129,7 @@ struct netns_ipv4 { int sysctl_tcp_window_scaling; int sysctl_tcp_timestamps; int sysctl_tcp_early_retrans; + int sysctl_tcp_recovery; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index a12b71d4118b..c7f51534fc44 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -265,7 +265,7 @@ extern int sysctl_tcp_workaround_signed_windows; extern int sysctl_tcp_slow_start_after_idle; extern int sysctl_tcp_thin_linear_timeouts; extern int sysctl_tcp_thin_dupack; -extern int sysctl_tcp_recovery; + #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ extern int sysctl_tcp_limit_output_bytes; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index f0f650f020af..78019adcae87 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -449,13 +449,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_recovery", - .data = &sysctl_tcp_recovery, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "tcp_max_reordering", .data = &sysctl_tcp_max_reordering, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = &zero, .extra2 = &four, }, + { + .procname = "tcp_recovery", + .data = &init_net.ipv4.sysctl_tcp_recovery, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 7656b1e6d504..5b2272dbf6a9 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2788,7 +2788,7 @@ static void tcp_rack_identify_loss(struct sock *sk, int *ack_flag) struct tcp_sock *tp = tcp_sk(sk); /* Use RACK to detect loss */ - if (sysctl_tcp_recovery & TCP_RACK_LOSS_DETECTION) { + if (sock_net(sk)->ipv4.sysctl_tcp_recovery & TCP_RACK_LOSS_DETECTION) { u32 prior_retrans = tp->retrans_out; tcp_rack_mark_lost(sk); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 7ab313f6768e..517ff1948a71 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2485,6 +2485,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_window_scaling = 1; net->ipv4.sysctl_tcp_timestamps = 1; net->ipv4.sysctl_tcp_early_retrans = 3; + net->ipv4.sysctl_tcp_recovery = TCP_RACK_LOSS_DETECTION; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c index cda6074a429a..d3603a9e24ea 100644 --- a/net/ipv4/tcp_recovery.c +++ b/net/ipv4/tcp_recovery.c @@ -1,8 +1,6 @@ #include #include -int sysctl_tcp_recovery __read_mostly = TCP_RACK_LOSS_DETECTION; - static void tcp_rack_mark_skb_lost(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); -- cgit v1.2.3-59-g8ed1b From 2c04ac8ae0b61e0780a30b7069a11bb202b21f26 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:54:58 -0700 Subject: tcp: Namespace-ify sysctl_tcp_thin_linear_timeouts Note that sysctl_tcp_thin_dupack was not used, I deleted it. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 2 -- net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_timer.c | 4 +--- 4 files changed, 9 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index d6ed718075d4..2a9f37b39c45 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -130,6 +130,7 @@ struct netns_ipv4 { int sysctl_tcp_timestamps; int sysctl_tcp_early_retrans; int sysctl_tcp_recovery; + int sysctl_tcp_thin_linear_timeouts; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index c7f51534fc44..063a7a48b7fe 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -263,8 +263,6 @@ extern int sysctl_tcp_moderate_rcvbuf; extern int sysctl_tcp_tso_win_divisor; extern int sysctl_tcp_workaround_signed_windows; extern int sysctl_tcp_slow_start_after_idle; -extern int sysctl_tcp_thin_linear_timeouts; -extern int sysctl_tcp_thin_dupack; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 78019adcae87..12003214f4d8 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -620,13 +620,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_allowed_congestion_control, }, - { - .procname = "tcp_thin_linear_timeouts", - .data = &sysctl_tcp_thin_linear_timeouts, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_min_tso_segs", .data = &sysctl_tcp_min_tso_segs, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "tcp_thin_linear_timeouts", + .data = &init_net.ipv4.sysctl_tcp_thin_linear_timeouts, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 804a8d34ce86..035a1ef1f2d8 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -22,8 +22,6 @@ #include #include -int sysctl_tcp_thin_linear_timeouts __read_mostly; - /** * tcp_write_err() - close socket and save error info * @sk: The socket the error has appeared on. @@ -522,7 +520,7 @@ out_reset_timer: * linear-timeout retransmissions into a black hole */ if (sk->sk_state == TCP_ESTABLISHED && - (tp->thin_lto || sysctl_tcp_thin_linear_timeouts) && + (tp->thin_lto || net->ipv4.sysctl_tcp_thin_linear_timeouts) && tcp_stream_is_thin(tp) && icsk->icsk_retransmits <= TCP_THIN_LINEAR_RETRIES) { icsk->icsk_backoff = 0; -- cgit v1.2.3-59-g8ed1b From b510f0d23a47c3d1f074fe583e7867dc4918fe02 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:54:59 -0700 Subject: tcp: Namespace-ify sysctl_tcp_slow_start_after_idle Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 3 +-- net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 5 +---- 5 files changed, 11 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 2a9f37b39c45..8662692686b3 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -131,6 +131,7 @@ struct netns_ipv4 { int sysctl_tcp_early_retrans; int sysctl_tcp_recovery; int sysctl_tcp_thin_linear_timeouts; + int sysctl_tcp_slow_start_after_idle; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 063a7a48b7fe..cc2ab522eb5c 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -262,7 +262,6 @@ extern int sysctl_tcp_nometrics_save; extern int sysctl_tcp_moderate_rcvbuf; extern int sysctl_tcp_tso_win_divisor; extern int sysctl_tcp_workaround_signed_windows; -extern int sysctl_tcp_slow_start_after_idle; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ @@ -1308,7 +1307,7 @@ static inline void tcp_slow_start_after_idle_check(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); s32 delta; - if (!sysctl_tcp_slow_start_after_idle || tp->packets_out || + if (!sock_net(sk)->ipv4.sysctl_tcp_slow_start_after_idle || tp->packets_out || ca_ops->cong_control) return; delta = tcp_jiffies32 - tp->lsndtime; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 12003214f4d8..40d69af8b363 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -571,13 +571,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_slow_start_after_idle", - .data = &sysctl_tcp_slow_start_after_idle, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, #ifdef CONFIG_NETLABEL { .procname = "cipso_cache_enable", @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_slow_start_after_idle", + .data = &init_net.ipv4.sysctl_tcp_slow_start_after_idle, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 517ff1948a71..cea63a4b5965 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2486,6 +2486,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_timestamps = 1; net->ipv4.sysctl_tcp_early_retrans = 3; net->ipv4.sysctl_tcp_recovery = TCP_RACK_LOSS_DETECTION; + net->ipv4.sysctl_tcp_slow_start_after_idle = 1; /* By default, RFC2861 behavior. */ net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 21713836d46a..bdc288a06f94 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -62,9 +62,6 @@ int sysctl_tcp_limit_output_bytes __read_mostly = 262144; */ int sysctl_tcp_tso_win_divisor __read_mostly = 3; -/* By default, RFC2861 behavior. */ -int sysctl_tcp_slow_start_after_idle __read_mostly = 1; - static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp); @@ -1690,7 +1687,7 @@ static void tcp_cwnd_validate(struct sock *sk, bool is_cwnd_limited) if (tp->packets_out > tp->snd_cwnd_used) tp->snd_cwnd_used = tp->packets_out; - if (sysctl_tcp_slow_start_after_idle && + if (sock_net(sk)->ipv4.sysctl_tcp_slow_start_after_idle && (s32)(tcp_jiffies32 - tp->snd_cwnd_stamp) >= inet_csk(sk)->icsk_rto && !ca_ops->cong_control) tcp_cwnd_application_limited(sk); -- cgit v1.2.3-59-g8ed1b From e0a1e5b519236dc1662ff25e42560dd1be9e3776 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:00 -0700 Subject: tcp: Namespace-ify sysctl_tcp_retrans_collapse Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_output.c | 5 +---- 5 files changed, 10 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 8662692686b3..b28c172b10e4 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -132,6 +132,7 @@ struct netns_ipv4 { int sysctl_tcp_recovery; int sysctl_tcp_thin_linear_timeouts; int sysctl_tcp_slow_start_after_idle; + int sysctl_tcp_retrans_collapse; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index cc2ab522eb5c..33cc86355b8f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -243,7 +243,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* sysctl variables for tcp */ -extern int sysctl_tcp_retrans_collapse; extern int sysctl_tcp_stdurg; extern int sysctl_tcp_rfc1337; extern int sysctl_tcp_abort_on_overflow; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 40d69af8b363..533b92ad39dd 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -386,13 +386,6 @@ static int proc_tcp_available_ulp(struct ctl_table *ctl, } static struct ctl_table ipv4_table[] = { - { - .procname = "tcp_retrans_collapse", - .data = &sysctl_tcp_retrans_collapse, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_max_orphans", .data = &sysctl_tcp_max_orphans, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_retrans_collapse", + .data = &init_net.ipv4.sysctl_tcp_retrans_collapse, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index cea63a4b5965..2bc6ba2059d3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2487,7 +2487,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_early_retrans = 3; net->ipv4.sysctl_tcp_recovery = TCP_RACK_LOSS_DETECTION; net->ipv4.sysctl_tcp_slow_start_after_idle = 1; /* By default, RFC2861 behavior. */ - + net->ipv4.sysctl_tcp_retrans_collapse = 1; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); net->ipv4.sysctl_tcp_fastopen_blackhole_timeout = 60 * 60; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index bdc288a06f94..55a0aa4b96df 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -45,9 +45,6 @@ #include -/* People can turn this off for buggy TCP's found in printers etc. */ -int sysctl_tcp_retrans_collapse __read_mostly = 1; - /* People can turn this on to work with those rare, broken TCPs that * interpret the window field as a signed quantity. */ @@ -2804,7 +2801,7 @@ static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to, struct sk_buff *skb = to, *tmp; bool first = true; - if (!sysctl_tcp_retrans_collapse) + if (!sock_net(sk)->ipv4.sysctl_tcp_retrans_collapse) return; if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN) return; -- cgit v1.2.3-59-g8ed1b From 3f4c7c6f6a9053493ce7dd8a0f17ed8eafc53893 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:01 -0700 Subject: tcp: Namespace-ify sysctl_tcp_stdurg Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 3 +-- 4 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index b28c172b10e4..ffa2cf3dc747 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -133,6 +133,7 @@ struct netns_ipv4 { int sysctl_tcp_thin_linear_timeouts; int sysctl_tcp_slow_start_after_idle; int sysctl_tcp_retrans_collapse; + int sysctl_tcp_stdurg; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 33cc86355b8f..cf3fac7008d7 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -243,7 +243,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* sysctl variables for tcp */ -extern int sysctl_tcp_stdurg; extern int sysctl_tcp_rfc1337; extern int sysctl_tcp_abort_on_overflow; extern int sysctl_tcp_max_orphans; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 533b92ad39dd..a34bb75815c1 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -400,13 +400,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_stdurg", - .data = &sysctl_tcp_stdurg, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_rfc1337", .data = &sysctl_tcp_rfc1337, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_stdurg", + .data = &init_net.ipv4.sysctl_tcp_stdurg, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 5b2272dbf6a9..14b06963c102 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -89,7 +89,6 @@ EXPORT_SYMBOL(sysctl_tcp_adv_win_scale); /* rfc5961 challenge ack rate limiting */ int sysctl_tcp_challenge_ack_limit = 1000; -int sysctl_tcp_stdurg __read_mostly; int sysctl_tcp_rfc1337 __read_mostly; int sysctl_tcp_max_orphans __read_mostly = NR_FILE; int sysctl_tcp_frto __read_mostly = 2; @@ -5123,7 +5122,7 @@ static void tcp_check_urg(struct sock *sk, const struct tcphdr *th) struct tcp_sock *tp = tcp_sk(sk); u32 ptr = ntohs(th->urg_ptr); - if (ptr && !sysctl_tcp_stdurg) + if (ptr && !sock_net(sk)->ipv4.sysctl_tcp_stdurg) ptr--; ptr += ntohl(th->seq); -- cgit v1.2.3-59-g8ed1b From 625357aa175c688d219da43c8cfaa2e1629e0e1a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:02 -0700 Subject: tcp: Namespace-ify sysctl_tcp_rfc1337 Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 1 - net/ipv4/tcp_minisocks.c | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index ffa2cf3dc747..968edce38eb5 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -134,6 +134,7 @@ struct netns_ipv4 { int sysctl_tcp_slow_start_after_idle; int sysctl_tcp_retrans_collapse; int sysctl_tcp_stdurg; + int sysctl_tcp_rfc1337; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index cf3fac7008d7..2aea2b3373b3 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -243,7 +243,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* sysctl variables for tcp */ -extern int sysctl_tcp_rfc1337; extern int sysctl_tcp_abort_on_overflow; extern int sysctl_tcp_max_orphans; extern int sysctl_tcp_fack; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index a34bb75815c1..832e554235df 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -400,13 +400,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_rfc1337", - .data = &sysctl_tcp_rfc1337, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "inet_peer_threshold", .data = &inet_peer_threshold, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_rfc1337", + .data = &init_net.ipv4.sysctl_tcp_rfc1337, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 14b06963c102..64fde81b0eb7 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -89,7 +89,6 @@ EXPORT_SYMBOL(sysctl_tcp_adv_win_scale); /* rfc5961 challenge ack rate limiting */ int sysctl_tcp_challenge_ack_limit = 1000; -int sysctl_tcp_rfc1337 __read_mostly; int sysctl_tcp_max_orphans __read_mostly = NR_FILE; int sysctl_tcp_frto __read_mostly = 2; int sysctl_tcp_min_rtt_wlen __read_mostly = 300; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 056009f1c14f..11836667763c 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -181,7 +181,7 @@ tcp_timewait_state_process(struct inet_timewait_sock *tw, struct sk_buff *skb, * Oh well... nobody has a sufficient solution to this * protocol bug yet. */ - if (sysctl_tcp_rfc1337 == 0) { + if (twsk_net(tw)->ipv4.sysctl_tcp_rfc1337 == 0) { kill: inet_twsk_deschedule_put(tw); return TCP_TW_SUCCESS; -- cgit v1.2.3-59-g8ed1b From 65c9410cf55ecf32da1b720f563365d565d6289a Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:03 -0700 Subject: tcp: Namespace-ify sysctl_tcp_abort_on_overflow Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_minisocks.c | 4 +--- 4 files changed, 9 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 968edce38eb5..3875fdf6b186 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -135,6 +135,7 @@ struct netns_ipv4 { int sysctl_tcp_retrans_collapse; int sysctl_tcp_stdurg; int sysctl_tcp_rfc1337; + int sysctl_tcp_abort_on_overflow; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 2aea2b3373b3..7331281a2292 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -243,7 +243,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* sysctl variables for tcp */ -extern int sysctl_tcp_abort_on_overflow; extern int sysctl_tcp_max_orphans; extern int sysctl_tcp_fack; extern int sysctl_tcp_reordering; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 832e554235df..ffd1fd769bba 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -393,13 +393,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_abort_on_overflow", - .data = &sysctl_tcp_abort_on_overflow, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "inet_peer_threshold", .data = &inet_peer_threshold, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_abort_on_overflow", + .data = &init_net.ipv4.sysctl_tcp_abort_on_overflow, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 11836667763c..3674d63170b2 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -29,8 +29,6 @@ #include #include -int sysctl_tcp_abort_on_overflow __read_mostly; - static bool tcp_in_window(u32 seq, u32 end_seq, u32 s_win, u32 e_win) { if (seq == s_win) @@ -783,7 +781,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, return inet_csk_complete_hashdance(sk, child, req, own_req); listen_overflow: - if (!sysctl_tcp_abort_on_overflow) { + if (!sock_net(sk)->ipv4.sysctl_tcp_abort_on_overflow) { inet_rsk(req)->acked = 1; return NULL; } -- cgit v1.2.3-59-g8ed1b From 0bc65a28ae2aeb14aab7f4a930e0d8cf4cad9dc4 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:04 -0700 Subject: tcp: Namespace-ify sysctl_tcp_fack Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp.c | 2 +- net/ipv4/tcp_input.c | 3 +-- net/ipv4/tcp_minisocks.c | 2 +- 6 files changed, 11 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 3875fdf6b186..f0e792beeea9 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -136,6 +136,7 @@ struct netns_ipv4 { int sysctl_tcp_stdurg; int sysctl_tcp_rfc1337; int sysctl_tcp_abort_on_overflow; + int sysctl_tcp_fack; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 7331281a2292..e7b15e9f6e28 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -244,7 +244,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* sysctl variables for tcp */ extern int sysctl_tcp_max_orphans; -extern int sysctl_tcp_fack; extern int sysctl_tcp_reordering; extern int sysctl_tcp_max_reordering; extern int sysctl_tcp_dsack; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index ffd1fd769bba..1f23be13ce7b 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -414,13 +414,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, - { - .procname = "tcp_fack", - .data = &sysctl_tcp_fack, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_max_reordering", .data = &sysctl_tcp_max_reordering, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_fack", + .data = &init_net.ipv4.sysctl_tcp_fack, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index f6e1c00e300e..c7c983f0f817 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2517,7 +2517,7 @@ static int tcp_repair_options_est(struct sock *sk, return -EINVAL; tp->rx_opt.sack_ok |= TCP_SACK_SEEN; - if (sysctl_tcp_fack) + if (sock_net(sk)->ipv4.sysctl_tcp_fack) tcp_enable_fack(tp); break; case TCPOPT_TIMESTAMP: diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 64fde81b0eb7..c5b94460793f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -79,7 +79,6 @@ #include #include -int sysctl_tcp_fack __read_mostly; int sysctl_tcp_max_reordering __read_mostly = 300; int sysctl_tcp_dsack __read_mostly = 1; int sysctl_tcp_app_win __read_mostly = 31; @@ -5720,7 +5719,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, tp->tcp_header_len = sizeof(struct tcphdr); } - if (tcp_is_sack(tp) && sysctl_tcp_fack) + if (tcp_is_sack(tp) && sock_net(sk)->ipv4.sysctl_tcp_fack) tcp_enable_fack(tp); tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 3674d63170b2..3270ab8416ce 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -510,7 +510,7 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, newtp->rx_opt.tstamp_ok = ireq->tstamp_ok; if ((newtp->rx_opt.sack_ok = ireq->sack_ok) != 0) { - if (sysctl_tcp_fack) + if (sock_net(sk)->ipv4.sysctl_tcp_fack) tcp_enable_fack(newtp); } newtp->window_clamp = req->rsk_window_clamp; -- cgit v1.2.3-59-g8ed1b From 773d4bb96ceca6829ae9928f6b002b93e2e62cdc Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:05 -0700 Subject: tcp: remove stale sysctl_tcp_reordering This extern is no longer used. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index e7b15e9f6e28..fc134ba74c7d 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -244,7 +244,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* sysctl variables for tcp */ extern int sysctl_tcp_max_orphans; -extern int sysctl_tcp_reordering; extern int sysctl_tcp_max_reordering; extern int sysctl_tcp_dsack; extern long sysctl_tcp_mem[3]; -- cgit v1.2.3-59-g8ed1b From c6e218035913e14952b04ceecf1a543205106fdb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:06 -0700 Subject: tcp: Namespace-ify sysctl_tcp_max_reordering Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 3 +-- net/ipv4/tcp_ipv4.c | 2 ++ 5 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index f0e792beeea9..3f6844665a2f 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -137,6 +137,7 @@ struct netns_ipv4 { int sysctl_tcp_rfc1337; int sysctl_tcp_abort_on_overflow; int sysctl_tcp_fack; + int sysctl_tcp_max_reordering; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index fc134ba74c7d..8cd286226a1e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -244,7 +244,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* sysctl variables for tcp */ extern int sysctl_tcp_max_orphans; -extern int sysctl_tcp_max_reordering; extern int sysctl_tcp_dsack; extern long sysctl_tcp_mem[3]; extern int sysctl_tcp_wmem[3]; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 1f23be13ce7b..18cd228a2069 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -414,13 +414,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, - { - .procname = "tcp_max_reordering", - .data = &sysctl_tcp_max_reordering, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_dsack", .data = &sysctl_tcp_dsack, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_max_reordering", + .data = &init_net.ipv4.sysctl_tcp_max_reordering, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index c5b94460793f..c118657f06ee 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -79,7 +79,6 @@ #include #include -int sysctl_tcp_max_reordering __read_mostly = 300; int sysctl_tcp_dsack __read_mostly = 1; int sysctl_tcp_app_win __read_mostly = 31; int sysctl_tcp_adv_win_scale __read_mostly = 1; @@ -889,7 +888,7 @@ static void tcp_update_reordering(struct sock *sk, const int metric, return; if (metric > tp->reordering) { - tp->reordering = min(sysctl_tcp_max_reordering, metric); + tp->reordering = min(sock_net(sk)->ipv4.sysctl_tcp_max_reordering, metric); #if FASTRETRANS_DEBUG > 1 pr_debug("Disorder%d %d %u f%u s%u rr%d\n", diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 2bc6ba2059d3..c379a242abb3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2488,6 +2488,8 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_recovery = TCP_RACK_LOSS_DETECTION; net->ipv4.sysctl_tcp_slow_start_after_idle = 1; /* By default, RFC2861 behavior. */ net->ipv4.sysctl_tcp_retrans_collapse = 1; + net->ipv4.sysctl_tcp_max_reordering = 300; + net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); net->ipv4.sysctl_tcp_fastopen_blackhole_timeout = 60 * 60; -- cgit v1.2.3-59-g8ed1b From 6496f6bde0c323fba5e8c5b5cbf3a7bf28dad7ed Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:07 -0700 Subject: tcp: Namespace-ify sysctl_tcp_dsack Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 5 ++--- net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 3f6844665a2f..956957a77db9 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -138,6 +138,7 @@ struct netns_ipv4 { int sysctl_tcp_abort_on_overflow; int sysctl_tcp_fack; int sysctl_tcp_max_reordering; + int sysctl_tcp_dsack; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 8cd286226a1e..8b2ae3e8d79f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -244,7 +244,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); /* sysctl variables for tcp */ extern int sysctl_tcp_max_orphans; -extern int sysctl_tcp_dsack; extern long sysctl_tcp_mem[3]; extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 18cd228a2069..7652a9c2a65d 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -414,13 +414,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec_jiffies, }, - { - .procname = "tcp_dsack", - .data = &sysctl_tcp_dsack, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_mem", .maxlen = sizeof(sysctl_tcp_mem), @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_dsack", + .data = &init_net.ipv4.sysctl_tcp_dsack, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index c118657f06ee..fd77037ac800 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -79,7 +79,6 @@ #include #include -int sysctl_tcp_dsack __read_mostly = 1; int sysctl_tcp_app_win __read_mostly = 31; int sysctl_tcp_adv_win_scale __read_mostly = 1; EXPORT_SYMBOL(sysctl_tcp_adv_win_scale); @@ -4150,7 +4149,7 @@ static void tcp_dsack_set(struct sock *sk, u32 seq, u32 end_seq) { struct tcp_sock *tp = tcp_sk(sk); - if (tcp_is_sack(tp) && sysctl_tcp_dsack) { + if (tcp_is_sack(tp) && sock_net(sk)->ipv4.sysctl_tcp_dsack) { int mib_idx; if (before(seq, tp->rcv_nxt)) @@ -4185,7 +4184,7 @@ static void tcp_send_dupack(struct sock *sk, const struct sk_buff *skb) NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOST); tcp_enter_quickack_mode(sk); - if (tcp_is_sack(tp) && sysctl_tcp_dsack) { + if (tcp_is_sack(tp) && sock_net(sk)->ipv4.sysctl_tcp_dsack) { u32 end_seq = TCP_SKB_CB(skb)->end_seq; if (after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index c379a242abb3..d9d4d191e8f3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2489,6 +2489,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_slow_start_after_idle = 1; /* By default, RFC2861 behavior. */ net->ipv4.sysctl_tcp_retrans_collapse = 1; net->ipv4.sysctl_tcp_max_reordering = 300; + net->ipv4.sysctl_tcp_dsack = 1; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From 0c12654ac6d9004b9538b2a969b2b59e9a5ed831 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:08 -0700 Subject: tcp: Namespace-ify sysctl_tcp_app_win Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 8 ++++---- net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 13 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 956957a77db9..63f91d52cbc0 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -139,6 +139,7 @@ struct netns_ipv4 { int sysctl_tcp_fack; int sysctl_tcp_max_reordering; int sysctl_tcp_dsack; + int sysctl_tcp_app_win; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 8b2ae3e8d79f..7aa3d65062a1 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -247,7 +247,6 @@ extern int sysctl_tcp_max_orphans; extern long sysctl_tcp_mem[3]; extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; -extern int sysctl_tcp_app_win; extern int sysctl_tcp_adv_win_scale; extern int sysctl_tcp_frto; extern int sysctl_tcp_nometrics_save; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 7652a9c2a65d..e057788834a9 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -437,13 +437,6 @@ static struct ctl_table ipv4_table[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &one, }, - { - .procname = "tcp_app_win", - .data = &sysctl_tcp_app_win, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_adv_win_scale", .data = &sysctl_tcp_adv_win_scale, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_app_win", + .data = &init_net.ipv4.sysctl_tcp_app_win, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index fd77037ac800..6af4b58ac6d5 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -79,7 +79,6 @@ #include #include -int sysctl_tcp_app_win __read_mostly = 31; int sysctl_tcp_adv_win_scale __read_mostly = 1; EXPORT_SYMBOL(sysctl_tcp_adv_win_scale); @@ -428,6 +427,7 @@ static void tcp_fixup_rcvbuf(struct sock *sk) */ void tcp_init_buffer_space(struct sock *sk) { + int tcp_app_win = sock_net(sk)->ipv4.sysctl_tcp_app_win; struct tcp_sock *tp = tcp_sk(sk); int maxwin; @@ -446,14 +446,14 @@ void tcp_init_buffer_space(struct sock *sk) if (tp->window_clamp >= maxwin) { tp->window_clamp = maxwin; - if (sysctl_tcp_app_win && maxwin > 4 * tp->advmss) + if (tcp_app_win && maxwin > 4 * tp->advmss) tp->window_clamp = max(maxwin - - (maxwin >> sysctl_tcp_app_win), + (maxwin >> tcp_app_win), 4 * tp->advmss); } /* Force reservation of one segment. */ - if (sysctl_tcp_app_win && + if (tcp_app_win && tp->window_clamp > 2 * tp->advmss && tp->window_clamp + tp->advmss > maxwin) tp->window_clamp = max(2 * tp->advmss, maxwin - tp->advmss); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d9d4d191e8f3..189664ebd28e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2490,6 +2490,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_retrans_collapse = 1; net->ipv4.sysctl_tcp_max_reordering = 300; net->ipv4.sysctl_tcp_dsack = 1; + net->ipv4.sysctl_tcp_app_win = 31; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From 94f0893e0c27219f4a726932618505aab6795973 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:09 -0700 Subject: tcp: Namespace-ify sysctl_tcp_adv_win_scale Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 9 ++++----- net/ipv4/sysctl_net_ipv4.c | 18 +++++++++--------- net/ipv4/tcp_input.c | 13 +++++-------- net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 20 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 63f91d52cbc0..9dbb07d4eff4 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -140,6 +140,7 @@ struct netns_ipv4 { int sysctl_tcp_max_reordering; int sysctl_tcp_dsack; int sysctl_tcp_app_win; + int sysctl_tcp_adv_win_scale; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 7aa3d65062a1..0dc27cd24899 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -247,7 +247,6 @@ extern int sysctl_tcp_max_orphans; extern long sysctl_tcp_mem[3]; extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; -extern int sysctl_tcp_adv_win_scale; extern int sysctl_tcp_frto; extern int sysctl_tcp_nometrics_save; extern int sysctl_tcp_moderate_rcvbuf; @@ -1311,9 +1310,9 @@ void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, __u32 *window_clamp, int wscale_ok, __u8 *rcv_wscale, __u32 init_rcv_wnd); -static inline int tcp_win_from_space(int space) +static inline int tcp_win_from_space(const struct sock *sk, int space) { - int tcp_adv_win_scale = sysctl_tcp_adv_win_scale; + int tcp_adv_win_scale = sock_net(sk)->ipv4.sysctl_tcp_adv_win_scale; return tcp_adv_win_scale <= 0 ? (space>>(-tcp_adv_win_scale)) : @@ -1323,13 +1322,13 @@ static inline int tcp_win_from_space(int space) /* Note: caller must be prepared to deal with negative returns */ static inline int tcp_space(const struct sock *sk) { - return tcp_win_from_space(sk->sk_rcvbuf - + return tcp_win_from_space(sk, sk->sk_rcvbuf - atomic_read(&sk->sk_rmem_alloc)); } static inline int tcp_full_space(const struct sock *sk) { - return tcp_win_from_space(sk->sk_rcvbuf); + return tcp_win_from_space(sk, sk->sk_rcvbuf); } extern void tcp_openreq_init_rwin(struct request_sock *req, diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index e057788834a9..a95123e1e7da 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -437,15 +437,6 @@ static struct ctl_table ipv4_table[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &one, }, - { - .procname = "tcp_adv_win_scale", - .data = &sysctl_tcp_adv_win_scale, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &tcp_adv_win_scale_min, - .extra2 = &tcp_adv_win_scale_max, - }, { .procname = "tcp_frto", .data = &sysctl_tcp_frto, @@ -1145,6 +1136,15 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_adv_win_scale", + .data = &init_net.ipv4.sysctl_tcp_adv_win_scale, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &tcp_adv_win_scale_min, + .extra2 = &tcp_adv_win_scale_max, + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 6af4b58ac6d5..8ee2c84b0bc6 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -79,9 +79,6 @@ #include #include -int sysctl_tcp_adv_win_scale __read_mostly = 1; -EXPORT_SYMBOL(sysctl_tcp_adv_win_scale); - /* rfc5961 challenge ack rate limiting */ int sysctl_tcp_challenge_ack_limit = 1000; @@ -363,8 +360,8 @@ static int __tcp_grow_window(const struct sock *sk, const struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); /* Optimize this! */ - int truesize = tcp_win_from_space(skb->truesize) >> 1; - int window = tcp_win_from_space(sysctl_tcp_rmem[2]) >> 1; + int truesize = tcp_win_from_space(sk, skb->truesize) >> 1; + int window = tcp_win_from_space(sk, sysctl_tcp_rmem[2]) >> 1; while (tp->rcv_ssthresh <= window) { if (truesize <= skb->len) @@ -389,7 +386,7 @@ static void tcp_grow_window(struct sock *sk, const struct sk_buff *skb) /* Check #2. Increase window, if skb with such overhead * will fit to rcvbuf in future. */ - if (tcp_win_from_space(skb->truesize) <= skb->len) + if (tcp_win_from_space(sk, skb->truesize) <= skb->len) incr = 2 * tp->advmss; else incr = __tcp_grow_window(sk, skb); @@ -630,7 +627,7 @@ void tcp_rcv_space_adjust(struct sock *sk) } rcvmem = SKB_TRUESIZE(tp->advmss + MAX_TCP_HEADER); - while (tcp_win_from_space(rcvmem) < tp->advmss) + while (tcp_win_from_space(sk, rcvmem) < tp->advmss) rcvmem += 128; rcvbuf = min(rcvwin / tp->advmss * rcvmem, sysctl_tcp_rmem[2]); @@ -4809,7 +4806,7 @@ restart: * overlaps to the next one. */ if (!(TCP_SKB_CB(skb)->tcp_flags & (TCPHDR_SYN | TCPHDR_FIN)) && - (tcp_win_from_space(skb->truesize) > skb->len || + (tcp_win_from_space(sk, skb->truesize) > skb->len || before(TCP_SKB_CB(skb)->seq, start))) { end_of_skbs = false; break; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 189664ebd28e..1fe30fb99308 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2491,6 +2491,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_max_reordering = 300; net->ipv4.sysctl_tcp_dsack = 1; net->ipv4.sysctl_tcp_app_win = 31; + net->ipv4.sysctl_tcp_adv_win_scale = 1; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From af9b69a7a6ca6b817e8d6f416e7aa5b2a5bf1d91 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 26 Oct 2017 21:55:10 -0700 Subject: tcp: Namespace-ify sysctl_tcp_frto Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 3 +-- net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 9dbb07d4eff4..f4622e28db3a 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -141,6 +141,7 @@ struct netns_ipv4 { int sysctl_tcp_dsack; int sysctl_tcp_app_win; int sysctl_tcp_adv_win_scale; + int sysctl_tcp_frto; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 0dc27cd24899..18f047501f53 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -247,7 +247,6 @@ extern int sysctl_tcp_max_orphans; extern long sysctl_tcp_mem[3]; extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; -extern int sysctl_tcp_frto; extern int sysctl_tcp_nometrics_save; extern int sysctl_tcp_moderate_rcvbuf; extern int sysctl_tcp_tso_win_divisor; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index a95123e1e7da..f1bcb9b7e082 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -437,13 +437,6 @@ static struct ctl_table ipv4_table[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &one, }, - { - .procname = "tcp_frto", - .data = &sysctl_tcp_frto, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_min_rtt_wlen", .data = &sysctl_tcp_min_rtt_wlen, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = &tcp_adv_win_scale_min, .extra2 = &tcp_adv_win_scale_max, }, + { + .procname = "tcp_frto", + .data = &init_net.ipv4.sysctl_tcp_frto, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 8ee2c84b0bc6..90d76f1c8f96 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -83,7 +83,6 @@ int sysctl_tcp_challenge_ack_limit = 1000; int sysctl_tcp_max_orphans __read_mostly = NR_FILE; -int sysctl_tcp_frto __read_mostly = 2; int sysctl_tcp_min_rtt_wlen __read_mostly = 300; int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2; @@ -2026,7 +2025,7 @@ void tcp_enter_loss(struct sock *sk) * falsely raise the receive window, which results in repeated * timeouts and stop-and-go behavior. */ - tp->frto = sysctl_tcp_frto && + tp->frto = net->ipv4.sysctl_tcp_frto && (new_recovery || icsk->icsk_retransmits) && !inet_csk(sk)->icsk_mtup.probe_size; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 1fe30fb99308..49757c758211 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2492,6 +2492,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_dsack = 1; net->ipv4.sysctl_tcp_app_win = 31; net->ipv4.sysctl_tcp_adv_win_scale = 1; + net->ipv4.sysctl_tcp_frto = 2; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From 5bf24e08b685d862f1249987a6d3423b42989622 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Oct 2017 18:01:32 +0200 Subject: media: cec-pin.h: move non-kAPI parts into cec-pin-priv.h The kAPI cec-pin.h header also defined data structures that did not belong here but were private to the CEC core code. Split that part off into a cec-pin-priv.h header. Signed-off-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/cec/cec-api.c | 1 + drivers/media/cec/cec-pin-priv.h | 133 +++++++++++++++++++++++++++++++++++++++ drivers/media/cec/cec-pin.c | 1 + include/media/cec-pin.h | 107 ------------------------------- 4 files changed, 135 insertions(+), 107 deletions(-) create mode 100644 drivers/media/cec/cec-pin-priv.h (limited to 'include') diff --git a/drivers/media/cec/cec-api.c b/drivers/media/cec/cec-api.c index 465bb3ec21f6..3dba3aa34a43 100644 --- a/drivers/media/cec/cec-api.c +++ b/drivers/media/cec/cec-api.c @@ -32,6 +32,7 @@ #include #include "cec-priv.h" +#include "cec-pin-priv.h" static inline struct cec_devnode *cec_devnode_data(struct file *filp) { diff --git a/drivers/media/cec/cec-pin-priv.h b/drivers/media/cec/cec-pin-priv.h new file mode 100644 index 000000000000..7d0def199762 --- /dev/null +++ b/drivers/media/cec/cec-pin-priv.h @@ -0,0 +1,133 @@ +/* + * cec-pin-priv.h - internal cec-pin header + * + * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef LINUX_CEC_PIN_PRIV_H +#define LINUX_CEC_PIN_PRIV_H + +#include +#include +#include + +enum cec_pin_state { + /* CEC is off */ + CEC_ST_OFF, + /* CEC is idle, waiting for Rx or Tx */ + CEC_ST_IDLE, + + /* Tx states */ + + /* Pending Tx, waiting for Signal Free Time to expire */ + CEC_ST_TX_WAIT, + /* Low-drive was detected, wait for bus to go high */ + CEC_ST_TX_WAIT_FOR_HIGH, + /* Drive CEC low for the start bit */ + CEC_ST_TX_START_BIT_LOW, + /* Drive CEC high for the start bit */ + CEC_ST_TX_START_BIT_HIGH, + /* Drive CEC low for the 0 bit */ + CEC_ST_TX_DATA_BIT_0_LOW, + /* Drive CEC high for the 0 bit */ + CEC_ST_TX_DATA_BIT_0_HIGH, + /* Drive CEC low for the 1 bit */ + CEC_ST_TX_DATA_BIT_1_LOW, + /* Drive CEC high for the 1 bit */ + CEC_ST_TX_DATA_BIT_1_HIGH, + /* + * Wait for start of sample time to check for Ack bit or first + * four initiator bits to check for Arbitration Lost. + */ + CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE, + /* Wait for end of bit period after sampling */ + CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE, + + /* Rx states */ + + /* Start bit low detected */ + CEC_ST_RX_START_BIT_LOW, + /* Start bit high detected */ + CEC_ST_RX_START_BIT_HIGH, + /* Wait for bit sample time */ + CEC_ST_RX_DATA_SAMPLE, + /* Wait for earliest end of bit period after sampling */ + CEC_ST_RX_DATA_POST_SAMPLE, + /* Wait for CEC to go high (i.e. end of bit period */ + CEC_ST_RX_DATA_HIGH, + /* Drive CEC low to send 0 Ack bit */ + CEC_ST_RX_ACK_LOW, + /* End of 0 Ack time, wait for earliest end of bit period */ + CEC_ST_RX_ACK_LOW_POST, + /* Wait for CEC to go high (i.e. end of bit period */ + CEC_ST_RX_ACK_HIGH_POST, + /* Wait for earliest end of bit period and end of message */ + CEC_ST_RX_ACK_FINISH, + + /* Start low drive */ + CEC_ST_LOW_DRIVE, + /* Monitor pin using interrupts */ + CEC_ST_RX_IRQ, + + /* Total number of pin states */ + CEC_PIN_STATES +}; + +#define CEC_NUM_PIN_EVENTS 128 + +#define CEC_PIN_IRQ_UNCHANGED 0 +#define CEC_PIN_IRQ_DISABLE 1 +#define CEC_PIN_IRQ_ENABLE 2 + +struct cec_pin { + struct cec_adapter *adap; + const struct cec_pin_ops *ops; + struct task_struct *kthread; + wait_queue_head_t kthread_waitq; + struct hrtimer timer; + ktime_t ts; + unsigned int wait_usecs; + u16 la_mask; + bool enabled; + bool monitor_all; + bool rx_eom; + bool enable_irq_failed; + enum cec_pin_state state; + struct cec_msg tx_msg; + u32 tx_bit; + bool tx_nacked; + u32 tx_signal_free_time; + struct cec_msg rx_msg; + u32 rx_bit; + + struct cec_msg work_rx_msg; + u8 work_tx_status; + ktime_t work_tx_ts; + atomic_t work_irq_change; + atomic_t work_pin_events; + unsigned int work_pin_events_wr; + unsigned int work_pin_events_rd; + ktime_t work_pin_ts[CEC_NUM_PIN_EVENTS]; + bool work_pin_is_high[CEC_NUM_PIN_EVENTS]; + ktime_t timer_ts; + u32 timer_cnt; + u32 timer_100ms_overruns; + u32 timer_300ms_overruns; + u32 timer_max_overrun; + u32 timer_sum_overrun; +}; + +#endif diff --git a/drivers/media/cec/cec-pin.c b/drivers/media/cec/cec-pin.c index e2aa5d6e619d..a4c881dc81c4 100644 --- a/drivers/media/cec/cec-pin.c +++ b/drivers/media/cec/cec-pin.c @@ -20,6 +20,7 @@ #include #include +#include "cec-pin-priv.h" /* All timings are in microseconds */ diff --git a/include/media/cec-pin.h b/include/media/cec-pin.h index ea84b9c9e0c3..83b3e17e0a07 100644 --- a/include/media/cec-pin.h +++ b/include/media/cec-pin.h @@ -21,71 +21,8 @@ #define LINUX_CEC_PIN_H #include -#include #include -enum cec_pin_state { - /* CEC is off */ - CEC_ST_OFF, - /* CEC is idle, waiting for Rx or Tx */ - CEC_ST_IDLE, - - /* Tx states */ - - /* Pending Tx, waiting for Signal Free Time to expire */ - CEC_ST_TX_WAIT, - /* Low-drive was detected, wait for bus to go high */ - CEC_ST_TX_WAIT_FOR_HIGH, - /* Drive CEC low for the start bit */ - CEC_ST_TX_START_BIT_LOW, - /* Drive CEC high for the start bit */ - CEC_ST_TX_START_BIT_HIGH, - /* Drive CEC low for the 0 bit */ - CEC_ST_TX_DATA_BIT_0_LOW, - /* Drive CEC high for the 0 bit */ - CEC_ST_TX_DATA_BIT_0_HIGH, - /* Drive CEC low for the 1 bit */ - CEC_ST_TX_DATA_BIT_1_LOW, - /* Drive CEC high for the 1 bit */ - CEC_ST_TX_DATA_BIT_1_HIGH, - /* - * Wait for start of sample time to check for Ack bit or first - * four initiator bits to check for Arbitration Lost. - */ - CEC_ST_TX_DATA_BIT_1_HIGH_PRE_SAMPLE, - /* Wait for end of bit period after sampling */ - CEC_ST_TX_DATA_BIT_1_HIGH_POST_SAMPLE, - - /* Rx states */ - - /* Start bit low detected */ - CEC_ST_RX_START_BIT_LOW, - /* Start bit high detected */ - CEC_ST_RX_START_BIT_HIGH, - /* Wait for bit sample time */ - CEC_ST_RX_DATA_SAMPLE, - /* Wait for earliest end of bit period after sampling */ - CEC_ST_RX_DATA_POST_SAMPLE, - /* Wait for CEC to go high (i.e. end of bit period */ - CEC_ST_RX_DATA_HIGH, - /* Drive CEC low to send 0 Ack bit */ - CEC_ST_RX_ACK_LOW, - /* End of 0 Ack time, wait for earliest end of bit period */ - CEC_ST_RX_ACK_LOW_POST, - /* Wait for CEC to go high (i.e. end of bit period */ - CEC_ST_RX_ACK_HIGH_POST, - /* Wait for earliest end of bit period and end of message */ - CEC_ST_RX_ACK_FINISH, - - /* Start low drive */ - CEC_ST_LOW_DRIVE, - /* Monitor pin using interrupts */ - CEC_ST_RX_IRQ, - - /* Total number of pin states */ - CEC_PIN_STATES -}; - /** * struct cec_pin_ops - low-level CEC pin operations * @read: read the CEC pin. Return true if high, false if low. @@ -115,50 +52,6 @@ struct cec_pin_ops { int (*read_hpd)(struct cec_adapter *adap); }; -#define CEC_NUM_PIN_EVENTS 128 - -#define CEC_PIN_IRQ_UNCHANGED 0 -#define CEC_PIN_IRQ_DISABLE 1 -#define CEC_PIN_IRQ_ENABLE 2 - -struct cec_pin { - struct cec_adapter *adap; - const struct cec_pin_ops *ops; - struct task_struct *kthread; - wait_queue_head_t kthread_waitq; - struct hrtimer timer; - ktime_t ts; - unsigned int wait_usecs; - u16 la_mask; - bool enabled; - bool monitor_all; - bool rx_eom; - bool enable_irq_failed; - enum cec_pin_state state; - struct cec_msg tx_msg; - u32 tx_bit; - bool tx_nacked; - u32 tx_signal_free_time; - struct cec_msg rx_msg; - u32 rx_bit; - - struct cec_msg work_rx_msg; - u8 work_tx_status; - ktime_t work_tx_ts; - atomic_t work_irq_change; - atomic_t work_pin_events; - unsigned int work_pin_events_wr; - unsigned int work_pin_events_rd; - ktime_t work_pin_ts[CEC_NUM_PIN_EVENTS]; - bool work_pin_is_high[CEC_NUM_PIN_EVENTS]; - ktime_t timer_ts; - u32 timer_cnt; - u32 timer_100ms_overruns; - u32 timer_300ms_overruns; - u32 timer_max_overrun; - u32 timer_sum_overrun; -}; - /** * cec_pin_changed() - update pin state from interrupt * -- cgit v1.2.3-59-g8ed1b From 035226b964c820f65e201cdf123705a8f1d7c670 Mon Sep 17 00:00:00 2001 From: Gianluca Borello Date: Thu, 26 Oct 2017 01:47:42 +0000 Subject: bpf: remove tail_call and get_stackid helper declarations from bpf.h commit afdb09c720b6 ("security: bpf: Add LSM hooks for bpf object related syscall") included linux/bpf.h in linux/security.h. As a result, bpf programs including bpf_helpers.h and some other header that ends up pulling in also security.h, such as several examples under samples/bpf, fail to compile because bpf_tail_call and bpf_get_stackid are now "redefined as different kind of symbol". >From bpf.h: u64 bpf_tail_call(u64 ctx, u64 r2, u64 index, u64 r4, u64 r5); u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); Whereas in bpf_helpers.h they are: static void (*bpf_tail_call)(void *ctx, void *map, int index); static int (*bpf_get_stackid)(void *ctx, void *map, int flags); Fix this by removing the unused declaration of bpf_tail_call and moving the declaration of bpf_get_stackid in bpf_trace.c, which is the only place where it's needed. Signed-off-by: Gianluca Borello Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/bpf.h | 3 --- kernel/trace/bpf_trace.c | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 172be7faf7ba..520aeebe0d93 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -231,9 +231,6 @@ struct bpf_event_entry { struct rcu_head rcu; }; -u64 bpf_tail_call(u64 ctx, u64 r2, u64 index, u64 r4, u64 r5); -u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); - bool bpf_prog_array_compatible(struct bpf_array *array, const struct bpf_prog *fp); int bpf_prog_calc_tag(struct bpf_prog *fp); diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index b65011d320e3..136aa6bb0422 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -15,6 +15,8 @@ #include #include "trace.h" +u64 bpf_get_stackid(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); + /** * trace_call_bpf - invoke BPF program * @call: tracepoint event -- cgit v1.2.3-59-g8ed1b From 728fe6cef27444b6575c5e7ab5de13274610488b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Oct 2017 11:41:19 +0200 Subject: i2c: Allow overriding dev_name through board_info For devices not instantiated through ACPI the i2c-client's device-name gets set to - by default, e.g. "0-0022" this means that the device-name is dependent on the order in which the i2c-busses are enumerated. In some cases having a predictable constant device-name is desirable, for example on non device-tree platforms the link between a regulator and its consumers is specified by the platform code by setting regulator_init_data.consumers. This array identifies the regulator's consumers by dev_name and supply(-name). Which requires a constant dev_name. This commit adds a dev_name field to i2c_board_info allowing platform code to set a contstant dev_name so that the device can be identified by its dev_name in other platform code. Signed-off-by: Hans de Goede Acked-by: Mark Brown (live at ELCE17) Acked-by: Andy Shevchenko (live at ELCE17) Signed-off-by: Wolfram Sang --- drivers/i2c/i2c-core-base.c | 10 ++++++++-- include/linux/i2c.h | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index 56e46581b84b..875d6cacaa17 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -666,10 +666,16 @@ static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter, } static void i2c_dev_set_name(struct i2c_adapter *adap, - struct i2c_client *client) + struct i2c_client *client, + struct i2c_board_info const *info) { struct acpi_device *adev = ACPI_COMPANION(&client->dev); + if (info && info->dev_name) { + dev_set_name(&client->dev, "i2c-%s", info->dev_name); + return; + } + if (adev) { dev_set_name(&client->dev, "i2c-%s", acpi_dev_name(adev)); return; @@ -766,7 +772,7 @@ i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) client->dev.of_node = info->of_node; client->dev.fwnode = info->fwnode; - i2c_dev_set_name(adap, client); + i2c_dev_set_name(adap, client, info); if (info->properties) { status = device_add_properties(&client->dev, info->properties); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index d501d3956f13..0f774406fad0 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -304,6 +304,7 @@ static inline bool i2c_detect_slave_mode(struct device *dev) { return false; } * @type: chip type, to initialize i2c_client.name * @flags: to initialize i2c_client.flags * @addr: stored in i2c_client.addr + * @dev_name: Overrides the default - dev_name if set * @platform_data: stored in i2c_client.dev.platform_data * @archdata: copied into i2c_client.dev.archdata * @of_node: pointer to OpenFirmware device node @@ -328,6 +329,7 @@ struct i2c_board_info { char type[I2C_NAME_SIZE]; unsigned short flags; unsigned short addr; + const char *dev_name; void *platform_data; struct dev_archdata *archdata; struct device_node *of_node; -- cgit v1.2.3-59-g8ed1b From 356c3e9afac0cc19c3d3b0cbc67106ce8efa0743 Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Thu, 26 Oct 2017 11:00:48 +0200 Subject: net: dsa: lan9303: Move struct lan9303 to include/linux/dsa/lan9303.h The next patch require net/dsa/tag_lan9303.c to access struct lan9303. Therefore move struct lan9303 definitions from drivers/net/dsa/lan9303.h to new file include/linux/dsa/lan9303.h. Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- MAINTAINERS | 1 + drivers/net/dsa/lan9303.h | 34 +--------------------------------- include/linux/dsa/lan9303.h | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 33 deletions(-) create mode 100644 include/linux/dsa/lan9303.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index e3a7ca9d2783..c9ee7abf4627 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9415,6 +9415,7 @@ M: Florian Fainelli S: Maintained F: net/dsa/ F: include/net/dsa.h +F: include/linux/dsa/ F: drivers/net/dsa/ NETWORKING [GENERAL] diff --git a/drivers/net/dsa/lan9303.h b/drivers/net/dsa/lan9303.h index d807b1be35f2..b868e5040830 100644 --- a/drivers/net/dsa/lan9303.h +++ b/drivers/net/dsa/lan9303.h @@ -2,39 +2,7 @@ #include #include -struct lan9303; - -struct lan9303_phy_ops { - /* PHY 1 and 2 access*/ - int (*phy_read)(struct lan9303 *chip, int port, int regnum); - int (*phy_write)(struct lan9303 *chip, int port, - int regnum, u16 val); -}; - -#define LAN9303_NUM_ALR_RECORDS 512 -struct lan9303_alr_cache_entry { - u8 mac_addr[ETH_ALEN]; - u8 port_map; /* Bitmap of ports. Zero if unused entry */ - u8 stp_override; /* non zero if set ALR_DAT1_AGE_OVERRID */ -}; - -struct lan9303 { - struct device *dev; - struct regmap *regmap; - struct regmap_irq_chip_data *irq_data; - struct gpio_desc *reset_gpio; - u32 reset_duration; /* in [ms] */ - bool phy_addr_sel_strap; - struct dsa_switch *ds; - struct mutex indirect_mutex; /* protect indexed register access */ - const struct lan9303_phy_ops *ops; - bool is_bridged; /* true if port 1 and 2 are bridged */ - u32 swe_port_state; /* remember SWE_PORT_STATE while not bridged */ - /* LAN9303 do not offer reading specific ALR entry. Cache all - * static entries in a flat table - **/ - struct lan9303_alr_cache_entry alr_cache[LAN9303_NUM_ALR_RECORDS]; -}; +#include extern const struct regmap_access_table lan9303_register_set; extern const struct lan9303_phy_ops lan9303_indirect_phy_ops; diff --git a/include/linux/dsa/lan9303.h b/include/linux/dsa/lan9303.h new file mode 100644 index 000000000000..05d8d136baab --- /dev/null +++ b/include/linux/dsa/lan9303.h @@ -0,0 +1,36 @@ +/* Included by drivers/net/dsa/lan9303.h and net/dsa/tag_lan9303.c */ +#include + +struct lan9303; + +struct lan9303_phy_ops { + /* PHY 1 and 2 access*/ + int (*phy_read)(struct lan9303 *chip, int port, int regnum); + int (*phy_write)(struct lan9303 *chip, int port, + int regnum, u16 val); +}; + +#define LAN9303_NUM_ALR_RECORDS 512 +struct lan9303_alr_cache_entry { + u8 mac_addr[ETH_ALEN]; + u8 port_map; /* Bitmap of ports. Zero if unused entry */ + u8 stp_override; /* non zero if set ALR_DAT1_AGE_OVERRID */ +}; + +struct lan9303 { + struct device *dev; + struct regmap *regmap; + struct regmap_irq_chip_data *irq_data; + struct gpio_desc *reset_gpio; + u32 reset_duration; /* in [ms] */ + bool phy_addr_sel_strap; + struct dsa_switch *ds; + struct mutex indirect_mutex; /* protect indexed register access */ + const struct lan9303_phy_ops *ops; + bool is_bridged; /* true if port 1 and 2 are bridged */ + u32 swe_port_state; /* remember SWE_PORT_STATE while not bridged */ + /* LAN9303 do not offer reading specific ALR entry. Cache all + * static entries in a flat table + **/ + struct lan9303_alr_cache_entry alr_cache[LAN9303_NUM_ALR_RECORDS]; +}; -- cgit v1.2.3-59-g8ed1b From bff7b688d5b10a8cb8cecefdea5e255408b78f2f Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 11:22:51 -0400 Subject: net: dsa: add dsa_is_unused_port helper As the comment above the chunk states, the b53 driver attempts to disable the unused ports. But using ds->enabled_port_mask is misleading, because this mask reports in fact the user ports. To avoid confusion and fix this, this patch introduces an explicit dsa_is_unused_port helper which ensures the corresponding bit is not masked in any of the switch port masks. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 2 +- include/net/dsa.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index b48cf0487b43..c74a50112551 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -873,7 +873,7 @@ static int b53_setup(struct dsa_switch *ds) for (port = 0; port < dev->num_ports; port++) { if (dsa_is_cpu_port(ds, port)) b53_enable_cpu_port(dev, port); - else if (!(BIT(port) & ds->enabled_port_mask)) + else if (dsa_is_unused_port(ds, port)) b53_disable_port(ds, port, NULL); } diff --git a/include/net/dsa.h b/include/net/dsa.h index 38961ef91d3d..6b1bc1c8f7e2 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -254,6 +254,13 @@ struct dsa_switch { struct dsa_port ports[]; }; +static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p) +{ + u32 m = ds->enabled_port_mask | ds->dsa_port_mask | ds->cpu_port_mask; + + return !(m & BIT(p)); +} + static inline bool dsa_is_cpu_port(struct dsa_switch *ds, int p) { return !!(ds->cpu_port_mask & (1 << p)); -- cgit v1.2.3-59-g8ed1b From deb8ee0b51204273c120a3b3848efbb5695ad658 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 11:22:53 -0400 Subject: net: dsa: fix dsa_is_normal_port helper In order to know if a port is of type user, dsa_is_normal_port checks that the given port is not of type DSA nor CPU. This is not enough because a port can be unused. Without the previous fix, this caused the unused mv88e6xxx ports to be configured in normal mode. The ds->enabled_port_mask reports the user ports, so check this instead. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 6b1bc1c8f7e2..4ad432ad2d40 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -273,7 +273,7 @@ static inline bool dsa_is_dsa_port(struct dsa_switch *ds, int p) static inline bool dsa_is_normal_port(struct dsa_switch *ds, int p) { - return !dsa_is_cpu_port(ds, p) && !dsa_is_dsa_port(ds, p); + return !!(ds->enabled_port_mask & BIT(p)); } static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) -- cgit v1.2.3-59-g8ed1b From 2b3e9891cb607f7c7d5a4b11fb5a6e775e7f3ef4 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 11:22:54 -0400 Subject: net: dsa: rename dsa_is_normal_port helper This patch renames dsa_is_normal_port to dsa_is_user_port because "user" is the correct term in the DSA terminology, not "normal". Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/mv88e6xxx/chip.c | 2 +- include/net/dsa.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 2d8cf66e8f74..09a66d4d9492 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1676,7 +1676,7 @@ static int mv88e6xxx_setup_port_mode(struct mv88e6xxx_chip *chip, int port) if (dsa_is_dsa_port(chip->ds, port)) return mv88e6xxx_set_port_mode_dsa(chip, port); - if (dsa_is_normal_port(chip->ds, port)) + if (dsa_is_user_port(chip->ds, port)) return mv88e6xxx_set_port_mode_normal(chip, port); /* Setup CPU port mode depending on its supported tag format */ diff --git a/include/net/dsa.h b/include/net/dsa.h index 4ad432ad2d40..49701d958663 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -271,7 +271,7 @@ static inline bool dsa_is_dsa_port(struct dsa_switch *ds, int p) return !!((ds->dsa_port_mask) & (1 << p)); } -static inline bool dsa_is_normal_port(struct dsa_switch *ds, int p) +static inline bool dsa_is_user_port(struct dsa_switch *ds, int p) { return !!(ds->enabled_port_mask & BIT(p)); } -- cgit v1.2.3-59-g8ed1b From 02bc6e546e858b209c3ebe380a13a73b333b1b3f Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 11:22:56 -0400 Subject: net: dsa: introduce dsa_user_ports helper Introduce a dsa_user_ports() helper to return the ds->enabled_port_mask mask which is more explicit. This will also minimize diffs when touching this internal mask. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/mt7530.c | 2 +- drivers/net/dsa/mv88e6060.c | 5 ++--- drivers/net/dsa/qca8k.c | 3 +-- include/net/dsa.h | 5 +++++ net/dsa/dsa2.c | 2 +- net/dsa/legacy.c | 2 +- 6 files changed, 11 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index d1d4eea6a875..627c039f12ca 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -688,7 +688,7 @@ mt7530_cpu_port_enable(struct mt7530_priv *priv, * the switch */ mt7530_write(priv, MT7530_PCR_P(port), - PCR_MATRIX(priv->ds->enabled_port_mask)); + PCR_MATRIX(dsa_user_ports(priv->ds))); return 0; } diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index f78b9e13be1c..45768e3c5bc5 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -175,9 +175,8 @@ static int mv88e6060_setup_port(struct dsa_switch *ds, int p) */ REG_WRITE(addr, PORT_VLAN_MAP, ((p & 0xf) << PORT_VLAN_MAP_DBNUM_SHIFT) | - (dsa_is_cpu_port(ds, p) ? - ds->enabled_port_mask : - BIT(dsa_to_port(ds, p)->cpu_dp->index))); + (dsa_is_cpu_port(ds, p) ? dsa_user_ports(ds) : + BIT(dsa_to_port(ds, p)->cpu_dp->index))); /* Port Association Vector: when learning source addresses * of packets, add the address to the address database using diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 37125858fe80..cf72e274275f 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -551,8 +551,7 @@ qca8k_setup(struct dsa_switch *ds) /* CPU port gets connected to all user ports of the switch */ if (dsa_is_cpu_port(ds, i)) { qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(QCA8K_CPU_PORT), - QCA8K_PORT_LOOKUP_MEMBER, - ds->enabled_port_mask); + QCA8K_PORT_LOOKUP_MEMBER, dsa_user_ports(ds)); } /* Invividual user ports get connected to CPU port only */ diff --git a/include/net/dsa.h b/include/net/dsa.h index 49701d958663..dc7728062396 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -276,6 +276,11 @@ static inline bool dsa_is_user_port(struct dsa_switch *ds, int p) return !!(ds->enabled_port_mask & BIT(p)); } +static inline u32 dsa_user_ports(struct dsa_switch *ds) +{ + return ds->enabled_port_mask; +} + static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) { return &ds->ports[p]; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 62485a57dbfc..d43c59c91058 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -312,7 +312,7 @@ static int dsa_ds_apply(struct dsa_switch_tree *dst, struct dsa_switch *ds) * the slave MDIO bus driver rely on these values for probing PHY * devices or not */ - ds->phys_mii_mask = ds->enabled_port_mask; + ds->phys_mii_mask |= dsa_user_ports(ds); /* Add the switch to devlink before calling setup, so that setup can * add dpipe tables diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 0b79c6171d0d..fa543c4a6061 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -136,7 +136,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, /* Make the built-in MII bus mask match the number of ports, * switch drivers can override this later */ - ds->phys_mii_mask = ds->enabled_port_mask; + ds->phys_mii_mask |= dsa_user_ports(ds); /* * If the CPU connects to this switch, set the switch tree -- cgit v1.2.3-59-g8ed1b From 057cad2c59d73b0c4a6638546f3099d6fb444094 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 11:22:57 -0400 Subject: net: dsa: define port types Introduce an enumerated type for ports, which will be way more explicit to identify a port type instead of digging into switch port masks. A port can be of type CPU, DSA, user, or unused by default. This is a static parsed information that cannot be changed at runtime. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 7 +++++++ net/dsa/dsa2.c | 3 +++ net/dsa/legacy.c | 6 ++++++ 3 files changed, 16 insertions(+) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index dc7728062396..8da20c4a6552 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -180,6 +180,13 @@ struct dsa_port { struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt); + enum { + DSA_PORT_TYPE_UNUSED = 0, + DSA_PORT_TYPE_CPU, + DSA_PORT_TYPE_DSA, + DSA_PORT_TYPE_USER, + } type; + struct dsa_switch *ds; unsigned int index; const char *name; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index d43c59c91058..dd6f35b92937 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -185,6 +185,7 @@ static int dsa_ds_complete(struct dsa_switch_tree *dst, struct dsa_switch *ds) return err; ds->dsa_port_mask |= BIT(index); + port->type = DSA_PORT_TYPE_DSA; } return 0; @@ -504,6 +505,7 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, * net/dsa/dsa.c::dsa_switch_setup_one does. */ ds->cpu_port_mask |= BIT(index); + port->type = DSA_PORT_TYPE_CPU; tag_protocol = ds->ops->get_tag_protocol(ds); tag_ops = dsa_resolve_tag_protocol(tag_protocol); @@ -543,6 +545,7 @@ static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds) * net/dsa/dsa.c::dsa_switch_setup_one does. */ ds->enabled_port_mask |= BIT(index); + port->type = DSA_PORT_TYPE_USER; } } diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index fa543c4a6061..9fd5b3adab1e 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -101,6 +101,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct dsa_chip_data *cd = ds->cd; bool valid_name_found = false; int index = ds->index; + struct dsa_port *dp; int i, ret; /* @@ -109,6 +110,8 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, for (i = 0; i < ds->num_ports; i++) { char *name; + dp = &ds->ports[i]; + name = cd->port_names[i]; if (name == NULL) continue; @@ -122,10 +125,13 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, dst->cpu_dp = &ds->ports[i]; dst->cpu_dp->master = master; ds->cpu_port_mask |= 1 << i; + dp->type = DSA_PORT_TYPE_CPU; } else if (!strcmp(name, "dsa")) { ds->dsa_port_mask |= 1 << i; + dp->type = DSA_PORT_TYPE_DSA; } else { ds->enabled_port_mask |= 1 << i; + dp->type = DSA_PORT_TYPE_USER; } valid_name_found = true; } -- cgit v1.2.3-59-g8ed1b From c38c5a66506e4e8223fd03e950b1bde99190701e Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 11:22:58 -0400 Subject: net: dsa: use new port type in helpers Now that DSA exposes an enumerated type for the ports, we can use them directly instead of checking bitmaps, which is more consistent. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 8da20c4a6552..07dfbd7f4fd5 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -261,36 +261,41 @@ struct dsa_switch { struct dsa_port ports[]; }; -static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p) +static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) { - u32 m = ds->enabled_port_mask | ds->dsa_port_mask | ds->cpu_port_mask; + return &ds->ports[p]; +} - return !(m & BIT(p)); +static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p) +{ + return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED; } static inline bool dsa_is_cpu_port(struct dsa_switch *ds, int p) { - return !!(ds->cpu_port_mask & (1 << p)); + return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_CPU; } static inline bool dsa_is_dsa_port(struct dsa_switch *ds, int p) { - return !!((ds->dsa_port_mask) & (1 << p)); + return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_DSA; } static inline bool dsa_is_user_port(struct dsa_switch *ds, int p) { - return !!(ds->enabled_port_mask & BIT(p)); + return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_USER; } static inline u32 dsa_user_ports(struct dsa_switch *ds) { - return ds->enabled_port_mask; -} + u32 mask = 0; + int p; -static inline const struct dsa_port *dsa_to_port(struct dsa_switch *ds, int p) -{ - return &ds->ports[p]; + for (p = 0; p < ds->num_ports; p++) + if (dsa_is_user_port(ds, p)) + mask |= BIT(p); + + return mask; } static inline u8 dsa_upstream_port(struct dsa_switch *ds) -- cgit v1.2.3-59-g8ed1b From 5749f0f3772b9d98f37e3a92539f49fafaa64eca Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Thu, 26 Oct 2017 11:22:59 -0400 Subject: net: dsa: remove port masks Now that DSA core provides port types, there is no need to keep this information at the switch level. This is a static information that is part of a DSA core dsa_port structure. Remove them. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 3 --- net/dsa/dsa2.c | 11 ----------- net/dsa/legacy.c | 3 --- 3 files changed, 17 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 07dfbd7f4fd5..50e276dc4c01 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -240,9 +240,6 @@ struct dsa_switch { /* * Slave mii_bus and devices for the individual ports. */ - u32 dsa_port_mask; - u32 cpu_port_mask; - u32 enabled_port_mask; u32 phys_mii_mask; struct mii_bus *slave_mii_bus; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index dd6f35b92937..ec58654a71cd 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -184,7 +184,6 @@ static int dsa_ds_complete(struct dsa_switch_tree *dst, struct dsa_switch *ds) if (err != 0) return err; - ds->dsa_port_mask |= BIT(index); port->type = DSA_PORT_TYPE_DSA; } @@ -500,11 +499,6 @@ static int dsa_cpu_parse(struct dsa_port *port, u32 index, dst->cpu_dp->master = ethernet_dev; } - /* Initialize cpu_port_mask now for drv->setup() - * to have access to a correct value, just like what - * net/dsa/dsa.c::dsa_switch_setup_one does. - */ - ds->cpu_port_mask |= BIT(index); port->type = DSA_PORT_TYPE_CPU; tag_protocol = ds->ops->get_tag_protocol(ds); @@ -540,11 +534,6 @@ static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds) if (err) return err; } else { - /* Initialize enabled_port_mask now for drv->setup() - * to have access to a correct value, just like what - * net/dsa/dsa.c::dsa_switch_setup_one does. - */ - ds->enabled_port_mask |= BIT(index); port->type = DSA_PORT_TYPE_USER; } diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 9fd5b3adab1e..93e1b116ef83 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -124,13 +124,10 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, } dst->cpu_dp = &ds->ports[i]; dst->cpu_dp->master = master; - ds->cpu_port_mask |= 1 << i; dp->type = DSA_PORT_TYPE_CPU; } else if (!strcmp(name, "dsa")) { - ds->dsa_port_mask |= 1 << i; dp->type = DSA_PORT_TYPE_DSA; } else { - ds->enabled_port_mask |= 1 << i; dp->type = DSA_PORT_TYPE_USER; } valid_name_found = true; -- cgit v1.2.3-59-g8ed1b From ce5aa6d205576904552b98e4e6ca88d8d285849f Mon Sep 17 00:00:00 2001 From: Younian Wang Date: Thu, 19 Oct 2017 21:43:29 +0200 Subject: media: rc/keymaps: add support for RC of hisilicon TV demo boards This is a NEC protocol type remote controller distributed with hisilicon TV demo boards. Signed-off-by: Younian Wang Signed-off-by: Jiancheng Xue Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-hisi-tv-demo.c | 81 ++++++++++++++++++++++++++++++ include/media/rc-map.h | 1 + 3 files changed, 83 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-hisi-tv-demo.c (limited to 'include') diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 3c1e31321e21..ba471f09102a 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-geekbox.o \ rc-genius-tvgo-a11mce.o \ rc-gotview7135.o \ + rc-hisi-tv-demo.o \ rc-imon-mce.o \ rc-imon-pad.o \ rc-iodata-bctv7e.o \ diff --git a/drivers/media/rc/keymaps/rc-hisi-tv-demo.c b/drivers/media/rc/keymaps/rc-hisi-tv-demo.c new file mode 100644 index 000000000000..4816e3a4a18d --- /dev/null +++ b/drivers/media/rc/keymaps/rc-hisi-tv-demo.c @@ -0,0 +1,81 @@ +/* + * Keytable for remote controller of HiSilicon tv demo board. + * + * Copyright (c) 2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table hisi_tv_demo_keymap[] = { + { 0x00000092, KEY_1}, + { 0x00000093, KEY_2}, + { 0x000000cc, KEY_3}, + { 0x0000009f, KEY_4}, + { 0x0000008e, KEY_5}, + { 0x0000008f, KEY_6}, + { 0x000000c8, KEY_7}, + { 0x00000094, KEY_8}, + { 0x0000008a, KEY_9}, + { 0x0000008b, KEY_0}, + { 0x000000ce, KEY_ENTER}, + { 0x000000ca, KEY_UP}, + { 0x00000099, KEY_LEFT}, + { 0x00000084, KEY_PAGEUP}, + { 0x000000c1, KEY_RIGHT}, + { 0x000000d2, KEY_DOWN}, + { 0x00000089, KEY_PAGEDOWN}, + { 0x000000d1, KEY_MUTE}, + { 0x00000098, KEY_VOLUMEDOWN}, + { 0x00000090, KEY_VOLUMEUP}, + { 0x0000009c, KEY_POWER}, + { 0x000000d6, KEY_STOP}, + { 0x00000097, KEY_MENU}, + { 0x000000cb, KEY_BACK}, + { 0x000000da, KEY_PLAYPAUSE}, + { 0x00000080, KEY_INFO}, + { 0x000000c3, KEY_REWIND}, + { 0x00000087, KEY_HOMEPAGE}, + { 0x000000d0, KEY_FASTFORWARD}, + { 0x000000c4, KEY_SOUND}, + { 0x00000082, BTN_1}, + { 0x000000c7, BTN_2}, + { 0x00000086, KEY_PROGRAM}, + { 0x000000d9, KEY_SUBTITLE}, + { 0x00000085, KEY_ZOOM}, + { 0x0000009b, KEY_RED}, + { 0x0000009a, KEY_GREEN}, + { 0x000000c0, KEY_YELLOW}, + { 0x000000c2, KEY_BLUE}, + { 0x0000009d, KEY_CHANNELDOWN}, + { 0x000000cf, KEY_CHANNELUP}, +}; + +static struct rc_map_list hisi_tv_demo_map = { + .map = { + .scan = hisi_tv_demo_keymap, + .size = ARRAY_SIZE(hisi_tv_demo_keymap), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_HISI_TV_DEMO, + } +}; + +static int __init init_rc_map_hisi_tv_demo(void) +{ + return rc_map_register(&hisi_tv_demo_map); +} + +static void __exit exit_rc_map_hisi_tv_demo(void) +{ + rc_map_unregister(&hisi_tv_demo_map); +} + +module_init(init_rc_map_hisi_tv_demo) +module_exit(exit_rc_map_hisi_tv_demo) + +MODULE_LICENSE("GPL v2"); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index b4ddcb62c993..6d2172b6a0ed 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -258,6 +258,7 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_GENIUS_TVGO_A11MCE "rc-genius-tvgo-a11mce" #define RC_MAP_GOTVIEW7135 "rc-gotview7135" #define RC_MAP_HAUPPAUGE_NEW "rc-hauppauge" +#define RC_MAP_HISI_TV_DEMO "rc-hisi-tv-demo" #define RC_MAP_IMON_MCE "rc-imon-mce" #define RC_MAP_IMON_PAD "rc-imon-pad" #define RC_MAP_IODATA_BCTV7E "rc-iodata-bctv7e" -- cgit v1.2.3-59-g8ed1b From c62cf662a2cbaa1f5ad2a5e1998b4662ed15a316 Mon Sep 17 00:00:00 2001 From: Younian Wang Date: Thu, 19 Oct 2017 21:43:30 +0200 Subject: media: rc/keymaps: add support for RC of hisilicon poplar board This is a NEC protocol type remote controller distributed with 96boards poplar@tocoding board. Signed-off-by: Younian Wang Signed-off-by: Jiancheng Xue Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-hisi-poplar.c | 69 +++++++++++++++++++++++++++++++ include/media/rc-map.h | 1 + 3 files changed, 71 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-hisi-poplar.c (limited to 'include') diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index ba471f09102a..3f196232904f 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-geekbox.o \ rc-genius-tvgo-a11mce.o \ rc-gotview7135.o \ + rc-hisi-poplar.o \ rc-hisi-tv-demo.o \ rc-imon-mce.o \ rc-imon-pad.o \ diff --git a/drivers/media/rc/keymaps/rc-hisi-poplar.c b/drivers/media/rc/keymaps/rc-hisi-poplar.c new file mode 100644 index 000000000000..78728bc7f63a --- /dev/null +++ b/drivers/media/rc/keymaps/rc-hisi-poplar.c @@ -0,0 +1,69 @@ +/* + * Keytable for remote controller of HiSilicon poplar board. + * + * Copyright (c) 2017 HiSilicon Technologies Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +static struct rc_map_table hisi_poplar_keymap[] = { + { 0x0000b292, KEY_1}, + { 0x0000b293, KEY_2}, + { 0x0000b2cc, KEY_3}, + { 0x0000b28e, KEY_4}, + { 0x0000b28f, KEY_5}, + { 0x0000b2c8, KEY_6}, + { 0x0000b28a, KEY_7}, + { 0x0000b28b, KEY_8}, + { 0x0000b2c4, KEY_9}, + { 0x0000b287, KEY_0}, + { 0x0000b282, KEY_HOMEPAGE}, + { 0x0000b2ca, KEY_UP}, + { 0x0000b299, KEY_LEFT}, + { 0x0000b2c1, KEY_RIGHT}, + { 0x0000b2d2, KEY_DOWN}, + { 0x0000b2c5, KEY_DELETE}, + { 0x0000b29c, KEY_MUTE}, + { 0x0000b281, KEY_VOLUMEDOWN}, + { 0x0000b280, KEY_VOLUMEUP}, + { 0x0000b2dc, KEY_POWER}, + { 0x0000b29a, KEY_MENU}, + { 0x0000b28d, KEY_SETUP}, + { 0x0000b2c5, KEY_BACK}, + { 0x0000b295, KEY_PLAYPAUSE}, + { 0x0000b2ce, KEY_ENTER}, + { 0x0000b285, KEY_CHANNELUP}, + { 0x0000b286, KEY_CHANNELDOWN}, + { 0x0000b2da, KEY_NUMERIC_STAR}, + { 0x0000b2d0, KEY_NUMERIC_POUND}, +}; + +static struct rc_map_list hisi_poplar_map = { + .map = { + .scan = hisi_poplar_keymap, + .size = ARRAY_SIZE(hisi_poplar_keymap), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_HISI_POPLAR, + } +}; + +static int __init init_rc_map_hisi_poplar(void) +{ + return rc_map_register(&hisi_poplar_map); +} + +static void __exit exit_rc_map_hisi_poplar(void) +{ + rc_map_unregister(&hisi_poplar_map); +} + +module_init(init_rc_map_hisi_poplar) +module_exit(exit_rc_map_hisi_poplar) + +MODULE_LICENSE("GPL v2"); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 6d2172b6a0ed..cc59c72ac282 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -258,6 +258,7 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_GENIUS_TVGO_A11MCE "rc-genius-tvgo-a11mce" #define RC_MAP_GOTVIEW7135 "rc-gotview7135" #define RC_MAP_HAUPPAUGE_NEW "rc-hauppauge" +#define RC_MAP_HISI_POPLAR "rc-hisi-poplar" #define RC_MAP_HISI_TV_DEMO "rc-hisi-tv-demo" #define RC_MAP_IMON_MCE "rc-imon-mce" #define RC_MAP_IMON_PAD "rc-imon-pad" -- cgit v1.2.3-59-g8ed1b From 585d763af09cc21daf48ecc873604ccdb70f6014 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Mon, 16 Oct 2017 18:01:26 -0700 Subject: net/sched: Introduce Credit Based Shaper (CBS) qdisc This queueing discipline implements the shaper algorithm defined by the 802.1Q-2014 Section 8.6.8.2 and detailed in Annex L. It's primary usage is to apply some bandwidth reservation to user defined traffic classes, which are mapped to different queues via the mqprio qdisc. Only a simple software implementation is added for now. Signed-off-by: Vinicius Costa Gomes Signed-off-by: Jesus Sanchez-Palencia Tested-by: Henrik Austad Signed-off-by: Jeff Kirsher --- include/uapi/linux/pkt_sched.h | 19 +++ net/sched/Kconfig | 11 ++ net/sched/Makefile | 1 + net/sched/sch_cbs.c | 293 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 324 insertions(+) create mode 100644 net/sched/sch_cbs.c (limited to 'include') diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index e7cc3d3c7421..0e88cc262ca0 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -904,4 +904,23 @@ struct tc_pie_xstats { __u32 maxq; /* maximum queue size */ __u32 ecn_mark; /* packets marked with ecn*/ }; + +/* CBS */ +struct tc_cbs_qopt { + __u8 offload; + __u8 _pad[3]; + __s32 hicredit; + __s32 locredit; + __s32 idleslope; + __s32 sendslope; +}; + +enum { + TCA_CBS_UNSPEC, + TCA_CBS_PARMS, + __TCA_CBS_MAX, +}; + +#define TCA_CBS_MAX (__TCA_CBS_MAX - 1) + #endif diff --git a/net/sched/Kconfig b/net/sched/Kconfig index e70ed26485a2..c03d86a7775e 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -172,6 +172,17 @@ config NET_SCH_TBF To compile this code as a module, choose M here: the module will be called sch_tbf. +config NET_SCH_CBS + tristate "Credit Based Shaper (CBS)" + ---help--- + Say Y here if you want to use the Credit Based Shaper (CBS) packet + scheduling algorithm. + + See the top of for more details. + + To compile this code as a module, choose M here: the + module will be called sch_cbs. + config NET_SCH_GRED tristate "Generic Random Early Detection (GRED)" ---help--- diff --git a/net/sched/Makefile b/net/sched/Makefile index 7b915d226de7..80c8f92d162d 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_NET_SCH_FQ_CODEL) += sch_fq_codel.o obj-$(CONFIG_NET_SCH_FQ) += sch_fq.o obj-$(CONFIG_NET_SCH_HHF) += sch_hhf.o obj-$(CONFIG_NET_SCH_PIE) += sch_pie.o +obj-$(CONFIG_NET_SCH_CBS) += sch_cbs.o obj-$(CONFIG_NET_CLS_U32) += cls_u32.o obj-$(CONFIG_NET_CLS_ROUTE4) += cls_route.o diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c new file mode 100644 index 000000000000..0e85133c5653 --- /dev/null +++ b/net/sched/sch_cbs.c @@ -0,0 +1,293 @@ +/* + * net/sched/sch_cbs.c Credit Based Shaper + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Authors: Vinicius Costa Gomes + * + */ + +/* Credit Based Shaper (CBS) + * ========================= + * + * This is a simple rate-limiting shaper aimed at TSN applications on + * systems with known traffic workloads. + * + * Its algorithm is defined by the IEEE 802.1Q-2014 Specification, + * Section 8.6.8.2, and explained in more detail in the Annex L of the + * same specification. + * + * There are four tunables to be considered: + * + * 'idleslope': Idleslope is the rate of credits that is + * accumulated (in kilobits per second) when there is at least + * one packet waiting for transmission. Packets are transmitted + * when the current value of credits is equal or greater than + * zero. When there is no packet to be transmitted the amount of + * credits is set to zero. This is the main tunable of the CBS + * algorithm. + * + * 'sendslope': + * Sendslope is the rate of credits that is depleted (it should be a + * negative number of kilobits per second) when a transmission is + * ocurring. It can be calculated as follows, (IEEE 802.1Q-2014 Section + * 8.6.8.2 item g): + * + * sendslope = idleslope - port_transmit_rate + * + * 'hicredit': Hicredit defines the maximum amount of credits (in + * bytes) that can be accumulated. Hicredit depends on the + * characteristics of interfering traffic, + * 'max_interference_size' is the maximum size of any burst of + * traffic that can delay the transmission of a frame that is + * available for transmission for this traffic class, (IEEE + * 802.1Q-2014 Annex L, Equation L-3): + * + * hicredit = max_interference_size * (idleslope / port_transmit_rate) + * + * 'locredit': Locredit is the minimum amount of credits that can + * be reached. It is a function of the traffic flowing through + * this qdisc (IEEE 802.1Q-2014 Annex L, Equation L-2): + * + * locredit = max_frame_size * (sendslope / port_transmit_rate) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BYTES_PER_KBIT (1000LL / 8) + +struct cbs_sched_data { + s64 port_rate; /* in bytes/s */ + s64 last; /* timestamp in ns */ + s64 credits; /* in bytes */ + s32 locredit; /* in bytes */ + s32 hicredit; /* in bytes */ + s64 sendslope; /* in bytes/s */ + s64 idleslope; /* in bytes/s */ + struct qdisc_watchdog watchdog; + int (*enqueue)(struct sk_buff *skb, struct Qdisc *sch); + struct sk_buff *(*dequeue)(struct Qdisc *sch); +}; + +static int cbs_enqueue_soft(struct sk_buff *skb, struct Qdisc *sch) +{ + struct cbs_sched_data *q = qdisc_priv(sch); + + if (sch->q.qlen == 0 && q->credits > 0) { + /* We need to stop accumulating credits when there's + * no enqueued packets and q->credits is positive. + */ + q->credits = 0; + q->last = ktime_get_ns(); + } + + return qdisc_enqueue_tail(skb, sch); +} + +static int cbs_enqueue(struct sk_buff *skb, struct Qdisc *sch, + struct sk_buff **to_free) +{ + struct cbs_sched_data *q = qdisc_priv(sch); + + return q->enqueue(skb, sch); +} + +/* timediff is in ns, slope is in bytes/s */ +static s64 timediff_to_credits(s64 timediff, s64 slope) +{ + return div64_s64(timediff * slope, NSEC_PER_SEC); +} + +static s64 delay_from_credits(s64 credits, s64 slope) +{ + if (unlikely(slope == 0)) + return S64_MAX; + + return div64_s64(-credits * NSEC_PER_SEC, slope); +} + +static s64 credits_from_len(unsigned int len, s64 slope, s64 port_rate) +{ + if (unlikely(port_rate == 0)) + return S64_MAX; + + return div64_s64(len * slope, port_rate); +} + +static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch) +{ + struct cbs_sched_data *q = qdisc_priv(sch); + s64 now = ktime_get_ns(); + struct sk_buff *skb; + s64 credits; + int len; + + if (q->credits < 0) { + credits = timediff_to_credits(now - q->last, q->idleslope); + + credits = q->credits + credits; + q->credits = min_t(s64, credits, q->hicredit); + + if (q->credits < 0) { + s64 delay; + + delay = delay_from_credits(q->credits, q->idleslope); + qdisc_watchdog_schedule_ns(&q->watchdog, now + delay); + + q->last = now; + + return NULL; + } + } + + skb = qdisc_dequeue_head(sch); + if (!skb) + return NULL; + + len = qdisc_pkt_len(skb); + + /* As sendslope is a negative number, this will decrease the + * amount of q->credits. + */ + credits = credits_from_len(len, q->sendslope, q->port_rate); + credits += q->credits; + + q->credits = max_t(s64, credits, q->locredit); + q->last = now; + + return skb; +} + +static struct sk_buff *cbs_dequeue(struct Qdisc *sch) +{ + struct cbs_sched_data *q = qdisc_priv(sch); + + return q->dequeue(sch); +} + +static const struct nla_policy cbs_policy[TCA_CBS_MAX + 1] = { + [TCA_CBS_PARMS] = { .len = sizeof(struct tc_cbs_qopt) }, +}; + +static int cbs_change(struct Qdisc *sch, struct nlattr *opt) +{ + struct cbs_sched_data *q = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); + struct nlattr *tb[TCA_CBS_MAX + 1]; + struct ethtool_link_ksettings ecmd; + struct tc_cbs_qopt *qopt; + s64 link_speed; + int err; + + err = nla_parse_nested(tb, TCA_CBS_MAX, opt, cbs_policy, NULL); + if (err < 0) + return err; + + if (!tb[TCA_CBS_PARMS]) + return -EINVAL; + + qopt = nla_data(tb[TCA_CBS_PARMS]); + + if (qopt->offload) + return -EOPNOTSUPP; + + if (!__ethtool_get_link_ksettings(dev, &ecmd)) + link_speed = ecmd.base.speed; + else + link_speed = SPEED_1000; + + q->port_rate = link_speed * 1000 * BYTES_PER_KBIT; + + q->enqueue = cbs_enqueue_soft; + q->dequeue = cbs_dequeue_soft; + + q->hicredit = qopt->hicredit; + q->locredit = qopt->locredit; + q->idleslope = qopt->idleslope * BYTES_PER_KBIT; + q->sendslope = qopt->sendslope * BYTES_PER_KBIT; + + return 0; +} + +static int cbs_init(struct Qdisc *sch, struct nlattr *opt) +{ + struct cbs_sched_data *q = qdisc_priv(sch); + + if (!opt) + return -EINVAL; + + qdisc_watchdog_init(&q->watchdog, sch); + + return cbs_change(sch, opt); +} + +static void cbs_destroy(struct Qdisc *sch) +{ + struct cbs_sched_data *q = qdisc_priv(sch); + + qdisc_watchdog_cancel(&q->watchdog); +} + +static int cbs_dump(struct Qdisc *sch, struct sk_buff *skb) +{ + struct cbs_sched_data *q = qdisc_priv(sch); + struct tc_cbs_qopt opt = { }; + struct nlattr *nest; + + nest = nla_nest_start(skb, TCA_OPTIONS); + if (!nest) + goto nla_put_failure; + + opt.hicredit = q->hicredit; + opt.locredit = q->locredit; + opt.sendslope = div64_s64(q->sendslope, BYTES_PER_KBIT); + opt.idleslope = div64_s64(q->idleslope, BYTES_PER_KBIT); + opt.offload = 0; + + if (nla_put(skb, TCA_CBS_PARMS, sizeof(opt), &opt)) + goto nla_put_failure; + + return nla_nest_end(skb, nest); + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -1; +} + +static struct Qdisc_ops cbs_qdisc_ops __read_mostly = { + .id = "cbs", + .priv_size = sizeof(struct cbs_sched_data), + .enqueue = cbs_enqueue, + .dequeue = cbs_dequeue, + .peek = qdisc_peek_dequeued, + .init = cbs_init, + .reset = qdisc_reset_queue, + .destroy = cbs_destroy, + .change = cbs_change, + .dump = cbs_dump, + .owner = THIS_MODULE, +}; + +static int __init cbs_module_init(void) +{ + return register_qdisc(&cbs_qdisc_ops); +} + +static void __exit cbs_module_exit(void) +{ + unregister_qdisc(&cbs_qdisc_ops); +} +module_init(cbs_module_init) +module_exit(cbs_module_exit) +MODULE_LICENSE("GPL"); -- cgit v1.2.3-59-g8ed1b From 3d0bd028ffb4a4915cb64cfa0d2cee1578cc0321 Mon Sep 17 00:00:00 2001 From: Vinicius Costa Gomes Date: Mon, 16 Oct 2017 18:01:27 -0700 Subject: net/sched: Add support for HW offloading for CBS This adds support for offloading the CBS algorithm to the controller, if supported. Drivers wanting to support CBS offload must implement the .ndo_setup_tc callback and handle the TC_SETUP_CBS (introduced here) type. Signed-off-by: Vinicius Costa Gomes Tested-by: Henrik Austad Signed-off-by: Jeff Kirsher --- include/linux/netdevice.h | 1 + include/net/pkt_sched.h | 9 ++++ net/sched/sch_cbs.c | 104 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 102 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6c7960c8338a..5e02f79b2110 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -776,6 +776,7 @@ enum tc_setup_type { TC_SETUP_CLSMATCHALL, TC_SETUP_CLSBPF, TC_SETUP_BLOCK, + TC_SETUP_CBS, }; /* These structures hold the attributes of xdp state that are being passed diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index b8ecafce4ba1..02f2db26e30c 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -140,4 +140,13 @@ static inline struct net *qdisc_net(struct Qdisc *q) return dev_net(q->dev_queue->dev); } +struct tc_cbs_qopt_offload { + u8 enable; + s32 queue; + s32 hicredit; + s32 locredit; + s32 idleslope; + s32 sendslope; +}; + #endif diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c index 0e85133c5653..bdb533b7fb8c 100644 --- a/net/sched/sch_cbs.c +++ b/net/sched/sch_cbs.c @@ -68,6 +68,8 @@ #define BYTES_PER_KBIT (1000LL / 8) struct cbs_sched_data { + bool offload; + int queue; s64 port_rate; /* in bytes/s */ s64 last; /* timestamp in ns */ s64 credits; /* in bytes */ @@ -80,6 +82,11 @@ struct cbs_sched_data { struct sk_buff *(*dequeue)(struct Qdisc *sch); }; +static int cbs_enqueue_offload(struct sk_buff *skb, struct Qdisc *sch) +{ + return qdisc_enqueue_tail(skb, sch); +} + static int cbs_enqueue_soft(struct sk_buff *skb, struct Qdisc *sch) { struct cbs_sched_data *q = qdisc_priv(sch); @@ -169,6 +176,11 @@ static struct sk_buff *cbs_dequeue_soft(struct Qdisc *sch) return skb; } +static struct sk_buff *cbs_dequeue_offload(struct Qdisc *sch) +{ + return qdisc_dequeue_head(sch); +} + static struct sk_buff *cbs_dequeue(struct Qdisc *sch) { struct cbs_sched_data *q = qdisc_priv(sch); @@ -180,14 +192,66 @@ static const struct nla_policy cbs_policy[TCA_CBS_MAX + 1] = { [TCA_CBS_PARMS] = { .len = sizeof(struct tc_cbs_qopt) }, }; +static void cbs_disable_offload(struct net_device *dev, + struct cbs_sched_data *q) +{ + struct tc_cbs_qopt_offload cbs = { }; + const struct net_device_ops *ops; + int err; + + if (!q->offload) + return; + + q->enqueue = cbs_enqueue_soft; + q->dequeue = cbs_dequeue_soft; + + ops = dev->netdev_ops; + if (!ops->ndo_setup_tc) + return; + + cbs.queue = q->queue; + cbs.enable = 0; + + err = ops->ndo_setup_tc(dev, TC_SETUP_CBS, &cbs); + if (err < 0) + pr_warn("Couldn't disable CBS offload for queue %d\n", + cbs.queue); +} + +static int cbs_enable_offload(struct net_device *dev, struct cbs_sched_data *q, + const struct tc_cbs_qopt *opt) +{ + const struct net_device_ops *ops = dev->netdev_ops; + struct tc_cbs_qopt_offload cbs = { }; + int err; + + if (!ops->ndo_setup_tc) + return -EOPNOTSUPP; + + cbs.queue = q->queue; + + cbs.enable = 1; + cbs.hicredit = opt->hicredit; + cbs.locredit = opt->locredit; + cbs.idleslope = opt->idleslope; + cbs.sendslope = opt->sendslope; + + err = ops->ndo_setup_tc(dev, TC_SETUP_CBS, &cbs); + if (err < 0) + return err; + + q->enqueue = cbs_enqueue_offload; + q->dequeue = cbs_dequeue_offload; + + return 0; +} + static int cbs_change(struct Qdisc *sch, struct nlattr *opt) { struct cbs_sched_data *q = qdisc_priv(sch); struct net_device *dev = qdisc_dev(sch); struct nlattr *tb[TCA_CBS_MAX + 1]; - struct ethtool_link_ksettings ecmd; struct tc_cbs_qopt *qopt; - s64 link_speed; int err; err = nla_parse_nested(tb, TCA_CBS_MAX, opt, cbs_policy, NULL); @@ -199,23 +263,30 @@ static int cbs_change(struct Qdisc *sch, struct nlattr *opt) qopt = nla_data(tb[TCA_CBS_PARMS]); - if (qopt->offload) - return -EOPNOTSUPP; + if (!qopt->offload) { + struct ethtool_link_ksettings ecmd; + s64 link_speed; - if (!__ethtool_get_link_ksettings(dev, &ecmd)) - link_speed = ecmd.base.speed; - else - link_speed = SPEED_1000; + if (!__ethtool_get_link_ksettings(dev, &ecmd)) + link_speed = ecmd.base.speed; + else + link_speed = SPEED_1000; - q->port_rate = link_speed * 1000 * BYTES_PER_KBIT; + q->port_rate = link_speed * 1000 * BYTES_PER_KBIT; - q->enqueue = cbs_enqueue_soft; - q->dequeue = cbs_dequeue_soft; + cbs_disable_offload(dev, q); + } else { + err = cbs_enable_offload(dev, q, qopt); + if (err < 0) + return err; + } + /* Everything went OK, save the parameters used. */ q->hicredit = qopt->hicredit; q->locredit = qopt->locredit; q->idleslope = qopt->idleslope * BYTES_PER_KBIT; q->sendslope = qopt->sendslope * BYTES_PER_KBIT; + q->offload = qopt->offload; return 0; } @@ -223,10 +294,16 @@ static int cbs_change(struct Qdisc *sch, struct nlattr *opt) static int cbs_init(struct Qdisc *sch, struct nlattr *opt) { struct cbs_sched_data *q = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); if (!opt) return -EINVAL; + q->queue = sch->dev_queue - netdev_get_tx_queue(dev, 0); + + q->enqueue = cbs_enqueue_soft; + q->dequeue = cbs_dequeue_soft; + qdisc_watchdog_init(&q->watchdog, sch); return cbs_change(sch, opt); @@ -235,8 +312,11 @@ static int cbs_init(struct Qdisc *sch, struct nlattr *opt) static void cbs_destroy(struct Qdisc *sch) { struct cbs_sched_data *q = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); qdisc_watchdog_cancel(&q->watchdog); + + cbs_disable_offload(dev, q); } static int cbs_dump(struct Qdisc *sch, struct sk_buff *skb) @@ -253,7 +333,7 @@ static int cbs_dump(struct Qdisc *sch, struct sk_buff *skb) opt.locredit = q->locredit; opt.sendslope = div64_s64(q->sendslope, BYTES_PER_KBIT); opt.idleslope = div64_s64(q->idleslope, BYTES_PER_KBIT); - opt.offload = 0; + opt.offload = q->offload; if (nla_put(skb, TCA_CBS_PARMS, sizeof(opt), &opt)) goto nla_put_failure; -- cgit v1.2.3-59-g8ed1b From f7de15450e906ed6586b29bde609a5686cd0d034 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Fri, 20 Oct 2017 11:06:55 -0600 Subject: drm/msm: Add per-instance submit queues Currently the behavior of a command stream is provided by the user application during submission and the application is expected to internally maintain the settings for each 'context' or 'rendering queue' and specify the correct ones. This works okay for simple cases but as applications become more complex we will want to set context specific flags and do various permission checks to allow certain contexts to enable additional privileges. Add kernel-side submit queues to be analogous to 'contexts' or 'rendering queues' on the application side. Each file descriptor instance will maintain its own list of queues. Queues cannot be shared between file descriptors. For backwards compatibility context id '0' is defined as a default context specifying no priority and no special flags. This is intended to be the usual configuration for 99% of applications so that a garden variety application can function correctly without creating a queue. Only those applications requiring the specific benefit of different queues need create one. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/Makefile | 3 +- drivers/gpu/drm/msm/msm_drv.c | 56 ++++++++++++-- drivers/gpu/drm/msm/msm_drv.h | 20 +++-- drivers/gpu/drm/msm/msm_gem.h | 1 + drivers/gpu/drm/msm/msm_gem_submit.c | 14 +++- drivers/gpu/drm/msm/msm_gpu.h | 15 ++++ drivers/gpu/drm/msm/msm_submitqueue.c | 135 ++++++++++++++++++++++++++++++++++ include/uapi/drm/msm_drm.h | 22 ++++++ 8 files changed, 250 insertions(+), 16 deletions(-) create mode 100644 drivers/gpu/drm/msm/msm_submitqueue.c (limited to 'include') diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 33008fa1be9b..3c234e7b4742 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -57,7 +57,8 @@ msm-y := \ msm_iommu.o \ msm_perf.o \ msm_rd.o \ - msm_ringbuffer.o + msm_ringbuffer.o \ + msm_submitqueue.o msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index d4d4146b3425..2585bebfcf3e 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -29,9 +29,12 @@ * - 1.0.0 - initial interface * - 1.1.0 - adds madvise, and support for submits with > 4 cmd buffers * - 1.2.0 - adds explicit fence support for submit ioctl + * - 1.3.0 - adds GMEM_BASE + NR_RINGS params, SUBMITQUEUE_NEW + + * SUBMITQUEUE_CLOSE ioctls, and MSM_INFO_IOVA flag for + * MSM_GEM_INFO ioctl. */ #define MSM_VERSION_MAJOR 1 -#define MSM_VERSION_MINOR 2 +#define MSM_VERSION_MINOR 3 #define MSM_VERSION_PATCHLEVEL 0 static void msm_fb_output_poll_changed(struct drm_device *dev) @@ -504,24 +507,37 @@ static void load_gpu(struct drm_device *dev) mutex_unlock(&init_lock); } -static int msm_open(struct drm_device *dev, struct drm_file *file) +static int context_init(struct drm_file *file) { struct msm_file_private *ctx; - /* For now, load gpu on open.. to avoid the requirement of having - * firmware in the initrd. - */ - load_gpu(dev); - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; + msm_submitqueue_init(ctx); + file->driver_priv = ctx; return 0; } +static int msm_open(struct drm_device *dev, struct drm_file *file) +{ + /* For now, load gpu on open.. to avoid the requirement of having + * firmware in the initrd. + */ + load_gpu(dev); + + return context_init(file); +} + +static void context_close(struct msm_file_private *ctx) +{ + msm_submitqueue_close(ctx); + kfree(ctx); +} + static void msm_postclose(struct drm_device *dev, struct drm_file *file) { struct msm_drm_private *priv = dev->dev_private; @@ -532,7 +548,7 @@ static void msm_postclose(struct drm_device *dev, struct drm_file *file) priv->lastctx = NULL; mutex_unlock(&dev->struct_mutex); - kfree(ctx); + context_close(ctx); } static void msm_lastclose(struct drm_device *dev) @@ -777,6 +793,28 @@ unlock: return ret; } + +static int msm_ioctl_submitqueue_new(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_msm_submitqueue *args = data; + + if (args->flags & ~MSM_SUBMITQUEUE_FLAGS) + return -EINVAL; + + return msm_submitqueue_create(file->driver_priv, args->prio, + args->flags, &args->id); +} + + +static int msm_ioctl_submitqueue_close(struct drm_device *dev, void *data, + struct drm_file *file) +{ + u32 id = *(u32 *) data; + + return msm_submitqueue_remove(file->driver_priv, id); +} + static const struct drm_ioctl_desc msm_ioctls[] = { DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_AUTH|DRM_RENDER_ALLOW), @@ -786,6 +824,8 @@ static const struct drm_ioctl_desc msm_ioctls[] = { DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_GEM_MADVISE, msm_ioctl_gem_madvise, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_NEW, msm_ioctl_submitqueue_new, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_CLOSE, msm_ioctl_submitqueue_close, DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct vm_operations_struct vm_ops = { diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 5e8109c07560..b3b8f20f58db 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -56,11 +56,9 @@ struct msm_gem_address_space; struct msm_gem_vma; struct msm_file_private { - /* currently we don't do anything useful with this.. but when - * per-context address spaces are supported we'd keep track of - * the context's page-tables here. - */ - int dummy; + rwlock_t queuelock; + struct list_head submitqueues; + int queueid; }; enum msm_mdp_plane_property { @@ -319,6 +317,18 @@ void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, void msm_writel(u32 data, void __iomem *addr); u32 msm_readl(const void __iomem *addr); +struct msm_gpu_submitqueue; +int msm_submitqueue_init(struct msm_file_private *ctx); +struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx, + u32 id); +int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio, + u32 flags, u32 *id); +int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id); +void msm_submitqueue_close(struct msm_file_private *ctx); + +void msm_submitqueue_destroy(struct kref *kref); + + #define DBG(fmt, ...) DRM_DEBUG_DRIVER(fmt"\n", ##__VA_ARGS__) #define VERB(fmt, ...) if (0) DRM_DEBUG_DRIVER(fmt"\n", ##__VA_ARGS__) diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 91c210d2359c..17f8a6c23464 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -142,6 +142,7 @@ struct msm_gem_submit { struct list_head bo_list; struct ww_acquire_ctx ticket; struct dma_fence *fence; + struct msm_gpu_submitqueue *queue; struct pid *pid; /* submitting process */ bool valid; /* true if no cmdstream patching needed */ unsigned int nr_cmds; diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 93535cac0676..c196cc615e77 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -31,7 +31,8 @@ #define BO_PINNED 0x2000 static struct msm_gem_submit *submit_create(struct drm_device *dev, - struct msm_gpu *gpu, uint32_t nr_bos, uint32_t nr_cmds) + struct msm_gpu *gpu, struct msm_gpu_submitqueue *queue, + uint32_t nr_bos, uint32_t nr_cmds) { struct msm_gem_submit *submit; uint64_t sz = sizeof(*submit) + ((u64)nr_bos * sizeof(submit->bos[0])) + @@ -49,6 +50,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, submit->fence = NULL; submit->pid = get_pid(task_pid(current)); submit->cmd = (void *)&submit->bos[nr_bos]; + submit->queue = queue; /* initially, until copy_from_user() and bo lookup succeeds: */ submit->nr_bos = 0; @@ -66,6 +68,8 @@ void msm_gem_submit_free(struct msm_gem_submit *submit) dma_fence_put(submit->fence); list_del(&submit->node); put_pid(submit->pid); + msm_submitqueue_put(submit->queue); + kfree(submit); } @@ -405,6 +409,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, struct msm_gpu *gpu = priv->gpu; struct dma_fence *in_fence = NULL; struct sync_file *sync_file = NULL; + struct msm_gpu_submitqueue *queue; int out_fence_fd = -1; unsigned i; int ret; @@ -421,6 +426,10 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, if (MSM_PIPE_FLAGS(args->flags) & ~MSM_SUBMIT_FLAGS) return -EINVAL; + queue = msm_submitqueue_get(ctx, args->queueid); + if (!queue) + return -ENOENT; + if (args->flags & MSM_SUBMIT_FENCE_FD_IN) { in_fence = sync_file_get_fence(args->fence_fd); @@ -451,7 +460,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, } priv->struct_mutex_task = current; - submit = submit_create(dev, gpu, args->nr_bos, args->nr_cmds); + submit = submit_create(dev, gpu, queue, args->nr_bos, args->nr_cmds); if (!submit) { ret = -ENOMEM; goto out_unlock; @@ -535,6 +544,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, submit->nr_cmds = i; submit->fence = msm_fence_alloc(gpu->fctx); + if (IS_ERR(submit->fence)) { ret = PTR_ERR(submit->fence); submit->fence = NULL; diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index df4e2771fb85..a890176c27ce 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -150,6 +150,15 @@ struct msm_gpu_perfcntr { const char *name; }; +struct msm_gpu_submitqueue { + int id; + u32 flags; + u32 prio; + int faults; + struct list_head node; + struct kref ref; +}; + static inline void gpu_write(struct msm_gpu *gpu, u32 reg, u32 data) { msm_writel(data, gpu->mmio + (reg << 2)); @@ -223,4 +232,10 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev); void __init adreno_register(void); void __exit adreno_unregister(void); +static inline void msm_submitqueue_put(struct msm_gpu_submitqueue *queue) +{ + if (queue) + kref_put(&queue->ref, msm_submitqueue_destroy); +} + #endif /* __MSM_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c new file mode 100644 index 000000000000..593c3b5f44cd --- /dev/null +++ b/drivers/gpu/drm/msm/msm_submitqueue.c @@ -0,0 +1,135 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + * + */ + +#include +#include "msm_gpu.h" + +void msm_submitqueue_destroy(struct kref *kref) +{ + struct msm_gpu_submitqueue *queue = container_of(kref, + struct msm_gpu_submitqueue, ref); + + kfree(queue); +} + +struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx, + u32 id) +{ + struct msm_gpu_submitqueue *entry; + + if (!ctx) + return NULL; + + read_lock(&ctx->queuelock); + + list_for_each_entry(entry, &ctx->submitqueues, node) { + if (entry->id == id) { + kref_get(&entry->ref); + read_unlock(&ctx->queuelock); + + return entry; + } + } + + read_unlock(&ctx->queuelock); + return NULL; +} + +void msm_submitqueue_close(struct msm_file_private *ctx) +{ + struct msm_gpu_submitqueue *entry, *tmp; + + if (!ctx) + return; + + /* + * No lock needed in close and there won't + * be any more user ioctls coming our way + */ + list_for_each_entry_safe(entry, tmp, &ctx->submitqueues, node) + msm_submitqueue_put(entry); +} + +int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio, u32 flags, + u32 *id) +{ + struct msm_gpu_submitqueue *queue; + + if (!ctx) + return -ENODEV; + + queue = kzalloc(sizeof(*queue), GFP_KERNEL); + + if (!queue) + return -ENOMEM; + + kref_init(&queue->ref); + queue->flags = flags; + queue->prio = prio; + + write_lock(&ctx->queuelock); + + queue->id = ctx->queueid++; + + if (id) + *id = queue->id; + + list_add_tail(&queue->node, &ctx->submitqueues); + + write_unlock(&ctx->queuelock); + + return 0; +} + +int msm_submitqueue_init(struct msm_file_private *ctx) +{ + if (!ctx) + return 0; + + INIT_LIST_HEAD(&ctx->submitqueues); + + rwlock_init(&ctx->queuelock); + + return msm_submitqueue_create(ctx, 2, 0, NULL); +} + +int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id) +{ + struct msm_gpu_submitqueue *entry; + + if (!ctx) + return 0; + + /* + * id 0 is the "default" queue and can't be destroyed + * by the user + */ + if (!id) + return -ENOENT; + + write_lock(&ctx->queuelock); + + list_for_each_entry(entry, &ctx->submitqueues, node) { + if (entry->id == id) { + list_del(&entry->node); + write_unlock(&ctx->queuelock); + + msm_submitqueue_put(entry); + return 0; + } + } + + write_unlock(&ctx->queuelock); + return -ENOENT; +} + diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index ad4eb2863e70..fee44322a69c 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -218,6 +218,7 @@ struct drm_msm_gem_submit { __u64 bos; /* in, ptr to array of submit_bo's */ __u64 cmds; /* in, ptr to array of submit_cmd's */ __s32 fence_fd; /* in/out fence fd (see MSM_SUBMIT_FENCE_FD_IN/OUT) */ + __u32 queueid; /* in, submitqueue id */ }; /* The normal way to synchronize with the GPU is just to CPU_PREP on @@ -254,6 +255,20 @@ struct drm_msm_gem_madvise { __u32 retained; /* out, whether backing store still exists */ }; +/* + * Draw queues allow the user to set specific submission parameter. Command + * submissions specify a specific submitqueue to use. ID 0 is reserved for + * backwards compatibility as a "default" submitqueue + */ + +#define MSM_SUBMITQUEUE_FLAGS (0) + +struct drm_msm_submitqueue { + __u32 flags; /* in, MSM_SUBMITQUEUE_x */ + __u32 prio; /* in, Priority level */ + __u32 id; /* out, identifier */ +}; + #define DRM_MSM_GET_PARAM 0x00 /* placeholder: #define DRM_MSM_SET_PARAM 0x01 @@ -265,6 +280,11 @@ struct drm_msm_gem_madvise { #define DRM_MSM_GEM_SUBMIT 0x06 #define DRM_MSM_WAIT_FENCE 0x07 #define DRM_MSM_GEM_MADVISE 0x08 +/* placeholder: +#define DRM_MSM_GEM_SVM_NEW 0x09 + */ +#define DRM_MSM_SUBMITQUEUE_NEW 0x0A +#define DRM_MSM_SUBMITQUEUE_CLOSE 0x0B #define DRM_IOCTL_MSM_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param) #define DRM_IOCTL_MSM_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new) @@ -274,6 +294,8 @@ struct drm_msm_gem_madvise { #define DRM_IOCTL_MSM_GEM_SUBMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_SUBMIT, struct drm_msm_gem_submit) #define DRM_IOCTL_MSM_WAIT_FENCE DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_WAIT_FENCE, struct drm_msm_wait_fence) #define DRM_IOCTL_MSM_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_MADVISE, struct drm_msm_gem_madvise) +#define DRM_IOCTL_MSM_SUBMITQUEUE_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_NEW, struct drm_msm_submitqueue) +#define DRM_IOCTL_MSM_SUBMITQUEUE_CLOSE DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_CLOSE, __u32) #if defined(__cplusplus) } -- cgit v1.2.3-59-g8ed1b From f97decac5f4c2d862e5b848694e3ffb29fc8acdd Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Fri, 20 Oct 2017 11:06:57 -0600 Subject: drm/msm: Support multiple ringbuffers Add the infrastructure to support the idea of multiple ringbuffers. Assign each ringbuffer an id and use that as an index for the various ring specific operations. The biggest delta is to support legacy fences. Each fence gets its own sequence number but the legacy functions expect to use a unique integer. To handle this we return a unique identifier for each submission but map it to a specific ring/sequence under the covers. Newer users use a dma_fence pointer anyway so they don't care about the actual sequence ID or ring. The actual mechanics for multiple ringbuffers are very target specific so this code just allows for the possibility but still only defines one ringbuffer for each target family. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/a3xx_gpu.c | 9 +- drivers/gpu/drm/msm/adreno/a4xx_gpu.c | 9 +- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 54 +- drivers/gpu/drm/msm/adreno/a5xx_gpu.h | 2 +- drivers/gpu/drm/msm/adreno/a5xx_power.c | 6 +- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 136 +++-- drivers/gpu/drm/msm/adreno/adreno_gpu.h | 20 +- drivers/gpu/drm/msm/msm_drv.c | 23 +- drivers/gpu/drm/msm/msm_drv.h | 8 +- drivers/gpu/drm/msm/msm_fence.c | 2 +- drivers/gpu/drm/msm/msm_fence.h | 2 +- drivers/gpu/drm/msm/msm_gem.h | 4 +- drivers/gpu/drm/msm/msm_gem_submit.c | 12 +- drivers/gpu/drm/msm/msm_gpu.c | 163 ++++-- drivers/gpu/drm/msm/msm_gpu.h | 42 +- drivers/gpu/drm/msm/msm_ringbuffer.c | 34 +- drivers/gpu/drm/msm/msm_ringbuffer.h | 20 +- drivers/gpu/drm/msm/msm_submitqueue.c | 27 +- include/dt-bindings/msm/msm-bus-ids.h | 887 ++++++++++++++++++++++++++++++++ include/uapi/drm/msm_drm.h | 1 + 20 files changed, 1251 insertions(+), 210 deletions(-) create mode 100644 include/dt-bindings/msm/msm-bus-ids.h (limited to 'include') diff --git a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c index 789f7fb86cba..4baef2738178 100644 --- a/drivers/gpu/drm/msm/adreno/a3xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a3xx_gpu.c @@ -44,7 +44,7 @@ static bool a3xx_idle(struct msm_gpu *gpu); static bool a3xx_me_init(struct msm_gpu *gpu) { - struct msm_ringbuffer *ring = gpu->rb; + struct msm_ringbuffer *ring = gpu->rb[0]; OUT_PKT3(ring, CP_ME_INIT, 17); OUT_RING(ring, 0x000003f7); @@ -65,7 +65,7 @@ static bool a3xx_me_init(struct msm_gpu *gpu) OUT_RING(ring, 0x00000000); OUT_RING(ring, 0x00000000); - gpu->funcs->flush(gpu); + gpu->funcs->flush(gpu, ring); return a3xx_idle(gpu); } @@ -339,7 +339,7 @@ static void a3xx_destroy(struct msm_gpu *gpu) static bool a3xx_idle(struct msm_gpu *gpu) { /* wait for ringbuffer to drain: */ - if (!adreno_idle(gpu)) + if (!adreno_idle(gpu, gpu->rb[0])) return false; /* then wait for GPU to finish: */ @@ -446,6 +446,7 @@ static const struct adreno_gpu_funcs funcs = { .recover = a3xx_recover, .submit = adreno_submit, .flush = adreno_flush, + .active_ring = adreno_active_ring, .irq = a3xx_irq, .destroy = a3xx_destroy, #ifdef CONFIG_DEBUG_FS @@ -491,7 +492,7 @@ struct msm_gpu *a3xx_gpu_init(struct drm_device *dev) adreno_gpu->registers = a3xx_registers; adreno_gpu->reg_offsets = a3xx_register_offsets; - ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs); + ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c index f87c43124099..8199a4b9f2fa 100644 --- a/drivers/gpu/drm/msm/adreno/a4xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a4xx_gpu.c @@ -116,7 +116,7 @@ static void a4xx_enable_hwcg(struct msm_gpu *gpu) static bool a4xx_me_init(struct msm_gpu *gpu) { - struct msm_ringbuffer *ring = gpu->rb; + struct msm_ringbuffer *ring = gpu->rb[0]; OUT_PKT3(ring, CP_ME_INIT, 17); OUT_RING(ring, 0x000003f7); @@ -137,7 +137,7 @@ static bool a4xx_me_init(struct msm_gpu *gpu) OUT_RING(ring, 0x00000000); OUT_RING(ring, 0x00000000); - gpu->funcs->flush(gpu); + gpu->funcs->flush(gpu, ring); return a4xx_idle(gpu); } @@ -337,7 +337,7 @@ static void a4xx_destroy(struct msm_gpu *gpu) static bool a4xx_idle(struct msm_gpu *gpu) { /* wait for ringbuffer to drain: */ - if (!adreno_idle(gpu)) + if (!adreno_idle(gpu, gpu->rb[0])) return false; /* then wait for GPU to finish: */ @@ -534,6 +534,7 @@ static const struct adreno_gpu_funcs funcs = { .recover = a4xx_recover, .submit = adreno_submit, .flush = adreno_flush, + .active_ring = adreno_active_ring, .irq = a4xx_irq, .destroy = a4xx_destroy, #ifdef CONFIG_DEBUG_FS @@ -573,7 +574,7 @@ struct msm_gpu *a4xx_gpu_init(struct drm_device *dev) adreno_gpu->registers = a4xx_registers; adreno_gpu->reg_offsets = a4xx_register_offsets; - ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs); + ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1); if (ret) goto fail; diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index e34835c3b55d..32252f8ac30c 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -117,7 +117,7 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, struct msm_file_private *ctx) { struct msm_drm_private *priv = gpu->dev->dev_private; - struct msm_ringbuffer *ring = gpu->rb; + struct msm_ringbuffer *ring = submit->ring; unsigned int i, ibs = 0; for (i = 0; i < submit->nr_cmds; i++) { @@ -138,15 +138,15 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, } OUT_PKT4(ring, REG_A5XX_CP_SCRATCH_REG(2), 1); - OUT_RING(ring, submit->fence->seqno); + OUT_RING(ring, submit->seqno); OUT_PKT7(ring, CP_EVENT_WRITE, 4); OUT_RING(ring, CACHE_FLUSH_TS | (1 << 31)); - OUT_RING(ring, lower_32_bits(rbmemptr(gpu, fence))); - OUT_RING(ring, upper_32_bits(rbmemptr(gpu, fence))); - OUT_RING(ring, submit->fence->seqno); + OUT_RING(ring, lower_32_bits(rbmemptr(ring, fence))); + OUT_RING(ring, upper_32_bits(rbmemptr(ring, fence))); + OUT_RING(ring, submit->seqno); - gpu->funcs->flush(gpu); + gpu->funcs->flush(gpu, ring); } static const struct { @@ -262,7 +262,7 @@ void a5xx_set_hwcg(struct msm_gpu *gpu, bool state) static int a5xx_me_init(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - struct msm_ringbuffer *ring = gpu->rb; + struct msm_ringbuffer *ring = gpu->rb[0]; OUT_PKT7(ring, CP_ME_INIT, 8); @@ -293,9 +293,8 @@ static int a5xx_me_init(struct msm_gpu *gpu) OUT_RING(ring, 0x00000000); OUT_RING(ring, 0x00000000); - gpu->funcs->flush(gpu); - - return a5xx_idle(gpu) ? 0 : -EINVAL; + gpu->funcs->flush(gpu, ring); + return a5xx_idle(gpu, ring) ? 0 : -EINVAL; } static struct drm_gem_object *a5xx_ucode_load_bo(struct msm_gpu *gpu, @@ -581,11 +580,11 @@ static int a5xx_hw_init(struct msm_gpu *gpu) * ticking correctly */ if (adreno_is_a530(adreno_gpu)) { - OUT_PKT7(gpu->rb, CP_EVENT_WRITE, 1); - OUT_RING(gpu->rb, 0x0F); + OUT_PKT7(gpu->rb[0], CP_EVENT_WRITE, 1); + OUT_RING(gpu->rb[0], 0x0F); - gpu->funcs->flush(gpu); - if (!a5xx_idle(gpu)) + gpu->funcs->flush(gpu, gpu->rb[0]); + if (!a5xx_idle(gpu, gpu->rb[0])) return -EINVAL; } @@ -598,11 +597,11 @@ static int a5xx_hw_init(struct msm_gpu *gpu) */ ret = a5xx_zap_shader_init(gpu); if (!ret) { - OUT_PKT7(gpu->rb, CP_SET_SECURE_MODE, 1); - OUT_RING(gpu->rb, 0x00000000); + OUT_PKT7(gpu->rb[0], CP_SET_SECURE_MODE, 1); + OUT_RING(gpu->rb[0], 0x00000000); - gpu->funcs->flush(gpu); - if (!a5xx_idle(gpu)) + gpu->funcs->flush(gpu, gpu->rb[0]); + if (!a5xx_idle(gpu, gpu->rb[0])) return -EINVAL; } else { /* Print a warning so if we die, we know why */ @@ -676,18 +675,19 @@ static inline bool _a5xx_check_idle(struct msm_gpu *gpu) A5XX_RBBM_INT_0_MASK_MISC_HANG_DETECT); } -bool a5xx_idle(struct msm_gpu *gpu) +bool a5xx_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring) { /* wait for CP to drain ringbuffer: */ - if (!adreno_idle(gpu)) + if (!adreno_idle(gpu, ring)) return false; if (spin_until(_a5xx_check_idle(gpu))) { - DRM_ERROR("%s: %ps: timeout waiting for GPU to idle: status %8.8X irq %8.8X\n", + DRM_ERROR("%s: %ps: timeout waiting for GPU to idle: status %8.8X irq %8.8X rptr/wptr %d/%d\n", gpu->name, __builtin_return_address(0), gpu_read(gpu, REG_A5XX_RBBM_STATUS), - gpu_read(gpu, REG_A5XX_RBBM_INT_0_STATUS)); - + gpu_read(gpu, REG_A5XX_RBBM_INT_0_STATUS), + gpu_read(gpu, REG_A5XX_CP_RB_RPTR), + gpu_read(gpu, REG_A5XX_CP_RB_WPTR)); return false; } @@ -818,9 +818,10 @@ static void a5xx_fault_detect_irq(struct msm_gpu *gpu) { struct drm_device *dev = gpu->dev; struct msm_drm_private *priv = dev->dev_private; + struct msm_ringbuffer *ring = gpu->funcs->active_ring(gpu); - dev_err(dev->dev, "gpu fault fence %x status %8.8X rb %4.4x/%4.4x ib1 %16.16llX/%4.4x ib2 %16.16llX/%4.4x\n", - gpu->memptrs->fence, + dev_err(dev->dev, "gpu fault ring %d fence %x status %8.8X rb %4.4x/%4.4x ib1 %16.16llX/%4.4x ib2 %16.16llX/%4.4x\n", + ring ? ring->id : -1, ring ? ring->seqno : 0, gpu_read(gpu, REG_A5XX_RBBM_STATUS), gpu_read(gpu, REG_A5XX_CP_RB_RPTR), gpu_read(gpu, REG_A5XX_CP_RB_WPTR), @@ -1010,6 +1011,7 @@ static const struct adreno_gpu_funcs funcs = { .recover = a5xx_recover, .submit = a5xx_submit, .flush = adreno_flush, + .active_ring = adreno_active_ring, .irq = a5xx_irq, .destroy = a5xx_destroy, #ifdef CONFIG_DEBUG_FS @@ -1045,7 +1047,7 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev) a5xx_gpu->lm_leakage = 0x4E001A; - ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs); + ret = adreno_gpu_init(dev, pdev, adreno_gpu, &funcs, 1); if (ret) { a5xx_destroy(&(a5xx_gpu->base.base)); return ERR_PTR(ret); diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h index e94451685bf8..44db48d86202 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h @@ -55,7 +55,7 @@ static inline int spin_usecs(struct msm_gpu *gpu, uint32_t usecs, return -ETIMEDOUT; } -bool a5xx_idle(struct msm_gpu *gpu); +bool a5xx_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring); void a5xx_set_hwcg(struct msm_gpu *gpu, bool state); #endif /* __A5XX_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/adreno/a5xx_power.c b/drivers/gpu/drm/msm/adreno/a5xx_power.c index b5de2be67732..e5700bbf09dd 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_power.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_power.c @@ -173,7 +173,7 @@ static int a5xx_gpmu_init(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); - struct msm_ringbuffer *ring = gpu->rb; + struct msm_ringbuffer *ring = gpu->rb[0]; if (!a5xx_gpu->gpmu_dwords) return 0; @@ -192,9 +192,9 @@ static int a5xx_gpmu_init(struct msm_gpu *gpu) OUT_PKT7(ring, CP_SET_PROTECTED_MODE, 1); OUT_RING(ring, 1); - gpu->funcs->flush(gpu); + gpu->funcs->flush(gpu, ring); - if (!a5xx_idle(gpu)) { + if (!a5xx_idle(gpu, ring)) { DRM_ERROR("%s: Unable to load GPMU firmware. GPMU will not be active\n", gpu->name); return -EINVAL; diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index 5f2501c2cd3f..fd0fb0568dd3 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -21,7 +21,6 @@ #include "msm_gem.h" #include "msm_mmu.h" -#define RB_SIZE SZ_32K #define RB_BLKSIZE 32 int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) @@ -163,7 +162,7 @@ static int adreno_load_fw(struct adreno_gpu *adreno_gpu) int adreno_hw_init(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - int ret; + int ret, i; DBG("%s", gpu->name); @@ -171,34 +170,42 @@ int adreno_hw_init(struct msm_gpu *gpu) if (ret) return ret; - ret = msm_gem_get_iova(gpu->rb->bo, gpu->aspace, &gpu->rb_iova); - if (ret) { - gpu->rb_iova = 0; - dev_err(gpu->dev->dev, "could not map ringbuffer: %d\n", ret); - return ret; - } + for (i = 0; i < gpu->nr_rings; i++) { + struct msm_ringbuffer *ring = gpu->rb[i]; - /* reset ringbuffer: */ - gpu->rb->cur = gpu->rb->start; + if (!ring) + continue; - /* reset completed fence seqno: */ - gpu->memptrs->fence = gpu->fctx->completed_fence; - gpu->memptrs->rptr = 0; + ret = msm_gem_get_iova(ring->bo, gpu->aspace, &ring->iova); + if (ret) { + ring->iova = 0; + dev_err(gpu->dev->dev, + "could not map ringbuffer %d: %d\n", i, ret); + return ret; + } + + ring->cur = ring->start; + + /* reset completed fence seqno: */ + ring->memptrs->fence = ring->seqno; + ring->memptrs->rptr = 0; + } /* Setup REG_CP_RB_CNTL: */ adreno_gpu_write(adreno_gpu, REG_ADRENO_CP_RB_CNTL, - /* size is log2(quad-words): */ - AXXX_CP_RB_CNTL_BUFSZ(ilog2(gpu->rb->size / 8)) | - AXXX_CP_RB_CNTL_BLKSZ(ilog2(RB_BLKSIZE / 8)) | - (adreno_is_a430(adreno_gpu) ? AXXX_CP_RB_CNTL_NO_UPDATE : 0)); + /* size is log2(quad-words): */ + AXXX_CP_RB_CNTL_BUFSZ(ilog2(MSM_GPU_RINGBUFFER_SZ / 8)) | + AXXX_CP_RB_CNTL_BLKSZ(ilog2(RB_BLKSIZE / 8)) | + (adreno_is_a430(adreno_gpu) ? AXXX_CP_RB_CNTL_NO_UPDATE : 0)); - /* Setup ringbuffer address: */ + /* Setup ringbuffer address - use ringbuffer[0] for GPU init */ adreno_gpu_write64(adreno_gpu, REG_ADRENO_CP_RB_BASE, - REG_ADRENO_CP_RB_BASE_HI, gpu->rb_iova); + REG_ADRENO_CP_RB_BASE_HI, gpu->rb[0]->iova); if (!adreno_is_a430(adreno_gpu)) { adreno_gpu_write64(adreno_gpu, REG_ADRENO_CP_RB_RPTR_ADDR, - REG_ADRENO_CP_RB_RPTR_ADDR_HI, rbmemptr(gpu, rptr)); + REG_ADRENO_CP_RB_RPTR_ADDR_HI, + rbmemptr(gpu->rb[0], rptr)); } return 0; @@ -210,15 +217,19 @@ static uint32_t get_wptr(struct msm_ringbuffer *ring) } /* Use this helper to read rptr, since a430 doesn't update rptr in memory */ -static uint32_t get_rptr(struct adreno_gpu *adreno_gpu) +static uint32_t get_rptr(struct adreno_gpu *adreno_gpu, + struct msm_ringbuffer *ring) { - struct msm_gpu *gpu = &adreno_gpu->base; - if (adreno_is_a430(adreno_gpu)) - return gpu->memptrs->rptr = adreno_gpu_read( + return ring->memptrs->rptr = adreno_gpu_read( adreno_gpu, REG_ADRENO_CP_RB_RPTR); else - return gpu->memptrs->rptr; + return ring->memptrs->rptr; +} + +struct msm_ringbuffer *adreno_active_ring(struct msm_gpu *gpu) +{ + return gpu->rb[0]; } void adreno_recover(struct msm_gpu *gpu) @@ -244,7 +255,7 @@ void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct msm_drm_private *priv = gpu->dev->dev_private; - struct msm_ringbuffer *ring = gpu->rb; + struct msm_ringbuffer *ring = submit->ring; unsigned i; for (i = 0; i < submit->nr_cmds; i++) { @@ -267,7 +278,7 @@ void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, } OUT_PKT0(ring, REG_AXXX_CP_SCRATCH_REG2, 1); - OUT_RING(ring, submit->fence->seqno); + OUT_RING(ring, submit->seqno); if (adreno_is_a3xx(adreno_gpu) || adreno_is_a4xx(adreno_gpu)) { /* Flush HLSQ lazy updates to make sure there is nothing @@ -283,8 +294,8 @@ void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, OUT_PKT3(ring, CP_EVENT_WRITE, 3); OUT_RING(ring, CACHE_FLUSH_TS); - OUT_RING(ring, rbmemptr(gpu, fence)); - OUT_RING(ring, submit->fence->seqno); + OUT_RING(ring, rbmemptr(ring, fence)); + OUT_RING(ring, submit->seqno); /* we could maybe be clever and only CP_COND_EXEC the interrupt: */ OUT_PKT3(ring, CP_INTERRUPT, 1); @@ -310,10 +321,10 @@ void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, } #endif - gpu->funcs->flush(gpu); + gpu->funcs->flush(gpu, ring); } -void adreno_flush(struct msm_gpu *gpu) +void adreno_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); uint32_t wptr; @@ -323,7 +334,7 @@ void adreno_flush(struct msm_gpu *gpu) * to account for the possibility that the last command fit exactly into * the ringbuffer and rb->next hasn't wrapped to zero yet */ - wptr = get_wptr(gpu->rb) & ((gpu->rb->size / 4) - 1); + wptr = get_wptr(ring) % (MSM_GPU_RINGBUFFER_SZ >> 2); /* ensure writes to ringbuffer have hit system memory: */ mb(); @@ -331,17 +342,18 @@ void adreno_flush(struct msm_gpu *gpu) adreno_gpu_write(adreno_gpu, REG_ADRENO_CP_RB_WPTR, wptr); } -bool adreno_idle(struct msm_gpu *gpu) +bool adreno_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - uint32_t wptr = get_wptr(gpu->rb); + uint32_t wptr = get_wptr(ring); /* wait for CP to drain ringbuffer: */ - if (!spin_until(get_rptr(adreno_gpu) == wptr)) + if (!spin_until(get_rptr(adreno_gpu, ring) == wptr)) return true; /* TODO maybe we need to reset GPU here to recover from hang? */ - DRM_ERROR("%s: timeout waiting to drain ringbuffer!\n", gpu->name); + DRM_ERROR("%s: timeout waiting to drain ringbuffer %d!\n", gpu->name, + ring->id); return false; } @@ -356,10 +368,16 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) adreno_gpu->rev.major, adreno_gpu->rev.minor, adreno_gpu->rev.patchid); - seq_printf(m, "fence: %d/%d\n", gpu->memptrs->fence, - gpu->fctx->last_fence); - seq_printf(m, "rptr: %d\n", get_rptr(adreno_gpu)); - seq_printf(m, "rb wptr: %d\n", get_wptr(gpu->rb)); + for (i = 0; i < gpu->nr_rings; i++) { + struct msm_ringbuffer *ring = gpu->rb[i]; + + seq_printf(m, "rb %d: fence: %d/%d\n", i, + ring->memptrs->fence, ring->seqno); + + seq_printf(m, " rptr: %d\n", + get_rptr(adreno_gpu, ring)); + seq_printf(m, "rb wptr: %d\n", get_wptr(ring)); + } /* dump these out in a form that can be parsed by demsm: */ seq_printf(m, "IO:region %s 00000000 00020000\n", gpu->name); @@ -385,16 +403,23 @@ void adreno_show(struct msm_gpu *gpu, struct seq_file *m) void adreno_dump_info(struct msm_gpu *gpu) { struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); + int i; printk("revision: %d (%d.%d.%d.%d)\n", adreno_gpu->info->revn, adreno_gpu->rev.core, adreno_gpu->rev.major, adreno_gpu->rev.minor, adreno_gpu->rev.patchid); - printk("fence: %d/%d\n", gpu->memptrs->fence, - gpu->fctx->last_fence); - printk("rptr: %d\n", get_rptr(adreno_gpu)); - printk("rb wptr: %d\n", get_wptr(gpu->rb)); + for (i = 0; i < gpu->nr_rings; i++) { + struct msm_ringbuffer *ring = gpu->rb[i]; + + printk("rb %d: fence: %d/%d\n", i, + ring->memptrs->fence, + ring->seqno); + + printk("rptr: %d\n", get_rptr(adreno_gpu, ring)); + printk("rb wptr: %d\n", get_wptr(ring)); + } } /* would be nice to not have to duplicate the _show() stuff with printk(): */ @@ -417,23 +442,26 @@ void adreno_dump(struct msm_gpu *gpu) } } -static uint32_t ring_freewords(struct msm_gpu *gpu) +static uint32_t ring_freewords(struct msm_ringbuffer *ring) { - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - uint32_t size = gpu->rb->size / 4; - uint32_t wptr = get_wptr(gpu->rb); - uint32_t rptr = get_rptr(adreno_gpu); + struct adreno_gpu *adreno_gpu = to_adreno_gpu(ring->gpu); + uint32_t size = MSM_GPU_RINGBUFFER_SZ >> 2; + uint32_t wptr = get_wptr(ring); + uint32_t rptr = get_rptr(adreno_gpu, ring); return (rptr + (size - 1) - wptr) % size; } -void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords) +void adreno_wait_ring(struct msm_ringbuffer *ring, uint32_t ndwords) { - if (spin_until(ring_freewords(gpu) >= ndwords)) - DRM_ERROR("%s: timeout waiting for ringbuffer space\n", gpu->name); + if (spin_until(ring_freewords(ring) >= ndwords)) + DRM_DEV_ERROR(ring->gpu->dev->dev, + "timeout waiting for space in ringubffer %d\n", + ring->id); } int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, - struct adreno_gpu *adreno_gpu, const struct adreno_gpu_funcs *funcs) + struct adreno_gpu *adreno_gpu, + const struct adreno_gpu_funcs *funcs, int nr_rings) { struct adreno_platform_config *config = pdev->dev.platform_data; struct msm_gpu_config adreno_gpu_config = { 0 }; @@ -460,7 +488,7 @@ int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, adreno_gpu_config.va_start = SZ_16M; adreno_gpu_config.va_end = 0xffffffff; - adreno_gpu_config.ringsz = RB_SIZE; + adreno_gpu_config.nr_rings = nr_rings; pm_runtime_set_autosuspend_delay(&pdev->dev, DRM_MSM_INACTIVE_PERIOD); pm_runtime_use_autosuspend(&pdev->dev); diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.h b/drivers/gpu/drm/msm/adreno/adreno_gpu.h index 1676282948d5..3e9a1743f476 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.h +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.h @@ -208,17 +208,19 @@ int adreno_hw_init(struct msm_gpu *gpu); void adreno_recover(struct msm_gpu *gpu); void adreno_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, struct msm_file_private *ctx); -void adreno_flush(struct msm_gpu *gpu); -bool adreno_idle(struct msm_gpu *gpu); +void adreno_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring); +bool adreno_idle(struct msm_gpu *gpu, struct msm_ringbuffer *ring); #ifdef CONFIG_DEBUG_FS void adreno_show(struct msm_gpu *gpu, struct seq_file *m); #endif void adreno_dump_info(struct msm_gpu *gpu); void adreno_dump(struct msm_gpu *gpu); -void adreno_wait_ring(struct msm_gpu *gpu, uint32_t ndwords); +void adreno_wait_ring(struct msm_ringbuffer *ring, uint32_t ndwords); +struct msm_ringbuffer *adreno_active_ring(struct msm_gpu *gpu); int adreno_gpu_init(struct drm_device *drm, struct platform_device *pdev, - struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs); + struct adreno_gpu *gpu, const struct adreno_gpu_funcs *funcs, + int nr_rings); void adreno_gpu_cleanup(struct adreno_gpu *gpu); @@ -227,7 +229,7 @@ void adreno_gpu_cleanup(struct adreno_gpu *gpu); static inline void OUT_PKT0(struct msm_ringbuffer *ring, uint16_t regindx, uint16_t cnt) { - adreno_wait_ring(ring->gpu, cnt+1); + adreno_wait_ring(ring, cnt+1); OUT_RING(ring, CP_TYPE0_PKT | ((cnt-1) << 16) | (regindx & 0x7FFF)); } @@ -235,14 +237,14 @@ OUT_PKT0(struct msm_ringbuffer *ring, uint16_t regindx, uint16_t cnt) static inline void OUT_PKT2(struct msm_ringbuffer *ring) { - adreno_wait_ring(ring->gpu, 1); + adreno_wait_ring(ring, 1); OUT_RING(ring, CP_TYPE2_PKT); } static inline void OUT_PKT3(struct msm_ringbuffer *ring, uint8_t opcode, uint16_t cnt) { - adreno_wait_ring(ring->gpu, cnt+1); + adreno_wait_ring(ring, cnt+1); OUT_RING(ring, CP_TYPE3_PKT | ((cnt-1) << 16) | ((opcode & 0xFF) << 8)); } @@ -264,14 +266,14 @@ static inline u32 PM4_PARITY(u32 val) static inline void OUT_PKT4(struct msm_ringbuffer *ring, uint16_t regindx, uint16_t cnt) { - adreno_wait_ring(ring->gpu, cnt + 1); + adreno_wait_ring(ring, cnt + 1); OUT_RING(ring, PKT4(regindx, cnt)); } static inline void OUT_PKT7(struct msm_ringbuffer *ring, uint8_t opcode, uint16_t cnt) { - adreno_wait_ring(ring->gpu, cnt + 1); + adreno_wait_ring(ring, cnt + 1); OUT_RING(ring, CP_TYPE7_PKT | (cnt << 0) | (PM4_PARITY(cnt) << 15) | ((opcode & 0x7F) << 16) | (PM4_PARITY(opcode) << 23)); } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 2585bebfcf3e..bfb8c7cf800a 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -507,7 +507,7 @@ static void load_gpu(struct drm_device *dev) mutex_unlock(&init_lock); } -static int context_init(struct drm_file *file) +static int context_init(struct drm_device *dev, struct drm_file *file) { struct msm_file_private *ctx; @@ -515,7 +515,7 @@ static int context_init(struct drm_file *file) if (!ctx) return -ENOMEM; - msm_submitqueue_init(ctx); + msm_submitqueue_init(dev, ctx); file->driver_priv = ctx; @@ -529,7 +529,7 @@ static int msm_open(struct drm_device *dev, struct drm_file *file) */ load_gpu(dev); - return context_init(file); + return context_init(dev, file); } static void context_close(struct msm_file_private *ctx) @@ -743,16 +743,27 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data, struct msm_drm_private *priv = dev->dev_private; struct drm_msm_wait_fence *args = data; ktime_t timeout = to_ktime(args->timeout); + struct msm_gpu_submitqueue *queue; + struct msm_gpu *gpu = priv->gpu; + int ret; if (args->pad) { DRM_ERROR("invalid pad: %08x\n", args->pad); return -EINVAL; } - if (!priv->gpu) + if (!gpu) return 0; - return msm_wait_fence(priv->gpu->fctx, args->fence, &timeout, true); + queue = msm_submitqueue_get(file->driver_priv, args->queueid); + if (!queue) + return -ENOENT; + + ret = msm_wait_fence(gpu->rb[queue->prio]->fctx, args->fence, &timeout, + true); + + msm_submitqueue_put(queue); + return ret; } static int msm_ioctl_gem_madvise(struct drm_device *dev, void *data, @@ -802,7 +813,7 @@ static int msm_ioctl_submitqueue_new(struct drm_device *dev, void *data, if (args->flags & ~MSM_SUBMITQUEUE_FLAGS) return -EINVAL; - return msm_submitqueue_create(file->driver_priv, args->prio, + return msm_submitqueue_create(dev, file->driver_priv, args->prio, args->flags, &args->id); } diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index b3b8f20f58db..2821f572ecd8 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -74,6 +74,8 @@ struct msm_vblank_ctrl { spinlock_t lock; }; +#define MSM_GPU_MAX_RINGS 1 + struct msm_drm_private { struct drm_device *dev; @@ -318,11 +320,11 @@ void msm_writel(u32 data, void __iomem *addr); u32 msm_readl(const void __iomem *addr); struct msm_gpu_submitqueue; -int msm_submitqueue_init(struct msm_file_private *ctx); +int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx); struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx, u32 id); -int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio, - u32 flags, u32 *id); +int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx, + u32 prio, u32 flags, u32 *id); int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id); void msm_submitqueue_close(struct msm_file_private *ctx); diff --git a/drivers/gpu/drm/msm/msm_fence.c b/drivers/gpu/drm/msm/msm_fence.c index a2f89bac9c16..349c12f670eb 100644 --- a/drivers/gpu/drm/msm/msm_fence.c +++ b/drivers/gpu/drm/msm/msm_fence.c @@ -31,7 +31,7 @@ msm_fence_context_alloc(struct drm_device *dev, const char *name) return ERR_PTR(-ENOMEM); fctx->dev = dev; - fctx->name = name; + strncpy(fctx->name, name, sizeof(fctx->name)); fctx->context = dma_fence_context_alloc(1); init_waitqueue_head(&fctx->event); spin_lock_init(&fctx->spinlock); diff --git a/drivers/gpu/drm/msm/msm_fence.h b/drivers/gpu/drm/msm/msm_fence.h index 56061aa1959d..1aa6a4c6530c 100644 --- a/drivers/gpu/drm/msm/msm_fence.h +++ b/drivers/gpu/drm/msm/msm_fence.h @@ -22,7 +22,7 @@ struct msm_fence_context { struct drm_device *dev; - const char *name; + char name[32]; unsigned context; /* last_fence == completed_fence --> no pending work */ uint32_t last_fence; /* last assigned fence */ diff --git a/drivers/gpu/drm/msm/msm_gem.h b/drivers/gpu/drm/msm/msm_gem.h index 17f8a6c23464..9320e184b48d 100644 --- a/drivers/gpu/drm/msm/msm_gem.h +++ b/drivers/gpu/drm/msm/msm_gem.h @@ -138,13 +138,15 @@ void msm_gem_vunmap(struct drm_gem_object *obj, enum msm_gem_lock subclass); struct msm_gem_submit { struct drm_device *dev; struct msm_gpu *gpu; - struct list_head node; /* node in gpu submit_list */ + struct list_head node; /* node in ring submit list */ struct list_head bo_list; struct ww_acquire_ctx ticket; + uint32_t seqno; /* Sequence number of the submit on the ring */ struct dma_fence *fence; struct msm_gpu_submitqueue *queue; struct pid *pid; /* submitting process */ bool valid; /* true if no cmdstream patching needed */ + struct msm_ringbuffer *ring; unsigned int nr_cmds; unsigned int nr_bos; struct { diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index c196cc615e77..d3551aa130fb 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -51,6 +51,7 @@ static struct msm_gem_submit *submit_create(struct drm_device *dev, submit->pid = get_pid(task_pid(current)); submit->cmd = (void *)&submit->bos[nr_bos]; submit->queue = queue; + submit->ring = gpu->rb[queue->prio]; /* initially, until copy_from_user() and bo lookup succeeds: */ submit->nr_bos = 0; @@ -247,7 +248,8 @@ static int submit_fence_sync(struct msm_gem_submit *submit, bool no_implicit) if (no_implicit) continue; - ret = msm_gem_sync_object(&msm_obj->base, submit->gpu->fctx, write); + ret = msm_gem_sync_object(&msm_obj->base, submit->ring->fctx, + write); if (ret) break; } @@ -410,6 +412,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, struct dma_fence *in_fence = NULL; struct sync_file *sync_file = NULL; struct msm_gpu_submitqueue *queue; + struct msm_ringbuffer *ring; int out_fence_fd = -1; unsigned i; int ret; @@ -430,6 +433,8 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, if (!queue) return -ENOENT; + ring = gpu->rb[queue->prio]; + if (args->flags & MSM_SUBMIT_FENCE_FD_IN) { in_fence = sync_file_get_fence(args->fence_fd); @@ -440,7 +445,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, * Wait if the fence is from a foreign context, or if the fence * array contains any fence from a foreign context. */ - if (!dma_fence_match_context(in_fence, gpu->fctx->context)) { + if (!dma_fence_match_context(in_fence, ring->fctx->context)) { ret = dma_fence_wait(in_fence, true); if (ret) return ret; @@ -543,8 +548,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, submit->nr_cmds = i; - submit->fence = msm_fence_alloc(gpu->fctx); - + submit->fence = msm_fence_alloc(ring->fctx); if (IS_ERR(submit->fence)) { ret = PTR_ERR(submit->fence); submit->fence = NULL; diff --git a/drivers/gpu/drm/msm/msm_gpu.c b/drivers/gpu/drm/msm/msm_gpu.c index 0744837ed70f..ec28c99ee36e 100644 --- a/drivers/gpu/drm/msm/msm_gpu.c +++ b/drivers/gpu/drm/msm/msm_gpu.c @@ -221,6 +221,20 @@ int msm_gpu_hw_init(struct msm_gpu *gpu) * Hangcheck detection for locked gpu: */ +static void update_fences(struct msm_gpu *gpu, struct msm_ringbuffer *ring, + uint32_t fence) +{ + struct msm_gem_submit *submit; + + list_for_each_entry(submit, &ring->submits, node) { + if (submit->seqno > fence) + break; + + msm_update_fence(submit->ring->fctx, + submit->fence->seqno); + } +} + static void retire_submits(struct msm_gpu *gpu); static void recover_worker(struct work_struct *work) @@ -228,15 +242,34 @@ static void recover_worker(struct work_struct *work) struct msm_gpu *gpu = container_of(work, struct msm_gpu, recover_work); struct drm_device *dev = gpu->dev; struct msm_gem_submit *submit; - uint32_t fence = gpu->memptrs->fence; + struct msm_ringbuffer *cur_ring = gpu->funcs->active_ring(gpu); + uint64_t fence; + int i; + + /* Update all the rings with the latest and greatest fence */ + for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) { + struct msm_ringbuffer *ring = gpu->rb[i]; - msm_update_fence(gpu->fctx, fence + 1); + fence = ring->memptrs->fence; + + /* + * For the current (faulting?) ring/submit advance the fence by + * one more to clear the faulting submit + */ + if (ring == cur_ring) + fence = fence + 1; + + update_fences(gpu, ring, fence); + } mutex_lock(&dev->struct_mutex); + dev_err(dev->dev, "%s: hangcheck recover!\n", gpu->name); - list_for_each_entry(submit, &gpu->submit_list, node) { - if (submit->fence->seqno == (fence + 1)) { + fence = cur_ring->memptrs->fence + 1; + + list_for_each_entry(submit, &cur_ring->submits, node) { + if (submit->seqno == fence) { struct task_struct *task; rcu_read_lock(); @@ -258,9 +291,16 @@ static void recover_worker(struct work_struct *work) gpu->funcs->recover(gpu); pm_runtime_put_sync(&gpu->pdev->dev); - /* replay the remaining submits after the one that hung: */ - list_for_each_entry(submit, &gpu->submit_list, node) { - gpu->funcs->submit(gpu, submit, NULL); + /* + * Replay all remaining submits starting with highest priority + * ring + */ + + for (i = gpu->nr_rings - 1; i >= 0; i--) { + struct msm_ringbuffer *ring = gpu->rb[i]; + + list_for_each_entry(submit, &ring->submits, node) + gpu->funcs->submit(gpu, submit, NULL); } } @@ -281,25 +321,27 @@ static void hangcheck_handler(unsigned long data) struct msm_gpu *gpu = (struct msm_gpu *)data; struct drm_device *dev = gpu->dev; struct msm_drm_private *priv = dev->dev_private; - uint32_t fence = gpu->memptrs->fence; + struct msm_ringbuffer *ring = gpu->funcs->active_ring(gpu); + uint32_t fence = ring->memptrs->fence; - if (fence != gpu->hangcheck_fence) { + if (fence != ring->hangcheck_fence) { /* some progress has been made.. ya! */ - gpu->hangcheck_fence = fence; - } else if (fence < gpu->fctx->last_fence) { + ring->hangcheck_fence = fence; + } else if (fence < ring->seqno) { /* no progress and not done.. hung! */ - gpu->hangcheck_fence = fence; - dev_err(dev->dev, "%s: hangcheck detected gpu lockup!\n", - gpu->name); + ring->hangcheck_fence = fence; + dev_err(dev->dev, "%s: hangcheck detected gpu lockup rb %d!\n", + gpu->name, ring->id); dev_err(dev->dev, "%s: completed fence: %u\n", gpu->name, fence); dev_err(dev->dev, "%s: submitted fence: %u\n", - gpu->name, gpu->fctx->last_fence); + gpu->name, ring->seqno); + queue_work(priv->wq, &gpu->recover_work); } /* if still more pending work, reset the hangcheck timer: */ - if (gpu->fctx->last_fence > gpu->hangcheck_fence) + if (ring->seqno > ring->hangcheck_fence) hangcheck_timer_reset(gpu); /* workaround for missing irq: */ @@ -428,19 +470,18 @@ static void retire_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit) static void retire_submits(struct msm_gpu *gpu) { struct drm_device *dev = gpu->dev; + struct msm_gem_submit *submit, *tmp; + int i; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - while (!list_empty(&gpu->submit_list)) { - struct msm_gem_submit *submit; - - submit = list_first_entry(&gpu->submit_list, - struct msm_gem_submit, node); + /* Retire the commits starting with highest priority */ + for (i = gpu->nr_rings - 1; i >= 0; i--) { + struct msm_ringbuffer *ring = gpu->rb[i]; - if (dma_fence_is_signaled(submit->fence)) { - retire_submit(gpu, submit); - } else { - break; + list_for_each_entry_safe(submit, tmp, &ring->submits, node) { + if (dma_fence_is_signaled(submit->fence)) + retire_submit(gpu, submit); } } } @@ -449,9 +490,10 @@ static void retire_worker(struct work_struct *work) { struct msm_gpu *gpu = container_of(work, struct msm_gpu, retire_work); struct drm_device *dev = gpu->dev; - uint32_t fence = gpu->memptrs->fence; + int i; - msm_update_fence(gpu->fctx, fence); + for (i = 0; i < gpu->nr_rings; i++) + update_fences(gpu, gpu->rb[i], gpu->rb[i]->memptrs->fence); mutex_lock(&dev->struct_mutex); retire_submits(gpu); @@ -472,6 +514,7 @@ void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, { struct drm_device *dev = gpu->dev; struct msm_drm_private *priv = dev->dev_private; + struct msm_ringbuffer *ring = submit->ring; int i; WARN_ON(!mutex_is_locked(&dev->struct_mutex)); @@ -480,7 +523,9 @@ void msm_gpu_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, msm_gpu_hw_init(gpu); - list_add_tail(&submit->node, &gpu->submit_list); + submit->seqno = ++ring->seqno; + + list_add_tail(&submit->node, &ring->submits); msm_rd_dump_submit(submit); @@ -605,7 +650,9 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, struct msm_gpu *gpu, const struct msm_gpu_funcs *funcs, const char *name, struct msm_gpu_config *config) { - int ret; + int i, ret, nr_rings = config->nr_rings; + void *memptrs; + uint64_t memptrs_iova; if (WARN_ON(gpu->num_perfcntrs > ARRAY_SIZE(gpu->last_cntrs))) gpu->num_perfcntrs = ARRAY_SIZE(gpu->last_cntrs); @@ -613,18 +660,11 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, gpu->dev = drm; gpu->funcs = funcs; gpu->name = name; - gpu->fctx = msm_fence_context_alloc(drm, name); - if (IS_ERR(gpu->fctx)) { - ret = PTR_ERR(gpu->fctx); - gpu->fctx = NULL; - goto fail; - } INIT_LIST_HEAD(&gpu->active_list); INIT_WORK(&gpu->retire_work, retire_worker); INIT_WORK(&gpu->recover_work, recover_worker); - INIT_LIST_HEAD(&gpu->submit_list); setup_timer(&gpu->hangcheck_timer, hangcheck_handler, (unsigned long)gpu); @@ -689,29 +729,47 @@ int msm_gpu_init(struct drm_device *drm, struct platform_device *pdev, goto fail; } - gpu->memptrs = msm_gem_kernel_new(drm, sizeof(*gpu->memptrs_bo), + memptrs = msm_gem_kernel_new(drm, sizeof(*gpu->memptrs_bo), MSM_BO_UNCACHED, gpu->aspace, &gpu->memptrs_bo, - &gpu->memptrs_iova); + &memptrs_iova); - if (IS_ERR(gpu->memptrs)) { - ret = PTR_ERR(gpu->memptrs); - gpu->memptrs = NULL; + if (IS_ERR(memptrs)) { + ret = PTR_ERR(memptrs); dev_err(drm->dev, "could not allocate memptrs: %d\n", ret); goto fail; } - /* Create ringbuffer: */ - gpu->rb = msm_ringbuffer_new(gpu, config->ringsz); - if (IS_ERR(gpu->rb)) { - ret = PTR_ERR(gpu->rb); - gpu->rb = NULL; - dev_err(drm->dev, "could not create ringbuffer: %d\n", ret); - goto fail; + if (nr_rings > ARRAY_SIZE(gpu->rb)) { + DRM_DEV_INFO_ONCE(drm->dev, "Only creating %lu ringbuffers\n", + ARRAY_SIZE(gpu->rb)); + nr_rings = ARRAY_SIZE(gpu->rb); } + /* Create ringbuffer(s): */ + for (i = 0; i < nr_rings; i++) { + gpu->rb[i] = msm_ringbuffer_new(gpu, i, memptrs, memptrs_iova); + + if (IS_ERR(gpu->rb[i])) { + ret = PTR_ERR(gpu->rb[i]); + dev_err(drm->dev, + "could not create ringbuffer %d: %d\n", i, ret); + goto fail; + } + + memptrs += sizeof(struct msm_rbmemptrs); + memptrs_iova += sizeof(struct msm_rbmemptrs); + } + + gpu->nr_rings = nr_rings; + return 0; fail: + for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) { + msm_ringbuffer_destroy(gpu->rb[i]); + gpu->rb[i] = NULL; + } + if (gpu->memptrs_bo) { msm_gem_put_vaddr(gpu->memptrs_bo); msm_gem_put_iova(gpu->memptrs_bo, gpu->aspace); @@ -724,16 +782,17 @@ fail: void msm_gpu_cleanup(struct msm_gpu *gpu) { + int i; + DBG("%s", gpu->name); WARN_ON(!list_empty(&gpu->active_list)); bs_fini(gpu); - if (gpu->rb) { - if (gpu->rb_iova) - msm_gem_put_iova(gpu->rb->bo, gpu->aspace); - msm_ringbuffer_destroy(gpu->rb); + for (i = 0; i < ARRAY_SIZE(gpu->rb); i++) { + msm_ringbuffer_destroy(gpu->rb[i]); + gpu->rb[i] = NULL; } if (gpu->memptrs_bo) { diff --git a/drivers/gpu/drm/msm/msm_gpu.h b/drivers/gpu/drm/msm/msm_gpu.h index 8ddda059de34..1be0317bb2c0 100644 --- a/drivers/gpu/drm/msm/msm_gpu.h +++ b/drivers/gpu/drm/msm/msm_gpu.h @@ -33,7 +33,7 @@ struct msm_gpu_config { const char *irqname; uint64_t va_start; uint64_t va_end; - unsigned int ringsz; + unsigned int nr_rings; }; /* So far, with hardware that I've seen to date, we can have: @@ -57,8 +57,9 @@ struct msm_gpu_funcs { int (*pm_resume)(struct msm_gpu *gpu); void (*submit)(struct msm_gpu *gpu, struct msm_gem_submit *submit, struct msm_file_private *ctx); - void (*flush)(struct msm_gpu *gpu); + void (*flush)(struct msm_gpu *gpu, struct msm_ringbuffer *ring); irqreturn_t (*irq)(struct msm_gpu *irq); + struct msm_ringbuffer *(*active_ring)(struct msm_gpu *gpu); void (*recover)(struct msm_gpu *gpu); void (*destroy)(struct msm_gpu *gpu); #ifdef CONFIG_DEBUG_FS @@ -67,14 +68,6 @@ struct msm_gpu_funcs { #endif }; -#define rbmemptr(gpu, member) \ - ((gpu)->memptrs_iova + offsetof(struct msm_rbmemptrs, member)) - -struct msm_rbmemptrs { - volatile uint32_t rptr; - volatile uint32_t fence; -}; - struct msm_gpu { const char *name; struct drm_device *dev; @@ -93,16 +86,12 @@ struct msm_gpu { const struct msm_gpu_perfcntr *perfcntrs; uint32_t num_perfcntrs; - /* ringbuffer: */ - struct msm_ringbuffer *rb; - uint64_t rb_iova; + struct msm_ringbuffer *rb[MSM_GPU_MAX_RINGS]; + int nr_rings; /* list of GEM active objects: */ struct list_head active_list; - /* fencing: */ - struct msm_fence_context *fctx; - /* does gpu need hw_init? */ bool needs_hw_init; @@ -133,21 +122,26 @@ struct msm_gpu { #define DRM_MSM_HANGCHECK_PERIOD 500 /* in ms */ #define DRM_MSM_HANGCHECK_JIFFIES msecs_to_jiffies(DRM_MSM_HANGCHECK_PERIOD) struct timer_list hangcheck_timer; - uint32_t hangcheck_fence; struct work_struct recover_work; - struct list_head submit_list; - - struct msm_rbmemptrs *memptrs; struct drm_gem_object *memptrs_bo; - uint64_t memptrs_iova; - - }; +/* It turns out that all targets use the same ringbuffer size */ +#define MSM_GPU_RINGBUFFER_SZ SZ_32K + static inline bool msm_gpu_active(struct msm_gpu *gpu) { - return gpu->fctx->last_fence > gpu->memptrs->fence; + int i; + + for (i = 0; i < gpu->nr_rings; i++) { + struct msm_ringbuffer *ring = gpu->rb[i]; + + if (ring->seqno > ring->memptrs->fence) + return true; + } + + return false; } /* Perf-Counters: diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.c b/drivers/gpu/drm/msm/msm_ringbuffer.c index bf065a540130..4db6ca719706 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.c +++ b/drivers/gpu/drm/msm/msm_ringbuffer.c @@ -18,13 +18,15 @@ #include "msm_ringbuffer.h" #include "msm_gpu.h" -struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size) +struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id, + void *memptrs, uint64_t memptrs_iova) { struct msm_ringbuffer *ring; + char name[32]; int ret; - if (WARN_ON(!is_power_of_2(size))) - return ERR_PTR(-EINVAL); + /* We assume everwhere that MSM_GPU_RINGBUFFER_SZ is a power of 2 */ + BUILD_BUG_ON(!is_power_of_2(MSM_GPU_RINGBUFFER_SZ)); ring = kzalloc(sizeof(*ring), GFP_KERNEL); if (!ring) { @@ -33,32 +35,44 @@ struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size) } ring->gpu = gpu; - + ring->id = id; /* Pass NULL for the iova pointer - we will map it later */ - ring->start = msm_gem_kernel_new(gpu->dev, size, MSM_BO_WC, - gpu->aspace, &ring->bo, NULL); + ring->start = msm_gem_kernel_new(gpu->dev, MSM_GPU_RINGBUFFER_SZ, + MSM_BO_WC, gpu->aspace, &ring->bo, NULL); if (IS_ERR(ring->start)) { ret = PTR_ERR(ring->start); ring->start = 0; goto fail; } - ring->end = ring->start + (size / 4); + ring->end = ring->start + (MSM_GPU_RINGBUFFER_SZ >> 2); ring->cur = ring->start; - ring->size = size; + ring->memptrs = memptrs; + ring->memptrs_iova = memptrs_iova; + + INIT_LIST_HEAD(&ring->submits); + + snprintf(name, sizeof(name), "gpu-ring-%d", ring->id); + + ring->fctx = msm_fence_context_alloc(gpu->dev, name); return ring; fail: - if (ring) - msm_ringbuffer_destroy(ring); + msm_ringbuffer_destroy(ring); return ERR_PTR(ret); } void msm_ringbuffer_destroy(struct msm_ringbuffer *ring) { + if (IS_ERR_OR_NULL(ring)) + return; + + msm_fence_context_free(ring->fctx); + if (ring->bo) { + msm_gem_put_iova(ring->bo, ring->gpu->aspace); msm_gem_put_vaddr(ring->bo); drm_gem_object_unreference_unlocked(ring->bo); } diff --git a/drivers/gpu/drm/msm/msm_ringbuffer.h b/drivers/gpu/drm/msm/msm_ringbuffer.h index 6e0e1049fa4f..ec44251ef9f2 100644 --- a/drivers/gpu/drm/msm/msm_ringbuffer.h +++ b/drivers/gpu/drm/msm/msm_ringbuffer.h @@ -20,14 +20,30 @@ #include "msm_drv.h" +#define rbmemptr(ring, member) \ + ((ring)->memptrs_iova + offsetof(struct msm_rbmemptrs, member)) + +struct msm_rbmemptrs { + volatile uint32_t rptr; + volatile uint32_t fence; +}; + struct msm_ringbuffer { struct msm_gpu *gpu; - int size; + int id; struct drm_gem_object *bo; uint32_t *start, *end, *cur; + struct list_head submits; + uint64_t iova; + uint32_t seqno; + uint32_t hangcheck_fence; + struct msm_rbmemptrs *memptrs; + uint64_t memptrs_iova; + struct msm_fence_context *fctx; }; -struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int size); +struct msm_ringbuffer *msm_ringbuffer_new(struct msm_gpu *gpu, int id, + void *memptrs, uint64_t memptrs_iova); void msm_ringbuffer_destroy(struct msm_ringbuffer *ring); /* ringbuffer helpers (the parts that are same for a3xx/a2xx/z180..) */ diff --git a/drivers/gpu/drm/msm/msm_submitqueue.c b/drivers/gpu/drm/msm/msm_submitqueue.c index 593c3b5f44cd..5115f75b5b7f 100644 --- a/drivers/gpu/drm/msm/msm_submitqueue.c +++ b/drivers/gpu/drm/msm/msm_submitqueue.c @@ -60,9 +60,10 @@ void msm_submitqueue_close(struct msm_file_private *ctx) msm_submitqueue_put(entry); } -int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio, u32 flags, - u32 *id) +int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx, + u32 prio, u32 flags, u32 *id) { + struct msm_drm_private *priv = drm->dev_private; struct msm_gpu_submitqueue *queue; if (!ctx) @@ -75,7 +76,13 @@ int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio, u32 flags, kref_init(&queue->ref); queue->flags = flags; - queue->prio = prio; + + if (priv->gpu) { + if (prio >= priv->gpu->nr_rings) + return -EINVAL; + + queue->prio = prio; + } write_lock(&ctx->queuelock); @@ -91,16 +98,26 @@ int msm_submitqueue_create(struct msm_file_private *ctx, u32 prio, u32 flags, return 0; } -int msm_submitqueue_init(struct msm_file_private *ctx) +int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx) { + struct msm_drm_private *priv = drm->dev_private; + int default_prio; + if (!ctx) return 0; + /* + * Select priority 2 as the "default priority" unless nr_rings is less + * than 2 and then pick the lowest pirority + */ + default_prio = priv->gpu ? + clamp_t(uint32_t, 2, 0, priv->gpu->nr_rings - 1) : 0; + INIT_LIST_HEAD(&ctx->submitqueues); rwlock_init(&ctx->queuelock); - return msm_submitqueue_create(ctx, 2, 0, NULL); + return msm_submitqueue_create(drm, ctx, default_prio, 0, NULL); } int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id) diff --git a/include/dt-bindings/msm/msm-bus-ids.h b/include/dt-bindings/msm/msm-bus-ids.h new file mode 100644 index 000000000000..a75d304473d5 --- /dev/null +++ b/include/dt-bindings/msm/msm-bus-ids.h @@ -0,0 +1,887 @@ +/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only 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. + */ + +#ifndef __MSM_BUS_IDS_H +#define __MSM_BUS_IDS_H + +/* Aggregation types */ +#define AGG_SCHEME_NONE 0 +#define AGG_SCHEME_LEG 1 +#define AGG_SCHEME_1 2 + +/* Topology related enums */ +#define MSM_BUS_FAB_DEFAULT 0 +#define MSM_BUS_FAB_APPSS 0 +#define MSM_BUS_FAB_SYSTEM 1024 +#define MSM_BUS_FAB_MMSS 2048 +#define MSM_BUS_FAB_SYSTEM_FPB 3072 +#define MSM_BUS_FAB_CPSS_FPB 4096 + +#define MSM_BUS_FAB_BIMC 0 +#define MSM_BUS_FAB_SYS_NOC 1024 +#define MSM_BUS_FAB_MMSS_NOC 2048 +#define MSM_BUS_FAB_OCMEM_NOC 3072 +#define MSM_BUS_FAB_PERIPH_NOC 4096 +#define MSM_BUS_FAB_CONFIG_NOC 5120 +#define MSM_BUS_FAB_OCMEM_VNOC 6144 +#define MSM_BUS_FAB_MMSS_AHB 2049 +#define MSM_BUS_FAB_A0_NOC 6145 +#define MSM_BUS_FAB_A1_NOC 6146 +#define MSM_BUS_FAB_A2_NOC 6147 +#define MSM_BUS_FAB_GNOC 6148 +#define MSM_BUS_FAB_CR_VIRT 6149 + +#define MSM_BUS_MASTER_FIRST 1 +#define MSM_BUS_MASTER_AMPSS_M0 1 +#define MSM_BUS_MASTER_AMPSS_M1 2 +#define MSM_BUS_APPSS_MASTER_FAB_MMSS 3 +#define MSM_BUS_APPSS_MASTER_FAB_SYSTEM 4 +#define MSM_BUS_SYSTEM_MASTER_FAB_APPSS 5 +#define MSM_BUS_MASTER_SPS 6 +#define MSM_BUS_MASTER_ADM_PORT0 7 +#define MSM_BUS_MASTER_ADM_PORT1 8 +#define MSM_BUS_SYSTEM_MASTER_ADM1_PORT0 9 +#define MSM_BUS_MASTER_ADM1_PORT1 10 +#define MSM_BUS_MASTER_LPASS_PROC 11 +#define MSM_BUS_MASTER_MSS_PROCI 12 +#define MSM_BUS_MASTER_MSS_PROCD 13 +#define MSM_BUS_MASTER_MSS_MDM_PORT0 14 +#define MSM_BUS_MASTER_LPASS 15 +#define MSM_BUS_SYSTEM_MASTER_CPSS_FPB 16 +#define MSM_BUS_SYSTEM_MASTER_SYSTEM_FPB 17 +#define MSM_BUS_SYSTEM_MASTER_MMSS_FPB 18 +#define MSM_BUS_MASTER_ADM1_CI 19 +#define MSM_BUS_MASTER_ADM0_CI 20 +#define MSM_BUS_MASTER_MSS_MDM_PORT1 21 +#define MSM_BUS_MASTER_MDP_PORT0 22 +#define MSM_BUS_MASTER_MDP_PORT1 23 +#define MSM_BUS_MMSS_MASTER_ADM1_PORT0 24 +#define MSM_BUS_MASTER_ROTATOR 25 +#define MSM_BUS_MASTER_GRAPHICS_3D 26 +#define MSM_BUS_MASTER_JPEG_DEC 27 +#define MSM_BUS_MASTER_GRAPHICS_2D_CORE0 28 +#define MSM_BUS_MASTER_VFE 29 +#define MSM_BUS_MASTER_VFE0 MSM_BUS_MASTER_VFE +#define MSM_BUS_MASTER_VPE 30 +#define MSM_BUS_MASTER_JPEG_ENC 31 +#define MSM_BUS_MASTER_GRAPHICS_2D_CORE1 32 +#define MSM_BUS_MMSS_MASTER_APPS_FAB 33 +#define MSM_BUS_MASTER_HD_CODEC_PORT0 34 +#define MSM_BUS_MASTER_HD_CODEC_PORT1 35 +#define MSM_BUS_MASTER_SPDM 36 +#define MSM_BUS_MASTER_RPM 37 +#define MSM_BUS_MASTER_MSS 38 +#define MSM_BUS_MASTER_RIVA 39 +#define MSM_BUS_MASTER_SNOC_VMEM 40 +#define MSM_BUS_MASTER_MSS_SW_PROC 41 +#define MSM_BUS_MASTER_MSS_FW_PROC 42 +#define MSM_BUS_MASTER_HMSS 43 +#define MSM_BUS_MASTER_GSS_NAV 44 +#define MSM_BUS_MASTER_PCIE 45 +#define MSM_BUS_MASTER_SATA 46 +#define MSM_BUS_MASTER_CRYPTO 47 +#define MSM_BUS_MASTER_VIDEO_CAP 48 +#define MSM_BUS_MASTER_GRAPHICS_3D_PORT1 49 +#define MSM_BUS_MASTER_VIDEO_ENC 50 +#define MSM_BUS_MASTER_VIDEO_DEC 51 +#define MSM_BUS_MASTER_LPASS_AHB 52 +#define MSM_BUS_MASTER_QDSS_BAM 53 +#define MSM_BUS_MASTER_SNOC_CFG 54 +#define MSM_BUS_MASTER_CRYPTO_CORE0 55 +#define MSM_BUS_MASTER_CRYPTO_CORE1 56 +#define MSM_BUS_MASTER_MSS_NAV 57 +#define MSM_BUS_MASTER_OCMEM_DMA 58 +#define MSM_BUS_MASTER_WCSS 59 +#define MSM_BUS_MASTER_QDSS_ETR 60 +#define MSM_BUS_MASTER_USB3 61 +#define MSM_BUS_MASTER_JPEG 62 +#define MSM_BUS_MASTER_VIDEO_P0 63 +#define MSM_BUS_MASTER_VIDEO_P1 64 +#define MSM_BUS_MASTER_MSS_PROC 65 +#define MSM_BUS_MASTER_JPEG_OCMEM 66 +#define MSM_BUS_MASTER_MDP_OCMEM 67 +#define MSM_BUS_MASTER_VIDEO_P0_OCMEM 68 +#define MSM_BUS_MASTER_VIDEO_P1_OCMEM 69 +#define MSM_BUS_MASTER_VFE_OCMEM 70 +#define MSM_BUS_MASTER_CNOC_ONOC_CFG 71 +#define MSM_BUS_MASTER_RPM_INST 72 +#define MSM_BUS_MASTER_RPM_DATA 73 +#define MSM_BUS_MASTER_RPM_SYS 74 +#define MSM_BUS_MASTER_DEHR 75 +#define MSM_BUS_MASTER_QDSS_DAP 76 +#define MSM_BUS_MASTER_TIC 77 +#define MSM_BUS_MASTER_SDCC_1 78 +#define MSM_BUS_MASTER_SDCC_3 79 +#define MSM_BUS_MASTER_SDCC_4 80 +#define MSM_BUS_MASTER_SDCC_2 81 +#define MSM_BUS_MASTER_TSIF 82 +#define MSM_BUS_MASTER_BAM_DMA 83 +#define MSM_BUS_MASTER_BLSP_2 84 +#define MSM_BUS_MASTER_USB_HSIC 85 +#define MSM_BUS_MASTER_BLSP_1 86 +#define MSM_BUS_MASTER_USB_HS 87 +#define MSM_BUS_MASTER_PNOC_CFG 88 +#define MSM_BUS_MASTER_V_OCMEM_GFX3D 89 +#define MSM_BUS_MASTER_IPA 90 +#define MSM_BUS_MASTER_QPIC 91 +#define MSM_BUS_MASTER_MDPE 92 +#define MSM_BUS_MASTER_USB_HS2 93 +#define MSM_BUS_MASTER_VPU 94 +#define MSM_BUS_MASTER_UFS 95 +#define MSM_BUS_MASTER_BCAST 96 +#define MSM_BUS_MASTER_CRYPTO_CORE2 97 +#define MSM_BUS_MASTER_EMAC 98 +#define MSM_BUS_MASTER_VPU_1 99 +#define MSM_BUS_MASTER_PCIE_1 100 +#define MSM_BUS_MASTER_USB3_1 101 +#define MSM_BUS_MASTER_CNOC_MNOC_MMSS_CFG 102 +#define MSM_BUS_MASTER_CNOC_MNOC_CFG 103 +#define MSM_BUS_MASTER_TCU_0 104 +#define MSM_BUS_MASTER_TCU_1 105 +#define MSM_BUS_MASTER_CPP 106 +#define MSM_BUS_MASTER_AUDIO 107 +#define MSM_BUS_MASTER_PCIE_2 108 +#define MSM_BUS_MASTER_VFE1 109 +#define MSM_BUS_MASTER_XM_USB_HS1 110 +#define MSM_BUS_MASTER_PCNOC_BIMC_1 111 +#define MSM_BUS_MASTER_BIMC_PCNOC 112 +#define MSM_BUS_MASTER_XI_USB_HSIC 113 +#define MSM_BUS_MASTER_SGMII 114 +#define MSM_BUS_SPMI_FETCHER 115 +#define MSM_BUS_MASTER_GNOC_BIMC 116 +#define MSM_BUS_MASTER_CRVIRT_A2NOC 117 +#define MSM_BUS_MASTER_CNOC_A2NOC 118 +#define MSM_BUS_MASTER_WLAN 119 +#define MSM_BUS_MASTER_MSS_CE 120 +#define MSM_BUS_MASTER_CDSP_PROC 121 +#define MSM_BUS_MASTER_GNOC_SNOC 122 +#define MSM_BUS_MASTER_PIMEM 123 +#define MSM_BUS_MASTER_MASTER_LAST 124 + +#define MSM_BUS_SYSTEM_FPB_MASTER_SYSTEM MSM_BUS_SYSTEM_MASTER_SYSTEM_FPB +#define MSM_BUS_CPSS_FPB_MASTER_SYSTEM MSM_BUS_SYSTEM_MASTER_CPSS_FPB + +#define MSM_BUS_SNOC_MM_INT_0 10000 +#define MSM_BUS_SNOC_MM_INT_1 10001 +#define MSM_BUS_SNOC_MM_INT_2 10002 +#define MSM_BUS_SNOC_MM_INT_BIMC 10003 +#define MSM_BUS_SNOC_INT_0 10004 +#define MSM_BUS_SNOC_INT_1 10005 +#define MSM_BUS_SNOC_INT_BIMC 10006 +#define MSM_BUS_SNOC_BIMC_0_MAS 10007 +#define MSM_BUS_SNOC_BIMC_1_MAS 10008 +#define MSM_BUS_SNOC_QDSS_INT 10009 +#define MSM_BUS_PNOC_SNOC_MAS 10010 +#define MSM_BUS_PNOC_SNOC_SLV 10011 +#define MSM_BUS_PNOC_INT_0 10012 +#define MSM_BUS_PNOC_INT_1 10013 +#define MSM_BUS_PNOC_M_0 10014 +#define MSM_BUS_PNOC_M_1 10015 +#define MSM_BUS_BIMC_SNOC_MAS 10016 +#define MSM_BUS_BIMC_SNOC_SLV 10017 +#define MSM_BUS_PNOC_SLV_0 10018 +#define MSM_BUS_PNOC_SLV_1 10019 +#define MSM_BUS_PNOC_SLV_2 10020 +#define MSM_BUS_PNOC_SLV_3 10021 +#define MSM_BUS_PNOC_SLV_4 10022 +#define MSM_BUS_PNOC_SLV_8 10023 +#define MSM_BUS_PNOC_SLV_9 10024 +#define MSM_BUS_SNOC_BIMC_0_SLV 10025 +#define MSM_BUS_SNOC_BIMC_1_SLV 10026 +#define MSM_BUS_MNOC_BIMC_MAS 10027 +#define MSM_BUS_MNOC_BIMC_SLV 10028 +#define MSM_BUS_BIMC_MNOC_MAS 10029 +#define MSM_BUS_BIMC_MNOC_SLV 10030 +#define MSM_BUS_SNOC_BIMC_MAS 10031 +#define MSM_BUS_SNOC_BIMC_SLV 10032 +#define MSM_BUS_CNOC_SNOC_MAS 10033 +#define MSM_BUS_CNOC_SNOC_SLV 10034 +#define MSM_BUS_SNOC_CNOC_MAS 10035 +#define MSM_BUS_SNOC_CNOC_SLV 10036 +#define MSM_BUS_OVNOC_SNOC_MAS 10037 +#define MSM_BUS_OVNOC_SNOC_SLV 10038 +#define MSM_BUS_SNOC_OVNOC_MAS 10039 +#define MSM_BUS_SNOC_OVNOC_SLV 10040 +#define MSM_BUS_SNOC_PNOC_MAS 10041 +#define MSM_BUS_SNOC_PNOC_SLV 10042 +#define MSM_BUS_BIMC_INT_APPS_EBI 10043 +#define MSM_BUS_BIMC_INT_APPS_SNOC 10044 +#define MSM_BUS_SNOC_BIMC_2_MAS 10045 +#define MSM_BUS_SNOC_BIMC_2_SLV 10046 +#define MSM_BUS_PNOC_SLV_5 10047 +#define MSM_BUS_PNOC_SLV_7 10048 +#define MSM_BUS_PNOC_INT_2 10049 +#define MSM_BUS_PNOC_INT_3 10050 +#define MSM_BUS_PNOC_INT_4 10051 +#define MSM_BUS_PNOC_INT_5 10052 +#define MSM_BUS_PNOC_INT_6 10053 +#define MSM_BUS_PNOC_INT_7 10054 +#define MSM_BUS_BIMC_SNOC_1_MAS 10055 +#define MSM_BUS_BIMC_SNOC_1_SLV 10056 +#define MSM_BUS_PNOC_A1NOC_MAS 10057 +#define MSM_BUS_PNOC_A1NOC_SLV 10058 +#define MSM_BUS_CNOC_A1NOC_MAS 10059 +#define MSM_BUS_A0NOC_SNOC_MAS 10060 +#define MSM_BUS_A0NOC_SNOC_SLV 10061 +#define MSM_BUS_A1NOC_SNOC_SLV 10062 +#define MSM_BUS_A1NOC_SNOC_MAS 10063 +#define MSM_BUS_A2NOC_SNOC_MAS 10064 +#define MSM_BUS_A2NOC_SNOC_SLV 10065 +#define MSM_BUS_SNOC_INT_2 10066 +#define MSM_BUS_A0NOC_QDSS_INT 10067 +#define MSM_BUS_INT_LAST 10068 + +#define MSM_BUS_INT_TEST_ID 20000 +#define MSM_BUS_INT_TEST_LAST 20050 + +#define MSM_BUS_SLAVE_FIRST 512 +#define MSM_BUS_SLAVE_EBI_CH0 512 +#define MSM_BUS_SLAVE_EBI_CH1 513 +#define MSM_BUS_SLAVE_AMPSS_L2 514 +#define MSM_BUS_APPSS_SLAVE_FAB_MMSS 515 +#define MSM_BUS_APPSS_SLAVE_FAB_SYSTEM 516 +#define MSM_BUS_SYSTEM_SLAVE_FAB_APPS 517 +#define MSM_BUS_SLAVE_SPS 518 +#define MSM_BUS_SLAVE_SYSTEM_IMEM 519 +#define MSM_BUS_SLAVE_AMPSS 520 +#define MSM_BUS_SLAVE_MSS 521 +#define MSM_BUS_SLAVE_LPASS 522 +#define MSM_BUS_SYSTEM_SLAVE_CPSS_FPB 523 +#define MSM_BUS_SYSTEM_SLAVE_SYSTEM_FPB 524 +#define MSM_BUS_SYSTEM_SLAVE_MMSS_FPB 525 +#define MSM_BUS_SLAVE_CORESIGHT 526 +#define MSM_BUS_SLAVE_RIVA 527 +#define MSM_BUS_SLAVE_SMI 528 +#define MSM_BUS_MMSS_SLAVE_FAB_APPS 529 +#define MSM_BUS_MMSS_SLAVE_FAB_APPS_1 530 +#define MSM_BUS_SLAVE_MM_IMEM 531 +#define MSM_BUS_SLAVE_CRYPTO 532 +#define MSM_BUS_SLAVE_SPDM 533 +#define MSM_BUS_SLAVE_RPM 534 +#define MSM_BUS_SLAVE_RPM_MSG_RAM 535 +#define MSM_BUS_SLAVE_MPM 536 +#define MSM_BUS_SLAVE_PMIC1_SSBI1_A 537 +#define MSM_BUS_SLAVE_PMIC1_SSBI1_B 538 +#define MSM_BUS_SLAVE_PMIC1_SSBI1_C 539 +#define MSM_BUS_SLAVE_PMIC2_SSBI2_A 540 +#define MSM_BUS_SLAVE_PMIC2_SSBI2_B 541 +#define MSM_BUS_SLAVE_GSBI1_UART 542 +#define MSM_BUS_SLAVE_GSBI2_UART 543 +#define MSM_BUS_SLAVE_GSBI3_UART 544 +#define MSM_BUS_SLAVE_GSBI4_UART 545 +#define MSM_BUS_SLAVE_GSBI5_UART 546 +#define MSM_BUS_SLAVE_GSBI6_UART 547 +#define MSM_BUS_SLAVE_GSBI7_UART 548 +#define MSM_BUS_SLAVE_GSBI8_UART 549 +#define MSM_BUS_SLAVE_GSBI9_UART 550 +#define MSM_BUS_SLAVE_GSBI10_UART 551 +#define MSM_BUS_SLAVE_GSBI11_UART 552 +#define MSM_BUS_SLAVE_GSBI12_UART 553 +#define MSM_BUS_SLAVE_GSBI1_QUP 554 +#define MSM_BUS_SLAVE_GSBI2_QUP 555 +#define MSM_BUS_SLAVE_GSBI3_QUP 556 +#define MSM_BUS_SLAVE_GSBI4_QUP 557 +#define MSM_BUS_SLAVE_GSBI5_QUP 558 +#define MSM_BUS_SLAVE_GSBI6_QUP 559 +#define MSM_BUS_SLAVE_GSBI7_QUP 560 +#define MSM_BUS_SLAVE_GSBI8_QUP 561 +#define MSM_BUS_SLAVE_GSBI9_QUP 562 +#define MSM_BUS_SLAVE_GSBI10_QUP 563 +#define MSM_BUS_SLAVE_GSBI11_QUP 564 +#define MSM_BUS_SLAVE_GSBI12_QUP 565 +#define MSM_BUS_SLAVE_EBI2_NAND 566 +#define MSM_BUS_SLAVE_EBI2_CS0 567 +#define MSM_BUS_SLAVE_EBI2_CS1 568 +#define MSM_BUS_SLAVE_EBI2_CS2 569 +#define MSM_BUS_SLAVE_EBI2_CS3 570 +#define MSM_BUS_SLAVE_EBI2_CS4 571 +#define MSM_BUS_SLAVE_EBI2_CS5 572 +#define MSM_BUS_SLAVE_USB_FS1 573 +#define MSM_BUS_SLAVE_USB_FS2 574 +#define MSM_BUS_SLAVE_TSIF 575 +#define MSM_BUS_SLAVE_MSM_TSSC 576 +#define MSM_BUS_SLAVE_MSM_PDM 577 +#define MSM_BUS_SLAVE_MSM_DIMEM 578 +#define MSM_BUS_SLAVE_MSM_TCSR 579 +#define MSM_BUS_SLAVE_MSM_PRNG 580 +#define MSM_BUS_SLAVE_GSS 581 +#define MSM_BUS_SLAVE_SATA 582 +#define MSM_BUS_SLAVE_USB3 583 +#define MSM_BUS_SLAVE_WCSS 584 +#define MSM_BUS_SLAVE_OCIMEM 585 +#define MSM_BUS_SLAVE_SNOC_OCMEM 586 +#define MSM_BUS_SLAVE_SERVICE_SNOC 587 +#define MSM_BUS_SLAVE_QDSS_STM 588 +#define MSM_BUS_SLAVE_CAMERA_CFG 589 +#define MSM_BUS_SLAVE_DISPLAY_CFG 590 +#define MSM_BUS_SLAVE_OCMEM_CFG 591 +#define MSM_BUS_SLAVE_CPR_CFG 592 +#define MSM_BUS_SLAVE_CPR_XPU_CFG 593 +#define MSM_BUS_SLAVE_MISC_CFG 594 +#define MSM_BUS_SLAVE_MISC_XPU_CFG 595 +#define MSM_BUS_SLAVE_VENUS_CFG 596 +#define MSM_BUS_SLAVE_MISC_VENUS_CFG 597 +#define MSM_BUS_SLAVE_GRAPHICS_3D_CFG 598 +#define MSM_BUS_SLAVE_MMSS_CLK_CFG 599 +#define MSM_BUS_SLAVE_MMSS_CLK_XPU_CFG 600 +#define MSM_BUS_SLAVE_MNOC_MPU_CFG 601 +#define MSM_BUS_SLAVE_ONOC_MPU_CFG 602 +#define MSM_BUS_SLAVE_SERVICE_MNOC 603 +#define MSM_BUS_SLAVE_OCMEM 604 +#define MSM_BUS_SLAVE_SERVICE_ONOC 605 +#define MSM_BUS_SLAVE_SDCC_1 606 +#define MSM_BUS_SLAVE_SDCC_3 607 +#define MSM_BUS_SLAVE_SDCC_2 608 +#define MSM_BUS_SLAVE_SDCC_4 609 +#define MSM_BUS_SLAVE_BAM_DMA 610 +#define MSM_BUS_SLAVE_BLSP_2 611 +#define MSM_BUS_SLAVE_USB_HSIC 612 +#define MSM_BUS_SLAVE_BLSP_1 613 +#define MSM_BUS_SLAVE_USB_HS 614 +#define MSM_BUS_SLAVE_PDM 615 +#define MSM_BUS_SLAVE_PERIPH_APU_CFG 616 +#define MSM_BUS_SLAVE_PNOC_MPU_CFG 617 +#define MSM_BUS_SLAVE_PRNG 618 +#define MSM_BUS_SLAVE_SERVICE_PNOC 619 +#define MSM_BUS_SLAVE_CLK_CTL 620 +#define MSM_BUS_SLAVE_CNOC_MSS 621 +#define MSM_BUS_SLAVE_SECURITY 622 +#define MSM_BUS_SLAVE_TCSR 623 +#define MSM_BUS_SLAVE_TLMM 624 +#define MSM_BUS_SLAVE_CRYPTO_0_CFG 625 +#define MSM_BUS_SLAVE_CRYPTO_1_CFG 626 +#define MSM_BUS_SLAVE_IMEM_CFG 627 +#define MSM_BUS_SLAVE_MESSAGE_RAM 628 +#define MSM_BUS_SLAVE_BIMC_CFG 629 +#define MSM_BUS_SLAVE_BOOT_ROM 630 +#define MSM_BUS_SLAVE_CNOC_MNOC_MMSS_CFG 631 +#define MSM_BUS_SLAVE_PMIC_ARB 632 +#define MSM_BUS_SLAVE_SPDM_WRAPPER 633 +#define MSM_BUS_SLAVE_DEHR_CFG 634 +#define MSM_BUS_SLAVE_QDSS_CFG 635 +#define MSM_BUS_SLAVE_RBCPR_CFG 636 +#define MSM_BUS_SLAVE_RBCPR_QDSS_APU_CFG 637 +#define MSM_BUS_SLAVE_SNOC_MPU_CFG 638 +#define MSM_BUS_SLAVE_CNOC_ONOC_CFG 639 +#define MSM_BUS_SLAVE_CNOC_MNOC_CFG 640 +#define MSM_BUS_SLAVE_PNOC_CFG 641 +#define MSM_BUS_SLAVE_SNOC_CFG 642 +#define MSM_BUS_SLAVE_EBI1_DLL_CFG 643 +#define MSM_BUS_SLAVE_PHY_APU_CFG 644 +#define MSM_BUS_SLAVE_EBI1_PHY_CFG 645 +#define MSM_BUS_SLAVE_SERVICE_CNOC 646 +#define MSM_BUS_SLAVE_IPS_CFG 647 +#define MSM_BUS_SLAVE_QPIC 648 +#define MSM_BUS_SLAVE_DSI_CFG 649 +#define MSM_BUS_SLAVE_UFS_CFG 650 +#define MSM_BUS_SLAVE_RBCPR_CX_CFG 651 +#define MSM_BUS_SLAVE_RBCPR_MX_CFG 652 +#define MSM_BUS_SLAVE_PCIE_CFG 653 +#define MSM_BUS_SLAVE_USB_PHYS_CFG 654 +#define MSM_BUS_SLAVE_VIDEO_CAP_CFG 655 +#define MSM_BUS_SLAVE_AVSYNC_CFG 656 +#define MSM_BUS_SLAVE_CRYPTO_2_CFG 657 +#define MSM_BUS_SLAVE_VPU_CFG 658 +#define MSM_BUS_SLAVE_BCAST_CFG 659 +#define MSM_BUS_SLAVE_KLM_CFG 660 +#define MSM_BUS_SLAVE_GENI_IR_CFG 661 +#define MSM_BUS_SLAVE_OCMEM_GFX 662 +#define MSM_BUS_SLAVE_CATS_128 663 +#define MSM_BUS_SLAVE_OCMEM_64 664 +#define MSM_BUS_SLAVE_PCIE_0 665 +#define MSM_BUS_SLAVE_PCIE_1 666 +#define MSM_BUS_SLAVE_PCIE_0_CFG 667 +#define MSM_BUS_SLAVE_PCIE_1_CFG 668 +#define MSM_BUS_SLAVE_SRVC_MNOC 669 +#define MSM_BUS_SLAVE_USB_HS2 670 +#define MSM_BUS_SLAVE_AUDIO 671 +#define MSM_BUS_SLAVE_TCU 672 +#define MSM_BUS_SLAVE_APPSS 673 +#define MSM_BUS_SLAVE_PCIE_PARF 674 +#define MSM_BUS_SLAVE_USB3_PHY_CFG 675 +#define MSM_BUS_SLAVE_IPA_CFG 676 +#define MSM_BUS_SLAVE_A0NOC_SNOC 677 +#define MSM_BUS_SLAVE_A1NOC_SNOC 678 +#define MSM_BUS_SLAVE_A2NOC_SNOC 679 +#define MSM_BUS_SLAVE_HMSS_L3 680 +#define MSM_BUS_SLAVE_PIMEM_CFG 681 +#define MSM_BUS_SLAVE_DCC_CFG 682 +#define MSM_BUS_SLAVE_QDSS_RBCPR_APU_CFG 683 +#define MSM_BUS_SLAVE_PCIE_2_CFG 684 +#define MSM_BUS_SLAVE_PCIE20_AHB2PHY 685 +#define MSM_BUS_SLAVE_A0NOC_CFG 686 +#define MSM_BUS_SLAVE_A1NOC_CFG 687 +#define MSM_BUS_SLAVE_A2NOC_CFG 688 +#define MSM_BUS_SLAVE_A1NOC_MPU_CFG 689 +#define MSM_BUS_SLAVE_A2NOC_MPU_CFG 690 +#define MSM_BUS_SLAVE_A0NOC_SMMU_CFG 691 +#define MSM_BUS_SLAVE_A1NOC_SMMU_CFG 692 +#define MSM_BUS_SLAVE_A2NOC_SMMU_CFG 693 +#define MSM_BUS_SLAVE_LPASS_SMMU_CFG 694 +#define MSM_BUS_SLAVE_MMAGIC_CFG 695 +#define MSM_BUS_SLAVE_VENUS_THROTTLE_CFG 696 +#define MSM_BUS_SLAVE_SSC_CFG 697 +#define MSM_BUS_SLAVE_DSA_CFG 698 +#define MSM_BUS_SLAVE_DSA_MPU_CFG 699 +#define MSM_BUS_SLAVE_DISPLAY_THROTTLE_CFG 700 +#define MSM_BUS_SLAVE_SMMU_CPP_CFG 701 +#define MSM_BUS_SLAVE_SMMU_JPEG_CFG 702 +#define MSM_BUS_SLAVE_SMMU_MDP_CFG 703 +#define MSM_BUS_SLAVE_SMMU_ROTATOR_CFG 704 +#define MSM_BUS_SLAVE_SMMU_VENUS_CFG 705 +#define MSM_BUS_SLAVE_SMMU_VFE_CFG 706 +#define MSM_BUS_SLAVE_A0NOC_MPU_CFG 707 +#define MSM_BUS_SLAVE_VMEM_CFG 708 +#define MSM_BUS_SLAVE_CAMERA_THROTTLE_CFG 709 +#define MSM_BUS_SLAVE_VMEM 710 +#define MSM_BUS_SLAVE_AHB2PHY 711 +#define MSM_BUS_SLAVE_PIMEM 712 +#define MSM_BUS_SLAVE_SNOC_VMEM 713 +#define MSM_BUS_SLAVE_PCIE_2 714 +#define MSM_BUS_SLAVE_RBCPR_MX 715 +#define MSM_BUS_SLAVE_RBCPR_CX 716 +#define MSM_BUS_SLAVE_BIMC_PCNOC 717 +#define MSM_BUS_SLAVE_PCNOC_BIMC_1 718 +#define MSM_BUS_SLAVE_SGMII 719 +#define MSM_BUS_SLAVE_SPMI_FETCHER 720 +#define MSM_BUS_PNOC_SLV_6 721 +#define MSM_BUS_SLAVE_MMSS_SMMU_CFG 722 +#define MSM_BUS_SLAVE_WLAN 723 +#define MSM_BUS_SLAVE_CRVIRT_A2NOC 724 +#define MSM_BUS_SLAVE_CNOC_A2NOC 725 +#define MSM_BUS_SLAVE_GLM 726 +#define MSM_BUS_SLAVE_GNOC_BIMC 727 +#define MSM_BUS_SLAVE_GNOC_SNOC 728 +#define MSM_BUS_SLAVE_QM_CFG 729 +#define MSM_BUS_SLAVE_TLMM_EAST 730 +#define MSM_BUS_SLAVE_TLMM_NORTH 731 +#define MSM_BUS_SLAVE_TLMM_WEST 732 +#define MSM_BUS_SLAVE_SKL 733 +#define MSM_BUS_SLAVE_LPASS_TCM 734 +#define MSM_BUS_SLAVE_TLMM_SOUTH 735 +#define MSM_BUS_SLAVE_TLMM_CENTER 736 +#define MSM_BUS_MSS_NAV_CE_MPU_CFG 737 +#define MSM_BUS_SLAVE_A2NOC_THROTTLE_CFG 738 +#define MSM_BUS_SLAVE_CDSP 739 +#define MSM_BUS_SLAVE_CDSP_SMMU_CFG 740 +#define MSM_BUS_SLAVE_LPASS_MPU_CFG 741 +#define MSM_BUS_SLAVE_CSI_PHY_CFG 742 +#define MSM_BUS_SLAVE_LAST 743 + +#define MSM_BUS_SYSTEM_FPB_SLAVE_SYSTEM MSM_BUS_SYSTEM_SLAVE_SYSTEM_FPB +#define MSM_BUS_CPSS_FPB_SLAVE_SYSTEM MSM_BUS_SYSTEM_SLAVE_CPSS_FPB + +/* + * ID's used in RPM messages + */ +#define ICBID_MASTER_APPSS_PROC 0 +#define ICBID_MASTER_MSS_PROC 1 +#define ICBID_MASTER_MNOC_BIMC 2 +#define ICBID_MASTER_SNOC_BIMC 3 +#define ICBID_MASTER_SNOC_BIMC_0 ICBID_MASTER_SNOC_BIMC +#define ICBID_MASTER_CNOC_MNOC_MMSS_CFG 4 +#define ICBID_MASTER_CNOC_MNOC_CFG 5 +#define ICBID_MASTER_GFX3D 6 +#define ICBID_MASTER_JPEG 7 +#define ICBID_MASTER_MDP 8 +#define ICBID_MASTER_MDP0 ICBID_MASTER_MDP +#define ICBID_MASTER_MDPS ICBID_MASTER_MDP +#define ICBID_MASTER_VIDEO 9 +#define ICBID_MASTER_VIDEO_P0 ICBID_MASTER_VIDEO +#define ICBID_MASTER_VIDEO_P1 10 +#define ICBID_MASTER_VFE 11 +#define ICBID_MASTER_VFE0 ICBID_MASTER_VFE +#define ICBID_MASTER_CNOC_ONOC_CFG 12 +#define ICBID_MASTER_JPEG_OCMEM 13 +#define ICBID_MASTER_MDP_OCMEM 14 +#define ICBID_MASTER_VIDEO_P0_OCMEM 15 +#define ICBID_MASTER_VIDEO_P1_OCMEM 16 +#define ICBID_MASTER_VFE_OCMEM 17 +#define ICBID_MASTER_LPASS_AHB 18 +#define ICBID_MASTER_QDSS_BAM 19 +#define ICBID_MASTER_SNOC_CFG 20 +#define ICBID_MASTER_BIMC_SNOC 21 +#define ICBID_MASTER_BIMC_SNOC_0 ICBID_MASTER_BIMC_SNOC +#define ICBID_MASTER_CNOC_SNOC 22 +#define ICBID_MASTER_CRYPTO 23 +#define ICBID_MASTER_CRYPTO_CORE0 ICBID_MASTER_CRYPTO +#define ICBID_MASTER_CRYPTO_CORE1 24 +#define ICBID_MASTER_LPASS_PROC 25 +#define ICBID_MASTER_MSS 26 +#define ICBID_MASTER_MSS_NAV 27 +#define ICBID_MASTER_OCMEM_DMA 28 +#define ICBID_MASTER_PNOC_SNOC 29 +#define ICBID_MASTER_WCSS 30 +#define ICBID_MASTER_QDSS_ETR 31 +#define ICBID_MASTER_USB3 32 +#define ICBID_MASTER_USB3_0 ICBID_MASTER_USB3 +#define ICBID_MASTER_SDCC_1 33 +#define ICBID_MASTER_SDCC_3 34 +#define ICBID_MASTER_SDCC_2 35 +#define ICBID_MASTER_SDCC_4 36 +#define ICBID_MASTER_TSIF 37 +#define ICBID_MASTER_BAM_DMA 38 +#define ICBID_MASTER_BLSP_2 39 +#define ICBID_MASTER_USB_HSIC 40 +#define ICBID_MASTER_BLSP_1 41 +#define ICBID_MASTER_USB_HS 42 +#define ICBID_MASTER_USB_HS1 ICBID_MASTER_USB_HS +#define ICBID_MASTER_PNOC_CFG 43 +#define ICBID_MASTER_SNOC_PNOC 44 +#define ICBID_MASTER_RPM_INST 45 +#define ICBID_MASTER_RPM_DATA 46 +#define ICBID_MASTER_RPM_SYS 47 +#define ICBID_MASTER_DEHR 48 +#define ICBID_MASTER_QDSS_DAP 49 +#define ICBID_MASTER_SPDM 50 +#define ICBID_MASTER_TIC 51 +#define ICBID_MASTER_SNOC_CNOC 52 +#define ICBID_MASTER_GFX3D_OCMEM 53 +#define ICBID_MASTER_GFX3D_GMEM ICBID_MASTER_GFX3D_OCMEM +#define ICBID_MASTER_OVIRT_SNOC 54 +#define ICBID_MASTER_SNOC_OVIRT 55 +#define ICBID_MASTER_SNOC_GVIRT ICBID_MASTER_SNOC_OVIRT +#define ICBID_MASTER_ONOC_OVIRT 56 +#define ICBID_MASTER_USB_HS2 57 +#define ICBID_MASTER_QPIC 58 +#define ICBID_MASTER_IPA 59 +#define ICBID_MASTER_DSI 60 +#define ICBID_MASTER_MDP1 61 +#define ICBID_MASTER_MDPE ICBID_MASTER_MDP1 +#define ICBID_MASTER_VPU_PROC 62 +#define ICBID_MASTER_VPU 63 +#define ICBID_MASTER_VPU0 ICBID_MASTER_VPU +#define ICBID_MASTER_CRYPTO_CORE2 64 +#define ICBID_MASTER_PCIE_0 65 +#define ICBID_MASTER_PCIE_1 66 +#define ICBID_MASTER_SATA 67 +#define ICBID_MASTER_UFS 68 +#define ICBID_MASTER_USB3_1 69 +#define ICBID_MASTER_VIDEO_OCMEM 70 +#define ICBID_MASTER_VPU1 71 +#define ICBID_MASTER_VCAP 72 +#define ICBID_MASTER_EMAC 73 +#define ICBID_MASTER_BCAST 74 +#define ICBID_MASTER_MMSS_PROC 75 +#define ICBID_MASTER_SNOC_BIMC_1 76 +#define ICBID_MASTER_SNOC_PCNOC 77 +#define ICBID_MASTER_AUDIO 78 +#define ICBID_MASTER_MM_INT_0 79 +#define ICBID_MASTER_MM_INT_1 80 +#define ICBID_MASTER_MM_INT_2 81 +#define ICBID_MASTER_MM_INT_BIMC 82 +#define ICBID_MASTER_MSS_INT 83 +#define ICBID_MASTER_PCNOC_CFG 84 +#define ICBID_MASTER_PCNOC_INT_0 85 +#define ICBID_MASTER_PCNOC_INT_1 86 +#define ICBID_MASTER_PCNOC_M_0 87 +#define ICBID_MASTER_PCNOC_M_1 88 +#define ICBID_MASTER_PCNOC_S_0 89 +#define ICBID_MASTER_PCNOC_S_1 90 +#define ICBID_MASTER_PCNOC_S_2 91 +#define ICBID_MASTER_PCNOC_S_3 92 +#define ICBID_MASTER_PCNOC_S_4 93 +#define ICBID_MASTER_PCNOC_S_6 94 +#define ICBID_MASTER_PCNOC_S_7 95 +#define ICBID_MASTER_PCNOC_S_8 96 +#define ICBID_MASTER_PCNOC_S_9 97 +#define ICBID_MASTER_QDSS_INT 98 +#define ICBID_MASTER_SNOC_INT_0 99 +#define ICBID_MASTER_SNOC_INT_1 100 +#define ICBID_MASTER_SNOC_INT_BIMC 101 +#define ICBID_MASTER_TCU_0 102 +#define ICBID_MASTER_TCU_1 103 +#define ICBID_MASTER_BIMC_INT_0 104 +#define ICBID_MASTER_BIMC_INT_1 105 +#define ICBID_MASTER_CAMERA 106 +#define ICBID_MASTER_RICA 107 +#define ICBID_MASTER_SNOC_BIMC_2 108 +#define ICBID_MASTER_BIMC_SNOC_1 109 +#define ICBID_MASTER_A0NOC_SNOC 110 +#define ICBID_MASTER_A1NOC_SNOC 111 +#define ICBID_MASTER_A2NOC_SNOC 112 +#define ICBID_MASTER_PIMEM 113 +#define ICBID_MASTER_SNOC_VMEM 114 +#define ICBID_MASTER_CPP 115 +#define ICBID_MASTER_CNOC_A1NOC 116 +#define ICBID_MASTER_PNOC_A1NOC 117 +#define ICBID_MASTER_HMSS 118 +#define ICBID_MASTER_PCIE_2 119 +#define ICBID_MASTER_ROTATOR 120 +#define ICBID_MASTER_VENUS_VMEM 121 +#define ICBID_MASTER_DCC 122 +#define ICBID_MASTER_MCDMA 123 +#define ICBID_MASTER_PCNOC_INT_2 124 +#define ICBID_MASTER_PCNOC_INT_3 125 +#define ICBID_MASTER_PCNOC_INT_4 126 +#define ICBID_MASTER_PCNOC_INT_5 127 +#define ICBID_MASTER_PCNOC_INT_6 128 +#define ICBID_MASTER_PCNOC_S_5 129 +#define ICBID_MASTER_SENSORS_AHB 130 +#define ICBID_MASTER_SENSORS_PROC 131 +#define ICBID_MASTER_QSPI 132 +#define ICBID_MASTER_VFE1 133 +#define ICBID_MASTER_SNOC_INT_2 134 +#define ICBID_MASTER_SMMNOC_BIMC 135 +#define ICBID_MASTER_CRVIRT_A1NOC 136 +#define ICBID_MASTER_XM_USB_HS1 137 +#define ICBID_MASTER_XI_USB_HS1 138 +#define ICBID_MASTER_PCNOC_BIMC_1 139 +#define ICBID_MASTER_BIMC_PCNOC 140 +#define ICBID_MASTER_XI_HSIC 141 +#define ICBID_MASTER_SGMII 142 +#define ICBID_MASTER_SPMI_FETCHER 143 +#define ICBID_MASTER_GNOC_BIMC 144 +#define ICBID_MASTER_CRVIRT_A2NOC 145 +#define ICBID_MASTER_CNOC_A2NOC 146 +#define ICBID_MASTER_WLAN 147 +#define ICBID_MASTER_MSS_CE 148 +#define ICBID_MASTER_CDSP_PROC 149 +#define ICBID_MASTER_GNOC_SNOC 150 + +#define ICBID_SLAVE_EBI1 0 +#define ICBID_SLAVE_APPSS_L2 1 +#define ICBID_SLAVE_BIMC_SNOC 2 +#define ICBID_SLAVE_BIMC_SNOC_0 ICBID_SLAVE_BIMC_SNOC +#define ICBID_SLAVE_CAMERA_CFG 3 +#define ICBID_SLAVE_DISPLAY_CFG 4 +#define ICBID_SLAVE_OCMEM_CFG 5 +#define ICBID_SLAVE_CPR_CFG 6 +#define ICBID_SLAVE_CPR_XPU_CFG 7 +#define ICBID_SLAVE_MISC_CFG 8 +#define ICBID_SLAVE_MISC_XPU_CFG 9 +#define ICBID_SLAVE_VENUS_CFG 10 +#define ICBID_SLAVE_GFX3D_CFG 11 +#define ICBID_SLAVE_MMSS_CLK_CFG 12 +#define ICBID_SLAVE_MMSS_CLK_XPU_CFG 13 +#define ICBID_SLAVE_MNOC_MPU_CFG 14 +#define ICBID_SLAVE_ONOC_MPU_CFG 15 +#define ICBID_SLAVE_MNOC_BIMC 16 +#define ICBID_SLAVE_SERVICE_MNOC 17 +#define ICBID_SLAVE_OCMEM 18 +#define ICBID_SLAVE_GMEM ICBID_SLAVE_OCMEM +#define ICBID_SLAVE_SERVICE_ONOC 19 +#define ICBID_SLAVE_APPSS 20 +#define ICBID_SLAVE_LPASS 21 +#define ICBID_SLAVE_USB3 22 +#define ICBID_SLAVE_USB3_0 ICBID_SLAVE_USB3 +#define ICBID_SLAVE_WCSS 23 +#define ICBID_SLAVE_SNOC_BIMC 24 +#define ICBID_SLAVE_SNOC_BIMC_0 ICBID_SLAVE_SNOC_BIMC +#define ICBID_SLAVE_SNOC_CNOC 25 +#define ICBID_SLAVE_IMEM 26 +#define ICBID_SLAVE_OCIMEM ICBID_SLAVE_IMEM +#define ICBID_SLAVE_SNOC_OVIRT 27 +#define ICBID_SLAVE_SNOC_GVIRT ICBID_SLAVE_SNOC_OVIRT +#define ICBID_SLAVE_SNOC_PNOC 28 +#define ICBID_SLAVE_SNOC_PCNOC ICBID_SLAVE_SNOC_PNOC +#define ICBID_SLAVE_SERVICE_SNOC 29 +#define ICBID_SLAVE_QDSS_STM 30 +#define ICBID_SLAVE_SDCC_1 31 +#define ICBID_SLAVE_SDCC_3 32 +#define ICBID_SLAVE_SDCC_2 33 +#define ICBID_SLAVE_SDCC_4 34 +#define ICBID_SLAVE_TSIF 35 +#define ICBID_SLAVE_BAM_DMA 36 +#define ICBID_SLAVE_BLSP_2 37 +#define ICBID_SLAVE_USB_HSIC 38 +#define ICBID_SLAVE_BLSP_1 39 +#define ICBID_SLAVE_USB_HS 40 +#define ICBID_SLAVE_USB_HS1 ICBID_SLAVE_USB_HS +#define ICBID_SLAVE_PDM 41 +#define ICBID_SLAVE_PERIPH_APU_CFG 42 +#define ICBID_SLAVE_PNOC_MPU_CFG 43 +#define ICBID_SLAVE_PRNG 44 +#define ICBID_SLAVE_PNOC_SNOC 45 +#define ICBID_SLAVE_PCNOC_SNOC ICBID_SLAVE_PNOC_SNOC +#define ICBID_SLAVE_SERVICE_PNOC 46 +#define ICBID_SLAVE_CLK_CTL 47 +#define ICBID_SLAVE_CNOC_MSS 48 +#define ICBID_SLAVE_PCNOC_MSS ICBID_SLAVE_CNOC_MSS +#define ICBID_SLAVE_SECURITY 49 +#define ICBID_SLAVE_TCSR 50 +#define ICBID_SLAVE_TLMM 51 +#define ICBID_SLAVE_CRYPTO_0_CFG 52 +#define ICBID_SLAVE_CRYPTO_1_CFG 53 +#define ICBID_SLAVE_IMEM_CFG 54 +#define ICBID_SLAVE_MESSAGE_RAM 55 +#define ICBID_SLAVE_BIMC_CFG 56 +#define ICBID_SLAVE_BOOT_ROM 57 +#define ICBID_SLAVE_CNOC_MNOC_MMSS_CFG 58 +#define ICBID_SLAVE_PMIC_ARB 59 +#define ICBID_SLAVE_SPDM_WRAPPER 60 +#define ICBID_SLAVE_DEHR_CFG 61 +#define ICBID_SLAVE_MPM 62 +#define ICBID_SLAVE_QDSS_CFG 63 +#define ICBID_SLAVE_RBCPR_CFG 64 +#define ICBID_SLAVE_RBCPR_CX_CFG ICBID_SLAVE_RBCPR_CFG +#define ICBID_SLAVE_RBCPR_QDSS_APU_CFG 65 +#define ICBID_SLAVE_CNOC_MNOC_CFG 66 +#define ICBID_SLAVE_SNOC_MPU_CFG 67 +#define ICBID_SLAVE_CNOC_ONOC_CFG 68 +#define ICBID_SLAVE_PNOC_CFG 69 +#define ICBID_SLAVE_SNOC_CFG 70 +#define ICBID_SLAVE_EBI1_DLL_CFG 71 +#define ICBID_SLAVE_PHY_APU_CFG 72 +#define ICBID_SLAVE_EBI1_PHY_CFG 73 +#define ICBID_SLAVE_RPM 74 +#define ICBID_SLAVE_CNOC_SNOC 75 +#define ICBID_SLAVE_SERVICE_CNOC 76 +#define ICBID_SLAVE_OVIRT_SNOC 77 +#define ICBID_SLAVE_OVIRT_OCMEM 78 +#define ICBID_SLAVE_USB_HS2 79 +#define ICBID_SLAVE_QPIC 80 +#define ICBID_SLAVE_IPS_CFG 81 +#define ICBID_SLAVE_DSI_CFG 82 +#define ICBID_SLAVE_USB3_1 83 +#define ICBID_SLAVE_PCIE_0 84 +#define ICBID_SLAVE_PCIE_1 85 +#define ICBID_SLAVE_PSS_SMMU_CFG 86 +#define ICBID_SLAVE_CRYPTO_2_CFG 87 +#define ICBID_SLAVE_PCIE_0_CFG 88 +#define ICBID_SLAVE_PCIE_1_CFG 89 +#define ICBID_SLAVE_SATA_CFG 90 +#define ICBID_SLAVE_SPSS_GENI_IR 91 +#define ICBID_SLAVE_UFS_CFG 92 +#define ICBID_SLAVE_AVSYNC_CFG 93 +#define ICBID_SLAVE_VPU_CFG 94 +#define ICBID_SLAVE_USB_PHY_CFG 95 +#define ICBID_SLAVE_RBCPR_MX_CFG 96 +#define ICBID_SLAVE_PCIE_PARF 97 +#define ICBID_SLAVE_VCAP_CFG 98 +#define ICBID_SLAVE_EMAC_CFG 99 +#define ICBID_SLAVE_BCAST_CFG 100 +#define ICBID_SLAVE_KLM_CFG 101 +#define ICBID_SLAVE_DISPLAY_PWM 102 +#define ICBID_SLAVE_GENI 103 +#define ICBID_SLAVE_SNOC_BIMC_1 104 +#define ICBID_SLAVE_AUDIO 105 +#define ICBID_SLAVE_CATS_0 106 +#define ICBID_SLAVE_CATS_1 107 +#define ICBID_SLAVE_MM_INT_0 108 +#define ICBID_SLAVE_MM_INT_1 109 +#define ICBID_SLAVE_MM_INT_2 110 +#define ICBID_SLAVE_MM_INT_BIMC 111 +#define ICBID_SLAVE_MMU_MODEM_XPU_CFG 112 +#define ICBID_SLAVE_MSS_INT 113 +#define ICBID_SLAVE_PCNOC_INT_0 114 +#define ICBID_SLAVE_PCNOC_INT_1 115 +#define ICBID_SLAVE_PCNOC_M_0 116 +#define ICBID_SLAVE_PCNOC_M_1 117 +#define ICBID_SLAVE_PCNOC_S_0 118 +#define ICBID_SLAVE_PCNOC_S_1 119 +#define ICBID_SLAVE_PCNOC_S_2 120 +#define ICBID_SLAVE_PCNOC_S_3 121 +#define ICBID_SLAVE_PCNOC_S_4 122 +#define ICBID_SLAVE_PCNOC_S_6 123 +#define ICBID_SLAVE_PCNOC_S_7 124 +#define ICBID_SLAVE_PCNOC_S_8 125 +#define ICBID_SLAVE_PCNOC_S_9 126 +#define ICBID_SLAVE_PRNG_XPU_CFG 127 +#define ICBID_SLAVE_QDSS_INT 128 +#define ICBID_SLAVE_RPM_XPU_CFG 129 +#define ICBID_SLAVE_SNOC_INT_0 130 +#define ICBID_SLAVE_SNOC_INT_1 131 +#define ICBID_SLAVE_SNOC_INT_BIMC 132 +#define ICBID_SLAVE_TCU 133 +#define ICBID_SLAVE_BIMC_INT_0 134 +#define ICBID_SLAVE_BIMC_INT_1 135 +#define ICBID_SLAVE_RICA_CFG 136 +#define ICBID_SLAVE_SNOC_BIMC_2 137 +#define ICBID_SLAVE_BIMC_SNOC_1 138 +#define ICBID_SLAVE_PNOC_A1NOC 139 +#define ICBID_SLAVE_SNOC_VMEM 140 +#define ICBID_SLAVE_A0NOC_SNOC 141 +#define ICBID_SLAVE_A1NOC_SNOC 142 +#define ICBID_SLAVE_A2NOC_SNOC 143 +#define ICBID_SLAVE_A0NOC_CFG 144 +#define ICBID_SLAVE_A0NOC_MPU_CFG 145 +#define ICBID_SLAVE_A0NOC_SMMU_CFG 146 +#define ICBID_SLAVE_A1NOC_CFG 147 +#define ICBID_SLAVE_A1NOC_MPU_CFG 148 +#define ICBID_SLAVE_A1NOC_SMMU_CFG 149 +#define ICBID_SLAVE_A2NOC_CFG 150 +#define ICBID_SLAVE_A2NOC_MPU_CFG 151 +#define ICBID_SLAVE_A2NOC_SMMU_CFG 152 +#define ICBID_SLAVE_AHB2PHY 153 +#define ICBID_SLAVE_CAMERA_THROTTLE_CFG 154 +#define ICBID_SLAVE_DCC_CFG 155 +#define ICBID_SLAVE_DISPLAY_THROTTLE_CFG 156 +#define ICBID_SLAVE_DSA_CFG 157 +#define ICBID_SLAVE_DSA_MPU_CFG 158 +#define ICBID_SLAVE_SSC_MPU_CFG 159 +#define ICBID_SLAVE_HMSS_L3 160 +#define ICBID_SLAVE_LPASS_SMMU_CFG 161 +#define ICBID_SLAVE_MMAGIC_CFG 162 +#define ICBID_SLAVE_PCIE20_AHB2PHY 163 +#define ICBID_SLAVE_PCIE_2 164 +#define ICBID_SLAVE_PCIE_2_CFG 165 +#define ICBID_SLAVE_PIMEM 166 +#define ICBID_SLAVE_PIMEM_CFG 167 +#define ICBID_SLAVE_QDSS_RBCPR_APU_CFG 168 +#define ICBID_SLAVE_RBCPR_CX 169 +#define ICBID_SLAVE_RBCPR_MX 170 +#define ICBID_SLAVE_SMMU_CPP_CFG 171 +#define ICBID_SLAVE_SMMU_JPEG_CFG 172 +#define ICBID_SLAVE_SMMU_MDP_CFG 173 +#define ICBID_SLAVE_SMMU_ROTATOR_CFG 174 +#define ICBID_SLAVE_SMMU_VENUS_CFG 175 +#define ICBID_SLAVE_SMMU_VFE_CFG 176 +#define ICBID_SLAVE_SSC_CFG 177 +#define ICBID_SLAVE_VENUS_THROTTLE_CFG 178 +#define ICBID_SLAVE_VMEM 179 +#define ICBID_SLAVE_VMEM_CFG 180 +#define ICBID_SLAVE_QDSS_MPU_CFG 181 +#define ICBID_SLAVE_USB3_PHY_CFG 182 +#define ICBID_SLAVE_IPA_CFG 183 +#define ICBID_SLAVE_PCNOC_INT_2 184 +#define ICBID_SLAVE_PCNOC_INT_3 185 +#define ICBID_SLAVE_PCNOC_INT_4 186 +#define ICBID_SLAVE_PCNOC_INT_5 187 +#define ICBID_SLAVE_PCNOC_INT_6 188 +#define ICBID_SLAVE_PCNOC_S_5 189 +#define ICBID_SLAVE_QSPI 190 +#define ICBID_SLAVE_A1NOC_MS_MPU_CFG 191 +#define ICBID_SLAVE_A2NOC_MS_MPU_CFG 192 +#define ICBID_SLAVE_MODEM_Q6_SMMU_CFG 193 +#define ICBID_SLAVE_MSS_MPU_CFG 194 +#define ICBID_SLAVE_MSS_PROC_MS_MPU_CFG 195 +#define ICBID_SLAVE_SKL 196 +#define ICBID_SLAVE_SNOC_INT_2 197 +#define ICBID_SLAVE_SMMNOC_BIMC 198 +#define ICBID_SLAVE_CRVIRT_A1NOC 199 +#define ICBID_SLAVE_SGMII 200 +#define ICBID_SLAVE_QHS4_APPS 201 +#define ICBID_SLAVE_BIMC_PCNOC 202 +#define ICBID_SLAVE_PCNOC_BIMC_1 203 +#define ICBID_SLAVE_SPMI_FETCHER 204 +#define ICBID_SLAVE_MMSS_SMMU_CFG 205 +#define ICBID_SLAVE_WLAN 206 +#define ICBID_SLAVE_CRVIRT_A2NOC 207 +#define ICBID_SLAVE_CNOC_A2NOC 208 +#define ICBID_SLAVE_GLM 209 +#define ICBID_SLAVE_GNOC_BIMC 210 +#define ICBID_SLAVE_GNOC_SNOC 211 +#define ICBID_SLAVE_QM_CFG 212 +#define ICBID_SLAVE_TLMM_EAST 213 +#define ICBID_SLAVE_TLMM_NORTH 214 +#define ICBID_SLAVE_TLMM_WEST 215 +#define ICBID_SLAVE_LPASS_TCM 216 +#define ICBID_SLAVE_TLMM_SOUTH 217 +#define ICBID_SLAVE_TLMM_CENTER 218 +#define ICBID_SLAVE_MSS_NAV_CE_MPU_CFG 219 +#define ICBID_SLAVE_A2NOC_THROTTLE_CFG 220 +#define ICBID_SLAVE_CDSP 221 +#define ICBID_SLAVE_CDSP_SMMU_CFG 222 +#define ICBID_SLAVE_LPASS_MPU_CFG 223 +#define ICBID_SLAVE_CSI_PHY_CFG 224 +#endif diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index fee44322a69c..12d7619afa22 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -232,6 +232,7 @@ struct drm_msm_wait_fence { __u32 fence; /* in */ __u32 pad; struct drm_msm_timespec timeout; /* in */ + __u32 queueid; /* in, submitqueue id */ }; /* madvise provides a way to tell the kernel in case a buffers contents -- cgit v1.2.3-59-g8ed1b From a6e29a0eea3ccbf6fb8a908a3fc3e931f3ba2ae4 Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Fri, 20 Oct 2017 11:06:58 -0600 Subject: drm/msm: Add a parameter query for the number of ringbuffers In order to manage ringbuffer priority to its fullest userspace should know how many ringbuffers it has to work with. Add a parameter to return the number of active rings. Signed-off-by: Jordan Crouse Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/adreno/adreno_gpu.c | 3 +++ include/uapi/drm/msm_drm.h | 1 + 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index fd0fb0568dd3..4e9caf97b20f 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -57,6 +57,9 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) return ret; } return -EINVAL; + case MSM_PARAM_NR_RINGS: + *value = gpu->nr_rings; + return 0; default: DBG("%s: invalid param: %u", gpu->name, param); return -EINVAL; diff --git a/include/uapi/drm/msm_drm.h b/include/uapi/drm/msm_drm.h index 12d7619afa22..bbbaffad772d 100644 --- a/include/uapi/drm/msm_drm.h +++ b/include/uapi/drm/msm_drm.h @@ -73,6 +73,7 @@ struct drm_msm_timespec { #define MSM_PARAM_MAX_FREQ 0x04 #define MSM_PARAM_TIMESTAMP 0x05 #define MSM_PARAM_GMEM_BASE 0x06 +#define MSM_PARAM_NR_RINGS 0x07 struct drm_msm_param { __u32 pipe; /* in, MSM_PIPE_x */ -- cgit v1.2.3-59-g8ed1b From 7e86a365a8319970e002f83c73701a86d95a69e6 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Fri, 27 Oct 2017 19:35:30 -0400 Subject: drm/amdkfd: increase limit of signal events to 4096 per process Signed-off-by: Oded Gabbay Reviewed-by: Ben Goz Signed-off-by: Felix Kuehling --- include/uapi/linux/kfd_ioctl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/kfd_ioctl.h b/include/uapi/linux/kfd_ioctl.h index 26283fefdf5f..731d0df722e3 100644 --- a/include/uapi/linux/kfd_ioctl.h +++ b/include/uapi/linux/kfd_ioctl.h @@ -169,7 +169,7 @@ struct kfd_ioctl_dbg_wave_control_args { #define KFD_IOC_WAIT_RESULT_TIMEOUT 1 #define KFD_IOC_WAIT_RESULT_FAIL 2 -#define KFD_SIGNAL_EVENT_LIMIT 256 +#define KFD_SIGNAL_EVENT_LIMIT 4096 struct kfd_ioctl_create_event_args { __u64 event_page_offset; /* from KFD */ -- cgit v1.2.3-59-g8ed1b From ec36e416f06f6a8659281053fdc46ce484ad2211 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:21 -0700 Subject: tcp: Namespace-ify sysctl_tcp_nometrics_save Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_metrics.c | 4 +--- 4 files changed, 9 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index f4622e28db3a..9606e2ea1f14 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -142,6 +142,7 @@ struct netns_ipv4 { int sysctl_tcp_app_win; int sysctl_tcp_adv_win_scale; int sysctl_tcp_frto; + int sysctl_tcp_nometrics_save; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 18f047501f53..6ab7fa4154b2 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -247,7 +247,6 @@ extern int sysctl_tcp_max_orphans; extern long sysctl_tcp_mem[3]; extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; -extern int sysctl_tcp_nometrics_save; extern int sysctl_tcp_moderate_rcvbuf; extern int sysctl_tcp_tso_win_divisor; extern int sysctl_tcp_workaround_signed_windows; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index f1bcb9b7e082..b742a5e26a9d 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -451,13 +451,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_no_metrics_save", - .data = &sysctl_tcp_nometrics_save, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "tcp_moderate_rcvbuf", .data = &sysctl_tcp_moderate_rcvbuf, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_no_metrics_save", + .data = &init_net.ipv4.sysctl_tcp_nometrics_save, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 0ab78abc811b..0507b56b6d4b 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -20,8 +20,6 @@ #include #include -int sysctl_tcp_nometrics_save __read_mostly; - static struct tcp_metrics_block *__tcp_get_metrics(const struct inetpeer_addr *saddr, const struct inetpeer_addr *daddr, struct net *net, unsigned int hash); @@ -330,7 +328,7 @@ void tcp_update_metrics(struct sock *sk) int m; sk_dst_confirm(sk); - if (sysctl_tcp_nometrics_save || !dst) + if (net->ipv4.sysctl_tcp_nometrics_save || !dst) return; rcu_read_lock(); -- cgit v1.2.3-59-g8ed1b From 4540c0cf98b8892a642d2453eec20ae3eb5696fb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:22 -0700 Subject: tcp: Namespace-ify sysctl_tcp_moderate_rcvbuf Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 5 ++--- net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 9606e2ea1f14..4458a54fe3f4 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -143,6 +143,7 @@ struct netns_ipv4 { int sysctl_tcp_adv_win_scale; int sysctl_tcp_frto; int sysctl_tcp_nometrics_save; + int sysctl_tcp_moderate_rcvbuf; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 6ab7fa4154b2..f954e74578ff 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -247,7 +247,6 @@ extern int sysctl_tcp_max_orphans; extern long sysctl_tcp_mem[3]; extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; -extern int sysctl_tcp_moderate_rcvbuf; extern int sysctl_tcp_tso_win_divisor; extern int sysctl_tcp_workaround_signed_windows; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index b742a5e26a9d..2ebe87fd1169 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -451,13 +451,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_moderate_rcvbuf", - .data = &sysctl_tcp_moderate_rcvbuf, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "tcp_tso_win_divisor", .data = &sysctl_tcp_tso_win_divisor, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "tcp_moderate_rcvbuf", + .data = &init_net.ipv4.sysctl_tcp_moderate_rcvbuf, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 90d76f1c8f96..ce481325115f 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -84,7 +84,6 @@ int sysctl_tcp_challenge_ack_limit = 1000; int sysctl_tcp_max_orphans __read_mostly = NR_FILE; int sysctl_tcp_min_rtt_wlen __read_mostly = 300; -int sysctl_tcp_moderate_rcvbuf __read_mostly = 1; int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ @@ -411,7 +410,7 @@ static void tcp_fixup_rcvbuf(struct sock *sk) /* Dynamic Right Sizing (DRS) has 2 to 3 RTT latency * Allow enough cushion so that sender is not limited by our window */ - if (sysctl_tcp_moderate_rcvbuf) + if (sock_net(sk)->ipv4.sysctl_tcp_moderate_rcvbuf) rcvmem <<= 2; if (sk->sk_rcvbuf < rcvmem) @@ -602,7 +601,7 @@ void tcp_rcv_space_adjust(struct sock *sk) * */ - if (sysctl_tcp_moderate_rcvbuf && + if (sock_net(sk)->ipv4.sysctl_tcp_moderate_rcvbuf && !(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) { int rcvwin, rcvmem, rcvbuf; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 49757c758211..27f376b90913 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2493,6 +2493,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_app_win = 31; net->ipv4.sysctl_tcp_adv_win_scale = 1; net->ipv4.sysctl_tcp_frto = 2; + net->ipv4.sysctl_tcp_moderate_rcvbuf = 1; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From d06a99045837d3f4d5431793c4c390b0daf2a08d Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:23 -0700 Subject: tcp: Namespace-ify sysctl_tcp_tso_win_divisor Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_ipv4.c | 5 +++++ net/ipv4/tcp_output.c | 8 +------- 5 files changed, 14 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 4458a54fe3f4..60bccda046db 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -144,6 +144,7 @@ struct netns_ipv4 { int sysctl_tcp_frto; int sysctl_tcp_nometrics_save; int sysctl_tcp_moderate_rcvbuf; + int sysctl_tcp_tso_win_divisor; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index f954e74578ff..ed0828dc82f1 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -247,7 +247,6 @@ extern int sysctl_tcp_max_orphans; extern long sysctl_tcp_mem[3]; extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; -extern int sysctl_tcp_tso_win_divisor; extern int sysctl_tcp_workaround_signed_windows; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 2ebe87fd1169..a053cacb8290 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -451,13 +451,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "tcp_tso_win_divisor", - .data = &sysctl_tcp_tso_win_divisor, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "tcp_congestion_control", .mode = 0644, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "tcp_tso_win_divisor", + .data = &init_net.ipv4.sysctl_tcp_tso_win_divisor, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 27f376b90913..284ff16148df 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2494,6 +2494,11 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_adv_win_scale = 1; net->ipv4.sysctl_tcp_frto = 2; net->ipv4.sysctl_tcp_moderate_rcvbuf = 1; + /* This limits the percentage of the congestion window which we + * will allow a single TSO frame to consume. Building TSO frames + * which are too large can cause TCP streams to be bursty. + */ + net->ipv4.sysctl_tcp_tso_win_divisor = 3; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 55a0aa4b96df..60df3ab52166 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -53,12 +53,6 @@ int sysctl_tcp_workaround_signed_windows __read_mostly = 0; /* Default TSQ limit of four TSO segments */ int sysctl_tcp_limit_output_bytes __read_mostly = 262144; -/* This limits the percentage of the congestion window which we - * will allow a single TSO frame to consume. Building TSO frames - * which are too large can cause TCP streams to be bursty. - */ -int sysctl_tcp_tso_win_divisor __read_mostly = 3; - static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp); @@ -1988,7 +1982,7 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb, if ((skb != tcp_write_queue_tail(sk)) && (limit >= skb->len)) goto send_now; - win_divisor = ACCESS_ONCE(sysctl_tcp_tso_win_divisor); + win_divisor = ACCESS_ONCE(sock_net(sk)->ipv4.sysctl_tcp_tso_win_divisor); if (win_divisor) { u32 chunk = min(tp->snd_wnd, tp->snd_cwnd * tp->mss_cache); -- cgit v1.2.3-59-g8ed1b From ceef9ab6be7234f9e49f79769e0da88d1dccfcc7 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:24 -0700 Subject: tcp: Namespace-ify sysctl_tcp_workaround_signed_windows Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 4 ++-- net/ipv4/syncookies.c | 2 +- net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_minisocks.c | 2 +- net/ipv4/tcp_output.c | 14 +++++--------- net/ipv6/syncookies.c | 2 +- 7 files changed, 18 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 60bccda046db..e74c7c1b0d18 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -145,6 +145,7 @@ struct netns_ipv4 { int sysctl_tcp_nometrics_save; int sysctl_tcp_moderate_rcvbuf; int sysctl_tcp_tso_win_divisor; + int sysctl_tcp_workaround_signed_windows; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index ed0828dc82f1..e338e16178dd 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -247,7 +247,6 @@ extern int sysctl_tcp_max_orphans; extern long sysctl_tcp_mem[3]; extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; -extern int sysctl_tcp_workaround_signed_windows; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ @@ -1302,7 +1301,8 @@ static inline void tcp_slow_start_after_idle_check(struct sock *sk) } /* Determine a window scaling and initial window to offer. */ -void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, +void tcp_select_initial_window(const struct sock *sk, int __space, + __u32 mss, __u32 *rcv_wnd, __u32 *window_clamp, int wscale_ok, __u8 *rcv_wscale, __u32 init_rcv_wnd); diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 77cf32a80952..fda37f2862c9 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -385,7 +385,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) /* Try to redo what tcp_v4_send_synack did. */ req->rsk_window_clamp = tp->window_clamp ? :dst_metric(&rt->dst, RTAX_WINDOW); - tcp_select_initial_window(tcp_full_space(sk), req->mss, + tcp_select_initial_window(sk, tcp_full_space(sk), req->mss, &req->rsk_rcv_wnd, &req->rsk_window_clamp, ireq->wscale_ok, &rcv_wscale, dst_metric(&rt->dst, RTAX_INITRWND)); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index a053cacb8290..3ae9012a4979 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -457,13 +457,6 @@ static struct ctl_table ipv4_table[] = { .maxlen = TCP_CA_NAME_MAX, .proc_handler = proc_tcp_congestion_control, }, - { - .procname = "tcp_workaround_signed_windows", - .data = &sysctl_tcp_workaround_signed_windows, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_limit_output_bytes", .data = &sysctl_tcp_limit_output_bytes, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "tcp_workaround_signed_windows", + .data = &init_net.ipv4.sysctl_tcp_workaround_signed_windows, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 3270ab8416ce..3c65c1a3f944 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -369,7 +369,7 @@ void tcp_openreq_init_rwin(struct request_sock *req, full_space = rcv_wnd * mss; /* tcp_full_space because it is guaranteed to be the first packet */ - tcp_select_initial_window(full_space, + tcp_select_initial_window(sk_listener, full_space, mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0), &req->rsk_rcv_wnd, &req->rsk_window_clamp, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 60df3ab52166..5bbed67c27e9 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -45,11 +45,6 @@ #include -/* People can turn this on to work with those rare, broken TCPs that - * interpret the window field as a signed quantity. - */ -int sysctl_tcp_workaround_signed_windows __read_mostly = 0; - /* Default TSQ limit of four TSO segments */ int sysctl_tcp_limit_output_bytes __read_mostly = 262144; @@ -196,7 +191,7 @@ u32 tcp_default_init_rwnd(u32 mss) * be a multiple of mss if possible. We assume here that mss >= 1. * This MUST be enforced by all callers. */ -void tcp_select_initial_window(int __space, __u32 mss, +void tcp_select_initial_window(const struct sock *sk, int __space, __u32 mss, __u32 *rcv_wnd, __u32 *window_clamp, int wscale_ok, __u8 *rcv_wscale, __u32 init_rcv_wnd) @@ -220,7 +215,7 @@ void tcp_select_initial_window(int __space, __u32 mss, * which we interpret as a sign the remote TCP is not * misinterpreting the window field as a signed quantity. */ - if (sysctl_tcp_workaround_signed_windows) + if (sock_net(sk)->ipv4.sysctl_tcp_workaround_signed_windows) (*rcv_wnd) = min(space, MAX_TCP_WINDOW); else (*rcv_wnd) = space; @@ -280,7 +275,8 @@ static u16 tcp_select_window(struct sock *sk) /* Make sure we do not exceed the maximum possible * scaled window. */ - if (!tp->rx_opt.rcv_wscale && sysctl_tcp_workaround_signed_windows) + if (!tp->rx_opt.rcv_wscale && + sock_net(sk)->ipv4.sysctl_tcp_workaround_signed_windows) new_win = min(new_win, MAX_TCP_WINDOW); else new_win = min(new_win, (65535U << tp->rx_opt.rcv_wscale)); @@ -3349,7 +3345,7 @@ static void tcp_connect_init(struct sock *sk) if (rcv_wnd == 0) rcv_wnd = dst_metric(dst, RTAX_INITRWND); - tcp_select_initial_window(tcp_full_space(sk), + tcp_select_initial_window(sk, tcp_full_space(sk), tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0), &tp->rcv_wnd, &tp->window_clamp, diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 4e7817abc0b9..e7a3a6b6cf56 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -244,7 +244,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) } req->rsk_window_clamp = tp->window_clamp ? :dst_metric(dst, RTAX_WINDOW); - tcp_select_initial_window(tcp_full_space(sk), req->mss, + tcp_select_initial_window(sk, tcp_full_space(sk), req->mss, &req->rsk_rcv_wnd, &req->rsk_window_clamp, ireq->wscale_ok, &rcv_wscale, dst_metric(dst, RTAX_INITRWND)); -- cgit v1.2.3-59-g8ed1b From 9184d8bb448a3d2c2d9f90f1e2f5de625292e769 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:25 -0700 Subject: tcp: Namespace-ify sysctl_tcp_limit_output_bytes Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_ipv4.c | 2 ++ net/ipv4/tcp_output.c | 6 ++---- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index e74c7c1b0d18..e98f473bab13 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -146,6 +146,7 @@ struct netns_ipv4 { int sysctl_tcp_moderate_rcvbuf; int sysctl_tcp_tso_win_divisor; int sysctl_tcp_workaround_signed_windows; + int sysctl_tcp_limit_output_bytes; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index e338e16178dd..33f9d30a6905 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -250,7 +250,6 @@ extern int sysctl_tcp_rmem[3]; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ -extern int sysctl_tcp_limit_output_bytes; extern int sysctl_tcp_challenge_ack_limit; extern int sysctl_tcp_min_tso_segs; extern int sysctl_tcp_min_rtt_wlen; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 3ae9012a4979..6caf5c40730f 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -457,13 +457,6 @@ static struct ctl_table ipv4_table[] = { .maxlen = TCP_CA_NAME_MAX, .proc_handler = proc_tcp_congestion_control, }, - { - .procname = "tcp_limit_output_bytes", - .data = &sysctl_tcp_limit_output_bytes, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_challenge_ack_limit", .data = &sysctl_tcp_challenge_ack_limit, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_limit_output_bytes", + .data = &init_net.ipv4.sysctl_tcp_limit_output_bytes, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 284ff16148df..713b80261e4f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2499,6 +2499,8 @@ static int __net_init tcp_sk_init(struct net *net) * which are too large can cause TCP streams to be bursty. */ net->ipv4.sysctl_tcp_tso_win_divisor = 3; + /* Default TSQ limit of four TSO segments */ + net->ipv4.sysctl_tcp_limit_output_bytes = 262144; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 5bbed67c27e9..f018892c6a98 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -45,9 +45,6 @@ #include -/* Default TSQ limit of four TSO segments */ -int sysctl_tcp_limit_output_bytes __read_mostly = 262144; - static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, int push_one, gfp_t gfp); @@ -2215,7 +2212,8 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, unsigned int limit; limit = max(2 * skb->truesize, sk->sk_pacing_rate >> 10); - limit = min_t(u32, limit, sysctl_tcp_limit_output_bytes); + limit = min_t(u32, limit, + sock_net(sk)->ipv4.sysctl_tcp_limit_output_bytes); limit <<= factor; if (refcount_read(&sk->sk_wmem_alloc) > limit) { -- cgit v1.2.3-59-g8ed1b From b530b68148301d73775cd27cc136ce4dd5738ae8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:26 -0700 Subject: tcp: Namespace-ify sysctl_tcp_challenge_ack_limit Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 14 ++++++-------- net/ipv4/tcp_ipv4.c | 2 ++ 5 files changed, 16 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index e98f473bab13..e9895d40868e 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -147,6 +147,7 @@ struct netns_ipv4 { int sysctl_tcp_tso_win_divisor; int sysctl_tcp_workaround_signed_windows; int sysctl_tcp_limit_output_bytes; + int sysctl_tcp_challenge_ack_limit; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 33f9d30a6905..afc23596e9aa 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -250,7 +250,6 @@ extern int sysctl_tcp_rmem[3]; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ -extern int sysctl_tcp_challenge_ack_limit; extern int sysctl_tcp_min_tso_segs; extern int sysctl_tcp_min_rtt_wlen; extern int sysctl_tcp_autocorking; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 6caf5c40730f..e28b3b7a7bbc 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -457,13 +457,6 @@ static struct ctl_table ipv4_table[] = { .maxlen = TCP_CA_NAME_MAX, .proc_handler = proc_tcp_congestion_control, }, - { - .procname = "tcp_challenge_ack_limit", - .data = &sysctl_tcp_challenge_ack_limit, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, #ifdef CONFIG_NETLABEL { .procname = "cipso_cache_enable", @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_challenge_ack_limit", + .data = &init_net.ipv4.sysctl_tcp_challenge_ack_limit, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ce481325115f..928048a4e2c5 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -79,9 +79,6 @@ #include #include -/* rfc5961 challenge ack rate limiting */ -int sysctl_tcp_challenge_ack_limit = 1000; - int sysctl_tcp_max_orphans __read_mostly = NR_FILE; int sysctl_tcp_min_rtt_wlen __read_mostly = 300; int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2; @@ -3443,10 +3440,11 @@ static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb) static u32 challenge_timestamp; static unsigned int challenge_count; struct tcp_sock *tp = tcp_sk(sk); + struct net *net = sock_net(sk); u32 count, now; /* First check our per-socket dupack rate limit. */ - if (__tcp_oow_rate_limited(sock_net(sk), + if (__tcp_oow_rate_limited(net, LINUX_MIB_TCPACKSKIPPEDCHALLENGE, &tp->last_oow_ack_time)) return; @@ -3454,16 +3452,16 @@ static void tcp_send_challenge_ack(struct sock *sk, const struct sk_buff *skb) /* Then check host-wide RFC 5961 rate limit. */ now = jiffies / HZ; if (now != challenge_timestamp) { - u32 half = (sysctl_tcp_challenge_ack_limit + 1) >> 1; + u32 ack_limit = net->ipv4.sysctl_tcp_challenge_ack_limit; + u32 half = (ack_limit + 1) >> 1; challenge_timestamp = now; - WRITE_ONCE(challenge_count, half + - prandom_u32_max(sysctl_tcp_challenge_ack_limit)); + WRITE_ONCE(challenge_count, half + prandom_u32_max(ack_limit)); } count = READ_ONCE(challenge_count); if (count > 0) { WRITE_ONCE(challenge_count, count - 1); - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPCHALLENGEACK); + NET_INC_STATS(net, LINUX_MIB_TCPCHALLENGEACK); tcp_send_ack(sk); } } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 713b80261e4f..50ab3a3eced3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2501,6 +2501,8 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_tso_win_divisor = 3; /* Default TSQ limit of four TSO segments */ net->ipv4.sysctl_tcp_limit_output_bytes = 262144; + /* rfc5961 challenge ack rate limiting */ + net->ipv4.sysctl_tcp_challenge_ack_limit = 1000; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From 26e9596e5b8f11025b57b12e7265df649129ab00 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:27 -0700 Subject: tcp: Namespace-ify sysctl_tcp_min_tso_segs Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 18 +++++++++--------- net/ipv4/tcp.c | 2 -- net/ipv4/tcp_ipv4.c | 1 + net/ipv4/tcp_output.c | 3 ++- 6 files changed, 13 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index e9895d40868e..a2da3e19a977 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -148,6 +148,7 @@ struct netns_ipv4 { int sysctl_tcp_workaround_signed_windows; int sysctl_tcp_limit_output_bytes; int sysctl_tcp_challenge_ack_limit; + int sysctl_tcp_min_tso_segs; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index afc23596e9aa..0735303a6575 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -250,7 +250,6 @@ extern int sysctl_tcp_rmem[3]; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ -extern int sysctl_tcp_min_tso_segs; extern int sysctl_tcp_min_rtt_wlen; extern int sysctl_tcp_autocorking; extern int sysctl_tcp_invalid_ratelimit; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index e28b3b7a7bbc..00b4aea3705b 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -499,15 +499,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_allowed_congestion_control, }, - { - .procname = "tcp_min_tso_segs", - .data = &sysctl_tcp_min_tso_segs, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &one, - .extra2 = &gso_max_segs, - }, { .procname = "tcp_pacing_ss_ratio", .data = &sysctl_tcp_pacing_ss_ratio, @@ -1145,6 +1136,15 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_min_tso_segs", + .data = &init_net.ipv4.sysctl_tcp_min_tso_segs, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &one, + .extra2 = &gso_max_segs, + }, { } }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c7c983f0f817..a01c97708d83 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -285,8 +285,6 @@ #include -int sysctl_tcp_min_tso_segs __read_mostly = 2; - int sysctl_tcp_autocorking __read_mostly = 1; struct percpu_counter tcp_orphan_count; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 50ab3a3eced3..6192f26145d3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2503,6 +2503,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_limit_output_bytes = 262144; /* rfc5961 challenge ack rate limiting */ net->ipv4.sysctl_tcp_challenge_ack_limit = 1000; + net->ipv4.sysctl_tcp_min_tso_segs = 2; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index f018892c6a98..aab6e7145013 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -1758,7 +1758,8 @@ static u32 tcp_tso_segs(struct sock *sk, unsigned int mss_now) u32 tso_segs = ca_ops->tso_segs_goal ? ca_ops->tso_segs_goal(sk) : 0; return tso_segs ? : - tcp_tso_autosize(sk, mss_now, sysctl_tcp_min_tso_segs); + tcp_tso_autosize(sk, mss_now, + sock_net(sk)->ipv4.sysctl_tcp_min_tso_segs); } /* Returns the portion of skb which can be sent right away */ -- cgit v1.2.3-59-g8ed1b From bd239704295c66196e6b77c5717ec4aec076ddd5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:28 -0700 Subject: tcp: Namespace-ify sysctl_tcp_min_rtt_wlen Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 3 +-- net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index a2da3e19a977..1a66af8a0d32 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -149,6 +149,7 @@ struct netns_ipv4 { int sysctl_tcp_limit_output_bytes; int sysctl_tcp_challenge_ack_limit; int sysctl_tcp_min_tso_segs; + int sysctl_tcp_min_rtt_wlen; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 0735303a6575..56f50c9a3e6a 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -250,7 +250,6 @@ extern int sysctl_tcp_rmem[3]; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ -extern int sysctl_tcp_min_rtt_wlen; extern int sysctl_tcp_autocorking; extern int sysctl_tcp_invalid_ratelimit; extern int sysctl_tcp_pacing_ss_ratio; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 00b4aea3705b..029692d2e4ae 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -437,13 +437,6 @@ static struct ctl_table ipv4_table[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &one, }, - { - .procname = "tcp_min_rtt_wlen", - .data = &sysctl_tcp_min_rtt_wlen, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, { .procname = "tcp_low_latency", .data = &sysctl_tcp_low_latency, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = &one, .extra2 = &gso_max_segs, }, + { + .procname = "tcp_min_rtt_wlen", + .data = &init_net.ipv4.sysctl_tcp_min_rtt_wlen, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 928048a4e2c5..da1ef666d1f9 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -80,7 +80,6 @@ #include int sysctl_tcp_max_orphans __read_mostly = NR_FILE; -int sysctl_tcp_min_rtt_wlen __read_mostly = 300; int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ @@ -2915,8 +2914,8 @@ static void tcp_fastretrans_alert(struct sock *sk, const int acked, static void tcp_update_rtt_min(struct sock *sk, u32 rtt_us) { + u32 wlen = sock_net(sk)->ipv4.sysctl_tcp_min_rtt_wlen * HZ; struct tcp_sock *tp = tcp_sk(sk); - u32 wlen = sysctl_tcp_min_rtt_wlen * HZ; minmax_running_min(&tp->rtt_min, wlen, tcp_jiffies32, rtt_us ? : jiffies_to_usecs(1)); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 6192f26145d3..ced35af5737a 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2504,6 +2504,7 @@ static int __net_init tcp_sk_init(struct net *net) /* rfc5961 challenge ack rate limiting */ net->ipv4.sysctl_tcp_challenge_ack_limit = 1000; net->ipv4.sysctl_tcp_min_tso_segs = 2; + net->ipv4.sysctl_tcp_min_rtt_wlen = 300; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From 790f00e19f65673c3c169dfc137c09a9236847d5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:29 -0700 Subject: tcp: Namespace-ify sysctl_tcp_autocorking Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 18 +++++++++--------- net/ipv4/tcp.c | 4 +--- net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 12 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 1a66af8a0d32..537830882149 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -150,6 +150,7 @@ struct netns_ipv4 { int sysctl_tcp_challenge_ack_limit; int sysctl_tcp_min_tso_segs; int sysctl_tcp_min_rtt_wlen; + int sysctl_tcp_autocorking; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 56f50c9a3e6a..0268f1025d9d 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -250,7 +250,6 @@ extern int sysctl_tcp_rmem[3]; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ -extern int sysctl_tcp_autocorking; extern int sysctl_tcp_invalid_ratelimit; extern int sysctl_tcp_pacing_ss_ratio; extern int sysctl_tcp_pacing_ca_ratio; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 029692d2e4ae..43a18a317053 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -510,15 +510,6 @@ static struct ctl_table ipv4_table[] = { .extra1 = &zero, .extra2 = &thousand, }, - { - .procname = "tcp_autocorking", - .data = &sysctl_tcp_autocorking, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &one, - }, { .procname = "tcp_invalid_ratelimit", .data = &sysctl_tcp_invalid_ratelimit, @@ -1145,6 +1136,15 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_autocorking", + .data = &init_net.ipv4.sysctl_tcp_autocorking, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &one, + }, { } }; diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index a01c97708d83..a7a0f316eb86 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -285,8 +285,6 @@ #include -int sysctl_tcp_autocorking __read_mostly = 1; - struct percpu_counter tcp_orphan_count; EXPORT_SYMBOL_GPL(tcp_orphan_count); @@ -697,7 +695,7 @@ static bool tcp_should_autocork(struct sock *sk, struct sk_buff *skb, int size_goal) { return skb->len < size_goal && - sysctl_tcp_autocorking && + sock_net(sk)->ipv4.sysctl_tcp_autocorking && skb != tcp_write_queue_head(sk) && refcount_read(&sk->sk_wmem_alloc) > skb->truesize; } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index ced35af5737a..351e3497c8f3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2505,6 +2505,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_challenge_ack_limit = 1000; net->ipv4.sysctl_tcp_min_tso_segs = 2; net->ipv4.sysctl_tcp_min_rtt_wlen = 300; + net->ipv4.sysctl_tcp_autocorking = 1; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From 4170ba6b589ced82da56c7e4f71cc84b2be036d6 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:30 -0700 Subject: tcp: Namespace-ify sysctl_tcp_invalid_ratelimit Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 14 +++++++------- net/ipv4/tcp_input.c | 3 +-- net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 537830882149..e52c2124b32e 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -151,6 +151,7 @@ struct netns_ipv4 { int sysctl_tcp_min_tso_segs; int sysctl_tcp_min_rtt_wlen; int sysctl_tcp_autocorking; + int sysctl_tcp_invalid_ratelimit; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 0268f1025d9d..5869a822ecb1 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -250,7 +250,6 @@ extern int sysctl_tcp_rmem[3]; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ -extern int sysctl_tcp_invalid_ratelimit; extern int sysctl_tcp_pacing_ss_ratio; extern int sysctl_tcp_pacing_ca_ratio; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 43a18a317053..6a9349c27f00 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -510,13 +510,6 @@ static struct ctl_table ipv4_table[] = { .extra1 = &zero, .extra2 = &thousand, }, - { - .procname = "tcp_invalid_ratelimit", - .data = &sysctl_tcp_invalid_ratelimit, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_ms_jiffies, - }, { .procname = "tcp_available_ulp", .maxlen = TCP_ULP_BUF_MAX, @@ -1145,6 +1138,13 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = &zero, .extra2 = &one, }, + { + .procname = "tcp_invalid_ratelimit", + .data = &init_net.ipv4.sysctl_tcp_invalid_ratelimit, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_ms_jiffies, + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index da1ef666d1f9..db4d458d0205 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -80,7 +80,6 @@ #include int sysctl_tcp_max_orphans __read_mostly = NR_FILE; -int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2; #define FLAG_DATA 0x01 /* Incoming frame contained data. */ #define FLAG_WIN_UPDATE 0x02 /* Incoming ACK was a window update. */ @@ -3403,7 +3402,7 @@ static bool __tcp_oow_rate_limited(struct net *net, int mib_idx, if (*last_oow_ack_time) { s32 elapsed = (s32)(tcp_jiffies32 - *last_oow_ack_time); - if (0 <= elapsed && elapsed < sysctl_tcp_invalid_ratelimit) { + if (0 <= elapsed && elapsed < net->ipv4.sysctl_tcp_invalid_ratelimit) { NET_INC_STATS(net, mib_idx); return true; /* rate-limited: don't send yet! */ } diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 351e3497c8f3..6617aae18ba2 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2506,6 +2506,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_min_tso_segs = 2; net->ipv4.sysctl_tcp_min_rtt_wlen = 300; net->ipv4.sysctl_tcp_autocorking = 1; + net->ipv4.sysctl_tcp_invalid_ratelimit = HZ/2; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From 23a7102a2d1068508fa2a0ce593a0df7f8fdc0ac Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:31 -0700 Subject: tcp: Namespace-ify sysctl_tcp_pacing_ss_ratio Also remove an obsolete comment about TCP pacing. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 18 +++++++++--------- net/ipv4/tcp_input.c | 9 +-------- net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 12 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index e52c2124b32e..eb2dcf1cbe61 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -152,6 +152,7 @@ struct netns_ipv4 { int sysctl_tcp_min_rtt_wlen; int sysctl_tcp_autocorking; int sysctl_tcp_invalid_ratelimit; + int sysctl_tcp_pacing_ss_ratio; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 5869a822ecb1..2a5f8261ca03 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -250,7 +250,6 @@ extern int sysctl_tcp_rmem[3]; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ -extern int sysctl_tcp_pacing_ss_ratio; extern int sysctl_tcp_pacing_ca_ratio; extern atomic_long_t tcp_memory_allocated; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 6a9349c27f00..7f0dba852d47 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -492,15 +492,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_allowed_congestion_control, }, - { - .procname = "tcp_pacing_ss_ratio", - .data = &sysctl_tcp_pacing_ss_ratio, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &thousand, - }, { .procname = "tcp_pacing_ca_ratio", .data = &sysctl_tcp_pacing_ca_ratio, @@ -1145,6 +1136,15 @@ static struct ctl_table ipv4_net_table[] = { .mode = 0644, .proc_handler = proc_dointvec_ms_jiffies, }, + { + .procname = "tcp_pacing_ss_ratio", + .data = &init_net.ipv4.sysctl_tcp_pacing_ss_ratio, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &thousand, + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index db4d458d0205..29539d39e61a 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -767,13 +767,6 @@ static void tcp_rtt_estimator(struct sock *sk, long mrtt_us) tp->srtt_us = max(1U, srtt); } -/* Set the sk_pacing_rate to allow proper sizing of TSO packets. - * Note: TCP stack does not yet implement pacing. - * FQ packet scheduler can be used to implement cheap but effective - * TCP pacing, to smooth the burst on large writes when packets - * in flight is significantly lower than cwnd (or rwin) - */ -int sysctl_tcp_pacing_ss_ratio __read_mostly = 200; int sysctl_tcp_pacing_ca_ratio __read_mostly = 120; static void tcp_update_pacing_rate(struct sock *sk) @@ -793,7 +786,7 @@ static void tcp_update_pacing_rate(struct sock *sk) * end of slow start and should slow down. */ if (tp->snd_cwnd < tp->snd_ssthresh / 2) - rate *= sysctl_tcp_pacing_ss_ratio; + rate *= sock_net(sk)->ipv4.sysctl_tcp_pacing_ss_ratio; else rate *= sysctl_tcp_pacing_ca_ratio; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 6617aae18ba2..1d8fc663af51 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2507,6 +2507,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_min_rtt_wlen = 300; net->ipv4.sysctl_tcp_autocorking = 1; net->ipv4.sysctl_tcp_invalid_ratelimit = HZ/2; + net->ipv4.sysctl_tcp_pacing_ss_ratio = 200; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From c26e91f8b9b8e1fd252e07c1f60e50220cd7ebab Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 27 Oct 2017 07:47:32 -0700 Subject: tcp: Namespace-ify sysctl_tcp_pacing_ca_ratio Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/netns/ipv4.h | 1 + include/net/tcp.h | 2 -- net/ipv4/sysctl_net_ipv4.c | 18 +++++++++--------- net/ipv4/tcp_input.c | 4 +--- net/ipv4/tcp_ipv4.c | 1 + 5 files changed, 12 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index eb2dcf1cbe61..141ba82b5efb 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -153,6 +153,7 @@ struct netns_ipv4 { int sysctl_tcp_autocorking; int sysctl_tcp_invalid_ratelimit; int sysctl_tcp_pacing_ss_ratio; + int sysctl_tcp_pacing_ca_ratio; struct inet_timewait_death_row tcp_death_row; int sysctl_max_syn_backlog; int sysctl_tcp_fastopen; diff --git a/include/net/tcp.h b/include/net/tcp.h index 2a5f8261ca03..092d606fcc16 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -250,8 +250,6 @@ extern int sysctl_tcp_rmem[3]; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ -extern int sysctl_tcp_pacing_ca_ratio; - extern atomic_long_t tcp_memory_allocated; extern struct percpu_counter tcp_sockets_allocated; extern unsigned long tcp_memory_pressure; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 7f0dba852d47..4602af6d5358 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -492,15 +492,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_allowed_congestion_control, }, - { - .procname = "tcp_pacing_ca_ratio", - .data = &sysctl_tcp_pacing_ca_ratio, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &zero, - .extra2 = &thousand, - }, { .procname = "tcp_available_ulp", .maxlen = TCP_ULP_BUF_MAX, @@ -1145,6 +1136,15 @@ static struct ctl_table ipv4_net_table[] = { .extra1 = &zero, .extra2 = &thousand, }, + { + .procname = "tcp_pacing_ca_ratio", + .data = &init_net.ipv4.sysctl_tcp_pacing_ca_ratio, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &zero, + .extra2 = &thousand, + }, { } }; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 29539d39e61a..21c358c0cf2e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -767,8 +767,6 @@ static void tcp_rtt_estimator(struct sock *sk, long mrtt_us) tp->srtt_us = max(1U, srtt); } -int sysctl_tcp_pacing_ca_ratio __read_mostly = 120; - static void tcp_update_pacing_rate(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); @@ -788,7 +786,7 @@ static void tcp_update_pacing_rate(struct sock *sk) if (tp->snd_cwnd < tp->snd_ssthresh / 2) rate *= sock_net(sk)->ipv4.sysctl_tcp_pacing_ss_ratio; else - rate *= sysctl_tcp_pacing_ca_ratio; + rate *= sock_net(sk)->ipv4.sysctl_tcp_pacing_ca_ratio; rate *= max(tp->snd_cwnd, tp->packets_out); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 1d8fc663af51..7c1dae6493c3 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -2508,6 +2508,7 @@ static int __net_init tcp_sk_init(struct net *net) net->ipv4.sysctl_tcp_autocorking = 1; net->ipv4.sysctl_tcp_invalid_ratelimit = HZ/2; net->ipv4.sysctl_tcp_pacing_ss_ratio = 200; + net->ipv4.sysctl_tcp_pacing_ca_ratio = 120; net->ipv4.sysctl_tcp_fastopen = TFO_CLIENT_ENABLE; spin_lock_init(&net->ipv4.tcp_fastopen_ctx_lock); -- cgit v1.2.3-59-g8ed1b From 5b52a4c3acf5f4b4854d1c3ddc8be8770330a79c Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Fri, 27 Oct 2017 10:01:39 -0700 Subject: tcp: remove unnecessary include two extra #include are not necessary in tcp.h Remove them. Fixes: 40304b2a1567 ("bpf: BPF support for sock_ops") Signed-off-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/net/tcp.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/net/tcp.h b/include/net/tcp.h index 092d606fcc16..aa1cc90fdc02 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -45,9 +45,6 @@ #include #include - -#include -#include #include extern struct inet_hashinfo tcp_hashinfo; -- cgit v1.2.3-59-g8ed1b From 9b9f2b8bc2ac98d91da714660c53d1cdac999e09 Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Thu, 24 Aug 2017 17:31:01 +0800 Subject: i2c: i2c-smbus: Use threaded irq for smbalert Prior to this commit the smbalert_irq was handling in the hard irq context. This change switch to using a thread irq which avoids the need for the work thread. Using threaded irq also removes the need for the edge_triggered flag as the enabling / disabling of the hard irq for level triggered interrupts will be handled by the irq core. Without this change have an irq connected to something like an i2c gpio resulted in a null ptr deferences. Specifically handle_nested_irq calls the threaded irq handler. There are currently 3 in tree drivers affected by this change. i2c-parport driver calls i2c_handle_smbus_alert in a hard irq context. This driver use edge trigger interrupts which skip the enable / disable calls. But it still need to handle the smbus transaction on a thread. So the work thread is kept for this driver. i2c-parport-light & i2c-thunderx-pcidrv provide the irq number in the setup which will result in the thread irq being used. i2c-parport-light is edge trigger so the enable / disable call was skipped as well. i2c-thunderx-pcidrv is getting the edge / level trigger setting from of data and was setting the flag as required. However the irq core should handle this automatically. Signed-off-by: Phil Reid Reviewed-by: Benjamin Tissoires Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-parport-light.c | 1 - drivers/i2c/busses/i2c-parport.c | 1 - drivers/i2c/busses/i2c-thunderx-pcidrv.c | 6 ----- drivers/i2c/i2c-smbus.c | 41 +++++++++++++------------------- include/linux/i2c-smbus.h | 1 - 5 files changed, 17 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c index faa8fb8f2b8f..fa41ff799533 100644 --- a/drivers/i2c/busses/i2c-parport-light.c +++ b/drivers/i2c/busses/i2c-parport-light.c @@ -123,7 +123,6 @@ static struct i2c_adapter parport_adapter = { /* SMBus alert support */ static struct i2c_smbus_alert_setup alert_data = { - .alert_edge_triggered = 1, }; static struct i2c_client *ara; static struct lineop parport_ctrl_irq = { diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c index a8e54df4aed6..319209a07353 100644 --- a/drivers/i2c/busses/i2c-parport.c +++ b/drivers/i2c/busses/i2c-parport.c @@ -237,7 +237,6 @@ static void i2c_parport_attach(struct parport *port) /* Setup SMBus alert if supported */ if (adapter_parm[type].smbus_alert) { - adapter->alert_data.alert_edge_triggered = 1; adapter->ara = i2c_setup_smbus_alert(&adapter->adapter, &adapter->alert_data); if (adapter->ara) diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c index df0976f4432a..1a7cad874756 100644 --- a/drivers/i2c/busses/i2c-thunderx-pcidrv.c +++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c @@ -118,8 +118,6 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk) static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c, struct device_node *node) { - u32 type; - if (!node) return -EINVAL; @@ -127,10 +125,6 @@ static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c, if (!i2c->alert_data.irq) return -EINVAL; - type = irqd_get_trigger_type(irq_get_irq_data(i2c->alert_data.irq)); - i2c->alert_data.alert_edge_triggered = - (type & IRQ_TYPE_LEVEL_MASK) ? 1 : 0; - i2c->ara = i2c_setup_smbus_alert(&i2c->adap, &i2c->alert_data); if (!i2c->ara) return -ENODEV; diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index f9271c713d20..d4af2701ac6e 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -25,8 +25,6 @@ #include struct i2c_smbus_alert { - unsigned int alert_edge_triggered:1; - int irq; struct work_struct alert; struct i2c_client *ara; /* Alert response address */ }; @@ -72,13 +70,12 @@ static int smbus_do_alert(struct device *dev, void *addrp) * The alert IRQ handler needs to hand work off to a task which can issue * SMBus calls, because those sleeping calls can't be made in IRQ context. */ -static void smbus_alert(struct work_struct *work) +static irqreturn_t smbus_alert(int irq, void *d) { - struct i2c_smbus_alert *alert; + struct i2c_smbus_alert *alert = d; struct i2c_client *ara; unsigned short prev_addr = 0; /* Not a valid address */ - alert = container_of(work, struct i2c_smbus_alert, alert); ara = alert->ara; for (;;) { @@ -115,21 +112,17 @@ static void smbus_alert(struct work_struct *work) prev_addr = data.addr; } - /* We handled all alerts; re-enable level-triggered IRQs */ - if (!alert->alert_edge_triggered) - enable_irq(alert->irq); + return IRQ_HANDLED; } -static irqreturn_t smbalert_irq(int irq, void *d) +static void smbalert_work(struct work_struct *work) { - struct i2c_smbus_alert *alert = d; + struct i2c_smbus_alert *alert; + + alert = container_of(work, struct i2c_smbus_alert, alert); - /* Disable level-triggered IRQs until we handle them */ - if (!alert->alert_edge_triggered) - disable_irq_nosync(irq); + smbus_alert(0, alert); - schedule_work(&alert->alert); - return IRQ_HANDLED; } /* Setup SMBALERT# infrastructure */ @@ -139,28 +132,28 @@ static int smbalert_probe(struct i2c_client *ara, struct i2c_smbus_alert_setup *setup = dev_get_platdata(&ara->dev); struct i2c_smbus_alert *alert; struct i2c_adapter *adapter = ara->adapter; - int res; + int res, irq; alert = devm_kzalloc(&ara->dev, sizeof(struct i2c_smbus_alert), GFP_KERNEL); if (!alert) return -ENOMEM; - alert->alert_edge_triggered = setup->alert_edge_triggered; - alert->irq = setup->irq; - INIT_WORK(&alert->alert, smbus_alert); + irq = setup->irq; + INIT_WORK(&alert->alert, smbalert_work); alert->ara = ara; - if (setup->irq > 0) { - res = devm_request_irq(&ara->dev, setup->irq, smbalert_irq, - 0, "smbus_alert", alert); + if (irq > 0) { + res = devm_request_threaded_irq(&ara->dev, irq, + NULL, smbus_alert, + IRQF_SHARED | IRQF_ONESHOT, + "smbus_alert", alert); if (res) return res; } i2c_set_clientdata(ara, alert); - dev_info(&adapter->dev, "supports SMBALERT#, %s trigger\n", - setup->alert_edge_triggered ? "edge" : "level"); + dev_info(&adapter->dev, "supports SMBALERT#\n"); return 0; } diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h index a1385023a29b..19efbd14e812 100644 --- a/include/linux/i2c-smbus.h +++ b/include/linux/i2c-smbus.h @@ -42,7 +42,6 @@ * properly set. */ struct i2c_smbus_alert_setup { - unsigned int alert_edge_triggered:1; int irq; }; -- cgit v1.2.3-59-g8ed1b From 69d17246ab255dda8e71c8d65396b4aa6121b7ad Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Thu, 24 Aug 2017 17:31:03 +0800 Subject: i2c: i2c-smbus: add of_i2c_setup_smbus_alert This commit adds of_i2c_setup_smbus_alert which allows the smbalert driver to be attached to an i2c adapter via the device tree. Signed-off-by: Phil Reid Acked-by: Rob Herring Signed-off-by: Wolfram Sang --- Documentation/devicetree/bindings/i2c/i2c.txt | 4 ++-- drivers/i2c/i2c-core-smbus.c | 22 ++++++++++++++++++++++ drivers/i2c/i2c-smbus.c | 10 +++++++++- include/linux/i2c-smbus.h | 9 +++++++++ 4 files changed, 42 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/i2c/i2c.txt b/Documentation/devicetree/bindings/i2c/i2c.txt index cee9d5055fa2..11263982470e 100644 --- a/Documentation/devicetree/bindings/i2c/i2c.txt +++ b/Documentation/devicetree/bindings/i2c/i2c.txt @@ -59,8 +59,8 @@ wants to support one of the below features, it should adapt the bindings below. interrupts used by the device. - interrupt-names - "irq" and "wakeup" names are recognized by I2C core, other names are - left to individual drivers. + "irq", "wakeup" and "smbus_alert" names are recognized by I2C core, + other names are left to individual drivers. - host-notify device uses SMBus host notify protocol instead of interrupt line. diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 7f3ec02f085a..4bb9927afd01 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -625,3 +625,25 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, return i2c_new_device(adapter, &ara_board_info); } EXPORT_SYMBOL_GPL(i2c_setup_smbus_alert); + +#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF) +int of_i2c_setup_smbus_alert(struct i2c_adapter *adapter) +{ + struct i2c_client *client; + int irq; + + irq = of_property_match_string(adapter->dev.of_node, "interrupt-names", + "smbus_alert"); + if (irq == -EINVAL || irq == -ENODATA) + return 0; + else if (irq < 0) + return irq; + + client = i2c_setup_smbus_alert(adapter, NULL); + if (!client) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_GPL(of_i2c_setup_smbus_alert); +#endif diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index d0bb0358b578..5a1dd7f13bac 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -139,7 +140,14 @@ static int smbalert_probe(struct i2c_client *ara, if (!alert) return -ENOMEM; - irq = setup->irq; + if (setup) { + irq = setup->irq; + } else { + irq = of_irq_get_byname(adapter->dev.of_node, "smbus_alert"); + if (irq <= 0) + return irq; + } + INIT_WORK(&alert->alert, smbalert_work); alert->ara = ara; diff --git a/include/linux/i2c-smbus.h b/include/linux/i2c-smbus.h index 19efbd14e812..fb0e040b1abb 100644 --- a/include/linux/i2c-smbus.h +++ b/include/linux/i2c-smbus.h @@ -49,4 +49,13 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter, struct i2c_smbus_alert_setup *setup); int i2c_handle_smbus_alert(struct i2c_client *ara); +#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF) +int of_i2c_setup_smbus_alert(struct i2c_adapter *adap); +#else +static inline int of_i2c_setup_smbus_alert(struct i2c_adapter *adap) +{ + return 0; +} +#endif + #endif /* _LINUX_I2C_SMBUS_H */ -- cgit v1.2.3-59-g8ed1b From 2ea2352ede9d97585164a7e19224955f4e4ca8db Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 27 Oct 2017 17:30:12 -0700 Subject: ipv6: prevent user from adding cached routes Cached routes should only be created by the system when receiving pmtu discovery or ip redirect msg. Users should not be allowed to create cached routes. Furthermore, after the patch series to move cached routes into exception table, user added cached routes will trigger the following warning in fib6_add(): WARNING: CPU: 0 PID: 2985 at net/ipv6/ip6_fib.c:1137 fib6_add+0x20d9/0x2c10 net/ipv6/ip6_fib.c:1137 Kernel panic - not syncing: panic_on_warn set ... CPU: 0 PID: 2985 Comm: syzkaller320388 Not tainted 4.14.0-rc3+ #74 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 Call Trace: __dump_stack lib/dump_stack.c:16 [inline] dump_stack+0x194/0x257 lib/dump_stack.c:52 panic+0x1e4/0x417 kernel/panic.c:181 __warn+0x1c4/0x1d9 kernel/panic.c:542 report_bug+0x211/0x2d0 lib/bug.c:183 fixup_bug+0x40/0x90 arch/x86/kernel/traps.c:178 do_trap_no_signal arch/x86/kernel/traps.c:212 [inline] do_trap+0x260/0x390 arch/x86/kernel/traps.c:261 do_error_trap+0x120/0x390 arch/x86/kernel/traps.c:298 do_invalid_op+0x1b/0x20 arch/x86/kernel/traps.c:311 invalid_op+0x18/0x20 arch/x86/entry/entry_64.S:905 RIP: 0010:fib6_add+0x20d9/0x2c10 net/ipv6/ip6_fib.c:1137 RSP: 0018:ffff8801cf09f6a0 EFLAGS: 00010297 RAX: ffff8801ce45e340 RBX: 1ffff10039e13eec RCX: ffff8801d749c814 RDX: 0000000000000000 RSI: ffff8801d749c700 RDI: ffff8801d749c780 RBP: ffff8801cf09fa08 R08: 0000000000000000 R09: ffff8801cf09f360 R10: ffff8801cf09f2d8 R11: 1ffff10039c8befb R12: 0000000000000001 R13: dffffc0000000000 R14: ffff8801d749c700 R15: ffffffff860655c0 __ip6_ins_rt+0x6c/0x90 net/ipv6/route.c:1011 ip6_route_add+0x148/0x1a0 net/ipv6/route.c:2782 ipv6_route_ioctl+0x4d5/0x690 net/ipv6/route.c:3291 inet6_ioctl+0xef/0x1e0 net/ipv6/af_inet6.c:521 sock_do_ioctl+0x65/0xb0 net/socket.c:961 sock_ioctl+0x2c2/0x440 net/socket.c:1058 vfs_ioctl fs/ioctl.c:45 [inline] do_vfs_ioctl+0x1b1/0x1530 fs/ioctl.c:685 SYSC_ioctl fs/ioctl.c:700 [inline] SyS_ioctl+0x8f/0xc0 fs/ioctl.c:691 entry_SYSCALL_64_fastpath+0x1f/0xbe So we fix this by failing the attemp to add cached routes from userspace with returning EINVAL error. Fixes: 2b760fcf5cfb ("ipv6: hook up exception table to store dst cache") Signed-off-by: Wei Wang Signed-off-by: Eric Dumazet Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- include/uapi/linux/ipv6_route.h | 2 +- net/ipv6/route.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/uapi/linux/ipv6_route.h b/include/uapi/linux/ipv6_route.h index d496c02e14bc..c15d8054905c 100644 --- a/include/uapi/linux/ipv6_route.h +++ b/include/uapi/linux/ipv6_route.h @@ -28,7 +28,7 @@ #define RTF_ROUTEINFO 0x00800000 /* route information - RA */ -#define RTF_CACHE 0x01000000 /* cache entry */ +#define RTF_CACHE 0x01000000 /* read-only: can not be set by user */ #define RTF_FLOW 0x02000000 /* flow significant route */ #define RTF_POLICY 0x04000000 /* policy route */ diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 605e5dc1c010..70d9659fc1e9 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2478,6 +2478,12 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, goto out; } + /* RTF_CACHE is an internal flag; can not be set by userspace */ + if (cfg->fc_flags & RTF_CACHE) { + NL_SET_ERR_MSG(extack, "Userspace can not set RTF_CACHE"); + goto out; + } + if (cfg->fc_dst_len > 128) { NL_SET_ERR_MSG(extack, "Invalid prefix length"); goto out; -- cgit v1.2.3-59-g8ed1b From a190d04db93710ae166749055b6985397c6d13f5 Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Thu, 26 Oct 2017 15:09:21 -0700 Subject: ipvlan: introduce 'private' attribute for all existing modes. IPvlan has always operated in bridge mode. However there are scenarios where each slave should be able to talk through the master device but not necessarily across each other. Think of an environment where each of a namespace is a private and independant customer. In this scenario the machine which is hosting these namespaces neither want to tell who their neighbor is nor the individual namespaces care to talk to neighbor on short-circuited network path. This patch implements the mode that is very similar to the 'private' mode in macvlan where individual slaves can send and receive traffic through the master device, just that they can not talk among slave devices. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- Documentation/networking/ipvlan.txt | 30 ++++++++++++++++++++++++++--- drivers/net/ipvlan/ipvlan.h | 16 ++++++++++++++++ drivers/net/ipvlan/ipvlan_core.c | 15 ++++++++++++--- drivers/net/ipvlan/ipvlan_main.c | 38 +++++++++++++++++++++++++++++++++++-- include/uapi/linux/if_link.h | 3 +++ 5 files changed, 94 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ipvlan.txt b/Documentation/networking/ipvlan.txt index 1fe42a874aae..bfa91c77a4c9 100644 --- a/Documentation/networking/ipvlan.txt +++ b/Documentation/networking/ipvlan.txt @@ -22,9 +22,19 @@ The driver can be built into the kernel (CONFIG_IPVLAN=y) or as a module There are no module parameters for this driver and it can be configured using IProute2/ip utility. - ip link add link name type ipvlan mode { l2 | l3 | l3s } + ip link add link name type ipvlan [ mode MODE ] [ FLAGS ] + where + MODE: l3 (default) | l3s | l2 + FLAGS: bridge (default) | private - e.g. ip link add link eth0 name ipvl0 type ipvlan mode l2 + e.g. + (a) Following will create IPvlan link with eth0 as master in + L3 bridge mode + bash# ip link add link eth0 name ipvl0 type ipvlan + (b) This command will create IPvlan link in L2 bridge mode. + bash# ip link add link eth0 name ipvl0 type ipvlan mode l2 bridge + (c) This command will create an IPvlan device in L2 private mode. + bash# ip link add link eth0 name ipvlan type ipvlan mode l2 private 4. Operating modes: @@ -54,7 +64,21 @@ works in this mode and hence it is L3-symmetric (L3s). This will have slightly l performance but that shouldn't matter since you are choosing this mode over plain-L3 mode to make conn-tracking work. -5. What to choose (macvlan vs. ipvlan)? +5. Mode flags: + At this time following mode flags are available + +5.1 bridge: + This is the default option. To configure the IPvlan port in this mode, +user can choose to either add this option on the command-line or don't specify +anything. This is the traditional mode where slaves can cross-talk among +themseleves apart from talking through the master device. + +5.2 private: + If this option is added to the command-line, the port is set in private +mode. i.e. port wont allow cross communication between slaves. + + +6. What to choose (macvlan vs. ipvlan)? These two devices are very similar in many regards and the specific use case could very well define which device to choose. if one of the following situations defines your use case then you can choose to use ipvlan - diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index ba8173a0b62e..9941851bcc13 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -96,6 +96,7 @@ struct ipvl_port { struct hlist_head hlhead[IPVLAN_HASH_SIZE]; struct list_head ipvlans; u16 mode; + u16 flags; u16 dev_id_start; struct work_struct wq; struct sk_buff_head backlog; @@ -123,6 +124,21 @@ static inline struct ipvl_port *ipvlan_port_get_rtnl(const struct net_device *d) return rtnl_dereference(d->rx_handler_data); } +static inline bool ipvlan_is_private(const struct ipvl_port *port) +{ + return !!(port->flags & IPVLAN_F_PRIVATE); +} + +static inline void ipvlan_mark_private(struct ipvl_port *port) +{ + port->flags |= IPVLAN_F_PRIVATE; +} + +static inline void ipvlan_clear_private(struct ipvl_port *port) +{ + port->flags &= ~IPVLAN_F_PRIVATE; +} + void ipvlan_init_secret(void); unsigned int ipvlan_mac_hash(const unsigned char *addr); rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb); diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 1f3295e274d0..72fd56de9c00 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -515,9 +515,13 @@ static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev) goto out; addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true); - if (addr) + if (addr) { + if (ipvlan_is_private(ipvlan->port)) { + consume_skb(skb); + return NET_XMIT_DROP; + } return ipvlan_rcv_frame(addr, &skb, true); - + } out: ipvlan_skb_crossing_ns(skb, ipvlan->phy_dev); return ipvlan_process_outbound(skb); @@ -535,8 +539,13 @@ static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev) lyr3h = ipvlan_get_L3_hdr(skb, &addr_type); if (lyr3h) { addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true); - if (addr) + if (addr) { + if (ipvlan_is_private(ipvlan->port)) { + consume_skb(skb); + return NET_XMIT_DROP; + } return ipvlan_rcv_frame(addr, &skb, true); + } } skb = skb_share_check(skb, GFP_ATOMIC); if (!skb) diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index f0ab55df57f1..4368afb1934c 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -462,11 +462,24 @@ static int ipvlan_nl_changelink(struct net_device *dev, struct ipvl_port *port = ipvlan_port_get_rtnl(ipvlan->phy_dev); int err = 0; - if (data && data[IFLA_IPVLAN_MODE]) { + if (!data) + return 0; + + if (data[IFLA_IPVLAN_MODE]) { u16 nmode = nla_get_u16(data[IFLA_IPVLAN_MODE]); err = ipvlan_set_port_mode(port, nmode); } + + if (!err && data[IFLA_IPVLAN_FLAGS]) { + u16 flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); + + if (flags & IPVLAN_F_PRIVATE) + ipvlan_mark_private(port); + else + ipvlan_clear_private(port); + } + return err; } @@ -474,18 +487,30 @@ static size_t ipvlan_nl_getsize(const struct net_device *dev) { return (0 + nla_total_size(2) /* IFLA_IPVLAN_MODE */ + + nla_total_size(2) /* IFLA_IPVLAN_FLAGS */ ); } static int ipvlan_nl_validate(struct nlattr *tb[], struct nlattr *data[], struct netlink_ext_ack *extack) { - if (data && data[IFLA_IPVLAN_MODE]) { + if (!data) + return 0; + + if (data[IFLA_IPVLAN_MODE]) { u16 mode = nla_get_u16(data[IFLA_IPVLAN_MODE]); if (mode < IPVLAN_MODE_L2 || mode >= IPVLAN_MODE_MAX) return -EINVAL; } + if (data[IFLA_IPVLAN_FLAGS]) { + u16 flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); + + /* Only one bit is used at this moment. */ + if (flags & ~IPVLAN_F_PRIVATE) + return -EINVAL; + } + return 0; } @@ -502,6 +527,8 @@ static int ipvlan_nl_fillinfo(struct sk_buff *skb, ret = -EMSGSIZE; if (nla_put_u16(skb, IFLA_IPVLAN_MODE, port->mode)) goto err; + if (nla_put_u16(skb, IFLA_IPVLAN_FLAGS, port->flags)) + goto err; return 0; @@ -549,6 +576,12 @@ int ipvlan_link_new(struct net *src_net, struct net_device *dev, ipvlan_adjust_mtu(ipvlan, phy_dev); INIT_LIST_HEAD(&ipvlan->addrs); + /* Flags are per port and latest update overrides. User has + * to be consistent in setting it just like the mode attribute. + */ + if (data && data[IFLA_IPVLAN_FLAGS]) + ipvlan->port->flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); + /* If the port-id base is at the MAX value, then wrap it around and * begin from 0x1 again. This may be due to a busy system where lots * of slaves are getting created and deleted. @@ -644,6 +677,7 @@ EXPORT_SYMBOL_GPL(ipvlan_link_setup); static const struct nla_policy ipvlan_nl_policy[IFLA_IPVLAN_MAX + 1] = { [IFLA_IPVLAN_MODE] = { .type = NLA_U16 }, + [IFLA_IPVLAN_FLAGS] = { .type = NLA_U16 }, }; static struct rtnl_link_ops ipvlan_link_ops = { diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index b037e0ab1975..052e32cd584c 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -465,6 +465,7 @@ enum macsec_validation_type { enum { IFLA_IPVLAN_UNSPEC, IFLA_IPVLAN_MODE, + IFLA_IPVLAN_FLAGS, __IFLA_IPVLAN_MAX }; @@ -477,6 +478,8 @@ enum ipvlan_mode { IPVLAN_MODE_MAX }; +#define IPVLAN_F_PRIVATE 0x01 + /* VXLAN section */ enum { IFLA_VXLAN_UNSPEC, -- cgit v1.2.3-59-g8ed1b From fe89aa6b250c1011ccf425fbb7998e96bd54263f Mon Sep 17 00:00:00 2001 From: Mahesh Bandewar Date: Thu, 26 Oct 2017 15:09:25 -0700 Subject: ipvlan: implement VEPA mode This is very similar to the Macvlan VEPA mode, however, there is some difference. IPvlan uses the mac-address of the lower device, so the VEPA mode has implications of ICMP-redirects for packets destined for its immediate neighbors sharing same master since the packets will have same source and dest mac. The external switch/router will send redirect msg. Having said that, this will be useful tool in terms of debugging since IPvlan will not switch packets within its slaves and rely completely on the external entity as intended in 802.1Qbg. Signed-off-by: Mahesh Bandewar Signed-off-by: David S. Miller --- Documentation/networking/ipvlan.txt | 12 +++++++++++- drivers/net/ipvlan/ipvlan.h | 15 +++++++++++++++ drivers/net/ipvlan/ipvlan_core.c | 17 ++++++++++------- drivers/net/ipvlan/ipvlan_main.c | 13 +++++++++++-- include/uapi/linux/if_link.h | 1 + 5 files changed, 48 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ipvlan.txt b/Documentation/networking/ipvlan.txt index bfa91c77a4c9..812ef003e0a8 100644 --- a/Documentation/networking/ipvlan.txt +++ b/Documentation/networking/ipvlan.txt @@ -25,7 +25,7 @@ using IProute2/ip utility. ip link add link name type ipvlan [ mode MODE ] [ FLAGS ] where MODE: l3 (default) | l3s | l2 - FLAGS: bridge (default) | private + FLAGS: bridge (default) | private | vepa e.g. (a) Following will create IPvlan link with eth0 as master in @@ -35,6 +35,8 @@ using IProute2/ip utility. bash# ip link add link eth0 name ipvl0 type ipvlan mode l2 bridge (c) This command will create an IPvlan device in L2 private mode. bash# ip link add link eth0 name ipvlan type ipvlan mode l2 private + (d) This command will create an IPvlan device in L2 vepa mode. + bash# ip link add link eth0 name ipvlan type ipvlan mode l2 vepa 4. Operating modes: @@ -77,6 +79,14 @@ themseleves apart from talking through the master device. If this option is added to the command-line, the port is set in private mode. i.e. port wont allow cross communication between slaves. +5.3 vepa: + If this is added to the command-line, the port is set in VEPA mode. +i.e. port will offload switching functionality to the external entity as +described in 802.1Qbg +Note: VEPA mode in IPvlan has limitations. IPvlan uses the mac-address of the +master-device, so the packets which are emitted in this mode for the adjacent +neighbor will have source and destination mac same. This will make the switch / +router send the redirect message. 6. What to choose (macvlan vs. ipvlan)? These two devices are very similar in many regards and the specific use diff --git a/drivers/net/ipvlan/ipvlan.h b/drivers/net/ipvlan/ipvlan.h index 9941851bcc13..5166575a164d 100644 --- a/drivers/net/ipvlan/ipvlan.h +++ b/drivers/net/ipvlan/ipvlan.h @@ -139,6 +139,21 @@ static inline void ipvlan_clear_private(struct ipvl_port *port) port->flags &= ~IPVLAN_F_PRIVATE; } +static inline bool ipvlan_is_vepa(const struct ipvl_port *port) +{ + return !!(port->flags & IPVLAN_F_VEPA); +} + +static inline void ipvlan_mark_vepa(struct ipvl_port *port) +{ + port->flags |= IPVLAN_F_VEPA; +} + +static inline void ipvlan_clear_vepa(struct ipvl_port *port) +{ + port->flags &= ~IPVLAN_F_VEPA; +} + void ipvlan_init_secret(void); unsigned int ipvlan_mac_hash(const unsigned char *addr); rx_handler_result_t ipvlan_handle_frame(struct sk_buff **pskb); diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index 72fd56de9c00..034ae4c57196 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -514,13 +514,15 @@ static int ipvlan_xmit_mode_l3(struct sk_buff *skb, struct net_device *dev) if (!lyr3h) goto out; - addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true); - if (addr) { - if (ipvlan_is_private(ipvlan->port)) { - consume_skb(skb); - return NET_XMIT_DROP; + if (!ipvlan_is_vepa(ipvlan->port)) { + addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true); + if (addr) { + if (ipvlan_is_private(ipvlan->port)) { + consume_skb(skb); + return NET_XMIT_DROP; + } + return ipvlan_rcv_frame(addr, &skb, true); } - return ipvlan_rcv_frame(addr, &skb, true); } out: ipvlan_skb_crossing_ns(skb, ipvlan->phy_dev); @@ -535,7 +537,8 @@ static int ipvlan_xmit_mode_l2(struct sk_buff *skb, struct net_device *dev) void *lyr3h; int addr_type; - if (ether_addr_equal(eth->h_dest, eth->h_source)) { + if (!ipvlan_is_vepa(ipvlan->port) && + ether_addr_equal(eth->h_dest, eth->h_source)) { lyr3h = ipvlan_get_L3_hdr(skb, &addr_type); if (lyr3h) { addr = ipvlan_addr_lookup(ipvlan->port, lyr3h, addr_type, true); diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index 4368afb1934c..a266aa435d4d 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -478,6 +478,11 @@ static int ipvlan_nl_changelink(struct net_device *dev, ipvlan_mark_private(port); else ipvlan_clear_private(port); + + if (flags & IPVLAN_F_VEPA) + ipvlan_mark_vepa(port); + else + ipvlan_clear_vepa(port); } return err; @@ -506,8 +511,12 @@ static int ipvlan_nl_validate(struct nlattr *tb[], struct nlattr *data[], if (data[IFLA_IPVLAN_FLAGS]) { u16 flags = nla_get_u16(data[IFLA_IPVLAN_FLAGS]); - /* Only one bit is used at this moment. */ - if (flags & ~IPVLAN_F_PRIVATE) + /* Only two bits are used at this moment. */ + if (flags & ~(IPVLAN_F_PRIVATE | IPVLAN_F_VEPA)) + return -EINVAL; + /* Also both flags can't be active at the same time. */ + if ((flags & (IPVLAN_F_PRIVATE | IPVLAN_F_VEPA)) == + (IPVLAN_F_PRIVATE | IPVLAN_F_VEPA)) return -EINVAL; } diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 052e32cd584c..81f26473d728 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -479,6 +479,7 @@ enum ipvlan_mode { }; #define IPVLAN_F_PRIVATE 0x01 +#define IPVLAN_F_VEPA 0x02 /* VXLAN section */ enum { -- cgit v1.2.3-59-g8ed1b From 1f01d8be0e6a04bd682a55f6d50c14c1679e7571 Mon Sep 17 00:00:00 2001 From: Konrad Zapałowicz Date: Tue, 17 Oct 2017 15:53:49 +0200 Subject: Bluetooth: increase timeout for le auto connections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch increases the connection timeout for LE connections that are triggered by the advertising report to 4 seconds. It has been observed that devices equipped with wifi+bt combo SoC fail to create a connection with BLE devices due to their coexistence issues. Increasing this timeout gives them enough time to complete the connection with success. Signed-off-by: Konrad Zapałowicz Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index fe98f0a5bef0..1668211297a9 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -273,7 +273,7 @@ enum { #define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_POWER_OFF_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */ #define HCI_LE_CONN_TIMEOUT msecs_to_jiffies(20000) /* 20 seconds */ -#define HCI_LE_AUTOCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ +#define HCI_LE_AUTOCONN_TIMEOUT msecs_to_jiffies(4000) /* 4 seconds */ /* HCI data types */ #define HCI_COMMAND_PKT 0x01 -- cgit v1.2.3-59-g8ed1b From b2e63555592f81331c8da3afaa607d8cf83e8138 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 10 Sep 2017 01:30:46 +0200 Subject: i2c: gpio: Convert to use descriptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This converts the GPIO-based I2C-driver to using GPIO descriptors instead of the old global numberspace-based GPIO interface. We: - Convert the driver to unconditionally grab two GPIOs from the device by index 0 (SDA) and 1 (SCL) which will work fine with device tree and descriptor tables. The existing device trees will continue to work just like before, but without any roundtrip through the global numberspace. - Brutally convert all boardfiles still passing global GPIOs by registering descriptor tables associated with the devices instead so this driver does not need to keep supporting passing any GPIO numbers as platform data. There is no stepwise approach as elegant as this, I strongly prefer this big hammer over any antsteps for this conversion. This way the old GPIO numbers go away and NEVER COME BACK. Special conversion for the different boards utilizing I2C-GPIO: - EP93xx (arch/arm/mach-ep93xx): pretty straight forward as all boards were using the same two GPIO lines, just define these two in a lookup table for "i2c-gpio" and register these along with the device. None of them define any other platform data so just pass NULL as platform data. This platform selects GPIOLIB so all should be smooth. The pins appear on a gpiochip for bank "G" as pins 1 (SDA) and 0 (SCL). - IXP4 (arch/arm/mach-ixp4): descriptor tables have to be registered for each board separately. They all use "IXP4XX_GPIO_CHIP" so it is pretty straight forward. Most board define no other platform data than SCL/SDA so they can drop the #include of and assign NULL to platform data. The "goramo_mlr" (Goramo Multilink Router) board is a bit worrisome: it implements its own I2C bit-banging in the board file, and optionally registers an I2C serial port, but claims the same GPIO lines for itself in the board file. This is not going to work: there will be competition for the GPIO lines, so delete the optional extra I2C bus instead, no I2C devices are registered on it anyway, there are just hints that it may contain an EEPROM that may be accessed from userspace. This needs to be fixed up properly by the serial clock using I2C emulation so drop a note in the code. - KS8695 board acs5k (arch/arm/mach-ks8695/board-acs5.c) has some platform data in addition to the pins so it needs to be kept around sans GPIO lines. Its GPIO chip is named "KS8695" and the arch selects GPIOLIB. - PXA boards (arch/arm/mach-pxa/*) use some of the platform data so it needs to be preserved here. The viper board even registers two GPIO I2Cs. The gpiochip is named "gpio-pxa" and the arch selects GPIOLIB. - SA1100 Simpad (arch/arm/mach-sa1100/simpad.c) defines a GPIO I2C bus, and the arch selects GPIOLIB. - Blackfin boards (arch/blackfin/bf533 etc) for these I assume their I2C GPIOs refer to the local gpiochip defined in arch/blackfin/kernel/bfin_gpio.c names "BFIN-GPIO". The arch selects GPIOLIB. The boards get spiked with IF_ENABLED(I2C_GPIO) but that is a side effect of it being like that already (I would just have Kconfig select I2C_GPIO and get rid of them all.) I also delete any platform data set to 0 as it will get that value anyway from static declartions of platform data. - The MIPS selects GPIOLIB and the Alchemy machine is using two local GPIO chips, one of them has a GPIO I2C. We need to adjust the local offset from the global number space here. The ATH79 has a proper GPIO driver in drivers/gpio/gpio-ath79.c and AFAICT the chip is named "ath79-gpio" and the PB44 PCF857x expander spawns from this on GPIO 1 and 0. The latter board only use the platform data to specify pins so it can be cut altogether after this. - The MFD Silicon Motion SM501 is a special case. It dynamically spawns an I2C bus off the MFD using sm501_create_subdev(). We use an approach to dynamically create a machine descriptor table and attach this to the "SM501-LOW" or "SM501-HIGH" gpiochip. We use chip-local offsets to grab the right lines. We can get rid of two local static inline helpers as part of this refactoring. Cc: Steven Miao Cc: Ralf Baechle Cc: Guenter Roeck Cc: Ville Syrjälä Cc: Magnus Damm Cc: Ben Dooks Cc: Heiko Schocher Acked-by: Wu, Aaron Acked-by: Olof Johansson Acked-by: Lee Jones Acked-by: Ralf Baechle Tested-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- arch/arm/mach-ep93xx/core.c | 39 ++++---- arch/arm/mach-ep93xx/edb93xx.c | 15 +-- arch/arm/mach-ep93xx/include/mach/platform.h | 4 +- arch/arm/mach-ep93xx/simone.c | 12 +-- arch/arm/mach-ep93xx/snappercl15.c | 12 +-- arch/arm/mach-ep93xx/vision_ep9307.c | 7 +- arch/arm/mach-ixp4xx/avila-setup.c | 17 +++- arch/arm/mach-ixp4xx/dsmg600-setup.c | 16 +++- arch/arm/mach-ixp4xx/fsg-setup.c | 16 +++- arch/arm/mach-ixp4xx/goramo_mlr.c | 24 ++--- arch/arm/mach-ixp4xx/ixdp425-setup.c | 16 +++- arch/arm/mach-ixp4xx/nas100d-setup.c | 16 +++- arch/arm/mach-ixp4xx/nslu2-setup.c | 16 +++- arch/arm/mach-ks8695/board-acs5k.c | 13 ++- arch/arm/mach-pxa/palmz72.c | 12 ++- arch/arm/mach-pxa/viper.c | 27 +++++- arch/arm/mach-sa1100/simpad.c | 12 ++- arch/blackfin/mach-bf533/boards/blackstamp.c | 19 +++- arch/blackfin/mach-bf533/boards/ezkit.c | 18 +++- arch/blackfin/mach-bf533/boards/stamp.c | 18 +++- arch/blackfin/mach-bf561/boards/ezkit.c | 18 +++- arch/mips/alchemy/board-gpr.c | 19 +++- arch/mips/ath79/mach-pb44.c | 16 +++- drivers/i2c/busses/i2c-gpio.c | 134 +++++++++++++-------------- drivers/mfd/sm501.c | 49 +++++----- include/linux/i2c-gpio.h | 4 - 26 files changed, 327 insertions(+), 242 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index f53c61813998..7e99fe829ad1 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include #include #include @@ -320,42 +320,45 @@ void __init ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr) /************************************************************************* * EP93xx i2c peripheral handling *************************************************************************/ -static struct i2c_gpio_platform_data ep93xx_i2c_data; + +/* All EP93xx devices use the same two GPIO pins for I2C bit-banging */ +static struct gpiod_lookup_table ep93xx_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + /* Use local offsets on gpiochip/port "G" */ + GPIO_LOOKUP_IDX("G", 1, NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("G", 0, NULL, 1, GPIO_ACTIVE_HIGH), + }, +}; static struct platform_device ep93xx_i2c_device = { .name = "i2c-gpio", .id = 0, .dev = { - .platform_data = &ep93xx_i2c_data, + .platform_data = NULL, }, }; /** * ep93xx_register_i2c - Register the i2c platform device. - * @data: platform specific i2c-gpio configuration (__initdata) * @devices: platform specific i2c bus device information (__initdata) * @num: the number of devices on the i2c bus */ -void __init ep93xx_register_i2c(struct i2c_gpio_platform_data *data, - struct i2c_board_info *devices, int num) +void __init ep93xx_register_i2c(struct i2c_board_info *devices, int num) { /* - * Set the EEPROM interface pin drive type control. - * Defines the driver type for the EECLK and EEDAT pins as either - * open drain, which will require an external pull-up, or a normal - * CMOS driver. + * FIXME: this just sets the two pins as non-opendrain, as no + * platforms tries to do that anyway. Flag the applicable lines + * as open drain in the GPIO_LOOKUP above and the driver or + * gpiolib will handle open drain/open drain emulation as need + * be. Right now i2c-gpio emulates open drain which is not + * optimal. */ - if (data->sda_is_open_drain && data->sda_pin != EP93XX_GPIO_LINE_EEDAT) - pr_warning("sda != EEDAT, open drain has no effect\n"); - if (data->scl_is_open_drain && data->scl_pin != EP93XX_GPIO_LINE_EECLK) - pr_warning("scl != EECLK, open drain has no effect\n"); - - __raw_writel((data->sda_is_open_drain << 1) | - (data->scl_is_open_drain << 0), + __raw_writel((0 << 1) | (0 << 0), EP93XX_GPIO_EEDRIVE); - ep93xx_i2c_data = *data; i2c_register_board_info(0, devices, num); + gpiod_add_lookup_table(&ep93xx_i2c_gpiod_table); platform_device_register(&ep93xx_i2c_device); } diff --git a/arch/arm/mach-ep93xx/edb93xx.c b/arch/arm/mach-ep93xx/edb93xx.c index 7a7f280b07d7..8e89ec8b6f0f 100644 --- a/arch/arm/mach-ep93xx/edb93xx.c +++ b/arch/arm/mach-ep93xx/edb93xx.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -61,14 +60,6 @@ static struct ep93xx_eth_data __initdata edb93xx_eth_data = { /************************************************************************* * EDB93xx i2c peripheral handling *************************************************************************/ -static struct i2c_gpio_platform_data __initdata edb93xx_i2c_gpio_data = { - .sda_pin = EP93XX_GPIO_LINE_EEDAT, - .sda_is_open_drain = 0, - .scl_pin = EP93XX_GPIO_LINE_EECLK, - .scl_is_open_drain = 0, - .udelay = 0, /* default to 100 kHz */ - .timeout = 0, /* default to 100 ms */ -}; static struct i2c_board_info __initdata edb93xxa_i2c_board_info[] = { { @@ -86,13 +77,11 @@ static void __init edb93xx_register_i2c(void) { if (machine_is_edb9302a() || machine_is_edb9307a() || machine_is_edb9315a()) { - ep93xx_register_i2c(&edb93xx_i2c_gpio_data, - edb93xxa_i2c_board_info, + ep93xx_register_i2c(edb93xxa_i2c_board_info, ARRAY_SIZE(edb93xxa_i2c_board_info)); } else if (machine_is_edb9302() || machine_is_edb9307() || machine_is_edb9312() || machine_is_edb9315()) { - ep93xx_register_i2c(&edb93xx_i2c_gpio_data, - edb93xx_i2c_board_info, + ep93xx_register_i2c(edb93xx_i2c_board_info, ARRAY_SIZE(edb93xx_i2c_board_info)); } } diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h index db0839691ef5..a686fd6caee1 100644 --- a/arch/arm/mach-ep93xx/include/mach/platform.h +++ b/arch/arm/mach-ep93xx/include/mach/platform.h @@ -7,7 +7,6 @@ #include struct device; -struct i2c_gpio_platform_data; struct i2c_board_info; struct spi_board_info; struct platform_device; @@ -36,8 +35,7 @@ void ep93xx_register_flash(unsigned int width, resource_size_t start, resource_size_t size); void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr); -void ep93xx_register_i2c(struct i2c_gpio_platform_data *data, - struct i2c_board_info *devices, int num); +void ep93xx_register_i2c(struct i2c_board_info *devices, int num); void ep93xx_register_spi(struct ep93xx_spi_info *info, struct spi_board_info *devices, int num); void ep93xx_register_fb(struct ep93xxfb_mach_info *data); diff --git a/arch/arm/mach-ep93xx/simone.c b/arch/arm/mach-ep93xx/simone.c index c7a40f245892..e61f3dee24c2 100644 --- a/arch/arm/mach-ep93xx/simone.c +++ b/arch/arm/mach-ep93xx/simone.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -129,15 +128,6 @@ static struct ep93xx_spi_info simone_spi_info __initdata = { .use_dma = 1, }; -static struct i2c_gpio_platform_data __initdata simone_i2c_gpio_data = { - .sda_pin = EP93XX_GPIO_LINE_EEDAT, - .sda_is_open_drain = 0, - .scl_pin = EP93XX_GPIO_LINE_EECLK, - .scl_is_open_drain = 0, - .udelay = 0, - .timeout = 0, -}; - static struct i2c_board_info __initdata simone_i2c_board_info[] = { { I2C_BOARD_INFO("ds1337", 0x68), @@ -161,7 +151,7 @@ static void __init simone_init_machine(void) ep93xx_register_flash(2, EP93XX_CS6_PHYS_BASE, SZ_8M); ep93xx_register_eth(&simone_eth_data, 1); ep93xx_register_fb(&simone_fb_info); - ep93xx_register_i2c(&simone_i2c_gpio_data, simone_i2c_board_info, + ep93xx_register_i2c(simone_i2c_board_info, ARRAY_SIZE(simone_i2c_board_info)); ep93xx_register_spi(&simone_spi_info, simone_spi_devices, ARRAY_SIZE(simone_spi_devices)); diff --git a/arch/arm/mach-ep93xx/snappercl15.c b/arch/arm/mach-ep93xx/snappercl15.c index 8b29398f4dc7..45940c1d7787 100644 --- a/arch/arm/mach-ep93xx/snappercl15.c +++ b/arch/arm/mach-ep93xx/snappercl15.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -127,15 +126,6 @@ static struct ep93xx_eth_data __initdata snappercl15_eth_data = { .phy_id = 1, }; -static struct i2c_gpio_platform_data __initdata snappercl15_i2c_gpio_data = { - .sda_pin = EP93XX_GPIO_LINE_EEDAT, - .sda_is_open_drain = 0, - .scl_pin = EP93XX_GPIO_LINE_EECLK, - .scl_is_open_drain = 0, - .udelay = 0, - .timeout = 0, -}; - static struct i2c_board_info __initdata snappercl15_i2c_data[] = { { /* Audio codec */ @@ -161,7 +151,7 @@ static void __init snappercl15_init_machine(void) { ep93xx_init_devices(); ep93xx_register_eth(&snappercl15_eth_data, 1); - ep93xx_register_i2c(&snappercl15_i2c_gpio_data, snappercl15_i2c_data, + ep93xx_register_i2c(snappercl15_i2c_data, ARRAY_SIZE(snappercl15_i2c_data)); ep93xx_register_fb(&snappercl15_fb_info); snappercl15_register_audio(); diff --git a/arch/arm/mach-ep93xx/vision_ep9307.c b/arch/arm/mach-ep93xx/vision_ep9307.c index 1daf9441058c..5a0b6187990a 100644 --- a/arch/arm/mach-ep93xx/vision_ep9307.c +++ b/arch/arm/mach-ep93xx/vision_ep9307.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -144,10 +143,6 @@ static struct pca953x_platform_data pca953x_77_gpio_data = { /************************************************************************* * I2C Bus *************************************************************************/ -static struct i2c_gpio_platform_data vision_i2c_gpio_data __initdata = { - .sda_pin = EP93XX_GPIO_LINE_EEDAT, - .scl_pin = EP93XX_GPIO_LINE_EECLK, -}; static struct i2c_board_info vision_i2c_info[] __initdata = { { @@ -289,7 +284,7 @@ static void __init vision_init_machine(void) vision_i2c_info[1].irq = gpio_to_irq(EP93XX_GPIO_LINE_F(7)); - ep93xx_register_i2c(&vision_i2c_gpio_data, vision_i2c_info, + ep93xx_register_i2c(vision_i2c_info, ARRAY_SIZE(vision_i2c_info)); ep93xx_register_spi(&vision_spi_master, vision_spi_board_info, ARRAY_SIZE(vision_spi_board_info)); diff --git a/arch/arm/mach-ixp4xx/avila-setup.c b/arch/arm/mach-ixp4xx/avila-setup.c index 6beec150c060..72122b5e7f28 100644 --- a/arch/arm/mach-ixp4xx/avila-setup.c +++ b/arch/arm/mach-ixp4xx/avila-setup.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -49,16 +49,21 @@ static struct platform_device avila_flash = { .resource = &avila_flash_resource, }; -static struct i2c_gpio_platform_data avila_i2c_gpio_data = { - .sda_pin = AVILA_SDA_PIN, - .scl_pin = AVILA_SCL_PIN, +static struct gpiod_lookup_table avila_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", AVILA_SDA_PIN, + NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", AVILA_SCL_PIN, + NULL, 1, GPIO_ACTIVE_HIGH), + }, }; static struct platform_device avila_i2c_gpio = { .name = "i2c-gpio", .id = 0, .dev = { - .platform_data = &avila_i2c_gpio_data, + .platform_data = NULL, }, }; @@ -147,6 +152,8 @@ static void __init avila_init(void) avila_flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; + gpiod_add_lookup_table(&avila_i2c_gpiod_table); + platform_add_devices(avila_devices, ARRAY_SIZE(avila_devices)); avila_pata_resources[0].start = IXP4XX_EXP_BUS_BASE(1); diff --git a/arch/arm/mach-ixp4xx/dsmg600-setup.c b/arch/arm/mach-ixp4xx/dsmg600-setup.c index b3bd0e137f6d..68ccd669051b 100644 --- a/arch/arm/mach-ixp4xx/dsmg600-setup.c +++ b/arch/arm/mach-ixp4xx/dsmg600-setup.c @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include @@ -68,16 +68,21 @@ static struct platform_device dsmg600_flash = { .resource = &dsmg600_flash_resource, }; -static struct i2c_gpio_platform_data dsmg600_i2c_gpio_data = { - .sda_pin = DSMG600_SDA_PIN, - .scl_pin = DSMG600_SCL_PIN, +static struct gpiod_lookup_table dsmg600_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", DSMG600_SDA_PIN, + NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", DSMG600_SCL_PIN, + NULL, 1, GPIO_ACTIVE_HIGH), + }, }; static struct platform_device dsmg600_i2c_gpio = { .name = "i2c-gpio", .id = 0, .dev = { - .platform_data = &dsmg600_i2c_gpio_data, + .platform_data = NULL, }, }; @@ -269,6 +274,7 @@ static void __init dsmg600_init(void) dsmg600_flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; + gpiod_add_lookup_table(&dsmg600_i2c_gpiod_table); i2c_register_board_info(0, dsmg600_i2c_board_info, ARRAY_SIZE(dsmg600_i2c_board_info)); diff --git a/arch/arm/mach-ixp4xx/fsg-setup.c b/arch/arm/mach-ixp4xx/fsg-setup.c index 5c4b0c4a1b37..a0350ad15175 100644 --- a/arch/arm/mach-ixp4xx/fsg-setup.c +++ b/arch/arm/mach-ixp4xx/fsg-setup.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include #include #include @@ -54,16 +54,21 @@ static struct platform_device fsg_flash = { .resource = &fsg_flash_resource, }; -static struct i2c_gpio_platform_data fsg_i2c_gpio_data = { - .sda_pin = FSG_SDA_PIN, - .scl_pin = FSG_SCL_PIN, +static struct gpiod_lookup_table fsg_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", FSG_SDA_PIN, + NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", FSG_SCL_PIN, + NULL, 1, GPIO_ACTIVE_HIGH), + }, }; static struct platform_device fsg_i2c_gpio = { .name = "i2c-gpio", .id = 0, .dev = { - .platform_data = &fsg_i2c_gpio_data, + .platform_data = NULL, }, }; @@ -196,6 +201,7 @@ static void __init fsg_init(void) /* Configure CS2 for operation, 8bit and writable */ *IXP4XX_EXP_CS2 = 0xbfff0002; + gpiod_add_lookup_table(&fsg_i2c_gpiod_table); i2c_register_board_info(0, fsg_i2c_board_info, ARRAY_SIZE(fsg_i2c_board_info)); diff --git a/arch/arm/mach-ixp4xx/goramo_mlr.c b/arch/arm/mach-ixp4xx/goramo_mlr.c index 80bd9d6d04de..f1529aa3f8e2 100644 --- a/arch/arm/mach-ixp4xx/goramo_mlr.c +++ b/arch/arm/mach-ixp4xx/goramo_mlr.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -78,6 +77,12 @@ static u32 hw_bits = 0xFFFFFFFD; /* assume all hardware present */; static u8 control_value; +/* + * FIXME: this is reimplementing I2C bit-bangining. Move this + * over to using driver/i2c/busses/i2c-gpio.c like all other boards + * and register proper I2C device(s) on the bus for this. (See + * other IXP4xx boards for examples.) + */ static void set_scl(u8 value) { gpio_set_value(GPIO_SCL, !!value); @@ -216,20 +221,6 @@ static struct platform_device device_flash = { .resource = &flash_resource, }; - -/* I^2C interface */ -static struct i2c_gpio_platform_data i2c_data = { - .sda_pin = GPIO_SDA, - .scl_pin = GPIO_SCL, -}; - -static struct platform_device device_i2c = { - .name = "i2c-gpio", - .id = 0, - .dev = { .platform_data = &i2c_data }, -}; - - /* IXP425 2 UART ports */ static struct resource uart_resources[] = { { @@ -411,9 +402,6 @@ static void __init gmlr_init(void) if (hw_bits & CFG_HW_HAS_HSS1) device_tab[devices++] = &device_hss_tab[1]; /* max index 5 */ - if (hw_bits & CFG_HW_HAS_EEPROM) - device_tab[devices++] = &device_i2c; /* max index 6 */ - gpio_request(GPIO_SCL, "SCL/clock"); gpio_request(GPIO_SDA, "SDA/data"); gpio_request(GPIO_STR, "strobe"); diff --git a/arch/arm/mach-ixp4xx/ixdp425-setup.c b/arch/arm/mach-ixp4xx/ixdp425-setup.c index 93b89291c06b..8937263cec4a 100644 --- a/arch/arm/mach-ixp4xx/ixdp425-setup.c +++ b/arch/arm/mach-ixp4xx/ixdp425-setup.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -122,16 +122,21 @@ static struct platform_device ixdp425_flash_nand = { }; #endif /* CONFIG_MTD_NAND_PLATFORM */ -static struct i2c_gpio_platform_data ixdp425_i2c_gpio_data = { - .sda_pin = IXDP425_SDA_PIN, - .scl_pin = IXDP425_SCL_PIN, +static struct gpiod_lookup_table ixdp425_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", IXDP425_SDA_PIN, + NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", IXDP425_SCL_PIN, + NULL, 1, GPIO_ACTIVE_HIGH), + }, }; static struct platform_device ixdp425_i2c_gpio = { .name = "i2c-gpio", .id = 0, .dev = { - .platform_data = &ixdp425_i2c_gpio_data, + .platform_data = NULL, }, }; @@ -245,6 +250,7 @@ static void __init ixdp425_init(void) ixdp425_uart_data[1].flags = 0; } + gpiod_add_lookup_table(&ixdp425_i2c_gpiod_table); platform_add_devices(ixdp425_devices, ARRAY_SIZE(ixdp425_devices)); } diff --git a/arch/arm/mach-ixp4xx/nas100d-setup.c b/arch/arm/mach-ixp4xx/nas100d-setup.c index 4e0f762bc651..612ec8c63456 100644 --- a/arch/arm/mach-ixp4xx/nas100d-setup.c +++ b/arch/arm/mach-ixp4xx/nas100d-setup.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include @@ -100,16 +100,21 @@ static struct platform_device nas100d_leds = { .dev.platform_data = &nas100d_led_data, }; -static struct i2c_gpio_platform_data nas100d_i2c_gpio_data = { - .sda_pin = NAS100D_SDA_PIN, - .scl_pin = NAS100D_SCL_PIN, +static struct gpiod_lookup_table nas100d_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", NAS100D_SDA_PIN, + NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", NAS100D_SCL_PIN, + NULL, 1, GPIO_ACTIVE_HIGH), + }, }; static struct platform_device nas100d_i2c_gpio = { .name = "i2c-gpio", .id = 0, .dev = { - .platform_data = &nas100d_i2c_gpio_data, + .platform_data = NULL, }, }; @@ -280,6 +285,7 @@ static void __init nas100d_init(void) nas100d_flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; + gpiod_add_lookup_table(&nas100d_i2c_gpiod_table); i2c_register_board_info(0, nas100d_i2c_board_info, ARRAY_SIZE(nas100d_i2c_board_info)); diff --git a/arch/arm/mach-ixp4xx/nslu2-setup.c b/arch/arm/mach-ixp4xx/nslu2-setup.c index 88c025f52d8d..13afb03b50fa 100644 --- a/arch/arm/mach-ixp4xx/nslu2-setup.c +++ b/arch/arm/mach-ixp4xx/nslu2-setup.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -68,9 +68,14 @@ static struct platform_device nslu2_flash = { .resource = &nslu2_flash_resource, }; -static struct i2c_gpio_platform_data nslu2_i2c_gpio_data = { - .sda_pin = NSLU2_SDA_PIN, - .scl_pin = NSLU2_SCL_PIN, +static struct gpiod_lookup_table nslu2_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", NSLU2_SDA_PIN, + NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("IXP4XX_GPIO_CHIP", NSLU2_SCL_PIN, + NULL, 1, GPIO_ACTIVE_HIGH), + }, }; static struct i2c_board_info __initdata nslu2_i2c_board_info [] = { @@ -115,7 +120,7 @@ static struct platform_device nslu2_i2c_gpio = { .name = "i2c-gpio", .id = 0, .dev = { - .platform_data = &nslu2_i2c_gpio_data, + .platform_data = NULL, }, }; @@ -250,6 +255,7 @@ static void __init nslu2_init(void) nslu2_flash_resource.end = IXP4XX_EXP_BUS_BASE(0) + ixp4xx_exp_bus_size - 1; + gpiod_add_lookup_table(&nslu2_i2c_gpiod_table); i2c_register_board_info(0, nslu2_i2c_board_info, ARRAY_SIZE(nslu2_i2c_board_info)); diff --git a/arch/arm/mach-ks8695/board-acs5k.c b/arch/arm/mach-ks8695/board-acs5k.c index e4d709c8ed32..f034724e01e1 100644 --- a/arch/arm/mach-ks8695/board-acs5k.c +++ b/arch/arm/mach-ks8695/board-acs5k.c @@ -16,7 +16,7 @@ #include #include #include - +#include #include #include #include @@ -38,9 +38,15 @@ #include "generic.h" +static struct gpiod_lookup_table acs5k_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("KS8695", 4, NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("KS8695", 5, NULL, 1, GPIO_ACTIVE_HIGH), + }, +}; + static struct i2c_gpio_platform_data acs5k_i2c_device_platdata = { - .sda_pin = 4, - .scl_pin = 5, .udelay = 10, }; @@ -95,6 +101,7 @@ static struct i2c_board_info acs5k_i2c_devs[] __initdata = { static void acs5k_i2c_init(void) { /* The gpio interface */ + gpiod_add_lookup_table(&acs5k_i2c_gpiod_table); platform_device_register(&acs5k_i2c_device); /* I2C devices */ i2c_register_board_info(0, acs5k_i2c_devs, diff --git a/arch/arm/mach-pxa/palmz72.c b/arch/arm/mach-pxa/palmz72.c index 29630061e700..94f75632c007 100644 --- a/arch/arm/mach-pxa/palmz72.c +++ b/arch/arm/mach-pxa/palmz72.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -320,9 +321,15 @@ static struct soc_camera_link palmz72_iclink = { .flags = SOCAM_DATAWIDTH_8, }; +static struct gpiod_lookup_table palmz72_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("gpio-pxa", 118, NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("gpio-pxa", 117, NULL, 1, GPIO_ACTIVE_HIGH), + }, +}; + static struct i2c_gpio_platform_data palmz72_i2c_bus_data = { - .sda_pin = 118, - .scl_pin = 117, .udelay = 10, .timeout = 100, }; @@ -369,6 +376,7 @@ static void __init palmz72_camera_init(void) { palmz72_cam_gpio_init(); pxa_set_camera_info(&palmz72_pxacamera_platform_data); + gpiod_add_lookup_table(&palmz72_i2c_gpiod_table); platform_device_register(&palmz72_i2c_bus_device); platform_device_register(&palmz72_camera); } diff --git a/arch/arm/mach-pxa/viper.c b/arch/arm/mach-pxa/viper.c index 8e89d91b206b..a680742bee2b 100644 --- a/arch/arm/mach-pxa/viper.c +++ b/arch/arm/mach-pxa/viper.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -458,9 +459,17 @@ static struct platform_device smc91x_device = { }; /* i2c */ +static struct gpiod_lookup_table viper_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("gpio-pxa", VIPER_RTC_I2C_SDA_GPIO, + NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("gpio-pxa", VIPER_RTC_I2C_SCL_GPIO, + NULL, 1, GPIO_ACTIVE_HIGH), + }, +}; + static struct i2c_gpio_platform_data i2c_bus_data = { - .sda_pin = VIPER_RTC_I2C_SDA_GPIO, - .scl_pin = VIPER_RTC_I2C_SCL_GPIO, .udelay = 10, .timeout = HZ, }; @@ -779,12 +788,20 @@ static int __init viper_tpm_setup(char *str) __setup("tpm=", viper_tpm_setup); +struct gpiod_lookup_table viper_tpm_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("gpio-pxa", VIPER_TPM_I2C_SDA_GPIO, + NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("gpio-pxa", VIPER_TPM_I2C_SCL_GPIO, + NULL, 1, GPIO_ACTIVE_HIGH), + }, +}; + static void __init viper_tpm_init(void) { struct platform_device *tpm_device; struct i2c_gpio_platform_data i2c_tpm_data = { - .sda_pin = VIPER_TPM_I2C_SDA_GPIO, - .scl_pin = VIPER_TPM_I2C_SCL_GPIO, .udelay = 10, .timeout = HZ, }; @@ -794,6 +811,7 @@ static void __init viper_tpm_init(void) if (!viper_tpm) return; + gpiod_add_lookup_table(&viper_tpm_i2c_gpiod_table); tpm_device = platform_device_alloc("i2c-gpio", 2); if (tpm_device) { if (!platform_device_add_data(tpm_device, @@ -943,6 +961,7 @@ static void __init viper_init(void) smc91x_device.num_resources--; pxa_set_i2c_info(NULL); + gpiod_add_lookup_table(&viper_i2c_gpiod_table); pwm_add_table(viper_pwm_lookup, ARRAY_SIZE(viper_pwm_lookup)); platform_add_devices(viper_devs, ARRAY_SIZE(viper_devs)); diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c index bb3ca9c763de..c6e7e6d8733a 100644 --- a/arch/arm/mach-sa1100/simpad.c +++ b/arch/arm/mach-sa1100/simpad.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -323,9 +324,15 @@ static struct platform_device simpad_gpio_leds = { /* * i2c */ +static struct gpiod_lookup_table simpad_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("gpio", GPIO_GPIO21, NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("gpio", GPIO_GPIO25, NULL, 1, GPIO_ACTIVE_HIGH), + }, +}; + static struct i2c_gpio_platform_data simpad_i2c_data = { - .sda_pin = GPIO_GPIO21, - .scl_pin = GPIO_GPIO25, .udelay = 10, .timeout = HZ, }; @@ -380,6 +387,7 @@ static int __init simpad_init(void) ARRAY_SIZE(simpad_flash_resources)); sa11x0_register_mcp(&simpad_mcp_data); + gpiod_add_lookup_table(&simpad_i2c_gpiod_table); ret = platform_add_devices(devices, ARRAY_SIZE(devices)); if(ret) printk(KERN_WARNING "simpad: Unable to register mq200 framebuffer device"); diff --git a/arch/blackfin/mach-bf533/boards/blackstamp.c b/arch/blackfin/mach-bf533/boards/blackstamp.c index 0ccf0cf4daaf..d801ca5ca6c4 100644 --- a/arch/blackfin/mach-bf533/boards/blackstamp.c +++ b/arch/blackfin/mach-bf533/boards/blackstamp.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -362,11 +363,17 @@ static struct platform_device bfin_device_gpiokeys = { #if IS_ENABLED(CONFIG_I2C_GPIO) #include +static struct gpiod_lookup_table bfin_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("BFIN-GPIO", GPIO_PF8, NULL, 0, + GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("BFIN-GPIO", GPIO_PF9, NULL, 1, + GPIO_ACTIVE_HIGH), + }, +}; + static struct i2c_gpio_platform_data i2c_gpio_data = { - .sda_pin = GPIO_PF8, - .scl_pin = GPIO_PF9, - .sda_is_open_drain = 0, - .scl_is_open_drain = 0, .udelay = 40, }; /* This hasn't actually been used these pins * are (currently) free pins on the expansion connector */ @@ -462,7 +469,9 @@ static int __init blackstamp_init(void) int ret; printk(KERN_INFO "%s(): registering device resources\n", __func__); - +#if IS_ENABLED(CONFIG_I2C_GPIO) + gpiod_add_lookup_table(&bfin_i2c_gpiod_table); +#endif i2c_register_board_info(0, bfin_i2c_board_info, ARRAY_SIZE(bfin_i2c_board_info)); diff --git a/arch/blackfin/mach-bf533/boards/ezkit.c b/arch/blackfin/mach-bf533/boards/ezkit.c index 3625e9eaa8a8..463a72358b0e 100644 --- a/arch/blackfin/mach-bf533/boards/ezkit.c +++ b/arch/blackfin/mach-bf533/boards/ezkit.c @@ -19,6 +19,7 @@ #endif #include #include +#include #include #include #include @@ -390,11 +391,17 @@ static struct platform_device bfin_device_gpiokeys = { #if IS_ENABLED(CONFIG_I2C_GPIO) #include +static struct gpiod_lookup_table bfin_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("BFIN-GPIO", GPIO_PF1, NULL, 0, + GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("BFIN-GPIO", GPIO_PF0, NULL, 1, + GPIO_ACTIVE_HIGH), + }, +}; + static struct i2c_gpio_platform_data i2c_gpio_data = { - .sda_pin = GPIO_PF1, - .scl_pin = GPIO_PF0, - .sda_is_open_drain = 0, - .scl_is_open_drain = 0, .udelay = 40, }; @@ -516,6 +523,9 @@ static struct platform_device *ezkit_devices[] __initdata = { static int __init ezkit_init(void) { printk(KERN_INFO "%s(): registering device resources\n", __func__); +#if IS_ENABLED(CONFIG_I2C_GPIO) + gpiod_add_lookup_table(&bfin_i2c_gpiod_table); +#endif platform_add_devices(ezkit_devices, ARRAY_SIZE(ezkit_devices)); spi_register_board_info(bfin_spi_board_info, ARRAY_SIZE(bfin_spi_board_info)); i2c_register_board_info(0, bfin_i2c_board_info, diff --git a/arch/blackfin/mach-bf533/boards/stamp.c b/arch/blackfin/mach-bf533/boards/stamp.c index 23eada79439c..d2479359adb7 100644 --- a/arch/blackfin/mach-bf533/boards/stamp.c +++ b/arch/blackfin/mach-bf533/boards/stamp.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -512,11 +513,17 @@ static struct platform_device bfin_device_gpiokeys = { #if IS_ENABLED(CONFIG_I2C_GPIO) #include +static struct gpiod_lookup_table bfin_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("BFIN-GPIO", GPIO_PF2, NULL, 0, + GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("BFIN-GPIO", GPIO_PF3, NULL, 1, + GPIO_ACTIVE_HIGH), + }, +}; + static struct i2c_gpio_platform_data i2c_gpio_data = { - .sda_pin = GPIO_PF2, - .scl_pin = GPIO_PF3, - .sda_is_open_drain = 0, - .scl_is_open_drain = 0, .udelay = 10, }; @@ -848,6 +855,9 @@ static int __init stamp_init(void) printk(KERN_INFO "%s(): registering device resources\n", __func__); +#if IS_ENABLED(CONFIG_I2C_GPIO) + gpiod_add_lookup_table(&bfin_i2c_gpiod_table); +#endif i2c_register_board_info(0, bfin_i2c_board_info, ARRAY_SIZE(bfin_i2c_board_info)); diff --git a/arch/blackfin/mach-bf561/boards/ezkit.c b/arch/blackfin/mach-bf561/boards/ezkit.c index 57d1c43726d9..72f757ebaa84 100644 --- a/arch/blackfin/mach-bf561/boards/ezkit.c +++ b/arch/blackfin/mach-bf561/boards/ezkit.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -379,11 +380,17 @@ static struct platform_device bfin_device_gpiokeys = { #if IS_ENABLED(CONFIG_I2C_GPIO) #include +static struct gpiod_lookup_table bfin_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("BFIN-GPIO", GPIO_PF1, NULL, 0, + GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("BFIN-GPIO", GPIO_PF0, NULL, 1, + GPIO_ACTIVE_HIGH), + }, +}; + static struct i2c_gpio_platform_data i2c_gpio_data = { - .sda_pin = GPIO_PF1, - .scl_pin = GPIO_PF0, - .sda_is_open_drain = 0, - .scl_is_open_drain = 0, .udelay = 10, }; @@ -633,6 +640,9 @@ static int __init ezkit_init(void) printk(KERN_INFO "%s(): registering device resources\n", __func__); +#if IS_ENABLED(CONFIG_I2C_GPIO) + gpiod_add_lookup_table(&bfin_i2c_gpiod_table); +#endif ret = platform_add_devices(ezkit_devices, ARRAY_SIZE(ezkit_devices)); if (ret < 0) return ret; diff --git a/arch/mips/alchemy/board-gpr.c b/arch/mips/alchemy/board-gpr.c index 6fb6b3faa158..daebc36e5ecb 100644 --- a/arch/mips/alchemy/board-gpr.c +++ b/arch/mips/alchemy/board-gpr.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -218,10 +219,23 @@ static struct platform_device gpr_led_devices = { /* * I2C */ +static struct gpiod_lookup_table gpr_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + /* + * This should be on "GPIO2" which has base at 200 so + * the global numbers 209 and 210 should correspond to + * local offsets 9 and 10. + */ + GPIO_LOOKUP_IDX("alchemy-gpio2", 9, NULL, 0, + GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("alchemy-gpio2", 10, NULL, 1, + GPIO_ACTIVE_HIGH), + }, +}; + static struct i2c_gpio_platform_data gpr_i2c_data = { - .sda_pin = 209, .sda_is_open_drain = 1, - .scl_pin = 210, .scl_is_open_drain = 1, .udelay = 2, /* ~100 kHz */ .timeout = HZ, @@ -295,6 +309,7 @@ arch_initcall(gpr_pci_init); static int __init gpr_dev_init(void) { + gpiod_add_lookup_table(&gpr_i2c_gpiod_table); i2c_register_board_info(0, gpr_i2c_info, ARRAY_SIZE(gpr_i2c_info)); return platform_add_devices(gpr_devices, ARRAY_SIZE(gpr_devices)); diff --git a/arch/mips/ath79/mach-pb44.c b/arch/mips/ath79/mach-pb44.c index be78298dffb4..a95409063847 100644 --- a/arch/mips/ath79/mach-pb44.c +++ b/arch/mips/ath79/mach-pb44.c @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include "machtypes.h" @@ -33,16 +33,21 @@ #define PB44_KEYS_POLL_INTERVAL 20 /* msecs */ #define PB44_KEYS_DEBOUNCE_INTERVAL (3 * PB44_KEYS_POLL_INTERVAL) -static struct i2c_gpio_platform_data pb44_i2c_gpio_data = { - .sda_pin = PB44_GPIO_I2C_SDA, - .scl_pin = PB44_GPIO_I2C_SCL, +static struct gpiod_lookup_table pb44_i2c_gpiod_table = { + .dev_id = "i2c-gpio", + .table = { + GPIO_LOOKUP_IDX("ath79-gpio", PB44_GPIO_I2C_SDA, + NULL, 0, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP_IDX("ath79-gpio", PB44_GPIO_I2C_SCL, + NULL, 1, GPIO_ACTIVE_HIGH), + }, }; static struct platform_device pb44_i2c_gpio_device = { .name = "i2c-gpio", .id = 0, .dev = { - .platform_data = &pb44_i2c_gpio_data, + .platform_data = NULL, } }; @@ -103,6 +108,7 @@ static struct ath79_spi_platform_data pb44_spi_data = { static void __init pb44_init(void) { + gpiod_add_lookup_table(&pb44_i2c_gpiod_table); i2c_register_board_info(0, pb44_i2c_board_info, ARRAY_SIZE(pb44_i2c_board_info)); platform_device_register(&pb44_i2c_gpio_device); diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c index 0ef8fcc6ac3a..b4664037eded 100644 --- a/drivers/i2c/busses/i2c-gpio.c +++ b/drivers/i2c/busses/i2c-gpio.c @@ -14,11 +14,12 @@ #include #include #include -#include +#include #include -#include struct i2c_gpio_private_data { + struct gpio_desc *sda; + struct gpio_desc *scl; struct i2c_adapter adap; struct i2c_algo_bit_data bit_data; struct i2c_gpio_platform_data pdata; @@ -27,12 +28,18 @@ struct i2c_gpio_private_data { /* Toggle SDA by changing the direction of the pin */ static void i2c_gpio_setsda_dir(void *data, int state) { - struct i2c_gpio_platform_data *pdata = data; - + struct i2c_gpio_private_data *priv = data; + + /* + * This is a way of saying "do not drive + * me actively high" which means emulating open drain. + * The right way to do this is for gpiolib to + * handle this, by the function below. + */ if (state) - gpio_direction_input(pdata->sda_pin); + gpiod_direction_input(priv->sda); else - gpio_direction_output(pdata->sda_pin, 0); + gpiod_direction_output(priv->sda, 0); } /* @@ -42,20 +49,20 @@ static void i2c_gpio_setsda_dir(void *data, int state) */ static void i2c_gpio_setsda_val(void *data, int state) { - struct i2c_gpio_platform_data *pdata = data; + struct i2c_gpio_private_data *priv = data; - gpio_set_value(pdata->sda_pin, state); + gpiod_set_value(priv->sda, state); } /* Toggle SCL by changing the direction of the pin. */ static void i2c_gpio_setscl_dir(void *data, int state) { - struct i2c_gpio_platform_data *pdata = data; + struct i2c_gpio_private_data *priv = data; if (state) - gpio_direction_input(pdata->scl_pin); + gpiod_direction_input(priv->scl); else - gpio_direction_output(pdata->scl_pin, 0); + gpiod_direction_output(priv->scl, 0); } /* @@ -66,44 +73,23 @@ static void i2c_gpio_setscl_dir(void *data, int state) */ static void i2c_gpio_setscl_val(void *data, int state) { - struct i2c_gpio_platform_data *pdata = data; + struct i2c_gpio_private_data *priv = data; - gpio_set_value(pdata->scl_pin, state); + gpiod_set_value(priv->scl, state); } static int i2c_gpio_getsda(void *data) { - struct i2c_gpio_platform_data *pdata = data; + struct i2c_gpio_private_data *priv = data; - return gpio_get_value(pdata->sda_pin); + return gpiod_get_value(priv->sda); } static int i2c_gpio_getscl(void *data) { - struct i2c_gpio_platform_data *pdata = data; + struct i2c_gpio_private_data *priv = data; - return gpio_get_value(pdata->scl_pin); -} - -static int of_i2c_gpio_get_pins(struct device_node *np, - unsigned int *sda_pin, unsigned int *scl_pin) -{ - if (of_gpio_count(np) < 2) - return -ENODEV; - - *sda_pin = of_get_gpio(np, 0); - *scl_pin = of_get_gpio(np, 1); - - if (*sda_pin == -EPROBE_DEFER || *scl_pin == -EPROBE_DEFER) - return -EPROBE_DEFER; - - if (!gpio_is_valid(*sda_pin) || !gpio_is_valid(*scl_pin)) { - pr_err("%pOF: invalid GPIO pins, sda=%d/scl=%d\n", - np, *sda_pin, *scl_pin); - return -ENODEV; - } - - return 0; + return gpiod_get_value(priv->scl); } static void of_i2c_gpio_get_props(struct device_node *np, @@ -130,64 +116,65 @@ static int i2c_gpio_probe(struct platform_device *pdev) struct i2c_gpio_platform_data *pdata; struct i2c_algo_bit_data *bit_data; struct i2c_adapter *adap; - unsigned int sda_pin, scl_pin; int ret; - /* First get the GPIO pins; if it fails, we'll defer the probe. */ - if (pdev->dev.of_node) { - ret = of_i2c_gpio_get_pins(pdev->dev.of_node, - &sda_pin, &scl_pin); - if (ret) - return ret; - } else { - if (!dev_get_platdata(&pdev->dev)) - return -ENXIO; - pdata = dev_get_platdata(&pdev->dev); - sda_pin = pdata->sda_pin; - scl_pin = pdata->scl_pin; - } + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - ret = devm_gpio_request(&pdev->dev, sda_pin, "sda"); - if (ret) { + /* First get the GPIO pins; if it fails, we'll defer the probe. */ + priv->sda = devm_gpiod_get_index(&pdev->dev, NULL, 0, GPIOD_OUT_HIGH); + if (IS_ERR(priv->sda)) { + ret = PTR_ERR(priv->sda); + /* FIXME: hack in the old code, is this really necessary? */ if (ret == -EINVAL) - ret = -EPROBE_DEFER; /* Try again later */ + ret = -EPROBE_DEFER; return ret; } - ret = devm_gpio_request(&pdev->dev, scl_pin, "scl"); - if (ret) { + priv->scl = devm_gpiod_get_index(&pdev->dev, NULL, 1, GPIOD_OUT_LOW); + if (IS_ERR(priv->scl)) { + ret = PTR_ERR(priv->scl); + /* FIXME: hack in the old code, is this really necessary? */ if (ret == -EINVAL) - ret = -EPROBE_DEFER; /* Try again later */ + ret = -EPROBE_DEFER; return ret; } - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; adap = &priv->adap; bit_data = &priv->bit_data; pdata = &priv->pdata; if (pdev->dev.of_node) { - pdata->sda_pin = sda_pin; - pdata->scl_pin = scl_pin; of_i2c_gpio_get_props(pdev->dev.of_node, pdata); } else { - memcpy(pdata, dev_get_platdata(&pdev->dev), sizeof(*pdata)); + /* + * If all platform data settings are zero it is OK + * to not provide any platform data from the board. + */ + if (dev_get_platdata(&pdev->dev)) + memcpy(pdata, dev_get_platdata(&pdev->dev), + sizeof(*pdata)); } + /* + * FIXME: this is a hack emulating the open drain emulation + * that gpiolib can already do for us. Make all clients properly + * flag their lines as open drain and get rid of this property + * and the special callback. + */ if (pdata->sda_is_open_drain) { - gpio_direction_output(pdata->sda_pin, 1); + gpiod_direction_output(priv->sda, 1); bit_data->setsda = i2c_gpio_setsda_val; } else { - gpio_direction_input(pdata->sda_pin); + gpiod_direction_input(priv->sda); bit_data->setsda = i2c_gpio_setsda_dir; } if (pdata->scl_is_open_drain || pdata->scl_is_output_only) { - gpio_direction_output(pdata->scl_pin, 1); + gpiod_direction_output(priv->scl, 1); bit_data->setscl = i2c_gpio_setscl_val; } else { - gpio_direction_input(pdata->scl_pin); + gpiod_direction_input(priv->scl); bit_data->setscl = i2c_gpio_setscl_dir; } @@ -207,7 +194,7 @@ static int i2c_gpio_probe(struct platform_device *pdev) else bit_data->timeout = HZ / 10; /* 100 ms */ - bit_data->data = pdata; + bit_data->data = priv; adap->owner = THIS_MODULE; if (pdev->dev.of_node) @@ -227,8 +214,13 @@ static int i2c_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); - dev_info(&pdev->dev, "using pins %u (SDA) and %u (SCL%s)\n", - pdata->sda_pin, pdata->scl_pin, + /* + * FIXME: using global GPIO numbers is not helpful. If/when we + * get accessors to get the actual name of the GPIO line, + * from the descriptor, then provide that instead. + */ + dev_info(&pdev->dev, "using lines %u (SDA) and %u (SCL%s)\n", + desc_to_gpio(priv->sda), desc_to_gpio(priv->scl), pdata->scl_is_output_only ? ", no clock stretching" : ""); diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index 40534352e574..4d40d013a412 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -1107,14 +1108,6 @@ static void sm501_gpio_remove(struct sm501_devdata *sm) kfree(gpio->regs_res); } -static inline int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin) -{ - struct sm501_gpio *gpio = &sm->gpio; - int base = (pin < 32) ? gpio->low.gpio.base : gpio->high.gpio.base; - - return (pin % 32) + base; -} - static inline int sm501_gpio_isregistered(struct sm501_devdata *sm) { return sm->gpio.registered; @@ -1129,11 +1122,6 @@ static inline void sm501_gpio_remove(struct sm501_devdata *sm) { } -static inline int sm501_gpio_pin2nr(struct sm501_devdata *sm, unsigned int pin) -{ - return -1; -} - static inline int sm501_gpio_isregistered(struct sm501_devdata *sm) { return 0; @@ -1145,20 +1133,37 @@ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, { struct i2c_gpio_platform_data *icd; struct platform_device *pdev; + struct gpiod_lookup_table *lookup; pdev = sm501_create_subdev(sm, "i2c-gpio", 0, sizeof(struct i2c_gpio_platform_data)); if (!pdev) return -ENOMEM; - icd = dev_get_platdata(&pdev->dev); - - /* We keep the pin_sda and pin_scl fields relative in case the - * same platform data is passed to >1 SM501. - */ + /* Create a gpiod lookup using gpiochip-local offsets */ + lookup = devm_kzalloc(&pdev->dev, + sizeof(*lookup) + 3 * sizeof(struct gpiod_lookup), + GFP_KERNEL); + lookup->dev_id = "i2c-gpio"; + if (iic->pin_sda < 32) + lookup->table[0].chip_label = "SM501-LOW"; + else + lookup->table[0].chip_label = "SM501-HIGH"; + lookup->table[0].chip_hwnum = iic->pin_sda % 32; + lookup->table[0].con_id = NULL; + lookup->table[0].idx = 0; + lookup->table[0].flags = GPIO_ACTIVE_HIGH; + if (iic->pin_scl < 32) + lookup->table[1].chip_label = "SM501-LOW"; + else + lookup->table[1].chip_label = "SM501-HIGH"; + lookup->table[1].chip_hwnum = iic->pin_scl % 32; + lookup->table[1].con_id = NULL; + lookup->table[1].idx = 1; + lookup->table[1].flags = GPIO_ACTIVE_HIGH; + gpiod_add_lookup_table(lookup); - icd->sda_pin = sm501_gpio_pin2nr(sm, iic->pin_sda); - icd->scl_pin = sm501_gpio_pin2nr(sm, iic->pin_scl); + icd = dev_get_platdata(&pdev->dev); icd->timeout = iic->timeout; icd->udelay = iic->udelay; @@ -1170,9 +1175,9 @@ static int sm501_register_gpio_i2c_instance(struct sm501_devdata *sm, pdev->id = iic->bus_num; - dev_info(sm->dev, "registering i2c-%d: sda=%d (%d), scl=%d (%d)\n", + dev_info(sm->dev, "registering i2c-%d: sda=%d, scl=%d\n", iic->bus_num, - icd->sda_pin, iic->pin_sda, icd->scl_pin, iic->pin_scl); + iic->pin_sda, iic->pin_scl); return sm501_register_device(sm, pdev); } diff --git a/include/linux/i2c-gpio.h b/include/linux/i2c-gpio.h index c1bcb1f1d73b..352c1426fd4d 100644 --- a/include/linux/i2c-gpio.h +++ b/include/linux/i2c-gpio.h @@ -12,8 +12,6 @@ /** * struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio - * @sda_pin: GPIO pin ID to use for SDA - * @scl_pin: GPIO pin ID to use for SCL * @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz * @timeout: clock stretching timeout in jiffies. If the slave keeps * SCL low for longer than this, the transfer will time out. @@ -26,8 +24,6 @@ * @scl_is_output_only: SCL output drivers cannot be turned off. */ struct i2c_gpio_platform_data { - unsigned int sda_pin; - unsigned int scl_pin; int udelay; int timeout; unsigned int sda_is_open_drain:1; -- cgit v1.2.3-59-g8ed1b From f926dfc112bc6cf41d7068ee5e3f261e13a5bec8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 10 Sep 2017 19:26:22 +0200 Subject: gpio: Make it possible for consumers to enforce open drain Some busses, like I2C, strictly need to have the line handled as open drain, i.e. not actively driven high. For this reason the i2c-gpio.c bit-banged I2C driver is reimplementing open drain handling outside of gpiolib. This is not very optimal. Instead make it possible for a consumer to explcitly express that the line must be handled as open drain instead of allowing local hacks papering over this issue. The descriptor tables, whether DT, ACPI or board files, should of course have flagged these lines as open drain. E.g.: enum gpio_lookup_flags GPIO_OPEN_DRAIN for a board file, or gpios = <&foo 42 GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN>; in a device tree using But more often than not, these descriptors are wrong. So we need to make it possible for consumers to enforce this open drain behaviour. We now have two new enumerated GPIO descriptor config flags: GPIOD_OUT_LOW_OPEN_DRAIN and GPIOD_OUT_HIGH_OPEN_DRAIN that will set up the lined enforced as open drain as output low or high, using open drain (if the driver supports it) or using open drain emulation (setting the line as input to drive it high) from the gpiolib core. Cc: linux-gpio@vger.kernel.org Tested-by: Geert Uytterhoeven Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 13 +++++++++++++ include/linux/gpio/consumer.h | 6 ++++++ 2 files changed, 19 insertions(+) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index eb80dac4e26a..c952ef1b2f46 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3264,8 +3264,21 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, if (lflags & GPIO_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (lflags & GPIO_OPEN_DRAIN) set_bit(FLAG_OPEN_DRAIN, &desc->flags); + else if (dflags & GPIOD_FLAGS_BIT_OPEN_DRAIN) { + /* + * This enforces open drain mode from the consumer side. + * This is necessary for some busses like I2C, but the lookup + * should *REALLY* have specified them as open drain in the + * first place, so print a little warning here. + */ + set_bit(FLAG_OPEN_DRAIN, &desc->flags); + gpiod_warn(desc, + "enforced open drain please flag it properly in DT/ACPI DSDT/board file\n"); + } + if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); if (lflags & GPIO_SLEEP_MAY_LOOSE_VALUE) diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 8f702fcbe485..5f72a49d1aa3 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -28,6 +28,7 @@ struct gpio_descs { #define GPIOD_FLAGS_BIT_DIR_SET BIT(0) #define GPIOD_FLAGS_BIT_DIR_OUT BIT(1) #define GPIOD_FLAGS_BIT_DIR_VAL BIT(2) +#define GPIOD_FLAGS_BIT_OPEN_DRAIN BIT(3) /** * Optional flags that can be passed to one of gpiod_* to configure direction @@ -39,6 +40,11 @@ enum gpiod_flags { GPIOD_OUT_LOW = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT, GPIOD_OUT_HIGH = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT | GPIOD_FLAGS_BIT_DIR_VAL, + GPIOD_OUT_LOW_OPEN_DRAIN = GPIOD_FLAGS_BIT_DIR_SET | + GPIOD_FLAGS_BIT_DIR_OUT | GPIOD_FLAGS_BIT_OPEN_DRAIN, + GPIOD_OUT_HIGH_OPEN_DRAIN = GPIOD_FLAGS_BIT_DIR_SET | + GPIOD_FLAGS_BIT_DIR_OUT | GPIOD_FLAGS_BIT_DIR_VAL | + GPIOD_FLAGS_BIT_OPEN_DRAIN, }; #ifdef CONFIG_GPIOLIB -- cgit v1.2.3-59-g8ed1b From 2064ee332e4c1b7495cf68b84355c213d8fe71fd Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 30 Oct 2017 10:42:59 +0100 Subject: Bluetooth: Use bt_dev_err and bt_dev_info when possible In case of using BT_ERR and BT_INFO, convert to bt_dev_err and bt_dev_info when possible. This allows for controller specific reporting. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/bpa10x.c | 15 ++-- drivers/bluetooth/bt3c_cs.c | 2 +- drivers/bluetooth/btbcm.c | 103 ++++++++++++------------- drivers/bluetooth/btintel.c | 42 +++++------ drivers/bluetooth/btqca.c | 6 +- drivers/bluetooth/btrtl.c | 21 +++--- drivers/bluetooth/btusb.c | 155 ++++++++++++++++++-------------------- drivers/bluetooth/hci_ath.c | 2 +- drivers/bluetooth/hci_h4.c | 2 +- drivers/bluetooth/hci_qca.c | 12 +-- drivers/bluetooth/hci_serdev.c | 9 +-- include/net/bluetooth/bluetooth.h | 3 + net/bluetooth/a2mp.c | 2 +- net/bluetooth/amp.c | 4 +- net/bluetooth/hci_conn.c | 4 +- net/bluetooth/hci_core.c | 35 +++++---- net/bluetooth/hci_event.c | 46 +++++------ net/bluetooth/hci_request.c | 16 ++-- net/bluetooth/hci_sysfs.c | 2 +- net/bluetooth/mgmt.c | 56 +++++++------- net/bluetooth/smp.c | 22 +++--- 21 files changed, 279 insertions(+), 280 deletions(-) (limited to 'include') diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 48d10cb5c9a1..7971bfbd4321 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -117,7 +117,7 @@ static void bpa10x_rx_complete(struct urb *urb) bpa10x_recv_pkts, ARRAY_SIZE(bpa10x_recv_pkts)); if (IS_ERR(data->rx_skb[idx])) { - BT_ERR("%s corrupted event packet", hdev->name); + bt_dev_err(hdev, "corrupted event packet"); hdev->stat.err_rx++; data->rx_skb[idx] = NULL; } @@ -127,8 +127,7 @@ static void bpa10x_rx_complete(struct urb *urb) err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { - BT_ERR("%s urb %p failed to resubmit (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p failed to resubmit (%d)", urb, -err); usb_unanchor_urb(urb); } } @@ -164,8 +163,7 @@ static inline int bpa10x_submit_intr_urb(struct hci_dev *hdev) err = usb_submit_urb(urb, GFP_KERNEL); if (err < 0) { - BT_ERR("%s urb %p submission failed (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p submission failed (%d)", urb, -err); usb_unanchor_urb(urb); } @@ -205,8 +203,7 @@ static inline int bpa10x_submit_bulk_urb(struct hci_dev *hdev) err = usb_submit_urb(urb, GFP_KERNEL); if (err < 0) { - BT_ERR("%s urb %p submission failed (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p submission failed (%d)", urb, -err); usb_unanchor_urb(urb); } @@ -272,7 +269,7 @@ static int bpa10x_setup(struct hci_dev *hdev) if (IS_ERR(skb)) return PTR_ERR(skb); - BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); hci_set_fw_info(hdev, "%s", skb->data + 1); @@ -348,7 +345,7 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb) err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { - BT_ERR("%s urb %p submission failed", hdev->name, urb); + bt_dev_err(hdev, "urb %p submission failed", urb); kfree(urb->setup_packet); usb_unanchor_urb(urb); } diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 194788739a83..25b0cf952b91 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -355,7 +355,7 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst) } else if ((stat & 0xff) != 0xff) { if (stat & 0x0020) { int status = bt3c_read(iobase, 0x7002) & 0x10; - BT_INFO("%s: Antenna %s", info->hdev->name, + bt_dev_info(info->hdev, "Antenna %s", status ? "out" : "in"); } if (stat & 0x0001) diff --git a/drivers/bluetooth/btbcm.c b/drivers/bluetooth/btbcm.c index ae1fa390f508..afa4cb3b16e3 100644 --- a/drivers/bluetooth/btbcm.c +++ b/drivers/bluetooth/btbcm.c @@ -45,13 +45,12 @@ int btbcm_check_bdaddr(struct hci_dev *hdev) HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); - BT_ERR("%s: BCM: Reading device address failed (%d)", - hdev->name, err); + bt_dev_err(hdev, "BCM: Reading device address failed (%d)", err); return err; } if (skb->len != sizeof(*bda)) { - BT_ERR("%s: BCM: Device address length mismatch", hdev->name); + bt_dev_err(hdev, "BCM: Device address length mismatch"); kfree_skb(skb); return -EIO; } @@ -74,8 +73,8 @@ int btbcm_check_bdaddr(struct hci_dev *hdev) if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) || !bacmp(&bda->bdaddr, BDADDR_BCM4324B3) || !bacmp(&bda->bdaddr, BDADDR_BCM4330B1)) { - BT_INFO("%s: BCM: Using default device address (%pMR)", - hdev->name, &bda->bdaddr); + bt_dev_info(hdev, "BCM: Using default device address (%pMR)", + &bda->bdaddr); set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); } @@ -93,8 +92,7 @@ int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); - BT_ERR("%s: BCM: Change address command failed (%d)", - hdev->name, err); + bt_dev_err(hdev, "BCM: Change address command failed (%d)", err); return err; } kfree_skb(skb); @@ -116,8 +114,8 @@ int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw) skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); - BT_ERR("%s: BCM: Download Minidrv command failed (%d)", - hdev->name, err); + bt_dev_err(hdev, "BCM: Download Minidrv command failed (%d)", + err); goto done; } kfree_skb(skb); @@ -136,7 +134,7 @@ int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw) fw_size -= sizeof(*cmd); if (fw_size < cmd->plen) { - BT_ERR("%s: BCM: Patch is corrupted", hdev->name); + bt_dev_err(hdev, "BCM: Patch is corrupted"); err = -EINVAL; goto done; } @@ -151,8 +149,8 @@ int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw) HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); - BT_ERR("%s: BCM: Patch command %04x failed (%d)", - hdev->name, opcode, err); + bt_dev_err(hdev, "BCM: Patch command %04x failed (%d)", + opcode, err); goto done; } kfree_skb(skb); @@ -173,7 +171,7 @@ static int btbcm_reset(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); - BT_ERR("%s: BCM: Reset failed (%d)", hdev->name, err); + bt_dev_err(hdev, "BCM: Reset failed (%d)", err); return err; } kfree_skb(skb); @@ -191,13 +189,13 @@ static struct sk_buff *btbcm_read_local_name(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: BCM: Reading local name failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "BCM: Reading local name failed (%ld)", + PTR_ERR(skb)); return skb; } if (skb->len != sizeof(struct hci_rp_read_local_name)) { - BT_ERR("%s: BCM: Local name length mismatch", hdev->name); + bt_dev_err(hdev, "BCM: Local name length mismatch"); kfree_skb(skb); return ERR_PTR(-EIO); } @@ -212,13 +210,13 @@ static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: BCM: Reading local version info failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "BCM: Reading local version info failed (%ld)", + PTR_ERR(skb)); return skb; } if (skb->len != sizeof(struct hci_rp_read_local_version)) { - BT_ERR("%s: BCM: Local version length mismatch", hdev->name); + bt_dev_err(hdev, "BCM: Local version length mismatch"); kfree_skb(skb); return ERR_PTR(-EIO); } @@ -232,13 +230,13 @@ static struct sk_buff *btbcm_read_verbose_config(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, 0xfc79, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: BCM: Read verbose config info failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "BCM: Read verbose config info failed (%ld)", + PTR_ERR(skb)); return skb; } if (skb->len != 7) { - BT_ERR("%s: BCM: Verbose config length mismatch", hdev->name); + bt_dev_err(hdev, "BCM: Verbose config length mismatch"); kfree_skb(skb); return ERR_PTR(-EIO); } @@ -252,14 +250,13 @@ static struct sk_buff *btbcm_read_controller_features(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, 0xfc6e, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: BCM: Read controller features failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "BCM: Read controller features failed (%ld)", + PTR_ERR(skb)); return skb; } if (skb->len != 9) { - BT_ERR("%s: BCM: Controller features length mismatch", - hdev->name); + bt_dev_err(hdev, "BCM: Controller features length mismatch"); kfree_skb(skb); return ERR_PTR(-EIO); } @@ -273,13 +270,13 @@ static struct sk_buff *btbcm_read_usb_product(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, 0xfc5a, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: BCM: Read USB product info failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "BCM: Read USB product info failed (%ld)", + PTR_ERR(skb)); return skb; } if (skb->len != 5) { - BT_ERR("%s: BCM: USB product length mismatch", hdev->name); + bt_dev_err(hdev, "BCM: USB product length mismatch"); kfree_skb(skb); return ERR_PTR(-EIO); } @@ -296,7 +293,7 @@ static int btbcm_read_info(struct hci_dev *hdev) if (IS_ERR(skb)) return PTR_ERR(skb); - BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]); + bt_dev_info(hdev, "BCM: chip id %u", skb->data[1]); kfree_skb(skb); /* Read Controller Features */ @@ -304,7 +301,7 @@ static int btbcm_read_info(struct hci_dev *hdev) if (IS_ERR(skb)) return PTR_ERR(skb); - BT_INFO("%s: BCM: features 0x%2.2x", hdev->name, skb->data[1]); + bt_dev_info(hdev, "BCM: features 0x%2.2x", skb->data[1]); kfree_skb(skb); /* Read Local Name */ @@ -312,7 +309,7 @@ static int btbcm_read_info(struct hci_dev *hdev) if (IS_ERR(skb)) return PTR_ERR(skb); - BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); kfree_skb(skb); return 0; @@ -378,9 +375,9 @@ int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len) return 0; } - BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, - hw_name ? : "BCM", (subver & 0xe000) >> 13, - (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); + bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u", + hw_name ? : "BCM", (subver & 0xe000) >> 13, + (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); return 0; } @@ -408,9 +405,9 @@ int btbcm_finalize(struct hci_dev *hdev) subver = le16_to_cpu(ver->lmp_subver); kfree_skb(skb); - BT_INFO("%s: BCM (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, - (subver & 0xe000) >> 13, (subver & 0x1f00) >> 8, - (subver & 0x00ff), rev & 0x0fff); + bt_dev_info(hdev, "BCM (%3.3u.%3.3u.%3.3u) build %4.4u", + (subver & 0xe000) >> 13, (subver & 0x1f00) >> 8, + (subver & 0x00ff), rev & 0x0fff); btbcm_check_bdaddr(hdev); @@ -505,13 +502,13 @@ int btbcm_setup_patchram(struct hci_dev *hdev) return 0; } - BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, - hw_name ? : "BCM", (subver & 0xe000) >> 13, - (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); + bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u", + hw_name ? : "BCM", (subver & 0xe000) >> 13, + (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); err = request_firmware(&fw, fw_name, &hdev->dev); if (err < 0) { - BT_INFO("%s: BCM: Patch %s not found", hdev->name, fw_name); + bt_dev_info(hdev, "BCM: Patch %s not found", fw_name); goto done; } @@ -534,16 +531,16 @@ int btbcm_setup_patchram(struct hci_dev *hdev) subver = le16_to_cpu(ver->lmp_subver); kfree_skb(skb); - BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name, - hw_name ? : "BCM", (subver & 0xe000) >> 13, - (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); + bt_dev_info(hdev, "%s (%3.3u.%3.3u.%3.3u) build %4.4u", + hw_name ? : "BCM", (subver & 0xe000) >> 13, + (subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff); /* Read Local Name */ skb = btbcm_read_local_name(hdev); if (IS_ERR(skb)) return PTR_ERR(skb); - BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); kfree_skb(skb); done: @@ -568,31 +565,31 @@ int btbcm_setup_apple(struct hci_dev *hdev) /* Read Verbose Config Version Info */ skb = btbcm_read_verbose_config(hdev); if (!IS_ERR(skb)) { - BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, - skb->data[1], get_unaligned_le16(skb->data + 5)); + bt_dev_info(hdev, "BCM: chip id %u build %4.4u", + skb->data[1], get_unaligned_le16(skb->data + 5)); kfree_skb(skb); } /* Read USB Product Info */ skb = btbcm_read_usb_product(hdev); if (!IS_ERR(skb)) { - BT_INFO("%s: BCM: product %4.4x:%4.4x", hdev->name, - get_unaligned_le16(skb->data + 1), - get_unaligned_le16(skb->data + 3)); + bt_dev_info(hdev, "BCM: product %4.4x:%4.4x", + get_unaligned_le16(skb->data + 1), + get_unaligned_le16(skb->data + 3)); kfree_skb(skb); } /* Read Controller Features */ skb = btbcm_read_controller_features(hdev); if (!IS_ERR(skb)) { - BT_INFO("%s: BCM: features 0x%2.2x", hdev->name, skb->data[1]); + bt_dev_info(hdev, "BCM: features 0x%2.2x", skb->data[1]); kfree_skb(skb); } /* Read Local Name */ skb = btbcm_read_local_name(hdev); if (!IS_ERR(skb)) { - BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); + bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); kfree_skb(skb); } diff --git a/drivers/bluetooth/btintel.c b/drivers/bluetooth/btintel.c index d32e109bd5cb..4459555c9d88 100644 --- a/drivers/bluetooth/btintel.c +++ b/drivers/bluetooth/btintel.c @@ -43,13 +43,13 @@ int btintel_check_bdaddr(struct hci_dev *hdev) HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); - BT_ERR("%s: Reading Intel device address failed (%d)", - hdev->name, err); + bt_dev_err(hdev, "Reading Intel device address failed (%d)", + err); return err; } if (skb->len != sizeof(*bda)) { - BT_ERR("%s: Intel device address length mismatch", hdev->name); + bt_dev_err(hdev, "Intel device address length mismatch"); kfree_skb(skb); return -EIO; } @@ -62,8 +62,8 @@ int btintel_check_bdaddr(struct hci_dev *hdev) * and that in turn can cause problems with Bluetooth operation. */ if (!bacmp(&bda->bdaddr, BDADDR_INTEL)) { - BT_ERR("%s: Found Intel default device address (%pMR)", - hdev->name, &bda->bdaddr); + bt_dev_err(hdev, "Found Intel default device address (%pMR)", + &bda->bdaddr); set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); } @@ -123,8 +123,8 @@ int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr) skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); - BT_ERR("%s: Changing Intel device address failed (%d)", - hdev->name, err); + bt_dev_err(hdev, "Changing Intel device address failed (%d)", + err); return err; } kfree_skb(skb); @@ -154,8 +154,8 @@ int btintel_set_diag(struct hci_dev *hdev, bool enable) err = PTR_ERR(skb); if (err == -ENODATA) goto done; - BT_ERR("%s: Changing Intel diagnostic mode failed (%d)", - hdev->name, err); + bt_dev_err(hdev, "Changing Intel diagnostic mode failed (%d)", + err); return err; } kfree_skb(skb); @@ -189,30 +189,30 @@ void btintel_hw_error(struct hci_dev *hdev, u8 code) struct sk_buff *skb; u8 type = 0x00; - BT_ERR("%s: Hardware error 0x%2.2x", hdev->name, code); + bt_dev_err(hdev, "Hardware error 0x%2.2x", code); skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: Reset after hardware error failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "Reset after hardware error failed (%ld)", + PTR_ERR(skb)); return; } kfree_skb(skb); skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: Retrieving Intel exception info failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "Retrieving Intel exception info failed (%ld)", + PTR_ERR(skb)); return; } if (skb->len != 13) { - BT_ERR("%s: Exception info size mismatch", hdev->name); + bt_dev_err(hdev, "Exception info size mismatch"); kfree_skb(skb); return; } - BT_ERR("%s: Exception info %s", hdev->name, (char *)(skb->data + 1)); + bt_dev_err(hdev, "Exception info %s", (char *)(skb->data + 1)); kfree_skb(skb); } @@ -233,9 +233,10 @@ void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver) return; } - BT_INFO("%s: %s revision %u.%u build %u week %u %u", hdev->name, - variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f, - ver->fw_build_num, ver->fw_build_ww, 2000 + ver->fw_build_yy); + bt_dev_info(hdev, "%s revision %u.%u build %u week %u %u", + variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f, + ver->fw_build_num, ver->fw_build_ww, + 2000 + ver->fw_build_yy); } EXPORT_SYMBOL_GPL(btintel_version_info); @@ -321,8 +322,7 @@ int btintel_set_event_mask(struct hci_dev *hdev, bool debug) skb = __hci_cmd_sync(hdev, 0xfc52, 8, mask, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { err = PTR_ERR(skb); - BT_ERR("%s: Setting Intel event mask failed (%d)", - hdev->name, err); + bt_dev_err(hdev, "Setting Intel event mask failed (%d)", err); return err; } kfree_skb(skb); diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index 0bbdfcef2aa8..2793d4180d2f 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -287,7 +287,7 @@ static int rome_download_firmware(struct hci_dev *hdev, const struct firmware *fw; int ret; - BT_INFO("%s: ROME Downloading %s", hdev->name, config->fwname); + bt_dev_info(hdev, "ROME Downloading %s", config->fwname); ret = request_firmware(&fw, config->fwname, &hdev->dev); if (ret) { @@ -351,7 +351,7 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) return err; } - BT_INFO("%s: ROME controller version 0x%08x", hdev->name, rome_ver); + bt_dev_info(hdev, "ROME controller version 0x%08x", rome_ver); /* Download rampatch file */ config.type = TLV_TYPE_PATCH; @@ -380,7 +380,7 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate) return err; } - BT_INFO("%s: ROME setup on UART is completed", hdev->name); + bt_dev_info(hdev, "ROME setup on UART is completed"); return 0; } diff --git a/drivers/bluetooth/btrtl.c b/drivers/bluetooth/btrtl.c index d9a99b4302ea..6e2ad748abba 100644 --- a/drivers/bluetooth/btrtl.c +++ b/drivers/bluetooth/btrtl.c @@ -55,8 +55,8 @@ static int rtl_read_rom_version(struct hci_dev *hdev, u8 *version) } rom_version = (struct rtl_rom_version_evt *)skb->data; - BT_INFO("%s: rom_version status=%x version=%x", - hdev->name, rom_version->status, rom_version->version); + bt_dev_info(hdev, "rom_version status=%x version=%x", + rom_version->status, rom_version->version); *version = rom_version->version; @@ -273,7 +273,7 @@ static int rtl_load_config(struct hci_dev *hdev, const char *name, u8 **buff) const struct firmware *fw; int ret; - BT_INFO("%s: rtl: loading %s", hdev->name, name); + bt_dev_info(hdev, "rtl: loading %s", name); ret = request_firmware(&fw, name, &hdev->dev); if (ret < 0) return ret; @@ -292,7 +292,7 @@ static int btrtl_setup_rtl8723a(struct hci_dev *hdev) const struct firmware *fw; int ret; - BT_INFO("%s: rtl: loading rtl_bt/rtl8723a_fw.bin", hdev->name); + bt_dev_info(hdev, "rtl: loading rtl_bt/rtl8723a_fw.bin"); ret = request_firmware(&fw, "rtl_bt/rtl8723a_fw.bin", &hdev->dev); if (ret < 0) { BT_ERR("%s: Failed to load rtl_bt/rtl8723a_fw.bin", hdev->name); @@ -363,7 +363,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver, } else cfg_sz = 0; - BT_INFO("%s: rtl: loading %s", hdev->name, fw_name); + bt_dev_info(hdev, "rtl: loading %s", fw_name); ret = request_firmware(&fw, fw_name, &hdev->dev); if (ret < 0) { BT_ERR("%s: Failed to load %s", hdev->name, fw_name); @@ -390,7 +390,7 @@ static int btrtl_setup_rtl8723b(struct hci_dev *hdev, u16 lmp_subver, fw_data = tbuff; } - BT_INFO("cfg_sz %d, total size %d", cfg_sz, ret); + bt_dev_info(hdev, "cfg_sz %d, total size %d", cfg_sz, ret); ret = rtl_download_firmware(hdev, fw_data, ret); @@ -436,9 +436,10 @@ int btrtl_setup_realtek(struct hci_dev *hdev) return -PTR_ERR(skb); resp = (struct hci_rp_read_local_version *)skb->data; - BT_INFO("%s: rtl: examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x " - "lmp_subver=%04x", hdev->name, resp->hci_ver, resp->hci_rev, - resp->lmp_ver, resp->lmp_subver); + bt_dev_info(hdev, "rtl: examining hci_ver=%02x hci_rev=%04x " + "lmp_ver=%02x lmp_subver=%04x", + resp->hci_ver, resp->hci_rev, + resp->lmp_ver, resp->lmp_subver); lmp_subver = le16_to_cpu(resp->lmp_subver); kfree_skb(skb); @@ -466,7 +467,7 @@ int btrtl_setup_realtek(struct hci_dev *hdev) return btrtl_setup_rtl8723b(hdev, lmp_subver, "rtl_bt/rtl8822b_fw.bin"); default: - BT_INFO("rtl: assuming no firmware upload needed."); + bt_dev_info(hdev, "rtl: assuming no firmware upload needed"); return 0; } } diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b8eb39436ef2..8701140e11f9 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -643,7 +643,7 @@ static void btusb_intr_complete(struct urb *urb) if (btusb_recv_intr(data, urb->transfer_buffer, urb->actual_length) < 0) { - BT_ERR("%s corrupted event packet", hdev->name); + bt_dev_err(hdev, "corrupted event packet"); hdev->stat.err_rx++; } } else if (urb->status == -ENOENT) { @@ -663,8 +663,8 @@ static void btusb_intr_complete(struct urb *urb) * -ENODEV: device got disconnected */ if (err != -EPERM && err != -ENODEV) - BT_ERR("%s urb %p failed to resubmit (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p failed to resubmit (%d)", + urb, -err); usb_unanchor_urb(urb); } } @@ -706,8 +706,8 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags) err = usb_submit_urb(urb, mem_flags); if (err < 0) { if (err != -EPERM && err != -ENODEV) - BT_ERR("%s urb %p submission failed (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p submission failed (%d)", + urb, -err); usb_unanchor_urb(urb); } @@ -733,7 +733,7 @@ static void btusb_bulk_complete(struct urb *urb) if (data->recv_bulk(data, urb->transfer_buffer, urb->actual_length) < 0) { - BT_ERR("%s corrupted ACL packet", hdev->name); + bt_dev_err(hdev, "corrupted ACL packet"); hdev->stat.err_rx++; } } else if (urb->status == -ENOENT) { @@ -753,8 +753,8 @@ static void btusb_bulk_complete(struct urb *urb) * -ENODEV: device got disconnected */ if (err != -EPERM && err != -ENODEV) - BT_ERR("%s urb %p failed to resubmit (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p failed to resubmit (%d)", + urb, -err); usb_unanchor_urb(urb); } } @@ -795,8 +795,8 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) err = usb_submit_urb(urb, mem_flags); if (err < 0) { if (err != -EPERM && err != -ENODEV) - BT_ERR("%s urb %p submission failed (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p submission failed (%d)", + urb, -err); usb_unanchor_urb(urb); } @@ -829,7 +829,7 @@ static void btusb_isoc_complete(struct urb *urb) if (btusb_recv_isoc(data, urb->transfer_buffer + offset, length) < 0) { - BT_ERR("%s corrupted SCO packet", hdev->name); + bt_dev_err(hdev, "corrupted SCO packet"); hdev->stat.err_rx++; } } @@ -849,8 +849,8 @@ static void btusb_isoc_complete(struct urb *urb) * -ENODEV: device got disconnected */ if (err != -EPERM && err != -ENODEV) - BT_ERR("%s urb %p failed to resubmit (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p failed to resubmit (%d)", + urb, -err); usb_unanchor_urb(urb); } } @@ -917,8 +917,8 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags) err = usb_submit_urb(urb, mem_flags); if (err < 0) { if (err != -EPERM && err != -ENODEV) - BT_ERR("%s urb %p submission failed (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p submission failed (%d)", + urb, -err); usb_unanchor_urb(urb); } @@ -962,8 +962,8 @@ static void btusb_diag_complete(struct urb *urb) * -ENODEV: device got disconnected */ if (err != -EPERM && err != -ENODEV) - BT_ERR("%s urb %p failed to resubmit (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p failed to resubmit (%d)", + urb, -err); usb_unanchor_urb(urb); } } @@ -1004,8 +1004,8 @@ static int btusb_submit_diag_urb(struct hci_dev *hdev, gfp_t mem_flags) err = usb_submit_urb(urb, mem_flags); if (err < 0) { if (err != -EPERM && err != -ENODEV) - BT_ERR("%s urb %p submission failed (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p submission failed (%d)", + urb, -err); usb_unanchor_urb(urb); } @@ -1266,8 +1266,8 @@ static int submit_tx_urb(struct hci_dev *hdev, struct urb *urb) err = usb_submit_urb(urb, GFP_KERNEL); if (err < 0) { if (err != -EPERM && err != -ENODEV) - BT_ERR("%s urb %p submission failed (%d)", - hdev->name, urb, -err); + bt_dev_err(hdev, "urb %p submission failed (%d)", + urb, -err); kfree(urb->setup_packet); usb_unanchor_urb(urb); } else { @@ -1362,7 +1362,7 @@ static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting) err = usb_set_interface(data->udev, 1, altsetting); if (err < 0) { - BT_ERR("%s setting interface failed (%d)", hdev->name, -err); + bt_dev_err(hdev, "setting interface failed (%d)", -err); return err; } @@ -1386,7 +1386,7 @@ static inline int __set_isoc_interface(struct hci_dev *hdev, int altsetting) } if (!data->isoc_tx_ep || !data->isoc_rx_ep) { - BT_ERR("%s invalid SCO descriptors", hdev->name); + bt_dev_err(hdev, "invalid SCO descriptors"); return -ENODEV; } @@ -1481,7 +1481,7 @@ static int btusb_setup_bcm92035(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, 0xfc3b, 1, &val, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) - BT_ERR("BCM92035 command failed (%ld)", -PTR_ERR(skb)); + bt_dev_err(hdev, "BCM92035 command failed (%ld)", PTR_ERR(skb)); else kfree_skb(skb); @@ -1499,12 +1499,12 @@ static int btusb_setup_csr(struct hci_dev *hdev) HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { int err = PTR_ERR(skb); - BT_ERR("%s: CSR: Local version failed (%d)", hdev->name, err); + bt_dev_err(hdev, "CSR: Local version failed (%d)", err); return err; } if (skb->len != sizeof(struct hci_rp_read_local_version)) { - BT_ERR("%s: CSR: Local version length mismatch", hdev->name); + bt_dev_err(hdev, "CSR: Local version length mismatch"); kfree_skb(skb); return -EIO; } @@ -1566,7 +1566,7 @@ static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev, } } - BT_INFO("%s: Intel Bluetooth firmware file: %s", hdev->name, fwname); + bt_dev_info(hdev, "Intel Bluetooth firmware file: %s", fwname); return fw; } @@ -1722,18 +1722,18 @@ static int btusb_setup_intel(struct hci_dev *hdev) if (err) return err; - BT_INFO("%s: read Intel version: %02x%02x%02x%02x%02x%02x%02x%02x%02x", - hdev->name, ver.hw_platform, ver.hw_variant, ver.hw_revision, - ver.fw_variant, ver.fw_revision, ver.fw_build_num, - ver.fw_build_ww, ver.fw_build_yy, ver.fw_patch_num); + bt_dev_info(hdev, "read Intel version: %02x%02x%02x%02x%02x%02x%02x%02x%02x", + ver.hw_platform, ver.hw_variant, ver.hw_revision, + ver.fw_variant, ver.fw_revision, ver.fw_build_num, + ver.fw_build_ww, ver.fw_build_yy, ver.fw_patch_num); /* fw_patch_num indicates the version of patch the device currently * have. If there is no patch data in the device, it is always 0x00. * So, if it is other than 0x00, no need to patch the device again. */ if (ver.fw_patch_num) { - BT_INFO("%s: Intel device is already patched. patch num: %02x", - hdev->name, ver.fw_patch_num); + bt_dev_info(hdev, "Intel device is already patched. " + "patch num: %02x", ver.fw_patch_num); goto complete; } @@ -1801,8 +1801,7 @@ static int btusb_setup_intel(struct hci_dev *hdev) if (err) return err; - BT_INFO("%s: Intel Bluetooth firmware patch completed and activated", - hdev->name); + bt_dev_info(hdev, "Intel firmware patch completed and activated"); goto complete; @@ -1812,7 +1811,7 @@ exit_mfg_disable: if (err) return err; - BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name); + bt_dev_info(hdev, "Intel firmware patch completed"); goto complete; @@ -1826,8 +1825,7 @@ exit_mfg_deactivate: if (err) return err; - BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated", - hdev->name); + bt_dev_info(hdev, "Intel firmware patch completed and deactivated"); complete: /* Set the event mask for Intel specific vendor events. This enables @@ -2108,24 +2106,24 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) params = (struct intel_boot_params *)skb->data; - BT_INFO("%s: Device revision is %u", hdev->name, - le16_to_cpu(params->dev_revid)); + bt_dev_info(hdev, "Device revision is %u", + le16_to_cpu(params->dev_revid)); - BT_INFO("%s: Secure boot is %s", hdev->name, - params->secure_boot ? "enabled" : "disabled"); + bt_dev_info(hdev, "Secure boot is %s", + params->secure_boot ? "enabled" : "disabled"); - BT_INFO("%s: OTP lock is %s", hdev->name, - params->otp_lock ? "enabled" : "disabled"); + bt_dev_info(hdev, "OTP lock is %s", + params->otp_lock ? "enabled" : "disabled"); - BT_INFO("%s: API lock is %s", hdev->name, - params->api_lock ? "enabled" : "disabled"); + bt_dev_info(hdev, "API lock is %s", + params->api_lock ? "enabled" : "disabled"); - BT_INFO("%s: Debug lock is %s", hdev->name, - params->debug_lock ? "enabled" : "disabled"); + bt_dev_info(hdev, "Debug lock is %s", + params->debug_lock ? "enabled" : "disabled"); - BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name, - params->min_fw_build_nn, params->min_fw_build_cw, - 2000 + params->min_fw_build_yy); + bt_dev_info(hdev, "Minimum firmware build %u week %u %u", + params->min_fw_build_nn, params->min_fw_build_cw, + 2000 + params->min_fw_build_yy); /* It is required that every single firmware fragment is acknowledged * with a command complete event. If the boot parameters indicate @@ -2142,7 +2140,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) * also be no valid address for the operational firmware. */ if (!bacmp(¶ms->otp_bdaddr, BDADDR_ANY)) { - BT_INFO("%s: No device address configured", hdev->name); + bt_dev_info(hdev, "No device address configured"); set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); } @@ -2169,7 +2167,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) return err; } - BT_INFO("%s: Found device firmware: %s", hdev->name, fwname); + bt_dev_info(hdev, "Found device firmware: %s", fwname); /* Save the DDC file name for later use to apply once the firmware * downloading is done. @@ -2250,7 +2248,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) set_bit(BTUSB_FIRMWARE_LOADED, &data->flags); - BT_INFO("%s: Waiting for firmware download to complete", hdev->name); + bt_dev_info(hdev, "Waiting for firmware download to complete"); /* Before switching the device into operational mode and with that * booting the loaded firmware, wait for the bootloader notification @@ -2287,7 +2285,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev) delta = ktime_sub(rettime, calltime); duration = (unsigned long long) ktime_to_ns(delta) >> 10; - BT_INFO("%s: Firmware loaded in %llu usecs", hdev->name, duration); + bt_dev_info(hdev, "Firmware loaded in %llu usecs", duration); done: release_firmware(fw); @@ -2313,7 +2311,7 @@ done: * 1 second. However if that happens, then just fail the setup * since something went wrong. */ - BT_INFO("%s: Waiting for device to boot", hdev->name); + bt_dev_info(hdev, "Waiting for device to boot"); err = wait_on_bit_timeout(&data->flags, BTUSB_BOOTING, TASK_INTERRUPTIBLE, @@ -2333,7 +2331,7 @@ done: delta = ktime_sub(rettime, calltime); duration = (unsigned long long) ktime_to_ns(delta) >> 10; - BT_INFO("%s: Device booted in %llu usecs", hdev->name, duration); + bt_dev_info(hdev, "Device booted in %llu usecs", duration); clear_bit(BTUSB_BOOTLOADER, &data->flags); @@ -2436,8 +2434,8 @@ static int btusb_set_bdaddr_marvell(struct hci_dev *hdev, skb = __hci_cmd_sync(hdev, 0xfc22, sizeof(buf), buf, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { ret = PTR_ERR(skb); - BT_ERR("%s: changing Marvell device address failed (%ld)", - hdev->name, ret); + bt_dev_err(hdev, "changing Marvell device address failed (%ld)", + ret); return ret; } kfree_skb(skb); @@ -2461,8 +2459,7 @@ static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev, skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { ret = PTR_ERR(skb); - BT_ERR("%s: Change address command failed (%ld)", - hdev->name, ret); + bt_dev_err(hdev, "Change address command failed (%ld)", ret); return ret; } kfree_skb(skb); @@ -2528,7 +2525,7 @@ static int btusb_qca_send_vendor_req(struct hci_dev *hdev, u8 request, err = usb_control_msg(udev, pipe, request, USB_TYPE_VENDOR | USB_DIR_IN, 0, 0, buf, size, USB_CTRL_SET_TIMEOUT); if (err < 0) { - BT_ERR("%s: Failed to access otp area (%d)", hdev->name, err); + bt_dev_err(hdev, "Failed to access otp area (%d)", err); goto done; } @@ -2568,7 +2565,7 @@ static int btusb_setup_qca_download_fw(struct hci_dev *hdev, err = usb_control_msg(udev, pipe, QCA_DFU_DOWNLOAD, USB_TYPE_VENDOR, 0, 0, buf, size, USB_CTRL_SET_TIMEOUT); if (err < 0) { - BT_ERR("%s: Failed to send headers (%d)", hdev->name, err); + bt_dev_err(hdev, "Failed to send headers (%d)", err); goto done; } @@ -2584,13 +2581,13 @@ static int btusb_setup_qca_download_fw(struct hci_dev *hdev, err = usb_bulk_msg(udev, pipe, buf, size, &len, QCA_DFU_TIMEOUT); if (err < 0) { - BT_ERR("%s: Failed to send body at %zd of %zd (%d)", - hdev->name, sent, firmware->size, err); + bt_dev_err(hdev, "Failed to send body at %zd of %zd (%d)", + sent, firmware->size, err); break; } if (size != len) { - BT_ERR("%s: Failed to get bulk buffer", hdev->name); + bt_dev_err(hdev, "Failed to get bulk buffer"); err = -EILSEQ; break; } @@ -2622,24 +2619,23 @@ static int btusb_setup_qca_load_rampatch(struct hci_dev *hdev, err = request_firmware(&fw, fwname, &hdev->dev); if (err) { - BT_ERR("%s: failed to request rampatch file: %s (%d)", - hdev->name, fwname, err); + bt_dev_err(hdev, "failed to request rampatch file: %s (%d)", + fwname, err); return err; } - BT_INFO("%s: using rampatch file: %s", hdev->name, fwname); + bt_dev_info(hdev, "using rampatch file: %s", fwname); rver = (struct qca_rampatch_version *)(fw->data + info->ver_offset); rver_rom = le16_to_cpu(rver->rom_version); rver_patch = le16_to_cpu(rver->patch_version); - BT_INFO("%s: QCA: patch rome 0x%x build 0x%x, firmware rome 0x%x " - "build 0x%x", hdev->name, rver_rom, rver_patch, ver_rom, - ver_patch); + bt_dev_info(hdev, "QCA: patch rome 0x%x build 0x%x, " + "firmware rome 0x%x build 0x%x", + rver_rom, rver_patch, ver_rom, ver_patch); if (rver_rom != ver_rom || rver_patch <= ver_patch) { - BT_ERR("%s: rampatch file version did not match with firmware", - hdev->name); + bt_dev_err(hdev, "rampatch file version did not match with firmware"); err = -EINVAL; goto done; } @@ -2665,12 +2661,12 @@ static int btusb_setup_qca_load_nvm(struct hci_dev *hdev, err = request_firmware(&fw, fwname, &hdev->dev); if (err) { - BT_ERR("%s: failed to request NVM file: %s (%d)", - hdev->name, fwname, err); + bt_dev_err(hdev, "failed to request NVM file: %s (%d)", + fwname, err); return err; } - BT_INFO("%s: using NVM file: %s", hdev->name, fwname); + bt_dev_info(hdev, "using NVM file: %s", fwname); err = btusb_setup_qca_download_fw(hdev, fw, info->nvm_hdr); @@ -2698,8 +2694,7 @@ static int btusb_setup_qca(struct hci_dev *hdev) info = &qca_devices_table[i]; } if (!info) { - BT_ERR("%s: don't support firmware rome 0x%x", hdev->name, - ver_rom); + bt_dev_err(hdev, "don't support firmware rome 0x%x", ver_rom); return -ENODEV; } @@ -2753,7 +2748,7 @@ static inline int __set_diag_interface(struct hci_dev *hdev) } if (!data->diag_tx_ep || !data->diag_rx_ep) { - BT_ERR("%s invalid diagnostic descriptors", hdev->name); + bt_dev_err(hdev, "invalid diagnostic descriptors"); return -ENODEV; } diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index 5f17ec339fb8..14ae7ee88acb 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -206,7 +206,7 @@ static int ath_recv(struct hci_uart *hu, const void *data, int count) ath_recv_pkts, ARRAY_SIZE(ath_recv_pkts)); if (IS_ERR(ath->rx_skb)) { int err = PTR_ERR(ath->rx_skb); - BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); ath->rx_skb = NULL; return err; } diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 3b82a87224a9..fb97a3bf069b 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -132,7 +132,7 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count) h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts)); if (IS_ERR(h4->rx_skb)) { int err = PTR_ERR(h4->rx_skb); - BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); h4->rx_skb = NULL; return err; } diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 4a949bb60394..bbd7db7384e6 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -800,7 +800,7 @@ static int qca_recv(struct hci_uart *hu, const void *data, int count) qca_recv_pkts, ARRAY_SIZE(qca_recv_pkts)); if (IS_ERR(qca->rx_skb)) { int err = PTR_ERR(qca->rx_skb); - BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err); + bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); qca->rx_skb = NULL; return err; } @@ -863,7 +863,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate) skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC); if (!skb) { - BT_ERR("Failed to allocate memory for baudrate packet"); + bt_dev_err(hdev, "Failed to allocate baudrate packet"); return -ENOMEM; } @@ -892,7 +892,7 @@ static int qca_setup(struct hci_uart *hu) unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200; int ret; - BT_INFO("%s: ROME setup", hdev->name); + bt_dev_info(hdev, "ROME setup"); /* Patch downloading has to be done without IBS mode */ clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags); @@ -917,11 +917,11 @@ static int qca_setup(struct hci_uart *hu) if (speed) { qca_baudrate = qca_get_baudrate_value(speed); - BT_INFO("%s: Set UART speed to %d", hdev->name, speed); + bt_dev_info(hdev, "Set UART speed to %d", speed); ret = qca_set_baudrate(hdev, qca_baudrate); if (ret) { - BT_ERR("%s: Failed to change the baud rate (%d)", - hdev->name, ret); + bt_dev_err(hdev, "Failed to change the baud rate (%d)", + ret); return ret; } hci_uart_set_baudrate(hu, speed); diff --git a/drivers/bluetooth/hci_serdev.c b/drivers/bluetooth/hci_serdev.c index b725ac4f7ff6..71664b22ec9d 100644 --- a/drivers/bluetooth/hci_serdev.c +++ b/drivers/bluetooth/hci_serdev.c @@ -185,7 +185,7 @@ static int hci_uart_setup(struct hci_dev *hdev) if (hu->proto->set_baudrate && speed) { err = hu->proto->set_baudrate(hu, speed); if (err) - BT_ERR("%s: failed to set baudrate", hdev->name); + bt_dev_err(hdev, "Failed to set baudrate"); else serdev_device_set_baudrate(hu->serdev, speed); } @@ -199,14 +199,13 @@ static int hci_uart_setup(struct hci_dev *hdev) skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL, HCI_INIT_TIMEOUT); if (IS_ERR(skb)) { - BT_ERR("%s: Reading local version information failed (%ld)", - hdev->name, PTR_ERR(skb)); + bt_dev_err(hdev, "Reading local version info failed (%ld)", + PTR_ERR(skb)); return 0; } if (skb->len != sizeof(*ver)) { - BT_ERR("%s: Event length mismatch for version information", - hdev->name); + bt_dev_err(hdev, "Event length mismatch for version info"); } kfree_skb(skb); diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 020142bb9735..e89cff0c4c23 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -147,6 +147,9 @@ void bt_err_ratelimited(const char *fmt, ...); #define bt_dev_dbg(hdev, fmt, ...) \ BT_DBG("%s: " fmt, (hdev)->name, ##__VA_ARGS__) +#define bt_dev_err_ratelimited(hdev, fmt, ...) \ + BT_ERR_RATELIMITED("%s: " fmt, (hdev)->name, ##__VA_ARGS__) + /* Connection and socket states */ enum { BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index aad994edd3bb..51c2cf2d8923 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -573,7 +573,7 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, &mgr->l2cap_conn->hcon->dst); if (!hcon) { - BT_ERR("No phys link exist"); + bt_dev_err(hdev, "no phys link exist"); rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS; goto clean; } diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index ebcab5bbadd7..78bec8df8525 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -187,7 +187,7 @@ int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type) /* Legacy key */ if (conn->key_type < 3) { - BT_ERR("Legacy key type %d", conn->key_type); + bt_dev_err(hdev, "legacy key type %d", conn->key_type); return -EACCES; } @@ -207,7 +207,7 @@ int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type) /* Derive Generic AMP Link Key (gamp) */ err = hmac_sha256(keybuf, HCI_AMP_LINK_KEY_SIZE, "gamp", 4, gamp_key); if (err) { - BT_ERR("Could not derive Generic AMP Key: err %d", err); + bt_dev_err(hdev, "could not derive Generic AMP Key: err %d", err); return err; } diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 746adcb62259..a9682534c377 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -729,8 +729,8 @@ static void create_le_conn_complete(struct hci_dev *hdev, u8 status, u16 opcode) goto done; } - BT_ERR("HCI request failed to create LE connection: status 0x%2.2x", - status); + bt_dev_err(hdev, "request failed to create LE connection: " + "status 0x%2.2x", status); if (!conn) goto done; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6bc679cd3481..40d260f2bea5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -267,7 +267,7 @@ static int hci_init1_req(struct hci_request *req, unsigned long opt) amp_init1(req); break; default: - BT_ERR("Unknown device type %d", hdev->dev_type); + bt_dev_err(hdev, "Unknown device type %d", hdev->dev_type); break; } @@ -2150,8 +2150,7 @@ static void hci_error_reset(struct work_struct *work) if (hdev->hw_error) hdev->hw_error(hdev, hdev->hw_error_code); else - BT_ERR("%s hardware error 0x%2.2x", hdev->name, - hdev->hw_error_code); + bt_dev_err(hdev, "hardware error 0x%2.2x", hdev->hw_error_code); if (hci_dev_do_close(hdev)) return; @@ -2524,9 +2523,9 @@ static void hci_cmd_timeout(struct work_struct *work) struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data; u16 opcode = __le16_to_cpu(sent->opcode); - BT_ERR("%s command 0x%4.4x tx timeout", hdev->name, opcode); + bt_dev_err(hdev, "command 0x%4.4x tx timeout", opcode); } else { - BT_ERR("%s command tx timeout", hdev->name); + bt_dev_err(hdev, "command tx timeout"); } atomic_set(&hdev->cmd_cnt, 1); @@ -2858,7 +2857,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, params = kzalloc(sizeof(*params), GFP_KERNEL); if (!params) { - BT_ERR("Out of memory"); + bt_dev_err(hdev, "out of memory"); return NULL; } @@ -3393,7 +3392,7 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) err = hdev->send(hdev, skb); if (err < 0) { - BT_ERR("%s sending frame failed (%d)", hdev->name, err); + bt_dev_err(hdev, "sending frame failed (%d)", err); kfree_skb(skb); } } @@ -3408,7 +3407,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, skb = hci_prepare_cmd(hdev, opcode, plen, param); if (!skb) { - BT_ERR("%s no memory for command", hdev->name); + bt_dev_err(hdev, "no memory for command"); return -ENOMEM; } @@ -3493,7 +3492,7 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue, hci_add_acl_hdr(skb, chan->handle, flags); break; default: - BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type); + bt_dev_err(hdev, "unknown dev_type %d", hdev->dev_type); return; } @@ -3618,7 +3617,7 @@ static struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, break; default: cnt = 0; - BT_ERR("Unknown link type"); + bt_dev_err(hdev, "unknown link type %d", conn->type); } q = cnt / num; @@ -3635,15 +3634,15 @@ static void hci_link_tx_to(struct hci_dev *hdev, __u8 type) struct hci_conn_hash *h = &hdev->conn_hash; struct hci_conn *c; - BT_ERR("%s link tx timeout", hdev->name); + bt_dev_err(hdev, "link tx timeout"); rcu_read_lock(); /* Kill stalled connections */ list_for_each_entry_rcu(c, &h->list, list) { if (c->type == type && c->sent) { - BT_ERR("%s killing stalled connection %pMR", - hdev->name, &c->dst); + bt_dev_err(hdev, "killing stalled connection %pMR", + &c->dst); hci_disconnect(c, HCI_ERROR_REMOTE_USER_TERM); } } @@ -3724,7 +3723,7 @@ static struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, break; default: cnt = 0; - BT_ERR("Unknown link type"); + bt_dev_err(hdev, "unknown link type %d", chan->conn->type); } q = cnt / num; @@ -4066,8 +4065,8 @@ static void hci_acldata_packet(struct hci_dev *hdev, struct sk_buff *skb) l2cap_recv_acldata(conn, skb, flags); return; } else { - BT_ERR("%s ACL packet for unknown connection handle %d", - hdev->name, handle); + bt_dev_err(hdev, "ACL packet for unknown connection handle %d", + handle); } kfree_skb(skb); @@ -4097,8 +4096,8 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb) sco_recv_scodata(conn, skb); return; } else { - BT_ERR("%s SCO packet for unknown connection handle %d", - hdev->name, handle); + bt_dev_err(hdev, "SCO packet for unknown connection handle %d", + handle); } kfree_skb(skb); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0b4dba08a14e..cd3bbb766c24 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1188,7 +1188,8 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, break; default: - BT_ERR("Used reserved LE_Scan_Enable param %d", cp->enable); + bt_dev_err(hdev, "use of reserved LE_Scan_Enable param %d", + cp->enable); break; } @@ -1485,7 +1486,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr, HCI_ROLE_MASTER); if (!conn) - BT_ERR("No memory for new connection"); + bt_dev_err(hdev, "no memory for new connection"); } } @@ -2269,7 +2270,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr, HCI_ROLE_SLAVE); if (!conn) { - BT_ERR("No memory for new connection"); + bt_dev_err(hdev, "no memory for new connection"); hci_dev_unlock(hdev); return; } @@ -2431,7 +2432,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (!hci_conn_ssp_enabled(conn) && test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { - BT_INFO("re-auth of legacy device is not possible."); + bt_dev_info(hdev, "re-auth of legacy device is not possible."); } else { set_bit(HCI_CONN_AUTH, &conn->flags); conn->sec_level = conn->pending_sec_level; @@ -2535,8 +2536,7 @@ static void read_enc_key_size_complete(struct hci_dev *hdev, u8 status, BT_DBG("%s status 0x%02x", hdev->name, status); if (!skb || skb->len < sizeof(*rp)) { - BT_ERR("%s invalid HCI Read Encryption Key Size response", - hdev->name); + bt_dev_err(hdev, "invalid read key size response"); return; } @@ -2554,8 +2554,8 @@ static void read_enc_key_size_complete(struct hci_dev *hdev, u8 status, * supported. */ if (rp->status) { - BT_ERR("%s failed to read key size for handle %u", hdev->name, - handle); + bt_dev_err(hdev, "failed to read key size for handle %u", + handle); conn->enc_key_size = HCI_LINK_KEY_SIZE; } else { conn->enc_key_size = rp->key_size; @@ -2664,7 +2664,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_req_add(&req, HCI_OP_READ_ENC_KEY_SIZE, sizeof(cp), &cp); if (hci_req_run_skb(&req, read_enc_key_size_complete)) { - BT_ERR("Sending HCI Read Encryption Key Size failed"); + bt_dev_err(hdev, "sending read key size failed"); conn->enc_key_size = HCI_LINK_KEY_SIZE; goto notify; } @@ -3197,7 +3197,7 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb) int i; if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_PACKET_BASED) { - BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode); + bt_dev_err(hdev, "wrong event for mode %d", hdev->flow_ctl_mode); return; } @@ -3249,7 +3249,8 @@ static void hci_num_comp_pkts_evt(struct hci_dev *hdev, struct sk_buff *skb) break; default: - BT_ERR("Unknown type %d conn %p", conn->type, conn); + bt_dev_err(hdev, "unknown type %d conn %p", + conn->type, conn); break; } } @@ -3271,7 +3272,7 @@ static struct hci_conn *__hci_conn_lookup_handle(struct hci_dev *hdev, return chan->conn; break; default: - BT_ERR("%s unknown dev_type %d", hdev->name, hdev->dev_type); + bt_dev_err(hdev, "unknown dev_type %d", hdev->dev_type); break; } @@ -3284,7 +3285,7 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb) int i; if (hdev->flow_ctl_mode != HCI_FLOW_CTL_MODE_BLOCK_BASED) { - BT_ERR("Wrong event for mode %d", hdev->flow_ctl_mode); + bt_dev_err(hdev, "wrong event for mode %d", hdev->flow_ctl_mode); return; } @@ -3320,7 +3321,8 @@ static void hci_num_comp_blocks_evt(struct hci_dev *hdev, struct sk_buff *skb) break; default: - BT_ERR("Unknown type %d conn %p", conn->type, conn); + bt_dev_err(hdev, "unknown type %d conn %p", + conn->type, conn); break; } } @@ -4479,7 +4481,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (!conn) { conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr, ev->role); if (!conn) { - BT_ERR("No memory for new connection"); + bt_dev_err(hdev, "no memory for new connection"); goto unlock; } @@ -4749,8 +4751,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, case LE_ADV_SCAN_RSP: break; default: - BT_ERR_RATELIMITED("Unknown advertising packet type: 0x%02x", - type); + bt_dev_err_ratelimited(hdev, "unknown advertising packet " + "type: 0x%02x", type); return; } @@ -4769,8 +4771,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, /* Adjust for actual length */ if (len != real_len) { - BT_ERR_RATELIMITED("%s advertising data length corrected", - hdev->name); + bt_dev_err_ratelimited(hdev, "advertising data len corrected"); len = real_len; } @@ -5192,7 +5193,7 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode, return false; if (skb->len < sizeof(*hdr)) { - BT_ERR("Too short HCI event"); + bt_dev_err(hdev, "too short HCI event"); return false; } @@ -5206,12 +5207,13 @@ static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode, } if (hdr->evt != HCI_EV_CMD_COMPLETE) { - BT_DBG("Last event is not cmd complete (0x%2.2x)", hdr->evt); + bt_dev_err(hdev, "last event is not cmd complete (0x%2.2x)", + hdr->evt); return false; } if (skb->len < sizeof(*ev)) { - BT_ERR("Too short cmd_complete event"); + bt_dev_err(hdev, "too short cmd_complete event"); return false; } diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c index 7f28d17dc792..abc0f3224dd1 100644 --- a/net/bluetooth/hci_request.c +++ b/net/bluetooth/hci_request.c @@ -336,8 +336,8 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, skb = hci_prepare_cmd(hdev, opcode, plen, param); if (!skb) { - BT_ERR("%s no memory for command (opcode 0x%4.4x)", - hdev->name, opcode); + bt_dev_err(hdev, "no memory for command (opcode 0x%4.4x)", + opcode); req->err = -ENOMEM; return; } @@ -1426,7 +1426,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa); if (err < 0) { - BT_ERR("%s failed to generate new RPA", hdev->name); + bt_dev_err(hdev, "failed to generate new RPA"); return err; } @@ -1788,7 +1788,7 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason) err = hci_req_run(&req, abort_conn_complete); if (err && err != -ENODATA) { - BT_ERR("Failed to run HCI request: err %d", err); + bt_dev_err(conn->hdev, "failed to run HCI request: err %d", err); return err; } @@ -1872,7 +1872,8 @@ static void le_scan_disable_work(struct work_struct *work) hci_req_sync(hdev, le_scan_disable, 0, HCI_CMD_TIMEOUT, &status); if (status) { - BT_ERR("Failed to disable LE scan: status 0x%02x", status); + bt_dev_err(hdev, "failed to disable LE scan: status 0x%02x", + status); return; } @@ -1903,7 +1904,7 @@ static void le_scan_disable_work(struct work_struct *work) hci_req_sync(hdev, bredr_inquiry, DISCOV_INTERLEAVED_INQUIRY_LEN, HCI_CMD_TIMEOUT, &status); if (status) { - BT_ERR("Inquiry failed: status 0x%02x", status); + bt_dev_err(hdev, "inquiry failed: status 0x%02x", status); goto discov_stopped; } @@ -1945,7 +1946,8 @@ static void le_scan_restart_work(struct work_struct *work) hci_req_sync(hdev, le_scan_restart, 0, HCI_CMD_TIMEOUT, &status); if (status) { - BT_ERR("Failed to restart LE scan: status %d", status); + bt_dev_err(hdev, "failed to restart LE scan: status %d", + status); return; } diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index aa300f3a0d51..34aaa2340ac8 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -50,7 +50,7 @@ void hci_conn_add_sysfs(struct hci_conn *conn) dev_set_name(&conn->dev, "%s:%d", hdev->name, conn->handle); if (device_add(&conn->dev) < 0) { - BT_ERR("Failed to register connection device"); + bt_dev_err(hdev, "failed to register connection device"); return; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 07a3cc29f426..6e9fc86d8daf 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2159,8 +2159,8 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, key_count = __le16_to_cpu(cp->key_count); if (key_count > max_key_count) { - BT_ERR("load_link_keys: too big key_count value %u", - key_count); + bt_dev_err(hdev, "load_link_keys: too big key_count value %u", + key_count); return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, MGMT_STATUS_INVALID_PARAMS); } @@ -2168,8 +2168,8 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_link_key_info); if (expected_len != len) { - BT_ERR("load_link_keys: expected %u bytes, got %u bytes", - expected_len, len); + bt_dev_err(hdev, "load_link_keys: expected %u bytes, got %u bytes", + expected_len, len); return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, MGMT_STATUS_INVALID_PARAMS); } @@ -2561,7 +2561,7 @@ static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data, memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr)); - BT_ERR("PIN code is not 16 bytes long"); + bt_dev_err(hdev, "PIN code is not 16 bytes long"); err = send_pin_code_neg_reply(sk, hdev, &ncp); if (err >= 0) @@ -3391,7 +3391,8 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, MGMT_OP_ADD_REMOTE_OOB_DATA, status, &cp->addr, sizeof(cp->addr)); } else { - BT_ERR("add_remote_oob_data: invalid length of %u bytes", len); + bt_dev_err(hdev, "add_remote_oob_data: invalid len of %u bytes", + len); err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, MGMT_STATUS_INVALID_PARAMS); } @@ -3604,8 +3605,8 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev, uuid_count = __le16_to_cpu(cp->uuid_count); if (uuid_count > max_uuid_count) { - BT_ERR("service_discovery: too big uuid_count value %u", - uuid_count); + bt_dev_err(hdev, "service_discovery: too big uuid_count value %u", + uuid_count); err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_SERVICE_DISCOVERY, MGMT_STATUS_INVALID_PARAMS, &cp->type, @@ -3615,8 +3616,8 @@ static int start_service_discovery(struct sock *sk, struct hci_dev *hdev, expected_len = sizeof(*cp) + uuid_count * 16; if (expected_len != len) { - BT_ERR("service_discovery: expected %u bytes, got %u bytes", - expected_len, len); + bt_dev_err(hdev, "service_discovery: expected %u bytes, got %u bytes", + expected_len, len); err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_START_SERVICE_DISCOVERY, MGMT_STATUS_INVALID_PARAMS, &cp->type, @@ -3943,7 +3944,7 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status, err = hci_req_run(&req, enable_advertising_instance); if (err) - BT_ERR("Failed to re-configure advertising"); + bt_dev_err(hdev, "failed to re-configure advertising"); unlock: hci_dev_unlock(hdev); @@ -4664,15 +4665,16 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, irk_count = __le16_to_cpu(cp->irk_count); if (irk_count > max_irk_count) { - BT_ERR("load_irks: too big irk_count value %u", irk_count); + bt_dev_err(hdev, "load_irks: too big irk_count value %u", + irk_count); return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS, MGMT_STATUS_INVALID_PARAMS); } expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info); if (expected_len != len) { - BT_ERR("load_irks: expected %u bytes, got %u bytes", - expected_len, len); + bt_dev_err(hdev, "load_irks: expected %u bytes, got %u bytes", + expected_len, len); return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS, MGMT_STATUS_INVALID_PARAMS); } @@ -4745,7 +4747,8 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, key_count = __le16_to_cpu(cp->key_count); if (key_count > max_key_count) { - BT_ERR("load_ltks: too big key_count value %u", key_count); + bt_dev_err(hdev, "load_ltks: too big key_count value %u", + key_count); return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, MGMT_STATUS_INVALID_PARAMS); } @@ -4753,8 +4756,8 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_ltk_info); if (expected_len != len) { - BT_ERR("load_keys: expected %u bytes, got %u bytes", - expected_len, len); + bt_dev_err(hdev, "load_keys: expected %u bytes, got %u bytes", + expected_len, len); return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, MGMT_STATUS_INVALID_PARAMS); } @@ -4873,14 +4876,15 @@ static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status, } if (!cp) { - BT_ERR("invalid sent_cmd in conn_info response"); + bt_dev_err(hdev, "invalid sent_cmd in conn_info response"); goto unlock; } handle = __le16_to_cpu(cp->handle); conn = hci_conn_hash_lookup_handle(hdev, handle); if (!conn) { - BT_ERR("unknown handle (%d) in conn_info response", handle); + bt_dev_err(hdev, "unknown handle (%d) in conn_info response", + handle); goto unlock; } @@ -5477,8 +5481,8 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, param_count = __le16_to_cpu(cp->param_count); if (param_count > max_param_count) { - BT_ERR("load_conn_param: too big param_count value %u", - param_count); + bt_dev_err(hdev, "load_conn_param: too big param_count value %u", + param_count); return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, MGMT_STATUS_INVALID_PARAMS); } @@ -5486,8 +5490,8 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, expected_len = sizeof(*cp) + param_count * sizeof(struct mgmt_conn_param); if (expected_len != len) { - BT_ERR("load_conn_param: expected %u bytes, got %u bytes", - expected_len, len); + bt_dev_err(hdev, "load_conn_param: expected %u bytes, got %u bytes", + expected_len, len); return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, MGMT_STATUS_INVALID_PARAMS); } @@ -5512,7 +5516,7 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, } else if (param->addr.type == BDADDR_LE_RANDOM) { addr_type = ADDR_LE_DEV_RANDOM; } else { - BT_ERR("Ignoring invalid connection parameters"); + bt_dev_err(hdev, "ignoring invalid connection parameters"); continue; } @@ -5525,14 +5529,14 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, min, max, latency, timeout); if (hci_check_conn_params(min, max, latency, timeout) < 0) { - BT_ERR("Ignoring invalid connection parameters"); + bt_dev_err(hdev, "ignoring invalid connection parameters"); continue; } hci_param = hci_conn_params_add(hdev, ¶m->addr.bdaddr, addr_type); if (!hci_param) { - BT_ERR("Failed to add connection parameters"); + bt_dev_err(hdev, "failed to add connection parameters"); continue; } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index d41449b9e9d6..01117ae84f1d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -996,7 +996,8 @@ static u8 smp_random(struct smp_chan *smp) return SMP_UNSPECIFIED; if (crypto_memneq(smp->pcnf, confirm, sizeof(smp->pcnf))) { - BT_ERR("Pairing failed (confirmation values mismatch)"); + bt_dev_err(hcon->hdev, "pairing failed " + "(confirmation values mismatch)"); return SMP_CONFIRM_FAILED; } @@ -1210,7 +1211,7 @@ static void sc_generate_ltk(struct smp_chan *smp) key = hci_find_link_key(hdev, &hcon->dst); if (!key) { - BT_ERR("%s No Link Key found to generate LTK", hdev->name); + bt_dev_err(hdev, "no Link Key found to generate LTK"); return; } @@ -2067,11 +2068,11 @@ static int fixup_sc_false_positive(struct smp_chan *smp) return SMP_UNSPECIFIED; if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) { - BT_ERR("Refusing SMP SC -> legacy fallback in SC-only mode"); + bt_dev_err(hdev, "refusing legacy fallback in SC-only mode"); return SMP_UNSPECIFIED; } - BT_ERR("Trying to fall back to legacy SMP"); + bt_dev_err(hdev, "trying to fall back to legacy SMP"); req = (void *) &smp->preq[1]; rsp = (void *) &smp->prsp[1]; @@ -2082,7 +2083,7 @@ static int fixup_sc_false_positive(struct smp_chan *smp) auth = req->auth_req & AUTH_REQ_MASK(hdev); if (tk_request(conn, 0, auth, rsp->io_capability, req->io_capability)) { - BT_ERR("Failed to fall back to legacy SMP"); + bt_dev_err(hdev, "failed to fall back to legacy SMP"); return SMP_UNSPECIFIED; } @@ -2355,7 +2356,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) chan = conn->smp; if (!chan) { - BT_ERR("SMP security requested but not available"); + bt_dev_err(hcon->hdev, "security requested but not available"); return 1; } @@ -2548,7 +2549,7 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, */ if (!bacmp(&info->bdaddr, BDADDR_ANY) || !hci_is_identity_address(&info->bdaddr, info->addr_type)) { - BT_ERR("Ignoring IRK with no identity address"); + bt_dev_err(hcon->hdev, "ignoring IRK with no identity address"); goto distribute; } @@ -2953,8 +2954,8 @@ done: return err; drop: - BT_ERR("%s unexpected SMP command 0x%02x from %pMR", hcon->hdev->name, - code, &hcon->dst); + bt_dev_err(hcon->hdev, "unexpected SMP command 0x%02x from %pMR", + code, &hcon->dst); kfree_skb(skb); return 0; } @@ -3021,8 +3022,7 @@ static void bredr_pairing(struct l2cap_chan *chan) smp = smp_chan_create(conn); if (!smp) { - BT_ERR("%s unable to create SMP context for BR/EDR", - hdev->name); + bt_dev_err(hdev, "unable to create SMP context for BR/EDR"); return; } -- cgit v1.2.3-59-g8ed1b From 012e513e1bfeb39e1ce393f5a444f772ea27e954 Mon Sep 17 00:00:00 2001 From: Anand Jain Date: Wed, 30 Aug 2017 22:36:48 +0800 Subject: btrfs: declare TRACE_DEFINE_ENUM for each of show_flush_state enum So that perf can show the state symbol. Signed-off-by: Anand Jain Reviewed-by: David Sterba Signed-off-by: David Sterba --- include/trace/events/btrfs.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'include') diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index dc1d0df91e0b..77437f545c63 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -28,6 +28,13 @@ struct btrfs_qgroup_extent_record; struct btrfs_qgroup; struct prelim_ref; +TRACE_DEFINE_ENUM(FLUSH_DELAYED_ITEMS_NR); +TRACE_DEFINE_ENUM(FLUSH_DELAYED_ITEMS); +TRACE_DEFINE_ENUM(FLUSH_DELALLOC); +TRACE_DEFINE_ENUM(FLUSH_DELALLOC_WAIT); +TRACE_DEFINE_ENUM(ALLOC_CHUNK); +TRACE_DEFINE_ENUM(COMMIT_TRANS); + #define show_ref_type(type) \ __print_symbolic(type, \ { BTRFS_TREE_BLOCK_REF_KEY, "TREE_BLOCK_REF" }, \ -- cgit v1.2.3-59-g8ed1b From 40c3c40947324d9f40bf47830c92c59a9bbadf4a Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Wed, 23 Aug 2017 16:57:58 +0900 Subject: btrfs: Add sanity check for EXTENT_DATA when reading out leaf Add extra checks for item with EXTENT_DATA type. This checks the following thing: 0) Key offset All key offsets must be aligned to sectorsize. Inline extent must have 0 for key offset. 1) Item size Uncompressed inline file extent size must match item size. (Compressed inline file extent has no information about its on-disk size.) Regular/preallocated file extent size must be a fixed value. 2) Every member of regular file extent item Including alignment for bytenr and offset, possible value for compression/encryption/type. 3) Type/compression/encode must be one of the valid values. This should be the most comprehensive and strict check in the context of btrfs_item for EXTENT_DATA. Signed-off-by: Qu Wenruo Reviewed-by: Nikolay Borisov Reviewed-by: David Sterba [ switch to BTRFS_FILE_EXTENT_TYPES, similar to what BTRFS_COMPRESS_TYPES does ] Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 103 ++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/btrfs_tree.h | 1 + 2 files changed, 104 insertions(+) (limited to 'include') diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index d1770b3e0385..b863d41f7d0a 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -549,6 +549,100 @@ static int check_tree_block_fsid(struct btrfs_fs_info *fs_info, btrfs_header_level(eb) == 0 ? "leaf" : "node", \ reason, btrfs_header_bytenr(eb), root->objectid, slot) +static int check_extent_data_item(struct btrfs_root *root, + struct extent_buffer *leaf, + struct btrfs_key *key, int slot) +{ + struct btrfs_file_extent_item *fi; + u32 sectorsize = root->fs_info->sectorsize; + u32 item_size = btrfs_item_size_nr(leaf, slot); + + if (!IS_ALIGNED(key->offset, sectorsize)) { + CORRUPT("unaligned key offset for file extent", + leaf, root, slot); + return -EUCLEAN; + } + + fi = btrfs_item_ptr(leaf, slot, struct btrfs_file_extent_item); + + if (btrfs_file_extent_type(leaf, fi) > BTRFS_FILE_EXTENT_TYPES) { + CORRUPT("invalid file extent type", leaf, root, slot); + return -EUCLEAN; + } + + /* + * Support for new compression/encrption must introduce incompat flag, + * and must be caught in open_ctree(). + */ + if (btrfs_file_extent_compression(leaf, fi) > BTRFS_COMPRESS_TYPES) { + CORRUPT("invalid file extent compression", leaf, root, slot); + return -EUCLEAN; + } + if (btrfs_file_extent_encryption(leaf, fi)) { + CORRUPT("invalid file extent encryption", leaf, root, slot); + return -EUCLEAN; + } + if (btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE) { + /* Inline extent must have 0 as key offset */ + if (key->offset) { + CORRUPT("inline extent has non-zero key offset", + leaf, root, slot); + return -EUCLEAN; + } + + /* Compressed inline extent has no on-disk size, skip it */ + if (btrfs_file_extent_compression(leaf, fi) != + BTRFS_COMPRESS_NONE) + return 0; + + /* Uncompressed inline extent size must match item size */ + if (item_size != BTRFS_FILE_EXTENT_INLINE_DATA_START + + btrfs_file_extent_ram_bytes(leaf, fi)) { + CORRUPT("plaintext inline extent has invalid size", + leaf, root, slot); + return -EUCLEAN; + } + return 0; + } + + /* Regular or preallocated extent has fixed item size */ + if (item_size != sizeof(*fi)) { + CORRUPT( + "regluar or preallocated extent data item size is invalid", + leaf, root, slot); + return -EUCLEAN; + } + if (!IS_ALIGNED(btrfs_file_extent_ram_bytes(leaf, fi), sectorsize) || + !IS_ALIGNED(btrfs_file_extent_disk_bytenr(leaf, fi), sectorsize) || + !IS_ALIGNED(btrfs_file_extent_disk_num_bytes(leaf, fi), sectorsize) || + !IS_ALIGNED(btrfs_file_extent_offset(leaf, fi), sectorsize) || + !IS_ALIGNED(btrfs_file_extent_num_bytes(leaf, fi), sectorsize)) { + CORRUPT( + "regular or preallocated extent data item has unaligned value", + leaf, root, slot); + return -EUCLEAN; + } + + return 0; +} + +/* + * Common point to switch the item-specific validation. + */ +static int check_leaf_item(struct btrfs_root *root, + struct extent_buffer *leaf, + struct btrfs_key *key, int slot) +{ + int ret = 0; + + switch (key->type) { + case BTRFS_EXTENT_DATA_KEY: + ret = check_extent_data_item(root, leaf, key, slot); + break; + } + return ret; +} + static noinline int check_leaf(struct btrfs_root *root, struct extent_buffer *leaf) { @@ -605,9 +699,13 @@ static noinline int check_leaf(struct btrfs_root *root, * 1) key order * 2) item offset and size * No overlap, no hole, all inside the leaf. + * 3) item content + * If possible, do comprehensive sanity check. + * NOTE: All checks must only rely on the item data itself. */ for (slot = 0; slot < nritems; slot++) { u32 item_end_expected; + int ret; btrfs_item_key_to_cpu(leaf, &key, slot); @@ -650,6 +748,11 @@ static noinline int check_leaf(struct btrfs_root *root, return -EUCLEAN; } + /* Check if the item size and content meet other criteria */ + ret = check_leaf_item(root, leaf, &key, slot); + if (ret < 0) + return ret; + prev_key.objectid = key.objectid; prev_key.type = key.type; prev_key.offset = key.offset; diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index 10689e1fdf11..3142645a27f5 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -732,6 +732,7 @@ struct btrfs_balance_item { #define BTRFS_FILE_EXTENT_INLINE 0 #define BTRFS_FILE_EXTENT_REG 1 #define BTRFS_FILE_EXTENT_PREALLOC 2 +#define BTRFS_FILE_EXTENT_TYPES 2 struct btrfs_file_extent_item { /* -- cgit v1.2.3-59-g8ed1b From d278850eff3053ef166cf64c16f798dfe36278a2 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 29 Sep 2017 15:43:57 -0400 Subject: btrfs: remove delayed_ref_node from ref_head This is just excessive information in the ref_head, and makes the code complicated. It is a relic from when we had the heads and the refs in the same tree, which is no longer the case. With this removal I've cleaned up a bunch of the cruft around this old assumption as well. Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/backref.c | 4 +- fs/btrfs/delayed-ref.c | 126 +++++++++++++++++++------------------------ fs/btrfs/delayed-ref.h | 49 ++++++----------- fs/btrfs/disk-io.c | 12 ++--- fs/btrfs/extent-tree.c | 90 ++++++++++++------------------- include/trace/events/btrfs.h | 13 ++--- 6 files changed, 119 insertions(+), 175 deletions(-) (limited to 'include') diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index b517ef1477ea..33cba1abf8b6 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -1178,7 +1178,7 @@ again: head = btrfs_find_delayed_ref_head(delayed_refs, bytenr); if (head) { if (!mutex_trylock(&head->mutex)) { - refcount_inc(&head->node.refs); + refcount_inc(&head->refs); spin_unlock(&delayed_refs->lock); btrfs_release_path(path); @@ -1189,7 +1189,7 @@ again: */ mutex_lock(&head->mutex); mutex_unlock(&head->mutex); - btrfs_put_delayed_ref(&head->node); + btrfs_put_delayed_ref_head(head); goto again; } spin_unlock(&delayed_refs->lock); diff --git a/fs/btrfs/delayed-ref.c b/fs/btrfs/delayed-ref.c index 93ffa898df6d..b9b41c838da4 100644 --- a/fs/btrfs/delayed-ref.c +++ b/fs/btrfs/delayed-ref.c @@ -96,15 +96,15 @@ static struct btrfs_delayed_ref_head *htree_insert(struct rb_root *root, u64 bytenr; ins = rb_entry(node, struct btrfs_delayed_ref_head, href_node); - bytenr = ins->node.bytenr; + bytenr = ins->bytenr; while (*p) { parent_node = *p; entry = rb_entry(parent_node, struct btrfs_delayed_ref_head, href_node); - if (bytenr < entry->node.bytenr) + if (bytenr < entry->bytenr) p = &(*p)->rb_left; - else if (bytenr > entry->node.bytenr) + else if (bytenr > entry->bytenr) p = &(*p)->rb_right; else return entry; @@ -133,15 +133,15 @@ find_ref_head(struct rb_root *root, u64 bytenr, while (n) { entry = rb_entry(n, struct btrfs_delayed_ref_head, href_node); - if (bytenr < entry->node.bytenr) + if (bytenr < entry->bytenr) n = n->rb_left; - else if (bytenr > entry->node.bytenr) + else if (bytenr > entry->bytenr) n = n->rb_right; else return entry; } if (entry && return_bigger) { - if (bytenr > entry->node.bytenr) { + if (bytenr > entry->bytenr) { n = rb_next(&entry->href_node); if (!n) n = rb_first(root); @@ -164,17 +164,17 @@ int btrfs_delayed_ref_lock(struct btrfs_trans_handle *trans, if (mutex_trylock(&head->mutex)) return 0; - refcount_inc(&head->node.refs); + refcount_inc(&head->refs); spin_unlock(&delayed_refs->lock); mutex_lock(&head->mutex); spin_lock(&delayed_refs->lock); - if (!head->node.in_tree) { + if (RB_EMPTY_NODE(&head->href_node)) { mutex_unlock(&head->mutex); - btrfs_put_delayed_ref(&head->node); + btrfs_put_delayed_ref_head(head); return -EAGAIN; } - btrfs_put_delayed_ref(&head->node); + btrfs_put_delayed_ref_head(head); return 0; } @@ -183,15 +183,10 @@ static inline void drop_delayed_ref(struct btrfs_trans_handle *trans, struct btrfs_delayed_ref_head *head, struct btrfs_delayed_ref_node *ref) { - if (btrfs_delayed_ref_is_head(ref)) { - head = btrfs_delayed_node_to_head(ref); - rb_erase(&head->href_node, &delayed_refs->href_root); - } else { - assert_spin_locked(&head->lock); - list_del(&ref->list); - if (!list_empty(&ref->add_list)) - list_del(&ref->add_list); - } + assert_spin_locked(&head->lock); + list_del(&ref->list); + if (!list_empty(&ref->add_list)) + list_del(&ref->add_list); ref->in_tree = 0; btrfs_put_delayed_ref(ref); atomic_dec(&delayed_refs->num_entries); @@ -380,8 +375,8 @@ again: head->processing = 1; WARN_ON(delayed_refs->num_heads_ready == 0); delayed_refs->num_heads_ready--; - delayed_refs->run_delayed_start = head->node.bytenr + - head->node.num_bytes; + delayed_refs->run_delayed_start = head->bytenr + + head->num_bytes; return head; } @@ -469,20 +464,16 @@ add_tail: */ static noinline void update_existing_head_ref(struct btrfs_delayed_ref_root *delayed_refs, - struct btrfs_delayed_ref_node *existing, - struct btrfs_delayed_ref_node *update, + struct btrfs_delayed_ref_head *existing, + struct btrfs_delayed_ref_head *update, int *old_ref_mod_ret) { - struct btrfs_delayed_ref_head *existing_ref; - struct btrfs_delayed_ref_head *ref; int old_ref_mod; - existing_ref = btrfs_delayed_node_to_head(existing); - ref = btrfs_delayed_node_to_head(update); - BUG_ON(existing_ref->is_data != ref->is_data); + BUG_ON(existing->is_data != update->is_data); - spin_lock(&existing_ref->lock); - if (ref->must_insert_reserved) { + spin_lock(&existing->lock); + if (update->must_insert_reserved) { /* if the extent was freed and then * reallocated before the delayed ref * entries were processed, we can end up @@ -490,7 +481,7 @@ update_existing_head_ref(struct btrfs_delayed_ref_root *delayed_refs, * the must_insert_reserved flag set. * Set it again here */ - existing_ref->must_insert_reserved = ref->must_insert_reserved; + existing->must_insert_reserved = update->must_insert_reserved; /* * update the num_bytes so we make sure the accounting @@ -500,22 +491,22 @@ update_existing_head_ref(struct btrfs_delayed_ref_root *delayed_refs, } - if (ref->extent_op) { - if (!existing_ref->extent_op) { - existing_ref->extent_op = ref->extent_op; + if (update->extent_op) { + if (!existing->extent_op) { + existing->extent_op = update->extent_op; } else { - if (ref->extent_op->update_key) { - memcpy(&existing_ref->extent_op->key, - &ref->extent_op->key, - sizeof(ref->extent_op->key)); - existing_ref->extent_op->update_key = true; + if (update->extent_op->update_key) { + memcpy(&existing->extent_op->key, + &update->extent_op->key, + sizeof(update->extent_op->key)); + existing->extent_op->update_key = true; } - if (ref->extent_op->update_flags) { - existing_ref->extent_op->flags_to_set |= - ref->extent_op->flags_to_set; - existing_ref->extent_op->update_flags = true; + if (update->extent_op->update_flags) { + existing->extent_op->flags_to_set |= + update->extent_op->flags_to_set; + existing->extent_op->update_flags = true; } - btrfs_free_delayed_extent_op(ref->extent_op); + btrfs_free_delayed_extent_op(update->extent_op); } } /* @@ -523,23 +514,23 @@ update_existing_head_ref(struct btrfs_delayed_ref_root *delayed_refs, * only need the lock for this case cause we could be processing it * currently, for refs we just added we know we're a-ok. */ - old_ref_mod = existing_ref->total_ref_mod; + old_ref_mod = existing->total_ref_mod; if (old_ref_mod_ret) *old_ref_mod_ret = old_ref_mod; existing->ref_mod += update->ref_mod; - existing_ref->total_ref_mod += update->ref_mod; + existing->total_ref_mod += update->ref_mod; /* * If we are going to from a positive ref mod to a negative or vice * versa we need to make sure to adjust pending_csums accordingly. */ - if (existing_ref->is_data) { - if (existing_ref->total_ref_mod >= 0 && old_ref_mod < 0) + if (existing->is_data) { + if (existing->total_ref_mod >= 0 && old_ref_mod < 0) delayed_refs->pending_csums -= existing->num_bytes; - if (existing_ref->total_ref_mod < 0 && old_ref_mod >= 0) + if (existing->total_ref_mod < 0 && old_ref_mod >= 0) delayed_refs->pending_csums += existing->num_bytes; } - spin_unlock(&existing_ref->lock); + spin_unlock(&existing->lock); } /* @@ -550,14 +541,13 @@ update_existing_head_ref(struct btrfs_delayed_ref_root *delayed_refs, static noinline struct btrfs_delayed_ref_head * add_delayed_ref_head(struct btrfs_fs_info *fs_info, struct btrfs_trans_handle *trans, - struct btrfs_delayed_ref_node *ref, + struct btrfs_delayed_ref_head *head_ref, struct btrfs_qgroup_extent_record *qrecord, u64 bytenr, u64 num_bytes, u64 ref_root, u64 reserved, int action, int is_data, int *qrecord_inserted_ret, int *old_ref_mod, int *new_ref_mod) { struct btrfs_delayed_ref_head *existing; - struct btrfs_delayed_ref_head *head_ref = NULL; struct btrfs_delayed_ref_root *delayed_refs; int count_mod = 1; int must_insert_reserved = 0; @@ -593,26 +583,21 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, delayed_refs = &trans->transaction->delayed_refs; - /* first set the basic ref node struct up */ - refcount_set(&ref->refs, 1); - ref->bytenr = bytenr; - ref->num_bytes = num_bytes; - ref->ref_mod = count_mod; - ref->type = 0; - ref->action = 0; - ref->is_head = 1; - ref->in_tree = 1; - ref->seq = 0; - - head_ref = btrfs_delayed_node_to_head(ref); + refcount_set(&head_ref->refs, 1); + head_ref->bytenr = bytenr; + head_ref->num_bytes = num_bytes; + head_ref->ref_mod = count_mod; head_ref->must_insert_reserved = must_insert_reserved; head_ref->is_data = is_data; INIT_LIST_HEAD(&head_ref->ref_list); INIT_LIST_HEAD(&head_ref->ref_add_list); + RB_CLEAR_NODE(&head_ref->href_node); head_ref->processing = 0; head_ref->total_ref_mod = count_mod; head_ref->qgroup_reserved = 0; head_ref->qgroup_ref_root = 0; + spin_lock_init(&head_ref->lock); + mutex_init(&head_ref->mutex); /* Record qgroup extent info if provided */ if (qrecord) { @@ -632,17 +617,14 @@ add_delayed_ref_head(struct btrfs_fs_info *fs_info, qrecord_inserted = 1; } - spin_lock_init(&head_ref->lock); - mutex_init(&head_ref->mutex); - - trace_add_delayed_ref_head(fs_info, ref, head_ref, action); + trace_add_delayed_ref_head(fs_info, head_ref, action); existing = htree_insert(&delayed_refs->href_root, &head_ref->href_node); if (existing) { WARN_ON(ref_root && reserved && existing->qgroup_ref_root && existing->qgroup_reserved); - update_existing_head_ref(delayed_refs, &existing->node, ref, + update_existing_head_ref(delayed_refs, existing, head_ref, old_ref_mod); /* * we've updated the existing ref, free the newly @@ -821,7 +803,7 @@ int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, * insert both the head node and the new ref without dropping * the spin lock */ - head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, record, + head_ref = add_delayed_ref_head(fs_info, trans, head_ref, record, bytenr, num_bytes, 0, 0, action, 0, &qrecord_inserted, old_ref_mod, new_ref_mod); @@ -888,7 +870,7 @@ int btrfs_add_delayed_data_ref(struct btrfs_fs_info *fs_info, * insert both the head node and the new ref without dropping * the spin lock */ - head_ref = add_delayed_ref_head(fs_info, trans, &head_ref->node, record, + head_ref = add_delayed_ref_head(fs_info, trans, head_ref, record, bytenr, num_bytes, ref_root, reserved, action, 1, &qrecord_inserted, old_ref_mod, new_ref_mod); @@ -920,7 +902,7 @@ int btrfs_add_delayed_extent_op(struct btrfs_fs_info *fs_info, delayed_refs = &trans->transaction->delayed_refs; spin_lock(&delayed_refs->lock); - add_delayed_ref_head(fs_info, trans, &head_ref->node, NULL, bytenr, + add_delayed_ref_head(fs_info, trans, head_ref, NULL, bytenr, num_bytes, 0, 0, BTRFS_UPDATE_DELAYED_HEAD, extent_op->is_data, NULL, NULL, NULL); diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h index ce88e4ac5276..1ce11858d727 100644 --- a/fs/btrfs/delayed-ref.h +++ b/fs/btrfs/delayed-ref.h @@ -26,15 +26,6 @@ #define BTRFS_ADD_DELAYED_EXTENT 3 /* record a full extent allocation */ #define BTRFS_UPDATE_DELAYED_HEAD 4 /* not changing ref count on head ref */ -/* - * XXX: Qu: I really hate the design that ref_head and tree/data ref shares the - * same ref_node structure. - * Ref_head is in a higher logic level than tree/data ref, and duplicated - * bytenr/num_bytes in ref_node is really a waste or memory, they should be - * referred from ref_head. - * This gets more disgusting after we use list to store tree/data ref in - * ref_head. Must clean this mess up later. - */ struct btrfs_delayed_ref_node { /*data/tree ref use list, stored in ref_head->ref_list. */ struct list_head list; @@ -91,8 +82,9 @@ struct btrfs_delayed_extent_op { * reference count modifications we've queued up. */ struct btrfs_delayed_ref_head { - struct btrfs_delayed_ref_node node; - + u64 bytenr; + u64 num_bytes; + refcount_t refs; /* * the mutex is held while running the refs, and it is also * held when checking the sum of reference modifications. @@ -115,6 +107,14 @@ struct btrfs_delayed_ref_head { */ int total_ref_mod; + /* + * This is the current outstanding mod references for this bytenr. This + * is used with lookup_extent_info to get an accurate reference count + * for a bytenr, so it is adjusted as delayed refs are run so that any + * on disk reference count + ref_mod is accurate. + */ + int ref_mod; + /* * For qgroup reserved space freeing. * @@ -234,15 +234,18 @@ static inline void btrfs_put_delayed_ref(struct btrfs_delayed_ref_node *ref) case BTRFS_SHARED_DATA_REF_KEY: kmem_cache_free(btrfs_delayed_data_ref_cachep, ref); break; - case 0: - kmem_cache_free(btrfs_delayed_ref_head_cachep, ref); - break; default: BUG(); } } } +static inline void btrfs_put_delayed_ref_head(struct btrfs_delayed_ref_head *head) +{ + if (refcount_dec_and_test(&head->refs)) + kmem_cache_free(btrfs_delayed_ref_head_cachep, head); +} + int btrfs_add_delayed_tree_ref(struct btrfs_fs_info *fs_info, struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, u64 parent, @@ -282,36 +285,18 @@ int btrfs_check_delayed_seq(struct btrfs_fs_info *fs_info, struct btrfs_delayed_ref_root *delayed_refs, u64 seq); -/* - * a node might live in a head or a regular ref, this lets you - * test for the proper type to use. - */ -static int btrfs_delayed_ref_is_head(struct btrfs_delayed_ref_node *node) -{ - return node->is_head; -} - /* * helper functions to cast a node into its container */ static inline struct btrfs_delayed_tree_ref * btrfs_delayed_node_to_tree_ref(struct btrfs_delayed_ref_node *node) { - WARN_ON(btrfs_delayed_ref_is_head(node)); return container_of(node, struct btrfs_delayed_tree_ref, node); } static inline struct btrfs_delayed_data_ref * btrfs_delayed_node_to_data_ref(struct btrfs_delayed_ref_node *node) { - WARN_ON(btrfs_delayed_ref_is_head(node)); return container_of(node, struct btrfs_delayed_data_ref, node); } - -static inline struct btrfs_delayed_ref_head * -btrfs_delayed_node_to_head(struct btrfs_delayed_ref_node *node) -{ - WARN_ON(!btrfs_delayed_ref_is_head(node)); - return container_of(node, struct btrfs_delayed_ref_head, node); -} #endif diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index f971d5680e6f..69ce738c00d0 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -4121,12 +4121,12 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, head = rb_entry(node, struct btrfs_delayed_ref_head, href_node); if (!mutex_trylock(&head->mutex)) { - refcount_inc(&head->node.refs); + refcount_inc(&head->refs); spin_unlock(&delayed_refs->lock); mutex_lock(&head->mutex); mutex_unlock(&head->mutex); - btrfs_put_delayed_ref(&head->node); + btrfs_put_delayed_ref_head(head); spin_lock(&delayed_refs->lock); continue; } @@ -4147,16 +4147,16 @@ static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans, if (head->processing == 0) delayed_refs->num_heads_ready--; atomic_dec(&delayed_refs->num_entries); - head->node.in_tree = 0; rb_erase(&head->href_node, &delayed_refs->href_root); + RB_CLEAR_NODE(&head->href_node); spin_unlock(&head->lock); spin_unlock(&delayed_refs->lock); mutex_unlock(&head->mutex); if (pin_bytes) - btrfs_pin_extent(fs_info, head->node.bytenr, - head->node.num_bytes, 1); - btrfs_put_delayed_ref(&head->node); + btrfs_pin_extent(fs_info, head->bytenr, + head->num_bytes, 1); + btrfs_put_delayed_ref_head(head); cond_resched(); spin_lock(&delayed_refs->lock); } diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 628ae71094f2..423d89145bac 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -912,7 +912,7 @@ search_again: head = btrfs_find_delayed_ref_head(delayed_refs, bytenr); if (head) { if (!mutex_trylock(&head->mutex)) { - refcount_inc(&head->node.refs); + refcount_inc(&head->refs); spin_unlock(&delayed_refs->lock); btrfs_release_path(path); @@ -923,7 +923,7 @@ search_again: */ mutex_lock(&head->mutex); mutex_unlock(&head->mutex); - btrfs_put_delayed_ref(&head->node); + btrfs_put_delayed_ref_head(head); goto search_again; } spin_lock(&head->lock); @@ -932,7 +932,7 @@ search_again: else BUG_ON(num_refs == 0); - num_refs += head->node.ref_mod; + num_refs += head->ref_mod; spin_unlock(&head->lock); mutex_unlock(&head->mutex); } @@ -2337,7 +2337,7 @@ static void __run_delayed_extent_op(struct btrfs_delayed_extent_op *extent_op, static int run_delayed_extent_op(struct btrfs_trans_handle *trans, struct btrfs_fs_info *fs_info, - struct btrfs_delayed_ref_node *node, + struct btrfs_delayed_ref_head *head, struct btrfs_delayed_extent_op *extent_op) { struct btrfs_key key; @@ -2359,14 +2359,14 @@ static int run_delayed_extent_op(struct btrfs_trans_handle *trans, if (!path) return -ENOMEM; - key.objectid = node->bytenr; + key.objectid = head->bytenr; if (metadata) { key.type = BTRFS_METADATA_ITEM_KEY; key.offset = extent_op->level; } else { key.type = BTRFS_EXTENT_ITEM_KEY; - key.offset = node->num_bytes; + key.offset = head->num_bytes; } again: @@ -2383,17 +2383,17 @@ again: path->slots[0]--; btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); - if (key.objectid == node->bytenr && + if (key.objectid == head->bytenr && key.type == BTRFS_EXTENT_ITEM_KEY && - key.offset == node->num_bytes) + key.offset == head->num_bytes) ret = 0; } if (ret > 0) { btrfs_release_path(path); metadata = 0; - key.objectid = node->bytenr; - key.offset = node->num_bytes; + key.objectid = head->bytenr; + key.offset = head->num_bytes; key.type = BTRFS_EXTENT_ITEM_KEY; goto again; } @@ -2562,7 +2562,7 @@ static int cleanup_extent_op(struct btrfs_trans_handle *trans, return 0; } spin_unlock(&head->lock); - ret = run_delayed_extent_op(trans, fs_info, &head->node, extent_op); + ret = run_delayed_extent_op(trans, fs_info, head, extent_op); btrfs_free_delayed_extent_op(extent_op); return ret ? ret : 1; } @@ -2597,39 +2597,37 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans, spin_unlock(&delayed_refs->lock); return 1; } - head->node.in_tree = 0; delayed_refs->num_heads--; rb_erase(&head->href_node, &delayed_refs->href_root); + RB_CLEAR_NODE(&head->href_node); spin_unlock(&delayed_refs->lock); spin_unlock(&head->lock); atomic_dec(&delayed_refs->num_entries); - trace_run_delayed_ref_head(fs_info, &head->node, head, - head->node.action); + trace_run_delayed_ref_head(fs_info, head, 0); if (head->total_ref_mod < 0) { struct btrfs_block_group_cache *cache; - cache = btrfs_lookup_block_group(fs_info, head->node.bytenr); + cache = btrfs_lookup_block_group(fs_info, head->bytenr); ASSERT(cache); percpu_counter_add(&cache->space_info->total_bytes_pinned, - -head->node.num_bytes); + -head->num_bytes); btrfs_put_block_group(cache); if (head->is_data) { spin_lock(&delayed_refs->lock); - delayed_refs->pending_csums -= head->node.num_bytes; + delayed_refs->pending_csums -= head->num_bytes; spin_unlock(&delayed_refs->lock); } } if (head->must_insert_reserved) { - btrfs_pin_extent(fs_info, head->node.bytenr, - head->node.num_bytes, 1); + btrfs_pin_extent(fs_info, head->bytenr, + head->num_bytes, 1); if (head->is_data) { - ret = btrfs_del_csums(trans, fs_info, - head->node.bytenr, - head->node.num_bytes); + ret = btrfs_del_csums(trans, fs_info, head->bytenr, + head->num_bytes); } } @@ -2637,7 +2635,7 @@ static int cleanup_ref_head(struct btrfs_trans_handle *trans, btrfs_qgroup_free_delayed_ref(fs_info, head->qgroup_ref_root, head->qgroup_reserved); btrfs_delayed_ref_unlock(head); - btrfs_put_delayed_ref(&head->node); + btrfs_put_delayed_ref_head(head); return 0; } @@ -2751,10 +2749,10 @@ static noinline int __btrfs_run_delayed_refs(struct btrfs_trans_handle *trans, switch (ref->action) { case BTRFS_ADD_DELAYED_REF: case BTRFS_ADD_DELAYED_EXTENT: - locked_ref->node.ref_mod -= ref->ref_mod; + locked_ref->ref_mod -= ref->ref_mod; break; case BTRFS_DROP_DELAYED_REF: - locked_ref->node.ref_mod += ref->ref_mod; + locked_ref->ref_mod += ref->ref_mod; break; default: WARN_ON(1); @@ -3087,33 +3085,16 @@ again: spin_unlock(&delayed_refs->lock); goto out; } + head = rb_entry(node, struct btrfs_delayed_ref_head, + href_node); + refcount_inc(&head->refs); + spin_unlock(&delayed_refs->lock); - while (node) { - head = rb_entry(node, struct btrfs_delayed_ref_head, - href_node); - if (btrfs_delayed_ref_is_head(&head->node)) { - struct btrfs_delayed_ref_node *ref; - - ref = &head->node; - refcount_inc(&ref->refs); - - spin_unlock(&delayed_refs->lock); - /* - * Mutex was contended, block until it's - * released and try again - */ - mutex_lock(&head->mutex); - mutex_unlock(&head->mutex); + /* Mutex was contended, block until it's released and retry. */ + mutex_lock(&head->mutex); + mutex_unlock(&head->mutex); - btrfs_put_delayed_ref(ref); - cond_resched(); - goto again; - } else { - WARN_ON(1); - } - node = rb_next(node); - } - spin_unlock(&delayed_refs->lock); + btrfs_put_delayed_ref_head(head); cond_resched(); goto again; } @@ -3171,7 +3152,7 @@ static noinline int check_delayed_ref(struct btrfs_root *root, } if (!mutex_trylock(&head->mutex)) { - refcount_inc(&head->node.refs); + refcount_inc(&head->refs); spin_unlock(&delayed_refs->lock); btrfs_release_path(path); @@ -3182,7 +3163,7 @@ static noinline int check_delayed_ref(struct btrfs_root *root, */ mutex_lock(&head->mutex); mutex_unlock(&head->mutex); - btrfs_put_delayed_ref(&head->node); + btrfs_put_delayed_ref_head(head); return -EAGAIN; } spin_unlock(&delayed_refs->lock); @@ -7235,9 +7216,8 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans, * at this point we have a head with no other entries. Go * ahead and process it. */ - head->node.in_tree = 0; rb_erase(&head->href_node, &delayed_refs->href_root); - + RB_CLEAR_NODE(&head->href_node); atomic_dec(&delayed_refs->num_entries); /* @@ -7256,7 +7236,7 @@ static noinline int check_ref_cleanup(struct btrfs_trans_handle *trans, ret = 1; mutex_unlock(&head->mutex); - btrfs_put_delayed_ref(&head->node); + btrfs_put_delayed_ref_head(head); return ret; out: spin_unlock(&head->lock); diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index 77437f545c63..bfe2f23b578c 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -798,11 +798,10 @@ DEFINE_EVENT(btrfs_delayed_data_ref, run_delayed_data_ref, DECLARE_EVENT_CLASS(btrfs_delayed_ref_head, TP_PROTO(const struct btrfs_fs_info *fs_info, - const struct btrfs_delayed_ref_node *ref, const struct btrfs_delayed_ref_head *head_ref, int action), - TP_ARGS(fs_info, ref, head_ref, action), + TP_ARGS(fs_info, head_ref, action), TP_STRUCT__entry_btrfs( __field( u64, bytenr ) @@ -812,8 +811,8 @@ DECLARE_EVENT_CLASS(btrfs_delayed_ref_head, ), TP_fast_assign_btrfs(fs_info, - __entry->bytenr = ref->bytenr; - __entry->num_bytes = ref->num_bytes; + __entry->bytenr = head_ref->bytenr; + __entry->num_bytes = head_ref->num_bytes; __entry->action = action; __entry->is_data = head_ref->is_data; ), @@ -828,21 +827,19 @@ DECLARE_EVENT_CLASS(btrfs_delayed_ref_head, DEFINE_EVENT(btrfs_delayed_ref_head, add_delayed_ref_head, TP_PROTO(const struct btrfs_fs_info *fs_info, - const struct btrfs_delayed_ref_node *ref, const struct btrfs_delayed_ref_head *head_ref, int action), - TP_ARGS(fs_info, ref, head_ref, action) + TP_ARGS(fs_info, head_ref, action) ); DEFINE_EVENT(btrfs_delayed_ref_head, run_delayed_ref_head, TP_PROTO(const struct btrfs_fs_info *fs_info, - const struct btrfs_delayed_ref_node *ref, const struct btrfs_delayed_ref_head *head_ref, int action), - TP_ARGS(fs_info, ref, head_ref, action) + TP_ARGS(fs_info, head_ref, action) ); #define show_chunk_type(type) \ -- cgit v1.2.3-59-g8ed1b From d82bd359972a7fe71a778396cf287bc9f9f3b981 Mon Sep 17 00:00:00 2001 From: Avaneesh Kumar Dwivedi Date: Tue, 24 Oct 2017 21:22:24 +0530 Subject: firmware: scm: Add new SCM call API for switching memory ownership Two different processors on a SOC need to switch memory ownership during load/unload. To enable this, second level memory map table need to be updated, which is done by secure layer. This patch adds the interface for making secure monitor call for memory ownership switching request. Acked-by: Andy Gross Signed-off-by: Avaneesh Kumar Dwivedi [bjorn: Minor style and kerneldoc updates] Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm-32.c | 7 ++++ drivers/firmware/qcom_scm-64.c | 27 ++++++++++++ drivers/firmware/qcom_scm.c | 95 ++++++++++++++++++++++++++++++++++++++++++ drivers/firmware/qcom_scm.h | 5 +++ include/linux/qcom_scm.h | 16 +++++++ 5 files changed, 150 insertions(+) (limited to 'include') diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 93e3b96b6dfa..60d96bcf6694 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -579,6 +579,13 @@ int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) return ret ? : le32_to_cpu(scm_ret); } +int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, + size_t mem_sz, phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz) +{ + return -ENODEV; +} + int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) { diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 6e6d561708e2..ea5b1e680f67 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -382,6 +382,33 @@ int __qcom_scm_set_remote_state(struct device *dev, u32 state, u32 id) return ret ? : res.a1; } +int __qcom_scm_assign_mem(struct device *dev, phys_addr_t mem_region, + size_t mem_sz, phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz) +{ + int ret; + struct qcom_scm_desc desc = {0}; + struct arm_smccc_res res; + + desc.args[0] = mem_region; + desc.args[1] = mem_sz; + desc.args[2] = src; + desc.args[3] = src_sz; + desc.args[4] = dest; + desc.args[5] = dest_sz; + desc.args[6] = 0; + + desc.arginfo = QCOM_SCM_ARGS(7, QCOM_SCM_RO, QCOM_SCM_VAL, + QCOM_SCM_RO, QCOM_SCM_VAL, QCOM_SCM_RO, + QCOM_SCM_VAL, QCOM_SCM_VAL); + + ret = qcom_scm_call(dev, QCOM_SCM_SVC_MP, + QCOM_MEM_PROT_ASSIGN_ID, + &desc, &res); + + return ret ? : res.a1; +} + int __qcom_scm_restore_sec_cfg(struct device *dev, u32 device_id, u32 spare) { struct qcom_scm_desc desc = {0}; diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index bb16510d75ba..334eaa341828 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -40,6 +40,19 @@ struct qcom_scm { struct reset_controller_dev reset; }; +struct qcom_scm_current_perm_info { + __le32 vmid; + __le32 perm; + __le64 ctx; + __le32 ctx_size; + __le32 unused; +}; + +struct qcom_scm_mem_map_info { + __le64 mem_addr; + __le64 mem_size; +}; + static struct qcom_scm *__scm; static int qcom_scm_clk_enable(void) @@ -348,6 +361,88 @@ int qcom_scm_set_remote_state(u32 state, u32 id) } EXPORT_SYMBOL(qcom_scm_set_remote_state); +/** + * qcom_scm_assign_mem() - Make a secure call to reassign memory ownership + * @mem_addr: mem region whose ownership need to be reassigned + * @mem_sz: size of the region. + * @srcvm: vmid for current set of owners, each set bit in + * flag indicate a unique owner + * @newvm: array having new owners and corrsponding permission + * flags + * @dest_cnt: number of owners in next set. + * + * Return negative errno on failure, 0 on success, with @srcvm updated. + */ +int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, + unsigned int *srcvm, + struct qcom_scm_vmperm *newvm, int dest_cnt) +{ + struct qcom_scm_current_perm_info *destvm; + struct qcom_scm_mem_map_info *mem_to_map; + phys_addr_t mem_to_map_phys; + phys_addr_t dest_phys; + phys_addr_t ptr_phys; + size_t mem_to_map_sz; + size_t dest_sz; + size_t src_sz; + size_t ptr_sz; + int next_vm; + __le32 *src; + void *ptr; + int ret; + int len; + int i; + + src_sz = hweight_long(*srcvm) * sizeof(*src); + mem_to_map_sz = sizeof(*mem_to_map); + dest_sz = dest_cnt * sizeof(*destvm); + ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + + ALIGN(dest_sz, SZ_64); + + ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + /* Fill source vmid detail */ + src = ptr; + len = hweight_long(*srcvm); + for (i = 0; i < len; i++) { + src[i] = cpu_to_le32(ffs(*srcvm) - 1); + *srcvm ^= 1 << (ffs(*srcvm) - 1); + } + + /* Fill details of mem buff to map */ + mem_to_map = ptr + ALIGN(src_sz, SZ_64); + mem_to_map_phys = ptr_phys + ALIGN(src_sz, SZ_64); + mem_to_map[0].mem_addr = cpu_to_le64(mem_addr); + mem_to_map[0].mem_size = cpu_to_le64(mem_sz); + + next_vm = 0; + /* Fill details of next vmid detail */ + destvm = ptr + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); + dest_phys = ptr_phys + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(src_sz, SZ_64); + for (i = 0; i < dest_cnt; i++) { + destvm[i].vmid = cpu_to_le32(newvm[i].vmid); + destvm[i].perm = cpu_to_le32(newvm[i].perm); + destvm[i].ctx = 0; + destvm[i].ctx_size = 0; + next_vm |= BIT(newvm[i].vmid); + } + + ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, + ptr_phys, src_sz, dest_phys, dest_sz); + dma_free_coherent(__scm->dev, ALIGN(ptr_sz, SZ_64), ptr, ptr_phys); + if (ret) { + dev_err(__scm->dev, + "Assign memory protection call failed %d.\n", ret); + return -EINVAL; + } + + *srcvm = next_vm; + return 0; +} +EXPORT_SYMBOL(qcom_scm_assign_mem); + static int qcom_scm_probe(struct platform_device *pdev) { struct qcom_scm *scm; diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index 9bea691f30fb..fe54b7ba4db4 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -95,5 +95,10 @@ extern int __qcom_scm_iommu_secure_ptbl_size(struct device *dev, u32 spare, size_t *size); extern int __qcom_scm_iommu_secure_ptbl_init(struct device *dev, u64 addr, u32 size, u32 spare); +#define QCOM_MEM_PROT_ASSIGN_ID 0x16 +extern int __qcom_scm_assign_mem(struct device *dev, + phys_addr_t mem_region, size_t mem_sz, + phys_addr_t src, size_t src_sz, + phys_addr_t dest, size_t dest_sz); #endif diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index e5380471c2cd..6f8da9e182f9 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -23,6 +23,19 @@ struct qcom_scm_hdcp_req { u32 val; }; +struct qcom_scm_vmperm { + int vmid; + int perm; +}; + +#define QCOM_SCM_VMID_HLOS 0x3 +#define QCOM_SCM_VMID_MSS_MSA 0xF +#define QCOM_SCM_PERM_READ 0x4 +#define QCOM_SCM_PERM_WRITE 0x2 +#define QCOM_SCM_PERM_EXEC 0x1 +#define QCOM_SCM_PERM_RW (QCOM_SCM_PERM_READ | QCOM_SCM_PERM_WRITE) +#define QCOM_SCM_PERM_RWX (QCOM_SCM_PERM_RW | QCOM_SCM_PERM_EXEC) + #if IS_ENABLED(CONFIG_QCOM_SCM) extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); extern int qcom_scm_set_warm_boot_addr(void *entry, const cpumask_t *cpus); @@ -37,6 +50,9 @@ extern int qcom_scm_pas_mem_setup(u32 peripheral, phys_addr_t addr, phys_addr_t size); extern int qcom_scm_pas_auth_and_reset(u32 peripheral); extern int qcom_scm_pas_shutdown(u32 peripheral); +extern int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, + unsigned int *src, struct qcom_scm_vmperm *newvm, + int dest_cnt); extern void qcom_scm_cpu_power_down(u32 flags); extern u32 qcom_scm_get_version(void); extern int qcom_scm_set_remote_state(u32 state, u32 id); -- cgit v1.2.3-59-g8ed1b From ad596b68ad26bc2d3da9a0be3c5c21a265f5edcb Mon Sep 17 00:00:00 2001 From: Oleh Kravchenko Date: Sat, 28 Oct 2017 09:38:17 -0400 Subject: media: rc: Add Astrometa T2hybrid keymap module Add the keymap module for Astrometa T2hybrid remote control commands. Signed-off-by: Oleh Kravchenko Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/rc/keymaps/Makefile | 1 + drivers/media/rc/keymaps/rc-astrometa-t2hybrid.c | 70 ++++++++++++++++++++++++ include/media/rc-map.h | 1 + 3 files changed, 72 insertions(+) create mode 100644 drivers/media/rc/keymaps/rc-astrometa-t2hybrid.c (limited to 'include') diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 3f196232904f..039a631b348e 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-alink-dtu-m.o \ rc-anysee.o \ rc-apac-viewcomp.o \ + rc-astrometa-t2hybrid.o \ rc-asus-pc39.o \ rc-asus-ps3-100.o \ rc-ati-tv-wonder-hd-600.o \ diff --git a/drivers/media/rc/keymaps/rc-astrometa-t2hybrid.c b/drivers/media/rc/keymaps/rc-astrometa-t2hybrid.c new file mode 100644 index 000000000000..51690960fec4 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-astrometa-t2hybrid.c @@ -0,0 +1,70 @@ +/* + * Keytable for the Astrometa T2hybrid remote controller + * + * Copyright (C) 2017 Oleh Kravchenko + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +static struct rc_map_table t2hybrid[] = { + { 0x4d, KEY_POWER2 }, + { 0x54, KEY_VIDEO }, /* Source */ + { 0x16, KEY_MUTE }, + + { 0x4c, KEY_RECORD }, + { 0x05, KEY_CHANNELUP }, + { 0x0c, KEY_TIME}, /* Timeshift */ + + { 0x0a, KEY_VOLUMEDOWN }, + { 0x40, KEY_ZOOM }, /* Fullscreen */ + { 0x1e, KEY_VOLUMEUP }, + + { 0x12, KEY_0 }, + { 0x02, KEY_CHANNELDOWN }, + { 0x1c, KEY_AGAIN }, /* Recall */ + + { 0x09, KEY_1 }, + { 0x1d, KEY_2 }, + { 0x1f, KEY_3 }, + + { 0x0d, KEY_4 }, + { 0x19, KEY_5 }, + { 0x1b, KEY_6 }, + + { 0x11, KEY_7 }, + { 0x15, KEY_8 }, + { 0x17, KEY_9 }, +}; + +static struct rc_map_list t2hybrid_map = { + .map = { + .scan = t2hybrid, + .size = ARRAY_SIZE(t2hybrid), + .rc_proto = RC_PROTO_NEC, + .name = RC_MAP_ASTROMETA_T2HYBRID, + } +}; + +static int __init init_rc_map_t2hybrid(void) +{ + return rc_map_register(&t2hybrid_map); +} + +static void __exit exit_rc_map_t2hybrid(void) +{ + rc_map_unregister(&t2hybrid_map); +} + +module_init(init_rc_map_t2hybrid) +module_exit(exit_rc_map_t2hybrid) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Oleh Kravchenko "); diff --git a/include/media/rc-map.h b/include/media/rc-map.h index cc59c72ac282..72197cb43781 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -211,6 +211,7 @@ struct rc_map *rc_map_get(const char *name); #define RC_MAP_ALINK_DTU_M "rc-alink-dtu-m" #define RC_MAP_ANYSEE "rc-anysee" #define RC_MAP_APAC_VIEWCOMP "rc-apac-viewcomp" +#define RC_MAP_ASTROMETA_T2HYBRID "rc-astrometa-t2hybrid" #define RC_MAP_ASUS_PC39 "rc-asus-pc39" #define RC_MAP_ASUS_PS3_100 "rc-asus-ps3-100" #define RC_MAP_ATI_TV_WONDER_HD_600 "rc-ati-tv-wonder-hd-600" -- cgit v1.2.3-59-g8ed1b From 3e3149173fd831e554f45c694e5349370601dd5f Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 16 Oct 2017 19:10:12 -0400 Subject: media: media/saa7146: Convert timers to use timer_setup() In preparation for unconditionally passing the struct timer_list pointer to all timer callbacks, switch to using the new timer_setup() and from_timer() to pass the timer pointer explicitly. This requires adding a pointer to hold the timer's target file, as there won't be a way to pass this in the future. Signed-off-by: Kees Cook Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 2 +- drivers/media/common/saa7146/saa7146_vbi.c | 9 +++++---- include/media/drv-intf/saa7146_vv.h | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index 930d2c94d5d3..c4664f0da874 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -559,7 +559,7 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) vbi->start[1] = 312; vbi->count[1] = 16; - init_timer(&vv->vbi_read_timeout); + timer_setup(&vv->vbi_read_timeout, NULL, 0); vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING; vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY; diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c index 69525ca4f52c..ae66c2325228 100644 --- a/drivers/media/common/saa7146/saa7146_vbi.c +++ b/drivers/media/common/saa7146/saa7146_vbi.c @@ -348,9 +348,10 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file) spin_unlock_irqrestore(&dev->slock, flags); } -static void vbi_read_timeout(unsigned long data) +static void vbi_read_timeout(struct timer_list *t) { - struct file *file = (struct file*)data; + struct saa7146_vv *vv = from_timer(vv, t, vbi_read_timeout); + struct file *file = vv->vbi_read_timeout_file; struct saa7146_fh *fh = file->private_data; struct saa7146_dev *dev = fh->dev; @@ -401,8 +402,8 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) sizeof(struct saa7146_buf), file, &dev->v4l2_lock); - vv->vbi_read_timeout.function = vbi_read_timeout; - vv->vbi_read_timeout.data = (unsigned long)file; + vv->vbi_read_timeout.function = (TIMER_FUNC_TYPE)vbi_read_timeout; + vv->vbi_read_timeout_file = file; /* initialize the brs */ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { diff --git a/include/media/drv-intf/saa7146_vv.h b/include/media/drv-intf/saa7146_vv.h index 736f4f2d8290..926c5b145279 100644 --- a/include/media/drv-intf/saa7146_vv.h +++ b/include/media/drv-intf/saa7146_vv.h @@ -107,6 +107,7 @@ struct saa7146_vv struct saa7146_dmaqueue vbi_dmaq; struct v4l2_vbi_format vbi_fmt; struct timer_list vbi_read_timeout; + struct file *vbi_read_timeout_file; /* vbi workaround interrupt queue */ wait_queue_head_t vbi_wq; int vbi_fieldcount; -- cgit v1.2.3-59-g8ed1b From 12a83612ed23fccafe168828cafcce514f02e74d Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 16 Oct 2017 19:10:42 -0400 Subject: media: saa7146: Convert timers to use timer_setup() In preparation for unconditionally passing the struct timer_list pointer to all timer callbacks, switch to using the new timer_setup() and from_timer() to pass the timer pointer explicitly. Signed-off-by: Kees Cook Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146/saa7146_fops.c | 4 ++-- drivers/media/common/saa7146/saa7146_vbi.c | 3 +-- drivers/media/common/saa7146/saa7146_video.c | 3 +-- include/media/drv-intf/saa7146_vv.h | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/media/common/saa7146/saa7146_fops.c b/drivers/media/common/saa7146/saa7146_fops.c index c4664f0da874..8c87d6837c49 100644 --- a/drivers/media/common/saa7146/saa7146_fops.c +++ b/drivers/media/common/saa7146/saa7146_fops.c @@ -163,9 +163,9 @@ void saa7146_buffer_next(struct saa7146_dev *dev, } } -void saa7146_buffer_timeout(unsigned long data) +void saa7146_buffer_timeout(struct timer_list *t) { - struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data; + struct saa7146_dmaqueue *q = from_timer(q, t, timeout); struct saa7146_dev *dev = q->dev; unsigned long flags; diff --git a/drivers/media/common/saa7146/saa7146_vbi.c b/drivers/media/common/saa7146/saa7146_vbi.c index ae66c2325228..7fa3147c2d7e 100644 --- a/drivers/media/common/saa7146/saa7146_vbi.c +++ b/drivers/media/common/saa7146/saa7146_vbi.c @@ -366,8 +366,7 @@ static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) INIT_LIST_HEAD(&vv->vbi_dmaq.queue); - setup_timer(&vv->vbi_dmaq.timeout, saa7146_buffer_timeout, - (unsigned long)(&vv->vbi_dmaq)); + timer_setup(&vv->vbi_dmaq.timeout, saa7146_buffer_timeout, 0); vv->vbi_dmaq.dev = dev; init_waitqueue_head(&vv->vbi_wq); diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c index 51eeed830de4..2b631eaa65b3 100644 --- a/drivers/media/common/saa7146/saa7146_video.c +++ b/drivers/media/common/saa7146/saa7146_video.c @@ -1201,8 +1201,7 @@ static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) { INIT_LIST_HEAD(&vv->video_dmaq.queue); - setup_timer(&vv->video_dmaq.timeout, saa7146_buffer_timeout, - (unsigned long)(&vv->video_dmaq)); + timer_setup(&vv->video_dmaq.timeout, saa7146_buffer_timeout, 0); vv->video_dmaq.dev = dev; /* set some default values */ diff --git a/include/media/drv-intf/saa7146_vv.h b/include/media/drv-intf/saa7146_vv.h index 926c5b145279..17bbe3851d75 100644 --- a/include/media/drv-intf/saa7146_vv.h +++ b/include/media/drv-intf/saa7146_vv.h @@ -184,7 +184,7 @@ int saa7146_unregister_device(struct video_device *vid, struct saa7146_dev *dev) void saa7146_buffer_finish(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, int state); void saa7146_buffer_next(struct saa7146_dev *dev, struct saa7146_dmaqueue *q,int vbi); int saa7146_buffer_queue(struct saa7146_dev *dev, struct saa7146_dmaqueue *q, struct saa7146_buf *buf); -void saa7146_buffer_timeout(unsigned long data); +void saa7146_buffer_timeout(struct timer_list *t); void saa7146_dma_free(struct saa7146_dev* dev,struct videobuf_queue *q, struct saa7146_buf *buf); -- cgit v1.2.3-59-g8ed1b From 6981fbf3780366093858c5d2dcdaadcd1fbb04be Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sun, 29 Oct 2017 11:33:40 -0700 Subject: Drivers: hv: vmbus: Expose per-channel interrupts and events counters When investigating performance, it is useful to be able to look at the number of host and guest events per-channel. This is equivalent to per-device interrupt statistics. Signed-off-by: Stephen Hemminger Signed-off-by: K. Y. Srinivasan Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/stable/sysfs-bus-vmbus | 26 ++++++++++++++++++++------ drivers/hv/connection.c | 2 ++ drivers/hv/vmbus_drv.c | 16 ++++++++++++++++ include/linux/hyperv.h | 4 ++++ 4 files changed, 42 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/stable/sysfs-bus-vmbus b/Documentation/ABI/stable/sysfs-bus-vmbus index 0ebd8a1537a0..d4077cc60d55 100644 --- a/Documentation/ABI/stable/sysfs-bus-vmbus +++ b/Documentation/ABI/stable/sysfs-bus-vmbus @@ -61,39 +61,53 @@ Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Inbound channel signaling state -Users: Debuggig tools +Users: Debugging tools What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/latency Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Channel signaling latency -Users: Debuggig tools +Users: Debugging tools What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/out_mask Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Outbound channel signaling state -Users: Debuggig tools +Users: Debugging tools What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/pending Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Channel interrupt pending state -Users: Debuggig tools +Users: Debugging tools What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/read_avail Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Bytes availabble to read -Users: Debuggig tools +Users: Debugging tools What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/write_avail Date: September. 2017 KernelVersion: 4.14 Contact: Stephen Hemminger Description: Bytes availabble to write -Users: Debuggig tools +Users: Debugging tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/events +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger +Description: Number of times we have signaled the host +Users: Debugging tools + +What: /sys/bus/vmbus/devices/vmbus_*/channels/relid/interrupts +Date: September. 2017 +KernelVersion: 4.14 +Contact: Stephen Hemminger +Description: Number of times we have taken an interrupt (incoming) +Users: Debugging tools diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index f41901f80b64..b06a6b796819 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -409,6 +409,8 @@ void vmbus_set_event(struct vmbus_channel *channel) if (!channel->is_dedicated_interrupt) vmbus_send_interrupt(child_relid); + ++channel->sig_events; + hv_do_fast_hypercall8(HVCALL_SIGNAL_EVENT, channel->sig_event); } EXPORT_SYMBOL_GPL(vmbus_set_event); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 1da8e818f4de..bca8188f3c8c 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -944,6 +944,8 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) if (channel->rescind) continue; + ++channel->interrupts; + switch (channel->callback_mode) { case HV_CALL_ISR: vmbus_channel_isr(channel); @@ -1237,6 +1239,18 @@ static ssize_t channel_latency_show(const struct vmbus_channel *channel, } VMBUS_CHAN_ATTR(latency, S_IRUGO, channel_latency_show, NULL); +static ssize_t channel_interrupts_show(const struct vmbus_channel *channel, char *buf) +{ + return sprintf(buf, "%llu\n", channel->interrupts); +} +VMBUS_CHAN_ATTR(interrupts, S_IRUGO, channel_interrupts_show, NULL); + +static ssize_t channel_events_show(const struct vmbus_channel *channel, char *buf) +{ + return sprintf(buf, "%llu\n", channel->sig_events); +} +VMBUS_CHAN_ATTR(events, S_IRUGO, channel_events_show, NULL); + static struct attribute *vmbus_chan_attrs[] = { &chan_attr_out_mask.attr, &chan_attr_in_mask.attr, @@ -1245,6 +1259,8 @@ static struct attribute *vmbus_chan_attrs[] = { &chan_attr_cpu.attr, &chan_attr_pending.attr, &chan_attr_latency.attr, + &chan_attr_interrupts.attr, + &chan_attr_events.attr, NULL }; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index ea6b5586ad77..f3e97c5f94c9 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -719,6 +719,10 @@ struct vmbus_channel { struct vmbus_close_msg close_msg; + /* Statistics */ + u64 interrupts; /* Host to Guest interrupts */ + u64 sig_events; /* Guest to Host events */ + /* Channel callback's invoked in softirq context */ struct tasklet_struct callback_event; void (*onchannel_callback)(void *context); -- cgit v1.2.3-59-g8ed1b From 3974320ca6aa68d479051f208d5c95afd1e47a4c Mon Sep 17 00:00:00 2001 From: Bob Peterson Date: Thu, 16 Feb 2017 10:27:16 -0500 Subject: GFS2: Implement iomap for block_map This patch implements iomap for block mapping, and switches the block_map function to use it under the covers. The additional IOMAP_F_BOUNDARY iomap flag indicates when iomap has reached a "metadata boundary" and fetching the next mapping is likely to incur an additional I/O. This flag is used for setting the bh buffer boundary flag. Signed-off-by: Bob Peterson Signed-off-by: Andreas Gruenbacher --- fs/gfs2/bmap.c | 273 +++++++++++++++++++++++++++++++++++++------------- fs/gfs2/bmap.h | 4 + fs/gfs2/trace_gfs2.h | 65 ++++++++++++ include/linux/iomap.h | 3 +- 4 files changed, 276 insertions(+), 69 deletions(-) (limited to 'include') diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index 03badc8417d7..d5f0d96169c5 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "gfs2.h" #include "incore.h" @@ -505,10 +506,8 @@ static inline unsigned int hptrs(struct gfs2_sbd *sdp, const unsigned int hgt) * Returns: errno on error */ -static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock, - bool zero_new, struct metapath *mp, - const size_t maxlen, sector_t *dblock, - unsigned *dblks) +static int gfs2_iomap_alloc(struct inode *inode, struct iomap *iomap, + unsigned flags, struct metapath *mp) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); @@ -516,36 +515,37 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock, struct buffer_head *dibh = mp->mp_bh[0]; u64 bn; unsigned n, i, blks, alloced = 0, iblks = 0, branch_start = 0; + unsigned dblks = 0; unsigned ptrs_per_blk; const unsigned end_of_metadata = mp->mp_fheight - 1; int ret; - int eob = 0; enum alloc_state state; __be64 *ptr; __be64 zero_bn = 0; + size_t maxlen = iomap->length >> inode->i_blkbits; BUG_ON(mp->mp_aheight < 1); BUG_ON(dibh == NULL); - *dblock = 0; - *dblks = 0; gfs2_trans_add_meta(ip->i_gl, dibh); if (mp->mp_fheight == mp->mp_aheight) { struct buffer_head *bh; + int eob; + /* Bottom indirect block exists, find unalloced extent size */ ptr = metapointer(end_of_metadata, mp); bh = mp->mp_bh[end_of_metadata]; - *dblks = gfs2_extent_length(bh->b_data, bh->b_size, ptr, - maxlen, &eob); - BUG_ON(*dblks < 1); + dblks = gfs2_extent_length(bh->b_data, bh->b_size, ptr, + maxlen, &eob); + BUG_ON(dblks < 1); state = ALLOC_DATA; } else { /* Need to allocate indirect blocks */ ptrs_per_blk = mp->mp_fheight > 1 ? sdp->sd_inptrs : sdp->sd_diptrs; - *dblks = min(maxlen, (size_t)(ptrs_per_blk - - mp->mp_list[end_of_metadata])); + dblks = min(maxlen, (size_t)(ptrs_per_blk - + mp->mp_list[end_of_metadata])); if (mp->mp_fheight == ip->i_height) { /* Writing into existing tree, extend tree down */ iblks = mp->mp_fheight - mp->mp_aheight; @@ -561,7 +561,7 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock, /* start of the second part of the function (state machine) */ - blks = *dblks + iblks; + blks = dblks + iblks; i = mp->mp_aheight; do { int error; @@ -618,26 +618,29 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock, break; /* Tree complete, adding data blocks */ case ALLOC_DATA: - BUG_ON(n > *dblks); + BUG_ON(n > dblks); BUG_ON(mp->mp_bh[end_of_metadata] == NULL); gfs2_trans_add_meta(ip->i_gl, mp->mp_bh[end_of_metadata]); - *dblks = n; + dblks = n; ptr = metapointer(end_of_metadata, mp); - *dblock = bn; + iomap->addr = bn << inode->i_blkbits; + iomap->flags |= IOMAP_F_NEW; while (n-- > 0) *ptr++ = cpu_to_be64(bn++); - if (zero_new) { - ret = sb_issue_zeroout(sb, *dblock, *dblks, - GFP_NOFS); + if (flags & IOMAP_ZERO) { + ret = sb_issue_zeroout(sb, iomap->addr >> inode->i_blkbits, + dblks, GFP_NOFS); if (ret) { fs_err(sdp, "Failed to zero data buffers\n"); + flags &= ~IOMAP_ZERO; } } break; } - } while ((state != ALLOC_DATA) || !(*dblock)); + } while (iomap->addr == IOMAP_NULL_ADDR); + iomap->length = (u64)dblks << inode->i_blkbits; ip->i_height = mp->mp_fheight; gfs2_add_inode_blocks(&ip->i_inode, alloced); gfs2_dinode_out(ip, mp->mp_bh[0]->b_data); @@ -645,47 +648,123 @@ static int gfs2_bmap_alloc(struct inode *inode, const sector_t lblock, } /** - * gfs2_block_map - Map a block from an inode to a disk block + * hole_size - figure out the size of a hole * @inode: The inode - * @lblock: The logical block number - * @bh_map: The bh to be mapped - * @create: True if its ok to alloc blocks to satify the request + * @lblock: The logical starting block number + * @mp: The metapath * - * Sets buffer_mapped() if successful, sets buffer_boundary() if a - * read of metadata will be required before the next block can be - * mapped. Sets buffer_new() if new blocks were allocated. + * Returns: The hole size in bytes * - * Returns: errno */ +static u64 hole_size(struct inode *inode, sector_t lblock, struct metapath *mp) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct gfs2_sbd *sdp = GFS2_SB(inode); + struct metapath mp_eof; + u64 factor = 1; + int hgt; + u64 holesz = 0; + const __be64 *first, *end, *ptr; + const struct buffer_head *bh; + u64 lblock_stop = (i_size_read(inode) - 1) >> inode->i_blkbits; + int zeroptrs; + bool done = false; + + /* Get another metapath, to the very last byte */ + find_metapath(sdp, lblock_stop, &mp_eof, ip->i_height); + for (hgt = ip->i_height - 1; hgt >= 0 && !done; hgt--) { + bh = mp->mp_bh[hgt]; + if (bh) { + zeroptrs = 0; + first = metapointer(hgt, mp); + end = (const __be64 *)(bh->b_data + bh->b_size); + + for (ptr = first; ptr < end; ptr++) { + if (*ptr) { + done = true; + break; + } else { + zeroptrs++; + } + } + } else { + zeroptrs = sdp->sd_inptrs; + } + if (factor * zeroptrs >= lblock_stop - lblock + 1) { + holesz = lblock_stop - lblock + 1; + break; + } + holesz += factor * zeroptrs; -int gfs2_block_map(struct inode *inode, sector_t lblock, - struct buffer_head *bh_map, int create) + factor *= sdp->sd_inptrs; + if (hgt && (mp->mp_list[hgt - 1] < mp_eof.mp_list[hgt - 1])) + (mp->mp_list[hgt - 1])++; + } + return holesz << inode->i_blkbits; +} + +static void gfs2_stuffed_iomap(struct inode *inode, struct iomap *iomap) +{ + struct gfs2_inode *ip = GFS2_I(inode); + + iomap->addr = (ip->i_no_addr << inode->i_blkbits) + + sizeof(struct gfs2_dinode); + iomap->offset = 0; + iomap->length = i_size_read(inode); + iomap->type = IOMAP_MAPPED; + iomap->flags = IOMAP_F_DATA_INLINE; +} + +/** + * gfs2_iomap_begin - Map blocks from an inode to disk blocks + * @inode: The inode + * @pos: Starting position in bytes + * @length: Length to map, in bytes + * @flags: iomap flags + * @iomap: The iomap structure + * + * Returns: errno + */ +int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, + unsigned flags, struct iomap *iomap) { struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_sbd *sdp = GFS2_SB(inode); + struct metapath mp = { .mp_aheight = 1, }; unsigned int factor = sdp->sd_sb.sb_bsize; - const size_t maxlen = bh_map->b_size >> inode->i_blkbits; const u64 *arr = sdp->sd_heightsize; __be64 *ptr; - u64 size; - struct metapath mp; + sector_t lblock; + sector_t lend; int ret; int eob; unsigned int len; struct buffer_head *bh; u8 height; - bool zero_new = false; - sector_t dblock = 0; - unsigned dblks; - BUG_ON(maxlen == 0); + trace_gfs2_iomap_start(ip, pos, length, flags); + if (!length) { + ret = -EINVAL; + goto out; + } - memset(&mp, 0, sizeof(mp)); - bmap_lock(ip, create); - clear_buffer_mapped(bh_map); - clear_buffer_new(bh_map); - clear_buffer_boundary(bh_map); - trace_gfs2_bmap(ip, bh_map, lblock, create, 1); + if ((flags & IOMAP_REPORT) && gfs2_is_stuffed(ip)) { + gfs2_stuffed_iomap(inode, iomap); + if (pos >= iomap->length) + return -ENOENT; + ret = 0; + goto out; + } + + lblock = pos >> inode->i_blkbits; + lend = (pos + length + sdp->sd_sb.sb_bsize - 1) >> inode->i_blkbits; + + iomap->offset = lblock << inode->i_blkbits; + iomap->addr = IOMAP_NULL_ADDR; + iomap->type = IOMAP_HOLE; + iomap->length = (u64)(lend - lblock) << inode->i_blkbits; + iomap->flags = IOMAP_F_MERGED; + bmap_lock(ip, 0); /* * Directory data blocks have a struct gfs2_meta_header header, so the @@ -699,56 +778,114 @@ int gfs2_block_map(struct inode *inode, sector_t lblock, ret = gfs2_meta_inode_buffer(ip, &mp.mp_bh[0]); if (ret) - goto out; + goto out_release; height = ip->i_height; - size = (lblock + 1) * factor; - while (size > arr[height]) + while ((lblock + 1) * factor > arr[height]) height++; find_metapath(sdp, lblock, &mp, height); - mp.mp_aheight = 1; if (height > ip->i_height || gfs2_is_stuffed(ip)) goto do_alloc; + ret = lookup_metapath(ip, &mp); if (ret < 0) - goto out; + goto out_release; + if (mp.mp_aheight != ip->i_height) goto do_alloc; + ptr = metapointer(ip->i_height - 1, &mp); if (*ptr == 0) goto do_alloc; - map_bh(bh_map, inode->i_sb, be64_to_cpu(*ptr)); + + iomap->type = IOMAP_MAPPED; + iomap->addr = be64_to_cpu(*ptr) << inode->i_blkbits; + bh = mp.mp_bh[ip->i_height - 1]; - len = gfs2_extent_length(bh->b_data, bh->b_size, ptr, maxlen, &eob); - bh_map->b_size = (len << inode->i_blkbits); + len = gfs2_extent_length(bh->b_data, bh->b_size, ptr, lend - lblock, &eob); if (eob) - set_buffer_boundary(bh_map); + iomap->flags |= IOMAP_F_BOUNDARY; + iomap->length = (u64)len << inode->i_blkbits; + ret = 0; -out: + +out_release: release_metapath(&mp); - trace_gfs2_bmap(ip, bh_map, lblock, create, ret); - bmap_unlock(ip, create); + bmap_unlock(ip, 0); +out: + trace_gfs2_iomap_end(ip, iomap, ret); return ret; do_alloc: - /* All allocations are done here, firstly check create flag */ - if (!create) { - BUG_ON(gfs2_is_stuffed(ip)); + if (!(flags & IOMAP_WRITE)) { + if (pos >= i_size_read(inode)) { + ret = -ENOENT; + goto out_release; + } ret = 0; - goto out; + iomap->length = hole_size(inode, lblock, &mp); + goto out_release; } - /* At this point ret is the tree depth of already allocated blocks */ + ret = gfs2_iomap_alloc(inode, iomap, flags, &mp); + goto out_release; +} + +/** + * gfs2_block_map - Map a block from an inode to a disk block + * @inode: The inode + * @lblock: The logical block number + * @bh_map: The bh to be mapped + * @create: True if its ok to alloc blocks to satify the request + * + * Sets buffer_mapped() if successful, sets buffer_boundary() if a + * read of metadata will be required before the next block can be + * mapped. Sets buffer_new() if new blocks were allocated. + * + * Returns: errno + */ + +int gfs2_block_map(struct inode *inode, sector_t lblock, + struct buffer_head *bh_map, int create) +{ + struct gfs2_inode *ip = GFS2_I(inode); + struct iomap iomap; + int ret, flags = 0; + + clear_buffer_mapped(bh_map); + clear_buffer_new(bh_map); + clear_buffer_boundary(bh_map); + trace_gfs2_bmap(ip, bh_map, lblock, create, 1); + + if (create) + flags |= IOMAP_WRITE; if (buffer_zeronew(bh_map)) - zero_new = true; - ret = gfs2_bmap_alloc(inode, lblock, zero_new, &mp, maxlen, &dblock, - &dblks); - if (ret == 0) { - map_bh(bh_map, inode->i_sb, dblock); - bh_map->b_size = dblks << inode->i_blkbits; - set_buffer_new(bh_map); + flags |= IOMAP_ZERO; + ret = gfs2_iomap_begin(inode, (loff_t)lblock << inode->i_blkbits, + bh_map->b_size, flags, &iomap); + if (ret) { + if (!create && ret == -ENOENT) { + /* Return unmapped buffer beyond the end of file. */ + ret = 0; + } + goto out; + } + + if (iomap.length > bh_map->b_size) { + iomap.length = bh_map->b_size; + iomap.flags &= ~IOMAP_F_BOUNDARY; } - goto out; + if (iomap.addr != IOMAP_NULL_ADDR) + map_bh(bh_map, inode->i_sb, iomap.addr >> inode->i_blkbits); + bh_map->b_size = iomap.length; + if (iomap.flags & IOMAP_F_BOUNDARY) + set_buffer_boundary(bh_map); + if (iomap.flags & IOMAP_F_NEW) + set_buffer_new(bh_map); + +out: + trace_gfs2_bmap(ip, bh_map, lblock, create, ret); + return ret; } /* diff --git a/fs/gfs2/bmap.h b/fs/gfs2/bmap.h index 81ded5e2aaa2..443cc182cf18 100644 --- a/fs/gfs2/bmap.h +++ b/fs/gfs2/bmap.h @@ -10,6 +10,8 @@ #ifndef __BMAP_DOT_H__ #define __BMAP_DOT_H__ +#include + #include "inode.h" struct inode; @@ -47,6 +49,8 @@ static inline void gfs2_write_calc_reserv(const struct gfs2_inode *ip, extern int gfs2_unstuff_dinode(struct gfs2_inode *ip, struct page *page); extern int gfs2_block_map(struct inode *inode, sector_t lblock, struct buffer_head *bh, int create); +extern int gfs2_iomap_begin(struct inode *inode, loff_t pos, loff_t length, + unsigned flags, struct iomap *iomap); extern int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsigned *extlen); extern int gfs2_setattr_size(struct inode *inode, u64 size); diff --git a/fs/gfs2/trace_gfs2.h b/fs/gfs2/trace_gfs2.h index 49ac55da4e33..3c91ae3cf0b2 100644 --- a/fs/gfs2/trace_gfs2.h +++ b/fs/gfs2/trace_gfs2.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "incore.h" #include "glock.h" #include "rgrp.h" @@ -469,6 +470,70 @@ TRACE_EVENT(gfs2_bmap, __entry->errno) ); +TRACE_EVENT(gfs2_iomap_start, + + TP_PROTO(const struct gfs2_inode *ip, loff_t pos, ssize_t length, + u16 flags), + + TP_ARGS(ip, pos, length, flags), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( u64, inum ) + __field( loff_t, pos ) + __field( ssize_t, length ) + __field( u16, flags ) + ), + + TP_fast_assign( + __entry->dev = ip->i_gl->gl_name.ln_sbd->sd_vfs->s_dev; + __entry->inum = ip->i_no_addr; + __entry->pos = pos; + __entry->length = length; + __entry->flags = flags; + ), + + TP_printk("%u,%u bmap %llu iomap start %llu/%lu flags:%08x", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long long)__entry->inum, + (unsigned long long)__entry->pos, + (unsigned long)__entry->length, (u16)__entry->flags) +); + +TRACE_EVENT(gfs2_iomap_end, + + TP_PROTO(const struct gfs2_inode *ip, struct iomap *iomap, int ret), + + TP_ARGS(ip, iomap, ret), + + TP_STRUCT__entry( + __field( dev_t, dev ) + __field( u64, inum ) + __field( loff_t, offset ) + __field( ssize_t, length ) + __field( u16, flags ) + __field( u16, type ) + __field( int, ret ) + ), + + TP_fast_assign( + __entry->dev = ip->i_gl->gl_name.ln_sbd->sd_vfs->s_dev; + __entry->inum = ip->i_no_addr; + __entry->offset = iomap->offset; + __entry->length = iomap->length; + __entry->flags = iomap->flags; + __entry->type = iomap->type; + __entry->ret = ret; + ), + + TP_printk("%u,%u bmap %llu iomap end %llu/%lu ty:%d flags:%08x rc:%d", + MAJOR(__entry->dev), MINOR(__entry->dev), + (unsigned long long)__entry->inum, + (unsigned long long)__entry->offset, + (unsigned long)__entry->length, (u16)__entry->type, + (u16)__entry->flags, __entry->ret) +); + /* Keep track of blocks as they are allocated/freed */ TRACE_EVENT(gfs2_block_alloc, diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 2b0790dbd6ea..a61be86710b5 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -21,7 +21,8 @@ struct vm_fault; /* * Flags for all iomap mappings: */ -#define IOMAP_F_NEW 0x01 /* blocks have been newly allocated */ +#define IOMAP_F_NEW 0x01 /* blocks have been newly allocated */ +#define IOMAP_F_BOUNDARY 0x02 /* mapping ends at metadata boundary */ /* * Flags that only need to be reported for IOMAP_REPORT requests: -- cgit v1.2.3-59-g8ed1b From b2f270e8747387335d80428c576118e7d87f69cc Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 17 Oct 2017 19:04:41 -0700 Subject: module: Prepare to convert all module_param_call() prototypes After actually converting all module_param_call() function prototypes, we no longer need to do a tricky sizeof(func(thing)) type-check. Remove it. Signed-off-by: Kees Cook Signed-off-by: Jessica Yu --- include/linux/moduleparam.h | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 1ee7b30dafec..037a90a522a5 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -228,18 +228,10 @@ struct kparam_array /* Obsolete - use module_param_cb() */ #define module_param_call(name, set, get, arg, perm) \ - static const struct kernel_param_ops __param_ops_##name = \ + static const struct kernel_param_ops __param_ops_##name = \ { .flags = 0, (void *)set, (void *)get }; \ __module_param_call(MODULE_PARAM_PREFIX, \ - name, &__param_ops_##name, arg, \ - (perm) + sizeof(__check_old_set_param(set))*0, -1, 0) - -/* We don't get oldget: it's often a new-style param_get_uint, etc. */ -static inline int -__check_old_set_param(int (*oldset)(const char *, struct kernel_param *)) -{ - return 0; -} + name, &__param_ops_##name, arg, perm, -1, 0) #ifdef CONFIG_SYSFS extern void kernel_param_lock(struct module *mod); -- cgit v1.2.3-59-g8ed1b From e4dca7b7aa08b22893c45485d222b5807c1375ae Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 17 Oct 2017 19:04:42 -0700 Subject: treewide: Fix function prototypes for module_param_call() Several function prototypes for the set/get functions defined by module_param_call() have a slightly wrong argument types. This fixes those in an effort to clean up the calls when running under type-enforced compiler instrumentation for CFI. This is the result of running the following semantic patch: @match_module_param_call_function@ declarer name module_param_call; identifier _name, _set_func, _get_func; expression _arg, _mode; @@ module_param_call(_name, _set_func, _get_func, _arg, _mode); @fix_set_prototype depends on match_module_param_call_function@ identifier match_module_param_call_function._set_func; identifier _val, _param; type _val_type, _param_type; @@ int _set_func( -_val_type _val +const char * _val , -_param_type _param +const struct kernel_param * _param ) { ... } @fix_get_prototype depends on match_module_param_call_function@ identifier match_module_param_call_function._get_func; identifier _val, _param; type _val_type, _param_type; @@ int _get_func( -_val_type _val +char * _val , -_param_type _param +const struct kernel_param * _param ) { ... } Two additional by-hand changes are included for places where the above Coccinelle script didn't notice them: drivers/platform/x86/thinkpad_acpi.c fs/lockd/svc.c Signed-off-by: Kees Cook Signed-off-by: Jessica Yu --- arch/powerpc/platforms/pseries/cmm.c | 2 +- arch/x86/oprofile/nmi_int.c | 2 +- drivers/acpi/button.c | 6 ++++-- drivers/acpi/ec.c | 6 ++++-- drivers/acpi/sysfs.c | 8 +++++--- drivers/android/binder.c | 2 +- drivers/char/ipmi/ipmi_poweroff.c | 2 +- drivers/char/ipmi/ipmi_si_intf.c | 4 ++-- drivers/edac/edac_mc_sysfs.c | 2 +- drivers/edac/edac_module.c | 3 ++- drivers/hid/hid-magicmouse.c | 3 ++- drivers/ide/ide.c | 4 ++-- drivers/infiniband/hw/qib/qib_iba7322.c | 4 ++-- drivers/infiniband/ulp/srpt/ib_srpt.c | 2 +- drivers/isdn/hardware/mISDN/avmfritz.c | 2 +- drivers/isdn/hardware/mISDN/mISDNinfineon.c | 2 +- drivers/isdn/hardware/mISDN/netjet.c | 2 +- drivers/isdn/hardware/mISDN/speedfax.c | 2 +- drivers/isdn/hardware/mISDN/w6692.c | 2 +- drivers/md/md.c | 6 +++--- drivers/media/pci/tw686x/tw686x-core.c | 4 ++-- drivers/media/usb/uvc/uvc_driver.c | 4 ++-- drivers/message/fusion/mptbase.c | 4 ++-- drivers/misc/kgdbts.c | 3 ++- drivers/mtd/devices/block2mtd.c | 2 +- drivers/mtd/devices/phram.c | 2 +- drivers/mtd/ubi/build.c | 2 +- drivers/pci/pcie/aspm.c | 5 +++-- drivers/platform/x86/thinkpad_acpi.c | 2 +- drivers/scsi/fcoe/fcoe_transport.c | 20 ++++++++++++-------- drivers/scsi/mpt3sas/mpt3sas_base.c | 2 +- drivers/scsi/mpt3sas/mpt3sas_scsih.c | 2 +- drivers/tty/serial/kgdboc.c | 3 ++- fs/fuse/inode.c | 4 ++-- fs/lockd/svc.c | 2 +- fs/ocfs2/dlmfs/dlmfs.c | 4 ++-- include/net/netfilter/nf_conntrack.h | 2 +- net/netfilter/nf_conntrack_core.c | 2 +- net/netfilter/nf_nat_ftp.c | 2 +- net/netfilter/nf_nat_irc.c | 2 +- net/sunrpc/svc.c | 4 ++-- security/apparmor/lsm.c | 16 ++++++++-------- 42 files changed, 87 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c index 4ac419c7eb4c..560aefde06c0 100644 --- a/arch/powerpc/platforms/pseries/cmm.c +++ b/arch/powerpc/platforms/pseries/cmm.c @@ -742,7 +742,7 @@ static void cmm_exit(void) * Return value: * 0 on success / other on failure **/ -static int cmm_set_disable(const char *val, struct kernel_param *kp) +static int cmm_set_disable(const char *val, const struct kernel_param *kp) { int disable = simple_strtoul(val, NULL, 10); diff --git a/arch/x86/oprofile/nmi_int.c b/arch/x86/oprofile/nmi_int.c index ffdbc4836b4f..174c59774cc9 100644 --- a/arch/x86/oprofile/nmi_int.c +++ b/arch/x86/oprofile/nmi_int.c @@ -592,7 +592,7 @@ enum __force_cpu_type { static int force_cpu_type; -static int set_cpu_type(const char *str, struct kernel_param *kp) +static int set_cpu_type(const char *str, const struct kernel_param *kp) { if (!strcmp(str, "timer")) { force_cpu_type = timer; diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index ef1856b15488..891b0921a307 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -557,7 +557,8 @@ static int acpi_button_remove(struct acpi_device *device) return 0; } -static int param_set_lid_init_state(const char *val, struct kernel_param *kp) +static int param_set_lid_init_state(const char *val, + const struct kernel_param *kp) { int result = 0; @@ -575,7 +576,8 @@ static int param_set_lid_init_state(const char *val, struct kernel_param *kp) return result; } -static int param_get_lid_init_state(char *buffer, struct kernel_param *kp) +static int param_get_lid_init_state(char *buffer, + const struct kernel_param *kp) { switch (lid_init_state) { case ACPI_BUTTON_LID_INIT_OPEN: diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 236b14324780..ba2cebf1bb2f 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1939,7 +1939,8 @@ static const struct dev_pm_ops acpi_ec_pm = { SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume) }; -static int param_set_event_clearing(const char *val, struct kernel_param *kp) +static int param_set_event_clearing(const char *val, + const struct kernel_param *kp) { int result = 0; @@ -1957,7 +1958,8 @@ static int param_set_event_clearing(const char *val, struct kernel_param *kp) return result; } -static int param_get_event_clearing(char *buffer, struct kernel_param *kp) +static int param_get_event_clearing(char *buffer, + const struct kernel_param *kp) { switch (ec_event_clearing) { case ACPI_EC_EVT_TIMING_STATUS: diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c index 78a5a23010ab..cf2c1b2b2d64 100644 --- a/drivers/acpi/sysfs.c +++ b/drivers/acpi/sysfs.c @@ -229,7 +229,8 @@ module_param_cb(trace_method_name, ¶m_ops_trace_method, &trace_method_name, module_param_cb(trace_debug_layer, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_layer, 0644); module_param_cb(trace_debug_level, ¶m_ops_trace_attrib, &acpi_gbl_trace_dbg_level, 0644); -static int param_set_trace_state(const char *val, struct kernel_param *kp) +static int param_set_trace_state(const char *val, + const struct kernel_param *kp) { acpi_status status; const char *method = trace_method_name; @@ -265,7 +266,7 @@ static int param_set_trace_state(const char *val, struct kernel_param *kp) return 0; } -static int param_get_trace_state(char *buffer, struct kernel_param *kp) +static int param_get_trace_state(char *buffer, const struct kernel_param *kp) { if (!(acpi_gbl_trace_flags & ACPI_TRACE_ENABLED)) return sprintf(buffer, "disable"); @@ -294,7 +295,8 @@ MODULE_PARM_DESC(aml_debug_output, "To enable/disable the ACPI Debug Object output."); /* /sys/module/acpi/parameters/acpica_version */ -static int param_get_acpica_version(char *buffer, struct kernel_param *kp) +static int param_get_acpica_version(char *buffer, + const struct kernel_param *kp) { int result; diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 0621a95b8597..c45157d71b33 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -150,7 +150,7 @@ static DECLARE_WAIT_QUEUE_HEAD(binder_user_error_wait); static int binder_stop_on_user_error; static int binder_set_stop_on_user_error(const char *val, - struct kernel_param *kp) + const struct kernel_param *kp) { int ret; diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index 9f2e3be2c5b8..676c910e990f 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -66,7 +66,7 @@ static void (*specific_poweroff_func)(ipmi_user_t user); /* Holds the old poweroff function so we can restore it on removal. */ static void (*old_poweroff_func)(void); -static int set_param_ifnum(const char *val, struct kernel_param *kp) +static int set_param_ifnum(const char *val, const struct kernel_param *kp) { int rv = param_set_int(val, kp); if (rv) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 36f47e8d06a3..d2ac66fe0645 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1345,7 +1345,7 @@ static unsigned int num_slave_addrs; #define IPMI_MEM_ADDR_SPACE 1 static const char * const addr_space_to_str[] = { "i/o", "mem" }; -static int hotmod_handler(const char *val, struct kernel_param *kp); +static int hotmod_handler(const char *val, const struct kernel_param *kp); module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" @@ -1811,7 +1811,7 @@ static struct smi_info *smi_info_alloc(void) return info; } -static int hotmod_handler(const char *val, struct kernel_param *kp) +static int hotmod_handler(const char *val, const struct kernel_param *kp) { char *str = kstrdup(val, GFP_KERNEL); int rv; diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index e4fcfa84fbd3..c70ea82c815c 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -50,7 +50,7 @@ int edac_mc_get_poll_msec(void) return edac_mc_poll_msec; } -static int edac_set_poll_msec(const char *val, struct kernel_param *kp) +static int edac_set_poll_msec(const char *val, const struct kernel_param *kp) { unsigned long l; int ret; diff --git a/drivers/edac/edac_module.c b/drivers/edac/edac_module.c index 172598a27d7d..32a931d0cb71 100644 --- a/drivers/edac/edac_module.c +++ b/drivers/edac/edac_module.c @@ -19,7 +19,8 @@ #ifdef CONFIG_EDAC_DEBUG -static int edac_set_debug_level(const char *buf, struct kernel_param *kp) +static int edac_set_debug_level(const char *buf, + const struct kernel_param *kp) { unsigned long val; int ret; diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 20b40ad26325..42ed887ba0be 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c @@ -34,7 +34,8 @@ module_param(emulate_scroll_wheel, bool, 0644); MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel"); static unsigned int scroll_speed = 32; -static int param_set_scroll_speed(const char *val, struct kernel_param *kp) { +static int param_set_scroll_speed(const char *val, + const struct kernel_param *kp) { unsigned long speed; if (!val || kstrtoul(val, 0, &speed) || speed > 63) return -EINVAL; diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index d127ace6aa57..6ee866fcc5dd 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -244,7 +244,7 @@ struct chs_geom { static unsigned int ide_disks; static struct chs_geom ide_disks_chs[MAX_HWIFS * MAX_DRIVES]; -static int ide_set_disk_chs(const char *str, struct kernel_param *kp) +static int ide_set_disk_chs(const char *str, const struct kernel_param *kp) { unsigned int a, b, c = 0, h = 0, s = 0, i, j = 1; @@ -328,7 +328,7 @@ static void ide_dev_apply_params(ide_drive_t *drive, u8 unit) static unsigned int ide_ignore_cable; -static int ide_set_ignore_cable(const char *s, struct kernel_param *kp) +static int ide_set_ignore_cable(const char *s, const struct kernel_param *kp) { int i, j = 1; diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c index 14cadf6d6214..a45e46098914 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -150,7 +150,7 @@ static struct kparam_string kp_txselect = { .string = txselect_list, .maxlen = MAX_ATTEN_LEN }; -static int setup_txselect(const char *, struct kernel_param *); +static int setup_txselect(const char *, const struct kernel_param *); module_param_call(txselect, setup_txselect, param_get_string, &kp_txselect, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(txselect, @@ -6169,7 +6169,7 @@ static void set_no_qsfp_atten(struct qib_devdata *dd, int change) } /* handle the txselect parameter changing */ -static int setup_txselect(const char *str, struct kernel_param *kp) +static int setup_txselect(const char *str, const struct kernel_param *kp) { struct qib_devdata *dd; unsigned long val; diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 9e8e9220f816..9612e5bdfb00 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -80,7 +80,7 @@ module_param(srpt_srq_size, int, 0444); MODULE_PARM_DESC(srpt_srq_size, "Shared receive queue (SRQ) size."); -static int srpt_get_u64_x(char *buffer, struct kernel_param *kp) +static int srpt_get_u64_x(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "0x%016llx", *(u64 *)kp->arg); } diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c index dce6632daae1..ae2b2669af1b 100644 --- a/drivers/isdn/hardware/mISDN/avmfritz.c +++ b/drivers/isdn/hardware/mISDN/avmfritz.c @@ -156,7 +156,7 @@ _set_debug(struct fritzcard *card) } static int -set_debug(const char *val, struct kernel_param *kp) +set_debug(const char *val, const struct kernel_param *kp) { int ret; struct fritzcard *card; diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c index d5bdbaf93a1a..1fc290659e94 100644 --- a/drivers/isdn/hardware/mISDN/mISDNinfineon.c +++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c @@ -244,7 +244,7 @@ _set_debug(struct inf_hw *card) } static int -set_debug(const char *val, struct kernel_param *kp) +set_debug(const char *val, const struct kernel_param *kp) { int ret; struct inf_hw *card; diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c index 6a6d848bd18e..89d9ba8ed535 100644 --- a/drivers/isdn/hardware/mISDN/netjet.c +++ b/drivers/isdn/hardware/mISDN/netjet.c @@ -111,7 +111,7 @@ _set_debug(struct tiger_hw *card) } static int -set_debug(const char *val, struct kernel_param *kp) +set_debug(const char *val, const struct kernel_param *kp) { int ret; struct tiger_hw *card; diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c index 9815bb4eec9c..1f1446ed8d5f 100644 --- a/drivers/isdn/hardware/mISDN/speedfax.c +++ b/drivers/isdn/hardware/mISDN/speedfax.c @@ -94,7 +94,7 @@ _set_debug(struct sfax_hw *card) } static int -set_debug(const char *val, struct kernel_param *kp) +set_debug(const char *val, const struct kernel_param *kp) { int ret; struct sfax_hw *card; diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c index d80072fef434..209036a4af3a 100644 --- a/drivers/isdn/hardware/mISDN/w6692.c +++ b/drivers/isdn/hardware/mISDN/w6692.c @@ -101,7 +101,7 @@ _set_debug(struct w6692_hw *card) } static int -set_debug(const char *val, struct kernel_param *kp) +set_debug(const char *val, const struct kernel_param *kp) { int ret; struct w6692_hw *card; diff --git a/drivers/md/md.c b/drivers/md/md.c index 0ff1bbf6c90e..276c7ecedf10 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5357,7 +5357,7 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) return NULL; } -static int add_named_array(const char *val, struct kernel_param *kp) +static int add_named_array(const char *val, const struct kernel_param *kp) { /* * val must be "md_*" or "mdNNN". @@ -9274,11 +9274,11 @@ static __exit void md_exit(void) subsys_initcall(md_init); module_exit(md_exit) -static int get_ro(char *buffer, struct kernel_param *kp) +static int get_ro(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%d", start_readonly); } -static int set_ro(const char *val, struct kernel_param *kp) +static int set_ro(const char *val, const struct kernel_param *kp) { return kstrtouint(val, 10, (unsigned int *)&start_readonly); } diff --git a/drivers/media/pci/tw686x/tw686x-core.c b/drivers/media/pci/tw686x/tw686x-core.c index 336e2f9bc1b6..b762e5f0ba1d 100644 --- a/drivers/media/pci/tw686x/tw686x-core.c +++ b/drivers/media/pci/tw686x/tw686x-core.c @@ -72,12 +72,12 @@ static const char *dma_mode_name(unsigned int mode) } } -static int tw686x_dma_mode_get(char *buffer, struct kernel_param *kp) +static int tw686x_dma_mode_get(char *buffer, const struct kernel_param *kp) { return sprintf(buffer, "%s", dma_mode_name(dma_mode)); } -static int tw686x_dma_mode_set(const char *val, struct kernel_param *kp) +static int tw686x_dma_mode_set(const char *val, const struct kernel_param *kp) { if (!strcasecmp(val, dma_mode_name(TW686X_DMA_MODE_MEMCPY))) dma_mode = TW686X_DMA_MODE_MEMCPY; diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 6d22b22cb35b..28b91b7d756f 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -2230,7 +2230,7 @@ static int uvc_reset_resume(struct usb_interface *intf) * Module parameters */ -static int uvc_clock_param_get(char *buffer, struct kernel_param *kp) +static int uvc_clock_param_get(char *buffer, const struct kernel_param *kp) { if (uvc_clock_param == CLOCK_MONOTONIC) return sprintf(buffer, "CLOCK_MONOTONIC"); @@ -2238,7 +2238,7 @@ static int uvc_clock_param_get(char *buffer, struct kernel_param *kp) return sprintf(buffer, "CLOCK_REALTIME"); } -static int uvc_clock_param_set(const char *val, struct kernel_param *kp) +static int uvc_clock_param_set(const char *val, const struct kernel_param *kp) { if (strncasecmp(val, "clock_", strlen("clock_")) == 0) val += strlen("clock_"); diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c index 84eab28665f3..7a93400eea2a 100644 --- a/drivers/message/fusion/mptbase.c +++ b/drivers/message/fusion/mptbase.c @@ -99,7 +99,7 @@ module_param(mpt_channel_mapping, int, 0); MODULE_PARM_DESC(mpt_channel_mapping, " Mapping id's to channels (default=0)"); static int mpt_debug_level; -static int mpt_set_debug_level(const char *val, struct kernel_param *kp); +static int mpt_set_debug_level(const char *val, const struct kernel_param *kp); module_param_call(mpt_debug_level, mpt_set_debug_level, param_get_int, &mpt_debug_level, 0600); MODULE_PARM_DESC(mpt_debug_level, @@ -242,7 +242,7 @@ pci_enable_io_access(struct pci_dev *pdev) pci_write_config_word(pdev, PCI_COMMAND, command_reg); } -static int mpt_set_debug_level(const char *val, struct kernel_param *kp) +static int mpt_set_debug_level(const char *val, const struct kernel_param *kp) { int ret = param_set_int(val, kp); MPT_ADAPTER *ioc; diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c index fc7efedbc4be..24108bfad889 100644 --- a/drivers/misc/kgdbts.c +++ b/drivers/misc/kgdbts.c @@ -1132,7 +1132,8 @@ static void kgdbts_put_char(u8 chr) ts.run_test(0, chr); } -static int param_set_kgdbts_var(const char *kmessage, struct kernel_param *kp) +static int param_set_kgdbts_var(const char *kmessage, + const struct kernel_param *kp) { int len = strlen(kmessage); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 7c887f111a7d..62fd6905c648 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -431,7 +431,7 @@ static int block2mtd_setup2(const char *val) } -static int block2mtd_setup(const char *val, struct kernel_param *kp) +static int block2mtd_setup(const char *val, const struct kernel_param *kp) { #ifdef MODULE return block2mtd_setup2(val); diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 8b66e52ca3cc..7287696a21f9 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -266,7 +266,7 @@ static int phram_setup(const char *val) return ret; } -static int phram_param_call(const char *val, struct kernel_param *kp) +static int phram_param_call(const char *val, const struct kernel_param *kp) { #ifdef MODULE return phram_setup(val); diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 842550b5712a..136ce05d2328 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -1334,7 +1334,7 @@ static int bytes_str_to_int(const char *str) * This function returns zero in case of success and a negative error code in * case of error. */ -static int ubi_mtd_param_parse(const char *val, struct kernel_param *kp) +static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp) { int i, len; struct mtd_dev_param *p; diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 1dfa10cc566b..ca3ee0f7e61d 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1060,7 +1060,8 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) } EXPORT_SYMBOL(pci_disable_link_state); -static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) +static int pcie_aspm_set_policy(const char *val, + const struct kernel_param *kp) { int i; struct pcie_link_state *link; @@ -1087,7 +1088,7 @@ static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) return 0; } -static int pcie_aspm_get_policy(char *buffer, struct kernel_param *kp) +static int pcie_aspm_get_policy(char *buffer, const struct kernel_param *kp) { int i, cnt = 0; for (i = 0; i < ARRAY_SIZE(policy_str); i++) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 2242d6035d9e..3887dfeafc96 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -9543,7 +9543,7 @@ static struct ibm_init_struct ibms_init[] __initdata = { }, }; -static int __init set_ibm_param(const char *val, struct kernel_param *kp) +static int __init set_ibm_param(const char *val, const struct kernel_param *kp) { unsigned int i; struct ibm_struct *ibm; diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c index 375c536cbc68..c5eb0c468f0b 100644 --- a/drivers/scsi/fcoe/fcoe_transport.c +++ b/drivers/scsi/fcoe/fcoe_transport.c @@ -32,13 +32,13 @@ MODULE_AUTHOR("Open-FCoE.org"); MODULE_DESCRIPTION("FIP discovery protocol and FCoE transport for FCoE HBAs"); MODULE_LICENSE("GPL v2"); -static int fcoe_transport_create(const char *, struct kernel_param *); -static int fcoe_transport_destroy(const char *, struct kernel_param *); +static int fcoe_transport_create(const char *, const struct kernel_param *); +static int fcoe_transport_destroy(const char *, const struct kernel_param *); static int fcoe_transport_show(char *buffer, const struct kernel_param *kp); static struct fcoe_transport *fcoe_transport_lookup(struct net_device *device); static struct fcoe_transport *fcoe_netdev_map_lookup(struct net_device *device); -static int fcoe_transport_enable(const char *, struct kernel_param *); -static int fcoe_transport_disable(const char *, struct kernel_param *); +static int fcoe_transport_enable(const char *, const struct kernel_param *); +static int fcoe_transport_disable(const char *, const struct kernel_param *); static int libfcoe_device_notification(struct notifier_block *notifier, ulong event, void *ptr); @@ -865,7 +865,8 @@ EXPORT_SYMBOL(fcoe_ctlr_destroy_store); * * Returns: 0 for success */ -static int fcoe_transport_create(const char *buffer, struct kernel_param *kp) +static int fcoe_transport_create(const char *buffer, + const struct kernel_param *kp) { int rc = -ENODEV; struct net_device *netdev = NULL; @@ -930,7 +931,8 @@ out_nodev: * * Returns: 0 for success */ -static int fcoe_transport_destroy(const char *buffer, struct kernel_param *kp) +static int fcoe_transport_destroy(const char *buffer, + const struct kernel_param *kp) { int rc = -ENODEV; struct net_device *netdev = NULL; @@ -974,7 +976,8 @@ out_nodev: * * Returns: 0 for success */ -static int fcoe_transport_disable(const char *buffer, struct kernel_param *kp) +static int fcoe_transport_disable(const char *buffer, + const struct kernel_param *kp) { int rc = -ENODEV; struct net_device *netdev = NULL; @@ -1008,7 +1011,8 @@ out_nodev: * * Returns: 0 for success */ -static int fcoe_transport_enable(const char *buffer, struct kernel_param *kp) +static int fcoe_transport_enable(const char *buffer, + const struct kernel_param *kp) { int rc = -ENODEV; struct net_device *netdev = NULL; diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 87999905bca3..3d36deee8285 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -105,7 +105,7 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc); * */ static int -_scsih_set_fwfault_debug(const char *val, struct kernel_param *kp) +_scsih_set_fwfault_debug(const char *val, const struct kernel_param *kp) { int ret = param_set_int(val, kp); struct MPT3SAS_ADAPTER *ioc; diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 22998cbd538f..07719da7ae4a 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -281,7 +281,7 @@ struct _scsi_io_transfer { * Note: The logging levels are defined in mpt3sas_debug.h. */ static int -_scsih_set_debug_level(const char *val, struct kernel_param *kp) +_scsih_set_debug_level(const char *val, const struct kernel_param *kp) { int ret = param_set_int(val, kp); struct MPT3SAS_ADAPTER *ioc; diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index a260cde743e2..5532c440bf61 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -245,7 +245,8 @@ static void kgdboc_put_char(u8 chr) kgdb_tty_line, chr); } -static int param_set_kgdboc_var(const char *kmessage, struct kernel_param *kp) +static int param_set_kgdboc_var(const char *kmessage, + const struct kernel_param *kp) { int len = strlen(kmessage); diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 65c88379a3a1..7d67fc150edc 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -31,7 +31,7 @@ static struct kmem_cache *fuse_inode_cachep; struct list_head fuse_conn_list; DEFINE_MUTEX(fuse_mutex); -static int set_global_limit(const char *val, struct kernel_param *kp); +static int set_global_limit(const char *val, const struct kernel_param *kp); unsigned max_user_bgreq; module_param_call(max_user_bgreq, set_global_limit, param_get_uint, @@ -823,7 +823,7 @@ static void sanitize_global_limit(unsigned *limit) *limit = (1 << 16) - 1; } -static int set_global_limit(const char *val, struct kernel_param *kp) +static int set_global_limit(const char *val, const struct kernel_param *kp) { int rv; diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index b995bdc13976..b837fb7e290a 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -602,7 +602,7 @@ static struct ctl_table nlm_sysctl_root[] = { */ #define param_set_min_max(name, type, which_strtol, min, max) \ -static int param_set_##name(const char *val, struct kernel_param *kp) \ +static int param_set_##name(const char *val, const struct kernel_param *kp) \ { \ char *endp; \ __typeof__(type) num = which_strtol(val, &endp, 0); \ diff --git a/fs/ocfs2/dlmfs/dlmfs.c b/fs/ocfs2/dlmfs/dlmfs.c index 9ab9e1892b5f..988137de08f5 100644 --- a/fs/ocfs2/dlmfs/dlmfs.c +++ b/fs/ocfs2/dlmfs/dlmfs.c @@ -88,13 +88,13 @@ struct workqueue_struct *user_dlm_worker; */ #define DLMFS_CAPABILITIES "bast stackglue" static int param_set_dlmfs_capabilities(const char *val, - struct kernel_param *kp) + const struct kernel_param *kp) { printk(KERN_ERR "%s: readonly parameter\n", kp->name); return -EINVAL; } static int param_get_dlmfs_capabilities(char *buffer, - struct kernel_param *kp) + const struct kernel_param *kp) { return strlcpy(buffer, DLMFS_CAPABILITIES, strlen(DLMFS_CAPABILITIES) + 1); diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 8f3bd30511de..fd5241ce1fc9 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -284,7 +284,7 @@ static inline bool nf_ct_should_gc(const struct nf_conn *ct) struct kernel_param; -int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp); +int nf_conntrack_set_hashsize(const char *val, const struct kernel_param *kp); int nf_conntrack_hash_resize(unsigned int hashsize); extern struct hlist_nulls_head *nf_conntrack_hash; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 01130392b7c0..5e50c54e1318 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1940,7 +1940,7 @@ int nf_conntrack_hash_resize(unsigned int hashsize) return 0; } -int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp) +int nf_conntrack_set_hashsize(const char *val, const struct kernel_param *kp) { unsigned int hashsize; int rc; diff --git a/net/netfilter/nf_nat_ftp.c b/net/netfilter/nf_nat_ftp.c index e84a578dbe35..d76afafdc699 100644 --- a/net/netfilter/nf_nat_ftp.c +++ b/net/netfilter/nf_nat_ftp.c @@ -134,7 +134,7 @@ static int __init nf_nat_ftp_init(void) } /* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ -static int warn_set(const char *val, struct kernel_param *kp) +static int warn_set(const char *val, const struct kernel_param *kp) { printk(KERN_INFO KBUILD_MODNAME ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); diff --git a/net/netfilter/nf_nat_irc.c b/net/netfilter/nf_nat_irc.c index 0648cb096bd8..dcb5f6375d9d 100644 --- a/net/netfilter/nf_nat_irc.c +++ b/net/netfilter/nf_nat_irc.c @@ -106,7 +106,7 @@ static int __init nf_nat_irc_init(void) } /* Prior to 2.6.11, we had a ports param. No longer, but don't break users. */ -static int warn_set(const char *val, struct kernel_param *kp) +static int warn_set(const char *val, const struct kernel_param *kp) { printk(KERN_INFO KBUILD_MODNAME ": kernel >= 2.6.10 only uses 'ports' for conntrack modules\n"); diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index aa04666f929d..e5e4e18699c8 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -50,7 +50,7 @@ EXPORT_SYMBOL_GPL(svc_pool_map); static DEFINE_MUTEX(svc_pool_map_mutex);/* protects svc_pool_map.count only */ static int -param_set_pool_mode(const char *val, struct kernel_param *kp) +param_set_pool_mode(const char *val, const struct kernel_param *kp) { int *ip = (int *)kp->arg; struct svc_pool_map *m = &svc_pool_map; @@ -80,7 +80,7 @@ out: } static int -param_get_pool_mode(char *buf, struct kernel_param *kp) +param_get_pool_mode(char *buf, const struct kernel_param *kp) { int *ip = (int *)kp->arg; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 72b915dfcaf7..f46a12b339bc 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -1200,11 +1200,11 @@ static const struct kernel_param_ops param_ops_aalockpolicy = { .get = param_get_aalockpolicy }; -static int param_set_audit(const char *val, struct kernel_param *kp); -static int param_get_audit(char *buffer, struct kernel_param *kp); +static int param_set_audit(const char *val, const struct kernel_param *kp); +static int param_get_audit(char *buffer, const struct kernel_param *kp); -static int param_set_mode(const char *val, struct kernel_param *kp); -static int param_get_mode(char *buffer, struct kernel_param *kp); +static int param_set_mode(const char *val, const struct kernel_param *kp); +static int param_get_mode(char *buffer, const struct kernel_param *kp); /* Flag values, also controllable via /sys/module/apparmor/parameters * We define special types as we want to do additional mediation. @@ -1338,7 +1338,7 @@ static int param_get_aauint(char *buffer, const struct kernel_param *kp) return param_get_uint(buffer, kp); } -static int param_get_audit(char *buffer, struct kernel_param *kp) +static int param_get_audit(char *buffer, const struct kernel_param *kp) { if (!apparmor_enabled) return -EINVAL; @@ -1347,7 +1347,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]); } -static int param_set_audit(const char *val, struct kernel_param *kp) +static int param_set_audit(const char *val, const struct kernel_param *kp) { int i; @@ -1368,7 +1368,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) return -EINVAL; } -static int param_get_mode(char *buffer, struct kernel_param *kp) +static int param_get_mode(char *buffer, const struct kernel_param *kp) { if (!apparmor_enabled) return -EINVAL; @@ -1378,7 +1378,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]); } -static int param_set_mode(const char *val, struct kernel_param *kp) +static int param_set_mode(const char *val, const struct kernel_param *kp) { int i; -- cgit v1.2.3-59-g8ed1b From ece1996a21eeb344b49200e627c6660111009c10 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 17 Oct 2017 19:04:43 -0700 Subject: module: Do not paper over type mismatches in module_param_call() The module_param_call() macro was explicitly casting the .set and .get function prototypes away. This can lead to hard-to-find type mismatches. Now that all the function prototypes have been fixed tree-wide, we can drop these casts, and use named initializers too. Signed-off-by: Kees Cook Signed-off-by: Jessica Yu --- include/linux/moduleparam.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/moduleparam.h b/include/linux/moduleparam.h index 037a90a522a5..20386252fe3e 100644 --- a/include/linux/moduleparam.h +++ b/include/linux/moduleparam.h @@ -227,9 +227,9 @@ struct kparam_array VERIFY_OCTAL_PERMISSIONS(perm), level, flags, { arg } } /* Obsolete - use module_param_cb() */ -#define module_param_call(name, set, get, arg, perm) \ +#define module_param_call(name, _set, _get, arg, perm) \ static const struct kernel_param_ops __param_ops_##name = \ - { .flags = 0, (void *)set, (void *)get }; \ + { .flags = 0, .set = _set, .get = _get }; \ __module_param_call(MODULE_PARAM_PREFIX, \ name, &__param_ops_##name, arg, perm, -1, 0) -- cgit v1.2.3-59-g8ed1b From 7761daa6a1599fa5479b8da367470f632a1927e0 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 20 Oct 2017 13:26:01 +0300 Subject: fsnotify: convert fsnotify_group.refcnt from atomic_t to refcount_t atomic_t variables are currently used to implement reference counters with the following properties: - counter is initialized to 1 using atomic_set() - a resource is freed upon counter reaching zero - once counter reaches zero, its further increments aren't allowed - counter schema uses basic atomic operations (set, inc, inc_not_zero, dec_and_test, etc.) Such atomic variables should be converted to a newly provided refcount_t type and API that prevents accidental counter overflows and underflows. This is important since overflows and underflows can lead to use-after-free situation and be exploitable. The variable fsnotify_group.refcnt is used as pure reference counter. Convert it to refcount_t and fix up the operations. Suggested-by: Kees Cook Reviewed-by: David Windsor Reviewed-by: Hans Liljestrand Signed-off-by: Elena Reshetova Signed-off-by: Jan Kara --- fs/notify/group.c | 6 +++--- include/linux/fsnotify_backend.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/notify/group.c b/fs/notify/group.c index 32357534de18..b7a4b6a69efa 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -107,7 +107,7 @@ void fsnotify_destroy_group(struct fsnotify_group *group) */ void fsnotify_get_group(struct fsnotify_group *group) { - atomic_inc(&group->refcnt); + refcount_inc(&group->refcnt); } /* @@ -115,7 +115,7 @@ void fsnotify_get_group(struct fsnotify_group *group) */ void fsnotify_put_group(struct fsnotify_group *group) { - if (atomic_dec_and_test(&group->refcnt)) + if (refcount_dec_and_test(&group->refcnt)) fsnotify_final_destroy_group(group); } @@ -131,7 +131,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) return ERR_PTR(-ENOMEM); /* set to 0 when there a no external references to this group */ - atomic_set(&group->refcnt, 1); + refcount_set(&group->refcnt, 1); atomic_set(&group->num_marks, 0); atomic_set(&group->user_waits, 0); diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index c6c69318752b..20a57bac38f2 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -17,6 +17,7 @@ #include #include #include +#include /* * IN_* from inotfy.h lines up EXACTLY with FS_*, this is so we can easily @@ -135,7 +136,7 @@ struct fsnotify_group { * inotify_init() and the refcnt will hit 0 only when that fd has been * closed. */ - atomic_t refcnt; /* things with interest in this group */ + refcount_t refcnt; /* things with interest in this group */ const struct fsnotify_ops *ops; /* how this group handles things */ -- cgit v1.2.3-59-g8ed1b From 6685df31255493c3f0e9e0b8bf885e4c9762fc5d Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Mon, 30 Oct 2017 21:14:56 +0100 Subject: fanotify: clean up CONFIG_FANOTIFY_ACCESS_PERMISSIONS ifdefs The only negative from this patch should be an addition of 32bytes to 'struct fsnotify_group' if CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not defined. Reviewed-by: Amir Goldstein Signed-off-by: Miklos Szeredi Signed-off-by: Jan Kara --- fs/notify/fanotify/fanotify.c | 30 +++++++------------------- fs/notify/fanotify/fanotify.h | 8 +++++-- fs/notify/fanotify/fanotify_user.c | 43 ++++++++++++++------------------------ include/linux/fsnotify_backend.h | 2 -- 4 files changed, 29 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index df3f484e458a..63f56b007280 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -35,15 +35,13 @@ static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) pr_debug("%s: list=%p event=%p\n", __func__, list, event); -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS /* * Don't merge a permission event with any other event so that we know * the event structure we have created in fanotify_handle_event() is the * one we should check for permission response. */ - if (event->mask & FAN_ALL_PERM_EVENTS) + if (fanotify_is_perm_event(event->mask)) return 0; -#endif list_for_each_entry_reverse(test_event, list, list) { if (should_merge(test_event, event)) { @@ -55,7 +53,6 @@ static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) return 0; } -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS static int fanotify_get_response(struct fsnotify_group *group, struct fanotify_perm_event_info *event, struct fsnotify_iter_info *iter_info) @@ -82,7 +79,6 @@ static int fanotify_get_response(struct fsnotify_group *group, return ret; } -#endif static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmnt_mark, @@ -141,8 +137,7 @@ struct fanotify_event_info *fanotify_alloc_event(struct inode *inode, u32 mask, { struct fanotify_event_info *event; -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - if (mask & FAN_ALL_PERM_EVENTS) { + if (fanotify_is_perm_event(mask)) { struct fanotify_perm_event_info *pevent; pevent = kmem_cache_alloc(fanotify_perm_event_cachep, @@ -153,7 +148,6 @@ struct fanotify_event_info *fanotify_alloc_event(struct inode *inode, u32 mask, pevent->response = 0; goto init; } -#endif event = kmem_cache_alloc(fanotify_event_cachep, GFP_KERNEL); if (!event) return NULL; @@ -200,8 +194,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, mask); -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - if (mask & FAN_ALL_PERM_EVENTS) { + if (fanotify_is_perm_event(mask)) { /* * fsnotify_prepare_user_wait() fails if we race with mark * deletion. Just let the operation pass in that case. @@ -209,7 +202,6 @@ static int fanotify_handle_event(struct fsnotify_group *group, if (!fsnotify_prepare_user_wait(iter_info)) return 0; } -#endif event = fanotify_alloc_event(inode, mask, data); ret = -ENOMEM; @@ -225,21 +217,15 @@ static int fanotify_handle_event(struct fsnotify_group *group, fsnotify_destroy_event(group, fsn_event); ret = 0; - goto finish; - } - -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - if (mask & FAN_ALL_PERM_EVENTS) { + } else if (fanotify_is_perm_event(mask)) { ret = fanotify_get_response(group, FANOTIFY_PE(fsn_event), iter_info); fsnotify_destroy_event(group, fsn_event); } finish: - if (mask & FAN_ALL_PERM_EVENTS) + if (fanotify_is_perm_event(mask)) fsnotify_finish_user_wait(iter_info); -#else -finish: -#endif + return ret; } @@ -259,13 +245,11 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event) event = FANOTIFY_E(fsn_event); path_put(&event->path); put_pid(event->tgid); -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - if (fsn_event->mask & FAN_ALL_PERM_EVENTS) { + if (fanotify_is_perm_event(fsn_event->mask)) { kmem_cache_free(fanotify_perm_event_cachep, FANOTIFY_PE(fsn_event)); return; } -#endif kmem_cache_free(fanotify_event_cachep, event); } diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 4eb6f5efa282..dc219cf07a6a 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -21,7 +21,6 @@ struct fanotify_event_info { struct pid *tgid; }; -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS /* * Structure for permission fanotify events. It gets allocated and freed in * fanotify_handle_event() since we wait there for user response. When the @@ -40,7 +39,12 @@ FANOTIFY_PE(struct fsnotify_event *fse) { return container_of(fse, struct fanotify_perm_event_info, fae.fse); } -#endif + +static inline bool fanotify_is_perm_event(u32 mask) +{ + return IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS) && + mask & FAN_ALL_PERM_EVENTS; +} static inline struct fanotify_event_info *FANOTIFY_E(struct fsnotify_event *fse) { diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 907a481ac781..a434de023c49 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -142,7 +142,6 @@ static int fill_event_metadata(struct fsnotify_group *group, return ret; } -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS static struct fanotify_perm_event_info *dequeue_event( struct fsnotify_group *group, int fd) { @@ -199,7 +198,6 @@ static int process_access_response(struct fsnotify_group *group, return 0; } -#endif static ssize_t copy_event_to_user(struct fsnotify_group *group, struct fsnotify_event *event, @@ -221,10 +219,8 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, fanotify_event_metadata.event_len)) goto out_close_fd; -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - if (event->mask & FAN_ALL_PERM_EVENTS) + if (fanotify_is_perm_event(event->mask)) FANOTIFY_PE(event)->fd = fd; -#endif if (fd != FAN_NOFD) fd_install(fd, f); @@ -309,10 +305,9 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, * Permission events get queued to wait for response. Other * events can be destroyed now. */ - if (!(kevent->mask & FAN_ALL_PERM_EVENTS)) { + if (!fanotify_is_perm_event(kevent->mask)) { fsnotify_destroy_event(group, kevent); } else { -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS if (ret <= 0) { FANOTIFY_PE(kevent)->response = FAN_DENY; wake_up(&group->fanotify_data.access_waitq); @@ -322,7 +317,6 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, &group->fanotify_data.access_list); spin_unlock(&group->notification_lock); } -#endif } if (ret < 0) break; @@ -338,11 +332,13 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) { -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS struct fanotify_response response = { .fd = -1, .response = -1 }; struct fsnotify_group *group; int ret; + if (!IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS)) + return -EINVAL; + group = file->private_data; if (count > sizeof(response)) @@ -358,16 +354,11 @@ static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t count = ret; return count; -#else - return -EINVAL; -#endif } static int fanotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; - -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS struct fanotify_perm_event_info *event, *next; struct fsnotify_event *fsn_event; @@ -403,14 +394,14 @@ static int fanotify_release(struct inode *ignored, struct file *file) spin_unlock(&group->notification_lock); fsnotify_destroy_event(group, fsn_event); spin_lock(&group->notification_lock); - } else + } else { FANOTIFY_PE(fsn_event)->response = FAN_ALLOW; + } } spin_unlock(&group->notification_lock); /* Response for all permission events it set, wakeup waiters */ wake_up(&group->fanotify_data.access_waitq); -#endif /* matches the fanotify_init->fsnotify_alloc_group */ fsnotify_destroy_group(group); @@ -768,10 +759,8 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) if (force_o_largefile()) event_f_flags |= O_LARGEFILE; group->fanotify_data.f_flags = event_f_flags; -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS init_waitqueue_head(&group->fanotify_data.access_waitq); INIT_LIST_HEAD(&group->fanotify_data.access_list); -#endif switch (flags & FAN_ALL_CLASS_BITS) { case FAN_CLASS_NOTIF: group->priority = FS_PRIO_0; @@ -825,6 +814,7 @@ SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, struct fsnotify_group *group; struct fd f; struct path path; + u32 valid_mask = FAN_ALL_EVENTS | FAN_EVENT_ON_CHILD; int ret; pr_debug("%s: fanotify_fd=%d flags=%x dfd=%d pathname=%p mask=%llx\n", @@ -855,11 +845,10 @@ SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, mask &= ~FAN_ONDIR; } -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - if (mask & ~(FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_EVENT_ON_CHILD)) -#else - if (mask & ~(FAN_ALL_EVENTS | FAN_EVENT_ON_CHILD)) -#endif + if (IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS)) + valid_mask |= FAN_ALL_PERM_EVENTS; + + if (mask & ~valid_mask) return -EINVAL; f = fdget(fanotify_fd); @@ -949,10 +938,10 @@ static int __init fanotify_user_setup(void) { fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); fanotify_event_cachep = KMEM_CACHE(fanotify_event_info, SLAB_PANIC); -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS - fanotify_perm_event_cachep = KMEM_CACHE(fanotify_perm_event_info, - SLAB_PANIC); -#endif + if (IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS)) { + fanotify_perm_event_cachep = + KMEM_CACHE(fanotify_perm_event_info, SLAB_PANIC); + } return 0; } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 20a57bac38f2..744e2b9969fc 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -183,11 +183,9 @@ struct fsnotify_group { #endif #ifdef CONFIG_FANOTIFY struct fanotify_group_private_data { -#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS /* allows a group to block waiting for a userspace response */ struct list_head access_list; wait_queue_head_t access_waitq; -#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */ int f_flags; unsigned int max_marks; struct user_struct *user; -- cgit v1.2.3-59-g8ed1b From ab97f87325e28b7ef7717e6cb62e8da14a7176e1 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 20 Oct 2017 13:26:02 +0300 Subject: fsnotify: convert fsnotify_mark.refcnt from atomic_t to refcount_t atomic_t variables are currently used to implement reference counters with the following properties: - counter is initialized to 1 using atomic_set() - a resource is freed upon counter reaching zero - once counter reaches zero, its further increments aren't allowed - counter schema uses basic atomic operations (set, inc, inc_not_zero, dec_and_test, etc.) Such atomic variables should be converted to a newly provided refcount_t type and API that prevents accidental counter overflows and underflows. This is important since overflows and underflows can lead to use-after-free situation and be exploitable. The variable fsnotify_mark.refcnt is used as pure reference counter. Convert it to refcount_t and fix up the operations. Suggested-by: Kees Cook Reviewed-by: David Windsor Reviewed-by: Hans Liljestrand Signed-off-by: Elena Reshetova Signed-off-by: Jan Kara --- fs/notify/inotify/inotify_user.c | 4 ++-- fs/notify/mark.c | 14 +++++++------- include/linux/fsnotify_backend.h | 2 +- kernel/audit_tree.c | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 7cc7d3fb1862..d3c20e0bb046 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -376,7 +376,7 @@ static struct inotify_inode_mark *inotify_idr_find_locked(struct fsnotify_group fsnotify_get_mark(fsn_mark); /* One ref for being in the idr, one ref we just took */ - BUG_ON(atomic_read(&fsn_mark->refcnt) < 2); + BUG_ON(refcount_read(&fsn_mark->refcnt) < 2); } return i_mark; @@ -446,7 +446,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, * One ref for being in the idr * one ref grabbed by inotify_idr_find */ - if (unlikely(atomic_read(&i_mark->fsn_mark.refcnt) < 2)) { + if (unlikely(refcount_read(&i_mark->fsn_mark.refcnt) < 2)) { printk(KERN_ERR "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p\n", __func__, i_mark, i_mark->wd, i_mark->fsn_mark.group); /* we can't really recover with bad ref cnting.. */ diff --git a/fs/notify/mark.c b/fs/notify/mark.c index f3a32ea15b49..e9191b416434 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -105,8 +105,8 @@ static DECLARE_WORK(connector_reaper_work, fsnotify_connector_destroy_workfn); void fsnotify_get_mark(struct fsnotify_mark *mark) { - WARN_ON_ONCE(!atomic_read(&mark->refcnt)); - atomic_inc(&mark->refcnt); + WARN_ON_ONCE(!refcount_read(&mark->refcnt)); + refcount_inc(&mark->refcnt); } static void __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn) @@ -201,7 +201,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) /* Catch marks that were actually never attached to object */ if (!mark->connector) { - if (atomic_dec_and_test(&mark->refcnt)) + if (refcount_dec_and_test(&mark->refcnt)) fsnotify_final_mark_destroy(mark); return; } @@ -210,7 +210,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) * We have to be careful so that traversals of obj_list under lock can * safely grab mark reference. */ - if (!atomic_dec_and_lock(&mark->refcnt, &mark->connector->lock)) + if (!refcount_dec_and_lock(&mark->refcnt, &mark->connector->lock)) return; conn = mark->connector; @@ -258,7 +258,7 @@ static bool fsnotify_get_mark_safe(struct fsnotify_mark *mark) if (!mark) return true; - if (atomic_inc_not_zero(&mark->refcnt)) { + if (refcount_inc_not_zero(&mark->refcnt)) { spin_lock(&mark->lock); if (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) { /* mark is attached, group is still alive then */ @@ -335,7 +335,7 @@ void fsnotify_detach_mark(struct fsnotify_mark *mark) WARN_ON_ONCE(!mutex_is_locked(&group->mark_mutex)); WARN_ON_ONCE(!srcu_read_lock_held(&fsnotify_mark_srcu) && - atomic_read(&mark->refcnt) < 1 + + refcount_read(&mark->refcnt) < 1 + !!(mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)); spin_lock(&mark->lock); @@ -737,7 +737,7 @@ void fsnotify_init_mark(struct fsnotify_mark *mark, { memset(mark, 0, sizeof(*mark)); spin_lock_init(&mark->lock); - atomic_set(&mark->refcnt, 1); + refcount_set(&mark->refcnt, 1); fsnotify_get_group(group); mark->group = group; } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 744e2b9969fc..9bcb43953f4e 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -242,7 +242,7 @@ struct fsnotify_mark { __u32 mask; /* We hold one for presence in g_list. Also one ref for each 'thing' * in kernel that found and may be using this mark. */ - atomic_t refcnt; + refcount_t refcnt; /* Group this mark is for. Set on mark creation, stable until last ref * is dropped */ struct fsnotify_group *group; diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 011d46e5f73f..45ec960ad536 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -1007,7 +1007,7 @@ static void audit_tree_freeing_mark(struct fsnotify_mark *entry, struct fsnotify * We are guaranteed to have at least one reference to the mark from * either the inode or the caller of fsnotify_destroy_mark(). */ - BUG_ON(atomic_read(&entry->refcnt) < 1); + BUG_ON(refcount_read(&entry->refcnt) < 1); } static const struct fsnotify_ops audit_tree_ops = { -- cgit v1.2.3-59-g8ed1b From cb91775711b2f3f7adea8d33aa83104baf75ee07 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 19 Oct 2017 16:47:49 +0200 Subject: isofs: use unsigned char types consistently Based on the discussion about the signed character field for the year, I went through all fields in the iso9660 and rockridge standards to see whether they should used signed or unsigned characters. Only a single 8-bit value is defined as signed per 'section 7.1.2': the timezone offset in a timestamp, this has always been handled correctly through explicit sign-extension. All others are either '7.1.1 8-bit unsigned numerical values' or composite fields. I also read the linux source code and came to the same conclusion, also I could not find any other part of the implementation that actually behaves differently for signed or unsigned values. Since it is still ambigous to use plain 'char' in interface definitions, I'm changing all fields representing numbers and reserved bytes to the unambiguous '__u8'. Fields that hold actual strings are left as 'char' arrays. I built the code with '-Wpointer-sign -Wsign-compare' to see if anything got left out, but couldn't find anything wrong with the remaining warnings. This patch should not change runtime behavior and does not need to be backported. Signed-off-by: Arnd Bergmann Signed-off-by: Jan Kara --- fs/isofs/isofs.h | 20 +++--- fs/isofs/rock.h | 62 ++++++++--------- include/uapi/linux/iso_fs.h | 162 ++++++++++++++++++++++---------------------- 3 files changed, 122 insertions(+), 122 deletions(-) (limited to 'include') diff --git a/fs/isofs/isofs.h b/fs/isofs/isofs.h index bd4047585431..c882f207dd5c 100644 --- a/fs/isofs/isofs.h +++ b/fs/isofs/isofs.h @@ -72,36 +72,36 @@ static inline struct iso_inode_info *ISOFS_I(struct inode *inode) return container_of(inode, struct iso_inode_info, vfs_inode); } -static inline int isonum_711(char *p) +static inline int isonum_711(u8 *p) { - return *(u8 *)p; + return *p; } -static inline int isonum_712(char *p) +static inline int isonum_712(s8 *p) { - return *(s8 *)p; + return *p; } -static inline unsigned int isonum_721(char *p) +static inline unsigned int isonum_721(u8 *p) { return get_unaligned_le16(p); } -static inline unsigned int isonum_722(char *p) +static inline unsigned int isonum_722(u8 *p) { return get_unaligned_be16(p); } -static inline unsigned int isonum_723(char *p) +static inline unsigned int isonum_723(u8 *p) { /* Ignore bigendian datum due to broken mastering programs */ return get_unaligned_le16(p); } -static inline unsigned int isonum_731(char *p) +static inline unsigned int isonum_731(u8 *p) { return get_unaligned_le32(p); } -static inline unsigned int isonum_732(char *p) +static inline unsigned int isonum_732(u8 *p) { return get_unaligned_be32(p); } -static inline unsigned int isonum_733(char *p) +static inline unsigned int isonum_733(u8 *p) { /* Ignore bigendian datum due to broken mastering programs */ return get_unaligned_le32(p); diff --git a/fs/isofs/rock.h b/fs/isofs/rock.h index f835976ce033..8780d67c6ca5 100644 --- a/fs/isofs/rock.h +++ b/fs/isofs/rock.h @@ -6,62 +6,62 @@ */ struct SU_SP_s { - unsigned char magic[2]; - unsigned char skip; + __u8 magic[2]; + __u8 skip; } __attribute__ ((packed)); struct SU_CE_s { - char extent[8]; - char offset[8]; - char size[8]; + __u8 extent[8]; + __u8 offset[8]; + __u8 size[8]; }; struct SU_ER_s { - unsigned char len_id; - unsigned char len_des; - unsigned char len_src; - unsigned char ext_ver; - char data[0]; + __u8 len_id; + __u8 len_des; + __u8 len_src; + __u8 ext_ver; + __u8 data[0]; } __attribute__ ((packed)); struct RR_RR_s { - char flags[1]; + __u8 flags[1]; } __attribute__ ((packed)); struct RR_PX_s { - char mode[8]; - char n_links[8]; - char uid[8]; - char gid[8]; + __u8 mode[8]; + __u8 n_links[8]; + __u8 uid[8]; + __u8 gid[8]; }; struct RR_PN_s { - char dev_high[8]; - char dev_low[8]; + __u8 dev_high[8]; + __u8 dev_low[8]; }; struct SL_component { - unsigned char flags; - unsigned char len; - char text[0]; + __u8 flags; + __u8 len; + __u8 text[0]; } __attribute__ ((packed)); struct RR_SL_s { - unsigned char flags; + __u8 flags; struct SL_component link; } __attribute__ ((packed)); struct RR_NM_s { - unsigned char flags; + __u8 flags; char name[0]; } __attribute__ ((packed)); struct RR_CL_s { - char location[8]; + __u8 location[8]; }; struct RR_PL_s { - char location[8]; + __u8 location[8]; }; struct stamp { @@ -69,15 +69,15 @@ struct stamp { } __attribute__ ((packed)); struct RR_TF_s { - char flags; + __u8 flags; struct stamp times[0]; /* Variable number of these beasts */ } __attribute__ ((packed)); /* Linux-specific extension for transparent decompression */ struct RR_ZF_s { - char algorithm[2]; - char parms[2]; - char real_size[8]; + __u8 algorithm[2]; + __u8 parms[2]; + __u8 real_size[8]; }; /* @@ -93,9 +93,9 @@ struct RR_ZF_s { #define TF_LONG_FORM 128 struct rock_ridge { - char signature[2]; - unsigned char len; - unsigned char version; + __u8 signature[2]; + __u8 len; + __u8 version; union { struct SU_SP_s SP; struct SU_CE_s CE; diff --git a/include/uapi/linux/iso_fs.h b/include/uapi/linux/iso_fs.h index 4688ac4284e2..07c4c6405b3c 100644 --- a/include/uapi/linux/iso_fs.h +++ b/include/uapi/linux/iso_fs.h @@ -12,10 +12,10 @@ #define ISODCL(from, to) (to - from + 1) struct iso_volume_descriptor { - char type[ISODCL(1,1)]; /* 711 */ + __u8 type[ISODCL(1,1)]; /* 711 */ char id[ISODCL(2,6)]; - char version[ISODCL(7,7)]; - char data[ISODCL(8,2048)]; + __u8 version[ISODCL(7,7)]; + __u8 data[ISODCL(8,2048)]; }; /* volume descriptor types */ @@ -26,24 +26,24 @@ struct iso_volume_descriptor { #define ISO_STANDARD_ID "CD001" struct iso_primary_descriptor { - char type [ISODCL ( 1, 1)]; /* 711 */ + __u8 type [ISODCL ( 1, 1)]; /* 711 */ char id [ISODCL ( 2, 6)]; - char version [ISODCL ( 7, 7)]; /* 711 */ - char unused1 [ISODCL ( 8, 8)]; + __u8 version [ISODCL ( 7, 7)]; /* 711 */ + __u8 unused1 [ISODCL ( 8, 8)]; char system_id [ISODCL ( 9, 40)]; /* achars */ char volume_id [ISODCL ( 41, 72)]; /* dchars */ - char unused2 [ISODCL ( 73, 80)]; - char volume_space_size [ISODCL ( 81, 88)]; /* 733 */ - char unused3 [ISODCL ( 89, 120)]; - char volume_set_size [ISODCL (121, 124)]; /* 723 */ - char volume_sequence_number [ISODCL (125, 128)]; /* 723 */ - char logical_block_size [ISODCL (129, 132)]; /* 723 */ - char path_table_size [ISODCL (133, 140)]; /* 733 */ - char type_l_path_table [ISODCL (141, 144)]; /* 731 */ - char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ - char type_m_path_table [ISODCL (149, 152)]; /* 732 */ - char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ - char root_directory_record [ISODCL (157, 190)]; /* 9.1 */ + __u8 unused2 [ISODCL ( 73, 80)]; + __u8 volume_space_size [ISODCL ( 81, 88)]; /* 733 */ + __u8 unused3 [ISODCL ( 89, 120)]; + __u8 volume_set_size [ISODCL (121, 124)]; /* 723 */ + __u8 volume_sequence_number [ISODCL (125, 128)]; /* 723 */ + __u8 logical_block_size [ISODCL (129, 132)]; /* 723 */ + __u8 path_table_size [ISODCL (133, 140)]; /* 733 */ + __u8 type_l_path_table [ISODCL (141, 144)]; /* 731 */ + __u8 opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ + __u8 type_m_path_table [ISODCL (149, 152)]; /* 732 */ + __u8 opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ + __u8 root_directory_record [ISODCL (157, 190)]; /* 9.1 */ char volume_set_id [ISODCL (191, 318)]; /* dchars */ char publisher_id [ISODCL (319, 446)]; /* achars */ char preparer_id [ISODCL (447, 574)]; /* achars */ @@ -51,36 +51,36 @@ struct iso_primary_descriptor { char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */ - char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ - char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ - char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ - char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ - char file_structure_version [ISODCL (882, 882)]; /* 711 */ - char unused4 [ISODCL (883, 883)]; - char application_data [ISODCL (884, 1395)]; - char unused5 [ISODCL (1396, 2048)]; + __u8 creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ + __u8 modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ + __u8 expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ + __u8 effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ + __u8 file_structure_version [ISODCL (882, 882)]; /* 711 */ + __u8 unused4 [ISODCL (883, 883)]; + __u8 application_data [ISODCL (884, 1395)]; + __u8 unused5 [ISODCL (1396, 2048)]; }; /* Almost the same as the primary descriptor but two fields are specified */ struct iso_supplementary_descriptor { - char type [ISODCL ( 1, 1)]; /* 711 */ + __u8 type [ISODCL ( 1, 1)]; /* 711 */ char id [ISODCL ( 2, 6)]; - char version [ISODCL ( 7, 7)]; /* 711 */ - char flags [ISODCL ( 8, 8)]; /* 853 */ + __u8 version [ISODCL ( 7, 7)]; /* 711 */ + __u8 flags [ISODCL ( 8, 8)]; /* 853 */ char system_id [ISODCL ( 9, 40)]; /* achars */ char volume_id [ISODCL ( 41, 72)]; /* dchars */ - char unused2 [ISODCL ( 73, 80)]; - char volume_space_size [ISODCL ( 81, 88)]; /* 733 */ - char escape [ISODCL ( 89, 120)]; /* 856 */ - char volume_set_size [ISODCL (121, 124)]; /* 723 */ - char volume_sequence_number [ISODCL (125, 128)]; /* 723 */ - char logical_block_size [ISODCL (129, 132)]; /* 723 */ - char path_table_size [ISODCL (133, 140)]; /* 733 */ - char type_l_path_table [ISODCL (141, 144)]; /* 731 */ - char opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ - char type_m_path_table [ISODCL (149, 152)]; /* 732 */ - char opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ - char root_directory_record [ISODCL (157, 190)]; /* 9.1 */ + __u8 unused2 [ISODCL ( 73, 80)]; + __u8 volume_space_size [ISODCL ( 81, 88)]; /* 733 */ + __u8 escape [ISODCL ( 89, 120)]; /* 856 */ + __u8 volume_set_size [ISODCL (121, 124)]; /* 723 */ + __u8 volume_sequence_number [ISODCL (125, 128)]; /* 723 */ + __u8 logical_block_size [ISODCL (129, 132)]; /* 723 */ + __u8 path_table_size [ISODCL (133, 140)]; /* 733 */ + __u8 type_l_path_table [ISODCL (141, 144)]; /* 731 */ + __u8 opt_type_l_path_table [ISODCL (145, 148)]; /* 731 */ + __u8 type_m_path_table [ISODCL (149, 152)]; /* 732 */ + __u8 opt_type_m_path_table [ISODCL (153, 156)]; /* 732 */ + __u8 root_directory_record [ISODCL (157, 190)]; /* 9.1 */ char volume_set_id [ISODCL (191, 318)]; /* dchars */ char publisher_id [ISODCL (319, 446)]; /* achars */ char preparer_id [ISODCL (447, 574)]; /* achars */ @@ -88,54 +88,54 @@ struct iso_supplementary_descriptor { char copyright_file_id [ISODCL (703, 739)]; /* 7.5 dchars */ char abstract_file_id [ISODCL (740, 776)]; /* 7.5 dchars */ char bibliographic_file_id [ISODCL (777, 813)]; /* 7.5 dchars */ - char creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ - char modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ - char expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ - char effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ - char file_structure_version [ISODCL (882, 882)]; /* 711 */ - char unused4 [ISODCL (883, 883)]; - char application_data [ISODCL (884, 1395)]; - char unused5 [ISODCL (1396, 2048)]; + __u8 creation_date [ISODCL (814, 830)]; /* 8.4.26.1 */ + __u8 modification_date [ISODCL (831, 847)]; /* 8.4.26.1 */ + __u8 expiration_date [ISODCL (848, 864)]; /* 8.4.26.1 */ + __u8 effective_date [ISODCL (865, 881)]; /* 8.4.26.1 */ + __u8 file_structure_version [ISODCL (882, 882)]; /* 711 */ + __u8 unused4 [ISODCL (883, 883)]; + __u8 application_data [ISODCL (884, 1395)]; + __u8 unused5 [ISODCL (1396, 2048)]; }; #define HS_STANDARD_ID "CDROM" struct hs_volume_descriptor { - char foo [ISODCL ( 1, 8)]; /* 733 */ - char type [ISODCL ( 9, 9)]; /* 711 */ + __u8 foo [ISODCL ( 1, 8)]; /* 733 */ + __u8 type [ISODCL ( 9, 9)]; /* 711 */ char id [ISODCL ( 10, 14)]; - char version [ISODCL ( 15, 15)]; /* 711 */ - char data[ISODCL(16,2048)]; + __u8 version [ISODCL ( 15, 15)]; /* 711 */ + __u8 data[ISODCL(16,2048)]; }; struct hs_primary_descriptor { - char foo [ISODCL ( 1, 8)]; /* 733 */ - char type [ISODCL ( 9, 9)]; /* 711 */ - char id [ISODCL ( 10, 14)]; - char version [ISODCL ( 15, 15)]; /* 711 */ - char unused1 [ISODCL ( 16, 16)]; /* 711 */ + __u8 foo [ISODCL ( 1, 8)]; /* 733 */ + __u8 type [ISODCL ( 9, 9)]; /* 711 */ + __u8 id [ISODCL ( 10, 14)]; + __u8 version [ISODCL ( 15, 15)]; /* 711 */ + __u8 unused1 [ISODCL ( 16, 16)]; /* 711 */ char system_id [ISODCL ( 17, 48)]; /* achars */ char volume_id [ISODCL ( 49, 80)]; /* dchars */ - char unused2 [ISODCL ( 81, 88)]; /* 733 */ - char volume_space_size [ISODCL ( 89, 96)]; /* 733 */ - char unused3 [ISODCL ( 97, 128)]; /* 733 */ - char volume_set_size [ISODCL (129, 132)]; /* 723 */ - char volume_sequence_number [ISODCL (133, 136)]; /* 723 */ - char logical_block_size [ISODCL (137, 140)]; /* 723 */ - char path_table_size [ISODCL (141, 148)]; /* 733 */ - char type_l_path_table [ISODCL (149, 152)]; /* 731 */ - char unused4 [ISODCL (153, 180)]; /* 733 */ - char root_directory_record [ISODCL (181, 214)]; /* 9.1 */ + __u8 unused2 [ISODCL ( 81, 88)]; /* 733 */ + __u8 volume_space_size [ISODCL ( 89, 96)]; /* 733 */ + __u8 unused3 [ISODCL ( 97, 128)]; /* 733 */ + __u8 volume_set_size [ISODCL (129, 132)]; /* 723 */ + __u8 volume_sequence_number [ISODCL (133, 136)]; /* 723 */ + __u8 logical_block_size [ISODCL (137, 140)]; /* 723 */ + __u8 path_table_size [ISODCL (141, 148)]; /* 733 */ + __u8 type_l_path_table [ISODCL (149, 152)]; /* 731 */ + __u8 unused4 [ISODCL (153, 180)]; /* 733 */ + __u8 root_directory_record [ISODCL (181, 214)]; /* 9.1 */ }; /* We use this to help us look up the parent inode numbers. */ struct iso_path_table{ - unsigned char name_len[2]; /* 721 */ - char extent[4]; /* 731 */ - char parent[2]; /* 721 */ + __u8 name_len[2]; /* 721 */ + __u8 extent[4]; /* 731 */ + __u8 parent[2]; /* 721 */ char name[0]; } __attribute__((packed)); @@ -143,16 +143,16 @@ struct iso_path_table{ there is an extra reserved byte after the flags */ struct iso_directory_record { - char length [ISODCL (1, 1)]; /* 711 */ - char ext_attr_length [ISODCL (2, 2)]; /* 711 */ - char extent [ISODCL (3, 10)]; /* 733 */ - char size [ISODCL (11, 18)]; /* 733 */ - char date [ISODCL (19, 25)]; /* 7 by 711 */ - char flags [ISODCL (26, 26)]; - char file_unit_size [ISODCL (27, 27)]; /* 711 */ - char interleave [ISODCL (28, 28)]; /* 711 */ - char volume_sequence_number [ISODCL (29, 32)]; /* 723 */ - unsigned char name_len [ISODCL (33, 33)]; /* 711 */ + __u8 length [ISODCL (1, 1)]; /* 711 */ + __u8 ext_attr_length [ISODCL (2, 2)]; /* 711 */ + __u8 extent [ISODCL (3, 10)]; /* 733 */ + __u8 size [ISODCL (11, 18)]; /* 733 */ + __u8 date [ISODCL (19, 25)]; /* 7 by 711 */ + __u8 flags [ISODCL (26, 26)]; + __u8 file_unit_size [ISODCL (27, 27)]; /* 711 */ + __u8 interleave [ISODCL (28, 28)]; /* 711 */ + __u8 volume_sequence_number [ISODCL (29, 32)]; /* 723 */ + __u8 name_len [ISODCL (33, 33)]; /* 711 */ char name [0]; } __attribute__((packed)); -- cgit v1.2.3-59-g8ed1b From 384c181e3780ddc45e70483e29d84495b484730d Mon Sep 17 00:00:00 2001 From: Amritha Nambiar Date: Fri, 27 Oct 2017 02:35:34 -0700 Subject: net: sched: Identify hardware traffic classes using classid This patch offloads the classid to hardware and uses the classid reserved in the range :ffe0 - :ffef to identify hardware traffic classes reported via dev->num_tc. tcf_result structure contains the class ID of the class to which the packet belongs and is offloaded to hardware via flower filter. A new helper function is introduced to represent HW traffic classes 0 through 15 using the reserved classid values :ffe0 - :ffef. Signed-off-by: Amritha Nambiar Acked-by: Shannon Nelson Tested-by: Andrew Bowers Signed-off-by: Jeff Kirsher --- include/net/pkt_cls.h | 1 + include/net/sch_generic.h | 7 +++++++ net/sched/cls_flower.c | 2 ++ 3 files changed, 10 insertions(+) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index bf73e1675519..37c5ef766655 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -666,6 +666,7 @@ struct tc_cls_flower_offload { struct fl_flow_key *mask; struct fl_flow_key *key; struct tcf_exts *exts; + u32 classid; }; enum tc_matchall_command { diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 07c179dab478..c23e938f5b19 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -411,6 +411,13 @@ qdisc_class_find(const struct Qdisc_class_hash *hash, u32 id) return NULL; } +static inline int tc_classid_to_hwtc(struct net_device *dev, u32 classid) +{ + u32 hwtc = TC_H_MIN(classid) - TC_H_MIN_PRIORITY; + + return (hwtc < netdev_get_num_tc(dev)) ? hwtc : -EINVAL; +} + int qdisc_class_hash_init(struct Qdisc_class_hash *); void qdisc_class_hash_insert(struct Qdisc_class_hash *, struct Qdisc_class_common *); diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 35cb6d684e44..c99fa9e5be46 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -241,6 +241,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, cls_flower.mask = mask; cls_flower.key = &f->mkey; cls_flower.exts = &f->exts; + cls_flower.classid = f->res.classid; err = tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER, &cls_flower, skip_sw); @@ -266,6 +267,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) cls_flower.command = TC_CLSFLOWER_STATS; cls_flower.cookie = (unsigned long) f; cls_flower.exts = &f->exts; + cls_flower.classid = f->res.classid; tc_setup_cb_call(block, &f->exts, TC_SETUP_CLSFLOWER, &cls_flower, false); -- cgit v1.2.3-59-g8ed1b From 9ca4653121329595443df4e7a458154e8f745edf Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 17 Aug 2017 11:28:21 -0400 Subject: media: v4l: fwnode: Support generic parsing of graph endpoints in a device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two functions for parsing devices graph endpoints: v4l2_async_notifier_parse_fwnode_endpoints and v4l2_async_notifier_parse_fwnode_endpoints_by_port. The former iterates over all endpoints whereas the latter only iterates over the endpoints in a given port. The former is mostly useful for existing drivers that currently implement the iteration over all the endpoints themselves whereas the latter is especially intended for devices with both sinks and sources: async sub-devices for external devices connected to the device's sources will have already been set up, or the external sub-devices are part of the master device. Depends-on: ("device property: preserve usecount for node passed to of_fwnode_graph_get_port_parent()") Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Acked-by: Niklas Söderlund Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 31 ++++++ drivers/media/v4l2-core/v4l2-fwnode.c | 196 ++++++++++++++++++++++++++++++++++ include/media/v4l2-async.h | 24 ++++- include/media/v4l2-fwnode.h | 118 ++++++++++++++++++++ 4 files changed, 367 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 8b84fea50c2a..46aebfc75e43 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -22,6 +22,7 @@ #include #include +#include #include static bool match_i2c(struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) @@ -237,6 +238,36 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) } EXPORT_SYMBOL(v4l2_async_notifier_unregister); +void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier) +{ + unsigned int i; + + if (!notifier->max_subdevs) + return; + + for (i = 0; i < notifier->num_subdevs; i++) { + struct v4l2_async_subdev *asd = notifier->subdevs[i]; + + switch (asd->match_type) { + case V4L2_ASYNC_MATCH_FWNODE: + fwnode_handle_put(asd->match.fwnode.fwnode); + break; + default: + WARN_ON_ONCE(true); + break; + } + + kfree(asd); + } + + notifier->max_subdevs = 0; + notifier->num_subdevs = 0; + + kvfree(notifier->subdevs); + notifier->subdevs = NULL; +} +EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup); + int v4l2_async_register_subdev(struct v4l2_subdev *sd) { struct v4l2_async_notifier *notifier; diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 40b2fbfe8865..df0695b7bbcc 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -19,6 +19,7 @@ */ #include #include +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include #include enum v4l2_fwnode_bus_type { @@ -388,6 +390,200 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) } EXPORT_SYMBOL_GPL(v4l2_fwnode_put_link); +static int v4l2_async_notifier_realloc(struct v4l2_async_notifier *notifier, + unsigned int max_subdevs) +{ + struct v4l2_async_subdev **subdevs; + + if (max_subdevs <= notifier->max_subdevs) + return 0; + + subdevs = kvmalloc_array( + max_subdevs, sizeof(*notifier->subdevs), + GFP_KERNEL | __GFP_ZERO); + if (!subdevs) + return -ENOMEM; + + if (notifier->subdevs) { + memcpy(subdevs, notifier->subdevs, + sizeof(*subdevs) * notifier->num_subdevs); + + kvfree(notifier->subdevs); + } + + notifier->subdevs = subdevs; + notifier->max_subdevs = max_subdevs; + + return 0; +} + +static int v4l2_async_notifier_fwnode_parse_endpoint( + struct device *dev, struct v4l2_async_notifier *notifier, + struct fwnode_handle *endpoint, unsigned int asd_struct_size, + int (*parse_endpoint)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd)) +{ + struct v4l2_async_subdev *asd; + struct v4l2_fwnode_endpoint *vep; + int ret = 0; + + asd = kzalloc(asd_struct_size, GFP_KERNEL); + if (!asd) + return -ENOMEM; + + asd->match_type = V4L2_ASYNC_MATCH_FWNODE; + asd->match.fwnode.fwnode = + fwnode_graph_get_remote_port_parent(endpoint); + if (!asd->match.fwnode.fwnode) { + dev_warn(dev, "bad remote port parent\n"); + ret = -EINVAL; + goto out_err; + } + + vep = v4l2_fwnode_endpoint_alloc_parse(endpoint); + if (IS_ERR(vep)) { + ret = PTR_ERR(vep); + dev_warn(dev, "unable to parse V4L2 fwnode endpoint (%d)\n", + ret); + goto out_err; + } + + ret = parse_endpoint ? parse_endpoint(dev, vep, asd) : 0; + if (ret == -ENOTCONN) + dev_dbg(dev, "ignoring port@%u/endpoint@%u\n", vep->base.port, + vep->base.id); + else if (ret < 0) + dev_warn(dev, + "driver could not parse port@%u/endpoint@%u (%d)\n", + vep->base.port, vep->base.id, ret); + v4l2_fwnode_endpoint_free(vep); + if (ret < 0) + goto out_err; + + notifier->subdevs[notifier->num_subdevs] = asd; + notifier->num_subdevs++; + + return 0; + +out_err: + fwnode_handle_put(asd->match.fwnode.fwnode); + kfree(asd); + + return ret == -ENOTCONN ? 0 : ret; +} + +static int __v4l2_async_notifier_parse_fwnode_endpoints( + struct device *dev, struct v4l2_async_notifier *notifier, + size_t asd_struct_size, unsigned int port, bool has_port, + int (*parse_endpoint)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd)) +{ + struct fwnode_handle *fwnode; + unsigned int max_subdevs = notifier->max_subdevs; + int ret; + + if (WARN_ON(asd_struct_size < sizeof(struct v4l2_async_subdev))) + return -EINVAL; + + for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint( + dev_fwnode(dev), fwnode)); ) { + struct fwnode_handle *dev_fwnode; + bool is_available; + + dev_fwnode = fwnode_graph_get_port_parent(fwnode); + is_available = fwnode_device_is_available(dev_fwnode); + fwnode_handle_put(dev_fwnode); + if (!is_available) + continue; + + if (has_port) { + struct fwnode_endpoint ep; + + ret = fwnode_graph_parse_endpoint(fwnode, &ep); + if (ret) { + fwnode_handle_put(fwnode); + return ret; + } + + if (ep.port != port) + continue; + } + max_subdevs++; + } + + /* No subdevs to add? Return here. */ + if (max_subdevs == notifier->max_subdevs) + return 0; + + ret = v4l2_async_notifier_realloc(notifier, max_subdevs); + if (ret) + return ret; + + for (fwnode = NULL; (fwnode = fwnode_graph_get_next_endpoint( + dev_fwnode(dev), fwnode)); ) { + struct fwnode_handle *dev_fwnode; + bool is_available; + + dev_fwnode = fwnode_graph_get_port_parent(fwnode); + is_available = fwnode_device_is_available(dev_fwnode); + fwnode_handle_put(dev_fwnode); + + if (!fwnode_device_is_available(dev_fwnode)) + continue; + + if (WARN_ON(notifier->num_subdevs >= notifier->max_subdevs)) { + ret = -EINVAL; + break; + } + + if (has_port) { + struct fwnode_endpoint ep; + + ret = fwnode_graph_parse_endpoint(fwnode, &ep); + if (ret) + break; + + if (ep.port != port) + continue; + } + + ret = v4l2_async_notifier_fwnode_parse_endpoint( + dev, notifier, fwnode, asd_struct_size, parse_endpoint); + if (ret < 0) + break; + } + + fwnode_handle_put(fwnode); + + return ret; +} + +int v4l2_async_notifier_parse_fwnode_endpoints( + struct device *dev, struct v4l2_async_notifier *notifier, + size_t asd_struct_size, + int (*parse_endpoint)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd)) +{ + return __v4l2_async_notifier_parse_fwnode_endpoints( + dev, notifier, asd_struct_size, 0, false, parse_endpoint); +} +EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints); + +int v4l2_async_notifier_parse_fwnode_endpoints_by_port( + struct device *dev, struct v4l2_async_notifier *notifier, + size_t asd_struct_size, unsigned int port, + int (*parse_endpoint)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd)) +{ + return __v4l2_async_notifier_parse_fwnode_endpoints( + dev, notifier, asd_struct_size, port, true, parse_endpoint); +} +EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_endpoints_by_port); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Sylwester Nawrocki "); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index c69d8c8a66d0..329aeebd1a80 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -18,7 +18,6 @@ struct device; struct device_node; struct v4l2_device; struct v4l2_subdev; -struct v4l2_async_notifier; /* A random max subdevice number, used to allocate an array on stack */ #define V4L2_MAX_SUBDEVS 128U @@ -50,6 +49,10 @@ enum v4l2_async_match_type { * @match: union of per-bus type matching data sets * @list: used to link struct v4l2_async_subdev objects, waiting to be * probed, to a notifier->waiting list + * + * When this struct is used as a member in a driver specific struct, + * the driver specific struct shall contain the &struct + * v4l2_async_subdev as its first member. */ struct v4l2_async_subdev { enum v4l2_async_match_type match_type; @@ -78,7 +81,8 @@ struct v4l2_async_subdev { /** * struct v4l2_async_notifier - v4l2_device notifier data * - * @num_subdevs: number of subdevices + * @num_subdevs: number of subdevices used in the subdevs array + * @max_subdevs: number of subdevices allocated in the subdevs array * @subdevs: array of pointers to subdevice descriptors * @v4l2_dev: pointer to struct v4l2_device * @waiting: list of struct v4l2_async_subdev, waiting for their drivers @@ -90,6 +94,7 @@ struct v4l2_async_subdev { */ struct v4l2_async_notifier { unsigned int num_subdevs; + unsigned int max_subdevs; struct v4l2_async_subdev **subdevs; struct v4l2_device *v4l2_dev; struct list_head waiting; @@ -120,6 +125,21 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, */ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier); +/** + * v4l2_async_notifier_cleanup - clean up notifier resources + * @notifier: the notifier the resources of which are to be cleaned up + * + * Release memory resources related to a notifier, including the async + * sub-devices allocated for the purposes of the notifier but not the notifier + * itself. The user is responsible for calling this function to clean up the + * notifier after calling @v4l2_async_notifier_parse_fwnode_endpoints. + * + * There is no harm from calling v4l2_async_notifier_cleanup in other + * cases as long as its memory has been zeroed after it has been + * allocated. + */ +void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier); + /** * v4l2_async_register_subdev - registers a sub-device to the asynchronous * subdevice framework diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index 7adec9851d9e..ac605af9b877 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -25,6 +25,8 @@ #include struct fwnode_handle; +struct v4l2_async_notifier; +struct v4l2_async_subdev; #define V4L2_FWNODE_CSI2_MAX_DATA_LANES 4 @@ -122,4 +124,120 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, struct v4l2_fwnode_link *link); void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link); +/** + * v4l2_async_notifier_parse_fwnode_endpoints - Parse V4L2 fwnode endpoints in a + * device node + * @dev: the device the endpoints of which are to be parsed + * @notifier: notifier for @dev + * @asd_struct_size: size of the driver's async sub-device struct, including + * sizeof(struct v4l2_async_subdev). The &struct + * v4l2_async_subdev shall be the first member of + * the driver's async sub-device struct, i.e. both + * begin at the same memory address. + * @parse_endpoint: Driver's callback function called on each V4L2 fwnode + * endpoint. Optional. + * Return: %0 on success + * %-ENOTCONN if the endpoint is to be skipped but this + * should not be considered as an error + * %-EINVAL if the endpoint configuration is invalid + * + * Parse the fwnode endpoints of the @dev device and populate the async sub- + * devices array of the notifier. The @parse_endpoint callback function is + * called for each endpoint with the corresponding async sub-device pointer to + * let the caller initialize the driver-specific part of the async sub-device + * structure. + * + * The notifier memory shall be zeroed before this function is called on the + * notifier. + * + * This function may not be called on a registered notifier and may be called on + * a notifier only once. + * + * Do not change the notifier's subdevs array, take references to the subdevs + * array itself or change the notifier's num_subdevs field. This is because this + * function allocates and reallocates the subdevs array based on parsing + * endpoints. + * + * The &struct v4l2_fwnode_endpoint passed to the callback function + * @parse_endpoint is released once the function is finished. If there is a need + * to retain that configuration, the user needs to allocate memory for it. + * + * Any notifier populated using this function must be released with a call to + * v4l2_async_notifier_cleanup() after it has been unregistered and the async + * sub-devices are no longer in use, even if the function returned an error. + * + * Return: %0 on success, including when no async sub-devices are found + * %-ENOMEM if memory allocation failed + * %-EINVAL if graph or endpoint parsing failed + * Other error codes as returned by @parse_endpoint + */ +int v4l2_async_notifier_parse_fwnode_endpoints( + struct device *dev, struct v4l2_async_notifier *notifier, + size_t asd_struct_size, + int (*parse_endpoint)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd)); + +/** + * v4l2_async_notifier_parse_fwnode_endpoints_by_port - Parse V4L2 fwnode + * endpoints of a port in a + * device node + * @dev: the device the endpoints of which are to be parsed + * @notifier: notifier for @dev + * @asd_struct_size: size of the driver's async sub-device struct, including + * sizeof(struct v4l2_async_subdev). The &struct + * v4l2_async_subdev shall be the first member of + * the driver's async sub-device struct, i.e. both + * begin at the same memory address. + * @port: port number where endpoints are to be parsed + * @parse_endpoint: Driver's callback function called on each V4L2 fwnode + * endpoint. Optional. + * Return: %0 on success + * %-ENOTCONN if the endpoint is to be skipped but this + * should not be considered as an error + * %-EINVAL if the endpoint configuration is invalid + * + * This function is just like v4l2_async_notifier_parse_fwnode_endpoints() with + * the exception that it only parses endpoints in a given port. This is useful + * on devices that have both sinks and sources: the async sub-devices connected + * to sources have already been configured by another driver (on capture + * devices). In this case the driver must know which ports to parse. + * + * Parse the fwnode endpoints of the @dev device on a given @port and populate + * the async sub-devices array of the notifier. The @parse_endpoint callback + * function is called for each endpoint with the corresponding async sub-device + * pointer to let the caller initialize the driver-specific part of the async + * sub-device structure. + * + * The notifier memory shall be zeroed before this function is called on the + * notifier the first time. + * + * This function may not be called on a registered notifier and may be called on + * a notifier only once per port. + * + * Do not change the notifier's subdevs array, take references to the subdevs + * array itself or change the notifier's num_subdevs field. This is because this + * function allocates and reallocates the subdevs array based on parsing + * endpoints. + * + * The &struct v4l2_fwnode_endpoint passed to the callback function + * @parse_endpoint is released once the function is finished. If there is a need + * to retain that configuration, the user needs to allocate memory for it. + * + * Any notifier populated using this function must be released with a call to + * v4l2_async_notifier_cleanup() after it has been unregistered and the async + * sub-devices are no longer in use, even if the function returned an error. + * + * Return: %0 on success, including when no async sub-devices are found + * %-ENOMEM if memory allocation failed + * %-EINVAL if graph or endpoint parsing failed + * Other error codes as returned by @parse_endpoint + */ +int v4l2_async_notifier_parse_fwnode_endpoints_by_port( + struct device *dev, struct v4l2_async_notifier *notifier, + size_t asd_struct_size, unsigned int port, + int (*parse_endpoint)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd)); + #endif /* _V4L2_FWNODE_H */ -- cgit v1.2.3-59-g8ed1b From b6ee3f0dcf43dc3e8dbbe9be9c4e728c8d52f1ba Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 30 Aug 2017 13:18:04 -0400 Subject: media: v4l: async: Move async subdev notifier operations to a separate structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The async subdev notifier .bound(), .unbind() and .complete() operations are function pointers stored directly in the v4l2_async_subdev structure. As the structure isn't immutable, this creates a potential security risk as the function pointers are mutable. To fix this, move the function pointers to a new v4l2_async_subdev_operations structure that can be made const in drivers. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Niklas Söderlund Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/am437x/am437x-vpfe.c | 8 +++++-- drivers/media/platform/atmel/atmel-isc.c | 10 ++++++--- drivers/media/platform/atmel/atmel-isi.c | 10 ++++++--- drivers/media/platform/davinci/vpif_capture.c | 8 +++++-- drivers/media/platform/davinci/vpif_display.c | 8 +++++-- drivers/media/platform/exynos4-is/media-dev.c | 8 +++++-- drivers/media/platform/omap3isp/isp.c | 6 +++++- drivers/media/platform/pxa_camera.c | 8 +++++-- drivers/media/platform/qcom/camss-8x16/camss.c | 8 +++++-- drivers/media/platform/rcar-vin/rcar-core.c | 10 ++++++--- drivers/media/platform/rcar_drif.c | 10 ++++++--- drivers/media/platform/soc_camera/soc_camera.c | 14 ++++++------ drivers/media/platform/stm32/stm32-dcmi.c | 10 ++++++--- drivers/media/platform/ti-vpe/cal.c | 8 +++++-- drivers/media/platform/xilinx/xilinx-vipp.c | 8 +++++-- drivers/media/v4l2-core/v4l2-async.c | 30 ++++++++++++-------------- drivers/staging/media/imx/imx-media-dev.c | 8 +++++-- include/media/v4l2-async.h | 29 ++++++++++++++++--------- 18 files changed, 135 insertions(+), 66 deletions(-) (limited to 'include') diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c index dfcc484cab89..0997c640191d 100644 --- a/drivers/media/platform/am437x/am437x-vpfe.c +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -2417,6 +2417,11 @@ static int vpfe_async_complete(struct v4l2_async_notifier *notifier) return vpfe_probe_complete(vpfe); } +static const struct v4l2_async_notifier_operations vpfe_async_ops = { + .bound = vpfe_async_bound, + .complete = vpfe_async_complete, +}; + static struct vpfe_config * vpfe_get_pdata(struct platform_device *pdev) { @@ -2590,8 +2595,7 @@ static int vpfe_probe(struct platform_device *pdev) vpfe->notifier.subdevs = vpfe->cfg->asd; vpfe->notifier.num_subdevs = ARRAY_SIZE(vpfe->cfg->asd); - vpfe->notifier.bound = vpfe_async_bound; - vpfe->notifier.complete = vpfe_async_complete; + vpfe->notifier.ops = &vpfe_async_ops; ret = v4l2_async_notifier_register(&vpfe->v4l2_dev, &vpfe->notifier); if (ret) { diff --git a/drivers/media/platform/atmel/atmel-isc.c b/drivers/media/platform/atmel/atmel-isc.c index 30f2867c2932..13f1c1c797b0 100644 --- a/drivers/media/platform/atmel/atmel-isc.c +++ b/drivers/media/platform/atmel/atmel-isc.c @@ -1979,6 +1979,12 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier) return 0; } +static const struct v4l2_async_notifier_operations isc_async_ops = { + .bound = isc_async_bound, + .unbind = isc_async_unbind, + .complete = isc_async_complete, +}; + static void isc_subdev_cleanup(struct isc_device *isc) { struct isc_subdev_entity *subdev_entity; @@ -2203,9 +2209,7 @@ static int atmel_isc_probe(struct platform_device *pdev) list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { subdev_entity->notifier.subdevs = &subdev_entity->asd; subdev_entity->notifier.num_subdevs = 1; - subdev_entity->notifier.bound = isc_async_bound; - subdev_entity->notifier.unbind = isc_async_unbind; - subdev_entity->notifier.complete = isc_async_complete; + subdev_entity->notifier.ops = &isc_async_ops; ret = v4l2_async_notifier_register(&isc->v4l2_dev, &subdev_entity->notifier); diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c index 463c0146915e..e900995143a3 100644 --- a/drivers/media/platform/atmel/atmel-isi.c +++ b/drivers/media/platform/atmel/atmel-isi.c @@ -1103,6 +1103,12 @@ static int isi_graph_notify_bound(struct v4l2_async_notifier *notifier, return 0; } +static const struct v4l2_async_notifier_operations isi_graph_notify_ops = { + .bound = isi_graph_notify_bound, + .unbind = isi_graph_notify_unbind, + .complete = isi_graph_notify_complete, +}; + static int isi_graph_parse(struct atmel_isi *isi, struct device_node *node) { struct device_node *ep = NULL; @@ -1150,9 +1156,7 @@ static int isi_graph_init(struct atmel_isi *isi) isi->notifier.subdevs = subdevs; isi->notifier.num_subdevs = 1; - isi->notifier.bound = isi_graph_notify_bound; - isi->notifier.unbind = isi_graph_notify_unbind; - isi->notifier.complete = isi_graph_notify_complete; + isi->notifier.ops = &isi_graph_notify_ops; ret = v4l2_async_notifier_register(&isi->v4l2_dev, &isi->notifier); if (ret < 0) { diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index 0ef36cec21d1..a89367ab1e06 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1500,6 +1500,11 @@ static int vpif_async_complete(struct v4l2_async_notifier *notifier) return vpif_probe_complete(); } +static const struct v4l2_async_notifier_operations vpif_async_ops = { + .bound = vpif_async_bound, + .complete = vpif_async_complete, +}; + static struct vpif_capture_config * vpif_capture_get_pdata(struct platform_device *pdev) { @@ -1691,8 +1696,7 @@ static __init int vpif_probe(struct platform_device *pdev) } else { vpif_obj.notifier.subdevs = vpif_obj.config->asd; vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; - vpif_obj.notifier.bound = vpif_async_bound; - vpif_obj.notifier.complete = vpif_async_complete; + vpif_obj.notifier.ops = &vpif_async_ops; err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev, &vpif_obj.notifier); if (err) { diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index 56fe4e5b396e..ff2f75a328c9 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1232,6 +1232,11 @@ static int vpif_async_complete(struct v4l2_async_notifier *notifier) return vpif_probe_complete(); } +static const struct v4l2_async_notifier_operations vpif_async_ops = { + .bound = vpif_async_bound, + .complete = vpif_async_complete, +}; + /* * vpif_probe: This function creates device entries by register itself to the * V4L2 driver and initializes fields of each channel objects @@ -1313,8 +1318,7 @@ static __init int vpif_probe(struct platform_device *pdev) } else { vpif_obj.notifier.subdevs = vpif_obj.config->asd; vpif_obj.notifier.num_subdevs = vpif_obj.config->asd_sizes[0]; - vpif_obj.notifier.bound = vpif_async_bound; - vpif_obj.notifier.complete = vpif_async_complete; + vpif_obj.notifier.ops = &vpif_async_ops; err = v4l2_async_notifier_register(&vpif_obj.v4l2_dev, &vpif_obj.notifier); if (err) { diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/exynos4-is/media-dev.c index d4656d5175d7..c15596b56dc9 100644 --- a/drivers/media/platform/exynos4-is/media-dev.c +++ b/drivers/media/platform/exynos4-is/media-dev.c @@ -1405,6 +1405,11 @@ unlock: return media_device_register(&fmd->media_dev); } +static const struct v4l2_async_notifier_operations subdev_notifier_ops = { + .bound = subdev_notifier_bound, + .complete = subdev_notifier_complete, +}; + static int fimc_md_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1479,8 +1484,7 @@ static int fimc_md_probe(struct platform_device *pdev) if (fmd->num_sensors > 0) { fmd->subdev_notifier.subdevs = fmd->async_subdevs; fmd->subdev_notifier.num_subdevs = fmd->num_sensors; - fmd->subdev_notifier.bound = subdev_notifier_bound; - fmd->subdev_notifier.complete = subdev_notifier_complete; + fmd->subdev_notifier.ops = &subdev_notifier_ops; fmd->num_sensors = 0; ret = v4l2_async_notifier_register(&fmd->v4l2_dev, diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 35687c9707e0..b7ff3842afc0 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -2171,6 +2171,10 @@ static int isp_subdev_notifier_complete(struct v4l2_async_notifier *async) return media_device_register(&isp->media_dev); } +static const struct v4l2_async_notifier_operations isp_subdev_notifier_ops = { + .complete = isp_subdev_notifier_complete, +}; + /* * isp_probe - Probe ISP platform device * @pdev: Pointer to ISP platform device @@ -2341,7 +2345,7 @@ static int isp_probe(struct platform_device *pdev) if (ret < 0) goto error_register_entities; - isp->notifier.complete = isp_subdev_notifier_complete; + isp->notifier.ops = &isp_subdev_notifier_ops; ret = v4l2_async_notifier_register(&isp->v4l2_dev, &isp->notifier); if (ret) diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/pxa_camera.c index edca993c2b1f..9d3f0cb1d95a 100644 --- a/drivers/media/platform/pxa_camera.c +++ b/drivers/media/platform/pxa_camera.c @@ -2221,6 +2221,11 @@ static void pxa_camera_sensor_unbind(struct v4l2_async_notifier *notifier, mutex_unlock(&pcdev->mlock); } +static const struct v4l2_async_notifier_operations pxa_camera_sensor_ops = { + .bound = pxa_camera_sensor_bound, + .unbind = pxa_camera_sensor_unbind, +}; + /* * Driver probe, remove, suspend and resume operations */ @@ -2489,8 +2494,7 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->asds[0] = &pcdev->asd; pcdev->notifier.subdevs = pcdev->asds; pcdev->notifier.num_subdevs = 1; - pcdev->notifier.bound = pxa_camera_sensor_bound; - pcdev->notifier.unbind = pxa_camera_sensor_unbind; + pcdev->notifier.ops = &pxa_camera_sensor_ops; if (!of_have_populated_dt()) pcdev->asd.match_type = V4L2_ASYNC_MATCH_I2C; diff --git a/drivers/media/platform/qcom/camss-8x16/camss.c b/drivers/media/platform/qcom/camss-8x16/camss.c index a3760b5dd1d1..390a42c17b66 100644 --- a/drivers/media/platform/qcom/camss-8x16/camss.c +++ b/drivers/media/platform/qcom/camss-8x16/camss.c @@ -601,6 +601,11 @@ static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async) return media_device_register(&camss->media_dev); } +static const struct v4l2_async_notifier_operations camss_subdev_notifier_ops = { + .bound = camss_subdev_notifier_bound, + .complete = camss_subdev_notifier_complete, +}; + static const struct media_device_ops camss_media_ops = { .link_notify = v4l2_pipeline_link_notify, }; @@ -655,8 +660,7 @@ static int camss_probe(struct platform_device *pdev) goto err_register_entities; if (camss->notifier.num_subdevs) { - camss->notifier.bound = camss_subdev_notifier_bound; - camss->notifier.complete = camss_subdev_notifier_complete; + camss->notifier.ops = &camss_subdev_notifier_ops; ret = v4l2_async_notifier_register(&camss->v4l2_dev, &camss->notifier); diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/rcar-vin/rcar-core.c index 380288658601..108d776f3265 100644 --- a/drivers/media/platform/rcar-vin/rcar-core.c +++ b/drivers/media/platform/rcar-vin/rcar-core.c @@ -134,6 +134,12 @@ static int rvin_digital_notify_bound(struct v4l2_async_notifier *notifier, return 0; } +static const struct v4l2_async_notifier_operations rvin_digital_notify_ops = { + .bound = rvin_digital_notify_bound, + .unbind = rvin_digital_notify_unbind, + .complete = rvin_digital_notify_complete, +}; + static int rvin_digital_parse_v4l2(struct device *dev, struct v4l2_fwnode_endpoint *vep, @@ -183,9 +189,7 @@ static int rvin_digital_graph_init(struct rvin_dev *vin) vin_dbg(vin, "Found digital subdevice %pOF\n", to_of_node(vin->digital->asd.match.fwnode.fwnode)); - vin->notifier.bound = rvin_digital_notify_bound; - vin->notifier.unbind = rvin_digital_notify_unbind; - vin->notifier.complete = rvin_digital_notify_complete; + vin->notifier.ops = &rvin_digital_notify_ops; ret = v4l2_async_notifier_register(&vin->v4l2_dev, &vin->notifier); if (ret < 0) { vin_err(vin, "Notifier registration failed\n"); diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/rcar_drif.c index 2c6afd38b78a..63c94f4028a7 100644 --- a/drivers/media/platform/rcar_drif.c +++ b/drivers/media/platform/rcar_drif.c @@ -1185,6 +1185,12 @@ error: return ret; } +static const struct v4l2_async_notifier_operations rcar_drif_notify_ops = { + .bound = rcar_drif_notify_bound, + .unbind = rcar_drif_notify_unbind, + .complete = rcar_drif_notify_complete, +}; + /* Read endpoint properties */ static void rcar_drif_get_ep_properties(struct rcar_drif_sdr *sdr, struct fwnode_handle *fwnode) @@ -1347,9 +1353,7 @@ static int rcar_drif_sdr_probe(struct rcar_drif_sdr *sdr) if (ret) goto error; - sdr->notifier.bound = rcar_drif_notify_bound; - sdr->notifier.unbind = rcar_drif_notify_unbind; - sdr->notifier.complete = rcar_drif_notify_complete; + sdr->notifier.ops = &rcar_drif_notify_ops; /* Register notifier */ ret = v4l2_async_notifier_register(&sdr->v4l2_dev, &sdr->notifier); diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index 1f3c450c7a69..916ff68b73d4 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -1391,6 +1391,12 @@ static int soc_camera_async_complete(struct v4l2_async_notifier *notifier) return 0; } +static const struct v4l2_async_notifier_operations soc_camera_async_ops = { + .bound = soc_camera_async_bound, + .unbind = soc_camera_async_unbind, + .complete = soc_camera_async_complete, +}; + static int scan_async_group(struct soc_camera_host *ici, struct v4l2_async_subdev **asd, unsigned int size) { @@ -1437,9 +1443,7 @@ static int scan_async_group(struct soc_camera_host *ici, sasc->notifier.subdevs = asd; sasc->notifier.num_subdevs = size; - sasc->notifier.bound = soc_camera_async_bound; - sasc->notifier.unbind = soc_camera_async_unbind; - sasc->notifier.complete = soc_camera_async_complete; + sasc->notifier.ops = &soc_camera_async_ops; icd->sasc = sasc; icd->parent = ici->v4l2_dev.dev; @@ -1537,9 +1541,7 @@ static int soc_of_bind(struct soc_camera_host *ici, sasc->notifier.subdevs = &info->subdev; sasc->notifier.num_subdevs = 1; - sasc->notifier.bound = soc_camera_async_bound; - sasc->notifier.unbind = soc_camera_async_unbind; - sasc->notifier.complete = soc_camera_async_complete; + sasc->notifier.ops = &soc_camera_async_ops; icd->sasc = sasc; icd->parent = ici->v4l2_dev.dev; diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 35ba6f211b79..ac4c450a6c7d 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c @@ -1495,6 +1495,12 @@ static int dcmi_graph_notify_bound(struct v4l2_async_notifier *notifier, return 0; } +static const struct v4l2_async_notifier_operations dcmi_graph_notify_ops = { + .bound = dcmi_graph_notify_bound, + .unbind = dcmi_graph_notify_unbind, + .complete = dcmi_graph_notify_complete, +}; + static int dcmi_graph_parse(struct stm32_dcmi *dcmi, struct device_node *node) { struct device_node *ep = NULL; @@ -1542,9 +1548,7 @@ static int dcmi_graph_init(struct stm32_dcmi *dcmi) dcmi->notifier.subdevs = subdevs; dcmi->notifier.num_subdevs = 1; - dcmi->notifier.bound = dcmi_graph_notify_bound; - dcmi->notifier.unbind = dcmi_graph_notify_unbind; - dcmi->notifier.complete = dcmi_graph_notify_complete; + dcmi->notifier.ops = &dcmi_graph_notify_ops; ret = v4l2_async_notifier_register(&dcmi->v4l2_dev, &dcmi->notifier); if (ret < 0) { diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 42e383a48ffe..8b586c864524 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1522,6 +1522,11 @@ static int cal_async_complete(struct v4l2_async_notifier *notifier) return 0; } +static const struct v4l2_async_notifier_operations cal_async_ops = { + .bound = cal_async_bound, + .complete = cal_async_complete, +}; + static int cal_complete_ctx(struct cal_ctx *ctx) { struct video_device *vfd; @@ -1736,8 +1741,7 @@ static int of_cal_create_instance(struct cal_ctx *ctx, int inst) ctx->asd_list[0] = asd; ctx->notifier.subdevs = ctx->asd_list; ctx->notifier.num_subdevs = 1; - ctx->notifier.bound = cal_async_bound; - ctx->notifier.complete = cal_async_complete; + ctx->notifier.ops = &cal_async_ops; ret = v4l2_async_notifier_register(&ctx->v4l2_dev, &ctx->notifier); if (ret) { diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c index ebfdf334d99c..d881cf09876d 100644 --- a/drivers/media/platform/xilinx/xilinx-vipp.c +++ b/drivers/media/platform/xilinx/xilinx-vipp.c @@ -351,6 +351,11 @@ static int xvip_graph_notify_bound(struct v4l2_async_notifier *notifier, return -EINVAL; } +static const struct v4l2_async_notifier_operations xvip_graph_notify_ops = { + .bound = xvip_graph_notify_bound, + .complete = xvip_graph_notify_complete, +}; + static int xvip_graph_parse_one(struct xvip_composite_device *xdev, struct device_node *node) { @@ -548,8 +553,7 @@ static int xvip_graph_init(struct xvip_composite_device *xdev) xdev->notifier.subdevs = subdevs; xdev->notifier.num_subdevs = num_subdevs; - xdev->notifier.bound = xvip_graph_notify_bound; - xdev->notifier.complete = xvip_graph_notify_complete; + xdev->notifier.ops = &xvip_graph_notify_ops; ret = v4l2_async_notifier_register(&xdev->v4l2_dev, &xdev->notifier); if (ret < 0) { diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 46aebfc75e43..9d6fc5f25619 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -102,16 +102,16 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, { int ret; - if (notifier->bound) { - ret = notifier->bound(notifier, sd, asd); + if (notifier->ops->bound) { + ret = notifier->ops->bound(notifier, sd, asd); if (ret < 0) return ret; } ret = v4l2_device_register_subdev(notifier->v4l2_dev, sd); if (ret < 0) { - if (notifier->unbind) - notifier->unbind(notifier, sd, asd); + if (notifier->ops->unbind) + notifier->ops->unbind(notifier, sd, asd); return ret; } @@ -140,9 +140,8 @@ static void v4l2_async_notifier_unbind_all_subdevs( struct v4l2_subdev *sd, *tmp; list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) { - if (notifier->unbind) - notifier->unbind(notifier, sd, sd->asd); - + if (notifier->ops->unbind) + notifier->ops->unbind(notifier, sd, sd->asd); v4l2_async_cleanup(sd); list_move(&sd->async_list, &subdev_list); @@ -199,8 +198,8 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, } } - if (list_empty(¬ifier->waiting) && notifier->complete) { - ret = notifier->complete(notifier); + if (list_empty(¬ifier->waiting) && notifier->ops->complete) { + ret = notifier->ops->complete(notifier); if (ret) goto err_complete; } @@ -297,10 +296,10 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd) if (ret) goto err_unlock; - if (!list_empty(¬ifier->waiting) || !notifier->complete) + if (!list_empty(¬ifier->waiting) || !notifier->ops->complete) goto out_unlock; - ret = notifier->complete(notifier); + ret = notifier->ops->complete(notifier); if (ret) goto err_cleanup; @@ -316,9 +315,8 @@ out_unlock: return 0; err_cleanup: - if (notifier->unbind) - notifier->unbind(notifier, sd, sd->asd); - + if (notifier->ops->unbind) + notifier->ops->unbind(notifier, sd, sd->asd); v4l2_async_cleanup(sd); err_unlock: @@ -337,8 +335,8 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) list_add(&sd->asd->list, ¬ifier->waiting); - if (notifier->unbind) - notifier->unbind(notifier, sd, sd->asd); + if (notifier->ops->unbind) + notifier->ops->unbind(notifier, sd, sd->asd); } v4l2_async_cleanup(sd); diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c index b55e5ebba8b4..47c4c954fed5 100644 --- a/drivers/staging/media/imx/imx-media-dev.c +++ b/drivers/staging/media/imx/imx-media-dev.c @@ -440,6 +440,11 @@ unlock: return media_device_register(&imxmd->md); } +static const struct v4l2_async_notifier_operations imx_media_subdev_ops = { + .bound = imx_media_subdev_bound, + .complete = imx_media_probe_complete, +}; + /* * adds controls to a video device from an entity subdevice. * Continues upstream from the entity's sink pads. @@ -608,8 +613,7 @@ static int imx_media_probe(struct platform_device *pdev) /* prepare the async subdev notifier and register it */ imxmd->subdev_notifier.subdevs = imxmd->async_ptrs; - imxmd->subdev_notifier.bound = imx_media_subdev_bound; - imxmd->subdev_notifier.complete = imx_media_probe_complete; + imxmd->subdev_notifier.ops = &imx_media_subdev_ops; ret = v4l2_async_notifier_register(&imxmd->v4l2_dev, &imxmd->subdev_notifier); if (ret) { diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 329aeebd1a80..68606afb5ef9 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -18,6 +18,7 @@ struct device; struct device_node; struct v4l2_device; struct v4l2_subdev; +struct v4l2_async_notifier; /* A random max subdevice number, used to allocate an array on stack */ #define V4L2_MAX_SUBDEVS 128U @@ -78,9 +79,26 @@ struct v4l2_async_subdev { struct list_head list; }; +/** + * struct v4l2_async_notifier_operations - Asynchronous V4L2 notifier operations + * @bound: a subdevice driver has successfully probed one of the subdevices + * @complete: all subdevices have been probed successfully + * @unbind: a subdevice is leaving + */ +struct v4l2_async_notifier_operations { + int (*bound)(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd); + int (*complete)(struct v4l2_async_notifier *notifier); + void (*unbind)(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd); +}; + /** * struct v4l2_async_notifier - v4l2_device notifier data * + * @ops: notifier operations * @num_subdevs: number of subdevices used in the subdevs array * @max_subdevs: number of subdevices allocated in the subdevs array * @subdevs: array of pointers to subdevice descriptors @@ -88,11 +106,9 @@ struct v4l2_async_subdev { * @waiting: list of struct v4l2_async_subdev, waiting for their drivers * @done: list of struct v4l2_subdev, already probed * @list: member in a global list of notifiers - * @bound: a subdevice driver has successfully probed one of subdevices - * @complete: all subdevices have been probed successfully - * @unbind: a subdevice is leaving */ struct v4l2_async_notifier { + const struct v4l2_async_notifier_operations *ops; unsigned int num_subdevs; unsigned int max_subdevs; struct v4l2_async_subdev **subdevs; @@ -100,13 +116,6 @@ struct v4l2_async_notifier { struct list_head waiting; struct list_head done; struct list_head list; - int (*bound)(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd); - int (*complete)(struct v4l2_async_notifier *notifier); - void (*unbind)(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd); }; /** -- cgit v1.2.3-59-g8ed1b From 2cab00bb076b9f0e8442e3d72425843d2b441143 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 24 Sep 2017 20:54:31 -0400 Subject: media: v4l: async: Allow binding notifiers to sub-devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registering a notifier has required the knowledge of struct v4l2_device for the reason that sub-devices generally are registered to the v4l2_device (as well as the media device, also available through v4l2_device). This information is not available for sub-device drivers at probe time. What this patch does is that it allows registering notifiers without having v4l2_device around. Instead the sub-device pointer is stored in the notifier. Once the sub-device of the driver that registered the notifier is registered, the notifier will gain the knowledge of the v4l2_device, and the binding of async sub-devices from the sub-device driver's notifier may proceed. The complete callback of the root notifier will be called only when the v4l2_device is available and no notifier has pending sub-devices to bind. No complete callbacks are supported for sub-device notifiers. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Acked-by: Niklas Söderlund Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 212 ++++++++++++++++++++++++++++------- include/media/v4l2-async.h | 19 +++- 2 files changed, 189 insertions(+), 42 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 6265717769d2..ed539c4fd5dc 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -124,11 +124,87 @@ static struct v4l2_async_subdev *v4l2_async_find_match( return NULL; } +/* Find the sub-device notifier registered by a sub-device driver. */ +static struct v4l2_async_notifier *v4l2_async_find_subdev_notifier( + struct v4l2_subdev *sd) +{ + struct v4l2_async_notifier *n; + + list_for_each_entry(n, ¬ifier_list, list) + if (n->sd == sd) + return n; + + return NULL; +} + +/* Get v4l2_device related to the notifier if one can be found. */ +static struct v4l2_device *v4l2_async_notifier_find_v4l2_dev( + struct v4l2_async_notifier *notifier) +{ + while (notifier->parent) + notifier = notifier->parent; + + return notifier->v4l2_dev; +} + +/* + * Return true if all child sub-device notifiers are complete, false otherwise. + */ +static bool v4l2_async_notifier_can_complete( + struct v4l2_async_notifier *notifier) +{ + struct v4l2_subdev *sd; + + if (!list_empty(¬ifier->waiting)) + return false; + + list_for_each_entry(sd, ¬ifier->done, async_list) { + struct v4l2_async_notifier *subdev_notifier = + v4l2_async_find_subdev_notifier(sd); + + if (subdev_notifier && + !v4l2_async_notifier_can_complete(subdev_notifier)) + return false; + } + + return true; +} + +/* + * Complete the master notifier if possible. This is done when all async + * sub-devices have been bound; v4l2_device is also available then. + */ +static int v4l2_async_notifier_try_complete( + struct v4l2_async_notifier *notifier) +{ + /* Quick check whether there are still more sub-devices here. */ + if (!list_empty(¬ifier->waiting)) + return 0; + + /* Check the entire notifier tree; find the root notifier first. */ + while (notifier->parent) + notifier = notifier->parent; + + /* This is root if it has v4l2_dev. */ + if (!notifier->v4l2_dev) + return 0; + + /* Is everything ready? */ + if (!v4l2_async_notifier_can_complete(notifier)) + return 0; + + return v4l2_async_notifier_call_complete(notifier); +} + +static int v4l2_async_notifier_try_all_subdevs( + struct v4l2_async_notifier *notifier); + static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd, struct v4l2_async_subdev *asd) { + struct v4l2_async_notifier *subdev_notifier; int ret; ret = v4l2_device_register_subdev(v4l2_dev, sd); @@ -149,17 +225,36 @@ static int v4l2_async_match_notify(struct v4l2_async_notifier *notifier, /* Move from the global subdevice list to notifier's done */ list_move(&sd->async_list, ¬ifier->done); - return 0; + /* + * See if the sub-device has a notifier. If not, return here. + */ + subdev_notifier = v4l2_async_find_subdev_notifier(sd); + if (!subdev_notifier || subdev_notifier->parent) + return 0; + + /* + * Proceed with checking for the sub-device notifier's async + * sub-devices, and return the result. The error will be handled by the + * caller. + */ + subdev_notifier->parent = notifier; + + return v4l2_async_notifier_try_all_subdevs(subdev_notifier); } /* Test all async sub-devices in a notifier for a match. */ static int v4l2_async_notifier_try_all_subdevs( struct v4l2_async_notifier *notifier) { - struct v4l2_device *v4l2_dev = notifier->v4l2_dev; - struct v4l2_subdev *sd, *tmp; + struct v4l2_device *v4l2_dev = + v4l2_async_notifier_find_v4l2_dev(notifier); + struct v4l2_subdev *sd; + + if (!v4l2_dev) + return 0; - list_for_each_entry_safe(sd, tmp, &subdev_list, async_list) { +again: + list_for_each_entry(sd, &subdev_list, async_list) { struct v4l2_async_subdev *asd; int ret; @@ -170,6 +265,14 @@ static int v4l2_async_notifier_try_all_subdevs( ret = v4l2_async_match_notify(notifier, v4l2_dev, sd, asd); if (ret < 0) return ret; + + /* + * v4l2_async_match_notify() may lead to registering a + * new notifier and thus changing the async subdevs + * list. In order to proceed safely from here, restart + * parsing the list from the beginning. + */ + goto again; } return 0; @@ -183,17 +286,26 @@ static void v4l2_async_cleanup(struct v4l2_subdev *sd) sd->asd = NULL; } +/* Unbind all sub-devices in the notifier tree. */ static void v4l2_async_notifier_unbind_all_subdevs( struct v4l2_async_notifier *notifier) { struct v4l2_subdev *sd, *tmp; list_for_each_entry_safe(sd, tmp, ¬ifier->done, async_list) { + struct v4l2_async_notifier *subdev_notifier = + v4l2_async_find_subdev_notifier(sd); + + if (subdev_notifier) + v4l2_async_notifier_unbind_all_subdevs(subdev_notifier); + v4l2_async_notifier_call_unbind(notifier, sd, sd->asd); v4l2_async_cleanup(sd); list_move(&sd->async_list, &subdev_list); } + + notifier->parent = NULL; } static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) @@ -208,15 +320,6 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) INIT_LIST_HEAD(¬ifier->waiting); INIT_LIST_HEAD(¬ifier->done); - if (!notifier->num_subdevs) { - int ret; - - ret = v4l2_async_notifier_call_complete(notifier); - notifier->v4l2_dev = NULL; - - return ret; - } - for (i = 0; i < notifier->num_subdevs; i++) { asd = notifier->subdevs[i]; @@ -238,16 +341,12 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) mutex_lock(&list_lock); ret = v4l2_async_notifier_try_all_subdevs(notifier); - if (ret) { - mutex_unlock(&list_lock); - return ret; - } + if (ret) + goto err_unbind; - if (list_empty(¬ifier->waiting)) { - ret = v4l2_async_notifier_call_complete(notifier); - if (ret) - goto err_complete; - } + ret = v4l2_async_notifier_try_complete(notifier); + if (ret) + goto err_unbind; /* Keep also completed notifiers on the list */ list_add(¬ifier->list, ¬ifier_list); @@ -256,7 +355,10 @@ static int __v4l2_async_notifier_register(struct v4l2_async_notifier *notifier) return 0; -err_complete: +err_unbind: + /* + * On failure, unbind all sub-devices registered through this notifier. + */ v4l2_async_notifier_unbind_all_subdevs(notifier); mutex_unlock(&list_lock); @@ -269,7 +371,7 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, { int ret; - if (WARN_ON(!v4l2_dev)) + if (WARN_ON(!v4l2_dev || notifier->sd)) return -EINVAL; notifier->v4l2_dev = v4l2_dev; @@ -282,20 +384,39 @@ int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, } EXPORT_SYMBOL(v4l2_async_notifier_register); +int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd, + struct v4l2_async_notifier *notifier) +{ + int ret; + + if (WARN_ON(!sd || notifier->v4l2_dev)) + return -EINVAL; + + notifier->sd = sd; + + ret = __v4l2_async_notifier_register(notifier); + if (ret) + notifier->sd = NULL; + + return ret; +} +EXPORT_SYMBOL(v4l2_async_subdev_notifier_register); + void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) { - if (!notifier->v4l2_dev) + if (!notifier->v4l2_dev && !notifier->sd) return; mutex_lock(&list_lock); - list_del(¬ifier->list); - v4l2_async_notifier_unbind_all_subdevs(notifier); - mutex_unlock(&list_lock); - + notifier->sd = NULL; notifier->v4l2_dev = NULL; + + list_del(¬ifier->list); + + mutex_unlock(&list_lock); } EXPORT_SYMBOL(v4l2_async_notifier_unregister); @@ -331,6 +452,7 @@ EXPORT_SYMBOL_GPL(v4l2_async_notifier_cleanup); int v4l2_async_register_subdev(struct v4l2_subdev *sd) { + struct v4l2_async_notifier *subdev_notifier; struct v4l2_async_notifier *notifier; int ret; @@ -347,24 +469,26 @@ int v4l2_async_register_subdev(struct v4l2_subdev *sd) INIT_LIST_HEAD(&sd->async_list); list_for_each_entry(notifier, ¬ifier_list, list) { - struct v4l2_async_subdev *asd = v4l2_async_find_match(notifier, - sd); + struct v4l2_device *v4l2_dev = + v4l2_async_notifier_find_v4l2_dev(notifier); + struct v4l2_async_subdev *asd; int ret; + if (!v4l2_dev) + continue; + + asd = v4l2_async_find_match(notifier, sd); if (!asd) continue; ret = v4l2_async_match_notify(notifier, notifier->v4l2_dev, sd, asd); if (ret) - goto err_unlock; - - if (!list_empty(¬ifier->waiting)) - goto out_unlock; + goto err_unbind; - ret = v4l2_async_notifier_call_complete(notifier); + ret = v4l2_async_notifier_try_complete(notifier); if (ret) - goto err_cleanup; + goto err_unbind; goto out_unlock; } @@ -377,11 +501,19 @@ out_unlock: return 0; -err_cleanup: - v4l2_async_notifier_call_unbind(notifier, sd, sd->asd); +err_unbind: + /* + * Complete failed. Unbind the sub-devices bound through registering + * this async sub-device. + */ + subdev_notifier = v4l2_async_find_subdev_notifier(sd); + if (subdev_notifier) + v4l2_async_notifier_unbind_all_subdevs(subdev_notifier); + + if (sd->asd) + v4l2_async_notifier_call_unbind(notifier, sd, sd->asd); v4l2_async_cleanup(sd); -err_unlock: mutex_unlock(&list_lock); return ret; diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 68606afb5ef9..17c4ac7c73e8 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -82,7 +82,8 @@ struct v4l2_async_subdev { /** * struct v4l2_async_notifier_operations - Asynchronous V4L2 notifier operations * @bound: a subdevice driver has successfully probed one of the subdevices - * @complete: all subdevices have been probed successfully + * @complete: All subdevices have been probed successfully. The complete + * callback is only executed for the root notifier. * @unbind: a subdevice is leaving */ struct v4l2_async_notifier_operations { @@ -102,7 +103,9 @@ struct v4l2_async_notifier_operations { * @num_subdevs: number of subdevices used in the subdevs array * @max_subdevs: number of subdevices allocated in the subdevs array * @subdevs: array of pointers to subdevice descriptors - * @v4l2_dev: pointer to struct v4l2_device + * @v4l2_dev: v4l2_device of the root notifier, NULL otherwise + * @sd: sub-device that registered the notifier, NULL otherwise + * @parent: parent notifier * @waiting: list of struct v4l2_async_subdev, waiting for their drivers * @done: list of struct v4l2_subdev, already probed * @list: member in a global list of notifiers @@ -113,6 +116,8 @@ struct v4l2_async_notifier { unsigned int max_subdevs; struct v4l2_async_subdev **subdevs; struct v4l2_device *v4l2_dev; + struct v4l2_subdev *sd; + struct v4l2_async_notifier *parent; struct list_head waiting; struct list_head done; struct list_head list; @@ -127,6 +132,16 @@ struct v4l2_async_notifier { int v4l2_async_notifier_register(struct v4l2_device *v4l2_dev, struct v4l2_async_notifier *notifier); +/** + * v4l2_async_subdev_notifier_register - registers a subdevice asynchronous + * notifier for a sub-device + * + * @sd: pointer to &struct v4l2_subdev + * @notifier: pointer to &struct v4l2_async_notifier + */ +int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd, + struct v4l2_async_notifier *notifier); + /** * v4l2_async_notifier_unregister - unregisters a subdevice asynchronous notifier * -- cgit v1.2.3-59-g8ed1b From baf249e40fddd1793c7970b5afe7f10945dcfb0c Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 29 Aug 2017 06:13:19 -0400 Subject: media: v4l: fwnode: Move KernelDoc documentation to the header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In V4L2 the practice is to have the KernelDoc documentation in the header and not in .c source code files. This consequently makes the V4L2 fwnode function documentation part of the Media documentation build. Also correct the link related function and argument naming in documentation and add an asterisk to v4l2_fwnode_endpoint_free() documentation to make it proper KernelDoc documentation. Signed-off-by: Sakari Ailus Reviewed-by: Niklas Söderlund Acked-by: Hans Verkuil Acked-by: Pavel Machek Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 75 -------------------------------- include/media/v4l2-fwnode.h | 81 ++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 76 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index df0695b7bbcc..65bdcd59744a 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -183,25 +183,6 @@ v4l2_fwnode_endpoint_parse_csi1_bus(struct fwnode_handle *fwnode, vep->bus_type = V4L2_MBUS_CSI1; } -/** - * v4l2_fwnode_endpoint_parse() - parse all fwnode node properties - * @fwnode: pointer to the endpoint's fwnode handle - * @vep: pointer to the V4L2 fwnode data structure - * - * All properties are optional. If none are found, we don't set any flags. This - * means the port has a static configuration and no properties have to be - * specified explicitly. If any properties that identify the bus as parallel - * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if - * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we - * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a - * reference to @fwnode. - * - * NOTE: This function does not parse properties the size of which is variable - * without a low fixed limit. Please use v4l2_fwnode_endpoint_alloc_parse() in - * new drivers instead. - * - * Return: 0 on success or a negative error code on failure. - */ int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep) { @@ -241,14 +222,6 @@ int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, } EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_parse); -/* - * v4l2_fwnode_endpoint_free() - free the V4L2 fwnode acquired by - * v4l2_fwnode_endpoint_alloc_parse() - * @vep - the V4L2 fwnode the resources of which are to be released - * - * It is safe to call this function with NULL argument or on a V4L2 fwnode the - * parsing of which failed. - */ void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep) { if (IS_ERR_OR_NULL(vep)) @@ -259,29 +232,6 @@ void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep) } EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_free); -/** - * v4l2_fwnode_endpoint_alloc_parse() - parse all fwnode node properties - * @fwnode: pointer to the endpoint's fwnode handle - * - * All properties are optional. If none are found, we don't set any flags. This - * means the port has a static configuration and no properties have to be - * specified explicitly. If any properties that identify the bus as parallel - * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if - * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we - * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a - * reference to @fwnode. - * - * v4l2_fwnode_endpoint_alloc_parse() has two important differences to - * v4l2_fwnode_endpoint_parse(): - * - * 1. It also parses variable size data. - * - * 2. The memory it has allocated to store the variable size data must be freed - * using v4l2_fwnode_endpoint_free() when no longer needed. - * - * Return: Pointer to v4l2_fwnode_endpoint if successful, on an error pointer - * on error. - */ struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( struct fwnode_handle *fwnode) { @@ -324,24 +274,6 @@ out_err: } EXPORT_SYMBOL_GPL(v4l2_fwnode_endpoint_alloc_parse); -/** - * v4l2_fwnode_endpoint_parse_link() - parse a link between two endpoints - * @__fwnode: pointer to the endpoint's fwnode at the local end of the link - * @link: pointer to the V4L2 fwnode link data structure - * - * Fill the link structure with the local and remote nodes and port numbers. - * The local_node and remote_node fields are set to point to the local and - * remote port's parent nodes respectively (the port parent node being the - * parent node of the port node if that node isn't a 'ports' node, or the - * grand-parent node of the port node otherwise). - * - * A reference is taken to both the local and remote nodes, the caller must use - * v4l2_fwnode_endpoint_put_link() to drop the references when done with the - * link. - * - * Return: 0 on success, or -ENOLINK if the remote endpoint fwnode can't be - * found. - */ int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode, struct v4l2_fwnode_link *link) { @@ -376,13 +308,6 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *__fwnode, } EXPORT_SYMBOL_GPL(v4l2_fwnode_parse_link); -/** - * v4l2_fwnode_put_link() - drop references to nodes in a link - * @link: pointer to the V4L2 fwnode link data structure - * - * Drop references to the local and remote nodes in the link. This function - * must be called on every link parsed with v4l2_fwnode_parse_link(). - */ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link) { fwnode_handle_put(link->local_node); diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index ac605af9b877..105cfeee44ef 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -115,13 +115,92 @@ struct v4l2_fwnode_link { unsigned int remote_port; }; +/** + * v4l2_fwnode_endpoint_parse() - parse all fwnode node properties + * @fwnode: pointer to the endpoint's fwnode handle + * @vep: pointer to the V4L2 fwnode data structure + * + * All properties are optional. If none are found, we don't set any flags. This + * means the port has a static configuration and no properties have to be + * specified explicitly. If any properties that identify the bus as parallel + * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if + * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we + * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a + * reference to @fwnode. + * + * NOTE: This function does not parse properties the size of which is variable + * without a low fixed limit. Please use v4l2_fwnode_endpoint_alloc_parse() in + * new drivers instead. + * + * Return: 0 on success or a negative error code on failure. + */ int v4l2_fwnode_endpoint_parse(struct fwnode_handle *fwnode, struct v4l2_fwnode_endpoint *vep); + +/** + * v4l2_fwnode_endpoint_free() - free the V4L2 fwnode acquired by + * v4l2_fwnode_endpoint_alloc_parse() + * @vep: the V4L2 fwnode the resources of which are to be released + * + * It is safe to call this function with NULL argument or on a V4L2 fwnode the + * parsing of which failed. + */ +void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep); + +/** + * v4l2_fwnode_endpoint_alloc_parse() - parse all fwnode node properties + * @fwnode: pointer to the endpoint's fwnode handle + * + * All properties are optional. If none are found, we don't set any flags. This + * means the port has a static configuration and no properties have to be + * specified explicitly. If any properties that identify the bus as parallel + * are found and slave-mode isn't set, we set V4L2_MBUS_MASTER. Similarly, if + * we recognise the bus as serial CSI-2 and clock-noncontinuous isn't set, we + * set the V4L2_MBUS_CSI2_CONTINUOUS_CLOCK flag. The caller should hold a + * reference to @fwnode. + * + * v4l2_fwnode_endpoint_alloc_parse() has two important differences to + * v4l2_fwnode_endpoint_parse(): + * + * 1. It also parses variable size data. + * + * 2. The memory it has allocated to store the variable size data must be freed + * using v4l2_fwnode_endpoint_free() when no longer needed. + * + * Return: Pointer to v4l2_fwnode_endpoint if successful, on an error pointer + * on error. + */ struct v4l2_fwnode_endpoint *v4l2_fwnode_endpoint_alloc_parse( struct fwnode_handle *fwnode); -void v4l2_fwnode_endpoint_free(struct v4l2_fwnode_endpoint *vep); + +/** + * v4l2_fwnode_parse_link() - parse a link between two endpoints + * @fwnode: pointer to the endpoint's fwnode at the local end of the link + * @link: pointer to the V4L2 fwnode link data structure + * + * Fill the link structure with the local and remote nodes and port numbers. + * The local_node and remote_node fields are set to point to the local and + * remote port's parent nodes respectively (the port parent node being the + * parent node of the port node if that node isn't a 'ports' node, or the + * grand-parent node of the port node otherwise). + * + * A reference is taken to both the local and remote nodes, the caller must use + * v4l2_fwnode_put_link() to drop the references when done with the + * link. + * + * Return: 0 on success, or -ENOLINK if the remote endpoint fwnode can't be + * found. + */ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, struct v4l2_fwnode_link *link); + +/** + * v4l2_fwnode_put_link() - drop references to nodes in a link + * @link: pointer to the V4L2 fwnode link data structure + * + * Drop references to the local and remote nodes in the link. This function + * must be called on every link parsed with v4l2_fwnode_parse_link(). + */ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link); /** -- cgit v1.2.3-59-g8ed1b From 7a9ec808ad46d1e02e5e766a83adc5f88e5df264 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 6 Sep 2017 08:35:42 -0400 Subject: media: v4l: fwnode: Add convenience function for parsing common external refs Add v4l2_fwnode_parse_reference_sensor_common for parsing common sensor properties that refer to adjacent devices such as flash or lens driver chips. As this is an association only, there's little a regular driver needs to know about these devices as such. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Acked-by: Pavel Machek Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-fwnode.c | 35 +++++++++++++++++++++++++++++++++++ include/media/v4l2-async.h | 3 ++- include/media/v4l2-fwnode.h | 21 +++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index f8cd88f791c4..39387dc6cadd 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -865,6 +865,41 @@ error: return ret; } +int v4l2_async_notifier_parse_fwnode_sensor_common( + struct device *dev, struct v4l2_async_notifier *notifier) +{ + static const char * const led_props[] = { "led" }; + static const struct { + const char *name; + const char * const *props; + unsigned int nprops; + } props[] = { + { "flash-leds", led_props, ARRAY_SIZE(led_props) }, + { "lens-focus", NULL, 0 }, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(props); i++) { + int ret; + + if (props[i].props && is_acpi_node(dev_fwnode(dev))) + ret = v4l2_fwnode_reference_parse_int_props( + dev, notifier, props[i].name, + props[i].props, props[i].nprops); + else + ret = v4l2_fwnode_reference_parse( + dev, notifier, props[i].name); + if (ret && ret != -ENOENT) { + dev_warn(dev, "parsing property \"%s\" failed (%d)\n", + props[i].name, ret); + return ret; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Sylwester Nawrocki "); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 17c4ac7c73e8..8d8cfc3f3100 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -156,7 +156,8 @@ void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier); * Release memory resources related to a notifier, including the async * sub-devices allocated for the purposes of the notifier but not the notifier * itself. The user is responsible for calling this function to clean up the - * notifier after calling @v4l2_async_notifier_parse_fwnode_endpoints. + * notifier after calling @v4l2_async_notifier_parse_fwnode_endpoints or + * @v4l2_fwnode_reference_parse_sensor_common. * * There is no harm from calling v4l2_async_notifier_cleanup in other * cases as long as its memory has been zeroed after it has been diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index 105cfeee44ef..ca50108dfd8f 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -319,4 +319,25 @@ int v4l2_async_notifier_parse_fwnode_endpoints_by_port( struct v4l2_fwnode_endpoint *vep, struct v4l2_async_subdev *asd)); +/** + * v4l2_fwnode_reference_parse_sensor_common - parse common references on + * sensors for async sub-devices + * @dev: the device node the properties of which are parsed for references + * @notifier: the async notifier where the async subdevs will be added + * + * Parse common sensor properties for remote devices related to the + * sensor and set up async sub-devices for them. + * + * Any notifier populated using this function must be released with a call to + * v4l2_async_notifier_release() after it has been unregistered and the async + * sub-devices are no longer in use, even in the case the function returned an + * error. + * + * Return: 0 on success + * -ENOMEM if memory allocation failed + * -EINVAL if property parsing failed + */ +int v4l2_async_notifier_parse_fwnode_sensor_common( + struct device *dev, struct v4l2_async_notifier *notifier); + #endif /* _V4L2_FWNODE_H */ -- cgit v1.2.3-59-g8ed1b From aef69d54755d45edefbf347a51efd1673d7daed9 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Sun, 24 Sep 2017 18:47:44 -0400 Subject: media: v4l: fwnode: Add a convenience function for registering sensors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a convenience function for parsing firmware for information on related devices using v4l2_async_notifier_parse_fwnode_sensor_common() registering the notifier and finally the async sub-device itself. This should be useful for sensor drivers that do not have device specific requirements related to firmware information parsing or the async framework. Signed-off-by: Sakari Ailus Acked-by: Hans Verkuil Reviewed-by: Niklas Söderlund Reviewed-by: Sebastian Reichel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-async.c | 19 ++++++++++++---- drivers/media/v4l2-core/v4l2-fwnode.c | 41 +++++++++++++++++++++++++++++++++++ include/media/v4l2-async.h | 22 +++++++++++++++++++ include/media/v4l2-subdev.h | 3 +++ 4 files changed, 81 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/media/v4l2-core/v4l2-async.c b/drivers/media/v4l2-core/v4l2-async.c index 57bd54016064..49f7eccc76db 100644 --- a/drivers/media/v4l2-core/v4l2-async.c +++ b/drivers/media/v4l2-core/v4l2-async.c @@ -474,19 +474,25 @@ int v4l2_async_subdev_notifier_register(struct v4l2_subdev *sd, } EXPORT_SYMBOL(v4l2_async_subdev_notifier_register); -void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) +static void __v4l2_async_notifier_unregister( + struct v4l2_async_notifier *notifier) { - if (!notifier->v4l2_dev && !notifier->sd) + if (!notifier || (!notifier->v4l2_dev && !notifier->sd)) return; - mutex_lock(&list_lock); - v4l2_async_notifier_unbind_all_subdevs(notifier); notifier->sd = NULL; notifier->v4l2_dev = NULL; list_del(¬ifier->list); +} + +void v4l2_async_notifier_unregister(struct v4l2_async_notifier *notifier) +{ + mutex_lock(&list_lock); + + __v4l2_async_notifier_unregister(notifier); mutex_unlock(&list_lock); } @@ -596,6 +602,11 @@ void v4l2_async_unregister_subdev(struct v4l2_subdev *sd) { mutex_lock(&list_lock); + __v4l2_async_notifier_unregister(sd->subdev_notifier); + v4l2_async_notifier_cleanup(sd->subdev_notifier); + kfree(sd->subdev_notifier); + sd->subdev_notifier = NULL; + if (sd->asd) { struct v4l2_async_notifier *notifier = sd->notifier; diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c index 39387dc6cadd..3b9c6afb49a3 100644 --- a/drivers/media/v4l2-core/v4l2-fwnode.c +++ b/drivers/media/v4l2-core/v4l2-fwnode.c @@ -29,6 +29,7 @@ #include #include +#include enum v4l2_fwnode_bus_type { V4L2_FWNODE_BUS_TYPE_GUESS = 0, @@ -900,6 +901,46 @@ int v4l2_async_notifier_parse_fwnode_sensor_common( } EXPORT_SYMBOL_GPL(v4l2_async_notifier_parse_fwnode_sensor_common); +int v4l2_async_register_subdev_sensor_common(struct v4l2_subdev *sd) +{ + struct v4l2_async_notifier *notifier; + int ret; + + if (WARN_ON(!sd->dev)) + return -ENODEV; + + notifier = kzalloc(sizeof(*notifier), GFP_KERNEL); + if (!notifier) + return -ENOMEM; + + ret = v4l2_async_notifier_parse_fwnode_sensor_common(sd->dev, + notifier); + if (ret < 0) + goto out_cleanup; + + ret = v4l2_async_subdev_notifier_register(sd, notifier); + if (ret < 0) + goto out_cleanup; + + ret = v4l2_async_register_subdev(sd); + if (ret < 0) + goto out_unregister; + + sd->subdev_notifier = notifier; + + return 0; + +out_unregister: + v4l2_async_notifier_unregister(notifier); + +out_cleanup: + v4l2_async_notifier_cleanup(notifier); + kfree(notifier); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor_common); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Sakari Ailus "); MODULE_AUTHOR("Sylwester Nawrocki "); diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h index 8d8cfc3f3100..6152434cbe82 100644 --- a/include/media/v4l2-async.h +++ b/include/media/v4l2-async.h @@ -173,6 +173,28 @@ void v4l2_async_notifier_cleanup(struct v4l2_async_notifier *notifier); */ int v4l2_async_register_subdev(struct v4l2_subdev *sd); +/** + * v4l2_async_register_subdev_sensor_common - registers a sensor sub-device to + * the asynchronous sub-device + * framework and parse set up common + * sensor related devices + * + * @sd: pointer to struct &v4l2_subdev + * + * This function is just like v4l2_async_register_subdev() with the exception + * that calling it will also parse firmware interfaces for remote references + * using v4l2_async_notifier_parse_fwnode_sensor_common() and registers the + * async sub-devices. The sub-device is similarly unregistered by calling + * v4l2_async_unregister_subdev(). + * + * While registered, the subdev module is marked as in-use. + * + * An error is returned if the module is no longer loaded on any attempts + * to register it. + */ +int __must_check v4l2_async_register_subdev_sensor_common( + struct v4l2_subdev *sd); + /** * v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous * subdevice framework diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index e83872078376..ec399c770301 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -793,6 +793,8 @@ struct v4l2_subdev_platform_data { * list. * @asd: Pointer to respective &struct v4l2_async_subdev. * @notifier: Pointer to the managing notifier. + * @subdev_notifier: A sub-device notifier implicitly registered for the sub- + * device using v4l2_device_register_sensor_subdev(). * @pdata: common part of subdevice platform data * * Each instance of a subdev driver should create this struct, either @@ -823,6 +825,7 @@ struct v4l2_subdev { struct list_head async_list; struct v4l2_async_subdev *asd; struct v4l2_async_notifier *notifier; + struct v4l2_async_notifier *subdev_notifier; struct v4l2_subdev_platform_data *pdata; }; -- cgit v1.2.3-59-g8ed1b From aa4bf44dc851c6bdd4f7b61b5f2c56c84dfe2ff0 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 25 Oct 2017 00:04:40 +0200 Subject: userns: use union in {g,u}idmap struct - Add a struct containing two pointer to extents and wrap both the static extent array and the struct into a union. This is done in preparation for bumping the {g,u}idmap limits for user namespaces. - Add brackets around anonymous union when using designated initializers to initialize members in order to please gcc <= 4.4. Signed-off-by: Christian Brauner Signed-off-by: Eric W. Biederman --- include/linux/user_namespace.h | 18 +++++++++++++----- kernel/user.c | 30 ++++++++++++++++++------------ 2 files changed, 31 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index c18e01252346..7c83d7f6289b 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -12,13 +12,21 @@ #define UID_GID_MAP_MAX_EXTENTS 5 +struct uid_gid_extent { + u32 first; + u32 lower_first; + u32 count; +}; + struct uid_gid_map { /* 64 bytes -- 1 cache line */ u32 nr_extents; - struct uid_gid_extent { - u32 first; - u32 lower_first; - u32 count; - } extent[UID_GID_MAP_MAX_EXTENTS]; + union { + struct uid_gid_extent extent[UID_GID_MAP_MAX_EXTENTS]; + struct { + struct uid_gid_extent *forward; + struct uid_gid_extent *reverse; + }; + }; }; #define USERNS_SETGROUPS_ALLOWED 1UL diff --git a/kernel/user.c b/kernel/user.c index 00281add65b2..9a20acce460d 100644 --- a/kernel/user.c +++ b/kernel/user.c @@ -26,26 +26,32 @@ struct user_namespace init_user_ns = { .uid_map = { .nr_extents = 1, - .extent[0] = { - .first = 0, - .lower_first = 0, - .count = 4294967295U, + { + .extent[0] = { + .first = 0, + .lower_first = 0, + .count = 4294967295U, + }, }, }, .gid_map = { .nr_extents = 1, - .extent[0] = { - .first = 0, - .lower_first = 0, - .count = 4294967295U, + { + .extent[0] = { + .first = 0, + .lower_first = 0, + .count = 4294967295U, + }, }, }, .projid_map = { .nr_extents = 1, - .extent[0] = { - .first = 0, - .lower_first = 0, - .count = 4294967295U, + { + .extent[0] = { + .first = 0, + .lower_first = 0, + .count = 4294967295U, + }, }, }, .count = ATOMIC_INIT(3), -- cgit v1.2.3-59-g8ed1b From 6397fac4915ab3002dc15aae751455da1a852f25 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 25 Oct 2017 00:04:41 +0200 Subject: userns: bump idmap limits to 340 There are quite some use cases where users run into the current limit for {g,u}id mappings. Consider a user requesting us to map everything but 999, and 1001 for a given range of 1000000000 with a sub{g,u}id layout of: some-user:100000:1000000000 some-user:999:1 some-user:1000:1 some-user:1001:1 some-user:1002:1 This translates to: MAPPING-TYPE | CONTAINER | HOST | RANGE | -------------|-----------|---------|-----------| uid | 999 | 999 | 1 | uid | 1001 | 1001 | 1 | uid | 0 | 1000000 | 999 | uid | 1000 | 1001000 | 1 | uid | 1002 | 1001002 | 999998998 | ------------------------------------------------ gid | 999 | 999 | 1 | gid | 1001 | 1001 | 1 | gid | 0 | 1000000 | 999 | gid | 1000 | 1001000 | 1 | gid | 1002 | 1001002 | 999998998 | which is already the current limit. As discussed at LPC simply bumping the number of limits is not going to work since this would mean that struct uid_gid_map won't fit into a single cache-line anymore thereby regressing performance for the base-cases. The same problem seems to arise when using a single pointer. So the idea is to use struct uid_gid_extent { u32 first; u32 lower_first; u32 count; }; struct uid_gid_map { /* 64 bytes -- 1 cache line */ u32 nr_extents; union { struct uid_gid_extent extent[UID_GID_MAP_MAX_BASE_EXTENTS]; struct { struct uid_gid_extent *forward; struct uid_gid_extent *reverse; }; }; }; For the base cases we will only use the struct uid_gid_extent extent member. If we go over UID_GID_MAP_MAX_BASE_EXTENTS mappings we perform a single 4k kmalloc() which means we can have a maximum of 340 mappings (340 * size(struct uid_gid_extent) = 4080). For the latter case we use two pointers "forward" and "reverse". The forward pointer points to an array sorted by "first" and the reverse pointer points to an array sorted by "lower_first". We can then perform binary search on those arrays. Performance Testing: When Eric introduced the extent-based struct uid_gid_map approach he measured the performanc impact of his idmap changes: > My benchmark consisted of going to single user mode where nothing else was > running. On an ext4 filesystem opening 1,000,000 files and looping through all > of the files 1000 times and calling fstat on the individuals files. This was > to ensure I was benchmarking stat times where the inodes were in the kernels > cache, but the inode values were not in the processors cache. My results: > v3.4-rc1: ~= 156ns (unmodified v3.4-rc1 with user namespace support disabled) > v3.4-rc1-userns-: ~= 155ns (v3.4-rc1 with my user namespace patches and user namespace support disabled) > v3.4-rc1-userns+: ~= 164ns (v3.4-rc1 with my user namespace patches and user namespace support enabled) I used an identical approach on my laptop. Here's a thorough description of what I did. I built a 4.14.0-rc4 mainline kernel with my new idmap patches applied. I booted into single user mode and used an ext4 filesystem to open/create 1,000,000 files. Then I looped through all of the files calling fstat() on each of them 1000 times and calculated the mean fstat() time for a single file. (The test program can be found below.) Here are the results. For fun, I compared the first version of my patch which scaled linearly with the new version of the patch: | # MAPPINGS | PATCH-V1 | PATCH-NEW | |--------------|------------|-----------| | 0 mappings | 158 ns | 158 ns | | 1 mappings | 164 ns | 157 ns | | 2 mappings | 170 ns | 158 ns | | 3 mappings | 175 ns | 161 ns | | 5 mappings | 187 ns | 165 ns | | 10 mappings | 218 ns | 199 ns | | 50 mappings | 528 ns | 218 ns | | 100 mappings | 980 ns | 229 ns | | 200 mappings | 1880 ns | 239 ns | | 300 mappings | 2760 ns | 240 ns | | 340 mappings | not tested | 248 ns | Here's the test program I used. I asked Eric what he did and this is a more "advanced" implementation of the idea. It's pretty straight-forward: #define __GNU_SOURCE #define __STDC_FORMAT_MACROS #include #include #include #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { int ret; size_t i, k; int fd[1000000]; int times[1000]; char pathname[4096]; struct stat st; struct timeval t1, t2; uint64_t time_in_mcs; uint64_t sum = 0; if (argc != 2) { fprintf(stderr, "Please specify a directory where to create " "the test files\n"); exit(EXIT_FAILURE); } for (i = 0; i < sizeof(fd) / sizeof(fd[0]); i++) { sprintf(pathname, "%s/idmap_test_%zu", argv[1], i); fd[i]= open(pathname, O_RDWR | O_CREAT, S_IXUSR | S_IXGRP | S_IXOTH); if (fd[i] < 0) { ssize_t j; for (j = i; j >= 0; j--) close(fd[j]); exit(EXIT_FAILURE); } } for (k = 0; k < 1000; k++) { ret = gettimeofday(&t1, NULL); if (ret < 0) goto close_all; for (i = 0; i < sizeof(fd) / sizeof(fd[0]); i++) { ret = fstat(fd[i], &st); if (ret < 0) goto close_all; } ret = gettimeofday(&t2, NULL); if (ret < 0) goto close_all; time_in_mcs = (1000000 * t2.tv_sec + t2.tv_usec) - (1000000 * t1.tv_sec + t1.tv_usec); printf("Total time in micro seconds: %" PRIu64 "\n", time_in_mcs); printf("Total time in nanoseconds: %" PRIu64 "\n", time_in_mcs * 1000); printf("Time per file in nanoseconds: %" PRIu64 "\n", (time_in_mcs * 1000) / 1000000); times[k] = (time_in_mcs * 1000) / 1000000; } close_all: for (i = 0; i < sizeof(fd) / sizeof(fd[0]); i++) close(fd[i]); if (ret < 0) exit(EXIT_FAILURE); for (k = 0; k < 1000; k++) { sum += times[k]; } printf("Mean time per file in nanoseconds: %" PRIu64 "\n", sum / 1000); exit(EXIT_SUCCESS);; } Signed-off-by: Christian Brauner CC: Serge Hallyn CC: Eric Biederman Signed-off-by: Eric W. Biederman --- include/linux/user_namespace.h | 7 +- kernel/user_namespace.c | 350 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 324 insertions(+), 33 deletions(-) (limited to 'include') diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 7c83d7f6289b..1c1046a60fb4 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -10,7 +10,8 @@ #include #include -#define UID_GID_MAP_MAX_EXTENTS 5 +#define UID_GID_MAP_MAX_BASE_EXTENTS 5 +#define UID_GID_MAP_MAX_EXTENTS 340 struct uid_gid_extent { u32 first; @@ -18,10 +19,10 @@ struct uid_gid_extent { u32 count; }; -struct uid_gid_map { /* 64 bytes -- 1 cache line */ +struct uid_gid_map { /* 64 bytes -- 1 cache line */ u32 nr_extents; union { - struct uid_gid_extent extent[UID_GID_MAP_MAX_EXTENTS]; + struct uid_gid_extent extent[UID_GID_MAP_MAX_BASE_EXTENTS]; struct { struct uid_gid_extent *forward; struct uid_gid_extent *reverse; diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index c490f1e4313b..5fd2d53dbc75 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include static struct kmem_cache *user_ns_cachep __read_mostly; static DEFINE_MUTEX(userns_state_mutex); @@ -181,6 +183,18 @@ static void free_user_ns(struct work_struct *work) do { struct ucounts *ucounts = ns->ucounts; parent = ns->parent; + if (ns->gid_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) { + kfree(ns->gid_map.forward); + kfree(ns->gid_map.reverse); + } + if (ns->uid_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) { + kfree(ns->uid_map.forward); + kfree(ns->uid_map.reverse); + } + if (ns->projid_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) { + kfree(ns->projid_map.forward); + kfree(ns->projid_map.reverse); + } retire_userns_sysctls(ns); #ifdef CONFIG_PERSISTENT_KEYRINGS key_put(ns->persistent_keyring_register); @@ -198,7 +212,84 @@ void __put_user_ns(struct user_namespace *ns) } EXPORT_SYMBOL(__put_user_ns); -static u32 map_id_range_down(struct uid_gid_map *map, u32 id, u32 count) +/** + * idmap_key struct holds the information necessary to find an idmapping in a + * sorted idmap array. It is passed to cmp_map_id() as first argument. + */ +struct idmap_key { + bool map_up; /* true -> id from kid; false -> kid from id */ + u32 id; /* id to find */ + u32 count; /* == 0 unless used with map_id_range_down() */ +}; + +/** + * cmp_map_id - Function to be passed to bsearch() to find the requested + * idmapping. Expects struct idmap_key to be passed via @k. + */ +static int cmp_map_id(const void *k, const void *e) +{ + u32 first, last, id2; + const struct idmap_key *key = k; + const struct uid_gid_extent *el = e; + + /* handle map_id_range_down() */ + if (key->count) + id2 = key->id + key->count - 1; + else + id2 = key->id; + + /* handle map_id_{down,up}() */ + if (key->map_up) + first = el->lower_first; + else + first = el->first; + + last = first + el->count - 1; + + if (key->id >= first && key->id <= last && + (id2 >= first && id2 <= last)) + return 0; + + if (key->id < first || id2 < first) + return -1; + + return 1; +} + +/** + * map_id_range_down_max - Find idmap via binary search in ordered idmap array. + * Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS. + */ +static u32 map_id_range_down_max(struct uid_gid_map *map, u32 id, u32 count) +{ + u32 extents; + struct uid_gid_extent *extent; + struct idmap_key key; + + key.map_up = false; + key.count = count; + key.id = id; + + extents = map->nr_extents; + smp_rmb(); + + extent = bsearch(&key, map->forward, extents, + sizeof(struct uid_gid_extent), cmp_map_id); + /* Map the id or note failure */ + if (extent) + id = (id - extent->first) + extent->lower_first; + else + id = (u32) -1; + + return id; +} + +/** + * map_id_range_down_base - Find idmap via binary search in static extent array. + * Can only be called if number of mappings is equal or less than + * UID_GID_MAP_MAX_BASE_EXTENTS. + */ +static u32 map_id_range_down_base(struct uid_gid_map *map, u32 id, u32 count) { unsigned idx, extents; u32 first, last, id2; @@ -224,7 +315,23 @@ static u32 map_id_range_down(struct uid_gid_map *map, u32 id, u32 count) return id; } -static u32 map_id_down(struct uid_gid_map *map, u32 id) +static u32 map_id_range_down(struct uid_gid_map *map, u32 id, u32 count) +{ + u32 extents = map->nr_extents; + smp_rmb(); + + if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS) + return map_id_range_down_base(map, id, count); + + return map_id_range_down_max(map, id, count); +} + +/** + * map_id_down_base - Find idmap via binary search in static extent array. + * Can only be called if number of mappings is equal or less than + * UID_GID_MAP_MAX_BASE_EXTENTS. + */ +static u32 map_id_down_base(struct uid_gid_map *map, u32 id) { unsigned idx, extents; u32 first, last; @@ -247,7 +354,23 @@ static u32 map_id_down(struct uid_gid_map *map, u32 id) return id; } -static u32 map_id_up(struct uid_gid_map *map, u32 id) +static u32 map_id_down(struct uid_gid_map *map, u32 id) +{ + u32 extents = map->nr_extents; + smp_rmb(); + + if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS) + return map_id_down_base(map, id); + + return map_id_range_down_max(map, id, 0); +} + +/** + * map_id_up_base - Find idmap via binary search in static extent array. + * Can only be called if number of mappings is equal or less than + * UID_GID_MAP_MAX_BASE_EXTENTS. + */ +static u32 map_id_up_base(struct uid_gid_map *map, u32 id) { unsigned idx, extents; u32 first, last; @@ -270,6 +393,45 @@ static u32 map_id_up(struct uid_gid_map *map, u32 id) return id; } +/** + * map_id_up_max - Find idmap via binary search in ordered idmap array. + * Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS. + */ +static u32 map_id_up_max(struct uid_gid_map *map, u32 id) +{ + u32 extents; + struct uid_gid_extent *extent; + struct idmap_key key; + + key.map_up = true; + key.count = 0; + key.id = id; + + extents = map->nr_extents; + smp_rmb(); + + extent = bsearch(&key, map->reverse, extents, + sizeof(struct uid_gid_extent), cmp_map_id); + /* Map the id or note failure */ + if (extent) + id = (id - extent->lower_first) + extent->first; + else + id = (u32) -1; + + return id; +} + +static u32 map_id_up(struct uid_gid_map *map, u32 id) +{ + u32 extents = map->nr_extents; + smp_rmb(); + + if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS) + return map_id_up_base(map, id); + + return map_id_up_max(map, id); +} + /** * make_kuid - Map a user-namespace uid pair into a kuid. * @ns: User namespace that the uid is in @@ -540,13 +702,15 @@ static int projid_m_show(struct seq_file *seq, void *v) static void *m_start(struct seq_file *seq, loff_t *ppos, struct uid_gid_map *map) { - struct uid_gid_extent *extent = NULL; loff_t pos = *ppos; - if (pos < map->nr_extents) - extent = &map->extent[pos]; + if (pos >= map->nr_extents) + return NULL; + + if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) + return &map->extent[pos]; - return extent; + return &map->forward[pos]; } static void *uid_m_start(struct seq_file *seq, loff_t *ppos) @@ -618,7 +782,10 @@ static bool mappings_overlap(struct uid_gid_map *new_map, u32 prev_upper_last, prev_lower_last; struct uid_gid_extent *prev; - prev = &new_map->extent[idx]; + if (new_map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) + prev = &new_map->extent[idx]; + else + prev = &new_map->forward[idx]; prev_upper_first = prev->first; prev_lower_first = prev->lower_first; @@ -638,6 +805,102 @@ static bool mappings_overlap(struct uid_gid_map *new_map, return false; } +/** + * insert_extent - Safely insert a new idmap extent into struct uid_gid_map. + * Takes care to allocate a 4K block of memory if the number of mappings exceeds + * UID_GID_MAP_MAX_BASE_EXTENTS. + */ +static int insert_extent(struct uid_gid_map *map, struct uid_gid_extent *extent) +{ + if (map->nr_extents < UID_GID_MAP_MAX_BASE_EXTENTS) { + map->extent[map->nr_extents].first = extent->first; + map->extent[map->nr_extents].lower_first = extent->lower_first; + map->extent[map->nr_extents].count = extent->count; + return 0; + } + + if (map->nr_extents == UID_GID_MAP_MAX_BASE_EXTENTS) { + struct uid_gid_extent *forward; + + /* Allocate memory for 340 mappings. */ + forward = kmalloc(sizeof(struct uid_gid_extent) * + UID_GID_MAP_MAX_EXTENTS, GFP_KERNEL); + if (!forward) + return -ENOMEM; + + /* Copy over memory. Only set up memory for the forward pointer. + * Defer the memory setup for the reverse pointer. + */ + memcpy(forward, map->extent, + map->nr_extents * sizeof(map->extent[0])); + + map->forward = forward; + map->reverse = NULL; + } + + map->forward[map->nr_extents].first = extent->first; + map->forward[map->nr_extents].lower_first = extent->lower_first; + map->forward[map->nr_extents].count = extent->count; + return 0; +} + +/* cmp function to sort() forward mappings */ +static int cmp_extents_forward(const void *a, const void *b) +{ + const struct uid_gid_extent *e1 = a; + const struct uid_gid_extent *e2 = b; + + if (e1->first < e2->first) + return -1; + + if (e1->first > e2->first) + return 1; + + return 0; +} + +/* cmp function to sort() reverse mappings */ +static int cmp_extents_reverse(const void *a, const void *b) +{ + const struct uid_gid_extent *e1 = a; + const struct uid_gid_extent *e2 = b; + + if (e1->lower_first < e2->lower_first) + return -1; + + if (e1->lower_first > e2->lower_first) + return 1; + + return 0; +} + +/** + * sort_idmaps - Sorts an array of idmap entries. + * Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS. + */ +static int sort_idmaps(struct uid_gid_map *map) +{ + if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) + return 0; + + /* Sort forward array. */ + sort(map->forward, map->nr_extents, sizeof(struct uid_gid_extent), + cmp_extents_forward, NULL); + + /* Only copy the memory from forward we actually need. */ + map->reverse = kmemdup(map->forward, + map->nr_extents * sizeof(struct uid_gid_extent), + GFP_KERNEL); + if (!map->reverse) + return -ENOMEM; + + /* Sort reverse array. */ + sort(map->reverse, map->nr_extents, sizeof(struct uid_gid_extent), + cmp_extents_reverse, NULL); + + return 0; +} + static ssize_t map_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos, int cap_setid, @@ -648,7 +911,7 @@ static ssize_t map_write(struct file *file, const char __user *buf, struct user_namespace *ns = seq->private; struct uid_gid_map new_map; unsigned idx; - struct uid_gid_extent *extent = NULL; + struct uid_gid_extent extent; char *kbuf = NULL, *pos, *next_line; ssize_t ret = -EINVAL; @@ -673,6 +936,8 @@ static ssize_t map_write(struct file *file, const char __user *buf, */ mutex_lock(&userns_state_mutex); + memset(&new_map, 0, sizeof(struct uid_gid_map)); + ret = -EPERM; /* Only allow one successful write to the map */ if (map->nr_extents != 0) @@ -700,9 +965,7 @@ static ssize_t map_write(struct file *file, const char __user *buf, /* Parse the user data */ ret = -EINVAL; pos = kbuf; - new_map.nr_extents = 0; for (; pos; pos = next_line) { - extent = &new_map.extent[new_map.nr_extents]; /* Find the end of line and ensure I don't look past it */ next_line = strchr(pos, '\n'); @@ -714,17 +977,17 @@ static ssize_t map_write(struct file *file, const char __user *buf, } pos = skip_spaces(pos); - extent->first = simple_strtoul(pos, &pos, 10); + extent.first = simple_strtoul(pos, &pos, 10); if (!isspace(*pos)) goto out; pos = skip_spaces(pos); - extent->lower_first = simple_strtoul(pos, &pos, 10); + extent.lower_first = simple_strtoul(pos, &pos, 10); if (!isspace(*pos)) goto out; pos = skip_spaces(pos); - extent->count = simple_strtoul(pos, &pos, 10); + extent.count = simple_strtoul(pos, &pos, 10); if (*pos && !isspace(*pos)) goto out; @@ -734,29 +997,33 @@ static ssize_t map_write(struct file *file, const char __user *buf, goto out; /* Verify we have been given valid starting values */ - if ((extent->first == (u32) -1) || - (extent->lower_first == (u32) -1)) + if ((extent.first == (u32) -1) || + (extent.lower_first == (u32) -1)) goto out; /* Verify count is not zero and does not cause the * extent to wrap */ - if ((extent->first + extent->count) <= extent->first) + if ((extent.first + extent.count) <= extent.first) goto out; - if ((extent->lower_first + extent->count) <= - extent->lower_first) + if ((extent.lower_first + extent.count) <= + extent.lower_first) goto out; /* Do the ranges in extent overlap any previous extents? */ - if (mappings_overlap(&new_map, extent)) + if (mappings_overlap(&new_map, &extent)) goto out; - new_map.nr_extents++; - - /* Fail if the file contains too many extents */ - if ((new_map.nr_extents == UID_GID_MAP_MAX_EXTENTS) && + if ((new_map.nr_extents + 1) == UID_GID_MAP_MAX_EXTENTS && (next_line != NULL)) goto out; + + ret = insert_extent(&new_map, &extent); + if (ret < 0) + goto out; + ret = -EINVAL; + + new_map.nr_extents++; } /* Be very certaint the new map actually exists */ if (new_map.nr_extents == 0) @@ -767,16 +1034,26 @@ static ssize_t map_write(struct file *file, const char __user *buf, if (!new_idmap_permitted(file, ns, cap_setid, &new_map)) goto out; + ret = sort_idmaps(&new_map); + if (ret < 0) + goto out; + + ret = -EPERM; /* Map the lower ids from the parent user namespace to the * kernel global id space. */ for (idx = 0; idx < new_map.nr_extents; idx++) { + struct uid_gid_extent *e; u32 lower_first; - extent = &new_map.extent[idx]; + + if (new_map.nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) + e = &new_map.extent[idx]; + else + e = &new_map.forward[idx]; lower_first = map_id_range_down(parent_map, - extent->lower_first, - extent->count); + e->lower_first, + e->count); /* Fail if we can not map the specified extent to * the kernel global id space. @@ -784,18 +1061,31 @@ static ssize_t map_write(struct file *file, const char __user *buf, if (lower_first == (u32) -1) goto out; - extent->lower_first = lower_first; + e->lower_first = lower_first; } /* Install the map */ - memcpy(map->extent, new_map.extent, - new_map.nr_extents*sizeof(new_map.extent[0])); + if (new_map.nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) { + memcpy(map->extent, new_map.extent, + new_map.nr_extents * sizeof(new_map.extent[0])); + } else { + map->forward = new_map.forward; + map->reverse = new_map.reverse; + } smp_wmb(); map->nr_extents = new_map.nr_extents; *ppos = count; ret = count; out: + if (ret < 0 && new_map.nr_extents > UID_GID_MAP_MAX_BASE_EXTENTS) { + kfree(new_map.forward); + kfree(new_map.reverse); + map->forward = NULL; + map->reverse = NULL; + map->nr_extents = 0; + } + mutex_unlock(&userns_state_mutex); kfree(kbuf); return ret; -- cgit v1.2.3-59-g8ed1b From 638f5b90d46016372a8e3e0a434f199cc5e12b8c Mon Sep 17 00:00:00 2001 From: Alexei Starovoitov Date: Tue, 31 Oct 2017 18:16:05 -0700 Subject: bpf: reduce verifier memory consumption the verifier got progressively smarter over time and size of its internal state grew as well. Time to reduce the memory consumption. Before: sizeof(struct bpf_verifier_state) = 6520 After: sizeof(struct bpf_verifier_state) = 896 It's done by observing that majority of BPF programs use little to no stack whereas verifier kept all of 512 stack slots ready always. Instead dynamically reallocate struct verifier state when stack access is detected. Runtime difference before vs after is within a noise. The number of processed instructions stays the same. Signed-off-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/bpf/verifier.c | 8 +- include/linux/bpf_verifier.h | 16 +- kernel/bpf/verifier.c | 437 ++++++++++++++-------- 3 files changed, 305 insertions(+), 156 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c index 3d3dcac1c942..a8c7615546a9 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c @@ -76,9 +76,9 @@ nfp_bpf_goto_meta(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, static int nfp_bpf_check_exit(struct nfp_prog *nfp_prog, - const struct bpf_verifier_env *env) + struct bpf_verifier_env *env) { - const struct bpf_reg_state *reg0 = &env->cur_state.regs[0]; + const struct bpf_reg_state *reg0 = cur_regs(env) + BPF_REG_0; u64 imm; if (nfp_prog->act == NN_ACT_XDP) @@ -144,9 +144,9 @@ nfp_bpf_check_stack_access(struct nfp_prog *nfp_prog, static int nfp_bpf_check_ptr(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta, - const struct bpf_verifier_env *env, u8 reg_no) + struct bpf_verifier_env *env, u8 reg_no) { - const struct bpf_reg_state *reg = &env->cur_state.regs[reg_no]; + const struct bpf_reg_state *reg = cur_regs(env) + reg_no; int err; if (reg->type != PTR_TO_CTX && diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index feeaea93d959..3b0976aaac75 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -88,14 +88,19 @@ enum bpf_stack_slot_type { #define BPF_REG_SIZE 8 /* size of eBPF register in bytes */ +struct bpf_stack_state { + struct bpf_reg_state spilled_ptr; + u8 slot_type[BPF_REG_SIZE]; +}; + /* state of the program: * type of all registers and stack info */ struct bpf_verifier_state { struct bpf_reg_state regs[MAX_BPF_REG]; - u8 stack_slot_type[MAX_BPF_STACK]; - struct bpf_reg_state spilled_regs[MAX_BPF_STACK / BPF_REG_SIZE]; struct bpf_verifier_state *parent; + int allocated_stack; + struct bpf_stack_state *stack; }; /* linked list of verifier states used to prune search */ @@ -145,7 +150,7 @@ struct bpf_verifier_env { struct bpf_verifier_stack_elem *head; /* stack of verifier states to be processed */ int stack_size; /* number of states to be processed */ bool strict_alignment; /* perform strict pointer alignment checks */ - struct bpf_verifier_state cur_state; /* current verifier state */ + struct bpf_verifier_state *cur_state; /* current verifier state */ struct bpf_verifier_state_list **explored_states; /* search pruning optimization */ const struct bpf_ext_analyzer_ops *analyzer_ops; /* external analyzer ops */ void *analyzer_priv; /* pointer to external analyzer's private data */ @@ -159,6 +164,11 @@ struct bpf_verifier_env { struct bpf_verifer_log log; }; +static inline struct bpf_reg_state *cur_regs(struct bpf_verifier_env *env) +{ + return env->cur_state->regs; +} + int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, void *priv); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index d906775e12c1..5f26f7ad124f 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -276,43 +276,132 @@ static void print_verifier_state(struct bpf_verifier_env *env, verbose(env, ")"); } } - for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { - if (state->stack_slot_type[i] == STACK_SPILL) - verbose(env, " fp%d=%s", -MAX_BPF_STACK + i, - reg_type_str[state->spilled_regs[i / BPF_REG_SIZE].type]); + for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) { + if (state->stack[i].slot_type[0] == STACK_SPILL) + verbose(env, " fp%d=%s", + -MAX_BPF_STACK + i * BPF_REG_SIZE, + reg_type_str[state->stack[i].spilled_ptr.type]); } verbose(env, "\n"); } -static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx) +static int copy_stack_state(struct bpf_verifier_state *dst, + const struct bpf_verifier_state *src) { - struct bpf_verifier_stack_elem *elem; - int insn_idx; + if (!src->stack) + return 0; + if (WARN_ON_ONCE(dst->allocated_stack < src->allocated_stack)) { + /* internal bug, make state invalid to reject the program */ + memset(dst, 0, sizeof(*dst)); + return -EFAULT; + } + memcpy(dst->stack, src->stack, + sizeof(*src->stack) * (src->allocated_stack / BPF_REG_SIZE)); + return 0; +} + +/* do_check() starts with zero-sized stack in struct bpf_verifier_state to + * make it consume minimal amount of memory. check_stack_write() access from + * the program calls into realloc_verifier_state() to grow the stack size. + * Note there is a non-zero 'parent' pointer inside bpf_verifier_state + * which this function copies over. It points to previous bpf_verifier_state + * which is never reallocated + */ +static int realloc_verifier_state(struct bpf_verifier_state *state, int size, + bool copy_old) +{ + u32 old_size = state->allocated_stack; + struct bpf_stack_state *new_stack; + int slot = size / BPF_REG_SIZE; + + if (size <= old_size || !size) { + if (copy_old) + return 0; + state->allocated_stack = slot * BPF_REG_SIZE; + if (!size && old_size) { + kfree(state->stack); + state->stack = NULL; + } + return 0; + } + new_stack = kmalloc_array(slot, sizeof(struct bpf_stack_state), + GFP_KERNEL); + if (!new_stack) + return -ENOMEM; + if (copy_old) { + if (state->stack) + memcpy(new_stack, state->stack, + sizeof(*new_stack) * (old_size / BPF_REG_SIZE)); + memset(new_stack + old_size / BPF_REG_SIZE, 0, + sizeof(*new_stack) * (size - old_size) / BPF_REG_SIZE); + } + state->allocated_stack = slot * BPF_REG_SIZE; + kfree(state->stack); + state->stack = new_stack; + return 0; +} + +static void free_verifier_state(struct bpf_verifier_state *state) +{ + kfree(state->stack); + kfree(state); +} + +/* copy verifier state from src to dst growing dst stack space + * when necessary to accommodate larger src stack + */ +static int copy_verifier_state(struct bpf_verifier_state *dst, + const struct bpf_verifier_state *src) +{ + int err; + + err = realloc_verifier_state(dst, src->allocated_stack, false); + if (err) + return err; + memcpy(dst, src, offsetof(struct bpf_verifier_state, allocated_stack)); + return copy_stack_state(dst, src); +} + +static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx, + int *insn_idx) +{ + struct bpf_verifier_state *cur = env->cur_state; + struct bpf_verifier_stack_elem *elem, *head = env->head; + int err; if (env->head == NULL) - return -1; + return -ENOENT; - memcpy(&env->cur_state, &env->head->st, sizeof(env->cur_state)); - insn_idx = env->head->insn_idx; + if (cur) { + err = copy_verifier_state(cur, &head->st); + if (err) + return err; + } + if (insn_idx) + *insn_idx = head->insn_idx; if (prev_insn_idx) - *prev_insn_idx = env->head->prev_insn_idx; - elem = env->head->next; - kfree(env->head); + *prev_insn_idx = head->prev_insn_idx; + elem = head->next; + kfree(head); env->head = elem; env->stack_size--; - return insn_idx; + return 0; } static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx) { + struct bpf_verifier_state *cur = env->cur_state; struct bpf_verifier_stack_elem *elem; + int err; - elem = kmalloc(sizeof(struct bpf_verifier_stack_elem), GFP_KERNEL); + elem = kzalloc(sizeof(struct bpf_verifier_stack_elem), GFP_KERNEL); if (!elem) goto err; - memcpy(&elem->st, &env->cur_state, sizeof(env->cur_state)); + err = copy_verifier_state(&elem->st, cur); + if (err) + return NULL; elem->insn_idx = insn_idx; elem->prev_insn_idx = prev_insn_idx; elem->next = env->head; @@ -325,7 +414,7 @@ static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env, return &elem->st; err: /* pop all elements and return */ - while (pop_stack(env, NULL) >= 0); + while (!pop_stack(env, NULL, NULL)); return NULL; } @@ -550,7 +639,7 @@ static void mark_reg_read(const struct bpf_verifier_state *state, u32 regno) static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, enum reg_arg_type t) { - struct bpf_reg_state *regs = env->cur_state.regs; + struct bpf_reg_state *regs = env->cur_state->regs; if (regno >= MAX_BPF_REG) { verbose(env, "R%d is invalid\n", regno); @@ -563,7 +652,7 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno, verbose(env, "R%d !read_ok\n", regno); return -EACCES; } - mark_reg_read(&env->cur_state, regno); + mark_reg_read(env->cur_state, regno); } else { /* check whether register used as dest operand can be written to */ if (regno == BPF_REG_FP) { @@ -601,10 +690,21 @@ static int check_stack_write(struct bpf_verifier_env *env, struct bpf_verifier_state *state, int off, int size, int value_regno) { - int i, spi = (MAX_BPF_STACK + off) / BPF_REG_SIZE; + int i, slot = -off - 1, spi = slot / BPF_REG_SIZE, err; + + err = realloc_verifier_state(state, round_up(slot + 1, BPF_REG_SIZE), + true); + if (err) + return err; /* caller checked that off % size == 0 and -MAX_BPF_STACK <= off < 0, * so it's aligned access and [off, off + size) are within stack limits */ + if (!env->allow_ptr_leaks && + state->stack[spi].slot_type[0] == STACK_SPILL && + size != BPF_REG_SIZE) { + verbose(env, "attempt to corrupt spilled pointer on stack\n"); + return -EACCES; + } if (value_regno >= 0 && is_spillable_regtype(state->regs[value_regno].type)) { @@ -616,17 +716,18 @@ static int check_stack_write(struct bpf_verifier_env *env, } /* save register state */ - state->spilled_regs[spi] = state->regs[value_regno]; - state->spilled_regs[spi].live |= REG_LIVE_WRITTEN; + state->stack[spi].spilled_ptr = state->regs[value_regno]; + state->stack[spi].spilled_ptr.live |= REG_LIVE_WRITTEN; for (i = 0; i < BPF_REG_SIZE; i++) - state->stack_slot_type[MAX_BPF_STACK + off + i] = STACK_SPILL; + state->stack[spi].slot_type[i] = STACK_SPILL; } else { /* regular write of data into stack */ - state->spilled_regs[spi] = (struct bpf_reg_state) {}; + state->stack[spi].spilled_ptr = (struct bpf_reg_state) {}; for (i = 0; i < size; i++) - state->stack_slot_type[MAX_BPF_STACK + off + i] = STACK_MISC; + state->stack[spi].slot_type[(slot - i) % BPF_REG_SIZE] = + STACK_MISC; } return 0; } @@ -637,10 +738,10 @@ static void mark_stack_slot_read(const struct bpf_verifier_state *state, int slo while (parent) { /* if read wasn't screened by an earlier write ... */ - if (state->spilled_regs[slot].live & REG_LIVE_WRITTEN) + if (state->stack[slot].spilled_ptr.live & REG_LIVE_WRITTEN) break; /* ... then we depend on parent's value */ - parent->spilled_regs[slot].live |= REG_LIVE_READ; + parent->stack[slot].spilled_ptr.live |= REG_LIVE_READ; state = parent; parent = state->parent; } @@ -650,34 +751,37 @@ static int check_stack_read(struct bpf_verifier_env *env, struct bpf_verifier_state *state, int off, int size, int value_regno) { - u8 *slot_type; - int i, spi; + int i, slot = -off - 1, spi = slot / BPF_REG_SIZE; + u8 *stype; - slot_type = &state->stack_slot_type[MAX_BPF_STACK + off]; + if (state->allocated_stack <= slot) { + verbose(env, "invalid read from stack off %d+0 size %d\n", + off, size); + return -EACCES; + } + stype = state->stack[spi].slot_type; - if (slot_type[0] == STACK_SPILL) { + if (stype[0] == STACK_SPILL) { if (size != BPF_REG_SIZE) { verbose(env, "invalid size of register spill\n"); return -EACCES; } for (i = 1; i < BPF_REG_SIZE; i++) { - if (slot_type[i] != STACK_SPILL) { + if (stype[(slot - i) % BPF_REG_SIZE] != STACK_SPILL) { verbose(env, "corrupted spill memory\n"); return -EACCES; } } - spi = (MAX_BPF_STACK + off) / BPF_REG_SIZE; - if (value_regno >= 0) { /* restore register state from stack */ - state->regs[value_regno] = state->spilled_regs[spi]; + state->regs[value_regno] = state->stack[spi].spilled_ptr; mark_stack_slot_read(state, spi); } return 0; } else { for (i = 0; i < size; i++) { - if (slot_type[i] != STACK_MISC) { + if (stype[(slot - i) % BPF_REG_SIZE] != STACK_MISC) { verbose(env, "invalid read from stack off %d+%d size %d\n", off, i, size); return -EACCES; @@ -694,7 +798,8 @@ static int check_stack_read(struct bpf_verifier_env *env, static int __check_map_access(struct bpf_verifier_env *env, u32 regno, int off, int size) { - struct bpf_map *map = env->cur_state.regs[regno].map_ptr; + struct bpf_reg_state *regs = cur_regs(env); + struct bpf_map *map = regs[regno].map_ptr; if (off < 0 || size <= 0 || off + size > map->value_size) { verbose(env, "invalid access to map value, value_size=%d off=%d size=%d\n", @@ -706,9 +811,9 @@ static int __check_map_access(struct bpf_verifier_env *env, u32 regno, int off, /* check read/write into a map element with possible variable offset */ static int check_map_access(struct bpf_verifier_env *env, u32 regno, - int off, int size) + int off, int size) { - struct bpf_verifier_state *state = &env->cur_state; + struct bpf_verifier_state *state = env->cur_state; struct bpf_reg_state *reg = &state->regs[regno]; int err; @@ -783,7 +888,7 @@ static bool may_access_direct_pkt_data(struct bpf_verifier_env *env, static int __check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, int size) { - struct bpf_reg_state *regs = env->cur_state.regs; + struct bpf_reg_state *regs = cur_regs(env); struct bpf_reg_state *reg = ®s[regno]; if (off < 0 || size <= 0 || (u64)off + size > reg->range) { @@ -797,7 +902,7 @@ static int __check_packet_access(struct bpf_verifier_env *env, u32 regno, static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off, int size) { - struct bpf_reg_state *regs = env->cur_state.regs; + struct bpf_reg_state *regs = cur_regs(env); struct bpf_reg_state *reg = ®s[regno]; int err; @@ -866,7 +971,7 @@ static bool __is_pointer_value(bool allow_ptr_leaks, static bool is_pointer_value(struct bpf_verifier_env *env, int regno) { - return __is_pointer_value(env->allow_ptr_leaks, &env->cur_state.regs[regno]); + return __is_pointer_value(env->allow_ptr_leaks, cur_regs(env) + regno); } static int check_pkt_ptr_alignment(struct bpf_verifier_env *env, @@ -968,8 +1073,9 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn int bpf_size, enum bpf_access_type t, int value_regno) { - struct bpf_verifier_state *state = &env->cur_state; - struct bpf_reg_state *reg = &state->regs[regno]; + struct bpf_verifier_state *state = env->cur_state; + struct bpf_reg_state *regs = cur_regs(env); + struct bpf_reg_state *reg = regs + regno; int size, err = 0; size = bpf_size_to_bytes(bpf_size); @@ -993,7 +1099,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn err = check_map_access(env, regno, off, size); if (!err && t == BPF_READ && value_regno >= 0) - mark_reg_unknown(env, state->regs, value_regno); + mark_reg_unknown(env, regs, value_regno); } else if (reg->type == PTR_TO_CTX) { enum bpf_reg_type reg_type = SCALAR_VALUE; @@ -1028,14 +1134,14 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn * case, we know the offset is zero. */ if (reg_type == SCALAR_VALUE) - mark_reg_unknown(env, state->regs, value_regno); + mark_reg_unknown(env, regs, value_regno); else - mark_reg_known_zero(env, state->regs, + mark_reg_known_zero(env, regs, value_regno); - state->regs[value_regno].id = 0; - state->regs[value_regno].off = 0; - state->regs[value_regno].range = 0; - state->regs[value_regno].type = reg_type; + regs[value_regno].id = 0; + regs[value_regno].off = 0; + regs[value_regno].range = 0; + regs[value_regno].type = reg_type; } } else if (reg->type == PTR_TO_STACK) { @@ -1061,19 +1167,12 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn if (env->prog->aux->stack_depth < -off) env->prog->aux->stack_depth = -off; - if (t == BPF_WRITE) { - if (!env->allow_ptr_leaks && - state->stack_slot_type[MAX_BPF_STACK + off] == STACK_SPILL && - size != BPF_REG_SIZE) { - verbose(env, "attempt to corrupt spilled pointer on stack\n"); - return -EACCES; - } + if (t == BPF_WRITE) err = check_stack_write(env, state, off, size, value_regno); - } else { + else err = check_stack_read(env, state, off, size, value_regno); - } } else if (reg_is_pkt_pointer(reg)) { if (t == BPF_WRITE && !may_access_direct_pkt_data(env, NULL, t)) { verbose(env, "cannot write into packet\n"); @@ -1087,7 +1186,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn } err = check_packet_access(env, regno, off, size); if (!err && t == BPF_READ && value_regno >= 0) - mark_reg_unknown(env, state->regs, value_regno); + mark_reg_unknown(env, regs, value_regno); } else { verbose(env, "R%d invalid mem access '%s'\n", regno, reg_type_str[reg->type]); @@ -1095,11 +1194,11 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn } if (!err && size < BPF_REG_SIZE && value_regno >= 0 && t == BPF_READ && - state->regs[value_regno].type == SCALAR_VALUE) { + regs[value_regno].type == SCALAR_VALUE) { /* b/h/w load zero-extends, mark upper bits as known 0 */ - state->regs[value_regno].var_off = tnum_cast( - state->regs[value_regno].var_off, size); - __update_reg_bounds(&state->regs[value_regno]); + regs[value_regno].var_off = + tnum_cast(regs[value_regno].var_off, size); + __update_reg_bounds(®s[value_regno]); } return err; } @@ -1156,9 +1255,9 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno, int access_size, bool zero_size_allowed, struct bpf_call_arg_meta *meta) { - struct bpf_verifier_state *state = &env->cur_state; + struct bpf_verifier_state *state = env->cur_state; struct bpf_reg_state *regs = state->regs; - int off, i; + int off, i, slot, spi; if (regs[regno].type != PTR_TO_STACK) { /* Allow zero-byte read from NULL, regardless of pointer type */ @@ -1198,7 +1297,11 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno, } for (i = 0; i < access_size; i++) { - if (state->stack_slot_type[MAX_BPF_STACK + off + i] != STACK_MISC) { + slot = -(off + i) - 1; + spi = slot / BPF_REG_SIZE; + if (state->allocated_stack <= slot || + state->stack[spi].slot_type[slot % BPF_REG_SIZE] != + STACK_MISC) { verbose(env, "invalid indirect read from stack off %d+%d size %d\n", off, i, access_size); return -EACCES; @@ -1211,7 +1314,7 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno, int access_size, bool zero_size_allowed, struct bpf_call_arg_meta *meta) { - struct bpf_reg_state *regs = env->cur_state.regs, *reg = ®s[regno]; + struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; switch (reg->type) { case PTR_TO_PACKET: @@ -1229,7 +1332,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno, enum bpf_arg_type arg_type, struct bpf_call_arg_meta *meta) { - struct bpf_reg_state *regs = env->cur_state.regs, *reg = ®s[regno]; + struct bpf_reg_state *regs = cur_regs(env), *reg = ®s[regno]; enum bpf_reg_type expected_type, type = reg->type; int err = 0; @@ -1514,7 +1617,7 @@ static int check_raw_mode(const struct bpf_func_proto *fn) */ static void clear_all_pkt_pointers(struct bpf_verifier_env *env) { - struct bpf_verifier_state *state = &env->cur_state; + struct bpf_verifier_state *state = env->cur_state; struct bpf_reg_state *regs = state->regs, *reg; int i; @@ -1522,10 +1625,10 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env) if (reg_is_pkt_pointer_any(®s[i])) mark_reg_unknown(env, regs, i); - for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { - if (state->stack_slot_type[i] != STACK_SPILL) + for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) { + if (state->stack[i].slot_type[0] != STACK_SPILL) continue; - reg = &state->spilled_regs[i / BPF_REG_SIZE]; + reg = &state->stack[i].spilled_ptr; if (reg_is_pkt_pointer_any(reg)) __mark_reg_unknown(reg); } @@ -1533,9 +1636,8 @@ static void clear_all_pkt_pointers(struct bpf_verifier_env *env) static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) { - struct bpf_verifier_state *state = &env->cur_state; const struct bpf_func_proto *fn = NULL; - struct bpf_reg_state *regs = state->regs; + struct bpf_reg_state *regs; struct bpf_call_arg_meta meta; bool changes_data; int i, err; @@ -1603,6 +1705,7 @@ static int check_call(struct bpf_verifier_env *env, int func_id, int insn_idx) return err; } + regs = cur_regs(env); /* reset caller saved regs */ for (i = 0; i < CALLER_SAVED_REGS; i++) { mark_reg_not_init(env, regs, caller_saved[i]); @@ -1691,7 +1794,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, const struct bpf_reg_state *ptr_reg, const struct bpf_reg_state *off_reg) { - struct bpf_reg_state *regs = env->cur_state.regs, *dst_reg; + struct bpf_reg_state *regs = cur_regs(env), *dst_reg; bool known = tnum_is_const(off_reg->var_off); s64 smin_val = off_reg->smin_value, smax_val = off_reg->smax_value, smin_ptr = ptr_reg->smin_value, smax_ptr = ptr_reg->smax_value; @@ -1703,13 +1806,13 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, dst_reg = ®s[dst]; if (WARN_ON_ONCE(known && (smin_val != smax_val))) { - print_verifier_state(env, &env->cur_state); + print_verifier_state(env, env->cur_state); verbose(env, "verifier internal error: known but bad sbounds\n"); return -EINVAL; } if (WARN_ON_ONCE(known && (umin_val != umax_val))) { - print_verifier_state(env, &env->cur_state); + print_verifier_state(env, env->cur_state); verbose(env, "verifier internal error: known but bad ubounds\n"); return -EINVAL; @@ -1890,7 +1993,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, struct bpf_reg_state *dst_reg, struct bpf_reg_state src_reg) { - struct bpf_reg_state *regs = env->cur_state.regs; + struct bpf_reg_state *regs = cur_regs(env); u8 opcode = BPF_OP(insn->code); bool src_known, dst_known; s64 smin_val, smax_val; @@ -2111,7 +2214,7 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env, static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn *insn) { - struct bpf_reg_state *regs = env->cur_state.regs, *dst_reg, *src_reg; + struct bpf_reg_state *regs = cur_regs(env), *dst_reg, *src_reg; struct bpf_reg_state *ptr_reg = NULL, off_reg = {0}; u8 opcode = BPF_OP(insn->code); int rc; @@ -2185,12 +2288,12 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, /* Got here implies adding two SCALAR_VALUEs */ if (WARN_ON_ONCE(ptr_reg)) { - print_verifier_state(env, &env->cur_state); + print_verifier_state(env, env->cur_state); verbose(env, "verifier internal error: unexpected ptr_reg\n"); return -EINVAL; } if (WARN_ON(!src_reg)) { - print_verifier_state(env, &env->cur_state); + print_verifier_state(env, env->cur_state); verbose(env, "verifier internal error: no src_reg\n"); return -EINVAL; } @@ -2200,7 +2303,7 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, /* check validity of 32-bit and 64-bit arithmetic operations */ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) { - struct bpf_reg_state *regs = env->cur_state.regs; + struct bpf_reg_state *regs = cur_regs(env); u8 opcode = BPF_OP(insn->code); int err; @@ -2421,10 +2524,10 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state, /* keep the maximum range already checked */ regs[i].range = max(regs[i].range, new_range); - for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { - if (state->stack_slot_type[i] != STACK_SPILL) + for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) { + if (state->stack[i].slot_type[0] != STACK_SPILL) continue; - reg = &state->spilled_regs[i / BPF_REG_SIZE]; + reg = &state->stack[i].spilled_ptr; if (reg->type == type && reg->id == dst_reg->id) reg->range = max_t(u16, reg->range, new_range); } @@ -2674,17 +2777,17 @@ static void mark_map_regs(struct bpf_verifier_state *state, u32 regno, for (i = 0; i < MAX_BPF_REG; i++) mark_map_reg(regs, i, id, is_null); - for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { - if (state->stack_slot_type[i] != STACK_SPILL) + for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) { + if (state->stack[i].slot_type[0] != STACK_SPILL) continue; - mark_map_reg(state->spilled_regs, i / BPF_REG_SIZE, id, is_null); + mark_map_reg(&state->stack[i].spilled_ptr, 0, id, is_null); } } static int check_cond_jmp_op(struct bpf_verifier_env *env, struct bpf_insn *insn, int *insn_idx) { - struct bpf_verifier_state *other_branch, *this_branch = &env->cur_state; + struct bpf_verifier_state *other_branch, *this_branch = env->cur_state; struct bpf_reg_state *regs = this_branch->regs, *dst_reg; u8 opcode = BPF_OP(insn->code); int err; @@ -2876,7 +2979,7 @@ static struct bpf_map *ld_imm64_to_map_ptr(struct bpf_insn *insn) /* verify BPF_LD_IMM64 instruction */ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn) { - struct bpf_reg_state *regs = env->cur_state.regs; + struct bpf_reg_state *regs = cur_regs(env); int err; if (BPF_SIZE(insn->code) != BPF_DW) { @@ -2937,7 +3040,7 @@ static bool may_access_skb(enum bpf_prog_type type) */ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn) { - struct bpf_reg_state *regs = env->cur_state.regs; + struct bpf_reg_state *regs = cur_regs(env); u8 mode = BPF_MODE(insn->code); int i, err; @@ -2999,7 +3102,7 @@ static int check_return_code(struct bpf_verifier_env *env) return 0; } - reg = &env->cur_state.regs[BPF_REG_0]; + reg = cur_regs(env) + BPF_REG_0; if (reg->type != SCALAR_VALUE) { verbose(env, "At program exit the register R0 is not a known value (%s)\n", reg_type_str[reg->type]); @@ -3363,6 +3466,57 @@ static bool regsafe(struct bpf_reg_state *rold, struct bpf_reg_state *rcur, return false; } +static bool stacksafe(struct bpf_verifier_state *old, + struct bpf_verifier_state *cur, + struct idpair *idmap) +{ + int i, spi; + + /* if explored stack has more populated slots than current stack + * such stacks are not equivalent + */ + if (old->allocated_stack > cur->allocated_stack) + return false; + + /* walk slots of the explored stack and ignore any additional + * slots in the current stack, since explored(safe) state + * didn't use them + */ + for (i = 0; i < old->allocated_stack; i++) { + spi = i / BPF_REG_SIZE; + + if (old->stack[spi].slot_type[i % BPF_REG_SIZE] == STACK_INVALID) + continue; + if (old->stack[spi].slot_type[i % BPF_REG_SIZE] != + cur->stack[spi].slot_type[i % BPF_REG_SIZE]) + /* Ex: old explored (safe) state has STACK_SPILL in + * this stack slot, but current has has STACK_MISC -> + * this verifier states are not equivalent, + * return false to continue verification of this path + */ + return false; + if (i % BPF_REG_SIZE) + continue; + if (old->stack[spi].slot_type[0] != STACK_SPILL) + continue; + if (!regsafe(&old->stack[spi].spilled_ptr, + &cur->stack[spi].spilled_ptr, + idmap)) + /* when explored and current stack slot are both storing + * spilled registers, check that stored pointers types + * are the same as well. + * Ex: explored safe path could have stored + * (bpf_reg_state) {.type = PTR_TO_STACK, .off = -8} + * but current path has stored: + * (bpf_reg_state) {.type = PTR_TO_STACK, .off = -16} + * such verifier states are not equivalent. + * return false to continue verification of this path + */ + return false; + } + return true; +} + /* compare two verifier states * * all states stored in state_list are known to be valid, since @@ -3407,37 +3561,8 @@ static bool states_equal(struct bpf_verifier_env *env, goto out_free; } - for (i = 0; i < MAX_BPF_STACK; i++) { - if (old->stack_slot_type[i] == STACK_INVALID) - continue; - if (old->stack_slot_type[i] != cur->stack_slot_type[i]) - /* Ex: old explored (safe) state has STACK_SPILL in - * this stack slot, but current has has STACK_MISC -> - * this verifier states are not equivalent, - * return false to continue verification of this path - */ - goto out_free; - if (i % BPF_REG_SIZE) - continue; - if (old->stack_slot_type[i] != STACK_SPILL) - continue; - if (!regsafe(&old->spilled_regs[i / BPF_REG_SIZE], - &cur->spilled_regs[i / BPF_REG_SIZE], - idmap)) - /* when explored and current stack slot are both storing - * spilled registers, check that stored pointers types - * are the same as well. - * Ex: explored safe path could have stored - * (bpf_reg_state) {.type = PTR_TO_STACK, .off = -8} - * but current path has stored: - * (bpf_reg_state) {.type = PTR_TO_STACK, .off = -16} - * such verifier states are not equivalent. - * return false to continue verification of this path - */ - goto out_free; - else - continue; - } + if (!stacksafe(old, cur, idmap)) + goto out_free; ret = true; out_free: kfree(idmap); @@ -3473,17 +3598,19 @@ static bool do_propagate_liveness(const struct bpf_verifier_state *state, } } /* ... and stack slots */ - for (i = 0; i < MAX_BPF_STACK / BPF_REG_SIZE; i++) { - if (parent->stack_slot_type[i * BPF_REG_SIZE] != STACK_SPILL) + for (i = 0; i < state->allocated_stack / BPF_REG_SIZE && + i < parent->allocated_stack / BPF_REG_SIZE; i++) { + if (parent->stack[i].slot_type[0] != STACK_SPILL) continue; - if (state->stack_slot_type[i * BPF_REG_SIZE] != STACK_SPILL) + if (state->stack[i].slot_type[0] != STACK_SPILL) continue; - if (parent->spilled_regs[i].live & REG_LIVE_READ) + if (parent->stack[i].spilled_ptr.live & REG_LIVE_READ) continue; - if (writes && (state->spilled_regs[i].live & REG_LIVE_WRITTEN)) + if (writes && + (state->stack[i].spilled_ptr.live & REG_LIVE_WRITTEN)) continue; - if (state->spilled_regs[i].live & REG_LIVE_READ) { - parent->spilled_regs[i].live |= REG_LIVE_READ; + if (state->stack[i].spilled_ptr.live & REG_LIVE_READ) { + parent->stack[i].spilled_ptr.live |= REG_LIVE_READ; touched = true; } } @@ -3513,6 +3640,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) { struct bpf_verifier_state_list *new_sl; struct bpf_verifier_state_list *sl; + struct bpf_verifier_state *cur = env->cur_state; int i; sl = env->explored_states[insn_idx]; @@ -3523,7 +3651,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) return 0; while (sl != STATE_LIST_MARK) { - if (states_equal(env, &sl->state, &env->cur_state)) { + if (states_equal(env, &sl->state, cur)) { /* reached equivalent register/stack state, * prune the search. * Registers read by the continuation are read by us. @@ -3534,7 +3662,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) * they'll be immediately forgotten as we're pruning * this state and will pop a new one. */ - propagate_liveness(&sl->state, &env->cur_state); + propagate_liveness(&sl->state, cur); return 1; } sl = sl->next; @@ -3546,16 +3674,16 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) * it will be rejected. Since there are no loops, we won't be * seeing this 'insn_idx' instruction again on the way to bpf_exit */ - new_sl = kmalloc(sizeof(struct bpf_verifier_state_list), GFP_USER); + new_sl = kzalloc(sizeof(struct bpf_verifier_state_list), GFP_KERNEL); if (!new_sl) return -ENOMEM; /* add new state to the head of linked list */ - memcpy(&new_sl->state, &env->cur_state, sizeof(env->cur_state)); + copy_verifier_state(&new_sl->state, cur); new_sl->next = env->explored_states[insn_idx]; env->explored_states[insn_idx] = new_sl; /* connect new state to parentage chain */ - env->cur_state.parent = &new_sl->state; + cur->parent = &new_sl->state; /* clear write marks in current state: the writes we did are not writes * our child did, so they don't screen off its reads from us. * (There are no read marks in current state, because reads always mark @@ -3563,10 +3691,10 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) * explored_states can get read marks.) */ for (i = 0; i < BPF_REG_FP; i++) - env->cur_state.regs[i].live = REG_LIVE_NONE; - for (i = 0; i < MAX_BPF_STACK / BPF_REG_SIZE; i++) - if (env->cur_state.stack_slot_type[i * BPF_REG_SIZE] == STACK_SPILL) - env->cur_state.spilled_regs[i].live = REG_LIVE_NONE; + cur->regs[i].live = REG_LIVE_NONE; + for (i = 0; i < cur->allocated_stack / BPF_REG_SIZE; i++) + if (cur->stack[i].slot_type[0] == STACK_SPILL) + cur->stack[i].spilled_ptr.live = REG_LIVE_NONE; return 0; } @@ -3581,15 +3709,19 @@ static int ext_analyzer_insn_hook(struct bpf_verifier_env *env, static int do_check(struct bpf_verifier_env *env) { - struct bpf_verifier_state *state = &env->cur_state; + struct bpf_verifier_state *state; struct bpf_insn *insns = env->prog->insnsi; - struct bpf_reg_state *regs = state->regs; + struct bpf_reg_state *regs; int insn_cnt = env->prog->len; int insn_idx, prev_insn_idx = 0; int insn_processed = 0; bool do_print_state = false; - init_reg_state(env, regs); + state = kzalloc(sizeof(struct bpf_verifier_state), GFP_KERNEL); + if (!state) + return -ENOMEM; + env->cur_state = state; + init_reg_state(env, state->regs); state->parent = NULL; insn_idx = 0; for (;;) { @@ -3637,7 +3769,7 @@ static int do_check(struct bpf_verifier_env *env) else verbose(env, "\nfrom %d to %d:", prev_insn_idx, insn_idx); - print_verifier_state(env, &env->cur_state); + print_verifier_state(env, state); do_print_state = false; } @@ -3651,6 +3783,7 @@ static int do_check(struct bpf_verifier_env *env) if (err) return err; + regs = cur_regs(env); if (class == BPF_ALU || class == BPF_ALU64) { err = check_alu_op(env, insn); if (err) @@ -3818,8 +3951,10 @@ static int do_check(struct bpf_verifier_env *env) if (err) return err; process_bpf_exit: - insn_idx = pop_stack(env, &prev_insn_idx); - if (insn_idx < 0) { + err = pop_stack(env, &prev_insn_idx, &insn_idx); + if (err < 0) { + if (err != -ENOENT) + return err; break; } else { do_print_state = true; @@ -4359,9 +4494,11 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) env->allow_ptr_leaks = capable(CAP_SYS_ADMIN); ret = do_check(env); + free_verifier_state(env->cur_state); + env->cur_state = NULL; skip_full_check: - while (pop_stack(env, NULL) >= 0); + while (!pop_stack(env, NULL, NULL)); free_states(env); if (ret == 0) @@ -4464,9 +4601,11 @@ int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, env->allow_ptr_leaks = capable(CAP_SYS_ADMIN); ret = do_check(env); + free_verifier_state(env->cur_state); + env->cur_state = NULL; skip_full_check: - while (pop_stack(env, NULL) >= 0); + while (!pop_stack(env, NULL, NULL)); free_states(env); mutex_unlock(&bpf_verifier_lock); -- cgit v1.2.3-59-g8ed1b From 6c31e5a91fde2e718e59c8a627c56451f88be54c Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 27 Oct 2017 17:37:13 -0700 Subject: net: Add extack to fib_notifier_info Add extack to fib_notifier_info and plumb through stack to call_fib_rule_notifiers, call_fib_entry_notifiers and call_fib6_entry_notifiers. This allows notifer handlers to return messages to user. Signed-off-by: David Ahern Reviewed-by: Ido Schimmel Signed-off-by: David S. Miller --- include/net/fib_notifier.h | 1 + net/core/fib_rules.c | 9 ++++++--- net/ipv4/fib_trie.c | 13 ++++++++----- net/ipv6/ip6_fib.c | 15 +++++++++------ 4 files changed, 24 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/net/fib_notifier.h b/include/net/fib_notifier.h index 54cd6b839d2f..c91ec732afd6 100644 --- a/include/net/fib_notifier.h +++ b/include/net/fib_notifier.h @@ -9,6 +9,7 @@ struct fib_notifier_info { struct net *net; int family; + struct netlink_ext_ack *extack; }; enum fib_event_type { diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 9a6d97c1d810..fafd0a41e3f7 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -314,10 +314,12 @@ static int call_fib_rule_notifier(struct notifier_block *nb, struct net *net, static int call_fib_rule_notifiers(struct net *net, enum fib_event_type event_type, struct fib_rule *rule, - struct fib_rules_ops *ops) + struct fib_rules_ops *ops, + struct netlink_ext_ack *extack) { struct fib_rule_notifier_info info = { .info.family = ops->family, + .info.extack = extack, .rule = rule, }; @@ -609,7 +611,7 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, if (rule->tun_id) ip_tunnel_need_metadata(); - call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD, rule, ops); + call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD, rule, ops, extack); notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid); flush_route_cache(ops); rules_ops_put(ops); @@ -749,7 +751,8 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, } } - call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops); + call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops, + NULL); notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).portid); fib_rule_put(rule); diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index aaa1ba09afaa..5ddc4aefff12 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -102,9 +102,11 @@ static int call_fib_entry_notifier(struct notifier_block *nb, struct net *net, static int call_fib_entry_notifiers(struct net *net, enum fib_event_type event_type, u32 dst, - int dst_len, struct fib_alias *fa) + int dst_len, struct fib_alias *fa, + struct netlink_ext_ack *extack) { struct fib_entry_notifier_info info = { + .info.extack = extack, .dst = dst, .dst_len = dst_len, .fi = fa->fa_info, @@ -1214,7 +1216,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb, new_fa->fa_default = -1; call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_REPLACE, - key, plen, new_fa); + key, plen, new_fa, extack); rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id, &cfg->fc_nlinfo, nlflags); @@ -1269,7 +1271,7 @@ int fib_table_insert(struct net *net, struct fib_table *tb, tb->tb_num_default++; rt_cache_flush(cfg->fc_nlinfo.nl_net); - call_fib_entry_notifiers(net, event, key, plen, new_fa); + call_fib_entry_notifiers(net, event, key, plen, new_fa, extack); rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id, &cfg->fc_nlinfo, nlflags); succeeded: @@ -1569,7 +1571,7 @@ int fib_table_delete(struct net *net, struct fib_table *tb, return -ESRCH; call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, key, plen, - fa_to_delete); + fa_to_delete, extack); rtmsg_fib(RTM_DELROUTE, htonl(key), fa_to_delete, plen, tb->tb_id, &cfg->fc_nlinfo, 0); @@ -1886,7 +1888,8 @@ int fib_table_flush(struct net *net, struct fib_table *tb) call_fib_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, n->key, - KEYLENGTH - fa->fa_slen, fa); + KEYLENGTH - fa->fa_slen, fa, + NULL); hlist_del_rcu(&fa->fa_list); fib_release_info(fa->fa_info); alias_free_mem_rcu(fa); diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 1ada9672d198..2e2804f5823e 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -353,9 +353,11 @@ static int call_fib6_entry_notifier(struct notifier_block *nb, struct net *net, static int call_fib6_entry_notifiers(struct net *net, enum fib_event_type event_type, - struct rt6_info *rt) + struct rt6_info *rt, + struct netlink_ext_ack *extack) { struct fib6_entry_notifier_info info = { + .info.extack = extack, .rt = rt, }; @@ -868,7 +870,8 @@ static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn, */ static int fib6_add_rt2node(struct fib6_node *fn, struct rt6_info *rt, - struct nl_info *info, struct mx6_config *mxc) + struct nl_info *info, struct mx6_config *mxc, + struct netlink_ext_ack *extack) { struct rt6_info *leaf = rcu_dereference_protected(fn->leaf, lockdep_is_held(&rt->rt6i_table->tb6_lock)); @@ -1011,7 +1014,7 @@ add: rcu_assign_pointer(rt->rt6i_node, fn); rcu_assign_pointer(*ins, rt); call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_ADD, - rt); + rt, extack); if (!info->skip_notify) inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); info->nl_net->ipv6.rt6_stats->fib_rt_entries++; @@ -1040,7 +1043,7 @@ add: rt->dst.rt6_next = iter->dst.rt6_next; rcu_assign_pointer(*ins, rt); call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_REPLACE, - rt); + rt, extack); if (!info->skip_notify) inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE); if (!(fn->fn_flags & RTN_RTINFO)) { @@ -1225,7 +1228,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, } #endif - err = fib6_add_rt2node(fn, rt, info, mxc); + err = fib6_add_rt2node(fn, rt, info, mxc, extack); if (!err) { fib6_update_sernum_upto_root(rt, sernum); fib6_start_gc(info->nl_net, rt); @@ -1686,7 +1689,7 @@ static void fib6_del_route(struct fib6_table *table, struct fib6_node *fn, fib6_purge_rt(rt, fn, net); - call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt); + call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt, NULL); if (!info->skip_notify) inet6_rt_notify(RTM_DELROUTE, rt, info, 0); rt6_release(rt); -- cgit v1.2.3-59-g8ed1b From da13c59b9936dfedcf9f2203bd29fbf83ad672bf Mon Sep 17 00:00:00 2001 From: Vishwanath Pai Date: Mon, 30 Oct 2017 19:38:52 -0400 Subject: net: display hw address of source machine during ipv6 DAD failure This patch updates the error messages displayed in kernel log to include hwaddress of the source machine that caused ipv6 duplicate address detection failures. Examples: a) When we receive a NA packet from another machine advertising our address: ICMPv6: NA: 34:ab:cd:56:11:e8 advertised our address 2001:db8:: on eth0! b) When we detect DAD failure during address assignment to an interface: IPv6: eth0: IPv6 duplicate address 2001:db8:: used by 34:ab:cd:56:11:e8 detected! v2: Changed %pI6 to %pI6c in ndisc_recv_na() Chaged the v6 address in the commit message to 2001:db8:: Suggested-by: Igor Lubashev Signed-off-by: Vishwanath Pai Acked-by: David Ahern Signed-off-by: David S. Miller --- include/net/addrconf.h | 2 +- net/ipv6/addrconf.c | 6 +++--- net/ipv6/ndisc.c | 9 +++++---- 3 files changed, 9 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 15b5ffd7253d..2a616ea53956 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -208,7 +208,7 @@ void ipv6_mc_remap(struct inet6_dev *idev); void ipv6_mc_init_dev(struct inet6_dev *idev); void ipv6_mc_destroy_dev(struct inet6_dev *idev); int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed); -void addrconf_dad_failure(struct inet6_ifaddr *ifp); +void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp); bool ipv6_chk_mcast_addr(struct net_device *dev, const struct in6_addr *group, const struct in6_addr *src_addr); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 5a8a10229a07..cfa374c8b54c 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1987,7 +1987,7 @@ static int addrconf_dad_end(struct inet6_ifaddr *ifp) return err; } -void addrconf_dad_failure(struct inet6_ifaddr *ifp) +void addrconf_dad_failure(struct sk_buff *skb, struct inet6_ifaddr *ifp) { struct inet6_dev *idev = ifp->idev; struct net *net = dev_net(ifp->idev->dev); @@ -1997,8 +1997,8 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp) return; } - net_info_ratelimited("%s: IPv6 duplicate address %pI6c detected!\n", - ifp->idev->dev->name, &ifp->addr); + net_info_ratelimited("%s: IPv6 duplicate address %pI6c used by %pM detected!\n", + ifp->idev->dev->name, &ifp->addr, eth_hdr(skb)->h_source); spin_lock_bh(&ifp->lock); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 266a530414d7..f9c3ffe04382 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -46,6 +46,7 @@ #endif #include +#include #include #include #include @@ -822,7 +823,7 @@ have_ifp: * who is doing DAD * so fail our DAD process */ - addrconf_dad_failure(ifp); + addrconf_dad_failure(skb, ifp); return; } else { /* @@ -975,7 +976,7 @@ static void ndisc_recv_na(struct sk_buff *skb) if (ifp) { if (skb->pkt_type != PACKET_LOOPBACK && (ifp->flags & IFA_F_TENTATIVE)) { - addrconf_dad_failure(ifp); + addrconf_dad_failure(skb, ifp); return; } /* What should we make now? The advertisement @@ -989,8 +990,8 @@ static void ndisc_recv_na(struct sk_buff *skb) */ if (skb->pkt_type != PACKET_LOOPBACK) ND_PRINTK(1, warn, - "NA: someone advertises our address %pI6 on %s!\n", - &ifp->addr, ifp->idev->dev->name); + "NA: %pM advertised our address %pI6c on %s!\n", + eth_hdr(skb)->h_source, &ifp->addr, ifp->idev->dev->name); in6_ifa_put(ifp); return; } -- cgit v1.2.3-59-g8ed1b From e9292f2c03851ef81bef38579a0ee9c42140e586 Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Tue, 31 Oct 2017 15:48:01 +0100 Subject: net: dsa: lan9303: Add STP ALR entry on port 0 STP BPDUs arriving on user ports must sent to CPU port only, for processing by the SW bridge. Add an ALR entry with STP state override to fix that. Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 2 ++ include/linux/dsa/lan9303.h | 2 ++ net/dsa/tag_lan9303.c | 1 - 3 files changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 4c412bd52319..c4afc8f1a66d 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -773,6 +773,7 @@ static int lan9303_separate_ports(struct lan9303 *chip) { int ret; + lan9303_alr_del_port(chip, eth_stp_addr, 0); ret = lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_MIRROR, LAN9303_SWE_PORT_MIRROR_SNIFFER_PORT0 | LAN9303_SWE_PORT_MIRROR_MIRRORED_PORT1 | @@ -797,6 +798,7 @@ static void lan9303_bridge_ports(struct lan9303 *chip) lan9303_write_switch_reg(chip, LAN9303_SWE_PORT_STATE, chip->swe_port_state); + lan9303_alr_add_port(chip, eth_stp_addr, 0, true); } static int lan9303_handle_reset(struct lan9303 *chip) diff --git a/include/linux/dsa/lan9303.h b/include/linux/dsa/lan9303.h index 05d8d136baab..b2110e69630f 100644 --- a/include/linux/dsa/lan9303.h +++ b/include/linux/dsa/lan9303.h @@ -34,3 +34,5 @@ struct lan9303 { **/ struct lan9303_alr_cache_entry alr_cache[LAN9303_NUM_ALR_RECORDS]; }; + +#define eth_stp_addr eth_reserved_addr_base diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c index 537ca991fafe..18f45cd9f625 100644 --- a/net/dsa/tag_lan9303.c +++ b/net/dsa/tag_lan9303.c @@ -42,7 +42,6 @@ #define LAN9303_TAG_LEN 4 # define LAN9303_TAG_TX_USE_ALR BIT(3) # define LAN9303_TAG_TX_STP_OVERRIDE BIT(4) -#define eth_stp_addr eth_reserved_addr_base /* Decide whether to transmit using ALR lookup, or transmit directly to * port using tag. ALR learning is performed only when using ALR lookup. -- cgit v1.2.3-59-g8ed1b From 7930d0a00ff5dbcc80f793d1a7a6b8de4e591f1a Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 14 Oct 2017 17:22:27 +0800 Subject: sbitmap: introduce __sbitmap_for_each_set() For blk-mq, we need to be able to iterate software queues starting from any queue in a round robin fashion, so introduce this helper. Reviewed-by: Omar Sandoval Cc: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- include/linux/sbitmap.h | 64 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/linux/sbitmap.h b/include/linux/sbitmap.h index a1904aadbc45..0dcc60e820de 100644 --- a/include/linux/sbitmap.h +++ b/include/linux/sbitmap.h @@ -211,10 +211,14 @@ bool sbitmap_any_bit_set(const struct sbitmap *sb); */ bool sbitmap_any_bit_clear(const struct sbitmap *sb); +#define SB_NR_TO_INDEX(sb, bitnr) ((bitnr) >> (sb)->shift) +#define SB_NR_TO_BIT(sb, bitnr) ((bitnr) & ((1U << (sb)->shift) - 1U)) + typedef bool (*sb_for_each_fn)(struct sbitmap *, unsigned int, void *); /** - * sbitmap_for_each_set() - Iterate over each set bit in a &struct sbitmap. + * __sbitmap_for_each_set() - Iterate over each set bit in a &struct sbitmap. + * @start: Where to start the iteration. * @sb: Bitmap to iterate over. * @fn: Callback. Should return true to continue or false to break early. * @data: Pointer to pass to callback. @@ -222,35 +226,61 @@ typedef bool (*sb_for_each_fn)(struct sbitmap *, unsigned int, void *); * This is inline even though it's non-trivial so that the function calls to the * callback will hopefully get optimized away. */ -static inline void sbitmap_for_each_set(struct sbitmap *sb, sb_for_each_fn fn, - void *data) +static inline void __sbitmap_for_each_set(struct sbitmap *sb, + unsigned int start, + sb_for_each_fn fn, void *data) { - unsigned int i; + unsigned int index; + unsigned int nr; + unsigned int scanned = 0; - for (i = 0; i < sb->map_nr; i++) { - struct sbitmap_word *word = &sb->map[i]; - unsigned int off, nr; + if (start >= sb->depth) + start = 0; + index = SB_NR_TO_INDEX(sb, start); + nr = SB_NR_TO_BIT(sb, start); - if (!word->word) - continue; + while (scanned < sb->depth) { + struct sbitmap_word *word = &sb->map[index]; + unsigned int depth = min_t(unsigned int, word->depth - nr, + sb->depth - scanned); - nr = 0; - off = i << sb->shift; + scanned += depth; + if (!word->word) + goto next; + + /* + * On the first iteration of the outer loop, we need to add the + * bit offset back to the size of the word for find_next_bit(). + * On all other iterations, nr is zero, so this is a noop. + */ + depth += nr; while (1) { - nr = find_next_bit(&word->word, word->depth, nr); - if (nr >= word->depth) + nr = find_next_bit(&word->word, depth, nr); + if (nr >= depth) break; - - if (!fn(sb, off + nr, data)) + if (!fn(sb, (index << sb->shift) + nr, data)) return; nr++; } +next: + nr = 0; + if (++index >= sb->map_nr) + index = 0; } } -#define SB_NR_TO_INDEX(sb, bitnr) ((bitnr) >> (sb)->shift) -#define SB_NR_TO_BIT(sb, bitnr) ((bitnr) & ((1U << (sb)->shift) - 1U)) +/** + * sbitmap_for_each_set() - Iterate over each set bit in a &struct sbitmap. + * @sb: Bitmap to iterate over. + * @fn: Callback. Should return true to continue or false to break early. + * @data: Pointer to pass to callback. + */ +static inline void sbitmap_for_each_set(struct sbitmap *sb, sb_for_each_fn fn, + void *data) +{ + __sbitmap_for_each_set(sb, 0, fn, data); +} static inline unsigned long *__sbitmap_word(struct sbitmap *sb, unsigned int bitnr) -- cgit v1.2.3-59-g8ed1b From de1482974080ec9ef414bf048b2646b246b63f6e Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 14 Oct 2017 17:22:29 +0800 Subject: blk-mq: introduce .get_budget and .put_budget in blk_mq_ops For SCSI devices, there is often a per-request-queue depth, which needs to be respected before queuing one request. Currently blk-mq always dequeues the request first, then calls .queue_rq() to dispatch the request to lld. One obvious issue with this approach is that I/O merging may not be successful, because when the per-request-queue depth can't be respected, .queue_rq() has to return BLK_STS_RESOURCE, and then this request has to stay in hctx->dispatch list. This means it never gets a chance to be merged with other IO. This patch introduces .get_budget and .put_budget callback in blk_mq_ops, then we can try to get reserved budget first before dequeuing request. If the budget for queueing I/O can't be satisfied, we don't need to dequeue request at all. Hence the request can be left in the IO scheduler queue, for more merging opportunities. Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- block/blk-mq-sched.c | 55 +++++++++++++++++++++++++++++++++++++++++--------- block/blk-mq-sched.h | 2 +- block/blk-mq.c | 43 ++++++++++++++++++++++++++++++++++----- block/blk-mq.h | 20 +++++++++++++++++- include/linux/blk-mq.h | 11 ++++++++++ 5 files changed, 114 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c index be29ba849408..8e525e66a0d9 100644 --- a/block/blk-mq-sched.c +++ b/block/blk-mq-sched.c @@ -89,31 +89,57 @@ static bool blk_mq_sched_restart_hctx(struct blk_mq_hw_ctx *hctx) return false; } -static void blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx) +/* return true if hctx need to run again */ +static bool blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx) { struct request_queue *q = hctx->queue; struct elevator_queue *e = q->elevator; LIST_HEAD(rq_list); do { - struct request *rq = e->type->ops.mq.dispatch_request(hctx); + struct request *rq; + blk_status_t ret; - if (!rq) + if (e->type->ops.mq.has_work && + !e->type->ops.mq.has_work(hctx)) break; + + ret = blk_mq_get_dispatch_budget(hctx); + if (ret == BLK_STS_RESOURCE) + return true; + + rq = e->type->ops.mq.dispatch_request(hctx); + if (!rq) { + blk_mq_put_dispatch_budget(hctx); + break; + } else if (ret != BLK_STS_OK) { + blk_mq_end_request(rq, ret); + continue; + } + + /* + * Now this rq owns the budget which has to be released + * if this rq won't be queued to driver via .queue_rq() + * in blk_mq_dispatch_rq_list(). + */ list_add(&rq->queuelist, &rq_list); - } while (blk_mq_dispatch_rq_list(q, &rq_list)); + } while (blk_mq_dispatch_rq_list(q, &rq_list, true)); + + return false; } -void blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx) +/* return true if hw queue need to be run again */ +bool blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx) { struct request_queue *q = hctx->queue; struct elevator_queue *e = q->elevator; const bool has_sched_dispatch = e && e->type->ops.mq.dispatch_request; LIST_HEAD(rq_list); + bool run_queue = false; /* RCU or SRCU read lock is needed before checking quiesced flag */ if (unlikely(blk_mq_hctx_stopped(hctx) || blk_queue_quiesced(q))) - return; + return false; hctx->run++; @@ -143,14 +169,23 @@ void blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx) */ if (!list_empty(&rq_list)) { blk_mq_sched_mark_restart_hctx(hctx); - if (blk_mq_dispatch_rq_list(q, &rq_list) && has_sched_dispatch) - blk_mq_do_dispatch_sched(hctx); + if (blk_mq_dispatch_rq_list(q, &rq_list, false) && + has_sched_dispatch) + run_queue = blk_mq_do_dispatch_sched(hctx); } else if (has_sched_dispatch) { - blk_mq_do_dispatch_sched(hctx); + run_queue = blk_mq_do_dispatch_sched(hctx); } else { blk_mq_flush_busy_ctxs(hctx, &rq_list); - blk_mq_dispatch_rq_list(q, &rq_list); + blk_mq_dispatch_rq_list(q, &rq_list, false); } + + if (run_queue && !blk_mq_sched_needs_restart(hctx) && + !test_bit(BLK_MQ_S_TAG_WAITING, &hctx->state)) { + blk_mq_sched_mark_restart_hctx(hctx); + return true; + } + + return false; } bool blk_mq_sched_try_merge(struct request_queue *q, struct bio *bio, diff --git a/block/blk-mq-sched.h b/block/blk-mq-sched.h index 9267d0b7c197..2434061cc5b7 100644 --- a/block/blk-mq-sched.h +++ b/block/blk-mq-sched.h @@ -22,7 +22,7 @@ void blk_mq_sched_insert_requests(struct request_queue *q, struct blk_mq_ctx *ctx, struct list_head *list, bool run_queue_async); -void blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx); +bool blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx); int blk_mq_init_sched(struct request_queue *q, struct elevator_type *e); void blk_mq_exit_sched(struct request_queue *q, struct elevator_queue *e); diff --git a/block/blk-mq.c b/block/blk-mq.c index 40cba1b1978f..dcb467369999 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1048,7 +1048,8 @@ static bool blk_mq_dispatch_wait_add(struct blk_mq_hw_ctx *hctx) return true; } -bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list) +bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, + bool got_budget) { struct blk_mq_hw_ctx *hctx; struct request *rq; @@ -1057,6 +1058,8 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list) if (list_empty(list)) return false; + WARN_ON(!list_is_singular(list) && got_budget); + /* * Now process all the entries, sending them to the driver. */ @@ -1074,16 +1077,30 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list) * The initial allocation attempt failed, so we need to * rerun the hardware queue when a tag is freed. */ - if (!blk_mq_dispatch_wait_add(hctx)) + if (!blk_mq_dispatch_wait_add(hctx)) { + if (got_budget) + blk_mq_put_dispatch_budget(hctx); break; + } /* * It's possible that a tag was freed in the window * between the allocation failure and adding the * hardware queue to the wait queue. */ - if (!blk_mq_get_driver_tag(rq, &hctx, false)) + if (!blk_mq_get_driver_tag(rq, &hctx, false)) { + if (got_budget) + blk_mq_put_dispatch_budget(hctx); + break; + } + } + + if (!got_budget) { + ret = blk_mq_get_dispatch_budget(hctx); + if (ret == BLK_STS_RESOURCE) break; + if (ret != BLK_STS_OK) + goto fail_rq; } list_del_init(&rq->queuelist); @@ -1111,6 +1128,7 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list) break; } + fail_rq: if (unlikely(ret != BLK_STS_OK)) { errors++; blk_mq_end_request(rq, BLK_STS_IOERR); @@ -1169,6 +1187,7 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list) static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) { int srcu_idx; + bool run_queue; /* * We should be running this queue from one of the CPUs that @@ -1185,15 +1204,18 @@ static void __blk_mq_run_hw_queue(struct blk_mq_hw_ctx *hctx) if (!(hctx->flags & BLK_MQ_F_BLOCKING)) { rcu_read_lock(); - blk_mq_sched_dispatch_requests(hctx); + run_queue = blk_mq_sched_dispatch_requests(hctx); rcu_read_unlock(); } else { might_sleep(); srcu_idx = srcu_read_lock(hctx->queue_rq_srcu); - blk_mq_sched_dispatch_requests(hctx); + run_queue = blk_mq_sched_dispatch_requests(hctx); srcu_read_unlock(hctx->queue_rq_srcu, srcu_idx); } + + if (run_queue) + blk_mq_run_hw_queue(hctx, true); } /* @@ -1582,6 +1604,13 @@ static void __blk_mq_try_issue_directly(struct blk_mq_hw_ctx *hctx, if (!blk_mq_get_driver_tag(rq, NULL, false)) goto insert; + ret = blk_mq_get_dispatch_budget(hctx); + if (ret == BLK_STS_RESOURCE) { + blk_mq_put_driver_tag(rq); + goto insert; + } else if (ret != BLK_STS_OK) + goto fail_rq; + new_cookie = request_to_qc_t(hctx, rq); /* @@ -1598,6 +1627,7 @@ static void __blk_mq_try_issue_directly(struct blk_mq_hw_ctx *hctx, __blk_mq_requeue_request(rq); goto insert; default: + fail_rq: *cookie = BLK_QC_T_NONE; blk_mq_end_request(rq, ret); return; @@ -2582,6 +2612,9 @@ int blk_mq_alloc_tag_set(struct blk_mq_tag_set *set) if (!set->ops->queue_rq) return -EINVAL; + if (!set->ops->get_budget ^ !set->ops->put_budget) + return -EINVAL; + if (set->queue_depth > BLK_MQ_MAX_DEPTH) { pr_info("blk-mq: reduced tag depth to %u\n", BLK_MQ_MAX_DEPTH); diff --git a/block/blk-mq.h b/block/blk-mq.h index ef15b3414da5..e413b732374e 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -30,7 +30,7 @@ void blk_mq_freeze_queue(struct request_queue *q); void blk_mq_free_queue(struct request_queue *q); int blk_mq_update_nr_requests(struct request_queue *q, unsigned int nr); void blk_mq_wake_waiters(struct request_queue *q); -bool blk_mq_dispatch_rq_list(struct request_queue *, struct list_head *); +bool blk_mq_dispatch_rq_list(struct request_queue *, struct list_head *, bool); void blk_mq_flush_busy_ctxs(struct blk_mq_hw_ctx *hctx, struct list_head *list); bool blk_mq_hctx_has_pending(struct blk_mq_hw_ctx *hctx); bool blk_mq_get_driver_tag(struct request *rq, struct blk_mq_hw_ctx **hctx, @@ -137,4 +137,22 @@ static inline bool blk_mq_hw_queue_mapped(struct blk_mq_hw_ctx *hctx) void blk_mq_in_flight(struct request_queue *q, struct hd_struct *part, unsigned int inflight[2]); +static inline void blk_mq_put_dispatch_budget(struct blk_mq_hw_ctx *hctx) +{ + struct request_queue *q = hctx->queue; + + if (q->mq_ops->put_budget) + q->mq_ops->put_budget(hctx); +} + +static inline blk_status_t blk_mq_get_dispatch_budget( + struct blk_mq_hw_ctx *hctx) +{ + struct request_queue *q = hctx->queue; + + if (q->mq_ops->get_budget) + return q->mq_ops->get_budget(hctx); + return BLK_STS_OK; +} + #endif diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 50c6485cb04f..901457df3d64 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -90,6 +90,8 @@ struct blk_mq_queue_data { typedef blk_status_t (queue_rq_fn)(struct blk_mq_hw_ctx *, const struct blk_mq_queue_data *); +typedef blk_status_t (get_budget_fn)(struct blk_mq_hw_ctx *); +typedef void (put_budget_fn)(struct blk_mq_hw_ctx *); typedef enum blk_eh_timer_return (timeout_fn)(struct request *, bool); typedef int (init_hctx_fn)(struct blk_mq_hw_ctx *, void *, unsigned int); typedef void (exit_hctx_fn)(struct blk_mq_hw_ctx *, unsigned int); @@ -111,6 +113,15 @@ struct blk_mq_ops { */ queue_rq_fn *queue_rq; + /* + * Reserve budget before queue request, once .queue_rq is + * run, it is driver's responsibility to release the + * reserved budget. Also we have to handle failure case + * of .get_budget for avoiding I/O deadlock. + */ + get_budget_fn *get_budget; + put_budget_fn *put_budget; + /* * Called on request timeout */ -- cgit v1.2.3-59-g8ed1b From b347689ffbca745ac457ee27400ce1affd571c6f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sat, 14 Oct 2017 17:22:30 +0800 Subject: blk-mq-sched: improve dispatching from sw queue SCSI devices use host-wide tagset, and the shared driver tag space is often quite big. However, there is also a queue depth for each lun( .cmd_per_lun), which is often small, for example, on both lpfc and qla2xxx, .cmd_per_lun is just 3. So lots of requests may stay in sw queue, and we always flush all belonging to same hw queue and dispatch them all to driver. Unfortunately it is easy to cause queue busy because of the small .cmd_per_lun. Once these requests are flushed out, they have to stay in hctx->dispatch, and no bio merge can happen on these requests, and sequential IO performance is harmed. This patch introduces blk_mq_dequeue_from_ctx for dequeuing a request from a sw queue, so that we can dispatch them in scheduler's way. We can then avoid dequeueing too many requests from sw queue, since we don't flush ->dispatch completely. This patch improves dispatching from sw queue by using the .get_budget and .put_budget callbacks. Reviewed-by: Omar Sandoval Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- block/blk-mq-sched.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++-- block/blk-mq.c | 39 ++++++++++++++++++++++++++ block/blk-mq.h | 2 ++ include/linux/blk-mq.h | 2 ++ 4 files changed, 114 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c index 8e525e66a0d9..df8581bb0a37 100644 --- a/block/blk-mq-sched.c +++ b/block/blk-mq-sched.c @@ -128,6 +128,61 @@ static bool blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx) return false; } +static struct blk_mq_ctx *blk_mq_next_ctx(struct blk_mq_hw_ctx *hctx, + struct blk_mq_ctx *ctx) +{ + unsigned idx = ctx->index_hw; + + if (++idx == hctx->nr_ctx) + idx = 0; + + return hctx->ctxs[idx]; +} + +/* return true if hctx need to run again */ +static bool blk_mq_do_dispatch_ctx(struct blk_mq_hw_ctx *hctx) +{ + struct request_queue *q = hctx->queue; + LIST_HEAD(rq_list); + struct blk_mq_ctx *ctx = READ_ONCE(hctx->dispatch_from); + + do { + struct request *rq; + blk_status_t ret; + + if (!sbitmap_any_bit_set(&hctx->ctx_map)) + break; + + ret = blk_mq_get_dispatch_budget(hctx); + if (ret == BLK_STS_RESOURCE) + return true; + + rq = blk_mq_dequeue_from_ctx(hctx, ctx); + if (!rq) { + blk_mq_put_dispatch_budget(hctx); + break; + } else if (ret != BLK_STS_OK) { + blk_mq_end_request(rq, ret); + continue; + } + + /* + * Now this rq owns the budget which has to be released + * if this rq won't be queued to driver via .queue_rq() + * in blk_mq_dispatch_rq_list(). + */ + list_add(&rq->queuelist, &rq_list); + + /* round robin for fair dispatch */ + ctx = blk_mq_next_ctx(hctx, rq->mq_ctx); + + } while (blk_mq_dispatch_rq_list(q, &rq_list, true)); + + WRITE_ONCE(hctx->dispatch_from, ctx); + + return false; +} + /* return true if hw queue need to be run again */ bool blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx) { @@ -169,11 +224,24 @@ bool blk_mq_sched_dispatch_requests(struct blk_mq_hw_ctx *hctx) */ if (!list_empty(&rq_list)) { blk_mq_sched_mark_restart_hctx(hctx); - if (blk_mq_dispatch_rq_list(q, &rq_list, false) && - has_sched_dispatch) - run_queue = blk_mq_do_dispatch_sched(hctx); + if (blk_mq_dispatch_rq_list(q, &rq_list, false)) { + if (has_sched_dispatch) + run_queue = blk_mq_do_dispatch_sched(hctx); + else + run_queue = blk_mq_do_dispatch_ctx(hctx); + } } else if (has_sched_dispatch) { run_queue = blk_mq_do_dispatch_sched(hctx); + } else if (q->mq_ops->get_budget) { + /* + * If we need to get budget before queuing request, we + * dequeue request one by one from sw queue for avoiding + * to mess up I/O merge when dispatch runs out of resource. + * + * TODO: get more budgets, and dequeue more requests in + * one time. + */ + run_queue = blk_mq_do_dispatch_ctx(hctx); } else { blk_mq_flush_busy_ctxs(hctx, &rq_list); blk_mq_dispatch_rq_list(q, &rq_list, false); diff --git a/block/blk-mq.c b/block/blk-mq.c index dcb467369999..097ca3ece716 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -914,6 +914,45 @@ void blk_mq_flush_busy_ctxs(struct blk_mq_hw_ctx *hctx, struct list_head *list) } EXPORT_SYMBOL_GPL(blk_mq_flush_busy_ctxs); +struct dispatch_rq_data { + struct blk_mq_hw_ctx *hctx; + struct request *rq; +}; + +static bool dispatch_rq_from_ctx(struct sbitmap *sb, unsigned int bitnr, + void *data) +{ + struct dispatch_rq_data *dispatch_data = data; + struct blk_mq_hw_ctx *hctx = dispatch_data->hctx; + struct blk_mq_ctx *ctx = hctx->ctxs[bitnr]; + + spin_lock(&ctx->lock); + if (unlikely(!list_empty(&ctx->rq_list))) { + dispatch_data->rq = list_entry_rq(ctx->rq_list.next); + list_del_init(&dispatch_data->rq->queuelist); + if (list_empty(&ctx->rq_list)) + sbitmap_clear_bit(sb, bitnr); + } + spin_unlock(&ctx->lock); + + return !dispatch_data->rq; +} + +struct request *blk_mq_dequeue_from_ctx(struct blk_mq_hw_ctx *hctx, + struct blk_mq_ctx *start) +{ + unsigned off = start ? start->index_hw : 0; + struct dispatch_rq_data data = { + .hctx = hctx, + .rq = NULL, + }; + + __sbitmap_for_each_set(&hctx->ctx_map, off, + dispatch_rq_from_ctx, &data); + + return data.rq; +} + static inline unsigned int queued_to_index(unsigned int queued) { if (!queued) diff --git a/block/blk-mq.h b/block/blk-mq.h index e413b732374e..522b420dedc0 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -35,6 +35,8 @@ void blk_mq_flush_busy_ctxs(struct blk_mq_hw_ctx *hctx, struct list_head *list); bool blk_mq_hctx_has_pending(struct blk_mq_hw_ctx *hctx); bool blk_mq_get_driver_tag(struct request *rq, struct blk_mq_hw_ctx **hctx, bool wait); +struct request *blk_mq_dequeue_from_ctx(struct blk_mq_hw_ctx *hctx, + struct blk_mq_ctx *start); /* * Internal helpers for allocating/freeing the request map diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index 901457df3d64..e5e6becd57d3 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -30,6 +30,8 @@ struct blk_mq_hw_ctx { struct sbitmap ctx_map; + struct blk_mq_ctx *dispatch_from; + struct blk_mq_ctx **ctxs; unsigned int nr_ctx; -- cgit v1.2.3-59-g8ed1b From ac7fe82b6fcf77e757e88005c33b8147c1b7b73f Mon Sep 17 00:00:00 2001 From: James Smart Date: Wed, 25 Oct 2017 16:43:15 -0700 Subject: nvme-fc: add a dev_loss_tmo field to the remoteport Add a dev_loss_tmo value, paralleling the SCSI FC transport, for device connectivity loss. The transport initializes the value in the nvme_fc_register_remoteport() call. If the value is not set, a default of 60s is set. Add a new routine to the api, nvme_fc_set_remoteport_devloss() routine, which allows the lldd to dynamically update the value on an existing remoteport. Signed-off-by: James Smart Reviewed-by: Johannes Thumshirn Reviewed-by: Hannes Reinecke Signed-off-by: Christoph Hellwig --- drivers/nvme/host/fc.c | 31 +++++++++++++++++++++++++++++++ include/linux/nvme-fc-driver.h | 11 +++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index e37c69f7921d..25479d3031fa 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -45,6 +45,8 @@ enum nvme_fc_queue_flags { #define NVMEFC_QUEUE_DELAY 3 /* ms units */ +#define NVME_FC_DEFAULT_DEV_LOSS_TMO 60 /* seconds */ + struct nvme_fc_queue { struct nvme_fc_ctrl *ctrl; struct device *dev; @@ -585,6 +587,11 @@ nvme_fc_register_remoteport(struct nvme_fc_local_port *localport, newrec->remoteport.port_id = pinfo->port_id; newrec->remoteport.port_state = FC_OBJSTATE_ONLINE; newrec->remoteport.port_num = idx; + /* a registration value of dev_loss_tmo=0 results in the default */ + if (pinfo->dev_loss_tmo) + newrec->remoteport.dev_loss_tmo = pinfo->dev_loss_tmo; + else + newrec->remoteport.dev_loss_tmo = NVME_FC_DEFAULT_DEV_LOSS_TMO; spin_lock_irqsave(&nvme_fc_lock, flags); list_add_tail(&newrec->endp_list, &lport->endp_list); @@ -688,6 +695,30 @@ nvme_fc_rescan_remoteport(struct nvme_fc_remote_port *remoteport) } EXPORT_SYMBOL_GPL(nvme_fc_rescan_remoteport); +int +nvme_fc_set_remoteport_devloss(struct nvme_fc_remote_port *portptr, + u32 dev_loss_tmo) +{ + struct nvme_fc_rport *rport = remoteport_to_rport(portptr); + struct nvme_fc_ctrl *ctrl; + unsigned long flags; + + spin_lock_irqsave(&rport->lock, flags); + + if (portptr->port_state != FC_OBJSTATE_ONLINE) { + spin_unlock_irqrestore(&rport->lock, flags); + return -EINVAL; + } + + /* a dev_loss_tmo of 0 (immediate) is allowed to be set */ + rport->remoteport.dev_loss_tmo = dev_loss_tmo; + + spin_unlock_irqrestore(&rport->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(nvme_fc_set_remoteport_devloss); + /* *********************** FC-NVME DMA Handling **************************** */ diff --git a/include/linux/nvme-fc-driver.h b/include/linux/nvme-fc-driver.h index 2be4db353937..496ff759f84c 100644 --- a/include/linux/nvme-fc-driver.h +++ b/include/linux/nvme-fc-driver.h @@ -40,6 +40,8 @@ * @node_name: FC WWNN for the port * @port_name: FC WWPN for the port * @port_role: What NVME roles are supported (see FC_PORT_ROLE_xxx) + * @dev_loss_tmo: maximum delay for reconnects to an association on + * this device. Used only on a remoteport. * * Initialization values for dynamic port fields: * @port_id: FC N_Port_ID currently assigned the port. Upper 8 bits must @@ -50,6 +52,7 @@ struct nvme_fc_port_info { u64 port_name; u32 port_role; u32 port_id; + u32 dev_loss_tmo; }; @@ -200,6 +203,9 @@ enum nvme_fc_obj_state { * The length of the buffer corresponds to the local_priv_sz * value specified in the nvme_fc_port_template supplied by * the LLDD. + * @dev_loss_tmo: maximum delay for reconnects to an association on + * this device. To modify, lldd must call + * nvme_fc_set_remoteport_devloss(). * * Fields with dynamic values. Values may change base on link state. LLDD * may reference fields directly to change them. Initialized by the @@ -257,10 +263,9 @@ struct nvme_fc_remote_port { u32 port_role; u64 node_name; u64 port_name; - struct nvme_fc_local_port *localport; - void *private; + u32 dev_loss_tmo; /* dynamic fields */ u32 port_id; @@ -446,6 +451,8 @@ int nvme_fc_unregister_remoteport(struct nvme_fc_remote_port *remoteport); void nvme_fc_rescan_remoteport(struct nvme_fc_remote_port *remoteport); +int nvme_fc_set_remoteport_devloss(struct nvme_fc_remote_port *remoteport, + u32 dev_loss_tmo); /* -- cgit v1.2.3-59-g8ed1b From e2cec86528becf143341e2c5c59921bd6cfa1f45 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 1 Nov 2017 09:12:13 -0400 Subject: media: v4l2-fwnode: use a typedef for a function callback That allows having a kernel-doc markup for the function prototype. It also prevents the need of describing the return values twice. Signed-off-by: Mauro Carvalho Chehab Acked-by: Sakari Ailus --- include/media/v4l2-fwnode.h | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/media/v4l2-fwnode.h b/include/media/v4l2-fwnode.h index ca50108dfd8f..b5b465677d28 100644 --- a/include/media/v4l2-fwnode.h +++ b/include/media/v4l2-fwnode.h @@ -203,6 +203,26 @@ int v4l2_fwnode_parse_link(struct fwnode_handle *fwnode, */ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link); + +/** + * typedef parse_endpoint_func - Driver's callback function to be called on + * each V4L2 fwnode endpoint. + * + * @dev: pointer to &struct device + * @vep: pointer to &struct v4l2_fwnode_endpoint + * @asd: pointer to &struct v4l2_async_subdev + * + * Return: + * * %0 on success + * * %-ENOTCONN if the endpoint is to be skipped but this + * should not be considered as an error + * * %-EINVAL if the endpoint configuration is invalid + */ +typedef int (*parse_endpoint_func)(struct device *dev, + struct v4l2_fwnode_endpoint *vep, + struct v4l2_async_subdev *asd); + + /** * v4l2_async_notifier_parse_fwnode_endpoints - Parse V4L2 fwnode endpoints in a * device node @@ -215,10 +235,6 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link); * begin at the same memory address. * @parse_endpoint: Driver's callback function called on each V4L2 fwnode * endpoint. Optional. - * Return: %0 on success - * %-ENOTCONN if the endpoint is to be skipped but this - * should not be considered as an error - * %-EINVAL if the endpoint configuration is invalid * * Parse the fwnode endpoints of the @dev device and populate the async sub- * devices array of the notifier. The @parse_endpoint callback function is @@ -253,9 +269,7 @@ void v4l2_fwnode_put_link(struct v4l2_fwnode_link *link); int v4l2_async_notifier_parse_fwnode_endpoints( struct device *dev, struct v4l2_async_notifier *notifier, size_t asd_struct_size, - int (*parse_endpoint)(struct device *dev, - struct v4l2_fwnode_endpoint *vep, - struct v4l2_async_subdev *asd)); + parse_endpoint_func parse_endpoint); /** * v4l2_async_notifier_parse_fwnode_endpoints_by_port - Parse V4L2 fwnode @@ -271,10 +285,6 @@ int v4l2_async_notifier_parse_fwnode_endpoints( * @port: port number where endpoints are to be parsed * @parse_endpoint: Driver's callback function called on each V4L2 fwnode * endpoint. Optional. - * Return: %0 on success - * %-ENOTCONN if the endpoint is to be skipped but this - * should not be considered as an error - * %-EINVAL if the endpoint configuration is invalid * * This function is just like v4l2_async_notifier_parse_fwnode_endpoints() with * the exception that it only parses endpoints in a given port. This is useful @@ -315,9 +325,7 @@ int v4l2_async_notifier_parse_fwnode_endpoints( int v4l2_async_notifier_parse_fwnode_endpoints_by_port( struct device *dev, struct v4l2_async_notifier *notifier, size_t asd_struct_size, unsigned int port, - int (*parse_endpoint)(struct device *dev, - struct v4l2_fwnode_endpoint *vep, - struct v4l2_async_subdev *asd)); + parse_endpoint_func parse_endpoint); /** * v4l2_fwnode_reference_parse_sensor_common - parse common references on -- cgit v1.2.3-59-g8ed1b From d24a67b2d997c860a42516076f3315c2ad2d2884 Mon Sep 17 00:00:00 2001 From: Zygo Blaxell Date: Fri, 22 Sep 2017 13:58:46 -0400 Subject: btrfs: add a flags argument to LOGICAL_INO and call it LOGICAL_INO_V2 Now that check_extent_in_eb()'s extent offset filter can be turned off, we need a way to do it from userspace. Add a 'flags' field to the btrfs_logical_ino_args structure to disable extent offset filtering, taking the place of one of the existing reserved[] fields. Previous versions of LOGICAL_INO neglected to check whether any of the reserved fields have non-zero values. Assigning meaning to those fields now may change the behavior of existing programs that left these fields uninitialized. The lack of a zero check also means that new programs have no way to know whether the kernel is honoring the flags field. To avoid these problems, define a new ioctl LOGICAL_INO_V2. We can use the same argument layout as LOGICAL_INO, but shorten the reserved[] array by one element and turn it into the 'flags' field. The V2 ioctl explicitly checks that reserved fields and unsupported flag bits are zero so that userspace can negotiate future feature bits as they are defined. Since the memory layouts of the two ioctls' arguments are compatible, there is no need for a separate function for logical_to_ino_v2 (contrast with tree_search_v2 vs tree_search where the layout and code are quite different). A version parameter and an 'if' statement will suffice. Now that we have a flags field in logical_ino_args, add a flag BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET to get the behavior we want, and pass it down the stack to iterate_inodes_from_logical. Motivation and background, copied from the patchset cover letter: Suppose we have a file with one extent: root@tester:~# zcat /usr/share/doc/cpio/changelog.gz > /test/a root@tester:~# sync Split the extent by overwriting it in the middle: root@tester:~# cat /dev/urandom | dd bs=4k seek=2 skip=2 count=1 conv=notrunc of=/test/a We should now have 3 extent refs to 2 extents, with one block unreachable. The extent tree looks like: root@tester:~# btrfs-debug-tree /dev/vdc -t 2 [...] item 9 key (1103101952 EXTENT_ITEM 73728) itemoff 15942 itemsize 53 extent refs 2 gen 29 flags DATA extent data backref root 5 objectid 261 offset 0 count 2 [...] item 11 key (1103175680 EXTENT_ITEM 4096) itemoff 15865 itemsize 53 extent refs 1 gen 30 flags DATA extent data backref root 5 objectid 261 offset 8192 count 1 [...] and the ref tree looks like: root@tester:~# btrfs-debug-tree /dev/vdc -t 5 [...] item 6 key (261 EXTENT_DATA 0) itemoff 15825 itemsize 53 extent data disk byte 1103101952 nr 73728 extent data offset 0 nr 8192 ram 73728 extent compression(none) item 7 key (261 EXTENT_DATA 8192) itemoff 15772 itemsize 53 extent data disk byte 1103175680 nr 4096 extent data offset 0 nr 4096 ram 4096 extent compression(none) item 8 key (261 EXTENT_DATA 12288) itemoff 15719 itemsize 53 extent data disk byte 1103101952 nr 73728 extent data offset 12288 nr 61440 ram 73728 extent compression(none) [...] There are two references to the same extent with different, non-overlapping byte offsets: [------------------72K extent at 1103101952----------------------] [--8K----------------|--4K unreachable----|--60K-----------------] ^ ^ | | [--8K ref offset 0--][--4K ref offset 0--][--60K ref offset 12K--] | v [-----4K extent-----] at 1103175680 We want to find all of the references to extent bytenr 1103101952. Without the patch (and without running btrfs-debug-tree), we have to do it with 18 LOGICAL_INO calls: root@tester:~# btrfs ins log 1103101952 -P /test/ Using LOGICAL_INO inode 261 offset 0 root 5 root@tester:~# for x in $(seq 0 17); do btrfs ins log $((1103101952 + x * 4096)) -P /test/; done 2>&1 | grep inode inode 261 offset 0 root 5 inode 261 offset 4096 root 5 <- same extent ref as offset 0 (offset 8192 returns empty set, not reachable) inode 261 offset 12288 root 5 inode 261 offset 16384 root 5 \ inode 261 offset 20480 root 5 | inode 261 offset 24576 root 5 | inode 261 offset 28672 root 5 | inode 261 offset 32768 root 5 | inode 261 offset 36864 root 5 \ inode 261 offset 40960 root 5 > all the same extent ref as offset 12288. inode 261 offset 45056 root 5 / More processing required in userspace inode 261 offset 49152 root 5 | to figure out these are all duplicates. inode 261 offset 53248 root 5 | inode 261 offset 57344 root 5 | inode 261 offset 61440 root 5 | inode 261 offset 65536 root 5 | inode 261 offset 69632 root 5 / In the worst case the extents are 128MB long, and we have to do 32768 iterations of the loop to find one 4K extent ref. With the patch, we just use one call to map all refs to the extent at once: root@tester:~# btrfs ins log 1103101952 -P /test/ Using LOGICAL_INO_V2 inode 261 offset 0 root 5 inode 261 offset 12288 root 5 The TREE_SEARCH ioctl allows userspace to retrieve the offset and extent bytenr fields easily once the root, inode and offset are known. This is sufficient information to build a complete map of the extent and all of its references. Userspace can use this information to make better choices to dedup or defrag. Signed-off-by: Zygo Blaxell Reviewed-by: Hans van Kranenburg Tested-by: Hans van Kranenburg [ copy background and motivation from cover letter ] Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 26 +++++++++++++++++++++++--- include/uapi/linux/btrfs.h | 8 +++++++- 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 2497a5d45d9c..fa9996ab3da6 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -4530,13 +4530,14 @@ static int build_ino_list(u64 inum, u64 offset, u64 root, void *ctx) } static long btrfs_ioctl_logical_to_ino(struct btrfs_fs_info *fs_info, - void __user *arg) + void __user *arg, int version) { int ret = 0; int size; struct btrfs_ioctl_logical_ino_args *loi; struct btrfs_data_container *inodes = NULL; struct btrfs_path *path = NULL; + bool ignore_offset; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -4545,6 +4546,22 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_fs_info *fs_info, if (IS_ERR(loi)) return PTR_ERR(loi); + if (version == 1) { + ignore_offset = false; + } else { + /* All reserved bits must be 0 for now */ + if (memchr_inv(loi->reserved, 0, sizeof(loi->reserved))) { + ret = -EINVAL; + goto out_loi; + } + /* Only accept flags we have defined so far */ + if (loi->flags & ~(BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET)) { + ret = -EINVAL; + goto out_loi; + } + ignore_offset = loi->flags & BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET; + } + path = btrfs_alloc_path(); if (!path) { ret = -ENOMEM; @@ -4560,7 +4577,7 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_fs_info *fs_info, } ret = iterate_inodes_from_logical(loi->logical, fs_info, path, - build_ino_list, inodes, false); + build_ino_list, inodes, ignore_offset); if (ret == -EINVAL) ret = -ENOENT; if (ret < 0) @@ -4574,6 +4591,7 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_fs_info *fs_info, out: btrfs_free_path(path); kvfree(inodes); +out_loi: kfree(loi); return ret; @@ -5575,7 +5593,9 @@ long btrfs_ioctl(struct file *file, unsigned int case BTRFS_IOC_INO_PATHS: return btrfs_ioctl_ino_to_path(root, argp); case BTRFS_IOC_LOGICAL_INO: - return btrfs_ioctl_logical_to_ino(fs_info, argp); + return btrfs_ioctl_logical_to_ino(fs_info, argp, 1); + case BTRFS_IOC_LOGICAL_INO_V2: + return btrfs_ioctl_logical_to_ino(fs_info, argp, 2); case BTRFS_IOC_SPACE_INFO: return btrfs_ioctl_space_info(fs_info, argp); case BTRFS_IOC_SYNC: { diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 378230c163d5..99bb7988e6fe 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -608,10 +608,14 @@ struct btrfs_ioctl_ino_path_args { struct btrfs_ioctl_logical_ino_args { __u64 logical; /* in */ __u64 size; /* in */ - __u64 reserved[4]; + __u64 reserved[3]; /* must be 0 for now */ + __u64 flags; /* in, v2 only */ /* struct btrfs_data_container *inodes; out */ __u64 inodes; }; +/* Return every ref to the extent, not just those containing logical block. + * Requires logical == extent bytenr. */ +#define BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET (1ULL << 0) enum btrfs_dev_stat_values { /* disk I/O failure stats */ @@ -835,5 +839,7 @@ enum btrfs_err_code { struct btrfs_ioctl_feature_flags[3]) #define BTRFS_IOC_RM_DEV_V2 _IOW(BTRFS_IOCTL_MAGIC, 58, \ struct btrfs_ioctl_vol_args_v2) +#define BTRFS_IOC_LOGICAL_INO_V2 _IOWR(BTRFS_IOCTL_MAGIC, 59, \ + struct btrfs_ioctl_logical_ino_args) #endif /* _UAPI_LINUX_BTRFS_H */ -- cgit v1.2.3-59-g8ed1b From dd48d4072e0cdac51edcbff66342fe2f21b5b588 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 19 Oct 2017 14:15:56 -0400 Subject: btrfs: add tracepoints for outstanding extents mods This is handy for tracing problems with modifying the outstanding extents counters. Signed-off-by: Josef Bacik Signed-off-by: David Sterba --- fs/btrfs/btrfs_inode.h | 2 ++ include/trace/events/btrfs.h | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index bed88811f686..427c8738a3bd 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -274,6 +274,8 @@ static inline void btrfs_mod_outstanding_extents(struct btrfs_inode *inode, inode->outstanding_extents += mod; if (btrfs_is_free_space_inode(inode)) return; + trace_btrfs_inode_mod_outstanding_extents(inode->root, btrfs_ino(inode), + mod); } static inline void btrfs_mod_reserved_extents(struct btrfs_inode *inode, int mod) diff --git a/include/trace/events/btrfs.h b/include/trace/events/btrfs.h index bfe2f23b578c..5848ae7845da 100644 --- a/include/trace/events/btrfs.h +++ b/include/trace/events/btrfs.h @@ -1695,6 +1695,27 @@ DEFINE_EVENT(btrfs__prelim_ref, btrfs_prelim_ref_insert, TP_ARGS(fs_info, oldref, newref, tree_size) ); +TRACE_EVENT(btrfs_inode_mod_outstanding_extents, + TP_PROTO(struct btrfs_root *root, u64 ino, int mod), + + TP_ARGS(root, ino, mod), + + TP_STRUCT__entry_btrfs( + __field( u64, root_objectid ) + __field( u64, ino ) + __field( int, mod ) + ), + + TP_fast_assign_btrfs(root->fs_info, + __entry->root_objectid = root->objectid; + __entry->ino = ino; + __entry->mod = mod; + ), + + TP_printk_btrfs("root=%llu(%s) ino=%llu mod=%d", + show_root_type(__entry->root_objectid), + (unsigned long long)__entry->ino, __entry->mod) +); #endif /* _TRACE_BTRFS_H */ /* This part must be outside protection */ -- cgit v1.2.3-59-g8ed1b From aa795c41d9cd41dc9c915dd1956ddd0e4ae44485 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 Sep 2017 16:16:40 -0700 Subject: clk: Add devm_of_clk_add_hw_provider()/del_provider() APIs Sometimes we only have one of_clk_del_provider() call in driver error and remove paths, because we're missing a devm_of_clk_add_hw_provider() API. Introduce the API so we can convert drivers to use this and potentially reduce the amount of code needed to remove providers in drivers. Signed-off-by: Stephen Boyd --- Documentation/driver-model/devres.txt | 1 + drivers/clk/clk.c | 52 +++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 13 +++++++++ 3 files changed, 66 insertions(+) (limited to 'include') diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 69f08c0f23a8..c180045eb43b 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -237,6 +237,7 @@ CLOCK devm_clk_get() devm_clk_put() devm_clk_hw_register() + devm_of_clk_add_hw_provider() DMA dmam_alloc_coherent() diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c8d83acda006..856d4858ae27 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3177,6 +3177,37 @@ int of_clk_add_hw_provider(struct device_node *np, } EXPORT_SYMBOL_GPL(of_clk_add_hw_provider); +static void devm_of_clk_release_provider(struct device *dev, void *res) +{ + of_clk_del_provider(*(struct device_node **)res); +} + +int devm_of_clk_add_hw_provider(struct device *dev, + struct clk_hw *(*get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + struct device_node **ptr, *np; + int ret; + + ptr = devres_alloc(devm_of_clk_release_provider, sizeof(*ptr), + GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + np = dev->of_node; + ret = of_clk_add_hw_provider(np, get, data); + if (!ret) { + *ptr = np; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return ret; +} +EXPORT_SYMBOL_GPL(devm_of_clk_add_hw_provider); + /** * of_clk_del_provider() - Remove a previously registered clock provider * @np: Device node pointer associated with clock provider @@ -3198,6 +3229,27 @@ void of_clk_del_provider(struct device_node *np) } EXPORT_SYMBOL_GPL(of_clk_del_provider); +static int devm_clk_provider_match(struct device *dev, void *res, void *data) +{ + struct device_node **np = res; + + if (WARN_ON(!np || !*np)) + return 0; + + return *np == data; +} + +void devm_of_clk_del_provider(struct device *dev) +{ + int ret; + + ret = devres_release(dev, devm_of_clk_release_provider, + devm_clk_provider_match, dev->of_node); + + WARN_ON(ret); +} +EXPORT_SYMBOL(devm_of_clk_del_provider); + static struct clk_hw * __of_clk_get_hw_from_provider(struct of_clk_provider *provider, struct of_phandle_args *clkspec) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 5100ec1b5d55..ba79d6186133 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -815,7 +815,12 @@ int of_clk_add_hw_provider(struct device_node *np, struct clk_hw *(*get)(struct of_phandle_args *clkspec, void *data), void *data); +int devm_of_clk_add_hw_provider(struct device *dev, + struct clk_hw *(*get)(struct of_phandle_args *clkspec, + void *data), + void *data); void of_clk_del_provider(struct device_node *np); +void devm_of_clk_del_provider(struct device *dev); struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data); struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, @@ -847,7 +852,15 @@ static inline int of_clk_add_hw_provider(struct device_node *np, { return 0; } +static inline int devm_of_clk_add_hw_provider(struct device *dev, + struct clk_hw *(*get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + return 0; +} static inline void of_clk_del_provider(struct device_node *np) {} +static inline void devm_of_clk_del_provider(struct device *dev) {} static inline struct clk *of_clk_src_simple_get( struct of_phandle_args *clkspec, void *data) { -- cgit v1.2.3-59-g8ed1b From 92041a9ff003858f117009501c14f2d075dd68ce Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Wed, 6 Sep 2017 11:51:59 +0200 Subject: clk: at91: utmi: set the mainck rate By default, it is assumed that the UTMI clock is generated from a 12 MHz reference clock (MAINCK). If it's not the case, the FREQ field of the SFR_UTMICKTRIM has to be updated to generate the UTMI clock in the proper way. The UTMI clock has a fixed rate of 480 MHz. In fact, there is no multiplier we can configure. The multiplier is managed internally, depending on the reference clock frequency, to achieve the target of 480 MHz. Signed-off-by: Ludovic Desroches Acked-by: Ingo van Lil Signed-off-by: Stephen Boyd --- drivers/clk/at91/clk-utmi.c | 95 +++++++++++++++++++++++++++++++++++++------- include/soc/at91/atmel-sfr.h | 2 + 2 files changed, 83 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c index aadabd9d1e2b..cd8d689138ff 100644 --- a/drivers/clk/at91/clk-utmi.c +++ b/drivers/clk/at91/clk-utmi.c @@ -14,14 +14,20 @@ #include #include #include +#include #include "pmc.h" -#define UTMI_FIXED_MUL 40 +/* + * The purpose of this clock is to generate a 480 MHz signal. A different + * rate can't be configured. + */ +#define UTMI_RATE 480000000 struct clk_utmi { struct clk_hw hw; - struct regmap *regmap; + struct regmap *regmap_pmc; + struct regmap *regmap_sfr; }; #define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) @@ -37,13 +43,54 @@ static inline bool clk_utmi_ready(struct regmap *regmap) static int clk_utmi_prepare(struct clk_hw *hw) { + struct clk_hw *hw_parent; struct clk_utmi *utmi = to_clk_utmi(hw); unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | AT91_PMC_BIASEN; + unsigned int utmi_ref_clk_freq; + unsigned long parent_rate; + + /* + * If mainck rate is different from 12 MHz, we have to configure the + * FREQ field of the SFR_UTMICKTRIM register to generate properly + * the utmi clock. + */ + hw_parent = clk_hw_get_parent(hw); + parent_rate = clk_hw_get_rate(hw_parent); + + switch (parent_rate) { + case 12000000: + utmi_ref_clk_freq = 0; + break; + case 16000000: + utmi_ref_clk_freq = 1; + break; + case 24000000: + utmi_ref_clk_freq = 2; + break; + /* + * Not supported on SAMA5D2 but it's not an issue since MAINCK + * maximum value is 24 MHz. + */ + case 48000000: + utmi_ref_clk_freq = 3; + break; + default: + pr_err("UTMICK: unsupported mainck rate\n"); + return -EINVAL; + } - regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr); + if (utmi->regmap_sfr) { + regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, + AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); + } else if (utmi_ref_clk_freq) { + pr_err("UTMICK: sfr node required\n"); + return -EINVAL; + } - while (!clk_utmi_ready(utmi->regmap)) + regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr); + + while (!clk_utmi_ready(utmi->regmap_pmc)) cpu_relax(); return 0; @@ -53,21 +100,22 @@ static int clk_utmi_is_prepared(struct clk_hw *hw) { struct clk_utmi *utmi = to_clk_utmi(hw); - return clk_utmi_ready(utmi->regmap); + return clk_utmi_ready(utmi->regmap_pmc); } static void clk_utmi_unprepare(struct clk_hw *hw) { struct clk_utmi *utmi = to_clk_utmi(hw); - regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0); + regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, + AT91_PMC_UPLLEN, 0); } static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { - /* UTMI clk is a fixed clk multiplier */ - return parent_rate * UTMI_FIXED_MUL; + /* UTMI clk rate is fixed. */ + return UTMI_RATE; } static const struct clk_ops utmi_ops = { @@ -78,7 +126,7 @@ static const struct clk_ops utmi_ops = { }; static struct clk_hw * __init -at91_clk_register_utmi(struct regmap *regmap, +at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, const char *name, const char *parent_name) { struct clk_utmi *utmi; @@ -97,7 +145,8 @@ at91_clk_register_utmi(struct regmap *regmap, init.flags = CLK_SET_RATE_GATE; utmi->hw.init = &init; - utmi->regmap = regmap; + utmi->regmap_pmc = regmap_pmc; + utmi->regmap_sfr = regmap_sfr; hw = &utmi->hw; ret = clk_hw_register(NULL, &utmi->hw); @@ -114,17 +163,35 @@ static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np) struct clk_hw *hw; const char *parent_name; const char *name = np->name; - struct regmap *regmap; + struct regmap *regmap_pmc, *regmap_sfr; parent_name = of_clk_get_parent_name(np, 0); of_property_read_string(np, "clock-output-names", &name); - regmap = syscon_node_to_regmap(of_get_parent(np)); - if (IS_ERR(regmap)) + regmap_pmc = syscon_node_to_regmap(of_get_parent(np)); + if (IS_ERR(regmap_pmc)) return; - hw = at91_clk_register_utmi(regmap, name, parent_name); + /* + * If the device supports different mainck rates, this value has to be + * set in the UTMI Clock Trimming register. + * - 9x5: mainck supports several rates but it is indicated that a + * 12 MHz is needed in case of USB. + * - sama5d3 and sama5d2: mainck supports several rates. Configuring + * the FREQ field of the UTMI Clock Trimming register is mandatory. + * - sama5d4: mainck is at 12 MHz. + * + * We only need to retrieve sama5d3 or sama5d2 sfr regmap. + */ + regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d3-sfr"); + if (IS_ERR(regmap_sfr)) { + regmap_sfr = syscon_regmap_lookup_by_compatible("atmel,sama5d2-sfr"); + if (IS_ERR(regmap_sfr)) + regmap_sfr = NULL; + } + + hw = at91_clk_register_utmi(regmap_pmc, regmap_sfr, name, parent_name); if (IS_ERR(hw)) return; diff --git a/include/soc/at91/atmel-sfr.h b/include/soc/at91/atmel-sfr.h index 506ea8ffda19..482337af06b8 100644 --- a/include/soc/at91/atmel-sfr.h +++ b/include/soc/at91/atmel-sfr.h @@ -17,6 +17,7 @@ /* 0x08 ~ 0x0c: Reserved */ #define AT91_SFR_OHCIICR 0x10 /* OHCI INT Configuration Register */ #define AT91_SFR_OHCIISR 0x14 /* OHCI INT Status Register */ +#define AT91_SFR_UTMICKTRIM 0x30 /* UTMI Clock Trimming Register */ #define AT91_SFR_I2SCLKSEL 0x90 /* I2SC Register */ /* Field definitions */ @@ -28,5 +29,6 @@ AT91_OHCIICR_SUSPEND_B | \ AT91_OHCIICR_SUSPEND_C) +#define AT91_UTMICKTRIM_FREQ GENMASK(1, 0) #endif /* _LINUX_MFD_SYSCON_ATMEL_SFR_H */ -- cgit v1.2.3-59-g8ed1b From 856e6bb91e198c4fb0ca2f4f3ee0e7f660b3e0ca Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 13 Oct 2017 13:59:16 +0200 Subject: clk: qcom: Update DT bindings for the MSM8660/APQ8060 RPMCC These compatible strings need to be added to extend support for the RPM CC to cover MSM8660/APQ8060. We also need to add enumberators to the include file for a few clocks that were missing. Cc: devicetree@vger.kernel.org Acked-by: Rob Herring Signed-off-by: Linus Walleij Signed-off-by: Stephen Boyd --- Documentation/devicetree/bindings/clock/qcom,rpmcc.txt | 2 ++ include/dt-bindings/clock/qcom,rpmcc.h | 3 +++ 2 files changed, 5 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt index 32c6e9ce64c3..833a89f06ae0 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt @@ -10,6 +10,8 @@ Required properties : - compatible : shall contain only one of the following. The generic compatible "qcom,rpmcc" should be also included. + "qcom,rpmcc-msm8660", "qcom,rpmcc" + "qcom,rpmcc-apq8060", "qcom,rpmcc" "qcom,rpmcc-msm8916", "qcom,rpmcc" "qcom,rpmcc-msm8974", "qcom,rpmcc" "qcom,rpmcc-apq8064", "qcom,rpmcc" diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h index 96b63c00249e..29dad206a74b 100644 --- a/include/dt-bindings/clock/qcom,rpmcc.h +++ b/include/dt-bindings/clock/qcom,rpmcc.h @@ -37,6 +37,9 @@ #define RPM_SYS_FABRIC_A_CLK 19 #define RPM_SFPB_CLK 20 #define RPM_SFPB_A_CLK 21 +#define RPM_SMI_CLK 22 +#define RPM_SMI_A_CLK 23 +#define RPM_PLL4_CLK 24 /* SMD RPM clocks */ #define RPM_SMD_XO_CLK_SRC 0 -- cgit v1.2.3-59-g8ed1b From 7066fdd0d7427f1ba455d186e75213c442c9663b Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Tue, 10 Oct 2017 14:27:13 +0530 Subject: clk: qcom: clk-smd-rpm: add msm8996 rpmclks Add all RPM controlled clocks on msm8996 platform [srini: Fixed various issues with offsets and made names specific to msm8996] Signed-off-by: Srinivas Kandagatla Signed-off-by: Rajendra Nayak Acked-by: Rob Herring Acked-by: Bjorn Andersson Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,rpmcc.txt | 1 + drivers/clk/qcom/clk-smd-rpm.c | 82 ++++++++++++++++++++++ include/dt-bindings/clock/qcom,rpmcc.h | 14 ++++ include/linux/soc/qcom/smd-rpm.h | 4 ++ 4 files changed, 101 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt index 833a89f06ae0..4491d1c104aa 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.txt @@ -15,6 +15,7 @@ Required properties : "qcom,rpmcc-msm8916", "qcom,rpmcc" "qcom,rpmcc-msm8974", "qcom,rpmcc" "qcom,rpmcc-apq8064", "qcom,rpmcc" + "qcom,rpmcc-msm8996", "qcom,rpmcc" - #clock-cells : shall contain 1 diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index cc03d5508627..c26d9007bfc4 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -530,9 +530,91 @@ static const struct rpm_smd_clk_desc rpm_clk_msm8974 = { .clks = msm8974_clks, .num_clks = ARRAY_SIZE(msm8974_clks), }; + +/* msm8996 */ +DEFINE_CLK_SMD_RPM(msm8996, pcnoc_clk, pcnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8996, snoc_clk, snoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 1); +DEFINE_CLK_SMD_RPM(msm8996, cnoc_clk, cnoc_a_clk, QCOM_SMD_RPM_BUS_CLK, 2); +DEFINE_CLK_SMD_RPM(msm8996, bimc_clk, bimc_a_clk, QCOM_SMD_RPM_MEM_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8996, mmssnoc_axi_rpm_clk, mmssnoc_axi_rpm_a_clk, + QCOM_SMD_RPM_MMAXI_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8996, ipa_clk, ipa_a_clk, QCOM_SMD_RPM_IPA_CLK, 0); +DEFINE_CLK_SMD_RPM(msm8996, ce1_clk, ce1_a_clk, QCOM_SMD_RPM_CE_CLK, 0); +DEFINE_CLK_SMD_RPM_BRANCH(msm8996, aggre1_noc_clk, aggre1_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 1, 1000); +DEFINE_CLK_SMD_RPM_BRANCH(msm8996, aggre2_noc_clk, aggre2_noc_a_clk, + QCOM_SMD_RPM_AGGR_CLK, 2, 1000); +DEFINE_CLK_SMD_RPM_QDSS(msm8996, qdss_clk, qdss_a_clk, + QCOM_SMD_RPM_MISC_CLK, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, bb_clk1, bb_clk1_a, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, bb_clk2, bb_clk2_a, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, rf_clk1, rf_clk1_a, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, rf_clk2, rf_clk2_a, 5); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, ln_bb_clk, ln_bb_a_clk, 8); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, div_clk1, div_clk1_a, 0xb); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, div_clk2, div_clk2_a, 0xc); +DEFINE_CLK_SMD_RPM_XO_BUFFER(msm8996, div_clk3, div_clk3_a, 0xd); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, bb_clk1_pin, bb_clk1_a_pin, 1); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, bb_clk2_pin, bb_clk2_a_pin, 2); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, rf_clk1_pin, rf_clk1_a_pin, 4); +DEFINE_CLK_SMD_RPM_XO_BUFFER_PINCTRL(msm8996, rf_clk2_pin, rf_clk2_a_pin, 5); + +static struct clk_smd_rpm *msm8996_clks[] = { + [RPM_SMD_PCNOC_CLK] = &msm8996_pcnoc_clk, + [RPM_SMD_PCNOC_A_CLK] = &msm8996_pcnoc_a_clk, + [RPM_SMD_SNOC_CLK] = &msm8996_snoc_clk, + [RPM_SMD_SNOC_A_CLK] = &msm8996_snoc_a_clk, + [RPM_SMD_CNOC_CLK] = &msm8996_cnoc_clk, + [RPM_SMD_CNOC_A_CLK] = &msm8996_cnoc_a_clk, + [RPM_SMD_BIMC_CLK] = &msm8996_bimc_clk, + [RPM_SMD_BIMC_A_CLK] = &msm8996_bimc_a_clk, + [RPM_SMD_MMAXI_CLK] = &msm8996_mmssnoc_axi_rpm_clk, + [RPM_SMD_MMAXI_A_CLK] = &msm8996_mmssnoc_axi_rpm_a_clk, + [RPM_SMD_IPA_CLK] = &msm8996_ipa_clk, + [RPM_SMD_IPA_A_CLK] = &msm8996_ipa_a_clk, + [RPM_SMD_CE1_CLK] = &msm8996_ce1_clk, + [RPM_SMD_CE1_A_CLK] = &msm8996_ce1_a_clk, + [RPM_SMD_AGGR1_NOC_CLK] = &msm8996_aggre1_noc_clk, + [RPM_SMD_AGGR1_NOC_A_CLK] = &msm8996_aggre1_noc_a_clk, + [RPM_SMD_AGGR2_NOC_CLK] = &msm8996_aggre2_noc_clk, + [RPM_SMD_AGGR2_NOC_A_CLK] = &msm8996_aggre2_noc_a_clk, + [RPM_SMD_QDSS_CLK] = &msm8996_qdss_clk, + [RPM_SMD_QDSS_A_CLK] = &msm8996_qdss_a_clk, + [RPM_SMD_BB_CLK1] = &msm8996_bb_clk1, + [RPM_SMD_BB_CLK1_A] = &msm8996_bb_clk1_a, + [RPM_SMD_BB_CLK2] = &msm8996_bb_clk2, + [RPM_SMD_BB_CLK2_A] = &msm8996_bb_clk2_a, + [RPM_SMD_RF_CLK1] = &msm8996_rf_clk1, + [RPM_SMD_RF_CLK1_A] = &msm8996_rf_clk1_a, + [RPM_SMD_RF_CLK2] = &msm8996_rf_clk2, + [RPM_SMD_RF_CLK2_A] = &msm8996_rf_clk2_a, + [RPM_SMD_LN_BB_CLK] = &msm8996_ln_bb_clk, + [RPM_SMD_LN_BB_A_CLK] = &msm8996_ln_bb_a_clk, + [RPM_SMD_DIV_CLK1] = &msm8996_div_clk1, + [RPM_SMD_DIV_A_CLK1] = &msm8996_div_clk1_a, + [RPM_SMD_DIV_CLK2] = &msm8996_div_clk2, + [RPM_SMD_DIV_A_CLK2] = &msm8996_div_clk2_a, + [RPM_SMD_DIV_CLK3] = &msm8996_div_clk3, + [RPM_SMD_DIV_A_CLK3] = &msm8996_div_clk3_a, + [RPM_SMD_BB_CLK1_PIN] = &msm8996_bb_clk1_pin, + [RPM_SMD_BB_CLK1_A_PIN] = &msm8996_bb_clk1_a_pin, + [RPM_SMD_BB_CLK2_PIN] = &msm8996_bb_clk2_pin, + [RPM_SMD_BB_CLK2_A_PIN] = &msm8996_bb_clk2_a_pin, + [RPM_SMD_RF_CLK1_PIN] = &msm8996_rf_clk1_pin, + [RPM_SMD_RF_CLK1_A_PIN] = &msm8996_rf_clk1_a_pin, + [RPM_SMD_RF_CLK2_PIN] = &msm8996_rf_clk2_pin, + [RPM_SMD_RF_CLK2_A_PIN] = &msm8996_rf_clk2_a_pin, +}; + +static const struct rpm_smd_clk_desc rpm_clk_msm8996 = { + .clks = msm8996_clks, + .num_clks = ARRAY_SIZE(msm8996_clks), +}; + static const struct of_device_id rpm_smd_clk_match_table[] = { { .compatible = "qcom,rpmcc-msm8916", .data = &rpm_clk_msm8916 }, { .compatible = "qcom,rpmcc-msm8974", .data = &rpm_clk_msm8974 }, + { .compatible = "qcom,rpmcc-msm8996", .data = &rpm_clk_msm8996 }, { } }; MODULE_DEVICE_TABLE(of, rpm_smd_clk_match_table); diff --git a/include/dt-bindings/clock/qcom,rpmcc.h b/include/dt-bindings/clock/qcom,rpmcc.h index 29dad206a74b..b8337a5fa347 100644 --- a/include/dt-bindings/clock/qcom,rpmcc.h +++ b/include/dt-bindings/clock/qcom,rpmcc.h @@ -104,5 +104,19 @@ #define RPM_SMD_CXO_A1_A_PIN 59 #define RPM_SMD_CXO_A2_PIN 60 #define RPM_SMD_CXO_A2_A_PIN 61 +#define RPM_SMD_AGGR1_NOC_CLK 62 +#define RPM_SMD_AGGR1_NOC_A_CLK 63 +#define RPM_SMD_AGGR2_NOC_CLK 64 +#define RPM_SMD_AGGR2_NOC_A_CLK 65 +#define RPM_SMD_MMAXI_CLK 66 +#define RPM_SMD_MMAXI_A_CLK 67 +#define RPM_SMD_IPA_CLK 68 +#define RPM_SMD_IPA_A_CLK 69 +#define RPM_SMD_CE1_CLK 70 +#define RPM_SMD_CE1_A_CLK 71 +#define RPM_SMD_DIV_CLK3 72 +#define RPM_SMD_DIV_A_CLK3 73 +#define RPM_SMD_LN_BB_CLK 74 +#define RPM_SMD_LN_BB_A_CLK 75 #endif diff --git a/include/linux/soc/qcom/smd-rpm.h b/include/linux/soc/qcom/smd-rpm.h index 2a53dcaeeeed..ebdabd669d93 100644 --- a/include/linux/soc/qcom/smd-rpm.h +++ b/include/linux/soc/qcom/smd-rpm.h @@ -26,6 +26,10 @@ struct qcom_smd_rpm; #define QCOM_SMD_RPM_SMPB 0x62706d73 #define QCOM_SMD_RPM_SPDM 0x63707362 #define QCOM_SMD_RPM_VSA 0x00617376 +#define QCOM_SMD_RPM_MMAXI_CLK 0x69786d6d +#define QCOM_SMD_RPM_IPA_CLK 0x617069 +#define QCOM_SMD_RPM_CE_CLK 0x6563 +#define QCOM_SMD_RPM_AGGR_CLK 0x72676761 int qcom_rpm_smd_write(struct qcom_smd_rpm *rpm, int state, -- cgit v1.2.3-59-g8ed1b From 0b5a89caee5c9958c18cd933c7f8891e35b21781 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 1 Nov 2017 11:47:38 +0100 Subject: net: sched: remove unused tc_should_offload helper tc_should_offload is no longer used, remove it. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 7 ------- 1 file changed, 7 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 37c5ef766655..108dcdd96421 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -623,13 +623,6 @@ static inline bool tc_skip_hw(u32 flags) return (flags & TCA_CLS_FLAGS_SKIP_HW) ? true : false; } -static inline bool tc_should_offload(const struct net_device *dev, u32 flags) -{ - if (tc_skip_hw(flags)) - return false; - return tc_can_offload(dev); -} - static inline bool tc_skip_sw(u32 flags) { return (flags & TCA_CLS_FLAGS_SKIP_SW) ? true : false; -- cgit v1.2.3-59-g8ed1b From 70b5aee46782208c14d93b715e9f62f7fec844f1 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 1 Nov 2017 11:47:41 +0100 Subject: net: sched: remove ndo_setup_tc check from tc_can_offload Since tc_can_offload is always called from block callback or egdev callback, no need to check if ndo_setup_tc exists. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 108dcdd96421..d15c40c7bde7 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -611,11 +611,7 @@ struct tc_cls_u32_offload { static inline bool tc_can_offload(const struct net_device *dev) { - if (!(dev->features & NETIF_F_HW_TC)) - return false; - if (!dev->netdev_ops->ndo_setup_tc) - return false; - return true; + return dev->features & NETIF_F_HW_TC; } static inline bool tc_skip_hw(u32 flags) -- cgit v1.2.3-59-g8ed1b From 259bc283069975004067c51cc09525a50db4b470 Mon Sep 17 00:00:00 2001 From: Adriana Reus Date: Mon, 2 Oct 2017 13:32:11 +0300 Subject: clk: imx: imx7d: Remove ARM_M0 clock IMX7d does not have an M0 Core and this particular clock doesn't seem connected to anything else. Remove this entry from the CCM driver. Signed-off-by: Adriana Reus Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-imx7d.c | 9 --------- include/dt-bindings/clock/imx7d-clock.h | 8 ++++---- 2 files changed, 4 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c index 0ac9b30c8b90..80dc211eb74b 100644 --- a/drivers/clk/imx/clk-imx7d.c +++ b/drivers/clk/imx/clk-imx7d.c @@ -54,11 +54,6 @@ static const char *arm_m4_sel[] = { "osc", "pll_sys_main_240m_clk", "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk", "pll_usb_main_clk", }; -static const char *arm_m0_sel[] = { "osc", "pll_sys_main_120m_clk", - "pll_enet_125m_clk", "pll_sys_pfd2_135m_clk", - "pll_dram_533m_clk", "pll_audio_post_div", "pll_video_main_clk", - "pll_usb_main_clk", }; - static const char *axi_sel[] = { "osc", "pll_sys_pfd1_332m_clk", "pll_dram_533m_clk", "pll_enet_250m_clk", "pll_sys_pfd5_clk", "pll_audio_post_div", "pll_video_main_clk", "pll_sys_pfd7_clk", }; @@ -510,7 +505,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_ARM_A7_ROOT_SRC] = imx_clk_mux2("arm_a7_src", base + 0x8000, 24, 3, arm_a7_sel, ARRAY_SIZE(arm_a7_sel)); clks[IMX7D_ARM_M4_ROOT_SRC] = imx_clk_mux2("arm_m4_src", base + 0x8080, 24, 3, arm_m4_sel, ARRAY_SIZE(arm_m4_sel)); - clks[IMX7D_ARM_M0_ROOT_SRC] = imx_clk_mux2("arm_m0_src", base + 0x8100, 24, 3, arm_m0_sel, ARRAY_SIZE(arm_m0_sel)); clks[IMX7D_MAIN_AXI_ROOT_SRC] = imx_clk_mux2("axi_src", base + 0x8800, 24, 3, axi_sel, ARRAY_SIZE(axi_sel)); clks[IMX7D_DISP_AXI_ROOT_SRC] = imx_clk_mux2("disp_axi_src", base + 0x8880, 24, 3, disp_axi_sel, ARRAY_SIZE(disp_axi_sel)); clks[IMX7D_ENET_AXI_ROOT_SRC] = imx_clk_mux2("enet_axi_src", base + 0x8900, 24, 3, enet_axi_sel, ARRAY_SIZE(enet_axi_sel)); @@ -582,7 +576,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_ARM_A7_ROOT_CG] = imx_clk_gate3("arm_a7_cg", "arm_a7_src", base + 0x8000, 28); clks[IMX7D_ARM_M4_ROOT_CG] = imx_clk_gate3("arm_m4_cg", "arm_m4_src", base + 0x8080, 28); - clks[IMX7D_ARM_M0_ROOT_CG] = imx_clk_gate3("arm_m0_cg", "arm_m0_src", base + 0x8100, 28); clks[IMX7D_MAIN_AXI_ROOT_CG] = imx_clk_gate3("axi_cg", "axi_src", base + 0x8800, 28); clks[IMX7D_DISP_AXI_ROOT_CG] = imx_clk_gate3("disp_axi_cg", "disp_axi_src", base + 0x8880, 28); clks[IMX7D_ENET_AXI_ROOT_CG] = imx_clk_gate3("enet_axi_cg", "enet_axi_src", base + 0x8900, 28); @@ -721,7 +714,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_ARM_A7_ROOT_DIV] = imx_clk_divider2("arm_a7_div", "arm_a7_cg", base + 0x8000, 0, 3); clks[IMX7D_ARM_M4_ROOT_DIV] = imx_clk_divider2("arm_m4_div", "arm_m4_cg", base + 0x8080, 0, 3); - clks[IMX7D_ARM_M0_ROOT_DIV] = imx_clk_divider2("arm_m0_div", "arm_m0_cg", base + 0x8100, 0, 3); clks[IMX7D_MAIN_AXI_ROOT_DIV] = imx_clk_divider2("axi_post_div", "axi_pre_div", base + 0x8800, 0, 6); clks[IMX7D_DISP_AXI_ROOT_DIV] = imx_clk_divider2("disp_axi_post_div", "disp_axi_pre_div", base + 0x8880, 0, 6); clks[IMX7D_ENET_AXI_ROOT_DIV] = imx_clk_divider2("enet_axi_post_div", "enet_axi_pre_div", base + 0x8900, 0, 6); @@ -793,7 +785,6 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node) clks[IMX7D_ARM_A7_ROOT_CLK] = imx_clk_gate4("arm_a7_root_clk", "arm_a7_div", base + 0x4000, 0); clks[IMX7D_ARM_M4_ROOT_CLK] = imx_clk_gate4("arm_m4_root_clk", "arm_m4_div", base + 0x4010, 0); - clks[IMX7D_ARM_M0_ROOT_CLK] = imx_clk_gate4("arm_m0_root_clk", "arm_m0_div", base + 0x4020, 0); clks[IMX7D_MAIN_AXI_ROOT_CLK] = imx_clk_gate4("main_axi_root_clk", "axi_post_div", base + 0x4040, 0); clks[IMX7D_DISP_AXI_ROOT_CLK] = imx_clk_gate4("disp_axi_root_clk", "disp_axi_post_div", base + 0x4050, 0); clks[IMX7D_ENET_AXI_ROOT_CLK] = imx_clk_gate4("enet_axi_root_clk", "enet_axi_post_div", base + 0x4060, 0); diff --git a/include/dt-bindings/clock/imx7d-clock.h b/include/dt-bindings/clock/imx7d-clock.h index de62a83b6c80..e2f99ae72d5c 100644 --- a/include/dt-bindings/clock/imx7d-clock.h +++ b/include/dt-bindings/clock/imx7d-clock.h @@ -80,10 +80,10 @@ #define IMX7D_ARM_M4_ROOT_SRC 67 #define IMX7D_ARM_M4_ROOT_CG 68 #define IMX7D_ARM_M4_ROOT_DIV 69 -#define IMX7D_ARM_M0_ROOT_CLK 70 -#define IMX7D_ARM_M0_ROOT_SRC 71 -#define IMX7D_ARM_M0_ROOT_CG 72 -#define IMX7D_ARM_M0_ROOT_DIV 73 +#define IMX7D_ARM_M0_ROOT_CLK 70 /* unused */ +#define IMX7D_ARM_M0_ROOT_SRC 71 /* unused */ +#define IMX7D_ARM_M0_ROOT_CG 72 /* unused */ +#define IMX7D_ARM_M0_ROOT_DIV 73 /* unused */ #define IMX7D_MAIN_AXI_ROOT_CLK 74 #define IMX7D_MAIN_AXI_ROOT_SRC 75 #define IMX7D_MAIN_AXI_ROOT_CG 76 -- cgit v1.2.3-59-g8ed1b From 1495dc9f0a711a54f8fec849ce7f3a8f585a11e5 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 1 Nov 2017 11:48:00 -0700 Subject: security: bpf: replace include of linux/bpf.h with forward declarations Touching linux/bpf.h makes us rebuild a surprisingly large portion of the kernel. Remove the unnecessary dependency from security.h, it only needs forward declarations. Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/security.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index 18800b0911e5..73f1ef625d40 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -31,7 +31,6 @@ #include #include #include -#include struct linux_binprm; struct cred; @@ -1732,6 +1731,10 @@ static inline void securityfs_remove(struct dentry *dentry) #endif #ifdef CONFIG_BPF_SYSCALL +union bpf_attr; +struct bpf_map; +struct bpf_prog; +struct bpf_prog_aux; #ifdef CONFIG_SECURITY extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size); extern int security_bpf_map(struct bpf_map *map, fmode_t fmode); -- cgit v1.2.3-59-g8ed1b From b7f1a721bb3e493627d9db0a00edfb53c70ba823 Mon Sep 17 00:00:00 2001 From: "weiyi.lu@mediatek.com" Date: Mon, 23 Oct 2017 12:10:33 +0800 Subject: clk: mediatek: Add dt-bindings for MT2712 clocks Add MT2712 clock dt-bindings, include topckgen, apmixedsys, infracfg, pericfg, mcucfg and subsystem clocks. Signed-off-by: Weiyi Lu Acked-by: Rob Herring Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/mt2712-clk.h | 427 +++++++++++++++++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 include/dt-bindings/clock/mt2712-clk.h (limited to 'include') diff --git a/include/dt-bindings/clock/mt2712-clk.h b/include/dt-bindings/clock/mt2712-clk.h new file mode 100644 index 000000000000..48a8e797a617 --- /dev/null +++ b/include/dt-bindings/clock/mt2712-clk.h @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Weiyi Lu + * + * 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. + */ + +#ifndef _DT_BINDINGS_CLK_MT2712_H +#define _DT_BINDINGS_CLK_MT2712_H + +/* APMIXEDSYS */ + +#define CLK_APMIXED_MAINPLL 0 +#define CLK_APMIXED_UNIVPLL 1 +#define CLK_APMIXED_VCODECPLL 2 +#define CLK_APMIXED_VENCPLL 3 +#define CLK_APMIXED_APLL1 4 +#define CLK_APMIXED_APLL2 5 +#define CLK_APMIXED_LVDSPLL 6 +#define CLK_APMIXED_LVDSPLL2 7 +#define CLK_APMIXED_MSDCPLL 8 +#define CLK_APMIXED_MSDCPLL2 9 +#define CLK_APMIXED_TVDPLL 10 +#define CLK_APMIXED_MMPLL 11 +#define CLK_APMIXED_ARMCA35PLL 12 +#define CLK_APMIXED_ARMCA72PLL 13 +#define CLK_APMIXED_ETHERPLL 14 +#define CLK_APMIXED_NR_CLK 15 + +/* TOPCKGEN */ + +#define CLK_TOP_ARMCA35PLL 0 +#define CLK_TOP_ARMCA35PLL_600M 1 +#define CLK_TOP_ARMCA35PLL_400M 2 +#define CLK_TOP_ARMCA72PLL 3 +#define CLK_TOP_SYSPLL 4 +#define CLK_TOP_SYSPLL_D2 5 +#define CLK_TOP_SYSPLL1_D2 6 +#define CLK_TOP_SYSPLL1_D4 7 +#define CLK_TOP_SYSPLL1_D8 8 +#define CLK_TOP_SYSPLL1_D16 9 +#define CLK_TOP_SYSPLL_D3 10 +#define CLK_TOP_SYSPLL2_D2 11 +#define CLK_TOP_SYSPLL2_D4 12 +#define CLK_TOP_SYSPLL_D5 13 +#define CLK_TOP_SYSPLL3_D2 14 +#define CLK_TOP_SYSPLL3_D4 15 +#define CLK_TOP_SYSPLL_D7 16 +#define CLK_TOP_SYSPLL4_D2 17 +#define CLK_TOP_SYSPLL4_D4 18 +#define CLK_TOP_UNIVPLL 19 +#define CLK_TOP_UNIVPLL_D7 20 +#define CLK_TOP_UNIVPLL_D26 21 +#define CLK_TOP_UNIVPLL_D52 22 +#define CLK_TOP_UNIVPLL_D104 23 +#define CLK_TOP_UNIVPLL_D208 24 +#define CLK_TOP_UNIVPLL_D2 25 +#define CLK_TOP_UNIVPLL1_D2 26 +#define CLK_TOP_UNIVPLL1_D4 27 +#define CLK_TOP_UNIVPLL1_D8 28 +#define CLK_TOP_UNIVPLL_D3 29 +#define CLK_TOP_UNIVPLL2_D2 30 +#define CLK_TOP_UNIVPLL2_D4 31 +#define CLK_TOP_UNIVPLL2_D8 32 +#define CLK_TOP_UNIVPLL_D5 33 +#define CLK_TOP_UNIVPLL3_D2 34 +#define CLK_TOP_UNIVPLL3_D4 35 +#define CLK_TOP_UNIVPLL3_D8 36 +#define CLK_TOP_F_MP0_PLL1 37 +#define CLK_TOP_F_MP0_PLL2 38 +#define CLK_TOP_F_BIG_PLL1 39 +#define CLK_TOP_F_BIG_PLL2 40 +#define CLK_TOP_F_BUS_PLL1 41 +#define CLK_TOP_F_BUS_PLL2 42 +#define CLK_TOP_APLL1 43 +#define CLK_TOP_APLL1_D2 44 +#define CLK_TOP_APLL1_D4 45 +#define CLK_TOP_APLL1_D8 46 +#define CLK_TOP_APLL1_D16 47 +#define CLK_TOP_APLL2 48 +#define CLK_TOP_APLL2_D2 49 +#define CLK_TOP_APLL2_D4 50 +#define CLK_TOP_APLL2_D8 51 +#define CLK_TOP_APLL2_D16 52 +#define CLK_TOP_LVDSPLL 53 +#define CLK_TOP_LVDSPLL_D2 54 +#define CLK_TOP_LVDSPLL_D4 55 +#define CLK_TOP_LVDSPLL_D8 56 +#define CLK_TOP_LVDSPLL2 57 +#define CLK_TOP_LVDSPLL2_D2 58 +#define CLK_TOP_LVDSPLL2_D4 59 +#define CLK_TOP_LVDSPLL2_D8 60 +#define CLK_TOP_ETHERPLL_125M 61 +#define CLK_TOP_ETHERPLL_50M 62 +#define CLK_TOP_CVBS 63 +#define CLK_TOP_CVBS_D2 64 +#define CLK_TOP_SYS_26M 65 +#define CLK_TOP_MMPLL 66 +#define CLK_TOP_MMPLL_D2 67 +#define CLK_TOP_VENCPLL 68 +#define CLK_TOP_VENCPLL_D2 69 +#define CLK_TOP_VCODECPLL 70 +#define CLK_TOP_VCODECPLL_D2 71 +#define CLK_TOP_TVDPLL 72 +#define CLK_TOP_TVDPLL_D2 73 +#define CLK_TOP_TVDPLL_D4 74 +#define CLK_TOP_TVDPLL_D8 75 +#define CLK_TOP_TVDPLL_429M 76 +#define CLK_TOP_TVDPLL_429M_D2 77 +#define CLK_TOP_TVDPLL_429M_D4 78 +#define CLK_TOP_MSDCPLL 79 +#define CLK_TOP_MSDCPLL_D2 80 +#define CLK_TOP_MSDCPLL_D4 81 +#define CLK_TOP_MSDCPLL2 82 +#define CLK_TOP_MSDCPLL2_D2 83 +#define CLK_TOP_MSDCPLL2_D4 84 +#define CLK_TOP_CLK26M_D2 85 +#define CLK_TOP_D2A_ULCLK_6P5M 86 +#define CLK_TOP_VPLL3_DPIX 87 +#define CLK_TOP_VPLL_DPIX 88 +#define CLK_TOP_LTEPLL_FS26M 89 +#define CLK_TOP_DMPLL 90 +#define CLK_TOP_DSI0_LNTC 91 +#define CLK_TOP_DSI1_LNTC 92 +#define CLK_TOP_LVDSTX3_CLKDIG_CTS 93 +#define CLK_TOP_LVDSTX_CLKDIG_CTS 94 +#define CLK_TOP_CLKRTC_EXT 95 +#define CLK_TOP_CLKRTC_INT 96 +#define CLK_TOP_CSI0 97 +#define CLK_TOP_CVBSPLL 98 +#define CLK_TOP_AXI_SEL 99 +#define CLK_TOP_MEM_SEL 100 +#define CLK_TOP_MM_SEL 101 +#define CLK_TOP_PWM_SEL 102 +#define CLK_TOP_VDEC_SEL 103 +#define CLK_TOP_VENC_SEL 104 +#define CLK_TOP_MFG_SEL 105 +#define CLK_TOP_CAMTG_SEL 106 +#define CLK_TOP_UART_SEL 107 +#define CLK_TOP_SPI_SEL 108 +#define CLK_TOP_USB20_SEL 109 +#define CLK_TOP_USB30_SEL 110 +#define CLK_TOP_MSDC50_0_HCLK_SEL 111 +#define CLK_TOP_MSDC50_0_SEL 112 +#define CLK_TOP_MSDC30_1_SEL 113 +#define CLK_TOP_MSDC30_2_SEL 114 +#define CLK_TOP_MSDC30_3_SEL 115 +#define CLK_TOP_AUDIO_SEL 116 +#define CLK_TOP_AUD_INTBUS_SEL 117 +#define CLK_TOP_PMICSPI_SEL 118 +#define CLK_TOP_DPILVDS1_SEL 119 +#define CLK_TOP_ATB_SEL 120 +#define CLK_TOP_NR_SEL 121 +#define CLK_TOP_NFI2X_SEL 122 +#define CLK_TOP_IRDA_SEL 123 +#define CLK_TOP_CCI400_SEL 124 +#define CLK_TOP_AUD_1_SEL 125 +#define CLK_TOP_AUD_2_SEL 126 +#define CLK_TOP_MEM_MFG_IN_AS_SEL 127 +#define CLK_TOP_AXI_MFG_IN_AS_SEL 128 +#define CLK_TOP_SCAM_SEL 129 +#define CLK_TOP_NFIECC_SEL 130 +#define CLK_TOP_PE2_MAC_P0_SEL 131 +#define CLK_TOP_PE2_MAC_P1_SEL 132 +#define CLK_TOP_DPILVDS_SEL 133 +#define CLK_TOP_MSDC50_3_HCLK_SEL 134 +#define CLK_TOP_HDCP_SEL 135 +#define CLK_TOP_HDCP_24M_SEL 136 +#define CLK_TOP_RTC_SEL 137 +#define CLK_TOP_SPINOR_SEL 138 +#define CLK_TOP_APLL_SEL 139 +#define CLK_TOP_APLL2_SEL 140 +#define CLK_TOP_A1SYS_HP_SEL 141 +#define CLK_TOP_A2SYS_HP_SEL 142 +#define CLK_TOP_ASM_L_SEL 143 +#define CLK_TOP_ASM_M_SEL 144 +#define CLK_TOP_ASM_H_SEL 145 +#define CLK_TOP_I2SO1_SEL 146 +#define CLK_TOP_I2SO2_SEL 147 +#define CLK_TOP_I2SO3_SEL 148 +#define CLK_TOP_TDMO0_SEL 149 +#define CLK_TOP_TDMO1_SEL 150 +#define CLK_TOP_I2SI1_SEL 151 +#define CLK_TOP_I2SI2_SEL 152 +#define CLK_TOP_I2SI3_SEL 153 +#define CLK_TOP_ETHER_125M_SEL 154 +#define CLK_TOP_ETHER_50M_SEL 155 +#define CLK_TOP_JPGDEC_SEL 156 +#define CLK_TOP_SPISLV_SEL 157 +#define CLK_TOP_ETHER_50M_RMII_SEL 158 +#define CLK_TOP_CAM2TG_SEL 159 +#define CLK_TOP_DI_SEL 160 +#define CLK_TOP_TVD_SEL 161 +#define CLK_TOP_I2C_SEL 162 +#define CLK_TOP_PWM_INFRA_SEL 163 +#define CLK_TOP_MSDC0P_AES_SEL 164 +#define CLK_TOP_CMSYS_SEL 165 +#define CLK_TOP_GCPU_SEL 166 +#define CLK_TOP_AUD_APLL1_SEL 167 +#define CLK_TOP_AUD_APLL2_SEL 168 +#define CLK_TOP_DA_AUDULL_VTX_6P5M_SEL 169 +#define CLK_TOP_APLL_DIV0 170 +#define CLK_TOP_APLL_DIV1 171 +#define CLK_TOP_APLL_DIV2 172 +#define CLK_TOP_APLL_DIV3 173 +#define CLK_TOP_APLL_DIV4 174 +#define CLK_TOP_APLL_DIV5 175 +#define CLK_TOP_APLL_DIV6 176 +#define CLK_TOP_APLL_DIV7 177 +#define CLK_TOP_APLL_DIV_PDN0 178 +#define CLK_TOP_APLL_DIV_PDN1 179 +#define CLK_TOP_APLL_DIV_PDN2 180 +#define CLK_TOP_APLL_DIV_PDN3 181 +#define CLK_TOP_APLL_DIV_PDN4 182 +#define CLK_TOP_APLL_DIV_PDN5 183 +#define CLK_TOP_APLL_DIV_PDN6 184 +#define CLK_TOP_APLL_DIV_PDN7 185 +#define CLK_TOP_NR_CLK 186 + +/* INFRACFG */ + +#define CLK_INFRA_DBGCLK 0 +#define CLK_INFRA_GCE 1 +#define CLK_INFRA_M4U 2 +#define CLK_INFRA_KP 3 +#define CLK_INFRA_AO_SPI0 4 +#define CLK_INFRA_AO_SPI1 5 +#define CLK_INFRA_AO_UART5 6 +#define CLK_INFRA_NR_CLK 7 + +/* PERICFG */ + +#define CLK_PERI_NFI 0 +#define CLK_PERI_THERM 1 +#define CLK_PERI_PWM0 2 +#define CLK_PERI_PWM1 3 +#define CLK_PERI_PWM2 4 +#define CLK_PERI_PWM3 5 +#define CLK_PERI_PWM4 6 +#define CLK_PERI_PWM5 7 +#define CLK_PERI_PWM6 8 +#define CLK_PERI_PWM7 9 +#define CLK_PERI_PWM 10 +#define CLK_PERI_AP_DMA 11 +#define CLK_PERI_MSDC30_0 12 +#define CLK_PERI_MSDC30_1 13 +#define CLK_PERI_MSDC30_2 14 +#define CLK_PERI_MSDC30_3 15 +#define CLK_PERI_UART0 16 +#define CLK_PERI_UART1 17 +#define CLK_PERI_UART2 18 +#define CLK_PERI_UART3 19 +#define CLK_PERI_I2C0 20 +#define CLK_PERI_I2C1 21 +#define CLK_PERI_I2C2 22 +#define CLK_PERI_I2C3 23 +#define CLK_PERI_I2C4 24 +#define CLK_PERI_AUXADC 25 +#define CLK_PERI_SPI0 26 +#define CLK_PERI_SPI 27 +#define CLK_PERI_I2C5 28 +#define CLK_PERI_SPI2 29 +#define CLK_PERI_SPI3 30 +#define CLK_PERI_SPI5 31 +#define CLK_PERI_UART4 32 +#define CLK_PERI_SFLASH 33 +#define CLK_PERI_GMAC 34 +#define CLK_PERI_PCIE0 35 +#define CLK_PERI_PCIE1 36 +#define CLK_PERI_GMAC_PCLK 37 +#define CLK_PERI_MSDC50_0_EN 38 +#define CLK_PERI_MSDC30_1_EN 39 +#define CLK_PERI_MSDC30_2_EN 40 +#define CLK_PERI_MSDC30_3_EN 41 +#define CLK_PERI_MSDC50_0_HCLK_EN 42 +#define CLK_PERI_MSDC50_3_HCLK_EN 43 +#define CLK_PERI_NR_CLK 44 + +/* MCUCFG */ + +#define CLK_MCU_MP0_SEL 0 +#define CLK_MCU_MP2_SEL 1 +#define CLK_MCU_BUS_SEL 2 +#define CLK_MCU_NR_CLK 3 + +/* MFGCFG */ + +#define CLK_MFG_BG3D 0 +#define CLK_MFG_NR_CLK 1 + +/* MMSYS */ + +#define CLK_MM_SMI_COMMON 0 +#define CLK_MM_SMI_LARB0 1 +#define CLK_MM_CAM_MDP 2 +#define CLK_MM_MDP_RDMA0 3 +#define CLK_MM_MDP_RDMA1 4 +#define CLK_MM_MDP_RSZ0 5 +#define CLK_MM_MDP_RSZ1 6 +#define CLK_MM_MDP_RSZ2 7 +#define CLK_MM_MDP_TDSHP0 8 +#define CLK_MM_MDP_TDSHP1 9 +#define CLK_MM_MDP_CROP 10 +#define CLK_MM_MDP_WDMA 11 +#define CLK_MM_MDP_WROT0 12 +#define CLK_MM_MDP_WROT1 13 +#define CLK_MM_FAKE_ENG 14 +#define CLK_MM_MUTEX_32K 15 +#define CLK_MM_DISP_OVL0 16 +#define CLK_MM_DISP_OVL1 17 +#define CLK_MM_DISP_RDMA0 18 +#define CLK_MM_DISP_RDMA1 19 +#define CLK_MM_DISP_RDMA2 20 +#define CLK_MM_DISP_WDMA0 21 +#define CLK_MM_DISP_WDMA1 22 +#define CLK_MM_DISP_COLOR0 23 +#define CLK_MM_DISP_COLOR1 24 +#define CLK_MM_DISP_AAL 25 +#define CLK_MM_DISP_GAMMA 26 +#define CLK_MM_DISP_UFOE 27 +#define CLK_MM_DISP_SPLIT0 28 +#define CLK_MM_DISP_OD 29 +#define CLK_MM_DISP_PWM0_MM 30 +#define CLK_MM_DISP_PWM0_26M 31 +#define CLK_MM_DISP_PWM1_MM 32 +#define CLK_MM_DISP_PWM1_26M 33 +#define CLK_MM_DSI0_ENGINE 34 +#define CLK_MM_DSI0_DIGITAL 35 +#define CLK_MM_DSI1_ENGINE 36 +#define CLK_MM_DSI1_DIGITAL 37 +#define CLK_MM_DPI_PIXEL 38 +#define CLK_MM_DPI_ENGINE 39 +#define CLK_MM_DPI1_PIXEL 40 +#define CLK_MM_DPI1_ENGINE 41 +#define CLK_MM_LVDS_PIXEL 42 +#define CLK_MM_LVDS_CTS 43 +#define CLK_MM_SMI_LARB4 44 +#define CLK_MM_SMI_COMMON1 45 +#define CLK_MM_SMI_LARB5 46 +#define CLK_MM_MDP_RDMA2 47 +#define CLK_MM_MDP_TDSHP2 48 +#define CLK_MM_DISP_OVL2 49 +#define CLK_MM_DISP_WDMA2 50 +#define CLK_MM_DISP_COLOR2 51 +#define CLK_MM_DISP_AAL1 52 +#define CLK_MM_DISP_OD1 53 +#define CLK_MM_LVDS1_PIXEL 54 +#define CLK_MM_LVDS1_CTS 55 +#define CLK_MM_SMI_LARB7 56 +#define CLK_MM_MDP_RDMA3 57 +#define CLK_MM_MDP_WROT2 58 +#define CLK_MM_DSI2 59 +#define CLK_MM_DSI2_DIGITAL 60 +#define CLK_MM_DSI3 61 +#define CLK_MM_DSI3_DIGITAL 62 +#define CLK_MM_NR_CLK 63 + +/* IMGSYS */ + +#define CLK_IMG_SMI_LARB2 0 +#define CLK_IMG_SENINF_SCAM_EN 1 +#define CLK_IMG_SENINF_CAM_EN 2 +#define CLK_IMG_CAM_SV_EN 3 +#define CLK_IMG_CAM_SV1_EN 4 +#define CLK_IMG_CAM_SV2_EN 5 +#define CLK_IMG_NR_CLK 6 + +/* BDPSYS */ + +#define CLK_BDP_BRIDGE_B 0 +#define CLK_BDP_BRIDGE_DRAM 1 +#define CLK_BDP_LARB_DRAM 2 +#define CLK_BDP_WR_CHANNEL_VDI_PXL 3 +#define CLK_BDP_WR_CHANNEL_VDI_DRAM 4 +#define CLK_BDP_WR_CHANNEL_VDI_B 5 +#define CLK_BDP_MT_B 6 +#define CLK_BDP_DISPFMT_27M 7 +#define CLK_BDP_DISPFMT_27M_VDOUT 8 +#define CLK_BDP_DISPFMT_27_74_74 9 +#define CLK_BDP_DISPFMT_2FS 10 +#define CLK_BDP_DISPFMT_2FS_2FS74_148 11 +#define CLK_BDP_DISPFMT_B 12 +#define CLK_BDP_VDO_DRAM 13 +#define CLK_BDP_VDO_2FS 14 +#define CLK_BDP_VDO_B 15 +#define CLK_BDP_WR_CHANNEL_DI_PXL 16 +#define CLK_BDP_WR_CHANNEL_DI_DRAM 17 +#define CLK_BDP_WR_CHANNEL_DI_B 18 +#define CLK_BDP_NR_AGENT 19 +#define CLK_BDP_NR_DRAM 20 +#define CLK_BDP_NR_B 21 +#define CLK_BDP_BRIDGE_RT_B 22 +#define CLK_BDP_BRIDGE_RT_DRAM 23 +#define CLK_BDP_LARB_RT_DRAM 24 +#define CLK_BDP_TVD_TDC 25 +#define CLK_BDP_TVD_54 26 +#define CLK_BDP_TVD_CBUS 27 +#define CLK_BDP_NR_CLK 28 + +/* VDECSYS */ + +#define CLK_VDEC_CKEN 0 +#define CLK_VDEC_LARB1_CKEN 1 +#define CLK_VDEC_IMGRZ_CKEN 2 +#define CLK_VDEC_NR_CLK 3 + +/* VENCSYS */ + +#define CLK_VENC_SMI_COMMON_CON 0 +#define CLK_VENC_VENC 1 +#define CLK_VENC_SMI_LARB6 2 +#define CLK_VENC_NR_CLK 3 + +/* JPGDECSYS */ + +#define CLK_JPGDEC_JPGDEC1 0 +#define CLK_JPGDEC_JPGDEC 1 +#define CLK_JPGDEC_NR_CLK 2 + +#endif /* _DT_BINDINGS_CLK_MT2712_H */ -- cgit v1.2.3-59-g8ed1b From bda921fad518b4d5d2249d41432025ce5b368173 Mon Sep 17 00:00:00 2001 From: Chen Zhong Date: Thu, 5 Oct 2017 11:50:25 +0800 Subject: clk: mediatek: add clocks dt-bindings required header for MT7622 SoC Add the required header for the entire clocks dt-bindings exported from topckgen, apmixedsys, infracfg, pericfg, ethsys, pciesys, ssusbsys and audsys which could be found on MT7622 SoC. Signed-off-by: Chen Zhong Signed-off-by: Sean Wang Signed-off-by: Stephen Boyd --- include/dt-bindings/clock/mt7622-clk.h | 289 +++++++++++++++++++++++++++++++++ 1 file changed, 289 insertions(+) create mode 100644 include/dt-bindings/clock/mt7622-clk.h (limited to 'include') diff --git a/include/dt-bindings/clock/mt7622-clk.h b/include/dt-bindings/clock/mt7622-clk.h new file mode 100644 index 000000000000..3e514ed51d15 --- /dev/null +++ b/include/dt-bindings/clock/mt7622-clk.h @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2017 MediaTek Inc. + * Author: Chen Zhong + * + * 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. + */ + +#ifndef _DT_BINDINGS_CLK_MT7622_H +#define _DT_BINDINGS_CLK_MT7622_H + +/* TOPCKGEN */ + +#define CLK_TOP_TO_U2_PHY 0 +#define CLK_TOP_TO_U2_PHY_1P 1 +#define CLK_TOP_PCIE0_PIPE_EN 2 +#define CLK_TOP_PCIE1_PIPE_EN 3 +#define CLK_TOP_SSUSB_TX250M 4 +#define CLK_TOP_SSUSB_EQ_RX250M 5 +#define CLK_TOP_SSUSB_CDR_REF 6 +#define CLK_TOP_SSUSB_CDR_FB 7 +#define CLK_TOP_SATA_ASIC 8 +#define CLK_TOP_SATA_RBC 9 +#define CLK_TOP_TO_USB3_SYS 10 +#define CLK_TOP_P1_1MHZ 11 +#define CLK_TOP_4MHZ 12 +#define CLK_TOP_P0_1MHZ 13 +#define CLK_TOP_TXCLK_SRC_PRE 14 +#define CLK_TOP_RTC 15 +#define CLK_TOP_MEMPLL 16 +#define CLK_TOP_DMPLL 17 +#define CLK_TOP_SYSPLL_D2 18 +#define CLK_TOP_SYSPLL1_D2 19 +#define CLK_TOP_SYSPLL1_D4 20 +#define CLK_TOP_SYSPLL1_D8 21 +#define CLK_TOP_SYSPLL2_D4 22 +#define CLK_TOP_SYSPLL2_D8 23 +#define CLK_TOP_SYSPLL_D5 24 +#define CLK_TOP_SYSPLL3_D2 25 +#define CLK_TOP_SYSPLL3_D4 26 +#define CLK_TOP_SYSPLL4_D2 27 +#define CLK_TOP_SYSPLL4_D4 28 +#define CLK_TOP_SYSPLL4_D16 29 +#define CLK_TOP_UNIVPLL 30 +#define CLK_TOP_UNIVPLL_D2 31 +#define CLK_TOP_UNIVPLL1_D2 32 +#define CLK_TOP_UNIVPLL1_D4 33 +#define CLK_TOP_UNIVPLL1_D8 34 +#define CLK_TOP_UNIVPLL1_D16 35 +#define CLK_TOP_UNIVPLL2_D2 36 +#define CLK_TOP_UNIVPLL2_D4 37 +#define CLK_TOP_UNIVPLL2_D8 38 +#define CLK_TOP_UNIVPLL2_D16 39 +#define CLK_TOP_UNIVPLL_D5 40 +#define CLK_TOP_UNIVPLL3_D2 41 +#define CLK_TOP_UNIVPLL3_D4 42 +#define CLK_TOP_UNIVPLL3_D16 43 +#define CLK_TOP_UNIVPLL_D7 44 +#define CLK_TOP_UNIVPLL_D80_D4 45 +#define CLK_TOP_UNIV48M 46 +#define CLK_TOP_SGMIIPLL 47 +#define CLK_TOP_SGMIIPLL_D2 48 +#define CLK_TOP_AUD1PLL 49 +#define CLK_TOP_AUD2PLL 50 +#define CLK_TOP_AUD_I2S2_MCK 51 +#define CLK_TOP_TO_USB3_REF 52 +#define CLK_TOP_PCIE1_MAC_EN 53 +#define CLK_TOP_PCIE0_MAC_EN 54 +#define CLK_TOP_ETH_500M 55 +#define CLK_TOP_AXI_SEL 56 +#define CLK_TOP_MEM_SEL 57 +#define CLK_TOP_DDRPHYCFG_SEL 58 +#define CLK_TOP_ETH_SEL 59 +#define CLK_TOP_PWM_SEL 60 +#define CLK_TOP_F10M_REF_SEL 61 +#define CLK_TOP_NFI_INFRA_SEL 62 +#define CLK_TOP_FLASH_SEL 63 +#define CLK_TOP_UART_SEL 64 +#define CLK_TOP_SPI0_SEL 65 +#define CLK_TOP_SPI1_SEL 66 +#define CLK_TOP_MSDC50_0_SEL 67 +#define CLK_TOP_MSDC30_0_SEL 68 +#define CLK_TOP_MSDC30_1_SEL 69 +#define CLK_TOP_A1SYS_HP_SEL 70 +#define CLK_TOP_A2SYS_HP_SEL 71 +#define CLK_TOP_INTDIR_SEL 72 +#define CLK_TOP_AUD_INTBUS_SEL 73 +#define CLK_TOP_PMICSPI_SEL 74 +#define CLK_TOP_SCP_SEL 75 +#define CLK_TOP_ATB_SEL 76 +#define CLK_TOP_HIF_SEL 77 +#define CLK_TOP_AUDIO_SEL 78 +#define CLK_TOP_U2_SEL 79 +#define CLK_TOP_AUD1_SEL 80 +#define CLK_TOP_AUD2_SEL 81 +#define CLK_TOP_IRRX_SEL 82 +#define CLK_TOP_IRTX_SEL 83 +#define CLK_TOP_ASM_L_SEL 84 +#define CLK_TOP_ASM_M_SEL 85 +#define CLK_TOP_ASM_H_SEL 86 +#define CLK_TOP_APLL1_SEL 87 +#define CLK_TOP_APLL2_SEL 88 +#define CLK_TOP_I2S0_MCK_SEL 89 +#define CLK_TOP_I2S1_MCK_SEL 90 +#define CLK_TOP_I2S2_MCK_SEL 91 +#define CLK_TOP_I2S3_MCK_SEL 92 +#define CLK_TOP_APLL1_DIV 93 +#define CLK_TOP_APLL2_DIV 94 +#define CLK_TOP_I2S0_MCK_DIV 95 +#define CLK_TOP_I2S1_MCK_DIV 96 +#define CLK_TOP_I2S2_MCK_DIV 97 +#define CLK_TOP_I2S3_MCK_DIV 98 +#define CLK_TOP_A1SYS_HP_DIV 99 +#define CLK_TOP_A2SYS_HP_DIV 100 +#define CLK_TOP_APLL1_DIV_PD 101 +#define CLK_TOP_APLL2_DIV_PD 102 +#define CLK_TOP_I2S0_MCK_DIV_PD 103 +#define CLK_TOP_I2S1_MCK_DIV_PD 104 +#define CLK_TOP_I2S2_MCK_DIV_PD 105 +#define CLK_TOP_I2S3_MCK_DIV_PD 106 +#define CLK_TOP_A1SYS_HP_DIV_PD 107 +#define CLK_TOP_A2SYS_HP_DIV_PD 108 +#define CLK_TOP_NR_CLK 109 + +/* INFRACFG */ + +#define CLK_INFRA_MUX1_SEL 0 +#define CLK_INFRA_DBGCLK_PD 1 +#define CLK_INFRA_AUDIO_PD 2 +#define CLK_INFRA_IRRX_PD 3 +#define CLK_INFRA_APXGPT_PD 4 +#define CLK_INFRA_PMIC_PD 5 +#define CLK_INFRA_TRNG 6 +#define CLK_INFRA_NR_CLK 7 + +/* PERICFG */ + +#define CLK_PERIBUS_SEL 0 +#define CLK_PERI_THERM_PD 1 +#define CLK_PERI_PWM1_PD 2 +#define CLK_PERI_PWM2_PD 3 +#define CLK_PERI_PWM3_PD 4 +#define CLK_PERI_PWM4_PD 5 +#define CLK_PERI_PWM5_PD 6 +#define CLK_PERI_PWM6_PD 7 +#define CLK_PERI_PWM7_PD 8 +#define CLK_PERI_PWM_PD 9 +#define CLK_PERI_AP_DMA_PD 10 +#define CLK_PERI_MSDC30_0_PD 11 +#define CLK_PERI_MSDC30_1_PD 12 +#define CLK_PERI_UART0_PD 13 +#define CLK_PERI_UART1_PD 14 +#define CLK_PERI_UART2_PD 15 +#define CLK_PERI_UART3_PD 16 +#define CLK_PERI_UART4_PD 17 +#define CLK_PERI_BTIF_PD 18 +#define CLK_PERI_I2C0_PD 19 +#define CLK_PERI_I2C1_PD 20 +#define CLK_PERI_I2C2_PD 21 +#define CLK_PERI_SPI1_PD 22 +#define CLK_PERI_AUXADC_PD 23 +#define CLK_PERI_SPI0_PD 24 +#define CLK_PERI_SNFI_PD 25 +#define CLK_PERI_NFI_PD 26 +#define CLK_PERI_NFIECC_PD 27 +#define CLK_PERI_FLASH_PD 28 +#define CLK_PERI_IRTX_PD 29 +#define CLK_PERI_NR_CLK 30 + +/* APMIXEDSYS */ + +#define CLK_APMIXED_ARMPLL 0 +#define CLK_APMIXED_MAINPLL 1 +#define CLK_APMIXED_UNIV2PLL 2 +#define CLK_APMIXED_ETH1PLL 3 +#define CLK_APMIXED_ETH2PLL 4 +#define CLK_APMIXED_AUD1PLL 5 +#define CLK_APMIXED_AUD2PLL 6 +#define CLK_APMIXED_TRGPLL 7 +#define CLK_APMIXED_SGMIPLL 8 +#define CLK_APMIXED_MAIN_CORE_EN 9 +#define CLK_APMIXED_NR_CLK 10 + +/* AUDIOSYS */ + +#define CLK_AUDIO_AFE 0 +#define CLK_AUDIO_HDMI 1 +#define CLK_AUDIO_SPDF 2 +#define CLK_AUDIO_APLL 3 +#define CLK_AUDIO_I2SIN1 4 +#define CLK_AUDIO_I2SIN2 5 +#define CLK_AUDIO_I2SIN3 6 +#define CLK_AUDIO_I2SIN4 7 +#define CLK_AUDIO_I2SO1 8 +#define CLK_AUDIO_I2SO2 9 +#define CLK_AUDIO_I2SO3 10 +#define CLK_AUDIO_I2SO4 11 +#define CLK_AUDIO_ASRCI1 12 +#define CLK_AUDIO_ASRCI2 13 +#define CLK_AUDIO_ASRCO1 14 +#define CLK_AUDIO_ASRCO2 15 +#define CLK_AUDIO_INTDIR 16 +#define CLK_AUDIO_A1SYS 17 +#define CLK_AUDIO_A2SYS 18 +#define CLK_AUDIO_UL1 19 +#define CLK_AUDIO_UL2 20 +#define CLK_AUDIO_UL3 21 +#define CLK_AUDIO_UL4 22 +#define CLK_AUDIO_UL5 23 +#define CLK_AUDIO_UL6 24 +#define CLK_AUDIO_DL1 25 +#define CLK_AUDIO_DL2 26 +#define CLK_AUDIO_DL3 27 +#define CLK_AUDIO_DL4 28 +#define CLK_AUDIO_DL5 29 +#define CLK_AUDIO_DL6 30 +#define CLK_AUDIO_DLMCH 31 +#define CLK_AUDIO_ARB1 32 +#define CLK_AUDIO_AWB 33 +#define CLK_AUDIO_AWB2 34 +#define CLK_AUDIO_DAI 35 +#define CLK_AUDIO_MOD 36 +#define CLK_AUDIO_ASRCI3 37 +#define CLK_AUDIO_ASRCI4 38 +#define CLK_AUDIO_ASRCO3 39 +#define CLK_AUDIO_ASRCO4 40 +#define CLK_AUDIO_MEM_ASRC1 41 +#define CLK_AUDIO_MEM_ASRC2 42 +#define CLK_AUDIO_MEM_ASRC3 43 +#define CLK_AUDIO_MEM_ASRC4 44 +#define CLK_AUDIO_MEM_ASRC5 45 +#define CLK_AUDIO_NR_CLK 46 + +/* SSUSBSYS */ + +#define CLK_SSUSB_U2_PHY_1P_EN 0 +#define CLK_SSUSB_U2_PHY_EN 1 +#define CLK_SSUSB_REF_EN 2 +#define CLK_SSUSB_SYS_EN 3 +#define CLK_SSUSB_MCU_EN 4 +#define CLK_SSUSB_DMA_EN 5 +#define CLK_SSUSB_NR_CLK 6 + +/* PCIESYS */ + +#define CLK_PCIE_P1_AUX_EN 0 +#define CLK_PCIE_P1_OBFF_EN 1 +#define CLK_PCIE_P1_AHB_EN 2 +#define CLK_PCIE_P1_AXI_EN 3 +#define CLK_PCIE_P1_MAC_EN 4 +#define CLK_PCIE_P1_PIPE_EN 5 +#define CLK_PCIE_P0_AUX_EN 6 +#define CLK_PCIE_P0_OBFF_EN 7 +#define CLK_PCIE_P0_AHB_EN 8 +#define CLK_PCIE_P0_AXI_EN 9 +#define CLK_PCIE_P0_MAC_EN 10 +#define CLK_PCIE_P0_PIPE_EN 11 +#define CLK_SATA_AHB_EN 12 +#define CLK_SATA_AXI_EN 13 +#define CLK_SATA_ASIC_EN 14 +#define CLK_SATA_RBC_EN 15 +#define CLK_SATA_PM_EN 16 +#define CLK_PCIE_NR_CLK 17 + +/* ETHSYS */ + +#define CLK_ETH_HSDMA_EN 0 +#define CLK_ETH_ESW_EN 1 +#define CLK_ETH_GP2_EN 2 +#define CLK_ETH_GP1_EN 3 +#define CLK_ETH_GP0_EN 4 +#define CLK_ETH_NR_CLK 5 + +/* SGMIISYS */ + +#define CLK_SGMII_TX250M_EN 0 +#define CLK_SGMII_RX250M_EN 1 +#define CLK_SGMII_CDR_REF 2 +#define CLK_SGMII_CDR_FB 3 +#define CLK_SGMII_NR_CLK 4 + +#endif /* _DT_BINDINGS_CLK_MT7622_H */ + -- cgit v1.2.3-59-g8ed1b From 908a543ac7cdf3aa8a283ec42cab3c16e2fc45a2 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 24 Sep 2017 18:19:18 +0200 Subject: clk: clk-gpio: Make GPIO clock provider use descriptors only After som grep:ing it turns out nothing in the kernel is really calling clk_[hw_]_register_gpio_[gate|mux](). All existing instances are just created directly from the device tree probe functions at the bottom of the clk-gpio.c clock provider file. This means we can change the signature of the function without any consequences! Everyone should be using GPIO descriptors now, so let's just go in and enforce that. This saves a bit of code since GPIO descriptors know inherently if they are active low so no need for the code keeping track of that. We leave it to the caller to come up with the GPIO descriptor. It is nowadays possible to do that even without a corresponding device, so no excuse not to pass them around. The one in-kernel user lifecycles it using devm_gpiod_get() in gpio_clk_driver_probe(). Cc: Sergej Sawazki Cc: Jyri Sarha Signed-off-by: Linus Walleij Signed-off-by: Stephen Boyd --- drivers/clk/clk-gpio.c | 90 +++++++++++++++++--------------------------- include/linux/clk-provider.h | 12 +++--- 2 files changed, 41 insertions(+), 61 deletions(-) (limited to 'include') diff --git a/drivers/clk/clk-gpio.c b/drivers/clk/clk-gpio.c index 86b245746a6b..9d057073e110 100644 --- a/drivers/clk/clk-gpio.c +++ b/drivers/clk/clk-gpio.c @@ -15,9 +15,7 @@ #include #include #include -#include #include -#include #include #include #include @@ -95,14 +93,12 @@ const struct clk_ops clk_gpio_mux_ops = { EXPORT_SYMBOL_GPL(clk_gpio_mux_ops); static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, unsigned gpio, - bool active_low, unsigned long flags, - const struct clk_ops *clk_gpio_ops) + const char * const *parent_names, u8 num_parents, struct gpio_desc *gpiod, + unsigned long flags, const struct clk_ops *clk_gpio_ops) { struct clk_gpio *clk_gpio; struct clk_hw *hw; struct clk_init_data init = {}; - unsigned long gpio_flags; int err; if (dev) @@ -113,24 +109,13 @@ static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, if (!clk_gpio) return ERR_PTR(-ENOMEM); - if (active_low) - gpio_flags = GPIOF_ACTIVE_LOW | GPIOF_OUT_INIT_HIGH; - else - gpio_flags = GPIOF_OUT_INIT_LOW; - - if (dev) - err = devm_gpio_request_one(dev, gpio, gpio_flags, name); - else - err = gpio_request_one(gpio, gpio_flags, name); - if (err) { - if (err != -EPROBE_DEFER) - pr_err("%s: %s: Error requesting clock control gpio %u\n", - __func__, name, gpio); - if (!dev) - kfree(clk_gpio); - - return ERR_PTR(err); - } + /* + * Set to disabled no matter what: NOTE if the GPIO line is active low + * the GPIO descriptor knows this and will set it high to deassert the + * line. This assumes the GPIO descriptor has been requested using + * GPIOD_ASIS by the callers so we need to initialize it as disabled here. + */ + gpiod_set_value(gpiod, 0); init.name = name; init.ops = clk_gpio_ops; @@ -138,7 +123,7 @@ static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, init.parent_names = parent_names; init.num_parents = num_parents; - clk_gpio->gpiod = gpio_to_desc(gpio); + clk_gpio->gpiod = gpiod; clk_gpio->hw.init = &init; hw = &clk_gpio->hw; @@ -151,7 +136,6 @@ static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, return hw; if (!dev) { - gpiod_put(clk_gpio->gpiod); kfree(clk_gpio); } @@ -164,29 +148,27 @@ static struct clk_hw *clk_register_gpio(struct device *dev, const char *name, * @dev: device that is registering this clock * @name: name of this clock * @parent_name: name of this clock's parent - * @gpio: gpio number to gate this clock - * @active_low: true if gpio should be set to 0 to enable clock + * @gpiod: gpio descriptor to gate this clock * @flags: clock flags */ struct clk_hw *clk_hw_register_gpio_gate(struct device *dev, const char *name, - const char *parent_name, unsigned gpio, bool active_low, + const char *parent_name, struct gpio_desc *gpiod, unsigned long flags) { return clk_register_gpio(dev, name, (parent_name ? &parent_name : NULL), - (parent_name ? 1 : 0), gpio, active_low, flags, + (parent_name ? 1 : 0), gpiod, flags, &clk_gpio_gate_ops); } EXPORT_SYMBOL_GPL(clk_hw_register_gpio_gate); struct clk *clk_register_gpio_gate(struct device *dev, const char *name, - const char *parent_name, unsigned gpio, bool active_low, + const char *parent_name, struct gpio_desc *gpiod, unsigned long flags) { struct clk_hw *hw; - hw = clk_hw_register_gpio_gate(dev, name, parent_name, gpio, active_low, - flags); + hw = clk_hw_register_gpio_gate(dev, name, parent_name, gpiod, flags); if (IS_ERR(hw)) return ERR_CAST(hw); return hw->clk; @@ -199,13 +181,12 @@ EXPORT_SYMBOL_GPL(clk_register_gpio_gate); * @name: name of this clock * @parent_names: names of this clock's parents * @num_parents: number of parents listed in @parent_names - * @gpio: gpio number to gate this clock - * @active_low: true if gpio should be set to 0 to enable clock + * @gpiod: gpio descriptor to gate this clock * @flags: clock flags */ struct clk_hw *clk_hw_register_gpio_mux(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, unsigned gpio, - bool active_low, unsigned long flags) + const char * const *parent_names, u8 num_parents, struct gpio_desc *gpiod, + unsigned long flags) { if (num_parents != 2) { pr_err("mux-clock %s must have 2 parents\n", name); @@ -213,18 +194,18 @@ struct clk_hw *clk_hw_register_gpio_mux(struct device *dev, const char *name, } return clk_register_gpio(dev, name, parent_names, num_parents, - gpio, active_low, flags, &clk_gpio_mux_ops); + gpiod, flags, &clk_gpio_mux_ops); } EXPORT_SYMBOL_GPL(clk_hw_register_gpio_mux); struct clk *clk_register_gpio_mux(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, unsigned gpio, - bool active_low, unsigned long flags) + const char * const *parent_names, u8 num_parents, struct gpio_desc *gpiod, + unsigned long flags) { struct clk_hw *hw; hw = clk_hw_register_gpio_mux(dev, name, parent_names, num_parents, - gpio, active_low, flags); + gpiod, flags); if (IS_ERR(hw)) return ERR_CAST(hw); return hw->clk; @@ -236,10 +217,10 @@ static int gpio_clk_driver_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; const char **parent_names, *gpio_name; unsigned int num_parents; - int gpio; - enum of_gpio_flags of_flags; + struct gpio_desc *gpiod; struct clk *clk; - bool active_low, is_mux; + bool is_mux; + int ret; num_parents = of_clk_get_parent_count(node); if (num_parents) { @@ -255,28 +236,27 @@ static int gpio_clk_driver_probe(struct platform_device *pdev) is_mux = of_device_is_compatible(node, "gpio-mux-clock"); - gpio_name = is_mux ? "select-gpios" : "enable-gpios"; - gpio = of_get_named_gpio_flags(node, gpio_name, 0, &of_flags); - if (gpio < 0) { - if (gpio == -EPROBE_DEFER) + gpio_name = is_mux ? "select" : "enable"; + gpiod = devm_gpiod_get(&pdev->dev, gpio_name, GPIOD_ASIS); + if (IS_ERR(gpiod)) { + ret = PTR_ERR(gpiod); + if (ret == -EPROBE_DEFER) pr_debug("%s: %s: GPIOs not yet available, retry later\n", node->name, __func__); else - pr_err("%s: %s: Can't get '%s' DT property\n", + pr_err("%s: %s: Can't get '%s' named GPIO property\n", node->name, __func__, gpio_name); - return gpio; + return ret; } - active_low = of_flags & OF_GPIO_ACTIVE_LOW; - if (is_mux) clk = clk_register_gpio_mux(&pdev->dev, node->name, - parent_names, num_parents, gpio, active_low, 0); + parent_names, num_parents, gpiod, 0); else clk = clk_register_gpio_gate(&pdev->dev, node->name, - parent_names ? parent_names[0] : NULL, gpio, - active_low, 0); + parent_names ? parent_names[0] : NULL, gpiod, + 0); if (IS_ERR(clk)) return PTR_ERR(clk); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 5100ec1b5d55..063d8cb9926f 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -682,10 +682,10 @@ struct clk_gpio { extern const struct clk_ops clk_gpio_gate_ops; struct clk *clk_register_gpio_gate(struct device *dev, const char *name, - const char *parent_name, unsigned gpio, bool active_low, + const char *parent_name, struct gpio_desc *gpiod, unsigned long flags); struct clk_hw *clk_hw_register_gpio_gate(struct device *dev, const char *name, - const char *parent_name, unsigned gpio, bool active_low, + const char *parent_name, struct gpio_desc *gpiod, unsigned long flags); void clk_hw_unregister_gpio_gate(struct clk_hw *hw); @@ -701,11 +701,11 @@ void clk_hw_unregister_gpio_gate(struct clk_hw *hw); extern const struct clk_ops clk_gpio_mux_ops; struct clk *clk_register_gpio_mux(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, unsigned gpio, - bool active_low, unsigned long flags); + const char * const *parent_names, u8 num_parents, struct gpio_desc *gpiod, + unsigned long flags); struct clk_hw *clk_hw_register_gpio_mux(struct device *dev, const char *name, - const char * const *parent_names, u8 num_parents, unsigned gpio, - bool active_low, unsigned long flags); + const char * const *parent_names, u8 num_parents, struct gpio_desc *gpiod, + unsigned long flags); void clk_hw_unregister_gpio_mux(struct clk_hw *hw); /** -- cgit v1.2.3-59-g8ed1b From aa9ad44a42b4cf4387f8ecddaf8e51707fdcda5a Mon Sep 17 00:00:00 2001 From: Dave Jiang Date: Wed, 23 Aug 2017 12:48:26 -0700 Subject: libnvdimm: move poison list functions to a new 'badrange' file nfit_test needs to use the poison list manipulation code as well. Make it more generic and in the process rename poison to badrange, and move all the related helpers to a new file. Signed-off-by: Dave Jiang [vishal: Add badrange.o to nfit_test's Kbuild] [vishal: add a missed include in bus.c for the new badrange functions] [vishal: rename all instances of 'be' to 'bre'] Signed-off-by: Vishal Verma Signed-off-by: Dan Williams --- drivers/acpi/nfit/core.c | 2 +- drivers/acpi/nfit/mce.c | 2 +- drivers/nvdimm/Makefile | 1 + drivers/nvdimm/badrange.c | 294 ++++++++++++++++++++++++++++++++++++++++++++ drivers/nvdimm/bus.c | 24 ++-- drivers/nvdimm/core.c | 260 +-------------------------------------- drivers/nvdimm/nd-core.h | 3 +- drivers/nvdimm/nd.h | 6 - include/linux/libnvdimm.h | 21 +++- tools/testing/nvdimm/Kbuild | 1 + 10 files changed, 332 insertions(+), 282 deletions(-) create mode 100644 drivers/nvdimm/badrange.c (limited to 'include') diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index aa75d67ceee9..8043bfde7c63 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2486,7 +2486,7 @@ static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc, if (ars_status->out_length < 44 + sizeof(struct nd_ars_record) * (i + 1)) break; - rc = nvdimm_bus_add_poison(nvdimm_bus, + rc = nvdimm_bus_add_badrange(nvdimm_bus, ars_status->records[i].err_address, ars_status->records[i].length); if (rc) diff --git a/drivers/acpi/nfit/mce.c b/drivers/acpi/nfit/mce.c index feeb95d574fa..b92921439657 100644 --- a/drivers/acpi/nfit/mce.c +++ b/drivers/acpi/nfit/mce.c @@ -67,7 +67,7 @@ static int nfit_handle_mce(struct notifier_block *nb, unsigned long val, continue; /* If this fails due to an -ENOMEM, there is little we can do */ - nvdimm_bus_add_poison(acpi_desc->nvdimm_bus, + nvdimm_bus_add_badrange(acpi_desc->nvdimm_bus, ALIGN(mce->addr, L1_CACHE_BYTES), L1_CACHE_BYTES); nvdimm_region_notify(nfit_spa->nd_region, diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile index 909554c3f955..ca6d325438a3 100644 --- a/drivers/nvdimm/Makefile +++ b/drivers/nvdimm/Makefile @@ -20,6 +20,7 @@ libnvdimm-y += region_devs.o libnvdimm-y += region.o libnvdimm-y += namespace_devs.o libnvdimm-y += label.o +libnvdimm-y += badrange.o libnvdimm-$(CONFIG_ND_CLAIM) += claim.o libnvdimm-$(CONFIG_BTT) += btt_devs.o libnvdimm-$(CONFIG_NVDIMM_PFN) += pfn_devs.o diff --git a/drivers/nvdimm/badrange.c b/drivers/nvdimm/badrange.c new file mode 100644 index 000000000000..0b67dcf43234 --- /dev/null +++ b/drivers/nvdimm/badrange.c @@ -0,0 +1,294 @@ +/* + * Copyright(c) 2017 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nd-core.h" +#include "nd.h" + +void badrange_init(struct badrange *badrange) +{ + INIT_LIST_HEAD(&badrange->list); + spin_lock_init(&badrange->lock); +} +EXPORT_SYMBOL_GPL(badrange_init); + +static void append_badrange_entry(struct badrange *badrange, + struct badrange_entry *bre, u64 addr, u64 length) +{ + lockdep_assert_held(&badrange->lock); + bre->start = addr; + bre->length = length; + list_add_tail(&bre->list, &badrange->list); +} + +static int alloc_and_append_badrange_entry(struct badrange *badrange, + u64 addr, u64 length, gfp_t flags) +{ + struct badrange_entry *bre; + + bre = kzalloc(sizeof(*bre), flags); + if (!bre) + return -ENOMEM; + + append_badrange_entry(badrange, bre, addr, length); + return 0; +} + +static int add_badrange(struct badrange *badrange, u64 addr, u64 length) +{ + struct badrange_entry *bre, *bre_new; + + spin_unlock(&badrange->lock); + bre_new = kzalloc(sizeof(*bre_new), GFP_KERNEL); + spin_lock(&badrange->lock); + + if (list_empty(&badrange->list)) { + if (!bre_new) + return -ENOMEM; + append_badrange_entry(badrange, bre_new, addr, length); + return 0; + } + + /* + * There is a chance this is a duplicate, check for those first. + * This will be the common case as ARS_STATUS returns all known + * errors in the SPA space, and we can't query it per region + */ + list_for_each_entry(bre, &badrange->list, list) + if (bre->start == addr) { + /* If length has changed, update this list entry */ + if (bre->length != length) + bre->length = length; + kfree(bre_new); + return 0; + } + + /* + * If not a duplicate or a simple length update, add the entry as is, + * as any overlapping ranges will get resolved when the list is consumed + * and converted to badblocks + */ + if (!bre_new) + return -ENOMEM; + append_badrange_entry(badrange, bre_new, addr, length); + + return 0; +} + +int badrange_add(struct badrange *badrange, u64 addr, u64 length) +{ + int rc; + + spin_lock(&badrange->lock); + rc = add_badrange(badrange, addr, length); + spin_unlock(&badrange->lock); + + return rc; +} +EXPORT_SYMBOL_GPL(badrange_add); + +void badrange_forget(struct badrange *badrange, phys_addr_t start, + unsigned int len) +{ + struct list_head *badrange_list = &badrange->list; + u64 clr_end = start + len - 1; + struct badrange_entry *bre, *next; + + spin_lock(&badrange->lock); + WARN_ON_ONCE(list_empty(badrange_list)); + + /* + * [start, clr_end] is the badrange interval being cleared. + * [bre->start, bre_end] is the badrange_list entry we're comparing + * the above interval against. The badrange list entry may need + * to be modified (update either start or length), deleted, or + * split into two based on the overlap characteristics + */ + + list_for_each_entry_safe(bre, next, badrange_list, list) { + u64 bre_end = bre->start + bre->length - 1; + + /* Skip intervals with no intersection */ + if (bre_end < start) + continue; + if (bre->start > clr_end) + continue; + /* Delete completely overlapped badrange entries */ + if ((bre->start >= start) && (bre_end <= clr_end)) { + list_del(&bre->list); + kfree(bre); + continue; + } + /* Adjust start point of partially cleared entries */ + if ((start <= bre->start) && (clr_end > bre->start)) { + bre->length -= clr_end - bre->start + 1; + bre->start = clr_end + 1; + continue; + } + /* Adjust bre->length for partial clearing at the tail end */ + if ((bre->start < start) && (bre_end <= clr_end)) { + /* bre->start remains the same */ + bre->length = start - bre->start; + continue; + } + /* + * If clearing in the middle of an entry, we split it into + * two by modifying the current entry to represent one half of + * the split, and adding a new entry for the second half. + */ + if ((bre->start < start) && (bre_end > clr_end)) { + u64 new_start = clr_end + 1; + u64 new_len = bre_end - new_start + 1; + + /* Add new entry covering the right half */ + alloc_and_append_badrange_entry(badrange, new_start, + new_len, GFP_NOWAIT); + /* Adjust this entry to cover the left half */ + bre->length = start - bre->start; + continue; + } + } + spin_unlock(&badrange->lock); +} +EXPORT_SYMBOL_GPL(badrange_forget); + +static void set_badblock(struct badblocks *bb, sector_t s, int num) +{ + dev_dbg(bb->dev, "Found a bad range (0x%llx, 0x%llx)\n", + (u64) s * 512, (u64) num * 512); + /* this isn't an error as the hardware will still throw an exception */ + if (badblocks_set(bb, s, num, 1)) + dev_info_once(bb->dev, "%s: failed for sector %llx\n", + __func__, (u64) s); +} + +/** + * __add_badblock_range() - Convert a physical address range to bad sectors + * @bb: badblocks instance to populate + * @ns_offset: namespace offset where the error range begins (in bytes) + * @len: number of bytes of badrange to be added + * + * This assumes that the range provided with (ns_offset, len) is within + * the bounds of physical addresses for this namespace, i.e. lies in the + * interval [ns_start, ns_start + ns_size) + */ +static void __add_badblock_range(struct badblocks *bb, u64 ns_offset, u64 len) +{ + const unsigned int sector_size = 512; + sector_t start_sector, end_sector; + u64 num_sectors; + u32 rem; + + start_sector = div_u64(ns_offset, sector_size); + end_sector = div_u64_rem(ns_offset + len, sector_size, &rem); + if (rem) + end_sector++; + num_sectors = end_sector - start_sector; + + if (unlikely(num_sectors > (u64)INT_MAX)) { + u64 remaining = num_sectors; + sector_t s = start_sector; + + while (remaining) { + int done = min_t(u64, remaining, INT_MAX); + + set_badblock(bb, s, done); + remaining -= done; + s += done; + } + } else + set_badblock(bb, start_sector, num_sectors); +} + +static void badblocks_populate(struct badrange *badrange, + struct badblocks *bb, const struct resource *res) +{ + struct badrange_entry *bre; + + if (list_empty(&badrange->list)) + return; + + list_for_each_entry(bre, &badrange->list, list) { + u64 bre_end = bre->start + bre->length - 1; + + /* Discard intervals with no intersection */ + if (bre_end < res->start) + continue; + if (bre->start > res->end) + continue; + /* Deal with any overlap after start of the namespace */ + if (bre->start >= res->start) { + u64 start = bre->start; + u64 len; + + if (bre_end <= res->end) + len = bre->length; + else + len = res->start + resource_size(res) + - bre->start; + __add_badblock_range(bb, start - res->start, len); + continue; + } + /* + * Deal with overlap for badrange starting before + * the namespace. + */ + if (bre->start < res->start) { + u64 len; + + if (bre_end < res->end) + len = bre->start + bre->length - res->start; + else + len = resource_size(res); + __add_badblock_range(bb, 0, len); + } + } +} + +/** + * nvdimm_badblocks_populate() - Convert a list of badranges to badblocks + * @region: parent region of the range to interrogate + * @bb: badblocks instance to populate + * @res: resource range to consider + * + * The badrange list generated during bus initialization may contain + * multiple, possibly overlapping physical address ranges. Compare each + * of these ranges to the resource range currently being initialized, + * and add badblocks entries for all matching sub-ranges + */ +void nvdimm_badblocks_populate(struct nd_region *nd_region, + struct badblocks *bb, const struct resource *res) +{ + struct nvdimm_bus *nvdimm_bus; + + if (!is_memory(&nd_region->dev)) { + dev_WARN_ONCE(&nd_region->dev, 1, + "%s only valid for pmem regions\n", __func__); + return; + } + nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); + + nvdimm_bus_lock(&nvdimm_bus->dev); + badblocks_populate(&nvdimm_bus->badrange, bb, res); + nvdimm_bus_unlock(&nvdimm_bus->dev); +} +EXPORT_SYMBOL_GPL(nvdimm_badblocks_populate); diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index baf283986a7e..0a5e6cd758fe 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -11,6 +11,7 @@ * General Public License for more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -221,7 +222,7 @@ static void nvdimm_account_cleared_poison(struct nvdimm_bus *nvdimm_bus, phys_addr_t phys, u64 cleared) { if (cleared > 0) - nvdimm_forget_poison(nvdimm_bus, phys, cleared); + badrange_forget(&nvdimm_bus->badrange, phys, cleared); if (cleared > 0 && cleared / 512) nvdimm_clear_badblocks_regions(nvdimm_bus, phys, cleared); @@ -344,11 +345,10 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent, return NULL; INIT_LIST_HEAD(&nvdimm_bus->list); INIT_LIST_HEAD(&nvdimm_bus->mapping_list); - INIT_LIST_HEAD(&nvdimm_bus->poison_list); init_waitqueue_head(&nvdimm_bus->probe_wait); nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL); mutex_init(&nvdimm_bus->reconfig_mutex); - spin_lock_init(&nvdimm_bus->poison_lock); + badrange_init(&nvdimm_bus->badrange); if (nvdimm_bus->id < 0) { kfree(nvdimm_bus); return NULL; @@ -395,15 +395,15 @@ static int child_unregister(struct device *dev, void *data) return 0; } -static void free_poison_list(struct list_head *poison_list) +static void free_badrange_list(struct list_head *badrange_list) { - struct nd_poison *pl, *next; + struct badrange_entry *bre, *next; - list_for_each_entry_safe(pl, next, poison_list, list) { - list_del(&pl->list); - kfree(pl); + list_for_each_entry_safe(bre, next, badrange_list, list) { + list_del(&bre->list); + kfree(bre); } - list_del_init(poison_list); + list_del_init(badrange_list); } static int nd_bus_remove(struct device *dev) @@ -417,9 +417,9 @@ static int nd_bus_remove(struct device *dev) nd_synchronize(); device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister); - spin_lock(&nvdimm_bus->poison_lock); - free_poison_list(&nvdimm_bus->poison_list); - spin_unlock(&nvdimm_bus->poison_lock); + spin_lock(&nvdimm_bus->badrange.lock); + free_badrange_list(&nvdimm_bus->badrange.list); + spin_unlock(&nvdimm_bus->badrange.lock); nvdimm_bus_destroy_ndctl(nvdimm_bus); diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index bb71f0cf8f5d..1dc527660637 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -398,265 +398,11 @@ struct attribute_group nvdimm_bus_attribute_group = { }; EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group); -static void set_badblock(struct badblocks *bb, sector_t s, int num) +int nvdimm_bus_add_badrange(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) { - dev_dbg(bb->dev, "Found a poison range (0x%llx, 0x%llx)\n", - (u64) s * 512, (u64) num * 512); - /* this isn't an error as the hardware will still throw an exception */ - if (badblocks_set(bb, s, num, 1)) - dev_info_once(bb->dev, "%s: failed for sector %llx\n", - __func__, (u64) s); + return badrange_add(&nvdimm_bus->badrange, addr, length); } - -/** - * __add_badblock_range() - Convert a physical address range to bad sectors - * @bb: badblocks instance to populate - * @ns_offset: namespace offset where the error range begins (in bytes) - * @len: number of bytes of poison to be added - * - * This assumes that the range provided with (ns_offset, len) is within - * the bounds of physical addresses for this namespace, i.e. lies in the - * interval [ns_start, ns_start + ns_size) - */ -static void __add_badblock_range(struct badblocks *bb, u64 ns_offset, u64 len) -{ - const unsigned int sector_size = 512; - sector_t start_sector, end_sector; - u64 num_sectors; - u32 rem; - - start_sector = div_u64(ns_offset, sector_size); - end_sector = div_u64_rem(ns_offset + len, sector_size, &rem); - if (rem) - end_sector++; - num_sectors = end_sector - start_sector; - - if (unlikely(num_sectors > (u64)INT_MAX)) { - u64 remaining = num_sectors; - sector_t s = start_sector; - - while (remaining) { - int done = min_t(u64, remaining, INT_MAX); - - set_badblock(bb, s, done); - remaining -= done; - s += done; - } - } else - set_badblock(bb, start_sector, num_sectors); -} - -static void badblocks_populate(struct list_head *poison_list, - struct badblocks *bb, const struct resource *res) -{ - struct nd_poison *pl; - - if (list_empty(poison_list)) - return; - - list_for_each_entry(pl, poison_list, list) { - u64 pl_end = pl->start + pl->length - 1; - - /* Discard intervals with no intersection */ - if (pl_end < res->start) - continue; - if (pl->start > res->end) - continue; - /* Deal with any overlap after start of the namespace */ - if (pl->start >= res->start) { - u64 start = pl->start; - u64 len; - - if (pl_end <= res->end) - len = pl->length; - else - len = res->start + resource_size(res) - - pl->start; - __add_badblock_range(bb, start - res->start, len); - continue; - } - /* Deal with overlap for poison starting before the namespace */ - if (pl->start < res->start) { - u64 len; - - if (pl_end < res->end) - len = pl->start + pl->length - res->start; - else - len = resource_size(res); - __add_badblock_range(bb, 0, len); - } - } -} - -/** - * nvdimm_badblocks_populate() - Convert a list of poison ranges to badblocks - * @region: parent region of the range to interrogate - * @bb: badblocks instance to populate - * @res: resource range to consider - * - * The poison list generated during bus initialization may contain - * multiple, possibly overlapping physical address ranges. Compare each - * of these ranges to the resource range currently being initialized, - * and add badblocks entries for all matching sub-ranges - */ -void nvdimm_badblocks_populate(struct nd_region *nd_region, - struct badblocks *bb, const struct resource *res) -{ - struct nvdimm_bus *nvdimm_bus; - struct list_head *poison_list; - - if (!is_memory(&nd_region->dev)) { - dev_WARN_ONCE(&nd_region->dev, 1, - "%s only valid for pmem regions\n", __func__); - return; - } - nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); - poison_list = &nvdimm_bus->poison_list; - - nvdimm_bus_lock(&nvdimm_bus->dev); - badblocks_populate(poison_list, bb, res); - nvdimm_bus_unlock(&nvdimm_bus->dev); -} -EXPORT_SYMBOL_GPL(nvdimm_badblocks_populate); - -static void append_poison_entry(struct nvdimm_bus *nvdimm_bus, - struct nd_poison *pl, u64 addr, u64 length) -{ - lockdep_assert_held(&nvdimm_bus->poison_lock); - pl->start = addr; - pl->length = length; - list_add_tail(&pl->list, &nvdimm_bus->poison_list); -} - -static int add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length, - gfp_t flags) -{ - struct nd_poison *pl; - - pl = kzalloc(sizeof(*pl), flags); - if (!pl) - return -ENOMEM; - - append_poison_entry(nvdimm_bus, pl, addr, length); - return 0; -} - -static int bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) -{ - struct nd_poison *pl, *pl_new; - - spin_unlock(&nvdimm_bus->poison_lock); - pl_new = kzalloc(sizeof(*pl_new), GFP_KERNEL); - spin_lock(&nvdimm_bus->poison_lock); - - if (list_empty(&nvdimm_bus->poison_list)) { - if (!pl_new) - return -ENOMEM; - append_poison_entry(nvdimm_bus, pl_new, addr, length); - return 0; - } - - /* - * There is a chance this is a duplicate, check for those first. - * This will be the common case as ARS_STATUS returns all known - * errors in the SPA space, and we can't query it per region - */ - list_for_each_entry(pl, &nvdimm_bus->poison_list, list) - if (pl->start == addr) { - /* If length has changed, update this list entry */ - if (pl->length != length) - pl->length = length; - kfree(pl_new); - return 0; - } - - /* - * If not a duplicate or a simple length update, add the entry as is, - * as any overlapping ranges will get resolved when the list is consumed - * and converted to badblocks - */ - if (!pl_new) - return -ENOMEM; - append_poison_entry(nvdimm_bus, pl_new, addr, length); - - return 0; -} - -int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) -{ - int rc; - - spin_lock(&nvdimm_bus->poison_lock); - rc = bus_add_poison(nvdimm_bus, addr, length); - spin_unlock(&nvdimm_bus->poison_lock); - - return rc; -} -EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison); - -void nvdimm_forget_poison(struct nvdimm_bus *nvdimm_bus, phys_addr_t start, - unsigned int len) -{ - struct list_head *poison_list = &nvdimm_bus->poison_list; - u64 clr_end = start + len - 1; - struct nd_poison *pl, *next; - - spin_lock(&nvdimm_bus->poison_lock); - WARN_ON_ONCE(list_empty(poison_list)); - - /* - * [start, clr_end] is the poison interval being cleared. - * [pl->start, pl_end] is the poison_list entry we're comparing - * the above interval against. The poison list entry may need - * to be modified (update either start or length), deleted, or - * split into two based on the overlap characteristics - */ - - list_for_each_entry_safe(pl, next, poison_list, list) { - u64 pl_end = pl->start + pl->length - 1; - - /* Skip intervals with no intersection */ - if (pl_end < start) - continue; - if (pl->start > clr_end) - continue; - /* Delete completely overlapped poison entries */ - if ((pl->start >= start) && (pl_end <= clr_end)) { - list_del(&pl->list); - kfree(pl); - continue; - } - /* Adjust start point of partially cleared entries */ - if ((start <= pl->start) && (clr_end > pl->start)) { - pl->length -= clr_end - pl->start + 1; - pl->start = clr_end + 1; - continue; - } - /* Adjust pl->length for partial clearing at the tail end */ - if ((pl->start < start) && (pl_end <= clr_end)) { - /* pl->start remains the same */ - pl->length = start - pl->start; - continue; - } - /* - * If clearing in the middle of an entry, we split it into - * two by modifying the current entry to represent one half of - * the split, and adding a new entry for the second half. - */ - if ((pl->start < start) && (pl_end > clr_end)) { - u64 new_start = clr_end + 1; - u64 new_len = pl_end - new_start + 1; - - /* Add new entry covering the right half */ - add_poison(nvdimm_bus, new_start, new_len, GFP_NOWAIT); - /* Adjust this entry to cover the left half */ - pl->length = start - pl->start; - continue; - } - } - spin_unlock(&nvdimm_bus->poison_lock); -} -EXPORT_SYMBOL_GPL(nvdimm_forget_poison); +EXPORT_SYMBOL_GPL(nvdimm_bus_add_badrange); #ifdef CONFIG_BLK_DEV_INTEGRITY int nd_integrity_init(struct gendisk *disk, unsigned long meta_size) diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 86bc19ae30da..79274ead54fb 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -29,10 +29,9 @@ struct nvdimm_bus { struct list_head list; struct device dev; int id, probe_active; - struct list_head poison_list; struct list_head mapping_list; struct mutex reconfig_mutex; - spinlock_t poison_lock; + struct badrange badrange; }; struct nvdimm { diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index 156be00e1f76..e958f3724c41 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -34,12 +34,6 @@ enum { NVDIMM_IO_ATOMIC = 1, }; -struct nd_poison { - u64 start; - u64 length; - struct list_head list; -}; - struct nvdimm_drvdata { struct device *dev; int nslabel_size; diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 3eaad2fbf284..f8109ddb5ef1 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h @@ -18,6 +18,18 @@ #include #include #include +#include + +struct badrange_entry { + u64 start; + u64 length; + struct list_head list; +}; + +struct badrange { + struct list_head list; + spinlock_t lock; +}; enum { /* when a dimm supports both PMEM and BLK access a label is required */ @@ -129,9 +141,12 @@ static inline struct nd_blk_region_desc *to_blk_region_desc( } -int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length); -void nvdimm_forget_poison(struct nvdimm_bus *nvdimm_bus, - phys_addr_t start, unsigned int len); +void badrange_init(struct badrange *badrange); +int badrange_add(struct badrange *badrange, u64 addr, u64 length); +void badrange_forget(struct badrange *badrange, phys_addr_t start, + unsigned int len); +int nvdimm_bus_add_badrange(struct nvdimm_bus *nvdimm_bus, u64 addr, + u64 length); struct nvdimm_bus *nvdimm_bus_register(struct device *parent, struct nvdimm_bus_descriptor *nfit_desc); void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus); diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index d870520da68b..3272ab5a5cb9 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild @@ -69,6 +69,7 @@ libnvdimm-y += $(NVDIMM_SRC)/region_devs.o libnvdimm-y += $(NVDIMM_SRC)/region.o libnvdimm-y += $(NVDIMM_SRC)/namespace_devs.o libnvdimm-y += $(NVDIMM_SRC)/label.o +libnvdimm-y += $(NVDIMM_SRC)/badrange.o libnvdimm-$(CONFIG_ND_CLAIM) += $(NVDIMM_SRC)/claim.o libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o libnvdimm-$(CONFIG_NVDIMM_PFN) += $(NVDIMM_SRC)/pfn_devs.o -- cgit v1.2.3-59-g8ed1b From 5e25f5db6abb96ca8ee2aaedcb863daa6dfcc07a Mon Sep 17 00:00:00 2001 From: Dongli Zhang Date: Wed, 1 Nov 2017 09:46:33 +0800 Subject: xen/time: do not decrease steal time after live migration on xen After guest live migration on xen, steal time in /proc/stat (cpustat[CPUTIME_STEAL]) might decrease because steal returned by xen_steal_lock() might be less than this_rq()->prev_steal_time which is derived from previous return value of xen_steal_clock(). For instance, steal time of each vcpu is 335 before live migration. cpu 198 0 368 200064 1962 0 0 1340 0 0 cpu0 38 0 81 50063 492 0 0 335 0 0 cpu1 65 0 97 49763 634 0 0 335 0 0 cpu2 38 0 81 50098 462 0 0 335 0 0 cpu3 56 0 107 50138 374 0 0 335 0 0 After live migration, steal time is reduced to 312. cpu 200 0 370 200330 1971 0 0 1248 0 0 cpu0 38 0 82 50123 500 0 0 312 0 0 cpu1 65 0 97 49832 634 0 0 312 0 0 cpu2 39 0 82 50167 462 0 0 312 0 0 cpu3 56 0 107 50207 374 0 0 312 0 0 Since runstate times are cumulative and cleared during xen live migration by xen hypervisor, the idea of this patch is to accumulate runstate times to global percpu variables before live migration suspend. Once guest VM is resumed, xen_get_runstate_snapshot_cpu() would always return the sum of new runstate times and previously accumulated times stored in global percpu variables. Comment above HYPERVISOR_suspend() has been removed as it is inaccurate: the call can return an error code (e.g., possibly -EPERM in the future). Similar and more severe issue would impact prior linux 4.8-4.10 as discussed by Michael Las at https://0xstubs.org/debugging-a-flaky-cpu-steal-time-counter-on-a-paravirtualized-xen-guest, which would overflow steal time and lead to 100% st usage in top command for linux 4.8-4.10. A backport of this patch would fix that issue. [boris: added linux/slab.h to driver/xen/time.c, slightly reformatted commit message] References: https://0xstubs.org/debugging-a-flaky-cpu-steal-time-counter-on-a-paravirtualized-xen-guest Signed-off-by: Dongli Zhang Reviewed-by: Boris Ostrovsky Signed-off-by: Boris Ostrovsky --- drivers/xen/manage.c | 7 ++--- drivers/xen/time.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++-- include/xen/xen-ops.h | 1 + 3 files changed, 73 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index c425d03d37d2..8835065029d3 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -72,18 +72,15 @@ static int xen_suspend(void *data) } gnttab_suspend(); + xen_manage_runstate_time(-1); xen_arch_pre_suspend(); - /* - * This hypercall returns 1 if suspend was cancelled - * or the domain was merely checkpointed, and 0 if it - * is resuming in a new domain. - */ si->cancelled = HYPERVISOR_suspend(xen_pv_domain() ? virt_to_gfn(xen_start_info) : 0); xen_arch_post_suspend(si->cancelled); + xen_manage_runstate_time(si->cancelled ? 1 : 0); gnttab_resume(); if (!si->cancelled) { diff --git a/drivers/xen/time.c b/drivers/xen/time.c index ac5f23fcafc2..8c46f555d82a 100644 --- a/drivers/xen/time.c +++ b/drivers/xen/time.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -19,6 +20,8 @@ /* runstate info updated by Xen */ static DEFINE_PER_CPU(struct vcpu_runstate_info, xen_runstate); +static DEFINE_PER_CPU(u64[4], old_runstate_time); + /* return an consistent snapshot of 64-bit time/counter value */ static u64 get64(const u64 *p) { @@ -47,8 +50,8 @@ static u64 get64(const u64 *p) return ret; } -static void xen_get_runstate_snapshot_cpu(struct vcpu_runstate_info *res, - unsigned int cpu) +static void xen_get_runstate_snapshot_cpu_delta( + struct vcpu_runstate_info *res, unsigned int cpu) { u64 state_time; struct vcpu_runstate_info *state; @@ -66,6 +69,71 @@ static void xen_get_runstate_snapshot_cpu(struct vcpu_runstate_info *res, (state_time & XEN_RUNSTATE_UPDATE)); } +static void xen_get_runstate_snapshot_cpu(struct vcpu_runstate_info *res, + unsigned int cpu) +{ + int i; + + xen_get_runstate_snapshot_cpu_delta(res, cpu); + + for (i = 0; i < 4; i++) + res->time[i] += per_cpu(old_runstate_time, cpu)[i]; +} + +void xen_manage_runstate_time(int action) +{ + static struct vcpu_runstate_info *runstate_delta; + struct vcpu_runstate_info state; + int cpu, i; + + switch (action) { + case -1: /* backup runstate time before suspend */ + if (unlikely(runstate_delta)) + pr_warn_once("%s: memory leak as runstate_delta is not NULL\n", + __func__); + + runstate_delta = kmalloc_array(num_possible_cpus(), + sizeof(*runstate_delta), + GFP_ATOMIC); + if (unlikely(!runstate_delta)) { + pr_warn("%s: failed to allocate runstate_delta\n", + __func__); + return; + } + + for_each_possible_cpu(cpu) { + xen_get_runstate_snapshot_cpu_delta(&state, cpu); + memcpy(runstate_delta[cpu].time, state.time, + sizeof(runstate_delta[cpu].time)); + } + + break; + + case 0: /* backup runstate time after resume */ + if (unlikely(!runstate_delta)) { + pr_warn("%s: cannot accumulate runstate time as runstate_delta is NULL\n", + __func__); + return; + } + + for_each_possible_cpu(cpu) { + for (i = 0; i < 4; i++) + per_cpu(old_runstate_time, cpu)[i] += + runstate_delta[cpu].time[i]; + } + + break; + + default: /* do not accumulate runstate time for checkpointing */ + break; + } + + if (action != -1 && runstate_delta) { + kfree(runstate_delta); + runstate_delta = NULL; + } +} + /* * Runstate accounting */ diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 218e6aae5433..09072271f122 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -32,6 +32,7 @@ void xen_resume_notifier_unregister(struct notifier_block *nb); bool xen_vcpu_stolen(int vcpu); void xen_setup_runstate_info(int cpu); void xen_time_setup_guest(void); +void xen_manage_runstate_time(int action); void xen_get_runstate_snapshot(struct vcpu_runstate_info *res); u64 xen_steal_clock(int cpu); -- cgit v1.2.3-59-g8ed1b From 47d3d7ac656a1ffb9d0f0d3c845663ed6fd7e78d Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Mon, 30 Oct 2017 14:16:00 -0700 Subject: ipv6: Implement limits on Hop-by-Hop and Destination options RFC 8200 (IPv6) defines Hop-by-Hop options and Destination options extension headers. Both of these carry a list of TLVs which is only limited by the maximum length of the extension header (2048 bytes). By the spec a host must process all the TLVs in these options, however these could be used as a fairly obvious denial of service attack. I think this could in fact be a significant DOS vector on the Internet, one mitigating factor might be that many FWs drop all packets with EH (and obviously this is only IPv6) so an Internet wide attack might not be so effective (yet!). By my calculation, the worse case packet with TLVs in a standard 1500 byte MTU packet that would be processed by the stack contains 1282 invidual TLVs (including pad TLVS) or 724 two byte TLVs. I wrote a quick test program that floods a whole bunch of these packets to a host and sure enough there is substantial time spent in ip6_parse_tlv. These packets contain nothing but unknown TLVS (that are ignored), TLV padding, and bogus UDP header with zero payload length. 25.38% [kernel] [k] __fib6_clean_all 21.63% [kernel] [k] ip6_parse_tlv 4.21% [kernel] [k] __local_bh_enable_ip 2.18% [kernel] [k] ip6_pol_route.isra.39 1.98% [kernel] [k] fib6_walk_continue 1.88% [kernel] [k] _raw_write_lock_bh 1.65% [kernel] [k] dst_release This patch adds configurable limits to Destination and Hop-by-Hop options. There are three limits that may be set: - Limit the number of options in a Hop-by-Hop or Destination options extension header. - Limit the byte length of a Hop-by-Hop or Destination options extension header. - Disallow unrecognized options in a Hop-by-Hop or Destination options extension header. The limits are set in corresponding sysctls: ipv6.sysctl.max_dst_opts_cnt ipv6.sysctl.max_hbh_opts_cnt ipv6.sysctl.max_dst_opts_len ipv6.sysctl.max_hbh_opts_len If a max_*_opts_cnt is less than zero then unknown TLVs are disallowed. The number of known TLVs that are allowed is the absolute value of this number. If a limit is exceeded when processing an extension header the packet is dropped. Default values are set to 8 for options counts, and set to INT_MAX for maximum length. Note the choice to limit options to 8 is an arbitrary guess (roughly based on the fact that the stack supports three HBH options and just one destination option). These limits have being proposed in draft-ietf-6man-rfc6434-bis. Tested (by Martin Lau) I tested out 1 thread (i.e. one raw_udp process). I changed the net.ipv6.max_dst_(opts|hbh)_number between 8 to 2048. With sysctls setting to 2048, the softirq% is packed to 100%. With 8, the softirq% is almost unnoticable from mpstat. v2; - Code and documention cleanup. - Change references of RFC2460 to be RFC8200. - Add reference to RFC6434-bis where the limits will be in standard. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 24 ++++++++++++ include/net/ipv6.h | 40 ++++++++++++++++++++ include/net/netns/ipv6.h | 4 ++ net/ipv6/af_inet6.c | 4 ++ net/ipv6/exthdrs.c | 67 ++++++++++++++++++++++++++++------ net/ipv6/sysctl_net_ipv6.c | 32 ++++++++++++++++ 6 files changed, 159 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 77f4de59dc9c..e6661b205f72 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1385,6 +1385,30 @@ mld_qrv - INTEGER Default: 2 (as specified by RFC3810 9.1) Minimum: 1 (as specified by RFC6636 4.5) +max_dst_opts_cnt - INTEGER + Maximum number of non-padding TLVs allowed in a Destination + options extension header. If this value is less than zero + then unknown options are disallowed and the number of known + TLVs allowed is the absolute value of this number. + Default: 8 + +max_hbh_opts_cnt - INTEGER + Maximum number of non-padding TLVs allowed in a Hop-by-Hop + options extension header. If this value is less than zero + then unknown options are disallowed and the number of known + TLVs allowed is the absolute value of this number. + Default: 8 + +max dst_opts_len - INTEGER + Maximum length allowed for a Destination options extension + header. + Default: INT_MAX (unlimited) + +max hbh_opts_len - INTEGER + Maximum length allowed for a Hop-by-Hop options extension + header. + Default: INT_MAX (unlimited) + IPv6 Fragmentation: ip6frag_high_thresh - INTEGER diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 3cda3b521c36..fb6d67012de6 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -51,6 +51,46 @@ #define IPV6_DEFAULT_HOPLIMIT 64 #define IPV6_DEFAULT_MCASTHOPS 1 +/* Limits on Hop-by-Hop and Destination options. + * + * Per RFC8200 there is no limit on the maximum number or lengths of options in + * Hop-by-Hop or Destination options other then the packet must fit in an MTU. + * We allow configurable limits in order to mitigate potential denial of + * service attacks. + * + * There are three limits that may be set: + * - Limit the number of options in a Hop-by-Hop or Destination options + * extension header + * - Limit the byte length of a Hop-by-Hop or Destination options extension + * header + * - Disallow unknown options + * + * The limits are expressed in corresponding sysctls: + * + * ipv6.sysctl.max_dst_opts_cnt + * ipv6.sysctl.max_hbh_opts_cnt + * ipv6.sysctl.max_dst_opts_len + * ipv6.sysctl.max_hbh_opts_len + * + * max_*_opts_cnt is the number of TLVs that are allowed for Destination + * options or Hop-by-Hop options. If the number is less than zero then unknown + * TLVs are disallowed and the number of known options that are allowed is the + * absolute value. Setting the value to INT_MAX indicates no limit. + * + * max_*_opts_len is the length limit in bytes of a Destination or + * Hop-by-Hop options extension header. Setting the value to INT_MAX + * indicates no length limit. + * + * If a limit is exceeded when processing an extension header the packet is + * silently discarded. + */ + +/* Default limits for Hop-by-Hop and Destination options */ +#define IP6_DEFAULT_MAX_DST_OPTS_CNT 8 +#define IP6_DEFAULT_MAX_HBH_OPTS_CNT 8 +#define IP6_DEFAULT_MAX_DST_OPTS_LEN INT_MAX /* No limit */ +#define IP6_DEFAULT_MAX_HBH_OPTS_LEN INT_MAX /* No limit */ + /* * Addr type * diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 2ea1ed341ef8..600ba1c1befc 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -37,6 +37,10 @@ struct netns_sysctl_ipv6 { int idgen_delay; int flowlabel_state_ranges; int flowlabel_reflect; + int max_dst_opts_cnt; + int max_hbh_opts_cnt; + int max_dst_opts_len; + int max_hbh_opts_len; }; struct netns_ipv6 { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index fe5262fd6aa5..c26f71234b9c 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -810,6 +810,10 @@ static int __net_init inet6_net_init(struct net *net) net->ipv6.sysctl.idgen_retries = 3; net->ipv6.sysctl.idgen_delay = 1 * HZ; net->ipv6.sysctl.flowlabel_state_ranges = 0; + net->ipv6.sysctl.max_dst_opts_cnt = IP6_DEFAULT_MAX_DST_OPTS_CNT; + net->ipv6.sysctl.max_hbh_opts_cnt = IP6_DEFAULT_MAX_HBH_OPTS_CNT; + net->ipv6.sysctl.max_dst_opts_len = IP6_DEFAULT_MAX_DST_OPTS_LEN; + net->ipv6.sysctl.max_hbh_opts_len = IP6_DEFAULT_MAX_HBH_OPTS_LEN; atomic_set(&net->ipv6.fib6_sernum, 1); err = ipv6_init_mibs(net); diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 9f918a770f87..83bd75713535 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -74,8 +74,20 @@ struct tlvtype_proc { /* An unknown option is detected, decide what to do */ -static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff) +static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff, + bool disallow_unknowns) { + if (disallow_unknowns) { + /* If unknown TLVs are disallowed by configuration + * then always silently drop packet. Note this also + * means no ICMP parameter problem is sent which + * could be a good property to mitigate a reflection DOS + * attack. + */ + + goto drop; + } + switch ((skb_network_header(skb)[optoff] & 0xC0) >> 6) { case 0: /* ignore */ return true; @@ -95,20 +107,30 @@ static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff) return false; } +drop: kfree_skb(skb); return false; } /* Parse tlv encoded option header (hop-by-hop or destination) */ -static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb) +static bool ip6_parse_tlv(const struct tlvtype_proc *procs, + struct sk_buff *skb, + int max_count) { - const struct tlvtype_proc *curr; + int len = (skb_transport_header(skb)[1] + 1) << 3; const unsigned char *nh = skb_network_header(skb); int off = skb_network_header_len(skb); - int len = (skb_transport_header(skb)[1] + 1) << 3; + const struct tlvtype_proc *curr; + bool disallow_unknowns = false; + int tlv_count = 0; int padlen = 0; + if (unlikely(max_count < 0)) { + disallow_unknowns = true; + max_count = -max_count; + } + if (skb_transport_offset(skb) + len > skb_headlen(skb)) goto bad; @@ -149,6 +171,11 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb) default: /* Other TLV code so scan list */ if (optlen > len) goto bad; + + tlv_count++; + if (tlv_count > max_count) + goto bad; + for (curr = procs; curr->type >= 0; curr++) { if (curr->type == nh[off]) { /* type specific length/alignment @@ -159,10 +186,10 @@ static bool ip6_parse_tlv(const struct tlvtype_proc *procs, struct sk_buff *skb) break; } } - if (curr->type < 0) { - if (ip6_tlvopt_unknown(skb, off) == 0) - return false; - } + if (curr->type < 0 && + !ip6_tlvopt_unknown(skb, off, disallow_unknowns)) + return false; + padlen = 0; break; } @@ -258,23 +285,31 @@ static int ipv6_destopt_rcv(struct sk_buff *skb) __u16 dstbuf; #endif struct dst_entry *dst = skb_dst(skb); + struct net *net = dev_net(skb->dev); + int extlen; if (!pskb_may_pull(skb, skb_transport_offset(skb) + 8) || !pskb_may_pull(skb, (skb_transport_offset(skb) + ((skb_transport_header(skb)[1] + 1) << 3)))) { __IP6_INC_STATS(dev_net(dst->dev), ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS); +fail_and_free: kfree_skb(skb); return -1; } + extlen = (skb_transport_header(skb)[1] + 1) << 3; + if (extlen > net->ipv6.sysctl.max_dst_opts_len) + goto fail_and_free; + opt->lastopt = opt->dst1 = skb_network_header_len(skb); #if IS_ENABLED(CONFIG_IPV6_MIP6) dstbuf = opt->dst1; #endif - if (ip6_parse_tlv(tlvprocdestopt_lst, skb)) { - skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3; + if (ip6_parse_tlv(tlvprocdestopt_lst, skb, + init_net.ipv6.sysctl.max_dst_opts_cnt)) { + skb->transport_header += extlen; opt = IP6CB(skb); #if IS_ENABLED(CONFIG_IPV6_MIP6) opt->nhoff = dstbuf; @@ -803,6 +838,8 @@ static const struct tlvtype_proc tlvprochopopt_lst[] = { int ipv6_parse_hopopts(struct sk_buff *skb) { struct inet6_skb_parm *opt = IP6CB(skb); + struct net *net = dev_net(skb->dev); + int extlen; /* * skb_network_header(skb) is equal to skb->data, and @@ -813,13 +850,19 @@ int ipv6_parse_hopopts(struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(struct ipv6hdr) + 8) || !pskb_may_pull(skb, (sizeof(struct ipv6hdr) + ((skb_transport_header(skb)[1] + 1) << 3)))) { +fail_and_free: kfree_skb(skb); return -1; } + extlen = (skb_transport_header(skb)[1] + 1) << 3; + if (extlen > net->ipv6.sysctl.max_hbh_opts_len) + goto fail_and_free; + opt->flags |= IP6SKB_HOPBYHOP; - if (ip6_parse_tlv(tlvprochopopt_lst, skb)) { - skb->transport_header += (skb_transport_header(skb)[1] + 1) << 3; + if (ip6_parse_tlv(tlvprochopopt_lst, skb, + init_net.ipv6.sysctl.max_hbh_opts_cnt)) { + skb->transport_header += extlen; opt = IP6CB(skb); opt->nhoff = sizeof(struct ipv6hdr); return 1; diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 6fbf8ae5e52c..4a2f0fd870bc 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -97,6 +97,34 @@ static struct ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec, }, + { + .procname = "max_dst_opts_number", + .data = &init_net.ipv6.sysctl.max_dst_opts_cnt, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { + .procname = "max_hbh_opts_number", + .data = &init_net.ipv6.sysctl.max_hbh_opts_cnt, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { + .procname = "max_dst_opts_length", + .data = &init_net.ipv6.sysctl.max_dst_opts_len, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { + .procname = "max_hbh_length", + .data = &init_net.ipv6.sysctl.max_hbh_opts_len, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, { } }; @@ -157,6 +185,10 @@ static int __net_init ipv6_sysctl_net_init(struct net *net) ipv6_table[7].data = &net->ipv6.sysctl.flowlabel_state_ranges; ipv6_table[8].data = &net->ipv6.sysctl.ip_nonlocal_bind; ipv6_table[9].data = &net->ipv6.sysctl.flowlabel_reflect; + ipv6_table[10].data = &net->ipv6.sysctl.max_dst_opts_cnt; + ipv6_table[11].data = &net->ipv6.sysctl.max_hbh_opts_cnt; + ipv6_table[12].data = &net->ipv6.sysctl.max_dst_opts_len; + ipv6_table[13].data = &net->ipv6.sysctl.max_hbh_opts_len; ipv6_route_table = ipv6_route_sysctl_init(net); if (!ipv6_route_table) -- cgit v1.2.3-59-g8ed1b From cf34ce3da1e41579296364509266c7dac573822a Mon Sep 17 00:00:00 2001 From: Song Liu Date: Mon, 30 Oct 2017 14:41:35 -0700 Subject: tcp: add tracepoint trace_tcp_retransmit_synack() This tracepoint can be used to trace synack retransmits. It maintains pointer to struct request_sock. We cannot simply reuse trace_tcp_retransmit_skb() here, because the sk here is the LISTEN socket. The IP addresses and ports should be extracted from struct request_sock. Note that, like many other tracepoints, this patch uses IS_ENABLED in TP_fast_assign macro, which triggers sparse warning like: ./include/trace/events/tcp.h:274:1: error: directive in argument list ./include/trace/events/tcp.h:281:1: error: directive in argument list However, there is no good solution to avoid these warnings. To the best of our knowledge, these warnings are harmless. Signed-off-by: Song Liu Acked-by: Alexei Starovoitov Acked-by: Martin KaFai Lau Signed-off-by: David S. Miller --- include/trace/events/tcp.h | 56 ++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_output.c | 1 + 2 files changed, 57 insertions(+) (limited to 'include') diff --git a/include/trace/events/tcp.h b/include/trace/events/tcp.h index 03699ba71623..07cccca6cbf1 100644 --- a/include/trace/events/tcp.h +++ b/include/trace/events/tcp.h @@ -237,6 +237,62 @@ TRACE_EVENT(tcp_set_state, show_tcp_state_name(__entry->newstate)) ); +TRACE_EVENT(tcp_retransmit_synack, + + TP_PROTO(const struct sock *sk, const struct request_sock *req), + + TP_ARGS(sk, req), + + TP_STRUCT__entry( + __field(const void *, skaddr) + __field(const void *, req) + __field(__u16, sport) + __field(__u16, dport) + __array(__u8, saddr, 4) + __array(__u8, daddr, 4) + __array(__u8, saddr_v6, 16) + __array(__u8, daddr_v6, 16) + ), + + TP_fast_assign( + struct inet_request_sock *ireq = inet_rsk(req); + struct in6_addr *pin6; + __be32 *p32; + + __entry->skaddr = sk; + __entry->req = req; + + __entry->sport = ireq->ir_num; + __entry->dport = ntohs(ireq->ir_rmt_port); + + p32 = (__be32 *) __entry->saddr; + *p32 = ireq->ir_loc_addr; + + p32 = (__be32 *) __entry->daddr; + *p32 = ireq->ir_rmt_addr; + +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family == AF_INET6) { + pin6 = (struct in6_addr *)__entry->saddr_v6; + *pin6 = ireq->ir_v6_loc_addr; + pin6 = (struct in6_addr *)__entry->daddr_v6; + *pin6 = ireq->ir_v6_rmt_addr; + } else +#endif + { + pin6 = (struct in6_addr *)__entry->saddr_v6; + ipv6_addr_set_v4mapped(ireq->ir_loc_addr, pin6); + pin6 = (struct in6_addr *)__entry->daddr_v6; + ipv6_addr_set_v4mapped(ireq->ir_rmt_addr, pin6); + } + ), + + TP_printk("sport=%hu dport=%hu saddr=%pI4 daddr=%pI4 saddrv6=%pI6c daddrv6=%pI6c", + __entry->sport, __entry->dport, + __entry->saddr, __entry->daddr, + __entry->saddr_v6, __entry->daddr_v6) +); + #endif /* _TRACE_TCP_H */ /* This part must be outside protection */ diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index a85e8a282d17..06a0c89ffe40 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -3782,6 +3782,7 @@ int tcp_rtx_synack(const struct sock *sk, struct request_sock *req) __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSYNRETRANS); if (unlikely(tcp_passive_fastopen(sk))) tcp_sk(sk)->total_retrans++; + trace_tcp_retransmit_synack(sk, req); } return res; } -- cgit v1.2.3-59-g8ed1b From 054287295b1132c8742ea55f8e3af9cbd630c932 Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Thu, 2 Nov 2017 10:36:48 +0100 Subject: net: Define eth_stp_addr in linux/etherdevice.h The lan9303 driver defines eth_stp_addr as a synonym to eth_reserved_addr_base to get the STP ethernet address 01:80:c2:00:00:00. eth_reserved_addr_base is also used to define the start of Bridge Reserved ethernet address range, which happen to be the STP address. br_dev_setup refer to eth_reserved_addr_base as a definition of STP address. Clean up by: - Move the eth_stp_addr definition to linux/etherdevice.h - Use eth_stp_addr instead of eth_reserved_addr_base in br_dev_setup. Signed-off-by: Egil Hjelmeland Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- include/linux/dsa/lan9303.h | 2 -- include/linux/etherdevice.h | 1 + net/bridge/br_device.c | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/dsa/lan9303.h b/include/linux/dsa/lan9303.h index b2110e69630f..05d8d136baab 100644 --- a/include/linux/dsa/lan9303.h +++ b/include/linux/dsa/lan9303.h @@ -34,5 +34,3 @@ struct lan9303 { **/ struct lan9303_alr_cache_entry alr_cache[LAN9303_NUM_ALR_RECORDS]; }; - -#define eth_stp_addr eth_reserved_addr_base diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index 2d9f80848d4b..263dbcad22fc 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -66,6 +66,7 @@ int eth_gro_complete(struct sk_buff *skb, int nhoff); /* Reserved Ethernet Addresses per IEEE 802.1Q */ static const u8 eth_reserved_addr_base[ETH_ALEN] __aligned(2) = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; +#define eth_stp_addr eth_reserved_addr_base /** * is_link_local_ether_addr - Determine if given Ethernet address is link-local diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 28bb22186fa0..af5b8c87f590 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -421,7 +421,7 @@ void br_dev_setup(struct net_device *dev) br->bridge_id.prio[0] = 0x80; br->bridge_id.prio[1] = 0x00; - ether_addr_copy(br->group_addr, eth_reserved_addr_base); + ether_addr_copy(br->group_addr, eth_stp_addr); br->stp_enabled = BR_NO_STP; br->group_fwd_mask = BR_GROUPFWD_DEFAULT; -- cgit v1.2.3-59-g8ed1b From 3ae6ec08292f01c6782d1a80be0b2cc675e0ecfc Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 2 Nov 2017 17:14:05 +0100 Subject: ipv4: Send a netevent whenever multipath hash policy is changed Devices performing IPv4 forwarding need to update their multipath hash policy whenever it is changed. Inform these devices by generating a netevent. Signed-off-by: Ido Schimmel Reviewed-by: Petr Machata Signed-off-by: Jiri Pirko Acked-by: David Ahern Signed-off-by: David S. Miller --- include/net/netevent.h | 1 + net/ipv4/sysctl_net_ipv4.c | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/netevent.h b/include/net/netevent.h index f440df172b56..e3f0e8f2f6e8 100644 --- a/include/net/netevent.h +++ b/include/net/netevent.h @@ -25,6 +25,7 @@ enum netevent_notif_type { NETEVENT_NEIGH_UPDATE = 1, /* arg is struct neighbour ptr */ NETEVENT_REDIRECT, /* arg is struct netevent_redirect ptr */ NETEVENT_DELAY_PROBE_TIME_UPDATE, /* arg is struct neigh_parms ptr */ + NETEVENT_MULTIPATH_HASH_UPDATE, /* arg is struct net ptr */ }; int register_netevent_notifier(struct notifier_block *nb); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 4602af6d5358..8dcc2b185fcc 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -25,6 +25,7 @@ #include #include #include +#include static int zero; static int one = 1; @@ -385,6 +386,23 @@ static int proc_tcp_available_ulp(struct ctl_table *ctl, return ret; } +#ifdef CONFIG_IP_ROUTE_MULTIPATH +static int proc_fib_multipath_hash_policy(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + struct net *net = container_of(table->data, struct net, + ipv4.sysctl_fib_multipath_hash_policy); + int ret; + + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + if (write && ret == 0) + call_netevent_notifiers(NETEVENT_MULTIPATH_HASH_UPDATE, net); + + return ret; +} +#endif + static struct ctl_table ipv4_table[] = { { .procname = "tcp_max_orphans", @@ -907,7 +925,7 @@ static struct ctl_table ipv4_net_table[] = { .data = &init_net.ipv4.sysctl_fib_multipath_hash_policy, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = proc_fib_multipath_hash_policy, .extra1 = &zero, .extra2 = &one, }, -- cgit v1.2.3-59-g8ed1b From 17c0899682a55666e58f2200d5599f3a7b22867f Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Thu, 2 Nov 2017 17:21:41 -0700 Subject: usb: remove msm_hsusb_hw.h The last two remaining drivers (ehci-msm.c and phy-msm-usb.c) that needed this header were recently removed, so delete this now-unused file. Signed-off-by: Jack Pham Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/msm_hsusb_hw.h | 77 ---------------------------------------- 1 file changed, 77 deletions(-) delete mode 100644 include/linux/usb/msm_hsusb_hw.h (limited to 'include') diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h deleted file mode 100644 index 974c3796a23f..000000000000 --- a/include/linux/usb/msm_hsusb_hw.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2007 Google, Inc. - * Author: Brian Swetland - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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. - * - */ - -#ifndef __LINUX_USB_GADGET_MSM72K_UDC_H__ -#define __LINUX_USB_GADGET_MSM72K_UDC_H__ - -/* USB phy selector - in TCSR address range */ -#define USB2_PHY_SEL 0xfd4ab000 - -#define USB_AHBBURST (MSM_USB_BASE + 0x0090) -#define USB_AHBMODE (MSM_USB_BASE + 0x0098) -#define USB_GENCONFIG_2 (MSM_USB_BASE + 0x00a0) -#define ULPI_TX_PKT_EN_CLR_FIX BIT(19) - -#define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ - -#define USB_USBCMD (MSM_USB_BASE + 0x0140) -#define USB_PORTSC (MSM_USB_BASE + 0x0184) -#define USB_OTGSC (MSM_USB_BASE + 0x01A4) -#define USB_USBMODE (MSM_USB_BASE + 0x01A8) -#define USB_PHY_CTRL (MSM_USB_BASE + 0x0240) -#define USB_PHY_CTRL2 (MSM_USB_BASE + 0x0278) - -#define GENCONFIG_2_SESS_VLD_CTRL_EN BIT(7) -#define USBCMD_SESS_VLD_CTRL BIT(25) - -#define USBCMD_RESET 2 -#define USB_USBINTR (MSM_USB_BASE + 0x0148) - -#define PORTSC_PHCD (1 << 23) /* phy suspend mode */ -#define PORTSC_PTS_MASK (3 << 30) -#define PORTSC_PTS_ULPI (2 << 30) -#define PORTSC_PTS_SERIAL (3 << 30) - -#define USB_ULPI_VIEWPORT (MSM_USB_BASE + 0x0170) -#define ULPI_RUN (1 << 30) -#define ULPI_WRITE (1 << 29) -#define ULPI_READ (0 << 29) -#define ULPI_ADDR(n) (((n) & 255) << 16) -#define ULPI_DATA(n) ((n) & 255) -#define ULPI_DATA_READ(n) (((n) >> 8) & 255) - -/* synopsys 28nm phy registers */ -#define ULPI_PWR_CLK_MNG_REG 0x88 -#define OTG_COMP_DISABLE BIT(0) - -#define ULPI_MISC_A 0x96 -#define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) -#define ULPI_MISC_A_VBUSVLDEXT BIT(0) - -#define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */ -#define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */ -#define PHY_RETEN (1 << 1) /* PHY retention enable/disable */ -#define PHY_POR_ASSERT (1 << 0) /* USB2 28nm PHY POR ASSERT */ - -/* OTG definitions */ -#define OTGSC_INTSTS_MASK (0x7f << 16) -#define OTGSC_ID (1 << 8) -#define OTGSC_BSV (1 << 11) -#define OTGSC_IDIS (1 << 16) -#define OTGSC_BSVIS (1 << 19) -#define OTGSC_IDIE (1 << 24) -#define OTGSC_BSVIE (1 << 27) - -#endif /* __LINUX_USB_GADGET_MSM72K_UDC_H__ */ -- cgit v1.2.3-59-g8ed1b From c7eb7d7230509ec862d4144f7a831f995bc5d028 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 3 Nov 2017 11:46:24 +0100 Subject: net: sched: introduce chain_head_change callback Add a callback that is to be called whenever head of the chain changes. Also provide a callback for the default case when the caller gets a block using non-extended getter. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 14 ++++++------ include/net/sch_generic.h | 5 ++++- net/sched/cls_api.c | 54 ++++++++++++++++++++++++++++------------------- net/sched/sch_ingress.c | 36 +++++++++++++++++-------------- 4 files changed, 62 insertions(+), 47 deletions(-) (limited to 'include') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index d15c40c7bde7..505d4b71975f 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -26,6 +26,8 @@ enum tcf_block_binder_type { struct tcf_block_ext_info { enum tcf_block_binder_type binder_type; + tcf_chain_head_change_t *chain_head_change; + void *chain_head_change_priv; }; struct tcf_block_cb; @@ -37,12 +39,10 @@ struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index, void tcf_chain_put(struct tcf_chain *chain); int tcf_block_get(struct tcf_block **p_block, struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q); -int tcf_block_get_ext(struct tcf_block **p_block, - struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, +int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, struct tcf_block_ext_info *ei); void tcf_block_put(struct tcf_block *block); -void tcf_block_put_ext(struct tcf_block *block, - struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, +void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, struct tcf_block_ext_info *ei); static inline struct Qdisc *tcf_block_q(struct tcf_block *block) @@ -82,8 +82,7 @@ int tcf_block_get(struct tcf_block **p_block, } static inline -int tcf_block_get_ext(struct tcf_block **p_block, - struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, +int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, struct tcf_block_ext_info *ei) { return 0; @@ -94,8 +93,7 @@ static inline void tcf_block_put(struct tcf_block *block) } static inline -void tcf_block_put_ext(struct tcf_block *block, - struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, +void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, struct tcf_block_ext_info *ei) { } diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index c23e938f5b19..f230269e0bfb 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -260,9 +260,12 @@ struct qdisc_skb_cb { unsigned char data[QDISC_CB_PRIV_LEN]; }; +typedef void tcf_chain_head_change_t(struct tcf_proto *tp_head, void *priv); + struct tcf_chain { struct tcf_proto __rcu *filter_chain; - struct tcf_proto __rcu **p_filter_chain; + tcf_chain_head_change_t *chain_head_change; + void *chain_head_change_priv; struct list_head list; struct tcf_block *block; u32 index; /* chain index */ diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 8d1885abee83..206e19f4fc01 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -195,12 +195,19 @@ static struct tcf_chain *tcf_chain_create(struct tcf_block *block, return chain; } +static void tcf_chain_head_change(struct tcf_chain *chain, + struct tcf_proto *tp_head) +{ + if (chain->chain_head_change) + chain->chain_head_change(tp_head, + chain->chain_head_change_priv); +} + static void tcf_chain_flush(struct tcf_chain *chain) { struct tcf_proto *tp; - if (chain->p_filter_chain) - RCU_INIT_POINTER(*chain->p_filter_chain, NULL); + tcf_chain_head_change(chain, NULL); while ((tp = rtnl_dereference(chain->filter_chain)) != NULL) { RCU_INIT_POINTER(chain->filter_chain, tp->next); tcf_chain_put(chain); @@ -242,13 +249,6 @@ void tcf_chain_put(struct tcf_chain *chain) } EXPORT_SYMBOL(tcf_chain_put); -static void -tcf_chain_filter_chain_ptr_set(struct tcf_chain *chain, - struct tcf_proto __rcu **p_filter_chain) -{ - chain->p_filter_chain = p_filter_chain; -} - static void tcf_block_offload_cmd(struct tcf_block *block, struct Qdisc *q, struct tcf_block_ext_info *ei, enum tc_block_command command) @@ -276,8 +276,7 @@ static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q, tcf_block_offload_cmd(block, q, ei, TC_BLOCK_UNBIND); } -int tcf_block_get_ext(struct tcf_block **p_block, - struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, +int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q, struct tcf_block_ext_info *ei) { struct tcf_block *block = kzalloc(sizeof(*block), GFP_KERNEL); @@ -295,7 +294,9 @@ int tcf_block_get_ext(struct tcf_block **p_block, err = -ENOMEM; goto err_chain_create; } - tcf_chain_filter_chain_ptr_set(chain, p_filter_chain); + WARN_ON(!ei->chain_head_change); + chain->chain_head_change = ei->chain_head_change; + chain->chain_head_change_priv = ei->chain_head_change_priv; block->net = qdisc_net(q); block->q = q; tcf_block_offload_bind(block, q, ei); @@ -308,12 +309,23 @@ err_chain_create: } EXPORT_SYMBOL(tcf_block_get_ext); +static void tcf_chain_head_change_dflt(struct tcf_proto *tp_head, void *priv) +{ + struct tcf_proto __rcu **p_filter_chain = priv; + + rcu_assign_pointer(*p_filter_chain, tp_head); +} + int tcf_block_get(struct tcf_block **p_block, struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q) { - struct tcf_block_ext_info ei = {0, }; + struct tcf_block_ext_info ei = { + .chain_head_change = tcf_chain_head_change_dflt, + .chain_head_change_priv = p_filter_chain, + }; - return tcf_block_get_ext(p_block, p_filter_chain, q, &ei); + WARN_ON(!p_filter_chain); + return tcf_block_get_ext(p_block, q, &ei); } EXPORT_SYMBOL(tcf_block_get); @@ -334,8 +346,7 @@ static void tcf_block_put_final(struct work_struct *work) * actions should be all removed after flushing. However, filters are now * destroyed in tc filter workqueue with RTNL lock, they can not race here. */ -void tcf_block_put_ext(struct tcf_block *block, - struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q, +void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q, struct tcf_block_ext_info *ei) { struct tcf_chain *chain, *tmp; @@ -361,7 +372,7 @@ void tcf_block_put(struct tcf_block *block) if (!block) return; - tcf_block_put_ext(block, NULL, block->q, &ei); + tcf_block_put_ext(block, block->q, &ei); } EXPORT_SYMBOL(tcf_block_put); @@ -537,9 +548,8 @@ static void tcf_chain_tp_insert(struct tcf_chain *chain, struct tcf_chain_info *chain_info, struct tcf_proto *tp) { - if (chain->p_filter_chain && - *chain_info->pprev == chain->filter_chain) - rcu_assign_pointer(*chain->p_filter_chain, tp); + if (*chain_info->pprev == chain->filter_chain) + tcf_chain_head_change(chain, tp); RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain_info)); rcu_assign_pointer(*chain_info->pprev, tp); tcf_chain_hold(chain); @@ -551,8 +561,8 @@ static void tcf_chain_tp_remove(struct tcf_chain *chain, { struct tcf_proto *next = rtnl_dereference(chain_info->next); - if (chain->p_filter_chain && tp == chain->filter_chain) - RCU_INIT_POINTER(*chain->p_filter_chain, next); + if (tp == chain->filter_chain) + tcf_chain_head_change(chain, next); RCU_INIT_POINTER(*chain_info->pprev, next); tcf_chain_put(chain); } diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index b599db26d34b..811845815b8c 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -54,6 +54,13 @@ static struct tcf_block *ingress_tcf_block(struct Qdisc *sch, unsigned long cl) return q->block; } +static void clsact_chain_head_change(struct tcf_proto *tp_head, void *priv) +{ + struct tcf_proto __rcu **p_filter_chain = priv; + + rcu_assign_pointer(*p_filter_chain, tp_head); +} + static int ingress_init(struct Qdisc *sch, struct nlattr *opt) { struct ingress_sched_data *q = qdisc_priv(sch); @@ -61,9 +68,10 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt) int err; q->block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; + q->block_info.chain_head_change = clsact_chain_head_change; + q->block_info.chain_head_change_priv = &dev->ingress_cl_list; - err = tcf_block_get_ext(&q->block, &dev->ingress_cl_list, - sch, &q->block_info); + err = tcf_block_get_ext(&q->block, sch, &q->block_info); if (err) return err; @@ -76,10 +84,8 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt) static void ingress_destroy(struct Qdisc *sch) { struct ingress_sched_data *q = qdisc_priv(sch); - struct net_device *dev = qdisc_dev(sch); - tcf_block_put_ext(q->block, &dev->ingress_cl_list, - sch, &q->block_info); + tcf_block_put_ext(q->block, sch, &q->block_info); net_dec_ingress_queue(); } @@ -162,16 +168,18 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt) int err; q->ingress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; + q->ingress_block_info.chain_head_change = clsact_chain_head_change; + q->ingress_block_info.chain_head_change_priv = &dev->ingress_cl_list; - err = tcf_block_get_ext(&q->ingress_block, &dev->ingress_cl_list, - sch, &q->ingress_block_info); + err = tcf_block_get_ext(&q->ingress_block, sch, &q->ingress_block_info); if (err) return err; q->egress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS; + q->egress_block_info.chain_head_change = clsact_chain_head_change; + q->egress_block_info.chain_head_change_priv = &dev->egress_cl_list; - err = tcf_block_get_ext(&q->egress_block, &dev->egress_cl_list, - sch, &q->egress_block_info); + err = tcf_block_get_ext(&q->egress_block, sch, &q->egress_block_info); if (err) goto err_egress_block_get; @@ -183,20 +191,16 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt) return 0; err_egress_block_get: - tcf_block_put_ext(q->ingress_block, &dev->ingress_cl_list, - sch, &q->ingress_block_info); + tcf_block_put_ext(q->ingress_block, sch, &q->ingress_block_info); return err; } static void clsact_destroy(struct Qdisc *sch) { struct clsact_sched_data *q = qdisc_priv(sch); - struct net_device *dev = qdisc_dev(sch); - tcf_block_put_ext(q->egress_block, &dev->egress_cl_list, - sch, &q->egress_block_info); - tcf_block_put_ext(q->ingress_block, &dev->ingress_cl_list, - sch, &q->ingress_block_info); + tcf_block_put_ext(q->egress_block, sch, &q->egress_block_info); + tcf_block_put_ext(q->ingress_block, sch, &q->ingress_block_info); net_dec_ingress_queue(); net_dec_egress_queue(); -- cgit v1.2.3-59-g8ed1b From 46209401f8f6116bd0b2c2d14a63958e83ffca0b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 3 Nov 2017 11:46:25 +0100 Subject: net: core: introduce mini_Qdisc and eliminate usage of tp->q for clsact fastpath In sch_handle_egress and sch_handle_ingress tp->q is used only in order to update stats. So stats and filter list are the only things that are needed in clsact qdisc fastpath processing. Introduce new mini_Qdisc struct to hold those items. Also, introduce a helper to swap the mini_Qdisc structures in case filter list head changes. This removes need for tp->q usage without added overhead. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/linux/netdevice.h | 9 ++++++--- include/net/sch_generic.h | 32 ++++++++++++++++++++++++++++++++ net/core/dev.c | 21 +++++++++++---------- net/sched/sch_generic.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ net/sched/sch_ingress.c | 19 ++++++++++++++----- 5 files changed, 109 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 5e02f79b2110..7de7656550c2 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1559,6 +1559,8 @@ enum netdev_priv_flags { * * @rx_handler: handler for received packets * @rx_handler_data: XXX: need comments on this one + * @miniq_ingress: ingress/clsact qdisc specific data for + * ingress processing * @ingress_queue: XXX: need comments on this one * @broadcast: hw bcast address * @@ -1576,7 +1578,8 @@ enum netdev_priv_flags { * @tx_global_lock: XXX: need comments on this one * * @xps_maps: XXX: need comments on this one - * + * @miniq_egress: clsact qdisc specific data for + * egress processing * @watchdog_timeo: Represents the timeout that is used by * the watchdog (see dev_watchdog()) * @watchdog_timer: List of timers @@ -1795,7 +1798,7 @@ struct net_device { void __rcu *rx_handler_data; #ifdef CONFIG_NET_CLS_ACT - struct tcf_proto __rcu *ingress_cl_list; + struct mini_Qdisc __rcu *miniq_ingress; #endif struct netdev_queue __rcu *ingress_queue; #ifdef CONFIG_NETFILTER_INGRESS @@ -1826,7 +1829,7 @@ struct net_device { struct xps_dev_maps __rcu *xps_maps; #endif #ifdef CONFIG_NET_CLS_ACT - struct tcf_proto __rcu *egress_cl_list; + struct mini_Qdisc __rcu *miniq_egress; #endif /* These may be needed for future network-power-down code. */ diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index f230269e0bfb..c64e62c9450a 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -904,4 +904,36 @@ static inline void psched_ratecfg_getrate(struct tc_ratespec *res, res->linklayer = (r->linklayer & TC_LINKLAYER_MASK); } +/* Mini Qdisc serves for specific needs of ingress/clsact Qdisc. + * The fast path only needs to access filter list and to update stats + */ +struct mini_Qdisc { + struct tcf_proto *filter_list; + struct gnet_stats_basic_cpu __percpu *cpu_bstats; + struct gnet_stats_queue __percpu *cpu_qstats; + struct rcu_head rcu; +}; + +static inline void mini_qdisc_bstats_cpu_update(struct mini_Qdisc *miniq, + const struct sk_buff *skb) +{ + bstats_cpu_update(this_cpu_ptr(miniq->cpu_bstats), skb); +} + +static inline void mini_qdisc_qstats_cpu_drop(struct mini_Qdisc *miniq) +{ + this_cpu_inc(miniq->cpu_qstats->drops); +} + +struct mini_Qdisc_pair { + struct mini_Qdisc miniq1; + struct mini_Qdisc miniq2; + struct mini_Qdisc __rcu **p_miniq; +}; + +void mini_qdisc_pair_swap(struct mini_Qdisc_pair *miniqp, + struct tcf_proto *tp_head); +void mini_qdisc_pair_init(struct mini_Qdisc_pair *miniqp, struct Qdisc *qdisc, + struct mini_Qdisc __rcu **p_miniq); + #endif diff --git a/net/core/dev.c b/net/core/dev.c index 24ac9083bc13..1423cf4d695c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3274,22 +3274,22 @@ EXPORT_SYMBOL(dev_loopback_xmit); static struct sk_buff * sch_handle_egress(struct sk_buff *skb, int *ret, struct net_device *dev) { - struct tcf_proto *cl = rcu_dereference_bh(dev->egress_cl_list); + struct mini_Qdisc *miniq = rcu_dereference_bh(dev->miniq_egress); struct tcf_result cl_res; - if (!cl) + if (!miniq) return skb; /* qdisc_skb_cb(skb)->pkt_len was already set by the caller. */ - qdisc_bstats_cpu_update(cl->q, skb); + mini_qdisc_bstats_cpu_update(miniq, skb); - switch (tcf_classify(skb, cl, &cl_res, false)) { + switch (tcf_classify(skb, miniq->filter_list, &cl_res, false)) { case TC_ACT_OK: case TC_ACT_RECLASSIFY: skb->tc_index = TC_H_MIN(cl_res.classid); break; case TC_ACT_SHOT: - qdisc_qstats_cpu_drop(cl->q); + mini_qdisc_qstats_cpu_drop(miniq); *ret = NET_XMIT_DROP; kfree_skb(skb); return NULL; @@ -4189,7 +4189,7 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, struct net_device *orig_dev) { #ifdef CONFIG_NET_CLS_ACT - struct tcf_proto *cl = rcu_dereference_bh(skb->dev->ingress_cl_list); + struct mini_Qdisc *miniq = rcu_dereference_bh(skb->dev->miniq_ingress); struct tcf_result cl_res; /* If there's at least one ingress present somewhere (so @@ -4197,8 +4197,9 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, * that are not configured with an ingress qdisc will bail * out here. */ - if (!cl) + if (!miniq) return skb; + if (*pt_prev) { *ret = deliver_skb(skb, *pt_prev, orig_dev); *pt_prev = NULL; @@ -4206,15 +4207,15 @@ sch_handle_ingress(struct sk_buff *skb, struct packet_type **pt_prev, int *ret, qdisc_skb_cb(skb)->pkt_len = skb->len; skb->tc_at_ingress = 1; - qdisc_bstats_cpu_update(cl->q, skb); + mini_qdisc_bstats_cpu_update(miniq, skb); - switch (tcf_classify(skb, cl, &cl_res, false)) { + switch (tcf_classify(skb, miniq->filter_list, &cl_res, false)) { case TC_ACT_OK: case TC_ACT_RECLASSIFY: skb->tc_index = TC_H_MIN(cl_res.classid); break; case TC_ACT_SHOT: - qdisc_qstats_cpu_drop(cl->q); + mini_qdisc_qstats_cpu_drop(miniq); kfree_skb(skb); return NULL; case TC_ACT_STOLEN: diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index aa74aa42b5d7..3839cbbdc32b 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -1024,3 +1024,49 @@ void psched_ratecfg_precompute(struct psched_ratecfg *r, } } EXPORT_SYMBOL(psched_ratecfg_precompute); + +static void mini_qdisc_rcu_func(struct rcu_head *head) +{ +} + +void mini_qdisc_pair_swap(struct mini_Qdisc_pair *miniqp, + struct tcf_proto *tp_head) +{ + struct mini_Qdisc *miniq_old = rtnl_dereference(*miniqp->p_miniq); + struct mini_Qdisc *miniq; + + if (!tp_head) { + RCU_INIT_POINTER(*miniqp->p_miniq, NULL); + return; + } + + miniq = !miniq_old || miniq_old == &miniqp->miniq2 ? + &miniqp->miniq1 : &miniqp->miniq2; + + /* We need to make sure that readers won't see the miniq + * we are about to modify. So wait until previous call_rcu_bh callback + * is done. + */ + rcu_barrier_bh(); + miniq->filter_list = tp_head; + rcu_assign_pointer(*miniqp->p_miniq, miniq); + + if (miniq_old) + /* This is counterpart of the rcu barrier above. We need to + * block potential new user of miniq_old until all readers + * are not seeing it. + */ + call_rcu_bh(&miniq_old->rcu, mini_qdisc_rcu_func); +} +EXPORT_SYMBOL(mini_qdisc_pair_swap); + +void mini_qdisc_pair_init(struct mini_Qdisc_pair *miniqp, struct Qdisc *qdisc, + struct mini_Qdisc __rcu **p_miniq) +{ + miniqp->miniq1.cpu_bstats = qdisc->cpu_bstats; + miniqp->miniq1.cpu_qstats = qdisc->cpu_qstats; + miniqp->miniq2.cpu_bstats = qdisc->cpu_bstats; + miniqp->miniq2.cpu_qstats = qdisc->cpu_qstats; + miniqp->p_miniq = p_miniq; +} +EXPORT_SYMBOL(mini_qdisc_pair_init); diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index 811845815b8c..5ecc38f35d47 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -21,6 +21,7 @@ struct ingress_sched_data { struct tcf_block *block; struct tcf_block_ext_info block_info; + struct mini_Qdisc_pair miniqp; }; static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg) @@ -56,9 +57,9 @@ static struct tcf_block *ingress_tcf_block(struct Qdisc *sch, unsigned long cl) static void clsact_chain_head_change(struct tcf_proto *tp_head, void *priv) { - struct tcf_proto __rcu **p_filter_chain = priv; + struct mini_Qdisc_pair *miniqp = priv; - rcu_assign_pointer(*p_filter_chain, tp_head); + mini_qdisc_pair_swap(miniqp, tp_head); } static int ingress_init(struct Qdisc *sch, struct nlattr *opt) @@ -67,9 +68,11 @@ static int ingress_init(struct Qdisc *sch, struct nlattr *opt) struct net_device *dev = qdisc_dev(sch); int err; + mini_qdisc_pair_init(&q->miniqp, sch, &dev->miniq_ingress); + q->block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; q->block_info.chain_head_change = clsact_chain_head_change; - q->block_info.chain_head_change_priv = &dev->ingress_cl_list; + q->block_info.chain_head_change_priv = &q->miniqp; err = tcf_block_get_ext(&q->block, sch, &q->block_info); if (err) @@ -128,6 +131,8 @@ struct clsact_sched_data { struct tcf_block *egress_block; struct tcf_block_ext_info ingress_block_info; struct tcf_block_ext_info egress_block_info; + struct mini_Qdisc_pair miniqp_ingress; + struct mini_Qdisc_pair miniqp_egress; }; static unsigned long clsact_find(struct Qdisc *sch, u32 classid) @@ -167,17 +172,21 @@ static int clsact_init(struct Qdisc *sch, struct nlattr *opt) struct net_device *dev = qdisc_dev(sch); int err; + mini_qdisc_pair_init(&q->miniqp_ingress, sch, &dev->miniq_ingress); + q->ingress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS; q->ingress_block_info.chain_head_change = clsact_chain_head_change; - q->ingress_block_info.chain_head_change_priv = &dev->ingress_cl_list; + q->ingress_block_info.chain_head_change_priv = &q->miniqp_ingress; err = tcf_block_get_ext(&q->ingress_block, sch, &q->ingress_block_info); if (err) return err; + mini_qdisc_pair_init(&q->miniqp_egress, sch, &dev->miniq_egress); + q->egress_block_info.binder_type = TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS; q->egress_block_info.chain_head_change = clsact_chain_head_change; - q->egress_block_info.chain_head_change_priv = &dev->egress_cl_list; + q->egress_block_info.chain_head_change_priv = &q->miniqp_egress; err = tcf_block_get_ext(&q->egress_block, sch, &q->egress_block_info); if (err) -- cgit v1.2.3-59-g8ed1b From a622c641665c32a879348efb2abc389527479ee4 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Wed, 25 Oct 2017 20:42:57 +0200 Subject: memory: omap-gpmc: Remove deprecated gpmc_update_nand_reg() Deprecated gpmc_update_nand_reg() is no longer used, remove. Signed-off-by: Ladislav Michl Signed-off-by: Roger Quadros --- drivers/memory/omap-gpmc.c | 53 +++++++++++++++++++++------------------------- include/linux/omap-gpmc.h | 12 ----------- 2 files changed, 24 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index f2aef0b87bc6..a385a35c7de9 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -1075,10 +1075,33 @@ int gpmc_configure(int cmd, int wval) } EXPORT_SYMBOL(gpmc_configure); -void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) +static bool gpmc_nand_writebuffer_empty(void) +{ + if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS) + return true; + + return false; +} + +static struct gpmc_nand_ops nand_ops = { + .nand_writebuffer_empty = gpmc_nand_writebuffer_empty, +}; + +/** + * gpmc_omap_get_nand_ops - Get the GPMC NAND interface + * @regs: the GPMC NAND register map exclusive for NAND use. + * @cs: GPMC chip select number on which the NAND sits. The + * register map returned will be specific to this chip select. + * + * Returns NULL on error e.g. invalid cs. + */ +struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs) { int i; + if (cs >= gpmc_cs_num) + return NULL; + reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET + GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs; reg->gpmc_nand_address = gpmc_base + GPMC_CS0_OFFSET + @@ -1110,34 +1133,6 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) reg->gpmc_bch_result6[i] = gpmc_base + GPMC_ECC_BCH_RESULT_6 + i * GPMC_BCH_SIZE; } -} - -static bool gpmc_nand_writebuffer_empty(void) -{ - if (gpmc_read_reg(GPMC_STATUS) & GPMC_STATUS_EMPTYWRITEBUFFERSTATUS) - return true; - - return false; -} - -static struct gpmc_nand_ops nand_ops = { - .nand_writebuffer_empty = gpmc_nand_writebuffer_empty, -}; - -/** - * gpmc_omap_get_nand_ops - Get the GPMC NAND interface - * @regs: the GPMC NAND register map exclusive for NAND use. - * @cs: GPMC chip select number on which the NAND sits. The - * register map returned will be specific to this chip select. - * - * Returns NULL on error e.g. invalid cs. - */ -struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs *reg, int cs) -{ - if (cs >= gpmc_cs_num) - return NULL; - - gpmc_update_nand_reg(reg, cs); return &nand_ops; } diff --git a/include/linux/omap-gpmc.h b/include/linux/omap-gpmc.h index fd0de00c0d77..edfa280c3d56 100644 --- a/include/linux/omap-gpmc.h +++ b/include/linux/omap-gpmc.h @@ -36,18 +36,6 @@ static inline struct gpmc_nand_ops *gpmc_omap_get_nand_ops(struct gpmc_nand_regs } #endif /* CONFIG_OMAP_GPMC */ -/*--------------------------------*/ - -/* deprecated APIs */ -#if IS_ENABLED(CONFIG_OMAP_GPMC) -void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs); -#else -static inline void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) -{ -} -#endif /* CONFIG_OMAP_GPMC */ -/*--------------------------------*/ - extern int gpmc_calc_timings(struct gpmc_timings *gpmc_t, struct gpmc_settings *gpmc_s, struct gpmc_device_timings *dev_t); -- cgit v1.2.3-59-g8ed1b From 592e254502041f953e84d091eae2c68cba04c10b Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Fri, 3 Nov 2017 12:21:21 +0100 Subject: mm: Handle 0 flags in _calc_vm_trans() macro _calc_vm_trans() does not handle the situation when some of the passed flags are 0 (which can happen if these VM flags do not make sense for the architecture). Improve the _calc_vm_trans() macro to return 0 in such situation. Since all passed flags are constant, this does not add any runtime overhead. Signed-off-by: Jan Kara Signed-off-by: Dan Williams --- include/linux/mman.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mman.h b/include/linux/mman.h index c8367041fafd..edb6cf6a81ed 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -63,8 +63,9 @@ static inline bool arch_validate_prot(unsigned long prot) * ("bit1" and "bit2" must be single bits) */ #define _calc_vm_trans(x, bit1, bit2) \ + ((!(bit1) || !(bit2)) ? 0 : \ ((bit1) <= (bit2) ? ((x) & (bit1)) * ((bit2) / (bit1)) \ - : ((x) & (bit1)) / ((bit1) / (bit2))) + : ((x) & (bit1)) / ((bit1) / (bit2)))) /* * Combine the mmap "prot" argument into "vm_flags" used internally. -- cgit v1.2.3-59-g8ed1b From 1c9725974074a047f6080eecc62c50a8e840d050 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 1 Nov 2017 16:36:30 +0100 Subject: mm: introduce MAP_SHARED_VALIDATE, a mechanism to safely define new mmap flags The mmap(2) syscall suffers from the ABI anti-pattern of not validating unknown flags. However, proposals like MAP_SYNC need a mechanism to define new behavior that is known to fail on older kernels without the support. Define a new MAP_SHARED_VALIDATE flag pattern that is guaranteed to fail on all legacy mmap implementations. It is worth noting that the original proposal was for a standalone MAP_VALIDATE flag. However, when that could not be supported by all archs Linus observed: I see why you *think* you want a bitmap. You think you want a bitmap because you want to make MAP_VALIDATE be part of MAP_SYNC etc, so that people can do ret = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_SYNC, fd, 0); and "know" that MAP_SYNC actually takes. And I'm saying that whole wish is bogus. You're fundamentally depending on special semantics, just make it explicit. It's already not portable, so don't try to make it so. Rename that MAP_VALIDATE as MAP_SHARED_VALIDATE, make it have a value of 0x3, and make people do ret = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED_VALIDATE | MAP_SYNC, fd, 0); and then the kernel side is easier too (none of that random garbage playing games with looking at the "MAP_VALIDATE bit", but just another case statement in that map type thing. Boom. Done. Similar to ->fallocate() we also want the ability to validate the support for new flags on a per ->mmap() 'struct file_operations' instance basis. Towards that end arrange for flags to be generically validated against a mmap_supported_flags exported by 'struct file_operations'. By default all existing flags are implicitly supported, but new flags require MAP_SHARED_VALIDATE and per-instance-opt-in. Cc: Jan Kara Cc: Arnd Bergmann Cc: Andy Lutomirski Cc: Andrew Morton Suggested-by: Christoph Hellwig Suggested-by: Linus Torvalds Reviewed-by: Ross Zwisler Signed-off-by: Dan Williams Signed-off-by: Jan Kara Signed-off-by: Dan Williams --- arch/alpha/include/uapi/asm/mman.h | 1 + arch/mips/include/uapi/asm/mman.h | 1 + arch/parisc/include/uapi/asm/mman.h | 1 + arch/xtensa/include/uapi/asm/mman.h | 1 + include/linux/fs.h | 1 + include/linux/mman.h | 39 ++++++++++++++++++++++++++++ include/uapi/asm-generic/mman-common.h | 1 + mm/mmap.c | 15 +++++++++++ tools/include/uapi/asm-generic/mman-common.h | 1 + 9 files changed, 61 insertions(+) (limited to 'include') diff --git a/arch/alpha/include/uapi/asm/mman.h b/arch/alpha/include/uapi/asm/mman.h index 3b26cc62dadb..f6d118aaedb9 100644 --- a/arch/alpha/include/uapi/asm/mman.h +++ b/arch/alpha/include/uapi/asm/mman.h @@ -11,6 +11,7 @@ #define MAP_SHARED 0x01 /* Share changes */ #define MAP_PRIVATE 0x02 /* Changes are private */ +#define MAP_SHARED_VALIDATE 0x03 /* share + validate extension flags */ #define MAP_TYPE 0x0f /* Mask for type of mapping (OSF/1 is _wrong_) */ #define MAP_FIXED 0x100 /* Interpret addr exactly */ #define MAP_ANONYMOUS 0x10 /* don't use a file */ diff --git a/arch/mips/include/uapi/asm/mman.h b/arch/mips/include/uapi/asm/mman.h index da3216007fe0..93268e4cd3c7 100644 --- a/arch/mips/include/uapi/asm/mman.h +++ b/arch/mips/include/uapi/asm/mman.h @@ -28,6 +28,7 @@ */ #define MAP_SHARED 0x001 /* Share changes */ #define MAP_PRIVATE 0x002 /* Changes are private */ +#define MAP_SHARED_VALIDATE 0x003 /* share + validate extension flags */ #define MAP_TYPE 0x00f /* Mask for type of mapping */ #define MAP_FIXED 0x010 /* Interpret addr exactly */ diff --git a/arch/parisc/include/uapi/asm/mman.h b/arch/parisc/include/uapi/asm/mman.h index 775b5d5e41a1..bca652aa1677 100644 --- a/arch/parisc/include/uapi/asm/mman.h +++ b/arch/parisc/include/uapi/asm/mman.h @@ -11,6 +11,7 @@ #define MAP_SHARED 0x01 /* Share changes */ #define MAP_PRIVATE 0x02 /* Changes are private */ +#define MAP_SHARED_VALIDATE 0x03 /* share + validate extension flags */ #define MAP_TYPE 0x03 /* Mask for type of mapping */ #define MAP_FIXED 0x04 /* Interpret addr exactly */ #define MAP_ANONYMOUS 0x10 /* don't use a file */ diff --git a/arch/xtensa/include/uapi/asm/mman.h b/arch/xtensa/include/uapi/asm/mman.h index b15b278aa314..9ab426374714 100644 --- a/arch/xtensa/include/uapi/asm/mman.h +++ b/arch/xtensa/include/uapi/asm/mman.h @@ -35,6 +35,7 @@ */ #define MAP_SHARED 0x001 /* Share changes */ #define MAP_PRIVATE 0x002 /* Changes are private */ +#define MAP_SHARED_VALIDATE 0x003 /* share + validate extension flags */ #define MAP_TYPE 0x00f /* Mask for type of mapping */ #define MAP_FIXED 0x010 /* Interpret addr exactly */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 13dab191a23e..57added3201d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1701,6 +1701,7 @@ struct file_operations { long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (struct file *, struct vm_area_struct *); + unsigned long mmap_supported_flags; int (*open) (struct inode *, struct file *); int (*flush) (struct file *, fl_owner_t id); int (*release) (struct inode *, struct file *); diff --git a/include/linux/mman.h b/include/linux/mman.h index edb6cf6a81ed..74452e3f2536 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -7,6 +7,45 @@ #include #include +/* + * Arrange for legacy / undefined architecture specific flags to be + * ignored by default in LEGACY_MAP_MASK. + */ +#ifndef MAP_32BIT +#define MAP_32BIT 0 +#endif +#ifndef MAP_HUGE_2MB +#define MAP_HUGE_2MB 0 +#endif +#ifndef MAP_HUGE_1GB +#define MAP_HUGE_1GB 0 +#endif +#ifndef MAP_UNINITIALIZED +#define MAP_UNINITIALIZED 0 +#endif + +/* + * The historical set of flags that all mmap implementations implicitly + * support when a ->mmap_validate() op is not provided in file_operations. + */ +#define LEGACY_MAP_MASK (MAP_SHARED \ + | MAP_PRIVATE \ + | MAP_FIXED \ + | MAP_ANONYMOUS \ + | MAP_DENYWRITE \ + | MAP_EXECUTABLE \ + | MAP_UNINITIALIZED \ + | MAP_GROWSDOWN \ + | MAP_LOCKED \ + | MAP_NORESERVE \ + | MAP_POPULATE \ + | MAP_NONBLOCK \ + | MAP_STACK \ + | MAP_HUGETLB \ + | MAP_32BIT \ + | MAP_HUGE_2MB \ + | MAP_HUGE_1GB) + extern int sysctl_overcommit_memory; extern int sysctl_overcommit_ratio; extern unsigned long sysctl_overcommit_kbytes; diff --git a/include/uapi/asm-generic/mman-common.h b/include/uapi/asm-generic/mman-common.h index 203268f9231e..8ce7f5a0800f 100644 --- a/include/uapi/asm-generic/mman-common.h +++ b/include/uapi/asm-generic/mman-common.h @@ -16,6 +16,7 @@ #define MAP_SHARED 0x01 /* Share changes */ #define MAP_PRIVATE 0x02 /* Changes are private */ +#define MAP_SHARED_VALIDATE 0x03 /* share + validate extension flags */ #define MAP_TYPE 0x0f /* Mask for type of mapping */ #define MAP_FIXED 0x10 /* Interpret addr exactly */ #define MAP_ANONYMOUS 0x20 /* don't use a file */ diff --git a/mm/mmap.c b/mm/mmap.c index 680506faceae..924839fac0e6 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1387,9 +1387,24 @@ unsigned long do_mmap(struct file *file, unsigned long addr, if (file) { struct inode *inode = file_inode(file); + unsigned long flags_mask; + + flags_mask = LEGACY_MAP_MASK | file->f_op->mmap_supported_flags; switch (flags & MAP_TYPE) { case MAP_SHARED: + /* + * Force use of MAP_SHARED_VALIDATE with non-legacy + * flags. E.g. MAP_SYNC is dangerous to use with + * MAP_SHARED as you don't know which consistency model + * you will get. We silently ignore unsupported flags + * with MAP_SHARED to preserve backward compatibility. + */ + flags &= LEGACY_MAP_MASK; + /* fall through */ + case MAP_SHARED_VALIDATE: + if (flags & ~flags_mask) + return -EOPNOTSUPP; if ((prot&PROT_WRITE) && !(file->f_mode&FMODE_WRITE)) return -EACCES; diff --git a/tools/include/uapi/asm-generic/mman-common.h b/tools/include/uapi/asm-generic/mman-common.h index 203268f9231e..8ce7f5a0800f 100644 --- a/tools/include/uapi/asm-generic/mman-common.h +++ b/tools/include/uapi/asm-generic/mman-common.h @@ -16,6 +16,7 @@ #define MAP_SHARED 0x01 /* Share changes */ #define MAP_PRIVATE 0x02 /* Changes are private */ +#define MAP_SHARED_VALIDATE 0x03 /* share + validate extension flags */ #define MAP_TYPE 0x0f /* Mask for type of mapping */ #define MAP_FIXED 0x10 /* Interpret addr exactly */ #define MAP_ANONYMOUS 0x20 /* don't use a file */ -- cgit v1.2.3-59-g8ed1b From d81b8a722f691a3907eb4a6df410ab517ba5bfcc Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 1 Nov 2017 16:36:31 +0100 Subject: mm: Remove VM_FAULT_HWPOISON_LARGE_MASK It is unused. Reviewed-by: Ross Zwisler Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara Signed-off-by: Dan Williams --- include/linux/mm.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include') diff --git a/include/linux/mm.h b/include/linux/mm.h index 065d99deb847..ca72b67153d5 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1182,8 +1182,6 @@ static inline void clear_page_pfmemalloc(struct page *page) #define VM_FAULT_FALLBACK 0x0800 /* huge page fault failed, fall back to small */ #define VM_FAULT_DONE_COW 0x1000 /* ->fault has fully handled COW */ -#define VM_FAULT_HWPOISON_LARGE_MASK 0xf000 /* encodes hpage index for large hwpoison */ - #define VM_FAULT_ERROR (VM_FAULT_OOM | VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV | \ VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE | \ VM_FAULT_FALLBACK) -- cgit v1.2.3-59-g8ed1b From 302a5e312b3a106fec49ba08da3f6545c9b7ee18 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 1 Nov 2017 16:36:37 +0100 Subject: dax: Inline dax_pmd_insert_mapping() into the callsite dax_pmd_insert_mapping() has only one callsite and we will need to further fine tune what it does for synchronous faults. Just inline it into the callsite so that we don't have to pass awkward bools around. Reviewed-by: Christoph Hellwig Reviewed-by: Ross Zwisler Signed-off-by: Jan Kara Signed-off-by: Dan Williams --- fs/dax.c | 47 +++++++++++++++++-------------------------- include/trace/events/fs_dax.h | 1 - 2 files changed, 19 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/fs/dax.c b/fs/dax.c index 5b20c6456926..675fab8ec41f 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -1235,33 +1235,11 @@ static int dax_iomap_pte_fault(struct vm_fault *vmf, } #ifdef CONFIG_FS_DAX_PMD -static int dax_pmd_insert_mapping(struct vm_fault *vmf, struct iomap *iomap, - loff_t pos, void *entry) -{ - struct address_space *mapping = vmf->vma->vm_file->f_mapping; - const sector_t sector = dax_iomap_sector(iomap, pos); - struct inode *inode = mapping->host; - void *ret = NULL; - pfn_t pfn = {}; - int rc; - - rc = dax_iomap_pfn(iomap, pos, PMD_SIZE, &pfn); - if (rc < 0) - goto fallback; - - ret = dax_insert_mapping_entry(mapping, vmf, entry, sector, - RADIX_DAX_PMD); - if (IS_ERR(ret)) - goto fallback; - - trace_dax_pmd_insert_mapping(inode, vmf, PMD_SIZE, pfn, ret); - return vmf_insert_pfn_pmd(vmf->vma, vmf->address, vmf->pmd, - pfn, vmf->flags & FAULT_FLAG_WRITE); - -fallback: - trace_dax_pmd_insert_mapping_fallback(inode, vmf, PMD_SIZE, pfn, ret); - return VM_FAULT_FALLBACK; -} +/* + * The 'colour' (ie low bits) within a PMD of a page offset. This comes up + * more often than one might expect in the below functions. + */ +#define PG_PMD_COLOUR ((PMD_SIZE >> PAGE_SHIFT) - 1) static int dax_pmd_load_hole(struct vm_fault *vmf, struct iomap *iomap, void *entry) @@ -1317,6 +1295,7 @@ static int dax_iomap_pmd_fault(struct vm_fault *vmf, void *entry; loff_t pos; int error; + pfn_t pfn; /* * Check whether offset isn't beyond end of file now. Caller is @@ -1394,7 +1373,19 @@ static int dax_iomap_pmd_fault(struct vm_fault *vmf, switch (iomap.type) { case IOMAP_MAPPED: - result = dax_pmd_insert_mapping(vmf, &iomap, pos, entry); + error = dax_iomap_pfn(&iomap, pos, PMD_SIZE, &pfn); + if (error < 0) + goto finish_iomap; + + entry = dax_insert_mapping_entry(mapping, vmf, entry, + dax_iomap_sector(&iomap, pos), + RADIX_DAX_PMD); + if (IS_ERR(entry)) + goto finish_iomap; + + trace_dax_pmd_insert_mapping(inode, vmf, PMD_SIZE, pfn, entry); + result = vmf_insert_pfn_pmd(vma, vmf->address, vmf->pmd, pfn, + write); break; case IOMAP_UNWRITTEN: case IOMAP_HOLE: diff --git a/include/trace/events/fs_dax.h b/include/trace/events/fs_dax.h index fbc4a06f7310..88a9d19b8ff8 100644 --- a/include/trace/events/fs_dax.h +++ b/include/trace/events/fs_dax.h @@ -148,7 +148,6 @@ DEFINE_EVENT(dax_pmd_insert_mapping_class, name, \ TP_ARGS(inode, vmf, length, pfn, radix_entry)) DEFINE_PMD_INSERT_MAPPING_EVENT(dax_pmd_insert_mapping); -DEFINE_PMD_INSERT_MAPPING_EVENT(dax_pmd_insert_mapping_fallback); DECLARE_EVENT_CLASS(dax_pte_fault_class, TP_PROTO(struct inode *inode, struct vm_fault *vmf, int result), -- cgit v1.2.3-59-g8ed1b From 9a0dd42251439de635088b97533109a935864a84 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 1 Nov 2017 16:36:39 +0100 Subject: dax: Allow dax_iomap_fault() to return pfn For synchronous page fault dax_iomap_fault() will need to return PFN which will then need to be inserted into page tables after fsync() completes. Add necessary parameter to dax_iomap_fault(). Reviewed-by: Christoph Hellwig Reviewed-by: Ross Zwisler Signed-off-by: Jan Kara Signed-off-by: Dan Williams --- fs/dax.c | 13 +++++++------ fs/ext2/file.c | 2 +- fs/ext4/file.c | 2 +- fs/xfs/xfs_file.c | 4 ++-- include/linux/dax.h | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/fs/dax.c b/fs/dax.c index 5214ed9ba508..5ddf15161390 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -1079,7 +1079,7 @@ static int dax_fault_return(int error) return VM_FAULT_SIGBUS; } -static int dax_iomap_pte_fault(struct vm_fault *vmf, +static int dax_iomap_pte_fault(struct vm_fault *vmf, pfn_t *pfnp, const struct iomap_ops *ops) { struct vm_area_struct *vma = vmf->vma; @@ -1280,7 +1280,7 @@ fallback: return VM_FAULT_FALLBACK; } -static int dax_iomap_pmd_fault(struct vm_fault *vmf, +static int dax_iomap_pmd_fault(struct vm_fault *vmf, pfn_t *pfnp, const struct iomap_ops *ops) { struct vm_area_struct *vma = vmf->vma; @@ -1425,7 +1425,7 @@ out: return result; } #else -static int dax_iomap_pmd_fault(struct vm_fault *vmf, +static int dax_iomap_pmd_fault(struct vm_fault *vmf, pfn_t *pfnp, const struct iomap_ops *ops) { return VM_FAULT_FALLBACK; @@ -1436,6 +1436,7 @@ static int dax_iomap_pmd_fault(struct vm_fault *vmf, * dax_iomap_fault - handle a page fault on a DAX file * @vmf: The description of the fault * @pe_size: Size of the page to fault in + * @pfnp: PFN to insert for synchronous faults if fsync is required * @ops: Iomap ops passed from the file system * * When a page fault occurs, filesystems may call this helper in @@ -1444,13 +1445,13 @@ static int dax_iomap_pmd_fault(struct vm_fault *vmf, * successfully. */ int dax_iomap_fault(struct vm_fault *vmf, enum page_entry_size pe_size, - const struct iomap_ops *ops) + pfn_t *pfnp, const struct iomap_ops *ops) { switch (pe_size) { case PE_SIZE_PTE: - return dax_iomap_pte_fault(vmf, ops); + return dax_iomap_pte_fault(vmf, pfnp, ops); case PE_SIZE_PMD: - return dax_iomap_pmd_fault(vmf, ops); + return dax_iomap_pmd_fault(vmf, pfnp, ops); default: return VM_FAULT_FALLBACK; } diff --git a/fs/ext2/file.c b/fs/ext2/file.c index ff3a3636a5ca..d2bb7c96307d 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -99,7 +99,7 @@ static int ext2_dax_fault(struct vm_fault *vmf) } down_read(&ei->dax_sem); - ret = dax_iomap_fault(vmf, PE_SIZE_PTE, &ext2_iomap_ops); + ret = dax_iomap_fault(vmf, PE_SIZE_PTE, NULL, &ext2_iomap_ops); up_read(&ei->dax_sem); if (vmf->flags & FAULT_FLAG_WRITE) diff --git a/fs/ext4/file.c b/fs/ext4/file.c index b1da660ac3bc..3cec0b95672f 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -306,7 +306,7 @@ static int ext4_dax_huge_fault(struct vm_fault *vmf, down_read(&EXT4_I(inode)->i_mmap_sem); } if (!IS_ERR(handle)) - result = dax_iomap_fault(vmf, pe_size, &ext4_iomap_ops); + result = dax_iomap_fault(vmf, pe_size, NULL, &ext4_iomap_ops); else result = VM_FAULT_SIGBUS; if (write) { diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 309e26c9dddb..7c6b8def6eed 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -1040,7 +1040,7 @@ __xfs_filemap_fault( xfs_ilock(XFS_I(inode), XFS_MMAPLOCK_SHARED); if (IS_DAX(inode)) { - ret = dax_iomap_fault(vmf, pe_size, &xfs_iomap_ops); + ret = dax_iomap_fault(vmf, pe_size, NULL, &xfs_iomap_ops); } else { if (write_fault) ret = iomap_page_mkwrite(vmf, &xfs_iomap_ops); @@ -1111,7 +1111,7 @@ xfs_filemap_pfn_mkwrite( if (vmf->pgoff >= size) ret = VM_FAULT_SIGBUS; else if (IS_DAX(inode)) - ret = dax_iomap_fault(vmf, PE_SIZE_PTE, &xfs_iomap_ops); + ret = dax_iomap_fault(vmf, PE_SIZE_PTE, NULL, &xfs_iomap_ops); xfs_iunlock(ip, XFS_MMAPLOCK_SHARED); sb_end_pagefault(inode->i_sb); return ret; diff --git a/include/linux/dax.h b/include/linux/dax.h index 122197124b9d..e7fa4b8f45bc 100644 --- a/include/linux/dax.h +++ b/include/linux/dax.h @@ -95,7 +95,7 @@ bool dax_write_cache_enabled(struct dax_device *dax_dev); ssize_t dax_iomap_rw(struct kiocb *iocb, struct iov_iter *iter, const struct iomap_ops *ops); int dax_iomap_fault(struct vm_fault *vmf, enum page_entry_size pe_size, - const struct iomap_ops *ops); + pfn_t *pfnp, const struct iomap_ops *ops); int dax_delete_mapping_entry(struct address_space *mapping, pgoff_t index); int dax_invalidate_mapping_entry_sync(struct address_space *mapping, pgoff_t index); -- cgit v1.2.3-59-g8ed1b From b6fb293f2497a9841d94f6b57bd2bb2cd222da43 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 1 Nov 2017 16:36:41 +0100 Subject: mm: Define MAP_SYNC and VM_SYNC flags Define new MAP_SYNC flag and corresponding VMA VM_SYNC flag. As the MAP_SYNC flag is not part of LEGACY_MAP_MASK, currently it will be refused by all MAP_SHARED_VALIDATE map attempts and silently ignored for everything else. Reviewed-by: Ross Zwisler Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara Signed-off-by: Dan Williams --- fs/proc/task_mmu.c | 1 + include/linux/mm.h | 1 + include/linux/mman.h | 8 ++++++-- include/uapi/asm-generic/mman.h | 1 + 4 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 5589b4bd4b85..ea78b37deeaa 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -664,6 +664,7 @@ static void show_smap_vma_flags(struct seq_file *m, struct vm_area_struct *vma) [ilog2(VM_ACCOUNT)] = "ac", [ilog2(VM_NORESERVE)] = "nr", [ilog2(VM_HUGETLB)] = "ht", + [ilog2(VM_SYNC)] = "sf", [ilog2(VM_ARCH_1)] = "ar", [ilog2(VM_WIPEONFORK)] = "wf", [ilog2(VM_DONTDUMP)] = "dd", diff --git a/include/linux/mm.h b/include/linux/mm.h index ca72b67153d5..5411cb7442de 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -189,6 +189,7 @@ extern unsigned int kobjsize(const void *objp); #define VM_ACCOUNT 0x00100000 /* Is a VM accounted object */ #define VM_NORESERVE 0x00200000 /* should the VM suppress accounting */ #define VM_HUGETLB 0x00400000 /* Huge TLB Page VM */ +#define VM_SYNC 0x00800000 /* Synchronous page faults */ #define VM_ARCH_1 0x01000000 /* Architecture-specific flag */ #define VM_WIPEONFORK 0x02000000 /* Wipe VMA contents in child. */ #define VM_DONTDUMP 0x04000000 /* Do not include in the core dump */ diff --git a/include/linux/mman.h b/include/linux/mman.h index 74452e3f2536..3427bf3daef5 100644 --- a/include/linux/mman.h +++ b/include/linux/mman.h @@ -9,7 +9,7 @@ /* * Arrange for legacy / undefined architecture specific flags to be - * ignored by default in LEGACY_MAP_MASK. + * ignored by mmap handling code. */ #ifndef MAP_32BIT #define MAP_32BIT 0 @@ -23,6 +23,9 @@ #ifndef MAP_UNINITIALIZED #define MAP_UNINITIALIZED 0 #endif +#ifndef MAP_SYNC +#define MAP_SYNC 0 +#endif /* * The historical set of flags that all mmap implementations implicitly @@ -126,7 +129,8 @@ calc_vm_flag_bits(unsigned long flags) { return _calc_vm_trans(flags, MAP_GROWSDOWN, VM_GROWSDOWN ) | _calc_vm_trans(flags, MAP_DENYWRITE, VM_DENYWRITE ) | - _calc_vm_trans(flags, MAP_LOCKED, VM_LOCKED ); + _calc_vm_trans(flags, MAP_LOCKED, VM_LOCKED ) | + _calc_vm_trans(flags, MAP_SYNC, VM_SYNC ); } unsigned long vm_commit_limit(void); diff --git a/include/uapi/asm-generic/mman.h b/include/uapi/asm-generic/mman.h index 7162cd4cca73..00e55627d2df 100644 --- a/include/uapi/asm-generic/mman.h +++ b/include/uapi/asm-generic/mman.h @@ -12,6 +12,7 @@ #define MAP_NONBLOCK 0x10000 /* do not block on IO */ #define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */ #define MAP_HUGETLB 0x40000 /* create a huge page mapping */ +#define MAP_SYNC 0x80000 /* perform synchronous page faults for the mapping */ /* Bits [26:31] are reserved, see mman-common.h for MAP_HUGETLB usage */ -- cgit v1.2.3-59-g8ed1b From caa51d26f85c248f1c4f43a870ad3ef84bf9eb8f Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 1 Nov 2017 16:36:42 +0100 Subject: dax, iomap: Add support for synchronous faults Add a flag to iomap interface informing the caller that inode needs fdstasync(2) for returned extent to become persistent and use it in DAX fault code so that we don't map such extents into page tables immediately. Instead we propagate the information that fdatasync(2) is necessary from dax_iomap_fault() with a new VM_FAULT_NEEDDSYNC flag. Filesystem fault handler is then responsible for calling fdatasync(2) and inserting pfn into page tables. Reviewed-by: Ross Zwisler Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara Signed-off-by: Dan Williams --- fs/dax.c | 39 +++++++++++++++++++++++++++++++++++++-- include/linux/iomap.h | 5 +++++ include/linux/mm.h | 6 +++++- 3 files changed, 47 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/fs/dax.c b/fs/dax.c index efc210ff6665..bb9ff907738c 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -1091,6 +1091,7 @@ static int dax_iomap_pte_fault(struct vm_fault *vmf, pfn_t *pfnp, unsigned flags = IOMAP_FAULT; int error, major = 0; bool write = vmf->flags & FAULT_FLAG_WRITE; + bool sync; int vmf_ret = 0; void *entry; pfn_t pfn; @@ -1169,6 +1170,8 @@ static int dax_iomap_pte_fault(struct vm_fault *vmf, pfn_t *pfnp, goto finish_iomap; } + sync = (vma->vm_flags & VM_SYNC) && (iomap.flags & IOMAP_F_DIRTY); + switch (iomap.type) { case IOMAP_MAPPED: if (iomap.flags & IOMAP_F_NEW) { @@ -1182,12 +1185,27 @@ static int dax_iomap_pte_fault(struct vm_fault *vmf, pfn_t *pfnp, entry = dax_insert_mapping_entry(mapping, vmf, entry, dax_iomap_sector(&iomap, pos), - 0, write); + 0, write && !sync); if (IS_ERR(entry)) { error = PTR_ERR(entry); goto error_finish_iomap; } + /* + * If we are doing synchronous page fault and inode needs fsync, + * we can insert PTE into page tables only after that happens. + * Skip insertion for now and return the pfn so that caller can + * insert it after fsync is done. + */ + if (sync) { + if (WARN_ON_ONCE(!pfnp)) { + error = -EIO; + goto error_finish_iomap; + } + *pfnp = pfn; + vmf_ret = VM_FAULT_NEEDDSYNC | major; + goto finish_iomap; + } trace_dax_insert_mapping(inode, vmf, entry); if (write) error = vm_insert_mixed_mkwrite(vma, vaddr, pfn); @@ -1287,6 +1305,7 @@ static int dax_iomap_pmd_fault(struct vm_fault *vmf, pfn_t *pfnp, struct address_space *mapping = vma->vm_file->f_mapping; unsigned long pmd_addr = vmf->address & PMD_MASK; bool write = vmf->flags & FAULT_FLAG_WRITE; + bool sync; unsigned int iomap_flags = (write ? IOMAP_WRITE : 0) | IOMAP_FAULT; struct inode *inode = mapping->host; int result = VM_FAULT_FALLBACK; @@ -1371,6 +1390,8 @@ static int dax_iomap_pmd_fault(struct vm_fault *vmf, pfn_t *pfnp, if (iomap.offset + iomap.length < pos + PMD_SIZE) goto finish_iomap; + sync = (vma->vm_flags & VM_SYNC) && (iomap.flags & IOMAP_F_DIRTY); + switch (iomap.type) { case IOMAP_MAPPED: error = dax_iomap_pfn(&iomap, pos, PMD_SIZE, &pfn); @@ -1379,10 +1400,24 @@ static int dax_iomap_pmd_fault(struct vm_fault *vmf, pfn_t *pfnp, entry = dax_insert_mapping_entry(mapping, vmf, entry, dax_iomap_sector(&iomap, pos), - RADIX_DAX_PMD, write); + RADIX_DAX_PMD, write && !sync); if (IS_ERR(entry)) goto finish_iomap; + /* + * If we are doing synchronous page fault and inode needs fsync, + * we can insert PMD into page tables only after that happens. + * Skip insertion for now and return the pfn so that caller can + * insert it after fsync is done. + */ + if (sync) { + if (WARN_ON_ONCE(!pfnp)) + goto finish_iomap; + *pfnp = pfn; + result = VM_FAULT_NEEDDSYNC; + goto finish_iomap; + } + trace_dax_pmd_insert_mapping(inode, vmf, PMD_SIZE, pfn, entry); result = vmf_insert_pfn_pmd(vma, vmf->address, vmf->pmd, pfn, write); diff --git a/include/linux/iomap.h b/include/linux/iomap.h index f64dc6ce5161..73e3b7085dbe 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h @@ -22,6 +22,11 @@ struct vm_fault; * Flags for all iomap mappings: */ #define IOMAP_F_NEW 0x01 /* blocks have been newly allocated */ +/* + * IOMAP_F_DIRTY indicates the inode has uncommitted metadata needed to access + * written data and requires fdatasync to commit them to persistent storage. + */ +#define IOMAP_F_DIRTY 0x02 /* * Flags that only need to be reported for IOMAP_REPORT requests: diff --git a/include/linux/mm.h b/include/linux/mm.h index 5411cb7442de..f57e55782d7d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1182,6 +1182,9 @@ static inline void clear_page_pfmemalloc(struct page *page) #define VM_FAULT_RETRY 0x0400 /* ->fault blocked, must retry */ #define VM_FAULT_FALLBACK 0x0800 /* huge page fault failed, fall back to small */ #define VM_FAULT_DONE_COW 0x1000 /* ->fault has fully handled COW */ +#define VM_FAULT_NEEDDSYNC 0x2000 /* ->fault did not modify page tables + * and needs fsync() to complete (for + * synchronous page faults in DAX) */ #define VM_FAULT_ERROR (VM_FAULT_OOM | VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV | \ VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE | \ @@ -1199,7 +1202,8 @@ static inline void clear_page_pfmemalloc(struct page *page) { VM_FAULT_LOCKED, "LOCKED" }, \ { VM_FAULT_RETRY, "RETRY" }, \ { VM_FAULT_FALLBACK, "FALLBACK" }, \ - { VM_FAULT_DONE_COW, "DONE_COW" } + { VM_FAULT_DONE_COW, "DONE_COW" }, \ + { VM_FAULT_NEEDDSYNC, "NEEDDSYNC" } /* Encode hstate index for a hwpoisoned large page */ #define VM_FAULT_SET_HINDEX(x) ((x) << 12) -- cgit v1.2.3-59-g8ed1b From 71eab6dfd91eabf06fe782a590c07e8a0795f5ea Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 1 Nov 2017 16:36:43 +0100 Subject: dax: Implement dax_finish_sync_fault() Implement a function that filesystems can call to finish handling of synchronous page faults. It takes care of syncing appropriare file range and insertion of page table entry. Reviewed-by: Ross Zwisler Reviewed-by: Christoph Hellwig Signed-off-by: Jan Kara Signed-off-by: Dan Williams --- fs/dax.c | 83 +++++++++++++++++++++++++++++++++++++++++++ include/linux/dax.h | 2 ++ include/trace/events/fs_dax.h | 2 ++ 3 files changed, 87 insertions(+) (limited to 'include') diff --git a/fs/dax.c b/fs/dax.c index bb9ff907738c..78233c716757 100644 --- a/fs/dax.c +++ b/fs/dax.c @@ -1492,3 +1492,86 @@ int dax_iomap_fault(struct vm_fault *vmf, enum page_entry_size pe_size, } } EXPORT_SYMBOL_GPL(dax_iomap_fault); + +/** + * dax_insert_pfn_mkwrite - insert PTE or PMD entry into page tables + * @vmf: The description of the fault + * @pe_size: Size of entry to be inserted + * @pfn: PFN to insert + * + * This function inserts writeable PTE or PMD entry into page tables for mmaped + * DAX file. It takes care of marking corresponding radix tree entry as dirty + * as well. + */ +static int dax_insert_pfn_mkwrite(struct vm_fault *vmf, + enum page_entry_size pe_size, + pfn_t pfn) +{ + struct address_space *mapping = vmf->vma->vm_file->f_mapping; + void *entry, **slot; + pgoff_t index = vmf->pgoff; + int vmf_ret, error; + + spin_lock_irq(&mapping->tree_lock); + entry = get_unlocked_mapping_entry(mapping, index, &slot); + /* Did we race with someone splitting entry or so? */ + if (!entry || + (pe_size == PE_SIZE_PTE && !dax_is_pte_entry(entry)) || + (pe_size == PE_SIZE_PMD && !dax_is_pmd_entry(entry))) { + put_unlocked_mapping_entry(mapping, index, entry); + spin_unlock_irq(&mapping->tree_lock); + trace_dax_insert_pfn_mkwrite_no_entry(mapping->host, vmf, + VM_FAULT_NOPAGE); + return VM_FAULT_NOPAGE; + } + radix_tree_tag_set(&mapping->page_tree, index, PAGECACHE_TAG_DIRTY); + entry = lock_slot(mapping, slot); + spin_unlock_irq(&mapping->tree_lock); + switch (pe_size) { + case PE_SIZE_PTE: + error = vm_insert_mixed_mkwrite(vmf->vma, vmf->address, pfn); + vmf_ret = dax_fault_return(error); + break; +#ifdef CONFIG_FS_DAX_PMD + case PE_SIZE_PMD: + vmf_ret = vmf_insert_pfn_pmd(vmf->vma, vmf->address, vmf->pmd, + pfn, true); + break; +#endif + default: + vmf_ret = VM_FAULT_FALLBACK; + } + put_locked_mapping_entry(mapping, index); + trace_dax_insert_pfn_mkwrite(mapping->host, vmf, vmf_ret); + return vmf_ret; +} + +/** + * dax_finish_sync_fault - finish synchronous page fault + * @vmf: The description of the fault + * @pe_size: Size of entry to be inserted + * @pfn: PFN to insert + * + * This function ensures that the file range touched by the page fault is + * stored persistently on the media and handles inserting of appropriate page + * table entry. + */ +int dax_finish_sync_fault(struct vm_fault *vmf, enum page_entry_size pe_size, + pfn_t pfn) +{ + int err; + loff_t start = ((loff_t)vmf->pgoff) << PAGE_SHIFT; + size_t len = 0; + + if (pe_size == PE_SIZE_PTE) + len = PAGE_SIZE; + else if (pe_size == PE_SIZE_PMD) + len = PMD_SIZE; + else + WARN_ON_ONCE(1); + err = vfs_fsync_range(vmf->vma->vm_file, start, start + len - 1, 1); + if (err) + return VM_FAULT_SIGBUS; + return dax_insert_pfn_mkwrite(vmf, pe_size, pfn); +} +EXPORT_SYMBOL_GPL(dax_finish_sync_fault); diff --git a/include/linux/dax.h b/include/linux/dax.h index e7fa4b8f45bc..d403f78b706c 100644 --- a/include/linux/dax.h +++ b/include/linux/dax.h @@ -96,6 +96,8 @@ ssize_t dax_iomap_rw(struct kiocb *iocb, struct iov_iter *iter, const struct iomap_ops *ops); int dax_iomap_fault(struct vm_fault *vmf, enum page_entry_size pe_size, pfn_t *pfnp, const struct iomap_ops *ops); +int dax_finish_sync_fault(struct vm_fault *vmf, enum page_entry_size pe_size, + pfn_t pfn); int dax_delete_mapping_entry(struct address_space *mapping, pgoff_t index); int dax_invalidate_mapping_entry_sync(struct address_space *mapping, pgoff_t index); diff --git a/include/trace/events/fs_dax.h b/include/trace/events/fs_dax.h index 88a9d19b8ff8..7725459fafef 100644 --- a/include/trace/events/fs_dax.h +++ b/include/trace/events/fs_dax.h @@ -190,6 +190,8 @@ DEFINE_EVENT(dax_pte_fault_class, name, \ DEFINE_PTE_FAULT_EVENT(dax_pte_fault); DEFINE_PTE_FAULT_EVENT(dax_pte_fault_done); DEFINE_PTE_FAULT_EVENT(dax_load_hole); +DEFINE_PTE_FAULT_EVENT(dax_insert_pfn_mkwrite_no_entry); +DEFINE_PTE_FAULT_EVENT(dax_insert_pfn_mkwrite); TRACE_EVENT(dax_insert_mapping, TP_PROTO(struct inode *inode, struct vm_fault *vmf, void *radix_entry), -- cgit v1.2.3-59-g8ed1b From b8a6176c214cf9aa2679131ed7e4515cddaadc33 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 1 Nov 2017 16:36:45 +0100 Subject: ext4: Support for synchronous DAX faults We return IOMAP_F_DIRTY flag from ext4_iomap_begin() when asked to prepare blocks for writing and the inode has some uncommitted metadata changes. In the fault handler ext4_dax_fault() we then detect this case (through VM_FAULT_NEEDDSYNC return value) and call helper dax_finish_sync_fault() to flush metadata changes and insert page table entry. Note that this will also dirty corresponding radix tree entry which is what we want - fsync(2) will still provide data integrity guarantees for applications not using userspace flushing. And applications using userspace flushing can avoid calling fsync(2) and thus avoid the performance overhead. Reviewed-by: Ross Zwisler Signed-off-by: Jan Kara Signed-off-by: Dan Williams --- fs/ext4/file.c | 15 ++++++++++++++- fs/ext4/inode.c | 15 +++++++++++++++ fs/jbd2/journal.c | 17 +++++++++++++++++ include/linux/jbd2.h | 1 + 4 files changed, 47 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 208adfc3e673..08a1d1a33a90 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "ext4.h" #include "ext4_jbd2.h" #include "xattr.h" @@ -295,6 +296,7 @@ static int ext4_dax_huge_fault(struct vm_fault *vmf, */ bool write = (vmf->flags & FAULT_FLAG_WRITE) && (vmf->vma->vm_flags & VM_SHARED); + pfn_t pfn; if (write) { sb_start_pagefault(sb); @@ -310,9 +312,12 @@ static int ext4_dax_huge_fault(struct vm_fault *vmf, } else { down_read(&EXT4_I(inode)->i_mmap_sem); } - result = dax_iomap_fault(vmf, pe_size, NULL, &ext4_iomap_ops); + result = dax_iomap_fault(vmf, pe_size, &pfn, &ext4_iomap_ops); if (write) { ext4_journal_stop(handle); + /* Handling synchronous page fault? */ + if (result & VM_FAULT_NEEDDSYNC) + result = dax_finish_sync_fault(vmf, pe_size, pfn); up_read(&EXT4_I(inode)->i_mmap_sem); sb_end_pagefault(sb); } else { @@ -350,6 +355,13 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma) if (unlikely(ext4_forced_shutdown(EXT4_SB(inode->i_sb)))) return -EIO; + /* + * We don't support synchronous mappings for non-DAX files. At least + * until someone comes with a sensible use case. + */ + if (!IS_DAX(file_inode(file)) && (vma->vm_flags & VM_SYNC)) + return -EOPNOTSUPP; + file_accessed(file); if (IS_DAX(file_inode(file))) { vma->vm_ops = &ext4_dax_vm_ops; @@ -719,6 +731,7 @@ const struct file_operations ext4_file_operations = { .compat_ioctl = ext4_compat_ioctl, #endif .mmap = ext4_file_mmap, + .mmap_supported_flags = MAP_SYNC, .open = ext4_file_open, .release = ext4_release_file, .fsync = ext4_sync_file, diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 31db875bc7a1..13a198924a0f 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -3394,6 +3394,19 @@ static int ext4_releasepage(struct page *page, gfp_t wait) } #ifdef CONFIG_FS_DAX +static bool ext4_inode_datasync_dirty(struct inode *inode) +{ + journal_t *journal = EXT4_SB(inode->i_sb)->s_journal; + + if (journal) + return !jbd2_transaction_committed(journal, + EXT4_I(inode)->i_datasync_tid); + /* Any metadata buffers to write? */ + if (!list_empty(&inode->i_mapping->private_list)) + return true; + return inode->i_state & I_DIRTY_DATASYNC; +} + static int ext4_iomap_begin(struct inode *inode, loff_t offset, loff_t length, unsigned flags, struct iomap *iomap) { @@ -3466,6 +3479,8 @@ retry: } iomap->flags = 0; + if ((flags & IOMAP_WRITE) && ext4_inode_datasync_dirty(inode)) + iomap->flags |= IOMAP_F_DIRTY; iomap->bdev = inode->i_sb->s_bdev; iomap->dax_dev = sbi->s_daxdev; iomap->offset = first_block << blkbits; diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 7d5ef3bf3f3e..fa8cde498b4b 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -738,6 +738,23 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid) return err; } +/* Return 1 when transaction with given tid has already committed. */ +int jbd2_transaction_committed(journal_t *journal, tid_t tid) +{ + int ret = 1; + + read_lock(&journal->j_state_lock); + if (journal->j_running_transaction && + journal->j_running_transaction->t_tid == tid) + ret = 0; + if (journal->j_committing_transaction && + journal->j_committing_transaction->t_tid == tid) + ret = 0; + read_unlock(&journal->j_state_lock); + return ret; +} +EXPORT_SYMBOL(jbd2_transaction_committed); + /* * When this function returns the transaction corresponding to tid * will be completed. If the transaction has currently running, start diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 606b6bce3a5b..296d1e0ea87b 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -1367,6 +1367,7 @@ int jbd2_log_start_commit(journal_t *journal, tid_t tid); int __jbd2_log_start_commit(journal_t *journal, tid_t tid); int jbd2_journal_start_commit(journal_t *journal, tid_t *tid); int jbd2_log_wait_commit(journal_t *journal, tid_t tid); +int jbd2_transaction_committed(journal_t *journal, tid_t tid); int jbd2_complete_transaction(journal_t *journal, tid_t tid); int jbd2_log_do_checkpoint(journal_t *journal); int jbd2_trans_will_send_data_barrier(journal_t *journal, tid_t tid); -- cgit v1.2.3-59-g8ed1b From acfc587810bea08fdd62c9d5ed7cefe8be874a4f Mon Sep 17 00:00:00 2001 From: Harsh Jain Date: Sun, 8 Oct 2017 13:37:20 +0530 Subject: crypto: gf128mul - The x8_ble multiplication functions It multiply GF(2^128) elements in the ble format. It will be used by chelsio driver to speed up gf multiplication. Signed-off-by: Harsh Jain Signed-off-by: Herbert Xu --- crypto/gf128mul.c | 13 +++++++++++++ include/crypto/gf128mul.h | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/crypto/gf128mul.c b/crypto/gf128mul.c index dc012129c063..24e601954c7a 100644 --- a/crypto/gf128mul.c +++ b/crypto/gf128mul.c @@ -156,6 +156,19 @@ static void gf128mul_x8_bbe(be128 *x) x->b = cpu_to_be64((b << 8) ^ _tt); } +void gf128mul_x8_ble(le128 *r, const le128 *x) +{ + u64 a = le64_to_cpu(x->a); + u64 b = le64_to_cpu(x->b); + + /* equivalent to gf128mul_table_be[b >> 63] (see crypto/gf128mul.c): */ + u64 _tt = gf128mul_table_be[a >> 56]; + + r->a = cpu_to_le64((a << 8) | (b >> 56)); + r->b = cpu_to_le64((b << 8) ^ _tt); +} +EXPORT_SYMBOL(gf128mul_x8_ble); + void gf128mul_lle(be128 *r, const be128 *b) { be128 p[8]; diff --git a/include/crypto/gf128mul.h b/include/crypto/gf128mul.h index 0977fb18ff68..fa0a63d298dc 100644 --- a/include/crypto/gf128mul.h +++ b/include/crypto/gf128mul.h @@ -227,7 +227,7 @@ struct gf128mul_4k *gf128mul_init_4k_lle(const be128 *g); struct gf128mul_4k *gf128mul_init_4k_bbe(const be128 *g); void gf128mul_4k_lle(be128 *a, const struct gf128mul_4k *t); void gf128mul_4k_bbe(be128 *a, const struct gf128mul_4k *t); - +void gf128mul_x8_ble(le128 *r, const le128 *x); static inline void gf128mul_free_4k(struct gf128mul_4k *t) { kzfree(t); -- cgit v1.2.3-59-g8ed1b From ada69a1639eca54ff74d839a6513c43db8d57d70 Mon Sep 17 00:00:00 2001 From: Gilad Ben-Yossef Date: Wed, 18 Oct 2017 08:00:38 +0100 Subject: crypto: introduce crypto wait for async op Invoking a possibly async. crypto op and waiting for completion while correctly handling backlog processing is a common task in the crypto API implementation and outside users of it. This patch adds a generic implementation for doing so in preparation for using it across the board instead of hand rolled versions. Signed-off-by: Gilad Ben-Yossef CC: Eric Biggers CC: Jonathan Cameron Signed-off-by: Herbert Xu --- crypto/api.c | 13 +++++++++++++ include/linux/crypto.h | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) (limited to 'include') diff --git a/crypto/api.c b/crypto/api.c index 941cd4c6c7ec..2a2479d168aa 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "internal.h" LIST_HEAD(crypto_alg_list); @@ -595,5 +596,17 @@ int crypto_has_alg(const char *name, u32 type, u32 mask) } EXPORT_SYMBOL_GPL(crypto_has_alg); +void crypto_req_done(struct crypto_async_request *req, int err) +{ + struct crypto_wait *wait = req->data; + + if (err == -EINPROGRESS) + return; + + wait->err = err; + complete(&wait->completion); +} +EXPORT_SYMBOL_GPL(crypto_req_done); + MODULE_DESCRIPTION("Cryptographic core API"); MODULE_LICENSE("GPL"); diff --git a/include/linux/crypto.h b/include/linux/crypto.h index 84da9978e951..78508ca4b108 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -24,6 +24,7 @@ #include #include #include +#include /* * Autoloaded crypto modules should only use a prefixed name to avoid allowing @@ -467,6 +468,45 @@ struct crypto_alg { struct module *cra_module; } CRYPTO_MINALIGN_ATTR; +/* + * A helper struct for waiting for completion of async crypto ops + */ +struct crypto_wait { + struct completion completion; + int err; +}; + +/* + * Macro for declaring a crypto op async wait object on stack + */ +#define DECLARE_CRYPTO_WAIT(_wait) \ + struct crypto_wait _wait = { \ + COMPLETION_INITIALIZER_ONSTACK((_wait).completion), 0 } + +/* + * Async ops completion helper functioons + */ +void crypto_req_done(struct crypto_async_request *req, int err); + +static inline int crypto_wait_req(int err, struct crypto_wait *wait) +{ + switch (err) { + case -EINPROGRESS: + case -EBUSY: + wait_for_completion(&wait->completion); + reinit_completion(&wait->completion); + err = wait->err; + break; + }; + + return err; +} + +static inline void crypto_init_wait(struct crypto_wait *wait) +{ + init_completion(&wait->completion); +} + /* * Algorithm registration interface. */ -- cgit v1.2.3-59-g8ed1b From 2c3f8b162106a7d12097d02eb22459f57fab8247 Mon Sep 17 00:00:00 2001 From: Gilad Ben-Yossef Date: Wed, 18 Oct 2017 08:00:39 +0100 Subject: crypto: algif - move to generic async completion algif starts several async crypto ops and waits for their completion. Move it over to generic code doing the same. Signed-off-by: Gilad Ben-Yossef Signed-off-by: Herbert Xu --- crypto/af_alg.c | 27 --------------------------- crypto/algif_aead.c | 8 ++++---- crypto/algif_hash.c | 30 ++++++++++++++---------------- crypto/algif_skcipher.c | 9 ++++----- include/crypto/if_alg.h | 15 +-------------- 5 files changed, 23 insertions(+), 66 deletions(-) (limited to 'include') diff --git a/crypto/af_alg.c b/crypto/af_alg.c index 337cf382718e..85cea9de324a 100644 --- a/crypto/af_alg.c +++ b/crypto/af_alg.c @@ -481,33 +481,6 @@ int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con) } EXPORT_SYMBOL_GPL(af_alg_cmsg_send); -int af_alg_wait_for_completion(int err, struct af_alg_completion *completion) -{ - switch (err) { - case -EINPROGRESS: - case -EBUSY: - wait_for_completion(&completion->completion); - reinit_completion(&completion->completion); - err = completion->err; - break; - }; - - return err; -} -EXPORT_SYMBOL_GPL(af_alg_wait_for_completion); - -void af_alg_complete(struct crypto_async_request *req, int err) -{ - struct af_alg_completion *completion = req->data; - - if (err == -EINPROGRESS) - return; - - completion->err = err; - complete(&completion->completion); -} -EXPORT_SYMBOL_GPL(af_alg_complete); - /** * af_alg_alloc_tsgl - allocate the TX SGL * diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c index 516b38c3a169..aacae0837aff 100644 --- a/crypto/algif_aead.c +++ b/crypto/algif_aead.c @@ -278,11 +278,11 @@ static int _aead_recvmsg(struct socket *sock, struct msghdr *msg, /* Synchronous operation */ aead_request_set_callback(&areq->cra_u.aead_req, CRYPTO_TFM_REQ_MAY_BACKLOG, - af_alg_complete, &ctx->completion); - err = af_alg_wait_for_completion(ctx->enc ? + crypto_req_done, &ctx->wait); + err = crypto_wait_req(ctx->enc ? crypto_aead_encrypt(&areq->cra_u.aead_req) : crypto_aead_decrypt(&areq->cra_u.aead_req), - &ctx->completion); + &ctx->wait); } /* AIO operation in progress */ @@ -554,7 +554,7 @@ static int aead_accept_parent_nokey(void *private, struct sock *sk) ctx->merge = 0; ctx->enc = 0; ctx->aead_assoclen = 0; - af_alg_init_completion(&ctx->completion); + crypto_init_wait(&ctx->wait); ask->private = ctx; diff --git a/crypto/algif_hash.c b/crypto/algif_hash.c index 5e92bd275ef3..76d2e716c792 100644 --- a/crypto/algif_hash.c +++ b/crypto/algif_hash.c @@ -26,7 +26,7 @@ struct hash_ctx { u8 *result; - struct af_alg_completion completion; + struct crypto_wait wait; unsigned int len; bool more; @@ -88,8 +88,7 @@ static int hash_sendmsg(struct socket *sock, struct msghdr *msg, if ((msg->msg_flags & MSG_MORE)) hash_free_result(sk, ctx); - err = af_alg_wait_for_completion(crypto_ahash_init(&ctx->req), - &ctx->completion); + err = crypto_wait_req(crypto_ahash_init(&ctx->req), &ctx->wait); if (err) goto unlock; } @@ -110,8 +109,8 @@ static int hash_sendmsg(struct socket *sock, struct msghdr *msg, ahash_request_set_crypt(&ctx->req, ctx->sgl.sg, NULL, len); - err = af_alg_wait_for_completion(crypto_ahash_update(&ctx->req), - &ctx->completion); + err = crypto_wait_req(crypto_ahash_update(&ctx->req), + &ctx->wait); af_alg_free_sg(&ctx->sgl); if (err) goto unlock; @@ -129,8 +128,8 @@ static int hash_sendmsg(struct socket *sock, struct msghdr *msg, goto unlock; ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0); - err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req), - &ctx->completion); + err = crypto_wait_req(crypto_ahash_final(&ctx->req), + &ctx->wait); } unlock: @@ -171,7 +170,7 @@ static ssize_t hash_sendpage(struct socket *sock, struct page *page, } else { if (!ctx->more) { err = crypto_ahash_init(&ctx->req); - err = af_alg_wait_for_completion(err, &ctx->completion); + err = crypto_wait_req(err, &ctx->wait); if (err) goto unlock; } @@ -179,7 +178,7 @@ static ssize_t hash_sendpage(struct socket *sock, struct page *page, err = crypto_ahash_update(&ctx->req); } - err = af_alg_wait_for_completion(err, &ctx->completion); + err = crypto_wait_req(err, &ctx->wait); if (err) goto unlock; @@ -215,17 +214,16 @@ static int hash_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, ahash_request_set_crypt(&ctx->req, NULL, ctx->result, 0); if (!result && !ctx->more) { - err = af_alg_wait_for_completion( - crypto_ahash_init(&ctx->req), - &ctx->completion); + err = crypto_wait_req(crypto_ahash_init(&ctx->req), + &ctx->wait); if (err) goto unlock; } if (!result || ctx->more) { ctx->more = 0; - err = af_alg_wait_for_completion(crypto_ahash_final(&ctx->req), - &ctx->completion); + err = crypto_wait_req(crypto_ahash_final(&ctx->req), + &ctx->wait); if (err) goto unlock; } @@ -476,13 +474,13 @@ static int hash_accept_parent_nokey(void *private, struct sock *sk) ctx->result = NULL; ctx->len = len; ctx->more = 0; - af_alg_init_completion(&ctx->completion); + crypto_init_wait(&ctx->wait); ask->private = ctx; ahash_request_set_tfm(&ctx->req, hash); ahash_request_set_callback(&ctx->req, CRYPTO_TFM_REQ_MAY_BACKLOG, - af_alg_complete, &ctx->completion); + crypto_req_done, &ctx->wait); sk->sk_destruct = hash_sock_destruct; diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c index 8ae4170aaeb4..9954b078f0b9 100644 --- a/crypto/algif_skcipher.c +++ b/crypto/algif_skcipher.c @@ -129,12 +129,11 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg, skcipher_request_set_callback(&areq->cra_u.skcipher_req, CRYPTO_TFM_REQ_MAY_SLEEP | CRYPTO_TFM_REQ_MAY_BACKLOG, - af_alg_complete, - &ctx->completion); - err = af_alg_wait_for_completion(ctx->enc ? + crypto_req_done, &ctx->wait); + err = crypto_wait_req(ctx->enc ? crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) : crypto_skcipher_decrypt(&areq->cra_u.skcipher_req), - &ctx->completion); + &ctx->wait); } /* AIO operation in progress */ @@ -388,7 +387,7 @@ static int skcipher_accept_parent_nokey(void *private, struct sock *sk) ctx->more = 0; ctx->merge = 0; ctx->enc = 0; - af_alg_init_completion(&ctx->completion); + crypto_init_wait(&ctx->wait); ask->private = ctx; diff --git a/include/crypto/if_alg.h b/include/crypto/if_alg.h index 75ec9c662268..6abf0a3604dc 100644 --- a/include/crypto/if_alg.h +++ b/include/crypto/if_alg.h @@ -40,11 +40,6 @@ struct alg_sock { void *private; }; -struct af_alg_completion { - struct completion completion; - int err; -}; - struct af_alg_control { struct af_alg_iv *iv; int op; @@ -152,7 +147,7 @@ struct af_alg_ctx { void *iv; size_t aead_assoclen; - struct af_alg_completion completion; + struct crypto_wait wait; size_t used; size_t rcvused; @@ -177,19 +172,11 @@ void af_alg_link_sg(struct af_alg_sgl *sgl_prev, struct af_alg_sgl *sgl_new); int af_alg_cmsg_send(struct msghdr *msg, struct af_alg_control *con); -int af_alg_wait_for_completion(int err, struct af_alg_completion *completion); -void af_alg_complete(struct crypto_async_request *req, int err); - static inline struct alg_sock *alg_sk(struct sock *sk) { return (struct alg_sock *)sk; } -static inline void af_alg_init_completion(struct af_alg_completion *completion) -{ - init_completion(&completion->completion); -} - /** * Size of available buffer for sending data from user space to kernel. * -- cgit v1.2.3-59-g8ed1b From 85a2dea4bdbfa7565818ca094d08e838cf62da77 Mon Sep 17 00:00:00 2001 From: Gilad Ben-Yossef Date: Wed, 18 Oct 2017 08:00:41 +0100 Subject: crypto: drbg - move to generic async completion DRBG is starting an async. crypto op and waiting for it complete. Move it over to generic code doing the same. The code now also passes CRYPTO_TFM_REQ_MAY_SLEEP flag indicating crypto request memory allocation may use GFP_KERNEL which should be perfectly fine as the code is obviously sleeping for the completion of the request any way. Signed-off-by: Gilad Ben-Yossef Signed-off-by: Herbert Xu --- crypto/drbg.c | 36 +++++++++--------------------------- include/crypto/drbg.h | 3 +-- 2 files changed, 10 insertions(+), 29 deletions(-) (limited to 'include') diff --git a/crypto/drbg.c b/crypto/drbg.c index 70018397e59a..4faa2781c964 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -1651,16 +1651,6 @@ static int drbg_fini_sym_kernel(struct drbg_state *drbg) return 0; } -static void drbg_skcipher_cb(struct crypto_async_request *req, int error) -{ - struct drbg_state *drbg = req->data; - - if (error == -EINPROGRESS) - return; - drbg->ctr_async_err = error; - complete(&drbg->ctr_completion); -} - static int drbg_init_sym_kernel(struct drbg_state *drbg) { struct crypto_cipher *tfm; @@ -1691,7 +1681,7 @@ static int drbg_init_sym_kernel(struct drbg_state *drbg) return PTR_ERR(sk_tfm); } drbg->ctr_handle = sk_tfm; - init_completion(&drbg->ctr_completion); + crypto_init_wait(&drbg->ctr_wait); req = skcipher_request_alloc(sk_tfm, GFP_KERNEL); if (!req) { @@ -1700,8 +1690,9 @@ static int drbg_init_sym_kernel(struct drbg_state *drbg) return -ENOMEM; } drbg->ctr_req = req; - skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, - drbg_skcipher_cb, drbg); + skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + crypto_req_done, &drbg->ctr_wait); alignmask = crypto_skcipher_alignmask(sk_tfm); drbg->ctr_null_value_buf = kzalloc(DRBG_CTR_NULL_LEN + alignmask, @@ -1762,21 +1753,12 @@ static int drbg_kcapi_sym_ctr(struct drbg_state *drbg, /* Output buffer may not be valid for SGL, use scratchpad */ skcipher_request_set_crypt(drbg->ctr_req, &sg_in, &sg_out, cryptlen, drbg->V); - ret = crypto_skcipher_encrypt(drbg->ctr_req); - switch (ret) { - case 0: - break; - case -EINPROGRESS: - case -EBUSY: - wait_for_completion(&drbg->ctr_completion); - if (!drbg->ctr_async_err) { - reinit_completion(&drbg->ctr_completion); - break; - } - default: + ret = crypto_wait_req(crypto_skcipher_encrypt(drbg->ctr_req), + &drbg->ctr_wait); + if (ret) goto out; - } - init_completion(&drbg->ctr_completion); + + crypto_init_wait(&drbg->ctr_wait); memcpy(outbuf, drbg->outscratchpad, cryptlen); diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h index 22f884c97387..8f941102af36 100644 --- a/include/crypto/drbg.h +++ b/include/crypto/drbg.h @@ -126,8 +126,7 @@ struct drbg_state { __u8 *ctr_null_value; /* CTR mode aligned zero buf */ __u8 *outscratchpadbuf; /* CTR mode output scratchpad */ __u8 *outscratchpad; /* CTR mode aligned outbuf */ - struct completion ctr_completion; /* CTR mode async handler */ - int ctr_async_err; /* CTR mode async error */ + struct crypto_wait ctr_wait; /* CTR mode async wait obj */ bool seeded; /* DRBG fully seeded? */ bool pr; /* Prediction resistance enabled? */ -- cgit v1.2.3-59-g8ed1b From 27e64b4be4b863d884f3ec1686a2f744ae93a1b9 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 31 Oct 2017 15:50:53 +0000 Subject: regset: Add support for dynamically sized regsets Currently the regset API doesn't allow for the possibility that regsets (or at least, the amount of meaningful data in a regset) may change in size. In particular, this results in useless padding being added to coredumps if a regset's current size is smaller than its theoretical maximum size. This patch adds a get_size() function to struct user_regset. Individual regset implementations can implement this function to return the current size of the regset data. A regset_size() function is added to provide callers with an abstract interface for determining the size of a regset without needing to know whether the regset is dynamically sized or not. The only affected user of this interface is the ELF coredump code: This patch ports ELF coredump to dump regsets with their actual size in the coredump. This has no effect except for new regsets that are dynamically sized and provide a get_size() implementation. Signed-off-by: Dave Martin Reviewed-by: Catalin Marinas Cc: Oleg Nesterov Cc: Alexander Viro Cc: Thomas Gleixner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Dmitry Safonov Cc: H. J. Lu Signed-off-by: Will Deacon --- fs/binfmt_elf.c | 10 ++++---- include/linux/regset.h | 67 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 65 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 73b01e474fdc..c697882b3aba 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1699,7 +1699,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, long signr, size_t *total) { unsigned int i; - unsigned int regset_size = view->regsets[0].n * view->regsets[0].size; + unsigned int regset0_size = regset_size(t->task, &view->regsets[0]); /* * NT_PRSTATUS is the one special case, because the regset data @@ -1708,11 +1708,11 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, * We assume that regset 0 is NT_PRSTATUS. */ fill_prstatus(&t->prstatus, t->task, signr); - (void) view->regsets[0].get(t->task, &view->regsets[0], 0, regset_size, + (void) view->regsets[0].get(t->task, &view->regsets[0], 0, regset0_size, &t->prstatus.pr_reg, NULL); fill_note(&t->notes[0], "CORE", NT_PRSTATUS, - PRSTATUS_SIZE(t->prstatus, regset_size), &t->prstatus); + PRSTATUS_SIZE(t->prstatus, regset0_size), &t->prstatus); *total += notesize(&t->notes[0]); do_thread_regset_writeback(t->task, &view->regsets[0]); @@ -1728,7 +1728,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, if (regset->core_note_type && regset->get && (!regset->active || regset->active(t->task, regset))) { int ret; - size_t size = regset->n * regset->size; + size_t size = regset_size(t->task, regset); void *data = kmalloc(size, GFP_KERNEL); if (unlikely(!data)) return 0; @@ -1743,7 +1743,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t, size, data); else { SET_PR_FPVALID(&t->prstatus, - 1, regset_size); + 1, regset0_size); fill_note(&t->notes[i], "CORE", NT_PRFPREG, size, data); } diff --git a/include/linux/regset.h b/include/linux/regset.h index 8e0c9febf495..494cedaafdf2 100644 --- a/include/linux/regset.h +++ b/include/linux/regset.h @@ -106,6 +106,28 @@ typedef int user_regset_writeback_fn(struct task_struct *target, const struct user_regset *regset, int immediate); +/** + * user_regset_get_size_fn - type of @get_size function in &struct user_regset + * @target: thread being examined + * @regset: regset being examined + * + * This call is optional; usually the pointer is %NULL. + * + * When provided, this function must return the current size of regset + * data, as observed by the @get function in &struct user_regset. The + * value returned must be a multiple of @size. The returned size is + * required to be valid only until the next time (if any) @regset is + * modified for @target. + * + * This function is intended for dynamically sized regsets. A regset + * that is statically sized does not need to implement it. + * + * This function should not be called directly: instead, callers should + * call regset_size() to determine the current size of a regset. + */ +typedef unsigned int user_regset_get_size_fn(struct task_struct *target, + const struct user_regset *regset); + /** * struct user_regset - accessible thread CPU state * @n: Number of slots (registers). @@ -117,19 +139,33 @@ typedef int user_regset_writeback_fn(struct task_struct *target, * @set: Function to store values. * @active: Function to report if regset is active, or %NULL. * @writeback: Function to write data back to user memory, or %NULL. + * @get_size: Function to return the regset's size, or %NULL. * * This data structure describes a machine resource we call a register set. * This is part of the state of an individual thread, not necessarily * actual CPU registers per se. A register set consists of a number of * similar slots, given by @n. Each slot is @size bytes, and aligned to - * @align bytes (which is at least @size). + * @align bytes (which is at least @size). For dynamically-sized + * regsets, @n must contain the maximum possible number of slots for the + * regset, and @get_size must point to a function that returns the + * current regset size. * - * These functions must be called only on the current thread or on a - * thread that is in %TASK_STOPPED or %TASK_TRACED state, that we are - * guaranteed will not be woken up and return to user mode, and that we - * have called wait_task_inactive() on. (The target thread always might - * wake up for SIGKILL while these functions are working, in which case - * that thread's user_regset state might be scrambled.) + * Callers that need to know only the current size of the regset and do + * not care about its internal structure should call regset_size() + * instead of inspecting @n or calling @get_size. + * + * For backward compatibility, the @get and @set methods must pad to, or + * accept, @n * @size bytes, even if the current regset size is smaller. + * The precise semantics of these operations depend on the regset being + * accessed. + * + * The functions to which &struct user_regset members point must be + * called only on the current thread or on a thread that is in + * %TASK_STOPPED or %TASK_TRACED state, that we are guaranteed will not + * be woken up and return to user mode, and that we have called + * wait_task_inactive() on. (The target thread always might wake up for + * SIGKILL while these functions are working, in which case that + * thread's user_regset state might be scrambled.) * * The @pos argument must be aligned according to @align; the @count * argument must be a multiple of @size. These functions are not @@ -156,6 +192,7 @@ struct user_regset { user_regset_set_fn *set; user_regset_active_fn *active; user_regset_writeback_fn *writeback; + user_regset_get_size_fn *get_size; unsigned int n; unsigned int size; unsigned int align; @@ -371,5 +408,21 @@ static inline int copy_regset_from_user(struct task_struct *target, return regset->set(target, regset, offset, size, NULL, data); } +/** + * regset_size - determine the current size of a regset + * @target: thread to be examined + * @regset: regset to be examined + * + * Note that the returned size is valid only until the next time + * (if any) @regset is modified for @target. + */ +static inline unsigned int regset_size(struct task_struct *target, + const struct user_regset *regset) +{ + if (!regset->get_size) + return regset->n * regset->size; + else + return regset->get_size(target, regset); +} #endif /* */ -- cgit v1.2.3-59-g8ed1b From 7582e22038a266444eb87bc07c372592ad647439 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 31 Oct 2017 15:51:08 +0000 Subject: arm64/sve: Backend logic for setting the vector length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch implements the core logic for changing a task's vector length on request from userspace. This will be used by the ptrace and prctl frontends that are implemented in later patches. The SVE architecture permits, but does not require, implementations to support vector lengths that are not a power of two. To handle this, logic is added to check a requested vector length against a possibly sparse bitmap of available vector lengths at runtime, so that the best supported value can be chosen. Signed-off-by: Dave Martin Reviewed-by: Catalin Marinas Cc: Alex Bennée Signed-off-by: Will Deacon --- arch/arm64/include/asm/fpsimd.h | 8 +++ arch/arm64/kernel/fpsimd.c | 137 +++++++++++++++++++++++++++++++++++++++- include/uapi/linux/prctl.h | 5 ++ 3 files changed, 149 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h index 9bbd74c0ea6b..86f550ce7b4d 100644 --- a/arch/arm64/include/asm/fpsimd.h +++ b/arch/arm64/include/asm/fpsimd.h @@ -20,6 +20,7 @@ #ifndef __ASSEMBLY__ +#include #include /* @@ -70,17 +71,24 @@ extern void fpsimd_update_current_state(struct fpsimd_state *state); extern void fpsimd_flush_task_state(struct task_struct *target); +/* Maximum VL that SVE VL-agnostic software can transparently support */ +#define SVE_VL_ARCH_MAX 0x100 + extern void sve_save_state(void *state, u32 *pfpsr); extern void sve_load_state(void const *state, u32 const *pfpsr, unsigned long vq_minus_1); extern unsigned int sve_get_vl(void); +extern int __ro_after_init sve_max_vl; + #ifdef CONFIG_ARM64_SVE extern size_t sve_state_size(struct task_struct const *task); extern void sve_alloc(struct task_struct *task); extern void fpsimd_release_task(struct task_struct *task); +extern int sve_set_vector_length(struct task_struct *task, + unsigned long vl, unsigned long flags); #else /* ! CONFIG_ARM64_SVE */ diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index e7733fb19388..667be3472114 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c @@ -17,8 +17,10 @@ * along with this program. If not, see . */ +#include #include #include +#include #include #include #include @@ -28,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -114,6 +117,20 @@ static DEFINE_PER_CPU(struct fpsimd_state *, fpsimd_last_state); /* Default VL for tasks that don't set it explicitly: */ static int sve_default_vl = SVE_VL_MIN; +#ifdef CONFIG_ARM64_SVE + +/* Maximum supported vector length across all CPUs (initially poisoned) */ +int __ro_after_init sve_max_vl = -1; +/* Set of available vector lengths, as vq_to_bit(vq): */ +static DECLARE_BITMAP(sve_vq_map, SVE_VQ_MAX); + +#else /* ! CONFIG_ARM64_SVE */ + +/* Dummy declaration for code that will be optimised out: */ +extern DECLARE_BITMAP(sve_vq_map, SVE_VQ_MAX); + +#endif /* ! CONFIG_ARM64_SVE */ + /* * Call __sve_free() directly only if you know task can't be scheduled * or preempted. @@ -271,6 +288,50 @@ static void task_fpsimd_save(void) } } +/* + * Helpers to translate bit indices in sve_vq_map to VQ values (and + * vice versa). This allows find_next_bit() to be used to find the + * _maximum_ VQ not exceeding a certain value. + */ + +static unsigned int vq_to_bit(unsigned int vq) +{ + return SVE_VQ_MAX - vq; +} + +static unsigned int bit_to_vq(unsigned int bit) +{ + if (WARN_ON(bit >= SVE_VQ_MAX)) + bit = SVE_VQ_MAX - 1; + + return SVE_VQ_MAX - bit; +} + +/* + * All vector length selection from userspace comes through here. + * We're on a slow path, so some sanity-checks are included. + * If things go wrong there's a bug somewhere, but try to fall back to a + * safe choice. + */ +static unsigned int find_supported_vector_length(unsigned int vl) +{ + int bit; + int max_vl = sve_max_vl; + + if (WARN_ON(!sve_vl_valid(vl))) + vl = SVE_VL_MIN; + + if (WARN_ON(!sve_vl_valid(max_vl))) + max_vl = SVE_VL_MIN; + + if (vl > max_vl) + vl = max_vl; + + bit = find_next_bit(sve_vq_map, SVE_VQ_MAX, + vq_to_bit(sve_vq_from_vl(vl))); + return sve_vl_from_vq(bit_to_vq(bit)); +} + #define ZREG(sve_state, vq, n) ((char *)(sve_state) + \ (SVE_SIG_ZREG_OFFSET(vq, n) - SVE_SIG_REGS_OFFSET)) @@ -365,6 +426,76 @@ void sve_alloc(struct task_struct *task) BUG_ON(!task->thread.sve_state); } +int sve_set_vector_length(struct task_struct *task, + unsigned long vl, unsigned long flags) +{ + if (flags & ~(unsigned long)(PR_SVE_VL_INHERIT | + PR_SVE_SET_VL_ONEXEC)) + return -EINVAL; + + if (!sve_vl_valid(vl)) + return -EINVAL; + + /* + * Clamp to the maximum vector length that VL-agnostic SVE code can + * work with. A flag may be assigned in the future to allow setting + * of larger vector lengths without confusing older software. + */ + if (vl > SVE_VL_ARCH_MAX) + vl = SVE_VL_ARCH_MAX; + + vl = find_supported_vector_length(vl); + + if (flags & (PR_SVE_VL_INHERIT | + PR_SVE_SET_VL_ONEXEC)) + task->thread.sve_vl_onexec = vl; + else + /* Reset VL to system default on next exec: */ + task->thread.sve_vl_onexec = 0; + + /* Only actually set the VL if not deferred: */ + if (flags & PR_SVE_SET_VL_ONEXEC) + goto out; + + if (vl == task->thread.sve_vl) + goto out; + + /* + * To ensure the FPSIMD bits of the SVE vector registers are preserved, + * write any live register state back to task_struct, and convert to a + * non-SVE thread. + */ + if (task == current) { + local_bh_disable(); + + task_fpsimd_save(); + set_thread_flag(TIF_FOREIGN_FPSTATE); + } + + fpsimd_flush_task_state(task); + if (test_and_clear_tsk_thread_flag(task, TIF_SVE)) + sve_to_fpsimd(task); + + if (task == current) + local_bh_enable(); + + /* + * Force reallocation of task SVE state to the correct size + * on next use: + */ + sve_free(task); + + task->thread.sve_vl = vl; + +out: + if (flags & PR_SVE_VL_INHERIT) + set_tsk_thread_flag(task, TIF_SVE_VL_INHERIT); + else + clear_tsk_thread_flag(task, TIF_SVE_VL_INHERIT); + + return 0; +} + /* * Called from the put_task_struct() path, which cannot get here * unless dead_task is really dead and not schedulable. @@ -481,7 +612,7 @@ void fpsimd_thread_switch(struct task_struct *next) void fpsimd_flush_thread(void) { - int vl; + int vl, supported_vl; if (!system_supports_fpsimd()) return; @@ -509,6 +640,10 @@ void fpsimd_flush_thread(void) if (WARN_ON(!sve_vl_valid(vl))) vl = SVE_VL_MIN; + supported_vl = find_supported_vector_length(vl); + if (WARN_ON(supported_vl != vl)) + vl = supported_vl; + current->thread.sve_vl = vl; /* diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index a8d0759a9e40..1b64901ca6b3 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -197,4 +197,9 @@ struct prctl_mm_map { # define PR_CAP_AMBIENT_LOWER 3 # define PR_CAP_AMBIENT_CLEAR_ALL 4 +/* arm64 Scalable Vector Extension controls */ +# define PR_SVE_SET_VL_ONEXEC (1 << 18) /* defer effect until exec */ +# define PR_SVE_VL_LEN_MASK 0xffff +# define PR_SVE_VL_INHERIT (1 << 17) /* inherit across exec */ + #endif /* _LINUX_PRCTL_H */ -- cgit v1.2.3-59-g8ed1b From 43d4da2c45b2f5d62f8a79ff7c6f95089bb24656 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 31 Oct 2017 15:51:13 +0000 Subject: arm64/sve: ptrace and ELF coredump support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch defines and implements a new regset NT_ARM_SVE, which describes a thread's SVE register state. This allows a debugger to manipulate the SVE state, as well as being included in ELF coredumps for post-mortem debugging. Because the regset size and layout are dependent on the thread's current vector length, it is not possible to define a C struct to describe the regset contents as is done for existing regsets. Instead, and for the same reasons, NT_ARM_SVE is based on the freeform variable-layout approach used for the SVE signal frame. Additionally, to reduce debug overhead when debugging threads that might or might not have live SVE register state, NT_ARM_SVE may be presented in one of two different formats: the old struct user_fpsimd_state format is embedded for describing the state of a thread with no live SVE state, whereas a new variable-layout structure is embedded for describing live SVE state. This avoids a debugger needing to poll NT_PRFPREG in addition to NT_ARM_SVE, and allows existing userspace code to handle the non-SVE case without too much modification. For this to work, NT_ARM_SVE is defined with a fixed-format header of type struct user_sve_header, which the recipient can use to figure out the content, size and layout of the reset of the regset. Accessor macros are defined to allow the vector-length-dependent parts of the regset to be manipulated. Signed-off-by: Alan Hayward Signed-off-by: Dave Martin Reviewed-by: Catalin Marinas Cc: Alex Bennée Cc: Okamoto Takayuki Signed-off-by: Will Deacon --- arch/arm64/include/asm/fpsimd.h | 12 +- arch/arm64/include/uapi/asm/ptrace.h | 138 +++++++++++++++++ arch/arm64/kernel/fpsimd.c | 60 ++++++++ arch/arm64/kernel/ptrace.c | 280 ++++++++++++++++++++++++++++++++++- include/uapi/linux/elf.h | 1 + 5 files changed, 482 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h index d8e0dc9f65a1..d754e5a6949c 100644 --- a/arch/arm64/include/asm/fpsimd.h +++ b/arch/arm64/include/asm/fpsimd.h @@ -38,13 +38,16 @@ struct fpsimd_state { __uint128_t vregs[32]; u32 fpsr; u32 fpcr; + /* + * For ptrace compatibility, pad to next 128-bit + * boundary here if extending this struct. + */ }; }; /* the id of the last cpu to have restored this state */ unsigned int cpu; }; - #if defined(__KERNEL__) && defined(CONFIG_COMPAT) /* Masks for extracting the FPSR and FPCR from the FPSCR */ #define VFP_FPSCR_STAT_MASK 0xf800009f @@ -88,6 +91,10 @@ extern size_t sve_state_size(struct task_struct const *task); extern void sve_alloc(struct task_struct *task); extern void fpsimd_release_task(struct task_struct *task); +extern void fpsimd_sync_to_sve(struct task_struct *task); +extern void sve_sync_to_fpsimd(struct task_struct *task); +extern void sve_sync_from_fpsimd_zeropad(struct task_struct *task); + extern int sve_set_vector_length(struct task_struct *task, unsigned long vl, unsigned long flags); @@ -104,6 +111,9 @@ extern void __init sve_setup(void); static inline void sve_alloc(struct task_struct *task) { } static inline void fpsimd_release_task(struct task_struct *task) { } +static inline void sve_sync_to_fpsimd(struct task_struct *task) { } +static inline void sve_sync_from_fpsimd_zeropad(struct task_struct *task) { } + static inline void sve_init_vq_map(void) { } static inline void sve_update_vq_map(void) { } static inline int sve_verify_vq_map(void) { return 0; } diff --git a/arch/arm64/include/uapi/asm/ptrace.h b/arch/arm64/include/uapi/asm/ptrace.h index 3697d95ba0a1..e7085589f81c 100644 --- a/arch/arm64/include/uapi/asm/ptrace.h +++ b/arch/arm64/include/uapi/asm/ptrace.h @@ -22,6 +22,7 @@ #include #include +#include /* @@ -62,6 +63,8 @@ #ifndef __ASSEMBLY__ +#include + /* * User structures for general purpose, floating point and debug registers. */ @@ -89,6 +92,141 @@ struct user_hwdebug_state { } dbg_regs[16]; }; +/* SVE/FP/SIMD state (NT_ARM_SVE) */ + +struct user_sve_header { + __u32 size; /* total meaningful regset content in bytes */ + __u32 max_size; /* maxmium possible size for this thread */ + __u16 vl; /* current vector length */ + __u16 max_vl; /* maximum possible vector length */ + __u16 flags; + __u16 __reserved; +}; + +/* Definitions for user_sve_header.flags: */ +#define SVE_PT_REGS_MASK (1 << 0) + +#define SVE_PT_REGS_FPSIMD 0 +#define SVE_PT_REGS_SVE SVE_PT_REGS_MASK + +/* + * Common SVE_PT_* flags: + * These must be kept in sync with prctl interface in + */ +#define SVE_PT_VL_INHERIT (PR_SVE_VL_INHERIT >> 16) +#define SVE_PT_VL_ONEXEC (PR_SVE_SET_VL_ONEXEC >> 16) + + +/* + * The remainder of the SVE state follows struct user_sve_header. The + * total size of the SVE state (including header) depends on the + * metadata in the header: SVE_PT_SIZE(vq, flags) gives the total size + * of the state in bytes, including the header. + * + * Refer to for details of how to pass the correct + * "vq" argument to these macros. + */ + +/* Offset from the start of struct user_sve_header to the register data */ +#define SVE_PT_REGS_OFFSET \ + ((sizeof(struct sve_context) + (SVE_VQ_BYTES - 1)) \ + / SVE_VQ_BYTES * SVE_VQ_BYTES) + +/* + * The register data content and layout depends on the value of the + * flags field. + */ + +/* + * (flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD case: + * + * The payload starts at offset SVE_PT_FPSIMD_OFFSET, and is of type + * struct user_fpsimd_state. Additional data might be appended in the + * future: use SVE_PT_FPSIMD_SIZE(vq, flags) to compute the total size. + * SVE_PT_FPSIMD_SIZE(vq, flags) will never be less than + * sizeof(struct user_fpsimd_state). + */ + +#define SVE_PT_FPSIMD_OFFSET SVE_PT_REGS_OFFSET + +#define SVE_PT_FPSIMD_SIZE(vq, flags) (sizeof(struct user_fpsimd_state)) + +/* + * (flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE case: + * + * The payload starts at offset SVE_PT_SVE_OFFSET, and is of size + * SVE_PT_SVE_SIZE(vq, flags). + * + * Additional macros describe the contents and layout of the payload. + * For each, SVE_PT_SVE_x_OFFSET(args) is the start offset relative to + * the start of struct user_sve_header, and SVE_PT_SVE_x_SIZE(args) is + * the size in bytes: + * + * x type description + * - ---- ----------- + * ZREGS \ + * ZREG | + * PREGS | refer to + * PREG | + * FFR / + * + * FPSR uint32_t FPSR + * FPCR uint32_t FPCR + * + * Additional data might be appended in the future. + */ + +#define SVE_PT_SVE_ZREG_SIZE(vq) SVE_SIG_ZREG_SIZE(vq) +#define SVE_PT_SVE_PREG_SIZE(vq) SVE_SIG_PREG_SIZE(vq) +#define SVE_PT_SVE_FFR_SIZE(vq) SVE_SIG_FFR_SIZE(vq) +#define SVE_PT_SVE_FPSR_SIZE sizeof(__u32) +#define SVE_PT_SVE_FPCR_SIZE sizeof(__u32) + +#define __SVE_SIG_TO_PT(offset) \ + ((offset) - SVE_SIG_REGS_OFFSET + SVE_PT_REGS_OFFSET) + +#define SVE_PT_SVE_OFFSET SVE_PT_REGS_OFFSET + +#define SVE_PT_SVE_ZREGS_OFFSET \ + __SVE_SIG_TO_PT(SVE_SIG_ZREGS_OFFSET) +#define SVE_PT_SVE_ZREG_OFFSET(vq, n) \ + __SVE_SIG_TO_PT(SVE_SIG_ZREG_OFFSET(vq, n)) +#define SVE_PT_SVE_ZREGS_SIZE(vq) \ + (SVE_PT_SVE_ZREG_OFFSET(vq, SVE_NUM_ZREGS) - SVE_PT_SVE_ZREGS_OFFSET) + +#define SVE_PT_SVE_PREGS_OFFSET(vq) \ + __SVE_SIG_TO_PT(SVE_SIG_PREGS_OFFSET(vq)) +#define SVE_PT_SVE_PREG_OFFSET(vq, n) \ + __SVE_SIG_TO_PT(SVE_SIG_PREG_OFFSET(vq, n)) +#define SVE_PT_SVE_PREGS_SIZE(vq) \ + (SVE_PT_SVE_PREG_OFFSET(vq, SVE_NUM_PREGS) - \ + SVE_PT_SVE_PREGS_OFFSET(vq)) + +#define SVE_PT_SVE_FFR_OFFSET(vq) \ + __SVE_SIG_TO_PT(SVE_SIG_FFR_OFFSET(vq)) + +#define SVE_PT_SVE_FPSR_OFFSET(vq) \ + ((SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq) + \ + (SVE_VQ_BYTES - 1)) \ + / SVE_VQ_BYTES * SVE_VQ_BYTES) +#define SVE_PT_SVE_FPCR_OFFSET(vq) \ + (SVE_PT_SVE_FPSR_OFFSET(vq) + SVE_PT_SVE_FPSR_SIZE) + +/* + * Any future extension appended after FPCR must be aligned to the next + * 128-bit boundary. + */ + +#define SVE_PT_SVE_SIZE(vq, flags) \ + ((SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE \ + - SVE_PT_SVE_OFFSET + (SVE_VQ_BYTES - 1)) \ + / SVE_VQ_BYTES * SVE_VQ_BYTES) + +#define SVE_PT_SIZE(vq, flags) \ + (((flags) & SVE_PT_REGS_MASK) == SVE_PT_REGS_SVE ? \ + SVE_PT_SVE_OFFSET + SVE_PT_SVE_SIZE(vq, flags) \ + : SVE_PT_FPSIMD_OFFSET + SVE_PT_FPSIMD_SIZE(vq, flags)) + #endif /* __ASSEMBLY__ */ #endif /* _UAPI__ASM_PTRACE_H */ diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index 1e531156f1d7..b82d44693b9d 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c @@ -428,6 +428,66 @@ void sve_alloc(struct task_struct *task) BUG_ON(!task->thread.sve_state); } + +/* + * Ensure that task->thread.sve_state is up to date with respect to + * the user task, irrespective of when SVE is in use or not. + * + * This should only be called by ptrace. task must be non-runnable. + * task->thread.sve_state must point to at least sve_state_size(task) + * bytes of allocated kernel memory. + */ +void fpsimd_sync_to_sve(struct task_struct *task) +{ + if (!test_tsk_thread_flag(task, TIF_SVE)) + fpsimd_to_sve(task); +} + +/* + * Ensure that task->thread.fpsimd_state is up to date with respect to + * the user task, irrespective of whether SVE is in use or not. + * + * This should only be called by ptrace. task must be non-runnable. + * task->thread.sve_state must point to at least sve_state_size(task) + * bytes of allocated kernel memory. + */ +void sve_sync_to_fpsimd(struct task_struct *task) +{ + if (test_tsk_thread_flag(task, TIF_SVE)) + sve_to_fpsimd(task); +} + +/* + * Ensure that task->thread.sve_state is up to date with respect to + * the task->thread.fpsimd_state. + * + * This should only be called by ptrace to merge new FPSIMD register + * values into a task for which SVE is currently active. + * task must be non-runnable. + * task->thread.sve_state must point to at least sve_state_size(task) + * bytes of allocated kernel memory. + * task->thread.fpsimd_state must already have been initialised with + * the new FPSIMD register values to be merged in. + */ +void sve_sync_from_fpsimd_zeropad(struct task_struct *task) +{ + unsigned int vq; + void *sst = task->thread.sve_state; + struct fpsimd_state const *fst = &task->thread.fpsimd_state; + unsigned int i; + + if (!test_tsk_thread_flag(task, TIF_SVE)) + return; + + vq = sve_vq_from_vl(task->thread.sve_vl); + + memset(sst, 0, SVE_SIG_REGS_SIZE(vq)); + + for (i = 0; i < 32; ++i) + memcpy(ZREG(sst, vq, i), &fst->vregs[i], + sizeof(fst->vregs[i])); +} + int sve_set_vector_length(struct task_struct *task, unsigned long vl, unsigned long flags) { diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 9cbb6123208f..7c44658b316d 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ #include #include +#include #include #include #include @@ -618,17 +620,56 @@ static int gpr_set(struct task_struct *target, const struct user_regset *regset, /* * TODO: update fp accessors for lazy context switching (sync/flush hwstate) */ -static int fpr_get(struct task_struct *target, const struct user_regset *regset, - unsigned int pos, unsigned int count, - void *kbuf, void __user *ubuf) +static int __fpr_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf, unsigned int start_pos) { struct user_fpsimd_state *uregs; + + sve_sync_to_fpsimd(target); + uregs = &target->thread.fpsimd_state.user_fpsimd; + return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, + start_pos, start_pos + sizeof(*uregs)); +} + +static int fpr_get(struct task_struct *target, const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ if (target == current) fpsimd_preserve_current_state(); - return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1); + return __fpr_get(target, regset, pos, count, kbuf, ubuf, 0); +} + +static int __fpr_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf, + unsigned int start_pos) +{ + int ret; + struct user_fpsimd_state newstate; + + /* + * Ensure target->thread.fpsimd_state is up to date, so that a + * short copyin can't resurrect stale data. + */ + sve_sync_to_fpsimd(target); + + newstate = target->thread.fpsimd_state.user_fpsimd; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, + start_pos, start_pos + sizeof(newstate)); + if (ret) + return ret; + + target->thread.fpsimd_state.user_fpsimd = newstate; + + return ret; } static int fpr_set(struct task_struct *target, const struct user_regset *regset, @@ -636,15 +677,14 @@ static int fpr_set(struct task_struct *target, const struct user_regset *regset, const void *kbuf, const void __user *ubuf) { int ret; - struct user_fpsimd_state newstate = - target->thread.fpsimd_state.user_fpsimd; - ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, 0, -1); + ret = __fpr_set(target, regset, pos, count, kbuf, ubuf, 0); if (ret) return ret; - target->thread.fpsimd_state.user_fpsimd = newstate; + sve_sync_from_fpsimd_zeropad(target); fpsimd_flush_task_state(target); + return ret; } @@ -702,6 +742,215 @@ static int system_call_set(struct task_struct *target, return ret; } +#ifdef CONFIG_ARM64_SVE + +static void sve_init_header_from_task(struct user_sve_header *header, + struct task_struct *target) +{ + unsigned int vq; + + memset(header, 0, sizeof(*header)); + + header->flags = test_tsk_thread_flag(target, TIF_SVE) ? + SVE_PT_REGS_SVE : SVE_PT_REGS_FPSIMD; + if (test_tsk_thread_flag(target, TIF_SVE_VL_INHERIT)) + header->flags |= SVE_PT_VL_INHERIT; + + header->vl = target->thread.sve_vl; + vq = sve_vq_from_vl(header->vl); + + header->max_vl = sve_max_vl; + if (WARN_ON(!sve_vl_valid(sve_max_vl))) + header->max_vl = header->vl; + + header->size = SVE_PT_SIZE(vq, header->flags); + header->max_size = SVE_PT_SIZE(sve_vq_from_vl(header->max_vl), + SVE_PT_REGS_SVE); +} + +static unsigned int sve_size_from_header(struct user_sve_header const *header) +{ + return ALIGN(header->size, SVE_VQ_BYTES); +} + +static unsigned int sve_get_size(struct task_struct *target, + const struct user_regset *regset) +{ + struct user_sve_header header; + + if (!system_supports_sve()) + return 0; + + sve_init_header_from_task(&header, target); + return sve_size_from_header(&header); +} + +static int sve_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + int ret; + struct user_sve_header header; + unsigned int vq; + unsigned long start, end; + + if (!system_supports_sve()) + return -EINVAL; + + /* Header */ + sve_init_header_from_task(&header, target); + vq = sve_vq_from_vl(header.vl); + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &header, + 0, sizeof(header)); + if (ret) + return ret; + + if (target == current) + fpsimd_preserve_current_state(); + + /* Registers: FPSIMD-only case */ + + BUILD_BUG_ON(SVE_PT_FPSIMD_OFFSET != sizeof(header)); + if ((header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) + return __fpr_get(target, regset, pos, count, kbuf, ubuf, + SVE_PT_FPSIMD_OFFSET); + + /* Otherwise: full SVE case */ + + BUILD_BUG_ON(SVE_PT_SVE_OFFSET != sizeof(header)); + start = SVE_PT_SVE_OFFSET; + end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq); + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + target->thread.sve_state, + start, end); + if (ret) + return ret; + + start = end; + end = SVE_PT_SVE_FPSR_OFFSET(vq); + ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + start, end); + if (ret) + return ret; + + /* + * Copy fpsr, and fpcr which must follow contiguously in + * struct fpsimd_state: + */ + start = end; + end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE; + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + &target->thread.fpsimd_state.fpsr, + start, end); + if (ret) + return ret; + + start = end; + end = sve_size_from_header(&header); + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + start, end); +} + +static int sve_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + int ret; + struct user_sve_header header; + unsigned int vq; + unsigned long start, end; + + if (!system_supports_sve()) + return -EINVAL; + + /* Header */ + if (count < sizeof(header)) + return -EINVAL; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &header, + 0, sizeof(header)); + if (ret) + goto out; + + /* + * Apart from PT_SVE_REGS_MASK, all PT_SVE_* flags are consumed by + * sve_set_vector_length(), which will also validate them for us: + */ + ret = sve_set_vector_length(target, header.vl, + ((unsigned long)header.flags & ~SVE_PT_REGS_MASK) << 16); + if (ret) + goto out; + + /* Actual VL set may be less than the user asked for: */ + vq = sve_vq_from_vl(target->thread.sve_vl); + + /* Registers: FPSIMD-only case */ + + BUILD_BUG_ON(SVE_PT_FPSIMD_OFFSET != sizeof(header)); + if ((header.flags & SVE_PT_REGS_MASK) == SVE_PT_REGS_FPSIMD) { + ret = __fpr_set(target, regset, pos, count, kbuf, ubuf, + SVE_PT_FPSIMD_OFFSET); + clear_tsk_thread_flag(target, TIF_SVE); + goto out; + } + + /* Otherwise: full SVE case */ + + /* + * If setting a different VL from the requested VL and there is + * register data, the data layout will be wrong: don't even + * try to set the registers in this case. + */ + if (count && vq != sve_vq_from_vl(header.vl)) { + ret = -EIO; + goto out; + } + + sve_alloc(target); + + /* + * Ensure target->thread.sve_state is up to date with target's + * FPSIMD regs, so that a short copyin leaves trailing registers + * unmodified. + */ + fpsimd_sync_to_sve(target); + set_tsk_thread_flag(target, TIF_SVE); + + BUILD_BUG_ON(SVE_PT_SVE_OFFSET != sizeof(header)); + start = SVE_PT_SVE_OFFSET; + end = SVE_PT_SVE_FFR_OFFSET(vq) + SVE_PT_SVE_FFR_SIZE(vq); + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + target->thread.sve_state, + start, end); + if (ret) + goto out; + + start = end; + end = SVE_PT_SVE_FPSR_OFFSET(vq); + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + start, end); + if (ret) + goto out; + + /* + * Copy fpsr, and fpcr which must follow contiguously in + * struct fpsimd_state: + */ + start = end; + end = SVE_PT_SVE_FPCR_OFFSET(vq) + SVE_PT_SVE_FPCR_SIZE; + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &target->thread.fpsimd_state.fpsr, + start, end); + +out: + fpsimd_flush_task_state(target); + return ret; +} + +#endif /* CONFIG_ARM64_SVE */ + enum aarch64_regset { REGSET_GPR, REGSET_FPR, @@ -711,6 +960,9 @@ enum aarch64_regset { REGSET_HW_WATCH, #endif REGSET_SYSTEM_CALL, +#ifdef CONFIG_ARM64_SVE + REGSET_SVE, +#endif }; static const struct user_regset aarch64_regsets[] = { @@ -768,6 +1020,18 @@ static const struct user_regset aarch64_regsets[] = { .get = system_call_get, .set = system_call_set, }, +#ifdef CONFIG_ARM64_SVE + [REGSET_SVE] = { /* Scalable Vector Extension */ + .core_note_type = NT_ARM_SVE, + .n = DIV_ROUND_UP(SVE_PT_SIZE(SVE_VQ_MAX, SVE_PT_REGS_SVE), + SVE_VQ_BYTES), + .size = SVE_VQ_BYTES, + .align = SVE_VQ_BYTES, + .get = sve_get, + .set = sve_set, + .get_size = sve_get_size, + }, +#endif }; static const struct user_regset_view user_aarch64_view = { diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index b5280db9ef6a..735b8f4d12fc 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h @@ -416,6 +416,7 @@ typedef struct elf64_shdr { #define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ #define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ #define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ +#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension registers */ #define NT_METAG_CBUF 0x500 /* Metag catch buffer registers */ #define NT_METAG_RPIPE 0x501 /* Metag read pipeline state */ #define NT_METAG_TLS 0x502 /* Metag TLS pointer */ -- cgit v1.2.3-59-g8ed1b From 2d2123bc7c7f843aa9db87720de159a049839862 Mon Sep 17 00:00:00 2001 From: Dave Martin Date: Tue, 31 Oct 2017 15:51:14 +0000 Subject: arm64/sve: Add prctl controls for userspace vector length management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds two arm64-specific prctls, to permit userspace to control its vector length: * PR_SVE_SET_VL: set the thread's SVE vector length and vector length inheritance mode. * PR_SVE_GET_VL: get the same information. Although these prctls resemble instruction set features in the SVE architecture, they provide additional control: the vector length inheritance mode is Linux-specific and nothing to do with the architecture, and the architecture does not permit EL0 to set its own vector length directly. Both can be used in portable tools without requiring the use of SVE instructions. Signed-off-by: Dave Martin Reviewed-by: Catalin Marinas Cc: Alex Bennée [will: Fixed up prctl constants to avoid clash with PDEATHSIG] Signed-off-by: Will Deacon --- arch/arm64/include/asm/fpsimd.h | 14 +++++++++++ arch/arm64/include/asm/processor.h | 4 +++ arch/arm64/kernel/fpsimd.c | 50 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/prctl.h | 4 +++ kernel/sys.c | 12 +++++++++ 5 files changed, 84 insertions(+) (limited to 'include') diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h index d754e5a6949c..b868412c815c 100644 --- a/arch/arm64/include/asm/fpsimd.h +++ b/arch/arm64/include/asm/fpsimd.h @@ -17,6 +17,7 @@ #define __ASM_FP_H #include +#include #ifndef __ASSEMBLY__ @@ -98,6 +99,9 @@ extern void sve_sync_from_fpsimd_zeropad(struct task_struct *task); extern int sve_set_vector_length(struct task_struct *task, unsigned long vl, unsigned long flags); +extern int sve_set_current_vl(unsigned long arg); +extern int sve_get_current_vl(void); + /* * Probing and setup functions. * Calls to these functions must be serialised with one another. @@ -114,6 +118,16 @@ static inline void fpsimd_release_task(struct task_struct *task) { } static inline void sve_sync_to_fpsimd(struct task_struct *task) { } static inline void sve_sync_from_fpsimd_zeropad(struct task_struct *task) { } +static inline int sve_set_current_vl(unsigned long arg) +{ + return -EINVAL; +} + +static inline int sve_get_current_vl(void) +{ + return -EINVAL; +} + static inline void sve_init_vq_map(void) { } static inline void sve_update_vq_map(void) { } static inline int sve_verify_vq_map(void) { return 0; } diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index c6fddb005dc2..023cacb946c3 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h @@ -217,5 +217,9 @@ static inline void spin_lock_prefetch(const void *ptr) int cpu_enable_pan(void *__unused); int cpu_enable_cache_maint_trap(void *__unused); +/* Userspace interface for PR_SVE_{SET,GET}_VL prctl()s: */ +#define SVE_SET_VL(arg) sve_set_current_vl(arg) +#define SVE_GET_VL() sve_get_current_vl() + #endif /* __ASSEMBLY__ */ #endif /* __ASM_PROCESSOR_H */ diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c index b82d44693b9d..fd3cfdd7f9be 100644 --- a/arch/arm64/kernel/fpsimd.c +++ b/arch/arm64/kernel/fpsimd.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -558,6 +559,55 @@ out: return 0; } +/* + * Encode the current vector length and flags for return. + * This is only required for prctl(): ptrace has separate fields + * + * flags are as for sve_set_vector_length(). + */ +static int sve_prctl_status(unsigned long flags) +{ + int ret; + + if (flags & PR_SVE_SET_VL_ONEXEC) + ret = current->thread.sve_vl_onexec; + else + ret = current->thread.sve_vl; + + if (test_thread_flag(TIF_SVE_VL_INHERIT)) + ret |= PR_SVE_VL_INHERIT; + + return ret; +} + +/* PR_SVE_SET_VL */ +int sve_set_current_vl(unsigned long arg) +{ + unsigned long vl, flags; + int ret; + + vl = arg & PR_SVE_VL_LEN_MASK; + flags = arg & ~vl; + + if (!system_supports_sve()) + return -EINVAL; + + ret = sve_set_vector_length(current, vl, flags); + if (ret) + return ret; + + return sve_prctl_status(flags); +} + +/* PR_SVE_GET_VL */ +int sve_get_current_vl(void) +{ + if (!system_supports_sve()) + return -EINVAL; + + return sve_prctl_status(0); +} + /* * Bitmap for temporary storage of the per-CPU set of supported vector lengths * during secondary boot. diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index 1b64901ca6b3..f60db5db6e8e 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -198,7 +198,11 @@ struct prctl_mm_map { # define PR_CAP_AMBIENT_CLEAR_ALL 4 /* arm64 Scalable Vector Extension controls */ +/* Flag values must be kept in sync with ptrace NT_ARM_SVE interface */ +#define PR_SVE_SET_VL 50 /* set task vector length */ # define PR_SVE_SET_VL_ONEXEC (1 << 18) /* defer effect until exec */ +#define PR_SVE_GET_VL 51 /* get task vector length */ +/* Bits common to PR_SVE_SET_VL and PR_SVE_GET_VL */ # define PR_SVE_VL_LEN_MASK 0xffff # define PR_SVE_VL_INHERIT (1 << 17) /* inherit across exec */ diff --git a/kernel/sys.c b/kernel/sys.c index 9aebc2935013..c541916b38c6 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -110,6 +110,12 @@ #ifndef SET_FP_MODE # define SET_FP_MODE(a,b) (-EINVAL) #endif +#ifndef SVE_SET_VL +# define SVE_SET_VL(a) (-EINVAL) +#endif +#ifndef SVE_GET_VL +# define SVE_GET_VL() (-EINVAL) +#endif /* * this is where the system-wide overflow UID and GID are defined, for @@ -2385,6 +2391,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, case PR_GET_FP_MODE: error = GET_FP_MODE(me); break; + case PR_SVE_SET_VL: + error = SVE_SET_VL(arg2); + break; + case PR_SVE_GET_VL: + error = SVE_GET_VL(); + break; default: error = -EINVAL; break; -- cgit v1.2.3-59-g8ed1b From 8977f563845bdd61fc61c4dfb399270b7d5667c6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 2 Nov 2017 21:29:48 +0300 Subject: block: move REQ_NOWAIT This flag should be before the operation-specific REQ_NOUNMAP bit. Signed-off-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Reviewed-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Signed-off-by: Jens Axboe --- include/linux/blk_types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 3385c89f402e..c4c6c8bced2e 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -224,11 +224,11 @@ enum req_flag_bits { __REQ_PREFLUSH, /* request for cache flush */ __REQ_RAHEAD, /* read ahead, can fail anytime */ __REQ_BACKGROUND, /* background IO */ + __REQ_NOWAIT, /* Don't wait if request will block */ /* command specific flags for REQ_OP_WRITE_ZEROES: */ __REQ_NOUNMAP, /* do not free blocks when zeroing */ - __REQ_NOWAIT, /* Don't wait if request will block */ __REQ_NR_BITS, /* stops here */ }; @@ -245,9 +245,9 @@ enum req_flag_bits { #define REQ_PREFLUSH (1ULL << __REQ_PREFLUSH) #define REQ_RAHEAD (1ULL << __REQ_RAHEAD) #define REQ_BACKGROUND (1ULL << __REQ_BACKGROUND) +#define REQ_NOWAIT (1ULL << __REQ_NOWAIT) #define REQ_NOUNMAP (1ULL << __REQ_NOUNMAP) -#define REQ_NOWAIT (1ULL << __REQ_NOWAIT) #define REQ_FAILFAST_MASK \ (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER) -- cgit v1.2.3-59-g8ed1b From 96222bcc732d0504363dc772637c50e53b4bd41e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 2 Nov 2017 21:29:49 +0300 Subject: block: add REQ_DRV bit Set aside a bit in the request/bio flags for driver use. Signed-off-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Reviewed-by: Hannes Reinecke Reviewed-by: Johannes Thumshirn Signed-off-by: Jens Axboe --- include/linux/blk_types.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index c4c6c8bced2e..1b04085255fb 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -229,6 +229,9 @@ enum req_flag_bits { /* command specific flags for REQ_OP_WRITE_ZEROES: */ __REQ_NOUNMAP, /* do not free blocks when zeroing */ + /* for driver use */ + __REQ_DRV, + __REQ_NR_BITS, /* stops here */ }; @@ -249,6 +252,8 @@ enum req_flag_bits { #define REQ_NOUNMAP (1ULL << __REQ_NOUNMAP) +#define REQ_DRV (1ULL << __REQ_DRV) + #define REQ_FAILFAST_MASK \ (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER) -- cgit v1.2.3-59-g8ed1b From f421e1d9ade4e1b88183e54425cf50e390d16a7f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 2 Nov 2017 21:29:50 +0300 Subject: block: provide a direct_make_request helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper allows reinserting a bio into a new queue without much overhead, but requires all queue limits to be the same for the upper and lower queues, and it does not provide any recursion preventions. Signed-off-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Reviewed-by: Javier González Reviewed-by: Hannes Reinecke Signed-off-by: Jens Axboe --- block/blk-core.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/blkdev.h | 1 + 2 files changed, 35 insertions(+) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index bb4fce694a60..96aaa3a419a3 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2243,6 +2243,40 @@ out: } EXPORT_SYMBOL(generic_make_request); +/** + * direct_make_request - hand a buffer directly to its device driver for I/O + * @bio: The bio describing the location in memory and on the device. + * + * This function behaves like generic_make_request(), but does not protect + * against recursion. Must only be used if the called driver is known + * to not call generic_make_request (or direct_make_request) again from + * its make_request function. (Calling direct_make_request again from + * a workqueue is perfectly fine as that doesn't recurse). + */ +blk_qc_t direct_make_request(struct bio *bio) +{ + struct request_queue *q = bio->bi_disk->queue; + bool nowait = bio->bi_opf & REQ_NOWAIT; + blk_qc_t ret; + + if (!generic_make_request_checks(bio)) + return BLK_QC_T_NONE; + + if (unlikely(blk_queue_enter(q, nowait))) { + if (nowait && !blk_queue_dying(q)) + bio->bi_status = BLK_STS_AGAIN; + else + bio->bi_status = BLK_STS_IOERR; + bio_endio(bio); + return BLK_QC_T_NONE; + } + + ret = q->make_request_fn(q, bio); + blk_queue_exit(q); + return ret; +} +EXPORT_SYMBOL_GPL(direct_make_request); + /** * submit_bio - submit a bio to the block device layer for I/O * @bio: The &struct bio which describes the I/O diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 72637028f3c9..eda3d25c0f68 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -920,6 +920,7 @@ static inline void rq_flush_dcache_pages(struct request *rq) extern int blk_register_queue(struct gendisk *disk); extern void blk_unregister_queue(struct gendisk *disk); extern blk_qc_t generic_make_request(struct bio *bio); +extern blk_qc_t direct_make_request(struct bio *bio); extern void blk_rq_init(struct request_queue *q, struct request *rq); extern void blk_init_request_from_bio(struct request *req, struct bio *bio); extern void blk_put_request(struct request *); -- cgit v1.2.3-59-g8ed1b From ef71de8b15d891b27b8c983a9a8972b11cb4576a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 2 Nov 2017 21:29:51 +0300 Subject: block: add a blk_steal_bios helper This helpers allows to bounce steal the uncompleted bios from a request so that they can be reissued on another path. Signed-off-by: Christoph Hellwig Reviewed-by: Sagi Grimberg Reviewed-by: Hannes Reinecke Signed-off-by: Jens Axboe --- block/blk-core.c | 21 +++++++++++++++++++++ include/linux/blkdev.h | 2 ++ 2 files changed, 23 insertions(+) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 96aaa3a419a3..68cfe6780a9b 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2769,6 +2769,27 @@ struct request *blk_fetch_request(struct request_queue *q) } EXPORT_SYMBOL(blk_fetch_request); +/* + * Steal bios from a request and add them to a bio list. + * The request must not have been partially completed before. + */ +void blk_steal_bios(struct bio_list *list, struct request *rq) +{ + if (rq->bio) { + if (list->tail) + list->tail->bi_next = rq->bio; + else + list->head = rq->bio; + list->tail = rq->biotail; + + rq->bio = NULL; + rq->biotail = NULL; + } + + rq->__data_len = 0; +} +EXPORT_SYMBOL_GPL(blk_steal_bios); + /** * blk_update_request - Special helper function for request stacking drivers * @req: the request being processed diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index eda3d25c0f68..fddda6a1f9b5 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1094,6 +1094,8 @@ extern struct request *blk_peek_request(struct request_queue *q); extern void blk_start_request(struct request *rq); extern struct request *blk_fetch_request(struct request_queue *q); +void blk_steal_bios(struct bio_list *list, struct request *rq); + /* * Request completion related functions. * -- cgit v1.2.3-59-g8ed1b From 517bf3c306bad4fe0da631f90ae2ec40924dee2b Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 2 Nov 2017 21:29:52 +0300 Subject: block: don't look at the struct device dev_t in disk_devt The hidden gendisks introduced in the next patch need to keep the dev field in their struct device empty so that udev won't try to create block device nodes for them. To support that rewrite disk_devt to look at the major and first_minor fields in the gendisk itself instead of looking into the struct device. Signed-off-by: Christoph Hellwig Reviewed-by: Johannes Thumshirn Reviewed-by: Hannes Reinecke Signed-off-by: Jens Axboe --- block/genhd.c | 4 ---- include/linux/genhd.h | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/block/genhd.c b/block/genhd.c index dd305c65ffb0..1174d24e405e 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -649,10 +649,6 @@ void device_add_disk(struct device *parent, struct gendisk *disk) return; } disk_to_dev(disk)->devt = devt; - - /* ->major and ->first_minor aren't supposed to be - * dereferenced from here on, but set them just in case. - */ disk->major = MAJOR(devt); disk->first_minor = MINOR(devt); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index ea652bfcd675..5c0ed5db33c2 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -234,7 +234,7 @@ static inline bool disk_part_scan_enabled(struct gendisk *disk) static inline dev_t disk_devt(struct gendisk *disk) { - return disk_to_dev(disk)->devt; + return MKDEV(disk->major, disk->first_minor); } static inline dev_t part_devt(struct hd_struct *part) -- cgit v1.2.3-59-g8ed1b From 8ddcd653257c18a669fcb75ee42c37054908e0d6 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 2 Nov 2017 21:29:53 +0300 Subject: block: introduce GENHD_FL_HIDDEN With this flag a driver can create a gendisk that can be used for I/O submission inside the kernel, but which is not registered as user facing block device. This will be useful for the NVMe multipath implementation. Signed-off-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/genhd.c | 68 +++++++++++++++++++++++++++++++++++++-------------- include/linux/genhd.h | 1 + 2 files changed, 51 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/block/genhd.c b/block/genhd.c index 1174d24e405e..835e907e6e01 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -585,6 +585,11 @@ static void register_disk(struct device *parent, struct gendisk *disk) */ pm_runtime_set_memalloc_noio(ddev, true); + if (disk->flags & GENHD_FL_HIDDEN) { + dev_set_uevent_suppress(ddev, 0); + return; + } + disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj); disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj); @@ -616,6 +621,11 @@ exit: while ((part = disk_part_iter_next(&piter))) kobject_uevent(&part_to_dev(part)->kobj, KOBJ_ADD); disk_part_iter_exit(&piter); + + err = sysfs_create_link(&ddev->kobj, + &disk->queue->backing_dev_info->dev->kobj, + "bdi"); + WARN_ON(err); } /** @@ -630,7 +640,6 @@ exit: */ void device_add_disk(struct device *parent, struct gendisk *disk) { - struct backing_dev_info *bdi; dev_t devt; int retval; @@ -639,7 +648,8 @@ void device_add_disk(struct device *parent, struct gendisk *disk) * parameters make sense. */ WARN_ON(disk->minors && !(disk->major || disk->first_minor)); - WARN_ON(!disk->minors && !(disk->flags & GENHD_FL_EXT_DEVT)); + WARN_ON(!disk->minors && + !(disk->flags & (GENHD_FL_EXT_DEVT | GENHD_FL_HIDDEN))); disk->flags |= GENHD_FL_UP; @@ -648,18 +658,26 @@ void device_add_disk(struct device *parent, struct gendisk *disk) WARN_ON(1); return; } - disk_to_dev(disk)->devt = devt; disk->major = MAJOR(devt); disk->first_minor = MINOR(devt); disk_alloc_events(disk); - /* Register BDI before referencing it from bdev */ - bdi = disk->queue->backing_dev_info; - bdi_register_owner(bdi, disk_to_dev(disk)); - - blk_register_region(disk_devt(disk), disk->minors, NULL, - exact_match, exact_lock, disk); + if (disk->flags & GENHD_FL_HIDDEN) { + /* + * Don't let hidden disks show up in /proc/partitions, + * and don't bother scanning for partitions either. + */ + disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO; + disk->flags |= GENHD_FL_NO_PART_SCAN; + } else { + /* Register BDI before referencing it from bdev */ + disk_to_dev(disk)->devt = devt; + bdi_register_owner(disk->queue->backing_dev_info, + disk_to_dev(disk)); + blk_register_region(disk_devt(disk), disk->minors, NULL, + exact_match, exact_lock, disk); + } register_disk(parent, disk); blk_register_queue(disk); @@ -669,10 +687,6 @@ void device_add_disk(struct device *parent, struct gendisk *disk) */ WARN_ON_ONCE(!blk_get_queue(disk->queue)); - retval = sysfs_create_link(&disk_to_dev(disk)->kobj, &bdi->dev->kobj, - "bdi"); - WARN_ON(retval); - disk_add_events(disk); blk_integrity_add(disk); } @@ -701,7 +715,8 @@ void del_gendisk(struct gendisk *disk) set_capacity(disk, 0); disk->flags &= ~GENHD_FL_UP; - sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi"); + if (!(disk->flags & GENHD_FL_HIDDEN)) + sysfs_remove_link(&disk_to_dev(disk)->kobj, "bdi"); if (disk->queue) { /* * Unregister bdi before releasing device numbers (as they can @@ -712,13 +727,15 @@ void del_gendisk(struct gendisk *disk) } else { WARN_ON(1); } - blk_unregister_region(disk_devt(disk), disk->minors); + + if (!(disk->flags & GENHD_FL_HIDDEN)) { + blk_unregister_region(disk_devt(disk), disk->minors); + kobject_put(disk->part0.holder_dir); + kobject_put(disk->slave_dir); + } part_stat_set_all(&disk->part0, 0); disk->part0.stamp = 0; - - kobject_put(disk->part0.holder_dir); - kobject_put(disk->slave_dir); if (!sysfs_deprecated) sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); pm_runtime_set_memalloc_noio(disk_to_dev(disk), false); @@ -781,6 +798,10 @@ struct gendisk *get_gendisk(dev_t devt, int *partno) spin_unlock_bh(&ext_devt_lock); } + if (unlikely(disk->flags & GENHD_FL_HIDDEN)) { + put_disk(disk); + disk = NULL; + } return disk; } EXPORT_SYMBOL(get_gendisk); @@ -1024,6 +1045,15 @@ static ssize_t disk_removable_show(struct device *dev, (disk->flags & GENHD_FL_REMOVABLE ? 1 : 0)); } +static ssize_t disk_hidden_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gendisk *disk = dev_to_disk(dev); + + return sprintf(buf, "%d\n", + (disk->flags & GENHD_FL_HIDDEN ? 1 : 0)); +} + static ssize_t disk_ro_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1061,6 +1091,7 @@ static ssize_t disk_discard_alignment_show(struct device *dev, static DEVICE_ATTR(range, S_IRUGO, disk_range_show, NULL); static DEVICE_ATTR(ext_range, S_IRUGO, disk_ext_range_show, NULL); static DEVICE_ATTR(removable, S_IRUGO, disk_removable_show, NULL); +static DEVICE_ATTR(hidden, S_IRUGO, disk_hidden_show, NULL); static DEVICE_ATTR(ro, S_IRUGO, disk_ro_show, NULL); static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL); static DEVICE_ATTR(alignment_offset, S_IRUGO, disk_alignment_offset_show, NULL); @@ -1085,6 +1116,7 @@ static struct attribute *disk_attrs[] = { &dev_attr_range.attr, &dev_attr_ext_range.attr, &dev_attr_removable.attr, + &dev_attr_hidden.attr, &dev_attr_ro.attr, &dev_attr_size.attr, &dev_attr_alignment_offset.attr, diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 5c0ed5db33c2..93aae3476f58 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -140,6 +140,7 @@ struct hd_struct { #define GENHD_FL_NATIVE_CAPACITY 128 #define GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE 256 #define GENHD_FL_NO_PART_SCAN 512 +#define GENHD_FL_HIDDEN 1024 enum { DISK_EVENT_MEDIA_CHANGE = 1 << 0, /* media changed */ -- cgit v1.2.3-59-g8ed1b From ea435e1b9392a33deceaea2a16ebaa3397bead93 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 2 Nov 2017 21:29:54 +0300 Subject: block: add a poll_fn callback to struct request_queue That we we can also poll non blk-mq queues. Mostly needed for the NVMe multipath code, but could also be useful elsewhere. Signed-off-by: Christoph Hellwig Reviewed-by: Hannes Reinecke Signed-off-by: Jens Axboe --- block/blk-core.c | 11 +++++++++++ block/blk-mq.c | 14 +++++--------- drivers/nvme/target/io-cmd.c | 2 +- fs/block_dev.c | 4 ++-- fs/direct-io.c | 2 +- fs/iomap.c | 2 +- include/linux/blkdev.h | 4 +++- mm/page_io.c | 2 +- 8 files changed, 25 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/block/blk-core.c b/block/blk-core.c index 68cfe6780a9b..395bfb10d658 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -2321,6 +2321,17 @@ blk_qc_t submit_bio(struct bio *bio) } EXPORT_SYMBOL(submit_bio); +bool blk_poll(struct request_queue *q, blk_qc_t cookie) +{ + if (!q->poll_fn || !blk_qc_t_valid(cookie)) + return false; + + if (current->plug) + blk_flush_plug_list(current->plug, false); + return q->poll_fn(q, cookie); +} +EXPORT_SYMBOL_GPL(blk_poll); + /** * blk_cloned_rq_check_limits - Helper function to check a cloned request * for new the queue limits diff --git a/block/blk-mq.c b/block/blk-mq.c index e4d2490f4e7e..95ea5889b825 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -37,6 +37,7 @@ #include "blk-wbt.h" #include "blk-mq-sched.h" +static bool blk_mq_poll(struct request_queue *q, blk_qc_t cookie); static void blk_mq_poll_stats_start(struct request_queue *q); static void blk_mq_poll_stats_fn(struct blk_stat_callback *cb); @@ -2499,6 +2500,8 @@ struct request_queue *blk_mq_init_allocated_queue(struct blk_mq_tag_set *set, spin_lock_init(&q->requeue_lock); blk_queue_make_request(q, blk_mq_make_request); + if (q->mq_ops->poll) + q->poll_fn = blk_mq_poll; /* * Do this after blk_queue_make_request() overrides it... @@ -2961,20 +2964,14 @@ static bool __blk_mq_poll(struct blk_mq_hw_ctx *hctx, struct request *rq) return false; } -bool blk_mq_poll(struct request_queue *q, blk_qc_t cookie) +static bool blk_mq_poll(struct request_queue *q, blk_qc_t cookie) { struct blk_mq_hw_ctx *hctx; - struct blk_plug *plug; struct request *rq; - if (!q->mq_ops || !q->mq_ops->poll || !blk_qc_t_valid(cookie) || - !test_bit(QUEUE_FLAG_POLL, &q->queue_flags)) + if (!test_bit(QUEUE_FLAG_POLL, &q->queue_flags)) return false; - plug = current->plug; - if (plug) - blk_flush_plug_list(plug, false); - hctx = q->queue_hw_ctx[blk_qc_t_to_queue_num(cookie)]; if (!blk_qc_t_is_internal(cookie)) rq = blk_mq_tag_to_rq(hctx->tags, blk_qc_t_to_tag(cookie)); @@ -2992,7 +2989,6 @@ bool blk_mq_poll(struct request_queue *q, blk_qc_t cookie) return __blk_mq_poll(hctx, rq); } -EXPORT_SYMBOL_GPL(blk_mq_poll); static int __init blk_mq_init(void) { diff --git a/drivers/nvme/target/io-cmd.c b/drivers/nvme/target/io-cmd.c index 0d4c23dc4532..db632818777d 100644 --- a/drivers/nvme/target/io-cmd.c +++ b/drivers/nvme/target/io-cmd.c @@ -94,7 +94,7 @@ static void nvmet_execute_rw(struct nvmet_req *req) cookie = submit_bio(bio); - blk_mq_poll(bdev_get_queue(req->ns->bdev), cookie); + blk_poll(bdev_get_queue(req->ns->bdev), cookie); } static void nvmet_execute_flush(struct nvmet_req *req) diff --git a/fs/block_dev.c b/fs/block_dev.c index 07ddccd17801..4afa4d5ff969 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -237,7 +237,7 @@ __blkdev_direct_IO_simple(struct kiocb *iocb, struct iov_iter *iter, if (!READ_ONCE(bio.bi_private)) break; if (!(iocb->ki_flags & IOCB_HIPRI) || - !blk_mq_poll(bdev_get_queue(bdev), qc)) + !blk_poll(bdev_get_queue(bdev), qc)) io_schedule(); } __set_current_state(TASK_RUNNING); @@ -402,7 +402,7 @@ __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter, int nr_pages) break; if (!(iocb->ki_flags & IOCB_HIPRI) || - !blk_mq_poll(bdev_get_queue(bdev), qc)) + !blk_poll(bdev_get_queue(bdev), qc)) io_schedule(); } __set_current_state(TASK_RUNNING); diff --git a/fs/direct-io.c b/fs/direct-io.c index 62cf812ed0e5..d2bc339cb1e9 100644 --- a/fs/direct-io.c +++ b/fs/direct-io.c @@ -486,7 +486,7 @@ static struct bio *dio_await_one(struct dio *dio) dio->waiter = current; spin_unlock_irqrestore(&dio->bio_lock, flags); if (!(dio->iocb->ki_flags & IOCB_HIPRI) || - !blk_mq_poll(dio->bio_disk->queue, dio->bio_cookie)) + !blk_poll(dio->bio_disk->queue, dio->bio_cookie)) io_schedule(); /* wake up sets us TASK_RUNNING */ spin_lock_irqsave(&dio->bio_lock, flags); diff --git a/fs/iomap.c b/fs/iomap.c index 8194d30bdca0..4241bac905b1 100644 --- a/fs/iomap.c +++ b/fs/iomap.c @@ -1049,7 +1049,7 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter, if (!(iocb->ki_flags & IOCB_HIPRI) || !dio->submit.last_queue || - !blk_mq_poll(dio->submit.last_queue, + !blk_poll(dio->submit.last_queue, dio->submit.cookie)) io_schedule(); } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index fddda6a1f9b5..225617dd0a3f 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -266,6 +266,7 @@ struct blk_queue_ctx; typedef void (request_fn_proc) (struct request_queue *q); typedef blk_qc_t (make_request_fn) (struct request_queue *q, struct bio *bio); +typedef bool (poll_q_fn) (struct request_queue *q, blk_qc_t); typedef int (prep_rq_fn) (struct request_queue *, struct request *); typedef void (unprep_rq_fn) (struct request_queue *, struct request *); @@ -408,6 +409,7 @@ struct request_queue { request_fn_proc *request_fn; make_request_fn *make_request_fn; + poll_q_fn *poll_fn; prep_rq_fn *prep_rq_fn; unprep_rq_fn *unprep_rq_fn; softirq_done_fn *softirq_done_fn; @@ -975,7 +977,7 @@ extern void blk_execute_rq_nowait(struct request_queue *, struct gendisk *, int blk_status_to_errno(blk_status_t status); blk_status_t errno_to_blk_status(int errno); -bool blk_mq_poll(struct request_queue *q, blk_qc_t cookie); +bool blk_poll(struct request_queue *q, blk_qc_t cookie); static inline struct request_queue *bdev_get_queue(struct block_device *bdev) { diff --git a/mm/page_io.c b/mm/page_io.c index 21502d341a67..ff04de630c46 100644 --- a/mm/page_io.c +++ b/mm/page_io.c @@ -407,7 +407,7 @@ int swap_readpage(struct page *page, bool do_poll) if (!READ_ONCE(bio->bi_private)) break; - if (!blk_mq_poll(disk->queue, qc)) + if (!blk_poll(disk->queue, qc)) break; } __set_current_state(TASK_RUNNING); -- cgit v1.2.3-59-g8ed1b From 973b546451fdf11e518cc96d1b137af893a38db5 Mon Sep 17 00:00:00 2001 From: Lu Baolu Date: Fri, 3 Nov 2017 10:51:33 -0600 Subject: iommu/vt-d: Clear Page Request Overflow fault bit Currently Page Request Overflow bit in IOMMU Fault Status register is not cleared. Not clearing this bit would mean that any future page-request is going to be automatically dropped by IOMMU. Suggested-by: Ashok Raj Signed-off-by: Lu Baolu Signed-off-by: Alex Williamson --- drivers/iommu/dmar.c | 3 ++- include/linux/intel-iommu.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/iommu/dmar.c b/drivers/iommu/dmar.c index 1ea7cd537873..9a7ffd13c7f0 100644 --- a/drivers/iommu/dmar.c +++ b/drivers/iommu/dmar.c @@ -1679,7 +1679,8 @@ irqreturn_t dmar_fault(int irq, void *dev_id) raw_spin_lock_irqsave(&iommu->register_lock, flag); } - writel(DMA_FSTS_PFO | DMA_FSTS_PPF, iommu->reg + DMAR_FSTS_REG); + writel(DMA_FSTS_PFO | DMA_FSTS_PPF | DMA_FSTS_PRO, + iommu->reg + DMAR_FSTS_REG); unlock_exit: raw_spin_unlock_irqrestore(&iommu->register_lock, flag); diff --git a/include/linux/intel-iommu.h b/include/linux/intel-iommu.h index 485a5b48f038..f3274d9f46a2 100644 --- a/include/linux/intel-iommu.h +++ b/include/linux/intel-iommu.h @@ -212,6 +212,7 @@ #define DMA_FSTS_IQE (1 << 4) #define DMA_FSTS_ICE (1 << 5) #define DMA_FSTS_ITE (1 << 6) +#define DMA_FSTS_PRO (1 << 7) #define dma_fsts_fault_record_index(s) (((s) >> 8) & 0xff) /* FRCD_REG, 32 bits access */ -- cgit v1.2.3-59-g8ed1b From ec4001c3f29ebb3d4147aaec7be9c687ddadb7c8 Mon Sep 17 00:00:00 2001 From: Paul Durrant Date: Fri, 3 Nov 2017 17:04:11 +0000 Subject: xen: support priv-mapping in an HVM tools domain If the domain has XENFEAT_auto_translated_physmap then use of the PV- specific HYPERVISOR_mmu_update hypercall is clearly incorrect. This patch adds checks in xen_remap_domain_gfn_array() and xen_unmap_domain_gfn_array() which call through to the approprate xlate_mmu function if the feature is present. A check is also added to xen_remap_domain_gfn_range() to fail with -EOPNOTSUPP since this should not be used in an HVM tools domain. Signed-off-by: Paul Durrant Reviewed-by: Boris Ostrovsky Signed-off-by: Boris Ostrovsky --- arch/x86/xen/mmu.c | 14 ++++++++++++-- include/xen/xen-ops.h | 24 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c index 3e15345abfe7..d33e7dbe3129 100644 --- a/arch/x86/xen/mmu.c +++ b/arch/x86/xen/mmu.c @@ -172,6 +172,9 @@ int xen_remap_domain_gfn_range(struct vm_area_struct *vma, pgprot_t prot, unsigned domid, struct page **pages) { + if (xen_feature(XENFEAT_auto_translated_physmap)) + return -EOPNOTSUPP; + return do_remap_gfn(vma, addr, &gfn, nr, NULL, prot, domid, pages); } EXPORT_SYMBOL_GPL(xen_remap_domain_gfn_range); @@ -182,6 +185,10 @@ int xen_remap_domain_gfn_array(struct vm_area_struct *vma, int *err_ptr, pgprot_t prot, unsigned domid, struct page **pages) { + if (xen_feature(XENFEAT_auto_translated_physmap)) + return xen_xlate_remap_gfn_array(vma, addr, gfn, nr, err_ptr, + prot, domid, pages); + /* We BUG_ON because it's a programmer error to pass a NULL err_ptr, * and the consequences later is quite hard to detect what the actual * cause of "wrong memory was mapped in". @@ -193,9 +200,12 @@ EXPORT_SYMBOL_GPL(xen_remap_domain_gfn_array); /* Returns: 0 success */ int xen_unmap_domain_gfn_range(struct vm_area_struct *vma, - int numpgs, struct page **pages) + int nr, struct page **pages) { - if (!pages || !xen_feature(XENFEAT_auto_translated_physmap)) + if (xen_feature(XENFEAT_auto_translated_physmap)) + return xen_xlate_unmap_gfn_range(vma, nr, pages); + + if (!pages) return 0; return -EINVAL; diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 09072271f122..c278b21cad39 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -104,6 +104,8 @@ int xen_remap_domain_gfn_range(struct vm_area_struct *vma, struct page **pages); int xen_unmap_domain_gfn_range(struct vm_area_struct *vma, int numpgs, struct page **pages); + +#ifdef CONFIG_XEN_AUTO_XLATE int xen_xlate_remap_gfn_array(struct vm_area_struct *vma, unsigned long addr, xen_pfn_t *gfn, int nr, @@ -112,6 +114,28 @@ int xen_xlate_remap_gfn_array(struct vm_area_struct *vma, struct page **pages); int xen_xlate_unmap_gfn_range(struct vm_area_struct *vma, int nr, struct page **pages); +#else +/* + * These two functions are called from arch/x86/xen/mmu.c and so stubs + * are needed for a configuration not specifying CONFIG_XEN_AUTO_XLATE. + */ +static inline int xen_xlate_remap_gfn_array(struct vm_area_struct *vma, + unsigned long addr, + xen_pfn_t *gfn, int nr, + int *err_ptr, pgprot_t prot, + unsigned int domid, + struct page **pages) +{ + return -EOPNOTSUPP; +} + +static inline int xen_xlate_unmap_gfn_range(struct vm_area_struct *vma, + int nr, struct page **pages) +{ + return -EOPNOTSUPP; +} +#endif + int xen_xlate_map_ballooned_pages(xen_pfn_t **pfns, void **vaddr, unsigned long nr_grant_frames); -- cgit v1.2.3-59-g8ed1b From 722c856d46c6ca74a246b54a72f14751fec01aae Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 1 Nov 2017 14:25:23 -0500 Subject: platform/x86: wmi: Add new method wmidev_evaluate_method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drivers properly using the wmibus can pass their wmi_device pointer rather than the GUID back to the WMI bus to evaluate the proper methods. Any "new" drivers added that use the WMI bus should use this rather than the old wmi_evaluate_method that would take the GUID. Signed-off-by: Mario Limonciello Reviewed-by: Edward O'Callaghan Reviewed-by: Pali Rohár Signed-off-by: Darren Hart (VMware) --- drivers/platform/x86/wmi.c | 28 ++++++++++++++++++++++++---- include/linux/wmi.h | 6 ++++++ 2 files changed, 30 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 7a05843aff19..4d73a87c2ddf 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -200,6 +200,28 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) */ acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) +{ + struct wmi_block *wblock = NULL; + + if (!find_guid(guid_string, &wblock)) + return AE_ERROR; + return wmidev_evaluate_method(&wblock->dev, instance, method_id, + in, out); +} +EXPORT_SYMBOL_GPL(wmi_evaluate_method); + +/** + * wmidev_evaluate_method - Evaluate a WMI method + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * @method_id: Method ID to call + * &in: Buffer containing input for the method call + * &out: Empty buffer to return the method results + * + * Call an ACPI-WMI method + */ +acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, + u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) { struct guid_block *block = NULL; struct wmi_block *wblock = NULL; @@ -209,9 +231,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) union acpi_object params[3]; char method[5] = "WM"; - if (!find_guid(guid_string, &wblock)) - return AE_ERROR; - + wblock = container_of(wdev, struct wmi_block, dev); block = &wblock->gblock; handle = wblock->acpi_device->handle; @@ -246,7 +266,7 @@ u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out) return status; } -EXPORT_SYMBOL_GPL(wmi_evaluate_method); +EXPORT_SYMBOL_GPL(wmidev_evaluate_method); static acpi_status __query_block(struct wmi_block *wblock, u8 instance, struct acpi_buffer *out) diff --git a/include/linux/wmi.h b/include/linux/wmi.h index cd0d7734dc49..2cd10c3b89e9 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -26,6 +26,12 @@ struct wmi_device { bool setable; }; +/* evaluate the ACPI method associated with this device */ +extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev, + u8 instance, u32 method_id, + const struct acpi_buffer *in, + struct acpi_buffer *out); + /* Caller must kfree the result. */ extern union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance); -- cgit v1.2.3-59-g8ed1b From f97e058cfe8032504e310bd5c20e35d640ef2858 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 1 Nov 2017 14:25:28 -0500 Subject: platform/x86: wmi: Don't allow drivers to get each other's GUIDs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only driver using this was dell-wmi, and it really was a hack. The driver was getting a data attribute from another driver and this type of action should not be encouraged. Rather drivers that need to interact with one another should pass data back and forth via exported functions. Signed-off-by: Mario Limonciello Reviewed-by: Edward O'Callaghan Reviewed-by: Pali Rohár Signed-off-by: Darren Hart (VMware) --- drivers/platform/x86/wmi.c | 17 ----------------- include/linux/wmi.h | 4 ---- 2 files changed, 21 deletions(-) (limited to 'include') diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 4d73a87c2ddf..bcb41c1c7f52 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -368,23 +368,6 @@ union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) } EXPORT_SYMBOL_GPL(wmidev_block_query); -struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev, - const char *guid_string) -{ - struct wmi_block *this_wb = container_of(wdev, struct wmi_block, dev); - struct wmi_block *other_wb; - - if (!find_guid(guid_string, &other_wb)) - return NULL; - - if (other_wb->acpi_device != this_wb->acpi_device) - return NULL; - - get_device(&other_wb->dev.dev); - return &other_wb->dev; -} -EXPORT_SYMBOL_GPL(wmidev_get_other_guid); - /** * wmi_set_block - Write to a WMI block * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 2cd10c3b89e9..ddee427e0721 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -36,10 +36,6 @@ extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev, extern union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance); -/* Gets another device on the same bus. Caller must put_device the result. */ -extern struct wmi_device *wmidev_get_other_guid(struct wmi_device *wdev, - const char *guid_string); - struct wmi_device_id { const char *guid_string; }; -- cgit v1.2.3-59-g8ed1b From 44b6b7661132b1b0e5fd3147ded66f1e4a817ca9 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 1 Nov 2017 14:25:35 -0500 Subject: platform/x86: wmi: create userspace interface for drivers For WMI operations that are only Set or Query readable and writable sysfs attributes created by WMI vendor drivers or the bus driver makes sense. For other WMI operations that are run on Method, there needs to be a way to guarantee to userspace that the results from the method call belong to the data request to the method call. Sysfs attributes don't work well in this scenario because two userspace processes may be competing at reading/writing an attribute and step on each other's data. When a WMI vendor driver declares a callback method in the wmi_driver the WMI bus driver will create a character device that maps to that function. This callback method will be responsible for filtering invalid requests and performing the actual call. That character device will correspond to this path: /dev/wmi/$driver Performing read() on this character device will provide the size of the buffer that the character device needs to perform calls. This buffer size can be set by vendor drivers through a new symbol or when MOF parsing is available by the MOF. Performing ioctl() on this character device will be interpretd by the WMI bus driver. It will perform sanity tests for size of data, test them for a valid instance, copy the data from userspace and pass iton to the vendor driver to further process and run. This creates an implicit policy that each driver will only be allowed a single character device. If a module matches multiple GUID's, the wmi_devices will need to be all handled by the same wmi_driver. The WMI vendor drivers will be responsible for managing inappropriate access to this character device and proper locking on data used by it. When a WMI vendor driver is unloaded the WMI bus driver will clean up the character device and any memory allocated for the call. Signed-off-by: Mario Limonciello Reviewed-by: Edward O'Callaghan Signed-off-by: Darren Hart (VMware) --- MAINTAINERS | 1 + drivers/platform/x86/wmi.c | 189 ++++++++++++++++++++++++++++++++++++++++++++- include/linux/wmi.h | 5 ++ include/uapi/linux/wmi.h | 26 +++++++ 4 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 include/uapi/linux/wmi.h (limited to 'include') diff --git a/MAINTAINERS b/MAINTAINERS index aede236d10f1..3af07502220a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -384,6 +384,7 @@ ACPI WMI DRIVER L: platform-driver-x86@vger.kernel.org S: Orphan F: drivers/platform/x86/wmi.c +F: include/uapi/linux/wmi.h AD1889 ALSA SOUND DRIVER M: Thibaut Varene diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index bcb41c1c7f52..8c31ed4f0e1b 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -38,12 +38,15 @@ #include #include #include +#include #include #include #include #include +#include #include #include +#include ACPI_MODULE_NAME("wmi"); MODULE_AUTHOR("Carlos Corbacho"); @@ -69,9 +72,12 @@ struct wmi_block { struct wmi_device dev; struct list_head list; struct guid_block gblock; + struct miscdevice char_dev; + struct mutex char_mutex; struct acpi_device *acpi_device; wmi_notify_handler handler; void *handler_data; + u64 req_buf_size; bool read_takes_no_args; }; @@ -188,6 +194,25 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable) /* * Exported WMI functions */ + +/** + * set_required_buffer_size - Sets the buffer size needed for performing IOCTL + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * + * Allocates memory needed for buffer, stores the buffer size in that memory + */ +int set_required_buffer_size(struct wmi_device *wdev, u64 length) +{ + struct wmi_block *wblock; + + wblock = container_of(wdev, struct wmi_block, dev); + wblock->req_buf_size = length; + + return 0; +} +EXPORT_SYMBOL_GPL(set_required_buffer_size); + /** * wmi_evaluate_method - Evaluate a WMI method * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -764,6 +789,111 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) return 0; } +static int wmi_char_open(struct inode *inode, struct file *filp) +{ + const char *driver_name = filp->f_path.dentry->d_iname; + struct wmi_block *wblock = NULL; + struct wmi_block *next = NULL; + + list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { + if (!wblock->dev.dev.driver) + continue; + if (strcmp(driver_name, wblock->dev.dev.driver->name) == 0) { + filp->private_data = wblock; + break; + } + } + + if (!filp->private_data) + return -ENODEV; + + return nonseekable_open(inode, filp); +} + +static ssize_t wmi_char_read(struct file *filp, char __user *buffer, + size_t length, loff_t *offset) +{ + struct wmi_block *wblock = filp->private_data; + + return simple_read_from_buffer(buffer, length, offset, + &wblock->req_buf_size, + sizeof(wblock->req_buf_size)); +} + +static long wmi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct wmi_ioctl_buffer __user *input = + (struct wmi_ioctl_buffer __user *) arg; + struct wmi_block *wblock = filp->private_data; + struct wmi_ioctl_buffer *buf = NULL; + struct wmi_driver *wdriver = NULL; + int ret; + + if (_IOC_TYPE(cmd) != WMI_IOC) + return -ENOTTY; + + /* make sure we're not calling a higher instance than exists*/ + if (_IOC_NR(cmd) >= wblock->gblock.instance_count) + return -EINVAL; + + mutex_lock(&wblock->char_mutex); + buf = wblock->handler_data; + if (get_user(buf->length, &input->length)) { + dev_dbg(&wblock->dev.dev, "Read length from user failed\n"); + ret = -EFAULT; + goto out_ioctl; + } + /* if it's too small, abort */ + if (buf->length < wblock->req_buf_size) { + dev_err(&wblock->dev.dev, + "Buffer %lld too small, need at least %lld\n", + buf->length, wblock->req_buf_size); + ret = -EINVAL; + goto out_ioctl; + } + /* if it's too big, warn, driver will only use what is needed */ + if (buf->length > wblock->req_buf_size) + dev_warn(&wblock->dev.dev, + "Buffer %lld is bigger than required %lld\n", + buf->length, wblock->req_buf_size); + + /* copy the structure from userspace */ + if (copy_from_user(buf, input, wblock->req_buf_size)) { + dev_dbg(&wblock->dev.dev, "Copy %llu from user failed\n", + wblock->req_buf_size); + ret = -EFAULT; + goto out_ioctl; + } + + /* let the driver do any filtering and do the call */ + wdriver = container_of(wblock->dev.dev.driver, + struct wmi_driver, driver); + if (!try_module_get(wdriver->driver.owner)) + return -EBUSY; + ret = wdriver->filter_callback(&wblock->dev, cmd, buf); + module_put(wdriver->driver.owner); + if (ret) + goto out_ioctl; + + /* return the result (only up to our internal buffer size) */ + if (copy_to_user(input, buf, wblock->req_buf_size)) { + dev_dbg(&wblock->dev.dev, "Copy %llu to user failed\n", + wblock->req_buf_size); + ret = -EFAULT; + } + +out_ioctl: + mutex_unlock(&wblock->char_mutex); + return ret; +} + +static const struct file_operations wmi_fops = { + .owner = THIS_MODULE, + .read = wmi_char_read, + .open = wmi_char_open, + .unlocked_ioctl = wmi_ioctl, + .compat_ioctl = wmi_ioctl, +}; static int wmi_dev_probe(struct device *dev) { @@ -771,16 +901,63 @@ static int wmi_dev_probe(struct device *dev) struct wmi_driver *wdriver = container_of(dev->driver, struct wmi_driver, driver); int ret = 0; + int count; + char *buf; if (ACPI_FAILURE(wmi_method_enable(wblock, 1))) dev_warn(dev, "failed to enable device -- probing anyway\n"); if (wdriver->probe) { ret = wdriver->probe(dev_to_wdev(dev)); - if (ret != 0 && ACPI_FAILURE(wmi_method_enable(wblock, 0))) - dev_warn(dev, "failed to disable device\n"); + if (ret != 0) + goto probe_failure; } + /* driver wants a character device made */ + if (wdriver->filter_callback) { + /* check that required buffer size declared by driver or MOF */ + if (!wblock->req_buf_size) { + dev_err(&wblock->dev.dev, + "Required buffer size not set\n"); + ret = -EINVAL; + goto probe_failure; + } + + count = get_order(wblock->req_buf_size); + wblock->handler_data = (void *)__get_free_pages(GFP_KERNEL, + count); + if (!wblock->handler_data) { + ret = -ENOMEM; + goto probe_failure; + } + + buf = kmalloc(strlen(wdriver->driver.name) + 4, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto probe_string_failure; + } + sprintf(buf, "wmi/%s", wdriver->driver.name); + wblock->char_dev.minor = MISC_DYNAMIC_MINOR; + wblock->char_dev.name = buf; + wblock->char_dev.fops = &wmi_fops; + wblock->char_dev.mode = 0444; + ret = misc_register(&wblock->char_dev); + if (ret) { + dev_warn(dev, "failed to register char dev: %d", ret); + ret = -ENOMEM; + goto probe_misc_failure; + } + } + + return 0; + +probe_misc_failure: + kfree(buf); +probe_string_failure: + kfree(wblock->handler_data); +probe_failure: + if (ACPI_FAILURE(wmi_method_enable(wblock, 0))) + dev_warn(dev, "failed to disable device\n"); return ret; } @@ -791,6 +968,13 @@ static int wmi_dev_remove(struct device *dev) container_of(dev->driver, struct wmi_driver, driver); int ret = 0; + if (wdriver->filter_callback) { + misc_deregister(&wblock->char_dev); + kfree(wblock->char_dev.name); + free_pages((unsigned long)wblock->handler_data, + get_order(wblock->req_buf_size)); + } + if (wdriver->remove) ret = wdriver->remove(dev_to_wdev(dev)); @@ -847,6 +1031,7 @@ static int wmi_create_device(struct device *wmi_bus_dev, if (gblock->flags & ACPI_WMI_METHOD) { wblock->dev.dev.type = &wmi_type_method; + mutex_init(&wblock->char_mutex); goto out_init; } diff --git a/include/linux/wmi.h b/include/linux/wmi.h index ddee427e0721..4757cb5077e5 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -18,6 +18,7 @@ #include #include +#include struct wmi_device { struct device dev; @@ -36,6 +37,8 @@ extern acpi_status wmidev_evaluate_method(struct wmi_device *wdev, extern union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance); +extern int set_required_buffer_size(struct wmi_device *wdev, u64 length); + struct wmi_device_id { const char *guid_string; }; @@ -47,6 +50,8 @@ struct wmi_driver { int (*probe)(struct wmi_device *wdev); int (*remove)(struct wmi_device *wdev); void (*notify)(struct wmi_device *device, union acpi_object *data); + long (*filter_callback)(struct wmi_device *wdev, unsigned int cmd, + struct wmi_ioctl_buffer *arg); }; extern int __must_check __wmi_driver_register(struct wmi_driver *driver, diff --git a/include/uapi/linux/wmi.h b/include/uapi/linux/wmi.h new file mode 100644 index 000000000000..7e52350ac9b3 --- /dev/null +++ b/include/uapi/linux/wmi.h @@ -0,0 +1,26 @@ +/* + * User API methods for ACPI-WMI mapping driver + * + * Copyright (C) 2017 Dell, Inc. + * + * 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. + */ +#ifndef _UAPI_LINUX_WMI_H +#define _UAPI_LINUX_WMI_H + +#include + +/* WMI bus will filter all WMI vendor driver requests through this IOC */ +#define WMI_IOC 'W' + +/* All ioctl requests through WMI should declare their size followed by + * relevant data objects + */ +struct wmi_ioctl_buffer { + __u64 length; + __u8 data[]; +}; + +#endif -- cgit v1.2.3-59-g8ed1b From f2645fa317b8905b8934f06a0601d5b7fa66aba0 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 1 Nov 2017 14:25:36 -0500 Subject: platform/x86: dell-smbios-wmi: introduce userspace interface It's important for the driver to provide a R/W ioctl to ensure that two competing userspace processes don't race to provide or read each others data. This userspace character device will be used to perform SMBIOS calls from any applications. It provides an ioctl that will allow passing the WMI calling interface buffer between userspace and kernel space. This character device is intended to deprecate the dcdbas kernel module and the interface that it provides to userspace. To perform an SMBIOS IOCTL call using the character device userspace will perform a read() on the the character device. The WMI bus will provide a u64 variable containing the necessary size of the IOCTL buffer. The API for interacting with this interface is defined in documentation as well as the WMI uapi header provides the format of the structures. Not all userspace requests will be accepted. The dell-smbios filtering functionality will be used to prevent access to certain tokens and calls. All whitelisted commands and tokens are now shared out to userspace so applications don't need to define them in their own headers. Signed-off-by: Mario Limonciello Reviewed-by: Edward O'Callaghan Signed-off-by: Darren Hart (VMware) --- Documentation/ABI/testing/dell-smbios-wmi | 41 +++++++++++++++++++++++ drivers/platform/x86/dell-smbios-wmi.c | 54 ++++++++++++++++++++++++------- drivers/platform/x86/dell-smbios.h | 32 ++---------------- include/uapi/linux/wmi.h | 47 +++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 41 deletions(-) create mode 100644 Documentation/ABI/testing/dell-smbios-wmi (limited to 'include') diff --git a/Documentation/ABI/testing/dell-smbios-wmi b/Documentation/ABI/testing/dell-smbios-wmi new file mode 100644 index 000000000000..fc919ce16008 --- /dev/null +++ b/Documentation/ABI/testing/dell-smbios-wmi @@ -0,0 +1,41 @@ +What: /dev/wmi/dell-smbios +Date: November 2017 +KernelVersion: 4.15 +Contact: "Mario Limonciello" +Description: + Perform SMBIOS calls on supported Dell machines. + through the Dell ACPI-WMI interface. + + IOCTL's and buffer formats are defined in: + + + 1) To perform an SMBIOS call from userspace, you'll need to + first determine the minimum size of the calling interface + buffer for your machine. + Platforms that contain larger buffers can return larger + objects from the system firmware. + Commonly this size is either 4k or 32k. + + To determine the size of the buffer read() a u64 dword from + the WMI character device /dev/wmi/dell-smbios. + + 2) After you've determined the minimum size of the calling + interface buffer, you can allocate a structure that represents + the structure documented above. + + 3) In the 'length' object store the size of the buffer you + determined above and allocated. + + 4) In this buffer object, prepare as necessary for the SMBIOS + call you're interested in. Typically SMBIOS buffers have + "class", "select", and "input" defined to values that coincide + with the data you are interested in. + Documenting class/select/input values is outside of the scope + of this documentation. Check with the libsmbios project for + further documentation on these values. + + 6) Run the call by using ioctl() as described in the header. + + 7) The output will be returned in the buffer object. + + 8) Be sure to free up your allocated object. diff --git a/drivers/platform/x86/dell-smbios-wmi.c b/drivers/platform/x86/dell-smbios-wmi.c index b31f457e58c3..35c13815b24c 100644 --- a/drivers/platform/x86/dell-smbios-wmi.c +++ b/drivers/platform/x86/dell-smbios-wmi.c @@ -30,17 +30,6 @@ struct misc_bios_flags_structure { #define DELL_WMI_SMBIOS_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" -struct dell_wmi_extensions { - __u32 argattrib; - __u32 blength; - __u8 data[]; -} __packed; - -struct dell_wmi_smbios_buffer { - struct calling_interface_buffer std; - struct dell_wmi_extensions ext; -} __packed; - struct wmi_smbios_priv { struct dell_wmi_smbios_buffer *buf; struct list_head list; @@ -117,6 +106,42 @@ int dell_smbios_wmi_call(struct calling_interface_buffer *buffer) return ret; } +static long dell_smbios_wmi_filter(struct wmi_device *wdev, unsigned int cmd, + struct wmi_ioctl_buffer *arg) +{ + struct wmi_smbios_priv *priv; + int ret = 0; + + switch (cmd) { + case DELL_WMI_SMBIOS_CMD: + mutex_lock(&call_mutex); + priv = dev_get_drvdata(&wdev->dev); + if (!priv) { + ret = -ENODEV; + goto fail_smbios_cmd; + } + memcpy(priv->buf, arg, priv->req_buf_size); + if (dell_smbios_call_filter(&wdev->dev, &priv->buf->std)) { + dev_err(&wdev->dev, "Invalid call %d/%d:%8x\n", + priv->buf->std.cmd_class, + priv->buf->std.cmd_select, + priv->buf->std.input[0]); + ret = -EFAULT; + goto fail_smbios_cmd; + } + ret = run_smbios_call(priv->wdev); + if (ret) + goto fail_smbios_cmd; + memcpy(arg, priv->buf, priv->req_buf_size); +fail_smbios_cmd: + mutex_unlock(&call_mutex); + break; + default: + ret = -ENOIOCTLCMD; + } + return ret; +} + static int dell_smbios_wmi_probe(struct wmi_device *wdev) { struct wmi_smbios_priv *priv; @@ -135,6 +160,12 @@ static int dell_smbios_wmi_probe(struct wmi_device *wdev) if (!dell_wmi_get_size(&priv->req_buf_size)) return -EPROBE_DEFER; + /* add in the length object we will use internally with ioctl */ + priv->req_buf_size += sizeof(u64); + ret = set_required_buffer_size(wdev, priv->req_buf_size); + if (ret) + return ret; + count = get_order(priv->req_buf_size); priv->buf = (void *)__get_free_pages(GFP_KERNEL, count); if (!priv->buf) @@ -210,6 +241,7 @@ static struct wmi_driver dell_smbios_wmi_driver = { .probe = dell_smbios_wmi_probe, .remove = dell_smbios_wmi_remove, .id_table = dell_smbios_wmi_id_table, + .filter_callback = dell_smbios_wmi_filter, }; static int __init init_dell_smbios_wmi(void) diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h index 91e8004d48ba..138d478d9adc 100644 --- a/drivers/platform/x86/dell-smbios.h +++ b/drivers/platform/x86/dell-smbios.h @@ -17,23 +17,11 @@ #define _DELL_SMBIOS_H_ #include +#include -/* Classes and selects used in kernel drivers */ -#define CLASS_TOKEN_READ 0 -#define CLASS_TOKEN_WRITE 1 -#define SELECT_TOKEN_STD 0 -#define SELECT_TOKEN_BAT 1 -#define SELECT_TOKEN_AC 2 +/* Classes and selects used only in kernel drivers */ #define CLASS_KBD_BACKLIGHT 4 #define SELECT_KBD_BACKLIGHT 11 -#define CLASS_FLASH_INTERFACE 7 -#define SELECT_FLASH_INTERFACE 3 -#define CLASS_ADMIN_PROP 10 -#define SELECT_ADMIN_PROP 3 -#define CLASS_INFO 17 -#define SELECT_RFKILL 11 -#define SELECT_APP_REGISTRATION 3 -#define SELECT_DOCK 22 /* Tokens used in kernel drivers, any of these * should be filtered from userspace access @@ -50,24 +38,8 @@ #define GLOBAL_MIC_MUTE_ENABLE 0x0364 #define GLOBAL_MIC_MUTE_DISABLE 0x0365 -/* tokens whitelisted to userspace use */ -#define CAPSULE_EN_TOKEN 0x0461 -#define CAPSULE_DIS_TOKEN 0x0462 -#define WSMT_EN_TOKEN 0x04EC -#define WSMT_DIS_TOKEN 0x04ED - struct notifier_block; -/* This structure will be modified by the firmware when we enter - * system management mode, hence the volatiles */ - -struct calling_interface_buffer { - u16 cmd_class; - u16 cmd_select; - volatile u32 input[4]; - volatile u32 output[4]; -} __packed; - struct calling_interface_token { u16 tokenID; u16 location; diff --git a/include/uapi/linux/wmi.h b/include/uapi/linux/wmi.h index 7e52350ac9b3..7a92e9e3d1c0 100644 --- a/include/uapi/linux/wmi.h +++ b/include/uapi/linux/wmi.h @@ -10,6 +10,7 @@ #ifndef _UAPI_LINUX_WMI_H #define _UAPI_LINUX_WMI_H +#include #include /* WMI bus will filter all WMI vendor driver requests through this IOC */ @@ -23,4 +24,50 @@ struct wmi_ioctl_buffer { __u8 data[]; }; +/* This structure may be modified by the firmware when we enter + * system management mode through SMM, hence the volatiles + */ +struct calling_interface_buffer { + __u16 cmd_class; + __u16 cmd_select; + volatile __u32 input[4]; + volatile __u32 output[4]; +} __packed; + +struct dell_wmi_extensions { + __u32 argattrib; + __u32 blength; + __u8 data[]; +} __packed; + +struct dell_wmi_smbios_buffer { + __u64 length; + struct calling_interface_buffer std; + struct dell_wmi_extensions ext; +} __packed; + +/* Whitelisted smbios class/select commands */ +#define CLASS_TOKEN_READ 0 +#define CLASS_TOKEN_WRITE 1 +#define SELECT_TOKEN_STD 0 +#define SELECT_TOKEN_BAT 1 +#define SELECT_TOKEN_AC 2 +#define CLASS_FLASH_INTERFACE 7 +#define SELECT_FLASH_INTERFACE 3 +#define CLASS_ADMIN_PROP 10 +#define SELECT_ADMIN_PROP 3 +#define CLASS_INFO 17 +#define SELECT_RFKILL 11 +#define SELECT_APP_REGISTRATION 3 +#define SELECT_DOCK 22 + +/* whitelisted tokens */ +#define CAPSULE_EN_TOKEN 0x0461 +#define CAPSULE_DIS_TOKEN 0x0462 +#define WSMT_EN_TOKEN 0x04EC +#define WSMT_DIS_TOKEN 0x04ED + +/* Dell SMBIOS calling IOCTL command used by dell-smbios-wmi */ +#define DELL_WMI_SMBIOS_CMD _IOWR(WMI_IOC, 0, struct dell_wmi_smbios_buffer) + #endif -- cgit v1.2.3-59-g8ed1b From 5fd54ace4721fc5ce2bb5aef6318fcf17f421460 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 3 Nov 2017 11:28:30 +0100 Subject: USB: add SPDX identifiers to all remaining files in drivers/usb/ It's good to have SPDX identifiers in all files to make it easier to audit the kernel tree for correct licenses. Update the drivers/usb/ and include/linux/usb* files with the correct SPDX license identifier based on the license text in the file itself. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This work is based on a script and data from Thomas Gleixner, Philippe Ombredanne, and Kate Stewart. Cc: Thomas Gleixner Cc: Kate Stewart Cc: Philippe Ombredanne Signed-off-by: Greg Kroah-Hartman Acked-by: Felipe Balbi Acked-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/atm/cxacru.c | 1 + drivers/usb/atm/speedtch.c | 1 + drivers/usb/atm/ueagle-atm.c | 1 + drivers/usb/atm/usbatm.c | 1 + drivers/usb/atm/usbatm.h | 1 + drivers/usb/atm/xusbatm.c | 1 + drivers/usb/c67x00/c67x00-drv.c | 1 + drivers/usb/c67x00/c67x00-hcd.c | 1 + drivers/usb/c67x00/c67x00-hcd.h | 1 + drivers/usb/c67x00/c67x00-ll-hpi.c | 1 + drivers/usb/c67x00/c67x00-sched.c | 1 + drivers/usb/c67x00/c67x00.h | 1 + drivers/usb/chipidea/bits.h | 1 + drivers/usb/chipidea/ci.h | 1 + drivers/usb/chipidea/ci_hdrc_imx.c | 1 + drivers/usb/chipidea/ci_hdrc_imx.h | 1 + drivers/usb/chipidea/ci_hdrc_msm.c | 1 + drivers/usb/chipidea/ci_hdrc_pci.c | 1 + drivers/usb/chipidea/ci_hdrc_usb2.c | 1 + drivers/usb/chipidea/ci_hdrc_zevio.c | 1 + drivers/usb/chipidea/core.c | 1 + drivers/usb/chipidea/host.c | 1 + drivers/usb/chipidea/otg.c | 1 + drivers/usb/chipidea/otg.h | 1 + drivers/usb/chipidea/otg_fsm.c | 1 + drivers/usb/chipidea/otg_fsm.h | 1 + drivers/usb/chipidea/udc.c | 1 + drivers/usb/chipidea/udc.h | 1 + drivers/usb/chipidea/ulpi.c | 1 + drivers/usb/chipidea/usbmisc_imx.c | 1 + drivers/usb/class/cdc-acm.c | 1 + drivers/usb/class/cdc-wdm.c | 1 + drivers/usb/class/usblp.c | 1 + drivers/usb/class/usbtmc.c | 1 + drivers/usb/common/common.c | 1 + drivers/usb/common/led.c | 1 + drivers/usb/common/ulpi.c | 1 + drivers/usb/common/usb-otg-fsm.c | 1 + drivers/usb/core/devices.c | 1 + drivers/usb/core/devio.c | 1 + drivers/usb/core/hcd-pci.c | 1 + drivers/usb/core/hcd.c | 1 + drivers/usb/core/hub.h | 1 + drivers/usb/core/ledtrig-usbport.c | 1 + drivers/usb/core/of.c | 1 + drivers/usb/core/otg_whitelist.h | 1 + drivers/usb/core/port.c | 1 + drivers/usb/core/quirks.c | 1 + drivers/usb/core/usb-acpi.c | 1 + drivers/usb/dwc2/core.c | 1 + drivers/usb/dwc2/core.h | 1 + drivers/usb/dwc2/core_intr.c | 1 + drivers/usb/dwc2/debug.h | 1 + drivers/usb/dwc2/debugfs.c | 1 + drivers/usb/dwc2/gadget.c | 1 + drivers/usb/dwc2/hcd.c | 1 + drivers/usb/dwc2/hcd.h | 1 + drivers/usb/dwc2/hcd_ddma.c | 1 + drivers/usb/dwc2/hcd_intr.c | 1 + drivers/usb/dwc2/hcd_queue.c | 1 + drivers/usb/dwc2/hw.h | 1 + drivers/usb/dwc2/params.c | 1 + drivers/usb/dwc2/pci.c | 1 + drivers/usb/dwc2/platform.c | 1 + drivers/usb/dwc3/core.c | 1 + drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/debug.h | 1 + drivers/usb/dwc3/debugfs.c | 1 + drivers/usb/dwc3/drd.c | 1 + drivers/usb/dwc3/dwc3-exynos.c | 1 + drivers/usb/dwc3/dwc3-keystone.c | 1 + drivers/usb/dwc3/dwc3-of-simple.c | 1 + drivers/usb/dwc3/dwc3-omap.c | 1 + drivers/usb/dwc3/dwc3-pci.c | 1 + drivers/usb/dwc3/dwc3-st.c | 1 + drivers/usb/dwc3/ep0.c | 1 + drivers/usb/dwc3/gadget.c | 1 + drivers/usb/dwc3/gadget.h | 1 + drivers/usb/dwc3/host.c | 1 + drivers/usb/dwc3/io.h | 1 + drivers/usb/dwc3/trace.c | 1 + drivers/usb/dwc3/trace.h | 1 + drivers/usb/dwc3/ulpi.c | 1 + drivers/usb/early/ehci-dbgp.c | 1 + drivers/usb/early/xhci-dbc.c | 1 + drivers/usb/early/xhci-dbc.h | 1 + drivers/usb/gadget/composite.c | 1 + drivers/usb/gadget/config.c | 1 + drivers/usb/gadget/configfs.c | 1 + drivers/usb/gadget/epautoconf.c | 1 + drivers/usb/gadget/function/f_acm.c | 1 + drivers/usb/gadget/function/f_ecm.c | 1 + drivers/usb/gadget/function/f_eem.c | 1 + drivers/usb/gadget/function/f_fs.c | 1 + drivers/usb/gadget/function/f_hid.c | 1 + drivers/usb/gadget/function/f_loopback.c | 1 + drivers/usb/gadget/function/f_mass_storage.c | 1 + drivers/usb/gadget/function/f_midi.c | 1 + drivers/usb/gadget/function/f_ncm.c | 1 + drivers/usb/gadget/function/f_obex.c | 1 + drivers/usb/gadget/function/f_phonet.c | 1 + drivers/usb/gadget/function/f_printer.c | 1 + drivers/usb/gadget/function/f_rndis.c | 1 + drivers/usb/gadget/function/f_serial.c | 1 + drivers/usb/gadget/function/f_sourcesink.c | 1 + drivers/usb/gadget/function/f_subset.c | 1 + drivers/usb/gadget/function/f_tcm.c | 1 + drivers/usb/gadget/function/f_uac1.c | 1 + drivers/usb/gadget/function/f_uac1_legacy.c | 1 + drivers/usb/gadget/function/f_uac2.c | 1 + drivers/usb/gadget/function/f_uvc.c | 1 + drivers/usb/gadget/function/f_uvc.h | 1 + drivers/usb/gadget/function/rndis.c | 1 + drivers/usb/gadget/function/rndis.h | 1 + drivers/usb/gadget/function/storage_common.c | 1 + drivers/usb/gadget/function/u_audio.c | 1 + drivers/usb/gadget/function/u_audio.h | 1 + drivers/usb/gadget/function/u_ecm.h | 1 + drivers/usb/gadget/function/u_eem.h | 1 + drivers/usb/gadget/function/u_ether.c | 1 + drivers/usb/gadget/function/u_ether.h | 1 + drivers/usb/gadget/function/u_ether_configfs.h | 1 + drivers/usb/gadget/function/u_fs.h | 1 + drivers/usb/gadget/function/u_gether.h | 1 + drivers/usb/gadget/function/u_hid.h | 1 + drivers/usb/gadget/function/u_midi.h | 1 + drivers/usb/gadget/function/u_ncm.h | 1 + drivers/usb/gadget/function/u_phonet.h | 1 + drivers/usb/gadget/function/u_printer.h | 1 + drivers/usb/gadget/function/u_rndis.h | 1 + drivers/usb/gadget/function/u_serial.c | 1 + drivers/usb/gadget/function/u_serial.h | 1 + drivers/usb/gadget/function/u_tcm.h | 1 + drivers/usb/gadget/function/u_uac1.h | 1 + drivers/usb/gadget/function/u_uac1_legacy.c | 1 + drivers/usb/gadget/function/u_uac1_legacy.h | 1 + drivers/usb/gadget/function/u_uac2.h | 1 + drivers/usb/gadget/function/u_uvc.h | 1 + drivers/usb/gadget/function/uvc.h | 1 + drivers/usb/gadget/function/uvc_configfs.c | 1 + drivers/usb/gadget/function/uvc_configfs.h | 1 + drivers/usb/gadget/function/uvc_queue.c | 1 + drivers/usb/gadget/function/uvc_v4l2.c | 1 + drivers/usb/gadget/function/uvc_v4l2.h | 1 + drivers/usb/gadget/function/uvc_video.c | 1 + drivers/usb/gadget/function/uvc_video.h | 1 + drivers/usb/gadget/functions.c | 1 + drivers/usb/gadget/legacy/acm_ms.c | 1 + drivers/usb/gadget/legacy/audio.c | 1 + drivers/usb/gadget/legacy/cdc2.c | 1 + drivers/usb/gadget/legacy/dbgp.c | 1 + drivers/usb/gadget/legacy/ether.c | 1 + drivers/usb/gadget/legacy/g_ffs.c | 1 + drivers/usb/gadget/legacy/gmidi.c | 1 + drivers/usb/gadget/legacy/hid.c | 1 + drivers/usb/gadget/legacy/inode.c | 1 + drivers/usb/gadget/legacy/mass_storage.c | 1 + drivers/usb/gadget/legacy/multi.c | 1 + drivers/usb/gadget/legacy/ncm.c | 1 + drivers/usb/gadget/legacy/nokia.c | 1 + drivers/usb/gadget/legacy/printer.c | 1 + drivers/usb/gadget/legacy/serial.c | 1 + drivers/usb/gadget/legacy/tcm_usb_gadget.c | 1 + drivers/usb/gadget/legacy/webcam.c | 1 + drivers/usb/gadget/legacy/zero.c | 1 + drivers/usb/gadget/u_f.c | 1 + drivers/usb/gadget/u_f.h | 1 + drivers/usb/gadget/u_os_desc.h | 1 + drivers/usb/gadget/udc/amd5536udc.h | 1 + drivers/usb/gadget/udc/amd5536udc_pci.c | 1 + drivers/usb/gadget/udc/at91_udc.c | 1 + drivers/usb/gadget/udc/at91_udc.h | 1 + drivers/usb/gadget/udc/atmel_usba_udc.c | 1 + drivers/usb/gadget/udc/atmel_usba_udc.h | 1 + drivers/usb/gadget/udc/bcm63xx_udc.c | 1 + drivers/usb/gadget/udc/bdc/bdc.h | 1 + drivers/usb/gadget/udc/bdc/bdc_cmd.c | 1 + drivers/usb/gadget/udc/bdc/bdc_cmd.h | 1 + drivers/usb/gadget/udc/bdc/bdc_core.c | 1 + drivers/usb/gadget/udc/bdc/bdc_dbg.c | 1 + drivers/usb/gadget/udc/bdc/bdc_dbg.h | 1 + drivers/usb/gadget/udc/bdc/bdc_ep.c | 1 + drivers/usb/gadget/udc/bdc/bdc_ep.h | 1 + drivers/usb/gadget/udc/bdc/bdc_pci.c | 1 + drivers/usb/gadget/udc/bdc/bdc_udc.c | 1 + drivers/usb/gadget/udc/core.c | 1 + drivers/usb/gadget/udc/dummy_hcd.c | 1 + drivers/usb/gadget/udc/fotg210-udc.c | 1 + drivers/usb/gadget/udc/fotg210.h | 1 + drivers/usb/gadget/udc/fsl_mxc_udc.c | 1 + drivers/usb/gadget/udc/fsl_qe_udc.c | 1 + drivers/usb/gadget/udc/fsl_qe_udc.h | 1 + drivers/usb/gadget/udc/fsl_udc_core.c | 1 + drivers/usb/gadget/udc/fsl_usb2_udc.h | 1 + drivers/usb/gadget/udc/fusb300_udc.c | 1 + drivers/usb/gadget/udc/fusb300_udc.h | 1 + drivers/usb/gadget/udc/goku_udc.c | 1 + drivers/usb/gadget/udc/goku_udc.h | 1 + drivers/usb/gadget/udc/gr_udc.c | 1 + drivers/usb/gadget/udc/gr_udc.h | 1 + drivers/usb/gadget/udc/lpc32xx_udc.c | 1 + drivers/usb/gadget/udc/m66592-udc.c | 1 + drivers/usb/gadget/udc/m66592-udc.h | 1 + drivers/usb/gadget/udc/mv_u3d.h | 1 + drivers/usb/gadget/udc/mv_u3d_core.c | 1 + drivers/usb/gadget/udc/mv_udc.h | 1 + drivers/usb/gadget/udc/mv_udc_core.c | 1 + drivers/usb/gadget/udc/net2272.c | 1 + drivers/usb/gadget/udc/net2272.h | 1 + drivers/usb/gadget/udc/net2280.c | 1 + drivers/usb/gadget/udc/net2280.h | 1 + drivers/usb/gadget/udc/omap_udc.c | 1 + drivers/usb/gadget/udc/pch_udc.c | 1 + drivers/usb/gadget/udc/pxa25x_udc.c | 1 + drivers/usb/gadget/udc/pxa25x_udc.h | 1 + drivers/usb/gadget/udc/pxa27x_udc.c | 1 + drivers/usb/gadget/udc/pxa27x_udc.h | 1 + drivers/usb/gadget/udc/r8a66597-udc.c | 1 + drivers/usb/gadget/udc/r8a66597-udc.h | 1 + drivers/usb/gadget/udc/renesas_usb3.c | 1 + drivers/usb/gadget/udc/s3c-hsudc.c | 1 + drivers/usb/gadget/udc/s3c2410_udc.c | 1 + drivers/usb/gadget/udc/s3c2410_udc.h | 1 + drivers/usb/gadget/udc/snps_udc_core.c | 1 + drivers/usb/gadget/udc/snps_udc_plat.c | 1 + drivers/usb/gadget/udc/trace.c | 1 + drivers/usb/gadget/udc/trace.h | 1 + drivers/usb/gadget/udc/udc-xilinx.c | 1 + drivers/usb/gadget/usbstring.c | 1 + drivers/usb/host/bcma-hcd.c | 1 + drivers/usb/host/ehci-atmel.c | 1 + drivers/usb/host/ehci-dbg.c | 1 + drivers/usb/host/ehci-exynos.c | 1 + drivers/usb/host/ehci-fsl.c | 1 + drivers/usb/host/ehci-fsl.h | 1 + drivers/usb/host/ehci-grlib.c | 1 + drivers/usb/host/ehci-hcd.c | 1 + drivers/usb/host/ehci-hub.c | 1 + drivers/usb/host/ehci-mem.c | 1 + drivers/usb/host/ehci-mv.c | 1 + drivers/usb/host/ehci-mxc.c | 1 + drivers/usb/host/ehci-omap.c | 1 + drivers/usb/host/ehci-orion.c | 1 + drivers/usb/host/ehci-pci.c | 1 + drivers/usb/host/ehci-platform.c | 1 + drivers/usb/host/ehci-pmcmsp.c | 1 + drivers/usb/host/ehci-ppc-of.c | 1 + drivers/usb/host/ehci-ps3.c | 1 + drivers/usb/host/ehci-q.c | 1 + drivers/usb/host/ehci-sched.c | 1 + drivers/usb/host/ehci-sh.c | 1 + drivers/usb/host/ehci-spear.c | 1 + drivers/usb/host/ehci-st.c | 1 + drivers/usb/host/ehci-sysfs.c | 1 + drivers/usb/host/ehci-tegra.c | 1 + drivers/usb/host/ehci-tilegx.c | 1 + drivers/usb/host/ehci-timer.c | 1 + drivers/usb/host/ehci-w90x900.c | 1 + drivers/usb/host/ehci-xilinx-of.c | 1 + drivers/usb/host/ehci.h | 1 + drivers/usb/host/fhci-dbg.c | 1 + drivers/usb/host/fhci-hcd.c | 1 + drivers/usb/host/fhci-hub.c | 1 + drivers/usb/host/fhci-mem.c | 1 + drivers/usb/host/fhci-q.c | 1 + drivers/usb/host/fhci-sched.c | 1 + drivers/usb/host/fhci-tds.c | 1 + drivers/usb/host/fhci.h | 1 + drivers/usb/host/fotg210-hcd.c | 1 + drivers/usb/host/fsl-mph-dr-of.c | 1 + drivers/usb/host/hwa-hc.c | 1 + drivers/usb/host/imx21-dbg.c | 1 + drivers/usb/host/imx21-hcd.c | 1 + drivers/usb/host/imx21-hcd.h | 1 + drivers/usb/host/isp116x-hcd.c | 1 + drivers/usb/host/isp1362-hcd.c | 1 + drivers/usb/host/max3421-hcd.c | 1 + drivers/usb/host/ohci-at91.c | 1 + drivers/usb/host/ohci-da8xx.c | 1 + drivers/usb/host/ohci-dbg.c | 1 + drivers/usb/host/ohci-exynos.c | 1 + drivers/usb/host/ohci-hcd.c | 1 + drivers/usb/host/ohci-hub.c | 1 + drivers/usb/host/ohci-mem.c | 1 + drivers/usb/host/ohci-nxp.c | 1 + drivers/usb/host/ohci-omap.c | 1 + drivers/usb/host/ohci-pci.c | 1 + drivers/usb/host/ohci-platform.c | 1 + drivers/usb/host/ohci-ppc-of.c | 1 + drivers/usb/host/ohci-ps3.c | 1 + drivers/usb/host/ohci-pxa27x.c | 1 + drivers/usb/host/ohci-q.c | 1 + drivers/usb/host/ohci-s3c2410.c | 1 + drivers/usb/host/ohci-sa1111.c | 1 + drivers/usb/host/ohci-sm501.c | 1 + drivers/usb/host/ohci-spear.c | 1 + drivers/usb/host/ohci-st.c | 1 + drivers/usb/host/ohci-tilegx.c | 1 + drivers/usb/host/ohci-tmio.c | 1 + drivers/usb/host/ohci.h | 1 + drivers/usb/host/oxu210hp-hcd.c | 1 + drivers/usb/host/pci-quirks.c | 1 + drivers/usb/host/r8a66597-hcd.c | 1 + drivers/usb/host/r8a66597.h | 1 + drivers/usb/host/sl811-hcd.c | 1 + drivers/usb/host/sl811_cs.c | 1 + drivers/usb/host/ssb-hcd.c | 1 + drivers/usb/host/u132-hcd.c | 1 + drivers/usb/host/uhci-hcd.c | 1 + drivers/usb/host/whci/asl.c | 1 + drivers/usb/host/whci/debug.c | 1 + drivers/usb/host/whci/hcd.c | 1 + drivers/usb/host/whci/hw.c | 1 + drivers/usb/host/whci/init.c | 1 + drivers/usb/host/whci/int.c | 1 + drivers/usb/host/whci/pzl.c | 1 + drivers/usb/host/whci/qset.c | 1 + drivers/usb/host/whci/whcd.h | 1 + drivers/usb/host/whci/whci-hc.h | 1 + drivers/usb/host/whci/wusb.c | 1 + drivers/usb/host/xhci-dbg.c | 1 + drivers/usb/host/xhci-ext-caps.h | 1 + drivers/usb/host/xhci-hub.c | 1 + drivers/usb/host/xhci-mem.c | 1 + drivers/usb/host/xhci-mtk-sch.c | 1 + drivers/usb/host/xhci-mtk.c | 1 + drivers/usb/host/xhci-mtk.h | 1 + drivers/usb/host/xhci-mvebu.c | 1 + drivers/usb/host/xhci-mvebu.h | 1 + drivers/usb/host/xhci-pci.c | 1 + drivers/usb/host/xhci-plat.c | 1 + drivers/usb/host/xhci-plat.h | 1 + drivers/usb/host/xhci-rcar.c | 1 + drivers/usb/host/xhci-rcar.h | 1 + drivers/usb/host/xhci-ring.c | 1 + drivers/usb/host/xhci-tegra.c | 1 + drivers/usb/host/xhci-trace.c | 1 + drivers/usb/host/xhci-trace.h | 1 + drivers/usb/host/xhci.c | 1 + drivers/usb/host/xhci.h | 1 + drivers/usb/image/mdc800.c | 1 + drivers/usb/image/microtek.c | 1 + drivers/usb/isp1760/isp1760-core.c | 1 + drivers/usb/isp1760/isp1760-core.h | 1 + drivers/usb/isp1760/isp1760-regs.h | 1 + drivers/usb/isp1760/isp1760-udc.c | 1 + drivers/usb/isp1760/isp1760-udc.h | 1 + drivers/usb/misc/adutux.c | 1 + drivers/usb/misc/appledisplay.c | 1 + drivers/usb/misc/chaoskey.c | 1 + drivers/usb/misc/cypress_cy7c63.c | 1 + drivers/usb/misc/cytherm.c | 1 + drivers/usb/misc/ehset.c | 1 + drivers/usb/misc/emi26.c | 1 + drivers/usb/misc/emi62.c | 1 + drivers/usb/misc/ezusb.c | 1 + drivers/usb/misc/ftdi-elan.c | 1 + drivers/usb/misc/idmouse.c | 1 + drivers/usb/misc/iowarrior.c | 1 + drivers/usb/misc/isight_firmware.c | 1 + drivers/usb/misc/ldusb.c | 1 + drivers/usb/misc/legousbtower.c | 1 + drivers/usb/misc/lvstest.c | 1 + drivers/usb/misc/rio500.c | 1 + drivers/usb/misc/rio500_usb.h | 1 + drivers/usb/misc/sisusbvga/sisusb.c | 1 + drivers/usb/misc/sisusbvga/sisusb.h | 1 + drivers/usb/misc/sisusbvga/sisusb_con.c | 1 + drivers/usb/misc/sisusbvga/sisusb_init.c | 1 + drivers/usb/misc/sisusbvga/sisusb_init.h | 1 + drivers/usb/misc/sisusbvga/sisusb_struct.h | 1 + drivers/usb/misc/trancevibrator.c | 1 + drivers/usb/misc/usb251xb.c | 1 + drivers/usb/misc/usb3503.c | 1 + drivers/usb/misc/usb4604.c | 1 + drivers/usb/misc/usb_u132.h | 1 + drivers/usb/misc/usblcd.c | 1 + drivers/usb/misc/usbsevseg.c | 1 + drivers/usb/misc/usbtest.c | 1 + drivers/usb/misc/uss720.c | 1 + drivers/usb/misc/yurex.c | 1 + drivers/usb/mon/mon_main.c | 1 + drivers/usb/mtu3/mtu3.h | 1 + drivers/usb/mtu3/mtu3_core.c | 1 + drivers/usb/mtu3/mtu3_dr.c | 1 + drivers/usb/mtu3/mtu3_dr.h | 1 + drivers/usb/mtu3/mtu3_gadget.c | 1 + drivers/usb/mtu3/mtu3_gadget_ep0.c | 1 + drivers/usb/mtu3/mtu3_host.c | 1 + drivers/usb/mtu3/mtu3_hw_regs.h | 1 + drivers/usb/mtu3/mtu3_plat.c | 1 + drivers/usb/mtu3/mtu3_qmu.c | 1 + drivers/usb/mtu3/mtu3_qmu.h | 1 + drivers/usb/musb/am35x.c | 1 + drivers/usb/musb/blackfin.c | 1 + drivers/usb/musb/blackfin.h | 1 + drivers/usb/musb/cppi_dma.c | 1 + drivers/usb/musb/da8xx.c | 1 + drivers/usb/musb/davinci.c | 1 + drivers/usb/musb/davinci.h | 1 + drivers/usb/musb/jz4740.c | 1 + drivers/usb/musb/musb_am335x.c | 1 + drivers/usb/musb/musb_core.c | 1 + drivers/usb/musb/musb_core.h | 1 + drivers/usb/musb/musb_cppi41.c | 1 + drivers/usb/musb/musb_debug.h | 1 + drivers/usb/musb/musb_debugfs.c | 1 + drivers/usb/musb/musb_dma.h | 1 + drivers/usb/musb/musb_dsps.c | 1 + drivers/usb/musb/musb_gadget.c | 1 + drivers/usb/musb/musb_gadget.h | 1 + drivers/usb/musb/musb_gadget_ep0.c | 1 + drivers/usb/musb/musb_host.c | 1 + drivers/usb/musb/musb_host.h | 1 + drivers/usb/musb/musb_io.h | 1 + drivers/usb/musb/musb_regs.h | 1 + drivers/usb/musb/musb_trace.c | 1 + drivers/usb/musb/musb_trace.h | 1 + drivers/usb/musb/musb_virthub.c | 1 + drivers/usb/musb/musbhsdma.c | 1 + drivers/usb/musb/musbhsdma.h | 1 + drivers/usb/musb/omap2430.c | 1 + drivers/usb/musb/omap2430.h | 1 + drivers/usb/musb/sunxi.c | 1 + drivers/usb/musb/tusb6010.c | 1 + drivers/usb/musb/tusb6010.h | 1 + drivers/usb/musb/tusb6010_omap.c | 1 + drivers/usb/musb/ux500.c | 1 + drivers/usb/musb/ux500_dma.c | 1 + drivers/usb/phy/of.c | 1 + drivers/usb/phy/phy-ab8500-usb.c | 1 + drivers/usb/phy/phy-am335x-control.c | 1 + drivers/usb/phy/phy-am335x.c | 1 + drivers/usb/phy/phy-fsl-usb.c | 1 + drivers/usb/phy/phy-fsl-usb.h | 1 + drivers/usb/phy/phy-generic.c | 1 + drivers/usb/phy/phy-gpio-vbus-usb.c | 1 + drivers/usb/phy/phy-isp1301-omap.c | 1 + drivers/usb/phy/phy-isp1301.c | 1 + drivers/usb/phy/phy-keystone.c | 1 + drivers/usb/phy/phy-mv-usb.c | 1 + drivers/usb/phy/phy-mv-usb.h | 1 + drivers/usb/phy/phy-mxs-usb.c | 1 + drivers/usb/phy/phy-omap-otg.c | 1 + drivers/usb/phy/phy-tahvo.c | 1 + drivers/usb/phy/phy-tegra-usb.c | 1 + drivers/usb/phy/phy-twl6030-usb.c | 1 + drivers/usb/phy/phy-ulpi-viewport.c | 1 + drivers/usb/phy/phy-ulpi.c | 1 + drivers/usb/phy/phy.c | 1 + drivers/usb/renesas_usbhs/common.c | 1 + drivers/usb/renesas_usbhs/common.h | 1 + drivers/usb/renesas_usbhs/fifo.c | 1 + drivers/usb/renesas_usbhs/fifo.h | 1 + drivers/usb/renesas_usbhs/mod.c | 1 + drivers/usb/renesas_usbhs/mod.h | 1 + drivers/usb/renesas_usbhs/mod_gadget.c | 1 + drivers/usb/renesas_usbhs/mod_host.c | 1 + drivers/usb/renesas_usbhs/pipe.c | 1 + drivers/usb/renesas_usbhs/pipe.h | 1 + drivers/usb/renesas_usbhs/rcar2.c | 1 + drivers/usb/renesas_usbhs/rcar3.c | 1 + drivers/usb/serial/aircable.c | 1 + drivers/usb/serial/ark3116.c | 1 + drivers/usb/serial/belkin_sa.c | 1 + drivers/usb/serial/belkin_sa.h | 1 + drivers/usb/serial/bus.c | 1 + drivers/usb/serial/ch341.c | 1 + drivers/usb/serial/console.c | 1 + drivers/usb/serial/cp210x.c | 1 + drivers/usb/serial/cyberjack.c | 1 + drivers/usb/serial/cypress_m8.c | 1 + drivers/usb/serial/digi_acceleport.c | 1 + drivers/usb/serial/empeg.c | 1 + drivers/usb/serial/f81232.c | 1 + drivers/usb/serial/f81534.c | 1 + drivers/usb/serial/ftdi_sio.c | 1 + drivers/usb/serial/garmin_gps.c | 1 + drivers/usb/serial/generic.c | 1 + drivers/usb/serial/io_16654.h | 1 + drivers/usb/serial/io_edgeport.c | 1 + drivers/usb/serial/io_edgeport.h | 1 + drivers/usb/serial/io_ionsp.h | 1 + drivers/usb/serial/io_ti.c | 1 + drivers/usb/serial/io_ti.h | 1 + drivers/usb/serial/io_usbvend.h | 1 + drivers/usb/serial/ipaq.c | 1 + drivers/usb/serial/ipw.c | 1 + drivers/usb/serial/ir-usb.c | 1 + drivers/usb/serial/iuu_phoenix.c | 1 + drivers/usb/serial/iuu_phoenix.h | 1 + drivers/usb/serial/keyspan.c | 1 + drivers/usb/serial/keyspan_pda.c | 1 + drivers/usb/serial/kl5kusb105.c | 1 + drivers/usb/serial/kobil_sct.c | 1 + drivers/usb/serial/mct_u232.c | 1 + drivers/usb/serial/mct_u232.h | 1 + drivers/usb/serial/metro-usb.c | 1 + drivers/usb/serial/mos7720.c | 1 + drivers/usb/serial/mos7840.c | 1 + drivers/usb/serial/mxuport.c | 1 + drivers/usb/serial/navman.c | 1 + drivers/usb/serial/omninet.c | 1 + drivers/usb/serial/opticon.c | 1 + drivers/usb/serial/option.c | 1 + drivers/usb/serial/oti6858.c | 1 + drivers/usb/serial/oti6858.h | 1 + drivers/usb/serial/pl2303.c | 1 + drivers/usb/serial/pl2303.h | 1 + drivers/usb/serial/qcaux.c | 1 + drivers/usb/serial/qcserial.c | 1 + drivers/usb/serial/quatech2.c | 1 + drivers/usb/serial/safe_serial.c | 1 + drivers/usb/serial/sierra.c | 1 + drivers/usb/serial/spcp8x5.c | 1 + drivers/usb/serial/ssu100.c | 1 + drivers/usb/serial/symbolserial.c | 1 + drivers/usb/serial/ti_usb_3410_5052.c | 1 + drivers/usb/serial/upd78f0730.c | 1 + drivers/usb/serial/usb-serial-simple.c | 1 + drivers/usb/serial/usb-serial.c | 1 + drivers/usb/serial/usb_debug.c | 1 + drivers/usb/serial/usb_wwan.c | 1 + drivers/usb/serial/visor.c | 1 + drivers/usb/serial/visor.h | 1 + drivers/usb/serial/whiteheat.c | 1 + drivers/usb/serial/whiteheat.h | 1 + drivers/usb/serial/wishbone-serial.c | 1 + drivers/usb/serial/xsens_mt.c | 1 + drivers/usb/storage/alauda.c | 1 + drivers/usb/storage/cypress_atacb.c | 1 + drivers/usb/storage/datafab.c | 1 + drivers/usb/storage/debug.c | 1 + drivers/usb/storage/debug.h | 1 + drivers/usb/storage/ene_ub6250.c | 1 + drivers/usb/storage/freecom.c | 1 + drivers/usb/storage/initializers.c | 1 + drivers/usb/storage/initializers.h | 1 + drivers/usb/storage/isd200.c | 1 + drivers/usb/storage/jumpshot.c | 1 + drivers/usb/storage/karma.c | 1 + drivers/usb/storage/onetouch.c | 1 + drivers/usb/storage/option_ms.c | 1 + drivers/usb/storage/protocol.c | 1 + drivers/usb/storage/protocol.h | 1 + drivers/usb/storage/realtek_cr.c | 1 + drivers/usb/storage/scsiglue.c | 1 + drivers/usb/storage/scsiglue.h | 1 + drivers/usb/storage/sddr09.c | 1 + drivers/usb/storage/sddr55.c | 1 + drivers/usb/storage/shuttle_usbat.c | 1 + drivers/usb/storage/transport.c | 1 + drivers/usb/storage/transport.h | 1 + drivers/usb/storage/uas.c | 1 + drivers/usb/storage/unusual_alauda.h | 1 + drivers/usb/storage/unusual_cypress.h | 1 + drivers/usb/storage/unusual_datafab.h | 1 + drivers/usb/storage/unusual_devs.h | 1 + drivers/usb/storage/unusual_ene_ub6250.h | 1 + drivers/usb/storage/unusual_freecom.h | 1 + drivers/usb/storage/unusual_isd200.h | 1 + drivers/usb/storage/unusual_jumpshot.h | 1 + drivers/usb/storage/unusual_karma.h | 1 + drivers/usb/storage/unusual_onetouch.h | 1 + drivers/usb/storage/unusual_realtek.h | 1 + drivers/usb/storage/unusual_sddr09.h | 1 + drivers/usb/storage/unusual_sddr55.h | 1 + drivers/usb/storage/unusual_uas.h | 1 + drivers/usb/storage/unusual_usbat.h | 1 + drivers/usb/storage/usb.c | 1 + drivers/usb/storage/usb.h | 1 + drivers/usb/storage/usual-tables.c | 1 + drivers/usb/typec/typec.c | 1 + drivers/usb/typec/typec_wcove.c | 1 + drivers/usb/typec/ucsi/ucsi.c | 1 + drivers/usb/typec/ucsi/ucsi_acpi.c | 1 + drivers/usb/usb-skeleton.c | 1 + drivers/usb/usbip/stub.h | 1 + drivers/usb/usbip/stub_dev.c | 1 + drivers/usb/usbip/stub_main.c | 1 + drivers/usb/usbip/stub_rx.c | 1 + drivers/usb/usbip/stub_tx.c | 1 + drivers/usb/usbip/usbip_common.c | 1 + drivers/usb/usbip/usbip_common.h | 1 + drivers/usb/usbip/usbip_event.c | 1 + drivers/usb/usbip/vhci.h | 1 + drivers/usb/usbip/vhci_hcd.c | 1 + drivers/usb/usbip/vhci_rx.c | 1 + drivers/usb/usbip/vhci_sysfs.c | 1 + drivers/usb/usbip/vhci_tx.c | 1 + drivers/usb/usbip/vudc.h | 1 + drivers/usb/usbip/vudc_dev.c | 1 + drivers/usb/usbip/vudc_main.c | 1 + drivers/usb/usbip/vudc_rx.c | 1 + drivers/usb/usbip/vudc_sysfs.c | 1 + drivers/usb/usbip/vudc_transfer.c | 1 + drivers/usb/usbip/vudc_tx.c | 1 + drivers/usb/wusbcore/cbaf.c | 1 + drivers/usb/wusbcore/crypto.c | 1 + drivers/usb/wusbcore/dev-sysfs.c | 1 + drivers/usb/wusbcore/devconnect.c | 1 + drivers/usb/wusbcore/mmc.c | 1 + drivers/usb/wusbcore/pal.c | 1 + drivers/usb/wusbcore/reservation.c | 1 + drivers/usb/wusbcore/rh.c | 1 + drivers/usb/wusbcore/security.c | 1 + drivers/usb/wusbcore/wa-hc.c | 1 + drivers/usb/wusbcore/wa-hc.h | 1 + drivers/usb/wusbcore/wa-nep.c | 1 + drivers/usb/wusbcore/wa-rpipe.c | 1 + drivers/usb/wusbcore/wa-xfer.c | 1 + drivers/usb/wusbcore/wusbhc.c | 1 + drivers/usb/wusbcore/wusbhc.h | 1 + include/linux/usb/association.h | 1 + include/linux/usb/audio-v2.h | 1 + include/linux/usb/audio.h | 1 + include/linux/usb/c67x00.h | 1 + include/linux/usb/cdc-wdm.h | 1 + include/linux/usb/cdc.h | 1 + include/linux/usb/cdc_ncm.h | 1 + include/linux/usb/composite.h | 1 + include/linux/usb/ehci_def.h | 1 + include/linux/usb/ehci_pdriver.h | 1 + include/linux/usb/g_hid.h | 1 + include/linux/usb/gadget.h | 1 + include/linux/usb/gpio_vbus.h | 1 + include/linux/usb/hcd.h | 1 + include/linux/usb/input.h | 1 + include/linux/usb/isp1301.h | 1 + include/linux/usb/m66592.h | 1 + include/linux/usb/musb-ux500.h | 1 + include/linux/usb/net2280.h | 1 + include/linux/usb/of.h | 1 + include/linux/usb/ohci_pdriver.h | 1 + include/linux/usb/otg-fsm.h | 1 + include/linux/usb/phy_companion.h | 1 + include/linux/usb/r8a66597.h | 1 + include/linux/usb/renesas_usbhs.h | 1 + include/linux/usb/rndis_host.h | 1 + include/linux/usb/samsung_usb_phy.h | 1 + include/linux/usb/serial.h | 1 + include/linux/usb/storage.h | 1 + include/linux/usb/tegra_usb_phy.h | 1 + include/linux/usb/tilegx.h | 1 + include/linux/usb/ulpi.h | 1 + include/linux/usb/usb338x.h | 1 + include/linux/usb/usbnet.h | 1 + include/linux/usb/wusb-wa.h | 1 + include/linux/usb/wusb.h | 1 + include/linux/usb/xhci-dbgp.h | 1 + include/linux/usbdevice_fs.h | 1 + 651 files changed, 651 insertions(+) (limited to 'include') diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index e4f177da164d..87d16578c5a4 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /****************************************************************************** * cxacru.c - driver for USB ADSL modems based on * Conexant AccessRunner chipset diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 091db9b281f5..cc65bec5b331 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /****************************************************************************** * speedtch.c - Alcatel SpeedTouch USB xDSL modem driver * diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index ba7616395db2..ab75690044bb 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /*- * Copyright (c) 2003, 2004 * Damien Bergamini . All rights reserved. diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index 8607af758bbd..ce1c57cb413f 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /****************************************************************************** * usbatm.c - Generic USB xDSL driver core * diff --git a/drivers/usb/atm/usbatm.h b/drivers/usb/atm/usbatm.h index f3eecd967a8a..72f9d3b8adb6 100644 --- a/drivers/usb/atm/usbatm.h +++ b/drivers/usb/atm/usbatm.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /****************************************************************************** * usbatm.h - Generic USB xDSL driver core * diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c index c73c1ec3005e..7c55032a2c2c 100644 --- a/drivers/usb/atm/xusbatm.c +++ b/drivers/usb/atm/xusbatm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /****************************************************************************** * xusbatm.c - dumb usbatm-based driver for modems initialized in userspace * diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c index 5796c8820514..3e4b46f8aa67 100644 --- a/drivers/usb/c67x00/c67x00-drv.c +++ b/drivers/usb/c67x00/c67x00-drv.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * c67x00-drv.c: Cypress C67X00 USB Common infrastructure * diff --git a/drivers/usb/c67x00/c67x00-hcd.c b/drivers/usb/c67x00/c67x00-hcd.c index 30d3f346686e..705c40a0097d 100644 --- a/drivers/usb/c67x00/c67x00-hcd.c +++ b/drivers/usb/c67x00/c67x00-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver * diff --git a/drivers/usb/c67x00/c67x00-hcd.h b/drivers/usb/c67x00/c67x00-hcd.h index cf8a455a6403..05ab1ba3b327 100644 --- a/drivers/usb/c67x00/c67x00-hcd.h +++ b/drivers/usb/c67x00/c67x00-hcd.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * c67x00-hcd.h: Cypress C67X00 USB HCD * diff --git a/drivers/usb/c67x00/c67x00-ll-hpi.c b/drivers/usb/c67x00/c67x00-ll-hpi.c index b58151841e10..285f40aa16bf 100644 --- a/drivers/usb/c67x00/c67x00-ll-hpi.c +++ b/drivers/usb/c67x00/c67x00-ll-hpi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * c67x00-ll-hpi.c: Cypress C67X00 USB Low level interface using HPI * diff --git a/drivers/usb/c67x00/c67x00-sched.c b/drivers/usb/c67x00/c67x00-sched.c index 0b68cd6874d2..233a8be2d8e4 100644 --- a/drivers/usb/c67x00/c67x00-sched.c +++ b/drivers/usb/c67x00/c67x00-sched.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * c67x00-sched.c: Cypress C67X00 USB Host Controller Driver - TD scheduling * diff --git a/drivers/usb/c67x00/c67x00.h b/drivers/usb/c67x00/c67x00.h index a26e9ded0f32..31339d7f7a4b 100644 --- a/drivers/usb/c67x00/c67x00.h +++ b/drivers/usb/c67x00/c67x00.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * c67x00.h: Cypress C67X00 USB register and field definitions * diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h index e462f55c8b99..018a28e2e7d8 100644 --- a/drivers/usb/chipidea/bits.h +++ b/drivers/usb/chipidea/bits.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * bits.h - register bits of the ChipIdea USB IP core * diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 6743f85b1b7a..fa6dd9d9f5c6 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ci.h - common structures, functions, and macros of the ChipIdea driver * diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 5f4a8157fad8..9a2f7416ff9e 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2012 Freescale Semiconductor, Inc. * Copyright (C) 2012 Marek Vasut diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index d666c9f036ba..98a8b8c817ff 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2012 Freescale Semiconductor, Inc. * diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index bb626120296f..85aac4e881aa 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2010, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/usb/chipidea/ci_hdrc_pci.c b/drivers/usb/chipidea/ci_hdrc_pci.c index 39414e4b2d81..a9dd661026bf 100644 --- a/drivers/usb/chipidea/ci_hdrc_pci.c +++ b/drivers/usb/chipidea/ci_hdrc_pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ci_hdrc_pci.c - MIPS USB IP core family device controller * diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index 99425db9ba62..41c5f8b9c0b9 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2014 Marvell Technology Group Ltd. * diff --git a/drivers/usb/chipidea/ci_hdrc_zevio.c b/drivers/usb/chipidea/ci_hdrc_zevio.c index 1264de505527..b8c75304d805 100644 --- a/drivers/usb/chipidea/ci_hdrc_zevio.c +++ b/drivers/usb/chipidea/ci_hdrc_zevio.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2013 Daniel Tang * diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 43ea5fb87b9a..708c2eaba81c 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * core.c - ChipIdea USB IP core family device controller * diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 18cb8e46262d..a49e8567ac7d 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * host.c - ChipIdea USB host controller driver * diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index 10236fe71522..ef22fcc63f33 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * otg.c - ChipIdea USB IP core OTG driver * diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h index 9ecb598e48f0..b8e2415bfa7c 100644 --- a/drivers/usb/chipidea/otg.h +++ b/drivers/usb/chipidea/otg.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2013-2014 Freescale Semiconductor, Inc. * diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 5ea0246f650d..485aaa1747e8 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * otg_fsm.c - ChipIdea USB IP core OTG FSM driver * diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h index 6366fe398ba6..08e74aeea21d 100644 --- a/drivers/usb/chipidea/otg_fsm.h +++ b/drivers/usb/chipidea/otg_fsm.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2014 Freescale Semiconductor, Inc. * diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index daffa5ed396d..9e75e0921b3f 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * udc.c - ChipIdea UDC driver * diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h index 2ecd1174d66c..840275342873 100644 --- a/drivers/usb/chipidea/udc.h +++ b/drivers/usb/chipidea/udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * udc.h - ChipIdea UDC structures * diff --git a/drivers/usb/chipidea/ulpi.c b/drivers/usb/chipidea/ulpi.c index 1219583dc1b2..c4e1900b9777 100644 --- a/drivers/usb/chipidea/ulpi.c +++ b/drivers/usb/chipidea/ulpi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2016 Linaro Ltd. * diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 9f4a0185dd60..e3842238adeb 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2012 Freescale Semiconductor, Inc. * diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 18c923a4c16e..ee33370b012d 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * cdc-acm.c * diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 3e865dbf878c..80529ac4b083 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * cdc-wdm.c * diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index fb87c17ed6fa..af771fa25800 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * usblp.c * diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 6adceacaa919..a1475fafc5f4 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /** * drivers/usb/class/usbtmc.c - USB Test & Measurement class driver * diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index 552ff7ac5a6b..2b45b0517b3d 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Provides code common for host and device side USB. * diff --git a/drivers/usb/common/led.c b/drivers/usb/common/led.c index df23da00a901..fd5538c8e067 100644 --- a/drivers/usb/common/led.c +++ b/drivers/usb/common/led.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * LED Triggers for USB Activity * diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c index 4aa5195db8ea..73c38258eec7 100644 --- a/drivers/usb/common/ulpi.c +++ b/drivers/usb/common/ulpi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * ulpi.c - USB ULPI PHY bus * diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c index b8fe31e409a5..f960d5374ee0 100644 --- a/drivers/usb/common/usb-otg-fsm.c +++ b/drivers/usb/common/usb-otg-fsm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * OTG Finite State Machine from OTG spec * diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 55dea2e7828f..1c8b6faa7e66 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * devices.c * (C) Copyright 1999 Randy Dunlap. diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index e9326f31db8d..f67e9fce6847 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /*****************************************************************************/ /* diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index ea829ad798c0..07d5919ccae1 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright David Brownell 2000-2002 * diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 67aa3d039b9b..16a19faae376 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * (C) Copyright Linus Torvalds 1999 * (C) Copyright Johannes Erdfelt 1999-2001 diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 34c1a7e22aae..7d29bf3f095b 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * usb hub driver head file * diff --git a/drivers/usb/core/ledtrig-usbport.c b/drivers/usb/core/ledtrig-usbport.c index 1af877942110..f832a2b854ff 100644 --- a/drivers/usb/core/ledtrig-usbport.c +++ b/drivers/usb/core/ledtrig-usbport.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB port LED trigger * diff --git a/drivers/usb/core/of.c b/drivers/usb/core/of.c index 3863bb1ce8c5..a95416d28aa6 100644 --- a/drivers/usb/core/of.c +++ b/drivers/usb/core/of.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * of.c The helpers for hcd device tree support * diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h index 085049d37d7a..4629f6e93954 100644 --- a/drivers/usb/core/otg_whitelist.h +++ b/drivers/usb/core/otg_whitelist.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * drivers/usb/core/otg_whitelist.h * diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 460c855be0d0..bd757a951a18 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * usb port device code * diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 3f08a9be65a8..270c5105d3c6 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB device quirk handling logic and table * diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index ef9cf4a21afe..90afee5079dc 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB-ACPI glue code * diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 1b6612c2cdda..82a7d98c3436 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * core.c - DesignWare HS OTG Controller common routines * diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 730d7eb449bb..f66c94130cac 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * core.h - DesignWare HS OTG Controller common declarations * diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index b8bcb007c92a..ab3fa1630853 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * core_intr.c - DesignWare HS OTG Controller common interrupt handling * diff --git a/drivers/usb/dwc2/debug.h b/drivers/usb/dwc2/debug.h index 8222783e6822..7e2442bcbb2e 100644 --- a/drivers/usb/dwc2/debug.h +++ b/drivers/usb/dwc2/debug.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * debug.h - Designware USB2 DRD controller debug header * diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index 794b959a7c8c..c6492cca872f 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * debugfs.c - Designware USB2 DRD controller debugfs * diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 603c21612051..40fd4e747e20 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * Copyright (c) 2011 Samsung Electronics Co., Ltd. * http://www.samsung.com diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index e8fee0f969f0..69eb40cd1b47 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * hcd.c - DesignWare HS OTG Controller host-mode routines * diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 11c3c145b793..78e9e01051b5 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * hcd.h - DesignWare HS OTG Controller host-mode declarations * diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c index b8bdf545c3a7..28c8898b3b66 100644 --- a/drivers/usb/dwc2/hcd_ddma.c +++ b/drivers/usb/dwc2/hcd_ddma.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * hcd_ddma.c - DesignWare HS OTG Controller descriptor DMA routines * diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 28a8210710b1..916d991b96b8 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * hcd_intr.c - DesignWare HS OTG Controller host-mode interrupt handling * diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 3ae8b1bbaa55..f472de238ac2 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * hcd_queue.c - DesignWare HS OTG Controller host queuing routines * diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 4592012c4743..2c906d8ee465 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * hw.h - DesignWare HS OTG Controller hardware definitions * diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index fe770a2834d2..ef73af6e03a9 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * Copyright (C) 2004-2016 Synopsys, Inc. * diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c index fdeb8c7bf30a..3ecc951a1aea 100644 --- a/drivers/usb/dwc2/pci.c +++ b/drivers/usb/dwc2/pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * pci.c - DesignWare HS OTG Controller PCI driver * diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index daf0d37acb37..3e26550d13dd 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * platform.c - DesignWare HS OTG Controller platform driver * diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index dabfa16fa267..d52f087cd8bb 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * core.c - DesignWare USB3 DRD Controller Core file * diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e33cc10121be..5ac60463e8c5 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * core.h - DesignWare USB3 DRD Core Header * diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 5e9c070ec874..2778d2d1e9b6 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * debug.h - DesignWare USB3 DRD Controller Debug Header * diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 4e09be80e59f..b104a8786896 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * debugfs.c - DesignWare USB3 DRD Controller DebugFS file * diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 2765c51c7ef5..a7e6803ba2e1 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * drd.c - DesignWare USB3 DRD Controller Dual-role support * diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index e089df72f766..24294c3e3c9b 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * dwc3-exynos.c - Samsung EXYNOS DWC3 Specific Glue layer * diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index d2ed9523e77c..7310646aec33 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * dwc3-keystone.c - Keystone Specific Glue layer * diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index ceea1619f8aa..3269a5f64823 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * dwc3-of-simple.c - OF glue layer for simple integrations * diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 3530795bbb8f..d86aaba9c618 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * dwc3-omap.c - OMAP Specific Glue layer * diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 261ee805763f..dd893208761f 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * dwc3-pci.c - PCI Specific glue layer * diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index 505676fd3ba4..3d635df22568 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /** * dwc3-st.c Support for dwc3 platform devices on ST Microelectronics platforms * diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index a4dceae4bdc7..7f5bdb318f89 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ep0.c - DesignWare USB3 DRD Controller Endpoint 0 Handling * diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 66dc99b9525a..8bba7ae12aae 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * gadget.c - DesignWare USB3 DRD Controller Gadget Framework Link * diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 4a3227543255..fe502e542c39 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * gadget.h - DesignWare USB3 DRD Gadget Header * diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 76f0b0df37c1..8eddbe17e99e 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * host.c - DesignWare USB3 DRD Controller Host Glue * diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index c69b06696824..085db0412ce0 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * io.h - DesignWare USB3 DRD IO Header * diff --git a/drivers/usb/dwc3/trace.c b/drivers/usb/dwc3/trace.c index 6cd166412ad0..31acdc3ce436 100644 --- a/drivers/usb/dwc3/trace.c +++ b/drivers/usb/dwc3/trace.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * trace.c - DesignWare USB3 DRD Controller Trace Support * diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 6504b116da04..9e6e10b2ea5c 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * trace.h - DesignWare USB3 DRD Controller Trace Support * diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c index e87ce8e9edee..5a6edbfc9e88 100644 --- a/drivers/usb/dwc3/ulpi.c +++ b/drivers/usb/dwc3/ulpi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * ulpi.c - DesignWare USB3 Controller's ULPI PHY interface * diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c index e2654443e8eb..d633c2abe5a4 100644 --- a/drivers/usb/early/ehci-dbgp.c +++ b/drivers/usb/early/ehci-dbgp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Standalone EHCI usb debug driver * diff --git a/drivers/usb/early/xhci-dbc.c b/drivers/usb/early/xhci-dbc.c index 12fe70beae69..6901a08a6866 100644 --- a/drivers/usb/early/xhci-dbc.c +++ b/drivers/usb/early/xhci-dbc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * xhci-dbc.c - xHCI debug capability early driver * diff --git a/drivers/usb/early/xhci-dbc.h b/drivers/usb/early/xhci-dbc.h index 2df0f6e613fe..24118d3d6e07 100644 --- a/drivers/usb/early/xhci-dbc.h +++ b/drivers/usb/early/xhci-dbc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xhci-dbc.h - xHCI debug capability early driver * diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 1f1ab6fa3f9d..a9c3904dbe1b 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * composite.c - infrastructure for Composite USB Gadgets * diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index 17a6077b89a4..80a75c038696 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * usb/gadget/config.c -- simplify building config descriptors * diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index aeb9f3c40521..4ddf063b9f47 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 30fdab0ae383..3a3b3027f234 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers * diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 5e3828d9dac7..5a2229784bd6 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_acm.c -- USB CDC serial (ACM) function driver * diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 4c488d15b6f6..d2a83dd78cb9 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_ecm.c -- USB CDC Ethernet (ECM) link function driver * diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c index 007ec6e4a5d4..bfb4cacc8b8e 100644 --- a/drivers/usb/gadget/function/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_eem.c -- USB CDC Ethernet (EEM) link function driver * diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 8b342587f8ad..eb4196fa9959 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_fs.c -- user mode file system API for USB composite function controllers * diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index d8e359ef6eb1..fc1d9282c1d9 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_hid.c -- USB HID function driver * diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index e70093835e14..2dea6e63ef4d 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_loopback.c - USB peripheral loopback configuration driver * diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 5153e29870c3..697224237976 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * f_mass_storage.c -- Mass Storage USB Composite Function * diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index 5d3d7941d2c2..09a69b2c8125 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_midi.c -- USB MIDI class function driver * diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 45b334ceaf2e..a45e6174a387 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_ncm.c -- USB CDC Network (NCM) link function driver * diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index d43e86cea74f..34e81d888b24 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_obex.c -- USB CDC OBEX function driver * diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index 710b6885c55b..b0c2ad17e3e3 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * f_phonet.c -- USB CDC Phonet function * diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index ea0da35a44e2..fff806d1e4e9 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_printer.c - USB printer function driver * diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index c7c5b3ce1d98..355e2308bd7a 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_rndis.c -- RNDIS link function driver * diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index cb00ada21d9c..9dae136d81e4 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_serial.c - generic USB serial function driver * diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index 8784fa12ea2c..4581b172ced7 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_sourcesink.c - USB peripheral source/sink configuration driver * diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index 434b983f3b4c..8e13415cf91e 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_subset.c -- "CDC Subset" Ethernet link function driver * diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index c9d741dfeff4..e4e86705a15e 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* Target based USB-Gadget * * UAS protocol handling, target callbacks, configfs handling, diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 29efbedc91f9..9dc33cbcc06c 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API) * diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c index 5d229e72912e..83d33ee3c40a 100644 --- a/drivers/usb/gadget/function/f_uac1_legacy.c +++ b/drivers/usb/gadget/function/f_uac1_legacy.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_audio.c -- USB Audio class function driver * diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index f05c3f3e6103..452b250be960 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_uac2.c -- USB Audio Class 2.0 Function * diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index f8a1881609a2..67af76361e7f 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * uvc_gadget.c -- USB Video Class Gadget driver * diff --git a/drivers/usb/gadget/function/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h index d0a73bdcbba1..b78d1af0aa9c 100644 --- a/drivers/usb/gadget/function/f_uvc.h +++ b/drivers/usb/gadget/function/f_uvc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * f_uvc.h -- USB Video Class Gadget driver * diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index d6341045c631..ac8a5476d968 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * RNDIS MSG parser * diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index 21e0430ffb98..f3bbd224cc16 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * RNDIS Definitions for Remote NDIS * diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c index 8fbf6861690d..1beb7ce507ce 100644 --- a/drivers/usb/gadget/function/storage_common.c +++ b/drivers/usb/gadget/function/storage_common.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * storage_common.c -- Common definitions for mass storage functionality * diff --git a/drivers/usb/gadget/function/u_audio.c b/drivers/usb/gadget/function/u_audio.c index 3971bbab88bd..efc5a918842b 100644 --- a/drivers/usb/gadget/function/u_audio.c +++ b/drivers/usb/gadget/function/u_audio.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * u_audio.c -- interface to USB gadget "ALSA sound card" utilities * diff --git a/drivers/usb/gadget/function/u_audio.h b/drivers/usb/gadget/function/u_audio.h index 07e13784cbb8..fae06fa2ef5d 100644 --- a/drivers/usb/gadget/function/u_audio.h +++ b/drivers/usb/gadget/function/u_audio.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * u_audio.h -- interface to USB gadget "ALSA sound card" utilities * diff --git a/drivers/usb/gadget/function/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h index 262cc03cc2c0..320cd890bb0e 100644 --- a/drivers/usb/gadget/function/u_ecm.h +++ b/drivers/usb/gadget/function/u_ecm.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_ecm.h * diff --git a/drivers/usb/gadget/function/u_eem.h b/drivers/usb/gadget/function/u_eem.h index e3ae97874c4f..b756664f9c5e 100644 --- a/drivers/usb/gadget/function/u_eem.h +++ b/drivers/usb/gadget/function/u_eem.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_eem.h * diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index bdbc3fdc7c4f..247eb69cb99b 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack * diff --git a/drivers/usb/gadget/function/u_ether.h b/drivers/usb/gadget/function/u_ether.h index c77145bd6b5b..015084eda722 100644 --- a/drivers/usb/gadget/function/u_ether.h +++ b/drivers/usb/gadget/function/u_ether.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * u_ether.h -- interface to USB gadget "ethernet link" utilities * diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h index e4c3f84af4c3..0f303fc288fa 100644 --- a/drivers/usb/gadget/function/u_ether_configfs.h +++ b/drivers/usb/gadget/function/u_ether_configfs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_ether_configfs.h * diff --git a/drivers/usb/gadget/function/u_fs.h b/drivers/usb/gadget/function/u_fs.h index 79f70ebf85dc..79a585ce51c1 100644 --- a/drivers/usb/gadget/function/u_fs.h +++ b/drivers/usb/gadget/function/u_fs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_fs.h * diff --git a/drivers/usb/gadget/function/u_gether.h b/drivers/usb/gadget/function/u_gether.h index d4078426ba5d..b9643d83b5cc 100644 --- a/drivers/usb/gadget/function/u_gether.h +++ b/drivers/usb/gadget/function/u_gether.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_gether.h * diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h index aaa0e368a159..5c9cae57b41a 100644 --- a/drivers/usb/gadget/function/u_hid.h +++ b/drivers/usb/gadget/function/u_hid.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_hid.h * diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h index 22510189758e..0b3e14bb9021 100644 --- a/drivers/usb/gadget/function/u_midi.h +++ b/drivers/usb/gadget/function/u_midi.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_midi.h * diff --git a/drivers/usb/gadget/function/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h index ce0f3a78ca13..e90054039f40 100644 --- a/drivers/usb/gadget/function/u_ncm.h +++ b/drivers/usb/gadget/function/u_ncm.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_ncm.h * diff --git a/drivers/usb/gadget/function/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h index 98ced18779ea..5387ed46f415 100644 --- a/drivers/usb/gadget/function/u_phonet.h +++ b/drivers/usb/gadget/function/u_phonet.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * u_phonet.h - interface to Phonet * diff --git a/drivers/usb/gadget/function/u_printer.h b/drivers/usb/gadget/function/u_printer.h index 8d30b7577f87..65737e58259a 100644 --- a/drivers/usb/gadget/function/u_printer.h +++ b/drivers/usb/gadget/function/u_printer.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_printer.h * diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index efdb7ac381d9..87d365d9cf5d 100644 --- a/drivers/usb/gadget/function/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_rndis.h * diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 961457ef5a1c..5188c4842f86 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * u_serial.c - utilities for USB gadget "serial port"/TTY support * diff --git a/drivers/usb/gadget/function/u_serial.h b/drivers/usb/gadget/function/u_serial.h index c20210c0babd..44617b205bc7 100644 --- a/drivers/usb/gadget/function/u_serial.h +++ b/drivers/usb/gadget/function/u_serial.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * u_serial.h - interface to USB gadget "serial port"/TTY utilities * diff --git a/drivers/usb/gadget/function/u_tcm.h b/drivers/usb/gadget/function/u_tcm.h index 0bd751e0483f..1ec6f702d400 100644 --- a/drivers/usb/gadget/function/u_tcm.h +++ b/drivers/usb/gadget/function/u_tcm.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_tcm.h * diff --git a/drivers/usb/gadget/function/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h index 6f188fd8633f..84e05cc42659 100644 --- a/drivers/usb/gadget/function/u_uac1.h +++ b/drivers/usb/gadget/function/u_uac1.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_uac1.h - Utility definitions for UAC1 function * diff --git a/drivers/usb/gadget/function/u_uac1_legacy.c b/drivers/usb/gadget/function/u_uac1_legacy.c index fa4684a1c54c..0f66c4b4f772 100644 --- a/drivers/usb/gadget/function/u_uac1_legacy.c +++ b/drivers/usb/gadget/function/u_uac1_legacy.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * u_uac1.c -- ALSA audio utilities for Gadget stack * diff --git a/drivers/usb/gadget/function/u_uac1_legacy.h b/drivers/usb/gadget/function/u_uac1_legacy.h index d715b1af56a4..ad77043567cd 100644 --- a/drivers/usb/gadget/function/u_uac1_legacy.h +++ b/drivers/usb/gadget/function/u_uac1_legacy.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities * diff --git a/drivers/usb/gadget/function/u_uac2.h b/drivers/usb/gadget/function/u_uac2.h index 19eeb83538a5..6ad31b29df8a 100644 --- a/drivers/usb/gadget/function/u_uac2.h +++ b/drivers/usb/gadget/function/u_uac2.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_uac2.h * diff --git a/drivers/usb/gadget/function/u_uvc.h b/drivers/usb/gadget/function/u_uvc.h index 4676b60a5063..ec5b5e6839b3 100644 --- a/drivers/usb/gadget/function/u_uvc.h +++ b/drivers/usb/gadget/function/u_uvc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_uvc.h * diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 11d70dead32b..f6388ddad19f 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * uvc_gadget.h -- USB Video Class Gadget driver * diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index 844cb738bafd..66f1312d50e7 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * uvc_configfs.c * diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h index 085e67be7c71..f604bcb241ea 100644 --- a/drivers/usb/gadget/function/uvc_configfs.h +++ b/drivers/usb/gadget/function/uvc_configfs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * uvc_configfs.h * diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index 6377e9fee6e5..6c819d9fa7af 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * uvc_queue.c -- USB Video Class driver - Buffers management * diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index 66124024278b..c1fb5249f4a4 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * uvc_v4l2.c -- USB Video Class Gadget driver * diff --git a/drivers/usb/gadget/function/uvc_v4l2.h b/drivers/usb/gadget/function/uvc_v4l2.h index ad6ca0671740..20cf417a56c0 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.h +++ b/drivers/usb/gadget/function/uvc_v4l2.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * uvc_v4l2.h -- USB Video Class Gadget driver * diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index 0f01c04d7cbd..29d81ac02391 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * uvc_video.c -- USB Video Class Gadget driver * diff --git a/drivers/usb/gadget/function/uvc_video.h b/drivers/usb/gadget/function/uvc_video.h index ef00f06fa00b..1c48a8bdca02 100644 --- a/drivers/usb/gadget/function/uvc_video.h +++ b/drivers/usb/gadget/function/uvc_video.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * uvc_video.h -- USB Video Class Gadget driver * diff --git a/drivers/usb/gadget/functions.c b/drivers/usb/gadget/functions.c index b13f839e7368..203361a64212 100644 --- a/drivers/usb/gadget/functions.c +++ b/drivers/usb/gadget/functions.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c index c39de65a448b..eed2cb6483b6 100644 --- a/drivers/usb/gadget/legacy/acm_ms.c +++ b/drivers/usb/gadget/legacy/acm_ms.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * acm_ms.c -- Composite driver, with ACM and mass storage support * diff --git a/drivers/usb/gadget/legacy/audio.c b/drivers/usb/gadget/legacy/audio.c index 1f5cdbe162df..d0bffaa0bc09 100644 --- a/drivers/usb/gadget/legacy/audio.c +++ b/drivers/usb/gadget/legacy/audio.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * audio.c -- Audio gadget driver * diff --git a/drivers/usb/gadget/legacy/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c index 51c08682de84..074c0d4efcf9 100644 --- a/drivers/usb/gadget/legacy/cdc2.c +++ b/drivers/usb/gadget/legacy/cdc2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * cdc2.c -- CDC Composite driver, with ECM and ACM support * diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index 99ca3dabc4f3..e1d566c9918a 100644 --- a/drivers/usb/gadget/legacy/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dbgp.c -- EHCI Debug Port device gadget * diff --git a/drivers/usb/gadget/legacy/ether.c b/drivers/usb/gadget/legacy/ether.c index 25a2c2e48592..8eb0043f6697 100644 --- a/drivers/usb/gadget/legacy/ether.c +++ b/drivers/usb/gadget/legacy/ether.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ether.c -- Ethernet gadget driver, with CDC and non-CDC options * diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index 6da7316f8e87..95db302cec7d 100644 --- a/drivers/usb/gadget/legacy/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * g_ffs.c -- user mode file system API for USB composite function controllers * diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c index 0bf39c3ccdb1..c03674b02718 100644 --- a/drivers/usb/gadget/legacy/gmidi.c +++ b/drivers/usb/gadget/legacy/gmidi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * gmidi.c -- USB MIDI Gadget Driver * diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c index a71a884f79fc..c9fb9a3c034f 100644 --- a/drivers/usb/gadget/legacy/hid.c +++ b/drivers/usb/gadget/legacy/hid.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * hid.c -- HID Composite driver * diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 5c28bee327e1..ae4fe683f7e0 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * inode.c -- user mode filesystem api for usb gadget controllers * diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index fcba59782f26..3700cd272d75 100644 --- a/drivers/usb/gadget/legacy/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * mass_storage.c -- Mass Storage USB Gadget * diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c index a70a406580ea..7dc276c68b54 100644 --- a/drivers/usb/gadget/legacy/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * multi.c -- Multifunction Composite driver * diff --git a/drivers/usb/gadget/legacy/ncm.c b/drivers/usb/gadget/legacy/ncm.c index 0aba68253e3d..7bfd306ea1ee 100644 --- a/drivers/usb/gadget/legacy/ncm.c +++ b/drivers/usb/gadget/legacy/ncm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ncm.c -- NCM gadget driver * diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c index b1e535f4022e..f10dd6e19cc9 100644 --- a/drivers/usb/gadget/legacy/nokia.c +++ b/drivers/usb/gadget/legacy/nokia.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * nokia.c -- Nokia Composite Gadget Driver * diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c index 4c9cfff34a03..6e1eef41ad86 100644 --- a/drivers/usb/gadget/legacy/printer.c +++ b/drivers/usb/gadget/legacy/printer.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * printer.c -- Printer gadget driver * diff --git a/drivers/usb/gadget/legacy/serial.c b/drivers/usb/gadget/legacy/serial.c index 9d89adce756d..e84cb9f97b5d 100644 --- a/drivers/usb/gadget/legacy/serial.c +++ b/drivers/usb/gadget/legacy/serial.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * serial.c -- USB gadget serial driver * diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c index 0b0bb98319cd..1089cb118b66 100644 --- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* Target based USB-Gadget * * UAS protocol handling, target callbacks, configfs handling, diff --git a/drivers/usb/gadget/legacy/webcam.c b/drivers/usb/gadget/legacy/webcam.c index 82c13fce9232..a3929a38ede9 100644 --- a/drivers/usb/gadget/legacy/webcam.c +++ b/drivers/usb/gadget/legacy/webcam.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * webcam.c -- USB webcam gadget driver * diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c index 3acc589dae98..6c77940091c5 100644 --- a/drivers/usb/gadget/legacy/zero.c +++ b/drivers/usb/gadget/legacy/zero.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * zero.c -- Gadget Zero, for USB development * diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c index 18839732c840..f52fb321d266 100644 --- a/drivers/usb/gadget/u_f.c +++ b/drivers/usb/gadget/u_f.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_f.c -- USB function utilities for Gadget stack * diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h index 7d53a4773d1a..35aaaaa80926 100644 --- a/drivers/usb/gadget/u_f.h +++ b/drivers/usb/gadget/u_f.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_f.h * diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h index 947b7ddff691..cfa53a204de1 100644 --- a/drivers/usb/gadget/u_os_desc.h +++ b/drivers/usb/gadget/u_os_desc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * u_os_desc.h * diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h index 4fe22d432af2..5a92388ef8bb 100644 --- a/drivers/usb/gadget/udc/amd5536udc.h +++ b/drivers/usb/gadget/udc/amd5536udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * amd5536.h -- header for AMD 5536 UDC high/full speed USB device controller * diff --git a/drivers/usb/gadget/udc/amd5536udc_pci.c b/drivers/usb/gadget/udc/amd5536udc_pci.c index 57a13f080a79..cf9117e84534 100644 --- a/drivers/usb/gadget/udc/amd5536udc_pci.c +++ b/drivers/usb/gadget/udc/amd5536udc_pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * amd5536udc_pci.c -- AMD 5536 UDC high/full speed USB device controller * diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c index 8bc78418d40e..972f78409df7 100644 --- a/drivers/usb/gadget/udc/at91_udc.c +++ b/drivers/usb/gadget/udc/at91_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * at91_udc -- driver for at91-series USB peripheral controller * diff --git a/drivers/usb/gadget/udc/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index 9bbe72764f31..9581a868032e 100644 --- a/drivers/usb/gadget/udc/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2004 by Thomas Rathbone, HP Labs * Copyright (C) 2005 by Ivan Kokshaysky diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index a884c022df7a..12543decf9ab 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for the Atmel USBA high speed USB device controller * diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h index f8ebe0389bd4..10df5e4aaeb2 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.h +++ b/drivers/usb/gadget/udc/atmel_usba_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for the Atmel USBA high speed USB device controller * diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index f78503203f42..403cb339fd7b 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bcm63xx_udc.c -- BCM63xx UDC high/full speed USB device controller * diff --git a/drivers/usb/gadget/udc/bdc/bdc.h b/drivers/usb/gadget/udc/bdc/bdc.h index 6df0352cdc50..960620bccc25 100644 --- a/drivers/usb/gadget/udc/bdc/bdc.h +++ b/drivers/usb/gadget/udc/bdc/bdc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bdc.h - header for the BRCM BDC USB3.0 device controller * diff --git a/drivers/usb/gadget/udc/bdc/bdc_cmd.c b/drivers/usb/gadget/udc/bdc/bdc_cmd.c index 6e920f1dce02..ad3240375f87 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_cmd.c +++ b/drivers/usb/gadget/udc/bdc/bdc_cmd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bdc_cmd.c - BRCM BDC USB3.0 device controller * diff --git a/drivers/usb/gadget/udc/bdc/bdc_cmd.h b/drivers/usb/gadget/udc/bdc/bdc_cmd.h index 61d0e3bf9853..64648fbef233 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_cmd.h +++ b/drivers/usb/gadget/udc/bdc/bdc_cmd.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bdc_cmd.h - header for the BDC debug functions * diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 7a8af4b916cf..2ab6a6b45f9e 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bdc_core.c - BRCM BDC USB3.0 device controller core operations * diff --git a/drivers/usb/gadget/udc/bdc/bdc_dbg.c b/drivers/usb/gadget/udc/bdc/bdc_dbg.c index ac98f6f681b7..11216cd6cb94 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_dbg.c +++ b/drivers/usb/gadget/udc/bdc/bdc_dbg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bdc_dbg.c - BRCM BDC USB3.0 device controller debug functions * diff --git a/drivers/usb/gadget/udc/bdc/bdc_dbg.h b/drivers/usb/gadget/udc/bdc/bdc_dbg.h index 338a6c701315..f62d59b30a3e 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_dbg.h +++ b/drivers/usb/gadget/udc/bdc/bdc_dbg.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bdc_dbg.h - header for the BDC debug functions * diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index bfd8f7ade935..e9fda8e6e87d 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bdc_ep.c - BRCM BDC USB3.0 device controller endpoint related functions * diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.h b/drivers/usb/gadget/udc/bdc/bdc_ep.h index 8a6b36cbf2ea..db52fc78c8bf 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.h +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bdc_ep.h - header for the BDC debug functions * diff --git a/drivers/usb/gadget/udc/bdc/bdc_pci.c b/drivers/usb/gadget/udc/bdc/bdc_pci.c index 02968842b359..8eca33c545b1 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_pci.c +++ b/drivers/usb/gadget/udc/bdc/bdc_pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bdc_pci.c - BRCM BDC USB3.0 device controller PCI interface file. * diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c index c84346146456..492b8b872d2c 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_udc.c +++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * bdc_udc.c - BRCM BDC USB3.0 device controller gagdet ops * diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 54b02eca0456..4fb5ca05278e 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * udc.c - Core UDC Framework * diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 9dd26ff2a4ff..b5ded16d1f42 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * dummy_hcd.c -- Dummy/Loopback USB host and device emulator driver. * diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c index 78d0204e3e20..6a7e0e26a1d1 100644 --- a/drivers/usb/gadget/udc/fotg210-udc.c +++ b/drivers/usb/gadget/udc/fotg210-udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * FOTG210 UDC Driver supports Bulk transfer so far * diff --git a/drivers/usb/gadget/udc/fotg210.h b/drivers/usb/gadget/udc/fotg210.h index bbf991bcbe7c..2c825a884ebc 100644 --- a/drivers/usb/gadget/udc/fotg210.h +++ b/drivers/usb/gadget/udc/fotg210.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Faraday FOTG210 USB OTG controller * diff --git a/drivers/usb/gadget/udc/fsl_mxc_udc.c b/drivers/usb/gadget/udc/fsl_mxc_udc.c index f16e149c5b3e..089fbfc44da7 100644 --- a/drivers/usb/gadget/udc/fsl_mxc_udc.c +++ b/drivers/usb/gadget/udc/fsl_mxc_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2009 * Guennadi Liakhovetski, DENX Software Engineering, diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index a3e72d690eef..228577c6c180 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * driver/usb/gadget/fsl_qe_udc.c * diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.h b/drivers/usb/gadget/udc/fsl_qe_udc.h index 7026919fc901..2b1aec81c397 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.h +++ b/drivers/usb/gadget/udc/fsl_qe_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * drivers/usb/gadget/qe_udc.h * diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c index 6f2f71c054be..9be768d04cfa 100644 --- a/drivers/usb/gadget/udc/fsl_udc_core.c +++ b/drivers/usb/gadget/udc/fsl_udc_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2004-2007,2011-2012 Freescale Semiconductor, Inc. * All rights reserved. diff --git a/drivers/usb/gadget/udc/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h index e92b8408b6f6..e5a25ef5803b 100644 --- a/drivers/usb/gadget/udc/fsl_usb2_udc.h +++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2004,2012 Freescale Semiconductor, Inc * All rights reserved. diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index e0c1b0099265..e05946c421ed 100644 --- a/drivers/usb/gadget/udc/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Fusb300 UDC (USB gadget) * diff --git a/drivers/usb/gadget/udc/fusb300_udc.h b/drivers/usb/gadget/udc/fusb300_udc.h index ad39f892d200..4b055ef31cc1 100644 --- a/drivers/usb/gadget/udc/fusb300_udc.h +++ b/drivers/usb/gadget/udc/fusb300_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Fusb300 UDC (USB gadget) * diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c index cccad51eb999..11dfcc82abdf 100644 --- a/drivers/usb/gadget/udc/goku_udc.c +++ b/drivers/usb/gadget/udc/goku_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Toshiba TC86C001 ("Goku-S") USB Device Controller driver * diff --git a/drivers/usb/gadget/udc/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h index 86d2adafe149..6ac811feeee4 100644 --- a/drivers/usb/gadget/udc/goku_udc.h +++ b/drivers/usb/gadget/udc/goku_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Toshiba TC86C001 ("Goku-S") USB Device Controller driver * diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c index 48117a539146..ed818b118a82 100644 --- a/drivers/usb/gadget/udc/gr_udc.c +++ b/drivers/usb/gadget/udc/gr_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC. * diff --git a/drivers/usb/gadget/udc/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h index 4297c4e8021f..6c08ddf03521 100644 --- a/drivers/usb/gadget/udc/gr_udc.h +++ b/drivers/usb/gadget/udc/gr_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB Peripheral Controller driver for Aeroflex Gaisler GRUSBDC. * diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c index 8f32b5ee7734..7dcd0904bf25 100644 --- a/drivers/usb/gadget/udc/lpc32xx_udc.c +++ b/drivers/usb/gadget/udc/lpc32xx_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB Gadget driver for LPC32xx * diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index 3b8dbed7e2e9..ca5c98226ad2 100644 --- a/drivers/usb/gadget/udc/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * M66592 UDC (USB gadget) * diff --git a/drivers/usb/gadget/udc/m66592-udc.h b/drivers/usb/gadget/udc/m66592-udc.h index 96d49d7bfb6b..4a62b4fda942 100644 --- a/drivers/usb/gadget/udc/m66592-udc.h +++ b/drivers/usb/gadget/udc/m66592-udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * M66592 UDC (USB gadget) * diff --git a/drivers/usb/gadget/udc/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h index e32a787ac373..4c7812429920 100644 --- a/drivers/usb/gadget/udc/mv_u3d.h +++ b/drivers/usb/gadget/udc/mv_u3d.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2011 Marvell International Ltd. All rights reserved. * diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index 772049afe166..6f336fe8bbef 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2011 Marvell International Ltd. All rights reserved. * diff --git a/drivers/usb/gadget/udc/mv_udc.h b/drivers/usb/gadget/udc/mv_udc.h index be77f207dbaf..4acf7edf4d86 100644 --- a/drivers/usb/gadget/udc/mv_udc.h +++ b/drivers/usb/gadget/udc/mv_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2011 Marvell International Ltd. All rights reserved. * diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 4103bf7cf52a..df4065cf5fcd 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2011 Marvell International Ltd. All rights reserved. * Author: Chao Xie diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 8f85a51bd2b3..a3018f93df58 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for PLX NET2272 USB device controller * diff --git a/drivers/usb/gadget/udc/net2272.h b/drivers/usb/gadget/udc/net2272.h index 69bc9c3c6ce4..f0212cf042a2 100644 --- a/drivers/usb/gadget/udc/net2272.h +++ b/drivers/usb/gadget/udc/net2272.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * PLX NET2272 high/full speed USB device controller * diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index f608c1f85e61..a0b2ab0c04f7 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for the PLX NET2280 USB device controller. * Specs and errata are available from . diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index 1088c3745999..18a881e7f93f 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * NetChip 2280 high/full speed USB device controller. * Unlike many such controllers, this one talks PCI. diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index f05ba6825bfe..5531ea492ed2 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * omap_udc.c -- for OMAP full speed udc; most chips support OTG. * diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 84dcbcd756f0..cc24334504b8 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2011 LAPIS Semiconductor Co., Ltd. * diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c index 974b778e033c..1f36abdfac96 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.c +++ b/drivers/usb/gadget/udc/pxa25x_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Intel PXA25x and IXP4xx on-chip full speed USB device controllers * diff --git a/drivers/usb/gadget/udc/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h index a458bec2536d..1532e7e71f99 100644 --- a/drivers/usb/gadget/udc/pxa25x_udc.h +++ b/drivers/usb/gadget/udc/pxa25x_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Intel PXA25x on-chip full speed USB device controller * diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index d48e239660c3..14606f340325 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Handles the Intel 27x USB Device Controller (UDC) * diff --git a/drivers/usb/gadget/udc/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h index cea2cb79b30c..cfdece686abe 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.h +++ b/drivers/usb/gadget/udc/pxa27x_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * linux/drivers/usb/gadget/pxa27x_udc.h * Intel PXA27x on-chip full speed USB device controller diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index bb844b94df10..75c9e94ecd59 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * R8A66597 UDC (USB gadget) * diff --git a/drivers/usb/gadget/udc/r8a66597-udc.h b/drivers/usb/gadget/udc/r8a66597-udc.h index 45c4b2df1785..0f6d41e61841 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.h +++ b/drivers/usb/gadget/udc/r8a66597-udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * R8A66597 UDC * diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index cb461b8f4509..3d4b885e7958 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas USB3.0 Peripheral driver (USB gadget) * diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c index 42587b738a1f..9707b945eef2 100644 --- a/drivers/usb/gadget/udc/s3c-hsudc.c +++ b/drivers/usb/gadget/udc/s3c-hsudc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* linux/drivers/usb/gadget/s3c-hsudc.c * * Copyright (c) 2010 Samsung Electronics Co., Ltd. diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 394abd5d65c0..ed874cabd339 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * linux/drivers/usb/gadget/s3c2410_udc.c * diff --git a/drivers/usb/gadget/udc/s3c2410_udc.h b/drivers/usb/gadget/udc/s3c2410_udc.h index 93bf225f1969..cfabc83c2244 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.h +++ b/drivers/usb/gadget/udc/s3c2410_udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * linux/drivers/usb/gadget/s3c2410_udc.h * Samsung on-chip full speed USB device controllers diff --git a/drivers/usb/gadget/udc/snps_udc_core.c b/drivers/usb/gadget/udc/snps_udc_core.c index 2f5e788dd978..a9569080c30d 100644 --- a/drivers/usb/gadget/udc/snps_udc_core.c +++ b/drivers/usb/gadget/udc/snps_udc_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * amd5536.c -- AMD 5536 UDC high/full speed USB device controller * diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index e8a5fdaee37d..800a35b48ab1 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * snps_udc_plat.c - Synopsys UDC Platform Driver * diff --git a/drivers/usb/gadget/udc/trace.c b/drivers/usb/gadget/udc/trace.c index 8c551ab91ad8..fbc139292245 100644 --- a/drivers/usb/gadget/udc/trace.c +++ b/drivers/usb/gadget/udc/trace.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * trace.c - USB Gadget Framework Trace Support * diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h index da29874b5366..06b162bcdb54 100644 --- a/drivers/usb/gadget/udc/trace.h +++ b/drivers/usb/gadget/udc/trace.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * udc.c - Core UDC Framework * diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index de207a90571e..374a75d68365 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Xilinx USB peripheral controller driver * diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c index 73a4dfba0edb..6c7a4e0c45c8 100644 --- a/drivers/usb/gadget/usbstring.c +++ b/drivers/usb/gadget/usbstring.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: LGPL-2.1+ /* * Copyright (C) 2003 David Brownell * diff --git a/drivers/usb/host/bcma-hcd.c b/drivers/usb/host/bcma-hcd.c index 5f425c89faf1..0f595c630600 100644 --- a/drivers/usb/host/bcma-hcd.c +++ b/drivers/usb/host/bcma-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Broadcom specific Advanced Microcontroller Bus * Broadcom USB-core driver (BCMA bus glue) diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 2a8b9bdc0e57..50beacc581de 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for EHCI UHP on Atmel chips * diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index cbb9b8e12c3c..7fb21d01b3d0 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2001-2002 by David Brownell * diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 26b641100639..0a131659fc33 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * SAMSUNG EXYNOS USB HOST EHCI Controller * diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index d025cc06dda7..7c4bb32230d2 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2005-2009 MontaVista Software, Inc. * Copyright 2008,2012,2015 Freescale Semiconductor, Inc. diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index 1a8a60a57cf2..21a6f10b5e3a 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* Copyright (C) 2005-2010,2012 Freescale Semiconductor, Inc. * Copyright (c) 2005 MontaVista Software * diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index 21650044b09e..a8cffcf13451 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Aeroflex Gaisler GRLIB GRUSBHC EHCI host controller * diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c560a01f4971..74d82148954d 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Enhanced Host Controller Interface (EHCI) driver for USB. * diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index df169c8e7225..b94793b56270 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2001-2004 by David Brownell * diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c index 9b7e63977215..212d2042fb9f 100644 --- a/drivers/usb/host/ehci-mem.c +++ b/drivers/usb/host/ehci-mem.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2001 by David Brownell * diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index 849806a75f1c..c9e15225a30f 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2011 Marvell International Ltd. All rights reserved. * Author: Chao Xie diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index c7a9b31eeaef..67adea97c26c 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2008 Sascha Hauer , Pengutronix * Copyright (c) 2009 Daniel Mack diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 4d308533bc83..dd319d3219b6 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ehci-omap.c - driver for USBHOST on OMAP3/4 processors * diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 1aec87ec68df..199a6d2778dd 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * drivers/usb/host/ehci-orion.c * diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 93326974ff4b..f6015f6ca488 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * EHCI HCD (Host Controller Driver) PCI Bus Glue. * diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index a41acd661c46..080014197f25 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Generic platform ehci driver * diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index 342816a7f8b1..9a05bff230bb 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * PMC MSP EHCI (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 1a10c8d542ca..576f7d79ad4e 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * EHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 7934ff9b35e1..c74066790e69 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * PS3 EHCI Host Controller driver * diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 8f3f055c05fa..c0074f212a09 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2001-2004 by David Brownell * diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 6bc6304672bc..ebbc2c60de89 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2001-2004 by David Brownell * Copyright (c) 2003 Michal Sojka, for high-speed iso transfers diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 5caf88d679e4..d565f24ca7f5 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * SuperH EHCI host controller driver * diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 1f25c7985f5b..d12259d06f53 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for EHCI HCD on SPEAr SOC * diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c index be4a2788fc58..336e9fa5274f 100644 --- a/drivers/usb/host/ehci-st.c +++ b/drivers/usb/host/ehci-st.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ST EHCI driver * diff --git a/drivers/usb/host/ehci-sysfs.c b/drivers/usb/host/ehci-sysfs.c index 5216f2b09d63..16669619cfc5 100644 --- a/drivers/usb/host/ehci-sysfs.c +++ b/drivers/usb/host/ehci-sysfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2007 by Alan Stern * diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 9a3d7db5be57..fe8423e17877 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * EHCI-compliant USB host controller driver for NVIDIA Tegra SoCs * diff --git a/drivers/usb/host/ehci-tilegx.c b/drivers/usb/host/ehci-tilegx.c index bdb93b6a356f..d41b3d217253 100644 --- a/drivers/usb/host/ehci-tilegx.c +++ b/drivers/usb/host/ehci-tilegx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2012 Tilera Corporation. All Rights Reserved. * diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 0b6cdb723192..047a5b131717 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 by Alan Stern * diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index 63b9d0c67963..da2c99d3ba6b 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * linux/driver/usb/host/ehci-w90x900.c * diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index f54480850bb8..886b05678de9 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * EHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index a8e36170d8b8..1794d6254cfc 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2001-2002 by David Brownell * diff --git a/drivers/usb/host/fhci-dbg.c b/drivers/usb/host/fhci-dbg.c index b58e7a60913a..9935f10ad407 100644 --- a/drivers/usb/host/fhci-dbg.c +++ b/drivers/usb/host/fhci-dbg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale QUICC Engine USB Host Controller Driver * diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 55a0ae6f2d74..763131134ab1 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale QUICC Engine USB Host Controller Driver * diff --git a/drivers/usb/host/fhci-hub.c b/drivers/usb/host/fhci-hub.c index 60d55eb3de0d..d50a9ca15830 100644 --- a/drivers/usb/host/fhci-hub.c +++ b/drivers/usb/host/fhci-hub.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale QUICC Engine USB Host Controller Driver * diff --git a/drivers/usb/host/fhci-mem.c b/drivers/usb/host/fhci-mem.c index b0b88f57a5ac..532a5960ff48 100644 --- a/drivers/usb/host/fhci-mem.c +++ b/drivers/usb/host/fhci-mem.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale QUICC Engine USB Host Controller Driver * diff --git a/drivers/usb/host/fhci-q.c b/drivers/usb/host/fhci-q.c index 03be7494a476..664e1f98d68f 100644 --- a/drivers/usb/host/fhci-q.c +++ b/drivers/usb/host/fhci-q.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale QUICC Engine USB Host Controller Driver * diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c index 2f162faabbca..c8f3de90f464 100644 --- a/drivers/usb/host/fhci-sched.c +++ b/drivers/usb/host/fhci-sched.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale QUICC Engine USB Host Controller Driver * diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c index f82ad5df1b0d..fa54315064da 100644 --- a/drivers/usb/host/fhci-tds.c +++ b/drivers/usb/host/fhci-tds.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale QUICC Engine USB Host Controller Driver * diff --git a/drivers/usb/host/fhci.h b/drivers/usb/host/fhci.h index 3fc82c1c3c73..257c04c8af0c 100644 --- a/drivers/usb/host/fhci.h +++ b/drivers/usb/host/fhci.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Freescale QUICC Engine USB Host Controller Driver * diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 33a4f7ed0d7d..302b66fc6cfa 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* Faraday FOTG210 EHCI-like driver * * Copyright (c) 2013 Faraday Technology Corporation diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index ba557cdba8ef..c749cbd8dd3c 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Setup platform devices needed by the Freescale multi-port host * and/or dual-role USB controller modules based on the description diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index da3b18038d23..e5fda058b5d6 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Host Wire Adapter: * Driver glue, HWA-specific functions, bridges to WAHC and WUSBHC diff --git a/drivers/usb/host/imx21-dbg.c b/drivers/usb/host/imx21-dbg.c index 4f320d050da7..d6a72acceacf 100644 --- a/drivers/usb/host/imx21-dbg.c +++ b/drivers/usb/host/imx21-dbg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2009 by Martin Fuzzey * diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index 39ae7fb64b6f..4dbf28bc2652 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB Host Controller Driver for IMX21 * diff --git a/drivers/usb/host/imx21-hcd.h b/drivers/usb/host/imx21-hcd.h index 05122f8a6983..768e714bcb30 100644 --- a/drivers/usb/host/imx21-hcd.h +++ b/drivers/usb/host/imx21-hcd.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Macros and prototypes for i.MX21 * diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index ebf2ff239539..5f9234b9cf7b 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ISP116x HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index a822de4f36a1..c7e60b662e9b 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ISP1362 HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 02bbf8938bb9..afa321ab55fc 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MAX3421 Host Controller driver for USB. * diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 5302f988e7e6..5ad9e9bdc8ee 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index 05da2cb59612..2056573c2b12 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index c3eded317495..ac7d4ac34b02 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 6865b919403f..a12cbb295425 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * SAMSUNG EXYNOS USB HOST OHCI Controller * diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 15ec8f90aa6d..9902bc3c15fe 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Open Host Controller Interface (OHCI) driver for USB. * diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index 248eb7702463..fb7aaa3b9d06 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c index ed8a762b8670..b3da3f12e5b1 100644 --- a/drivers/usb/host/ohci-mem.c +++ b/drivers/usb/host/ohci-mem.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 6df8e2ed40fd..5509b50bc417 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * driver for NXP USB Host devices * diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 91393ec7d850..0201c49bc4fc 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index a84aebe9b0a9..fbcd34911025 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 05ebde6adf1e..56be56c1ab63 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Generic platform ohci driver * diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 4f87a5c61b08..76a9b40b08f1 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 71d8bc4c27f6..bb0375d9eef0 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * PS3 OHCI Host Controller driver * diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 21c010ffb03c..3e2474959735 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 641fed609911..b2ec8c399363 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index b006b93126f7..4511e27e9da8 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c index 8758c73215d7..ebec9a7699e3 100644 --- a/drivers/usb/host/ohci-sa1111.c +++ b/drivers/usb/host/ohci-sa1111.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index d4e0f7cd96fa..c9233cddf9a2 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 56478ed2f932..b3554f70bd27 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c index 02816a1515a1..697e6d95bb7e 100644 --- a/drivers/usb/host/ohci-st.c +++ b/drivers/usb/host/ohci-st.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ST OHCI driver * diff --git a/drivers/usb/host/ohci-tilegx.c b/drivers/usb/host/ohci-tilegx.c index e1b208da460a..e5a9f68cd648 100644 --- a/drivers/usb/host/ohci-tilegx.c +++ b/drivers/usb/host/ohci-tilegx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2012 Tilera Corporation. All Rights Reserved. * diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c index 16d081a093bb..5702964408db 100644 --- a/drivers/usb/host/ohci-tmio.c +++ b/drivers/usb/host/ohci-tmio.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * OHCI HCD(Host Controller Driver) for USB. * diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h index 12742d002d2d..508a803139dd 100644 --- a/drivers/usb/host/ohci.h +++ b/drivers/usb/host/ohci.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * OHCI HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 9f8c61eafd45..08a72f658305 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2008 Rodolfo Giometti * Copyright (c) 2008 Eurotech S.p.A. diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 6731f8d8d4c9..161536717025 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * This file contains code to reset and initialize USB host controllers. * Some of it includes work-arounds for PCI hardware and BIOS quirks. diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 0f3d2aedaec5..46d595f1b2eb 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * R8A66597 HCD (Host Controller Driver) * diff --git a/drivers/usb/host/r8a66597.h b/drivers/usb/host/r8a66597.h index b8406c07f363..cc8241c7d653 100644 --- a/drivers/usb/host/r8a66597.h +++ b/drivers/usb/host/r8a66597.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * R8A66597 HCD (Host Controller Driver) * diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index 24ad1d6cec25..601fb00603cc 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * SL811HS HCD (Host Controller Driver) for USB. * diff --git a/drivers/usb/host/sl811_cs.c b/drivers/usb/host/sl811_cs.c index 88a9bffe93df..72136373ffab 100644 --- a/drivers/usb/host/sl811_cs.c +++ b/drivers/usb/host/sl811_cs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * PCMCIA driver for SL811HS (as found in REX-CFU1U) * Filename: sl811_cs.c diff --git a/drivers/usb/host/ssb-hcd.c b/drivers/usb/host/ssb-hcd.c index 62b6b7804c66..2f9087dc4cab 100644 --- a/drivers/usb/host/ssb-hcd.c +++ b/drivers/usb/host/ssb-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Sonics Silicon Backplane * Broadcom USB-core driver (SSB bus glue) diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index c38855aed62c..228d22bfb36e 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Host Controller Driver for the Elan Digital Systems U132 adapter * diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index c3267a78c94e..babeefd84ffd 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Universal Host Controller Interface driver for USB. * diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c index 773249306031..81a6286f50cf 100644 --- a/drivers/usb/host/whci/asl.c +++ b/drivers/usb/host/whci/asl.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) asynchronous schedule management. * diff --git a/drivers/usb/host/whci/debug.c b/drivers/usb/host/whci/debug.c index 774b89d28fae..3cbd84893b6f 100644 --- a/drivers/usb/host/whci/debug.c +++ b/drivers/usb/host/whci/debug.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) debug. * diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index cf84269c3e6d..eb30567fa6d1 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) driver. * diff --git a/drivers/usb/host/whci/hw.c b/drivers/usb/host/whci/hw.c index 6afa2e379160..2a89686d6971 100644 --- a/drivers/usb/host/whci/hw.c +++ b/drivers/usb/host/whci/hw.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) hardware access helpers. * diff --git a/drivers/usb/host/whci/init.c b/drivers/usb/host/whci/init.c index ad8eb575c30a..48a6f50df24a 100644 --- a/drivers/usb/host/whci/init.c +++ b/drivers/usb/host/whci/init.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) initialization. * diff --git a/drivers/usb/host/whci/int.c b/drivers/usb/host/whci/int.c index 0c086b2790d1..15a2df0b29ab 100644 --- a/drivers/usb/host/whci/int.c +++ b/drivers/usb/host/whci/int.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) interrupt handling. * diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c index 33c5580b4d25..bafac6a88551 100644 --- a/drivers/usb/host/whci/pzl.c +++ b/drivers/usb/host/whci/pzl.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) periodic schedule management. * diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c index c0e6812426b3..1a92d0e492a0 100644 --- a/drivers/usb/host/whci/qset.c +++ b/drivers/usb/host/whci/qset.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) qset management. * diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h index c80c7d93bc4a..4712972682fe 100644 --- a/drivers/usb/host/whci/whcd.h +++ b/drivers/usb/host/whci/whcd.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) private header. * diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h index 4d4cbc0730bf..5dfbc9837b00 100644 --- a/drivers/usb/host/whci/whci-hc.h +++ b/drivers/usb/host/whci/whci-hc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) data structures. * diff --git a/drivers/usb/host/whci/wusb.c b/drivers/usb/host/whci/wusb.c index 8d2762682869..8c8d8bc8eac4 100644 --- a/drivers/usb/host/whci/wusb.c +++ b/drivers/usb/host/whci/wusb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless Host Controller (WHC) WUSB operations. * diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 2c83b37ae8f2..83904170be5c 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver * diff --git a/drivers/usb/host/xhci-ext-caps.h b/drivers/usb/host/xhci-ext-caps.h index 28deea584884..259963bbe3aa 100644 --- a/drivers/usb/host/xhci-ext-caps.h +++ b/drivers/usb/host/xhci-ext-caps.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver * diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 3693b1f487e5..53209113e170 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver * diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 57be885fb544..4df7c2ea3e1c 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver * diff --git a/drivers/usb/host/xhci-mtk-sch.c b/drivers/usb/host/xhci-mtk-sch.c index bfc51bc902b8..6945350671e2 100644 --- a/drivers/usb/host/xhci-mtk-sch.c +++ b/drivers/usb/host/xhci-mtk-sch.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2015 MediaTek Inc. * Author: diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 19d27ce31fdc..371524ea0739 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MediaTek xHCI Host Controller Driver * diff --git a/drivers/usb/host/xhci-mtk.h b/drivers/usb/host/xhci-mtk.h index 45ff5c67efb5..303723e3f885 100644 --- a/drivers/usb/host/xhci-mtk.h +++ b/drivers/usb/host/xhci-mtk.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2015 MediaTek Inc. * Author: diff --git a/drivers/usb/host/xhci-mvebu.c b/drivers/usb/host/xhci-mvebu.c index 85908a3ecb8f..fe7a2f84faeb 100644 --- a/drivers/usb/host/xhci-mvebu.c +++ b/drivers/usb/host/xhci-mvebu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2014 Marvell * Author: Gregory CLEMENT diff --git a/drivers/usb/host/xhci-mvebu.h b/drivers/usb/host/xhci-mvebu.h index 301fc984cae6..619792ae75b8 100644 --- a/drivers/usb/host/xhci-mvebu.h +++ b/drivers/usb/host/xhci-mvebu.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2014 Marvell * diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 76f392954733..4281bbee0544 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver PCI Bus Glue. * diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 1969e56a8d8d..72505e602527 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xhci-plat.c - xHCI host controller driver platform Bus Glue. * diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index 29b227895b07..ac8f8eb0bf49 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xhci-plat.h - xHCI host controller driver platform Bus Glue. * diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index 198bc188ab25..bde650594b70 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver for R-Car SoCs * diff --git a/drivers/usb/host/xhci-rcar.h b/drivers/usb/host/xhci-rcar.h index d247951147a1..162706528c4c 100644 --- a/drivers/usb/host/xhci-rcar.h +++ b/drivers/usb/host/xhci-rcar.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * drivers/usb/host/xhci-rcar.h * diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 521d19e82494..fb07211babd8 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver * diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 39b6e93130d3..40e18a7ffce9 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * NVIDIA Tegra xHCI host controller driver * diff --git a/drivers/usb/host/xhci-trace.c b/drivers/usb/host/xhci-trace.c index 367b630bdb3c..0be3e83025ae 100644 --- a/drivers/usb/host/xhci-trace.c +++ b/drivers/usb/host/xhci-trace.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver * diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 754dfb0e1a02..846f2859fa4a 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver * diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 05db6e977ba1..ab9dc0a5af9e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver * diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 86df906aec46..dd8bcdf64a9f 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller driver diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index 185c4e2db2a1..bdbaf6bc4f38 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * copyright (C) 1999/2000 by Henning Zabel * diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c index 0b21ba757bba..9f2f563c82ed 100644 --- a/drivers/usb/image/microtek.c +++ b/drivers/usb/image/microtek.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* Driver for Microtek Scanmaker X6 USB scanner, and possibly others. * * (C) Copyright 2000 John Fremlin diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c index bfa402cf3a27..8157d18135b8 100644 --- a/drivers/usb/isp1760/isp1760-core.c +++ b/drivers/usb/isp1760/isp1760-core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for the NXP ISP1760 chip * diff --git a/drivers/usb/isp1760/isp1760-core.h b/drivers/usb/isp1760/isp1760-core.h index c70f8368a794..47985161ee77 100644 --- a/drivers/usb/isp1760/isp1760-core.h +++ b/drivers/usb/isp1760/isp1760-core.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for the NXP ISP1760 chip * diff --git a/drivers/usb/isp1760/isp1760-regs.h b/drivers/usb/isp1760/isp1760-regs.h index b67095c9a9d4..eecdb76c132c 100644 --- a/drivers/usb/isp1760/isp1760-regs.h +++ b/drivers/usb/isp1760/isp1760-regs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for the NXP ISP1760 chip * diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index ad566f27dffe..6f1b4fdee802 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for the NXP ISP1761 device controller * diff --git a/drivers/usb/isp1760/isp1760-udc.h b/drivers/usb/isp1760/isp1760-udc.h index 26899ed81145..ea1598df1f11 100644 --- a/drivers/usb/isp1760/isp1760-udc.h +++ b/drivers/usb/isp1760/isp1760-udc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for the NXP ISP1761 device controller * diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 1c0ada75c35d..092db5ae5fa1 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * adutux - driver for ADU devices from Ontrak Control Systems * This is an experimental driver. Use at your own risk. diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index 8efdc500e790..b75defc52c24 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Apple Cinema Display driver * diff --git a/drivers/usb/misc/chaoskey.c b/drivers/usb/misc/chaoskey.c index abec6e604a62..8a22b4997c5f 100644 --- a/drivers/usb/misc/chaoskey.c +++ b/drivers/usb/misc/chaoskey.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * chaoskey - driver for ChaosKey device from Altus Metrum. * diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index 5c93a888c40e..819ad8dc2376 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * cypress_cy7c63.c * diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c index 63207c42acf6..a65f8817ecf8 100644 --- a/drivers/usb/misc/cytherm.c +++ b/drivers/usb/misc/cytherm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* -*- linux-c -*- * Cypress USB Thermometer driver * diff --git a/drivers/usb/misc/ehset.c b/drivers/usb/misc/ehset.c index c31b4a33e6bb..8b8e25424aa1 100644 --- a/drivers/usb/misc/ehset.c +++ b/drivers/usb/misc/ehset.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved. * diff --git a/drivers/usb/misc/emi26.c b/drivers/usb/misc/emi26.c index 8950fa5e973d..81836e9d787f 100644 --- a/drivers/usb/misc/emi26.c +++ b/drivers/usb/misc/emi26.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Emagic EMI 2|6 usb audio interface firmware loader. * Copyright (C) 2002 diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c index 1d9be4431b72..fad894a63c52 100644 --- a/drivers/usb/misc/emi62.c +++ b/drivers/usb/misc/emi62.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Emagic EMI 2|6 usb audio interface firmware loader. * Copyright (C) 2002 diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c index 837208f14f86..c9be0d484e5c 100644 --- a/drivers/usb/misc/ezusb.c +++ b/drivers/usb/misc/ezusb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * EZ-USB specific functions used by some of the USB to Serial drivers. * diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 424ff12f3b51..c1fbc2a32eb2 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB FTDI client driver for Elan Digital Systems's Uxxx adapters * diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c index 39d8fedfaf3b..8d144903f05e 100644 --- a/drivers/usb/misc/idmouse.c +++ b/drivers/usb/misc/idmouse.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* Siemens ID Mouse driver v0.6 This program is free software; you can redistribute it and/or diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index be5881303681..ad3109490c0f 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Native support for the I/O-Warrior USB devices * diff --git a/drivers/usb/misc/isight_firmware.c b/drivers/usb/misc/isight_firmware.c index 1c61830e96f9..91c028f16d31 100644 --- a/drivers/usb/misc/isight_firmware.c +++ b/drivers/usb/misc/isight_firmware.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for loading USB isight firmware * diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 680bddb3ce05..0f01dd3b59d0 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /** * Generic USB driver for report based interrupt in/out devices * like LD Didactic's USB devices. LD Didactic's USB devices are diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index 5628f678ab59..cd4d49d8aea5 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * LEGO USB Tower driver * diff --git a/drivers/usb/misc/lvstest.c b/drivers/usb/misc/lvstest.c index ddddd6387f66..5e5d128e16c0 100644 --- a/drivers/usb/misc/lvstest.c +++ b/drivers/usb/misc/lvstest.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * drivers/usb/misc/lvstest.c * diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c index ddfebb144aaa..84bd682e9e61 100644 --- a/drivers/usb/misc/rio500.c +++ b/drivers/usb/misc/rio500.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* -*- linux-c -*- */ /* diff --git a/drivers/usb/misc/rio500_usb.h b/drivers/usb/misc/rio500_usb.h index 359abc98e706..ce2ac1099d86 100644 --- a/drivers/usb/misc/rio500_usb.h +++ b/drivers/usb/misc/rio500_usb.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* ---------------------------------------------------------------------- Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar) diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 30774e0aeadd..3e65bdc2615c 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) /* * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles * diff --git a/drivers/usb/misc/sisusbvga/sisusb.h b/drivers/usb/misc/sisusbvga/sisusb.h index 55492a5930bd..20f03ad0ea16 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.h +++ b/drivers/usb/misc/sisusbvga/sisusb.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) /* * sisusb - usb kernel driver for Net2280/SiS315 based USB2VGA dongles * diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c index f019d80ca9e4..73f7bde78e11 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) /* * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles * diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.c b/drivers/usb/misc/sisusbvga/sisusb_init.c index bf0032ca35ed..6a30e8bd9221 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_init.c +++ b/drivers/usb/misc/sisusbvga/sisusb_init.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) /* * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles * diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.h b/drivers/usb/misc/sisusbvga/sisusb_init.h index e79a616f0d26..1782c759c4ad 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_init.h +++ b/drivers/usb/misc/sisusbvga/sisusb_init.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* $XFree86$ */ /* $XdotOrg$ */ /* diff --git a/drivers/usb/misc/sisusbvga/sisusb_struct.h b/drivers/usb/misc/sisusbvga/sisusb_struct.h index 1c4240e802c1..706d77090e00 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_struct.h +++ b/drivers/usb/misc/sisusbvga/sisusb_struct.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * General structure definitions for universal mode switching modules * diff --git a/drivers/usb/misc/trancevibrator.c b/drivers/usb/misc/trancevibrator.c index 1862ed15ce28..405726b8ebe6 100644 --- a/drivers/usb/misc/trancevibrator.c +++ b/drivers/usb/misc/trancevibrator.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * PlayStation 2 Trance Vibrator driver * diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 9c8f7e2b6740..5234bc3e0f01 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Microchip USB251xB USB 2.0 Hi-Speed Hub Controller * Configuration via SMBus. diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 8e7737d7ac0a..b5416f887fbf 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for SMSC USB3503 USB 2.0 hub controller driver * diff --git a/drivers/usb/misc/usb4604.c b/drivers/usb/misc/usb4604.c index e9f37fb746ac..40fa85807aae 100644 --- a/drivers/usb/misc/usb4604.c +++ b/drivers/usb/misc/usb4604.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for SMSC USB4604 USB HSIC 4-port 2.0 hub controller driver * Based on usb3503 driver diff --git a/drivers/usb/misc/usb_u132.h b/drivers/usb/misc/usb_u132.h index dc2e5a31caec..4dbfea3237f7 100644 --- a/drivers/usb/misc/usb_u132.h +++ b/drivers/usb/misc/usb_u132.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Common Header File for the Elan Digital Systems U132 adapter * this file should be included by both the "ftdi-u132" and diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index 0f5ad896c7e3..9ba4a4e68d91 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /***************************************************************************** * USBLCD Kernel Driver * * Version 1.05 * diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index 3f6a28045b53..4990d5757019 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB 7 Segment Driver * diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 3639e00a51a0..aeda01f037c1 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index 8a13b2fcf3e1..876a7a32defc 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /*****************************************************************************/ /* diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c index 58abdf28620a..1aec1d25ee44 100644 --- a/drivers/usb/misc/yurex.c +++ b/drivers/usb/misc/yurex.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for Meywa-Denki & KAYAC YUREX * diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index 46847340b819..9812d102a005 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * The USB Monitor, inspired by Dave Harding's USBMon. * diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index d80e4e813248..dfdace0d81a0 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtu3.h - MediaTek USB3 DRD header * diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index 7c149a7da14e..dbbf0ee1bd5f 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtu3_core.c - hardware access layer and gadget init/exit of * MediaTek usb3 Dual-Role Controller Driver diff --git a/drivers/usb/mtu3/mtu3_dr.c b/drivers/usb/mtu3/mtu3_dr.c index ec442cd5a1ad..ff842835329f 100644 --- a/drivers/usb/mtu3/mtu3_dr.c +++ b/drivers/usb/mtu3/mtu3_dr.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtu3_dr.c - dual role switch and host glue layer * diff --git a/drivers/usb/mtu3/mtu3_dr.h b/drivers/usb/mtu3/mtu3_dr.h index 0f0cbac00192..0a54c0bc4752 100644 --- a/drivers/usb/mtu3/mtu3_dr.h +++ b/drivers/usb/mtu3/mtu3_dr.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtu3_dr.h - dual role switch and host glue layer header * diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index b495471f689f..8679a5bc484e 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtu3_gadget.c - MediaTek usb3 DRD peripheral support * diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 020b25314a68..678432f6ef74 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtu3_gadget_ep0.c - MediaTek USB3 DRD peripheral driver ep0 handling * diff --git a/drivers/usb/mtu3/mtu3_host.c b/drivers/usb/mtu3/mtu3_host.c index ec76b86dd887..4c570de20d56 100644 --- a/drivers/usb/mtu3/mtu3_host.c +++ b/drivers/usb/mtu3/mtu3_host.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtu3_dr.c - dual role switch and host glue layer * diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index 6953436a1688..8507363a7e44 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions * diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 9ff33579b42e..3808b2e7bcb8 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2016 MediaTek Inc. * diff --git a/drivers/usb/mtu3/mtu3_qmu.c b/drivers/usb/mtu3/mtu3_qmu.c index 0b4b412b1d0d..9f273dad0fd9 100644 --- a/drivers/usb/mtu3/mtu3_qmu.c +++ b/drivers/usb/mtu3/mtu3_qmu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtu3_qmu.c - Queue Management Unit driver for device controller * diff --git a/drivers/usb/mtu3/mtu3_qmu.h b/drivers/usb/mtu3/mtu3_qmu.h index 4dafa16bf120..05cfdfe8be4c 100644 --- a/drivers/usb/mtu3/mtu3_qmu.h +++ b/drivers/usb/mtu3/mtu3_qmu.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mtu3_qmu.h - Queue Management Unit driver header * diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 2cf990e85bb2..efacff81fd32 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Texas Instruments AM35x "glue layer" diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 7c580df75dc1..432fd5795877 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * MUSB OTG controller driver for Blackfin Processors * diff --git a/drivers/usb/musb/blackfin.h b/drivers/usb/musb/blackfin.h index 231f2d23b8a1..c0ae8ce81b10 100644 --- a/drivers/usb/musb/blackfin.h +++ b/drivers/usb/musb/blackfin.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2007 by Analog Devices, Inc. * diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index a13bd3625043..b4d6d9bb3239 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2005-2006 by Texas Instruments * diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 1ed5b0e0c2ca..6446921bd98e 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Texas Instruments DA8xx/OMAP-L1x "glue layer" * diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 3a7048e84e1c..e25890379c8e 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2005-2006 by Texas Instruments * diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h index 371baa0ee509..2e507cedf2f4 100644 --- a/drivers/usb/musb/davinci.h +++ b/drivers/usb/musb/davinci.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2005-2006 by Texas Instruments * diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 40c68c23d553..354d143ad740 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Ingenic JZ4740 "glue layer" * diff --git a/drivers/usb/musb/musb_am335x.c b/drivers/usb/musb/musb_am335x.c index 1e58ed2361cc..5f04f8e3a640 100644 --- a/drivers/usb/musb/musb_am335x.c +++ b/drivers/usb/musb/musb_am335x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 82a5089caa34..e568690410fd 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver core code * diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index e8573975743d..ca823231bbb3 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver defines * diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 1ec0a4947b6b..d0dd4f470bbe 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h index 9a78877a8afe..345a359de57a 100644 --- a/drivers/usb/musb/musb_debug.h +++ b/drivers/usb/musb/musb_debug.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver debug defines * diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index 952733ceaac8..b91d4b60b8c7 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver debugfs support * diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 04c3bd86bd62..7fea3455cd3b 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver DMA controller abstraction * diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 4d95fe714f27..8c292820330c 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Texas Instruments DSPS platforms "glue layer" * diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index bc6d1717c9ec..2c47704adbd3 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver peripheral support * diff --git a/drivers/usb/musb/musb_gadget.h b/drivers/usb/musb/musb_gadget.h index 0314dfc770c7..c8c9d5565848 100644 --- a/drivers/usb/musb/musb_gadget.h +++ b/drivers/usb/musb/musb_gadget.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver peripheral defines * diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 844a309fe895..1586f3be37be 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG peripheral driver ep0 handling * diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index b17450a59882..262b425c231d 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver host support * diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 7bbf01bf4bb0..2546cdd5d3b9 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver host defines * diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 17a80ae20674..2ebf033ced87 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver register I/O * diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index cff5bcf0d00f..31f92798b408 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver register defines * diff --git a/drivers/usb/musb/musb_trace.c b/drivers/usb/musb/musb_trace.c index 70973d901a21..037509918844 100644 --- a/drivers/usb/musb/musb_trace.c +++ b/drivers/usb/musb/musb_trace.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * musb_trace.c - MUSB Controller Trace Support * diff --git a/drivers/usb/musb/musb_trace.h b/drivers/usb/musb/musb_trace.h index f031c9e74322..669cd1df5bf8 100644 --- a/drivers/usb/musb/musb_trace.h +++ b/drivers/usb/musb/musb_trace.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * musb_trace.h - MUSB Controller Trace Support * diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 0b4595439d51..de00a6ccd7b3 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver virtual root hub support * diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index 3620073da58c..d1bb309070a4 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver - support for Mentor's DMA controller * diff --git a/drivers/usb/musb/musbhsdma.h b/drivers/usb/musb/musbhsdma.h index a3dcbd55e436..51289c0b277e 100644 --- a/drivers/usb/musb/musbhsdma.h +++ b/drivers/usb/musb/musbhsdma.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * MUSB OTG driver - support for Mentor's DMA controller * diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 456f3e6ecf03..f68053165b05 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2005-2007 by Texas Instruments * Some code has been taken from tusb6010.c diff --git a/drivers/usb/musb/omap2430.h b/drivers/usb/musb/omap2430.h index 1b5e83a9840e..f484ba592d40 100644 --- a/drivers/usb/musb/omap2430.h +++ b/drivers/usb/musb/omap2430.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2005-2006 by Texas Instruments * diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index dc353e24d53c..ecc9e1a60f46 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Allwinner sun4i MUSB Glue Layer * diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index f8fce7df654f..d844066a411d 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * TUSB6010 USB 2.0 OTG Dual Role controller * diff --git a/drivers/usb/musb/tusb6010.h b/drivers/usb/musb/tusb6010.h index 72cdad23ced9..fc8c01d994c6 100644 --- a/drivers/usb/musb/tusb6010.h +++ b/drivers/usb/musb/tusb6010.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Definitions for TUSB6010 USB 2.0 OTG Dual Role controller * diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index e8060e49b0f4..9db5eb9ba215 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * TUSB6010 USB 2.0 OTG Dual Role controller OMAP DMA interface * diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index 5a572500c418..b29930253acb 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2010 ST-Ericsson AB * Mian Yousaf Kaukab diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index c92a295049ad..1a14b0e15ba3 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * drivers/usb/musb/ux500_dma.c * diff --git a/drivers/usb/phy/of.c b/drivers/usb/phy/of.c index 66ffa82457a8..3b0ebdb63488 100644 --- a/drivers/usb/phy/of.c +++ b/drivers/usb/phy/of.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB of helper code * diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 61bf2285d5b1..c1394c524c6b 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB transceiver driver for AB8500 family chips * diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 5f5f19813fde..a3cb25cb74f8 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index 7e5aece769da..b36fa8b953d0 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #include #include #include diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index cf8f40ae6e01..0d9f2e27ce34 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2007,2008 Freescale semiconductor, Inc. * diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index 23149954a09c..5db4668661cd 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index 1cc02eb3fc65..9e67a0d58fa1 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * NOP USB transceiver for all USB transceiver which are either built-in * into USB IP or which are mostly autonomous. diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c index f66120db8a41..d7a3aeaa5ae6 100644 --- a/drivers/usb/phy/phy-gpio-vbus-usb.c +++ b/drivers/usb/phy/phy-gpio-vbus-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * gpio-vbus.c - simple GPIO VBUS sensing driver for B peripheral devices * diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c index 00bcc6c6db17..e0e1759463ba 100644 --- a/drivers/usb/phy/phy-isp1301-omap.c +++ b/drivers/usb/phy/phy-isp1301-omap.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * isp1301_omap - ISP 1301 USB transceiver, talking to OMAP OTG controller * diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index f333024660b4..22499abc8272 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * NXP ISP1301 USB transceiver driver * diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index 01d4e4cdbc79..0670414ccc38 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * phy-keystone - USB PHY, talking to dwc3 controller in Keystone. * diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index fc9ed047d25d..50041d72427d 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2011 Marvell International Ltd. All rights reserved. * Author: Chao Xie diff --git a/drivers/usb/phy/phy-mv-usb.h b/drivers/usb/phy/phy-mv-usb.h index 551da6eb0ba8..6150f6bba30b 100644 --- a/drivers/usb/phy/phy-mv-usb.h +++ b/drivers/usb/phy/phy-mv-usb.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2011 Marvell International Ltd. All rights reserved. * diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index 269fc030656e..5de62677df98 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2012-2014 Freescale Semiconductor, Inc. * Copyright (C) 2012 Marek Vasut diff --git a/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c index 800d1d90753d..1ce4a846d9b0 100644 --- a/drivers/usb/phy/phy-omap-otg.c +++ b/drivers/usb/phy/phy-omap-otg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * OMAP OTG controller driver * diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index bf2c364867a0..3a437bf5e004 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Tahvo USB transceiver driver * diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index ccc2bf5274b4..1ebfbdef4529 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2010 Google, Inc. * Copyright (C) 2013 NVIDIA Corporation diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index b5dc077ed7d3..ddcb0dff4d5e 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * twl6030_usb - TWL6030 USB transceiver, talking to OMAP OTG driver. * diff --git a/drivers/usb/phy/phy-ulpi-viewport.c b/drivers/usb/phy/phy-ulpi-viewport.c index 18bb8264b5a0..394778a5219c 100644 --- a/drivers/usb/phy/phy-ulpi-viewport.c +++ b/drivers/usb/phy/phy-ulpi-viewport.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2011 Google, Inc. * diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c index f48a7a21e3c2..1a594b356ad8 100644 --- a/drivers/usb/phy/phy-ulpi.c +++ b/drivers/usb/phy/phy-ulpi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Generic ULPI USB transceiver support * diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 89f4ac4cd93e..3405e8e30a01 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * phy.c -- USB phy handling * diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 490ebc7df7b2..e1bc8a84c118 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h index 8c5fc12ad778..416331c6990a 100644 --- a/drivers/usb/renesas_usbhs/common.h +++ b/drivers/usb/renesas_usbhs/common.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 50285b01da92..e99c323b2f9e 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 8b98507d7abc..7a741234c24b 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 28965ef4f824..c0a0789d8b1e 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h index 1ef5bf604070..5355a13045d9 100644 --- a/drivers/usb/renesas_usbhs/mod.h +++ b/drivers/usb/renesas_usbhs/mod.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index c068b673420b..019bbc8bf9b2 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c index e256351cb72d..1ab0ac83b00c 100644 --- a/drivers/usb/renesas_usbhs/mod_host.c +++ b/drivers/usb/renesas_usbhs/mod_host.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c index d811f0550c04..3c500aaadf35 100644 --- a/drivers/usb/renesas_usbhs/pipe.c +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h index 95185fdb29b1..ed32cb11fe09 100644 --- a/drivers/usb/renesas_usbhs/pipe.h +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c index 277160bc6f25..b03b3cb36b49 100644 --- a/drivers/usb/renesas_usbhs/rcar2.c +++ b/drivers/usb/renesas_usbhs/rcar2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver R-Car Gen. 2 initialization and power control * diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c index f436e9d51127..11a13887a71d 100644 --- a/drivers/usb/renesas_usbhs/rcar3.c +++ b/drivers/usb/renesas_usbhs/rcar3.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas USB driver R-Car Gen. 3 initialization and power control * diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 569c2200ba42..2ea3fc79acd6 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * AIRcable USB Bluetooth Dongle Driver. * diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 0adbd38b4eea..a0cc93b0c64d 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2009 by Bart Hartgers (bart.hartgers+ark3116@gmail.com) * Original version: diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 15bc71853db5..6e0c98bde745 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Belkin USB Serial Adapter Driver * diff --git a/drivers/usb/serial/belkin_sa.h b/drivers/usb/serial/belkin_sa.h index c74b58ab56f9..345c57f63831 100644 --- a/drivers/usb/serial/belkin_sa.h +++ b/drivers/usb/serial/belkin_sa.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Definitions for Belkin USB Serial Adapter Driver * diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 8936a83c96cd..db49e2a4346b 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Serial Converter Bus specific functions * diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 351745aec0e1..0543836f053e 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2007, Frank A Kingswood * Copyright 2007, Werner Cornelius diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 43a862a90a77..4f80a422d4e5 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Serial Console driver * diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 412f812522ee..2bc560a8d9a0 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Silicon Laboratories CP210x USB to RS232 serial adaptor driver * diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c index 47fbd9f0c0c7..830e793dfe44 100644 --- a/drivers/usb/serial/cyberjack.c +++ b/drivers/usb/serial/cyberjack.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * REINER SCT cyberJack pinpad/e-com USB Chipcard Reader Driver * diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 90110de715e0..a43ee56d3032 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB Cypress M8 driver * diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 2ce39af32cfa..fc30c1d04d6e 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Digi AccelePort USB-4 and USB-2 Serial Converters * diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index 90e603d5f660..b270ff3a721a 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Empeg empeg-car player driver * diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 972f5a5fe577..869bfd05e02e 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Fintek F81232 USB to serial adaptor driver * diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index cb8214860192..cc535426208f 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * F81532/F81534 USB to Serial Ports Bridge * diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 49d1b2d4606d..70791b672af6 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB FTDI SIO driver * diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 4f793c86978e..e06bde8aaac4 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Garmin GPS driver * diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 35cb8c0e584f..6d64595df6fd 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Serial Converter Generic functions * diff --git a/drivers/usb/serial/io_16654.h b/drivers/usb/serial/io_16654.h index a53abc9530ff..9a594aa65cd8 100644 --- a/drivers/usb/serial/io_16654.h +++ b/drivers/usb/serial/io_16654.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /************************************************************************ * * 16654.H Definitions for 16C654 UART used on EdgePorts diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index bdf8bd814a9a..9ccfe291d6ad 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Edgeport USB Serial Converter driver * diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h index ad9c1d47a619..657158133a88 100644 --- a/drivers/usb/serial/io_edgeport.h +++ b/drivers/usb/serial/io_edgeport.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /************************************************************************ * * io_edgeport.h Edgeport Linux Interface definitions diff --git a/drivers/usb/serial/io_ionsp.h b/drivers/usb/serial/io_ionsp.h index 5cc591bae54d..e9d5cde2e4a6 100644 --- a/drivers/usb/serial/io_ionsp.h +++ b/drivers/usb/serial/io_ionsp.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /************************************************************************ * * IONSP.H Definitions for I/O Networks Serial Protocol diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 6cefb9cb133d..c6fc62447b25 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Edgeport USB Serial Converter driver * diff --git a/drivers/usb/serial/io_ti.h b/drivers/usb/serial/io_ti.h index 1bd67b24f916..42eb4afc4550 100644 --- a/drivers/usb/serial/io_ti.h +++ b/drivers/usb/serial/io_ti.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /***************************************************************************** * * Copyright (C) 1997-2002 Inside Out Networks, Inc. diff --git a/drivers/usb/serial/io_usbvend.h b/drivers/usb/serial/io_usbvend.h index 6f6a856bc37c..fec5a0aec075 100644 --- a/drivers/usb/serial/io_usbvend.h +++ b/drivers/usb/serial/io_usbvend.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /************************************************************************ * * USBVEND.H Vendor-specific USB definitions diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index cde0dcdce9c4..52da1a82d7f0 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB Compaq iPAQ driver * diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c index 8b1cf18a668b..4a534e8037d5 100644 --- a/drivers/usb/serial/ipw.c +++ b/drivers/usb/serial/ipw.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * IPWireless 3G UMTS TDD Modem driver (USB connected) * diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c index f9734a96d516..8109bcfed9f4 100644 --- a/drivers/usb/serial/ir-usb.c +++ b/drivers/usb/serial/ir-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB IR Dongle driver * diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index 18fc992a245f..c0565a352674 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Infinity Unlimited USB Phoenix driver * diff --git a/drivers/usb/serial/iuu_phoenix.h b/drivers/usb/serial/iuu_phoenix.h index b82630a3b8fd..d473cde2aae7 100644 --- a/drivers/usb/serial/iuu_phoenix.h +++ b/drivers/usb/serial/iuu_phoenix.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Infinity Unlimited USB Phoenix driver * diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 5662d324edd2..c5e34999bea4 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* Keyspan USB to Serial Converter driver diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 196908dd25a1..04c26f4a9542 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB Keyspan PDA / Xircom / Entrega Converter driver * diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index 595415e59d5d..e71978840780 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * KLSI KL5KUSB105 chip RS232 converter driver * diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 2f8fa3589b20..86625e9f3a8b 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * KOBIL USB Smart Card Terminal Driver * diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 70f346f1aa86..ef4f9067499a 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * MCT (Magic Control Technology Corp.) USB RS232 Converter Driver * diff --git a/drivers/usb/serial/mct_u232.h b/drivers/usb/serial/mct_u232.h index d325bb8cb583..bd3f40770adb 100644 --- a/drivers/usb/serial/mct_u232.h +++ b/drivers/usb/serial/mct_u232.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Definitions for MCT (Magic Control Technology) USB-RS232 Converter Driver * diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c index 36f65df41721..2832810c9693 100644 --- a/drivers/usb/serial/metro-usb.c +++ b/drivers/usb/serial/metro-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* Some of this code is credited to Linux USB open source files that are distributed with Linux. diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index a453965f9e9a..b7075252e9ca 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * mos7720.c * Controls the Moschip 7720 usb to dual port serial converter diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index e8669aae14b3..24b7a4a6f8f1 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index 3aef091fe88b..1e9430b077f2 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * mxuport.c - MOXA UPort series driver * diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c index 2a97cdc078d5..ab387a58992b 100644 --- a/drivers/usb/serial/navman.c +++ b/drivers/usb/serial/navman.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Navman Serial USB driver * diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index efcd7feed6f4..8e1406aecafb 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB ZyXEL omni.net LCD PLUS driver * diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 58657d64678b..66e6f77d27c2 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Opticon USB barcode to serial driver * diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index ba672cf4e888..eb7d55a8e0ca 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* USB Driver for GSM modems diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index b11eead469ee..c6adf41b62da 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Ours Technology Inc. OTi-6858 USB to serial adapter driver. * diff --git a/drivers/usb/serial/oti6858.h b/drivers/usb/serial/oti6858.h index 704ac3a532b3..9fb62f03005d 100644 --- a/drivers/usb/serial/oti6858.h +++ b/drivers/usb/serial/oti6858.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Ours Technology Inc. OTi-6858 USB to serial adapter driver. * diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index a585b477415d..5a12f5d81fc2 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Prolific PL2303 USB to serial adaptor driver * diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 3b5a15d1dc0d..35db75405f9d 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Prolific PL2303 USB to serial adaptor driver header file * diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c index 6e9f8af96959..f4a5921cc500 100644 --- a/drivers/usb/serial/qcaux.c +++ b/drivers/usb/serial/qcaux.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Qualcomm USB Auxiliary Serial Port driver * diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index 9f9d3a904464..6968170beda7 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Qualcomm Serial USB driver * diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 60e17d1444c3..58fe44db82e8 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * usb-serial driver for Quatech USB 2 devices * diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 27d7a7016298..3dd85f025cbe 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Safe Encapsulated USB Serial Driver * diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 4c4ac4705ac0..1adbd5a99cf8 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* USB Driver for Sierra Wireless diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 5167b6564c8b..48d330ff03fa 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * spcp8x5 USB to serial adaptor driver * diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c index 5aa7bbbeba3d..a7db9aff97a1 100644 --- a/drivers/usb/serial/ssu100.c +++ b/drivers/usb/serial/ssu100.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * usb-serial driver for Quatech SSU-100 * diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c index 0d1727232d0c..25d966165211 100644 --- a/drivers/usb/serial/symbolserial.c +++ b/drivers/usb/serial/symbolserial.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Symbol USB barcode to serial driver * diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index 8fc3854e5e69..eb184a78db94 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * TI 3410/5052 USB Serial Driver * diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c index 6819a3486e5d..facad97ff487 100644 --- a/drivers/usb/serial/upd78f0730.c +++ b/drivers/usb/serial/upd78f0730.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Renesas Electronics uPD78F0730 USB to serial converter driver * diff --git a/drivers/usb/serial/usb-serial-simple.c b/drivers/usb/serial/usb-serial-simple.c index e98b6e57b703..0d692f187d85 100644 --- a/drivers/usb/serial/usb-serial-simple.c +++ b/drivers/usb/serial/usb-serial-simple.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Serial "Simple" driver * diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 20639d5b8886..3e27c844345c 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Serial Converter driver * diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index 12f4c5a91e62..9fbd6352de4f 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Debug cable driver * diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 59bfcb3da116..9e48be615b2f 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* USB Driver layer for GSM modems diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 9f3317a940ef..2ea07ee08c4f 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB HandSpring Visor, Palm m50x, and Sony Clie driver * (supports all of the Palm OS USB devices) diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h index 4c456dd69ce5..e87de8c0c239 100644 --- a/drivers/usb/serial/visor.h +++ b/drivers/usb/serial/visor.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB HandSpring Visor driver * diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 55cebc1e6fec..1873f1046c58 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB ConnectTech WhiteHEAT driver * diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h index 38065df4d2d8..024cc9266ecb 100644 --- a/drivers/usb/serial/whiteheat.h +++ b/drivers/usb/serial/whiteheat.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB ConnectTech WhiteHEAT driver * diff --git a/drivers/usb/serial/wishbone-serial.c b/drivers/usb/serial/wishbone-serial.c index 4fed4a0bd702..8f227c485bfd 100644 --- a/drivers/usb/serial/wishbone-serial.c +++ b/drivers/usb/serial/wishbone-serial.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB Wishbone-Serial adapter driver * diff --git a/drivers/usb/serial/xsens_mt.c b/drivers/usb/serial/xsens_mt.c index 3837d5113bb2..ef5f3f655a42 100644 --- a/drivers/usb/serial/xsens_mt.c +++ b/drivers/usb/serial/xsens_mt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Xsens MT USB driver * diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index 878b4b8761f5..ad71ff132080 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Alauda-based card readers * diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c index 5e4af44d7d9f..a326ad73de16 100644 --- a/drivers/usb/storage/cypress_atacb.c +++ b/drivers/usb/storage/cypress_atacb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Support for emulating SAT (ata pass through) on devices based * on the Cypress USB/ATA bridge supporting ATACB. diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 723197af6ec5..f408d26700ce 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Datafab USB Compact Flash reader * diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c index 8d20804a59e6..182d2f8e9b2b 100644 --- a/drivers/usb/storage/debug.c +++ b/drivers/usb/storage/debug.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * Debugging Functions Source Code File diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h index 8ab73299b650..69dd4c480fb2 100644 --- a/drivers/usb/storage/debug.h +++ b/drivers/usb/storage/debug.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * Debugging Functions Header File diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 28100374f7bd..fc733fa14415 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index c0a5d954414b..3b1ffadd07bf 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Freecom USB/IDE adaptor * diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index d9d8c17e05d1..9b574caa80ac 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Special Initializers for certain USB Mass Storage devices * diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index 039abf4d1cb7..9e6f6efb57f4 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Header file for Special Initializers for certain USB Mass Storage devices * diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 6a7720e66595..0f8603cf2755 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC * diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 011e5270690a..a1ead5b9d559 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Lexar "Jumpshot" Compact Flash reader * diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c index b05ba4929f00..7ecf2f83f8c9 100644 --- a/drivers/usb/storage/karma.c +++ b/drivers/usb/storage/karma.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Rio Karma * diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index acc3d03d8c1e..27873d0ad130 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Support for the Maxtor OneTouch USB hard drive's button * diff --git a/drivers/usb/storage/option_ms.c b/drivers/usb/storage/option_ms.c index 4a73cd4783ae..e23e47413271 100644 --- a/drivers/usb/storage/option_ms.c +++ b/drivers/usb/storage/option_ms.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Option High Speed Mobile Devices. * diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c index 74c38870a17e..b8b8e7066a04 100644 --- a/drivers/usb/storage/protocol.c +++ b/drivers/usb/storage/protocol.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h index a55666880b7b..abedf13a8cf1 100644 --- a/drivers/usb/storage/protocol.h +++ b/drivers/usb/storage/protocol.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * Protocol Functions Header File diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index ec83b3b5efa9..7d02a7c5cdd6 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Realtek RTS51xx USB card reader * diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 8cd2926fb1fe..878922fb54b8 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * SCSI layer glue code diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h index d0a331dd9bc5..add14c47ce1d 100644 --- a/drivers/usb/storage/scsiglue.h +++ b/drivers/usb/storage/scsiglue.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * SCSI Connecting Glue Header File diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 44f8ffccd031..b37cb07dfc80 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for SanDisk SDDR-09 SmartMedia reader * diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index b973da42b593..705c9724ab9d 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for SanDisk SDDR-55 SmartMedia reader * diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index 3b0294e4df93..3e9da1d257b6 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable * diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index a3ccb899df60..b31568e8f6c3 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h index dae3ecd2e6cf..98591db5fb6a 100644 --- a/drivers/usb/storage/transport.h +++ b/drivers/usb/storage/transport.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * Transport Functions Header File diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index bd4671d5dfc3..d86754b65ef1 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Attached SCSI * Note that this is not the same as the USB Mass Storage driver diff --git a/drivers/usb/storage/unusual_alauda.h b/drivers/usb/storage/unusual_alauda.h index 763bc03032a1..311c5a21ac13 100644 --- a/drivers/usb/storage/unusual_alauda.h +++ b/drivers/usb/storage/unusual_alauda.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for the Alauda-based card readers * diff --git a/drivers/usb/storage/unusual_cypress.h b/drivers/usb/storage/unusual_cypress.h index e9a2eb88869a..285370f13ee4 100644 --- a/drivers/usb/storage/unusual_cypress.h +++ b/drivers/usb/storage/unusual_cypress.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for devices based on the Cypress USB/ATA bridge * with support for ATACB diff --git a/drivers/usb/storage/unusual_datafab.h b/drivers/usb/storage/unusual_datafab.h index 5049b6bbe5d5..03c0e72d7b9d 100644 --- a/drivers/usb/storage/unusual_datafab.h +++ b/drivers/usb/storage/unusual_datafab.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for the Datafab USB Compact Flash reader * diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index eb06d88b41d6..33f691916762 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * Unusual Devices File diff --git a/drivers/usb/storage/unusual_ene_ub6250.h b/drivers/usb/storage/unusual_ene_ub6250.h index 5667f5d365c6..c9d9e52d884d 100644 --- a/drivers/usb/storage/unusual_ene_ub6250.h +++ b/drivers/usb/storage/unusual_ene_ub6250.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/usb/storage/unusual_freecom.h b/drivers/usb/storage/unusual_freecom.h index 1f5aab42ece2..06088feb8f96 100644 --- a/drivers/usb/storage/unusual_freecom.h +++ b/drivers/usb/storage/unusual_freecom.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for the Freecom USB/IDE adaptor * diff --git a/drivers/usb/storage/unusual_isd200.h b/drivers/usb/storage/unusual_isd200.h index 9b6862ec3d4f..b924e3d75960 100644 --- a/drivers/usb/storage/unusual_isd200.h +++ b/drivers/usb/storage/unusual_isd200.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for In-System Design, Inc. ISD200 ASIC * diff --git a/drivers/usb/storage/unusual_jumpshot.h b/drivers/usb/storage/unusual_jumpshot.h index 413e64fa6b95..1ca977374ba4 100644 --- a/drivers/usb/storage/unusual_jumpshot.h +++ b/drivers/usb/storage/unusual_jumpshot.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for the Lexar "Jumpshot" Compact Flash reader * diff --git a/drivers/usb/storage/unusual_karma.h b/drivers/usb/storage/unusual_karma.h index e6fad3aeae20..84910d13c80a 100644 --- a/drivers/usb/storage/unusual_karma.h +++ b/drivers/usb/storage/unusual_karma.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for the Rio Karma * diff --git a/drivers/usb/storage/unusual_onetouch.h b/drivers/usb/storage/unusual_onetouch.h index 425dc22f345a..28d39c4e20e2 100644 --- a/drivers/usb/storage/unusual_onetouch.h +++ b/drivers/usb/storage/unusual_onetouch.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for the Maxtor OneTouch USB hard drive's button * diff --git a/drivers/usb/storage/unusual_realtek.h b/drivers/usb/storage/unusual_realtek.h index 8fe624ad302a..736e4f7ebe10 100644 --- a/drivers/usb/storage/unusual_realtek.h +++ b/drivers/usb/storage/unusual_realtek.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Realtek RTS51xx USB card reader * diff --git a/drivers/usb/storage/unusual_sddr09.h b/drivers/usb/storage/unusual_sddr09.h index d9d38ac4abf9..1098646d4b75 100644 --- a/drivers/usb/storage/unusual_sddr09.h +++ b/drivers/usb/storage/unusual_sddr09.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for SanDisk SDDR-09 SmartMedia reader * diff --git a/drivers/usb/storage/unusual_sddr55.h b/drivers/usb/storage/unusual_sddr55.h index ebb1d1c6c467..b98c778051b8 100644 --- a/drivers/usb/storage/unusual_sddr55.h +++ b/drivers/usb/storage/unusual_sddr55.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for SanDisk SDDR-55 SmartMedia reader * diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index cde115359793..2df3319efd66 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Attached SCSI devices - Unusual Devices File * diff --git a/drivers/usb/storage/unusual_usbat.h b/drivers/usb/storage/unusual_usbat.h index 2044ad5ef5e4..ac6bae08ac79 100644 --- a/drivers/usb/storage/unusual_usbat.h +++ b/drivers/usb/storage/unusual_usbat.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Unusual Devices File for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable * diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 1ae2b8554a88..7b1524be01bb 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 8fae28b40bb4..aa3da222f6b1 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage compliant devices * Main Header File diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c index 499669bcf700..90ac516c1f12 100644 --- a/drivers/usb/storage/usual-tables.c +++ b/drivers/usb/storage/usual-tables.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for USB Mass Storage devices * Usual Tables File for usb-storage and libusual diff --git a/drivers/usb/typec/typec.c b/drivers/usb/typec/typec.c index 24e355ba109d..51fbd9a3ef9f 100644 --- a/drivers/usb/typec/typec.c +++ b/drivers/usb/typec/typec.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Type-C Connector Class * diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/typec_wcove.c index 4ce30a967962..e845cdd0f765 100644 --- a/drivers/usb/typec/typec_wcove.c +++ b/drivers/usb/typec/typec_wcove.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /** * typec_wcove.c - WhiskeyCove PMIC USB Type-C PHY driver * diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 714c5bcedf2b..8a9cbe9354ea 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Type-C Connector System Software Interface driver * diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index cabd47612b0a..e848cc508230 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * UCSI ACPI driver * diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index bb0bd732e29a..cc31a1b6c7bd 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Skeleton driver - 2.2 * diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h index 910f027773aa..1001a9c1b6fc 100644 --- a/drivers/usb/usbip/stub.h +++ b/drivers/usb/usbip/stub.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c index c653ce533430..4ff622549781 100644 --- a/drivers/usb/usbip/stub_dev.c +++ b/drivers/usb/usbip/stub_dev.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c index 7170404e8979..e07166534c62 100644 --- a/drivers/usb/usbip/stub_main.c +++ b/drivers/usb/usbip/stub_main.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c index 191b176ffedf..095429b533d8 100644 --- a/drivers/usb/usbip/stub_rx.c +++ b/drivers/usb/usbip/stub_rx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * diff --git a/drivers/usb/usbip/stub_tx.c b/drivers/usb/usbip/stub_tx.c index be50cef645d8..814b4e6a945b 100644 --- a/drivers/usb/usbip/stub_tx.c +++ b/drivers/usb/usbip/stub_tx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * diff --git a/drivers/usb/usbip/usbip_common.c b/drivers/usb/usbip/usbip_common.c index 2281f3562870..edd14fd92377 100644 --- a/drivers/usb/usbip/usbip_common.c +++ b/drivers/usb/usbip/usbip_common.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * Copyright (C) 2015-2016 Samsung Electronics diff --git a/drivers/usb/usbip/usbip_common.h b/drivers/usb/usbip/usbip_common.h index 3050fc99a417..d0bd3ae24465 100644 --- a/drivers/usb/usbip/usbip_common.h +++ b/drivers/usb/usbip/usbip_common.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * Copyright (C) 2015-2016 Samsung Electronics diff --git a/drivers/usb/usbip/usbip_event.c b/drivers/usb/usbip/usbip_event.c index f1635662c299..ffeb0f53fc68 100644 --- a/drivers/usb/usbip/usbip_event.c +++ b/drivers/usb/usbip/usbip_event.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * Copyright (C) 2015 Nobuo Iwata diff --git a/drivers/usb/usbip/vhci.h b/drivers/usb/usbip/vhci.h index 5cfb59e98e44..04240e450e3f 100644 --- a/drivers/usb/usbip/vhci.h +++ b/drivers/usb/usbip/vhci.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * Copyright (C) 2015 Nobuo Iwata diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 11b9a22799cc..0e16a7dcfb38 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * Copyright (C) 2015-2016 Nobuo Iwata diff --git a/drivers/usb/usbip/vhci_rx.c b/drivers/usb/usbip/vhci_rx.c index ef2f2d5ca6b2..2389ba443caa 100644 --- a/drivers/usb/usbip/vhci_rx.c +++ b/drivers/usb/usbip/vhci_rx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * diff --git a/drivers/usb/usbip/vhci_sysfs.c b/drivers/usb/usbip/vhci_sysfs.c index 1b9f60a22e0b..2a05f3198b32 100644 --- a/drivers/usb/usbip/vhci_sysfs.c +++ b/drivers/usb/usbip/vhci_sysfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * Copyright (C) 2015-2016 Nobuo Iwata diff --git a/drivers/usb/usbip/vhci_tx.c b/drivers/usb/usbip/vhci_tx.c index 3e7878fe2fd4..8b127d98ce93 100644 --- a/drivers/usb/usbip/vhci_tx.c +++ b/drivers/usb/usbip/vhci_tx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2003-2008 Takahiro Hirofuchi * diff --git a/drivers/usb/usbip/vudc.h b/drivers/usb/usbip/vudc.h index 25e01b09c4c3..44fb24193acd 100644 --- a/drivers/usb/usbip/vudc.h +++ b/drivers/usb/usbip/vudc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Karol Kosik * Copyright (C) 2015-2016 Samsung Electronics diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index 968471b62cbc..0c07348820ea 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Karol Kosik * Copyright (C) 2015-2016 Samsung Electronics diff --git a/drivers/usb/usbip/vudc_main.c b/drivers/usb/usbip/vudc_main.c index 9e655714e389..63aee6bb0dd9 100644 --- a/drivers/usb/usbip/vudc_main.c +++ b/drivers/usb/usbip/vudc_main.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Karol Kosik * Copyright (C) 2015-2016 Samsung Electronics diff --git a/drivers/usb/usbip/vudc_rx.c b/drivers/usb/usbip/vudc_rx.c index e429b59f6f8a..2faccf62b17e 100644 --- a/drivers/usb/usbip/vudc_rx.c +++ b/drivers/usb/usbip/vudc_rx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Karol Kosik * Copyright (C) 2015-2016 Samsung Electronics diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c index 0f98f2c7475f..7d978b824ed4 100644 --- a/drivers/usb/usbip/vudc_sysfs.c +++ b/drivers/usb/usbip/vudc_sysfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Karol Kosik * Copyright (C) 2015-2016 Samsung Electronics diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c index 718f1595a18e..80feece59b21 100644 --- a/drivers/usb/usbip/vudc_transfer.c +++ b/drivers/usb/usbip/vudc_transfer.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Karol Kosik * Copyright (C) 2015-2016 Samsung Electronics diff --git a/drivers/usb/usbip/vudc_tx.c b/drivers/usb/usbip/vudc_tx.c index 234661782fa0..63d8bb810ff8 100644 --- a/drivers/usb/usbip/vudc_tx.c +++ b/drivers/usb/usbip/vudc_tx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Karol Kosik * Copyright (C) 2015-2016 Samsung Electronics diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/usb/wusbcore/cbaf.c index aa4e440e9975..5a3ee0d6cf31 100644 --- a/drivers/usb/wusbcore/cbaf.c +++ b/drivers/usb/wusbcore/cbaf.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless USB - Cable Based Association * diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c index 062c205f0046..011ce6a35137 100644 --- a/drivers/usb/wusbcore/crypto.c +++ b/drivers/usb/wusbcore/crypto.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Ultra Wide Band * AES-128 CCM Encryption diff --git a/drivers/usb/wusbcore/dev-sysfs.c b/drivers/usb/wusbcore/dev-sysfs.c index 78212f8180ce..02457f7e2ee3 100644 --- a/drivers/usb/wusbcore/dev-sysfs.c +++ b/drivers/usb/wusbcore/dev-sysfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * WUSB devices * sysfs bindings diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c index bf9551735938..18826c98b7d6 100644 --- a/drivers/usb/wusbcore/devconnect.c +++ b/drivers/usb/wusbcore/devconnect.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8]) * Device Connect handling diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/usb/wusbcore/mmc.c index 3f485df96226..235099df783e 100644 --- a/drivers/usb/wusbcore/mmc.c +++ b/drivers/usb/wusbcore/mmc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8]) * MMC (Microscheduled Management Command) handling diff --git a/drivers/usb/wusbcore/pal.c b/drivers/usb/wusbcore/pal.c index 090f27371a8f..6e47b055ad47 100644 --- a/drivers/usb/wusbcore/pal.c +++ b/drivers/usb/wusbcore/pal.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless USB Host Controller * UWB Protocol Adaptation Layer (PAL) glue. diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/usb/wusbcore/reservation.c index 7b1b2e2fb673..9463afe44c09 100644 --- a/drivers/usb/wusbcore/reservation.c +++ b/drivers/usb/wusbcore/reservation.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * WUSB cluster reservation management * diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c index a082fe62b1f0..0fb11cde5ff8 100644 --- a/drivers/usb/wusbcore/rh.c +++ b/drivers/usb/wusbcore/rh.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless USB Host Controller * Root Hub operations diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c index 724490580c1e..c55a74de0945 100644 --- a/drivers/usb/wusbcore/security.c +++ b/drivers/usb/wusbcore/security.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless USB Host Controller * Security support: encryption enablement, etc diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c index d01496fd27fe..80e539931e34 100644 --- a/drivers/usb/wusbcore/wa-hc.c +++ b/drivers/usb/wusbcore/wa-hc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wire Adapter Host Controller Driver * Common items to HWA and DWA based HCDs diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h index edc7267157f3..ab5fc274a73f 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/usb/wusbcore/wa-hc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * HWA Host Controller Driver * Wire Adapter Control/Data Streaming Iface (WUSB1.0[8]) diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/usb/wusbcore/wa-nep.c index e3819fc182b0..4cfc5ba70e62 100644 --- a/drivers/usb/wusbcore/wa-nep.c +++ b/drivers/usb/wusbcore/wa-nep.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8]) * Notification EndPoint support diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c index c7ecdbe19a32..aff01f19f09e 100644 --- a/drivers/usb/wusbcore/wa-rpipe.c +++ b/drivers/usb/wusbcore/wa-rpipe.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * WUSB Wire Adapter * rpipe management diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c index bee2404353d9..2a9f95f7d653 100644 --- a/drivers/usb/wusbcore/wa-xfer.c +++ b/drivers/usb/wusbcore/wa-xfer.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * WUSB Wire Adapter * Data transfer and URB enqueing diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c index 5338e42533c8..ebd07e906906 100644 --- a/drivers/usb/wusbcore/wusbhc.c +++ b/drivers/usb/wusbcore/wusbhc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless USB Host Controller * sysfs glue, wusbcore module support and life cycle management diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h index 8c5bd000739b..6ccef2d0c7b3 100644 --- a/drivers/usb/wusbcore/wusbhc.h +++ b/drivers/usb/wusbcore/wusbhc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless USB Host Controller * Common infrastructure for WHCI and HWA WUSB-HC drivers diff --git a/include/linux/usb/association.h b/include/linux/usb/association.h index 0a4a18b3c1bb..d7f3cb9b9db5 100644 --- a/include/linux/usb/association.h +++ b/include/linux/usb/association.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless USB - Cable Based Association * diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index fd73bc0e9027..3119d0ace7aa 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2010 Daniel Mack * diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h index 3d84619110a4..170acd500ea1 100644 --- a/include/linux/usb/audio.h +++ b/include/linux/usb/audio.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * -- USB Audio definitions. * diff --git a/include/linux/usb/c67x00.h b/include/linux/usb/c67x00.h index 83c6b45470ca..2fc39e3b7281 100644 --- a/include/linux/usb/c67x00.h +++ b/include/linux/usb/c67x00.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * usb_c67x00.h: platform definitions for the Cypress C67X00 USB chip * diff --git a/include/linux/usb/cdc-wdm.h b/include/linux/usb/cdc-wdm.h index 0b3f4295c025..9b895f93d8de 100644 --- a/include/linux/usb/cdc-wdm.h +++ b/include/linux/usb/cdc-wdm.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB CDC Device Management subdriver * diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h index b5706f94ee9e..35d784cf32a4 100644 --- a/include/linux/usb/cdc.h +++ b/include/linux/usb/cdc.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB CDC common helpers * diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 1a59699cf82a..1646c06989df 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) /* * Copyright (C) ST-Ericsson 2010-2012 * Contact: Alexey Orishko diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index f665d2ceac20..cef0e44601f8 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * composite.h -- framework for usb gadgets which are composite devices * diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index e479033bd782..a15ce99dfc2d 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2001-2002 by David Brownell * diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h index db0431b39a63..dd742afdc03f 100644 --- a/include/linux/usb/ehci_pdriver.h +++ b/include/linux/usb/ehci_pdriver.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 Hauke Mehrtens * diff --git a/include/linux/usb/g_hid.h b/include/linux/usb/g_hid.h index 50f5745df28c..7581e488c237 100644 --- a/include/linux/usb/g_hid.h +++ b/include/linux/usb/g_hid.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * g_hid.h -- Header file for USB HID gadget driver * diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 523e5a2b5015..0142f3af0da6 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * * diff --git a/include/linux/usb/gpio_vbus.h b/include/linux/usb/gpio_vbus.h index 837bba604a0b..804fb06cf6d6 100644 --- a/include/linux/usb/gpio_vbus.h +++ b/include/linux/usb/gpio_vbus.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * A simple GPIO VBUS sensing driver for B peripheral only devices * with internal transceivers. diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index a1f03ebfde47..176900528822 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2001-2002 by David Brownell * diff --git a/include/linux/usb/input.h b/include/linux/usb/input.h index 0e010b220e85..974befa72ac0 100644 --- a/include/linux/usb/input.h +++ b/include/linux/usb/input.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2005 Dmitry Torokhov * diff --git a/include/linux/usb/isp1301.h b/include/linux/usb/isp1301.h index d3a851c28b6a..dedb3b2473e8 100644 --- a/include/linux/usb/isp1301.h +++ b/include/linux/usb/isp1301.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * NXP ISP1301 USB transceiver driver * diff --git a/include/linux/usb/m66592.h b/include/linux/usb/m66592.h index a4ba31ab2fed..2dfe68183495 100644 --- a/include/linux/usb/m66592.h +++ b/include/linux/usb/m66592.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * M66592 driver platform data * diff --git a/include/linux/usb/musb-ux500.h b/include/linux/usb/musb-ux500.h index 1e2c7130f6e1..c4b7ad9850ca 100644 --- a/include/linux/usb/musb-ux500.h +++ b/include/linux/usb/musb-ux500.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2013 ST-Ericsson AB * diff --git a/include/linux/usb/net2280.h b/include/linux/usb/net2280.h index 725120224472..08b85caecfaf 100644 --- a/include/linux/usb/net2280.h +++ b/include/linux/usb/net2280.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * NetChip 2280 high/full speed USB device controller. * Unlike many such controllers, this one talks PCI. diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index 4031f47629ec..6cbe7a5c2b57 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * OF helpers for usb devices. * diff --git a/include/linux/usb/ohci_pdriver.h b/include/linux/usb/ohci_pdriver.h index 012f2b7eb2b6..7eb16cf587ee 100644 --- a/include/linux/usb/ohci_pdriver.h +++ b/include/linux/usb/ohci_pdriver.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 Hauke Mehrtens * diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h index a0a8f878503c..e78eb577d0fa 100644 --- a/include/linux/usb/otg-fsm.h +++ b/include/linux/usb/otg-fsm.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify it diff --git a/include/linux/usb/phy_companion.h b/include/linux/usb/phy_companion.h index edd2ec23d282..407f530061cd 100644 --- a/include/linux/usb/phy_companion.h +++ b/include/linux/usb/phy_companion.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * phy-companion.h -- phy companion to indicate the comparator part of PHY * diff --git a/include/linux/usb/r8a66597.h b/include/linux/usb/r8a66597.h index 55805f9dcf21..c0753d026bbf 100644 --- a/include/linux/usb/r8a66597.h +++ b/include/linux/usb/r8a66597.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * R8A66597 driver platform data * diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index d2f41e4dd7a8..67102f3d59d4 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB * diff --git a/include/linux/usb/rndis_host.h b/include/linux/usb/rndis_host.h index d44ef85db177..809bccd08455 100644 --- a/include/linux/usb/rndis_host.h +++ b/include/linux/usb/rndis_host.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Host Side support for RNDIS Networking Links * Copyright (C) 2005 by David Brownell diff --git a/include/linux/usb/samsung_usb_phy.h b/include/linux/usb/samsung_usb_phy.h index 916782699f1c..dc0071741695 100644 --- a/include/linux/usb/samsung_usb_phy.h +++ b/include/linux/usb/samsung_usb_phy.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 Samsung Electronics Co.Ltd * http://www.samsung.com/ diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index e2f0ab07eea5..106551a5616e 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * USB Serial Converter stuff * diff --git a/include/linux/usb/storage.h b/include/linux/usb/storage.h index 305ee8db7faf..e0240f864548 100644 --- a/include/linux/usb/storage.h +++ b/include/linux/usb/storage.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 #ifndef __LINUX_USB_STORAGE_H #define __LINUX_USB_STORAGE_H diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 1de16c324ec8..d641ea1660b7 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2010 Google, Inc. * diff --git a/include/linux/usb/tilegx.h b/include/linux/usb/tilegx.h index 2d65e3435680..817908573fe8 100644 --- a/include/linux/usb/tilegx.h +++ b/include/linux/usb/tilegx.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2012 Tilera Corporation. All Rights Reserved. * diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h index 5f07407a367a..c515765adab7 100644 --- a/include/linux/usb/ulpi.h +++ b/include/linux/usb/ulpi.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * ulpi.h -- ULPI defines and function prorotypes * diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h index 11525d8d89a7..7189e3387bf9 100644 --- a/include/linux/usb/usb338x.h +++ b/include/linux/usb/usb338x.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB 338x super/high/full speed USB device controller. * Unlike many such controllers, this one talks PCI. diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 97116379db5f..a69877734c4e 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * USB Networking Link Interface * diff --git a/include/linux/usb/wusb-wa.h b/include/linux/usb/wusb-wa.h index c1257130769b..64a840b5106e 100644 --- a/include/linux/usb/wusb-wa.h +++ b/include/linux/usb/wusb-wa.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless USB Wire Adapter constants and structures. * diff --git a/include/linux/usb/wusb.h b/include/linux/usb/wusb.h index eeb28329fa3c..9e4a3213f2c2 100644 --- a/include/linux/usb/wusb.h +++ b/include/linux/usb/wusb.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Wireless USB Standard Definitions * Event Size Tables diff --git a/include/linux/usb/xhci-dbgp.h b/include/linux/usb/xhci-dbgp.h index 80c1cca1f529..0a37f1283bf0 100644 --- a/include/linux/usb/xhci-dbgp.h +++ b/include/linux/usb/xhci-dbgp.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Standalone xHCI debug capability driver * diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h index 04a26285416c..79aab0065ec8 100644 --- a/include/linux/usbdevice_fs.h +++ b/include/linux/usbdevice_fs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /*****************************************************************************/ /* -- cgit v1.2.3-59-g8ed1b From a8497b31fee650846fcc5a1e41f4ef22318a9925 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 24 Oct 2017 03:00:18 -0700 Subject: tty: cyclades: Convert timers to use timer_setup() In preparation for unconditionally passing the struct timer_list pointer to all timer callbacks, switch to using the new timer_setup() and from_timer() to pass the timer pointer explicitly. Moves timer structures from global to attached to struct cyclades_port. Cc: Jiri Slaby Signed-off-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/tty/cyclades.c | 16 ++++++---------- include/linux/cyclades.h | 3 +++ 2 files changed, 9 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/tty/cyclades.c b/drivers/tty/cyclades.c index d272bc4e7fb5..f30f7a90995c 100644 --- a/drivers/tty/cyclades.c +++ b/drivers/tty/cyclades.c @@ -286,8 +286,7 @@ static long cyz_polling_cycle = CZ_DEF_POLL; static DEFINE_TIMER(cyz_timerlist, cyz_poll, 0, 0); #else /* CONFIG_CYZ_INTR */ -static void cyz_rx_restart(unsigned long); -static struct timer_list cyz_rx_full_timer[NR_PORTS]; +static void cyz_rx_restart(struct timer_list *); #endif /* CONFIG_CYZ_INTR */ static void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val) @@ -992,10 +991,8 @@ static void cyz_handle_rx(struct cyclades_port *info) else char_count = rx_put - rx_get + rx_bufsize; if (char_count >= readl(&buf_ctrl->rx_threshold) && - !timer_pending(&cyz_rx_full_timer[ - info->line])) - mod_timer(&cyz_rx_full_timer[info->line], - jiffies + 1); + !timer_pending(&info->rx_full_timer)) + mod_timer(&info->rx_full_timer, jiffies + 1); #endif info->idle_stats.recv_idle = jiffies; tty_schedule_flip(&info->port); @@ -1197,9 +1194,9 @@ static irqreturn_t cyz_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } /* cyz_interrupt */ -static void cyz_rx_restart(unsigned long arg) +static void cyz_rx_restart(struct timer_list *t) { - struct cyclades_port *info = (struct cyclades_port *)arg; + struct cyclades_port *info = from_timer(info, t, rx_full_timer); struct cyclades_card *card = info->card; int retval; __u32 channel = info->line - card->first_line; @@ -3097,8 +3094,7 @@ static int cy_init_card(struct cyclades_card *cinfo) else info->xmit_fifo_size = 4 * CYZ_FIFO_SIZE; #ifdef CONFIG_CYZ_INTR - setup_timer(&cyz_rx_full_timer[port], - cyz_rx_restart, (unsigned long)info); + timer_setup(&info->rx_full_timer, cyz_rx_restart, 0); #endif } else { unsigned short chip_number; diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index 19ae518f5471..3638eb2d52f6 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -156,6 +156,9 @@ struct cyclades_port { struct cyclades_icount icount; struct completion shutdown_wait; int throttle; +#ifdef CONFIG_CYZ_INTR + struct timer_list rx_full_timer; +#endif }; #define CLOSING_WAIT_DELAY 30*HZ -- cgit v1.2.3-59-g8ed1b From 88022d7201e96b43f1754b0358fc6bcd8dbdcde1 Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 5 Nov 2017 02:21:12 +0800 Subject: blk-mq: don't handle failure in .get_budget It is enough to just check if we can get the budget via .get_budget(). And we don't need to deal with device state change in .get_budget(). For SCSI, one issue to be fixed is that we have to call scsi_mq_uninit_cmd() to free allocated ressources if SCSI device fails to handle the request. And it isn't enough to simply call blk_mq_end_request() to do that if this request is marked as RQF_DONTPREP. Fixes: 0df21c86bdbf(scsi: implement .get_budget and .put_budget for blk-mq) Signed-off-by: Ming Lei Signed-off-by: Jens Axboe --- block/blk-mq-sched.c | 14 ++------------ block/blk-mq.c | 17 ++++------------- block/blk-mq.h | 5 ++--- drivers/scsi/scsi_lib.c | 11 +++-------- include/linux/blk-mq.h | 2 +- 5 files changed, 12 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c index 7775f6b12fa9..13a27d4d1671 100644 --- a/block/blk-mq-sched.c +++ b/block/blk-mq-sched.c @@ -94,23 +94,18 @@ static void blk_mq_do_dispatch_sched(struct blk_mq_hw_ctx *hctx) do { struct request *rq; - blk_status_t ret; if (e->type->ops.mq.has_work && !e->type->ops.mq.has_work(hctx)) break; - ret = blk_mq_get_dispatch_budget(hctx); - if (ret == BLK_STS_RESOURCE) + if (!blk_mq_get_dispatch_budget(hctx)) break; rq = e->type->ops.mq.dispatch_request(hctx); if (!rq) { blk_mq_put_dispatch_budget(hctx); break; - } else if (ret != BLK_STS_OK) { - blk_mq_end_request(rq, ret); - continue; } /* @@ -146,22 +141,17 @@ static void blk_mq_do_dispatch_ctx(struct blk_mq_hw_ctx *hctx) do { struct request *rq; - blk_status_t ret; if (!sbitmap_any_bit_set(&hctx->ctx_map)) break; - ret = blk_mq_get_dispatch_budget(hctx); - if (ret == BLK_STS_RESOURCE) + if (!blk_mq_get_dispatch_budget(hctx)) break; rq = blk_mq_dequeue_from_ctx(hctx, ctx); if (!rq) { blk_mq_put_dispatch_budget(hctx); break; - } else if (ret != BLK_STS_OK) { - blk_mq_end_request(rq, ret); - continue; } /* diff --git a/block/blk-mq.c b/block/blk-mq.c index 13cdccef543c..c9fa4b294664 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -1137,13 +1137,8 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, } } - if (!got_budget) { - ret = blk_mq_get_dispatch_budget(hctx); - if (ret == BLK_STS_RESOURCE) - break; - if (ret != BLK_STS_OK) - goto fail_rq; - } + if (!got_budget && !blk_mq_get_dispatch_budget(hctx)) + break; list_del_init(&rq->queuelist); @@ -1170,7 +1165,6 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, break; } - fail_rq: if (unlikely(ret != BLK_STS_OK)) { errors++; blk_mq_end_request(rq, BLK_STS_IOERR); @@ -1642,12 +1636,10 @@ static void __blk_mq_try_issue_directly(struct blk_mq_hw_ctx *hctx, if (!blk_mq_get_driver_tag(rq, NULL, false)) goto insert; - ret = blk_mq_get_dispatch_budget(hctx); - if (ret == BLK_STS_RESOURCE) { + if (!blk_mq_get_dispatch_budget(hctx)) { blk_mq_put_driver_tag(rq); goto insert; - } else if (ret != BLK_STS_OK) - goto fail_rq; + } new_cookie = request_to_qc_t(hctx, rq); @@ -1665,7 +1657,6 @@ static void __blk_mq_try_issue_directly(struct blk_mq_hw_ctx *hctx, __blk_mq_requeue_request(rq); goto insert; default: - fail_rq: *cookie = BLK_QC_T_NONE; blk_mq_end_request(rq, ret); return; diff --git a/block/blk-mq.h b/block/blk-mq.h index 522b420dedc0..f97aceff76e9 100644 --- a/block/blk-mq.h +++ b/block/blk-mq.h @@ -147,14 +147,13 @@ static inline void blk_mq_put_dispatch_budget(struct blk_mq_hw_ctx *hctx) q->mq_ops->put_budget(hctx); } -static inline blk_status_t blk_mq_get_dispatch_budget( - struct blk_mq_hw_ctx *hctx) +static inline bool blk_mq_get_dispatch_budget(struct blk_mq_hw_ctx *hctx) { struct request_queue *q = hctx->queue; if (q->mq_ops->get_budget) return q->mq_ops->get_budget(hctx); - return BLK_STS_OK; + return true; } #endif diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 22a7e4c47207..286ea983c9e3 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1955,27 +1955,22 @@ static void scsi_mq_put_budget(struct blk_mq_hw_ctx *hctx) put_device(&sdev->sdev_gendev); } -static blk_status_t scsi_mq_get_budget(struct blk_mq_hw_ctx *hctx) +static bool scsi_mq_get_budget(struct blk_mq_hw_ctx *hctx) { struct request_queue *q = hctx->queue; struct scsi_device *sdev = q->queuedata; - blk_status_t ret; - - ret = prep_to_mq(scsi_prep_state_check(sdev, NULL)); - if (ret == BLK_STS_RESOURCE || ret != BLK_STS_OK) - return ret; if (!get_device(&sdev->sdev_gendev)) goto out; if (!scsi_dev_queue_ready(q, sdev)) goto out_put_device; - return BLK_STS_OK; + return true; out_put_device: put_device(&sdev->sdev_gendev); out: - return BLK_STS_RESOURCE; + return false; } static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx *hctx, diff --git a/include/linux/blk-mq.h b/include/linux/blk-mq.h index f2e3079eecdd..674641527da7 100644 --- a/include/linux/blk-mq.h +++ b/include/linux/blk-mq.h @@ -92,7 +92,7 @@ struct blk_mq_queue_data { typedef blk_status_t (queue_rq_fn)(struct blk_mq_hw_ctx *, const struct blk_mq_queue_data *); -typedef blk_status_t (get_budget_fn)(struct blk_mq_hw_ctx *); +typedef bool (get_budget_fn)(struct blk_mq_hw_ctx *); typedef void (put_budget_fn)(struct blk_mq_hw_ctx *); typedef enum blk_eh_timer_return (timeout_fn)(struct request *, bool); typedef int (init_hctx_fn)(struct blk_mq_hw_ctx *, void *, unsigned int); -- cgit v1.2.3-59-g8ed1b From 27c565ae9d554fa1c00c799754cff43476c8d3b5 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Sat, 4 Nov 2017 08:53:27 -0700 Subject: ipv6: remove IN6_ADDR_HSIZE from addrconf.h IN6_ADDR_HSIZE is private to addrconf.c, move it here to avoid confusion. Signed-off-by: Eric Dumazet Acked-by: David Ahern Signed-off-by: David S. Miller --- include/net/addrconf.h | 3 --- net/ipv6/addrconf.c | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 3357332ea375..b623b65a79d1 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -59,9 +59,6 @@ struct in6_validator_info { struct netlink_ext_ack *extack; }; -#define IN6_ADDR_HSIZE_SHIFT 8 -#define IN6_ADDR_HSIZE (1 << IN6_ADDR_HSIZE_SHIFT) - int addrconf_init(void); void addrconf_cleanup(void); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 69b8cdb43aa2..66d8c3d912fd 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -157,6 +157,8 @@ static int ipv6_generate_stable_address(struct in6_addr *addr, u8 dad_count, const struct inet6_dev *idev); +#define IN6_ADDR_HSIZE_SHIFT 8 +#define IN6_ADDR_HSIZE (1 << IN6_ADDR_HSIZE_SHIFT) /* * Configured unicast address hash table */ -- cgit v1.2.3-59-g8ed1b From ee20598194500e82c477cf13e52b58e569446ed0 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Tue, 18 Jul 2017 15:42:15 -0500 Subject: net/dcb: Add dscp to priority selector type IEEE specification P802.1Qcd/D2.1 defines priority selector 5. This APP TLV selector defines DSCP to priority map. This patch defines such DSCP selector. Signed-off-by: Huy Nguyen Reviewed-by: Parav Pandit Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- include/uapi/linux/dcbnl.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/uapi/linux/dcbnl.h b/include/uapi/linux/dcbnl.h index b6170a6af7c2..2c0c6453c3f4 100644 --- a/include/uapi/linux/dcbnl.h +++ b/include/uapi/linux/dcbnl.h @@ -206,6 +206,7 @@ struct cee_pfc { #define IEEE_8021QAZ_APP_SEL_STREAM 2 #define IEEE_8021QAZ_APP_SEL_DGRAM 3 #define IEEE_8021QAZ_APP_SEL_ANY 4 +#define IEEE_8021QAZ_APP_SEL_DSCP 5 /* This structure contains the IEEE 802.1Qaz APP managed object. This * object is also used for the CEE std as well. -- cgit v1.2.3-59-g8ed1b From c02762eb20cb57ec5b7c037b056c37d5838c803f Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Tue, 18 Jul 2017 16:03:17 -0500 Subject: net/mlx5: QCAM register firmware command support The QCAM register provides capability bit for all the QoS registers using ACCESS_REG command. Signed-off-by: Huy Nguyen Reviewed-by: Parav Pandit Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/fw.c | 10 ++++++ .../net/ethernet/mellanox/mlx5/core/mlx5_core.h | 2 ++ drivers/net/ethernet/mellanox/mlx5/core/port.c | 12 +++++++ include/linux/mlx5/device.h | 14 ++++++++ include/linux/mlx5/driver.h | 2 ++ include/linux/mlx5/mlx5_ifc.h | 40 +++++++++++++++++++++- 6 files changed, 79 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fw.c b/drivers/net/ethernet/mellanox/mlx5/core/fw.c index 2c71557d1cee..5ef1b56b6a96 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fw.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fw.c @@ -106,6 +106,13 @@ static int mlx5_get_mcam_reg(struct mlx5_core_dev *dev) MLX5_MCAM_REGS_FIRST_128); } +static int mlx5_get_qcam_reg(struct mlx5_core_dev *dev) +{ + return mlx5_query_qcam_reg(dev, dev->caps.qcam, + MLX5_QCAM_FEATURE_ENHANCED_FEATURES, + MLX5_QCAM_REGS_FIRST_128); +} + int mlx5_query_hca_caps(struct mlx5_core_dev *dev) { int err; @@ -182,6 +189,9 @@ int mlx5_query_hca_caps(struct mlx5_core_dev *dev) if (MLX5_CAP_GEN(dev, mcam_reg)) mlx5_get_mcam_reg(dev); + if (MLX5_CAP_GEN(dev, qcam_reg)) + mlx5_get_qcam_reg(dev); + return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 8f00de2fe283..ff4a0b889a6f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -122,6 +122,8 @@ int mlx5_query_pcam_reg(struct mlx5_core_dev *dev, u32 *pcam, u8 feature_group, u8 access_reg_group); int mlx5_query_mcam_reg(struct mlx5_core_dev *dev, u32 *mcap, u8 feature_group, u8 access_reg_group); +int mlx5_query_qcam_reg(struct mlx5_core_dev *mdev, u32 *qcam, + u8 feature_group, u8 access_reg_group); void mlx5_lag_add(struct mlx5_core_dev *dev, struct net_device *netdev); void mlx5_lag_remove(struct mlx5_core_dev *dev); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index e07061f565d6..b6553be841f9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -98,6 +98,18 @@ int mlx5_query_mcam_reg(struct mlx5_core_dev *dev, u32 *mcam, u8 feature_group, return mlx5_core_access_reg(dev, in, sz, mcam, sz, MLX5_REG_MCAM, 0, 0); } +int mlx5_query_qcam_reg(struct mlx5_core_dev *mdev, u32 *qcam, + u8 feature_group, u8 access_reg_group) +{ + u32 in[MLX5_ST_SZ_DW(qcam_reg)] = {}; + int sz = MLX5_ST_SZ_BYTES(qcam_reg); + + MLX5_SET(qcam_reg, in, feature_group, feature_group); + MLX5_SET(qcam_reg, in, access_reg_group, access_reg_group); + + return mlx5_core_access_reg(mdev, in, sz, qcam, sz, MLX5_REG_QCAM, 0, 0); +} + struct mlx5_reg_pcap { u8 rsvd0; u8 port_num; diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index e32dbc4934db..6d79b3f79458 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -1000,6 +1000,14 @@ enum mlx5_mcam_feature_groups { MLX5_MCAM_FEATURE_ENHANCED_FEATURES = 0x0, }; +enum mlx5_qcam_reg_groups { + MLX5_QCAM_REGS_FIRST_128 = 0x0, +}; + +enum mlx5_qcam_feature_groups { + MLX5_QCAM_FEATURE_ENHANCED_FEATURES = 0x0, +}; + /* GET Dev Caps macros */ #define MLX5_CAP_GEN(mdev, cap) \ MLX5_GET(cmd_hca_cap, mdev->caps.hca_cur[MLX5_CAP_GENERAL], cap) @@ -1108,6 +1116,12 @@ enum mlx5_mcam_feature_groups { #define MLX5_CAP_MCAM_FEATURE(mdev, fld) \ MLX5_GET(mcam_reg, (mdev)->caps.mcam, mng_feature_cap_mask.enhanced_features.fld) +#define MLX5_CAP_QCAM_REG(mdev, fld) \ + MLX5_GET(qcam_reg, (mdev)->caps.qcam, qos_access_reg_cap_mask.reg_cap.fld) + +#define MLX5_CAP_QCAM_FEATURE(mdev, fld) \ + MLX5_GET(qcam_reg, (mdev)->caps.qcam, qos_feature_cap_mask.feature_cap.fld) + #define MLX5_CAP_FPGA(mdev, cap) \ MLX5_GET(fpga_cap, (mdev)->caps.fpga, cap) diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index 08c77b7e59cb..ed5be52282ea 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -109,6 +109,7 @@ enum { enum { MLX5_REG_QETCR = 0x4005, MLX5_REG_QTCT = 0x400a, + MLX5_REG_QCAM = 0x4019, MLX5_REG_DCBX_PARAM = 0x4020, MLX5_REG_DCBX_APP = 0x4021, MLX5_REG_FPGA_CAP = 0x4022, @@ -798,6 +799,7 @@ struct mlx5_core_dev { u32 pcam[MLX5_ST_SZ_DW(pcam_reg)]; u32 mcam[MLX5_ST_SZ_DW(mcam_reg)]; u32 fpga[MLX5_ST_SZ_DW(fpga_cap)]; + u32 qcam[MLX5_ST_SZ_DW(qcam_reg)]; } caps; phys_addr_t iseg_base; struct mlx5_init_seg __iomem *iseg; diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 69772347f866..f127c5b310c5 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -838,7 +838,8 @@ struct mlx5_ifc_cmd_hca_cap_bits { u8 cc_modify_allowed[0x1]; u8 start_pad[0x1]; u8 cache_line_128byte[0x1]; - u8 reserved_at_165[0xb]; + u8 reserved_at_165[0xa]; + u8 qcam_reg[0x1]; u8 gid_table_size[0x10]; u8 out_of_seq_cnt[0x1]; @@ -7890,6 +7891,43 @@ struct mlx5_ifc_mcam_reg_bits { u8 reserved_at_1c0[0x80]; }; +struct mlx5_ifc_qcam_access_reg_cap_mask { + u8 qcam_access_reg_cap_mask_127_to_20[0x6C]; + u8 qpdpm[0x1]; + u8 qcam_access_reg_cap_mask_18_to_4[0x0F]; + u8 qdpm[0x1]; + u8 qpts[0x1]; + u8 qcap[0x1]; + u8 qcam_access_reg_cap_mask_0[0x1]; +}; + +struct mlx5_ifc_qcam_qos_feature_cap_mask { + u8 qcam_qos_feature_cap_mask_127_to_1[0x7F]; + u8 qpts_trust_both[0x1]; +}; + +struct mlx5_ifc_qcam_reg_bits { + u8 reserved_at_0[0x8]; + u8 feature_group[0x8]; + u8 reserved_at_10[0x8]; + u8 access_reg_group[0x8]; + u8 reserved_at_20[0x20]; + + union { + struct mlx5_ifc_qcam_access_reg_cap_mask reg_cap; + u8 reserved_at_0[0x80]; + } qos_access_reg_cap_mask; + + u8 reserved_at_c0[0x80]; + + union { + struct mlx5_ifc_qcam_qos_feature_cap_mask feature_cap; + u8 reserved_at_0[0x80]; + } qos_feature_cap_mask; + + u8 reserved_at_1c0[0x80]; +}; + struct mlx5_ifc_pcap_reg_bits { u8 reserved_at_0[0x8]; u8 local_port[0x8]; -- cgit v1.2.3-59-g8ed1b From 71c70eb21c33c60433b95e72a59d40bb128db649 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Wed, 2 Aug 2017 21:36:23 -0500 Subject: net/mlx5: Add MLX5_SET16 and MLX5_GET16 Add MLX5_SET16 and MLX5_GET16 for 16bit structure field in firmware command. Signed-off-by: Huy Nguyen Reviewed-by: Parav Pandit Reviewed-by: Eli Cohen Signed-off-by: Saeed Mahameed --- include/linux/mlx5/device.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'include') diff --git a/include/linux/mlx5/device.h b/include/linux/mlx5/device.h index 6d79b3f79458..409ffb14298a 100644 --- a/include/linux/mlx5/device.h +++ b/include/linux/mlx5/device.h @@ -49,11 +49,15 @@ #define __mlx5_nullp(typ) ((struct mlx5_ifc_##typ##_bits *)0) #define __mlx5_bit_sz(typ, fld) sizeof(__mlx5_nullp(typ)->fld) #define __mlx5_bit_off(typ, fld) (offsetof(struct mlx5_ifc_##typ##_bits, fld)) +#define __mlx5_16_off(typ, fld) (__mlx5_bit_off(typ, fld) / 16) #define __mlx5_dw_off(typ, fld) (__mlx5_bit_off(typ, fld) / 32) #define __mlx5_64_off(typ, fld) (__mlx5_bit_off(typ, fld) / 64) +#define __mlx5_16_bit_off(typ, fld) (16 - __mlx5_bit_sz(typ, fld) - (__mlx5_bit_off(typ, fld) & 0xf)) #define __mlx5_dw_bit_off(typ, fld) (32 - __mlx5_bit_sz(typ, fld) - (__mlx5_bit_off(typ, fld) & 0x1f)) #define __mlx5_mask(typ, fld) ((u32)((1ull << __mlx5_bit_sz(typ, fld)) - 1)) #define __mlx5_dw_mask(typ, fld) (__mlx5_mask(typ, fld) << __mlx5_dw_bit_off(typ, fld)) +#define __mlx5_mask16(typ, fld) ((u16)((1ull << __mlx5_bit_sz(typ, fld)) - 1)) +#define __mlx5_16_mask(typ, fld) (__mlx5_mask16(typ, fld) << __mlx5_16_bit_off(typ, fld)) #define __mlx5_st_sz_bits(typ) sizeof(struct mlx5_ifc_##typ##_bits) #define MLX5_FLD_SZ_BYTES(typ, fld) (__mlx5_bit_sz(typ, fld) / 8) @@ -116,6 +120,19 @@ __mlx5_mask(typ, fld)) ___t; \ }) +#define MLX5_GET16(typ, p, fld) ((be16_to_cpu(*((__be16 *)(p) +\ +__mlx5_16_off(typ, fld))) >> __mlx5_16_bit_off(typ, fld)) & \ +__mlx5_mask16(typ, fld)) + +#define MLX5_SET16(typ, p, fld, v) do { \ + u16 _v = v; \ + BUILD_BUG_ON(__mlx5_st_sz_bits(typ) % 16); \ + *((__be16 *)(p) + __mlx5_16_off(typ, fld)) = \ + cpu_to_be16((be16_to_cpu(*((__be16 *)(p) + __mlx5_16_off(typ, fld))) & \ + (~__mlx5_16_mask(typ, fld))) | (((_v) & __mlx5_mask16(typ, fld)) \ + << __mlx5_16_bit_off(typ, fld))); \ +} while (0) + /* Big endian getters */ #define MLX5_GET64_BE(typ, p, fld) (*((__be64 *)(p) +\ __mlx5_64_off(typ, fld))) -- cgit v1.2.3-59-g8ed1b From 415a64aa8dc6b4fc478609c549ca652d95a12f13 Mon Sep 17 00:00:00 2001 From: Huy Nguyen Date: Tue, 18 Jul 2017 16:08:46 -0500 Subject: net/mlx5: QPTS and QPDPM register firmware command support The QPTS register allows changing the priority trust state between pcp and dscp. Add support to get/set trust state from device. When the port is in pcp/dscp trust state, packet is routed by hardware to matching priority based on its pcp/dscp value respectively. The QPDPM register allow channing the dscp to priority mapping. Add support to get/set dscp to priority mapping from device. Note that to change a dscp mapping, the "e" bit of this dscp structure must be set in the QPDPM firmware command. Signed-off-by: Huy Nguyen Reviewed-by: Parav Pandit Reviewed-by: Or Gerlitz Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/port.c | 99 ++++++++++++++++++++++++++ include/linux/mlx5/driver.h | 7 ++ include/linux/mlx5/mlx5_ifc.h | 20 ++++++ include/linux/mlx5/port.h | 5 ++ 4 files changed, 131 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/port.c b/drivers/net/ethernet/mellanox/mlx5/core/port.c index b6553be841f9..c37d00cd472a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/port.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/port.c @@ -971,3 +971,102 @@ int mlx5_set_mtppse(struct mlx5_core_dev *mdev, u8 pin, u8 arm, u8 mode) return mlx5_core_access_reg(mdev, in, sizeof(in), out, sizeof(out), MLX5_REG_MTPPSE, 0, 1); } + +int mlx5_set_trust_state(struct mlx5_core_dev *mdev, u8 trust_state) +{ + u32 out[MLX5_ST_SZ_DW(qpts_reg)] = {}; + u32 in[MLX5_ST_SZ_DW(qpts_reg)] = {}; + int err; + + MLX5_SET(qpts_reg, in, local_port, 1); + MLX5_SET(qpts_reg, in, trust_state, trust_state); + + err = mlx5_core_access_reg(mdev, in, sizeof(in), out, + sizeof(out), MLX5_REG_QPTS, 0, 1); + return err; +} + +int mlx5_query_trust_state(struct mlx5_core_dev *mdev, u8 *trust_state) +{ + u32 out[MLX5_ST_SZ_DW(qpts_reg)] = {}; + u32 in[MLX5_ST_SZ_DW(qpts_reg)] = {}; + int err; + + MLX5_SET(qpts_reg, in, local_port, 1); + + err = mlx5_core_access_reg(mdev, in, sizeof(in), out, + sizeof(out), MLX5_REG_QPTS, 0, 0); + if (!err) + *trust_state = MLX5_GET(qpts_reg, out, trust_state); + + return err; +} + +int mlx5_set_dscp2prio(struct mlx5_core_dev *mdev, u8 dscp, u8 prio) +{ + int sz = MLX5_ST_SZ_BYTES(qpdpm_reg); + void *qpdpm_dscp; + void *out; + void *in; + int err; + + in = kzalloc(sz, GFP_KERNEL); + out = kzalloc(sz, GFP_KERNEL); + if (!in || !out) { + err = -ENOMEM; + goto out; + } + + MLX5_SET(qpdpm_reg, in, local_port, 1); + err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_QPDPM, 0, 0); + if (err) + goto out; + + memcpy(in, out, sz); + MLX5_SET(qpdpm_reg, in, local_port, 1); + + /* Update the corresponding dscp entry */ + qpdpm_dscp = MLX5_ADDR_OF(qpdpm_reg, in, dscp[dscp]); + MLX5_SET16(qpdpm_dscp_reg, qpdpm_dscp, prio, prio); + MLX5_SET16(qpdpm_dscp_reg, qpdpm_dscp, e, 1); + err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_QPDPM, 0, 1); + +out: + kfree(in); + kfree(out); + return err; +} + +/* dscp2prio[i]: priority that dscp i mapped to */ +#define MLX5E_SUPPORTED_DSCP 64 +int mlx5_query_dscp2prio(struct mlx5_core_dev *mdev, u8 *dscp2prio) +{ + int sz = MLX5_ST_SZ_BYTES(qpdpm_reg); + void *qpdpm_dscp; + void *out; + void *in; + int err; + int i; + + in = kzalloc(sz, GFP_KERNEL); + out = kzalloc(sz, GFP_KERNEL); + if (!in || !out) { + err = -ENOMEM; + goto out; + } + + MLX5_SET(qpdpm_reg, in, local_port, 1); + err = mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_QPDPM, 0, 0); + if (err) + goto out; + + for (i = 0; i < (MLX5E_SUPPORTED_DSCP); i++) { + qpdpm_dscp = MLX5_ADDR_OF(qpdpm_reg, out, dscp[i]); + dscp2prio[i] = MLX5_GET16(qpdpm_dscp_reg, qpdpm_dscp, prio); + } + +out: + kfree(in); + kfree(out); + return err; +} diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index ed5be52282ea..a886b51511ab 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -107,8 +107,10 @@ enum { }; enum { + MLX5_REG_QPTS = 0x4002, MLX5_REG_QETCR = 0x4005, MLX5_REG_QTCT = 0x400a, + MLX5_REG_QPDPM = 0x4013, MLX5_REG_QCAM = 0x4019, MLX5_REG_DCBX_PARAM = 0x4020, MLX5_REG_DCBX_APP = 0x4021, @@ -142,6 +144,11 @@ enum { MLX5_REG_MCAM = 0x907f, }; +enum mlx5_qpts_trust_state { + MLX5_QPTS_TRUST_PCP = 1, + MLX5_QPTS_TRUST_DSCP = 2, +}; + enum mlx5_dcbx_oper_mode { MLX5E_DCBX_PARAM_VER_OPER_HOST = 0x0, MLX5E_DCBX_PARAM_VER_OPER_AUTO = 0x3, diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index f127c5b310c5..3e5363f760dd 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -8578,6 +8578,26 @@ struct mlx5_ifc_qetc_reg_bits { struct mlx5_ifc_ets_global_config_reg_bits global_configuration; }; +struct mlx5_ifc_qpdpm_dscp_reg_bits { + u8 e[0x1]; + u8 reserved_at_01[0x0b]; + u8 prio[0x04]; +}; + +struct mlx5_ifc_qpdpm_reg_bits { + u8 reserved_at_0[0x8]; + u8 local_port[0x8]; + u8 reserved_at_10[0x10]; + struct mlx5_ifc_qpdpm_dscp_reg_bits dscp[64]; +}; + +struct mlx5_ifc_qpts_reg_bits { + u8 reserved_at_0[0x8]; + u8 local_port[0x8]; + u8 reserved_at_10[0x2d]; + u8 trust_state[0x3]; +}; + struct mlx5_ifc_qtct_reg_bits { u8 reserved_at_0[0x8]; u8 port_number[0x8]; diff --git a/include/linux/mlx5/port.h b/include/linux/mlx5/port.h index c59af8ab753a..035f0d4dc9fe 100644 --- a/include/linux/mlx5/port.h +++ b/include/linux/mlx5/port.h @@ -179,4 +179,9 @@ int mlx5_query_module_eeprom(struct mlx5_core_dev *dev, int mlx5_query_port_dcbx_param(struct mlx5_core_dev *mdev, u32 *out); int mlx5_set_port_dcbx_param(struct mlx5_core_dev *mdev, u32 *in); + +int mlx5_set_trust_state(struct mlx5_core_dev *mdev, u8 trust_state); +int mlx5_query_trust_state(struct mlx5_core_dev *mdev, u8 *trust_state); +int mlx5_set_dscp2prio(struct mlx5_core_dev *mdev, u8 dscp, u8 prio); +int mlx5_query_dscp2prio(struct mlx5_core_dev *mdev, u8 *dscp2prio); #endif /* __MLX5_PORT_H__ */ -- cgit v1.2.3-59-g8ed1b From eb3b05fb0ff2cf0097e70b6c5e3afbe854a8deb7 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 4 Nov 2017 21:49:26 +0100 Subject: i2c: nuc900: remove platform_data, too Commit 7da62cb1853025 ("i2c: nuc900: remove driver") removed the driver, we should remove the platform_data as well. Signed-off-by: Wolfram Sang --- include/linux/platform_data/i2c-nuc900.h | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 include/linux/platform_data/i2c-nuc900.h (limited to 'include') diff --git a/include/linux/platform_data/i2c-nuc900.h b/include/linux/platform_data/i2c-nuc900.h deleted file mode 100644 index 9ffb12d06e91..000000000000 --- a/include/linux/platform_data/i2c-nuc900.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __ASM_ARCH_NUC900_I2C_H -#define __ASM_ARCH_NUC900_I2C_H - -struct nuc900_platform_i2c { - int bus_num; - unsigned long bus_freq; -}; - -#endif /* __ASM_ARCH_NUC900_I2C_H */ -- cgit v1.2.3-59-g8ed1b From 9354d452034273a50a4fd703bea31e5d6b1fc20b Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 2 Nov 2017 17:04:37 -0200 Subject: openvswitch: reliable interface indentification in port dumps This patch allows reliable identification of netdevice interfaces connected to openvswitch bridges. In particular, user space queries the netdev interfaces belonging to the ports for statistics, up/down state, etc. Datapath dump needs to provide enough information for the user space to be able to do that. Currently, only interface names are returned. This is not sufficient, as openvswitch allows its ports to be in different name spaces and the interface name is valid only in its name space. What is needed and generally used in other netlink APIs, is the pair ifindex+netnsid. The solution is addition of the ifindex+netnsid pair (or only ifindex if in the same name space) to vport get/dump operation. On request side, ideally the ifindex+netnsid pair could be used to get/set/del the corresponding vport. This is not implemented by this patch and can be added later if needed. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- include/uapi/linux/openvswitch.h | 2 ++ net/openvswitch/datapath.c | 47 +++++++++++++++++++++++++++++----------- net/openvswitch/datapath.h | 4 ++-- net/openvswitch/dp_notify.c | 4 ++-- 4 files changed, 40 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index ffe397daad49..501e4c4e2a03 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -258,6 +258,8 @@ enum ovs_vport_attr { /* receiving upcalls */ OVS_VPORT_ATTR_STATS, /* struct ovs_vport_stats */ OVS_VPORT_ATTR_PAD, + OVS_VPORT_ATTR_IFINDEX, + OVS_VPORT_ATTR_NETNSID, __OVS_VPORT_ATTR_MAX }; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index c3aec6227c91..4d38ac044cee 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -1848,7 +1848,8 @@ static struct genl_family dp_datapath_genl_family __ro_after_init = { /* Called with ovs_mutex or RCU read lock. */ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, - u32 portid, u32 seq, u32 flags, u8 cmd) + struct net *net, u32 portid, u32 seq, + u32 flags, u8 cmd) { struct ovs_header *ovs_header; struct ovs_vport_stats vport_stats; @@ -1864,9 +1865,17 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb, if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) || nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) || nla_put_string(skb, OVS_VPORT_ATTR_NAME, - ovs_vport_name(vport))) + ovs_vport_name(vport)) || + nla_put_u32(skb, OVS_VPORT_ATTR_IFINDEX, vport->dev->ifindex)) goto nla_put_failure; + if (!net_eq(net, dev_net(vport->dev))) { + int id = peernet2id_alloc(net, dev_net(vport->dev)); + + if (nla_put_s32(skb, OVS_VPORT_ATTR_NETNSID, id)) + goto nla_put_failure; + } + ovs_vport_get_stats(vport, &vport_stats); if (nla_put_64bit(skb, OVS_VPORT_ATTR_STATS, sizeof(struct ovs_vport_stats), &vport_stats, @@ -1896,8 +1905,8 @@ static struct sk_buff *ovs_vport_cmd_alloc_info(void) } /* Called with ovs_mutex, only via ovs_dp_notify_wq(). */ -struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid, - u32 seq, u8 cmd) +struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net, + u32 portid, u32 seq, u8 cmd) { struct sk_buff *skb; int retval; @@ -1906,7 +1915,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid, if (!skb) return ERR_PTR(-ENOMEM); - retval = ovs_vport_cmd_fill_info(vport, skb, portid, seq, 0, cmd); + retval = ovs_vport_cmd_fill_info(vport, skb, net, portid, seq, 0, cmd); BUG_ON(retval < 0); return skb; @@ -1920,6 +1929,8 @@ static struct vport *lookup_vport(struct net *net, struct datapath *dp; struct vport *vport; + if (a[OVS_VPORT_ATTR_IFINDEX]) + return ERR_PTR(-EOPNOTSUPP); if (a[OVS_VPORT_ATTR_NAME]) { vport = ovs_vport_locate(net, nla_data(a[OVS_VPORT_ATTR_NAME])); if (!vport) @@ -1944,6 +1955,7 @@ static struct vport *lookup_vport(struct net *net, return vport; } else return ERR_PTR(-EINVAL); + } /* Called with ovs_mutex */ @@ -1983,6 +1995,8 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info) if (!a[OVS_VPORT_ATTR_NAME] || !a[OVS_VPORT_ATTR_TYPE] || !a[OVS_VPORT_ATTR_UPCALL_PID]) return -EINVAL; + if (a[OVS_VPORT_ATTR_IFINDEX]) + return -EOPNOTSUPP; port_no = a[OVS_VPORT_ATTR_PORT_NO] ? nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]) : 0; @@ -2032,8 +2046,9 @@ restart: goto exit_unlock_free; } - err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, - info->snd_seq, 0, OVS_VPORT_CMD_NEW); + err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info), + info->snd_portid, info->snd_seq, 0, + OVS_VPORT_CMD_NEW); if (netdev_get_fwd_headroom(vport->dev) > dp->max_headroom) update_headroom(dp); @@ -2090,8 +2105,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info) goto exit_unlock_free; } - err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, - info->snd_seq, 0, OVS_VPORT_CMD_NEW); + err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info), + info->snd_portid, info->snd_seq, 0, + OVS_VPORT_CMD_NEW); BUG_ON(err < 0); ovs_unlock(); @@ -2128,8 +2144,9 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info) goto exit_unlock_free; } - err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, - info->snd_seq, 0, OVS_VPORT_CMD_DEL); + err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info), + info->snd_portid, info->snd_seq, 0, + OVS_VPORT_CMD_DEL); BUG_ON(err < 0); /* the vport deletion may trigger dp headroom update */ @@ -2169,8 +2186,9 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info) err = PTR_ERR(vport); if (IS_ERR(vport)) goto exit_unlock_free; - err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid, - info->snd_seq, 0, OVS_VPORT_CMD_NEW); + err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info), + info->snd_portid, info->snd_seq, 0, + OVS_VPORT_CMD_NEW); BUG_ON(err < 0); rcu_read_unlock(); @@ -2202,6 +2220,7 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) hlist_for_each_entry_rcu(vport, &dp->ports[i], dp_hash_node) { if (j >= skip && ovs_vport_cmd_fill_info(vport, skb, + sock_net(skb->sk), NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, NLM_F_MULTI, @@ -2228,6 +2247,8 @@ static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = { [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 }, [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 }, [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED }, + [OVS_VPORT_ATTR_IFINDEX] = { .type = NLA_U32 }, + [OVS_VPORT_ATTR_NETNSID] = { .type = NLA_S32 }, }; static const struct genl_ops dp_vport_genl_ops[] = { diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 480600649d0b..4a104ef9e12c 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -200,8 +200,8 @@ int ovs_dp_upcall(struct datapath *, struct sk_buff *, uint32_t cutlen); const char *ovs_dp_name(const struct datapath *dp); -struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, - u8 cmd); +struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net, + u32 portid, u32 seq, u8 cmd); int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_actions *, struct sw_flow_key *); diff --git a/net/openvswitch/dp_notify.c b/net/openvswitch/dp_notify.c index 653d073bae45..f3ee2f2825c0 100644 --- a/net/openvswitch/dp_notify.c +++ b/net/openvswitch/dp_notify.c @@ -30,8 +30,8 @@ static void dp_detach_port_notify(struct vport *vport) struct datapath *dp; dp = vport->dp; - notify = ovs_vport_cmd_build_info(vport, 0, 0, - OVS_VPORT_CMD_DEL); + notify = ovs_vport_cmd_build_info(vport, ovs_dp_get_net(dp), + 0, 0, OVS_VPORT_CMD_DEL); ovs_dp_detach_port(vport); if (IS_ERR(notify)) { genl_set_err(&dp_vport_genl_family, ovs_dp_get_net(dp), 0, -- cgit v1.2.3-59-g8ed1b From 79e1ad148c844f5c8b9d76b36b26e3886dca95ae Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Thu, 2 Nov 2017 17:04:38 -0200 Subject: rtnetlink: use netnsid to query interface Currently, when an application gets netnsid from the kernel (for example as the result of RTM_GETLINK call on one end of the veth pair), it's not much useful. There's no reliable way to get to the netns fd from the netnsid, nor does any kernel API accept netnsid. Extend the RTM_GETLINK call to also accept netnsid. It will operate on the netns with the given netnsid in such case. Of course, the calling process needs to have enough capabilities in the target name space; for now, require CAP_NET_ADMIN. This can be relaxed in the future. To signal to the calling process that the kernel understood the new IFLA_IF_NETNSID attribute in the query, it will include it in the response. This is needed to detect older kernels, as they will just ignore IFLA_IF_NETNSID and query in the current name space. This patch implemetns IFLA_IF_NETNSID only for get and dump. For set operations, this can be extended later. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- include/uapi/linux/if_link.h | 1 + net/core/rtnetlink.c | 103 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 86 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index b3cf5639ac8f..19fc02660e0c 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -160,6 +160,7 @@ enum { IFLA_XDP, IFLA_EVENT, IFLA_NEW_NETNSID, + IFLA_IF_NETNSID, __IFLA_MAX }; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index de24d394c69e..8a8c51937edf 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -921,7 +921,8 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev, + nla_total_size(4) /* IFLA_EVENT */ + nla_total_size(4) /* IFLA_NEW_NETNSID */ + nla_total_size(1); /* IFLA_PROTO_DOWN */ - + + nla_total_size(4) /* IFLA_IF_NETNSID */ + + 0; } static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev) @@ -1370,13 +1371,14 @@ static noinline_for_stack int nla_put_ifalias(struct sk_buff *skb, } static int rtnl_fill_link_netnsid(struct sk_buff *skb, - const struct net_device *dev) + const struct net_device *dev, + struct net *src_net) { if (dev->rtnl_link_ops && dev->rtnl_link_ops->get_link_net) { struct net *link_net = dev->rtnl_link_ops->get_link_net(dev); if (!net_eq(dev_net(dev), link_net)) { - int id = peernet2id_alloc(dev_net(dev), link_net); + int id = peernet2id_alloc(src_net, link_net); if (nla_put_s32(skb, IFLA_LINK_NETNSID, id)) return -EMSGSIZE; @@ -1427,10 +1429,11 @@ static int rtnl_fill_link_af(struct sk_buff *skb, return 0; } -static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, +static int rtnl_fill_ifinfo(struct sk_buff *skb, + struct net_device *dev, struct net *src_net, int type, u32 pid, u32 seq, u32 change, unsigned int flags, u32 ext_filter_mask, - u32 event, int *new_nsid) + u32 event, int *new_nsid, int tgt_netnsid) { struct ifinfomsg *ifm; struct nlmsghdr *nlh; @@ -1448,6 +1451,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, ifm->ifi_flags = dev_get_flags(dev); ifm->ifi_change = change; + if (tgt_netnsid >= 0 && nla_put_s32(skb, IFLA_IF_NETNSID, tgt_netnsid)) + goto nla_put_failure; + if (nla_put_string(skb, IFLA_IFNAME, dev->name) || nla_put_u32(skb, IFLA_TXQLEN, dev->tx_queue_len) || nla_put_u8(skb, IFLA_OPERSTATE, @@ -1513,7 +1519,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, goto nla_put_failure; } - if (rtnl_fill_link_netnsid(skb, dev)) + if (rtnl_fill_link_netnsid(skb, dev, src_net)) goto nla_put_failure; if (new_nsid && @@ -1571,6 +1577,7 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = { [IFLA_XDP] = { .type = NLA_NESTED }, [IFLA_EVENT] = { .type = NLA_U32 }, [IFLA_GROUP] = { .type = NLA_U32 }, + [IFLA_IF_NETNSID] = { .type = NLA_S32 }, }; static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = { @@ -1674,9 +1681,28 @@ static bool link_dump_filtered(struct net_device *dev, return false; } +static struct net *get_target_net(struct sk_buff *skb, int netnsid) +{ + struct net *net; + + net = get_net_ns_by_id(sock_net(skb->sk), netnsid); + if (!net) + return ERR_PTR(-EINVAL); + + /* For now, the caller is required to have CAP_NET_ADMIN in + * the user namespace owning the target net ns. + */ + if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) { + put_net(net); + return ERR_PTR(-EACCES); + } + return net; +} + static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); + struct net *tgt_net = net; int h, s_h; int idx = 0, s_idx; struct net_device *dev; @@ -1686,6 +1712,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) const struct rtnl_link_ops *kind_ops = NULL; unsigned int flags = NLM_F_MULTI; int master_idx = 0; + int netnsid = -1; int err; int hdrlen; @@ -1704,6 +1731,15 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) if (nlmsg_parse(cb->nlh, hdrlen, tb, IFLA_MAX, ifla_policy, NULL) >= 0) { + if (tb[IFLA_IF_NETNSID]) { + netnsid = nla_get_s32(tb[IFLA_IF_NETNSID]); + tgt_net = get_target_net(skb, netnsid); + if (IS_ERR(tgt_net)) { + tgt_net = net; + netnsid = -1; + } + } + if (tb[IFLA_EXT_MASK]) ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); @@ -1719,17 +1755,19 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { idx = 0; - head = &net->dev_index_head[h]; + head = &tgt_net->dev_index_head[h]; hlist_for_each_entry(dev, head, index_hlist) { if (link_dump_filtered(dev, master_idx, kind_ops)) goto cont; if (idx < s_idx) goto cont; - err = rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK, + err = rtnl_fill_ifinfo(skb, dev, net, + RTM_NEWLINK, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, 0, flags, - ext_filter_mask, 0, NULL); + ext_filter_mask, 0, NULL, + netnsid); if (err < 0) { if (likely(skb->len)) @@ -1748,6 +1786,8 @@ out_err: cb->args[0] = h; cb->seq = net->dev_base_seq; nl_dump_check_consistent(cb, nlmsg_hdr(skb)); + if (netnsid >= 0) + put_net(tgt_net); return err; } @@ -2360,6 +2400,9 @@ static int rtnl_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, if (err < 0) goto errout; + if (tb[IFLA_IF_NETNSID]) + return -EOPNOTSUPP; + if (tb[IFLA_IFNAME]) nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); else @@ -2454,6 +2497,9 @@ static int rtnl_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, if (err < 0) return err; + if (tb[IFLA_IF_NETNSID]) + return -EOPNOTSUPP; + if (tb[IFLA_IFNAME]) nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); @@ -2585,6 +2631,9 @@ replay: if (err < 0) return err; + if (tb[IFLA_IF_NETNSID]) + return -EOPNOTSUPP; + if (tb[IFLA_IFNAME]) nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); else @@ -2818,11 +2867,13 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); + struct net *tgt_net = net; struct ifinfomsg *ifm; char ifname[IFNAMSIZ]; struct nlattr *tb[IFLA_MAX+1]; struct net_device *dev = NULL; struct sk_buff *nskb; + int netnsid = -1; int err; u32 ext_filter_mask = 0; @@ -2830,35 +2881,50 @@ static int rtnl_getlink(struct sk_buff *skb, struct nlmsghdr *nlh, if (err < 0) return err; + if (tb[IFLA_IF_NETNSID]) { + netnsid = nla_get_s32(tb[IFLA_IF_NETNSID]); + tgt_net = get_target_net(skb, netnsid); + if (IS_ERR(tgt_net)) + return PTR_ERR(tgt_net); + } + if (tb[IFLA_IFNAME]) nla_strlcpy(ifname, tb[IFLA_IFNAME], IFNAMSIZ); if (tb[IFLA_EXT_MASK]) ext_filter_mask = nla_get_u32(tb[IFLA_EXT_MASK]); + err = -EINVAL; ifm = nlmsg_data(nlh); if (ifm->ifi_index > 0) - dev = __dev_get_by_index(net, ifm->ifi_index); + dev = __dev_get_by_index(tgt_net, ifm->ifi_index); else if (tb[IFLA_IFNAME]) - dev = __dev_get_by_name(net, ifname); + dev = __dev_get_by_name(tgt_net, ifname); else - return -EINVAL; + goto out; + err = -ENODEV; if (dev == NULL) - return -ENODEV; + goto out; + err = -ENOBUFS; nskb = nlmsg_new(if_nlmsg_size(dev, ext_filter_mask), GFP_KERNEL); if (nskb == NULL) - return -ENOBUFS; + goto out; - err = rtnl_fill_ifinfo(nskb, dev, RTM_NEWLINK, NETLINK_CB(skb).portid, - nlh->nlmsg_seq, 0, 0, ext_filter_mask, 0, NULL); + err = rtnl_fill_ifinfo(nskb, dev, net, + RTM_NEWLINK, NETLINK_CB(skb).portid, + nlh->nlmsg_seq, 0, 0, ext_filter_mask, + 0, NULL, netnsid); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size */ WARN_ON(err == -EMSGSIZE); kfree_skb(nskb); } else err = rtnl_unicast(nskb, net, NETLINK_CB(skb).portid); +out: + if (netnsid >= 0) + put_net(tgt_net); return err; } @@ -2948,8 +3014,9 @@ struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev, if (skb == NULL) goto errout; - err = rtnl_fill_ifinfo(skb, dev, type, 0, 0, change, 0, 0, event, - new_nsid); + err = rtnl_fill_ifinfo(skb, dev, dev_net(dev), + type, 0, 0, change, 0, 0, event, + new_nsid, -1); if (err < 0) { /* -EMSGSIZE implies BUG in if_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); -- cgit v1.2.3-59-g8ed1b From f4e63525ee35f9c02e9f51f90571718363e9a9a9 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 3 Nov 2017 13:56:16 -0700 Subject: net: bpf: rename ndo_xdp to ndo_bpf ndo_xdp is a control path callback for setting up XDP in the driver. We can reuse it for other forms of communication between the eBPF stack and the drivers. Rename the callback and associated structures and definitions. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Reviewed-by: Quentin Monnet Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h | 2 +- drivers/net/ethernet/cavium/thunder/nicvf_main.c | 4 +-- drivers/net/ethernet/intel/i40e/i40e_main.c | 6 ++-- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 4 +-- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 6 ++-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 +-- .../net/ethernet/netronome/nfp/nfp_net_common.c | 4 +-- drivers/net/ethernet/qlogic/qede/qede.h | 2 +- drivers/net/ethernet/qlogic/qede/qede_filter.c | 2 +- drivers/net/ethernet/qlogic/qede/qede_main.c | 4 +-- drivers/net/tun.c | 4 +-- drivers/net/virtio_net.c | 4 +-- include/linux/netdevice.h | 23 ++++++++------- net/core/dev.c | 34 +++++++++++----------- net/core/rtnetlink.c | 4 +-- 17 files changed, 56 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 4e3d569bf32e..96416f5d97f3 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7775,7 +7775,7 @@ static const struct net_device_ops bnxt_netdev_ops = { #endif .ndo_udp_tunnel_add = bnxt_udp_tunnel_add, .ndo_udp_tunnel_del = bnxt_udp_tunnel_del, - .ndo_xdp = bnxt_xdp, + .ndo_bpf = bnxt_xdp, .ndo_bridge_getlink = bnxt_bridge_getlink, .ndo_bridge_setlink = bnxt_bridge_setlink, .ndo_get_phys_port_name = bnxt_get_phys_port_name diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 06ce63c00821..261e5847557a 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -208,7 +208,7 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) return 0; } -int bnxt_xdp(struct net_device *dev, struct netdev_xdp *xdp) +int bnxt_xdp(struct net_device *dev, struct netdev_bpf *xdp) { struct bnxt *bp = netdev_priv(dev); int rc; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h index 12a5ad66b564..414b748038ca 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.h @@ -16,6 +16,6 @@ void bnxt_tx_int_xdp(struct bnxt *bp, struct bnxt_napi *bnapi, int nr_pkts); bool bnxt_rx_xdp(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, u16 cons, struct page *page, u8 **data_ptr, unsigned int *len, u8 *event); -int bnxt_xdp(struct net_device *dev, struct netdev_xdp *xdp); +int bnxt_xdp(struct net_device *dev, struct netdev_bpf *xdp); #endif diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_main.c b/drivers/net/ethernet/cavium/thunder/nicvf_main.c index 71989e180289..a063c36c4c58 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c @@ -1741,7 +1741,7 @@ static int nicvf_xdp_setup(struct nicvf *nic, struct bpf_prog *prog) return 0; } -static int nicvf_xdp(struct net_device *netdev, struct netdev_xdp *xdp) +static int nicvf_xdp(struct net_device *netdev, struct netdev_bpf *xdp) { struct nicvf *nic = netdev_priv(netdev); @@ -1774,7 +1774,7 @@ static const struct net_device_ops nicvf_netdev_ops = { .ndo_tx_timeout = nicvf_tx_timeout, .ndo_fix_features = nicvf_fix_features, .ndo_set_features = nicvf_set_features, - .ndo_xdp = nicvf_xdp, + .ndo_bpf = nicvf_xdp, }; static int nicvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index dfecaeda0654..05b94d87a6c3 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -11648,12 +11648,12 @@ static int i40e_xdp_setup(struct i40e_vsi *vsi, } /** - * i40e_xdp - implements ndo_xdp for i40e + * i40e_xdp - implements ndo_bpf for i40e * @dev: netdevice * @xdp: XDP command **/ static int i40e_xdp(struct net_device *dev, - struct netdev_xdp *xdp) + struct netdev_bpf *xdp) { struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; @@ -11705,7 +11705,7 @@ static const struct net_device_ops i40e_netdev_ops = { .ndo_features_check = i40e_features_check, .ndo_bridge_getlink = i40e_ndo_bridge_getlink, .ndo_bridge_setlink = i40e_ndo_bridge_setlink, - .ndo_xdp = i40e_xdp, + .ndo_bpf = i40e_xdp, }; /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 507977994a03..e5dcb25be398 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -10004,7 +10004,7 @@ static int ixgbe_xdp_setup(struct net_device *dev, struct bpf_prog *prog) return 0; } -static int ixgbe_xdp(struct net_device *dev, struct netdev_xdp *xdp) +static int ixgbe_xdp(struct net_device *dev, struct netdev_bpf *xdp) { struct ixgbe_adapter *adapter = netdev_priv(dev); @@ -10113,7 +10113,7 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_udp_tunnel_add = ixgbe_add_udp_tunnel_port, .ndo_udp_tunnel_del = ixgbe_del_udp_tunnel_port, .ndo_features_check = ixgbe_features_check, - .ndo_xdp = ixgbe_xdp, + .ndo_bpf = ixgbe_xdp, .ndo_xdp_xmit = ixgbe_xdp_xmit, .ndo_xdp_flush = ixgbe_xdp_flush, }; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index d611df2f274d..736a6ccaf05e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -2916,7 +2916,7 @@ static u32 mlx4_xdp_query(struct net_device *dev) return prog_id; } -static int mlx4_xdp(struct net_device *dev, struct netdev_xdp *xdp) +static int mlx4_xdp(struct net_device *dev, struct netdev_bpf *xdp) { switch (xdp->command) { case XDP_SETUP_PROG: @@ -2958,7 +2958,7 @@ static const struct net_device_ops mlx4_netdev_ops = { .ndo_udp_tunnel_del = mlx4_en_del_vxlan_port, .ndo_features_check = mlx4_en_features_check, .ndo_set_tx_maxrate = mlx4_en_set_tx_maxrate, - .ndo_xdp = mlx4_xdp, + .ndo_bpf = mlx4_xdp, }; static const struct net_device_ops mlx4_netdev_ops_master = { @@ -2995,7 +2995,7 @@ static const struct net_device_ops mlx4_netdev_ops_master = { .ndo_udp_tunnel_del = mlx4_en_del_vxlan_port, .ndo_features_check = mlx4_en_features_check, .ndo_set_tx_maxrate = mlx4_en_set_tx_maxrate, - .ndo_xdp = mlx4_xdp, + .ndo_bpf = mlx4_xdp, }; struct mlx4_en_bond { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 28ae00b3eb88..3b7b7bb84eb0 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3831,7 +3831,7 @@ static u32 mlx5e_xdp_query(struct net_device *dev) return prog_id; } -static int mlx5e_xdp(struct net_device *dev, struct netdev_xdp *xdp) +static int mlx5e_xdp(struct net_device *dev, struct netdev_bpf *xdp) { switch (xdp->command) { case XDP_SETUP_PROG: @@ -3883,7 +3883,7 @@ static const struct net_device_ops mlx5e_netdev_ops = { .ndo_rx_flow_steer = mlx5e_rx_flow_steer, #endif .ndo_tx_timeout = mlx5e_tx_timeout, - .ndo_xdp = mlx5e_xdp, + .ndo_bpf = mlx5e_xdp, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = mlx5e_netpoll, #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 185a3dd35a3f..f6c6ad4e8a59 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -3378,7 +3378,7 @@ nfp_net_xdp_setup(struct nfp_net *nn, struct bpf_prog *prog, u32 flags, return 0; } -static int nfp_net_xdp(struct net_device *netdev, struct netdev_xdp *xdp) +static int nfp_net_xdp(struct net_device *netdev, struct netdev_bpf *xdp) { struct nfp_net *nn = netdev_priv(netdev); @@ -3441,7 +3441,7 @@ const struct net_device_ops nfp_net_netdev_ops = { .ndo_get_phys_port_name = nfp_port_get_phys_port_name, .ndo_udp_tunnel_add = nfp_net_add_vxlan_port, .ndo_udp_tunnel_del = nfp_net_del_vxlan_port, - .ndo_xdp = nfp_net_xdp, + .ndo_bpf = nfp_net_xdp, }; /** diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index adb700512baa..a3a70ade411f 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -503,7 +503,7 @@ void qede_fill_rss_params(struct qede_dev *edev, void qede_udp_tunnel_add(struct net_device *dev, struct udp_tunnel_info *ti); void qede_udp_tunnel_del(struct net_device *dev, struct udp_tunnel_info *ti); -int qede_xdp(struct net_device *dev, struct netdev_xdp *xdp); +int qede_xdp(struct net_device *dev, struct netdev_bpf *xdp); #ifdef CONFIG_DCB void qede_set_dcbnl_ops(struct net_device *ndev); diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index f79e36e4060a..c1a0708a7d7c 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -1065,7 +1065,7 @@ static int qede_xdp_set(struct qede_dev *edev, struct bpf_prog *prog) return 0; } -int qede_xdp(struct net_device *dev, struct netdev_xdp *xdp) +int qede_xdp(struct net_device *dev, struct netdev_bpf *xdp) { struct qede_dev *edev = netdev_priv(dev); diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index e5ee9f274a71..8f9b3eb82137 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -556,7 +556,7 @@ static const struct net_device_ops qede_netdev_ops = { .ndo_udp_tunnel_add = qede_udp_tunnel_add, .ndo_udp_tunnel_del = qede_udp_tunnel_del, .ndo_features_check = qede_features_check, - .ndo_xdp = qede_xdp, + .ndo_bpf = qede_xdp, #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = qede_rx_flow_steer, #endif @@ -594,7 +594,7 @@ static const struct net_device_ops qede_netdev_vf_xdp_ops = { .ndo_udp_tunnel_add = qede_udp_tunnel_add, .ndo_udp_tunnel_del = qede_udp_tunnel_del, .ndo_features_check = qede_features_check, - .ndo_xdp = qede_xdp, + .ndo_bpf = qede_xdp, }; /* ------------------------------------------------------------------------- diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 8125956f62a1..1a326b697221 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1141,7 +1141,7 @@ static u32 tun_xdp_query(struct net_device *dev) return 0; } -static int tun_xdp(struct net_device *dev, struct netdev_xdp *xdp) +static int tun_xdp(struct net_device *dev, struct netdev_bpf *xdp) { switch (xdp->command) { case XDP_SETUP_PROG: @@ -1185,7 +1185,7 @@ static const struct net_device_ops tap_netdev_ops = { .ndo_features_check = passthru_features_check, .ndo_set_rx_headroom = tun_set_headroom, .ndo_get_stats64 = tun_net_get_stats64, - .ndo_xdp = tun_xdp, + .ndo_bpf = tun_xdp, }; static void tun_flow_init(struct tun_struct *tun) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index fc059f193e7d..edf984406ba0 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2088,7 +2088,7 @@ static u32 virtnet_xdp_query(struct net_device *dev) return 0; } -static int virtnet_xdp(struct net_device *dev, struct netdev_xdp *xdp) +static int virtnet_xdp(struct net_device *dev, struct netdev_bpf *xdp) { switch (xdp->command) { case XDP_SETUP_PROG: @@ -2115,7 +2115,7 @@ static const struct net_device_ops virtnet_netdev = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = virtnet_netpoll, #endif - .ndo_xdp = virtnet_xdp, + .ndo_bpf = virtnet_xdp, .ndo_xdp_xmit = virtnet_xdp_xmit, .ndo_xdp_flush = virtnet_xdp_flush, .ndo_features_check = passthru_features_check, diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 7de7656550c2..9af9feaaeb64 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -779,10 +779,10 @@ enum tc_setup_type { TC_SETUP_CBS, }; -/* These structures hold the attributes of xdp state that are being passed - * to the netdevice through the xdp op. +/* These structures hold the attributes of bpf state that are being passed + * to the netdevice through the bpf op. */ -enum xdp_netdev_command { +enum bpf_netdev_command { /* Set or clear a bpf program used in the earliest stages of packet * rx. The prog will have been loaded as BPF_PROG_TYPE_XDP. The callee * is responsible for calling bpf_prog_put on any old progs that are @@ -801,8 +801,8 @@ enum xdp_netdev_command { struct netlink_ext_ack; -struct netdev_xdp { - enum xdp_netdev_command command; +struct netdev_bpf { + enum bpf_netdev_command command; union { /* XDP_SETUP_PROG */ struct { @@ -1124,9 +1124,10 @@ struct dev_ifalias { * appropriate rx headroom value allows avoiding skb head copy on * forward. Setting a negative value resets the rx headroom to the * default value. - * int (*ndo_xdp)(struct net_device *dev, struct netdev_xdp *xdp); + * int (*ndo_bpf)(struct net_device *dev, struct netdev_bpf *bpf); * This function is used to set or query state related to XDP on the - * netdevice. See definition of enum xdp_netdev_command for details. + * netdevice and manage BPF offload. See definition of + * enum bpf_netdev_command for details. * int (*ndo_xdp_xmit)(struct net_device *dev, struct xdp_buff *xdp); * This function is used to submit a XDP packet for transmit on a * netdevice. @@ -1315,8 +1316,8 @@ struct net_device_ops { struct sk_buff *skb); void (*ndo_set_rx_headroom)(struct net_device *dev, int needed_headroom); - int (*ndo_xdp)(struct net_device *dev, - struct netdev_xdp *xdp); + int (*ndo_bpf)(struct net_device *dev, + struct netdev_bpf *bpf); int (*ndo_xdp_xmit)(struct net_device *dev, struct xdp_buff *xdp); void (*ndo_xdp_flush)(struct net_device *dev); @@ -3311,10 +3312,10 @@ struct sk_buff *validate_xmit_skb_list(struct sk_buff *skb, struct net_device *d struct sk_buff *dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, struct netdev_queue *txq, int *ret); -typedef int (*xdp_op_t)(struct net_device *dev, struct netdev_xdp *xdp); +typedef int (*bpf_op_t)(struct net_device *dev, struct netdev_bpf *bpf); int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, int fd, u32 flags); -u8 __dev_xdp_attached(struct net_device *dev, xdp_op_t xdp_op, u32 *prog_id); +u8 __dev_xdp_attached(struct net_device *dev, bpf_op_t xdp_op, u32 *prog_id); int __dev_forward_skb(struct net_device *dev, struct sk_buff *skb); int dev_forward_skb(struct net_device *dev, struct sk_buff *skb); diff --git a/net/core/dev.c b/net/core/dev.c index 1423cf4d695c..10cde58d3275 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4545,7 +4545,7 @@ static int __netif_receive_skb(struct sk_buff *skb) return ret; } -static int generic_xdp_install(struct net_device *dev, struct netdev_xdp *xdp) +static int generic_xdp_install(struct net_device *dev, struct netdev_bpf *xdp) { struct bpf_prog *old = rtnl_dereference(dev->xdp_prog); struct bpf_prog *new = xdp->prog; @@ -7090,26 +7090,26 @@ int dev_change_proto_down(struct net_device *dev, bool proto_down) } EXPORT_SYMBOL(dev_change_proto_down); -u8 __dev_xdp_attached(struct net_device *dev, xdp_op_t xdp_op, u32 *prog_id) +u8 __dev_xdp_attached(struct net_device *dev, bpf_op_t bpf_op, u32 *prog_id) { - struct netdev_xdp xdp; + struct netdev_bpf xdp; memset(&xdp, 0, sizeof(xdp)); xdp.command = XDP_QUERY_PROG; /* Query must always succeed. */ - WARN_ON(xdp_op(dev, &xdp) < 0); + WARN_ON(bpf_op(dev, &xdp) < 0); if (prog_id) *prog_id = xdp.prog_id; return xdp.prog_attached; } -static int dev_xdp_install(struct net_device *dev, xdp_op_t xdp_op, +static int dev_xdp_install(struct net_device *dev, bpf_op_t bpf_op, struct netlink_ext_ack *extack, u32 flags, struct bpf_prog *prog) { - struct netdev_xdp xdp; + struct netdev_bpf xdp; memset(&xdp, 0, sizeof(xdp)); if (flags & XDP_FLAGS_HW_MODE) @@ -7120,7 +7120,7 @@ static int dev_xdp_install(struct net_device *dev, xdp_op_t xdp_op, xdp.flags = flags; xdp.prog = prog; - return xdp_op(dev, &xdp); + return bpf_op(dev, &xdp); } /** @@ -7137,24 +7137,24 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, { const struct net_device_ops *ops = dev->netdev_ops; struct bpf_prog *prog = NULL; - xdp_op_t xdp_op, xdp_chk; + bpf_op_t bpf_op, bpf_chk; int err; ASSERT_RTNL(); - xdp_op = xdp_chk = ops->ndo_xdp; - if (!xdp_op && (flags & (XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE))) + bpf_op = bpf_chk = ops->ndo_bpf; + if (!bpf_op && (flags & (XDP_FLAGS_DRV_MODE | XDP_FLAGS_HW_MODE))) return -EOPNOTSUPP; - if (!xdp_op || (flags & XDP_FLAGS_SKB_MODE)) - xdp_op = generic_xdp_install; - if (xdp_op == xdp_chk) - xdp_chk = generic_xdp_install; + if (!bpf_op || (flags & XDP_FLAGS_SKB_MODE)) + bpf_op = generic_xdp_install; + if (bpf_op == bpf_chk) + bpf_chk = generic_xdp_install; if (fd >= 0) { - if (xdp_chk && __dev_xdp_attached(dev, xdp_chk, NULL)) + if (bpf_chk && __dev_xdp_attached(dev, bpf_chk, NULL)) return -EEXIST; if ((flags & XDP_FLAGS_UPDATE_IF_NOEXIST) && - __dev_xdp_attached(dev, xdp_op, NULL)) + __dev_xdp_attached(dev, bpf_op, NULL)) return -EBUSY; prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP); @@ -7162,7 +7162,7 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, return PTR_ERR(prog); } - err = dev_xdp_install(dev, xdp_op, extack, flags, prog); + err = dev_xdp_install(dev, bpf_op, extack, flags, prog); if (err < 0 && prog) bpf_prog_put(prog); diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 8a8c51937edf..dc5ad84ac096 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1270,10 +1270,10 @@ static u8 rtnl_xdp_attached_mode(struct net_device *dev, u32 *prog_id) *prog_id = generic_xdp_prog->aux->id; return XDP_ATTACHED_SKB; } - if (!ops->ndo_xdp) + if (!ops->ndo_bpf) return XDP_ATTACHED_NONE; - return __dev_xdp_attached(dev, ops->ndo_xdp, prog_id); + return __dev_xdp_attached(dev, ops->ndo_bpf, prog_id); } static int rtnl_xdp_fill(struct sk_buff *skb, struct net_device *dev) -- cgit v1.2.3-59-g8ed1b From ab3f0063c48c26c927851b6767824e35a716d878 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 3 Nov 2017 13:56:17 -0700 Subject: bpf: offload: add infrastructure for loading programs for a specific netdev The fact that we don't know which device the program is going to be used on is quite limiting in current eBPF infrastructure. We have to reverse or limit the changes which kernel makes to the loaded bytecode if we want it to be offloaded to a networking device. We also have to invent new APIs for debugging and troubleshooting support. Make it possible to load programs for a specific netdev. This helps us to bring the debug information closer to the core eBPF infrastructure (e.g. we will be able to reuse the verifer log in device JIT). It allows device JITs to perform translation on the original bytecode. __bpf_prog_get() when called to get a reference for an attachment point will now refuse to give it if program has a device assigned. Following patches will add a version of that function which passes the expected netdev in. @type argument in __bpf_prog_get() is renamed to attach_type to make it clearer that it's only set on attachment. All calls to ndo_bpf are protected by rtnl, only verifier callbacks are not. We need a wait queue to make sure netdev doesn't get destroyed while verifier is still running and calling its driver. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Reviewed-by: Quentin Monnet Signed-off-by: David S. Miller --- include/linux/bpf.h | 36 +++++++++ include/linux/bpf_verifier.h | 10 +++ include/linux/netdevice.h | 14 ++++ include/uapi/linux/bpf.h | 1 + kernel/bpf/Makefile | 1 + kernel/bpf/core.c | 10 ++- kernel/bpf/offload.c | 182 +++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 17 +++- kernel/bpf/verifier.c | 15 +++- 9 files changed, 278 insertions(+), 8 deletions(-) create mode 100644 kernel/bpf/offload.c (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 520aeebe0d93..e45d43f9ec92 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -15,6 +15,7 @@ #include #include #include +#include struct perf_event; struct bpf_prog; @@ -182,6 +183,16 @@ struct bpf_verifier_ops { struct bpf_prog *prog, u32 *target_size); }; +struct bpf_dev_offload { + struct bpf_prog *prog; + struct net_device *netdev; + void *dev_priv; + struct list_head offloads; + bool dev_state; + bool verifier_running; + wait_queue_head_t verifier_done; +}; + struct bpf_prog_aux { atomic_t refcnt; u32 used_map_cnt; @@ -199,6 +210,7 @@ struct bpf_prog_aux { #ifdef CONFIG_SECURITY void *security; #endif + struct bpf_dev_offload *offload; union { struct work_struct work; struct rcu_head rcu; @@ -317,6 +329,7 @@ extern const struct file_operations bpf_prog_fops; #undef BPF_PROG_TYPE #undef BPF_MAP_TYPE +extern const struct bpf_prog_ops bpf_offload_prog_ops; extern const struct bpf_verifier_ops tc_cls_act_analyzer_ops; extern const struct bpf_verifier_ops xdp_analyzer_ops; @@ -491,6 +504,29 @@ static inline int cpu_map_enqueue(struct bpf_cpu_map_entry *rcpu, } #endif /* CONFIG_BPF_SYSCALL */ +int bpf_prog_offload_compile(struct bpf_prog *prog); +void bpf_prog_offload_destroy(struct bpf_prog *prog); + +#if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL) +int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr); + +static inline bool bpf_prog_is_dev_bound(struct bpf_prog_aux *aux) +{ + return aux->offload; +} +#else +static inline int bpf_prog_offload_init(struct bpf_prog *prog, + union bpf_attr *attr) +{ + return -EOPNOTSUPP; +} + +static inline bool bpf_prog_is_dev_bound(struct bpf_prog_aux *aux) +{ + return false; +} +#endif /* CONFIG_NET && CONFIG_BPF_SYSCALL */ + #if defined(CONFIG_STREAM_PARSER) && defined(CONFIG_BPF_SYSCALL) struct sock *__sock_map_lookup_elem(struct bpf_map *map, u32 key); int sock_map_prog(struct bpf_map *map, struct bpf_prog *prog, u32 type); diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index 3b0976aaac75..e45011dbc02d 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -153,6 +153,7 @@ struct bpf_verifier_env { struct bpf_verifier_state *cur_state; /* current verifier state */ struct bpf_verifier_state_list **explored_states; /* search pruning optimization */ const struct bpf_ext_analyzer_ops *analyzer_ops; /* external analyzer ops */ + const struct bpf_ext_analyzer_ops *dev_ops; /* device analyzer ops */ void *analyzer_priv; /* pointer to external analyzer's private data */ struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */ u32 used_map_cnt; /* number of used maps */ @@ -169,6 +170,15 @@ static inline struct bpf_reg_state *cur_regs(struct bpf_verifier_env *env) return env->cur_state->regs; } +#if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL) +int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env); +#else +int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env) +{ + return -EOPNOTSUPP; +} +#endif + int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, void *priv); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9af9feaaeb64..fda527ccb263 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -797,8 +797,13 @@ enum bpf_netdev_command { * is equivalent to XDP_ATTACHED_DRV. */ XDP_QUERY_PROG, + /* BPF program for offload callbacks, invoked at program load time. */ + BPF_OFFLOAD_VERIFIER_PREP, + BPF_OFFLOAD_TRANSLATE, + BPF_OFFLOAD_DESTROY, }; +struct bpf_ext_analyzer_ops; struct netlink_ext_ack; struct netdev_bpf { @@ -815,6 +820,15 @@ struct netdev_bpf { u8 prog_attached; u32 prog_id; }; + /* BPF_OFFLOAD_VERIFIER_PREP */ + struct { + struct bpf_prog *prog; + const struct bpf_ext_analyzer_ops *ops; /* callee set */ + } verifier; + /* BPF_OFFLOAD_TRANSLATE, BPF_OFFLOAD_DESTROY */ + struct { + struct bpf_prog *prog; + } offload; }; }; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index a9820677c2ff..80d191a93fb0 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -260,6 +260,7 @@ union bpf_attr { __u32 kern_version; /* checked when prog_type=kprobe */ __u32 prog_flags; char prog_name[BPF_OBJ_NAME_LEN]; + __u32 prog_target_ifindex; /* ifindex of netdev to prep for */ }; struct { /* anonymous struct used by BPF_OBJ_* commands */ diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 16e95c8e749e..e691da0b3bab 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_BPF_SYSCALL) += disasm.o ifeq ($(CONFIG_NET),y) obj-$(CONFIG_BPF_SYSCALL) += devmap.o obj-$(CONFIG_BPF_SYSCALL) += cpumap.o +obj-$(CONFIG_BPF_SYSCALL) += offload.o ifeq ($(CONFIG_STREAM_PARSER),y) obj-$(CONFIG_BPF_SYSCALL) += sockmap.o endif diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index 7fe448799d76..8a6c37762330 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -1380,7 +1380,13 @@ struct bpf_prog *bpf_prog_select_runtime(struct bpf_prog *fp, int *err) * valid program, which in this case would simply not * be JITed, but falls back to the interpreter. */ - fp = bpf_int_jit_compile(fp); + if (!bpf_prog_is_dev_bound(fp->aux)) { + fp = bpf_int_jit_compile(fp); + } else { + *err = bpf_prog_offload_compile(fp); + if (*err) + return fp; + } bpf_prog_lock_ro(fp); /* The tail call compatibility check can only be done at @@ -1549,6 +1555,8 @@ static void bpf_prog_free_deferred(struct work_struct *work) struct bpf_prog_aux *aux; aux = container_of(work, struct bpf_prog_aux, work); + if (bpf_prog_is_dev_bound(aux)) + bpf_prog_offload_destroy(aux->prog); bpf_jit_free(aux->prog); } diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c new file mode 100644 index 000000000000..5553e0e2f8b1 --- /dev/null +++ b/kernel/bpf/offload.c @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include +#include +#include + +/* protected by RTNL */ +static LIST_HEAD(bpf_prog_offload_devs); + +int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr) +{ + struct net *net = current->nsproxy->net_ns; + struct bpf_dev_offload *offload; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (attr->prog_flags) + return -EINVAL; + + offload = kzalloc(sizeof(*offload), GFP_USER); + if (!offload) + return -ENOMEM; + + offload->prog = prog; + init_waitqueue_head(&offload->verifier_done); + + rtnl_lock(); + offload->netdev = __dev_get_by_index(net, attr->prog_target_ifindex); + if (!offload->netdev) { + rtnl_unlock(); + kfree(offload); + return -EINVAL; + } + + prog->aux->offload = offload; + list_add_tail(&offload->offloads, &bpf_prog_offload_devs); + rtnl_unlock(); + + return 0; +} + +static int __bpf_offload_ndo(struct bpf_prog *prog, enum bpf_netdev_command cmd, + struct netdev_bpf *data) +{ + struct net_device *netdev = prog->aux->offload->netdev; + + ASSERT_RTNL(); + + if (!netdev) + return -ENODEV; + if (!netdev->netdev_ops->ndo_bpf) + return -EOPNOTSUPP; + + data->command = cmd; + + return netdev->netdev_ops->ndo_bpf(netdev, data); +} + +int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env) +{ + struct netdev_bpf data = {}; + int err; + + data.verifier.prog = env->prog; + + rtnl_lock(); + err = __bpf_offload_ndo(env->prog, BPF_OFFLOAD_VERIFIER_PREP, &data); + if (err) + goto exit_unlock; + + env->dev_ops = data.verifier.ops; + + env->prog->aux->offload->dev_state = true; + env->prog->aux->offload->verifier_running = true; +exit_unlock: + rtnl_unlock(); + return err; +} + +static void __bpf_prog_offload_destroy(struct bpf_prog *prog) +{ + struct bpf_dev_offload *offload = prog->aux->offload; + struct netdev_bpf data = {}; + + data.offload.prog = prog; + + if (offload->verifier_running) + wait_event(offload->verifier_done, !offload->verifier_running); + + if (offload->dev_state) + WARN_ON(__bpf_offload_ndo(prog, BPF_OFFLOAD_DESTROY, &data)); + + offload->dev_state = false; + list_del_init(&offload->offloads); + offload->netdev = NULL; +} + +void bpf_prog_offload_destroy(struct bpf_prog *prog) +{ + struct bpf_dev_offload *offload = prog->aux->offload; + + offload->verifier_running = false; + wake_up(&offload->verifier_done); + + rtnl_lock(); + __bpf_prog_offload_destroy(prog); + rtnl_unlock(); + + kfree(offload); +} + +static int bpf_prog_offload_translate(struct bpf_prog *prog) +{ + struct bpf_dev_offload *offload = prog->aux->offload; + struct netdev_bpf data = {}; + int ret; + + data.offload.prog = prog; + + offload->verifier_running = false; + wake_up(&offload->verifier_done); + + rtnl_lock(); + ret = __bpf_offload_ndo(prog, BPF_OFFLOAD_TRANSLATE, &data); + rtnl_unlock(); + + return ret; +} + +static unsigned int bpf_prog_warn_on_exec(const void *ctx, + const struct bpf_insn *insn) +{ + WARN(1, "attempt to execute device eBPF program on the host!"); + return 0; +} + +int bpf_prog_offload_compile(struct bpf_prog *prog) +{ + prog->bpf_func = bpf_prog_warn_on_exec; + + return bpf_prog_offload_translate(prog); +} + +const struct bpf_prog_ops bpf_offload_prog_ops = { +}; + +static int bpf_offload_notification(struct notifier_block *notifier, + ulong event, void *ptr) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(ptr); + struct bpf_dev_offload *offload, *tmp; + + ASSERT_RTNL(); + + switch (event) { + case NETDEV_UNREGISTER: + list_for_each_entry_safe(offload, tmp, &bpf_prog_offload_devs, + offloads) { + if (offload->netdev == netdev) + __bpf_prog_offload_destroy(offload->prog); + } + break; + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block bpf_offload_notifier = { + .notifier_call = bpf_offload_notification, +}; + +static int __init bpf_offload_init(void) +{ + register_netdevice_notifier(&bpf_offload_notifier); + return 0; +} + +subsys_initcall(bpf_offload_init); diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 323be2473c4b..1574b9f0f24e 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -824,7 +824,10 @@ static int find_prog_type(enum bpf_prog_type type, struct bpf_prog *prog) if (type >= ARRAY_SIZE(bpf_prog_types) || !bpf_prog_types[type]) return -EINVAL; - prog->aux->ops = bpf_prog_types[type]; + if (!bpf_prog_is_dev_bound(prog->aux)) + prog->aux->ops = bpf_prog_types[type]; + else + prog->aux->ops = &bpf_offload_prog_ops; prog->type = type; return 0; } @@ -1054,7 +1057,7 @@ struct bpf_prog *bpf_prog_inc_not_zero(struct bpf_prog *prog) } EXPORT_SYMBOL_GPL(bpf_prog_inc_not_zero); -static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *type) +static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *attach_type) { struct fd f = fdget(ufd); struct bpf_prog *prog; @@ -1062,7 +1065,7 @@ static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *type) prog = ____bpf_prog_get(f); if (IS_ERR(prog)) return prog; - if (type && prog->type != *type) { + if (attach_type && (prog->type != *attach_type || prog->aux->offload)) { prog = ERR_PTR(-EINVAL); goto out; } @@ -1089,7 +1092,7 @@ struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type) EXPORT_SYMBOL_GPL(bpf_prog_get_type); /* last field in 'union bpf_attr' used by this command */ -#define BPF_PROG_LOAD_LAST_FIELD prog_name +#define BPF_PROG_LOAD_LAST_FIELD prog_target_ifindex static int bpf_prog_load(union bpf_attr *attr) { @@ -1152,6 +1155,12 @@ static int bpf_prog_load(union bpf_attr *attr) atomic_set(&prog->aux->refcnt, 1); prog->gpl_compatible = is_gpl ? 1 : 0; + if (attr->prog_target_ifindex) { + err = bpf_prog_offload_init(prog, attr); + if (err) + goto free_prog; + } + /* find program type: socket_filter vs tracing_filter */ err = find_prog_type(type, prog); if (err < 0) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 04357ad5a812..51aabb32ad67 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3736,10 +3736,13 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) static int ext_analyzer_insn_hook(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx) { - if (!env->analyzer_ops || !env->analyzer_ops->insn_hook) - return 0; + if (env->analyzer_ops && env->analyzer_ops->insn_hook) + return env->analyzer_ops->insn_hook(env, insn_idx, + prev_insn_idx); + if (env->dev_ops && env->dev_ops->insn_hook) + return env->dev_ops->insn_hook(env, insn_idx, prev_insn_idx); - return env->analyzer_ops->insn_hook(env, insn_idx, prev_insn_idx); + return 0; } static int do_check(struct bpf_verifier_env *env) @@ -4516,6 +4519,12 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr) if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) env->strict_alignment = true; + if (env->prog->aux->offload) { + ret = bpf_prog_offload_verifier_prep(env); + if (ret) + goto err_unlock; + } + ret = replace_map_fd_with_map_ptr(env); if (ret < 0) goto skip_full_check; -- cgit v1.2.3-59-g8ed1b From bd601b6ada11fdfb9e277f24ad2eb54bc599156b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 3 Nov 2017 13:56:18 -0700 Subject: bpf: report offload info to user space Extend struct bpf_prog_info to contain information about program being bound to a device. Since the netdev may get destroyed while program still exists we need a flag to indicate the program is loaded for a device, even if the device is gone. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Reviewed-by: Quentin Monnet Signed-off-by: David S. Miller --- include/linux/bpf.h | 1 + include/uapi/linux/bpf.h | 6 ++++++ kernel/bpf/offload.c | 12 ++++++++++++ kernel/bpf/syscall.c | 5 +++++ 4 files changed, 24 insertions(+) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index e45d43f9ec92..98bacd0fa5cc 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -506,6 +506,7 @@ static inline int cpu_map_enqueue(struct bpf_cpu_map_entry *rcpu, int bpf_prog_offload_compile(struct bpf_prog *prog); void bpf_prog_offload_destroy(struct bpf_prog *prog); +u32 bpf_prog_offload_ifindex(struct bpf_prog *prog); #if defined(CONFIG_NET) && defined(CONFIG_BPF_SYSCALL) int bpf_prog_offload_init(struct bpf_prog *prog, union bpf_attr *attr); diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 80d191a93fb0..4455dd195201 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -895,6 +895,10 @@ enum sk_action { #define BPF_TAG_SIZE 8 +enum bpf_prog_status { + BPF_PROG_STATUS_DEV_BOUND = (1 << 0), +}; + struct bpf_prog_info { __u32 type; __u32 id; @@ -908,6 +912,8 @@ struct bpf_prog_info { __u32 nr_map_ids; __aligned_u64 map_ids; char name[BPF_OBJ_NAME_LEN]; + __u32 ifindex; + __u32 status; } __attribute__((aligned(8))); struct bpf_map_info { diff --git a/kernel/bpf/offload.c b/kernel/bpf/offload.c index 5553e0e2f8b1..2816feb38be1 100644 --- a/kernel/bpf/offload.c +++ b/kernel/bpf/offload.c @@ -144,6 +144,18 @@ int bpf_prog_offload_compile(struct bpf_prog *prog) return bpf_prog_offload_translate(prog); } +u32 bpf_prog_offload_ifindex(struct bpf_prog *prog) +{ + struct bpf_dev_offload *offload = prog->aux->offload; + u32 ifindex; + + rtnl_lock(); + ifindex = offload->netdev ? offload->netdev->ifindex : 0; + rtnl_unlock(); + + return ifindex; +} + const struct bpf_prog_ops bpf_offload_prog_ops = { }; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 1574b9f0f24e..3217c20ea91b 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1592,6 +1592,11 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog, return -EFAULT; } + if (bpf_prog_is_dev_bound(prog->aux)) { + info.status |= BPF_PROG_STATUS_DEV_BOUND; + info.ifindex = bpf_prog_offload_ifindex(prog); + } + done: if (copy_to_user(uinfo, &info, info_len) || put_user(info_len, &uattr->info.info_len)) -- cgit v1.2.3-59-g8ed1b From 248f346ffe9508dee0039db4ac839cb31ba3bdec Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 3 Nov 2017 13:56:20 -0700 Subject: xdp: allow attaching programs loaded for specific device Pass the netdev pointer to bpf_prog_get_type(). This way BPF code can decide whether the device matches what the code was loaded/translated for. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Reviewed-by: Quentin Monnet Signed-off-by: David S. Miller --- include/linux/bpf.h | 10 ++++++++++ kernel/bpf/syscall.c | 33 +++++++++++++++++++++++++++++---- net/core/dev.c | 6 +++++- 3 files changed, 44 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index 98bacd0fa5cc..c397934f91dd 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -335,6 +335,8 @@ extern const struct bpf_verifier_ops xdp_analyzer_ops; struct bpf_prog *bpf_prog_get(u32 ufd); struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type); +struct bpf_prog *bpf_prog_get_type_dev(u32 ufd, enum bpf_prog_type type, + struct net_device *netdev); struct bpf_prog * __must_check bpf_prog_add(struct bpf_prog *prog, int i); void bpf_prog_sub(struct bpf_prog *prog, int i); struct bpf_prog * __must_check bpf_prog_inc(struct bpf_prog *prog); @@ -428,6 +430,14 @@ static inline struct bpf_prog *bpf_prog_get_type(u32 ufd, { return ERR_PTR(-EOPNOTSUPP); } + +static inline struct bpf_prog *bpf_prog_get_type_dev(u32 ufd, + enum bpf_prog_type type, + struct net_device *netdev) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static inline struct bpf_prog * __must_check bpf_prog_add(struct bpf_prog *prog, int i) { diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 3217c20ea91b..68f9123acd39 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1057,7 +1057,22 @@ struct bpf_prog *bpf_prog_inc_not_zero(struct bpf_prog *prog) } EXPORT_SYMBOL_GPL(bpf_prog_inc_not_zero); -static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *attach_type) +static bool bpf_prog_can_attach(struct bpf_prog *prog, + enum bpf_prog_type *attach_type, + struct net_device *netdev) +{ + struct bpf_dev_offload *offload = prog->aux->offload; + + if (prog->type != *attach_type) + return false; + if (offload && offload->netdev != netdev) + return false; + + return true; +} + +static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *attach_type, + struct net_device *netdev) { struct fd f = fdget(ufd); struct bpf_prog *prog; @@ -1065,7 +1080,7 @@ static struct bpf_prog *__bpf_prog_get(u32 ufd, enum bpf_prog_type *attach_type) prog = ____bpf_prog_get(f); if (IS_ERR(prog)) return prog; - if (attach_type && (prog->type != *attach_type || prog->aux->offload)) { + if (attach_type && !bpf_prog_can_attach(prog, attach_type, netdev)) { prog = ERR_PTR(-EINVAL); goto out; } @@ -1078,12 +1093,12 @@ out: struct bpf_prog *bpf_prog_get(u32 ufd) { - return __bpf_prog_get(ufd, NULL); + return __bpf_prog_get(ufd, NULL, NULL); } struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type) { - struct bpf_prog *prog = __bpf_prog_get(ufd, &type); + struct bpf_prog *prog = __bpf_prog_get(ufd, &type, NULL); if (!IS_ERR(prog)) trace_bpf_prog_get_type(prog); @@ -1091,6 +1106,16 @@ struct bpf_prog *bpf_prog_get_type(u32 ufd, enum bpf_prog_type type) } EXPORT_SYMBOL_GPL(bpf_prog_get_type); +struct bpf_prog *bpf_prog_get_type_dev(u32 ufd, enum bpf_prog_type type, + struct net_device *netdev) +{ + struct bpf_prog *prog = __bpf_prog_get(ufd, &type, netdev); + + if (!IS_ERR(prog)) + trace_bpf_prog_get_type(prog); + return prog; +} + /* last field in 'union bpf_attr' used by this command */ #define BPF_PROG_LOAD_LAST_FIELD prog_target_ifindex diff --git a/net/core/dev.c b/net/core/dev.c index 10cde58d3275..30b5fe32c525 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -7157,7 +7157,11 @@ int dev_change_xdp_fd(struct net_device *dev, struct netlink_ext_ack *extack, __dev_xdp_attached(dev, bpf_op, NULL)) return -EBUSY; - prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP); + if (bpf_op == ops->ndo_bpf) + prog = bpf_prog_get_type_dev(fd, BPF_PROG_TYPE_XDP, + dev); + else + prog = bpf_prog_get_type(fd, BPF_PROG_TYPE_XDP); if (IS_ERR(prog)) return PTR_ERR(prog); } -- cgit v1.2.3-59-g8ed1b From b37a530613104aa3f592376c67a462823298759c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Fri, 3 Nov 2017 13:56:30 -0700 Subject: bpf: remove old offload/analyzer Thanks to the ability to load a program for a specific device, running verifier twice is no longer needed. Signed-off-by: Jakub Kicinski Reviewed-by: Quentin Monnet Signed-off-by: David S. Miller --- include/linux/bpf_verifier.h | 5 --- kernel/bpf/verifier.c | 75 -------------------------------------------- net/core/filter.c | 42 ------------------------- 3 files changed, 122 deletions(-) (limited to 'include') diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index e45011dbc02d..07b96aaca256 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -152,9 +152,7 @@ struct bpf_verifier_env { bool strict_alignment; /* perform strict pointer alignment checks */ struct bpf_verifier_state *cur_state; /* current verifier state */ struct bpf_verifier_state_list **explored_states; /* search pruning optimization */ - const struct bpf_ext_analyzer_ops *analyzer_ops; /* external analyzer ops */ const struct bpf_ext_analyzer_ops *dev_ops; /* device analyzer ops */ - void *analyzer_priv; /* pointer to external analyzer's private data */ struct bpf_map *used_maps[MAX_USED_MAPS]; /* array of map's used by eBPF program */ u32 used_map_cnt; /* number of used maps */ u32 id_gen; /* used to generate unique reg IDs */ @@ -179,7 +177,4 @@ int bpf_prog_offload_verifier_prep(struct bpf_verifier_env *env) } #endif -int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, - void *priv); - #endif /* _LINUX_BPF_VERIFIER_H */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 51aabb32ad67..add845fe788a 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -949,9 +949,6 @@ static int check_ctx_access(struct bpf_verifier_env *env, int insn_idx, int off, */ *reg_type = info.reg_type; - if (env->analyzer_ops) - return 0; - env->insn_aux_data[insn_idx].ctx_field_size = info.ctx_field_size; /* remember the offset of last byte accessed in ctx */ if (env->prog->aux->max_ctx_offset < off + size) @@ -3736,9 +3733,6 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) static int ext_analyzer_insn_hook(struct bpf_verifier_env *env, int insn_idx, int prev_insn_idx) { - if (env->analyzer_ops && env->analyzer_ops->insn_hook) - return env->analyzer_ops->insn_hook(env, insn_idx, - prev_insn_idx); if (env->dev_ops && env->dev_ops->insn_hook) return env->dev_ops->insn_hook(env, insn_idx, prev_insn_idx); @@ -4601,72 +4595,3 @@ err_free_env: kfree(env); return ret; } - -static const struct bpf_verifier_ops * const bpf_analyzer_ops[] = { -#ifdef CONFIG_NET - [BPF_PROG_TYPE_XDP] = &xdp_analyzer_ops, - [BPF_PROG_TYPE_SCHED_CLS] = &tc_cls_act_analyzer_ops, -#endif -}; - -int bpf_analyzer(struct bpf_prog *prog, const struct bpf_ext_analyzer_ops *ops, - void *priv) -{ - struct bpf_verifier_env *env; - int ret; - - if (prog->type >= ARRAY_SIZE(bpf_analyzer_ops) || - !bpf_analyzer_ops[prog->type]) - return -EOPNOTSUPP; - - env = kzalloc(sizeof(struct bpf_verifier_env), GFP_KERNEL); - if (!env) - return -ENOMEM; - - env->insn_aux_data = vzalloc(sizeof(struct bpf_insn_aux_data) * - prog->len); - ret = -ENOMEM; - if (!env->insn_aux_data) - goto err_free_env; - env->prog = prog; - env->ops = bpf_analyzer_ops[env->prog->type]; - env->analyzer_ops = ops; - env->analyzer_priv = priv; - - /* grab the mutex to protect few globals used by verifier */ - mutex_lock(&bpf_verifier_lock); - - env->strict_alignment = false; - if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)) - env->strict_alignment = true; - - env->explored_states = kcalloc(env->prog->len, - sizeof(struct bpf_verifier_state_list *), - GFP_KERNEL); - ret = -ENOMEM; - if (!env->explored_states) - goto skip_full_check; - - ret = check_cfg(env); - if (ret < 0) - goto skip_full_check; - - env->allow_ptr_leaks = capable(CAP_SYS_ADMIN); - - ret = do_check(env); - if (env->cur_state) { - free_verifier_state(env->cur_state, true); - env->cur_state = NULL; - } - -skip_full_check: - while (!pop_stack(env, NULL, NULL)); - free_states(env); - - mutex_unlock(&bpf_verifier_lock); - vfree(env->insn_aux_data); -err_free_env: - kfree(env); - return ret; -} -EXPORT_SYMBOL_GPL(bpf_analyzer); diff --git a/net/core/filter.c b/net/core/filter.c index a0112168d6f9..1afa17935954 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3777,25 +3777,6 @@ static bool tc_cls_act_is_valid_access(int off, int size, return bpf_skb_is_valid_access(off, size, type, info); } -static bool -tc_cls_act_is_valid_access_analyzer(int off, int size, - enum bpf_access_type type, - struct bpf_insn_access_aux *info) -{ - switch (off) { - case offsetof(struct sk_buff, len): - return true; - case offsetof(struct sk_buff, data): - info->reg_type = PTR_TO_PACKET; - return true; - case offsetof(struct sk_buff, cb) + - offsetof(struct bpf_skb_data_end, data_end): - info->reg_type = PTR_TO_PACKET_END; - return true; - } - return false; -} - static bool __is_valid_xdp_access(int off, int size) { if (off < 0 || off >= sizeof(struct xdp_md)) @@ -3830,21 +3811,6 @@ static bool xdp_is_valid_access(int off, int size, return __is_valid_xdp_access(off, size); } -static bool xdp_is_valid_access_analyzer(int off, int size, - enum bpf_access_type type, - struct bpf_insn_access_aux *info) -{ - switch (off) { - case offsetof(struct xdp_buff, data): - info->reg_type = PTR_TO_PACKET; - return true; - case offsetof(struct xdp_buff, data_end): - info->reg_type = PTR_TO_PACKET_END; - return true; - } - return false; -} - void bpf_warn_invalid_xdp_action(u32 act) { const u32 act_max = XDP_REDIRECT; @@ -4516,10 +4482,6 @@ const struct bpf_verifier_ops tc_cls_act_verifier_ops = { .gen_prologue = tc_cls_act_prologue, }; -const struct bpf_verifier_ops tc_cls_act_analyzer_ops = { - .is_valid_access = tc_cls_act_is_valid_access_analyzer, -}; - const struct bpf_prog_ops tc_cls_act_prog_ops = { .test_run = bpf_prog_test_run_skb, }; @@ -4530,10 +4492,6 @@ const struct bpf_verifier_ops xdp_verifier_ops = { .convert_ctx_access = xdp_convert_ctx_access, }; -const struct bpf_verifier_ops xdp_analyzer_ops = { - .is_valid_access = xdp_is_valid_access_analyzer, -}; - const struct bpf_prog_ops xdp_prog_ops = { .test_run = bpf_prog_test_run_xdp, }; -- cgit v1.2.3-59-g8ed1b From 99feaafcdb566e8f032e7acc2a303713ad6bf196 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:20 -0400 Subject: net: dsa: make switch index unsigned Define the DSA switch index as an unsigned int, because it will never be less than 0. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 50e276dc4c01..fa1c21ab8092 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -209,7 +209,7 @@ struct dsa_switch { * Parent switch tree, and switch index. */ struct dsa_switch_tree *dst; - int index; + unsigned int index; /* Listener for switch fabric events */ struct notifier_block nb; -- cgit v1.2.3-59-g8ed1b From 49463b7f2da1a115404b02c5533bc2c2125833a3 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Fri, 3 Nov 2017 19:05:21 -0400 Subject: net: dsa: make tree index unsigned Similarly to a DSA switch and port, rename the tree index from "tree" to "index" and make it an unsigned int because it isn't supposed to be less than 0. u32 is an OF specific data used to retrieve the value and has no need to be propagated up to the tree index. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 2 +- net/dsa/dsa2.c | 14 +++++++------- net/dsa/slave.c | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index fa1c21ab8092..e54332968417 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -116,7 +116,7 @@ struct dsa_switch_tree { struct raw_notifier_head nh; /* Tree identifier */ - u32 tree; + unsigned int index; /* Number of switches attached to this tree */ struct kref refcount; diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 797d1156b4e6..8b68dc2f5707 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -27,12 +27,12 @@ static DEFINE_MUTEX(dsa2_mutex); static const struct devlink_ops dsa_devlink_ops = { }; -static struct dsa_switch_tree *dsa_get_dst(u32 tree) +static struct dsa_switch_tree *dsa_get_dst(unsigned int index) { struct dsa_switch_tree *dst; list_for_each_entry(dst, &dsa_switch_trees, list) - if (dst->tree == tree) { + if (dst->index == index) { kref_get(&dst->refcount); return dst; } @@ -53,14 +53,14 @@ static void dsa_put_dst(struct dsa_switch_tree *dst) kref_put(&dst->refcount, dsa_free_dst); } -static struct dsa_switch_tree *dsa_add_dst(u32 tree) +static struct dsa_switch_tree *dsa_add_dst(unsigned int index) { struct dsa_switch_tree *dst; dst = kzalloc(sizeof(*dst), GFP_KERNEL); if (!dst) return NULL; - dst->tree = tree; + dst->index = index; INIT_LIST_HEAD(&dst->list); list_add_tail(&dsa_switch_trees, &dst->list); kref_init(&dst->refcount); @@ -454,7 +454,7 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) dst->cpu_dp = NULL; - pr_info("DSA: tree %d unapplied\n", dst->tree); + pr_info("DSA: tree %d unapplied\n", dst->index); dst->applied = false; } @@ -504,7 +504,7 @@ static int dsa_ds_parse(struct dsa_switch_tree *dst, struct dsa_switch *ds) } - pr_info("DSA: switch %d %d parsed\n", dst->tree, ds->index); + pr_info("DSA: switch %d %d parsed\n", dst->index, ds->index); return 0; } @@ -549,7 +549,7 @@ static int dsa_dst_parse(struct dsa_switch_tree *dst) } } - pr_info("DSA: tree %d parsed\n", dst->tree); + pr_info("DSA: tree %d parsed\n", dst->index); return 0; } diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 9b75d0ac4092..814ced75a0cc 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -55,7 +55,7 @@ void dsa_slave_mii_bus_init(struct dsa_switch *ds) ds->slave_mii_bus->read = dsa_slave_phy_read; ds->slave_mii_bus->write = dsa_slave_phy_write; snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "dsa-%d.%d", - ds->dst->tree, ds->index); + ds->dst->index, ds->index); ds->slave_mii_bus->parent = ds->dev; ds->slave_mii_bus->phy_mask = ~ds->phys_mii_mask; } -- cgit v1.2.3-59-g8ed1b From 1f2556916d974cfb62b6af51660186b5f58bd869 Mon Sep 17 00:00:00 2001 From: Priyaranjan Jha Date: Fri, 3 Nov 2017 16:38:48 -0700 Subject: tcp: higher throughput under reordering with adaptive RACK reordering wnd Currently TCP RACK loss detection does not work well if packets are being reordered beyond its static reordering window (min_rtt/4).Under such reordering it may falsely trigger loss recoveries and reduce TCP throughput significantly. This patch improves that by increasing and reducing the reordering window based on DSACK, which is now supported in major TCP implementations. It makes RACK's reo_wnd adaptive based on DSACK and no. of recoveries. - If DSACK is received, increment reo_wnd by min_rtt/4 (upper bounded by srtt), since there is possibility that spurious retransmission was due to reordering delay longer than reo_wnd. - Persist the current reo_wnd value for TCP_RACK_RECOVERY_THRESH (16) no. of successful recoveries (accounts for full DSACK-based loss recovery undo). After that, reset it to default (min_rtt/4). - At max, reo_wnd is incremented only once per rtt. So that the new DSACK on which we are reacting, is due to the spurious retx (approx) after the reo_wnd has been updated last time. - reo_wnd is tracked in terms of steps (of min_rtt/4), rather than absolute value to account for change in rtt. In our internal testing, we observed significant increase in throughput, in scenarios where reordering exceeds min_rtt/4 (previous static value). Signed-off-by: Priyaranjan Jha Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 1 + include/linux/tcp.h | 9 +++++-- include/net/tcp.h | 2 ++ net/ipv4/tcp.c | 1 + net/ipv4/tcp_input.c | 7 +++++ net/ipv4/tcp_minisocks.c | 4 +++ net/ipv4/tcp_recovery.c | 48 ++++++++++++++++++++++++++++++++-- 7 files changed, 68 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index e6661b205f72..54410a1d4065 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -454,6 +454,7 @@ tcp_recovery - INTEGER RACK: 0x1 enables the RACK loss detection for fast detection of lost retransmissions and tail drops. + RACK: 0x2 makes RACK's reordering window static (min_rtt/4). Default: 0x1 diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 8c431385b272..22f40c96a15b 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -210,8 +210,13 @@ struct tcp_sock { u64 mstamp; /* (Re)sent time of the skb */ u32 rtt_us; /* Associated RTT */ u32 end_seq; /* Ending TCP sequence of the skb */ - u8 advanced; /* mstamp advanced since last lost marking */ - u8 reord; /* reordering detected */ + u32 last_delivered; /* tp->delivered at last reo_wnd adj */ + u8 reo_wnd_steps; /* Allowed reordering window */ +#define TCP_RACK_RECOVERY_THRESH 16 + u8 reo_wnd_persist:5, /* No. of recovery since last adj */ + dsack_seen:1, /* Whether DSACK seen after last adj */ + advanced:1, /* mstamp advanced since last lost marking */ + reord:1; /* reordering detected */ } rack; u16 advmss; /* Advertised MSS */ u32 chrono_start; /* Start time in jiffies of a TCP chrono */ diff --git a/include/net/tcp.h b/include/net/tcp.h index c2bf2a822b10..babfd4da1515 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -246,6 +246,7 @@ extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; #define TCP_RACK_LOSS_DETECTION 0x1 /* Use RACK to detect losses */ +#define TCP_RACK_STATIC_REO_WND 0x2 /* Use static RACK reo wnd */ extern atomic_long_t tcp_memory_allocated; extern struct percpu_counter tcp_sockets_allocated; @@ -1901,6 +1902,7 @@ extern void tcp_rack_mark_lost(struct sock *sk); extern void tcp_rack_advance(struct tcp_sock *tp, u8 sacked, u32 end_seq, u64 xmit_time); extern void tcp_rack_reo_timeout(struct sock *sk); +extern void tcp_rack_update_reo_wnd(struct sock *sk, struct rate_sample *rs); /* At how many usecs into the future should the RTO fire? */ static inline s64 tcp_rto_delta_us(const struct sock *sk) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index a7a0f316eb86..c4cb19ed4628 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -447,6 +447,7 @@ void tcp_init_sock(struct sock *sk) tcp_assign_congestion_control(sk); tp->tsoffset = 0; + tp->rack.reo_wnd_steps = 1; sk->sk_state = TCP_CLOSE; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 8393b405ea98..0ada8bfc2ebd 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -856,6 +856,7 @@ void tcp_disable_fack(struct tcp_sock *tp) static void tcp_dsack_seen(struct tcp_sock *tp) { tp->rx_opt.sack_ok |= TCP_DSACK_SEEN; + tp->rack.dsack_seen = 1; } static void tcp_update_reordering(struct sock *sk, const int metric, @@ -2408,6 +2409,8 @@ static bool tcp_try_undo_recovery(struct sock *sk) mib_idx = LINUX_MIB_TCPFULLUNDO; NET_INC_STATS(sock_net(sk), mib_idx); + } else if (tp->rack.reo_wnd_persist) { + tp->rack.reo_wnd_persist--; } if (tp->snd_una == tp->high_seq && tcp_is_reno(tp)) { /* Hold old state until something *above* high_seq @@ -2427,6 +2430,8 @@ static bool tcp_try_undo_dsack(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); if (tp->undo_marker && !tp->undo_retrans) { + tp->rack.reo_wnd_persist = min(TCP_RACK_RECOVERY_THRESH, + tp->rack.reo_wnd_persist + 1); DBGUNDO(sk, "D-SACK"); tcp_undo_cwnd_reduction(sk, false); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPDSACKUNDO); @@ -3644,6 +3649,8 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) flag |= tcp_clean_rtx_queue(sk, prior_fackets, prior_snd_una, &acked, &sack_state); + tcp_rack_update_reo_wnd(sk, &rs); + if (tp->tlp_high_seq) tcp_process_tlp_ack(sk, ack, flag); /* If needed, reset TLP/RTO timer; RACK may later override this. */ diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 3c65c1a3f944..4bb86580decd 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -551,6 +551,10 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, newtp->syn_data_acked = 0; newtp->rack.mstamp = 0; newtp->rack.advanced = 0; + newtp->rack.reo_wnd_steps = 1; + newtp->rack.last_delivered = 0; + newtp->rack.reo_wnd_persist = 0; + newtp->rack.dsack_seen = 0; __TCP_INC_STATS(sock_net(sk), TCP_MIB_PASSIVEOPENS); } diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c index ac3e9c6d3a3d..d3ea89020c69 100644 --- a/net/ipv4/tcp_recovery.c +++ b/net/ipv4/tcp_recovery.c @@ -44,6 +44,7 @@ static bool tcp_rack_sent_after(u64 t1, u64 t2, u32 seq1, u32 seq2) static void tcp_rack_detect_loss(struct sock *sk, u32 *reo_timeout) { struct tcp_sock *tp = tcp_sk(sk); + u32 min_rtt = tcp_min_rtt(tp); struct sk_buff *skb, *n; u32 reo_wnd; @@ -54,8 +55,10 @@ static void tcp_rack_detect_loss(struct sock *sk, u32 *reo_timeout) * to queuing or delayed ACKs. */ reo_wnd = 1000; - if ((tp->rack.reord || !tp->lost_out) && tcp_min_rtt(tp) != ~0U) - reo_wnd = max(tcp_min_rtt(tp) >> 2, reo_wnd); + if ((tp->rack.reord || !tp->lost_out) && min_rtt != ~0U) { + reo_wnd = max((min_rtt >> 2) * tp->rack.reo_wnd_steps, reo_wnd); + reo_wnd = min(reo_wnd, tp->srtt_us >> 3); + } list_for_each_entry_safe(skb, n, &tp->tsorted_sent_queue, tcp_tsorted_anchor) { @@ -160,3 +163,44 @@ void tcp_rack_reo_timeout(struct sock *sk) if (inet_csk(sk)->icsk_pending != ICSK_TIME_RETRANS) tcp_rearm_rto(sk); } + +/* Updates the RACK's reo_wnd based on DSACK and no. of recoveries. + * + * If DSACK is received, increment reo_wnd by min_rtt/4 (upper bounded + * by srtt), since there is possibility that spurious retransmission was + * due to reordering delay longer than reo_wnd. + * + * Persist the current reo_wnd value for TCP_RACK_RECOVERY_THRESH (16) + * no. of successful recoveries (accounts for full DSACK-based loss + * recovery undo). After that, reset it to default (min_rtt/4). + * + * At max, reo_wnd is incremented only once per rtt. So that the new + * DSACK on which we are reacting, is due to the spurious retx (approx) + * after the reo_wnd has been updated last time. + * + * reo_wnd is tracked in terms of steps (of min_rtt/4), rather than + * absolute value to account for change in rtt. + */ +void tcp_rack_update_reo_wnd(struct sock *sk, struct rate_sample *rs) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (sock_net(sk)->ipv4.sysctl_tcp_recovery & TCP_RACK_STATIC_REO_WND || + !rs->prior_delivered) + return; + + /* Disregard DSACK if a rtt has not passed since we adjusted reo_wnd */ + if (before(rs->prior_delivered, tp->rack.last_delivered)) + tp->rack.dsack_seen = 0; + + /* Adjust the reo_wnd if update is pending */ + if (tp->rack.dsack_seen) { + tp->rack.reo_wnd_steps = min_t(u32, 0xFF, + tp->rack.reo_wnd_steps + 1); + tp->rack.dsack_seen = 0; + tp->rack.last_delivered = tp->delivered; + tp->rack.reo_wnd_persist = TCP_RACK_RECOVERY_THRESH; + } else if (!tp->rack.reo_wnd_persist) { + tp->rack.reo_wnd_steps = 1; + } +} -- cgit v1.2.3-59-g8ed1b From ecf8fecb7828648cba0e42de7464a7e600c93459 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Sun, 5 Nov 2017 08:15:31 -0500 Subject: device_cgroup: prepare code for bpf-based device controller This is non-functional change to prepare the device cgroup code for adding eBPF-based controller for cgroups v2. The patch performs the following changes: 1) __devcgroup_inode_permission() and devcgroup_inode_mknod() are moving to the device-cgroup.h and converting into static inline. 2) __devcgroup_check_permission() is exported. 3) devcgroup_check_permission() wrapper is introduced to be used by both existing and new bpf-based implementations. Signed-off-by: Roman Gushchin Acked-by: Tejun Heo Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/linux/device_cgroup.h | 61 ++++++++++++++++++++++++++++++++++++++++--- security/device_cgroup.c | 47 ++------------------------------- 2 files changed, 59 insertions(+), 49 deletions(-) (limited to 'include') diff --git a/include/linux/device_cgroup.h b/include/linux/device_cgroup.h index cdbc344a92e4..2d93d7ecd479 100644 --- a/include/linux/device_cgroup.h +++ b/include/linux/device_cgroup.h @@ -1,17 +1,70 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include +#define DEVCG_ACC_MKNOD 1 +#define DEVCG_ACC_READ 2 +#define DEVCG_ACC_WRITE 4 +#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE) + +#define DEVCG_DEV_BLOCK 1 +#define DEVCG_DEV_CHAR 2 +#define DEVCG_DEV_ALL 4 /* this represents all devices */ + +#ifdef CONFIG_CGROUP_DEVICE +extern int __devcgroup_check_permission(short type, u32 major, u32 minor, + short access); +#else +static inline int __devcgroup_check_permission(short type, u32 major, u32 minor, + short access) +{ return 0; } +#endif + #ifdef CONFIG_CGROUP_DEVICE -extern int __devcgroup_inode_permission(struct inode *inode, int mask); -extern int devcgroup_inode_mknod(int mode, dev_t dev); +static inline int devcgroup_check_permission(short type, u32 major, u32 minor, + short access) +{ + return __devcgroup_check_permission(type, major, minor, access); +} + static inline int devcgroup_inode_permission(struct inode *inode, int mask) { + short type, access = 0; + if (likely(!inode->i_rdev)) return 0; - if (!S_ISBLK(inode->i_mode) && !S_ISCHR(inode->i_mode)) + + if (S_ISBLK(inode->i_mode)) + type = DEVCG_DEV_BLOCK; + else if (S_ISCHR(inode->i_mode)) + type = DEVCG_DEV_CHAR; + else return 0; - return __devcgroup_inode_permission(inode, mask); + + if (mask & MAY_WRITE) + access |= DEVCG_ACC_WRITE; + if (mask & MAY_READ) + access |= DEVCG_ACC_READ; + + return devcgroup_check_permission(type, imajor(inode), iminor(inode), + access); } + +static inline int devcgroup_inode_mknod(int mode, dev_t dev) +{ + short type; + + if (!S_ISBLK(mode) && !S_ISCHR(mode)) + return 0; + + if (S_ISBLK(mode)) + type = DEVCG_DEV_BLOCK; + else + type = DEVCG_DEV_CHAR; + + return devcgroup_check_permission(type, MAJOR(dev), MINOR(dev), + DEVCG_ACC_MKNOD); +} + #else static inline int devcgroup_inode_permission(struct inode *inode, int mask) { return 0; } diff --git a/security/device_cgroup.c b/security/device_cgroup.c index 968c21557ba7..c65b39bafdfe 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -15,15 +15,6 @@ #include #include -#define DEVCG_ACC_MKNOD 1 -#define DEVCG_ACC_READ 2 -#define DEVCG_ACC_WRITE 4 -#define DEVCG_ACC_MASK (DEVCG_ACC_MKNOD | DEVCG_ACC_READ | DEVCG_ACC_WRITE) - -#define DEVCG_DEV_BLOCK 1 -#define DEVCG_DEV_CHAR 2 -#define DEVCG_DEV_ALL 4 /* this represents all devices */ - static DEFINE_MUTEX(devcgroup_mutex); enum devcg_behavior { @@ -810,8 +801,8 @@ struct cgroup_subsys devices_cgrp_subsys = { * * returns 0 on success, -EPERM case the operation is not permitted */ -static int __devcgroup_check_permission(short type, u32 major, u32 minor, - short access) +int __devcgroup_check_permission(short type, u32 major, u32 minor, + short access) { struct dev_cgroup *dev_cgroup; bool rc; @@ -833,37 +824,3 @@ static int __devcgroup_check_permission(short type, u32 major, u32 minor, return 0; } - -int __devcgroup_inode_permission(struct inode *inode, int mask) -{ - short type, access = 0; - - if (S_ISBLK(inode->i_mode)) - type = DEVCG_DEV_BLOCK; - if (S_ISCHR(inode->i_mode)) - type = DEVCG_DEV_CHAR; - if (mask & MAY_WRITE) - access |= DEVCG_ACC_WRITE; - if (mask & MAY_READ) - access |= DEVCG_ACC_READ; - - return __devcgroup_check_permission(type, imajor(inode), iminor(inode), - access); -} - -int devcgroup_inode_mknod(int mode, dev_t dev) -{ - short type; - - if (!S_ISBLK(mode) && !S_ISCHR(mode)) - return 0; - - if (S_ISBLK(mode)) - type = DEVCG_DEV_BLOCK; - else - type = DEVCG_DEV_CHAR; - - return __devcgroup_check_permission(type, MAJOR(dev), MINOR(dev), - DEVCG_ACC_MKNOD); - -} -- cgit v1.2.3-59-g8ed1b From ebc614f687369f9df99828572b1d85a7c2de3d92 Mon Sep 17 00:00:00 2001 From: Roman Gushchin Date: Sun, 5 Nov 2017 08:15:32 -0500 Subject: bpf, cgroup: implement eBPF-based device controller for cgroup v2 Cgroup v2 lacks the device controller, provided by cgroup v1. This patch adds a new eBPF program type, which in combination of previously added ability to attach multiple eBPF programs to a cgroup, will provide a similar functionality, but with some additional flexibility. This patch introduces a BPF_PROG_TYPE_CGROUP_DEVICE program type. A program takes major and minor device numbers, device type (block/character) and access type (mknod/read/write) as parameters and returns an integer which defines if the operation should be allowed or terminated with -EPERM. Signed-off-by: Roman Gushchin Acked-by: Alexei Starovoitov Acked-by: Tejun Heo Cc: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf-cgroup.h | 15 ++++++++++ include/linux/bpf_types.h | 3 ++ include/linux/device_cgroup.h | 8 ++++- include/uapi/linux/bpf.h | 15 ++++++++++ kernel/bpf/cgroup.c | 67 ++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 7 +++++ kernel/bpf/verifier.c | 1 + tools/include/uapi/linux/bpf.h | 15 ++++++++++ 8 files changed, 130 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h index 87a7db9feb38..a7f16e0f8d68 100644 --- a/include/linux/bpf-cgroup.h +++ b/include/linux/bpf-cgroup.h @@ -67,6 +67,9 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk, struct bpf_sock_ops_kern *sock_ops, enum bpf_attach_type type); +int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, + short access, enum bpf_attach_type type); + /* Wrappers for __cgroup_bpf_run_filter_skb() guarded by cgroup_bpf_enabled. */ #define BPF_CGROUP_RUN_PROG_INET_INGRESS(sk, skb) \ ({ \ @@ -112,6 +115,17 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk, } \ __ret; \ }) + +#define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access) \ +({ \ + int __ret = 0; \ + if (cgroup_bpf_enabled) \ + __ret = __cgroup_bpf_check_dev_permission(type, major, minor, \ + access, \ + BPF_CGROUP_DEVICE); \ + \ + __ret; \ +}) #else struct cgroup_bpf {}; @@ -122,6 +136,7 @@ static inline int cgroup_bpf_inherit(struct cgroup *cgrp) { return 0; } #define BPF_CGROUP_RUN_PROG_INET_EGRESS(sk,skb) ({ 0; }) #define BPF_CGROUP_RUN_PROG_INET_SOCK(sk) ({ 0; }) #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; }) +#define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; }) #endif /* CONFIG_CGROUP_BPF */ diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 53c5b9ad7220..978c1d9c9383 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -19,6 +19,9 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe) BPF_PROG_TYPE(BPF_PROG_TYPE_TRACEPOINT, tracepoint) BPF_PROG_TYPE(BPF_PROG_TYPE_PERF_EVENT, perf_event) #endif +#ifdef CONFIG_CGROUP_BPF +BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev) +#endif BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY, array_map_ops) BPF_MAP_TYPE(BPF_MAP_TYPE_PERCPU_ARRAY, percpu_array_map_ops) diff --git a/include/linux/device_cgroup.h b/include/linux/device_cgroup.h index 2d93d7ecd479..8557efe096dc 100644 --- a/include/linux/device_cgroup.h +++ b/include/linux/device_cgroup.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include +#include #define DEVCG_ACC_MKNOD 1 #define DEVCG_ACC_READ 2 @@ -19,10 +20,15 @@ static inline int __devcgroup_check_permission(short type, u32 major, u32 minor, { return 0; } #endif -#ifdef CONFIG_CGROUP_DEVICE +#if defined(CONFIG_CGROUP_DEVICE) || defined(CONFIG_CGROUP_BPF) static inline int devcgroup_check_permission(short type, u32 major, u32 minor, short access) { + int rc = BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type, major, minor, access); + + if (rc) + return -EPERM; + return __devcgroup_check_permission(type, major, minor, access); } diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 4455dd195201..e880ae6434ee 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -132,6 +132,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_LWT_XMIT, BPF_PROG_TYPE_SOCK_OPS, BPF_PROG_TYPE_SK_SKB, + BPF_PROG_TYPE_CGROUP_DEVICE, }; enum bpf_attach_type { @@ -141,6 +142,7 @@ enum bpf_attach_type { BPF_CGROUP_SOCK_OPS, BPF_SK_SKB_STREAM_PARSER, BPF_SK_SKB_STREAM_VERDICT, + BPF_CGROUP_DEVICE, __MAX_BPF_ATTACH_TYPE }; @@ -991,4 +993,17 @@ struct bpf_perf_event_value { __u64 running; }; +#define BPF_DEVCG_ACC_MKNOD (1ULL << 0) +#define BPF_DEVCG_ACC_READ (1ULL << 1) +#define BPF_DEVCG_ACC_WRITE (1ULL << 2) + +#define BPF_DEVCG_DEV_BLOCK (1ULL << 0) +#define BPF_DEVCG_DEV_CHAR (1ULL << 1) + +struct bpf_cgroup_dev_ctx { + __u32 access_type; /* (access << 16) | type */ + __u32 major; + __u32 minor; +}; + #endif /* _UAPI__LINUX_BPF_H__ */ diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 3db5a17fcfe8..b789ab78d28f 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -522,3 +522,70 @@ int __cgroup_bpf_run_filter_sock_ops(struct sock *sk, return ret == 1 ? 0 : -EPERM; } EXPORT_SYMBOL(__cgroup_bpf_run_filter_sock_ops); + +int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor, + short access, enum bpf_attach_type type) +{ + struct cgroup *cgrp; + struct bpf_cgroup_dev_ctx ctx = { + .access_type = (access << 16) | dev_type, + .major = major, + .minor = minor, + }; + int allow = 1; + + rcu_read_lock(); + cgrp = task_dfl_cgroup(current); + allow = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, + BPF_PROG_RUN); + rcu_read_unlock(); + + return !allow; +} +EXPORT_SYMBOL(__cgroup_bpf_check_dev_permission); + +static const struct bpf_func_proto * +cgroup_dev_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + case BPF_FUNC_map_lookup_elem: + return &bpf_map_lookup_elem_proto; + case BPF_FUNC_map_update_elem: + return &bpf_map_update_elem_proto; + case BPF_FUNC_map_delete_elem: + return &bpf_map_delete_elem_proto; + case BPF_FUNC_get_current_uid_gid: + return &bpf_get_current_uid_gid_proto; + case BPF_FUNC_trace_printk: + if (capable(CAP_SYS_ADMIN)) + return bpf_get_trace_printk_proto(); + default: + return NULL; + } +} + +static bool cgroup_dev_is_valid_access(int off, int size, + enum bpf_access_type type, + struct bpf_insn_access_aux *info) +{ + if (type == BPF_WRITE) + return false; + + if (off < 0 || off + size > sizeof(struct bpf_cgroup_dev_ctx)) + return false; + /* The verifier guarantees that size > 0. */ + if (off % size != 0) + return false; + if (size != sizeof(__u32)) + return false; + + return true; +} + +const struct bpf_prog_ops cg_dev_prog_ops = { +}; + +const struct bpf_verifier_ops cg_dev_verifier_ops = { + .get_func_proto = cgroup_dev_func_proto, + .is_valid_access = cgroup_dev_is_valid_access, +}; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 416d70cdfc76..09badc37e864 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1326,6 +1326,9 @@ static int bpf_prog_attach(const union bpf_attr *attr) case BPF_CGROUP_SOCK_OPS: ptype = BPF_PROG_TYPE_SOCK_OPS; break; + case BPF_CGROUP_DEVICE: + ptype = BPF_PROG_TYPE_CGROUP_DEVICE; + break; case BPF_SK_SKB_STREAM_PARSER: case BPF_SK_SKB_STREAM_VERDICT: return sockmap_get_from_fd(attr, true); @@ -1378,6 +1381,9 @@ static int bpf_prog_detach(const union bpf_attr *attr) case BPF_CGROUP_SOCK_OPS: ptype = BPF_PROG_TYPE_SOCK_OPS; break; + case BPF_CGROUP_DEVICE: + ptype = BPF_PROG_TYPE_CGROUP_DEVICE; + break; case BPF_SK_SKB_STREAM_PARSER: case BPF_SK_SKB_STREAM_VERDICT: return sockmap_get_from_fd(attr, false); @@ -1420,6 +1426,7 @@ static int bpf_prog_query(const union bpf_attr *attr, case BPF_CGROUP_INET_EGRESS: case BPF_CGROUP_INET_SOCK_CREATE: case BPF_CGROUP_SOCK_OPS: + case BPF_CGROUP_DEVICE: break; default: return -EINVAL; diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index add845fe788a..4a942e2e753d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3124,6 +3124,7 @@ static int check_return_code(struct bpf_verifier_env *env) case BPF_PROG_TYPE_CGROUP_SKB: case BPF_PROG_TYPE_CGROUP_SOCK: case BPF_PROG_TYPE_SOCK_OPS: + case BPF_PROG_TYPE_CGROUP_DEVICE: break; default: return 0; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index e92f62cf933a..b280f37cd057 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -131,6 +131,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_LWT_XMIT, BPF_PROG_TYPE_SOCK_OPS, BPF_PROG_TYPE_SK_SKB, + BPF_PROG_TYPE_CGROUP_DEVICE, }; enum bpf_attach_type { @@ -140,6 +141,7 @@ enum bpf_attach_type { BPF_CGROUP_SOCK_OPS, BPF_SK_SKB_STREAM_PARSER, BPF_SK_SKB_STREAM_VERDICT, + BPF_CGROUP_DEVICE, __MAX_BPF_ATTACH_TYPE }; @@ -990,4 +992,17 @@ struct bpf_perf_event_value { __u64 running; }; +#define BPF_DEVCG_ACC_MKNOD (1ULL << 0) +#define BPF_DEVCG_ACC_READ (1ULL << 1) +#define BPF_DEVCG_ACC_WRITE (1ULL << 2) + +#define BPF_DEVCG_DEV_BLOCK (1ULL << 0) +#define BPF_DEVCG_DEV_CHAR (1ULL << 1) + +struct bpf_cgroup_dev_ctx { + __u32 access_type; /* (access << 16) | type */ + __u32 major; + __u32 minor; +}; + #endif /* _UAPI__LINUX_BPF_H__ */ -- cgit v1.2.3-59-g8ed1b From 63dcb81e5b9e1faadf4b55450141bc4446e5a3d3 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 3 Oct 2017 08:53:07 +0300 Subject: include/linux/fs.h: fix comment about struct address_space Before commit 9c5d760b8d22 ("mm: split gfp_mask and mapping flags into separate fields") the private_* fields of struct adrress_space were grouped together and using "ditto" in comments describing the last fields was correct. With introduction of gpf_mask between private_lock and private_list "ditto" references the wrong description. Fix it by using the elaborate description. Signed-off-by: Mike Rapoport Signed-off-by: Al Viro --- include/linux/fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/fs.h b/include/linux/fs.h index 31f8b2ea358c..ccbac0ed672c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -403,7 +403,7 @@ struct address_space { unsigned long flags; /* error bits */ spinlock_t private_lock; /* for use by the address_space */ gfp_t gfp_mask; /* implicit gfp mask for allocations */ - struct list_head private_list; /* ditto */ + struct list_head private_list; /* for use by the address_space */ void *private_data; /* ditto */ errseq_t wb_err; } __attribute__((aligned(sizeof(long)))) __randomize_layout; -- cgit v1.2.3-59-g8ed1b From 6afc662e68b5f988282ff20afd58a89b1c279dca Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Wed, 6 Sep 2017 21:59:50 +0800 Subject: f2fs: support flexible inline xattr size Now, in product, more and more features based on file encryption were introduced, their demand of xattr space is increasing, however, inline xattr has fixed-size of 200 bytes, once inline xattr space is full, new increased xattr data would occupy additional xattr block which may bring us more space usage and performance regression during persisting. In order to resolve above issue, it's better to expand inline xattr size flexibly according to user's requirement. So this patch introduces new filesystem feature 'flexible inline xattr', and new mount option 'inline_xattr_size=%u', once mkfs enables the feature, we can use the option to make f2fs supporting flexible inline xattr size. To support this feature, we add extra attribute i_inline_xattr_size in inode layout, indicating that how many space inline xattr borrows from block address mapping space in inode layout, by this, we can easily locate and store flexible-sized inline xattr data in inode. Inode disk layout: +----------------------+ | .i_mode | | ... | | .i_ext | +----------------------+ | .i_extra_isize | | .i_inline_xattr_size |-----------+ | ... | | +----------------------+ | | .i_addr | | | - block address or | | | - inline data | | +----------------------+<---+ v | inline xattr | +---inline xattr range +----------------------+<---+ | .i_nid | +----------------------+ | node_footer | | (nid, ino, offset) | +----------------------+ Note that, we have to cnosider backward compatibility which reserved inline_data space, 200 bytes, all the time, reported by Sheng Yong. Previous inline data or directory always reserved 200 bytes in inode layout, even if inline_xattr is disabled. In order to keep inline_dentry's structure for backward compatibility, we get the space back only from inline_data. Signed-off-by: Chao Yu Reported-by: Sheng Yong Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 39 ++++++++++++++++++++++++++------------- fs/f2fs/inode.c | 21 +++++++++++++++++++++ fs/f2fs/namei.c | 13 +++++++++++++ fs/f2fs/node.c | 10 ++++++++-- fs/f2fs/super.c | 32 +++++++++++++++++++++++++++++++- fs/f2fs/sysfs.c | 7 +++++++ fs/f2fs/xattr.c | 18 +++++++++--------- include/linux/f2fs_fs.h | 5 +++-- 8 files changed, 118 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4034459f1f5d..13a96b8aa2d7 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -93,6 +93,7 @@ extern char *fault_name[FAULT_MAX]; #define F2FS_MOUNT_GRPQUOTA 0x00100000 #define F2FS_MOUNT_PRJQUOTA 0x00200000 #define F2FS_MOUNT_QUOTA 0x00400000 +#define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00800000 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option) #define set_opt(sbi, option) ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option) @@ -118,6 +119,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_EXTRA_ATTR 0x0008 #define F2FS_FEATURE_PRJQUOTA 0x0010 #define F2FS_FEATURE_INODE_CHKSUM 0x0020 +#define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -397,11 +399,14 @@ struct f2fs_flush_device { /* for inline stuff */ #define DEF_INLINE_RESERVED_SIZE 1 +#define DEF_MIN_INLINE_SIZE 1 static inline int get_extra_isize(struct inode *inode); -#define MAX_INLINE_DATA(inode) (sizeof(__le32) * \ - (CUR_ADDRS_PER_INODE(inode) - \ - DEF_INLINE_RESERVED_SIZE - \ - F2FS_INLINE_XATTR_ADDRS)) +static inline int get_inline_xattr_addrs(struct inode *inode); +#define F2FS_INLINE_XATTR_ADDRS(inode) get_inline_xattr_addrs(inode) +#define MAX_INLINE_DATA(inode) (sizeof(__le32) * \ + (CUR_ADDRS_PER_INODE(inode) - \ + F2FS_INLINE_XATTR_ADDRS(inode) - \ + DEF_INLINE_RESERVED_SIZE)) /* for inline dir */ #define NR_INLINE_DENTRY(inode) (MAX_INLINE_DATA(inode) * BITS_PER_BYTE / \ @@ -612,6 +617,7 @@ struct f2fs_inode_info { int i_extra_isize; /* size of extra space located in i_addr */ kprojid_t i_projid; /* id for project quota */ + int i_inline_xattr_size; /* inline xattr size */ }; static inline void get_extent_info(struct extent_info *ext, @@ -1069,6 +1075,7 @@ struct f2fs_sb_info { loff_t max_file_blocks; /* max block index of file */ int active_logs; /* # of active logs */ int dir_level; /* directory level */ + int inline_xattr_size; /* inline xattr size */ block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ @@ -2186,25 +2193,20 @@ static inline int f2fs_has_inline_xattr(struct inode *inode) static inline unsigned int addrs_per_inode(struct inode *inode) { - if (f2fs_has_inline_xattr(inode)) - return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS; - return CUR_ADDRS_PER_INODE(inode); + return CUR_ADDRS_PER_INODE(inode) - F2FS_INLINE_XATTR_ADDRS(inode); } -static inline void *inline_xattr_addr(struct page *page) +static inline void *inline_xattr_addr(struct inode *inode, struct page *page) { struct f2fs_inode *ri = F2FS_INODE(page); return (void *)&(ri->i_addr[DEF_ADDRS_PER_INODE - - F2FS_INLINE_XATTR_ADDRS]); + F2FS_INLINE_XATTR_ADDRS(inode)]); } static inline int inline_xattr_size(struct inode *inode) { - if (f2fs_has_inline_xattr(inode)) - return F2FS_INLINE_XATTR_ADDRS << 2; - else - return 0; + return get_inline_xattr_addrs(inode) * sizeof(__le32); } static inline int f2fs_has_inline_data(struct inode *inode) @@ -2354,6 +2356,12 @@ static inline int get_extra_isize(struct inode *inode) return F2FS_I(inode)->i_extra_isize / sizeof(__le32); } +static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb); +static inline int get_inline_xattr_addrs(struct inode *inode) +{ + return F2FS_I(inode)->i_inline_xattr_size; +} + #define get_inode_mode(i) \ ((is_inode_flag_set(i, FI_ACL_MODE)) ? \ (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) @@ -3023,6 +3031,11 @@ static inline int f2fs_sb_has_inode_chksum(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_INODE_CHKSUM); } +static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_FLEXIBLE_INLINE_XATTR); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c index 3617e7fca930..9684d53563f1 100644 --- a/fs/f2fs/inode.c +++ b/fs/f2fs/inode.c @@ -232,6 +232,23 @@ static int do_read_inode(struct inode *inode) fi->i_extra_isize = f2fs_has_extra_attr(inode) ? le16_to_cpu(ri->i_extra_isize) : 0; + if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)) { + f2fs_bug_on(sbi, !f2fs_has_extra_attr(inode)); + fi->i_inline_xattr_size = le16_to_cpu(ri->i_inline_xattr_size); + } else if (f2fs_has_inline_xattr(inode) || + f2fs_has_inline_dentry(inode)) { + fi->i_inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS; + } else { + + /* + * Previous inline data or directory always reserved 200 bytes + * in inode layout, even if inline_xattr is disabled. In order + * to keep inline_dentry's structure for backward compatibility, + * we get the space back only from inline_data. + */ + fi->i_inline_xattr_size = 0; + } + /* check data exist */ if (f2fs_has_inline_data(inode) && !f2fs_exist_data(inode)) __recover_inline_status(inode, node_page); @@ -384,6 +401,10 @@ int update_inode(struct inode *inode, struct page *node_page) if (f2fs_has_extra_attr(inode)) { ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize); + if (f2fs_sb_has_flexible_inline_xattr(F2FS_I_SB(inode)->sb)) + ri->i_inline_xattr_size = + cpu_to_le16(F2FS_I(inode)->i_inline_xattr_size); + if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)->sb) && F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize, i_projid)) { diff --git a/fs/f2fs/namei.c b/fs/f2fs/namei.c index 773d327f8fc1..28bdf8828e73 100644 --- a/fs/f2fs/namei.c +++ b/fs/f2fs/namei.c @@ -29,6 +29,7 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) nid_t ino; struct inode *inode; bool nid_free = false; + int xattr_size = 0; int err; inode = new_inode(dir->i_sb); @@ -86,11 +87,23 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) if (test_opt(sbi, INLINE_XATTR)) set_inode_flag(inode, FI_INLINE_XATTR); + if (test_opt(sbi, INLINE_DATA) && f2fs_may_inline_data(inode)) set_inode_flag(inode, FI_INLINE_DATA); if (f2fs_may_inline_dentry(inode)) set_inode_flag(inode, FI_INLINE_DENTRY); + if (f2fs_sb_has_flexible_inline_xattr(sbi->sb)) { + f2fs_bug_on(sbi, !f2fs_has_extra_attr(inode)); + if (f2fs_has_inline_xattr(inode)) + xattr_size = sbi->inline_xattr_size; + /* Otherwise, will be 0 */ + } else if (f2fs_has_inline_xattr(inode) || + f2fs_has_inline_dentry(inode)) { + xattr_size = DEFAULT_INLINE_XATTR_ADDRS; + } + F2FS_I(inode)->i_inline_xattr_size = xattr_size; + f2fs_init_extent_tree(inode, NULL); stat_inc_inline_xattr(inode); diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index bdb7e92636b4..f44d83705245 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -2191,8 +2191,8 @@ void recover_inline_xattr(struct inode *inode, struct page *page) goto update_inode; } - dst_addr = inline_xattr_addr(ipage); - src_addr = inline_xattr_addr(page); + dst_addr = inline_xattr_addr(inode, ipage); + src_addr = inline_xattr_addr(inode, page); inline_size = inline_xattr_size(inode); f2fs_wait_on_page_writeback(ipage, NODE, true); @@ -2281,6 +2281,12 @@ retry: dst->i_inline = src->i_inline & (F2FS_INLINE_XATTR | F2FS_EXTRA_ATTR); if (dst->i_inline & F2FS_EXTRA_ATTR) { dst->i_extra_isize = src->i_extra_isize; + + if (f2fs_sb_has_flexible_inline_xattr(sbi->sb) && + F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), + i_inline_xattr_size)) + dst->i_inline_xattr_size = src->i_inline_xattr_size; + if (f2fs_sb_has_project_quota(sbi->sb) && F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize), i_projid)) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 97fa81503e1f..213d2c1e5759 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -92,6 +92,7 @@ enum { Opt_disable_ext_identify, Opt_inline_xattr, Opt_noinline_xattr, + Opt_inline_xattr_size, Opt_inline_data, Opt_inline_dentry, Opt_noinline_dentry, @@ -141,6 +142,7 @@ static match_table_t f2fs_tokens = { {Opt_disable_ext_identify, "disable_ext_identify"}, {Opt_inline_xattr, "inline_xattr"}, {Opt_noinline_xattr, "noinline_xattr"}, + {Opt_inline_xattr_size, "inline_xattr_size=%u"}, {Opt_inline_data, "inline_data"}, {Opt_inline_dentry, "inline_dentry"}, {Opt_noinline_dentry, "noinline_dentry"}, @@ -383,6 +385,12 @@ static int parse_options(struct super_block *sb, char *options) case Opt_noinline_xattr: clear_opt(sbi, INLINE_XATTR); break; + case Opt_inline_xattr_size: + if (args->from && match_int(args, &arg)) + return -EINVAL; + set_opt(sbi, INLINE_XATTR_SIZE); + sbi->inline_xattr_size = arg; + break; #else case Opt_user_xattr: f2fs_msg(sb, KERN_INFO, @@ -604,6 +612,24 @@ static int parse_options(struct super_block *sb, char *options) F2FS_IO_SIZE_KB(sbi)); return -EINVAL; } + + if (test_opt(sbi, INLINE_XATTR_SIZE)) { + if (!test_opt(sbi, INLINE_XATTR)) { + f2fs_msg(sb, KERN_ERR, + "inline_xattr_size option should be " + "set with inline_xattr option"); + return -EINVAL; + } + if (!sbi->inline_xattr_size || + sbi->inline_xattr_size >= DEF_ADDRS_PER_INODE - + F2FS_TOTAL_EXTRA_ATTR_SIZE - + DEF_INLINE_RESERVED_SIZE - + DEF_MIN_INLINE_SIZE) { + f2fs_msg(sb, KERN_ERR, + "inline xattr size is out of range"); + return -EINVAL; + } + } return 0; } @@ -1050,6 +1076,9 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) seq_puts(seq, ",inline_xattr"); else seq_puts(seq, ",noinline_xattr"); + if (test_opt(sbi, INLINE_XATTR_SIZE)) + seq_printf(seq, ",inline_xattr_size=%u", + sbi->inline_xattr_size); #endif #ifdef CONFIG_F2FS_FS_POSIX_ACL if (test_opt(sbi, POSIX_ACL)) @@ -1112,6 +1141,7 @@ static void default_options(struct f2fs_sb_info *sbi) { /* init some FS parameters */ sbi->active_logs = NR_CURSEG_TYPE; + sbi->inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS; set_opt(sbi, BG_GC); set_opt(sbi, INLINE_XATTR); @@ -1670,7 +1700,7 @@ static loff_t max_file_blocks(void) /* * note: previously, result is equal to (DEF_ADDRS_PER_INODE - - * F2FS_INLINE_XATTR_ADDRS), but now f2fs try to reserve more + * DEFAULT_INLINE_XATTR_ADDRS), but now f2fs try to reserve more * space in inode.i_addr, it will be more safe to reassign * result as zero. */ diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index 48ebe6153cc5..e09e59cc678a 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -107,6 +107,9 @@ static ssize_t features_show(struct f2fs_attr *a, if (f2fs_sb_has_inode_chksum(sb)) len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "inode_checksum"); + if (f2fs_sb_has_flexible_inline_xattr(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "flexible_inline_xattr"); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } @@ -216,6 +219,7 @@ enum feat_id { FEAT_EXTRA_ATTR, FEAT_PROJECT_QUOTA, FEAT_INODE_CHECKSUM, + FEAT_FLEXIBLE_INLINE_XATTR, }; static ssize_t f2fs_feature_show(struct f2fs_attr *a, @@ -228,6 +232,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, case FEAT_EXTRA_ATTR: case FEAT_PROJECT_QUOTA: case FEAT_INODE_CHECKSUM: + case FEAT_FLEXIBLE_INLINE_XATTR: return snprintf(buf, PAGE_SIZE, "supported\n"); } return 0; @@ -299,6 +304,7 @@ F2FS_FEATURE_RO_ATTR(atomic_write, FEAT_ATOMIC_WRITE); F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR); F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA); F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); +F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -346,6 +352,7 @@ static struct attribute *f2fs_feat_attrs[] = { ATTR_LIST(extra_attr), ATTR_LIST(project_quota), ATTR_LIST(inode_checksum), + ATTR_LIST(flexible_inline_xattr), NULL, }; diff --git a/fs/f2fs/xattr.c b/fs/f2fs/xattr.c index 9fbcb7687d3b..52b46f598470 100644 --- a/fs/f2fs/xattr.c +++ b/fs/f2fs/xattr.c @@ -217,12 +217,12 @@ static struct f2fs_xattr_entry *__find_xattr(void *base_addr, int index, return entry; } -static struct f2fs_xattr_entry *__find_inline_xattr(void *base_addr, - void **last_addr, int index, - size_t len, const char *name) +static struct f2fs_xattr_entry *__find_inline_xattr(struct inode *inode, + void *base_addr, void **last_addr, int index, + size_t len, const char *name) { struct f2fs_xattr_entry *entry; - unsigned int inline_size = F2FS_INLINE_XATTR_ADDRS << 2; + unsigned int inline_size = inline_xattr_size(inode); list_for_each_xattr(entry, base_addr) { if ((void *)entry + sizeof(__u32) > base_addr + inline_size || @@ -250,13 +250,13 @@ static int read_inline_xattr(struct inode *inode, struct page *ipage, void *inline_addr; if (ipage) { - inline_addr = inline_xattr_addr(ipage); + inline_addr = inline_xattr_addr(inode, ipage); } else { page = get_node_page(sbi, inode->i_ino); if (IS_ERR(page)) return PTR_ERR(page); - inline_addr = inline_xattr_addr(page); + inline_addr = inline_xattr_addr(inode, page); } memcpy(txattr_addr, inline_addr, inline_size); f2fs_put_page(page, 1); @@ -309,7 +309,7 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage, if (err) goto out; - *xe = __find_inline_xattr(txattr_addr, &last_addr, + *xe = __find_inline_xattr(inode, txattr_addr, &last_addr, index, len, name); if (*xe) goto check; @@ -404,7 +404,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, void *inline_addr; if (ipage) { - inline_addr = inline_xattr_addr(ipage); + inline_addr = inline_xattr_addr(inode, ipage); f2fs_wait_on_page_writeback(ipage, NODE, true); set_page_dirty(ipage); } else { @@ -413,7 +413,7 @@ static inline int write_all_xattrs(struct inode *inode, __u32 hsize, alloc_nid_failed(sbi, new_nid); return PTR_ERR(page); } - inline_addr = inline_xattr_addr(page); + inline_addr = inline_xattr_addr(inode, page); f2fs_wait_on_page_writeback(page, NODE, true); } memcpy(inline_addr, txattr_addr, inline_size); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 2a0c453d7235..50a8ee501bf1 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -184,7 +184,8 @@ struct f2fs_extent { } __packed; #define F2FS_NAME_LEN 255 -#define F2FS_INLINE_XATTR_ADDRS 50 /* 200 bytes for inline xattrs */ +/* 200 bytes for inline xattrs by default */ +#define DEFAULT_INLINE_XATTR_ADDRS 50 #define DEF_ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ #define CUR_ADDRS_PER_INODE(inode) (DEF_ADDRS_PER_INODE - \ get_extra_isize(inode)) @@ -238,7 +239,7 @@ struct f2fs_inode { union { struct { __le16 i_extra_isize; /* extra inode attribute size */ - __le16 i_padding; /* padding */ + __le16 i_inline_xattr_size; /* inline xattr size, unit: 4 bytes */ __le32 i_projid; /* project id */ __le32 i_inode_checksum;/* inode meta checksum */ __le32 i_extra_end[0]; /* for attribute size calculation */ -- cgit v1.2.3-59-g8ed1b From 234a9689614272d93271b308adbd303b59d266e3 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Thu, 5 Oct 2017 21:03:06 -0700 Subject: f2fs: add quota_ino feature infra This patch adds quota_ino feature infra to be used for quota files. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 6 ++++++ fs/f2fs/sysfs.c | 7 +++++++ include/linux/f2fs_fs.h | 6 +++++- 3 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 4a75f07f1dc8..9a1c7ffa6845 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -122,6 +122,7 @@ struct f2fs_mount_info { #define F2FS_FEATURE_PRJQUOTA 0x0010 #define F2FS_FEATURE_INODE_CHKSUM 0x0020 #define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040 +#define F2FS_FEATURE_QUOTA_INO 0x0080 #define F2FS_HAS_FEATURE(sb, mask) \ ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0) @@ -3070,6 +3071,11 @@ static inline int f2fs_sb_has_flexible_inline_xattr(struct super_block *sb) return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_FLEXIBLE_INLINE_XATTR); } +static inline int f2fs_sb_has_quota_ino(struct super_block *sb) +{ + return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_QUOTA_INO); +} + #ifdef CONFIG_BLK_DEV_ZONED static inline int get_blkz_type(struct f2fs_sb_info *sbi, struct block_device *bdev, block_t blkaddr) diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c index f0fdc89ce82f..9835348b6e5d 100644 --- a/fs/f2fs/sysfs.c +++ b/fs/f2fs/sysfs.c @@ -110,6 +110,9 @@ static ssize_t features_show(struct f2fs_attr *a, if (f2fs_sb_has_flexible_inline_xattr(sb)) len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len ? ", " : "", "flexible_inline_xattr"); + if (f2fs_sb_has_quota_ino(sb)) + len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", + len ? ", " : "", "quota_ino"); len += snprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } @@ -227,6 +230,7 @@ enum feat_id { FEAT_PROJECT_QUOTA, FEAT_INODE_CHECKSUM, FEAT_FLEXIBLE_INLINE_XATTR, + FEAT_QUOTA_INO, }; static ssize_t f2fs_feature_show(struct f2fs_attr *a, @@ -240,6 +244,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, case FEAT_PROJECT_QUOTA: case FEAT_INODE_CHECKSUM: case FEAT_FLEXIBLE_INLINE_XATTR: + case FEAT_QUOTA_INO: return snprintf(buf, PAGE_SIZE, "supported\n"); } return 0; @@ -314,6 +319,7 @@ F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR); F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA); F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR); +F2FS_FEATURE_RO_ATTR(quota_ino, FEAT_QUOTA_INO); #define ATTR_LIST(name) (&f2fs_attr_##name.attr) static struct attribute *f2fs_attrs[] = { @@ -364,6 +370,7 @@ static struct attribute *f2fs_feat_attrs[] = { ATTR_LIST(project_quota), ATTR_LIST(inode_checksum), ATTR_LIST(flexible_inline_xattr), + ATTR_LIST(quota_ino), NULL, }; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index 50a8ee501bf1..ce34007972c3 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -36,6 +36,9 @@ #define F2FS_NODE_INO(sbi) ((sbi)->node_ino_num) #define F2FS_META_INO(sbi) ((sbi)->meta_ino_num) +#define F2FS_QUOTA_INO 3 +#define F2FS_MAX_QUOTAS 3 + #define F2FS_IO_SIZE(sbi) (1 << (sbi)->write_io_size_bits) /* Blocks */ #define F2FS_IO_SIZE_KB(sbi) (1 << ((sbi)->write_io_size_bits + 2)) /* KB */ #define F2FS_IO_SIZE_BYTES(sbi) (1 << ((sbi)->write_io_size_bits + 12)) /* B */ @@ -108,7 +111,8 @@ struct f2fs_super_block { __u8 encryption_level; /* versioning level for encryption */ __u8 encrypt_pw_salt[16]; /* Salt used for string2key algorithm */ struct f2fs_device devs[MAX_DEVICES]; /* device list */ - __u8 reserved[327]; /* valid reserved region */ + __le32 qf_ino[F2FS_MAX_QUOTAS]; /* quota inode numbers */ + __u8 reserved[315]; /* valid reserved region */ } __packed; /* -- cgit v1.2.3-59-g8ed1b From ea6767337f86312ebe473585899a159bf50ef1b7 Mon Sep 17 00:00:00 2001 From: Jaegeuk Kim Date: Fri, 6 Oct 2017 09:14:28 -0700 Subject: f2fs: support quota sys files This patch supports hidden quota files in the system, which will be used for Android. It requires up-to-date f2fs-tools later than v1.9.0. Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/checkpoint.c | 9 ++- fs/f2fs/f2fs.h | 9 ++- fs/f2fs/recovery.c | 8 ++- fs/f2fs/super.c | 149 +++++++++++++++++++++++++++++++++++++++++++----- include/linux/f2fs_fs.h | 1 - 5 files changed, 157 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c index 6b52d4b66c7b..78e1b2998bbd 100644 --- a/fs/f2fs/checkpoint.c +++ b/fs/f2fs/checkpoint.c @@ -615,6 +615,9 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) block_t start_blk, orphan_blocks, i, j; unsigned int s_flags = sbi->sb->s_flags; int err = 0; +#ifdef CONFIG_QUOTA + int quota_enabled; +#endif if (!is_set_ckpt_flags(sbi, CP_ORPHAN_PRESENT_FLAG)) return 0; @@ -627,8 +630,9 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) #ifdef CONFIG_QUOTA /* Needed for iput() to work correctly and not trash data */ sbi->sb->s_flags |= MS_ACTIVE; + /* Turn on quotas so that they are updated correctly */ - f2fs_enable_quota_files(sbi); + quota_enabled = f2fs_enable_quota_files(sbi, s_flags & MS_RDONLY); #endif start_blk = __start_cp_addr(sbi) + 1 + __cp_payload(sbi); @@ -656,7 +660,8 @@ int recover_orphan_inodes(struct f2fs_sb_info *sbi) out: #ifdef CONFIG_QUOTA /* Turn quotas off */ - f2fs_quota_off_umount(sbi->sb); + if (quota_enabled) + f2fs_quota_off_umount(sbi->sb); #endif sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 9a1c7ffa6845..484b5c6ac36b 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -1384,6 +1384,13 @@ static inline unsigned long long cur_cp_version(struct f2fs_checkpoint *cp) return le64_to_cpu(cp->checkpoint_ver); } +static inline unsigned long f2fs_qf_ino(struct super_block *sb, int type) +{ + if (type < F2FS_MAX_QUOTAS) + return le32_to_cpu(F2FS_SB(sb)->raw_super->qf_ino[type]); + return 0; +} + static inline __u64 cur_cp_crc(struct f2fs_checkpoint *cp) { size_t crc_offset = le32_to_cpu(cp->checksum_offset); @@ -2526,7 +2533,7 @@ static inline int f2fs_add_link(struct dentry *dentry, struct inode *inode) */ int f2fs_inode_dirtied(struct inode *inode, bool sync); void f2fs_inode_synced(struct inode *inode); -void f2fs_enable_quota_files(struct f2fs_sb_info *sbi); +int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly); void f2fs_quota_off_umount(struct super_block *sb); int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover); int f2fs_sync_fs(struct super_block *sb, int sync); diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 9626758bc762..92c57ace1939 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -594,6 +594,9 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) int ret = 0; unsigned long s_flags = sbi->sb->s_flags; bool need_writecp = false; +#ifdef CONFIG_QUOTA + int quota_enabled; +#endif if (s_flags & MS_RDONLY) { f2fs_msg(sbi->sb, KERN_INFO, "orphan cleanup on readonly fs"); @@ -604,7 +607,7 @@ int recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only) /* Needed for iput() to work correctly and not trash data */ sbi->sb->s_flags |= MS_ACTIVE; /* Turn on quotas so that they are updated correctly */ - f2fs_enable_quota_files(sbi); + quota_enabled = f2fs_enable_quota_files(sbi, s_flags & MS_RDONLY); #endif fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", @@ -665,7 +668,8 @@ skip: out: #ifdef CONFIG_QUOTA /* Turn quotas off */ - f2fs_quota_off_umount(sbi->sb); + if (quota_enabled) + f2fs_quota_off_umount(sbi->sb); #endif sbi->sb->s_flags = s_flags; /* Restore MS_RDONLY status */ diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 96e145c34ba2..91eaa7b235eb 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -213,6 +213,12 @@ static int f2fs_set_qf_name(struct super_block *sb, int qtype, "quota options when quota turned on"); return -EINVAL; } + if (f2fs_sb_has_quota_ino(sb)) { + f2fs_msg(sb, KERN_INFO, + "QUOTA feature is enabled, so ignore qf_name"); + return 0; + } + qname = match_strdup(args); if (!qname) { f2fs_msg(sb, KERN_ERR, @@ -291,6 +297,18 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi) return -1; } } + + if (f2fs_sb_has_quota_ino(sbi->sb) && sbi->s_jquota_fmt) { + f2fs_msg(sbi->sb, KERN_INFO, + "QUOTA feature is enabled, so ignore jquota_fmt"); + sbi->s_jquota_fmt = 0; + } + if (f2fs_sb_has_quota_ino(sbi->sb) && sb_rdonly(sbi->sb)) { + f2fs_msg(sbi->sb, KERN_INFO, + "Filesystem with quota feature cannot be mounted RDWR " + "without CONFIG_QUOTA"); + return -1; + } return 0; } #endif @@ -1172,6 +1190,9 @@ static void default_options(struct f2fs_sb_info *sbi) #endif } +#ifdef CONFIG_QUOTA +static int f2fs_enable_quotas(struct super_block *sb); +#endif static int f2fs_remount(struct super_block *sb, int *flags, char *data) { struct f2fs_sb_info *sbi = F2FS_SB(sb); @@ -1238,6 +1259,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) if (f2fs_readonly(sb) && (*flags & MS_RDONLY)) goto skip; +#ifdef CONFIG_QUOTA if (!f2fs_readonly(sb) && (*flags & MS_RDONLY)) { err = dquot_suspend(sb, -1); if (err < 0) @@ -1245,9 +1267,15 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data) } else { /* dquot_resume needs RW */ sb->s_flags &= ~MS_RDONLY; - dquot_resume(sb, -1); + if (sb_any_quota_suspended(sb)) { + dquot_resume(sb, -1); + } else if (f2fs_sb_has_quota_ino(sb)) { + err = f2fs_enable_quotas(sb); + if (err) + goto restore_opts; + } } - +#endif /* disallow enable/disable extent_cache dynamically */ if (no_extent_cache == !!test_opt(sbi, EXTENT_CACHE)) { err = -EINVAL; @@ -1454,19 +1482,91 @@ static int f2fs_quota_on_mount(struct f2fs_sb_info *sbi, int type) sbi->s_jquota_fmt, type); } -void f2fs_enable_quota_files(struct f2fs_sb_info *sbi) +int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly) { - int i, ret; + int enabled = 0; + int i, err; + + if (f2fs_sb_has_quota_ino(sbi->sb) && rdonly) { + err = f2fs_enable_quotas(sbi->sb); + if (err) { + f2fs_msg(sbi->sb, KERN_ERR, + "Cannot turn on quota_ino: %d", err); + return 0; + } + return 1; + } for (i = 0; i < MAXQUOTAS; i++) { if (sbi->s_qf_names[i]) { - ret = f2fs_quota_on_mount(sbi, i); - if (ret < 0) - f2fs_msg(sbi->sb, KERN_ERR, - "Cannot turn on journaled " - "quota: error %d", ret); + err = f2fs_quota_on_mount(sbi, i); + if (!err) { + enabled = 1; + continue; + } + f2fs_msg(sbi->sb, KERN_ERR, + "Cannot turn on quotas: %d on %d", err, i); + } + } + return enabled; +} + +static int f2fs_quota_enable(struct super_block *sb, int type, int format_id, + unsigned int flags) +{ + struct inode *qf_inode; + unsigned long qf_inum; + int err; + + BUG_ON(!f2fs_sb_has_quota_ino(sb)); + + qf_inum = f2fs_qf_ino(sb, type); + if (!qf_inum) + return -EPERM; + + qf_inode = f2fs_iget(sb, qf_inum); + if (IS_ERR(qf_inode)) { + f2fs_msg(sb, KERN_ERR, + "Bad quota inode %u:%lu", type, qf_inum); + return PTR_ERR(qf_inode); + } + + /* Don't account quota for quota files to avoid recursion */ + qf_inode->i_flags |= S_NOQUOTA; + err = dquot_enable(qf_inode, type, format_id, flags); + iput(qf_inode); + return err; +} + +static int f2fs_enable_quotas(struct super_block *sb) +{ + int type, err = 0; + unsigned long qf_inum; + bool quota_mopt[MAXQUOTAS] = { + test_opt(F2FS_SB(sb), USRQUOTA), + test_opt(F2FS_SB(sb), GRPQUOTA), + test_opt(F2FS_SB(sb), PRJQUOTA), + }; + + sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NOLIST_DIRTY; + for (type = 0; type < MAXQUOTAS; type++) { + qf_inum = f2fs_qf_ino(sb, type); + if (qf_inum) { + err = f2fs_quota_enable(sb, type, QFMT_VFS_V1, + DQUOT_USAGE_ENABLED | + (quota_mopt[type] ? DQUOT_LIMITS_ENABLED : 0)); + if (err) { + f2fs_msg(sb, KERN_ERR, + "Failed to enable quota tracking " + "(type=%d, err=%d). Please run " + "fsck to fix.", type, err); + for (type--; type >= 0; type--) + dquot_quota_off(sb, type); + return err; + } } } + return 0; } static int f2fs_quota_sync(struct super_block *sb, int type) @@ -1537,7 +1637,7 @@ static int f2fs_quota_off(struct super_block *sb, int type) f2fs_quota_sync(sb, type); err = dquot_quota_off(sb, type); - if (err) + if (err || f2fs_sb_has_quota_ino(sb)) goto out_put; inode_lock(inode); @@ -2364,7 +2464,10 @@ try_onemore: #ifdef CONFIG_QUOTA sb->dq_op = &f2fs_quota_operations; - sb->s_qcop = &f2fs_quotactl_ops; + if (f2fs_sb_has_quota_ino(sb)) + sb->s_qcop = &dquot_quotactl_sysfile_ops; + else + sb->s_qcop = &f2fs_quotactl_ops; sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ; #endif @@ -2535,10 +2638,24 @@ try_onemore: if (err) goto free_root_inode; +#ifdef CONFIG_QUOTA + /* + * Turn on quotas which were not enabled for read-only mounts if + * filesystem has quota feature, so that they are updated correctly. + */ + if (f2fs_sb_has_quota_ino(sb) && !sb_rdonly(sb)) { + err = f2fs_enable_quotas(sb); + if (err) { + f2fs_msg(sb, KERN_ERR, + "Cannot turn on quotas: error %d", err); + goto free_sysfs; + } + } +#endif /* if there are nt orphan nodes free them */ err = recover_orphan_inodes(sbi); if (err) - goto free_sysfs; + goto free_meta; /* recover fsynced data */ if (!test_opt(sbi, DISABLE_ROLL_FORWARD)) { @@ -2572,7 +2689,7 @@ try_onemore: err = -EINVAL; f2fs_msg(sb, KERN_ERR, "Need to recover fsync data"); - goto free_sysfs; + goto free_meta; } } skip_recovery: @@ -2606,6 +2723,10 @@ skip_recovery: return 0; free_meta: +#ifdef CONFIG_QUOTA + if (f2fs_sb_has_quota_ino(sb) && !sb_rdonly(sb)) + f2fs_quota_off_umount(sbi->sb); +#endif f2fs_sync_inode_meta(sbi); /* * Some dirty meta pages can be produced by recover_orphan_inodes() @@ -2614,7 +2735,9 @@ free_meta: * falls into an infinite loop in sync_meta_pages(). */ truncate_inode_pages_final(META_MAPPING(sbi)); +#ifdef CONFIG_QUOTA free_sysfs: +#endif f2fs_unregister_sysfs(sbi); free_root_inode: dput(sb->s_root); diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index ce34007972c3..43e98d30d2df 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -36,7 +36,6 @@ #define F2FS_NODE_INO(sbi) ((sbi)->node_ino_num) #define F2FS_META_INO(sbi) ((sbi)->meta_ino_num) -#define F2FS_QUOTA_INO 3 #define F2FS_MAX_QUOTAS 3 #define F2FS_IO_SIZE(sbi) (1 << (sbi)->write_io_size_bits) /* Blocks */ -- cgit v1.2.3-59-g8ed1b From 08810a4119aaebf6318f209ec5dd9828e969cba4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 25 Oct 2017 14:12:29 +0200 Subject: PM / core: Add NEVER_SKIP and SMART_PREPARE driver flags The motivation for this change is to provide a way to work around a problem with the direct-complete mechanism used for avoiding system suspend/resume handling for devices in runtime suspend. The problem is that some middle layer code (the PCI bus type and the ACPI PM domain in particular) returns positive values from its system suspend ->prepare callbacks regardless of whether the driver's ->prepare returns a positive value or 0, which effectively prevents drivers from being able to control the direct-complete feature. Some drivers need that control, however, and the PCI bus type has grown its own flag to deal with this issue, but since it is not limited to PCI, it is better to address it by adding driver flags at the core level. To that end, add a driver_flags field to struct dev_pm_info for flags that can be set by device drivers at the probe time to inform the PM core and/or bus types, PM domains and so on on the capabilities and/or preferences of device drivers. Also add two static inline helpers for setting that field and testing it against a given set of flags and make the driver core clear it automatically on driver remove and probe failures. Define and document two PM driver flags related to the direct- complete feature: NEVER_SKIP and SMART_PREPARE that can be used, respectively, to indicate to the PM core that the direct-complete mechanism should never be used for the device and to inform the middle layer code (bus types, PM domains etc) that it can only request the PM core to use the direct-complete mechanism for the device (by returning a positive value from its ->prepare callback) if it also has been requested by the driver. While at it, make the core check pm_runtime_suspended() when setting power.direct_complete so that it doesn't need to be checked by ->prepare callbacks. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Acked-by: Bjorn Helgaas Reviewed-by: Ulf Hansson --- Documentation/driver-api/pm/devices.rst | 14 ++++++++++++++ Documentation/power/pci.txt | 19 +++++++++++++++++++ drivers/acpi/device_pm.c | 13 +++++++++---- drivers/base/dd.c | 2 ++ drivers/base/power/main.c | 4 +++- drivers/pci/pci-driver.c | 5 ++++- include/linux/device.h | 10 ++++++++++ include/linux/pm.h | 20 ++++++++++++++++++++ 8 files changed, 81 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/Documentation/driver-api/pm/devices.rst b/Documentation/driver-api/pm/devices.rst index 4a18ef9997c0..8add5b302a89 100644 --- a/Documentation/driver-api/pm/devices.rst +++ b/Documentation/driver-api/pm/devices.rst @@ -354,6 +354,20 @@ the phases are: ``prepare``, ``suspend``, ``suspend_late``, ``suspend_noirq``. is because all such devices are initially set to runtime-suspended with runtime PM disabled. + This feature also can be controlled by device drivers by using the + ``DPM_FLAG_NEVER_SKIP`` and ``DPM_FLAG_SMART_PREPARE`` driver power + management flags. [Typically, they are set at the time the driver is + probed against the device in question by passing them to the + :c:func:`dev_pm_set_driver_flags` helper function.] If the first of + these flags is set, the PM core will not apply the direct-complete + procedure described above to the given device and, consequenty, to any + of its ancestors. The second flag, when set, informs the middle layer + code (bus types, device types, PM domains, classes) that it should take + the return value of the ``->prepare`` callback provided by the driver + into account and it may only return a positive value from its own + ``->prepare`` callback if the driver's one also has returned a positive + value. + 2. The ``->suspend`` methods should quiesce the device to stop it from performing I/O. They also may save the device registers and put it into the appropriate low-power state, depending on the bus type the device is diff --git a/Documentation/power/pci.txt b/Documentation/power/pci.txt index a1b7f7158930..ab4e7d0540c1 100644 --- a/Documentation/power/pci.txt +++ b/Documentation/power/pci.txt @@ -961,6 +961,25 @@ dev_pm_ops to indicate that one suspend routine is to be pointed to by the .suspend(), .freeze(), and .poweroff() members and one resume routine is to be pointed to by the .resume(), .thaw(), and .restore() members. +3.1.19. Driver Flags for Power Management + +The PM core allows device drivers to set flags that influence the handling of +power management for the devices by the core itself and by middle layer code +including the PCI bus type. The flags should be set once at the driver probe +time with the help of the dev_pm_set_driver_flags() function and they should not +be updated directly afterwards. + +The DPM_FLAG_NEVER_SKIP flag prevents the PM core from using the direct-complete +mechanism allowing device suspend/resume callbacks to be skipped if the device +is in runtime suspend when the system suspend starts. That also affects all of +the ancestors of the device, so this flag should only be used if absolutely +necessary. + +The DPM_FLAG_SMART_PREPARE flag instructs the PCI bus type to only return a +positive value from pci_pm_prepare() if the ->prepare callback provided by the +driver of the device returns a positive value. That allows the driver to opt +out from using the direct-complete mechanism dynamically. + 3.2. Device Runtime Power Management ------------------------------------ In addition to providing device power management callbacks PCI device drivers diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 17e8eb93a76c..b4dcc6144e6b 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -959,11 +959,16 @@ static bool acpi_dev_needs_resume(struct device *dev, struct acpi_device *adev) int acpi_subsys_prepare(struct device *dev) { struct acpi_device *adev = ACPI_COMPANION(dev); - int ret; - ret = pm_generic_prepare(dev); - if (ret < 0) - return ret; + if (dev->driver && dev->driver->pm && dev->driver->pm->prepare) { + int ret = dev->driver->pm->prepare(dev); + + if (ret < 0) + return ret; + + if (!ret && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE)) + return 0; + } if (!adev || !pm_runtime_suspended(dev)) return 0; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index ad44b40fe284..45575e134696 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -464,6 +464,7 @@ pinctrl_bind_failed: if (dev->pm_domain && dev->pm_domain->dismiss) dev->pm_domain->dismiss(dev); pm_runtime_reinit(dev); + dev_pm_set_driver_flags(dev, 0); switch (ret) { case -EPROBE_DEFER: @@ -869,6 +870,7 @@ static void __device_release_driver(struct device *dev, struct device *parent) if (dev->pm_domain && dev->pm_domain->dismiss) dev->pm_domain->dismiss(dev); pm_runtime_reinit(dev); + dev_pm_set_driver_flags(dev, 0); klist_remove(&dev->p->knode_driver); device_pm_check_callbacks(dev); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 9bbbbb13a9db..c0135cd95ada 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1700,7 +1700,9 @@ unlock: * applies to suspend transitions, however. */ spin_lock_irq(&dev->power.lock); - dev->power.direct_complete = ret > 0 && state.event == PM_EVENT_SUSPEND; + dev->power.direct_complete = state.event == PM_EVENT_SUSPEND && + pm_runtime_suspended(dev) && ret > 0 && + !dev_pm_test_driver_flags(dev, DPM_FLAG_NEVER_SKIP); spin_unlock_irq(&dev->power.lock); return 0; } diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 11bd267fc137..68a32703b30a 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -689,8 +689,11 @@ static int pci_pm_prepare(struct device *dev) if (drv && drv->pm && drv->pm->prepare) { int error = drv->pm->prepare(dev); - if (error) + if (error < 0) return error; + + if (!error && dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_PREPARE)) + return 0; } return pci_dev_keep_suspended(to_pci_dev(dev)); } diff --git a/include/linux/device.h b/include/linux/device.h index c32e6f974d4a..fb9451599aca 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1070,6 +1070,16 @@ static inline void dev_pm_syscore_device(struct device *dev, bool val) #endif } +static inline void dev_pm_set_driver_flags(struct device *dev, u32 flags) +{ + dev->power.driver_flags = flags; +} + +static inline bool dev_pm_test_driver_flags(struct device *dev, u32 flags) +{ + return !!(dev->power.driver_flags & flags); +} + static inline void device_lock(struct device *dev) { mutex_lock(&dev->mutex); diff --git a/include/linux/pm.h b/include/linux/pm.h index a0ceeccf2846..f10bad831bfa 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -550,6 +550,25 @@ struct pm_subsys_data { #endif }; +/* + * Driver flags to control system suspend/resume behavior. + * + * These flags can be set by device drivers at the probe time. They need not be + * cleared by the drivers as the driver core will take care of that. + * + * NEVER_SKIP: Do not skip system suspend/resume callbacks for the device. + * SMART_PREPARE: Check the return value of the driver's ->prepare callback. + * + * Setting SMART_PREPARE instructs bus types and PM domains which may want + * system suspend/resume callbacks to be skipped for the device to return 0 from + * their ->prepare callbacks if the driver's ->prepare callback returns 0 (in + * other words, the system suspend/resume callbacks can only be skipped for the + * device if its driver doesn't object against that). This flag has no effect + * if NEVER_SKIP is set. + */ +#define DPM_FLAG_NEVER_SKIP BIT(0) +#define DPM_FLAG_SMART_PREPARE BIT(1) + struct dev_pm_info { pm_message_t power_state; unsigned int can_wakeup:1; @@ -561,6 +580,7 @@ struct dev_pm_info { bool is_late_suspended:1; bool early_init:1; /* Owned by the PM core */ bool direct_complete:1; /* Owned by the PM core */ + u32 driver_flags; spinlock_t lock; #ifdef CONFIG_PM_SLEEP struct list_head entry; -- cgit v1.2.3-59-g8ed1b From c2eac4d3a115e2f511844e7bcf73f4e877fbf5da Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 25 Oct 2017 14:16:46 +0200 Subject: PCI / PM: Use the NEVER_SKIP driver flag Replace the PCI-specific flag PCI_DEV_FLAGS_NEEDS_RESUME with the PM core's DPM_FLAG_NEVER_SKIP one everywhere and drop it. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Acked-by: Bjorn Helgaas Reviewed-by: Ulf Hansson --- drivers/gpu/drm/i915/i915_drv.c | 2 +- drivers/misc/mei/pci-me.c | 2 +- drivers/misc/mei/pci-txe.c | 2 +- drivers/pci/pci.c | 3 +-- include/linux/pci.h | 7 +------ 5 files changed, 5 insertions(+), 11 deletions(-) (limited to 'include') diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 9f45cfeae775..f124de3a0668 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1304,7 +1304,7 @@ int i915_driver_load(struct pci_dev *pdev, const struct pci_device_id *ent) * becaue the HDA driver may require us to enable the audio power * domain during system suspend. */ - pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME; + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP); ret = i915_driver_init_early(dev_priv, ent); if (ret < 0) diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 4ff40d319676..f17e4b435fa9 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -223,7 +223,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * MEI requires to resume from runtime suspend mode * in order to perform link reset flow upon system suspend. */ - pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME; + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP); /* * For not wake-able HW runtime pm framework diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index e38a5f144373..f911a08e3579 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -141,7 +141,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * MEI requires to resume from runtime suspend mode * in order to perform link reset flow upon system suspend. */ - pdev->dev_flags |= PCI_DEV_FLAGS_NEEDS_RESUME; + dev_pm_set_driver_flags(&pdev->dev, DPM_FLAG_NEVER_SKIP); /* * For not wake-able HW runtime pm framework diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6078dfc11b11..374f5686e2bc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2166,8 +2166,7 @@ bool pci_dev_keep_suspended(struct pci_dev *pci_dev) if (!pm_runtime_suspended(dev) || pci_target_state(pci_dev, wakeup) != pci_dev->current_state - || platform_pci_need_resume(pci_dev) - || (pci_dev->dev_flags & PCI_DEV_FLAGS_NEEDS_RESUME)) + || platform_pci_need_resume(pci_dev)) return false; /* diff --git a/include/linux/pci.h b/include/linux/pci.h index f4f8ee5a7362..4b65fa4fb94e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -205,13 +205,8 @@ enum pci_dev_flags { PCI_DEV_FLAGS_BRIDGE_XLATE_ROOT = (__force pci_dev_flags_t) (1 << 9), /* Do not use FLR even if device advertises PCI_AF_CAP */ PCI_DEV_FLAGS_NO_FLR_RESET = (__force pci_dev_flags_t) (1 << 10), - /* - * Resume before calling the driver's system suspend hooks, disabling - * the direct_complete optimization. - */ - PCI_DEV_FLAGS_NEEDS_RESUME = (__force pci_dev_flags_t) (1 << 11), /* Don't use Relaxed Ordering for TLPs directed at this device */ - PCI_DEV_FLAGS_NO_RELAXED_ORDERING = (__force pci_dev_flags_t) (1 << 12), + PCI_DEV_FLAGS_NO_RELAXED_ORDERING = (__force pci_dev_flags_t) (1 << 11), }; enum pci_irq_reroute_variant { -- cgit v1.2.3-59-g8ed1b From 0eab11c9ae3b3cc5dd76f20b81d0247647a6e96f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 26 Oct 2017 12:12:08 +0200 Subject: PM / core: Add SMART_SUSPEND driver flag Define and document a SMART_SUSPEND flag to instruct bus types and PM domains that the system suspend callbacks provided by the driver can cope with runtime-suspended devices, so from the driver's perspective it should be safe to leave devices in runtime suspend during system suspend. Setting that flag may also cause middle-layer code (bus types, PM domains etc.) to skip invocations of the ->suspend_late and ->suspend_noirq callbacks provided by the driver if the device is in runtime suspend at the beginning of the "late" phase of the system-wide suspend transition, in which case the driver's system-wide resume callbacks may be invoked back-to-back with its ->runtime_suspend callback, so the driver has to be able to cope with that too. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Reviewed-by: Ulf Hansson --- Documentation/driver-api/pm/devices.rst | 20 ++++++++++++++++++++ drivers/base/power/main.c | 3 +++ include/linux/pm.h | 8 ++++++++ 3 files changed, 31 insertions(+) (limited to 'include') diff --git a/Documentation/driver-api/pm/devices.rst b/Documentation/driver-api/pm/devices.rst index 8add5b302a89..574dadd06dec 100644 --- a/Documentation/driver-api/pm/devices.rst +++ b/Documentation/driver-api/pm/devices.rst @@ -766,6 +766,26 @@ the state of devices (possibly except for resuming them from runtime suspend) from their ``->prepare`` and ``->suspend`` callbacks (or equivalent) *before* invoking device drivers' ``->suspend`` callbacks (or equivalent). +Some bus types and PM domains have a policy to resume all devices from runtime +suspend upfront in their ``->suspend`` callbacks, but that may not be really +necessary if the driver of the device can cope with runtime-suspended devices. +The driver can indicate that by setting ``DPM_FLAG_SMART_SUSPEND`` in +:c:member:`power.driver_flags` at the probe time, by passing it to the +:c:func:`dev_pm_set_driver_flags` helper. That also may cause middle-layer code +(bus types, PM domains etc.) to skip the ``->suspend_late`` and +``->suspend_noirq`` callbacks provided by the driver if the device remains in +runtime suspend at the beginning of the ``suspend_late`` phase of system-wide +suspend (or in the ``poweroff_late`` phase of hibernation), when runtime PM +has been disabled for it, under the assumption that its state should not change +after that point until the system-wide transition is over. If that happens, the +driver's system-wide resume callbacks, if present, may still be invoked during +the subsequent system-wide resume transition and the device's runtime power +management status may be set to "active" before enabling runtime PM for it, +so the driver must be prepared to cope with the invocation of its system-wide +resume callbacks back-to-back with its ``->runtime_suspend`` one (without the +intervening ``->runtime_resume`` and so on) and the final state of the device +must reflect the "active" status for runtime PM in that case. + During system-wide resume from a sleep state it's easiest to put devices into the full-power state, as explained in :file:`Documentation/power/runtime_pm.txt`. Refer to that document for more information regarding this particular issue as diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index c0135cd95ada..8d9024017645 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1652,6 +1652,9 @@ static int device_prepare(struct device *dev, pm_message_t state) if (dev->power.syscore) return 0; + WARN_ON(dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) && + !pm_runtime_enabled(dev)); + /* * If a device's parent goes into runtime suspend at the wrong time, * it won't be possible to resume the device. To prevent this we diff --git a/include/linux/pm.h b/include/linux/pm.h index f10bad831bfa..43b5418e05bb 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -558,6 +558,7 @@ struct pm_subsys_data { * * NEVER_SKIP: Do not skip system suspend/resume callbacks for the device. * SMART_PREPARE: Check the return value of the driver's ->prepare callback. + * SMART_SUSPEND: No need to resume the device from runtime suspend. * * Setting SMART_PREPARE instructs bus types and PM domains which may want * system suspend/resume callbacks to be skipped for the device to return 0 from @@ -565,9 +566,16 @@ struct pm_subsys_data { * other words, the system suspend/resume callbacks can only be skipped for the * device if its driver doesn't object against that). This flag has no effect * if NEVER_SKIP is set. + * + * Setting SMART_SUSPEND instructs bus types and PM domains which may want to + * runtime resume the device upfront during system suspend that doing so is not + * necessary from the driver's perspective. It also may cause them to skip + * invocations of the ->suspend_late and ->suspend_noirq callbacks provided by + * the driver if they decide to leave the device in runtime suspend. */ #define DPM_FLAG_NEVER_SKIP BIT(0) #define DPM_FLAG_SMART_PREPARE BIT(1) +#define DPM_FLAG_SMART_SUSPEND BIT(2) struct dev_pm_info { pm_message_t power_state; -- cgit v1.2.3-59-g8ed1b From c4b65157aeefad29b2351a00a010e8c40ce7fd0e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 26 Oct 2017 12:12:22 +0200 Subject: PCI / PM: Take SMART_SUSPEND driver flag into account Make the PCI bus type take DPM_FLAG_SMART_SUSPEND into account in its system-wide PM callbacks and make sure that all code that should not run in parallel with pci_pm_runtime_resume() is executed in the "late" phases of system suspend, freeze and poweroff transitions. [Note that the pm_runtime_suspended() check in pci_dev_keep_suspended() is an optimization, because if is not passed, all of the subsequent checks may be skipped and some of them are much more overhead in general.] Also use the observation that if the device is in runtime suspend at the beginning of the "late" phase of a system-wide suspend-like transition, its state cannot change going forward (runtime PM is disabled for it at that time) until the transition is over and the subsequent system-wide PM callbacks should be skipped for it (as they generally assume the device to not be suspended), so add checks for that in pci_pm_suspend_late/noirq(), pci_pm_freeze_late/noirq() and pci_pm_poweroff_late/noirq(). Moreover, if pci_pm_resume_noirq() or pci_pm_restore_noirq() is called during the subsequent system-wide resume transition and if the device was left in runtime suspend previously, its runtime PM status needs to be changed to "active" as it is going to be put into the full-power state, so add checks for that too to these functions. In turn, if pci_pm_thaw_noirq() runs after the device has been left in runtime suspend, the subsequent "thaw" callbacks need to be skipped for it (as they may not work correctly with a suspended device), so set the power.direct_complete flag for the device then to make the PM core skip those callbacks. In addition to the above add a core helper for checking if DPM_FLAG_SMART_SUSPEND is set and the device runtime PM status is "suspended" at the same time, which is done quite often in the new code (and will be done elsewhere going forward too). Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Acked-by: Bjorn Helgaas --- Documentation/power/pci.txt | 14 ++++++ drivers/base/power/main.c | 6 +++ drivers/pci/pci-driver.c | 103 ++++++++++++++++++++++++++++++++++++-------- include/linux/pm.h | 2 + 4 files changed, 108 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/Documentation/power/pci.txt b/Documentation/power/pci.txt index ab4e7d0540c1..304162ea377e 100644 --- a/Documentation/power/pci.txt +++ b/Documentation/power/pci.txt @@ -980,6 +980,20 @@ positive value from pci_pm_prepare() if the ->prepare callback provided by the driver of the device returns a positive value. That allows the driver to opt out from using the direct-complete mechanism dynamically. +The DPM_FLAG_SMART_SUSPEND flag tells the PCI bus type that from the driver's +perspective the device can be safely left in runtime suspend during system +suspend. That causes pci_pm_suspend(), pci_pm_freeze() and pci_pm_poweroff() +to skip resuming the device from runtime suspend unless there are PCI-specific +reasons for doing that. Also, it causes pci_pm_suspend_late/noirq(), +pci_pm_freeze_late/noirq() and pci_pm_poweroff_late/noirq() to return early +if the device remains in runtime suspend in the beginning of the "late" phase +of the system-wide transition under way. Moreover, if the device is in +runtime suspend in pci_pm_resume_noirq() or pci_pm_restore_noirq(), its runtime +power management status will be changed to "active" (as it is going to be put +into D0 going forward), but if it is in runtime suspend in pci_pm_thaw_noirq(), +the function will set the power.direct_complete flag for it (to make the PM core +skip the subsequent "thaw" callbacks for it) and return. + 3.2. Device Runtime Power Management ------------------------------------ In addition to providing device power management callbacks PCI device drivers diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 8d9024017645..6c6f1c74c24c 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1861,3 +1861,9 @@ void device_pm_check_callbacks(struct device *dev) !dev->driver->suspend && !dev->driver->resume)); spin_unlock_irq(&dev->power.lock); } + +bool dev_pm_smart_suspend_and_suspended(struct device *dev) +{ + return dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) && + pm_runtime_status_suspended(dev); +} diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c1aeeb10539e..d19bd54d337e 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -734,18 +734,25 @@ static int pci_pm_suspend(struct device *dev) if (!pm) { pci_pm_default_suspend(pci_dev); - goto Fixup; + return 0; } /* - * PCI devices suspended at run time need to be resumed at this point, - * because in general it is necessary to reconfigure them for system - * suspend. Namely, if the device is supposed to wake up the system - * from the sleep state, we may need to reconfigure it for this purpose. - * In turn, if the device is not supposed to wake up the system from the - * sleep state, we'll have to prevent it from signaling wake-up. + * PCI devices suspended at run time may need to be resumed at this + * point, because in general it may be necessary to reconfigure them for + * system suspend. Namely, if the device is expected to wake up the + * system from the sleep state, it may have to be reconfigured for this + * purpose, or if the device is not expected to wake up the system from + * the sleep state, it should be prevented from signaling wakeup events + * going forward. + * + * Also if the driver of the device does not indicate that its system + * suspend callbacks can cope with runtime-suspended devices, it is + * better to resume the device from runtime suspend here. */ - pm_runtime_resume(dev); + if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) || + !pci_dev_keep_suspended(pci_dev)) + pm_runtime_resume(dev); pci_dev->state_saved = false; if (pm->suspend) { @@ -765,17 +772,27 @@ static int pci_pm_suspend(struct device *dev) } } - Fixup: - pci_fixup_device(pci_fixup_suspend, pci_dev); - return 0; } +static int pci_pm_suspend_late(struct device *dev) +{ + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev)); + + return pm_generic_suspend_late(dev); +} + static int pci_pm_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend_late(dev, PMSG_SUSPEND); @@ -834,6 +851,14 @@ static int pci_pm_resume_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + /* + * Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend + * during system suspend, so update their runtime PM status to "active" + * as they are going to be put into D0 shortly. + */ + if (dev_pm_smart_suspend_and_suspended(dev)) + pm_runtime_set_active(dev); + pci_pm_default_resume_early(pci_dev); if (pci_has_legacy_pm_support(pci_dev)) @@ -876,6 +901,7 @@ static int pci_pm_resume(struct device *dev) #else /* !CONFIG_SUSPEND */ #define pci_pm_suspend NULL +#define pci_pm_suspend_late NULL #define pci_pm_suspend_noirq NULL #define pci_pm_resume NULL #define pci_pm_resume_noirq NULL @@ -910,7 +936,8 @@ static int pci_pm_freeze(struct device *dev) * devices should not be touched during freeze/thaw transitions, * however. */ - pm_runtime_resume(dev); + if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND)) + pm_runtime_resume(dev); pci_dev->state_saved = false; if (pm->freeze) { @@ -925,11 +952,22 @@ static int pci_pm_freeze(struct device *dev) return 0; } +static int pci_pm_freeze_late(struct device *dev) +{ + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + return pm_generic_freeze_late(dev);; +} + static int pci_pm_freeze_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct device_driver *drv = dev->driver; + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend_late(dev, PMSG_FREEZE); @@ -959,6 +997,16 @@ static int pci_pm_thaw_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + /* + * If the device is in runtime suspend, the code below may not work + * correctly with it, so skip that code and make the PM core skip all of + * the subsequent "thaw" callbacks for the device. + */ + if (dev_pm_smart_suspend_and_suspended(dev)) { + dev->power.direct_complete = true; + return 0; + } + if (pcibios_pm_ops.thaw_noirq) { error = pcibios_pm_ops.thaw_noirq(dev); if (error) @@ -1008,11 +1056,13 @@ static int pci_pm_poweroff(struct device *dev) if (!pm) { pci_pm_default_suspend(pci_dev); - goto Fixup; + return 0; } /* The reason to do that is the same as in pci_pm_suspend(). */ - pm_runtime_resume(dev); + if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) || + !pci_dev_keep_suspended(pci_dev)) + pm_runtime_resume(dev); pci_dev->state_saved = false; if (pm->poweroff) { @@ -1024,17 +1074,27 @@ static int pci_pm_poweroff(struct device *dev) return error; } - Fixup: - pci_fixup_device(pci_fixup_suspend, pci_dev); - return 0; } +static int pci_pm_poweroff_late(struct device *dev) +{ + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev)); + + return pm_generic_poweroff_late(dev); +} + static int pci_pm_poweroff_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); struct device_driver *drv = dev->driver; + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + if (pci_has_legacy_pm_support(to_pci_dev(dev))) return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); @@ -1076,6 +1136,10 @@ static int pci_pm_restore_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + /* This is analogous to the pci_pm_resume_noirq() case. */ + if (dev_pm_smart_suspend_and_suspended(dev)) + pm_runtime_set_active(dev); + if (pcibios_pm_ops.restore_noirq) { error = pcibios_pm_ops.restore_noirq(dev); if (error) @@ -1124,10 +1188,12 @@ static int pci_pm_restore(struct device *dev) #else /* !CONFIG_HIBERNATE_CALLBACKS */ #define pci_pm_freeze NULL +#define pci_pm_freeze_late NULL #define pci_pm_freeze_noirq NULL #define pci_pm_thaw NULL #define pci_pm_thaw_noirq NULL #define pci_pm_poweroff NULL +#define pci_pm_poweroff_late NULL #define pci_pm_poweroff_noirq NULL #define pci_pm_restore NULL #define pci_pm_restore_noirq NULL @@ -1243,10 +1309,13 @@ static const struct dev_pm_ops pci_dev_pm_ops = { .prepare = pci_pm_prepare, .complete = pci_pm_complete, .suspend = pci_pm_suspend, + .suspend_late = pci_pm_suspend_late, .resume = pci_pm_resume, .freeze = pci_pm_freeze, + .freeze_late = pci_pm_freeze_late, .thaw = pci_pm_thaw, .poweroff = pci_pm_poweroff, + .poweroff_late = pci_pm_poweroff_late, .restore = pci_pm_restore, .suspend_noirq = pci_pm_suspend_noirq, .resume_noirq = pci_pm_resume_noirq, diff --git a/include/linux/pm.h b/include/linux/pm.h index 43b5418e05bb..65d39115f06d 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -765,6 +765,8 @@ extern int pm_generic_poweroff_late(struct device *dev); extern int pm_generic_poweroff(struct device *dev); extern void pm_generic_complete(struct device *dev); +extern bool dev_pm_smart_suspend_and_suspended(struct device *dev); + #else /* !CONFIG_PM_SLEEP */ #define device_pm_lock() do {} while (0) -- cgit v1.2.3-59-g8ed1b From 05087360fd7acf2cc9b7bbb243c12765c44c7693 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 27 Oct 2017 10:10:16 +0200 Subject: ACPI / PM: Take SMART_SUSPEND driver flag into account Make the ACPI PM domain take DPM_FLAG_SMART_SUSPEND into account in its system suspend callbacks. [Note that the pm_runtime_suspended() check in acpi_dev_needs_resume() is an optimization, because if is not passed, all of the subsequent checks may be skipped and some of them are much more overhead in general.] Also use the observation that if the device is in runtime suspend at the beginning of the "late" phase of a system-wide suspend-like transition, its state cannot change going forward (runtime PM is disabled for it at that time) until the transition is over and the subsequent system-wide PM callbacks should be skipped for it (as they generally assume the device to not be suspended), so add checks for that in acpi_subsys_suspend_late/noirq() and acpi_subsys_freeze_late/noirq(). Moreover, if acpi_subsys_resume_noirq() is called during the subsequent system-wide resume transition and if the device was left in runtime suspend previously, its runtime PM status needs to be changed to "active" as it is going to be put into the full-power state going forward, so add a check for that too in there. In turn, if acpi_subsys_thaw_noirq() runs after the device has been left in runtime suspend, the subsequent "thaw" callbacks need to be skipped for it (as they may not work correctly with a suspended device), so set the power.direct_complete flag for the device then to make the PM core skip those callbacks. On top of the above, make the analogous changes in the acpi_lpss driver that uses the ACPI PM domain callbacks. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman --- drivers/acpi/acpi_lpss.c | 13 +++++- drivers/acpi/device_pm.c | 113 +++++++++++++++++++++++++++++++++++++++++++---- include/linux/acpi.h | 10 +++++ 3 files changed, 126 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 04d32bdb5a95..de7385b824e1 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -849,8 +849,12 @@ static int acpi_lpss_resume(struct device *dev) #ifdef CONFIG_PM_SLEEP static int acpi_lpss_suspend_late(struct device *dev) { - int ret = pm_generic_suspend_late(dev); + int ret; + + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + ret = pm_generic_suspend_late(dev); return ret ? ret : acpi_lpss_suspend(dev, device_may_wakeup(dev)); } @@ -889,10 +893,17 @@ static struct dev_pm_domain acpi_lpss_pm_domain = { .complete = acpi_subsys_complete, .suspend = acpi_subsys_suspend, .suspend_late = acpi_lpss_suspend_late, + .suspend_noirq = acpi_subsys_suspend_noirq, + .resume_noirq = acpi_subsys_resume_noirq, .resume_early = acpi_lpss_resume_early, .freeze = acpi_subsys_freeze, + .freeze_late = acpi_subsys_freeze_late, + .freeze_noirq = acpi_subsys_freeze_noirq, + .thaw_noirq = acpi_subsys_thaw_noirq, .poweroff = acpi_subsys_suspend, .poweroff_late = acpi_lpss_suspend_late, + .poweroff_noirq = acpi_subsys_suspend_noirq, + .restore_noirq = acpi_subsys_resume_noirq, .restore_early = acpi_lpss_resume_early, #endif .runtime_suspend = acpi_lpss_runtime_suspend, diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index b4dcc6144e6b..3d6ec51d2bbc 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -936,7 +936,8 @@ static bool acpi_dev_needs_resume(struct device *dev, struct acpi_device *adev) u32 sys_target = acpi_target_system_state(); int ret, state; - if (device_may_wakeup(dev) != !!adev->wakeup.prepare_count) + if (!pm_runtime_suspended(dev) || !adev || + device_may_wakeup(dev) != !!adev->wakeup.prepare_count) return true; if (sys_target == ACPI_STATE_S0) @@ -970,9 +971,6 @@ int acpi_subsys_prepare(struct device *dev) return 0; } - if (!adev || !pm_runtime_suspended(dev)) - return 0; - return !acpi_dev_needs_resume(dev, adev); } EXPORT_SYMBOL_GPL(acpi_subsys_prepare); @@ -998,12 +996,17 @@ EXPORT_SYMBOL_GPL(acpi_subsys_complete); * acpi_subsys_suspend - Run the device driver's suspend callback. * @dev: Device to handle. * - * Follow PCI and resume devices suspended at run time before running their - * system suspend callbacks. + * Follow PCI and resume devices from runtime suspend before running their + * system suspend callbacks, unless the driver can cope with runtime-suspended + * devices during system suspend and there are no ACPI-specific reasons for + * resuming them. */ int acpi_subsys_suspend(struct device *dev) { - pm_runtime_resume(dev); + if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) || + acpi_dev_needs_resume(dev, ACPI_COMPANION(dev))) + pm_runtime_resume(dev); + return pm_generic_suspend(dev); } EXPORT_SYMBOL_GPL(acpi_subsys_suspend); @@ -1017,11 +1020,47 @@ EXPORT_SYMBOL_GPL(acpi_subsys_suspend); */ int acpi_subsys_suspend_late(struct device *dev) { - int ret = pm_generic_suspend_late(dev); + int ret; + + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + ret = pm_generic_suspend_late(dev); return ret ? ret : acpi_dev_suspend(dev, device_may_wakeup(dev)); } EXPORT_SYMBOL_GPL(acpi_subsys_suspend_late); +/** + * acpi_subsys_suspend_noirq - Run the device driver's "noirq" suspend callback. + * @dev: Device to suspend. + */ +int acpi_subsys_suspend_noirq(struct device *dev) +{ + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + return pm_generic_suspend_noirq(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_suspend_noirq); + +/** + * acpi_subsys_resume_noirq - Run the device driver's "noirq" resume callback. + * @dev: Device to handle. + */ +int acpi_subsys_resume_noirq(struct device *dev) +{ + /* + * Devices with DPM_FLAG_SMART_SUSPEND may be left in runtime suspend + * during system suspend, so update their runtime PM status to "active" + * as they will be put into D0 going forward. + */ + if (dev_pm_smart_suspend_and_suspended(dev)) + pm_runtime_set_active(dev); + + return pm_generic_resume_noirq(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_resume_noirq); + /** * acpi_subsys_resume_early - Resume device using ACPI. * @dev: Device to Resume. @@ -1049,11 +1088,60 @@ int acpi_subsys_freeze(struct device *dev) * runtime-suspended devices should not be touched during freeze/thaw * transitions. */ - pm_runtime_resume(dev); + if (!dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND)) + pm_runtime_resume(dev); + return pm_generic_freeze(dev); } EXPORT_SYMBOL_GPL(acpi_subsys_freeze); +/** + * acpi_subsys_freeze_late - Run the device driver's "late" freeze callback. + * @dev: Device to handle. + */ +int acpi_subsys_freeze_late(struct device *dev) +{ + + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + return pm_generic_freeze_late(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_freeze_late); + +/** + * acpi_subsys_freeze_noirq - Run the device driver's "noirq" freeze callback. + * @dev: Device to handle. + */ +int acpi_subsys_freeze_noirq(struct device *dev) +{ + + if (dev_pm_smart_suspend_and_suspended(dev)) + return 0; + + return pm_generic_freeze_noirq(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_freeze_noirq); + +/** + * acpi_subsys_thaw_noirq - Run the device driver's "noirq" thaw callback. + * @dev: Device to handle. + */ +int acpi_subsys_thaw_noirq(struct device *dev) +{ + /* + * If the device is in runtime suspend, the "thaw" code may not work + * correctly with it, so skip the driver callback and make the PM core + * skip all of the subsequent "thaw" callbacks for the device. + */ + if (dev_pm_smart_suspend_and_suspended(dev)) { + dev->power.direct_complete = true; + return 0; + } + + return pm_generic_thaw_noirq(dev); +} +EXPORT_SYMBOL_GPL(acpi_subsys_thaw_noirq); #endif /* CONFIG_PM_SLEEP */ static struct dev_pm_domain acpi_general_pm_domain = { @@ -1065,10 +1153,17 @@ static struct dev_pm_domain acpi_general_pm_domain = { .complete = acpi_subsys_complete, .suspend = acpi_subsys_suspend, .suspend_late = acpi_subsys_suspend_late, + .suspend_noirq = acpi_subsys_suspend_noirq, + .resume_noirq = acpi_subsys_resume_noirq, .resume_early = acpi_subsys_resume_early, .freeze = acpi_subsys_freeze, + .freeze_late = acpi_subsys_freeze_late, + .freeze_noirq = acpi_subsys_freeze_noirq, + .thaw_noirq = acpi_subsys_thaw_noirq, .poweroff = acpi_subsys_suspend, .poweroff_late = acpi_subsys_suspend_late, + .poweroff_noirq = acpi_subsys_suspend_noirq, + .restore_noirq = acpi_subsys_resume_noirq, .restore_early = acpi_subsys_resume_early, #endif }, diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 0ada2a948b44..dc1ebfeeb5ec 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -885,17 +885,27 @@ int acpi_dev_suspend_late(struct device *dev); int acpi_subsys_prepare(struct device *dev); void acpi_subsys_complete(struct device *dev); int acpi_subsys_suspend_late(struct device *dev); +int acpi_subsys_suspend_noirq(struct device *dev); +int acpi_subsys_resume_noirq(struct device *dev); int acpi_subsys_resume_early(struct device *dev); int acpi_subsys_suspend(struct device *dev); int acpi_subsys_freeze(struct device *dev); +int acpi_subsys_freeze_late(struct device *dev); +int acpi_subsys_freeze_noirq(struct device *dev); +int acpi_subsys_thaw_noirq(struct device *dev); #else static inline int acpi_dev_resume_early(struct device *dev) { return 0; } static inline int acpi_subsys_prepare(struct device *dev) { return 0; } static inline void acpi_subsys_complete(struct device *dev) {} static inline int acpi_subsys_suspend_late(struct device *dev) { return 0; } +static inline int acpi_subsys_suspend_noirq(struct device *dev) { return 0; } +static inline int acpi_subsys_resume_noirq(struct device *dev) { return 0; } static inline int acpi_subsys_resume_early(struct device *dev) { return 0; } static inline int acpi_subsys_suspend(struct device *dev) { return 0; } static inline int acpi_subsys_freeze(struct device *dev) { return 0; } +static inline int acpi_subsys_freeze_late(struct device *dev) { return 0; } +static inline int acpi_subsys_freeze_noirq(struct device *dev) { return 0; } +static inline int acpi_subsys_thaw_noirq(struct device *dev) { return 0; } #endif #ifdef CONFIG_ACPI -- cgit v1.2.3-59-g8ed1b From 8409a06f2a2c0baeb6e6ff020b2c5a4592b3078d Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Sat, 17 Jun 2017 01:09:19 -0700 Subject: KVM: arm/arm64: Make timer_arm and timer_disarm helpers more generic We are about to add an additional soft timer to the arch timer state for a VCPU and would like to be able to reuse the functions to program and cancel a timer, so we make them slightly more generic and rename to make it more clear that these functions work on soft timers and not the hardware resource that this code is managing. The armed flag on the timer state is only used to assert a condition, and we don't rely on this assertion in any meaningful way, so we can simply get rid of this flack and slightly reduce complexity. Acked-by: Marc Zyngier Signed-off-by: Christoffer Dall --- include/kvm/arm_arch_timer.h | 3 --- virt/kvm/arm/arch_timer.c | 33 +++++++++++---------------------- 2 files changed, 11 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index f0053f884b4a..d0beae98f755 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -48,9 +48,6 @@ struct arch_timer_cpu { /* Work queued with the above timer expires */ struct work_struct expired; - /* Background timer active */ - bool armed; - /* Is the timer enabled */ bool enabled; }; diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 8e89d63005c7..223230191195 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -56,26 +56,16 @@ u64 kvm_phys_timer_read(void) return timecounter->cc->read(timecounter->cc); } -static bool timer_is_armed(struct arch_timer_cpu *timer) +static void soft_timer_start(struct hrtimer *hrt, u64 ns) { - return timer->armed; -} - -/* timer_arm: as in "arm the timer", not as in ARM the company */ -static void timer_arm(struct arch_timer_cpu *timer, u64 ns) -{ - timer->armed = true; - hrtimer_start(&timer->timer, ktime_add_ns(ktime_get(), ns), + hrtimer_start(hrt, ktime_add_ns(ktime_get(), ns), HRTIMER_MODE_ABS); } -static void timer_disarm(struct arch_timer_cpu *timer) +static void soft_timer_cancel(struct hrtimer *hrt, struct work_struct *work) { - if (timer_is_armed(timer)) { - hrtimer_cancel(&timer->timer); - cancel_work_sync(&timer->expired); - timer->armed = false; - } + hrtimer_cancel(hrt); + cancel_work_sync(work); } static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) @@ -271,7 +261,7 @@ static void kvm_timer_emulate(struct kvm_vcpu *vcpu, return; /* The timer has not yet expired, schedule a background timer */ - timer_arm(timer, kvm_timer_compute_delta(timer_ctx)); + soft_timer_start(&timer->timer, kvm_timer_compute_delta(timer_ctx)); } /* @@ -285,8 +275,6 @@ void kvm_timer_schedule(struct kvm_vcpu *vcpu) struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); - BUG_ON(timer_is_armed(timer)); - /* * No need to schedule a background timer if any guest timer has * already expired, because kvm_vcpu_block will return before putting @@ -306,13 +294,14 @@ void kvm_timer_schedule(struct kvm_vcpu *vcpu) * The guest timers have not yet expired, schedule a background timer. * Set the earliest expiration time among the guest timers. */ - timer_arm(timer, kvm_timer_earliest_exp(vcpu)); + soft_timer_start(&timer->timer, kvm_timer_earliest_exp(vcpu)); } void kvm_timer_unschedule(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; - timer_disarm(timer); + + soft_timer_cancel(&timer->timer, &timer->expired); } static void kvm_timer_flush_hwstate_vgic(struct kvm_vcpu *vcpu) @@ -448,7 +437,7 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) * This is to cancel the background timer for the physical timer * emulation if it is set. */ - timer_disarm(timer); + soft_timer_cancel(&timer->timer, &timer->expired); /* * The guest could have modified the timer registers or the timer @@ -615,7 +604,7 @@ void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); - timer_disarm(timer); + soft_timer_cancel(&timer->timer, &timer->expired); kvm_vgic_unmap_phys_irq(vcpu, vtimer->irq.irq); } -- cgit v1.2.3-59-g8ed1b From 14d61fa98f03cb01f3aea7e3069fdf460caf5587 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Sat, 17 Jun 2017 07:33:02 -0700 Subject: KVM: arm/arm64: Rename soft timer to bg_timer As we are about to introduce a separate hrtimer for the physical timer, call this timer bg_timer, because we refer to this timer as the background timer in the code and comments elsewhere. Signed-off-by: Christoffer Dall Acked-by: Marc Zyngier --- include/kvm/arm_arch_timer.h | 2 +- virt/kvm/arm/arch_timer.c | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index d0beae98f755..0eed5bc9fd5e 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -43,7 +43,7 @@ struct arch_timer_cpu { struct arch_timer_context ptimer; /* Background timer used when the guest is not running */ - struct hrtimer timer; + struct hrtimer bg_timer; /* Work queued with the above timer expires */ struct work_struct expired; diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 223230191195..8ec392850e69 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -148,13 +148,13 @@ static u64 kvm_timer_earliest_exp(struct kvm_vcpu *vcpu) return min(min_virt, min_phys); } -static enum hrtimer_restart kvm_timer_expire(struct hrtimer *hrt) +static enum hrtimer_restart kvm_bg_timer_expire(struct hrtimer *hrt) { struct arch_timer_cpu *timer; struct kvm_vcpu *vcpu; u64 ns; - timer = container_of(hrt, struct arch_timer_cpu, timer); + timer = container_of(hrt, struct arch_timer_cpu, bg_timer); vcpu = container_of(timer, struct kvm_vcpu, arch.timer_cpu); /* @@ -261,7 +261,7 @@ static void kvm_timer_emulate(struct kvm_vcpu *vcpu, return; /* The timer has not yet expired, schedule a background timer */ - soft_timer_start(&timer->timer, kvm_timer_compute_delta(timer_ctx)); + soft_timer_start(&timer->bg_timer, kvm_timer_compute_delta(timer_ctx)); } /* @@ -294,14 +294,14 @@ void kvm_timer_schedule(struct kvm_vcpu *vcpu) * The guest timers have not yet expired, schedule a background timer. * Set the earliest expiration time among the guest timers. */ - soft_timer_start(&timer->timer, kvm_timer_earliest_exp(vcpu)); + soft_timer_start(&timer->bg_timer, kvm_timer_earliest_exp(vcpu)); } void kvm_timer_unschedule(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; - soft_timer_cancel(&timer->timer, &timer->expired); + soft_timer_cancel(&timer->bg_timer, &timer->expired); } static void kvm_timer_flush_hwstate_vgic(struct kvm_vcpu *vcpu) @@ -437,7 +437,7 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) * This is to cancel the background timer for the physical timer * emulation if it is set. */ - soft_timer_cancel(&timer->timer, &timer->expired); + soft_timer_cancel(&timer->bg_timer, &timer->expired); /* * The guest could have modified the timer registers or the timer @@ -494,8 +494,8 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) vcpu_ptimer(vcpu)->cntvoff = 0; INIT_WORK(&timer->expired, kvm_timer_inject_irq_work); - hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - timer->timer.function = kvm_timer_expire; + hrtimer_init(&timer->bg_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + timer->bg_timer.function = kvm_bg_timer_expire; vtimer->irq.irq = default_vtimer_irq.irq; ptimer->irq.irq = default_ptimer_irq.irq; @@ -604,7 +604,7 @@ void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); - soft_timer_cancel(&timer->timer, &timer->expired); + soft_timer_cancel(&timer->bg_timer, &timer->expired); kvm_vgic_unmap_phys_irq(vcpu, vtimer->irq.irq); } -- cgit v1.2.3-59-g8ed1b From f2a2129e0ac8d8fa79c3f85425c36f6e3368f022 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Sun, 18 Jun 2017 00:32:08 -0700 Subject: KVM: arm/arm64: Use separate timer for phys timer emulation We were using the same hrtimer for emulating the physical timer and for making sure a blocking VCPU thread would be eventually woken up. That worked fine in the previous arch timer design, but as we are about to actually use the soft timer expire function for the physical timer emulation, change the logic to use a dedicated hrtimer. This has the added benefit of not having to cancel any work in the sync path, which in turn allows us to run the flush and sync with IRQs disabled. Note that the hrtimer used to program the host kernel's timer to generate an exit from the guest when the emulated physical timer fires never has to inject any work, and to share the soft_timer_cancel() function with the bg_timer, we change the function to only cancel any pending work if the pointer to the work struct is not null. Acked-by: Marc Zyngier Signed-off-by: Christoffer Dall --- include/kvm/arm_arch_timer.h | 3 +++ virt/kvm/arm/arch_timer.c | 21 ++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index 0eed5bc9fd5e..184c3ef2df93 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -48,6 +48,9 @@ struct arch_timer_cpu { /* Work queued with the above timer expires */ struct work_struct expired; + /* Physical timer emulation */ + struct hrtimer phys_timer; + /* Is the timer enabled */ bool enabled; }; diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 8ec392850e69..4ad853737c34 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -65,7 +65,8 @@ static void soft_timer_start(struct hrtimer *hrt, u64 ns) static void soft_timer_cancel(struct hrtimer *hrt, struct work_struct *work) { hrtimer_cancel(hrt); - cancel_work_sync(work); + if (work) + cancel_work_sync(work); } static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) @@ -172,6 +173,12 @@ static enum hrtimer_restart kvm_bg_timer_expire(struct hrtimer *hrt) return HRTIMER_NORESTART; } +static enum hrtimer_restart kvm_phys_timer_expire(struct hrtimer *hrt) +{ + WARN(1, "Timer only used to ensure guest exit - unexpected event."); + return HRTIMER_NORESTART; +} + bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx) { u64 cval, now; @@ -249,7 +256,7 @@ static void kvm_timer_update_state(struct kvm_vcpu *vcpu) } /* Schedule the background timer for the emulated timer. */ -static void kvm_timer_emulate(struct kvm_vcpu *vcpu, +static void phys_timer_emulate(struct kvm_vcpu *vcpu, struct arch_timer_context *timer_ctx) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; @@ -261,7 +268,7 @@ static void kvm_timer_emulate(struct kvm_vcpu *vcpu, return; /* The timer has not yet expired, schedule a background timer */ - soft_timer_start(&timer->bg_timer, kvm_timer_compute_delta(timer_ctx)); + soft_timer_start(&timer->phys_timer, kvm_timer_compute_delta(timer_ctx)); } /* @@ -414,7 +421,7 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) kvm_timer_update_state(vcpu); /* Set the background timer for the physical timer emulation. */ - kvm_timer_emulate(vcpu, vcpu_ptimer(vcpu)); + phys_timer_emulate(vcpu, vcpu_ptimer(vcpu)); if (unlikely(!irqchip_in_kernel(vcpu->kvm))) kvm_timer_flush_hwstate_user(vcpu); @@ -437,7 +444,7 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) * This is to cancel the background timer for the physical timer * emulation if it is set. */ - soft_timer_cancel(&timer->bg_timer, &timer->expired); + soft_timer_cancel(&timer->phys_timer, NULL); /* * The guest could have modified the timer registers or the timer @@ -497,6 +504,9 @@ void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) hrtimer_init(&timer->bg_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); timer->bg_timer.function = kvm_bg_timer_expire; + hrtimer_init(&timer->phys_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + timer->phys_timer.function = kvm_phys_timer_expire; + vtimer->irq.irq = default_vtimer_irq.irq; ptimer->irq.irq = default_ptimer_irq.irq; } @@ -605,6 +615,7 @@ void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); soft_timer_cancel(&timer->bg_timer, &timer->expired); + soft_timer_cancel(&timer->phys_timer, NULL); kvm_vgic_unmap_phys_irq(vcpu, vtimer->irq.irq); } -- cgit v1.2.3-59-g8ed1b From b103cc3f10c06fb81faacd4ee6f88bbd21246073 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Sun, 16 Oct 2016 20:30:38 +0200 Subject: KVM: arm/arm64: Avoid timer save/restore in vcpu entry/exit We don't need to save and restore the hardware timer state and examine if it generates interrupts on on every entry/exit to the guest. The timer hardware is perfectly capable of telling us when it has expired by signaling interrupts. When taking a vtimer interrupt in the host, we don't want to mess with the timer configuration, we just want to forward the physical interrupt to the guest as a virtual interrupt. We can use the split priority drop and deactivate feature of the GIC to do this, which leaves an EOI'ed interrupt active on the physical distributor, making sure we don't keep taking timer interrupts which would prevent the guest from running. We can then forward the physical interrupt to the VM using the HW bit in the LR of the GIC, like we do already, which lets the guest directly deactivate both the physical and virtual timer simultaneously, allowing the timer hardware to exit the VM and generate a new physical interrupt when the timer output is again asserted later on. We do need to capture this state when migrating VCPUs between physical CPUs, however, which we use the vcpu put/load functions for, which are called through preempt notifiers whenever the thread is scheduled away from the CPU or called directly if we return from the ioctl to userspace. One caveat is that we have to save and restore the timer state in both kvm_timer_vcpu_[put/load] and kvm_timer_[schedule/unschedule], because we can have the following flows: 1. kvm_vcpu_block 2. kvm_timer_schedule 3. schedule 4. kvm_timer_vcpu_put (preempt notifier) 5. schedule (vcpu thread gets scheduled back) 6. kvm_timer_vcpu_load (preempt notifier) 7. kvm_timer_unschedule And a version where we don't actually call schedule: 1. kvm_vcpu_block 2. kvm_timer_schedule 7. kvm_timer_unschedule Since kvm_timer_[schedule/unschedule] may not be followed by put/load, but put/load also may be called independently, we call the timer save/restore functions from both paths. Since they rely on the loaded flag to never save/restore when unnecessary, this doesn't cause any harm, and we ensure that all invokations of either set of functions work as intended. An added benefit beyond not having to read and write the timer sysregs on every entry and exit is that we no longer have to actively write the active state to the physical distributor, because we configured the irq for the vtimer to only get a priority drop when handling the interrupt in the GIC driver (we called irq_set_vcpu_affinity()), and the interrupt stays active after firing on the host. Reviewed-by: Marc Zyngier Signed-off-by: Christoffer Dall --- include/kvm/arm_arch_timer.h | 16 ++- virt/kvm/arm/arch_timer.c | 237 +++++++++++++++++++++++++++---------------- virt/kvm/arm/arm.c | 19 +++- 3 files changed, 178 insertions(+), 94 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index 184c3ef2df93..c538f707e1c1 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -31,8 +31,15 @@ struct arch_timer_context { /* Timer IRQ */ struct kvm_irq_level irq; - /* Active IRQ state caching */ - bool active_cleared_last; + /* + * We have multiple paths which can save/restore the timer state + * onto the hardware, so we need some way of keeping track of + * where the latest state is. + * + * loaded == true: State is loaded on the hardware registers. + * loaded == false: State is stored in memory. + */ + bool loaded; /* Virtual offset */ u64 cntvoff; @@ -78,10 +85,15 @@ void kvm_timer_unschedule(struct kvm_vcpu *vcpu); u64 kvm_phys_timer_read(void); +void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu); void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu); void kvm_timer_init_vhe(void); #define vcpu_vtimer(v) (&(v)->arch.timer_cpu.vtimer) #define vcpu_ptimer(v) (&(v)->arch.timer_cpu.ptimer) + +void enable_el1_phys_timer_access(void); +void disable_el1_phys_timer_access(void); + #endif diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index eac1b3d83a86..ec685c1f3b78 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -46,10 +46,9 @@ static const struct kvm_irq_level default_vtimer_irq = { .level = 1, }; -void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu) -{ - vcpu_vtimer(vcpu)->active_cleared_last = false; -} +static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx); +static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, + struct arch_timer_context *timer_ctx); u64 kvm_phys_timer_read(void) { @@ -69,17 +68,45 @@ static void soft_timer_cancel(struct hrtimer *hrt, struct work_struct *work) cancel_work_sync(work); } -static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) +static void kvm_vtimer_update_mask_user(struct kvm_vcpu *vcpu) { - struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id; + struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); /* - * We disable the timer in the world switch and let it be - * handled by kvm_timer_sync_hwstate(). Getting a timer - * interrupt at this point is a sure sign of some major - * breakage. + * When using a userspace irqchip with the architected timers, we must + * prevent continuously exiting from the guest, and therefore mask the + * physical interrupt by disabling it on the host interrupt controller + * when the virtual level is high, such that the guest can make + * forward progress. Once we detect the output level being + * de-asserted, we unmask the interrupt again so that we exit from the + * guest when the timer fires. */ - pr_warn("Unexpected interrupt %d on vcpu %p\n", irq, vcpu); + if (vtimer->irq.level) + disable_percpu_irq(host_vtimer_irq); + else + enable_percpu_irq(host_vtimer_irq, 0); +} + +static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) +{ + struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id; + struct arch_timer_context *vtimer; + + if (!vcpu) { + pr_warn_once("Spurious arch timer IRQ on non-VCPU thread\n"); + return IRQ_NONE; + } + vtimer = vcpu_vtimer(vcpu); + + if (!vtimer->irq.level) { + vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl); + if (kvm_timer_irq_can_fire(vtimer)) + kvm_timer_update_irq(vcpu, true, vtimer); + } + + if (unlikely(!irqchip_in_kernel(vcpu->kvm))) + kvm_vtimer_update_mask_user(vcpu); + return IRQ_HANDLED; } @@ -215,7 +242,6 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, { int ret; - timer_ctx->active_cleared_last = false; timer_ctx->irq.level = new_level; trace_kvm_timer_update_irq(vcpu->vcpu_id, timer_ctx->irq.irq, timer_ctx->irq.level); @@ -271,10 +297,16 @@ static void phys_timer_emulate(struct kvm_vcpu *vcpu, soft_timer_start(&timer->phys_timer, kvm_timer_compute_delta(timer_ctx)); } -static void timer_save_state(struct kvm_vcpu *vcpu) +static void vtimer_save_state(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); + unsigned long flags; + + local_irq_save(flags); + + if (!vtimer->loaded) + goto out; if (timer->enabled) { vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl); @@ -283,6 +315,10 @@ static void timer_save_state(struct kvm_vcpu *vcpu) /* Disable the virtual timer */ write_sysreg_el0(0, cntv_ctl); + + vtimer->loaded = false; +out: + local_irq_restore(flags); } /* @@ -296,6 +332,8 @@ void kvm_timer_schedule(struct kvm_vcpu *vcpu) struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); + vtimer_save_state(vcpu); + /* * No need to schedule a background timer if any guest timer has * already expired, because kvm_vcpu_block will return before putting @@ -318,22 +356,34 @@ void kvm_timer_schedule(struct kvm_vcpu *vcpu) soft_timer_start(&timer->bg_timer, kvm_timer_earliest_exp(vcpu)); } -static void timer_restore_state(struct kvm_vcpu *vcpu) +static void vtimer_restore_state(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); + unsigned long flags; + + local_irq_save(flags); + + if (vtimer->loaded) + goto out; if (timer->enabled) { write_sysreg_el0(vtimer->cnt_cval, cntv_cval); isb(); write_sysreg_el0(vtimer->cnt_ctl, cntv_ctl); } + + vtimer->loaded = true; +out: + local_irq_restore(flags); } void kvm_timer_unschedule(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; + vtimer_restore_state(vcpu); + soft_timer_cancel(&timer->bg_timer, &timer->expired); } @@ -352,61 +402,45 @@ static void set_cntvoff(u64 cntvoff) kvm_call_hyp(__kvm_timer_set_cntvoff, low, high); } -static void kvm_timer_flush_hwstate_vgic(struct kvm_vcpu *vcpu) +static void kvm_timer_vcpu_load_vgic(struct kvm_vcpu *vcpu) { struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); bool phys_active; int ret; - /* - * If we enter the guest with the virtual input level to the VGIC - * asserted, then we have already told the VGIC what we need to, and - * we don't need to exit from the guest until the guest deactivates - * the already injected interrupt, so therefore we should set the - * hardware active state to prevent unnecessary exits from the guest. - * - * Also, if we enter the guest with the virtual timer interrupt active, - * then it must be active on the physical distributor, because we set - * the HW bit and the guest must be able to deactivate the virtual and - * physical interrupt at the same time. - * - * Conversely, if the virtual input level is deasserted and the virtual - * interrupt is not active, then always clear the hardware active state - * to ensure that hardware interrupts from the timer triggers a guest - * exit. - */ phys_active = vtimer->irq.level || - kvm_vgic_map_is_active(vcpu, vtimer->irq.irq); - - /* - * We want to avoid hitting the (re)distributor as much as - * possible, as this is a potentially expensive MMIO access - * (not to mention locks in the irq layer), and a solution for - * this is to cache the "active" state in memory. - * - * Things to consider: we cannot cache an "active set" state, - * because the HW can change this behind our back (it becomes - * "clear" in the HW). We must then restrict the caching to - * the "clear" state. - * - * The cache is invalidated on: - * - vcpu put, indicating that the HW cannot be trusted to be - * in a sane state on the next vcpu load, - * - any change in the interrupt state - * - * Usage conditions: - * - cached value is "active clear" - * - value to be programmed is "active clear" - */ - if (vtimer->active_cleared_last && !phys_active) - return; + kvm_vgic_map_is_active(vcpu, vtimer->irq.irq); ret = irq_set_irqchip_state(host_vtimer_irq, IRQCHIP_STATE_ACTIVE, phys_active); WARN_ON(ret); +} - vtimer->active_cleared_last = !phys_active; +static void kvm_timer_vcpu_load_user(struct kvm_vcpu *vcpu) +{ + kvm_vtimer_update_mask_user(vcpu); +} + +void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu) +{ + struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; + struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); + + if (unlikely(!timer->enabled)) + return; + + if (unlikely(!irqchip_in_kernel(vcpu->kvm))) + kvm_timer_vcpu_load_user(vcpu); + else + kvm_timer_vcpu_load_vgic(vcpu); + + set_cntvoff(vtimer->cntvoff); + + vtimer_restore_state(vcpu); + + if (has_vhe()) + disable_el1_phys_timer_access(); } bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu) @@ -426,23 +460,6 @@ bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu) ptimer->irq.level != plevel; } -static void kvm_timer_flush_hwstate_user(struct kvm_vcpu *vcpu) -{ - struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); - - /* - * To prevent continuously exiting from the guest, we mask the - * physical interrupt such that the guest can make forward progress. - * Once we detect the output level being deasserted, we unmask the - * interrupt again so that we exit from the guest when the timer - * fires. - */ - if (vtimer->irq.level) - disable_percpu_irq(host_vtimer_irq); - else - enable_percpu_irq(host_vtimer_irq, 0); -} - /** * kvm_timer_flush_hwstate - prepare timers before running the vcpu * @vcpu: The vcpu pointer @@ -455,23 +472,61 @@ static void kvm_timer_flush_hwstate_user(struct kvm_vcpu *vcpu) void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; - struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); + struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); if (unlikely(!timer->enabled)) return; - kvm_timer_update_state(vcpu); + if (kvm_timer_should_fire(ptimer) != ptimer->irq.level) + kvm_timer_update_irq(vcpu, !ptimer->irq.level, ptimer); /* Set the background timer for the physical timer emulation. */ phys_timer_emulate(vcpu, vcpu_ptimer(vcpu)); +} - if (unlikely(!irqchip_in_kernel(vcpu->kvm))) - kvm_timer_flush_hwstate_user(vcpu); - else - kvm_timer_flush_hwstate_vgic(vcpu); +void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu) +{ + struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; - set_cntvoff(vtimer->cntvoff); - timer_restore_state(vcpu); + if (unlikely(!timer->enabled)) + return; + + if (has_vhe()) + enable_el1_phys_timer_access(); + + vtimer_save_state(vcpu); + + /* + * The kernel may decide to run userspace after calling vcpu_put, so + * we reset cntvoff to 0 to ensure a consistent read between user + * accesses to the virtual counter and kernel access to the physical + * counter. + */ + set_cntvoff(0); +} + +static void unmask_vtimer_irq(struct kvm_vcpu *vcpu) +{ + struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); + + if (unlikely(!irqchip_in_kernel(vcpu->kvm))) { + kvm_vtimer_update_mask_user(vcpu); + return; + } + + /* + * If the guest disabled the timer without acking the interrupt, then + * we must make sure the physical and virtual active states are in + * sync by deactivating the physical interrupt, because otherwise we + * wouldn't see the next timer interrupt in the host. + */ + if (!kvm_vgic_map_is_active(vcpu, vtimer->irq.irq)) { + int ret; + ret = irq_set_irqchip_state(host_vtimer_irq, + IRQCHIP_STATE_ACTIVE, + false); + WARN_ON(ret); + } } /** @@ -484,6 +539,7 @@ void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; + struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); /* * This is to cancel the background timer for the physical timer @@ -491,14 +547,19 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) */ soft_timer_cancel(&timer->phys_timer, NULL); - timer_save_state(vcpu); - set_cntvoff(0); - /* - * The guest could have modified the timer registers or the timer - * could have expired, update the timer state. + * If we entered the guest with the vtimer output asserted we have to + * check if the guest has modified the timer so that we should lower + * the line at this point. */ - kvm_timer_update_state(vcpu); + if (vtimer->irq.level) { + vtimer->cnt_ctl = read_sysreg_el0(cntv_ctl); + vtimer->cnt_cval = read_sysreg_el0(cntv_cval); + if (!kvm_timer_should_fire(vtimer)) { + kvm_timer_update_irq(vcpu, false, vtimer); + unmask_vtimer_irq(vcpu); + } + } } int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu) diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c index 27db222a0c8d..132d39ae13d2 100644 --- a/virt/kvm/arm/arm.c +++ b/virt/kvm/arm/arm.c @@ -354,18 +354,18 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state); kvm_arm_set_running_vcpu(vcpu); - kvm_vgic_load(vcpu); + kvm_timer_vcpu_load(vcpu); } void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) { + kvm_timer_vcpu_put(vcpu); kvm_vgic_put(vcpu); vcpu->cpu = -1; kvm_arm_set_running_vcpu(NULL); - kvm_timer_vcpu_put(vcpu); } static void vcpu_power_off(struct kvm_vcpu *vcpu) @@ -710,15 +710,26 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) kvm_arm_clear_debug(vcpu); /* - * We must sync the PMU and timer state before the vgic state so + * We must sync the PMU state before the vgic state so * that the vgic can properly sample the updated state of the * interrupt line. */ kvm_pmu_sync_hwstate(vcpu); - kvm_timer_sync_hwstate(vcpu); + /* + * Sync the vgic state before syncing the timer state because + * the timer code needs to know if the virtual timer + * interrupts are active. + */ kvm_vgic_sync_hwstate(vcpu); + /* + * Sync the timer hardware state before enabling interrupts as + * we don't want vtimer interrupts to race with syncing the + * timer virtual interrupt state. + */ + kvm_timer_sync_hwstate(vcpu); + /* * We may have taken a host interrupt in HYP mode (ie * while executing the guest). This interrupt is still -- cgit v1.2.3-59-g8ed1b From 7e90c8e5704cbb299d48e7debb1e61614cb12f41 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Tue, 20 Jun 2017 07:56:20 -0700 Subject: KVM: arm/arm64: Get rid of kvm_timer_flush_hwstate Now when both the vtimer and the ptimer when using both the in-kernel vgic emulation and a userspace IRQ chip are driven by the timer signals and at the vcpu load/put boundaries, instead of recomputing the timer state at every entry/exit to/from the guest, we can get entirely rid of the flush hwstate function. Signed-off-by: Christoffer Dall Acked-by: Marc Zyngier --- include/kvm/arm_arch_timer.h | 1 - virt/kvm/arm/arch_timer.c | 24 ------------------------ virt/kvm/arm/arm.c | 1 - 3 files changed, 26 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index c538f707e1c1..2352f3a4e88b 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -66,7 +66,6 @@ int kvm_timer_hyp_init(void); int kvm_timer_enable(struct kvm_vcpu *vcpu); int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu); void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu); -void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu); void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu); bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu); void kvm_timer_update_run(struct kvm_vcpu *vcpu); diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 20e56c420632..53d9bd4a734f 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -305,12 +305,6 @@ static void kvm_timer_update_state(struct kvm_vcpu *vcpu) struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); - /* - * If userspace modified the timer registers via SET_ONE_REG before - * the vgic was initialized, we mustn't set the vtimer->irq.level value - * because the guest would never see the interrupt. Instead wait - * until we call this function from kvm_timer_flush_hwstate. - */ if (unlikely(!timer->enabled)) return; @@ -489,24 +483,6 @@ bool kvm_timer_should_notify_user(struct kvm_vcpu *vcpu) ptimer->irq.level != plevel; } -/** - * kvm_timer_flush_hwstate - prepare timers before running the vcpu - * @vcpu: The vcpu pointer - * - * Check if the virtual timer has expired while we were running in the host, - * and inject an interrupt if that was the case, making sure the timer is - * masked or disabled on the host so that we keep executing. Also schedule a - * software timer for the physical timer if it is enabled. - */ -void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) -{ - struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; - struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); - - if (unlikely(!timer->enabled)) - return; -} - void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu) { struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c index 132d39ae13d2..14c50d142c67 100644 --- a/virt/kvm/arm/arm.c +++ b/virt/kvm/arm/arm.c @@ -656,7 +656,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) local_irq_disable(); - kvm_timer_flush_hwstate(vcpu); kvm_vgic_flush_hwstate(vcpu); /* -- cgit v1.2.3-59-g8ed1b From 1c88ab7ec8c53c4d806bb2b6871ddafdebbffa8b Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Fri, 6 Jan 2017 16:07:48 +0100 Subject: KVM: arm/arm64: Rework kvm_timer_should_fire kvm_timer_should_fire() can be called in two different situations from the kvm_vcpu_block(). The first case is before calling kvm_timer_schedule(), used for wait polling, and in this case the VCPU thread is running and the timer state is loaded onto the hardware so all we have to do is check if the virtual interrupt lines are asserted, becasue the timer interrupt handler functions will raise those lines as appropriate. The second case is inside the wait loop of kvm_vcpu_block(), where we have already called kvm_timer_schedule() and therefore the hardware will be disabled and the software view of the timer state is up to date (timer->loaded is false), and so we can simply check if the timer should fire by looking at the software state. Signed-off-by: Christoffer Dall Reviewed-by: Marc Zyngier --- include/kvm/arm_arch_timer.h | 3 ++- virt/kvm/arm/arch_timer.c | 22 +++++++++++++++++++++- virt/kvm/arm/arm.c | 3 +-- 3 files changed, 24 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h index 2352f3a4e88b..01ee473517e2 100644 --- a/include/kvm/arm_arch_timer.h +++ b/include/kvm/arm_arch_timer.h @@ -78,7 +78,8 @@ int kvm_arm_timer_set_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr); int kvm_arm_timer_get_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr); int kvm_arm_timer_has_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr); -bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx); +bool kvm_timer_is_pending(struct kvm_vcpu *vcpu); + void kvm_timer_schedule(struct kvm_vcpu *vcpu); void kvm_timer_unschedule(struct kvm_vcpu *vcpu); diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c index 53d9bd4a734f..2035cf251701 100644 --- a/virt/kvm/arm/arch_timer.c +++ b/virt/kvm/arm/arch_timer.c @@ -49,6 +49,7 @@ static const struct kvm_irq_level default_vtimer_irq = { static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx); static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, struct arch_timer_context *timer_ctx); +static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx); u64 kvm_phys_timer_read(void) { @@ -226,7 +227,7 @@ static enum hrtimer_restart kvm_phys_timer_expire(struct hrtimer *hrt) return HRTIMER_NORESTART; } -bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx) +static bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx) { u64 cval, now; @@ -239,6 +240,25 @@ bool kvm_timer_should_fire(struct arch_timer_context *timer_ctx) return cval <= now; } +bool kvm_timer_is_pending(struct kvm_vcpu *vcpu) +{ + struct arch_timer_context *vtimer = vcpu_vtimer(vcpu); + struct arch_timer_context *ptimer = vcpu_ptimer(vcpu); + + if (vtimer->irq.level || ptimer->irq.level) + return true; + + /* + * When this is called from withing the wait loop of kvm_vcpu_block(), + * the software view of the timer state is up to date (timer->loaded + * is false), and so we can simply check if the timer should fire now. + */ + if (!vtimer->loaded && kvm_timer_should_fire(vtimer)) + return true; + + return kvm_timer_should_fire(ptimer); +} + /* * Reflect the timer output level into the kvm_run structure */ diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c index 14c50d142c67..bc126fb99a3d 100644 --- a/virt/kvm/arm/arm.c +++ b/virt/kvm/arm/arm.c @@ -307,8 +307,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu) { - return kvm_timer_should_fire(vcpu_vtimer(vcpu)) || - kvm_timer_should_fire(vcpu_ptimer(vcpu)); + return kvm_timer_is_pending(vcpu); } void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) -- cgit v1.2.3-59-g8ed1b From 5caaed151a68ae36aca2981cc245f5960a0a7603 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 2 Nov 2017 19:41:09 +0100 Subject: netfilter: conntrack: don't cache nlattr_tuple_size result in nla_size We currently call ->nlattr_tuple_size() once at register time and cache result in l4proto->nla_size. nla_size is the only member that is written to, avoiding this would allow to make l4proto trackers const. We can use ->nlattr_tuple_size() at run time, and cache result in the individual trackers instead. This is an intermediate step, next patch removes nlattr_size() callback and computes size at compile time, then removes nla_size. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 4 ++-- net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 9 +++++++-- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 9 +++++++-- net/netfilter/nf_conntrack_core.c | 9 +++++++-- net/netfilter/nf_conntrack_netlink.c | 10 +++++++--- net/netfilter/nf_conntrack_proto.c | 2 -- net/netfilter/nf_conntrack_proto_tcp.c | 9 +++++++-- 7 files changed, 37 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index e06518874144..46e786ffcf2f 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -74,7 +74,7 @@ struct nf_conntrack_l4proto { int (*tuple_to_nlattr)(struct sk_buff *skb, const struct nf_conntrack_tuple *t); /* Calculate tuple nlattr size */ - int (*nlattr_tuple_size)(void); + unsigned int (*nlattr_tuple_size)(void); int (*nlattr_to_tuple)(struct nlattr *tb[], struct nf_conntrack_tuple *t); const struct nla_policy *nla_policy; @@ -144,7 +144,7 @@ int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple); int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[], struct nf_conntrack_tuple *t); -int nf_ct_port_nlattr_tuple_size(void); +unsigned int nf_ct_port_nlattr_tuple_size(void); extern const struct nla_policy nf_ct_port_nla_policy[]; #ifdef CONFIG_SYSCTL diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 8969420cecc3..1849fedd9b81 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -258,9 +258,14 @@ static int icmp_nlattr_to_tuple(struct nlattr *tb[], return 0; } -static int icmp_nlattr_tuple_size(void) +static unsigned int icmp_nlattr_tuple_size(void) { - return nla_policy_len(icmp_nla_policy, CTA_PROTO_MAX + 1); + static unsigned int size __read_mostly; + + if (!size) + size = nla_policy_len(icmp_nla_policy, CTA_PROTO_MAX + 1); + + return size; } #endif diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index dca921df28e1..3ac0d826afc4 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -259,9 +259,14 @@ static int icmpv6_nlattr_to_tuple(struct nlattr *tb[], return 0; } -static int icmpv6_nlattr_tuple_size(void) +static unsigned int icmpv6_nlattr_tuple_size(void) { - return nla_policy_len(icmpv6_nla_policy, CTA_PROTO_MAX + 1); + static unsigned int size __read_mostly; + + if (!size) + size = nla_policy_len(icmpv6_nla_policy, CTA_PROTO_MAX + 1); + + return size; } #endif diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 28e675150853..0e516947c16f 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1563,9 +1563,14 @@ int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[], } EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_to_tuple); -int nf_ct_port_nlattr_tuple_size(void) +unsigned int nf_ct_port_nlattr_tuple_size(void) { - return nla_policy_len(nf_ct_port_nla_policy, CTA_PROTO_MAX + 1); + static unsigned int size __read_mostly; + + if (!size) + size = nla_policy_len(nf_ct_port_nla_policy, CTA_PROTO_MAX + 1); + + return size; } EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_tuple_size); #endif diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index de4053d84364..6e0adfefb9ed 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -533,11 +533,11 @@ nla_put_failure: return -1; } -static inline size_t ctnetlink_proto_size(const struct nf_conn *ct) +static size_t ctnetlink_proto_size(const struct nf_conn *ct) { const struct nf_conntrack_l3proto *l3proto; const struct nf_conntrack_l4proto *l4proto; - size_t len; + size_t len, len4 = 0; l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct)); len = l3proto->nla_size; @@ -545,8 +545,12 @@ static inline size_t ctnetlink_proto_size(const struct nf_conn *ct) l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); len += l4proto->nla_size; + if (l4proto->nlattr_tuple_size) { + len4 = l4proto->nlattr_tuple_size(); + len4 *= 3u; /* ORIG, REPLY, MASTER */ + } - return len; + return len + len4; } static inline size_t ctnetlink_acct_size(const struct nf_conn *ct) diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 83f739e9dc08..3b06ff3f2dee 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -398,8 +398,6 @@ int nf_ct_l4proto_register_one(struct nf_conntrack_l4proto *l4proto) l4proto->nla_size = 0; if (l4proto->nlattr_size) l4proto->nla_size += l4proto->nlattr_size(); - if (l4proto->nlattr_tuple_size) - l4proto->nla_size += 3 * l4proto->nlattr_tuple_size(); rcu_assign_pointer(nf_ct_protos[l4proto->l3proto][l4proto->l4proto], l4proto); diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 8f283294d70f..b12fc07111d0 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1277,9 +1277,14 @@ static int tcp_nlattr_size(void) + nla_policy_len(tcp_nla_policy, CTA_PROTOINFO_TCP_MAX + 1); } -static int tcp_nlattr_tuple_size(void) +static unsigned int tcp_nlattr_tuple_size(void) { - return nla_policy_len(nf_ct_port_nla_policy, CTA_PROTO_MAX + 1); + static unsigned int size __read_mostly; + + if (!size) + size = nla_policy_len(nf_ct_port_nla_policy, CTA_PROTO_MAX + 1); + + return size; } #endif -- cgit v1.2.3-59-g8ed1b From b988b8ff072ab04abd62d10d3fe44ec544be8a7d Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Thu, 2 Nov 2017 10:19:17 +0100 Subject: xen: re-introduce support for grant v2 interface The grant v2 support was removed from the kernel with commit 438b33c7145ca8a5131a30c36d8f59bce119a19a ("xen/grant-table: remove support for V2 tables") as the higher memory footprint of v2 grants resulted in less grants being possible for a kernel compared to the v1 grant interface. As machines with more than 16TB of memory are expected to be more common in the near future support of grant v2 is mandatory in order to be able to run a Xen pv domain at any memory location. So re-add grant v2 support basically by reverting above commit. Signed-off-by: Juergen Gross Reviewed-by: Boris Ostrovsky Signed-off-by: Boris Ostrovsky --- arch/arm/xen/grant-table.c | 9 +- arch/x86/xen/grant-table.c | 60 ++++++++- drivers/xen/grant-table.c | 312 ++++++++++++++++++++++++++++++++++++++++++++- include/xen/grant_table.h | 30 ++++- 4 files changed, 398 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/arm/xen/grant-table.c b/arch/arm/xen/grant-table.c index e43791829ace..91cf08ba1e95 100644 --- a/arch/arm/xen/grant-table.c +++ b/arch/arm/xen/grant-table.c @@ -45,7 +45,14 @@ void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) return; } -int arch_gnttab_init(unsigned long nr_shared) +int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, + unsigned long max_nr_gframes, + grant_status_t **__shared) +{ + return -ENOSYS; +} + +int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status) { return 0; } diff --git a/arch/x86/xen/grant-table.c b/arch/x86/xen/grant-table.c index 809b6c812654..92ccc718152d 100644 --- a/arch/x86/xen/grant-table.c +++ b/arch/x86/xen/grant-table.c @@ -49,7 +49,7 @@ static struct gnttab_vm_area { struct vm_struct *area; pte_t **ptes; -} gnttab_shared_vm_area; +} gnttab_shared_vm_area, gnttab_status_vm_area; int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, unsigned long max_nr_gframes, @@ -73,16 +73,43 @@ int arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes, return 0; } +int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, + unsigned long max_nr_gframes, + grant_status_t **__shared) +{ + grant_status_t *shared = *__shared; + unsigned long addr; + unsigned long i; + + if (shared == NULL) + *__shared = shared = gnttab_status_vm_area.area->addr; + + addr = (unsigned long)shared; + + for (i = 0; i < nr_gframes; i++) { + set_pte_at(&init_mm, addr, gnttab_status_vm_area.ptes[i], + mfn_pte(frames[i], PAGE_KERNEL)); + addr += PAGE_SIZE; + } + + return 0; +} + void arch_gnttab_unmap(void *shared, unsigned long nr_gframes) { + pte_t **ptes; unsigned long addr; unsigned long i; + if (shared == gnttab_status_vm_area.area->addr) + ptes = gnttab_status_vm_area.ptes; + else + ptes = gnttab_shared_vm_area.ptes; + addr = (unsigned long)shared; for (i = 0; i < nr_gframes; i++) { - set_pte_at(&init_mm, addr, gnttab_shared_vm_area.ptes[i], - __pte(0)); + set_pte_at(&init_mm, addr, ptes[i], __pte(0)); addr += PAGE_SIZE; } } @@ -102,12 +129,35 @@ static int arch_gnttab_valloc(struct gnttab_vm_area *area, unsigned nr_frames) return 0; } -int arch_gnttab_init(unsigned long nr_shared) +static void arch_gnttab_vfree(struct gnttab_vm_area *area) { + free_vm_area(area->area); + kfree(area->ptes); +} + +int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status) +{ + int ret; + if (!xen_pv_domain()) return 0; - return arch_gnttab_valloc(&gnttab_shared_vm_area, nr_shared); + ret = arch_gnttab_valloc(&gnttab_shared_vm_area, nr_shared); + if (ret < 0) + return ret; + + /* + * Always allocate the space for the status frames in case + * we're migrated to a host with V2 support. + */ + ret = arch_gnttab_valloc(&gnttab_status_vm_area, nr_status); + if (ret < 0) + goto err; + + return 0; +err: + arch_gnttab_vfree(&gnttab_shared_vm_area); + return -ENOMEM; } #ifdef CONFIG_XEN_PVH diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 2c6a9114d332..65c4bdb0b463 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -71,6 +71,7 @@ struct grant_frames xen_auto_xlat_grant_frames; static union { struct grant_entry_v1 *v1; + union grant_entry_v2 *v2; void *addr; } gnttab_shared; @@ -121,6 +122,29 @@ struct gnttab_ops { * by bit operations. */ int (*query_foreign_access)(grant_ref_t ref); + /* + * Grant a domain to access a range of bytes within the page referred by + * an available grant entry. Ref parameter is reference of a grant entry + * which will be sub-page accessed, domid is id of grantee domain, frame + * is frame address of subpage grant, flags is grant type and flag + * information, page_off is offset of the range of bytes, and length is + * length of bytes to be accessed. + */ + void (*update_subpage_entry)(grant_ref_t ref, domid_t domid, + unsigned long frame, int flags, + unsigned page_off, unsigned length); + /* + * Redirect an available grant entry on domain A to another grant + * reference of domain B, then allow domain C to use grant reference + * of domain B transitively. Ref parameter is an available grant entry + * reference on domain A, domid is id of domain C which accesses grant + * entry transitively, flags is grant type and flag information, + * trans_domid is id of domain B whose grant entry is finally accessed + * transitively, trans_gref is grant entry transitive reference of + * domain B. + */ + void (*update_trans_entry)(grant_ref_t ref, domid_t domid, int flags, + domid_t trans_domid, grant_ref_t trans_gref); }; struct unmap_refs_callback_data { @@ -130,6 +154,9 @@ struct unmap_refs_callback_data { static const struct gnttab_ops *gnttab_interface; +/* This reflects status of grant entries, so act as a global value. */ +static grant_status_t *grstatus; + static int grant_table_version; static int grefs_per_grant_frame; @@ -138,6 +165,7 @@ static struct gnttab_free_callback *gnttab_free_callback_list; static int gnttab_expand(unsigned int req_entries); #define RPP (PAGE_SIZE / sizeof(grant_ref_t)) +#define SPP (PAGE_SIZE / sizeof(grant_status_t)) static inline grant_ref_t *__gnttab_entry(grant_ref_t entry) { @@ -210,7 +238,7 @@ static void put_free_entry(grant_ref_t ref) } /* - * Following applies to gnttab_update_entry_v1. + * Following applies to gnttab_update_entry_v1 and gnttab_update_entry_v2. * Introducing a valid entry into the grant table: * 1. Write ent->domid. * 2. Write ent->frame: @@ -229,6 +257,15 @@ static void gnttab_update_entry_v1(grant_ref_t ref, domid_t domid, gnttab_shared.v1[ref].flags = flags; } +static void gnttab_update_entry_v2(grant_ref_t ref, domid_t domid, + unsigned long frame, unsigned int flags) +{ + gnttab_shared.v2[ref].hdr.domid = domid; + gnttab_shared.v2[ref].full_page.frame = frame; + wmb(); /* Hypervisor concurrent accesses. */ + gnttab_shared.v2[ref].hdr.flags = GTF_permit_access | flags; +} + /* * Public grant-issuing interface functions */ @@ -255,11 +292,132 @@ int gnttab_grant_foreign_access(domid_t domid, unsigned long frame, } EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access); +static void gnttab_update_subpage_entry_v2(grant_ref_t ref, domid_t domid, + unsigned long frame, int flags, + unsigned page_off, unsigned length) +{ + gnttab_shared.v2[ref].sub_page.frame = frame; + gnttab_shared.v2[ref].sub_page.page_off = page_off; + gnttab_shared.v2[ref].sub_page.length = length; + gnttab_shared.v2[ref].hdr.domid = domid; + wmb(); + gnttab_shared.v2[ref].hdr.flags = + GTF_permit_access | GTF_sub_page | flags; +} + +int gnttab_grant_foreign_access_subpage_ref(grant_ref_t ref, domid_t domid, + unsigned long frame, int flags, + unsigned page_off, + unsigned length) +{ + if (flags & (GTF_accept_transfer | GTF_reading | + GTF_writing | GTF_transitive)) + return -EPERM; + + if (gnttab_interface->update_subpage_entry == NULL) + return -ENOSYS; + + gnttab_interface->update_subpage_entry(ref, domid, frame, flags, + page_off, length); + + return 0; +} +EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_subpage_ref); + +int gnttab_grant_foreign_access_subpage(domid_t domid, unsigned long frame, + int flags, unsigned page_off, + unsigned length) +{ + int ref, rc; + + ref = get_free_entries(1); + if (unlikely(ref < 0)) + return -ENOSPC; + + rc = gnttab_grant_foreign_access_subpage_ref(ref, domid, frame, flags, + page_off, length); + if (rc < 0) { + put_free_entry(ref); + return rc; + } + + return ref; +} +EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_subpage); + +bool gnttab_subpage_grants_available(void) +{ + return gnttab_interface->update_subpage_entry != NULL; +} +EXPORT_SYMBOL_GPL(gnttab_subpage_grants_available); + +static void gnttab_update_trans_entry_v2(grant_ref_t ref, domid_t domid, + int flags, domid_t trans_domid, + grant_ref_t trans_gref) +{ + gnttab_shared.v2[ref].transitive.trans_domid = trans_domid; + gnttab_shared.v2[ref].transitive.gref = trans_gref; + gnttab_shared.v2[ref].hdr.domid = domid; + wmb(); + gnttab_shared.v2[ref].hdr.flags = + GTF_permit_access | GTF_transitive | flags; +} + +int gnttab_grant_foreign_access_trans_ref(grant_ref_t ref, domid_t domid, + int flags, domid_t trans_domid, + grant_ref_t trans_gref) +{ + if (flags & (GTF_accept_transfer | GTF_reading | + GTF_writing | GTF_sub_page)) + return -EPERM; + + if (gnttab_interface->update_trans_entry == NULL) + return -ENOSYS; + + gnttab_interface->update_trans_entry(ref, domid, flags, trans_domid, + trans_gref); + + return 0; +} +EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_trans_ref); + +int gnttab_grant_foreign_access_trans(domid_t domid, int flags, + domid_t trans_domid, + grant_ref_t trans_gref) +{ + int ref, rc; + + ref = get_free_entries(1); + if (unlikely(ref < 0)) + return -ENOSPC; + + rc = gnttab_grant_foreign_access_trans_ref(ref, domid, flags, + trans_domid, trans_gref); + if (rc < 0) { + put_free_entry(ref); + return rc; + } + + return ref; +} +EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_trans); + +bool gnttab_trans_grants_available(void) +{ + return gnttab_interface->update_trans_entry != NULL; +} +EXPORT_SYMBOL_GPL(gnttab_trans_grants_available); + static int gnttab_query_foreign_access_v1(grant_ref_t ref) { return gnttab_shared.v1[ref].flags & (GTF_reading|GTF_writing); } +static int gnttab_query_foreign_access_v2(grant_ref_t ref) +{ + return grstatus[ref] & (GTF_reading|GTF_writing); +} + int gnttab_query_foreign_access(grant_ref_t ref) { return gnttab_interface->query_foreign_access(ref); @@ -282,6 +440,29 @@ static int gnttab_end_foreign_access_ref_v1(grant_ref_t ref, int readonly) return 1; } +static int gnttab_end_foreign_access_ref_v2(grant_ref_t ref, int readonly) +{ + gnttab_shared.v2[ref].hdr.flags = 0; + mb(); /* Concurrent access by hypervisor. */ + if (grstatus[ref] & (GTF_reading|GTF_writing)) { + return 0; + } else { + /* + * The read of grstatus needs to have acquire semantics. + * On x86, reads already have that, and we just need to + * protect against compiler reorderings. + * On other architectures we may need a full barrier. + */ +#ifdef CONFIG_X86 + barrier(); +#else + mb(); +#endif + } + + return 1; +} + static inline int _gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly) { return gnttab_interface->end_foreign_access_ref(ref, readonly); @@ -442,6 +623,37 @@ static unsigned long gnttab_end_foreign_transfer_ref_v1(grant_ref_t ref) return frame; } +static unsigned long gnttab_end_foreign_transfer_ref_v2(grant_ref_t ref) +{ + unsigned long frame; + u16 flags; + u16 *pflags; + + pflags = &gnttab_shared.v2[ref].hdr.flags; + + /* + * If a transfer is not even yet started, try to reclaim the grant + * reference and return failure (== 0). + */ + while (!((flags = *pflags) & GTF_transfer_committed)) { + if (sync_cmpxchg(pflags, flags, 0) == flags) + return 0; + cpu_relax(); + } + + /* If a transfer is in progress then wait until it is completed. */ + while (!(flags & GTF_transfer_completed)) { + flags = *pflags; + cpu_relax(); + } + + rmb(); /* Read the frame number /after/ reading completion status. */ + frame = gnttab_shared.v2[ref].full_page.frame; + BUG_ON(frame == 0); + + return frame; +} + unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref) { return gnttab_interface->end_foreign_transfer_ref(ref); @@ -938,6 +1150,12 @@ int gnttab_unmap_refs_sync(struct gntab_unmap_queue_data *item) } EXPORT_SYMBOL_GPL(gnttab_unmap_refs_sync); +static unsigned int nr_status_frames(unsigned int nr_grant_frames) +{ + BUG_ON(grefs_per_grant_frame == 0); + return (nr_grant_frames * grefs_per_grant_frame + SPP - 1) / SPP; +} + static int gnttab_map_frames_v1(xen_pfn_t *frames, unsigned int nr_gframes) { int rc; @@ -955,6 +1173,55 @@ static void gnttab_unmap_frames_v1(void) arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames); } +static int gnttab_map_frames_v2(xen_pfn_t *frames, unsigned int nr_gframes) +{ + uint64_t *sframes; + unsigned int nr_sframes; + struct gnttab_get_status_frames getframes; + int rc; + + nr_sframes = nr_status_frames(nr_gframes); + + /* No need for kzalloc as it is initialized in following hypercall + * GNTTABOP_get_status_frames. + */ + sframes = kmalloc_array(nr_sframes, sizeof(uint64_t), GFP_ATOMIC); + if (!sframes) + return -ENOMEM; + + getframes.dom = DOMID_SELF; + getframes.nr_frames = nr_sframes; + set_xen_guest_handle(getframes.frame_list, sframes); + + rc = HYPERVISOR_grant_table_op(GNTTABOP_get_status_frames, + &getframes, 1); + if (rc == -ENOSYS) { + kfree(sframes); + return -ENOSYS; + } + + BUG_ON(rc || getframes.status); + + rc = arch_gnttab_map_status(sframes, nr_sframes, + nr_status_frames(gnttab_max_grant_frames()), + &grstatus); + BUG_ON(rc); + kfree(sframes); + + rc = arch_gnttab_map_shared(frames, nr_gframes, + gnttab_max_grant_frames(), + &gnttab_shared.addr); + BUG_ON(rc); + + return 0; +} + +static void gnttab_unmap_frames_v2(void) +{ + arch_gnttab_unmap(gnttab_shared.addr, nr_grant_frames); + arch_gnttab_unmap(grstatus, nr_status_frames(nr_grant_frames)); +} + static int gnttab_map(unsigned int start_idx, unsigned int end_idx) { struct gnttab_setup_table setup; @@ -1022,13 +1289,45 @@ static const struct gnttab_ops gnttab_v1_ops = { .query_foreign_access = gnttab_query_foreign_access_v1, }; +static const struct gnttab_ops gnttab_v2_ops = { + .map_frames = gnttab_map_frames_v2, + .unmap_frames = gnttab_unmap_frames_v2, + .update_entry = gnttab_update_entry_v2, + .end_foreign_access_ref = gnttab_end_foreign_access_ref_v2, + .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v2, + .query_foreign_access = gnttab_query_foreign_access_v2, + .update_subpage_entry = gnttab_update_subpage_entry_v2, + .update_trans_entry = gnttab_update_trans_entry_v2, +}; + static void gnttab_request_version(void) { - /* Only version 1 is used, which will always be available. */ - grant_table_version = 1; - grefs_per_grant_frame = XEN_PAGE_SIZE / sizeof(struct grant_entry_v1); - gnttab_interface = &gnttab_v1_ops; + int rc; + struct gnttab_set_version gsv; + gsv.version = 1; + + rc = HYPERVISOR_grant_table_op(GNTTABOP_set_version, &gsv, 1); + if (rc == 0 && gsv.version == 2) { + grant_table_version = 2; + grefs_per_grant_frame = XEN_PAGE_SIZE / + sizeof(union grant_entry_v2); + gnttab_interface = &gnttab_v2_ops; + } else if (grant_table_version == 2) { + /* + * If we've already used version 2 features, + * but then suddenly discover that they're not + * available (e.g. migrating to an older + * version of Xen), almost unbounded badness + * can happen. + */ + panic("we need grant tables version 2, but only version 1 is available"); + } else { + grant_table_version = 1; + grefs_per_grant_frame = XEN_PAGE_SIZE / + sizeof(struct grant_entry_v1); + gnttab_interface = &gnttab_v1_ops; + } pr_info("Grant tables using version %d layout\n", grant_table_version); } @@ -1122,7 +1421,8 @@ int gnttab_init(void) } } - ret = arch_gnttab_init(max_nr_grant_frames); + ret = arch_gnttab_init(max_nr_grant_frames, + nr_status_frames(max_nr_grant_frames)); if (ret < 0) goto ini_nomem; diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index 34b1379f9777..dd6c7a32ee32 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h @@ -84,6 +84,24 @@ int gnttab_resume(void); int gnttab_grant_foreign_access(domid_t domid, unsigned long frame, int readonly); +int gnttab_grant_foreign_access_subpage(domid_t domid, unsigned long frame, + int flags, unsigned page_off, + unsigned length); +int gnttab_grant_foreign_access_trans(domid_t domid, int flags, + domid_t trans_domid, + grant_ref_t trans_gref); + +/* + * Are sub-page grants available on this version of Xen? Returns true if they + * are, and false if they're not. + */ +bool gnttab_subpage_grants_available(void); + +/* + * Are transitive grants available on this version of Xen? Returns true if they + * are, and false if they're not. + */ +bool gnttab_trans_grants_available(void); /* * End access through the given grant reference, iff the grant entry is no @@ -130,6 +148,13 @@ void gnttab_cancel_free_callback(struct gnttab_free_callback *callback); void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid, unsigned long frame, int readonly); +int gnttab_grant_foreign_access_subpage_ref(grant_ref_t ref, domid_t domid, + unsigned long frame, int flags, + unsigned page_off, + unsigned length); +int gnttab_grant_foreign_access_trans_ref(grant_ref_t ref, domid_t domid, + int flags, domid_t trans_domid, + grant_ref_t trans_gref); /* Give access to the first 4K of the page */ static inline void gnttab_page_grant_foreign_access_ref_one( @@ -174,10 +199,13 @@ gnttab_set_unmap_op(struct gnttab_unmap_grant_ref *unmap, phys_addr_t addr, unmap->dev_bus_addr = 0; } -int arch_gnttab_init(unsigned long nr_shared); +int arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status); int arch_gnttab_map_shared(xen_pfn_t *frames, unsigned long nr_gframes, unsigned long max_nr_gframes, void **__shared); +int arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes, + unsigned long max_nr_gframes, + grant_status_t **__shared); void arch_gnttab_unmap(void *shared, unsigned long nr_gframes); struct grant_frames { -- cgit v1.2.3-59-g8ed1b From 56c9c700c4399858e50971d98ac44b5842b06a87 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Thu, 2 Nov 2017 10:19:18 +0100 Subject: xen: limit grant v2 interface to the v1 functionality As there is currently no user for sub-page grants or transient grants remove that functionality. This at once makes it possible to switch from grant v2 to grant v1 without restrictions, as there is no loss of functionality other than the limited frame number width related to the switch. Signed-off-by: Juergen Gross Reviewed-by: Boris Ostrovsky Signed-off-by: Boris Ostrovsky --- drivers/xen/grant-table.c | 150 ---------------------------------------------- include/xen/grant_table.h | 25 -------- 2 files changed, 175 deletions(-) (limited to 'include') diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 65c4bdb0b463..db6a1b9efd11 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -122,29 +122,6 @@ struct gnttab_ops { * by bit operations. */ int (*query_foreign_access)(grant_ref_t ref); - /* - * Grant a domain to access a range of bytes within the page referred by - * an available grant entry. Ref parameter is reference of a grant entry - * which will be sub-page accessed, domid is id of grantee domain, frame - * is frame address of subpage grant, flags is grant type and flag - * information, page_off is offset of the range of bytes, and length is - * length of bytes to be accessed. - */ - void (*update_subpage_entry)(grant_ref_t ref, domid_t domid, - unsigned long frame, int flags, - unsigned page_off, unsigned length); - /* - * Redirect an available grant entry on domain A to another grant - * reference of domain B, then allow domain C to use grant reference - * of domain B transitively. Ref parameter is an available grant entry - * reference on domain A, domid is id of domain C which accesses grant - * entry transitively, flags is grant type and flag information, - * trans_domid is id of domain B whose grant entry is finally accessed - * transitively, trans_gref is grant entry transitive reference of - * domain B. - */ - void (*update_trans_entry)(grant_ref_t ref, domid_t domid, int flags, - domid_t trans_domid, grant_ref_t trans_gref); }; struct unmap_refs_callback_data { @@ -292,122 +269,6 @@ int gnttab_grant_foreign_access(domid_t domid, unsigned long frame, } EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access); -static void gnttab_update_subpage_entry_v2(grant_ref_t ref, domid_t domid, - unsigned long frame, int flags, - unsigned page_off, unsigned length) -{ - gnttab_shared.v2[ref].sub_page.frame = frame; - gnttab_shared.v2[ref].sub_page.page_off = page_off; - gnttab_shared.v2[ref].sub_page.length = length; - gnttab_shared.v2[ref].hdr.domid = domid; - wmb(); - gnttab_shared.v2[ref].hdr.flags = - GTF_permit_access | GTF_sub_page | flags; -} - -int gnttab_grant_foreign_access_subpage_ref(grant_ref_t ref, domid_t domid, - unsigned long frame, int flags, - unsigned page_off, - unsigned length) -{ - if (flags & (GTF_accept_transfer | GTF_reading | - GTF_writing | GTF_transitive)) - return -EPERM; - - if (gnttab_interface->update_subpage_entry == NULL) - return -ENOSYS; - - gnttab_interface->update_subpage_entry(ref, domid, frame, flags, - page_off, length); - - return 0; -} -EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_subpage_ref); - -int gnttab_grant_foreign_access_subpage(domid_t domid, unsigned long frame, - int flags, unsigned page_off, - unsigned length) -{ - int ref, rc; - - ref = get_free_entries(1); - if (unlikely(ref < 0)) - return -ENOSPC; - - rc = gnttab_grant_foreign_access_subpage_ref(ref, domid, frame, flags, - page_off, length); - if (rc < 0) { - put_free_entry(ref); - return rc; - } - - return ref; -} -EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_subpage); - -bool gnttab_subpage_grants_available(void) -{ - return gnttab_interface->update_subpage_entry != NULL; -} -EXPORT_SYMBOL_GPL(gnttab_subpage_grants_available); - -static void gnttab_update_trans_entry_v2(grant_ref_t ref, domid_t domid, - int flags, domid_t trans_domid, - grant_ref_t trans_gref) -{ - gnttab_shared.v2[ref].transitive.trans_domid = trans_domid; - gnttab_shared.v2[ref].transitive.gref = trans_gref; - gnttab_shared.v2[ref].hdr.domid = domid; - wmb(); - gnttab_shared.v2[ref].hdr.flags = - GTF_permit_access | GTF_transitive | flags; -} - -int gnttab_grant_foreign_access_trans_ref(grant_ref_t ref, domid_t domid, - int flags, domid_t trans_domid, - grant_ref_t trans_gref) -{ - if (flags & (GTF_accept_transfer | GTF_reading | - GTF_writing | GTF_sub_page)) - return -EPERM; - - if (gnttab_interface->update_trans_entry == NULL) - return -ENOSYS; - - gnttab_interface->update_trans_entry(ref, domid, flags, trans_domid, - trans_gref); - - return 0; -} -EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_trans_ref); - -int gnttab_grant_foreign_access_trans(domid_t domid, int flags, - domid_t trans_domid, - grant_ref_t trans_gref) -{ - int ref, rc; - - ref = get_free_entries(1); - if (unlikely(ref < 0)) - return -ENOSPC; - - rc = gnttab_grant_foreign_access_trans_ref(ref, domid, flags, - trans_domid, trans_gref); - if (rc < 0) { - put_free_entry(ref); - return rc; - } - - return ref; -} -EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_trans); - -bool gnttab_trans_grants_available(void) -{ - return gnttab_interface->update_trans_entry != NULL; -} -EXPORT_SYMBOL_GPL(gnttab_trans_grants_available); - static int gnttab_query_foreign_access_v1(grant_ref_t ref) { return gnttab_shared.v1[ref].flags & (GTF_reading|GTF_writing); @@ -1296,8 +1157,6 @@ static const struct gnttab_ops gnttab_v2_ops = { .end_foreign_access_ref = gnttab_end_foreign_access_ref_v2, .end_foreign_transfer_ref = gnttab_end_foreign_transfer_ref_v2, .query_foreign_access = gnttab_query_foreign_access_v2, - .update_subpage_entry = gnttab_update_subpage_entry_v2, - .update_trans_entry = gnttab_update_trans_entry_v2, }; static void gnttab_request_version(void) @@ -1313,15 +1172,6 @@ static void gnttab_request_version(void) grefs_per_grant_frame = XEN_PAGE_SIZE / sizeof(union grant_entry_v2); gnttab_interface = &gnttab_v2_ops; - } else if (grant_table_version == 2) { - /* - * If we've already used version 2 features, - * but then suddenly discover that they're not - * available (e.g. migrating to an older - * version of Xen), almost unbounded badness - * can happen. - */ - panic("we need grant tables version 2, but only version 1 is available"); } else { grant_table_version = 1; grefs_per_grant_frame = XEN_PAGE_SIZE / diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h index dd6c7a32ee32..2e37741f6b8d 100644 --- a/include/xen/grant_table.h +++ b/include/xen/grant_table.h @@ -84,24 +84,6 @@ int gnttab_resume(void); int gnttab_grant_foreign_access(domid_t domid, unsigned long frame, int readonly); -int gnttab_grant_foreign_access_subpage(domid_t domid, unsigned long frame, - int flags, unsigned page_off, - unsigned length); -int gnttab_grant_foreign_access_trans(domid_t domid, int flags, - domid_t trans_domid, - grant_ref_t trans_gref); - -/* - * Are sub-page grants available on this version of Xen? Returns true if they - * are, and false if they're not. - */ -bool gnttab_subpage_grants_available(void); - -/* - * Are transitive grants available on this version of Xen? Returns true if they - * are, and false if they're not. - */ -bool gnttab_trans_grants_available(void); /* * End access through the given grant reference, iff the grant entry is no @@ -148,13 +130,6 @@ void gnttab_cancel_free_callback(struct gnttab_free_callback *callback); void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid, unsigned long frame, int readonly); -int gnttab_grant_foreign_access_subpage_ref(grant_ref_t ref, domid_t domid, - unsigned long frame, int flags, - unsigned page_off, - unsigned length); -int gnttab_grant_foreign_access_trans_ref(grant_ref_t ref, domid_t domid, - int flags, domid_t trans_domid, - grant_ref_t trans_gref); /* Give access to the first 4K of the page */ static inline void gnttab_page_grant_foreign_access_ref_one( -- cgit v1.2.3-59-g8ed1b From ba0e4d9917b43dfa746cbbcb4477da59aae73bd6 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 9 Oct 2017 19:52:28 +0200 Subject: netfilter: nf_tables: get set elements via netlink This patch adds a new get operation to look up for specific elements in a set via netlink interface. You can also use it to check if an interval already exists. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 5 ++ net/netfilter/nf_tables_api.c | 184 ++++++++++++++++++++++++++------------ net/netfilter/nft_set_bitmap.c | 18 ++++ net/netfilter/nft_set_hash.c | 39 ++++++++ net/netfilter/nft_set_rbtree.c | 73 +++++++++++++++ 5 files changed, 264 insertions(+), 55 deletions(-) (limited to 'include') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 0f5b12a4ad09..d011e56cc7a9 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -311,6 +311,7 @@ struct nft_expr; * @flush: deactivate element in the next generation * @remove: remove element from set * @walk: iterate over all set elemeennts + * @get: get set elements * @privsize: function to return size of set private data * @init: initialize private data of new set instance * @destroy: destroy private data of set instance @@ -350,6 +351,10 @@ struct nft_set_ops { void (*walk)(const struct nft_ctx *ctx, struct nft_set *set, struct nft_set_iter *iter); + void * (*get)(const struct net *net, + const struct nft_set *set, + const struct nft_set_elem *elem, + unsigned int flags); unsigned int (*privsize)(const struct nlattr * const nla[], const struct nft_set_desc *desc); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 3b4a0739ee39..1d66be0d8ef7 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -3586,45 +3586,6 @@ static int nf_tables_dump_set_done(struct netlink_callback *cb) return 0; } -static int nf_tables_getsetelem(struct net *net, struct sock *nlsk, - struct sk_buff *skb, const struct nlmsghdr *nlh, - const struct nlattr * const nla[], - struct netlink_ext_ack *extack) -{ - u8 genmask = nft_genmask_cur(net); - const struct nft_set *set; - struct nft_ctx ctx; - int err; - - err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, genmask); - if (err < 0) - return err; - - set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET], - genmask); - if (IS_ERR(set)) - return PTR_ERR(set); - - if (nlh->nlmsg_flags & NLM_F_DUMP) { - struct netlink_dump_control c = { - .dump = nf_tables_dump_set, - .done = nf_tables_dump_set_done, - }; - struct nft_set_dump_ctx *dump_ctx; - - dump_ctx = kmalloc(sizeof(*dump_ctx), GFP_KERNEL); - if (!dump_ctx) - return -ENOMEM; - - dump_ctx->set = set; - dump_ctx->ctx = ctx; - - c.data = dump_ctx; - return netlink_dump_start(nlsk, skb, nlh, &c); - } - return -EOPNOTSUPP; -} - static int nf_tables_fill_setelem_info(struct sk_buff *skb, const struct nft_ctx *ctx, u32 seq, u32 portid, int event, u16 flags, @@ -3670,6 +3631,135 @@ nla_put_failure: return -1; } +static int nft_setelem_parse_flags(const struct nft_set *set, + const struct nlattr *attr, u32 *flags) +{ + if (attr == NULL) + return 0; + + *flags = ntohl(nla_get_be32(attr)); + if (*flags & ~NFT_SET_ELEM_INTERVAL_END) + return -EINVAL; + if (!(set->flags & NFT_SET_INTERVAL) && + *flags & NFT_SET_ELEM_INTERVAL_END) + return -EINVAL; + + return 0; +} + +static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set, + const struct nlattr *attr) +{ + struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; + const struct nft_set_ext *ext; + struct nft_data_desc desc; + struct nft_set_elem elem; + struct sk_buff *skb; + uint32_t flags = 0; + void *priv; + int err; + + err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, + nft_set_elem_policy, NULL); + if (err < 0) + return err; + + if (!nla[NFTA_SET_ELEM_KEY]) + return -EINVAL; + + err = nft_setelem_parse_flags(set, nla[NFTA_SET_ELEM_FLAGS], &flags); + if (err < 0) + return err; + + err = nft_data_init(ctx, &elem.key.val, sizeof(elem.key), &desc, + nla[NFTA_SET_ELEM_KEY]); + if (err < 0) + return err; + + err = -EINVAL; + if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) + return err; + + priv = set->ops->get(ctx->net, set, &elem, flags); + if (IS_ERR(priv)) + return PTR_ERR(priv); + + elem.priv = priv; + ext = nft_set_elem_ext(set, &elem); + + err = -ENOMEM; + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb == NULL) + goto err1; + + err = nf_tables_fill_setelem_info(skb, ctx, ctx->seq, ctx->portid, + NFT_MSG_NEWSETELEM, 0, set, &elem); + if (err < 0) + goto err2; + + err = nfnetlink_unicast(skb, ctx->net, ctx->portid, MSG_DONTWAIT); + /* This avoids a loop in nfnetlink. */ + if (err < 0) + goto err1; + + return 0; +err2: + kfree_skb(skb); +err1: + /* this avoids a loop in nfnetlink. */ + return err == -EAGAIN ? -ENOBUFS : err; +} + +static int nf_tables_getsetelem(struct net *net, struct sock *nlsk, + struct sk_buff *skb, const struct nlmsghdr *nlh, + const struct nlattr * const nla[], + struct netlink_ext_ack *extack) +{ + u8 genmask = nft_genmask_cur(net); + struct nft_set *set; + struct nlattr *attr; + struct nft_ctx ctx; + int rem, err = 0; + + err = nft_ctx_init_from_elemattr(&ctx, net, skb, nlh, nla, genmask); + if (err < 0) + return err; + + set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET], + genmask); + if (IS_ERR(set)) + return PTR_ERR(set); + + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = nf_tables_dump_set, + .done = nf_tables_dump_set_done, + }; + struct nft_set_dump_ctx *dump_ctx; + + dump_ctx = kmalloc(sizeof(*dump_ctx), GFP_KERNEL); + if (!dump_ctx) + return -ENOMEM; + + dump_ctx->set = set; + dump_ctx->ctx = ctx; + + c.data = dump_ctx; + return netlink_dump_start(nlsk, skb, nlh, &c); + } + + if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS]) + return -EINVAL; + + nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { + err = nft_get_set_elem(&ctx, set, attr); + if (err < 0) + break; + } + + return err; +} + static void nf_tables_setelem_notify(const struct nft_ctx *ctx, const struct nft_set *set, const struct nft_set_elem *elem, @@ -3770,22 +3860,6 @@ static void nf_tables_set_elem_destroy(const struct nft_set *set, void *elem) kfree(elem); } -static int nft_setelem_parse_flags(const struct nft_set *set, - const struct nlattr *attr, u32 *flags) -{ - if (attr == NULL) - return 0; - - *flags = ntohl(nla_get_be32(attr)); - if (*flags & ~NFT_SET_ELEM_INTERVAL_END) - return -EINVAL; - if (!(set->flags & NFT_SET_INTERVAL) && - *flags & NFT_SET_ELEM_INTERVAL_END) - return -EINVAL; - - return 0; -} - static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set, const struct nlattr *attr, u32 nlmsg_flags) { diff --git a/net/netfilter/nft_set_bitmap.c b/net/netfilter/nft_set_bitmap.c index 734989c40579..45fb2752fb63 100644 --- a/net/netfilter/nft_set_bitmap.c +++ b/net/netfilter/nft_set_bitmap.c @@ -106,6 +106,23 @@ nft_bitmap_elem_find(const struct nft_set *set, struct nft_bitmap_elem *this, return NULL; } +static void *nft_bitmap_get(const struct net *net, const struct nft_set *set, + const struct nft_set_elem *elem, unsigned int flags) +{ + const struct nft_bitmap *priv = nft_set_priv(set); + u8 genmask = nft_genmask_cur(net); + struct nft_bitmap_elem *be; + + list_for_each_entry_rcu(be, &priv->list, head) { + if (memcmp(nft_set_ext_key(&be->ext), elem->key.val.data, set->klen) || + !nft_set_elem_active(&be->ext, genmask)) + continue; + + return be; + } + return ERR_PTR(-ENOENT); +} + static int nft_bitmap_insert(const struct net *net, const struct nft_set *set, const struct nft_set_elem *elem, struct nft_set_ext **ext) @@ -294,6 +311,7 @@ static struct nft_set_ops nft_bitmap_ops __read_mostly = { .activate = nft_bitmap_activate, .lookup = nft_bitmap_lookup, .walk = nft_bitmap_walk, + .get = nft_bitmap_get, }; static struct nft_set_type nft_bitmap_type __read_mostly = { diff --git a/net/netfilter/nft_set_hash.c b/net/netfilter/nft_set_hash.c index 650677f1e539..c68a7e0fcf1e 100644 --- a/net/netfilter/nft_set_hash.c +++ b/net/netfilter/nft_set_hash.c @@ -95,6 +95,24 @@ static bool nft_rhash_lookup(const struct net *net, const struct nft_set *set, return !!he; } +static void *nft_rhash_get(const struct net *net, const struct nft_set *set, + const struct nft_set_elem *elem, unsigned int flags) +{ + struct nft_rhash *priv = nft_set_priv(set); + struct nft_rhash_elem *he; + struct nft_rhash_cmp_arg arg = { + .genmask = nft_genmask_cur(net), + .set = set, + .key = elem->key.val.data, + }; + + he = rhashtable_lookup_fast(&priv->ht, &arg, nft_rhash_params); + if (he != NULL) + return he; + + return ERR_PTR(-ENOENT); +} + static bool nft_rhash_update(struct nft_set *set, const u32 *key, void *(*new)(struct nft_set *, const struct nft_expr *, @@ -409,6 +427,24 @@ static bool nft_hash_lookup(const struct net *net, const struct nft_set *set, return false; } +static void *nft_hash_get(const struct net *net, const struct nft_set *set, + const struct nft_set_elem *elem, unsigned int flags) +{ + struct nft_hash *priv = nft_set_priv(set); + u8 genmask = nft_genmask_cur(net); + struct nft_hash_elem *he; + u32 hash; + + hash = jhash(elem->key.val.data, set->klen, priv->seed); + hash = reciprocal_scale(hash, priv->buckets); + hlist_for_each_entry_rcu(he, &priv->table[hash], node) { + if (!memcmp(nft_set_ext_key(&he->ext), elem->key.val.data, set->klen) && + nft_set_elem_active(&he->ext, genmask)) + return he; + } + return ERR_PTR(-ENOENT); +} + /* nft_hash_select_ops() makes sure key size can be either 2 or 4 bytes . */ static inline u32 nft_hash_key(const u32 *key, u32 klen) { @@ -600,6 +636,7 @@ static struct nft_set_ops nft_rhash_ops __read_mostly = { .lookup = nft_rhash_lookup, .update = nft_rhash_update, .walk = nft_rhash_walk, + .get = nft_rhash_get, .features = NFT_SET_MAP | NFT_SET_OBJECT | NFT_SET_TIMEOUT, }; @@ -617,6 +654,7 @@ static struct nft_set_ops nft_hash_ops __read_mostly = { .remove = nft_hash_remove, .lookup = nft_hash_lookup, .walk = nft_hash_walk, + .get = nft_hash_get, .features = NFT_SET_MAP | NFT_SET_OBJECT, }; @@ -634,6 +672,7 @@ static struct nft_set_ops nft_hash_fast_ops __read_mostly = { .remove = nft_hash_remove, .lookup = nft_hash_lookup_fast, .walk = nft_hash_walk, + .get = nft_hash_get, .features = NFT_SET_MAP | NFT_SET_OBJECT, }; diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index d83a4ec5900d..e6f08bc5f359 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -113,6 +113,78 @@ static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set, return ret; } +static bool __nft_rbtree_get(const struct net *net, const struct nft_set *set, + const u32 *key, struct nft_rbtree_elem **elem, + unsigned int seq, unsigned int flags, u8 genmask) +{ + struct nft_rbtree_elem *rbe, *interval = NULL; + struct nft_rbtree *priv = nft_set_priv(set); + const struct rb_node *parent; + const void *this; + int d; + + parent = rcu_dereference_raw(priv->root.rb_node); + while (parent != NULL) { + if (read_seqcount_retry(&priv->count, seq)) + return false; + + rbe = rb_entry(parent, struct nft_rbtree_elem, node); + + this = nft_set_ext_key(&rbe->ext); + d = memcmp(this, key, set->klen); + if (d < 0) { + parent = rcu_dereference_raw(parent->rb_left); + interval = rbe; + } else if (d > 0) { + parent = rcu_dereference_raw(parent->rb_right); + } else { + if (!nft_set_elem_active(&rbe->ext, genmask)) + parent = rcu_dereference_raw(parent->rb_left); + + if (!nft_set_ext_exists(&rbe->ext, NFT_SET_EXT_FLAGS) || + (*nft_set_ext_flags(&rbe->ext) & NFT_SET_ELEM_INTERVAL_END) == + (flags & NFT_SET_ELEM_INTERVAL_END)) { + *elem = rbe; + return true; + } + return false; + } + } + + if (set->flags & NFT_SET_INTERVAL && interval != NULL && + nft_set_elem_active(&interval->ext, genmask) && + !nft_rbtree_interval_end(interval)) { + *elem = interval; + return true; + } + + return false; +} + +static void *nft_rbtree_get(const struct net *net, const struct nft_set *set, + const struct nft_set_elem *elem, unsigned int flags) +{ + struct nft_rbtree *priv = nft_set_priv(set); + unsigned int seq = read_seqcount_begin(&priv->count); + struct nft_rbtree_elem *rbe = ERR_PTR(-ENOENT); + const u32 *key = (const u32 *)&elem->key.val; + u8 genmask = nft_genmask_cur(net); + bool ret; + + ret = __nft_rbtree_get(net, set, key, &rbe, seq, flags, genmask); + if (ret || !read_seqcount_retry(&priv->count, seq)) + return rbe; + + read_lock_bh(&priv->lock); + seq = read_seqcount_begin(&priv->count); + ret = __nft_rbtree_get(net, set, key, &rbe, seq, flags, genmask); + if (!ret) + rbe = ERR_PTR(-ENOENT); + read_unlock_bh(&priv->lock); + + return rbe; +} + static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, struct nft_rbtree_elem *new, struct nft_set_ext **ext) @@ -336,6 +408,7 @@ static struct nft_set_ops nft_rbtree_ops __read_mostly = { .activate = nft_rbtree_activate, .lookup = nft_rbtree_lookup, .walk = nft_rbtree_walk, + .get = nft_rbtree_get, .features = NFT_SET_INTERVAL | NFT_SET_MAP | NFT_SET_OBJECT, }; -- cgit v1.2.3-59-g8ed1b From 24a0c654d7d6063301c51361f911369264342b3c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Oct 2017 15:38:54 -0500 Subject: PCI: Add for_each_pci_bridge() helper The following pattern is often used: list_for_each_entry(dev, &bus->devices, bus_list) { if (pci_is_bridge(dev)) { ... } } Add a for_each_pci_bridge() helper to make that code easier to write and read by reducing indentation level. It also saves one or few lines of code in each occurrence. Convert PCI core parts here at the same time. Signed-off-by: Andy Shevchenko [bhelgaas: fold in http://lkml.kernel.org/r/20171013165352.25550-1-andriy.shevchenko@linux.intel.com] Signed-off-by: Bjorn Helgaas --- drivers/pci/hotplug/acpiphp_glue.c | 15 ++++++--------- drivers/pci/hotplug/cpci_hotplug_pci.c | 7 ++----- drivers/pci/hotplug/pciehp_pci.c | 5 ++--- drivers/pci/hotplug/shpchp_pci.c | 6 ++---- drivers/pci/probe.c | 6 ++---- drivers/pci/setup-bus.c | 7 +++---- drivers/pcmcia/cardbus.c | 5 ++--- include/linux/pci.h | 4 ++++ 8 files changed, 23 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 5ed2dcaa8e27..5db6f1839dad 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -462,18 +462,15 @@ static void enable_slot(struct acpiphp_slot *slot) acpiphp_rescan_slot(slot); max = acpiphp_max_busnr(bus); for (pass = 0; pass < 2; pass++) { - list_for_each_entry(dev, &bus->devices, bus_list) { + for_each_pci_bridge(dev, bus) { if (PCI_SLOT(dev->devfn) != slot->device) continue; - if (pci_is_bridge(dev)) { - max = pci_scan_bridge(bus, dev, max, pass); - if (pass && dev->subordinate) { - check_hotplug_bridge(slot, dev); - pcibios_resource_survey_bus(dev->subordinate); - __pci_bus_size_bridges(dev->subordinate, - &add_list); - } + max = pci_scan_bridge(bus, dev, max, pass); + if (pass && dev->subordinate) { + check_hotplug_bridge(slot, dev); + pcibios_resource_survey_bus(dev->subordinate); + __pci_bus_size_bridges(dev->subordinate, &add_list); } } } diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c index 80c80017197d..f616358fa938 100644 --- a/drivers/pci/hotplug/cpci_hotplug_pci.c +++ b/drivers/pci/hotplug/cpci_hotplug_pci.c @@ -286,14 +286,11 @@ int cpci_configure_slot(struct slot *slot) } parent = slot->dev->bus; - list_for_each_entry(dev, &parent->devices, bus_list) { - if (PCI_SLOT(dev->devfn) != PCI_SLOT(slot->devfn)) - continue; - if (pci_is_bridge(dev)) + for_each_pci_bridge(dev, parent) { + if (PCI_SLOT(dev->devfn) == PCI_SLOT(slot->devfn)) pci_hp_add_bridge(dev); } - pci_assign_unassigned_bridge_resources(parent->self); pci_bus_add_devices(parent); diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 19f30a9f461d..c3af027ee1a6 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -60,9 +60,8 @@ int pciehp_configure_device(struct slot *p_slot) goto out; } - list_for_each_entry(dev, &parent->devices, bus_list) - if (pci_is_bridge(dev)) - pci_hp_add_bridge(dev); + for_each_pci_bridge(dev, parent) + pci_hp_add_bridge(dev); pci_assign_unassigned_bridge_resources(bridge); pcie_bus_configure_settings(parent); diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index f8cd3a27e351..ea63db58b4b1 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -61,10 +61,8 @@ int shpchp_configure_device(struct slot *p_slot) goto out; } - list_for_each_entry(dev, &parent->devices, bus_list) { - if (PCI_SLOT(dev->devfn) != p_slot->device) - continue; - if (pci_is_bridge(dev)) + for_each_pci_bridge(dev, parent) { + if (PCI_SLOT(dev->devfn) == p_slot->device) pci_hp_add_bridge(dev); } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ff94b69738a8..cdc2f83c11c5 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2421,10 +2421,8 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus) } for (pass = 0; pass < 2; pass++) - list_for_each_entry(dev, &bus->devices, bus_list) { - if (pci_is_bridge(dev)) - max = pci_scan_bridge(bus, dev, max, pass); - } + for_each_pci_bridge(dev, bus) + max = pci_scan_bridge(bus, dev, max, pass); /* * Make sure a hotplug bridge has at least the minimum requested diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 958da7db9033..7ca03407404c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1921,10 +1921,9 @@ void pci_assign_unassigned_bus_resources(struct pci_bus *bus) want additional resources */ down_read(&pci_bus_sem); - list_for_each_entry(dev, &bus->devices, bus_list) - if (pci_is_bridge(dev) && pci_has_subordinate(dev)) - __pci_bus_size_bridges(dev->subordinate, - &add_list); + for_each_pci_bridge(dev, bus) + if (pci_has_subordinate(dev)) + __pci_bus_size_bridges(dev->subordinate, &add_list); up_read(&pci_bus_sem); __pci_bus_assign_resources(bus, &add_list, NULL); BUG_ON(!list_empty(&add_list)); diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index 4fe4cc4ae19a..5c0170597037 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -77,9 +77,8 @@ int __ref cb_alloc(struct pcmcia_socket *s) max = bus->busn_res.start; for (pass = 0; pass < 2; pass++) - list_for_each_entry(dev, &bus->devices, bus_list) - if (pci_is_bridge(dev)) - max = pci_scan_bridge(bus, dev, max, pass); + for_each_pci_bridge(dev, bus) + max = pci_scan_bridge(bus, dev, max, pass); /* * Size all resources below the CardBus controller. diff --git a/include/linux/pci.h b/include/linux/pci.h index f4f8ee5a7362..3dbe947b4152 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -596,6 +596,10 @@ static inline bool pci_is_bridge(struct pci_dev *dev) dev->hdr_type == PCI_HEADER_TYPE_CARDBUS; } +#define for_each_pci_bridge(dev, bus) \ + list_for_each_entry(dev, &bus->devices, bus_list) \ + if (!pci_is_bridge(dev)) {} else + static inline struct pci_dev *pci_upstream_bridge(struct pci_dev *dev) { dev = pci_physfn(dev); -- cgit v1.2.3-59-g8ed1b From a5fd505092863c520ddf26bd188716b532c1efcf Mon Sep 17 00:00:00 2001 From: Chao Yu Date: Mon, 6 Nov 2017 22:51:45 +0800 Subject: f2fs: trace checkpoint reason in fsync() This patch slightly changes need_do_checkpoint to return the detail info that indicates why we need do checkpoint, then caller could print it with trace message. Signed-off-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 12 ++++++++++++ fs/f2fs/file.c | 34 ++++++++++++++++++---------------- include/trace/events/f2fs.h | 24 ++++++++++++++++++------ 3 files changed, 48 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index cc46c22192ee..5c379a8ea075 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -913,6 +913,18 @@ enum need_lock_type { LOCK_RETRY, }; +enum cp_reason_type { + CP_NO_NEEDED, + CP_NON_REGULAR, + CP_HARDLINK, + CP_SB_NEED_CP, + CP_WRONG_PINO, + CP_NO_SPC_ROLL, + CP_NODE_NEED_CP, + CP_FASTBOOT_MODE, + CP_SPEC_LOG_NUM, +}; + enum iostat_type { APP_DIRECT_IO, /* app direct IOs */ APP_BUFFERED_IO, /* app buffered IOs */ diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 847e933e4a84..8742c028acc6 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -144,27 +144,29 @@ static int get_parent_ino(struct inode *inode, nid_t *pino) return 1; } -static inline bool need_do_checkpoint(struct inode *inode) +static inline enum cp_reason_type need_do_checkpoint(struct inode *inode) { struct f2fs_sb_info *sbi = F2FS_I_SB(inode); - bool need_cp = false; + enum cp_reason_type cp_reason = CP_NO_NEEDED; - if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) - need_cp = true; + if (!S_ISREG(inode->i_mode)) + cp_reason = CP_NON_REGULAR; + else if (inode->i_nlink != 1) + cp_reason = CP_HARDLINK; else if (is_sbi_flag_set(sbi, SBI_NEED_CP)) - need_cp = true; + cp_reason = CP_SB_NEED_CP; else if (file_wrong_pino(inode)) - need_cp = true; + cp_reason = CP_WRONG_PINO; else if (!space_for_roll_forward(sbi)) - need_cp = true; + cp_reason = CP_NO_SPC_ROLL; else if (!is_checkpointed_node(sbi, F2FS_I(inode)->i_pino)) - need_cp = true; + cp_reason = CP_NODE_NEED_CP; else if (test_opt(sbi, FASTBOOT)) - need_cp = true; + cp_reason = CP_FASTBOOT_MODE; else if (sbi->active_logs == 2) - need_cp = true; + cp_reason = CP_SPEC_LOG_NUM; - return need_cp; + return cp_reason; } static bool need_inode_page_update(struct f2fs_sb_info *sbi, nid_t ino) @@ -199,7 +201,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, struct f2fs_sb_info *sbi = F2FS_I_SB(inode); nid_t ino = inode->i_ino; int ret = 0; - bool need_cp = false; + enum cp_reason_type cp_reason = 0; struct writeback_control wbc = { .sync_mode = WB_SYNC_ALL, .nr_to_write = LONG_MAX, @@ -218,7 +220,7 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end, clear_inode_flag(inode, FI_NEED_IPU); if (ret) { - trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); + trace_f2fs_sync_file_exit(inode, cp_reason, datasync, ret); return ret; } @@ -249,10 +251,10 @@ go_write: * sudden-power-off. */ down_read(&F2FS_I(inode)->i_sem); - need_cp = need_do_checkpoint(inode); + cp_reason = need_do_checkpoint(inode); up_read(&F2FS_I(inode)->i_sem); - if (need_cp) { + if (cp_reason) { /* all the dirty node pages should be flushed for POR */ ret = f2fs_sync_fs(inode->i_sb, 1); @@ -309,7 +311,7 @@ flush_out: } f2fs_update_time(sbi, REQ_TIME); out: - trace_f2fs_sync_file_exit(inode, need_cp, datasync, ret); + trace_f2fs_sync_file_exit(inode, cp_reason, datasync, ret); f2fs_trace_ios(NULL, 1); return ret; } diff --git a/include/trace/events/f2fs.h b/include/trace/events/f2fs.h index 009745564214..30d30399db02 100644 --- a/include/trace/events/f2fs.h +++ b/include/trace/events/f2fs.h @@ -136,6 +136,18 @@ TRACE_DEFINE_ENUM(CP_TRIMMED); { CP_UMOUNT, "Umount" }, \ { CP_TRIMMED, "Trimmed" }) +#define show_fsync_cpreason(type) \ + __print_symbolic(type, \ + { CP_NO_NEEDED, "no needed" }, \ + { CP_NON_REGULAR, "non regular" }, \ + { CP_HARDLINK, "hardlink" }, \ + { CP_SB_NEED_CP, "sb needs cp" }, \ + { CP_WRONG_PINO, "wrong pino" }, \ + { CP_NO_SPC_ROLL, "no space roll forward" }, \ + { CP_NODE_NEED_CP, "node needs cp" }, \ + { CP_FASTBOOT_MODE, "fastboot mode" }, \ + { CP_SPEC_LOG_NUM, "log type is 2" }) + struct victim_sel_policy; struct f2fs_map_blocks; @@ -210,14 +222,14 @@ DEFINE_EVENT(f2fs__inode, f2fs_sync_file_enter, TRACE_EVENT(f2fs_sync_file_exit, - TP_PROTO(struct inode *inode, int need_cp, int datasync, int ret), + TP_PROTO(struct inode *inode, int cp_reason, int datasync, int ret), - TP_ARGS(inode, need_cp, datasync, ret), + TP_ARGS(inode, cp_reason, datasync, ret), TP_STRUCT__entry( __field(dev_t, dev) __field(ino_t, ino) - __field(int, need_cp) + __field(int, cp_reason) __field(int, datasync) __field(int, ret) ), @@ -225,15 +237,15 @@ TRACE_EVENT(f2fs_sync_file_exit, TP_fast_assign( __entry->dev = inode->i_sb->s_dev; __entry->ino = inode->i_ino; - __entry->need_cp = need_cp; + __entry->cp_reason = cp_reason; __entry->datasync = datasync; __entry->ret = ret; ), - TP_printk("dev = (%d,%d), ino = %lu, checkpoint is %s, " + TP_printk("dev = (%d,%d), ino = %lu, cp_reason: %s, " "datasync = %d, ret = %d", show_dev_ino(__entry), - __entry->need_cp ? "needed" : "not needed", + show_fsync_cpreason(__entry->cp_reason), __entry->datasync, __entry->ret) ); -- cgit v1.2.3-59-g8ed1b From 4a75aeacda3c2455954596593d89187df5420d0a Mon Sep 17 00:00:00 2001 From: James Morse Date: Mon, 6 Nov 2017 18:44:27 +0000 Subject: ACPI / APEI: Remove arch_apei_flush_tlb_one() Nothing calls arch_apei_flush_tlb_one() anymore, instead relying on __set_pte_vaddr() to do the invalidation when called from clear_fixmap() Remove arch_apei_flush_tlb_one(). Signed-off-by: James Morse Reviewed-by: Borislav Petkov Signed-off-by: Rafael J. Wysocki Cc: All applicable --- arch/x86/kernel/acpi/apei.c | 5 ----- include/acpi/apei.h | 1 - 2 files changed, 6 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/acpi/apei.c b/arch/x86/kernel/acpi/apei.c index ea3046e0b0cf..bb8d300fecbd 100644 --- a/arch/x86/kernel/acpi/apei.c +++ b/arch/x86/kernel/acpi/apei.c @@ -52,8 +52,3 @@ void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err) apei_mce_report_mem_error(sev, mem_err); #endif } - -void arch_apei_flush_tlb_one(unsigned long addr) -{ - __flush_tlb_one(addr); -} diff --git a/include/acpi/apei.h b/include/acpi/apei.h index c46694abea28..82c451698c98 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -50,7 +50,6 @@ int erst_clear(u64 record_id); int arch_apei_enable_cmcff(struct acpi_hest_header *hest_hdr, void *data); void arch_apei_report_mem_error(int sev, struct cper_sec_mem_err *mem_err); -void arch_apei_flush_tlb_one(unsigned long addr); #endif #endif -- cgit v1.2.3-59-g8ed1b From 6f27f4f97ee8cbec99b429b653333f4e781a47a1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Nov 2017 10:57:38 +0200 Subject: usb: core: add Status Type definitions USB 3.1 added a PTM_STATUS type. Let's add a define for it and following patches will let usb_get_status() accept the new argument. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 4 ++-- include/uapi/linux/usb/ch9.h | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 5e8379b42f47..f35cbfa2b87b 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -946,8 +946,8 @@ int usb_get_status(struct usb_device *dev, int type, int target, void *data) return -ENOMEM; ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, status, - sizeof(*status), USB_CTRL_GET_TIMEOUT); + USB_REQ_GET_STATUS, USB_DIR_IN | type, USB_STATUS_TYPE_STANDARD, + target, status, sizeof(*status), USB_CTRL_GET_TIMEOUT); if (ret == 2) { *(u16 *) data = le16_to_cpu(*status); diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index 2a5d63040a0b..b2167e89ae6e 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -143,6 +143,10 @@ #define TEST_PACKET 4 #define TEST_FORCE_EN 5 +/* Status Type */ +#define USB_STATUS_TYPE_STANDARD 0 +#define USB_STATUS_TYPE_PTM 1 + /* * New Feature Selectors as added by USB 3.0 * See USB 3.0 spec Table 9-7 -- cgit v1.2.3-59-g8ed1b From 3c377ef1000d57cb1faf8b86ea77cfa47141db33 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Nov 2017 10:57:39 +0200 Subject: usb: core: rename usb_get_status() 'type' argument to 'recip' This makes it a lot clearer that we're expecting a recipient as the argument. A follow-up patch will use the argument 'type' as the status type selector (standard or ptm). Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 6 +++--- include/linux/usb.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index f35cbfa2b87b..7e95db70bba0 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -918,7 +918,7 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) /** * usb_get_status - issues a GET_STATUS call * @dev: the device whose status is being checked - * @type: USB_RECIP_*; for device, interface, or endpoint + * @recip: USB_RECIP_*; for device, interface, or endpoint * @target: zero (for device), else interface or endpoint number * @data: pointer to two bytes of bitmap data * Context: !in_interrupt () @@ -937,7 +937,7 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) * Returns 0 and the status value in *@data (in host byte order) on success, * or else the status code from the underlying usb_control_msg() call. */ -int usb_get_status(struct usb_device *dev, int type, int target, void *data) +int usb_get_status(struct usb_device *dev, int recip, int target, void *data) { int ret; __le16 *status = kmalloc(sizeof(*status), GFP_KERNEL); @@ -946,7 +946,7 @@ int usb_get_status(struct usb_device *dev, int type, int target, void *data) return -ENOMEM; ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), - USB_REQ_GET_STATUS, USB_DIR_IN | type, USB_STATUS_TYPE_STANDARD, + USB_REQ_GET_STATUS, USB_DIR_IN | recip, USB_STATUS_TYPE_STANDARD, target, status, sizeof(*status), USB_CTRL_GET_TIMEOUT); if (ret == 2) { diff --git a/include/linux/usb.h b/include/linux/usb.h index cb9fbd54386e..2e2c1d40081b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1766,7 +1766,7 @@ extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, extern int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, unsigned char descindex, void *buf, int size); extern int usb_get_status(struct usb_device *dev, - int type, int target, void *data); + int recip, int target, void *data); extern int usb_string(struct usb_device *dev, int index, char *buf, size_t size); -- cgit v1.2.3-59-g8ed1b From d9e1e1484ade396b3a979ba6c68798dbaceed1b9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Nov 2017 10:57:40 +0200 Subject: usb: core: introduce a new usb_get_std_status() helper This new helper is a simple wrapper around usb_get_status(). This patch is in preparation to adding support for fetching PTM_STATUS types. No functional changes. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/staging/wlan-ng/hfa384x_usb.c | 4 ++-- drivers/usb/core/driver.c | 4 ++-- drivers/usb/core/hub.c | 8 ++++---- drivers/usb/misc/usbtest.c | 8 ++++---- include/linux/usb.h | 7 +++++++ 5 files changed, 19 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/staging/wlan-ng/hfa384x_usb.c b/drivers/staging/wlan-ng/hfa384x_usb.c index d1e8218f96fb..d24f5eba9375 100644 --- a/drivers/staging/wlan-ng/hfa384x_usb.c +++ b/drivers/staging/wlan-ng/hfa384x_usb.c @@ -2460,7 +2460,7 @@ int hfa384x_drvr_start(struct hfa384x *hw) * ok */ result = - usb_get_status(hw->usb, USB_RECIP_ENDPOINT, hw->endp_in, &status); + usb_get_std_status(hw->usb, USB_RECIP_ENDPOINT, hw->endp_in, &status); if (result < 0) { netdev_err(hw->wlandev->netdev, "Cannot get bulk in endpoint status.\n"); goto done; @@ -2469,7 +2469,7 @@ int hfa384x_drvr_start(struct hfa384x *hw) netdev_err(hw->wlandev->netdev, "Failed to reset bulk in endpoint.\n"); result = - usb_get_status(hw->usb, USB_RECIP_ENDPOINT, hw->endp_out, &status); + usb_get_std_status(hw->usb, USB_RECIP_ENDPOINT, hw->endp_out, &status); if (result < 0) { netdev_err(hw->wlandev->netdev, "Cannot get bulk out endpoint status.\n"); goto done; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 4b941a3a746e..64262a9a8829 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1340,8 +1340,8 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) int err; u16 devstat; - err = usb_get_status(udev, USB_RECIP_DEVICE, 0, - &devstat); + err = usb_get_std_status(udev, USB_RECIP_DEVICE, 0, + &devstat); if (err) { dev_err(&udev->dev, "Failed to suspend device, error %d\n", diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3edcaf105692..7ccdd3d4db84 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1482,7 +1482,7 @@ static int hub_configure(struct usb_hub *hub, /* power budgeting mostly matters with bus-powered hubs, * and battery-powered root hubs (may provide just 8 mA). */ - ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus); + ret = usb_get_std_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus); if (ret) { message = "can't get hub status"; goto fail; @@ -3279,7 +3279,7 @@ static int finish_port_resume(struct usb_device *udev) */ if (status == 0) { devstatus = 0; - status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); + status = usb_get_std_status(udev, USB_RECIP_DEVICE, 0, &devstatus); /* If a normal resume failed, try doing a reset-resume */ if (status && !udev->reset_resume && udev->persist_enabled) { @@ -3303,7 +3303,7 @@ static int finish_port_resume(struct usb_device *udev) if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) status = usb_disable_remote_wakeup(udev); } else { - status = usb_get_status(udev, USB_RECIP_INTERFACE, 0, + status = usb_get_std_status(udev, USB_RECIP_INTERFACE, 0, &devstatus); if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP | USB_INTRF_STAT_FUNC_RW)) @@ -4866,7 +4866,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, && udev->bus_mA <= unit_load) { u16 devstat; - status = usb_get_status(udev, USB_RECIP_DEVICE, 0, + status = usb_get_std_status(udev, USB_RECIP_DEVICE, 0, &devstat); if (status) { dev_dbg(&udev->dev, "get status %d ?\n", status); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index aeda01f037c1..203f29894bce 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -1016,7 +1016,7 @@ static int ch9_postconfig(struct usbtest_dev *dev) /* FIXME fetch strings from at least the device descriptor */ /* [9.4.5] get_status always works */ - retval = usb_get_status(udev, USB_RECIP_DEVICE, 0, dev->buf); + retval = usb_get_std_status(udev, USB_RECIP_DEVICE, 0, dev->buf); if (retval) { dev_err(&iface->dev, "get dev status --> %d\n", retval); return retval; @@ -1026,7 +1026,7 @@ static int ch9_postconfig(struct usbtest_dev *dev) * the device's remote wakeup feature ... if we can, test that here */ - retval = usb_get_status(udev, USB_RECIP_INTERFACE, + retval = usb_get_std_status(udev, USB_RECIP_INTERFACE, iface->altsetting[0].desc.bInterfaceNumber, dev->buf); if (retval) { dev_err(&iface->dev, "get interface status --> %d\n", retval); @@ -1615,7 +1615,7 @@ static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) u16 status; /* shouldn't look or act halted */ - retval = usb_get_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status); + retval = usb_get_std_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status); if (retval < 0) { ERROR(tdev, "ep %02x couldn't get no-halt status, %d\n", ep, retval); @@ -1637,7 +1637,7 @@ static int verify_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) u16 status; /* should look and act halted */ - retval = usb_get_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status); + retval = usb_get_std_status(urb->dev, USB_RECIP_ENDPOINT, ep, &status); if (retval < 0) { ERROR(tdev, "ep %02x couldn't get halt status, %d\n", ep, retval); diff --git a/include/linux/usb.h b/include/linux/usb.h index 2e2c1d40081b..e86b6a2a35e4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1767,6 +1767,13 @@ extern int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, unsigned char descindex, void *buf, int size); extern int usb_get_status(struct usb_device *dev, int recip, int target, void *data); + +static inline int usb_get_std_status(struct usb_device *dev, + int recip, int target, void *data) +{ + return usb_get_status(dev, recip, target, data); +} + extern int usb_string(struct usb_device *dev, int index, char *buf, size_t size); -- cgit v1.2.3-59-g8ed1b From 2e43f0fe379c317d1ca27a69d860e397682ce957 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Nov 2017 10:57:41 +0200 Subject: usb: core: add a 'type' parameter to usb_get_status() This new 'type' parameter will allows interested drivers to request for PTM status or Standard status. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/message.c | 47 +++++++++++++++++++++++++++++++++++++++------- include/linux/usb.h | 5 +++-- 2 files changed, 43 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 7e95db70bba0..ebaea514161f 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -919,6 +919,7 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) * usb_get_status - issues a GET_STATUS call * @dev: the device whose status is being checked * @recip: USB_RECIP_*; for device, interface, or endpoint + * @type: USB_STATUS_TYPE_*; for standard or PTM status types * @target: zero (for device), else interface or endpoint number * @data: pointer to two bytes of bitmap data * Context: !in_interrupt () @@ -937,24 +938,56 @@ int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) * Returns 0 and the status value in *@data (in host byte order) on success, * or else the status code from the underlying usb_control_msg() call. */ -int usb_get_status(struct usb_device *dev, int recip, int target, void *data) +int usb_get_status(struct usb_device *dev, int recip, int type, int target, + void *data) { int ret; - __le16 *status = kmalloc(sizeof(*status), GFP_KERNEL); + void *status; + int length; + + switch (type) { + case USB_STATUS_TYPE_STANDARD: + length = 2; + break; + case USB_STATUS_TYPE_PTM: + if (recip != USB_RECIP_DEVICE) + return -EINVAL; + + length = 4; + break; + default: + return -EINVAL; + } + status = kmalloc(length, GFP_KERNEL); if (!status) return -ENOMEM; ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), USB_REQ_GET_STATUS, USB_DIR_IN | recip, USB_STATUS_TYPE_STANDARD, - target, status, sizeof(*status), USB_CTRL_GET_TIMEOUT); + target, status, length, USB_CTRL_GET_TIMEOUT); + + switch (ret) { + case 4: + if (type != USB_STATUS_TYPE_PTM) { + ret = -EIO; + break; + } + + *(u32 *) data = le32_to_cpu(*(__le32 *) status); + break; + case 2: + if (type != USB_STATUS_TYPE_STANDARD) { + ret = -EIO; + break; + } - if (ret == 2) { - *(u16 *) data = le16_to_cpu(*status); - ret = 0; - } else if (ret >= 0) { + *(u16 *) data = le16_to_cpu(*(__le16 *) status); + break; + default: ret = -EIO; } + kfree(status); return ret; } diff --git a/include/linux/usb.h b/include/linux/usb.h index e86b6a2a35e4..6228d8177bfe 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1766,12 +1766,13 @@ extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, extern int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, unsigned char descindex, void *buf, int size); extern int usb_get_status(struct usb_device *dev, - int recip, int target, void *data); + int recip, int type, int target, void *data); static inline int usb_get_std_status(struct usb_device *dev, int recip, int target, void *data) { - return usb_get_status(dev, recip, target, data); + return usb_get_status(dev, recip, USB_STATUS_TYPE_STANDARD, target, + data); } extern int usb_string(struct usb_device *dev, int index, -- cgit v1.2.3-59-g8ed1b From f8f3e4acbde3dff2e433d02476034606e07ac742 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Nov 2017 10:57:42 +0200 Subject: usb: core: add a new usb_get_ptm_status() helper Drivers who are interested in the PTM status stype, should use this new helper to make sure they issue the correct GetStatus message. Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/usb.h b/include/linux/usb.h index 6228d8177bfe..3555936025ec 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1775,6 +1775,12 @@ static inline int usb_get_std_status(struct usb_device *dev, data); } +static inline int usb_get_ptm_status(struct usb_device *dev, void *data) +{ + return usb_get_status(dev, USB_RECIP_DEVICE, USB_STATUS_TYPE_PTM, + 0, data); +} + extern int usb_string(struct usb_device *dev, int index, char *buf, size_t size); -- cgit v1.2.3-59-g8ed1b From 5b143d2a6edeae59700420c948f7d793da3356a8 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdelin Date: Wed, 1 Nov 2017 13:14:33 -0400 Subject: bus: add driver for the Technologic Systems NBUS This driver implements a GPIOs bit-banged bus, called the NBUS by Technologic Systems. It is used to communicate with the peripherals in the FPGA on the TS-4600 SoM. Signed-off-by: Sebastien Bourdelin Acked-by: Linus Walleij Signed-off-by: Arnd Bergmann --- drivers/bus/Kconfig | 8 ++ drivers/bus/Makefile | 1 + drivers/bus/ts-nbus.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/ts-nbus.h | 18 +++ 4 files changed, 402 insertions(+) create mode 100644 drivers/bus/ts-nbus.c create mode 100644 include/linux/ts-nbus.h (limited to 'include') diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index ae3d8f3444b9..854348e646b3 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -157,6 +157,14 @@ config TEGRA_GMI Driver for the Tegra Generic Memory Interface bus which can be used to attach devices such as NOR, UART, FPGA and more. +config TS_NBUS + tristate "Technologic Systems NBUS Driver" + depends on SOC_IMX28 + depends on OF_GPIO && PWM + help + Driver for the Technologic Systems NBUS which is used to interface + with the peripherals in the FPGA of the TS-4600 SoM. + config UNIPHIER_SYSTEM_BUS tristate "UniPhier System Bus driver" depends on ARCH_UNIPHIER && OF diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index cc6364bec054..72377f77651c 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o obj-$(CONFIG_TEGRA_ACONNECT) += tegra-aconnect.o obj-$(CONFIG_TEGRA_GMI) += tegra-gmi.o +obj-$(CONFIG_TS_NBUS) += ts-nbus.o obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c new file mode 100644 index 000000000000..073fd9011154 --- /dev/null +++ b/drivers/bus/ts-nbus.c @@ -0,0 +1,375 @@ +/* + * NBUS driver for TS-4600 based boards + * + * Copyright (c) 2016 - Savoir-faire Linux + * Author: Sebastien Bourdelin + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * This driver implements a GPIOs bit-banged bus, called the NBUS by Technologic + * Systems. It is used to communicate with the peripherals in the FPGA on the + * TS-4600 SoM. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TS_NBUS_DIRECTION_IN 0 +#define TS_NBUS_DIRECTION_OUT 1 +#define TS_NBUS_WRITE_ADR 0 +#define TS_NBUS_WRITE_VAL 1 + +struct ts_nbus { + struct pwm_device *pwm; + struct gpio_descs *data; + struct gpio_desc *csn; + struct gpio_desc *txrx; + struct gpio_desc *strobe; + struct gpio_desc *ale; + struct gpio_desc *rdy; + struct mutex lock; +}; + +/* + * request all gpios required by the bus. + */ +static int ts_nbus_init_pdata(struct platform_device *pdev, struct ts_nbus + *ts_nbus) +{ + ts_nbus->data = devm_gpiod_get_array(&pdev->dev, "ts,data", + GPIOD_OUT_HIGH); + if (IS_ERR(ts_nbus->data)) { + dev_err(&pdev->dev, "failed to retrieve ts,data-gpio from dts\n"); + return PTR_ERR(ts_nbus->data); + } + + ts_nbus->csn = devm_gpiod_get(&pdev->dev, "ts,csn", GPIOD_OUT_HIGH); + if (IS_ERR(ts_nbus->csn)) { + dev_err(&pdev->dev, "failed to retrieve ts,csn-gpio from dts\n"); + return PTR_ERR(ts_nbus->csn); + } + + ts_nbus->txrx = devm_gpiod_get(&pdev->dev, "ts,txrx", GPIOD_OUT_HIGH); + if (IS_ERR(ts_nbus->txrx)) { + dev_err(&pdev->dev, "failed to retrieve ts,txrx-gpio from dts\n"); + return PTR_ERR(ts_nbus->txrx); + } + + ts_nbus->strobe = devm_gpiod_get(&pdev->dev, "ts,strobe", GPIOD_OUT_HIGH); + if (IS_ERR(ts_nbus->strobe)) { + dev_err(&pdev->dev, "failed to retrieve ts,strobe-gpio from dts\n"); + return PTR_ERR(ts_nbus->strobe); + } + + ts_nbus->ale = devm_gpiod_get(&pdev->dev, "ts,ale", GPIOD_OUT_HIGH); + if (IS_ERR(ts_nbus->ale)) { + dev_err(&pdev->dev, "failed to retrieve ts,ale-gpio from dts\n"); + return PTR_ERR(ts_nbus->ale); + } + + ts_nbus->rdy = devm_gpiod_get(&pdev->dev, "ts,rdy", GPIOD_IN); + if (IS_ERR(ts_nbus->rdy)) { + dev_err(&pdev->dev, "failed to retrieve ts,rdy-gpio from dts\n"); + return PTR_ERR(ts_nbus->rdy); + } + + return 0; +} + +/* + * the data gpios are used for reading and writing values, their directions + * should be adjusted accordingly. + */ +static void ts_nbus_set_direction(struct ts_nbus *ts_nbus, int direction) +{ + int i; + + for (i = 0; i < 8; i++) { + if (direction == TS_NBUS_DIRECTION_IN) + gpiod_direction_input(ts_nbus->data->desc[i]); + else + /* when used as output the default state of the data + * lines are set to high */ + gpiod_direction_output(ts_nbus->data->desc[i], 1); + } +} + +/* + * reset the bus in its initial state. + * The data, csn, strobe and ale lines must be zero'ed to let the FPGA knows a + * new transaction can be process. + */ +static void ts_nbus_reset_bus(struct ts_nbus *ts_nbus) +{ + int i; + int values[8]; + + for (i = 0; i < 8; i++) + values[i] = 0; + + gpiod_set_array_value_cansleep(8, ts_nbus->data->desc, values); + gpiod_set_value_cansleep(ts_nbus->csn, 0); + gpiod_set_value_cansleep(ts_nbus->strobe, 0); + gpiod_set_value_cansleep(ts_nbus->ale, 0); +} + +/* + * let the FPGA knows it can process. + */ +static void ts_nbus_start_transaction(struct ts_nbus *ts_nbus) +{ + gpiod_set_value_cansleep(ts_nbus->strobe, 1); +} + +/* + * read a byte value from the data gpios. + * return 0 on success or negative errno on failure. + */ +static int ts_nbus_read_byte(struct ts_nbus *ts_nbus, u8 *val) +{ + struct gpio_descs *gpios = ts_nbus->data; + int ret, i; + + *val = 0; + for (i = 0; i < 8; i++) { + ret = gpiod_get_value_cansleep(gpios->desc[i]); + if (ret < 0) + return ret; + if (ret) + *val |= BIT(i); + } + + return 0; +} + +/* + * set the data gpios accordingly to the byte value. + */ +static void ts_nbus_write_byte(struct ts_nbus *ts_nbus, u8 byte) +{ + struct gpio_descs *gpios = ts_nbus->data; + int i; + int values[8]; + + for (i = 0; i < 8; i++) + if (byte & BIT(i)) + values[i] = 1; + else + values[i] = 0; + + gpiod_set_array_value_cansleep(8, gpios->desc, values); +} + +/* + * reading the bus consists of resetting the bus, then notifying the FPGA to + * send the data in the data gpios and return the read value. + * return 0 on success or negative errno on failure. + */ +static int ts_nbus_read_bus(struct ts_nbus *ts_nbus, u8 *val) +{ + ts_nbus_reset_bus(ts_nbus); + ts_nbus_start_transaction(ts_nbus); + + return ts_nbus_read_byte(ts_nbus, val); +} + +/* + * writing to the bus consists of resetting the bus, then define the type of + * command (address/value), write the data and notify the FPGA to retrieve the + * value in the data gpios. + */ +static void ts_nbus_write_bus(struct ts_nbus *ts_nbus, int cmd, u8 val) +{ + ts_nbus_reset_bus(ts_nbus); + + if (cmd == TS_NBUS_WRITE_ADR) + gpiod_set_value_cansleep(ts_nbus->ale, 1); + + ts_nbus_write_byte(ts_nbus, val); + ts_nbus_start_transaction(ts_nbus); +} + +/* + * read the value in the FPGA register at the given address. + * return 0 on success or negative errno on failure. + */ +int ts_nbus_read(struct ts_nbus *ts_nbus, u8 adr, u16 *val) +{ + int ret, i; + u8 byte; + + /* bus access must be atomic */ + mutex_lock(&ts_nbus->lock); + + /* set the bus in read mode */ + gpiod_set_value_cansleep(ts_nbus->txrx, 0); + + /* write address */ + ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, adr); + + /* set the data gpios direction as input before reading */ + ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_IN); + + /* reading value MSB first */ + do { + *val = 0; + byte = 0; + for (i = 1; i >= 0; i--) { + /* read a byte from the bus, leave on error */ + ret = ts_nbus_read_bus(ts_nbus, &byte); + if (ret < 0) + goto err; + + /* append the byte read to the final value */ + *val |= byte << (i * 8); + } + gpiod_set_value_cansleep(ts_nbus->csn, 1); + ret = gpiod_get_value_cansleep(ts_nbus->rdy); + } while (ret); + +err: + /* restore the data gpios direction as output after reading */ + ts_nbus_set_direction(ts_nbus, TS_NBUS_DIRECTION_OUT); + + mutex_unlock(&ts_nbus->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(ts_nbus_read); + +/* + * write the desired value in the FPGA register at the given address. + */ +int ts_nbus_write(struct ts_nbus *ts_nbus, u8 adr, u16 val) +{ + int i; + + /* bus access must be atomic */ + mutex_lock(&ts_nbus->lock); + + /* set the bus in write mode */ + gpiod_set_value_cansleep(ts_nbus->txrx, 1); + + /* write address */ + ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_ADR, adr); + + /* writing value MSB first */ + for (i = 1; i >= 0; i--) + ts_nbus_write_bus(ts_nbus, TS_NBUS_WRITE_VAL, (u8)(val >> (i * 8))); + + /* wait for completion */ + gpiod_set_value_cansleep(ts_nbus->csn, 1); + while (gpiod_get_value_cansleep(ts_nbus->rdy) != 0) { + gpiod_set_value_cansleep(ts_nbus->csn, 0); + gpiod_set_value_cansleep(ts_nbus->csn, 1); + } + + mutex_unlock(&ts_nbus->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(ts_nbus_write); + +static int ts_nbus_probe(struct platform_device *pdev) +{ + struct pwm_device *pwm; + struct pwm_args pargs; + struct device *dev = &pdev->dev; + struct ts_nbus *ts_nbus; + int ret; + + ts_nbus = devm_kzalloc(dev, sizeof(*ts_nbus), GFP_KERNEL); + if (!ts_nbus) + return -ENOMEM; + + mutex_init(&ts_nbus->lock); + + ret = ts_nbus_init_pdata(pdev, ts_nbus); + if (ret < 0) + return ret; + + pwm = devm_pwm_get(dev, NULL); + if (IS_ERR(pwm)) { + ret = PTR_ERR(pwm); + if (ret != -EPROBE_DEFER) + dev_err(dev, "unable to request PWM\n"); + return ret; + } + + pwm_get_args(pwm, &pargs); + if (!pargs.period) { + dev_err(&pdev->dev, "invalid PWM period\n"); + return -EINVAL; + } + + /* + * FIXME: pwm_apply_args() should be removed when switching to + * the atomic PWM API. + */ + pwm_apply_args(pwm); + ret = pwm_config(pwm, pargs.period, pargs.period); + if (ret < 0) + return ret; + + /* + * we can now start the FPGA and populate the peripherals. + */ + pwm_enable(pwm); + ts_nbus->pwm = pwm; + + /* + * let the child nodes retrieve this instance of the ts-nbus. + */ + dev_set_drvdata(dev, ts_nbus); + + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret < 0) + return ret; + + dev_info(dev, "initialized\n"); + + return 0; +} + +static int ts_nbus_remove(struct platform_device *pdev) +{ + struct ts_nbus *ts_nbus = dev_get_drvdata(&pdev->dev); + + /* shutdown the FPGA */ + mutex_lock(&ts_nbus->lock); + pwm_disable(ts_nbus->pwm); + mutex_unlock(&ts_nbus->lock); + + return 0; +} + +static const struct of_device_id ts_nbus_of_match[] = { + { .compatible = "technologic,ts-nbus", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ts_nbus_of_match); + +static struct platform_driver ts_nbus_driver = { + .probe = ts_nbus_probe, + .remove = ts_nbus_remove, + .driver = { + .name = "ts_nbus", + .of_match_table = ts_nbus_of_match, + }, +}; + +module_platform_driver(ts_nbus_driver); + +MODULE_ALIAS("platform:ts_nbus"); +MODULE_AUTHOR("Sebastien Bourdelin "); +MODULE_DESCRIPTION("Technologic Systems NBUS"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/ts-nbus.h b/include/linux/ts-nbus.h new file mode 100644 index 000000000000..5bd4c822f7cf --- /dev/null +++ b/include/linux/ts-nbus.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016 - Savoir-faire Linux + * Author: Sebastien Bourdelin + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#ifndef _TS_NBUS_H +#define _TS_NBUS_H + +struct ts_nbus; + +extern int ts_nbus_read(struct ts_nbus *ts_nbus, u8 adr, u16 *val); +extern int ts_nbus_write(struct ts_nbus *ts_nbus, u8 adr, u16 val); + +#endif /* _TS_NBUS_H */ -- cgit v1.2.3-59-g8ed1b From 7c8d469877b16d2c1cecf101a0abb7b218db85bc Mon Sep 17 00:00:00 2001 From: Nicolai Stange Date: Tue, 31 Oct 2017 00:15:47 +0100 Subject: debugfs: add support for more elaborate ->d_fsdata Currently, the user provided fops, "real_fops", are stored directly into ->d_fsdata. In order to be able to store more per-file state and thus prepare for more granular file removal protection, wrap the real_fops into a dynamically allocated container struct, debugfs_fsdata. A struct debugfs_fsdata gets allocated at file creation and freed from the newly intoduced ->d_release(). Finally, move the implementation of debugfs_real_fops() out of the public debugfs header such that struct debugfs_fsdata's declaration can be kept private. Signed-off-by: Nicolai Stange Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 12 ++++++++++++ fs/debugfs/inode.c | 22 +++++++++++++++++++--- fs/debugfs/internal.h | 4 ++++ include/linux/debugfs.h | 20 +++----------------- 4 files changed, 38 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 6dabc4a10396..b6f5ddab66bf 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -97,6 +97,18 @@ EXPORT_SYMBOL_GPL(debugfs_use_file_finish); #define F_DENTRY(filp) ((filp)->f_path.dentry) +const struct file_operations *debugfs_real_fops(const struct file *filp) + __must_hold(&debugfs_srcu) +{ + struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata; + /* + * Neither the pointer to the struct file_operations, nor its + * contents ever change -- srcu_dereference() is not needed here. + */ + return fsd->real_fops; +} +EXPORT_SYMBOL_GPL(debugfs_real_fops); + static int open_proxy_open(struct inode *inode, struct file *filp) { const struct dentry *dentry = F_DENTRY(filp); diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index c59f015f386e..a9c3d3e9af39 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -185,6 +185,11 @@ static const struct super_operations debugfs_super_operations = { .evict_inode = debugfs_evict_inode, }; +static void debugfs_release_dentry(struct dentry *dentry) +{ + kfree(dentry->d_fsdata); +} + static struct vfsmount *debugfs_automount(struct path *path) { debugfs_automount_t f; @@ -194,6 +199,7 @@ static struct vfsmount *debugfs_automount(struct path *path) static const struct dentry_operations debugfs_dops = { .d_delete = always_delete_dentry, + .d_release = debugfs_release_dentry, .d_automount = debugfs_automount, }; @@ -341,24 +347,34 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode, { struct dentry *dentry; struct inode *inode; + struct debugfs_fsdata *fsd; + + fsd = kmalloc(sizeof(*fsd), GFP_KERNEL); + if (!fsd) + return NULL; if (!(mode & S_IFMT)) mode |= S_IFREG; BUG_ON(!S_ISREG(mode)); dentry = start_creating(name, parent); - if (IS_ERR(dentry)) + if (IS_ERR(dentry)) { + kfree(fsd); return NULL; + } inode = debugfs_get_inode(dentry->d_sb); - if (unlikely(!inode)) + if (unlikely(!inode)) { + kfree(fsd); return failed_creating(dentry); + } inode->i_mode = mode; inode->i_private = data; inode->i_fop = proxy_fops; - dentry->d_fsdata = (void *)real_fops; + fsd->real_fops = real_fops; + dentry->d_fsdata = fsd; d_instantiate(dentry, inode); fsnotify_create(d_inode(dentry->d_parent), dentry); diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h index b3e8443a1f47..512601eed3ce 100644 --- a/fs/debugfs/internal.h +++ b/fs/debugfs/internal.h @@ -19,4 +19,8 @@ extern const struct file_operations debugfs_noop_file_operations; extern const struct file_operations debugfs_open_proxy_file_operations; extern const struct file_operations debugfs_full_proxy_file_operations; +struct debugfs_fsdata { + const struct file_operations *real_fops; +}; + #endif /* _DEBUGFS_INTERNAL_H_ */ diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index b93efc8feecd..cbee5f4a02a3 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -45,23 +45,6 @@ extern struct dentry *arch_debugfs_dir; extern struct srcu_struct debugfs_srcu; -/** - * debugfs_real_fops - getter for the real file operation - * @filp: a pointer to a struct file - * - * Must only be called under the protection established by - * debugfs_use_file_start(). - */ -static inline const struct file_operations *debugfs_real_fops(const struct file *filp) - __must_hold(&debugfs_srcu) -{ - /* - * Neither the pointer to the struct file_operations, nor its - * contents ever change -- srcu_dereference() is not needed here. - */ - return filp->f_path.dentry->d_fsdata; -} - #define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \ static int __fops ## _open(struct inode *inode, struct file *file) \ { \ @@ -112,6 +95,9 @@ int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu); +const struct file_operations *debugfs_real_fops(const struct file *filp) + __must_hold(&debugfs_srcu); + ssize_t debugfs_attr_read(struct file *file, char __user *buf, size_t len, loff_t *ppos); ssize_t debugfs_attr_write(struct file *file, const char __user *buf, -- cgit v1.2.3-59-g8ed1b From e9117a5a4bf65d8e99f060d356a04d27a60b436d Mon Sep 17 00:00:00 2001 From: Nicolai Stange Date: Tue, 31 Oct 2017 00:15:48 +0100 Subject: debugfs: implement per-file removal protection Since commit 49d200deaa68 ("debugfs: prevent access to removed files' private data"), accesses to a file's private data are protected from concurrent removal by covering all file_operations with a SRCU read section and sychronizing with those before returning from debugfs_remove() by means of synchronize_srcu(). As pointed out by Johannes Berg, there are debugfs files with forever blocking file_operations. Their corresponding SRCU read side sections would block any debugfs_remove() forever as well, even unrelated ones. This results in a livelock. Because a remover can't cancel any indefinite blocking within foreign files, this is a problem. Resolve this by introducing support for more granular protection on a per-file basis. This is implemented by introducing an 'active_users' refcount_t to the per-file struct debugfs_fsdata state. At file creation time, it is set to one and a debugfs_remove() will drop that initial reference. The new debugfs_file_get() and debugfs_file_put(), intended to be used in place of former debugfs_use_file_start() and debugfs_use_file_finish(), increment and decrement it respectively. Once the count drops to zero, debugfs_file_put() will signal a completion which is possibly being waited for from debugfs_remove(). Thus, as long as there is a debugfs_file_get() not yet matched by a corresponding debugfs_file_put() around, debugfs_remove() will block. Actual users of debugfs_use_file_start() and -finish() will get converted to the new debugfs_file_get() and debugfs_file_put() by followup patches. Fixes: 49d200deaa68 ("debugfs: prevent access to removed files' private data") Reported-by: Johannes Berg Signed-off-by: Nicolai Stange Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/debugfs/inode.c | 29 +++++++++++++++++++++++------ fs/debugfs/internal.h | 2 ++ include/linux/debugfs.h | 11 +++++++++++ 4 files changed, 84 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index b6f5ddab66bf..6644bfdea2f8 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -109,6 +109,54 @@ const struct file_operations *debugfs_real_fops(const struct file *filp) } EXPORT_SYMBOL_GPL(debugfs_real_fops); +/** + * debugfs_file_get - mark the beginning of file data access + * @dentry: the dentry object whose data is being accessed. + * + * Up to a matching call to debugfs_file_put(), any successive call + * into the file removing functions debugfs_remove() and + * debugfs_remove_recursive() will block. Since associated private + * file data may only get freed after a successful return of any of + * the removal functions, you may safely access it after a successful + * call to debugfs_file_get() without worrying about lifetime issues. + * + * If -%EIO is returned, the file has already been removed and thus, + * it is not safe to access any of its data. If, on the other hand, + * it is allowed to access the file data, zero is returned. + */ +int debugfs_file_get(struct dentry *dentry) +{ + struct debugfs_fsdata *fsd = dentry->d_fsdata; + + /* Avoid starvation of removers. */ + if (d_unlinked(dentry)) + return -EIO; + + if (!refcount_inc_not_zero(&fsd->active_users)) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(debugfs_file_get); + +/** + * debugfs_file_put - mark the end of file data access + * @dentry: the dentry object formerly passed to + * debugfs_file_get(). + * + * Allow any ongoing concurrent call into debugfs_remove() or + * debugfs_remove_recursive() blocked by a former call to + * debugfs_file_get() to proceed and return to its caller. + */ +void debugfs_file_put(struct dentry *dentry) +{ + struct debugfs_fsdata *fsd = dentry->d_fsdata; + + if (refcount_dec_and_test(&fsd->active_users)) + complete(&fsd->active_users_drained); +} +EXPORT_SYMBOL_GPL(debugfs_file_put); + static int open_proxy_open(struct inode *inode, struct file *filp) { const struct dentry *dentry = F_DENTRY(filp); diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index a9c3d3e9af39..6449eb935540 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -374,6 +374,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode, inode->i_fop = proxy_fops; fsd->real_fops = real_fops; + refcount_set(&fsd->active_users, 1); dentry->d_fsdata = fsd; d_instantiate(dentry, inode); @@ -631,18 +632,34 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, } EXPORT_SYMBOL_GPL(debugfs_create_symlink); +static void __debugfs_remove_file(struct dentry *dentry, struct dentry *parent) +{ + struct debugfs_fsdata *fsd; + + simple_unlink(d_inode(parent), dentry); + d_delete(dentry); + fsd = dentry->d_fsdata; + init_completion(&fsd->active_users_drained); + if (!refcount_dec_and_test(&fsd->active_users)) + wait_for_completion(&fsd->active_users_drained); +} + static int __debugfs_remove(struct dentry *dentry, struct dentry *parent) { int ret = 0; if (simple_positive(dentry)) { dget(dentry); - if (d_is_dir(dentry)) - ret = simple_rmdir(d_inode(parent), dentry); - else - simple_unlink(d_inode(parent), dentry); - if (!ret) - d_delete(dentry); + if (!d_is_reg(dentry)) { + if (d_is_dir(dentry)) + ret = simple_rmdir(d_inode(parent), dentry); + else + simple_unlink(d_inode(parent), dentry); + if (!ret) + d_delete(dentry); + } else { + __debugfs_remove_file(dentry, parent); + } dput(dentry); } return ret; diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h index 512601eed3ce..0eea99432840 100644 --- a/fs/debugfs/internal.h +++ b/fs/debugfs/internal.h @@ -21,6 +21,8 @@ extern const struct file_operations debugfs_full_proxy_file_operations; struct debugfs_fsdata { const struct file_operations *real_fops; + refcount_t active_users; + struct completion active_users_drained; }; #endif /* _DEBUGFS_INTERNAL_H_ */ diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index cbee5f4a02a3..3b914d588148 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -98,6 +98,9 @@ void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu); const struct file_operations *debugfs_real_fops(const struct file *filp) __must_hold(&debugfs_srcu); +int debugfs_file_get(struct dentry *dentry); +void debugfs_file_put(struct dentry *dentry); + ssize_t debugfs_attr_read(struct file *file, char __user *buf, size_t len, loff_t *ppos); ssize_t debugfs_attr_write(struct file *file, const char __user *buf, @@ -236,6 +239,14 @@ static inline void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) { } +static inline int debugfs_file_get(struct dentry *dentry) +{ + return 0; +} + +static inline void debugfs_file_put(struct dentry *dentry) +{ } + static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) { -- cgit v1.2.3-59-g8ed1b From 055ab8e3e3d52e005d2047b14ce63551b3a8b8b5 Mon Sep 17 00:00:00 2001 From: Nicolai Stange Date: Tue, 31 Oct 2017 00:15:49 +0100 Subject: debugfs: debugfs_real_fops(): drop __must_hold sparse annotation Currently, debugfs_real_fops() is annotated with a __must_hold(&debugfs_srcu) sparse annotation. With the conversion of the SRCU based protection of users against concurrent file removals to a per-file refcount based scheme, this becomes wrong. Drop this annotation. Signed-off-by: Nicolai Stange Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 6 +----- include/linux/debugfs.h | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 6644bfdea2f8..08511678b782 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -98,13 +98,9 @@ EXPORT_SYMBOL_GPL(debugfs_use_file_finish); #define F_DENTRY(filp) ((filp)->f_path.dentry) const struct file_operations *debugfs_real_fops(const struct file *filp) - __must_hold(&debugfs_srcu) { struct debugfs_fsdata *fsd = F_DENTRY(filp)->d_fsdata; - /* - * Neither the pointer to the struct file_operations, nor its - * contents ever change -- srcu_dereference() is not needed here. - */ + return fsd->real_fops; } EXPORT_SYMBOL_GPL(debugfs_real_fops); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 3b914d588148..c5eda259b9d6 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -95,8 +95,7 @@ int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu); -const struct file_operations *debugfs_real_fops(const struct file *filp) - __must_hold(&debugfs_srcu); +const struct file_operations *debugfs_real_fops(const struct file *filp); int debugfs_file_get(struct dentry *dentry); void debugfs_file_put(struct dentry *dentry); -- cgit v1.2.3-59-g8ed1b From c9afbec27089cd6b4e621b639f41c7fc726c3bf1 Mon Sep 17 00:00:00 2001 From: Nicolai Stange Date: Tue, 31 Oct 2017 00:15:52 +0100 Subject: debugfs: purge obsolete SRCU based removal protection Purge the SRCU based file removal race protection in favour of the new, refcount based debugfs_file_get()/debugfs_file_put() API. Fixes: 49d200deaa68 ("debugfs: prevent access to removed files' private data") Signed-off-by: Nicolai Stange Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 48 ------------------------------------------------ fs/debugfs/inode.c | 7 ------- include/linux/debugfs.h | 19 ------------------- lib/Kconfig.debug | 1 - 4 files changed, 75 deletions(-) (limited to 'include') diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index d3a972b45ff0..53f5c9a2af88 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "internal.h" @@ -48,53 +47,6 @@ const struct file_operations debugfs_noop_file_operations = { .llseek = noop_llseek, }; -/** - * debugfs_use_file_start - mark the beginning of file data access - * @dentry: the dentry object whose data is being accessed. - * @srcu_idx: a pointer to some memory to store a SRCU index in. - * - * Up to a matching call to debugfs_use_file_finish(), any - * successive call into the file removing functions debugfs_remove() - * and debugfs_remove_recursive() will block. Since associated private - * file data may only get freed after a successful return of any of - * the removal functions, you may safely access it after a successful - * call to debugfs_use_file_start() without worrying about - * lifetime issues. - * - * If -%EIO is returned, the file has already been removed and thus, - * it is not safe to access any of its data. If, on the other hand, - * it is allowed to access the file data, zero is returned. - * - * Regardless of the return code, any call to - * debugfs_use_file_start() must be followed by a matching call - * to debugfs_use_file_finish(). - */ -int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) - __acquires(&debugfs_srcu) -{ - *srcu_idx = srcu_read_lock(&debugfs_srcu); - barrier(); - if (d_unlinked(dentry)) - return -EIO; - return 0; -} -EXPORT_SYMBOL_GPL(debugfs_use_file_start); - -/** - * debugfs_use_file_finish - mark the end of file data access - * @srcu_idx: the SRCU index "created" by a former call to - * debugfs_use_file_start(). - * - * Allow any ongoing concurrent call into debugfs_remove() or - * debugfs_remove_recursive() blocked by a former call to - * debugfs_use_file_start() to proceed and return to its caller. - */ -void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu) -{ - srcu_read_unlock(&debugfs_srcu, srcu_idx); -} -EXPORT_SYMBOL_GPL(debugfs_use_file_finish); - #define F_DENTRY(filp) ((filp)->f_path.dentry) const struct file_operations *debugfs_real_fops(const struct file *filp) diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 6449eb935540..f587aded46b5 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -27,14 +27,11 @@ #include #include #include -#include #include "internal.h" #define DEBUGFS_DEFAULT_MODE 0700 -DEFINE_SRCU(debugfs_srcu); - static struct vfsmount *debugfs_mount; static int debugfs_mount_count; static bool debugfs_registered; @@ -693,8 +690,6 @@ void debugfs_remove(struct dentry *dentry) inode_unlock(d_inode(parent)); if (!ret) simple_release_fs(&debugfs_mount, &debugfs_mount_count); - - synchronize_srcu(&debugfs_srcu); } EXPORT_SYMBOL_GPL(debugfs_remove); @@ -768,8 +763,6 @@ void debugfs_remove_recursive(struct dentry *dentry) if (!__debugfs_remove(child, parent)) simple_release_fs(&debugfs_mount, &debugfs_mount_count); inode_unlock(d_inode(parent)); - - synchronize_srcu(&debugfs_srcu); } EXPORT_SYMBOL_GPL(debugfs_remove_recursive); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index c5eda259b9d6..9f821a43bb0d 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -23,7 +23,6 @@ struct device; struct file_operations; -struct srcu_struct; struct debugfs_blob_wrapper { void *data; @@ -43,8 +42,6 @@ struct debugfs_regset32 { extern struct dentry *arch_debugfs_dir; -extern struct srcu_struct debugfs_srcu; - #define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt) \ static int __fops ## _open(struct inode *inode, struct file *file) \ { \ @@ -90,11 +87,6 @@ struct dentry *debugfs_create_automount(const char *name, void debugfs_remove(struct dentry *dentry); void debugfs_remove_recursive(struct dentry *dentry); -int debugfs_use_file_start(const struct dentry *dentry, int *srcu_idx) - __acquires(&debugfs_srcu); - -void debugfs_use_file_finish(int srcu_idx) __releases(&debugfs_srcu); - const struct file_operations *debugfs_real_fops(const struct file *filp); int debugfs_file_get(struct dentry *dentry); @@ -227,17 +219,6 @@ static inline void debugfs_remove(struct dentry *dentry) static inline void debugfs_remove_recursive(struct dentry *dentry) { } -static inline int debugfs_use_file_start(const struct dentry *dentry, - int *srcu_idx) - __acquires(&debugfs_srcu) -{ - return 0; -} - -static inline void debugfs_use_file_finish(int srcu_idx) - __releases(&debugfs_srcu) -{ } - static inline int debugfs_file_get(struct dentry *dentry) { return 0; diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index dfdad67d8f6c..57fb43f521c3 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -280,7 +280,6 @@ config PAGE_OWNER config DEBUG_FS bool "Debug Filesystem" - select SRCU help debugfs is a virtual file system that kernel developers use to put debugging files into. Enable this option to be able to read and -- cgit v1.2.3-59-g8ed1b From 3bce94fd5f4c05337dedbe218501fb9f8789fc40 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Nov 2017 16:59:23 +0100 Subject: debugfs: add SPDX identifiers to all debugfs files It's good to have SPDX identifiers in all files to make it easier to audit the kernel tree for correct licenses. Update the debugfs files files with the correct SPDX license identifier based on the license text in the file itself. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This work is based on a script and data from Thomas Gleixner, Philippe Ombredanne, and Kate Stewart. Cc: Thomas Gleixner Cc: Kate Stewart Cc: Philippe Ombredanne Cc: Nicolai Stange Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 1 + fs/debugfs/inode.c | 1 + fs/debugfs/internal.h | 1 + include/linux/debugfs.h | 1 + 4 files changed, 4 insertions(+) (limited to 'include') diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 65872340e301..133be3c689af 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * file.c - part of debugfs, a tiny little debug file system * diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 9dca4da059b3..7c107b3b9c93 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * inode.c - part of debugfs, a tiny little debug file system * diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h index cb1e8139c398..b9567541b737 100644 --- a/fs/debugfs/internal.h +++ b/fs/debugfs/internal.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * internal.h - declarations internal to debugfs * diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 9f821a43bb0d..7beab3fd2f1a 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * debugfs.h - a tiny little debug file system * -- cgit v1.2.3-59-g8ed1b From 2b2d8788dd565cbe1ab22da6a1bc63d0934a80eb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 7 Nov 2017 17:01:46 +0100 Subject: debugfs: Remove redundant license text Now that the SPDX tag is in all debugfs files, that identifies the license in a specific and legally-defined manner. So the extra GPL text wording can be removed as it is no longer needed at all. This is done on a quest to remove the 700+ different ways that files in the kernel describe the GPL license text. And there's unneeded stuff like the address (sometimes incorrect) for the FSF which is never needed. No copyright headers or other non-license-description text was removed. Cc: Nicolai Stange Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/file.c | 5 ----- fs/debugfs/inode.c | 5 ----- fs/debugfs/internal.h | 5 ----- include/linux/debugfs.h | 4 ---- 4 files changed, 19 deletions(-) (limited to 'include') diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 133be3c689af..cd12e6576b48 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -5,13 +5,8 @@ * Copyright (C) 2004 Greg Kroah-Hartman * Copyright (C) 2004 IBM Inc. * - * 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. - * * debugfs is for people to use instead of /proc or /sys. * See Documentation/filesystems/ for more details. - * */ #include diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 7c107b3b9c93..63a998c3f252 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -5,13 +5,8 @@ * Copyright (C) 2004 Greg Kroah-Hartman * Copyright (C) 2004 IBM Inc. * - * 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. - * * debugfs is for people to use instead of /proc or /sys. * See ./Documentation/core-api/kernel-api.rst for more details. - * */ #include diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h index b9567541b737..f0d73d86cc1a 100644 --- a/fs/debugfs/internal.h +++ b/fs/debugfs/internal.h @@ -3,11 +3,6 @@ * internal.h - declarations internal to debugfs * * Copyright (C) 2016 Nicolai Stange - * - * 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. - * */ #ifndef _DEBUGFS_INTERNAL_H_ diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 7beab3fd2f1a..f36ecc2a5712 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -5,10 +5,6 @@ * Copyright (C) 2004 Greg Kroah-Hartman * Copyright (C) 2004 IBM Inc. * - * 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. - * * debugfs is for people to use instead of /proc or /sys. * See Documentation/filesystems/ for more details. */ -- cgit v1.2.3-59-g8ed1b From 22700f3c6df55387cec2ee27c533a7b23c76dc51 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Tue, 10 Oct 2017 17:31:43 -0400 Subject: SUNRPC: Improve ordering of transport processing Since it can take a while before a specific thread gets scheduled, it is better to just implement a first come first served queue mechanism. That way, if a thread is already scheduled and is idle, it can pick up the work to do from the queue. Signed-off-by: Trond Myklebust Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc.h | 1 + net/sunrpc/svc_xprt.c | 100 ++++++++++++++------------------------------- 2 files changed, 31 insertions(+), 70 deletions(-) (limited to 'include') diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index 38f561b2dda3..23c4d6496aac 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h @@ -46,6 +46,7 @@ struct svc_pool { struct svc_pool_stats sp_stats; /* statistics on pool operation */ #define SP_TASK_PENDING (0) /* still work to do even if no * xprt is queued. */ +#define SP_CONGESTED (1) unsigned long sp_flags; } ____cacheline_aligned_in_smp; diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 18e87791350f..80112c45aad1 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -380,7 +380,6 @@ void svc_xprt_do_enqueue(struct svc_xprt *xprt) struct svc_pool *pool; struct svc_rqst *rqstp = NULL; int cpu; - bool queued = false; if (!svc_xprt_has_something_to_do(xprt)) goto out; @@ -401,58 +400,25 @@ void svc_xprt_do_enqueue(struct svc_xprt *xprt) atomic_long_inc(&pool->sp_stats.packets); -redo_search: + dprintk("svc: transport %p put into queue\n", xprt); + spin_lock_bh(&pool->sp_lock); + list_add_tail(&xprt->xpt_ready, &pool->sp_sockets); + pool->sp_stats.sockets_queued++; + spin_unlock_bh(&pool->sp_lock); + /* find a thread for this xprt */ rcu_read_lock(); list_for_each_entry_rcu(rqstp, &pool->sp_all_threads, rq_all) { - /* Do a lockless check first */ - if (test_bit(RQ_BUSY, &rqstp->rq_flags)) + if (test_and_set_bit(RQ_BUSY, &rqstp->rq_flags)) continue; - - /* - * Once the xprt has been queued, it can only be dequeued by - * the task that intends to service it. All we can do at that - * point is to try to wake this thread back up so that it can - * do so. - */ - if (!queued) { - spin_lock_bh(&rqstp->rq_lock); - if (test_and_set_bit(RQ_BUSY, &rqstp->rq_flags)) { - /* already busy, move on... */ - spin_unlock_bh(&rqstp->rq_lock); - continue; - } - - /* this one will do */ - rqstp->rq_xprt = xprt; - svc_xprt_get(xprt); - spin_unlock_bh(&rqstp->rq_lock); - } - rcu_read_unlock(); - atomic_long_inc(&pool->sp_stats.threads_woken); wake_up_process(rqstp->rq_task); - put_cpu(); - goto out; - } - rcu_read_unlock(); - - /* - * We didn't find an idle thread to use, so we need to queue the xprt. - * Do so and then search again. If we find one, we can't hook this one - * up to it directly but we can wake the thread up in the hopes that it - * will pick it up once it searches for a xprt to service. - */ - if (!queued) { - queued = true; - dprintk("svc: transport %p put into queue\n", xprt); - spin_lock_bh(&pool->sp_lock); - list_add_tail(&xprt->xpt_ready, &pool->sp_sockets); - pool->sp_stats.sockets_queued++; - spin_unlock_bh(&pool->sp_lock); - goto redo_search; + goto out_unlock; } + set_bit(SP_CONGESTED, &pool->sp_flags); rqstp = NULL; +out_unlock: + rcu_read_unlock(); put_cpu(); out: trace_svc_xprt_do_enqueue(xprt, rqstp); @@ -721,38 +687,25 @@ rqst_should_sleep(struct svc_rqst *rqstp) static struct svc_xprt *svc_get_next_xprt(struct svc_rqst *rqstp, long timeout) { - struct svc_xprt *xprt; struct svc_pool *pool = rqstp->rq_pool; long time_left = 0; /* rq_xprt should be clear on entry */ WARN_ON_ONCE(rqstp->rq_xprt); - /* Normally we will wait up to 5 seconds for any required - * cache information to be provided. - */ - rqstp->rq_chandle.thread_wait = 5*HZ; - - xprt = svc_xprt_dequeue(pool); - if (xprt) { - rqstp->rq_xprt = xprt; - - /* As there is a shortage of threads and this request - * had to be queued, don't allow the thread to wait so - * long for cache updates. - */ - rqstp->rq_chandle.thread_wait = 1*HZ; - clear_bit(SP_TASK_PENDING, &pool->sp_flags); - return xprt; - } + rqstp->rq_xprt = svc_xprt_dequeue(pool); + if (rqstp->rq_xprt) + goto out_found; /* * We have to be able to interrupt this wait * to bring down the daemons ... */ set_current_state(TASK_INTERRUPTIBLE); + smp_mb__before_atomic(); + clear_bit(SP_CONGESTED, &pool->sp_flags); clear_bit(RQ_BUSY, &rqstp->rq_flags); - smp_mb(); + smp_mb__after_atomic(); if (likely(rqst_should_sleep(rqstp))) time_left = schedule_timeout(timeout); @@ -761,13 +714,11 @@ static struct svc_xprt *svc_get_next_xprt(struct svc_rqst *rqstp, long timeout) try_to_freeze(); - spin_lock_bh(&rqstp->rq_lock); set_bit(RQ_BUSY, &rqstp->rq_flags); - spin_unlock_bh(&rqstp->rq_lock); - - xprt = rqstp->rq_xprt; - if (xprt != NULL) - return xprt; + smp_mb__after_atomic(); + rqstp->rq_xprt = svc_xprt_dequeue(pool); + if (rqstp->rq_xprt) + goto out_found; if (!time_left) atomic_long_inc(&pool->sp_stats.threads_timedout); @@ -775,6 +726,15 @@ static struct svc_xprt *svc_get_next_xprt(struct svc_rqst *rqstp, long timeout) if (signalled() || kthread_should_stop()) return ERR_PTR(-EINTR); return ERR_PTR(-EAGAIN); +out_found: + /* Normally we will wait up to 5 seconds for any required + * cache information to be provided. + */ + if (!test_bit(SP_CONGESTED, &pool->sp_flags)) + rqstp->rq_chandle.thread_wait = 5*HZ; + else + rqstp->rq_chandle.thread_wait = 1*HZ; + return rqstp->rq_xprt; } static void svc_add_new_temp_xprt(struct svc_serv *serv, struct svc_xprt *newxpt) -- cgit v1.2.3-59-g8ed1b From 95a20ef6f7e54c6a982715a7d0da2fd81790db28 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 7 Nov 2017 13:48:11 +0100 Subject: PM / Domains: Allow genpd users to specify default active wakeup behavior It is quite common for PM Domains to require slave devices to be kept active during system suspend if they are to be used as wakeup sources. To enable this, currently each PM Domain or driver has to provide its own gpd_dev_ops.active_wakeup() callback. Introduce a new flag GENPD_FLAG_ACTIVE_WAKEUP to consolidate this. If specified, all slave devices configured as wakeup sources will be kept active during system suspend. PM Domains that need more fine-grained controls, based on the slave device, can still provide their own callbacks, as before. Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 3 +++ include/linux/pm_domain.h | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 7e01ae364d78..e343844357c8 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -124,6 +124,7 @@ static const struct genpd_lock_ops genpd_spin_ops = { #define genpd_status_on(genpd) (genpd->status == GPD_STATE_ACTIVE) #define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE) #define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON) +#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP) static inline bool irq_safe_dev_in_no_sleep_domain(struct device *dev, const struct generic_pm_domain *genpd) @@ -868,6 +869,8 @@ static bool genpd_present(const struct generic_pm_domain *genpd) static bool genpd_dev_active_wakeup(const struct generic_pm_domain *genpd, struct device *dev) { + if (genpd_is_active_wakeup(genpd)) + return true; return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); } diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 9af0356bd69c..28c24c58d479 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -18,9 +18,10 @@ #include /* Defines used for the flags field in the struct generic_pm_domain */ -#define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */ -#define GENPD_FLAG_IRQ_SAFE (1U << 1) /* PM domain operates in atomic */ -#define GENPD_FLAG_ALWAYS_ON (1U << 2) /* PM domain is always powered on */ +#define GENPD_FLAG_PM_CLK (1U << 0) /* PM domain uses PM clk */ +#define GENPD_FLAG_IRQ_SAFE (1U << 1) /* PM domain operates in atomic */ +#define GENPD_FLAG_ALWAYS_ON (1U << 2) /* PM domain is always powered on */ +#define GENPD_FLAG_ACTIVE_WAKEUP (1U << 3) /* Keep devices active if wakeup */ enum gpd_status { GPD_STATE_ACTIVE = 0, /* PM domain is active */ -- cgit v1.2.3-59-g8ed1b From d0af45f1f6528949e05385976eb61c5ebd31854e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 7 Nov 2017 13:48:15 +0100 Subject: PM / Domains: Remove gpd_dev_ops.active_wakeup() callback There are no more users left of the gpd_dev_ops.active_wakeup() callback. All have been converted to GENPD_FLAG_ACTIVE_WAKEUP. Hence remove the callback. Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Reviewed-by: Kevin Hilman Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 14 +++----------- include/linux/pm_domain.h | 1 - 2 files changed, 3 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index e343844357c8..65bb40c240fb 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -866,14 +866,6 @@ static bool genpd_present(const struct generic_pm_domain *genpd) #ifdef CONFIG_PM_SLEEP -static bool genpd_dev_active_wakeup(const struct generic_pm_domain *genpd, - struct device *dev) -{ - if (genpd_is_active_wakeup(genpd)) - return true; - return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev); -} - /** * genpd_sync_power_off - Synchronously power off a PM domain and its masters. * @genpd: PM domain to power off, if possible. @@ -978,7 +970,7 @@ static bool resume_needed(struct device *dev, if (!device_can_wakeup(dev)) return false; - active_wakeup = genpd_dev_active_wakeup(genpd, dev); + active_wakeup = genpd_is_active_wakeup(genpd); return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; } @@ -1047,7 +1039,7 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff) if (IS_ERR(genpd)) return -EINVAL; - if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) + if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd)) return 0; if (poweroff) @@ -1102,7 +1094,7 @@ static int genpd_resume_noirq(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev)) + if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd)) return 0; genpd_lock(genpd); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 28c24c58d479..04dbef9847d3 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -36,7 +36,6 @@ struct dev_power_governor { struct gpd_dev_ops { int (*start)(struct device *dev); int (*stop)(struct device *dev); - bool (*active_wakeup)(struct device *dev); }; struct genpd_power_state { -- cgit v1.2.3-59-g8ed1b From 3928ee6485a316c8abde7e24c7f82033a1c8d3ae Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Thu, 2 Nov 2017 00:49:18 +0100 Subject: net: phy: leds: Add support for "link" trigger Currently, we create a LED trigger for any link speed known to a PHY. These triggers only fire when their exact link speed had been negotiated (they aren't cumulative, that is, they don't fire for "their or any higher" link speed). What we are missing, however, is a trigger which will fire on any link speed known to the PHY. Such trigger can then be used for implementing a poor man's substitute of the "link" LED on boards that lack it. Let's add it. Signed-off-by: Maciej S. Szmigiero Signed-off-by: David S. Miller --- drivers/net/phy/Kconfig | 7 +++++-- drivers/net/phy/phy_led_triggers.c | 43 +++++++++++++++++++++++++++++++++++--- include/linux/phy.h | 2 ++ 3 files changed, 47 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 8125412c8814..bdfbabb86ee0 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -191,11 +191,14 @@ config LED_TRIGGER_PHY Adds support for a set of LED trigger events per-PHY. Link state change will trigger the events, for consumption by an LED class driver. There are triggers for each link speed currently - supported by the phy, and are of the form: + supported by the PHY and also a one common "link" trigger as a + logical-or of all the link speed ones. + All these triggers are named according to the following pattern: :: Where speed is in the form: - Mbps or Gbps + Mbps OR Gbps OR link + for any speed known to the PHY. comment "MII PHY device drivers" diff --git a/drivers/net/phy/phy_led_triggers.c b/drivers/net/phy/phy_led_triggers.c index c736f29b3b2a..39ecad25b201 100644 --- a/drivers/net/phy/phy_led_triggers.c +++ b/drivers/net/phy/phy_led_triggers.c @@ -31,6 +31,7 @@ static void phy_led_trigger_no_link(struct phy_device *phy) { if (phy->last_triggered) { led_trigger_event(&phy->last_triggered->trigger, LED_OFF); + led_trigger_event(&phy->led_link_trigger->trigger, LED_OFF); phy->last_triggered = NULL; } } @@ -54,6 +55,10 @@ void phy_led_trigger_change_speed(struct phy_device *phy) } if (plt != phy->last_triggered) { + if (!phy->last_triggered) + led_trigger_event(&phy->led_link_trigger->trigger, + LED_FULL); + led_trigger_event(&phy->last_triggered->trigger, LED_OFF); led_trigger_event(&plt->trigger, LED_FULL); phy->last_triggered = plt; @@ -61,6 +66,13 @@ void phy_led_trigger_change_speed(struct phy_device *phy) } EXPORT_SYMBOL_GPL(phy_led_trigger_change_speed); +static void phy_led_trigger_format_name(struct phy_device *phy, char *buf, + size_t size, char *suffix) +{ + snprintf(buf, size, PHY_ID_FMT ":%s", + phy->mdio.bus->id, phy->mdio.addr, suffix); +} + static int phy_led_trigger_register(struct phy_device *phy, struct phy_led_trigger *plt, unsigned int speed) @@ -77,8 +89,8 @@ static int phy_led_trigger_register(struct phy_device *phy, snprintf(name_suffix, sizeof(name_suffix), "%dGbps", DIV_ROUND_CLOSEST(speed, 1000)); - snprintf(plt->name, sizeof(plt->name), PHY_ID_FMT ":%s", - phy->mdio.bus->id, phy->mdio.addr, name_suffix); + phy_led_trigger_format_name(phy, plt->name, sizeof(plt->name), + name_suffix); plt->trigger.name = plt->name; return led_trigger_register(&plt->trigger); @@ -99,13 +111,30 @@ int phy_led_triggers_register(struct phy_device *phy) if (!phy->phy_num_led_triggers) return 0; + phy->led_link_trigger = devm_kzalloc(&phy->mdio.dev, + sizeof(*phy->led_link_trigger), + GFP_KERNEL); + if (!phy->led_link_trigger) { + err = -ENOMEM; + goto out_clear; + } + + phy_led_trigger_format_name(phy, phy->led_link_trigger->name, + sizeof(phy->led_link_trigger->name), + "link"); + phy->led_link_trigger->trigger.name = phy->led_link_trigger->name; + + err = led_trigger_register(&phy->led_link_trigger->trigger); + if (err) + goto out_free_link; + phy->phy_led_triggers = devm_kzalloc(&phy->mdio.dev, sizeof(struct phy_led_trigger) * phy->phy_num_led_triggers, GFP_KERNEL); if (!phy->phy_led_triggers) { err = -ENOMEM; - goto out_clear; + goto out_unreg_link; } for (i = 0; i < phy->phy_num_led_triggers; i++) { @@ -123,6 +152,11 @@ out_unreg: while (i--) phy_led_trigger_unregister(&phy->phy_led_triggers[i]); devm_kfree(&phy->mdio.dev, phy->phy_led_triggers); +out_unreg_link: + phy_led_trigger_unregister(phy->led_link_trigger); +out_free_link: + devm_kfree(&phy->mdio.dev, phy->led_link_trigger); + phy->led_link_trigger = NULL; out_clear: phy->phy_num_led_triggers = 0; return err; @@ -135,5 +169,8 @@ void phy_led_triggers_unregister(struct phy_device *phy) for (i = 0; i < phy->phy_num_led_triggers; i++) phy_led_trigger_unregister(&phy->phy_led_triggers[i]); + + if (phy->led_link_trigger) + phy_led_trigger_unregister(phy->led_link_trigger); } EXPORT_SYMBOL_GPL(phy_led_triggers_unregister); diff --git a/include/linux/phy.h b/include/linux/phy.h index d78cd01ea513..dc82a07cb4fd 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -451,6 +451,8 @@ struct phy_device { struct phy_led_trigger *phy_led_triggers; unsigned int phy_num_led_triggers; struct phy_led_trigger *last_triggered; + + struct phy_led_trigger *led_link_trigger; #endif /* -- cgit v1.2.3-59-g8ed1b From 4a1a57df97636b9323c4221cc75a35694b6d34c7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 31 Oct 2017 09:47:28 -0700 Subject: Input: st1232 - remove obsolete platform device support Commit 1fa59bda21c7fa36 ("ARM: shmobile: Remove legacy board code for Armadillo-800 EVA"), removed the last user of st1232_pdata and the "st1232-ts" platform device. All remaining users use DT. Signed-off-by: Geert Uytterhoeven Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/st1232.c | 16 +++------------- include/linux/platform_data/st1232_pdata.h | 14 -------------- 2 files changed, 3 insertions(+), 27 deletions(-) delete mode 100644 include/linux/platform_data/st1232_pdata.h (limited to 'include') diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c index be5615c6bf8f..d5dfa4053bbf 100644 --- a/drivers/input/touchscreen/st1232.c +++ b/drivers/input/touchscreen/st1232.c @@ -29,7 +29,6 @@ #include #include #include -#include #define ST1232_TS_NAME "st1232-ts" @@ -152,10 +151,9 @@ static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron) } static int st1232_ts_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct st1232_ts_data *ts; - struct st1232_pdata *pdata = dev_get_platdata(&client->dev); struct input_dev *input_dev; int error; @@ -180,13 +178,7 @@ static int st1232_ts_probe(struct i2c_client *client, ts->client = client; ts->input_dev = input_dev; - if (pdata) - ts->reset_gpio = pdata->reset_gpio; - else if (client->dev.of_node) - ts->reset_gpio = of_get_gpio(client->dev.of_node, 0); - else - ts->reset_gpio = -ENODEV; - + ts->reset_gpio = of_get_gpio(client->dev.of_node, 0); if (gpio_is_valid(ts->reset_gpio)) { error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL); if (error) { @@ -281,13 +273,11 @@ static const struct i2c_device_id st1232_ts_id[] = { }; MODULE_DEVICE_TABLE(i2c, st1232_ts_id); -#ifdef CONFIG_OF static const struct of_device_id st1232_ts_dt_ids[] = { { .compatible = "sitronix,st1232", }, { } }; MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids); -#endif static struct i2c_driver st1232_ts_driver = { .probe = st1232_ts_probe, @@ -295,7 +285,7 @@ static struct i2c_driver st1232_ts_driver = { .id_table = st1232_ts_id, .driver = { .name = ST1232_TS_NAME, - .of_match_table = of_match_ptr(st1232_ts_dt_ids), + .of_match_table = st1232_ts_dt_ids, .pm = &st1232_ts_pm_ops, }, }; diff --git a/include/linux/platform_data/st1232_pdata.h b/include/linux/platform_data/st1232_pdata.h deleted file mode 100644 index 1dcd23bee24e..000000000000 --- a/include/linux/platform_data/st1232_pdata.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _LINUX_ST1232_PDATA_H -#define _LINUX_ST1232_PDATA_H - -/* - * Optional platform data - * - * Use this if you want the driver to drive the reset pin. - */ -struct st1232_pdata { - int reset_gpio; -}; - -#endif -- cgit v1.2.3-59-g8ed1b From 84287bb3285634b60c55c00a1d5ed843b44fde92 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Sun, 5 Nov 2017 15:58:23 -0800 Subject: ila: add checksum neutral map auto Add checksum neutral auto that performs checksum neutral mapping without using the C-bit. This is enabled by configuration of a mapping. The checksum neutral function has been split into ila_csum_do_neutral_fmt and ila_csum_do_neutral_nofmt. The former handles the C-bit and includes it in the adjustment value. The latter just sets the adjustment value on the locator diff only. Added configuration for checksum neutral map aut in ila_lwt and ila_xlat. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/uapi/linux/ila.h | 1 + net/ipv6/ila/ila_common.c | 65 ++++++++++++++++++++++++++++------------------- net/ipv6/ila/ila_lwt.c | 29 +++++++++++---------- net/ipv6/ila/ila_xlat.c | 10 +++++--- 4 files changed, 61 insertions(+), 44 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h index f54853288f99..0744881dcef3 100644 --- a/include/uapi/linux/ila.h +++ b/include/uapi/linux/ila.h @@ -41,6 +41,7 @@ enum { ILA_CSUM_ADJUST_TRANSPORT, ILA_CSUM_NEUTRAL_MAP, ILA_CSUM_NO_ACTION, + ILA_CSUM_NEUTRAL_MAP_AUTO, }; #endif /* _UAPI_LINUX_ILA_H */ diff --git a/net/ipv6/ila/ila_common.c b/net/ipv6/ila/ila_common.c index f1d9248d8b86..8c88ecf29b93 100644 --- a/net/ipv6/ila/ila_common.c +++ b/net/ipv6/ila/ila_common.c @@ -37,8 +37,8 @@ static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p) return get_csum_diff_iaddr(ila_a2i(&ip6h->daddr), p); } -static void ila_csum_do_neutral(struct ila_addr *iaddr, - struct ila_params *p) +static void ila_csum_do_neutral_fmt(struct ila_addr *iaddr, + struct ila_params *p) { __sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3]; __wsum diff, fval; @@ -60,13 +60,23 @@ static void ila_csum_do_neutral(struct ila_addr *iaddr, iaddr->ident.csum_neutral ^= 1; } -static void ila_csum_adjust_transport(struct sk_buff *skb, +static void ila_csum_do_neutral_nofmt(struct ila_addr *iaddr, struct ila_params *p) { + __sum16 *adjust = (__force __sum16 *)&iaddr->ident.v16[3]; __wsum diff; - struct ipv6hdr *ip6h = ipv6_hdr(skb); - struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); + + diff = get_csum_diff_iaddr(iaddr, p); + + *adjust = ~csum_fold(csum_add(diff, csum_unfold(*adjust))); +} + +static void ila_csum_adjust_transport(struct sk_buff *skb, + struct ila_params *p) +{ size_t nhoff = sizeof(struct ipv6hdr); + struct ipv6hdr *ip6h = ipv6_hdr(skb); + __wsum diff; switch (ip6h->nexthdr) { case NEXTHDR_TCP: @@ -105,36 +115,39 @@ static void ila_csum_adjust_transport(struct sk_buff *skb, } break; } - - /* Now change destination address */ - iaddr->loc = p->locator; } void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p, - bool set_csum_neutral) + bool sir2ila) { struct ipv6hdr *ip6h = ipv6_hdr(skb); struct ila_addr *iaddr = ila_a2i(&ip6h->daddr); - /* First deal with the transport checksum */ - if (ila_csum_neutral_set(iaddr->ident)) { - /* C-bit is set in the locator indicating that this - * is a locator being translated to a SIR address. - * Perform (receiver) checksum-neutral translation. - */ - if (!set_csum_neutral) - ila_csum_do_neutral(iaddr, p); - } else { - switch (p->csum_mode) { - case ILA_CSUM_ADJUST_TRANSPORT: - ila_csum_adjust_transport(skb, p); - break; - case ILA_CSUM_NEUTRAL_MAP: - ila_csum_do_neutral(iaddr, p); - break; - case ILA_CSUM_NO_ACTION: + switch (p->csum_mode) { + case ILA_CSUM_ADJUST_TRANSPORT: + ila_csum_adjust_transport(skb, p); + break; + case ILA_CSUM_NEUTRAL_MAP: + if (sir2ila) { + if (WARN_ON(ila_csum_neutral_set(iaddr->ident))) { + /* Checksum flag should never be + * set in a formatted SIR address. + */ + break; + } + } else if (!ila_csum_neutral_set(iaddr->ident)) { + /* ILA to SIR translation and C-bit isn't + * set so we're good. + */ break; } + ila_csum_do_neutral_fmt(iaddr, p); + break; + case ILA_CSUM_NEUTRAL_MAP_AUTO: + ila_csum_do_neutral_nofmt(iaddr, p); + break; + case ILA_CSUM_NO_ACTION: + break; } /* Now change destination address */ diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index 696281b4bca2..104af07d83a6 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -127,6 +127,7 @@ static int ila_build_state(struct nlattr *nla, struct lwtunnel_state *newts; const struct fib6_config *cfg6 = cfg; struct ila_addr *iaddr; + u8 csum_mode = ILA_CSUM_NO_ACTION; int ret; if (family != AF_INET6) @@ -139,15 +140,6 @@ static int ila_build_state(struct nlattr *nla, return -EINVAL; } - iaddr = (struct ila_addr *)&cfg6->fc_dst; - - if (!ila_addr_is_ila(iaddr) || ila_csum_neutral_set(iaddr->ident)) { - /* Don't allow translation for a non-ILA address or checksum - * neutral flag to be set. - */ - return -EINVAL; - } - ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, ila_nl_policy, extack); if (ret < 0) return ret; @@ -155,6 +147,19 @@ static int ila_build_state(struct nlattr *nla, if (!tb[ILA_ATTR_LOCATOR]) return -EINVAL; + iaddr = (struct ila_addr *)&cfg6->fc_dst; + + if (tb[ILA_ATTR_CSUM_MODE]) + csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]); + + if (csum_mode == ILA_CSUM_NEUTRAL_MAP && + ila_csum_neutral_set(iaddr->ident)) { + /* Don't allow translation if checksum neutral bit is + * configured and it's set in the SIR address. + */ + return -EINVAL; + } + newts = lwtunnel_state_alloc(sizeof(*ilwt)); if (!newts) return -ENOMEM; @@ -168,17 +173,13 @@ static int ila_build_state(struct nlattr *nla, p = ila_params_lwtunnel(newts); + p->csum_mode = csum_mode; p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); /* Precompute checksum difference for translation since we * know both the old locator and the new one. */ p->locator_match = iaddr->loc; - p->csum_diff = compute_csum_diff8( - (__be32 *)&p->locator_match, (__be32 *)&p->locator); - - if (tb[ILA_ATTR_CSUM_MODE]) - p->csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]); ila_init_saved_csum(p); diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c index 3123b9de91b5..213259629e66 100644 --- a/net/ipv6/ila/ila_xlat.c +++ b/net/ipv6/ila/ila_xlat.c @@ -138,6 +138,8 @@ static int parse_nl_config(struct genl_info *info, if (info->attrs[ILA_ATTR_CSUM_MODE]) xp->ip.csum_mode = nla_get_u8(info->attrs[ILA_ATTR_CSUM_MODE]); + else + xp->ip.csum_mode = ILA_CSUM_NO_ACTION; if (info->attrs[ILA_ATTR_IFINDEX]) xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]); @@ -198,7 +200,7 @@ static void ila_free_cb(void *ptr, void *arg) } } -static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral); +static int ila_xlat_addr(struct sk_buff *skb, bool sir2ila); static unsigned int ila_nf_input(void *priv, @@ -396,7 +398,7 @@ static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg) (__force u64)ila->xp.ip.locator_match.v64, ILA_ATTR_PAD) || nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->xp.ifindex) || - nla_put_u32(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode)) + nla_put_u8(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode)) return -1; return 0; @@ -607,7 +609,7 @@ static struct pernet_operations ila_net_ops = { .size = sizeof(struct ila_net), }; -static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral) +static int ila_xlat_addr(struct sk_buff *skb, bool sir2ila) { struct ila_map *ila; struct ipv6hdr *ip6h = ipv6_hdr(skb); @@ -626,7 +628,7 @@ static int ila_xlat_addr(struct sk_buff *skb, bool set_csum_neutral) ila = ila_lookup_wildcards(iaddr, skb->dev->ifindex, ilan); if (ila) - ila_update_ipv6_locator(skb, &ila->xp.ip, set_csum_neutral); + ila_update_ipv6_locator(skb, &ila->xp.ip, sir2ila); rcu_read_unlock(); -- cgit v1.2.3-59-g8ed1b From 70d5aef48a421a68bd9d1bf8f8267af406681580 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Sun, 5 Nov 2017 15:58:24 -0800 Subject: ila: allow configuration of identifier type Allow identifier to be explicitly configured for a mapping. This can either be one of the identifier types specified in the ILA draft or a value of ILA_ATYPE_USE_FORMAT which means the identifier type is inferred from the identifier type field. If a value other than ILA_ATYPE_USE_FORMAT is set for a mapping then it is assumed that the identifier type field is not present in an identifier. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/uapi/linux/ila.h | 13 ++++++++++++ net/ipv6/ila/ila.h | 12 +----------- net/ipv6/ila/ila_lwt.c | 51 +++++++++++++++++++++++++++++++++++++++++------- net/ipv6/ila/ila_xlat.c | 18 ++++++++++++----- 4 files changed, 71 insertions(+), 23 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h index 0744881dcef3..8353c78a7781 100644 --- a/include/uapi/linux/ila.h +++ b/include/uapi/linux/ila.h @@ -17,6 +17,7 @@ enum { ILA_ATTR_DIR, /* u32 */ ILA_ATTR_PAD, ILA_ATTR_CSUM_MODE, /* u8 */ + ILA_ATTR_IDENT_TYPE, /* u8 */ __ILA_ATTR_MAX, }; @@ -44,4 +45,16 @@ enum { ILA_CSUM_NEUTRAL_MAP_AUTO, }; +enum { + ILA_ATYPE_IID = 0, + ILA_ATYPE_LUID, + ILA_ATYPE_VIRT_V4, + ILA_ATYPE_VIRT_UNI_V6, + ILA_ATYPE_VIRT_MULTI_V6, + ILA_ATYPE_NONLOCAL_ADDR, + ILA_ATYPE_RSVD_1, + ILA_ATYPE_RSVD_2, + + ILA_ATYPE_USE_FORMAT = 32, /* Get type from type field in identifier */ +}; #endif /* _UAPI_LINUX_ILA_H */ diff --git a/net/ipv6/ila/ila.h b/net/ipv6/ila/ila.h index e0170f62bc39..3c7a11b62334 100644 --- a/net/ipv6/ila/ila.h +++ b/net/ipv6/ila/ila.h @@ -55,17 +55,6 @@ struct ila_identifier { }; }; -enum { - ILA_ATYPE_IID = 0, - ILA_ATYPE_LUID, - ILA_ATYPE_VIRT_V4, - ILA_ATYPE_VIRT_UNI_V6, - ILA_ATYPE_VIRT_MULTI_V6, - ILA_ATYPE_RSVD_1, - ILA_ATYPE_RSVD_2, - ILA_ATYPE_RSVD_3, -}; - #define CSUM_NEUTRAL_FLAG htonl(0x10000000) struct ila_addr { @@ -93,6 +82,7 @@ struct ila_params { struct ila_locator locator_match; __wsum csum_diff; u8 csum_mode; + u8 ident_type; }; static inline __wsum compute_csum_diff8(const __be32 *from, const __be32 *to) diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index 104af07d83a6..4b97d573f223 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -114,6 +114,7 @@ drop: static const struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { [ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, [ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, }, + [ILA_ATTR_IDENT_TYPE] = { .type = NLA_U8, }, }; static int ila_build_state(struct nlattr *nla, @@ -127,19 +128,14 @@ static int ila_build_state(struct nlattr *nla, struct lwtunnel_state *newts; const struct fib6_config *cfg6 = cfg; struct ila_addr *iaddr; + u8 ident_type = ILA_ATYPE_USE_FORMAT; u8 csum_mode = ILA_CSUM_NO_ACTION; + u8 eff_ident_type; int ret; if (family != AF_INET6) return -EINVAL; - if (cfg6->fc_dst_len < 8 * sizeof(struct ila_locator) + 3) { - /* Need to have full locator and at least type field - * included in destination - */ - return -EINVAL; - } - ret = nla_parse_nested(tb, ILA_ATTR_MAX, nla, ila_nl_policy, extack); if (ret < 0) return ret; @@ -149,6 +145,41 @@ static int ila_build_state(struct nlattr *nla, iaddr = (struct ila_addr *)&cfg6->fc_dst; + if (tb[ILA_ATTR_IDENT_TYPE]) + ident_type = nla_get_u8(tb[ILA_ATTR_IDENT_TYPE]); + + if (ident_type == ILA_ATYPE_USE_FORMAT) { + /* Infer identifier type from type field in formatted + * identifier. + */ + + if (cfg6->fc_dst_len < 8 * sizeof(struct ila_locator) + 3) { + /* Need to have full locator and at least type field + * included in destination + */ + return -EINVAL; + } + + eff_ident_type = iaddr->ident.type; + } else { + eff_ident_type = ident_type; + } + + switch (eff_ident_type) { + case ILA_ATYPE_IID: + /* Don't allow ILA for IID type */ + return -EINVAL; + case ILA_ATYPE_LUID: + break; + case ILA_ATYPE_VIRT_V4: + case ILA_ATYPE_VIRT_UNI_V6: + case ILA_ATYPE_VIRT_MULTI_V6: + case ILA_ATYPE_NONLOCAL_ADDR: + /* These ILA formats are not supported yet. */ + default: + return -EINVAL; + } + if (tb[ILA_ATTR_CSUM_MODE]) csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]); @@ -174,6 +205,7 @@ static int ila_build_state(struct nlattr *nla, p = ila_params_lwtunnel(newts); p->csum_mode = csum_mode; + p->ident_type = ident_type; p->locator.v64 = (__force __be64)nla_get_u64(tb[ILA_ATTR_LOCATOR]); /* Precompute checksum difference for translation since we @@ -208,9 +240,13 @@ static int ila_fill_encap_info(struct sk_buff *skb, if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator.v64, ILA_ATTR_PAD)) goto nla_put_failure; + if (nla_put_u8(skb, ILA_ATTR_CSUM_MODE, (__force u8)p->csum_mode)) goto nla_put_failure; + if (nla_put_u8(skb, ILA_ATTR_IDENT_TYPE, (__force u8)p->ident_type)) + goto nla_put_failure; + return 0; nla_put_failure: @@ -221,6 +257,7 @@ static int ila_encap_nlsize(struct lwtunnel_state *lwtstate) { return nla_total_size_64bit(sizeof(u64)) + /* ILA_ATTR_LOCATOR */ nla_total_size(sizeof(u8)) + /* ILA_ATTR_CSUM_MODE */ + nla_total_size(sizeof(u8)) + /* ILA_ATTR_IDENT_TYPE */ 0; } diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c index 213259629e66..6eb5e68f112a 100644 --- a/net/ipv6/ila/ila_xlat.c +++ b/net/ipv6/ila/ila_xlat.c @@ -121,6 +121,7 @@ static const struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { [ILA_ATTR_LOCATOR_MATCH] = { .type = NLA_U64, }, [ILA_ATTR_IFINDEX] = { .type = NLA_U32, }, [ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, }, + [ILA_ATTR_IDENT_TYPE] = { .type = NLA_U8, }, }; static int parse_nl_config(struct genl_info *info, @@ -141,6 +142,12 @@ static int parse_nl_config(struct genl_info *info, else xp->ip.csum_mode = ILA_CSUM_NO_ACTION; + if (info->attrs[ILA_ATTR_IDENT_TYPE]) + xp->ip.ident_type = nla_get_u8( + info->attrs[ILA_ATTR_IDENT_TYPE]); + else + xp->ip.ident_type = ILA_ATYPE_USE_FORMAT; + if (info->attrs[ILA_ATTR_IFINDEX]) xp->ifindex = nla_get_s32(info->attrs[ILA_ATTR_IFINDEX]); @@ -398,7 +405,8 @@ static int ila_fill_info(struct ila_map *ila, struct sk_buff *msg) (__force u64)ila->xp.ip.locator_match.v64, ILA_ATTR_PAD) || nla_put_s32(msg, ILA_ATTR_IFINDEX, ila->xp.ifindex) || - nla_put_u8(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode)) + nla_put_u8(msg, ILA_ATTR_CSUM_MODE, ila->xp.ip.csum_mode) || + nla_put_u8(msg, ILA_ATTR_IDENT_TYPE, ila->xp.ip.ident_type)) return -1; return 0; @@ -619,10 +627,10 @@ static int ila_xlat_addr(struct sk_buff *skb, bool sir2ila) /* Assumes skb contains a valid IPv6 header that is pulled */ - if (!ila_addr_is_ila(iaddr)) { - /* Type indicates this is not an ILA address */ - return 0; - } + /* No check here that ILA type in the mapping matches what is in the + * address. We assume that whatever sender gaves us can be translated. + * The checksum mode however is relevant. + */ rcu_read_lock(); -- cgit v1.2.3-59-g8ed1b From fddb231ebe647749782a9ebf11106a81f7168ba7 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Sun, 5 Nov 2017 15:58:25 -0800 Subject: ila: Add a hook type for LWT routes In LWT tunnels both an input and output route method is defined. If both of these are executed in the same path then double translation happens and the effect is not correct. This patch adds a new attribute that indicates the hook type. Two values are defined for route output and route output. ILA translation is only done for the one that is set. The default is to enable ILA on route output. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/uapi/linux/ila.h | 7 +++++++ net/ipv6/ila/ila_lwt.c | 39 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/uapi/linux/ila.h b/include/uapi/linux/ila.h index 8353c78a7781..483b77af4eb8 100644 --- a/include/uapi/linux/ila.h +++ b/include/uapi/linux/ila.h @@ -18,6 +18,7 @@ enum { ILA_ATTR_PAD, ILA_ATTR_CSUM_MODE, /* u8 */ ILA_ATTR_IDENT_TYPE, /* u8 */ + ILA_ATTR_HOOK_TYPE, /* u8 */ __ILA_ATTR_MAX, }; @@ -57,4 +58,10 @@ enum { ILA_ATYPE_USE_FORMAT = 32, /* Get type from type field in identifier */ }; + +enum { + ILA_HOOK_ROUTE_OUTPUT, + ILA_HOOK_ROUTE_INPUT, +}; + #endif /* _UAPI_LINUX_ILA_H */ diff --git a/net/ipv6/ila/ila_lwt.c b/net/ipv6/ila/ila_lwt.c index 4b97d573f223..3d56a2fb6f86 100644 --- a/net/ipv6/ila/ila_lwt.c +++ b/net/ipv6/ila/ila_lwt.c @@ -20,6 +20,7 @@ struct ila_lwt { struct ila_params p; struct dst_cache dst_cache; u32 connected : 1; + u32 lwt_output : 1; }; static inline struct ila_lwt *ila_lwt_lwtunnel( @@ -45,8 +46,10 @@ static int ila_output(struct net *net, struct sock *sk, struct sk_buff *skb) if (skb->protocol != htons(ETH_P_IPV6)) goto drop; - ila_update_ipv6_locator(skb, ila_params_lwtunnel(orig_dst->lwtstate), - true); + if (ilwt->lwt_output) + ila_update_ipv6_locator(skb, + ila_params_lwtunnel(orig_dst->lwtstate), + true); if (rt->rt6i_flags & (RTF_GATEWAY | RTF_CACHE)) { /* Already have a next hop address in route, no need for @@ -98,11 +101,15 @@ drop: static int ila_input(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); + struct ila_lwt *ilwt = ila_lwt_lwtunnel(dst->lwtstate); if (skb->protocol != htons(ETH_P_IPV6)) goto drop; - ila_update_ipv6_locator(skb, ila_params_lwtunnel(dst->lwtstate), false); + if (!ilwt->lwt_output) + ila_update_ipv6_locator(skb, + ila_params_lwtunnel(dst->lwtstate), + false); return dst->lwtstate->orig_input(skb); @@ -115,6 +122,7 @@ static const struct nla_policy ila_nl_policy[ILA_ATTR_MAX + 1] = { [ILA_ATTR_LOCATOR] = { .type = NLA_U64, }, [ILA_ATTR_CSUM_MODE] = { .type = NLA_U8, }, [ILA_ATTR_IDENT_TYPE] = { .type = NLA_U8, }, + [ILA_ATTR_HOOK_TYPE] = { .type = NLA_U8, }, }; static int ila_build_state(struct nlattr *nla, @@ -129,7 +137,9 @@ static int ila_build_state(struct nlattr *nla, const struct fib6_config *cfg6 = cfg; struct ila_addr *iaddr; u8 ident_type = ILA_ATYPE_USE_FORMAT; + u8 hook_type = ILA_HOOK_ROUTE_OUTPUT; u8 csum_mode = ILA_CSUM_NO_ACTION; + bool lwt_output = true; u8 eff_ident_type; int ret; @@ -180,6 +190,20 @@ static int ila_build_state(struct nlattr *nla, return -EINVAL; } + if (tb[ILA_ATTR_HOOK_TYPE]) + hook_type = nla_get_u8(tb[ILA_ATTR_HOOK_TYPE]); + + switch (hook_type) { + case ILA_HOOK_ROUTE_OUTPUT: + lwt_output = true; + break; + case ILA_HOOK_ROUTE_INPUT: + lwt_output = false; + break; + default: + return -EINVAL; + } + if (tb[ILA_ATTR_CSUM_MODE]) csum_mode = nla_get_u8(tb[ILA_ATTR_CSUM_MODE]); @@ -202,6 +226,8 @@ static int ila_build_state(struct nlattr *nla, return ret; } + ilwt->lwt_output = !!lwt_output; + p = ila_params_lwtunnel(newts); p->csum_mode = csum_mode; @@ -236,6 +262,7 @@ static int ila_fill_encap_info(struct sk_buff *skb, struct lwtunnel_state *lwtstate) { struct ila_params *p = ila_params_lwtunnel(lwtstate); + struct ila_lwt *ilwt = ila_lwt_lwtunnel(lwtstate); if (nla_put_u64_64bit(skb, ILA_ATTR_LOCATOR, (__force u64)p->locator.v64, ILA_ATTR_PAD)) @@ -247,6 +274,11 @@ static int ila_fill_encap_info(struct sk_buff *skb, if (nla_put_u8(skb, ILA_ATTR_IDENT_TYPE, (__force u8)p->ident_type)) goto nla_put_failure; + if (nla_put_u8(skb, ILA_ATTR_HOOK_TYPE, + ilwt->lwt_output ? ILA_HOOK_ROUTE_OUTPUT : + ILA_HOOK_ROUTE_INPUT)) + goto nla_put_failure; + return 0; nla_put_failure: @@ -258,6 +290,7 @@ static int ila_encap_nlsize(struct lwtunnel_state *lwtstate) return nla_total_size_64bit(sizeof(u64)) + /* ILA_ATTR_LOCATOR */ nla_total_size(sizeof(u8)) + /* ILA_ATTR_CSUM_MODE */ nla_total_size(sizeof(u8)) + /* ILA_ATTR_IDENT_TYPE */ + nla_total_size(sizeof(u8)) + /* ILA_ATTR_HOOK_TYPE */ 0; } -- cgit v1.2.3-59-g8ed1b From 602f3baf22188aad24b9a58be3209ab774b97d74 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Mon, 6 Nov 2017 07:23:41 +0100 Subject: net_sch: red: Add offload ability to RED qdisc Add the ability to offload RED qdisc by using ndo_setup_tc. There are four commands for RED offloading: * TC_RED_SET: handles set and change. * TC_RED_DESTROY: handle qdisc destroy. * TC_RED_STATS: update the qdiscs counters (given as reference) * TC_RED_XSTAT: returns red xstats. Whether RED is being offloaded is being determined every time dump action is being called because parent change of this qdisc could change its offload state but doesn't require any RED function to be called. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + include/net/pkt_cls.h | 30 ++++++++++++++++ include/uapi/linux/pkt_sched.h | 1 + net/sched/sch_red.c | 79 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index fda527ccb263..71968a2ca9f3 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -777,6 +777,7 @@ enum tc_setup_type { TC_SETUP_CLSBPF, TC_SETUP_BLOCK, TC_SETUP_CBS, + TC_SETUP_QDISC_RED, }; /* These structures hold the attributes of bpf state that are being passed diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 98fef3221227..03c208d3c922 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -703,4 +703,34 @@ struct tc_cookie { u8 *data; u32 len; }; + +enum tc_red_command { + TC_RED_REPLACE, + TC_RED_DESTROY, + TC_RED_STATS, + TC_RED_XSTATS, +}; + +struct tc_red_qopt_offload_params { + u32 min; + u32 max; + u32 probability; + bool is_ecn; +}; +struct tc_red_qopt_offload_stats { + struct gnet_stats_basic_packed *bstats; + struct gnet_stats_queue *qstats; +}; + +struct tc_red_qopt_offload { + enum tc_red_command command; + u32 handle; + u32 parent; + union { + struct tc_red_qopt_offload_params set; + struct tc_red_qopt_offload_stats stats; + struct red_stats *xstats; + }; +}; + #endif diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index 5002562868cc..6a2c5ea7e9c4 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -256,6 +256,7 @@ struct tc_red_qopt { #define TC_RED_ECN 1 #define TC_RED_HARDDROP 2 #define TC_RED_ADAPTATIVE 4 +#define TC_RED_OFFLOADED 8 }; struct tc_red_xstats { diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index fdfdb56aaae2..007dd8ef8aac 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -148,11 +149,37 @@ static void red_reset(struct Qdisc *sch) red_restart(&q->vars); } +static int red_offload(struct Qdisc *sch, bool enable) +{ + struct red_sched_data *q = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); + struct tc_red_qopt_offload opt = { + .handle = sch->handle, + .parent = sch->parent, + }; + + if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) + return -EOPNOTSUPP; + + if (enable) { + opt.command = TC_RED_REPLACE; + opt.set.min = q->parms.qth_min >> q->parms.Wlog; + opt.set.max = q->parms.qth_max >> q->parms.Wlog; + opt.set.probability = q->parms.max_P; + opt.set.is_ecn = red_use_ecn(q); + } else { + opt.command = TC_RED_DESTROY; + } + + return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, &opt); +} + static void red_destroy(struct Qdisc *sch) { struct red_sched_data *q = qdisc_priv(sch); del_timer_sync(&q->adapt_timer); + red_offload(sch, false); qdisc_destroy(q->qdisc); } @@ -219,6 +246,7 @@ static int red_change(struct Qdisc *sch, struct nlattr *opt) red_start_of_idle_period(&q->vars); sch_tree_unlock(sch); + red_offload(sch, true); return 0; } @@ -244,6 +272,33 @@ static int red_init(struct Qdisc *sch, struct nlattr *opt) return red_change(sch, opt); } +static int red_dump_offload(struct Qdisc *sch, struct tc_red_qopt *opt) +{ + struct net_device *dev = qdisc_dev(sch); + struct tc_red_qopt_offload hw_stats = { + .handle = sch->handle, + .parent = sch->parent, + .command = TC_RED_STATS, + .stats.bstats = &sch->bstats, + .stats.qstats = &sch->qstats, + }; + int err; + + opt->flags &= ~TC_RED_OFFLOADED; + if (!tc_can_offload(dev) || !dev->netdev_ops->ndo_setup_tc) + return 0; + + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_QDISC_RED, + &hw_stats); + if (err == -EOPNOTSUPP) + return 0; + + if (!err) + opt->flags |= TC_RED_OFFLOADED; + + return err; +} + static int red_dump(struct Qdisc *sch, struct sk_buff *skb) { struct red_sched_data *q = qdisc_priv(sch); @@ -257,8 +312,13 @@ static int red_dump(struct Qdisc *sch, struct sk_buff *skb) .Plog = q->parms.Plog, .Scell_log = q->parms.Scell_log, }; + int err; sch->qstats.backlog = q->qdisc->qstats.backlog; + err = red_dump_offload(sch, &opt); + if (err) + goto nla_put_failure; + opts = nla_nest_start(skb, TCA_OPTIONS); if (opts == NULL) goto nla_put_failure; @@ -275,6 +335,7 @@ nla_put_failure: static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d) { struct red_sched_data *q = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); struct tc_red_xstats st = { .early = q->stats.prob_drop + q->stats.forced_drop, .pdrop = q->stats.pdrop, @@ -282,6 +343,24 @@ static int red_dump_stats(struct Qdisc *sch, struct gnet_dump *d) .marked = q->stats.prob_mark + q->stats.forced_mark, }; + if (tc_can_offload(dev) && dev->netdev_ops->ndo_setup_tc) { + struct red_stats hw_stats = {0}; + struct tc_red_qopt_offload hw_stats_request = { + .handle = sch->handle, + .parent = sch->parent, + .command = TC_RED_XSTATS, + .xstats = &hw_stats, + }; + if (!dev->netdev_ops->ndo_setup_tc(dev, + TC_SETUP_QDISC_RED, + &hw_stats_request)) { + st.early += hw_stats.prob_drop + hw_stats.forced_drop; + st.pdrop += hw_stats.pdrop; + st.other += hw_stats.other; + st.marked += hw_stats.prob_mark + hw_stats.forced_mark; + } + } + return gnet_stats_copy_app(d, &st, sizeof(st)); } -- cgit v1.2.3-59-g8ed1b From 575ed7d39e2fbe602a3894bc766a8cb49af83bd3 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Mon, 6 Nov 2017 07:23:42 +0100 Subject: net_sch: mqprio: Change TC_SETUP_MQPRIO to TC_SETUP_QDISC_MQPRIO Change TC_SETUP_MQPRIO to TC_SETUP_QDISC_MQPRIO to match the new convention. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 2 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 2 +- drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 2 +- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 2 +- drivers/net/ethernet/intel/i40e/i40e_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- drivers/net/ethernet/sfc/falcon/tx.c | 2 +- drivers/net/ethernet/sfc/tx.c | 2 +- drivers/net/ethernet/ti/netcp_core.c | 2 +- include/linux/netdevice.h | 2 +- net/sched/sch_mqprio.c | 5 +++-- 15 files changed, 17 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 3d53153ce751..a74a8fbad53a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -2206,7 +2206,7 @@ static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, struct tc_mqprio_qopt *mqprio = type_data; u8 tc; - if (type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 1216c1f1e052..4c739d5355d2 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4289,7 +4289,7 @@ int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, { struct tc_mqprio_qopt *mqprio = type_data; - if (type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 96416f5d97f3..e5472e5ae7b2 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7388,7 +7388,7 @@ static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, switch (type) { case TC_SETUP_BLOCK: return bnxt_setup_tc_block(dev, type_data); - case TC_SETUP_MQPRIO: { + case TC_SETUP_QDISC_MQPRIO: { struct tc_mqprio_qopt *mqprio = type_data; mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index ebc55b6a6349..784dbf5a3e12 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -351,7 +351,7 @@ static int dpaa_setup_tc(struct net_device *net_dev, enum tc_setup_type type, u8 num_tc; int i; - if (type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index 2a0af11c9b59..59415090ff0f 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1252,7 +1252,7 @@ out: static int hns3_nic_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data) { - if (type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; return hns3_setup_tc(dev, type_data); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 81e4425f0529..adc62fb38c49 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1389,7 +1389,7 @@ static int __fm10k_setup_tc(struct net_device *dev, enum tc_setup_type type, { struct tc_mqprio_qopt *mqprio = type_data; - if (type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 05b94d87a6c3..17e6f64299cf 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -7550,7 +7550,7 @@ static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, void *type_data) { switch (type) { - case TC_SETUP_MQPRIO: + case TC_SETUP_QDISC_MQPRIO: return i40e_setup_tc(netdev, type_data); case TC_SETUP_BLOCK: return i40e_setup_tc_block(netdev, type_data); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index e5dcb25be398..6eaca8366ac8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9431,7 +9431,7 @@ static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type, switch (type) { case TC_SETUP_BLOCK: return ixgbe_setup_tc_block(dev, type_data); - case TC_SETUP_MQPRIO: + case TC_SETUP_QDISC_MQPRIO: return ixgbe_setup_tc_mqprio(dev, type_data); default: return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 736a6ccaf05e..99051a294fa6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -135,7 +135,7 @@ static int __mlx4_en_setup_tc(struct net_device *dev, enum tc_setup_type type, { struct tc_mqprio_qopt *mqprio = type_data; - if (type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; if (mqprio->num_tc && mqprio->num_tc != MLX4_EN_NUM_UP_HIGH) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index f877f2f5f2a5..5d5d2e50e4bf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3146,7 +3146,7 @@ int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, case TC_SETUP_BLOCK: return mlx5e_setup_tc_block(dev, type_data); #endif - case TC_SETUP_MQPRIO: + case TC_SETUP_QDISC_MQPRIO: return mlx5e_setup_tc_mqprio(dev, type_data); default: return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c index 6a75f4140a4b..1b978d69e702 100644 --- a/drivers/net/ethernet/sfc/falcon/tx.c +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -435,7 +435,7 @@ int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, unsigned tc, num_tc; int rc; - if (type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; num_tc = mqprio->num_tc; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 32bf1fecf864..ea27b8a7f465 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -663,7 +663,7 @@ int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, unsigned tc, num_tc; int rc; - if (type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; num_tc = mqprio->num_tc; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 437d36289786..15e2e3031d36 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1887,7 +1887,7 @@ static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, /* setup tc must be called under rtnl lock */ ASSERT_RTNL(); - if (type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_QDISC_MQPRIO) return -EOPNOTSUPP; mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 71968a2ca9f3..703885aed856 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -770,7 +770,7 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, struct sk_buff *skb); enum tc_setup_type { - TC_SETUP_MQPRIO, + TC_SETUP_QDISC_MQPRIO, TC_SETUP_CLSU32, TC_SETUP_CLSFLOWER, TC_SETUP_CLSMATCHALL, diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 4d5ed45123f0..b85885a9d8a1 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -50,7 +50,8 @@ static void mqprio_destroy(struct Qdisc *sch) switch (priv->mode) { case TC_MQPRIO_MODE_DCB: case TC_MQPRIO_MODE_CHANNEL: - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, + dev->netdev_ops->ndo_setup_tc(dev, + TC_SETUP_QDISC_MQPRIO, &mqprio); break; default: @@ -265,7 +266,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) return -EINVAL; } err = dev->netdev_ops->ndo_setup_tc(dev, - TC_SETUP_MQPRIO, + TC_SETUP_QDISC_MQPRIO, &mqprio); if (err) return err; -- cgit v1.2.3-59-g8ed1b From 8521db4c7e155d12fb280686c0552e47f77e9110 Mon Sep 17 00:00:00 2001 From: Nogah Frankel Date: Mon, 6 Nov 2017 07:23:43 +0100 Subject: net_sch: cbs: Change TC_SETUP_CBS to TC_SETUP_QDISC_CBS Change TC_SETUP_CBS to TC_SETUP_QDISC_CBS to match the new convention.. Signed-off-by: Nogah Frankel Signed-off-by: Jiri Pirko Reviewed-by: Simon Horman Acked-by: Vinicius Costa Gomes Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/igb/igb_main.c | 2 +- include/linux/netdevice.h | 2 +- net/sched/sch_cbs.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index e22bce7cdacd..43cf39527660 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -2488,7 +2488,7 @@ static int igb_setup_tc(struct net_device *dev, enum tc_setup_type type, struct igb_adapter *adapter = netdev_priv(dev); switch (type) { - case TC_SETUP_CBS: + case TC_SETUP_QDISC_CBS: return igb_offload_cbs(adapter, type_data); default: diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 703885aed856..30f0f2928808 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -776,7 +776,7 @@ enum tc_setup_type { TC_SETUP_CLSMATCHALL, TC_SETUP_CLSBPF, TC_SETUP_BLOCK, - TC_SETUP_CBS, + TC_SETUP_QDISC_CBS, TC_SETUP_QDISC_RED, }; diff --git a/net/sched/sch_cbs.c b/net/sched/sch_cbs.c index bdb533b7fb8c..7a72980c1509 100644 --- a/net/sched/sch_cbs.c +++ b/net/sched/sch_cbs.c @@ -212,7 +212,7 @@ static void cbs_disable_offload(struct net_device *dev, cbs.queue = q->queue; cbs.enable = 0; - err = ops->ndo_setup_tc(dev, TC_SETUP_CBS, &cbs); + err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_CBS, &cbs); if (err < 0) pr_warn("Couldn't disable CBS offload for queue %d\n", cbs.queue); @@ -236,7 +236,7 @@ static int cbs_enable_offload(struct net_device *dev, struct cbs_sched_data *q, cbs.idleslope = opt->idleslope; cbs.sendslope = opt->sendslope; - err = ops->ndo_setup_tc(dev, TC_SETUP_CBS, &cbs); + err = ops->ndo_setup_tc(dev, TC_SETUP_QDISC_CBS, &cbs); if (err < 0) return err; -- cgit v1.2.3-59-g8ed1b From 620a5c860b774a81ce3f193eefb52bf4d128cca5 Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Mon, 6 Nov 2017 12:42:01 +0100 Subject: net: dsa: lan9303: Correct register names in comments Two comments refer to registers, but lack the LAN9303_ prefix. Fix that. Signed-off-by: Egil Hjelmeland Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- include/linux/dsa/lan9303.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/dsa/lan9303.h b/include/linux/dsa/lan9303.h index 05d8d136baab..f48a85c377de 100644 --- a/include/linux/dsa/lan9303.h +++ b/include/linux/dsa/lan9303.h @@ -13,8 +13,8 @@ struct lan9303_phy_ops { #define LAN9303_NUM_ALR_RECORDS 512 struct lan9303_alr_cache_entry { u8 mac_addr[ETH_ALEN]; - u8 port_map; /* Bitmap of ports. Zero if unused entry */ - u8 stp_override; /* non zero if set ALR_DAT1_AGE_OVERRID */ + u8 port_map; /* Bitmap of ports. Zero if unused entry */ + u8 stp_override; /* non zero if set LAN9303_ALR_DAT1_AGE_OVERRID */ }; struct lan9303 { @@ -28,7 +28,9 @@ struct lan9303 { struct mutex indirect_mutex; /* protect indexed register access */ const struct lan9303_phy_ops *ops; bool is_bridged; /* true if port 1 and 2 are bridged */ - u32 swe_port_state; /* remember SWE_PORT_STATE while not bridged */ + + /* remember LAN9303_SWE_PORT_STATE while not bridged */ + u32 swe_port_state; /* LAN9303 do not offer reading specific ALR entry. Cache all * static entries in a flat table **/ -- cgit v1.2.3-59-g8ed1b From 96c623e51f1c40bf524decc48c6fac7ce5dd41f7 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 6 Nov 2017 14:26:10 +0100 Subject: of: add of_property_read_variable_* dummy helpers Commit a67e9472da42 ("of: Add array read functions with min/max size limits") added a new interface for reading variable-length arrays from DT properties. One user was added in dsa recently and this causes a build error because that code can be built with CONFIG_OF disabled: net/dsa/dsa2.c: In function 'dsa_switch_parse_member_of': net/dsa/dsa2.c:678:7: error: implicit declaration of function 'of_property_read_variable_u32_array'; did you mean 'of_property_read_u32_array'? [-Werror=implicit-function-declaration] This adds a dummy functions for of_property_read_variable_u32_array() and a few others that had been missing here. I decided to move of_property_read_string() and of_property_read_string_helper() in the process to make it easier to compare the two sets of function prototypes to make sure they match. Fixes: 975e6e32215e ("net: dsa: rework switch parsing") Signed-off-by: Arnd Bergmann Acked-by: Rob Herring Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- include/linux/of.h | 62 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/linux/of.h b/include/linux/of.h index b240ed69dc96..b32d418d011a 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -675,12 +675,6 @@ static inline int of_property_count_elems_of_size(const struct device_node *np, return -ENOSYS; } -static inline int of_property_read_u32_index(const struct device_node *np, - const char *propname, u32 index, u32 *out_value) -{ - return -ENOSYS; -} - static inline int of_property_read_u8_array(const struct device_node *np, const char *propname, u8 *out_values, size_t sz) { @@ -707,16 +701,14 @@ static inline int of_property_read_u64_array(const struct device_node *np, return -ENOSYS; } -static inline int of_property_read_string(const struct device_node *np, - const char *propname, - const char **out_string) +static inline int of_property_read_u32_index(const struct device_node *np, + const char *propname, u32 index, u32 *out_value) { return -ENOSYS; } -static inline int of_property_read_string_helper(const struct device_node *np, - const char *propname, - const char **out_strs, size_t sz, int index) +static inline int of_property_read_u64_index(const struct device_node *np, + const char *propname, u32 index, u64 *out_value) { return -ENOSYS; } @@ -744,12 +736,51 @@ static inline int of_n_size_cells(struct device_node *np) return 0; } +static inline int of_property_read_variable_u8_array(const struct device_node *np, + const char *propname, u8 *out_values, + size_t sz_min, size_t sz_max) +{ + return -ENOSYS; +} + +static inline int of_property_read_variable_u16_array(const struct device_node *np, + const char *propname, u16 *out_values, + size_t sz_min, size_t sz_max) +{ + return -ENOSYS; +} + +static inline int of_property_read_variable_u32_array(const struct device_node *np, + const char *propname, + u32 *out_values, + size_t sz_min, + size_t sz_max) +{ + return -ENOSYS; +} + static inline int of_property_read_u64(const struct device_node *np, const char *propname, u64 *out_value) { return -ENOSYS; } +static inline int of_property_read_variable_u64_array(const struct device_node *np, + const char *propname, + u64 *out_values, + size_t sz_min, + size_t sz_max) +{ + return -ENOSYS; +} + +static inline int of_property_read_string(const struct device_node *np, + const char *propname, + const char **out_string) +{ + return -ENOSYS; +} + static inline int of_property_match_string(const struct device_node *np, const char *propname, const char *string) @@ -757,6 +788,13 @@ static inline int of_property_match_string(const struct device_node *np, return -ENOSYS; } +static inline int of_property_read_string_helper(const struct device_node *np, + const char *propname, + const char **out_strs, size_t sz, int index) +{ + return -ENOSYS; +} + static inline struct device_node *of_parse_phandle(const struct device_node *np, const char *phandle_name, int index) -- cgit v1.2.3-59-g8ed1b From b2d0f5d5dc53532e6f07bc546a476a55ebdfe0f3 Mon Sep 17 00:00:00 2001 From: Yi Yang Date: Tue, 7 Nov 2017 21:07:02 +0800 Subject: openvswitch: enable NSH support v16->17 - Fixed disputed check code: keep them in nsh_push and nsh_pop but also add them in __ovs_nla_copy_actions v15->v16 - Add csum recalculation for nsh_push, nsh_pop and set_nsh pointed out by Pravin - Move nsh key into the union with ipv4 and ipv6 and add check for nsh key in match_validate pointed out by Pravin - Add nsh check in validate_set and __ovs_nla_copy_actions v14->v15 - Check size in nsh_hdr_from_nlattr - Fixed four small issues pointed out By Jiri and Eric v13->v14 - Rename skb_push_nsh to nsh_push per Dave's comment - Rename skb_pop_nsh to nsh_pop per Dave's comment v12->v13 - Fix NSH header length check in set_nsh v11->v12 - Fix missing changes old comments pointed out - Fix new comments for v11 v10->v11 - Fix the left three disputable comments for v9 but not fixed in v10. v9->v10 - Change struct ovs_key_nsh to struct ovs_nsh_key_base base; __be32 context[NSH_MD1_CONTEXT_SIZE]; - Fix new comments for v9 v8->v9 - Fix build error reported by daily intel build because nsh module isn't selected by openvswitch v7->v8 - Rework nested value and mask for OVS_KEY_ATTR_NSH - Change pop_nsh to adapt to nsh kernel module - Fix many issues per comments from Jiri Benc v6->v7 - Remove NSH GSO patches in v6 because Jiri Benc reworked it as another patch series and they have been merged. - Change it to adapt to nsh kernel module added by NSH GSO patch series v5->v6 - Fix the rest comments for v4. - Add NSH GSO support for VxLAN-gpe + NSH and Eth + NSH. v4->v5 - Fix many comments by Jiri Benc and Eric Garver for v4. v3->v4 - Add new NSH match field ttl - Update NSH header to the latest format which will be final format and won't change per its author's confirmation. - Fix comments for v3. v2->v3 - Change OVS_KEY_ATTR_NSH to nested key to handle length-fixed attributes and length-variable attriubte more flexibly. - Remove struct ovs_action_push_nsh completely - Add code to handle nested attribute for SET_MASKED - Change PUSH_NSH to use the nested OVS_KEY_ATTR_NSH to transfer NSH header data. - Fix comments and coding style issues by Jiri and Eric v1->v2 - Change encap_nsh and decap_nsh to push_nsh and pop_nsh - Dynamically allocate struct ovs_action_push_nsh for length-variable metadata. OVS master and 2.8 branch has merged NSH userspace patch series, this patch is to enable NSH support in kernel data path in order that OVS can support NSH in compat mode by porting this. Signed-off-by: Yi Yang Acked-by: Jiri Benc Acked-by: Eric Garver Acked-by: Pravin Shelar Signed-off-by: David S. Miller --- include/net/nsh.h | 3 + include/uapi/linux/openvswitch.h | 29 ++++ net/nsh/nsh.c | 60 +++++++ net/openvswitch/Kconfig | 1 + net/openvswitch/actions.c | 116 +++++++++++++ net/openvswitch/flow.c | 51 ++++++ net/openvswitch/flow.h | 7 + net/openvswitch/flow_netlink.c | 343 ++++++++++++++++++++++++++++++++++++++- net/openvswitch/flow_netlink.h | 5 + 9 files changed, 613 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/nsh.h b/include/net/nsh.h index a1eaea20be96..350b1ad11c7f 100644 --- a/include/net/nsh.h +++ b/include/net/nsh.h @@ -304,4 +304,7 @@ static inline void nsh_set_flags_ttl_len(struct nshhdr *nsh, u8 flags, NSH_FLAGS_MASK | NSH_TTL_MASK | NSH_LEN_MASK); } +int nsh_push(struct sk_buff *skb, const struct nshhdr *pushed_nh); +int nsh_pop(struct sk_buff *skb); + #endif /* __NET_NSH_H */ diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index 501e4c4e2a03..ec75a685f1dd 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -336,6 +336,7 @@ enum ovs_key_attr { OVS_KEY_ATTR_CT_LABELS, /* 16-octet connection tracking label */ OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4, /* struct ovs_key_ct_tuple_ipv4 */ OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6, /* struct ovs_key_ct_tuple_ipv6 */ + OVS_KEY_ATTR_NSH, /* Nested set of ovs_nsh_key_* */ #ifdef __KERNEL__ OVS_KEY_ATTR_TUNNEL_INFO, /* struct ip_tunnel_info */ @@ -495,6 +496,30 @@ struct ovs_key_ct_tuple_ipv6 { __u8 ipv6_proto; }; +enum ovs_nsh_key_attr { + OVS_NSH_KEY_ATTR_UNSPEC, + OVS_NSH_KEY_ATTR_BASE, /* struct ovs_nsh_key_base. */ + OVS_NSH_KEY_ATTR_MD1, /* struct ovs_nsh_key_md1. */ + OVS_NSH_KEY_ATTR_MD2, /* variable-length octets for MD type 2. */ + __OVS_NSH_KEY_ATTR_MAX +}; + +#define OVS_NSH_KEY_ATTR_MAX (__OVS_NSH_KEY_ATTR_MAX - 1) + +struct ovs_nsh_key_base { + __u8 flags; + __u8 ttl; + __u8 mdtype; + __u8 np; + __be32 path_hdr; +}; + +#define NSH_MD1_CONTEXT_SIZE 4 + +struct ovs_nsh_key_md1 { + __be32 context[NSH_MD1_CONTEXT_SIZE]; +}; + /** * enum ovs_flow_attr - attributes for %OVS_FLOW_* commands. * @OVS_FLOW_ATTR_KEY: Nested %OVS_KEY_ATTR_* attributes specifying the flow @@ -811,6 +836,8 @@ struct ovs_action_push_eth { * @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the * packet. * @OVS_ACTION_ATTR_CT_CLEAR: Clear conntrack state from the packet. + * @OVS_ACTION_ATTR_PUSH_NSH: push NSH header to the packet. + * @OVS_ACTION_ATTR_POP_NSH: pop the outermost NSH header off the packet. * * Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all * fields within a header are modifiable, e.g. the IPv4 protocol and fragment @@ -841,6 +868,8 @@ enum ovs_action_attr { OVS_ACTION_ATTR_PUSH_ETH, /* struct ovs_action_push_eth. */ OVS_ACTION_ATTR_POP_ETH, /* No argument. */ OVS_ACTION_ATTR_CT_CLEAR, /* No argument. */ + OVS_ACTION_ATTR_PUSH_NSH, /* Nested OVS_NSH_KEY_ATTR_*. */ + OVS_ACTION_ATTR_POP_NSH, /* No argument. */ __OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted * from userspace. */ diff --git a/net/nsh/nsh.c b/net/nsh/nsh.c index 58fb827439a8..d7da99a0b0b8 100644 --- a/net/nsh/nsh.c +++ b/net/nsh/nsh.c @@ -14,6 +14,66 @@ #include #include +int nsh_push(struct sk_buff *skb, const struct nshhdr *pushed_nh) +{ + struct nshhdr *nh; + size_t length = nsh_hdr_len(pushed_nh); + u8 next_proto; + + if (skb->mac_len) { + next_proto = TUN_P_ETHERNET; + } else { + next_proto = tun_p_from_eth_p(skb->protocol); + if (!next_proto) + return -EAFNOSUPPORT; + } + + /* Add the NSH header */ + if (skb_cow_head(skb, length) < 0) + return -ENOMEM; + + skb_push(skb, length); + nh = (struct nshhdr *)(skb->data); + memcpy(nh, pushed_nh, length); + nh->np = next_proto; + skb_postpush_rcsum(skb, nh, length); + + skb->protocol = htons(ETH_P_NSH); + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_mac_len(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(nsh_push); + +int nsh_pop(struct sk_buff *skb) +{ + struct nshhdr *nh; + size_t length; + __be16 inner_proto; + + if (!pskb_may_pull(skb, NSH_BASE_HDR_LEN)) + return -ENOMEM; + nh = (struct nshhdr *)(skb->data); + length = nsh_hdr_len(nh); + inner_proto = tun_p_to_eth_p(nh->np); + if (!pskb_may_pull(skb, length)) + return -ENOMEM; + + if (!inner_proto) + return -EAFNOSUPPORT; + + skb_pull_rcsum(skb, length); + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_mac_len(skb); + skb->protocol = inner_proto; + + return 0; +} +EXPORT_SYMBOL_GPL(nsh_pop); + static struct sk_buff *nsh_gso_segment(struct sk_buff *skb, netdev_features_t features) { diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig index ce947292ae77..2650205cdaf9 100644 --- a/net/openvswitch/Kconfig +++ b/net/openvswitch/Kconfig @@ -14,6 +14,7 @@ config OPENVSWITCH select MPLS select NET_MPLS_GSO select DST_CACHE + select NET_NSH ---help--- Open vSwitch is a multilayer Ethernet switch targeted at virtualized environments. In addition to supporting a variety of features diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index a551232daf61..9a6a6d51e421 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -43,6 +43,7 @@ #include "flow.h" #include "conntrack.h" #include "vport.h" +#include "flow_netlink.h" struct deferred_action { struct sk_buff *skb; @@ -380,6 +381,38 @@ static int push_eth(struct sk_buff *skb, struct sw_flow_key *key, return 0; } +static int push_nsh(struct sk_buff *skb, struct sw_flow_key *key, + const struct nshhdr *nh) +{ + int err; + + err = nsh_push(skb, nh); + if (err) + return err; + + /* safe right before invalidate_flow_key */ + key->mac_proto = MAC_PROTO_NONE; + invalidate_flow_key(key); + return 0; +} + +static int pop_nsh(struct sk_buff *skb, struct sw_flow_key *key) +{ + int err; + + err = nsh_pop(skb); + if (err) + return err; + + /* safe right before invalidate_flow_key */ + if (skb->protocol == htons(ETH_P_TEB)) + key->mac_proto = MAC_PROTO_ETHERNET; + else + key->mac_proto = MAC_PROTO_NONE; + invalidate_flow_key(key); + return 0; +} + static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh, __be32 addr, __be32 new_addr) { @@ -602,6 +635,69 @@ static int set_ipv6(struct sk_buff *skb, struct sw_flow_key *flow_key, return 0; } +static int set_nsh(struct sk_buff *skb, struct sw_flow_key *flow_key, + const struct nlattr *a) +{ + struct nshhdr *nh; + size_t length; + int err; + u8 flags; + u8 ttl; + int i; + + struct ovs_key_nsh key; + struct ovs_key_nsh mask; + + err = nsh_key_from_nlattr(a, &key, &mask); + if (err) + return err; + + /* Make sure the NSH base header is there */ + if (!pskb_may_pull(skb, skb_network_offset(skb) + NSH_BASE_HDR_LEN)) + return -ENOMEM; + + nh = nsh_hdr(skb); + length = nsh_hdr_len(nh); + + /* Make sure the whole NSH header is there */ + err = skb_ensure_writable(skb, skb_network_offset(skb) + + length); + if (unlikely(err)) + return err; + + nh = nsh_hdr(skb); + skb_postpull_rcsum(skb, nh, length); + flags = nsh_get_flags(nh); + flags = OVS_MASKED(flags, key.base.flags, mask.base.flags); + flow_key->nsh.base.flags = flags; + ttl = nsh_get_ttl(nh); + ttl = OVS_MASKED(ttl, key.base.ttl, mask.base.ttl); + flow_key->nsh.base.ttl = ttl; + nsh_set_flags_and_ttl(nh, flags, ttl); + nh->path_hdr = OVS_MASKED(nh->path_hdr, key.base.path_hdr, + mask.base.path_hdr); + flow_key->nsh.base.path_hdr = nh->path_hdr; + switch (nh->mdtype) { + case NSH_M_TYPE1: + for (i = 0; i < NSH_MD1_CONTEXT_SIZE; i++) { + nh->md1.context[i] = + OVS_MASKED(nh->md1.context[i], key.context[i], + mask.context[i]); + } + memcpy(flow_key->nsh.context, nh->md1.context, + sizeof(nh->md1.context)); + break; + case NSH_M_TYPE2: + memset(flow_key->nsh.context, 0, + sizeof(flow_key->nsh.context)); + break; + default: + return -EINVAL; + } + skb_postpush_rcsum(skb, nh, length); + return 0; +} + /* Must follow skb_ensure_writable() since that can move the skb data. */ static void set_tp_port(struct sk_buff *skb, __be16 *port, __be16 new_port, __sum16 *check) @@ -1024,6 +1120,10 @@ static int execute_masked_set_action(struct sk_buff *skb, get_mask(a, struct ovs_key_ethernet *)); break; + case OVS_KEY_ATTR_NSH: + err = set_nsh(skb, flow_key, a); + break; + case OVS_KEY_ATTR_IPV4: err = set_ipv4(skb, flow_key, nla_data(a), get_mask(a, struct ovs_key_ipv4 *)); @@ -1214,6 +1314,22 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, case OVS_ACTION_ATTR_POP_ETH: err = pop_eth(skb, key); break; + + case OVS_ACTION_ATTR_PUSH_NSH: { + u8 buffer[NSH_HDR_MAX_LEN]; + struct nshhdr *nh = (struct nshhdr *)buffer; + + err = nsh_hdr_from_nlattr(nla_data(a), nh, + NSH_HDR_MAX_LEN); + if (unlikely(err)) + break; + err = push_nsh(skb, key, nh); + break; + } + + case OVS_ACTION_ATTR_POP_NSH: + err = pop_nsh(skb, key); + break; } if (unlikely(err)) { diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 8c94cef25a72..864ddb1e3642 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "conntrack.h" #include "datapath.h" @@ -490,6 +491,52 @@ invalid: return 0; } +static int parse_nsh(struct sk_buff *skb, struct sw_flow_key *key) +{ + struct nshhdr *nh; + unsigned int nh_ofs = skb_network_offset(skb); + u8 version, length; + int err; + + err = check_header(skb, nh_ofs + NSH_BASE_HDR_LEN); + if (unlikely(err)) + return err; + + nh = nsh_hdr(skb); + version = nsh_get_ver(nh); + length = nsh_hdr_len(nh); + + if (version != 0) + return -EINVAL; + + err = check_header(skb, nh_ofs + length); + if (unlikely(err)) + return err; + + nh = nsh_hdr(skb); + key->nsh.base.flags = nsh_get_flags(nh); + key->nsh.base.ttl = nsh_get_ttl(nh); + key->nsh.base.mdtype = nh->mdtype; + key->nsh.base.np = nh->np; + key->nsh.base.path_hdr = nh->path_hdr; + switch (key->nsh.base.mdtype) { + case NSH_M_TYPE1: + if (length != NSH_M_TYPE1_LEN) + return -EINVAL; + memcpy(key->nsh.context, nh->md1.context, + sizeof(nh->md1)); + break; + case NSH_M_TYPE2: + memset(key->nsh.context, 0, + sizeof(nh->md1)); + break; + default: + return -EINVAL; + } + + return 0; +} + /** * key_extract - extracts a flow key from an Ethernet frame. * @skb: sk_buff that contains the frame, with skb->data pointing to the @@ -735,6 +782,10 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) memset(&key->tp, 0, sizeof(key->tp)); } } + } else if (key->eth.type == htons(ETH_P_NSH)) { + error = parse_nsh(skb, key); + if (error) + return error; } return 0; } diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 1875bba4f865..c670dd24b8b7 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -35,6 +35,7 @@ #include #include #include +#include struct sk_buff; @@ -66,6 +67,11 @@ struct vlan_head { (offsetof(struct sw_flow_key, recirc_id) + \ FIELD_SIZEOF(struct sw_flow_key, recirc_id)) +struct ovs_key_nsh { + struct ovs_nsh_key_base base; + __be32 context[NSH_MD1_CONTEXT_SIZE]; +}; + struct sw_flow_key { u8 tun_opts[IP_TUNNEL_OPTS_MAX]; u8 tun_opts_len; @@ -143,6 +149,7 @@ struct sw_flow_key { } nd; }; } ipv6; + struct ovs_key_nsh nsh; /* network service header */ }; struct { /* Connection tracking fields not packed above. */ diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index dc0d79092e74..4201f9293af3 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include "flow_netlink.h" @@ -80,9 +81,11 @@ static bool actions_may_change_flow(const struct nlattr *actions) case OVS_ACTION_ATTR_HASH: case OVS_ACTION_ATTR_POP_ETH: case OVS_ACTION_ATTR_POP_MPLS: + case OVS_ACTION_ATTR_POP_NSH: case OVS_ACTION_ATTR_POP_VLAN: case OVS_ACTION_ATTR_PUSH_ETH: case OVS_ACTION_ATTR_PUSH_MPLS: + case OVS_ACTION_ATTR_PUSH_NSH: case OVS_ACTION_ATTR_PUSH_VLAN: case OVS_ACTION_ATTR_SAMPLE: case OVS_ACTION_ATTR_SET: @@ -175,7 +178,8 @@ static bool match_validate(const struct sw_flow_match *match, | (1 << OVS_KEY_ATTR_ICMPV6) | (1 << OVS_KEY_ATTR_ARP) | (1 << OVS_KEY_ATTR_ND) - | (1 << OVS_KEY_ATTR_MPLS)); + | (1 << OVS_KEY_ATTR_MPLS) + | (1 << OVS_KEY_ATTR_NSH)); /* Always allowed mask fields. */ mask_allowed |= ((1 << OVS_KEY_ATTR_TUNNEL) @@ -284,6 +288,14 @@ static bool match_validate(const struct sw_flow_match *match, } } + if (match->key->eth.type == htons(ETH_P_NSH)) { + key_expected |= 1 << OVS_KEY_ATTR_NSH; + if (match->mask && + match->mask->key.eth.type == htons(0xffff)) { + mask_allowed |= 1 << OVS_KEY_ATTR_NSH; + } + } + if ((key_attrs & key_expected) != key_expected) { /* Key attributes check failed. */ OVS_NLERR(log, "Missing key (keys=%llx, expected=%llx)", @@ -325,12 +337,25 @@ size_t ovs_tun_key_attr_size(void) + nla_total_size(4); /* OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS */ } +size_t ovs_nsh_key_attr_size(void) +{ + /* Whenever adding new OVS_NSH_KEY_ FIELDS, we should consider + * updating this function. + */ + return nla_total_size(NSH_BASE_HDR_LEN) /* OVS_NSH_KEY_ATTR_BASE */ + /* OVS_NSH_KEY_ATTR_MD1 and OVS_NSH_KEY_ATTR_MD2 are + * mutually exclusive, so the bigger one can cover + * the small one. + */ + + nla_total_size(NSH_CTX_HDRS_MAX_LEN); +} + size_t ovs_key_attr_size(void) { /* Whenever adding new OVS_KEY_ FIELDS, we should consider * updating this function. */ - BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 28); + BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 29); return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */ + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */ @@ -344,6 +369,8 @@ size_t ovs_key_attr_size(void) + nla_total_size(4) /* OVS_KEY_ATTR_CT_MARK */ + nla_total_size(16) /* OVS_KEY_ATTR_CT_LABELS */ + nla_total_size(40) /* OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6 */ + + nla_total_size(0) /* OVS_KEY_ATTR_NSH */ + + ovs_nsh_key_attr_size() + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */ + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */ @@ -377,6 +404,13 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] [OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS] = { .len = sizeof(u32) }, }; +static const struct ovs_len_tbl +ovs_nsh_key_attr_lens[OVS_NSH_KEY_ATTR_MAX + 1] = { + [OVS_NSH_KEY_ATTR_BASE] = { .len = sizeof(struct ovs_nsh_key_base) }, + [OVS_NSH_KEY_ATTR_MD1] = { .len = sizeof(struct ovs_nsh_key_md1) }, + [OVS_NSH_KEY_ATTR_MD2] = { .len = OVS_ATTR_VARIABLE }, +}; + /* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_ENCAP] = { .len = OVS_ATTR_NESTED }, @@ -409,6 +443,8 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { .len = sizeof(struct ovs_key_ct_tuple_ipv4) }, [OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6] = { .len = sizeof(struct ovs_key_ct_tuple_ipv6) }, + [OVS_KEY_ATTR_NSH] = { .len = OVS_ATTR_NESTED, + .next = ovs_nsh_key_attr_lens, }, }; static bool check_attr_len(unsigned int attr_len, unsigned int expected_len) @@ -1227,6 +1263,221 @@ static int metadata_from_nlattrs(struct net *net, struct sw_flow_match *match, return 0; } +int nsh_hdr_from_nlattr(const struct nlattr *attr, + struct nshhdr *nh, size_t size) +{ + struct nlattr *a; + int rem; + u8 flags = 0; + u8 ttl = 0; + int mdlen = 0; + + /* validate_nsh has check this, so we needn't do duplicate check here + */ + if (size < NSH_BASE_HDR_LEN) + return -ENOBUFS; + + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + + switch (type) { + case OVS_NSH_KEY_ATTR_BASE: { + const struct ovs_nsh_key_base *base = nla_data(a); + + flags = base->flags; + ttl = base->ttl; + nh->np = base->np; + nh->mdtype = base->mdtype; + nh->path_hdr = base->path_hdr; + break; + } + case OVS_NSH_KEY_ATTR_MD1: + mdlen = nla_len(a); + if (mdlen > size - NSH_BASE_HDR_LEN) + return -ENOBUFS; + memcpy(&nh->md1, nla_data(a), mdlen); + break; + + case OVS_NSH_KEY_ATTR_MD2: + mdlen = nla_len(a); + if (mdlen > size - NSH_BASE_HDR_LEN) + return -ENOBUFS; + memcpy(&nh->md2, nla_data(a), mdlen); + break; + + default: + return -EINVAL; + } + } + + /* nsh header length = NSH_BASE_HDR_LEN + mdlen */ + nh->ver_flags_ttl_len = 0; + nsh_set_flags_ttl_len(nh, flags, ttl, NSH_BASE_HDR_LEN + mdlen); + + return 0; +} + +int nsh_key_from_nlattr(const struct nlattr *attr, + struct ovs_key_nsh *nsh, struct ovs_key_nsh *nsh_mask) +{ + struct nlattr *a; + int rem; + + /* validate_nsh has check this, so we needn't do duplicate check here + */ + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + + switch (type) { + case OVS_NSH_KEY_ATTR_BASE: { + const struct ovs_nsh_key_base *base = nla_data(a); + const struct ovs_nsh_key_base *base_mask = base + 1; + + nsh->base = *base; + nsh_mask->base = *base_mask; + break; + } + case OVS_NSH_KEY_ATTR_MD1: { + const struct ovs_nsh_key_md1 *md1 = nla_data(a); + const struct ovs_nsh_key_md1 *md1_mask = md1 + 1; + + memcpy(nsh->context, md1->context, sizeof(*md1)); + memcpy(nsh_mask->context, md1_mask->context, + sizeof(*md1_mask)); + break; + } + case OVS_NSH_KEY_ATTR_MD2: + /* Not supported yet */ + return -ENOTSUPP; + default: + return -EINVAL; + } + } + + return 0; +} + +static int nsh_key_put_from_nlattr(const struct nlattr *attr, + struct sw_flow_match *match, bool is_mask, + bool is_push_nsh, bool log) +{ + struct nlattr *a; + int rem; + bool has_base = false; + bool has_md1 = false; + bool has_md2 = false; + u8 mdtype = 0; + int mdlen = 0; + + if (WARN_ON(is_push_nsh && is_mask)) + return -EINVAL; + + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + int i; + + if (type > OVS_NSH_KEY_ATTR_MAX) { + OVS_NLERR(log, "nsh attr %d is out of range max %d", + type, OVS_NSH_KEY_ATTR_MAX); + return -EINVAL; + } + + if (!check_attr_len(nla_len(a), + ovs_nsh_key_attr_lens[type].len)) { + OVS_NLERR( + log, + "nsh attr %d has unexpected len %d expected %d", + type, + nla_len(a), + ovs_nsh_key_attr_lens[type].len + ); + return -EINVAL; + } + + switch (type) { + case OVS_NSH_KEY_ATTR_BASE: { + const struct ovs_nsh_key_base *base = nla_data(a); + + has_base = true; + mdtype = base->mdtype; + SW_FLOW_KEY_PUT(match, nsh.base.flags, + base->flags, is_mask); + SW_FLOW_KEY_PUT(match, nsh.base.ttl, + base->ttl, is_mask); + SW_FLOW_KEY_PUT(match, nsh.base.mdtype, + base->mdtype, is_mask); + SW_FLOW_KEY_PUT(match, nsh.base.np, + base->np, is_mask); + SW_FLOW_KEY_PUT(match, nsh.base.path_hdr, + base->path_hdr, is_mask); + break; + } + case OVS_NSH_KEY_ATTR_MD1: { + const struct ovs_nsh_key_md1 *md1 = nla_data(a); + + has_md1 = true; + for (i = 0; i < NSH_MD1_CONTEXT_SIZE; i++) + SW_FLOW_KEY_PUT(match, nsh.context[i], + md1->context[i], is_mask); + break; + } + case OVS_NSH_KEY_ATTR_MD2: + if (!is_push_nsh) /* Not supported MD type 2 yet */ + return -ENOTSUPP; + + has_md2 = true; + mdlen = nla_len(a); + if (mdlen > NSH_CTX_HDRS_MAX_LEN || mdlen <= 0) { + OVS_NLERR( + log, + "Invalid MD length %d for MD type %d", + mdlen, + mdtype + ); + return -EINVAL; + } + break; + default: + OVS_NLERR(log, "Unknown nsh attribute %d", + type); + return -EINVAL; + } + } + + if (rem > 0) { + OVS_NLERR(log, "nsh attribute has %d unknown bytes.", rem); + return -EINVAL; + } + + if (has_md1 && has_md2) { + OVS_NLERR( + 1, + "invalid nsh attribute: md1 and md2 are exclusive." + ); + return -EINVAL; + } + + if (!is_mask) { + if ((has_md1 && mdtype != NSH_M_TYPE1) || + (has_md2 && mdtype != NSH_M_TYPE2)) { + OVS_NLERR(1, "nsh attribute has unmatched MD type %d.", + mdtype); + return -EINVAL; + } + + if (is_push_nsh && + (!has_base || (!has_md1 && !has_md2))) { + OVS_NLERR( + 1, + "push_nsh: missing base or metadata attributes" + ); + return -EINVAL; + } + } + + return 0; +} + static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match, u64 attrs, const struct nlattr **a, bool is_mask, bool log) @@ -1354,6 +1605,13 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match, attrs &= ~(1 << OVS_KEY_ATTR_ARP); } + if (attrs & (1 << OVS_KEY_ATTR_NSH)) { + if (nsh_key_put_from_nlattr(a[OVS_KEY_ATTR_NSH], match, + is_mask, false, log) < 0) + return -EINVAL; + attrs &= ~(1 << OVS_KEY_ATTR_NSH); + } + if (attrs & (1 << OVS_KEY_ATTR_MPLS)) { const struct ovs_key_mpls *mpls_key; @@ -1670,6 +1928,34 @@ static int ovs_nla_put_vlan(struct sk_buff *skb, const struct vlan_head *vh, return 0; } +static int nsh_key_to_nlattr(const struct ovs_key_nsh *nsh, bool is_mask, + struct sk_buff *skb) +{ + struct nlattr *start; + + start = nla_nest_start(skb, OVS_KEY_ATTR_NSH); + if (!start) + return -EMSGSIZE; + + if (nla_put(skb, OVS_NSH_KEY_ATTR_BASE, sizeof(nsh->base), &nsh->base)) + goto nla_put_failure; + + if (is_mask || nsh->base.mdtype == NSH_M_TYPE1) { + if (nla_put(skb, OVS_NSH_KEY_ATTR_MD1, + sizeof(nsh->context), nsh->context)) + goto nla_put_failure; + } + + /* Don't support MD type 2 yet */ + + nla_nest_end(skb, start); + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + static int __ovs_nla_put_key(const struct sw_flow_key *swkey, const struct sw_flow_key *output, bool is_mask, struct sk_buff *skb) @@ -1798,6 +2084,9 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey, ipv6_key->ipv6_tclass = output->ip.tos; ipv6_key->ipv6_hlimit = output->ip.ttl; ipv6_key->ipv6_frag = output->ip.frag; + } else if (swkey->eth.type == htons(ETH_P_NSH)) { + if (nsh_key_to_nlattr(&output->nsh, is_mask, skb)) + goto nla_put_failure; } else if (swkey->eth.type == htons(ETH_P_ARP) || swkey->eth.type == htons(ETH_P_RARP)) { struct ovs_key_arp *arp_key; @@ -2292,6 +2581,19 @@ static int validate_and_copy_set_tun(const struct nlattr *attr, return err; } +static bool validate_nsh(const struct nlattr *attr, bool is_mask, + bool is_push_nsh, bool log) +{ + struct sw_flow_match match; + struct sw_flow_key key; + int ret = 0; + + ovs_match_init(&match, &key, true, NULL); + ret = nsh_key_put_from_nlattr(attr, &match, is_mask, + is_push_nsh, log); + return !ret; +} + /* Return false if there are any non-masked bits set. * Mask follows data immediately, before any netlink padding. */ @@ -2434,6 +2736,13 @@ static int validate_set(const struct nlattr *a, break; + case OVS_KEY_ATTR_NSH: + if (eth_type != htons(ETH_P_NSH)) + return -EINVAL; + if (!validate_nsh(nla_data(a), masked, false, log)) + return -EINVAL; + break; + default: return -EINVAL; } @@ -2533,6 +2842,8 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, [OVS_ACTION_ATTR_TRUNC] = sizeof(struct ovs_action_trunc), [OVS_ACTION_ATTR_PUSH_ETH] = sizeof(struct ovs_action_push_eth), [OVS_ACTION_ATTR_POP_ETH] = 0, + [OVS_ACTION_ATTR_PUSH_NSH] = (u32)-1, + [OVS_ACTION_ATTR_POP_NSH] = 0, }; const struct ovs_action_push_vlan *vlan; int type = nla_type(a); @@ -2690,6 +3001,34 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, mac_proto = MAC_PROTO_ETHERNET; break; + case OVS_ACTION_ATTR_PUSH_NSH: + if (mac_proto != MAC_PROTO_ETHERNET) { + u8 next_proto; + + next_proto = tun_p_from_eth_p(eth_type); + if (!next_proto) + return -EINVAL; + } + mac_proto = MAC_PROTO_NONE; + if (!validate_nsh(nla_data(a), false, true, true)) + return -EINVAL; + break; + + case OVS_ACTION_ATTR_POP_NSH: { + __be16 inner_proto; + + if (eth_type != htons(ETH_P_NSH)) + return -EINVAL; + inner_proto = tun_p_to_eth_p(key->nsh.base.np); + if (!inner_proto) + return -EINVAL; + if (key->nsh.base.np == TUN_P_ETHERNET) + mac_proto = MAC_PROTO_ETHERNET; + else + mac_proto = MAC_PROTO_NONE; + break; + } + default: OVS_NLERR(log, "Unknown Action type %d", type); return -EINVAL; diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h index 929c665ac3aa..6657606b2b47 100644 --- a/net/openvswitch/flow_netlink.h +++ b/net/openvswitch/flow_netlink.h @@ -79,4 +79,9 @@ int ovs_nla_put_actions(const struct nlattr *attr, void ovs_nla_free_flow_actions(struct sw_flow_actions *); void ovs_nla_free_flow_actions_rcu(struct sw_flow_actions *); +int nsh_key_from_nlattr(const struct nlattr *attr, struct ovs_key_nsh *nsh, + struct ovs_key_nsh *nsh_mask); +int nsh_hdr_from_nlattr(const struct nlattr *attr, struct nshhdr *nh, + size_t size); + #endif /* flow_netlink.h */ -- cgit v1.2.3-59-g8ed1b From 0759e80b84e34a84e7e46e2b1adb528c83d84a47 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 7 Nov 2017 11:33:49 +0100 Subject: PM / QoS: Fix device resume latency framework The special value of 0 for device resume latency PM QoS means "no restriction", but there are two problems with that. First, device resume latency PM QoS requests with 0 as the value are always put in front of requests with positive values in the priority lists used internally by the PM QoS framework, causing 0 to be chosen as an effective constraint value. However, that 0 is then interpreted as "no restriction" effectively overriding the other requests with specific restrictions which is incorrect. Second, the users of device resume latency PM QoS have no way to specify that *any* resume latency at all should be avoided, which is an artificial limitation in general. To address these issues, modify device resume latency PM QoS to use S32_MAX as the "no constraint" value and 0 as the "no latency at all" one and rework its users (the cpuidle menu governor, the genpd QoS governor and the runtime PM framework) to follow these changes. Also add a special "n/a" value to the corresponding user space I/F to allow user space to indicate that it cannot accept any resume latencies at all for the given device. Fixes: 85dc0b8a4019 (PM / QoS: Make it possible to expose PM QoS latency constraints) Link: https://bugzilla.kernel.org/show_bug.cgi?id=197323 Reported-by: Reinette Chatre Signed-off-by: Rafael J. Wysocki Tested-by: Reinette Chatre Tested-by: Geert Uytterhoeven Tested-by: Tero Kristo Reviewed-by: Ramesh Thomas --- Documentation/ABI/testing/sysfs-devices-power | 4 ++- drivers/base/cpu.c | 3 +- drivers/base/power/domain.c | 2 +- drivers/base/power/domain_governor.c | 40 +++++++++++---------------- drivers/base/power/qos.c | 5 +++- drivers/base/power/runtime.c | 2 +- drivers/base/power/sysfs.c | 25 ++++++++++++++--- drivers/cpuidle/governors/menu.c | 4 +-- include/linux/pm_qos.h | 26 +++++++++++------ 9 files changed, 68 insertions(+), 43 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index f4b24c327665..80a00f7b6667 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -211,7 +211,9 @@ Description: device, after it has been suspended at run time, from a resume request to the moment the device will be ready to process I/O, in microseconds. If it is equal to 0, however, this means that - the PM QoS resume latency may be arbitrary. + the PM QoS resume latency may be arbitrary and the special value + "n/a" means that user space cannot accept any resume latency at + all for the given device. Not all drivers support this attribute. If it isn't supported, it is not present. diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 321cd7b4d817..227bac5f1191 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -377,7 +377,8 @@ int register_cpu(struct cpu *cpu, int num) per_cpu(cpu_sys_devices, num) = &cpu->dev; register_cpu_under_node(num, cpu_to_node(num)); - dev_pm_qos_expose_latency_limit(&cpu->dev, 0); + dev_pm_qos_expose_latency_limit(&cpu->dev, + PM_QOS_RESUME_LATENCY_NO_CONSTRAINT); return 0; } diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 679c79545e42..24e39ce27bd8 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1326,7 +1326,7 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, gpd_data->base.dev = dev; gpd_data->td.constraint_changed = true; - gpd_data->td.effective_constraint_ns = 0; + gpd_data->td.effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS; gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; spin_lock_irq(&dev->power.lock); diff --git a/drivers/base/power/domain_governor.c b/drivers/base/power/domain_governor.c index e4cca8adab32..99896fbf18e4 100644 --- a/drivers/base/power/domain_governor.c +++ b/drivers/base/power/domain_governor.c @@ -33,15 +33,10 @@ static int dev_update_qos_constraint(struct device *dev, void *data) * known at this point anyway). */ constraint_ns = dev_pm_qos_read_value(dev); - if (constraint_ns > 0) - constraint_ns *= NSEC_PER_USEC; + constraint_ns *= NSEC_PER_USEC; } - /* 0 means "no constraint" */ - if (constraint_ns == 0) - return 0; - - if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0) + if (constraint_ns < *constraint_ns_p) *constraint_ns_p = constraint_ns; return 0; @@ -69,12 +64,12 @@ static bool default_suspend_ok(struct device *dev) } td->constraint_changed = false; td->cached_suspend_ok = false; - td->effective_constraint_ns = -1; + td->effective_constraint_ns = 0; constraint_ns = __dev_pm_qos_read_value(dev); spin_unlock_irqrestore(&dev->power.lock, flags); - if (constraint_ns < 0) + if (constraint_ns == 0) return false; constraint_ns *= NSEC_PER_USEC; @@ -87,25 +82,25 @@ static bool default_suspend_ok(struct device *dev) device_for_each_child(dev, &constraint_ns, dev_update_qos_constraint); - if (constraint_ns == 0) { + if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS) { /* "No restriction", so the device is allowed to suspend. */ - td->effective_constraint_ns = 0; + td->effective_constraint_ns = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS; td->cached_suspend_ok = true; - } else if (constraint_ns < 0) { + } else if (constraint_ns == 0) { /* * This triggers if one of the children that don't belong to a - * domain has a negative PM QoS constraint and it's better not - * to suspend then. effective_constraint_ns is negative already - * and cached_suspend_ok is false, so bail out. + * domain has a zero PM QoS constraint and it's better not to + * suspend then. effective_constraint_ns is zero already and + * cached_suspend_ok is false, so bail out. */ return false; } else { constraint_ns -= td->suspend_latency_ns + td->resume_latency_ns; /* - * effective_constraint_ns is negative already and - * cached_suspend_ok is false, so if the computed value is not - * positive, return right away. + * effective_constraint_ns is zero already and cached_suspend_ok + * is false, so if the computed value is not positive, return + * right away. */ if (constraint_ns <= 0) return false; @@ -174,13 +169,10 @@ static bool __default_power_down_ok(struct dev_pm_domain *pd, td = &to_gpd_data(pdd)->td; constraint_ns = td->effective_constraint_ns; /* - * Negative values mean "no suspend at all" and this runs only - * when all devices in the domain are suspended, so it must be - * 0 at least. - * - * 0 means "no constraint" + * Zero means "no suspend at all" and this runs only when all + * devices in the domain are suspended, so it must be positive. */ - if (constraint_ns == 0) + if (constraint_ns == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS) continue; if (constraint_ns <= off_on_time_ns) diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 277d43a83f53..3382542b39b7 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -139,6 +139,9 @@ static int apply_constraint(struct dev_pm_qos_request *req, switch(req->type) { case DEV_PM_QOS_RESUME_LATENCY: + if (WARN_ON(action != PM_QOS_REMOVE_REQ && value < 0)) + value = 0; + ret = pm_qos_update_target(&qos->resume_latency, &req->data.pnode, action, value); break; @@ -189,7 +192,7 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) plist_head_init(&c->list); c->target_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; c->default_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; - c->no_constraint_value = PM_QOS_RESUME_LATENCY_DEFAULT_VALUE; + c->no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; c->type = PM_QOS_MIN; c->notifiers = n; diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 7bcf80fa9ada..13e015905543 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -253,7 +253,7 @@ static int rpm_check_suspend_allowed(struct device *dev) || (dev->power.request_pending && dev->power.request == RPM_REQ_RESUME)) retval = -EAGAIN; - else if (__dev_pm_qos_read_value(dev) < 0) + else if (__dev_pm_qos_read_value(dev) == 0) retval = -EPERM; else if (dev->power.runtime_status == RPM_SUSPENDED) retval = 1; diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 29bf28fef136..e153e28b1857 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -218,7 +218,14 @@ static ssize_t pm_qos_resume_latency_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev_pm_qos_requested_resume_latency(dev)); + s32 value = dev_pm_qos_requested_resume_latency(dev); + + if (value == 0) + return sprintf(buf, "n/a\n"); + else if (value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT) + value = 0; + + return sprintf(buf, "%d\n", value); } static ssize_t pm_qos_resume_latency_store(struct device *dev, @@ -228,11 +235,21 @@ static ssize_t pm_qos_resume_latency_store(struct device *dev, s32 value; int ret; - if (kstrtos32(buf, 0, &value)) - return -EINVAL; + if (!kstrtos32(buf, 0, &value)) { + /* + * Prevent users from writing negative or "no constraint" values + * directly. + */ + if (value < 0 || value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT) + return -EINVAL; - if (value < 0) + if (value == 0) + value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; + } else if (!strcmp(buf, "n/a") || !strcmp(buf, "n/a\n")) { + value = 0; + } else { return -EINVAL; + } ret = dev_pm_qos_update_request(dev->power.qos->resume_latency_req, value); diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index 48eaf2879228..aa390404e85f 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -298,8 +298,8 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev) data->needs_update = 0; } - /* resume_latency is 0 means no restriction */ - if (resume_latency && resume_latency < latency_req) + if (resume_latency < latency_req && + resume_latency != PM_QOS_RESUME_LATENCY_NO_CONSTRAINT) latency_req = resume_latency; /* Special case when user has set very strict latency requirement */ diff --git a/include/linux/pm_qos.h b/include/linux/pm_qos.h index 51f0d7e0b15f..2a3b36da61b1 100644 --- a/include/linux/pm_qos.h +++ b/include/linux/pm_qos.h @@ -27,16 +27,19 @@ enum pm_qos_flags_status { PM_QOS_FLAGS_ALL, }; -#define PM_QOS_DEFAULT_VALUE -1 +#define PM_QOS_DEFAULT_VALUE (-1) +#define PM_QOS_LATENCY_ANY S32_MAX +#define PM_QOS_LATENCY_ANY_NS ((s64)PM_QOS_LATENCY_ANY * NSEC_PER_USEC) #define PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) #define PM_QOS_NETWORK_LAT_DEFAULT_VALUE (2000 * USEC_PER_SEC) #define PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE 0 #define PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE 0 -#define PM_QOS_RESUME_LATENCY_DEFAULT_VALUE 0 +#define PM_QOS_RESUME_LATENCY_DEFAULT_VALUE PM_QOS_LATENCY_ANY +#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT PM_QOS_LATENCY_ANY +#define PM_QOS_RESUME_LATENCY_NO_CONSTRAINT_NS PM_QOS_LATENCY_ANY_NS #define PM_QOS_LATENCY_TOLERANCE_DEFAULT_VALUE 0 #define PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT (-1) -#define PM_QOS_LATENCY_ANY ((s32)(~(__u32)0 >> 1)) #define PM_QOS_FLAG_NO_POWER_OFF (1 << 0) @@ -173,7 +176,8 @@ static inline s32 dev_pm_qos_requested_flags(struct device *dev) static inline s32 dev_pm_qos_raw_read_value(struct device *dev) { return IS_ERR_OR_NULL(dev->power.qos) ? - 0 : pm_qos_read_value(&dev->power.qos->resume_latency); + PM_QOS_RESUME_LATENCY_NO_CONSTRAINT : + pm_qos_read_value(&dev->power.qos->resume_latency); } #else static inline enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, @@ -183,9 +187,9 @@ static inline enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask) { return PM_QOS_FLAGS_UNDEFINED; } static inline s32 __dev_pm_qos_read_value(struct device *dev) - { return 0; } + { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; } static inline s32 dev_pm_qos_read_value(struct device *dev) - { return 0; } + { return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; } static inline int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, enum dev_pm_qos_req_type type, @@ -231,9 +235,15 @@ static inline int dev_pm_qos_expose_latency_tolerance(struct device *dev) { return 0; } static inline void dev_pm_qos_hide_latency_tolerance(struct device *dev) {} -static inline s32 dev_pm_qos_requested_resume_latency(struct device *dev) { return 0; } +static inline s32 dev_pm_qos_requested_resume_latency(struct device *dev) +{ + return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; +} static inline s32 dev_pm_qos_requested_flags(struct device *dev) { return 0; } -static inline s32 dev_pm_qos_raw_read_value(struct device *dev) { return 0; } +static inline s32 dev_pm_qos_raw_read_value(struct device *dev) +{ + return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT; +} #endif #endif -- cgit v1.2.3-59-g8ed1b From e0e1e39de490a2d9b8a173363ccf2415ddada871 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sat, 28 Oct 2017 15:37:17 +0200 Subject: pinctrl: Add skew-delay pin config and bindings Some pin controllers (such as the Gemini) can control the expected clock skew and output delay on certain pins with a sub-nanosecond granularity. This is typically done by shunting in a number of double inverters in front of or behind the pin. Make it possible to configure this with a generic binding. Cc: devicetree@vger.kernel.org Acked-by: Rob Herring Acked-by: Hans Ulli Kroll Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt | 4 ++++ drivers/pinctrl/pinconf-generic.c | 2 ++ include/linux/pinctrl/pinconf-generic.h | 5 +++++ 3 files changed, 11 insertions(+) (limited to 'include') diff --git a/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt b/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt index 4483cc31e531..ad9bbbba36e9 100644 --- a/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt +++ b/Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt @@ -271,6 +271,10 @@ output-high - set the pin to output mode with high level sleep-hardware-state - indicate this is sleep related state which will be programmed into the registers for the sleep state. slew-rate - set the slew rate +skew-delay - this affects the expected clock skew on input pins + and the delay before latching a value to an output + pin. Typically indicates how many double-inverters are + used to delay the signal. For example: diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 8eaa25c3384f..b4f7f8a458ea 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -49,6 +49,7 @@ static const struct pin_config_item conf_items[] = { PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector", true), PCONFDUMP(PIN_CONFIG_SLEEP_HARDWARE_STATE, "sleep hardware state", NULL, false), PCONFDUMP(PIN_CONFIG_SLEW_RATE, "slew rate", NULL, true), + PCONFDUMP(PIN_CONFIG_SKEW_DELAY, "skew delay", NULL, true), }; static void pinconf_generic_dump_one(struct pinctrl_dev *pctldev, @@ -181,6 +182,7 @@ static const struct pinconf_generic_params dt_params[] = { { "power-source", PIN_CONFIG_POWER_SOURCE, 0 }, { "sleep-hardware-state", PIN_CONFIG_SLEEP_HARDWARE_STATE, 0 }, { "slew-rate", PIN_CONFIG_SLEW_RATE, 0 }, + { "skew-delay", PIN_CONFIG_SKEW_DELAY, 0 }, }; /** diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h index 5d8bc7f21c2a..ec6dadcc1fde 100644 --- a/include/linux/pinctrl/pinconf-generic.h +++ b/include/linux/pinctrl/pinconf-generic.h @@ -90,6 +90,10 @@ * @PIN_CONFIG_SLEW_RATE: if the pin can select slew rate, the argument to * this parameter (on a custom format) tells the driver which alternative * slew rate to use. + * @PIN_CONFIG_SKEW_DELAY: if the pin has programmable skew rate (on inputs) + * or latch delay (on outputs) this parameter (in a custom format) + * specifies the clock skew or latch delay. It typically controls how + * many double inverters are put in front of the line. * @PIN_CONFIG_END: this is the last enumerator for pin configurations, if * you need to pass in custom configurations to the pin controller, use * PIN_CONFIG_END+1 as the base offset. @@ -117,6 +121,7 @@ enum pin_config_param { PIN_CONFIG_POWER_SOURCE, PIN_CONFIG_SLEEP_HARDWARE_STATE, PIN_CONFIG_SLEW_RATE, + PIN_CONFIG_SKEW_DELAY, PIN_CONFIG_END = 0x7F, PIN_CONFIG_MAX = 0xFF, }; -- cgit v1.2.3-59-g8ed1b From c44eafd79be666e7c81d22e215c945b27f2785f7 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:45 +0100 Subject: gpio: Introduce struct gpio_irq_chip This new structure will be used to group all fields related to interrupt handling in a GPIO chip. Doing so will properly namespace these fields and make it easier to distinguish which fields are used for IRQ support. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- include/linux/gpio/driver.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) (limited to 'include') diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index ed04fa2a00a8..36a065521fa0 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -19,6 +19,36 @@ struct module; #ifdef CONFIG_GPIOLIB +#ifdef CONFIG_GPIOLIB_IRQCHIP +/** + * struct gpio_irq_chip - GPIO interrupt controller + */ +struct gpio_irq_chip { + /** + * @domain_ops: + * + * Table of interrupt domain operations for this IRQ chip. + */ + const struct irq_domain_ops *domain_ops; + + /** + * @parent_handler: + * + * The interrupt handler for the GPIO chip's parent interrupts, may be + * NULL if the parent interrupts are nested rather than cascaded. + */ + irq_flow_handler_t parent_handler; + + /** + * @parent_handler_data: + * + * Data associated, and passed to, the handler for the parent + * interrupt. + */ + void *parent_handler_data; +}; +#endif + /** * struct gpio_chip - abstract a GPIO controller * @label: a functional name for the GPIO device, such as a part @@ -176,6 +206,14 @@ struct gpio_chip { bool irq_need_valid_mask; unsigned long *irq_valid_mask; struct lock_class_key *lock_key; + + /** + * @irq: + * + * Integrates interrupt chip functionality with the GPIO chip. Can be + * used to handle IRQs for most practical cases. + */ + struct gpio_irq_chip irq; #endif #if defined(CONFIG_OF_GPIO) -- cgit v1.2.3-59-g8ed1b From da80ff81a8f54611b834d73149f8ac0d59151c87 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:46 +0100 Subject: gpio: Move irqchip into struct gpio_irq_chip In order to consolidate the multiple ways to associate an IRQ chip with a GPIO chip, move more fields into the new struct gpio_irq_chip. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 14 +++++++------- include/linux/gpio/driver.h | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3827f0767101..d3d0b3134ba3 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1646,7 +1646,7 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, * category than their parents, so it won't report false recursion. */ irq_set_lockdep_class(irq, chip->lock_key); - irq_set_chip_and_handler(irq, chip->irqchip, chip->irq_handler); + irq_set_chip_and_handler(irq, chip->irq.chip, chip->irq_handler); /* Chips that use nested thread handlers have them marked */ if (chip->irq_nested) irq_set_nested_thread(irq, 1); @@ -1739,10 +1739,10 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) irq_domain_remove(gpiochip->irqdomain); } - if (gpiochip->irqchip) { - gpiochip->irqchip->irq_request_resources = NULL; - gpiochip->irqchip->irq_release_resources = NULL; - gpiochip->irqchip = NULL; + if (gpiochip->irq.chip) { + gpiochip->irq.chip->irq_request_resources = NULL; + gpiochip->irq.chip->irq_release_resources = NULL; + gpiochip->irq.chip = NULL; } gpiochip_irqchip_free_valid_mask(gpiochip); @@ -1817,7 +1817,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, type = IRQ_TYPE_NONE; } - gpiochip->irqchip = irqchip; + gpiochip->irq.chip = irqchip; gpiochip->irq_handler = handler; gpiochip->irq_default_type = type; gpiochip->to_irq = gpiochip_to_irq; @@ -1826,7 +1826,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, gpiochip->ngpio, first_irq, &gpiochip_domain_ops, gpiochip); if (!gpiochip->irqdomain) { - gpiochip->irqchip = NULL; + gpiochip->irq.chip = NULL; return -EINVAL; } diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 36a065521fa0..a79b3b18fadd 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -24,6 +24,13 @@ struct module; * struct gpio_irq_chip - GPIO interrupt controller */ struct gpio_irq_chip { + /** + * @chip: + * + * GPIO IRQ chip implementation, provided by GPIO driver. + */ + struct irq_chip *chip; + /** * @domain_ops: * @@ -47,6 +54,11 @@ struct gpio_irq_chip { */ void *parent_handler_data; }; + +static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) +{ + return container_of(chip, struct gpio_irq_chip, chip); +} #endif /** @@ -112,7 +124,6 @@ struct gpio_irq_chip { * safely. * @bgpio_dir: shadowed direction register for generic GPIO to clear/set * direction safely. - * @irqchip: GPIO IRQ chip impl, provided by GPIO driver * @irqdomain: Interrupt translation domain; responsible for mapping * between GPIO hwirq number and linux irq number * @irq_handler: the irq handler to use (often a predefined irq core function) @@ -197,7 +208,6 @@ struct gpio_chip { * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib * to handle IRQs for most practical cases. */ - struct irq_chip *irqchip; struct irq_domain *irqdomain; irq_flow_handler_t irq_handler; unsigned int irq_default_type; -- cgit v1.2.3-59-g8ed1b From f0fbe7bce733561b76a5b55c5f4625888acd3792 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:47 +0100 Subject: gpio: Move irqdomain into struct gpio_irq_chip In order to consolidate the multiple ways to associate an IRQ chip with a GPIO chip, move more fields into the new struct gpio_irq_chip. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- Documentation/gpio/driver.txt | 2 +- drivers/bcma/driver_gpio.c | 2 +- drivers/gpio/gpio-104-dio-48e.c | 2 +- drivers/gpio/gpio-104-idi-48.c | 2 +- drivers/gpio/gpio-104-idio-16.c | 2 +- drivers/gpio/gpio-adnp.c | 2 +- drivers/gpio/gpio-altera.c | 4 ++-- drivers/gpio/gpio-aspeed.c | 2 +- drivers/gpio/gpio-ath79.c | 2 +- drivers/gpio/gpio-crystalcove.c | 2 +- drivers/gpio/gpio-dln2.c | 2 +- drivers/gpio/gpio-ftgpio010.c | 2 +- drivers/gpio/gpio-ingenic.c | 2 +- drivers/gpio/gpio-intel-mid.c | 2 +- drivers/gpio/gpio-lynxpoint.c | 2 +- drivers/gpio/gpio-max732x.c | 2 +- drivers/gpio/gpio-merrifield.c | 2 +- drivers/gpio/gpio-omap.c | 2 +- drivers/gpio/gpio-pca953x.c | 2 +- drivers/gpio/gpio-pcf857x.c | 2 +- drivers/gpio/gpio-pci-idio-16.c | 2 +- drivers/gpio/gpio-pl061.c | 2 +- drivers/gpio/gpio-rcar.c | 2 +- drivers/gpio/gpio-reg.c | 4 ++-- drivers/gpio/gpio-stmpe.c | 2 +- drivers/gpio/gpio-tc3589x.c | 2 +- drivers/gpio/gpio-vf610.c | 2 +- drivers/gpio/gpio-wcove.c | 2 +- drivers/gpio/gpio-ws16c48.c | 2 +- drivers/gpio/gpio-xgene-sb.c | 2 +- drivers/gpio/gpio-xlp.c | 2 +- drivers/gpio/gpio-zx.c | 2 +- drivers/gpio/gpio-zynq.c | 2 +- drivers/gpio/gpiolib.c | 22 ++++++++++++---------- drivers/pinctrl/bcm/pinctrl-bcm2835.c | 4 ++-- drivers/pinctrl/bcm/pinctrl-iproc-gpio.c | 2 +- drivers/pinctrl/intel/pinctrl-baytrail.c | 2 +- drivers/pinctrl/intel/pinctrl-cherryview.c | 2 +- drivers/pinctrl/intel/pinctrl-intel.c | 2 +- drivers/pinctrl/mvebu/pinctrl-armada-37xx.c | 2 +- drivers/pinctrl/nomadik/pinctrl-nomadik.c | 4 ++-- drivers/pinctrl/pinctrl-amd.c | 2 +- drivers/pinctrl/pinctrl-at91.c | 2 +- drivers/pinctrl/pinctrl-coh901.c | 2 +- drivers/pinctrl/pinctrl-mcp23s08.c | 2 +- drivers/pinctrl/pinctrl-oxnas.c | 2 +- drivers/pinctrl/pinctrl-pic32.c | 2 +- drivers/pinctrl/pinctrl-pistachio.c | 2 +- drivers/pinctrl/pinctrl-st.c | 2 +- drivers/pinctrl/pinctrl-sx150x.c | 2 +- drivers/pinctrl/qcom/pinctrl-msm.c | 2 +- drivers/pinctrl/sirf/pinctrl-atlas7.c | 2 +- drivers/pinctrl/sirf/pinctrl-sirf.c | 2 +- drivers/pinctrl/spear/pinctrl-plgpio.c | 2 +- drivers/platform/x86/intel_int0002_vgpio.c | 2 +- include/linux/gpio/driver.h | 11 ++++++++--- 56 files changed, 78 insertions(+), 71 deletions(-) (limited to 'include') diff --git a/Documentation/gpio/driver.txt b/Documentation/gpio/driver.txt index fc1d2f83564d..dcf6af1d9e56 100644 --- a/Documentation/gpio/driver.txt +++ b/Documentation/gpio/driver.txt @@ -254,7 +254,7 @@ GPIO irqchips usually fall in one of two categories: static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank) unsigned long wa_lock_flags; raw_spin_lock_irqsave(&bank->wa_lock, wa_lock_flags); - generic_handle_irq(irq_find_mapping(bank->chip.irqdomain, bit)); + generic_handle_irq(irq_find_mapping(bank->chip.irq.domain, bit)); raw_spin_unlock_irqrestore(&bank->wa_lock, wa_lock_flags); * GENERIC CHAINED GPIO irqchips: these are the same as "CHAINED GPIO irqchips", diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c index 982d5781d3ce..2c0ffb77d738 100644 --- a/drivers/bcma/driver_gpio.c +++ b/drivers/bcma/driver_gpio.c @@ -113,7 +113,7 @@ static irqreturn_t bcma_gpio_irq_handler(int irq, void *dev_id) return IRQ_NONE; for_each_set_bit(gpio, &irqs, gc->ngpio) - generic_handle_irq(irq_find_mapping(gc->irqdomain, gpio)); + generic_handle_irq(irq_find_mapping(gc->irq.domain, gpio)); bcma_chipco_gpio_polarity(cc, irqs, val & irqs); return IRQ_HANDLED; diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index 598e209efa2d..bab3b94c5cbc 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -326,7 +326,7 @@ static irqreturn_t dio48e_irq_handler(int irq, void *dev_id) unsigned long gpio; for_each_set_bit(gpio, &irq_mask, 2) - generic_handle_irq(irq_find_mapping(chip->irqdomain, + generic_handle_irq(irq_find_mapping(chip->irq.domain, 19 + gpio*24)); raw_spin_lock(&dio48egpio->lock); diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index 51f046e29ff7..add859d59766 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -209,7 +209,7 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id) for_each_set_bit(bit_num, &irq_mask, 8) { gpio = bit_num + boundary * 8; - generic_handle_irq(irq_find_mapping(chip->irqdomain, + generic_handle_irq(irq_find_mapping(chip->irq.domain, gpio)); } } diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index ec2ce34ff473..2f16638a0589 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -199,7 +199,7 @@ static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) int gpio; for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio) - generic_handle_irq(irq_find_mapping(chip->irqdomain, gpio)); + generic_handle_irq(irq_find_mapping(chip->irq.domain, gpio)); raw_spin_lock(&idio16gpio->lock); diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index 7f475eef3faa..44c09904daa6 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -320,7 +320,7 @@ static irqreturn_t adnp_irq(int irq, void *data) for_each_set_bit(bit, &pending, 8) { unsigned int child_irq; - child_irq = irq_find_mapping(adnp->gpio.irqdomain, + child_irq = irq_find_mapping(adnp->gpio.irq.domain, base + bit); handle_nested_irq(child_irq); } diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c index ccc02ed65b3c..8e76d390e653 100644 --- a/drivers/gpio/gpio-altera.c +++ b/drivers/gpio/gpio-altera.c @@ -211,7 +211,7 @@ static void altera_gpio_irq_edge_handler(struct irq_desc *desc) altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); chip = irq_desc_get_chip(desc); mm_gc = &altera_gc->mmchip; - irqdomain = altera_gc->mmchip.gc.irqdomain; + irqdomain = altera_gc->mmchip.gc.irq.domain; chained_irq_enter(chip, desc); @@ -239,7 +239,7 @@ static void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc) altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc)); chip = irq_desc_get_chip(desc); mm_gc = &altera_gc->mmchip; - irqdomain = altera_gc->mmchip.gc.irqdomain; + irqdomain = altera_gc->mmchip.gc.irq.domain; chained_irq_enter(chip, desc); diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 00dc1c020198..2bfce0ab7326 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -469,7 +469,7 @@ static void aspeed_gpio_irq_handler(struct irq_desc *desc) reg = ioread32(bank_irq_reg(data, bank, GPIO_IRQ_STATUS)); for_each_set_bit(p, ®, 32) { - girq = irq_find_mapping(gc->irqdomain, i * 32 + p); + girq = irq_find_mapping(gc->irq.domain, i * 32 + p); generic_handle_irq(girq); } diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index 02e56e0c793a..5fad89dfab7e 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -209,7 +209,7 @@ static void ath79_gpio_irq_handler(struct irq_desc *desc) if (pending) { for_each_set_bit(irq, &pending, gc->ngpio) generic_handle_irq( - irq_linear_revmap(gc->irqdomain, irq)); + irq_linear_revmap(gc->irq.domain, irq)); } chained_irq_exit(irqchip, desc); diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index e60156ec0c18..b6f0f729656c 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -295,7 +295,7 @@ static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data) for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) { if (pending & BIT(gpio)) { - virq = irq_find_mapping(cg->chip.irqdomain, gpio); + virq = irq_find_mapping(cg->chip.irq.domain, gpio); handle_nested_irq(virq); } } diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index aecb847166f5..1dada68b9a27 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -420,7 +420,7 @@ static void dln2_gpio_event(struct platform_device *pdev, u16 echo, return; } - irq = irq_find_mapping(dln2->gpio.irqdomain, pin); + irq = irq_find_mapping(dln2->gpio.irq.domain, pin); if (!irq) { dev_err(dln2->gpio.parent, "pin %d not mapped to IRQ\n", pin); return; diff --git a/drivers/gpio/gpio-ftgpio010.c b/drivers/gpio/gpio-ftgpio010.c index e9386f8b67f5..b7896bae83ca 100644 --- a/drivers/gpio/gpio-ftgpio010.c +++ b/drivers/gpio/gpio-ftgpio010.c @@ -149,7 +149,7 @@ static void ftgpio_gpio_irq_handler(struct irq_desc *desc) stat = readl(g->base + GPIO_INT_STAT); if (stat) for_each_set_bit(offset, &stat, gc->ngpio) - generic_handle_irq(irq_find_mapping(gc->irqdomain, + generic_handle_irq(irq_find_mapping(gc->irq.domain, offset)); chained_irq_exit(irqchip, desc); diff --git a/drivers/gpio/gpio-ingenic.c b/drivers/gpio/gpio-ingenic.c index 254780730b95..15fb2bc796a8 100644 --- a/drivers/gpio/gpio-ingenic.c +++ b/drivers/gpio/gpio-ingenic.c @@ -242,7 +242,7 @@ static void ingenic_gpio_irq_handler(struct irq_desc *desc) flag = gpio_ingenic_read_reg(jzgc, JZ4740_GPIO_FLAG); for_each_set_bit(i, &flag, 32) - generic_handle_irq(irq_linear_revmap(gc->irqdomain, i)); + generic_handle_irq(irq_linear_revmap(gc->irq.domain, i)); chained_irq_exit(irq_chip, desc); } diff --git a/drivers/gpio/gpio-intel-mid.c b/drivers/gpio/gpio-intel-mid.c index b76ecee82c3f..629575ea46a0 100644 --- a/drivers/gpio/gpio-intel-mid.c +++ b/drivers/gpio/gpio-intel-mid.c @@ -295,7 +295,7 @@ static void intel_mid_irq_handler(struct irq_desc *desc) mask = BIT(gpio); /* Clear before handling so we can't lose an edge */ writel(mask, gedr); - generic_handle_irq(irq_find_mapping(gc->irqdomain, + generic_handle_irq(irq_find_mapping(gc->irq.domain, base + gpio)); } } diff --git a/drivers/gpio/gpio-lynxpoint.c b/drivers/gpio/gpio-lynxpoint.c index fbd393b46ce0..1e557b10d73e 100644 --- a/drivers/gpio/gpio-lynxpoint.c +++ b/drivers/gpio/gpio-lynxpoint.c @@ -255,7 +255,7 @@ static void lp_gpio_irq_handler(struct irq_desc *desc) mask = BIT(pin); /* Clear before handling so we don't lose an edge */ outl(mask, reg); - irq = irq_find_mapping(lg->chip.irqdomain, base + pin); + irq = irq_find_mapping(lg->chip.irq.domain, base + pin); generic_handle_irq(irq); } } diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 7f4d26ce5f23..c04fae1ba32a 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -486,7 +486,7 @@ static irqreturn_t max732x_irq_handler(int irq, void *devid) do { level = __ffs(pending); - handle_nested_irq(irq_find_mapping(chip->gpio_chip.irqdomain, + handle_nested_irq(irq_find_mapping(chip->gpio_chip.irq.domain, level)); pending &= ~(1 << level); diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index ec8560298805..dd67a31ac337 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -357,7 +357,7 @@ static void mrfld_irq_handler(struct irq_desc *desc) for_each_set_bit(gpio, &pending, 32) { unsigned int irq; - irq = irq_find_mapping(gc->irqdomain, base + gpio); + irq = irq_find_mapping(gc->irq.domain, base + gpio); generic_handle_irq(irq); } } diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index dbf869fb63ce..ce27d6a586bf 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -733,7 +733,7 @@ static irqreturn_t omap_gpio_irq_handler(int irq, void *gpiobank) raw_spin_lock_irqsave(&bank->wa_lock, wa_lock_flags); - generic_handle_irq(irq_find_mapping(bank->chip.irqdomain, + generic_handle_irq(irq_find_mapping(bank->chip.irq.domain, bit)); raw_spin_unlock_irqrestore(&bank->wa_lock, diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 1b9dbf691ae7..babb7bd2ba59 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -608,7 +608,7 @@ static irqreturn_t pca953x_irq_handler(int irq, void *devid) for (i = 0; i < NBANK(chip); i++) { while (pending[i]) { level = __ffs(pending[i]); - handle_nested_irq(irq_find_mapping(chip->gpio_chip.irqdomain, + handle_nested_irq(irq_find_mapping(chip->gpio_chip.irq.domain, level + (BANK_SZ * i))); pending[i] &= ~(1 << level); nhandled++; diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index a4fd78b9c0e4..38fbb420c6cd 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -196,7 +196,7 @@ static irqreturn_t pcf857x_irq(int irq, void *data) mutex_unlock(&gpio->lock); for_each_set_bit(i, &change, gpio->chip.ngpio) - handle_nested_irq(irq_find_mapping(gpio->chip.irqdomain, i)); + handle_nested_irq(irq_find_mapping(gpio->chip.irq.domain, i)); return IRQ_HANDLED; } diff --git a/drivers/gpio/gpio-pci-idio-16.c b/drivers/gpio/gpio-pci-idio-16.c index 7de4f6a2cb49..57d1b7fbf07b 100644 --- a/drivers/gpio/gpio-pci-idio-16.c +++ b/drivers/gpio/gpio-pci-idio-16.c @@ -240,7 +240,7 @@ static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) return IRQ_NONE; for_each_set_bit(gpio, &idio16gpio->irq_mask, chip->ngpio) - generic_handle_irq(irq_find_mapping(chip->irqdomain, gpio)); + generic_handle_irq(irq_find_mapping(chip->irq.domain, gpio)); raw_spin_lock(&idio16gpio->lock); diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 6aaaab79c205..b70974cb9ef1 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -221,7 +221,7 @@ static void pl061_irq_handler(struct irq_desc *desc) pending = readb(pl061->base + GPIOMIS); if (pending) { for_each_set_bit(offset, &pending, PL061_GPIO_NR) - generic_handle_irq(irq_find_mapping(gc->irqdomain, + generic_handle_irq(irq_find_mapping(gc->irq.domain, offset)); } diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index ddcff4f543bc..0ea998a3e357 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -207,7 +207,7 @@ static irqreturn_t gpio_rcar_irq_handler(int irq, void *dev_id) gpio_rcar_read(p, INTMSK))) { offset = __ffs(pending); gpio_rcar_write(p, INTCLR, BIT(offset)); - generic_handle_irq(irq_find_mapping(p->gpio_chip.irqdomain, + generic_handle_irq(irq_find_mapping(p->gpio_chip.irq.domain, offset)); irqs_handled++; } diff --git a/drivers/gpio/gpio-reg.c b/drivers/gpio/gpio-reg.c index e85903eddc68..23e771dba4c1 100644 --- a/drivers/gpio/gpio-reg.c +++ b/drivers/gpio/gpio-reg.c @@ -103,8 +103,8 @@ static int gpio_reg_to_irq(struct gpio_chip *gc, unsigned offset) struct gpio_reg *r = to_gpio_reg(gc); int irq = r->irqs[offset]; - if (irq >= 0 && r->irqdomain) - irq = irq_find_mapping(r->irqdomain, irq); + if (irq >= 0 && r->irq.domain) + irq = irq_find_mapping(r->irq.domain, irq); return irq; } diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 001a89db5161..18d8bef76d85 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -397,7 +397,7 @@ static irqreturn_t stmpe_gpio_irq(int irq, void *dev) while (stat) { int bit = __ffs(stat); int line = bank * 8 + bit; - int child_irq = irq_find_mapping(stmpe_gpio->chip.irqdomain, + int child_irq = irq_find_mapping(stmpe_gpio->chip.irq.domain, line); handle_nested_irq(child_irq); diff --git a/drivers/gpio/gpio-tc3589x.c b/drivers/gpio/gpio-tc3589x.c index 433b45ef332e..91a8ef8e7f3f 100644 --- a/drivers/gpio/gpio-tc3589x.c +++ b/drivers/gpio/gpio-tc3589x.c @@ -268,7 +268,7 @@ static irqreturn_t tc3589x_gpio_irq(int irq, void *dev) while (stat) { int bit = __ffs(stat); int line = i * 8 + bit; - int irq = irq_find_mapping(tc3589x_gpio->chip.irqdomain, + int irq = irq_find_mapping(tc3589x_gpio->chip.irq.domain, line); handle_nested_irq(irq); diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index cbe9e06861de..4610cc2938ad 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -160,7 +160,7 @@ static void vf610_gpio_irq_handler(struct irq_desc *desc) for_each_set_bit(pin, &irq_isfr, VF610_GPIO_PER_PORT) { vf610_gpio_writel(BIT(pin), port->base + PORT_ISFR); - generic_handle_irq(irq_find_mapping(port->gc.irqdomain, pin)); + generic_handle_irq(irq_find_mapping(port->gc.irq.domain, pin)); } chained_irq_exit(chip, desc); diff --git a/drivers/gpio/gpio-wcove.c b/drivers/gpio/gpio-wcove.c index 85341eab795d..dde7c6aecbb5 100644 --- a/drivers/gpio/gpio-wcove.c +++ b/drivers/gpio/gpio-wcove.c @@ -350,7 +350,7 @@ static irqreturn_t wcove_gpio_irq_handler(int irq, void *data) offset = (gpio > GROUP0_NR_IRQS) ? 1 : 0; mask = (offset == 1) ? BIT(gpio - GROUP0_NR_IRQS) : BIT(gpio); - virq = irq_find_mapping(wg->chip.irqdomain, gpio); + virq = irq_find_mapping(wg->chip.irq.domain, gpio); handle_nested_irq(virq); regmap_update_bits(wg->regmap, IRQ_STATUS_BASE + offset, mask, mask); diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index 5037974ac063..746648244bf3 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -332,7 +332,7 @@ static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id) int_id = inb(ws16c48gpio->base + 8 + port); for_each_set_bit(gpio, &int_id, 8) generic_handle_irq(irq_find_mapping( - chip->irqdomain, gpio + 8*port)); + chip->irq.domain, gpio + 8*port)); } int_pending = inb(ws16c48gpio->base + 6) & 0x7; diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c index 82c3ee6da66a..4f2623c2393e 100644 --- a/drivers/gpio/gpio-xgene-sb.c +++ b/drivers/gpio/gpio-xgene-sb.c @@ -287,7 +287,7 @@ static int xgene_gpio_sb_probe(struct platform_device *pdev) if (!priv->irq_domain) return -ENODEV; - priv->gc.irqdomain = priv->irq_domain; + priv->gc.irq.domain = priv->irq_domain; ret = devm_gpiochip_add_data(&pdev->dev, &priv->gc, priv); if (ret) { diff --git a/drivers/gpio/gpio-xlp.c b/drivers/gpio/gpio-xlp.c index d857e1d8e731..e74bd43a6974 100644 --- a/drivers/gpio/gpio-xlp.c +++ b/drivers/gpio/gpio-xlp.c @@ -225,7 +225,7 @@ static void xlp_gpio_generic_handler(struct irq_desc *desc) if (gpio_stat & BIT(gpio % XLP_GPIO_REGSZ)) generic_handle_irq(irq_find_mapping( - priv->chip.irqdomain, gpio)); + priv->chip.irq.domain, gpio)); } chained_irq_exit(irqchip, desc); } diff --git a/drivers/gpio/gpio-zx.c b/drivers/gpio/gpio-zx.c index be3a87da8438..5eacad9b2692 100644 --- a/drivers/gpio/gpio-zx.c +++ b/drivers/gpio/gpio-zx.c @@ -170,7 +170,7 @@ static void zx_irq_handler(struct irq_desc *desc) writew_relaxed(pending, chip->base + ZX_GPIO_IC); if (pending) { for_each_set_bit(offset, &pending, ZX_GPIO_NR) - generic_handle_irq(irq_find_mapping(gc->irqdomain, + generic_handle_irq(irq_find_mapping(gc->irq.domain, offset)); } diff --git a/drivers/gpio/gpio-zynq.c b/drivers/gpio/gpio-zynq.c index b3cc948a2d8b..75ee877e5cd5 100644 --- a/drivers/gpio/gpio-zynq.c +++ b/drivers/gpio/gpio-zynq.c @@ -562,7 +562,7 @@ static void zynq_gpio_handle_bank_irq(struct zynq_gpio *gpio, unsigned long pending) { unsigned int bank_offset = gpio->p_data->bank_min[bank_num]; - struct irq_domain *irqdomain = gpio->chip.irqdomain; + struct irq_domain *irqdomain = gpio->chip.irq.domain; int offset; if (!pending) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d3d0b3134ba3..9ee75a45ba37 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1550,7 +1550,7 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip, { unsigned int offset; - if (!gpiochip->irqdomain) { + if (!gpiochip->irq.domain) { chip_err(gpiochip, "called %s before setting up irqchip\n", __func__); return; @@ -1577,7 +1577,7 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip, for (offset = 0; offset < gpiochip->ngpio; offset++) { if (!gpiochip_irqchip_irq_valid(gpiochip, offset)) continue; - irq_set_parent(irq_find_mapping(gpiochip->irqdomain, offset), + irq_set_parent(irq_find_mapping(gpiochip->irq.domain, offset), parent_irq); } } @@ -1708,7 +1708,7 @@ static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) { if (!gpiochip_irqchip_irq_valid(chip, offset)) return -ENXIO; - return irq_create_mapping(chip->irqdomain, offset); + return irq_create_mapping(chip->irq.domain, offset); } /** @@ -1719,7 +1719,7 @@ static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) */ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) { - unsigned int offset; + unsigned int offset, irq; acpi_gpiochip_free_interrupts(gpiochip); @@ -1729,14 +1729,16 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) } /* Remove all IRQ mappings and delete the domain */ - if (gpiochip->irqdomain) { + if (gpiochip->irq.domain) { for (offset = 0; offset < gpiochip->ngpio; offset++) { if (!gpiochip_irqchip_irq_valid(gpiochip, offset)) continue; - irq_dispose_mapping( - irq_find_mapping(gpiochip->irqdomain, offset)); + + irq = irq_find_mapping(gpiochip->irq.domain, offset); + irq_dispose_mapping(irq); } - irq_domain_remove(gpiochip->irqdomain); + + irq_domain_remove(gpiochip->irq.domain); } if (gpiochip->irq.chip) { @@ -1822,10 +1824,10 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, gpiochip->irq_default_type = type; gpiochip->to_irq = gpiochip_to_irq; gpiochip->lock_key = lock_key; - gpiochip->irqdomain = irq_domain_add_simple(of_node, + gpiochip->irq.domain = irq_domain_add_simple(of_node, gpiochip->ngpio, first_irq, &gpiochip_domain_ops, gpiochip); - if (!gpiochip->irqdomain) { + if (!gpiochip->irq.domain) { gpiochip->irq.chip = NULL; return -EINVAL; } diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c index 0944310225db..fb85ab3cf662 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c @@ -383,7 +383,7 @@ static void bcm2835_gpio_irq_handle_bank(struct bcm2835_pinctrl *pc, /* FIXME: no clue why the code looks up the type here */ type = pc->irq_type[gpio]; - generic_handle_irq(irq_linear_revmap(pc->gpio_chip.irqdomain, + generic_handle_irq(irq_linear_revmap(pc->gpio_chip.irq.irqdomain, gpio)); } } @@ -665,7 +665,7 @@ static void bcm2835_pctl_pin_dbg_show(struct pinctrl_dev *pctldev, enum bcm2835_fsel fsel = bcm2835_pinctrl_fsel_get(pc, offset); const char *fname = bcm2835_functions[fsel]; int value = bcm2835_gpio_get_bit(pc, GPLEV0, offset); - int irq = irq_find_mapping(chip->irqdomain, offset); + int irq = irq_find_mapping(chip->irq.domain, offset); seq_printf(s, "function %s in %s; irq %d (%s)", fname, value ? "hi" : "lo", diff --git a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c index 85a8c97d9dfe..b93f62dc8733 100644 --- a/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-iproc-gpio.c @@ -172,7 +172,7 @@ static void iproc_gpio_irq_handler(struct irq_desc *desc) for_each_set_bit(bit, &val, NGPIOS_PER_BANK) { unsigned pin = NGPIOS_PER_BANK * i + bit; - int child_irq = irq_find_mapping(gc->irqdomain, pin); + int child_irq = irq_find_mapping(gc->irq.domain, pin); /* * Clear the interrupt before invoking the diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index 0f3a02495aeb..5897981e5ed3 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -1627,7 +1627,7 @@ static void byt_gpio_irq_handler(struct irq_desc *desc) pending = readl(reg); raw_spin_unlock(&vg->lock); for_each_set_bit(pin, &pending, 32) { - virq = irq_find_mapping(vg->chip.irqdomain, base + pin); + virq = irq_find_mapping(vg->chip.irq.domain, base + pin); generic_handle_irq(virq); } } diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 04e929fd0ffe..1cd7043edbc1 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1523,7 +1523,7 @@ static void chv_gpio_irq_handler(struct irq_desc *desc) unsigned irq, offset; offset = pctrl->intr_lines[intr_line]; - irq = irq_find_mapping(gc->irqdomain, offset); + irq = irq_find_mapping(gc->irq.domain, offset); generic_handle_irq(irq); } diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index 71df0f70b61f..ffda27bfd133 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -1005,7 +1005,7 @@ static irqreturn_t intel_gpio_community_irq_handler(struct intel_pinctrl *pctrl, if (padno >= community->npins) break; - irq = irq_find_mapping(gc->irqdomain, + irq = irq_find_mapping(gc->irq.domain, community->pin_base + padno); generic_handle_irq(irq); diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index e800d55c340b..d754a9b10e19 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -592,7 +592,7 @@ static void armada_37xx_irq_handler(struct irq_desc *desc) struct gpio_chip *gc = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); struct armada_37xx_pinctrl *info = gpiochip_get_data(gc); - struct irq_domain *d = gc->irqdomain; + struct irq_domain *d = gc->irq.domain; int i; chained_irq_enter(chip, desc); diff --git a/drivers/pinctrl/nomadik/pinctrl-nomadik.c b/drivers/pinctrl/nomadik/pinctrl-nomadik.c index a53f1a9b1ed2..f0e7a8c114b2 100644 --- a/drivers/pinctrl/nomadik/pinctrl-nomadik.c +++ b/drivers/pinctrl/nomadik/pinctrl-nomadik.c @@ -413,7 +413,7 @@ nmk_gpio_disable_lazy_irq(struct nmk_gpio_chip *nmk_chip, unsigned offset) u32 falling = nmk_chip->fimsc & BIT(offset); u32 rising = nmk_chip->rimsc & BIT(offset); int gpio = nmk_chip->chip.base + offset; - int irq = irq_find_mapping(nmk_chip->chip.irqdomain, offset); + int irq = irq_find_mapping(nmk_chip->chip.irq.domain, offset); struct irq_data *d = irq_get_irq_data(irq); if (!rising && !falling) @@ -815,7 +815,7 @@ static void __nmk_gpio_irq_handler(struct irq_desc *desc, u32 status) while (status) { int bit = __ffs(status); - generic_handle_irq(irq_find_mapping(chip->irqdomain, bit)); + generic_handle_irq(irq_find_mapping(chip->irq.domain, bit)); status &= ~BIT(bit); } diff --git a/drivers/pinctrl/pinctrl-amd.c b/drivers/pinctrl/pinctrl-amd.c index 3f6b34febbf1..06362bf84a5b 100644 --- a/drivers/pinctrl/pinctrl-amd.c +++ b/drivers/pinctrl/pinctrl-amd.c @@ -532,7 +532,7 @@ static irqreturn_t amd_gpio_irq_handler(int irq, void *dev_id) regval = readl(regs + i); if (!(regval & PIN_IRQ_PENDING)) continue; - irq = irq_find_mapping(gc->irqdomain, irqnr + i); + irq = irq_find_mapping(gc->irq.domain, irqnr + i); generic_handle_irq(irq); /* Clear interrupt */ writel(regval, regs + i); diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index 569bc28cb909..03492e3c09fa 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -1603,7 +1603,7 @@ static void gpio_irq_handler(struct irq_desc *desc) for_each_set_bit(n, &isr, BITS_PER_LONG) { generic_handle_irq(irq_find_mapping( - gpio_chip->irqdomain, n)); + gpio_chip->irq.domain, n)); } } chained_irq_exit(chip, desc); diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c index ac155e7d3412..7939b178c6ae 100644 --- a/drivers/pinctrl/pinctrl-coh901.c +++ b/drivers/pinctrl/pinctrl-coh901.c @@ -517,7 +517,7 @@ static void u300_gpio_irq_handler(struct irq_desc *desc) for_each_set_bit(irqoffset, &val, U300_GPIO_PINS_PER_PORT) { int offset = pinoffset + irqoffset; - int pin_irq = irq_find_mapping(chip->irqdomain, offset); + int pin_irq = irq_find_mapping(chip->irq.domain, offset); dev_dbg(gpio->dev, "GPIO IRQ %d on pin %d\n", pin_irq, offset); diff --git a/drivers/pinctrl/pinctrl-mcp23s08.c b/drivers/pinctrl/pinctrl-mcp23s08.c index 3e40d4245512..db19a2f2f575 100644 --- a/drivers/pinctrl/pinctrl-mcp23s08.c +++ b/drivers/pinctrl/pinctrl-mcp23s08.c @@ -537,7 +537,7 @@ static irqreturn_t mcp23s08_irq(int irq, void *data) ((gpio_bit_changed || intcap_changed) && (BIT(i) & mcp->irq_fall) && !gpio_set) || defval_changed) { - child_irq = irq_find_mapping(mcp->chip.irqdomain, i); + child_irq = irq_find_mapping(mcp->chip.irq.domain, i); handle_nested_irq(child_irq); } } diff --git a/drivers/pinctrl/pinctrl-oxnas.c b/drivers/pinctrl/pinctrl-oxnas.c index 494ec9a7573a..53ec22a51f5c 100644 --- a/drivers/pinctrl/pinctrl-oxnas.c +++ b/drivers/pinctrl/pinctrl-oxnas.c @@ -1064,7 +1064,7 @@ static void oxnas_gpio_irq_handler(struct irq_desc *desc) stat = readl(bank->reg_base + IRQ_PENDING); for_each_set_bit(pin, &stat, BITS_PER_LONG) - generic_handle_irq(irq_linear_revmap(gc->irqdomain, pin)); + generic_handle_irq(irq_linear_revmap(gc->irq.domain, pin)); chained_irq_exit(chip, desc); } diff --git a/drivers/pinctrl/pinctrl-pic32.c b/drivers/pinctrl/pinctrl-pic32.c index 31ceb958b3fe..96390228d388 100644 --- a/drivers/pinctrl/pinctrl-pic32.c +++ b/drivers/pinctrl/pinctrl-pic32.c @@ -2106,7 +2106,7 @@ static void pic32_gpio_irq_handler(struct irq_desc *desc) pending = pic32_gpio_get_pending(gc, stat); for_each_set_bit(pin, &pending, BITS_PER_LONG) - generic_handle_irq(irq_linear_revmap(gc->irqdomain, pin)); + generic_handle_irq(irq_linear_revmap(gc->irq.domain, pin)); chained_irq_exit(chip, desc); } diff --git a/drivers/pinctrl/pinctrl-pistachio.c b/drivers/pinctrl/pinctrl-pistachio.c index 55375b1b3cc8..302190d1558d 100644 --- a/drivers/pinctrl/pinctrl-pistachio.c +++ b/drivers/pinctrl/pinctrl-pistachio.c @@ -1307,7 +1307,7 @@ static void pistachio_gpio_irq_handler(struct irq_desc *desc) pending = gpio_readl(bank, GPIO_INTERRUPT_STATUS) & gpio_readl(bank, GPIO_INTERRUPT_EN); for_each_set_bit(pin, &pending, 16) - generic_handle_irq(irq_linear_revmap(gc->irqdomain, pin)); + generic_handle_irq(irq_linear_revmap(gc->irq.domain, pin)); chained_irq_exit(chip, desc); } diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index a5205b94b2e6..2081c67667a8 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -1408,7 +1408,7 @@ static void __gpio_irq_handler(struct st_gpio_bank *bank) continue; } - generic_handle_irq(irq_find_mapping(bank->gpio_chip.irqdomain, n)); + generic_handle_irq(irq_find_mapping(bank->gpio_chip.irq.domain, n)); } } } diff --git a/drivers/pinctrl/pinctrl-sx150x.c b/drivers/pinctrl/pinctrl-sx150x.c index 7450f5118445..7db4f6a6eb17 100644 --- a/drivers/pinctrl/pinctrl-sx150x.c +++ b/drivers/pinctrl/pinctrl-sx150x.c @@ -561,7 +561,7 @@ static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id) status = val; for_each_set_bit(n, &status, pctl->data->ngpios) - handle_nested_irq(irq_find_mapping(pctl->gpio.irqdomain, n)); + handle_nested_irq(irq_find_mapping(pctl->gpio.irq.domain, n)); return IRQ_HANDLED; } diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index ff491da64dab..7a960590ecaa 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -795,7 +795,7 @@ static void msm_gpio_irq_handler(struct irq_desc *desc) g = &pctrl->soc->groups[i]; val = readl(pctrl->regs + g->intr_status_reg); if (val & BIT(g->intr_status_bit)) { - irq_pin = irq_find_mapping(gc->irqdomain, i); + irq_pin = irq_find_mapping(gc->irq.domain, i); generic_handle_irq(irq_pin); handled++; } diff --git a/drivers/pinctrl/sirf/pinctrl-atlas7.c b/drivers/pinctrl/sirf/pinctrl-atlas7.c index 4db9323251e3..f5cef6e5fa3e 100644 --- a/drivers/pinctrl/sirf/pinctrl-atlas7.c +++ b/drivers/pinctrl/sirf/pinctrl-atlas7.c @@ -5820,7 +5820,7 @@ static void atlas7_gpio_handle_irq(struct irq_desc *desc) __func__, gc->label, bank->gpio_offset + pin_in_bank); generic_handle_irq( - irq_find_mapping(gc->irqdomain, + irq_find_mapping(gc->irq.domain, bank->gpio_offset + pin_in_bank)); } diff --git a/drivers/pinctrl/sirf/pinctrl-sirf.c b/drivers/pinctrl/sirf/pinctrl-sirf.c index d3ef05973901..8b14a1f1e671 100644 --- a/drivers/pinctrl/sirf/pinctrl-sirf.c +++ b/drivers/pinctrl/sirf/pinctrl-sirf.c @@ -587,7 +587,7 @@ static void sirfsoc_gpio_handle_irq(struct irq_desc *desc) if ((status & 0x1) && (ctrl & SIRFSOC_GPIO_CTL_INTR_EN_MASK)) { pr_debug("%s: gpio id %d idx %d happens\n", __func__, bank->id, idx); - generic_handle_irq(irq_find_mapping(gc->irqdomain, idx + + generic_handle_irq(irq_find_mapping(gc->irq.domain, idx + bank->id * SIRFSOC_GPIO_BANK_SIZE)); } diff --git a/drivers/pinctrl/spear/pinctrl-plgpio.c b/drivers/pinctrl/spear/pinctrl-plgpio.c index cf6d68c7345b..72ae6bccee55 100644 --- a/drivers/pinctrl/spear/pinctrl-plgpio.c +++ b/drivers/pinctrl/spear/pinctrl-plgpio.c @@ -401,7 +401,7 @@ static void plgpio_irq_handler(struct irq_desc *desc) /* get correct irq line number */ pin = i * MAX_GPIO_PER_REG + pin; generic_handle_irq( - irq_find_mapping(gc->irqdomain, pin)); + irq_find_mapping(gc->irq.domain, pin)); } } chained_irq_exit(irqchip, desc); diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index 92dc230ef5b2..f6b3af73dea5 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -119,7 +119,7 @@ static irqreturn_t int0002_irq(int irq, void *data) if (!(gpe_sts_reg & GPE0A_PME_B0_STS_BIT)) return IRQ_NONE; - generic_handle_irq(irq_find_mapping(chip->irqdomain, + generic_handle_irq(irq_find_mapping(chip->irq.domain, GPE0A_PME_B0_VIRT_GPIO_PIN)); pm_system_wakeup(); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index a79b3b18fadd..c5dfa8c0b829 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -31,6 +31,14 @@ struct gpio_irq_chip { */ struct irq_chip *chip; + /** + * @domain: + * + * Interrupt translation domain; responsible for mapping between GPIO + * hwirq number and Linux IRQ number. + */ + struct irq_domain *domain; + /** * @domain_ops: * @@ -124,8 +132,6 @@ static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) * safely. * @bgpio_dir: shadowed direction register for generic GPIO to clear/set * direction safely. - * @irqdomain: Interrupt translation domain; responsible for mapping - * between GPIO hwirq number and linux irq number * @irq_handler: the irq handler to use (often a predefined irq core function) * for GPIO IRQs, provided by GPIO driver * @irq_default_type: default IRQ triggering type applied during GPIO driver @@ -208,7 +214,6 @@ struct gpio_chip { * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib * to handle IRQs for most practical cases. */ - struct irq_domain *irqdomain; irq_flow_handler_t irq_handler; unsigned int irq_default_type; unsigned int irq_chained_parent; -- cgit v1.2.3-59-g8ed1b From c7a0aa59524c5bb20bebaca360f7c5faaec6b806 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:48 +0100 Subject: gpio: Move irq_handler to struct gpio_irq_chip In order to consolidate the multiple ways to associate an IRQ chip with a GPIO chip, move more fields into the new struct gpio_irq_chip. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 4 ++-- include/linux/gpio/driver.h | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9ee75a45ba37..dafbca12c4ca 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1646,7 +1646,7 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, * category than their parents, so it won't report false recursion. */ irq_set_lockdep_class(irq, chip->lock_key); - irq_set_chip_and_handler(irq, chip->irq.chip, chip->irq_handler); + irq_set_chip_and_handler(irq, chip->irq.chip, chip->irq.handler); /* Chips that use nested thread handlers have them marked */ if (chip->irq_nested) irq_set_nested_thread(irq, 1); @@ -1820,7 +1820,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, } gpiochip->irq.chip = irqchip; - gpiochip->irq_handler = handler; + gpiochip->irq.handler = handler; gpiochip->irq_default_type = type; gpiochip->to_irq = gpiochip_to_irq; gpiochip->lock_key = lock_key; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index c5dfa8c0b829..864f507e859b 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -46,6 +46,14 @@ struct gpio_irq_chip { */ const struct irq_domain_ops *domain_ops; + /** + * @handler: + * + * The IRQ handler to use (often a predefined IRQ core function) for + * GPIO IRQs, provided by GPIO driver. + */ + irq_flow_handler_t handler; + /** * @parent_handler: * @@ -132,8 +140,6 @@ static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) * safely. * @bgpio_dir: shadowed direction register for generic GPIO to clear/set * direction safely. - * @irq_handler: the irq handler to use (often a predefined irq core function) - * for GPIO IRQs, provided by GPIO driver * @irq_default_type: default IRQ triggering type applied during GPIO driver * initialization, provided by GPIO driver * @irq_chained_parent: GPIO IRQ chip parent/bank linux irq number, @@ -214,7 +220,6 @@ struct gpio_chip { * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib * to handle IRQs for most practical cases. */ - irq_flow_handler_t irq_handler; unsigned int irq_default_type; unsigned int irq_chained_parent; bool irq_nested; -- cgit v1.2.3-59-g8ed1b From 3634eeb0fe9176e453c99834749dce21ea1305c1 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:49 +0100 Subject: gpio: Move irq_default_type to struct gpio_irq_chip In order to consolidate the multiple ways to associate an IRQ chip with a GPIO chip, move more fields into the new struct gpio_irq_chip. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 6 +++--- include/linux/gpio/driver.h | 11 ++++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index dafbca12c4ca..d3608cf511de 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1656,8 +1656,8 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, * No set-up of the hardware will happen if IRQ_TYPE_NONE * is passed as default type. */ - if (chip->irq_default_type != IRQ_TYPE_NONE) - irq_set_irq_type(irq, chip->irq_default_type); + if (chip->irq.default_type != IRQ_TYPE_NONE) + irq_set_irq_type(irq, chip->irq.default_type); return 0; } @@ -1821,7 +1821,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, gpiochip->irq.chip = irqchip; gpiochip->irq.handler = handler; - gpiochip->irq_default_type = type; + gpiochip->irq.default_type = type; gpiochip->to_irq = gpiochip_to_irq; gpiochip->lock_key = lock_key; gpiochip->irq.domain = irq_domain_add_simple(of_node, diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 864f507e859b..1367fa94105f 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -54,6 +54,14 @@ struct gpio_irq_chip { */ irq_flow_handler_t handler; + /** + * @default_type: + * + * Default IRQ triggering type applied during GPIO driver + * initialization, provided by GPIO driver. + */ + unsigned int default_type; + /** * @parent_handler: * @@ -140,8 +148,6 @@ static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) * safely. * @bgpio_dir: shadowed direction register for generic GPIO to clear/set * direction safely. - * @irq_default_type: default IRQ triggering type applied during GPIO driver - * initialization, provided by GPIO driver * @irq_chained_parent: GPIO IRQ chip parent/bank linux irq number, * provided by GPIO driver for chained interrupt (not for nested * interrupts). @@ -220,7 +226,6 @@ struct gpio_chip { * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib * to handle IRQs for most practical cases. */ - unsigned int irq_default_type; unsigned int irq_chained_parent; bool irq_nested; bool irq_need_valid_mask; -- cgit v1.2.3-59-g8ed1b From 39e5f0969514fbfd6c235ac52586c72ca77cee00 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:50 +0100 Subject: gpio: Move irq_chained_parent to struct gpio_irq_chip In order to consolidate the multiple ways to associate an IRQ chip with a GPIO chip, move more fields into the new struct gpio_irq_chip. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 17 ++++++++++++----- include/linux/gpio/driver.h | 19 +++++++++++++++---- 2 files changed, 27 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d3608cf511de..8eadae73ff20 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1570,7 +1570,8 @@ static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gpiochip, irq_set_chained_handler_and_data(parent_irq, parent_handler, gpiochip); - gpiochip->irq_chained_parent = parent_irq; + gpiochip->irq.parents = &parent_irq; + gpiochip->irq.num_parents = 1; } /* Set the parent IRQ for all affected IRQs */ @@ -1719,17 +1720,23 @@ static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) */ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) { - unsigned int offset, irq; + unsigned int offset; acpi_gpiochip_free_interrupts(gpiochip); - if (gpiochip->irq_chained_parent) { - irq_set_chained_handler_and_data( - gpiochip->irq_chained_parent, NULL, NULL); + if (gpiochip->irq.num_parents > 0) { + struct gpio_irq_chip *irq = &gpiochip->irq; + unsigned int i; + + for (i = 0; i < irq->num_parents; i++) + irq_set_chained_handler_and_data(irq->parents[i], + NULL, NULL); } /* Remove all IRQ mappings and delete the domain */ if (gpiochip->irq.domain) { + unsigned int irq; + for (offset = 0; offset < gpiochip->ngpio; offset++) { if (!gpiochip_irqchip_irq_valid(gpiochip, offset)) continue; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 1367fa94105f..86f00d908e90 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -77,6 +77,21 @@ struct gpio_irq_chip { * interrupt. */ void *parent_handler_data; + + /** + * @num_parents: + * + * The number of interrupt parents of a GPIO chip. + */ + unsigned int num_parents; + + /** + * @parents: + * + * A list of interrupt parents of a GPIO chip. This is owned by the + * driver, so the core will only reference this list, not modify it. + */ + unsigned int *parents; }; static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) @@ -148,9 +163,6 @@ static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) * safely. * @bgpio_dir: shadowed direction register for generic GPIO to clear/set * direction safely. - * @irq_chained_parent: GPIO IRQ chip parent/bank linux irq number, - * provided by GPIO driver for chained interrupt (not for nested - * interrupts). * @irq_nested: True if set the interrupt handling is nested. * @irq_need_valid_mask: If set core allocates @irq_valid_mask with all * bits set to one @@ -226,7 +238,6 @@ struct gpio_chip { * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib * to handle IRQs for most practical cases. */ - unsigned int irq_chained_parent; bool irq_nested; bool irq_need_valid_mask; unsigned long *irq_valid_mask; -- cgit v1.2.3-59-g8ed1b From dc6bafee86897419b0908e8d1e52ef46ca0ea487 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:51 +0100 Subject: gpio: Move irq_nested into struct gpio_irq_chip In order to consolidate the multiple ways to associate an IRQ chip with a GPIO chip, move more fields into the new struct gpio_irq_chip. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 8 ++++---- include/linux/gpio/driver.h | 9 +++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 8eadae73ff20..236a9f55a265 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1614,7 +1614,7 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip, struct irq_chip *irqchip, unsigned int parent_irq) { - if (!gpiochip->irq_nested) { + if (!gpiochip->irq.nested) { chip_err(gpiochip, "tried to nest a chained gpiochip\n"); return; } @@ -1649,7 +1649,7 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_set_lockdep_class(irq, chip->lock_key); irq_set_chip_and_handler(irq, chip->irq.chip, chip->irq.handler); /* Chips that use nested thread handlers have them marked */ - if (chip->irq_nested) + if (chip->irq.nested) irq_set_nested_thread(irq, 1); irq_set_noprobe(irq); @@ -1667,7 +1667,7 @@ static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) { struct gpio_chip *chip = d->host_data; - if (chip->irq_nested) + if (chip->irq.nested) irq_set_nested_thread(irq, 0); irq_set_chip_and_handler(irq, NULL, NULL); irq_set_chip_data(irq, NULL); @@ -1801,7 +1801,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, pr_err("missing gpiochip .dev parent pointer\n"); return -EINVAL; } - gpiochip->irq_nested = nested; + gpiochip->irq.nested = nested; of_node = gpiochip->parent->of_node; #ifdef CONFIG_OF_GPIO /* diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 86f00d908e90..1c3d06fe54b1 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -92,6 +92,13 @@ struct gpio_irq_chip { * driver, so the core will only reference this list, not modify it. */ unsigned int *parents; + + /** + * @nested: + * + * True if set the interrupt handling is nested. + */ + bool nested; }; static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) @@ -163,7 +170,6 @@ static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) * safely. * @bgpio_dir: shadowed direction register for generic GPIO to clear/set * direction safely. - * @irq_nested: True if set the interrupt handling is nested. * @irq_need_valid_mask: If set core allocates @irq_valid_mask with all * bits set to one * @irq_valid_mask: If not %NULL holds bitmask of GPIOs which are valid to @@ -238,7 +244,6 @@ struct gpio_chip { * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib * to handle IRQs for most practical cases. */ - bool irq_nested; bool irq_need_valid_mask; unsigned long *irq_valid_mask; struct lock_class_key *lock_key; -- cgit v1.2.3-59-g8ed1b From dc7b0387ee894c115ef5ddcaaf794125d6d9058c Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:52 +0100 Subject: gpio: Move irq_valid_mask into struct gpio_irq_chip In order to consolidate the multiple ways to associate an IRQ chip with a GPIO chip, move more fields into the new struct gpio_irq_chip. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- Documentation/gpio/driver.txt | 4 ++-- drivers/gpio/gpio-aspeed.c | 4 ++-- drivers/gpio/gpio-stmpe.c | 4 ++-- drivers/gpio/gpiolib.c | 16 ++++++++-------- drivers/pinctrl/intel/pinctrl-baytrail.c | 4 ++-- drivers/pinctrl/intel/pinctrl-cherryview.c | 4 ++-- drivers/platform/x86/intel_int0002_vgpio.c | 4 ++-- include/linux/gpio/driver.h | 21 +++++++++++++++------ 8 files changed, 35 insertions(+), 26 deletions(-) (limited to 'include') diff --git a/Documentation/gpio/driver.txt b/Documentation/gpio/driver.txt index dcf6af1d9e56..d8de1c7de85a 100644 --- a/Documentation/gpio/driver.txt +++ b/Documentation/gpio/driver.txt @@ -313,8 +313,8 @@ symbol: mark all the child IRQs as having the other IRQ as parent. If there is a need to exclude certain GPIOs from the IRQ domain, you can -set .irq_need_valid_mask of the gpiochip before gpiochip_add_data() is -called. This allocates an .irq_valid_mask with as many bits set as there +set .irq.need_valid_mask of the gpiochip before gpiochip_add_data() is +called. This allocates an .irq.valid_mask with as many bits set as there are GPIOs in the chip. Drivers can exclude GPIOs by clearing bits from this mask. The mask must be filled in before gpiochip_irqchip_add() or gpiochip_irqchip_add_nested() is called. diff --git a/drivers/gpio/gpio-aspeed.c b/drivers/gpio/gpio-aspeed.c index 2bfce0ab7326..8781817d9003 100644 --- a/drivers/gpio/gpio-aspeed.c +++ b/drivers/gpio/gpio-aspeed.c @@ -501,7 +501,7 @@ static void set_irq_valid_mask(struct aspeed_gpio *gpio) if (i >= gpio->config->nr_gpios) break; - clear_bit(i, gpio->chip.irq_valid_mask); + clear_bit(i, gpio->chip.irq.valid_mask); } props++; @@ -856,7 +856,7 @@ static int __init aspeed_gpio_probe(struct platform_device *pdev) gpio->chip.set_config = aspeed_gpio_set_config; gpio->chip.label = dev_name(&pdev->dev); gpio->chip.base = -1; - gpio->chip.irq_need_valid_mask = true; + gpio->chip.irq.need_valid_mask = true; rc = devm_gpiochip_add_data(&pdev->dev, &gpio->chip, gpio); if (rc < 0) diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 18d8bef76d85..e6e5cca624a7 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -451,7 +451,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev) of_property_read_u32(np, "st,norequest-mask", &stmpe_gpio->norequest_mask); if (stmpe_gpio->norequest_mask) - stmpe_gpio->chip.irq_need_valid_mask = true; + stmpe_gpio->chip.irq.need_valid_mask = true; if (irq < 0) dev_info(&pdev->dev, @@ -482,7 +482,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev) /* Forbid unused lines to be mapped as IRQs */ for (i = 0; i < sizeof(u32); i++) if (stmpe_gpio->norequest_mask & BIT(i)) - clear_bit(i, stmpe_gpio->chip.irq_valid_mask); + clear_bit(i, stmpe_gpio->chip.irq.valid_mask); } ret = gpiochip_irqchip_add_nested(&stmpe_gpio->chip, &stmpe_gpio_irq_chip, diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 236a9f55a265..0bf844470693 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1504,33 +1504,33 @@ static struct gpio_chip *find_chip_by_name(const char *name) static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip) { - if (!gpiochip->irq_need_valid_mask) + if (!gpiochip->irq.need_valid_mask) return 0; - gpiochip->irq_valid_mask = kcalloc(BITS_TO_LONGS(gpiochip->ngpio), + gpiochip->irq.valid_mask = kcalloc(BITS_TO_LONGS(gpiochip->ngpio), sizeof(long), GFP_KERNEL); - if (!gpiochip->irq_valid_mask) + if (!gpiochip->irq.valid_mask) return -ENOMEM; /* Assume by default all GPIOs are valid */ - bitmap_fill(gpiochip->irq_valid_mask, gpiochip->ngpio); + bitmap_fill(gpiochip->irq.valid_mask, gpiochip->ngpio); return 0; } static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip) { - kfree(gpiochip->irq_valid_mask); - gpiochip->irq_valid_mask = NULL; + kfree(gpiochip->irq.valid_mask); + gpiochip->irq.valid_mask = NULL; } static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip, unsigned int offset) { /* No mask means all valid */ - if (likely(!gpiochip->irq_valid_mask)) + if (likely(!gpiochip->irq.valid_mask)) return true; - return test_bit(offset, gpiochip->irq_valid_mask); + return test_bit(offset, gpiochip->irq.valid_mask); } /** diff --git a/drivers/pinctrl/intel/pinctrl-baytrail.c b/drivers/pinctrl/intel/pinctrl-baytrail.c index 5897981e5ed3..9c1ca29c60b7 100644 --- a/drivers/pinctrl/intel/pinctrl-baytrail.c +++ b/drivers/pinctrl/intel/pinctrl-baytrail.c @@ -1660,7 +1660,7 @@ static void byt_gpio_irq_init_hw(struct byt_gpio *vg) value = readl(reg); if (value & BYT_DIRECT_IRQ_EN) { - clear_bit(i, gc->irq_valid_mask); + clear_bit(i, gc->irq.valid_mask); dev_dbg(dev, "excluding GPIO %d from IRQ domain\n", i); } else if ((value & BYT_PIN_MUX) == byt_get_gpio_mux(vg, i)) { byt_gpio_clear_triggering(vg, i); @@ -1703,7 +1703,7 @@ static int byt_gpio_probe(struct byt_gpio *vg) gc->can_sleep = false; gc->parent = &vg->pdev->dev; gc->ngpio = vg->soc_data->npins; - gc->irq_need_valid_mask = true; + gc->irq.need_valid_mask = true; #ifdef CONFIG_PM_SLEEP vg->saved_context = devm_kcalloc(&vg->pdev->dev, gc->ngpio, diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 1cd7043edbc1..e23def322de2 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1584,7 +1584,7 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) chip->label = dev_name(pctrl->dev); chip->parent = pctrl->dev; chip->base = -1; - chip->irq_need_valid_mask = need_valid_mask; + chip->irq.need_valid_mask = need_valid_mask; ret = devm_gpiochip_add_data(pctrl->dev, chip, pctrl); if (ret) { @@ -1616,7 +1616,7 @@ static int chv_gpio_probe(struct chv_pinctrl *pctrl, int irq) intsel >>= CHV_PADCTRL0_INTSEL_SHIFT; if (need_valid_mask && intsel >= pctrl->community->nirqs) - clear_bit(i, chip->irq_valid_mask); + clear_bit(i, chip->irq.valid_mask); } /* Clear all interrupts */ diff --git a/drivers/platform/x86/intel_int0002_vgpio.c b/drivers/platform/x86/intel_int0002_vgpio.c index f6b3af73dea5..f7b67e898abc 100644 --- a/drivers/platform/x86/intel_int0002_vgpio.c +++ b/drivers/platform/x86/intel_int0002_vgpio.c @@ -165,7 +165,7 @@ static int int0002_probe(struct platform_device *pdev) chip->direction_output = int0002_gpio_direction_output; chip->base = -1; chip->ngpio = GPE0A_PME_B0_VIRT_GPIO_PIN + 1; - chip->irq_need_valid_mask = true; + chip->irq.need_valid_mask = true; ret = devm_gpiochip_add_data(&pdev->dev, chip, NULL); if (ret) { @@ -173,7 +173,7 @@ static int int0002_probe(struct platform_device *pdev) return ret; } - bitmap_clear(chip->irq_valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN); + bitmap_clear(chip->irq.valid_mask, 0, GPE0A_PME_B0_VIRT_GPIO_PIN); /* * We manually request the irq here instead of passing a flow-handler diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 1c3d06fe54b1..067efcd4f46d 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -99,6 +99,21 @@ struct gpio_irq_chip { * True if set the interrupt handling is nested. */ bool nested; + + /** + * @need_valid_mask: + * + * If set core allocates @valid_mask with all bits set to one. + */ + bool need_valid_mask; + + /** + * @valid_mask: + * + * If not %NULL holds bitmask of GPIOs which are valid to be included + * in IRQ domain of the chip. + */ + unsigned long *valid_mask; }; static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) @@ -170,10 +185,6 @@ static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) * safely. * @bgpio_dir: shadowed direction register for generic GPIO to clear/set * direction safely. - * @irq_need_valid_mask: If set core allocates @irq_valid_mask with all - * bits set to one - * @irq_valid_mask: If not %NULL holds bitmask of GPIOs which are valid to - * be included in IRQ domain of the chip * @lock_key: per GPIO IRQ chip lockdep class * * A gpio_chip can help platforms abstract various sources of GPIOs so @@ -244,8 +255,6 @@ struct gpio_chip { * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib * to handle IRQs for most practical cases. */ - bool irq_need_valid_mask; - unsigned long *irq_valid_mask; struct lock_class_key *lock_key; /** -- cgit v1.2.3-59-g8ed1b From ca9df053fb2bb2fcc64f37a1668321c7e19edd04 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:53 +0100 Subject: gpio: Move lock_key into struct gpio_irq_chip In order to consolidate the multiple ways to associate an IRQ chip with a GPIO chip, move more fields into the new struct gpio_irq_chip. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 4 ++-- include/linux/gpio/driver.h | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 0bf844470693..685a05caf1ba 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1646,7 +1646,7 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, * This lock class tells lockdep that GPIO irqs are in a different * category than their parents, so it won't report false recursion. */ - irq_set_lockdep_class(irq, chip->lock_key); + irq_set_lockdep_class(irq, chip->irq.lock_key); irq_set_chip_and_handler(irq, chip->irq.chip, chip->irq.handler); /* Chips that use nested thread handlers have them marked */ if (chip->irq.nested) @@ -1830,7 +1830,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, gpiochip->irq.handler = handler; gpiochip->irq.default_type = type; gpiochip->to_irq = gpiochip_to_irq; - gpiochip->lock_key = lock_key; + gpiochip->irq.lock_key = lock_key; gpiochip->irq.domain = irq_domain_add_simple(of_node, gpiochip->ngpio, first_irq, &gpiochip_domain_ops, gpiochip); diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 067efcd4f46d..c363ee198ff9 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -62,6 +62,13 @@ struct gpio_irq_chip { */ unsigned int default_type; + /** + * @lock_key: + * + * Per GPIO IRQ chip lockdep class. + */ + struct lock_class_key *lock_key; + /** * @parent_handler: * @@ -185,7 +192,6 @@ static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) * safely. * @bgpio_dir: shadowed direction register for generic GPIO to clear/set * direction safely. - * @lock_key: per GPIO IRQ chip lockdep class * * A gpio_chip can help platforms abstract various sources of GPIOs so * they can all be accessed through a common programing interface. @@ -255,7 +261,6 @@ struct gpio_chip { * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib * to handle IRQs for most practical cases. */ - struct lock_class_key *lock_key; /** * @irq: -- cgit v1.2.3-59-g8ed1b From e0d89728981393b7d694bd3419b7794b9882c92d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:54 +0100 Subject: gpio: Implement tighter IRQ chip integration Currently GPIO drivers are required to add the GPIO chip and its corresponding IRQ chip separately, which can result in a lot of boilerplate. Use the newly introduced struct gpio_irq_chip, embedded in struct gpio_chip, that drivers can fill in if they want the GPIO core to automatically register the IRQ chip associated with a GPIO chip. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 108 +++++++++++++++++++++++++++++++++++++++++++- include/linux/gpio/driver.h | 7 +++ 2 files changed, 114 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 685a05caf1ba..003d1bb85165 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -72,6 +72,7 @@ static LIST_HEAD(gpio_lookup_list); LIST_HEAD(gpio_devices); static void gpiochip_free_hogs(struct gpio_chip *chip); +static int gpiochip_add_irqchip(struct gpio_chip *gpiochip); static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip); static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip); static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip); @@ -1266,6 +1267,10 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) if (status) goto err_remove_from_list; + status = gpiochip_add_irqchip(chip); + if (status) + goto err_remove_chip; + status = of_gpiochip_add(chip); if (status) goto err_remove_chip; @@ -1637,6 +1642,7 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) { struct gpio_chip *chip = d->host_data; + int err = 0; if (!gpiochip_irqchip_irq_valid(chip, hwirq)) return -ENXIO; @@ -1653,6 +1659,14 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_set_nested_thread(irq, 1); irq_set_noprobe(irq); + if (chip->irq.num_parents == 1) + err = irq_set_parent(irq, chip->irq.parents[0]); + else if (chip->irq.map) + err = irq_set_parent(irq, chip->irq.map[hwirq]); + + if (err < 0) + return err; + /* * No set-up of the hardware will happen if IRQ_TYPE_NONE * is passed as default type. @@ -1709,9 +1723,96 @@ static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) { if (!gpiochip_irqchip_irq_valid(chip, offset)) return -ENXIO; + return irq_create_mapping(chip->irq.domain, offset); } +/** + * gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip + * @gpiochip: the GPIO chip to add the IRQ chip to + */ +static int gpiochip_add_irqchip(struct gpio_chip *gpiochip) +{ + struct irq_chip *irqchip = gpiochip->irq.chip; + const struct irq_domain_ops *ops; + struct device_node *np; + unsigned int type; + unsigned int i; + + if (!irqchip) + return 0; + + if (gpiochip->irq.parent_handler && gpiochip->can_sleep) { + chip_err(gpiochip, "you cannot have chained interrupts on a " + "chip that may sleep\n"); + return -EINVAL; + } + + np = gpiochip->gpiodev->dev.of_node; + type = gpiochip->irq.default_type; + + /* + * Specifying a default trigger is a terrible idea if DT or ACPI is + * used to configure the interrupts, as you may end up with + * conflicting triggers. Tell the user, and reset to NONE. + */ + if (WARN(np && type != IRQ_TYPE_NONE, + "%s: Ignoring %u default trigger\n", np->full_name, type)) + type = IRQ_TYPE_NONE; + + if (has_acpi_companion(gpiochip->parent) && type != IRQ_TYPE_NONE) { + acpi_handle_warn(ACPI_HANDLE(gpiochip->parent), + "Ignoring %u default trigger\n", type); + type = IRQ_TYPE_NONE; + } + + gpiochip->to_irq = gpiochip_to_irq; + gpiochip->irq.default_type = type; + + if (gpiochip->irq.domain_ops) + ops = gpiochip->irq.domain_ops; + else + ops = &gpiochip_domain_ops; + + gpiochip->irq.domain = irq_domain_add_simple(np, gpiochip->ngpio, + 0, ops, gpiochip); + if (!gpiochip->irq.domain) + return -EINVAL; + + /* + * It is possible for a driver to override this, but only if the + * alternative functions are both implemented. + */ + if (!irqchip->irq_request_resources && + !irqchip->irq_release_resources) { + irqchip->irq_request_resources = gpiochip_irq_reqres; + irqchip->irq_release_resources = gpiochip_irq_relres; + } + + if (gpiochip->irq.parent_handler) { + void *data = gpiochip->irq.parent_handler_data ?: gpiochip; + + for (i = 0; i < gpiochip->irq.num_parents; i++) { + /* + * The parent IRQ chip is already using the chip_data + * for this IRQ chip, so our callbacks simply use the + * handler_data. + */ + irq_set_chained_handler_and_data(gpiochip->irq.parents[i], + gpiochip->irq.parent_handler, + data); + } + + gpiochip->irq.nested = false; + } else { + gpiochip->irq.nested = true; + } + + acpi_gpiochip_request_interrupts(gpiochip); + + return 0; +} + /** * gpiochip_irqchip_remove() - removes an irqchip added to a gpiochip * @gpiochip: the gpiochip to remove the irqchip from @@ -1724,7 +1825,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) acpi_gpiochip_free_interrupts(gpiochip); - if (gpiochip->irq.num_parents > 0) { + if (gpiochip->irq.chip && gpiochip->irq.parent_handler) { struct gpio_irq_chip *irq = &gpiochip->irq; unsigned int i; @@ -1857,6 +1958,11 @@ EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_key); #else /* CONFIG_GPIOLIB_IRQCHIP */ +static inline int gpiochip_add_irqchip(struct gpio_chip *gpiochip) +{ + return 0; +} + static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) {} static inline int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip) { diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index c363ee198ff9..51fc7b023364 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -100,6 +100,13 @@ struct gpio_irq_chip { */ unsigned int *parents; + /** + * @map: + * + * A list of interrupt parents for each line of a GPIO chip. + */ + unsigned int *map; + /** * @nested: * -- cgit v1.2.3-59-g8ed1b From 1b95b4eb567aab1cafcdcb14c60a7dd9d56236a9 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:55 +0100 Subject: gpio: Export gpiochip_irq_{map,unmap}() Export these functions so that drivers can explicitly use these when setting up their IRQ domain. Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 8 +++++--- include/linux/gpio/driver.h | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 003d1bb85165..7347ea1c699a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1638,8 +1638,8 @@ EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); * gpiochip by assigning the gpiochip as chip data, and using the irqchip * stored inside the gpiochip. */ -static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) +int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) { struct gpio_chip *chip = d->host_data; int err = 0; @@ -1676,8 +1676,9 @@ static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, return 0; } +EXPORT_SYMBOL_GPL(gpiochip_irq_map); -static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) +void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) { struct gpio_chip *chip = d->host_data; @@ -1686,6 +1687,7 @@ static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) irq_set_chip_and_handler(irq, NULL, NULL); irq_set_chip_data(irq, NULL); } +EXPORT_SYMBOL_GPL(gpiochip_irq_unmap); static const struct irq_domain_ops gpiochip_domain_ops = { .map = gpiochip_irq_map, diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 51fc7b023364..bbe5c647f29d 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -367,6 +367,10 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev, #ifdef CONFIG_GPIOLIB_IRQCHIP +int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq); +void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq); + void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, struct irq_chip *irqchip, unsigned int parent_irq, -- cgit v1.2.3-59-g8ed1b From 60ed54cae8dc0f2d41cafbd477bbed6deb716615 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:57 +0100 Subject: gpio: Disambiguate struct gpio_irq_chip.nested The nested field in struct gpio_irq_chip currently has two meanings. On one hand it marks an IRQ chip as being nested (as opposed to chained), while on the other hand it also means that an IRQ chip uses nested thread handlers. However, nested IRQ chips can already be identified by the fact that they don't pass a parent handler (the driver would instead already have installed a nested handler using request_irq()). Therefore, the only use for the nested attribute is to inform gpiolib that an IRQ chip uses nested thread handlers (as opposed to regular, non-threaded handlers). To clarify its purpose, rename the field to "threaded". Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 24 ++++++++++-------------- include/linux/gpio/driver.h | 8 ++++---- 2 files changed, 14 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 7347ea1c699a..389257f97e45 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1603,6 +1603,11 @@ void gpiochip_set_chained_irqchip(struct gpio_chip *gpiochip, unsigned int parent_irq, irq_flow_handler_t parent_handler) { + if (gpiochip->irq.threaded) { + chip_err(gpiochip, "tried to chain a threaded gpiochip\n"); + return; + } + gpiochip_set_cascaded_irqchip(gpiochip, irqchip, parent_irq, parent_handler); } @@ -1619,10 +1624,6 @@ void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip, struct irq_chip *irqchip, unsigned int parent_irq) { - if (!gpiochip->irq.nested) { - chip_err(gpiochip, "tried to nest a chained gpiochip\n"); - return; - } gpiochip_set_cascaded_irqchip(gpiochip, irqchip, parent_irq, NULL); } @@ -1655,7 +1656,7 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_set_lockdep_class(irq, chip->irq.lock_key); irq_set_chip_and_handler(irq, chip->irq.chip, chip->irq.handler); /* Chips that use nested thread handlers have them marked */ - if (chip->irq.nested) + if (chip->irq.threaded) irq_set_nested_thread(irq, 1); irq_set_noprobe(irq); @@ -1682,7 +1683,7 @@ void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) { struct gpio_chip *chip = d->host_data; - if (chip->irq.nested) + if (chip->irq.threaded) irq_set_nested_thread(irq, 0); irq_set_chip_and_handler(irq, NULL, NULL); irq_set_chip_data(irq, NULL); @@ -1804,10 +1805,6 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip) gpiochip->irq.parent_handler, data); } - - gpiochip->irq.nested = false; - } else { - gpiochip->irq.nested = true; } acpi_gpiochip_request_interrupts(gpiochip); @@ -1869,8 +1866,7 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip) * @handler: the irq handler to use (often a predefined irq core function) * @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE * to have the core avoid setting up any default type in the hardware. - * @nested: whether this is a nested irqchip calling handle_nested_irq() - * in its IRQ handler + * @threaded: whether this irqchip uses a nested thread handler * @lock_key: lockdep class * * This function closely associates a certain irqchip with a certain @@ -1892,7 +1888,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, unsigned int first_irq, irq_flow_handler_t handler, unsigned int type, - bool nested, + bool threaded, struct lock_class_key *lock_key) { struct device_node *of_node; @@ -1904,7 +1900,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, pr_err("missing gpiochip .dev parent pointer\n"); return -EINVAL; } - gpiochip->irq.nested = nested; + gpiochip->irq.threaded = threaded; of_node = gpiochip->parent->of_node; #ifdef CONFIG_OF_GPIO /* diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index bbe5c647f29d..0a3fdd4d9d8d 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -108,11 +108,11 @@ struct gpio_irq_chip { unsigned int *map; /** - * @nested: + * @threaded: * - * True if set the interrupt handling is nested. + * True if set the interrupt handling uses nested threads. */ - bool nested; + bool threaded; /** * @need_valid_mask: @@ -385,7 +385,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip, unsigned int first_irq, irq_flow_handler_t handler, unsigned int type, - bool nested, + bool threaded, struct lock_class_key *lock_key); #ifdef CONFIG_LOCKDEP -- cgit v1.2.3-59-g8ed1b From 8302cf585288f75fd253f6b9a094d51ae371a3f3 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:58 +0100 Subject: gpio: Introduce struct gpio_irq_chip.first Some GPIO chips cannot support sparse IRQ numbering and therefore need to manually allocate their interrupt descriptors statically. For these cases, a driver can pass the first allocated IRQ via the struct gpio_irq_chip's "first" field and thereby cause the IRQ domain to map all IRQs during initialization. Suggested-by: Grygorii Strashko Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 3 ++- include/linux/gpio/driver.h | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 389257f97e45..6d5c366a1378 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1778,7 +1778,8 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip) ops = &gpiochip_domain_ops; gpiochip->irq.domain = irq_domain_add_simple(np, gpiochip->ngpio, - 0, ops, gpiochip); + gpiochip->irq.first, + ops, gpiochip); if (!gpiochip->irq.domain) return -EINVAL; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 0a3fdd4d9d8d..a77d4ada060c 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -128,6 +128,14 @@ struct gpio_irq_chip { * in IRQ domain of the chip. */ unsigned long *valid_mask; + + /** + * @first: + * + * Required for static IRQ allocation. If set, irq_domain_add_simple() + * will allocate and map all IRQs during initialization. + */ + unsigned int first; }; static inline struct gpio_irq_chip *to_gpio_irq_chip(struct irq_chip *chip) -- cgit v1.2.3-59-g8ed1b From 959bc7b22bd25a3a907fbb9b26a1d0cbdf98ef40 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 7 Nov 2017 19:15:59 +0100 Subject: gpio: Automatically add lockdep keys In order to avoid lockdep boilerplate in individual drivers, turn the gpiochip_add_data() function into a macro that creates a unique class key for each driver. Note that this has the slight disadvantage of adding a key for each driver registered with the system. However, these keys are 8 bytes in size, which is negligible and a small price to pay for generic infrastructure. Suggested-by: Grygorii Strashko Signed-off-by: Thierry Reding Acked-by: Grygorii Strashko [renane __gpiochip_add_data() to gpiochip_add_data_with_key] Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 41 ++++++++++++----------------------------- include/linux/gpio/driver.h | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 6d5c366a1378..961b5da38bb9 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -72,7 +72,8 @@ static LIST_HEAD(gpio_lookup_list); LIST_HEAD(gpio_devices); static void gpiochip_free_hogs(struct gpio_chip *chip); -static int gpiochip_add_irqchip(struct gpio_chip *gpiochip); +static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, + struct lock_class_key *key); static void gpiochip_irqchip_remove(struct gpio_chip *gpiochip); static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip); static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip); @@ -1098,30 +1099,8 @@ static void gpiochip_setup_devs(void) } } -/** - * gpiochip_add_data() - register a gpio_chip - * @chip: the chip to register, with chip->base initialized - * @data: driver-private data associated with this chip - * - * Context: potentially before irqs will work - * - * When gpiochip_add_data() is called very early during boot, so that GPIOs - * can be freely used, the chip->parent device must be registered before - * the gpio framework's arch_initcall(). Otherwise sysfs initialization - * for GPIOs will fail rudely. - * - * gpiochip_add_data() must only be called after gpiolib initialization, - * ie after core_initcall(). - * - * If chip->base is negative, this requests dynamic assignment of - * a range of valid GPIOs. - * - * Returns: - * A negative errno if the chip can't be registered, such as because the - * chip->base is invalid or already associated with a different chip. - * Otherwise it returns zero as a success code. - */ -int gpiochip_add_data(struct gpio_chip *chip, void *data) +int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, + struct lock_class_key *key) { unsigned long flags; int status = 0; @@ -1267,7 +1246,7 @@ int gpiochip_add_data(struct gpio_chip *chip, void *data) if (status) goto err_remove_from_list; - status = gpiochip_add_irqchip(chip); + status = gpiochip_add_irqchip(chip, key); if (status) goto err_remove_chip; @@ -1314,7 +1293,7 @@ err_free_gdev: kfree(gdev); return status; } -EXPORT_SYMBOL_GPL(gpiochip_add_data); +EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); /** * gpiochip_get_data() - get per-subdriver data for the chip @@ -1733,8 +1712,10 @@ static int gpiochip_to_irq(struct gpio_chip *chip, unsigned offset) /** * gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip * @gpiochip: the GPIO chip to add the IRQ chip to + * @lock_key: lockdep class */ -static int gpiochip_add_irqchip(struct gpio_chip *gpiochip) +static int gpiochip_add_irqchip(struct gpio_chip *gpiochip, + struct lock_class_key *lock_key) { struct irq_chip *irqchip = gpiochip->irq.chip; const struct irq_domain_ops *ops; @@ -1771,6 +1752,7 @@ static int gpiochip_add_irqchip(struct gpio_chip *gpiochip) gpiochip->to_irq = gpiochip_to_irq; gpiochip->irq.default_type = type; + gpiochip->irq.lock_key = lock_key; if (gpiochip->irq.domain_ops) ops = gpiochip->irq.domain_ops; @@ -1957,7 +1939,8 @@ EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_key); #else /* CONFIG_GPIOLIB_IRQCHIP */ -static inline int gpiochip_add_irqchip(struct gpio_chip *gpiochip) +static inline int gpiochip_add_irqchip(struct gpio_chip *gpiochip, + struct lock_dep_class *lock_key) { return 0; } diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index a77d4ada060c..8dd282c5167a 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -321,7 +321,41 @@ extern const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset); /* add/remove chips */ -extern int gpiochip_add_data(struct gpio_chip *chip, void *data); +extern int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data, + struct lock_class_key *lock_key); + +/** + * gpiochip_add_data() - register a gpio_chip + * @chip: the chip to register, with chip->base initialized + * @data: driver-private data associated with this chip + * + * Context: potentially before irqs will work + * + * When gpiochip_add_data() is called very early during boot, so that GPIOs + * can be freely used, the chip->parent device must be registered before + * the gpio framework's arch_initcall(). Otherwise sysfs initialization + * for GPIOs will fail rudely. + * + * gpiochip_add_data() must only be called after gpiolib initialization, + * ie after core_initcall(). + * + * If chip->base is negative, this requests dynamic assignment of + * a range of valid GPIOs. + * + * Returns: + * A negative errno if the chip can't be registered, such as because the + * chip->base is invalid or already associated with a different chip. + * Otherwise it returns zero as a success code. + */ +#ifdef CONFIG_LOCKDEP +#define gpiochip_add_data(chip, data) ({ \ + static struct lock_class_key key; \ + gpiochip_add_data_with_key(chip, data, &key); \ + }) +#else +#define gpiochip_add_data(chip, data) gpiochip_add_data_with_key(chip, data, NULL) +#endif + static inline int gpiochip_add(struct gpio_chip *chip) { return gpiochip_add_data(chip, NULL); -- cgit v1.2.3-59-g8ed1b From 738b49efe6c6ba485e2b45265db535c58bbd54e7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Nov 2017 01:48:19 +0000 Subject: ASoC: add snd_soc_component_read32 Current codec drivers are using snd_soc_read(). It will be replaced into snd_soc_component_read(), but these 2 are using different style. For example, it will be - val = snd_soc_read(xxx, reg); + ret = snd_soc_component_read(xxx, reg, &val); + if (ret < 0) { + ... + } To more smooth replace, let's add snd_soc_component_read32 which is copied from snd_soc_read() - val = snd_soc_read(xxx, reg); + val = snd_soc_component_read32(xxx, reg); Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 2 ++ sound/soc/soc-io.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 6c808527a4f6..747588273c96 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1498,6 +1498,8 @@ static inline int snd_soc_cache_sync(struct snd_soc_codec *codec) /* component IO */ int snd_soc_component_read(struct snd_soc_component *component, unsigned int reg, unsigned int *val); +unsigned int snd_soc_component_read32(struct snd_soc_component *component, + unsigned int reg); int snd_soc_component_write(struct snd_soc_component *component, unsigned int reg, unsigned int val); int snd_soc_component_update_bits(struct snd_soc_component *component, diff --git a/sound/soc/soc-io.c b/sound/soc/soc-io.c index 9b3939049cef..20340ade20a7 100644 --- a/sound/soc/soc-io.c +++ b/sound/soc/soc-io.c @@ -41,6 +41,20 @@ int snd_soc_component_read(struct snd_soc_component *component, } EXPORT_SYMBOL_GPL(snd_soc_component_read); +unsigned int snd_soc_component_read32(struct snd_soc_component *component, + unsigned int reg) +{ + unsigned int val; + int ret; + + ret = snd_soc_component_read(component, reg, &val); + if (ret < 0) + return -1; + + return val; +} +EXPORT_SYMBOL_GPL(snd_soc_component_read32); + /** * snd_soc_component_write() - Write register value * @component: Component to write to -- cgit v1.2.3-59-g8ed1b From 10e079d9874802c35dc3f51af922531e5423a6bf Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Nov 2017 01:48:37 +0000 Subject: ASoC: add snd_soc_component_xxx_bias_level() snd_soc_codec_xxx_bias_level() (= for Codec) will be removed soon. This patch Component version of it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 747588273c96..f69b13c4a641 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1439,6 +1439,21 @@ static inline void snd_soc_codec_init_bias_level(struct snd_soc_codec *codec, snd_soc_dapm_init_bias_level(snd_soc_codec_get_dapm(codec), level); } +/** + * snd_soc_component_init_bias_level() - Initialize COMPONENT DAPM bias level + * @component: The COMPONENT for which to initialize the DAPM bias level + * @level: The DAPM level to initialize to + * + * Initializes the COMPONENT DAPM bias level. See snd_soc_dapm_init_bias_level(). + */ +static inline void +snd_soc_component_init_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + snd_soc_dapm_init_bias_level( + snd_soc_component_get_dapm(component), level); +} + /** * snd_soc_dapm_get_bias_level() - Get current CODEC DAPM bias level * @codec: The CODEC for which to get the DAPM bias level @@ -1451,6 +1466,19 @@ static inline enum snd_soc_bias_level snd_soc_codec_get_bias_level( return snd_soc_dapm_get_bias_level(snd_soc_codec_get_dapm(codec)); } +/** + * snd_soc_component_get_bias_level() - Get current COMPONENT DAPM bias level + * @component: The COMPONENT for which to get the DAPM bias level + * + * Returns: The current DAPM bias level of the COMPONENT. + */ +static inline enum snd_soc_bias_level +snd_soc_component_get_bias_level(struct snd_soc_component *component) +{ + return snd_soc_dapm_get_bias_level( + snd_soc_component_get_dapm(component)); +} + /** * snd_soc_codec_force_bias_level() - Set the CODEC DAPM bias level * @codec: The CODEC for which to set the level @@ -1466,6 +1494,23 @@ static inline int snd_soc_codec_force_bias_level(struct snd_soc_codec *codec, level); } +/** + * snd_soc_component_force_bias_level() - Set the COMPONENT DAPM bias level + * @component: The COMPONENT for which to set the level + * @level: The level to set to + * + * Forces the COMPONENT bias level to a specific state. See + * snd_soc_dapm_force_bias_level(). + */ +static inline int +snd_soc_component_force_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + return snd_soc_dapm_force_bias_level( + snd_soc_component_get_dapm(component), + level); +} + /** * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol * @kcontrol: The kcontrol -- cgit v1.2.3-59-g8ed1b From 474db2c9991cc624e7415770c8b80fc7acf40cdd Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Nov 2017 01:48:52 +0000 Subject: ASoC: add snd_soc_component_cache_sync() snd_soc_cache_sync() (= for Codec) will be removed soon. This patch Component version of it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index f69b13c4a641..4961f4455b65 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1540,6 +1540,18 @@ static inline int snd_soc_cache_sync(struct snd_soc_codec *codec) return regcache_sync(codec->component.regmap); } +/** + * snd_soc_component_cache_sync() - Sync the register cache with the hardware + * @component: COMPONENT to sync + * + * Note: This function will call regcache_sync() + */ +static inline int snd_soc_component_cache_sync( + struct snd_soc_component *component) +{ + return regcache_sync(component->regmap); +} + /* component IO */ int snd_soc_component_read(struct snd_soc_component *component, unsigned int reg, unsigned int *val); -- cgit v1.2.3-59-g8ed1b From e07bd30bb87f6a6ca1f75fa41df71ff5e7bc6a3f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 6 Nov 2017 01:49:06 +0000 Subject: ASoC: add snd_soc_dapm_kcontrol_component() snd_soc_dapm_kcontrol_codec() (= for Codec) will be removed soon. This patch Component version of it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 4961f4455b65..0668cbd9f0b5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1524,6 +1524,19 @@ static inline struct snd_soc_codec *snd_soc_dapm_kcontrol_codec( return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol)); } +/** + * snd_soc_dapm_kcontrol_component() - Returns the component associated to a kcontrol + * @kcontrol: The kcontrol + * + * This function must only be used on DAPM contexts that are known to be part of + * a COMPONENT (e.g. in a COMPONENT driver). Otherwise the behavior is undefined. + */ +static inline struct snd_soc_component *snd_soc_dapm_kcontrol_component( + struct snd_kcontrol *kcontrol) +{ + return snd_soc_dapm_to_component(snd_soc_dapm_kcontrol_dapm(kcontrol)); +} + /* codec IO */ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, -- cgit v1.2.3-59-g8ed1b From 2229f70b5bbb025e1394b61007938a68060afbfb Mon Sep 17 00:00:00 2001 From: Joao Martins Date: Wed, 8 Nov 2017 17:19:57 +0000 Subject: x86/xen/time: setup vcpu 0 time info page In order to support pvclock vdso on xen we need to setup the time info page for vcpu 0 and register the page with Xen using the VCPUOP_register_vcpu_time_memory_area hypercall. This hypercall will also forcefully update the pvti which will set some of the necessary flags for vdso. Afterwards we check if it supports the PVCLOCK_TSC_STABLE_BIT flag which is mandatory for having vdso/vsyscall support. And if so, it will set the cpu 0 pvti that will be later on used when mapping the vdso image. The xen headers are also updated to include the new hypercall for registering the secondary vcpu_time_info struct. Signed-off-by: Joao Martins Reviewed-by: Juergen Gross Reviewed-by: Boris Ostrovsky Signed-off-by: Boris Ostrovsky --- arch/x86/xen/suspend.c | 4 ++ arch/x86/xen/time.c | 90 +++++++++++++++++++++++++++++++++++++++++++- arch/x86/xen/xen-ops.h | 2 + include/xen/interface/vcpu.h | 42 +++++++++++++++++++++ 4 files changed, 137 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c index d6b1680693a9..800ed36ecfba 100644 --- a/arch/x86/xen/suspend.c +++ b/arch/x86/xen/suspend.c @@ -16,6 +16,8 @@ void xen_arch_pre_suspend(void) { + xen_save_time_memory_area(); + if (xen_pv_domain()) xen_pv_pre_suspend(); } @@ -26,6 +28,8 @@ void xen_arch_post_suspend(int cancelled) xen_pv_post_suspend(cancelled); else xen_hvm_post_suspend(cancelled); + + xen_restore_time_memory_area(); } static void xen_vcpu_notify_restore(void *data) diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c index c96e61fd70e7..c2041043c606 100644 --- a/arch/x86/xen/time.c +++ b/arch/x86/xen/time.c @@ -370,6 +370,92 @@ static const struct pv_time_ops xen_time_ops __initconst = { .steal_clock = xen_steal_clock, }; +static struct pvclock_vsyscall_time_info *xen_clock __read_mostly; + +void xen_save_time_memory_area(void) +{ + struct vcpu_register_time_memory_area t; + int ret; + + if (!xen_clock) + return; + + t.addr.v = NULL; + + ret = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_time_memory_area, 0, &t); + if (ret != 0) + pr_notice("Cannot save secondary vcpu_time_info (err %d)", + ret); + else + clear_page(xen_clock); +} + +void xen_restore_time_memory_area(void) +{ + struct vcpu_register_time_memory_area t; + int ret; + + if (!xen_clock) + return; + + t.addr.v = &xen_clock->pvti; + + ret = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_time_memory_area, 0, &t); + + /* + * We don't disable VCLOCK_PVCLOCK entirely if it fails to register the + * secondary time info with Xen or if we migrated to a host without the + * necessary flags. On both of these cases what happens is either + * process seeing a zeroed out pvti or seeing no PVCLOCK_TSC_STABLE_BIT + * bit set. Userspace checks the latter and if 0, it discards the data + * in pvti and fallbacks to a system call for a reliable timestamp. + */ + if (ret != 0) + pr_notice("Cannot restore secondary vcpu_time_info (err %d)", + ret); +} + +static void xen_setup_vsyscall_time_info(void) +{ + struct vcpu_register_time_memory_area t; + struct pvclock_vsyscall_time_info *ti; + int ret; + + ti = (struct pvclock_vsyscall_time_info *)get_zeroed_page(GFP_KERNEL); + if (!ti) + return; + + t.addr.v = &ti->pvti; + + ret = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_time_memory_area, 0, &t); + if (ret) { + pr_notice("xen: VCLOCK_PVCLOCK not supported (err %d)\n", ret); + free_page((unsigned long)ti); + return; + } + + /* + * If primary time info had this bit set, secondary should too since + * it's the same data on both just different memory regions. But we + * still check it in case hypervisor is buggy. + */ + if (!(ti->pvti.flags & PVCLOCK_TSC_STABLE_BIT)) { + t.addr.v = NULL; + ret = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_time_memory_area, + 0, &t); + if (!ret) + free_page((unsigned long)ti); + + pr_notice("xen: VCLOCK_PVCLOCK not supported (tsc unstable)\n"); + return; + } + + xen_clock = ti; + pvclock_set_pvti_cpu0_va(xen_clock); + + xen_clocksource.archdata.vclock_mode = VCLOCK_PVCLOCK; +} + static void __init xen_time_init(void) { struct pvclock_vcpu_time_info *pvti; @@ -401,8 +487,10 @@ static void __init xen_time_init(void) * bit is supported hence speeding up Xen clocksource. */ pvti = &__this_cpu_read(xen_vcpu)->time; - if (pvti->flags & PVCLOCK_TSC_STABLE_BIT) + if (pvti->flags & PVCLOCK_TSC_STABLE_BIT) { pvclock_set_flags(PVCLOCK_TSC_STABLE_BIT); + xen_setup_vsyscall_time_info(); + } xen_setup_runstate_info(cpu); xen_setup_timer(cpu); diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index c8a6d224f7ed..f96dbedb33d4 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h @@ -69,6 +69,8 @@ void xen_setup_runstate_info(int cpu); void xen_teardown_timer(int cpu); u64 xen_clocksource_read(void); void xen_setup_cpu_clockevents(void); +void xen_save_time_memory_area(void); +void xen_restore_time_memory_area(void); void __init xen_init_time_ops(void); void __init xen_hvm_init_time_ops(void); diff --git a/include/xen/interface/vcpu.h b/include/xen/interface/vcpu.h index 98188c87f5c1..504c71601511 100644 --- a/include/xen/interface/vcpu.h +++ b/include/xen/interface/vcpu.h @@ -178,4 +178,46 @@ DEFINE_GUEST_HANDLE_STRUCT(vcpu_register_vcpu_info); /* Send an NMI to the specified VCPU. @extra_arg == NULL. */ #define VCPUOP_send_nmi 11 + +/* + * Get the physical ID information for a pinned vcpu's underlying physical + * processor. The physical ID informmation is architecture-specific. + * On x86: id[31:0]=apic_id, id[63:32]=acpi_id. + * This command returns -EINVAL if it is not a valid operation for this VCPU. + */ +#define VCPUOP_get_physid 12 /* arg == vcpu_get_physid_t */ +struct vcpu_get_physid { + uint64_t phys_id; +}; +DEFINE_GUEST_HANDLE_STRUCT(vcpu_get_physid); +#define xen_vcpu_physid_to_x86_apicid(physid) ((uint32_t)(physid)) +#define xen_vcpu_physid_to_x86_acpiid(physid) ((uint32_t)((physid) >> 32)) + +/* + * Register a memory location to get a secondary copy of the vcpu time + * parameters. The master copy still exists as part of the vcpu shared + * memory area, and this secondary copy is updated whenever the master copy + * is updated (and using the same versioning scheme for synchronisation). + * + * The intent is that this copy may be mapped (RO) into userspace so + * that usermode can compute system time using the time info and the + * tsc. Usermode will see an array of vcpu_time_info structures, one + * for each vcpu, and choose the right one by an existing mechanism + * which allows it to get the current vcpu number (such as via a + * segment limit). It can then apply the normal algorithm to compute + * system time from the tsc. + * + * @extra_arg == pointer to vcpu_register_time_info_memory_area structure. + */ +#define VCPUOP_register_vcpu_time_memory_area 13 +DEFINE_GUEST_HANDLE_STRUCT(vcpu_time_info); +struct vcpu_register_time_memory_area { + union { + GUEST_HANDLE(vcpu_time_info) h; + struct pvclock_vcpu_time_info *v; + uint64_t p; + } addr; +}; +DEFINE_GUEST_HANDLE_STRUCT(vcpu_register_time_memory_area); + #endif /* __XEN_PUBLIC_VCPU_H__ */ -- cgit v1.2.3-59-g8ed1b From 2dd9789c76ffde05d5f4c56f45c3cb71b3936694 Mon Sep 17 00:00:00 2001 From: Himanshu Jha Date: Sun, 5 Nov 2017 03:27:32 +0530 Subject: freezer: Fix typo in freezable_schedule_timeout() comment Signed-off-by: Himanshu Jha Acked-by: Luis R. Rodriguez Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- include/linux/freezer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/freezer.h b/include/linux/freezer.h index dd03e837ebb7..5b2cf48b2a7c 100644 --- a/include/linux/freezer.h +++ b/include/linux/freezer.h @@ -181,7 +181,7 @@ static inline void freezable_schedule_unsafe(void) } /* - * Like freezable_schedule_timeout(), but should not block the freezer. Do not + * Like schedule_timeout(), but should not block the freezer. Do not * call this with locks held. */ static inline long freezable_schedule_timeout(long timeout) -- cgit v1.2.3-59-g8ed1b From 335f83b9113421a65bfb19e0fa6a2b262ca86c26 Mon Sep 17 00:00:00 2001 From: James Smart Date: Fri, 3 Nov 2017 16:26:08 -0700 Subject: scsi: scsi_transport_fc: add 64GBIT and 128GBIT port speed definitions Add 64GBIT and 128GBIT port speed definitions. Upcoming hardware will reference these speeds. Signed-off-by: James Smart Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_transport_fc.c | 2 ++ include/scsi/scsi_transport_fc.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 3c6bc0081fcb..49058295d9bc 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -267,6 +267,8 @@ static const struct { { FC_PORTSPEED_50GBIT, "50 Gbit" }, { FC_PORTSPEED_100GBIT, "100 Gbit" }, { FC_PORTSPEED_25GBIT, "25 Gbit" }, + { FC_PORTSPEED_64BIT, "64 Gbit" }, + { FC_PORTSPEED_128BIT, "128 Gbit" }, { FC_PORTSPEED_NOT_NEGOTIATED, "Not Negotiated" }, }; fc_bitfield_name_search(port_speed, fc_port_speed_names) diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h index e8644eea9fe5..8cf30215c177 100644 --- a/include/scsi/scsi_transport_fc.h +++ b/include/scsi/scsi_transport_fc.h @@ -139,6 +139,8 @@ enum fc_vport_state { #define FC_PORTSPEED_50GBIT 0x200 #define FC_PORTSPEED_100GBIT 0x400 #define FC_PORTSPEED_25GBIT 0x800 +#define FC_PORTSPEED_64BIT 0x1000 +#define FC_PORTSPEED_128BIT 0x2000 #define FC_PORTSPEED_NOT_NEGOTIATED (1 << 15) /* Speed not established */ /* -- cgit v1.2.3-59-g8ed1b From cf89a31ca55272e1dfb9527b5a61eee4d417747a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 19 Sep 2017 12:39:11 +0300 Subject: device property: Make fwnode_handle_get() return the fwnode The fwnode_handle_get() function is used to obtain a reference to an fwnode. A common usage pattern for the OF equivalent of the function is: mynode = of_node_get(node); Similarly make fwnode_handle_get() return the fwnode to which the reference was obtained. Signed-off-by: Sakari Ailus Signed-off-by: Rafael J. Wysocki --- drivers/base/property.c | 9 +++++++-- drivers/of/property.c | 4 ++-- include/linux/fwnode.h | 2 +- include/linux/property.h | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/base/property.c b/drivers/base/property.c index 7ed99c1b2a8b..851b1b6596a4 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -1044,10 +1044,15 @@ EXPORT_SYMBOL_GPL(device_get_named_child_node); /** * fwnode_handle_get - Obtain a reference to a device node * @fwnode: Pointer to the device node to obtain the reference to. + * + * Returns the fwnode handle. */ -void fwnode_handle_get(struct fwnode_handle *fwnode) +struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode) { - fwnode_call_void_op(fwnode, get); + if (!fwnode_has_op(fwnode, get)) + return fwnode; + + return fwnode_call_ptr_op(fwnode, get); } EXPORT_SYMBOL_GPL(fwnode_handle_get); diff --git a/drivers/of/property.c b/drivers/of/property.c index 264c355ba1ff..8ad33a44a7b8 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -817,9 +817,9 @@ struct device_node *of_graph_get_remote_node(const struct device_node *node, } EXPORT_SYMBOL(of_graph_get_remote_node); -static void of_fwnode_get(struct fwnode_handle *fwnode) +static struct fwnode_handle *of_fwnode_get(struct fwnode_handle *fwnode) { - of_node_get(to_of_node(fwnode)); + return of_fwnode_handle(of_node_get(to_of_node(fwnode))); } static void of_fwnode_put(struct fwnode_handle *fwnode) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 0c35b6caf0f6..411a84c6c400 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -68,7 +68,7 @@ struct fwnode_reference_args { * @graph_parse_endpoint: Parse endpoint for port and endpoint id. */ struct fwnode_operations { - void (*get)(struct fwnode_handle *fwnode); + struct fwnode_handle *(*get)(struct fwnode_handle *fwnode); void (*put)(struct fwnode_handle *fwnode); bool (*device_is_available)(const struct fwnode_handle *fwnode); bool (*property_present)(const struct fwnode_handle *fwnode, diff --git a/include/linux/property.h b/include/linux/property.h index 6bebee13c5e0..42069c20caee 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -100,7 +100,7 @@ struct fwnode_handle *fwnode_get_named_child_node( struct fwnode_handle *device_get_named_child_node(struct device *dev, const char *childname); -void fwnode_handle_get(struct fwnode_handle *fwnode); +struct fwnode_handle *fwnode_handle_get(struct fwnode_handle *fwnode); void fwnode_handle_put(struct fwnode_handle *fwnode); unsigned int device_get_child_node_count(struct device *dev); -- cgit v1.2.3-59-g8ed1b From 5e4b1b707b7ed795585b8ee28c44ce692aa8dfef Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Tue, 19 Sep 2017 12:39:12 +0300 Subject: device property: Add a macro for interating over graph endpoints Add a convenience macro for iterating over graph endpoints. Iterating over graph endpoints using fwnode_graph_get_next_endpoint() is a recurring pattern, and this macro allows calling that function in a slightly more convenient way. For instance, for (child = NULL; (child = fwnode_graph_get_next_endpoint(fwnode, child)); ) becomes fwnode_graph_for_each_endpoint(fwnode, child) Signed-off-by: Sakari Ailus Signed-off-by: Rafael J. Wysocki --- include/linux/property.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/property.h b/include/linux/property.h index 42069c20caee..f6189a3ac63c 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -293,6 +293,10 @@ struct fwnode_handle * fwnode_graph_get_remote_node(const struct fwnode_handle *fwnode, u32 port, u32 endpoint); +#define fwnode_graph_for_each_endpoint(fwnode, child) \ + for (child = NULL; \ + (child = fwnode_graph_get_next_endpoint(fwnode, child)); ) + int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode, struct fwnode_endpoint *endpoint); -- cgit v1.2.3-59-g8ed1b From c4b766c2f3fc4d847f16a3735913b7edf3136343 Mon Sep 17 00:00:00 2001 From: George Cherian Date: Wed, 11 Oct 2017 08:54:57 +0000 Subject: mailbox: PCC: Move the MAX_PCC_SUBSPACES definition to header file Move the MAX_PCC_SUBSPACES definition to acpi/pcc.h file in preparation to add subspace ID support for cppc_acpi driver. Signed-off-by: George Cherian Reviewed-by: Prashanth Prakash Signed-off-by: Rafael J. Wysocki --- drivers/mailbox/pcc.c | 1 - include/acpi/pcc.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index 9b7005e1345e..e5a69679cfa2 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -69,7 +69,6 @@ #include "mailbox.h" -#define MAX_PCC_SUBSPACES 256 #define MBOX_IRQ_NAME "pcc-mbox" static struct mbox_chan *pcc_mbox_channels; diff --git a/include/acpi/pcc.h b/include/acpi/pcc.h index 8caa79c61703..cd6ef45e614e 100644 --- a/include/acpi/pcc.h +++ b/include/acpi/pcc.h @@ -13,6 +13,7 @@ #include #include +#define MAX_PCC_SUBSPACES 256 #ifdef CONFIG_PCC extern struct mbox_chan *pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id); -- cgit v1.2.3-59-g8ed1b From 24a9332a58b7f41a0d36c35a2c6897242bffdbc0 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:43 -0500 Subject: net: dsa: constify cpu_dp member of dsa_port A DSA port has a dedicated CPU port assigned to it, stored in the cpu_dp member. It is not meant to be modified by a port, thus make it const. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 2 +- net/dsa/slave.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index e54332968417..2a8613b5a23d 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -190,7 +190,7 @@ struct dsa_port { struct dsa_switch *ds; unsigned int index; const char *name; - struct dsa_port *cpu_dp; + const struct dsa_port *cpu_dp; struct device_node *dn; unsigned int ageing_time; u8 stp_state; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 814ced75a0cc..cc7fe47dd4bf 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1147,7 +1147,7 @@ static void dsa_slave_notify(struct net_device *dev, unsigned long val) int dsa_slave_create(struct dsa_port *port) { - struct dsa_port *cpu_dp = port->cpu_dp; + const struct dsa_port *cpu_dp = port->cpu_dp; struct net_device *master = cpu_dp->master; struct dsa_switch *ds = port->ds; const char *name = port->name; -- cgit v1.2.3-59-g8ed1b From ec15dd4269d0cbf947c9a2dfdcf08a917098fab1 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Mon, 6 Nov 2017 16:11:46 -0500 Subject: net: dsa: setup and teardown tree This commit provides better scope for the DSA tree setup and teardown functions. It renames the "applied" bool to "setup" and print a message when the tree is setup, as it is done during teardown. At the same time, check dst->setup in dsa_tree_setup, where it is set to true. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 2 +- net/dsa/dsa2.c | 32 ++++++++++++++++++-------------- 2 files changed, 19 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/net/dsa.h b/include/net/dsa.h index 2a8613b5a23d..6c239257309b 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -122,7 +122,7 @@ struct dsa_switch_tree { struct kref refcount; /* Has this tree been applied to the hardware? */ - bool applied; + bool setup; /* * Configuration data for the platform device that owns diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index c9b50339fcac..1a8df0a177b5 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -486,12 +486,18 @@ static void dsa_tree_teardown_master(struct dsa_switch_tree *dst) return dsa_master_teardown(master); } -static int dsa_dst_apply(struct dsa_switch_tree *dst) +static int dsa_tree_setup(struct dsa_switch_tree *dst) { struct dsa_switch *ds; u32 index; int err; + if (dst->setup) { + pr_err("DSA: tree %d already setup! Disjoint trees?\n", + dst->index); + return -EEXIST; + } + err = dsa_tree_setup_default_cpu(dst); if (err) return err; @@ -510,17 +516,19 @@ static int dsa_dst_apply(struct dsa_switch_tree *dst) if (err) return err; - dst->applied = true; + dst->setup = true; + + pr_info("DSA: tree %d setup\n", dst->index); return 0; } -static void dsa_dst_unapply(struct dsa_switch_tree *dst) +static void dsa_tree_teardown(struct dsa_switch_tree *dst) { struct dsa_switch *ds; u32 index; - if (!dst->applied) + if (!dst->setup) return; dsa_tree_teardown_master(dst); @@ -535,8 +543,9 @@ static void dsa_dst_unapply(struct dsa_switch_tree *dst) dsa_tree_teardown_default_cpu(dst); - pr_info("DSA: tree %d unapplied\n", dst->index); - dst->applied = false; + pr_info("DSA: tree %d torn down\n", dst->index); + + dst->setup = false; } static void dsa_tree_remove_switch(struct dsa_switch_tree *dst, @@ -794,14 +803,9 @@ static int _dsa_register_switch(struct dsa_switch *ds) if (err == 1) return 0; - if (dst->applied) { - pr_info("DSA: Disjoint trees?\n"); - return -EINVAL; - } - - err = dsa_dst_apply(dst); + err = dsa_tree_setup(dst); if (err) { - dsa_dst_unapply(dst); + dsa_tree_teardown(dst); goto out_del_dst; } @@ -852,7 +856,7 @@ static void _dsa_unregister_switch(struct dsa_switch *ds) struct dsa_switch_tree *dst = ds->dst; unsigned int index = ds->index; - dsa_dst_unapply(dst); + dsa_tree_teardown(dst); dsa_tree_remove_switch(dst, index); } -- cgit v1.2.3-59-g8ed1b From 375ef2b1f0d0b43b0d36ffdd521637ff59b0c13c Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Sun, 17 Sep 2017 13:43:58 +0300 Subject: net: Introduce netdev_*_once functions Extend the net device error logging with netdev_*_once macros. netdev_*_once are the equivalents of the dev_*_once macros which are useful for messages that should only be logged once. Also add netdev_WARN_ONCE, which is the "once" extension for the already existing netdev_WARN macro. Signed-off-by: Gal Pressman Signed-off-by: Saeed Mahameed --- include/linux/netdevice.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'include') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 30f0f2928808..79518ede3170 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -4336,6 +4336,31 @@ void netdev_notice(const struct net_device *dev, const char *format, ...); __printf(2, 3) void netdev_info(const struct net_device *dev, const char *format, ...); +#define netdev_level_once(level, dev, fmt, ...) \ +do { \ + static bool __print_once __read_mostly; \ + \ + if (!__print_once) { \ + __print_once = true; \ + netdev_printk(level, dev, fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +#define netdev_emerg_once(dev, fmt, ...) \ + netdev_level_once(KERN_EMERG, dev, fmt, ##__VA_ARGS__) +#define netdev_alert_once(dev, fmt, ...) \ + netdev_level_once(KERN_ALERT, dev, fmt, ##__VA_ARGS__) +#define netdev_crit_once(dev, fmt, ...) \ + netdev_level_once(KERN_CRIT, dev, fmt, ##__VA_ARGS__) +#define netdev_err_once(dev, fmt, ...) \ + netdev_level_once(KERN_ERR, dev, fmt, ##__VA_ARGS__) +#define netdev_warn_once(dev, fmt, ...) \ + netdev_level_once(KERN_WARNING, dev, fmt, ##__VA_ARGS__) +#define netdev_notice_once(dev, fmt, ...) \ + netdev_level_once(KERN_NOTICE, dev, fmt, ##__VA_ARGS__) +#define netdev_info_once(dev, fmt, ...) \ + netdev_level_once(KERN_INFO, dev, fmt, ##__VA_ARGS__) + #define MODULE_ALIAS_NETDEV(device) \ MODULE_ALIAS("netdev-" device) @@ -4376,6 +4401,10 @@ do { \ WARN(1, "netdevice: %s%s\n" format, netdev_name(dev), \ netdev_reg_state(dev), ##args) +#define netdev_WARN_ONCE(dev, condition, format, arg...) \ + WARN_ONCE(1, "netdevice: %s%s\n" format, netdev_name(dev) \ + netdev_reg_state(dev), ##args) + /* netif printk helpers, similar to netdev_printk */ #define netif_printk(priv, type, level, dev, fmt, args...) \ -- cgit v1.2.3-59-g8ed1b From 4382c7b92a1db397874ca62c73aa8b023af6dba8 Mon Sep 17 00:00:00 2001 From: Gal Pressman Date: Sun, 10 Sep 2017 13:22:51 +0300 Subject: net/mlx5e: Add 802.1ad VLAN insertion support Report VLAN insertion support for S-tagged packets and add support by choosing the correct VLAN type in the WQE. Signed-off-by: Gal Pressman Reviewed-by: Tariq Toukan Signed-off-by: Saeed Mahameed --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_tx.c | 2 ++ include/linux/mlx5/qp.h | 1 + 3 files changed, 4 insertions(+) (limited to 'include') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 59b8a2d62b8d..c408b7efa42e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -4194,6 +4194,7 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX; netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + netdev->hw_features |= NETIF_F_HW_VLAN_STAG_TX; if (mlx5e_vxlan_allowed(mdev) || MLX5_CAP_ETH(mdev, tunnel_stateless_gre)) { netdev->hw_features |= NETIF_F_GSO_PARTIAL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index de651de35c9b..c62305b214cc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -369,6 +369,8 @@ static netdev_tx_t mlx5e_sq_xmit(struct mlx5e_txqsq *sq, struct sk_buff *skb, ds_cnt += DIV_ROUND_UP(ihs - sizeof(eseg->inline_hdr.start), MLX5_SEND_WQE_DS); } else if (skb_vlan_tag_present(skb)) { eseg->insert.type = cpu_to_be16(MLX5_ETH_WQE_INSERT_VLAN); + if (skb->vlan_proto == cpu_to_be16(ETH_P_8021AD)) + eseg->insert.type |= cpu_to_be16(MLX5_ETH_WQE_SVLAN); eseg->insert.vlan_tci = cpu_to_be16(skb_vlan_tag_get(skb)); } diff --git a/include/linux/mlx5/qp.h b/include/linux/mlx5/qp.h index 66d19b611fe4..62af7512dabb 100644 --- a/include/linux/mlx5/qp.h +++ b/include/linux/mlx5/qp.h @@ -221,6 +221,7 @@ enum { }; enum { + MLX5_ETH_WQE_SVLAN = 1 << 0, MLX5_ETH_WQE_INSERT_VLAN = 1 << 15, }; -- cgit v1.2.3-59-g8ed1b From f7a6509fe002e3909cb41c09e807b7f3ca4a361b Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Fri, 1 Sep 2017 17:11:43 +0200 Subject: KVM: s390: vsie: use common code functions for pinning We will not see -ENOMEM (gfn_to_hva() will return KVM_ERR_PTR_BAD_PAGE for all errors). So we can also get rid of special handling in the callers of pin_guest_page() and always assume that it is a g2 error. As also kvm_s390_inject_program_int() should never fail, we can simplify pin_scb(), too. Signed-off-by: David Hildenbrand Message-Id: <20170901151143.22714-1-david@redhat.com> Acked-by: Cornelia Huck Signed-off-by: Christian Borntraeger --- arch/s390/kvm/vsie.c | 50 +++++++++++++++++------------------------------- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 4 ++-- 3 files changed, 21 insertions(+), 34 deletions(-) (limited to 'include') diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index b18b5652e5c5..a311938b63b3 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -443,22 +443,14 @@ static int map_prefix(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) * * Returns: - 0 on success * - -EINVAL if the gpa is not valid guest storage - * - -ENOMEM if out of memory */ static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa) { struct page *page; - hva_t hva; - int rc; - hva = gfn_to_hva(kvm, gpa_to_gfn(gpa)); - if (kvm_is_error_hva(hva)) + page = gfn_to_page(kvm, gpa_to_gfn(gpa)); + if (is_error_page(page)) return -EINVAL; - rc = get_user_pages_fast(hva, 1, 1, &page); - if (rc < 0) - return rc; - else if (rc != 1) - return -ENOMEM; *hpa = (hpa_t) page_to_virt(page) + (gpa & ~PAGE_MASK); return 0; } @@ -466,11 +458,7 @@ static int pin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t *hpa) /* Unpins a page previously pinned via pin_guest_page, marking it as dirty. */ static void unpin_guest_page(struct kvm *kvm, gpa_t gpa, hpa_t hpa) { - struct page *page; - - page = virt_to_page(hpa); - set_page_dirty_lock(page); - put_page(page); + kvm_release_pfn_dirty(hpa >> PAGE_SHIFT); /* mark the page always as dirty for migration */ mark_page_dirty(kvm, gpa_to_gfn(gpa)); } @@ -557,7 +545,7 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) rc = set_validity_icpt(scb_s, 0x003bU); if (!rc) { rc = pin_guest_page(vcpu->kvm, gpa, &hpa); - if (rc == -EINVAL) + if (rc) rc = set_validity_icpt(scb_s, 0x0034U); } if (rc) @@ -574,10 +562,10 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) } /* 256 bytes cannot cross page boundaries */ rc = pin_guest_page(vcpu->kvm, gpa, &hpa); - if (rc == -EINVAL) + if (rc) { rc = set_validity_icpt(scb_s, 0x0080U); - if (rc) goto unpin; + } scb_s->itdba = hpa; } @@ -592,10 +580,10 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) * if this block gets bigger, we have to shadow it. */ rc = pin_guest_page(vcpu->kvm, gpa, &hpa); - if (rc == -EINVAL) + if (rc) { rc = set_validity_icpt(scb_s, 0x1310U); - if (rc) goto unpin; + } scb_s->gvrd = hpa; } @@ -607,11 +595,11 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) } /* 64 bytes cannot cross page boundaries */ rc = pin_guest_page(vcpu->kvm, gpa, &hpa); - if (rc == -EINVAL) + if (rc) { rc = set_validity_icpt(scb_s, 0x0043U); - /* Validity 0x0044 will be checked by SIE */ - if (rc) goto unpin; + } + /* Validity 0x0044 will be checked by SIE */ scb_s->riccbd = hpa; } if ((scb_s->ecb & ECB_GS) && !(scb_s->ecd & ECD_HOSTREGMGMT)) { @@ -635,10 +623,10 @@ static int pin_blocks(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) * cross page boundaries */ rc = pin_guest_page(vcpu->kvm, gpa, &hpa); - if (rc == -EINVAL) + if (rc) { rc = set_validity_icpt(scb_s, 0x10b0U); - if (rc) goto unpin; + } scb_s->sdnxo = hpa | sdnxc; } return 0; @@ -663,7 +651,6 @@ static void unpin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, * * Returns: - 0 if the scb was pinned. * - > 0 if control has to be given to guest 2 - * - -ENOMEM if out of memory */ static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, gpa_t gpa) @@ -672,14 +659,13 @@ static int pin_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, int rc; rc = pin_guest_page(vcpu->kvm, gpa, &hpa); - if (rc == -EINVAL) { + if (rc) { rc = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); - if (!rc) - rc = 1; + WARN_ON_ONCE(rc); + return 1; } - if (!rc) - vsie_page->scb_o = (struct kvm_s390_sie_block *) hpa; - return rc; + vsie_page->scb_o = (struct kvm_s390_sie_block *) hpa; + return 0; } /* diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 6882538eda32..2e754b7c282c 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -667,6 +667,7 @@ kvm_pfn_t __gfn_to_pfn_memslot(struct kvm_memory_slot *slot, gfn_t gfn, bool *writable); void kvm_release_pfn_clean(kvm_pfn_t pfn); +void kvm_release_pfn_dirty(kvm_pfn_t pfn); void kvm_set_pfn_dirty(kvm_pfn_t pfn); void kvm_set_pfn_accessed(kvm_pfn_t pfn); void kvm_get_pfn(kvm_pfn_t pfn); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 9deb5a245b83..37731f661be5 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -122,7 +122,6 @@ static void hardware_disable_all(void); static void kvm_io_bus_destroy(struct kvm_io_bus *bus); -static void kvm_release_pfn_dirty(kvm_pfn_t pfn); static void mark_page_dirty_in_slot(struct kvm_memory_slot *memslot, gfn_t gfn); __visible bool kvm_rebooting; @@ -1679,11 +1678,12 @@ void kvm_release_page_dirty(struct page *page) } EXPORT_SYMBOL_GPL(kvm_release_page_dirty); -static void kvm_release_pfn_dirty(kvm_pfn_t pfn) +void kvm_release_pfn_dirty(kvm_pfn_t pfn) { kvm_set_pfn_dirty(pfn); kvm_release_pfn_clean(pfn); } +EXPORT_SYMBOL_GPL(kvm_release_pfn_dirty); void kvm_set_pfn_dirty(kvm_pfn_t pfn) { -- cgit v1.2.3-59-g8ed1b From f121aadede37bcbb77ea552d195a09c734486fe7 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 9 Nov 2017 10:23:28 +0100 Subject: vfs: add path_put_init() This one initializes the struct path to all zeroes so that multiple path_put_init() on the path is safe. Signed-off-by: Miklos Szeredi --- include/linux/path.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/path.h b/include/linux/path.h index 81e65a5be7ce..475225a03d0d 100644 --- a/include/linux/path.h +++ b/include/linux/path.h @@ -18,4 +18,10 @@ static inline int path_equal(const struct path *path1, const struct path *path2) return path1->mnt == path2->mnt && path1->dentry == path2->dentry; } +static inline void path_put_init(struct path *path) +{ + path_put(path); + *path = (struct path) { }; +} + #endif /* _LINUX_PATH_H */ -- cgit v1.2.3-59-g8ed1b From 9e429d564926d3bca49907fa03031da705ad6f2c Mon Sep 17 00:00:00 2001 From: Jason Gerecke Date: Tue, 7 Nov 2017 08:25:17 -0800 Subject: HID: wacom: generic: Send BTN_STYLUS3 when both barrel switches are set The Wacom Pro Pen 3D includes a third barrel switch which is intended to be particularly useful in applications where one frequency uses pan, zoom, and rotate to navigate around a scene or model. The pen is compatible with the MobileStudio Pro, 2nd-gen Intuos Pro, and Cintiq Pro. When the third button is pressed, these devices set both the HID_DG_BARRELSWITCH and HID_DG_BARRELSWITCH2 usages since their HID descriptors do not include a usage specific to the button. Rather than send both BTN_STYLUS and BTN_STYLUS2 when the third button is pressed, userspace (libinput) has requested that we detect this condition and report a newly-defined BTN_STYLUS3 event instead. We could define a quirk specific to devices compatible with the Pro Pen 3D, but the liklihood of seeing both barrel switch bits set with other pens/devices is low enough to not worry about (pens mechanically prevent accidental activation of multiple switches). Signed-off-by: Jason Gerecke Acked-by: Dmitry Torokhov Acked-by: Peter Hutterer Acked-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/wacom_wac.c | 18 ++++++++++++++++-- drivers/hid/wacom_wac.h | 2 ++ include/uapi/linux/input-event-codes.h | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index e3223b0c4f90..16af6886e828 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -2140,6 +2140,12 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field case HID_DG_TIPSWITCH: wacom_wac->hid_data.tipswitch |= value; return; + case HID_DG_BARRELSWITCH: + wacom_wac->hid_data.barrelswitch = value; + return; + case HID_DG_BARRELSWITCH2: + wacom_wac->hid_data.barrelswitch2 = value; + return; case HID_DG_TOOLSERIALNUMBER: if (value) { wacom_wac->serial[0] = (wacom_wac->serial[0] & ~0xFFFFFFFFULL); @@ -2254,6 +2260,12 @@ static void wacom_wac_pen_report(struct hid_device *hdev, if (!delay_pen_events(wacom_wac) && wacom_wac->tool[0]) { int id = wacom_wac->id[0]; + int sw_state = wacom_wac->hid_data.barrelswitch | + (wacom_wac->hid_data.barrelswitch2 << 1); + + input_report_key(input, BTN_STYLUS, sw_state == 1); + input_report_key(input, BTN_STYLUS2, sw_state == 2); + input_report_key(input, BTN_STYLUS3, sw_state == 3); /* * Non-USI EMR tools should have their IDs mangled to @@ -3300,9 +3312,11 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev, else __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - if (features->type == HID_GENERIC) - /* setup has already been done */ + if (features->type == HID_GENERIC) { + /* setup has already been done; apply otherwise-undetectible quirks */ + input_set_capability(input_dev, EV_KEY, BTN_STYLUS3); return 0; + } __set_bit(BTN_TOUCH, input_dev->keybit); __set_bit(ABS_MISC, input_dev->absbit); diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index 8a03654048bf..69dda27e8dde 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -291,6 +291,8 @@ struct hid_data { bool inrange_state; bool invert_state; bool tipswitch; + bool barrelswitch; + bool barrelswitch2; int x; int y; int pressure; diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h index 179891074b3c..9b3a522f50d1 100644 --- a/include/uapi/linux/input-event-codes.h +++ b/include/uapi/linux/input-event-codes.h @@ -406,6 +406,7 @@ #define BTN_TOOL_MOUSE 0x146 #define BTN_TOOL_LENS 0x147 #define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */ +#define BTN_STYLUS3 0x149 #define BTN_TOUCH 0x14a #define BTN_STYLUS 0x14b #define BTN_STYLUS2 0x14c -- cgit v1.2.3-59-g8ed1b From da9a1446d248f673a8560ce46251ff620214ab7b Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Thu, 9 Nov 2017 10:00:45 +0100 Subject: KVM: s390: provide a capability for AIS state migration The AIS capability was introduced in 4.12, while the interface to migrate the state was added in 4.13. Unfortunately it is not possible for userspace to detect the migration capability without creating a flic kvm device. As in QEMU the cpu model detection runs on the "none" machine this will result in cpu model issues regarding the "ais" capability. To get the "ais" capability properly let's add a new KVM capability that tells userspace that AIS states can be migrated. Signed-off-by: Christian Borntraeger Reviewed-by: Cornelia Huck Reviewed-by: David Hildenbrand Acked-by: Halil Pasic --- Documentation/virtual/kvm/api.txt | 9 +++++++++ Documentation/virtual/kvm/devices/s390_flic.txt | 2 ++ arch/s390/kvm/kvm-s390.c | 1 + include/uapi/linux/kvm.h | 1 + 4 files changed, 13 insertions(+) (limited to 'include') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index e63a35fafef0..49540e53c4bd 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -4347,3 +4347,12 @@ This capability indicates that userspace can load HV_X64_MSR_VP_INDEX msr. Its value is used to denote the target vcpu for a SynIC interrupt. For compatibilty, KVM initializes this msr to KVM's internal vcpu index. When this capability is absent, userspace can still query this msr's value. + +8.13 KVM_CAP_S390_AIS_MIGRATION + +Architectures: s390 +Parameters: none + +This capability indicates if the flic device will be able to get/set the +AIS states for migration via the KVM_DEV_FLIC_AISM_ALL attribute and allows +to discover this without having to create a flic device. diff --git a/Documentation/virtual/kvm/devices/s390_flic.txt b/Documentation/virtual/kvm/devices/s390_flic.txt index 27ad53c7149d..a4e20a090174 100644 --- a/Documentation/virtual/kvm/devices/s390_flic.txt +++ b/Documentation/virtual/kvm/devices/s390_flic.txt @@ -151,6 +151,8 @@ struct kvm_s390_ais_all { to an ISC (MSB0 bit 0 to ISC 0 and so on). The combination of simm bit and nimm bit presents AIS mode for a ISC. + KVM_DEV_FLIC_AISM_ALL is indicated by KVM_CAP_S390_AIS_MIGRATION. + Note: The KVM_SET_DEVICE_ATTR/KVM_GET_DEVICE_ATTR device ioctls executed on FLIC with an unknown group or attribute gives the error code EINVAL (instead of ENXIO, as specified in the API documentation). It is not possible to conclude diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index de6a5b790da0..8f4b655f65d7 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -395,6 +395,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_S390_USER_INSTR0: case KVM_CAP_S390_CMMA_MIGRATION: case KVM_CAP_S390_AIS: + case KVM_CAP_S390_AIS_MIGRATION: r = 1; break; case KVM_CAP_S390_MEM_OP: diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 838887587411..b60595696836 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -930,6 +930,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_PPC_SMT_POSSIBLE 147 #define KVM_CAP_HYPERV_SYNIC2 148 #define KVM_CAP_HYPERV_VP_INDEX 149 +#define KVM_CAP_S390_AIS_MIGRATION 150 #ifdef KVM_CAP_IRQ_ROUTING -- cgit v1.2.3-59-g8ed1b From 1e7d4beba2f5c8d22c46e15d3b89559934356501 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Thu, 9 Nov 2017 18:09:30 +0100 Subject: video: fbdev: remove dead igafb driver igafb driver hasn't compiled since at least kernel v2.6.34 as commit 6016a363f6b5 ("of: unify phandle name in struct device_node") missed updating igafb.c to use dp->phandle instead of dp->node. Cc: "David S. Miller" Cc: Bhumika Goyal Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/video/fbdev/Kconfig | 10 - drivers/video/fbdev/Makefile | 1 - drivers/video/fbdev/igafb.c | 579 ------------------------------------------- include/video/iga.h | 24 -- 4 files changed, 614 deletions(-) delete mode 100644 drivers/video/fbdev/igafb.c delete mode 100644 include/video/iga.h (limited to 'include') diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 5e58f5ec0a28..2f615b7f1c9f 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -905,16 +905,6 @@ config FB_LEO This is the frame buffer device driver for the SBUS-based Sun ZX (leo) frame buffer cards. -config FB_IGA - bool "IGA 168x display support" - depends on (FB = y) && SPARC32 - select FB_CFB_FILLRECT - select FB_CFB_COPYAREA - select FB_CFB_IMAGEBLIT - help - This is the framebuffer device for the INTERGRAPHICS 1680 and - successor frame buffer cards. - config FB_XVR500 bool "Sun XVR-500 3DLABS Wildcat support" depends on (FB = y) && PCI && SPARC64 diff --git a/drivers/video/fbdev/Makefile b/drivers/video/fbdev/Makefile index ee8c81405a7f..deb76674e5e9 100644 --- a/drivers/video/fbdev/Makefile +++ b/drivers/video/fbdev/Makefile @@ -64,7 +64,6 @@ obj-$(CONFIG_FB_HGA) += hgafb.o obj-$(CONFIG_FB_XVR500) += sunxvr500.o obj-$(CONFIG_FB_XVR2500) += sunxvr2500.o obj-$(CONFIG_FB_XVR1000) += sunxvr1000.o -obj-$(CONFIG_FB_IGA) += igafb.o obj-$(CONFIG_FB_APOLLO) += dnfb.o obj-$(CONFIG_FB_Q40) += q40fb.o obj-$(CONFIG_FB_TGA) += tgafb.o diff --git a/drivers/video/fbdev/igafb.c b/drivers/video/fbdev/igafb.c deleted file mode 100644 index 486f18897414..000000000000 --- a/drivers/video/fbdev/igafb.c +++ /dev/null @@ -1,579 +0,0 @@ -/* - * linux/drivers/video/igafb.c -- Frame buffer device for IGA 1682 - * - * Copyright (C) 1998 Vladimir Roganov and Gleb Raiko - * - * This driver is partly based on the Frame buffer device for ATI Mach64 - * and partially on VESA-related code. - * - * Copyright (C) 1997-1998 Geert Uytterhoeven - * Copyright (C) 1998 Bernd Harries - * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive for - * more details. - */ - -/****************************************************************************** - - TODO: - Despite of IGA Card has advanced graphic acceleration, - initial version is almost dummy and does not support it. - Support for video modes and acceleration must be added - together with accelerated X-Windows driver implementation. - - Most important thing at this moment is that we have working - JavaEngine1 console & X with new console interface. - -******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef CONFIG_SPARC -#include -#include -#endif - -#include