diff options
Diffstat (limited to 'drivers')
871 files changed, 19972 insertions, 6758 deletions
diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index fc6c416f8724..d5999eb41c00 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -180,8 +180,8 @@ static const struct acpi_device_id acpi_apd_device_ids[] = { { "APMC0D0F", APD_ADDR(xgene_i2c_desc) }, { "BRCM900D", APD_ADDR(vulcan_spi_desc) }, { "CAV900D", APD_ADDR(vulcan_spi_desc) }, - { "HISI0A21", APD_ADDR(hip07_i2c_desc) }, - { "HISI0A22", APD_ADDR(hip08_i2c_desc) }, + { "HISI02A1", APD_ADDR(hip07_i2c_desc) }, + { "HISI02A2", APD_ADDR(hip08_i2c_desc) }, #endif { } }; diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index e51a1e98e62f..f88caf5aab76 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -85,6 +85,7 @@ static const struct lpss_device_desc lpss_dma_desc = { }; struct lpss_private_data { + struct acpi_device *adev; void __iomem *mmio_base; resource_size_t mmio_size; unsigned int fixed_clk_rate; @@ -155,6 +156,12 @@ static struct pwm_lookup byt_pwm_lookup[] = { static void byt_pwm_setup(struct lpss_private_data *pdata) { + struct acpi_device *adev = pdata->adev; + + /* Only call pwm_add_table for the first PWM controller */ + if (!adev->pnp.unique_id || strcmp(adev->pnp.unique_id, "1")) + return; + if (!acpi_dev_present("INT33FD", NULL, -1)) pwm_add_table(byt_pwm_lookup, ARRAY_SIZE(byt_pwm_lookup)); } @@ -180,6 +187,12 @@ static struct pwm_lookup bsw_pwm_lookup[] = { static void bsw_pwm_setup(struct lpss_private_data *pdata) { + struct acpi_device *adev = pdata->adev; + + /* Only call pwm_add_table for the first PWM controller */ + if (!adev->pnp.unique_id || strcmp(adev->pnp.unique_id, "1")) + return; + pwm_add_table(bsw_pwm_lookup, ARRAY_SIZE(bsw_pwm_lookup)); } @@ -456,6 +469,7 @@ static int acpi_lpss_create_device(struct acpi_device *adev, goto err_out; } + pdata->adev = adev; pdata->dev_desc = dev_desc; if (dev_desc->setup) diff --git a/drivers/acpi/acpi_watchdog.c b/drivers/acpi/acpi_watchdog.c index 8c4e0a18460a..bf22c29d2517 100644 --- a/drivers/acpi/acpi_watchdog.c +++ b/drivers/acpi/acpi_watchdog.c @@ -86,7 +86,12 @@ void __init acpi_watchdog_init(void) found = false; resource_list_for_each_entry(rentry, &resource_list) { - if (resource_contains(rentry->res, &res)) { + if (rentry->res->flags == res.flags && + resource_overlaps(rentry->res, &res)) { + if (res.start < rentry->res->start) + rentry->res->start = res.start; + if (res.end > rentry->res->end) + rentry->res->end = res.end; found = true; break; } diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index 538c61677c10..783f4c838aee 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -100,9 +100,13 @@ acpi_evaluate_object_typed(acpi_handle handle, free_buffer_on_error = TRUE; } - status = acpi_get_handle(handle, pathname, &target_handle); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); + if (pathname) { + status = acpi_get_handle(handle, pathname, &target_handle); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + } else { + target_handle = handle; } full_pathname = acpi_ns_get_external_pathname(target_handle); diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index ddb01e9fa5b2..ae3d6d152633 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -151,6 +151,10 @@ static bool ec_freeze_events __read_mostly = false; module_param(ec_freeze_events, bool, 0644); MODULE_PARM_DESC(ec_freeze_events, "Disabling event handling during suspend/resume"); +static bool ec_no_wakeup __read_mostly; +module_param(ec_no_wakeup, bool, 0644); +MODULE_PARM_DESC(ec_no_wakeup, "Do not wake up from suspend-to-idle"); + struct acpi_ec_query_handler { struct list_head node; acpi_ec_query_func func; @@ -535,6 +539,14 @@ static void acpi_ec_disable_event(struct acpi_ec *ec) spin_unlock_irqrestore(&ec->lock, flags); __acpi_ec_flush_event(ec); } + +void acpi_ec_flush_work(void) +{ + if (first_ec) + __acpi_ec_flush_event(first_ec); + + flush_scheduled_work(); +} #endif /* CONFIG_PM_SLEEP */ static bool acpi_ec_guard_event(struct acpi_ec *ec) @@ -1729,7 +1741,7 @@ error: * functioning ECDT EC first in order to handle the events. * https://bugzilla.kernel.org/show_bug.cgi?id=115021 */ -int __init acpi_ec_ecdt_start(void) +static int __init acpi_ec_ecdt_start(void) { acpi_handle handle; @@ -1880,6 +1892,32 @@ static int acpi_ec_suspend(struct device *dev) return 0; } +static int acpi_ec_suspend_noirq(struct device *dev) +{ + struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); + + /* + * The SCI handler doesn't run at this point, so the GPE can be + * masked at the low level without side effects. + */ + if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) && + ec->reference_count >= 1) + acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); + + return 0; +} + +static int acpi_ec_resume_noirq(struct device *dev) +{ + struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); + + if (ec_no_wakeup && test_bit(EC_FLAGS_STARTED, &ec->flags) && + ec->reference_count >= 1) + acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); + + return 0; +} + static int acpi_ec_resume(struct device *dev) { struct acpi_ec *ec = @@ -1891,6 +1929,7 @@ static int acpi_ec_resume(struct device *dev) #endif static const struct dev_pm_ops acpi_ec_pm = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend_noirq, acpi_ec_resume_noirq) SET_SYSTEM_SLEEP_PM_OPS(acpi_ec_suspend, acpi_ec_resume) }; @@ -1964,20 +2003,17 @@ static inline void acpi_ec_query_exit(void) int __init acpi_ec_init(void) { int result; + int ecdt_fail, dsdt_fail; /* register workqueue for _Qxx evaluations */ result = acpi_ec_query_init(); if (result) - goto err_exit; - /* Now register the driver for the EC */ - result = acpi_bus_register_driver(&acpi_ec_driver); - if (result) - goto err_exit; + return result; -err_exit: - if (result) - acpi_ec_query_exit(); - return result; + /* Drivers must be started after acpi_ec_query_init() */ + ecdt_fail = acpi_ec_ecdt_start(); + dsdt_fail = acpi_bus_register_driver(&acpi_ec_driver); + return ecdt_fail && dsdt_fail ? -ENODEV : 0; } /* EC driver currently not unloadable */ diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 9531d3276f65..3f5af4d7a739 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -185,7 +185,6 @@ typedef int (*acpi_ec_query_func) (void *data); int acpi_ec_init(void); int acpi_ec_ecdt_probe(void); int acpi_ec_dsdt_probe(void); -int acpi_ec_ecdt_start(void); void acpi_ec_block_transactions(void); void acpi_ec_unblock_transactions(void); int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, @@ -193,6 +192,10 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, void *data); void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); +#ifdef CONFIG_PM_SLEEP +void acpi_ec_flush_work(void); +#endif + /*-------------------------------------------------------------------------- Suspend/Resume diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index b75b734ee73a..19182d091587 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3160,6 +3160,8 @@ static struct acpi_driver acpi_nfit_driver = { static __init int nfit_init(void) { + int ret; + BUILD_BUG_ON(sizeof(struct acpi_table_nfit) != 40); BUILD_BUG_ON(sizeof(struct acpi_nfit_system_address) != 56); BUILD_BUG_ON(sizeof(struct acpi_nfit_memory_map) != 48); @@ -3187,8 +3189,14 @@ static __init int nfit_init(void) return -ENOMEM; nfit_mce_register(); + ret = acpi_bus_register_driver(&acpi_nfit_driver); + if (ret) { + nfit_mce_unregister(); + destroy_workqueue(nfit_wq); + } + + return ret; - return acpi_bus_register_driver(&acpi_nfit_driver); } static __exit void nfit_exit(void) diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index edb0c79f7c64..917f1cc0fda4 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -443,7 +443,7 @@ int __init acpi_numa_init(void) * So go over all cpu entries in SRAT to get apicid to node mapping. */ - /* SRAT: Static Resource Affinity Table */ + /* SRAT: System Resource Affinity Table */ if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) { struct acpi_subtable_proc srat_proc[3]; diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 917c789f953d..476a52c60cf3 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -1047,7 +1047,7 @@ static struct fwnode_handle *acpi_graph_get_child_prop_value( fwnode_for_each_child_node(fwnode, child) { u32 nr; - if (!fwnode_property_read_u32(fwnode, prop_name, &nr)) + if (fwnode_property_read_u32(child, prop_name, &nr)) continue; if (val == nr) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 33897298f03e..70fd5502c284 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2084,7 +2084,6 @@ int __init acpi_scan_init(void) acpi_gpe_apply_masked_gpes(); acpi_update_all_gpes(); - acpi_ec_ecdt_start(); acpi_scan_initialized = true; diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index be17664736b2..fa8243c5c062 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -777,11 +777,11 @@ static void acpi_freeze_sync(void) /* * Process all pending events in case there are any wakeup ones. * - * The EC driver uses the system workqueue, so that one needs to be - * flushed too. + * The EC driver uses the system workqueue and an additional special + * one, so those need to be flushed too. */ + acpi_ec_flush_work(); acpi_os_wait_events_complete(); - flush_scheduled_work(); s2idle_wakeup = false; } diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c index 4ac3e06b41d8..98aa8c808a33 100644 --- a/drivers/acpi/spcr.c +++ b/drivers/acpi/spcr.c @@ -17,6 +17,16 @@ #include <linux/serial_core.h> /* + * Erratum 44 for QDF2432v1 and QDF2400v1 SoCs describes the BUSY bit as + * occasionally getting stuck as 1. To avoid the potential for a hang, check + * TXFE == 0 instead of BUSY == 1. This may not be suitable for all UART + * implementations, so only do so if an affected platform is detected in + * parse_spcr(). + */ +bool qdf2400_e44_present; +EXPORT_SYMBOL(qdf2400_e44_present); + +/* * Some Qualcomm Datacenter Technologies SoCs have a defective UART BUSY bit. * Detect them by examining the OEM fields in the SPCR header, similiar to PCI * quirk detection in pci_mcfg.c. @@ -147,8 +157,30 @@ int __init parse_spcr(bool earlycon) goto done; } - if (qdf2400_erratum_44_present(&table->header)) - uart = "qdf2400_e44"; + /* + * If the E44 erratum is required, then we need to tell the pl011 + * driver to implement the work-around. + * + * The global variable is used by the probe function when it + * creates the UARTs, whether or not they're used as a console. + * + * If the user specifies "traditional" earlycon, the qdf2400_e44 + * console name matches the EARLYCON_DECLARE() statement, and + * SPCR is not used. Parameter "earlycon" is false. + * + * If the user specifies "SPCR" earlycon, then we need to update + * the console name so that it also says "qdf2400_e44". Parameter + * "earlycon" is true. + * + * For consistency, if we change the console name, then we do it + * for everyone, not just earlycon. + */ + if (qdf2400_erratum_44_present(&table->header)) { + qdf2400_e44_present = true; + if (earlycon) + uart = "qdf2400_e44"; + } + if (xgene_8250_erratum_present(table)) iotype = "mmio32"; diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index 832e885349b1..9801d852bd56 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -22,7 +22,7 @@ config ANDROID_BINDER_IPC config ANDROID_BINDER_DEVICES string "Android Binder devices" depends on ANDROID_BINDER_IPC - default "binder,hwbinder" + default "binder,hwbinder,vndbinder" ---help--- Default value for the binder.devices parameter. @@ -32,7 +32,7 @@ config ANDROID_BINDER_DEVICES therefore logically separated from the other devices. config ANDROID_BINDER_IPC_32BIT - bool + bool "Use old (Android 4.4 and earlier) 32-bit binder API" depends on !64BIT && ANDROID_BINDER_IPC default y ---help--- @@ -44,6 +44,16 @@ config ANDROID_BINDER_IPC_32BIT Note that enabling this will break newer Android user-space. +config ANDROID_BINDER_IPC_SELFTEST + bool "Android Binder IPC Driver Selftest" + depends on ANDROID_BINDER_IPC + ---help--- + This feature allows binder selftest to run. + + Binder selftest checks the allocation and free of binder buffers + exhaustively with combinations of various buffer sizes and + alignments. + endif # if ANDROID endmenu diff --git a/drivers/android/Makefile b/drivers/android/Makefile index 3b7e4b072c58..a01254c43ee3 100644 --- a/drivers/android/Makefile +++ b/drivers/android/Makefile @@ -1,3 +1,4 @@ ccflags-y += -I$(src) # needed for trace events -obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o +obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o binder_alloc.o +obj-$(CONFIG_ANDROID_BINDER_IPC_SELFTEST) += binder_alloc_selftest.o diff --git a/drivers/android/binder.c b/drivers/android/binder.c index aae4d8d4be36..ba9e613b42d6 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -15,6 +15,40 @@ * */ +/* + * Locking overview + * + * There are 3 main spinlocks which must be acquired in the + * order shown: + * + * 1) proc->outer_lock : protects binder_ref + * binder_proc_lock() and binder_proc_unlock() are + * used to acq/rel. + * 2) node->lock : protects most fields of binder_node. + * binder_node_lock() and binder_node_unlock() are + * used to acq/rel + * 3) proc->inner_lock : protects the thread and node lists + * (proc->threads, proc->nodes) and all todo lists associated + * with the binder_proc (proc->todo, thread->todo, + * proc->delivered_death and node->async_todo), as well as + * thread->transaction_stack + * binder_inner_proc_lock() and binder_inner_proc_unlock() + * are used to acq/rel + * + * Any lock under procA must never be nested under any lock at the same + * level or below on procB. + * + * Functions that require a lock held on entry indicate which lock + * in the suffix of the function name: + * + * foo_olocked() : requires node->outer_lock + * foo_nlocked() : requires node->lock + * foo_ilocked() : requires proc->inner_lock + * foo_oilocked(): requires proc->outer_lock and proc->inner_lock + * foo_nilocked(): requires node->lock and proc->inner_lock + * ... + */ + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <asm/cacheflush.h> @@ -24,7 +58,6 @@ #include <linux/fs.h> #include <linux/list.h> #include <linux/miscdevice.h> -#include <linux/mm.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/nsproxy.h> @@ -35,30 +68,31 @@ #include <linux/sched/mm.h> #include <linux/seq_file.h> #include <linux/uaccess.h> -#include <linux/vmalloc.h> -#include <linux/slab.h> #include <linux/pid_namespace.h> #include <linux/security.h> +#include <linux/spinlock.h> #ifdef CONFIG_ANDROID_BINDER_IPC_32BIT #define BINDER_IPC_32BIT 1 #endif #include <uapi/linux/android/binder.h> +#include "binder_alloc.h" #include "binder_trace.h" -static DEFINE_MUTEX(binder_main_lock); +static HLIST_HEAD(binder_deferred_list); static DEFINE_MUTEX(binder_deferred_lock); -static DEFINE_MUTEX(binder_mmap_lock); static HLIST_HEAD(binder_devices); static HLIST_HEAD(binder_procs); -static HLIST_HEAD(binder_deferred_list); +static DEFINE_MUTEX(binder_procs_lock); + static HLIST_HEAD(binder_dead_nodes); +static DEFINE_SPINLOCK(binder_dead_nodes_lock); static struct dentry *binder_debugfs_dir_entry_root; static struct dentry *binder_debugfs_dir_entry_proc; -static int binder_last_id; +static atomic_t binder_last_id; #define BINDER_DEBUG_ENTRY(name) \ static int binder_##name##_open(struct inode *inode, struct file *file) \ @@ -88,8 +122,6 @@ BINDER_DEBUG_ENTRY(proc); #define FORBIDDEN_MMAP_FLAGS (VM_WRITE) -#define BINDER_SMALL_BUF_SIZE (PAGE_SIZE * 64) - enum { BINDER_DEBUG_USER_ERROR = 1U << 0, BINDER_DEBUG_FAILED_TRANSACTION = 1U << 1, @@ -104,17 +136,13 @@ enum { BINDER_DEBUG_TRANSACTION_COMPLETE = 1U << 10, BINDER_DEBUG_FREE_BUFFER = 1U << 11, BINDER_DEBUG_INTERNAL_REFS = 1U << 12, - BINDER_DEBUG_BUFFER_ALLOC = 1U << 13, - BINDER_DEBUG_PRIORITY_CAP = 1U << 14, - BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 15, + BINDER_DEBUG_PRIORITY_CAP = 1U << 13, + BINDER_DEBUG_SPINLOCKS = 1U << 14, }; static uint32_t binder_debug_mask = BINDER_DEBUG_USER_ERROR | BINDER_DEBUG_FAILED_TRANSACTION | BINDER_DEBUG_DEAD_TRANSACTION; module_param_named(debug_mask, binder_debug_mask, uint, S_IWUSR | S_IRUGO); -static bool binder_debug_no_lock; -module_param_named(proc_no_lock, binder_debug_no_lock, bool, S_IWUSR | S_IRUGO); - static char *binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES; module_param_named(devices, binder_devices_param, charp, 0444); @@ -171,26 +199,27 @@ enum binder_stat_types { }; struct binder_stats { - int br[_IOC_NR(BR_FAILED_REPLY) + 1]; - int bc[_IOC_NR(BC_REPLY_SG) + 1]; - int obj_created[BINDER_STAT_COUNT]; - int obj_deleted[BINDER_STAT_COUNT]; + atomic_t br[_IOC_NR(BR_FAILED_REPLY) + 1]; + atomic_t bc[_IOC_NR(BC_REPLY_SG) + 1]; + atomic_t obj_created[BINDER_STAT_COUNT]; + atomic_t obj_deleted[BINDER_STAT_COUNT]; }; static struct binder_stats binder_stats; static inline void binder_stats_deleted(enum binder_stat_types type) { - binder_stats.obj_deleted[type]++; + atomic_inc(&binder_stats.obj_deleted[type]); } static inline void binder_stats_created(enum binder_stat_types type) { - binder_stats.obj_created[type]++; + atomic_inc(&binder_stats.obj_created[type]); } struct binder_transaction_log_entry { int debug_id; + int debug_id_done; int call_type; int from_proc; int from_thread; @@ -200,11 +229,14 @@ struct binder_transaction_log_entry { int to_node; int data_size; int offsets_size; + int return_error_line; + uint32_t return_error; + uint32_t return_error_param; const char *context_name; }; struct binder_transaction_log { - int next; - int full; + atomic_t cur; + bool full; struct binder_transaction_log_entry entry[32]; }; static struct binder_transaction_log binder_transaction_log; @@ -214,19 +246,26 @@ static struct binder_transaction_log_entry *binder_transaction_log_add( struct binder_transaction_log *log) { struct binder_transaction_log_entry *e; + unsigned int cur = atomic_inc_return(&log->cur); - e = &log->entry[log->next]; - memset(e, 0, sizeof(*e)); - log->next++; - if (log->next == ARRAY_SIZE(log->entry)) { - log->next = 0; + if (cur >= ARRAY_SIZE(log->entry)) log->full = 1; - } + e = &log->entry[cur % ARRAY_SIZE(log->entry)]; + WRITE_ONCE(e->debug_id_done, 0); + /* + * write-barrier to synchronize access to e->debug_id_done. + * We make sure the initialized 0 value is seen before + * memset() other fields are zeroed by memset. + */ + smp_wmb(); + memset(e, 0, sizeof(*e)); return e; } struct binder_context { struct binder_node *binder_context_mgr_node; + struct mutex context_mgr_node_lock; + kuid_t binder_context_mgr_uid; const char *name; }; @@ -237,11 +276,20 @@ struct binder_device { struct binder_context context; }; +/** + * struct binder_work - work enqueued on a worklist + * @entry: node enqueued on list + * @type: type of work to be performed + * + * There are separate work lists for proc, thread, and node (async). + */ struct binder_work { struct list_head entry; + enum { BINDER_WORK_TRANSACTION = 1, BINDER_WORK_TRANSACTION_COMPLETE, + BINDER_WORK_RETURN_ERROR, BINDER_WORK_NODE, BINDER_WORK_DEAD_BINDER, BINDER_WORK_DEAD_BINDER_AND_CLEAR, @@ -249,8 +297,72 @@ struct binder_work { } type; }; +struct binder_error { + struct binder_work work; + uint32_t cmd; +}; + +/** + * struct binder_node - binder node bookkeeping + * @debug_id: unique ID for debugging + * (invariant after initialized) + * @lock: lock for node fields + * @work: worklist element for node work + * (protected by @proc->inner_lock) + * @rb_node: element for proc->nodes tree + * (protected by @proc->inner_lock) + * @dead_node: element for binder_dead_nodes list + * (protected by binder_dead_nodes_lock) + * @proc: binder_proc that owns this node + * (invariant after initialized) + * @refs: list of references on this node + * (protected by @lock) + * @internal_strong_refs: used to take strong references when + * initiating a transaction + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @local_weak_refs: weak user refs from local process + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @local_strong_refs: strong user refs from local process + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @tmp_refs: temporary kernel refs + * (protected by @proc->inner_lock while @proc + * is valid, and by binder_dead_nodes_lock + * if @proc is NULL. During inc/dec and node release + * it is also protected by @lock to provide safety + * as the node dies and @proc becomes NULL) + * @ptr: userspace pointer for node + * (invariant, no lock needed) + * @cookie: userspace cookie for node + * (invariant, no lock needed) + * @has_strong_ref: userspace notified of strong ref + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @pending_strong_ref: userspace has acked notification of strong ref + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @has_weak_ref: userspace notified of weak ref + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @pending_weak_ref: userspace has acked notification of weak ref + * (protected by @proc->inner_lock if @proc + * and by @lock) + * @has_async_transaction: async transaction to node in progress + * (protected by @lock) + * @accept_fds: file descriptor operations supported for node + * (invariant after initialized) + * @min_priority: minimum scheduling priority + * (invariant after initialized) + * @async_todo: list of async work items + * (protected by @proc->inner_lock) + * + * Bookkeeping structure for binder nodes. + */ struct binder_node { int debug_id; + spinlock_t lock; struct binder_work work; union { struct rb_node rb_node; @@ -261,64 +373,153 @@ struct binder_node { int internal_strong_refs; int local_weak_refs; int local_strong_refs; + int tmp_refs; binder_uintptr_t ptr; binder_uintptr_t cookie; - unsigned has_strong_ref:1; - unsigned pending_strong_ref:1; - unsigned has_weak_ref:1; - unsigned pending_weak_ref:1; - unsigned has_async_transaction:1; - unsigned accept_fds:1; - unsigned min_priority:8; + struct { + /* + * bitfield elements protected by + * proc inner_lock + */ + u8 has_strong_ref:1; + u8 pending_strong_ref:1; + u8 has_weak_ref:1; + u8 pending_weak_ref:1; + }; + struct { + /* + * invariant after initialization + */ + u8 accept_fds:1; + u8 min_priority; + }; + bool has_async_transaction; struct list_head async_todo; }; struct binder_ref_death { + /** + * @work: worklist element for death notifications + * (protected by inner_lock of the proc that + * this ref belongs to) + */ struct binder_work work; binder_uintptr_t cookie; }; +/** + * struct binder_ref_data - binder_ref counts and id + * @debug_id: unique ID for the ref + * @desc: unique userspace handle for ref + * @strong: strong ref count (debugging only if not locked) + * @weak: weak ref count (debugging only if not locked) + * + * Structure to hold ref count and ref id information. Since + * the actual ref can only be accessed with a lock, this structure + * is used to return information about the ref to callers of + * ref inc/dec functions. + */ +struct binder_ref_data { + int debug_id; + uint32_t desc; + int strong; + int weak; +}; + +/** + * struct binder_ref - struct to track references on nodes + * @data: binder_ref_data containing id, handle, and current refcounts + * @rb_node_desc: node for lookup by @data.desc in proc's rb_tree + * @rb_node_node: node for lookup by @node in proc's rb_tree + * @node_entry: list entry for node->refs list in target node + * (protected by @node->lock) + * @proc: binder_proc containing ref + * @node: binder_node of target node. When cleaning up a + * ref for deletion in binder_cleanup_ref, a non-NULL + * @node indicates the node must be freed + * @death: pointer to death notification (ref_death) if requested + * (protected by @node->lock) + * + * Structure to track references from procA to target node (on procB). This + * structure is unsafe to access without holding @proc->outer_lock. + */ struct binder_ref { /* Lookups needed: */ /* node + proc => ref (transaction) */ /* desc + proc => ref (transaction, inc/dec ref) */ /* node => refs + procs (proc exit) */ - int debug_id; + struct binder_ref_data data; struct rb_node rb_node_desc; struct rb_node rb_node_node; struct hlist_node node_entry; struct binder_proc *proc; struct binder_node *node; - uint32_t desc; - int strong; - int weak; struct binder_ref_death *death; }; -struct binder_buffer { - struct list_head entry; /* free and allocated entries by address */ - struct rb_node rb_node; /* free entry by size or allocated entry */ - /* by address */ - unsigned free:1; - unsigned allow_user_free:1; - unsigned async_transaction:1; - unsigned debug_id:29; - - struct binder_transaction *transaction; - - struct binder_node *target_node; - size_t data_size; - size_t offsets_size; - size_t extra_buffers_size; - uint8_t data[0]; -}; - enum binder_deferred_state { BINDER_DEFERRED_PUT_FILES = 0x01, BINDER_DEFERRED_FLUSH = 0x02, BINDER_DEFERRED_RELEASE = 0x04, }; +/** + * struct binder_proc - binder process bookkeeping + * @proc_node: element for binder_procs list + * @threads: rbtree of binder_threads in this proc + * (protected by @inner_lock) + * @nodes: rbtree of binder nodes associated with + * this proc ordered by node->ptr + * (protected by @inner_lock) + * @refs_by_desc: rbtree of refs ordered by ref->desc + * (protected by @outer_lock) + * @refs_by_node: rbtree of refs ordered by ref->node + * (protected by @outer_lock) + * @pid PID of group_leader of process + * (invariant after initialized) + * @tsk task_struct for group_leader of process + * (invariant after initialized) + * @files files_struct for process + * (invariant after initialized) + * @deferred_work_node: element for binder_deferred_list + * (protected by binder_deferred_lock) + * @deferred_work: bitmap of deferred work to perform + * (protected by binder_deferred_lock) + * @is_dead: process is dead and awaiting free + * when outstanding transactions are cleaned up + * (protected by @inner_lock) + * @todo: list of work for this process + * (protected by @inner_lock) + * @wait: wait queue head to wait for proc work + * (invariant after initialized) + * @stats: per-process binder statistics + * (atomics, no lock needed) + * @delivered_death: list of delivered death notification + * (protected by @inner_lock) + * @max_threads: cap on number of binder threads + * (protected by @inner_lock) + * @requested_threads: number of binder threads requested but not + * yet started. In current implementation, can + * only be 0 or 1. + * (protected by @inner_lock) + * @requested_threads_started: number binder threads started + * (protected by @inner_lock) + * @ready_threads: number of threads waiting for proc work + * (protected by @inner_lock) + * @tmp_ref: temporary reference to indicate proc is in use + * (protected by @inner_lock) + * @default_priority: default scheduler priority + * (invariant after initialized) + * @debugfs_entry: debugfs node + * @alloc: binder allocator bookkeeping + * @context: binder_context for this proc + * (invariant after initialized) + * @inner_lock: can nest under outer_lock and/or node lock + * @outer_lock: no nesting under innor or node lock + * Lock order: 1) outer, 2) node, 3) inner + * + * Bookkeeping structure for binder processes + */ struct binder_proc { struct hlist_node proc_node; struct rb_root threads; @@ -326,23 +527,12 @@ struct binder_proc { struct rb_root refs_by_desc; struct rb_root refs_by_node; int pid; - struct vm_area_struct *vma; - struct mm_struct *vma_vm_mm; struct task_struct *tsk; struct files_struct *files; struct hlist_node deferred_work_node; int deferred_work; - void *buffer; - ptrdiff_t user_buffer_offset; + bool is_dead; - struct list_head buffers; - struct rb_root free_buffers; - struct rb_root allocated_buffers; - size_t free_async_space; - - struct page **pages; - size_t buffer_size; - uint32_t buffer_free; struct list_head todo; wait_queue_head_t wait; struct binder_stats stats; @@ -351,9 +541,13 @@ struct binder_proc { int requested_threads; int requested_threads_started; int ready_threads; + int tmp_ref; long default_priority; struct dentry *debugfs_entry; + struct binder_alloc alloc; struct binder_context *context; + spinlock_t inner_lock; + spinlock_t outer_lock; }; enum { @@ -362,22 +556,54 @@ enum { BINDER_LOOPER_STATE_EXITED = 0x04, BINDER_LOOPER_STATE_INVALID = 0x08, BINDER_LOOPER_STATE_WAITING = 0x10, - BINDER_LOOPER_STATE_NEED_RETURN = 0x20 }; +/** + * struct binder_thread - binder thread bookkeeping + * @proc: binder process for this thread + * (invariant after initialization) + * @rb_node: element for proc->threads rbtree + * (protected by @proc->inner_lock) + * @pid: PID for this thread + * (invariant after initialization) + * @looper: bitmap of looping state + * (only accessed by this thread) + * @looper_needs_return: looping thread needs to exit driver + * (no lock needed) + * @transaction_stack: stack of in-progress transactions for this thread + * (protected by @proc->inner_lock) + * @todo: list of work to do for this thread + * (protected by @proc->inner_lock) + * @return_error: transaction errors reported by this thread + * (only accessed by this thread) + * @reply_error: transaction errors reported by target thread + * (protected by @proc->inner_lock) + * @wait: wait queue for thread work + * @stats: per-thread statistics + * (atomics, no lock needed) + * @tmp_ref: temporary reference to indicate thread is in use + * (atomic since @proc->inner_lock cannot + * always be acquired) + * @is_dead: thread is dead and awaiting free + * when outstanding transactions are cleaned up + * (protected by @proc->inner_lock) + * + * Bookkeeping structure for binder threads. + */ struct binder_thread { struct binder_proc *proc; struct rb_node rb_node; int pid; - int looper; + int looper; /* only modified by this thread */ + bool looper_need_return; /* can be written by other thread */ struct binder_transaction *transaction_stack; struct list_head todo; - uint32_t return_error; /* Write failed, return error code in read buf */ - uint32_t return_error2; /* Write failed, return error code in read */ - /* buffer. Used when sending a reply to a dead process that */ - /* we are also waiting on */ + struct binder_error return_error; + struct binder_error reply_error; wait_queue_head_t wait; struct binder_stats stats; + atomic_t tmp_ref; + bool is_dead; }; struct binder_transaction { @@ -397,10 +623,253 @@ struct binder_transaction { long priority; long saved_priority; kuid_t sender_euid; + /** + * @lock: protects @from, @to_proc, and @to_thread + * + * @from, @to_proc, and @to_thread can be set to NULL + * during thread teardown + */ + spinlock_t lock; }; +/** + * binder_proc_lock() - Acquire outer lock for given binder_proc + * @proc: struct binder_proc to acquire + * + * Acquires proc->outer_lock. Used to protect binder_ref + * structures associated with the given proc. + */ +#define binder_proc_lock(proc) _binder_proc_lock(proc, __LINE__) +static void +_binder_proc_lock(struct binder_proc *proc, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_lock(&proc->outer_lock); +} + +/** + * binder_proc_unlock() - Release spinlock for given binder_proc + * @proc: struct binder_proc to acquire + * + * Release lock acquired via binder_proc_lock() + */ +#define binder_proc_unlock(_proc) _binder_proc_unlock(_proc, __LINE__) +static void +_binder_proc_unlock(struct binder_proc *proc, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_unlock(&proc->outer_lock); +} + +/** + * binder_inner_proc_lock() - Acquire inner lock for given binder_proc + * @proc: struct binder_proc to acquire + * + * Acquires proc->inner_lock. Used to protect todo lists + */ +#define binder_inner_proc_lock(proc) _binder_inner_proc_lock(proc, __LINE__) +static void +_binder_inner_proc_lock(struct binder_proc *proc, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_lock(&proc->inner_lock); +} + +/** + * binder_inner_proc_unlock() - Release inner lock for given binder_proc + * @proc: struct binder_proc to acquire + * + * Release lock acquired via binder_inner_proc_lock() + */ +#define binder_inner_proc_unlock(proc) _binder_inner_proc_unlock(proc, __LINE__) +static void +_binder_inner_proc_unlock(struct binder_proc *proc, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_unlock(&proc->inner_lock); +} + +/** + * binder_node_lock() - Acquire spinlock for given binder_node + * @node: struct binder_node to acquire + * + * Acquires node->lock. Used to protect binder_node fields + */ +#define binder_node_lock(node) _binder_node_lock(node, __LINE__) +static void +_binder_node_lock(struct binder_node *node, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_lock(&node->lock); +} + +/** + * binder_node_unlock() - Release spinlock for given binder_proc + * @node: struct binder_node to acquire + * + * Release lock acquired via binder_node_lock() + */ +#define binder_node_unlock(node) _binder_node_unlock(node, __LINE__) +static void +_binder_node_unlock(struct binder_node *node, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_unlock(&node->lock); +} + +/** + * binder_node_inner_lock() - Acquire node and inner locks + * @node: struct binder_node to acquire + * + * Acquires node->lock. If node->proc also acquires + * proc->inner_lock. Used to protect binder_node fields + */ +#define binder_node_inner_lock(node) _binder_node_inner_lock(node, __LINE__) +static void +_binder_node_inner_lock(struct binder_node *node, int line) +{ + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + spin_lock(&node->lock); + if (node->proc) + binder_inner_proc_lock(node->proc); +} + +/** + * binder_node_unlock() - Release node and inner locks + * @node: struct binder_node to acquire + * + * Release lock acquired via binder_node_lock() + */ +#define binder_node_inner_unlock(node) _binder_node_inner_unlock(node, __LINE__) +static void +_binder_node_inner_unlock(struct binder_node *node, int line) +{ + struct binder_proc *proc = node->proc; + + binder_debug(BINDER_DEBUG_SPINLOCKS, + "%s: line=%d\n", __func__, line); + if (proc) + binder_inner_proc_unlock(proc); + spin_unlock(&node->lock); +} + +static bool binder_worklist_empty_ilocked(struct list_head *list) +{ + return list_empty(list); +} + +/** + * binder_worklist_empty() - Check if no items on the work list + * @proc: binder_proc associated with list + * @list: list to check + * + * Return: true if there are no items on list, else false + */ +static bool binder_worklist_empty(struct binder_proc *proc, + struct list_head *list) +{ + bool ret; + + binder_inner_proc_lock(proc); + ret = binder_worklist_empty_ilocked(list); + binder_inner_proc_unlock(proc); + return ret; +} + +static void +binder_enqueue_work_ilocked(struct binder_work *work, + struct list_head *target_list) +{ + BUG_ON(target_list == NULL); + BUG_ON(work->entry.next && !list_empty(&work->entry)); + list_add_tail(&work->entry, target_list); +} + +/** + * binder_enqueue_work() - Add an item to the work list + * @proc: binder_proc associated with list + * @work: struct binder_work to add to list + * @target_list: list to add work to + * + * Adds the work to the specified list. Asserts that work + * is not already on a list. + */ +static void +binder_enqueue_work(struct binder_proc *proc, + struct binder_work *work, + struct list_head *target_list) +{ + binder_inner_proc_lock(proc); + binder_enqueue_work_ilocked(work, target_list); + binder_inner_proc_unlock(proc); +} + +static void +binder_dequeue_work_ilocked(struct binder_work *work) +{ + list_del_init(&work->entry); +} + +/** + * binder_dequeue_work() - Removes an item from the work list + * @proc: binder_proc associated with list + * @work: struct binder_work to remove from list + * + * Removes the specified work item from whatever list it is on. + * Can safely be called if work is not on any list. + */ +static void +binder_dequeue_work(struct binder_proc *proc, struct binder_work *work) +{ + binder_inner_proc_lock(proc); + binder_dequeue_work_ilocked(work); + binder_inner_proc_unlock(proc); +} + +static struct binder_work *binder_dequeue_work_head_ilocked( + struct list_head *list) +{ + struct binder_work *w; + + w = list_first_entry_or_null(list, struct binder_work, entry); + if (w) + list_del_init(&w->entry); + return w; +} + +/** + * binder_dequeue_work_head() - Dequeues the item at head of list + * @proc: binder_proc associated with list + * @list: list to dequeue head + * + * Removes the head of the list if there are items on the list + * + * Return: pointer dequeued binder_work, NULL if list was empty + */ +static struct binder_work *binder_dequeue_work_head( + struct binder_proc *proc, + struct list_head *list) +{ + struct binder_work *w; + + binder_inner_proc_lock(proc); + w = binder_dequeue_work_head_ilocked(list); + binder_inner_proc_unlock(proc); + return w; +} + static void binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer); +static void binder_free_thread(struct binder_thread *thread); +static void binder_free_proc(struct binder_proc *proc); +static void binder_inc_node_tmpref_ilocked(struct binder_node *node); static int task_get_unused_fd_flags(struct binder_proc *proc, int flags) { @@ -451,19 +920,6 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) return retval; } -static inline void binder_lock(const char *tag) -{ - trace_binder_lock(tag); - mutex_lock(&binder_main_lock); - trace_binder_locked(tag); -} - -static inline void binder_unlock(const char *tag) -{ - trace_binder_unlock(tag); - mutex_unlock(&binder_main_lock); -} - static void binder_set_nice(long nice) { long min_nice; @@ -472,7 +928,7 @@ static void binder_set_nice(long nice) set_user_nice(current, nice); return; } - min_nice = rlimit_to_nice(current->signal->rlim[RLIMIT_NICE].rlim_cur); + min_nice = rlimit_to_nice(rlimit(RLIMIT_NICE)); binder_debug(BINDER_DEBUG_PRIORITY_CAP, "%d: nice value %ld not allowed use %ld instead\n", current->pid, nice, min_nice); @@ -482,453 +938,60 @@ static void binder_set_nice(long nice) binder_user_error("%d RLIMIT_NICE not set\n", current->pid); } -static size_t binder_buffer_size(struct binder_proc *proc, - struct binder_buffer *buffer) -{ - if (list_is_last(&buffer->entry, &proc->buffers)) - return proc->buffer + proc->buffer_size - (void *)buffer->data; - return (size_t)list_entry(buffer->entry.next, - struct binder_buffer, entry) - (size_t)buffer->data; -} - -static void binder_insert_free_buffer(struct binder_proc *proc, - struct binder_buffer *new_buffer) -{ - struct rb_node **p = &proc->free_buffers.rb_node; - struct rb_node *parent = NULL; - struct binder_buffer *buffer; - size_t buffer_size; - size_t new_buffer_size; - - BUG_ON(!new_buffer->free); - - new_buffer_size = binder_buffer_size(proc, new_buffer); - - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: add free buffer, size %zd, at %p\n", - proc->pid, new_buffer_size, new_buffer); - - while (*p) { - parent = *p; - buffer = rb_entry(parent, struct binder_buffer, rb_node); - BUG_ON(!buffer->free); - - buffer_size = binder_buffer_size(proc, buffer); - - if (new_buffer_size < buffer_size) - p = &parent->rb_left; - else - p = &parent->rb_right; - } - rb_link_node(&new_buffer->rb_node, parent, p); - rb_insert_color(&new_buffer->rb_node, &proc->free_buffers); -} - -static void binder_insert_allocated_buffer(struct binder_proc *proc, - struct binder_buffer *new_buffer) +static struct binder_node *binder_get_node_ilocked(struct binder_proc *proc, + binder_uintptr_t ptr) { - struct rb_node **p = &proc->allocated_buffers.rb_node; - struct rb_node *parent = NULL; - struct binder_buffer *buffer; - - BUG_ON(new_buffer->free); - - while (*p) { - parent = *p; - buffer = rb_entry(parent, struct binder_buffer, rb_node); - BUG_ON(buffer->free); - - if (new_buffer < buffer) - p = &parent->rb_left; - else if (new_buffer > buffer) - p = &parent->rb_right; - else - BUG(); - } - rb_link_node(&new_buffer->rb_node, parent, p); - rb_insert_color(&new_buffer->rb_node, &proc->allocated_buffers); -} - -static struct binder_buffer *binder_buffer_lookup(struct binder_proc *proc, - uintptr_t user_ptr) -{ - struct rb_node *n = proc->allocated_buffers.rb_node; - struct binder_buffer *buffer; - struct binder_buffer *kern_ptr; - - kern_ptr = (struct binder_buffer *)(user_ptr - proc->user_buffer_offset - - offsetof(struct binder_buffer, data)); - - while (n) { - buffer = rb_entry(n, struct binder_buffer, rb_node); - BUG_ON(buffer->free); - - if (kern_ptr < buffer) - n = n->rb_left; - else if (kern_ptr > buffer) - n = n->rb_right; - else - return buffer; - } - return NULL; -} - -static int binder_update_page_range(struct binder_proc *proc, int allocate, - void *start, void *end, - struct vm_area_struct *vma) -{ - void *page_addr; - unsigned long user_page_addr; - struct page **page; - struct mm_struct *mm; - - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: %s pages %p-%p\n", proc->pid, - allocate ? "allocate" : "free", start, end); - - if (end <= start) - return 0; - - trace_binder_update_page_range(proc, allocate, start, end); - - if (vma) - mm = NULL; - else - mm = get_task_mm(proc->tsk); - - if (mm) { - down_write(&mm->mmap_sem); - vma = proc->vma; - if (vma && mm != proc->vma_vm_mm) { - pr_err("%d: vma mm and task mm mismatch\n", - proc->pid); - vma = NULL; - } - } - - if (allocate == 0) - goto free_range; - - if (vma == NULL) { - pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n", - proc->pid); - goto err_no_vma; - } - - for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { - int ret; - - page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE]; - - BUG_ON(*page); - *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO); - if (*page == NULL) { - pr_err("%d: binder_alloc_buf failed for page at %p\n", - proc->pid, page_addr); - goto err_alloc_page_failed; - } - ret = map_kernel_range_noflush((unsigned long)page_addr, - PAGE_SIZE, PAGE_KERNEL, page); - flush_cache_vmap((unsigned long)page_addr, - (unsigned long)page_addr + PAGE_SIZE); - if (ret != 1) { - pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n", - proc->pid, page_addr); - goto err_map_kernel_failed; - } - user_page_addr = - (uintptr_t)page_addr + proc->user_buffer_offset; - ret = vm_insert_page(vma, user_page_addr, page[0]); - if (ret) { - pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n", - proc->pid, user_page_addr); - goto err_vm_insert_page_failed; - } - /* vm_insert_page does not seem to increment the refcount */ - } - if (mm) { - up_write(&mm->mmap_sem); - mmput(mm); - } - return 0; - -free_range: - for (page_addr = end - PAGE_SIZE; page_addr >= start; - page_addr -= PAGE_SIZE) { - page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE]; - if (vma) - zap_page_range(vma, (uintptr_t)page_addr + - proc->user_buffer_offset, PAGE_SIZE); -err_vm_insert_page_failed: - unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); -err_map_kernel_failed: - __free_page(*page); - *page = NULL; -err_alloc_page_failed: - ; - } -err_no_vma: - if (mm) { - up_write(&mm->mmap_sem); - mmput(mm); - } - return -ENOMEM; -} - -static struct binder_buffer *binder_alloc_buf(struct binder_proc *proc, - size_t data_size, - size_t offsets_size, - size_t extra_buffers_size, - int is_async) -{ - struct rb_node *n = proc->free_buffers.rb_node; - struct binder_buffer *buffer; - size_t buffer_size; - struct rb_node *best_fit = NULL; - void *has_page_addr; - void *end_page_addr; - size_t size, data_offsets_size; - - if (proc->vma == NULL) { - pr_err("%d: binder_alloc_buf, no vma\n", - proc->pid); - return NULL; - } - - data_offsets_size = ALIGN(data_size, sizeof(void *)) + - ALIGN(offsets_size, sizeof(void *)); + struct rb_node *n = proc->nodes.rb_node; + struct binder_node *node; - if (data_offsets_size < data_size || data_offsets_size < offsets_size) { - binder_user_error("%d: got transaction with invalid size %zd-%zd\n", - proc->pid, data_size, offsets_size); - return NULL; - } - size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *)); - if (size < data_offsets_size || size < extra_buffers_size) { - binder_user_error("%d: got transaction with invalid extra_buffers_size %zd\n", - proc->pid, extra_buffers_size); - return NULL; - } - if (is_async && - proc->free_async_space < size + sizeof(struct binder_buffer)) { - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: binder_alloc_buf size %zd failed, no async space left\n", - proc->pid, size); - return NULL; - } + BUG_ON(!spin_is_locked(&proc->inner_lock)); while (n) { - buffer = rb_entry(n, struct binder_buffer, rb_node); - BUG_ON(!buffer->free); - buffer_size = binder_buffer_size(proc, buffer); + node = rb_entry(n, struct binder_node, rb_node); - if (size < buffer_size) { - best_fit = n; + if (ptr < node->ptr) n = n->rb_left; - } else if (size > buffer_size) + else if (ptr > node->ptr) n = n->rb_right; else { - best_fit = n; - break; - } - } - if (best_fit == NULL) { - pr_err("%d: binder_alloc_buf size %zd failed, no address space\n", - proc->pid, size); - return NULL; - } - if (n == NULL) { - buffer = rb_entry(best_fit, struct binder_buffer, rb_node); - buffer_size = binder_buffer_size(proc, buffer); - } - - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: binder_alloc_buf size %zd got buffer %p size %zd\n", - proc->pid, size, buffer, buffer_size); - - has_page_addr = - (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK); - if (n == NULL) { - if (size + sizeof(struct binder_buffer) + 4 >= buffer_size) - buffer_size = size; /* no room for other buffers */ - else - buffer_size = size + sizeof(struct binder_buffer); - } - end_page_addr = - (void *)PAGE_ALIGN((uintptr_t)buffer->data + buffer_size); - if (end_page_addr > has_page_addr) - end_page_addr = has_page_addr; - if (binder_update_page_range(proc, 1, - (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL)) - return NULL; - - rb_erase(best_fit, &proc->free_buffers); - buffer->free = 0; - binder_insert_allocated_buffer(proc, buffer); - if (buffer_size != size) { - struct binder_buffer *new_buffer = (void *)buffer->data + size; - - list_add(&new_buffer->entry, &buffer->entry); - new_buffer->free = 1; - binder_insert_free_buffer(proc, new_buffer); - } - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: binder_alloc_buf size %zd got %p\n", - proc->pid, size, buffer); - buffer->data_size = data_size; - buffer->offsets_size = offsets_size; - buffer->extra_buffers_size = extra_buffers_size; - buffer->async_transaction = is_async; - if (is_async) { - proc->free_async_space -= size + sizeof(struct binder_buffer); - binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, - "%d: binder_alloc_buf size %zd async free %zd\n", - proc->pid, size, proc->free_async_space); - } - - return buffer; -} - -static void *buffer_start_page(struct binder_buffer *buffer) -{ - return (void *)((uintptr_t)buffer & PAGE_MASK); -} - -static void *buffer_end_page(struct binder_buffer *buffer) -{ - return (void *)(((uintptr_t)(buffer + 1) - 1) & PAGE_MASK); -} - -static void binder_delete_free_buffer(struct binder_proc *proc, - struct binder_buffer *buffer) -{ - struct binder_buffer *prev, *next = NULL; - int free_page_end = 1; - int free_page_start = 1; - - BUG_ON(proc->buffers.next == &buffer->entry); - prev = list_entry(buffer->entry.prev, struct binder_buffer, entry); - BUG_ON(!prev->free); - if (buffer_end_page(prev) == buffer_start_page(buffer)) { - free_page_start = 0; - if (buffer_end_page(prev) == buffer_end_page(buffer)) - free_page_end = 0; - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer %p share page with %p\n", - proc->pid, buffer, prev); - } - - if (!list_is_last(&buffer->entry, &proc->buffers)) { - next = list_entry(buffer->entry.next, - struct binder_buffer, entry); - if (buffer_start_page(next) == buffer_end_page(buffer)) { - free_page_end = 0; - if (buffer_start_page(next) == - buffer_start_page(buffer)) - free_page_start = 0; - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer %p share page with %p\n", - proc->pid, buffer, prev); - } - } - list_del(&buffer->entry); - if (free_page_start || free_page_end) { - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: merge free, buffer %p do not share page%s%s with %p or %p\n", - proc->pid, buffer, free_page_start ? "" : " end", - free_page_end ? "" : " start", prev, next); - binder_update_page_range(proc, 0, free_page_start ? - buffer_start_page(buffer) : buffer_end_page(buffer), - (free_page_end ? buffer_end_page(buffer) : - buffer_start_page(buffer)) + PAGE_SIZE, NULL); - } -} - -static void binder_free_buf(struct binder_proc *proc, - struct binder_buffer *buffer) -{ - size_t size, buffer_size; - - buffer_size = binder_buffer_size(proc, buffer); - - size = ALIGN(buffer->data_size, sizeof(void *)) + - ALIGN(buffer->offsets_size, sizeof(void *)) + - ALIGN(buffer->extra_buffers_size, sizeof(void *)); - - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%d: binder_free_buf %p size %zd buffer_size %zd\n", - proc->pid, buffer, size, buffer_size); - - BUG_ON(buffer->free); - BUG_ON(size > buffer_size); - BUG_ON(buffer->transaction != NULL); - BUG_ON((void *)buffer < proc->buffer); - BUG_ON((void *)buffer > proc->buffer + proc->buffer_size); - - if (buffer->async_transaction) { - proc->free_async_space += size + sizeof(struct binder_buffer); - - binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, - "%d: binder_free_buf size %zd async free %zd\n", - proc->pid, size, proc->free_async_space); - } - - binder_update_page_range(proc, 0, - (void *)PAGE_ALIGN((uintptr_t)buffer->data), - (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK), - NULL); - rb_erase(&buffer->rb_node, &proc->allocated_buffers); - buffer->free = 1; - if (!list_is_last(&buffer->entry, &proc->buffers)) { - struct binder_buffer *next = list_entry(buffer->entry.next, - struct binder_buffer, entry); - - if (next->free) { - rb_erase(&next->rb_node, &proc->free_buffers); - binder_delete_free_buffer(proc, next); - } - } - if (proc->buffers.next != &buffer->entry) { - struct binder_buffer *prev = list_entry(buffer->entry.prev, - struct binder_buffer, entry); - - if (prev->free) { - binder_delete_free_buffer(proc, buffer); - rb_erase(&prev->rb_node, &proc->free_buffers); - buffer = prev; + /* + * take an implicit weak reference + * to ensure node stays alive until + * call to binder_put_node() + */ + binder_inc_node_tmpref_ilocked(node); + return node; } } - binder_insert_free_buffer(proc, buffer); + return NULL; } static struct binder_node *binder_get_node(struct binder_proc *proc, binder_uintptr_t ptr) { - struct rb_node *n = proc->nodes.rb_node; struct binder_node *node; - while (n) { - node = rb_entry(n, struct binder_node, rb_node); - - if (ptr < node->ptr) - n = n->rb_left; - else if (ptr > node->ptr) - n = n->rb_right; - else - return node; - } - return NULL; + binder_inner_proc_lock(proc); + node = binder_get_node_ilocked(proc, ptr); + binder_inner_proc_unlock(proc); + return node; } -static struct binder_node *binder_new_node(struct binder_proc *proc, - binder_uintptr_t ptr, - binder_uintptr_t cookie) +static struct binder_node *binder_init_node_ilocked( + struct binder_proc *proc, + struct binder_node *new_node, + struct flat_binder_object *fp) { struct rb_node **p = &proc->nodes.rb_node; struct rb_node *parent = NULL; struct binder_node *node; + binder_uintptr_t ptr = fp ? fp->binder : 0; + binder_uintptr_t cookie = fp ? fp->cookie : 0; + __u32 flags = fp ? fp->flags : 0; + BUG_ON(!spin_is_locked(&proc->inner_lock)); while (*p) { + parent = *p; node = rb_entry(parent, struct binder_node, rb_node); @@ -936,33 +999,74 @@ static struct binder_node *binder_new_node(struct binder_proc *proc, p = &(*p)->rb_left; else if (ptr > node->ptr) p = &(*p)->rb_right; - else - return NULL; + else { + /* + * A matching node is already in + * the rb tree. Abandon the init + * and return it. + */ + binder_inc_node_tmpref_ilocked(node); + return node; + } } - - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (node == NULL) - return NULL; + node = new_node; binder_stats_created(BINDER_STAT_NODE); + node->tmp_refs++; rb_link_node(&node->rb_node, parent, p); rb_insert_color(&node->rb_node, &proc->nodes); - node->debug_id = ++binder_last_id; + node->debug_id = atomic_inc_return(&binder_last_id); node->proc = proc; node->ptr = ptr; node->cookie = cookie; node->work.type = BINDER_WORK_NODE; + node->min_priority = flags & FLAT_BINDER_FLAG_PRIORITY_MASK; + node->accept_fds = !!(flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); + spin_lock_init(&node->lock); INIT_LIST_HEAD(&node->work.entry); INIT_LIST_HEAD(&node->async_todo); binder_debug(BINDER_DEBUG_INTERNAL_REFS, "%d:%d node %d u%016llx c%016llx created\n", proc->pid, current->pid, node->debug_id, (u64)node->ptr, (u64)node->cookie); + return node; } -static int binder_inc_node(struct binder_node *node, int strong, int internal, - struct list_head *target_list) +static struct binder_node *binder_new_node(struct binder_proc *proc, + struct flat_binder_object *fp) { + struct binder_node *node; + struct binder_node *new_node = kzalloc(sizeof(*node), GFP_KERNEL); + + if (!new_node) + return NULL; + binder_inner_proc_lock(proc); + node = binder_init_node_ilocked(proc, new_node, fp); + binder_inner_proc_unlock(proc); + if (node != new_node) + /* + * The node was already added by another thread + */ + kfree(new_node); + + return node; +} + +static void binder_free_node(struct binder_node *node) +{ + kfree(node); + binder_stats_deleted(BINDER_STAT_NODE); +} + +static int binder_inc_node_nilocked(struct binder_node *node, int strong, + int internal, + struct list_head *target_list) +{ + struct binder_proc *proc = node->proc; + + BUG_ON(!spin_is_locked(&node->lock)); + if (proc) + BUG_ON(!spin_is_locked(&proc->inner_lock)); if (strong) { if (internal) { if (target_list == NULL && @@ -978,8 +1082,8 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal, } else node->local_strong_refs++; if (!node->has_strong_ref && target_list) { - list_del_init(&node->work.entry); - list_add_tail(&node->work.entry, target_list); + binder_dequeue_work_ilocked(&node->work); + binder_enqueue_work_ilocked(&node->work, target_list); } } else { if (!internal) @@ -990,58 +1094,169 @@ static int binder_inc_node(struct binder_node *node, int strong, int internal, node->debug_id); return -EINVAL; } - list_add_tail(&node->work.entry, target_list); + binder_enqueue_work_ilocked(&node->work, target_list); } } return 0; } -static int binder_dec_node(struct binder_node *node, int strong, int internal) +static int binder_inc_node(struct binder_node *node, int strong, int internal, + struct list_head *target_list) +{ + int ret; + + binder_node_inner_lock(node); + ret = binder_inc_node_nilocked(node, strong, internal, target_list); + binder_node_inner_unlock(node); + + return ret; +} + +static bool binder_dec_node_nilocked(struct binder_node *node, + int strong, int internal) { + struct binder_proc *proc = node->proc; + + BUG_ON(!spin_is_locked(&node->lock)); + if (proc) + BUG_ON(!spin_is_locked(&proc->inner_lock)); if (strong) { if (internal) node->internal_strong_refs--; else node->local_strong_refs--; if (node->local_strong_refs || node->internal_strong_refs) - return 0; + return false; } else { if (!internal) node->local_weak_refs--; - if (node->local_weak_refs || !hlist_empty(&node->refs)) - return 0; + if (node->local_weak_refs || node->tmp_refs || + !hlist_empty(&node->refs)) + return false; } - if (node->proc && (node->has_strong_ref || node->has_weak_ref)) { + + if (proc && (node->has_strong_ref || node->has_weak_ref)) { if (list_empty(&node->work.entry)) { - list_add_tail(&node->work.entry, &node->proc->todo); + binder_enqueue_work_ilocked(&node->work, &proc->todo); wake_up_interruptible(&node->proc->wait); } } else { if (hlist_empty(&node->refs) && !node->local_strong_refs && - !node->local_weak_refs) { - list_del_init(&node->work.entry); - if (node->proc) { - rb_erase(&node->rb_node, &node->proc->nodes); + !node->local_weak_refs && !node->tmp_refs) { + if (proc) { + binder_dequeue_work_ilocked(&node->work); + rb_erase(&node->rb_node, &proc->nodes); binder_debug(BINDER_DEBUG_INTERNAL_REFS, "refless node %d deleted\n", node->debug_id); } else { + BUG_ON(!list_empty(&node->work.entry)); + spin_lock(&binder_dead_nodes_lock); + /* + * tmp_refs could have changed so + * check it again + */ + if (node->tmp_refs) { + spin_unlock(&binder_dead_nodes_lock); + return false; + } hlist_del(&node->dead_node); + spin_unlock(&binder_dead_nodes_lock); binder_debug(BINDER_DEBUG_INTERNAL_REFS, "dead node %d deleted\n", node->debug_id); } - kfree(node); - binder_stats_deleted(BINDER_STAT_NODE); + return true; } } + return false; +} - return 0; +static void binder_dec_node(struct binder_node *node, int strong, int internal) +{ + bool free_node; + + binder_node_inner_lock(node); + free_node = binder_dec_node_nilocked(node, strong, internal); + binder_node_inner_unlock(node); + if (free_node) + binder_free_node(node); +} + +static void binder_inc_node_tmpref_ilocked(struct binder_node *node) +{ + /* + * No call to binder_inc_node() is needed since we + * don't need to inform userspace of any changes to + * tmp_refs + */ + node->tmp_refs++; +} + +/** + * binder_inc_node_tmpref() - take a temporary reference on node + * @node: node to reference + * + * Take reference on node to prevent the node from being freed + * while referenced only by a local variable. The inner lock is + * needed to serialize with the node work on the queue (which + * isn't needed after the node is dead). If the node is dead + * (node->proc is NULL), use binder_dead_nodes_lock to protect + * node->tmp_refs against dead-node-only cases where the node + * lock cannot be acquired (eg traversing the dead node list to + * print nodes) + */ +static void binder_inc_node_tmpref(struct binder_node *node) +{ + binder_node_lock(node); + if (node->proc) + binder_inner_proc_lock(node->proc); + else + spin_lock(&binder_dead_nodes_lock); + binder_inc_node_tmpref_ilocked(node); + if (node->proc) + binder_inner_proc_unlock(node->proc); + else + spin_unlock(&binder_dead_nodes_lock); + binder_node_unlock(node); +} + +/** + * binder_dec_node_tmpref() - remove a temporary reference on node + * @node: node to reference + * + * Release temporary reference on node taken via binder_inc_node_tmpref() + */ +static void binder_dec_node_tmpref(struct binder_node *node) +{ + bool free_node; + + binder_node_inner_lock(node); + if (!node->proc) + spin_lock(&binder_dead_nodes_lock); + node->tmp_refs--; + BUG_ON(node->tmp_refs < 0); + if (!node->proc) + spin_unlock(&binder_dead_nodes_lock); + /* + * Call binder_dec_node() to check if all refcounts are 0 + * and cleanup is needed. Calling with strong=0 and internal=1 + * causes no actual reference to be released in binder_dec_node(). + * If that changes, a change is needed here too. + */ + free_node = binder_dec_node_nilocked(node, 0, 1); + binder_node_inner_unlock(node); + if (free_node) + binder_free_node(node); } +static void binder_put_node(struct binder_node *node) +{ + binder_dec_node_tmpref(node); +} -static struct binder_ref *binder_get_ref(struct binder_proc *proc, - u32 desc, bool need_strong_ref) +static struct binder_ref *binder_get_ref_olocked(struct binder_proc *proc, + u32 desc, bool need_strong_ref) { struct rb_node *n = proc->refs_by_desc.rb_node; struct binder_ref *ref; @@ -1049,11 +1264,11 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc, while (n) { ref = rb_entry(n, struct binder_ref, rb_node_desc); - if (desc < ref->desc) { + if (desc < ref->data.desc) { n = n->rb_left; - } else if (desc > ref->desc) { + } else if (desc > ref->data.desc) { n = n->rb_right; - } else if (need_strong_ref && !ref->strong) { + } else if (need_strong_ref && !ref->data.strong) { binder_user_error("tried to use weak ref as strong ref\n"); return NULL; } else { @@ -1063,14 +1278,34 @@ static struct binder_ref *binder_get_ref(struct binder_proc *proc, return NULL; } -static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, - struct binder_node *node) +/** + * binder_get_ref_for_node_olocked() - get the ref associated with given node + * @proc: binder_proc that owns the ref + * @node: binder_node of target + * @new_ref: newly allocated binder_ref to be initialized or %NULL + * + * Look up the ref for the given node and return it if it exists + * + * If it doesn't exist and the caller provides a newly allocated + * ref, initialize the fields of the newly allocated ref and insert + * into the given proc rb_trees and node refs list. + * + * Return: the ref for node. It is possible that another thread + * allocated/initialized the ref first in which case the + * returned ref would be different than the passed-in + * new_ref. new_ref must be kfree'd by the caller in + * this case. + */ +static struct binder_ref *binder_get_ref_for_node_olocked( + struct binder_proc *proc, + struct binder_node *node, + struct binder_ref *new_ref) { - struct rb_node *n; + struct binder_context *context = proc->context; struct rb_node **p = &proc->refs_by_node.rb_node; struct rb_node *parent = NULL; - struct binder_ref *ref, *new_ref; - struct binder_context *context = proc->context; + struct binder_ref *ref; + struct rb_node *n; while (*p) { parent = *p; @@ -1083,22 +1318,22 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, else return ref; } - new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); - if (new_ref == NULL) + if (!new_ref) return NULL; + binder_stats_created(BINDER_STAT_REF); - new_ref->debug_id = ++binder_last_id; + new_ref->data.debug_id = atomic_inc_return(&binder_last_id); new_ref->proc = proc; new_ref->node = node; rb_link_node(&new_ref->rb_node_node, parent, p); rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); - new_ref->desc = (node == context->binder_context_mgr_node) ? 0 : 1; + new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1; for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { ref = rb_entry(n, struct binder_ref, rb_node_desc); - if (ref->desc > new_ref->desc) + if (ref->data.desc > new_ref->data.desc) break; - new_ref->desc = ref->desc + 1; + new_ref->data.desc = ref->data.desc + 1; } p = &proc->refs_by_desc.rb_node; @@ -1106,121 +1341,423 @@ static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc, parent = *p; ref = rb_entry(parent, struct binder_ref, rb_node_desc); - if (new_ref->desc < ref->desc) + if (new_ref->data.desc < ref->data.desc) p = &(*p)->rb_left; - else if (new_ref->desc > ref->desc) + else if (new_ref->data.desc > ref->data.desc) p = &(*p)->rb_right; else BUG(); } rb_link_node(&new_ref->rb_node_desc, parent, p); rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc); - if (node) { - hlist_add_head(&new_ref->node_entry, &node->refs); - binder_debug(BINDER_DEBUG_INTERNAL_REFS, - "%d new ref %d desc %d for node %d\n", - proc->pid, new_ref->debug_id, new_ref->desc, - node->debug_id); - } else { - binder_debug(BINDER_DEBUG_INTERNAL_REFS, - "%d new ref %d desc %d for dead node\n", - proc->pid, new_ref->debug_id, new_ref->desc); - } + binder_node_lock(node); + hlist_add_head(&new_ref->node_entry, &node->refs); + + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "%d new ref %d desc %d for node %d\n", + proc->pid, new_ref->data.debug_id, new_ref->data.desc, + node->debug_id); + binder_node_unlock(node); return new_ref; } -static void binder_delete_ref(struct binder_ref *ref) +static void binder_cleanup_ref_olocked(struct binder_ref *ref) { + bool delete_node = false; + binder_debug(BINDER_DEBUG_INTERNAL_REFS, "%d delete ref %d desc %d for node %d\n", - ref->proc->pid, ref->debug_id, ref->desc, + ref->proc->pid, ref->data.debug_id, ref->data.desc, ref->node->debug_id); rb_erase(&ref->rb_node_desc, &ref->proc->refs_by_desc); rb_erase(&ref->rb_node_node, &ref->proc->refs_by_node); - if (ref->strong) - binder_dec_node(ref->node, 1, 1); + + binder_node_inner_lock(ref->node); + if (ref->data.strong) + binder_dec_node_nilocked(ref->node, 1, 1); + hlist_del(&ref->node_entry); - binder_dec_node(ref->node, 0, 1); + delete_node = binder_dec_node_nilocked(ref->node, 0, 1); + binder_node_inner_unlock(ref->node); + /* + * Clear ref->node unless we want the caller to free the node + */ + if (!delete_node) { + /* + * The caller uses ref->node to determine + * whether the node needs to be freed. Clear + * it since the node is still alive. + */ + ref->node = NULL; + } + if (ref->death) { binder_debug(BINDER_DEBUG_DEAD_BINDER, "%d delete ref %d desc %d has death notification\n", - ref->proc->pid, ref->debug_id, ref->desc); - list_del(&ref->death->work.entry); - kfree(ref->death); + ref->proc->pid, ref->data.debug_id, + ref->data.desc); + binder_dequeue_work(ref->proc, &ref->death->work); binder_stats_deleted(BINDER_STAT_DEATH); } - kfree(ref); binder_stats_deleted(BINDER_STAT_REF); } -static int binder_inc_ref(struct binder_ref *ref, int strong, - struct list_head *target_list) +/** + * binder_inc_ref_olocked() - increment the ref for given handle + * @ref: ref to be incremented + * @strong: if true, strong increment, else weak + * @target_list: list to queue node work on + * + * Increment the ref. @ref->proc->outer_lock must be held on entry + * + * Return: 0, if successful, else errno + */ +static int binder_inc_ref_olocked(struct binder_ref *ref, int strong, + struct list_head *target_list) { int ret; if (strong) { - if (ref->strong == 0) { + if (ref->data.strong == 0) { ret = binder_inc_node(ref->node, 1, 1, target_list); if (ret) return ret; } - ref->strong++; + ref->data.strong++; } else { - if (ref->weak == 0) { + if (ref->data.weak == 0) { ret = binder_inc_node(ref->node, 0, 1, target_list); if (ret) return ret; } - ref->weak++; + ref->data.weak++; } return 0; } - -static int binder_dec_ref(struct binder_ref *ref, int strong) +/** + * binder_dec_ref() - dec the ref for given handle + * @ref: ref to be decremented + * @strong: if true, strong decrement, else weak + * + * Decrement the ref. + * + * Return: true if ref is cleaned up and ready to be freed + */ +static bool binder_dec_ref_olocked(struct binder_ref *ref, int strong) { if (strong) { - if (ref->strong == 0) { + if (ref->data.strong == 0) { binder_user_error("%d invalid dec strong, ref %d desc %d s %d w %d\n", - ref->proc->pid, ref->debug_id, - ref->desc, ref->strong, ref->weak); - return -EINVAL; - } - ref->strong--; - if (ref->strong == 0) { - int ret; - - ret = binder_dec_node(ref->node, strong, 1); - if (ret) - return ret; + ref->proc->pid, ref->data.debug_id, + ref->data.desc, ref->data.strong, + ref->data.weak); + return false; } + ref->data.strong--; + if (ref->data.strong == 0) + binder_dec_node(ref->node, strong, 1); } else { - if (ref->weak == 0) { + if (ref->data.weak == 0) { binder_user_error("%d invalid dec weak, ref %d desc %d s %d w %d\n", - ref->proc->pid, ref->debug_id, - ref->desc, ref->strong, ref->weak); - return -EINVAL; + ref->proc->pid, ref->data.debug_id, + ref->data.desc, ref->data.strong, + ref->data.weak); + return false; } - ref->weak--; + ref->data.weak--; } - if (ref->strong == 0 && ref->weak == 0) - binder_delete_ref(ref); - return 0; + if (ref->data.strong == 0 && ref->data.weak == 0) { + binder_cleanup_ref_olocked(ref); + return true; + } + return false; } -static void binder_pop_transaction(struct binder_thread *target_thread, - struct binder_transaction *t) +/** + * binder_get_node_from_ref() - get the node from the given proc/desc + * @proc: proc containing the ref + * @desc: the handle associated with the ref + * @need_strong_ref: if true, only return node if ref is strong + * @rdata: the id/refcount data for the ref + * + * Given a proc and ref handle, return the associated binder_node + * + * Return: a binder_node or NULL if not found or not strong when strong required + */ +static struct binder_node *binder_get_node_from_ref( + struct binder_proc *proc, + u32 desc, bool need_strong_ref, + struct binder_ref_data *rdata) { - if (target_thread) { - BUG_ON(target_thread->transaction_stack != t); - BUG_ON(target_thread->transaction_stack->from != target_thread); - target_thread->transaction_stack = - target_thread->transaction_stack->from_parent; - t->from = NULL; + struct binder_node *node; + struct binder_ref *ref; + + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, desc, need_strong_ref); + if (!ref) + goto err_no_ref; + node = ref->node; + /* + * Take an implicit reference on the node to ensure + * it stays alive until the call to binder_put_node() + */ + binder_inc_node_tmpref(node); + if (rdata) + *rdata = ref->data; + binder_proc_unlock(proc); + + return node; + +err_no_ref: + binder_proc_unlock(proc); + return NULL; +} + +/** + * binder_free_ref() - free the binder_ref + * @ref: ref to free + * + * Free the binder_ref. Free the binder_node indicated by ref->node + * (if non-NULL) and the binder_ref_death indicated by ref->death. + */ +static void binder_free_ref(struct binder_ref *ref) +{ + if (ref->node) + binder_free_node(ref->node); + kfree(ref->death); + kfree(ref); +} + +/** + * binder_update_ref_for_handle() - inc/dec the ref for given handle + * @proc: proc containing the ref + * @desc: the handle associated with the ref + * @increment: true=inc reference, false=dec reference + * @strong: true=strong reference, false=weak reference + * @rdata: the id/refcount data for the ref + * + * Given a proc and ref handle, increment or decrement the ref + * according to "increment" arg. + * + * Return: 0 if successful, else errno + */ +static int binder_update_ref_for_handle(struct binder_proc *proc, + uint32_t desc, bool increment, bool strong, + struct binder_ref_data *rdata) +{ + int ret = 0; + struct binder_ref *ref; + bool delete_ref = false; + + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, desc, strong); + if (!ref) { + ret = -EINVAL; + goto err_no_ref; + } + if (increment) + ret = binder_inc_ref_olocked(ref, strong, NULL); + else + delete_ref = binder_dec_ref_olocked(ref, strong); + + if (rdata) + *rdata = ref->data; + binder_proc_unlock(proc); + + if (delete_ref) + binder_free_ref(ref); + return ret; + +err_no_ref: + binder_proc_unlock(proc); + return ret; +} + +/** + * binder_dec_ref_for_handle() - dec the ref for given handle + * @proc: proc containing the ref + * @desc: the handle associated with the ref + * @strong: true=strong reference, false=weak reference + * @rdata: the id/refcount data for the ref + * + * Just calls binder_update_ref_for_handle() to decrement the ref. + * + * Return: 0 if successful, else errno + */ +static int binder_dec_ref_for_handle(struct binder_proc *proc, + uint32_t desc, bool strong, struct binder_ref_data *rdata) +{ + return binder_update_ref_for_handle(proc, desc, false, strong, rdata); +} + + +/** + * binder_inc_ref_for_node() - increment the ref for given proc/node + * @proc: proc containing the ref + * @node: target node + * @strong: true=strong reference, false=weak reference + * @target_list: worklist to use if node is incremented + * @rdata: the id/refcount data for the ref + * + * Given a proc and node, increment the ref. Create the ref if it + * doesn't already exist + * + * Return: 0 if successful, else errno + */ +static int binder_inc_ref_for_node(struct binder_proc *proc, + struct binder_node *node, + bool strong, + struct list_head *target_list, + struct binder_ref_data *rdata) +{ + struct binder_ref *ref; + struct binder_ref *new_ref = NULL; + int ret = 0; + + binder_proc_lock(proc); + ref = binder_get_ref_for_node_olocked(proc, node, NULL); + if (!ref) { + binder_proc_unlock(proc); + new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); + if (!new_ref) + return -ENOMEM; + binder_proc_lock(proc); + ref = binder_get_ref_for_node_olocked(proc, node, new_ref); + } + ret = binder_inc_ref_olocked(ref, strong, target_list); + *rdata = ref->data; + binder_proc_unlock(proc); + if (new_ref && ref != new_ref) + /* + * Another thread created the ref first so + * free the one we allocated + */ + kfree(new_ref); + return ret; +} + +static void binder_pop_transaction_ilocked(struct binder_thread *target_thread, + struct binder_transaction *t) +{ + BUG_ON(!target_thread); + BUG_ON(!spin_is_locked(&target_thread->proc->inner_lock)); + BUG_ON(target_thread->transaction_stack != t); + BUG_ON(target_thread->transaction_stack->from != target_thread); + target_thread->transaction_stack = + target_thread->transaction_stack->from_parent; + t->from = NULL; +} + +/** + * binder_thread_dec_tmpref() - decrement thread->tmp_ref + * @thread: thread to decrement + * + * A thread needs to be kept alive while being used to create or + * handle a transaction. binder_get_txn_from() is used to safely + * extract t->from from a binder_transaction and keep the thread + * indicated by t->from from being freed. When done with that + * binder_thread, this function is called to decrement the + * tmp_ref and free if appropriate (thread has been released + * and no transaction being processed by the driver) + */ +static void binder_thread_dec_tmpref(struct binder_thread *thread) +{ + /* + * atomic is used to protect the counter value while + * it cannot reach zero or thread->is_dead is false + */ + binder_inner_proc_lock(thread->proc); + atomic_dec(&thread->tmp_ref); + if (thread->is_dead && !atomic_read(&thread->tmp_ref)) { + binder_inner_proc_unlock(thread->proc); + binder_free_thread(thread); + return; + } + binder_inner_proc_unlock(thread->proc); +} + +/** + * binder_proc_dec_tmpref() - decrement proc->tmp_ref + * @proc: proc to decrement + * + * A binder_proc needs to be kept alive while being used to create or + * handle a transaction. proc->tmp_ref is incremented when + * creating a new transaction or the binder_proc is currently in-use + * by threads that are being released. When done with the binder_proc, + * this function is called to decrement the counter and free the + * proc if appropriate (proc has been released, all threads have + * been released and not currenly in-use to process a transaction). + */ +static void binder_proc_dec_tmpref(struct binder_proc *proc) +{ + binder_inner_proc_lock(proc); + proc->tmp_ref--; + if (proc->is_dead && RB_EMPTY_ROOT(&proc->threads) && + !proc->tmp_ref) { + binder_inner_proc_unlock(proc); + binder_free_proc(proc); + return; + } + binder_inner_proc_unlock(proc); +} + +/** + * binder_get_txn_from() - safely extract the "from" thread in transaction + * @t: binder transaction for t->from + * + * Atomically return the "from" thread and increment the tmp_ref + * count for the thread to ensure it stays alive until + * binder_thread_dec_tmpref() is called. + * + * Return: the value of t->from + */ +static struct binder_thread *binder_get_txn_from( + struct binder_transaction *t) +{ + struct binder_thread *from; + + spin_lock(&t->lock); + from = t->from; + if (from) + atomic_inc(&from->tmp_ref); + spin_unlock(&t->lock); + return from; +} + +/** + * binder_get_txn_from_and_acq_inner() - get t->from and acquire inner lock + * @t: binder transaction for t->from + * + * Same as binder_get_txn_from() except it also acquires the proc->inner_lock + * to guarantee that the thread cannot be released while operating on it. + * The caller must call binder_inner_proc_unlock() to release the inner lock + * as well as call binder_dec_thread_txn() to release the reference. + * + * Return: the value of t->from + */ +static struct binder_thread *binder_get_txn_from_and_acq_inner( + struct binder_transaction *t) +{ + struct binder_thread *from; + + from = binder_get_txn_from(t); + if (!from) + return NULL; + binder_inner_proc_lock(from->proc); + if (t->from) { + BUG_ON(from != t->from); + return from; } - t->need_reply = 0; + binder_inner_proc_unlock(from->proc); + binder_thread_dec_tmpref(from); + return NULL; +} + +static void binder_free_transaction(struct binder_transaction *t) +{ if (t->buffer) t->buffer->transaction = NULL; kfree(t); @@ -1235,30 +1772,28 @@ static void binder_send_failed_reply(struct binder_transaction *t, BUG_ON(t->flags & TF_ONE_WAY); while (1) { - target_thread = t->from; + target_thread = binder_get_txn_from_and_acq_inner(t); if (target_thread) { - if (target_thread->return_error != BR_OK && - target_thread->return_error2 == BR_OK) { - target_thread->return_error2 = - target_thread->return_error; - target_thread->return_error = BR_OK; - } - if (target_thread->return_error == BR_OK) { - binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, - "send failed reply for transaction %d to %d:%d\n", - t->debug_id, - target_thread->proc->pid, - target_thread->pid); - - binder_pop_transaction(target_thread, t); - target_thread->return_error = error_code; + binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, + "send failed reply for transaction %d to %d:%d\n", + t->debug_id, + target_thread->proc->pid, + target_thread->pid); + + binder_pop_transaction_ilocked(target_thread, t); + if (target_thread->reply_error.cmd == BR_OK) { + target_thread->reply_error.cmd = error_code; + binder_enqueue_work_ilocked( + &target_thread->reply_error.work, + &target_thread->todo); wake_up_interruptible(&target_thread->wait); } else { - pr_err("reply failed, target thread, %d:%d, has error code %d already\n", - target_thread->proc->pid, - target_thread->pid, - target_thread->return_error); + WARN(1, "Unexpected reply error: %u\n", + target_thread->reply_error.cmd); } + binder_inner_proc_unlock(target_thread->proc); + binder_thread_dec_tmpref(target_thread); + binder_free_transaction(t); return; } next = t->from_parent; @@ -1267,7 +1802,7 @@ static void binder_send_failed_reply(struct binder_transaction *t, "send failed reply for transaction %d, target dead\n", t->debug_id); - binder_pop_transaction(target_thread, t); + binder_free_transaction(t); if (next == NULL) { binder_debug(BINDER_DEBUG_DEAD_BINDER, "reply failed, no target thread at root\n"); @@ -1476,24 +2011,26 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, node->debug_id, (u64)node->ptr); binder_dec_node(node, hdr->type == BINDER_TYPE_BINDER, 0); + binder_put_node(node); } break; case BINDER_TYPE_HANDLE: case BINDER_TYPE_WEAK_HANDLE: { struct flat_binder_object *fp; - struct binder_ref *ref; + struct binder_ref_data rdata; + int ret; fp = to_flat_binder_object(hdr); - ref = binder_get_ref(proc, fp->handle, - hdr->type == BINDER_TYPE_HANDLE); - if (ref == NULL) { - pr_err("transaction release %d bad handle %d\n", - debug_id, fp->handle); + ret = binder_dec_ref_for_handle(proc, fp->handle, + hdr->type == BINDER_TYPE_HANDLE, &rdata); + + if (ret) { + pr_err("transaction release %d bad handle %d, ret = %d\n", + debug_id, fp->handle, ret); break; } binder_debug(BINDER_DEBUG_TRANSACTION, - " ref %d desc %d (node %d)\n", - ref->debug_id, ref->desc, ref->node->debug_id); - binder_dec_ref(ref, hdr->type == BINDER_TYPE_HANDLE); + " ref %d desc %d\n", + rdata.debug_id, rdata.desc); } break; case BINDER_TYPE_FD: { @@ -1532,7 +2069,8 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, * back to kernel address space to access it */ parent_buffer = parent->buffer - - proc->user_buffer_offset; + binder_alloc_get_user_buffer_offset( + &proc->alloc); fd_buf_size = sizeof(u32) * fda->num_fds; if (fda->num_fds >= SIZE_MAX / sizeof(u32)) { @@ -1564,102 +2102,122 @@ static int binder_translate_binder(struct flat_binder_object *fp, struct binder_thread *thread) { struct binder_node *node; - struct binder_ref *ref; struct binder_proc *proc = thread->proc; struct binder_proc *target_proc = t->to_proc; + struct binder_ref_data rdata; + int ret = 0; node = binder_get_node(proc, fp->binder); if (!node) { - node = binder_new_node(proc, fp->binder, fp->cookie); + node = binder_new_node(proc, fp); if (!node) return -ENOMEM; - - node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK; - node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS); } if (fp->cookie != node->cookie) { binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n", proc->pid, thread->pid, (u64)fp->binder, node->debug_id, (u64)fp->cookie, (u64)node->cookie); - return -EINVAL; + ret = -EINVAL; + goto done; + } + if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { + ret = -EPERM; + goto done; } - if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) - return -EPERM; - ref = binder_get_ref_for_node(target_proc, node); - if (!ref) - return -EINVAL; + ret = binder_inc_ref_for_node(target_proc, node, + fp->hdr.type == BINDER_TYPE_BINDER, + &thread->todo, &rdata); + if (ret) + goto done; if (fp->hdr.type == BINDER_TYPE_BINDER) fp->hdr.type = BINDER_TYPE_HANDLE; else fp->hdr.type = BINDER_TYPE_WEAK_HANDLE; fp->binder = 0; - fp->handle = ref->desc; + fp->handle = rdata.desc; fp->cookie = 0; - binder_inc_ref(ref, fp->hdr.type == BINDER_TYPE_HANDLE, &thread->todo); - trace_binder_transaction_node_to_ref(t, node, ref); + trace_binder_transaction_node_to_ref(t, node, &rdata); binder_debug(BINDER_DEBUG_TRANSACTION, " node %d u%016llx -> ref %d desc %d\n", node->debug_id, (u64)node->ptr, - ref->debug_id, ref->desc); - - return 0; + rdata.debug_id, rdata.desc); +done: + binder_put_node(node); + return ret; } static int binder_translate_handle(struct flat_binder_object *fp, struct binder_transaction *t, struct binder_thread *thread) { - struct binder_ref *ref; struct binder_proc *proc = thread->proc; struct binder_proc *target_proc = t->to_proc; + struct binder_node *node; + struct binder_ref_data src_rdata; + int ret = 0; - ref = binder_get_ref(proc, fp->handle, - fp->hdr.type == BINDER_TYPE_HANDLE); - if (!ref) { + node = binder_get_node_from_ref(proc, fp->handle, + fp->hdr.type == BINDER_TYPE_HANDLE, &src_rdata); + if (!node) { binder_user_error("%d:%d got transaction with invalid handle, %d\n", proc->pid, thread->pid, fp->handle); return -EINVAL; } - if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) - return -EPERM; + if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) { + ret = -EPERM; + goto done; + } - if (ref->node->proc == target_proc) { + binder_node_lock(node); + if (node->proc == target_proc) { if (fp->hdr.type == BINDER_TYPE_HANDLE) fp->hdr.type = BINDER_TYPE_BINDER; else fp->hdr.type = BINDER_TYPE_WEAK_BINDER; - fp->binder = ref->node->ptr; - fp->cookie = ref->node->cookie; - binder_inc_node(ref->node, fp->hdr.type == BINDER_TYPE_BINDER, - 0, NULL); - trace_binder_transaction_ref_to_node(t, ref); + fp->binder = node->ptr; + fp->cookie = node->cookie; + if (node->proc) + binder_inner_proc_lock(node->proc); + binder_inc_node_nilocked(node, + fp->hdr.type == BINDER_TYPE_BINDER, + 0, NULL); + if (node->proc) + binder_inner_proc_unlock(node->proc); + trace_binder_transaction_ref_to_node(t, node, &src_rdata); binder_debug(BINDER_DEBUG_TRANSACTION, " ref %d desc %d -> node %d u%016llx\n", - ref->debug_id, ref->desc, ref->node->debug_id, - (u64)ref->node->ptr); + src_rdata.debug_id, src_rdata.desc, node->debug_id, + (u64)node->ptr); + binder_node_unlock(node); } else { - struct binder_ref *new_ref; + int ret; + struct binder_ref_data dest_rdata; - new_ref = binder_get_ref_for_node(target_proc, ref->node); - if (!new_ref) - return -EINVAL; + binder_node_unlock(node); + ret = binder_inc_ref_for_node(target_proc, node, + fp->hdr.type == BINDER_TYPE_HANDLE, + NULL, &dest_rdata); + if (ret) + goto done; fp->binder = 0; - fp->handle = new_ref->desc; + fp->handle = dest_rdata.desc; fp->cookie = 0; - binder_inc_ref(new_ref, fp->hdr.type == BINDER_TYPE_HANDLE, - NULL); - trace_binder_transaction_ref_to_ref(t, ref, new_ref); + trace_binder_transaction_ref_to_ref(t, node, &src_rdata, + &dest_rdata); binder_debug(BINDER_DEBUG_TRANSACTION, " ref %d desc %d -> ref %d desc %d (node %d)\n", - ref->debug_id, ref->desc, new_ref->debug_id, - new_ref->desc, ref->node->debug_id); + src_rdata.debug_id, src_rdata.desc, + dest_rdata.debug_id, dest_rdata.desc, + node->debug_id); } - return 0; +done: + binder_put_node(node); + return ret; } static int binder_translate_fd(int fd, @@ -1750,7 +2308,8 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, * Since the parent was already fixed up, convert it * back to the kernel address space to access it */ - parent_buffer = parent->buffer - target_proc->user_buffer_offset; + parent_buffer = parent->buffer - + binder_alloc_get_user_buffer_offset(&target_proc->alloc); fd_array = (u32 *)(parent_buffer + fda->parent_offset); if (!IS_ALIGNED((unsigned long)fd_array, sizeof(u32))) { binder_user_error("%d:%d parent offset not aligned correctly.\n", @@ -1818,7 +2377,8 @@ static int binder_fixup_parent(struct binder_transaction *t, return -EINVAL; } parent_buffer = (u8 *)(parent->buffer - - target_proc->user_buffer_offset); + binder_alloc_get_user_buffer_offset( + &target_proc->alloc)); *(binder_uintptr_t *)(parent_buffer + bp->parent_offset) = bp->buffer; return 0; @@ -1835,19 +2395,23 @@ static void binder_transaction(struct binder_proc *proc, binder_size_t *offp, *off_end, *off_start; binder_size_t off_min; u8 *sg_bufp, *sg_buf_end; - struct binder_proc *target_proc; + struct binder_proc *target_proc = NULL; struct binder_thread *target_thread = NULL; struct binder_node *target_node = NULL; struct list_head *target_list; wait_queue_head_t *target_wait; struct binder_transaction *in_reply_to = NULL; struct binder_transaction_log_entry *e; - uint32_t return_error; + uint32_t return_error = 0; + uint32_t return_error_param = 0; + uint32_t return_error_line = 0; struct binder_buffer_object *last_fixup_obj = NULL; binder_size_t last_fixup_min_off = 0; struct binder_context *context = proc->context; + int t_debug_id = atomic_inc_return(&binder_last_id); e = binder_transaction_log_add(&binder_transaction_log); + e->debug_id = t_debug_id; e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY); e->from_proc = proc->pid; e->from_thread = thread->pid; @@ -1857,29 +2421,40 @@ static void binder_transaction(struct binder_proc *proc, e->context_name = proc->context->name; if (reply) { + binder_inner_proc_lock(proc); in_reply_to = thread->transaction_stack; if (in_reply_to == NULL) { + binder_inner_proc_unlock(proc); binder_user_error("%d:%d got reply transaction with no transaction stack\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EPROTO; + return_error_line = __LINE__; goto err_empty_call_stack; } - binder_set_nice(in_reply_to->saved_priority); if (in_reply_to->to_thread != thread) { + spin_lock(&in_reply_to->lock); binder_user_error("%d:%d got reply transaction with bad transaction stack, transaction %d has target %d:%d\n", proc->pid, thread->pid, in_reply_to->debug_id, in_reply_to->to_proc ? in_reply_to->to_proc->pid : 0, in_reply_to->to_thread ? in_reply_to->to_thread->pid : 0); + spin_unlock(&in_reply_to->lock); + binder_inner_proc_unlock(proc); return_error = BR_FAILED_REPLY; + return_error_param = -EPROTO; + return_error_line = __LINE__; in_reply_to = NULL; goto err_bad_call_stack; } thread->transaction_stack = in_reply_to->to_parent; - target_thread = in_reply_to->from; + binder_inner_proc_unlock(proc); + binder_set_nice(in_reply_to->saved_priority); + target_thread = binder_get_txn_from_and_acq_inner(in_reply_to); if (target_thread == NULL) { return_error = BR_DEAD_REPLY; + return_error_line = __LINE__; goto err_dead_binder; } if (target_thread->transaction_stack != in_reply_to) { @@ -1888,61 +2463,111 @@ static void binder_transaction(struct binder_proc *proc, target_thread->transaction_stack ? target_thread->transaction_stack->debug_id : 0, in_reply_to->debug_id); + binder_inner_proc_unlock(target_thread->proc); return_error = BR_FAILED_REPLY; + return_error_param = -EPROTO; + return_error_line = __LINE__; in_reply_to = NULL; target_thread = NULL; goto err_dead_binder; } target_proc = target_thread->proc; + target_proc->tmp_ref++; + binder_inner_proc_unlock(target_thread->proc); } else { if (tr->target.handle) { struct binder_ref *ref; - ref = binder_get_ref(proc, tr->target.handle, true); - if (ref == NULL) { + /* + * There must already be a strong ref + * on this node. If so, do a strong + * increment on the node to ensure it + * stays alive until the transaction is + * done. + */ + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, tr->target.handle, + true); + if (ref) { + binder_inc_node(ref->node, 1, 0, NULL); + target_node = ref->node; + } + binder_proc_unlock(proc); + if (target_node == NULL) { binder_user_error("%d:%d got transaction to invalid handle\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_invalid_target_handle; } - target_node = ref->node; } else { + mutex_lock(&context->context_mgr_node_lock); target_node = context->binder_context_mgr_node; if (target_node == NULL) { return_error = BR_DEAD_REPLY; + mutex_unlock(&context->context_mgr_node_lock); + return_error_line = __LINE__; goto err_no_context_mgr_node; } + binder_inc_node(target_node, 1, 0, NULL); + mutex_unlock(&context->context_mgr_node_lock); } e->to_node = target_node->debug_id; + binder_node_lock(target_node); target_proc = target_node->proc; if (target_proc == NULL) { + binder_node_unlock(target_node); return_error = BR_DEAD_REPLY; + return_error_line = __LINE__; goto err_dead_binder; } + binder_inner_proc_lock(target_proc); + target_proc->tmp_ref++; + binder_inner_proc_unlock(target_proc); + binder_node_unlock(target_node); if (security_binder_transaction(proc->tsk, target_proc->tsk) < 0) { return_error = BR_FAILED_REPLY; + return_error_param = -EPERM; + return_error_line = __LINE__; goto err_invalid_target_handle; } + binder_inner_proc_lock(proc); if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { struct binder_transaction *tmp; tmp = thread->transaction_stack; if (tmp->to_thread != thread) { + spin_lock(&tmp->lock); binder_user_error("%d:%d got new transaction with bad transaction stack, transaction %d has target %d:%d\n", proc->pid, thread->pid, tmp->debug_id, tmp->to_proc ? tmp->to_proc->pid : 0, tmp->to_thread ? tmp->to_thread->pid : 0); + spin_unlock(&tmp->lock); + binder_inner_proc_unlock(proc); return_error = BR_FAILED_REPLY; + return_error_param = -EPROTO; + return_error_line = __LINE__; goto err_bad_call_stack; } while (tmp) { - if (tmp->from && tmp->from->proc == target_proc) - target_thread = tmp->from; + struct binder_thread *from; + + spin_lock(&tmp->lock); + from = tmp->from; + if (from && from->proc == target_proc) { + atomic_inc(&from->tmp_ref); + target_thread = from; + spin_unlock(&tmp->lock); + break; + } + spin_unlock(&tmp->lock); tmp = tmp->from_parent; } } + binder_inner_proc_unlock(proc); } if (target_thread) { e->to_thread = target_thread->pid; @@ -1958,19 +2583,23 @@ static void binder_transaction(struct binder_proc *proc, t = kzalloc(sizeof(*t), GFP_KERNEL); if (t == NULL) { return_error = BR_FAILED_REPLY; + return_error_param = -ENOMEM; + return_error_line = __LINE__; goto err_alloc_t_failed; } binder_stats_created(BINDER_STAT_TRANSACTION); + spin_lock_init(&t->lock); tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); if (tcomplete == NULL) { return_error = BR_FAILED_REPLY; + return_error_param = -ENOMEM; + return_error_line = __LINE__; goto err_alloc_tcomplete_failed; } binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE); - t->debug_id = ++binder_last_id; - e->debug_id = t->debug_id; + t->debug_id = t_debug_id; if (reply) binder_debug(BINDER_DEBUG_TRANSACTION, @@ -2004,11 +2633,18 @@ static void binder_transaction(struct binder_proc *proc, trace_binder_transaction(reply, t, target_node); - t->buffer = binder_alloc_buf(target_proc, tr->data_size, + t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size, tr->offsets_size, extra_buffers_size, !reply && (t->flags & TF_ONE_WAY)); - if (t->buffer == NULL) { - return_error = BR_FAILED_REPLY; + if (IS_ERR(t->buffer)) { + /* + * -ESRCH indicates VMA cleared. The target is dying. + */ + return_error_param = PTR_ERR(t->buffer); + return_error = return_error_param == -ESRCH ? + BR_DEAD_REPLY : BR_FAILED_REPLY; + return_error_line = __LINE__; + t->buffer = NULL; goto err_binder_alloc_buf_failed; } t->buffer->allow_user_free = 0; @@ -2016,9 +2652,6 @@ static void binder_transaction(struct binder_proc *proc, t->buffer->transaction = t; t->buffer->target_node = target_node; trace_binder_transaction_alloc_buf(t->buffer); - if (target_node) - binder_inc_node(target_node, 1, 0, NULL); - off_start = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *))); offp = off_start; @@ -2028,6 +2661,8 @@ static void binder_transaction(struct binder_proc *proc, binder_user_error("%d:%d got transaction with invalid data ptr\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EFAULT; + return_error_line = __LINE__; goto err_copy_data_failed; } if (copy_from_user(offp, (const void __user *)(uintptr_t) @@ -2035,12 +2670,16 @@ static void binder_transaction(struct binder_proc *proc, binder_user_error("%d:%d got transaction with invalid offsets ptr\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EFAULT; + return_error_line = __LINE__; goto err_copy_data_failed; } if (!IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))) { binder_user_error("%d:%d got transaction with invalid offsets size, %lld\n", proc->pid, thread->pid, (u64)tr->offsets_size); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_offset; } if (!IS_ALIGNED(extra_buffers_size, sizeof(u64))) { @@ -2048,6 +2687,8 @@ static void binder_transaction(struct binder_proc *proc, proc->pid, thread->pid, (u64)extra_buffers_size); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_offset; } off_end = (void *)off_start + tr->offsets_size; @@ -2064,6 +2705,8 @@ static void binder_transaction(struct binder_proc *proc, (u64)off_min, (u64)t->buffer->data_size); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_offset; } @@ -2078,6 +2721,8 @@ static void binder_transaction(struct binder_proc *proc, ret = binder_translate_binder(fp, t, thread); if (ret < 0) { return_error = BR_FAILED_REPLY; + return_error_param = ret; + return_error_line = __LINE__; goto err_translate_failed; } } break; @@ -2089,6 +2734,8 @@ static void binder_transaction(struct binder_proc *proc, ret = binder_translate_handle(fp, t, thread); if (ret < 0) { return_error = BR_FAILED_REPLY; + return_error_param = ret; + return_error_line = __LINE__; goto err_translate_failed; } } break; @@ -2100,6 +2747,8 @@ static void binder_transaction(struct binder_proc *proc, if (target_fd < 0) { return_error = BR_FAILED_REPLY; + return_error_param = target_fd; + return_error_line = __LINE__; goto err_translate_failed; } fp->pad_binder = 0; @@ -2116,6 +2765,8 @@ static void binder_transaction(struct binder_proc *proc, binder_user_error("%d:%d got transaction with invalid parent offset or type\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_parent; } if (!binder_validate_fixup(t->buffer, off_start, @@ -2125,12 +2776,16 @@ static void binder_transaction(struct binder_proc *proc, binder_user_error("%d:%d got transaction with out-of-order buffer fixup\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_parent; } ret = binder_translate_fd_array(fda, parent, t, thread, in_reply_to); if (ret < 0) { return_error = BR_FAILED_REPLY; + return_error_param = ret; + return_error_line = __LINE__; goto err_translate_failed; } last_fixup_obj = parent; @@ -2146,6 +2801,8 @@ static void binder_transaction(struct binder_proc *proc, binder_user_error("%d:%d got transaction with too large buffer\n", proc->pid, thread->pid); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_offset; } if (copy_from_user(sg_bufp, @@ -2153,12 +2810,15 @@ static void binder_transaction(struct binder_proc *proc, bp->buffer, bp->length)) { binder_user_error("%d:%d got transaction with invalid offsets ptr\n", proc->pid, thread->pid); + return_error_param = -EFAULT; return_error = BR_FAILED_REPLY; + return_error_line = __LINE__; goto err_copy_data_failed; } /* Fixup buffer pointer to target proc address space */ bp->buffer = (uintptr_t)sg_bufp + - target_proc->user_buffer_offset; + binder_alloc_get_user_buffer_offset( + &target_proc->alloc); sg_bufp += ALIGN(bp->length, sizeof(u64)); ret = binder_fixup_parent(t, thread, bp, off_start, @@ -2167,6 +2827,8 @@ static void binder_transaction(struct binder_proc *proc, last_fixup_min_off); if (ret < 0) { return_error = BR_FAILED_REPLY; + return_error_param = ret; + return_error_line = __LINE__; goto err_translate_failed; } last_fixup_obj = bp; @@ -2176,34 +2838,89 @@ static void binder_transaction(struct binder_proc *proc, binder_user_error("%d:%d got transaction with invalid object type, %x\n", proc->pid, thread->pid, hdr->type); return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; goto err_bad_object_type; } } + tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; + binder_enqueue_work(proc, tcomplete, &thread->todo); + t->work.type = BINDER_WORK_TRANSACTION; + if (reply) { + binder_inner_proc_lock(target_proc); + if (target_thread->is_dead) { + binder_inner_proc_unlock(target_proc); + goto err_dead_proc_or_thread; + } BUG_ON(t->buffer->async_transaction != 0); - binder_pop_transaction(target_thread, in_reply_to); + binder_pop_transaction_ilocked(target_thread, in_reply_to); + binder_enqueue_work_ilocked(&t->work, target_list); + binder_inner_proc_unlock(target_proc); + binder_free_transaction(in_reply_to); } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); + binder_inner_proc_lock(proc); t->need_reply = 1; t->from_parent = thread->transaction_stack; thread->transaction_stack = t; + binder_inner_proc_unlock(proc); + binder_inner_proc_lock(target_proc); + if (target_proc->is_dead || + (target_thread && target_thread->is_dead)) { + binder_inner_proc_unlock(target_proc); + binder_inner_proc_lock(proc); + binder_pop_transaction_ilocked(thread, t); + binder_inner_proc_unlock(proc); + goto err_dead_proc_or_thread; + } + binder_enqueue_work_ilocked(&t->work, target_list); + binder_inner_proc_unlock(target_proc); } else { BUG_ON(target_node == NULL); BUG_ON(t->buffer->async_transaction != 1); + binder_node_lock(target_node); if (target_node->has_async_transaction) { target_list = &target_node->async_todo; target_wait = NULL; } else target_node->has_async_transaction = 1; + /* + * Test/set of has_async_transaction + * must be atomic with enqueue on + * async_todo + */ + binder_inner_proc_lock(target_proc); + if (target_proc->is_dead || + (target_thread && target_thread->is_dead)) { + binder_inner_proc_unlock(target_proc); + binder_node_unlock(target_node); + goto err_dead_proc_or_thread; + } + binder_enqueue_work_ilocked(&t->work, target_list); + binder_inner_proc_unlock(target_proc); + binder_node_unlock(target_node); } - t->work.type = BINDER_WORK_TRANSACTION; - list_add_tail(&t->work.entry, target_list); - tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; - list_add_tail(&tcomplete->entry, &thread->todo); - if (target_wait) - wake_up_interruptible(target_wait); + if (target_wait) { + if (reply || !(tr->flags & TF_ONE_WAY)) + wake_up_interruptible_sync(target_wait); + else + wake_up_interruptible(target_wait); + } + if (target_thread) + binder_thread_dec_tmpref(target_thread); + binder_proc_dec_tmpref(target_proc); + /* + * write barrier to synchronize with initialization + * of log entry + */ + smp_wmb(); + WRITE_ONCE(e->debug_id_done, t_debug_id); return; +err_dead_proc_or_thread: + return_error = BR_DEAD_REPLY; + return_error_line = __LINE__; err_translate_failed: err_bad_object_type: err_bad_offset: @@ -2211,8 +2928,9 @@ err_bad_parent: err_copy_data_failed: trace_binder_transaction_failed_buffer_release(t->buffer); binder_transaction_buffer_release(target_proc, t->buffer, offp); + target_node = NULL; t->buffer->transaction = NULL; - binder_free_buf(target_proc, t->buffer); + binder_alloc_free_buf(&target_proc->alloc, t->buffer); err_binder_alloc_buf_failed: kfree(tcomplete); binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); @@ -2225,24 +2943,49 @@ err_empty_call_stack: err_dead_binder: err_invalid_target_handle: err_no_context_mgr_node: + if (target_thread) + binder_thread_dec_tmpref(target_thread); + if (target_proc) + binder_proc_dec_tmpref(target_proc); + if (target_node) + binder_dec_node(target_node, 1, 0); + binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, - "%d:%d transaction failed %d, size %lld-%lld\n", - proc->pid, thread->pid, return_error, - (u64)tr->data_size, (u64)tr->offsets_size); + "%d:%d transaction failed %d/%d, size %lld-%lld line %d\n", + proc->pid, thread->pid, return_error, return_error_param, + (u64)tr->data_size, (u64)tr->offsets_size, + return_error_line); { struct binder_transaction_log_entry *fe; + e->return_error = return_error; + e->return_error_param = return_error_param; + e->return_error_line = return_error_line; fe = binder_transaction_log_add(&binder_transaction_log_failed); *fe = *e; + /* + * write barrier to synchronize with initialization + * of log entry + */ + smp_wmb(); + WRITE_ONCE(e->debug_id_done, t_debug_id); + WRITE_ONCE(fe->debug_id_done, t_debug_id); } - BUG_ON(thread->return_error != BR_OK); + BUG_ON(thread->return_error.cmd != BR_OK); if (in_reply_to) { - thread->return_error = BR_TRANSACTION_COMPLETE; + thread->return_error.cmd = BR_TRANSACTION_COMPLETE; + binder_enqueue_work(thread->proc, + &thread->return_error.work, + &thread->todo); binder_send_failed_reply(in_reply_to, return_error); - } else - thread->return_error = return_error; + } else { + thread->return_error.cmd = return_error; + binder_enqueue_work(thread->proc, + &thread->return_error.work, + &thread->todo); + } } static int binder_thread_write(struct binder_proc *proc, @@ -2256,15 +2999,17 @@ static int binder_thread_write(struct binder_proc *proc, void __user *ptr = buffer + *consumed; void __user *end = buffer + size; - while (ptr < end && thread->return_error == BR_OK) { + while (ptr < end && thread->return_error.cmd == BR_OK) { + int ret; + if (get_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); trace_binder_command(cmd); if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) { - binder_stats.bc[_IOC_NR(cmd)]++; - proc->stats.bc[_IOC_NR(cmd)]++; - thread->stats.bc[_IOC_NR(cmd)]++; + atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]); + atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]); + atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]); } switch (cmd) { case BC_INCREFS: @@ -2272,53 +3017,61 @@ static int binder_thread_write(struct binder_proc *proc, case BC_RELEASE: case BC_DECREFS: { uint32_t target; - struct binder_ref *ref; const char *debug_string; + bool strong = cmd == BC_ACQUIRE || cmd == BC_RELEASE; + bool increment = cmd == BC_INCREFS || cmd == BC_ACQUIRE; + struct binder_ref_data rdata; if (get_user(target, (uint32_t __user *)ptr)) return -EFAULT; + ptr += sizeof(uint32_t); - if (target == 0 && context->binder_context_mgr_node && - (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) { - ref = binder_get_ref_for_node(proc, - context->binder_context_mgr_node); - if (ref->desc != target) { - binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n", - proc->pid, thread->pid, - ref->desc); - } - } else - ref = binder_get_ref(proc, target, - cmd == BC_ACQUIRE || - cmd == BC_RELEASE); - if (ref == NULL) { - binder_user_error("%d:%d refcount change on invalid ref %d\n", - proc->pid, thread->pid, target); - break; + ret = -1; + if (increment && !target) { + struct binder_node *ctx_mgr_node; + mutex_lock(&context->context_mgr_node_lock); + ctx_mgr_node = context->binder_context_mgr_node; + if (ctx_mgr_node) + ret = binder_inc_ref_for_node( + proc, ctx_mgr_node, + strong, NULL, &rdata); + mutex_unlock(&context->context_mgr_node_lock); + } + if (ret) + ret = binder_update_ref_for_handle( + proc, target, increment, strong, + &rdata); + if (!ret && rdata.desc != target) { + binder_user_error("%d:%d tried to acquire reference to desc %d, got %d instead\n", + proc->pid, thread->pid, + target, rdata.desc); } switch (cmd) { case BC_INCREFS: debug_string = "IncRefs"; - binder_inc_ref(ref, 0, NULL); break; case BC_ACQUIRE: debug_string = "Acquire"; - binder_inc_ref(ref, 1, NULL); break; case BC_RELEASE: debug_string = "Release"; - binder_dec_ref(ref, 1); break; case BC_DECREFS: default: debug_string = "DecRefs"; - binder_dec_ref(ref, 0); + break; + } + if (ret) { + binder_user_error("%d:%d %s %d refcount change on invalid ref %d ret %d\n", + proc->pid, thread->pid, debug_string, + strong, target, ret); break; } binder_debug(BINDER_DEBUG_USER_REFS, - "%d:%d %s ref %d desc %d s %d w %d for node %d\n", - proc->pid, thread->pid, debug_string, ref->debug_id, - ref->desc, ref->strong, ref->weak, ref->node->debug_id); + "%d:%d %s ref %d desc %d s %d w %d\n", + proc->pid, thread->pid, debug_string, + rdata.debug_id, rdata.desc, rdata.strong, + rdata.weak); break; } case BC_INCREFS_DONE: @@ -2326,6 +3079,7 @@ static int binder_thread_write(struct binder_proc *proc, binder_uintptr_t node_ptr; binder_uintptr_t cookie; struct binder_node *node; + bool free_node; if (get_user(node_ptr, (binder_uintptr_t __user *)ptr)) return -EFAULT; @@ -2350,13 +3104,17 @@ static int binder_thread_write(struct binder_proc *proc, "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE", (u64)node_ptr, node->debug_id, (u64)cookie, (u64)node->cookie); + binder_put_node(node); break; } + binder_node_inner_lock(node); if (cmd == BC_ACQUIRE_DONE) { if (node->pending_strong_ref == 0) { binder_user_error("%d:%d BC_ACQUIRE_DONE node %d has no pending acquire request\n", proc->pid, thread->pid, node->debug_id); + binder_node_inner_unlock(node); + binder_put_node(node); break; } node->pending_strong_ref = 0; @@ -2365,16 +3123,23 @@ static int binder_thread_write(struct binder_proc *proc, binder_user_error("%d:%d BC_INCREFS_DONE node %d has no pending increfs request\n", proc->pid, thread->pid, node->debug_id); + binder_node_inner_unlock(node); + binder_put_node(node); break; } node->pending_weak_ref = 0; } - binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0); + free_node = binder_dec_node_nilocked(node, + cmd == BC_ACQUIRE_DONE, 0); + WARN_ON(free_node); binder_debug(BINDER_DEBUG_USER_REFS, - "%d:%d %s node %d ls %d lw %d\n", + "%d:%d %s node %d ls %d lw %d tr %d\n", proc->pid, thread->pid, cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE", - node->debug_id, node->local_strong_refs, node->local_weak_refs); + node->debug_id, node->local_strong_refs, + node->local_weak_refs, node->tmp_refs); + binder_node_inner_unlock(node); + binder_put_node(node); break; } case BC_ATTEMPT_ACQUIRE: @@ -2392,7 +3157,8 @@ static int binder_thread_write(struct binder_proc *proc, return -EFAULT; ptr += sizeof(binder_uintptr_t); - buffer = binder_buffer_lookup(proc, data_ptr); + buffer = binder_alloc_prepare_to_free(&proc->alloc, + data_ptr); if (buffer == NULL) { binder_user_error("%d:%d BC_FREE_BUFFER u%016llx no match\n", proc->pid, thread->pid, (u64)data_ptr); @@ -2414,15 +3180,25 @@ static int binder_thread_write(struct binder_proc *proc, buffer->transaction = NULL; } if (buffer->async_transaction && buffer->target_node) { - BUG_ON(!buffer->target_node->has_async_transaction); - if (list_empty(&buffer->target_node->async_todo)) - buffer->target_node->has_async_transaction = 0; + struct binder_node *buf_node; + struct binder_work *w; + + buf_node = buffer->target_node; + binder_node_inner_lock(buf_node); + BUG_ON(!buf_node->has_async_transaction); + BUG_ON(buf_node->proc != proc); + w = binder_dequeue_work_head_ilocked( + &buf_node->async_todo); + if (!w) + buf_node->has_async_transaction = 0; else - list_move_tail(buffer->target_node->async_todo.next, &thread->todo); + binder_enqueue_work_ilocked( + w, &thread->todo); + binder_node_inner_unlock(buf_node); } trace_binder_transaction_buffer_release(buffer); binder_transaction_buffer_release(proc, buffer, NULL); - binder_free_buf(proc, buffer); + binder_alloc_free_buf(&proc->alloc, buffer); break; } @@ -2453,6 +3229,7 @@ static int binder_thread_write(struct binder_proc *proc, binder_debug(BINDER_DEBUG_THREADS, "%d:%d BC_REGISTER_LOOPER\n", proc->pid, thread->pid); + binder_inner_proc_lock(proc); if (thread->looper & BINDER_LOOPER_STATE_ENTERED) { thread->looper |= BINDER_LOOPER_STATE_INVALID; binder_user_error("%d:%d ERROR: BC_REGISTER_LOOPER called after BC_ENTER_LOOPER\n", @@ -2466,6 +3243,7 @@ static int binder_thread_write(struct binder_proc *proc, proc->requested_threads_started++; } thread->looper |= BINDER_LOOPER_STATE_REGISTERED; + binder_inner_proc_unlock(proc); break; case BC_ENTER_LOOPER: binder_debug(BINDER_DEBUG_THREADS, @@ -2490,7 +3268,7 @@ static int binder_thread_write(struct binder_proc *proc, uint32_t target; binder_uintptr_t cookie; struct binder_ref *ref; - struct binder_ref_death *death; + struct binder_ref_death *death = NULL; if (get_user(target, (uint32_t __user *)ptr)) return -EFAULT; @@ -2498,7 +3276,29 @@ static int binder_thread_write(struct binder_proc *proc, if (get_user(cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); - ref = binder_get_ref(proc, target, false); + if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { + /* + * Allocate memory for death notification + * before taking lock + */ + death = kzalloc(sizeof(*death), GFP_KERNEL); + if (death == NULL) { + WARN_ON(thread->return_error.cmd != + BR_OK); + thread->return_error.cmd = BR_ERROR; + binder_enqueue_work( + thread->proc, + &thread->return_error.work, + &thread->todo); + binder_debug( + BINDER_DEBUG_FAILED_TRANSACTION, + "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n", + proc->pid, thread->pid); + break; + } + } + binder_proc_lock(proc); + ref = binder_get_ref_olocked(proc, target, false); if (ref == NULL) { binder_user_error("%d:%d %s invalid ref %d\n", proc->pid, thread->pid, @@ -2506,6 +3306,8 @@ static int binder_thread_write(struct binder_proc *proc, "BC_REQUEST_DEATH_NOTIFICATION" : "BC_CLEAR_DEATH_NOTIFICATION", target); + binder_proc_unlock(proc); + kfree(death); break; } @@ -2515,21 +3317,18 @@ static int binder_thread_write(struct binder_proc *proc, cmd == BC_REQUEST_DEATH_NOTIFICATION ? "BC_REQUEST_DEATH_NOTIFICATION" : "BC_CLEAR_DEATH_NOTIFICATION", - (u64)cookie, ref->debug_id, ref->desc, - ref->strong, ref->weak, ref->node->debug_id); + (u64)cookie, ref->data.debug_id, + ref->data.desc, ref->data.strong, + ref->data.weak, ref->node->debug_id); + binder_node_lock(ref->node); if (cmd == BC_REQUEST_DEATH_NOTIFICATION) { if (ref->death) { binder_user_error("%d:%d BC_REQUEST_DEATH_NOTIFICATION death notification already set\n", proc->pid, thread->pid); - break; - } - death = kzalloc(sizeof(*death), GFP_KERNEL); - if (death == NULL) { - thread->return_error = BR_ERROR; - binder_debug(BINDER_DEBUG_FAILED_TRANSACTION, - "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n", - proc->pid, thread->pid); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); + kfree(death); break; } binder_stats_created(BINDER_STAT_DEATH); @@ -2538,17 +3337,28 @@ static int binder_thread_write(struct binder_proc *proc, ref->death = death; if (ref->node->proc == NULL) { ref->death->work.type = BINDER_WORK_DEAD_BINDER; - if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { - list_add_tail(&ref->death->work.entry, &thread->todo); - } else { - list_add_tail(&ref->death->work.entry, &proc->todo); - wake_up_interruptible(&proc->wait); + if (thread->looper & + (BINDER_LOOPER_STATE_REGISTERED | + BINDER_LOOPER_STATE_ENTERED)) + binder_enqueue_work( + proc, + &ref->death->work, + &thread->todo); + else { + binder_enqueue_work( + proc, + &ref->death->work, + &proc->todo); + wake_up_interruptible( + &proc->wait); } } } else { if (ref->death == NULL) { binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification not active\n", proc->pid, thread->pid); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); break; } death = ref->death; @@ -2557,22 +3367,35 @@ static int binder_thread_write(struct binder_proc *proc, proc->pid, thread->pid, (u64)death->cookie, (u64)cookie); + binder_node_unlock(ref->node); + binder_proc_unlock(proc); break; } ref->death = NULL; + binder_inner_proc_lock(proc); if (list_empty(&death->work.entry)) { death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; - if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { - list_add_tail(&death->work.entry, &thread->todo); - } else { - list_add_tail(&death->work.entry, &proc->todo); - wake_up_interruptible(&proc->wait); + if (thread->looper & + (BINDER_LOOPER_STATE_REGISTERED | + BINDER_LOOPER_STATE_ENTERED)) + binder_enqueue_work_ilocked( + &death->work, + &thread->todo); + else { + binder_enqueue_work_ilocked( + &death->work, + &proc->todo); + wake_up_interruptible( + &proc->wait); } } else { BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER); death->work.type = BINDER_WORK_DEAD_BINDER_AND_CLEAR; } + binder_inner_proc_unlock(proc); } + binder_node_unlock(ref->node); + binder_proc_unlock(proc); } break; case BC_DEAD_BINDER_DONE: { struct binder_work *w; @@ -2583,8 +3406,13 @@ static int binder_thread_write(struct binder_proc *proc, return -EFAULT; ptr += sizeof(cookie); - list_for_each_entry(w, &proc->delivered_death, entry) { - struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work); + binder_inner_proc_lock(proc); + list_for_each_entry(w, &proc->delivered_death, + entry) { + struct binder_ref_death *tmp_death = + container_of(w, + struct binder_ref_death, + work); if (tmp_death->cookie == cookie) { death = tmp_death; @@ -2598,19 +3426,25 @@ static int binder_thread_write(struct binder_proc *proc, if (death == NULL) { binder_user_error("%d:%d BC_DEAD_BINDER_DONE %016llx not found\n", proc->pid, thread->pid, (u64)cookie); + binder_inner_proc_unlock(proc); break; } - - list_del_init(&death->work.entry); + binder_dequeue_work_ilocked(&death->work); if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) { death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION; - if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) { - list_add_tail(&death->work.entry, &thread->todo); - } else { - list_add_tail(&death->work.entry, &proc->todo); + if (thread->looper & + (BINDER_LOOPER_STATE_REGISTERED | + BINDER_LOOPER_STATE_ENTERED)) + binder_enqueue_work_ilocked( + &death->work, &thread->todo); + else { + binder_enqueue_work_ilocked( + &death->work, + &proc->todo); wake_up_interruptible(&proc->wait); } } + binder_inner_proc_unlock(proc); } break; default: @@ -2628,23 +3462,54 @@ static void binder_stat_br(struct binder_proc *proc, { trace_binder_return(cmd); if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.br)) { - binder_stats.br[_IOC_NR(cmd)]++; - proc->stats.br[_IOC_NR(cmd)]++; - thread->stats.br[_IOC_NR(cmd)]++; + atomic_inc(&binder_stats.br[_IOC_NR(cmd)]); + atomic_inc(&proc->stats.br[_IOC_NR(cmd)]); + atomic_inc(&thread->stats.br[_IOC_NR(cmd)]); } } static int binder_has_proc_work(struct binder_proc *proc, struct binder_thread *thread) { - return !list_empty(&proc->todo) || - (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN); + return !binder_worklist_empty(proc, &proc->todo) || + thread->looper_need_return; } static int binder_has_thread_work(struct binder_thread *thread) { - return !list_empty(&thread->todo) || thread->return_error != BR_OK || - (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN); + return !binder_worklist_empty(thread->proc, &thread->todo) || + thread->looper_need_return; +} + +static int binder_put_node_cmd(struct binder_proc *proc, + struct binder_thread *thread, + void __user **ptrp, + binder_uintptr_t node_ptr, + binder_uintptr_t node_cookie, + int node_debug_id, + uint32_t cmd, const char *cmd_name) +{ + void __user *ptr = *ptrp; + + if (put_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + + if (put_user(node_ptr, (binder_uintptr_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(binder_uintptr_t); + + if (put_user(node_cookie, (binder_uintptr_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(binder_uintptr_t); + + binder_stat_br(proc, thread, cmd); + binder_debug(BINDER_DEBUG_USER_REFS, "%d:%d %s %d u%016llx c%016llx\n", + proc->pid, thread->pid, cmd_name, node_debug_id, + (u64)node_ptr, (u64)node_cookie); + + *ptrp = ptr; + return 0; } static int binder_thread_read(struct binder_proc *proc, @@ -2666,37 +3531,18 @@ static int binder_thread_read(struct binder_proc *proc, } retry: + binder_inner_proc_lock(proc); wait_for_proc_work = thread->transaction_stack == NULL && - list_empty(&thread->todo); - - if (thread->return_error != BR_OK && ptr < end) { - if (thread->return_error2 != BR_OK) { - if (put_user(thread->return_error2, (uint32_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(uint32_t); - binder_stat_br(proc, thread, thread->return_error2); - if (ptr == end) - goto done; - thread->return_error2 = BR_OK; - } - if (put_user(thread->return_error, (uint32_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(uint32_t); - binder_stat_br(proc, thread, thread->return_error); - thread->return_error = BR_OK; - goto done; - } - - - thread->looper |= BINDER_LOOPER_STATE_WAITING; + binder_worklist_empty_ilocked(&thread->todo); if (wait_for_proc_work) proc->ready_threads++; + binder_inner_proc_unlock(proc); - binder_unlock(__func__); + thread->looper |= BINDER_LOOPER_STATE_WAITING; trace_binder_wait_for_work(wait_for_proc_work, !!thread->transaction_stack, - !list_empty(&thread->todo)); + !binder_worklist_empty(proc, &thread->todo)); if (wait_for_proc_work) { if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED))) { @@ -2719,10 +3565,10 @@ retry: ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread)); } - binder_lock(__func__); - + binder_inner_proc_lock(proc); if (wait_for_proc_work) proc->ready_threads--; + binder_inner_proc_unlock(proc); thread->looper &= ~BINDER_LOOPER_STATE_WAITING; if (ret) @@ -2731,31 +3577,52 @@ retry: while (1) { uint32_t cmd; struct binder_transaction_data tr; - struct binder_work *w; + struct binder_work *w = NULL; + struct list_head *list = NULL; struct binder_transaction *t = NULL; + struct binder_thread *t_from; + + binder_inner_proc_lock(proc); + if (!binder_worklist_empty_ilocked(&thread->todo)) + list = &thread->todo; + else if (!binder_worklist_empty_ilocked(&proc->todo) && + wait_for_proc_work) + list = &proc->todo; + else { + binder_inner_proc_unlock(proc); - if (!list_empty(&thread->todo)) { - w = list_first_entry(&thread->todo, struct binder_work, - entry); - } else if (!list_empty(&proc->todo) && wait_for_proc_work) { - w = list_first_entry(&proc->todo, struct binder_work, - entry); - } else { /* no data added */ - if (ptr - buffer == 4 && - !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) + if (ptr - buffer == 4 && !thread->looper_need_return) goto retry; break; } - if (end - ptr < sizeof(tr) + 4) + if (end - ptr < sizeof(tr) + 4) { + binder_inner_proc_unlock(proc); break; + } + w = binder_dequeue_work_head_ilocked(list); switch (w->type) { case BINDER_WORK_TRANSACTION: { + binder_inner_proc_unlock(proc); t = container_of(w, struct binder_transaction, work); } break; + case BINDER_WORK_RETURN_ERROR: { + struct binder_error *e = container_of( + w, struct binder_error, work); + + WARN_ON(e->cmd == BR_OK); + binder_inner_proc_unlock(proc); + if (put_user(e->cmd, (uint32_t __user *)ptr)) + return -EFAULT; + e->cmd = BR_OK; + ptr += sizeof(uint32_t); + + binder_stat_br(proc, thread, e->cmd); + } break; case BINDER_WORK_TRANSACTION_COMPLETE: { + binder_inner_proc_unlock(proc); cmd = BR_TRANSACTION_COMPLETE; if (put_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; @@ -2765,113 +3632,134 @@ retry: binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE, "%d:%d BR_TRANSACTION_COMPLETE\n", proc->pid, thread->pid); - - list_del(&w->entry); kfree(w); binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); } break; case BINDER_WORK_NODE: { struct binder_node *node = container_of(w, struct binder_node, work); - uint32_t cmd = BR_NOOP; - const char *cmd_name; - int strong = node->internal_strong_refs || node->local_strong_refs; - int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong; - - if (weak && !node->has_weak_ref) { - cmd = BR_INCREFS; - cmd_name = "BR_INCREFS"; + int strong, weak; + binder_uintptr_t node_ptr = node->ptr; + binder_uintptr_t node_cookie = node->cookie; + int node_debug_id = node->debug_id; + int has_weak_ref; + int has_strong_ref; + void __user *orig_ptr = ptr; + + BUG_ON(proc != node->proc); + strong = node->internal_strong_refs || + node->local_strong_refs; + weak = !hlist_empty(&node->refs) || + node->local_weak_refs || + node->tmp_refs || strong; + has_strong_ref = node->has_strong_ref; + has_weak_ref = node->has_weak_ref; + + if (weak && !has_weak_ref) { node->has_weak_ref = 1; node->pending_weak_ref = 1; node->local_weak_refs++; - } else if (strong && !node->has_strong_ref) { - cmd = BR_ACQUIRE; - cmd_name = "BR_ACQUIRE"; + } + if (strong && !has_strong_ref) { node->has_strong_ref = 1; node->pending_strong_ref = 1; node->local_strong_refs++; - } else if (!strong && node->has_strong_ref) { - cmd = BR_RELEASE; - cmd_name = "BR_RELEASE"; + } + if (!strong && has_strong_ref) node->has_strong_ref = 0; - } else if (!weak && node->has_weak_ref) { - cmd = BR_DECREFS; - cmd_name = "BR_DECREFS"; + if (!weak && has_weak_ref) node->has_weak_ref = 0; - } - if (cmd != BR_NOOP) { - if (put_user(cmd, (uint32_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(uint32_t); - if (put_user(node->ptr, - (binder_uintptr_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(binder_uintptr_t); - if (put_user(node->cookie, - (binder_uintptr_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(binder_uintptr_t); - - binder_stat_br(proc, thread, cmd); - binder_debug(BINDER_DEBUG_USER_REFS, - "%d:%d %s %d u%016llx c%016llx\n", - proc->pid, thread->pid, cmd_name, - node->debug_id, - (u64)node->ptr, (u64)node->cookie); - } else { - list_del_init(&w->entry); - if (!weak && !strong) { - binder_debug(BINDER_DEBUG_INTERNAL_REFS, - "%d:%d node %d u%016llx c%016llx deleted\n", - proc->pid, thread->pid, - node->debug_id, - (u64)node->ptr, - (u64)node->cookie); - rb_erase(&node->rb_node, &proc->nodes); - kfree(node); - binder_stats_deleted(BINDER_STAT_NODE); - } else { - binder_debug(BINDER_DEBUG_INTERNAL_REFS, - "%d:%d node %d u%016llx c%016llx state unchanged\n", - proc->pid, thread->pid, - node->debug_id, - (u64)node->ptr, - (u64)node->cookie); - } - } + if (!weak && !strong) { + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "%d:%d node %d u%016llx c%016llx deleted\n", + proc->pid, thread->pid, + node_debug_id, + (u64)node_ptr, + (u64)node_cookie); + rb_erase(&node->rb_node, &proc->nodes); + binder_inner_proc_unlock(proc); + binder_node_lock(node); + /* + * Acquire the node lock before freeing the + * node to serialize with other threads that + * may have been holding the node lock while + * decrementing this node (avoids race where + * this thread frees while the other thread + * is unlocking the node after the final + * decrement) + */ + binder_node_unlock(node); + binder_free_node(node); + } else + binder_inner_proc_unlock(proc); + + if (weak && !has_weak_ref) + ret = binder_put_node_cmd( + proc, thread, &ptr, node_ptr, + node_cookie, node_debug_id, + BR_INCREFS, "BR_INCREFS"); + if (!ret && strong && !has_strong_ref) + ret = binder_put_node_cmd( + proc, thread, &ptr, node_ptr, + node_cookie, node_debug_id, + BR_ACQUIRE, "BR_ACQUIRE"); + if (!ret && !strong && has_strong_ref) + ret = binder_put_node_cmd( + proc, thread, &ptr, node_ptr, + node_cookie, node_debug_id, + BR_RELEASE, "BR_RELEASE"); + if (!ret && !weak && has_weak_ref) + ret = binder_put_node_cmd( + proc, thread, &ptr, node_ptr, + node_cookie, node_debug_id, + BR_DECREFS, "BR_DECREFS"); + if (orig_ptr == ptr) + binder_debug(BINDER_DEBUG_INTERNAL_REFS, + "%d:%d node %d u%016llx c%016llx state unchanged\n", + proc->pid, thread->pid, + node_debug_id, + (u64)node_ptr, + (u64)node_cookie); + if (ret) + return ret; } break; case BINDER_WORK_DEAD_BINDER: case BINDER_WORK_DEAD_BINDER_AND_CLEAR: case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: { struct binder_ref_death *death; uint32_t cmd; + binder_uintptr_t cookie; death = container_of(w, struct binder_ref_death, work); if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE; else cmd = BR_DEAD_BINDER; - if (put_user(cmd, (uint32_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(uint32_t); - if (put_user(death->cookie, - (binder_uintptr_t __user *)ptr)) - return -EFAULT; - ptr += sizeof(binder_uintptr_t); - binder_stat_br(proc, thread, cmd); + cookie = death->cookie; + binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION, "%d:%d %s %016llx\n", proc->pid, thread->pid, cmd == BR_DEAD_BINDER ? "BR_DEAD_BINDER" : "BR_CLEAR_DEATH_NOTIFICATION_DONE", - (u64)death->cookie); - + (u64)cookie); if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) { - list_del(&w->entry); + binder_inner_proc_unlock(proc); kfree(death); binder_stats_deleted(BINDER_STAT_DEATH); - } else - list_move(&w->entry, &proc->delivered_death); + } else { + binder_enqueue_work_ilocked( + w, &proc->delivered_death); + binder_inner_proc_unlock(proc); + } + if (put_user(cmd, (uint32_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(uint32_t); + if (put_user(cookie, + (binder_uintptr_t __user *)ptr)) + return -EFAULT; + ptr += sizeof(binder_uintptr_t); + binder_stat_br(proc, thread, cmd); if (cmd == BR_DEAD_BINDER) goto done; /* DEAD_BINDER notifications can cause transactions */ } break; @@ -2903,8 +3791,9 @@ retry: tr.flags = t->flags; tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid); - if (t->from) { - struct task_struct *sender = t->from->proc->tsk; + t_from = binder_get_txn_from(t); + if (t_from) { + struct task_struct *sender = t_from->proc->tsk; tr.sender_pid = task_tgid_nr_ns(sender, task_active_pid_ns(current)); @@ -2914,18 +3803,24 @@ retry: tr.data_size = t->buffer->data_size; tr.offsets_size = t->buffer->offsets_size; - tr.data.ptr.buffer = (binder_uintptr_t)( - (uintptr_t)t->buffer->data + - proc->user_buffer_offset); + tr.data.ptr.buffer = (binder_uintptr_t) + ((uintptr_t)t->buffer->data + + binder_alloc_get_user_buffer_offset(&proc->alloc)); tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *)); - if (put_user(cmd, (uint32_t __user *)ptr)) + if (put_user(cmd, (uint32_t __user *)ptr)) { + if (t_from) + binder_thread_dec_tmpref(t_from); return -EFAULT; + } ptr += sizeof(uint32_t); - if (copy_to_user(ptr, &tr, sizeof(tr))) + if (copy_to_user(ptr, &tr, sizeof(tr))) { + if (t_from) + binder_thread_dec_tmpref(t_from); return -EFAULT; + } ptr += sizeof(tr); trace_binder_transaction_received(t); @@ -2935,21 +3830,22 @@ retry: proc->pid, thread->pid, (cmd == BR_TRANSACTION) ? "BR_TRANSACTION" : "BR_REPLY", - t->debug_id, t->from ? t->from->proc->pid : 0, - t->from ? t->from->pid : 0, cmd, + t->debug_id, t_from ? t_from->proc->pid : 0, + t_from ? t_from->pid : 0, cmd, t->buffer->data_size, t->buffer->offsets_size, (u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets); - list_del(&t->work.entry); + if (t_from) + binder_thread_dec_tmpref(t_from); t->buffer->allow_user_free = 1; if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { + binder_inner_proc_lock(thread->proc); t->to_parent = thread->transaction_stack; t->to_thread = thread; thread->transaction_stack = t; + binder_inner_proc_unlock(thread->proc); } else { - t->buffer->transaction = NULL; - kfree(t); - binder_stats_deleted(BINDER_STAT_TRANSACTION); + binder_free_transaction(t); } break; } @@ -2957,29 +3853,35 @@ retry: done: *consumed = ptr - buffer; + binder_inner_proc_lock(proc); if (proc->requested_threads + proc->ready_threads == 0 && proc->requested_threads_started < proc->max_threads && (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */ /*spawn a new thread if we leave this out */) { proc->requested_threads++; + binder_inner_proc_unlock(proc); binder_debug(BINDER_DEBUG_THREADS, "%d:%d BR_SPAWN_LOOPER\n", proc->pid, thread->pid); if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)) return -EFAULT; binder_stat_br(proc, thread, BR_SPAWN_LOOPER); - } + } else + binder_inner_proc_unlock(proc); return 0; } -static void binder_release_work(struct list_head *list) +static void binder_release_work(struct binder_proc *proc, + struct list_head *list) { struct binder_work *w; - while (!list_empty(list)) { - w = list_first_entry(list, struct binder_work, entry); - list_del_init(&w->entry); + while (1) { + w = binder_dequeue_work_head(proc, list); + if (!w) + return; + switch (w->type) { case BINDER_WORK_TRANSACTION: { struct binder_transaction *t; @@ -2992,11 +3894,17 @@ static void binder_release_work(struct list_head *list) binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, "undelivered transaction %d\n", t->debug_id); - t->buffer->transaction = NULL; - kfree(t); - binder_stats_deleted(BINDER_STAT_TRANSACTION); + binder_free_transaction(t); } } break; + case BINDER_WORK_RETURN_ERROR: { + struct binder_error *e = container_of( + w, struct binder_error, work); + + binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, + "undelivered TRANSACTION_ERROR: %u\n", + e->cmd); + } break; case BINDER_WORK_TRANSACTION_COMPLETE: { binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, "undelivered TRANSACTION_COMPLETE\n"); @@ -3023,7 +3931,8 @@ static void binder_release_work(struct list_head *list) } -static struct binder_thread *binder_get_thread(struct binder_proc *proc) +static struct binder_thread *binder_get_thread_ilocked( + struct binder_proc *proc, struct binder_thread *new_thread) { struct binder_thread *thread = NULL; struct rb_node *parent = NULL; @@ -3038,38 +3947,99 @@ static struct binder_thread *binder_get_thread(struct binder_proc *proc) else if (current->pid > thread->pid) p = &(*p)->rb_right; else - break; + return thread; } - if (*p == NULL) { - thread = kzalloc(sizeof(*thread), GFP_KERNEL); - if (thread == NULL) + if (!new_thread) + return NULL; + thread = new_thread; + binder_stats_created(BINDER_STAT_THREAD); + thread->proc = proc; + thread->pid = current->pid; + atomic_set(&thread->tmp_ref, 0); + init_waitqueue_head(&thread->wait); + INIT_LIST_HEAD(&thread->todo); + rb_link_node(&thread->rb_node, parent, p); + rb_insert_color(&thread->rb_node, &proc->threads); + thread->looper_need_return = true; + thread->return_error.work.type = BINDER_WORK_RETURN_ERROR; + thread->return_error.cmd = BR_OK; + thread->reply_error.work.type = BINDER_WORK_RETURN_ERROR; + thread->reply_error.cmd = BR_OK; + + return thread; +} + +static struct binder_thread *binder_get_thread(struct binder_proc *proc) +{ + struct binder_thread *thread; + struct binder_thread *new_thread; + + binder_inner_proc_lock(proc); + thread = binder_get_thread_ilocked(proc, NULL); + binder_inner_proc_unlock(proc); + if (!thread) { + new_thread = kzalloc(sizeof(*thread), GFP_KERNEL); + if (new_thread == NULL) return NULL; - binder_stats_created(BINDER_STAT_THREAD); - thread->proc = proc; - thread->pid = current->pid; - init_waitqueue_head(&thread->wait); - INIT_LIST_HEAD(&thread->todo); - rb_link_node(&thread->rb_node, parent, p); - rb_insert_color(&thread->rb_node, &proc->threads); - thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; - thread->return_error = BR_OK; - thread->return_error2 = BR_OK; + binder_inner_proc_lock(proc); + thread = binder_get_thread_ilocked(proc, new_thread); + binder_inner_proc_unlock(proc); + if (thread != new_thread) + kfree(new_thread); } return thread; } -static int binder_free_thread(struct binder_proc *proc, - struct binder_thread *thread) +static void binder_free_proc(struct binder_proc *proc) +{ + BUG_ON(!list_empty(&proc->todo)); + BUG_ON(!list_empty(&proc->delivered_death)); + binder_alloc_deferred_release(&proc->alloc); + put_task_struct(proc->tsk); + binder_stats_deleted(BINDER_STAT_PROC); + kfree(proc); +} + +static void binder_free_thread(struct binder_thread *thread) +{ + BUG_ON(!list_empty(&thread->todo)); + binder_stats_deleted(BINDER_STAT_THREAD); + binder_proc_dec_tmpref(thread->proc); + kfree(thread); +} + +static int binder_thread_release(struct binder_proc *proc, + struct binder_thread *thread) { struct binder_transaction *t; struct binder_transaction *send_reply = NULL; int active_transactions = 0; + struct binder_transaction *last_t = NULL; + binder_inner_proc_lock(thread->proc); + /* + * take a ref on the proc so it survives + * after we remove this thread from proc->threads. + * The corresponding dec is when we actually + * free the thread in binder_free_thread() + */ + proc->tmp_ref++; + /* + * take a ref on this thread to ensure it + * survives while we are releasing it + */ + atomic_inc(&thread->tmp_ref); rb_erase(&thread->rb_node, &proc->threads); t = thread->transaction_stack; - if (t && t->to_thread == thread) - send_reply = t; + if (t) { + spin_lock(&t->lock); + if (t->to_thread == thread) + send_reply = t; + } + thread->is_dead = true; + while (t) { + last_t = t; active_transactions++; binder_debug(BINDER_DEBUG_DEAD_TRANSACTION, "release %d:%d transaction %d %s, still active\n", @@ -3090,12 +4060,16 @@ static int binder_free_thread(struct binder_proc *proc, t = t->from_parent; } else BUG(); + spin_unlock(&last_t->lock); + if (t) + spin_lock(&t->lock); } + binder_inner_proc_unlock(thread->proc); + if (send_reply) binder_send_failed_reply(send_reply, BR_DEAD_REPLY); - binder_release_work(&thread->todo); - kfree(thread); - binder_stats_deleted(BINDER_STAT_THREAD); + binder_release_work(proc, &thread->todo); + binder_thread_dec_tmpref(thread); return active_transactions; } @@ -3106,14 +4080,12 @@ static unsigned int binder_poll(struct file *filp, struct binder_thread *thread = NULL; int wait_for_proc_work; - binder_lock(__func__); - thread = binder_get_thread(proc); + binder_inner_proc_lock(thread->proc); wait_for_proc_work = thread->transaction_stack == NULL && - list_empty(&thread->todo) && thread->return_error == BR_OK; - - binder_unlock(__func__); + binder_worklist_empty_ilocked(&thread->todo); + binder_inner_proc_unlock(thread->proc); if (wait_for_proc_work) { if (binder_has_proc_work(proc, thread)) @@ -3174,7 +4146,7 @@ static int binder_ioctl_write_read(struct file *filp, &bwr.read_consumed, filp->f_flags & O_NONBLOCK); trace_binder_read_done(ret); - if (!list_empty(&proc->todo)) + if (!binder_worklist_empty(proc, &proc->todo)) wake_up_interruptible(&proc->wait); if (ret < 0) { if (copy_to_user(ubuf, &bwr, sizeof(bwr))) @@ -3200,9 +4172,10 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) int ret = 0; struct binder_proc *proc = filp->private_data; struct binder_context *context = proc->context; - + struct binder_node *new_node; kuid_t curr_euid = current_euid(); + mutex_lock(&context->context_mgr_node_lock); if (context->binder_context_mgr_node) { pr_err("BINDER_SET_CONTEXT_MGR already set\n"); ret = -EBUSY; @@ -3223,16 +4196,21 @@ static int binder_ioctl_set_ctx_mgr(struct file *filp) } else { context->binder_context_mgr_uid = curr_euid; } - context->binder_context_mgr_node = binder_new_node(proc, 0, 0); - if (!context->binder_context_mgr_node) { + new_node = binder_new_node(proc, NULL); + if (!new_node) { ret = -ENOMEM; goto out; } - context->binder_context_mgr_node->local_weak_refs++; - context->binder_context_mgr_node->local_strong_refs++; - context->binder_context_mgr_node->has_strong_ref = 1; - context->binder_context_mgr_node->has_weak_ref = 1; + binder_node_lock(new_node); + new_node->local_weak_refs++; + new_node->local_strong_refs++; + new_node->has_strong_ref = 1; + new_node->has_weak_ref = 1; + context->binder_context_mgr_node = new_node; + binder_node_unlock(new_node); + binder_put_node(new_node); out: + mutex_unlock(&context->context_mgr_node_lock); return ret; } @@ -3247,17 +4225,14 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) /*pr_info("binder_ioctl: %d:%d %x %lx\n", proc->pid, current->pid, cmd, arg);*/ - if (unlikely(current->mm != proc->vma_vm_mm)) { - pr_err("current mm mismatch proc mm\n"); - return -EINVAL; - } + binder_selftest_alloc(&proc->alloc); + trace_binder_ioctl(cmd, arg); ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); if (ret) goto err_unlocked; - binder_lock(__func__); thread = binder_get_thread(proc); if (thread == NULL) { ret = -ENOMEM; @@ -3270,12 +4245,19 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (ret) goto err; break; - case BINDER_SET_MAX_THREADS: - if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) { + case BINDER_SET_MAX_THREADS: { + int max_threads; + + if (copy_from_user(&max_threads, ubuf, + sizeof(max_threads))) { ret = -EINVAL; goto err; } + binder_inner_proc_lock(proc); + proc->max_threads = max_threads; + binder_inner_proc_unlock(proc); break; + } case BINDER_SET_CONTEXT_MGR: ret = binder_ioctl_set_ctx_mgr(filp); if (ret) @@ -3284,7 +4266,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) case BINDER_THREAD_EXIT: binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n", proc->pid, thread->pid); - binder_free_thread(proc, thread); + binder_thread_release(proc, thread); thread = NULL; break; case BINDER_VERSION: { @@ -3308,8 +4290,7 @@ static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = 0; err: if (thread) - thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN; - binder_unlock(__func__); + thread->looper_need_return = false; wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2); if (ret && ret != -ERESTARTSYS) pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret); @@ -3338,8 +4319,7 @@ static void binder_vma_close(struct vm_area_struct *vma) proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, (unsigned long)pgprot_val(vma->vm_page_prot)); - proc->vma = NULL; - proc->vma_vm_mm = NULL; + binder_alloc_vma_close(&proc->alloc); binder_defer_work(proc, BINDER_DEFERRED_PUT_FILES); } @@ -3357,20 +4337,18 @@ static const struct vm_operations_struct binder_vm_ops = { static int binder_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; - struct vm_struct *area; struct binder_proc *proc = filp->private_data; const char *failure_string; - struct binder_buffer *buffer; - if (proc->tsk != current) + if (proc->tsk != current->group_leader) return -EINVAL; if ((vma->vm_end - vma->vm_start) > SZ_4M) vma->vm_end = vma->vm_start + SZ_4M; binder_debug(BINDER_DEBUG_OPEN_CLOSE, - "binder_mmap: %d %lx-%lx (%ld K) vma %lx pagep %lx\n", - proc->pid, vma->vm_start, vma->vm_end, + "%s: %d %lx-%lx (%ld K) vma %lx pagep %lx\n", + __func__, proc->pid, vma->vm_start, vma->vm_end, (vma->vm_end - vma->vm_start) / SZ_1K, vma->vm_flags, (unsigned long)pgprot_val(vma->vm_page_prot)); @@ -3380,73 +4358,15 @@ static int binder_mmap(struct file *filp, struct vm_area_struct *vma) goto err_bad_arg; } vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE; - - mutex_lock(&binder_mmap_lock); - if (proc->buffer) { - ret = -EBUSY; - failure_string = "already mapped"; - goto err_already_mapped; - } - - area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); - if (area == NULL) { - ret = -ENOMEM; - failure_string = "get_vm_area"; - goto err_get_vm_area_failed; - } - proc->buffer = area->addr; - proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer; - mutex_unlock(&binder_mmap_lock); - -#ifdef CONFIG_CPU_CACHE_VIPT - if (cache_is_vipt_aliasing()) { - while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) { - pr_info("binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer); - vma->vm_start += PAGE_SIZE; - } - } -#endif - proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL); - if (proc->pages == NULL) { - ret = -ENOMEM; - failure_string = "alloc page array"; - goto err_alloc_pages_failed; - } - proc->buffer_size = vma->vm_end - vma->vm_start; - vma->vm_ops = &binder_vm_ops; vma->vm_private_data = proc; - if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) { - ret = -ENOMEM; - failure_string = "alloc small buf"; - goto err_alloc_small_buf_failed; - } - buffer = proc->buffer; - INIT_LIST_HEAD(&proc->buffers); - list_add(&buffer->entry, &proc->buffers); - buffer->free = 1; - binder_insert_free_buffer(proc, buffer); - proc->free_async_space = proc->buffer_size / 2; - barrier(); + ret = binder_alloc_mmap_handler(&proc->alloc, vma); + if (ret) + return ret; proc->files = get_files_struct(current); - proc->vma = vma; - proc->vma_vm_mm = vma->vm_mm; - - /*pr_info("binder_mmap: %d %lx-%lx maps %p\n", - proc->pid, vma->vm_start, vma->vm_end, proc->buffer);*/ return 0; -err_alloc_small_buf_failed: - kfree(proc->pages); - proc->pages = NULL; -err_alloc_pages_failed: - mutex_lock(&binder_mmap_lock); - vfree(proc->buffer); - proc->buffer = NULL; -err_get_vm_area_failed: -err_already_mapped: - mutex_unlock(&binder_mmap_lock); err_bad_arg: pr_err("binder_mmap: %d %lx-%lx %s failed %d\n", proc->pid, vma->vm_start, vma->vm_end, failure_string, ret); @@ -3464,25 +4384,26 @@ static int binder_open(struct inode *nodp, struct file *filp) proc = kzalloc(sizeof(*proc), GFP_KERNEL); if (proc == NULL) return -ENOMEM; - get_task_struct(current); - proc->tsk = current; - proc->vma_vm_mm = current->mm; + spin_lock_init(&proc->inner_lock); + spin_lock_init(&proc->outer_lock); + get_task_struct(current->group_leader); + proc->tsk = current->group_leader; INIT_LIST_HEAD(&proc->todo); init_waitqueue_head(&proc->wait); proc->default_priority = task_nice(current); binder_dev = container_of(filp->private_data, struct binder_device, miscdev); proc->context = &binder_dev->context; - - binder_lock(__func__); + binder_alloc_init(&proc->alloc); binder_stats_created(BINDER_STAT_PROC); - hlist_add_head(&proc->proc_node, &binder_procs); proc->pid = current->group_leader->pid; INIT_LIST_HEAD(&proc->delivered_death); filp->private_data = proc; - binder_unlock(__func__); + mutex_lock(&binder_procs_lock); + hlist_add_head(&proc->proc_node, &binder_procs); + mutex_unlock(&binder_procs_lock); if (binder_debugfs_dir_entry_proc) { char strbuf[11]; @@ -3518,15 +4439,17 @@ static void binder_deferred_flush(struct binder_proc *proc) struct rb_node *n; int wake_count = 0; + binder_inner_proc_lock(proc); for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) { struct binder_thread *thread = rb_entry(n, struct binder_thread, rb_node); - thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; + thread->looper_need_return = true; if (thread->looper & BINDER_LOOPER_STATE_WAITING) { wake_up_interruptible(&thread->wait); wake_count++; } } + binder_inner_proc_unlock(proc); wake_up_interruptible_all(&proc->wait); binder_debug(BINDER_DEBUG_OPEN_CLOSE, @@ -3548,13 +4471,21 @@ static int binder_node_release(struct binder_node *node, int refs) { struct binder_ref *ref; int death = 0; + struct binder_proc *proc = node->proc; - list_del_init(&node->work.entry); - binder_release_work(&node->async_todo); + binder_release_work(proc, &node->async_todo); - if (hlist_empty(&node->refs)) { - kfree(node); - binder_stats_deleted(BINDER_STAT_NODE); + binder_node_lock(node); + binder_inner_proc_lock(proc); + binder_dequeue_work_ilocked(&node->work); + /* + * The caller must have taken a temporary ref on the node, + */ + BUG_ON(!node->tmp_refs); + if (hlist_empty(&node->refs) && node->tmp_refs == 1) { + binder_inner_proc_unlock(proc); + binder_node_unlock(node); + binder_free_node(node); return refs; } @@ -3562,45 +4493,58 @@ static int binder_node_release(struct binder_node *node, int refs) node->proc = NULL; node->local_strong_refs = 0; node->local_weak_refs = 0; + binder_inner_proc_unlock(proc); + + spin_lock(&binder_dead_nodes_lock); hlist_add_head(&node->dead_node, &binder_dead_nodes); + spin_unlock(&binder_dead_nodes_lock); hlist_for_each_entry(ref, &node->refs, node_entry) { refs++; - - if (!ref->death) + /* + * Need the node lock to synchronize + * with new notification requests and the + * inner lock to synchronize with queued + * death notifications. + */ + binder_inner_proc_lock(ref->proc); + if (!ref->death) { + binder_inner_proc_unlock(ref->proc); continue; + } death++; - if (list_empty(&ref->death->work.entry)) { - ref->death->work.type = BINDER_WORK_DEAD_BINDER; - list_add_tail(&ref->death->work.entry, - &ref->proc->todo); - wake_up_interruptible(&ref->proc->wait); - } else - BUG(); + BUG_ON(!list_empty(&ref->death->work.entry)); + ref->death->work.type = BINDER_WORK_DEAD_BINDER; + binder_enqueue_work_ilocked(&ref->death->work, + &ref->proc->todo); + wake_up_interruptible(&ref->proc->wait); + binder_inner_proc_unlock(ref->proc); } binder_debug(BINDER_DEBUG_DEAD_BINDER, "node %d now dead, refs %d, death %d\n", node->debug_id, refs, death); + binder_node_unlock(node); + binder_put_node(node); return refs; } static void binder_deferred_release(struct binder_proc *proc) { - struct binder_transaction *t; struct binder_context *context = proc->context; struct rb_node *n; - int threads, nodes, incoming_refs, outgoing_refs, buffers, - active_transactions, page_count; + int threads, nodes, incoming_refs, outgoing_refs, active_transactions; - BUG_ON(proc->vma); BUG_ON(proc->files); + mutex_lock(&binder_procs_lock); hlist_del(&proc->proc_node); + mutex_unlock(&binder_procs_lock); + mutex_lock(&context->context_mgr_node_lock); if (context->binder_context_mgr_node && context->binder_context_mgr_node->proc == proc) { binder_debug(BINDER_DEBUG_DEAD_BINDER, @@ -3608,15 +4552,25 @@ static void binder_deferred_release(struct binder_proc *proc) __func__, proc->pid); context->binder_context_mgr_node = NULL; } + mutex_unlock(&context->context_mgr_node_lock); + binder_inner_proc_lock(proc); + /* + * Make sure proc stays alive after we + * remove all the threads + */ + proc->tmp_ref++; + proc->is_dead = true; threads = 0; active_transactions = 0; while ((n = rb_first(&proc->threads))) { struct binder_thread *thread; thread = rb_entry(n, struct binder_thread, rb_node); + binder_inner_proc_unlock(proc); threads++; - active_transactions += binder_free_thread(proc, thread); + active_transactions += binder_thread_release(proc, thread); + binder_inner_proc_lock(proc); } nodes = 0; @@ -3626,73 +4580,42 @@ static void binder_deferred_release(struct binder_proc *proc) node = rb_entry(n, struct binder_node, rb_node); nodes++; + /* + * take a temporary ref on the node before + * calling binder_node_release() which will either + * kfree() the node or call binder_put_node() + */ + binder_inc_node_tmpref_ilocked(node); rb_erase(&node->rb_node, &proc->nodes); + binder_inner_proc_unlock(proc); incoming_refs = binder_node_release(node, incoming_refs); + binder_inner_proc_lock(proc); } + binder_inner_proc_unlock(proc); outgoing_refs = 0; + binder_proc_lock(proc); while ((n = rb_first(&proc->refs_by_desc))) { struct binder_ref *ref; ref = rb_entry(n, struct binder_ref, rb_node_desc); outgoing_refs++; - binder_delete_ref(ref); - } - - binder_release_work(&proc->todo); - binder_release_work(&proc->delivered_death); - - buffers = 0; - while ((n = rb_first(&proc->allocated_buffers))) { - struct binder_buffer *buffer; - - buffer = rb_entry(n, struct binder_buffer, rb_node); - - t = buffer->transaction; - if (t) { - t->buffer = NULL; - buffer->transaction = NULL; - pr_err("release proc %d, transaction %d, not freed\n", - proc->pid, t->debug_id); - /*BUG();*/ - } - - binder_free_buf(proc, buffer); - buffers++; - } - - binder_stats_deleted(BINDER_STAT_PROC); - - page_count = 0; - if (proc->pages) { - int i; - - for (i = 0; i < proc->buffer_size / PAGE_SIZE; i++) { - void *page_addr; - - if (!proc->pages[i]) - continue; - - page_addr = proc->buffer + i * PAGE_SIZE; - binder_debug(BINDER_DEBUG_BUFFER_ALLOC, - "%s: %d: page %d at %p not freed\n", - __func__, proc->pid, i, page_addr); - unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); - __free_page(proc->pages[i]); - page_count++; - } - kfree(proc->pages); - vfree(proc->buffer); + binder_cleanup_ref_olocked(ref); + binder_proc_unlock(proc); + binder_free_ref(ref); + binder_proc_lock(proc); } + binder_proc_unlock(proc); - put_task_struct(proc->tsk); + binder_release_work(proc, &proc->todo); + binder_release_work(proc, &proc->delivered_death); binder_debug(BINDER_DEBUG_OPEN_CLOSE, - "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d, buffers %d, pages %d\n", + "%s: %d threads %d, nodes %d (ref %d), refs %d, active transactions %d\n", __func__, proc->pid, threads, nodes, incoming_refs, - outgoing_refs, active_transactions, buffers, page_count); + outgoing_refs, active_transactions); - kfree(proc); + binder_proc_dec_tmpref(proc); } static void binder_deferred_func(struct work_struct *work) @@ -3703,7 +4626,6 @@ static void binder_deferred_func(struct work_struct *work) int defer; do { - binder_lock(__func__); mutex_lock(&binder_deferred_lock); if (!hlist_empty(&binder_deferred_list)) { proc = hlist_entry(binder_deferred_list.first, @@ -3730,7 +4652,6 @@ static void binder_deferred_func(struct work_struct *work) if (defer & BINDER_DEFERRED_RELEASE) binder_deferred_release(proc); /* frees proc */ - binder_unlock(__func__); if (files) put_files_struct(files); } while (proc); @@ -3750,41 +4671,52 @@ binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer) mutex_unlock(&binder_deferred_lock); } -static void print_binder_transaction(struct seq_file *m, const char *prefix, - struct binder_transaction *t) +static void print_binder_transaction_ilocked(struct seq_file *m, + struct binder_proc *proc, + const char *prefix, + struct binder_transaction *t) { + struct binder_proc *to_proc; + struct binder_buffer *buffer = t->buffer; + + WARN_ON(!spin_is_locked(&proc->inner_lock)); + spin_lock(&t->lock); + to_proc = t->to_proc; seq_printf(m, "%s %d: %p from %d:%d to %d:%d code %x flags %x pri %ld r%d", prefix, t->debug_id, t, t->from ? t->from->proc->pid : 0, t->from ? t->from->pid : 0, - t->to_proc ? t->to_proc->pid : 0, + to_proc ? to_proc->pid : 0, t->to_thread ? t->to_thread->pid : 0, t->code, t->flags, t->priority, t->need_reply); - if (t->buffer == NULL) { + spin_unlock(&t->lock); + + if (proc != to_proc) { + /* + * Can only safely deref buffer if we are holding the + * correct proc inner lock for this node + */ + seq_puts(m, "\n"); + return; + } + + if (buffer == NULL) { seq_puts(m, " buffer free\n"); return; } - if (t->buffer->target_node) - seq_printf(m, " node %d", - t->buffer->target_node->debug_id); + if (buffer->target_node) + seq_printf(m, " node %d", buffer->target_node->debug_id); seq_printf(m, " size %zd:%zd data %p\n", - t->buffer->data_size, t->buffer->offsets_size, - t->buffer->data); -} - -static void print_binder_buffer(struct seq_file *m, const char *prefix, - struct binder_buffer *buffer) -{ - seq_printf(m, "%s %d: %p size %zd:%zd %s\n", - prefix, buffer->debug_id, buffer->data, buffer->data_size, buffer->offsets_size, - buffer->transaction ? "active" : "delivered"); + buffer->data); } -static void print_binder_work(struct seq_file *m, const char *prefix, - const char *transaction_prefix, - struct binder_work *w) +static void print_binder_work_ilocked(struct seq_file *m, + struct binder_proc *proc, + const char *prefix, + const char *transaction_prefix, + struct binder_work *w) { struct binder_node *node; struct binder_transaction *t; @@ -3792,8 +4724,16 @@ static void print_binder_work(struct seq_file *m, const char *prefix, switch (w->type) { case BINDER_WORK_TRANSACTION: t = container_of(w, struct binder_transaction, work); - print_binder_transaction(m, transaction_prefix, t); + print_binder_transaction_ilocked( + m, proc, transaction_prefix, t); break; + case BINDER_WORK_RETURN_ERROR: { + struct binder_error *e = container_of( + w, struct binder_error, work); + + seq_printf(m, "%stransaction error: %u\n", + prefix, e->cmd); + } break; case BINDER_WORK_TRANSACTION_COMPLETE: seq_printf(m, "%stransaction complete\n", prefix); break; @@ -3818,70 +4758,89 @@ static void print_binder_work(struct seq_file *m, const char *prefix, } } -static void print_binder_thread(struct seq_file *m, - struct binder_thread *thread, - int print_always) +static void print_binder_thread_ilocked(struct seq_file *m, + struct binder_thread *thread, + int print_always) { struct binder_transaction *t; struct binder_work *w; size_t start_pos = m->count; size_t header_pos; - seq_printf(m, " thread %d: l %02x\n", thread->pid, thread->looper); + WARN_ON(!spin_is_locked(&thread->proc->inner_lock)); + seq_printf(m, " thread %d: l %02x need_return %d tr %d\n", + thread->pid, thread->looper, + thread->looper_need_return, + atomic_read(&thread->tmp_ref)); header_pos = m->count; t = thread->transaction_stack; while (t) { if (t->from == thread) { - print_binder_transaction(m, - " outgoing transaction", t); + print_binder_transaction_ilocked(m, thread->proc, + " outgoing transaction", t); t = t->from_parent; } else if (t->to_thread == thread) { - print_binder_transaction(m, + print_binder_transaction_ilocked(m, thread->proc, " incoming transaction", t); t = t->to_parent; } else { - print_binder_transaction(m, " bad transaction", t); + print_binder_transaction_ilocked(m, thread->proc, + " bad transaction", t); t = NULL; } } list_for_each_entry(w, &thread->todo, entry) { - print_binder_work(m, " ", " pending transaction", w); + print_binder_work_ilocked(m, thread->proc, " ", + " pending transaction", w); } if (!print_always && m->count == header_pos) m->count = start_pos; } -static void print_binder_node(struct seq_file *m, struct binder_node *node) +static void print_binder_node_nilocked(struct seq_file *m, + struct binder_node *node) { struct binder_ref *ref; struct binder_work *w; int count; + WARN_ON(!spin_is_locked(&node->lock)); + if (node->proc) + WARN_ON(!spin_is_locked(&node->proc->inner_lock)); + count = 0; hlist_for_each_entry(ref, &node->refs, node_entry) count++; - seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d", + seq_printf(m, " node %d: u%016llx c%016llx hs %d hw %d ls %d lw %d is %d iw %d tr %d", node->debug_id, (u64)node->ptr, (u64)node->cookie, node->has_strong_ref, node->has_weak_ref, node->local_strong_refs, node->local_weak_refs, - node->internal_strong_refs, count); + node->internal_strong_refs, count, node->tmp_refs); if (count) { seq_puts(m, " proc"); hlist_for_each_entry(ref, &node->refs, node_entry) seq_printf(m, " %d", ref->proc->pid); } seq_puts(m, "\n"); - list_for_each_entry(w, &node->async_todo, entry) - print_binder_work(m, " ", - " pending async transaction", w); + if (node->proc) { + list_for_each_entry(w, &node->async_todo, entry) + print_binder_work_ilocked(m, node->proc, " ", + " pending async transaction", w); + } } -static void print_binder_ref(struct seq_file *m, struct binder_ref *ref) +static void print_binder_ref_olocked(struct seq_file *m, + struct binder_ref *ref) { - seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %p\n", - ref->debug_id, ref->desc, ref->node->proc ? "" : "dead ", - ref->node->debug_id, ref->strong, ref->weak, ref->death); + WARN_ON(!spin_is_locked(&ref->proc->outer_lock)); + binder_node_lock(ref->node); + seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n", + ref->data.debug_id, ref->data.desc, + ref->node->proc ? "" : "dead ", + ref->node->debug_id, ref->data.strong, + ref->data.weak, ref->death); + binder_node_unlock(ref->node); } static void print_binder_proc(struct seq_file *m, @@ -3891,36 +4850,60 @@ static void print_binder_proc(struct seq_file *m, struct rb_node *n; size_t start_pos = m->count; size_t header_pos; + struct binder_node *last_node = NULL; seq_printf(m, "proc %d\n", proc->pid); seq_printf(m, "context %s\n", proc->context->name); header_pos = m->count; + binder_inner_proc_lock(proc); for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) - print_binder_thread(m, rb_entry(n, struct binder_thread, + print_binder_thread_ilocked(m, rb_entry(n, struct binder_thread, rb_node), print_all); + for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) { struct binder_node *node = rb_entry(n, struct binder_node, rb_node); - if (print_all || node->has_async_transaction) - print_binder_node(m, node); - } + /* + * take a temporary reference on the node so it + * survives and isn't removed from the tree + * while we print it. + */ + binder_inc_node_tmpref_ilocked(node); + /* Need to drop inner lock to take node lock */ + binder_inner_proc_unlock(proc); + if (last_node) + binder_put_node(last_node); + binder_node_inner_lock(node); + print_binder_node_nilocked(m, node); + binder_node_inner_unlock(node); + last_node = node; + binder_inner_proc_lock(proc); + } + binder_inner_proc_unlock(proc); + if (last_node) + binder_put_node(last_node); + if (print_all) { + binder_proc_lock(proc); for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) - print_binder_ref(m, rb_entry(n, struct binder_ref, - rb_node_desc)); + print_binder_ref_olocked(m, rb_entry(n, + struct binder_ref, + rb_node_desc)); + binder_proc_unlock(proc); } - for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n)) - print_binder_buffer(m, " buffer", - rb_entry(n, struct binder_buffer, rb_node)); + binder_alloc_print_allocated(m, &proc->alloc); + binder_inner_proc_lock(proc); list_for_each_entry(w, &proc->todo, entry) - print_binder_work(m, " ", " pending transaction", w); + print_binder_work_ilocked(m, proc, " ", + " pending transaction", w); list_for_each_entry(w, &proc->delivered_death, entry) { seq_puts(m, " has delivered dead binder\n"); break; } + binder_inner_proc_unlock(proc); if (!print_all && m->count == header_pos) m->count = start_pos; } @@ -3986,17 +4969,21 @@ static void print_binder_stats(struct seq_file *m, const char *prefix, BUILD_BUG_ON(ARRAY_SIZE(stats->bc) != ARRAY_SIZE(binder_command_strings)); for (i = 0; i < ARRAY_SIZE(stats->bc); i++) { - if (stats->bc[i]) + int temp = atomic_read(&stats->bc[i]); + + if (temp) seq_printf(m, "%s%s: %d\n", prefix, - binder_command_strings[i], stats->bc[i]); + binder_command_strings[i], temp); } BUILD_BUG_ON(ARRAY_SIZE(stats->br) != ARRAY_SIZE(binder_return_strings)); for (i = 0; i < ARRAY_SIZE(stats->br); i++) { - if (stats->br[i]) + int temp = atomic_read(&stats->br[i]); + + if (temp) seq_printf(m, "%s%s: %d\n", prefix, - binder_return_strings[i], stats->br[i]); + binder_return_strings[i], temp); } BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) != @@ -4004,11 +4991,15 @@ static void print_binder_stats(struct seq_file *m, const char *prefix, BUILD_BUG_ON(ARRAY_SIZE(stats->obj_created) != ARRAY_SIZE(stats->obj_deleted)); for (i = 0; i < ARRAY_SIZE(stats->obj_created); i++) { - if (stats->obj_created[i] || stats->obj_deleted[i]) - seq_printf(m, "%s%s: active %d total %d\n", prefix, + int created = atomic_read(&stats->obj_created[i]); + int deleted = atomic_read(&stats->obj_deleted[i]); + + if (created || deleted) + seq_printf(m, "%s%s: active %d total %d\n", + prefix, binder_objstat_strings[i], - stats->obj_created[i] - stats->obj_deleted[i], - stats->obj_created[i]); + created - deleted, + created); } } @@ -4018,10 +5009,13 @@ static void print_binder_proc_stats(struct seq_file *m, struct binder_work *w; struct rb_node *n; int count, strong, weak; + size_t free_async_space = + binder_alloc_get_free_async_space(&proc->alloc); seq_printf(m, "proc %d\n", proc->pid); seq_printf(m, "context %s\n", proc->context->name); count = 0; + binder_inner_proc_lock(proc); for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) count++; seq_printf(m, " threads: %d\n", count); @@ -4029,38 +5023,37 @@ static void print_binder_proc_stats(struct seq_file *m, " ready threads %d\n" " free async space %zd\n", proc->requested_threads, proc->requested_threads_started, proc->max_threads, - proc->ready_threads, proc->free_async_space); + proc->ready_threads, + free_async_space); count = 0; for (n = rb_first(&proc->nodes); n != NULL; n = rb_next(n)) count++; + binder_inner_proc_unlock(proc); seq_printf(m, " nodes: %d\n", count); count = 0; strong = 0; weak = 0; + binder_proc_lock(proc); for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { struct binder_ref *ref = rb_entry(n, struct binder_ref, rb_node_desc); count++; - strong += ref->strong; - weak += ref->weak; + strong += ref->data.strong; + weak += ref->data.weak; } + binder_proc_unlock(proc); seq_printf(m, " refs: %d s %d w %d\n", count, strong, weak); - count = 0; - for (n = rb_first(&proc->allocated_buffers); n != NULL; n = rb_next(n)) - count++; + count = binder_alloc_get_allocated_count(&proc->alloc); seq_printf(m, " buffers: %d\n", count); count = 0; + binder_inner_proc_lock(proc); list_for_each_entry(w, &proc->todo, entry) { - switch (w->type) { - case BINDER_WORK_TRANSACTION: + if (w->type == BINDER_WORK_TRANSACTION) count++; - break; - default: - break; - } } + binder_inner_proc_unlock(proc); seq_printf(m, " pending transactions: %d\n", count); print_binder_stats(m, " ", &proc->stats); @@ -4071,57 +5064,67 @@ static int binder_state_show(struct seq_file *m, void *unused) { struct binder_proc *proc; struct binder_node *node; - int do_lock = !binder_debug_no_lock; - - if (do_lock) - binder_lock(__func__); + struct binder_node *last_node = NULL; seq_puts(m, "binder state:\n"); + spin_lock(&binder_dead_nodes_lock); if (!hlist_empty(&binder_dead_nodes)) seq_puts(m, "dead nodes:\n"); - hlist_for_each_entry(node, &binder_dead_nodes, dead_node) - print_binder_node(m, node); - + hlist_for_each_entry(node, &binder_dead_nodes, dead_node) { + /* + * take a temporary reference on the node so it + * survives and isn't removed from the list + * while we print it. + */ + node->tmp_refs++; + spin_unlock(&binder_dead_nodes_lock); + if (last_node) + binder_put_node(last_node); + binder_node_lock(node); + print_binder_node_nilocked(m, node); + binder_node_unlock(node); + last_node = node; + spin_lock(&binder_dead_nodes_lock); + } + spin_unlock(&binder_dead_nodes_lock); + if (last_node) + binder_put_node(last_node); + + mutex_lock(&binder_procs_lock); hlist_for_each_entry(proc, &binder_procs, proc_node) print_binder_proc(m, proc, 1); - if (do_lock) - binder_unlock(__func__); + mutex_unlock(&binder_procs_lock); + return 0; } static int binder_stats_show(struct seq_file *m, void *unused) { struct binder_proc *proc; - int do_lock = !binder_debug_no_lock; - - if (do_lock) - binder_lock(__func__); seq_puts(m, "binder stats:\n"); print_binder_stats(m, "", &binder_stats); + mutex_lock(&binder_procs_lock); hlist_for_each_entry(proc, &binder_procs, proc_node) print_binder_proc_stats(m, proc); - if (do_lock) - binder_unlock(__func__); + mutex_unlock(&binder_procs_lock); + return 0; } static int binder_transactions_show(struct seq_file *m, void *unused) { struct binder_proc *proc; - int do_lock = !binder_debug_no_lock; - - if (do_lock) - binder_lock(__func__); seq_puts(m, "binder transactions:\n"); + mutex_lock(&binder_procs_lock); hlist_for_each_entry(proc, &binder_procs, proc_node) print_binder_proc(m, proc, 0); - if (do_lock) - binder_unlock(__func__); + mutex_unlock(&binder_procs_lock); + return 0; } @@ -4129,44 +5132,63 @@ static int binder_proc_show(struct seq_file *m, void *unused) { struct binder_proc *itr; int pid = (unsigned long)m->private; - int do_lock = !binder_debug_no_lock; - - if (do_lock) - binder_lock(__func__); + mutex_lock(&binder_procs_lock); hlist_for_each_entry(itr, &binder_procs, proc_node) { if (itr->pid == pid) { seq_puts(m, "binder proc state:\n"); print_binder_proc(m, itr, 1); } } - if (do_lock) - binder_unlock(__func__); + mutex_unlock(&binder_procs_lock); + return 0; } static void print_binder_transaction_log_entry(struct seq_file *m, struct binder_transaction_log_entry *e) { + int debug_id = READ_ONCE(e->debug_id_done); + /* + * read barrier to guarantee debug_id_done read before + * we print the log values + */ + smp_rmb(); seq_printf(m, - "%d: %s from %d:%d to %d:%d context %s node %d handle %d size %d:%d\n", + "%d: %s from %d:%d to %d:%d context %s node %d handle %d size %d:%d ret %d/%d l=%d", e->debug_id, (e->call_type == 2) ? "reply" : ((e->call_type == 1) ? "async" : "call "), e->from_proc, e->from_thread, e->to_proc, e->to_thread, e->context_name, - e->to_node, e->target_handle, e->data_size, e->offsets_size); + e->to_node, e->target_handle, e->data_size, e->offsets_size, + e->return_error, e->return_error_param, + e->return_error_line); + /* + * read-barrier to guarantee read of debug_id_done after + * done printing the fields of the entry + */ + smp_rmb(); + seq_printf(m, debug_id && debug_id == READ_ONCE(e->debug_id_done) ? + "\n" : " (incomplete)\n"); } static int binder_transaction_log_show(struct seq_file *m, void *unused) { struct binder_transaction_log *log = m->private; + unsigned int log_cur = atomic_read(&log->cur); + unsigned int count; + unsigned int cur; int i; - if (log->full) { - for (i = log->next; i < ARRAY_SIZE(log->entry); i++) - print_binder_transaction_log_entry(m, &log->entry[i]); + count = log_cur + 1; + cur = count < ARRAY_SIZE(log->entry) && !log->full ? + 0 : count % ARRAY_SIZE(log->entry); + if (count > ARRAY_SIZE(log->entry) || log->full) + count = ARRAY_SIZE(log->entry); + for (i = 0; i < count; i++) { + unsigned int index = cur++ % ARRAY_SIZE(log->entry); + + print_binder_transaction_log_entry(m, &log->entry[index]); } - for (i = 0; i < log->next; i++) - print_binder_transaction_log_entry(m, &log->entry[i]); return 0; } @@ -4201,6 +5223,7 @@ static int __init init_binder_device(const char *name) binder_device->context.binder_context_mgr_uid = INVALID_UID; binder_device->context.name = name; + mutex_init(&binder_device->context.context_mgr_node_lock); ret = misc_register(&binder_device->miscdev); if (ret < 0) { @@ -4216,10 +5239,15 @@ static int __init init_binder_device(const char *name) static int __init binder_init(void) { int ret; - char *device_name, *device_names; + char *device_name, *device_names, *device_tmp; struct binder_device *device; struct hlist_node *tmp; + binder_alloc_shrinker_init(); + + atomic_set(&binder_transaction_log.cur, ~0U); + atomic_set(&binder_transaction_log_failed.cur, ~0U); + binder_debugfs_dir_entry_root = debugfs_create_dir("binder", NULL); if (binder_debugfs_dir_entry_root) binder_debugfs_dir_entry_proc = debugfs_create_dir("proc", @@ -4264,7 +5292,8 @@ static int __init binder_init(void) } strcpy(device_names, binder_devices_param); - while ((device_name = strsep(&device_names, ","))) { + device_tmp = device_names; + while ((device_name = strsep(&device_tmp, ","))) { ret = init_binder_device(device_name); if (ret) goto err_init_binder_device_failed; @@ -4278,6 +5307,9 @@ err_init_binder_device_failed: hlist_del(&device->hlist); kfree(device); } + + kfree(device_names); + err_alloc_device_names_failed: debugfs_remove_recursive(binder_debugfs_dir_entry_root); diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c new file mode 100644 index 000000000000..78c42c0d62b9 --- /dev/null +++ b/drivers/android/binder_alloc.c @@ -0,0 +1,981 @@ +/* binder_alloc.c + * + * Android IPC Subsystem + * + * Copyright (C) 2007-2017 Google, Inc. + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <asm/cacheflush.h> +#include <linux/list.h> +#include <linux/sched/mm.h> +#include <linux/module.h> +#include <linux/rtmutex.h> +#include <linux/rbtree.h> +#include <linux/seq_file.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/list_lru.h> +#include "binder_alloc.h" +#include "binder_trace.h" + +struct list_lru binder_alloc_lru; + +static DEFINE_MUTEX(binder_alloc_mmap_lock); + +enum { + BINDER_DEBUG_OPEN_CLOSE = 1U << 1, + BINDER_DEBUG_BUFFER_ALLOC = 1U << 2, + BINDER_DEBUG_BUFFER_ALLOC_ASYNC = 1U << 3, +}; +static uint32_t binder_alloc_debug_mask; + +module_param_named(debug_mask, binder_alloc_debug_mask, + uint, 0644); + +#define binder_alloc_debug(mask, x...) \ + do { \ + if (binder_alloc_debug_mask & mask) \ + pr_info(x); \ + } while (0) + +static struct binder_buffer *binder_buffer_next(struct binder_buffer *buffer) +{ + return list_entry(buffer->entry.next, struct binder_buffer, entry); +} + +static struct binder_buffer *binder_buffer_prev(struct binder_buffer *buffer) +{ + return list_entry(buffer->entry.prev, struct binder_buffer, entry); +} + +static size_t binder_alloc_buffer_size(struct binder_alloc *alloc, + struct binder_buffer *buffer) +{ + if (list_is_last(&buffer->entry, &alloc->buffers)) + return (u8 *)alloc->buffer + + alloc->buffer_size - (u8 *)buffer->data; + return (u8 *)binder_buffer_next(buffer)->data - (u8 *)buffer->data; +} + +static void binder_insert_free_buffer(struct binder_alloc *alloc, + struct binder_buffer *new_buffer) +{ + struct rb_node **p = &alloc->free_buffers.rb_node; + struct rb_node *parent = NULL; + struct binder_buffer *buffer; + size_t buffer_size; + size_t new_buffer_size; + + BUG_ON(!new_buffer->free); + + new_buffer_size = binder_alloc_buffer_size(alloc, new_buffer); + + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: add free buffer, size %zd, at %pK\n", + alloc->pid, new_buffer_size, new_buffer); + + while (*p) { + parent = *p; + buffer = rb_entry(parent, struct binder_buffer, rb_node); + BUG_ON(!buffer->free); + + buffer_size = binder_alloc_buffer_size(alloc, buffer); + + if (new_buffer_size < buffer_size) + p = &parent->rb_left; + else + p = &parent->rb_right; + } + rb_link_node(&new_buffer->rb_node, parent, p); + rb_insert_color(&new_buffer->rb_node, &alloc->free_buffers); +} + +static void binder_insert_allocated_buffer_locked( + struct binder_alloc *alloc, struct binder_buffer *new_buffer) +{ + struct rb_node **p = &alloc->allocated_buffers.rb_node; + struct rb_node *parent = NULL; + struct binder_buffer *buffer; + + BUG_ON(new_buffer->free); + + while (*p) { + parent = *p; + buffer = rb_entry(parent, struct binder_buffer, rb_node); + BUG_ON(buffer->free); + + if (new_buffer->data < buffer->data) + p = &parent->rb_left; + else if (new_buffer->data > buffer->data) + p = &parent->rb_right; + else + BUG(); + } + rb_link_node(&new_buffer->rb_node, parent, p); + rb_insert_color(&new_buffer->rb_node, &alloc->allocated_buffers); +} + +static struct binder_buffer *binder_alloc_prepare_to_free_locked( + struct binder_alloc *alloc, + uintptr_t user_ptr) +{ + struct rb_node *n = alloc->allocated_buffers.rb_node; + struct binder_buffer *buffer; + void *kern_ptr; + + kern_ptr = (void *)(user_ptr - alloc->user_buffer_offset); + + while (n) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + BUG_ON(buffer->free); + + if (kern_ptr < buffer->data) + n = n->rb_left; + else if (kern_ptr > buffer->data) + n = n->rb_right; + else { + /* + * Guard against user threads attempting to + * free the buffer twice + */ + if (buffer->free_in_progress) { + pr_err("%d:%d FREE_BUFFER u%016llx user freed buffer twice\n", + alloc->pid, current->pid, (u64)user_ptr); + return NULL; + } + buffer->free_in_progress = 1; + return buffer; + } + } + return NULL; +} + +/** + * binder_alloc_buffer_lookup() - get buffer given user ptr + * @alloc: binder_alloc for this proc + * @user_ptr: User pointer to buffer data + * + * Validate userspace pointer to buffer data and return buffer corresponding to + * that user pointer. Search the rb tree for buffer that matches user data + * pointer. + * + * Return: Pointer to buffer or NULL + */ +struct binder_buffer *binder_alloc_prepare_to_free(struct binder_alloc *alloc, + uintptr_t user_ptr) +{ + struct binder_buffer *buffer; + + mutex_lock(&alloc->mutex); + buffer = binder_alloc_prepare_to_free_locked(alloc, user_ptr); + mutex_unlock(&alloc->mutex); + return buffer; +} + +static int binder_update_page_range(struct binder_alloc *alloc, int allocate, + void *start, void *end, + struct vm_area_struct *vma) +{ + void *page_addr; + unsigned long user_page_addr; + struct binder_lru_page *page; + struct mm_struct *mm = NULL; + bool need_mm = false; + + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: %s pages %pK-%pK\n", alloc->pid, + allocate ? "allocate" : "free", start, end); + + if (end <= start) + return 0; + + trace_binder_update_page_range(alloc, allocate, start, end); + + if (allocate == 0) + goto free_range; + + for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { + page = &alloc->pages[(page_addr - alloc->buffer) / PAGE_SIZE]; + if (!page->page_ptr) { + need_mm = true; + break; + } + } + + if (!vma && need_mm) + mm = get_task_mm(alloc->tsk); + + if (mm) { + down_write(&mm->mmap_sem); + vma = alloc->vma; + if (vma && mm != alloc->vma_vm_mm) { + pr_err("%d: vma mm and task mm mismatch\n", + alloc->pid); + vma = NULL; + } + } + + if (!vma && need_mm) { + pr_err("%d: binder_alloc_buf failed to map pages in userspace, no vma\n", + alloc->pid); + goto err_no_vma; + } + + for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) { + int ret; + bool on_lru; + size_t index; + + index = (page_addr - alloc->buffer) / PAGE_SIZE; + page = &alloc->pages[index]; + + if (page->page_ptr) { + trace_binder_alloc_lru_start(alloc, index); + + on_lru = list_lru_del(&binder_alloc_lru, &page->lru); + WARN_ON(!on_lru); + + trace_binder_alloc_lru_end(alloc, index); + continue; + } + + if (WARN_ON(!vma)) + goto err_page_ptr_cleared; + + trace_binder_alloc_page_start(alloc, index); + page->page_ptr = alloc_page(GFP_KERNEL | + __GFP_HIGHMEM | + __GFP_ZERO); + if (!page->page_ptr) { + pr_err("%d: binder_alloc_buf failed for page at %pK\n", + alloc->pid, page_addr); + goto err_alloc_page_failed; + } + page->alloc = alloc; + INIT_LIST_HEAD(&page->lru); + + ret = map_kernel_range_noflush((unsigned long)page_addr, + PAGE_SIZE, PAGE_KERNEL, + &page->page_ptr); + flush_cache_vmap((unsigned long)page_addr, + (unsigned long)page_addr + PAGE_SIZE); + if (ret != 1) { + pr_err("%d: binder_alloc_buf failed to map page at %pK in kernel\n", + alloc->pid, page_addr); + goto err_map_kernel_failed; + } + user_page_addr = + (uintptr_t)page_addr + alloc->user_buffer_offset; + ret = vm_insert_page(vma, user_page_addr, page[0].page_ptr); + if (ret) { + pr_err("%d: binder_alloc_buf failed to map page at %lx in userspace\n", + alloc->pid, user_page_addr); + goto err_vm_insert_page_failed; + } + + trace_binder_alloc_page_end(alloc, index); + /* vm_insert_page does not seem to increment the refcount */ + } + if (mm) { + up_write(&mm->mmap_sem); + mmput(mm); + } + return 0; + +free_range: + for (page_addr = end - PAGE_SIZE; page_addr >= start; + page_addr -= PAGE_SIZE) { + bool ret; + size_t index; + + index = (page_addr - alloc->buffer) / PAGE_SIZE; + page = &alloc->pages[index]; + + trace_binder_free_lru_start(alloc, index); + + ret = list_lru_add(&binder_alloc_lru, &page->lru); + WARN_ON(!ret); + + trace_binder_free_lru_end(alloc, index); + continue; + +err_vm_insert_page_failed: + unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); +err_map_kernel_failed: + __free_page(page->page_ptr); + page->page_ptr = NULL; +err_alloc_page_failed: +err_page_ptr_cleared: + ; + } +err_no_vma: + if (mm) { + up_write(&mm->mmap_sem); + mmput(mm); + } + return vma ? -ENOMEM : -ESRCH; +} + +struct binder_buffer *binder_alloc_new_buf_locked(struct binder_alloc *alloc, + size_t data_size, + size_t offsets_size, + size_t extra_buffers_size, + int is_async) +{ + struct rb_node *n = alloc->free_buffers.rb_node; + struct binder_buffer *buffer; + size_t buffer_size; + struct rb_node *best_fit = NULL; + void *has_page_addr; + void *end_page_addr; + size_t size, data_offsets_size; + int ret; + + if (alloc->vma == NULL) { + pr_err("%d: binder_alloc_buf, no vma\n", + alloc->pid); + return ERR_PTR(-ESRCH); + } + + data_offsets_size = ALIGN(data_size, sizeof(void *)) + + ALIGN(offsets_size, sizeof(void *)); + + if (data_offsets_size < data_size || data_offsets_size < offsets_size) { + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: got transaction with invalid size %zd-%zd\n", + alloc->pid, data_size, offsets_size); + return ERR_PTR(-EINVAL); + } + size = data_offsets_size + ALIGN(extra_buffers_size, sizeof(void *)); + if (size < data_offsets_size || size < extra_buffers_size) { + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: got transaction with invalid extra_buffers_size %zd\n", + alloc->pid, extra_buffers_size); + return ERR_PTR(-EINVAL); + } + if (is_async && + alloc->free_async_space < size + sizeof(struct binder_buffer)) { + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: binder_alloc_buf size %zd failed, no async space left\n", + alloc->pid, size); + return ERR_PTR(-ENOSPC); + } + + /* Pad 0-size buffers so they get assigned unique addresses */ + size = max(size, sizeof(void *)); + + while (n) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + BUG_ON(!buffer->free); + buffer_size = binder_alloc_buffer_size(alloc, buffer); + + if (size < buffer_size) { + best_fit = n; + n = n->rb_left; + } else if (size > buffer_size) + n = n->rb_right; + else { + best_fit = n; + break; + } + } + if (best_fit == NULL) { + size_t allocated_buffers = 0; + size_t largest_alloc_size = 0; + size_t total_alloc_size = 0; + size_t free_buffers = 0; + size_t largest_free_size = 0; + size_t total_free_size = 0; + + for (n = rb_first(&alloc->allocated_buffers); n != NULL; + n = rb_next(n)) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + buffer_size = binder_alloc_buffer_size(alloc, buffer); + allocated_buffers++; + total_alloc_size += buffer_size; + if (buffer_size > largest_alloc_size) + largest_alloc_size = buffer_size; + } + for (n = rb_first(&alloc->free_buffers); n != NULL; + n = rb_next(n)) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + buffer_size = binder_alloc_buffer_size(alloc, buffer); + free_buffers++; + total_free_size += buffer_size; + if (buffer_size > largest_free_size) + largest_free_size = buffer_size; + } + pr_err("%d: binder_alloc_buf size %zd failed, no address space\n", + alloc->pid, size); + pr_err("allocated: %zd (num: %zd largest: %zd), free: %zd (num: %zd largest: %zd)\n", + total_alloc_size, allocated_buffers, largest_alloc_size, + total_free_size, free_buffers, largest_free_size); + return ERR_PTR(-ENOSPC); + } + if (n == NULL) { + buffer = rb_entry(best_fit, struct binder_buffer, rb_node); + buffer_size = binder_alloc_buffer_size(alloc, buffer); + } + + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: binder_alloc_buf size %zd got buffer %pK size %zd\n", + alloc->pid, size, buffer, buffer_size); + + has_page_addr = + (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK); + WARN_ON(n && buffer_size != size); + end_page_addr = + (void *)PAGE_ALIGN((uintptr_t)buffer->data + size); + if (end_page_addr > has_page_addr) + end_page_addr = has_page_addr; + ret = binder_update_page_range(alloc, 1, + (void *)PAGE_ALIGN((uintptr_t)buffer->data), end_page_addr, NULL); + if (ret) + return ERR_PTR(ret); + + if (buffer_size != size) { + struct binder_buffer *new_buffer; + + new_buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!new_buffer) { + pr_err("%s: %d failed to alloc new buffer struct\n", + __func__, alloc->pid); + goto err_alloc_buf_struct_failed; + } + new_buffer->data = (u8 *)buffer->data + size; + list_add(&new_buffer->entry, &buffer->entry); + new_buffer->free = 1; + binder_insert_free_buffer(alloc, new_buffer); + } + + rb_erase(best_fit, &alloc->free_buffers); + buffer->free = 0; + buffer->free_in_progress = 0; + binder_insert_allocated_buffer_locked(alloc, buffer); + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: binder_alloc_buf size %zd got %pK\n", + alloc->pid, size, buffer); + buffer->data_size = data_size; + buffer->offsets_size = offsets_size; + buffer->async_transaction = is_async; + buffer->extra_buffers_size = extra_buffers_size; + if (is_async) { + alloc->free_async_space -= size + sizeof(struct binder_buffer); + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, + "%d: binder_alloc_buf size %zd async free %zd\n", + alloc->pid, size, alloc->free_async_space); + } + return buffer; + +err_alloc_buf_struct_failed: + binder_update_page_range(alloc, 0, + (void *)PAGE_ALIGN((uintptr_t)buffer->data), + end_page_addr, NULL); + return ERR_PTR(-ENOMEM); +} + +/** + * binder_alloc_new_buf() - Allocate a new binder buffer + * @alloc: binder_alloc for this proc + * @data_size: size of user data buffer + * @offsets_size: user specified buffer offset + * @extra_buffers_size: size of extra space for meta-data (eg, security context) + * @is_async: buffer for async transaction + * + * Allocate a new buffer given the requested sizes. Returns + * the kernel version of the buffer pointer. The size allocated + * is the sum of the three given sizes (each rounded up to + * pointer-sized boundary) + * + * Return: The allocated buffer or %NULL if error + */ +struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, + size_t data_size, + size_t offsets_size, + size_t extra_buffers_size, + int is_async) +{ + struct binder_buffer *buffer; + + mutex_lock(&alloc->mutex); + buffer = binder_alloc_new_buf_locked(alloc, data_size, offsets_size, + extra_buffers_size, is_async); + mutex_unlock(&alloc->mutex); + return buffer; +} + +static void *buffer_start_page(struct binder_buffer *buffer) +{ + return (void *)((uintptr_t)buffer->data & PAGE_MASK); +} + +static void *prev_buffer_end_page(struct binder_buffer *buffer) +{ + return (void *)(((uintptr_t)(buffer->data) - 1) & PAGE_MASK); +} + +static void binder_delete_free_buffer(struct binder_alloc *alloc, + struct binder_buffer *buffer) +{ + struct binder_buffer *prev, *next = NULL; + bool to_free = true; + BUG_ON(alloc->buffers.next == &buffer->entry); + prev = binder_buffer_prev(buffer); + BUG_ON(!prev->free); + if (prev_buffer_end_page(prev) == buffer_start_page(buffer)) { + to_free = false; + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: merge free, buffer %pK share page with %pK\n", + alloc->pid, buffer->data, prev->data); + } + + if (!list_is_last(&buffer->entry, &alloc->buffers)) { + next = binder_buffer_next(buffer); + if (buffer_start_page(next) == buffer_start_page(buffer)) { + to_free = false; + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: merge free, buffer %pK share page with %pK\n", + alloc->pid, + buffer->data, + next->data); + } + } + + if (PAGE_ALIGNED(buffer->data)) { + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: merge free, buffer start %pK is page aligned\n", + alloc->pid, buffer->data); + to_free = false; + } + + if (to_free) { + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: merge free, buffer %pK do not share page with %pK or %pK\n", + alloc->pid, buffer->data, + prev->data, next->data); + binder_update_page_range(alloc, 0, buffer_start_page(buffer), + buffer_start_page(buffer) + PAGE_SIZE, + NULL); + } + list_del(&buffer->entry); + kfree(buffer); +} + +static void binder_free_buf_locked(struct binder_alloc *alloc, + struct binder_buffer *buffer) +{ + size_t size, buffer_size; + + buffer_size = binder_alloc_buffer_size(alloc, buffer); + + size = ALIGN(buffer->data_size, sizeof(void *)) + + ALIGN(buffer->offsets_size, sizeof(void *)) + + ALIGN(buffer->extra_buffers_size, sizeof(void *)); + + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%d: binder_free_buf %pK size %zd buffer_size %zd\n", + alloc->pid, buffer, size, buffer_size); + + BUG_ON(buffer->free); + BUG_ON(size > buffer_size); + BUG_ON(buffer->transaction != NULL); + BUG_ON(buffer->data < alloc->buffer); + BUG_ON(buffer->data > alloc->buffer + alloc->buffer_size); + + if (buffer->async_transaction) { + alloc->free_async_space += size + sizeof(struct binder_buffer); + + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC, + "%d: binder_free_buf size %zd async free %zd\n", + alloc->pid, size, alloc->free_async_space); + } + + binder_update_page_range(alloc, 0, + (void *)PAGE_ALIGN((uintptr_t)buffer->data), + (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK), + NULL); + + rb_erase(&buffer->rb_node, &alloc->allocated_buffers); + buffer->free = 1; + if (!list_is_last(&buffer->entry, &alloc->buffers)) { + struct binder_buffer *next = binder_buffer_next(buffer); + + if (next->free) { + rb_erase(&next->rb_node, &alloc->free_buffers); + binder_delete_free_buffer(alloc, next); + } + } + if (alloc->buffers.next != &buffer->entry) { + struct binder_buffer *prev = binder_buffer_prev(buffer); + + if (prev->free) { + binder_delete_free_buffer(alloc, buffer); + rb_erase(&prev->rb_node, &alloc->free_buffers); + buffer = prev; + } + } + binder_insert_free_buffer(alloc, buffer); +} + +/** + * binder_alloc_free_buf() - free a binder buffer + * @alloc: binder_alloc for this proc + * @buffer: kernel pointer to buffer + * + * Free the buffer allocated via binder_alloc_new_buffer() + */ +void binder_alloc_free_buf(struct binder_alloc *alloc, + struct binder_buffer *buffer) +{ + mutex_lock(&alloc->mutex); + binder_free_buf_locked(alloc, buffer); + mutex_unlock(&alloc->mutex); +} + +/** + * binder_alloc_mmap_handler() - map virtual address space for proc + * @alloc: alloc structure for this proc + * @vma: vma passed to mmap() + * + * Called by binder_mmap() to initialize the space specified in + * vma for allocating binder buffers + * + * Return: + * 0 = success + * -EBUSY = address space already mapped + * -ENOMEM = failed to map memory to given address space + */ +int binder_alloc_mmap_handler(struct binder_alloc *alloc, + struct vm_area_struct *vma) +{ + int ret; + struct vm_struct *area; + const char *failure_string; + struct binder_buffer *buffer; + + mutex_lock(&binder_alloc_mmap_lock); + if (alloc->buffer) { + ret = -EBUSY; + failure_string = "already mapped"; + goto err_already_mapped; + } + + area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP); + if (area == NULL) { + ret = -ENOMEM; + failure_string = "get_vm_area"; + goto err_get_vm_area_failed; + } + alloc->buffer = area->addr; + alloc->user_buffer_offset = + vma->vm_start - (uintptr_t)alloc->buffer; + mutex_unlock(&binder_alloc_mmap_lock); + +#ifdef CONFIG_CPU_CACHE_VIPT + if (cache_is_vipt_aliasing()) { + while (CACHE_COLOUR( + (vma->vm_start ^ (uint32_t)alloc->buffer))) { + pr_info("%s: %d %lx-%lx maps %pK bad alignment\n", + __func__, alloc->pid, vma->vm_start, + vma->vm_end, alloc->buffer); + vma->vm_start += PAGE_SIZE; + } + } +#endif + alloc->pages = kzalloc(sizeof(alloc->pages[0]) * + ((vma->vm_end - vma->vm_start) / PAGE_SIZE), + GFP_KERNEL); + if (alloc->pages == NULL) { + ret = -ENOMEM; + failure_string = "alloc page array"; + goto err_alloc_pages_failed; + } + alloc->buffer_size = vma->vm_end - vma->vm_start; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + failure_string = "alloc buffer struct"; + goto err_alloc_buf_struct_failed; + } + + buffer->data = alloc->buffer; + INIT_LIST_HEAD(&alloc->buffers); + list_add(&buffer->entry, &alloc->buffers); + buffer->free = 1; + binder_insert_free_buffer(alloc, buffer); + alloc->free_async_space = alloc->buffer_size / 2; + barrier(); + alloc->vma = vma; + alloc->vma_vm_mm = vma->vm_mm; + + return 0; + +err_alloc_buf_struct_failed: + kfree(alloc->pages); + alloc->pages = NULL; +err_alloc_pages_failed: + mutex_lock(&binder_alloc_mmap_lock); + vfree(alloc->buffer); + alloc->buffer = NULL; +err_get_vm_area_failed: +err_already_mapped: + mutex_unlock(&binder_alloc_mmap_lock); + pr_err("%s: %d %lx-%lx %s failed %d\n", __func__, + alloc->pid, vma->vm_start, vma->vm_end, failure_string, ret); + return ret; +} + + +void binder_alloc_deferred_release(struct binder_alloc *alloc) +{ + struct rb_node *n; + int buffers, page_count; + struct binder_buffer *buffer; + + BUG_ON(alloc->vma); + + buffers = 0; + mutex_lock(&alloc->mutex); + while ((n = rb_first(&alloc->allocated_buffers))) { + buffer = rb_entry(n, struct binder_buffer, rb_node); + + /* Transaction should already have been freed */ + BUG_ON(buffer->transaction); + + binder_free_buf_locked(alloc, buffer); + buffers++; + } + + while (!list_empty(&alloc->buffers)) { + buffer = list_first_entry(&alloc->buffers, + struct binder_buffer, entry); + WARN_ON(!buffer->free); + + list_del(&buffer->entry); + WARN_ON_ONCE(!list_empty(&alloc->buffers)); + kfree(buffer); + } + + page_count = 0; + if (alloc->pages) { + int i; + + for (i = 0; i < alloc->buffer_size / PAGE_SIZE; i++) { + void *page_addr; + bool on_lru; + + if (!alloc->pages[i].page_ptr) + continue; + + on_lru = list_lru_del(&binder_alloc_lru, + &alloc->pages[i].lru); + page_addr = alloc->buffer + i * PAGE_SIZE; + binder_alloc_debug(BINDER_DEBUG_BUFFER_ALLOC, + "%s: %d: page %d at %pK %s\n", + __func__, alloc->pid, i, page_addr, + on_lru ? "on lru" : "active"); + unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE); + __free_page(alloc->pages[i].page_ptr); + page_count++; + } + kfree(alloc->pages); + vfree(alloc->buffer); + } + mutex_unlock(&alloc->mutex); + + binder_alloc_debug(BINDER_DEBUG_OPEN_CLOSE, + "%s: %d buffers %d, pages %d\n", + __func__, alloc->pid, buffers, page_count); +} + +static void print_binder_buffer(struct seq_file *m, const char *prefix, + struct binder_buffer *buffer) +{ + seq_printf(m, "%s %d: %pK size %zd:%zd:%zd %s\n", + prefix, buffer->debug_id, buffer->data, + buffer->data_size, buffer->offsets_size, + buffer->extra_buffers_size, + buffer->transaction ? "active" : "delivered"); +} + +/** + * binder_alloc_print_allocated() - print buffer info + * @m: seq_file for output via seq_printf() + * @alloc: binder_alloc for this proc + * + * Prints information about every buffer associated with + * the binder_alloc state to the given seq_file + */ +void binder_alloc_print_allocated(struct seq_file *m, + struct binder_alloc *alloc) +{ + struct rb_node *n; + + mutex_lock(&alloc->mutex); + for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) + print_binder_buffer(m, " buffer", + rb_entry(n, struct binder_buffer, rb_node)); + mutex_unlock(&alloc->mutex); +} + +/** + * binder_alloc_get_allocated_count() - return count of buffers + * @alloc: binder_alloc for this proc + * + * Return: count of allocated buffers + */ +int binder_alloc_get_allocated_count(struct binder_alloc *alloc) +{ + struct rb_node *n; + int count = 0; + + mutex_lock(&alloc->mutex); + for (n = rb_first(&alloc->allocated_buffers); n != NULL; n = rb_next(n)) + count++; + mutex_unlock(&alloc->mutex); + return count; +} + + +/** + * binder_alloc_vma_close() - invalidate address space + * @alloc: binder_alloc for this proc + * + * Called from binder_vma_close() when releasing address space. + * Clears alloc->vma to prevent new incoming transactions from + * allocating more buffers. + */ +void binder_alloc_vma_close(struct binder_alloc *alloc) +{ + WRITE_ONCE(alloc->vma, NULL); + WRITE_ONCE(alloc->vma_vm_mm, NULL); +} + +/** + * binder_alloc_free_page() - shrinker callback to free pages + * @item: item to free + * @lock: lock protecting the item + * @cb_arg: callback argument + * + * Called from list_lru_walk() in binder_shrink_scan() to free + * up pages when the system is under memory pressure. + */ +enum lru_status binder_alloc_free_page(struct list_head *item, + struct list_lru_one *lru, + spinlock_t *lock, + void *cb_arg) +{ + struct mm_struct *mm = NULL; + struct binder_lru_page *page = container_of(item, + struct binder_lru_page, + lru); + struct binder_alloc *alloc; + uintptr_t page_addr; + size_t index; + + alloc = page->alloc; + if (!mutex_trylock(&alloc->mutex)) + goto err_get_alloc_mutex_failed; + + if (!page->page_ptr) + goto err_page_already_freed; + + index = page - alloc->pages; + page_addr = (uintptr_t)alloc->buffer + index * PAGE_SIZE; + if (alloc->vma) { + mm = get_task_mm(alloc->tsk); + if (!mm) + goto err_get_task_mm_failed; + if (!down_write_trylock(&mm->mmap_sem)) + goto err_down_write_mmap_sem_failed; + + trace_binder_unmap_user_start(alloc, index); + + zap_page_range(alloc->vma, + page_addr + alloc->user_buffer_offset, + PAGE_SIZE); + + trace_binder_unmap_user_end(alloc, index); + + up_write(&mm->mmap_sem); + mmput(mm); + } + + trace_binder_unmap_kernel_start(alloc, index); + + unmap_kernel_range(page_addr, PAGE_SIZE); + __free_page(page->page_ptr); + page->page_ptr = NULL; + + trace_binder_unmap_kernel_end(alloc, index); + + list_lru_isolate(lru, item); + + mutex_unlock(&alloc->mutex); + return LRU_REMOVED; + +err_down_write_mmap_sem_failed: + mmput(mm); +err_get_task_mm_failed: +err_page_already_freed: + mutex_unlock(&alloc->mutex); +err_get_alloc_mutex_failed: + return LRU_SKIP; +} + +static unsigned long +binder_shrink_count(struct shrinker *shrink, struct shrink_control *sc) +{ + unsigned long ret = list_lru_count(&binder_alloc_lru); + return ret; +} + +static unsigned long +binder_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) +{ + unsigned long ret; + + ret = list_lru_walk(&binder_alloc_lru, binder_alloc_free_page, + NULL, sc->nr_to_scan); + return ret; +} + +struct shrinker binder_shrinker = { + .count_objects = binder_shrink_count, + .scan_objects = binder_shrink_scan, + .seeks = DEFAULT_SEEKS, +}; + +/** + * binder_alloc_init() - called by binder_open() for per-proc initialization + * @alloc: binder_alloc for this proc + * + * Called from binder_open() to initialize binder_alloc fields for + * new binder proc + */ +void binder_alloc_init(struct binder_alloc *alloc) +{ + alloc->tsk = current->group_leader; + alloc->pid = current->group_leader->pid; + mutex_init(&alloc->mutex); +} + +void binder_alloc_shrinker_init(void) +{ + list_lru_init(&binder_alloc_lru); + register_shrinker(&binder_shrinker); +} diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h new file mode 100644 index 000000000000..fa707cc63393 --- /dev/null +++ b/drivers/android/binder_alloc.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2017 Google, Inc. + * + * 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_BINDER_ALLOC_H +#define _LINUX_BINDER_ALLOC_H + +#include <linux/rbtree.h> +#include <linux/list.h> +#include <linux/mm.h> +#include <linux/rtmutex.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/list_lru.h> + +extern struct list_lru binder_alloc_lru; +struct binder_transaction; + +/** + * struct binder_buffer - buffer used for binder transactions + * @entry: entry alloc->buffers + * @rb_node: node for allocated_buffers/free_buffers rb trees + * @free: true if buffer is free + * @allow_user_free: describe the second member of struct blah, + * @async_transaction: describe the second member of struct blah, + * @debug_id: describe the second member of struct blah, + * @transaction: describe the second member of struct blah, + * @target_node: describe the second member of struct blah, + * @data_size: describe the second member of struct blah, + * @offsets_size: describe the second member of struct blah, + * @extra_buffers_size: describe the second member of struct blah, + * @data:i describe the second member of struct blah, + * + * Bookkeeping structure for binder transaction buffers + */ +struct binder_buffer { + struct list_head entry; /* free and allocated entries by address */ + struct rb_node rb_node; /* free entry by size or allocated entry */ + /* by address */ + unsigned free:1; + unsigned allow_user_free:1; + unsigned async_transaction:1; + unsigned free_in_progress:1; + unsigned debug_id:28; + + struct binder_transaction *transaction; + + struct binder_node *target_node; + size_t data_size; + size_t offsets_size; + size_t extra_buffers_size; + void *data; +}; + +/** + * struct binder_lru_page - page object used for binder shrinker + * @page_ptr: pointer to physical page in mmap'd space + * @lru: entry in binder_alloc_lru + * @alloc: binder_alloc for a proc + */ +struct binder_lru_page { + struct list_head lru; + struct page *page_ptr; + struct binder_alloc *alloc; +}; + +/** + * struct binder_alloc - per-binder proc state for binder allocator + * @vma: vm_area_struct passed to mmap_handler + * (invarient after mmap) + * @tsk: tid for task that called init for this proc + * (invariant after init) + * @vma_vm_mm: copy of vma->vm_mm (invarient after mmap) + * @buffer: base of per-proc address space mapped via mmap + * @user_buffer_offset: offset between user and kernel VAs for buffer + * @buffers: list of all buffers for this proc + * @free_buffers: rb tree of buffers available for allocation + * sorted by size + * @allocated_buffers: rb tree of allocated buffers sorted by address + * @free_async_space: VA space available for async buffers. This is + * initialized at mmap time to 1/2 the full VA space + * @pages: array of binder_lru_page + * @buffer_size: size of address space specified via mmap + * @pid: pid for associated binder_proc (invariant after init) + * + * Bookkeeping structure for per-proc address space management for binder + * buffers. It is normally initialized during binder_init() and binder_mmap() + * calls. The address space is used for both user-visible buffers and for + * struct binder_buffer objects used to track the user buffers + */ +struct binder_alloc { + struct mutex mutex; + struct task_struct *tsk; + struct vm_area_struct *vma; + struct mm_struct *vma_vm_mm; + void *buffer; + ptrdiff_t user_buffer_offset; + struct list_head buffers; + struct rb_root free_buffers; + struct rb_root allocated_buffers; + size_t free_async_space; + struct binder_lru_page *pages; + size_t buffer_size; + uint32_t buffer_free; + int pid; +}; + +#ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST +void binder_selftest_alloc(struct binder_alloc *alloc); +#else +static inline void binder_selftest_alloc(struct binder_alloc *alloc) {} +#endif +enum lru_status binder_alloc_free_page(struct list_head *item, + struct list_lru_one *lru, + spinlock_t *lock, void *cb_arg); +extern struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc, + size_t data_size, + size_t offsets_size, + size_t extra_buffers_size, + int is_async); +extern void binder_alloc_init(struct binder_alloc *alloc); +void binder_alloc_shrinker_init(void); +extern void binder_alloc_vma_close(struct binder_alloc *alloc); +extern struct binder_buffer * +binder_alloc_prepare_to_free(struct binder_alloc *alloc, + uintptr_t user_ptr); +extern void binder_alloc_free_buf(struct binder_alloc *alloc, + struct binder_buffer *buffer); +extern int binder_alloc_mmap_handler(struct binder_alloc *alloc, + struct vm_area_struct *vma); +extern void binder_alloc_deferred_release(struct binder_alloc *alloc); +extern int binder_alloc_get_allocated_count(struct binder_alloc *alloc); +extern void binder_alloc_print_allocated(struct seq_file *m, + struct binder_alloc *alloc); + +/** + * binder_alloc_get_free_async_space() - get free space available for async + * @alloc: binder_alloc for this proc + * + * Return: the bytes remaining in the address-space for async transactions + */ +static inline size_t +binder_alloc_get_free_async_space(struct binder_alloc *alloc) +{ + size_t free_async_space; + + mutex_lock(&alloc->mutex); + free_async_space = alloc->free_async_space; + mutex_unlock(&alloc->mutex); + return free_async_space; +} + +/** + * binder_alloc_get_user_buffer_offset() - get offset between kernel/user addrs + * @alloc: binder_alloc for this proc + * + * Return: the offset between kernel and user-space addresses to use for + * virtual address conversion + */ +static inline ptrdiff_t +binder_alloc_get_user_buffer_offset(struct binder_alloc *alloc) +{ + /* + * user_buffer_offset is constant if vma is set and + * undefined if vma is not set. It is possible to + * get here with !alloc->vma if the target process + * is dying while a transaction is being initiated. + * Returning the old value is ok in this case and + * the transaction will fail. + */ + return alloc->user_buffer_offset; +} + +#endif /* _LINUX_BINDER_ALLOC_H */ + diff --git a/drivers/android/binder_alloc_selftest.c b/drivers/android/binder_alloc_selftest.c new file mode 100644 index 000000000000..8bd7bcef967d --- /dev/null +++ b/drivers/android/binder_alloc_selftest.c @@ -0,0 +1,310 @@ +/* binder_alloc_selftest.c + * + * Android IPC Subsystem + * + * Copyright (C) 2017 Google, Inc. + * + * 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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/mm_types.h> +#include <linux/err.h> +#include "binder_alloc.h" + +#define BUFFER_NUM 5 +#define BUFFER_MIN_SIZE (PAGE_SIZE / 8) + +static bool binder_selftest_run = true; +static int binder_selftest_failures; +static DEFINE_MUTEX(binder_selftest_lock); + +/** + * enum buf_end_align_type - Page alignment of a buffer + * end with regard to the end of the previous buffer. + * + * In the pictures below, buf2 refers to the buffer we + * are aligning. buf1 refers to previous buffer by addr. + * Symbol [ means the start of a buffer, ] means the end + * of a buffer, and | means page boundaries. + */ +enum buf_end_align_type { + /** + * @SAME_PAGE_UNALIGNED: The end of this buffer is on + * the same page as the end of the previous buffer and + * is not page aligned. Examples: + * buf1 ][ buf2 ][ ... + * buf1 ]|[ buf2 ][ ... + */ + SAME_PAGE_UNALIGNED = 0, + /** + * @SAME_PAGE_ALIGNED: When the end of the previous buffer + * is not page aligned, the end of this buffer is on the + * same page as the end of the previous buffer and is page + * aligned. When the previous buffer is page aligned, the + * end of this buffer is aligned to the next page boundary. + * Examples: + * buf1 ][ buf2 ]| ... + * buf1 ]|[ buf2 ]| ... + */ + SAME_PAGE_ALIGNED, + /** + * @NEXT_PAGE_UNALIGNED: The end of this buffer is on + * the page next to the end of the previous buffer and + * is not page aligned. Examples: + * buf1 ][ buf2 | buf2 ][ ... + * buf1 ]|[ buf2 | buf2 ][ ... + */ + NEXT_PAGE_UNALIGNED, + /** + * @NEXT_PAGE_ALIGNED: The end of this buffer is on + * the page next to the end of the previous buffer and + * is page aligned. Examples: + * buf1 ][ buf2 | buf2 ]| ... + * buf1 ]|[ buf2 | buf2 ]| ... + */ + NEXT_PAGE_ALIGNED, + /** + * @NEXT_NEXT_UNALIGNED: The end of this buffer is on + * the page that follows the page after the end of the + * previous buffer and is not page aligned. Examples: + * buf1 ][ buf2 | buf2 | buf2 ][ ... + * buf1 ]|[ buf2 | buf2 | buf2 ][ ... + */ + NEXT_NEXT_UNALIGNED, + LOOP_END, +}; + +static void pr_err_size_seq(size_t *sizes, int *seq) +{ + int i; + + pr_err("alloc sizes: "); + for (i = 0; i < BUFFER_NUM; i++) + pr_cont("[%zu]", sizes[i]); + pr_cont("\n"); + pr_err("free seq: "); + for (i = 0; i < BUFFER_NUM; i++) + pr_cont("[%d]", seq[i]); + pr_cont("\n"); +} + +static bool check_buffer_pages_allocated(struct binder_alloc *alloc, + struct binder_buffer *buffer, + size_t size) +{ + void *page_addr, *end; + int page_index; + + end = (void *)PAGE_ALIGN((uintptr_t)buffer->data + size); + page_addr = buffer->data; + for (; page_addr < end; page_addr += PAGE_SIZE) { + page_index = (page_addr - alloc->buffer) / PAGE_SIZE; + if (!alloc->pages[page_index].page_ptr || + !list_empty(&alloc->pages[page_index].lru)) { + pr_err("expect alloc but is %s at page index %d\n", + alloc->pages[page_index].page_ptr ? + "lru" : "free", page_index); + return false; + } + } + return true; +} + +static void binder_selftest_alloc_buf(struct binder_alloc *alloc, + struct binder_buffer *buffers[], + size_t *sizes, int *seq) +{ + int i; + + for (i = 0; i < BUFFER_NUM; i++) { + buffers[i] = binder_alloc_new_buf(alloc, sizes[i], 0, 0, 0); + if (IS_ERR(buffers[i]) || + !check_buffer_pages_allocated(alloc, buffers[i], + sizes[i])) { + pr_err_size_seq(sizes, seq); + binder_selftest_failures++; + } + } +} + +static void binder_selftest_free_buf(struct binder_alloc *alloc, + struct binder_buffer *buffers[], + size_t *sizes, int *seq, size_t end) +{ + int i; + + for (i = 0; i < BUFFER_NUM; i++) + binder_alloc_free_buf(alloc, buffers[seq[i]]); + + for (i = 0; i < end / PAGE_SIZE; i++) { + /** + * Error message on a free page can be false positive + * if binder shrinker ran during binder_alloc_free_buf + * calls above. + */ + if (list_empty(&alloc->pages[i].lru)) { + pr_err_size_seq(sizes, seq); + pr_err("expect lru but is %s at page index %d\n", + alloc->pages[i].page_ptr ? "alloc" : "free", i); + binder_selftest_failures++; + } + } +} + +static void binder_selftest_free_page(struct binder_alloc *alloc) +{ + int i; + unsigned long count; + + while ((count = list_lru_count(&binder_alloc_lru))) { + list_lru_walk(&binder_alloc_lru, binder_alloc_free_page, + NULL, count); + } + + for (i = 0; i < (alloc->buffer_size / PAGE_SIZE); i++) { + if (alloc->pages[i].page_ptr) { + pr_err("expect free but is %s at page index %d\n", + list_empty(&alloc->pages[i].lru) ? + "alloc" : "lru", i); + binder_selftest_failures++; + } + } +} + +static void binder_selftest_alloc_free(struct binder_alloc *alloc, + size_t *sizes, int *seq, size_t end) +{ + struct binder_buffer *buffers[BUFFER_NUM]; + + binder_selftest_alloc_buf(alloc, buffers, sizes, seq); + binder_selftest_free_buf(alloc, buffers, sizes, seq, end); + + /* Allocate from lru. */ + binder_selftest_alloc_buf(alloc, buffers, sizes, seq); + if (list_lru_count(&binder_alloc_lru)) + pr_err("lru list should be empty but is not\n"); + + binder_selftest_free_buf(alloc, buffers, sizes, seq, end); + binder_selftest_free_page(alloc); +} + +static bool is_dup(int *seq, int index, int val) +{ + int i; + + for (i = 0; i < index; i++) { + if (seq[i] == val) + return true; + } + return false; +} + +/* Generate BUFFER_NUM factorial free orders. */ +static void binder_selftest_free_seq(struct binder_alloc *alloc, + size_t *sizes, int *seq, + int index, size_t end) +{ + int i; + + if (index == BUFFER_NUM) { + binder_selftest_alloc_free(alloc, sizes, seq, end); + return; + } + for (i = 0; i < BUFFER_NUM; i++) { + if (is_dup(seq, index, i)) + continue; + seq[index] = i; + binder_selftest_free_seq(alloc, sizes, seq, index + 1, end); + } +} + +static void binder_selftest_alloc_size(struct binder_alloc *alloc, + size_t *end_offset) +{ + int i; + int seq[BUFFER_NUM] = {0}; + size_t front_sizes[BUFFER_NUM]; + size_t back_sizes[BUFFER_NUM]; + size_t last_offset, offset = 0; + + for (i = 0; i < BUFFER_NUM; i++) { + last_offset = offset; + offset = end_offset[i]; + front_sizes[i] = offset - last_offset; + back_sizes[BUFFER_NUM - i - 1] = front_sizes[i]; + } + /* + * Buffers share the first or last few pages. + * Only BUFFER_NUM - 1 buffer sizes are adjustable since + * we need one giant buffer before getting to the last page. + */ + back_sizes[0] += alloc->buffer_size - end_offset[BUFFER_NUM - 1]; + binder_selftest_free_seq(alloc, front_sizes, seq, 0, + end_offset[BUFFER_NUM - 1]); + binder_selftest_free_seq(alloc, back_sizes, seq, 0, alloc->buffer_size); +} + +static void binder_selftest_alloc_offset(struct binder_alloc *alloc, + size_t *end_offset, int index) +{ + int align; + size_t end, prev; + + if (index == BUFFER_NUM) { + binder_selftest_alloc_size(alloc, end_offset); + return; + } + prev = index == 0 ? 0 : end_offset[index - 1]; + end = prev; + + BUILD_BUG_ON(BUFFER_MIN_SIZE * BUFFER_NUM >= PAGE_SIZE); + + for (align = SAME_PAGE_UNALIGNED; align < LOOP_END; align++) { + if (align % 2) + end = ALIGN(end, PAGE_SIZE); + else + end += BUFFER_MIN_SIZE; + end_offset[index] = end; + binder_selftest_alloc_offset(alloc, end_offset, index + 1); + } +} + +/** + * binder_selftest_alloc() - Test alloc and free of buffer pages. + * @alloc: Pointer to alloc struct. + * + * Allocate BUFFER_NUM buffers to cover all page alignment cases, + * then free them in all orders possible. Check that pages are + * correctly allocated, put onto lru when buffers are freed, and + * are freed when binder_alloc_free_page is called. + */ +void binder_selftest_alloc(struct binder_alloc *alloc) +{ + size_t end_offset[BUFFER_NUM]; + + if (!binder_selftest_run) + return; + mutex_lock(&binder_selftest_lock); + if (!binder_selftest_run || !alloc->vma) + goto done; + pr_info("STARTED\n"); + binder_selftest_alloc_offset(alloc, end_offset, 0); + binder_selftest_run = false; + if (binder_selftest_failures > 0) + pr_info("%d tests FAILED\n", binder_selftest_failures); + else + pr_info("PASSED\n"); + +done: + mutex_unlock(&binder_selftest_lock); +} diff --git a/drivers/android/binder_trace.h b/drivers/android/binder_trace.h index 7f20f3dc8369..76e3b9c8a8a2 100644 --- a/drivers/android/binder_trace.h +++ b/drivers/android/binder_trace.h @@ -23,7 +23,8 @@ struct binder_buffer; struct binder_node; struct binder_proc; -struct binder_ref; +struct binder_alloc; +struct binder_ref_data; struct binder_thread; struct binder_transaction; @@ -146,8 +147,8 @@ TRACE_EVENT(binder_transaction_received, TRACE_EVENT(binder_transaction_node_to_ref, TP_PROTO(struct binder_transaction *t, struct binder_node *node, - struct binder_ref *ref), - TP_ARGS(t, node, ref), + struct binder_ref_data *rdata), + TP_ARGS(t, node, rdata), TP_STRUCT__entry( __field(int, debug_id) @@ -160,8 +161,8 @@ TRACE_EVENT(binder_transaction_node_to_ref, __entry->debug_id = t->debug_id; __entry->node_debug_id = node->debug_id; __entry->node_ptr = node->ptr; - __entry->ref_debug_id = ref->debug_id; - __entry->ref_desc = ref->desc; + __entry->ref_debug_id = rdata->debug_id; + __entry->ref_desc = rdata->desc; ), TP_printk("transaction=%d node=%d src_ptr=0x%016llx ==> dest_ref=%d dest_desc=%d", __entry->debug_id, __entry->node_debug_id, @@ -170,8 +171,9 @@ TRACE_EVENT(binder_transaction_node_to_ref, ); TRACE_EVENT(binder_transaction_ref_to_node, - TP_PROTO(struct binder_transaction *t, struct binder_ref *ref), - TP_ARGS(t, ref), + TP_PROTO(struct binder_transaction *t, struct binder_node *node, + struct binder_ref_data *rdata), + TP_ARGS(t, node, rdata), TP_STRUCT__entry( __field(int, debug_id) @@ -182,10 +184,10 @@ TRACE_EVENT(binder_transaction_ref_to_node, ), TP_fast_assign( __entry->debug_id = t->debug_id; - __entry->ref_debug_id = ref->debug_id; - __entry->ref_desc = ref->desc; - __entry->node_debug_id = ref->node->debug_id; - __entry->node_ptr = ref->node->ptr; + __entry->ref_debug_id = rdata->debug_id; + __entry->ref_desc = rdata->desc; + __entry->node_debug_id = node->debug_id; + __entry->node_ptr = node->ptr; ), TP_printk("transaction=%d node=%d src_ref=%d src_desc=%d ==> dest_ptr=0x%016llx", __entry->debug_id, __entry->node_debug_id, @@ -194,9 +196,10 @@ TRACE_EVENT(binder_transaction_ref_to_node, ); TRACE_EVENT(binder_transaction_ref_to_ref, - TP_PROTO(struct binder_transaction *t, struct binder_ref *src_ref, - struct binder_ref *dest_ref), - TP_ARGS(t, src_ref, dest_ref), + TP_PROTO(struct binder_transaction *t, struct binder_node *node, + struct binder_ref_data *src_ref, + struct binder_ref_data *dest_ref), + TP_ARGS(t, node, src_ref, dest_ref), TP_STRUCT__entry( __field(int, debug_id) @@ -208,7 +211,7 @@ TRACE_EVENT(binder_transaction_ref_to_ref, ), TP_fast_assign( __entry->debug_id = t->debug_id; - __entry->node_debug_id = src_ref->node->debug_id; + __entry->node_debug_id = node->debug_id; __entry->src_ref_debug_id = src_ref->debug_id; __entry->src_ref_desc = src_ref->desc; __entry->dest_ref_debug_id = dest_ref->debug_id; @@ -268,9 +271,9 @@ DEFINE_EVENT(binder_buffer_class, binder_transaction_failed_buffer_release, TP_ARGS(buffer)); TRACE_EVENT(binder_update_page_range, - TP_PROTO(struct binder_proc *proc, bool allocate, + TP_PROTO(struct binder_alloc *alloc, bool allocate, void *start, void *end), - TP_ARGS(proc, allocate, start, end), + TP_ARGS(alloc, allocate, start, end), TP_STRUCT__entry( __field(int, proc) __field(bool, allocate) @@ -278,9 +281,9 @@ TRACE_EVENT(binder_update_page_range, __field(size_t, size) ), TP_fast_assign( - __entry->proc = proc->pid; + __entry->proc = alloc->pid; __entry->allocate = allocate; - __entry->offset = start - proc->buffer; + __entry->offset = start - alloc->buffer; __entry->size = end - start; ), TP_printk("proc=%d allocate=%d offset=%zu size=%zu", @@ -288,6 +291,61 @@ TRACE_EVENT(binder_update_page_range, __entry->offset, __entry->size) ); +DECLARE_EVENT_CLASS(binder_lru_page_class, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index), + TP_STRUCT__entry( + __field(int, proc) + __field(size_t, page_index) + ), + TP_fast_assign( + __entry->proc = alloc->pid; + __entry->page_index = page_index; + ), + TP_printk("proc=%d page_index=%zu", + __entry->proc, __entry->page_index) +); + +DEFINE_EVENT(binder_lru_page_class, binder_alloc_lru_start, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index)); + +DEFINE_EVENT(binder_lru_page_class, binder_alloc_lru_end, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index)); + +DEFINE_EVENT(binder_lru_page_class, binder_free_lru_start, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index)); + +DEFINE_EVENT(binder_lru_page_class, binder_free_lru_end, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index)); + +DEFINE_EVENT(binder_lru_page_class, binder_alloc_page_start, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index)); + +DEFINE_EVENT(binder_lru_page_class, binder_alloc_page_end, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index)); + +DEFINE_EVENT(binder_lru_page_class, binder_unmap_user_start, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index)); + +DEFINE_EVENT(binder_lru_page_class, binder_unmap_user_end, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index)); + +DEFINE_EVENT(binder_lru_page_class, binder_unmap_kernel_start, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index)); + +DEFINE_EVENT(binder_lru_page_class, binder_unmap_kernel_end, + TP_PROTO(const struct binder_alloc *alloc, size_t page_index), + TP_ARGS(alloc, page_index)); + TRACE_EVENT(binder_command, TP_PROTO(uint32_t cmd), TP_ARGS(cmd), diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 948fc86980a1..363fc5330c21 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -215,7 +215,7 @@ config SATA_FSL config SATA_GEMINI tristate "Gemini SATA bridge support" - depends on PATA_FTIDE010 + depends on ARCH_GEMINI || COMPILE_TEST default ARCH_GEMINI help This enabled support for the FTIDE010 to SATA bridge @@ -613,7 +613,7 @@ config PATA_FTIDE010 tristate "Faraday Technology FTIDE010 PATA support" depends on OF depends on ARM - default ARCH_GEMINI + depends on SATA_GEMINI help This option enables support for the Faraday FTIDE010 PATA controller found in the Cortina Gemini SoCs. diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 8453f9a4682f..fa7dd4394c02 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -2083,7 +2083,7 @@ unsigned int ata_read_log_page(struct ata_device *dev, u8 log, retry: ata_tf_init(dev, &tf); if (dev->dma_mode && ata_id_has_read_log_dma_ext(dev->id) && - !(dev->horkage & ATA_HORKAGE_NO_NCQ_LOG)) { + !(dev->horkage & ATA_HORKAGE_NO_DMA_LOG)) { tf.command = ATA_CMD_READ_LOG_DMA_EXT; tf.protocol = ATA_PROT_DMA; dma = true; @@ -2102,8 +2102,8 @@ retry: buf, sectors * ATA_SECT_SIZE, 0); if (err_mask && dma) { - dev->horkage |= ATA_HORKAGE_NO_NCQ_LOG; - ata_dev_warn(dev, "READ LOG DMA EXT failed, trying unqueued\n"); + dev->horkage |= ATA_HORKAGE_NO_DMA_LOG; + ata_dev_warn(dev, "READ LOG DMA EXT failed, trying PIO\n"); goto retry; } diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index b70bcf6d2914..3dbd05532c09 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1434,7 +1434,7 @@ void ata_eh_about_to_do(struct ata_link *link, struct ata_device *dev, /** * ata_eh_done - EH action complete -* @ap: target ATA port + * @link: ATA link for which EH actions are complete * @dev: target ATA dev for per-dev action (can be NULL) * @action: action just completed * @@ -1576,7 +1576,7 @@ unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) /** * ata_eh_request_sense - perform REQUEST_SENSE_DATA_EXT - * @dev: device to perform REQUEST_SENSE_SENSE_DATA_EXT to + * @qc: qc to perform REQUEST_SENSE_SENSE_DATA_EXT to * @cmd: scsi command for which the sense code should be set * * Perform REQUEST_SENSE_DATA_EXT after the device reported CHECK @@ -4175,7 +4175,6 @@ static void ata_eh_handle_port_resume(struct ata_port *ap) struct ata_link *link; struct ata_device *dev; unsigned long flags; - int rc = 0; /* are we resuming? */ spin_lock_irqsave(ap->lock, flags); @@ -4202,7 +4201,7 @@ static void ata_eh_handle_port_resume(struct ata_port *ap) ata_acpi_set_state(ap, ap->pm_mesg); if (ap->ops->port_resume) - rc = ap->ops->port_resume(ap); + ap->ops->port_resume(ap); /* tell ACPI that we're resuming */ ata_acpi_on_resume(ap); diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index d462c5a3a7ef..44ba292f2cd7 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -3030,10 +3030,12 @@ static unsigned int atapi_xlat(struct ata_queued_cmd *qc) static struct ata_device *ata_find_dev(struct ata_port *ap, int devno) { if (!sata_pmp_attached(ap)) { - if (likely(devno < ata_link_max_devices(&ap->link))) + if (likely(devno >= 0 && + devno < ata_link_max_devices(&ap->link))) return &ap->link.device[devno]; } else { - if (likely(devno < ap->nr_pmp_links)) + if (likely(devno >= 0 && + devno < ap->nr_pmp_links)) return &ap->pmp_link[devno].device[0]; } diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c index ee9844758736..537d11869069 100644 --- a/drivers/ata/sata_rcar.c +++ b/drivers/ata/sata_rcar.c @@ -858,6 +858,14 @@ static const struct of_device_id sata_rcar_match[] = { .compatible = "renesas,sata-r8a7795", .data = (void *)RCAR_GEN2_SATA }, + { + .compatible = "renesas,rcar-gen2-sata", + .data = (void *)RCAR_GEN2_SATA + }, + { + .compatible = "renesas,rcar-gen3-sata", + .data = (void *)RCAR_GEN2_SATA + }, { }, }; MODULE_DEVICE_TABLE(of, sata_rcar_match); diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c index 292dec18ffb8..07bdd51b3b9a 100644 --- a/drivers/atm/zatm.c +++ b/drivers/atm/zatm.c @@ -1613,7 +1613,7 @@ static int zatm_init_one(struct pci_dev *pci_dev, ret = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)); if (ret < 0) - goto out_disable; + goto out_release; zatm_dev->pci_dev = pci_dev; dev->dev_data = zatm_dev; diff --git a/drivers/auxdisplay/panel.c b/drivers/auxdisplay/panel.c index 7a8b8fb2f572..df126dcdaf18 100644 --- a/drivers/auxdisplay/panel.c +++ b/drivers/auxdisplay/panel.c @@ -877,21 +877,21 @@ static void lcd_clear_fast_tilcd(struct charlcd *charlcd) spin_unlock_irq(&pprt_lock); } -static struct charlcd_ops charlcd_serial_ops = { +static const struct charlcd_ops charlcd_serial_ops = { .write_cmd = lcd_write_cmd_s, .write_data = lcd_write_data_s, .clear_fast = lcd_clear_fast_s, .backlight = lcd_backlight, }; -static struct charlcd_ops charlcd_parallel_ops = { +static const struct charlcd_ops charlcd_parallel_ops = { .write_cmd = lcd_write_cmd_p8, .write_data = lcd_write_data_p8, .clear_fast = lcd_clear_fast_p8, .backlight = lcd_backlight, }; -static struct charlcd_ops charlcd_tilcd_ops = { +static const struct charlcd_ops charlcd_tilcd_ops = { .write_cmd = lcd_write_cmd_tilcd, .write_data = lcd_write_data_tilcd, .clear_fast = lcd_clear_fast_tilcd, diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index 2ae24c28e70c..1c152aed6b82 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -25,7 +25,7 @@ static inline struct dma_coherent_mem *dev_get_coherent_memory(struct device *de { if (dev && dev->dma_mem) return dev->dma_mem; - return dma_coherent_default_memory; + return NULL; } static inline dma_addr_t dma_get_device_base(struct device *dev, @@ -165,34 +165,15 @@ void *dma_mark_declared_memory_occupied(struct device *dev, } EXPORT_SYMBOL(dma_mark_declared_memory_occupied); -/** - * dma_alloc_from_coherent() - try to allocate memory from the per-device coherent area - * - * @dev: device from which we allocate memory - * @size: size of requested memory area - * @dma_handle: This will be filled with the correct dma handle - * @ret: This pointer will be filled with the virtual address - * to allocated area. - * - * This function should be only called from per-arch dma_alloc_coherent() - * to support allocation from per-device coherent memory pools. - * - * Returns 0 if dma_alloc_coherent should continue with allocating from - * generic memory areas, or !0 if dma_alloc_coherent should return @ret. - */ -int dma_alloc_from_coherent(struct device *dev, ssize_t size, - dma_addr_t *dma_handle, void **ret) +static void *__dma_alloc_from_coherent(struct dma_coherent_mem *mem, + ssize_t size, dma_addr_t *dma_handle) { - struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); int order = get_order(size); unsigned long flags; int pageno; int dma_memory_map; + void *ret; - if (!mem) - return 0; - - *ret = NULL; spin_lock_irqsave(&mem->spinlock, flags); if (unlikely(size > (mem->size << PAGE_SHIFT))) @@ -203,21 +184,50 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size, goto err; /* - * Memory was found in the per-device area. + * Memory was found in the coherent area. */ - *dma_handle = dma_get_device_base(dev, mem) + (pageno << PAGE_SHIFT); - *ret = mem->virt_base + (pageno << PAGE_SHIFT); + *dma_handle = mem->device_base + (pageno << PAGE_SHIFT); + ret = mem->virt_base + (pageno << PAGE_SHIFT); dma_memory_map = (mem->flags & DMA_MEMORY_MAP); spin_unlock_irqrestore(&mem->spinlock, flags); if (dma_memory_map) - memset(*ret, 0, size); + memset(ret, 0, size); else - memset_io(*ret, 0, size); + memset_io(ret, 0, size); - return 1; + return ret; err: spin_unlock_irqrestore(&mem->spinlock, flags); + return NULL; +} + +/** + * dma_alloc_from_dev_coherent() - allocate memory from device coherent pool + * @dev: device from which we allocate memory + * @size: size of requested memory area + * @dma_handle: This will be filled with the correct dma handle + * @ret: This pointer will be filled with the virtual address + * to allocated area. + * + * This function should be only called from per-arch dma_alloc_coherent() + * to support allocation from per-device coherent memory pools. + * + * Returns 0 if dma_alloc_coherent should continue with allocating from + * generic memory areas, or !0 if dma_alloc_coherent should return @ret. + */ +int dma_alloc_from_dev_coherent(struct device *dev, ssize_t size, + dma_addr_t *dma_handle, void **ret) +{ + struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); + + if (!mem) + return 0; + + *ret = __dma_alloc_from_coherent(mem, size, dma_handle); + if (*ret) + return 1; + /* * In the case where the allocation can not be satisfied from the * per-device area, try to fall back to generic memory if the @@ -225,25 +235,20 @@ err: */ return mem->flags & DMA_MEMORY_EXCLUSIVE; } -EXPORT_SYMBOL(dma_alloc_from_coherent); +EXPORT_SYMBOL(dma_alloc_from_dev_coherent); -/** - * dma_release_from_coherent() - try to free the memory allocated from per-device coherent memory pool - * @dev: device from which the memory was allocated - * @order: the order of pages allocated - * @vaddr: virtual address of allocated pages - * - * This checks whether the memory was allocated from the per-device - * coherent memory pool and if so, releases that memory. - * - * Returns 1 if we correctly released the memory, or 0 if - * dma_release_coherent() should proceed with releasing memory from - * generic pools. - */ -int dma_release_from_coherent(struct device *dev, int order, void *vaddr) +void *dma_alloc_from_global_coherent(ssize_t size, dma_addr_t *dma_handle) { - struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); + if (!dma_coherent_default_memory) + return NULL; + + return __dma_alloc_from_coherent(dma_coherent_default_memory, size, + dma_handle); +} +static int __dma_release_from_coherent(struct dma_coherent_mem *mem, + int order, void *vaddr) +{ if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { int page = (vaddr - mem->virt_base) >> PAGE_SHIFT; @@ -256,28 +261,39 @@ int dma_release_from_coherent(struct device *dev, int order, void *vaddr) } return 0; } -EXPORT_SYMBOL(dma_release_from_coherent); /** - * dma_mmap_from_coherent() - try to mmap the memory allocated from - * per-device coherent memory pool to userspace + * dma_release_from_dev_coherent() - free memory to device coherent memory pool * @dev: device from which the memory was allocated - * @vma: vm_area for the userspace memory - * @vaddr: cpu address returned by dma_alloc_from_coherent - * @size: size of the memory buffer allocated by dma_alloc_from_coherent - * @ret: result from remap_pfn_range() + * @order: the order of pages allocated + * @vaddr: virtual address of allocated pages * * This checks whether the memory was allocated from the per-device - * coherent memory pool and if so, maps that memory to the provided vma. + * coherent memory pool and if so, releases that memory. * - * Returns 1 if we correctly mapped the memory, or 0 if the caller should - * proceed with mapping memory from generic pools. + * Returns 1 if we correctly released the memory, or 0 if the caller should + * proceed with releasing memory from generic pools. */ -int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma, - void *vaddr, size_t size, int *ret) +int dma_release_from_dev_coherent(struct device *dev, int order, void *vaddr) { struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); + return __dma_release_from_coherent(mem, order, vaddr); +} +EXPORT_SYMBOL(dma_release_from_dev_coherent); + +int dma_release_from_global_coherent(int order, void *vaddr) +{ + if (!dma_coherent_default_memory) + return 0; + + return __dma_release_from_coherent(dma_coherent_default_memory, order, + vaddr); +} + +static int __dma_mmap_from_coherent(struct dma_coherent_mem *mem, + struct vm_area_struct *vma, void *vaddr, size_t size, int *ret) +{ if (mem && vaddr >= mem->virt_base && vaddr + size <= (mem->virt_base + (mem->size << PAGE_SHIFT))) { unsigned long off = vma->vm_pgoff; @@ -296,7 +312,39 @@ int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma, } return 0; } -EXPORT_SYMBOL(dma_mmap_from_coherent); + +/** + * dma_mmap_from_dev_coherent() - mmap memory from the device coherent pool + * @dev: device from which the memory was allocated + * @vma: vm_area for the userspace memory + * @vaddr: cpu address returned by dma_alloc_from_dev_coherent + * @size: size of the memory buffer allocated + * @ret: result from remap_pfn_range() + * + * This checks whether the memory was allocated from the per-device + * coherent memory pool and if so, maps that memory to the provided vma. + * + * Returns 1 if we correctly mapped the memory, or 0 if the caller should + * proceed with mapping memory from generic pools. + */ +int dma_mmap_from_dev_coherent(struct device *dev, struct vm_area_struct *vma, + void *vaddr, size_t size, int *ret) +{ + struct dma_coherent_mem *mem = dev_get_coherent_memory(dev); + + return __dma_mmap_from_coherent(mem, vma, vaddr, size, ret); +} +EXPORT_SYMBOL(dma_mmap_from_dev_coherent); + +int dma_mmap_from_global_coherent(struct vm_area_struct *vma, void *vaddr, + size_t size, int *ret) +{ + if (!dma_coherent_default_memory) + return 0; + + return __dma_mmap_from_coherent(dma_coherent_default_memory, vma, + vaddr, size, ret); +} /* * Support for reserved memory regions defined in device tree diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index 5096755d185e..b555ff9dd8fc 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c @@ -235,7 +235,7 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret)) + if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret)) return ret; if (off < count && user_count <= (count - off)) { diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b9f907eedbf7..bfbe1e154128 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -30,7 +30,6 @@ #include <linux/syscore_ops.h> #include <linux/reboot.h> #include <linux/security.h> -#include <linux/swait.h> #include <generated/utsrelease.h> @@ -112,13 +111,13 @@ static inline long firmware_loading_timeout(void) * state of the firmware loading. */ struct fw_state { - struct swait_queue_head wq; + struct completion completion; enum fw_status status; }; static void fw_state_init(struct fw_state *fw_st) { - init_swait_queue_head(&fw_st->wq); + init_completion(&fw_st->completion); fw_st->status = FW_STATUS_UNKNOWN; } @@ -131,9 +130,7 @@ static int __fw_state_wait_common(struct fw_state *fw_st, long timeout) { long ret; - ret = swait_event_interruptible_timeout(fw_st->wq, - __fw_state_is_done(READ_ONCE(fw_st->status)), - timeout); + ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout); if (ret != 0 && fw_st->status == FW_STATUS_ABORTED) return -ENOENT; if (!ret) @@ -148,35 +145,34 @@ static void __fw_state_set(struct fw_state *fw_st, WRITE_ONCE(fw_st->status, status); if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED) - swake_up(&fw_st->wq); + complete_all(&fw_st->completion); } #define fw_state_start(fw_st) \ __fw_state_set(fw_st, FW_STATUS_LOADING) #define fw_state_done(fw_st) \ __fw_state_set(fw_st, FW_STATUS_DONE) +#define fw_state_aborted(fw_st) \ + __fw_state_set(fw_st, FW_STATUS_ABORTED) #define fw_state_wait(fw_st) \ __fw_state_wait_common(fw_st, MAX_SCHEDULE_TIMEOUT) -#ifndef CONFIG_FW_LOADER_USER_HELPER - -#define fw_state_is_aborted(fw_st) false - -#else /* CONFIG_FW_LOADER_USER_HELPER */ - static int __fw_state_check(struct fw_state *fw_st, enum fw_status status) { return fw_st->status == status; } +#define fw_state_is_aborted(fw_st) \ + __fw_state_check(fw_st, FW_STATUS_ABORTED) + +#ifdef CONFIG_FW_LOADER_USER_HELPER + #define fw_state_aborted(fw_st) \ __fw_state_set(fw_st, FW_STATUS_ABORTED) #define fw_state_is_done(fw_st) \ __fw_state_check(fw_st, FW_STATUS_DONE) #define fw_state_is_loading(fw_st) \ __fw_state_check(fw_st, FW_STATUS_LOADING) -#define fw_state_is_aborted(fw_st) \ - __fw_state_check(fw_st, FW_STATUS_ABORTED) #define fw_state_wait_timeout(fw_st, timeout) \ __fw_state_wait_common(fw_st, timeout) @@ -1200,6 +1196,28 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, return 1; /* need to load */ } +/* + * Batched requests need only one wake, we need to do this step last due to the + * fallback mechanism. The buf is protected with kref_get(), and it won't be + * released until the last user calls release_firmware(). + * + * Failed batched requests are possible as well, in such cases we just share + * the struct firmware_buf and won't release it until all requests are woken + * and have gone through this same path. + */ +static void fw_abort_batch_reqs(struct firmware *fw) +{ + struct firmware_buf *buf; + + /* Loaded directly? */ + if (!fw || !fw->priv) + return; + + buf = fw->priv; + if (!fw_state_is_aborted(&buf->fw_st)) + fw_state_aborted(&buf->fw_st); +} + /* called from request_firmware() and request_firmware_work_func() */ static int _request_firmware(const struct firmware **firmware_p, const char *name, @@ -1243,6 +1261,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name, out: if (ret < 0) { + fw_abort_batch_reqs(fw); release_firmware(fw); fw = NULL; } diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 3b8210ebb50e..60303aa28587 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1222,8 +1222,6 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, spin_unlock_irq(&dev->power.lock); - dev_pm_domain_set(dev, &genpd->domain); - return gpd_data; err_free: @@ -1237,8 +1235,6 @@ static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, static void genpd_free_dev_data(struct device *dev, struct generic_pm_domain_data *gpd_data) { - dev_pm_domain_set(dev, NULL); - spin_lock_irq(&dev->power.lock); dev->power.subsys_data->domain_data = NULL; @@ -1275,6 +1271,8 @@ static int genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, if (ret) goto out; + dev_pm_domain_set(dev, &genpd->domain); + genpd->device_count++; genpd->max_off_time_changed = true; @@ -1336,6 +1334,8 @@ static int genpd_remove_device(struct generic_pm_domain *genpd, if (genpd->detach_dev) genpd->detach_dev(genpd, dev); + dev_pm_domain_set(dev, NULL); + list_del_init(&pdd->list_node); genpd_unlock(genpd); diff --git a/drivers/base/regmap/regmap-w1.c b/drivers/base/regmap/regmap-w1.c index 5f04e7bf063e..e6c64b0be5b2 100644 --- a/drivers/base/regmap/regmap-w1.c +++ b/drivers/base/regmap/regmap-w1.c @@ -1,7 +1,7 @@ /* * Register map access API - W1 (1-Wire) support * - * Copyright (C) 2017 OAO Radioavionica + * Copyright (c) 2017 Radioavionica Corporation * Author: Alex A. Mihaylov <minimumlaw@rambler.ru> * * This program is free software; you can redistribute it and/or modify @@ -11,7 +11,7 @@ #include <linux/regmap.h> #include <linux/module.h> -#include "../../w1/w1.h" +#include <linux/w1.h> #include "internal.h" diff --git a/drivers/block/loop.c b/drivers/block/loop.c index ef8334949b42..f321b96405f5 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -221,8 +221,7 @@ static void __loop_update_dio(struct loop_device *lo, bool dio) } static int -figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit, - loff_t logical_blocksize) +figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit) { loff_t size = get_size(offset, sizelimit, lo->lo_backing_file); sector_t x = (sector_t)size; @@ -234,12 +233,6 @@ figure_loop_size(struct loop_device *lo, loff_t offset, loff_t sizelimit, lo->lo_offset = offset; if (lo->lo_sizelimit != sizelimit) lo->lo_sizelimit = sizelimit; - if (lo->lo_flags & LO_FLAGS_BLOCKSIZE) { - lo->lo_logical_blocksize = logical_blocksize; - blk_queue_physical_block_size(lo->lo_queue, lo->lo_blocksize); - blk_queue_logical_block_size(lo->lo_queue, - lo->lo_logical_blocksize); - } set_capacity(lo->lo_disk, x); bd_set_size(bdev, (loff_t)get_capacity(bdev->bd_disk) << 9); /* let user-space know about the new size */ @@ -820,7 +813,6 @@ static void loop_config_discard(struct loop_device *lo) struct file *file = lo->lo_backing_file; struct inode *inode = file->f_mapping->host; struct request_queue *q = lo->lo_queue; - int lo_bits = 9; /* * We use punch hole to reclaim the free space used by the @@ -840,11 +832,9 @@ static void loop_config_discard(struct loop_device *lo) q->limits.discard_granularity = inode->i_sb->s_blocksize; q->limits.discard_alignment = 0; - if (lo->lo_flags & LO_FLAGS_BLOCKSIZE) - lo_bits = blksize_bits(lo->lo_logical_blocksize); - blk_queue_max_discard_sectors(q, UINT_MAX >> lo_bits); - blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> lo_bits); + blk_queue_max_discard_sectors(q, UINT_MAX >> 9); + blk_queue_max_write_zeroes_sectors(q, UINT_MAX >> 9); queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q); } @@ -938,7 +928,6 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, lo->use_dio = false; lo->lo_blocksize = lo_blocksize; - lo->lo_logical_blocksize = 512; lo->lo_device = bdev; lo->lo_flags = lo_flags; lo->lo_backing_file = file; @@ -1104,7 +1093,6 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) int err; struct loop_func_table *xfer; kuid_t uid = current_uid(); - int lo_flags = lo->lo_flags; if (lo->lo_encrypt_key_size && !uid_eq(lo->lo_key_owner, uid) && @@ -1137,26 +1125,9 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info) if (err) goto exit; - if (info->lo_flags & LO_FLAGS_BLOCKSIZE) { - if (!(lo->lo_flags & LO_FLAGS_BLOCKSIZE)) - lo->lo_logical_blocksize = 512; - lo->lo_flags |= LO_FLAGS_BLOCKSIZE; - if (LO_INFO_BLOCKSIZE(info) != 512 && - LO_INFO_BLOCKSIZE(info) != 1024 && - LO_INFO_BLOCKSIZE(info) != 2048 && - LO_INFO_BLOCKSIZE(info) != 4096) - return -EINVAL; - if (LO_INFO_BLOCKSIZE(info) > lo->lo_blocksize) - return -EINVAL; - } - if (lo->lo_offset != info->lo_offset || - lo->lo_sizelimit != info->lo_sizelimit || - lo->lo_flags != lo_flags || - ((lo->lo_flags & LO_FLAGS_BLOCKSIZE) && - lo->lo_logical_blocksize != LO_INFO_BLOCKSIZE(info))) { - if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit, - LO_INFO_BLOCKSIZE(info))) { + lo->lo_sizelimit != info->lo_sizelimit) { + if (figure_loop_size(lo, info->lo_offset, info->lo_sizelimit)) { err = -EFBIG; goto exit; } @@ -1348,8 +1319,7 @@ static int loop_set_capacity(struct loop_device *lo) if (unlikely(lo->lo_state != Lo_bound)) return -ENXIO; - return figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit, - lo->lo_logical_blocksize); + return figure_loop_size(lo, lo->lo_offset, lo->lo_sizelimit); } static int loop_set_dio(struct loop_device *lo, unsigned long arg) diff --git a/drivers/block/loop.h b/drivers/block/loop.h index 2c096b9a17b8..fecd3f97ef8c 100644 --- a/drivers/block/loop.h +++ b/drivers/block/loop.h @@ -49,7 +49,6 @@ struct loop_device { struct file * lo_backing_file; struct block_device *lo_device; unsigned lo_blocksize; - unsigned lo_logical_blocksize; void *key_data; gfp_t old_gfp_mask; diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index dea7d85134ee..5bdf923294a5 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -626,7 +626,6 @@ static void recv_work(struct work_struct *work) struct nbd_device *nbd = args->nbd; struct nbd_config *config = nbd->config; struct nbd_cmd *cmd; - int ret = 0; while (1) { cmd = nbd_read_stat(nbd, args->index); @@ -636,7 +635,6 @@ static void recv_work(struct work_struct *work) mutex_lock(&nsock->tx_lock); nbd_mark_nsock_dead(nbd, nsock, 1); mutex_unlock(&nsock->tx_lock); - ret = PTR_ERR(cmd); break; } @@ -910,7 +908,8 @@ static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg) continue; } sk_set_memalloc(sock->sk); - sock->sk->sk_sndtimeo = nbd->tag_set.timeout; + if (nbd->tag_set.timeout) + sock->sk->sk_sndtimeo = nbd->tag_set.timeout; atomic_inc(&config->recv_threads); refcount_inc(&nbd->config_refs); old = nsock->sock; @@ -924,6 +923,8 @@ static int nbd_reconnect_socket(struct nbd_device *nbd, unsigned long arg) mutex_unlock(&nsock->tx_lock); sockfd_put(old); + clear_bit(NBD_DISCONNECTED, &config->runtime_flags); + /* We take the tx_mutex in an error path in the recv_work, so we * need to queue_work outside of the tx_mutex. */ @@ -980,11 +981,15 @@ static void send_disconnects(struct nbd_device *nbd) int i, ret; for (i = 0; i < config->num_connections; i++) { + struct nbd_sock *nsock = config->socks[i]; + iov_iter_kvec(&from, WRITE | ITER_KVEC, &iov, 1, sizeof(request)); + mutex_lock(&nsock->tx_lock); ret = sock_xmit(nbd, i, 1, &from, 0, NULL); if (ret <= 0) dev_err(disk_to_dev(nbd->disk), "Send disconnect failed %d\n", ret); + mutex_unlock(&nsock->tx_lock); } } @@ -993,9 +998,8 @@ static int nbd_disconnect(struct nbd_device *nbd) struct nbd_config *config = nbd->config; dev_info(disk_to_dev(nbd->disk), "NBD_DISCONNECT\n"); - if (!test_and_set_bit(NBD_DISCONNECT_REQUESTED, - &config->runtime_flags)) - send_disconnects(nbd); + set_bit(NBD_DISCONNECT_REQUESTED, &config->runtime_flags); + send_disconnects(nbd); return 0; } @@ -1076,7 +1080,9 @@ static int nbd_start_device(struct nbd_device *nbd) return -ENOMEM; } sk_set_memalloc(config->socks[i]->sock->sk); - config->socks[i]->sock->sk->sk_sndtimeo = nbd->tag_set.timeout; + if (nbd->tag_set.timeout) + config->socks[i]->sock->sk->sk_sndtimeo = + nbd->tag_set.timeout; atomic_inc(&config->recv_threads); refcount_inc(&nbd->config_refs); INIT_WORK(&args->work, recv_work); diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index 6b16ead1da58..ad9749463d4f 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -875,6 +875,56 @@ static void print_version(void) printk(KERN_INFO "%s", version); } +struct vdc_check_port_data { + int dev_no; + char *type; +}; + +static int vdc_device_probed(struct device *dev, void *arg) +{ + struct vio_dev *vdev = to_vio_dev(dev); + struct vdc_check_port_data *port_data; + + port_data = (struct vdc_check_port_data *)arg; + + if ((vdev->dev_no == port_data->dev_no) && + (!(strcmp((char *)&vdev->type, port_data->type))) && + dev_get_drvdata(dev)) { + /* This device has already been configured + * by vdc_port_probe() + */ + return 1; + } else { + return 0; + } +} + +/* Determine whether the VIO device is part of an mpgroup + * by locating all the virtual-device-port nodes associated + * with the parent virtual-device node for the VIO device + * and checking whether any of these nodes are vdc-ports + * which have already been configured. + * + * Returns true if this device is part of an mpgroup and has + * already been probed. + */ +static bool vdc_port_mpgroup_check(struct vio_dev *vdev) +{ + struct vdc_check_port_data port_data; + struct device *dev; + + port_data.dev_no = vdev->dev_no; + port_data.type = (char *)&vdev->type; + + dev = device_find_child(vdev->dev.parent, &port_data, + vdc_device_probed); + + if (dev) + return true; + + return false; +} + static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) { struct mdesc_handle *hp; @@ -893,6 +943,14 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) goto err_out_release_mdesc; } + /* Check if this device is part of an mpgroup */ + if (vdc_port_mpgroup_check(vdev)) { + printk(KERN_WARNING + "VIO: Ignoring extra vdisk port %s", + dev_name(&vdev->dev)); + goto err_out_release_mdesc; + } + port = kzalloc(sizeof(*port), GFP_KERNEL); err = -ENOMEM; if (!port) { @@ -943,6 +1001,9 @@ static int vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) if (err) goto err_out_free_tx_ring; + /* Note that the device driver_data is used to determine + * whether the port has been probed. + */ dev_set_drvdata(&vdev->dev, port); mdesc_release(hp); diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 4e02aa5fdac0..d3d5523862c2 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -381,6 +381,7 @@ static void virtblk_config_changed_work(struct work_struct *work) struct request_queue *q = vblk->disk->queue; char cap_str_2[10], cap_str_10[10]; char *envp[] = { "RESIZE=1", NULL }; + unsigned long long nblocks; u64 capacity; /* Host must always specify the capacity. */ @@ -393,16 +394,19 @@ static void virtblk_config_changed_work(struct work_struct *work) capacity = (sector_t)-1; } - string_get_size(capacity, queue_logical_block_size(q), + nblocks = DIV_ROUND_UP_ULL(capacity, queue_logical_block_size(q) >> 9); + + string_get_size(nblocks, queue_logical_block_size(q), STRING_UNITS_2, cap_str_2, sizeof(cap_str_2)); - string_get_size(capacity, queue_logical_block_size(q), + string_get_size(nblocks, queue_logical_block_size(q), STRING_UNITS_10, cap_str_10, sizeof(cap_str_10)); dev_notice(&vdev->dev, - "new size: %llu %d-byte logical blocks (%s/%s)\n", - (unsigned long long)capacity, - queue_logical_block_size(q), - cap_str_10, cap_str_2); + "new size: %llu %d-byte logical blocks (%s/%s)\n", + nblocks, + queue_logical_block_size(q), + cap_str_10, + cap_str_2); set_capacity(vblk->disk, capacity); revalidate_disk(vblk->disk); @@ -541,12 +545,9 @@ virtblk_cache_type_store(struct device *dev, struct device_attribute *attr, int i; BUG_ON(!virtio_has_feature(vblk->vdev, VIRTIO_BLK_F_CONFIG_WCE)); - for (i = ARRAY_SIZE(virtblk_cache_types); --i >= 0; ) - if (sysfs_streq(buf, virtblk_cache_types[i])) - break; - + i = sysfs_match_string(virtblk_cache_types, buf); if (i < 0) - return -EINVAL; + return i; virtio_cwrite8(vdev, offsetof(struct virtio_blk_config, wce), i); virtblk_update_cache_mode(vdev); diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index c852ed3c01d5..2468c28d4771 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -111,7 +111,7 @@ struct blk_shadow { }; struct blkif_req { - int error; + blk_status_t error; }; static inline struct blkif_req *blkif_req(struct request *rq) @@ -708,6 +708,7 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri * existing persistent grants, or if we have to get new grants, * as there are not sufficiently many free. */ + bool new_persistent_gnts = false; struct scatterlist *sg; int num_sg, max_grefs, num_grant; @@ -719,19 +720,21 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri */ max_grefs += INDIRECT_GREFS(max_grefs); - /* - * We have to reserve 'max_grefs' grants because persistent - * grants are shared by all rings. - */ - if (max_grefs > 0) - if (gnttab_alloc_grant_references(max_grefs, &setup.gref_head) < 0) { + /* Check if we have enough persistent grants to allocate a requests */ + if (rinfo->persistent_gnts_c < max_grefs) { + new_persistent_gnts = true; + + if (gnttab_alloc_grant_references( + max_grefs - rinfo->persistent_gnts_c, + &setup.gref_head) < 0) { gnttab_request_free_callback( &rinfo->callback, blkif_restart_queue_callback, rinfo, - max_grefs); + max_grefs - rinfo->persistent_gnts_c); return 1; } + } /* Fill out a communications ring structure. */ id = blkif_ring_get_request(rinfo, req, &ring_req); @@ -832,7 +835,7 @@ static int blkif_queue_rw_req(struct request *req, struct blkfront_ring_info *ri if (unlikely(require_extra_req)) rinfo->shadow[extra_id].req = *extra_ring_req; - if (max_grefs > 0) + if (new_persistent_gnts) gnttab_free_grant_references(setup.gref_head); return 0; @@ -906,8 +909,8 @@ out_err: return BLK_STS_IOERR; out_busy: - spin_unlock_irqrestore(&rinfo->ring_lock, flags); blk_mq_stop_hw_queue(hctx); + spin_unlock_irqrestore(&rinfo->ring_lock, flags); return BLK_STS_RESOURCE; } @@ -1616,7 +1619,7 @@ static irqreturn_t blkif_interrupt(int irq, void *dev_id) if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { printk(KERN_WARNING "blkfront: %s: %s op failed\n", info->gd->disk_name, op_name(bret->operation)); - blkif_req(req)->error = -EOPNOTSUPP; + blkif_req(req)->error = BLK_STS_NOTSUPP; } if (unlikely(bret->status == BLKIF_RSP_ERROR && rinfo->shadow[id].req.u.rw.nr_segments == 0)) { @@ -2072,9 +2075,9 @@ static int blkfront_resume(struct xenbus_device *dev) /* * Get the bios in the request so we can re-queue them. */ - if (req_op(shadow[i].request) == REQ_OP_FLUSH || - req_op(shadow[i].request) == REQ_OP_DISCARD || - req_op(shadow[i].request) == REQ_OP_SECURE_ERASE || + if (req_op(shadow[j].request) == REQ_OP_FLUSH || + req_op(shadow[j].request) == REQ_OP_DISCARD || + req_op(shadow[j].request) == REQ_OP_SECURE_ERASE || shadow[j].request->cmd_flags & REQ_FUA) { /* * Flush operations don't contain bios, so diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 856d5dc02451..3b1b6340ba13 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -308,7 +308,7 @@ static ssize_t comp_algorithm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { struct zram *zram = dev_to_zram(dev); - char compressor[CRYPTO_MAX_ALG_NAME]; + char compressor[ARRAY_SIZE(zram->compressor)]; size_t sz; strlcpy(compressor, buf, sizeof(compressor)); @@ -327,7 +327,7 @@ static ssize_t comp_algorithm_store(struct device *dev, return -EBUSY; } - strlcpy(zram->compressor, compressor, sizeof(compressor)); + strcpy(zram->compressor, compressor); up_write(&zram->init_lock); return len; } diff --git a/drivers/bus/uniphier-system-bus.c b/drivers/bus/uniphier-system-bus.c index 1e6e0269edcc..f76be6bd6eb3 100644 --- a/drivers/bus/uniphier-system-bus.c +++ b/drivers/bus/uniphier-system-bus.c @@ -256,10 +256,23 @@ static int uniphier_system_bus_probe(struct platform_device *pdev) uniphier_system_bus_set_reg(priv); + platform_set_drvdata(pdev, priv); + /* Now, the bus is configured. Populate platform_devices below it */ return of_platform_default_populate(dev->of_node, NULL, dev); } +static int __maybe_unused uniphier_system_bus_resume(struct device *dev) +{ + uniphier_system_bus_set_reg(dev_get_drvdata(dev)); + + return 0; +} + +static const struct dev_pm_ops uniphier_system_bus_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(NULL, uniphier_system_bus_resume) +}; + static const struct of_device_id uniphier_system_bus_match[] = { { .compatible = "socionext,uniphier-system-bus" }, { /* sentinel */ } @@ -271,6 +284,7 @@ static struct platform_driver uniphier_system_bus_driver = { .driver = { .name = "uniphier-system-bus", .of_match_table = uniphier_system_bus_match, + .pm = &uniphier_system_bus_pm_ops, }, }; module_platform_driver(uniphier_system_bus_driver); diff --git a/drivers/char/mwave/smapi.c b/drivers/char/mwave/smapi.c index 8c5411a8f33f..691f5898bb32 100644 --- a/drivers/char/mwave/smapi.c +++ b/drivers/char/mwave/smapi.c @@ -128,10 +128,11 @@ int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings) { int bRC = -EIO; unsigned short usAX, usBX, usCX, usDX, usDI, usSI; - unsigned short ausDspBases[] = { 0x0030, 0x4E30, 0x8E30, 0xCE30, 0x0130, 0x0350, 0x0070, 0x0DB0 }; - unsigned short ausUartBases[] = { 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; - unsigned short numDspBases = 8; - unsigned short numUartBases = 4; + static const unsigned short ausDspBases[] = { + 0x0030, 0x4E30, 0x8E30, 0xCE30, + 0x0130, 0x0350, 0x0070, 0x0DB0 }; + static const unsigned short ausUartBases[] = { + 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; PRINTK_1(TRACE_SMAPI, "smapi::smapi_query_DSP_cfg entry\n"); @@ -148,7 +149,7 @@ int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings) pSettings->bDSPEnabled = ((usCX & 0x0001) != 0); pSettings->usDspIRQ = usSI & 0x00FF; pSettings->usDspDMA = (usSI & 0xFF00) >> 8; - if ((usDI & 0x00FF) < numDspBases) { + if ((usDI & 0x00FF) < ARRAY_SIZE(ausDspBases)) { pSettings->usDspBaseIO = ausDspBases[usDI & 0x00FF]; } else { pSettings->usDspBaseIO = 0; @@ -176,7 +177,7 @@ int smapi_query_DSP_cfg(SMAPI_DSP_SETTINGS * pSettings) pSettings->bModemEnabled = ((usCX & 0x0001) != 0); pSettings->usUartIRQ = usSI & 0x000F; - if (((usSI & 0xFF00) >> 8) < numUartBases) { + if (((usSI & 0xFF00) >> 8) < ARRAY_SIZE(ausUartBases)) { pSettings->usUartBaseIO = ausUartBases[(usSI & 0xFF00) >> 8]; } else { pSettings->usUartBaseIO = 0; @@ -205,15 +206,16 @@ int smapi_set_DSP_cfg(void) int bRC = -EIO; int i; unsigned short usAX, usBX, usCX, usDX, usDI, usSI; - unsigned short ausDspBases[] = { 0x0030, 0x4E30, 0x8E30, 0xCE30, 0x0130, 0x0350, 0x0070, 0x0DB0 }; - unsigned short ausUartBases[] = { 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; - unsigned short ausDspIrqs[] = { 5, 7, 10, 11, 15 }; - unsigned short ausUartIrqs[] = { 3, 4 }; - - unsigned short numDspBases = 8; - unsigned short numUartBases = 4; - unsigned short numDspIrqs = 5; - unsigned short numUartIrqs = 2; + static const unsigned short ausDspBases[] = { + 0x0030, 0x4E30, 0x8E30, 0xCE30, + 0x0130, 0x0350, 0x0070, 0x0DB0 }; + static const unsigned short ausUartBases[] = { + 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; + static const unsigned short ausDspIrqs[] = { + 5, 7, 10, 11, 15 }; + static const unsigned short ausUartIrqs[] = { + 3, 4 }; + unsigned short dspio_index = 0, uartio_index = 0; PRINTK_5(TRACE_SMAPI, @@ -221,11 +223,11 @@ int smapi_set_DSP_cfg(void) mwave_3780i_irq, mwave_3780i_io, mwave_uart_irq, mwave_uart_io); if (mwave_3780i_io) { - for (i = 0; i < numDspBases; i++) { + for (i = 0; i < ARRAY_SIZE(ausDspBases); i++) { if (mwave_3780i_io == ausDspBases[i]) break; } - if (i == numDspBases) { + if (i == ARRAY_SIZE(ausDspBases)) { PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_3780i_io address %x. Aborting.\n", mwave_3780i_io); return bRC; } @@ -233,22 +235,22 @@ int smapi_set_DSP_cfg(void) } if (mwave_3780i_irq) { - for (i = 0; i < numDspIrqs; i++) { + for (i = 0; i < ARRAY_SIZE(ausDspIrqs); i++) { if (mwave_3780i_irq == ausDspIrqs[i]) break; } - if (i == numDspIrqs) { + if (i == ARRAY_SIZE(ausDspIrqs)) { PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_3780i_irq %x. Aborting.\n", mwave_3780i_irq); return bRC; } } if (mwave_uart_io) { - for (i = 0; i < numUartBases; i++) { + for (i = 0; i < ARRAY_SIZE(ausUartBases); i++) { if (mwave_uart_io == ausUartBases[i]) break; } - if (i == numUartBases) { + if (i == ARRAY_SIZE(ausUartBases)) { PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_uart_io address %x. Aborting.\n", mwave_uart_io); return bRC; } @@ -257,11 +259,11 @@ int smapi_set_DSP_cfg(void) if (mwave_uart_irq) { - for (i = 0; i < numUartIrqs; i++) { + for (i = 0; i < ARRAY_SIZE(ausUartIrqs); i++) { if (mwave_uart_irq == ausUartIrqs[i]) break; } - if (i == numUartIrqs) { + if (i == ARRAY_SIZE(ausUartIrqs)) { PRINTK_ERROR(KERN_ERR_MWAVE "smapi::smapi_set_DSP_cfg: Error: Invalid mwave_uart_irq %x. Aborting.\n", mwave_uart_irq); return bRC; } diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 3e73bcdf9e65..d256110ba672 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -101,9 +101,6 @@ static DEFINE_IDA(ida_index); #define PP_BUFFER_SIZE 1024 #define PARDEVICE_MAX 8 -/* ROUND_UP macro from fs/select.c */ -#define ROUND_UP(x,y) (((x)+(y)-1)/(y)) - static DEFINE_MUTEX(pp_do_mutex); /* define fixed sized ioctl cmd for y2038 migration */ diff --git a/drivers/char/random.c b/drivers/char/random.c index afa3ce7d3e72..8ad92707e45f 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1492,7 +1492,7 @@ static void _warn_unseeded_randomness(const char *func_name, void *caller, #ifndef CONFIG_WARN_ALL_UNSEEDED_RANDOM print_once = true; #endif - pr_notice("random: %s called from %pF with crng_init=%d\n", + pr_notice("random: %s called from %pS with crng_init=%d\n", func_name, caller, crng_init); } diff --git a/drivers/clk/clk-gemini.c b/drivers/clk/clk-gemini.c index c391a49aaaff..b4cf2f699a21 100644 --- a/drivers/clk/clk-gemini.c +++ b/drivers/clk/clk-gemini.c @@ -237,6 +237,18 @@ static int gemini_reset(struct reset_controller_dev *rcdev, BIT(GEMINI_RESET_CPU1) | BIT(id)); } +static int gemini_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return 0; +} + +static int gemini_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return 0; +} + static int gemini_reset_status(struct reset_controller_dev *rcdev, unsigned long id) { @@ -253,6 +265,8 @@ static int gemini_reset_status(struct reset_controller_dev *rcdev, static const struct reset_control_ops gemini_reset_ops = { .reset = gemini_reset, + .assert = gemini_reset_assert, + .deassert = gemini_reset_deassert, .status = gemini_reset_status, }; diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c index 43b0f2f08df2..9cdf9d5050ac 100644 --- a/drivers/clk/keystone/sci-clk.c +++ b/drivers/clk/keystone/sci-clk.c @@ -22,6 +22,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/soc/ti/ti_sci_protocol.h> +#include <linux/bsearch.h> #define SCI_CLK_SSC_ENABLE BIT(0) #define SCI_CLK_ALLOW_FREQ_CHANGE BIT(1) @@ -44,6 +45,7 @@ struct sci_clk_data { * @dev: Device pointer for the clock provider * @clk_data: Clock data * @clocks: Clocks array for this device + * @num_clocks: Total number of clocks for this provider */ struct sci_clk_provider { const struct ti_sci_handle *sci; @@ -51,6 +53,7 @@ struct sci_clk_provider { struct device *dev; const struct sci_clk_data *clk_data; struct clk_hw **clocks; + int num_clocks; }; /** @@ -58,7 +61,6 @@ struct sci_clk_provider { * @hw: Hardware clock cookie for common clock framework * @dev_id: Device index * @clk_id: Clock index - * @node: Clocks list link * @provider: Master clock provider * @flags: Flags for the clock */ @@ -66,7 +68,6 @@ struct sci_clk { struct clk_hw hw; u16 dev_id; u8 clk_id; - struct list_head node; struct sci_clk_provider *provider; u8 flags; }; @@ -367,6 +368,19 @@ err: return &sci_clk->hw; } +static int _cmp_sci_clk(const void *a, const void *b) +{ + const struct sci_clk *ca = a; + const struct sci_clk *cb = *(struct sci_clk **)b; + + if (ca->dev_id == cb->dev_id && ca->clk_id == cb->clk_id) + return 0; + if (ca->dev_id > cb->dev_id || + (ca->dev_id == cb->dev_id && ca->clk_id > cb->clk_id)) + return 1; + return -1; +} + /** * sci_clk_get - Xlate function for getting clock handles * @clkspec: device tree clock specifier @@ -380,29 +394,22 @@ err: static struct clk_hw *sci_clk_get(struct of_phandle_args *clkspec, void *data) { struct sci_clk_provider *provider = data; - u16 dev_id; - u8 clk_id; - const struct sci_clk_data *clks = provider->clk_data; - struct clk_hw **clocks = provider->clocks; + struct sci_clk **clk; + struct sci_clk key; if (clkspec->args_count != 2) return ERR_PTR(-EINVAL); - dev_id = clkspec->args[0]; - clk_id = clkspec->args[1]; + key.dev_id = clkspec->args[0]; + key.clk_id = clkspec->args[1]; - while (clks->num_clks) { - if (clks->dev == dev_id) { - if (clk_id >= clks->num_clks) - return ERR_PTR(-EINVAL); - - return clocks[clk_id]; - } + clk = bsearch(&key, provider->clocks, provider->num_clocks, + sizeof(clk), _cmp_sci_clk); - clks++; - } + if (!clk) + return ERR_PTR(-ENODEV); - return ERR_PTR(-ENODEV); + return &(*clk)->hw; } static int ti_sci_init_clocks(struct sci_clk_provider *p) @@ -410,18 +417,29 @@ static int ti_sci_init_clocks(struct sci_clk_provider *p) const struct sci_clk_data *data = p->clk_data; struct clk_hw *hw; int i; + int num_clks = 0; while (data->num_clks) { - p->clocks = devm_kcalloc(p->dev, data->num_clks, - sizeof(struct sci_clk), - GFP_KERNEL); - if (!p->clocks) - return -ENOMEM; + num_clks += data->num_clks; + data++; + } + p->num_clocks = num_clks; + + p->clocks = devm_kcalloc(p->dev, num_clks, sizeof(struct sci_clk), + GFP_KERNEL); + if (!p->clocks) + return -ENOMEM; + + num_clks = 0; + + data = p->clk_data; + + while (data->num_clks) { for (i = 0; i < data->num_clks; i++) { hw = _sci_clk_build(p, data->dev, i); if (!IS_ERR(hw)) { - p->clocks[i] = hw; + p->clocks[num_clks++] = hw; continue; } diff --git a/drivers/clk/meson/clk-mpll.c b/drivers/clk/meson/clk-mpll.c index 39eab69fe51a..44a5a535ca63 100644 --- a/drivers/clk/meson/clk-mpll.c +++ b/drivers/clk/meson/clk-mpll.c @@ -161,6 +161,13 @@ static int mpll_set_rate(struct clk_hw *hw, reg = PARM_SET(p->width, p->shift, reg, 1); writel(reg, mpll->base + p->reg_off); + p = &mpll->ssen; + if (p->width != 0) { + reg = readl(mpll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, 1); + writel(reg, mpll->base + p->reg_off); + } + p = &mpll->n2; reg = readl(mpll->base + p->reg_off); reg = PARM_SET(p->width, p->shift, reg, n2); diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h index d6feafe8bd6c..1629da9b4141 100644 --- a/drivers/clk/meson/clkc.h +++ b/drivers/clk/meson/clkc.h @@ -118,6 +118,7 @@ struct meson_clk_mpll { struct parm sdm_en; struct parm n2; struct parm en; + struct parm ssen; spinlock_t *lock; }; diff --git a/drivers/clk/meson/gxbb.c b/drivers/clk/meson/gxbb.c index a897ea45327c..a7ea5f3da89d 100644 --- a/drivers/clk/meson/gxbb.c +++ b/drivers/clk/meson/gxbb.c @@ -528,6 +528,11 @@ static struct meson_clk_mpll gxbb_mpll0 = { .shift = 14, .width = 1, }, + .ssen = { + .reg_off = HHI_MPLL_CNTL, + .shift = 25, + .width = 1, + }, .lock = &clk_lock, .hw.init = &(struct clk_init_data){ .name = "mpll0", diff --git a/drivers/clk/meson/meson8b.c b/drivers/clk/meson/meson8b.c index bb3f1de876b1..6ec512ad2598 100644 --- a/drivers/clk/meson/meson8b.c +++ b/drivers/clk/meson/meson8b.c @@ -267,6 +267,11 @@ static struct meson_clk_mpll meson8b_mpll0 = { .shift = 14, .width = 1, }, + .ssen = { + .reg_off = HHI_MPLL_CNTL, + .shift = 25, + .width = 1, + }, .lock = &clk_lock, .hw.init = &(struct clk_init_data){ .name = "mpll0", diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 0748a0b333c5..9a6476aa7d81 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -1283,16 +1283,16 @@ static const struct samsung_pll_rate_table exynos5420_pll2550x_24mhz_tbl[] __ini static const struct samsung_pll_rate_table exynos5420_epll_24mhz_tbl[] = { PLL_36XX_RATE(600000000U, 100, 2, 1, 0), PLL_36XX_RATE(400000000U, 200, 3, 2, 0), - PLL_36XX_RATE(393216000U, 197, 3, 2, 25690), - PLL_36XX_RATE(361267200U, 301, 5, 2, 3671), + PLL_36XX_RATE(393216003U, 197, 3, 2, -25690), + PLL_36XX_RATE(361267218U, 301, 5, 2, 3671), PLL_36XX_RATE(200000000U, 200, 3, 3, 0), - PLL_36XX_RATE(196608000U, 197, 3, 3, -25690), - PLL_36XX_RATE(180633600U, 301, 5, 3, 3671), - PLL_36XX_RATE(131072000U, 131, 3, 3, 4719), + PLL_36XX_RATE(196608001U, 197, 3, 3, -25690), + PLL_36XX_RATE(180633609U, 301, 5, 3, 3671), + PLL_36XX_RATE(131072006U, 131, 3, 3, 4719), PLL_36XX_RATE(100000000U, 200, 3, 4, 0), - PLL_36XX_RATE(65536000U, 131, 3, 4, 4719), - PLL_36XX_RATE(49152000U, 197, 3, 5, 25690), - PLL_36XX_RATE(32768000U, 131, 3, 5, 4719), + PLL_36XX_RATE( 65536003U, 131, 3, 4, 4719), + PLL_36XX_RATE( 49152000U, 197, 3, 5, -25690), + PLL_36XX_RATE( 32768001U, 131, 3, 5, 4719), }; static struct samsung_pll_clock exynos5x_plls[nr_plls] __initdata = { diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.c b/drivers/clk/sunxi-ng/ccu-sun5i.c index 5372bf8be5e6..31d7ffda9aab 100644 --- a/drivers/clk/sunxi-ng/ccu-sun5i.c +++ b/drivers/clk/sunxi-ng/ccu-sun5i.c @@ -184,7 +184,7 @@ static struct ccu_mux cpu_clk = { .hw.init = CLK_HW_INIT_PARENTS("cpu", cpu_parents, &ccu_mux_ops, - CLK_IS_CRITICAL), + CLK_SET_RATE_PARENT | CLK_IS_CRITICAL), } }; diff --git a/drivers/clk/x86/clk-pmc-atom.c b/drivers/clk/x86/clk-pmc-atom.c index f99abc1106f0..08ef69945ffb 100644 --- a/drivers/clk/x86/clk-pmc-atom.c +++ b/drivers/clk/x86/clk-pmc-atom.c @@ -186,6 +186,13 @@ static struct clk_plt *plt_clk_register(struct platform_device *pdev, int id, pclk->reg = base + PMC_CLK_CTL_OFFSET + id * PMC_CLK_CTL_SIZE; spin_lock_init(&pclk->lock); + /* + * If the clock was already enabled by the firmware mark it as critical + * to avoid it being gated by the clock framework if no driver owns it. + */ + if (plt_clk_is_enabled(&pclk->hw)) + init.flags |= CLK_IS_CRITICAL; + ret = devm_clk_hw_register(&pdev->dev, &pclk->hw); if (ret) { pclk = ERR_PTR(ret); diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index fcae5ca6ac92..54a67f8a28eb 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -262,7 +262,7 @@ config CLKSRC_LPC32XX config CLKSRC_PISTACHIO bool "Clocksource for Pistachio SoC" if COMPILE_TEST - depends on HAS_IOMEM + depends on GENERIC_CLOCKEVENTS && HAS_IOMEM select TIMER_OF help Enables the clocksource for the Pistachio SoC. diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index aae87c4c546e..72bbfccef113 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -1440,7 +1440,7 @@ static int __init arch_timer_mem_acpi_init(int platform_timer_count) * While unlikely, it's theoretically possible that none of the frames * in a timer expose the combination of feature we want. */ - for (i = i; i < timer_count; i++) { + for (i = 0; i < timer_count; i++) { timer = &timers[i]; frame = arch_timer_mem_find_best_frame(timer); diff --git a/drivers/clocksource/em_sti.c b/drivers/clocksource/em_sti.c index bc48cbf6a795..269db74a0658 100644 --- a/drivers/clocksource/em_sti.c +++ b/drivers/clocksource/em_sti.c @@ -305,7 +305,7 @@ static int em_sti_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "failed to get irq\n"); - return -EINVAL; + return irq; } /* map memory, let base point to the STI instance */ @@ -314,11 +314,12 @@ static int em_sti_probe(struct platform_device *pdev) if (IS_ERR(p->base)) return PTR_ERR(p->base); - if (devm_request_irq(&pdev->dev, irq, em_sti_interrupt, - IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, - dev_name(&pdev->dev), p)) { + ret = devm_request_irq(&pdev->dev, irq, em_sti_interrupt, + IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING, + dev_name(&pdev->dev), p); + if (ret) { dev_err(&pdev->dev, "failed to request low IRQ\n"); - return -ENOENT; + return ret; } /* get hold of clock */ diff --git a/drivers/clocksource/timer-of.c b/drivers/clocksource/timer-of.c index f6e7491c873c..4d7aef9d9c15 100644 --- a/drivers/clocksource/timer-of.c +++ b/drivers/clocksource/timer-of.c @@ -41,8 +41,16 @@ static __init int timer_irq_init(struct device_node *np, struct timer_of *to = container_of(of_irq, struct timer_of, of_irq); struct clock_event_device *clkevt = &to->clkevt; - of_irq->irq = of_irq->name ? of_irq_get_byname(np, of_irq->name): - irq_of_parse_and_map(np, of_irq->index); + if (of_irq->name) { + of_irq->irq = ret = of_irq_get_byname(np, of_irq->name); + if (ret < 0) { + pr_err("Failed to get interrupt %s for %s\n", + of_irq->name, np->full_name); + return ret; + } + } else { + of_irq->irq = irq_of_parse_and_map(np, of_irq->index); + } if (!of_irq->irq) { pr_err("Failed to map interrupt for %s\n", np->full_name); return -EINVAL; @@ -120,9 +128,9 @@ static __init int timer_base_init(struct device_node *np, const char *name = of_base->name ? of_base->name : np->full_name; of_base->base = of_io_request_and_map(np, of_base->index, name); - if (!of_base->base) { + if (IS_ERR(of_base->base)) { pr_err("Failed to iomap (%s)\n", name); - return -ENXIO; + return PTR_ERR(of_base->base); } return 0; diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index b7fb8b7c980d..65ee4fcace1f 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -225,6 +225,9 @@ struct global_params { * @vid: Stores VID limits for this CPU * @pid: Stores PID parameters for this CPU * @last_sample_time: Last Sample time + * @aperf_mperf_shift: Number of clock cycles after aperf, merf is incremented + * This shift is a multiplier to mperf delta to + * calculate CPU busy. * @prev_aperf: Last APERF value read from APERF MSR * @prev_mperf: Last MPERF value read from MPERF MSR * @prev_tsc: Last timestamp counter (TSC) value @@ -259,6 +262,7 @@ struct cpudata { u64 last_update; u64 last_sample_time; + u64 aperf_mperf_shift; u64 prev_aperf; u64 prev_mperf; u64 prev_tsc; @@ -321,6 +325,7 @@ struct pstate_funcs { int (*get_min)(void); int (*get_turbo)(void); int (*get_scaling)(void); + int (*get_aperf_mperf_shift)(void); u64 (*get_val)(struct cpudata*, int pstate); void (*get_vid)(struct cpudata *); void (*update_util)(struct update_util_data *data, u64 time, @@ -1486,6 +1491,11 @@ static u64 core_get_val(struct cpudata *cpudata, int pstate) return val; } +static int knl_get_aperf_mperf_shift(void) +{ + return 10; +} + static int knl_get_turbo_pstate(void) { u64 value; @@ -1543,6 +1553,9 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) cpu->pstate.max_freq = cpu->pstate.max_pstate * cpu->pstate.scaling; cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * cpu->pstate.scaling; + if (pstate_funcs.get_aperf_mperf_shift) + cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift(); + if (pstate_funcs.get_vid) pstate_funcs.get_vid(cpu); @@ -1600,8 +1613,7 @@ static inline bool intel_pstate_sample(struct cpudata *cpu, u64 time) static inline int32_t get_avg_frequency(struct cpudata *cpu) { - return mul_ext_fp(cpu->sample.core_avg_perf, - cpu->pstate.max_pstate_physical * cpu->pstate.scaling); + return mul_ext_fp(cpu->sample.core_avg_perf, cpu_khz); } static inline int32_t get_avg_pstate(struct cpudata *cpu) @@ -1616,7 +1628,8 @@ static inline int32_t get_target_pstate_use_cpu_load(struct cpudata *cpu) int32_t busy_frac, boost; int target, avg_pstate; - busy_frac = div_fp(sample->mperf, sample->tsc); + busy_frac = div_fp(sample->mperf << cpu->aperf_mperf_shift, + sample->tsc); boost = cpu->iowait_boost; cpu->iowait_boost >>= 1; @@ -1675,7 +1688,8 @@ static inline int32_t get_target_pstate_use_performance(struct cpudata *cpu) sample_ratio = div_fp(pid_params.sample_rate_ns, duration_ns); perf_scaled = mul_fp(perf_scaled, sample_ratio); } else { - sample_ratio = div_fp(100 * cpu->sample.mperf, cpu->sample.tsc); + sample_ratio = div_fp(100 * (cpu->sample.mperf << cpu->aperf_mperf_shift), + cpu->sample.tsc); if (sample_ratio < int_tofp(1)) perf_scaled = 0; } @@ -1807,6 +1821,7 @@ static const struct pstate_funcs knl_funcs = { .get_max_physical = core_get_max_pstate_physical, .get_min = core_get_min_pstate, .get_turbo = knl_get_turbo_pstate, + .get_aperf_mperf_shift = knl_get_aperf_mperf_shift, .get_scaling = core_get_scaling, .get_val = core_get_val, .update_util = intel_pstate_update_util_pid, @@ -1906,13 +1921,6 @@ static int intel_pstate_init_cpu(unsigned int cpunum) return 0; } -static unsigned int intel_pstate_get(unsigned int cpu_num) -{ - struct cpudata *cpu = all_cpu_data[cpu_num]; - - return cpu ? get_avg_frequency(cpu) : 0; -} - static void intel_pstate_set_update_util_hook(unsigned int cpu_num) { struct cpudata *cpu = all_cpu_data[cpu_num]; @@ -2153,7 +2161,6 @@ static struct cpufreq_driver intel_pstate = { .setpolicy = intel_pstate_set_policy, .suspend = intel_pstate_hwp_save_state, .resume = intel_pstate_resume, - .get = intel_pstate_get, .init = intel_pstate_cpu_init, .exit = intel_pstate_cpu_exit, .stop_cpu = intel_pstate_stop_cpu, @@ -2403,6 +2410,7 @@ static void __init copy_cpu_funcs(struct pstate_funcs *funcs) pstate_funcs.get_val = funcs->get_val; pstate_funcs.get_vid = funcs->get_vid; pstate_funcs.update_util = funcs->update_util; + pstate_funcs.get_aperf_mperf_shift = funcs->get_aperf_mperf_shift; intel_pstate_use_acpi_profile(); } diff --git a/drivers/cpuidle/cpuidle-powernv.c b/drivers/cpuidle/cpuidle-powernv.c index 37b0698b7193..42896a67aeae 100644 --- a/drivers/cpuidle/cpuidle-powernv.c +++ b/drivers/cpuidle/cpuidle-powernv.c @@ -235,6 +235,7 @@ static inline int validate_dt_prop_sizes(const char *prop1, int prop1_len, return -1; } +extern u32 pnv_get_supported_cpuidle_states(void); static int powernv_add_idle_states(void) { struct device_node *power_mgt; @@ -248,6 +249,8 @@ static int powernv_add_idle_states(void) const char *names[CPUIDLE_STATE_MAX]; u32 has_stop_states = 0; int i, rc; + u32 supported_flags = pnv_get_supported_cpuidle_states(); + /* Currently we have snooze statically defined */ @@ -362,6 +365,13 @@ static int powernv_add_idle_states(void) for (i = 0; i < dt_idle_states; i++) { unsigned int exit_latency, target_residency; bool stops_timebase = false; + + /* + * Skip the platform idle state whose flag isn't in + * the supported_cpuidle_states flag mask. + */ + if ((flags[i] & supported_flags) != flags[i]) + continue; /* * If an idle state has exit latency beyond * POWERNV_THRESHOLD_LATENCY_NS then don't use it diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 193204dfbf3a..4b75084fabad 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -655,7 +655,7 @@ source "drivers/crypto/virtio/Kconfig" config CRYPTO_DEV_BCM_SPU tristate "Broadcom symmetric crypto/hash acceleration support" depends on ARCH_BCM_IPROC - depends on BCM_PDC_MBOX + depends on MAILBOX default m select CRYPTO_DES select CRYPTO_MD5 diff --git a/drivers/crypto/bcm/spu2.c b/drivers/crypto/bcm/spu2.c index ef04c9748317..bf7ac621c591 100644 --- a/drivers/crypto/bcm/spu2.c +++ b/drivers/crypto/bcm/spu2.c @@ -302,6 +302,7 @@ spu2_hash_xlate(enum hash_alg hash_alg, enum hash_mode hash_mode, break; case HASH_ALG_SHA3_512: *spu2_type = SPU2_HASH_TYPE_SHA3_512; + break; case HASH_ALG_LAST: default: err = -EINVAL; diff --git a/drivers/crypto/cavium/nitrox/nitrox_main.c b/drivers/crypto/cavium/nitrox/nitrox_main.c index ae44a464cd2d..9ccefb9b7232 100644 --- a/drivers/crypto/cavium/nitrox/nitrox_main.c +++ b/drivers/crypto/cavium/nitrox/nitrox_main.c @@ -18,8 +18,9 @@ #define SE_GROUP 0 #define DRIVER_VERSION "1.0" +#define FW_DIR "cavium/" /* SE microcode */ -#define SE_FW "cnn55xx_se.fw" +#define SE_FW FW_DIR "cnn55xx_se.fw" static const char nitrox_driver_name[] = "CNN55XX"; diff --git a/drivers/crypto/inside-secure/safexcel.c b/drivers/crypto/inside-secure/safexcel.c index e7f87ac12685..1fabd4aee81b 100644 --- a/drivers/crypto/inside-secure/safexcel.c +++ b/drivers/crypto/inside-secure/safexcel.c @@ -773,7 +773,6 @@ static int safexcel_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct resource *res; struct safexcel_crypto_priv *priv; - u64 dma_mask; int i, ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -802,9 +801,7 @@ static int safexcel_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - if (of_property_read_u64(dev->of_node, "dma-mask", &dma_mask)) - dma_mask = DMA_BIT_MASK(64); - ret = dma_set_mask_and_coherent(dev, dma_mask); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); if (ret) goto err_clk; diff --git a/drivers/crypto/inside-secure/safexcel_hash.c b/drivers/crypto/inside-secure/safexcel_hash.c index 8527a5899a2f..3f819399cd95 100644 --- a/drivers/crypto/inside-secure/safexcel_hash.c +++ b/drivers/crypto/inside-secure/safexcel_hash.c @@ -883,10 +883,7 @@ static int safexcel_hmac_sha1_setkey(struct crypto_ahash *tfm, const u8 *key, if (ret) return ret; - memcpy(ctx->ipad, &istate.state, SHA1_DIGEST_SIZE); - memcpy(ctx->opad, &ostate.state, SHA1_DIGEST_SIZE); - - for (i = 0; i < ARRAY_SIZE(istate.state); i++) { + for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(u32); i++) { if (ctx->ipad[i] != le32_to_cpu(istate.state[i]) || ctx->opad[i] != le32_to_cpu(ostate.state[i])) { ctx->base.needs_inv = true; @@ -894,6 +891,9 @@ static int safexcel_hmac_sha1_setkey(struct crypto_ahash *tfm, const u8 *key, } } + memcpy(ctx->ipad, &istate.state, SHA1_DIGEST_SIZE); + memcpy(ctx->opad, &ostate.state, SHA1_DIGEST_SIZE); + return 0; } diff --git a/drivers/crypto/ixp4xx_crypto.c b/drivers/crypto/ixp4xx_crypto.c index 427cbe012729..dadc4a808df5 100644 --- a/drivers/crypto/ixp4xx_crypto.c +++ b/drivers/crypto/ixp4xx_crypto.c @@ -1073,7 +1073,7 @@ static int aead_perform(struct aead_request *req, int encrypt, req_ctx->hmac_virt = dma_pool_alloc(buffer_pool, flags, &crypt->icv_rev_aes); if (unlikely(!req_ctx->hmac_virt)) - goto free_buf_src; + goto free_buf_dst; if (!encrypt) { scatterwalk_map_and_copy(req_ctx->hmac_virt, req->src, cryptlen, authsize, 0); @@ -1088,10 +1088,10 @@ static int aead_perform(struct aead_request *req, int encrypt, BUG_ON(qmgr_stat_overflow(SEND_QID)); return -EINPROGRESS; -free_buf_src: - free_buf_chain(dev, req_ctx->src, crypt->src_buf); free_buf_dst: free_buf_chain(dev, req_ctx->dst, crypt->dst_buf); +free_buf_src: + free_buf_chain(dev, req_ctx->src, crypt->src_buf); crypt->ctl_flags = CTL_FLAG_UNUSED; return -ENOMEM; } diff --git a/drivers/dax/device-dax.h b/drivers/dax/device-dax.h index fdcd9769ffde..688b051750bd 100644 --- a/drivers/dax/device-dax.h +++ b/drivers/dax/device-dax.h @@ -21,5 +21,5 @@ struct dax_region *alloc_dax_region(struct device *parent, int region_id, struct resource *res, unsigned int align, void *addr, unsigned long flags); struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, - struct resource *res, int count); + int id, struct resource *res, int count); #endif /* __DEVICE_DAX_H__ */ diff --git a/drivers/dax/device.c b/drivers/dax/device.c index 12943d19bfc4..e9f3b3e4bbf4 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -529,7 +529,8 @@ static void dev_dax_release(struct device *dev) struct dax_region *dax_region = dev_dax->region; struct dax_device *dax_dev = dev_dax->dax_dev; - ida_simple_remove(&dax_region->ida, dev_dax->id); + if (dev_dax->id >= 0) + ida_simple_remove(&dax_region->ida, dev_dax->id); dax_region_put(dax_region); put_dax(dax_dev); kfree(dev_dax); @@ -559,7 +560,7 @@ static void unregister_dev_dax(void *dev) } struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, - struct resource *res, int count) + int id, struct resource *res, int count) { struct device *parent = dax_region->dev; struct dax_device *dax_dev; @@ -567,7 +568,10 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, struct inode *inode; struct device *dev; struct cdev *cdev; - int rc = 0, i; + int rc, i; + + if (!count) + return ERR_PTR(-EINVAL); dev_dax = kzalloc(sizeof(*dev_dax) + sizeof(*res) * count, GFP_KERNEL); if (!dev_dax) @@ -587,10 +591,16 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, if (i < count) goto err_id; - dev_dax->id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL); - if (dev_dax->id < 0) { - rc = dev_dax->id; - goto err_id; + if (id < 0) { + id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL); + dev_dax->id = id; + if (id < 0) { + rc = id; + goto err_id; + } + } else { + /* region provider owns @id lifetime */ + dev_dax->id = -1; } /* @@ -598,8 +608,10 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, * device outside of mmap of the resulting character device. */ dax_dev = alloc_dax(dev_dax, NULL, NULL); - if (!dax_dev) + if (!dax_dev) { + rc = -ENOMEM; goto err_dax; + } /* from here on we're committed to teardown via dax_dev_release() */ dev = &dev_dax->dev; @@ -620,7 +632,7 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, dev->parent = parent; dev->groups = dax_attribute_groups; dev->release = dev_dax_release; - dev_set_name(dev, "dax%d.%d", dax_region->id, dev_dax->id); + dev_set_name(dev, "dax%d.%d", dax_region->id, id); rc = cdev_device_add(cdev, dev); if (rc) { @@ -636,7 +648,8 @@ struct dev_dax *devm_create_dev_dax(struct dax_region *dax_region, return dev_dax; err_dax: - ida_simple_remove(&dax_region->ida, dev_dax->id); + if (dev_dax->id >= 0) + ida_simple_remove(&dax_region->ida, dev_dax->id); err_id: kfree(dev_dax); diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c index 9f2a0b4fd801..8d8c852ba8f2 100644 --- a/drivers/dax/pmem.c +++ b/drivers/dax/pmem.c @@ -58,13 +58,12 @@ static void dax_pmem_percpu_kill(void *data) static int dax_pmem_probe(struct device *dev) { - int rc; void *addr; struct resource res; + int rc, id, region_id; struct nd_pfn_sb *pfn_sb; struct dev_dax *dev_dax; struct dax_pmem *dax_pmem; - struct nd_region *nd_region; struct nd_namespace_io *nsio; struct dax_region *dax_region; struct nd_namespace_common *ndns; @@ -123,14 +122,17 @@ static int dax_pmem_probe(struct device *dev) /* adjust the dax_region resource to the start of data */ res.start += le64_to_cpu(pfn_sb->dataoff); - nd_region = to_nd_region(dev->parent); - dax_region = alloc_dax_region(dev, nd_region->id, &res, + rc = sscanf(dev_name(&ndns->dev), "namespace%d.%d", ®ion_id, &id); + if (rc != 2) + return -EINVAL; + + dax_region = alloc_dax_region(dev, region_id, &res, le32_to_cpu(pfn_sb->align), addr, PFN_DEV|PFN_MAP); if (!dax_region) return -ENOMEM; /* TODO: support for subdividing a dax region... */ - dev_dax = devm_create_dev_dax(dax_region, &res, 1); + dev_dax = devm_create_dev_dax(dax_region, id, &res, 1); /* child dev_dax instances now own the lifetime of the dax_region */ dax_region_put(dax_region); diff --git a/drivers/dax/super.c b/drivers/dax/super.c index ce9e563e6e1d..938eb4868f7f 100644 --- a/drivers/dax/super.c +++ b/drivers/dax/super.c @@ -278,6 +278,12 @@ void dax_write_cache(struct dax_device *dax_dev, bool wc) } EXPORT_SYMBOL_GPL(dax_write_cache); +bool dax_write_cache_enabled(struct dax_device *dax_dev) +{ + return test_bit(DAXDEV_WRITE_CACHE, &dax_dev->flags); +} +EXPORT_SYMBOL_GPL(dax_write_cache_enabled); + bool dax_alive(struct dax_device *dax_dev) { lockdep_assert_held(&dax_srcu); diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 57da14c15987..56e0a0e1b600 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -75,11 +75,6 @@ int dma_fence_signal_locked(struct dma_fence *fence) if (WARN_ON(!fence)) return -EINVAL; - if (!ktime_to_ns(fence->timestamp)) { - fence->timestamp = ktime_get(); - smp_mb__before_atomic(); - } - if (test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) { ret = -EINVAL; @@ -87,8 +82,11 @@ int dma_fence_signal_locked(struct dma_fence *fence) * we might have raced with the unlocked dma_fence_signal, * still run through all callbacks */ - } else + } else { + fence->timestamp = ktime_get(); + set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags); trace_dma_fence_signaled(fence); + } list_for_each_entry_safe(cur, tmp, &fence->cb_list, node) { list_del_init(&cur->node); @@ -115,14 +113,11 @@ int dma_fence_signal(struct dma_fence *fence) if (!fence) return -EINVAL; - if (!ktime_to_ns(fence->timestamp)) { - fence->timestamp = ktime_get(); - smp_mb__before_atomic(); - } - if (test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) return -EINVAL; + fence->timestamp = ktime_get(); + set_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags); trace_dma_fence_signaled(fence); if (test_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, &fence->flags)) { diff --git a/drivers/dma-buf/sync_debug.c b/drivers/dma-buf/sync_debug.c index 82a6e7f6d37f..59a3b2f8ee91 100644 --- a/drivers/dma-buf/sync_debug.c +++ b/drivers/dma-buf/sync_debug.c @@ -84,7 +84,7 @@ static void sync_print_fence(struct seq_file *s, show ? "_" : "", sync_status_str(status)); - if (status) { + if (test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags)) { struct timespec64 ts64 = ktime_to_timespec64(fence->timestamp); diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c index 545e2c5c4815..66fb40d0ebdb 100644 --- a/drivers/dma-buf/sync_file.c +++ b/drivers/dma-buf/sync_file.c @@ -304,7 +304,7 @@ static int sync_file_release(struct inode *inode, struct file *file) { struct sync_file *sync_file = file->private_data; - if (test_bit(POLL_ENABLED, &sync_file->fence->flags)) + if (test_bit(POLL_ENABLED, &sync_file->flags)) dma_fence_remove_callback(sync_file->fence, &sync_file->cb); dma_fence_put(sync_file->fence); kfree(sync_file); @@ -318,7 +318,8 @@ static unsigned int sync_file_poll(struct file *file, poll_table *wait) poll_wait(file, &sync_file->wq, wait); - if (!test_and_set_bit(POLL_ENABLED, &sync_file->fence->flags)) { + if (list_empty(&sync_file->cb.node) && + !test_and_set_bit(POLL_ENABLED, &sync_file->flags)) { if (dma_fence_add_callback(sync_file->fence, &sync_file->cb, fence_check_cb_func) < 0) wake_up_all(&sync_file->wq); @@ -391,7 +392,13 @@ static void sync_fill_fence_info(struct dma_fence *fence, sizeof(info->driver_name)); info->status = dma_fence_get_status(fence); - info->timestamp_ns = ktime_to_ns(fence->timestamp); + while (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) && + !test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags)) + cpu_relax(); + info->timestamp_ns = + test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags) ? + ktime_to_ns(fence->timestamp) : + ktime_set(0, 0); } static long sync_file_ioctl_fence_info(struct sync_file *sync_file, diff --git a/drivers/dma/tegra210-adma.c b/drivers/dma/tegra210-adma.c index b10cbaa82ff5..b26256f23d67 100644 --- a/drivers/dma/tegra210-adma.c +++ b/drivers/dma/tegra210-adma.c @@ -717,8 +717,8 @@ static int tegra_adma_probe(struct platform_device *pdev) tdc->chan_addr = tdma->base_addr + ADMA_CH_REG_OFFSET(i); tdc->irq = of_irq_get(pdev->dev.of_node, i); - if (tdc->irq < 0) { - ret = tdc->irq; + if (tdc->irq <= 0) { + ret = tdc->irq ?: -ENXIO; goto irq_dispose; } diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c index 78945729388e..35e553b3b190 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/google/vpd.c @@ -202,7 +202,7 @@ static int vpd_section_init(const char *name, struct vpd_section *sec, sec->raw_name = kasprintf(GFP_KERNEL, "%s_raw", name); if (!sec->raw_name) { err = -ENOMEM; - goto err_iounmap; + goto err_memunmap; } sysfs_bin_attr_init(&sec->bin_attr); @@ -233,8 +233,8 @@ err_sysfs_remove: sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr); err_free_raw_name: kfree(sec->raw_name); -err_iounmap: - iounmap(sec->baseaddr); +err_memunmap: + memunmap(sec->baseaddr); return err; } @@ -245,7 +245,7 @@ static int vpd_section_destroy(struct vpd_section *sec) kobject_put(sec->kobj); sysfs_remove_bin_file(vpd_kobj, &sec->bin_attr); kfree(sec->raw_name); - iounmap(sec->baseaddr); + memunmap(sec->baseaddr); } return 0; @@ -262,7 +262,7 @@ static int vpd_sections_init(phys_addr_t physaddr) return -ENOMEM; memcpy_fromio(&header, temp, sizeof(struct vpd_cbmem)); - iounmap(temp); + memunmap(temp); if (header.magic != VPD_CBMEM_MAGIC) return -ENODEV; diff --git a/drivers/fmc/Makefile b/drivers/fmc/Makefile index b9452919739f..e809322e1bac 100644 --- a/drivers/fmc/Makefile +++ b/drivers/fmc/Makefile @@ -6,6 +6,7 @@ fmc-y += fmc-match.o fmc-y += fmc-sdb.o fmc-y += fru-parse.o fmc-y += fmc-dump.o +fmc-y += fmc-debug.o obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o diff --git a/drivers/fmc/fmc-chardev.c b/drivers/fmc/fmc-chardev.c index ace6ef24d15e..5ecf4090a610 100644 --- a/drivers/fmc/fmc-chardev.c +++ b/drivers/fmc/fmc-chardev.c @@ -129,8 +129,7 @@ static int fc_probe(struct fmc_device *fmc) struct fc_instance *fc; - if (fmc->op->validate) - index = fmc->op->validate(fmc, &fc_drv); + index = fmc_validate(fmc, &fc_drv); if (index < 0) return -EINVAL; /* not our device: invalid */ diff --git a/drivers/fmc/fmc-core.c b/drivers/fmc/fmc-core.c index 353fc546fb08..cec3b8db0d69 100644 --- a/drivers/fmc/fmc-core.c +++ b/drivers/fmc/fmc-core.c @@ -13,6 +13,9 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/fmc.h> +#include <linux/fmc-sdb.h> + +#include "fmc-private.h" static int fmc_check_version(unsigned long version, const char *name) { @@ -118,6 +121,61 @@ static struct bin_attribute fmc_eeprom_attr = { .write = fmc_write_eeprom, }; +int fmc_irq_request(struct fmc_device *fmc, irq_handler_t h, + char *name, int flags) +{ + if (fmc->op->irq_request) + return fmc->op->irq_request(fmc, h, name, flags); + return -EPERM; +} +EXPORT_SYMBOL(fmc_irq_request); + +void fmc_irq_free(struct fmc_device *fmc) +{ + if (fmc->op->irq_free) + fmc->op->irq_free(fmc); +} +EXPORT_SYMBOL(fmc_irq_free); + +void fmc_irq_ack(struct fmc_device *fmc) +{ + if (likely(fmc->op->irq_ack)) + fmc->op->irq_ack(fmc); +} +EXPORT_SYMBOL(fmc_irq_ack); + +int fmc_validate(struct fmc_device *fmc, struct fmc_driver *drv) +{ + if (fmc->op->validate) + return fmc->op->validate(fmc, drv); + return -EPERM; +} +EXPORT_SYMBOL(fmc_validate); + +int fmc_gpio_config(struct fmc_device *fmc, struct fmc_gpio *gpio, int ngpio) +{ + if (fmc->op->gpio_config) + return fmc->op->gpio_config(fmc, gpio, ngpio); + return -EPERM; +} +EXPORT_SYMBOL(fmc_gpio_config); + +int fmc_read_ee(struct fmc_device *fmc, int pos, void *d, int l) +{ + if (fmc->op->read_ee) + return fmc->op->read_ee(fmc, pos, d, l); + return -EPERM; +} +EXPORT_SYMBOL(fmc_read_ee); + +int fmc_write_ee(struct fmc_device *fmc, int pos, const void *d, int l) +{ + if (fmc->op->write_ee) + return fmc->op->write_ee(fmc, pos, d, l); + return -EPERM; +} +EXPORT_SYMBOL(fmc_write_ee); + /* * Functions for client modules follow */ @@ -141,7 +199,8 @@ EXPORT_SYMBOL(fmc_driver_unregister); * When a device set is registered, all eeproms must be read * and all FRUs must be parsed */ -int fmc_device_register_n(struct fmc_device **devs, int n) +int fmc_device_register_n_gw(struct fmc_device **devs, int n, + struct fmc_gateware *gw) { struct fmc_device *fmc, **devarray; uint32_t device_id; @@ -221,6 +280,21 @@ int fmc_device_register_n(struct fmc_device **devs, int n) else dev_set_name(&fmc->dev, "%s-%04x", fmc->mezzanine_name, device_id); + + if (gw) { + /* + * The carrier already know the bitstream to load + * for this set of FMC mezzanines. + */ + ret = fmc->op->reprogram_raw(fmc, NULL, + gw->bitstream, gw->len); + if (ret) { + dev_warn(fmc->hwdev, + "Invalid gateware for FMC mezzanine\n"); + goto out; + } + } + ret = device_add(&fmc->dev); if (ret < 0) { dev_err(fmc->hwdev, "Slot %i: Failed in registering " @@ -234,18 +308,16 @@ int fmc_device_register_n(struct fmc_device **devs, int n) } /* This device went well, give information to the user */ fmc_dump_eeprom(fmc); - fmc_dump_sdb(fmc); + fmc_debug_init(fmc); } return 0; out1: device_del(&fmc->dev); out: - fmc_free_id_info(fmc); - put_device(&fmc->dev); - kfree(devarray); for (i--; i >= 0; i--) { + fmc_debug_exit(devs[i]); sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); device_del(&devs[i]->dev); fmc_free_id_info(devs[i]); @@ -254,8 +326,20 @@ out: return ret; } +EXPORT_SYMBOL(fmc_device_register_n_gw); + +int fmc_device_register_n(struct fmc_device **devs, int n) +{ + return fmc_device_register_n_gw(devs, n, NULL); +} EXPORT_SYMBOL(fmc_device_register_n); +int fmc_device_register_gw(struct fmc_device *fmc, struct fmc_gateware *gw) +{ + return fmc_device_register_n_gw(&fmc, 1, gw); +} +EXPORT_SYMBOL(fmc_device_register_gw); + int fmc_device_register(struct fmc_device *fmc) { return fmc_device_register_n(&fmc, 1); @@ -273,6 +357,7 @@ void fmc_device_unregister_n(struct fmc_device **devs, int n) kfree(devs[0]->devarray); for (i = 0; i < n; i++) { + fmc_debug_exit(devs[i]); sysfs_remove_bin_file(&devs[i]->dev.kobj, &fmc_eeprom_attr); device_del(&devs[i]->dev); fmc_free_id_info(devs[i]); diff --git a/drivers/fmc/fmc-debug.c b/drivers/fmc/fmc-debug.c new file mode 100644 index 000000000000..32930722770c --- /dev/null +++ b/drivers/fmc/fmc-debug.c @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2015 CERN (www.cern.ch) + * Author: Federico Vaga <federico.vaga@cern.ch> + * + * Released according to the GNU GPL, version 2 or any later version. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <asm/byteorder.h> + +#include <linux/fmc.h> +#include <linux/sdb.h> +#include <linux/fmc-sdb.h> + +#define FMC_DBG_SDB_DUMP "dump_sdb" + +static char *__strip_trailing_space(char *buf, char *str, int len) +{ + int i = len - 1; + + memcpy(buf, str, len); + buf[len] = '\0'; + while (i >= 0 && buf[i] == ' ') + buf[i--] = '\0'; + return buf; +} + +#define __sdb_string(buf, field) ({ \ + BUILD_BUG_ON(sizeof(buf) < sizeof(field)); \ + __strip_trailing_space(buf, (void *)(field), sizeof(field)); \ + }) + +/** + * We do not check seq_printf() errors because we want to see things in any case + */ +static void fmc_sdb_dump_recursive(struct fmc_device *fmc, struct seq_file *s, + const struct sdb_array *arr) +{ + unsigned long base = arr->baseaddr; + int i, j, n = arr->len, level = arr->level; + char tmp[64]; + + for (i = 0; i < n; i++) { + union sdb_record *r; + struct sdb_product *p; + struct sdb_component *c; + + r = &arr->record[i]; + c = &r->dev.sdb_component; + p = &c->product; + + for (j = 0; j < level; j++) + seq_printf(s, " "); + switch (r->empty.record_type) { + case sdb_type_interconnect: + seq_printf(s, "%08llx:%08x %.19s\n", + __be64_to_cpu(p->vendor_id), + __be32_to_cpu(p->device_id), + p->name); + break; + case sdb_type_device: + seq_printf(s, "%08llx:%08x %.19s (%08llx-%08llx)\n", + __be64_to_cpu(p->vendor_id), + __be32_to_cpu(p->device_id), + p->name, + __be64_to_cpu(c->addr_first) + base, + __be64_to_cpu(c->addr_last) + base); + break; + case sdb_type_bridge: + seq_printf(s, "%08llx:%08x %.19s (bridge: %08llx)\n", + __be64_to_cpu(p->vendor_id), + __be32_to_cpu(p->device_id), + p->name, + __be64_to_cpu(c->addr_first) + base); + if (IS_ERR(arr->subtree[i])) { + seq_printf(s, "SDB: (bridge error %li)\n", + PTR_ERR(arr->subtree[i])); + break; + } + fmc_sdb_dump_recursive(fmc, s, arr->subtree[i]); + break; + case sdb_type_integration: + seq_printf(s, "integration\n"); + break; + case sdb_type_repo_url: + seq_printf(s, "Synthesis repository: %s\n", + __sdb_string(tmp, r->repo_url.repo_url)); + break; + case sdb_type_synthesis: + seq_printf(s, "Bitstream '%s' ", + __sdb_string(tmp, r->synthesis.syn_name)); + seq_printf(s, "synthesized %08x by %s ", + __be32_to_cpu(r->synthesis.date), + __sdb_string(tmp, r->synthesis.user_name)); + seq_printf(s, "(%s version %x), ", + __sdb_string(tmp, r->synthesis.tool_name), + __be32_to_cpu(r->synthesis.tool_version)); + seq_printf(s, "commit %pm\n", + r->synthesis.commit_id); + break; + case sdb_type_empty: + seq_printf(s, "empty\n"); + break; + default: + seq_printf(s, "UNKNOWN TYPE 0x%02x\n", + r->empty.record_type); + break; + } + } +} + +static int fmc_sdb_dump(struct seq_file *s, void *offset) +{ + struct fmc_device *fmc = s->private; + + if (!fmc->sdb) { + seq_printf(s, "no SDB information\n"); + return 0; + } + + seq_printf(s, "FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev), + fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev)); + /* Dump SDB information */ + fmc_sdb_dump_recursive(fmc, s, fmc->sdb); + + return 0; +} + + +static int fmc_sdb_dump_open(struct inode *inode, struct file *file) +{ + struct fmc_device *fmc = inode->i_private; + + return single_open(file, fmc_sdb_dump, fmc); +} + + +const struct file_operations fmc_dbgfs_sdb_dump = { + .owner = THIS_MODULE, + .open = fmc_sdb_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +int fmc_debug_init(struct fmc_device *fmc) +{ + fmc->dbg_dir = debugfs_create_dir(dev_name(&fmc->dev), NULL); + if (IS_ERR_OR_NULL(fmc->dbg_dir)) { + pr_err("FMC: Cannot create debugfs\n"); + return PTR_ERR(fmc->dbg_dir); + } + + fmc->dbg_sdb_dump = debugfs_create_file(FMC_DBG_SDB_DUMP, 0444, + fmc->dbg_dir, fmc, + &fmc_dbgfs_sdb_dump); + if (IS_ERR_OR_NULL(fmc->dbg_sdb_dump)) + pr_err("FMC: Cannot create debugfs file %s\n", + FMC_DBG_SDB_DUMP); + + return 0; +} + +void fmc_debug_exit(struct fmc_device *fmc) +{ + if (fmc->dbg_dir) + debugfs_remove_recursive(fmc->dbg_dir); +} diff --git a/drivers/fmc/fmc-dump.c b/drivers/fmc/fmc-dump.c index c91afd6388f6..cd1df475b254 100644 --- a/drivers/fmc/fmc-dump.c +++ b/drivers/fmc/fmc-dump.c @@ -15,8 +15,6 @@ static int fmc_must_dump_eeprom; module_param_named(dump_eeprom, fmc_must_dump_eeprom, int, 0644); -static int fmc_must_dump_sdb; -module_param_named(dump_sdb, fmc_must_dump_sdb, int, 0644); #define LINELEN 16 @@ -59,42 +57,3 @@ void fmc_dump_eeprom(const struct fmc_device *fmc) for (i = 0; i < fmc->eeprom_len; i += LINELEN, line += LINELEN) prev = dump_line(i, line, prev); } - -void fmc_dump_sdb(const struct fmc_device *fmc) -{ - const uint8_t *line, *prev; - int i, len; - - if (!fmc->sdb) - return; - if (!fmc_must_dump_sdb) - return; - - /* If the argument is not-zero, do simple dump (== show) */ - if (fmc_must_dump_sdb > 0) - fmc_show_sdb_tree(fmc); - - if (fmc_must_dump_sdb == 1) - return; - - /* If bigger than 1, dump it seriously, to help debugging */ - - /* - * Here we should really use libsdbfs (which is designed to - * work in kernel space as well) , but it doesn't support - * directories yet, and it requires better intergration (it - * should be used instead of fmc-specific code). - * - * So, lazily, just dump the top-level array - */ - pr_info("FMC: %s (%s), slot %i, device %s\n", dev_name(fmc->hwdev), - fmc->carrier_name, fmc->slot_id, dev_name(&fmc->dev)); - pr_info("FMC: poor dump of sdb first level:\n"); - - len = fmc->sdb->len * sizeof(union sdb_record); - line = (void *)fmc->sdb->record; - prev = NULL; - for (i = 0; i < len; i += LINELEN, line += LINELEN) - prev = dump_line(i, line, prev); - return; -} diff --git a/drivers/fmc/fmc-match.c b/drivers/fmc/fmc-match.c index 104a5efc2207..a0956d1f7550 100644 --- a/drivers/fmc/fmc-match.c +++ b/drivers/fmc/fmc-match.c @@ -63,7 +63,7 @@ int fmc_fill_id_info(struct fmc_device *fmc) if (!fmc->eeprom) return -ENOMEM; allocated = 1; - ret = fmc->op->read_ee(fmc, 0, fmc->eeprom, fmc->eeprom_len); + ret = fmc_read_ee(fmc, 0, fmc->eeprom, fmc->eeprom_len); if (ret < 0) goto out; } diff --git a/drivers/fmc/fmc-private.h b/drivers/fmc/fmc-private.h new file mode 100644 index 000000000000..1e5136643bdc --- /dev/null +++ b/drivers/fmc/fmc-private.h @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2015 CERN (www.cern.ch) + * Author: Federico Vaga <federico.vaga@cern.ch> + * + * Released according to the GNU GPL, version 2 or any later version. + */ + +extern int fmc_debug_init(struct fmc_device *fmc); +extern void fmc_debug_exit(struct fmc_device *fmc); diff --git a/drivers/fmc/fmc-sdb.c b/drivers/fmc/fmc-sdb.c index 4603fdb74465..ffdc1762b580 100644 --- a/drivers/fmc/fmc-sdb.c +++ b/drivers/fmc/fmc-sdb.c @@ -127,12 +127,12 @@ int fmc_free_sdb_tree(struct fmc_device *fmc) EXPORT_SYMBOL(fmc_free_sdb_tree); /* This helper calls reprogram and inizialized sdb as well */ -int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw, - int sdb_entry) +int fmc_reprogram_raw(struct fmc_device *fmc, struct fmc_driver *d, + void *gw, unsigned long len, int sdb_entry) { int ret; - ret = fmc->op->reprogram(fmc, d, gw); + ret = fmc->op->reprogram_raw(fmc, d, gw, len); if (ret < 0) return ret; if (sdb_entry < 0) @@ -145,108 +145,39 @@ int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw, sdb_entry); return -ENODEV; } - fmc_dump_sdb(fmc); - return 0; -} -EXPORT_SYMBOL(fmc_reprogram); - -static char *__strip_trailing_space(char *buf, char *str, int len) -{ - int i = len - 1; - memcpy(buf, str, len); - while(i >= 0 && buf[i] == ' ') - buf[i--] = '\0'; - return buf; + return 0; } +EXPORT_SYMBOL(fmc_reprogram_raw); -#define __sdb_string(buf, field) ({ \ - BUILD_BUG_ON(sizeof(buf) < sizeof(field)); \ - __strip_trailing_space(buf, (void *)(field), sizeof(field)); \ - }) - -static void __fmc_show_sdb_tree(const struct fmc_device *fmc, - const struct sdb_array *arr) +/* This helper calls reprogram and inizialized sdb as well */ +int fmc_reprogram(struct fmc_device *fmc, struct fmc_driver *d, char *gw, + int sdb_entry) { - unsigned long base = arr->baseaddr; - int i, j, n = arr->len, level = arr->level; - char buf[64]; - - for (i = 0; i < n; i++) { - union sdb_record *r; - struct sdb_product *p; - struct sdb_component *c; - r = &arr->record[i]; - c = &r->dev.sdb_component; - p = &c->product; + int ret; - dev_info(&fmc->dev, "SDB: "); + ret = fmc->op->reprogram(fmc, d, gw); + if (ret < 0) + return ret; + if (sdb_entry < 0) + return ret; - for (j = 0; j < level; j++) - printk(KERN_CONT " "); - switch (r->empty.record_type) { - case sdb_type_interconnect: - printk(KERN_CONT "%08llx:%08x %.19s\n", - __be64_to_cpu(p->vendor_id), - __be32_to_cpu(p->device_id), - p->name); - break; - case sdb_type_device: - printk(KERN_CONT "%08llx:%08x %.19s (%08llx-%08llx)\n", - __be64_to_cpu(p->vendor_id), - __be32_to_cpu(p->device_id), - p->name, - __be64_to_cpu(c->addr_first) + base, - __be64_to_cpu(c->addr_last) + base); - break; - case sdb_type_bridge: - printk(KERN_CONT "%08llx:%08x %.19s (bridge: %08llx)\n", - __be64_to_cpu(p->vendor_id), - __be32_to_cpu(p->device_id), - p->name, - __be64_to_cpu(c->addr_first) + base); - if (IS_ERR(arr->subtree[i])) { - dev_info(&fmc->dev, "SDB: (bridge error %li)\n", - PTR_ERR(arr->subtree[i])); - break; - } - __fmc_show_sdb_tree(fmc, arr->subtree[i]); - break; - case sdb_type_integration: - printk(KERN_CONT "integration\n"); - break; - case sdb_type_repo_url: - printk(KERN_CONT "Synthesis repository: %s\n", - __sdb_string(buf, r->repo_url.repo_url)); - break; - case sdb_type_synthesis: - printk(KERN_CONT "Bitstream '%s' ", - __sdb_string(buf, r->synthesis.syn_name)); - printk(KERN_CONT "synthesized %08x by %s ", - __be32_to_cpu(r->synthesis.date), - __sdb_string(buf, r->synthesis.user_name)); - printk(KERN_CONT "(%s version %x), ", - __sdb_string(buf, r->synthesis.tool_name), - __be32_to_cpu(r->synthesis.tool_version)); - printk(KERN_CONT "commit %pm\n", - r->synthesis.commit_id); - break; - case sdb_type_empty: - printk(KERN_CONT "empty\n"); - break; - default: - printk(KERN_CONT "UNKNOWN TYPE 0x%02x\n", - r->empty.record_type); - break; - } + /* We are required to find SDB at a given offset */ + ret = fmc_scan_sdb_tree(fmc, sdb_entry); + if (ret < 0) { + dev_err(&fmc->dev, "Can't find SDB at address 0x%x\n", + sdb_entry); + return -ENODEV; } + + return 0; } +EXPORT_SYMBOL(fmc_reprogram); void fmc_show_sdb_tree(const struct fmc_device *fmc) { - if (!fmc->sdb) - return; - __fmc_show_sdb_tree(fmc, fmc->sdb); + pr_err("%s: not supported anymore, use debugfs to dump SDB\n", + __func__); } EXPORT_SYMBOL(fmc_show_sdb_tree); diff --git a/drivers/fmc/fmc-trivial.c b/drivers/fmc/fmc-trivial.c index 6c590f54c79d..8defdee3e3a3 100644 --- a/drivers/fmc/fmc-trivial.c +++ b/drivers/fmc/fmc-trivial.c @@ -24,7 +24,7 @@ static irqreturn_t t_handler(int irq, void *dev_id) { struct fmc_device *fmc = dev_id; - fmc->op->irq_ack(fmc); + fmc_irq_ack(fmc); dev_info(&fmc->dev, "received irq %i\n", irq); return IRQ_HANDLED; } @@ -46,25 +46,21 @@ static int t_probe(struct fmc_device *fmc) int ret; int index = 0; - if (fmc->op->validate) - index = fmc->op->validate(fmc, &t_drv); + index = fmc_validate(fmc, &t_drv); if (index < 0) return -EINVAL; /* not our device: invalid */ - ret = fmc->op->irq_request(fmc, t_handler, "fmc-trivial", IRQF_SHARED); + ret = fmc_irq_request(fmc, t_handler, "fmc-trivial", IRQF_SHARED); if (ret < 0) return ret; /* ignore error code of call below, we really don't care */ - fmc->op->gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio)); + fmc_gpio_config(fmc, t_gpio, ARRAY_SIZE(t_gpio)); - /* Reprogram, if asked to. ESRCH == no filename specified */ - ret = -ESRCH; - if (fmc->op->reprogram) - ret = fmc->op->reprogram(fmc, &t_drv, ""); - if (ret == -ESRCH) + ret = fmc_reprogram(fmc, &t_drv, "", 0); + if (ret == -EPERM) /* programming not supported */ ret = 0; if (ret < 0) - fmc->op->irq_free(fmc); + fmc_irq_free(fmc); /* FIXME: reprogram LM32 too */ return ret; @@ -72,7 +68,7 @@ static int t_probe(struct fmc_device *fmc) static int t_remove(struct fmc_device *fmc) { - fmc->op->irq_free(fmc); + fmc_irq_free(fmc); return 0; } diff --git a/drivers/fmc/fmc-write-eeprom.c b/drivers/fmc/fmc-write-eeprom.c index 9bb2cbd5c9f2..3eb81bb1f1fc 100644 --- a/drivers/fmc/fmc-write-eeprom.c +++ b/drivers/fmc/fmc-write-eeprom.c @@ -50,7 +50,7 @@ static int fwe_run_tlv(struct fmc_device *fmc, const struct firmware *fw, if (write) { dev_info(&fmc->dev, "write %i bytes at 0x%04x\n", thislen, thisaddr); - err = fmc->op->write_ee(fmc, thisaddr, p + 5, thislen); + err = fmc_write_ee(fmc, thisaddr, p + 5, thislen); } if (err < 0) { dev_err(&fmc->dev, "write failure @0x%04x\n", @@ -70,7 +70,7 @@ static int fwe_run_bin(struct fmc_device *fmc, const struct firmware *fw) int ret; dev_info(&fmc->dev, "programming %zi bytes\n", fw->size); - ret = fmc->op->write_ee(fmc, 0, (void *)fw->data, fw->size); + ret = fmc_write_ee(fmc, 0, (void *)fw->data, fw->size); if (ret < 0) { dev_info(&fmc->dev, "write_eeprom: error %i\n", ret); return ret; @@ -115,8 +115,8 @@ static int fwe_probe(struct fmc_device *fmc) KBUILD_MODNAME); return -ENODEV; } - if (fmc->op->validate) - index = fmc->op->validate(fmc, &fwe_drv); + + index = fmc_validate(fmc, &fwe_drv); if (index < 0) { pr_err("%s: refusing device \"%s\"\n", KBUILD_MODNAME, dev_name(dev)); diff --git a/drivers/fmc/fru-parse.c b/drivers/fmc/fru-parse.c index cb46263c5da2..eb21480d399f 100644 --- a/drivers/fmc/fru-parse.c +++ b/drivers/fmc/fru-parse.c @@ -31,12 +31,11 @@ static char *__fru_alloc_get_tl(struct fru_common_header *header, int nr) { struct fru_type_length *tl; char *res; - int len; tl = __fru_get_board_tl(header, nr); if (!tl) return NULL; - len = fru_strlen(tl); + res = fru_alloc(fru_strlen(tl) + 1); if (!res) return NULL; diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index 161ba9dccede..ad5448f718b3 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -2,9 +2,7 @@ # FPGA framework configuration # -menu "FPGA Configuration Support" - -config FPGA +menuconfig FPGA tristate "FPGA Configuration Framework" help Say Y here if you want support for configuring FPGAs from the @@ -26,6 +24,20 @@ config FPGA_MGR_ICE40_SPI help FPGA manager driver support for Lattice iCE40 FPGAs over SPI. +config FPGA_MGR_ALTERA_CVP + tristate "Altera Arria-V/Cyclone-V/Stratix-V CvP FPGA Manager" + depends on PCI + help + FPGA manager driver support for Arria-V, Cyclone-V, Stratix-V + and Arria 10 Altera FPGAs using the CvP interface over PCIe. + +config FPGA_MGR_ALTERA_PS_SPI + tristate "Altera FPGA Passive Serial over SPI" + depends on SPI + help + FPGA manager driver support for Altera Arria/Cyclone/Stratix + using the passive serial interface over SPI. + config FPGA_MGR_SOCFPGA tristate "Altera SOCFPGA FPGA Manager" depends on ARCH_SOCFPGA || COMPILE_TEST @@ -106,5 +118,3 @@ config XILINX_PR_DECOUPLER being reprogrammed during partial reconfig. endif # FPGA - -endmenu diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index 2a4f0218145c..e09895f0525b 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -6,6 +6,8 @@ obj-$(CONFIG_FPGA) += fpga-mgr.o # FPGA Manager Drivers +obj-$(CONFIG_FPGA_MGR_ALTERA_CVP) += altera-cvp.o +obj-$(CONFIG_FPGA_MGR_ALTERA_PS_SPI) += altera-ps-spi.o obj-$(CONFIG_FPGA_MGR_ICE40_SPI) += ice40-spi.o obj-$(CONFIG_FPGA_MGR_SOCFPGA) += socfpga.o obj-$(CONFIG_FPGA_MGR_SOCFPGA_A10) += socfpga-a10.o diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c new file mode 100644 index 000000000000..08629ee69d11 --- /dev/null +++ b/drivers/fpga/altera-cvp.c @@ -0,0 +1,500 @@ +/* + * FPGA Manager Driver for Altera Arria/Cyclone/Stratix CvP + * + * Copyright (C) 2017 DENX Software Engineering + * + * Anatolij Gustschin <agust@denx.de> + * + * 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 of the License. + * + * 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. + * + * Manage Altera FPGA firmware using PCIe CvP. + * Firmware must be in binary "rbf" format. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/fpga/fpga-mgr.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/sizes.h> + +#define CVP_BAR 0 /* BAR used for data transfer in memory mode */ +#define CVP_DUMMY_WR 244 /* dummy writes to clear CvP state machine */ +#define TIMEOUT_US 2000 /* CVP STATUS timeout for USERMODE polling */ + +/* Vendor Specific Extended Capability Registers */ +#define VSE_PCIE_EXT_CAP_ID 0x200 +#define VSE_PCIE_EXT_CAP_ID_VAL 0x000b /* 16bit */ + +#define VSE_CVP_STATUS 0x21c /* 32bit */ +#define VSE_CVP_STATUS_CFG_RDY BIT(18) /* CVP_CONFIG_READY */ +#define VSE_CVP_STATUS_CFG_ERR BIT(19) /* CVP_CONFIG_ERROR */ +#define VSE_CVP_STATUS_CVP_EN BIT(20) /* ctrl block is enabling CVP */ +#define VSE_CVP_STATUS_USERMODE BIT(21) /* USERMODE */ +#define VSE_CVP_STATUS_CFG_DONE BIT(23) /* CVP_CONFIG_DONE */ +#define VSE_CVP_STATUS_PLD_CLK_IN_USE BIT(24) /* PLD_CLK_IN_USE */ + +#define VSE_CVP_MODE_CTRL 0x220 /* 32bit */ +#define VSE_CVP_MODE_CTRL_CVP_MODE BIT(0) /* CVP (1) or normal mode (0) */ +#define VSE_CVP_MODE_CTRL_HIP_CLK_SEL BIT(1) /* PMA (1) or fabric clock (0) */ +#define VSE_CVP_MODE_CTRL_NUMCLKS_OFF 8 /* NUMCLKS bits offset */ +#define VSE_CVP_MODE_CTRL_NUMCLKS_MASK GENMASK(15, 8) + +#define VSE_CVP_DATA 0x228 /* 32bit */ +#define VSE_CVP_PROG_CTRL 0x22c /* 32bit */ +#define VSE_CVP_PROG_CTRL_CONFIG BIT(0) +#define VSE_CVP_PROG_CTRL_START_XFER BIT(1) + +#define VSE_UNCOR_ERR_STATUS 0x234 /* 32bit */ +#define VSE_UNCOR_ERR_CVP_CFG_ERR BIT(5) /* CVP_CONFIG_ERROR_LATCHED */ + +#define DRV_NAME "altera-cvp" +#define ALTERA_CVP_MGR_NAME "Altera CvP FPGA Manager" + +/* Optional CvP config error status check for debugging */ +static bool altera_cvp_chkcfg; + +struct altera_cvp_conf { + struct fpga_manager *mgr; + struct pci_dev *pci_dev; + void __iomem *map; + void (*write_data)(struct altera_cvp_conf *, u32); + char mgr_name[64]; + u8 numclks; +}; + +static enum fpga_mgr_states altera_cvp_state(struct fpga_manager *mgr) +{ + struct altera_cvp_conf *conf = mgr->priv; + u32 status; + + pci_read_config_dword(conf->pci_dev, VSE_CVP_STATUS, &status); + + if (status & VSE_CVP_STATUS_CFG_DONE) + return FPGA_MGR_STATE_OPERATING; + + if (status & VSE_CVP_STATUS_CVP_EN) + return FPGA_MGR_STATE_POWER_UP; + + return FPGA_MGR_STATE_UNKNOWN; +} + +static void altera_cvp_write_data_iomem(struct altera_cvp_conf *conf, u32 val) +{ + writel(val, conf->map); +} + +static void altera_cvp_write_data_config(struct altera_cvp_conf *conf, u32 val) +{ + pci_write_config_dword(conf->pci_dev, VSE_CVP_DATA, val); +} + +/* switches between CvP clock and internal clock */ +static void altera_cvp_dummy_write(struct altera_cvp_conf *conf) +{ + unsigned int i; + u32 val; + + /* set 1 CVP clock cycle for every CVP Data Register Write */ + pci_read_config_dword(conf->pci_dev, VSE_CVP_MODE_CTRL, &val); + val &= ~VSE_CVP_MODE_CTRL_NUMCLKS_MASK; + val |= 1 << VSE_CVP_MODE_CTRL_NUMCLKS_OFF; + pci_write_config_dword(conf->pci_dev, VSE_CVP_MODE_CTRL, val); + + for (i = 0; i < CVP_DUMMY_WR; i++) + conf->write_data(conf, 0); /* dummy data, could be any value */ +} + +static int altera_cvp_wait_status(struct altera_cvp_conf *conf, u32 status_mask, + u32 status_val, int timeout_us) +{ + unsigned int retries; + u32 val; + + retries = timeout_us / 10; + if (timeout_us % 10) + retries++; + + do { + pci_read_config_dword(conf->pci_dev, VSE_CVP_STATUS, &val); + if ((val & status_mask) == status_val) + return 0; + + /* use small usleep value to re-check and break early */ + usleep_range(10, 11); + } while (--retries); + + return -ETIMEDOUT; +} + +static int altera_cvp_teardown(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + struct altera_cvp_conf *conf = mgr->priv; + struct pci_dev *pdev = conf->pci_dev; + int ret; + u32 val; + + /* STEP 12 - reset START_XFER bit */ + pci_read_config_dword(pdev, VSE_CVP_PROG_CTRL, &val); + val &= ~VSE_CVP_PROG_CTRL_START_XFER; + pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val); + + /* STEP 13 - reset CVP_CONFIG bit */ + val &= ~VSE_CVP_PROG_CTRL_CONFIG; + pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val); + + /* + * STEP 14 + * - set CVP_NUMCLKS to 1 and then issue CVP_DUMMY_WR dummy + * writes to the HIP + */ + altera_cvp_dummy_write(conf); /* from CVP clock to internal clock */ + + /* STEP 15 - poll CVP_CONFIG_READY bit for 0 with 10us timeout */ + ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, 0, 10); + if (ret) + dev_err(&mgr->dev, "CFG_RDY == 0 timeout\n"); + + return ret; +} + +static int altera_cvp_write_init(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t count) +{ + struct altera_cvp_conf *conf = mgr->priv; + struct pci_dev *pdev = conf->pci_dev; + u32 iflags, val; + int ret; + + iflags = info ? info->flags : 0; + + if (iflags & FPGA_MGR_PARTIAL_RECONFIG) { + dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); + return -EINVAL; + } + + /* Determine allowed clock to data ratio */ + if (iflags & FPGA_MGR_COMPRESSED_BITSTREAM) + conf->numclks = 8; /* ratio for all compressed images */ + else if (iflags & FPGA_MGR_ENCRYPTED_BITSTREAM) + conf->numclks = 4; /* for uncompressed and encrypted images */ + else + conf->numclks = 1; /* for uncompressed and unencrypted images */ + + /* STEP 1 - read CVP status and check CVP_EN flag */ + pci_read_config_dword(pdev, VSE_CVP_STATUS, &val); + if (!(val & VSE_CVP_STATUS_CVP_EN)) { + dev_err(&mgr->dev, "CVP mode off: 0x%04x\n", val); + return -ENODEV; + } + + if (val & VSE_CVP_STATUS_CFG_RDY) { + dev_warn(&mgr->dev, "CvP already started, teardown first\n"); + ret = altera_cvp_teardown(mgr, info); + if (ret) + return ret; + } + + /* + * STEP 2 + * - set HIP_CLK_SEL and CVP_MODE (must be set in the order mentioned) + */ + /* switch from fabric to PMA clock */ + pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val); + val |= VSE_CVP_MODE_CTRL_HIP_CLK_SEL; + pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val); + + /* set CVP mode */ + pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val); + val |= VSE_CVP_MODE_CTRL_CVP_MODE; + pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val); + + /* + * STEP 3 + * - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP + */ + altera_cvp_dummy_write(conf); + + /* STEP 4 - set CVP_CONFIG bit */ + pci_read_config_dword(pdev, VSE_CVP_PROG_CTRL, &val); + /* request control block to begin transfer using CVP */ + val |= VSE_CVP_PROG_CTRL_CONFIG; + pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val); + + /* STEP 5 - poll CVP_CONFIG READY for 1 with 10us timeout */ + ret = altera_cvp_wait_status(conf, VSE_CVP_STATUS_CFG_RDY, + VSE_CVP_STATUS_CFG_RDY, 10); + if (ret) { + dev_warn(&mgr->dev, "CFG_RDY == 1 timeout\n"); + return ret; + } + + /* + * STEP 6 + * - set CVP_NUMCLKS to 1 and issue CVP_DUMMY_WR dummy writes to the HIP + */ + altera_cvp_dummy_write(conf); + + /* STEP 7 - set START_XFER */ + pci_read_config_dword(pdev, VSE_CVP_PROG_CTRL, &val); + val |= VSE_CVP_PROG_CTRL_START_XFER; + pci_write_config_dword(pdev, VSE_CVP_PROG_CTRL, val); + + /* STEP 8 - start transfer (set CVP_NUMCLKS for bitstream) */ + pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val); + val &= ~VSE_CVP_MODE_CTRL_NUMCLKS_MASK; + val |= conf->numclks << VSE_CVP_MODE_CTRL_NUMCLKS_OFF; + pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val); + + return 0; +} + +static inline int altera_cvp_chk_error(struct fpga_manager *mgr, size_t bytes) +{ + struct altera_cvp_conf *conf = mgr->priv; + u32 val; + + /* STEP 10 (optional) - check CVP_CONFIG_ERROR flag */ + pci_read_config_dword(conf->pci_dev, VSE_CVP_STATUS, &val); + if (val & VSE_CVP_STATUS_CFG_ERR) { + dev_err(&mgr->dev, "CVP_CONFIG_ERROR after %zu bytes!\n", + bytes); + return -EPROTO; + } + return 0; +} + +static int altera_cvp_write(struct fpga_manager *mgr, const char *buf, + size_t count) +{ + struct altera_cvp_conf *conf = mgr->priv; + const u32 *data; + size_t done, remaining; + int status = 0; + u32 mask; + + /* STEP 9 - write 32-bit data from RBF file to CVP data register */ + data = (u32 *)buf; + remaining = count; + done = 0; + + while (remaining >= 4) { + conf->write_data(conf, *data++); + done += 4; + remaining -= 4; + + /* + * STEP 10 (optional) and STEP 11 + * - check error flag + * - loop until data transfer completed + * Config images can be huge (more than 40 MiB), so + * only check after a new 4k data block has been written. + * This reduces the number of checks and speeds up the + * configuration process. + */ + if (altera_cvp_chkcfg && !(done % SZ_4K)) { + status = altera_cvp_chk_error(mgr, done); + if (status < 0) + return status; + } + } + + /* write up to 3 trailing bytes, if any */ + mask = BIT(remaining * 8) - 1; + if (mask) + conf->write_data(conf, *data & mask); + + if (altera_cvp_chkcfg) + status = altera_cvp_chk_error(mgr, count); + + return status; +} + +static int altera_cvp_write_complete(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + struct altera_cvp_conf *conf = mgr->priv; + struct pci_dev *pdev = conf->pci_dev; + int ret; + u32 mask; + u32 val; + + ret = altera_cvp_teardown(mgr, info); + if (ret) + return ret; + + /* STEP 16 - check CVP_CONFIG_ERROR_LATCHED bit */ + pci_read_config_dword(pdev, VSE_UNCOR_ERR_STATUS, &val); + if (val & VSE_UNCOR_ERR_CVP_CFG_ERR) { + dev_err(&mgr->dev, "detected CVP_CONFIG_ERROR_LATCHED!\n"); + return -EPROTO; + } + + /* STEP 17 - reset CVP_MODE and HIP_CLK_SEL bit */ + pci_read_config_dword(pdev, VSE_CVP_MODE_CTRL, &val); + val &= ~VSE_CVP_MODE_CTRL_HIP_CLK_SEL; + val &= ~VSE_CVP_MODE_CTRL_CVP_MODE; + pci_write_config_dword(pdev, VSE_CVP_MODE_CTRL, val); + + /* STEP 18 - poll PLD_CLK_IN_USE and USER_MODE bits */ + mask = VSE_CVP_STATUS_PLD_CLK_IN_USE | VSE_CVP_STATUS_USERMODE; + ret = altera_cvp_wait_status(conf, mask, mask, TIMEOUT_US); + if (ret) + dev_err(&mgr->dev, "PLD_CLK_IN_USE|USERMODE timeout\n"); + + return ret; +} + +static const struct fpga_manager_ops altera_cvp_ops = { + .state = altera_cvp_state, + .write_init = altera_cvp_write_init, + .write = altera_cvp_write, + .write_complete = altera_cvp_write_complete, +}; + +static ssize_t show_chkcfg(struct device_driver *dev, char *buf) +{ + return snprintf(buf, 3, "%d\n", altera_cvp_chkcfg); +} + +static ssize_t store_chkcfg(struct device_driver *drv, const char *buf, + size_t count) +{ + int ret; + + ret = kstrtobool(buf, &altera_cvp_chkcfg); + if (ret) + return ret; + + return count; +} + +static DRIVER_ATTR(chkcfg, 0600, show_chkcfg, store_chkcfg); + +static int altera_cvp_probe(struct pci_dev *pdev, + const struct pci_device_id *dev_id); +static void altera_cvp_remove(struct pci_dev *pdev); + +#define PCI_VENDOR_ID_ALTERA 0x1172 + +static struct pci_device_id altera_cvp_id_tbl[] = { + { PCI_VDEVICE(ALTERA, PCI_ANY_ID) }, + { } +}; +MODULE_DEVICE_TABLE(pci, altera_cvp_id_tbl); + +static struct pci_driver altera_cvp_driver = { + .name = DRV_NAME, + .id_table = altera_cvp_id_tbl, + .probe = altera_cvp_probe, + .remove = altera_cvp_remove, +}; + +static int altera_cvp_probe(struct pci_dev *pdev, + const struct pci_device_id *dev_id) +{ + struct altera_cvp_conf *conf; + u16 cmd, val; + int ret; + + /* + * First check if this is the expected FPGA device. PCI config + * space access works without enabling the PCI device, memory + * space access is enabled further down. + */ + pci_read_config_word(pdev, VSE_PCIE_EXT_CAP_ID, &val); + if (val != VSE_PCIE_EXT_CAP_ID_VAL) { + dev_err(&pdev->dev, "Wrong EXT_CAP_ID value 0x%x\n", val); + return -ENODEV; + } + + conf = devm_kzalloc(&pdev->dev, sizeof(*conf), GFP_KERNEL); + if (!conf) + return -ENOMEM; + + /* + * Enable memory BAR access. We cannot use pci_enable_device() here + * because it will make the driver unusable with FPGA devices that + * have additional big IOMEM resources (e.g. 4GiB BARs) on 32-bit + * platform. Such BARs will not have an assigned address range and + * pci_enable_device() will fail, complaining about not claimed BAR, + * even if the concerned BAR is not needed for FPGA configuration + * at all. Thus, enable the device via PCI config space command. + */ + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MEMORY)) { + cmd |= PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + } + + ret = pci_request_region(pdev, CVP_BAR, "CVP"); + if (ret) { + dev_err(&pdev->dev, "Requesting CVP BAR region failed\n"); + goto err_disable; + } + + conf->pci_dev = pdev; + conf->write_data = altera_cvp_write_data_iomem; + + conf->map = pci_iomap(pdev, CVP_BAR, 0); + if (!conf->map) { + dev_warn(&pdev->dev, "Mapping CVP BAR failed\n"); + conf->write_data = altera_cvp_write_data_config; + } + + snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s", + ALTERA_CVP_MGR_NAME, pci_name(pdev)); + + ret = fpga_mgr_register(&pdev->dev, conf->mgr_name, + &altera_cvp_ops, conf); + if (ret) + goto err_unmap; + + ret = driver_create_file(&altera_cvp_driver.driver, + &driver_attr_chkcfg); + if (ret) { + dev_err(&pdev->dev, "Can't create sysfs chkcfg file\n"); + fpga_mgr_unregister(&pdev->dev); + goto err_unmap; + } + + return 0; + +err_unmap: + pci_iounmap(pdev, conf->map); + pci_release_region(pdev, CVP_BAR); +err_disable: + cmd &= ~PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, cmd); + return ret; +} + +static void altera_cvp_remove(struct pci_dev *pdev) +{ + struct fpga_manager *mgr = pci_get_drvdata(pdev); + struct altera_cvp_conf *conf = mgr->priv; + u16 cmd; + + driver_remove_file(&altera_cvp_driver.driver, &driver_attr_chkcfg); + fpga_mgr_unregister(&pdev->dev); + pci_iounmap(pdev, conf->map); + pci_release_region(pdev, CVP_BAR); + pci_read_config_word(pdev, PCI_COMMAND, &cmd); + cmd &= ~PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, cmd); +} + +module_pci_driver(altera_cvp_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); +MODULE_DESCRIPTION("Module to load Altera FPGA over CvP"); diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c index 3066b805f2d0..406d2f10741f 100644 --- a/drivers/fpga/altera-hps2fpga.c +++ b/drivers/fpga/altera-hps2fpga.c @@ -66,7 +66,7 @@ static int alt_hps2fpga_enable_show(struct fpga_bridge *bridge) /* The L3 REMAP register is write only, so keep a cached value. */ static unsigned int l3_remap_shadow; -static spinlock_t l3_remap_lock; +static DEFINE_SPINLOCK(l3_remap_lock); static int _alt_hps2fpga_enable_set(struct altera_hps2fpga_data *priv, bool enable) @@ -143,9 +143,15 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) int ret; of_id = of_match_device(altera_fpga_of_match, dev); + if (!of_id) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + priv = (struct altera_hps2fpga_data *)of_id->data; - priv->bridge_reset = of_reset_control_get_by_index(dev->of_node, 0); + priv->bridge_reset = of_reset_control_get_exclusive_by_index(dev->of_node, + 0); if (IS_ERR(priv->bridge_reset)) { dev_err(dev, "Could not get %s reset control\n", priv->name); return PTR_ERR(priv->bridge_reset); @@ -171,8 +177,6 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) return -EBUSY; } - spin_lock_init(&l3_remap_lock); - if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) { if (enable > 1) { dev_warn(dev, "invalid bridge-enable %u > 1\n", enable); diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c new file mode 100644 index 000000000000..14f14efdf0d5 --- /dev/null +++ b/drivers/fpga/altera-ps-spi.c @@ -0,0 +1,308 @@ +/* + * Altera Passive Serial SPI Driver + * + * Copyright (c) 2017 United Western Technologies, Corporation + * + * Joshua Clayton <stillcompiling@gmail.com> + * + * 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. + * + * Manage Altera FPGA firmware that is loaded over SPI using the passive + * serial configuration method. + * Firmware must be in binary "rbf" format. + * Works on Arria 10, Cyclone V and Stratix V. Should work on Cyclone series. + * May work on other Altera FPGAs. + */ + +#include <linux/bitrev.h> +#include <linux/delay.h> +#include <linux/fpga/fpga-mgr.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/of_device.h> +#include <linux/spi/spi.h> +#include <linux/sizes.h> + +enum altera_ps_devtype { + CYCLONE5, + ARRIA10, +}; + +struct altera_ps_data { + enum altera_ps_devtype devtype; + int status_wait_min_us; + int status_wait_max_us; + int t_cfg_us; + int t_st2ck_us; +}; + +struct altera_ps_conf { + struct gpio_desc *config; + struct gpio_desc *confd; + struct gpio_desc *status; + struct spi_device *spi; + const struct altera_ps_data *data; + u32 info_flags; + char mgr_name[64]; +}; + +/* | Arria 10 | Cyclone5 | Stratix5 | + * t_CF2ST0 | [; 600] | [; 600] | [; 600] |ns + * t_CFG | [2;] | [2;] | [2;] |µs + * t_STATUS | [268; 3000] | [268; 1506] | [268; 1506] |µs + * t_CF2ST1 | [; 3000] | [; 1506] | [; 1506] |µs + * t_CF2CK | [3010;] | [1506;] | [1506;] |µs + * t_ST2CK | [10;] | [2;] | [2;] |µs + * t_CD2UM | [175; 830] | [175; 437] | [175; 437] |µs + */ +static struct altera_ps_data c5_data = { + /* these values for Cyclone5 are compatible with Stratix5 */ + .devtype = CYCLONE5, + .status_wait_min_us = 268, + .status_wait_max_us = 1506, + .t_cfg_us = 2, + .t_st2ck_us = 2, +}; + +static struct altera_ps_data a10_data = { + .devtype = ARRIA10, + .status_wait_min_us = 268, /* min(t_STATUS) */ + .status_wait_max_us = 3000, /* max(t_CF2ST1) */ + .t_cfg_us = 2, /* max { min(t_CFG), max(tCF2ST0) } */ + .t_st2ck_us = 10, /* min(t_ST2CK) */ +}; + +static const struct of_device_id of_ef_match[] = { + { .compatible = "altr,fpga-passive-serial", .data = &c5_data }, + { .compatible = "altr,fpga-arria10-passive-serial", .data = &a10_data }, + {} +}; +MODULE_DEVICE_TABLE(of, of_ef_match); + +static enum fpga_mgr_states altera_ps_state(struct fpga_manager *mgr) +{ + struct altera_ps_conf *conf = mgr->priv; + + if (gpiod_get_value_cansleep(conf->status)) + return FPGA_MGR_STATE_RESET; + + return FPGA_MGR_STATE_UNKNOWN; +} + +static inline void altera_ps_delay(int delay_us) +{ + if (delay_us > 10) + usleep_range(delay_us, delay_us + 5); + else + udelay(delay_us); +} + +static int altera_ps_write_init(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t count) +{ + struct altera_ps_conf *conf = mgr->priv; + int min, max, waits; + int i; + + conf->info_flags = info->flags; + + if (info->flags & FPGA_MGR_PARTIAL_RECONFIG) { + dev_err(&mgr->dev, "Partial reconfiguration not supported.\n"); + return -EINVAL; + } + + gpiod_set_value_cansleep(conf->config, 1); + + /* wait min reset pulse time */ + altera_ps_delay(conf->data->t_cfg_us); + + if (!gpiod_get_value_cansleep(conf->status)) { + dev_err(&mgr->dev, "Status pin failed to show a reset\n"); + return -EIO; + } + + gpiod_set_value_cansleep(conf->config, 0); + + min = conf->data->status_wait_min_us; + max = conf->data->status_wait_max_us; + waits = max / min; + if (max % min) + waits++; + + /* wait for max { max(t_STATUS), max(t_CF2ST1) } */ + for (i = 0; i < waits; i++) { + usleep_range(min, min + 10); + if (!gpiod_get_value_cansleep(conf->status)) { + /* wait for min(t_ST2CK)*/ + altera_ps_delay(conf->data->t_st2ck_us); + return 0; + } + } + + dev_err(&mgr->dev, "Status pin not ready.\n"); + return -EIO; +} + +static void rev_buf(char *buf, size_t len) +{ + u32 *fw32 = (u32 *)buf; + size_t extra_bytes = (len & 0x03); + const u32 *fw_end = (u32 *)(buf + len - extra_bytes); + + /* set buffer to lsb first */ + while (fw32 < fw_end) { + *fw32 = bitrev8x4(*fw32); + fw32++; + } + + if (extra_bytes) { + buf = (char *)fw_end; + while (extra_bytes) { + *buf = bitrev8(*buf); + buf++; + extra_bytes--; + } + } +} + +static int altera_ps_write(struct fpga_manager *mgr, const char *buf, + size_t count) +{ + struct altera_ps_conf *conf = mgr->priv; + const char *fw_data = buf; + const char *fw_data_end = fw_data + count; + + while (fw_data < fw_data_end) { + int ret; + size_t stride = min_t(size_t, fw_data_end - fw_data, SZ_4K); + + if (!(conf->info_flags & FPGA_MGR_BITSTREAM_LSB_FIRST)) + rev_buf((char *)fw_data, stride); + + ret = spi_write(conf->spi, fw_data, stride); + if (ret) { + dev_err(&mgr->dev, "spi error in firmware write: %d\n", + ret); + return ret; + } + fw_data += stride; + } + + return 0; +} + +static int altera_ps_write_complete(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + struct altera_ps_conf *conf = mgr->priv; + const char dummy[] = {0}; + int ret; + + if (gpiod_get_value_cansleep(conf->status)) { + dev_err(&mgr->dev, "Error during configuration.\n"); + return -EIO; + } + + if (!IS_ERR(conf->confd)) { + if (!gpiod_get_raw_value_cansleep(conf->confd)) { + dev_err(&mgr->dev, "CONF_DONE is inactive!\n"); + return -EIO; + } + } + + /* + * After CONF_DONE goes high, send two additional falling edges on DCLK + * to begin initialization and enter user mode + */ + ret = spi_write(conf->spi, dummy, 1); + if (ret) { + dev_err(&mgr->dev, "spi error during end sequence: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct fpga_manager_ops altera_ps_ops = { + .state = altera_ps_state, + .write_init = altera_ps_write_init, + .write = altera_ps_write, + .write_complete = altera_ps_write_complete, +}; + +static int altera_ps_probe(struct spi_device *spi) +{ + struct altera_ps_conf *conf; + const struct of_device_id *of_id; + + conf = devm_kzalloc(&spi->dev, sizeof(*conf), GFP_KERNEL); + if (!conf) + return -ENOMEM; + + of_id = of_match_device(of_ef_match, &spi->dev); + if (!of_id) + return -ENODEV; + + conf->data = of_id->data; + conf->spi = spi; + conf->config = devm_gpiod_get(&spi->dev, "nconfig", GPIOD_OUT_HIGH); + if (IS_ERR(conf->config)) { + dev_err(&spi->dev, "Failed to get config gpio: %ld\n", + PTR_ERR(conf->config)); + return PTR_ERR(conf->config); + } + + conf->status = devm_gpiod_get(&spi->dev, "nstat", GPIOD_IN); + if (IS_ERR(conf->status)) { + dev_err(&spi->dev, "Failed to get status gpio: %ld\n", + PTR_ERR(conf->status)); + return PTR_ERR(conf->status); + } + + conf->confd = devm_gpiod_get(&spi->dev, "confd", GPIOD_IN); + if (IS_ERR(conf->confd)) { + dev_warn(&spi->dev, "Not using confd gpio: %ld\n", + PTR_ERR(conf->confd)); + } + + /* Register manager with unique name */ + snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s", + dev_driver_string(&spi->dev), dev_name(&spi->dev)); + + return fpga_mgr_register(&spi->dev, conf->mgr_name, + &altera_ps_ops, conf); +} + +static int altera_ps_remove(struct spi_device *spi) +{ + fpga_mgr_unregister(&spi->dev); + + return 0; +} + +static const struct spi_device_id altera_ps_spi_ids[] = { + {"cyclone-ps-spi", 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, altera_ps_spi_ids); + +static struct spi_driver altera_ps_driver = { + .driver = { + .name = "altera-ps-spi", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_ef_match), + }, + .id_table = altera_ps_spi_ids, + .probe = altera_ps_probe, + .remove = altera_ps_remove, +}; + +module_spi_driver(altera_ps_driver) + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Joshua Clayton <stillcompiling@gmail.com>"); +MODULE_DESCRIPTION("Module to load Altera FPGA firmware over SPI"); diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c index 3b6b2f4182a1..d9ab7c75b14f 100644 --- a/drivers/fpga/fpga-region.c +++ b/drivers/fpga/fpga-region.c @@ -319,8 +319,8 @@ static int child_regions_with_firmware(struct device_node *overlay) of_node_put(child_region); if (ret) - pr_err("firmware-name not allowed in child FPGA region: %s", - child_region->full_name); + pr_err("firmware-name not allowed in child FPGA region: %pOF", + child_region); return ret; } diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index a485864cb512..06432d84cbf8 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -532,7 +532,7 @@ static inline uint32_t fsi_smode_sid(int x) return (x & FSI_SMODE_SID_MASK) << FSI_SMODE_SID_SHIFT; } -static const uint32_t fsi_slave_smode(int id) +static uint32_t fsi_slave_smode(int id) { return FSI_SMODE_WSC | FSI_SMODE_ECRC | fsi_smode_sid(id) @@ -883,17 +883,16 @@ struct bus_type fsi_bus_type = { }; EXPORT_SYMBOL_GPL(fsi_bus_type); -static int fsi_init(void) +static int __init fsi_init(void) { return bus_register(&fsi_bus_type); } +postcore_initcall(fsi_init); static void fsi_exit(void) { bus_unregister(&fsi_bus_type); } - -module_init(fsi_init); module_exit(fsi_exit); module_param(discard_errors, int, 0664); MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f235eae04c16..461d6fc3688b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -504,6 +504,7 @@ config GPIO_XGENE_SB depends on ARCH_XGENE && OF_GPIO select GPIO_GENERIC select GPIOLIB_IRQCHIP + select IRQ_DOMAIN_HIERARCHY help This driver supports the GPIO block within the APM X-Gene Standby Domain. Say yes here to enable the GPIO functionality. diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index fb8d304cfa17..0ecd2369c2ca 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -132,7 +132,7 @@ static int gpio_exar_probe(struct platform_device *pdev) if (!p) return -ENOMEM; - ret = device_property_read_u32(&pdev->dev, "linux,first-pin", + ret = device_property_read_u32(&pdev->dev, "exar,first-pin", &first_pin); if (ret) return ret; diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c index 6313c50bb91b..a121c8f10610 100644 --- a/drivers/gpio/gpio-lp87565.c +++ b/drivers/gpio/gpio-lp87565.c @@ -26,6 +26,27 @@ struct lp87565_gpio { struct regmap *map; }; +static int lp87565_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + int ret, val; + + ret = regmap_read(gpio->map, LP87565_REG_GPIO_IN, &val); + if (ret < 0) + return ret; + + return !!(val & BIT(offset)); +} + +static void lp87565_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct lp87565_gpio *gpio = gpiochip_get_data(chip); + + regmap_update_bits(gpio->map, LP87565_REG_GPIO_OUT, + BIT(offset), value ? BIT(offset) : 0); +} + static int lp87565_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { @@ -54,30 +75,11 @@ static int lp87565_gpio_direction_output(struct gpio_chip *chip, { struct lp87565_gpio *gpio = gpiochip_get_data(chip); + lp87565_gpio_set(chip, offset, value); + return regmap_update_bits(gpio->map, LP87565_REG_GPIO_CONFIG, - BIT(offset), !value ? BIT(offset) : 0); -} - -static int lp87565_gpio_get(struct gpio_chip *chip, unsigned int offset) -{ - struct lp87565_gpio *gpio = gpiochip_get_data(chip); - int ret, val; - - ret = regmap_read(gpio->map, LP87565_REG_GPIO_IN, &val); - if (ret < 0) - return ret; - - return !!(val & BIT(offset)); -} - -static void lp87565_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) -{ - struct lp87565_gpio *gpio = gpiochip_get_data(chip); - - regmap_update_bits(gpio->map, LP87565_REG_GPIO_OUT, - BIT(offset), value ? BIT(offset) : 0); + BIT(offset), BIT(offset)); } static int lp87565_gpio_request(struct gpio_chip *gc, unsigned int offset) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index e338c3743562..45c65f805fd6 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -557,7 +557,7 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) edge_cause = mvebu_gpio_read_edge_cause(mvchip); edge_mask = mvebu_gpio_read_edge_mask(mvchip); - cause = (data_in ^ level_mask) | (edge_cause & edge_mask); + cause = (data_in & level_mask) | (edge_cause & edge_mask); for (i = 0; i < mvchip->chip.ngpio; i++) { int irq; diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 3abea3f0b307..92692251ade1 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -424,6 +424,9 @@ static int mxc_gpio_probe(struct platform_device *pdev) return PTR_ERR(port->base); port->irq_high = platform_get_irq(pdev, 1); + if (port->irq_high < 0) + port->irq_high = 0; + port->irq = platform_get_irq(pdev, 0); if (port->irq < 0) return port->irq; diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 88529d3c06c9..506c6a67c5fc 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -360,7 +360,7 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) { int port; int pin; - int unmasked = 0; + bool unmasked = false; int gpio; u32 lvl; unsigned long sta; @@ -384,8 +384,8 @@ static void tegra_gpio_irq_handler(struct irq_desc *desc) * before executing the handler so that we don't * miss edges */ - if (lvl & (0x100 << pin)) { - unmasked = 1; + if (!unmasked && lvl & (0x100 << pin)) { + unmasked = true; chained_irq_exit(chip, desc); } diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 16fe9742597b..fc80add5fedb 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -2,6 +2,7 @@ #include <linux/mutex.h> #include <linux/device.h> #include <linux/sysfs.h> +#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> #include <linux/interrupt.h> @@ -432,6 +433,11 @@ static struct attribute *gpiochip_attrs[] = { }; ATTRIBUTE_GROUPS(gpiochip); +static struct gpio_desc *gpio_to_valid_desc(int gpio) +{ + return gpio_is_valid(gpio) ? gpio_to_desc(gpio) : NULL; +} + /* * /sys/class/gpio/export ... write-only * integer N ... number of GPIO to export (full access) @@ -450,7 +456,7 @@ static ssize_t export_store(struct class *class, if (status < 0) goto done; - desc = gpio_to_desc(gpio); + desc = gpio_to_valid_desc(gpio); /* reject invalid GPIOs */ if (!desc) { pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); @@ -493,7 +499,7 @@ static ssize_t unexport_store(struct class *class, if (status < 0) goto done; - desc = gpio_to_desc(gpio); + desc = gpio_to_valid_desc(gpio); /* reject bogus commands (gpio_unexport ignores them) */ if (!desc) { pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9568708a550b..cd003b74512f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -704,24 +704,23 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) { struct lineevent_state *le = p; struct gpioevent_data ge; - int ret; + int ret, level; ge.timestamp = ktime_get_real_ns(); + level = gpiod_get_value_cansleep(le->desc); if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE && le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { - int level = gpiod_get_value_cansleep(le->desc); - if (level) /* Emit low-to-high event */ ge.id = GPIOEVENT_EVENT_RISING_EDGE; else /* Emit high-to-low event */ ge.id = GPIOEVENT_EVENT_FALLING_EDGE; - } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE) { + } else if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE && level) { /* Emit low-to-high event */ ge.id = GPIOEVENT_EVENT_RISING_EDGE; - } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE) { + } else if (le->eflags & GPIOEVENT_REQUEST_FALLING_EDGE && !level) { /* Emit high-to-low event */ ge.id = GPIOEVENT_EVENT_FALLING_EDGE; } else { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index 5f8ada1d872b..37971d9402e3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -101,7 +101,6 @@ void amdgpu_amdkfd_device_init(struct amdgpu_device *adev) if (adev->kfd) { struct kgd2kfd_shared_resources gpu_resources = { .compute_vmid_bitmap = 0xFF00, - .num_mec = adev->gfx.mec.num_mec, .num_pipe_per_mec = adev->gfx.mec.num_pipe_per_mec, .num_queue_per_pipe = adev->gfx.mec.num_queue_per_pipe }; @@ -122,7 +121,7 @@ void amdgpu_amdkfd_device_init(struct amdgpu_device *adev) /* According to linux/bitmap.h we shouldn't use bitmap_clear if * nbits is not compile time constant */ - last_valid_bit = adev->gfx.mec.num_mec + last_valid_bit = 1 /* only first MEC can have compute queues */ * adev->gfx.mec.num_pipe_per_mec * adev->gfx.mec.num_queue_per_pipe; for (i = last_valid_bit; i < KGD_MAX_QUEUES; ++i) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c index f621ee115c98..5e771bc11b00 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c @@ -198,12 +198,16 @@ amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id) result = idr_find(&fpriv->bo_list_handles, id); if (result) { - if (kref_get_unless_zero(&result->refcount)) + if (kref_get_unless_zero(&result->refcount)) { + rcu_read_unlock(); mutex_lock(&result->lock); - else + } else { + rcu_read_unlock(); result = NULL; + } + } else { + rcu_read_unlock(); } - rcu_read_unlock(); return result; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c index 38f739fb727b..6558a3ed57a7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mn.c @@ -359,7 +359,7 @@ void amdgpu_mn_unregister(struct amdgpu_bo *bo) head = bo->mn_list.next; bo->mn = NULL; - list_del(&bo->mn_list); + list_del_init(&bo->mn_list); if (list_empty(head)) { struct amdgpu_mn_node *node; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c index a6899180b265..c586f44312f9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sync.c @@ -244,6 +244,12 @@ struct dma_fence *amdgpu_sync_peek_fence(struct amdgpu_sync *sync, struct dma_fence *f = e->fence; struct amd_sched_fence *s_fence = to_amd_sched_fence(f); + if (dma_fence_is_signaled(f)) { + hash_del(&e->node); + dma_fence_put(f); + kmem_cache_free(amdgpu_sync_slab, e); + continue; + } if (ring && s_fence) { /* For fences from the same ring it is sufficient * when they are scheduled. @@ -256,13 +262,6 @@ struct dma_fence *amdgpu_sync_peek_fence(struct amdgpu_sync *sync, } } - if (dma_fence_is_signaled(f)) { - hash_del(&e->node); - dma_fence_put(f); - kmem_cache_free(amdgpu_sync_slab, e); - continue; - } - return f; } diff --git a/drivers/gpu/drm/amd/amdgpu/clearstate_gfx9.h b/drivers/gpu/drm/amd/amdgpu/clearstate_gfx9.h index 18fd01f3e4b2..003a131bad47 100644 --- a/drivers/gpu/drm/amd/amdgpu/clearstate_gfx9.h +++ b/drivers/gpu/drm/amd/amdgpu/clearstate_gfx9.h @@ -1,24 +1,25 @@ - /* -*************************************************************************************************** -* -* Trade secret of Advanced Micro Devices, Inc. -* Copyright (c) 2010 Advanced Micro Devices, Inc. (unpublished) -* -* All rights reserved. This notice is intended as a precaution against inadvertent publication and -* does not imply publication or any waiver of confidentiality. The year included in the foregoing -* notice is the year of creation of the work. -* -*************************************************************************************************** -*/ -/** -*************************************************************************************************** -* @brief gfx9 Clearstate Definitions -*************************************************************************************************** -* -* Do not edit! This is a machine-generated file! -* -*/ + * 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. + * + */ static const unsigned int gfx9_SECT_CONTEXT_def_1[] = { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 3a0b69b09ed6..c9b9c88231aa 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -1475,21 +1475,23 @@ static void gfx_v9_0_tiling_mode_table_init(struct amdgpu_device *adev) static void gfx_v9_0_select_se_sh(struct amdgpu_device *adev, u32 se_num, u32 sh_num, u32 instance) { - u32 data = REG_SET_FIELD(0, GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES, 1); + u32 data; - if ((se_num == 0xffffffff) && (sh_num == 0xffffffff)) { - data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SH_BROADCAST_WRITES, 1); - data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SE_BROADCAST_WRITES, 1); - } else if (se_num == 0xffffffff) { - data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SH_INDEX, sh_num); + if (instance == 0xffffffff) + data = REG_SET_FIELD(0, GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES, 1); + else + data = REG_SET_FIELD(0, GRBM_GFX_INDEX, INSTANCE_INDEX, instance); + + if (se_num == 0xffffffff) data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SE_BROADCAST_WRITES, 1); - } else if (sh_num == 0xffffffff) { - data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SH_BROADCAST_WRITES, 1); + else data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SE_INDEX, se_num); - } else { + + if (sh_num == 0xffffffff) + data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SH_BROADCAST_WRITES, 1); + else data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SH_INDEX, sh_num); - data = REG_SET_FIELD(data, GRBM_GFX_INDEX, SE_INDEX, se_num); - } + WREG32_SOC15(GC, 0, mmGRBM_GFX_INDEX, data); } diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c index f45fb0f022b3..4267fa417997 100644 --- a/drivers/gpu/drm/amd/amdgpu/si.c +++ b/drivers/gpu/drm/amd/amdgpu/si.c @@ -1385,6 +1385,7 @@ static void si_init_golden_registers(struct amdgpu_device *adev) amdgpu_program_register_sequence(adev, pitcairn_mgcg_cgcg_init, (const u32)ARRAY_SIZE(pitcairn_mgcg_cgcg_init)); + break; case CHIP_VERDE: amdgpu_program_register_sequence(adev, verde_golden_registers, @@ -1409,6 +1410,7 @@ static void si_init_golden_registers(struct amdgpu_device *adev) amdgpu_program_register_sequence(adev, oland_mgcg_cgcg_init, (const u32)ARRAY_SIZE(oland_mgcg_cgcg_init)); + break; case CHIP_HAINAN: amdgpu_program_register_sequence(adev, hainan_golden_registers, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 88187bfc5ea3..3f95f7cb4019 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -226,10 +226,6 @@ bool kgd2kfd_device_init(struct kfd_dev *kfd, kfd->shared_resources = *gpu_resources; - /* We only use the first MEC */ - if (kfd->shared_resources.num_mec > 1) - kfd->shared_resources.num_mec = 1; - /* calculate max size of mqds needed for queues */ size = max_num_of_queues_per_device * kfd->device_info->mqd_size_aligned; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 955aa304ff48..602769ced3bd 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -77,13 +77,6 @@ static bool is_pipe_enabled(struct device_queue_manager *dqm, int mec, int pipe) return false; } -unsigned int get_mec_num(struct device_queue_manager *dqm) -{ - BUG_ON(!dqm || !dqm->dev); - - return dqm->dev->shared_resources.num_mec; -} - unsigned int get_queues_num(struct device_queue_manager *dqm) { BUG_ON(!dqm || !dqm->dev); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h index 66b9615bc3c1..faf820a06400 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h @@ -180,7 +180,6 @@ void device_queue_manager_init_cik(struct device_queue_manager_asic_ops *ops); void device_queue_manager_init_vi(struct device_queue_manager_asic_ops *ops); void program_sh_mem_settings(struct device_queue_manager *dqm, struct qcm_process_device *qpd); -unsigned int get_mec_num(struct device_queue_manager *dqm); unsigned int get_queues_num(struct device_queue_manager *dqm); unsigned int get_queues_per_pipe(struct device_queue_manager *dqm); unsigned int get_pipes_per_mec(struct device_queue_manager *dqm); diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index 91ef1484b3bb..36f376677a53 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -63,9 +63,6 @@ struct kgd2kfd_shared_resources { /* Bit n == 1 means VMID n is available for KFD. */ unsigned int compute_vmid_bitmap; - /* number of mec available from the hardware */ - uint32_t num_mec; - /* number of pipes per mec */ uint32_t num_pipe_per_mec; diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c index d6f097f44b6c..197174e562d2 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/vega10_hwmgr.c @@ -2128,15 +2128,9 @@ static int vega10_populate_avfs_parameters(struct pp_hwmgr *hwmgr) pp_table->AvfsGbCksOff.m2_shift = 12; pp_table->AvfsGbCksOff.b_shift = 0; - for (i = 0; i < dep_table->count; i++) { - if (dep_table->entries[i].sclk_offset == 0) - pp_table->StaticVoltageOffsetVid[i] = 248; - else - pp_table->StaticVoltageOffsetVid[i] = - (uint8_t)(dep_table->entries[i].sclk_offset * - VOLTAGE_VID_OFFSET_SCALE2 / - VOLTAGE_VID_OFFSET_SCALE1); - } + for (i = 0; i < dep_table->count; i++) + pp_table->StaticVoltageOffsetVid[i] = + convert_to_vid((uint8_t)(dep_table->entries[i].sclk_offset)); if ((PPREGKEY_VEGA10QUADRATICEQUATION_DFLT != data->disp_clk_quad_eqn_a) && diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 5c26488e7a2d..0529e500c534 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1255,7 +1255,7 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) /* port@2 is the output port */ ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &tc->panel, NULL); - if (ret) + if (ret && ret != -ENODEV) return ret; /* Shut down GPIO is optional */ diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index c0f336d23f9c..aed25c4183bb 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1655,6 +1655,9 @@ int drm_atomic_check_only(struct drm_atomic_state *state) if (config->funcs->atomic_check) ret = config->funcs->atomic_check(state->dev, state); + if (ret) + return ret; + if (!state->allow_modeset) { for_each_new_crtc_in_state(state, crtc, crtc_state, i) { if (drm_atomic_crtc_needs_modeset(crtc_state)) { @@ -1665,7 +1668,7 @@ int drm_atomic_check_only(struct drm_atomic_state *state) } } - return ret; + return 0; } EXPORT_SYMBOL(drm_atomic_check_only); @@ -2167,10 +2170,10 @@ int drm_mode_atomic_ioctl(struct drm_device *dev, struct drm_atomic_state *state; struct drm_modeset_acquire_ctx ctx; struct drm_plane *plane; - struct drm_out_fence_state *fence_state = NULL; + struct drm_out_fence_state *fence_state; unsigned plane_mask; int ret = 0; - unsigned int i, j, num_fences = 0; + unsigned int i, j, num_fences; /* disallow for drivers not supporting atomic: */ if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) @@ -2211,6 +2214,8 @@ retry: plane_mask = 0; copied_objs = 0; copied_props = 0; + fence_state = NULL; + num_fences = 0; for (i = 0; i < arg->count_objs; i++) { uint32_t obj_id, count_props; diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 213fb837e1c4..08af8d6b844b 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -544,7 +544,7 @@ void drm_dp_downstream_debug(struct seq_file *m, DP_DETAILED_CAP_INFO_AVAILABLE; int clk; int bpc; - char id[6]; + char id[7]; int len; uint8_t rev[2]; int type = port_cap[0] & DP_DS_PORT_TYPE_MASK; @@ -583,6 +583,7 @@ void drm_dp_downstream_debug(struct seq_file *m, seq_puts(m, "\t\tType: N/A\n"); } + memset(id, 0, sizeof(id)); drm_dp_downstream_id(aux, id); seq_printf(m, "\t\tID: %s\n", id); @@ -591,7 +592,7 @@ void drm_dp_downstream_debug(struct seq_file *m, seq_printf(m, "\t\tHW: %d.%d\n", (rev[0] & 0xf0) >> 4, rev[0] & 0xf); - len = drm_dp_dpcd_read(aux, DP_BRANCH_SW_REV, &rev, 2); + len = drm_dp_dpcd_read(aux, DP_BRANCH_SW_REV, rev, 2); if (len > 0) seq_printf(m, "\t\tSW: %d.%d\n", rev[0], rev[1]); diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index bfd237c15e76..ae5f06895562 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -330,6 +330,13 @@ static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg, return false; } + /* + * ignore out-of-order messages or messages that are part of a + * failed transaction + */ + if (!recv_hdr.somt && !msg->have_somt) + return false; + /* get length contained in this portion */ msg->curchunk_len = recv_hdr.msg_len; msg->curchunk_hdrlen = hdrlen; @@ -2164,7 +2171,7 @@ out_unlock: } EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume); -static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) +static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) { int len; u8 replyblock[32]; @@ -2179,12 +2186,12 @@ static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) replyblock, len); if (ret != len) { DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret); - return; + return false; } ret = drm_dp_sideband_msg_build(msg, replyblock, len, true); if (!ret) { DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]); - return; + return false; } replylen = msg->curchunk_len + msg->curchunk_hdrlen; @@ -2196,21 +2203,32 @@ static void drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up) ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply, replyblock, len); if (ret != len) { - DRM_DEBUG_KMS("failed to read a chunk\n"); + DRM_DEBUG_KMS("failed to read a chunk (len %d, ret %d)\n", + len, ret); + return false; } + ret = drm_dp_sideband_msg_build(msg, replyblock, len, false); - if (ret == false) + if (!ret) { DRM_DEBUG_KMS("failed to build sideband msg\n"); + return false; + } + curreply += len; replylen -= len; } + return true; } static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) { int ret = 0; - drm_dp_get_one_sb_msg(mgr, false); + if (!drm_dp_get_one_sb_msg(mgr, false)) { + memset(&mgr->down_rep_recv, 0, + sizeof(struct drm_dp_sideband_msg_rx)); + return 0; + } if (mgr->down_rep_recv.have_eomt) { struct drm_dp_sideband_msg_tx *txmsg; @@ -2266,7 +2284,12 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) { int ret = 0; - drm_dp_get_one_sb_msg(mgr, true); + + if (!drm_dp_get_one_sb_msg(mgr, true)) { + memset(&mgr->up_req_recv, 0, + sizeof(struct drm_dp_sideband_msg_rx)); + return 0; + } if (mgr->up_req_recv.have_eomt) { struct drm_dp_sideband_msg_req_body msg; @@ -2318,7 +2341,9 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr) DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn); } - drm_dp_put_mst_branch_device(mstb); + if (mstb) + drm_dp_put_mst_branch_device(mstb); + memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx)); } return ret; diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index 8dc11064253d..cdaac37907b1 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -255,13 +255,13 @@ drm_gem_object_release_handle(int id, void *ptr, void *data) struct drm_gem_object *obj = ptr; struct drm_device *dev = obj->dev; + if (dev->driver->gem_close_object) + dev->driver->gem_close_object(obj, file_priv); + if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_gem_remove_prime_handles(obj, file_priv); drm_vma_node_revoke(&obj->vma_node, file_priv); - if (dev->driver->gem_close_object) - dev->driver->gem_close_object(obj, file_priv); - drm_gem_object_handle_put_unlocked(obj); return 0; diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 5dc8c4350602..e40c12fabbde 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -601,6 +601,7 @@ int drm_mode_setplane(struct drm_device *dev, void *data, crtc = drm_crtc_find(dev, plane_req->crtc_id); if (!crtc) { + drm_framebuffer_put(fb); DRM_DEBUG_KMS("Unknown crtc ID %d\n", plane_req->crtc_id); return -ENOENT; diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c index 5bd93169dac2..6463fc2c736f 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gem_submit.c @@ -270,8 +270,8 @@ static int submit_reloc(struct etnaviv_gem_submit *submit, void *stream, if (ret) return ret; - if (r->reloc_offset >= bo->obj->base.size - sizeof(*ptr)) { - DRM_ERROR("relocation %u outside object", i); + if (r->reloc_offset > bo->obj->base.size - sizeof(*ptr)) { + DRM_ERROR("relocation %u outside object\n", i); return -EINVAL; } diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 1d185347c64c..305dc3d4ff77 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -75,6 +75,7 @@ config DRM_EXYNOS_DP config DRM_EXYNOS_HDMI bool "HDMI" depends on DRM_EXYNOS_MIXER || DRM_EXYNOS5433_DECON + select CEC_CORE if CEC_NOTIFIER help Choose this option if you want to use Exynos HDMI for DRM. diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 35a8dfc93836..242bd50faa26 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -453,7 +453,6 @@ static int exynos_drm_platform_probe(struct platform_device *pdev) struct component_match *match; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); - exynos_drm_driver.num_ioctls = ARRAY_SIZE(exynos_ioctls); match = exynos_drm_match_add(&pdev->dev); if (IS_ERR(match)) diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index a11b79596e2f..b6a46d9a016e 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1651,8 +1651,6 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) return ret; dsi->bridge_node = of_graph_get_remote_node(node, DSI_PORT_IN, 0); - if (!dsi->bridge_node) - return -EINVAL; return 0; } @@ -1687,9 +1685,11 @@ static int exynos_dsi_bind(struct device *dev, struct device *master, return ret; } - bridge = of_drm_find_bridge(dsi->bridge_node); - if (bridge) - drm_bridge_attach(encoder, bridge, NULL); + if (dsi->bridge_node) { + bridge = of_drm_find_bridge(dsi->bridge_node); + if (bridge) + drm_bridge_attach(encoder, bridge, NULL); + } return mipi_dsi_host_register(&dsi->dsi_host); } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c index d48fd7c918f8..73217c281c9a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fb.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -145,13 +145,19 @@ static struct drm_framebuffer * exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) { + const struct drm_format_info *info = drm_get_format_info(dev, mode_cmd); struct exynos_drm_gem *exynos_gem[MAX_FB_BUFFER]; struct drm_gem_object *obj; struct drm_framebuffer *fb; int i; int ret; - for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) { + for (i = 0; i < info->num_planes; i++) { + unsigned int height = (i == 0) ? mode_cmd->height : + DIV_ROUND_UP(mode_cmd->height, info->vsub); + unsigned long size = height * mode_cmd->pitches[i] + + mode_cmd->offsets[i]; + obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]); if (!obj) { DRM_ERROR("failed to lookup gem object\n"); @@ -160,6 +166,12 @@ exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, } exynos_gem[i] = to_exynos_gem(obj); + + if (size > exynos_gem[i]->size) { + i++; + ret = -EINVAL; + goto err; + } } fb = exynos_drm_framebuffer_init(dev, mode_cmd, exynos_gem, i); diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c index e45720543a45..16bbee897e0d 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_mic.c +++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c @@ -340,16 +340,10 @@ static int exynos_mic_bind(struct device *dev, struct device *master, void *data) { struct exynos_mic *mic = dev_get_drvdata(dev); - int ret; - mic->bridge.funcs = &mic_bridge_funcs; - mic->bridge.of_node = dev->of_node; mic->bridge.driver_private = mic; - ret = drm_bridge_add(&mic->bridge); - if (ret) - DRM_ERROR("mic: Failed to add MIC to the global bridge list\n"); - return ret; + return 0; } static void exynos_mic_unbind(struct device *dev, struct device *master, @@ -365,8 +359,6 @@ static void exynos_mic_unbind(struct device *dev, struct device *master, already_disabled: mutex_unlock(&mic_mutex); - - drm_bridge_remove(&mic->bridge); } static const struct component_ops exynos_mic_component_ops = { @@ -461,6 +453,15 @@ static int exynos_mic_probe(struct platform_device *pdev) platform_set_drvdata(pdev, mic); + mic->bridge.funcs = &mic_bridge_funcs; + mic->bridge.of_node = dev->of_node; + + ret = drm_bridge_add(&mic->bridge); + if (ret) { + DRM_ERROR("mic: Failed to add MIC to the global bridge list\n"); + return ret; + } + pm_runtime_enable(dev); ret = component_add(dev, &exynos_mic_component_ops); @@ -479,8 +480,13 @@ err: static int exynos_mic_remove(struct platform_device *pdev) { + struct exynos_mic *mic = platform_get_drvdata(pdev); + component_del(&pdev->dev, &exynos_mic_component_ops); pm_runtime_disable(&pdev->dev); + + drm_bridge_remove(&mic->bridge); + return 0; } diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 06bfbe400cf1..d3b69d66736f 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1501,8 +1501,6 @@ static void hdmi_disable(struct drm_encoder *encoder) */ cancel_delayed_work(&hdata->hotplug_work); cec_notifier_set_phys_addr(hdata->notifier, CEC_PHYS_ADDR_INVALID); - - hdmiphy_disable(hdata); } static const struct drm_encoder_helper_funcs exynos_hdmi_encoder_helper_funcs = { @@ -1676,7 +1674,7 @@ static int hdmi_resources_init(struct hdmi_context *hdata) return hdmi_bridge_init(hdata); } -static struct of_device_id hdmi_match_types[] = { +static const struct of_device_id hdmi_match_types[] = { { .compatible = "samsung,exynos4210-hdmi", .data = &exynos4210_hdmi_driver_data, @@ -1934,8 +1932,7 @@ static int hdmi_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int exynos_hdmi_suspend(struct device *dev) +static int __maybe_unused exynos_hdmi_suspend(struct device *dev) { struct hdmi_context *hdata = dev_get_drvdata(dev); @@ -1944,7 +1941,7 @@ static int exynos_hdmi_suspend(struct device *dev) return 0; } -static int exynos_hdmi_resume(struct device *dev) +static int __maybe_unused exynos_hdmi_resume(struct device *dev) { struct hdmi_context *hdata = dev_get_drvdata(dev); int ret; @@ -1955,7 +1952,6 @@ static int exynos_hdmi_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops exynos_hdmi_pm_ops = { SET_RUNTIME_PM_OPS(exynos_hdmi_suspend, exynos_hdmi_resume, NULL) diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 6bed4f3ffcd6..a998a8dd783c 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1094,28 +1094,28 @@ static const struct exynos_drm_crtc_ops mixer_crtc_ops = { .atomic_check = mixer_atomic_check, }; -static struct mixer_drv_data exynos5420_mxr_drv_data = { +static const struct mixer_drv_data exynos5420_mxr_drv_data = { .version = MXR_VER_128_0_0_184, .is_vp_enabled = 0, }; -static struct mixer_drv_data exynos5250_mxr_drv_data = { +static const struct mixer_drv_data exynos5250_mxr_drv_data = { .version = MXR_VER_16_0_33_0, .is_vp_enabled = 0, }; -static struct mixer_drv_data exynos4212_mxr_drv_data = { +static const struct mixer_drv_data exynos4212_mxr_drv_data = { .version = MXR_VER_0_0_0_16, .is_vp_enabled = 1, }; -static struct mixer_drv_data exynos4210_mxr_drv_data = { +static const struct mixer_drv_data exynos4210_mxr_drv_data = { .version = MXR_VER_0_0_0_16, .is_vp_enabled = 1, .has_sclk = 1, }; -static struct of_device_id mixer_match_types[] = { +static const struct of_device_id mixer_match_types[] = { { .compatible = "samsung,exynos4210-mixer", .data = &exynos4210_mxr_drv_data, diff --git a/drivers/gpu/drm/i915/gvt/cmd_parser.c b/drivers/gpu/drm/i915/gvt/cmd_parser.c index 713848c36349..e556a46cd4c2 100644 --- a/drivers/gpu/drm/i915/gvt/cmd_parser.c +++ b/drivers/gpu/drm/i915/gvt/cmd_parser.c @@ -2714,7 +2714,7 @@ static int shadow_indirect_ctx(struct intel_shadow_wa_ctx *wa_ctx) unmap_src: i915_gem_object_unpin_map(obj); put_obj: - i915_gem_object_put(wa_ctx->indirect_ctx.obj); + i915_gem_object_put(obj); return ret; } diff --git a/drivers/gpu/drm/i915/gvt/display.c b/drivers/gpu/drm/i915/gvt/display.c index 2deb05f618fb..7cb0818a13de 100644 --- a/drivers/gpu/drm/i915/gvt/display.c +++ b/drivers/gpu/drm/i915/gvt/display.c @@ -323,27 +323,27 @@ void intel_gvt_check_vblank_emulation(struct intel_gvt *gvt) { struct intel_gvt_irq *irq = &gvt->irq; struct intel_vgpu *vgpu; - bool have_enabled_pipe = false; int pipe, id; if (WARN_ON(!mutex_is_locked(&gvt->lock))) return; - hrtimer_cancel(&irq->vblank_timer.timer); - for_each_active_vgpu(gvt, vgpu, id) { for (pipe = 0; pipe < I915_MAX_PIPES; pipe++) { - have_enabled_pipe = - pipe_is_enabled(vgpu, pipe); - if (have_enabled_pipe) - break; + if (pipe_is_enabled(vgpu, pipe)) + goto out; } } - if (have_enabled_pipe) - hrtimer_start(&irq->vblank_timer.timer, - ktime_add_ns(ktime_get(), irq->vblank_timer.period), - HRTIMER_MODE_ABS); + /* all the pipes are disabled */ + hrtimer_cancel(&irq->vblank_timer.timer); + return; + +out: + hrtimer_start(&irq->vblank_timer.timer, + ktime_add_ns(ktime_get(), irq->vblank_timer.period), + HRTIMER_MODE_ABS); + } static void emulate_vblank_on_pipe(struct intel_vgpu *vgpu, int pipe) diff --git a/drivers/gpu/drm/i915/gvt/execlist.c b/drivers/gpu/drm/i915/gvt/execlist.c index 700050556242..1648887d3f55 100644 --- a/drivers/gpu/drm/i915/gvt/execlist.c +++ b/drivers/gpu/drm/i915/gvt/execlist.c @@ -46,6 +46,8 @@ #define same_context(a, b) (((a)->context_id == (b)->context_id) && \ ((a)->lrca == (b)->lrca)) +static void clean_workloads(struct intel_vgpu *vgpu, unsigned long engine_mask); + static int context_switch_events[] = { [RCS] = RCS_AS_CONTEXT_SWITCH, [BCS] = BCS_AS_CONTEXT_SWITCH, @@ -499,10 +501,10 @@ static void release_shadow_wa_ctx(struct intel_shadow_wa_ctx *wa_ctx) static int complete_execlist_workload(struct intel_vgpu_workload *workload) { struct intel_vgpu *vgpu = workload->vgpu; - struct intel_vgpu_execlist *execlist = - &vgpu->execlist[workload->ring_id]; + int ring_id = workload->ring_id; + struct intel_vgpu_execlist *execlist = &vgpu->execlist[ring_id]; struct intel_vgpu_workload *next_workload; - struct list_head *next = workload_q_head(vgpu, workload->ring_id)->next; + struct list_head *next = workload_q_head(vgpu, ring_id)->next; bool lite_restore = false; int ret; @@ -512,10 +514,25 @@ static int complete_execlist_workload(struct intel_vgpu_workload *workload) release_shadow_batch_buffer(workload); release_shadow_wa_ctx(&workload->wa_ctx); - if (workload->status || vgpu->resetting) + if (workload->status || (vgpu->resetting_eng & ENGINE_MASK(ring_id))) { + /* if workload->status is not successful means HW GPU + * has occurred GPU hang or something wrong with i915/GVT, + * and GVT won't inject context switch interrupt to guest. + * So this error is a vGPU hang actually to the guest. + * According to this we should emunlate a vGPU hang. If + * there are pending workloads which are already submitted + * from guest, we should clean them up like HW GPU does. + * + * if it is in middle of engine resetting, the pending + * workloads won't be submitted to HW GPU and will be + * cleaned up during the resetting process later, so doing + * the workload clean up here doesn't have any impact. + **/ + clean_workloads(vgpu, ENGINE_MASK(ring_id)); goto out; + } - if (!list_empty(workload_q_head(vgpu, workload->ring_id))) { + if (!list_empty(workload_q_head(vgpu, ring_id))) { struct execlist_ctx_descriptor_format *this_desc, *next_desc; next_workload = container_of(next, diff --git a/drivers/gpu/drm/i915/gvt/firmware.c b/drivers/gpu/drm/i915/gvt/firmware.c index 5dad9298b2d5..a26c1705430e 100644 --- a/drivers/gpu/drm/i915/gvt/firmware.c +++ b/drivers/gpu/drm/i915/gvt/firmware.c @@ -72,11 +72,13 @@ static int expose_firmware_sysfs(struct intel_gvt *gvt) struct intel_gvt_device_info *info = &gvt->device_info; struct pci_dev *pdev = gvt->dev_priv->drm.pdev; struct intel_gvt_mmio_info *e; + struct gvt_mmio_block *block = gvt->mmio.mmio_block; + int num = gvt->mmio.num_mmio_block; struct gvt_firmware_header *h; void *firmware; void *p; unsigned long size, crc32_start; - int i; + int i, j; int ret; size = sizeof(*h) + info->mmio_size + info->cfg_space_size; @@ -105,6 +107,13 @@ static int expose_firmware_sysfs(struct intel_gvt *gvt) hash_for_each(gvt->mmio.mmio_info_table, i, e, node) *(u32 *)(p + e->offset) = I915_READ_NOTRACE(_MMIO(e->offset)); + for (i = 0; i < num; i++, block++) { + for (j = 0; j < block->size; j += 4) + *(u32 *)(p + INTEL_GVT_MMIO_OFFSET(block->offset) + j) = + I915_READ_NOTRACE(_MMIO(INTEL_GVT_MMIO_OFFSET( + block->offset) + j)); + } + memcpy(gvt->firmware.mmio, p, info->mmio_size); crc32_start = offsetof(struct gvt_firmware_header, crc32) + 4; diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 3a74e79eac2f..2964a4d01a66 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -149,7 +149,7 @@ struct intel_vgpu { bool active; bool pv_notified; bool failsafe; - bool resetting; + unsigned int resetting_eng; void *sched_data; struct vgpu_sched_ctl sched_ctl; @@ -195,6 +195,15 @@ struct intel_gvt_fence { unsigned long vgpu_allocated_fence_num; }; +/* Special MMIO blocks. */ +struct gvt_mmio_block { + unsigned int device; + i915_reg_t offset; + unsigned int size; + gvt_mmio_func read; + gvt_mmio_func write; +}; + #define INTEL_GVT_MMIO_HASH_BITS 11 struct intel_gvt_mmio { @@ -214,6 +223,9 @@ struct intel_gvt_mmio { /* This reg could be accessed by unaligned address */ #define F_UNALIGN (1 << 6) + struct gvt_mmio_block *mmio_block; + unsigned int num_mmio_block; + DECLARE_HASHTABLE(mmio_info_table, INTEL_GVT_MMIO_HASH_BITS); unsigned int num_tracked_mmio; }; diff --git a/drivers/gpu/drm/i915/gvt/handlers.c b/drivers/gpu/drm/i915/gvt/handlers.c index 17febe830ff6..feed9921b3b3 100644 --- a/drivers/gpu/drm/i915/gvt/handlers.c +++ b/drivers/gpu/drm/i915/gvt/handlers.c @@ -2857,31 +2857,15 @@ static int init_skl_mmio_info(struct intel_gvt *gvt) return 0; } -/* Special MMIO blocks. */ -static struct gvt_mmio_block { - unsigned int device; - i915_reg_t offset; - unsigned int size; - gvt_mmio_func read; - gvt_mmio_func write; -} gvt_mmio_blocks[] = { - {D_SKL_PLUS, _MMIO(CSR_MMIO_START_RANGE), 0x3000, NULL, NULL}, - {D_ALL, _MMIO(MCHBAR_MIRROR_BASE_SNB), 0x40000, NULL, NULL}, - {D_ALL, _MMIO(VGT_PVINFO_PAGE), VGT_PVINFO_SIZE, - pvinfo_mmio_read, pvinfo_mmio_write}, - {D_ALL, LGC_PALETTE(PIPE_A, 0), 1024, NULL, NULL}, - {D_ALL, LGC_PALETTE(PIPE_B, 0), 1024, NULL, NULL}, - {D_ALL, LGC_PALETTE(PIPE_C, 0), 1024, NULL, NULL}, -}; - static struct gvt_mmio_block *find_mmio_block(struct intel_gvt *gvt, unsigned int offset) { unsigned long device = intel_gvt_get_device_type(gvt); - struct gvt_mmio_block *block = gvt_mmio_blocks; + struct gvt_mmio_block *block = gvt->mmio.mmio_block; + int num = gvt->mmio.num_mmio_block; int i; - for (i = 0; i < ARRAY_SIZE(gvt_mmio_blocks); i++, block++) { + for (i = 0; i < num; i++, block++) { if (!(device & block->device)) continue; if (offset >= INTEL_GVT_MMIO_OFFSET(block->offset) && @@ -2912,6 +2896,17 @@ void intel_gvt_clean_mmio_info(struct intel_gvt *gvt) gvt->mmio.mmio_attribute = NULL; } +/* Special MMIO blocks. */ +static struct gvt_mmio_block mmio_blocks[] = { + {D_SKL_PLUS, _MMIO(CSR_MMIO_START_RANGE), 0x3000, NULL, NULL}, + {D_ALL, _MMIO(MCHBAR_MIRROR_BASE_SNB), 0x40000, NULL, NULL}, + {D_ALL, _MMIO(VGT_PVINFO_PAGE), VGT_PVINFO_SIZE, + pvinfo_mmio_read, pvinfo_mmio_write}, + {D_ALL, LGC_PALETTE(PIPE_A, 0), 1024, NULL, NULL}, + {D_ALL, LGC_PALETTE(PIPE_B, 0), 1024, NULL, NULL}, + {D_ALL, LGC_PALETTE(PIPE_C, 0), 1024, NULL, NULL}, +}; + /** * intel_gvt_setup_mmio_info - setup MMIO information table for GVT device * @gvt: GVT device @@ -2951,6 +2946,9 @@ int intel_gvt_setup_mmio_info(struct intel_gvt *gvt) goto err; } + gvt->mmio.mmio_block = mmio_blocks; + gvt->mmio.num_mmio_block = ARRAY_SIZE(mmio_blocks); + gvt_dbg_mmio("traced %u virtual mmio registers\n", gvt->mmio.num_tracked_mmio); return 0; @@ -3030,7 +3028,7 @@ int intel_vgpu_mmio_reg_rw(struct intel_vgpu *vgpu, unsigned int offset, gvt_mmio_func func; int ret; - if (WARN_ON(bytes > 4)) + if (WARN_ON(bytes > 8)) return -EINVAL; /* diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index 4f7057d62d88..22e08eb2d0b7 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -432,7 +432,8 @@ static void complete_current_workload(struct intel_gvt *gvt, int ring_id) i915_gem_request_put(fetch_and_zero(&workload->req)); - if (!workload->status && !vgpu->resetting) { + if (!workload->status && !(vgpu->resetting_eng & + ENGINE_MASK(ring_id))) { update_guest_context(workload); for_each_set_bit(event, workload->pending_events, diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index 90c14e6e3ea0..3deadcbd5a24 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -480,11 +480,13 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, { struct intel_gvt *gvt = vgpu->gvt; struct intel_gvt_workload_scheduler *scheduler = &gvt->scheduler; + unsigned int resetting_eng = dmlr ? ALL_ENGINES : engine_mask; gvt_dbg_core("------------------------------------------\n"); gvt_dbg_core("resseting vgpu%d, dmlr %d, engine_mask %08x\n", vgpu->id, dmlr, engine_mask); - vgpu->resetting = true; + + vgpu->resetting_eng = resetting_eng; intel_vgpu_stop_schedule(vgpu); /* @@ -497,7 +499,7 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, mutex_lock(&gvt->lock); } - intel_vgpu_reset_execlist(vgpu, dmlr ? ALL_ENGINES : engine_mask); + intel_vgpu_reset_execlist(vgpu, resetting_eng); /* full GPU reset or device model level reset */ if (engine_mask == ALL_ENGINES || dmlr) { @@ -520,7 +522,7 @@ void intel_gvt_reset_vgpu_locked(struct intel_vgpu *vgpu, bool dmlr, } } - vgpu->resetting = false; + vgpu->resetting_eng = 0; gvt_dbg_core("reset vgpu%d done\n", vgpu->id); gvt_dbg_core("------------------------------------------\n"); } diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 00d8967c8512..d1bd53b73738 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -4580,7 +4580,7 @@ static void gen9_sseu_device_status(struct drm_i915_private *dev_priv, sseu->slice_mask |= BIT(s); - if (IS_GEN9_BC(dev_priv)) + if (IS_GEN9_BC(dev_priv) || IS_CANNONLAKE(dev_priv)) sseu->subslice_mask = INTEL_INFO(dev_priv)->sseu.subslice_mask; diff --git a/drivers/gpu/drm/i915/i915_gem_clflush.c b/drivers/gpu/drm/i915/i915_gem_clflush.c index 152f16c11878..348b29a845c9 100644 --- a/drivers/gpu/drm/i915/i915_gem_clflush.c +++ b/drivers/gpu/drm/i915/i915_gem_clflush.c @@ -114,7 +114,7 @@ i915_clflush_notify(struct i915_sw_fence *fence, return NOTIFY_DONE; } -void i915_gem_clflush_object(struct drm_i915_gem_object *obj, +bool i915_gem_clflush_object(struct drm_i915_gem_object *obj, unsigned int flags) { struct clflush *clflush; @@ -128,7 +128,7 @@ void i915_gem_clflush_object(struct drm_i915_gem_object *obj, */ if (!i915_gem_object_has_struct_page(obj)) { obj->cache_dirty = false; - return; + return false; } /* If the GPU is snooping the contents of the CPU cache, @@ -140,7 +140,7 @@ void i915_gem_clflush_object(struct drm_i915_gem_object *obj, * tracking. */ if (!(flags & I915_CLFLUSH_FORCE) && obj->cache_coherent) - return; + return false; trace_i915_gem_object_clflush(obj); @@ -179,4 +179,5 @@ void i915_gem_clflush_object(struct drm_i915_gem_object *obj, } obj->cache_dirty = false; + return true; } diff --git a/drivers/gpu/drm/i915/i915_gem_clflush.h b/drivers/gpu/drm/i915/i915_gem_clflush.h index 2455a7820937..f390247561b3 100644 --- a/drivers/gpu/drm/i915/i915_gem_clflush.h +++ b/drivers/gpu/drm/i915/i915_gem_clflush.h @@ -28,7 +28,7 @@ struct drm_i915_private; struct drm_i915_gem_object; -void i915_gem_clflush_object(struct drm_i915_gem_object *obj, +bool i915_gem_clflush_object(struct drm_i915_gem_object *obj, unsigned int flags); #define I915_CLFLUSH_FORCE BIT(0) #define I915_CLFLUSH_SYNC BIT(1) diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 39ed58a21fc1..e1e971ee2ed5 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -688,19 +688,19 @@ static inline bool skip_rcs_switch(struct i915_hw_ppgtt *ppgtt, } static bool -needs_pd_load_pre(struct i915_hw_ppgtt *ppgtt, - struct intel_engine_cs *engine, - struct i915_gem_context *to) +needs_pd_load_pre(struct i915_hw_ppgtt *ppgtt, struct intel_engine_cs *engine) { + struct i915_gem_context *from = engine->legacy_active_context; + if (!ppgtt) return false; /* Always load the ppgtt on first use */ - if (!engine->legacy_active_context) + if (!from) return true; /* Same context without new entries, skip */ - if (engine->legacy_active_context == to && + if ((!from->ppgtt || from->ppgtt == ppgtt) && !(intel_engine_flag(engine) & ppgtt->pd_dirty_rings)) return false; @@ -744,7 +744,7 @@ static int do_rcs_switch(struct drm_i915_gem_request *req) if (skip_rcs_switch(ppgtt, engine, to)) return 0; - if (needs_pd_load_pre(ppgtt, engine, to)) { + if (needs_pd_load_pre(ppgtt, engine)) { /* Older GENs and non render rings still want the load first, * "PP_DCLV followed by PP_DIR_BASE register through Load * Register Immediate commands in Ring Buffer before submitting @@ -841,7 +841,7 @@ int i915_switch_context(struct drm_i915_gem_request *req) struct i915_hw_ppgtt *ppgtt = to->ppgtt ?: req->i915->mm.aliasing_ppgtt; - if (needs_pd_load_pre(ppgtt, engine, to)) { + if (needs_pd_load_pre(ppgtt, engine)) { int ret; trace_switch_mm(engine, to); @@ -852,6 +852,7 @@ int i915_switch_context(struct drm_i915_gem_request *req) ppgtt->pd_dirty_rings &= ~intel_engine_flag(engine); } + engine->legacy_active_context = to; return 0; } diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 054b2e54cdaf..e9503f6d1100 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -560,9 +560,6 @@ static int eb_reserve_vma(const struct i915_execbuffer *eb, eb->args->flags |= __EXEC_HAS_RELOC; } - entry->flags |= __EXEC_OBJECT_HAS_PIN; - GEM_BUG_ON(eb_vma_misplaced(entry, vma)); - if (unlikely(entry->flags & EXEC_OBJECT_NEEDS_FENCE)) { err = i915_vma_get_fence(vma); if (unlikely(err)) { @@ -574,6 +571,9 @@ static int eb_reserve_vma(const struct i915_execbuffer *eb, entry->flags |= __EXEC_OBJECT_HAS_FENCE; } + entry->flags |= __EXEC_OBJECT_HAS_PIN; + GEM_BUG_ON(eb_vma_misplaced(entry, vma)); + return 0; } @@ -1458,7 +1458,7 @@ static int eb_relocate_vma(struct i915_execbuffer *eb, struct i915_vma *vma) * to read. However, if the array is not writable the user loses * the updated relocation values. */ - if (unlikely(!access_ok(VERIFY_READ, urelocs, remain*sizeof(urelocs)))) + if (unlikely(!access_ok(VERIFY_READ, urelocs, remain*sizeof(*urelocs)))) return -EFAULT; do { @@ -1775,7 +1775,7 @@ out: } } - return err ?: have_copy; + return err; } static int eb_relocate(struct i915_execbuffer *eb) @@ -1825,7 +1825,7 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb) int err; for (i = 0; i < count; i++) { - const struct drm_i915_gem_exec_object2 *entry = &eb->exec[i]; + struct drm_i915_gem_exec_object2 *entry = &eb->exec[i]; struct i915_vma *vma = exec_to_vma(entry); struct drm_i915_gem_object *obj = vma->obj; @@ -1841,12 +1841,14 @@ static int eb_move_to_gpu(struct i915_execbuffer *eb) eb->request->capture_list = capture; } + if (unlikely(obj->cache_dirty && !obj->cache_coherent)) { + if (i915_gem_clflush_object(obj, 0)) + entry->flags &= ~EXEC_OBJECT_ASYNC; + } + if (entry->flags & EXEC_OBJECT_ASYNC) goto skip_flushes; - if (unlikely(obj->cache_dirty && !obj->cache_coherent)) - i915_gem_clflush_object(obj, 0); - err = i915_gem_request_await_object (eb->request, obj, entry->flags & EXEC_OBJECT_WRITE); if (err) @@ -2209,7 +2211,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, goto err_unlock; err = eb_relocate(&eb); - if (err) + if (err) { /* * If the user expects the execobject.offset and * reloc.presumed_offset to be an exact match, @@ -2218,8 +2220,8 @@ i915_gem_do_execbuffer(struct drm_device *dev, * relocation. */ args->flags &= ~__EXEC_HAS_RELOC; - if (err < 0) goto err_vma; + } if (unlikely(eb.batch->exec_entry->flags & EXEC_OBJECT_WRITE)) { DRM_DEBUG("Attempting to use self-modifying batch buffer\n"); diff --git a/drivers/gpu/drm/i915/i915_gem_render_state.c b/drivers/gpu/drm/i915/i915_gem_render_state.c index 7032c542a9b1..4dd4c2159a92 100644 --- a/drivers/gpu/drm/i915/i915_gem_render_state.c +++ b/drivers/gpu/drm/i915/i915_gem_render_state.c @@ -242,6 +242,10 @@ int i915_gem_render_state_emit(struct drm_i915_gem_request *req) goto err_unpin; } + ret = req->engine->emit_flush(req, EMIT_INVALIDATE); + if (ret) + goto err_unpin; + ret = req->engine->emit_bb_start(req, so->batch_offset, so->batch_size, I915_DISPATCH_SECURE); diff --git a/drivers/gpu/drm/i915/i915_gem_shrinker.c b/drivers/gpu/drm/i915/i915_gem_shrinker.c index 1032f98add11..77fb39808131 100644 --- a/drivers/gpu/drm/i915/i915_gem_shrinker.c +++ b/drivers/gpu/drm/i915/i915_gem_shrinker.c @@ -43,16 +43,21 @@ static bool shrinker_lock(struct drm_i915_private *dev_priv, bool *unlock) return true; case MUTEX_TRYLOCK_FAILED: + *unlock = false; + preempt_disable(); do { cpu_relax(); if (mutex_trylock(&dev_priv->drm.struct_mutex)) { - case MUTEX_TRYLOCK_SUCCESS: *unlock = true; - return true; + break; } } while (!need_resched()); + preempt_enable(); + return *unlock; - return false; + case MUTEX_TRYLOCK_SUCCESS: + *unlock = true; + return true; } BUG(); diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index 9cd22f83b0cf..f33d90226704 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -1601,11 +1601,11 @@ static int gen8_emit_oa_config(struct drm_i915_gem_request *req) u32 *cs; int i; - cs = intel_ring_begin(req, n_flex_regs * 2 + 4); + cs = intel_ring_begin(req, ARRAY_SIZE(flex_mmio) * 2 + 4); if (IS_ERR(cs)) return PTR_ERR(cs); - *cs++ = MI_LOAD_REGISTER_IMM(n_flex_regs + 1); + *cs++ = MI_LOAD_REGISTER_IMM(ARRAY_SIZE(flex_mmio) + 1); *cs++ = i915_mmio_reg_offset(GEN8_OACTXCONTROL); *cs++ = (dev_priv->perf.oa.period_exponent << GEN8_OA_TIMER_PERIOD_SHIFT) | diff --git a/drivers/gpu/drm/i915/i915_vma.h b/drivers/gpu/drm/i915/i915_vma.h index 4a673fc1a432..20cf272c97b1 100644 --- a/drivers/gpu/drm/i915/i915_vma.h +++ b/drivers/gpu/drm/i915/i915_vma.h @@ -284,12 +284,12 @@ static inline void __i915_vma_pin(struct i915_vma *vma) static inline void __i915_vma_unpin(struct i915_vma *vma) { - GEM_BUG_ON(!i915_vma_is_pinned(vma)); vma->flags--; } static inline void i915_vma_unpin(struct i915_vma *vma) { + GEM_BUG_ON(!i915_vma_is_pinned(vma)); GEM_BUG_ON(!drm_mm_node_allocated(&vma->node)); __i915_vma_unpin(vma); } diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 639d45c1dd2e..7ea7fd1e8856 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -1120,8 +1120,8 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port, bool is_dvi, is_hdmi, is_dp, is_edp, is_crt; uint8_t aux_channel, ddc_pin; /* Each DDI port can have more than one value on the "DVO Port" field, - * so look for all the possible values for each port and abort if more - * than one is found. */ + * so look for all the possible values for each port. + */ int dvo_ports[][3] = { {DVO_PORT_HDMIA, DVO_PORT_DPA, -1}, {DVO_PORT_HDMIB, DVO_PORT_DPB, -1}, @@ -1130,7 +1130,10 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port, {DVO_PORT_CRT, DVO_PORT_HDMIE, DVO_PORT_DPE}, }; - /* Find the child device to use, abort if more than one found. */ + /* + * Find the first child device to reference the port, report if more + * than one found. + */ for (i = 0; i < dev_priv->vbt.child_dev_num; i++) { it = dev_priv->vbt.child_dev + i; @@ -1140,11 +1143,11 @@ static void parse_ddi_port(struct drm_i915_private *dev_priv, enum port port, if (it->common.dvo_port == dvo_ports[port][j]) { if (child) { - DRM_DEBUG_KMS("More than one child device for port %c in VBT.\n", + DRM_DEBUG_KMS("More than one child device for port %c in VBT, using the first.\n", port_name(port)); - return; + } else { + child = it; } - child = it; } } } diff --git a/drivers/gpu/drm/i915/intel_color.c b/drivers/gpu/drm/i915/intel_color.c index 306c6b06b330..17c4ae7e4e7c 100644 --- a/drivers/gpu/drm/i915/intel_color.c +++ b/drivers/gpu/drm/i915/intel_color.c @@ -398,6 +398,7 @@ static void bdw_load_gamma_lut(struct drm_crtc_state *state, u32 offset) } /* Program the max register to clamp values > 1.0. */ + i = lut_size - 1; I915_WRITE(PREC_PAL_GC_MAX(pipe, 0), drm_color_lut_extract(lut[i].red, 16)); I915_WRITE(PREC_PAL_GC_MAX(pipe, 1), diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 80e96f1f49d2..d3b3252a8742 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -1762,7 +1762,7 @@ cnl_get_buf_trans_edp(struct drm_i915_private *dev_priv, if (dev_priv->vbt.edp.low_vswing) { if (voltage == VOLTAGE_INFO_0_85V) { *n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_0_85V); - return cnl_ddi_translations_dp_0_85V; + return cnl_ddi_translations_edp_0_85V; } else if (voltage == VOLTAGE_INFO_0_95V) { *n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_0_95V); return cnl_ddi_translations_edp_0_95V; @@ -1896,8 +1896,8 @@ static void cnl_ddi_vswing_sequence(struct intel_encoder *encoder, u32 level) val = I915_READ(CNL_PORT_TX_DW4_LN(port, ln)); val &= ~LOADGEN_SELECT; - if (((rate < 600000) && (width == 4) && (ln >= 1)) || - ((rate < 600000) && (width < 4) && ((ln == 1) || (ln == 2)))) { + if ((rate <= 600000 && width == 4 && ln >= 1) || + (rate <= 600000 && width < 4 && (ln == 1 || ln == 2))) { val |= LOADGEN_SELECT; } I915_WRITE(CNL_PORT_TX_DW4_LN(port, ln), val); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index dec9e58545a1..cc484b56eeaa 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3427,26 +3427,6 @@ static void intel_complete_page_flips(struct drm_i915_private *dev_priv) intel_finish_page_flip_cs(dev_priv, crtc->pipe); } -static void intel_update_primary_planes(struct drm_device *dev) -{ - struct drm_crtc *crtc; - - for_each_crtc(dev, crtc) { - struct intel_plane *plane = to_intel_plane(crtc->primary); - struct intel_plane_state *plane_state = - to_intel_plane_state(plane->base.state); - - if (plane_state->base.visible) { - trace_intel_update_plane(&plane->base, - to_intel_crtc(crtc)); - - plane->update_plane(plane, - to_intel_crtc_state(crtc->state), - plane_state); - } - } -} - static int __intel_display_resume(struct drm_device *dev, struct drm_atomic_state *state, @@ -3499,6 +3479,19 @@ void intel_prepare_reset(struct drm_i915_private *dev_priv) struct drm_atomic_state *state; int ret; + + /* reset doesn't touch the display */ + if (!i915.force_reset_modeset_test && + !gpu_reset_clobbers_display(dev_priv)) + return; + + /* We have a modeset vs reset deadlock, defensively unbreak it. + * + * FIXME: We can do a _lot_ better, this is just a first iteration. + */ + i915_gem_set_wedged(dev_priv); + DRM_DEBUG_DRIVER("Wedging GPU to avoid deadlocks with pending modeset updates\n"); + /* * Need mode_config.mutex so that we don't * trample ongoing ->detect() and whatnot. @@ -3512,12 +3505,6 @@ void intel_prepare_reset(struct drm_i915_private *dev_priv) drm_modeset_backoff(ctx); } - - /* reset doesn't touch the display, but flips might get nuked anyway, */ - if (!i915.force_reset_modeset_test && - !gpu_reset_clobbers_display(dev_priv)) - return; - /* * Disabling the crtcs gracefully seems nicer. Also the * g33 docs say we should at least disable all the planes. @@ -3547,6 +3534,14 @@ void intel_finish_reset(struct drm_i915_private *dev_priv) struct drm_atomic_state *state = dev_priv->modeset_restore_state; int ret; + /* reset doesn't touch the display */ + if (!i915.force_reset_modeset_test && + !gpu_reset_clobbers_display(dev_priv)) + return; + + if (!state) + goto unlock; + /* * Flips in the rings will be nuked by the reset, * so complete all pending flips so that user space @@ -3558,22 +3553,10 @@ void intel_finish_reset(struct drm_i915_private *dev_priv) /* reset doesn't touch the display */ if (!gpu_reset_clobbers_display(dev_priv)) { - if (!state) { - /* - * Flips in the rings have been nuked by the reset, - * so update the base address of all primary - * planes to the the last fb to make sure we're - * showing the correct fb after a reset. - * - * FIXME: Atomic will make this obsolete since we won't schedule - * CS-based flips (which might get lost in gpu resets) any more. - */ - intel_update_primary_planes(dev); - } else { - ret = __intel_display_resume(dev, state, ctx); + /* for testing only restore the display */ + ret = __intel_display_resume(dev, state, ctx); if (ret) DRM_ERROR("Restoring old state failed with %i\n", ret); - } } else { /* * The display has been reset as well, @@ -3597,8 +3580,8 @@ void intel_finish_reset(struct drm_i915_private *dev_priv) intel_hpd_init(dev_priv); } - if (state) - drm_atomic_state_put(state); + drm_atomic_state_put(state); +unlock: drm_modeset_drop_locks(ctx); drm_modeset_acquire_fini(ctx); mutex_unlock(&dev->mode_config.mutex); @@ -9117,6 +9100,13 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, u64 power_domain_mask; bool active; + if (INTEL_GEN(dev_priv) >= 9) { + intel_crtc_init_scalers(crtc, pipe_config); + + pipe_config->scaler_state.scaler_id = -1; + pipe_config->scaler_state.scaler_users &= ~(1 << SKL_CRTC_INDEX); + } + power_domain = POWER_DOMAIN_PIPE(crtc->pipe); if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) return false; @@ -9145,13 +9135,6 @@ static bool haswell_get_pipe_config(struct intel_crtc *crtc, pipe_config->gamma_mode = I915_READ(GAMMA_MODE(crtc->pipe)) & GAMMA_MODE_MODE_MASK; - if (INTEL_GEN(dev_priv) >= 9) { - intel_crtc_init_scalers(crtc, pipe_config); - - pipe_config->scaler_state.scaler_id = -1; - pipe_config->scaler_state.scaler_users &= ~(1 << SKL_CRTC_INDEX); - } - power_domain = POWER_DOMAIN_PIPE_PANEL_FITTER(crtc->pipe); if (intel_display_power_get_if_enabled(dev_priv, power_domain)) { power_domain_mask |= BIT_ULL(power_domain); @@ -9540,7 +9523,16 @@ static void i9xx_update_cursor(struct intel_plane *plane, * On some platforms writing CURCNTR first will also * cause CURPOS to be armed by the CURBASE write. * Without the CURCNTR write the CURPOS write would - * arm itself. + * arm itself. Thus we always start the full update + * with a CURCNTR write. + * + * On other platforms CURPOS always requires the + * CURBASE write to arm the update. Additonally + * a write to any of the cursor register will cancel + * an already armed cursor update. Thus leaving out + * the CURBASE write after CURPOS could lead to a + * cursor that doesn't appear to move, or even change + * shape. Thus we always write CURBASE. * * CURCNTR and CUR_FBC_CTL are always * armed by the CURBASE write only. @@ -9559,6 +9551,7 @@ static void i9xx_update_cursor(struct intel_plane *plane, plane->cursor.cntl = cntl; } else { I915_WRITE_FW(CURPOS(pipe), pos); + I915_WRITE_FW(CURBASE(pipe), base); } POSTING_READ_FW(CURBASE(pipe)); diff --git a/drivers/gpu/drm/i915/intel_dsi_dcs_backlight.c b/drivers/gpu/drm/i915/intel_dsi_dcs_backlight.c index 6e09ceb71500..150a156f3b1e 100644 --- a/drivers/gpu/drm/i915/intel_dsi_dcs_backlight.c +++ b/drivers/gpu/drm/i915/intel_dsi_dcs_backlight.c @@ -46,7 +46,7 @@ static u32 dcs_get_backlight(struct intel_connector *connector) struct intel_encoder *encoder = connector->encoder; struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); struct mipi_dsi_device *dsi_device; - u8 data; + u8 data = 0; enum port port; /* FIXME: Need to take care of 16 bit brightness level */ diff --git a/drivers/gpu/drm/i915/intel_dsi_vbt.c b/drivers/gpu/drm/i915/intel_dsi_vbt.c index 7158c7ce9c09..91c07b0c8db9 100644 --- a/drivers/gpu/drm/i915/intel_dsi_vbt.c +++ b/drivers/gpu/drm/i915/intel_dsi_vbt.c @@ -306,7 +306,7 @@ static void bxt_exec_gpio(struct drm_i915_private *dev_priv, if (!gpio_desc) { gpio_desc = devm_gpiod_get_index(dev_priv->drm.dev, - "panel", gpio_index, + NULL, gpio_index, value ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH); diff --git a/drivers/gpu/drm/i915/intel_gvt.c b/drivers/gpu/drm/i915/intel_gvt.c index 52d5b82790d9..c17ed0e62b67 100644 --- a/drivers/gpu/drm/i915/intel_gvt.c +++ b/drivers/gpu/drm/i915/intel_gvt.c @@ -45,7 +45,7 @@ static bool is_supported_device(struct drm_i915_private *dev_priv) return true; if (IS_SKYLAKE(dev_priv)) return true; - if (IS_KABYLAKE(dev_priv) && INTEL_DEVID(dev_priv) == 0x591D) + if (IS_KABYLAKE(dev_priv)) return true; return false; } diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 7404cf2aac28..2afa4daa88e8 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1221,6 +1221,14 @@ static int intel_init_workaround_bb(struct intel_engine_cs *engine) return ret; } +static u8 gtiir[] = { + [RCS] = 0, + [BCS] = 0, + [VCS] = 1, + [VCS2] = 1, + [VECS] = 3, +}; + static int gen8_init_common_ring(struct intel_engine_cs *engine) { struct drm_i915_private *dev_priv = engine->i915; @@ -1245,9 +1253,22 @@ static int gen8_init_common_ring(struct intel_engine_cs *engine) DRM_DEBUG_DRIVER("Execlists enabled for %s\n", engine->name); - /* After a GPU reset, we may have requests to replay */ + GEM_BUG_ON(engine->id >= ARRAY_SIZE(gtiir)); + + /* + * Clear any pending interrupt state. + * + * We do it twice out of paranoia that some of the IIR are double + * buffered, and if we only reset it once there may still be + * an interrupt pending. + */ + I915_WRITE(GEN8_GT_IIR(gtiir[engine->id]), + GT_CONTEXT_SWITCH_INTERRUPT << engine->irq_shift); + I915_WRITE(GEN8_GT_IIR(gtiir[engine->id]), + GT_CONTEXT_SWITCH_INTERRUPT << engine->irq_shift); clear_bit(ENGINE_IRQ_EXECLIST, &engine->irq_posted); + /* After a GPU reset, we may have requests to replay */ submit = false; for (n = 0; n < ARRAY_SIZE(engine->execlist_port); n++) { if (!port_isset(&port[n])) diff --git a/drivers/gpu/drm/i915/intel_lrc.h b/drivers/gpu/drm/i915/intel_lrc.h index 52b3a1fd4059..57ef5833c427 100644 --- a/drivers/gpu/drm/i915/intel_lrc.h +++ b/drivers/gpu/drm/i915/intel_lrc.h @@ -63,7 +63,6 @@ enum { }; /* Logical Rings */ -void intel_logical_ring_stop(struct intel_engine_cs *engine); void intel_logical_ring_cleanup(struct intel_engine_cs *engine); int logical_render_ring_init(struct intel_engine_cs *engine); int logical_xcs_ring_init(struct intel_engine_cs *engine); diff --git a/drivers/gpu/drm/i915/intel_lspcon.c b/drivers/gpu/drm/i915/intel_lspcon.c index 5abef482eacf..beb9baaf2f2e 100644 --- a/drivers/gpu/drm/i915/intel_lspcon.c +++ b/drivers/gpu/drm/i915/intel_lspcon.c @@ -210,8 +210,8 @@ bool lspcon_init(struct intel_digital_port *intel_dig_port) struct drm_device *dev = intel_dig_port->base.base.dev; struct drm_i915_private *dev_priv = to_i915(dev); - if (!IS_GEN9(dev_priv)) { - DRM_ERROR("LSPCON is supported on GEN9 only\n"); + if (!HAS_LSPCON(dev_priv)) { + DRM_ERROR("LSPCON is not supported on this platform\n"); return false; } diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 96c2cbd81869..593349be8b9d 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -469,7 +469,7 @@ static u32 intel_panel_compute_brightness(struct intel_connector *connector, if (i915.invert_brightness > 0 || dev_priv->quirks & QUIRK_INVERT_BRIGHTNESS) { - return panel->backlight.max - val; + return panel->backlight.max - val + panel->backlight.min; } return val; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 48ea0fca1f72..40b224b44d1b 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4463,8 +4463,8 @@ static int skl_compute_plane_wm(const struct drm_i915_private *dev_priv, if ((cpp * cstate->base.adjusted_mode.crtc_htotal / 512 < 1) && (plane_bytes_per_line / 512 < 1)) selected_result = method2; - else if ((ddb_allocation && ddb_allocation / - fixed_16_16_to_u32_round_up(plane_blocks_per_line)) >= 1) + else if (ddb_allocation >= + fixed_16_16_to_u32_round_up(plane_blocks_per_line)) selected_result = min_fixed_16_16(method1, method2); else if (latency >= linetime_us) selected_result = min_fixed_16_16(method1, method2); diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c index 627e2aa09766..8cdec455cf7d 100644 --- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c +++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c @@ -206,7 +206,7 @@ struct drm_i915_private *mock_gem_device(void) mkwrite_device_info(i915)->ring_mask = BIT(0); i915->engine[RCS] = mock_engine(i915, "mock"); if (!i915->engine[RCS]) - goto err_dependencies; + goto err_priorities; i915->kernel_context = mock_context(i915, NULL); if (!i915->kernel_context) diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3-plane.c index 49546222c6d3..d3845989a29d 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3-plane.c @@ -54,7 +54,7 @@ static const uint32_t ipu_plane_formats[] = { DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888, DRM_FORMAT_BGRA8888, - DRM_FORMAT_BGRA8888, + DRM_FORMAT_BGRX8888, DRM_FORMAT_UYVY, DRM_FORMAT_VYUY, DRM_FORMAT_YUYV, @@ -545,15 +545,13 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, return; } + ics = ipu_drm_fourcc_to_colorspace(fb->format->format); switch (ipu_plane->dp_flow) { case IPU_DP_FLOW_SYNC_BG: - ipu_dp_setup_channel(ipu_plane->dp, - IPUV3_COLORSPACE_RGB, - IPUV3_COLORSPACE_RGB); + ipu_dp_setup_channel(ipu_plane->dp, ics, IPUV3_COLORSPACE_RGB); ipu_dp_set_global_alpha(ipu_plane->dp, true, 0, true); break; case IPU_DP_FLOW_SYNC_FG: - ics = ipu_drm_fourcc_to_colorspace(state->fb->format->format); ipu_dp_setup_channel(ipu_plane->dp, ics, IPUV3_COLORSPACE_UNKNOWN); /* Enable local alpha on partial plane */ diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index 636031a30e17..8aca20209cb8 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -237,7 +237,7 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) /* port@1 is the output port */ ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel, &imxpd->bridge); - if (ret) + if (ret && ret != -ENODEV) return ret; imxpd->dev = dev; diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index b638d192ce5e..99d39b2aefa6 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -5,7 +5,7 @@ config DRM_MSM depends on ARCH_QCOM || (ARM && COMPILE_TEST) depends on OF && COMMON_CLK depends on MMU - select QCOM_MDT_LOADER + select QCOM_MDT_LOADER if ARCH_QCOM select REGULATOR select DRM_KMS_HELPER select DRM_PANEL diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c index b4b54f1c24bc..f9eae03aa1dc 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c @@ -15,7 +15,7 @@ #include <linux/cpumask.h> #include <linux/qcom_scm.h> #include <linux/dma-mapping.h> -#include <linux/of_reserved_mem.h> +#include <linux/of_address.h> #include <linux/soc/qcom/mdt_loader.h> #include "msm_gem.h" #include "msm_mmu.h" @@ -26,16 +26,34 @@ static void a5xx_dump(struct msm_gpu *gpu); #define GPU_PAS_ID 13 -#if IS_ENABLED(CONFIG_QCOM_MDT_LOADER) - static int zap_shader_load_mdt(struct device *dev, const char *fwname) { const struct firmware *fw; + struct device_node *np; + struct resource r; phys_addr_t mem_phys; ssize_t mem_size; void *mem_region = NULL; int ret; + if (!IS_ENABLED(CONFIG_ARCH_QCOM)) + return -EINVAL; + + np = of_get_child_by_name(dev->of_node, "zap-shader"); + if (!np) + return -ENODEV; + + np = of_parse_phandle(np, "memory-region", 0); + if (!np) + return -EINVAL; + + ret = of_address_to_resource(np, 0, &r); + if (ret) + return ret; + + mem_phys = r.start; + mem_size = resource_size(&r); + /* Request the MDT file for the firmware */ ret = request_firmware(&fw, fwname, dev); if (ret) { @@ -51,7 +69,7 @@ static int zap_shader_load_mdt(struct device *dev, const char *fwname) } /* Allocate memory for the firmware image */ - mem_region = dmam_alloc_coherent(dev, mem_size, &mem_phys, GFP_KERNEL); + mem_region = memremap(mem_phys, mem_size, MEMREMAP_WC); if (!mem_region) { ret = -ENOMEM; goto out; @@ -69,16 +87,13 @@ static int zap_shader_load_mdt(struct device *dev, const char *fwname) DRM_DEV_ERROR(dev, "Unable to authorize the image\n"); out: + if (mem_region) + memunmap(mem_region); + release_firmware(fw); return ret; } -#else -static int zap_shader_load_mdt(struct device *dev, const char *fwname) -{ - return -ENODEV; -} -#endif static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, struct msm_file_private *ctx) @@ -117,12 +132,10 @@ static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit, gpu->funcs->flush(gpu); } -struct a5xx_hwcg { +static const struct { u32 offset; u32 value; -}; - -static const struct a5xx_hwcg a530_hwcg[] = { +} a5xx_hwcg[] = { {REG_A5XX_RBBM_CLOCK_CNTL_SP0, 0x02222222}, {REG_A5XX_RBBM_CLOCK_CNTL_SP1, 0x02222222}, {REG_A5XX_RBBM_CLOCK_CNTL_SP2, 0x02222222}, @@ -217,38 +230,16 @@ static const struct a5xx_hwcg a530_hwcg[] = { {REG_A5XX_RBBM_CLOCK_DELAY_VFD, 0x00002222} }; -static const struct { - int (*test)(struct adreno_gpu *gpu); - const struct a5xx_hwcg *regs; - unsigned int count; -} a5xx_hwcg_regs[] = { - { adreno_is_a530, a530_hwcg, ARRAY_SIZE(a530_hwcg), }, -}; - -static void _a5xx_enable_hwcg(struct msm_gpu *gpu, - const struct a5xx_hwcg *regs, unsigned int count) +void a5xx_set_hwcg(struct msm_gpu *gpu, bool state) { unsigned int i; - for (i = 0; i < count; i++) - gpu_write(gpu, regs[i].offset, regs[i].value); + for (i = 0; i < ARRAY_SIZE(a5xx_hwcg); i++) + gpu_write(gpu, a5xx_hwcg[i].offset, + state ? a5xx_hwcg[i].value : 0); - gpu_write(gpu, REG_A5XX_RBBM_CLOCK_CNTL, 0xAAA8AA00); - gpu_write(gpu, REG_A5XX_RBBM_ISDB_CNT, 0x182); -} - -static void a5xx_enable_hwcg(struct msm_gpu *gpu) -{ - struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(a5xx_hwcg_regs); i++) { - if (a5xx_hwcg_regs[i].test(adreno_gpu)) { - _a5xx_enable_hwcg(gpu, a5xx_hwcg_regs[i].regs, - a5xx_hwcg_regs[i].count); - return; - } - } + gpu_write(gpu, REG_A5XX_RBBM_CLOCK_CNTL, state ? 0xAAA8AA00 : 0); + gpu_write(gpu, REG_A5XX_RBBM_ISDB_CNT, state ? 0x182 : 0x180); } static int a5xx_me_init(struct msm_gpu *gpu) @@ -377,45 +368,6 @@ static int a5xx_zap_shader_resume(struct msm_gpu *gpu) return ret; } -/* Set up a child device to "own" the zap shader */ -static int a5xx_zap_shader_dev_init(struct device *parent, struct device *dev) -{ - struct device_node *node; - int ret; - - if (dev->parent) - return 0; - - /* Find the sub-node for the zap shader */ - node = of_get_child_by_name(parent->of_node, "zap-shader"); - if (!node) { - DRM_DEV_ERROR(parent, "zap-shader not found in device tree\n"); - return -ENODEV; - } - - dev->parent = parent; - dev->of_node = node; - dev_set_name(dev, "adreno_zap_shader"); - - ret = device_register(dev); - if (ret) { - DRM_DEV_ERROR(parent, "Couldn't register zap shader device\n"); - goto out; - } - - ret = of_reserved_mem_device_init(dev); - if (ret) { - DRM_DEV_ERROR(parent, "Unable to set up the reserved memory\n"); - device_unregister(dev); - } - -out: - if (ret) - dev->parent = NULL; - - return ret; -} - static int a5xx_zap_shader_init(struct msm_gpu *gpu) { static bool loaded; @@ -444,11 +396,7 @@ static int a5xx_zap_shader_init(struct msm_gpu *gpu) return -ENODEV; } - ret = a5xx_zap_shader_dev_init(&pdev->dev, &a5xx_gpu->zap_dev); - - if (!ret) - ret = zap_shader_load_mdt(&a5xx_gpu->zap_dev, - adreno_gpu->info->zapfw); + ret = zap_shader_load_mdt(&pdev->dev, adreno_gpu->info->zapfw); loaded = !ret; @@ -545,7 +493,7 @@ static int a5xx_hw_init(struct msm_gpu *gpu) gpu_write(gpu, REG_A5XX_RBBM_AHB_CNTL1, 0xA6FFFFFF); /* Enable HWCG */ - a5xx_enable_hwcg(gpu); + a5xx_set_hwcg(gpu, true); gpu_write(gpu, REG_A5XX_RBBM_AHB_CNTL2, 0x0000003F); @@ -691,9 +639,6 @@ static void a5xx_destroy(struct msm_gpu *gpu) DBG("%s", gpu->name); - if (a5xx_gpu->zap_dev.parent) - device_unregister(&a5xx_gpu->zap_dev); - if (a5xx_gpu->pm4_bo) { if (a5xx_gpu->pm4_iova) msm_gem_put_iova(a5xx_gpu->pm4_bo, gpu->aspace); @@ -920,31 +865,30 @@ static const u32 a5xx_registers[] = { 0x0000, 0x0002, 0x0004, 0x0020, 0x0022, 0x0026, 0x0029, 0x002B, 0x002E, 0x0035, 0x0038, 0x0042, 0x0044, 0x0044, 0x0047, 0x0095, 0x0097, 0x00BB, 0x03A0, 0x0464, 0x0469, 0x046F, 0x04D2, 0x04D3, - 0x04E0, 0x0533, 0x0540, 0x0555, 0xF400, 0xF400, 0xF800, 0xF807, - 0x0800, 0x081A, 0x081F, 0x0841, 0x0860, 0x0860, 0x0880, 0x08A0, - 0x0B00, 0x0B12, 0x0B15, 0x0B28, 0x0B78, 0x0B7F, 0x0BB0, 0x0BBD, - 0x0BC0, 0x0BC6, 0x0BD0, 0x0C53, 0x0C60, 0x0C61, 0x0C80, 0x0C82, - 0x0C84, 0x0C85, 0x0C90, 0x0C98, 0x0CA0, 0x0CA0, 0x0CB0, 0x0CB2, - 0x2180, 0x2185, 0x2580, 0x2585, 0x0CC1, 0x0CC1, 0x0CC4, 0x0CC7, - 0x0CCC, 0x0CCC, 0x0CD0, 0x0CD8, 0x0CE0, 0x0CE5, 0x0CE8, 0x0CE8, - 0x0CEC, 0x0CF1, 0x0CFB, 0x0D0E, 0x2100, 0x211E, 0x2140, 0x2145, - 0x2500, 0x251E, 0x2540, 0x2545, 0x0D10, 0x0D17, 0x0D20, 0x0D23, - 0x0D30, 0x0D30, 0x20C0, 0x20C0, 0x24C0, 0x24C0, 0x0E40, 0x0E43, - 0x0E4A, 0x0E4A, 0x0E50, 0x0E57, 0x0E60, 0x0E7C, 0x0E80, 0x0E8E, - 0x0E90, 0x0E96, 0x0EA0, 0x0EA8, 0x0EB0, 0x0EB2, 0xE140, 0xE147, - 0xE150, 0xE187, 0xE1A0, 0xE1A9, 0xE1B0, 0xE1B6, 0xE1C0, 0xE1C7, - 0xE1D0, 0xE1D1, 0xE200, 0xE201, 0xE210, 0xE21C, 0xE240, 0xE268, - 0xE000, 0xE006, 0xE010, 0xE09A, 0xE0A0, 0xE0A4, 0xE0AA, 0xE0EB, - 0xE100, 0xE105, 0xE380, 0xE38F, 0xE3B0, 0xE3B0, 0xE400, 0xE405, - 0xE408, 0xE4E9, 0xE4F0, 0xE4F0, 0xE280, 0xE280, 0xE282, 0xE2A3, - 0xE2A5, 0xE2C2, 0xE940, 0xE947, 0xE950, 0xE987, 0xE9A0, 0xE9A9, - 0xE9B0, 0xE9B6, 0xE9C0, 0xE9C7, 0xE9D0, 0xE9D1, 0xEA00, 0xEA01, - 0xEA10, 0xEA1C, 0xEA40, 0xEA68, 0xE800, 0xE806, 0xE810, 0xE89A, - 0xE8A0, 0xE8A4, 0xE8AA, 0xE8EB, 0xE900, 0xE905, 0xEB80, 0xEB8F, - 0xEBB0, 0xEBB0, 0xEC00, 0xEC05, 0xEC08, 0xECE9, 0xECF0, 0xECF0, - 0xEA80, 0xEA80, 0xEA82, 0xEAA3, 0xEAA5, 0xEAC2, 0xA800, 0xA8FF, - 0xAC60, 0xAC60, 0xB000, 0xB97F, 0xB9A0, 0xB9BF, - ~0 + 0x04E0, 0x0533, 0x0540, 0x0555, 0x0800, 0x081A, 0x081F, 0x0841, + 0x0860, 0x0860, 0x0880, 0x08A0, 0x0B00, 0x0B12, 0x0B15, 0x0B28, + 0x0B78, 0x0B7F, 0x0BB0, 0x0BBD, 0x0BC0, 0x0BC6, 0x0BD0, 0x0C53, + 0x0C60, 0x0C61, 0x0C80, 0x0C82, 0x0C84, 0x0C85, 0x0C90, 0x0C98, + 0x0CA0, 0x0CA0, 0x0CB0, 0x0CB2, 0x2180, 0x2185, 0x2580, 0x2585, + 0x0CC1, 0x0CC1, 0x0CC4, 0x0CC7, 0x0CCC, 0x0CCC, 0x0CD0, 0x0CD8, + 0x0CE0, 0x0CE5, 0x0CE8, 0x0CE8, 0x0CEC, 0x0CF1, 0x0CFB, 0x0D0E, + 0x2100, 0x211E, 0x2140, 0x2145, 0x2500, 0x251E, 0x2540, 0x2545, + 0x0D10, 0x0D17, 0x0D20, 0x0D23, 0x0D30, 0x0D30, 0x20C0, 0x20C0, + 0x24C0, 0x24C0, 0x0E40, 0x0E43, 0x0E4A, 0x0E4A, 0x0E50, 0x0E57, + 0x0E60, 0x0E7C, 0x0E80, 0x0E8E, 0x0E90, 0x0E96, 0x0EA0, 0x0EA8, + 0x0EB0, 0x0EB2, 0xE140, 0xE147, 0xE150, 0xE187, 0xE1A0, 0xE1A9, + 0xE1B0, 0xE1B6, 0xE1C0, 0xE1C7, 0xE1D0, 0xE1D1, 0xE200, 0xE201, + 0xE210, 0xE21C, 0xE240, 0xE268, 0xE000, 0xE006, 0xE010, 0xE09A, + 0xE0A0, 0xE0A4, 0xE0AA, 0xE0EB, 0xE100, 0xE105, 0xE380, 0xE38F, + 0xE3B0, 0xE3B0, 0xE400, 0xE405, 0xE408, 0xE4E9, 0xE4F0, 0xE4F0, + 0xE280, 0xE280, 0xE282, 0xE2A3, 0xE2A5, 0xE2C2, 0xE940, 0xE947, + 0xE950, 0xE987, 0xE9A0, 0xE9A9, 0xE9B0, 0xE9B6, 0xE9C0, 0xE9C7, + 0xE9D0, 0xE9D1, 0xEA00, 0xEA01, 0xEA10, 0xEA1C, 0xEA40, 0xEA68, + 0xE800, 0xE806, 0xE810, 0xE89A, 0xE8A0, 0xE8A4, 0xE8AA, 0xE8EB, + 0xE900, 0xE905, 0xEB80, 0xEB8F, 0xEBB0, 0xEBB0, 0xEC00, 0xEC05, + 0xEC08, 0xECE9, 0xECF0, 0xECF0, 0xEA80, 0xEA80, 0xEA82, 0xEAA3, + 0xEAA5, 0xEAC2, 0xA800, 0xA8FF, 0xAC60, 0xAC60, 0xB000, 0xB97F, + 0xB9A0, 0xB9BF, ~0 }; static void a5xx_dump(struct msm_gpu *gpu) @@ -1020,7 +964,14 @@ static void a5xx_show(struct msm_gpu *gpu, struct seq_file *m) { seq_printf(m, "status: %08x\n", gpu_read(gpu, REG_A5XX_RBBM_STATUS)); + + /* + * Temporarily disable hardware clock gating before going into + * adreno_show to avoid issues while reading the registers + */ + a5xx_set_hwcg(gpu, false); adreno_show(gpu, m); + a5xx_set_hwcg(gpu, true); } #endif diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h index 6638bc85645d..1137092241d5 100644 --- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.h +++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.h @@ -36,8 +36,6 @@ struct a5xx_gpu { uint32_t gpmu_dwords; uint32_t lm_leakage; - - struct device zap_dev; }; #define to_a5xx_gpu(x) container_of(x, struct a5xx_gpu, base) @@ -59,5 +57,6 @@ static inline int spin_usecs(struct msm_gpu *gpu, uint32_t usecs, } bool a5xx_idle(struct msm_gpu *gpu); +void a5xx_set_hwcg(struct msm_gpu *gpu, bool state); #endif /* __A5XX_GPU_H__ */ diff --git a/drivers/gpu/drm/msm/adreno/adreno_gpu.c b/drivers/gpu/drm/msm/adreno/adreno_gpu.c index f1ab2703674a..7414c6bbd582 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_gpu.c +++ b/drivers/gpu/drm/msm/adreno/adreno_gpu.c @@ -48,8 +48,15 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) *value = adreno_gpu->base.fast_rate; return 0; case MSM_PARAM_TIMESTAMP: - if (adreno_gpu->funcs->get_timestamp) - return adreno_gpu->funcs->get_timestamp(gpu, value); + if (adreno_gpu->funcs->get_timestamp) { + int ret; + + pm_runtime_get_sync(&gpu->pdev->dev); + ret = adreno_gpu->funcs->get_timestamp(gpu, value); + pm_runtime_put_autosuspend(&gpu->pdev->dev); + + return ret; + } return -EINVAL; default: DBG("%s: invalid param: %u", gpu->name, param); diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 9e9c5696bc03..c7b612c3d771 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -2137,6 +2137,13 @@ void msm_dsi_host_get_phy_clk_req(struct mipi_dsi_host *host, struct msm_dsi_phy_clk_request *clk_req) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + int ret; + + ret = dsi_calc_clk_rate(msm_host); + if (ret) { + pr_err("%s: unable to calc clk rate, %d\n", __func__, ret); + return; + } clk_req->bitclk_rate = msm_host->byte_clk_rate * 8; clk_req->escclk_rate = msm_host->esc_clk_rate; @@ -2280,7 +2287,6 @@ int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, struct drm_display_mode *mode) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); - int ret; if (msm_host->mode) { drm_mode_destroy(msm_host->dev, msm_host->mode); @@ -2293,12 +2299,6 @@ int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, return -ENOMEM; } - ret = dsi_calc_clk_rate(msm_host); - if (ret) { - pr_err("%s: unable to calc clk rate, %d\n", __func__, ret); - return ret; - } - return 0; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index cb5415d6c04b..735a87a699fa 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c @@ -221,8 +221,8 @@ static void blend_setup(struct drm_crtc *crtc) struct mdp5_ctl *ctl = mdp5_cstate->ctl; uint32_t blend_op, fg_alpha, bg_alpha, ctl_blend_flags = 0; unsigned long flags; - enum mdp5_pipe stage[STAGE_MAX + 1][MAX_PIPE_STAGE] = { SSPP_NONE }; - enum mdp5_pipe r_stage[STAGE_MAX + 1][MAX_PIPE_STAGE] = { SSPP_NONE }; + enum mdp5_pipe stage[STAGE_MAX + 1][MAX_PIPE_STAGE] = { { SSPP_NONE } }; + enum mdp5_pipe r_stage[STAGE_MAX + 1][MAX_PIPE_STAGE] = { { SSPP_NONE } }; int i, plane_cnt = 0; bool bg_alpha_enabled = false; u32 mixer_op_mode = 0; @@ -753,6 +753,7 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, if (!handle) { DBG("Cursor off"); cursor_enable = false; + mdp5_enable(mdp5_kms); goto set_cursor; } @@ -776,6 +777,8 @@ static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, get_roi(crtc, &roi_w, &roi_h); + mdp5_enable(mdp5_kms); + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_STRIDE(lm), stride); mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_FORMAT(lm), MDP5_LM_CURSOR_FORMAT_FORMAT(CURSOR_FMT_ARGB8888)); @@ -804,6 +807,7 @@ set_cursor: crtc_flush(crtc, flush_mask); end: + mdp5_disable(mdp5_kms); if (old_bo) { drm_flip_work_queue(&mdp5_crtc->unref_cursor_work, old_bo); /* enable vblank to complete cursor work: */ @@ -836,6 +840,8 @@ static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) get_roi(crtc, &roi_w, &roi_h); + mdp5_enable(mdp5_kms); + spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(lm), MDP5_LM_CURSOR_SIZE_ROI_H(roi_h) | @@ -847,6 +853,8 @@ static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) crtc_flush(crtc, flush_mask); + mdp5_disable(mdp5_kms); + return 0; } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c index 97f3294fbfc6..70bef51245af 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c @@ -299,7 +299,7 @@ static void mdp5_encoder_enable(struct drm_encoder *encoder) struct mdp5_interface *intf = mdp5_encoder->intf; if (intf->mode == MDP5_INTF_DSI_MODE_COMMAND) - mdp5_cmd_encoder_disable(encoder); + mdp5_cmd_encoder_enable(encoder); else mdp5_vid_encoder_enable(encoder); } diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c index 5d13fa5381ee..1c603aef3c59 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c @@ -502,7 +502,7 @@ static int get_clk(struct platform_device *pdev, struct clk **clkp, const char *name, bool mandatory) { struct device *dev = &pdev->dev; - struct clk *clk = devm_clk_get(dev, name); + struct clk *clk = msm_clk_get(pdev, name); if (IS_ERR(clk) && mandatory) { dev_err(dev, "failed to get %s (%ld)\n", name, PTR_ERR(clk)); return PTR_ERR(clk); @@ -887,21 +887,21 @@ static int mdp5_init(struct platform_device *pdev, struct drm_device *dev) } /* mandatory clocks: */ - ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk", true); + ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus", true); if (ret) goto fail; - ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk", true); + ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface", true); if (ret) goto fail; - ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk", true); + ret = get_clk(pdev, &mdp5_kms->core_clk, "core", true); if (ret) goto fail; - ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk", true); + ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync", true); if (ret) goto fail; /* optional clocks: */ - get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk", false); + get_clk(pdev, &mdp5_kms->lut_clk, "lut", false); /* we need to set a default rate before enabling. Set a safe * rate first, then figure out hw revision, and then set a diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index fe3a4de1a433..61f39c86dd09 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -890,8 +890,8 @@ static int mdp5_plane_mode_set(struct drm_plane *plane, struct mdp5_hw_pipe *right_hwpipe; const struct mdp_format *format; uint32_t nplanes, config = 0; - struct phase_step step = { 0 }; - struct pixel_ext pe = { 0 }; + struct phase_step step = { { 0 } }; + struct pixel_ext pe = { { 0 } }; uint32_t hdecm = 0, vdecm = 0; uint32_t pix_format; unsigned int rotation; diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 65f35544c1ec..a0c60e738db8 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -383,8 +383,10 @@ int msm_gem_get_iova(struct drm_gem_object *obj, struct page **pages; vma = add_vma(obj, aspace); - if (IS_ERR(vma)) - return PTR_ERR(vma); + if (IS_ERR(vma)) { + ret = PTR_ERR(vma); + goto unlock; + } pages = get_pages(obj); if (IS_ERR(pages)) { @@ -405,7 +407,7 @@ int msm_gem_get_iova(struct drm_gem_object *obj, fail: del_vma(vma); - +unlock: mutex_unlock(&msm_obj->lock); return ret; } @@ -928,8 +930,12 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev, if (use_vram) { struct msm_gem_vma *vma; struct page **pages; + struct msm_gem_object *msm_obj = to_msm_bo(obj); + + mutex_lock(&msm_obj->lock); vma = add_vma(obj, NULL); + mutex_unlock(&msm_obj->lock); if (IS_ERR(vma)) { ret = PTR_ERR(vma); goto fail; diff --git a/drivers/gpu/drm/msm/msm_gem_submit.c b/drivers/gpu/drm/msm/msm_gem_submit.c index 6bfca7470141..8a75c0bd8a78 100644 --- a/drivers/gpu/drm/msm/msm_gem_submit.c +++ b/drivers/gpu/drm/msm/msm_gem_submit.c @@ -34,8 +34,8 @@ 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_gem_submit *submit; - uint64_t sz = sizeof(*submit) + (nr_bos * sizeof(submit->bos[0])) + - (nr_cmds * sizeof(submit->cmd[0])); + uint64_t sz = sizeof(*submit) + ((u64)nr_bos * sizeof(submit->bos[0])) + + ((u64)nr_cmds * sizeof(submit->cmd[0])); if (sz > SIZE_MAX) return NULL; @@ -451,7 +451,7 @@ int msm_ioctl_gem_submit(struct drm_device *dev, void *data, if (ret) goto out; - if (!(args->fence & MSM_SUBMIT_NO_IMPLICIT)) { + if (!(args->flags & MSM_SUBMIT_NO_IMPLICIT)) { ret = submit_fence_sync(submit); if (ret) goto out; diff --git a/drivers/gpu/drm/msm/msm_gem_vma.c b/drivers/gpu/drm/msm/msm_gem_vma.c index c36321bc8714..d34e331554f3 100644 --- a/drivers/gpu/drm/msm/msm_gem_vma.c +++ b/drivers/gpu/drm/msm/msm_gem_vma.c @@ -42,7 +42,7 @@ void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, struct sg_table *sgt) { - if (!vma->iova) + if (!aspace || !vma->iova) return; if (aspace->mmu) { diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 147b22163f9f..dab78c660dd6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1158,8 +1158,6 @@ nouveau_connector_aux_xfer(struct drm_dp_aux *obj, struct drm_dp_aux_msg *msg) return -ENODEV; if (WARN_ON(msg->size > 16)) return -E2BIG; - if (msg->size == 0) - return msg->size; ret = nvkm_i2c_aux_acquire(aux); if (ret) diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 8d1df5678eaa..f362c9fa8b3b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -409,7 +409,6 @@ nouveau_display_fini(struct drm_device *dev, bool suspend) struct nouveau_display *disp = nouveau_display(dev); struct nouveau_drm *drm = nouveau_drm(dev); struct drm_connector *connector; - struct drm_crtc *crtc; if (!suspend) { if (drm_drv_uses_atomic_modeset(dev)) @@ -418,10 +417,6 @@ nouveau_display_fini(struct drm_device *dev, bool suspend) drm_crtc_force_disable_all(dev); } - /* Make sure that drm and hw vblank irqs get properly disabled. */ - drm_for_each_crtc(crtc, dev) - drm_crtc_vblank_off(crtc); - /* disable flip completion events */ nvif_notify_put(&drm->flip); diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index e3132a2ce34d..2bc0dc985214 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -3674,15 +3674,24 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) drm_mode_connector_attach_encoder(connector, encoder); if (dcbe->type == DCB_OUTPUT_DP) { + struct nv50_disp *disp = nv50_disp(encoder->dev); struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, dcbe->i2c_index); if (aux) { - nv_encoder->i2c = &nv_connector->aux.ddc; + if (disp->disp->oclass < GF110_DISP) { + /* HW has no support for address-only + * transactions, so we're required to + * use custom I2C-over-AUX code. + */ + nv_encoder->i2c = &aux->i2c; + } else { + nv_encoder->i2c = &nv_connector->aux.ddc; + } nv_encoder->aux = aux; } /*TODO: Use DP Info Table to check for support. */ - if (nv50_disp(encoder->dev)->disp->oclass >= GF110_DISP) { + if (disp->disp->oclass >= GF110_DISP) { ret = nv50_mstm_new(nv_encoder, &nv_connector->aux, 16, nv_connector->base.base.id, &nv_encoder->dp.mstm); @@ -3931,6 +3940,8 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) NV_ATOMIC(drm, "%s: clr %04x (set %04x)\n", crtc->name, asyh->clr.mask, asyh->set.mask); + if (crtc_state->active && !asyh->state.active) + drm_crtc_vblank_off(crtc); if (asyh->clr.mask) { nv50_head_flush_clr(head, asyh, atom->flush_disable); @@ -4016,11 +4027,13 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) nv50_head_flush_set(head, asyh); interlock_core = 1; } - } - for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (crtc->state->event) - drm_crtc_vblank_get(crtc); + if (asyh->state.active) { + if (!crtc_state->active) + drm_crtc_vblank_on(crtc); + if (asyh->state.event) + drm_crtc_vblank_get(crtc); + } } /* Update plane(s). */ @@ -4067,12 +4080,14 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) if (crtc->state->event) { unsigned long flags; /* Get correct count/ts if racing with vblank irq */ - drm_accurate_vblank_count(crtc); + if (crtc->state->active) + drm_accurate_vblank_count(crtc); spin_lock_irqsave(&crtc->dev->event_lock, flags); drm_crtc_send_vblank_event(crtc, crtc->state->event); spin_unlock_irqrestore(&crtc->dev->event_lock, flags); crtc->state->event = NULL; - drm_crtc_vblank_put(crtc); + if (crtc->state->active) + drm_crtc_vblank_put(crtc); } } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c index c7c84d34d97e..88582af8bd89 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c @@ -267,6 +267,8 @@ nvkm_disp_oneinit(struct nvkm_engine *engine) /* Create output path objects for each VBIOS display path. */ i = -1; while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { + if (ver < 0x40) /* No support for chipsets prior to NV50. */ + break; if (dcbE.type == DCB_OUTPUT_UNUSED) continue; if (dcbE.type == DCB_OUTPUT_EOL) diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index a24312fb0228..a1e8bf48b778 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -22,6 +22,7 @@ struct nvkm_ior { unsigned proto_evo:4; enum nvkm_ior_proto { CRT, + TV, TMDS, LVDS, DP, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h index 19c635663399..6ea19466f436 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h @@ -22,7 +22,7 @@ struct nv50_disp { u8 type[3]; } pior; - struct nv50_disp_chan *chan[17]; + struct nv50_disp_chan *chan[21]; }; void nv50_disp_super_1(struct nv50_disp *); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c index 85aff85394ac..be9e7f8c3b23 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c @@ -62,6 +62,7 @@ nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type) case 0: switch (outp->info.type) { case DCB_OUTPUT_ANALOG: *type = DAC; return CRT; + case DCB_OUTPUT_TV : *type = DAC; return TV; case DCB_OUTPUT_TMDS : *type = SOR; return TMDS; case DCB_OUTPUT_LVDS : *type = SOR; return LVDS; case DCB_OUTPUT_DP : *type = SOR; return DP; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c index c794b2c2d21e..6d8f21290aa2 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bar/gf100.c @@ -129,7 +129,7 @@ gf100_bar_init(struct nvkm_bar *base) if (bar->bar[0].mem) { addr = nvkm_memory_addr(bar->bar[0].mem) >> 12; - nvkm_wr32(device, 0x001714, 0xc0000000 | addr); + nvkm_wr32(device, 0x001714, 0x80000000 | addr); } return 0; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild index 48f01e40b8fc..b768e66a472b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/Kbuild @@ -25,6 +25,7 @@ nvkm-y += nvkm/subdev/i2c/bit.o nvkm-y += nvkm/subdev/i2c/aux.o nvkm-y += nvkm/subdev/i2c/auxg94.o +nvkm-y += nvkm/subdev/i2c/auxgf119.o nvkm-y += nvkm/subdev/i2c/auxgm200.o nvkm-y += nvkm/subdev/i2c/anx9805.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c index d172e42dd228..4c1f547da463 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.c @@ -117,6 +117,10 @@ int nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *aux, bool retry, u8 type, u32 addr, u8 *data, u8 *size) { + if (!*size && !aux->func->address_only) { + AUX_ERR(aux, "address-only transaction dropped"); + return -ENOSYS; + } return aux->func->xfer(aux, retry, type, addr, data, size); } diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h index 27a4a39c87f0..9587ab456d9e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/aux.h @@ -3,6 +3,7 @@ #include "pad.h" struct nvkm_i2c_aux_func { + bool address_only; int (*xfer)(struct nvkm_i2c_aux *, bool retry, u8 type, u32 addr, u8 *data, u8 *size); int (*lnk_ctl)(struct nvkm_i2c_aux *, int link_nr, int link_bw, @@ -17,7 +18,12 @@ void nvkm_i2c_aux_del(struct nvkm_i2c_aux **); int nvkm_i2c_aux_xfer(struct nvkm_i2c_aux *, bool retry, u8 type, u32 addr, u8 *data, u8 *size); +int g94_i2c_aux_new_(const struct nvkm_i2c_aux_func *, struct nvkm_i2c_pad *, + int, u8, struct nvkm_i2c_aux **); + int g94_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **); +int g94_i2c_aux_xfer(struct nvkm_i2c_aux *, bool, u8, u32, u8 *, u8 *); +int gf119_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **); int gm200_i2c_aux_new(struct nvkm_i2c_pad *, int, u8, struct nvkm_i2c_aux **); #define AUX_MSG(b,l,f,a...) do { \ diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c index ab8cb196c34e..c8ab1b5741a3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxg94.c @@ -72,7 +72,7 @@ g94_i2c_aux_init(struct g94_i2c_aux *aux) return 0; } -static int +int g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry, u8 type, u32 addr, u8 *data, u8 *size) { @@ -105,9 +105,9 @@ g94_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry, } ctrl = nvkm_rd32(device, 0x00e4e4 + base); - ctrl &= ~0x0001f0ff; + ctrl &= ~0x0001f1ff; ctrl |= type << 12; - ctrl |= *size - 1; + ctrl |= (*size ? (*size - 1) : 0x00000100); nvkm_wr32(device, 0x00e4e0 + base, addr); /* (maybe) retry transaction a number of times on failure... */ @@ -160,14 +160,10 @@ out: return ret < 0 ? ret : (stat & 0x000f0000) >> 16; } -static const struct nvkm_i2c_aux_func -g94_i2c_aux_func = { - .xfer = g94_i2c_aux_xfer, -}; - int -g94_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive, - struct nvkm_i2c_aux **paux) +g94_i2c_aux_new_(const struct nvkm_i2c_aux_func *func, + struct nvkm_i2c_pad *pad, int index, u8 drive, + struct nvkm_i2c_aux **paux) { struct g94_i2c_aux *aux; @@ -175,8 +171,20 @@ g94_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive, return -ENOMEM; *paux = &aux->base; - nvkm_i2c_aux_ctor(&g94_i2c_aux_func, pad, index, &aux->base); + nvkm_i2c_aux_ctor(func, pad, index, &aux->base); aux->ch = drive; aux->base.intr = 1 << aux->ch; return 0; } + +static const struct nvkm_i2c_aux_func +g94_i2c_aux = { + .xfer = g94_i2c_aux_xfer, +}; + +int +g94_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive, + struct nvkm_i2c_aux **paux) +{ + return g94_i2c_aux_new_(&g94_i2c_aux, pad, index, drive, paux); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c new file mode 100644 index 000000000000..dab40cd8fe3a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgf119.c @@ -0,0 +1,35 @@ +/* + * Copyright 2017 Red Hat 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. + */ +#include "aux.h" + +static const struct nvkm_i2c_aux_func +gf119_i2c_aux = { + .address_only = true, + .xfer = g94_i2c_aux_xfer, +}; + +int +gf119_i2c_aux_new(struct nvkm_i2c_pad *pad, int index, u8 drive, + struct nvkm_i2c_aux **paux) +{ + return g94_i2c_aux_new_(&gf119_i2c_aux, pad, index, drive, paux); +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c index ee091fa79628..7ef60895f43a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/auxgm200.c @@ -105,9 +105,9 @@ gm200_i2c_aux_xfer(struct nvkm_i2c_aux *obj, bool retry, } ctrl = nvkm_rd32(device, 0x00d954 + base); - ctrl &= ~0x0001f0ff; + ctrl &= ~0x0001f1ff; ctrl |= type << 12; - ctrl |= *size - 1; + ctrl |= (*size ? (*size - 1) : 0x00000100); nvkm_wr32(device, 0x00d950 + base, addr); /* (maybe) retry transaction a number of times on failure... */ @@ -162,6 +162,7 @@ out: static const struct nvkm_i2c_aux_func gm200_i2c_aux_func = { + .address_only = true, .xfer = gm200_i2c_aux_xfer, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c index d53212f1aa52..3bc4d0310076 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/i2c/padgf119.c @@ -28,7 +28,7 @@ static const struct nvkm_i2c_pad_func gf119_i2c_pad_s_func = { .bus_new_4 = gf119_i2c_bus_new, - .aux_new_6 = g94_i2c_aux_new, + .aux_new_6 = gf119_i2c_aux_new, .mode = g94_i2c_pad_mode, }; @@ -41,7 +41,7 @@ gf119_i2c_pad_s_new(struct nvkm_i2c *i2c, int id, struct nvkm_i2c_pad **ppad) static const struct nvkm_i2c_pad_func gf119_i2c_pad_x_func = { .bus_new_4 = gf119_i2c_bus_new, - .aux_new_6 = g94_i2c_aux_new, + .aux_new_6 = gf119_i2c_aux_new, }; int diff --git a/drivers/gpu/drm/radeon/radeon_kfd.c b/drivers/gpu/drm/radeon/radeon_kfd.c index 699fe7f9b8bf..a2ab6dcdf4a2 100644 --- a/drivers/gpu/drm/radeon/radeon_kfd.c +++ b/drivers/gpu/drm/radeon/radeon_kfd.c @@ -184,7 +184,6 @@ void radeon_kfd_device_init(struct radeon_device *rdev) if (rdev->kfd) { struct kgd2kfd_shared_resources gpu_resources = { .compute_vmid_bitmap = 0xFF00, - .num_mec = 1, .num_pipe_per_mec = 4, .num_queue_per_pipe = 8 }; diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig index 50c41c0a50ef..dcc539ba85d6 100644 --- a/drivers/gpu/drm/rockchip/Kconfig +++ b/drivers/gpu/drm/rockchip/Kconfig @@ -5,6 +5,10 @@ config DRM_ROCKCHIP select DRM_KMS_HELPER select DRM_PANEL select VIDEOMODE_HELPERS + select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP + select DRM_DW_HDMI if ROCKCHIP_DW_HDMI + select DRM_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI + select SND_SOC_HDMI_CODEC if ROCKCHIP_CDN_DP && SND_SOC help Choose this option if you have a Rockchip soc chipset. This driver provides kernel mode setting and buffer @@ -12,10 +16,10 @@ config DRM_ROCKCHIP 2D or 3D acceleration; acceleration is performed by other IP found on the SoC. +if DRM_ROCKCHIP + config ROCKCHIP_ANALOGIX_DP bool "Rockchip specific extensions for Analogix DP driver" - depends on DRM_ROCKCHIP - select DRM_ANALOGIX_DP help This selects support for Rockchip SoC specific extensions for the Analogix Core DP driver. If you want to enable DP @@ -23,9 +27,7 @@ config ROCKCHIP_ANALOGIX_DP config ROCKCHIP_CDN_DP bool "Rockchip cdn DP" - depends on DRM_ROCKCHIP - depends on EXTCON - select SND_SOC_HDMI_CODEC if SND_SOC + depends on EXTCON=y || (EXTCON=m && DRM_ROCKCHIP=m) help This selects support for Rockchip SoC specific extensions for the cdn DP driver. If you want to enable Dp on @@ -34,8 +36,6 @@ config ROCKCHIP_CDN_DP config ROCKCHIP_DW_HDMI bool "Rockchip specific extensions for Synopsys DW HDMI" - depends on DRM_ROCKCHIP - select DRM_DW_HDMI help This selects support for Rockchip SoC specific extensions for the Synopsys DesignWare HDMI driver. If you want to @@ -44,8 +44,6 @@ config ROCKCHIP_DW_HDMI config ROCKCHIP_DW_MIPI_DSI bool "Rockchip specific extensions for Synopsys DW MIPI DSI" - depends on DRM_ROCKCHIP - select DRM_MIPI_DSI help This selects support for Rockchip SoC specific extensions for the Synopsys DesignWare HDMI driver. If you want to @@ -54,8 +52,9 @@ config ROCKCHIP_DW_MIPI_DSI config ROCKCHIP_INNO_HDMI bool "Rockchip specific extensions for Innosilicon HDMI" - depends on DRM_ROCKCHIP help This selects support for Rockchip SoC specific extensions for the Innosilicon HDMI driver. If you want to enable HDMI on RK3036 based SoC, you should select this option. + +endif diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index c6b1b7f3a2a3..c16bc0a7115b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -275,11 +275,15 @@ static void rockchip_drm_fb_resume(struct drm_device *drm) static int rockchip_drm_sys_suspend(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct rockchip_drm_private *priv = drm->dev_private; + struct rockchip_drm_private *priv; + + if (!drm) + return 0; drm_kms_helper_poll_disable(drm); rockchip_drm_fb_suspend(drm); + priv = drm->dev_private; priv->state = drm_atomic_helper_suspend(drm); if (IS_ERR(priv->state)) { rockchip_drm_fb_resume(drm); @@ -293,8 +297,12 @@ static int rockchip_drm_sys_suspend(struct device *dev) static int rockchip_drm_sys_resume(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); - struct rockchip_drm_private *priv = drm->dev_private; + struct rockchip_drm_private *priv; + + if (!drm) + return 0; + priv = drm->dev_private; drm_atomic_helper_resume(drm, priv->state); rockchip_drm_fb_resume(drm); drm_kms_helper_poll_enable(drm); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 5d450332c2fd..2900f1410d95 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -500,7 +500,7 @@ static void vop_line_flag_irq_disable(struct vop *vop) static int vop_enable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); - int ret; + int ret, i; ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { @@ -533,6 +533,20 @@ static int vop_enable(struct drm_crtc *crtc) } memcpy(vop->regs, vop->regsbak, vop->len); + /* + * We need to make sure that all windows are disabled before we + * enable the crtc. Otherwise we might try to scan from a destroyed + * buffer later. + */ + for (i = 0; i < vop->data->win_size; i++) { + struct vop_win *vop_win = &vop->win[i]; + const struct vop_win_data *win = vop_win->data; + + spin_lock(&vop->reg_lock); + VOP_WIN_SET(vop, win, enable, 0); + spin_unlock(&vop->reg_lock); + } + vop_cfg_done(vop); /* @@ -566,28 +580,11 @@ err_put_pm_runtime: static void vop_crtc_disable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); - int i; WARN_ON(vop->event); rockchip_drm_psr_deactivate(&vop->crtc); - /* - * We need to make sure that all windows are disabled before we - * disable that crtc. Otherwise we might try to scan from a destroyed - * buffer later. - */ - for (i = 0; i < vop->data->win_size; i++) { - struct vop_win *vop_win = &vop->win[i]; - const struct vop_win_data *win = vop_win->data; - - spin_lock(&vop->reg_lock); - VOP_WIN_SET(vop, win, enable, 0); - spin_unlock(&vop->reg_lock); - } - - vop_cfg_done(vop); - drm_crtc_vblank_off(crtc); /* @@ -682,8 +679,10 @@ static int vop_plane_atomic_check(struct drm_plane *plane, * Src.x1 can be odd when do clip, but yuv plane start point * need align with 2 pixel. */ - if (is_yuv_support(fb->format->format) && ((state->src.x1 >> 16) % 2)) + if (is_yuv_support(fb->format->format) && ((state->src.x1 >> 16) % 2)) { + DRM_ERROR("Invalid Source: Yuv format not support odd xpos\n"); return -EINVAL; + } return 0; } @@ -764,7 +763,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, spin_lock(&vop->reg_lock); VOP_WIN_SET(vop, win, format, format); - VOP_WIN_SET(vop, win, yrgb_vir, fb->pitches[0] >> 2); + VOP_WIN_SET(vop, win, yrgb_vir, DIV_ROUND_UP(fb->pitches[0], 4)); VOP_WIN_SET(vop, win, yrgb_mst, dma_addr); if (is_yuv_support(fb->format->format)) { int hsub = drm_format_horz_chroma_subsampling(fb->format->format); @@ -778,7 +777,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, offset += (src->y1 >> 16) * fb->pitches[1] / vsub; dma_addr = rk_uv_obj->dma_addr + offset + fb->offsets[1]; - VOP_WIN_SET(vop, win, uv_vir, fb->pitches[1] >> 2); + VOP_WIN_SET(vop, win, uv_vir, DIV_ROUND_UP(fb->pitches[1], 4)); VOP_WIN_SET(vop, win, uv_mst, dma_addr); } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h index 9979fd0c2282..27eefbfcf3d0 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h @@ -282,6 +282,9 @@ static inline uint16_t scl_get_bili_dn_vskip(int src_h, int dst_h, act_height = (src_h + vskiplines - 1) / vskiplines; + if (act_height == dst_h) + return GET_SCL_FT_BILI_DN(src_h, dst_h) / vskiplines; + return GET_SCL_FT_BILI_DN(act_height, dst_h); } diff --git a/drivers/gpu/drm/stm/Kconfig b/drivers/gpu/drm/stm/Kconfig index 2c4817fb0890..8fe5b184b4e8 100644 --- a/drivers/gpu/drm/stm/Kconfig +++ b/drivers/gpu/drm/stm/Kconfig @@ -7,7 +7,6 @@ config DRM_STM select DRM_PANEL select VIDEOMODE_HELPERS select FB_PROVIDE_GET_FB_UNMAPPED_AREA - default y help Enable support for the on-chip display controller on diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index abc7d8fe06b4..a45a627283a1 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -25,12 +25,20 @@ #include "sun4i_framebuffer.h" #include "sun4i_tcon.h" +static void sun4i_drv_lastclose(struct drm_device *dev) +{ + struct sun4i_drv *drv = dev->dev_private; + + drm_fbdev_cma_restore_mode(drv->fbdev); +} + DEFINE_DRM_GEM_CMA_FOPS(sun4i_drv_fops); static struct drm_driver sun4i_drv_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC, /* Generic Operations */ + .lastclose = sun4i_drv_lastclose, .fops = &sun4i_drv_fops, .name = "sun4i-drm", .desc = "Allwinner sun4i Display Engine", diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 403bbd5f99a9..a12cc7ea99b6 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -520,6 +520,34 @@ static void vc4_crtc_disable(struct drm_crtc *crtc) SCALER_DISPSTATX_EMPTY); } +static void vc4_crtc_update_dlist(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); + + if (crtc->state->event) { + unsigned long flags; + + crtc->state->event->pipe = drm_crtc_index(crtc); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irqsave(&dev->event_lock, flags); + vc4_crtc->event = crtc->state->event; + crtc->state->event = NULL; + + HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), + vc4_state->mm.start); + + spin_unlock_irqrestore(&dev->event_lock, flags); + } else { + HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), + vc4_state->mm.start); + } +} + static void vc4_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -530,6 +558,12 @@ static void vc4_crtc_enable(struct drm_crtc *crtc) require_hvs_enabled(dev); + /* Enable vblank irq handling before crtc is started otherwise + * drm_crtc_get_vblank() fails in vc4_crtc_update_dlist(). + */ + drm_crtc_vblank_on(crtc); + vc4_crtc_update_dlist(crtc); + /* Turn on the scaler, which will wait for vstart to start * compositing. */ @@ -541,9 +575,6 @@ static void vc4_crtc_enable(struct drm_crtc *crtc) /* Turn on the pixel valve, which will emit the vstart signal. */ CRTC_WRITE(PV_V_CONTROL, CRTC_READ(PV_V_CONTROL) | PV_VCONTROL_VIDEN); - - /* Enable vblank irq handling after crtc is started. */ - drm_crtc_vblank_on(crtc); } static bool vc4_crtc_mode_fixup(struct drm_crtc *crtc, @@ -598,7 +629,6 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct vc4_dev *vc4 = to_vc4_dev(dev); - struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); struct drm_plane *plane; bool debug_dump_regs = false; @@ -620,25 +650,15 @@ static void vc4_crtc_atomic_flush(struct drm_crtc *crtc, WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size); - if (crtc->state->event) { - unsigned long flags; - - crtc->state->event->pipe = drm_crtc_index(crtc); - - WARN_ON(drm_crtc_vblank_get(crtc) != 0); - - spin_lock_irqsave(&dev->event_lock, flags); - vc4_crtc->event = crtc->state->event; - crtc->state->event = NULL; - - HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), - vc4_state->mm.start); - - spin_unlock_irqrestore(&dev->event_lock, flags); - } else { - HVS_WRITE(SCALER_DISPLISTX(vc4_crtc->channel), - vc4_state->mm.start); - } + /* Only update DISPLIST if the CRTC was already running and is not + * being disabled. + * vc4_crtc_enable() takes care of updating the dlist just after + * re-enabling VBLANK interrupts and before enabling the engine. + * If the CRTC is being disabled, there's no point in updating this + * information. + */ + if (crtc->state->active && old_state->active) + vc4_crtc_update_dlist(crtc); if (debug_dump_regs) { DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c index 35bf781e418e..c7056322211c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c @@ -30,49 +30,49 @@ #include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_page_alloc.h> -static struct ttm_place vram_placement_flags = { +static const struct ttm_place vram_placement_flags = { .fpfn = 0, .lpfn = 0, .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED }; -static struct ttm_place vram_ne_placement_flags = { +static const struct ttm_place vram_ne_placement_flags = { .fpfn = 0, .lpfn = 0, .flags = TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT }; -static struct ttm_place sys_placement_flags = { +static const struct ttm_place sys_placement_flags = { .fpfn = 0, .lpfn = 0, .flags = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED }; -static struct ttm_place sys_ne_placement_flags = { +static const struct ttm_place sys_ne_placement_flags = { .fpfn = 0, .lpfn = 0, .flags = TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT }; -static struct ttm_place gmr_placement_flags = { +static const struct ttm_place gmr_placement_flags = { .fpfn = 0, .lpfn = 0, .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED }; -static struct ttm_place gmr_ne_placement_flags = { +static const struct ttm_place gmr_ne_placement_flags = { .fpfn = 0, .lpfn = 0, .flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT }; -static struct ttm_place mob_placement_flags = { +static const struct ttm_place mob_placement_flags = { .fpfn = 0, .lpfn = 0, .flags = VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED }; -static struct ttm_place mob_ne_placement_flags = { +static const struct ttm_place mob_ne_placement_flags = { .fpfn = 0, .lpfn = 0, .flags = VMW_PL_FLAG_MOB | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT @@ -85,7 +85,7 @@ struct ttm_placement vmw_vram_placement = { .busy_placement = &vram_placement_flags }; -static struct ttm_place vram_gmr_placement_flags[] = { +static const struct ttm_place vram_gmr_placement_flags[] = { { .fpfn = 0, .lpfn = 0, @@ -97,7 +97,7 @@ static struct ttm_place vram_gmr_placement_flags[] = { } }; -static struct ttm_place gmr_vram_placement_flags[] = { +static const struct ttm_place gmr_vram_placement_flags[] = { { .fpfn = 0, .lpfn = 0, @@ -116,7 +116,7 @@ struct ttm_placement vmw_vram_gmr_placement = { .busy_placement = &gmr_placement_flags }; -static struct ttm_place vram_gmr_ne_placement_flags[] = { +static const struct ttm_place vram_gmr_ne_placement_flags[] = { { .fpfn = 0, .lpfn = 0, @@ -165,7 +165,7 @@ struct ttm_placement vmw_sys_ne_placement = { .busy_placement = &sys_ne_placement_flags }; -static struct ttm_place evictable_placement_flags[] = { +static const struct ttm_place evictable_placement_flags[] = { { .fpfn = 0, .lpfn = 0, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c index 99a7f4ab7d97..86178796de6c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c @@ -779,8 +779,8 @@ static int vmw_cmdbuf_space_pool(struct vmw_cmdbuf_man *man, if (ret) return ret; - header->cb_header = dma_pool_alloc(man->headers, GFP_KERNEL, - &header->handle); + header->cb_header = dma_pool_zalloc(man->headers, GFP_KERNEL, + &header->handle); if (!header->cb_header) { ret = -ENOMEM; goto out_no_cb_header; @@ -790,7 +790,6 @@ static int vmw_cmdbuf_space_pool(struct vmw_cmdbuf_man *man, cb_hdr = header->cb_header; offset = header->node.start << PAGE_SHIFT; header->cmd = man->map + offset; - memset(cb_hdr, 0, sizeof(*cb_hdr)); if (man->using_mob) { cb_hdr->flags = SVGA_CB_FLAG_MOB; cb_hdr->ptr.mob.mobid = man->cmd_space->mem.start; @@ -827,8 +826,8 @@ static int vmw_cmdbuf_space_inline(struct vmw_cmdbuf_man *man, if (WARN_ON_ONCE(size > VMW_CMDBUF_INLINE_SIZE)) return -ENOMEM; - dheader = dma_pool_alloc(man->dheaders, GFP_KERNEL, - &header->handle); + dheader = dma_pool_zalloc(man->dheaders, GFP_KERNEL, + &header->handle); if (!dheader) return -ENOMEM; @@ -837,7 +836,6 @@ static int vmw_cmdbuf_space_inline(struct vmw_cmdbuf_man *man, cb_hdr = &dheader->cb_header; header->cb_header = cb_hdr; header->cmd = dheader->cmd; - memset(dheader, 0, sizeof(*dheader)); cb_hdr->status = SVGA_CB_STATUS_NONE; cb_hdr->flags = SVGA_CB_FLAG_NONE; cb_hdr->ptr.pa = (u64)header->handle + diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c index 1f013d45c9e9..36c7b6c839c0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf_res.c @@ -205,7 +205,7 @@ int vmw_cmdbuf_res_add(struct vmw_cmdbuf_res_manager *man, int ret; cres = kzalloc(sizeof(*cres), GFP_KERNEL); - if (unlikely(cres == NULL)) + if (unlikely(!cres)) return -ENOMEM; cres->hash.key = user_key | (res_type << 24); @@ -291,7 +291,7 @@ vmw_cmdbuf_res_man_create(struct vmw_private *dev_priv) int ret; man = kzalloc(sizeof(*man), GFP_KERNEL); - if (man == NULL) + if (!man) return ERR_PTR(-ENOMEM); man->dev_priv = dev_priv; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c index bcc6d4136c87..4212b3e673bc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c @@ -210,8 +210,8 @@ static int vmw_gb_context_init(struct vmw_private *dev_priv, for (i = 0; i < SVGA_COTABLE_DX10_MAX; ++i) { uctx->cotables[i] = vmw_cotable_alloc(dev_priv, &uctx->res, i); - if (unlikely(uctx->cotables[i] == NULL)) { - ret = -ENOMEM; + if (unlikely(IS_ERR(uctx->cotables[i]))) { + ret = PTR_ERR(uctx->cotables[i]); goto out_cotables; } } @@ -777,7 +777,7 @@ static int vmw_context_define(struct drm_device *dev, void *data, } ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (unlikely(ctx == NULL)) { + if (unlikely(!ctx)) { ttm_mem_global_free(vmw_mem_glob(dev_priv), vmw_user_context_size); ret = -ENOMEM; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c index 6c026d75c180..d87861bbe971 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c @@ -584,7 +584,7 @@ struct vmw_resource *vmw_cotable_alloc(struct vmw_private *dev_priv, return ERR_PTR(ret); vcotbl = kzalloc(sizeof(*vcotbl), GFP_KERNEL); - if (unlikely(vcotbl == NULL)) { + if (unlikely(!vcotbl)) { ret = -ENOMEM; goto out_no_alloc; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 4a641555b960..4436d53ae16c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -227,7 +227,7 @@ static const struct drm_ioctl_desc vmw_ioctls[] = { DRM_AUTH | DRM_RENDER_ALLOW), }; -static struct pci_device_id vmw_pci_id_list[] = { +static const struct pci_device_id vmw_pci_id_list[] = { {0x15ad, 0x0405, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VMWGFX_CHIP_SVGAII}, {0, 0, 0} }; @@ -630,7 +630,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) char host_log[100] = {0}; dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); - if (unlikely(dev_priv == NULL)) { + if (unlikely(!dev_priv)) { DRM_ERROR("Failed allocating a device private struct.\n"); return -ENOMEM; } @@ -1035,7 +1035,7 @@ static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv) int ret = -ENOMEM; vmw_fp = kzalloc(sizeof(*vmw_fp), GFP_KERNEL); - if (unlikely(vmw_fp == NULL)) + if (unlikely(!vmw_fp)) return ret; vmw_fp->tfile = ttm_object_file_init(dev_priv->tdev, 10); @@ -1196,7 +1196,7 @@ static int vmw_master_create(struct drm_device *dev, struct vmw_master *vmaster; vmaster = kzalloc(sizeof(*vmaster), GFP_KERNEL); - if (unlikely(vmaster == NULL)) + if (unlikely(!vmaster)) return -ENOMEM; vmw_master_init(vmaster); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index c7b53d987f06..2cfb3c93f42a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -264,7 +264,7 @@ static int vmw_resource_val_add(struct vmw_sw_context *sw_context, } node = kzalloc(sizeof(*node), GFP_KERNEL); - if (unlikely(node == NULL)) { + if (unlikely(!node)) { DRM_ERROR("Failed to allocate a resource validation " "entry.\n"); return -ENOMEM; @@ -452,7 +452,7 @@ static int vmw_resource_relocation_add(struct list_head *list, struct vmw_resource_relocation *rel; rel = kmalloc(sizeof(*rel), GFP_KERNEL); - if (unlikely(rel == NULL)) { + if (unlikely(!rel)) { DRM_ERROR("Failed to allocate a resource relocation.\n"); return -ENOMEM; } @@ -519,7 +519,7 @@ static int vmw_cmd_invalid(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGA3dCmdHeader *header) { - return capable(CAP_SYS_ADMIN) ? : -EINVAL; + return -EINVAL; } static int vmw_cmd_ok(struct vmw_private *dev_priv, @@ -2584,7 +2584,7 @@ static int vmw_cmd_dx_set_vertex_buffers(struct vmw_private *dev_priv, /** * vmw_cmd_dx_ia_set_vertex_buffers - Validate an - * SVGA_3D_CMD_DX_IA_SET_VERTEX_BUFFERS command. + * SVGA_3D_CMD_DX_IA_SET_INDEX_BUFFER command. * * @dev_priv: Pointer to a device private struct. * @sw_context: The software context being used for this batch. diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 6b2708b4eafe..b8bc5bc7de7e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -284,7 +284,7 @@ struct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv) { struct vmw_fence_manager *fman = kzalloc(sizeof(*fman), GFP_KERNEL); - if (unlikely(fman == NULL)) + if (unlikely(!fman)) return NULL; fman->dev_priv = dev_priv; @@ -541,7 +541,7 @@ int vmw_fence_create(struct vmw_fence_manager *fman, int ret; fence = kzalloc(sizeof(*fence), GFP_KERNEL); - if (unlikely(fence == NULL)) + if (unlikely(!fence)) return -ENOMEM; ret = vmw_fence_obj_init(fman, fence, seqno, @@ -606,7 +606,7 @@ int vmw_user_fence_create(struct drm_file *file_priv, return ret; ufence = kzalloc(sizeof(*ufence), GFP_KERNEL); - if (unlikely(ufence == NULL)) { + if (unlikely(!ufence)) { ret = -ENOMEM; goto out_no_object; } @@ -966,7 +966,7 @@ int vmw_event_fence_action_queue(struct drm_file *file_priv, struct vmw_fence_manager *fman = fman_from_fence(fence); eaction = kzalloc(sizeof(*eaction), GFP_KERNEL); - if (unlikely(eaction == NULL)) + if (unlikely(!eaction)) return -ENOMEM; eaction->event = event; @@ -1002,7 +1002,7 @@ static int vmw_event_fence_action_create(struct drm_file *file_priv, int ret; event = kzalloc(sizeof(*event), GFP_KERNEL); - if (unlikely(event == NULL)) { + if (unlikely(!event)) { DRM_ERROR("Failed to allocate an event.\n"); ret = -ENOMEM; goto out_no_space; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index c1900f4390a4..d2b03d4a3c86 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -121,7 +121,7 @@ static int vmw_gmrid_man_init(struct ttm_mem_type_manager *man, struct vmwgfx_gmrid_man *gman = kzalloc(sizeof(*gman), GFP_KERNEL); - if (unlikely(gman == NULL)) + if (unlikely(!gman)) return -ENOMEM; spin_lock_init(&gman->lock); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 3d94ea67a825..61e06f0e8cd3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -384,6 +384,12 @@ vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, hotspot_x = du->hotspot_x; hotspot_y = du->hotspot_y; + + if (plane->fb) { + hotspot_x += plane->fb->hot_x; + hotspot_y += plane->fb->hot_y; + } + du->cursor_surface = vps->surf; du->cursor_dmabuf = vps->dmabuf; @@ -411,6 +417,9 @@ vmw_du_cursor_plane_atomic_update(struct drm_plane *plane, vmw_cursor_update_position(dev_priv, true, du->cursor_x + hotspot_x, du->cursor_y + hotspot_y); + + du->core_hotspot_x = hotspot_x - du->hotspot_x; + du->core_hotspot_y = hotspot_y - du->hotspot_y; } else { DRM_ERROR("Failed to update cursor image\n"); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c index 941bcfd131ff..b17f08fc50d3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_mob.c @@ -320,14 +320,14 @@ int vmw_otables_setup(struct vmw_private *dev_priv) if (dev_priv->has_dx) { *otables = kmemdup(dx_tables, sizeof(dx_tables), GFP_KERNEL); - if (*otables == NULL) + if (!(*otables)) return -ENOMEM; dev_priv->otable_batch.num_otables = ARRAY_SIZE(dx_tables); } else { *otables = kmemdup(pre_dx_tables, sizeof(pre_dx_tables), GFP_KERNEL); - if (*otables == NULL) + if (!(*otables)) return -ENOMEM; dev_priv->otable_batch.num_otables = ARRAY_SIZE(pre_dx_tables); @@ -407,7 +407,7 @@ struct vmw_mob *vmw_mob_create(unsigned long data_pages) { struct vmw_mob *mob = kzalloc(sizeof(*mob), GFP_KERNEL); - if (unlikely(mob == NULL)) + if (unlikely(!mob)) return NULL; mob->num_pages = vmw_mob_calculate_pt_pages(data_pages); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c index 6063c9636d4a..97000996b8dc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c @@ -244,7 +244,7 @@ static int vmw_recv_msg(struct rpc_channel *channel, void **msg, reply_len = ebx; reply = kzalloc(reply_len + 1, GFP_KERNEL); - if (reply == NULL) { + if (!reply) { DRM_ERROR("Cannot allocate memory for reply\n"); return -ENOMEM; } @@ -340,7 +340,7 @@ int vmw_host_get_guestinfo(const char *guest_info_param, msg_len = strlen(guest_info_param) + strlen("info-get ") + 1; msg = kzalloc(msg_len, GFP_KERNEL); - if (msg == NULL) { + if (!msg) { DRM_ERROR("Cannot allocate memory to get %s", guest_info_param); return -ENOMEM; } @@ -400,7 +400,7 @@ int vmw_host_log(const char *log) msg_len = strlen(log) + strlen("log ") + 1; msg = kzalloc(msg_len, GFP_KERNEL); - if (msg == NULL) { + if (!msg) { DRM_ERROR("Cannot allocate memory for log message\n"); return -ENOMEM; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 7d591f653dfa..a96f90f017d1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -446,7 +446,7 @@ int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, int ret; user_bo = kzalloc(sizeof(*user_bo), GFP_KERNEL); - if (unlikely(user_bo == NULL)) { + if (unlikely(!user_bo)) { DRM_ERROR("Failed to allocate a buffer.\n"); return -ENOMEM; } @@ -836,7 +836,7 @@ static int vmw_resource_buf_alloc(struct vmw_resource *res, } backup = kzalloc(sizeof(*backup), GFP_KERNEL); - if (unlikely(backup == NULL)) + if (unlikely(!backup)) return -ENOMEM; ret = vmw_dmabuf_init(res->dev_priv, backup, res->backup_size, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c index 68f135c5b0d8..9b832f136813 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -751,7 +751,7 @@ static int vmw_user_shader_alloc(struct vmw_private *dev_priv, } ushader = kzalloc(sizeof(*ushader), GFP_KERNEL); - if (unlikely(ushader == NULL)) { + if (unlikely(!ushader)) { ttm_mem_global_free(vmw_mem_glob(dev_priv), vmw_user_shader_size); ret = -ENOMEM; @@ -821,7 +821,7 @@ static struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv, } shader = kzalloc(sizeof(*shader), GFP_KERNEL); - if (unlikely(shader == NULL)) { + if (unlikely(!shader)) { ttm_mem_global_free(vmw_mem_glob(dev_priv), vmw_shader_size); ret = -ENOMEM; @@ -981,7 +981,7 @@ int vmw_compat_shader_add(struct vmw_private *dev_priv, /* Allocate and pin a DMA buffer */ buf = kzalloc(sizeof(*buf), GFP_KERNEL); - if (unlikely(buf == NULL)) + if (unlikely(!buf)) return -ENOMEM; ret = vmw_dmabuf_init(dev_priv, buf, size, &vmw_sys_ne_placement, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 50be1f034f9e..5284e8d2f7ba 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -1640,8 +1640,8 @@ int vmw_kms_stdu_init_display(struct vmw_private *dev_priv) * something arbitrarily large and we will reject any layout * that doesn't fit prim_bb_mem later */ - dev->mode_config.max_width = 16384; - dev->mode_config.max_height = 16384; + dev->mode_config.max_width = 8192; + dev->mode_config.max_height = 8192; } vmw_kms_create_implicit_placement_property(dev_priv, false); diff --git a/drivers/gpu/host1x/dev.c b/drivers/gpu/host1x/dev.c index 2c58a390123a..778272514164 100644 --- a/drivers/gpu/host1x/dev.c +++ b/drivers/gpu/host1x/dev.c @@ -186,8 +186,13 @@ static int host1x_probe(struct platform_device *pdev) return -ENOMEM; err = iommu_attach_device(host->domain, &pdev->dev); - if (err) + if (err == -ENODEV) { + iommu_domain_free(host->domain); + host->domain = NULL; + goto skip_iommu; + } else if (err) { goto fail_free_domain; + } geometry = &host->domain->geometry; @@ -198,6 +203,7 @@ static int host1x_probe(struct platform_device *pdev) host->iova_end = geometry->aperture_end; } +skip_iommu: err = host1x_channel_list_init(&host->channel_list, host->info->nb_channels); if (err) { diff --git a/drivers/gpu/ipu-v3/Kconfig b/drivers/gpu/ipu-v3/Kconfig index 08766c6e7856..87a20b3dcf7a 100644 --- a/drivers/gpu/ipu-v3/Kconfig +++ b/drivers/gpu/ipu-v3/Kconfig @@ -1,6 +1,7 @@ config IMX_IPUV3_CORE tristate "IPUv3 core support" depends on SOC_IMX5 || SOC_IMX6Q || ARCH_MULTIPLATFORM + depends on DRM || !DRM # if DRM=m, this can't be 'y' select GENERIC_IRQ_CHIP help Choose this if you have a i.MX5/6 system and want to use the Image diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6fd01a692197..9017dcc14502 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2216,6 +2216,7 @@ static const struct hid_device_id hid_have_special_driver[] = { #if IS_ENABLED(CONFIG_HID_ORTEK) { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_IHOME_IMAC_A210S) }, { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, #endif #if IS_ENABLED(CONFIG_HID_PANTHERLORD) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3d911bfd91cf..c9ba4c6db74c 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -824,6 +824,7 @@ #define USB_VENDOR_ID_ORTEK 0x05a4 #define USB_DEVICE_ID_ORTEK_PKB1700 0x1700 #define USB_DEVICE_ID_ORTEK_WKB2000 0x2000 +#define USB_DEVICE_ID_ORTEK_IHOME_IMAC_A210S 0x8003 #define USB_VENDOR_ID_PLANTRONICS 0x047f diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 41b39464ded8..501e16a9227d 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -2732,6 +2732,9 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp) hidpp_battery_props, sizeof(hidpp_battery_props), GFP_KERNEL); + if (!battery_props) + return -ENOMEM; + num_battery_props = ARRAY_SIZE(hidpp_battery_props) - 2; if (hidpp->capabilities & HIDPP_CAPABILITY_BATTERY_MILEAGE) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index f3e35e7a189d..aff20f4b6d97 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -620,16 +620,6 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, return 0; } -static int mt_touch_input_mapped(struct hid_device *hdev, struct hid_input *hi, - struct hid_field *field, struct hid_usage *usage, - unsigned long **bit, int *max) -{ - if (usage->type == EV_KEY || usage->type == EV_ABS) - set_bit(usage->type, hi->input->evbit); - - return -1; -} - static int mt_compute_slot(struct mt_device *td, struct input_dev *input) { __s32 quirks = td->mtclass.quirks; @@ -969,8 +959,10 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi, return 0; if (field->application == HID_DG_TOUCHSCREEN || - field->application == HID_DG_TOUCHPAD) - return mt_touch_input_mapped(hdev, hi, field, usage, bit, max); + field->application == HID_DG_TOUCHPAD) { + /* We own these mappings, tell hid-input to ignore them */ + return -1; + } /* let hid-core decide for the others */ return 0; diff --git a/drivers/hid/hid-ortek.c b/drivers/hid/hid-ortek.c index 6620f15fec22..8783a064cdcf 100644 --- a/drivers/hid/hid-ortek.c +++ b/drivers/hid/hid-ortek.c @@ -5,6 +5,7 @@ * * Ortek PKB-1700 * Ortek WKB-2000 + * iHome IMAC-A210S * Skycable wireless presenter * * Copyright (c) 2010 Johnathon Harris <jmharris@gmail.com> @@ -28,10 +29,10 @@ static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc, unsigned int *rsize) { if (*rsize >= 56 && rdesc[54] == 0x25 && rdesc[55] == 0x01) { - hid_info(hdev, "Fixing up logical minimum in report descriptor (Ortek)\n"); + hid_info(hdev, "Fixing up logical maximum in report descriptor (Ortek)\n"); rdesc[55] = 0x92; } else if (*rsize >= 54 && rdesc[52] == 0x25 && rdesc[53] == 0x01) { - hid_info(hdev, "Fixing up logical minimum in report descriptor (Skycable)\n"); + hid_info(hdev, "Fixing up logical maximum in report descriptor (Skycable)\n"); rdesc[53] = 0x65; } return rdesc; @@ -40,6 +41,7 @@ static __u8 *ortek_report_fixup(struct hid_device *hdev, __u8 *rdesc, static const struct hid_device_id ortek_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_PKB1700) }, { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_IHOME_IMAC_A210S) }, { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) }, { } }; diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index 76013eb5cb7f..c008847e0b20 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -680,18 +680,21 @@ static int usbhid_open(struct hid_device *hid) struct usbhid_device *usbhid = hid->driver_data; int res; + set_bit(HID_OPENED, &usbhid->iofl); + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) return 0; res = usb_autopm_get_interface(usbhid->intf); /* the device must be awake to reliably request remote wakeup */ - if (res < 0) + if (res < 0) { + clear_bit(HID_OPENED, &usbhid->iofl); return -EIO; + } usbhid->intf->needs_remote_wakeup = 1; set_bit(HID_RESUME_RUNNING, &usbhid->iofl); - set_bit(HID_OPENED, &usbhid->iofl); set_bit(HID_IN_POLLING, &usbhid->iofl); res = hid_start_in(hid); @@ -727,19 +730,20 @@ static void usbhid_close(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; - if (hid->quirks & HID_QUIRK_ALWAYS_POLL) - return; - /* * Make sure we don't restart data acquisition due to * a resumption we no longer care about by avoiding racing * with hid_start_in(). */ spin_lock_irq(&usbhid->lock); - clear_bit(HID_IN_POLLING, &usbhid->iofl); clear_bit(HID_OPENED, &usbhid->iofl); + if (!(hid->quirks & HID_QUIRK_ALWAYS_POLL)) + clear_bit(HID_IN_POLLING, &usbhid->iofl); spin_unlock_irq(&usbhid->lock); + if (hid->quirks & HID_QUIRK_ALWAYS_POLL) + return; + hid_cancel_delayed_stuff(usbhid); usb_kill_urb(usbhid->urbin); usbhid->intf->needs_remote_wakeup = 0; diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index e9bf0bb87ac4..63ac1c6a825f 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -177,6 +177,11 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, &vmbus_connection.chn_msg_list); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + if (newchannel->rescind) { + err = -ENODEV; + goto error_free_gpadl; + } + ret = vmbus_post_msg(open_msg, sizeof(struct vmbus_channel_open_channel), true); @@ -421,6 +426,11 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer, spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + if (channel->rescind) { + ret = -ENODEV; + goto cleanup; + } + ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - sizeof(*msginfo), true); if (ret != 0) @@ -494,6 +504,10 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) list_add_tail(&info->msglistentry, &vmbus_connection.chn_msg_list); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + if (channel->rescind) + goto post_msg_err; + ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown), true); @@ -606,6 +620,8 @@ static int vmbus_close_internal(struct vmbus_channel *channel) get_order(channel->ringbuffer_pagecount * PAGE_SIZE)); out: + /* re-enable tasklet for use on re-open */ + tasklet_enable(&channel->callback_event); return ret; } diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 4bbb8dea4727..968af173c4c1 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -451,6 +451,12 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) /* Make sure this is a new offer */ mutex_lock(&vmbus_connection.channel_mutex); + /* + * Now that we have acquired the channel_mutex, + * we can release the potentially racing rescind thread. + */ + atomic_dec(&vmbus_connection.offer_in_progress); + list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { if (!uuid_le_cmp(channel->offermsg.offer.if_type, newchannel->offermsg.offer.if_type) && @@ -481,7 +487,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) channel->num_sc++; spin_unlock_irqrestore(&channel->lock, flags); } else { - atomic_dec(&vmbus_connection.offer_in_progress); goto err_free_chan; } } @@ -510,7 +515,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) if (!fnew) { if (channel->sc_creation_callback != NULL) channel->sc_creation_callback(newchannel); - atomic_dec(&vmbus_connection.offer_in_progress); return; } @@ -541,7 +545,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) goto err_deq_chan; } - atomic_dec(&vmbus_connection.offer_in_progress); + newchannel->probe_done = true; return; err_deq_chan: @@ -882,8 +886,27 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) channel->rescind = true; spin_unlock_irqrestore(&channel->lock, flags); + /* + * Now that we have posted the rescind state, perform + * rescind related cleanup. + */ vmbus_rescind_cleanup(channel); + /* + * Now wait for offer handling to complete. + */ + while (READ_ONCE(channel->probe_done) == false) { + /* + * We wait here until any channel offer is currently + * being processed. + */ + msleep(1); + } + + /* + * At this point, the rescind handling can proceed safely. + */ + if (channel->device_obj) { if (channel->chn_rescind_callback) { channel->chn_rescind_callback(channel); diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index f5728deff893..db0e6652d7ef 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -584,10 +584,6 @@ static int hv_memory_notifier(struct notifier_block *nb, unsigned long val, switch (val) { case MEM_ONLINE: - spin_lock_irqsave(&dm_device.ha_lock, flags); - dm_device.num_pages_onlined += mem->nr_pages; - spin_unlock_irqrestore(&dm_device.ha_lock, flags); - /* Fall through */ case MEM_CANCEL_ONLINE: if (dm_device.ha_waiting) { dm_device.ha_waiting = false; @@ -644,6 +640,9 @@ static void hv_page_online_one(struct hv_hotadd_state *has, struct page *pg) __online_page_set_limits(pg); __online_page_increment_counters(pg); __online_page_free(pg); + + WARN_ON_ONCE(!spin_is_locked(&dm_device.ha_lock)); + dm_device.num_pages_onlined++; } static void hv_bring_pgs_online(struct hv_hotadd_state *has, @@ -1036,8 +1035,8 @@ static void process_info(struct hv_dynmem_device *dm, struct dm_info_msg *msg) if (info_hdr->data_size == sizeof(__u64)) { __u64 *max_page_count = (__u64 *)&info_hdr[1]; - pr_info("INFO_TYPE_MAX_PAGE_CNT = %llu\n", - *max_page_count); + pr_info("Max. dynamic memory size: %llu MB\n", + (*max_page_count) >> (20 - PAGE_SHIFT)); } break; @@ -1656,6 +1655,7 @@ static int balloon_probe(struct hv_device *dev, } dm_device.state = DM_INITIALIZED; + last_post_time = jiffies; return 0; diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index 9a90b915b5be..5eed1e7da15c 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -304,7 +304,7 @@ static int process_ob_ipinfo(void *in_msg, void *out_msg, int op) strlen((char *)in->body.kvp_ip_val.adapter_id), UTF16_HOST_ENDIAN, (wchar_t *)out->kvp_ip_val.adapter_id, - MAX_IP_ADDR_SIZE); + MAX_ADAPTER_ID_SIZE); if (len < 0) return len; diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 1f450c39a9b0..12eb8caa4263 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -29,6 +29,7 @@ #include <linux/uio.h> #include <linux/vmalloc.h> #include <linux/slab.h> +#include <linux/prefetch.h> #include "hyperv_vmbus.h" @@ -94,30 +95,6 @@ hv_set_next_write_location(struct hv_ring_buffer_info *ring_info, ring_info->ring_buffer->write_index = next_write_location; } -/* Get the next read location for the specified ring buffer. */ -static inline u32 -hv_get_next_read_location(const struct hv_ring_buffer_info *ring_info) -{ - return ring_info->ring_buffer->read_index; -} - -/* - * Get the next read location + offset for the specified ring buffer. - * This allows the caller to skip. - */ -static inline u32 -hv_get_next_readlocation_withoffset(const struct hv_ring_buffer_info *ring_info, - u32 offset) -{ - u32 next = ring_info->ring_buffer->read_index; - - next += offset; - if (next >= ring_info->ring_datasize) - next -= ring_info->ring_datasize; - - return next; -} - /* Set the next read location for the specified ring buffer. */ static inline void hv_set_next_read_location(struct hv_ring_buffer_info *ring_info, @@ -142,29 +119,6 @@ hv_get_ring_bufferindices(struct hv_ring_buffer_info *ring_info) } /* - * Helper routine to copy to source from ring buffer. - * Assume there is enough room. Handles wrap-around in src case only!! - */ -static u32 hv_copyfrom_ringbuffer( - const struct hv_ring_buffer_info *ring_info, - void *dest, - u32 destlen, - u32 start_read_offset) -{ - void *ring_buffer = hv_get_ring_buffer(ring_info); - u32 ring_buffer_size = hv_get_ring_buffersize(ring_info); - - memcpy(dest, ring_buffer + start_read_offset, destlen); - - start_read_offset += destlen; - if (start_read_offset >= ring_buffer_size) - start_read_offset -= ring_buffer_size; - - return start_read_offset; -} - - -/* * Helper routine to copy from source to ring buffer. * Assume there is enough room. Handles wrap-around in dest case only!! */ @@ -334,33 +288,22 @@ int hv_ringbuffer_write(struct vmbus_channel *channel, return 0; } -static inline void -init_cached_read_index(struct hv_ring_buffer_info *rbi) -{ - rbi->cached_read_index = rbi->ring_buffer->read_index; -} - int hv_ringbuffer_read(struct vmbus_channel *channel, void *buffer, u32 buflen, u32 *buffer_actual_len, u64 *requestid, bool raw) { - u32 bytes_avail_toread; - u32 next_read_location; - u64 prev_indices = 0; - struct vmpacket_descriptor desc; - u32 offset; - u32 packetlen; - struct hv_ring_buffer_info *inring_info = &channel->inbound; - - if (buflen <= 0) + struct vmpacket_descriptor *desc; + u32 packetlen, offset; + + if (unlikely(buflen == 0)) return -EINVAL; *buffer_actual_len = 0; *requestid = 0; - bytes_avail_toread = hv_get_bytes_to_read(inring_info); /* Make sure there is something to read */ - if (bytes_avail_toread < sizeof(desc)) { + desc = hv_pkt_iter_first(channel); + if (desc == NULL) { /* * No error is set when there is even no header, drivers are * supposed to analyze buffer_actual_len. @@ -368,48 +311,22 @@ int hv_ringbuffer_read(struct vmbus_channel *channel, return 0; } - init_cached_read_index(inring_info); - - next_read_location = hv_get_next_read_location(inring_info); - next_read_location = hv_copyfrom_ringbuffer(inring_info, &desc, - sizeof(desc), - next_read_location); - - offset = raw ? 0 : (desc.offset8 << 3); - packetlen = (desc.len8 << 3) - offset; + offset = raw ? 0 : (desc->offset8 << 3); + packetlen = (desc->len8 << 3) - offset; *buffer_actual_len = packetlen; - *requestid = desc.trans_id; - - if (bytes_avail_toread < packetlen + offset) - return -EAGAIN; + *requestid = desc->trans_id; - if (packetlen > buflen) + if (unlikely(packetlen > buflen)) return -ENOBUFS; - next_read_location = - hv_get_next_readlocation_withoffset(inring_info, offset); + /* since ring is double mapped, only one copy is necessary */ + memcpy(buffer, (const char *)desc + offset, packetlen); - next_read_location = hv_copyfrom_ringbuffer(inring_info, - buffer, - packetlen, - next_read_location); + /* Advance ring index to next packet descriptor */ + __hv_pkt_iter_next(channel, desc); - next_read_location = hv_copyfrom_ringbuffer(inring_info, - &prev_indices, - sizeof(u64), - next_read_location); - - /* - * Make sure all reads are done before we update the read index since - * the writer may start writing to the read area once the read index - * is updated. - */ - virt_mb(); - - /* Update the read index */ - hv_set_next_read_location(inring_info, next_read_location); - - hv_signal_on_read(channel); + /* Notify host of update */ + hv_pkt_iter_close(channel); return 0; } @@ -440,14 +357,16 @@ static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi) struct vmpacket_descriptor *hv_pkt_iter_first(struct vmbus_channel *channel) { struct hv_ring_buffer_info *rbi = &channel->inbound; - - /* set state for later hv_signal_on_read() */ - init_cached_read_index(rbi); + struct vmpacket_descriptor *desc; if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor)) return NULL; - return hv_get_ring_buffer(rbi) + rbi->priv_read_index; + desc = hv_get_ring_buffer(rbi) + rbi->priv_read_index; + if (desc) + prefetch((char *)desc + (desc->len8 << 3)); + + return desc; } EXPORT_SYMBOL_GPL(hv_pkt_iter_first); @@ -471,10 +390,7 @@ __hv_pkt_iter_next(struct vmbus_channel *channel, rbi->priv_read_index -= dsize; /* more data? */ - if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor)) - return NULL; - else - return hv_get_ring_buffer(rbi) + rbi->priv_read_index; + return hv_pkt_iter_first(channel); } EXPORT_SYMBOL_GPL(__hv_pkt_iter_next); @@ -484,6 +400,7 @@ EXPORT_SYMBOL_GPL(__hv_pkt_iter_next); void hv_pkt_iter_close(struct vmbus_channel *channel) { struct hv_ring_buffer_info *rbi = &channel->inbound; + u32 orig_write_sz = hv_get_bytes_to_write(rbi); /* * Make sure all reads are done before we update the read index since @@ -493,6 +410,40 @@ void hv_pkt_iter_close(struct vmbus_channel *channel) virt_rmb(); rbi->ring_buffer->read_index = rbi->priv_read_index; - hv_signal_on_read(channel); + /* + * Issue a full memory barrier before making the signaling decision. + * Here is the reason for having this barrier: + * If the reading of the pend_sz (in this function) + * were to be reordered and read before we commit the new read + * index (in the calling function) we could + * have a problem. If the host were to set the pending_sz after we + * have sampled pending_sz and go to sleep before we commit the + * read index, we could miss sending the interrupt. Issue a full + * memory barrier to address this. + */ + virt_mb(); + + /* If host has disabled notifications then skip */ + if (rbi->ring_buffer->interrupt_mask) + return; + + if (rbi->ring_buffer->feature_bits.feat_pending_send_sz) { + u32 pending_sz = READ_ONCE(rbi->ring_buffer->pending_send_sz); + + /* + * If there was space before we began iteration, + * then host was not blocked. Also handles case where + * pending_sz is zero then host has nothing pending + * and does not need to be signaled. + */ + if (orig_write_sz > pending_sz) + return; + + /* If pending write will not fit, don't give false hope. */ + if (hv_get_bytes_to_write(rbi) < pending_sz) + return; + } + + vmbus_setevent(channel); } EXPORT_SYMBOL_GPL(hv_pkt_iter_close); diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index ed84e96715a0..43160a2eafe0 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -940,6 +940,9 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) if (channel->offermsg.child_relid != relid) continue; + if (channel->rescind) + continue; + switch (channel->callback_mode) { case HV_CALL_ISR: vmbus_channel_isr(channel); diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 0af7fd311979..76c34f4fde13 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -566,6 +566,8 @@ static int applesmc_init_smcreg_try(void) if (ret) return ret; s->fan_count = tmp[0]; + if (s->fan_count > 10) + s->fan_count = 10; ret = applesmc_get_lower_bound(&s->temp_begin, "T"); if (ret) @@ -811,7 +813,8 @@ static ssize_t applesmc_show_fan_speed(struct device *dev, char newkey[5]; u8 buffer[2]; - sprintf(newkey, fan_speed_fmt[to_option(attr)], to_index(attr)); + scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)], + to_index(attr)); ret = applesmc_read_key(newkey, buffer, 2); speed = ((buffer[0] << 8 | buffer[1]) >> 2); @@ -834,7 +837,8 @@ static ssize_t applesmc_store_fan_speed(struct device *dev, if (kstrtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000) return -EINVAL; /* Bigger than a 14-bit value */ - sprintf(newkey, fan_speed_fmt[to_option(attr)], to_index(attr)); + scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)], + to_index(attr)); buffer[0] = (speed >> 6) & 0xff; buffer[1] = (speed << 2) & 0xff; @@ -903,7 +907,7 @@ static ssize_t applesmc_show_fan_position(struct device *dev, char newkey[5]; u8 buffer[17]; - sprintf(newkey, FAN_ID_FMT, to_index(attr)); + scnprintf(newkey, sizeof(newkey), FAN_ID_FMT, to_index(attr)); ret = applesmc_read_key(newkey, buffer, 16); buffer[16] = 0; @@ -1116,7 +1120,8 @@ static int applesmc_create_nodes(struct applesmc_node_group *groups, int num) } for (i = 0; i < num; i++) { node = &grp->nodes[i]; - sprintf(node->name, grp->format, i + 1); + scnprintf(node->name, sizeof(node->name), grp->format, + i + 1); node->sda.index = (grp->option << 16) | (i & 0xffff); node->sda.dev_attr.show = grp->show; node->sda.dev_attr.store = grp->store; diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 8d55d6d79015..ef9cb3c164e1 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -70,13 +70,13 @@ config CORESIGHT_SOURCE_ETM4X for instruction level tracing. Depending on the implemented version data tracing may also be available. -config CORESIGHT_QCOM_REPLICATOR - bool "Qualcomm CoreSight Replicator driver" +config CORESIGHT_DYNAMIC_REPLICATOR + bool "CoreSight Programmable Replicator driver" depends on CORESIGHT_LINKS_AND_SINKS help - This enables support for Qualcomm CoreSight link driver. The - programmable ATB replicator sends the ATB trace stream from the - ETB/ETF to the TPIUi and ETR. + This enables support for dynamic CoreSight replicator link driver. + The programmable ATB replicator allows independent filtering of the + trace data based on the traceid. config CORESIGHT_STM bool "CoreSight System Trace Macrocell driver" diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 433d59025eb6..5bae90ce794d 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -14,6 +14,6 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ coresight-etm4x-sysfs.o -obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o +obj-$(CONFIG_CORESIGHT_DYNAMIC_REPLICATOR) += coresight-dynamic-replicator.o obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o diff --git a/drivers/hwtracing/coresight/coresight-cpu-debug.c b/drivers/hwtracing/coresight/coresight-cpu-debug.c index 64a77e00eaa6..6ea62c62ff27 100644 --- a/drivers/hwtracing/coresight/coresight-cpu-debug.c +++ b/drivers/hwtracing/coresight/coresight-cpu-debug.c @@ -667,7 +667,7 @@ static int debug_remove(struct amba_device *adev) return 0; } -static struct amba_id debug_ids[] = { +static const struct amba_id debug_ids[] = { { /* Debug for Cortex-A53 */ .id = 0x000bbd03, .mask = 0x000fffff, diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c index 0a3d15f0b009..accc2056f7c6 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-dynamic-replicator.c @@ -95,6 +95,28 @@ static const struct coresight_ops replicator_cs_ops = { .link_ops = &replicator_link_ops, }; +#define coresight_replicator_reg(name, offset) \ + coresight_simple_reg32(struct replicator_state, name, offset) + +coresight_replicator_reg(idfilter0, REPLICATOR_IDFILTER0); +coresight_replicator_reg(idfilter1, REPLICATOR_IDFILTER1); + +static struct attribute *replicator_mgmt_attrs[] = { + &dev_attr_idfilter0.attr, + &dev_attr_idfilter1.attr, + NULL, +}; + +static const struct attribute_group replicator_mgmt_group = { + .attrs = replicator_mgmt_attrs, + .name = "mgmt", +}; + +static const struct attribute_group *replicator_groups[] = { + &replicator_mgmt_group, + NULL, +}; + static int replicator_probe(struct amba_device *adev, const struct amba_id *id) { int ret; @@ -139,11 +161,11 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) desc.ops = &replicator_cs_ops; desc.pdata = adev->dev.platform_data; desc.dev = &adev->dev; + desc.groups = replicator_groups; drvdata->csdev = coresight_register(&desc); if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); - dev_info(dev, "%s initialized\n", (char *)id->data); return 0; } @@ -175,18 +197,22 @@ static const struct dev_pm_ops replicator_dev_pm_ops = { NULL) }; -static struct amba_id replicator_ids[] = { +static const struct amba_id replicator_ids[] = { { .id = 0x0003b909, .mask = 0x0003ffff, - .data = "REPLICATOR 1.0", + }, + { + /* Coresight SoC-600 */ + .id = 0x000bb9ec, + .mask = 0x000fffff, }, { 0, 0 }, }; static struct amba_driver replicator_driver = { .drv = { - .name = "coresight-replicator-qcom", + .name = "coresight-dynamic-replicator", .pm = &replicator_dev_pm_ops, .suppress_bind_attrs = true, }, diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index d5b96423e1a5..56ecd7aff5eb 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -200,8 +200,10 @@ static void etb_disable_hw(struct etb_drvdata *drvdata) static void etb_dump_hw(struct etb_drvdata *drvdata) { + bool lost = false; int i; u8 *buf_ptr; + const u32 *barrier; u32 read_data, depth; u32 read_ptr, write_ptr; u32 frame_off, frame_endoff; @@ -223,20 +225,26 @@ static void etb_dump_hw(struct etb_drvdata *drvdata) } if ((readl_relaxed(drvdata->base + ETB_STATUS_REG) - & ETB_STATUS_RAM_FULL) == 0) + & ETB_STATUS_RAM_FULL) == 0) { writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER); - else + } else { writel_relaxed(write_ptr, drvdata->base + ETB_RAM_READ_POINTER); + lost = true; + } depth = drvdata->buffer_depth; buf_ptr = drvdata->buf; + barrier = barrier_pkt; for (i = 0; i < depth; i++) { read_data = readl_relaxed(drvdata->base + ETB_RAM_READ_DATA_REG); - *buf_ptr++ = read_data >> 0; - *buf_ptr++ = read_data >> 8; - *buf_ptr++ = read_data >> 16; - *buf_ptr++ = read_data >> 24; + if (lost && *barrier) { + read_data = *barrier; + barrier++; + } + + *(u32 *)buf_ptr = read_data; + buf_ptr += 4; } if (frame_off) { @@ -353,8 +361,10 @@ static void etb_update_buffer(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config) { + bool lost = false; int i, cur; u8 *buf_ptr; + const u32 *barrier; u32 read_ptr, write_ptr, capacity; u32 status, read_data, to_read; unsigned long offset; @@ -366,8 +376,8 @@ static void etb_update_buffer(struct coresight_device *csdev, capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS; - CS_UNLOCK(drvdata->base); etb_disable_hw(drvdata); + CS_UNLOCK(drvdata->base); /* unit is in words, not bytes */ read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER); @@ -384,7 +394,7 @@ static void etb_update_buffer(struct coresight_device *csdev, (unsigned long)write_ptr); write_ptr &= ~(ETB_FRAME_SIZE_WORDS - 1); - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + lost = true; } /* @@ -395,7 +405,7 @@ static void etb_update_buffer(struct coresight_device *csdev, */ status = readl_relaxed(drvdata->base + ETB_STATUS_REG); if (status & ETB_STATUS_RAM_FULL) { - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + lost = true; to_read = capacity; read_ptr = write_ptr; } else { @@ -428,22 +438,30 @@ static void etb_update_buffer(struct coresight_device *csdev, if (read_ptr > (drvdata->buffer_depth - 1)) read_ptr -= drvdata->buffer_depth; /* let the decoder know we've skipped ahead */ - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + lost = true; } + if (lost) + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + /* finally tell HW where we want to start reading from */ writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER); cur = buf->cur; offset = buf->offset; + barrier = barrier_pkt; + for (i = 0; i < to_read; i += 4) { buf_ptr = buf->data_pages[cur] + offset; read_data = readl_relaxed(drvdata->base + ETB_RAM_READ_DATA_REG); - *buf_ptr++ = read_data >> 0; - *buf_ptr++ = read_data >> 8; - *buf_ptr++ = read_data >> 16; - *buf_ptr++ = read_data >> 24; + if (lost && *barrier) { + read_data = *barrier; + barrier++; + } + + *(u32 *)buf_ptr = read_data; + buf_ptr += 4; offset += 4; if (offset >= PAGE_SIZE) { @@ -557,17 +575,17 @@ static const struct file_operations etb_fops = { .llseek = no_llseek, }; -#define coresight_etb10_simple_func(name, offset) \ - coresight_simple_func(struct etb_drvdata, NULL, name, offset) +#define coresight_etb10_reg(name, offset) \ + coresight_simple_reg32(struct etb_drvdata, name, offset) -coresight_etb10_simple_func(rdp, ETB_RAM_DEPTH_REG); -coresight_etb10_simple_func(sts, ETB_STATUS_REG); -coresight_etb10_simple_func(rrp, ETB_RAM_READ_POINTER); -coresight_etb10_simple_func(rwp, ETB_RAM_WRITE_POINTER); -coresight_etb10_simple_func(trg, ETB_TRG); -coresight_etb10_simple_func(ctl, ETB_CTL_REG); -coresight_etb10_simple_func(ffsr, ETB_FFSR); -coresight_etb10_simple_func(ffcr, ETB_FFCR); +coresight_etb10_reg(rdp, ETB_RAM_DEPTH_REG); +coresight_etb10_reg(sts, ETB_STATUS_REG); +coresight_etb10_reg(rrp, ETB_RAM_READ_POINTER); +coresight_etb10_reg(rwp, ETB_RAM_WRITE_POINTER); +coresight_etb10_reg(trg, ETB_TRG); +coresight_etb10_reg(ctl, ETB_CTL_REG); +coresight_etb10_reg(ffsr, ETB_FFSR); +coresight_etb10_reg(ffcr, ETB_FFCR); static struct attribute *coresight_etb_mgmt_attrs[] = { &dev_attr_rdp.attr, @@ -728,7 +746,7 @@ static const struct dev_pm_ops etb_dev_pm_ops = { SET_RUNTIME_PM_OPS(etb_runtime_suspend, etb_runtime_resume, NULL) }; -static struct amba_id etb_ids[] = { +static const struct amba_id etb_ids[] = { { .id = 0x0003b907, .mask = 0x0003ffff, diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 8f546f59a3fd..8a0ad77574e7 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -53,14 +53,16 @@ static DEFINE_PER_CPU(struct coresight_device *, csdev_src); /* ETMv3.5/PTM's ETMCR is 'config' */ PMU_FORMAT_ATTR(cycacc, "config:" __stringify(ETM_OPT_CYCACC)); PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS)); +PMU_FORMAT_ATTR(retstack, "config:" __stringify(ETM_OPT_RETSTK)); static struct attribute *etm_config_formats_attr[] = { &format_attr_cycacc.attr, &format_attr_timestamp.attr, + &format_attr_retstack.attr, NULL, }; -static struct attribute_group etm_pmu_format_group = { +static const struct attribute_group etm_pmu_format_group = { .name = "format", .attrs = etm_config_formats_attr, }; diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index ad063d7444e1..70b0a248c321 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -106,6 +106,7 @@ #define ETMTECR1_START_STOP BIT(25) /* ETMCCER - 0x1E8 */ #define ETMCCER_TIMESTAMP BIT(22) +#define ETMCCER_RETSTACK BIT(23) #define ETM_MODE_EXCLUDE BIT(0) #define ETM_MODE_CYCACC BIT(1) diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index ca98ad13bb8c..6e547ec6fead 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -1232,19 +1232,19 @@ static struct attribute *coresight_etm_attrs[] = { NULL, }; -#define coresight_etm3x_simple_func(name, offset) \ - coresight_simple_func(struct etm_drvdata, NULL, name, offset) - -coresight_etm3x_simple_func(etmccr, ETMCCR); -coresight_etm3x_simple_func(etmccer, ETMCCER); -coresight_etm3x_simple_func(etmscr, ETMSCR); -coresight_etm3x_simple_func(etmidr, ETMIDR); -coresight_etm3x_simple_func(etmcr, ETMCR); -coresight_etm3x_simple_func(etmtraceidr, ETMTRACEIDR); -coresight_etm3x_simple_func(etmteevr, ETMTEEVR); -coresight_etm3x_simple_func(etmtssvr, ETMTSSCR); -coresight_etm3x_simple_func(etmtecr1, ETMTECR1); -coresight_etm3x_simple_func(etmtecr2, ETMTECR2); +#define coresight_etm3x_reg(name, offset) \ + coresight_simple_reg32(struct etm_drvdata, name, offset) + +coresight_etm3x_reg(etmccr, ETMCCR); +coresight_etm3x_reg(etmccer, ETMCCER); +coresight_etm3x_reg(etmscr, ETMSCR); +coresight_etm3x_reg(etmidr, ETMIDR); +coresight_etm3x_reg(etmcr, ETMCR); +coresight_etm3x_reg(etmtraceidr, ETMTRACEIDR); +coresight_etm3x_reg(etmteevr, ETMTEEVR); +coresight_etm3x_reg(etmtssvr, ETMTSSCR); +coresight_etm3x_reg(etmtecr1, ETMTECR1); +coresight_etm3x_reg(etmtecr2, ETMTECR2); static struct attribute *coresight_etm_mgmt_attrs[] = { &dev_attr_etmccr.attr, diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 93ee8fc539be..e5b1ec57dbde 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -243,6 +243,8 @@ void etm_set_default(struct etm_config *config) } config->ctxid_mask = 0x0; + /* Setting default to 1024 as per TRM recommendation */ + config->sync_freq = 0x400; } void etm_config_trace_mode(struct etm_config *config) @@ -308,7 +310,9 @@ void etm_config_trace_mode(struct etm_config *config) config->addr_type[1] = ETM_ADDR_TYPE_RANGE; } -#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN) +#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | \ + ETMCR_TIMESTAMP_EN | \ + ETMCR_RETURN_STACK) static int etm_parse_event_config(struct etm_drvdata *drvdata, struct perf_event *event) @@ -339,14 +343,24 @@ static int etm_parse_event_config(struct etm_drvdata *drvdata, etm_config_trace_mode(config); /* - * At this time only cycle accurate and timestamp options are - * available. + * At this time only cycle accurate, return stack and timestamp + * options are available. */ if (attr->config & ~ETM3X_SUPPORTED_OPTIONS) return -EINVAL; config->ctrl = attr->config; + /* + * Possible to have cores with PTM (supports ret stack) and ETM + * (never has ret stack) on the same SoC. So if we have a request + * for return stack that can't be honoured on this core then + * clear the bit - trace will still continue normally + */ + if ((config->ctrl & ETMCR_RETURN_STACK) && + !(drvdata->etmccer & ETMCCER_RETSTACK)) + config->ctrl &= ~ETMCR_RETURN_STACK; + return 0; } @@ -885,7 +899,7 @@ static const struct dev_pm_ops etm_dev_pm_ops = { SET_RUNTIME_PM_OPS(etm_runtime_suspend, etm_runtime_resume, NULL) }; -static struct amba_id etm_ids[] = { +static const struct amba_id etm_ids[] = { { /* ETM 3.3 */ .id = 0x0003b921, .mask = 0x0003ffff, diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index b9b1e9c8f4c4..4e6eab53e34e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -2066,23 +2066,23 @@ static u32 etmv4_cross_read(const struct device *dev, u32 offset) return reg.data; } -#define coresight_etm4x_simple_func(name, offset) \ - coresight_simple_func(struct etmv4_drvdata, NULL, name, offset) +#define coresight_etm4x_reg(name, offset) \ + coresight_simple_reg32(struct etmv4_drvdata, name, offset) #define coresight_etm4x_cross_read(name, offset) \ coresight_simple_func(struct etmv4_drvdata, etmv4_cross_read, \ name, offset) -coresight_etm4x_simple_func(trcpdcr, TRCPDCR); -coresight_etm4x_simple_func(trcpdsr, TRCPDSR); -coresight_etm4x_simple_func(trclsr, TRCLSR); -coresight_etm4x_simple_func(trcauthstatus, TRCAUTHSTATUS); -coresight_etm4x_simple_func(trcdevid, TRCDEVID); -coresight_etm4x_simple_func(trcdevtype, TRCDEVTYPE); -coresight_etm4x_simple_func(trcpidr0, TRCPIDR0); -coresight_etm4x_simple_func(trcpidr1, TRCPIDR1); -coresight_etm4x_simple_func(trcpidr2, TRCPIDR2); -coresight_etm4x_simple_func(trcpidr3, TRCPIDR3); +coresight_etm4x_reg(trcpdcr, TRCPDCR); +coresight_etm4x_reg(trcpdsr, TRCPDSR); +coresight_etm4x_reg(trclsr, TRCLSR); +coresight_etm4x_reg(trcauthstatus, TRCAUTHSTATUS); +coresight_etm4x_reg(trcdevid, TRCDEVID); +coresight_etm4x_reg(trcdevtype, TRCDEVTYPE); +coresight_etm4x_reg(trcpidr0, TRCPIDR0); +coresight_etm4x_reg(trcpidr1, TRCPIDR1); +coresight_etm4x_reg(trcpidr2, TRCPIDR2); +coresight_etm4x_reg(trcpidr3, TRCPIDR3); coresight_etm4x_cross_read(trcoslsr, TRCOSLSR); coresight_etm4x_cross_read(trcconfig, TRCCONFIGR); coresight_etm4x_cross_read(trctraceid, TRCTRACEIDR); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 532adc9dd32a..cf364a514c12 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -224,6 +224,10 @@ static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, if (attr->config & BIT(ETM_OPT_TS)) /* bit[11], Global timestamp tracing bit */ config->cfg |= BIT(11); + /* return stack - enable if selected and supported */ + if ((attr->config & BIT(ETM_OPT_RETSTK)) && drvdata->retstack) + /* bit[12], Return stack enable bit */ + config->cfg |= BIT(12); out: return ret; @@ -1048,7 +1052,7 @@ err_arch_supported: return ret; } -static struct amba_id etm4_ids[] = { +static const struct amba_id etm4_ids[] = { { /* ETM 4.0 - Cortex-A53 */ .id = 0x000bb95d, .mask = 0x000fffff, diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 860fe6ef5632..77642e0e955b 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -246,11 +246,16 @@ static const struct dev_pm_ops funnel_dev_pm_ops = { SET_RUNTIME_PM_OPS(funnel_runtime_suspend, funnel_runtime_resume, NULL) }; -static struct amba_id funnel_ids[] = { +static const struct amba_id funnel_ids[] = { { .id = 0x0003b908, .mask = 0x0003ffff, }, + { + /* Coresight SoC-600 */ + .id = 0x000bb9eb, + .mask = 0x000fffff, + }, { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 5f662d82052c..f1d0e21d8cab 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -39,23 +39,33 @@ #define ETM_MODE_EXCL_USER BIT(31) typedef u32 (*coresight_read_fn)(const struct device *, u32 offset); -#define coresight_simple_func(type, func, name, offset) \ +#define __coresight_simple_func(type, func, name, lo_off, hi_off) \ static ssize_t name##_show(struct device *_dev, \ struct device_attribute *attr, char *buf) \ { \ type *drvdata = dev_get_drvdata(_dev->parent); \ coresight_read_fn fn = func; \ - u32 val; \ + u64 val; \ pm_runtime_get_sync(_dev->parent); \ if (fn) \ - val = fn(_dev->parent, offset); \ + val = (u64)fn(_dev->parent, lo_off); \ else \ - val = readl_relaxed(drvdata->base + offset); \ + val = coresight_read_reg_pair(drvdata->base, \ + lo_off, hi_off); \ pm_runtime_put_sync(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", val); \ + return scnprintf(buf, PAGE_SIZE, "0x%llx\n", val); \ } \ static DEVICE_ATTR_RO(name) +#define coresight_simple_func(type, func, name, offset) \ + __coresight_simple_func(type, func, name, offset, -1) +#define coresight_simple_reg32(type, name, offset) \ + __coresight_simple_func(type, NULL, name, offset, -1) +#define coresight_simple_reg64(type, name, lo_off, hi_off) \ + __coresight_simple_func(type, NULL, name, lo_off, hi_off) + +extern const u32 barrier_pkt[5]; + enum etm_addr_type { ETM_ADDR_TYPE_NONE, ETM_ADDR_TYPE_SINGLE, @@ -106,6 +116,25 @@ static inline void CS_UNLOCK(void __iomem *addr) } while (0); } +static inline u64 +coresight_read_reg_pair(void __iomem *addr, s32 lo_offset, s32 hi_offset) +{ + u64 val; + + val = readl_relaxed(addr + lo_offset); + val |= (hi_offset < 0) ? 0 : + (u64)readl_relaxed(addr + hi_offset) << 32; + return val; +} + +static inline void coresight_write_reg_pair(void __iomem *addr, u64 val, + s32 lo_offset, s32 hi_offset) +{ + writel_relaxed((u32)val, addr + lo_offset); + if (hi_offset >= 0) + writel_relaxed((u32)(val >> 32), addr + hi_offset); +} + void coresight_disable_path(struct list_head *path); int coresight_enable_path(struct list_head *path, u32 mode); struct coresight_device *coresight_get_sink(struct list_head *path); diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 93fc26f01bab..92a780a6df1d 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -276,7 +276,7 @@ static void stm_disable(struct coresight_device *csdev, spin_unlock(&drvdata->spinlock); /* Wait until the engine has completely stopped */ - coresight_timeout(drvdata, STMTCSR, STMTCSR_BUSY_BIT, 0); + coresight_timeout(drvdata->base, STMTCSR, STMTCSR_BUSY_BIT, 0); pm_runtime_put(drvdata->dev); @@ -307,7 +307,8 @@ static inline bool stm_addr_unaligned(const void *addr, u8 write_bytes) return ((unsigned long)addr & (write_bytes - 1)); } -static void stm_send(void *addr, const void *data, u32 size, u8 write_bytes) +static void stm_send(void __iomem *addr, const void *data, + u32 size, u8 write_bytes) { u8 paload[8]; @@ -414,7 +415,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, unsigned int size, const unsigned char *payload) { - unsigned long ch_addr; + void __iomem *ch_addr; struct stm_drvdata *drvdata = container_of(stm_data, struct stm_drvdata, stm); @@ -424,7 +425,7 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, if (channel >= drvdata->numsp) return -EINVAL; - ch_addr = (unsigned long)stm_channel_addr(drvdata, channel); + ch_addr = stm_channel_addr(drvdata, channel); flags = (flags == STP_PACKET_TIMESTAMPED) ? STM_FLAG_TIMESTAMPED : 0; flags |= test_bit(channel, drvdata->chs.guaranteed) ? @@ -437,20 +438,20 @@ static ssize_t notrace stm_generic_packet(struct stm_data *stm_data, switch (packet) { case STP_PACKET_FLAG: - ch_addr |= stm_channel_off(STM_PKT_TYPE_FLAG, flags); + ch_addr += stm_channel_off(STM_PKT_TYPE_FLAG, flags); /* * The generic STM core sets a size of '0' on flag packets. * As such send a flag packet of size '1' and tell the * core we did so. */ - stm_send((void *)ch_addr, payload, 1, drvdata->write_bytes); + stm_send(ch_addr, payload, 1, drvdata->write_bytes); size = 1; break; case STP_PACKET_DATA: - ch_addr |= stm_channel_off(STM_PKT_TYPE_DATA, flags); - stm_send((void *)ch_addr, payload, size, + ch_addr += stm_channel_off(STM_PKT_TYPE_DATA, flags); + stm_send(ch_addr, payload, size, drvdata->write_bytes); break; @@ -635,21 +636,21 @@ static ssize_t traceid_store(struct device *dev, } static DEVICE_ATTR_RW(traceid); -#define coresight_stm_simple_func(name, offset) \ - coresight_simple_func(struct stm_drvdata, NULL, name, offset) - -coresight_stm_simple_func(tcsr, STMTCSR); -coresight_stm_simple_func(tsfreqr, STMTSFREQR); -coresight_stm_simple_func(syncr, STMSYNCR); -coresight_stm_simple_func(sper, STMSPER); -coresight_stm_simple_func(spter, STMSPTER); -coresight_stm_simple_func(privmaskr, STMPRIVMASKR); -coresight_stm_simple_func(spscr, STMSPSCR); -coresight_stm_simple_func(spmscr, STMSPMSCR); -coresight_stm_simple_func(spfeat1r, STMSPFEAT1R); -coresight_stm_simple_func(spfeat2r, STMSPFEAT2R); -coresight_stm_simple_func(spfeat3r, STMSPFEAT3R); -coresight_stm_simple_func(devid, CORESIGHT_DEVID); +#define coresight_stm_reg(name, offset) \ + coresight_simple_reg32(struct stm_drvdata, name, offset) + +coresight_stm_reg(tcsr, STMTCSR); +coresight_stm_reg(tsfreqr, STMTSFREQR); +coresight_stm_reg(syncr, STMSYNCR); +coresight_stm_reg(sper, STMSPER); +coresight_stm_reg(spter, STMSPTER); +coresight_stm_reg(privmaskr, STMPRIVMASKR); +coresight_stm_reg(spscr, STMSPSCR); +coresight_stm_reg(spmscr, STMSPMSCR); +coresight_stm_reg(spfeat1r, STMSPFEAT1R); +coresight_stm_reg(spfeat2r, STMSPFEAT2R); +coresight_stm_reg(spfeat3r, STMSPFEAT3R); +coresight_stm_reg(devid, CORESIGHT_DEVID); static struct attribute *coresight_stm_attrs[] = { &dev_attr_hwevent_enable.attr, @@ -914,7 +915,7 @@ static const struct dev_pm_ops stm_dev_pm_ops = { SET_RUNTIME_PM_OPS(stm_runtime_suspend, stm_runtime_resume, NULL) }; -static struct amba_id stm_ids[] = { +static const struct amba_id stm_ids[] = { { .id = 0x0003b962, .mask = 0x0003ffff, diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index e3b9fb82eb8d..e2513b786242 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -43,17 +43,34 @@ static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) { + bool lost = false; char *bufp; - u32 read_data; + const u32 *barrier; + u32 read_data, status; int i; + /* + * Get a hold of the status register and see if a wrap around + * has occurred. + */ + status = readl_relaxed(drvdata->base + TMC_STS); + if (status & TMC_STS_FULL) + lost = true; + bufp = drvdata->buf; drvdata->len = 0; + barrier = barrier_pkt; while (1) { for (i = 0; i < drvdata->memwidth; i++) { read_data = readl_relaxed(drvdata->base + TMC_RRD); if (read_data == 0xFFFFFFFF) return; + + if (lost && *barrier) { + read_data = *barrier; + barrier++; + } + memcpy(bufp, &read_data, 4); bufp += 4; drvdata->len += 4; @@ -369,9 +386,11 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, struct perf_output_handle *handle, void *sink_config) { + bool lost = false; int i, cur; + const u32 *barrier; u32 *buf_ptr; - u32 read_ptr, write_ptr; + u64 read_ptr, write_ptr; u32 status, to_read; unsigned long offset; struct cs_buffers *buf = sink_config; @@ -388,8 +407,8 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, tmc_flush_and_stop(drvdata); - read_ptr = readl_relaxed(drvdata->base + TMC_RRP); - write_ptr = readl_relaxed(drvdata->base + TMC_RWP); + read_ptr = tmc_read_rrp(drvdata); + write_ptr = tmc_read_rwp(drvdata); /* * Get a hold of the status register and see if a wrap around @@ -397,7 +416,7 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, */ status = readl_relaxed(drvdata->base + TMC_STS); if (status & TMC_STS_FULL) { - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + lost = true; to_read = drvdata->size; } else { to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size); @@ -441,18 +460,27 @@ static void tmc_update_etf_buffer(struct coresight_device *csdev, if (read_ptr > (drvdata->size - 1)) read_ptr -= drvdata->size; /* Tell the HW */ - writel_relaxed(read_ptr, drvdata->base + TMC_RRP); - perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + tmc_write_rrp(drvdata, read_ptr); + lost = true; } + if (lost) + perf_aux_output_flag(handle, PERF_AUX_FLAG_TRUNCATED); + cur = buf->cur; offset = buf->offset; + barrier = barrier_pkt; /* for every byte to read */ for (i = 0; i < to_read; i += 4) { buf_ptr = buf->data_pages[cur] + offset; *buf_ptr = readl_relaxed(drvdata->base + TMC_RRD); + if (lost && *barrier) { + *buf_ptr = *barrier; + barrier++; + } + offset += 4; if (offset >= PAGE_SIZE) { offset = 0; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 5d312699b3b9..68fbc8f7450e 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -22,7 +22,7 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) { - u32 axictl; + u32 axictl, sts; /* Zero out the memory to help with debug */ memset(drvdata->vaddr, 0, drvdata->size); @@ -36,17 +36,29 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); axictl = readl_relaxed(drvdata->base + TMC_AXICTL); - axictl |= TMC_AXICTL_WR_BURST_16; - writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - axictl &= ~TMC_AXICTL_SCT_GAT_MODE; - writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - axictl = (axictl & - ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) | - TMC_AXICTL_PROT_CTL_B1; + axictl &= ~TMC_AXICTL_CLEAR_MASK; + axictl |= (TMC_AXICTL_PROT_CTL_B1 | TMC_AXICTL_WR_BURST_16); + axictl |= TMC_AXICTL_AXCACHE_OS; + + if (tmc_etr_has_cap(drvdata, TMC_ETR_AXI_ARCACHE)) { + axictl &= ~TMC_AXICTL_ARCACHE_MASK; + axictl |= TMC_AXICTL_ARCACHE_OS; + } + writel_relaxed(axictl, drvdata->base + TMC_AXICTL); + tmc_write_dba(drvdata, drvdata->paddr); + /* + * If the TMC pointers must be programmed before the session, + * we have to set it properly (i.e, RRP/RWP to base address and + * STS to "not full"). + */ + if (tmc_etr_has_cap(drvdata, TMC_ETR_SAVE_RESTORE)) { + tmc_write_rrp(drvdata, drvdata->paddr); + tmc_write_rwp(drvdata, drvdata->paddr); + sts = readl_relaxed(drvdata->base + TMC_STS) & ~TMC_STS_FULL; + writel_relaxed(sts, drvdata->base + TMC_STS); + } - writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO); - writel_relaxed(0x0, drvdata->base + TMC_DBAHI); writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | TMC_FFCR_TRIGON_TRIGIN, @@ -59,9 +71,12 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) { - u32 rwp, val; + const u32 *barrier; + u32 val; + u32 *temp; + u64 rwp; - rwp = readl_relaxed(drvdata->base + TMC_RWP); + rwp = tmc_read_rwp(drvdata); val = readl_relaxed(drvdata->base + TMC_STS); /* @@ -71,6 +86,16 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) if (val & TMC_STS_FULL) { drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; drvdata->len = drvdata->size; + + barrier = barrier_pkt; + temp = (u32 *)drvdata->buf; + + while (*barrier) { + *temp = *barrier; + temp++; + barrier++; + } + } else { drvdata->buf = drvdata->vaddr; drvdata->len = rwp - drvdata->paddr; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 864488793f09..2ff4a66a3caa 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -217,20 +217,24 @@ static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid) return memwidth; } -#define coresight_tmc_simple_func(name, offset) \ - coresight_simple_func(struct tmc_drvdata, NULL, name, offset) - -coresight_tmc_simple_func(rsz, TMC_RSZ); -coresight_tmc_simple_func(sts, TMC_STS); -coresight_tmc_simple_func(rrp, TMC_RRP); -coresight_tmc_simple_func(rwp, TMC_RWP); -coresight_tmc_simple_func(trg, TMC_TRG); -coresight_tmc_simple_func(ctl, TMC_CTL); -coresight_tmc_simple_func(ffsr, TMC_FFSR); -coresight_tmc_simple_func(ffcr, TMC_FFCR); -coresight_tmc_simple_func(mode, TMC_MODE); -coresight_tmc_simple_func(pscr, TMC_PSCR); -coresight_tmc_simple_func(devid, CORESIGHT_DEVID); +#define coresight_tmc_reg(name, offset) \ + coresight_simple_reg32(struct tmc_drvdata, name, offset) +#define coresight_tmc_reg64(name, lo_off, hi_off) \ + coresight_simple_reg64(struct tmc_drvdata, name, lo_off, hi_off) + +coresight_tmc_reg(rsz, TMC_RSZ); +coresight_tmc_reg(sts, TMC_STS); +coresight_tmc_reg(trg, TMC_TRG); +coresight_tmc_reg(ctl, TMC_CTL); +coresight_tmc_reg(ffsr, TMC_FFSR); +coresight_tmc_reg(ffcr, TMC_FFCR); +coresight_tmc_reg(mode, TMC_MODE); +coresight_tmc_reg(pscr, TMC_PSCR); +coresight_tmc_reg(axictl, TMC_AXICTL); +coresight_tmc_reg(devid, CORESIGHT_DEVID); +coresight_tmc_reg64(rrp, TMC_RRP, TMC_RRPHI); +coresight_tmc_reg64(rwp, TMC_RWP, TMC_RWPHI); +coresight_tmc_reg64(dba, TMC_DBALO, TMC_DBAHI); static struct attribute *coresight_tmc_mgmt_attrs[] = { &dev_attr_rsz.attr, @@ -244,6 +248,8 @@ static struct attribute *coresight_tmc_mgmt_attrs[] = { &dev_attr_mode.attr, &dev_attr_pscr.attr, &dev_attr_devid.attr, + &dev_attr_dba.attr, + &dev_attr_axictl.attr, NULL, }; @@ -293,6 +299,42 @@ const struct attribute_group *coresight_tmc_groups[] = { NULL, }; +/* Detect and initialise the capabilities of a TMC ETR */ +static int tmc_etr_setup_caps(struct tmc_drvdata *drvdata, + u32 devid, void *dev_caps) +{ + u32 dma_mask = 0; + + /* Set the unadvertised capabilities */ + tmc_etr_init_caps(drvdata, (u32)(unsigned long)dev_caps); + + if (!(devid & TMC_DEVID_NOSCAT)) + tmc_etr_set_cap(drvdata, TMC_ETR_SG); + + /* Check if the AXI address width is available */ + if (devid & TMC_DEVID_AXIAW_VALID) + dma_mask = ((devid >> TMC_DEVID_AXIAW_SHIFT) & + TMC_DEVID_AXIAW_MASK); + + /* + * Unless specified in the device configuration, ETR uses a 40-bit + * AXI master in place of the embedded SRAM of ETB/ETF. + */ + switch (dma_mask) { + case 32: + case 40: + case 44: + case 48: + case 52: + dev_info(drvdata->dev, "Detected dma mask %dbits\n", dma_mask); + break; + default: + dma_mask = 40; + } + + return dma_set_mask_and_coherent(drvdata->dev, DMA_BIT_MASK(dma_mask)); +} + static int tmc_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; @@ -354,25 +396,29 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) desc.dev = dev; desc.groups = coresight_tmc_groups; - if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { + switch (drvdata->config_type) { + case TMC_CONFIG_TYPE_ETB: desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &tmc_etb_cs_ops; - } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { + break; + case TMC_CONFIG_TYPE_ETR: desc.type = CORESIGHT_DEV_TYPE_SINK; desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; desc.ops = &tmc_etr_cs_ops; - /* - * ETR configuration uses a 40-bit AXI master in place of - * the embedded SRAM of ETB/ETF. - */ - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); + ret = tmc_etr_setup_caps(drvdata, devid, id->data); if (ret) goto out; - } else { + break; + case TMC_CONFIG_TYPE_ETF: desc.type = CORESIGHT_DEV_TYPE_LINKSINK; desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; desc.ops = &tmc_etf_cs_ops; + break; + default: + pr_err("%s: Unsupported TMC config\n", pdata->name); + ret = -EINVAL; + goto out; } drvdata->csdev = coresight_register(&desc); @@ -391,11 +437,27 @@ out: return ret; } -static struct amba_id tmc_ids[] = { +static const struct amba_id tmc_ids[] = { { .id = 0x0003b961, .mask = 0x0003ffff, }, + { + /* Coresight SoC 600 TMC-ETR/ETS */ + .id = 0x000bb9e8, + .mask = 0x000fffff, + .data = (void *)(unsigned long)CORESIGHT_SOC_600_ETR_CAPS, + }, + { + /* Coresight SoC 600 TMC-ETB */ + .id = 0x000bb9e9, + .mask = 0x000fffff, + }, + { + /* Coresight SoC 600 TMC-ETF */ + .id = 0x000bb9ea, + .mask = 0x000fffff, + }, { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 51c01851533e..8df7a813f537 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -54,11 +54,32 @@ #define TMC_STS_TMCREADY_BIT 2 #define TMC_STS_FULL BIT(0) #define TMC_STS_TRIGGERED BIT(1) -/* TMC_AXICTL - 0x110 */ +/* + * TMC_AXICTL - 0x110 + * + * TMC AXICTL format for SoC-400 + * Bits [0-1] : ProtCtrlBit0-1 + * Bits [2-5] : CacheCtrlBits 0-3 (AXCACHE) + * Bit 6 : Reserved + * Bit 7 : ScatterGatherMode + * Bits [8-11] : WrBurstLen + * Bits [12-31] : Reserved. + * TMC AXICTL format for SoC-600, as above except: + * Bits [2-5] : AXI WCACHE + * Bits [16-19] : AXI RCACHE + * Bits [20-31] : Reserved + */ +#define TMC_AXICTL_CLEAR_MASK 0xfbf +#define TMC_AXICTL_ARCACHE_MASK (0xf << 16) + #define TMC_AXICTL_PROT_CTL_B0 BIT(0) #define TMC_AXICTL_PROT_CTL_B1 BIT(1) #define TMC_AXICTL_SCT_GAT_MODE BIT(7) #define TMC_AXICTL_WR_BURST_16 0xF00 +/* Write-back Read and Write-allocate */ +#define TMC_AXICTL_AXCACHE_OS (0xf << 2) +#define TMC_AXICTL_ARCACHE_OS (0xf << 16) + /* TMC_FFCR - 0x304 */ #define TMC_FFCR_FLUSHMAN_BIT 6 #define TMC_FFCR_EN_FMT BIT(0) @@ -69,6 +90,12 @@ #define TMC_FFCR_STOP_ON_FLUSH BIT(12) +#define TMC_DEVID_NOSCAT BIT(24) + +#define TMC_DEVID_AXIAW_VALID BIT(16) +#define TMC_DEVID_AXIAW_SHIFT 17 +#define TMC_DEVID_AXIAW_MASK 0x7f + enum tmc_config_type { TMC_CONFIG_TYPE_ETB, TMC_CONFIG_TYPE_ETR, @@ -88,6 +115,24 @@ enum tmc_mem_intf_width { TMC_MEM_INTF_WIDTH_256BITS = 8, }; +/* TMC ETR Capability bit definitions */ +#define TMC_ETR_SG (0x1U << 0) +/* ETR has separate read/write cache encodings */ +#define TMC_ETR_AXI_ARCACHE (0x1U << 1) +/* + * TMC_ETR_SAVE_RESTORE - Values of RRP/RWP/STS.Full are + * retained when TMC leaves Disabled state, allowing us to continue + * the tracing from a point where we stopped. This also implies that + * the RRP/RWP/STS.Full should always be programmed to the correct + * value. Unfortunately this is not advertised by the hardware, + * so we have to rely on PID of the IP to detect the functionality. + */ +#define TMC_ETR_SAVE_RESTORE (0x1U << 2) + +/* Coresight SoC-600 TMC-ETR unadvertised capabilities */ +#define CORESIGHT_SOC_600_ETR_CAPS \ + (TMC_ETR_SAVE_RESTORE | TMC_ETR_AXI_ARCACHE) + /** * struct tmc_drvdata - specifics associated to an TMC component * @base: memory mapped base address for this component. @@ -104,6 +149,8 @@ enum tmc_mem_intf_width { * @config_type: TMC variant, must be of type @tmc_config_type. * @memwidth: width of the memory interface databus, in bytes. * @trigger_cntr: amount of words to store after a trigger. + * @etr_caps: Bitmask of capabilities of the TMC ETR, inferred from the + * device configuration register (DEVID) */ struct tmc_drvdata { void __iomem *base; @@ -121,6 +168,7 @@ struct tmc_drvdata { enum tmc_config_type config_type; enum tmc_mem_intf_width memwidth; u32 trigger_cntr; + u32 etr_caps; }; /* Generic functions */ @@ -139,4 +187,39 @@ extern const struct coresight_ops tmc_etf_cs_ops; int tmc_read_prepare_etr(struct tmc_drvdata *drvdata); int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata); extern const struct coresight_ops tmc_etr_cs_ops; + + +#define TMC_REG_PAIR(name, lo_off, hi_off) \ +static inline u64 \ +tmc_read_##name(struct tmc_drvdata *drvdata) \ +{ \ + return coresight_read_reg_pair(drvdata->base, lo_off, hi_off); \ +} \ +static inline void \ +tmc_write_##name(struct tmc_drvdata *drvdata, u64 val) \ +{ \ + coresight_write_reg_pair(drvdata->base, val, lo_off, hi_off); \ +} + +TMC_REG_PAIR(rrp, TMC_RRP, TMC_RRPHI) +TMC_REG_PAIR(rwp, TMC_RWP, TMC_RWPHI) +TMC_REG_PAIR(dba, TMC_DBALO, TMC_DBAHI) + +/* Initialise the caps from unadvertised static capabilities of the device */ +static inline void tmc_etr_init_caps(struct tmc_drvdata *drvdata, u32 dev_caps) +{ + WARN_ON(drvdata->etr_caps); + drvdata->etr_caps = dev_caps; +} + +static inline void tmc_etr_set_cap(struct tmc_drvdata *drvdata, u32 cap) +{ + drvdata->etr_caps |= cap; +} + +static inline bool tmc_etr_has_cap(struct tmc_drvdata *drvdata, u32 cap) +{ + return !!(drvdata->etr_caps & cap); +} + #endif diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 0673baf0f2f5..d7a3e453016d 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -192,7 +192,7 @@ static const struct dev_pm_ops tpiu_dev_pm_ops = { SET_RUNTIME_PM_OPS(tpiu_runtime_suspend, tpiu_runtime_resume, NULL) }; -static struct amba_id tpiu_ids[] = { +static const struct amba_id tpiu_ids[] = { { .id = 0x0003b912, .mask = 0x0003ffff, @@ -201,6 +201,11 @@ static struct amba_id tpiu_ids[] = { .id = 0x0004b912, .mask = 0x0007ffff, }, + { + /* Coresight SoC-600 */ + .id = 0x000bb9e7, + .mask = 0x000fffff, + }, { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 6a0202b7384f..b8091bef21dc 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -53,6 +53,14 @@ static DEFINE_PER_CPU(struct list_head *, tracer_path); */ static struct list_head *stm_path; +/* + * When losing synchronisation a new barrier packet needs to be inserted at the + * beginning of the data collected in a buffer. That way the decoder knows that + * it needs to look for another sync sequence. + */ +const u32 barrier_pkt[5] = {0x7fffffff, 0x7fffffff, + 0x7fffffff, 0x7fffffff, 0x0}; + static int coresight_id_match(struct device *dev, void *data) { int trace_id, i_trace_id; diff --git a/drivers/hwtracing/intel_th/core.c b/drivers/hwtracing/intel_th/core.c index 8da567abc0ce..1a023e30488c 100644 --- a/drivers/hwtracing/intel_th/core.c +++ b/drivers/hwtracing/intel_th/core.c @@ -101,17 +101,53 @@ out_pm: return ret; } +static void intel_th_device_remove(struct intel_th_device *thdev); + static int intel_th_remove(struct device *dev) { struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver); struct intel_th_device *thdev = to_intel_th_device(dev); - struct intel_th_device *hub = to_intel_th_device(dev->parent); + struct intel_th_device *hub = to_intel_th_hub(thdev); int err; if (thdev->type == INTEL_TH_SWITCH) { + struct intel_th *th = to_intel_th(hub); + int i, lowest; + + /* disconnect outputs */ err = device_for_each_child(dev, thdev, intel_th_child_remove); if (err) return err; + + /* + * Remove outputs, that is, hub's children: they are created + * at hub's probe time by having the hub call + * intel_th_output_enable() for each of them. + */ + for (i = 0, lowest = -1; i < th->num_thdevs; i++) { + /* + * Move the non-output devices from higher up the + * th->thdev[] array to lower positions to maintain + * a contiguous array. + */ + if (th->thdev[i]->type != INTEL_TH_OUTPUT) { + if (lowest >= 0) { + th->thdev[lowest] = th->thdev[i]; + th->thdev[i] = NULL; + ++lowest; + } + + continue; + } + + if (lowest == -1) + lowest = i; + + intel_th_device_remove(th->thdev[i]); + th->thdev[i] = NULL; + } + + th->num_thdevs = lowest; } if (thdrv->attr_group) @@ -156,21 +192,6 @@ static struct device_type intel_th_source_device_type = { .release = intel_th_device_release, }; -static struct intel_th *to_intel_th(struct intel_th_device *thdev) -{ - /* - * subdevice tree is flat: if this one is not a switch, its - * parent must be - */ - if (thdev->type != INTEL_TH_SWITCH) - thdev = to_intel_th_hub(thdev); - - if (WARN_ON_ONCE(!thdev || thdev->type != INTEL_TH_SWITCH)) - return NULL; - - return dev_get_drvdata(thdev->dev.parent); -} - static char *intel_th_output_devnode(struct device *dev, umode_t *mode, kuid_t *uid, kgid_t *gid) { @@ -205,6 +226,7 @@ static int intel_th_output_activate(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver_or_null(thdev->dev.driver); + struct intel_th *th = to_intel_th(thdev); int ret = 0; if (!thdrv) @@ -215,15 +237,28 @@ static int intel_th_output_activate(struct intel_th_device *thdev) pm_runtime_get_sync(&thdev->dev); + if (th->activate) + ret = th->activate(th); + if (ret) + goto fail_put; + if (thdrv->activate) ret = thdrv->activate(thdev); else intel_th_trace_enable(thdev); - if (ret) { - pm_runtime_put(&thdev->dev); - module_put(thdrv->driver.owner); - } + if (ret) + goto fail_deactivate; + + return 0; + +fail_deactivate: + if (th->deactivate) + th->deactivate(th); + +fail_put: + pm_runtime_put(&thdev->dev); + module_put(thdrv->driver.owner); return ret; } @@ -232,6 +267,7 @@ static void intel_th_output_deactivate(struct intel_th_device *thdev) { struct intel_th_driver *thdrv = to_intel_th_driver_or_null(thdev->dev.driver); + struct intel_th *th = to_intel_th(thdev); if (!thdrv) return; @@ -241,6 +277,9 @@ static void intel_th_output_deactivate(struct intel_th_device *thdev) else intel_th_trace_disable(thdev); + if (th->deactivate) + th->deactivate(th); + pm_runtime_put(&thdev->dev); module_put(thdrv->driver.owner); } @@ -326,10 +365,10 @@ intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name, struct device *parent; struct intel_th_device *thdev; - if (type == INTEL_TH_SWITCH) - parent = th->dev; - else + if (type == INTEL_TH_OUTPUT) parent = &th->hub->dev; + else + parent = th->dev; thdev = kzalloc(sizeof(*thdev) + strlen(name) + 1, GFP_KERNEL); if (!thdev) @@ -392,13 +431,14 @@ static const struct intel_th_subdevice { unsigned otype; unsigned scrpd; int id; -} intel_th_subdevices[TH_SUBDEVICE_MAX] = { +} intel_th_subdevices[] = { { .nres = 1, .res = { { + /* Handle TSCU from GTH driver */ .start = REG_GTH_OFFSET, - .end = REG_GTH_OFFSET + REG_GTH_LENGTH - 1, + .end = REG_TSCU_OFFSET + REG_TSCU_LENGTH - 1, .flags = IORESOURCE_MEM, }, }, @@ -483,6 +523,21 @@ static const struct intel_th_subdevice { .nres = 1, .res = { { + .start = REG_PTI_OFFSET, + .end = REG_PTI_OFFSET + REG_PTI_LENGTH - 1, + .flags = IORESOURCE_MEM, + }, + }, + .id = -1, + .name = "lpp", + .type = INTEL_TH_OUTPUT, + .otype = GTH_LPP, + .scrpd = SCRPD_PTI_IS_PRIM_DEST, + }, + { + .nres = 1, + .res = { + { .start = REG_DCIH_OFFSET, .end = REG_DCIH_OFFSET + REG_DCIH_LENGTH - 1, .flags = IORESOURCE_MEM, @@ -526,98 +581,182 @@ static inline void intel_th_request_hub_module_flush(struct intel_th *th) } #endif /* CONFIG_MODULES */ -static int intel_th_populate(struct intel_th *th, struct resource *devres, - unsigned int ndevres, int irq) +static struct intel_th_device * +intel_th_subdevice_alloc(struct intel_th *th, + const struct intel_th_subdevice *subdev) { + struct intel_th_device *thdev; struct resource res[3]; unsigned int req = 0; - int src, dst, err; + int r, err; - /* create devices for each intel_th_subdevice */ - for (src = 0, dst = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) { - const struct intel_th_subdevice *subdev = - &intel_th_subdevices[src]; - struct intel_th_device *thdev; - int r; + thdev = intel_th_device_alloc(th, subdev->type, subdev->name, + subdev->id); + if (!thdev) + return ERR_PTR(-ENOMEM); - /* only allow SOURCE and SWITCH devices in host mode */ - if (host_mode && subdev->type == INTEL_TH_OUTPUT) - continue; + thdev->drvdata = th->drvdata; + + memcpy(res, subdev->res, + sizeof(struct resource) * subdev->nres); + + for (r = 0; r < subdev->nres; r++) { + struct resource *devres = th->resource; + int bar = TH_MMIO_CONFIG; + + /* + * Take .end == 0 to mean 'take the whole bar', + * .start then tells us which bar it is. Default to + * TH_MMIO_CONFIG. + */ + if (!res[r].end && res[r].flags == IORESOURCE_MEM) { + bar = res[r].start; + res[r].start = 0; + res[r].end = resource_size(&devres[bar]) - 1; + } + + if (res[r].flags & IORESOURCE_MEM) { + res[r].start += devres[bar].start; + res[r].end += devres[bar].start; - thdev = intel_th_device_alloc(th, subdev->type, subdev->name, - subdev->id); - if (!thdev) { - err = -ENOMEM; - goto kill_subdevs; + dev_dbg(th->dev, "%s:%d @ %pR\n", + subdev->name, r, &res[r]); + } else if (res[r].flags & IORESOURCE_IRQ) { + res[r].start = th->irq; } + } - memcpy(res, subdev->res, - sizeof(struct resource) * subdev->nres); + err = intel_th_device_add_resources(thdev, res, subdev->nres); + if (err) { + put_device(&thdev->dev); + goto fail_put_device; + } - for (r = 0; r < subdev->nres; r++) { - int bar = TH_MMIO_CONFIG; + if (subdev->type == INTEL_TH_OUTPUT) { + thdev->dev.devt = MKDEV(th->major, th->num_thdevs); + thdev->output.type = subdev->otype; + thdev->output.port = -1; + thdev->output.scratchpad = subdev->scrpd; + } else if (subdev->type == INTEL_TH_SWITCH) { + thdev->host_mode = host_mode; + th->hub = thdev; + } - /* - * Take .end == 0 to mean 'take the whole bar', - * .start then tells us which bar it is. Default to - * TH_MMIO_CONFIG. - */ - if (!res[r].end && res[r].flags == IORESOURCE_MEM) { - bar = res[r].start; - res[r].start = 0; - res[r].end = resource_size(&devres[bar]) - 1; - } + err = device_add(&thdev->dev); + if (err) { + put_device(&thdev->dev); + goto fail_free_res; + } - if (res[r].flags & IORESOURCE_MEM) { - res[r].start += devres[bar].start; - res[r].end += devres[bar].start; + /* need switch driver to be loaded to enumerate the rest */ + if (subdev->type == INTEL_TH_SWITCH && !req) { + err = intel_th_request_hub_module(th); + if (!err) + req++; + } - dev_dbg(th->dev, "%s:%d @ %pR\n", - subdev->name, r, &res[r]); - } else if (res[r].flags & IORESOURCE_IRQ) { - res[r].start = irq; - } - } + return thdev; - err = intel_th_device_add_resources(thdev, res, subdev->nres); - if (err) { - put_device(&thdev->dev); - goto kill_subdevs; - } +fail_free_res: + kfree(thdev->resource); - if (subdev->type == INTEL_TH_OUTPUT) { - thdev->dev.devt = MKDEV(th->major, dst); - thdev->output.type = subdev->otype; - thdev->output.port = -1; - thdev->output.scratchpad = subdev->scrpd; - } else if (subdev->type == INTEL_TH_SWITCH) { - thdev->host_mode = host_mode; - } +fail_put_device: + put_device(&thdev->dev); + + return ERR_PTR(err); +} - err = device_add(&thdev->dev); - if (err) { - put_device(&thdev->dev); - goto kill_subdevs; +/** + * intel_th_output_enable() - find and enable a device for a given output type + * @th: Intel TH instance + * @otype: output type + * + * Go through the unallocated output devices, find the first one whos type + * matches @otype and instantiate it. These devices are removed when the hub + * device is removed, see intel_th_remove(). + */ +int intel_th_output_enable(struct intel_th *th, unsigned int otype) +{ + struct intel_th_device *thdev; + int src = 0, dst = 0; + + for (src = 0, dst = 0; dst <= th->num_thdevs; src++, dst++) { + for (; src < ARRAY_SIZE(intel_th_subdevices); src++) { + if (intel_th_subdevices[src].type != INTEL_TH_OUTPUT) + continue; + + if (intel_th_subdevices[src].otype != otype) + continue; + + break; } - /* need switch driver to be loaded to enumerate the rest */ - if (subdev->type == INTEL_TH_SWITCH && !req) { - th->hub = thdev; - err = intel_th_request_hub_module(th); - if (!err) - req++; + /* no unallocated matching subdevices */ + if (src == ARRAY_SIZE(intel_th_subdevices)) + return -ENODEV; + + for (; dst < th->num_thdevs; dst++) { + if (th->thdev[dst]->type != INTEL_TH_OUTPUT) + continue; + + if (th->thdev[dst]->output.type != otype) + continue; + + break; } - th->thdev[dst++] = thdev; + /* + * intel_th_subdevices[src] matches our requirements and is + * not matched in th::thdev[] + */ + if (dst == th->num_thdevs) + goto found; } + return -ENODEV; + +found: + thdev = intel_th_subdevice_alloc(th, &intel_th_subdevices[src]); + if (IS_ERR(thdev)) + return PTR_ERR(thdev); + + th->thdev[th->num_thdevs++] = thdev; + return 0; +} +EXPORT_SYMBOL_GPL(intel_th_output_enable); + +static int intel_th_populate(struct intel_th *th) +{ + int src; + + /* create devices for each intel_th_subdevice */ + for (src = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) { + const struct intel_th_subdevice *subdev = + &intel_th_subdevices[src]; + struct intel_th_device *thdev; + + /* only allow SOURCE and SWITCH devices in host mode */ + if (host_mode && subdev->type == INTEL_TH_OUTPUT) + continue; -kill_subdevs: - for (; dst >= 0; dst--) - intel_th_device_remove(th->thdev[dst]); + /* + * don't enable port OUTPUTs in this path; SWITCH enables them + * via intel_th_output_enable() + */ + if (subdev->type == INTEL_TH_OUTPUT && + subdev->otype != GTH_NONE) + continue; + + thdev = intel_th_subdevice_alloc(th, subdev); + /* note: caller should free subdevices from th::thdev[] */ + if (IS_ERR(thdev)) + return PTR_ERR(thdev); + + th->thdev[th->num_thdevs++] = thdev; + } - return err; + return 0; } static int match_devt(struct device *dev, void *data) @@ -670,8 +809,8 @@ static const struct file_operations intel_th_output_fops = { * @irq: irq number */ struct intel_th * -intel_th_alloc(struct device *dev, struct resource *devres, - unsigned int ndevres, int irq) +intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, + struct resource *devres, unsigned int ndevres, int irq) { struct intel_th *th; int err; @@ -693,6 +832,11 @@ intel_th_alloc(struct device *dev, struct resource *devres, goto err_ida; } th->dev = dev; + th->drvdata = drvdata; + + th->resource = devres; + th->num_resources = ndevres; + th->irq = irq; dev_set_drvdata(dev, th); @@ -700,18 +844,15 @@ intel_th_alloc(struct device *dev, struct resource *devres, pm_runtime_put(dev); pm_runtime_allow(dev); - err = intel_th_populate(th, devres, ndevres, irq); - if (err) - goto err_chrdev; + err = intel_th_populate(th); + if (err) { + /* free the subdevices and undo everything */ + intel_th_free(th); + return ERR_PTR(err); + } return th; -err_chrdev: - pm_runtime_forbid(dev); - - __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS, - "intel_th/output"); - err_ida: ida_simple_remove(&intel_th_ida, th->id); @@ -727,11 +868,15 @@ void intel_th_free(struct intel_th *th) int i; intel_th_request_hub_module_flush(th); - for (i = 0; i < TH_SUBDEVICE_MAX; i++) - if (th->thdev[i] && th->thdev[i] != th->hub) - intel_th_device_remove(th->thdev[i]); intel_th_device_remove(th->hub); + for (i = 0; i < th->num_thdevs; i++) { + if (th->thdev[i] != th->hub) + intel_th_device_remove(th->thdev[i]); + th->thdev[i] = NULL; + } + + th->num_thdevs = 0; pm_runtime_get_sync(th->dev); pm_runtime_forbid(th->dev); diff --git a/drivers/hwtracing/intel_th/gth.c b/drivers/hwtracing/intel_th/gth.c index dd32d0bad687..018678ec3c13 100644 --- a/drivers/hwtracing/intel_th/gth.c +++ b/drivers/hwtracing/intel_th/gth.c @@ -285,16 +285,16 @@ gth_output_parm_get(struct gth_device *gth, int port, unsigned int parm) */ static int intel_th_gth_reset(struct gth_device *gth) { - u32 scratchpad; + u32 reg; int port, i; - scratchpad = ioread32(gth->base + REG_GTH_SCRPD0); - if (scratchpad & SCRPD_DEBUGGER_IN_USE) + reg = ioread32(gth->base + REG_GTH_SCRPD0); + if (reg & SCRPD_DEBUGGER_IN_USE) return -EBUSY; /* Always save/restore STH and TU registers in S0ix entry/exit */ - scratchpad |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; - iowrite32(scratchpad, gth->base + REG_GTH_SCRPD0); + reg |= SCRPD_STH_IS_ENABLED | SCRPD_TRIGGER_IS_ENABLED; + iowrite32(reg, gth->base + REG_GTH_SCRPD0); /* output ports */ for (port = 0; port < 8; port++) { @@ -512,6 +512,15 @@ static void intel_th_gth_disable(struct intel_th_device *thdev, iowrite32(reg, gth->base + REG_GTH_SCRPD0); } +static void gth_tscu_resync(struct gth_device *gth) +{ + u32 reg; + + reg = ioread32(gth->base + REG_TSCU_TSUCTRL); + reg &= ~TSUCTRL_CTCRESYNC; + iowrite32(reg, gth->base + REG_TSCU_TSUCTRL); +} + /** * intel_th_gth_enable() - enable tracing to an output device * @thdev: GTH device @@ -524,6 +533,7 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, struct intel_th_output *output) { struct gth_device *gth = dev_get_drvdata(&thdev->dev); + struct intel_th *th = to_intel_th(thdev); u32 scr = 0xfc0000, scrpd; int master; @@ -539,6 +549,9 @@ static void intel_th_gth_enable(struct intel_th_device *thdev, output->active = true; spin_unlock(>h->gth_lock); + if (INTEL_TH_CAP(th, tscu_enable)) + gth_tscu_resync(gth); + scrpd = ioread32(gth->base + REG_GTH_SCRPD0); scrpd |= output->scratchpad; iowrite32(scrpd, gth->base + REG_GTH_SCRPD0); @@ -639,6 +652,7 @@ intel_th_gth_set_output(struct intel_th_device *thdev, unsigned int master) static int intel_th_gth_probe(struct intel_th_device *thdev) { struct device *dev = &thdev->dev; + struct intel_th *th = dev_get_drvdata(dev->parent); struct gth_device *gth; struct resource *res; void __iomem *base; @@ -660,6 +674,8 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) gth->base = base; spin_lock_init(>h->gth_lock); + dev_set_drvdata(dev, gth); + /* * Host mode can be signalled via SW means or via SCRPD_DEBUGGER_IN_USE * bit. Either way, don't reset HW in this case, and don't export any @@ -667,7 +683,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) * drivers to ports, see intel_th_gth_assign(). */ if (thdev->host_mode) - goto done; + return 0; ret = intel_th_gth_reset(gth); if (ret) { @@ -676,7 +692,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) thdev->host_mode = true; - goto done; + return 0; } for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++) @@ -687,6 +703,13 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) gth->output[i].index = i; gth->output[i].port_type = gth_output_parm_get(gth, i, TH_OUTPUT_PARM(port)); + if (gth->output[i].port_type == GTH_NONE) + continue; + + ret = intel_th_output_enable(th, gth->output[i].port_type); + /* -ENODEV is ok, we just won't have that device enumerated */ + if (ret && ret != -ENODEV) + return ret; } if (intel_th_output_attributes(gth) || @@ -698,9 +721,6 @@ static int intel_th_gth_probe(struct intel_th_device *thdev) return -ENOMEM; } -done: - dev_set_drvdata(dev, gth); - return 0; } diff --git a/drivers/hwtracing/intel_th/gth.h b/drivers/hwtracing/intel_th/gth.h index 56f0d2620577..f3d234251a12 100644 --- a/drivers/hwtracing/intel_th/gth.h +++ b/drivers/hwtracing/intel_th/gth.h @@ -55,9 +55,14 @@ enum { REG_GTH_SCRPD1 = 0xe4, /* ScratchPad[1] */ REG_GTH_SCRPD2 = 0xe8, /* ScratchPad[2] */ REG_GTH_SCRPD3 = 0xec, /* ScratchPad[3] */ + REG_TSCU_TSUCTRL = 0x2000, /* TSCU control register */ + REG_TSCU_TSCUSTAT = 0x2004, /* TSCU status register */ }; /* waiting for Pipeline Empty bit(s) to assert for GTH */ #define GTH_PLE_WAITLOOP_DEPTH 10000 +#define TSUCTRL_CTCRESYNC BIT(0) +#define TSCUSTAT_CTCSYNCING BIT(1) + #endif /* __INTEL_TH_GTH_H__ */ diff --git a/drivers/hwtracing/intel_th/intel_th.h b/drivers/hwtracing/intel_th/intel_th.h index 3096e7054f6d..99ad563fc40d 100644 --- a/drivers/hwtracing/intel_th/intel_th.h +++ b/drivers/hwtracing/intel_th/intel_th.h @@ -48,8 +48,19 @@ struct intel_th_output { }; /** + * struct intel_th_drvdata - describes hardware capabilities and quirks + * @tscu_enable: device needs SW to enable time stamping unit + */ +struct intel_th_drvdata { + unsigned int tscu_enable : 1; +}; + +#define INTEL_TH_CAP(_th, _cap) ((_th)->drvdata ? (_th)->drvdata->_cap : 0) + +/** * struct intel_th_device - device on the intel_th bus * @dev: device + * @drvdata: hardware capabilities/quirks * @resource: array of resources available to this device * @num_resources: number of resources in @resource array * @type: INTEL_TH_{SOURCE,OUTPUT,SWITCH} @@ -59,11 +70,12 @@ struct intel_th_output { * @name: device name to match the driver */ struct intel_th_device { - struct device dev; - struct resource *resource; - unsigned int num_resources; - unsigned int type; - int id; + struct device dev; + struct intel_th_drvdata *drvdata; + struct resource *resource; + unsigned int num_resources; + unsigned int type; + int id; /* INTEL_TH_SWITCH specific */ bool host_mode; @@ -96,6 +108,17 @@ intel_th_device_get_resource(struct intel_th_device *thdev, unsigned int type, return NULL; } +/* + * GTH, output ports configuration + */ +enum { + GTH_NONE = 0, + GTH_MSU, /* memory/usb */ + GTH_CTP, /* Common Trace Port */ + GTH_LPP, /* Low Power Path */ + GTH_PTI, /* MIPI-PTI */ +}; + /** * intel_th_output_assigned() - if an output device is assigned to a switch port * @thdev: the output device @@ -106,7 +129,8 @@ static inline bool intel_th_output_assigned(struct intel_th_device *thdev) { return thdev->type == INTEL_TH_OUTPUT && - thdev->output.port >= 0; + (thdev->output.port >= 0 || + thdev->output.type == GTH_NONE); } /** @@ -161,8 +185,18 @@ struct intel_th_driver { #define to_intel_th_driver_or_null(_d) \ ((_d) ? to_intel_th_driver(_d) : NULL) +/* + * Subdevice tree structure is as follows: + * + struct intel_th device (pci; dev_{get,set}_drvdata() + * + struct intel_th_device INTEL_TH_SWITCH (GTH) + * + struct intel_th_device INTEL_TH_OUTPUT (MSU, PTI) + * + struct intel_th_device INTEL_TH_SOURCE (STH) + * + * In other words, INTEL_TH_OUTPUT devices are children of INTEL_TH_SWITCH; + * INTEL_TH_SWITCH and INTEL_TH_SOURCE are children of the intel_th device. + */ static inline struct intel_th_device * -to_intel_th_hub(struct intel_th_device *thdev) +to_intel_th_parent(struct intel_th_device *thdev) { struct device *parent = thdev->dev.parent; @@ -172,9 +206,20 @@ to_intel_th_hub(struct intel_th_device *thdev) return to_intel_th_device(parent); } +static inline struct intel_th *to_intel_th(struct intel_th_device *thdev) +{ + if (thdev->type == INTEL_TH_OUTPUT) + thdev = to_intel_th_parent(thdev); + + if (WARN_ON_ONCE(!thdev || thdev->type == INTEL_TH_OUTPUT)) + return NULL; + + return dev_get_drvdata(thdev->dev.parent); +} + struct intel_th * -intel_th_alloc(struct device *dev, struct resource *devres, - unsigned int ndevres, int irq); +intel_th_alloc(struct device *dev, struct intel_th_drvdata *drvdata, + struct resource *devres, unsigned int ndevres, int irq); void intel_th_free(struct intel_th *th); int intel_th_driver_register(struct intel_th_driver *thdrv); @@ -184,6 +229,7 @@ int intel_th_trace_enable(struct intel_th_device *thdev); int intel_th_trace_disable(struct intel_th_device *thdev); int intel_th_set_output(struct intel_th_device *thdev, unsigned int master); +int intel_th_output_enable(struct intel_th *th, unsigned int otype); enum { TH_MMIO_CONFIG = 0, @@ -191,8 +237,9 @@ enum { TH_MMIO_END, }; -#define TH_SUBDEVICE_MAX 6 #define TH_POSSIBLE_OUTPUTS 8 +/* Total number of possible subdevices: outputs + GTH + STH */ +#define TH_SUBDEVICE_MAX (TH_POSSIBLE_OUTPUTS + 2) #define TH_CONFIGURABLE_MASTERS 256 #define TH_MSC_MAX 2 @@ -201,6 +248,10 @@ enum { * @dev: driver core's device * @thdev: subdevices * @hub: "switch" subdevice (GTH) + * @resource: resources of the entire controller + * @num_thdevs: number of devices in the @thdev array + * @num_resources: number or resources in the @resource array + * @irq: irq number * @id: this Intel TH controller's device ID in the system * @major: device node major for output devices */ @@ -209,6 +260,14 @@ struct intel_th { struct intel_th_device *thdev[TH_SUBDEVICE_MAX]; struct intel_th_device *hub; + struct intel_th_drvdata *drvdata; + + struct resource *resource; + int (*activate)(struct intel_th *); + void (*deactivate)(struct intel_th *); + unsigned int num_thdevs; + unsigned int num_resources; + int irq; int id; int major; @@ -220,6 +279,17 @@ struct intel_th { #endif }; +static inline struct intel_th_device * +to_intel_th_hub(struct intel_th_device *thdev) +{ + if (thdev->type == INTEL_TH_SWITCH) + return thdev; + else if (thdev->type == INTEL_TH_OUTPUT) + return to_intel_th_parent(thdev); + + return to_intel_th(thdev)->hub; +} + /* * Register windows */ @@ -228,6 +298,10 @@ enum { REG_GTH_OFFSET = 0x0000, REG_GTH_LENGTH = 0x2000, + /* Timestamp counter unit (TSCU) */ + REG_TSCU_OFFSET = 0x2000, + REG_TSCU_LENGTH = 0x1000, + /* Software Trace Hub (STH) [0x4000..0x4fff] */ REG_STH_OFFSET = 0x4000, REG_STH_LENGTH = 0x2000, @@ -250,16 +324,6 @@ enum { }; /* - * GTH, output ports configuration - */ -enum { - GTH_NONE = 0, - GTH_MSU, /* memory/usb */ - GTH_CTP, /* Common Trace Port */ - GTH_PTI = 4, /* MIPI-PTI */ -}; - -/* * Scratchpad bits: tell firmware and external debuggers * what we are up to. */ diff --git a/drivers/hwtracing/intel_th/msu.c b/drivers/hwtracing/intel_th/msu.c index dbbe31df74df..dfb57eaa9f22 100644 --- a/drivers/hwtracing/intel_th/msu.c +++ b/drivers/hwtracing/intel_th/msu.c @@ -709,17 +709,17 @@ static int msc_buffer_win_alloc(struct msc *msc, unsigned int nr_blocks) } for (i = 0; i < nr_blocks; i++) { - win->block[i].bdesc = dma_alloc_coherent(msc_dev(msc), size, - &win->block[i].addr, - GFP_KERNEL); + win->block[i].bdesc = + dma_alloc_coherent(msc_dev(msc)->parent->parent, size, + &win->block[i].addr, GFP_KERNEL); + + if (!win->block[i].bdesc) + goto err_nomem; #ifdef CONFIG_X86 /* Set the page as uncached */ set_memory_uc((unsigned long)win->block[i].bdesc, 1); #endif - - if (!win->block[i].bdesc) - goto err_nomem; } win->msc = msc; diff --git a/drivers/hwtracing/intel_th/pci.c b/drivers/hwtracing/intel_th/pci.c index 590cf90dd21a..bc9cebc30526 100644 --- a/drivers/hwtracing/intel_th/pci.c +++ b/drivers/hwtracing/intel_th/pci.c @@ -27,9 +27,53 @@ #define BAR_MASK (BIT(TH_MMIO_CONFIG) | BIT(TH_MMIO_SW)) +#define PCI_REG_NPKDSC 0x80 +#define NPKDSC_TSACT BIT(5) + +static int intel_th_pci_activate(struct intel_th *th) +{ + struct pci_dev *pdev = to_pci_dev(th->dev); + u32 npkdsc; + int err; + + if (!INTEL_TH_CAP(th, tscu_enable)) + return 0; + + err = pci_read_config_dword(pdev, PCI_REG_NPKDSC, &npkdsc); + if (!err) { + npkdsc |= NPKDSC_TSACT; + err = pci_write_config_dword(pdev, PCI_REG_NPKDSC, npkdsc); + } + + if (err) + dev_err(&pdev->dev, "failed to read NPKDSC register\n"); + + return err; +} + +static void intel_th_pci_deactivate(struct intel_th *th) +{ + struct pci_dev *pdev = to_pci_dev(th->dev); + u32 npkdsc; + int err; + + if (!INTEL_TH_CAP(th, tscu_enable)) + return; + + err = pci_read_config_dword(pdev, PCI_REG_NPKDSC, &npkdsc); + if (!err) { + npkdsc |= NPKDSC_TSACT; + err = pci_write_config_dword(pdev, PCI_REG_NPKDSC, npkdsc); + } + + if (err) + dev_err(&pdev->dev, "failed to read NPKDSC register\n"); +} + static int intel_th_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + struct intel_th_drvdata *drvdata = (void *)id->driver_data; struct intel_th *th; int err; @@ -41,11 +85,16 @@ static int intel_th_pci_probe(struct pci_dev *pdev, if (err) return err; - th = intel_th_alloc(&pdev->dev, pdev->resource, + th = intel_th_alloc(&pdev->dev, drvdata, pdev->resource, DEVICE_COUNT_RESOURCE, pdev->irq); if (IS_ERR(th)) return PTR_ERR(th); + th->activate = intel_th_pci_activate; + th->deactivate = intel_th_pci_deactivate; + + pci_set_master(pdev); + return 0; } @@ -56,6 +105,10 @@ static void intel_th_pci_remove(struct pci_dev *pdev) intel_th_free(th); } +static const struct intel_th_drvdata intel_th_2x = { + .tscu_enable = 1, +}; + static const struct pci_device_id intel_th_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9d26), @@ -93,7 +146,17 @@ static const struct pci_device_id intel_th_pci_id_table[] = { { /* Gemini Lake */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x318e), - .driver_data = (kernel_ulong_t)0, + .driver_data = (kernel_ulong_t)&intel_th_2x, + }, + { + /* Cannon Lake H */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xa326), + .driver_data = (kernel_ulong_t)&intel_th_2x, + }, + { + /* Cannon Lake LP */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x9da6), + .driver_data = (kernel_ulong_t)&intel_th_2x, }, { 0 }, }; diff --git a/drivers/hwtracing/intel_th/pti.c b/drivers/hwtracing/intel_th/pti.c index 35738b5bfccd..e96a1fcb57b2 100644 --- a/drivers/hwtracing/intel_th/pti.c +++ b/drivers/hwtracing/intel_th/pti.c @@ -1,7 +1,7 @@ /* * Intel(R) Trace Hub PTI output driver * - * Copyright (C) 2014-2015 Intel Corporation. + * Copyright (C) 2014-2016 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, @@ -34,6 +34,8 @@ struct pti_device { unsigned int freeclk; unsigned int clkdiv; unsigned int patgen; + unsigned int lpp_dest_mask; + unsigned int lpp_dest; }; /* map PTI widths to MODE settings of PTI_CTL register */ @@ -163,6 +165,7 @@ static int intel_th_pti_activate(struct intel_th_device *thdev) ctl |= PTI_FCEN; ctl |= pti->mode << __ffs(PTI_MODE); ctl |= pti->clkdiv << __ffs(PTI_CLKDIV); + ctl |= pti->lpp_dest << __ffs(LPP_DEST); iowrite32(ctl, pti->base + REG_PTI_CTL); @@ -192,6 +195,15 @@ static void read_hw_config(struct pti_device *pti) pti->mode = pti_width_mode(4); if (!pti->clkdiv) pti->clkdiv = 1; + + if (pti->thdev->output.type == GTH_LPP) { + if (ctl & LPP_PTIPRESENT) + pti->lpp_dest_mask |= LPP_DEST_PTI; + if (ctl & LPP_BSSBPRESENT) + pti->lpp_dest_mask |= LPP_DEST_EXI; + if (ctl & LPP_DEST) + pti->lpp_dest = 1; + } } static int intel_th_pti_probe(struct intel_th_device *thdev) @@ -239,10 +251,103 @@ static struct intel_th_driver intel_th_pti_driver = { }, }; -module_driver(intel_th_pti_driver, - intel_th_driver_register, - intel_th_driver_unregister); +static const char * const lpp_dest_str[] = { "pti", "exi" }; + +static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pti_device *pti = dev_get_drvdata(dev); + ssize_t ret = 0; + int i; + + for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) { + const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s "; + + if (!(pti->lpp_dest_mask & BIT(i))) + continue; + + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + fmt, lpp_dest_str[i]); + } + + if (ret) + buf[ret - 1] = '\n'; + + return ret; +} + +static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct pti_device *pti = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(lpp_dest_str); i++) + if (sysfs_streq(buf, lpp_dest_str[i])) + break; + + if (i < ARRAY_SIZE(lpp_dest_str) && pti->lpp_dest_mask & BIT(i)) { + pti->lpp_dest = i; + ret = size; + } + + return ret; +} + +static DEVICE_ATTR_RW(lpp_dest); + +static struct attribute *lpp_output_attrs[] = { + &dev_attr_mode.attr, + &dev_attr_freerunning_clock.attr, + &dev_attr_clock_divider.attr, + &dev_attr_lpp_dest.attr, + NULL, +}; + +static struct attribute_group lpp_output_group = { + .attrs = lpp_output_attrs, +}; + +static struct intel_th_driver intel_th_lpp_driver = { + .probe = intel_th_pti_probe, + .remove = intel_th_pti_remove, + .activate = intel_th_pti_activate, + .deactivate = intel_th_pti_deactivate, + .attr_group = &lpp_output_group, + .driver = { + .name = "lpp", + .owner = THIS_MODULE, + }, +}; + +static int __init intel_th_pti_lpp_init(void) +{ + int err; + + err = intel_th_driver_register(&intel_th_pti_driver); + if (err) + return err; + + err = intel_th_driver_register(&intel_th_lpp_driver); + if (err) { + intel_th_driver_unregister(&intel_th_pti_driver); + return err; + } + + return 0; +} + +module_init(intel_th_pti_lpp_init); + +static void __exit intel_th_pti_lpp_exit(void) +{ + intel_th_driver_unregister(&intel_th_pti_driver); + intel_th_driver_unregister(&intel_th_lpp_driver); +} + +module_exit(intel_th_pti_lpp_exit); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver"); +MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver"); MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); diff --git a/drivers/hwtracing/intel_th/pti.h b/drivers/hwtracing/intel_th/pti.h index 20883f5628cf..30827be67b4c 100644 --- a/drivers/hwtracing/intel_th/pti.h +++ b/drivers/hwtracing/intel_th/pti.h @@ -23,7 +23,15 @@ enum { #define PTI_EN BIT(0) #define PTI_FCEN BIT(1) #define PTI_MODE 0xf0 +#define LPP_PTIPRESENT BIT(8) +#define LPP_BSSBPRESENT BIT(9) #define PTI_CLKDIV 0x000f0000 #define PTI_PATGENMODE 0x00f00000 +#define LPP_DEST BIT(25) +#define LPP_BSSBACT BIT(30) +#define LPP_LPPBUSY BIT(31) + +#define LPP_DEST_PTI BIT(0) +#define LPP_DEST_EXI BIT(1) #endif /* __INTEL_TH_STH_H__ */ diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 0e731143f6a4..9414900575d8 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -566,7 +566,7 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) if (copy_from_user(&size, arg, sizeof(size))) return -EFAULT; - if (size >= PATH_MAX + sizeof(*id)) + if (size < sizeof(*id) || size >= PATH_MAX + sizeof(*id)) return -EINVAL; /* diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 1006b230b236..65fa29591d21 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -983,7 +983,7 @@ config I2C_UNIPHIER_F config I2C_VERSATILE tristate "ARM Versatile/Realview I2C bus support" - depends on ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || COMPILE_TEST + depends on ARCH_MPS2 || ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || COMPILE_TEST select I2C_ALGOBIT help Say yes if you want to support the I2C serial bus on ARMs Versatile diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index f19348328a71..6fdf9231c23c 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -410,10 +410,11 @@ static bool aspeed_i2c_master_irq(struct aspeed_i2c_bus *bus) } /* We are in an invalid state; reset bus to a known state. */ - if (!bus->msgs && bus->master_state != ASPEED_I2C_MASTER_STOP) { + if (!bus->msgs) { dev_err(bus->dev, "bus in unknown state"); bus->cmd_err = -EIO; - aspeed_i2c_do_stop(bus); + if (bus->master_state != ASPEED_I2C_MASTER_STOP) + aspeed_i2c_do_stop(bus); goto out_no_complete; } msg = &bus->msgs[bus->msgs_index]; diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 2ea6d0d25a01..57248bccadbc 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -198,8 +198,7 @@ static void i2c_dw_configure_slave(struct dw_i2c_dev *dev) dev->functionality = I2C_FUNC_SLAVE | DW_IC_DEFAULT_FUNCTIONALITY; dev->slave_cfg = DW_IC_CON_RX_FIFO_FULL_HLD_CTRL | - DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED | - DW_IC_CON_SPEED_FAST; + DW_IC_CON_RESTART_EN | DW_IC_CON_STOP_DET_IFADDRESSED; dev->mode = DW_IC_SLAVE; @@ -298,6 +297,9 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) } acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev); + /* Some broken DSTDs use 1MiHz instead of 1MHz */ + if (acpi_speed == 1048576) + acpi_speed = 1000000; /* * Find bus speed from the "clock-frequency" device property, ACPI * or by using fast mode if neither is set. @@ -319,7 +321,8 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) if (dev->clk_freq != 100000 && dev->clk_freq != 400000 && dev->clk_freq != 1000000 && dev->clk_freq != 3400000) { dev_err(&pdev->dev, - "Only 100kHz, 400kHz, 1MHz and 3.4MHz supported"); + "%d Hz is unsupported, only 100kHz, 400kHz, 1MHz and 3.4MHz are supported\n", + dev->clk_freq); ret = -EINVAL; goto exit_reset; } @@ -426,7 +429,7 @@ static void dw_i2c_plat_complete(struct device *dev) #endif #ifdef CONFIG_PM -static int dw_i2c_plat_suspend(struct device *dev) +static int dw_i2c_plat_runtime_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev); @@ -448,11 +451,21 @@ static int dw_i2c_plat_resume(struct device *dev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int dw_i2c_plat_suspend(struct device *dev) +{ + pm_runtime_resume(dev); + return dw_i2c_plat_runtime_suspend(dev); +} +#endif + static const struct dev_pm_ops dw_i2c_dev_pm_ops = { .prepare = dw_i2c_plat_prepare, .complete = dw_i2c_plat_complete, SET_SYSTEM_SLEEP_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume) - SET_RUNTIME_PM_OPS(dw_i2c_plat_suspend, dw_i2c_plat_resume, NULL) + SET_RUNTIME_PM_OPS(dw_i2c_plat_runtime_suspend, + dw_i2c_plat_resume, + NULL) }; #define DW_I2C_DEV_PMOPS (&dw_i2c_dev_pm_ops) diff --git a/drivers/i2c/busses/i2c-designware-slave.c b/drivers/i2c/busses/i2c-designware-slave.c index 0548c7ea578c..78d8fb73927d 100644 --- a/drivers/i2c/busses/i2c-designware-slave.c +++ b/drivers/i2c/busses/i2c-designware-slave.c @@ -177,6 +177,8 @@ static int i2c_dw_reg_slave(struct i2c_client *slave) return -EBUSY; if (slave->flags & I2C_CLIENT_TEN) return -EAFNOSUPPORT; + pm_runtime_get_sync(dev->dev); + /* * Set slave address in the IC_SAR register, * the address to which the DW_apb_i2c responds. @@ -205,6 +207,7 @@ static int i2c_dw_unreg_slave(struct i2c_client *slave) dev->disable_int(dev); dev->disable(dev); dev->slave = NULL; + pm_runtime_put(dev->dev); return 0; } @@ -272,7 +275,7 @@ static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev) slave_activity = ((dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_SLAVE_ACTIVITY) >> 6); - if (!enabled || !(raw_stat & ~DW_IC_INTR_ACTIVITY)) + if (!enabled || !(raw_stat & ~DW_IC_INTR_ACTIVITY) || !dev->slave) return 0; dev_dbg(dev->dev, @@ -382,7 +385,6 @@ int i2c_dw_probe_slave(struct dw_i2c_dev *dev) ret = i2c_add_numbered_adapter(adap); if (ret) dev_err(dev->dev, "failure adding adapter: %d\n", ret); - pm_runtime_put_noidle(dev->dev); return ret; } diff --git a/drivers/i2c/busses/i2c-simtec.c b/drivers/i2c/busses/i2c-simtec.c index b4685bb9b5d7..adca51a99487 100644 --- a/drivers/i2c/busses/i2c-simtec.c +++ b/drivers/i2c/busses/i2c-simtec.c @@ -127,8 +127,7 @@ static int simtec_i2c_probe(struct platform_device *dev) iounmap(pd->reg); err_res: - release_resource(pd->ioarea); - kfree(pd->ioarea); + release_mem_region(pd->ioarea->start, size); err: kfree(pd); @@ -142,8 +141,7 @@ static int simtec_i2c_remove(struct platform_device *dev) i2c_del_adapter(&pd->adap); iounmap(pd->reg); - release_resource(pd->ioarea); - kfree(pd->ioarea); + release_mem_region(pd->ioarea->start, resource_size(pd->ioarea)); kfree(pd); return 0; diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c index 4842ec3a5451..a9126b3cda61 100644 --- a/drivers/i2c/i2c-core-acpi.c +++ b/drivers/i2c/i2c-core-acpi.c @@ -230,6 +230,16 @@ void i2c_acpi_register_devices(struct i2c_adapter *adap) dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); } +const struct acpi_device_id * +i2c_acpi_match_device(const struct acpi_device_id *matches, + struct i2c_client *client) +{ + if (!(client && matches)) + return NULL; + + return acpi_match_device(matches, &client->dev); +} + static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level, void *data, void **return_value) { @@ -289,7 +299,7 @@ u32 i2c_acpi_find_bus_speed(struct device *dev) } EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed); -static int i2c_acpi_match_adapter(struct device *dev, void *data) +static int i2c_acpi_find_match_adapter(struct device *dev, void *data) { struct i2c_adapter *adapter = i2c_verify_adapter(dev); @@ -299,7 +309,7 @@ static int i2c_acpi_match_adapter(struct device *dev, void *data) return ACPI_HANDLE(dev) == (acpi_handle)data; } -static int i2c_acpi_match_device(struct device *dev, void *data) +static int i2c_acpi_find_match_device(struct device *dev, void *data) { return ACPI_COMPANION(dev) == data; } @@ -309,7 +319,7 @@ static struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle) struct device *dev; dev = bus_find_device(&i2c_bus_type, NULL, handle, - i2c_acpi_match_adapter); + i2c_acpi_find_match_adapter); return dev ? i2c_verify_adapter(dev) : NULL; } @@ -317,7 +327,8 @@ static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev) { struct device *dev; - dev = bus_find_device(&i2c_bus_type, NULL, adev, i2c_acpi_match_device); + dev = bus_find_device(&i2c_bus_type, NULL, adev, + i2c_acpi_find_match_device); return dev ? i2c_verify_client(dev) : NULL; } diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index c89dac7fd2e7..56e46581b84b 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -353,10 +353,11 @@ static int i2c_device_probe(struct device *dev) } /* - * An I2C ID table is not mandatory, if and only if, a suitable Device - * Tree match table entry is supplied for the probing device. + * An I2C ID table is not mandatory, if and only if, a suitable OF + * or ACPI ID table is supplied for the probing device. */ if (!driver->id_table && + !i2c_acpi_match_device(dev->driver->acpi_match_table, client) && !i2c_of_match_device(dev->driver->of_match_table, client)) return -ENODEV; diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h index 3b63f5e5b89c..3d3d9bf02101 100644 --- a/drivers/i2c/i2c-core.h +++ b/drivers/i2c/i2c-core.h @@ -31,9 +31,18 @@ int i2c_check_addr_validity(unsigned addr, unsigned short flags); int i2c_check_7bit_addr_validity_strict(unsigned short addr); #ifdef CONFIG_ACPI +const struct acpi_device_id * +i2c_acpi_match_device(const struct acpi_device_id *matches, + struct i2c_client *client); void i2c_acpi_register_devices(struct i2c_adapter *adap); #else /* CONFIG_ACPI */ static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { } +static inline const struct acpi_device_id * +i2c_acpi_match_device(const struct acpi_device_id *matches, + struct i2c_client *client) +{ + return NULL; +} #endif /* CONFIG_ACPI */ extern struct notifier_block i2c_acpi_notifier; diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 2c64d0e0740f..17121329bb79 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -83,7 +83,7 @@ config I2C_MUX_PINCTRL different sets of pins at run-time. This driver can also be built as a module. If so, the module will be - called pinctrl-i2cmux. + called i2c-mux-pinctrl. config I2C_MUX_REG tristate "Register-based I2C multiplexer" diff --git a/drivers/ide/ide-timings.c b/drivers/ide/ide-timings.c index 0e05f75934c9..1858e3ce3993 100644 --- a/drivers/ide/ide-timings.c +++ b/drivers/ide/ide-timings.c @@ -104,19 +104,19 @@ u16 ide_pio_cycle_time(ide_drive_t *drive, u8 pio) EXPORT_SYMBOL_GPL(ide_pio_cycle_time); #define ENOUGH(v, unit) (((v) - 1) / (unit) + 1) -#define EZ(v, unit) ((v) ? ENOUGH(v, unit) : 0) +#define EZ(v, unit) ((v) ? ENOUGH((v) * 1000, unit) : 0) static void ide_timing_quantize(struct ide_timing *t, struct ide_timing *q, int T, int UT) { - q->setup = EZ(t->setup * 1000, T); - q->act8b = EZ(t->act8b * 1000, T); - q->rec8b = EZ(t->rec8b * 1000, T); - q->cyc8b = EZ(t->cyc8b * 1000, T); - q->active = EZ(t->active * 1000, T); - q->recover = EZ(t->recover * 1000, T); - q->cycle = EZ(t->cycle * 1000, T); - q->udma = EZ(t->udma * 1000, UT); + q->setup = EZ(t->setup, T); + q->act8b = EZ(t->act8b, T); + q->rec8b = EZ(t->rec8b, T); + q->cyc8b = EZ(t->cyc8b, T); + q->active = EZ(t->active, T); + q->recover = EZ(t->recover, T); + q->cycle = EZ(t->cycle, T); + q->udma = EZ(t->udma, UT); } void ide_timing_merge(struct ide_timing *a, struct ide_timing *b, diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 6b5d3be283c4..807299dd45eb 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -193,7 +193,6 @@ struct bmc150_accel_data { struct regmap *regmap; int irq; struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS]; - atomic_t active_intr; struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS]; struct mutex mutex; u8 fifo_mode, watermark; @@ -493,11 +492,6 @@ static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i, goto out_fix_power_state; } - if (state) - atomic_inc(&data->active_intr); - else - atomic_dec(&data->active_intr); - return 0; out_fix_power_state: @@ -1710,8 +1704,7 @@ static int bmc150_accel_resume(struct device *dev) struct bmc150_accel_data *data = iio_priv(indio_dev); mutex_lock(&data->mutex); - if (atomic_read(&data->active_intr)) - bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0); + bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0); bmc150_accel_fifo_set_mode(data); mutex_unlock(&data->mutex); diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 07d1489cd457..e44f62bf9caa 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -166,6 +166,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_ihl = 0x02, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, + .sim = { + .addr = 0x23, + .value = BIT(0), + }, .multi_read_bit = true, .bootime = 2, }, @@ -234,6 +238,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_od = 0x40, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, + .sim = { + .addr = 0x23, + .value = BIT(0), + }, .multi_read_bit = true, .bootime = 2, }, @@ -316,6 +324,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .en_mask = 0x08, }, }, + .sim = { + .addr = 0x24, + .value = BIT(0), + }, .multi_read_bit = false, .bootime = 2, }, @@ -379,6 +391,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int1 = 0x04, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, + .sim = { + .addr = 0x21, + .value = BIT(1), + }, .multi_read_bit = true, .bootime = 2, /* guess */ }, @@ -437,6 +453,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_od = 0x40, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, + .sim = { + .addr = 0x21, + .value = BIT(7), + }, .multi_read_bit = false, .bootime = 2, /* guess */ }, @@ -499,6 +519,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .addr_ihl = 0x22, .mask_ihl = 0x80, }, + .sim = { + .addr = 0x23, + .value = BIT(0), + }, .multi_read_bit = true, .bootime = 2, }, @@ -547,6 +571,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_int1 = 0x04, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, + .sim = { + .addr = 0x21, + .value = BIT(1), + }, .multi_read_bit = false, .bootime = 2, }, @@ -614,6 +642,10 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .mask_ihl = 0x02, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, + .sim = { + .addr = 0x23, + .value = BIT(0), + }, .multi_read_bit = true, .bootime = 2, }, diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index e0ea411a0b2d..c02b23d675cb 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -22,6 +22,7 @@ #include <linux/iio/iio.h> #include <linux/iio/driver.h> +#include <linux/iopoll.h> #define ASPEED_RESOLUTION_BITS 10 #define ASPEED_CLOCKS_PER_SAMPLE 12 @@ -38,11 +39,17 @@ #define ASPEED_ENGINE_ENABLE BIT(0) +#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) + +#define ASPEED_ADC_INIT_POLLING_TIME 500 +#define ASPEED_ADC_INIT_TIMEOUT 500000 + struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz unsigned int max_sampling_rate; // Hz unsigned int vref_voltage; // mV + bool wait_init_sequence; }; struct aspeed_adc_data { @@ -211,6 +218,24 @@ static int aspeed_adc_probe(struct platform_device *pdev) goto scaler_error; } + model_data = of_device_get_match_data(&pdev->dev); + + if (model_data->wait_init_sequence) { + /* Enable engine in normal mode. */ + writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE, + data->base + ASPEED_REG_ENGINE_CONTROL); + + /* Wait for initial sequence complete. */ + ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL, + adc_engine_control_reg_val, + adc_engine_control_reg_val & + ASPEED_ADC_CTRL_INIT_RDY, + ASPEED_ADC_INIT_POLLING_TIME, + ASPEED_ADC_INIT_TIMEOUT); + if (ret) + goto scaler_error; + } + /* Start all channels in normal mode. */ ret = clk_prepare_enable(data->clk_scaler->clk); if (ret) @@ -274,6 +299,7 @@ static const struct aspeed_adc_model_data ast2500_model_data = { .vref_voltage = 1800, // mV .min_sampling_rate = 1, .max_sampling_rate = 1000000, + .wait_init_sequence = true, }; static const struct of_device_id aspeed_adc_matches[] = { diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c index 64799ad7ebad..462a99c13e7a 100644 --- a/drivers/iio/adc/axp288_adc.c +++ b/drivers/iio/adc/axp288_adc.c @@ -28,6 +28,8 @@ #include <linux/iio/driver.h> #define AXP288_ADC_EN_MASK 0xF1 +#define AXP288_ADC_TS_PIN_GPADC 0xF2 +#define AXP288_ADC_TS_PIN_ON 0xF3 enum axp288_adc_id { AXP288_ADC_TS, @@ -121,6 +123,26 @@ static int axp288_adc_read_channel(int *val, unsigned long address, return IIO_VAL_INT; } +static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode, + unsigned long address) +{ + int ret; + + /* channels other than GPADC do not need to switch TS pin */ + if (address != AXP288_GP_ADC_H) + return 0; + + ret = regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode); + if (ret) + return ret; + + /* When switching to the GPADC pin give things some time to settle */ + if (mode == AXP288_ADC_TS_PIN_GPADC) + usleep_range(6000, 10000); + + return 0; +} + static int axp288_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -131,7 +153,16 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev, mutex_lock(&indio_dev->mlock); switch (mask) { case IIO_CHAN_INFO_RAW: + if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC, + chan->address)) { + dev_err(&indio_dev->dev, "GPADC mode\n"); + ret = -EINVAL; + break; + } ret = axp288_adc_read_channel(val, chan->address, info->regmap); + if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON, + chan->address)) + dev_err(&indio_dev->dev, "TS pin restore\n"); break; default: ret = -EINVAL; @@ -141,6 +172,15 @@ static int axp288_adc_read_raw(struct iio_dev *indio_dev, return ret; } +static int axp288_adc_set_state(struct regmap *regmap) +{ + /* ADC should be always enabled for internal FG to function */ + if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON)) + return -EIO; + + return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK); +} + static const struct iio_info axp288_adc_iio_info = { .read_raw = &axp288_adc_read_raw, .driver_module = THIS_MODULE, @@ -169,7 +209,7 @@ static int axp288_adc_probe(struct platform_device *pdev) * Set ADC to enabled state at all time, including system suspend. * otherwise internal fuel gauge functionality may be affected. */ - ret = regmap_write(info->regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK); + ret = axp288_adc_set_state(axp20x->regmap); if (ret) { dev_err(&pdev->dev, "unable to enable ADC device\n"); return ret; diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 232c0b80d658..c3f86138cb55 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -644,7 +644,7 @@ static int ina2xx_capture_thread(void *data) { struct iio_dev *indio_dev = data; struct ina2xx_chip_info *chip = iio_priv(indio_dev); - unsigned int sampling_us = SAMPLING_PERIOD(chip); + int sampling_us = SAMPLING_PERIOD(chip); int buffer_us; /* diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c index e09233b03c05..609676384f5e 100644 --- a/drivers/iio/adc/stm32-adc-core.c +++ b/drivers/iio/adc/stm32-adc-core.c @@ -64,7 +64,7 @@ #define STM32H7_CKMODE_MASK GENMASK(17, 16) /* STM32 H7 maximum analog clock rate (from datasheet) */ -#define STM32H7_ADC_MAX_CLK_RATE 72000000 +#define STM32H7_ADC_MAX_CLK_RATE 36000000 /** * stm32_adc_common_regs - stm32 common registers, compatible dependent data @@ -148,14 +148,14 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev, return -EINVAL; } - priv->common.rate = rate; + priv->common.rate = rate / stm32f4_pclk_div[i]; val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR); val &= ~STM32F4_ADC_ADCPRE_MASK; val |= i << STM32F4_ADC_ADCPRE_SHIFT; writel_relaxed(val, priv->common.base + STM32F4_ADC_CCR); dev_dbg(&pdev->dev, "Using analog clock source at %ld kHz\n", - rate / (stm32f4_pclk_div[i] * 1000)); + priv->common.rate / 1000); return 0; } @@ -250,7 +250,7 @@ static int stm32h7_adc_clk_sel(struct platform_device *pdev, out: /* rate used later by each ADC instance to control BOOST mode */ - priv->common.rate = rate; + priv->common.rate = rate / div; /* Set common clock mode and prescaler */ val = readl_relaxed(priv->common.base + STM32H7_ADC_CCR); @@ -260,7 +260,7 @@ out: writel_relaxed(val, priv->common.base + STM32H7_ADC_CCR); dev_dbg(&pdev->dev, "Using %s clock/%d source at %ld kHz\n", - ckmode ? "bus" : "adc", div, rate / (div * 1000)); + ckmode ? "bus" : "adc", div, priv->common.rate / 1000); return 0; } diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index 81d4c39e414a..137f577d9432 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -256,6 +256,7 @@ static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val, err: pm_runtime_put_autosuspend(indio_dev->dev.parent); + disable_irq(irq); mutex_unlock(&info->mutex); return ret; @@ -365,7 +366,6 @@ static irqreturn_t sun4i_gpadc_temp_data_irq_handler(int irq, void *dev_id) complete(&info->completion); out: - disable_irq_nosync(info->temp_data_irq); return IRQ_HANDLED; } @@ -380,7 +380,6 @@ static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id) complete(&info->completion); out: - disable_irq_nosync(info->fifo_data_irq); return IRQ_HANDLED; } diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 01fc76f7d660..c168e0db329a 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -77,7 +77,7 @@ #define VF610_ADC_ADSTS_MASK 0x300 #define VF610_ADC_ADLPC_EN 0x80 #define VF610_ADC_ADHSC_EN 0x400 -#define VF610_ADC_REFSEL_VALT 0x100 +#define VF610_ADC_REFSEL_VALT 0x800 #define VF610_ADC_REFSEL_VBG 0x1000 #define VF610_ADC_ADTRG_HARD 0x2000 #define VF610_ADC_AVGS_8 0x4000 diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 16ade0a0327b..0e4b379ada45 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -111,8 +111,6 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state) s32 poll_value = 0; if (state) { - if (!atomic_read(&st->user_requested_state)) - return 0; if (sensor_hub_device_open(st->hsdev)) return -EIO; @@ -161,6 +159,9 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state) &report_val); } + pr_debug("HID_SENSOR %s set power_state %d report_state %d\n", + st->pdev->name, state_val, report_val); + sensor_hub_get_feature(st->hsdev, st->power_state.report_id, st->power_state.index, sizeof(state_val), &state_val); @@ -182,6 +183,7 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state) ret = pm_runtime_get_sync(&st->pdev->dev); else { pm_runtime_mark_last_busy(&st->pdev->dev); + pm_runtime_use_autosuspend(&st->pdev->dev); ret = pm_runtime_put_autosuspend(&st->pdev->dev); } if (ret < 0) { @@ -285,8 +287,6 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, /* Default to 3 seconds, but can be changed from sysfs */ pm_runtime_set_autosuspend_delay(&attrb->pdev->dev, 3000); - pm_runtime_use_autosuspend(&attrb->pdev->dev); - return ret; error_unreg_trigger: iio_trigger_unregister(trig); diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 79c8c7cd70d5..6e6a1ecc99dd 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -550,6 +550,31 @@ out: } EXPORT_SYMBOL(st_sensors_read_info_raw); +static int st_sensors_init_interface_mode(struct iio_dev *indio_dev, + const struct st_sensor_settings *sensor_settings) +{ + struct st_sensor_data *sdata = iio_priv(indio_dev); + struct device_node *np = sdata->dev->of_node; + struct st_sensors_platform_data *pdata; + + pdata = (struct st_sensors_platform_data *)sdata->dev->platform_data; + if (((np && of_property_read_bool(np, "spi-3wire")) || + (pdata && pdata->spi_3wire)) && sensor_settings->sim.addr) { + int err; + + err = sdata->tf->write_byte(&sdata->tb, sdata->dev, + sensor_settings->sim.addr, + sensor_settings->sim.value); + if (err < 0) { + dev_err(&indio_dev->dev, + "failed to init interface mode\n"); + return err; + } + } + + return 0; +} + int st_sensors_check_device_support(struct iio_dev *indio_dev, int num_sensors_list, const struct st_sensor_settings *sensor_settings) @@ -574,6 +599,10 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev, return -ENODEV; } + err = st_sensors_init_interface_mode(indio_dev, &sensor_settings[i]); + if (err < 0) + return err; + if (sensor_settings[i].wai_addr) { err = sdata->tf->read_byte(&sdata->tb, sdata->dev, sensor_settings[i].wai_addr, &wai); diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index 8cf84d3488b2..12898424d838 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -696,7 +696,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .gyro_max_val = IIO_RAD_TO_DEGREE(22500), .gyro_max_scale = 450, .accel_max_val = IIO_M_S_2_TO_G(12500), - .accel_max_scale = 5, + .accel_max_scale = 10, }, [ADIS16485] = { .channels = adis16485_channels, diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index e7d4ea75e007..7599693f7fe9 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -626,7 +626,7 @@ static irqreturn_t tsl2563_event_handler(int irq, void *private) struct tsl2563_chip *chip = iio_priv(dev_info); iio_push_event(dev_info, - IIO_UNMOD_EVENT_CODE(IIO_LIGHT, + IIO_UNMOD_EVENT_CODE(IIO_INTENSITY, 0, IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 8e1b0861fbe4..c38563699984 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -356,9 +356,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .drdy_irq = { .addr = 0x62, .mask_int1 = 0x01, - .addr_ihl = 0x63, - .mask_ihl = 0x04, - .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, + .addr_stat_drdy = 0x67, }, .multi_read_bit = false, .bootime = 2, diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index d82b788374b6..0d2ea3ee371b 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -282,6 +282,11 @@ static int bmp280_read_temp(struct bmp280_data *data, } adc_temp = be32_to_cpu(tmp) >> 12; + if (adc_temp == BMP280_TEMP_SKIPPED) { + /* reading was skipped */ + dev_err(data->dev, "reading temperature skipped\n"); + return -EIO; + } comp_temp = bmp280_compensate_temp(data, adc_temp); /* @@ -317,6 +322,11 @@ static int bmp280_read_press(struct bmp280_data *data, } adc_press = be32_to_cpu(tmp) >> 12; + if (adc_press == BMP280_PRESS_SKIPPED) { + /* reading was skipped */ + dev_err(data->dev, "reading pressure skipped\n"); + return -EIO; + } comp_press = bmp280_compensate_press(data, adc_press); *val = comp_press; @@ -345,6 +355,11 @@ static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2) } adc_humidity = be16_to_cpu(tmp); + if (adc_humidity == BMP280_HUMIDITY_SKIPPED) { + /* reading was skipped */ + dev_err(data->dev, "reading humidity skipped\n"); + return -EIO; + } comp_humidity = bmp280_compensate_humidity(data, adc_humidity); *val = comp_humidity; @@ -597,14 +612,20 @@ static const struct bmp280_chip_info bmp280_chip_info = { static int bme280_chip_config(struct bmp280_data *data) { - int ret = bmp280_chip_config(data); + int ret; u8 osrs = BMP280_OSRS_HUMIDITIY_X(data->oversampling_humid + 1); + /* + * Oversampling of humidity must be set before oversampling of + * temperature/pressure is set to become effective. + */ + ret = regmap_update_bits(data->regmap, BMP280_REG_CTRL_HUMIDITY, + BMP280_OSRS_HUMIDITY_MASK, osrs); + if (ret < 0) return ret; - return regmap_update_bits(data->regmap, BMP280_REG_CTRL_HUMIDITY, - BMP280_OSRS_HUMIDITY_MASK, osrs); + return bmp280_chip_config(data); } static const struct bmp280_chip_info bme280_chip_info = { diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h index 2c770e13be0e..61347438b779 100644 --- a/drivers/iio/pressure/bmp280.h +++ b/drivers/iio/pressure/bmp280.h @@ -96,6 +96,11 @@ #define BME280_CHIP_ID 0x60 #define BMP280_SOFT_RESET_VAL 0xB6 +/* BMP280 register skipped special values */ +#define BMP280_TEMP_SKIPPED 0x80000 +#define BMP280_PRESS_SKIPPED 0x80000 +#define BMP280_HUMIDITY_SKIPPED 0x8000 + /* Regmap configurations */ extern const struct regmap_config bmp180_regmap_config; extern const struct regmap_config bmp280_regmap_config; diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index aa61ec15c139..f1bce05ffa13 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -456,7 +456,7 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .mask_od = 0x40, .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR, }, - .multi_read_bit = true, + .multi_read_bit = false, .bootime = 2, }, }; diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index d22bc56dd9fc..25ad6abfee22 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -366,34 +366,32 @@ static int stm32_counter_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 dat; switch (mask) { case IIO_CHAN_INFO_RAW: - { - u32 cnt; - - regmap_read(priv->regmap, TIM_CNT, &cnt); - *val = cnt; + regmap_read(priv->regmap, TIM_CNT, &dat); + *val = dat; + return IIO_VAL_INT; + case IIO_CHAN_INFO_ENABLE: + regmap_read(priv->regmap, TIM_CR1, &dat); + *val = (dat & TIM_CR1_CEN) ? 1 : 0; return IIO_VAL_INT; - } - case IIO_CHAN_INFO_SCALE: - { - u32 smcr; - regmap_read(priv->regmap, TIM_SMCR, &smcr); - smcr &= TIM_SMCR_SMS; + case IIO_CHAN_INFO_SCALE: + regmap_read(priv->regmap, TIM_SMCR, &dat); + dat &= TIM_SMCR_SMS; *val = 1; *val2 = 0; /* in quadrature case scale = 0.25 */ - if (smcr == 3) + if (dat == 3) *val2 = 2; return IIO_VAL_FRACTIONAL_LOG2; } - } return -EINVAL; } @@ -403,15 +401,31 @@ static int stm32_counter_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 dat; switch (mask) { case IIO_CHAN_INFO_RAW: - regmap_write(priv->regmap, TIM_CNT, val); + return regmap_write(priv->regmap, TIM_CNT, val); - return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: /* fixed scale */ return -EINVAL; + + case IIO_CHAN_INFO_ENABLE: + if (val) { + regmap_read(priv->regmap, TIM_CR1, &dat); + if (!(dat & TIM_CR1_CEN)) + clk_enable(priv->clk); + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, + TIM_CR1_CEN); + } else { + regmap_read(priv->regmap, TIM_CR1, &dat); + regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_CEN, + 0); + if (dat & TIM_CR1_CEN) + clk_disable(priv->clk); + } + return 0; } return -EINVAL; @@ -471,7 +485,7 @@ static int stm32_get_trigger_mode(struct iio_dev *indio_dev, regmap_read(priv->regmap, TIM_SMCR, &smcr); - return smcr == TIM_SMCR_SMS ? 0 : -EINVAL; + return (smcr & TIM_SMCR_SMS) == TIM_SMCR_SMS ? 0 : -EINVAL; } static const struct iio_enum stm32_trigger_mode_enum = { @@ -507,9 +521,19 @@ static int stm32_set_enable_mode(struct iio_dev *indio_dev, { struct stm32_timer_trigger *priv = iio_priv(indio_dev); int sms = stm32_enable_mode2sms(mode); + u32 val; if (sms < 0) return sms; + /* + * Triggered mode sets CEN bit automatically by hardware. So, first + * enable counter clock, so it can use it. Keeps it in sync with CEN. + */ + if (sms == 6) { + regmap_read(priv->regmap, TIM_CR1, &val); + if (!(val & TIM_CR1_CEN)) + clk_enable(priv->clk); + } regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms); @@ -571,11 +595,14 @@ static int stm32_get_quadrature_mode(struct iio_dev *indio_dev, { struct stm32_timer_trigger *priv = iio_priv(indio_dev); u32 smcr; + int mode; regmap_read(priv->regmap, TIM_SMCR, &smcr); - smcr &= TIM_SMCR_SMS; + mode = (smcr & TIM_SMCR_SMS) - 1; + if ((mode < 0) || (mode > ARRAY_SIZE(stm32_quadrature_modes))) + return -EINVAL; - return smcr - 1; + return mode; } static const struct iio_enum stm32_quadrature_mode_enum = { @@ -592,13 +619,20 @@ static const char *const stm32_count_direction_states[] = { static int stm32_set_count_direction(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, - unsigned int mode) + unsigned int dir) { struct stm32_timer_trigger *priv = iio_priv(indio_dev); + u32 val; + int mode; - regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_DIR, mode); + /* In encoder mode, direction is RO (given by TI1/TI2 signals) */ + regmap_read(priv->regmap, TIM_SMCR, &val); + mode = (val & TIM_SMCR_SMS) - 1; + if ((mode >= 0) || (mode < ARRAY_SIZE(stm32_quadrature_modes))) + return -EBUSY; - return 0; + return regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_DIR, + dir ? TIM_CR1_DIR : 0); } static int stm32_get_count_direction(struct iio_dev *indio_dev, @@ -609,7 +643,7 @@ static int stm32_get_count_direction(struct iio_dev *indio_dev, regmap_read(priv->regmap, TIM_CR1, &cr1); - return (cr1 & TIM_CR1_DIR); + return ((cr1 & TIM_CR1_DIR) ? 1 : 0); } static const struct iio_enum stm32_count_direction_enum = { @@ -672,7 +706,9 @@ static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = { static const struct iio_chan_spec stm32_trigger_channel = { .type = IIO_COUNT, .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_SCALE), .ext_info = stm32_trigger_count_info, .indexed = 1 }; diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index a6cb379a4ebc..437522ca97b4 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -61,6 +61,7 @@ struct addr_req { void (*callback)(int status, struct sockaddr *src_addr, struct rdma_dev_addr *addr, void *context); unsigned long timeout; + struct delayed_work work; int status; u32 seq; }; @@ -268,6 +269,7 @@ int rdma_translate_ip(const struct sockaddr *addr, return ret; ret = rdma_copy_addr(dev_addr, dev, NULL); + dev_addr->bound_dev_if = dev->ifindex; if (vlan_id) *vlan_id = rdma_vlan_dev_vlan_id(dev); dev_put(dev); @@ -280,6 +282,7 @@ int rdma_translate_ip(const struct sockaddr *addr, &((const struct sockaddr_in6 *)addr)->sin6_addr, dev, 1)) { ret = rdma_copy_addr(dev_addr, dev, NULL); + dev_addr->bound_dev_if = dev->ifindex; if (vlan_id) *vlan_id = rdma_vlan_dev_vlan_id(dev); break; @@ -293,7 +296,7 @@ int rdma_translate_ip(const struct sockaddr *addr, } EXPORT_SYMBOL(rdma_translate_ip); -static void set_timeout(unsigned long time) +static void set_timeout(struct delayed_work *delayed_work, unsigned long time) { unsigned long delay; @@ -301,7 +304,7 @@ static void set_timeout(unsigned long time) if ((long)delay < 0) delay = 0; - mod_delayed_work(addr_wq, &work, delay); + mod_delayed_work(addr_wq, delayed_work, delay); } static void queue_req(struct addr_req *req) @@ -316,8 +319,7 @@ static void queue_req(struct addr_req *req) list_add(&req->list, &temp_req->list); - if (req_list.next == &req->list) - set_timeout(req->timeout); + set_timeout(&req->work, req->timeout); mutex_unlock(&lock); } @@ -405,10 +407,10 @@ static int addr4_resolve(struct sockaddr_in *src_in, fl4.saddr = src_ip; fl4.flowi4_oif = addr->bound_dev_if; rt = ip_route_output_key(addr->net, &fl4); - if (IS_ERR(rt)) { - ret = PTR_ERR(rt); - goto out; - } + ret = PTR_ERR_OR_ZERO(rt); + if (ret) + return ret; + src_in->sin_family = AF_INET; src_in->sin_addr.s_addr = fl4.saddr; @@ -423,8 +425,6 @@ static int addr4_resolve(struct sockaddr_in *src_in, *prt = rt; return 0; -out: - return ret; } #if IS_ENABLED(CONFIG_IPV6) @@ -509,6 +509,11 @@ static int addr_resolve(struct sockaddr *src_in, struct dst_entry *dst; int ret; + if (!addr->net) { + pr_warn_ratelimited("%s: missing namespace\n", __func__); + return -EINVAL; + } + if (src_in->sa_family == AF_INET) { struct rtable *rt = NULL; const struct sockaddr_in *dst_in4 = @@ -522,8 +527,12 @@ static int addr_resolve(struct sockaddr *src_in, if (resolve_neigh) ret = addr_resolve_neigh(&rt->dst, dst_in, addr, seq); - ndev = rt->dst.dev; - dev_hold(ndev); + if (addr->bound_dev_if) { + ndev = dev_get_by_index(addr->net, addr->bound_dev_if); + } else { + ndev = rt->dst.dev; + dev_hold(ndev); + } ip_rt_put(rt); } else { @@ -539,19 +548,63 @@ static int addr_resolve(struct sockaddr *src_in, if (resolve_neigh) ret = addr_resolve_neigh(dst, dst_in, addr, seq); - ndev = dst->dev; - dev_hold(ndev); + if (addr->bound_dev_if) { + ndev = dev_get_by_index(addr->net, addr->bound_dev_if); + } else { + ndev = dst->dev; + dev_hold(ndev); + } dst_release(dst); } - addr->bound_dev_if = ndev->ifindex; - addr->net = dev_net(ndev); + if (ndev->flags & IFF_LOOPBACK) { + ret = rdma_translate_ip(dst_in, addr, NULL); + /* + * Put the loopback device and get the translated + * device instead. + */ + dev_put(ndev); + ndev = dev_get_by_index(addr->net, addr->bound_dev_if); + } else { + addr->bound_dev_if = ndev->ifindex; + } dev_put(ndev); return ret; } +static void process_one_req(struct work_struct *_work) +{ + struct addr_req *req; + struct sockaddr *src_in, *dst_in; + + mutex_lock(&lock); + req = container_of(_work, struct addr_req, work.work); + + if (req->status == -ENODATA) { + src_in = (struct sockaddr *)&req->src_addr; + dst_in = (struct sockaddr *)&req->dst_addr; + req->status = addr_resolve(src_in, dst_in, req->addr, + true, req->seq); + if (req->status && time_after_eq(jiffies, req->timeout)) { + req->status = -ETIMEDOUT; + } else if (req->status == -ENODATA) { + /* requeue the work for retrying again */ + set_timeout(&req->work, req->timeout); + mutex_unlock(&lock); + return; + } + } + list_del(&req->list); + mutex_unlock(&lock); + + req->callback(req->status, (struct sockaddr *)&req->src_addr, + req->addr, req->context); + put_client(req->client); + kfree(req); +} + static void process_req(struct work_struct *work) { struct addr_req *req, *temp_req; @@ -569,20 +622,23 @@ static void process_req(struct work_struct *work) true, req->seq); if (req->status && time_after_eq(jiffies, req->timeout)) req->status = -ETIMEDOUT; - else if (req->status == -ENODATA) + else if (req->status == -ENODATA) { + set_timeout(&req->work, req->timeout); continue; + } } list_move_tail(&req->list, &done_list); } - if (!list_empty(&req_list)) { - req = list_entry(req_list.next, struct addr_req, list); - set_timeout(req->timeout); - } mutex_unlock(&lock); list_for_each_entry_safe(req, temp_req, &done_list, list) { list_del(&req->list); + /* It is safe to cancel other work items from this work item + * because at a time there can be only one work item running + * with this single threaded work queue. + */ + cancel_delayed_work(&req->work); req->callback(req->status, (struct sockaddr *) &req->src_addr, req->addr, req->context); put_client(req->client); @@ -625,6 +681,7 @@ int rdma_resolve_ip(struct rdma_addr_client *client, req->context = context; req->client = client; atomic_inc(&client->refcount); + INIT_DELAYED_WORK(&req->work, process_one_req); req->seq = (u32)atomic_inc_return(&ib_nl_addr_request_seq); req->status = addr_resolve(src_in, dst_in, addr, true, req->seq); @@ -679,7 +736,7 @@ void rdma_addr_cancel(struct rdma_dev_addr *addr) req->status = -ECANCELED; req->timeout = jiffies; list_move(&req->list, &req_list); - set_timeout(req->timeout); + set_timeout(&req->work, req->timeout); break; } } @@ -785,9 +842,8 @@ static int netevent_callback(struct notifier_block *self, unsigned long event, if (event == NETEVENT_NEIGH_UPDATE) { struct neighbour *neigh = ctx; - if (neigh->nud_state & NUD_VALID) { - set_timeout(jiffies); - } + if (neigh->nud_state & NUD_VALID) + set_timeout(&work, jiffies); } return 0; } @@ -798,7 +854,7 @@ static struct notifier_block nb = { int addr_init(void) { - addr_wq = alloc_workqueue("ib_addr", WQ_MEM_RECLAIM, 0); + addr_wq = alloc_ordered_workqueue("ib_addr", WQ_MEM_RECLAIM); if (!addr_wq) return -ENOMEM; diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 31bb82d8ecd7..0eb393237ba2 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -623,22 +623,11 @@ static inline int cma_validate_port(struct ib_device *device, u8 port, if ((dev_type != ARPHRD_INFINIBAND) && rdma_protocol_ib(device, port)) return ret; - if (dev_type == ARPHRD_ETHER && rdma_protocol_roce(device, port)) { + if (dev_type == ARPHRD_ETHER && rdma_protocol_roce(device, port)) ndev = dev_get_by_index(&init_net, bound_if_index); - if (ndev && ndev->flags & IFF_LOOPBACK) { - pr_info("detected loopback device\n"); - dev_put(ndev); - - if (!device->get_netdev) - return -EOPNOTSUPP; - - ndev = device->get_netdev(device, port); - if (!ndev) - return -ENODEV; - } - } else { + else gid_type = IB_GID_TYPE_IB; - } + ret = ib_find_cached_gid_by_port(device, gid, gid_type, port, ndev, NULL); @@ -1044,6 +1033,8 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr, } else ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr, qp_attr_mask); + qp_attr->port_num = id_priv->id.port_num; + *qp_attr_mask |= IB_QP_PORT; } else ret = -ENOSYS; @@ -2569,21 +2560,6 @@ static int cma_resolve_iboe_route(struct rdma_id_private *id_priv) goto err2; } - if (ndev->flags & IFF_LOOPBACK) { - dev_put(ndev); - if (!id_priv->id.device->get_netdev) { - ret = -EOPNOTSUPP; - goto err2; - } - - ndev = id_priv->id.device->get_netdev(id_priv->id.device, - id_priv->id.port_num); - if (!ndev) { - ret = -ENODEV; - goto err2; - } - } - supported_gids = roce_gid_type_mask_support(id_priv->id.device, id_priv->id.port_num); gid_type = cma_route_gid_type(addr->dev_addr.network, diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c index a5dfab6adf49..221468f77128 100644 --- a/drivers/infiniband/core/device.c +++ b/drivers/infiniband/core/device.c @@ -537,10 +537,11 @@ void ib_unregister_device(struct ib_device *device) } up_read(&lists_rwsem); - mutex_unlock(&device_mutex); - ib_device_unregister_rdmacg(device); ib_device_unregister_sysfs(device); + + mutex_unlock(&device_mutex); + ib_cache_cleanup_one(device); ib_security_destroy_port_pkey_list(device); diff --git a/drivers/infiniband/core/roce_gid_mgmt.c b/drivers/infiniband/core/roce_gid_mgmt.c index db958d3207ef..94a9eefb3cfc 100644 --- a/drivers/infiniband/core/roce_gid_mgmt.c +++ b/drivers/infiniband/core/roce_gid_mgmt.c @@ -42,6 +42,8 @@ #include <rdma/ib_cache.h> #include <rdma/ib_addr.h> +static struct workqueue_struct *gid_cache_wq; + enum gid_op_type { GID_DEL = 0, GID_ADD @@ -560,7 +562,7 @@ static int netdevice_queue_work(struct netdev_event_work_cmd *cmds, } INIT_WORK(&ndev_work->work, netdevice_event_work_handler); - queue_work(ib_wq, &ndev_work->work); + queue_work(gid_cache_wq, &ndev_work->work); return NOTIFY_DONE; } @@ -693,7 +695,7 @@ static int addr_event(struct notifier_block *this, unsigned long event, dev_hold(ndev); work->gid_attr.ndev = ndev; - queue_work(ib_wq, &work->work); + queue_work(gid_cache_wq, &work->work); return NOTIFY_DONE; } @@ -740,6 +742,10 @@ static struct notifier_block nb_inet6addr = { int __init roce_gid_mgmt_init(void) { + gid_cache_wq = alloc_ordered_workqueue("gid-cache-wq", 0); + if (!gid_cache_wq) + return -ENOMEM; + register_inetaddr_notifier(&nb_inetaddr); if (IS_ENABLED(CONFIG_IPV6)) register_inet6addr_notifier(&nb_inet6addr); @@ -764,4 +770,5 @@ void __exit roce_gid_mgmt_cleanup(void) * ib-core is removed, all physical devices have been removed, * so no issue with remaining hardware contexts. */ + destroy_workqueue(gid_cache_wq); } diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 8ba9bfb073d1..739bd69ef1d4 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -1015,7 +1015,7 @@ static struct ib_ucq_object *create_cq(struct ib_uverbs_file *file, cq->uobject = &obj->uobject; cq->comp_handler = ib_uverbs_comp_handler; cq->event_handler = ib_uverbs_cq_event_handler; - cq->cq_context = &ev_file->ev_queue; + cq->cq_context = ev_file ? &ev_file->ev_queue : NULL; atomic_set(&cq->usecnt, 0); obj->uobject.object = cq; @@ -1153,7 +1153,7 @@ ssize_t ib_uverbs_resize_cq(struct ib_uverbs_file *file, int out_len) { struct ib_uverbs_resize_cq cmd; - struct ib_uverbs_resize_cq_resp resp; + struct ib_uverbs_resize_cq_resp resp = {}; struct ib_udata udata; struct ib_cq *cq; int ret = -EINVAL; @@ -1296,7 +1296,6 @@ ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file, struct ib_uobject *uobj; struct ib_cq *cq; struct ib_ucq_object *obj; - struct ib_uverbs_event_queue *ev_queue; int ret = -EINVAL; if (copy_from_user(&cmd, buf, sizeof cmd)) @@ -1313,7 +1312,6 @@ ssize_t ib_uverbs_destroy_cq(struct ib_uverbs_file *file, */ uverbs_uobject_get(uobj); cq = uobj->object; - ev_queue = cq->cq_context; obj = container_of(cq->uobject, struct ib_ucq_object, uobject); memset(&resp, 0, sizeof(resp)); @@ -1524,6 +1522,7 @@ static int create_qp(struct ib_uverbs_file *file, qp->qp_type = attr.qp_type; atomic_set(&qp->usecnt, 0); atomic_inc(&pd->usecnt); + qp->port = 0; if (attr.send_cq) atomic_inc(&attr.send_cq->usecnt); if (attr.recv_cq) @@ -1935,7 +1934,8 @@ static int modify_qp(struct ib_uverbs_file *file, goto out; } - if (!rdma_is_port_valid(qp->device, cmd->base.port_num)) { + if ((cmd->base.attr_mask & IB_QP_PORT) && + !rdma_is_port_valid(qp->device, cmd->base.port_num)) { ret = -EINVAL; goto release_qp; } @@ -1963,8 +1963,9 @@ static int modify_qp(struct ib_uverbs_file *file, attr->alt_timeout = cmd->base.alt_timeout; attr->rate_limit = cmd->rate_limit; - attr->ah_attr.type = rdma_ah_find_type(qp->device, - cmd->base.dest.port_num); + if (cmd->base.attr_mask & IB_QP_AV) + attr->ah_attr.type = rdma_ah_find_type(qp->device, + cmd->base.dest.port_num); if (cmd->base.dest.is_global) { rdma_ah_set_grh(&attr->ah_attr, NULL, cmd->base.dest.flow_label, @@ -1982,8 +1983,9 @@ static int modify_qp(struct ib_uverbs_file *file, rdma_ah_set_port_num(&attr->ah_attr, cmd->base.dest.port_num); - attr->alt_ah_attr.type = rdma_ah_find_type(qp->device, - cmd->base.dest.port_num); + if (cmd->base.attr_mask & IB_QP_ALT_PATH) + attr->alt_ah_attr.type = + rdma_ah_find_type(qp->device, cmd->base.dest.port_num); if (cmd->base.alt_dest.is_global) { rdma_ah_set_grh(&attr->alt_ah_attr, NULL, cmd->base.alt_dest.flow_label, @@ -2005,28 +2007,13 @@ static int modify_qp(struct ib_uverbs_file *file, rdma_ah_set_port_num(&attr->alt_ah_attr, cmd->base.alt_dest.port_num); - if (qp->real_qp == qp) { - if (cmd->base.attr_mask & IB_QP_AV) { - ret = ib_resolve_eth_dmac(qp->device, &attr->ah_attr); - if (ret) - goto release_qp; - } - ret = ib_security_modify_qp(qp, - attr, - modify_qp_mask(qp->qp_type, - cmd->base.attr_mask), - udata); - } else { - ret = ib_security_modify_qp(qp, - attr, - modify_qp_mask(qp->qp_type, - cmd->base.attr_mask), - NULL); - } + ret = ib_modify_qp_with_udata(qp, attr, + modify_qp_mask(qp->qp_type, + cmd->base.attr_mask), + udata); release_qp: uobj_put_obj_read(qp); - out: kfree(attr); @@ -2103,7 +2090,6 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file, struct ib_uverbs_destroy_qp cmd; struct ib_uverbs_destroy_qp_resp resp; struct ib_uobject *uobj; - struct ib_qp *qp; struct ib_uqp_object *obj; int ret = -EINVAL; @@ -2117,7 +2103,6 @@ ssize_t ib_uverbs_destroy_qp(struct ib_uverbs_file *file, if (IS_ERR(uobj)) return PTR_ERR(uobj); - qp = uobj->object; obj = container_of(uobj, struct ib_uqp_object, uevent.uobject); /* * Make sure we don't free the memory in remove_commit as we still @@ -3019,7 +3004,6 @@ int ib_uverbs_ex_destroy_wq(struct ib_uverbs_file *file, { struct ib_uverbs_ex_destroy_wq cmd = {}; struct ib_uverbs_ex_destroy_wq_resp resp = {}; - struct ib_wq *wq; struct ib_uobject *uobj; struct ib_uwq_object *obj; size_t required_cmd_sz; @@ -3053,7 +3037,6 @@ int ib_uverbs_ex_destroy_wq(struct ib_uverbs_file *file, if (IS_ERR(uobj)) return PTR_ERR(uobj); - wq = uobj->object; obj = container_of(uobj, struct ib_uwq_object, uevent.uobject); /* * Make sure we don't free the memory in remove_commit as we still @@ -3743,10 +3726,8 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file, struct ib_uverbs_destroy_srq cmd; struct ib_uverbs_destroy_srq_resp resp; struct ib_uobject *uobj; - struct ib_srq *srq; struct ib_uevent_object *obj; int ret = -EINVAL; - enum ib_srq_type srq_type; if (copy_from_user(&cmd, buf, sizeof cmd)) return -EFAULT; @@ -3756,9 +3737,7 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file, if (IS_ERR(uobj)) return PTR_ERR(uobj); - srq = uobj->object; obj = container_of(uobj, struct ib_uevent_object, uobject); - srq_type = srq->srq_type; /* * Make sure we don't free the memory in remove_commit as we still * needs the uobject memory to create the response. diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index 3d2609608f58..5e530d2bee44 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -250,6 +250,7 @@ void ib_uverbs_release_file(struct kref *ref) if (atomic_dec_and_test(&file->device->refcount)) ib_uverbs_comp_dev(file->device); + kobject_put(&file->device->kobj); kfree(file); } @@ -917,7 +918,6 @@ err: static int ib_uverbs_close(struct inode *inode, struct file *filp) { struct ib_uverbs_file *file = filp->private_data; - struct ib_uverbs_device *dev = file->device; mutex_lock(&file->cleanup_mutex); if (file->ucontext) { @@ -939,7 +939,6 @@ static int ib_uverbs_close(struct inode *inode, struct file *filp) ib_uverbs_release_async_event_file); kref_put(&file->ref, ib_uverbs_release_file); - kobject_put(&dev->kobj); return 0; } @@ -1154,7 +1153,6 @@ static void ib_uverbs_free_hw_resources(struct ib_uverbs_device *uverbs_dev, kref_get(&file->ref); mutex_unlock(&uverbs_dev->lists_mutex); - ib_uverbs_event_handler(&file->event_handler, &event); mutex_lock(&file->cleanup_mutex); ucontext = file->ucontext; @@ -1171,6 +1169,7 @@ static void ib_uverbs_free_hw_resources(struct ib_uverbs_device *uverbs_dev, * for example due to freeing the resources * (e.g mmput). */ + ib_uverbs_event_handler(&file->event_handler, &event); ib_dev->disassociate_ucontext(ucontext); mutex_lock(&file->cleanup_mutex); ib_uverbs_cleanup_ucontext(file, ucontext, true); diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index c973a83c898b..b456e3ca1876 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -452,6 +452,19 @@ int ib_get_gids_from_rdma_hdr(const union rdma_network_hdr *hdr, } EXPORT_SYMBOL(ib_get_gids_from_rdma_hdr); +/* + * This function creates ah from the incoming packet. + * Incoming packet has dgid of the receiver node on which this code is + * getting executed and, sgid contains the GID of the sender. + * + * When resolving mac address of destination, the arrived dgid is used + * as sgid and, sgid is used as dgid because sgid contains destinations + * GID whom to respond to. + * + * This is why when calling rdma_addr_find_l2_eth_by_grh() function, the + * position of arguments dgid and sgid do not match the order of the + * parameters. + */ int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, const struct ib_wc *wc, const struct ib_grh *grh, struct rdma_ah_attr *ah_attr) @@ -507,11 +520,6 @@ int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, } resolved_dev = dev_get_by_index(&init_net, if_index); - if (resolved_dev->flags & IFF_LOOPBACK) { - dev_put(resolved_dev); - resolved_dev = idev; - dev_hold(resolved_dev); - } rcu_read_lock(); if (resolved_dev != idev && !rdma_is_upper_dev_rcu(idev, resolved_dev)) @@ -830,6 +838,7 @@ struct ib_qp *ib_create_qp(struct ib_pd *pd, spin_lock_init(&qp->mr_lock); INIT_LIST_HEAD(&qp->rdma_mrs); INIT_LIST_HEAD(&qp->sig_mrs); + qp->port = 0; if (qp_init_attr->qp_type == IB_QPT_XRC_TGT) return ib_create_xrc_qp(qp, qp_init_attr); @@ -1268,20 +1277,40 @@ out: } EXPORT_SYMBOL(ib_resolve_eth_dmac); -int ib_modify_qp(struct ib_qp *qp, - struct ib_qp_attr *qp_attr, - int qp_attr_mask) +/** + * ib_modify_qp_with_udata - Modifies the attributes for the specified QP. + * @qp: The QP to modify. + * @attr: On input, specifies the QP attributes to modify. On output, + * the current values of selected QP attributes are returned. + * @attr_mask: A bit-mask used to specify which attributes of the QP + * are being modified. + * @udata: pointer to user's input output buffer information + * are being modified. + * It returns 0 on success and returns appropriate error code on error. + */ +int ib_modify_qp_with_udata(struct ib_qp *qp, struct ib_qp_attr *attr, + int attr_mask, struct ib_udata *udata) { + int ret; - if (qp_attr_mask & IB_QP_AV) { - int ret; - - ret = ib_resolve_eth_dmac(qp->device, &qp_attr->ah_attr); + if (attr_mask & IB_QP_AV) { + ret = ib_resolve_eth_dmac(qp->device, &attr->ah_attr); if (ret) return ret; } + ret = ib_security_modify_qp(qp, attr, attr_mask, udata); + if (!ret && (attr_mask & IB_QP_PORT)) + qp->port = attr->port_num; + + return ret; +} +EXPORT_SYMBOL(ib_modify_qp_with_udata); - return ib_security_modify_qp(qp->real_qp, qp_attr, qp_attr_mask, NULL); +int ib_modify_qp(struct ib_qp *qp, + struct ib_qp_attr *qp_attr, + int qp_attr_mask) +{ + return ib_modify_qp_with_udata(qp, qp_attr, qp_attr_mask, NULL); } EXPORT_SYMBOL(ib_modify_qp); diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h index 08772836fded..85527532c49d 100644 --- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h +++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h @@ -51,6 +51,8 @@ #define BNXT_RE_PAGE_SIZE_8M BIT(23) #define BNXT_RE_PAGE_SIZE_1G BIT(30) +#define BNXT_RE_MAX_MR_SIZE BIT(30) + #define BNXT_RE_MAX_QPC_COUNT (64 * 1024) #define BNXT_RE_MAX_MRW_COUNT (64 * 1024) #define BNXT_RE_MAX_SRQC_COUNT (64 * 1024) @@ -60,6 +62,13 @@ #define BNXT_RE_RQ_WQE_THRESHOLD 32 +/* + * Setting the default ack delay value to 16, which means + * the default timeout is approx. 260ms(4 usec * 2 ^(timeout)) + */ + +#define BNXT_RE_DEFAULT_ACK_DELAY 16 + struct bnxt_re_work { struct work_struct work; unsigned long event; diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.c b/drivers/infiniband/hw/bnxt_re/ib_verbs.c index c7bd68311d0c..f0e01b3ac711 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.c +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.c @@ -145,10 +145,8 @@ int bnxt_re_query_device(struct ib_device *ibdev, ib_attr->fw_ver = (u64)(unsigned long)(dev_attr->fw_ver); bnxt_qplib_get_guid(rdev->netdev->dev_addr, (u8 *)&ib_attr->sys_image_guid); - ib_attr->max_mr_size = ~0ull; - ib_attr->page_size_cap = BNXT_RE_PAGE_SIZE_4K | BNXT_RE_PAGE_SIZE_8K | - BNXT_RE_PAGE_SIZE_64K | BNXT_RE_PAGE_SIZE_2M | - BNXT_RE_PAGE_SIZE_8M | BNXT_RE_PAGE_SIZE_1G; + ib_attr->max_mr_size = BNXT_RE_MAX_MR_SIZE; + ib_attr->page_size_cap = BNXT_RE_PAGE_SIZE_4K; ib_attr->vendor_id = rdev->en_dev->pdev->vendor; ib_attr->vendor_part_id = rdev->en_dev->pdev->device; @@ -174,9 +172,11 @@ int bnxt_re_query_device(struct ib_device *ibdev, ib_attr->max_mr = dev_attr->max_mr; ib_attr->max_pd = dev_attr->max_pd; ib_attr->max_qp_rd_atom = dev_attr->max_qp_rd_atom; - ib_attr->max_qp_init_rd_atom = dev_attr->max_qp_rd_atom; - ib_attr->atomic_cap = IB_ATOMIC_HCA; - ib_attr->masked_atomic_cap = IB_ATOMIC_HCA; + ib_attr->max_qp_init_rd_atom = dev_attr->max_qp_init_rd_atom; + if (dev_attr->is_atomic) { + ib_attr->atomic_cap = IB_ATOMIC_HCA; + ib_attr->masked_atomic_cap = IB_ATOMIC_HCA; + } ib_attr->max_ee_rd_atom = 0; ib_attr->max_res_rd_atom = 0; @@ -201,7 +201,7 @@ int bnxt_re_query_device(struct ib_device *ibdev, ib_attr->max_fast_reg_page_list_len = MAX_PBL_LVL_1_PGS; ib_attr->max_pkeys = 1; - ib_attr->local_ca_ack_delay = 0; + ib_attr->local_ca_ack_delay = BNXT_RE_DEFAULT_ACK_DELAY; return 0; } @@ -390,15 +390,17 @@ int bnxt_re_del_gid(struct ib_device *ibdev, u8 port_num, return -EINVAL; ctx->refcnt--; if (!ctx->refcnt) { - rc = bnxt_qplib_del_sgid - (sgid_tbl, - &sgid_tbl->tbl[ctx->idx], true); - if (rc) + rc = bnxt_qplib_del_sgid(sgid_tbl, + &sgid_tbl->tbl[ctx->idx], + true); + if (rc) { dev_err(rdev_to_dev(rdev), "Failed to remove GID: %#x", rc); - ctx_tbl = sgid_tbl->ctx; - ctx_tbl[ctx->idx] = NULL; - kfree(ctx); + } else { + ctx_tbl = sgid_tbl->ctx; + ctx_tbl[ctx->idx] = NULL; + kfree(ctx); + } } } else { return -EINVAL; @@ -588,10 +590,10 @@ static int bnxt_re_create_fence_mr(struct bnxt_re_pd *pd) /* Create a fence MW only for kernel consumers */ mw = bnxt_re_alloc_mw(&pd->ib_pd, IB_MW_TYPE_1, NULL); - if (!mw) { + if (IS_ERR(mw)) { dev_err(rdev_to_dev(rdev), "Failed to create fence-MW for PD: %p\n", pd); - rc = -EINVAL; + rc = PTR_ERR(mw); goto fail; } fence->mw = mw; @@ -612,30 +614,13 @@ int bnxt_re_dealloc_pd(struct ib_pd *ib_pd) int rc; bnxt_re_destroy_fence_mr(pd); - if (ib_pd->uobject && pd->dpi.dbr) { - struct ib_ucontext *ib_uctx = ib_pd->uobject->context; - struct bnxt_re_ucontext *ucntx; - /* Free DPI only if this is the first PD allocated by the - * application and mark the context dpi as NULL - */ - ucntx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); - - rc = bnxt_qplib_dealloc_dpi(&rdev->qplib_res, - &rdev->qplib_res.dpi_tbl, - &pd->dpi); + if (pd->qplib_pd.id) { + rc = bnxt_qplib_dealloc_pd(&rdev->qplib_res, + &rdev->qplib_res.pd_tbl, + &pd->qplib_pd); if (rc) - dev_err(rdev_to_dev(rdev), "Failed to deallocate HW DPI"); - /* Don't fail, continue*/ - ucntx->dpi = NULL; - } - - rc = bnxt_qplib_dealloc_pd(&rdev->qplib_res, - &rdev->qplib_res.pd_tbl, - &pd->qplib_pd); - if (rc) { - dev_err(rdev_to_dev(rdev), "Failed to deallocate HW PD"); - return rc; + dev_err(rdev_to_dev(rdev), "Failed to deallocate HW PD"); } kfree(pd); @@ -667,23 +652,22 @@ struct ib_pd *bnxt_re_alloc_pd(struct ib_device *ibdev, if (udata) { struct bnxt_re_pd_resp resp; - if (!ucntx->dpi) { + if (!ucntx->dpi.dbr) { /* Allocate DPI in alloc_pd to avoid failing of * ibv_devinfo and family of application when DPIs * are depleted. */ if (bnxt_qplib_alloc_dpi(&rdev->qplib_res.dpi_tbl, - &pd->dpi, ucntx)) { + &ucntx->dpi, ucntx)) { rc = -ENOMEM; goto dbfail; } - ucntx->dpi = &pd->dpi; } resp.pdid = pd->qplib_pd.id; /* Still allow mapping this DBR to the new user PD. */ - resp.dpi = ucntx->dpi->dpi; - resp.dbr = (u64)ucntx->dpi->umdbr; + resp.dpi = ucntx->dpi.dpi; + resp.dbr = (u64)ucntx->dpi.umdbr; rc = ib_copy_to_udata(udata, &resp, sizeof(resp)); if (rc) { @@ -960,7 +944,7 @@ static int bnxt_re_init_user_qp(struct bnxt_re_dev *rdev, struct bnxt_re_pd *pd, qplib_qp->rq.nmap = umem->nmap; } - qplib_qp->dpi = cntx->dpi; + qplib_qp->dpi = &cntx->dpi; return 0; rqfail: ib_umem_release(qp->sumem); @@ -1530,13 +1514,24 @@ int bnxt_re_modify_qp(struct ib_qp *ib_qp, struct ib_qp_attr *qp_attr, if (qp_attr_mask & IB_QP_MAX_QP_RD_ATOMIC) { qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_MAX_RD_ATOMIC; - qp->qplib_qp.max_rd_atomic = qp_attr->max_rd_atomic; + /* Cap the max_rd_atomic to device max */ + qp->qplib_qp.max_rd_atomic = min_t(u32, qp_attr->max_rd_atomic, + dev_attr->max_qp_rd_atom); } if (qp_attr_mask & IB_QP_SQ_PSN) { qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_SQ_PSN; qp->qplib_qp.sq.psn = qp_attr->sq_psn; } if (qp_attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) { + if (qp_attr->max_dest_rd_atomic > + dev_attr->max_qp_init_rd_atom) { + dev_err(rdev_to_dev(rdev), + "max_dest_rd_atomic requested%d is > dev_max%d", + qp_attr->max_dest_rd_atomic, + dev_attr->max_qp_init_rd_atom); + return -EINVAL; + } + qp->qplib_qp.modify_flags |= CMDQ_MODIFY_QP_MODIFY_MASK_MAX_DEST_RD_ATOMIC; qp->qplib_qp.max_dest_rd_atomic = qp_attr->max_dest_rd_atomic; @@ -2403,7 +2398,7 @@ struct ib_cq *bnxt_re_create_cq(struct ib_device *ibdev, } cq->qplib_cq.sghead = cq->umem->sg_head.sgl; cq->qplib_cq.nmap = cq->umem->nmap; - cq->qplib_cq.dpi = uctx->dpi; + cq->qplib_cq.dpi = &uctx->dpi; } else { cq->max_cql = min_t(u32, entries, MAX_CQL_PER_POLL); cq->cql = kcalloc(cq->max_cql, sizeof(struct bnxt_qplib_cqe), @@ -2905,6 +2900,7 @@ int bnxt_re_poll_cq(struct ib_cq *ib_cq, int num_entries, struct ib_wc *wc) spin_lock_irqsave(&cq->cq_lock, flags); budget = min_t(u32, num_entries, cq->max_cql); + num_entries = budget; if (!cq->cql) { dev_err(rdev_to_dev(cq->rdev), "POLL CQ : no CQL to use"); goto exit; @@ -3031,6 +3027,11 @@ int bnxt_re_req_notify_cq(struct ib_cq *ib_cq, else if (ib_cqn_flags & IB_CQ_SOLICITED) type = DBR_DBR_TYPE_CQ_ARMSE; + /* Poll to see if there are missed events */ + if ((ib_cqn_flags & IB_CQ_REPORT_MISSED_EVENTS) && + !(bnxt_qplib_is_cq_empty(&cq->qplib_cq))) + return 1; + bnxt_qplib_req_notify_cq(&cq->qplib_cq, type); return 0; @@ -3245,6 +3246,12 @@ struct ib_mr *bnxt_re_reg_user_mr(struct ib_pd *ib_pd, u64 start, u64 length, struct scatterlist *sg; int entry; + if (length > BNXT_RE_MAX_MR_SIZE) { + dev_err(rdev_to_dev(rdev), "MR Size: %lld > Max supported:%ld\n", + length, BNXT_RE_MAX_MR_SIZE); + return ERR_PTR(-ENOMEM); + } + mr = kzalloc(sizeof(*mr), GFP_KERNEL); if (!mr) return ERR_PTR(-ENOMEM); @@ -3388,8 +3395,26 @@ int bnxt_re_dealloc_ucontext(struct ib_ucontext *ib_uctx) struct bnxt_re_ucontext *uctx = container_of(ib_uctx, struct bnxt_re_ucontext, ib_uctx); + + struct bnxt_re_dev *rdev = uctx->rdev; + int rc = 0; + if (uctx->shpg) free_page((unsigned long)uctx->shpg); + + if (uctx->dpi.dbr) { + /* Free DPI only if this is the first PD allocated by the + * application and mark the context dpi as NULL + */ + rc = bnxt_qplib_dealloc_dpi(&rdev->qplib_res, + &rdev->qplib_res.dpi_tbl, + &uctx->dpi); + if (rc) + dev_err(rdev_to_dev(rdev), "Deallocte HW DPI failed!"); + /* Don't fail, continue*/ + uctx->dpi.dbr = NULL; + } + kfree(uctx); return 0; } diff --git a/drivers/infiniband/hw/bnxt_re/ib_verbs.h b/drivers/infiniband/hw/bnxt_re/ib_verbs.h index 6c160f6a5398..a0bb7e33d7ca 100644 --- a/drivers/infiniband/hw/bnxt_re/ib_verbs.h +++ b/drivers/infiniband/hw/bnxt_re/ib_verbs.h @@ -59,7 +59,6 @@ struct bnxt_re_pd { struct bnxt_re_dev *rdev; struct ib_pd ib_pd; struct bnxt_qplib_pd qplib_pd; - struct bnxt_qplib_dpi dpi; struct bnxt_re_fence_data fence; }; @@ -127,7 +126,7 @@ struct bnxt_re_mw { struct bnxt_re_ucontext { struct bnxt_re_dev *rdev; struct ib_ucontext ib_uctx; - struct bnxt_qplib_dpi *dpi; + struct bnxt_qplib_dpi dpi; void *shpg; spinlock_t sh_lock; /* protect shpg */ }; diff --git a/drivers/infiniband/hw/bnxt_re/main.c b/drivers/infiniband/hw/bnxt_re/main.c index 1fce5e73216b..ceae2d92fb08 100644 --- a/drivers/infiniband/hw/bnxt_re/main.c +++ b/drivers/infiniband/hw/bnxt_re/main.c @@ -333,6 +333,7 @@ static int bnxt_re_net_stats_ctx_alloc(struct bnxt_re_dev *rdev, bnxt_re_init_hwrm_hdr(rdev, (void *)&req, HWRM_STAT_CTX_ALLOC, -1, -1); req.update_period_ms = cpu_to_le32(1000); req.stats_dma_addr = cpu_to_le64(dma_map); + req.stat_ctx_flags = STAT_CTX_ALLOC_REQ_STAT_CTX_FLAGS_ROCE; bnxt_re_fill_fw_msg(&fw_msg, (void *)&req, sizeof(req), (void *)&resp, sizeof(resp), DFLT_HWRM_CMD_TIMEOUT); rc = en_dev->en_ops->bnxt_send_fw_msg(en_dev, BNXT_ROCE_ULP, &fw_msg); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.c b/drivers/infiniband/hw/bnxt_re/qplib_fp.c index f05500bcdcf1..9af1514e5944 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.c @@ -1128,6 +1128,11 @@ int bnxt_qplib_post_send(struct bnxt_qplib_qp *qp, } /* Each SGE entry = 1 WQE size16 */ wqe_size16 = wqe->num_sge; + /* HW requires wqe size has room for atleast one SGE even if + * none was supplied by ULP + */ + if (!wqe->num_sge) + wqe_size16++; } /* Specifics */ @@ -1364,6 +1369,11 @@ int bnxt_qplib_post_recv(struct bnxt_qplib_qp *qp, rqe->flags = wqe->flags; rqe->wqe_size = wqe->num_sge + ((offsetof(typeof(*rqe), data) + 15) >> 4); + /* HW requires wqe size has room for atleast one SGE even if none + * was supplied by ULP + */ + if (!wqe->num_sge) + rqe->wqe_size++; /* Supply the rqe->wr_id index to the wr_id_tbl for now */ rqe->wr_id[0] = cpu_to_le32(sw_prod); @@ -1885,6 +1895,25 @@ flush_rq: return rc; } +bool bnxt_qplib_is_cq_empty(struct bnxt_qplib_cq *cq) +{ + struct cq_base *hw_cqe, **hw_cqe_ptr; + unsigned long flags; + u32 sw_cons, raw_cons; + bool rc = true; + + spin_lock_irqsave(&cq->hwq.lock, flags); + raw_cons = cq->hwq.cons; + sw_cons = HWQ_CMP(raw_cons, &cq->hwq); + hw_cqe_ptr = (struct cq_base **)cq->hwq.pbl_ptr; + hw_cqe = &hw_cqe_ptr[CQE_PG(sw_cons)][CQE_IDX(sw_cons)]; + + /* Check for Valid bit. If the CQE is valid, return false */ + rc = !CQE_CMP_VALID(hw_cqe, raw_cons, cq->hwq.max_elements); + spin_unlock_irqrestore(&cq->hwq.lock, flags); + return rc; +} + static int bnxt_qplib_cq_process_res_raweth_qp1(struct bnxt_qplib_cq *cq, struct cq_res_raweth_qp1 *hwcqe, struct bnxt_qplib_cqe **pcqe, diff --git a/drivers/infiniband/hw/bnxt_re/qplib_fp.h b/drivers/infiniband/hw/bnxt_re/qplib_fp.h index 36b7b7db0e3f..19176e06c98a 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_fp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_fp.h @@ -449,6 +449,7 @@ int bnxt_qplib_create_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq); int bnxt_qplib_destroy_cq(struct bnxt_qplib_res *res, struct bnxt_qplib_cq *cq); int bnxt_qplib_poll_cq(struct bnxt_qplib_cq *cq, struct bnxt_qplib_cqe *cqe, int num, struct bnxt_qplib_qp **qp); +bool bnxt_qplib_is_cq_empty(struct bnxt_qplib_cq *cq); void bnxt_qplib_req_notify_cq(struct bnxt_qplib_cq *cq, u32 arm_type); void bnxt_qplib_free_nq(struct bnxt_qplib_nq *nq); int bnxt_qplib_alloc_nq(struct pci_dev *pdev, struct bnxt_qplib_nq *nq); diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.c b/drivers/infiniband/hw/bnxt_re/qplib_sp.c index fde18cf0e406..ef91ab786dd4 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_sp.c +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.c @@ -51,6 +51,19 @@ const struct bnxt_qplib_gid bnxt_qplib_gid_zero = {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; /* Device */ + +static bool bnxt_qplib_is_atomic_cap(struct bnxt_qplib_rcfw *rcfw) +{ + int rc; + u16 pcie_ctl2; + + rc = pcie_capability_read_word(rcfw->pdev, PCI_EXP_DEVCTL2, + &pcie_ctl2); + if (rc) + return false; + return !!(pcie_ctl2 & PCI_EXP_DEVCTL2_ATOMIC_REQ); +} + int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, struct bnxt_qplib_dev_attr *attr) { @@ -81,6 +94,8 @@ int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, /* Extract the context from the side buffer */ attr->max_qp = le32_to_cpu(sb->max_qp); + /* max_qp value reported by FW for PF doesn't include the QP1 for PF */ + attr->max_qp += 1; attr->max_qp_rd_atom = sb->max_qp_rd_atom > BNXT_QPLIB_MAX_OUT_RD_ATOM ? BNXT_QPLIB_MAX_OUT_RD_ATOM : sb->max_qp_rd_atom; @@ -129,6 +144,7 @@ int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, attr->tqm_alloc_reqs[i * 4 + 3] = *(++tqm_alloc); } + attr->is_atomic = bnxt_qplib_is_atomic_cap(rcfw); bail: bnxt_qplib_rcfw_free_sbuf(rcfw, sbuf); return rc; diff --git a/drivers/infiniband/hw/bnxt_re/qplib_sp.h b/drivers/infiniband/hw/bnxt_re/qplib_sp.h index a543f959098b..2ce7e2a32cf0 100644 --- a/drivers/infiniband/hw/bnxt_re/qplib_sp.h +++ b/drivers/infiniband/hw/bnxt_re/qplib_sp.h @@ -42,6 +42,8 @@ #define BNXT_QPLIB_RESERVED_QP_WRS 128 +#define PCI_EXP_DEVCTL2_ATOMIC_REQ 0x0040 + struct bnxt_qplib_dev_attr { char fw_ver[32]; u16 max_sgid; @@ -70,6 +72,7 @@ struct bnxt_qplib_dev_attr { u32 max_inline_data; u32 l2_db_size; u8 tqm_alloc_reqs[MAX_TQM_ALLOC_REQ]; + bool is_atomic; }; struct bnxt_qplib_pd { diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 29d30744d6c9..0cd0c1fa27d4 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -718,7 +718,7 @@ static struct ib_mr *iwch_alloc_mr(struct ib_pd *pd, struct iwch_mr *mhp; u32 mmid; u32 stag = 0; - int ret = 0; + int ret = -ENOMEM; if (mr_type != IB_MR_TYPE_MEM_REG || max_num_sg > T3_MAX_FASTREG_DEPTH) @@ -731,10 +731,8 @@ static struct ib_mr *iwch_alloc_mr(struct ib_pd *pd, goto err; mhp->pages = kcalloc(max_num_sg, sizeof(u64), GFP_KERNEL); - if (!mhp->pages) { - ret = -ENOMEM; + if (!mhp->pages) goto pl_err; - } mhp->rhp = rhp; ret = iwch_alloc_pbl(mhp, max_num_sg); @@ -751,7 +749,8 @@ static struct ib_mr *iwch_alloc_mr(struct ib_pd *pd, mhp->attr.state = 1; mmid = (stag) >> 8; mhp->ibmr.rkey = mhp->ibmr.lkey = stag; - if (insert_handle(rhp, &rhp->mmidr, mhp, mmid)) + ret = insert_handle(rhp, &rhp->mmidr, mhp, mmid); + if (ret) goto err3; pr_debug("%s mmid 0x%x mhp %p stag 0x%x\n", __func__, mmid, mhp, stag); diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c index e16fcaf6b5a3..be07da1997e6 100644 --- a/drivers/infiniband/hw/cxgb4/cq.c +++ b/drivers/infiniband/hw/cxgb4/cq.c @@ -963,6 +963,7 @@ struct ib_cq *c4iw_create_cq(struct ib_device *ibdev, goto err3; if (ucontext) { + ret = -ENOMEM; mm = kmalloc(sizeof *mm, GFP_KERNEL); if (!mm) goto err4; diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index 5332f06b99ba..c2fba76becd4 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -661,7 +661,7 @@ struct ib_mr *c4iw_alloc_mr(struct ib_pd *pd, rhp = php->rhp; if (mr_type != IB_MR_TYPE_MEM_REG || - max_num_sg > t4_max_fr_depth(&rhp->rdev.lldi.ulptx_memwrite_dsgl && + max_num_sg > t4_max_fr_depth(rhp->rdev.lldi.ulptx_memwrite_dsgl && use_dsgl)) return ERR_PTR(-EINVAL); diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index bfc77596acbe..cb7fc0d35d1d 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -569,7 +569,7 @@ static int build_rdma_read(union t4_wr *wqe, struct ib_send_wr *wr, u8 *len16) { if (wr->num_sge > 1) return -EINVAL; - if (wr->num_sge) { + if (wr->num_sge && wr->sg_list[0].length) { wqe->read.stag_src = cpu_to_be32(rdma_wr(wr)->rkey); wqe->read.to_src_hi = cpu_to_be32((u32)(rdma_wr(wr)->remote_addr >> 32)); diff --git a/drivers/infiniband/hw/hfi1/chip.c b/drivers/infiniband/hw/hfi1/chip.c index 2ba00b89df6a..94b54850ec75 100644 --- a/drivers/infiniband/hw/hfi1/chip.c +++ b/drivers/infiniband/hw/hfi1/chip.c @@ -12847,7 +12847,12 @@ static void remap_intr(struct hfi1_devdata *dd, int isrc, int msix_intr) /* clear from the handled mask of the general interrupt */ m = isrc / 64; n = isrc % 64; - dd->gi_mask[m] &= ~((u64)1 << n); + if (likely(m < CCE_NUM_INT_CSRS)) { + dd->gi_mask[m] &= ~((u64)1 << n); + } else { + dd_dev_err(dd, "remap interrupt err\n"); + return; + } /* direct the chip source to the given MSI-X interrupt */ m = isrc / 8; diff --git a/drivers/infiniband/hw/hfi1/qp.c b/drivers/infiniband/hw/hfi1/qp.c index 650305cc0373..1a7af9f60c13 100644 --- a/drivers/infiniband/hw/hfi1/qp.c +++ b/drivers/infiniband/hw/hfi1/qp.c @@ -647,18 +647,17 @@ void qp_iter_print(struct seq_file *s, struct qp_iter *iter) qp->pid); } -void *qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp, - gfp_t gfp) +void *qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp) { struct hfi1_qp_priv *priv; - priv = kzalloc_node(sizeof(*priv), gfp, rdi->dparms.node); + priv = kzalloc_node(sizeof(*priv), GFP_KERNEL, rdi->dparms.node); if (!priv) return ERR_PTR(-ENOMEM); priv->owner = qp; - priv->s_ahg = kzalloc_node(sizeof(*priv->s_ahg), gfp, + priv->s_ahg = kzalloc_node(sizeof(*priv->s_ahg), GFP_KERNEL, rdi->dparms.node); if (!priv->s_ahg) { kfree(priv); diff --git a/drivers/infiniband/hw/hfi1/qp.h b/drivers/infiniband/hw/hfi1/qp.h index 1eb9cd7b8c19..6fe542b6a927 100644 --- a/drivers/infiniband/hw/hfi1/qp.h +++ b/drivers/infiniband/hw/hfi1/qp.h @@ -123,8 +123,7 @@ void hfi1_migrate_qp(struct rvt_qp *qp); /* * Functions provided by hfi1 driver for rdmavt to use */ -void *qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp, - gfp_t gfp); +void *qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp); void qp_priv_free(struct rvt_dev_info *rdi, struct rvt_qp *qp); unsigned free_all_qps(struct rvt_dev_info *rdi); void notify_qp_reset(struct rvt_qp *qp); diff --git a/drivers/infiniband/hw/hns/hns_roce_ah.c b/drivers/infiniband/hw/hns/hns_roce_ah.c index f78a733a63ec..d545302b8ef8 100644 --- a/drivers/infiniband/hw/hns/hns_roce_ah.c +++ b/drivers/infiniband/hw/hns/hns_roce_ah.c @@ -64,8 +64,10 @@ struct ib_ah *hns_roce_create_ah(struct ib_pd *ibpd, } else { u8 *dmac = rdma_ah_retrieve_dmac(ah_attr); - if (!dmac) + if (!dmac) { + kfree(ah); return ERR_PTR(-EINVAL); + } memcpy(ah->av.mac, dmac, ETH_ALEN); } diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c index 37d5d29597a4..2540b65e242c 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v1.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v1.c @@ -228,14 +228,14 @@ int hns_roce_v1_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, switch (wr->opcode) { case IB_WR_RDMA_READ: ps_opcode = HNS_ROCE_WQE_OPCODE_RDMA_READ; - set_raddr_seg(wqe, atomic_wr(wr)->remote_addr, - atomic_wr(wr)->rkey); + set_raddr_seg(wqe, rdma_wr(wr)->remote_addr, + rdma_wr(wr)->rkey); break; case IB_WR_RDMA_WRITE: case IB_WR_RDMA_WRITE_WITH_IMM: ps_opcode = HNS_ROCE_WQE_OPCODE_RDMA_WRITE; - set_raddr_seg(wqe, atomic_wr(wr)->remote_addr, - atomic_wr(wr)->rkey); + set_raddr_seg(wqe, rdma_wr(wr)->remote_addr, + rdma_wr(wr)->rkey); break; case IB_WR_SEND: case IB_WR_SEND_WITH_INV: @@ -661,9 +661,11 @@ static int hns_roce_v1_rsv_lp_qp(struct hns_roce_dev *hr_dev) union ib_gid dgid; u64 subnet_prefix; int attr_mask = 0; - int i; + int i, j; int ret; + u8 queue_en[HNS_ROCE_V1_RESV_QP] = { 0 }; u8 phy_port; + u8 port = 0; u8 sl; priv = (struct hns_roce_v1_priv *)hr_dev->hw->priv; @@ -709,27 +711,35 @@ static int hns_roce_v1_rsv_lp_qp(struct hns_roce_dev *hr_dev) attr.rnr_retry = 7; attr.timeout = 0x12; attr.path_mtu = IB_MTU_256; + attr.ah_attr.type = RDMA_AH_ATTR_TYPE_ROCE; rdma_ah_set_grh(&attr.ah_attr, NULL, 0, 0, 1, 0); rdma_ah_set_static_rate(&attr.ah_attr, 3); subnet_prefix = cpu_to_be64(0xfe80000000000000LL); for (i = 0; i < HNS_ROCE_V1_RESV_QP; i++) { + phy_port = (i >= HNS_ROCE_MAX_PORTS) ? (i - 2) : + (i % HNS_ROCE_MAX_PORTS); + sl = i / HNS_ROCE_MAX_PORTS; + + for (j = 0; j < caps->num_ports; j++) { + if (hr_dev->iboe.phy_port[j] == phy_port) { + queue_en[i] = 1; + port = j; + break; + } + } + + if (!queue_en[i]) + continue; + free_mr->mr_free_qp[i] = hns_roce_v1_create_lp_qp(hr_dev, pd); - if (IS_ERR(free_mr->mr_free_qp[i])) { + if (!free_mr->mr_free_qp[i]) { dev_err(dev, "Create loop qp failed!\n"); goto create_lp_qp_failed; } hr_qp = free_mr->mr_free_qp[i]; - sl = i / caps->num_ports; - - if (caps->num_ports == HNS_ROCE_MAX_PORTS) - phy_port = (i >= HNS_ROCE_MAX_PORTS) ? (i - 2) : - (i % caps->num_ports); - else - phy_port = i % caps->num_ports; - - hr_qp->port = phy_port + 1; + hr_qp->port = port; hr_qp->phy_port = phy_port; hr_qp->ibqp.qp_type = IB_QPT_RC; hr_qp->ibqp.device = &hr_dev->ib_dev; @@ -739,23 +749,22 @@ static int hns_roce_v1_rsv_lp_qp(struct hns_roce_dev *hr_dev) hr_qp->ibqp.recv_cq = cq; hr_qp->ibqp.send_cq = cq; - rdma_ah_set_port_num(&attr.ah_attr, phy_port + 1); - rdma_ah_set_sl(&attr.ah_attr, phy_port + 1); - attr.port_num = phy_port + 1; + rdma_ah_set_port_num(&attr.ah_attr, port + 1); + rdma_ah_set_sl(&attr.ah_attr, sl); + attr.port_num = port + 1; attr.dest_qp_num = hr_qp->qpn; memcpy(rdma_ah_retrieve_dmac(&attr.ah_attr), - hr_dev->dev_addr[phy_port], + hr_dev->dev_addr[port], MAC_ADDR_OCTET_NUM); memcpy(&dgid.raw, &subnet_prefix, sizeof(u64)); - memcpy(&dgid.raw[8], hr_dev->dev_addr[phy_port], 3); - memcpy(&dgid.raw[13], hr_dev->dev_addr[phy_port] + 3, 3); + memcpy(&dgid.raw[8], hr_dev->dev_addr[port], 3); + memcpy(&dgid.raw[13], hr_dev->dev_addr[port] + 3, 3); dgid.raw[11] = 0xff; dgid.raw[12] = 0xfe; dgid.raw[8] ^= 2; rdma_ah_set_dgid_raw(&attr.ah_attr, dgid.raw); - attr_mask |= IB_QP_PORT; ret = hr_dev->hw->modify_qp(&hr_qp->ibqp, &attr, attr_mask, IB_QPS_RESET, IB_QPS_INIT); @@ -812,6 +821,9 @@ static void hns_roce_v1_release_lp_qp(struct hns_roce_dev *hr_dev) for (i = 0; i < HNS_ROCE_V1_RESV_QP; i++) { hr_qp = free_mr->mr_free_qp[i]; + if (!hr_qp) + continue; + ret = hns_roce_v1_destroy_qp(&hr_qp->ibqp); if (ret) dev_err(dev, "Destroy qp %d for mr free failed(%d)!\n", @@ -963,7 +975,7 @@ static void hns_roce_v1_mr_free_work_fn(struct work_struct *work) msecs_to_jiffies(HNS_ROCE_V1_FREE_MR_TIMEOUT_MSECS) + jiffies; int i; int ret; - int ne; + int ne = 0; mr_work = container_of(work, struct hns_roce_mr_free_work, work); hr_mr = (struct hns_roce_mr *)mr_work->mr; @@ -976,6 +988,10 @@ static void hns_roce_v1_mr_free_work_fn(struct work_struct *work) for (i = 0; i < HNS_ROCE_V1_RESV_QP; i++) { hr_qp = free_mr->mr_free_qp[i]; + if (!hr_qp) + continue; + ne++; + ret = hns_roce_v1_send_lp_wqe(hr_qp); if (ret) { dev_err(dev, @@ -985,7 +1001,6 @@ static void hns_roce_v1_mr_free_work_fn(struct work_struct *work) } } - ne = HNS_ROCE_V1_RESV_QP; do { ret = hns_roce_v1_poll_cq(&mr_free_cq->ib_cq, ne, wc); if (ret < 0) { @@ -995,7 +1010,8 @@ static void hns_roce_v1_mr_free_work_fn(struct work_struct *work) goto free_work; } ne -= ret; - msleep(HNS_ROCE_V1_FREE_MR_WAIT_VALUE); + usleep_range(HNS_ROCE_V1_FREE_MR_WAIT_VALUE * 1000, + (1 + HNS_ROCE_V1_FREE_MR_WAIT_VALUE) * 1000); } while (ne && time_before_eq(jiffies, end)); if (ne != 0) @@ -2181,7 +2197,7 @@ static int hns_roce_v1_poll_one(struct hns_roce_cq *hr_cq, } wc->wr_id = wq->wrid[wq->tail & (wq->wqe_cnt - 1)]; ++wq->tail; - } else { + } else { /* RQ conrespond to CQE */ wc->byte_len = le32_to_cpu(cqe->byte_cnt); opcode = roce_get_field(cqe->cqe_byte_4, @@ -3533,10 +3549,12 @@ static int check_qp_db_process_status(struct hns_roce_dev *hr_dev, old_cnt = roce_get_field(old_send, ROCEE_SDB_SEND_PTR_SDB_SEND_PTR_M, ROCEE_SDB_SEND_PTR_SDB_SEND_PTR_S); - if (cur_cnt - old_cnt > SDB_ST_CMP_VAL) + if (cur_cnt - old_cnt > + SDB_ST_CMP_VAL) { success_flags = 1; - else { - send_ptr = roce_get_field(old_send, + } else { + send_ptr = + roce_get_field(old_send, ROCEE_SDB_SEND_PTR_SDB_SEND_PTR_M, ROCEE_SDB_SEND_PTR_SDB_SEND_PTR_S) + roce_get_field(sdb_retry_cnt, @@ -3641,6 +3659,7 @@ static void hns_roce_v1_destroy_qp_work_fn(struct work_struct *work) struct hns_roce_dev *hr_dev; struct hns_roce_qp *hr_qp; struct device *dev; + unsigned long qpn; int ret; qp_work_entry = container_of(work, struct hns_roce_qp_work, work); @@ -3648,8 +3667,9 @@ static void hns_roce_v1_destroy_qp_work_fn(struct work_struct *work) dev = &hr_dev->pdev->dev; priv = (struct hns_roce_v1_priv *)hr_dev->hw->priv; hr_qp = qp_work_entry->qp; + qpn = hr_qp->qpn; - dev_dbg(dev, "Schedule destroy QP(0x%lx) work.\n", hr_qp->qpn); + dev_dbg(dev, "Schedule destroy QP(0x%lx) work.\n", qpn); qp_work_entry->sche_cnt++; @@ -3660,7 +3680,7 @@ static void hns_roce_v1_destroy_qp_work_fn(struct work_struct *work) &qp_work_entry->db_wait_stage); if (ret) { dev_err(dev, "Check QP(0x%lx) db process status failed!\n", - hr_qp->qpn); + qpn); return; } @@ -3674,7 +3694,7 @@ static void hns_roce_v1_destroy_qp_work_fn(struct work_struct *work) ret = hns_roce_v1_modify_qp(&hr_qp->ibqp, NULL, 0, hr_qp->state, IB_QPS_RESET); if (ret) { - dev_err(dev, "Modify QP(0x%lx) to RST failed!\n", hr_qp->qpn); + dev_err(dev, "Modify QP(0x%lx) to RST failed!\n", qpn); return; } @@ -3683,14 +3703,14 @@ static void hns_roce_v1_destroy_qp_work_fn(struct work_struct *work) if (hr_qp->ibqp.qp_type == IB_QPT_RC) { /* RC QP, release QPN */ - hns_roce_release_range_qp(hr_dev, hr_qp->qpn, 1); + hns_roce_release_range_qp(hr_dev, qpn, 1); kfree(hr_qp); } else kfree(hr_to_hr_sqp(hr_qp)); kfree(qp_work_entry); - dev_dbg(dev, "Accomplished destroy QP(0x%lx) work.\n", hr_qp->qpn); + dev_dbg(dev, "Accomplished destroy QP(0x%lx) work.\n", qpn); } int hns_roce_v1_destroy_qp(struct ib_qp *ibqp) diff --git a/drivers/infiniband/hw/hns/hns_roce_main.c b/drivers/infiniband/hw/hns/hns_roce_main.c index c3b41f95e70a..d9777b662eba 100644 --- a/drivers/infiniband/hw/hns/hns_roce_main.c +++ b/drivers/infiniband/hw/hns/hns_roce_main.c @@ -125,8 +125,6 @@ static int handle_en_event(struct hns_roce_dev *hr_dev, u8 port, return -ENODEV; } - spin_lock_bh(&hr_dev->iboe.lock); - switch (event) { case NETDEV_UP: case NETDEV_CHANGE: @@ -144,7 +142,6 @@ static int handle_en_event(struct hns_roce_dev *hr_dev, u8 port, break; } - spin_unlock_bh(&hr_dev->iboe.lock); return 0; } diff --git a/drivers/infiniband/hw/i40iw/i40iw.h b/drivers/infiniband/hw/i40iw/i40iw.h index da2eb5a281fa..9b1566468744 100644 --- a/drivers/infiniband/hw/i40iw/i40iw.h +++ b/drivers/infiniband/hw/i40iw/i40iw.h @@ -527,6 +527,7 @@ enum i40iw_status_code i40iw_add_mac_addr(struct i40iw_device *iwdev, int i40iw_modify_qp(struct ib_qp *, struct ib_qp_attr *, int, struct ib_udata *); void i40iw_cq_wq_destroy(struct i40iw_device *iwdev, struct i40iw_sc_cq *cq); +void i40iw_cleanup_pending_cqp_op(struct i40iw_device *iwdev); void i40iw_rem_pdusecount(struct i40iw_pd *iwpd, struct i40iw_device *iwdev); void i40iw_add_pdusecount(struct i40iw_pd *iwpd); void i40iw_rem_devusecount(struct i40iw_device *iwdev); diff --git a/drivers/infiniband/hw/i40iw/i40iw_cm.c b/drivers/infiniband/hw/i40iw/i40iw_cm.c index 6ae98aa7f74e..5a2fa743676c 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_cm.c +++ b/drivers/infiniband/hw/i40iw/i40iw_cm.c @@ -3487,7 +3487,8 @@ static void i40iw_cm_disconn_true(struct i40iw_qp *iwqp) if (((original_hw_tcp_state == I40IW_TCP_STATE_CLOSED) || (original_hw_tcp_state == I40IW_TCP_STATE_TIME_WAIT) || (last_ae == I40IW_AE_RDMAP_ROE_BAD_LLP_CLOSE) || - (last_ae == I40IW_AE_LLP_CONNECTION_RESET))) { + (last_ae == I40IW_AE_LLP_CONNECTION_RESET) || + iwdev->reset)) { issue_close = 1; iwqp->cm_id = NULL; if (!iwqp->flush_issued) { @@ -4265,6 +4266,8 @@ void i40iw_cm_disconnect_all(struct i40iw_device *iwdev) cm_node = container_of(list_node, struct i40iw_cm_node, connected_entry); attr.qp_state = IB_QPS_ERR; i40iw_modify_qp(&cm_node->iwqp->ibqp, &attr, IB_QP_STATE, NULL); + if (iwdev->reset) + i40iw_cm_disconn(cm_node->iwqp); i40iw_rem_ref_cm_node(cm_node); } } diff --git a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c index a027e2072477..a49ff2eb6fb3 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_ctrl.c +++ b/drivers/infiniband/hw/i40iw/i40iw_ctrl.c @@ -130,20 +130,32 @@ static enum i40iw_status_code i40iw_sc_parse_fpm_commit_buf( u64 base = 0; u32 i, j; u32 k = 0; - u32 low; /* copy base values in obj_info */ - for (i = I40IW_HMC_IW_QP, j = 0; - i <= I40IW_HMC_IW_PBLE; i++, j += 8) { + for (i = I40IW_HMC_IW_QP, j = 0; i <= I40IW_HMC_IW_PBLE; i++, j += 8) { + if ((i == I40IW_HMC_IW_SRQ) || + (i == I40IW_HMC_IW_FSIMC) || + (i == I40IW_HMC_IW_FSIAV)) { + info[i].base = 0; + info[i].cnt = 0; + continue; + } get_64bit_val(buf, j, &temp); info[i].base = RS_64_1(temp, 32) * 512; if (info[i].base > base) { base = info[i].base; k = i; } - low = (u32)(temp); - if (low) - info[i].cnt = low; + if (i == I40IW_HMC_IW_APBVT_ENTRY) { + info[i].cnt = 1; + continue; + } + if (i == I40IW_HMC_IW_QP) + info[i].cnt = (u32)RS_64(temp, I40IW_QUERY_FPM_MAX_QPS); + else if (i == I40IW_HMC_IW_CQ) + info[i].cnt = (u32)RS_64(temp, I40IW_QUERY_FPM_MAX_CQS); + else + info[i].cnt = (u32)(temp); } size = info[k].cnt * info[k].size + info[k].base; if (size & 0x1FFFFF) @@ -155,6 +167,31 @@ static enum i40iw_status_code i40iw_sc_parse_fpm_commit_buf( } /** + * i40iw_sc_decode_fpm_query() - Decode a 64 bit value into max count and size + * @buf: ptr to fpm query buffer + * @buf_idx: index into buf + * @info: ptr to i40iw_hmc_obj_info struct + * @rsrc_idx: resource index into info + * + * Decode a 64 bit value from fpm query buffer into max count and size + */ +static u64 i40iw_sc_decode_fpm_query(u64 *buf, + u32 buf_idx, + struct i40iw_hmc_obj_info *obj_info, + u32 rsrc_idx) +{ + u64 temp; + u32 size; + + get_64bit_val(buf, buf_idx, &temp); + obj_info[rsrc_idx].max_cnt = (u32)temp; + size = (u32)RS_64_1(temp, 32); + obj_info[rsrc_idx].size = LS_64_1(1, size); + + return temp; +} + +/** * i40iw_sc_parse_fpm_query_buf() - parses fpm query buffer * @buf: ptr to fpm query buffer * @info: ptr to i40iw_hmc_obj_info struct @@ -168,9 +205,9 @@ static enum i40iw_status_code i40iw_sc_parse_fpm_query_buf( struct i40iw_hmc_info *hmc_info, struct i40iw_hmc_fpm_misc *hmc_fpm_misc) { - u64 temp; struct i40iw_hmc_obj_info *obj_info; - u32 i, j, size; + u64 temp; + u32 size; u16 max_pe_sds; obj_info = hmc_info->hmc_obj; @@ -185,41 +222,52 @@ static enum i40iw_status_code i40iw_sc_parse_fpm_query_buf( hmc_fpm_misc->max_sds = max_pe_sds; hmc_info->sd_table.sd_cnt = max_pe_sds + hmc_info->first_sd_index; - for (i = I40IW_HMC_IW_QP, j = 8; - i <= I40IW_HMC_IW_ARP; i++, j += 8) { - get_64bit_val(buf, j, &temp); - if (i == I40IW_HMC_IW_QP) - obj_info[i].max_cnt = (u32)RS_64(temp, I40IW_QUERY_FPM_MAX_QPS); - else if (i == I40IW_HMC_IW_CQ) - obj_info[i].max_cnt = (u32)RS_64(temp, I40IW_QUERY_FPM_MAX_CQS); - else - obj_info[i].max_cnt = (u32)temp; + get_64bit_val(buf, 8, &temp); + obj_info[I40IW_HMC_IW_QP].max_cnt = (u32)RS_64(temp, I40IW_QUERY_FPM_MAX_QPS); + size = (u32)RS_64_1(temp, 32); + obj_info[I40IW_HMC_IW_QP].size = LS_64_1(1, size); - size = (u32)RS_64_1(temp, 32); - obj_info[i].size = ((u64)1 << size); - } - for (i = I40IW_HMC_IW_MR, j = 48; - i <= I40IW_HMC_IW_PBLE; i++, j += 8) { - get_64bit_val(buf, j, &temp); - obj_info[i].max_cnt = (u32)temp; - size = (u32)RS_64_1(temp, 32); - obj_info[i].size = LS_64_1(1, size); - } + get_64bit_val(buf, 16, &temp); + obj_info[I40IW_HMC_IW_CQ].max_cnt = (u32)RS_64(temp, I40IW_QUERY_FPM_MAX_CQS); + size = (u32)RS_64_1(temp, 32); + obj_info[I40IW_HMC_IW_CQ].size = LS_64_1(1, size); + + i40iw_sc_decode_fpm_query(buf, 32, obj_info, I40IW_HMC_IW_HTE); + i40iw_sc_decode_fpm_query(buf, 40, obj_info, I40IW_HMC_IW_ARP); + + obj_info[I40IW_HMC_IW_APBVT_ENTRY].size = 8192; + obj_info[I40IW_HMC_IW_APBVT_ENTRY].max_cnt = 1; + + i40iw_sc_decode_fpm_query(buf, 48, obj_info, I40IW_HMC_IW_MR); + i40iw_sc_decode_fpm_query(buf, 56, obj_info, I40IW_HMC_IW_XF); - get_64bit_val(buf, 120, &temp); - hmc_fpm_misc->max_ceqs = (u8)RS_64(temp, I40IW_QUERY_FPM_MAX_CEQS); - get_64bit_val(buf, 120, &temp); - hmc_fpm_misc->ht_multiplier = RS_64(temp, I40IW_QUERY_FPM_HTMULTIPLIER); - get_64bit_val(buf, 120, &temp); - hmc_fpm_misc->timer_bucket = RS_64(temp, I40IW_QUERY_FPM_TIMERBUCKET); get_64bit_val(buf, 64, &temp); + obj_info[I40IW_HMC_IW_XFFL].max_cnt = (u32)temp; + obj_info[I40IW_HMC_IW_XFFL].size = 4; hmc_fpm_misc->xf_block_size = RS_64(temp, I40IW_QUERY_FPM_XFBLOCKSIZE); if (!hmc_fpm_misc->xf_block_size) return I40IW_ERR_INVALID_SIZE; + + i40iw_sc_decode_fpm_query(buf, 72, obj_info, I40IW_HMC_IW_Q1); + get_64bit_val(buf, 80, &temp); + obj_info[I40IW_HMC_IW_Q1FL].max_cnt = (u32)temp; + obj_info[I40IW_HMC_IW_Q1FL].size = 4; hmc_fpm_misc->q1_block_size = RS_64(temp, I40IW_QUERY_FPM_Q1BLOCKSIZE); if (!hmc_fpm_misc->q1_block_size) return I40IW_ERR_INVALID_SIZE; + + i40iw_sc_decode_fpm_query(buf, 88, obj_info, I40IW_HMC_IW_TIMER); + + get_64bit_val(buf, 112, &temp); + obj_info[I40IW_HMC_IW_PBLE].max_cnt = (u32)temp; + obj_info[I40IW_HMC_IW_PBLE].size = 8; + + get_64bit_val(buf, 120, &temp); + hmc_fpm_misc->max_ceqs = (u8)RS_64(temp, I40IW_QUERY_FPM_MAX_CEQS); + hmc_fpm_misc->ht_multiplier = RS_64(temp, I40IW_QUERY_FPM_HTMULTIPLIER); + hmc_fpm_misc->timer_bucket = RS_64(temp, I40IW_QUERY_FPM_TIMERBUCKET); + return 0; } @@ -1970,6 +2018,8 @@ static enum i40iw_status_code i40iw_sc_ccq_destroy(struct i40iw_sc_cq *ccq, ret_code = i40iw_cqp_poll_registers(cqp, tail, 1000); } + cqp->process_cqp_sds = i40iw_update_sds_noccq; + return ret_code; } @@ -3390,13 +3440,6 @@ enum i40iw_status_code i40iw_sc_init_iw_hmc(struct i40iw_sc_dev *dev, u8 hmc_fn_ hmc_info->sd_table.sd_entry = virt_mem.va; } - /* fill size of objects which are fixed */ - hmc_info->hmc_obj[I40IW_HMC_IW_XFFL].size = 4; - hmc_info->hmc_obj[I40IW_HMC_IW_Q1FL].size = 4; - hmc_info->hmc_obj[I40IW_HMC_IW_PBLE].size = 8; - hmc_info->hmc_obj[I40IW_HMC_IW_APBVT_ENTRY].size = 8192; - hmc_info->hmc_obj[I40IW_HMC_IW_APBVT_ENTRY].max_cnt = 1; - return ret_code; } @@ -4838,7 +4881,7 @@ void i40iw_vsi_stats_free(struct i40iw_sc_vsi *vsi) { u8 fcn_id = vsi->fcn_id; - if ((vsi->stats_fcn_id_alloc) && (fcn_id != I40IW_INVALID_FCN_ID)) + if (vsi->stats_fcn_id_alloc && fcn_id < I40IW_MAX_STATS_COUNT) vsi->dev->fcn_id_array[fcn_id] = false; i40iw_hw_stats_stop_timer(vsi); } diff --git a/drivers/infiniband/hw/i40iw/i40iw_d.h b/drivers/infiniband/hw/i40iw/i40iw_d.h index a39ac12b6a7e..2ebaadbed379 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_d.h +++ b/drivers/infiniband/hw/i40iw/i40iw_d.h @@ -1507,8 +1507,8 @@ enum { I40IW_CQ0_ALIGNMENT_MASK = (256 - 1), I40IW_HOST_CTX_ALIGNMENT_MASK = (4 - 1), I40IW_SHADOWAREA_MASK = (128 - 1), - I40IW_FPM_QUERY_BUF_ALIGNMENT_MASK = 0, - I40IW_FPM_COMMIT_BUF_ALIGNMENT_MASK = 0 + I40IW_FPM_QUERY_BUF_ALIGNMENT_MASK = (4 - 1), + I40IW_FPM_COMMIT_BUF_ALIGNMENT_MASK = (4 - 1) }; enum i40iw_alignment { diff --git a/drivers/infiniband/hw/i40iw/i40iw_main.c b/drivers/infiniband/hw/i40iw/i40iw_main.c index e0f47cc2effc..ae8463ff59a7 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_main.c +++ b/drivers/infiniband/hw/i40iw/i40iw_main.c @@ -243,6 +243,8 @@ static void i40iw_destroy_cqp(struct i40iw_device *iwdev, bool free_hwcqp) if (free_hwcqp) dev->cqp_ops->cqp_destroy(dev->cqp); + i40iw_cleanup_pending_cqp_op(iwdev); + i40iw_free_dma_mem(dev->hw, &cqp->sq); kfree(cqp->scratch_array); iwdev->cqp.scratch_array = NULL; @@ -274,13 +276,12 @@ static void i40iw_disable_irq(struct i40iw_sc_dev *dev, /** * i40iw_destroy_aeq - destroy aeq * @iwdev: iwarp device - * @reset: true if called before reset * * Issue a destroy aeq request and * free the resources associated with the aeq * The function is called during driver unload */ -static void i40iw_destroy_aeq(struct i40iw_device *iwdev, bool reset) +static void i40iw_destroy_aeq(struct i40iw_device *iwdev) { enum i40iw_status_code status = I40IW_ERR_NOT_READY; struct i40iw_sc_dev *dev = &iwdev->sc_dev; @@ -288,7 +289,7 @@ static void i40iw_destroy_aeq(struct i40iw_device *iwdev, bool reset) if (!iwdev->msix_shared) i40iw_disable_irq(dev, iwdev->iw_msixtbl, (void *)iwdev); - if (reset) + if (iwdev->reset) goto exit; if (!dev->aeq_ops->aeq_destroy(&aeq->sc_aeq, 0, 1)) @@ -304,19 +305,17 @@ exit: * i40iw_destroy_ceq - destroy ceq * @iwdev: iwarp device * @iwceq: ceq to be destroyed - * @reset: true if called before reset * * Issue a destroy ceq request and * free the resources associated with the ceq */ static void i40iw_destroy_ceq(struct i40iw_device *iwdev, - struct i40iw_ceq *iwceq, - bool reset) + struct i40iw_ceq *iwceq) { enum i40iw_status_code status; struct i40iw_sc_dev *dev = &iwdev->sc_dev; - if (reset) + if (iwdev->reset) goto exit; status = dev->ceq_ops->ceq_destroy(&iwceq->sc_ceq, 0, 1); @@ -335,12 +334,11 @@ exit: /** * i40iw_dele_ceqs - destroy all ceq's * @iwdev: iwarp device - * @reset: true if called before reset * * Go through all of the device ceq's and for each ceq * disable the ceq interrupt and destroy the ceq */ -static void i40iw_dele_ceqs(struct i40iw_device *iwdev, bool reset) +static void i40iw_dele_ceqs(struct i40iw_device *iwdev) { u32 i = 0; struct i40iw_sc_dev *dev = &iwdev->sc_dev; @@ -349,32 +347,31 @@ static void i40iw_dele_ceqs(struct i40iw_device *iwdev, bool reset) if (iwdev->msix_shared) { i40iw_disable_irq(dev, msix_vec, (void *)iwdev); - i40iw_destroy_ceq(iwdev, iwceq, reset); + i40iw_destroy_ceq(iwdev, iwceq); iwceq++; i++; } for (msix_vec++; i < iwdev->ceqs_count; i++, msix_vec++, iwceq++) { i40iw_disable_irq(dev, msix_vec, (void *)iwceq); - i40iw_destroy_ceq(iwdev, iwceq, reset); + i40iw_destroy_ceq(iwdev, iwceq); } } /** * i40iw_destroy_ccq - destroy control cq * @iwdev: iwarp device - * @reset: true if called before reset * * Issue destroy ccq request and * free the resources associated with the ccq */ -static void i40iw_destroy_ccq(struct i40iw_device *iwdev, bool reset) +static void i40iw_destroy_ccq(struct i40iw_device *iwdev) { struct i40iw_sc_dev *dev = &iwdev->sc_dev; struct i40iw_ccq *ccq = &iwdev->ccq; enum i40iw_status_code status = 0; - if (!reset) + if (!iwdev->reset) status = dev->ccq_ops->ccq_destroy(dev->ccq, 0, true); if (status) i40iw_pr_err("ccq destroy failed %d\n", status); @@ -810,7 +807,7 @@ static enum i40iw_status_code i40iw_setup_ceqs(struct i40iw_device *iwdev, iwceq->msix_idx = msix_vec->idx; status = i40iw_configure_ceq_vector(iwdev, iwceq, ceq_id, msix_vec); if (status) { - i40iw_destroy_ceq(iwdev, iwceq, false); + i40iw_destroy_ceq(iwdev, iwceq); break; } i40iw_enable_intr(&iwdev->sc_dev, msix_vec->idx); @@ -912,7 +909,7 @@ static enum i40iw_status_code i40iw_setup_aeq(struct i40iw_device *iwdev) status = i40iw_configure_aeq_vector(iwdev); if (status) { - i40iw_destroy_aeq(iwdev, false); + i40iw_destroy_aeq(iwdev); return status; } @@ -1442,12 +1439,11 @@ static enum i40iw_status_code i40iw_save_msix_info(struct i40iw_device *iwdev, /** * i40iw_deinit_device - clean up the device resources * @iwdev: iwarp device - * @reset: true if called before reset * * Destroy the ib device interface, remove the mac ip entry and ipv4/ipv6 addresses, * destroy the device queues and free the pble and the hmc objects */ -static void i40iw_deinit_device(struct i40iw_device *iwdev, bool reset) +static void i40iw_deinit_device(struct i40iw_device *iwdev) { struct i40e_info *ldev = iwdev->ldev; @@ -1464,7 +1460,7 @@ static void i40iw_deinit_device(struct i40iw_device *iwdev, bool reset) i40iw_destroy_rdma_device(iwdev->iwibdev); /* fallthrough */ case IP_ADDR_REGISTERED: - if (!reset) + if (!iwdev->reset) i40iw_del_macip_entry(iwdev, (u8)iwdev->mac_ip_table_idx); /* fallthrough */ case INET_NOTIFIER: @@ -1474,26 +1470,26 @@ static void i40iw_deinit_device(struct i40iw_device *iwdev, bool reset) unregister_inet6addr_notifier(&i40iw_inetaddr6_notifier); } /* fallthrough */ + case PBLE_CHUNK_MEM: + i40iw_destroy_pble_pool(dev, iwdev->pble_rsrc); + /* fallthrough */ case CEQ_CREATED: - i40iw_dele_ceqs(iwdev, reset); + i40iw_dele_ceqs(iwdev); /* fallthrough */ case AEQ_CREATED: - i40iw_destroy_aeq(iwdev, reset); + i40iw_destroy_aeq(iwdev); /* fallthrough */ case IEQ_CREATED: - i40iw_puda_dele_resources(&iwdev->vsi, I40IW_PUDA_RSRC_TYPE_IEQ, reset); + i40iw_puda_dele_resources(&iwdev->vsi, I40IW_PUDA_RSRC_TYPE_IEQ, iwdev->reset); /* fallthrough */ case ILQ_CREATED: - i40iw_puda_dele_resources(&iwdev->vsi, I40IW_PUDA_RSRC_TYPE_ILQ, reset); + i40iw_puda_dele_resources(&iwdev->vsi, I40IW_PUDA_RSRC_TYPE_ILQ, iwdev->reset); /* fallthrough */ case CCQ_CREATED: - i40iw_destroy_ccq(iwdev, reset); - /* fallthrough */ - case PBLE_CHUNK_MEM: - i40iw_destroy_pble_pool(dev, iwdev->pble_rsrc); + i40iw_destroy_ccq(iwdev); /* fallthrough */ case HMC_OBJS_CREATED: - i40iw_del_hmc_objects(dev, dev->hmc_info, true, reset); + i40iw_del_hmc_objects(dev, dev->hmc_info, true, iwdev->reset); /* fallthrough */ case CQP_CREATED: i40iw_destroy_cqp(iwdev, true); @@ -1670,6 +1666,7 @@ static int i40iw_open(struct i40e_info *ldev, struct i40e_client *client) status = i40iw_hmc_init_pble(&iwdev->sc_dev, iwdev->pble_rsrc); if (status) break; + iwdev->init_state = PBLE_CHUNK_MEM; iwdev->virtchnl_wq = alloc_ordered_workqueue("iwvch", WQ_MEM_RECLAIM); i40iw_register_notifiers(); iwdev->init_state = INET_NOTIFIER; @@ -1693,7 +1690,7 @@ static int i40iw_open(struct i40e_info *ldev, struct i40e_client *client) } while (0); i40iw_pr_err("status = %d last completion = %d\n", status, iwdev->init_state); - i40iw_deinit_device(iwdev, false); + i40iw_deinit_device(iwdev); return -ERESTART; } @@ -1774,9 +1771,12 @@ static void i40iw_close(struct i40e_info *ldev, struct i40e_client *client, bool iwdev = &hdl->device; iwdev->closing = true; + if (reset) + iwdev->reset = true; + i40iw_cm_disconnect_all(iwdev); destroy_workqueue(iwdev->virtchnl_wq); - i40iw_deinit_device(iwdev, reset); + i40iw_deinit_device(iwdev); } /** diff --git a/drivers/infiniband/hw/i40iw/i40iw_puda.c b/drivers/infiniband/hw/i40iw/i40iw_puda.c index db41ab40da9c..7f5583d83622 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_puda.c +++ b/drivers/infiniband/hw/i40iw/i40iw_puda.c @@ -408,6 +408,9 @@ enum i40iw_status_code i40iw_puda_send(struct i40iw_sc_qp *qp, set_64bit_val(wqe, 0, info->paddr); set_64bit_val(wqe, 8, LS_64(info->len, I40IWQPSQ_FRAG_LEN)); set_64bit_val(wqe, 16, header[0]); + + /* Ensure all data is written before writing valid bit */ + wmb(); set_64bit_val(wqe, 24, header[1]); i40iw_debug_buf(qp->dev, I40IW_DEBUG_PUDA, "PUDA SEND WQE", wqe, 32); @@ -682,7 +685,7 @@ static enum i40iw_status_code i40iw_puda_cq_create(struct i40iw_puda_rsrc *rsrc) cqsize = rsrc->cq_size * (sizeof(struct i40iw_cqe)); tsize = cqsize + sizeof(struct i40iw_cq_shadow_area); ret = i40iw_allocate_dma_mem(dev->hw, &rsrc->cqmem, tsize, - I40IW_CQ0_ALIGNMENT_MASK); + I40IW_CQ0_ALIGNMENT); if (ret) return ret; @@ -1411,10 +1414,10 @@ static void i40iw_ieq_handle_exception(struct i40iw_puda_rsrc *ieq, if (!list_empty(rxlist)) { tmpbuf = (struct i40iw_puda_buf *)rxlist->next; - plist = &tmpbuf->list; while ((struct list_head *)tmpbuf != rxlist) { if ((int)(buf->seqnum - tmpbuf->seqnum) < 0) break; + plist = &tmpbuf->list; tmpbuf = (struct i40iw_puda_buf *)plist->next; } /* Insert buf before tmpbuf */ diff --git a/drivers/infiniband/hw/i40iw/i40iw_status.h b/drivers/infiniband/hw/i40iw/i40iw_status.h index 91c421762f06..f7013f11d808 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_status.h +++ b/drivers/infiniband/hw/i40iw/i40iw_status.h @@ -62,7 +62,7 @@ enum i40iw_status_code { I40IW_ERR_INVALID_ALIGNMENT = -23, I40IW_ERR_FLUSHED_QUEUE = -24, I40IW_ERR_INVALID_PUSH_PAGE_INDEX = -25, - I40IW_ERR_INVALID_IMM_DATA_SIZE = -26, + I40IW_ERR_INVALID_INLINE_DATA_SIZE = -26, I40IW_ERR_TIMEOUT = -27, I40IW_ERR_OPCODE_MISMATCH = -28, I40IW_ERR_CQP_COMPL_ERROR = -29, diff --git a/drivers/infiniband/hw/i40iw/i40iw_uk.c b/drivers/infiniband/hw/i40iw/i40iw_uk.c index b0d3a0e8a9b5..1060725d18bc 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_uk.c +++ b/drivers/infiniband/hw/i40iw/i40iw_uk.c @@ -435,7 +435,7 @@ static enum i40iw_status_code i40iw_inline_rdma_write(struct i40iw_qp_uk *qp, op_info = &info->op.inline_rdma_write; if (op_info->len > I40IW_MAX_INLINE_DATA_SIZE) - return I40IW_ERR_INVALID_IMM_DATA_SIZE; + return I40IW_ERR_INVALID_INLINE_DATA_SIZE; ret_code = i40iw_inline_data_size_to_wqesize(op_info->len, &wqe_size); if (ret_code) @@ -511,7 +511,7 @@ static enum i40iw_status_code i40iw_inline_send(struct i40iw_qp_uk *qp, op_info = &info->op.inline_send; if (op_info->len > I40IW_MAX_INLINE_DATA_SIZE) - return I40IW_ERR_INVALID_IMM_DATA_SIZE; + return I40IW_ERR_INVALID_INLINE_DATA_SIZE; ret_code = i40iw_inline_data_size_to_wqesize(op_info->len, &wqe_size); if (ret_code) @@ -784,7 +784,7 @@ static enum i40iw_status_code i40iw_cq_poll_completion(struct i40iw_cq_uk *cq, get_64bit_val(cqe, 0, &qword0); get_64bit_val(cqe, 16, &qword2); - info->tcp_seq_num = (u8)RS_64(qword0, I40IWCQ_TCPSEQNUM); + info->tcp_seq_num = (u32)RS_64(qword0, I40IWCQ_TCPSEQNUM); info->qp_id = (u32)RS_64(qword2, I40IWCQ_QPID); @@ -1187,7 +1187,7 @@ enum i40iw_status_code i40iw_inline_data_size_to_wqesize(u32 data_size, u8 *wqe_size) { if (data_size > I40IW_MAX_INLINE_DATA_SIZE) - return I40IW_ERR_INVALID_IMM_DATA_SIZE; + return I40IW_ERR_INVALID_INLINE_DATA_SIZE; if (data_size <= 16) *wqe_size = I40IW_QP_WQE_MIN_SIZE; diff --git a/drivers/infiniband/hw/i40iw/i40iw_utils.c b/drivers/infiniband/hw/i40iw/i40iw_utils.c index 56d986924a4c..e311ec559f4e 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_utils.c +++ b/drivers/infiniband/hw/i40iw/i40iw_utils.c @@ -337,6 +337,7 @@ struct i40iw_cqp_request *i40iw_get_cqp_request(struct i40iw_cqp *cqp, bool wait */ void i40iw_free_cqp_request(struct i40iw_cqp *cqp, struct i40iw_cqp_request *cqp_request) { + struct i40iw_device *iwdev = container_of(cqp, struct i40iw_device, cqp); unsigned long flags; if (cqp_request->dynamic) { @@ -350,6 +351,7 @@ void i40iw_free_cqp_request(struct i40iw_cqp *cqp, struct i40iw_cqp_request *cqp list_add_tail(&cqp_request->list, &cqp->cqp_avail_reqs); spin_unlock_irqrestore(&cqp->req_lock, flags); } + wake_up(&iwdev->close_wq); } /** @@ -365,6 +367,56 @@ void i40iw_put_cqp_request(struct i40iw_cqp *cqp, } /** + * i40iw_free_pending_cqp_request -free pending cqp request objs + * @cqp: cqp ptr + * @cqp_request: to be put back in cqp list + */ +static void i40iw_free_pending_cqp_request(struct i40iw_cqp *cqp, + struct i40iw_cqp_request *cqp_request) +{ + struct i40iw_device *iwdev = container_of(cqp, struct i40iw_device, cqp); + + if (cqp_request->waiting) { + cqp_request->compl_info.error = true; + cqp_request->request_done = true; + wake_up(&cqp_request->waitq); + } + i40iw_put_cqp_request(cqp, cqp_request); + wait_event_timeout(iwdev->close_wq, + !atomic_read(&cqp_request->refcount), + 1000); +} + +/** + * i40iw_cleanup_pending_cqp_op - clean-up cqp with no completions + * @iwdev: iwarp device + */ +void i40iw_cleanup_pending_cqp_op(struct i40iw_device *iwdev) +{ + struct i40iw_sc_dev *dev = &iwdev->sc_dev; + struct i40iw_cqp *cqp = &iwdev->cqp; + struct i40iw_cqp_request *cqp_request = NULL; + struct cqp_commands_info *pcmdinfo = NULL; + u32 i, pending_work, wqe_idx; + + pending_work = I40IW_RING_WORK_AVAILABLE(cqp->sc_cqp.sq_ring); + wqe_idx = I40IW_RING_GETCURRENT_TAIL(cqp->sc_cqp.sq_ring); + for (i = 0; i < pending_work; i++) { + cqp_request = (struct i40iw_cqp_request *)(unsigned long)cqp->scratch_array[wqe_idx]; + if (cqp_request) + i40iw_free_pending_cqp_request(cqp, cqp_request); + wqe_idx = (wqe_idx + 1) % I40IW_RING_GETSIZE(cqp->sc_cqp.sq_ring); + } + + while (!list_empty(&dev->cqp_cmd_head)) { + pcmdinfo = (struct cqp_commands_info *)i40iw_remove_head(&dev->cqp_cmd_head); + cqp_request = container_of(pcmdinfo, struct i40iw_cqp_request, info); + if (cqp_request) + i40iw_free_pending_cqp_request(cqp, cqp_request); + } +} + +/** * i40iw_free_qp - callback after destroy cqp completes * @cqp_request: cqp request for destroy qp * @num: not used @@ -546,8 +598,12 @@ void i40iw_rem_ref(struct ib_qp *ibqp) cqp_info->in.u.qp_destroy.scratch = (uintptr_t)cqp_request; cqp_info->in.u.qp_destroy.remove_hash_idx = true; status = i40iw_handle_cqp_op(iwdev, cqp_request); - if (status) - i40iw_pr_err("CQP-OP Destroy QP fail"); + if (!status) + return; + + i40iw_rem_pdusecount(iwqp->iwpd, iwdev); + i40iw_free_qp_resources(iwdev, iwqp, qp_num); + i40iw_rem_devusecount(iwdev); } /** diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.c b/drivers/infiniband/hw/i40iw/i40iw_verbs.c index 4dbe61ec7a77..02d871db7ca5 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.c +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.c @@ -426,9 +426,13 @@ void i40iw_free_qp_resources(struct i40iw_device *iwdev, struct i40iw_qp *iwqp, u32 qp_num) { + struct i40iw_pbl *iwpbl = &iwqp->iwpbl; + i40iw_dealloc_push_page(iwdev, &iwqp->sc_qp); if (qp_num) i40iw_free_resource(iwdev, iwdev->allocated_qps, qp_num); + if (iwpbl->pbl_allocated) + i40iw_free_pble(iwdev->pble_rsrc, &iwpbl->pble_alloc); i40iw_free_dma_mem(iwdev->sc_dev.hw, &iwqp->q2_ctx_mem); i40iw_free_dma_mem(iwdev->sc_dev.hw, &iwqp->kqp.dma_mem); kfree(iwqp->kqp.wrid_mem); @@ -483,7 +487,7 @@ static int i40iw_setup_virt_qp(struct i40iw_device *iwdev, struct i40iw_qp *iwqp, struct i40iw_qp_init_info *init_info) { - struct i40iw_pbl *iwpbl = iwqp->iwpbl; + struct i40iw_pbl *iwpbl = &iwqp->iwpbl; struct i40iw_qp_mr *qpmr = &iwpbl->qp_mr; iwqp->page = qpmr->sq_page; @@ -688,19 +692,22 @@ static struct ib_qp *i40iw_create_qp(struct ib_pd *ibpd, ucontext = to_ucontext(ibpd->uobject->context); if (req.user_wqe_buffers) { + struct i40iw_pbl *iwpbl; + spin_lock_irqsave( &ucontext->qp_reg_mem_list_lock, flags); - iwqp->iwpbl = i40iw_get_pbl( + iwpbl = i40iw_get_pbl( (unsigned long)req.user_wqe_buffers, &ucontext->qp_reg_mem_list); spin_unlock_irqrestore( &ucontext->qp_reg_mem_list_lock, flags); - if (!iwqp->iwpbl) { + if (!iwpbl) { err_code = -ENODATA; i40iw_pr_err("no pbl info\n"); goto error; } + memcpy(&iwqp->iwpbl, iwpbl, sizeof(iwqp->iwpbl)); } } err_code = i40iw_setup_virt_qp(iwdev, iwqp, &init_info); @@ -1161,8 +1168,10 @@ static struct ib_cq *i40iw_create_cq(struct ib_device *ibdev, memset(&req, 0, sizeof(req)); iwcq->user_mode = true; ucontext = to_ucontext(context); - if (ib_copy_from_udata(&req, udata, sizeof(struct i40iw_create_cq_req))) + if (ib_copy_from_udata(&req, udata, sizeof(struct i40iw_create_cq_req))) { + err_code = -EFAULT; goto cq_free_resources; + } spin_lock_irqsave(&ucontext->cq_reg_mem_list_lock, flags); iwpbl = i40iw_get_pbl((unsigned long)req.user_cq_buffer, @@ -2063,7 +2072,7 @@ static int i40iw_dereg_mr(struct ib_mr *ib_mr) ucontext = to_ucontext(ibpd->uobject->context); i40iw_del_memlist(iwmr, ucontext); } - if (iwpbl->pbl_allocated) + if (iwpbl->pbl_allocated && iwmr->type != IW_MEMREG_TYPE_QP) i40iw_free_pble(iwdev->pble_rsrc, palloc); kfree(iwmr); return 0; diff --git a/drivers/infiniband/hw/i40iw/i40iw_verbs.h b/drivers/infiniband/hw/i40iw/i40iw_verbs.h index 07c3fec77de6..9067443cd311 100644 --- a/drivers/infiniband/hw/i40iw/i40iw_verbs.h +++ b/drivers/infiniband/hw/i40iw/i40iw_verbs.h @@ -170,7 +170,7 @@ struct i40iw_qp { struct i40iw_qp_kmode kqp; struct i40iw_dma_mem host_ctx; struct timer_list terminate_timer; - struct i40iw_pbl *iwpbl; + struct i40iw_pbl iwpbl; struct i40iw_dma_mem q2_ctx_mem; struct i40iw_dma_mem ietf_mem; struct completion sq_drained; diff --git a/drivers/infiniband/hw/mlx4/cm.c b/drivers/infiniband/hw/mlx4/cm.c index 1e6c526450d9..fedaf8260105 100644 --- a/drivers/infiniband/hw/mlx4/cm.c +++ b/drivers/infiniband/hw/mlx4/cm.c @@ -323,6 +323,9 @@ int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id mad->mad_hdr.attr_id == CM_REP_ATTR_ID || mad->mad_hdr.attr_id == CM_SIDR_REQ_ATTR_ID) { sl_cm_id = get_local_comm_id(mad); + id = id_map_get(ibdev, &pv_cm_id, slave_id, sl_cm_id); + if (id) + goto cont; id = id_map_alloc(ibdev, slave_id, sl_cm_id); if (IS_ERR(id)) { mlx4_ib_warn(ibdev, "%s: id{slave: %d, sl_cm_id: 0x%x} Failed to id_map_alloc\n", @@ -343,6 +346,7 @@ int mlx4_ib_multiplex_cm_handler(struct ib_device *ibdev, int port, int slave_id return -EINVAL; } +cont: set_local_comm_id(mad, id->pv_cm_id); if (mad->mad_hdr.attr_id == CM_DREQ_ATTR_ID) diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index 4f5a143fc0a7..ff931c580557 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -102,7 +102,7 @@ static int mlx4_ib_alloc_cq_buf(struct mlx4_ib_dev *dev, struct mlx4_ib_cq_buf * int err; err = mlx4_buf_alloc(dev->dev, nent * dev->dev->caps.cqe_size, - PAGE_SIZE * 2, &buf->buf, GFP_KERNEL); + PAGE_SIZE * 2, &buf->buf); if (err) goto out; @@ -113,7 +113,7 @@ static int mlx4_ib_alloc_cq_buf(struct mlx4_ib_dev *dev, struct mlx4_ib_cq_buf * if (err) goto err_buf; - err = mlx4_buf_write_mtt(dev->dev, &buf->mtt, &buf->buf, GFP_KERNEL); + err = mlx4_buf_write_mtt(dev->dev, &buf->mtt, &buf->buf); if (err) goto err_mtt; @@ -219,7 +219,7 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, uar = &to_mucontext(context)->uar; } else { - err = mlx4_db_alloc(dev->dev, &cq->db, 1, GFP_KERNEL); + err = mlx4_db_alloc(dev->dev, &cq->db, 1); if (err) goto err_cq; diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 75b2f7d4cd95..d1b43cbbfea7 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -1155,7 +1155,7 @@ static void mlx4_ib_disassociate_ucontext(struct ib_ucontext *ibcontext) * call to mlx4_ib_vma_close. */ put_task_struct(owning_process); - msleep(1); + usleep_range(1000, 2000); owning_process = get_pid_task(ibcontext->tgid, PIDTYPE_PID); if (!owning_process || diff --git a/drivers/infiniband/hw/mlx4/mcg.c b/drivers/infiniband/hw/mlx4/mcg.c index 3405e947dc1e..b73f89700ef9 100644 --- a/drivers/infiniband/hw/mlx4/mcg.c +++ b/drivers/infiniband/hw/mlx4/mcg.c @@ -1091,7 +1091,7 @@ static void _mlx4_ib_mcg_port_cleanup(struct mlx4_ib_demux_ctx *ctx, int destroy if (!count) break; - msleep(1); + usleep_range(1000, 2000); } while (time_after(end, jiffies)); flush_workqueue(ctx->mcg_wq); diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index c2b9cbf4da05..9db82e67e959 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -185,7 +185,6 @@ enum mlx4_ib_qp_flags { MLX4_IB_QP_LSO = IB_QP_CREATE_IPOIB_UD_LSO, MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK = IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK, MLX4_IB_QP_NETIF = IB_QP_CREATE_NETIF_QP, - MLX4_IB_QP_CREATE_USE_GFP_NOIO = IB_QP_CREATE_USE_GFP_NOIO, /* Mellanox specific flags start from IB_QP_CREATE_RESERVED_START */ MLX4_IB_ROCE_V2_GSI_QP = MLX4_IB_QP_CREATE_ROCE_V2_GSI, diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 996e9058e515..75c0e6c5dd56 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -634,8 +634,8 @@ static void mlx4_ib_free_qp_counter(struct mlx4_ib_dev *dev, static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, struct ib_qp_init_attr *init_attr, - struct ib_udata *udata, int sqpn, struct mlx4_ib_qp **caller_qp, - gfp_t gfp) + struct ib_udata *udata, int sqpn, + struct mlx4_ib_qp **caller_qp) { int qpn; int err; @@ -691,14 +691,14 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, if (qp_type == MLX4_IB_QPT_SMI || qp_type == MLX4_IB_QPT_GSI || (qp_type & (MLX4_IB_QPT_PROXY_SMI | MLX4_IB_QPT_PROXY_SMI_OWNER | MLX4_IB_QPT_PROXY_GSI | MLX4_IB_QPT_TUN_SMI_OWNER))) { - sqp = kzalloc(sizeof (struct mlx4_ib_sqp), gfp); + sqp = kzalloc(sizeof(struct mlx4_ib_sqp), GFP_KERNEL); if (!sqp) return -ENOMEM; qp = &sqp->qp; qp->pri.vid = 0xFFFF; qp->alt.vid = 0xFFFF; } else { - qp = kzalloc(sizeof (struct mlx4_ib_qp), gfp); + qp = kzalloc(sizeof(struct mlx4_ib_qp), GFP_KERNEL); if (!qp) return -ENOMEM; qp->pri.vid = 0xFFFF; @@ -780,7 +780,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, goto err; if (qp_has_rq(init_attr)) { - err = mlx4_db_alloc(dev->dev, &qp->db, 0, gfp); + err = mlx4_db_alloc(dev->dev, &qp->db, 0); if (err) goto err; @@ -788,7 +788,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, } if (mlx4_buf_alloc(dev->dev, qp->buf_size, qp->buf_size, - &qp->buf, gfp)) { + &qp->buf)) { memcpy(&init_attr->cap, &backup_cap, sizeof(backup_cap)); err = set_kernel_sq_size(dev, &init_attr->cap, qp_type, @@ -797,7 +797,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, goto err_db; if (mlx4_buf_alloc(dev->dev, qp->buf_size, - PAGE_SIZE * 2, &qp->buf, gfp)) { + PAGE_SIZE * 2, &qp->buf)) { err = -ENOMEM; goto err_db; } @@ -808,20 +808,20 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, if (err) goto err_buf; - err = mlx4_buf_write_mtt(dev->dev, &qp->mtt, &qp->buf, gfp); + err = mlx4_buf_write_mtt(dev->dev, &qp->mtt, &qp->buf); if (err) goto err_mtt; qp->sq.wrid = kmalloc_array(qp->sq.wqe_cnt, sizeof(u64), - gfp | __GFP_NOWARN); + GFP_KERNEL | __GFP_NOWARN); if (!qp->sq.wrid) qp->sq.wrid = __vmalloc(qp->sq.wqe_cnt * sizeof(u64), - gfp, PAGE_KERNEL); + GFP_KERNEL, PAGE_KERNEL); qp->rq.wrid = kmalloc_array(qp->rq.wqe_cnt, sizeof(u64), - gfp | __GFP_NOWARN); + GFP_KERNEL | __GFP_NOWARN); if (!qp->rq.wrid) qp->rq.wrid = __vmalloc(qp->rq.wqe_cnt * sizeof(u64), - gfp, PAGE_KERNEL); + GFP_KERNEL, PAGE_KERNEL); if (!qp->sq.wrid || !qp->rq.wrid) { err = -ENOMEM; goto err_wrid; @@ -859,7 +859,7 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, if (init_attr->create_flags & IB_QP_CREATE_BLOCK_MULTICAST_LOOPBACK) qp->flags |= MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK; - err = mlx4_qp_alloc(dev->dev, qpn, &qp->mqp, gfp); + err = mlx4_qp_alloc(dev->dev, qpn, &qp->mqp); if (err) goto err_qpn; @@ -1127,10 +1127,7 @@ static struct ib_qp *_mlx4_ib_create_qp(struct ib_pd *pd, int err; int sup_u_create_flags = MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK; u16 xrcdn = 0; - gfp_t gfp; - gfp = (init_attr->create_flags & MLX4_IB_QP_CREATE_USE_GFP_NOIO) ? - GFP_NOIO : GFP_KERNEL; /* * We only support LSO, vendor flag1, and multicast loopback blocking, * and only for kernel UD QPs. @@ -1140,8 +1137,7 @@ static struct ib_qp *_mlx4_ib_create_qp(struct ib_pd *pd, MLX4_IB_SRIOV_TUNNEL_QP | MLX4_IB_SRIOV_SQP | MLX4_IB_QP_NETIF | - MLX4_IB_QP_CREATE_ROCE_V2_GSI | - MLX4_IB_QP_CREATE_USE_GFP_NOIO)) + MLX4_IB_QP_CREATE_ROCE_V2_GSI)) return ERR_PTR(-EINVAL); if (init_attr->create_flags & IB_QP_CREATE_NETIF_QP) { @@ -1154,7 +1150,6 @@ static struct ib_qp *_mlx4_ib_create_qp(struct ib_pd *pd, return ERR_PTR(-EINVAL); if ((init_attr->create_flags & ~(MLX4_IB_SRIOV_SQP | - MLX4_IB_QP_CREATE_USE_GFP_NOIO | MLX4_IB_QP_CREATE_ROCE_V2_GSI | MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK) && init_attr->qp_type != IB_QPT_UD) || @@ -1179,7 +1174,7 @@ static struct ib_qp *_mlx4_ib_create_qp(struct ib_pd *pd, case IB_QPT_RC: case IB_QPT_UC: case IB_QPT_RAW_PACKET: - qp = kzalloc(sizeof *qp, gfp); + qp = kzalloc(sizeof(*qp), GFP_KERNEL); if (!qp) return ERR_PTR(-ENOMEM); qp->pri.vid = 0xFFFF; @@ -1188,7 +1183,7 @@ static struct ib_qp *_mlx4_ib_create_qp(struct ib_pd *pd, case IB_QPT_UD: { err = create_qp_common(to_mdev(pd->device), pd, init_attr, - udata, 0, &qp, gfp); + udata, 0, &qp); if (err) { kfree(qp); return ERR_PTR(err); @@ -1217,8 +1212,7 @@ static struct ib_qp *_mlx4_ib_create_qp(struct ib_pd *pd, } err = create_qp_common(to_mdev(pd->device), pd, init_attr, udata, - sqpn, - &qp, gfp); + sqpn, &qp); if (err) return ERR_PTR(err); diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c index e32dd58937a8..0facaf5f6d23 100644 --- a/drivers/infiniband/hw/mlx4/srq.c +++ b/drivers/infiniband/hw/mlx4/srq.c @@ -135,14 +135,14 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd, if (err) goto err_mtt; } else { - err = mlx4_db_alloc(dev->dev, &srq->db, 0, GFP_KERNEL); + err = mlx4_db_alloc(dev->dev, &srq->db, 0); if (err) goto err_srq; *srq->db.db = 0; - if (mlx4_buf_alloc(dev->dev, buf_size, PAGE_SIZE * 2, &srq->buf, - GFP_KERNEL)) { + if (mlx4_buf_alloc(dev->dev, buf_size, PAGE_SIZE * 2, + &srq->buf)) { err = -ENOMEM; goto err_db; } @@ -167,7 +167,7 @@ struct ib_srq *mlx4_ib_create_srq(struct ib_pd *pd, if (err) goto err_buf; - err = mlx4_buf_write_mtt(dev->dev, &srq->mtt, &srq->buf, GFP_KERNEL); + err = mlx4_buf_write_mtt(dev->dev, &srq->mtt, &srq->buf); if (err) goto err_mtt; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index a7f2e60085c4..f7fcde1ff0aa 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1085,6 +1085,12 @@ static int mlx5_ib_modify_port(struct ib_device *ibdev, u8 port, int mask, bool is_ib = (mlx5_ib_port_link_layer(ibdev, port) == IB_LINK_LAYER_INFINIBAND); + /* CM layer calls ib_modify_port() regardless of the link layer. For + * Ethernet ports, qkey violation and Port capabilities are meaningless. + */ + if (!is_ib) + return 0; + if (MLX5_CAP_GEN(dev->mdev, ib_virt) && is_ib) { change_mask = props->clr_port_cap_mask | props->set_port_cap_mask; value = ~props->clr_port_cap_mask | props->set_port_cap_mask; diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index 763bb5b36144..2c40a2e989d2 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -582,6 +582,15 @@ static void clean_keys(struct mlx5_ib_dev *dev, int c) } } +static void mlx5_mr_cache_debugfs_cleanup(struct mlx5_ib_dev *dev) +{ + if (!mlx5_debugfs_root) + return; + + debugfs_remove_recursive(dev->cache.root); + dev->cache.root = NULL; +} + static int mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev) { struct mlx5_mr_cache *cache = &dev->cache; @@ -600,38 +609,34 @@ static int mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev) sprintf(ent->name, "%d", ent->order); ent->dir = debugfs_create_dir(ent->name, cache->root); if (!ent->dir) - return -ENOMEM; + goto err; ent->fsize = debugfs_create_file("size", 0600, ent->dir, ent, &size_fops); if (!ent->fsize) - return -ENOMEM; + goto err; ent->flimit = debugfs_create_file("limit", 0600, ent->dir, ent, &limit_fops); if (!ent->flimit) - return -ENOMEM; + goto err; ent->fcur = debugfs_create_u32("cur", 0400, ent->dir, &ent->cur); if (!ent->fcur) - return -ENOMEM; + goto err; ent->fmiss = debugfs_create_u32("miss", 0600, ent->dir, &ent->miss); if (!ent->fmiss) - return -ENOMEM; + goto err; } return 0; -} - -static void mlx5_mr_cache_debugfs_cleanup(struct mlx5_ib_dev *dev) -{ - if (!mlx5_debugfs_root) - return; +err: + mlx5_mr_cache_debugfs_cleanup(dev); - debugfs_remove_recursive(dev->cache.root); + return -ENOMEM; } static void delay_time_func(unsigned long ctx) @@ -692,6 +697,11 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) if (err) mlx5_ib_warn(dev, "cache debugfs failure\n"); + /* + * We don't want to fail driver if debugfs failed to initialize, + * so we are not forwarding error to the user. + */ + return 0; } @@ -825,7 +835,7 @@ static int mr_umem_get(struct ib_pd *pd, u64 start, u64 length, access_flags, 0); err = PTR_ERR_OR_ZERO(*umem); if (err < 0) { - mlx5_ib_err(dev, "umem get failed (%ld)\n", PTR_ERR(umem)); + mlx5_ib_err(dev, "umem get failed (%d)\n", err); return err; } @@ -1779,7 +1789,7 @@ mlx5_ib_sg_to_klms(struct mlx5_ib_mr *mr, mr->ndescs = sg_nents; for_each_sg(sgl, sg, sg_nents, i) { - if (unlikely(i > mr->max_descs)) + if (unlikely(i >= mr->max_descs)) break; klms[i].va = cpu_to_be64(sg_dma_address(sg) + sg_offset); klms[i].bcount = cpu_to_be32(sg_dma_len(sg) - sg_offset); diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index ae0746754008..3d701c7a4c91 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -939,7 +939,7 @@ static int mlx5_ib_mr_initiator_pfault_handler( if (qp->ibqp.qp_type != IB_QPT_RC) { av = *wqe; - if (av->dqp_dct & be32_to_cpu(MLX5_WQE_AV_EXT)) + if (av->dqp_dct & cpu_to_be32(MLX5_EXTENDED_UD_AV)) *wqe += sizeof(struct mlx5_av); else *wqe += sizeof(struct mlx5_base_av); diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c index 0889ff367c86..f58f8f5f3ebe 100644 --- a/drivers/infiniband/hw/mlx5/qp.c +++ b/drivers/infiniband/hw/mlx5/qp.c @@ -1238,6 +1238,7 @@ static int create_raw_packet_qp(struct mlx5_ib_dev *dev, struct mlx5_ib_qp *qp, goto err_destroy_tis; sq->base.container_mibqp = qp; + sq->base.mqp.event = mlx5_ib_qp_event; } if (qp->rq.wqe_cnt) { diff --git a/drivers/infiniband/hw/nes/nes_hw.c b/drivers/infiniband/hw/nes/nes_hw.c index 8f9d8b4ad583..b0adf65e4bdb 100644 --- a/drivers/infiniband/hw/nes/nes_hw.c +++ b/drivers/infiniband/hw/nes/nes_hw.c @@ -551,7 +551,7 @@ struct nes_adapter *nes_init_adapter(struct nes_device *nesdev, u8 hw_rev) { if ((0x0F000100 == (pcs_control_status0 & 0x0F000100)) || (0x0F000100 == (pcs_control_status1 & 0x0F000100))) int_cnt++; - msleep(1); + usleep_range(1000, 2000); } if (int_cnt > 1) { spin_lock_irqsave(&nesadapter->phy_lock, flags); @@ -592,7 +592,7 @@ struct nes_adapter *nes_init_adapter(struct nes_device *nesdev, u8 hw_rev) { break; } } - msleep(1); + usleep_range(1000, 2000); } } } diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c index 2f30bda8457a..27d5e8d9f08d 100644 --- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c +++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c @@ -744,7 +744,8 @@ err: if (is_uctx_pd) { ocrdma_release_ucontext_pd(uctx); } else { - status = _ocrdma_dealloc_pd(dev, pd); + if (_ocrdma_dealloc_pd(dev, pd)) + pr_err("%s: _ocrdma_dealloc_pd() failed\n", __func__); } exit: return ERR_PTR(status); @@ -1901,6 +1902,7 @@ struct ib_srq *ocrdma_create_srq(struct ib_pd *ibpd, goto err; if (udata == NULL) { + status = -ENOMEM; srq->rqe_wr_id_tbl = kzalloc(sizeof(u64) * srq->rq.max_cnt, GFP_KERNEL); if (srq->rqe_wr_id_tbl == NULL) diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index 548e4d1e998f..2ae71b8f1ba8 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -53,6 +53,14 @@ #define DB_ADDR_SHIFT(addr) ((addr) << DB_PWM_ADDR_OFFSET_SHIFT) +static inline int qedr_ib_copy_to_udata(struct ib_udata *udata, void *src, + size_t len) +{ + size_t min_len = min_t(size_t, len, udata->outlen); + + return ib_copy_to_udata(udata, src, min_len); +} + int qedr_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey) { if (index > QEDR_ROCE_PKEY_TABLE_LEN) @@ -378,7 +386,7 @@ struct ib_ucontext *qedr_alloc_ucontext(struct ib_device *ibdev, uresp.sges_per_srq_wr = dev->attr.max_srq_sge; uresp.max_cqes = QEDR_MAX_CQES; - rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp)); + rc = qedr_ib_copy_to_udata(udata, &uresp, sizeof(uresp)); if (rc) goto err; @@ -499,7 +507,7 @@ struct ib_pd *qedr_alloc_pd(struct ib_device *ibdev, uresp.pd_id = pd_id; - rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp)); + rc = qedr_ib_copy_to_udata(udata, &uresp, sizeof(uresp)); if (rc) { DP_ERR(dev, "copy error pd_id=0x%x.\n", pd_id); dev->ops->rdma_dealloc_pd(dev->rdma_ctx, pd_id); @@ -729,7 +737,7 @@ static int qedr_copy_cq_uresp(struct qedr_dev *dev, uresp.db_offset = DB_ADDR_SHIFT(DQ_PWM_OFFSET_UCM_RDMA_CQ_CONS_32BIT); uresp.icid = cq->icid; - rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp)); + rc = qedr_ib_copy_to_udata(udata, &uresp, sizeof(uresp)); if (rc) DP_ERR(dev, "copy error cqid=0x%x.\n", cq->icid); @@ -1238,7 +1246,7 @@ static int qedr_copy_qp_uresp(struct qedr_dev *dev, uresp.atomic_supported = dev->atomic_cap != IB_ATOMIC_NONE; uresp.qp_id = qp->qp_id; - rc = ib_copy_to_udata(udata, &uresp, sizeof(uresp)); + rc = qedr_ib_copy_to_udata(udata, &uresp, sizeof(uresp)); if (rc) DP_ERR(dev, "create qp: failed a copy to user space with qp icid=0x%x.\n", diff --git a/drivers/infiniband/hw/qib/qib_qp.c b/drivers/infiniband/hw/qib/qib_qp.c index 5984981e7dd4..a343e3b5d4cb 100644 --- a/drivers/infiniband/hw/qib/qib_qp.c +++ b/drivers/infiniband/hw/qib/qib_qp.c @@ -104,10 +104,9 @@ const struct rvt_operation_params qib_post_parms[RVT_OPERATION_MAX] = { }; -static void get_map_page(struct rvt_qpn_table *qpt, struct rvt_qpn_map *map, - gfp_t gfp) +static void get_map_page(struct rvt_qpn_table *qpt, struct rvt_qpn_map *map) { - unsigned long page = get_zeroed_page(gfp); + unsigned long page = get_zeroed_page(GFP_KERNEL); /* * Free the page if someone raced with us installing it. @@ -126,7 +125,7 @@ static void get_map_page(struct rvt_qpn_table *qpt, struct rvt_qpn_map *map, * zero/one for QP type IB_QPT_SMI/IB_QPT_GSI. */ int qib_alloc_qpn(struct rvt_dev_info *rdi, struct rvt_qpn_table *qpt, - enum ib_qp_type type, u8 port, gfp_t gfp) + enum ib_qp_type type, u8 port) { u32 i, offset, max_scan, qpn; struct rvt_qpn_map *map; @@ -160,7 +159,7 @@ int qib_alloc_qpn(struct rvt_dev_info *rdi, struct rvt_qpn_table *qpt, max_scan = qpt->nmaps - !offset; for (i = 0;;) { if (unlikely(!map->page)) { - get_map_page(qpt, map, gfp); + get_map_page(qpt, map); if (unlikely(!map->page)) break; } @@ -317,16 +316,16 @@ u32 qib_mtu_from_qp(struct rvt_dev_info *rdi, struct rvt_qp *qp, u32 pmtu) return ib_mtu_enum_to_int(pmtu); } -void *qib_qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp, gfp_t gfp) +void *qib_qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp) { struct qib_qp_priv *priv; - priv = kzalloc(sizeof(*priv), gfp); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return ERR_PTR(-ENOMEM); priv->owner = qp; - priv->s_hdr = kzalloc(sizeof(*priv->s_hdr), gfp); + priv->s_hdr = kzalloc(sizeof(*priv->s_hdr), GFP_KERNEL); if (!priv->s_hdr) { kfree(priv); return ERR_PTR(-ENOMEM); diff --git a/drivers/infiniband/hw/qib/qib_verbs.h b/drivers/infiniband/hw/qib/qib_verbs.h index da0db5485ddc..a52fc67b40d7 100644 --- a/drivers/infiniband/hw/qib/qib_verbs.h +++ b/drivers/infiniband/hw/qib/qib_verbs.h @@ -274,11 +274,11 @@ int qib_get_counters(struct qib_pportdata *ppd, * Functions provided by qib driver for rdmavt to use */ unsigned qib_free_all_qps(struct rvt_dev_info *rdi); -void *qib_qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp, gfp_t gfp); +void *qib_qp_priv_alloc(struct rvt_dev_info *rdi, struct rvt_qp *qp); void qib_qp_priv_free(struct rvt_dev_info *rdi, struct rvt_qp *qp); void qib_notify_qp_reset(struct rvt_qp *qp); int qib_alloc_qpn(struct rvt_dev_info *rdi, struct rvt_qpn_table *qpt, - enum ib_qp_type type, u8 port, gfp_t gfp); + enum ib_qp_type type, u8 port); void qib_restart_rc(struct rvt_qp *qp, u32 psn, int wait); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c index 69bda611d313..90aa326fd7c0 100644 --- a/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c +++ b/drivers/infiniband/hw/vmw_pvrdma/pvrdma_cq.c @@ -65,13 +65,28 @@ int pvrdma_req_notify_cq(struct ib_cq *ibcq, struct pvrdma_dev *dev = to_vdev(ibcq->device); struct pvrdma_cq *cq = to_vcq(ibcq); u32 val = cq->cq_handle; + unsigned long flags; + int has_data = 0; val |= (notify_flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED ? PVRDMA_UAR_CQ_ARM_SOL : PVRDMA_UAR_CQ_ARM; + spin_lock_irqsave(&cq->cq_lock, flags); + pvrdma_write_uar_cq(dev, val); - return 0; + if (notify_flags & IB_CQ_REPORT_MISSED_EVENTS) { + unsigned int head; + + has_data = pvrdma_idx_ring_has_data(&cq->ring_state->rx, + cq->ibcq.cqe, &head); + if (unlikely(has_data == PVRDMA_INVALID_IDX)) + dev_err(&dev->pdev->dev, "CQ ring state invalid\n"); + } + + spin_unlock_irqrestore(&cq->cq_lock, flags); + + return has_data; } /** diff --git a/drivers/infiniband/sw/rdmavt/qp.c b/drivers/infiniband/sw/rdmavt/qp.c index 727e81cc2c8f..8876ee7bc326 100644 --- a/drivers/infiniband/sw/rdmavt/qp.c +++ b/drivers/infiniband/sw/rdmavt/qp.c @@ -118,10 +118,9 @@ const int ib_rvt_state_ops[IB_QPS_ERR + 1] = { EXPORT_SYMBOL(ib_rvt_state_ops); static void get_map_page(struct rvt_qpn_table *qpt, - struct rvt_qpn_map *map, - gfp_t gfp) + struct rvt_qpn_map *map) { - unsigned long page = get_zeroed_page(gfp); + unsigned long page = get_zeroed_page(GFP_KERNEL); /* * Free the page if someone raced with us installing it. @@ -173,7 +172,7 @@ static int init_qpn_table(struct rvt_dev_info *rdi, struct rvt_qpn_table *qpt) rdi->dparms.qpn_res_start, rdi->dparms.qpn_res_end); for (i = rdi->dparms.qpn_res_start; i <= rdi->dparms.qpn_res_end; i++) { if (!map->page) { - get_map_page(qpt, map, GFP_KERNEL); + get_map_page(qpt, map); if (!map->page) { ret = -ENOMEM; break; @@ -342,14 +341,14 @@ static inline unsigned mk_qpn(struct rvt_qpn_table *qpt, * Return: The queue pair number */ static int alloc_qpn(struct rvt_dev_info *rdi, struct rvt_qpn_table *qpt, - enum ib_qp_type type, u8 port_num, gfp_t gfp) + enum ib_qp_type type, u8 port_num) { u32 i, offset, max_scan, qpn; struct rvt_qpn_map *map; u32 ret; if (rdi->driver_f.alloc_qpn) - return rdi->driver_f.alloc_qpn(rdi, qpt, type, port_num, gfp); + return rdi->driver_f.alloc_qpn(rdi, qpt, type, port_num); if (type == IB_QPT_SMI || type == IB_QPT_GSI) { unsigned n; @@ -374,7 +373,7 @@ static int alloc_qpn(struct rvt_dev_info *rdi, struct rvt_qpn_table *qpt, max_scan = qpt->nmaps - !offset; for (i = 0;;) { if (unlikely(!map->page)) { - get_map_page(qpt, map, gfp); + get_map_page(qpt, map); if (unlikely(!map->page)) break; } @@ -672,7 +671,6 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, struct ib_qp *ret = ERR_PTR(-ENOMEM); struct rvt_dev_info *rdi = ib_to_rvt(ibpd->device); void *priv = NULL; - gfp_t gfp; size_t sqsize; if (!rdi) @@ -680,18 +678,9 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, if (init_attr->cap.max_send_sge > rdi->dparms.props.max_sge || init_attr->cap.max_send_wr > rdi->dparms.props.max_qp_wr || - init_attr->create_flags & ~(IB_QP_CREATE_USE_GFP_NOIO)) + init_attr->create_flags) return ERR_PTR(-EINVAL); - /* GFP_NOIO is applicable to RC QP's only */ - - if (init_attr->create_flags & IB_QP_CREATE_USE_GFP_NOIO && - init_attr->qp_type != IB_QPT_RC) - return ERR_PTR(-EINVAL); - - gfp = init_attr->create_flags & IB_QP_CREATE_USE_GFP_NOIO ? - GFP_NOIO : GFP_KERNEL; - /* Check receive queue parameters if no SRQ is specified. */ if (!init_attr->srq) { if (init_attr->cap.max_recv_sge > rdi->dparms.props.max_sge || @@ -719,14 +708,7 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, sz = sizeof(struct rvt_sge) * init_attr->cap.max_send_sge + sizeof(struct rvt_swqe); - if (gfp == GFP_NOIO) - swq = __vmalloc( - sqsize * sz, - gfp | __GFP_ZERO, PAGE_KERNEL); - else - swq = vzalloc_node( - sqsize * sz, - rdi->dparms.node); + swq = vzalloc_node(sqsize * sz, rdi->dparms.node); if (!swq) return ERR_PTR(-ENOMEM); @@ -741,7 +723,8 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, } else if (init_attr->cap.max_recv_sge > 1) sg_list_sz = sizeof(*qp->r_sg_list) * (init_attr->cap.max_recv_sge - 1); - qp = kzalloc_node(sz + sg_list_sz, gfp, rdi->dparms.node); + qp = kzalloc_node(sz + sg_list_sz, GFP_KERNEL, + rdi->dparms.node); if (!qp) goto bail_swq; @@ -751,7 +734,7 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, kzalloc_node( sizeof(*qp->s_ack_queue) * rvt_max_atomic(rdi), - gfp, + GFP_KERNEL, rdi->dparms.node); if (!qp->s_ack_queue) goto bail_qp; @@ -766,7 +749,7 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, * Driver needs to set up it's private QP structure and do any * initialization that is needed. */ - priv = rdi->driver_f.qp_priv_alloc(rdi, qp, gfp); + priv = rdi->driver_f.qp_priv_alloc(rdi, qp); if (IS_ERR(priv)) { ret = priv; goto bail_qp; @@ -786,11 +769,6 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, qp->r_rq.wq = vmalloc_user( sizeof(struct rvt_rwq) + qp->r_rq.size * sz); - else if (gfp == GFP_NOIO) - qp->r_rq.wq = __vmalloc( - sizeof(struct rvt_rwq) + - qp->r_rq.size * sz, - gfp | __GFP_ZERO, PAGE_KERNEL); else qp->r_rq.wq = vzalloc_node( sizeof(struct rvt_rwq) + @@ -824,7 +802,7 @@ struct ib_qp *rvt_create_qp(struct ib_pd *ibpd, err = alloc_qpn(rdi, &rdi->qp_dev->qpn_table, init_attr->qp_type, - init_attr->port_num, gfp); + init_attr->port_num); if (err < 0) { ret = ERR_PTR(err); goto bail_rq_wq; @@ -1280,9 +1258,7 @@ int rvt_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, if (attr_mask & IB_QP_TIMEOUT) { qp->timeout = attr->timeout; - qp->timeout_jiffies = - usecs_to_jiffies((4096UL * (1UL << qp->timeout)) / - 1000UL); + qp->timeout_jiffies = rvt_timeout_to_jiffies(qp->timeout); } if (attr_mask & IB_QP_QKEY) diff --git a/drivers/infiniband/sw/rxe/rxe_net.c b/drivers/infiniband/sw/rxe/rxe_net.c index c3a140ed4df2..08f3f90d2912 100644 --- a/drivers/infiniband/sw/rxe/rxe_net.c +++ b/drivers/infiniband/sw/rxe/rxe_net.c @@ -441,6 +441,8 @@ static void rxe_skb_tx_dtor(struct sk_buff *skb) if (unlikely(qp->need_req_skb && skb_out < RXE_INFLIGHT_SKBS_PER_QP_LOW)) rxe_run_task(&qp->req.task, 1); + + rxe_drop_ref(qp); } int rxe_send(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, struct sk_buff *skb) @@ -473,6 +475,7 @@ int rxe_send(struct rxe_dev *rxe, struct rxe_pkt_info *pkt, struct sk_buff *skb) return -EAGAIN; } + rxe_add_ref(pkt->qp); atomic_inc(&pkt->qp->skb_out); kfree_skb(skb); diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index be944d5aa9af..a958ee918a49 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -1219,6 +1219,9 @@ void rxe_drain_req_pkts(struct rxe_qp *qp, bool notify) kfree_skb(skb); } + if (notify) + return; + while (!qp->srq && qp->rq.queue && queue_head(qp->rq.queue)) advance_consumer(qp->rq.queue); } diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 073e66783f1d..af90a7d42b96 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -914,6 +914,9 @@ static int rxe_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr, spin_unlock_irqrestore(&rq->producer_lock, flags); + if (qp->resp.state == QP_STATE_ERROR) + rxe_run_task(&qp->resp.task, 1); + err1: return err; } @@ -1240,6 +1243,8 @@ int rxe_register_device(struct rxe_dev *rxe) addrconf_addr_eui48((unsigned char *)&dev->node_guid, rxe->ndev->dev_addr); dev->dev.dma_ops = &dma_virt_ops; + dma_coerce_mask_and_coherent(&dev->dev, + dma_get_required_mask(dev->dev.parent)); dev->uverbs_abi_ver = RXE_UVERBS_ABI_VERSION; dev->uverbs_cmd_mask = BIT_ULL(IB_USER_VERBS_CMD_GET_CONTEXT) diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index ff50a7bd66d8..7ac25059c40f 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -336,6 +336,7 @@ struct ipoib_dev_priv { unsigned long flags; struct rw_semaphore vlan_rwsem; + struct mutex mcast_mutex; struct rb_root path_tree; struct list_head path_list; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 7cbcfdac6529..d69410c2ed97 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -39,6 +39,7 @@ #include <linux/vmalloc.h> #include <linux/moduleparam.h> #include <linux/sched/signal.h> +#include <linux/sched/mm.h> #include "ipoib.h" @@ -510,7 +511,6 @@ static int ipoib_cm_rx_handler(struct ib_cm_id *cm_id, case IB_CM_REQ_RECEIVED: return ipoib_cm_req_handler(cm_id, event); case IB_CM_DREQ_RECEIVED: - p = cm_id->context; ib_send_cm_drep(cm_id, NULL, 0); /* Fall through */ case IB_CM_REJ_RECEIVED: @@ -954,7 +954,7 @@ void ipoib_cm_dev_stop(struct net_device *dev) break; } spin_unlock_irq(&priv->lock); - msleep(1); + usleep_range(1000, 2000); ipoib_drain_cq(dev); spin_lock_irq(&priv->lock); } @@ -1047,9 +1047,8 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_ .sq_sig_type = IB_SIGNAL_ALL_WR, .qp_type = IB_QPT_RC, .qp_context = tx, - .create_flags = IB_QP_CREATE_USE_GFP_NOIO + .create_flags = 0 }; - struct ib_qp *tx_qp; if (dev->features & NETIF_F_SG) @@ -1057,10 +1056,6 @@ static struct ib_qp *ipoib_cm_create_tx_qp(struct net_device *dev, struct ipoib_ min_t(u32, priv->ca->attrs.max_sge, MAX_SKB_FRAGS + 1); tx_qp = ib_create_qp(priv->pd, &attr); - if (PTR_ERR(tx_qp) == -EINVAL) { - attr.create_flags &= ~IB_QP_CREATE_USE_GFP_NOIO; - tx_qp = ib_create_qp(priv->pd, &attr); - } tx->max_send_sge = attr.cap.max_send_sge; return tx_qp; } @@ -1131,10 +1126,11 @@ static int ipoib_cm_tx_init(struct ipoib_cm_tx *p, u32 qpn, struct sa_path_rec *pathrec) { struct ipoib_dev_priv *priv = ipoib_priv(p->dev); + unsigned int noio_flag; int ret; - p->tx_ring = __vmalloc(ipoib_sendq_size * sizeof *p->tx_ring, - GFP_NOIO, PAGE_KERNEL); + noio_flag = memalloc_noio_save(); + p->tx_ring = vzalloc(ipoib_sendq_size * sizeof(*p->tx_ring)); if (!p->tx_ring) { ret = -ENOMEM; goto err_tx; @@ -1142,9 +1138,10 @@ static int ipoib_cm_tx_init(struct ipoib_cm_tx *p, u32 qpn, memset(p->tx_ring, 0, ipoib_sendq_size * sizeof *p->tx_ring); p->qp = ipoib_cm_create_tx_qp(p->dev, p); + memalloc_noio_restore(noio_flag); if (IS_ERR(p->qp)) { ret = PTR_ERR(p->qp); - ipoib_warn(priv, "failed to allocate tx qp: %d\n", ret); + ipoib_warn(priv, "failed to create tx qp: %d\n", ret); goto err_qp; } @@ -1206,7 +1203,7 @@ static void ipoib_cm_tx_destroy(struct ipoib_cm_tx *p) goto timeout; } - msleep(1); + usleep_range(1000, 2000); } } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c index 7871379342f4..184a22f48027 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c @@ -52,7 +52,8 @@ static const struct ipoib_stats ipoib_gstrings_stats[] = { IPOIB_NETDEV_STAT(tx_bytes), IPOIB_NETDEV_STAT(tx_errors), IPOIB_NETDEV_STAT(rx_dropped), - IPOIB_NETDEV_STAT(tx_dropped) + IPOIB_NETDEV_STAT(tx_dropped), + IPOIB_NETDEV_STAT(multicast), }; #define IPOIB_GLOBAL_STATS_LEN ARRAY_SIZE(ipoib_gstrings_stats) diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index efe7402f4885..2e075377242e 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -256,6 +256,8 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) ++dev->stats.rx_packets; dev->stats.rx_bytes += skb->len; + if (skb->pkt_type == PACKET_MULTICAST) + dev->stats.multicast++; skb->dev = dev; if ((dev->features & NETIF_F_RXCSUM) && @@ -709,6 +711,27 @@ static int recvs_pending(struct net_device *dev) return pending; } +static void check_qp_movement_and_print(struct ipoib_dev_priv *priv, + struct ib_qp *qp, + enum ib_qp_state new_state) +{ + struct ib_qp_attr qp_attr; + struct ib_qp_init_attr query_init_attr; + int ret; + + ret = ib_query_qp(qp, &qp_attr, IB_QP_STATE, &query_init_attr); + if (ret) { + ipoib_warn(priv, "%s: Failed to query QP\n", __func__); + return; + } + /* print according to the new-state and the previous state.*/ + if (new_state == IB_QPS_ERR && qp_attr.qp_state == IB_QPS_RESET) + ipoib_dbg(priv, "Failed modify QP, IB_QPS_RESET to IB_QPS_ERR, acceptable\n"); + else + ipoib_warn(priv, "Failed to modify QP to state: %d from state: %d\n", + new_state, qp_attr.qp_state); +} + int ipoib_ib_dev_stop_default(struct net_device *dev) { struct ipoib_dev_priv *priv = ipoib_priv(dev); @@ -728,7 +751,7 @@ int ipoib_ib_dev_stop_default(struct net_device *dev) */ qp_attr.qp_state = IB_QPS_ERR; if (ib_modify_qp(priv->qp, &qp_attr, IB_QP_STATE)) - ipoib_warn(priv, "Failed to modify QP to ERROR state\n"); + check_qp_movement_and_print(priv, priv->qp, IB_QPS_ERR); /* Wait for all sends and receives to complete */ begin = jiffies; @@ -770,7 +793,7 @@ int ipoib_ib_dev_stop_default(struct net_device *dev) ipoib_drain_cq(dev); - msleep(1); + usleep_range(1000, 2000); } ipoib_dbg(priv, "All sends and receives done.\n"); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 6e86eeee370e..6c77df34869d 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -233,6 +233,7 @@ static netdev_features_t ipoib_fix_features(struct net_device *dev, netdev_featu static int ipoib_change_mtu(struct net_device *dev, int new_mtu) { struct ipoib_dev_priv *priv = ipoib_priv(dev); + int ret = 0; /* dev->mtu > 2K ==> connected mode */ if (ipoib_cm_admin_enabled(dev)) { @@ -256,9 +257,34 @@ static int ipoib_change_mtu(struct net_device *dev, int new_mtu) ipoib_dbg(priv, "MTU must be smaller than the underlying " "link layer MTU - 4 (%u)\n", priv->mcast_mtu); - dev->mtu = min(priv->mcast_mtu, priv->admin_mtu); + new_mtu = min(priv->mcast_mtu, priv->admin_mtu); - return 0; + if (priv->rn_ops->ndo_change_mtu) { + bool carrier_status = netif_carrier_ok(dev); + + netif_carrier_off(dev); + + /* notify lower level on the real mtu */ + ret = priv->rn_ops->ndo_change_mtu(dev, new_mtu); + + if (carrier_status) + netif_carrier_on(dev); + } else { + dev->mtu = new_mtu; + } + + return ret; +} + +static void ipoib_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct ipoib_dev_priv *priv = ipoib_priv(dev); + + if (priv->rn_ops->ndo_get_stats64) + priv->rn_ops->ndo_get_stats64(dev, stats); + else + netdev_stats_to_stats64(stats, &dev->stats); } /* Called with an RCU read lock taken */ @@ -1534,6 +1560,7 @@ static void ipoib_flush_neighs(struct ipoib_dev_priv *priv) int i, wait_flushed = 0; init_completion(&priv->ntbl.flushed); + set_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags); spin_lock_irqsave(&priv->lock, flags); @@ -1578,7 +1605,6 @@ static void ipoib_neigh_hash_uninit(struct net_device *dev) ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n"); init_completion(&priv->ntbl.deleted); - set_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags); /* Stop GC if called at init fail need to cancel work */ stopped = test_and_set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); @@ -1808,6 +1834,7 @@ static const struct net_device_ops ipoib_netdev_ops_pf = { .ndo_get_vf_stats = ipoib_get_vf_stats, .ndo_set_vf_guid = ipoib_set_vf_guid, .ndo_set_mac_address = ipoib_set_mac, + .ndo_get_stats64 = ipoib_get_stats, }; static const struct net_device_ops ipoib_netdev_ops_vf = { @@ -1820,6 +1847,7 @@ static const struct net_device_ops ipoib_netdev_ops_vf = { .ndo_tx_timeout = ipoib_timeout, .ndo_set_rx_mode = ipoib_set_mcast_list, .ndo_get_iflink = ipoib_get_iflink, + .ndo_get_stats64 = ipoib_get_stats, }; void ipoib_setup_common(struct net_device *dev) @@ -1850,6 +1878,7 @@ static void ipoib_build_priv(struct net_device *dev) priv->dev = dev; spin_lock_init(&priv->lock); init_rwsem(&priv->vlan_rwsem); + mutex_init(&priv->mcast_mutex); INIT_LIST_HEAD(&priv->path_list); INIT_LIST_HEAD(&priv->child_intfs); @@ -2146,14 +2175,14 @@ static struct net_device *ipoib_add_port(const char *format, priv->dev->dev_id = port - 1; result = ib_query_port(hca, port, &attr); - if (!result) - priv->max_ib_mtu = ib_mtu_enum_to_int(attr.max_mtu); - else { + if (result) { printk(KERN_WARNING "%s: ib_query_port %d failed\n", hca->name, port); goto device_init_failed; } + priv->max_ib_mtu = ib_mtu_enum_to_int(attr.max_mtu); + /* MTU will be reset when mcast join happens */ priv->dev->mtu = IPOIB_UD_MTU(priv->max_ib_mtu); priv->mcast_mtu = priv->admin_mtu = priv->dev->mtu; @@ -2184,12 +2213,14 @@ static struct net_device *ipoib_add_port(const char *format, printk(KERN_WARNING "%s: ib_query_gid port %d failed (ret = %d)\n", hca->name, port, result); goto device_init_failed; - } else - memcpy(priv->dev->dev_addr + 4, priv->local_gid.raw, sizeof (union ib_gid)); + } + + memcpy(priv->dev->dev_addr + 4, priv->local_gid.raw, + sizeof(union ib_gid)); set_bit(IPOIB_FLAG_DEV_ADDR_SET, &priv->flags); result = ipoib_dev_init(priv->dev, hca, port); - if (result < 0) { + if (result) { printk(KERN_WARNING "%s: failed to initialize port %d (ret = %d)\n", hca->name, port, result); goto device_init_failed; @@ -2212,6 +2243,7 @@ static struct net_device *ipoib_add_port(const char *format, goto register_failed; } + result = -ENOMEM; if (ipoib_cm_add_mode_attr(priv->dev)) goto sysfs_failed; if (ipoib_add_pkey_attr(priv->dev)) @@ -2337,6 +2369,7 @@ static int __init ipoib_init_module(void) ipoib_sendq_size = max3(ipoib_sendq_size, 2 * MAX_SEND_CQE, IPOIB_MIN_QUEUE_SIZE); #ifdef CONFIG_INFINIBAND_IPOIB_CM ipoib_max_conn_qp = min(ipoib_max_conn_qp, IPOIB_CM_MAX_CONN_QP); + ipoib_max_conn_qp = max(ipoib_max_conn_qp, 0); #endif /* diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index 057f58e6afca..93e149efc1f5 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -684,15 +684,10 @@ void ipoib_mcast_start_thread(struct net_device *dev) int ipoib_mcast_stop_thread(struct net_device *dev) { struct ipoib_dev_priv *priv = ipoib_priv(dev); - unsigned long flags; ipoib_dbg_mcast(priv, "stopping multicast thread\n"); - spin_lock_irqsave(&priv->lock, flags); - cancel_delayed_work(&priv->mcast_task); - spin_unlock_irqrestore(&priv->lock, flags); - - flush_workqueue(priv->wq); + cancel_delayed_work_sync(&priv->mcast_task); return 0; } @@ -748,6 +743,14 @@ void ipoib_mcast_remove_list(struct list_head *remove_list) { struct ipoib_mcast *mcast, *tmcast; + /* + * make sure the in-flight joins have finished before we attempt + * to leave + */ + list_for_each_entry_safe(mcast, tmcast, remove_list, list) + if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) + wait_for_completion(&mcast->done); + list_for_each_entry_safe(mcast, tmcast, remove_list, list) { ipoib_mcast_leave(mcast->dev, mcast); ipoib_mcast_free(mcast); @@ -838,6 +841,7 @@ void ipoib_mcast_dev_flush(struct net_device *dev) struct ipoib_mcast *mcast, *tmcast; unsigned long flags; + mutex_lock(&priv->mcast_mutex); ipoib_dbg_mcast(priv, "flushing multicast list\n"); spin_lock_irqsave(&priv->lock, flags); @@ -856,15 +860,8 @@ void ipoib_mcast_dev_flush(struct net_device *dev) spin_unlock_irqrestore(&priv->lock, flags); - /* - * make sure the in-flight joins have finished before we attempt - * to leave - */ - list_for_each_entry_safe(mcast, tmcast, &remove_list, list) - if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) - wait_for_completion(&mcast->done); - ipoib_mcast_remove_list(&remove_list); + mutex_unlock(&priv->mcast_mutex); } static int ipoib_mcast_addr_is_valid(const u8 *addr, const u8 *broadcast) @@ -982,14 +979,6 @@ void ipoib_mcast_restart_task(struct work_struct *work) netif_addr_unlock(dev); local_irq_restore(flags); - /* - * make sure the in-flight joins have finished before we attempt - * to leave - */ - list_for_each_entry_safe(mcast, tmcast, &remove_list, list) - if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) - wait_for_completion(&mcast->done); - ipoib_mcast_remove_list(&remove_list); /* diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 5a887efb4bdf..37b33d708c2d 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -83,6 +83,7 @@ static struct scsi_host_template iscsi_iser_sht; static struct iscsi_transport iscsi_iser_transport; static struct scsi_transport_template *iscsi_iser_scsi_transport; static struct workqueue_struct *release_wq; +static DEFINE_MUTEX(unbind_iser_conn_mutex); struct iser_global ig; int iser_debug_level = 0; @@ -550,12 +551,14 @@ iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) */ if (iser_conn) { mutex_lock(&iser_conn->state_mutex); + mutex_lock(&unbind_iser_conn_mutex); iser_conn_terminate(iser_conn); iscsi_conn_stop(cls_conn, flag); /* unbind */ iser_conn->iscsi_conn = NULL; conn->dd_data = NULL; + mutex_unlock(&unbind_iser_conn_mutex); complete(&iser_conn->stop_completion); mutex_unlock(&iser_conn->state_mutex); @@ -977,13 +980,21 @@ static int iscsi_iser_slave_alloc(struct scsi_device *sdev) struct iser_conn *iser_conn; struct ib_device *ib_dev; + mutex_lock(&unbind_iser_conn_mutex); + session = starget_to_session(scsi_target(sdev))->dd_data; iser_conn = session->leadconn->dd_data; + if (!iser_conn) { + mutex_unlock(&unbind_iser_conn_mutex); + return -ENOTCONN; + } ib_dev = iser_conn->ib_conn.device->ib_device; if (!(ib_dev->attrs.device_cap_flags & IB_DEVICE_SG_GAPS_REG)) blk_queue_virt_boundary(sdev->request_queue, ~MASK_4K); + mutex_unlock(&unbind_iser_conn_mutex); + return 0; } diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c index 12ed62ce9ff7..2a07692007bd 100644 --- a/drivers/infiniband/ulp/iser/iser_initiator.c +++ b/drivers/infiniband/ulp/iser/iser_initiator.c @@ -137,8 +137,10 @@ iser_prepare_write_cmd(struct iscsi_task *task, if (unsol_sz < edtl) { hdr->flags |= ISER_WSV; - hdr->write_stag = cpu_to_be32(mem_reg->rkey); - hdr->write_va = cpu_to_be64(mem_reg->sge.addr + unsol_sz); + if (buf_out->data_len > imm_sz) { + hdr->write_stag = cpu_to_be32(mem_reg->rkey); + hdr->write_va = cpu_to_be64(mem_reg->sge.addr + unsol_sz); + } iser_dbg("Cmd itt:%d, WRITE tags, RKEY:%#.4X " "VA:%#llX + unsol:%d\n", diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index c538a38c91ce..26a004e97ae0 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -708,8 +708,14 @@ iser_calc_scsi_params(struct iser_conn *iser_conn, unsigned short sg_tablesize, sup_sg_tablesize; sg_tablesize = DIV_ROUND_UP(max_sectors * 512, SIZE_4K); - sup_sg_tablesize = min_t(unsigned, ISCSI_ISER_MAX_SG_TABLESIZE, - device->ib_device->attrs.max_fast_reg_page_list_len); + if (device->ib_device->attrs.device_cap_flags & + IB_DEVICE_MEM_MGT_EXTENSIONS) + sup_sg_tablesize = + min_t( + uint, ISCSI_ISER_MAX_SG_TABLESIZE, + device->ib_device->attrs.max_fast_reg_page_list_len); + else + sup_sg_tablesize = ISCSI_ISER_MAX_SG_TABLESIZE; iser_conn->scsi_sg_tablesize = min(sg_tablesize, sup_sg_tablesize); } diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index f600f3a7a3c6..23520df7650f 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -331,7 +331,7 @@ static int soc_button_probe(struct platform_device *pdev) error = gpiod_count(dev, NULL); if (error < 0) { dev_dbg(dev, "no GPIO attached, ignoring...\n"); - return error; + return -ENODEV; } priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 262d1057c1da..850b00e3ad8e 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1215,14 +1215,24 @@ static int alps_decode_ss4_v2(struct alps_fields *f, case SS4_PACKET_ID_TWO: if (priv->flags & ALPS_BUTTONPAD) { - f->mt[0].x = SS4_BTL_MF_X_V2(p, 0); + if (IS_SS4PLUS_DEV(priv->dev_id)) { + f->mt[0].x = SS4_PLUS_BTL_MF_X_V2(p, 0); + f->mt[1].x = SS4_PLUS_BTL_MF_X_V2(p, 1); + } else { + f->mt[0].x = SS4_BTL_MF_X_V2(p, 0); + f->mt[1].x = SS4_BTL_MF_X_V2(p, 1); + } f->mt[0].y = SS4_BTL_MF_Y_V2(p, 0); - f->mt[1].x = SS4_BTL_MF_X_V2(p, 1); f->mt[1].y = SS4_BTL_MF_Y_V2(p, 1); } else { - f->mt[0].x = SS4_STD_MF_X_V2(p, 0); + if (IS_SS4PLUS_DEV(priv->dev_id)) { + f->mt[0].x = SS4_PLUS_STD_MF_X_V2(p, 0); + f->mt[1].x = SS4_PLUS_STD_MF_X_V2(p, 1); + } else { + f->mt[0].x = SS4_STD_MF_X_V2(p, 0); + f->mt[1].x = SS4_STD_MF_X_V2(p, 1); + } f->mt[0].y = SS4_STD_MF_Y_V2(p, 0); - f->mt[1].x = SS4_STD_MF_X_V2(p, 1); f->mt[1].y = SS4_STD_MF_Y_V2(p, 1); } f->pressure = SS4_MF_Z_V2(p, 0) ? 0x30 : 0; @@ -1239,16 +1249,27 @@ static int alps_decode_ss4_v2(struct alps_fields *f, case SS4_PACKET_ID_MULTI: if (priv->flags & ALPS_BUTTONPAD) { - f->mt[2].x = SS4_BTL_MF_X_V2(p, 0); + if (IS_SS4PLUS_DEV(priv->dev_id)) { + f->mt[0].x = SS4_PLUS_BTL_MF_X_V2(p, 0); + f->mt[1].x = SS4_PLUS_BTL_MF_X_V2(p, 1); + } else { + f->mt[2].x = SS4_BTL_MF_X_V2(p, 0); + f->mt[3].x = SS4_BTL_MF_X_V2(p, 1); + } + f->mt[2].y = SS4_BTL_MF_Y_V2(p, 0); - f->mt[3].x = SS4_BTL_MF_X_V2(p, 1); f->mt[3].y = SS4_BTL_MF_Y_V2(p, 1); no_data_x = SS4_MFPACKET_NO_AX_BL; no_data_y = SS4_MFPACKET_NO_AY_BL; } else { - f->mt[2].x = SS4_STD_MF_X_V2(p, 0); + if (IS_SS4PLUS_DEV(priv->dev_id)) { + f->mt[0].x = SS4_PLUS_STD_MF_X_V2(p, 0); + f->mt[1].x = SS4_PLUS_STD_MF_X_V2(p, 1); + } else { + f->mt[0].x = SS4_STD_MF_X_V2(p, 0); + f->mt[1].x = SS4_STD_MF_X_V2(p, 1); + } f->mt[2].y = SS4_STD_MF_Y_V2(p, 0); - f->mt[3].x = SS4_STD_MF_X_V2(p, 1); f->mt[3].y = SS4_STD_MF_Y_V2(p, 1); no_data_x = SS4_MFPACKET_NO_AX; no_data_y = SS4_MFPACKET_NO_AY; @@ -2541,8 +2562,8 @@ static int alps_set_defaults_ss4_v2(struct psmouse *psmouse, memset(otp, 0, sizeof(otp)); - if (alps_get_otp_values_ss4_v2(psmouse, 0, &otp[0][0]) || - alps_get_otp_values_ss4_v2(psmouse, 1, &otp[1][0])) + if (alps_get_otp_values_ss4_v2(psmouse, 1, &otp[1][0]) || + alps_get_otp_values_ss4_v2(psmouse, 0, &otp[0][0])) return -1; alps_update_device_area_ss4_v2(otp, priv); diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index ed2d6879fa52..c80a7c76cb76 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -100,6 +100,10 @@ enum SS4_PACKET_ID { ((_b[1 + _i * 3] << 5) & 0x1F00) \ ) +#define SS4_PLUS_STD_MF_X_V2(_b, _i) (((_b[0 + (_i) * 3] << 4) & 0x0070) | \ + ((_b[1 + (_i) * 3] << 4) & 0x0F80) \ + ) + #define SS4_STD_MF_Y_V2(_b, _i) (((_b[1 + (_i) * 3] << 3) & 0x0010) | \ ((_b[2 + (_i) * 3] << 5) & 0x01E0) | \ ((_b[2 + (_i) * 3] << 4) & 0x0E00) \ @@ -109,6 +113,10 @@ enum SS4_PACKET_ID { ((_b[0 + (_i) * 3] >> 3) & 0x0010) \ ) +#define SS4_PLUS_BTL_MF_X_V2(_b, _i) (SS4_PLUS_STD_MF_X_V2(_b, _i) | \ + ((_b[0 + (_i) * 3] >> 4) & 0x0008) \ + ) + #define SS4_BTL_MF_Y_V2(_b, _i) (SS4_STD_MF_Y_V2(_b, _i) | \ ((_b[0 + (_i) * 3] >> 3) & 0x0008) \ ) diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 3b616cb7c67f..cfbc8ba4c96c 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -1247,7 +1247,12 @@ static const struct acpi_device_id elan_acpi_id[] = { { "ELAN0000", 0 }, { "ELAN0100", 0 }, { "ELAN0600", 0 }, + { "ELAN0602", 0 }, { "ELAN0605", 0 }, + { "ELAN0608", 0 }, + { "ELAN0605", 0 }, + { "ELAN0609", 0 }, + { "ELAN060B", 0 }, { "ELAN1000", 0 }, { } }; diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index 922ea02edcc3..0871010f18d5 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -265,7 +265,8 @@ static int trackpoint_start_protocol(struct psmouse *psmouse, unsigned char *fir if (ps2_command(&psmouse->ps2dev, param, MAKE_PS2_CMD(0, 2, TP_READ_ID))) return -1; - if (param[0] != TP_MAGIC_IDENT) + /* add new TP ID. */ + if (!(param[0] & TP_MAGIC_IDENT)) return -1; if (firmware_id) @@ -380,8 +381,8 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) return 0; if (trackpoint_read(ps2dev, TP_EXT_BTN, &button_info)) { - psmouse_warn(psmouse, "failed to get extended button data\n"); - button_info = 0; + psmouse_warn(psmouse, "failed to get extended button data, assuming 3 buttons\n"); + button_info = 0x33; } psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL); diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h index 5617ed3a7d7a..88055755f82e 100644 --- a/drivers/input/mouse/trackpoint.h +++ b/drivers/input/mouse/trackpoint.h @@ -21,8 +21,9 @@ #define TP_COMMAND 0xE2 /* Commands start with this */ #define TP_READ_ID 0xE1 /* Sent for device identification */ -#define TP_MAGIC_IDENT 0x01 /* Sent after a TP_READ_ID followed */ +#define TP_MAGIC_IDENT 0x03 /* Sent after a TP_READ_ID followed */ /* by the firmware ID */ + /* Firmware ID includes 0x1, 0x2, 0x3 */ /* diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 688e77576e5a..354cbd6392cd 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -4452,6 +4452,7 @@ static int amd_ir_set_vcpu_affinity(struct irq_data *data, void *vcpu_info) /* Setting */ irte->hi.fields.ga_root_ptr = (pi_data->base >> 12); irte->hi.fields.vector = vcpu_pi_info->vector; + irte->lo.fields_vapic.ga_log_intr = 1; irte->lo.fields_vapic.guest_mode = 1; irte->lo.fields_vapic.ga_tag = pi_data->ga_tag; diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index 5cc597b383c7..372303700566 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -2440,11 +2440,11 @@ static int __init state_next(void) break; case IOMMU_ACPI_FINISHED: early_enable_iommus(); - register_syscore_ops(&amd_iommu_syscore_ops); x86_platform.iommu_shutdown = disable_iommus; init_state = IOMMU_ENABLED; break; case IOMMU_ENABLED: + register_syscore_ops(&amd_iommu_syscore_ops); ret = amd_iommu_init_pci(); init_state = ret ? IOMMU_INIT_ERROR : IOMMU_PCI_INIT; enable_iommus_v2(); diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h index 294a409e283b..d6b873b57054 100644 --- a/drivers/iommu/amd_iommu_types.h +++ b/drivers/iommu/amd_iommu_types.h @@ -574,7 +574,9 @@ struct amd_iommu { static inline struct amd_iommu *dev_to_amd_iommu(struct device *dev) { - return container_of(dev, struct amd_iommu, iommu.dev); + struct iommu_device *iommu = dev_to_iommu_device(dev); + + return container_of(iommu, struct amd_iommu, iommu); } #define ACPIHID_UID_LEN 256 diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c index bc89b4d6c043..2d80fa8a0634 100644 --- a/drivers/iommu/arm-smmu.c +++ b/drivers/iommu/arm-smmu.c @@ -400,6 +400,8 @@ struct arm_smmu_device { u32 cavium_id_base; /* Specific to Cavium */ + spinlock_t global_sync_lock; + /* IOMMU core code handle */ struct iommu_device iommu; }; @@ -436,7 +438,7 @@ struct arm_smmu_domain { struct arm_smmu_cfg cfg; enum arm_smmu_domain_stage stage; struct mutex init_mutex; /* Protects smmu pointer */ - spinlock_t cb_lock; /* Serialises ATS1* ops */ + spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */ struct iommu_domain domain; }; @@ -602,9 +604,12 @@ static void __arm_smmu_tlb_sync(struct arm_smmu_device *smmu, static void arm_smmu_tlb_sync_global(struct arm_smmu_device *smmu) { void __iomem *base = ARM_SMMU_GR0(smmu); + unsigned long flags; + spin_lock_irqsave(&smmu->global_sync_lock, flags); __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_GR0_sTLBGSYNC, base + ARM_SMMU_GR0_sTLBGSTATUS); + spin_unlock_irqrestore(&smmu->global_sync_lock, flags); } static void arm_smmu_tlb_sync_context(void *cookie) @@ -612,9 +617,12 @@ static void arm_smmu_tlb_sync_context(void *cookie) struct arm_smmu_domain *smmu_domain = cookie; struct arm_smmu_device *smmu = smmu_domain->smmu; void __iomem *base = ARM_SMMU_CB(smmu, smmu_domain->cfg.cbndx); + unsigned long flags; + spin_lock_irqsave(&smmu_domain->cb_lock, flags); __arm_smmu_tlb_sync(smmu, base + ARM_SMMU_CB_TLBSYNC, base + ARM_SMMU_CB_TLBSTATUS); + spin_unlock_irqrestore(&smmu_domain->cb_lock, flags); } static void arm_smmu_tlb_sync_vmid(void *cookie) @@ -1511,6 +1519,12 @@ static int arm_smmu_add_device(struct device *dev) if (using_legacy_binding) { ret = arm_smmu_register_legacy_master(dev, &smmu); + + /* + * If dev->iommu_fwspec is initally NULL, arm_smmu_register_legacy_master() + * will allocate/initialise a new one. Thus we need to update fwspec for + * later use. + */ fwspec = dev->iommu_fwspec; if (ret) goto out_free; @@ -1550,15 +1564,15 @@ static int arm_smmu_add_device(struct device *dev) ret = arm_smmu_master_alloc_smes(dev); if (ret) - goto out_free; + goto out_cfg_free; iommu_device_link(&smmu->iommu, dev); return 0; +out_cfg_free: + kfree(cfg); out_free: - if (fwspec) - kfree(fwspec->iommu_priv); iommu_fwspec_free(dev); return ret; } @@ -1925,6 +1939,7 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu) smmu->num_mapping_groups = size; mutex_init(&smmu->stream_map_mutex); + spin_lock_init(&smmu->global_sync_lock); if (smmu->version < ARM_SMMU_V2 || !(id & ID0_PTFS_NO_AARCH32)) { smmu->features |= ARM_SMMU_FEAT_FMT_AARCH32_L; diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 687f18f65cea..3e8636f1220e 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4736,7 +4736,9 @@ static void intel_disable_iommus(void) static inline struct intel_iommu *dev_to_intel_iommu(struct device *dev) { - return container_of(dev, struct intel_iommu, iommu.dev); + struct iommu_device *iommu_dev = dev_to_iommu_device(dev); + + return container_of(iommu_dev, struct intel_iommu, iommu); } static ssize_t intel_iommu_show_version(struct device *dev, diff --git a/drivers/iommu/io-pgtable-arm-v7s.c b/drivers/iommu/io-pgtable-arm-v7s.c index af330f513653..d665d0dc16e8 100644 --- a/drivers/iommu/io-pgtable-arm-v7s.c +++ b/drivers/iommu/io-pgtable-arm-v7s.c @@ -479,6 +479,9 @@ static int arm_v7s_map(struct io_pgtable_ops *ops, unsigned long iova, if (!(prot & (IOMMU_READ | IOMMU_WRITE))) return 0; + if (WARN_ON(upper_32_bits(iova) || upper_32_bits(paddr))) + return -ERANGE; + ret = __arm_v7s_map(data, iova, paddr, size, prot, 1, data->pgd); /* * Synchronise all PTE updates for the new mapping before there's @@ -659,6 +662,9 @@ static int arm_v7s_unmap(struct io_pgtable_ops *ops, unsigned long iova, struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops); size_t unmapped; + if (WARN_ON(upper_32_bits(iova))) + return 0; + unmapped = __arm_v7s_unmap(data, iova, size, 1, data->pgd); if (unmapped) io_pgtable_tlb_sync(&data->iop); diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c index b182039862c5..e8018a308868 100644 --- a/drivers/iommu/io-pgtable-arm.c +++ b/drivers/iommu/io-pgtable-arm.c @@ -452,6 +452,10 @@ static int arm_lpae_map(struct io_pgtable_ops *ops, unsigned long iova, if (!(iommu_prot & (IOMMU_READ | IOMMU_WRITE))) return 0; + if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias) || + paddr >= (1ULL << data->iop.cfg.oas))) + return -ERANGE; + prot = arm_lpae_prot_to_pte(data, iommu_prot); ret = __arm_lpae_map(data, iova, paddr, size, prot, lvl, ptep); /* @@ -610,6 +614,9 @@ static int arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova, arm_lpae_iopte *ptep = data->pgd; int lvl = ARM_LPAE_START_LVL(data); + if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias))) + return 0; + unmapped = __arm_lpae_unmap(data, iova, size, lvl, ptep); if (unmapped) io_pgtable_tlb_sync(&data->iop); diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h index 524263a7ae6f..a3e667077b14 100644 --- a/drivers/iommu/io-pgtable.h +++ b/drivers/iommu/io-pgtable.h @@ -158,14 +158,12 @@ void free_io_pgtable_ops(struct io_pgtable_ops *ops); * @fmt: The page table format. * @cookie: An opaque token provided by the IOMMU driver and passed back to * any callback routines. - * @tlb_sync_pending: Private flag for optimising out redundant syncs. * @cfg: A copy of the page table configuration. * @ops: The page table operations in use for this set of page tables. */ struct io_pgtable { enum io_pgtable_fmt fmt; void *cookie; - bool tlb_sync_pending; struct io_pgtable_cfg cfg; struct io_pgtable_ops ops; }; @@ -175,22 +173,17 @@ struct io_pgtable { static inline void io_pgtable_tlb_flush_all(struct io_pgtable *iop) { iop->cfg.tlb->tlb_flush_all(iop->cookie); - iop->tlb_sync_pending = true; } static inline void io_pgtable_tlb_add_flush(struct io_pgtable *iop, unsigned long iova, size_t size, size_t granule, bool leaf) { iop->cfg.tlb->tlb_add_flush(iova, size, granule, leaf, iop->cookie); - iop->tlb_sync_pending = true; } static inline void io_pgtable_tlb_sync(struct io_pgtable *iop) { - if (iop->tlb_sync_pending) { - iop->cfg.tlb->tlb_sync(iop->cookie); - iop->tlb_sync_pending = false; - } + iop->cfg.tlb->tlb_sync(iop->cookie); } /** diff --git a/drivers/iommu/iommu-sysfs.c b/drivers/iommu/iommu-sysfs.c index c58351ed61c1..36d1a7ce7fc4 100644 --- a/drivers/iommu/iommu-sysfs.c +++ b/drivers/iommu/iommu-sysfs.c @@ -62,32 +62,40 @@ int iommu_device_sysfs_add(struct iommu_device *iommu, va_list vargs; int ret; - device_initialize(&iommu->dev); + iommu->dev = kzalloc(sizeof(*iommu->dev), GFP_KERNEL); + if (!iommu->dev) + return -ENOMEM; - iommu->dev.class = &iommu_class; - iommu->dev.parent = parent; - iommu->dev.groups = groups; + device_initialize(iommu->dev); + + iommu->dev->class = &iommu_class; + iommu->dev->parent = parent; + iommu->dev->groups = groups; va_start(vargs, fmt); - ret = kobject_set_name_vargs(&iommu->dev.kobj, fmt, vargs); + ret = kobject_set_name_vargs(&iommu->dev->kobj, fmt, vargs); va_end(vargs); if (ret) goto error; - ret = device_add(&iommu->dev); + ret = device_add(iommu->dev); if (ret) goto error; + dev_set_drvdata(iommu->dev, iommu); + return 0; error: - put_device(&iommu->dev); + put_device(iommu->dev); return ret; } void iommu_device_sysfs_remove(struct iommu_device *iommu) { - device_unregister(&iommu->dev); + dev_set_drvdata(iommu->dev, NULL); + device_unregister(iommu->dev); + iommu->dev = NULL; } /* * IOMMU drivers can indicate a device is managed by a given IOMMU using @@ -102,14 +110,14 @@ int iommu_device_link(struct iommu_device *iommu, struct device *link) if (!iommu || IS_ERR(iommu)) return -ENODEV; - ret = sysfs_add_link_to_group(&iommu->dev.kobj, "devices", + ret = sysfs_add_link_to_group(&iommu->dev->kobj, "devices", &link->kobj, dev_name(link)); if (ret) return ret; - ret = sysfs_create_link_nowarn(&link->kobj, &iommu->dev.kobj, "iommu"); + ret = sysfs_create_link_nowarn(&link->kobj, &iommu->dev->kobj, "iommu"); if (ret) - sysfs_remove_link_from_group(&iommu->dev.kobj, "devices", + sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", dev_name(link)); return ret; @@ -121,5 +129,5 @@ void iommu_device_unlink(struct iommu_device *iommu, struct device *link) return; sysfs_remove_link(&link->kobj, "iommu"); - sysfs_remove_link_from_group(&iommu->dev.kobj, "devices", dev_name(link)); + sysfs_remove_link_from_group(&iommu->dev->kobj, "devices", dev_name(link)); } diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c index 5d14cd15198d..91c6d367ab35 100644 --- a/drivers/iommu/mtk_iommu.c +++ b/drivers/iommu/mtk_iommu.c @@ -129,6 +129,7 @@ static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size, writel_relaxed(iova, data->base + REG_MMU_INVLD_START_A); writel_relaxed(iova + size - 1, data->base + REG_MMU_INVLD_END_A); writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE); + data->tlb_flush_active = true; } static void mtk_iommu_tlb_sync(void *cookie) @@ -137,6 +138,10 @@ static void mtk_iommu_tlb_sync(void *cookie) int ret; u32 tmp; + /* Avoid timing out if there's nothing to wait for */ + if (!data->tlb_flush_active) + return; + ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, tmp, tmp != 0, 10, 100000); if (ret) { @@ -146,6 +151,7 @@ static void mtk_iommu_tlb_sync(void *cookie) } /* Clear the CPE status */ writel_relaxed(0, data->base + REG_MMU_CPE_DONE); + data->tlb_flush_active = false; } static const struct iommu_gather_ops mtk_iommu_gather_ops = { diff --git a/drivers/iommu/mtk_iommu.h b/drivers/iommu/mtk_iommu.h index 2a28eadeea0e..c06cc91b5d9a 100644 --- a/drivers/iommu/mtk_iommu.h +++ b/drivers/iommu/mtk_iommu.h @@ -47,6 +47,7 @@ struct mtk_iommu_data { struct iommu_group *m4u_group; struct mtk_smi_iommu smi_imu; /* SMI larb iommu info */ bool enable_4GB; + bool tlb_flush_active; struct iommu_device iommu; }; diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c index 28b26c80f4cf..072bd227b6c6 100644 --- a/drivers/irqchip/irq-atmel-aic-common.c +++ b/drivers/irqchip/irq-atmel-aic-common.c @@ -137,14 +137,14 @@ static void __init aic_common_ext_irq_of_init(struct irq_domain *domain) #define AT91_RTC_IMR 0x28 #define AT91_RTC_IRQ_MASK 0x1f -void __init aic_common_rtc_irq_fixup(struct device_node *root) +void __init aic_common_rtc_irq_fixup(void) { struct device_node *np; void __iomem *regs; - np = of_find_compatible_node(root, NULL, "atmel,at91rm9200-rtc"); + np = of_find_compatible_node(NULL, NULL, "atmel,at91rm9200-rtc"); if (!np) - np = of_find_compatible_node(root, NULL, + np = of_find_compatible_node(NULL, NULL, "atmel,at91sam9x5-rtc"); if (!np) @@ -165,7 +165,7 @@ void __init aic_common_rtc_irq_fixup(struct device_node *root) #define AT91_RTT_ALMIEN (1 << 16) /* Alarm Interrupt Enable */ #define AT91_RTT_RTTINCIEN (1 << 17) /* Real Time Timer Increment Interrupt Enable */ -void __init aic_common_rtt_irq_fixup(struct device_node *root) +void __init aic_common_rtt_irq_fixup(void) { struct device_node *np; void __iomem *regs; @@ -196,11 +196,10 @@ static void __init aic_common_irq_fixup(const struct of_device_id *matches) return; match = of_match_node(matches, root); - of_node_put(root); if (match) { - void (*fixup)(struct device_node *) = match->data; - fixup(root); + void (*fixup)(void) = match->data; + fixup(); } of_node_put(root); diff --git a/drivers/irqchip/irq-atmel-aic-common.h b/drivers/irqchip/irq-atmel-aic-common.h index af60376d50de..242e62c1851e 100644 --- a/drivers/irqchip/irq-atmel-aic-common.h +++ b/drivers/irqchip/irq-atmel-aic-common.h @@ -33,8 +33,8 @@ struct irq_domain *__init aic_common_of_init(struct device_node *node, const char *name, int nirqs, const struct of_device_id *matches); -void __init aic_common_rtc_irq_fixup(struct device_node *root); +void __init aic_common_rtc_irq_fixup(void); -void __init aic_common_rtt_irq_fixup(struct device_node *root); +void __init aic_common_rtt_irq_fixup(void); #endif /* __IRQ_ATMEL_AIC_COMMON_H */ diff --git a/drivers/irqchip/irq-atmel-aic.c b/drivers/irqchip/irq-atmel-aic.c index 37f952dd9fc9..bb1ad451392f 100644 --- a/drivers/irqchip/irq-atmel-aic.c +++ b/drivers/irqchip/irq-atmel-aic.c @@ -209,20 +209,20 @@ static const struct irq_domain_ops aic_irq_ops = { .xlate = aic_irq_domain_xlate, }; -static void __init at91rm9200_aic_irq_fixup(struct device_node *root) +static void __init at91rm9200_aic_irq_fixup(void) { - aic_common_rtc_irq_fixup(root); + aic_common_rtc_irq_fixup(); } -static void __init at91sam9260_aic_irq_fixup(struct device_node *root) +static void __init at91sam9260_aic_irq_fixup(void) { - aic_common_rtt_irq_fixup(root); + aic_common_rtt_irq_fixup(); } -static void __init at91sam9g45_aic_irq_fixup(struct device_node *root) +static void __init at91sam9g45_aic_irq_fixup(void) { - aic_common_rtc_irq_fixup(root); - aic_common_rtt_irq_fixup(root); + aic_common_rtc_irq_fixup(); + aic_common_rtt_irq_fixup(); } static const struct of_device_id aic_irq_fixups[] __initconst = { diff --git a/drivers/irqchip/irq-atmel-aic5.c b/drivers/irqchip/irq-atmel-aic5.c index c04ee9a23d09..6acad2ea0fb3 100644 --- a/drivers/irqchip/irq-atmel-aic5.c +++ b/drivers/irqchip/irq-atmel-aic5.c @@ -305,9 +305,9 @@ static const struct irq_domain_ops aic5_irq_ops = { .xlate = aic5_irq_domain_xlate, }; -static void __init sama5d3_aic_irq_fixup(struct device_node *root) +static void __init sama5d3_aic_irq_fixup(void) { - aic_common_rtc_irq_fixup(root); + aic_common_rtc_irq_fixup(); } static const struct of_device_id aic5_irq_fixups[] __initconst = { diff --git a/drivers/irqchip/irq-brcmstb-l2.c b/drivers/irqchip/irq-brcmstb-l2.c index bddf169c4b37..b009b916a292 100644 --- a/drivers/irqchip/irq-brcmstb-l2.c +++ b/drivers/irqchip/irq-brcmstb-l2.c @@ -189,6 +189,7 @@ static int __init brcmstb_l2_intc_of_init(struct device_node *np, ct->chip.irq_suspend = brcmstb_l2_intc_suspend; ct->chip.irq_resume = brcmstb_l2_intc_resume; + ct->chip.irq_pm_shutdown = brcmstb_l2_intc_suspend; if (data->can_wake) { /* This IRQ chip can wake the system, set all child interrupts diff --git a/drivers/irqchip/irq-digicolor.c b/drivers/irqchip/irq-digicolor.c index dad85e74c37c..3aae015469a5 100644 --- a/drivers/irqchip/irq-digicolor.c +++ b/drivers/irqchip/irq-digicolor.c @@ -71,7 +71,7 @@ static void __init digicolor_set_gc(void __iomem *reg_base, unsigned irq_base, static int __init digicolor_of_init(struct device_node *node, struct device_node *parent) { - static void __iomem *reg_base; + void __iomem *reg_base; unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; struct regmap *ucregs; int ret; diff --git a/drivers/irqchip/irq-gic-realview.c b/drivers/irqchip/irq-gic-realview.c index 54c296401525..18d58d2b4ffe 100644 --- a/drivers/irqchip/irq-gic-realview.c +++ b/drivers/irqchip/irq-gic-realview.c @@ -43,7 +43,7 @@ static const struct of_device_id syscon_pldset_of_match[] = { static int __init realview_gic_of_init(struct device_node *node, struct device_node *parent) { - static struct regmap *map; + struct regmap *map; struct device_node *np; const struct of_device_id *gic_id; u32 pld1_ctrl; diff --git a/drivers/irqchip/irq-gic-v3-its-platform-msi.c b/drivers/irqchip/irq-gic-v3-its-platform-msi.c index 249240d9a425..833a90fe33ae 100644 --- a/drivers/irqchip/irq-gic-v3-its-platform-msi.c +++ b/drivers/irqchip/irq-gic-v3-its-platform-msi.c @@ -43,6 +43,7 @@ static int of_pmsi_get_dev_id(struct irq_domain *domain, struct device *dev, *dev_id = args.args[0]; break; } + index++; } while (!ret); return ret; diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 68932873eebc..284738add89b 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1835,7 +1835,7 @@ static int __init its_of_probe(struct device_node *node) #define ACPI_GICV3_ITS_MEM_SIZE (SZ_128K) -#if defined(CONFIG_ACPI_NUMA) && (ACPI_CA_VERSION >= 0x20170531) +#ifdef CONFIG_ACPI_NUMA struct its_srat_map { /* numa node id */ u32 numa_node; @@ -1843,7 +1843,7 @@ struct its_srat_map { u32 its_id; }; -static struct its_srat_map its_srat_maps[MAX_NUMNODES] __initdata; +static struct its_srat_map *its_srat_maps __initdata; static int its_in_srat __initdata; static int __init acpi_get_its_numa_node(u32 its_id) @@ -1857,6 +1857,12 @@ static int __init acpi_get_its_numa_node(u32 its_id) return NUMA_NO_NODE; } +static int __init gic_acpi_match_srat_its(struct acpi_subtable_header *header, + const unsigned long end) +{ + return 0; +} + static int __init gic_acpi_parse_srat_its(struct acpi_subtable_header *header, const unsigned long end) { @@ -1873,12 +1879,6 @@ static int __init gic_acpi_parse_srat_its(struct acpi_subtable_header *header, return -EINVAL; } - if (its_in_srat >= MAX_NUMNODES) { - pr_err("SRAT: ITS affinity exceeding max count[%d]\n", - MAX_NUMNODES); - return -EINVAL; - } - node = acpi_map_pxm_to_node(its_affinity->proximity_domain); if (node == NUMA_NO_NODE || node >= MAX_NUMNODES) { @@ -1897,14 +1897,37 @@ static int __init gic_acpi_parse_srat_its(struct acpi_subtable_header *header, static void __init acpi_table_parse_srat_its(void) { + int count; + + count = acpi_table_parse_entries(ACPI_SIG_SRAT, + sizeof(struct acpi_table_srat), + ACPI_SRAT_TYPE_GIC_ITS_AFFINITY, + gic_acpi_match_srat_its, 0); + if (count <= 0) + return; + + its_srat_maps = kmalloc(count * sizeof(struct its_srat_map), + GFP_KERNEL); + if (!its_srat_maps) { + pr_warn("SRAT: Failed to allocate memory for its_srat_maps!\n"); + return; + } + acpi_table_parse_entries(ACPI_SIG_SRAT, sizeof(struct acpi_table_srat), ACPI_SRAT_TYPE_GIC_ITS_AFFINITY, gic_acpi_parse_srat_its, 0); } + +/* free the its_srat_maps after ITS probing */ +static void __init acpi_its_srat_maps_free(void) +{ + kfree(its_srat_maps); +} #else static void __init acpi_table_parse_srat_its(void) { } static int __init acpi_get_its_numa_node(u32 its_id) { return NUMA_NO_NODE; } +static void __init acpi_its_srat_maps_free(void) { } #endif static int __init gic_acpi_parse_madt_its(struct acpi_subtable_header *header, @@ -1951,6 +1974,7 @@ static void __init its_acpi_probe(void) acpi_table_parse_srat_its(); acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR, gic_acpi_parse_madt_its, 0); + acpi_its_srat_maps_free(); } #else static void __init its_acpi_probe(void) { } diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index dbffb7ab6203..984c3ecfd22c 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -353,6 +353,8 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs if (static_key_true(&supports_deactivate)) gic_write_eoir(irqnr); + else + isb(); err = handle_domain_irq(gic_data.domain, irqnr, regs); if (err) { @@ -640,11 +642,16 @@ static void gic_smp_init(void) static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { - unsigned int cpu = cpumask_any_and(mask_val, cpu_online_mask); + unsigned int cpu; void __iomem *reg; int enabled; u64 val; + if (force) + cpu = cpumask_first(mask_val); + else + cpu = cpumask_any_and(mask_val, cpu_online_mask); + if (cpu >= nr_cpu_ids) return -EINVAL; @@ -831,8 +838,11 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, if (ret) return ret; - for (i = 0; i < nr_irqs; i++) - gic_irq_domain_map(domain, virq + i, hwirq + i); + for (i = 0; i < nr_irqs; i++) { + ret = gic_irq_domain_map(domain, virq + i, hwirq + i); + if (ret) + return ret; + } return 0; } diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 1b1df4f770bd..d3e7c43718b8 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -361,6 +361,7 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) if (likely(irqnr > 15 && irqnr < 1020)) { if (static_key_true(&supports_deactivate)) writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI); + isb(); handle_domain_irq(gic->domain, irqnr, regs); continue; } @@ -401,10 +402,12 @@ static void gic_handle_cascade_irq(struct irq_desc *desc) goto out; cascade_irq = irq_find_mapping(chip_data->domain, gic_irq); - if (unlikely(gic_irq < 32 || gic_irq > 1020)) + if (unlikely(gic_irq < 32 || gic_irq > 1020)) { handle_bad_irq(desc); - else + } else { + isb(); generic_handle_irq(cascade_irq); + } out: chained_irq_exit(chip, desc); @@ -1027,8 +1030,11 @@ static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, if (ret) return ret; - for (i = 0; i < nr_irqs; i++) - gic_irq_domain_map(domain, virq + i, hwirq + i); + for (i = 0; i < nr_irqs; i++) { + ret = gic_irq_domain_map(domain, virq + i, hwirq + i); + if (ret) + return ret; + } return 0; } diff --git a/drivers/irqchip/irq-mips-cpu.c b/drivers/irqchip/irq-mips-cpu.c index 0a8ed1c05518..14461cbfab2f 100644 --- a/drivers/irqchip/irq-mips-cpu.c +++ b/drivers/irqchip/irq-mips-cpu.c @@ -154,7 +154,7 @@ asmlinkage void __weak plat_irq_dispatch(void) static int mips_cpu_intc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { - static struct irq_chip *chip; + struct irq_chip *chip; if (hw < 2 && cpu_has_mipsmt) { /* Software interrupts are used for MT/CMT IPI */ diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index 832ebf4062f7..6ab1d3afec02 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -950,7 +950,6 @@ static void __init __gic_init(unsigned long gic_base_addr, &gic_irq_domain_ops, NULL); if (!gic_irq_domain) panic("Failed to add GIC IRQ domain"); - gic_irq_domain->name = "mips-gic-irq"; gic_ipi_domain = irq_domain_add_hierarchy(gic_irq_domain, IRQ_DOMAIN_FLAG_IPI_PER_CPU, @@ -959,7 +958,6 @@ static void __init __gic_init(unsigned long gic_base_addr, if (!gic_ipi_domain) panic("Failed to add GIC IPI domain"); - gic_ipi_domain->name = "mips-gic-ipi"; irq_domain_update_bus_token(gic_ipi_domain, DOMAIN_BUS_IPI); if (node && diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c index 060d357f107f..6f423bc49d0d 100644 --- a/drivers/isdn/divert/isdn_divert.c +++ b/drivers/isdn/divert/isdn_divert.c @@ -485,18 +485,19 @@ static int isdn_divert_icall(isdn_ctrl *ic) cs->deflect_dest[0] = '\0'; retval = 4; /* only proceed */ } - sprintf(cs->info, "%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n", - cs->akt_state, - cs->divert_id, - divert_if.drv_to_name(cs->ics.driver), - (ic->command == ISDN_STAT_ICALLW) ? "1" : "0", - cs->ics.parm.setup.phone, - cs->ics.parm.setup.eazmsn, - cs->ics.parm.setup.si1, - cs->ics.parm.setup.si2, - cs->ics.parm.setup.screen, - dv->rule.waittime, - cs->deflect_dest); + snprintf(cs->info, sizeof(cs->info), + "%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n", + cs->akt_state, + cs->divert_id, + divert_if.drv_to_name(cs->ics.driver), + (ic->command == ISDN_STAT_ICALLW) ? "1" : "0", + cs->ics.parm.setup.phone, + cs->ics.parm.setup.eazmsn, + cs->ics.parm.setup.si1, + cs->ics.parm.setup.si2, + cs->ics.parm.setup.screen, + dv->rule.waittime, + cs->deflect_dest); if ((dv->rule.action == DEFLECT_REPORT) || (dv->rule.action == DEFLECT_REJECT)) { put_info_buffer(cs->info); diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c index 40c7e2cf423b..034cabac699d 100644 --- a/drivers/isdn/hardware/avm/c4.c +++ b/drivers/isdn/hardware/avm/c4.c @@ -42,7 +42,7 @@ static char *revision = "$Revision: 1.1.2.2 $"; static bool suppress_pollack; -static struct pci_device_id c4_pci_tbl[] = { +static const struct pci_device_id c4_pci_tbl[] = { { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, 0, 0, (unsigned long)4 }, { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C2, 0, 0, (unsigned long)2 }, { } /* Terminating entry */ diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c index 8b7ad4f1ab01..b2023e08dcd2 100644 --- a/drivers/isdn/hardware/eicon/divasmain.c +++ b/drivers/isdn/hardware/eicon/divasmain.c @@ -110,7 +110,7 @@ typedef struct _diva_os_thread_dpc { /* This table should be sorted by PCI device ID */ -static struct pci_device_id divas_pci_tbl[] = { +static const struct pci_device_id divas_pci_tbl[] = { /* Diva Server BRI-2M PCI 0xE010 */ { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_MAESTRA), CARDTYPE_MAESTRA_PCI }, diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c index e3fa1cd64470..dce6632daae1 100644 --- a/drivers/isdn/hardware/mISDN/avmfritz.c +++ b/drivers/isdn/hardware/mISDN/avmfritz.c @@ -1142,7 +1142,7 @@ fritz_remove_pci(struct pci_dev *pdev) pr_info("%s: drvdata already removed\n", __func__); } -static struct pci_device_id fcpci_ids[] = { +static const struct pci_device_id fcpci_ids[] = { { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) "Fritz!Card PCI"}, { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index aea0c9616ea5..3cf07b8ced1c 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -5348,7 +5348,7 @@ static const struct hm_map hfcm_map[] = { #undef H #define H(x) ((unsigned long)&hfcm_map[x]) -static struct pci_device_id hfmultipci_ids[] = { +static const struct pci_device_id hfmultipci_ids[] = { /* Cards with HFC-4S Chip */ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c index 5dc246d71c16..d2e401a8090e 100644 --- a/drivers/isdn/hardware/mISDN/hfcpci.c +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -2161,7 +2161,7 @@ static const struct _hfc_map hfc_map[] = {}, }; -static struct pci_device_id hfc_ids[] = +static const struct pci_device_id hfc_ids[] = { { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_2BD0), (unsigned long) &hfc_map[0] }, diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c index afde4edef9ae..6a6d848bd18e 100644 --- a/drivers/isdn/hardware/mISDN/netjet.c +++ b/drivers/isdn/hardware/mISDN/netjet.c @@ -1137,7 +1137,7 @@ static void nj_remove(struct pci_dev *pdev) /* We cannot select cards with PCI_SUB... IDs, since here are cards with * SUB IDs set to PCI_ANY_ID, so we need to match all and reject * known other cards which not work with this driver - see probe function */ -static struct pci_device_id nj_pci_ids[] = { +static const struct pci_device_id nj_pci_ids[] = { { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { } diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c index 3052c836b89f..d80072fef434 100644 --- a/drivers/isdn/hardware/mISDN/w6692.c +++ b/drivers/isdn/hardware/mISDN/w6692.c @@ -1398,7 +1398,7 @@ w6692_remove_pci(struct pci_dev *pdev) pr_notice("%s: drvdata already removed\n", __func__); } -static struct pci_device_id w6692_ids[] = { +static const struct pci_device_id w6692_ids[] = { { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]}, { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c index c7d68675b028..7108bdb8742e 100644 --- a/drivers/isdn/hisax/config.c +++ b/drivers/isdn/hisax/config.c @@ -1909,7 +1909,7 @@ static void EChannel_proc_rcv(struct hisax_d_if *d_if) #ifdef CONFIG_PCI #include <linux/pci.h> -static struct pci_device_id hisax_pci_tbl[] __used = { +static const struct pci_device_id hisax_pci_tbl[] __used = { #ifdef CONFIG_HISAX_FRITZPCI {PCI_VDEVICE(AVM, PCI_DEVICE_ID_AVM_A1) }, #endif diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c index 90f051ce0259..9090cc1e1f29 100644 --- a/drivers/isdn/hisax/hfc4s8s_l1.c +++ b/drivers/isdn/hisax/hfc4s8s_l1.c @@ -86,7 +86,7 @@ typedef struct { char *device_name; } hfc4s8s_param; -static struct pci_device_id hfc4s8s_ids[] = { +static const struct pci_device_id hfc4s8s_ids[] = { {.vendor = PCI_VENDOR_ID_CCD, .device = PCI_DEVICE_ID_4S, .subvendor = 0x1397, diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c index 5a9f39ed1d5d..e4f7573ba9bf 100644 --- a/drivers/isdn/hisax/hisax_fcpcipnp.c +++ b/drivers/isdn/hisax/hisax_fcpcipnp.c @@ -52,7 +52,7 @@ module_param(debug, int, 0); MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>"); MODULE_DESCRIPTION("AVM Fritz!PCI/PnP ISDN driver"); -static struct pci_device_id fcpci_ids[] = { +static const struct pci_device_id fcpci_ids[] = { { .vendor = PCI_VENDOR_ID_AVM, .device = PCI_DEVICE_ID_AVM_A1, .subvendor = PCI_ANY_ID, diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c index 7b5fd8fb1761..aaca0b3d662e 100644 --- a/drivers/isdn/hysdn/hysdn_proclog.c +++ b/drivers/isdn/hysdn/hysdn_proclog.c @@ -44,7 +44,6 @@ struct procdata { char log_name[15]; /* log filename */ struct log_data *log_head, *log_tail; /* head and tail for queue */ int if_used; /* open count for interface */ - int volatile del_lock; /* lock for delete operations */ unsigned char logtmp[LOG_MAX_LINELEN]; wait_queue_head_t rd_queue; }; @@ -102,7 +101,6 @@ put_log_buffer(hysdn_card *card, char *cp) { struct log_data *ib; struct procdata *pd = card->proclog; - int i; unsigned long flags; if (!pd) @@ -126,21 +124,21 @@ put_log_buffer(hysdn_card *card, char *cp) else pd->log_tail->next = ib; /* follows existing messages */ pd->log_tail = ib; /* new tail */ - i = pd->del_lock++; /* get lock state */ - spin_unlock_irqrestore(&card->hysdn_lock, flags); /* delete old entrys */ - if (!i) - while (pd->log_head->next) { - if ((pd->log_head->usage_cnt <= 0) && - (pd->log_head->next->usage_cnt <= 0)) { - ib = pd->log_head; - pd->log_head = pd->log_head->next; - kfree(ib); - } else - break; - } /* pd->log_head->next */ - pd->del_lock--; /* release lock level */ + while (pd->log_head->next) { + if ((pd->log_head->usage_cnt <= 0) && + (pd->log_head->next->usage_cnt <= 0)) { + ib = pd->log_head; + pd->log_head = pd->log_head->next; + kfree(ib); + } else { + break; + } + } /* pd->log_head->next */ + + spin_unlock_irqrestore(&card->hysdn_lock, flags); + wake_up_interruptible(&(pd->rd_queue)); /* announce new entry */ } /* put_log_buffer */ diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index 89b09c51ab7c..38a5bb764c7b 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -1376,6 +1376,7 @@ isdn_ioctl(struct file *file, uint cmd, ulong arg) if (arg) { if (copy_from_user(bname, argp, sizeof(bname) - 1)) return -EFAULT; + bname[sizeof(bname)-1] = 0; } else return -EINVAL; ret = mutex_lock_interruptible(&dev->mtx); diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index c151c6daa67e..f63a110b7bcb 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -2611,10 +2611,9 @@ isdn_net_newslave(char *parm) char newname[10]; if (p) { - /* Slave-Name MUST not be empty */ - if (!strlen(p + 1)) + /* Slave-Name MUST not be empty or overflow 'newname' */ + if (strscpy(newname, p + 1, sizeof(newname)) <= 0) return NULL; - strcpy(newname, p + 1); *p = 0; /* Master must already exist */ if (!(n = isdn_net_findif(parm))) diff --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c index 78fc5d5e9051..92e6570b1143 100644 --- a/drivers/isdn/mISDN/fsm.c +++ b/drivers/isdn/mISDN/fsm.c @@ -26,7 +26,7 @@ #define FSM_TIMER_DEBUG 0 -void +int mISDN_FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount) { @@ -34,6 +34,8 @@ mISDN_FsmNew(struct Fsm *fsm, fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL); + if (fsm->jumpmatrix == NULL) + return -ENOMEM; for (i = 0; i < fncount; i++) if ((fnlist[i].state >= fsm->state_count) || @@ -45,6 +47,7 @@ mISDN_FsmNew(struct Fsm *fsm, } else fsm->jumpmatrix[fsm->state_count * fnlist[i].event + fnlist[i].state] = (FSMFNPTR) fnlist[i].routine; + return 0; } EXPORT_SYMBOL(mISDN_FsmNew); diff --git a/drivers/isdn/mISDN/fsm.h b/drivers/isdn/mISDN/fsm.h index 928f5be192c1..e1def8490221 100644 --- a/drivers/isdn/mISDN/fsm.h +++ b/drivers/isdn/mISDN/fsm.h @@ -55,7 +55,7 @@ struct FsmTimer { void *arg; }; -extern void mISDN_FsmNew(struct Fsm *, struct FsmNode *, int); +extern int mISDN_FsmNew(struct Fsm *, struct FsmNode *, int); extern void mISDN_FsmFree(struct Fsm *); extern int mISDN_FsmEvent(struct FsmInst *, int , void *); extern void mISDN_FsmChangeState(struct FsmInst *, int); diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c index bebc57b72138..3192b0eb3944 100644 --- a/drivers/isdn/mISDN/layer1.c +++ b/drivers/isdn/mISDN/layer1.c @@ -414,8 +414,7 @@ l1_init(u_int *deb) l1fsm_s.event_count = L1_EVENT_COUNT; l1fsm_s.strEvent = strL1Event; l1fsm_s.strState = strL1SState; - mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList)); - return 0; + return mISDN_FsmNew(&l1fsm_s, L1SFnList, ARRAY_SIZE(L1SFnList)); } void diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c index 7243a6746f8b..9ff0903a0e89 100644 --- a/drivers/isdn/mISDN/layer2.c +++ b/drivers/isdn/mISDN/layer2.c @@ -2247,15 +2247,26 @@ static struct Bprotocol X75SLP = { int Isdnl2_Init(u_int *deb) { + int res; debug = deb; mISDN_register_Bprotocol(&X75SLP); l2fsm.state_count = L2_STATE_COUNT; l2fsm.event_count = L2_EVENT_COUNT; l2fsm.strEvent = strL2Event; l2fsm.strState = strL2State; - mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList)); - TEIInit(deb); + res = mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList)); + if (res) + goto error; + res = TEIInit(deb); + if (res) + goto error_fsm; return 0; + +error_fsm: + mISDN_FsmFree(&l2fsm); +error: + mISDN_unregister_Bprotocol(&X75SLP); + return res; } void diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c index 908127efccf8..12d9e5f4beb1 100644 --- a/drivers/isdn/mISDN/tei.c +++ b/drivers/isdn/mISDN/tei.c @@ -1387,23 +1387,37 @@ create_teimanager(struct mISDNdevice *dev) int TEIInit(u_int *deb) { + int res; debug = deb; teifsmu.state_count = TEI_STATE_COUNT; teifsmu.event_count = TEI_EVENT_COUNT; teifsmu.strEvent = strTeiEvent; teifsmu.strState = strTeiState; - mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser)); + res = mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser)); + if (res) + goto error; teifsmn.state_count = TEI_STATE_COUNT; teifsmn.event_count = TEI_EVENT_COUNT; teifsmn.strEvent = strTeiEvent; teifsmn.strState = strTeiState; - mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet)); + res = mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet)); + if (res) + goto error_smn; deactfsm.state_count = DEACT_STATE_COUNT; deactfsm.event_count = DEACT_EVENT_COUNT; deactfsm.strEvent = strDeactEvent; deactfsm.strState = strDeactState; - mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList)); + res = mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList)); + if (res) + goto error_deact; return 0; + +error_deact: + mISDN_FsmFree(&teifsmn); +error_smn: + mISDN_FsmFree(&teifsmu); +error: + return res; } void TEIFree(void) diff --git a/drivers/lightnvm/pblk-rb.c b/drivers/lightnvm/pblk-rb.c index 5ecc154f6831..9bc32578a766 100644 --- a/drivers/lightnvm/pblk-rb.c +++ b/drivers/lightnvm/pblk-rb.c @@ -657,7 +657,7 @@ try: * be directed to disk. */ int pblk_rb_copy_to_bio(struct pblk_rb *rb, struct bio *bio, sector_t lba, - struct ppa_addr ppa, int bio_iter) + struct ppa_addr ppa, int bio_iter, bool advanced_bio) { struct pblk *pblk = container_of(rb, struct pblk, rwb); struct pblk_rb_entry *entry; @@ -694,7 +694,7 @@ int pblk_rb_copy_to_bio(struct pblk_rb *rb, struct bio *bio, sector_t lba, * filled with data from the cache). If part of the data resides on the * media, we will read later on */ - if (unlikely(!bio->bi_iter.bi_idx)) + if (unlikely(!advanced_bio)) bio_advance(bio, bio_iter * PBLK_EXPOSED_PAGE_SIZE); data = bio_data(bio); diff --git a/drivers/lightnvm/pblk-read.c b/drivers/lightnvm/pblk-read.c index 4e5c48f3de62..d682e89e6493 100644 --- a/drivers/lightnvm/pblk-read.c +++ b/drivers/lightnvm/pblk-read.c @@ -26,7 +26,7 @@ */ static int pblk_read_from_cache(struct pblk *pblk, struct bio *bio, sector_t lba, struct ppa_addr ppa, - int bio_iter) + int bio_iter, bool advanced_bio) { #ifdef CONFIG_NVM_DEBUG /* Callers must ensure that the ppa points to a cache address */ @@ -34,7 +34,8 @@ static int pblk_read_from_cache(struct pblk *pblk, struct bio *bio, BUG_ON(!pblk_addr_in_cache(ppa)); #endif - return pblk_rb_copy_to_bio(&pblk->rwb, bio, lba, ppa, bio_iter); + return pblk_rb_copy_to_bio(&pblk->rwb, bio, lba, ppa, + bio_iter, advanced_bio); } static void pblk_read_ppalist_rq(struct pblk *pblk, struct nvm_rq *rqd, @@ -44,7 +45,7 @@ static void pblk_read_ppalist_rq(struct pblk *pblk, struct nvm_rq *rqd, struct ppa_addr ppas[PBLK_MAX_REQ_ADDRS]; sector_t blba = pblk_get_lba(bio); int nr_secs = rqd->nr_ppas; - int advanced_bio = 0; + bool advanced_bio = false; int i, j = 0; /* logic error: lba out-of-bounds. Ignore read request */ @@ -62,19 +63,26 @@ static void pblk_read_ppalist_rq(struct pblk *pblk, struct nvm_rq *rqd, retry: if (pblk_ppa_empty(p)) { WARN_ON(test_and_set_bit(i, read_bitmap)); - continue; + + if (unlikely(!advanced_bio)) { + bio_advance(bio, (i) * PBLK_EXPOSED_PAGE_SIZE); + advanced_bio = true; + } + + goto next; } /* Try to read from write buffer. The address is later checked * on the write buffer to prevent retrieving overwritten data. */ if (pblk_addr_in_cache(p)) { - if (!pblk_read_from_cache(pblk, bio, lba, p, i)) { + if (!pblk_read_from_cache(pblk, bio, lba, p, i, + advanced_bio)) { pblk_lookup_l2p_seq(pblk, &p, lba, 1); goto retry; } WARN_ON(test_and_set_bit(i, read_bitmap)); - advanced_bio = 1; + advanced_bio = true; #ifdef CONFIG_NVM_DEBUG atomic_long_inc(&pblk->cache_reads); #endif @@ -83,6 +91,7 @@ retry: rqd->ppa_list[j++] = p; } +next: if (advanced_bio) bio_advance(bio, PBLK_EXPOSED_PAGE_SIZE); } @@ -282,7 +291,7 @@ retry: * write buffer to prevent retrieving overwritten data. */ if (pblk_addr_in_cache(ppa)) { - if (!pblk_read_from_cache(pblk, bio, lba, ppa, 0)) { + if (!pblk_read_from_cache(pblk, bio, lba, ppa, 0, 1)) { pblk_lookup_l2p_seq(pblk, &ppa, lba, 1); goto retry; } diff --git a/drivers/lightnvm/pblk.h b/drivers/lightnvm/pblk.h index 0c5692cc2f60..67e623bd5c2d 100644 --- a/drivers/lightnvm/pblk.h +++ b/drivers/lightnvm/pblk.h @@ -670,7 +670,7 @@ unsigned int pblk_rb_read_to_bio_list(struct pblk_rb *rb, struct bio *bio, struct list_head *list, unsigned int max); int pblk_rb_copy_to_bio(struct pblk_rb *rb, struct bio *bio, sector_t lba, - struct ppa_addr ppa, int bio_iter); + struct ppa_addr ppa, int bio_iter, bool advanced_bio); unsigned int pblk_rb_read_commit(struct pblk_rb *rb, unsigned int entries); unsigned int pblk_rb_sync_init(struct pblk_rb *rb, unsigned long *flags); diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index ac91fd0d62c6..cbca5e51b975 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -92,7 +92,7 @@ static struct mbox_controller pcc_mbox_ctrl = {}; */ static struct mbox_chan *get_pcc_channel(int id) { - if (id < 0 || id > pcc_mbox_ctrl.num_chans) + if (id < 0 || id >= pcc_mbox_ctrl.num_chans) return ERR_PTR(-ENOENT); return &pcc_mbox_channels[id]; diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index f4eace5ea184..40f3cd7eab0f 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -156,7 +156,8 @@ static int read_sb_page(struct mddev *mddev, loff_t offset, rdev_for_each(rdev, mddev) { if (! test_bit(In_sync, &rdev->flags) - || test_bit(Faulty, &rdev->flags)) + || test_bit(Faulty, &rdev->flags) + || test_bit(Bitmap_sync, &rdev->flags)) continue; target = offset + index * (PAGE_SIZE/512); diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index 850ff6c67994..44f4a8ac95bd 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -1258,8 +1258,7 @@ EXPORT_SYMBOL_GPL(dm_bufio_write_dirty_buffers_async); */ int dm_bufio_write_dirty_buffers(struct dm_bufio_client *c) { - blk_status_t a; - int f; + int a, f; unsigned long buffers_processed = 0; struct dm_buffer *b, *tmp; diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c index 1b224aa9cf15..3acce09bba35 100644 --- a/drivers/md/dm-integrity.c +++ b/drivers/md/dm-integrity.c @@ -1587,16 +1587,18 @@ retry: if (likely(ic->mode == 'J')) { if (dio->write) { unsigned next_entry, i, pos; - unsigned ws, we; + unsigned ws, we, range_sectors; - dio->range.n_sectors = min(dio->range.n_sectors, ic->free_sectors); + dio->range.n_sectors = min(dio->range.n_sectors, + ic->free_sectors << ic->sb->log2_sectors_per_block); if (unlikely(!dio->range.n_sectors)) goto sleep; - ic->free_sectors -= dio->range.n_sectors; + range_sectors = dio->range.n_sectors >> ic->sb->log2_sectors_per_block; + ic->free_sectors -= range_sectors; journal_section = ic->free_section; journal_entry = ic->free_section_entry; - next_entry = ic->free_section_entry + dio->range.n_sectors; + next_entry = ic->free_section_entry + range_sectors; ic->free_section_entry = next_entry % ic->journal_section_entries; ic->free_section += next_entry / ic->journal_section_entries; ic->n_uncommitted_sections += next_entry / ic->journal_section_entries; @@ -1727,6 +1729,8 @@ static void pad_uncommitted(struct dm_integrity_c *ic) wraparound_section(ic, &ic->free_section); ic->n_uncommitted_sections++; } + WARN_ON(ic->journal_sections * ic->journal_section_entries != + (ic->n_uncommitted_sections + ic->n_committed_sections) * ic->journal_section_entries + ic->free_sectors); } static void integrity_commit(struct work_struct *w) @@ -1821,6 +1825,9 @@ static void do_journal_write(struct dm_integrity_c *ic, unsigned write_start, { unsigned i, j, n; struct journal_completion comp; + struct blk_plug plug; + + blk_start_plug(&plug); comp.ic = ic; comp.in_flight = (atomic_t)ATOMIC_INIT(1); @@ -1945,6 +1952,8 @@ skip_io: dm_bufio_write_dirty_buffers_async(ic->bufio); + blk_finish_plug(&plug); + complete_journal_op(&comp); wait_for_completion_io(&comp.comp); @@ -3019,6 +3028,11 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv) ti->error = "Block size doesn't match the information in superblock"; goto bad; } + if (!le32_to_cpu(ic->sb->journal_sections)) { + r = -EINVAL; + ti->error = "Corrupted superblock, journal_sections is 0"; + goto bad; + } /* make sure that ti->max_io_len doesn't overflow */ if (ic->sb->log2_interleave_sectors < MIN_LOG2_INTERLEAVE_SECTORS || ic->sb->log2_interleave_sectors > MAX_LOG2_INTERLEAVE_SECTORS) { diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 2e10c2f13a34..5bfe285ea9d1 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -208,6 +208,7 @@ struct raid_dev { #define RT_FLAG_RS_BITMAP_LOADED 2 #define RT_FLAG_UPDATE_SBS 3 #define RT_FLAG_RESHAPE_RS 4 +#define RT_FLAG_RS_SUSPENDED 5 /* Array elements of 64 bit needed for rebuild/failed disk bits */ #define DISKS_ARRAY_ELEMS ((MAX_RAID_DEVICES + (sizeof(uint64_t) * 8 - 1)) / sizeof(uint64_t) / 8) @@ -564,9 +565,10 @@ static const char *raid10_md_layout_to_format(int layout) if (__raid10_near_copies(layout) > 1) return "near"; - WARN_ON(__raid10_far_copies(layout) < 2); + if (__raid10_far_copies(layout) > 1) + return "far"; - return "far"; + return "unknown"; } /* Return md raid10 algorithm for @name */ @@ -2540,11 +2542,6 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs) if (!freshest) return 0; - if (validate_raid_redundancy(rs)) { - rs->ti->error = "Insufficient redundancy to activate array"; - return -EINVAL; - } - /* * Validation of the freshest device provides the source of * validation for the remaining devices. @@ -2553,6 +2550,11 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs) if (super_validate(rs, freshest)) return -EINVAL; + if (validate_raid_redundancy(rs)) { + rs->ti->error = "Insufficient redundancy to activate array"; + return -EINVAL; + } + rdev_for_each(rdev, mddev) if (!test_bit(Journal, &rdev->flags) && rdev != freshest && @@ -3168,6 +3170,7 @@ static int raid_ctr(struct dm_target *ti, unsigned int argc, char **argv) } mddev_suspend(&rs->md); + set_bit(RT_FLAG_RS_SUSPENDED, &rs->runtime_flags); /* Try to adjust the raid4/5/6 stripe cache size to the stripe size */ if (rs_is_raid456(rs)) { @@ -3625,7 +3628,7 @@ static void raid_postsuspend(struct dm_target *ti) { struct raid_set *rs = ti->private; - if (!rs->md.suspended) + if (!test_and_set_bit(RT_FLAG_RS_SUSPENDED, &rs->runtime_flags)) mddev_suspend(&rs->md); rs->md.ro = 1; @@ -3759,7 +3762,7 @@ static int rs_start_reshape(struct raid_set *rs) return r; /* Need to be resumed to be able to start reshape, recovery is frozen until raid_resume() though */ - if (mddev->suspended) + if (test_and_clear_bit(RT_FLAG_RS_SUSPENDED, &rs->runtime_flags)) mddev_resume(mddev); /* @@ -3786,8 +3789,8 @@ static int rs_start_reshape(struct raid_set *rs) } /* Suspend because a resume will happen in raid_resume() */ - if (!mddev->suspended) - mddev_suspend(mddev); + set_bit(RT_FLAG_RS_SUSPENDED, &rs->runtime_flags); + mddev_suspend(mddev); /* * Now reshape got set up, update superblocks to @@ -3883,13 +3886,13 @@ static void raid_resume(struct dm_target *ti) if (!(rs->ctr_flags & RESUME_STAY_FROZEN_FLAGS)) clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery); - if (mddev->suspended) + if (test_and_clear_bit(RT_FLAG_RS_SUSPENDED, &rs->runtime_flags)) mddev_resume(mddev); } static struct target_type raid_target = { .name = "raid", - .version = {1, 11, 1}, + .version = {1, 12, 1}, .module = THIS_MODULE, .ctr = raid_ctr, .dtr = raid_dtr, diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index a39bcd9b982a..28a4071cdf85 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -20,6 +20,7 @@ #include <linux/atomic.h> #include <linux/blk-mq.h> #include <linux/mount.h> +#include <linux/dax.h> #define DM_MSG_PREFIX "table" @@ -1630,6 +1631,37 @@ static bool dm_table_supports_flush(struct dm_table *t, unsigned long flush) return false; } +static int device_dax_write_cache_enabled(struct dm_target *ti, + struct dm_dev *dev, sector_t start, + sector_t len, void *data) +{ + struct dax_device *dax_dev = dev->dax_dev; + + if (!dax_dev) + return false; + + if (dax_write_cache_enabled(dax_dev)) + return true; + return false; +} + +static int dm_table_supports_dax_write_cache(struct dm_table *t) +{ + struct dm_target *ti; + unsigned i; + + for (i = 0; i < dm_table_get_num_targets(t); i++) { + ti = dm_table_get_target(t, i); + + if (ti->type->iterate_devices && + ti->type->iterate_devices(ti, + device_dax_write_cache_enabled, NULL)) + return true; + } + + return false; +} + static int device_is_nonrot(struct dm_target *ti, struct dm_dev *dev, sector_t start, sector_t len, void *data) { @@ -1785,6 +1817,9 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q, } blk_queue_write_cache(q, wc, fua); + if (dm_table_supports_dax_write_cache(t)) + dax_write_cache(t->md->dax_dev, true); + /* Ensure that all underlying devices are non-rotational. */ if (dm_table_all_devices_attribute(t, device_is_nonrot)) queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q); diff --git a/drivers/md/dm-verity-fec.c b/drivers/md/dm-verity-fec.c index 504ba3fa328b..e13f90832b6b 100644 --- a/drivers/md/dm-verity-fec.c +++ b/drivers/md/dm-verity-fec.c @@ -308,19 +308,14 @@ static int fec_alloc_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio) { unsigned n; - if (!fio->rs) { - fio->rs = mempool_alloc(v->fec->rs_pool, 0); - if (unlikely(!fio->rs)) { - DMERR("failed to allocate RS"); - return -ENOMEM; - } - } + if (!fio->rs) + fio->rs = mempool_alloc(v->fec->rs_pool, GFP_NOIO); fec_for_each_prealloc_buffer(n) { if (fio->bufs[n]) continue; - fio->bufs[n] = mempool_alloc(v->fec->prealloc_pool, GFP_NOIO); + fio->bufs[n] = mempool_alloc(v->fec->prealloc_pool, GFP_NOWAIT); if (unlikely(!fio->bufs[n])) { DMERR("failed to allocate FEC buffer"); return -ENOMEM; @@ -332,22 +327,16 @@ static int fec_alloc_bufs(struct dm_verity *v, struct dm_verity_fec_io *fio) if (fio->bufs[n]) continue; - fio->bufs[n] = mempool_alloc(v->fec->extra_pool, GFP_NOIO); + fio->bufs[n] = mempool_alloc(v->fec->extra_pool, GFP_NOWAIT); /* we can manage with even one buffer if necessary */ if (unlikely(!fio->bufs[n])) break; } fio->nbufs = n; - if (!fio->output) { + if (!fio->output) fio->output = mempool_alloc(v->fec->output_pool, GFP_NOIO); - if (!fio->output) { - DMERR("failed to allocate FEC page"); - return -ENOMEM; - } - } - return 0; } diff --git a/drivers/md/dm-zoned-metadata.c b/drivers/md/dm-zoned-metadata.c index 884ff7c170a0..a4fa2ada6883 100644 --- a/drivers/md/dm-zoned-metadata.c +++ b/drivers/md/dm-zoned-metadata.c @@ -624,7 +624,7 @@ static int dmz_write_sb(struct dmz_metadata *zmd, unsigned int set) ret = dmz_rdwr_block(zmd, REQ_OP_WRITE, block, mblk->page); if (ret == 0) - ret = blkdev_issue_flush(zmd->dev->bdev, GFP_KERNEL, NULL); + ret = blkdev_issue_flush(zmd->dev->bdev, GFP_NOIO, NULL); return ret; } @@ -658,7 +658,7 @@ static int dmz_write_dirty_mblocks(struct dmz_metadata *zmd, /* Flush drive cache (this will also sync data) */ if (ret == 0) - ret = blkdev_issue_flush(zmd->dev->bdev, GFP_KERNEL, NULL); + ret = blkdev_issue_flush(zmd->dev->bdev, GFP_NOIO, NULL); return ret; } @@ -722,7 +722,7 @@ int dmz_flush_metadata(struct dmz_metadata *zmd) /* If there are no dirty metadata blocks, just flush the device cache */ if (list_empty(&write_list)) { - ret = blkdev_issue_flush(zmd->dev->bdev, GFP_KERNEL, NULL); + ret = blkdev_issue_flush(zmd->dev->bdev, GFP_NOIO, NULL); goto out; } @@ -927,7 +927,7 @@ static int dmz_recover_mblocks(struct dmz_metadata *zmd, unsigned int dst_set) (zmd->nr_meta_zones << zmd->dev->zone_nr_blocks_shift); } - page = alloc_page(GFP_KERNEL); + page = alloc_page(GFP_NOIO); if (!page) return -ENOMEM; @@ -1183,7 +1183,7 @@ static int dmz_update_zone(struct dmz_metadata *zmd, struct dm_zone *zone) /* Get zone information from disk */ ret = blkdev_report_zones(zmd->dev->bdev, dmz_start_sect(zmd, zone), - &blkz, &nr_blkz, GFP_KERNEL); + &blkz, &nr_blkz, GFP_NOIO); if (ret) { dmz_dev_err(zmd->dev, "Get zone %u report failed", dmz_id(zmd, zone)); @@ -1257,7 +1257,7 @@ static int dmz_reset_zone(struct dmz_metadata *zmd, struct dm_zone *zone) ret = blkdev_reset_zones(dev->bdev, dmz_start_sect(zmd, zone), - dev->zone_nr_sectors, GFP_KERNEL); + dev->zone_nr_sectors, GFP_NOIO); if (ret) { dmz_dev_err(dev, "Reset zone %u failed %d", dmz_id(zmd, zone), ret); diff --git a/drivers/md/dm-zoned-reclaim.c b/drivers/md/dm-zoned-reclaim.c index 05c0a126f5c8..44a119e12f1a 100644 --- a/drivers/md/dm-zoned-reclaim.c +++ b/drivers/md/dm-zoned-reclaim.c @@ -75,7 +75,7 @@ static int dmz_reclaim_align_wp(struct dmz_reclaim *zrc, struct dm_zone *zone, nr_blocks = block - wp_block; ret = blkdev_issue_zeroout(zrc->dev->bdev, dmz_start_sect(zmd, zone) + dmz_blk2sect(wp_block), - dmz_blk2sect(nr_blocks), GFP_NOFS, false); + dmz_blk2sect(nr_blocks), GFP_NOIO, 0); if (ret) { dmz_dev_err(zrc->dev, "Align zone %u wp %llu to %llu (wp+%u) blocks failed %d", diff --git a/drivers/md/dm-zoned-target.c b/drivers/md/dm-zoned-target.c index 2b538fa817f4..b08bbbd4d902 100644 --- a/drivers/md/dm-zoned-target.c +++ b/drivers/md/dm-zoned-target.c @@ -541,7 +541,7 @@ static void dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio) int ret; /* Create a new chunk work */ - cw = kmalloc(sizeof(struct dm_chunk_work), GFP_NOFS); + cw = kmalloc(sizeof(struct dm_chunk_work), GFP_NOIO); if (!cw) goto out; @@ -588,7 +588,7 @@ static int dmz_map(struct dm_target *ti, struct bio *bio) bio->bi_bdev = dev->bdev; - if (!nr_sectors && (bio_op(bio) != REQ_OP_FLUSH) && (bio_op(bio) != REQ_OP_WRITE)) + if (!nr_sectors && bio_op(bio) != REQ_OP_WRITE) return DM_MAPIO_REMAPPED; /* The BIO should be block aligned */ @@ -603,7 +603,7 @@ static int dmz_map(struct dm_target *ti, struct bio *bio) bioctx->status = BLK_STS_OK; /* Set the BIO pending in the flush list */ - if (bio_op(bio) == REQ_OP_FLUSH || (!nr_sectors && bio_op(bio) == REQ_OP_WRITE)) { + if (!nr_sectors && bio_op(bio) == REQ_OP_WRITE) { spin_lock(&dmz->flush_lock); bio_list_add(&dmz->flush_list, bio); spin_unlock(&dmz->flush_lock); @@ -785,7 +785,7 @@ static int dmz_ctr(struct dm_target *ti, unsigned int argc, char **argv) /* Chunk BIO work */ mutex_init(&dmz->chunk_lock); - INIT_RADIX_TREE(&dmz->chunk_rxtree, GFP_NOFS); + INIT_RADIX_TREE(&dmz->chunk_rxtree, GFP_KERNEL); dmz->chunk_wq = alloc_workqueue("dmz_cwq_%s", WQ_MEM_RECLAIM | WQ_UNBOUND, 0, dev->name); if (!dmz->chunk_wq) { diff --git a/drivers/md/md.c b/drivers/md/md.c index 8cdca0296749..b01e458d31e9 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2287,7 +2287,7 @@ static void export_array(struct mddev *mddev) static bool set_in_sync(struct mddev *mddev) { - WARN_ON_ONCE(!spin_is_locked(&mddev->lock)); + WARN_ON_ONCE(NR_CPUS != 1 && !spin_is_locked(&mddev->lock)); if (!mddev->in_sync) { mddev->sync_checkers++; spin_unlock(&mddev->lock); @@ -7996,7 +7996,7 @@ bool md_write_start(struct mddev *mddev, struct bio *bi) if (mddev->safemode == 1) mddev->safemode = 0; /* sync_checkers is always 0 when writes_pending is in per-cpu mode */ - if (mddev->in_sync || !mddev->sync_checkers) { + if (mddev->in_sync || mddev->sync_checkers) { spin_lock(&mddev->lock); if (mddev->in_sync) { mddev->in_sync = 0; @@ -8656,6 +8656,9 @@ void md_check_recovery(struct mddev *mddev) if (mddev_trylock(mddev)) { int spares = 0; + if (!mddev->external && mddev->safemode == 1) + mddev->safemode = 0; + if (mddev->ro) { struct md_rdev *rdev; if (!mddev->external && mddev->in_sync) diff --git a/drivers/md/md.h b/drivers/md/md.h index 991f0fe2dcc6..09db03455801 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -134,7 +134,9 @@ enum flag_bits { Faulty, /* device is known to have a fault */ In_sync, /* device is in_sync with rest of array */ Bitmap_sync, /* ..actually, not quite In_sync. Need a - * bitmap-based recovery to get fully in sync + * bitmap-based recovery to get fully in sync. + * The bit is only meaningful before device + * has been passed to pers->hot_add_disk. */ WriteMostly, /* Avoid reading if at all possible */ AutoDetected, /* added by auto-detect */ @@ -729,58 +731,4 @@ static inline void mddev_check_write_zeroes(struct mddev *mddev, struct bio *bio !bdev_get_queue(bio->bi_bdev)->limits.max_write_zeroes_sectors) mddev->queue->limits.max_write_zeroes_sectors = 0; } - -/* Maximum size of each resync request */ -#define RESYNC_BLOCK_SIZE (64*1024) -#define RESYNC_PAGES ((RESYNC_BLOCK_SIZE + PAGE_SIZE-1) / PAGE_SIZE) - -/* for managing resync I/O pages */ -struct resync_pages { - unsigned idx; /* for get/put page from the pool */ - void *raid_bio; - struct page *pages[RESYNC_PAGES]; -}; - -static inline int resync_alloc_pages(struct resync_pages *rp, - gfp_t gfp_flags) -{ - int i; - - for (i = 0; i < RESYNC_PAGES; i++) { - rp->pages[i] = alloc_page(gfp_flags); - if (!rp->pages[i]) - goto out_free; - } - - return 0; - -out_free: - while (--i >= 0) - put_page(rp->pages[i]); - return -ENOMEM; -} - -static inline void resync_free_pages(struct resync_pages *rp) -{ - int i; - - for (i = 0; i < RESYNC_PAGES; i++) - put_page(rp->pages[i]); -} - -static inline void resync_get_all_pages(struct resync_pages *rp) -{ - int i; - - for (i = 0; i < RESYNC_PAGES; i++) - get_page(rp->pages[i]); -} - -static inline struct page *resync_fetch_page(struct resync_pages *rp, - unsigned idx) -{ - if (WARN_ON_ONCE(idx >= RESYNC_PAGES)) - return NULL; - return rp->pages[idx]; -} #endif /* _MD_MD_H */ diff --git a/drivers/md/raid1-10.c b/drivers/md/raid1-10.c new file mode 100644 index 000000000000..9f2670b45f31 --- /dev/null +++ b/drivers/md/raid1-10.c @@ -0,0 +1,81 @@ +/* Maximum size of each resync request */ +#define RESYNC_BLOCK_SIZE (64*1024) +#define RESYNC_PAGES ((RESYNC_BLOCK_SIZE + PAGE_SIZE-1) / PAGE_SIZE) + +/* for managing resync I/O pages */ +struct resync_pages { + void *raid_bio; + struct page *pages[RESYNC_PAGES]; +}; + +static inline int resync_alloc_pages(struct resync_pages *rp, + gfp_t gfp_flags) +{ + int i; + + for (i = 0; i < RESYNC_PAGES; i++) { + rp->pages[i] = alloc_page(gfp_flags); + if (!rp->pages[i]) + goto out_free; + } + + return 0; + +out_free: + while (--i >= 0) + put_page(rp->pages[i]); + return -ENOMEM; +} + +static inline void resync_free_pages(struct resync_pages *rp) +{ + int i; + + for (i = 0; i < RESYNC_PAGES; i++) + put_page(rp->pages[i]); +} + +static inline void resync_get_all_pages(struct resync_pages *rp) +{ + int i; + + for (i = 0; i < RESYNC_PAGES; i++) + get_page(rp->pages[i]); +} + +static inline struct page *resync_fetch_page(struct resync_pages *rp, + unsigned idx) +{ + if (WARN_ON_ONCE(idx >= RESYNC_PAGES)) + return NULL; + return rp->pages[idx]; +} + +/* + * 'strct resync_pages' stores actual pages used for doing the resync + * IO, and it is per-bio, so make .bi_private points to it. + */ +static inline struct resync_pages *get_resync_pages(struct bio *bio) +{ + return bio->bi_private; +} + +/* generally called after bio_reset() for reseting bvec */ +static void md_bio_reset_resync_pages(struct bio *bio, struct resync_pages *rp, + int size) +{ + int idx = 0; + + /* initialize bvec table again */ + do { + struct page *page = resync_fetch_page(rp, idx); + int len = min_t(int, size, PAGE_SIZE); + + /* + * won't fail because the vec table is big + * enough to hold all these pages + */ + bio_add_page(bio, page, len, 0); + size -= len; + } while (idx++ < RESYNC_PAGES && size > 0); +} diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 3febfc8391fb..f50958ded9f0 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -81,14 +81,7 @@ static void lower_barrier(struct r1conf *conf, sector_t sector_nr); #define raid1_log(md, fmt, args...) \ do { if ((md)->queue) blk_add_trace_msg((md)->queue, "raid1 " fmt, ##args); } while (0) -/* - * 'strct resync_pages' stores actual pages used for doing the resync - * IO, and it is per-bio, so make .bi_private points to it. - */ -static inline struct resync_pages *get_resync_pages(struct bio *bio) -{ - return bio->bi_private; -} +#include "raid1-10.c" /* * for resync bio, r1bio pointer can be retrieved from the per-bio @@ -170,7 +163,6 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data) resync_get_all_pages(rp); } - rp->idx = 0; rp->raid_bio = r1_bio; bio->bi_private = rp; } @@ -492,10 +484,6 @@ static void raid1_end_write_request(struct bio *bio) } if (behind) { - /* we release behind master bio when all write are done */ - if (r1_bio->behind_master_bio == bio) - to_put = NULL; - if (test_bit(WriteMostly, &rdev->flags)) atomic_dec(&r1_bio->behind_remaining); @@ -802,8 +790,7 @@ static void flush_bio_list(struct r1conf *conf, struct bio *bio) bio->bi_next = NULL; bio->bi_bdev = rdev->bdev; if (test_bit(Faulty, &rdev->flags)) { - bio->bi_status = BLK_STS_IOERR; - bio_endio(bio); + bio_io_error(bio); } else if (unlikely((bio_op(bio) == REQ_OP_DISCARD) && !blk_queue_discard(bdev_get_queue(bio->bi_bdev)))) /* Just ignore it */ @@ -1088,7 +1075,7 @@ static void unfreeze_array(struct r1conf *conf) wake_up(&conf->wait_barrier); } -static struct bio *alloc_behind_master_bio(struct r1bio *r1_bio, +static void alloc_behind_master_bio(struct r1bio *r1_bio, struct bio *bio) { int size = bio->bi_iter.bi_size; @@ -1098,11 +1085,13 @@ static struct bio *alloc_behind_master_bio(struct r1bio *r1_bio, behind_bio = bio_alloc_mddev(GFP_NOIO, vcnt, r1_bio->mddev); if (!behind_bio) - goto fail; + return; /* discard op, we don't support writezero/writesame yet */ - if (!bio_has_data(bio)) + if (!bio_has_data(bio)) { + behind_bio->bi_iter.bi_size = size; goto skip_copy; + } while (i < vcnt && size) { struct page *page; @@ -1123,14 +1112,13 @@ skip_copy: r1_bio->behind_master_bio = behind_bio;; set_bit(R1BIO_BehindIO, &r1_bio->state); - return behind_bio; + return; free_pages: pr_debug("%dB behind alloc failed, doing sync I/O\n", bio->bi_iter.bi_size); bio_free_pages(behind_bio); -fail: - return behind_bio; + bio_put(behind_bio); } struct raid1_plug_cb { @@ -1483,7 +1471,7 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, (atomic_read(&bitmap->behind_writes) < mddev->bitmap_info.max_write_behind) && !waitqueue_active(&bitmap->behind_wait)) { - mbio = alloc_behind_master_bio(r1_bio, bio); + alloc_behind_master_bio(r1_bio, bio); } bitmap_startwrite(bitmap, r1_bio->sector, @@ -1493,14 +1481,11 @@ static void raid1_write_request(struct mddev *mddev, struct bio *bio, first_clone = 0; } - if (!mbio) { - if (r1_bio->behind_master_bio) - mbio = bio_clone_fast(r1_bio->behind_master_bio, - GFP_NOIO, - mddev->bio_set); - else - mbio = bio_clone_fast(bio, GFP_NOIO, mddev->bio_set); - } + if (r1_bio->behind_master_bio) + mbio = bio_clone_fast(r1_bio->behind_master_bio, + GFP_NOIO, mddev->bio_set); + else + mbio = bio_clone_fast(bio, GFP_NOIO, mddev->bio_set); if (r1_bio->behind_master_bio) { if (test_bit(WriteMostly, &conf->mirrors[i].rdev->flags)) @@ -2086,10 +2071,7 @@ static void process_checks(struct r1bio *r1_bio) /* Fix variable parts of all bios */ vcnt = (r1_bio->sectors + PAGE_SIZE / 512 - 1) >> (PAGE_SHIFT - 9); for (i = 0; i < conf->raid_disks * 2; i++) { - int j; - int size; blk_status_t status; - struct bio_vec *bi; struct bio *b = r1_bio->bios[i]; struct resync_pages *rp = get_resync_pages(b); if (b->bi_end_io != end_sync_read) @@ -2098,8 +2080,6 @@ static void process_checks(struct r1bio *r1_bio) status = b->bi_status; bio_reset(b); b->bi_status = status; - b->bi_vcnt = vcnt; - b->bi_iter.bi_size = r1_bio->sectors << 9; b->bi_iter.bi_sector = r1_bio->sector + conf->mirrors[i].rdev->data_offset; b->bi_bdev = conf->mirrors[i].rdev->bdev; @@ -2107,15 +2087,8 @@ static void process_checks(struct r1bio *r1_bio) rp->raid_bio = r1_bio; b->bi_private = rp; - size = b->bi_iter.bi_size; - bio_for_each_segment_all(bi, b, j) { - bi->bv_offset = 0; - if (size > PAGE_SIZE) - bi->bv_len = PAGE_SIZE; - else - bi->bv_len = size; - size -= PAGE_SIZE; - } + /* initialize bvec table again */ + md_bio_reset_resync_pages(b, rp, r1_bio->sectors << 9); } for (primary = 0; primary < conf->raid_disks * 2; primary++) if (r1_bio->bios[primary]->bi_end_io == end_sync_read && @@ -2366,8 +2339,6 @@ static int narrow_write_error(struct r1bio *r1_bio, int i) wbio = bio_clone_fast(r1_bio->behind_master_bio, GFP_NOIO, mddev->bio_set); - /* We really need a _all clone */ - wbio->bi_iter = (struct bvec_iter){ 0 }; } else { wbio = bio_clone_fast(r1_bio->master_bio, GFP_NOIO, mddev->bio_set); @@ -2619,6 +2590,7 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr, int good_sectors = RESYNC_SECTORS; int min_bad = 0; /* number of sectors that are bad in all devices */ int idx = sector_to_idx(sector_nr); + int page_idx = 0; if (!conf->r1buf_pool) if (init_resync(conf)) @@ -2846,7 +2818,7 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr, bio = r1_bio->bios[i]; rp = get_resync_pages(bio); if (bio->bi_end_io) { - page = resync_fetch_page(rp, rp->idx++); + page = resync_fetch_page(rp, page_idx); /* * won't fail because the vec table is big @@ -2858,7 +2830,7 @@ static sector_t raid1_sync_request(struct mddev *mddev, sector_t sector_nr, nr_sectors += len>>9; sector_nr += len>>9; sync_blocks -= (len>>9); - } while (get_resync_pages(r1_bio->bios[disk]->bi_private)->idx < RESYNC_PAGES); + } while (++page_idx < RESYNC_PAGES); r1_bio->sectors = nr_sectors; diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 5026e7ad51d3..f55d4cc085f6 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -110,14 +110,7 @@ static void end_reshape(struct r10conf *conf); #define raid10_log(md, fmt, args...) \ do { if ((md)->queue) blk_add_trace_msg((md)->queue, "raid10 " fmt, ##args); } while (0) -/* - * 'strct resync_pages' stores actual pages used for doing the resync - * IO, and it is per-bio, so make .bi_private points to it. - */ -static inline struct resync_pages *get_resync_pages(struct bio *bio) -{ - return bio->bi_private; -} +#include "raid1-10.c" /* * for resync bio, r10bio pointer can be retrieved from the per-bio @@ -221,7 +214,6 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data) resync_get_all_pages(rp); } - rp->idx = 0; rp->raid_bio = r10_bio; bio->bi_private = rp; if (rbio) { @@ -913,8 +905,7 @@ static void flush_pending_writes(struct r10conf *conf) bio->bi_next = NULL; bio->bi_bdev = rdev->bdev; if (test_bit(Faulty, &rdev->flags)) { - bio->bi_status = BLK_STS_IOERR; - bio_endio(bio); + bio_io_error(bio); } else if (unlikely((bio_op(bio) == REQ_OP_DISCARD) && !blk_queue_discard(bdev_get_queue(bio->bi_bdev)))) /* Just ignore it */ @@ -1098,8 +1089,7 @@ static void raid10_unplug(struct blk_plug_cb *cb, bool from_schedule) bio->bi_next = NULL; bio->bi_bdev = rdev->bdev; if (test_bit(Faulty, &rdev->flags)) { - bio->bi_status = BLK_STS_IOERR; - bio_endio(bio); + bio_io_error(bio); } else if (unlikely((bio_op(bio) == REQ_OP_DISCARD) && !blk_queue_discard(bdev_get_queue(bio->bi_bdev)))) /* Just ignore it */ @@ -2087,8 +2077,8 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio) rp = get_resync_pages(tbio); bio_reset(tbio); - tbio->bi_vcnt = vcnt; - tbio->bi_iter.bi_size = fbio->bi_iter.bi_size; + md_bio_reset_resync_pages(tbio, rp, fbio->bi_iter.bi_size); + rp->raid_bio = r10_bio; tbio->bi_private = rp; tbio->bi_iter.bi_sector = r10_bio->devs[i].addr; @@ -2853,6 +2843,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr, sector_t sectors_skipped = 0; int chunks_skipped = 0; sector_t chunk_mask = conf->geo.chunk_mask; + int page_idx = 0; if (!conf->r10buf_pool) if (init_resync(conf)) @@ -3355,7 +3346,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr, break; for (bio= biolist ; bio ; bio=bio->bi_next) { struct resync_pages *rp = get_resync_pages(bio); - page = resync_fetch_page(rp, rp->idx++); + page = resync_fetch_page(rp, page_idx); /* * won't fail because the vec table is big enough * to hold all these pages @@ -3364,7 +3355,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr, } nr_sectors += len>>9; sector_nr += len>>9; - } while (get_resync_pages(biolist)->idx < RESYNC_PAGES); + } while (++page_idx < RESYNC_PAGES); r10_bio->sectors = nr_sectors; while (biolist) { diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c index bfa1e907c472..2dcbafa8e66c 100644 --- a/drivers/md/raid5-cache.c +++ b/drivers/md/raid5-cache.c @@ -236,9 +236,10 @@ struct r5l_io_unit { bool need_split_bio; struct bio *split_bio; - unsigned int has_flush:1; /* include flush request */ - unsigned int has_fua:1; /* include fua request */ - unsigned int has_null_flush:1; /* include empty flush request */ + unsigned int has_flush:1; /* include flush request */ + unsigned int has_fua:1; /* include fua request */ + unsigned int has_null_flush:1; /* include null flush request */ + unsigned int has_flush_payload:1; /* include flush payload */ /* * io isn't sent yet, flush/fua request can only be submitted till it's * the first IO in running_ios list @@ -571,6 +572,8 @@ static void r5l_log_endio(struct bio *bio) struct r5l_io_unit *io_deferred; struct r5l_log *log = io->log; unsigned long flags; + bool has_null_flush; + bool has_flush_payload; if (bio->bi_status) md_error(log->rdev->mddev, log->rdev); @@ -580,6 +583,16 @@ static void r5l_log_endio(struct bio *bio) spin_lock_irqsave(&log->io_list_lock, flags); __r5l_set_io_unit_state(io, IO_UNIT_IO_END); + + /* + * if the io doesn't not have null_flush or flush payload, + * it is not safe to access it after releasing io_list_lock. + * Therefore, it is necessary to check the condition with + * the lock held. + */ + has_null_flush = io->has_null_flush; + has_flush_payload = io->has_flush_payload; + if (log->need_cache_flush && !list_empty(&io->stripe_list)) r5l_move_to_end_ios(log); else @@ -600,19 +613,23 @@ static void r5l_log_endio(struct bio *bio) if (log->need_cache_flush) md_wakeup_thread(log->rdev->mddev->thread); - if (io->has_null_flush) { + /* finish flush only io_unit and PAYLOAD_FLUSH only io_unit */ + if (has_null_flush) { struct bio *bi; WARN_ON(bio_list_empty(&io->flush_barriers)); while ((bi = bio_list_pop(&io->flush_barriers)) != NULL) { bio_endio(bi); - atomic_dec(&io->pending_stripe); + if (atomic_dec_and_test(&io->pending_stripe)) { + __r5l_stripe_write_finished(io); + return; + } } } - - /* finish flush only io_unit and PAYLOAD_FLUSH only io_unit */ - if (atomic_read(&io->pending_stripe) == 0) - __r5l_stripe_write_finished(io); + /* decrease pending_stripe for flush payload */ + if (has_flush_payload) + if (atomic_dec_and_test(&io->pending_stripe)) + __r5l_stripe_write_finished(io); } static void r5l_do_submit_io(struct r5l_log *log, struct r5l_io_unit *io) @@ -881,6 +898,11 @@ static void r5l_append_flush_payload(struct r5l_log *log, sector_t sect) payload->size = cpu_to_le32(sizeof(__le64)); payload->flush_stripes[0] = cpu_to_le64(sect); io->meta_offset += meta_size; + /* multiple flush payloads count as one pending_stripe */ + if (!io->has_flush_payload) { + io->has_flush_payload = 1; + atomic_inc(&io->pending_stripe); + } mutex_unlock(&log->io_mutex); } @@ -2540,23 +2562,32 @@ static ssize_t r5c_journal_mode_show(struct mddev *mddev, char *page) */ int r5c_journal_mode_set(struct mddev *mddev, int mode) { - struct r5conf *conf = mddev->private; - struct r5l_log *log = conf->log; - - if (!log) - return -ENODEV; + struct r5conf *conf; + int err; if (mode < R5C_JOURNAL_MODE_WRITE_THROUGH || mode > R5C_JOURNAL_MODE_WRITE_BACK) return -EINVAL; + err = mddev_lock(mddev); + if (err) + return err; + conf = mddev->private; + if (!conf || !conf->log) { + mddev_unlock(mddev); + return -ENODEV; + } + if (raid5_calc_degraded(conf) > 0 && - mode == R5C_JOURNAL_MODE_WRITE_BACK) + mode == R5C_JOURNAL_MODE_WRITE_BACK) { + mddev_unlock(mddev); return -EINVAL; + } mddev_suspend(mddev); conf->log->r5c_journal_mode = mode; mddev_resume(mddev); + mddev_unlock(mddev); pr_debug("md/raid:%s: setting r5c cache mode to %d: %s\n", mdname(mddev), mode, r5c_journal_mode_str[mode]); diff --git a/drivers/md/raid5-ppl.c b/drivers/md/raid5-ppl.c index 77cce3573aa8..44ad5baf3206 100644 --- a/drivers/md/raid5-ppl.c +++ b/drivers/md/raid5-ppl.c @@ -1150,7 +1150,7 @@ int ppl_init_log(struct r5conf *conf) goto err; } - ppl_conf->bs = bioset_create(conf->raid_disks, 0, 0); + ppl_conf->bs = bioset_create(conf->raid_disks, 0, BIOSET_NEED_BVECS); if (!ppl_conf->bs) { ret = -ENOMEM; goto err; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 2ceb338b094b..0fc2748aaf95 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3381,9 +3381,8 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh, sh->dev[i].sector + STRIPE_SECTORS) { struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector); - bi->bi_status = BLK_STS_IOERR; md_write_end(conf->mddev); - bio_endio(bi); + bio_io_error(bi); bi = nextbi; } if (bitmap_end) @@ -3403,9 +3402,8 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh, sh->dev[i].sector + STRIPE_SECTORS) { struct bio *bi2 = r5_next_bio(bi, sh->dev[i].sector); - bi->bi_status = BLK_STS_IOERR; md_write_end(conf->mddev); - bio_endio(bi); + bio_io_error(bi); bi = bi2; } @@ -3429,8 +3427,7 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh, struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector); - bi->bi_status = BLK_STS_IOERR; - bio_endio(bi); + bio_io_error(bi); bi = nextbi; } } @@ -6237,6 +6234,8 @@ static void raid5_do_work(struct work_struct *work) pr_debug("%d stripes handled\n", handled); spin_unlock_irq(&conf->device_lock); + + async_tx_issue_pending_all(); blk_finish_plug(&plug); pr_debug("--- raid5worker inactive\n"); @@ -7951,12 +7950,10 @@ static void end_reshape(struct r5conf *conf) { if (!test_bit(MD_RECOVERY_INTR, &conf->mddev->recovery)) { - struct md_rdev *rdev; spin_lock_irq(&conf->device_lock); conf->previous_raid_disks = conf->raid_disks; - rdev_for_each(rdev, conf->mddev) - rdev->data_offset = rdev->new_data_offset; + md_finish_reshape(conf->mddev); smp_wmb(); conf->reshape_progress = MaxSector; conf->mddev->reshape_position = MaxSector; diff --git a/drivers/media/cec/cec-adap.c b/drivers/media/cec/cec-adap.c index bf45977b2823..d596b601ff42 100644 --- a/drivers/media/cec/cec-adap.c +++ b/drivers/media/cec/cec-adap.c @@ -559,7 +559,7 @@ EXPORT_SYMBOL_GPL(cec_transmit_done); void cec_transmit_attempt_done(struct cec_adapter *adap, u8 status) { - switch (status) { + switch (status & ~CEC_TX_STATUS_MAX_RETRIES) { case CEC_TX_STATUS_OK: cec_transmit_done(adap, status, 0, 0, 0, 0); return; diff --git a/drivers/media/cec/cec-notifier.c b/drivers/media/cec/cec-notifier.c index 74dc1c32080e..08b619d0ea1e 100644 --- a/drivers/media/cec/cec-notifier.c +++ b/drivers/media/cec/cec-notifier.c @@ -87,6 +87,9 @@ EXPORT_SYMBOL_GPL(cec_notifier_put); void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) { + if (n == NULL) + return; + mutex_lock(&n->lock); n->phys_addr = pa; if (n->callback) @@ -100,6 +103,9 @@ void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n, { u16 pa = CEC_PHYS_ADDR_INVALID; + if (n == NULL) + return; + if (edid && edid->extensions) pa = cec_get_edid_phys_addr((const u8 *)edid, EDID_LENGTH * (edid->extensions + 1), NULL); diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c index af694f2066a2..17970cdd55fa 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb-core/dvb_ca_en50221.c @@ -349,7 +349,8 @@ static int dvb_ca_en50221_link_init(struct dvb_ca_private *ca, int slot) /* read the buffer size from the CAM */ if ((ret = ca->pub->write_cam_control(ca->pub, slot, CTRLIF_COMMAND, IRQEN | CMDREG_SR)) != 0) return ret; - if ((ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ / 10)) != 0) + ret = dvb_ca_en50221_wait_if_status(ca, slot, STATUSREG_DA, HZ); + if (ret != 0) return ret; if ((ret = dvb_ca_en50221_read_data(ca, slot, buf, 2)) != 2) return -EIO; @@ -644,72 +645,101 @@ static int dvb_ca_en50221_read_data(struct dvb_ca_private *ca, int slot, } buf_free = dvb_ringbuffer_free(&ca->slot_info[slot].rx_buffer); - if (buf_free < (ca->slot_info[slot].link_buf_size + DVB_RINGBUFFER_PKTHDRSIZE)) { + if (buf_free < (ca->slot_info[slot].link_buf_size + + DVB_RINGBUFFER_PKTHDRSIZE)) { status = -EAGAIN; goto exit; } } - /* check if there is data available */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) - goto exit; - if (!(status & STATUSREG_DA)) { - /* no data */ - status = 0; - goto exit; - } - - /* read the amount of data */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_HIGH)) < 0) - goto exit; - bytes_read = status << 8; - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_SIZE_LOW)) < 0) - goto exit; - bytes_read |= status; + if (ca->pub->read_data && + (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_LINKINIT)) { + if (ebuf == NULL) + status = ca->pub->read_data(ca->pub, slot, buf, + sizeof(buf)); + else + status = ca->pub->read_data(ca->pub, slot, buf, ecount); + if (status < 0) + return status; + bytes_read = status; + if (status == 0) + goto exit; + } else { - /* check it will fit */ - if (ebuf == NULL) { - if (bytes_read > ca->slot_info[slot].link_buf_size) { - pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n", - ca->dvbdev->adapter->num, bytes_read, - ca->slot_info[slot].link_buf_size); - ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; - status = -EIO; + /* check if there is data available */ + status = ca->pub->read_cam_control(ca->pub, slot, + CTRLIF_STATUS); + if (status < 0) goto exit; - } - if (bytes_read < 2) { - pr_err("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n", - ca->dvbdev->adapter->num); - ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; - status = -EIO; + if (!(status & STATUSREG_DA)) { + /* no data */ + status = 0; goto exit; } - } else { - if (bytes_read > ecount) { - pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n", - ca->dvbdev->adapter->num); - status = -EIO; + + /* read the amount of data */ + status = ca->pub->read_cam_control(ca->pub, slot, + CTRLIF_SIZE_HIGH); + if (status < 0) + goto exit; + bytes_read = status << 8; + status = ca->pub->read_cam_control(ca->pub, slot, + CTRLIF_SIZE_LOW); + if (status < 0) goto exit; + bytes_read |= status; + + /* check it will fit */ + if (ebuf == NULL) { + if (bytes_read > ca->slot_info[slot].link_buf_size) { + pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the link buffer size (%i > %i)!\n", + ca->dvbdev->adapter->num, bytes_read, + ca->slot_info[slot].link_buf_size); + ca->slot_info[slot].slot_state = + DVB_CA_SLOTSTATE_LINKINIT; + status = -EIO; + goto exit; + } + if (bytes_read < 2) { + pr_err("dvb_ca adapter %d: CAM sent a buffer that was less than 2 bytes!\n", + ca->dvbdev->adapter->num); + ca->slot_info[slot].slot_state = + DVB_CA_SLOTSTATE_LINKINIT; + status = -EIO; + goto exit; + } + } else { + if (bytes_read > ecount) { + pr_err("dvb_ca adapter %d: CAM tried to send a buffer larger than the ecount size!\n", + ca->dvbdev->adapter->num); + status = -EIO; + goto exit; + } } - } - /* fill the buffer */ - for (i = 0; i < bytes_read; i++) { - /* read byte and check */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_DATA)) < 0) - goto exit; + /* fill the buffer */ + for (i = 0; i < bytes_read; i++) { + /* read byte and check */ + status = ca->pub->read_cam_control(ca->pub, slot, + CTRLIF_DATA); + if (status < 0) + goto exit; - /* OK, store it in the buffer */ - buf[i] = status; - } + /* OK, store it in the buffer */ + buf[i] = status; + } - /* check for read error (RE should now be 0) */ - if ((status = ca->pub->read_cam_control(ca->pub, slot, CTRLIF_STATUS)) < 0) - goto exit; - if (status & STATUSREG_RE) { - ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_LINKINIT; - status = -EIO; - goto exit; + /* check for read error (RE should now be 0) */ + status = ca->pub->read_cam_control(ca->pub, slot, + CTRLIF_STATUS); + if (status < 0) + goto exit; + if (status & STATUSREG_RE) { + ca->slot_info[slot].slot_state = + DVB_CA_SLOTSTATE_LINKINIT; + status = -EIO; + goto exit; + } } /* OK, add it to the receive buffer, or copy into external buffer if supplied */ @@ -762,6 +792,10 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, if (bytes_write > ca->slot_info[slot].link_buf_size) return -EINVAL; + if (ca->pub->write_data && + (ca->slot_info[slot].slot_state != DVB_CA_SLOTSTATE_LINKINIT)) + return ca->pub->write_data(ca->pub, slot, buf, bytes_write); + /* it is possible we are dealing with a single buffer implementation, thus if there is data available for read or if there is even a read already in progress, we do nothing but awake the kernel thread to @@ -1176,7 +1210,8 @@ static int dvb_ca_en50221_thread(void *data) pr_err("dvb_ca adapter %d: DVB CAM link initialisation failed :(\n", ca->dvbdev->adapter->num); - ca->slot_info[slot].slot_state = DVB_CA_SLOTSTATE_INVALID; + ca->slot_info[slot].slot_state = + DVB_CA_SLOTSTATE_UNINITIALISED; dvb_ca_en50221_thread_update_delay(ca); break; } diff --git a/drivers/media/dvb-core/dvb_ca_en50221.h b/drivers/media/dvb-core/dvb_ca_en50221.h index 1e4bbbd34d91..82617bac0875 100644 --- a/drivers/media/dvb-core/dvb_ca_en50221.h +++ b/drivers/media/dvb-core/dvb_ca_en50221.h @@ -41,6 +41,8 @@ * @write_attribute_mem: function for writing attribute memory on the CAM * @read_cam_control: function for reading the control interface on the CAM * @write_cam_control: function for reading the control interface on the CAM + * @read_data: function for reading data (block mode) + * @write_data: function for writing data (block mode) * @slot_reset: function to reset the CAM slot * @slot_shutdown: function to shutdown a CAM slot * @slot_ts_enable: function to enable the Transport Stream on a CAM slot @@ -66,6 +68,11 @@ struct dvb_ca_en50221 { int (*write_cam_control)(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value); + int (*read_data)(struct dvb_ca_en50221 *ca, + int slot, u8 *ebuf, int ecount); + int (*write_data)(struct dvb_ca_en50221 *ca, + int slot, u8 *ebuf, int ecount); + int (*slot_reset)(struct dvb_ca_en50221 *ca, int slot); int (*slot_shutdown)(struct dvb_ca_en50221 *ca, int slot); int (*slot_ts_enable)(struct dvb_ca_en50221 *ca, int slot); diff --git a/drivers/media/dvb-frontends/cxd2841er.c b/drivers/media/dvb-frontends/cxd2841er.c index 08f67d60a7d9..12bff778c97f 100644 --- a/drivers/media/dvb-frontends/cxd2841er.c +++ b/drivers/media/dvb-frontends/cxd2841er.c @@ -3279,7 +3279,10 @@ static int cxd2841er_get_frontend(struct dvb_frontend *fe, else if (priv->state == STATE_ACTIVE_TC) cxd2841er_read_status_tc(fe, &status); - cxd2841er_read_signal_strength(fe); + if (priv->state == STATE_ACTIVE_TC || priv->state == STATE_ACTIVE_S) + cxd2841er_read_signal_strength(fe); + else + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; if (status & FE_HAS_LOCK) { cxd2841er_read_snr(fe); diff --git a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h index 4442e478db72..cd69e187ba7a 100644 --- a/drivers/media/dvb-frontends/drx39xyj/drx_driver.h +++ b/drivers/media/dvb-frontends/drx39xyj/drx_driver.h @@ -307,7 +307,7 @@ int drxbsp_tuner_default_i2c_write_read(struct tuner_instance *tuner, * \def DRX_UNKNOWN * \brief Generic UNKNOWN value for DRX enumerated types. * -* Used to indicate that the parameter value is unknown or not yet initalized. +* Used to indicate that the parameter value is unknown or not yet initialized. */ #ifndef DRX_UNKNOWN #define DRX_UNKNOWN (254) @@ -450,19 +450,6 @@ MACROS ((u8)((((u16)x)>>8)&0xFF)) /** -* \brief Macro to sign extend signed 9 bit value to signed 16 bit value -*/ -#define DRX_S9TOS16(x) ((((u16)x)&0x100) ? ((s16)((u16)(x)|0xFF00)) : (x)) - -/** -* \brief Macro to sign extend signed 9 bit value to signed 16 bit value -*/ -#define DRX_S24TODRXFREQ(x) ((((u32) x) & 0x00800000UL) ? \ - ((s32) \ - (((u32) x) | 0xFF000000)) : \ - ((s32) x)) - -/** * \brief Macro to convert 16 bit register value to a s32 */ #define DRX_U16TODRXFREQ(x) ((x & 0x8000) ? \ diff --git a/drivers/media/dvb-frontends/lnbh25.c b/drivers/media/dvb-frontends/lnbh25.c index ef3021e964be..cb486e879fdd 100644 --- a/drivers/media/dvb-frontends/lnbh25.c +++ b/drivers/media/dvb-frontends/lnbh25.c @@ -76,8 +76,8 @@ static int lnbh25_read_vmon(struct lnbh25_priv *priv) return ret; } } - print_hex_dump_bytes("lnbh25_read_vmon: ", - DUMP_PREFIX_OFFSET, status, sizeof(status)); + dev_dbg(&priv->i2c->dev, "%s(): %*ph\n", + __func__, (int) sizeof(status), status); if ((status[0] & (LNBH25_STATUS_OFL | LNBH25_STATUS_VMON)) != 0) { dev_err(&priv->i2c->dev, "%s(): voltage in failure state, status reg 0x%x\n", @@ -178,7 +178,7 @@ struct dvb_frontend *lnbh25_attach(struct dvb_frontend *fe, fe->ops.release_sec = lnbh25_release; fe->ops.set_voltage = lnbh25_set_voltage; - dev_err(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n", + dev_info(&i2c->dev, "%s(): attached at I2C addr 0x%02x\n", __func__, priv->i2c_address); return fe; } diff --git a/drivers/media/dvb-frontends/stv0367.c b/drivers/media/dvb-frontends/stv0367.c index e726c2e00460..8ac0f598978d 100644 --- a/drivers/media/dvb-frontends/stv0367.c +++ b/drivers/media/dvb-frontends/stv0367.c @@ -25,6 +25,8 @@ #include <linux/slab.h> #include <linux/i2c.h> +#include "dvb_math.h" + #include "stv0367.h" #include "stv0367_defs.h" #include "stv0367_regs.h" @@ -1437,7 +1439,7 @@ static int stv0367ter_get_frontend(struct dvb_frontend *fe, return 0; } -static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr) +static u32 stv0367ter_snr_readreg(struct dvb_frontend *fe) { struct stv0367_state *state = fe->demodulator_priv; u32 snru32 = 0; @@ -1453,10 +1455,16 @@ static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr) cpt++; } - snru32 /= 10;/*average on 10 values*/ - *snr = snru32 / 1000; + return snru32; +} + +static int stv0367ter_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + u32 snrval = stv0367ter_snr_readreg(fe); + + *snr = snrval / 1000; return 0; } @@ -1501,7 +1509,8 @@ static int stv0367ter_read_status(struct dvb_frontend *fe, *status = 0; if (stv0367_readbits(state, F367TER_LK)) { - *status |= FE_HAS_LOCK; + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI + | FE_HAS_SYNC | FE_HAS_LOCK; dprintk("%s: stv0367 has locked\n", __func__); } @@ -2149,6 +2158,18 @@ static int stv0367cab_read_status(struct dvb_frontend *fe, *status = 0; + if (state->cab_state->state > FE_CAB_NOSIGNAL) + *status |= FE_HAS_SIGNAL; + + if (state->cab_state->state > FE_CAB_NOCARRIER) + *status |= FE_HAS_CARRIER; + + if (state->cab_state->state >= FE_CAB_DEMODOK) + *status |= FE_HAS_VITERBI; + + if (state->cab_state->state >= FE_CAB_DATAOK) + *status |= FE_HAS_SYNC; + if (stv0367_readbits(state, (state->cab_state->qamfec_status_reg ? state->cab_state->qamfec_status_reg : F367CAB_QAMFEC_LOCK))) { *status |= FE_HAS_LOCK; @@ -2702,51 +2723,61 @@ static int stv0367cab_read_strength(struct dvb_frontend *fe, u16 *strength) return 0; } -static int stv0367cab_read_snr(struct dvb_frontend *fe, u16 *snr) +static int stv0367cab_snr_power(struct dvb_frontend *fe) { struct stv0367_state *state = fe->demodulator_priv; - u32 noisepercentage; enum stv0367cab_mod QAMSize; - u32 regval = 0, temp = 0; - int power, i; QAMSize = stv0367_readbits(state, F367CAB_QAM_MODE); switch (QAMSize) { case FE_CAB_MOD_QAM4: - power = 21904; - break; + return 21904; case FE_CAB_MOD_QAM16: - power = 20480; - break; + return 20480; case FE_CAB_MOD_QAM32: - power = 23040; - break; + return 23040; case FE_CAB_MOD_QAM64: - power = 21504; - break; + return 21504; case FE_CAB_MOD_QAM128: - power = 23616; - break; + return 23616; case FE_CAB_MOD_QAM256: - power = 21760; - break; - case FE_CAB_MOD_QAM512: - power = 1; - break; + return 21760; case FE_CAB_MOD_QAM1024: - power = 21280; - break; + return 21280; default: - power = 1; break; } + return 1; +} + +static int stv0367cab_snr_readreg(struct dvb_frontend *fe, int avgdiv) +{ + struct stv0367_state *state = fe->demodulator_priv; + u32 regval = 0; + int i; + for (i = 0; i < 10; i++) { regval += (stv0367_readbits(state, F367CAB_SNR_LO) + 256 * stv0367_readbits(state, F367CAB_SNR_HI)); } - regval /= 10; /*for average over 10 times in for loop above*/ + if (avgdiv) + regval /= 10; + + return regval; +} + +static int stv0367cab_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct stv0367_state *state = fe->demodulator_priv; + u32 noisepercentage; + u32 regval = 0, temp = 0; + int power; + + power = stv0367cab_snr_power(fe); + regval = stv0367cab_snr_readreg(fe, 1); + if (regval != 0) { temp = power * (1 << (3 + stv0367_readbits(state, F367CAB_SNR_PER))); @@ -2980,21 +3011,117 @@ static int stv0367ddb_set_frontend(struct dvb_frontend *fe) return -EINVAL; } +static void stv0367ddb_read_signal_strength(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + s32 signalstrength; + + switch (state->activedemod) { + case demod_cab: + signalstrength = stv0367cab_get_rf_lvl(state) * 1000; + break; + default: + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + p->strength.stat[0].scale = FE_SCALE_DECIBEL; + p->strength.stat[0].uvalue = signalstrength; +} + +static void stv0367ddb_read_snr(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + int cab_pwr; + u32 regval, tmpval, snrval = 0; + + switch (state->activedemod) { + case demod_ter: + snrval = stv0367ter_snr_readreg(fe); + break; + case demod_cab: + cab_pwr = stv0367cab_snr_power(fe); + regval = stv0367cab_snr_readreg(fe, 0); + + /* prevent division by zero */ + if (!regval) { + snrval = 0; + break; + } + + tmpval = (cab_pwr * 320) / regval; + snrval = ((tmpval != 0) ? (intlog2(tmpval) / 5581) : 0); + break; + default: + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + p->cnr.stat[0].scale = FE_SCALE_DECIBEL; + p->cnr.stat[0].uvalue = snrval; +} + +static void stv0367ddb_read_ucblocks(struct dvb_frontend *fe) +{ + struct stv0367_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 ucblocks = 0; + + switch (state->activedemod) { + case demod_ter: + stv0367ter_read_ucblocks(fe, &ucblocks); + break; + case demod_cab: + stv0367cab_read_ucblcks(fe, &ucblocks); + break; + default: + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return; + } + + p->block_error.stat[0].scale = FE_SCALE_COUNTER; + p->block_error.stat[0].uvalue = ucblocks; +} + static int stv0367ddb_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct stv0367_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + int ret; switch (state->activedemod) { case demod_ter: - return stv0367ter_read_status(fe, status); + ret = stv0367ter_read_status(fe, status); + break; case demod_cab: - return stv0367cab_read_status(fe, status); - default: + ret = stv0367cab_read_status(fe, status); break; + default: + return 0; } - return -EINVAL; + /* stop and report on *_read_status failure */ + if (ret) + return ret; + + stv0367ddb_read_signal_strength(fe); + + /* read carrier/noise when a carrier is detected */ + if (*status & FE_HAS_CARRIER) + stv0367ddb_read_snr(fe); + else + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + /* read uncorrected blocks on FE_HAS_LOCK */ + if (*status & FE_HAS_LOCK) + stv0367ddb_read_ucblocks(fe); + else + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + + return 0; } static int stv0367ddb_get_frontend(struct dvb_frontend *fe, @@ -3035,6 +3162,7 @@ static int stv0367ddb_sleep(struct dvb_frontend *fe) static int stv0367ddb_init(struct stv0367_state *state) { struct stv0367ter_state *ter_state = state->ter_state; + struct dtv_frontend_properties *p = &state->fe.dtv_property_cache; stv0367_writereg(state, R367TER_TOPCTRL, 0x10); @@ -3109,6 +3237,13 @@ static int stv0367ddb_init(struct stv0367_state *state) ter_state->first_lock = 0; ter_state->unlock_counter = 2; + p->strength.len = 1; + p->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->cnr.len = 1; + p->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + p->block_error.len = 1; + p->block_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + return 0; } @@ -3126,15 +3261,12 @@ static const struct dvb_frontend_ops stv0367ddb_ops = { 0x400 |/* FE_CAN_QAM_4 */ FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | FE_CAN_QAM_128 | - FE_CAN_QAM_256 | FE_CAN_FEC_AUTO | + FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | /* DVB-T */ - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | - FE_CAN_QAM_128 | FE_CAN_QAM_256 | FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_RECOVER | - FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_RECOVER | FE_CAN_INVERSION_AUTO | FE_CAN_MUTE_TS }, .release = stv0367_release, diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c index 6e313d5243a0..f39f5179dd95 100644 --- a/drivers/media/i2c/et8ek8/et8ek8_driver.c +++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c @@ -1496,7 +1496,6 @@ MODULE_DEVICE_TABLE(i2c, et8ek8_id_table); static const struct dev_pm_ops et8ek8_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(et8ek8_suspend, et8ek8_resume) }; -MODULE_DEVICE_TABLE(of, et8ek8_of_table); static struct i2c_driver et8ek8_i2c_driver = { .driver = { diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 9da4bf4f2c7a..7b79a7498751 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -659,7 +659,7 @@ static int tvp5150_set_vbi(struct v4l2_subdev *sd, struct tvp5150 *decoder = to_tvp5150(sd); v4l2_std_id std = decoder->norm; u8 reg; - int pos=0; + int pos = 0; if (std == V4L2_STD_ALL) { dev_err(sd->dev, "VBI can't be configured without knowing number of lines\n"); @@ -669,33 +669,30 @@ static int tvp5150_set_vbi(struct v4l2_subdev *sd, line += 3; } - if (line<6||line>27) + if (line < 6 || line > 27) return 0; - while (regs->reg != (u16)-1 ) { + while (regs->reg != (u16)-1) { if ((type & regs->type.vbi_type) && - (line>=regs->type.ini_line) && - (line<=regs->type.end_line)) { - type=regs->type.vbi_type; + (line >= regs->type.ini_line) && + (line <= regs->type.end_line)) break; - } regs++; pos++; } + if (regs->reg == (u16)-1) return 0; - type=pos | (flags & 0xf0); - reg=((line-6)<<1)+TVP5150_LINE_MODE_INI; + type = pos | (flags & 0xf0); + reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; - if (fields&1) { + if (fields & 1) tvp5150_write(sd, reg, type); - } - if (fields&2) { - tvp5150_write(sd, reg+1, type); - } + if (fields & 2) + tvp5150_write(sd, reg + 1, type); return type; } diff --git a/drivers/media/pci/ddbridge/ddbridge-core.c b/drivers/media/pci/ddbridge/ddbridge-core.c index 9420479bee9a..cd1723e79a07 100644 --- a/drivers/media/pci/ddbridge/ddbridge-core.c +++ b/drivers/media/pci/ddbridge/ddbridge-core.c @@ -17,6 +17,8 @@ * http://www.gnu.org/copyleft/gpl.html */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -114,6 +116,19 @@ static int i2c_write_reg(struct i2c_adapter *adap, u8 adr, return i2c_write(adap, adr, msg, 2); } +static inline u32 safe_ddbreadl(struct ddb *dev, u32 adr) +{ + u32 val = ddbreadl(adr); + + /* (ddb)readl returns (uint)-1 (all bits set) on failure, catch that */ + if (val == ~0) { + dev_err(&dev->pdev->dev, "ddbreadl failure, adr=%08x\n", adr); + return 0; + } + + return val; +} + static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) { struct ddb *dev = i2c->dev; @@ -124,10 +139,10 @@ static int ddb_i2c_cmd(struct ddb_i2c *i2c, u32 adr, u32 cmd) ddbwritel((adr << 9) | cmd, i2c->regs + I2C_COMMAND); stat = wait_event_timeout(i2c->wq, i2c->done == 1, HZ); if (stat == 0) { - printk(KERN_ERR "I2C timeout\n"); + dev_err(&dev->pdev->dev, "I2C timeout\n"); { /* MSI debugging*/ u32 istat = ddbreadl(INTERRUPT_STATUS); - printk(KERN_ERR "IRS %08x\n", istat); + dev_err(&dev->pdev->dev, "IRS %08x\n", istat); ddbwritel(istat, INTERRUPT_ACK); } return -EIO; @@ -533,7 +548,7 @@ static u32 ddb_input_avail(struct ddb_input *input) off = (stat & 0x7ff) << 7; if (ctrl & 4) { - printk(KERN_ERR "IA %d %d %08x\n", idx, off, ctrl); + dev_err(&dev->pdev->dev, "IA %d %d %08x\n", idx, off, ctrl); ddbwritel(input->stat, DMA_BUFFER_ACK(input->nr)); return 0; } @@ -611,6 +626,7 @@ static int demod_attach_drxk(struct ddb_input *input) struct i2c_adapter *i2c = &input->port->i2c->adap; struct dvb_frontend *fe; struct drxk_config config; + struct device *dev = &input->port->dev->pdev->dev; memset(&config, 0, sizeof(config)); config.microcode_name = "drxk_a3.mc"; @@ -619,7 +635,7 @@ static int demod_attach_drxk(struct ddb_input *input) fe = input->fe = dvb_attach(drxk_attach, &config, i2c); if (!input->fe) { - printk(KERN_ERR "No DRXK found!\n"); + dev_err(dev, "No DRXK found!\n"); return -ENODEV; } fe->sec_priv = input; @@ -632,12 +648,13 @@ static int tuner_attach_tda18271(struct ddb_input *input) { struct i2c_adapter *i2c = &input->port->i2c->adap; struct dvb_frontend *fe; + struct device *dev = &input->port->dev->pdev->dev; if (input->fe->ops.i2c_gate_ctrl) input->fe->ops.i2c_gate_ctrl(input->fe, 1); fe = dvb_attach(tda18271c2dd_attach, input->fe, i2c, 0x60); if (!fe) { - printk(KERN_ERR "No TDA18271 found!\n"); + dev_err(dev, "No TDA18271 found!\n"); return -ENODEV; } if (input->fe->ops.i2c_gate_ctrl) @@ -670,13 +687,14 @@ static struct stv0367_config ddb_stv0367_config[] = { static int demod_attach_stv0367(struct ddb_input *input) { struct i2c_adapter *i2c = &input->port->i2c->adap; + struct device *dev = &input->port->dev->pdev->dev; /* attach frontend */ input->fe = dvb_attach(stv0367ddb_attach, &ddb_stv0367_config[(input->nr & 1)], i2c); if (!input->fe) { - printk(KERN_ERR "stv0367ddb_attach failed (not found?)\n"); + dev_err(dev, "stv0367ddb_attach failed (not found?)\n"); return -ENODEV; } @@ -690,17 +708,19 @@ static int demod_attach_stv0367(struct ddb_input *input) static int tuner_tda18212_ping(struct ddb_input *input, unsigned short adr) { struct i2c_adapter *adapter = &input->port->i2c->adap; + struct device *dev = &input->port->dev->pdev->dev; + u8 tda_id[2]; u8 subaddr = 0x00; - printk(KERN_DEBUG "stv0367-tda18212 tuner ping\n"); + dev_dbg(dev, "stv0367-tda18212 tuner ping\n"); if (input->fe->ops.i2c_gate_ctrl) input->fe->ops.i2c_gate_ctrl(input->fe, 1); if (i2c_read_regs(adapter, adr, subaddr, tda_id, sizeof(tda_id)) < 0) - printk(KERN_DEBUG "tda18212 ping 1 fail\n"); + dev_dbg(dev, "tda18212 ping 1 fail\n"); if (i2c_read_regs(adapter, adr, subaddr, tda_id, sizeof(tda_id)) < 0) - printk(KERN_DEBUG "tda18212 ping 2 fail\n"); + dev_warn(dev, "tda18212 ping failed, expect problems\n"); if (input->fe->ops.i2c_gate_ctrl) input->fe->ops.i2c_gate_ctrl(input->fe, 0); @@ -711,6 +731,7 @@ static int tuner_tda18212_ping(struct ddb_input *input, unsigned short adr) static int demod_attach_cxd28xx(struct ddb_input *input, int par, int osc24) { struct i2c_adapter *i2c = &input->port->i2c->adap; + struct device *dev = &input->port->dev->pdev->dev; struct cxd2841er_config cfg; /* the cxd2841er driver expects 8bit/shifted I2C addresses */ @@ -728,7 +749,7 @@ static int demod_attach_cxd28xx(struct ddb_input *input, int par, int osc24) input->fe = dvb_attach(cxd2841er_attach_t_c, &cfg, i2c); if (!input->fe) { - printk(KERN_ERR "No Sony CXD28xx found!\n"); + dev_err(dev, "No Sony CXD28xx found!\n"); return -ENODEV; } @@ -742,6 +763,7 @@ static int demod_attach_cxd28xx(struct ddb_input *input, int par, int osc24) static int tuner_attach_tda18212(struct ddb_input *input, u32 porttype) { struct i2c_adapter *adapter = &input->port->i2c->adap; + struct device *dev = &input->port->dev->pdev->dev; struct i2c_client *client; struct tda18212_config config = { .fe = input->fe, @@ -786,7 +808,7 @@ static int tuner_attach_tda18212(struct ddb_input *input, u32 porttype) return 0; err: - printk(KERN_INFO "TDA18212 tuner not found. Device is not fully operational.\n"); + dev_warn(dev, "TDA18212 tuner not found. Device is not fully operational.\n"); return -ENODEV; } @@ -847,19 +869,20 @@ static struct stv6110x_config stv6110b = { static int demod_attach_stv0900(struct ddb_input *input, int type) { struct i2c_adapter *i2c = &input->port->i2c->adap; + struct device *dev = &input->port->dev->pdev->dev; struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900; input->fe = dvb_attach(stv090x_attach, feconf, i2c, (input->nr & 1) ? STV090x_DEMODULATOR_1 : STV090x_DEMODULATOR_0); if (!input->fe) { - printk(KERN_ERR "No STV0900 found!\n"); + dev_err(dev, "No STV0900 found!\n"); return -ENODEV; } if (!dvb_attach(lnbh24_attach, input->fe, i2c, 0, 0, (input->nr & 1) ? (0x09 - type) : (0x0b - type))) { - printk(KERN_ERR "No LNBH24 found!\n"); + dev_err(dev, "No LNBH24 found!\n"); return -ENODEV; } return 0; @@ -868,6 +891,7 @@ static int demod_attach_stv0900(struct ddb_input *input, int type) static int tuner_attach_stv6110(struct ddb_input *input, int type) { struct i2c_adapter *i2c = &input->port->i2c->adap; + struct device *dev = &input->port->dev->pdev->dev; struct stv090x_config *feconf = type ? &stv0900_aa : &stv0900; struct stv6110x_config *tunerconf = (input->nr & 1) ? &stv6110b : &stv6110a; @@ -875,10 +899,10 @@ static int tuner_attach_stv6110(struct ddb_input *input, int type) ctl = dvb_attach(stv6110x_attach, input->fe, tunerconf, i2c); if (!ctl) { - printk(KERN_ERR "No STV6110X found!\n"); + dev_err(dev, "No STV6110X found!\n"); return -ENODEV; } - printk(KERN_INFO "attach tuner input %d adr %02x\n", + dev_info(dev, "attach tuner input %d adr %02x\n", input->nr, tunerconf->addr); feconf->tuner_init = ctl->tuner_init; @@ -1009,13 +1033,14 @@ static int dvb_input_attach(struct ddb_input *input) struct ddb_port *port = input->port; struct dvb_adapter *adap = &input->adap; struct dvb_demux *dvbdemux = &input->demux; + struct device *dev = &input->port->dev->pdev->dev; int sony_osc24 = 0, sony_tspar = 0; ret = dvb_register_adapter(adap, "DDBridge", THIS_MODULE, &input->port->dev->pdev->dev, adapter_nr); if (ret < 0) { - printk(KERN_ERR "ddbridge: Could not register adapter.Check if you enabled enough adapters in dvb-core!\n"); + dev_err(dev, "Could not register adapter. Check if you enabled enough adapters in dvb-core!\n"); return ret; } input->attached = 1; @@ -1241,9 +1266,9 @@ static void input_tasklet(unsigned long data) if (input->port->class == DDB_PORT_TUNER) { if (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr))) - printk(KERN_ERR "Overflow input %d\n", input->nr); + dev_err(&dev->pdev->dev, "Overflow input %d\n", input->nr); while (input->cbuf != ((input->stat >> 11) & 0x1f) - || (4&ddbreadl(DMA_BUFFER_CONTROL(input->nr)))) { + || (4 & safe_ddbreadl(dev, DMA_BUFFER_CONTROL(input->nr)))) { dvb_dmx_swfilter_packets(&input->demux, input->vbuf[input->cbuf], input->dma_buf_size / 188); @@ -1280,6 +1305,7 @@ static struct cxd2099_cfg cxd_cfg = { .adr = 0x40, .polarity = 1, .clock_mode = 1, + .max_i2c = 512, }; static int ddb_ci_attach(struct ddb_port *port) @@ -1310,6 +1336,7 @@ static int ddb_ci_attach(struct ddb_port *port) static int ddb_port_attach(struct ddb_port *port) { + struct device *dev = &port->dev->pdev->dev; int ret = 0; switch (port->class) { @@ -1326,7 +1353,7 @@ static int ddb_port_attach(struct ddb_port *port) break; } if (ret < 0) - printk(KERN_ERR "port_attach on port %d failed\n", port->nr); + dev_err(dev, "port_attach on port %d failed\n", port->nr); return ret; } @@ -1377,6 +1404,7 @@ static void ddb_ports_detach(struct ddb *dev) static int init_xo2(struct ddb_port *port) { struct i2c_adapter *i2c = &port->i2c->adap; + struct device *dev = &port->dev->pdev->dev; u8 val, data[2]; int res; @@ -1385,7 +1413,7 @@ static int init_xo2(struct ddb_port *port) return res; if (data[0] != 0x01) { - pr_info("Port %d: invalid XO2\n", port->nr); + dev_info(dev, "Port %d: invalid XO2\n", port->nr); return -1; } @@ -1511,7 +1539,7 @@ static void ddb_port_probe(struct ddb_port *port) port->class = DDB_PORT_CI; ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING); } else if (port_has_xo2(port, &xo2_type, &xo2_id)) { - printk(KERN_INFO "Port %d (TAB %d): XO2 type: %d, id: %d\n", + dev_dbg(&dev->pdev->dev, "Port %d (TAB %d): XO2 type: %d, id: %d\n", port->nr, port->nr+1, xo2_type, xo2_id); ddbwritel(I2C_SPEED_400, port->i2c->regs + I2C_TIMING); @@ -1556,10 +1584,10 @@ static void ddb_port_probe(struct ddb_port *port) } break; case DDB_XO2_TYPE_CI: - printk(KERN_INFO "DuoFlex CI modules not supported\n"); + dev_info(&dev->pdev->dev, "DuoFlex CI modules not supported\n"); break; default: - printk(KERN_INFO "Unknown XO2 DuoFlex module\n"); + dev_info(&dev->pdev->dev, "Unknown XO2 DuoFlex module\n"); break; } } else if (port_has_cxd28xx(port, &cxd_id)) { @@ -1611,7 +1639,7 @@ static void ddb_port_probe(struct ddb_port *port) ddbwritel(I2C_SPEED_100, port->i2c->regs + I2C_TIMING); } - printk(KERN_INFO "Port %d (TAB %d): %s\n", + dev_info(&dev->pdev->dev, "Port %d (TAB %d): %s\n", port->nr, port->nr+1, modname); } @@ -1765,7 +1793,7 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) wbuf += 4; wlen -= 4; ddbwritel(data, SPI_DATA); - while (ddbreadl(SPI_CONTROL) & 0x0004) + while (safe_ddbreadl(dev, SPI_CONTROL) & 0x0004) ; } @@ -1785,7 +1813,7 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) if (shift) data <<= shift; ddbwritel(data, SPI_DATA); - while (ddbreadl(SPI_CONTROL) & 0x0004) + while (safe_ddbreadl(dev, SPI_CONTROL) & 0x0004) ; if (!rlen) { @@ -1797,7 +1825,7 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) while (rlen > 4) { ddbwritel(0xffffffff, SPI_DATA); - while (ddbreadl(SPI_CONTROL) & 0x0004) + while (safe_ddbreadl(dev, SPI_CONTROL) & 0x0004) ; data = ddbreadl(SPI_DATA); *(u32 *) rbuf = swab32(data); @@ -1806,7 +1834,7 @@ static int flashio(struct ddb *dev, u8 *wbuf, u32 wlen, u8 *rbuf, u32 rlen) } ddbwritel(0x0003 | ((rlen << (8 + 3)) & 0x1F00), SPI_CONTROL); ddbwritel(0xffffffff, SPI_DATA); - while (ddbreadl(SPI_CONTROL) & 0x0004) + while (safe_ddbreadl(dev, SPI_CONTROL) & 0x0004) ; data = ddbreadl(SPI_DATA); @@ -1993,7 +2021,7 @@ static int ddb_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->pdev = pdev; pci_set_drvdata(pdev, dev); dev->info = (struct ddb_info *) id->driver_data; - printk(KERN_INFO "DDBridge driver detected: %s\n", dev->info->name); + dev_info(&pdev->dev, "Detected %s\n", dev->info->name); dev->regs = ioremap(pci_resource_start(dev->pdev, 0), pci_resource_len(dev->pdev, 0)); @@ -2001,13 +2029,13 @@ static int ddb_probe(struct pci_dev *pdev, const struct pci_device_id *id) stat = -ENOMEM; goto fail; } - printk(KERN_INFO "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4)); + dev_info(&pdev->dev, "HW %08x FW %08x\n", ddbreadl(0), ddbreadl(4)); #ifdef CONFIG_PCI_MSI if (pci_msi_enabled()) stat = pci_enable_msi(dev->pdev); if (stat) { - printk(KERN_INFO ": MSI not available.\n"); + dev_info(&pdev->dev, "MSI not available.\n"); } else { irq_flag = 0; dev->msi = 1; @@ -2040,7 +2068,7 @@ static int ddb_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto fail1; ddb_ports_init(dev); if (ddb_buffers_alloc(dev) < 0) { - printk(KERN_INFO ": Could not allocate buffer memory\n"); + dev_err(&pdev->dev, "Could not allocate buffer memory\n"); goto fail2; } if (ddb_ports_attach(dev) < 0) @@ -2050,19 +2078,19 @@ static int ddb_probe(struct pci_dev *pdev, const struct pci_device_id *id) fail3: ddb_ports_detach(dev); - printk(KERN_ERR "fail3\n"); + dev_err(&pdev->dev, "fail3\n"); ddb_ports_release(dev); fail2: - printk(KERN_ERR "fail2\n"); + dev_err(&pdev->dev, "fail2\n"); ddb_buffers_free(dev); fail1: - printk(KERN_ERR "fail1\n"); + dev_err(&pdev->dev, "fail1\n"); if (dev->msi) pci_disable_msi(dev->pdev); if (stat == 0) free_irq(dev->pdev->irq, dev); fail: - printk(KERN_ERR "fail\n"); + dev_err(&pdev->dev, "fail\n"); ddb_unmap(dev); pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); @@ -2242,7 +2270,7 @@ static __init int module_init_ddbridge(void) { int ret; - printk(KERN_INFO "Digital Devices PCIE bridge driver, Copyright (C) 2010-11 Digital Devices GmbH\n"); + pr_info("Digital Devices PCIE bridge driver, Copyright (C) 2010-11 Digital Devices GmbH\n"); ret = ddb_class_create(); if (ret < 0) diff --git a/drivers/media/pci/ngene/ngene-core.c b/drivers/media/pci/ngene/ngene-core.c index ce69e648b663..8c92cb7f7e72 100644 --- a/drivers/media/pci/ngene/ngene-core.c +++ b/drivers/media/pci/ngene/ngene-core.c @@ -336,9 +336,9 @@ int ngene_command(struct ngene *dev, struct ngene_command *com) { int result; - down(&dev->cmd_mutex); + mutex_lock(&dev->cmd_mutex); result = ngene_command_mutex(dev, com); - up(&dev->cmd_mutex); + mutex_unlock(&dev->cmd_mutex); return result; } @@ -560,7 +560,6 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream, u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700); u16 BsSDO = 0x9B00; - down(&dev->stream_mutex); memset(&com, 0, sizeof(com)); com.cmd.hdr.Opcode = CMD_CONTROL; com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2; @@ -586,17 +585,13 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream, chan->State = KSSTATE_ACQUIRE; chan->HWState = HWSTATE_STOP; spin_unlock_irq(&chan->state_lock); - if (ngene_command(dev, &com) < 0) { - up(&dev->stream_mutex); + if (ngene_command(dev, &com) < 0) return -1; - } /* clear_buffers(chan); */ flush_buffers(chan); - up(&dev->stream_mutex); return 0; } spin_unlock_irq(&chan->state_lock); - up(&dev->stream_mutex); return 0; } @@ -692,11 +687,9 @@ static int ngene_command_stream_control(struct ngene *dev, u8 stream, chan->HWState = HWSTATE_STARTUP; spin_unlock_irq(&chan->state_lock); - if (ngene_command(dev, &com) < 0) { - up(&dev->stream_mutex); + if (ngene_command(dev, &com) < 0) return -1; - } - up(&dev->stream_mutex); + return 0; } @@ -750,8 +743,11 @@ void set_transfer(struct ngene_channel *chan, int state) /* else printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", ngreadl(0x9310)); */ + mutex_lock(&dev->stream_mutex); ret = ngene_command_stream_control(dev, chan->number, control, mode, flags); + mutex_unlock(&dev->stream_mutex); + if (!ret) chan->running = state; else @@ -1283,7 +1279,7 @@ static int ngene_load_firm(struct ngene *dev) static void ngene_stop(struct ngene *dev) { - down(&dev->cmd_mutex); + mutex_destroy(&dev->cmd_mutex); i2c_del_adapter(&(dev->channel[0].i2c_adapter)); i2c_del_adapter(&(dev->channel[1].i2c_adapter)); ngwritel(0, NGENE_INT_ENABLE); @@ -1346,10 +1342,10 @@ static int ngene_start(struct ngene *dev) init_waitqueue_head(&dev->cmd_wq); init_waitqueue_head(&dev->tx_wq); init_waitqueue_head(&dev->rx_wq); - sema_init(&dev->cmd_mutex, 1); - sema_init(&dev->stream_mutex, 1); + mutex_init(&dev->cmd_mutex); + mutex_init(&dev->stream_mutex); sema_init(&dev->pll_mutex, 1); - sema_init(&dev->i2c_switch_mutex, 1); + mutex_init(&dev->i2c_switch_mutex); spin_lock_init(&dev->cmd_lock); for (i = 0; i < MAX_STREAM; i++) spin_lock_init(&dev->channel[i].state_lock); @@ -1606,10 +1602,10 @@ static void ngene_unlink(struct ngene *dev) com.in_len = 3; com.out_len = 1; - down(&dev->cmd_mutex); + mutex_lock(&dev->cmd_mutex); ngwritel(0, NGENE_INT_ENABLE); ngene_command_mutex(dev, &com); - up(&dev->cmd_mutex); + mutex_unlock(&dev->cmd_mutex); } void ngene_shutdown(struct pci_dev *pdev) diff --git a/drivers/media/pci/ngene/ngene-i2c.c b/drivers/media/pci/ngene/ngene-i2c.c index cf39fcf54adf..fbf36353c701 100644 --- a/drivers/media/pci/ngene/ngene-i2c.c +++ b/drivers/media/pci/ngene/ngene-i2c.c @@ -118,7 +118,7 @@ static int ngene_i2c_master_xfer(struct i2c_adapter *adapter, (struct ngene_channel *)i2c_get_adapdata(adapter); struct ngene *dev = chan->dev; - down(&dev->i2c_switch_mutex); + mutex_lock(&dev->i2c_switch_mutex); ngene_i2c_set_bus(dev, chan->number); if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD)) @@ -136,11 +136,11 @@ static int ngene_i2c_master_xfer(struct i2c_adapter *adapter, msg[0].buf, msg[0].len, 0)) goto done; - up(&dev->i2c_switch_mutex); + mutex_unlock(&dev->i2c_switch_mutex); return -EIO; done: - up(&dev->i2c_switch_mutex); + mutex_unlock(&dev->i2c_switch_mutex); return num; } diff --git a/drivers/media/pci/ngene/ngene.h b/drivers/media/pci/ngene/ngene.h index 10d8f74c4f0a..7c7cd217333d 100644 --- a/drivers/media/pci/ngene/ngene.h +++ b/drivers/media/pci/ngene/ngene.h @@ -762,10 +762,10 @@ struct ngene { wait_queue_head_t cmd_wq; int cmd_done; - struct semaphore cmd_mutex; - struct semaphore stream_mutex; + struct mutex cmd_mutex; + struct mutex stream_mutex; struct semaphore pll_mutex; - struct semaphore i2c_switch_mutex; + struct mutex i2c_switch_mutex; int i2c_current_channel; int i2c_current_bus; spinlock_t cmd_lock; diff --git a/drivers/media/pci/tw5864/tw5864-video.c b/drivers/media/pci/tw5864/tw5864-video.c index 2a044be729da..e7bd2b8484e3 100644 --- a/drivers/media/pci/tw5864/tw5864-video.c +++ b/drivers/media/pci/tw5864/tw5864-video.c @@ -545,6 +545,7 @@ static int tw5864_fmt_vid_cap(struct file *file, void *priv, switch (input->std) { default: WARN_ON_ONCE(1); + return -EINVAL; case STD_NTSC: f->fmt.pix.height = 480; break; diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 1313cd533436..fb1fa0b82077 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -475,8 +475,8 @@ config VIDEO_QCOM_VENUS tristate "Qualcomm Venus V4L2 encoder/decoder driver" depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST - select QCOM_MDT_LOADER if (ARM || ARM64) - select QCOM_SCM if (ARM || ARM64) + select QCOM_MDT_LOADER if ARCH_QCOM + select QCOM_SCM if ARCH_QCOM select VIDEOBUF2_DMA_SG select V4L2_MEM2MEM_DEV ---help--- diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index 25cbf9e5ac5a..bba1eb43b5d8 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -393,8 +393,8 @@ static int coda_alloc_framebuffers(struct coda_ctx *ctx, int ret; int i; - if (ctx->codec && (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || - ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264)) { + if (ctx->codec->src_fourcc == V4L2_PIX_FMT_H264 || + ctx->codec->dst_fourcc == V4L2_PIX_FMT_H264) { width = round_up(q_data->width, 16); height = round_up(q_data->height, 16); } else { @@ -2198,7 +2198,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) ctx->display_idx = display_idx; } -static void coda_error_decode(struct coda_ctx *ctx) +static void coda_decode_timeout(struct coda_ctx *ctx) { struct vb2_v4l2_buffer *dst_buf; @@ -2223,7 +2223,7 @@ const struct coda_context_ops coda_bit_decode_ops = { .start_streaming = coda_start_decoding, .prepare_run = coda_prepare_decode, .finish_run = coda_finish_decode, - .error_run = coda_error_decode, + .run_timeout = coda_decode_timeout, .seq_end_work = coda_seq_end_work, .release = coda_bit_release, }; diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index f92cc7df58fb..829c7895a98a 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -1164,8 +1164,8 @@ static void coda_pic_run_work(struct work_struct *work) coda_hw_reset(ctx); - if (ctx->ops->error_run) - ctx->ops->error_run(ctx); + if (ctx->ops->run_timeout) + ctx->ops->run_timeout(ctx); } else if (!ctx->aborting) { ctx->ops->finish_run(ctx); } diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 40fe22f0d757..c5f504d8cf67 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -183,7 +183,7 @@ struct coda_context_ops { int (*start_streaming)(struct coda_ctx *ctx); int (*prepare_run)(struct coda_ctx *ctx); void (*finish_run)(struct coda_ctx *ctx); - void (*error_run)(struct coda_ctx *ctx); + void (*run_timeout)(struct coda_ctx *ctx); void (*seq_end_work)(struct work_struct *work); void (*release)(struct coda_ctx *ctx); }; diff --git a/drivers/media/platform/davinci/ccdc_hw_device.h b/drivers/media/platform/davinci/ccdc_hw_device.h index 8f6688a7a111..f1b521045d64 100644 --- a/drivers/media/platform/davinci/ccdc_hw_device.h +++ b/drivers/media/platform/davinci/ccdc_hw_device.h @@ -42,16 +42,6 @@ struct ccdc_hw_ops { int (*set_hw_if_params) (struct vpfe_hw_if_param *param); /* get interface parameters */ int (*get_hw_if_params) (struct vpfe_hw_if_param *param); - /* - * Pointer to function to set parameters. Used - * for implementing VPFE_S_CCDC_PARAMS - */ - int (*set_params) (void *params); - /* - * Pointer to function to get parameter. Used - * for implementing VPFE_G_CCDC_PARAMS - */ - int (*get_params) (void *params); /* Pointer to function to configure ccdc */ int (*configure) (void); diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/davinci/dm355_ccdc.c index 73db166dc338..6d492dc4c3a9 100644 --- a/drivers/media/platform/davinci/dm355_ccdc.c +++ b/drivers/media/platform/davinci/dm355_ccdc.c @@ -17,12 +17,7 @@ * This module is for configuring DM355 CCD controller of VPFE to capture * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules * such as Defect Pixel Correction, Color Space Conversion etc to - * pre-process the Bayer RGB data, before writing it to SDRAM. This - * module also allows application to configure individual - * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. - * To do so, application include dm355_ccdc.h and vpfe_capture.h header - * files. The setparams() API is called by vpfe_capture driver - * to configure module parameters + * pre-process the Bayer RGB data, before writing it to SDRAM. * * TODO: 1) Raw bayer parameter settings and bayer capture * 2) Split module parameter structure to module specific ioctl structs @@ -260,90 +255,6 @@ static void ccdc_setwin(struct v4l2_rect *image_win, dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); } -static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) -{ - if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT || - ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) { - dev_dbg(ccdc_cfg.dev, "Invalid value of data shift\n"); - return -EINVAL; - } - - if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 || - ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) { - dev_dbg(ccdc_cfg.dev, "Invalid value of median filter1\n"); - return -EINVAL; - } - - if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 || - ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) { - dev_dbg(ccdc_cfg.dev, "Invalid value of median filter2\n"); - return -EINVAL; - } - - if ((ccdcparam->med_filt_thres < 0) || - (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) { - dev_dbg(ccdc_cfg.dev, - "Invalid value of median filter threshold\n"); - return -EINVAL; - } - - if (ccdcparam->data_sz < CCDC_DATA_16BITS || - ccdcparam->data_sz > CCDC_DATA_8BITS) { - dev_dbg(ccdc_cfg.dev, "Invalid value of data size\n"); - return -EINVAL; - } - - if (ccdcparam->alaw.enable) { - if (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_13_4 || - ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) { - dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n"); - return -EINVAL; - } - } - - if (ccdcparam->blk_clamp.b_clamp_enable) { - if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS || - ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) { - dev_dbg(ccdc_cfg.dev, - "Invalid value of sample pixel\n"); - return -EINVAL; - } - if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES || - ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) { - dev_dbg(ccdc_cfg.dev, - "Invalid value of sample lines\n"); - return -EINVAL; - } - } - return 0; -} - -/* Parameter operations */ -static int ccdc_set_params(void __user *params) -{ - struct ccdc_config_params_raw ccdc_raw_params; - int x; - - /* only raw module parameters can be set through the IOCTL */ - if (ccdc_cfg.if_type != VPFE_RAW_BAYER) - return -EINVAL; - - x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); - if (x) { - dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying ccdcparams, %d\n", - x); - return -EFAULT; - } - - if (!validate_ccdc_param(&ccdc_raw_params)) { - memcpy(&ccdc_cfg.bayer.config_params, - &ccdc_raw_params, - sizeof(ccdc_raw_params)); - return 0; - } - return -EINVAL; -} - /* This function will configure CCDC for YCbCr video capture */ static void ccdc_config_ycbcr(void) { @@ -939,7 +850,6 @@ static struct ccdc_hw_device ccdc_hw_dev = { .enable = ccdc_enable, .enable_out_to_sdram = ccdc_enable_output_to_sdram, .set_hw_if_params = ccdc_set_hw_if_params, - .set_params = ccdc_set_params, .configure = ccdc_configure, .set_buftype = ccdc_set_buftype, .get_buftype = ccdc_get_buftype, diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/davinci/dm644x_ccdc.c index 740fbc7a8c14..3b2d8a9317b8 100644 --- a/drivers/media/platform/davinci/dm644x_ccdc.c +++ b/drivers/media/platform/davinci/dm644x_ccdc.c @@ -17,13 +17,9 @@ * This module is for configuring CCD controller of DM6446 VPFE to capture * Raw yuv or Bayer RGB data from a decoder. CCDC has several modules * such as Defect Pixel Correction, Color Space Conversion etc to - * pre-process the Raw Bayer RGB data, before writing it to SDRAM. This - * module also allows application to configure individual - * module parameters through VPFE_CMD_S_CCDC_RAW_PARAMS IOCTL. - * To do so, application includes dm644x_ccdc.h and vpfe_capture.h header - * files. The setparams() API is called by vpfe_capture driver - * to configure module parameters. This file is named DM644x so that other - * variants such DM6443 may be supported using the same module. + * pre-process the Raw Bayer RGB data, before writing it to SDRAM. + * This file is named DM644x so that other variants such DM6443 + * may be supported using the same module. * * TODO: Test Raw bayer parameter settings and bayer capture * Split module parameter structure to module specific ioctl structs @@ -216,96 +212,8 @@ static void ccdc_readregs(void) dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val); } -static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) -{ - if (ccdcparam->alaw.enable) { - u8 max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd); - u8 max_data = ccdc_data_size_max_bit(ccdcparam->data_sz); - - if ((ccdcparam->alaw.gamma_wd > CCDC_GAMMA_BITS_09_0) || - (ccdcparam->alaw.gamma_wd < CCDC_GAMMA_BITS_15_6) || - (max_gamma > max_data)) { - dev_dbg(ccdc_cfg.dev, "\nInvalid data line select"); - return -1; - } - } - return 0; -} - -static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) -{ - struct ccdc_config_params_raw *config_params = - &ccdc_cfg.bayer.config_params; - unsigned int *fpc_virtaddr = NULL; - unsigned int *fpc_physaddr = NULL; - - memcpy(config_params, raw_params, sizeof(*raw_params)); - /* - * allocate memory for fault pixel table and copy the user - * values to the table - */ - if (!config_params->fault_pxl.enable) - return 0; - - fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; - fpc_virtaddr = (unsigned int *)phys_to_virt( - (unsigned long)fpc_physaddr); - /* - * Allocate memory for FPC table if current - * FPC table buffer is not big enough to - * accommodate FPC Number requested - */ - if (raw_params->fault_pxl.fp_num != config_params->fault_pxl.fp_num) { - if (fpc_physaddr != NULL) { - free_pages((unsigned long)fpc_virtaddr, - get_order - (config_params->fault_pxl.fp_num * - FP_NUM_BYTES)); - } - - /* Allocate memory for FPC table */ - fpc_virtaddr = - (unsigned int *)__get_free_pages(GFP_KERNEL | GFP_DMA, - get_order(raw_params-> - fault_pxl.fp_num * - FP_NUM_BYTES)); - - if (fpc_virtaddr == NULL) { - dev_dbg(ccdc_cfg.dev, - "\nUnable to allocate memory for FPC"); - return -EFAULT; - } - fpc_physaddr = - (unsigned int *)virt_to_phys((void *)fpc_virtaddr); - } - - /* Copy number of fault pixels and FPC table */ - config_params->fault_pxl.fp_num = raw_params->fault_pxl.fp_num; - if (copy_from_user(fpc_virtaddr, - (void __user *)raw_params->fault_pxl.fpc_table_addr, - config_params->fault_pxl.fp_num * FP_NUM_BYTES)) { - dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed"); - return -EFAULT; - } - config_params->fault_pxl.fpc_table_addr = (unsigned long)fpc_physaddr; - return 0; -} - static int ccdc_close(struct device *dev) { - struct ccdc_config_params_raw *config_params = - &ccdc_cfg.bayer.config_params; - unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL; - - fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; - - if (fpc_physaddr != NULL) { - fpc_virtaddr = (unsigned int *) - phys_to_virt((unsigned long)fpc_physaddr); - free_pages((unsigned long)fpc_virtaddr, - get_order(config_params->fault_pxl.fp_num * - FP_NUM_BYTES)); - } return 0; } @@ -339,29 +247,6 @@ static void ccdc_sbl_reset(void) vpss_clear_wbl_overflow(VPSS_PCR_CCDC_WBL_O); } -/* Parameter operations */ -static int ccdc_set_params(void __user *params) -{ - struct ccdc_config_params_raw ccdc_raw_params; - int x; - - if (ccdc_cfg.if_type != VPFE_RAW_BAYER) - return -EINVAL; - - x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); - if (x) { - dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copyingccdc params, %d\n", - x); - return -EFAULT; - } - - if (!validate_ccdc_param(&ccdc_raw_params)) { - if (!ccdc_update_raw_params(&ccdc_raw_params)) - return 0; - } - return -EINVAL; -} - /* * ccdc_config_ycbcr() * This function will configure CCDC for YCbCr video capture @@ -489,32 +374,6 @@ static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) regw(val, CCDC_BLKCMP); } -static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc) -{ - u32 val; - - /* Initially disable FPC */ - val = CCDC_FPC_DISABLE; - regw(val, CCDC_FPC); - - if (!fpc->enable) - return; - - /* Configure Fault pixel if needed */ - regw(fpc->fpc_table_addr, CCDC_FPC_ADDR); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%lx to FPC_ADDR...\n", - (fpc->fpc_table_addr)); - /* Write the FPC params with FPC disable */ - val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK; - regw(val, CCDC_FPC); - - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val); - /* read the FPC register */ - val = regr(CCDC_FPC) | CCDC_FPC_ENABLE; - regw(val, CCDC_FPC); - dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val); -} - /* * ccdc_config_raw() * This function will configure CCDC for Raw capture mode @@ -569,9 +428,6 @@ static void ccdc_config_raw(void) /* Configure Black level compensation */ ccdc_config_black_compense(&config_params->blk_comp); - /* Configure Fault Pixel Correction */ - ccdc_config_fpc(&config_params->fault_pxl); - /* If data size is 8 bit then pack the data */ if ((config_params->data_sz == CCDC_DATA_8BITS) || config_params->alaw.enable) @@ -929,7 +785,6 @@ static struct ccdc_hw_device ccdc_hw_dev = { .reset = ccdc_sbl_reset, .enable = ccdc_enable, .set_hw_if_params = ccdc_set_hw_if_params, - .set_params = ccdc_set_params, .configure = ccdc_configure, .set_buftype = ccdc_set_buftype, .get_buftype = ccdc_get_buftype, diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/davinci/vpfe_capture.c index e3fe3e0635aa..b1bf4a7e8eb7 100644 --- a/drivers/media/platform/davinci/vpfe_capture.c +++ b/drivers/media/platform/davinci/vpfe_capture.c @@ -281,45 +281,6 @@ void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev) EXPORT_SYMBOL(vpfe_unregister_ccdc_device); /* - * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings - */ -static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe_dev, - struct v4l2_format *f) -{ - struct v4l2_rect image_win; - enum ccdc_buftype buf_type; - enum ccdc_frmfmt frm_fmt; - - memset(f, 0, sizeof(*f)); - f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - ccdc_dev->hw_ops.get_image_window(&image_win); - f->fmt.pix.width = image_win.width; - f->fmt.pix.height = image_win.height; - f->fmt.pix.bytesperline = ccdc_dev->hw_ops.get_line_length(); - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * - f->fmt.pix.height; - buf_type = ccdc_dev->hw_ops.get_buftype(); - f->fmt.pix.pixelformat = ccdc_dev->hw_ops.get_pixel_format(); - frm_fmt = ccdc_dev->hw_ops.get_frame_format(); - if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) - f->fmt.pix.field = V4L2_FIELD_NONE; - else if (frm_fmt == CCDC_FRMFMT_INTERLACED) { - if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) - f->fmt.pix.field = V4L2_FIELD_INTERLACED; - else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) - f->fmt.pix.field = V4L2_FIELD_SEQ_TB; - else { - v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf_type\n"); - return -EINVAL; - } - } else { - v4l2_err(&vpfe_dev->v4l2_dev, "Invalid frm_fmt\n"); - return -EINVAL; - } - return 0; -} - -/* * vpfe_config_ccdc_image_format() * For a pix format, configure ccdc to setup the capture */ @@ -1697,59 +1658,6 @@ unlock_out: return ret; } - -static long vpfe_param_handler(struct file *file, void *priv, - bool valid_prio, unsigned int cmd, void *param) -{ - struct vpfe_device *vpfe_dev = video_drvdata(file); - int ret; - - v4l2_dbg(2, debug, &vpfe_dev->v4l2_dev, "vpfe_param_handler\n"); - - if (vpfe_dev->started) { - /* only allowed if streaming is not started */ - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, - "device already started\n"); - return -EBUSY; - } - - ret = mutex_lock_interruptible(&vpfe_dev->lock); - if (ret) - return ret; - - switch (cmd) { - case VPFE_CMD_S_CCDC_RAW_PARAMS: - v4l2_warn(&vpfe_dev->v4l2_dev, - "VPFE_CMD_S_CCDC_RAW_PARAMS: experimental ioctl\n"); - if (ccdc_dev->hw_ops.set_params) { - ret = ccdc_dev->hw_ops.set_params(param); - if (ret) { - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, - "Error setting parameters in CCDC\n"); - goto unlock_out; - } - ret = vpfe_get_ccdc_image_format(vpfe_dev, - &vpfe_dev->fmt); - if (ret < 0) { - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, - "Invalid image format at CCDC\n"); - goto unlock_out; - } - } else { - ret = -EINVAL; - v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, - "VPFE_CMD_S_CCDC_RAW_PARAMS not supported\n"); - } - break; - default: - ret = -ENOTTY; - } -unlock_out: - mutex_unlock(&vpfe_dev->lock); - return ret; -} - - /* vpfe capture ioctl operations */ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { .vidioc_querycap = vpfe_querycap, @@ -1772,7 +1680,6 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { .vidioc_cropcap = vpfe_cropcap, .vidioc_g_selection = vpfe_g_selection, .vidioc_s_selection = vpfe_s_selection, - .vidioc_default = vpfe_param_handler, }; static struct vpfe_device *vpfe_initialize(void) diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/davinci/vpif_capture.c index d78580f9e431..4be6554c56c5 100644 --- a/drivers/media/platform/davinci/vpif_capture.c +++ b/drivers/media/platform/davinci/vpif_capture.c @@ -1719,7 +1719,6 @@ vpif_unregister: */ static int vpif_remove(struct platform_device *device) { - struct common_obj *common; struct channel_obj *ch; int i; @@ -1730,7 +1729,6 @@ static int vpif_remove(struct platform_device *device) for (i = 0; i < VPIF_CAPTURE_MAX_DEVICES; i++) { /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; - common = &ch->common[VPIF_VIDEO_INDEX]; /* Unregister video device */ video_unregister_device(&ch->video_dev); kfree(vpif_obj.dev[i]); diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/davinci/vpif_display.c index b5ac6ce626b3..bf982bf86542 100644 --- a/drivers/media/platform/davinci/vpif_display.c +++ b/drivers/media/platform/davinci/vpif_display.c @@ -1339,7 +1339,6 @@ vpif_unregister: */ static int vpif_remove(struct platform_device *device) { - struct common_obj *common; struct channel_obj *ch; int i; @@ -1350,7 +1349,6 @@ static int vpif_remove(struct platform_device *device) for (i = 0; i < VPIF_DISPLAY_MAX_DEVICES; i++) { /* Get the pointer to the channel object */ ch = vpif_obj.dev[i]; - common = &ch->common[VPIF_VIDEO_INDEX]; /* Unregister video device */ video_unregister_device(&ch->video_dev); kfree(vpif_obj.dev[i]); diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/omap/omap_vout_vrfb.c index 92c4e1826356..45a553d4f5b2 100644 --- a/drivers/media/platform/omap/omap_vout_vrfb.c +++ b/drivers/media/platform/omap/omap_vout_vrfb.c @@ -16,7 +16,6 @@ #include <media/videobuf-dma-contig.h> #include <media/v4l2-device.h> -#include <linux/omap-dma.h> #include <video/omapvrfb.h> #include "omap_voutdef.h" @@ -63,7 +62,7 @@ static int omap_vout_allocate_vrfb_buffers(struct omap_vout_device *vout, /* * Wakes up the application once the DMA transfer to VRFB space is completed. */ -static void omap_vout_vrfb_dma_tx_callback(int lch, u16 ch_status, void *data) +static void omap_vout_vrfb_dma_tx_callback(void *data) { struct vid_vrfb_dma *t = (struct vid_vrfb_dma *) data; @@ -94,6 +93,7 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, int ret = 0, i, j; struct omap_vout_device *vout; struct video_device *vfd; + dma_cap_mask_t mask; int image_width, image_height; int vrfb_num_bufs = VRFB_NUM_BUFS; struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev); @@ -131,18 +131,27 @@ int omap_vout_setup_vrfb_bufs(struct platform_device *pdev, int vid_num, /* * Request and Initialize DMA, for DMA based VRFB transfer */ - vout->vrfb_dma_tx.dev_id = OMAP_DMA_NO_DEVICE; - vout->vrfb_dma_tx.dma_ch = -1; - vout->vrfb_dma_tx.req_status = DMA_CHAN_ALLOTED; - ret = omap_request_dma(vout->vrfb_dma_tx.dev_id, "VRFB DMA TX", - omap_vout_vrfb_dma_tx_callback, - (void *) &vout->vrfb_dma_tx, &vout->vrfb_dma_tx.dma_ch); - if (ret < 0) { + dma_cap_zero(mask); + dma_cap_set(DMA_INTERLEAVE, mask); + vout->vrfb_dma_tx.chan = dma_request_chan_by_mask(&mask); + if (IS_ERR(vout->vrfb_dma_tx.chan)) { vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + } else { + size_t xt_size = sizeof(struct dma_interleaved_template) + + sizeof(struct data_chunk); + + vout->vrfb_dma_tx.xt = kzalloc(xt_size, GFP_KERNEL); + if (!vout->vrfb_dma_tx.xt) { + dma_release_channel(vout->vrfb_dma_tx.chan); + vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; + } + } + + if (vout->vrfb_dma_tx.req_status == DMA_CHAN_NOT_ALLOTED) dev_info(&pdev->dev, ": failed to allocate DMA Channel for video%d\n", vfd->minor); - } + init_waitqueue_head(&vout->vrfb_dma_tx.wait); /* statically allocated the VRFB buffer is done through @@ -177,7 +186,9 @@ void omap_vout_release_vrfb(struct omap_vout_device *vout) if (vout->vrfb_dma_tx.req_status == DMA_CHAN_ALLOTED) { vout->vrfb_dma_tx.req_status = DMA_CHAN_NOT_ALLOTED; - omap_free_dma(vout->vrfb_dma_tx.dma_ch); + kfree(vout->vrfb_dma_tx.xt); + dmaengine_terminate_sync(vout->vrfb_dma_tx.chan); + dma_release_channel(vout->vrfb_dma_tx.chan); } } @@ -219,70 +230,84 @@ int omap_vout_vrfb_buffer_setup(struct omap_vout_device *vout, } int omap_vout_prepare_vrfb(struct omap_vout_device *vout, - struct videobuf_buffer *vb) + struct videobuf_buffer *vb) { - dma_addr_t dmabuf; - struct vid_vrfb_dma *tx; + struct dma_async_tx_descriptor *tx; + enum dma_ctrl_flags flags; + struct dma_chan *chan = vout->vrfb_dma_tx.chan; + struct dma_device *dmadev = chan->device; + struct dma_interleaved_template *xt = vout->vrfb_dma_tx.xt; + dma_cookie_t cookie; + enum dma_status status; enum dss_rotation rotation; - u32 dest_frame_index = 0, src_element_index = 0; - u32 dest_element_index = 0, src_frame_index = 0; - u32 elem_count = 0, frame_count = 0, pixsize = 2; + size_t dst_icg; + u32 pixsize; if (!is_rotation_enabled(vout)) return 0; - dmabuf = vout->buf_phy_addr[vb->i]; /* If rotation is enabled, copy input buffer into VRFB * memory space using DMA. We are copying input buffer * into VRFB memory space of desired angle and DSS will * read image VRFB memory for 0 degree angle */ + pixsize = vout->bpp * vout->vrfb_bpp; - /* - * DMA transfer in double index mode - */ + dst_icg = ((MAX_PIXELS_PER_LINE * pixsize) - + (vout->pix.width * vout->bpp)) + 1; + + xt->src_start = vout->buf_phy_addr[vb->i]; + xt->dst_start = vout->vrfb_context[vb->i].paddr[0]; + + xt->numf = vout->pix.height; + xt->frame_size = 1; + xt->sgl[0].size = vout->pix.width * vout->bpp; + xt->sgl[0].icg = dst_icg; + + xt->dir = DMA_MEM_TO_MEM; + xt->src_sgl = false; + xt->src_inc = true; + xt->dst_sgl = true; + xt->dst_inc = true; + + tx = dmadev->device_prep_interleaved_dma(chan, xt, flags); + if (tx == NULL) { + pr_err("%s: DMA interleaved prep error\n", __func__); + return -EINVAL; + } - /* Frame index */ - dest_frame_index = ((MAX_PIXELS_PER_LINE * pixsize) - - (vout->pix.width * vout->bpp)) + 1; - - /* Source and destination parameters */ - src_element_index = 0; - src_frame_index = 0; - dest_element_index = 1; - /* Number of elements per frame */ - elem_count = vout->pix.width * vout->bpp; - frame_count = vout->pix.height; - tx = &vout->vrfb_dma_tx; - tx->tx_status = 0; - omap_set_dma_transfer_params(tx->dma_ch, OMAP_DMA_DATA_TYPE_S32, - (elem_count / 4), frame_count, OMAP_DMA_SYNC_ELEMENT, - tx->dev_id, 0x0); - /* src_port required only for OMAP1 */ - omap_set_dma_src_params(tx->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, - dmabuf, src_element_index, src_frame_index); - /*set dma source burst mode for VRFB */ - omap_set_dma_src_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16); - rotation = calc_rotation(vout); + tx->callback = omap_vout_vrfb_dma_tx_callback; + tx->callback_param = &vout->vrfb_dma_tx; + + cookie = dmaengine_submit(tx); + if (dma_submit_error(cookie)) { + pr_err("%s: dmaengine_submit failed (%d)\n", __func__, cookie); + return -EINVAL; + } - /* dest_port required only for OMAP1 */ - omap_set_dma_dest_params(tx->dma_ch, 0, OMAP_DMA_AMODE_DOUBLE_IDX, - vout->vrfb_context[vb->i].paddr[0], dest_element_index, - dest_frame_index); - /*set dma dest burst mode for VRFB */ - omap_set_dma_dest_burst_mode(tx->dma_ch, OMAP_DMA_DATA_BURST_16); - omap_dma_set_global_params(DMA_DEFAULT_ARB_RATE, 0x20, 0); + vout->vrfb_dma_tx.tx_status = 0; + dma_async_issue_pending(chan); - omap_start_dma(tx->dma_ch); - wait_event_interruptible_timeout(tx->wait, tx->tx_status == 1, + wait_event_interruptible_timeout(vout->vrfb_dma_tx.wait, + vout->vrfb_dma_tx.tx_status == 1, VRFB_TX_TIMEOUT); - if (tx->tx_status == 0) { - omap_stop_dma(tx->dma_ch); + status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); + + if (vout->vrfb_dma_tx.tx_status == 0) { + pr_err("%s: Timeout while waiting for DMA\n", __func__); + dmaengine_terminate_sync(chan); + return -EINVAL; + } else if (status != DMA_COMPLETE) { + pr_err("%s: DMA completion %s status\n", __func__, + status == DMA_ERROR ? "error" : "busy"); + dmaengine_terminate_sync(chan); return -EINVAL; } + /* Store buffers physical address into an array. Addresses * from this array will be used to configure DSS */ + rotation = calc_rotation(vout); vout->queued_buf_addr[vb->i] = (u8 *) vout->vrfb_context[vb->i].paddr[rotation]; return 0; diff --git a/drivers/media/platform/omap/omap_voutdef.h b/drivers/media/platform/omap/omap_voutdef.h index 80c79fabdf95..56b630b1c8b4 100644 --- a/drivers/media/platform/omap/omap_voutdef.h +++ b/drivers/media/platform/omap/omap_voutdef.h @@ -14,6 +14,7 @@ #include <media/v4l2-ctrls.h> #include <video/omapfb_dss.h> #include <video/omapvrfb.h> +#include <linux/dmaengine.h> #define YUYV_BPP 2 #define RGB565_BPP 2 @@ -81,8 +82,9 @@ enum vout_rotaion_type { * for VRFB hidden buffer */ struct vid_vrfb_dma { - int dev_id; - int dma_ch; + struct dma_chan *chan; + struct dma_interleaved_template *xt; + int req_status; int tx_status; wait_queue_head_t wait; diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index 776d2bae6979..41eef376eb2d 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -76,7 +76,7 @@ static void venus_sys_error_handler(struct work_struct *work) hfi_core_deinit(core, true); hfi_destroy(core); mutex_lock(&core->lock); - venus_shutdown(&core->dev_fw); + venus_shutdown(core->dev); pm_runtime_put_sync(core->dev); @@ -84,7 +84,7 @@ static void venus_sys_error_handler(struct work_struct *work) pm_runtime_get_sync(core->dev); - ret |= venus_boot(core->dev, &core->dev_fw, core->res->fwname); + ret |= venus_boot(core->dev, core->res->fwname); ret |= hfi_core_resume(core, true); @@ -137,7 +137,7 @@ static int venus_clks_enable(struct venus_core *core) return 0; err: - while (--i) + while (i--) clk_disable_unprepare(core->clks[i]); return ret; @@ -207,7 +207,7 @@ static int venus_probe(struct platform_device *pdev) if (ret < 0) goto err_runtime_disable; - ret = venus_boot(dev, &core->dev_fw, core->res->fwname); + ret = venus_boot(dev, core->res->fwname); if (ret) goto err_runtime_disable; @@ -238,7 +238,7 @@ err_dev_unregister: err_core_deinit: hfi_core_deinit(core, false); err_venus_shutdown: - venus_shutdown(&core->dev_fw); + venus_shutdown(dev); err_runtime_disable: pm_runtime_set_suspended(dev); pm_runtime_disable(dev); @@ -259,7 +259,7 @@ static int venus_remove(struct platform_device *pdev) WARN_ON(ret); hfi_destroy(core); - venus_shutdown(&core->dev_fw); + venus_shutdown(dev); of_platform_depopulate(dev); pm_runtime_put_sync(dev); @@ -270,8 +270,7 @@ static int venus_remove(struct platform_device *pdev) return ret; } -#ifdef CONFIG_PM -static int venus_runtime_suspend(struct device *dev) +static __maybe_unused int venus_runtime_suspend(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); int ret; @@ -283,7 +282,7 @@ static int venus_runtime_suspend(struct device *dev) return ret; } -static int venus_runtime_resume(struct device *dev) +static __maybe_unused int venus_runtime_resume(struct device *dev) { struct venus_core *core = dev_get_drvdata(dev); int ret; @@ -302,7 +301,6 @@ err_clks_disable: venus_clks_disable(core); return ret; } -#endif static const struct dev_pm_ops venus_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h index e542700eee32..cba092bcb76d 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -101,7 +101,6 @@ struct venus_core { struct device *dev; struct device *dev_dec; struct device *dev_enc; - struct device dev_fw; struct mutex lock; struct list_head instances; atomic_t insts_count; diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 1b1a4f355918..521d4b36c090 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -12,97 +12,87 @@ * */ -#include <linux/dma-mapping.h> +#include <linux/device.h> #include <linux/firmware.h> #include <linux/kernel.h> +#include <linux/io.h> #include <linux/of.h> -#include <linux/of_reserved_mem.h> -#include <linux/slab.h> +#include <linux/of_address.h> #include <linux/qcom_scm.h> +#include <linux/sizes.h> #include <linux/soc/qcom/mdt_loader.h> #include "firmware.h" #define VENUS_PAS_ID 9 -#define VENUS_FW_MEM_SIZE SZ_8M +#define VENUS_FW_MEM_SIZE (6 * SZ_1M) -static void device_release_dummy(struct device *dev) -{ - of_reserved_mem_device_release(dev); -} - -int venus_boot(struct device *parent, struct device *fw_dev, const char *fwname) +int venus_boot(struct device *dev, const char *fwname) { const struct firmware *mdt; + struct device_node *node; phys_addr_t mem_phys; + struct resource r; ssize_t fw_size; size_t mem_size; void *mem_va; int ret; - if (!qcom_scm_is_available()) + if (!IS_ENABLED(CONFIG_QCOM_MDT_LOADER) || !qcom_scm_is_available()) return -EPROBE_DEFER; - fw_dev->parent = parent; - fw_dev->release = device_release_dummy; + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!node) { + dev_err(dev, "no memory-region specified\n"); + return -EINVAL; + } - ret = dev_set_name(fw_dev, "%s:%s", dev_name(parent), "firmware"); + ret = of_address_to_resource(node, 0, &r); if (ret) return ret; - ret = device_register(fw_dev); - if (ret < 0) - return ret; + mem_phys = r.start; + mem_size = resource_size(&r); - ret = of_reserved_mem_device_init_by_idx(fw_dev, parent->of_node, 0); - if (ret) - goto err_unreg_device; + if (mem_size < VENUS_FW_MEM_SIZE) + return -EINVAL; - mem_size = VENUS_FW_MEM_SIZE; - - mem_va = dmam_alloc_coherent(fw_dev, mem_size, &mem_phys, GFP_KERNEL); + mem_va = memremap(r.start, mem_size, MEMREMAP_WC); if (!mem_va) { - ret = -ENOMEM; - goto err_unreg_device; + dev_err(dev, "unable to map memory region: %pa+%zx\n", + &r.start, mem_size); + return -ENOMEM; } - ret = request_firmware(&mdt, fwname, fw_dev); + ret = request_firmware(&mdt, fwname, dev); if (ret < 0) - goto err_unreg_device; + goto err_unmap; fw_size = qcom_mdt_get_size(mdt); if (fw_size < 0) { ret = fw_size; release_firmware(mdt); - goto err_unreg_device; + goto err_unmap; } - ret = qcom_mdt_load(fw_dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys, + ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys, mem_size); release_firmware(mdt); if (ret) - goto err_unreg_device; + goto err_unmap; ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID); if (ret) - goto err_unreg_device; - - return 0; + goto err_unmap; -err_unreg_device: - device_unregister(fw_dev); +err_unmap: + memunmap(mem_va); return ret; } -int venus_shutdown(struct device *fw_dev) +int venus_shutdown(struct device *dev) { - int ret; - - ret = qcom_scm_pas_shutdown(VENUS_PAS_ID); - device_unregister(fw_dev); - memset(fw_dev, 0, sizeof(*fw_dev)); - - return ret; + return qcom_scm_pas_shutdown(VENUS_PAS_ID); } diff --git a/drivers/media/platform/qcom/venus/firmware.h b/drivers/media/platform/qcom/venus/firmware.h index f81a98979798..428efb56d339 100644 --- a/drivers/media/platform/qcom/venus/firmware.h +++ b/drivers/media/platform/qcom/venus/firmware.h @@ -16,8 +16,7 @@ struct device; -int venus_boot(struct device *parent, struct device *fw_dev, - const char *fwname); -int venus_shutdown(struct device *fw_dev); +int venus_boot(struct device *dev, const char *fwname); +int venus_shutdown(struct device *dev); #endif diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index f8841713e417..a681ae5381d6 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -239,11 +239,12 @@ static void hfi_sys_init_done(struct venus_core *core, struct venus_inst *inst, break; } - if (!error) { - rem_bytes -= read_bytes; - data += read_bytes; - num_properties--; - } + if (error) + break; + + rem_bytes -= read_bytes; + data += read_bytes; + num_properties--; } err_no_prop: diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/sti/bdisp/bdisp-debug.c index 7af66860d624..2cc289e4dea1 100644 --- a/drivers/media/platform/sti/bdisp/bdisp-debug.c +++ b/drivers/media/platform/sti/bdisp/bdisp-debug.c @@ -104,7 +104,7 @@ static void bdisp_dbg_dump_ins(struct seq_file *s, u32 val) if (val & BLT_INS_IRQ) seq_puts(s, "IRQ - "); - seq_puts(s, "\n"); + seq_putc(s, '\n'); } static void bdisp_dbg_dump_tty(struct seq_file *s, u32 val) @@ -153,7 +153,7 @@ static void bdisp_dbg_dump_tty(struct seq_file *s, u32 val) if (val & BLT_TTY_BIG_END) seq_puts(s, "BigEndian - "); - seq_puts(s, "\n"); + seq_putc(s, '\n'); } static void bdisp_dbg_dump_xy(struct seq_file *s, u32 val, char *name) @@ -230,7 +230,7 @@ static void bdisp_dbg_dump_sty(struct seq_file *s, seq_puts(s, "BigEndian - "); done: - seq_puts(s, "\n"); + seq_putc(s, '\n'); } static void bdisp_dbg_dump_fctl(struct seq_file *s, u32 val) @@ -247,7 +247,7 @@ static void bdisp_dbg_dump_fctl(struct seq_file *s, u32 val) else if ((val & BLT_FCTL_HV_SCALE) == BLT_FCTL_HV_SAMPLE) seq_puts(s, "Sample Chroma"); - seq_puts(s, "\n"); + seq_putc(s, '\n'); } static void bdisp_dbg_dump_rsf(struct seq_file *s, u32 val, char *name) @@ -266,7 +266,7 @@ static void bdisp_dbg_dump_rsf(struct seq_file *s, u32 val, char *name) seq_printf(s, "V: %d(6.10) / scale~%dx0.1", inc, 1024 * 10 / inc); done: - seq_puts(s, "\n"); + seq_putc(s, '\n'); } static void bdisp_dbg_dump_rzi(struct seq_file *s, u32 val, char *name) @@ -281,7 +281,7 @@ static void bdisp_dbg_dump_rzi(struct seq_file *s, u32 val, char *name) seq_printf(s, "V: init=%d repeat=%d", val & 0x3FF, (val >> 12) & 7); done: - seq_puts(s, "\n"); + seq_putc(s, '\n'); } static void bdisp_dbg_dump_ivmx(struct seq_file *s, @@ -293,7 +293,7 @@ static void bdisp_dbg_dump_ivmx(struct seq_file *s, seq_printf(s, "IVMX3\t0x%08X\t", c3); if (!c0 && !c1 && !c2 && !c3) { - seq_puts(s, "\n"); + seq_putc(s, '\n'); return; } diff --git a/drivers/media/platform/vimc/vimc-capture.c b/drivers/media/platform/vimc/vimc-capture.c index 14cb32e21130..88a1e5670c72 100644 --- a/drivers/media/platform/vimc/vimc-capture.c +++ b/drivers/media/platform/vimc/vimc-capture.c @@ -517,21 +517,22 @@ static int vimc_cap_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id vimc_cap_driver_ids[] = { + { + .name = VIMC_CAP_DRV_NAME, + }, + { } +}; + static struct platform_driver vimc_cap_pdrv = { .probe = vimc_cap_probe, .remove = vimc_cap_remove, + .id_table = vimc_cap_driver_ids, .driver = { .name = VIMC_CAP_DRV_NAME, }, }; -static const struct platform_device_id vimc_cap_driver_ids[] = { - { - .name = VIMC_CAP_DRV_NAME, - }, - { } -}; - module_platform_driver(vimc_cap_pdrv); MODULE_DEVICE_TABLE(platform, vimc_cap_driver_ids); diff --git a/drivers/media/platform/vimc/vimc-debayer.c b/drivers/media/platform/vimc/vimc-debayer.c index 35b15bd4d61d..033a131f67af 100644 --- a/drivers/media/platform/vimc/vimc-debayer.c +++ b/drivers/media/platform/vimc/vimc-debayer.c @@ -577,21 +577,22 @@ static int vimc_deb_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id vimc_deb_driver_ids[] = { + { + .name = VIMC_DEB_DRV_NAME, + }, + { } +}; + static struct platform_driver vimc_deb_pdrv = { .probe = vimc_deb_probe, .remove = vimc_deb_remove, + .id_table = vimc_deb_driver_ids, .driver = { .name = VIMC_DEB_DRV_NAME, }, }; -static const struct platform_device_id vimc_deb_driver_ids[] = { - { - .name = VIMC_DEB_DRV_NAME, - }, - { } -}; - module_platform_driver(vimc_deb_pdrv); MODULE_DEVICE_TABLE(platform, vimc_deb_driver_ids); diff --git a/drivers/media/platform/vimc/vimc-scaler.c b/drivers/media/platform/vimc/vimc-scaler.c index fe77505d2679..0a3e086e12f3 100644 --- a/drivers/media/platform/vimc/vimc-scaler.c +++ b/drivers/media/platform/vimc/vimc-scaler.c @@ -431,21 +431,22 @@ static int vimc_sca_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id vimc_sca_driver_ids[] = { + { + .name = VIMC_SCA_DRV_NAME, + }, + { } +}; + static struct platform_driver vimc_sca_pdrv = { .probe = vimc_sca_probe, .remove = vimc_sca_remove, + .id_table = vimc_sca_driver_ids, .driver = { .name = VIMC_SCA_DRV_NAME, }, }; -static const struct platform_device_id vimc_sca_driver_ids[] = { - { - .name = VIMC_SCA_DRV_NAME, - }, - { } -}; - module_platform_driver(vimc_sca_pdrv); MODULE_DEVICE_TABLE(platform, vimc_sca_driver_ids); diff --git a/drivers/media/platform/vimc/vimc-sensor.c b/drivers/media/platform/vimc/vimc-sensor.c index ebdbbe8c05ed..615c2b18dcfc 100644 --- a/drivers/media/platform/vimc/vimc-sensor.c +++ b/drivers/media/platform/vimc/vimc-sensor.c @@ -365,21 +365,22 @@ static int vimc_sen_remove(struct platform_device *pdev) return 0; } +static const struct platform_device_id vimc_sen_driver_ids[] = { + { + .name = VIMC_SEN_DRV_NAME, + }, + { } +}; + static struct platform_driver vimc_sen_pdrv = { .probe = vimc_sen_probe, .remove = vimc_sen_remove, + .id_table = vimc_sen_driver_ids, .driver = { .name = VIMC_SEN_DRV_NAME, }, }; -static const struct platform_device_id vimc_sen_driver_ids[] = { - { - .name = VIMC_SEN_DRV_NAME, - }, - { } -}; - module_platform_driver(vimc_sen_pdrv); MODULE_DEVICE_TABLE(platform, vimc_sen_driver_ids); diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index 7240223dc15a..17e82a9a0109 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -610,10 +610,21 @@ static int wl1273_fm_start(struct wl1273_device *radio, int new_mode) } } - if (radio->rds_on) + if (radio->rds_on) { r = core->write(core, WL1273_RDS_DATA_ENB, 1); - else + if (r) { + dev_err(dev, "%s: RDS_DATA_ENB ON fails\n", + __func__); + goto fail; + } + } else { r = core->write(core, WL1273_RDS_DATA_ENB, 0); + if (r) { + dev_err(dev, "%s: RDS_DATA_ENB OFF fails\n", + __func__); + goto fail; + } + } } else { dev_warn(dev, "%s: Illegal mode.\n", __func__); } diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index a30af91710fe..d2223c04e9ad 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -266,7 +266,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, if (!dev->rx_resolution) return -ENOTTY; - val = dev->rx_resolution; + val = dev->rx_resolution / 1000; break; case LIRC_SET_WIDEBAND_RECEIVER: diff --git a/drivers/media/tuners/fc0011.c b/drivers/media/tuners/fc0011.c index 192b1c7740df..145407dee3db 100644 --- a/drivers/media/tuners/fc0011.c +++ b/drivers/media/tuners/fc0011.c @@ -342,6 +342,7 @@ static int fc0011_set_params(struct dvb_frontend *fe) switch (vco_sel) { default: WARN_ON(1); + return -EINVAL; case 0: if (vco_cal < 8) { regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); diff --git a/drivers/media/tuners/mxl5005s.c b/drivers/media/tuners/mxl5005s.c index 353744fee053..dd59c2c0e4a5 100644 --- a/drivers/media/tuners/mxl5005s.c +++ b/drivers/media/tuners/mxl5005s.c @@ -2737,8 +2737,6 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq) status += MXL_ControlWrite(fe, TG_LO_DIVVAL, 0x0); status += MXL_ControlWrite(fe, TG_LO_SELVAL, 0x7); divider_val = 2 ; - Fmax = FmaxBin ; - Fmin = FminBin ; } /* TG_DIV_VAL */ diff --git a/drivers/media/usb/au0828/au0828-input.c b/drivers/media/usb/au0828/au0828-input.c index 9ec919c68482..9d82ec0a4b64 100644 --- a/drivers/media/usb/au0828/au0828-input.c +++ b/drivers/media/usb/au0828/au0828-input.c @@ -351,7 +351,7 @@ int au0828_rc_register(struct au0828_dev *dev) if (err) goto error; - pr_info("Remote controller %s initalized\n", ir->name); + pr_info("Remote controller %s initialized\n", ir->name); return 0; diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 594360a63c18..a91fdad8f8d4 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -207,15 +207,13 @@ static int lme2510_stream_restart(struct dvb_usb_device *d) struct lme2510_state *st = d->priv; u8 all_pids[] = LME_ALL_PIDS; u8 stream_on[] = LME_ST_ON_W; - int ret; u8 rbuff[1]; if (st->pid_off) - ret = lme2510_usb_talk(d, all_pids, sizeof(all_pids), - rbuff, sizeof(rbuff)); + lme2510_usb_talk(d, all_pids, sizeof(all_pids), + rbuff, sizeof(rbuff)); /*Restart Stream Command*/ - ret = lme2510_usb_talk(d, stream_on, sizeof(stream_on), - rbuff, sizeof(rbuff)); - return ret; + return lme2510_usb_talk(d, stream_on, sizeof(stream_on), + rbuff, sizeof(rbuff)); } static int lme2510_enable_pid(struct dvb_usb_device *d, u8 index, u16 pid_out) diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c index 08acdd32e412..bea1b4764a66 100644 --- a/drivers/media/usb/dvb-usb/dib0700_core.c +++ b/drivers/media/usb/dvb-usb/dib0700_core.c @@ -215,13 +215,14 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, USB_CTRL_GET_TIMEOUT); if (result < 0) { deb_info("i2c read error (status = %d)\n", result); - break; + goto unlock; } if (msg[i].len > sizeof(st->buf)) { deb_info("buffer too small to fit %d bytes\n", msg[i].len); - return -EIO; + result = -EIO; + goto unlock; } memcpy(msg[i].buf, st->buf, msg[i].len); @@ -233,8 +234,8 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, /* Write request */ if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - mutex_unlock(&d->i2c_mutex); - return -EINTR; + result = -EINTR; + goto unlock; } st->buf[0] = REQUEST_NEW_I2C_WRITE; st->buf[1] = msg[i].addr << 1; @@ -247,7 +248,9 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, if (msg[i].len > sizeof(st->buf) - 4) { deb_info("i2c message to big: %d\n", msg[i].len); - return -EIO; + mutex_unlock(&d->usb_mutex); + result = -EIO; + goto unlock; } /* The Actual i2c payload */ @@ -269,8 +272,11 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, } } } + result = i; + +unlock: mutex_unlock(&d->i2c_mutex); - return i; + return result; } /* @@ -281,7 +287,7 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, { struct dvb_usb_device *d = i2c_get_adapdata(adap); struct dib0700_state *st = d->priv; - int i,len; + int i, len, result; if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EINTR; @@ -298,7 +304,8 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, if (msg[i].len > sizeof(st->buf) - 2) { deb_info("i2c xfer to big: %d\n", msg[i].len); - return -EIO; + result = -EIO; + goto unlock; } memcpy(&st->buf[2], msg[i].buf, msg[i].len); @@ -313,13 +320,15 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, if (len <= 0) { deb_info("I2C read failed on address 0x%02x\n", msg[i].addr); - break; + result = -EIO; + goto unlock; } if (msg[i + 1].len > sizeof(st->buf)) { deb_info("i2c xfer buffer to small for %d\n", msg[i].len); - return -EIO; + result = -EIO; + goto unlock; } memcpy(msg[i + 1].buf, st->buf, msg[i + 1].len); @@ -328,14 +337,17 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, i++; } else { st->buf[0] = REQUEST_I2C_WRITE; - if (dib0700_ctrl_wr(d, st->buf, msg[i].len + 2) < 0) - break; + result = dib0700_ctrl_wr(d, st->buf, msg[i].len + 2); + if (result < 0) + goto unlock; } } + result = i; +unlock: mutex_unlock(&d->usb_mutex); mutex_unlock(&d->i2c_mutex); - return i; + return result; } static int dib0700_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msg, diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 146341aeb782..4c57fd7929cb 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -1193,6 +1193,22 @@ struct em28xx_board em28xx_boards[] = { .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_FREQ_400_KHZ, }, + [EM2884_BOARD_TERRATEC_H6] = { + .name = "Terratec Cinergy H6 rev. 2", + .has_dvb = 1, + .ir_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS, +#if 0 + .tuner_type = TUNER_PHILIPS_TDA8290, + .tuner_addr = 0x41, + .dvb_gpio = terratec_h5_digital, /* FIXME: probably wrong */ + .tuner_gpio = terratec_h5_gpio, +#else + .tuner_type = TUNER_ABSENT, +#endif + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + }, [EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C] = { .name = "Hauppauge WinTV HVR 930C", .has_dvb = 1, @@ -2496,6 +2512,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2884_BOARD_TERRATEC_H5 }, { USB_DEVICE(0x0ccd, 0x10b6), /* H5 Rev. 3 */ .driver_info = EM2884_BOARD_TERRATEC_H5 }, + { USB_DEVICE(0x0ccd, 0x10b2), /* H6 */ + .driver_info = EM2884_BOARD_TERRATEC_H6 }, { USB_DEVICE(0x0ccd, 0x0084), .driver_info = EM2860_BOARD_TERRATEC_AV350 }, { USB_DEVICE(0x0ccd, 0x0096), diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 82edd37f0d73..4a7db623fe29 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1522,6 +1522,7 @@ static int em28xx_dvb_init(struct em28xx *dev) break; case EM2884_BOARD_ELGATO_EYETV_HYBRID_2008: case EM2884_BOARD_CINERGY_HTC_STICK: + case EM2884_BOARD_TERRATEC_H6: terratec_htc_stick_init(dev); /* attach demodulator */ diff --git a/drivers/media/usb/em28xx/em28xx-i2c.c b/drivers/media/usb/em28xx/em28xx-i2c.c index 8c472d5adb50..60b195c157b8 100644 --- a/drivers/media/usb/em28xx/em28xx-i2c.c +++ b/drivers/media/usb/em28xx/em28xx-i2c.c @@ -982,8 +982,6 @@ int em28xx_i2c_register(struct em28xx *dev, unsigned bus, dev_err(&dev->intf->dev, "%s: em28xx_i2_eeprom failed! retval [%d]\n", __func__, retval); - - return retval; } } diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index eba75736e654..ca9673917ad5 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -821,7 +821,7 @@ static int em28xx_ir_init(struct em28xx *dev) if (err) goto error; - dev_info(&dev->intf->dev, "Input extension successfully initalized\n"); + dev_info(&dev->intf->dev, "Input extension successfully initialized\n"); return 0; diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index e8d97d5ec161..88084f24f033 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -148,6 +148,7 @@ #define EM28178_BOARD_PLEX_PX_BCUD 98 #define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB 99 #define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 100 +#define EM2884_BOARD_TERRATEC_H6 101 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/drivers/media/usb/pulse8-cec/pulse8-cec.c b/drivers/media/usb/pulse8-cec/pulse8-cec.c index c843070f24c1..f9ed9c950247 100644 --- a/drivers/media/usb/pulse8-cec/pulse8-cec.c +++ b/drivers/media/usb/pulse8-cec/pulse8-cec.c @@ -51,7 +51,7 @@ MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver"); MODULE_LICENSE("GPL"); static int debug; -static int persistent_config = 1; +static int persistent_config; module_param(debug, int, 0644); module_param(persistent_config, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-1)"); diff --git a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c index f203699e9c1b..65692576690f 100644 --- a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c +++ b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c @@ -116,21 +116,19 @@ static void rain_irq_work_handler(struct work_struct *work) while (true) { unsigned long flags; - bool exit_loop = false; char data; spin_lock_irqsave(&rain->buf_lock, flags); - if (rain->buf_len) { - data = rain->buf[rain->buf_rd_idx]; - rain->buf_len--; - rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff; - } else { - exit_loop = true; + if (!rain->buf_len) { + spin_unlock_irqrestore(&rain->buf_lock, flags); + break; } - spin_unlock_irqrestore(&rain->buf_lock, flags); - if (exit_loop) - break; + data = rain->buf[rain->buf_rd_idx]; + rain->buf_len--; + rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff; + + spin_unlock_irqrestore(&rain->buf_lock, flags); if (!rain->cmd_started && data != '?') continue; diff --git a/drivers/media/usb/stkwebcam/stk-sensor.c b/drivers/media/usb/stkwebcam/stk-sensor.c index 985af9933c7e..c1d4505f84ea 100644 --- a/drivers/media/usb/stkwebcam/stk-sensor.c +++ b/drivers/media/usb/stkwebcam/stk-sensor.c @@ -41,6 +41,8 @@ /* It seems the i2c bus is controlled with these registers */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "stk-webcam.h" #define STK_IIC_BASE (0x0200) @@ -239,8 +241,8 @@ static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val) } while (tmpval == 0 && i < MAX_RETRIES); if (tmpval != STK_IIC_STAT_TX_OK) { if (tmpval) - STK_ERROR("stk_sensor_outb failed, status=0x%02x\n", - tmpval); + pr_err("stk_sensor_outb failed, status=0x%02x\n", + tmpval); return 1; } else return 0; @@ -262,8 +264,8 @@ static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val) } while (tmpval == 0 && i < MAX_RETRIES); if (tmpval != STK_IIC_STAT_RX_OK) { if (tmpval) - STK_ERROR("stk_sensor_inb failed, status=0x%02x\n", - tmpval); + pr_err("stk_sensor_inb failed, status=0x%02x\n", + tmpval); return 1; } @@ -366,29 +368,29 @@ int stk_sensor_init(struct stk_camera *dev) if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES) || stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS) || stk_sensor_outb(dev, REG_COM7, COM7_RESET)) { - STK_ERROR("Sensor resetting failed\n"); + pr_err("Sensor resetting failed\n"); return -ENODEV; } msleep(10); /* Read the manufacturer ID: ov = 0x7FA2 */ if (stk_sensor_inb(dev, REG_MIDH, &idh) || stk_sensor_inb(dev, REG_MIDL, &idl)) { - STK_ERROR("Strange error reading sensor ID\n"); + pr_err("Strange error reading sensor ID\n"); return -ENODEV; } if (idh != 0x7f || idl != 0xa2) { - STK_ERROR("Huh? you don't have a sensor from ovt\n"); + pr_err("Huh? you don't have a sensor from ovt\n"); return -ENODEV; } if (stk_sensor_inb(dev, REG_PID, &idh) || stk_sensor_inb(dev, REG_VER, &idl)) { - STK_ERROR("Could not read sensor model\n"); + pr_err("Could not read sensor model\n"); return -ENODEV; } stk_sensor_write_regvals(dev, ov_initvals); msleep(10); - STK_INFO("OmniVision sensor detected, id %02X%02X at address %x\n", - idh, idl, SENSOR_ADDRESS); + pr_info("OmniVision sensor detected, id %02X%02X at address %x\n", + idh, idl, SENSOR_ADDRESS); return 0; } @@ -520,7 +522,8 @@ int stk_sensor_configure(struct stk_camera *dev) case MODE_SXGA: com7 = COM7_FMT_SXGA; dummylines = 0; break; - default: STK_ERROR("Unsupported mode %d\n", dev->vsettings.mode); + default: + pr_err("Unsupported mode %d\n", dev->vsettings.mode); return -EFAULT; } switch (dev->vsettings.palette) { @@ -544,7 +547,8 @@ int stk_sensor_configure(struct stk_camera *dev) com7 |= COM7_PBAYER; rv = ov_fmt_bayer; break; - default: STK_ERROR("Unsupported colorspace\n"); + default: + pr_err("Unsupported colorspace\n"); return -EFAULT; } /*FIXME sometimes the sensor go to a bad state @@ -564,7 +568,7 @@ int stk_sensor_configure(struct stk_camera *dev) switch (dev->vsettings.mode) { case MODE_VGA: if (stk_sensor_set_hw(dev, 302, 1582, 6, 486)) - STK_ERROR("stk_sensor_set_hw failed (VGA)\n"); + pr_err("stk_sensor_set_hw failed (VGA)\n"); break; case MODE_SXGA: case MODE_CIF: @@ -572,7 +576,7 @@ int stk_sensor_configure(struct stk_camera *dev) case MODE_QCIF: /*FIXME These settings seem ignored by the sensor if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034)) - STK_ERROR("stk_sensor_set_hw failed (SXGA)\n"); + pr_err("stk_sensor_set_hw failed (SXGA)\n"); */ break; } diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 6e7fc36b658f..90d4a08cda31 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -18,6 +18,8 @@ * GNU General Public License for more details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> @@ -175,15 +177,15 @@ static int stk_start_stream(struct stk_camera *dev) if (!is_present(dev)) return -ENODEV; if (!is_memallocd(dev) || !is_initialised(dev)) { - STK_ERROR("FIXME: Buffers are not allocated\n"); + pr_err("FIXME: Buffers are not allocated\n"); return -EFAULT; } ret = usb_set_interface(dev->udev, 0, 5); if (ret < 0) - STK_ERROR("usb_set_interface failed !\n"); + pr_err("usb_set_interface failed !\n"); if (stk_sensor_wakeup(dev)) - STK_ERROR("error awaking the sensor\n"); + pr_err("error awaking the sensor\n"); stk_camera_read_reg(dev, 0x0116, &value_116); stk_camera_read_reg(dev, 0x0117, &value_117); @@ -224,9 +226,9 @@ static int stk_stop_stream(struct stk_camera *dev) unset_streaming(dev); if (usb_set_interface(dev->udev, 0, 0)) - STK_ERROR("usb_set_interface failed !\n"); + pr_err("usb_set_interface failed !\n"); if (stk_sensor_sleep(dev)) - STK_ERROR("error suspending the sensor\n"); + pr_err("error suspending the sensor\n"); } return 0; } @@ -313,7 +315,7 @@ static void stk_isoc_handler(struct urb *urb) dev = (struct stk_camera *) urb->context; if (dev == NULL) { - STK_ERROR("isoc_handler called with NULL device !\n"); + pr_err("isoc_handler called with NULL device !\n"); return; } @@ -326,14 +328,13 @@ static void stk_isoc_handler(struct urb *urb) spin_lock_irqsave(&dev->spinlock, flags); if (urb->status != -EINPROGRESS && urb->status != 0) { - STK_ERROR("isoc_handler: urb->status == %d\n", urb->status); + pr_err("isoc_handler: urb->status == %d\n", urb->status); goto resubmit; } if (list_empty(&dev->sio_avail)) { /*FIXME Stop streaming after a while */ - (void) (printk_ratelimit() && - STK_ERROR("isoc_handler without available buffer!\n")); + pr_err_ratelimited("isoc_handler without available buffer!\n"); goto resubmit; } fb = list_first_entry(&dev->sio_avail, @@ -343,8 +344,8 @@ static void stk_isoc_handler(struct urb *urb) for (i = 0; i < urb->number_of_packets; i++) { if (urb->iso_frame_desc[i].status != 0) { if (urb->iso_frame_desc[i].status != -EXDEV) - STK_ERROR("Frame %d has error %d\n", i, - urb->iso_frame_desc[i].status); + pr_err("Frame %d has error %d\n", + i, urb->iso_frame_desc[i].status); continue; } framelen = urb->iso_frame_desc[i].actual_length; @@ -368,9 +369,8 @@ static void stk_isoc_handler(struct urb *urb) /* This marks a new frame */ if (fb->v4lbuf.bytesused != 0 && fb->v4lbuf.bytesused != dev->frame_size) { - (void) (printk_ratelimit() && - STK_ERROR("frame %d, bytesused=%d, skipping\n", - i, fb->v4lbuf.bytesused)); + pr_err_ratelimited("frame %d, bytesused=%d, skipping\n", + i, fb->v4lbuf.bytesused); fb->v4lbuf.bytesused = 0; fill = fb->buffer; } else if (fb->v4lbuf.bytesused == dev->frame_size) { @@ -395,8 +395,7 @@ static void stk_isoc_handler(struct urb *urb) /* Our buffer is full !!! */ if (framelen + fb->v4lbuf.bytesused > dev->frame_size) { - (void) (printk_ratelimit() && - STK_ERROR("Frame buffer overflow, lost sync\n")); + pr_err_ratelimited("Frame buffer overflow, lost sync\n"); /*FIXME Do something here? */ continue; } @@ -414,8 +413,8 @@ resubmit: urb->dev = dev->udev; ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret != 0) { - STK_ERROR("Error (%d) re-submitting urb in stk_isoc_handler.\n", - ret); + pr_err("Error (%d) re-submitting urb in stk_isoc_handler\n", + ret); } } @@ -433,32 +432,31 @@ static int stk_prepare_iso(struct stk_camera *dev) udev = dev->udev; if (dev->isobufs) - STK_ERROR("isobufs already allocated. Bad\n"); + pr_err("isobufs already allocated. Bad\n"); else dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs), GFP_KERNEL); if (dev->isobufs == NULL) { - STK_ERROR("Unable to allocate iso buffers\n"); + pr_err("Unable to allocate iso buffers\n"); return -ENOMEM; } for (i = 0; i < MAX_ISO_BUFS; i++) { if (dev->isobufs[i].data == NULL) { kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); if (kbuf == NULL) { - STK_ERROR("Failed to allocate iso buffer %d\n", - i); + pr_err("Failed to allocate iso buffer %d\n", i); goto isobufs_out; } dev->isobufs[i].data = kbuf; } else - STK_ERROR("isobuf data already allocated\n"); + pr_err("isobuf data already allocated\n"); if (dev->isobufs[i].urb == NULL) { urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) goto isobufs_out; dev->isobufs[i].urb = urb; } else { - STK_ERROR("Killing URB\n"); + pr_err("Killing URB\n"); usb_kill_urb(dev->isobufs[i].urb); urb = dev->isobufs[i].urb; } @@ -567,7 +565,7 @@ static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) { int i; if (dev->sio_bufs != NULL) - STK_ERROR("sio_bufs already allocated\n"); + pr_err("sio_bufs already allocated\n"); else { dev->sio_bufs = kzalloc(n_sbufs * sizeof(struct stk_sio_buffer), GFP_KERNEL); @@ -690,7 +688,7 @@ static ssize_t stk_read(struct file *fp, char __user *buf, spin_lock_irqsave(&dev->spinlock, flags); if (list_empty(&dev->sio_full)) { spin_unlock_irqrestore(&dev->spinlock, flags); - STK_ERROR("BUG: No siobufs ready\n"); + pr_err("BUG: No siobufs ready\n"); return 0; } sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); @@ -907,7 +905,7 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp, stk_sizes[i].m != dev->vsettings.mode; i++) ; if (i == ARRAY_SIZE(stk_sizes)) { - STK_ERROR("ERROR: mode invalid\n"); + pr_err("ERROR: mode invalid\n"); return -EINVAL; } pix_format->width = stk_sizes[i].w; @@ -985,7 +983,7 @@ static int stk_setup_format(struct stk_camera *dev) stk_sizes[i].m != dev->vsettings.mode) i++; if (i == ARRAY_SIZE(stk_sizes)) { - STK_ERROR("Something is broken in %s\n", __func__); + pr_err("Something is broken in %s\n", __func__); return -EFAULT; } /* This registers controls some timings, not sure of what. */ @@ -1241,7 +1239,7 @@ static void stk_v4l_dev_release(struct video_device *vd) struct stk_camera *dev = vdev_to_camera(vd); if (dev->sio_bufs != NULL || dev->isobufs != NULL) - STK_ERROR("We are leaking memory\n"); + pr_err("We are leaking memory\n"); usb_put_intf(dev->interface); kfree(dev); } @@ -1264,10 +1262,10 @@ static int stk_register_video_device(struct stk_camera *dev) video_set_drvdata(&dev->vdev, dev); err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); if (err) - STK_ERROR("v4l registration failed\n"); + pr_err("v4l registration failed\n"); else - STK_INFO("Syntek USB2.0 Camera is now controlling device %s\n", - video_device_node_name(&dev->vdev)); + pr_info("Syntek USB2.0 Camera is now controlling device %s\n", + video_device_node_name(&dev->vdev)); return err; } @@ -1288,7 +1286,7 @@ static int stk_camera_probe(struct usb_interface *interface, dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL); if (dev == NULL) { - STK_ERROR("Out of memory !\n"); + pr_err("Out of memory !\n"); return -ENOMEM; } err = v4l2_device_register(&interface->dev, &dev->v4l2_dev); @@ -1352,7 +1350,7 @@ static int stk_camera_probe(struct usb_interface *interface, } } if (!dev->isoc_ep) { - STK_ERROR("Could not find isoc-in endpoint"); + pr_err("Could not find isoc-in endpoint\n"); err = -ENODEV; goto error; } @@ -1387,8 +1385,8 @@ static void stk_camera_disconnect(struct usb_interface *interface) wake_up_interruptible(&dev->wait_frame); - STK_INFO("Syntek USB2.0 Camera release resources device %s\n", - video_device_node_name(&dev->vdev)); + pr_info("Syntek USB2.0 Camera release resources device %s\n", + video_device_node_name(&dev->vdev)); video_unregister_device(&dev->vdev); v4l2_ctrl_handler_free(&dev->hdl); diff --git a/drivers/media/usb/stkwebcam/stk-webcam.h b/drivers/media/usb/stkwebcam/stk-webcam.h index 0284120ce246..5cecbdc97573 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.h +++ b/drivers/media/usb/stkwebcam/stk-webcam.h @@ -31,12 +31,6 @@ #define ISO_MAX_FRAME_SIZE 3 * 1024 #define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) - -#define PREFIX "stkwebcam: " -#define STK_INFO(str, args...) printk(KERN_INFO PREFIX str, ##args) -#define STK_ERROR(str, args...) printk(KERN_ERR PREFIX str, ##args) -#define STK_WARNING(str, args...) printk(KERN_WARNING PREFIX str, ##args) - struct stk_iso_buf { void *data; int length; diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c index e48b7c032c95..8db45dfc271b 100644 --- a/drivers/media/v4l2-core/tuner-core.c +++ b/drivers/media/v4l2-core/tuner-core.c @@ -43,8 +43,6 @@ #define UNSET (-1U) -#define PREFIX (t->i2c->dev.driver->name) - /* * Driver modprobe parameters */ diff --git a/drivers/memory/atmel-ebi.c b/drivers/memory/atmel-ebi.c index 99e644cda4d1..ebf69ff48ae2 100644 --- a/drivers/memory/atmel-ebi.c +++ b/drivers/memory/atmel-ebi.c @@ -72,7 +72,7 @@ struct atmel_smc_timing_xlate { { .name = nm, .converter = atmel_smc_cs_conf_set_pulse, .shift = pos} #define ATMEL_SMC_CYCLE_XLATE(nm, pos) \ - { .name = nm, .converter = atmel_smc_cs_conf_set_setup, .shift = pos} + { .name = nm, .converter = atmel_smc_cs_conf_set_cycle, .shift = pos} static void at91sam9_ebi_get_config(struct atmel_ebi_dev *ebid, struct atmel_ebi_dev_config *conf) @@ -120,12 +120,14 @@ static int atmel_ebi_xslate_smc_timings(struct atmel_ebi_dev *ebid, if (!ret) { required = true; ncycles = DIV_ROUND_UP(val, clk_period_ns); - if (ncycles > ATMEL_SMC_MODE_TDF_MAX || - ncycles < ATMEL_SMC_MODE_TDF_MIN) { + if (ncycles > ATMEL_SMC_MODE_TDF_MAX) { ret = -EINVAL; goto out; } + if (ncycles < ATMEL_SMC_MODE_TDF_MIN) + ncycles = ATMEL_SMC_MODE_TDF_MIN; + smcconf->mode |= ATMEL_SMC_MODE_TDF(ncycles); } @@ -263,7 +265,7 @@ static int atmel_ebi_xslate_smc_config(struct atmel_ebi_dev *ebid, } ret = atmel_ebi_xslate_smc_timings(ebid, np, &conf->smcconf); - if (ret) + if (ret < 0) return -EINVAL; if ((ret > 0 && !required) || (!ret && required)) { diff --git a/drivers/mfd/atmel-smc.c b/drivers/mfd/atmel-smc.c index 954cf0f66a31..20cc0ea470fa 100644 --- a/drivers/mfd/atmel-smc.c +++ b/drivers/mfd/atmel-smc.c @@ -206,7 +206,7 @@ EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_pulse); * parameter * * This function encodes the @ncycles value as described in the datasheet - * (section "SMC Pulse Register"), and then stores the result in the + * (section "SMC Cycle Register"), and then stores the result in the * @conf->setup field at @shift position. * * Returns -EINVAL if @shift is invalid, -ERANGE if @ncycles does not fit in diff --git a/drivers/mfd/da9062-core.c b/drivers/mfd/da9062-core.c index fbe0f245ce8e..fe1811523e4a 100644 --- a/drivers/mfd/da9062-core.c +++ b/drivers/mfd/da9062-core.c @@ -645,6 +645,9 @@ static const struct regmap_range da9062_aa_readable_ranges[] = { .range_min = DA9062AA_VLDO1_B, .range_max = DA9062AA_VLDO4_B, }, { + .range_min = DA9062AA_BBAT_CONT, + .range_max = DA9062AA_BBAT_CONT, + }, { .range_min = DA9062AA_INTERFACE, .range_max = DA9062AA_CONFIG_E, }, { @@ -721,6 +724,9 @@ static const struct regmap_range da9062_aa_writeable_ranges[] = { .range_min = DA9062AA_VLDO1_B, .range_max = DA9062AA_VLDO4_B, }, { + .range_min = DA9062AA_BBAT_CONT, + .range_max = DA9062AA_BBAT_CONT, + }, { .range_min = DA9062AA_GP_ID_0, .range_max = DA9062AA_GP_ID_19, }, diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b0b766416306..d84819dc2468 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -60,6 +60,7 @@ lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o lkdtm-$(CONFIG_LKDTM) += lkdtm_bugs.o lkdtm-$(CONFIG_LKDTM) += lkdtm_heap.o lkdtm-$(CONFIG_LKDTM) += lkdtm_perms.o +lkdtm-$(CONFIG_LKDTM) += lkdtm_refcount.o lkdtm-$(CONFIG_LKDTM) += lkdtm_rodata_objcopy.o lkdtm-$(CONFIG_LKDTM) += lkdtm_usercopy.o diff --git a/drivers/misc/apds9802als.c b/drivers/misc/apds9802als.c index c6cc3dc8ae1f..d8ac036f01ab 100644 --- a/drivers/misc/apds9802als.c +++ b/drivers/misc/apds9802als.c @@ -197,7 +197,7 @@ static struct attribute *mid_att_als[] = { NULL }; -static struct attribute_group m_als_gr = { +static const struct attribute_group m_als_gr = { .name = "apds9802als", .attrs = mid_att_als }; @@ -298,7 +298,7 @@ static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend, #define APDS9802ALS_PM_OPS NULL #endif /* CONFIG_PM */ -static struct i2c_device_id apds9802als_id[] = { +static const struct i2c_device_id apds9802als_id[] = { { DRIVER_NAME, 0 }, { } }; diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index 84e5b949399e..c9f07032c2fc 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -1051,7 +1051,7 @@ static struct attribute *sysfs_attrs_ctrl[] = { NULL }; -static struct attribute_group apds990x_attribute_group[] = { +static const struct attribute_group apds990x_attribute_group[] = { {.attrs = sysfs_attrs_ctrl }, }; diff --git a/drivers/misc/aspeed-lpc-snoop.c b/drivers/misc/aspeed-lpc-snoop.c index 593905565b74..cb78c98bc78d 100644 --- a/drivers/misc/aspeed-lpc-snoop.c +++ b/drivers/misc/aspeed-lpc-snoop.c @@ -20,6 +20,7 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/regmap.h> @@ -51,6 +52,13 @@ #define HICRB_ENSNP0D BIT(14) #define HICRB_ENSNP1D BIT(15) +struct aspeed_lpc_snoop_model_data { + /* The ast2400 has bits 14 and 15 as reserved, whereas the ast2500 + * can use them. + */ + unsigned int has_hicrb_ensnp; +}; + struct aspeed_lpc_snoop { struct regmap *regmap; int irq; @@ -123,10 +131,13 @@ static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop, } static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop, - int channel, u16 lpc_port) + struct device *dev, + int channel, u16 lpc_port) { int rc = 0; u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en; + const struct aspeed_lpc_snoop_model_data *model_data = + of_device_get_match_data(dev); /* Create FIFO datastructure */ rc = kfifo_alloc(&lpc_snoop->snoop_fifo[channel], @@ -155,7 +166,9 @@ static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop, regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en); regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask, lpc_port << snpwadr_shift); - regmap_update_bits(lpc_snoop->regmap, HICRB, hicrb_en, hicrb_en); + if (model_data->has_hicrb_ensnp) + regmap_update_bits(lpc_snoop->regmap, HICRB, + hicrb_en, hicrb_en); return rc; } @@ -213,14 +226,14 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev) if (rc) return rc; - rc = aspeed_lpc_enable_snoop(lpc_snoop, 0, port); + rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port); if (rc) return rc; /* Configuration of 2nd snoop channel port is optional */ if (of_property_read_u32_index(dev->of_node, "snoop-ports", 1, &port) == 0) { - rc = aspeed_lpc_enable_snoop(lpc_snoop, 1, port); + rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port); if (rc) aspeed_lpc_disable_snoop(lpc_snoop, 0); } @@ -239,8 +252,19 @@ static int aspeed_lpc_snoop_remove(struct platform_device *pdev) return 0; } +static const struct aspeed_lpc_snoop_model_data ast2400_model_data = { + .has_hicrb_ensnp = 0, +}; + +static const struct aspeed_lpc_snoop_model_data ast2500_model_data = { + .has_hicrb_ensnp = 1, +}; + static const struct of_device_id aspeed_lpc_snoop_match[] = { - { .compatible = "aspeed,ast2500-lpc-snoop" }, + { .compatible = "aspeed,ast2400-lpc-snoop", + .data = &ast2400_model_data }, + { .compatible = "aspeed,ast2500-lpc-snoop", + .data = &ast2500_model_data }, { }, }; diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index 38fcfe219d1c..9c62bf064f77 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -1175,7 +1175,7 @@ static struct attribute *sysfs_attrs[] = { NULL }; -static struct attribute_group bh1770_attribute_group = { +static const struct attribute_group bh1770_attribute_group = { .attrs = sysfs_attrs }; diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index 28bb495f0cf1..7231260ac287 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -173,7 +173,7 @@ static ssize_t ds1682_eeprom_write(struct file *filp, struct kobject *kobj, return count; } -static struct bin_attribute ds1682_eeprom_attr = { +static const struct bin_attribute ds1682_eeprom_attr = { .attr = { .name = "eeprom", .mode = S_IRUGO | S_IWUSR, diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index 2fad790db3bf..60e3d91b5e03 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -114,7 +114,7 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, return count; } -static struct bin_attribute eeprom_attr = { +static const struct bin_attribute eeprom_attr = { .attr = { .name = "eeprom", .mode = S_IRUGO, diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index 94cc035aa841..38766968bfa2 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -377,8 +377,6 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) struct device_node *np = spi->dev.of_node; struct eeprom_93xx46_platform_data *pd; u32 tmp; - int gpio; - enum of_gpio_flags of_flags; int ret; pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL); @@ -403,22 +401,14 @@ static int eeprom_93xx46_probe_dt(struct spi_device *spi) if (of_property_read_bool(np, "read-only")) pd->flags |= EE_READONLY; - gpio = of_get_named_gpio_flags(np, "select-gpios", 0, &of_flags); - if (gpio_is_valid(gpio)) { - unsigned long flags = - of_flags == OF_GPIO_ACTIVE_LOW ? GPIOF_ACTIVE_LOW : 0; + pd->select = devm_gpiod_get_optional(&spi->dev, "select", + GPIOD_OUT_LOW); + if (IS_ERR(pd->select)) + return PTR_ERR(pd->select); - ret = devm_gpio_request_one(&spi->dev, gpio, flags, - "eeprom_93xx46_select"); - if (ret) - return ret; - - pd->select = gpio_to_desc(gpio); - pd->prepare = select_assert; - pd->finish = select_deassert; - - gpiod_direction_output(pd->select, 0); - } + pd->prepare = select_assert; + pd->finish = select_deassert; + gpiod_direction_output(pd->select, 0); if (of_id->data) { const struct eeprom_93xx46_devtype_data *data = of_id->data; diff --git a/drivers/misc/eeprom/idt_89hpesx.c b/drivers/misc/eeprom/idt_89hpesx.c index ab0df6a17690..37312b548a03 100644 --- a/drivers/misc/eeprom/idt_89hpesx.c +++ b/drivers/misc/eeprom/idt_89hpesx.c @@ -1132,8 +1132,8 @@ static void idt_get_ofdata(struct idt_89hpesx_dev *pdev) for_each_available_child_of_node(node, child) { ee_id = idt_ee_match_id(child); if (IS_ERR_OR_NULL(ee_id)) { - dev_warn(dev, "Skip unsupported child node %s", - child->full_name); + dev_warn(dev, "Skip unsupported child node %pOF", + child); continue; } else break; @@ -1151,8 +1151,8 @@ static void idt_get_ofdata(struct idt_89hpesx_dev *pdev) /* Get custom EEPROM address from 'reg' attribute */ addr_be = of_get_property(child, "reg", &len); if (!addr_be || (len < sizeof(*addr_be))) { - dev_warn(dev, "No reg on %s, use default address %d", - child->full_name, EEPROM_DEF_ADDR); + dev_warn(dev, "No reg on %pOF, use default address %d", + child, EEPROM_DEF_ADDR); pdev->inieecmd = 0; pdev->eeaddr = EEPROM_DEF_ADDR << 1; } else { diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index e4dd93b2518c..0e32709d1022 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -124,7 +124,7 @@ static ssize_t max6875_read(struct file *filp, struct kobject *kobj, return count; } -static struct bin_attribute user_eeprom_attr = { +static const struct bin_attribute user_eeprom_attr = { .attr = { .name = "eeprom", .mode = S_IRUGO, diff --git a/drivers/misc/hmc6352.c b/drivers/misc/hmc6352.c index 90520d76633f..eeb7eef62174 100644 --- a/drivers/misc/hmc6352.c +++ b/drivers/misc/hmc6352.c @@ -132,7 +132,7 @@ static int hmc6352_remove(struct i2c_client *client) return 0; } -static struct i2c_device_id hmc6352_id[] = { +static const struct i2c_device_id hmc6352_id[] = { { "hmc6352", 0 }, { } }; diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index fea8ff40440f..097e3092c158 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -857,7 +857,7 @@ out: return error; } -static struct pci_device_id ilo_devices[] = { +static const struct pci_device_id ilo_devices[] = { { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB204) }, { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3307) }, { } diff --git a/drivers/misc/ioc4.c b/drivers/misc/ioc4.c index 8758d033db23..ec0832278170 100644 --- a/drivers/misc/ioc4.c +++ b/drivers/misc/ioc4.c @@ -454,7 +454,7 @@ ioc4_remove(struct pci_dev *pdev) kfree(idd); } -static struct pci_device_id ioc4_id_table[] = { +static const struct pci_device_id ioc4_id_table[] = { {PCI_VENDOR_ID_SGI, PCI_DEVICE_ID_SGI_IOC4, PCI_ANY_ID, PCI_ANY_ID, 0x0b4000, 0xFFFFFF}, {0} diff --git a/drivers/misc/isl29020.c b/drivers/misc/isl29020.c index 4a9c50a43afb..e3bd3c1d2574 100644 --- a/drivers/misc/isl29020.c +++ b/drivers/misc/isl29020.c @@ -145,7 +145,7 @@ static struct attribute *mid_att_als[] = { NULL }; -static struct attribute_group m_als_gr = { +static const struct attribute_group m_als_gr = { .name = "isl29020", .attrs = mid_att_als }; @@ -188,7 +188,7 @@ static int isl29020_remove(struct i2c_client *client) return 0; } -static struct i2c_device_id isl29020_id[] = { +static const struct i2c_device_id isl29020_id[] = { { "isl29020", 0 }, { } }; diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c index e389b0b5278d..8d53609861d8 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d.c +++ b/drivers/misc/lis3lv02d/lis3lv02d.c @@ -856,7 +856,7 @@ static struct attribute *lis3lv02d_attributes[] = { NULL }; -static struct attribute_group lis3lv02d_attribute_group = { +static const struct attribute_group lis3lv02d_attribute_group = { .attrs = lis3lv02d_attributes }; diff --git a/drivers/misc/lkdtm.h b/drivers/misc/lkdtm.h index 3b4976396ec4..bfb6c45b6130 100644 --- a/drivers/misc/lkdtm.h +++ b/drivers/misc/lkdtm.h @@ -14,20 +14,17 @@ void lkdtm_EXCEPTION(void); void lkdtm_LOOP(void); void lkdtm_OVERFLOW(void); void lkdtm_CORRUPT_STACK(void); +void lkdtm_CORRUPT_STACK_STRONG(void); void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void); void lkdtm_SOFTLOCKUP(void); void lkdtm_HARDLOCKUP(void); void lkdtm_SPINLOCKUP(void); void lkdtm_HUNG_TASK(void); -void lkdtm_REFCOUNT_SATURATE_INC(void); -void lkdtm_REFCOUNT_SATURATE_ADD(void); -void lkdtm_REFCOUNT_ZERO_DEC(void); -void lkdtm_REFCOUNT_ZERO_INC(void); -void lkdtm_REFCOUNT_ZERO_SUB(void); -void lkdtm_REFCOUNT_ZERO_ADD(void); void lkdtm_CORRUPT_LIST_ADD(void); void lkdtm_CORRUPT_LIST_DEL(void); void lkdtm_CORRUPT_USER_DS(void); +void lkdtm_STACK_GUARD_PAGE_LEADING(void); +void lkdtm_STACK_GUARD_PAGE_TRAILING(void); /* lkdtm_heap.c */ void lkdtm_OVERWRITE_ALLOCATION(void); @@ -49,6 +46,27 @@ void lkdtm_EXEC_RODATA(void); void lkdtm_EXEC_USERSPACE(void); void lkdtm_ACCESS_USERSPACE(void); +/* lkdtm_refcount.c */ +void lkdtm_REFCOUNT_INC_OVERFLOW(void); +void lkdtm_REFCOUNT_ADD_OVERFLOW(void); +void lkdtm_REFCOUNT_INC_NOT_ZERO_OVERFLOW(void); +void lkdtm_REFCOUNT_ADD_NOT_ZERO_OVERFLOW(void); +void lkdtm_REFCOUNT_DEC_ZERO(void); +void lkdtm_REFCOUNT_DEC_NEGATIVE(void); +void lkdtm_REFCOUNT_DEC_AND_TEST_NEGATIVE(void); +void lkdtm_REFCOUNT_SUB_AND_TEST_NEGATIVE(void); +void lkdtm_REFCOUNT_INC_ZERO(void); +void lkdtm_REFCOUNT_ADD_ZERO(void); +void lkdtm_REFCOUNT_INC_SATURATED(void); +void lkdtm_REFCOUNT_DEC_SATURATED(void); +void lkdtm_REFCOUNT_ADD_SATURATED(void); +void lkdtm_REFCOUNT_INC_NOT_ZERO_SATURATED(void); +void lkdtm_REFCOUNT_ADD_NOT_ZERO_SATURATED(void); +void lkdtm_REFCOUNT_DEC_AND_TEST_SATURATED(void); +void lkdtm_REFCOUNT_SUB_AND_TEST_SATURATED(void); +void lkdtm_REFCOUNT_TIMING(void); +void lkdtm_ATOMIC_TIMING(void); + /* lkdtm_rodata.c */ void lkdtm_rodata_do_nothing(void); diff --git a/drivers/misc/lkdtm_bugs.c b/drivers/misc/lkdtm_bugs.c index d9028ef50fbe..9e0b4f959987 100644 --- a/drivers/misc/lkdtm_bugs.c +++ b/drivers/misc/lkdtm_bugs.c @@ -6,9 +6,9 @@ */ #include "lkdtm.h" #include <linux/list.h> -#include <linux/refcount.h> #include <linux/sched.h> #include <linux/sched/signal.h> +#include <linux/sched/task_stack.h> #include <linux/uaccess.h> struct lkdtm_list { @@ -85,16 +85,31 @@ void lkdtm_OVERFLOW(void) static noinline void __lkdtm_CORRUPT_STACK(void *stack) { - memset(stack, 'a', 64); + memset(stack, '\xff', 64); } +/* This should trip the stack canary, not corrupt the return address. */ noinline void lkdtm_CORRUPT_STACK(void) { /* Use default char array length that triggers stack protection. */ - char data[8]; + char data[8] __aligned(sizeof(void *)); + + __lkdtm_CORRUPT_STACK(&data); + + pr_info("Corrupted stack containing char array ...\n"); +} + +/* Same as above but will only get a canary with -fstack-protector-strong */ +noinline void lkdtm_CORRUPT_STACK_STRONG(void) +{ + union { + unsigned short shorts[4]; + unsigned long *ptr; + } data __aligned(sizeof(void *)); + __lkdtm_CORRUPT_STACK(&data); - pr_info("Corrupted stack with '%16s'...\n", data); + pr_info("Corrupted stack containing union ...\n"); } void lkdtm_UNALIGNED_LOAD_STORE_WRITE(void) @@ -137,88 +152,6 @@ void lkdtm_HUNG_TASK(void) schedule(); } -void lkdtm_REFCOUNT_SATURATE_INC(void) -{ - refcount_t over = REFCOUNT_INIT(UINT_MAX - 1); - - pr_info("attempting good refcount decrement\n"); - refcount_dec(&over); - refcount_inc(&over); - - pr_info("attempting bad refcount inc overflow\n"); - refcount_inc(&over); - refcount_inc(&over); - if (refcount_read(&over) == UINT_MAX) - pr_err("Correctly stayed saturated, but no BUG?!\n"); - else - pr_err("Fail: refcount wrapped\n"); -} - -void lkdtm_REFCOUNT_SATURATE_ADD(void) -{ - refcount_t over = REFCOUNT_INIT(UINT_MAX - 1); - - pr_info("attempting good refcount decrement\n"); - refcount_dec(&over); - refcount_inc(&over); - - pr_info("attempting bad refcount add overflow\n"); - refcount_add(2, &over); - if (refcount_read(&over) == UINT_MAX) - pr_err("Correctly stayed saturated, but no BUG?!\n"); - else - pr_err("Fail: refcount wrapped\n"); -} - -void lkdtm_REFCOUNT_ZERO_DEC(void) -{ - refcount_t zero = REFCOUNT_INIT(1); - - pr_info("attempting bad refcount decrement to zero\n"); - refcount_dec(&zero); - if (refcount_read(&zero) == 0) - pr_err("Stayed at zero, but no BUG?!\n"); - else - pr_err("Fail: refcount went crazy\n"); -} - -void lkdtm_REFCOUNT_ZERO_SUB(void) -{ - refcount_t zero = REFCOUNT_INIT(1); - - pr_info("attempting bad refcount subtract past zero\n"); - if (!refcount_sub_and_test(2, &zero)) - pr_info("wrap attempt was noticed\n"); - if (refcount_read(&zero) == 1) - pr_err("Correctly stayed above 0, but no BUG?!\n"); - else - pr_err("Fail: refcount wrapped\n"); -} - -void lkdtm_REFCOUNT_ZERO_INC(void) -{ - refcount_t zero = REFCOUNT_INIT(0); - - pr_info("attempting bad refcount increment from zero\n"); - refcount_inc(&zero); - if (refcount_read(&zero) == 0) - pr_err("Stayed at zero, but no BUG?!\n"); - else - pr_err("Fail: refcount went past zero\n"); -} - -void lkdtm_REFCOUNT_ZERO_ADD(void) -{ - refcount_t zero = REFCOUNT_INIT(0); - - pr_info("attempting bad refcount addition from zero\n"); - refcount_add(2, &zero); - if (refcount_read(&zero) == 0) - pr_err("Stayed at zero, but no BUG?!\n"); - else - pr_err("Fail: refcount went past zero\n"); -} - void lkdtm_CORRUPT_LIST_ADD(void) { /* @@ -282,6 +215,7 @@ void lkdtm_CORRUPT_LIST_DEL(void) pr_err("list_del() corruption not detected!\n"); } +/* Test if unbalanced set_fs(KERNEL_DS)/set_fs(USER_DS) check exists. */ void lkdtm_CORRUPT_USER_DS(void) { pr_info("setting bad task size limit\n"); @@ -290,3 +224,31 @@ void lkdtm_CORRUPT_USER_DS(void) /* Make sure we do not keep running with a KERNEL_DS! */ force_sig(SIGKILL, current); } + +/* Test that VMAP_STACK is actually allocating with a leading guard page */ +void lkdtm_STACK_GUARD_PAGE_LEADING(void) +{ + const unsigned char *stack = task_stack_page(current); + const unsigned char *ptr = stack - 1; + volatile unsigned char byte; + + pr_info("attempting bad read from page below current stack\n"); + + byte = *ptr; + + pr_err("FAIL: accessed page before stack!\n"); +} + +/* Test that VMAP_STACK is actually allocating with a trailing guard page */ +void lkdtm_STACK_GUARD_PAGE_TRAILING(void) +{ + const unsigned char *stack = task_stack_page(current); + const unsigned char *ptr = stack + THREAD_SIZE; + volatile unsigned char byte; + + pr_info("attempting bad read from page above current stack\n"); + + byte = *ptr; + + pr_err("FAIL: accessed page after stack!\n"); +} diff --git a/drivers/misc/lkdtm_core.c b/drivers/misc/lkdtm_core.c index 42d2b8e31e6b..981b3ef71e47 100644 --- a/drivers/misc/lkdtm_core.c +++ b/drivers/misc/lkdtm_core.c @@ -201,6 +201,9 @@ struct crashtype crashtypes[] = { CRASHTYPE(CORRUPT_LIST_DEL), CRASHTYPE(CORRUPT_USER_DS), CRASHTYPE(CORRUPT_STACK), + CRASHTYPE(CORRUPT_STACK_STRONG), + CRASHTYPE(STACK_GUARD_PAGE_LEADING), + CRASHTYPE(STACK_GUARD_PAGE_TRAILING), CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), CRASHTYPE(OVERWRITE_ALLOCATION), CRASHTYPE(WRITE_AFTER_FREE), @@ -221,12 +224,25 @@ struct crashtype crashtypes[] = { CRASHTYPE(WRITE_RO), CRASHTYPE(WRITE_RO_AFTER_INIT), CRASHTYPE(WRITE_KERN), - CRASHTYPE(REFCOUNT_SATURATE_INC), - CRASHTYPE(REFCOUNT_SATURATE_ADD), - CRASHTYPE(REFCOUNT_ZERO_DEC), - CRASHTYPE(REFCOUNT_ZERO_INC), - CRASHTYPE(REFCOUNT_ZERO_SUB), - CRASHTYPE(REFCOUNT_ZERO_ADD), + CRASHTYPE(REFCOUNT_INC_OVERFLOW), + CRASHTYPE(REFCOUNT_ADD_OVERFLOW), + CRASHTYPE(REFCOUNT_INC_NOT_ZERO_OVERFLOW), + CRASHTYPE(REFCOUNT_ADD_NOT_ZERO_OVERFLOW), + CRASHTYPE(REFCOUNT_DEC_ZERO), + CRASHTYPE(REFCOUNT_DEC_NEGATIVE), + CRASHTYPE(REFCOUNT_DEC_AND_TEST_NEGATIVE), + CRASHTYPE(REFCOUNT_SUB_AND_TEST_NEGATIVE), + CRASHTYPE(REFCOUNT_INC_ZERO), + CRASHTYPE(REFCOUNT_ADD_ZERO), + CRASHTYPE(REFCOUNT_INC_SATURATED), + CRASHTYPE(REFCOUNT_DEC_SATURATED), + CRASHTYPE(REFCOUNT_ADD_SATURATED), + CRASHTYPE(REFCOUNT_INC_NOT_ZERO_SATURATED), + CRASHTYPE(REFCOUNT_ADD_NOT_ZERO_SATURATED), + CRASHTYPE(REFCOUNT_DEC_AND_TEST_SATURATED), + CRASHTYPE(REFCOUNT_SUB_AND_TEST_SATURATED), + CRASHTYPE(REFCOUNT_TIMING), + CRASHTYPE(ATOMIC_TIMING), CRASHTYPE(USERCOPY_HEAP_SIZE_TO), CRASHTYPE(USERCOPY_HEAP_SIZE_FROM), CRASHTYPE(USERCOPY_HEAP_FLAG_TO), diff --git a/drivers/misc/lkdtm_refcount.c b/drivers/misc/lkdtm_refcount.c new file mode 100644 index 000000000000..29af0152a337 --- /dev/null +++ b/drivers/misc/lkdtm_refcount.c @@ -0,0 +1,400 @@ +/* + * This is for all the tests related to refcount bugs (e.g. overflow, + * underflow, reaching zero untested, etc). + */ +#include "lkdtm.h" +#include <linux/refcount.h> + +#ifdef CONFIG_REFCOUNT_FULL +#define REFCOUNT_MAX (UINT_MAX - 1) +#define REFCOUNT_SATURATED UINT_MAX +#else +#define REFCOUNT_MAX INT_MAX +#define REFCOUNT_SATURATED (INT_MIN / 2) +#endif + +static void overflow_check(refcount_t *ref) +{ + switch (refcount_read(ref)) { + case REFCOUNT_SATURATED: + pr_info("Overflow detected: saturated\n"); + break; + case REFCOUNT_MAX: + pr_warn("Overflow detected: unsafely reset to max\n"); + break; + default: + pr_err("Fail: refcount wrapped to %d\n", refcount_read(ref)); + } +} + +/* + * A refcount_inc() above the maximum value of the refcount implementation, + * should at least saturate, and at most also WARN. + */ +void lkdtm_REFCOUNT_INC_OVERFLOW(void) +{ + refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1); + + pr_info("attempting good refcount_inc() without overflow\n"); + refcount_dec(&over); + refcount_inc(&over); + + pr_info("attempting bad refcount_inc() overflow\n"); + refcount_inc(&over); + refcount_inc(&over); + + overflow_check(&over); +} + +/* refcount_add() should behave just like refcount_inc() above. */ +void lkdtm_REFCOUNT_ADD_OVERFLOW(void) +{ + refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX - 1); + + pr_info("attempting good refcount_add() without overflow\n"); + refcount_dec(&over); + refcount_dec(&over); + refcount_dec(&over); + refcount_dec(&over); + refcount_add(4, &over); + + pr_info("attempting bad refcount_add() overflow\n"); + refcount_add(4, &over); + + overflow_check(&over); +} + +/* refcount_inc_not_zero() should behave just like refcount_inc() above. */ +void lkdtm_REFCOUNT_INC_NOT_ZERO_OVERFLOW(void) +{ + refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX); + + pr_info("attempting bad refcount_inc_not_zero() overflow\n"); + if (!refcount_inc_not_zero(&over)) + pr_warn("Weird: refcount_inc_not_zero() reported zero\n"); + + overflow_check(&over); +} + +/* refcount_add_not_zero() should behave just like refcount_inc() above. */ +void lkdtm_REFCOUNT_ADD_NOT_ZERO_OVERFLOW(void) +{ + refcount_t over = REFCOUNT_INIT(REFCOUNT_MAX); + + pr_info("attempting bad refcount_add_not_zero() overflow\n"); + if (!refcount_add_not_zero(6, &over)) + pr_warn("Weird: refcount_add_not_zero() reported zero\n"); + + overflow_check(&over); +} + +static void check_zero(refcount_t *ref) +{ + switch (refcount_read(ref)) { + case REFCOUNT_SATURATED: + pr_info("Zero detected: saturated\n"); + break; + case REFCOUNT_MAX: + pr_warn("Zero detected: unsafely reset to max\n"); + break; + case 0: + pr_warn("Still at zero: refcount_inc/add() must not inc-from-0\n"); + break; + default: + pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref)); + } +} + +/* + * A refcount_dec(), as opposed to a refcount_dec_and_test(), when it hits + * zero it should either saturate (when inc-from-zero isn't protected) + * or stay at zero (when inc-from-zero is protected) and should WARN for both. + */ +void lkdtm_REFCOUNT_DEC_ZERO(void) +{ + refcount_t zero = REFCOUNT_INIT(2); + + pr_info("attempting good refcount_dec()\n"); + refcount_dec(&zero); + + pr_info("attempting bad refcount_dec() to zero\n"); + refcount_dec(&zero); + + check_zero(&zero); +} + +static void check_negative(refcount_t *ref, int start) +{ + /* + * CONFIG_REFCOUNT_FULL refuses to move a refcount at all on an + * over-sub, so we have to track our starting position instead of + * looking only at zero-pinning. + */ + if (refcount_read(ref) == start) { + pr_warn("Still at %d: refcount_inc/add() must not inc-from-0\n", + start); + return; + } + + switch (refcount_read(ref)) { + case REFCOUNT_SATURATED: + pr_info("Negative detected: saturated\n"); + break; + case REFCOUNT_MAX: + pr_warn("Negative detected: unsafely reset to max\n"); + break; + default: + pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref)); + } +} + +/* A refcount_dec() going negative should saturate and may WARN. */ +void lkdtm_REFCOUNT_DEC_NEGATIVE(void) +{ + refcount_t neg = REFCOUNT_INIT(0); + + pr_info("attempting bad refcount_dec() below zero\n"); + refcount_dec(&neg); + + check_negative(&neg, 0); +} + +/* + * A refcount_dec_and_test() should act like refcount_dec() above when + * going negative. + */ +void lkdtm_REFCOUNT_DEC_AND_TEST_NEGATIVE(void) +{ + refcount_t neg = REFCOUNT_INIT(0); + + pr_info("attempting bad refcount_dec_and_test() below zero\n"); + if (refcount_dec_and_test(&neg)) + pr_warn("Weird: refcount_dec_and_test() reported zero\n"); + + check_negative(&neg, 0); +} + +/* + * A refcount_sub_and_test() should act like refcount_dec_and_test() + * above when going negative. + */ +void lkdtm_REFCOUNT_SUB_AND_TEST_NEGATIVE(void) +{ + refcount_t neg = REFCOUNT_INIT(3); + + pr_info("attempting bad refcount_sub_and_test() below zero\n"); + if (refcount_sub_and_test(5, &neg)) + pr_warn("Weird: refcount_sub_and_test() reported zero\n"); + + check_negative(&neg, 3); +} + +static void check_from_zero(refcount_t *ref) +{ + switch (refcount_read(ref)) { + case 0: + pr_info("Zero detected: stayed at zero\n"); + break; + case REFCOUNT_SATURATED: + pr_info("Zero detected: saturated\n"); + break; + case REFCOUNT_MAX: + pr_warn("Zero detected: unsafely reset to max\n"); + break; + default: + pr_info("Fail: zero not detected, incremeted to %d\n", + refcount_read(ref)); + } +} + +/* + * A refcount_inc() from zero should pin to zero or saturate and may WARN. + * Only CONFIG_REFCOUNT_FULL provides this protection currently. + */ +void lkdtm_REFCOUNT_INC_ZERO(void) +{ + refcount_t zero = REFCOUNT_INIT(0); + + pr_info("attempting safe refcount_inc_not_zero() from zero\n"); + if (!refcount_inc_not_zero(&zero)) { + pr_info("Good: zero detected\n"); + if (refcount_read(&zero) == 0) + pr_info("Correctly stayed at zero\n"); + else + pr_err("Fail: refcount went past zero!\n"); + } else { + pr_err("Fail: Zero not detected!?\n"); + } + + pr_info("attempting bad refcount_inc() from zero\n"); + refcount_inc(&zero); + + check_from_zero(&zero); +} + +/* + * A refcount_add() should act like refcount_inc() above when starting + * at zero. + */ +void lkdtm_REFCOUNT_ADD_ZERO(void) +{ + refcount_t zero = REFCOUNT_INIT(0); + + pr_info("attempting safe refcount_add_not_zero() from zero\n"); + if (!refcount_add_not_zero(3, &zero)) { + pr_info("Good: zero detected\n"); + if (refcount_read(&zero) == 0) + pr_info("Correctly stayed at zero\n"); + else + pr_err("Fail: refcount went past zero\n"); + } else { + pr_err("Fail: Zero not detected!?\n"); + } + + pr_info("attempting bad refcount_add() from zero\n"); + refcount_add(3, &zero); + + check_from_zero(&zero); +} + +static void check_saturated(refcount_t *ref) +{ + switch (refcount_read(ref)) { + case REFCOUNT_SATURATED: + pr_info("Saturation detected: still saturated\n"); + break; + case REFCOUNT_MAX: + pr_warn("Saturation detected: unsafely reset to max\n"); + break; + default: + pr_err("Fail: refcount went crazy: %d\n", refcount_read(ref)); + } +} + +/* + * A refcount_inc() from a saturated value should at most warn about + * being saturated already. + */ +void lkdtm_REFCOUNT_INC_SATURATED(void) +{ + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); + + pr_info("attempting bad refcount_inc() from saturated\n"); + refcount_inc(&sat); + + check_saturated(&sat); +} + +/* Should act like refcount_inc() above from saturated. */ +void lkdtm_REFCOUNT_DEC_SATURATED(void) +{ + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); + + pr_info("attempting bad refcount_dec() from saturated\n"); + refcount_dec(&sat); + + check_saturated(&sat); +} + +/* Should act like refcount_inc() above from saturated. */ +void lkdtm_REFCOUNT_ADD_SATURATED(void) +{ + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); + + pr_info("attempting bad refcount_dec() from saturated\n"); + refcount_add(8, &sat); + + check_saturated(&sat); +} + +/* Should act like refcount_inc() above from saturated. */ +void lkdtm_REFCOUNT_INC_NOT_ZERO_SATURATED(void) +{ + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); + + pr_info("attempting bad refcount_inc_not_zero() from saturated\n"); + if (!refcount_inc_not_zero(&sat)) + pr_warn("Weird: refcount_inc_not_zero() reported zero\n"); + + check_saturated(&sat); +} + +/* Should act like refcount_inc() above from saturated. */ +void lkdtm_REFCOUNT_ADD_NOT_ZERO_SATURATED(void) +{ + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); + + pr_info("attempting bad refcount_add_not_zero() from saturated\n"); + if (!refcount_add_not_zero(7, &sat)) + pr_warn("Weird: refcount_add_not_zero() reported zero\n"); + + check_saturated(&sat); +} + +/* Should act like refcount_inc() above from saturated. */ +void lkdtm_REFCOUNT_DEC_AND_TEST_SATURATED(void) +{ + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); + + pr_info("attempting bad refcount_dec_and_test() from saturated\n"); + if (refcount_dec_and_test(&sat)) + pr_warn("Weird: refcount_dec_and_test() reported zero\n"); + + check_saturated(&sat); +} + +/* Should act like refcount_inc() above from saturated. */ +void lkdtm_REFCOUNT_SUB_AND_TEST_SATURATED(void) +{ + refcount_t sat = REFCOUNT_INIT(REFCOUNT_SATURATED); + + pr_info("attempting bad refcount_sub_and_test() from saturated\n"); + if (refcount_sub_and_test(8, &sat)) + pr_warn("Weird: refcount_sub_and_test() reported zero\n"); + + check_saturated(&sat); +} + +/* Used to time the existing atomic_t when used for reference counting */ +void lkdtm_ATOMIC_TIMING(void) +{ + unsigned int i; + atomic_t count = ATOMIC_INIT(1); + + for (i = 0; i < INT_MAX - 1; i++) + atomic_inc(&count); + + for (i = INT_MAX; i > 0; i--) + if (atomic_dec_and_test(&count)) + break; + + if (i != 1) + pr_err("atomic timing: out of sync up/down cycle: %u\n", i - 1); + else + pr_info("atomic timing: done\n"); +} + +/* + * This can be compared to ATOMIC_TIMING when implementing fast refcount + * protections. Looking at the number of CPU cycles tells the real story + * about performance. For example: + * cd /sys/kernel/debug/provoke-crash + * perf stat -B -- cat <(echo REFCOUNT_TIMING) > DIRECT + */ +void lkdtm_REFCOUNT_TIMING(void) +{ + unsigned int i; + refcount_t count = REFCOUNT_INIT(1); + + for (i = 0; i < INT_MAX - 1; i++) + refcount_inc(&count); + + for (i = INT_MAX; i > 0; i--) + if (refcount_dec_and_test(&count)) + break; + + if (i != 1) + pr_err("refcount: out of sync up/down cycle: %u\n", i - 1); + else + pr_info("refcount timing: done\n"); +} diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 40c79089e548..1ac10cb64d6e 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -845,7 +845,7 @@ static void mei_cl_bus_dev_release(struct device *dev) kfree(cldev); } -static struct device_type mei_cl_device_type = { +static const struct device_type mei_cl_device_type = { .release = mei_cl_bus_dev_release, }; diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 71216affcab1..10dcf4ff99a5 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -1354,10 +1354,10 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev) .quirk_probe = mei_me_fw_type_sps -#define MEI_CFG_LEGACY_HFS \ +#define MEI_CFG_ICH_HFS \ .fw_status.count = 0 -#define MEI_CFG_ICH_HFS \ +#define MEI_CFG_ICH10_HFS \ .fw_status.count = 1, \ .fw_status.status[0] = PCI_CFG_HFS_1 @@ -1376,38 +1376,61 @@ static bool mei_me_fw_type_sps(struct pci_dev *pdev) .fw_status.status[5] = PCI_CFG_HFS_6 /* ICH Legacy devices */ -const struct mei_cfg mei_me_legacy_cfg = { - MEI_CFG_LEGACY_HFS, +static const struct mei_cfg mei_me_ich_cfg = { + MEI_CFG_ICH_HFS, }; /* ICH devices */ -const struct mei_cfg mei_me_ich_cfg = { - MEI_CFG_ICH_HFS, +static const struct mei_cfg mei_me_ich10_cfg = { + MEI_CFG_ICH10_HFS, }; /* PCH devices */ -const struct mei_cfg mei_me_pch_cfg = { +static const struct mei_cfg mei_me_pch_cfg = { MEI_CFG_PCH_HFS, }; - /* PCH Cougar Point and Patsburg with quirk for Node Manager exclusion */ -const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { +static const struct mei_cfg mei_me_pch_cpt_pbg_cfg = { MEI_CFG_PCH_HFS, MEI_CFG_FW_NM, }; /* PCH8 Lynx Point and newer devices */ -const struct mei_cfg mei_me_pch8_cfg = { +static const struct mei_cfg mei_me_pch8_cfg = { MEI_CFG_PCH8_HFS, }; /* PCH8 Lynx Point with quirk for SPS Firmware exclusion */ -const struct mei_cfg mei_me_pch8_sps_cfg = { +static const struct mei_cfg mei_me_pch8_sps_cfg = { MEI_CFG_PCH8_HFS, MEI_CFG_FW_SPS, }; +/* + * mei_cfg_list - A list of platform platform specific configurations. + * Note: has to be synchronized with enum mei_cfg_idx. + */ +static const struct mei_cfg *const mei_cfg_list[] = { + [MEI_ME_UNDEF_CFG] = NULL, + [MEI_ME_ICH_CFG] = &mei_me_ich_cfg, + [MEI_ME_ICH10_CFG] = &mei_me_ich10_cfg, + [MEI_ME_PCH_CFG] = &mei_me_pch_cfg, + [MEI_ME_PCH_CPT_PBG_CFG] = &mei_me_pch_cpt_pbg_cfg, + [MEI_ME_PCH8_CFG] = &mei_me_pch8_cfg, + [MEI_ME_PCH8_SPS_CFG] = &mei_me_pch8_sps_cfg, +}; + +const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx) +{ + BUILD_BUG_ON(ARRAY_SIZE(mei_cfg_list) != MEI_ME_NUM_CFG); + + if (idx >= MEI_ME_NUM_CFG) + return NULL; + + return mei_cfg_list[idx]; +}; + /** * mei_me_dev_init - allocates and initializes the mei device structure * diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index cf64847a35b9..67892533576e 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -41,8 +41,7 @@ struct mei_cfg { #define MEI_PCI_DEVICE(dev, cfg) \ .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, \ - .driver_data = (kernel_ulong_t)&(cfg) - + .driver_data = (kernel_ulong_t)(cfg), #define MEI_ME_RPM_TIMEOUT 500 /* ms */ @@ -63,12 +62,36 @@ struct mei_me_hw { #define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw) -extern const struct mei_cfg mei_me_legacy_cfg; -extern const struct mei_cfg mei_me_ich_cfg; -extern const struct mei_cfg mei_me_pch_cfg; -extern const struct mei_cfg mei_me_pch_cpt_pbg_cfg; -extern const struct mei_cfg mei_me_pch8_cfg; -extern const struct mei_cfg mei_me_pch8_sps_cfg; +/** + * enum mei_cfg_idx - indices to platform specific configurations. + * + * Note: has to be synchronized with mei_cfg_list[] + * + * @MEI_ME_UNDEF_CFG: Lower sentinel. + * @MEI_ME_ICH_CFG: I/O Controller Hub legacy devices. + * @MEI_ME_ICH10_CFG: I/O Controller Hub platforms Gen10 + * @MEI_ME_PCH_CFG: Platform Controller Hub platforms (Up to Gen8). + * @MEI_ME_PCH_CPT_PBG_CFG:Platform Controller Hub workstations + * with quirk for Node Manager exclusion. + * @MEI_ME_PCH8_CFG: Platform Controller Hub Gen8 and newer + * client platforms. + * @MEI_ME_PCH8_SPS_CFG: Platform Controller Hub Gen8 and newer + * servers platforms with quirk for + * SPS firmware exclusion. + * @MEI_ME_NUM_CFG: Upper Sentinel. + */ +enum mei_cfg_idx { + MEI_ME_UNDEF_CFG, + MEI_ME_ICH_CFG, + MEI_ME_ICH10_CFG, + MEI_ME_PCH_CFG, + MEI_ME_PCH_CPT_PBG_CFG, + MEI_ME_PCH8_CFG, + MEI_ME_PCH8_SPS_CFG, + MEI_ME_NUM_CFG, +}; + +const struct mei_cfg *mei_me_get_cfg(kernel_ulong_t idx); struct mei_device *mei_me_dev_init(struct pci_dev *pdev, const struct mei_cfg *cfg); diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 8621a198a2ce..4ff40d319676 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -43,57 +43,58 @@ /* mei_pci_tbl - PCI Device ID Table */ static const struct pci_device_id mei_me_pci_tbl[] = { - {MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82G35, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82G965, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, mei_me_legacy_cfg)}, - - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, mei_me_legacy_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, mei_me_ich_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, mei_me_ich_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, mei_me_ich_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, mei_me_ich_cfg)}, - - {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, mei_me_pch_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, mei_me_pch_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, mei_me_pch_cpt_pbg_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, mei_me_pch_cpt_pbg_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, mei_me_pch_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, mei_me_pch_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, mei_me_pch_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, mei_me_pch8_sps_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, mei_me_pch8_sps_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, mei_me_pch8_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, mei_me_pch8_sps_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, mei_me_pch8_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, mei_me_pch8_cfg)}, - - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, mei_me_pch8_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, mei_me_pch8_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_sps_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_sps_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, mei_me_pch8_cfg)}, - - {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, mei_me_pch8_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, mei_me_pch8_cfg)}, - - {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, mei_me_pch8_cfg)}, - {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, mei_me_pch8_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_82946GZ, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_82G35, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_82Q965, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_82G965, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_82GM965, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_82GME965, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q35, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82G33, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82Q33, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_82X38, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_3200, MEI_ME_ICH_CFG)}, + + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_6, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_7, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_8, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_9, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9_10, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_1, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_2, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_3, MEI_ME_ICH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH9M_4, MEI_ME_ICH_CFG)}, + + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_1, MEI_ME_ICH10_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_2, MEI_ME_ICH10_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_3, MEI_ME_ICH10_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_ICH10_4, MEI_ME_ICH10_CFG)}, + + {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_1, MEI_ME_PCH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_IBXPK_2, MEI_ME_PCH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_CPT_1, MEI_ME_PCH_CPT_PBG_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_PBG_1, MEI_ME_PCH_CPT_PBG_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_1, MEI_ME_PCH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_2, MEI_ME_PCH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_PPT_3, MEI_ME_PCH_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_H, MEI_ME_PCH8_SPS_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_W, MEI_ME_PCH8_SPS_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, MEI_ME_PCH8_SPS_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, MEI_ME_PCH8_CFG)}, + + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, MEI_ME_PCH8_SPS_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, MEI_ME_PCH8_SPS_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_LBG, MEI_ME_PCH8_CFG)}, + + {MEI_PCI_DEVICE(MEI_DEV_ID_BXT_M, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_APL_I, MEI_ME_PCH8_CFG)}, + + {MEI_PCI_DEVICE(MEI_DEV_ID_KBP, MEI_ME_PCH8_CFG)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_KBP_2, MEI_ME_PCH8_CFG)}, /* required last entry */ {0, } @@ -138,12 +139,15 @@ static bool mei_me_quirk_probe(struct pci_dev *pdev, */ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data); + const struct mei_cfg *cfg; struct mei_device *dev; struct mei_me_hw *hw; unsigned int irqflags; int err; + cfg = mei_me_get_cfg(ent->driver_data); + if (!cfg) + return -ENODEV; if (!mei_me_quirk_probe(pdev, cfg)) return -ENODEV; @@ -216,6 +220,12 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, dev); /* + * 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; + + /* * For not wake-able HW runtime pm framework * can't be used on pci device level. * Use domain runtime pm callbacks instead. @@ -485,6 +495,7 @@ static struct pci_driver mei_me_driver = { .remove = mei_me_remove, .shutdown = mei_me_shutdown, .driver.pm = MEI_ME_PM_OPS, + .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, }; module_pci_driver(mei_me_driver); diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c index f811cd524468..e38a5f144373 100644 --- a/drivers/misc/mei/pci-txe.c +++ b/drivers/misc/mei/pci-txe.c @@ -138,6 +138,12 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, dev); /* + * 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; + + /* * For not wake-able HW runtime pm framework * can't be used on pci device level. * Use domain runtime pm callbacks instead. diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c index e42bdc90fa27..540845651b8c 100644 --- a/drivers/misc/pch_phub.c +++ b/drivers/misc/pch_phub.c @@ -659,7 +659,7 @@ static ssize_t store_pch_mac(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(pch_mac, S_IRUGO | S_IWUSR, show_pch_mac, store_pch_mac); -static struct bin_attribute pch_bin_attr = { +static const struct bin_attribute pch_bin_attr = { .attr = { .name = "pch_firmware", .mode = S_IRUGO | S_IWUSR, @@ -891,7 +891,7 @@ static int pch_phub_resume(struct pci_dev *pdev) #define pch_phub_resume NULL #endif /* CONFIG_PM */ -static struct pci_device_id pch_phub_pcidev_id[] = { +static const struct pci_device_id pch_phub_pcidev_id[] = { { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH1_PHUB), 1, }, { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7213_PHUB), 2, }, { PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ROHM_ML7223_mPHUB), 3, }, diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index d1185b78cf9a..fc0415771c00 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -196,15 +196,15 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) ret = of_address_to_resource(child, 0, &child_res); if (ret < 0) { dev_err(sram->dev, - "could not get address for node %s\n", - child->full_name); + "could not get address for node %pOF\n", + child); goto err_chunks; } if (child_res.start < res->start || child_res.end > res->end) { dev_err(sram->dev, - "reserved block %s outside the sram area\n", - child->full_name); + "reserved block %pOF outside the sram area\n", + child); ret = -EINVAL; goto err_chunks; } @@ -230,8 +230,8 @@ static int sram_reserve_regions(struct sram_dev *sram, struct resource *res) ret = of_property_read_string(child, "label", &label); if (ret && ret != -EINVAL) { dev_err(sram->dev, - "%s has invalid label name\n", - child->full_name); + "%pOF has invalid label name\n", + child); goto err_chunks; } if (!label) diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index e74413f882ab..b77aacafc3fc 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -660,7 +660,7 @@ static struct attribute *uim_attrs[] = { NULL, }; -static struct attribute_group uim_attr_grp = { +static const struct attribute_group uim_attr_grp = { .attrs = uim_attrs, }; diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index a37a42f67088..e5f108713dd8 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -415,7 +415,7 @@ static void tifm_7xx1_remove(struct pci_dev *dev) tifm_free_adapter(fm); } -static struct pci_device_id tifm_7xx1_pci_tbl [] = { +static const struct pci_device_id tifm_7xx1_pci_tbl[] = { { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX21_XX11_FM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, /* xx21 - the one I have */ { PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_XX12_FM, PCI_ANY_ID, diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index 06c4974ee8dd..8af5c2672f71 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -2235,14 +2235,8 @@ int vmci_qp_broker_detach(struct vmci_handle handle, struct vmci_ctx *context) handle.context, handle.resource, result); - if (entry->vmci_page_files) - qp_host_unregister_user_memory(entry->produce_q, - entry-> - consume_q); - else - qp_host_unregister_user_memory(entry->produce_q, - entry-> - consume_q); + qp_host_unregister_user_memory(entry->produce_q, + entry->consume_q); } diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 8ac59dc80f23..80d1ec693d2d 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -1371,12 +1371,46 @@ static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq, R1_CC_ERROR | /* Card controller error */ \ R1_ERROR) /* General/unknown error */ -static bool mmc_blk_has_cmd_err(struct mmc_command *cmd) +static void mmc_blk_eval_resp_error(struct mmc_blk_request *brq) { - if (!cmd->error && cmd->resp[0] & CMD_ERRORS) - cmd->error = -EIO; + u32 val; - return cmd->error; + /* + * Per the SD specification(physical layer version 4.10)[1], + * section 4.3.3, it explicitly states that "When the last + * block of user area is read using CMD18, the host should + * ignore OUT_OF_RANGE error that may occur even the sequence + * is correct". And JESD84-B51 for eMMC also has a similar + * statement on section 6.8.3. + * + * Multiple block read/write could be done by either predefined + * method, namely CMD23, or open-ending mode. For open-ending mode, + * we should ignore the OUT_OF_RANGE error as it's normal behaviour. + * + * However the spec[1] doesn't tell us whether we should also + * ignore that for predefined method. But per the spec[1], section + * 4.15 Set Block Count Command, it says"If illegal block count + * is set, out of range error will be indicated during read/write + * operation (For example, data transfer is stopped at user area + * boundary)." In another word, we could expect a out of range error + * in the response for the following CMD18/25. And if argument of + * CMD23 + the argument of CMD18/25 exceed the max number of blocks, + * we could also expect to get a -ETIMEDOUT or any error number from + * the host drivers due to missing data response(for write)/data(for + * read), as the cards will stop the data transfer by itself per the + * spec. So we only need to check R1_OUT_OF_RANGE for open-ending mode. + */ + + if (!brq->stop.error) { + bool oor_with_open_end; + /* If there is no error yet, check R1 response */ + + val = brq->stop.resp[0] & CMD_ERRORS; + oor_with_open_end = val & R1_OUT_OF_RANGE && !brq->mrq.sbc; + + if (val && !oor_with_open_end) + brq->stop.error = -EIO; + } } static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card, @@ -1400,8 +1434,11 @@ static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card, * stop.error indicates a problem with the stop command. Data * may have been transferred, or may still be transferring. */ - if (brq->sbc.error || brq->cmd.error || mmc_blk_has_cmd_err(&brq->stop) || - brq->data.error) { + + mmc_blk_eval_resp_error(brq); + + if (brq->sbc.error || brq->cmd.error || + brq->stop.error || brq->data.error) { switch (mmc_blk_cmd_recovery(card, req, brq, &ecc_err, &gen_err)) { case ERR_RETRY: return MMC_BLK_RETRY; @@ -2170,6 +2207,9 @@ static void mmc_blk_remove_req(struct mmc_blk_data *md) * from being accepted. */ card = md->queue.card; + spin_lock_irq(md->queue.queue->queue_lock); + queue_flag_set(QUEUE_FLAG_BYPASS, md->queue.queue); + spin_unlock_irq(md->queue.queue->queue_lock); blk_set_queue_dying(md->queue.queue); mmc_cleanup_queue(&md->queue); if (md->disk->flags & GENHD_FL_UP) { diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 4ffea14b7eb6..2bae69e39544 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1289,7 +1289,7 @@ out_err: static int mmc_select_hs400es(struct mmc_card *card) { struct mmc_host *host = card->host; - int err = 0; + int err = -EINVAL; u8 val; if (!(host->caps & MMC_CAP_8_BIT_DATA)) { diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index a9dfb26972f2..250dc6ec4c82 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2957,7 +2957,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) } /* find out number of slots supported */ - if (device_property_read_u32(dev, "num-slots", &pdata->num_slots)) + if (!device_property_read_u32(dev, "num-slots", &pdata->num_slots)) dev_info(dev, "'num-slots' was deprecated.\n"); if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth)) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 7c12f3715676..2ab4788d021f 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -356,9 +356,6 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on, struct mmc_host *mmc = host->mmc; int ret = 0; - if (mmc_pdata(host)->set_power) - return mmc_pdata(host)->set_power(host->dev, power_on, vdd); - /* * If we don't see a Vcc regulator, assume it's a fixed * voltage always-on regulator. @@ -366,9 +363,6 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on, if (IS_ERR(mmc->supply.vmmc)) return 0; - if (mmc_pdata(host)->before_set_reg) - mmc_pdata(host)->before_set_reg(host->dev, power_on, vdd); - ret = omap_hsmmc_set_pbias(host, false, 0); if (ret) return ret; @@ -400,9 +394,6 @@ static int omap_hsmmc_set_power(struct omap_hsmmc_host *host, int power_on, return ret; } - if (mmc_pdata(host)->after_set_reg) - mmc_pdata(host)->after_set_reg(host->dev, power_on, vdd); - return 0; err_set_voltage: @@ -469,8 +460,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) int ret; struct mmc_host *mmc = host->mmc; - if (mmc_pdata(host)->set_power) - return 0; ret = mmc_regulator_get_supply(mmc); if (ret == -EPROBE_DEFER) @@ -2097,7 +2086,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) mmc->max_seg_size = mmc->max_req_size; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | - MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE; + MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE | MMC_CAP_CMD23; mmc->caps |= mmc_pdata(host)->caps; if (mmc->caps & MMC_CAP_8_BIT_DATA) diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index 7611fd679f1a..1485530c3592 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -31,6 +31,7 @@ #define SDMMC_MC1R 0x204 #define SDMMC_MC1R_DDR BIT(3) +#define SDMMC_MC1R_FCD BIT(7) #define SDMMC_CACR 0x230 #define SDMMC_CACR_CAPWREN BIT(0) #define SDMMC_CACR_KEY (0x46 << 8) @@ -43,6 +44,15 @@ struct sdhci_at91_priv { struct clk *mainck; }; +static void sdhci_at91_set_force_card_detect(struct sdhci_host *host) +{ + u8 mc1r; + + mc1r = readb(host->ioaddr + SDMMC_MC1R); + mc1r |= SDMMC_MC1R_FCD; + writeb(mc1r, host->ioaddr + SDMMC_MC1R); +} + static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock) { u16 clk; @@ -110,10 +120,18 @@ void sdhci_at91_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) sdhci_set_uhs_signaling(host, timing); } +static void sdhci_at91_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) + sdhci_at91_set_force_card_detect(host); +} + static const struct sdhci_ops sdhci_at91_sama5d2_ops = { .set_clock = sdhci_at91_set_clock, .set_bus_width = sdhci_set_bus_width, - .reset = sdhci_reset, + .reset = sdhci_at91_reset, .set_uhs_signaling = sdhci_at91_set_uhs_signaling, .set_power = sdhci_at91_set_power, }; @@ -324,6 +342,21 @@ static int sdhci_at91_probe(struct platform_device *pdev) host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; } + /* + * If the device attached to the MMC bus is not removable, it is safer + * to set the Force Card Detect bit. People often don't connect the + * card detect signal and use this pin for another purpose. If the card + * detect pin is not muxed to SDHCI controller, a default value is + * used. This value can be different from a SoC revision to another + * one. Problems come when this default value is not card present. To + * avoid this case, if the device is non removable then the card + * detection procedure using the SDMCC_CD signal is bypassed. + * This bit is reset when a software reset for all command is performed + * so we need to implement our own reset function to set back this bit. + */ + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) + sdhci_at91_set_force_card_detect(host); + pm_runtime_put_autosuspend(&pdev->dev); return 0; diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index d6fa2214aaae..0fb4e4c119e1 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -793,8 +793,12 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, } mmc_writel(host, REG_CLKCR, rval); - if (host->cfg->needs_new_timings) - mmc_writel(host, REG_SD_NTSR, SDXC_2X_TIMING_MODE); + if (host->cfg->needs_new_timings) { + /* Don't touch the delay bits */ + rval = mmc_readl(host, REG_SD_NTSR); + rval |= SDXC_2X_TIMING_MODE; + mmc_writel(host, REG_SD_NTSR, rval); + } ret = sunxi_mmc_clk_set_phase(host, ios, rate); if (ret) diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index f336a9b85576..9ec8f033ac5f 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -113,6 +113,7 @@ static blk_status_t do_blktrans_request(struct mtd_blktrans_ops *tr, for (; nsect > 0; nsect--, block++, buf += tr->blksize) if (tr->writesect(dev, block, buf)) return BLK_STS_IOERR; + return BLK_STS_OK; default: return BLK_STS_IOERR; } diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c index d922a88e407f..ceec21bd30c4 100644 --- a/drivers/mtd/nand/atmel/nand-controller.c +++ b/drivers/mtd/nand/atmel/nand-controller.c @@ -1201,7 +1201,7 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, * tRC < 30ns implies EDO mode. This controller does not support this * mode. */ - if (conf->timings.sdr.tRC_min < 30) + if (conf->timings.sdr.tRC_min < 30000) return -ENOTSUPP; atmel_smc_cs_conf_init(smcconf); @@ -1364,7 +1364,18 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand, ret = atmel_smc_cs_conf_set_timing(smcconf, ATMEL_HSMC_TIMINGS_TADL_SHIFT, ncycles); - if (ret) + /* + * Version 4 of the ONFI spec mandates that tADL be at least 400 + * nanoseconds, but, depending on the master clock rate, 400 ns may not + * fit in the tADL field of the SMC reg. We need to relax the check and + * accept the -ERANGE return code. + * + * Note that previous versions of the ONFI spec had a lower tADL_min + * (100 or 200 ns). It's not clear why this timing constraint got + * increased but it seems most NANDs are fine with values lower than + * 400ns, so we should be safe. + */ + if (ret && ret != -ERANGE) return ret; ncycles = DIV_ROUND_UP(conf->timings.sdr.tAR_min, mckperiodps); diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c index 55a8ee5306ea..8c210a5776bc 100644 --- a/drivers/mtd/nand/atmel/pmecc.c +++ b/drivers/mtd/nand/atmel/pmecc.c @@ -945,6 +945,7 @@ struct atmel_pmecc *devm_atmel_pmecc_get(struct device *userdev) */ struct platform_device *pdev = to_platform_device(userdev); const struct atmel_pmecc_caps *caps; + const struct of_device_id *match; /* No PMECC engine available. */ if (!of_property_read_bool(userdev->of_node, @@ -953,21 +954,11 @@ struct atmel_pmecc *devm_atmel_pmecc_get(struct device *userdev) caps = &at91sam9g45_caps; - /* - * Try to find the NFC subnode and extract the associated caps - * from there. - */ - np = of_find_compatible_node(userdev->of_node, NULL, - "atmel,sama5d3-nfc"); - if (np) { - const struct of_device_id *match; - - match = of_match_node(atmel_pmecc_legacy_match, np); - if (match && match->data) - caps = match->data; - - of_node_put(np); - } + /* Find the caps associated to the NAND dev node. */ + match = of_match_node(atmel_pmecc_legacy_match, + userdev->of_node); + if (match && match->data) + caps = match->data; pmecc = atmel_pmecc_create(pdev, caps, 1, 2); } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5fa5ddc94834..c6c18b82f8f4 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -65,8 +65,14 @@ static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section, if (!section) { oobregion->offset = 0; - oobregion->length = 4; + if (mtd->oobsize == 16) + oobregion->length = 4; + else + oobregion->length = 3; } else { + if (mtd->oobsize == 8) + return -ERANGE; + oobregion->offset = 6; oobregion->length = ecc->total - 4; } @@ -1125,7 +1131,9 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) * Ensure the timing mode has been changed on the chip side * before changing timings on the controller side. */ - if (chip->onfi_version) { + if (chip->onfi_version && + (le16_to_cpu(chip->onfi_params.opt_cmd) & + ONFI_OPT_CMD_SET_GET_FEATURES)) { u8 tmode_param[ONFI_SUBFEATURE_PARAM_LEN] = { chip->onfi_timing_mode_default, }; @@ -2741,7 +2749,6 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, * @buf: the data to write * @oob_required: must write chip->oob_poi to OOB * @page: page number to write - * @cached: cached programming * @raw: use _raw version of write_page */ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c index f06312df3669..7e36d7d13c26 100644 --- a/drivers/mtd/nand/nand_timings.c +++ b/drivers/mtd/nand/nand_timings.c @@ -311,9 +311,9 @@ int onfi_init_data_interface(struct nand_chip *chip, struct nand_sdr_timings *timings = &iface->timings.sdr; /* microseconds -> picoseconds */ - timings->tPROG_max = 1000000UL * le16_to_cpu(params->t_prog); - timings->tBERS_max = 1000000UL * le16_to_cpu(params->t_bers); - timings->tR_max = 1000000UL * le16_to_cpu(params->t_r); + timings->tPROG_max = 1000000ULL * le16_to_cpu(params->t_prog); + timings->tBERS_max = 1000000ULL * le16_to_cpu(params->t_bers); + timings->tR_max = 1000000ULL * le16_to_cpu(params->t_r); /* nanoseconds -> picoseconds */ timings->tCCS_min = 1000UL * le16_to_cpu(params->t_ccs); diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 03a0d057bf2f..e4211c3cc49b 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -2373,6 +2373,7 @@ static int __init ns_init_module(void) return 0; err_exit: + nandsim_debugfs_remove(nand); free_nandsim(nand); nand_release(nsmtd); for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i) diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c index d0b6f8f9f297..6abd142b1324 100644 --- a/drivers/mtd/nand/sunxi_nand.c +++ b/drivers/mtd/nand/sunxi_nand.c @@ -1728,6 +1728,10 @@ static int sunxi_nfc_setup_data_interface(struct mtd_info *mtd, int csline, */ chip->clk_rate = NSEC_PER_SEC / min_clk_period; real_clk_rate = clk_round_rate(nfc->mod_clk, chip->clk_rate); + if (real_clk_rate <= 0) { + dev_err(nfc->dev, "Unable to round clk %lu\n", chip->clk_rate); + return -EINVAL; + } /* * ONFI specification 3.1, paragraph 4.15.2 dictates that EDO data diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index 7c754a0f14bb..19e4e904c9bf 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -2,20 +2,11 @@ # Multiplexer devices # -menuconfig MULTIPLEXER - tristate "Multiplexer subsystem" - help - Multiplexer controller subsystem. Multiplexers are used in a - variety of settings, and this subsystem abstracts their use - so that the rest of the kernel sees a common interface. When - multiple parallel multiplexers are controlled by one single - multiplexer controller, this subsystem also coordinates the - multiplexer accesses. - - To compile the subsystem as a module, choose M here: the module will - be called mux-core. +config MULTIPLEXER + tristate -if MULTIPLEXER +menu "Multiplexer drivers" + depends on MULTIPLEXER config MUX_ADG792A tristate "Analog Devices ADG792A/ADG792G Multiplexers" @@ -56,4 +47,4 @@ config MUX_MMIO To compile the driver as a module, choose M here: the module will be called mux-mmio. -endif +endmenu diff --git a/drivers/mux/mux-core.c b/drivers/mux/mux-core.c index 90b8995f07cb..2fe96c470112 100644 --- a/drivers/mux/mux-core.c +++ b/drivers/mux/mux-core.c @@ -46,7 +46,7 @@ static int __init mux_init(void) static void __exit mux_exit(void) { - class_register(&mux_class); + class_unregister(&mux_class); ida_destroy(&mux_ida); } diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 14ff622190a5..fc63992ab0e0 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1569,7 +1569,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) new_slave->delay = 0; new_slave->link_failure_count = 0; - if (bond_update_speed_duplex(new_slave)) + if (bond_update_speed_duplex(new_slave) && + bond_needs_speed_duplex(bond)) new_slave->link = BOND_LINK_DOWN; new_slave->last_rx = jiffies - @@ -2050,6 +2051,7 @@ static int bond_miimon_inspect(struct bonding *bond) continue; bond_propose_link_state(slave, BOND_LINK_FAIL); + commit++; slave->delay = bond->params.downdelay; if (slave->delay) { netdev_info(bond->dev, "link status down for %sinterface %s, disabling it in %d ms\n", @@ -2088,6 +2090,7 @@ static int bond_miimon_inspect(struct bonding *bond) continue; bond_propose_link_state(slave, BOND_LINK_BACK); + commit++; slave->delay = bond->params.updelay; if (slave->delay) { @@ -2138,11 +2141,13 @@ static void bond_miimon_commit(struct bonding *bond) continue; case BOND_LINK_UP: - if (bond_update_speed_duplex(slave)) { + if (bond_update_speed_duplex(slave) && + bond_needs_speed_duplex(bond)) { slave->link = BOND_LINK_DOWN; - netdev_warn(bond->dev, - "failed to get link speed/duplex for %s\n", - slave->dev->name); + if (net_ratelimit()) + netdev_warn(bond->dev, + "failed to get link speed/duplex for %s\n", + slave->dev->name); continue; } bond_set_slave_link_state(slave, BOND_LINK_UP, @@ -4596,7 +4601,7 @@ static int bond_check_params(struct bond_params *params) } ad_user_port_key = valptr->value; - if (bond_mode == BOND_MODE_TLB) { + if ((bond_mode == BOND_MODE_TLB) || (bond_mode == BOND_MODE_ALB)) { bond_opt_initstr(&newval, "default"); valptr = bond_opt_parse(bond_opt_get(BOND_OPT_TLB_DYNAMIC_LB), &newval); diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index e68d368e20ac..7f36d3e3c98b 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1665,6 +1665,7 @@ static const struct b53_chip_data b53_switch_chips[] = { .dev_name = "BCM53125", .vlans = 4096, .enabled_ports = 0xff, + .arl_entries = 4, .cpu_port = B53_CPU_PORT, .vta_regs = B53_VTA_REGS, .duplex_reg = B53_DUPLEX_STAT_GE, diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 1e46418a3b74..264b281eb86b 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -625,6 +625,44 @@ static void mt7530_adjust_link(struct dsa_switch *ds, int port, * all finished. */ mt7623_pad_clk_setup(ds); + } else { + u16 lcl_adv = 0, rmt_adv = 0; + u8 flowctrl; + u32 mcr = PMCR_USERP_LINK | PMCR_FORCE_MODE; + + switch (phydev->speed) { + case SPEED_1000: + mcr |= PMCR_FORCE_SPEED_1000; + break; + case SPEED_100: + mcr |= PMCR_FORCE_SPEED_100; + break; + }; + + if (phydev->link) + mcr |= PMCR_FORCE_LNK; + + if (phydev->duplex) { + mcr |= PMCR_FORCE_FDX; + + if (phydev->pause) + rmt_adv = LPA_PAUSE_CAP; + if (phydev->asym_pause) + rmt_adv |= LPA_PAUSE_ASYM; + + if (phydev->advertising & ADVERTISED_Pause) + lcl_adv |= ADVERTISE_PAUSE_CAP; + if (phydev->advertising & ADVERTISED_Asym_Pause) + lcl_adv |= ADVERTISE_PAUSE_ASYM; + + flowctrl = mii_resolve_flowctrl_fdx(lcl_adv, rmt_adv); + + if (flowctrl & FLOW_CTRL_TX) + mcr |= PMCR_TX_FC_EN; + if (flowctrl & FLOW_CTRL_RX) + mcr |= PMCR_RX_FC_EN; + } + mt7530_write(priv, MT7530_PMCR_P(port), mcr); } } diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index b83d76b99802..74db9822eb40 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -151,6 +151,7 @@ enum mt7530_stp_state { #define PMCR_TX_FC_EN BIT(5) #define PMCR_RX_FC_EN BIT(4) #define PMCR_FORCE_SPEED_1000 BIT(3) +#define PMCR_FORCE_SPEED_100 BIT(2) #define PMCR_FORCE_FDX BIT(1) #define PMCR_FORCE_LNK BIT(0) #define PMCR_COMMON_LINK (PMCR_IFG_XMIT(1) | PMCR_MAC_MODE | \ diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 53b088166c28..5bcdd33101b0 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3178,6 +3178,7 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = { .port_set_jumbo_size = mv88e6165_port_set_jumbo_size, .port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting, .port_pause_limit = mv88e6390_port_pause_limit, + .port_set_cmode = mv88e6390x_port_set_cmode, .port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit, .port_disable_pri_override = mv88e6xxx_port_disable_pri_override, .stats_snapshot = mv88e6390_g1_stats_snapshot, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index d3906f6b01bd..1d307f2def2d 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1787,14 +1787,16 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) pdata->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(pdata->clk)) { - /* Abort if the clock is defined but couldn't be retrived. - * Always abort if the clock is missing on DT system as - * the driver can't cope with this case. - */ - if (PTR_ERR(pdata->clk) != -ENOENT || dev->of_node) - return PTR_ERR(pdata->clk); - /* Firmware may have set up the clock already. */ - dev_info(dev, "clocks have been setup already\n"); + if (pdata->phy_mode != PHY_INTERFACE_MODE_SGMII) { + /* Abort if the clock is defined but couldn't be + * retrived. Always abort if the clock is missing on + * DT system as the driver can't cope with this case. + */ + if (PTR_ERR(pdata->clk) != -ENOENT || dev->of_node) + return PTR_ERR(pdata->clk); + /* Firmware may have set up the clock already. */ + dev_info(dev, "clocks have been setup already\n"); + } } if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) diff --git a/drivers/net/ethernet/aurora/nb8800.c b/drivers/net/ethernet/aurora/nb8800.c index 041cfb7952f8..e94159507847 100644 --- a/drivers/net/ethernet/aurora/nb8800.c +++ b/drivers/net/ethernet/aurora/nb8800.c @@ -609,7 +609,7 @@ static void nb8800_mac_config(struct net_device *dev) mac_mode |= HALF_DUPLEX; if (gigabit) { - if (priv->phy_mode == PHY_INTERFACE_MODE_RGMII) + if (phy_interface_is_rgmii(dev->phydev)) mac_mode |= RGMII_MODE; mac_mode |= GMAC_MODE; @@ -1268,11 +1268,10 @@ static int nb8800_tangox_init(struct net_device *dev) break; case PHY_INTERFACE_MODE_RGMII: - pad_mode = PAD_MODE_RGMII; - break; - + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: - pad_mode = PAD_MODE_RGMII | PAD_MODE_GTX_CLK_DELAY; + pad_mode = PAD_MODE_RGMII; break; default: diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index f411936b744c..a1125d10c825 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -2368,6 +2368,7 @@ static int b44_init_one(struct ssb_device *sdev, bp->msg_enable = netif_msg_init(b44_debug, B44_DEF_MSG_ENABLE); spin_lock_init(&bp->lock); + u64_stats_init(&bp->hw_stats.syncp); bp->rx_pending = B44_DEF_RX_RING_PENDING; bp->tx_pending = B44_DEF_TX_RING_PENDING; diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 5333601f855f..dc3052751bc1 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -449,6 +449,10 @@ static void bcm_sysport_get_stats(struct net_device *dev, p = (char *)&dev->stats; else p = (char *)priv; + + if (priv->is_lite && !bcm_sysport_lite_stat_valid(s->type)) + continue; + p += s->stat_offset; data[j] = *(unsigned long *)p; j++; diff --git a/drivers/net/ethernet/broadcom/bgmac-platform.c b/drivers/net/ethernet/broadcom/bgmac-platform.c index 73aca97a96bc..d937083db9a4 100644 --- a/drivers/net/ethernet/broadcom/bgmac-platform.c +++ b/drivers/net/ethernet/broadcom/bgmac-platform.c @@ -50,11 +50,14 @@ static u32 platform_bgmac_idm_read(struct bgmac *bgmac, u16 offset) static void platform_bgmac_idm_write(struct bgmac *bgmac, u16 offset, u32 value) { - return writel(value, bgmac->plat.idm_base + offset); + writel(value, bgmac->plat.idm_base + offset); } static bool platform_bgmac_clk_enabled(struct bgmac *bgmac) { + if (!bgmac->plat.idm_base) + return true; + if ((bgmac_idm_read(bgmac, BCMA_IOCTL) & BGMAC_CLK_EN) != BGMAC_CLK_EN) return false; if (bgmac_idm_read(bgmac, BCMA_RESET_CTL) & BCMA_RESET_CTL_RESET) @@ -66,6 +69,9 @@ static void platform_bgmac_clk_enable(struct bgmac *bgmac, u32 flags) { u32 val; + if (!bgmac->plat.idm_base) + return; + /* The Reset Control register only contains a single bit to show if the * controller is currently in reset. Do a sanity check here, just in * case the bootloader happened to leave the device in reset. @@ -180,6 +186,7 @@ static int bgmac_probe(struct platform_device *pdev) bgmac->feature_flags |= BGMAC_FEAT_CMDCFG_SR_REV4; bgmac->feature_flags |= BGMAC_FEAT_TX_MASK_SETUP; bgmac->feature_flags |= BGMAC_FEAT_RX_MASK_SETUP; + bgmac->feature_flags |= BGMAC_FEAT_IDM_MASK; bgmac->dev = &pdev->dev; bgmac->dma_dev = &pdev->dev; @@ -207,15 +214,13 @@ static int bgmac_probe(struct platform_device *pdev) return PTR_ERR(bgmac->plat.base); regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "idm_base"); - if (!regs) { - dev_err(&pdev->dev, "Unable to obtain idm resource\n"); - return -EINVAL; + if (regs) { + bgmac->plat.idm_base = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(bgmac->plat.idm_base)) + return PTR_ERR(bgmac->plat.idm_base); + bgmac->feature_flags &= ~BGMAC_FEAT_IDM_MASK; } - bgmac->plat.idm_base = devm_ioremap_resource(&pdev->dev, regs); - if (IS_ERR(bgmac->plat.idm_base)) - return PTR_ERR(bgmac->plat.idm_base); - regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nicpm_base"); if (regs) { bgmac->plat.nicpm_base = devm_ioremap_resource(&pdev->dev, diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index ba4d2e145bb9..48d672b204a4 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -622,9 +622,11 @@ static int bgmac_dma_alloc(struct bgmac *bgmac) BUILD_BUG_ON(BGMAC_MAX_TX_RINGS > ARRAY_SIZE(ring_base)); BUILD_BUG_ON(BGMAC_MAX_RX_RINGS > ARRAY_SIZE(ring_base)); - if (!(bgmac_idm_read(bgmac, BCMA_IOST) & BCMA_IOST_DMA64)) { - dev_err(bgmac->dev, "Core does not report 64-bit DMA\n"); - return -ENOTSUPP; + if (!(bgmac->feature_flags & BGMAC_FEAT_IDM_MASK)) { + if (!(bgmac_idm_read(bgmac, BCMA_IOST) & BCMA_IOST_DMA64)) { + dev_err(bgmac->dev, "Core does not report 64-bit DMA\n"); + return -ENOTSUPP; + } } for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) { @@ -855,9 +857,11 @@ static void bgmac_mac_speed(struct bgmac *bgmac) static void bgmac_miiconfig(struct bgmac *bgmac) { if (bgmac->feature_flags & BGMAC_FEAT_FORCE_SPEED_2500) { - bgmac_idm_write(bgmac, BCMA_IOCTL, - bgmac_idm_read(bgmac, BCMA_IOCTL) | 0x40 | - BGMAC_BCMA_IOCTL_SW_CLKEN); + if (!(bgmac->feature_flags & BGMAC_FEAT_IDM_MASK)) { + bgmac_idm_write(bgmac, BCMA_IOCTL, + bgmac_idm_read(bgmac, BCMA_IOCTL) | + 0x40 | BGMAC_BCMA_IOCTL_SW_CLKEN); + } bgmac->mac_speed = SPEED_2500; bgmac->mac_duplex = DUPLEX_FULL; bgmac_mac_speed(bgmac); @@ -874,11 +878,36 @@ static void bgmac_miiconfig(struct bgmac *bgmac) } } +static void bgmac_chip_reset_idm_config(struct bgmac *bgmac) +{ + u32 iost; + + iost = bgmac_idm_read(bgmac, BCMA_IOST); + if (bgmac->feature_flags & BGMAC_FEAT_IOST_ATTACHED) + iost &= ~BGMAC_BCMA_IOST_ATTACHED; + + /* 3GMAC: for BCM4707 & BCM47094, only do core reset at bgmac_probe() */ + if (!(bgmac->feature_flags & BGMAC_FEAT_NO_RESET)) { + u32 flags = 0; + + if (iost & BGMAC_BCMA_IOST_ATTACHED) { + flags = BGMAC_BCMA_IOCTL_SW_CLKEN; + if (!bgmac->has_robosw) + flags |= BGMAC_BCMA_IOCTL_SW_RESET; + } + bgmac_clk_enable(bgmac, flags); + } + + if (iost & BGMAC_BCMA_IOST_ATTACHED && !bgmac->has_robosw) + bgmac_idm_write(bgmac, BCMA_IOCTL, + bgmac_idm_read(bgmac, BCMA_IOCTL) & + ~BGMAC_BCMA_IOCTL_SW_RESET); +} + /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipreset */ static void bgmac_chip_reset(struct bgmac *bgmac) { u32 cmdcfg_sr; - u32 iost; int i; if (bgmac_clk_enabled(bgmac)) { @@ -899,20 +928,8 @@ static void bgmac_chip_reset(struct bgmac *bgmac) /* TODO: Clear software multicast filter list */ } - iost = bgmac_idm_read(bgmac, BCMA_IOST); - if (bgmac->feature_flags & BGMAC_FEAT_IOST_ATTACHED) - iost &= ~BGMAC_BCMA_IOST_ATTACHED; - - /* 3GMAC: for BCM4707 & BCM47094, only do core reset at bgmac_probe() */ - if (!(bgmac->feature_flags & BGMAC_FEAT_NO_RESET)) { - u32 flags = 0; - if (iost & BGMAC_BCMA_IOST_ATTACHED) { - flags = BGMAC_BCMA_IOCTL_SW_CLKEN; - if (!bgmac->has_robosw) - flags |= BGMAC_BCMA_IOCTL_SW_RESET; - } - bgmac_clk_enable(bgmac, flags); - } + if (!(bgmac->feature_flags & BGMAC_FEAT_IDM_MASK)) + bgmac_chip_reset_idm_config(bgmac); /* Request Misc PLL for corerev > 2 */ if (bgmac->feature_flags & BGMAC_FEAT_MISC_PLL_REQ) { @@ -970,11 +987,6 @@ static void bgmac_chip_reset(struct bgmac *bgmac) BGMAC_CHIPCTL_7_IF_TYPE_RGMII); } - if (iost & BGMAC_BCMA_IOST_ATTACHED && !bgmac->has_robosw) - bgmac_idm_write(bgmac, BCMA_IOCTL, - bgmac_idm_read(bgmac, BCMA_IOCTL) & - ~BGMAC_BCMA_IOCTL_SW_RESET); - /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/gmac_reset * Specs don't say about using BGMAC_CMDCFG_SR, but in this routine * BGMAC_CMDCFG is read _after_ putting chip in a reset. So it has to @@ -1497,8 +1509,10 @@ int bgmac_enet_probe(struct bgmac *bgmac) bgmac_clk_enable(bgmac, 0); /* This seems to be fixing IRQ by assigning OOB #6 to the core */ - if (bgmac->feature_flags & BGMAC_FEAT_IRQ_ID_OOB_6) - bgmac_idm_write(bgmac, BCMA_OOB_SEL_OUT_A30, 0x86); + if (!(bgmac->feature_flags & BGMAC_FEAT_IDM_MASK)) { + if (bgmac->feature_flags & BGMAC_FEAT_IRQ_ID_OOB_6) + bgmac_idm_write(bgmac, BCMA_OOB_SEL_OUT_A30, 0x86); + } bgmac_chip_reset(bgmac); diff --git a/drivers/net/ethernet/broadcom/bgmac.h b/drivers/net/ethernet/broadcom/bgmac.h index c1818766c501..443d57b10264 100644 --- a/drivers/net/ethernet/broadcom/bgmac.h +++ b/drivers/net/ethernet/broadcom/bgmac.h @@ -425,6 +425,7 @@ #define BGMAC_FEAT_CC4_IF_SW_TYPE BIT(17) #define BGMAC_FEAT_CC4_IF_SW_TYPE_RGMII BIT(18) #define BGMAC_FEAT_CC7_IF_TYPE_RGMII BIT(19) +#define BGMAC_FEAT_IDM_MASK BIT(20) struct bgmac_slot_info { union { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 43423744fdfa..1e33abde4a3e 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -2886,7 +2886,7 @@ static int bnx2x_test_nvram_tbl(struct bnx2x *bp, static int bnx2x_test_nvram(struct bnx2x *bp) { - const struct crc_pair nvram_tbl[] = { + static const struct crc_pair nvram_tbl[] = { { 0, 0x14 }, /* bootstrap */ { 0x14, 0xec }, /* dir */ { 0x100, 0x350 }, /* manuf_info */ @@ -2895,7 +2895,7 @@ static int bnx2x_test_nvram(struct bnx2x *bp) { 0x708, 0x70 }, /* manuf_key_info */ { 0, 0 } }; - const struct crc_pair nvram_tbl2[] = { + static const struct crc_pair nvram_tbl2[] = { { 0x7e8, 0x350 }, /* manuf_info2 */ { 0xb38, 0xf0 }, /* feature_info */ { 0, 0 } @@ -3162,7 +3162,8 @@ static void bnx2x_get_strings(struct net_device *dev, u32 stringset, u8 *buf) if (is_multi(bp)) { for_each_eth_queue(bp, i) { memset(queue_name, 0, sizeof(queue_name)); - sprintf(queue_name, "%d", i); + snprintf(queue_name, sizeof(queue_name), + "%d", i); for (j = 0; j < BNX2X_NUM_Q_STATS; j++) snprintf(buf + (k + j)*ETH_GSTRING_LEN, ETH_GSTRING_LEN, diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index daca1c9d254b..a981c4ee9d72 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -1202,12 +1202,21 @@ static struct enet_cb *bcmgenet_get_txcb(struct bcmgenet_priv *priv, return tx_cb_ptr; } -/* Simple helper to free a control block's resources */ -static void bcmgenet_free_cb(struct enet_cb *cb) +static struct enet_cb *bcmgenet_put_txcb(struct bcmgenet_priv *priv, + struct bcmgenet_tx_ring *ring) { - dev_kfree_skb_any(cb->skb); - cb->skb = NULL; - dma_unmap_addr_set(cb, dma_addr, 0); + struct enet_cb *tx_cb_ptr; + + tx_cb_ptr = ring->cbs; + tx_cb_ptr += ring->write_ptr - ring->cb_ptr; + + /* Rewinding local write pointer */ + if (ring->write_ptr == ring->cb_ptr) + ring->write_ptr = ring->end_ptr; + else + ring->write_ptr--; + + return tx_cb_ptr; } static inline void bcmgenet_rx_ring16_int_disable(struct bcmgenet_rx_ring *ring) @@ -1260,18 +1269,72 @@ static inline void bcmgenet_tx_ring_int_disable(struct bcmgenet_tx_ring *ring) INTRL2_CPU_MASK_SET); } +/* Simple helper to free a transmit control block's resources + * Returns an skb when the last transmit control block associated with the + * skb is freed. The skb should be freed by the caller if necessary. + */ +static struct sk_buff *bcmgenet_free_tx_cb(struct device *dev, + struct enet_cb *cb) +{ + struct sk_buff *skb; + + skb = cb->skb; + + if (skb) { + cb->skb = NULL; + if (cb == GENET_CB(skb)->first_cb) + dma_unmap_single(dev, dma_unmap_addr(cb, dma_addr), + dma_unmap_len(cb, dma_len), + DMA_TO_DEVICE); + else + dma_unmap_page(dev, dma_unmap_addr(cb, dma_addr), + dma_unmap_len(cb, dma_len), + DMA_TO_DEVICE); + dma_unmap_addr_set(cb, dma_addr, 0); + + if (cb == GENET_CB(skb)->last_cb) + return skb; + + } else if (dma_unmap_addr(cb, dma_addr)) { + dma_unmap_page(dev, + dma_unmap_addr(cb, dma_addr), + dma_unmap_len(cb, dma_len), + DMA_TO_DEVICE); + dma_unmap_addr_set(cb, dma_addr, 0); + } + + return 0; +} + +/* Simple helper to free a receive control block's resources */ +static struct sk_buff *bcmgenet_free_rx_cb(struct device *dev, + struct enet_cb *cb) +{ + struct sk_buff *skb; + + skb = cb->skb; + cb->skb = NULL; + + if (dma_unmap_addr(cb, dma_addr)) { + dma_unmap_single(dev, dma_unmap_addr(cb, dma_addr), + dma_unmap_len(cb, dma_len), DMA_FROM_DEVICE); + dma_unmap_addr_set(cb, dma_addr, 0); + } + + return skb; +} + /* Unlocked version of the reclaim routine */ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, struct bcmgenet_tx_ring *ring) { struct bcmgenet_priv *priv = netdev_priv(dev); - struct device *kdev = &priv->pdev->dev; - struct enet_cb *tx_cb_ptr; - unsigned int pkts_compl = 0; + unsigned int txbds_processed = 0; unsigned int bytes_compl = 0; - unsigned int c_index; + unsigned int pkts_compl = 0; unsigned int txbds_ready; - unsigned int txbds_processed = 0; + unsigned int c_index; + struct sk_buff *skb; /* Clear status before servicing to reduce spurious interrupts */ if (ring->index == DESC_INDEX) @@ -1292,21 +1355,12 @@ static unsigned int __bcmgenet_tx_reclaim(struct net_device *dev, /* Reclaim transmitted buffers */ while (txbds_processed < txbds_ready) { - tx_cb_ptr = &priv->tx_cbs[ring->clean_ptr]; - if (tx_cb_ptr->skb) { + skb = bcmgenet_free_tx_cb(&priv->pdev->dev, + &priv->tx_cbs[ring->clean_ptr]); + if (skb) { pkts_compl++; - bytes_compl += GENET_CB(tx_cb_ptr->skb)->bytes_sent; - dma_unmap_single(kdev, - dma_unmap_addr(tx_cb_ptr, dma_addr), - dma_unmap_len(tx_cb_ptr, dma_len), - DMA_TO_DEVICE); - bcmgenet_free_cb(tx_cb_ptr); - } else if (dma_unmap_addr(tx_cb_ptr, dma_addr)) { - dma_unmap_page(kdev, - dma_unmap_addr(tx_cb_ptr, dma_addr), - dma_unmap_len(tx_cb_ptr, dma_len), - DMA_TO_DEVICE); - dma_unmap_addr_set(tx_cb_ptr, dma_addr, 0); + bytes_compl += GENET_CB(skb)->bytes_sent; + dev_kfree_skb_any(skb); } txbds_processed++; @@ -1380,95 +1434,6 @@ static void bcmgenet_tx_reclaim_all(struct net_device *dev) bcmgenet_tx_reclaim(dev, &priv->tx_rings[DESC_INDEX]); } -/* Transmits a single SKB (either head of a fragment or a single SKB) - * caller must hold priv->lock - */ -static int bcmgenet_xmit_single(struct net_device *dev, - struct sk_buff *skb, - u16 dma_desc_flags, - struct bcmgenet_tx_ring *ring) -{ - struct bcmgenet_priv *priv = netdev_priv(dev); - struct device *kdev = &priv->pdev->dev; - struct enet_cb *tx_cb_ptr; - unsigned int skb_len; - dma_addr_t mapping; - u32 length_status; - int ret; - - tx_cb_ptr = bcmgenet_get_txcb(priv, ring); - - if (unlikely(!tx_cb_ptr)) - BUG(); - - tx_cb_ptr->skb = skb; - - skb_len = skb_headlen(skb); - - mapping = dma_map_single(kdev, skb->data, skb_len, DMA_TO_DEVICE); - ret = dma_mapping_error(kdev, mapping); - if (ret) { - priv->mib.tx_dma_failed++; - netif_err(priv, tx_err, dev, "Tx DMA map failed\n"); - dev_kfree_skb(skb); - return ret; - } - - dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping); - dma_unmap_len_set(tx_cb_ptr, dma_len, skb_len); - length_status = (skb_len << DMA_BUFLENGTH_SHIFT) | dma_desc_flags | - (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT) | - DMA_TX_APPEND_CRC; - - if (skb->ip_summed == CHECKSUM_PARTIAL) - length_status |= DMA_TX_DO_CSUM; - - dmadesc_set(priv, tx_cb_ptr->bd_addr, mapping, length_status); - - return 0; -} - -/* Transmit a SKB fragment */ -static int bcmgenet_xmit_frag(struct net_device *dev, - skb_frag_t *frag, - u16 dma_desc_flags, - struct bcmgenet_tx_ring *ring) -{ - struct bcmgenet_priv *priv = netdev_priv(dev); - struct device *kdev = &priv->pdev->dev; - struct enet_cb *tx_cb_ptr; - unsigned int frag_size; - dma_addr_t mapping; - int ret; - - tx_cb_ptr = bcmgenet_get_txcb(priv, ring); - - if (unlikely(!tx_cb_ptr)) - BUG(); - - tx_cb_ptr->skb = NULL; - - frag_size = skb_frag_size(frag); - - mapping = skb_frag_dma_map(kdev, frag, 0, frag_size, DMA_TO_DEVICE); - ret = dma_mapping_error(kdev, mapping); - if (ret) { - priv->mib.tx_dma_failed++; - netif_err(priv, tx_err, dev, "%s: Tx DMA map failed\n", - __func__); - return ret; - } - - dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping); - dma_unmap_len_set(tx_cb_ptr, dma_len, frag_size); - - dmadesc_set(priv, tx_cb_ptr->bd_addr, mapping, - (frag_size << DMA_BUFLENGTH_SHIFT) | dma_desc_flags | - (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT)); - - return 0; -} - /* Reallocate the SKB to put enough headroom in front of it and insert * the transmit checksum offsets in the descriptors */ @@ -1535,11 +1500,16 @@ static struct sk_buff *bcmgenet_put_tx_csum(struct net_device *dev, static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); + struct device *kdev = &priv->pdev->dev; struct bcmgenet_tx_ring *ring = NULL; + struct enet_cb *tx_cb_ptr; struct netdev_queue *txq; unsigned long flags = 0; int nr_frags, index; - u16 dma_desc_flags; + dma_addr_t mapping; + unsigned int size; + skb_frag_t *frag; + u32 len_stat; int ret; int i; @@ -1592,29 +1562,53 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev) } } - dma_desc_flags = DMA_SOP; - if (nr_frags == 0) - dma_desc_flags |= DMA_EOP; + for (i = 0; i <= nr_frags; i++) { + tx_cb_ptr = bcmgenet_get_txcb(priv, ring); - /* Transmit single SKB or head of fragment list */ - ret = bcmgenet_xmit_single(dev, skb, dma_desc_flags, ring); - if (ret) { - ret = NETDEV_TX_OK; - goto out; - } + if (unlikely(!tx_cb_ptr)) + BUG(); + + if (!i) { + /* Transmit single SKB or head of fragment list */ + GENET_CB(skb)->first_cb = tx_cb_ptr; + size = skb_headlen(skb); + mapping = dma_map_single(kdev, skb->data, size, + DMA_TO_DEVICE); + } else { + /* xmit fragment */ + frag = &skb_shinfo(skb)->frags[i - 1]; + size = skb_frag_size(frag); + mapping = skb_frag_dma_map(kdev, frag, 0, size, + DMA_TO_DEVICE); + } - /* xmit fragment */ - for (i = 0; i < nr_frags; i++) { - ret = bcmgenet_xmit_frag(dev, - &skb_shinfo(skb)->frags[i], - (i == nr_frags - 1) ? DMA_EOP : 0, - ring); + ret = dma_mapping_error(kdev, mapping); if (ret) { + priv->mib.tx_dma_failed++; + netif_err(priv, tx_err, dev, "Tx DMA map failed\n"); ret = NETDEV_TX_OK; - goto out; + goto out_unmap_frags; + } + dma_unmap_addr_set(tx_cb_ptr, dma_addr, mapping); + dma_unmap_len_set(tx_cb_ptr, dma_len, size); + + tx_cb_ptr->skb = skb; + + len_stat = (size << DMA_BUFLENGTH_SHIFT) | + (priv->hw_params->qtag_mask << DMA_TX_QTAG_SHIFT); + + if (!i) { + len_stat |= DMA_TX_APPEND_CRC | DMA_SOP; + if (skb->ip_summed == CHECKSUM_PARTIAL) + len_stat |= DMA_TX_DO_CSUM; } + if (i == nr_frags) + len_stat |= DMA_EOP; + + dmadesc_set(priv, tx_cb_ptr->bd_addr, mapping, len_stat); } + GENET_CB(skb)->last_cb = tx_cb_ptr; skb_tx_timestamp(skb); /* Decrement total BD count and advance our write pointer */ @@ -1635,6 +1629,19 @@ out: spin_unlock_irqrestore(&ring->lock, flags); return ret; + +out_unmap_frags: + /* Back up for failed control block mapping */ + bcmgenet_put_txcb(priv, ring); + + /* Unmap successfully mapped control blocks */ + while (i-- > 0) { + tx_cb_ptr = bcmgenet_put_txcb(priv, ring); + bcmgenet_free_tx_cb(kdev, tx_cb_ptr); + } + + dev_kfree_skb(skb); + goto out; } static struct sk_buff *bcmgenet_rx_refill(struct bcmgenet_priv *priv, @@ -1666,14 +1673,12 @@ static struct sk_buff *bcmgenet_rx_refill(struct bcmgenet_priv *priv, } /* Grab the current Rx skb from the ring and DMA-unmap it */ - rx_skb = cb->skb; - if (likely(rx_skb)) - dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr), - priv->rx_buf_len, DMA_FROM_DEVICE); + rx_skb = bcmgenet_free_rx_cb(kdev, cb); /* Put the new Rx skb on the ring */ cb->skb = skb; dma_unmap_addr_set(cb, dma_addr, mapping); + dma_unmap_len_set(cb, dma_len, priv->rx_buf_len); dmadesc_set_addr(priv, cb->bd_addr, mapping); /* Return the current Rx skb to caller */ @@ -1880,22 +1885,16 @@ static int bcmgenet_alloc_rx_buffers(struct bcmgenet_priv *priv, static void bcmgenet_free_rx_buffers(struct bcmgenet_priv *priv) { - struct device *kdev = &priv->pdev->dev; + struct sk_buff *skb; struct enet_cb *cb; int i; for (i = 0; i < priv->num_rx_bds; i++) { cb = &priv->rx_cbs[i]; - if (dma_unmap_addr(cb, dma_addr)) { - dma_unmap_single(kdev, - dma_unmap_addr(cb, dma_addr), - priv->rx_buf_len, DMA_FROM_DEVICE); - dma_unmap_addr_set(cb, dma_addr, 0); - } - - if (cb->skb) - bcmgenet_free_cb(cb); + skb = bcmgenet_free_rx_cb(&priv->pdev->dev, cb); + if (skb) + dev_kfree_skb_any(skb); } } @@ -2479,8 +2478,10 @@ static int bcmgenet_dma_teardown(struct bcmgenet_priv *priv) static void bcmgenet_fini_dma(struct bcmgenet_priv *priv) { - int i; struct netdev_queue *txq; + struct sk_buff *skb; + struct enet_cb *cb; + int i; bcmgenet_fini_rx_napi(priv); bcmgenet_fini_tx_napi(priv); @@ -2489,10 +2490,10 @@ static void bcmgenet_fini_dma(struct bcmgenet_priv *priv) bcmgenet_dma_teardown(priv); for (i = 0; i < priv->num_tx_bds; i++) { - if (priv->tx_cbs[i].skb != NULL) { - dev_kfree_skb(priv->tx_cbs[i].skb); - priv->tx_cbs[i].skb = NULL; - } + cb = priv->tx_cbs + i; + skb = bcmgenet_free_tx_cb(&priv->pdev->dev, cb); + if (skb) + dev_kfree_skb(skb); } for (i = 0; i < priv->hw_params->tx_queues; i++) { @@ -3668,7 +3669,7 @@ static int bcmgenet_resume(struct device *d) phy_init_hw(priv->phydev); /* Speed settings must be restored */ - bcmgenet_mii_config(priv->dev); + bcmgenet_mii_config(priv->dev, false); /* disable ethernet MAC while updating its registers */ umac_enable_set(priv, CMD_TX_EN | CMD_RX_EN, false); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index efd07020b89f..3a34fdba5301 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -544,6 +544,8 @@ struct bcmgenet_hw_params { }; struct bcmgenet_skb_cb { + struct enet_cb *first_cb; /* First control block of SKB */ + struct enet_cb *last_cb; /* Last control block of SKB */ unsigned int bytes_sent; /* bytes on the wire (no TSB) */ }; @@ -696,7 +698,7 @@ GENET_IO_MACRO(rbuf, GENET_RBUF_OFF); /* MDIO routines */ int bcmgenet_mii_init(struct net_device *dev); -int bcmgenet_mii_config(struct net_device *dev); +int bcmgenet_mii_config(struct net_device *dev, bool init); int bcmgenet_mii_probe(struct net_device *dev); void bcmgenet_mii_exit(struct net_device *dev); void bcmgenet_mii_reset(struct net_device *dev); diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 071fcbd14e6a..30cb97b4a1d7 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -238,7 +238,7 @@ static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv) bcmgenet_fixed_phy_link_update); } -int bcmgenet_mii_config(struct net_device *dev) +int bcmgenet_mii_config(struct net_device *dev, bool init) { struct bcmgenet_priv *priv = netdev_priv(dev); struct phy_device *phydev = priv->phydev; @@ -327,7 +327,8 @@ int bcmgenet_mii_config(struct net_device *dev) bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); } - dev_info_once(kdev, "configuring instance for %s\n", phy_name); + if (init) + dev_info(kdev, "configuring instance for %s\n", phy_name); return 0; } @@ -375,7 +376,7 @@ int bcmgenet_mii_probe(struct net_device *dev) * PHY speed which is needed for bcmgenet_mii_config() to configure * things appropriately. */ - ret = bcmgenet_mii_config(dev); + ret = bcmgenet_mii_config(dev, true); if (ret) { phy_disconnect(priv->phydev); return ret; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 28ecda3d3404..ebd353bc78ff 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -335,7 +335,7 @@ lio_ethtool_get_channels(struct net_device *dev, static int lio_get_eeprom_len(struct net_device *netdev) { - u8 buf[128]; + u8 buf[192]; struct lio *lio = GET_LIO(netdev); struct octeon_device *oct_dev = lio->oct_dev; struct octeon_board_info *board_info; diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c index a0ca68ce3fbb..5e5c4d7796b8 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.c +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.c @@ -292,11 +292,30 @@ static void bgx_sgmii_change_link_state(struct lmac *lmac) u64 cmr_cfg; u64 port_cfg = 0; u64 misc_ctl = 0; + bool tx_en, rx_en; cmr_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_CMRX_CFG); - cmr_cfg &= ~CMR_EN; + tx_en = cmr_cfg & CMR_PKT_TX_EN; + rx_en = cmr_cfg & CMR_PKT_RX_EN; + cmr_cfg &= ~(CMR_PKT_RX_EN | CMR_PKT_TX_EN); bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg); + /* Wait for BGX RX to be idle */ + if (bgx_poll_reg(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, + GMI_PORT_CFG_RX_IDLE, false)) { + dev_err(&bgx->pdev->dev, "BGX%d LMAC%d GMI RX not idle\n", + bgx->bgx_id, lmac->lmacid); + return; + } + + /* Wait for BGX TX to be idle */ + if (bgx_poll_reg(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, + GMI_PORT_CFG_TX_IDLE, false)) { + dev_err(&bgx->pdev->dev, "BGX%d LMAC%d GMI TX not idle\n", + bgx->bgx_id, lmac->lmacid); + return; + } + port_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG); misc_ctl = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL); @@ -347,10 +366,8 @@ static void bgx_sgmii_change_link_state(struct lmac *lmac) bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_PCS_MISCX_CTL, misc_ctl); bgx_reg_write(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG, port_cfg); - port_cfg = bgx_reg_read(bgx, lmac->lmacid, BGX_GMP_GMI_PRTX_CFG); - - /* Re-enable lmac */ - cmr_cfg |= CMR_EN; + /* Restore CMR config settings */ + cmr_cfg |= (rx_en ? CMR_PKT_RX_EN : 0) | (tx_en ? CMR_PKT_TX_EN : 0); bgx_reg_write(bgx, lmac->lmacid, BGX_CMRX_CFG, cmr_cfg); if (bgx->is_rgx && (cmr_cfg & (CMR_PKT_RX_EN | CMR_PKT_TX_EN))) @@ -1008,7 +1025,7 @@ static void bgx_print_qlm_mode(struct bgx *bgx, u8 lmacid) { struct device *dev = &bgx->pdev->dev; struct lmac *lmac; - char str[20]; + char str[27]; if (!bgx->is_dlm && lmacid) return; diff --git a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h index 6b7fe6fdd13b..23acdc5ab896 100644 --- a/drivers/net/ethernet/cavium/thunder/thunder_bgx.h +++ b/drivers/net/ethernet/cavium/thunder/thunder_bgx.h @@ -170,6 +170,8 @@ #define GMI_PORT_CFG_DUPLEX BIT_ULL(2) #define GMI_PORT_CFG_SLOT_TIME BIT_ULL(3) #define GMI_PORT_CFG_SPEED_MSB BIT_ULL(8) +#define GMI_PORT_CFG_RX_IDLE BIT_ULL(12) +#define GMI_PORT_CFG_TX_IDLE BIT_ULL(13) #define BGX_GMP_GMI_RXX_JABBER 0x38038 #define BGX_GMP_GMI_TXX_THRESH 0x38210 #define BGX_GMP_GMI_TXX_APPEND 0x38218 diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index ef4be781fd05..09ea62ee96d3 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -529,6 +529,7 @@ enum { /* adapter flags */ USING_SOFT_PARAMS = (1 << 6), MASTER_PF = (1 << 7), FW_OFLD_CONN = (1 << 9), + ROOT_NO_RELAXED_ORDERING = (1 << 10), }; enum { diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index e403fa18f1b1..33bb8678833a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -4654,11 +4654,6 @@ static void print_port_info(const struct net_device *dev) dev->name, adap->params.vpd.id, adap->name, buf); } -static void enable_pcie_relaxed_ordering(struct pci_dev *dev) -{ - pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN); -} - /* * Free the following resources: * - memory used for tables @@ -4908,7 +4903,6 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } pci_enable_pcie_error_reporting(pdev); - enable_pcie_relaxed_ordering(pdev); pci_set_master(pdev); pci_save_state(pdev); @@ -4947,6 +4941,23 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->msg_enable = DFLT_MSG_ENABLE; memset(adapter->chan_map, 0xff, sizeof(adapter->chan_map)); + /* If possible, we use PCIe Relaxed Ordering Attribute to deliver + * Ingress Packet Data to Free List Buffers in order to allow for + * chipset performance optimizations between the Root Complex and + * Memory Controllers. (Messages to the associated Ingress Queue + * notifying new Packet Placement in the Free Lists Buffers will be + * send without the Relaxed Ordering Attribute thus guaranteeing that + * all preceding PCIe Transaction Layer Packets will be processed + * first.) But some Root Complexes have various issues with Upstream + * Transaction Layer Packets with the Relaxed Ordering Attribute set. + * The PCIe devices which under the Root Complexes will be cleared the + * Relaxed Ordering bit in the configuration space, So we check our + * PCIe configuration space to see if it's flagged with advice against + * using Relaxed Ordering. + */ + if (!pcie_relaxed_ordering_enabled(pdev)) + adapter->flags |= ROOT_NO_RELAXED_ORDERING; + spin_lock_init(&adapter->stats_lock); spin_lock_init(&adapter->tid_release_lock); spin_lock_init(&adapter->win0_lock); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c index 50517cfd9671..9f9d6cae39d5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ptp.c @@ -441,7 +441,8 @@ void cxgb4_ptp_init(struct adapter *adapter) adapter->ptp_clock = ptp_clock_register(&adapter->ptp_clock_info, &adapter->pdev->dev); - if (!adapter->ptp_clock) { + if (IS_ERR_OR_NULL(adapter->ptp_clock)) { + adapter->ptp_clock = NULL; dev_err(adapter->pdev_dev, "PTP %s Clock registration has failed\n", __func__); return; diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index ede12209f20b..4ef68f69b58c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -2719,6 +2719,7 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, struct fw_iq_cmd c; struct sge *s = &adap->sge; struct port_info *pi = netdev_priv(dev); + int relaxed = !(adap->flags & ROOT_NO_RELAXED_ORDERING); /* Size needs to be multiple of 16, including status entry. */ iq->size = roundup(iq->size, 16); @@ -2772,8 +2773,8 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, flsz = fl->size / 8 + s->stat_len / sizeof(struct tx_desc); c.iqns_to_fl0congen |= htonl(FW_IQ_CMD_FL0PACKEN_F | - FW_IQ_CMD_FL0FETCHRO_F | - FW_IQ_CMD_FL0DATARO_F | + FW_IQ_CMD_FL0FETCHRO_V(relaxed) | + FW_IQ_CMD_FL0DATARO_V(relaxed) | FW_IQ_CMD_FL0PADEN_F); if (cong >= 0) c.iqns_to_fl0congen |= diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h index 99987d8e437e..aa28299aef5f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h @@ -174,6 +174,8 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN CH_PCI_ID_TABLE_FENTRY(0x50a0), /* Custom T540-CR */ CH_PCI_ID_TABLE_FENTRY(0x50a1), /* Custom T540-CR */ CH_PCI_ID_TABLE_FENTRY(0x50a2), /* Custom T540-KR4 */ + CH_PCI_ID_TABLE_FENTRY(0x50a3), /* Custom T580-KR4 */ + CH_PCI_ID_TABLE_FENTRY(0x50a4), /* Custom 2x T540-CR */ /* T6 adapters: */ diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/adapter.h b/drivers/net/ethernet/chelsio/cxgb4vf/adapter.h index 109bc630408b..08c6ddb84a04 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/adapter.h +++ b/drivers/net/ethernet/chelsio/cxgb4vf/adapter.h @@ -408,6 +408,7 @@ enum { /* adapter flags */ USING_MSI = (1UL << 1), USING_MSIX = (1UL << 2), QUEUES_BOUND = (1UL << 3), + ROOT_NO_RELAXED_ORDERING = (1UL << 4), }; /* diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index ac7a150c54e9..2b85b874fd0d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -2888,6 +2888,24 @@ static int cxgb4vf_pci_probe(struct pci_dev *pdev, */ adapter->name = pci_name(pdev); adapter->msg_enable = DFLT_MSG_ENABLE; + + /* If possible, we use PCIe Relaxed Ordering Attribute to deliver + * Ingress Packet Data to Free List Buffers in order to allow for + * chipset performance optimizations between the Root Complex and + * Memory Controllers. (Messages to the associated Ingress Queue + * notifying new Packet Placement in the Free Lists Buffers will be + * send without the Relaxed Ordering Attribute thus guaranteeing that + * all preceding PCIe Transaction Layer Packets will be processed + * first.) But some Root Complexes have various issues with Upstream + * Transaction Layer Packets with the Relaxed Ordering Attribute set. + * The PCIe devices which under the Root Complexes will be cleared the + * Relaxed Ordering bit in the configuration space, So we check our + * PCIe configuration space to see if it's flagged with advice against + * using Relaxed Ordering. + */ + if (!pcie_relaxed_ordering_enabled(pdev)) + adapter->flags |= ROOT_NO_RELAXED_ORDERING; + err = adap_init0(adapter); if (err) goto err_unmap_bar; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index e37dde2ba97f..05498e7f2840 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -2205,6 +2205,7 @@ int t4vf_sge_alloc_rxq(struct adapter *adapter, struct sge_rspq *rspq, struct port_info *pi = netdev_priv(dev); struct fw_iq_cmd cmd, rpl; int ret, iqandst, flsz = 0; + int relaxed = !(adapter->flags & ROOT_NO_RELAXED_ORDERING); /* * If we're using MSI interrupts and we're not initializing the @@ -2300,6 +2301,8 @@ int t4vf_sge_alloc_rxq(struct adapter *adapter, struct sge_rspq *rspq, cpu_to_be32( FW_IQ_CMD_FL0HOSTFCMODE_V(SGE_HOSTFCMODE_NONE) | FW_IQ_CMD_FL0PACKEN_F | + FW_IQ_CMD_FL0FETCHRO_V(relaxed) | + FW_IQ_CMD_FL0DATARO_V(relaxed) | FW_IQ_CMD_FL0PADEN_F); /* In T6, for egress queue type FL there is internal overhead diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 95bf5e89cfd1..34dae51effd4 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -125,7 +125,7 @@ static int ftgmac100_reset_mac(struct ftgmac100 *priv, u32 maccr) iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); iowrite32(maccr | FTGMAC100_MACCR_SW_RST, priv->base + FTGMAC100_OFFSET_MACCR); - for (i = 0; i < 50; i++) { + for (i = 0; i < 200; i++) { unsigned int maccr; maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR); @@ -392,7 +392,7 @@ static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry, struct net_device *netdev = priv->netdev; struct sk_buff *skb; dma_addr_t map; - int err; + int err = 0; skb = netdev_alloc_skb_ip_align(netdev, RX_BUF_SIZE); if (unlikely(!skb)) { @@ -428,7 +428,7 @@ static int ftgmac100_alloc_rx_buf(struct ftgmac100 *priv, unsigned int entry, else rxdes->rxdes0 = 0; - return 0; + return err; } static unsigned int ftgmac100_next_rx_pointer(struct ftgmac100 *priv, @@ -1682,6 +1682,7 @@ static int ftgmac100_setup_mdio(struct net_device *netdev) priv->mii_bus->name = "ftgmac100_mdio"; snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); + priv->mii_bus->parent = priv->dev; priv->mii_bus->priv = priv->netdev; priv->mii_bus->read = ftgmac100_mdiobus_read; priv->mii_bus->write = ftgmac100_mdiobus_write; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index ff864a187d5a..a37166ee577b 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -776,8 +776,9 @@ void hns_ae_update_led_status(struct hnae_handle *handle) assert(handle); mac_cb = hns_get_mac_cb(handle); - if (!mac_cb->cpld_ctrl) + if (mac_cb->media_type != HNAE_MEDIA_TYPE_FIBER) return; + hns_set_led_opt(mac_cb); } diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c index 7a8addda726e..408b63faf9a8 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_misc.c @@ -53,6 +53,34 @@ static u32 dsaf_read_sub(struct dsaf_device *dsaf_dev, u32 reg) return ret; } +static void hns_dsaf_acpi_ledctrl_by_port(struct hns_mac_cb *mac_cb, u8 op_type, + u32 link, u32 port, u32 act) +{ + union acpi_object *obj; + union acpi_object obj_args[3], argv4; + + obj_args[0].integer.type = ACPI_TYPE_INTEGER; + obj_args[0].integer.value = link; + obj_args[1].integer.type = ACPI_TYPE_INTEGER; + obj_args[1].integer.value = port; + obj_args[2].integer.type = ACPI_TYPE_INTEGER; + obj_args[2].integer.value = act; + + argv4.type = ACPI_TYPE_PACKAGE; + argv4.package.count = 3; + argv4.package.elements = obj_args; + + obj = acpi_evaluate_dsm(ACPI_HANDLE(mac_cb->dev), + &hns_dsaf_acpi_dsm_guid, 0, op_type, &argv4); + if (!obj) { + dev_warn(mac_cb->dev, "ledctrl fail, link:%d port:%d act:%d!\n", + link, port, act); + return; + } + + ACPI_FREE(obj); +} + static void hns_cpld_set_led(struct hns_mac_cb *mac_cb, int link_status, u16 speed, int data) { @@ -93,6 +121,18 @@ static void hns_cpld_set_led(struct hns_mac_cb *mac_cb, int link_status, } } +static void hns_cpld_set_led_acpi(struct hns_mac_cb *mac_cb, int link_status, + u16 speed, int data) +{ + if (!mac_cb) { + pr_err("cpld_led_set mac_cb is null!\n"); + return; + } + + hns_dsaf_acpi_ledctrl_by_port(mac_cb, HNS_OP_LED_SET_FUNC, + link_status, mac_cb->mac_id, data); +} + static void cpld_led_reset(struct hns_mac_cb *mac_cb) { if (!mac_cb || !mac_cb->cpld_ctrl) @@ -103,6 +143,20 @@ static void cpld_led_reset(struct hns_mac_cb *mac_cb) mac_cb->cpld_led_value = CPLD_LED_DEFAULT_VALUE; } +static void cpld_led_reset_acpi(struct hns_mac_cb *mac_cb) +{ + if (!mac_cb) { + pr_err("cpld_led_reset mac_cb is null!\n"); + return; + } + + if (mac_cb->media_type != HNAE_MEDIA_TYPE_FIBER) + return; + + hns_dsaf_acpi_ledctrl_by_port(mac_cb, HNS_OP_LED_SET_FUNC, + 0, mac_cb->mac_id, 0); +} + static int cpld_set_led_id(struct hns_mac_cb *mac_cb, enum hnae_led_state status) { @@ -604,8 +658,8 @@ struct dsaf_misc_op *hns_misc_op_get(struct dsaf_device *dsaf_dev) misc_op->cfg_serdes_loopback = hns_mac_config_sds_loopback; } else if (is_acpi_node(dsaf_dev->dev->fwnode)) { - misc_op->cpld_set_led = hns_cpld_set_led; - misc_op->cpld_reset_led = cpld_led_reset; + misc_op->cpld_set_led = hns_cpld_set_led_acpi; + misc_op->cpld_reset_led = cpld_led_reset_acpi; misc_op->cpld_set_led_id = cpld_set_led_id; misc_op->dsaf_reset = hns_dsaf_rst_acpi; diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index a3e694679635..c45e8e3b82d3 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -111,6 +111,7 @@ static void send_request_map(struct ibmvnic_adapter *, dma_addr_t, __be32, u8); static void send_request_unmap(struct ibmvnic_adapter *, u8); static void send_login(struct ibmvnic_adapter *adapter); static void send_cap_queries(struct ibmvnic_adapter *adapter); +static int init_sub_crqs(struct ibmvnic_adapter *); static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter); static int ibmvnic_init(struct ibmvnic_adapter *); static void release_crq_queue(struct ibmvnic_adapter *); @@ -651,6 +652,7 @@ static int ibmvnic_login(struct net_device *netdev) struct ibmvnic_adapter *adapter = netdev_priv(netdev); unsigned long timeout = msecs_to_jiffies(30000); struct device *dev = &adapter->vdev->dev; + int rc; do { if (adapter->renegotiate) { @@ -664,6 +666,18 @@ static int ibmvnic_login(struct net_device *netdev) dev_err(dev, "Capabilities query timeout\n"); return -1; } + rc = init_sub_crqs(adapter); + if (rc) { + dev_err(dev, + "Initialization of SCRQ's failed\n"); + return -1; + } + rc = init_sub_crq_irqs(adapter); + if (rc) { + dev_err(dev, + "Initialization of SCRQ's irqs failed\n"); + return -1; + } } reinit_completion(&adapter->init_done); @@ -3004,7 +3018,6 @@ static void handle_request_cap_rsp(union ibmvnic_crq *crq, *req_value, (long int)be64_to_cpu(crq->request_capability_rsp. number), name); - release_sub_crqs(adapter); *req_value = be64_to_cpu(crq->request_capability_rsp.number); ibmvnic_send_req_caps(adapter, 1); return; diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index b936febc315a..2194960d5855 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -1113,6 +1113,8 @@ int i40e_setup_tx_descriptors(struct i40e_ring *tx_ring) if (!tx_ring->tx_bi) goto err; + u64_stats_init(&tx_ring->syncp); + /* round up to nearest 4K */ tx_ring->size = tx_ring->count * sizeof(struct i40e_tx_desc); /* add u32 for head writeback, align after this takes care of diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 084c53582793..032f8ac06357 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -2988,6 +2988,8 @@ int ixgbevf_setup_tx_resources(struct ixgbevf_ring *tx_ring) if (!tx_ring->tx_buffer_info) goto err; + u64_stats_init(&tx_ring->syncp); + /* round up to nearest 4K */ tx_ring->size = tx_ring->count * sizeof(union ixgbe_adv_tx_desc); tx_ring->size = ALIGN(tx_ring->size, 4096); @@ -3046,6 +3048,8 @@ int ixgbevf_setup_rx_resources(struct ixgbevf_ring *rx_ring) if (!rx_ring->rx_buffer_info) goto err; + u64_stats_init(&rx_ring->syncp); + /* Round up to nearest 4K */ rx_ring->size = rx_ring->count * sizeof(union ixgbe_adv_rx_desc); rx_ring->size = ALIGN(rx_ring->size, 4096); diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 5794d98d946f..9c94ea9b2b80 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2734,7 +2734,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev, ppd.shared = pdev; memset(&res, 0, sizeof(res)); - if (!of_irq_to_resource(pnp, 0, &res)) { + if (of_irq_to_resource(pnp, 0, &res) <= 0) { dev_err(&pdev->dev, "missing interrupt on %s\n", pnp->name); return -EINVAL; } diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index b3d0c2e6347a..e588a0cdb074 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -22,6 +22,7 @@ #include <linux/if_vlan.h> #include <linux/reset.h> #include <linux/tcp.h> +#include <linux/interrupt.h> #include "mtk_eth_soc.h" @@ -947,6 +948,10 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, RX_DMA_FPORT_MASK; mac--; + if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT || + !eth->netdev[mac])) + goto release_desc; + netdev = eth->netdev[mac]; if (unlikely(test_bit(MTK_RESETTING, ð->state))) diff --git a/drivers/net/ethernet/mellanox/mlx4/alloc.c b/drivers/net/ethernet/mellanox/mlx4/alloc.c index 249a4584401a..b651c1210555 100644 --- a/drivers/net/ethernet/mellanox/mlx4/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx4/alloc.c @@ -283,7 +283,7 @@ int mlx4_zone_add_one(struct mlx4_zone_allocator *zone_alloc, } /* Should be called under a lock */ -static int __mlx4_zone_remove_one_entry(struct mlx4_zone_entry *entry) +static void __mlx4_zone_remove_one_entry(struct mlx4_zone_entry *entry) { struct mlx4_zone_allocator *zone_alloc = entry->allocator; @@ -315,8 +315,6 @@ static int __mlx4_zone_remove_one_entry(struct mlx4_zone_entry *entry) } zone_alloc->mask = mask; } - - return 0; } void mlx4_zone_allocator_destroy(struct mlx4_zone_allocator *zone_alloc) @@ -457,7 +455,7 @@ struct mlx4_bitmap *mlx4_zone_get_bitmap(struct mlx4_zone_allocator *zones, u32 int mlx4_zone_remove_one(struct mlx4_zone_allocator *zones, u32 uid) { struct mlx4_zone_entry *zone; - int res; + int res = 0; spin_lock(&zones->lock); @@ -468,7 +466,7 @@ int mlx4_zone_remove_one(struct mlx4_zone_allocator *zones, u32 uid) goto out; } - res = __mlx4_zone_remove_one_entry(zone); + __mlx4_zone_remove_one_entry(zone); out: spin_unlock(&zones->lock); @@ -578,7 +576,7 @@ out: } static int mlx4_buf_direct_alloc(struct mlx4_dev *dev, int size, - struct mlx4_buf *buf, gfp_t gfp) + struct mlx4_buf *buf) { dma_addr_t t; @@ -587,7 +585,7 @@ static int mlx4_buf_direct_alloc(struct mlx4_dev *dev, int size, buf->page_shift = get_order(size) + PAGE_SHIFT; buf->direct.buf = dma_zalloc_coherent(&dev->persist->pdev->dev, - size, &t, gfp); + size, &t, GFP_KERNEL); if (!buf->direct.buf) return -ENOMEM; @@ -607,10 +605,10 @@ static int mlx4_buf_direct_alloc(struct mlx4_dev *dev, int size, * multiple pages, so we don't require too much contiguous memory. */ int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct, - struct mlx4_buf *buf, gfp_t gfp) + struct mlx4_buf *buf) { if (size <= max_direct) { - return mlx4_buf_direct_alloc(dev, size, buf, gfp); + return mlx4_buf_direct_alloc(dev, size, buf); } else { dma_addr_t t; int i; @@ -620,14 +618,14 @@ int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct, buf->npages = buf->nbufs; buf->page_shift = PAGE_SHIFT; buf->page_list = kcalloc(buf->nbufs, sizeof(*buf->page_list), - gfp); + GFP_KERNEL); if (!buf->page_list) return -ENOMEM; for (i = 0; i < buf->nbufs; ++i) { buf->page_list[i].buf = dma_zalloc_coherent(&dev->persist->pdev->dev, - PAGE_SIZE, &t, gfp); + PAGE_SIZE, &t, GFP_KERNEL); if (!buf->page_list[i].buf) goto err_free; @@ -663,12 +661,11 @@ void mlx4_buf_free(struct mlx4_dev *dev, int size, struct mlx4_buf *buf) } EXPORT_SYMBOL_GPL(mlx4_buf_free); -static struct mlx4_db_pgdir *mlx4_alloc_db_pgdir(struct device *dma_device, - gfp_t gfp) +static struct mlx4_db_pgdir *mlx4_alloc_db_pgdir(struct device *dma_device) { struct mlx4_db_pgdir *pgdir; - pgdir = kzalloc(sizeof *pgdir, gfp); + pgdir = kzalloc(sizeof(*pgdir), GFP_KERNEL); if (!pgdir) return NULL; @@ -676,7 +673,7 @@ static struct mlx4_db_pgdir *mlx4_alloc_db_pgdir(struct device *dma_device, pgdir->bits[0] = pgdir->order0; pgdir->bits[1] = pgdir->order1; pgdir->db_page = dma_alloc_coherent(dma_device, PAGE_SIZE, - &pgdir->db_dma, gfp); + &pgdir->db_dma, GFP_KERNEL); if (!pgdir->db_page) { kfree(pgdir); return NULL; @@ -716,7 +713,7 @@ found: return 0; } -int mlx4_db_alloc(struct mlx4_dev *dev, struct mlx4_db *db, int order, gfp_t gfp) +int mlx4_db_alloc(struct mlx4_dev *dev, struct mlx4_db *db, int order) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_db_pgdir *pgdir; @@ -728,7 +725,7 @@ int mlx4_db_alloc(struct mlx4_dev *dev, struct mlx4_db *db, int order, gfp_t gfp if (!mlx4_alloc_db_from_pgdir(pgdir, db, order)) goto out; - pgdir = mlx4_alloc_db_pgdir(&dev->persist->pdev->dev, gfp); + pgdir = mlx4_alloc_db_pgdir(&dev->persist->pdev->dev); if (!pgdir) { ret = -ENOMEM; goto out; @@ -780,13 +777,13 @@ int mlx4_alloc_hwq_res(struct mlx4_dev *dev, struct mlx4_hwq_resources *wqres, { int err; - err = mlx4_db_alloc(dev, &wqres->db, 1, GFP_KERNEL); + err = mlx4_db_alloc(dev, &wqres->db, 1); if (err) return err; *wqres->db.db = 0; - err = mlx4_buf_direct_alloc(dev, size, &wqres->buf, GFP_KERNEL); + err = mlx4_buf_direct_alloc(dev, size, &wqres->buf); if (err) goto err_db; @@ -795,7 +792,7 @@ int mlx4_alloc_hwq_res(struct mlx4_dev *dev, struct mlx4_hwq_resources *wqres, if (err) goto err_buf; - err = mlx4_buf_write_mtt(dev, &wqres->mtt, &wqres->buf, GFP_KERNEL); + err = mlx4_buf_write_mtt(dev, &wqres->mtt, &wqres->buf); if (err) goto err_mtt; diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index fa6d2354a0e9..c56a511b918e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -224,11 +224,11 @@ int __mlx4_cq_alloc_icm(struct mlx4_dev *dev, int *cqn) if (*cqn == -1) return -ENOMEM; - err = mlx4_table_get(dev, &cq_table->table, *cqn, GFP_KERNEL); + err = mlx4_table_get(dev, &cq_table->table, *cqn); if (err) goto err_out; - err = mlx4_table_get(dev, &cq_table->cmpt_table, *cqn, GFP_KERNEL); + err = mlx4_table_get(dev, &cq_table->cmpt_table, *cqn); if (err) goto err_put; return 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index c751a1d434ad..3d4e4a5d00d1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -223,6 +223,7 @@ static void mlx4_en_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct mlx4_en_priv *priv = netdev_priv(netdev); + struct mlx4_caps *caps = &priv->mdev->dev->caps; int err = 0; u64 config = 0; u64 mask; @@ -235,24 +236,24 @@ static void mlx4_en_get_wol(struct net_device *netdev, mask = (priv->port == 1) ? MLX4_DEV_CAP_FLAG_WOL_PORT1 : MLX4_DEV_CAP_FLAG_WOL_PORT2; - if (!(priv->mdev->dev->caps.flags & mask)) { + if (!(caps->flags & mask)) { wol->supported = 0; wol->wolopts = 0; return; } + if (caps->wol_port[priv->port]) + wol->supported = WAKE_MAGIC; + else + wol->supported = 0; + err = mlx4_wol_read(priv->mdev->dev, &config, priv->port); if (err) { en_err(priv, "Failed to get WoL information\n"); return; } - if (config & MLX4_EN_WOL_MAGIC) - wol->supported = WAKE_MAGIC; - else - wol->supported = 0; - - if (config & MLX4_EN_WOL_ENABLED) + if ((config & MLX4_EN_WOL_ENABLED) && (config & MLX4_EN_WOL_MAGIC)) wol->wolopts = WAKE_MAGIC; else wol->wolopts = 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index e5fb89505a13..bf1638044a7a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -574,16 +574,21 @@ static inline __wsum get_fixed_vlan_csum(__wsum hw_checksum, * header, the HW adds it. To address that, we are subtracting the pseudo * header checksum from the checksum value provided by the HW. */ -static void get_fixed_ipv4_csum(__wsum hw_checksum, struct sk_buff *skb, - struct iphdr *iph) +static int get_fixed_ipv4_csum(__wsum hw_checksum, struct sk_buff *skb, + struct iphdr *iph) { __u16 length_for_csum = 0; __wsum csum_pseudo_header = 0; + __u8 ipproto = iph->protocol; + + if (unlikely(ipproto == IPPROTO_SCTP)) + return -1; length_for_csum = (be16_to_cpu(iph->tot_len) - (iph->ihl << 2)); csum_pseudo_header = csum_tcpudp_nofold(iph->saddr, iph->daddr, - length_for_csum, iph->protocol, 0); + length_for_csum, ipproto, 0); skb->csum = csum_sub(hw_checksum, csum_pseudo_header); + return 0; } #if IS_ENABLED(CONFIG_IPV6) @@ -594,17 +599,20 @@ static void get_fixed_ipv4_csum(__wsum hw_checksum, struct sk_buff *skb, static int get_fixed_ipv6_csum(__wsum hw_checksum, struct sk_buff *skb, struct ipv6hdr *ipv6h) { + __u8 nexthdr = ipv6h->nexthdr; __wsum csum_pseudo_hdr = 0; - if (unlikely(ipv6h->nexthdr == IPPROTO_FRAGMENT || - ipv6h->nexthdr == IPPROTO_HOPOPTS)) + if (unlikely(nexthdr == IPPROTO_FRAGMENT || + nexthdr == IPPROTO_HOPOPTS || + nexthdr == IPPROTO_SCTP)) return -1; - hw_checksum = csum_add(hw_checksum, (__force __wsum)htons(ipv6h->nexthdr)); + hw_checksum = csum_add(hw_checksum, (__force __wsum)htons(nexthdr)); csum_pseudo_hdr = csum_partial(&ipv6h->saddr, sizeof(ipv6h->saddr) + sizeof(ipv6h->daddr), 0); csum_pseudo_hdr = csum_add(csum_pseudo_hdr, (__force __wsum)ipv6h->payload_len); - csum_pseudo_hdr = csum_add(csum_pseudo_hdr, (__force __wsum)ntohs(ipv6h->nexthdr)); + csum_pseudo_hdr = csum_add(csum_pseudo_hdr, + (__force __wsum)htons(nexthdr)); skb->csum = csum_sub(hw_checksum, csum_pseudo_hdr); skb->csum = csum_add(skb->csum, csum_partial(ipv6h, sizeof(struct ipv6hdr), 0)); @@ -627,11 +635,10 @@ static int check_csum(struct mlx4_cqe *cqe, struct sk_buff *skb, void *va, } if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV4)) - get_fixed_ipv4_csum(hw_checksum, skb, hdr); + return get_fixed_ipv4_csum(hw_checksum, skb, hdr); #if IS_ENABLED(CONFIG_IPV6) - else if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV6)) - if (unlikely(get_fixed_ipv6_csum(hw_checksum, skb, hdr))) - return -1; + if (cqe->status & cpu_to_be16(MLX4_CQE_STATUS_IPV6)) + return get_fixed_ipv6_csum(hw_checksum, skb, hdr); #endif return 0; } @@ -1042,7 +1049,7 @@ static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv, int qpn, if (!context) return -ENOMEM; - err = mlx4_qp_alloc(mdev->dev, qpn, qp, GFP_KERNEL); + err = mlx4_qp_alloc(mdev->dev, qpn, qp); if (err) { en_err(priv, "Failed to allocate qp #%x\n", qpn); goto out; @@ -1086,7 +1093,7 @@ int mlx4_en_create_drop_qp(struct mlx4_en_priv *priv) en_err(priv, "Failed reserving drop qpn\n"); return err; } - err = mlx4_qp_alloc(priv->mdev->dev, qpn, &priv->drop_qp, GFP_KERNEL); + err = mlx4_qp_alloc(priv->mdev->dev, qpn, &priv->drop_qp); if (err) { en_err(priv, "Failed allocating drop qp\n"); mlx4_qp_release_range(priv->mdev->dev, qpn, 1); @@ -1158,8 +1165,7 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) } /* Configure RSS indirection qp */ - err = mlx4_qp_alloc(mdev->dev, priv->base_qpn, rss_map->indir_qp, - GFP_KERNEL); + err = mlx4_qp_alloc(mdev->dev, priv->base_qpn, rss_map->indir_qp); if (err) { en_err(priv, "Failed to allocate RSS indirection QP\n"); goto rss_err; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 4f3a9b27ce4a..73faa3d77921 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -111,7 +111,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, goto err_hwq_res; } - err = mlx4_qp_alloc(mdev->dev, ring->qpn, &ring->sp_qp, GFP_KERNEL); + err = mlx4_qp_alloc(mdev->dev, ring->qpn, &ring->sp_qp); if (err) { en_err(priv, "Failed allocating qp %d\n", ring->qpn); goto err_reserve; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 37e84a59e751..041c0ed65929 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -159,8 +159,9 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [32] = "Loopback source checks support", [33] = "RoCEv2 support", [34] = "DMFS Sniffer support (UC & MC)", - [35] = "QinQ VST mode support", - [36] = "sl to vl mapping table change event support" + [35] = "Diag counters per port", + [36] = "QinQ VST mode support", + [37] = "sl to vl mapping table change event support", }; int i; @@ -764,6 +765,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_CQ_TS_SUPPORT_OFFSET 0x3e #define QUERY_DEV_CAP_MAX_PKEY_OFFSET 0x3f #define QUERY_DEV_CAP_EXT_FLAGS_OFFSET 0x40 +#define QUERY_DEV_CAP_WOL_OFFSET 0x43 #define QUERY_DEV_CAP_FLAGS_OFFSET 0x44 #define QUERY_DEV_CAP_RSVD_UAR_OFFSET 0x48 #define QUERY_DEV_CAP_UAR_SZ_OFFSET 0x49 @@ -920,6 +922,9 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(ext_flags, outbox, QUERY_DEV_CAP_EXT_FLAGS_OFFSET); MLX4_GET(flags, outbox, QUERY_DEV_CAP_FLAGS_OFFSET); dev_cap->flags = flags | (u64)ext_flags << 32; + MLX4_GET(field, outbox, QUERY_DEV_CAP_WOL_OFFSET); + dev_cap->wol_port[1] = !!(field & 0x20); + dev_cap->wol_port[2] = !!(field & 0x40); MLX4_GET(field, outbox, QUERY_DEV_CAP_RSVD_UAR_OFFSET); dev_cap->reserved_uars = field >> 4; MLX4_GET(field, outbox, QUERY_DEV_CAP_UAR_SZ_OFFSET); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index 5343a0599253..b52ba01aa486 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -129,6 +129,7 @@ struct mlx4_dev_cap { u32 dmfs_high_rate_qpn_range; struct mlx4_rate_limit_caps rl_caps; struct mlx4_port_cap port_cap[MLX4_MAX_PORTS + 1]; + bool wol_port[MLX4_MAX_PORTS + 1]; }; struct mlx4_func_cap { diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c index e1f9e7cebf8f..5a7816e7c7b4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.c +++ b/drivers/net/ethernet/mellanox/mlx4/icm.c @@ -251,8 +251,7 @@ int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev) MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); } -int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj, - gfp_t gfp) +int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj) { u32 i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size); @@ -266,7 +265,7 @@ int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj, } table->icm[i] = mlx4_alloc_icm(dev, MLX4_TABLE_CHUNK_SIZE >> PAGE_SHIFT, - (table->lowmem ? gfp : GFP_HIGHUSER) | + (table->lowmem ? GFP_KERNEL : GFP_HIGHUSER) | __GFP_NOWARN, table->coherent); if (!table->icm[i]) { ret = -ENOMEM; @@ -363,7 +362,7 @@ int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 i; for (i = start; i <= end; i += inc) { - err = mlx4_table_get(dev, table, i, GFP_KERNEL); + err = mlx4_table_get(dev, table, i); if (err) goto fail; } diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.h b/drivers/net/ethernet/mellanox/mlx4/icm.h index 0c7364550150..dee67fa39107 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.h +++ b/drivers/net/ethernet/mellanox/mlx4/icm.h @@ -71,8 +71,7 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, gfp_t gfp_mask, int coherent); void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent); -int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj, - gfp_t gfp); +int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj); void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj); int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 start, u32 end); diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index a27c9c13a36e..5fe5cdc51357 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -424,13 +424,15 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev->caps.stat_rate_support = dev_cap->stat_rate_support; dev->caps.max_gso_sz = dev_cap->max_gso_sz; dev->caps.max_rss_tbl_sz = dev_cap->max_rss_tbl_sz; + dev->caps.wol_port[1] = dev_cap->wol_port[1]; + dev->caps.wol_port[2] = dev_cap->wol_port[2]; /* Save uar page shift */ if (!mlx4_is_slave(dev)) { /* Virtual PCI function needs to determine UAR page size from * firmware. Only master PCI function can set the uar page size */ - if (enable_4k_uar) + if (enable_4k_uar || !dev->persist->num_vfs) dev->uar_page_shift = DEFAULT_UAR_PAGE_SHIFT; else dev->uar_page_shift = PAGE_SHIFT; @@ -2275,7 +2277,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev) dev->caps.max_fmr_maps = (1 << (32 - ilog2(dev->caps.num_mpts))) - 1; - if (enable_4k_uar) { + if (enable_4k_uar || !dev->persist->num_vfs) { init_hca.log_uar_sz = ilog2(dev->caps.num_uars) + PAGE_SHIFT - DEFAULT_UAR_PAGE_SHIFT; init_hca.uar_page_sz = DEFAULT_UAR_PAGE_SHIFT - 12; diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 30616cd0140d..706d7f21ac5c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -969,7 +969,7 @@ void mlx4_cleanup_cq_table(struct mlx4_dev *dev); void mlx4_cleanup_qp_table(struct mlx4_dev *dev); void mlx4_cleanup_srq_table(struct mlx4_dev *dev); void mlx4_cleanup_mcg_table(struct mlx4_dev *dev); -int __mlx4_qp_alloc_icm(struct mlx4_dev *dev, int qpn, gfp_t gfp); +int __mlx4_qp_alloc_icm(struct mlx4_dev *dev, int qpn); void __mlx4_qp_free_icm(struct mlx4_dev *dev, int qpn); int __mlx4_cq_alloc_icm(struct mlx4_dev *dev, int *cqn); void __mlx4_cq_free_icm(struct mlx4_dev *dev, int cqn); @@ -977,7 +977,7 @@ int __mlx4_srq_alloc_icm(struct mlx4_dev *dev, int *srqn); void __mlx4_srq_free_icm(struct mlx4_dev *dev, int srqn); int __mlx4_mpt_reserve(struct mlx4_dev *dev); void __mlx4_mpt_release(struct mlx4_dev *dev, u32 index); -int __mlx4_mpt_alloc_icm(struct mlx4_dev *dev, u32 index, gfp_t gfp); +int __mlx4_mpt_alloc_icm(struct mlx4_dev *dev, u32 index); void __mlx4_mpt_free_icm(struct mlx4_dev *dev, u32 index); u32 __mlx4_alloc_mtt_range(struct mlx4_dev *dev, int order); void __mlx4_free_mtt_range(struct mlx4_dev *dev, u32 first_seg, int order); diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index ce852ca22a96..24282cd017d3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -479,14 +479,14 @@ static void mlx4_mpt_release(struct mlx4_dev *dev, u32 index) __mlx4_mpt_release(dev, index); } -int __mlx4_mpt_alloc_icm(struct mlx4_dev *dev, u32 index, gfp_t gfp) +int __mlx4_mpt_alloc_icm(struct mlx4_dev *dev, u32 index) { struct mlx4_mr_table *mr_table = &mlx4_priv(dev)->mr_table; - return mlx4_table_get(dev, &mr_table->dmpt_table, index, gfp); + return mlx4_table_get(dev, &mr_table->dmpt_table, index); } -static int mlx4_mpt_alloc_icm(struct mlx4_dev *dev, u32 index, gfp_t gfp) +static int mlx4_mpt_alloc_icm(struct mlx4_dev *dev, u32 index) { u64 param = 0; @@ -497,7 +497,7 @@ static int mlx4_mpt_alloc_icm(struct mlx4_dev *dev, u32 index, gfp_t gfp) MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); } - return __mlx4_mpt_alloc_icm(dev, index, gfp); + return __mlx4_mpt_alloc_icm(dev, index); } void __mlx4_mpt_free_icm(struct mlx4_dev *dev, u32 index) @@ -629,7 +629,7 @@ int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr) struct mlx4_mpt_entry *mpt_entry; int err; - err = mlx4_mpt_alloc_icm(dev, key_to_hw_index(mr->key), GFP_KERNEL); + err = mlx4_mpt_alloc_icm(dev, key_to_hw_index(mr->key)); if (err) return err; @@ -787,14 +787,13 @@ int mlx4_write_mtt(struct mlx4_dev *dev, struct mlx4_mtt *mtt, EXPORT_SYMBOL_GPL(mlx4_write_mtt); int mlx4_buf_write_mtt(struct mlx4_dev *dev, struct mlx4_mtt *mtt, - struct mlx4_buf *buf, gfp_t gfp) + struct mlx4_buf *buf) { u64 *page_list; int err; int i; - page_list = kmalloc(buf->npages * sizeof *page_list, - gfp); + page_list = kcalloc(buf->npages, sizeof(*page_list), GFP_KERNEL); if (!page_list) return -ENOMEM; @@ -841,7 +840,7 @@ int mlx4_mw_enable(struct mlx4_dev *dev, struct mlx4_mw *mw) struct mlx4_mpt_entry *mpt_entry; int err; - err = mlx4_mpt_alloc_icm(dev, key_to_hw_index(mw->key), GFP_KERNEL); + err = mlx4_mpt_alloc_icm(dev, key_to_hw_index(mw->key)); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 5a310d313e94..26747212526b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -301,29 +301,29 @@ void mlx4_qp_release_range(struct mlx4_dev *dev, int base_qpn, int cnt) } EXPORT_SYMBOL_GPL(mlx4_qp_release_range); -int __mlx4_qp_alloc_icm(struct mlx4_dev *dev, int qpn, gfp_t gfp) +int __mlx4_qp_alloc_icm(struct mlx4_dev *dev, int qpn) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_qp_table *qp_table = &priv->qp_table; int err; - err = mlx4_table_get(dev, &qp_table->qp_table, qpn, gfp); + err = mlx4_table_get(dev, &qp_table->qp_table, qpn); if (err) goto err_out; - err = mlx4_table_get(dev, &qp_table->auxc_table, qpn, gfp); + err = mlx4_table_get(dev, &qp_table->auxc_table, qpn); if (err) goto err_put_qp; - err = mlx4_table_get(dev, &qp_table->altc_table, qpn, gfp); + err = mlx4_table_get(dev, &qp_table->altc_table, qpn); if (err) goto err_put_auxc; - err = mlx4_table_get(dev, &qp_table->rdmarc_table, qpn, gfp); + err = mlx4_table_get(dev, &qp_table->rdmarc_table, qpn); if (err) goto err_put_altc; - err = mlx4_table_get(dev, &qp_table->cmpt_table, qpn, gfp); + err = mlx4_table_get(dev, &qp_table->cmpt_table, qpn); if (err) goto err_put_rdmarc; @@ -345,7 +345,7 @@ err_out: return err; } -static int mlx4_qp_alloc_icm(struct mlx4_dev *dev, int qpn, gfp_t gfp) +static int mlx4_qp_alloc_icm(struct mlx4_dev *dev, int qpn) { u64 param = 0; @@ -355,7 +355,7 @@ static int mlx4_qp_alloc_icm(struct mlx4_dev *dev, int qpn, gfp_t gfp) MLX4_CMD_ALLOC_RES, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); } - return __mlx4_qp_alloc_icm(dev, qpn, gfp); + return __mlx4_qp_alloc_icm(dev, qpn); } void __mlx4_qp_free_icm(struct mlx4_dev *dev, int qpn) @@ -397,7 +397,7 @@ struct mlx4_qp *mlx4_qp_lookup(struct mlx4_dev *dev, u32 qpn) return qp; } -int mlx4_qp_alloc(struct mlx4_dev *dev, int qpn, struct mlx4_qp *qp, gfp_t gfp) +int mlx4_qp_alloc(struct mlx4_dev *dev, int qpn, struct mlx4_qp *qp) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_qp_table *qp_table = &priv->qp_table; @@ -408,7 +408,7 @@ int mlx4_qp_alloc(struct mlx4_dev *dev, int qpn, struct mlx4_qp *qp, gfp_t gfp) qp->qpn = qpn; - err = mlx4_qp_alloc_icm(dev, qpn, gfp); + err = mlx4_qp_alloc_icm(dev, qpn); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 812783865205..215e21c3dc8a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -1822,7 +1822,7 @@ static int qp_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, return err; if (!fw_reserved(dev, qpn)) { - err = __mlx4_qp_alloc_icm(dev, qpn, GFP_KERNEL); + err = __mlx4_qp_alloc_icm(dev, qpn); if (err) { res_abort_move(dev, slave, RES_QP, qpn); return err; @@ -1909,7 +1909,7 @@ static int mpt_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, if (err) return err; - err = __mlx4_mpt_alloc_icm(dev, mpt->key, GFP_KERNEL); + err = __mlx4_mpt_alloc_icm(dev, mpt->key); if (err) { res_abort_move(dev, slave, RES_MPT, id); return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c index f44d089e2ca6..bedf52126824 100644 --- a/drivers/net/ethernet/mellanox/mlx4/srq.c +++ b/drivers/net/ethernet/mellanox/mlx4/srq.c @@ -100,11 +100,11 @@ int __mlx4_srq_alloc_icm(struct mlx4_dev *dev, int *srqn) if (*srqn == -1) return -ENOMEM; - err = mlx4_table_get(dev, &srq_table->table, *srqn, GFP_KERNEL); + err = mlx4_table_get(dev, &srq_table->table, *srqn); if (err) goto err_out; - err = mlx4_table_get(dev, &srq_table->cmpt_table, *srqn, GFP_KERNEL); + err = mlx4_table_get(dev, &srq_table->cmpt_table, *srqn); if (err) goto err_put; return 0; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index f5a2c605749f..31cbe5e86a01 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -786,6 +786,10 @@ static void cb_timeout_handler(struct work_struct *work) mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true); } +static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg); +static void mlx5_free_cmd_msg(struct mlx5_core_dev *dev, + struct mlx5_cmd_msg *msg); + static void cmd_work_handler(struct work_struct *work) { struct mlx5_cmd_work_ent *ent = container_of(work, struct mlx5_cmd_work_ent, work); @@ -796,17 +800,28 @@ static void cmd_work_handler(struct work_struct *work) struct semaphore *sem; unsigned long flags; bool poll_cmd = ent->polling; + int alloc_ret; sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; down(sem); if (!ent->page_queue) { - ent->idx = alloc_ent(cmd); - if (ent->idx < 0) { + alloc_ret = alloc_ent(cmd); + if (alloc_ret < 0) { mlx5_core_err(dev, "failed to allocate command entry\n"); + if (ent->callback) { + ent->callback(-EAGAIN, ent->context); + mlx5_free_cmd_msg(dev, ent->out); + free_msg(dev, ent->in); + free_cmd(ent); + } else { + ent->ret = -EAGAIN; + complete(&ent->done); + } up(sem); return; } + ent->idx = alloc_ret; } else { ent->idx = cmd->max_reg_cmds; spin_lock_irqsave(&cmd->alloc_lock, flags); @@ -967,7 +982,7 @@ static int mlx5_cmd_invoke(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *in, err = wait_func(dev, ent); if (err == -ETIMEDOUT) - goto out_free; + goto out; ds = ent->ts2 - ent->ts1; op = MLX5_GET(mbox_in, in->first.data, opcode); @@ -1430,6 +1445,7 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced) mlx5_core_err(dev, "Command completion arrived after timeout (entry idx = %d).\n", ent->idx); free_ent(cmd, ent->idx); + free_cmd(ent); } continue; } @@ -1488,7 +1504,8 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced) free_msg(dev, ent->in); err = err ? err : ent->status; - free_cmd(ent); + if (!forced) + free_cmd(ent); callback(err, context); } else { complete(&ent->done); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index e1b7ddfecd01..0039b4725405 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -266,6 +266,14 @@ 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; @@ -277,7 +285,7 @@ struct mlx5e_tstamp { struct mlx5_core_dev *mdev; struct ptp_clock *ptp; struct ptp_clock_info ptp_info; - u8 *pps_pin_caps; + struct mlx5e_pps pps_info; }; enum { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c index 66f432385dbb..84dd63e74041 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_clock.c @@ -53,6 +53,15 @@ enum { MLX5E_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), +}; + void mlx5e_fill_hwstamp(struct mlx5e_tstamp *tstamp, u64 timestamp, struct skb_shared_hwtstamps *hwts) { @@ -73,17 +82,46 @@ static u64 mlx5e_read_internal_timer(const struct cyclecounter *cc) return mlx5_read_internal_timer(tstamp->mdev) & cc->mask; } +static void mlx5e_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); + u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; + unsigned long flags; + int i; + + for (i = 0; i < tstamp->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); + 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)); + } +} + static void mlx5e_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); unsigned long flags; write_lock_irqsave(&tstamp->lock, flags); timecounter_read(&tstamp->clock); write_unlock_irqrestore(&tstamp->lock, flags); - schedule_delayed_work(&tstamp->overflow_work, tstamp->overflow_period); + 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) @@ -213,18 +251,6 @@ static int mlx5e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 delta) int neg_adj = 0; struct mlx5e_tstamp *tstamp = container_of(ptp, struct mlx5e_tstamp, ptp_info); - struct mlx5e_priv *priv = - container_of(tstamp, struct mlx5e_priv, tstamp); - - if (MLX5_CAP_GEN(priv->mdev, pps_modify)) { - u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; - - /* For future use need to add a loop for finding all 1PPS out pins */ - MLX5_SET(mtpps_reg, in, pin_mode, MLX5E_PIN_MODE_OUT); - MLX5_SET(mtpps_reg, in, out_periodic_adjustment, delta & 0xFFFF); - - mlx5_set_mtpps(priv->mdev, in, sizeof(in)); - } if (delta < 0) { neg_adj = 1; @@ -253,12 +279,13 @@ static int mlx5e_extts_configure(struct ptp_clock_info *ptp, struct mlx5e_priv *priv = container_of(tstamp, struct mlx5e_priv, tstamp); u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; + u32 field_select = 0; + u8 pin_mode = 0; u8 pattern = 0; int pin = -1; int err = 0; - if (!MLX5_CAP_GEN(priv->mdev, pps) || - !MLX5_CAP_GEN(priv->mdev, pps_modify)) + if (!MLX5_PPS_CAP(priv->mdev)) return -EOPNOTSUPP; if (rq->extts.index >= tstamp->ptp_info.n_pins) @@ -268,15 +295,21 @@ static int mlx5e_extts_configure(struct ptp_clock_info *ptp, pin = ptp_find_pin(tstamp->ptp, PTP_PF_EXTTS, rq->extts.index); if (pin < 0) return -EBUSY; + pin_mode = MLX5E_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; + } else { + pin = rq->extts.index; + field_select = MLX5E_MTPPS_FS_ENABLE; } - if (rq->extts.flags & PTP_FALLING_EDGE) - pattern = 1; - MLX5_SET(mtpps_reg, in, pin, pin); - MLX5_SET(mtpps_reg, in, pin_mode, MLX5E_PIN_MODE_IN); + MLX5_SET(mtpps_reg, in, pin_mode, pin_mode); MLX5_SET(mtpps_reg, in, pattern, pattern); 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)); if (err) @@ -295,14 +328,18 @@ static int mlx5e_perout_configure(struct ptp_clock_info *ptp, struct mlx5e_priv *priv = container_of(tstamp, struct mlx5e_priv, tstamp); u32 in[MLX5_ST_SZ_DW(mtpps_reg)] = {0}; - u64 nsec_now, nsec_delta, time_stamp; + u64 nsec_now, nsec_delta, time_stamp = 0; u64 cycles_now, cycles_delta; struct timespec64 ts; unsigned long flags; + u32 field_select = 0; + u8 pin_mode = 0; + u8 pattern = 0; int pin = -1; + int err = 0; s64 ns; - if (!MLX5_CAP_GEN(priv->mdev, pps_modify)) + if (!MLX5_PPS_CAP(priv->mdev)) return -EOPNOTSUPP; if (rq->perout.index >= tstamp->ptp_info.n_pins) @@ -313,32 +350,60 @@ static int mlx5e_perout_configure(struct ptp_clock_info *ptp, rq->perout.index); if (pin < 0) return -EBUSY; - } - ts.tv_sec = rq->perout.period.sec; - ts.tv_nsec = rq->perout.period.nsec; - ns = timespec64_to_ns(&ts); - if (on) + pin_mode = MLX5E_PIN_MODE_OUT; + pattern = MLX5E_OUT_PATTERN_PERIODIC; + ts.tv_sec = rq->perout.period.sec; + ts.tv_nsec = rq->perout.period.nsec; + ns = timespec64_to_ns(&ts); + if ((ns >> 1) != 500000000LL) return -EINVAL; - 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); - nsec_delta = ns - nsec_now; - cycles_delta = div64_u64(nsec_delta << tstamp->cycles.shift, - tstamp->cycles.mult); - write_unlock_irqrestore(&tstamp->lock, flags); - time_stamp = cycles_now + cycles_delta; + + 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); + nsec_delta = ns - nsec_now; + cycles_delta = div64_u64(nsec_delta << tstamp->cycles.shift, + tstamp->cycles.mult); + write_unlock_irqrestore(&tstamp->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; + } else { + pin = rq->perout.index; + field_select = MLX5E_MTPPS_FS_ENABLE; + } + MLX5_SET(mtpps_reg, in, pin, pin); - MLX5_SET(mtpps_reg, in, pin_mode, MLX5E_PIN_MODE_OUT); - MLX5_SET(mtpps_reg, in, pattern, MLX5E_OUT_PATTERN_PERIODIC); + MLX5_SET(mtpps_reg, in, pin_mode, pin_mode); + MLX5_SET(mtpps_reg, in, pattern, pattern); MLX5_SET(mtpps_reg, in, enable, on); 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)); + if (err) + return err; - return mlx5_set_mtpps(priv->mdev, in, sizeof(in)); + return mlx5_set_mtppse(priv->mdev, pin, 0, + MLX5E_EVENT_MODE_REPETETIVE & on); +} + +static int mlx5e_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); + + tstamp->pps_info.enabled = !!on; + return 0; } static int mlx5e_ptp_enable(struct ptp_clock_info *ptp, @@ -350,6 +415,8 @@ static int mlx5e_ptp_enable(struct ptp_clock_info *ptp, return mlx5e_extts_configure(ptp, rq, on); case PTP_CLK_REQ_PEROUT: return mlx5e_perout_configure(ptp, rq, on); + case PTP_CLK_REQ_PPS: + return mlx5e_pps_configure(ptp, rq, on); default: return -EOPNOTSUPP; } @@ -395,6 +462,7 @@ static int mlx5e_init_pin_config(struct mlx5e_tstamp *tstamp) return -ENOMEM; tstamp->ptp_info.enable = mlx5e_ptp_enable; tstamp->ptp_info.verify = mlx5e_ptp_verify; + tstamp->ptp_info.pps = 1; for (i = 0; i < tstamp->ptp_info.n_pins; i++) { snprintf(tstamp->ptp_info.pin_config[i].name, @@ -422,22 +490,56 @@ static void mlx5e_get_pps_caps(struct mlx5e_priv *priv, tstamp->ptp_info.n_per_out = MLX5_GET(mtpps_reg, out, cap_max_num_of_pps_out_pins); - tstamp->pps_pin_caps[0] = MLX5_GET(mtpps_reg, out, cap_pin_0_mode); - tstamp->pps_pin_caps[1] = MLX5_GET(mtpps_reg, out, cap_pin_1_mode); - tstamp->pps_pin_caps[2] = MLX5_GET(mtpps_reg, out, cap_pin_2_mode); - tstamp->pps_pin_caps[3] = MLX5_GET(mtpps_reg, out, cap_pin_3_mode); - tstamp->pps_pin_caps[4] = MLX5_GET(mtpps_reg, out, cap_pin_4_mode); - tstamp->pps_pin_caps[5] = MLX5_GET(mtpps_reg, out, cap_pin_5_mode); - tstamp->pps_pin_caps[6] = MLX5_GET(mtpps_reg, out, cap_pin_6_mode); - tstamp->pps_pin_caps[7] = MLX5_GET(mtpps_reg, out, cap_pin_7_mode); + 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); } void mlx5e_pps_event_handler(struct mlx5e_priv *priv, struct ptp_clock_event *event) { + struct net_device *netdev = priv->netdev; struct mlx5e_tstamp *tstamp = &priv->tstamp; + struct timespec64 ts; + u64 nsec_now, nsec_delta; + u64 cycles_now, cycles_delta; + int pin = event->index; + s64 ns; + unsigned long flags; - ptp_clock_event(tstamp->ptp, event); + switch (tstamp->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); + } else { + event->type = PTP_CLOCK_EXTTS; + } + ptp_clock_event(tstamp->ptp, event); + break; + case PTP_PF_PEROUT: + mlx5e_ptp_gettime(&tstamp->ptp_info, &ts); + cycles_now = mlx5_read_internal_timer(tstamp->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); + 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); + break; + default: + netdev_err(netdev, "%s: Unhandled event\n", __func__); + } } void mlx5e_timestamp_init(struct mlx5e_priv *priv) @@ -473,9 +575,10 @@ void mlx5e_timestamp_init(struct mlx5e_priv *priv) do_div(ns, NSEC_PER_SEC / 2 / HZ); tstamp->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) - schedule_delayed_work(&tstamp->overflow_work, 0); + queue_delayed_work(priv->wq, &tstamp->overflow_work, 0); else mlx5_core_warn(priv->mdev, "invalid overflow period, overflow_work is not scheduled\n"); @@ -484,16 +587,10 @@ void mlx5e_timestamp_init(struct mlx5e_priv *priv) snprintf(tstamp->ptp_info.name, 16, "mlx5 ptp"); /* Initialize 1PPS data structures */ -#define MAX_PIN_NUM 8 - tstamp->pps_pin_caps = kzalloc(sizeof(u8) * MAX_PIN_NUM, GFP_KERNEL); - if (tstamp->pps_pin_caps) { - if (MLX5_CAP_GEN(priv->mdev, pps)) - mlx5e_get_pps_caps(priv, tstamp); - if (tstamp->ptp_info.n_pins) - mlx5e_init_pin_config(tstamp); - } else { - mlx5_core_warn(priv->mdev, "1PPS initialization failed\n"); - } + 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); @@ -516,8 +613,7 @@ void mlx5e_timestamp_cleanup(struct mlx5e_priv *priv) priv->tstamp.ptp = NULL; } - kfree(tstamp->pps_pin_caps); - kfree(tstamp->ptp_info.pin_config); - + cancel_work_sync(&tstamp->pps_info.out_work); cancel_delayed_work_sync(&tstamp->overflow_work); + kfree(tstamp->ptp_info.pin_config); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c index bdd82c9b3992..eafc59280ada 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs_ethtool.c @@ -276,7 +276,7 @@ static void add_rule_to_list(struct mlx5e_priv *priv, static bool outer_header_zero(u32 *match_criteria) { - int size = MLX5_ST_SZ_BYTES(fte_match_param); + int size = MLX5_FLD_SZ_BYTES(fte_match_param, outer_headers); char *outer_headers_c = MLX5_ADDR_OF(fte_match_param, match_criteria, outer_headers); @@ -320,7 +320,7 @@ add_ethtool_flow_rule(struct mlx5e_priv *priv, spec->match_criteria_enable = (!outer_header_zero(spec->match_criteria)); flow_act.flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; - rule = mlx5_add_flow_rules(ft, spec, &flow_act, dst, 1); + rule = mlx5_add_flow_rules(ft, spec, &flow_act, dst, dst ? 1 : 0); if (IS_ERR(rule)) { err = PTR_ERR(rule); netdev_err(priv->netdev, "%s: failed to add ethtool steering rule: %d\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 1eac5003084f..57f31fa478ce 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -377,7 +377,6 @@ static void mlx5e_async_event(struct mlx5_core_dev *mdev, void *vpriv, break; case MLX5_DEV_EVENT_PPS: eqe = (struct mlx5_eqe *)param; - ptp_event.type = PTP_CLOCK_EXTTS; ptp_event.index = eqe->data.pps.pin; ptp_event.timestamp = timecounter_cyc2time(&priv->tstamp.clock, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index af51a5d2b912..52b9a64cd3a2 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -698,7 +698,7 @@ int mlx5_start_eqs(struct mlx5_core_dev *dev) else mlx5_core_dbg(dev, "port_module_event is not set\n"); - if (MLX5_CAP_GEN(dev, pps)) + if (MLX5_PPS_CAP(dev)) async_event_mask |= (1ull << MLX5_EVENT_TYPE_PPS_EVENT); if (MLX5_CAP_GEN(dev, fpga)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 89bfda419efe..8b18cc9ec026 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -1668,7 +1668,8 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) int i; if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH || + esw->mode == SRIOV_NONE) return; esw_info(esw->dev, "disable SRIOV: active vports(%d) mode(%d)\n", diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c index 1ee5bce85901..85298051a3e4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ipoib.c @@ -178,8 +178,6 @@ out: static void mlx5i_destroy_underlay_qp(struct mlx5_core_dev *mdev, struct mlx5_core_qp *qp) { - mlx5_fs_remove_rx_underlay_qpn(mdev, qp->qpn); - mlx5_core_destroy_qp(mdev, qp); } @@ -194,8 +192,6 @@ static int mlx5i_init_tx(struct mlx5e_priv *priv) return err; } - mlx5_fs_add_rx_underlay_qpn(priv->mdev, ipriv->qp.qpn); - err = mlx5e_create_tis(priv->mdev, 0 /* tc */, ipriv->qp.qpn, &priv->tisn[0]); if (err) { mlx5_core_warn(priv->mdev, "create tis failed, %d\n", err); @@ -253,6 +249,7 @@ static void mlx5i_destroy_flow_steering(struct mlx5e_priv *priv) static int mlx5i_init_rx(struct mlx5e_priv *priv) { + struct mlx5i_priv *ipriv = priv->ppriv; int err; err = mlx5e_create_indirect_rqt(priv); @@ -271,12 +268,18 @@ static int mlx5i_init_rx(struct mlx5e_priv *priv) if (err) goto err_destroy_indirect_tirs; - err = mlx5i_create_flow_steering(priv); + err = mlx5_fs_add_rx_underlay_qpn(priv->mdev, ipriv->qp.qpn); if (err) goto err_destroy_direct_tirs; + err = mlx5i_create_flow_steering(priv); + if (err) + goto err_remove_rx_underlay_qpn; + return 0; +err_remove_rx_underlay_qpn: + mlx5_fs_remove_rx_underlay_qpn(priv->mdev, ipriv->qp.qpn); err_destroy_direct_tirs: mlx5e_destroy_direct_tirs(priv); err_destroy_indirect_tirs: @@ -290,6 +293,9 @@ err_destroy_indirect_rqts: static void mlx5i_cleanup_rx(struct mlx5e_priv *priv) { + struct mlx5i_priv *ipriv = priv->ppriv; + + mlx5_fs_remove_rx_underlay_qpn(priv->mdev, ipriv->qp.qpn); mlx5i_destroy_flow_steering(priv); mlx5e_destroy_direct_tirs(priv); mlx5e_destroy_indirect_tirs(priv); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lag.c b/drivers/net/ethernet/mellanox/mlx5/core/lag.c index a3a836bdcfd2..f26f97fe4666 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/lag.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/lag.c @@ -162,22 +162,17 @@ static bool mlx5_lag_is_bonded(struct mlx5_lag *ldev) static void mlx5_infer_tx_affinity_mapping(struct lag_tracker *tracker, u8 *port1, u8 *port2) { - if (tracker->tx_type == NETDEV_LAG_TX_TYPE_ACTIVEBACKUP) { - if (tracker->netdev_state[0].tx_enabled) { - *port1 = 1; - *port2 = 1; - } else { - *port1 = 2; - *port2 = 2; - } - } else { - *port1 = 1; - *port2 = 2; - if (!tracker->netdev_state[0].link_up) - *port1 = 2; - else if (!tracker->netdev_state[1].link_up) - *port2 = 1; + *port1 = 1; + *port2 = 2; + if (!tracker->netdev_state[0].tx_enabled || + !tracker->netdev_state[0].link_up) { + *port1 = 2; + return; } + + if (!tracker->netdev_state[1].tx_enabled || + !tracker->netdev_state[1].link_up) + *port2 = 1; } static void mlx5_activate_lag(struct mlx5_lag *ldev, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 6a3d6bef7dd4..6a263e8d883a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -154,6 +154,11 @@ int mlx5_set_mtpps(struct mlx5_core_dev *mdev, u32 *mtpps, u32 mtpps_size); int mlx5_query_mtppse(struct mlx5_core_dev *mdev, u8 pin, u8 *arm, u8 *mode); int mlx5_set_mtppse(struct mlx5_core_dev *mdev, u8 pin, u8 arm, u8 mode); +#define MLX5_PPS_CAP(mdev) (MLX5_CAP_GEN((mdev), pps) && \ + MLX5_CAP_GEN((mdev), pps_modify) && \ + MLX5_CAP_MCAM_FEATURE((mdev), mtpps_fs) && \ + MLX5_CAP_MCAM_FEATURE((mdev), mtpps_enh_out_per_adj)) + int mlx5_firmware_flash(struct mlx5_core_dev *dev, const struct firmware *fw); void mlx5e_init(void); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c index bcdf7779c48d..bf99d40e30b4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c @@ -88,7 +88,11 @@ static void mlx5_device_disable_sriov(struct mlx5_core_dev *dev) int vf; if (!sriov->enabled_vfs) +#ifdef CONFIG_MLX5_CORE_EN + goto disable_sriov_resources; +#else return; +#endif for (vf = 0; vf < sriov->num_vfs; vf++) { if (!sriov->vfs_ctx[vf].enabled) @@ -103,6 +107,7 @@ static void mlx5_device_disable_sriov(struct mlx5_core_dev *dev) } #ifdef CONFIG_MLX5_CORE_EN +disable_sriov_resources: mlx5_eswitch_disable_sriov(dev->priv.eswitch); #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 383fef5a8e24..4b2e0fd7d51e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1512,6 +1512,10 @@ mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry); +static bool +mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, + const struct mlxsw_sp_fib_entry *fib_entry); + static int mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp) @@ -1520,6 +1524,9 @@ mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp, int err; list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) { + if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node, + fib_entry)) + continue; err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 656b2d3f1bee..5eb1606765c5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -626,8 +626,8 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, orig_dev); - if (WARN_ON(!bridge_port)) - return -EINVAL; + if (!bridge_port) + return 0; err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port, MLXSW_SP_FLOOD_TYPE_UC, @@ -711,8 +711,8 @@ static int mlxsw_sp_port_attr_mc_router_set(struct mlxsw_sp_port *mlxsw_sp_port, bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge, orig_dev); - if (WARN_ON(!bridge_port)) - return -EINVAL; + if (!bridge_port) + return 0; if (!bridge_port->bridge_device->multicast_enabled) return 0; @@ -1283,15 +1283,15 @@ static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, return 0; bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); - if (WARN_ON(!bridge_port)) - return -EINVAL; + if (!bridge_port) + return 0; bridge_device = bridge_port->bridge_device; mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, bridge_device, mdb->vid); - if (WARN_ON(!mlxsw_sp_port_vlan)) - return -EINVAL; + if (!mlxsw_sp_port_vlan) + return 0; fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); @@ -1407,15 +1407,15 @@ static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, int err = 0; bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); - if (WARN_ON(!bridge_port)) - return -EINVAL; + if (!bridge_port) + return 0; bridge_device = bridge_port->bridge_device; mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_bridge(mlxsw_sp_port, bridge_device, mdb->vid); - if (WARN_ON(!mlxsw_sp_port_vlan)) - return -EINVAL; + if (!mlxsw_sp_port_vlan) + return 0; fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); @@ -1974,6 +1974,17 @@ static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp) } +static void mlxsw_sp_mids_fini(struct mlxsw_sp *mlxsw_sp) +{ + struct mlxsw_sp_mid *mid, *tmp; + + list_for_each_entry_safe(mid, tmp, &mlxsw_sp->bridge->mids_list, list) { + list_del(&mid->list); + clear_bit(mid->mid, mlxsw_sp->bridge->mids_bitmap); + kfree(mid); + } +} + int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp) { struct mlxsw_sp_bridge *bridge; @@ -1996,7 +2007,7 @@ int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp) void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp) { mlxsw_sp_fdb_fini(mlxsw_sp); - WARN_ON(!list_empty(&mlxsw_sp->bridge->mids_list)); + mlxsw_sp_mids_fini(mlxsw_sp); WARN_ON(!list_empty(&mlxsw_sp->bridge->bridges_list)); kfree(mlxsw_sp->bridge); } diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index dd7fa9cf225f..b0837b58c3a1 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -115,14 +115,10 @@ nfp_flower_cmsg_portmod_rx(struct nfp_app *app, struct sk_buff *skb) return; } - if (link) { + if (link) netif_carrier_on(netdev); - rtnl_lock(); - dev_set_mtu(netdev, be16_to_cpu(msg->mtu)); - rtnl_unlock(); - } else { + else netif_carrier_off(netdev); - } rcu_read_unlock(); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 18750ff0ede6..9f77ce038a4a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -513,6 +513,7 @@ nfp_net_tx_ring_init(struct nfp_net_tx_ring *tx_ring, tx_ring->idx = idx; tx_ring->r_vec = r_vec; tx_ring->is_xdp = is_xdp; + u64_stats_init(&tx_ring->r_vec->tx_sync); tx_ring->qcidx = tx_ring->idx * nn->stride_tx; tx_ring->qcp_q = nn->tx_bar + NFP_QCP_QUEUE_OFF(tx_ring->qcidx); @@ -532,6 +533,7 @@ nfp_net_rx_ring_init(struct nfp_net_rx_ring *rx_ring, rx_ring->idx = idx; rx_ring->r_vec = r_vec; + u64_stats_init(&rx_ring->r_vec->rx_sync); rx_ring->fl_qcidx = rx_ring->idx * nn->stride_rx; rx_ring->qcp_fl = nn->rx_bar + NFP_QCP_QUEUE_OFF(rx_ring->fl_qcidx); @@ -906,8 +908,7 @@ static int nfp_net_tx(struct sk_buff *skb, struct net_device *netdev) return NETDEV_TX_OK; err_unmap: - --f; - while (f >= 0) { + while (--f >= 0) { frag = &skb_shinfo(skb)->frags[f]; dma_unmap_page(dp->dev, tx_ring->txbufs[wr_idx].dma_addr, skb_frag_size(frag), DMA_TO_DEVICE); diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c index 66ff15d08bad..0a66389c06c2 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c @@ -2311,7 +2311,7 @@ netxen_md_rdqueue(struct netxen_adapter *adapter, loop_cnt++) { NX_WR_DUMP_REG(select_addr, adapter->ahw.pci_base0, queue_id); read_addr = queueEntry->read_addr; - for (k = 0; k < read_cnt; k--) { + for (k = 0; k < read_cnt; k++) { NX_RD_DUMP_REG(read_addr, adapter->ahw.pci_base0, &read_value); *data_buff++ = read_value; diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 9da91045d167..3eb241657368 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -253,7 +253,7 @@ int qed_mcp_cmd_init(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) size = MFW_DRV_MSG_MAX_DWORDS(p_info->mfw_mb_length) * sizeof(u32); p_info->mfw_mb_cur = kzalloc(size, GFP_KERNEL); p_info->mfw_mb_shadow = kzalloc(size, GFP_KERNEL); - if (!p_info->mfw_mb_shadow || !p_info->mfw_mb_addr) + if (!p_info->mfw_mb_cur || !p_info->mfw_mb_shadow) goto err; return 0; diff --git a/drivers/net/ethernet/qualcomm/emac/emac.c b/drivers/net/ethernet/qualcomm/emac/emac.c index 746d94e28470..60850bfa3d32 100644 --- a/drivers/net/ethernet/qualcomm/emac/emac.c +++ b/drivers/net/ethernet/qualcomm/emac/emac.c @@ -766,11 +766,13 @@ static void emac_shutdown(struct platform_device *pdev) struct emac_adapter *adpt = netdev_priv(netdev); struct emac_sgmii *sgmii = &adpt->phy; - /* Closing the SGMII turns off its interrupts */ - sgmii->close(adpt); + if (netdev->flags & IFF_UP) { + /* Closing the SGMII turns off its interrupts */ + sgmii->close(adpt); - /* Resetting the MAC turns off all DMA and its interrupts */ - emac_mac_reset(adpt); + /* Resetting the MAC turns off all DMA and its interrupts */ + emac_mac_reset(adpt); + } } static struct platform_driver emac_platform_driver = { diff --git a/drivers/net/ethernet/sfc/mcdi_port.c b/drivers/net/ethernet/sfc/mcdi_port.c index c905971c5f3a..990a63d7fcb7 100644 --- a/drivers/net/ethernet/sfc/mcdi_port.c +++ b/drivers/net/ethernet/sfc/mcdi_port.c @@ -938,7 +938,6 @@ enum efx_stats_action { static int efx_mcdi_mac_stats(struct efx_nic *efx, enum efx_stats_action action, int clear) { - struct efx_ef10_nic_data *nic_data = efx->nic_data; MCDI_DECLARE_BUF(inbuf, MC_CMD_MAC_STATS_IN_LEN); int rc; int change = action == EFX_STATS_PULL ? 0 : 1; @@ -960,7 +959,12 @@ static int efx_mcdi_mac_stats(struct efx_nic *efx, MAC_STATS_IN_PERIODIC_NOEVENT, 1, MAC_STATS_IN_PERIOD_MS, period); MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_LEN, dma_len); - MCDI_SET_DWORD(inbuf, MAC_STATS_IN_PORT_ID, nic_data->vport_id); + + if (efx_nic_rev(efx) >= EFX_REV_HUNT_A0) { + struct efx_ef10_nic_data *nic_data = efx->nic_data; + + MCDI_SET_DWORD(inbuf, MAC_STATS_IN_PORT_ID, nic_data->vport_id); + } rc = efx_mcdi_rpc_quiet(efx, MC_CMD_MAC_STATS, inbuf, sizeof(inbuf), NULL, 0, NULL); diff --git a/drivers/net/ethernet/sgi/ioc3-eth.c b/drivers/net/ethernet/sgi/ioc3-eth.c index b607936e1b3e..9c0488e0f08e 100644 --- a/drivers/net/ethernet/sgi/ioc3-eth.c +++ b/drivers/net/ethernet/sgi/ioc3-eth.c @@ -90,17 +90,13 @@ struct ioc3_private { spinlock_t ioc3_lock; struct mii_if_info mii; + struct net_device *dev; struct pci_dev *pdev; /* Members used by autonegotiation */ struct timer_list ioc3_timer; }; -static inline struct net_device *priv_netdev(struct ioc3_private *dev) -{ - return (void *)dev - ((sizeof(struct net_device) + 31) & ~31); -} - static int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static void ioc3_set_multicast_list(struct net_device *dev); static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev); @@ -427,7 +423,7 @@ static void ioc3_get_eaddr_nic(struct ioc3_private *ip) nic[i] = nic_read_byte(ioc3); for (i = 2; i < 8; i++) - priv_netdev(ip)->dev_addr[i - 2] = nic[i]; + ip->dev->dev_addr[i - 2] = nic[i]; } /* @@ -439,7 +435,7 @@ static void ioc3_get_eaddr(struct ioc3_private *ip) { ioc3_get_eaddr_nic(ip); - printk("Ethernet address is %pM.\n", priv_netdev(ip)->dev_addr); + printk("Ethernet address is %pM.\n", ip->dev->dev_addr); } static void __ioc3_set_mac_address(struct net_device *dev) @@ -790,13 +786,12 @@ static void ioc3_timer(unsigned long data) */ static int ioc3_mii_init(struct ioc3_private *ip) { - struct net_device *dev = priv_netdev(ip); int i, found = 0, res = 0; int ioc3_phy_workaround = 1; u16 word; for (i = 0; i < 32; i++) { - word = ioc3_mdio_read(dev, i, MII_PHYSID1); + word = ioc3_mdio_read(ip->dev, i, MII_PHYSID1); if (word != 0xffff && word != 0x0000) { found = 1; @@ -1276,6 +1271,7 @@ static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) SET_NETDEV_DEV(dev, &pdev->dev); ip = netdev_priv(dev); + ip->dev = dev; dev->irq = pdev->irq; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index 22cf6353ba04..7ecf549c7f1c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -205,7 +205,7 @@ static void dwmac1000_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space) { int i; - for (i = 0; i < 23; i++) + for (i = 0; i < NUM_DWMAC1000_DMA_REGS; i++) if ((i < 12) || (i > 17)) reg_space[DMA_BUS_MODE / 4 + i] = readl(ioaddr + DMA_BUS_MODE + i * 4); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c index eef2f222ce9a..6502b9aa3bf5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac100_dma.c @@ -70,7 +70,7 @@ static void dwmac100_dump_dma_regs(void __iomem *ioaddr, u32 *reg_space) { int i; - for (i = 0; i < 9; i++) + for (i = 0; i < NUM_DWMAC100_DMA_REGS; i++) reg_space[DMA_BUS_MODE / 4 + i] = readl(ioaddr + DMA_BUS_MODE + i * 4); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c index f233bf8b4ebb..c4407e8e39a3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_core.c @@ -117,7 +117,7 @@ static void dwmac4_tx_queue_routing(struct mac_device_info *hw, void __iomem *ioaddr = hw->pcsr; u32 value; - const struct stmmac_rx_routing route_possibilities[] = { + static const struct stmmac_rx_routing route_possibilities[] = { { GMAC_RXQCTRL_AVCPQ_MASK, GMAC_RXQCTRL_AVCPQ_SHIFT }, { GMAC_RXQCTRL_PTPQ_MASK, GMAC_RXQCTRL_PTPQ_SHIFT }, { GMAC_RXQCTRL_DCBCPQ_MASK, GMAC_RXQCTRL_DCBCPQ_SHIFT }, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index 9091df86723a..adc54006f884 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -136,6 +136,9 @@ #define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ #define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */ +#define NUM_DWMAC100_DMA_REGS 9 +#define NUM_DWMAC1000_DMA_REGS 23 + void dwmac_enable_dma_transmission(void __iomem *ioaddr); void dwmac_enable_dma_irq(void __iomem *ioaddr, u32 chan); void dwmac_disable_dma_irq(void __iomem *ioaddr, u32 chan); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index babb39c646ff..af30b4857c3b 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -33,6 +33,8 @@ #define MAC100_ETHTOOL_NAME "st_mac100" #define GMAC_ETHTOOL_NAME "st_gmac" +#define ETHTOOL_DMA_OFFSET 55 + struct stmmac_stats { char stat_string[ETH_GSTRING_LEN]; int sizeof_stat; @@ -442,6 +444,9 @@ static void stmmac_ethtool_gregs(struct net_device *dev, priv->hw->mac->dump_regs(priv->hw, reg_space); priv->hw->dma->dump_regs(priv->ioaddr, reg_space); + /* Copy DMA registers to where ethtool expects them */ + memcpy(®_space[ETHTOOL_DMA_OFFSET], ®_space[DMA_BUS_MODE / 4], + NUM_DWMAC1000_DMA_REGS * 4); } static void diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 1853f7ff6657..1763e48c84e2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -4120,8 +4120,15 @@ int stmmac_dvr_probe(struct device *device, if ((phyaddr >= 0) && (phyaddr <= 31)) priv->plat->phy_addr = phyaddr; - if (priv->plat->stmmac_rst) + if (priv->plat->stmmac_rst) { + ret = reset_control_assert(priv->plat->stmmac_rst); reset_control_deassert(priv->plat->stmmac_rst); + /* Some reset controllers have only reset callback instead of + * assert + deassert callbacks pair. + */ + if (ret == -ENOTSUPP) + reset_control_reset(priv->plat->stmmac_rst); + } /* Init MAC and get the capabilities */ ret = stmmac_hw_init(priv); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index db157a47000c..72ec711fcba2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -204,6 +204,7 @@ int stmmac_mdio_register(struct net_device *ndev) struct stmmac_priv *priv = netdev_priv(ndev); struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data; struct device_node *mdio_node = priv->plat->mdio_node; + struct device *dev = ndev->dev.parent; int addr, found; if (!mdio_bus_data) @@ -237,7 +238,7 @@ int stmmac_mdio_register(struct net_device *ndev) else err = mdiobus_register(new_bus); if (err != 0) { - netdev_err(ndev, "Cannot register the MDIO bus\n"); + dev_err(dev, "Cannot register the MDIO bus\n"); goto bus_register_fail; } @@ -285,14 +286,12 @@ int stmmac_mdio_register(struct net_device *ndev) irq_str = irq_num; break; } - netdev_info(ndev, "PHY ID %08x at %d IRQ %s (%s)%s\n", - phydev->phy_id, addr, irq_str, phydev_name(phydev), - act ? " active" : ""); + phy_attached_info(phydev); found = 1; } if (!found && !mdio_node) { - netdev_warn(ndev, "No PHY found\n"); + dev_warn(dev, "No PHY found\n"); mdiobus_unregister(new_bus); mdiobus_free(new_bus); return -ENODEV; diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index 46cb7f8955a2..4bb04aaf9650 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -9532,7 +9532,7 @@ static struct niu_parent *niu_get_parent(struct niu *np, p = niu_new_parent(np, id, ptype); if (p) { - char port_name[6]; + char port_name[8]; int err; sprintf(port_name, "port%d", port); @@ -9553,7 +9553,7 @@ static void niu_put_parent(struct niu *np) { struct niu_parent *p = np->parent; u8 port = np->port; - char port_name[6]; + char port_name[8]; BUG_ON(!p || p->ports[port] != np); diff --git a/drivers/net/ethernet/sun/sunhme.h b/drivers/net/ethernet/sun/sunhme.h index 3af540adb3c5..fca1bca7f69d 100644 --- a/drivers/net/ethernet/sun/sunhme.h +++ b/drivers/net/ethernet/sun/sunhme.h @@ -13,9 +13,9 @@ /* Happy Meal global registers. */ #define GREG_SWRESET 0x000UL /* Software Reset */ #define GREG_CFG 0x004UL /* Config Register */ -#define GREG_STAT 0x108UL /* Status */ -#define GREG_IMASK 0x10cUL /* Interrupt Mask */ -#define GREG_REG_SIZE 0x110UL +#define GREG_STAT 0x100UL /* Status */ +#define GREG_IMASK 0x104UL /* Interrupt Mask */ +#define GREG_REG_SIZE 0x108UL /* Global reset register. */ #define GREG_RESET_ETX 0x01 diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index 711fbbbc4b1f..163d8d16bc24 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -654,6 +654,8 @@ static int bdx_ioctl_priv(struct net_device *ndev, struct ifreq *ifr, int cmd) RET(-EFAULT); } DBG("%d 0x%x 0x%x\n", data[0], data[1], data[2]); + } else { + return -EOPNOTSUPP; } if (!capable(CAP_SYS_RAWIO)) diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 1850e348f555..badd0a8caeb9 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -3089,6 +3089,31 @@ static int cpsw_probe(struct platform_device *pdev) cpsw->quirk_irq = true; } + ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + + ndev->netdev_ops = &cpsw_netdev_ops; + ndev->ethtool_ops = &cpsw_ethtool_ops; + netif_napi_add(ndev, &cpsw->napi_rx, cpsw_rx_poll, CPSW_POLL_WEIGHT); + netif_tx_napi_add(ndev, &cpsw->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT); + cpsw_split_res(ndev); + + /* register the network device */ + SET_NETDEV_DEV(ndev, &pdev->dev); + ret = register_netdev(ndev); + if (ret) { + dev_err(priv->dev, "error registering net device\n"); + ret = -ENODEV; + goto clean_ale_ret; + } + + if (cpsw->data.dual_emac) { + ret = cpsw_probe_dual_emac(priv); + if (ret) { + cpsw_err(priv, probe, "error probe slave 2 emac interface\n"); + goto clean_unregister_netdev_ret; + } + } + /* Grab RX and TX IRQs. Note that we also have RX_THRESHOLD and * MISC IRQs which are always kept disabled with this driver so * we will not request them. @@ -3127,33 +3152,9 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_ale_ret; } - ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; - - ndev->netdev_ops = &cpsw_netdev_ops; - ndev->ethtool_ops = &cpsw_ethtool_ops; - netif_napi_add(ndev, &cpsw->napi_rx, cpsw_rx_poll, CPSW_POLL_WEIGHT); - netif_tx_napi_add(ndev, &cpsw->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT); - cpsw_split_res(ndev); - - /* register the network device */ - SET_NETDEV_DEV(ndev, &pdev->dev); - ret = register_netdev(ndev); - if (ret) { - dev_err(priv->dev, "error registering net device\n"); - ret = -ENODEV; - goto clean_ale_ret; - } - cpsw_notice(priv, probe, "initialized device (regs %pa, irq %d, pool size %d)\n", &ss_res->start, ndev->irq, dma_params.descs_pool_size); - if (cpsw->data.dual_emac) { - ret = cpsw_probe_dual_emac(priv); - if (ret) { - cpsw_err(priv, probe, "error probe slave 2 emac interface\n"); - goto clean_unregister_netdev_ret; - } - } pm_runtime_put(&pdev->dev); diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 32279d21c836..c2121d214f08 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -31,9 +31,18 @@ #include "cpts.h" +#define CPTS_SKB_TX_WORK_TIMEOUT 1 /* jiffies */ + +struct cpts_skb_cb_data { + unsigned long tmo; +}; + #define cpts_read32(c, r) readl_relaxed(&c->reg->r) #define cpts_write32(c, v, r) writel_relaxed(v, &c->reg->r) +static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, + u16 ts_seqid, u8 ts_msgtype); + static int event_expired(struct cpts_event *event) { return time_after(jiffies, event->tmo); @@ -77,6 +86,47 @@ static int cpts_purge_events(struct cpts *cpts) return removed ? 0 : -1; } +static bool cpts_match_tx_ts(struct cpts *cpts, struct cpts_event *event) +{ + struct sk_buff *skb, *tmp; + u16 seqid; + u8 mtype; + bool found = false; + + mtype = (event->high >> MESSAGE_TYPE_SHIFT) & MESSAGE_TYPE_MASK; + seqid = (event->high >> SEQUENCE_ID_SHIFT) & SEQUENCE_ID_MASK; + + /* no need to grab txq.lock as access is always done under cpts->lock */ + skb_queue_walk_safe(&cpts->txq, skb, tmp) { + struct skb_shared_hwtstamps ssh; + unsigned int class = ptp_classify_raw(skb); + struct cpts_skb_cb_data *skb_cb = + (struct cpts_skb_cb_data *)skb->cb; + + if (cpts_match(skb, class, seqid, mtype)) { + u64 ns = timecounter_cyc2time(&cpts->tc, event->low); + + memset(&ssh, 0, sizeof(ssh)); + ssh.hwtstamp = ns_to_ktime(ns); + skb_tstamp_tx(skb, &ssh); + found = true; + __skb_unlink(skb, &cpts->txq); + dev_consume_skb_any(skb); + dev_dbg(cpts->dev, "match tx timestamp mtype %u seqid %04x\n", + mtype, seqid); + } else if (time_after(jiffies, skb_cb->tmo)) { + /* timeout any expired skbs over 1s */ + dev_dbg(cpts->dev, + "expiring tx timestamp mtype %u seqid %04x\n", + mtype, seqid); + __skb_unlink(skb, &cpts->txq); + dev_consume_skb_any(skb); + } + } + + return found; +} + /* * Returns zero if matching event type was found. */ @@ -101,9 +151,15 @@ static int cpts_fifo_read(struct cpts *cpts, int match) event->low = lo; type = event_type(event); switch (type) { + case CPTS_EV_TX: + if (cpts_match_tx_ts(cpts, event)) { + /* if the new event matches an existing skb, + * then don't queue it + */ + break; + } case CPTS_EV_PUSH: case CPTS_EV_RX: - case CPTS_EV_TX: list_del_init(&event->list); list_add_tail(&event->list, &cpts->events); break; @@ -224,6 +280,24 @@ static int cpts_ptp_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } +static long cpts_overflow_check(struct ptp_clock_info *ptp) +{ + struct cpts *cpts = container_of(ptp, struct cpts, info); + unsigned long delay = cpts->ov_check_period; + struct timespec64 ts; + unsigned long flags; + + spin_lock_irqsave(&cpts->lock, flags); + ts = ns_to_timespec64(timecounter_read(&cpts->tc)); + + if (!skb_queue_empty(&cpts->txq)) + delay = CPTS_SKB_TX_WORK_TIMEOUT; + spin_unlock_irqrestore(&cpts->lock, flags); + + pr_debug("cpts overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec); + return (long)delay; +} + static struct ptp_clock_info cpts_info = { .owner = THIS_MODULE, .name = "CTPS timer", @@ -236,18 +310,9 @@ static struct ptp_clock_info cpts_info = { .gettime64 = cpts_ptp_gettime, .settime64 = cpts_ptp_settime, .enable = cpts_ptp_enable, + .do_aux_work = cpts_overflow_check, }; -static void cpts_overflow_check(struct work_struct *work) -{ - struct timespec64 ts; - struct cpts *cpts = container_of(work, struct cpts, overflow_work.work); - - cpts_ptp_gettime(&cpts->info, &ts); - pr_debug("cpts overflow check at %lld.%09lu\n", ts.tv_sec, ts.tv_nsec); - schedule_delayed_work(&cpts->overflow_work, cpts->ov_check_period); -} - static int cpts_match(struct sk_buff *skb, unsigned int ptp_class, u16 ts_seqid, u8 ts_msgtype) { @@ -299,7 +364,7 @@ static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type) return 0; spin_lock_irqsave(&cpts->lock, flags); - cpts_fifo_read(cpts, CPTS_EV_PUSH); + cpts_fifo_read(cpts, -1); list_for_each_safe(this, next, &cpts->events) { event = list_entry(this, struct cpts_event, list); if (event_expired(event)) { @@ -317,6 +382,19 @@ static u64 cpts_find_ts(struct cpts *cpts, struct sk_buff *skb, int ev_type) break; } } + + if (ev_type == CPTS_EV_TX && !ns) { + struct cpts_skb_cb_data *skb_cb = + (struct cpts_skb_cb_data *)skb->cb; + /* Not found, add frame to queue for processing later. + * The periodic FIFO check will handle this. + */ + skb_get(skb); + /* get the timestamp for timeouts */ + skb_cb->tmo = jiffies + msecs_to_jiffies(100); + __skb_queue_tail(&cpts->txq, skb); + ptp_schedule_worker(cpts->clock, 0); + } spin_unlock_irqrestore(&cpts->lock, flags); return ns; @@ -358,6 +436,7 @@ int cpts_register(struct cpts *cpts) { int err, i; + skb_queue_head_init(&cpts->txq); INIT_LIST_HEAD(&cpts->events); INIT_LIST_HEAD(&cpts->pool); for (i = 0; i < CPTS_MAX_EVENTS; i++) @@ -378,7 +457,7 @@ int cpts_register(struct cpts *cpts) } cpts->phc_index = ptp_clock_index(cpts->clock); - schedule_delayed_work(&cpts->overflow_work, cpts->ov_check_period); + ptp_schedule_worker(cpts->clock, cpts->ov_check_period); return 0; err_ptp: @@ -392,14 +471,15 @@ void cpts_unregister(struct cpts *cpts) if (WARN_ON(!cpts->clock)) return; - cancel_delayed_work_sync(&cpts->overflow_work); - ptp_clock_unregister(cpts->clock); cpts->clock = NULL; cpts_write32(cpts, 0, int_enable); cpts_write32(cpts, 0, control); + /* Drop all packet */ + skb_queue_purge(&cpts->txq); + clk_disable(cpts->refclk); } EXPORT_SYMBOL_GPL(cpts_unregister); @@ -476,7 +556,6 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs, cpts->dev = dev; cpts->reg = (struct cpsw_cpts __iomem *)regs; spin_lock_init(&cpts->lock); - INIT_DELAYED_WORK(&cpts->overflow_work, cpts_overflow_check); ret = cpts_of_parse(cpts, node); if (ret) diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 01ea82ba9cdc..73d73faf0f38 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -119,13 +119,13 @@ struct cpts { u32 cc_mult; /* for the nominal frequency */ struct cyclecounter cc; struct timecounter tc; - struct delayed_work overflow_work; int phc_index; struct clk *refclk; struct list_head events; struct list_head pool; struct cpts_event pool_data[CPTS_MAX_EVENTS]; unsigned long ov_check_period; + struct sk_buff_head txq; }; void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index d9db8a06afd2..cce9c9ed46aa 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -1338,7 +1338,7 @@ static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev) static void tc35815_fatal_error_interrupt(struct net_device *dev, u32 status) { static int count; - printk(KERN_WARNING "%s: Fatal Error Intterrupt (%#x):", + printk(KERN_WARNING "%s: Fatal Error Interrupt (%#x):", dev->name, status); if (status & Int_IntPCI) printk(" IntPCI"); diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c index de8156c6b292..2bbda71818ad 100644 --- a/drivers/net/geneve.c +++ b/drivers/net/geneve.c @@ -1091,7 +1091,7 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[], if (data[IFLA_GENEVE_ID]) { __u32 vni = nla_get_u32(data[IFLA_GENEVE_ID]); - if (vni >= GENEVE_VID_MASK) + if (vni >= GENEVE_N_VID) return -ERANGE; } diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c index 1542e837fdfa..f38e32a7ec9c 100644 --- a/drivers/net/gtp.c +++ b/drivers/net/gtp.c @@ -364,7 +364,7 @@ static int gtp_dev_init(struct net_device *dev) gtp->dev = dev; - dev->tstats = alloc_percpu(struct pcpu_sw_netstats); + dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); if (!dev->tstats) return -ENOMEM; diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index d6c25580f8dd..12cc64bfcff8 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -765,7 +765,8 @@ struct netvsc_device { u32 max_chn; u32 num_chn; - refcount_t sc_offered; + atomic_t open_chn; + wait_queue_head_t subchan_open; struct rndis_device *extension; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 0a9167dd72fb..d18c3326a1f7 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -78,6 +78,7 @@ static struct netvsc_device *alloc_net_device(void) net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT; net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT; init_completion(&net_device->channel_init_wait); + init_waitqueue_head(&net_device->subchan_open); return net_device; } @@ -1302,6 +1303,8 @@ int netvsc_device_add(struct hv_device *device, struct netvsc_channel *nvchan = &net_device->chan_table[i]; nvchan->channel = device->channel; + u64_stats_init(&nvchan->tx_stats.syncp); + u64_stats_init(&nvchan->rx_stats.syncp); } /* Enable NAPI handler before init callbacks */ diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 63c98bbbc596..0d78727f1a14 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -315,14 +315,34 @@ static u32 init_page_array(void *hdr, u32 len, struct sk_buff *skb, return slots_used; } -/* Estimate number of page buffers neede to transmit - * Need at most 2 for RNDIS header plus skb body and fragments. - */ -static unsigned int netvsc_get_slots(const struct sk_buff *skb) +static int count_skb_frag_slots(struct sk_buff *skb) +{ + int i, frags = skb_shinfo(skb)->nr_frags; + int pages = 0; + + for (i = 0; i < frags; i++) { + skb_frag_t *frag = skb_shinfo(skb)->frags + i; + unsigned long size = skb_frag_size(frag); + unsigned long offset = frag->page_offset; + + /* Skip unused frames from start of page */ + offset &= ~PAGE_MASK; + pages += PFN_UP(offset + size); + } + return pages; +} + +static int netvsc_get_slots(struct sk_buff *skb) { - return PFN_UP(offset_in_page(skb->data) + skb_headlen(skb)) - + skb_shinfo(skb)->nr_frags - + 2; + char *data = skb->data; + unsigned int offset = offset_in_page(data); + unsigned int len = skb_headlen(skb); + int slots; + int frag_slots; + + slots = DIV_ROUND_UP(offset + len, PAGE_SIZE); + frag_slots = count_skb_frag_slots(skb); + return slots + frag_slots; } static u32 net_checksum_info(struct sk_buff *skb) @@ -360,18 +380,21 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) struct hv_page_buffer page_buf[MAX_PAGE_BUFFER_COUNT]; struct hv_page_buffer *pb = page_buf; - /* We can only transmit MAX_PAGE_BUFFER_COUNT number + /* We will atmost need two pages to describe the rndis + * header. We can only transmit MAX_PAGE_BUFFER_COUNT number * of pages in a single packet. If skb is scattered around * more pages we try linearizing it. */ - num_data_pgs = netvsc_get_slots(skb); + + num_data_pgs = netvsc_get_slots(skb) + 2; + if (unlikely(num_data_pgs > MAX_PAGE_BUFFER_COUNT)) { ++net_device_ctx->eth_stats.tx_scattered; if (skb_linearize(skb)) goto no_memory; - num_data_pgs = netvsc_get_slots(skb); + num_data_pgs = netvsc_get_slots(skb) + 2; if (num_data_pgs > MAX_PAGE_BUFFER_COUNT) { ++net_device_ctx->eth_stats.tx_too_big; goto drop; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 85c00e1c52b6..d6308ffda53e 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -1048,8 +1048,8 @@ static void netvsc_sc_open(struct vmbus_channel *new_sc) else netif_napi_del(&nvchan->napi); - if (refcount_dec_and_test(&nvscdev->sc_offered)) - complete(&nvscdev->channel_init_wait); + atomic_inc(&nvscdev->open_chn); + wake_up(&nvscdev->subchan_open); } int rndis_filter_device_add(struct hv_device *dev, @@ -1090,8 +1090,6 @@ int rndis_filter_device_add(struct hv_device *dev, net_device->max_chn = 1; net_device->num_chn = 1; - refcount_set(&net_device->sc_offered, 0); - net_device->extension = rndis_device; rndis_device->ndev = net; @@ -1221,11 +1219,11 @@ int rndis_filter_device_add(struct hv_device *dev, rndis_device->ind_table[i] = ethtool_rxfh_indir_default(i, net_device->num_chn); + atomic_set(&net_device->open_chn, 1); num_rss_qs = net_device->num_chn - 1; if (num_rss_qs == 0) return 0; - refcount_set(&net_device->sc_offered, num_rss_qs); vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open); init_packet = &net_device->channel_init_pkt; @@ -1242,15 +1240,19 @@ int rndis_filter_device_add(struct hv_device *dev, if (ret) goto out; + wait_for_completion(&net_device->channel_init_wait); if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) { ret = -ENODEV; goto out; } - wait_for_completion(&net_device->channel_init_wait); net_device->num_chn = 1 + init_packet->msg.v5_msg.subchn_comp.num_subchannels; + /* wait for all sub channels to open */ + wait_event(net_device->subchan_open, + atomic_read(&net_device->open_chn) == net_device->num_chn); + /* ignore failues from setting rss parameters, still have channels */ rndis_filter_set_rss_param(rndis_device, netvsc_hash_key, net_device->num_chn); diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index f37e3c1fd4e7..8dab74a81303 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -192,7 +192,7 @@ static int ipvlan_init(struct net_device *dev) netdev_lockdep_set_classes(dev); - ipvlan->pcpu_stats = alloc_percpu(struct ipvl_pcpu_stats); + ipvlan->pcpu_stats = netdev_alloc_pcpu_stats(struct ipvl_pcpu_stats); if (!ipvlan->pcpu_stats) return -ENOMEM; diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c index 6f6ed75b63c9..765de3bedb88 100644 --- a/drivers/net/irda/mcs7780.c +++ b/drivers/net/irda/mcs7780.c @@ -141,9 +141,19 @@ static int mcs_set_reg(struct mcs_cb *mcs, __u16 reg, __u16 val) static int mcs_get_reg(struct mcs_cb *mcs, __u16 reg, __u16 * val) { struct usb_device *dev = mcs->usbdev; - int ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, - MCS_RD_RTYPE, 0, reg, val, 2, - msecs_to_jiffies(MCS_CTRL_TIMEOUT)); + void *dmabuf; + int ret; + + dmabuf = kmalloc(sizeof(__u16), GFP_KERNEL); + if (!dmabuf) + return -ENOMEM; + + ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ, + MCS_RD_RTYPE, 0, reg, dmabuf, 2, + msecs_to_jiffies(MCS_CTRL_TIMEOUT)); + + memcpy(val, dmabuf, sizeof(__u16)); + kfree(dmabuf); return ret; } diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 2dda72004a7d..928fd892f167 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -7,7 +7,16 @@ menuconfig MDIO_DEVICE help MDIO devices and driver infrastructure code. -if MDIO_DEVICE +config MDIO_BUS + tristate + default m if PHYLIB=m + default MDIO_DEVICE + help + This internal symbol is used for link time dependencies and it + reflects whether the mdio_bus/mdio_device code is built as a + loadable module or built-in. + +if MDIO_BUS config MDIO_BCM_IPROC tristate "Broadcom iProc MDIO bus controller" @@ -28,7 +37,6 @@ config MDIO_BCM_UNIMAC config MDIO_BITBANG tristate "Bitbanged MDIO buses" - depends on !(MDIO_DEVICE=y && PHYLIB=m) help This module implements the MDIO bus protocol in software, for use by low level drivers that export the ability to @@ -127,7 +135,6 @@ config MDIO_THUNDER tristate "ThunderX SOCs MDIO buses" depends on 64BIT depends on PCI - depends on !(MDIO_DEVICE=y && PHYLIB=m) select MDIO_CAVIUM help This driver supports the MDIO interfaces found on Cavium diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c index 00755b6a42cf..c608e1dfaf09 100644 --- a/drivers/net/phy/mdio-mux.c +++ b/drivers/net/phy/mdio-mux.c @@ -135,8 +135,8 @@ int mdio_mux_init(struct device *dev, for_each_available_child_of_node(dev->of_node, child_bus_node) { int v; - v = of_mdio_parse_addr(dev, child_bus_node); - if (v < 0) { + r = of_property_read_u32(child_bus_node, "reg", &v); + if (r) { dev_err(dev, "Error: Failed to find reg for child %s\n", of_node_full_name(child_bus_node)); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index d0626bf5c540..5068c582d502 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -749,6 +749,9 @@ void phy_stop_machine(struct phy_device *phydev) if (phydev->state > PHY_UP && phydev->state != PHY_HALTED) phydev->state = PHY_UP; mutex_unlock(&phydev->lock); + + /* Now we can run the state machine synchronously */ + phy_state_machine(&phydev->state_queue.work); } /** diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 13028833bee3..a404552555d4 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -120,6 +120,7 @@ struct ppp { int n_channels; /* how many channels are attached 54 */ spinlock_t rlock; /* lock for receive side 58 */ spinlock_t wlock; /* lock for transmit side 5c */ + int *xmit_recursion __percpu; /* xmit recursion detect */ int mru; /* max receive unit 60 */ unsigned int flags; /* control bits 64 */ unsigned int xstate; /* transmit state bits 68 */ @@ -1025,6 +1026,7 @@ static int ppp_dev_configure(struct net *src_net, struct net_device *dev, struct ppp *ppp = netdev_priv(dev); int indx; int err; + int cpu; ppp->dev = dev; ppp->ppp_net = src_net; @@ -1039,6 +1041,15 @@ static int ppp_dev_configure(struct net *src_net, struct net_device *dev, INIT_LIST_HEAD(&ppp->channels); spin_lock_init(&ppp->rlock); spin_lock_init(&ppp->wlock); + + ppp->xmit_recursion = alloc_percpu(int); + if (!ppp->xmit_recursion) { + err = -ENOMEM; + goto err1; + } + for_each_possible_cpu(cpu) + (*per_cpu_ptr(ppp->xmit_recursion, cpu)) = 0; + #ifdef CONFIG_PPP_MULTILINK ppp->minseq = -1; skb_queue_head_init(&ppp->mrq); @@ -1050,11 +1061,15 @@ static int ppp_dev_configure(struct net *src_net, struct net_device *dev, err = ppp_unit_register(ppp, conf->unit, conf->ifname_is_set); if (err < 0) - return err; + goto err2; conf->file->private_data = &ppp->file; return 0; +err2: + free_percpu(ppp->xmit_recursion); +err1: + return err; } static const struct nla_policy ppp_nl_policy[IFLA_PPP_MAX + 1] = { @@ -1400,18 +1415,16 @@ static void __ppp_xmit_process(struct ppp *ppp) ppp_xmit_unlock(ppp); } -static DEFINE_PER_CPU(int, ppp_xmit_recursion); - static void ppp_xmit_process(struct ppp *ppp) { local_bh_disable(); - if (unlikely(__this_cpu_read(ppp_xmit_recursion))) + if (unlikely(*this_cpu_ptr(ppp->xmit_recursion))) goto err; - __this_cpu_inc(ppp_xmit_recursion); + (*this_cpu_ptr(ppp->xmit_recursion))++; __ppp_xmit_process(ppp); - __this_cpu_dec(ppp_xmit_recursion); + (*this_cpu_ptr(ppp->xmit_recursion))--; local_bh_enable(); @@ -1902,23 +1915,23 @@ static void __ppp_channel_push(struct channel *pch) spin_unlock(&pch->downl); /* see if there is anything from the attached unit to be sent */ if (skb_queue_empty(&pch->file.xq)) { - read_lock(&pch->upl); ppp = pch->ppp; if (ppp) __ppp_xmit_process(ppp); - read_unlock(&pch->upl); } } static void ppp_channel_push(struct channel *pch) { - local_bh_disable(); - - __this_cpu_inc(ppp_xmit_recursion); - __ppp_channel_push(pch); - __this_cpu_dec(ppp_xmit_recursion); - - local_bh_enable(); + read_lock_bh(&pch->upl); + if (pch->ppp) { + (*this_cpu_ptr(pch->ppp->xmit_recursion))++; + __ppp_channel_push(pch); + (*this_cpu_ptr(pch->ppp->xmit_recursion))--; + } else { + __ppp_channel_push(pch); + } + read_unlock_bh(&pch->upl); } /* @@ -3057,6 +3070,7 @@ static void ppp_destroy_interface(struct ppp *ppp) #endif /* CONFIG_PPP_FILTER */ kfree_skb(ppp->xmit_pending); + free_percpu(ppp->xmit_recursion); free_netdev(ppp->dev); } diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c index eac499c58aa7..6dde9a0cfe76 100644 --- a/drivers/net/ppp/pptp.c +++ b/drivers/net/ppp/pptp.c @@ -131,7 +131,6 @@ static void del_chan(struct pppox_sock *sock) clear_bit(sock->proto.pptp.src_addr.call_id, callid_bitmap); RCU_INIT_POINTER(callid_sock[sock->proto.pptp.src_addr.call_id], NULL); spin_unlock(&chan_lock); - synchronize_rcu(); } static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) @@ -520,6 +519,7 @@ static int pptp_release(struct socket *sock) po = pppox_sk(sk); del_chan(po); + synchronize_rcu(); pppox_unbind_sock(sk); sk->sk_state = PPPOX_DEAD; diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 464570409796..ae53e899259f 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -60,11 +60,11 @@ static struct team_port *team_port_get_rtnl(const struct net_device *dev) static int __set_port_dev_addr(struct net_device *port_dev, const unsigned char *dev_addr) { - struct sockaddr addr; + struct sockaddr_storage addr; - memcpy(addr.sa_data, dev_addr, port_dev->addr_len); - addr.sa_family = port_dev->type; - return dev_set_mac_address(port_dev, &addr); + memcpy(addr.__data, dev_addr, port_dev->addr_len); + addr.ss_family = port_dev->type; + return dev_set_mac_address(port_dev, (struct sockaddr *)&addr); } static int team_port_set_orig_dev_addr(struct team_port *port) diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3d4c24572ecd..0a2c0a42283f 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1879,6 +1879,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) err_detach: tun_detach_all(dev); + /* register_netdevice() already called tun_free_netdev() */ + goto err_free_dev; + err_free_flow: tun_flow_uninit(tun); security_tun_dev_free_security(tun->security); @@ -2598,8 +2601,16 @@ static int __init tun_init(void) goto err_misc; } - register_netdevice_notifier(&tun_notifier_block); + ret = register_netdevice_notifier(&tun_notifier_block); + if (ret) { + pr_err("Can't register netdevice notifier\n"); + goto err_notifier; + } + return 0; + +err_notifier: + misc_deregister(&tun_miscdev); err_misc: rtnl_link_unregister(&tun_link_ops); err_linkops: diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h index d1092421aaa7..9a4171b90947 100644 --- a/drivers/net/usb/asix.h +++ b/drivers/net/usb/asix.h @@ -209,6 +209,7 @@ void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, struct asix_rx_fixup_info *rx); int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb); +void asix_rx_fixup_common_free(struct asix_common_private *dp); struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags); diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index 7847436c441e..522d2900cd1d 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -75,6 +75,27 @@ void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index, value, index, data, size); } +static void reset_asix_rx_fixup_info(struct asix_rx_fixup_info *rx) +{ + /* Reset the variables that have a lifetime outside of + * asix_rx_fixup_internal() so that future processing starts from a + * known set of initial conditions. + */ + + if (rx->ax_skb) { + /* Discard any incomplete Ethernet frame in the netdev buffer */ + kfree_skb(rx->ax_skb); + rx->ax_skb = NULL; + } + + /* Assume the Data header 32-bit word is at the start of the current + * or next URB socket buffer so reset all the state variables. + */ + rx->remaining = 0; + rx->split_head = false; + rx->header = 0; +} + int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, struct asix_rx_fixup_info *rx) { @@ -99,15 +120,7 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, if (size != ((~rx->header >> 16) & 0x7ff)) { netdev_err(dev->net, "asix_rx_fixup() Data Header synchronisation was lost, remaining %d\n", rx->remaining); - if (rx->ax_skb) { - kfree_skb(rx->ax_skb); - rx->ax_skb = NULL; - /* Discard the incomplete netdev Ethernet frame - * and assume the Data header is at the start of - * the current URB socket buffer. - */ - } - rx->remaining = 0; + reset_asix_rx_fixup_info(rx); } } @@ -139,11 +152,13 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, if (size != ((~rx->header >> 16) & 0x7ff)) { netdev_err(dev->net, "asix_rx_fixup() Bad Header Length 0x%x, offset %d\n", rx->header, offset); + reset_asix_rx_fixup_info(rx); return 0; } if (size > dev->net->mtu + ETH_HLEN + VLAN_HLEN) { netdev_dbg(dev->net, "asix_rx_fixup() Bad RX Length %d\n", size); + reset_asix_rx_fixup_info(rx); return 0; } @@ -168,8 +183,10 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, if (rx->ax_skb) { skb_put_data(rx->ax_skb, skb->data + offset, copy_length); - if (!rx->remaining) + if (!rx->remaining) { usbnet_skb_return(dev, rx->ax_skb); + rx->ax_skb = NULL; + } } offset += (copy_length + 1) & 0xfffe; @@ -178,6 +195,7 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, if (skb->len != offset) { netdev_err(dev->net, "asix_rx_fixup() Bad SKB Length %d, %d\n", skb->len, offset); + reset_asix_rx_fixup_info(rx); return 0; } @@ -192,6 +210,21 @@ int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb) return asix_rx_fixup_internal(dev, skb, rx); } +void asix_rx_fixup_common_free(struct asix_common_private *dp) +{ + struct asix_rx_fixup_info *rx; + + if (!dp) + return; + + rx = &dp->rx_fixup_info; + + if (rx->ax_skb) { + kfree_skb(rx->ax_skb); + rx->ax_skb = NULL; + } +} + struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c index a3aa0a27dfe5..b2ff88e69a81 100644 --- a/drivers/net/usb/asix_devices.c +++ b/drivers/net/usb/asix_devices.c @@ -764,6 +764,7 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf) static void ax88772_unbind(struct usbnet *dev, struct usb_interface *intf) { + asix_rx_fixup_common_free(dev->driver_priv); kfree(dev->driver_priv); } diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index d103a1d4fb36..8f572b9f3625 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -768,8 +768,10 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ u8 *buf; int len; int temp; + int err; u8 iface_no; struct usb_cdc_parsed_header hdr; + u16 curr_ntb_format; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -874,6 +876,32 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ goto error2; } + /* + * Some Huawei devices have been observed to come out of reset in NDP32 mode. + * Let's check if this is the case, and set the device to NDP16 mode again if + * needed. + */ + if (ctx->drvflags & CDC_NCM_FLAG_RESET_NTB16) { + err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_FORMAT, + USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + 0, iface_no, &curr_ntb_format, 2); + if (err < 0) { + goto error2; + } + + if (curr_ntb_format == USB_CDC_NCM_NTB32_FORMAT) { + dev_info(&intf->dev, "resetting NTB format to 16-bit"); + err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, + USB_TYPE_CLASS | USB_DIR_OUT + | USB_RECIP_INTERFACE, + USB_CDC_NCM_NTB16_FORMAT, + iface_no, NULL, 0); + + if (err < 0) + goto error2; + } + } + cdc_ncm_find_endpoints(dev, ctx->data); cdc_ncm_find_endpoints(dev, ctx->control); if (!dev->in || !dev->out || !dev->status) { diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c index 2680a65cd5e4..63f28908afda 100644 --- a/drivers/net/usb/huawei_cdc_ncm.c +++ b/drivers/net/usb/huawei_cdc_ncm.c @@ -80,6 +80,12 @@ static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev, * be at the end of the frame. */ drvflags |= CDC_NCM_FLAG_NDP_TO_END; + + /* Additionally, it has been reported that some Huawei E3372H devices, with + * firmware version 21.318.01.00.541, come out of reset in NTB32 format mode, hence + * needing to be set to the NTB16 one again. + */ + drvflags |= CDC_NCM_FLAG_RESET_NTB16; ret = cdc_ncm_bind_common(usbnet_dev, intf, 1, drvflags); if (ret) goto err; diff --git a/drivers/net/usb/lan78xx.c b/drivers/net/usb/lan78xx.c index 5833f7e2a127..b99a7fb09f8e 100644 --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c @@ -2367,9 +2367,6 @@ static int lan78xx_reset(struct lan78xx_net *dev) /* Init LTM */ lan78xx_init_ltm(dev); - dev->net->hard_header_len += TX_OVERHEAD; - dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; - if (dev->udev->speed == USB_SPEED_SUPER) { buf = DEFAULT_BURST_CAP_SIZE / SS_USB_PKT_SIZE; dev->rx_urb_size = DEFAULT_BURST_CAP_SIZE; @@ -2855,16 +2852,19 @@ static int lan78xx_bind(struct lan78xx_net *dev, struct usb_interface *intf) return ret; } + dev->net->hard_header_len += TX_OVERHEAD; + dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; + /* Init all registers */ ret = lan78xx_reset(dev); - lan78xx_mdio_init(dev); + ret = lan78xx_mdio_init(dev); dev->net->flags |= IFF_MULTICAST; pdata->wol = WAKE_MAGIC; - return 0; + return ret; } static void lan78xx_unbind(struct lan78xx_net *dev, struct usb_interface *intf) @@ -3525,11 +3525,11 @@ static int lan78xx_probe(struct usb_interface *intf, udev = interface_to_usbdev(intf); udev = usb_get_dev(udev); - ret = -ENOMEM; netdev = alloc_etherdev(sizeof(struct lan78xx_net)); if (!netdev) { - dev_err(&intf->dev, "Error: OOM\n"); - goto out1; + dev_err(&intf->dev, "Error: OOM\n"); + ret = -ENOMEM; + goto out1; } /* netdev_printk() needs this */ @@ -3610,7 +3610,7 @@ static int lan78xx_probe(struct usb_interface *intf, ret = register_netdev(netdev); if (ret != 0) { netif_err(dev, probe, netdev, "couldn't register the device\n"); - goto out2; + goto out3; } usb_set_intfdata(intf, dev); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 5894e3c9468f..8c3733608271 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -1175,6 +1175,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */ {QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */ {QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */ + {QMI_FIXED_INTF(0x2001, 0x7e35, 4)}, /* D-Link DWM-222 */ {QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */ {QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */ {QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */ @@ -1340,10 +1341,14 @@ static int qmi_wwan_probe(struct usb_interface *intf, static void qmi_wwan_disconnect(struct usb_interface *intf) { struct usbnet *dev = usb_get_intfdata(intf); - struct qmi_wwan_state *info = (void *)&dev->data; + struct qmi_wwan_state *info; struct list_head *iter; struct net_device *ldev; + /* called twice if separate control and data intf */ + if (!dev) + return; + info = (void *)&dev->data; if (info->flags & QMI_WWAN_FLAG_MUX) { if (!rtnl_trylock()) { restart_syscall(); diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 2dfca96a63b6..340c13484e5c 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -898,6 +898,7 @@ static const struct ethtool_ops smsc95xx_ethtool_ops = { .set_wol = smsc95xx_ethtool_set_wol, .get_link_ksettings = smsc95xx_get_link_ksettings, .set_link_ksettings = smsc95xx_set_link_ksettings, + .get_ts_info = ethtool_op_get_ts_info, }; static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 99a26a9efec1..98f17b05c68b 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -889,21 +889,20 @@ static int add_recvbuf_mergeable(struct virtnet_info *vi, buf = (char *)page_address(alloc_frag->page) + alloc_frag->offset; buf += headroom; /* advance address leaving hole at front of pkt */ - ctx = (void *)(unsigned long)len; get_page(alloc_frag->page); alloc_frag->offset += len + headroom; hole = alloc_frag->size - alloc_frag->offset; if (hole < len + headroom) { /* To avoid internal fragmentation, if there is very likely not * enough space for another buffer, add the remaining space to - * the current buffer. This extra space is not included in - * the truesize stored in ctx. + * the current buffer. */ len += hole; alloc_frag->offset += hole; } sg_init_one(rq->sg, buf, len); + ctx = (void *)(unsigned long)len; err = virtqueue_add_inbuf_ctx(rq->vq, rq->sg, 1, buf, ctx, gfp); if (err < 0) put_page(virt_to_head_page(buf)); @@ -2743,9 +2742,9 @@ module_init(virtio_net_driver_init); static __exit void virtio_net_driver_exit(void) { + unregister_virtio_driver(&virtio_net_driver); cpuhp_remove_multi_state(CPUHP_VIRT_NET_DEAD); cpuhp_remove_multi_state(virtionet_online); - unregister_virtio_driver(&virtio_net_driver); } module_exit(virtio_net_driver_exit); diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h index ba1c9f93592b..9c51b8be0038 100644 --- a/drivers/net/vmxnet3/vmxnet3_int.h +++ b/drivers/net/vmxnet3/vmxnet3_int.h @@ -311,7 +311,7 @@ struct vmxnet3_intr { u8 num_intrs; /* # of intr vectors */ u8 event_intr_idx; /* idx of the intr vector for event */ u8 mod_levels[VMXNET3_LINUX_MAX_MSIX_VECT]; /* moderation level */ - char event_msi_vector_name[IFNAMSIZ+11]; + char event_msi_vector_name[IFNAMSIZ+17]; #ifdef CONFIG_PCI_MSI struct msix_entry msix_entries[VMXNET3_LINUX_MAX_MSIX_VECT]; #endif diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 96aa7e6cf214..e17baac70f43 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -623,6 +623,7 @@ static struct sk_buff **vxlan_gro_receive(struct sock *sk, out: skb_gro_remcsum_cleanup(skb, &grc); + skb->remcsum_offload = 0; NAPI_GRO_CB(skb)->flush |= flush; return pp; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 2153e8062b4c..5cc3a07dda9e 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -214,7 +214,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, /* Make sure there's enough writeable headroom */ if (skb_headroom(skb) < drvr->hdrlen || skb_header_cloned(skb)) { - head_delta = drvr->hdrlen - skb_headroom(skb); + head_delta = max_t(int, drvr->hdrlen - skb_headroom(skb), 0); brcmf_dbg(INFO, "%s: insufficient headroom (%d)\n", brcmf_ifname(ifp), head_delta); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index d21258d277ce..f1b60740e020 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -159,8 +159,10 @@ void brcmf_feat_attach(struct brcmf_pub *drvr) brcmf_feat_firmware_capabilities(ifp); memset(&gscan_cfg, 0, sizeof(gscan_cfg)); - brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN, "pfn_gscan_cfg", - &gscan_cfg, sizeof(gscan_cfg)); + if (drvr->bus_if->chip != BRCM_CC_43430_CHIP_ID) + brcmf_feat_iovar_data_set(ifp, BRCMF_FEAT_GSCAN, + "pfn_gscan_cfg", + &gscan_cfg, sizeof(gscan_cfg)); brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn"); if (drvr->bus_if->wowl_supported) brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl"); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index fbcbb4325936..f3556122c6ac 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -2053,12 +2053,13 @@ static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt) atomic_inc(&stats->pktcow_failed); return -ENOMEM; } + head_pad = 0; } skb_push(pkt, head_pad); dat_buf = (u8 *)(pkt->data); } memset(dat_buf, 0, head_pad + bus->tx_hdrlen); - return 0; + return head_pad; } /** @@ -4174,11 +4175,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) goto fail; } - /* allocate scatter-gather table. sg support - * will be disabled upon allocation failure. - */ - brcmf_sdiod_sgtable_alloc(bus->sdiodev); - /* Query the F2 block size, set roundup accordingly */ bus->blocksize = bus->sdiodev->func[2]->cur_blksize; bus->roundup = min(max_roundup, bus->blocksize); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c index b4ecd1fe1374..97208ce19f92 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c @@ -154,7 +154,7 @@ static const struct iwl_tt_params iwl9000_tt_params = { const struct iwl_cfg iwl9160_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9160", .fw_name_pre = IWL9260A_FW_PRE, - .fw_name_pre_next_step = IWL9260B_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE, IWL_DEVICE_9000, .ht_params = &iwl9000_ht_params, .nvm_ver = IWL9000_NVM_VERSION, @@ -165,7 +165,7 @@ const struct iwl_cfg iwl9160_2ac_cfg = { const struct iwl_cfg iwl9260_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9260", .fw_name_pre = IWL9260A_FW_PRE, - .fw_name_pre_next_step = IWL9260B_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE, IWL_DEVICE_9000, .ht_params = &iwl9000_ht_params, .nvm_ver = IWL9000_NVM_VERSION, @@ -176,7 +176,7 @@ const struct iwl_cfg iwl9260_2ac_cfg = { const struct iwl_cfg iwl9270_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9270", .fw_name_pre = IWL9260A_FW_PRE, - .fw_name_pre_next_step = IWL9260B_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE, IWL_DEVICE_9000, .ht_params = &iwl9000_ht_params, .nvm_ver = IWL9000_NVM_VERSION, @@ -186,8 +186,8 @@ const struct iwl_cfg iwl9270_2ac_cfg = { const struct iwl_cfg iwl9460_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9460", - .fw_name_pre = IWL9000_FW_PRE, - .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE, + .fw_name_pre = IWL9260A_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE, IWL_DEVICE_9000, .ht_params = &iwl9000_ht_params, .nvm_ver = IWL9000_NVM_VERSION, @@ -198,8 +198,8 @@ const struct iwl_cfg iwl9460_2ac_cfg = { const struct iwl_cfg iwl9560_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 9560", - .fw_name_pre = IWL9000_FW_PRE, - .fw_name_pre_rf_next_step = IWL9000RFB_FW_PRE, + .fw_name_pre = IWL9260A_FW_PRE, + .fw_name_pre_b_or_c_step = IWL9260B_FW_PRE, IWL_DEVICE_9000, .ht_params = &iwl9000_ht_params, .nvm_ver = IWL9000_NVM_VERSION, diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c index adaa2f0097cc..fb40ddfced99 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c @@ -1189,11 +1189,11 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) next_reclaimed; IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", next_reclaimed); + iwlagn_check_ratid_empty(priv, sta_id, tid); } iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs); - iwlagn_check_ratid_empty(priv, sta_id, tid); freed = 0; /* process frames */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 0fa8c473f1e2..c73a6438ce8f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -328,6 +328,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_TX_POWER_ACK: reduced TX power API has larger * command size (command version 4) that supports toggling ACK TX * power reduction. + * @IWL_UCODE_TLV_CAPA_MLME_OFFLOAD: supports MLME offload * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -373,6 +374,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG = (__force iwl_ucode_tlv_capa_t)80, IWL_UCODE_TLV_CAPA_LQM_SUPPORT = (__force iwl_ucode_tlv_capa_t)81, IWL_UCODE_TLV_CAPA_TX_POWER_ACK = (__force iwl_ucode_tlv_capa_t)84, + IWL_UCODE_TLV_CAPA_MLME_OFFLOAD = (__force iwl_ucode_tlv_capa_t)96, NUM_IWL_UCODE_TLV_CAPA #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index c52623cb7c2a..d19c74827fbb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -276,10 +276,10 @@ struct iwl_pwr_tx_backoff { * @fw_name_pre: Firmware filename prefix. The api version and extension * (.ucode) will be added to filename before loading from disk. The * filename is constructed as fw_name_pre<api>.ucode. - * @fw_name_pre_next_step: same as @fw_name_pre, only for next step + * @fw_name_pre_b_or_c_step: same as @fw_name_pre, only for b or c steps * (if supported) - * @fw_name_pre_rf_next_step: same as @fw_name_pre_next_step, only for rf next - * step. Supported only in integrated solutions. + * @fw_name_pre_rf_next_step: same as @fw_name_pre_b_or_c_step, only for rf + * next step. Supported only in integrated solutions. * @ucode_api_max: Highest version of uCode API supported by driver. * @ucode_api_min: Lowest version of uCode API supported by driver. * @max_inst_size: The maximal length of the fw inst section @@ -330,7 +330,7 @@ struct iwl_cfg { /* params specific to an individual device within a device family */ const char *name; const char *fw_name_pre; - const char *fw_name_pre_next_step; + const char *fw_name_pre_b_or_c_step; const char *fw_name_pre_rf_next_step; /* params not likely to change within a device family */ const struct iwl_base_params *base_params; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h index 545d14b0bc92..f5c1127253cb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h @@ -55,8 +55,8 @@ static inline bool iwl_trace_data(struct sk_buff *skb) /* also account for the RFC 1042 header, of course */ offs += 6; - return skb->len > offs + 2 && - *(__be16 *)(skb->data + offs) == cpu_to_be16(ETH_P_PAE); + return skb->len <= offs + 2 || + *(__be16 *)(skb->data + offs) != cpu_to_be16(ETH_P_PAE); } static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 6fdb5921e17f..4e0f86fe0a6f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -216,8 +216,9 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) const char *fw_pre_name; if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_9000 && - CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_B_STEP) - fw_pre_name = cfg->fw_name_pre_next_step; + (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_B_STEP || + CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_C_STEP)) + fw_pre_name = cfg->fw_name_pre_b_or_c_step; else if (drv->trans->cfg->integrated && CSR_HW_RFID_STEP(drv->trans->hw_rf_id) == SILICON_B_STEP && cfg->fw_name_pre_rf_next_step) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 5c08f4d40f6a..3ee6767392b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -785,7 +785,8 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, int num_of_ch, __le32 *channels, u16 fw_mcc) { int ch_idx; - u16 ch_flags, prev_ch_flags = 0; + u16 ch_flags; + u32 reg_rule_flags, prev_reg_rule_flags = 0; const u8 *nvm_chan = cfg->ext_nvm ? iwl_ext_nvm_channels : iwl_nvm_channels; struct ieee80211_regdomain *regd; @@ -834,8 +835,11 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, continue; } + reg_rule_flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, + ch_flags, cfg); + /* we can't continue the same rule */ - if (ch_idx == 0 || prev_ch_flags != ch_flags || + if (ch_idx == 0 || prev_reg_rule_flags != reg_rule_flags || center_freq - prev_center_freq > 20) { valid_rules++; new_rule = true; @@ -854,18 +858,17 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, rule->power_rule.max_eirp = DBM_TO_MBM(IWL_DEFAULT_MAX_TX_POWER); - rule->flags = iwl_nvm_get_regdom_bw_flags(nvm_chan, ch_idx, - ch_flags, cfg); + rule->flags = reg_rule_flags; /* rely on auto-calculation to merge BW of contiguous chans */ rule->flags |= NL80211_RRF_AUTO_BW; rule->freq_range.max_bandwidth_khz = 0; - prev_ch_flags = ch_flags; prev_center_freq = center_freq; + prev_reg_rule_flags = reg_rule_flags; IWL_DEBUG_DEV(dev, IWL_DL_LAR, - "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x): Ad-Hoc %ssupported\n", + "Ch. %d [%sGHz] %s%s%s%s%s%s%s%s%s(0x%02x) reg_flags 0x%x: %s\n", center_freq, band == NL80211_BAND_5GHZ ? "5.2" : "2.4", CHECK_AND_PRINT_I(VALID), @@ -877,10 +880,10 @@ iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, CHECK_AND_PRINT_I(160MHZ), CHECK_AND_PRINT_I(INDOOR_ONLY), CHECK_AND_PRINT_I(GO_CONCURRENT), - ch_flags, + ch_flags, reg_rule_flags, ((ch_flags & NVM_CHANNEL_ACTIVE) && !(ch_flags & NVM_CHANNEL_RADAR)) - ? "" : "not "); + ? "Ad-Hoc" : ""); } regd->n_reg_rules = valid_rules; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 79e7a7a285dc..82863e9273eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1275,8 +1275,10 @@ static int iwl_mvm_sar_get_wgds_table(struct iwl_mvm *mvm) entry = &wifi_pkg->package.elements[idx++]; if ((entry->type != ACPI_TYPE_INTEGER) || - (entry->integer.value > U8_MAX)) - return -EINVAL; + (entry->integer.value > U8_MAX)) { + ret = -EINVAL; + goto out_free; + } mvm->geo_profiles[i].values[j] = entry->integer.value; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index bcde1ba0f1c8..ce901be5fba8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1084,7 +1084,13 @@ int __iwl_mvm_mac_start(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); - if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + if (test_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status)) { + /* + * Now convert the HW_RESTART_REQUESTED flag to IN_HW_RESTART + * so later code will - from now on - see that we're doing it. + */ + set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); + clear_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status); /* Clean up some internal and mac80211 state on restart */ iwl_mvm_restart_cleanup(mvm); } else { @@ -2591,8 +2597,18 @@ static void iwl_mvm_purge_deferred_tx_frames(struct iwl_mvm *mvm, spin_lock_bh(&mvm_sta->lock); for (i = 0; i <= IWL_MAX_TID_COUNT; i++) { tid_data = &mvm_sta->tid_data[i]; - while ((skb = __skb_dequeue(&tid_data->deferred_tx_frames))) + + while ((skb = __skb_dequeue(&tid_data->deferred_tx_frames))) { + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + /* + * The first deferred frame should've stopped the MAC + * queues, so we should never get a second deferred + * frame for the RA/TID. + */ + iwl_mvm_start_mac_queues(mvm, info->hw_queue); ieee80211_free_txskb(mvm->hw, skb); + } } spin_unlock_bh(&mvm_sta->lock); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index eaacfaf37206..ddd8719f27b8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1090,6 +1090,7 @@ struct iwl_mvm { * @IWL_MVM_STATUS_HW_RFKILL: HW RF-kill is asserted * @IWL_MVM_STATUS_HW_CTKILL: CT-kill is active * @IWL_MVM_STATUS_ROC_RUNNING: remain-on-channel is running + * @IWL_MVM_STATUS_HW_RESTART_REQUESTED: HW restart was requested * @IWL_MVM_STATUS_IN_HW_RESTART: HW restart is active * @IWL_MVM_STATUS_IN_D0I3: NIC is in D0i3 * @IWL_MVM_STATUS_ROC_AUX_RUNNING: AUX remain-on-channel is running @@ -1101,6 +1102,7 @@ enum iwl_mvm_status { IWL_MVM_STATUS_HW_RFKILL, IWL_MVM_STATUS_HW_CTKILL, IWL_MVM_STATUS_ROC_RUNNING, + IWL_MVM_STATUS_HW_RESTART_REQUESTED, IWL_MVM_STATUS_IN_HW_RESTART, IWL_MVM_STATUS_IN_D0I3, IWL_MVM_STATUS_ROC_AUX_RUNNING, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 4d1188b8736a..9c175d5e9d67 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1235,9 +1235,8 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) */ if (!mvm->fw_restart && fw_error) { iwl_mvm_fw_dbg_collect_desc(mvm, &iwl_mvm_dump_desc_assert, - NULL); - } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, - &mvm->status)) { + NULL); + } else if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { struct iwl_mvm_reprobe *reprobe; IWL_ERR(mvm, @@ -1268,6 +1267,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) if (fw_error && mvm->fw_restart > 0) mvm->fw_restart--; + set_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status); ieee80211_restart_hw(mvm->hw); } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 65beca3a457a..8999a1199d60 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -1291,7 +1291,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, * first index into rate scale table. */ if (info->flags & IEEE80211_TX_STAT_AMPDU) { - rs_collect_tpc_data(mvm, lq_sta, curr_tbl, lq_rate.index, + rs_collect_tpc_data(mvm, lq_sta, curr_tbl, tx_resp_rate.index, info->status.ampdu_len, info->status.ampdu_ack_len, reduced_txp); @@ -1312,7 +1312,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (info->status.ampdu_ack_len == 0) info->status.ampdu_len = 1; - rs_collect_tlc_data(mvm, lq_sta, curr_tbl, lq_rate.index, + rs_collect_tlc_data(mvm, lq_sta, curr_tbl, tx_resp_rate.index, info->status.ampdu_len, info->status.ampdu_ack_len); @@ -1348,11 +1348,11 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, continue; rs_collect_tpc_data(mvm, lq_sta, tmp_tbl, - lq_rate.index, 1, + tx_resp_rate.index, 1, i < retries ? 0 : legacy_success, reduced_txp); rs_collect_tlc_data(mvm, lq_sta, tmp_tbl, - lq_rate.index, 1, + tx_resp_rate.index, 1, i < retries ? 0 : legacy_success); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index f3e608196369..71c8b800ffa9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -636,9 +636,9 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, baid_data = rcu_dereference(mvm->baid_map[baid]); if (!baid_data) { - WARN(!(reorder & IWL_RX_MPDU_REORDER_BA_OLD_SN), - "Received baid %d, but no data exists for this BAID\n", - baid); + IWL_DEBUG_RX(mvm, + "Got valid BAID but no baid allocated, bypass the re-ordering buffer. Baid %d reorder 0x%x\n", + baid, reorder); return false; } @@ -759,7 +759,9 @@ static void iwl_mvm_agg_rx_received(struct iwl_mvm *mvm, data = rcu_dereference(mvm->baid_map[baid]); if (!data) { - WARN_ON(!(reorder_data & IWL_RX_MPDU_REORDER_BA_OLD_SN)); + IWL_DEBUG_RX(mvm, + "Got valid BAID but no baid allocated, bypass the re-ordering buffer. Baid %d reorder 0x%x\n", + baid, reorder_data); goto out; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 4df5f13fcdae..027ee5e72172 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -121,7 +121,8 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color), .add_modify = update ? 1 : 0, .station_flags_msk = cpu_to_le32(STA_FLG_FAT_EN_MSK | - STA_FLG_MIMO_EN_MSK), + STA_FLG_MIMO_EN_MSK | + STA_FLG_RTS_MIMO_PROT), .tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg), }; int ret; @@ -277,9 +278,21 @@ static void iwl_mvm_rx_agg_session_expired(unsigned long data) /* Timer expired */ sta = rcu_dereference(ba_data->mvm->fw_id_to_mac_id[ba_data->sta_id]); + + /* + * sta should be valid unless the following happens: + * The firmware asserts which triggers a reconfig flow, but + * the reconfig fails before we set the pointer to sta into + * the fw_id_to_mac_id pointer table. Mac80211 can't stop + * A-MDPU and hence the timer continues to run. Then, the + * timer expires and sta is NULL. + */ + if (!sta) + goto unlock; + mvm_sta = iwl_mvm_sta_from_mac80211(sta); - ieee80211_stop_rx_ba_session_offl(mvm_sta->vif, - sta->addr, ba_data->tid); + ieee80211_rx_ba_timer_expired(mvm_sta->vif, + sta->addr, ba_data->tid); unlock: rcu_read_unlock(); } @@ -2015,7 +2028,8 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) IWL_MAX_TID_COUNT, wdg_timeout); - if (vif->type == NL80211_IFTYPE_AP) + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) mvm->probe_queue = queue; else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) mvm->p2p_dev_queue = queue; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index 60360ed73f26..5fcc9dd6be56 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -185,8 +185,14 @@ static u16 iwl_mvm_tx_csum(struct iwl_mvm *mvm, struct sk_buff *skb, else udp_hdr(skb)->check = 0; - /* mac header len should include IV, size is in words */ - if (info->control.hw_key) + /* + * mac header len should include IV, size is in words unless + * the IV is added by the firmware like in WEP. + * In new Tx API, the IV is always added by the firmware. + */ + if (!iwl_mvm_has_new_tx_api(mvm) && info->control.hw_key && + info->control.hw_key->cipher != WLAN_CIPHER_SUITE_WEP40 && + info->control.hw_key->cipher != WLAN_CIPHER_SUITE_WEP104) mh_len += info->control.hw_key->iv_len; mh_len /= 2; offload_assist |= mh_len << TX_CMD_OFFLD_MH_SIZE; @@ -1815,6 +1821,8 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) struct iwl_mvm_tid_data *tid_data; struct iwl_mvm_sta *mvmsta; + ba_info.flags = IEEE80211_TX_STAT_AMPDU; + if (iwl_mvm_has_new_tx_api(mvm)) { struct iwl_mvm_compressed_ba_notif *ba_res = (void *)pkt->data; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index f16c1bb9bf94..84f4ba01e14f 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -510,9 +510,17 @@ static const struct pci_device_id iwl_hw_card_ids[] = { /* 9000 Series */ {IWL_PCI_DEVICE(0x271B, 0x0010, iwl9160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x271B, 0x0014, iwl9160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x271B, 0x0210, iwl9160_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x0000, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x0010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x0014, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0xA014, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x4010, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x0210, iwl9260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x0214, iwl9260_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x1410, iwl9270_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x1610, iwl9270_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0A10, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0010, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0210, iwl9460_2ac_cfg)}, @@ -527,10 +535,22 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x9DF0, 0x2A10, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0x30DC, 0x0060, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x0060, iwl9460_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x0260, iwl9460_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x0064, iwl9460_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x00A4, iwl9460_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x40A4, iwl9460_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x02A4, iwl9460_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x00A0, iwl9460_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x02A0, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0060, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0xA370, 0x0060, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0x31DC, 0x0060, iwl9460_2ac_cfg)}, {IWL_PCI_DEVICE(0x2526, 0x0030, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x4030, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x0230, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x0234, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x0238, iwl9560_2ac_cfg)}, + {IWL_PCI_DEVICE(0x2526, 0x023C, iwl9560_2ac_cfg)}, {IWL_PCI_DEVICE(0x9DF0, 0x0030, iwl9560_2ac_cfg)}, {IWL_PCI_DEVICE(0xA370, 0x0030, iwl9560_2ac_cfg)}, {IWL_PCI_DEVICE(0x31DC, 0x0030, iwl9560_2ac_cfg)}, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 92b3a55d0fbc..f95eec52508e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -3150,7 +3150,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, init_waitqueue_head(&trans_pcie->d0i3_waitq); if (trans_pcie->msix_enabled) { - if (iwl_pcie_init_msix_handler(pdev, trans_pcie)) + ret = iwl_pcie_init_msix_handler(pdev, trans_pcie); + if (ret) goto out_no_pci; } else { ret = iwl_pcie_alloc_ict(trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index de50418adae5..034bdb4a0b06 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -298,6 +298,9 @@ void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans) for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { struct iwl_txq *txq = trans_pcie->txq[i]; + if (!test_bit(i, trans_pcie->queue_used)) + continue; + spin_lock_bh(&txq->lock); if (txq->need_update) { iwl_pcie_txq_inc_wr_ptr(trans, txq); diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index 6e2e760d98b1..0b75def39c6c 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -5704,7 +5704,7 @@ static void rt2800_init_freq_calibration(struct rt2x00_dev *rt2x00dev) static void rt2800_init_bbp_5592_glrt(struct rt2x00_dev *rt2x00dev) { - const u8 glrt_table[] = { + static const u8 glrt_table[] = { 0xE0, 0x1F, 0X38, 0x32, 0x08, 0x28, 0x19, 0x0A, 0xFF, 0x00, /* 128 ~ 137 */ 0x16, 0x10, 0x10, 0x0B, 0x36, 0x2C, 0x26, 0x24, 0x42, 0x36, /* 138 ~ 147 */ 0x30, 0x2D, 0x4C, 0x46, 0x3D, 0x40, 0x3E, 0x42, 0x3D, 0x40, /* 148 ~ 157 */ diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c index 2a7ad5ffe997..cd5dc6dcb19f 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c @@ -846,9 +846,6 @@ static bool _rtl8723be_init_mac(struct ieee80211_hw *hw) return false; } - if (rtlpriv->cfg->ops->get_btc_status()) - rtlpriv->btcoexist.btc_ops->btc_power_on_setting(rtlpriv); - bytetmp = rtl_read_byte(rtlpriv, REG_MULTI_FUNC_CTRL); rtl_write_byte(rtlpriv, REG_MULTI_FUNC_CTRL, bytetmp | BIT(3)); diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h index fb1ebb01133f..70723e67b7d7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/wifi.h +++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h @@ -2547,7 +2547,6 @@ struct bt_coexist_info { struct rtl_btc_ops { void (*btc_init_variables) (struct rtl_priv *rtlpriv); void (*btc_init_hal_vars) (struct rtl_priv *rtlpriv); - void (*btc_power_on_setting)(struct rtl_priv *rtlpriv); void (*btc_init_hw_config) (struct rtl_priv *rtlpriv); void (*btc_ips_notify) (struct rtl_priv *rtlpriv, u8 type); void (*btc_lps_notify)(struct rtl_priv *rtlpriv, u8 type); diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 9a03c5871efe..f58d8e305323 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -924,10 +924,8 @@ out1: ntb_free_mw(nt, i); /* if there's an actual failure, we should just bail */ - if (rc < 0) { - ntb_link_disable(ndev); + if (rc < 0) return; - } out: if (ntb_link_is_up(ndev, NULL, NULL) == 1) @@ -1059,7 +1057,7 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev) int node; int rc, i; - mw_count = ntb_mw_count(ndev, PIDX); + mw_count = ntb_peer_mw_count(ndev); if (!ndev->ops->mw_set_trans) { dev_err(&ndev->dev, "Inbound MW based NTB API is required\n"); diff --git a/drivers/ntb/test/ntb_tool.c b/drivers/ntb/test/ntb_tool.c index f002bf48a08d..a69815c45ce6 100644 --- a/drivers/ntb/test/ntb_tool.c +++ b/drivers/ntb/test/ntb_tool.c @@ -959,7 +959,7 @@ static int tool_probe(struct ntb_client *self, struct ntb_dev *ntb) tc->ntb = ntb; init_waitqueue_head(&tc->link_wq); - tc->mw_count = min(ntb_mw_count(tc->ntb, PIDX), MAX_MWS); + tc->mw_count = min(ntb_peer_mw_count(tc->ntb), MAX_MWS); for (i = 0; i < tc->mw_count; i++) { rc = tool_init_mw(tc, i); if (rc) diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index 7cd99b1f8596..75bc08c6838c 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -421,14 +421,15 @@ static void set_badblock(struct badblocks *bb, sector_t s, int num) static void __add_badblock_range(struct badblocks *bb, u64 ns_offset, u64 len) { const unsigned int sector_size = 512; - sector_t start_sector; + sector_t start_sector, end_sector; u64 num_sectors; u32 rem; start_sector = div_u64(ns_offset, sector_size); - num_sectors = div_u64_rem(len, sector_size, &rem); + end_sector = div_u64_rem(ns_offset + len, sector_size, &rem); if (rem) - num_sectors++; + end_sector++; + num_sectors = end_sector - start_sector; if (unlikely(num_sectors > (u64)INT_MAX)) { u64 remaining = num_sectors; diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index cb96f4a7ae3a..37046ac2c441 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -336,7 +336,7 @@ static int nvme_get_stream_params(struct nvme_ctrl *ctrl, c.directive.opcode = nvme_admin_directive_recv; c.directive.nsid = cpu_to_le32(nsid); - c.directive.numd = sizeof(*s); + c.directive.numd = cpu_to_le32((sizeof(*s) >> 2) - 1); c.directive.doper = NVME_DIR_RCV_ST_OP_PARAM; c.directive.dtype = NVME_DIR_STREAMS; @@ -1509,7 +1509,7 @@ static void nvme_set_queue_limits(struct nvme_ctrl *ctrl, blk_queue_write_cache(q, vwc, vwc); } -static void nvme_configure_apst(struct nvme_ctrl *ctrl) +static int nvme_configure_apst(struct nvme_ctrl *ctrl) { /* * APST (Autonomous Power State Transition) lets us program a @@ -1538,16 +1538,16 @@ static void nvme_configure_apst(struct nvme_ctrl *ctrl) * then don't do anything. */ if (!ctrl->apsta) - return; + return 0; if (ctrl->npss > 31) { dev_warn(ctrl->device, "NPSS is invalid; not using APST\n"); - return; + return 0; } table = kzalloc(sizeof(*table), GFP_KERNEL); if (!table) - return; + return 0; if (!ctrl->apst_enabled || ctrl->ps_max_latency_us == 0) { /* Turn off APST. */ @@ -1629,6 +1629,7 @@ static void nvme_configure_apst(struct nvme_ctrl *ctrl) dev_err(ctrl->device, "failed to set APST feature (%d)\n", ret); kfree(table); + return ret; } static void nvme_set_latency_tolerance(struct device *dev, s32 val) @@ -1835,13 +1836,16 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) * In fabrics we need to verify the cntlid matches the * admin connect */ - if (ctrl->cntlid != le16_to_cpu(id->cntlid)) + if (ctrl->cntlid != le16_to_cpu(id->cntlid)) { ret = -EINVAL; + goto out_free; + } if (!ctrl->opts->discovery_nqn && !ctrl->kas) { dev_err(ctrl->device, "keep-alive support is mandatory for fabrics\n"); ret = -EINVAL; + goto out_free; } } else { ctrl->cntlid = le16_to_cpu(id->cntlid); @@ -1856,11 +1860,20 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) else if (!ctrl->apst_enabled && prev_apst_enabled) dev_pm_qos_hide_latency_tolerance(ctrl->device); - nvme_configure_apst(ctrl); - nvme_configure_directives(ctrl); + ret = nvme_configure_apst(ctrl); + if (ret < 0) + return ret; + + ret = nvme_configure_directives(ctrl); + if (ret < 0) + return ret; ctrl->identified = true; + return 0; + +out_free: + kfree(id); return ret; } EXPORT_SYMBOL_GPL(nvme_init_identify); @@ -1995,15 +2008,20 @@ static ssize_t wwid_show(struct device *dev, struct device_attribute *attr, int serial_len = sizeof(ctrl->serial); int model_len = sizeof(ctrl->model); + if (!uuid_is_null(&ns->uuid)) + return sprintf(buf, "uuid.%pU\n", &ns->uuid); + if (memchr_inv(ns->nguid, 0, sizeof(ns->nguid))) return sprintf(buf, "eui.%16phN\n", ns->nguid); if (memchr_inv(ns->eui, 0, sizeof(ns->eui))) return sprintf(buf, "eui.%8phN\n", ns->eui); - while (ctrl->serial[serial_len - 1] == ' ') + while (serial_len > 0 && (ctrl->serial[serial_len - 1] == ' ' || + ctrl->serial[serial_len - 1] == '\0')) serial_len--; - while (ctrl->model[model_len - 1] == ' ') + while (model_len > 0 && (ctrl->model[model_len - 1] == ' ' || + ctrl->model[model_len - 1] == '\0')) model_len--; return sprintf(buf, "nvme.%04x-%*phN-%*phN-%08x\n", ctrl->vid, @@ -2709,7 +2727,8 @@ void nvme_kill_queues(struct nvme_ctrl *ctrl) mutex_lock(&ctrl->namespaces_mutex); /* Forcibly unquiesce queues to avoid blocking dispatch */ - blk_mq_unquiesce_queue(ctrl->admin_q); + if (ctrl->admin_q) + blk_mq_unquiesce_queue(ctrl->admin_q); list_for_each_entry(ns, &ctrl->namespaces, list) { /* diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index 2e582a240943..5f5cd306f76d 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -794,7 +794,8 @@ static int nvmf_check_allowed_opts(struct nvmf_ctrl_options *opts, int i; for (i = 0; i < ARRAY_SIZE(opt_tokens); i++) { - if (opt_tokens[i].token & ~allowed_opts) { + if ((opt_tokens[i].token & opts->mask) && + (opt_tokens[i].token & ~allowed_opts)) { pr_warn("invalid parameter '%s'\n", opt_tokens[i].pattern); } diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index d666ada39a9b..5c2a08ef08ba 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -1888,7 +1888,7 @@ nvme_fc_start_fcp_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_queue *queue, * the target device is present */ if (ctrl->rport->remoteport.port_state != FC_OBJSTATE_ONLINE) - return BLK_STS_IOERR; + goto busy; if (!nvme_fc_ctrl_get(ctrl)) return BLK_STS_IOERR; @@ -1958,22 +1958,25 @@ nvme_fc_start_fcp_op(struct nvme_fc_ctrl *ctrl, struct nvme_fc_queue *queue, queue->lldd_handle, &op->fcp_req); if (ret) { - if (op->rq) /* normal request */ + if (!(op->flags & FCOP_FLAGS_AEN)) nvme_fc_unmap_data(ctrl, op->rq, op); - /* else - aen. no cleanup needed */ nvme_fc_ctrl_put(ctrl); - if (ret != -EBUSY) + if (ctrl->rport->remoteport.port_state == FC_OBJSTATE_ONLINE && + ret != -EBUSY) return BLK_STS_IOERR; - if (op->rq) - blk_mq_delay_run_hw_queue(queue->hctx, NVMEFC_QUEUE_DELAY); - - return BLK_STS_RESOURCE; + goto busy; } return BLK_STS_OK; + +busy: + if (!(op->flags & FCOP_FLAGS_AEN) && queue->hctx) + blk_mq_delay_run_hw_queue(queue->hctx, NVMEFC_QUEUE_DELAY); + + return BLK_STS_RESOURCE; } static blk_status_t @@ -2802,66 +2805,70 @@ out_fail: return ERR_PTR(ret); } -enum { - FCT_TRADDR_ERR = 0, - FCT_TRADDR_WWNN = 1 << 0, - FCT_TRADDR_WWPN = 1 << 1, -}; struct nvmet_fc_traddr { u64 nn; u64 pn; }; -static const match_table_t traddr_opt_tokens = { - { FCT_TRADDR_WWNN, "nn-%s" }, - { FCT_TRADDR_WWPN, "pn-%s" }, - { FCT_TRADDR_ERR, NULL } -}; - static int -nvme_fc_parse_address(struct nvmet_fc_traddr *traddr, char *buf) +__nvme_fc_parse_u64(substring_t *sstr, u64 *val) { - substring_t args[MAX_OPT_ARGS]; - char *options, *o, *p; - int token, ret = 0; u64 token64; - options = o = kstrdup(buf, GFP_KERNEL); - if (!options) - return -ENOMEM; + if (match_u64(sstr, &token64)) + return -EINVAL; + *val = token64; - while ((p = strsep(&o, ":\n")) != NULL) { - if (!*p) - continue; + return 0; +} - token = match_token(p, traddr_opt_tokens, args); - switch (token) { - case FCT_TRADDR_WWNN: - if (match_u64(args, &token64)) { - ret = -EINVAL; - goto out; - } - traddr->nn = token64; - break; - case FCT_TRADDR_WWPN: - if (match_u64(args, &token64)) { - ret = -EINVAL; - goto out; - } - traddr->pn = token64; - break; - default: - pr_warn("unknown traddr token or missing value '%s'\n", - p); - ret = -EINVAL; - goto out; - } - } +/* + * This routine validates and extracts the WWN's from the TRADDR string. + * As kernel parsers need the 0x to determine number base, universally + * build string to parse with 0x prefix before parsing name strings. + */ +static int +nvme_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf, size_t blen) +{ + char name[2 + NVME_FC_TRADDR_HEXNAMELEN + 1]; + substring_t wwn = { name, &name[sizeof(name)-1] }; + int nnoffset, pnoffset; + + /* validate it string one of the 2 allowed formats */ + if (strnlen(buf, blen) == NVME_FC_TRADDR_MAXLENGTH && + !strncmp(buf, "nn-0x", NVME_FC_TRADDR_OXNNLEN) && + !strncmp(&buf[NVME_FC_TRADDR_MAX_PN_OFFSET], + "pn-0x", NVME_FC_TRADDR_OXNNLEN)) { + nnoffset = NVME_FC_TRADDR_OXNNLEN; + pnoffset = NVME_FC_TRADDR_MAX_PN_OFFSET + + NVME_FC_TRADDR_OXNNLEN; + } else if ((strnlen(buf, blen) == NVME_FC_TRADDR_MINLENGTH && + !strncmp(buf, "nn-", NVME_FC_TRADDR_NNLEN) && + !strncmp(&buf[NVME_FC_TRADDR_MIN_PN_OFFSET], + "pn-", NVME_FC_TRADDR_NNLEN))) { + nnoffset = NVME_FC_TRADDR_NNLEN; + pnoffset = NVME_FC_TRADDR_MIN_PN_OFFSET + NVME_FC_TRADDR_NNLEN; + } else + goto out_einval; -out: - kfree(options); - return ret; + name[0] = '0'; + name[1] = 'x'; + name[2 + NVME_FC_TRADDR_HEXNAMELEN] = 0; + + memcpy(&name[2], &buf[nnoffset], NVME_FC_TRADDR_HEXNAMELEN); + if (__nvme_fc_parse_u64(&wwn, &traddr->nn)) + goto out_einval; + + memcpy(&name[2], &buf[pnoffset], NVME_FC_TRADDR_HEXNAMELEN); + if (__nvme_fc_parse_u64(&wwn, &traddr->pn)) + goto out_einval; + + return 0; + +out_einval: + pr_warn("%s: bad traddr string\n", __func__); + return -EINVAL; } static struct nvme_ctrl * @@ -2875,11 +2882,11 @@ nvme_fc_create_ctrl(struct device *dev, struct nvmf_ctrl_options *opts) unsigned long flags; int ret; - ret = nvme_fc_parse_address(&raddr, opts->traddr); + ret = nvme_fc_parse_traddr(&raddr, opts->traddr, NVMF_TRADDR_SIZE); if (ret || !raddr.nn || !raddr.pn) return ERR_PTR(-EINVAL); - ret = nvme_fc_parse_address(&laddr, opts->host_traddr); + ret = nvme_fc_parse_traddr(&laddr, opts->host_traddr, NVMF_TRADDR_SIZE); if (ret || !laddr.nn || !laddr.pn) return ERR_PTR(-EINVAL); diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index d10d2f279d19..925467b31a33 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -539,7 +539,7 @@ static void nvme_dif_complete(u32 p, u32 v, struct t10_pi_tuple *pi) } #endif -static bool nvme_setup_prps(struct nvme_dev *dev, struct request *req) +static blk_status_t nvme_setup_prps(struct nvme_dev *dev, struct request *req) { struct nvme_iod *iod = blk_mq_rq_to_pdu(req); struct dma_pool *pool; @@ -556,7 +556,7 @@ static bool nvme_setup_prps(struct nvme_dev *dev, struct request *req) length -= (page_size - offset); if (length <= 0) - return true; + return BLK_STS_OK; dma_len -= (page_size - offset); if (dma_len) { @@ -569,7 +569,7 @@ static bool nvme_setup_prps(struct nvme_dev *dev, struct request *req) if (length <= page_size) { iod->first_dma = dma_addr; - return true; + return BLK_STS_OK; } nprps = DIV_ROUND_UP(length, page_size); @@ -585,7 +585,7 @@ static bool nvme_setup_prps(struct nvme_dev *dev, struct request *req) if (!prp_list) { iod->first_dma = dma_addr; iod->npages = -1; - return false; + return BLK_STS_RESOURCE; } list[0] = prp_list; iod->first_dma = prp_dma; @@ -595,7 +595,7 @@ static bool nvme_setup_prps(struct nvme_dev *dev, struct request *req) __le64 *old_prp_list = prp_list; prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma); if (!prp_list) - return false; + return BLK_STS_RESOURCE; list[iod->npages++] = prp_list; prp_list[0] = old_prp_list[i - 1]; old_prp_list[i - 1] = cpu_to_le64(prp_dma); @@ -609,13 +609,29 @@ static bool nvme_setup_prps(struct nvme_dev *dev, struct request *req) break; if (dma_len > 0) continue; - BUG_ON(dma_len < 0); + if (unlikely(dma_len < 0)) + goto bad_sgl; sg = sg_next(sg); dma_addr = sg_dma_address(sg); dma_len = sg_dma_len(sg); } - return true; + return BLK_STS_OK; + + bad_sgl: + if (WARN_ONCE(1, "Invalid SGL for payload:%d nents:%d\n", + blk_rq_payload_bytes(req), iod->nents)) { + for_each_sg(iod->sg, sg, iod->nents, i) { + dma_addr_t phys = sg_phys(sg); + pr_warn("sg[%d] phys_addr:%pad offset:%d length:%d " + "dma_address:%pad dma_length:%d\n", i, &phys, + sg->offset, sg->length, + &sg_dma_address(sg), + sg_dma_len(sg)); + } + } + return BLK_STS_IOERR; + } static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req, @@ -637,7 +653,8 @@ static blk_status_t nvme_map_data(struct nvme_dev *dev, struct request *req, DMA_ATTR_NO_WARN)) goto out; - if (!nvme_setup_prps(dev, req)) + ret = nvme_setup_prps(dev, req); + if (ret != BLK_STS_OK) goto out_unmap; ret = BLK_STS_IOERR; @@ -784,6 +801,7 @@ static inline void nvme_handle_cqe(struct nvme_queue *nvmeq, return; } + nvmeq->cqe_seen = 1; req = blk_mq_tag_to_rq(*nvmeq->tags, cqe->command_id); nvme_end_request(req, cqe->status, cqe->result); } @@ -813,10 +831,8 @@ static void nvme_process_cq(struct nvme_queue *nvmeq) consumed++; } - if (consumed) { + if (consumed) nvme_ring_cq_doorbell(nvmeq); - nvmeq->cqe_seen = 1; - } } static irqreturn_t nvme_irq(int irq, void *data) @@ -1541,11 +1557,9 @@ static inline void nvme_release_cmb(struct nvme_dev *dev) if (dev->cmb) { iounmap(dev->cmb); dev->cmb = NULL; - if (dev->cmbsz) { - sysfs_remove_file_from_group(&dev->ctrl.device->kobj, - &dev_attr_cmb.attr, NULL); - dev->cmbsz = 0; - } + sysfs_remove_file_from_group(&dev->ctrl.device->kobj, + &dev_attr_cmb.attr, NULL); + dev->cmbsz = 0; } } @@ -1602,7 +1616,7 @@ static void nvme_free_host_mem(struct nvme_dev *dev) static int nvme_alloc_host_mem(struct nvme_dev *dev, u64 min, u64 preferred) { struct nvme_host_mem_buf_desc *descs; - u32 chunk_size, max_entries; + u32 chunk_size, max_entries, len; int i = 0; void **bufs; u64 size = 0, tmp; @@ -1621,10 +1635,10 @@ retry: if (!bufs) goto out_free_descs; - for (size = 0; size < preferred; size += chunk_size) { - u32 len = min_t(u64, chunk_size, preferred - size); + for (size = 0; size < preferred; size += len) { dma_addr_t dma_addr; + len = min_t(u64, chunk_size, preferred - size); bufs[i] = dma_alloc_attrs(dev->dev, len, &dma_addr, GFP_KERNEL, DMA_ATTR_NO_KERNEL_MAPPING | DMA_ATTR_NO_WARN); if (!bufs[i]) @@ -1936,16 +1950,14 @@ static int nvme_pci_enable(struct nvme_dev *dev) /* * CMBs can currently only exist on >=1.2 PCIe devices. We only - * populate sysfs if a CMB is implemented. Note that we add the - * CMB attribute to the nvme_ctrl kobj which removes the need to remove - * it on exit. Since nvme_dev_attrs_group has no name we can pass - * NULL as final argument to sysfs_add_file_to_group. + * populate sysfs if a CMB is implemented. Since nvme_dev_attrs_group + * has no name we can pass NULL as final argument to + * sysfs_add_file_to_group. */ if (readl(dev->bar + NVME_REG_VS) >= NVME_VS(1, 2, 0)) { dev->cmb = nvme_map_cmb(dev); - - if (dev->cmbsz) { + if (dev->cmb) { if (sysfs_add_file_to_group(&dev->ctrl.device->kobj, &dev_attr_cmb.attr, NULL)) dev_warn(dev->ctrl.device, @@ -2282,7 +2294,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) result = nvme_dev_map(dev); if (result) - goto free; + goto put_pci; INIT_WORK(&dev->ctrl.reset_work, nvme_reset_work); INIT_WORK(&dev->remove_work, nvme_remove_dead_ctrl_work); @@ -2291,7 +2303,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) result = nvme_setup_prp_pools(dev); if (result) - goto put_pci; + goto unmap; quirks |= check_dell_samsung_bug(pdev); @@ -2308,9 +2320,10 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) release_pools: nvme_release_prp_pools(dev); + unmap: + nvme_dev_unmap(dev); put_pci: put_device(dev->dev); - nvme_dev_unmap(dev); free: kfree(dev->queues); kfree(dev); @@ -2466,6 +2479,9 @@ static const struct pci_device_id nvme_id_table[] = { { PCI_VDEVICE(INTEL, 0x0a54), .driver_data = NVME_QUIRK_STRIPE_SIZE | NVME_QUIRK_DEALLOCATE_ZEROES, }, + { PCI_VDEVICE(INTEL, 0x0a55), + .driver_data = NVME_QUIRK_STRIPE_SIZE | + NVME_QUIRK_DEALLOCATE_ZEROES, }, { PCI_VDEVICE(INTEL, 0xf1a5), /* Intel 600P/P3100 */ .driver_data = NVME_QUIRK_NO_DEEPEST_PS }, { PCI_VDEVICE(INTEL, 0x5845), /* Qemu emulated controller */ diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c index 35f930db3c02..a53bb6635b83 100644 --- a/drivers/nvme/target/admin-cmd.c +++ b/drivers/nvme/target/admin-cmd.c @@ -168,11 +168,21 @@ out: nvmet_req_complete(req, status); } +static void copy_and_pad(char *dst, int dst_len, const char *src, int src_len) +{ + int len = min(src_len, dst_len); + + memcpy(dst, src, len); + if (dst_len > len) + memset(dst + len, ' ', dst_len - len); +} + static void nvmet_execute_identify_ctrl(struct nvmet_req *req) { struct nvmet_ctrl *ctrl = req->sq->ctrl; struct nvme_id_ctrl *id; u16 status = 0; + const char model[] = "Linux"; id = kzalloc(sizeof(*id), GFP_KERNEL); if (!id) { @@ -184,14 +194,10 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req) id->vid = 0; id->ssvid = 0; - memset(id->sn, ' ', sizeof(id->sn)); - snprintf(id->sn, sizeof(id->sn), "%llx", ctrl->serial); - - memset(id->mn, ' ', sizeof(id->mn)); - strncpy((char *)id->mn, "Linux", sizeof(id->mn)); - - memset(id->fr, ' ', sizeof(id->fr)); - strncpy((char *)id->fr, UTS_RELEASE, sizeof(id->fr)); + bin2hex(id->sn, &ctrl->subsys->serial, + min(sizeof(ctrl->subsys->serial), sizeof(id->sn) / 2)); + copy_and_pad(id->mn, sizeof(id->mn), model, sizeof(model) - 1); + copy_and_pad(id->fr, sizeof(id->fr), UTS_RELEASE, strlen(UTS_RELEASE)); id->rab = 6; diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index a358ecd93e11..0a0067e771f5 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -650,7 +650,7 @@ out_unlock: CONFIGFS_ATTR(nvmet_subsys_, attr_allow_any_host); -static ssize_t nvmet_subsys_version_show(struct config_item *item, +static ssize_t nvmet_subsys_attr_version_show(struct config_item *item, char *page) { struct nvmet_subsys *subsys = to_subsys(item); @@ -666,7 +666,7 @@ static ssize_t nvmet_subsys_version_show(struct config_item *item, (int)NVME_MINOR(subsys->ver)); } -static ssize_t nvmet_subsys_version_store(struct config_item *item, +static ssize_t nvmet_subsys_attr_version_store(struct config_item *item, const char *page, size_t count) { struct nvmet_subsys *subsys = to_subsys(item); @@ -684,11 +684,33 @@ static ssize_t nvmet_subsys_version_store(struct config_item *item, return count; } -CONFIGFS_ATTR(nvmet_subsys_, version); +CONFIGFS_ATTR(nvmet_subsys_, attr_version); + +static ssize_t nvmet_subsys_attr_serial_show(struct config_item *item, + char *page) +{ + struct nvmet_subsys *subsys = to_subsys(item); + + return snprintf(page, PAGE_SIZE, "%llx\n", subsys->serial); +} + +static ssize_t nvmet_subsys_attr_serial_store(struct config_item *item, + const char *page, size_t count) +{ + struct nvmet_subsys *subsys = to_subsys(item); + + down_write(&nvmet_config_sem); + sscanf(page, "%llx\n", &subsys->serial); + up_write(&nvmet_config_sem); + + return count; +} +CONFIGFS_ATTR(nvmet_subsys_, attr_serial); static struct configfs_attribute *nvmet_subsys_attrs[] = { &nvmet_subsys_attr_attr_allow_any_host, - &nvmet_subsys_attr_version, + &nvmet_subsys_attr_attr_version, + &nvmet_subsys_attr_attr_serial, NULL, }; diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index b5b4ac103748..f4b02bb4a1a8 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -767,9 +767,6 @@ u16 nvmet_alloc_ctrl(const char *subsysnqn, const char *hostnqn, memcpy(ctrl->subsysnqn, subsysnqn, NVMF_NQN_SIZE); memcpy(ctrl->hostnqn, hostnqn, NVMF_NQN_SIZE); - /* generate a random serial number as our controllers are ephemeral: */ - get_random_bytes(&ctrl->serial, sizeof(ctrl->serial)); - kref_init(&ctrl->ref); ctrl->subsys = subsys; @@ -928,6 +925,8 @@ struct nvmet_subsys *nvmet_subsys_alloc(const char *subsysnqn, return NULL; subsys->ver = NVME_VS(1, 3, 0); /* NVMe 1.3.0 */ + /* generate a random serial number as our controllers are ephemeral: */ + get_random_bytes(&subsys->serial, sizeof(subsys->serial)); switch (type) { case NVME_NQN_NVME: diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c index 1e6dcc241b3c..309c84aa7595 100644 --- a/drivers/nvme/target/fc.c +++ b/drivers/nvme/target/fc.c @@ -114,6 +114,11 @@ struct nvmet_fc_tgtport { struct kref ref; }; +struct nvmet_fc_defer_fcp_req { + struct list_head req_list; + struct nvmefc_tgt_fcp_req *fcp_req; +}; + struct nvmet_fc_tgt_queue { bool ninetypercent; u16 qid; @@ -132,6 +137,8 @@ struct nvmet_fc_tgt_queue { struct nvmet_fc_tgt_assoc *assoc; struct nvmet_fc_fcp_iod *fod; /* array of fcp_iods */ struct list_head fod_list; + struct list_head pending_cmd_list; + struct list_head avail_defer_list; struct workqueue_struct *work_q; struct kref ref; } __aligned(sizeof(unsigned long long)); @@ -223,6 +230,8 @@ static void nvmet_fc_tgt_q_put(struct nvmet_fc_tgt_queue *queue); static int nvmet_fc_tgt_q_get(struct nvmet_fc_tgt_queue *queue); static void nvmet_fc_tgtport_put(struct nvmet_fc_tgtport *tgtport); static int nvmet_fc_tgtport_get(struct nvmet_fc_tgtport *tgtport); +static void nvmet_fc_handle_fcp_rqst(struct nvmet_fc_tgtport *tgtport, + struct nvmet_fc_fcp_iod *fod); /* *********************** FC-NVME DMA Handling **************************** */ @@ -385,7 +394,7 @@ nvmet_fc_free_ls_iodlist(struct nvmet_fc_tgtport *tgtport) static struct nvmet_fc_ls_iod * nvmet_fc_alloc_ls_iod(struct nvmet_fc_tgtport *tgtport) { - static struct nvmet_fc_ls_iod *iod; + struct nvmet_fc_ls_iod *iod; unsigned long flags; spin_lock_irqsave(&tgtport->lock, flags); @@ -462,10 +471,10 @@ nvmet_fc_destroy_fcp_iodlist(struct nvmet_fc_tgtport *tgtport, static struct nvmet_fc_fcp_iod * nvmet_fc_alloc_fcp_iod(struct nvmet_fc_tgt_queue *queue) { - static struct nvmet_fc_fcp_iod *fod; - unsigned long flags; + struct nvmet_fc_fcp_iod *fod; + + lockdep_assert_held(&queue->qlock); - spin_lock_irqsave(&queue->qlock, flags); fod = list_first_entry_or_null(&queue->fod_list, struct nvmet_fc_fcp_iod, fcp_list); if (fod) { @@ -477,17 +486,37 @@ nvmet_fc_alloc_fcp_iod(struct nvmet_fc_tgt_queue *queue) * will "inherit" that reference. */ } - spin_unlock_irqrestore(&queue->qlock, flags); return fod; } static void +nvmet_fc_queue_fcp_req(struct nvmet_fc_tgtport *tgtport, + struct nvmet_fc_tgt_queue *queue, + struct nvmefc_tgt_fcp_req *fcpreq) +{ + struct nvmet_fc_fcp_iod *fod = fcpreq->nvmet_fc_private; + + /* + * put all admin cmds on hw queue id 0. All io commands go to + * the respective hw queue based on a modulo basis + */ + fcpreq->hwqid = queue->qid ? + ((queue->qid - 1) % tgtport->ops->max_hw_queues) : 0; + + if (tgtport->ops->target_features & NVMET_FCTGTFEAT_CMD_IN_ISR) + queue_work_on(queue->cpu, queue->work_q, &fod->work); + else + nvmet_fc_handle_fcp_rqst(tgtport, fod); +} + +static void nvmet_fc_free_fcp_iod(struct nvmet_fc_tgt_queue *queue, struct nvmet_fc_fcp_iod *fod) { struct nvmefc_tgt_fcp_req *fcpreq = fod->fcpreq; struct nvmet_fc_tgtport *tgtport = fod->tgtport; + struct nvmet_fc_defer_fcp_req *deferfcp; unsigned long flags; fc_dma_sync_single_for_cpu(tgtport->dev, fod->rspdma, @@ -495,21 +524,56 @@ nvmet_fc_free_fcp_iod(struct nvmet_fc_tgt_queue *queue, fcpreq->nvmet_fc_private = NULL; - spin_lock_irqsave(&queue->qlock, flags); - list_add_tail(&fod->fcp_list, &fod->queue->fod_list); fod->active = false; fod->abort = false; fod->aborted = false; fod->writedataactive = false; fod->fcpreq = NULL; + + tgtport->ops->fcp_req_release(&tgtport->fc_target_port, fcpreq); + + spin_lock_irqsave(&queue->qlock, flags); + deferfcp = list_first_entry_or_null(&queue->pending_cmd_list, + struct nvmet_fc_defer_fcp_req, req_list); + if (!deferfcp) { + list_add_tail(&fod->fcp_list, &fod->queue->fod_list); + spin_unlock_irqrestore(&queue->qlock, flags); + + /* Release reference taken at queue lookup and fod allocation */ + nvmet_fc_tgt_q_put(queue); + return; + } + + /* Re-use the fod for the next pending cmd that was deferred */ + list_del(&deferfcp->req_list); + + fcpreq = deferfcp->fcp_req; + + /* deferfcp can be reused for another IO at a later date */ + list_add_tail(&deferfcp->req_list, &queue->avail_defer_list); + spin_unlock_irqrestore(&queue->qlock, flags); + /* Save NVME CMD IO in fod */ + memcpy(&fod->cmdiubuf, fcpreq->rspaddr, fcpreq->rsplen); + + /* Setup new fcpreq to be processed */ + fcpreq->rspaddr = NULL; + fcpreq->rsplen = 0; + fcpreq->nvmet_fc_private = fod; + fod->fcpreq = fcpreq; + fod->active = true; + + /* inform LLDD IO is now being processed */ + tgtport->ops->defer_rcv(&tgtport->fc_target_port, fcpreq); + + /* Submit deferred IO for processing */ + nvmet_fc_queue_fcp_req(tgtport, queue, fcpreq); + /* - * release the reference taken at queue lookup and fod allocation + * Leave the queue lookup get reference taken when + * fod was originally allocated. */ - nvmet_fc_tgt_q_put(queue); - - tgtport->ops->fcp_req_release(&tgtport->fc_target_port, fcpreq); } static int @@ -569,6 +633,8 @@ nvmet_fc_alloc_target_queue(struct nvmet_fc_tgt_assoc *assoc, queue->port = assoc->tgtport->port; queue->cpu = nvmet_fc_queue_to_cpu(assoc->tgtport, qid); INIT_LIST_HEAD(&queue->fod_list); + INIT_LIST_HEAD(&queue->avail_defer_list); + INIT_LIST_HEAD(&queue->pending_cmd_list); atomic_set(&queue->connected, 0); atomic_set(&queue->sqtail, 0); atomic_set(&queue->rsn, 1); @@ -638,6 +704,7 @@ nvmet_fc_delete_target_queue(struct nvmet_fc_tgt_queue *queue) { struct nvmet_fc_tgtport *tgtport = queue->assoc->tgtport; struct nvmet_fc_fcp_iod *fod = queue->fod; + struct nvmet_fc_defer_fcp_req *deferfcp, *tempptr; unsigned long flags; int i, writedataactive; bool disconnect; @@ -666,6 +733,36 @@ nvmet_fc_delete_target_queue(struct nvmet_fc_tgt_queue *queue) } } } + + /* Cleanup defer'ed IOs in queue */ + list_for_each_entry_safe(deferfcp, tempptr, &queue->avail_defer_list, + req_list) { + list_del(&deferfcp->req_list); + kfree(deferfcp); + } + + for (;;) { + deferfcp = list_first_entry_or_null(&queue->pending_cmd_list, + struct nvmet_fc_defer_fcp_req, req_list); + if (!deferfcp) + break; + + list_del(&deferfcp->req_list); + spin_unlock_irqrestore(&queue->qlock, flags); + + tgtport->ops->defer_rcv(&tgtport->fc_target_port, + deferfcp->fcp_req); + + tgtport->ops->fcp_abort(&tgtport->fc_target_port, + deferfcp->fcp_req); + + tgtport->ops->fcp_req_release(&tgtport->fc_target_port, + deferfcp->fcp_req); + + kfree(deferfcp); + + spin_lock_irqsave(&queue->qlock, flags); + } spin_unlock_irqrestore(&queue->qlock, flags); flush_workqueue(queue->work_q); @@ -1174,14 +1271,14 @@ nvmet_fc_ls_create_association(struct nvmet_fc_tgtport *tgtport, */ if (iod->rqstdatalen < FCNVME_LSDESC_CRA_RQST_MINLEN) ret = VERR_CR_ASSOC_LEN; - else if (rqst->desc_list_len < - cpu_to_be32(FCNVME_LSDESC_CRA_RQST_MIN_LISTLEN)) + else if (be32_to_cpu(rqst->desc_list_len) < + FCNVME_LSDESC_CRA_RQST_MIN_LISTLEN) ret = VERR_CR_ASSOC_RQST_LEN; else if (rqst->assoc_cmd.desc_tag != cpu_to_be32(FCNVME_LSDESC_CREATE_ASSOC_CMD)) ret = VERR_CR_ASSOC_CMD; - else if (rqst->assoc_cmd.desc_len < - cpu_to_be32(FCNVME_LSDESC_CRA_CMD_DESC_MIN_DESCLEN)) + else if (be32_to_cpu(rqst->assoc_cmd.desc_len) < + FCNVME_LSDESC_CRA_CMD_DESC_MIN_DESCLEN) ret = VERR_CR_ASSOC_CMD_LEN; else if (!rqst->assoc_cmd.ersp_ratio || (be16_to_cpu(rqst->assoc_cmd.ersp_ratio) >= @@ -2172,11 +2269,38 @@ nvmet_fc_handle_fcp_rqst_work(struct work_struct *work) * Pass a FC-NVME FCP CMD IU received from the FC link to the nvmet-fc * layer for processing. * - * The nvmet-fc layer will copy cmd payload to an internal structure for - * processing. As such, upon completion of the routine, the LLDD may - * immediately free/reuse the CMD IU buffer passed in the call. + * The nvmet_fc layer allocates a local job structure (struct + * nvmet_fc_fcp_iod) from the queue for the io and copies the + * CMD IU buffer to the job structure. As such, on a successful + * completion (returns 0), the LLDD may immediately free/reuse + * the CMD IU buffer passed in the call. + * + * However, in some circumstances, due to the packetized nature of FC + * and the api of the FC LLDD which may issue a hw command to send the + * response, but the LLDD may not get the hw completion for that command + * and upcall the nvmet_fc layer before a new command may be + * asynchronously received - its possible for a command to be received + * before the LLDD and nvmet_fc have recycled the job structure. It gives + * the appearance of more commands received than fits in the sq. + * To alleviate this scenario, a temporary queue is maintained in the + * transport for pending LLDD requests waiting for a queue job structure. + * In these "overrun" cases, a temporary queue element is allocated + * the LLDD request and CMD iu buffer information remembered, and the + * routine returns a -EOVERFLOW status. Subsequently, when a queue job + * structure is freed, it is immediately reallocated for anything on the + * pending request list. The LLDDs defer_rcv() callback is called, + * informing the LLDD that it may reuse the CMD IU buffer, and the io + * is then started normally with the transport. * - * If this routine returns error, the lldd should abort the exchange. + * The LLDD, when receiving an -EOVERFLOW completion status, is to treat + * the completion as successful but must not reuse the CMD IU buffer + * until the LLDD's defer_rcv() callback has been called for the + * corresponding struct nvmefc_tgt_fcp_req pointer. + * + * If there is any other condition in which an error occurs, the + * transport will return a non-zero status indicating the error. + * In all cases other than -EOVERFLOW, the transport has not accepted the + * request and the LLDD should abort the exchange. * * @target_port: pointer to the (registered) target port the FCP CMD IU * was received on. @@ -2194,6 +2318,8 @@ nvmet_fc_rcv_fcp_req(struct nvmet_fc_target_port *target_port, struct nvme_fc_cmd_iu *cmdiu = cmdiubuf; struct nvmet_fc_tgt_queue *queue; struct nvmet_fc_fcp_iod *fod; + struct nvmet_fc_defer_fcp_req *deferfcp; + unsigned long flags; /* validate iu, so the connection id can be used to find the queue */ if ((cmdiubuf_len != sizeof(*cmdiu)) || @@ -2214,29 +2340,60 @@ nvmet_fc_rcv_fcp_req(struct nvmet_fc_target_port *target_port, * when the fod is freed. */ + spin_lock_irqsave(&queue->qlock, flags); + fod = nvmet_fc_alloc_fcp_iod(queue); - if (!fod) { + if (fod) { + spin_unlock_irqrestore(&queue->qlock, flags); + + fcpreq->nvmet_fc_private = fod; + fod->fcpreq = fcpreq; + + memcpy(&fod->cmdiubuf, cmdiubuf, cmdiubuf_len); + + nvmet_fc_queue_fcp_req(tgtport, queue, fcpreq); + + return 0; + } + + if (!tgtport->ops->defer_rcv) { + spin_unlock_irqrestore(&queue->qlock, flags); /* release the queue lookup reference */ nvmet_fc_tgt_q_put(queue); return -ENOENT; } - fcpreq->nvmet_fc_private = fod; - fod->fcpreq = fcpreq; - /* - * put all admin cmds on hw queue id 0. All io commands go to - * the respective hw queue based on a modulo basis - */ - fcpreq->hwqid = queue->qid ? - ((queue->qid - 1) % tgtport->ops->max_hw_queues) : 0; - memcpy(&fod->cmdiubuf, cmdiubuf, cmdiubuf_len); + deferfcp = list_first_entry_or_null(&queue->avail_defer_list, + struct nvmet_fc_defer_fcp_req, req_list); + if (deferfcp) { + /* Just re-use one that was previously allocated */ + list_del(&deferfcp->req_list); + } else { + spin_unlock_irqrestore(&queue->qlock, flags); - if (tgtport->ops->target_features & NVMET_FCTGTFEAT_CMD_IN_ISR) - queue_work_on(queue->cpu, queue->work_q, &fod->work); - else - nvmet_fc_handle_fcp_rqst(tgtport, fod); + /* Now we need to dynamically allocate one */ + deferfcp = kmalloc(sizeof(*deferfcp), GFP_KERNEL); + if (!deferfcp) { + /* release the queue lookup reference */ + nvmet_fc_tgt_q_put(queue); + return -ENOMEM; + } + spin_lock_irqsave(&queue->qlock, flags); + } - return 0; + /* For now, use rspaddr / rsplen to save payload information */ + fcpreq->rspaddr = cmdiubuf; + fcpreq->rsplen = cmdiubuf_len; + deferfcp->fcp_req = fcpreq; + + /* defer processing till a fod becomes available */ + list_add_tail(&deferfcp->req_list, &queue->pending_cmd_list); + + /* NOTE: the queue lookup reference is still valid */ + + spin_unlock_irqrestore(&queue->qlock, flags); + + return -EOVERFLOW; } EXPORT_SYMBOL_GPL(nvmet_fc_rcv_fcp_req); @@ -2293,66 +2450,70 @@ nvmet_fc_rcv_fcp_abort(struct nvmet_fc_target_port *target_port, } EXPORT_SYMBOL_GPL(nvmet_fc_rcv_fcp_abort); -enum { - FCT_TRADDR_ERR = 0, - FCT_TRADDR_WWNN = 1 << 0, - FCT_TRADDR_WWPN = 1 << 1, -}; struct nvmet_fc_traddr { u64 nn; u64 pn; }; -static const match_table_t traddr_opt_tokens = { - { FCT_TRADDR_WWNN, "nn-%s" }, - { FCT_TRADDR_WWPN, "pn-%s" }, - { FCT_TRADDR_ERR, NULL } -}; - static int -nvmet_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf) +__nvme_fc_parse_u64(substring_t *sstr, u64 *val) { - substring_t args[MAX_OPT_ARGS]; - char *options, *o, *p; - int token, ret = 0; u64 token64; - options = o = kstrdup(buf, GFP_KERNEL); - if (!options) - return -ENOMEM; + if (match_u64(sstr, &token64)) + return -EINVAL; + *val = token64; - while ((p = strsep(&o, ":\n")) != NULL) { - if (!*p) - continue; + return 0; +} - token = match_token(p, traddr_opt_tokens, args); - switch (token) { - case FCT_TRADDR_WWNN: - if (match_u64(args, &token64)) { - ret = -EINVAL; - goto out; - } - traddr->nn = token64; - break; - case FCT_TRADDR_WWPN: - if (match_u64(args, &token64)) { - ret = -EINVAL; - goto out; - } - traddr->pn = token64; - break; - default: - pr_warn("unknown traddr token or missing value '%s'\n", - p); - ret = -EINVAL; - goto out; - } - } +/* + * This routine validates and extracts the WWN's from the TRADDR string. + * As kernel parsers need the 0x to determine number base, universally + * build string to parse with 0x prefix before parsing name strings. + */ +static int +nvme_fc_parse_traddr(struct nvmet_fc_traddr *traddr, char *buf, size_t blen) +{ + char name[2 + NVME_FC_TRADDR_HEXNAMELEN + 1]; + substring_t wwn = { name, &name[sizeof(name)-1] }; + int nnoffset, pnoffset; + + /* validate it string one of the 2 allowed formats */ + if (strnlen(buf, blen) == NVME_FC_TRADDR_MAXLENGTH && + !strncmp(buf, "nn-0x", NVME_FC_TRADDR_OXNNLEN) && + !strncmp(&buf[NVME_FC_TRADDR_MAX_PN_OFFSET], + "pn-0x", NVME_FC_TRADDR_OXNNLEN)) { + nnoffset = NVME_FC_TRADDR_OXNNLEN; + pnoffset = NVME_FC_TRADDR_MAX_PN_OFFSET + + NVME_FC_TRADDR_OXNNLEN; + } else if ((strnlen(buf, blen) == NVME_FC_TRADDR_MINLENGTH && + !strncmp(buf, "nn-", NVME_FC_TRADDR_NNLEN) && + !strncmp(&buf[NVME_FC_TRADDR_MIN_PN_OFFSET], + "pn-", NVME_FC_TRADDR_NNLEN))) { + nnoffset = NVME_FC_TRADDR_NNLEN; + pnoffset = NVME_FC_TRADDR_MIN_PN_OFFSET + NVME_FC_TRADDR_NNLEN; + } else + goto out_einval; + + name[0] = '0'; + name[1] = 'x'; + name[2 + NVME_FC_TRADDR_HEXNAMELEN] = 0; + + memcpy(&name[2], &buf[nnoffset], NVME_FC_TRADDR_HEXNAMELEN); + if (__nvme_fc_parse_u64(&wwn, &traddr->nn)) + goto out_einval; + + memcpy(&name[2], &buf[pnoffset], NVME_FC_TRADDR_HEXNAMELEN); + if (__nvme_fc_parse_u64(&wwn, &traddr->pn)) + goto out_einval; -out: - kfree(options); - return ret; + return 0; + +out_einval: + pr_warn("%s: bad traddr string\n", __func__); + return -EINVAL; } static int @@ -2370,7 +2531,8 @@ nvmet_fc_add_port(struct nvmet_port *port) /* map the traddr address info to a target port */ - ret = nvmet_fc_parse_traddr(&traddr, port->disc_addr.traddr); + ret = nvme_fc_parse_traddr(&traddr, port->disc_addr.traddr, + sizeof(port->disc_addr.traddr)); if (ret) return ret; diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index 747bbdb4f9c6..e3b244c7e443 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -112,7 +112,6 @@ struct nvmet_ctrl { struct mutex lock; u64 cap; - u64 serial; u32 cc; u32 csts; @@ -152,6 +151,7 @@ struct nvmet_subsys { u16 max_qid; u64 ver; + u64 serial; char *subsysnqn; struct config_group group; diff --git a/drivers/nvmem/rockchip-efuse.c b/drivers/nvmem/rockchip-efuse.c index a0d4ede9b8fc..63e3eb55f3ac 100644 --- a/drivers/nvmem/rockchip-efuse.c +++ b/drivers/nvmem/rockchip-efuse.c @@ -170,7 +170,7 @@ static const struct of_device_id rockchip_efuse_match[] = { .data = (void *)&rockchip_rk3288_efuse_read, }, { - .compatible = "rockchip,rk322x-efuse", + .compatible = "rockchip,rk3228-efuse", .data = (void *)&rockchip_rk3288_efuse_read, }, { diff --git a/drivers/of/device.c b/drivers/of/device.c index 28c38c756f92..e0a28ea341fe 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -89,6 +89,7 @@ int of_dma_configure(struct device *dev, struct device_node *np) bool coherent; unsigned long offset; const struct iommu_ops *iommu; + u64 mask; /* * Set default coherent_dma_mask to 32 bit. Drivers are expected to @@ -134,10 +135,9 @@ int of_dma_configure(struct device *dev, struct device_node *np) * Limit coherent and dma mask based on size and default mask * set by the driver. */ - dev->coherent_dma_mask = min(dev->coherent_dma_mask, - DMA_BIT_MASK(ilog2(dma_addr + size))); - *dev->dma_mask = min((*dev->dma_mask), - DMA_BIT_MASK(ilog2(dma_addr + size))); + mask = DMA_BIT_MASK(ilog2(dma_addr + size - 1) + 1); + dev->coherent_dma_mask &= mask; + *dev->dma_mask &= mask; coherent = of_dma_is_coherent(np); dev_dbg(dev, "device is%sdma coherent\n", diff --git a/drivers/of/irq.c b/drivers/of/irq.c index 6ce72aa65425..ab21c846eb27 100644 --- a/drivers/of/irq.c +++ b/drivers/of/irq.c @@ -476,7 +476,7 @@ int of_irq_to_resource_table(struct device_node *dev, struct resource *res, int i; for (i = 0; i < nr_irqs; i++, res++) - if (!of_irq_to_resource(dev, i, res)) + if (of_irq_to_resource(dev, i, res) <= 0) break; return i; diff --git a/drivers/of/property.c b/drivers/of/property.c index eda50b4be934..067f9fab7b77 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -708,6 +708,15 @@ struct device_node *of_graph_get_port_parent(struct device_node *node) { unsigned int depth; + if (!node) + return NULL; + + /* + * Preserve usecount for passed in node as of_get_next_parent() + * will do of_node_put() on it. + */ + of_node_get(node); + /* Walk 3 levels up only if there is 'ports' node. */ for (depth = 3; depth && node; depth--) { node = of_get_next_parent(node); @@ -728,12 +737,16 @@ EXPORT_SYMBOL(of_graph_get_port_parent); struct device_node *of_graph_get_remote_port_parent( const struct device_node *node) { - struct device_node *np; + struct device_node *np, *pp; /* Get remote endpoint node. */ np = of_graph_get_remote_endpoint(node); - return of_graph_get_port_parent(np); + pp = of_graph_get_port_parent(np); + + of_node_put(np); + + return pp; } EXPORT_SYMBOL(of_graph_get_remote_port_parent); diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index 5c63b920b471..ed92c1254cff 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -956,7 +956,7 @@ static int __init dino_probe(struct parisc_device *dev) dino_dev->hba.dev = dev; dino_dev->hba.base_addr = ioremap_nocache(hpa, 4096); - dino_dev->hba.lmmio_space_offset = 0; /* CPU addrs == bus addrs */ + dino_dev->hba.lmmio_space_offset = PCI_F_EXTEND; spin_lock_init(&dino_dev->dinosaur_pen); dino_dev->hba.iommu = ccio_get_iommu(dev); diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c index 055f83fddc18..b1ff46fe4547 100644 --- a/drivers/parisc/pdc_stable.c +++ b/drivers/parisc/pdc_stable.c @@ -333,11 +333,11 @@ pdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t coun /* Update the symlink to the real device */ sysfs_remove_link(&entry->kobj, "device"); + write_unlock(&entry->rw_lock); + ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); WARN_ON(ret); - write_unlock(&entry->rw_lock); - printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"\n", entry->name, buf); @@ -954,7 +954,7 @@ static struct attribute *pdcs_subsys_attrs[] = { NULL, }; -static struct attribute_group pdcs_attr_group = { +static const struct attribute_group pdcs_attr_group = { .attrs = pdcs_subsys_attrs, }; @@ -998,6 +998,7 @@ pdcs_register_pathentries(void) /* kobject is now registered */ write_lock(&entry->rw_lock); entry->ready = 2; + write_unlock(&entry->rw_lock); /* Add a nice symlink to the real device */ if (entry->dev) { @@ -1005,7 +1006,6 @@ pdcs_register_pathentries(void) WARN_ON(err); } - write_unlock(&entry->rw_lock); kobject_uevent(&entry->kobj, KOBJ_ADD); } diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 253d92409bb3..2225afc1cbbb 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -538,12 +538,9 @@ msi_setup_entry(struct pci_dev *dev, int nvec, const struct irq_affinity *affd) struct msi_desc *entry; u16 control; - if (affd) { + if (affd) masks = irq_create_affinity_masks(nvec, affd); - if (!masks) - dev_err(&dev->dev, "can't allocate MSI affinity masks for %d vectors\n", - nvec); - } + /* MSI Entry Initialization */ entry = alloc_msi_entry(&dev->dev, nvec, masks); @@ -679,12 +676,8 @@ static int msix_setup_entries(struct pci_dev *dev, void __iomem *base, struct msi_desc *entry; int ret, i; - if (affd) { + if (affd) masks = irq_create_affinity_masks(nvec, affd); - if (!masks) - dev_err(&dev->dev, "can't allocate MSI-X affinity masks for %d vectors\n", - nvec); - } for (i = 0, curmsk = masks; i < nvec; i++) { entry = alloc_msi_entry(&dev->dev, 1, curmsk); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index af0cc3456dc1..fdf65a6c13f6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -514,7 +514,7 @@ EXPORT_SYMBOL(pci_find_resource); */ struct pci_dev *pci_find_pcie_root_port(struct pci_dev *dev) { - struct pci_dev *bridge, *highest_pcie_bridge = NULL; + struct pci_dev *bridge, *highest_pcie_bridge = dev; bridge = pci_upstream_bridge(dev); while (bridge && pci_is_pcie(bridge)) { @@ -4260,6 +4260,41 @@ int pci_reset_function(struct pci_dev *dev) EXPORT_SYMBOL_GPL(pci_reset_function); /** + * pci_reset_function_locked - quiesce and 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. + * + * 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. It also differs from pci_reset_function() in that it + * requires the PCI device lock to be held. + * + * 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_locked(struct pci_dev *dev) +{ + int rc; + + rc = pci_probe_reset_function(dev); + if (rc) + return rc; + + pci_dev_save_and_disable(dev); + + rc = __pci_reset_function_locked(dev); + + pci_dev_restore(dev); + + return rc; +} +EXPORT_SYMBOL_GPL(pci_reset_function_locked); + +/** * pci_try_reset_function - quiesce and reset a PCI device function * @dev: PCI device to reset * diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index c31310db0404..e6a917b4acd3 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1762,6 +1762,48 @@ static void pci_configure_extended_tags(struct pci_dev *dev) PCI_EXP_DEVCTL_EXT_TAG); } +/** + * pcie_relaxed_ordering_enabled - Probe for PCIe relaxed ordering enable + * @dev: PCI device to query + * + * Returns true if the device has enabled relaxed ordering attribute. + */ +bool pcie_relaxed_ordering_enabled(struct pci_dev *dev) +{ + u16 v; + + pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &v); + + return !!(v & PCI_EXP_DEVCTL_RELAX_EN); +} +EXPORT_SYMBOL(pcie_relaxed_ordering_enabled); + +static void pci_configure_relaxed_ordering(struct pci_dev *dev) +{ + struct pci_dev *root; + + /* PCI_EXP_DEVICE_RELAX_EN is RsvdP in VFs */ + if (dev->is_virtfn) + return; + + if (!pcie_relaxed_ordering_enabled(dev)) + return; + + /* + * For now, we only deal with Relaxed Ordering issues with Root + * Ports. Peer-to-Peer DMA is another can of worms. + */ + root = pci_find_pcie_root_port(dev); + if (!root) + return; + + if (root->dev_flags & PCI_DEV_FLAGS_NO_RELAXED_ORDERING) { + pcie_capability_clear_word(dev, PCI_EXP_DEVCTL, + PCI_EXP_DEVCTL_RELAX_EN); + dev_info(&dev->dev, "Disable Relaxed Ordering because the Root Port didn't support it\n"); + } +} + static void pci_configure_device(struct pci_dev *dev) { struct hotplug_params hpp; @@ -1769,6 +1811,7 @@ static void pci_configure_device(struct pci_dev *dev) pci_configure_mps(dev); pci_configure_extended_tags(dev); + pci_configure_relaxed_ordering(dev); memset(&hpp, 0, sizeof(hpp)); ret = pci_get_hp_params(dev, &hpp); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 6967c6b4cf6b..140760403f36 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4016,6 +4016,95 @@ DECLARE_PCI_FIXUP_CLASS_EARLY(0x1797, 0x6869, PCI_CLASS_NOT_DEFINED, 8, quirk_tw686x_class); /* + * Some devices have problems with Transaction Layer Packets with the Relaxed + * Ordering Attribute set. Such devices should mark themselves and other + * Device Drivers should check before sending TLPs with RO set. + */ +static void quirk_relaxedordering_disable(struct pci_dev *dev) +{ + dev->dev_flags |= PCI_DEV_FLAGS_NO_RELAXED_ORDERING; + dev_info(&dev->dev, "Disable Relaxed Ordering Attributes to avoid PCIe Completion erratum\n"); +} + +/* + * Intel Xeon processors based on Broadwell/Haswell microarchitecture Root + * Complex has a Flow Control Credit issue which can cause performance + * problems with Upstream Transaction Layer Packets with Relaxed Ordering set. + */ +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f01, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f02, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f03, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f04, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f05, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f06, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f07, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f08, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f09, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f0a, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f0b, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f0c, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f0d, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x6f0e, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f01, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f02, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f03, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f04, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f05, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f06, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f07, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f08, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f09, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f0a, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f0b, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f0c, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f0d, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_INTEL, 0x2f0e, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); + +/* + * The AMD ARM A1100 (AKA "SEATTLE") SoC has a bug in its PCIe Root Complex + * where Upstream Transaction Layer Packets with the Relaxed Ordering + * Attribute clear are allowed to bypass earlier TLPs with Relaxed Ordering + * set. This is a violation of the PCIe 3.0 Transaction Ordering Rules + * outlined in Section 2.4.1 (PCI Express(r) Base Specification Revision 3.0 + * November 10, 2010). As a result, on this platform we can't use Relaxed + * Ordering for Upstream TLPs. + */ +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AMD, 0x1a00, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AMD, 0x1a01, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); +DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AMD, 0x1a02, PCI_CLASS_NOT_DEFINED, 8, + quirk_relaxedordering_disable); + +/* * Per PCIe r3.0, sec 2.2.9, "Completion headers must supply the same * values for the Attribute as were supplied in the header of the * corresponding Request, except as explicitly allowed when IDO is used." diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index dc459eb1246b..1c5e0f333779 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -569,22 +569,41 @@ int armpmu_request_irq(struct arm_pmu *armpmu, int cpu) if (irq != other_irq) { pr_warn("mismatched PPIs detected.\n"); err = -EINVAL; + goto err_out; } } else { - err = request_irq(irq, handler, - IRQF_NOBALANCING | IRQF_NO_THREAD, "arm-pmu", + struct arm_pmu_platdata *platdata = armpmu_get_platdata(armpmu); + unsigned long irq_flags; + + err = irq_force_affinity(irq, cpumask_of(cpu)); + + if (err && num_possible_cpus() > 1) { + pr_warn("unable to set irq affinity (irq=%d, cpu=%u)\n", + irq, cpu); + goto err_out; + } + + if (platdata && platdata->irq_flags) { + irq_flags = platdata->irq_flags; + } else { + irq_flags = IRQF_PERCPU | + IRQF_NOBALANCING | + IRQF_NO_THREAD; + } + + err = request_irq(irq, handler, irq_flags, "arm-pmu", per_cpu_ptr(&hw_events->percpu_pmu, cpu)); } - if (err) { - pr_err("unable to request IRQ%d for ARM PMU counters\n", - irq); - return err; - } + if (err) + goto err_out; cpumask_set_cpu(cpu, &armpmu->active_irqs); - return 0; + +err_out: + pr_err("unable to request IRQ%d for ARM PMU counters\n", irq); + return err; } int armpmu_request_irqs(struct arm_pmu *armpmu) @@ -628,12 +647,6 @@ static int arm_perf_starting_cpu(unsigned int cpu, struct hlist_node *node) enable_percpu_irq(irq, IRQ_TYPE_NONE); return 0; } - - if (irq_force_affinity(irq, cpumask_of(cpu)) && - num_possible_cpus() > 1) { - pr_warn("unable to set irq affinity (irq=%d, cpu=%u)\n", - irq, cpu); - } } return 0; diff --git a/drivers/perf/arm_pmu_platform.c b/drivers/perf/arm_pmu_platform.c index 69255f53057a..4eafa7a42e52 100644 --- a/drivers/perf/arm_pmu_platform.c +++ b/drivers/perf/arm_pmu_platform.c @@ -131,8 +131,8 @@ static int pmu_parse_irqs(struct arm_pmu *pmu) } if (!pmu_has_irq_affinity(pdev->dev.of_node)) { - pr_warn("no interrupt-affinity property for %s, guessing.\n", - of_node_full_name(pdev->dev.of_node)); + pr_warn("no interrupt-affinity property for %pOF, guessing.\n", + pdev->dev.of_node); } /* @@ -211,7 +211,7 @@ int arm_pmu_device_probe(struct platform_device *pdev, } if (ret) { - pr_info("%s: failed to probe PMU!\n", of_node_full_name(node)); + pr_info("%pOF: failed to probe PMU!\n", node); goto out_free; } @@ -228,8 +228,7 @@ int arm_pmu_device_probe(struct platform_device *pdev, out_free_irqs: armpmu_free_irqs(pmu); out_free: - pr_info("%s: failed to register PMU devices!\n", - of_node_full_name(node)); + pr_info("%pOF: failed to register PMU devices!\n", node); armpmu_free(pmu); return ret; } diff --git a/drivers/perf/qcom_l2_pmu.c b/drivers/perf/qcom_l2_pmu.c index c259848228b4..b242cce10468 100644 --- a/drivers/perf/qcom_l2_pmu.c +++ b/drivers/perf/qcom_l2_pmu.c @@ -546,6 +546,7 @@ static int l2_cache_event_init(struct perf_event *event) } if ((event != event->group_leader) && + !is_software_event(event->group_leader) && (L2_EVT_GROUP(event->group_leader->attr.config) == L2_EVT_GROUP(event->attr.config))) { dev_dbg_ratelimited(&l2cache_pmu->pdev->dev, @@ -558,6 +559,7 @@ static int l2_cache_event_init(struct perf_event *event) list_for_each_entry(sibling, &event->group_leader->sibling_list, group_entry) { if ((sibling != event) && + !is_software_event(sibling) && (L2_EVT_GROUP(sibling->attr.config) == L2_EVT_GROUP(event->attr.config))) { dev_dbg_ratelimited(&l2cache_pmu->pdev->dev, diff --git a/drivers/phy/broadcom/Kconfig b/drivers/phy/broadcom/Kconfig index 37371b89b14f..64fc59c3ae6d 100644 --- a/drivers/phy/broadcom/Kconfig +++ b/drivers/phy/broadcom/Kconfig @@ -30,8 +30,8 @@ config PHY_BCM_NS_USB3 tristate "Broadcom Northstar USB 3.0 PHY Driver" depends on ARCH_BCM_IPROC || COMPILE_TEST depends on HAS_IOMEM && OF + depends on MDIO_BUS select GENERIC_PHY - select MDIO_DEVICE help Enable this to support Broadcom USB 3.0 PHY connected to the USB controller on Northstar family. diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c index 20f1b4493994..04e929fd0ffe 100644 --- a/drivers/pinctrl/intel/pinctrl-cherryview.c +++ b/drivers/pinctrl/intel/pinctrl-cherryview.c @@ -1548,6 +1548,13 @@ static const struct dmi_system_id chv_no_valid_mask[] = { }, }, { + .ident = "HP Chromebook 11 G5 (Setzer)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"), + }, + }, + { .ident = "Acer Chromebook R11 (Cyan)", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), diff --git a/drivers/pinctrl/intel/pinctrl-merrifield.c b/drivers/pinctrl/intel/pinctrl-merrifield.c index 4d4ef42a39b5..86c4b3fab7b0 100644 --- a/drivers/pinctrl/intel/pinctrl-merrifield.c +++ b/drivers/pinctrl/intel/pinctrl-merrifield.c @@ -343,9 +343,9 @@ static const struct pinctrl_pin_desc mrfld_pins[] = { static const unsigned int mrfld_sdio_pins[] = { 50, 51, 52, 53, 54, 55, 56 }; static const unsigned int mrfld_spi5_pins[] = { 90, 91, 92, 93, 94, 95, 96 }; -static const unsigned int mrfld_uart0_pins[] = { 124, 125, 126, 127 }; -static const unsigned int mrfld_uart1_pins[] = { 128, 129, 130, 131 }; -static const unsigned int mrfld_uart2_pins[] = { 132, 133, 134, 135 }; +static const unsigned int mrfld_uart0_pins[] = { 115, 116, 117, 118 }; +static const unsigned int mrfld_uart1_pins[] = { 119, 120, 121, 122 }; +static const unsigned int mrfld_uart2_pins[] = { 123, 124, 125, 126 }; static const unsigned int mrfld_pwm0_pins[] = { 144 }; static const unsigned int mrfld_pwm1_pins[] = { 145 }; static const unsigned int mrfld_pwm2_pins[] = { 132 }; diff --git a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c index f024e25787fc..0c6d7812d6fd 100644 --- a/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c +++ b/drivers/pinctrl/mvebu/pinctrl-armada-37xx.c @@ -37,7 +37,7 @@ #define IRQ_STATUS 0x10 #define IRQ_WKUP 0x18 -#define NB_FUNCS 2 +#define NB_FUNCS 3 #define GPIO_PER_REG 32 /** @@ -126,6 +126,16 @@ struct armada_37xx_pinctrl { .funcs = {_func1, "gpio"} \ } +#define PIN_GRP_GPIO_3(_name, _start, _nr, _mask, _v1, _v2, _v3, _f1, _f2) \ + { \ + .name = _name, \ + .start_pin = _start, \ + .npins = _nr, \ + .reg_mask = _mask, \ + .val = {_v1, _v2, _v3}, \ + .funcs = {_f1, _f2, "gpio"} \ + } + #define PIN_GRP_EXTRA(_name, _start, _nr, _mask, _v1, _v2, _start2, _nr2, \ _f1, _f2) \ { \ @@ -171,12 +181,13 @@ static struct armada_37xx_pin_group armada_37xx_sb_groups[] = { PIN_GRP_GPIO("usb32_drvvbus0", 0, 1, BIT(0), "drvbus"), PIN_GRP_GPIO("usb2_drvvbus1", 1, 1, BIT(1), "drvbus"), PIN_GRP_GPIO("sdio_sb", 24, 6, BIT(2), "sdio"), - PIN_GRP_EXTRA("rgmii", 6, 12, BIT(3), 0, BIT(3), 23, 1, "mii", "gpio"), + PIN_GRP_GPIO("rgmii", 6, 12, BIT(3), "mii"), PIN_GRP_GPIO("pcie1", 3, 2, BIT(4), "pcie"), PIN_GRP_GPIO("ptp", 20, 3, BIT(5), "ptp"), PIN_GRP("ptp_clk", 21, 1, BIT(6), "ptp", "mii"), PIN_GRP("ptp_trig", 22, 1, BIT(7), "ptp", "mii"), - PIN_GRP("mii_col", 23, 1, BIT(8), "mii", "mii_err"), + PIN_GRP_GPIO_3("mii_col", 23, 1, BIT(8) | BIT(14), 0, BIT(8), BIT(14), + "mii", "mii_err"), }; const struct armada_37xx_pin_data armada_37xx_pin_nb = { @@ -187,7 +198,7 @@ const struct armada_37xx_pin_data armada_37xx_pin_nb = { }; const struct armada_37xx_pin_data armada_37xx_pin_sb = { - .nr_pins = 29, + .nr_pins = 30, .name = "GPIO2", .groups = armada_37xx_sb_groups, .ngroups = ARRAY_SIZE(armada_37xx_sb_groups), @@ -208,7 +219,7 @@ static int armada_37xx_get_func_reg(struct armada_37xx_pin_group *grp, { int f; - for (f = 0; f < NB_FUNCS; f++) + for (f = 0; (f < NB_FUNCS) && grp->funcs[f]; f++) if (!strcmp(grp->funcs[f], func)) return f; @@ -795,7 +806,7 @@ static int armada_37xx_fill_group(struct armada_37xx_pinctrl *info) for (j = 0; j < grp->extra_npins; j++) grp->pins[i+j] = grp->extra_pin + j; - for (f = 0; f < NB_FUNCS; f++) { + for (f = 0; (f < NB_FUNCS) && grp->funcs[f]; f++) { int ret; /* check for unique functions and count groups */ ret = armada_37xx_add_function(info->funcs, &funcsize, @@ -847,7 +858,7 @@ static int armada_37xx_fill_func(struct armada_37xx_pinctrl *info) struct armada_37xx_pin_group *gp = &info->groups[g]; int f; - for (f = 0; f < NB_FUNCS; f++) { + for (f = 0; (f < NB_FUNCS) && gp->funcs[f]; f++) { if (strcmp(gp->funcs[f], name) == 0) { *groups = gp->name; groups++; diff --git a/drivers/pinctrl/stm32/Kconfig b/drivers/pinctrl/stm32/Kconfig index 3b8026fca057..7e1fe39a56a5 100644 --- a/drivers/pinctrl/stm32/Kconfig +++ b/drivers/pinctrl/stm32/Kconfig @@ -6,29 +6,30 @@ config PINCTRL_STM32 select PINMUX select GENERIC_PINCONF select GPIOLIB + select IRQ_DOMAIN_HIERARCHY select MFD_SYSCON config PINCTRL_STM32F429 bool "STMicroelectronics STM32F429 pin control" if COMPILE_TEST && !MACH_STM32F429 - depends on OF && IRQ_DOMAIN_HIERARCHY + depends on OF default MACH_STM32F429 select PINCTRL_STM32 config PINCTRL_STM32F469 bool "STMicroelectronics STM32F469 pin control" if COMPILE_TEST && !MACH_STM32F469 - depends on OF && IRQ_DOMAIN_HIERARCHY + depends on OF default MACH_STM32F469 select PINCTRL_STM32 config PINCTRL_STM32F746 bool "STMicroelectronics STM32F746 pin control" if COMPILE_TEST && !MACH_STM32F746 - depends on OF && IRQ_DOMAIN_HIERARCHY + depends on OF default MACH_STM32F746 select PINCTRL_STM32 config PINCTRL_STM32H743 bool "STMicroelectronics STM32H743 pin control" if COMPILE_TEST && !MACH_STM32H743 - depends on OF && IRQ_DOMAIN_HIERARCHY + depends on OF default MACH_STM32H743 select PINCTRL_STM32 endif diff --git a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c index 159580c04b14..47a392bc73c8 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun4i-a10.c @@ -918,6 +918,7 @@ static const struct sunxi_desc_pin sun4i_a10_pins[] = { SUNXI_FUNCTION_VARIANT(0x3, "emac", /* ETXD1 */ PINCTRL_SUN7I_A20), SUNXI_FUNCTION(0x4, "keypad"), /* IN6 */ + SUNXI_FUNCTION(0x5, "sim"), /* DET */ SUNXI_FUNCTION_IRQ(0x6, 16), /* EINT16 */ SUNXI_FUNCTION(0x7, "csi1")), /* D16 */ SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 17), diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c index a433a306a2d0..c75e094b2d90 100644 --- a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c +++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro4.c @@ -1084,7 +1084,7 @@ static const unsigned usb1_pins[] = {182, 183}; static const int usb1_muxvals[] = {0, 0}; static const unsigned usb2_pins[] = {184, 185}; static const int usb2_muxvals[] = {0, 0}; -static const unsigned usb3_pins[] = {186, 187}; +static const unsigned usb3_pins[] = {187, 188}; static const int usb3_muxvals[] = {0, 0}; static const unsigned port_range0_pins[] = { 300, 301, 302, 303, 304, 305, 306, 307, /* PORT0x */ diff --git a/drivers/pinctrl/zte/pinctrl-zx.c b/drivers/pinctrl/zte/pinctrl-zx.c index 787e3967bd5c..f828ee340a98 100644 --- a/drivers/pinctrl/zte/pinctrl-zx.c +++ b/drivers/pinctrl/zte/pinctrl-zx.c @@ -64,10 +64,8 @@ static int zx_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, struct zx_pinctrl_soc_info *info = zpctl->info; const struct pinctrl_pin_desc *pindesc = info->pins + group_selector; struct zx_pin_data *data = pindesc->drv_data; - struct zx_mux_desc *mux = data->muxes; - u32 mask = (1 << data->width) - 1; - u32 offset = data->offset; - u32 bitpos = data->bitpos; + struct zx_mux_desc *mux; + u32 mask, offset, bitpos; struct function_desc *func; unsigned long flags; u32 val, mval; @@ -76,6 +74,11 @@ static int zx_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, if (!data) return -EINVAL; + mux = data->muxes; + mask = (1 << data->width) - 1; + offset = data->offset; + bitpos = data->bitpos; + func = pinmux_generic_get_function(pctldev, func_selector); if (!func) return -EINVAL; diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index b04860703740..80b87954f6dd 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -675,6 +675,7 @@ config PEAQ_WMI tristate "PEAQ 2-in-1 WMI hotkey driver" depends on ACPI_WMI depends on INPUT + select INPUT_POLLDEV help Say Y here if you want to support WMI-based hotkeys on PEAQ 2-in-1s. diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index f8978464df31..dad8f4afa17c 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -626,7 +626,7 @@ static void dell_wmi_input_destroy(struct wmi_device *wdev) * WMI Interface Version 8 4 <version> * WMI buffer length 12 4 4096 */ -static int __init dell_wmi_check_descriptor_buffer(void) +static int dell_wmi_check_descriptor_buffer(void) { struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; @@ -717,9 +717,15 @@ static int dell_wmi_events_set_enabled(bool enable) static int dell_wmi_probe(struct wmi_device *wdev) { + int err; + struct dell_wmi_priv *priv = devm_kzalloc( &wdev->dev, sizeof(struct dell_wmi_priv), GFP_KERNEL); + err = dell_wmi_check_descriptor_buffer(); + if (err) + return err; + dev_set_drvdata(&wdev->dev, priv); return dell_wmi_input_setup(wdev); @@ -749,10 +755,6 @@ static int __init dell_wmi_init(void) { int err; - err = dell_wmi_check_descriptor_buffer(); - if (err) - return err; - dmi_check_system(dell_wmi_smbios_list); if (wmi_requires_smbios_request) { diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c index 61f106377661..480926786cb8 100644 --- a/drivers/platform/x86/intel-vbtn.c +++ b/drivers/platform/x86/intel-vbtn.c @@ -36,8 +36,8 @@ static const struct acpi_device_id intel_vbtn_ids[] = { /* In theory, these are HID usages. */ static const struct key_entry intel_vbtn_keymap[] = { - { KE_IGNORE, 0xC0, { KEY_POWER } }, /* power key press */ - { KE_KEY, 0xC1, { KEY_POWER } }, /* power key release */ + { KE_KEY, 0xC0, { KEY_POWER } }, /* power key press */ + { KE_IGNORE, 0xC1, { KEY_POWER } }, /* power key release */ { KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* volume-up key press */ { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* volume-up key release */ { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* volume-down key press */ diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 1a764e311e11..e32ba575e8d9 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -1252,12 +1252,12 @@ static int __init acpi_wmi_init(void) return 0; -err_unreg_class: - class_unregister(&wmi_bus_class); - err_unreg_bus: bus_unregister(&wmi_bus_type); +err_unreg_class: + class_unregister(&wmi_bus_class); + return error; } diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index b77435783ef3..7eacc1c4b3b1 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -28,6 +28,7 @@ #include <linux/slab.h> #include <linux/syscalls.h> #include <linux/uaccess.h> +#include <uapi/linux/sched/types.h> #include "ptp_private.h" @@ -184,6 +185,19 @@ static void delete_ptp_clock(struct posix_clock *pc) kfree(ptp); } +static void ptp_aux_kworker(struct kthread_work *work) +{ + struct ptp_clock *ptp = container_of(work, struct ptp_clock, + aux_work.work); + struct ptp_clock_info *info = ptp->info; + long delay; + + delay = info->do_aux_work(info); + + if (delay >= 0) + kthread_queue_delayed_work(ptp->kworker, &ptp->aux_work, delay); +} + /* public interface */ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, @@ -217,6 +231,20 @@ struct ptp_clock *ptp_clock_register(struct ptp_clock_info *info, mutex_init(&ptp->pincfg_mux); init_waitqueue_head(&ptp->tsev_wq); + if (ptp->info->do_aux_work) { + char *worker_name = kasprintf(GFP_KERNEL, "ptp%d", ptp->index); + + kthread_init_delayed_work(&ptp->aux_work, ptp_aux_kworker); + ptp->kworker = kthread_create_worker(0, worker_name ? + worker_name : info->name); + kfree(worker_name); + if (IS_ERR(ptp->kworker)) { + err = PTR_ERR(ptp->kworker); + pr_err("failed to create ptp aux_worker %d\n", err); + goto kworker_err; + } + } + err = ptp_populate_pin_groups(ptp); if (err) goto no_pin_groups; @@ -259,6 +287,9 @@ no_pps: no_device: ptp_cleanup_pin_groups(ptp); no_pin_groups: + if (ptp->kworker) + kthread_destroy_worker(ptp->kworker); +kworker_err: mutex_destroy(&ptp->tsevq_mux); mutex_destroy(&ptp->pincfg_mux); ida_simple_remove(&ptp_clocks_map, index); @@ -274,6 +305,11 @@ int ptp_clock_unregister(struct ptp_clock *ptp) ptp->defunct = 1; wake_up_interruptible(&ptp->tsev_wq); + if (ptp->kworker) { + kthread_cancel_delayed_work_sync(&ptp->aux_work); + kthread_destroy_worker(ptp->kworker); + } + /* Release the clock's resources. */ if (ptp->pps_source) pps_unregister_source(ptp->pps_source); @@ -339,6 +375,12 @@ int ptp_find_pin(struct ptp_clock *ptp, } EXPORT_SYMBOL(ptp_find_pin); +int ptp_schedule_worker(struct ptp_clock *ptp, unsigned long delay) +{ + return kthread_mod_delayed_work(ptp->kworker, &ptp->aux_work, delay); +} +EXPORT_SYMBOL(ptp_schedule_worker); + /* module operations */ static void __exit ptp_exit(void) diff --git a/drivers/ptp/ptp_private.h b/drivers/ptp/ptp_private.h index d95888974d0c..b86f1bfecd6f 100644 --- a/drivers/ptp/ptp_private.h +++ b/drivers/ptp/ptp_private.h @@ -22,6 +22,7 @@ #include <linux/cdev.h> #include <linux/device.h> +#include <linux/kthread.h> #include <linux/mutex.h> #include <linux/posix-clock.h> #include <linux/ptp_clock.h> @@ -56,6 +57,8 @@ struct ptp_clock { struct attribute_group pin_attr_group; /* 1st entry is a pointer to the real group, 2nd is NULL terminator */ const struct attribute_group *pin_attr_groups[2]; + struct kthread_worker *kworker; + struct kthread_delayed_work aux_work; }; /* diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 4fac49e55d47..4b43aa62fbc7 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -1301,7 +1301,6 @@ static void ds1307_clks_register(struct ds1307 *ds1307) static const struct regmap_config regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = 0x12, }; static int ds1307_probe(struct i2c_client *client, diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 7e0d4f724dda..432fc40990bd 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -559,6 +559,7 @@ static void chp_process_crw(struct crw *crw0, struct crw *crw1, chpid.id = crw0->rsid; switch (crw0->erc) { case CRW_ERC_IPARM: /* Path has come. */ + case CRW_ERC_INIT: if (!chp_is_registered(chpid)) chp_new(chpid); chsc_chp_online(chpid); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 8975cd321390..d42e758518ed 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -2512,7 +2512,7 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, struct rtable *rt = (struct rtable *) dst; __be32 *pkey = &ip_hdr(skb)->daddr; - if (rt->rt_gateway) + if (rt && rt->rt_gateway) pkey = &rt->rt_gateway; /* IPv4 */ @@ -2523,7 +2523,7 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, struct rt6_info *rt = (struct rt6_info *) dst; struct in6_addr *pkey = &ipv6_hdr(skb)->daddr; - if (!ipv6_addr_any(&rt->rt6i_gateway)) + if (rt && !ipv6_addr_any(&rt->rt6i_gateway)) pkey = &rt->rt6i_gateway; /* IPv6 */ diff --git a/drivers/sbus/char/display7seg.c b/drivers/sbus/char/display7seg.c index 04efed171c88..f32765d3cbd8 100644 --- a/drivers/sbus/char/display7seg.c +++ b/drivers/sbus/char/display7seg.c @@ -212,8 +212,8 @@ static int d7s_probe(struct platform_device *op) writeb(regs, p->regs); - printk(KERN_INFO PFX "7-Segment Display%s at [%s:0x%llx] %s\n", - op->dev.of_node->full_name, + printk(KERN_INFO PFX "7-Segment Display%pOF at [%s:0x%llx] %s\n", + op->dev.of_node, (regs & D7S_FLIP) ? " (FLIPPED)" : "", op->resource[0].start, sol_compat ? "in sol_compat mode" : ""); diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c index 216f923161d1..a610b8d3d11f 100644 --- a/drivers/sbus/char/flash.c +++ b/drivers/sbus/char/flash.c @@ -181,8 +181,8 @@ static int flash_probe(struct platform_device *op) } flash.busy = 0; - printk(KERN_INFO "%s: OBP Flash, RD %lx[%lx] WR %lx[%lx]\n", - op->dev.of_node->full_name, + printk(KERN_INFO "%pOF: OBP Flash, RD %lx[%lx] WR %lx[%lx]\n", + op->dev.of_node, flash.read_base, flash.read_size, flash.write_base, flash.write_size); diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c index 57696fc0b482..0a5013350acd 100644 --- a/drivers/sbus/char/uctrl.c +++ b/drivers/sbus/char/uctrl.c @@ -379,8 +379,8 @@ static int uctrl_probe(struct platform_device *op) } sbus_writel(UCTRL_INTR_RXNE_REQ|UCTRL_INTR_RXNE_MSK, &p->regs->uctrl_intr); - printk(KERN_INFO "%s: uctrl regs[0x%p] (irq %d)\n", - op->dev.of_node->full_name, p->regs, p->irq); + printk(KERN_INFO "%pOF: uctrl regs[0x%p] (irq %d)\n", + op->dev.of_node, p->regs, p->irq); uctrl_get_event_status(p); uctrl_get_external_status(p); diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index d384f4f86c26..d145e0d90227 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -47,6 +47,17 @@ config SCSI_NETLINK default n depends on NET +config SCSI_MQ_DEFAULT + bool "SCSI: use blk-mq I/O path by default" + depends on SCSI + ---help--- + This option enables the new blk-mq based I/O path for SCSI + devices by default. With the option the scsi_mod.use_blk_mq + module/boot option defaults to Y, without it to N, but it can + still be overridden either way. + + If unsure say N. + config SCSI_PROC_FS bool "legacy /proc/scsi/ support" depends on SCSI && PROC_FS @@ -1230,6 +1241,8 @@ config SCSI_LPFC tristate "Emulex LightPulse Fibre Channel Support" depends on PCI && SCSI depends on SCSI_FC_ATTRS + depends on NVME_TARGET_FC || NVME_TARGET_FC=n + depends on NVME_FC || NVME_FC=n select CRC_T10DIF ---help--- This lpfc driver supports the Emulex LightPulse diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 707ee2f5954d..a1a2c71e1626 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -549,7 +549,9 @@ static void get_container_name_callback(void *context, struct fib * fibptr) if ((le32_to_cpu(get_name_reply->status) == CT_OK) && (get_name_reply->data[0] != '\0')) { char *sp = get_name_reply->data; - sp[sizeof(((struct aac_get_name_resp *)NULL)->data)] = '\0'; + int data_size = FIELD_SIZEOF(struct aac_get_name_resp, data); + + sp[data_size - 1] = '\0'; while (*sp == ' ') ++sp; if (*sp) { @@ -579,12 +581,15 @@ static void get_container_name_callback(void *context, struct fib * fibptr) static int aac_get_container_name(struct scsi_cmnd * scsicmd) { int status; + int data_size; struct aac_get_name *dinfo; struct fib * cmd_fibcontext; struct aac_dev * dev; dev = (struct aac_dev *)scsicmd->device->host->hostdata; + data_size = FIELD_SIZEOF(struct aac_get_name_resp, data); + cmd_fibcontext = aac_fib_alloc_tag(dev, scsicmd); aac_fib_init(cmd_fibcontext); @@ -593,7 +598,7 @@ static int aac_get_container_name(struct scsi_cmnd * scsicmd) dinfo->command = cpu_to_le32(VM_ContainerConfig); dinfo->type = cpu_to_le32(CT_READ_NAME); dinfo->cid = cpu_to_le32(scmd_id(scsicmd)); - dinfo->count = cpu_to_le32(sizeof(((struct aac_get_name_resp *)NULL)->data)); + dinfo->count = cpu_to_le32(data_size - 1); status = aac_fib_send(ContainerCommand, cmd_fibcontext, @@ -3198,10 +3203,11 @@ static int query_disk(struct aac_dev *dev, void __user *arg) return -EBUSY; if (copy_from_user(&qd, arg, sizeof (struct aac_query_disk))) return -EFAULT; - if (qd.cnum == -1) + if (qd.cnum == -1) { + if (qd.id < 0 || qd.id >= dev->maximum_num_containers) + return -EINVAL; qd.cnum = qd.id; - else if ((qd.bus == -1) && (qd.id == -1) && (qd.lun == -1)) - { + } else if ((qd.bus == -1) && (qd.id == -1) && (qd.lun == -1)) { if (qd.cnum < 0 || qd.cnum >= dev->maximum_num_containers) return -EINVAL; qd.instance = dev->scsi_host_ptr->host_no; diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index d31a9bc2ba69..ee2667e20e42 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -2274,7 +2274,7 @@ struct aac_get_name_resp { __le32 parm3; __le32 parm4; __le32 parm5; - u8 data[16]; + u8 data[17]; }; #define CT_CID_TO_32BITS_UID 165 diff --git a/drivers/scsi/aic7xxx/Makefile b/drivers/scsi/aic7xxx/Makefile index 741d81861d17..07b60a780c06 100644 --- a/drivers/scsi/aic7xxx/Makefile +++ b/drivers/scsi/aic7xxx/Makefile @@ -55,9 +55,9 @@ aicasm-7xxx-opts-$(CONFIG_AIC7XXX_REG_PRETTY_PRINT) := \ ifeq ($(CONFIG_AIC7XXX_BUILD_FIRMWARE),y) $(obj)/aic7xxx_seq.h: $(src)/aic7xxx.seq $(src)/aic7xxx.reg $(obj)/aicasm/aicasm - $(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic7xxx_reg.h \ + $(obj)/aicasm/aicasm -I$(srctree)/$(src) -r $(obj)/aic7xxx_reg.h \ $(aicasm-7xxx-opts-y) -o $(obj)/aic7xxx_seq.h \ - $(src)/aic7xxx.seq + $(srctree)/$(src)/aic7xxx.seq $(aic7xxx-gen-y): $(obj)/aic7xxx_seq.h else @@ -72,14 +72,14 @@ aicasm-79xx-opts-$(CONFIG_AIC79XX_REG_PRETTY_PRINT) := \ ifeq ($(CONFIG_AIC79XX_BUILD_FIRMWARE),y) $(obj)/aic79xx_seq.h: $(src)/aic79xx.seq $(src)/aic79xx.reg $(obj)/aicasm/aicasm - $(obj)/aicasm/aicasm -I$(src) -r $(obj)/aic79xx_reg.h \ + $(obj)/aicasm/aicasm -I$(srctree)/$(src) -r $(obj)/aic79xx_reg.h \ $(aicasm-79xx-opts-y) -o $(obj)/aic79xx_seq.h \ - $(src)/aic79xx.seq + $(srctree)/$(src)/aic79xx.seq $(aic79xx-gen-y): $(obj)/aic79xx_seq.h else $(obj)/aic79xx_reg_print.c: $(src)/aic79xx_reg_print.c_shipped endif -$(obj)/aicasm/aicasm: $(src)/aicasm/*.[chyl] - $(MAKE) -C $(src)/aicasm +$(obj)/aicasm/aicasm: $(srctree)/$(src)/aicasm/*.[chyl] + $(MAKE) -C $(srctree)/$(src)/aicasm OUTDIR=$(shell pwd)/$(obj)/aicasm/ diff --git a/drivers/scsi/aic7xxx/aicasm/Makefile b/drivers/scsi/aic7xxx/aicasm/Makefile index b98c5c1056c3..45e2d49c1fff 100644 --- a/drivers/scsi/aic7xxx/aicasm/Makefile +++ b/drivers/scsi/aic7xxx/aicasm/Makefile @@ -1,19 +1,21 @@ PROG= aicasm +OUTDIR ?= ./ + .SUFFIXES= .l .y .c .h CSRCS= aicasm.c aicasm_symbol.c YSRCS= aicasm_gram.y aicasm_macro_gram.y LSRCS= aicasm_scan.l aicasm_macro_scan.l -GENHDRS= aicdb.h $(YSRCS:.y=.h) -GENSRCS= $(YSRCS:.y=.c) $(LSRCS:.l=.c) +GENHDRS= $(addprefix ${OUTDIR}/,aicdb.h $(YSRCS:.y=.h)) +GENSRCS= $(addprefix ${OUTDIR}/,$(YSRCS:.y=.c) $(LSRCS:.l=.c)) SRCS= ${CSRCS} ${GENSRCS} LIBS= -ldb clean-files:= ${GENSRCS} ${GENHDRS} $(YSRCS:.y=.output) $(PROG) # Override default kernel CFLAGS. This is a userland app. -AICASM_CFLAGS:= -I/usr/include -I. +AICASM_CFLAGS:= -I/usr/include -I. -I$(OUTDIR) LEX= flex YACC= bison YFLAGS= -d @@ -32,22 +34,25 @@ YFLAGS+= -t -v LFLAGS= -d endif -$(PROG): ${GENHDRS} $(SRCS) - $(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(PROG) $(LIBS) +$(PROG): $(OUTDIR) ${GENHDRS} $(SRCS) + $(AICASM_CC) $(AICASM_CFLAGS) $(SRCS) -o $(OUTDIR)/$(PROG) $(LIBS) + +$(OUTDIR): + mkdir -p $(OUTDIR) -aicdb.h: +$(OUTDIR)/aicdb.h: @if [ -e "/usr/include/db4/db_185.h" ]; then \ - echo "#include <db4/db_185.h>" > aicdb.h; \ + echo "#include <db4/db_185.h>" > $@; \ elif [ -e "/usr/include/db3/db_185.h" ]; then \ - echo "#include <db3/db_185.h>" > aicdb.h; \ + echo "#include <db3/db_185.h>" > $@; \ elif [ -e "/usr/include/db2/db_185.h" ]; then \ - echo "#include <db2/db_185.h>" > aicdb.h; \ + echo "#include <db2/db_185.h>" > $@; \ elif [ -e "/usr/include/db1/db_185.h" ]; then \ - echo "#include <db1/db_185.h>" > aicdb.h; \ + echo "#include <db1/db_185.h>" > $@; \ elif [ -e "/usr/include/db/db_185.h" ]; then \ - echo "#include <db/db_185.h>" > aicdb.h; \ + echo "#include <db/db_185.h>" > $@; \ elif [ -e "/usr/include/db_185.h" ]; then \ - echo "#include <db_185.h>" > aicdb.h; \ + echo "#include <db_185.h>" > $@; \ else \ echo "*** Install db development libraries"; \ fi @@ -58,23 +63,23 @@ clean: # Create a dependency chain in generated files # to avoid concurrent invocations of the single # rule that builds them all. -aicasm_gram.c: aicasm_gram.h -aicasm_gram.c aicasm_gram.h: aicasm_gram.y +$(OUTDIR)/aicasm_gram.c: $(OUTDIR)/aicasm_gram.h +$(OUTDIR)/aicasm_gram.c $(OUTDIR)/aicasm_gram.h: aicasm_gram.y $(YACC) $(YFLAGS) -b $(<:.y=) $< - mv $(<:.y=).tab.c $(<:.y=.c) - mv $(<:.y=).tab.h $(<:.y=.h) + mv $(<:.y=).tab.c $(OUTDIR)/$(<:.y=.c) + mv $(<:.y=).tab.h $(OUTDIR)/$(<:.y=.h) # Create a dependency chain in generated files # to avoid concurrent invocations of the single # rule that builds them all. -aicasm_macro_gram.c: aicasm_macro_gram.h -aicasm_macro_gram.c aicasm_macro_gram.h: aicasm_macro_gram.y +$(OUTDIR)/aicasm_macro_gram.c: $(OUTDIR)/aicasm_macro_gram.h +$(OUTDIR)/aicasm_macro_gram.c $(OUTDIR)/aicasm_macro_gram.h: aicasm_macro_gram.y $(YACC) $(YFLAGS) -b $(<:.y=) -p mm $< - mv $(<:.y=).tab.c $(<:.y=.c) - mv $(<:.y=).tab.h $(<:.y=.h) + mv $(<:.y=).tab.c $(OUTDIR)/$(<:.y=.c) + mv $(<:.y=).tab.h $(OUTDIR)/$(<:.y=.h) -aicasm_scan.c: aicasm_scan.l - $(LEX) $(LFLAGS) -o$@ $< +$(OUTDIR)/aicasm_scan.c: aicasm_scan.l + $(LEX) $(LFLAGS) -o $@ $< -aicasm_macro_scan.c: aicasm_macro_scan.l - $(LEX) $(LFLAGS) -Pmm -o$@ $< +$(OUTDIR)/aicasm_macro_scan.c: aicasm_macro_scan.l + $(LEX) $(LFLAGS) -Pmm -o $@ $< diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c index 7dfe709a7138..6844ba361616 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c +++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c @@ -2624,12 +2624,11 @@ static struct fcoe_transport bnx2fc_transport = { }; /** - * bnx2fc_percpu_thread_create - Create a receive thread for an - * online CPU + * bnx2fc_cpu_online - Create a receive thread for an online CPU * * @cpu: cpu index for the online cpu */ -static void bnx2fc_percpu_thread_create(unsigned int cpu) +static int bnx2fc_cpu_online(unsigned int cpu) { struct bnx2fc_percpu_s *p; struct task_struct *thread; @@ -2639,15 +2638,17 @@ static void bnx2fc_percpu_thread_create(unsigned int cpu) thread = kthread_create_on_node(bnx2fc_percpu_io_thread, (void *)p, cpu_to_node(cpu), "bnx2fc_thread/%d", cpu); + if (IS_ERR(thread)) + return PTR_ERR(thread); + /* bind thread to the cpu */ - if (likely(!IS_ERR(thread))) { - kthread_bind(thread, cpu); - p->iothread = thread; - wake_up_process(thread); - } + kthread_bind(thread, cpu); + p->iothread = thread; + wake_up_process(thread); + return 0; } -static void bnx2fc_percpu_thread_destroy(unsigned int cpu) +static int bnx2fc_cpu_offline(unsigned int cpu) { struct bnx2fc_percpu_s *p; struct task_struct *thread; @@ -2661,7 +2662,6 @@ static void bnx2fc_percpu_thread_destroy(unsigned int cpu) thread = p->iothread; p->iothread = NULL; - /* Free all work in the list */ list_for_each_entry_safe(work, tmp, &p->work_list, list) { list_del_init(&work->list); @@ -2673,20 +2673,6 @@ static void bnx2fc_percpu_thread_destroy(unsigned int cpu) if (thread) kthread_stop(thread); -} - - -static int bnx2fc_cpu_online(unsigned int cpu) -{ - printk(PFX "CPU %x online: Create Rx thread\n", cpu); - bnx2fc_percpu_thread_create(cpu); - return 0; -} - -static int bnx2fc_cpu_dead(unsigned int cpu) -{ - printk(PFX "CPU %x offline: Remove Rx thread\n", cpu); - bnx2fc_percpu_thread_destroy(cpu); return 0; } @@ -2761,30 +2747,16 @@ static int __init bnx2fc_mod_init(void) spin_lock_init(&p->fp_work_lock); } - get_online_cpus(); - - for_each_online_cpu(cpu) - bnx2fc_percpu_thread_create(cpu); - - rc = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, - "scsi/bnx2fc:online", - bnx2fc_cpu_online, NULL); + rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "scsi/bnx2fc:online", + bnx2fc_cpu_online, bnx2fc_cpu_offline); if (rc < 0) - goto stop_threads; + goto stop_thread; bnx2fc_online_state = rc; - cpuhp_setup_state_nocalls(CPUHP_SCSI_BNX2FC_DEAD, "scsi/bnx2fc:dead", - NULL, bnx2fc_cpu_dead); - put_online_cpus(); - cnic_register_driver(CNIC_ULP_FCOE, &bnx2fc_cnic_cb); - return 0; -stop_threads: - for_each_online_cpu(cpu) - bnx2fc_percpu_thread_destroy(cpu); - put_online_cpus(); +stop_thread: kthread_stop(l2_thread); free_wq: destroy_workqueue(bnx2fc_wq); @@ -2803,7 +2775,6 @@ static void __exit bnx2fc_mod_exit(void) struct fcoe_percpu_s *bg; struct task_struct *l2_thread; struct sk_buff *skb; - unsigned int cpu = 0; /* * NOTE: Since cnic calls register_driver routine rtnl_lock, @@ -2844,16 +2815,7 @@ static void __exit bnx2fc_mod_exit(void) if (l2_thread) kthread_stop(l2_thread); - get_online_cpus(); - /* Destroy per cpu threads */ - for_each_online_cpu(cpu) { - bnx2fc_percpu_thread_destroy(cpu); - } - - cpuhp_remove_state_nocalls(bnx2fc_online_state); - cpuhp_remove_state_nocalls(CPUHP_SCSI_BNX2FC_DEAD); - - put_online_cpus(); + cpuhp_remove_state(bnx2fc_online_state); destroy_workqueue(bnx2fc_wq); /* diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c index 913c750205ce..26de61d65a4d 100644 --- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c +++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c @@ -1008,6 +1008,28 @@ static struct bnx2fc_work *bnx2fc_alloc_work(struct bnx2fc_rport *tgt, u16 wqe) return work; } +/* Pending work request completion */ +static void bnx2fc_pending_work(struct bnx2fc_rport *tgt, unsigned int wqe) +{ + unsigned int cpu = wqe % num_possible_cpus(); + struct bnx2fc_percpu_s *fps; + struct bnx2fc_work *work; + + fps = &per_cpu(bnx2fc_percpu, cpu); + spin_lock_bh(&fps->fp_work_lock); + if (fps->iothread) { + work = bnx2fc_alloc_work(tgt, wqe); + if (work) { + list_add_tail(&work->list, &fps->work_list); + wake_up_process(fps->iothread); + spin_unlock_bh(&fps->fp_work_lock); + return; + } + } + spin_unlock_bh(&fps->fp_work_lock); + bnx2fc_process_cq_compl(tgt, wqe); +} + int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt) { struct fcoe_cqe *cq; @@ -1042,28 +1064,7 @@ int bnx2fc_process_new_cqes(struct bnx2fc_rport *tgt) /* Unsolicited event notification */ bnx2fc_process_unsol_compl(tgt, wqe); } else { - /* Pending work request completion */ - struct bnx2fc_work *work = NULL; - struct bnx2fc_percpu_s *fps = NULL; - unsigned int cpu = wqe % num_possible_cpus(); - - fps = &per_cpu(bnx2fc_percpu, cpu); - spin_lock_bh(&fps->fp_work_lock); - if (unlikely(!fps->iothread)) - goto unlock; - - work = bnx2fc_alloc_work(tgt, wqe); - if (work) - list_add_tail(&work->list, - &fps->work_list); -unlock: - spin_unlock_bh(&fps->fp_work_lock); - - /* Pending work request completion */ - if (fps->iothread && work) - wake_up_process(fps->iothread); - else - bnx2fc_process_cq_compl(tgt, wqe); + bnx2fc_pending_work(tgt, wqe); num_free_sqes++; } cqe++; diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c index 86afc002814c..4ebcda8d9500 100644 --- a/drivers/scsi/bnx2i/bnx2i_init.c +++ b/drivers/scsi/bnx2i/bnx2i_init.c @@ -404,12 +404,11 @@ int bnx2i_get_stats(void *handle) /** - * bnx2i_percpu_thread_create - Create a receive thread for an - * online CPU + * bnx2i_cpu_online - Create a receive thread for an online CPU * * @cpu: cpu index for the online cpu */ -static void bnx2i_percpu_thread_create(unsigned int cpu) +static int bnx2i_cpu_online(unsigned int cpu) { struct bnx2i_percpu_s *p; struct task_struct *thread; @@ -419,16 +418,17 @@ static void bnx2i_percpu_thread_create(unsigned int cpu) thread = kthread_create_on_node(bnx2i_percpu_io_thread, (void *)p, cpu_to_node(cpu), "bnx2i_thread/%d", cpu); + if (IS_ERR(thread)) + return PTR_ERR(thread); + /* bind thread to the cpu */ - if (likely(!IS_ERR(thread))) { - kthread_bind(thread, cpu); - p->iothread = thread; - wake_up_process(thread); - } + kthread_bind(thread, cpu); + p->iothread = thread; + wake_up_process(thread); + return 0; } - -static void bnx2i_percpu_thread_destroy(unsigned int cpu) +static int bnx2i_cpu_offline(unsigned int cpu) { struct bnx2i_percpu_s *p; struct task_struct *thread; @@ -451,19 +451,6 @@ static void bnx2i_percpu_thread_destroy(unsigned int cpu) spin_unlock_bh(&p->p_work_lock); if (thread) kthread_stop(thread); -} - -static int bnx2i_cpu_online(unsigned int cpu) -{ - pr_info("bnx2i: CPU %x online: Create Rx thread\n", cpu); - bnx2i_percpu_thread_create(cpu); - return 0; -} - -static int bnx2i_cpu_dead(unsigned int cpu) -{ - pr_info("CPU %x offline: Remove Rx thread\n", cpu); - bnx2i_percpu_thread_destroy(cpu); return 0; } @@ -511,27 +498,14 @@ static int __init bnx2i_mod_init(void) p->iothread = NULL; } - get_online_cpus(); - - for_each_online_cpu(cpu) - bnx2i_percpu_thread_create(cpu); - - err = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, - "scsi/bnx2i:online", - bnx2i_cpu_online, NULL); + err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "scsi/bnx2i:online", + bnx2i_cpu_online, bnx2i_cpu_offline); if (err < 0) - goto remove_threads; + goto unreg_driver; bnx2i_online_state = err; - - cpuhp_setup_state_nocalls(CPUHP_SCSI_BNX2I_DEAD, "scsi/bnx2i:dead", - NULL, bnx2i_cpu_dead); - put_online_cpus(); return 0; -remove_threads: - for_each_online_cpu(cpu) - bnx2i_percpu_thread_destroy(cpu); - put_online_cpus(); +unreg_driver: cnic_unregister_driver(CNIC_ULP_ISCSI); unreg_xport: iscsi_unregister_transport(&bnx2i_iscsi_transport); @@ -551,7 +525,6 @@ out: static void __exit bnx2i_mod_exit(void) { struct bnx2i_hba *hba; - unsigned cpu = 0; mutex_lock(&bnx2i_dev_lock); while (!list_empty(&adapter_list)) { @@ -569,14 +542,7 @@ static void __exit bnx2i_mod_exit(void) } mutex_unlock(&bnx2i_dev_lock); - get_online_cpus(); - - for_each_online_cpu(cpu) - bnx2i_percpu_thread_destroy(cpu); - - cpuhp_remove_state_nocalls(bnx2i_online_state); - cpuhp_remove_state_nocalls(CPUHP_SCSI_BNX2I_DEAD); - put_online_cpus(); + cpuhp_remove_state(bnx2i_online_state); iscsi_unregister_transport(&bnx2i_iscsi_transport); cnic_unregister_driver(CNIC_ULP_ISCSI); diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index 2029ad225121..5be0086142ca 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -3845,8 +3845,10 @@ csio_hw_start(struct csio_hw *hw) if (csio_is_hw_ready(hw)) return 0; - else + else if (csio_match_state(hw, csio_hws_uninit)) return -EINVAL; + else + return -ENODEV; } int diff --git a/drivers/scsi/csiostor/csio_init.c b/drivers/scsi/csiostor/csio_init.c index ea0c31086cc6..dcd074169aa9 100644 --- a/drivers/scsi/csiostor/csio_init.c +++ b/drivers/scsi/csiostor/csio_init.c @@ -969,10 +969,14 @@ static int csio_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, hw); - if (csio_hw_start(hw) != 0) { - dev_err(&pdev->dev, - "Failed to start FW, continuing in debug mode.\n"); - return 0; + rv = csio_hw_start(hw); + if (rv) { + if (rv == -EINVAL) { + dev_err(&pdev->dev, + "Failed to start FW, continuing in debug mode.\n"); + return 0; + } + goto err_lnode_exit; } sprintf(hw->fwrev_str, "%u.%u.%u.%u\n", diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index a69a9ac836f5..1d02cf9fe06c 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -1635,6 +1635,9 @@ static int init_act_open(struct cxgbi_sock *csk) goto rel_resource; } + if (!(n->nud_state & NUD_VALID)) + neigh_event_send(n, NULL); + csk->atid = cxgb4_alloc_atid(lldi->tids, csk); if (csk->atid < 0) { pr_err("%s, NO atid available.\n", ndev->name); diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index e4c83b7c96a8..1a4cfa562a60 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -2128,6 +2128,13 @@ void cxgbi_cleanup_task(struct iscsi_task *task) struct iscsi_tcp_task *tcp_task = task->dd_data; struct cxgbi_task_data *tdata = iscsi_task_cxgbi_data(task); + if (!tcp_task || !tdata || (tcp_task->dd_data != tdata)) { + pr_info("task 0x%p,0x%p, tcp_task 0x%p, tdata 0x%p/0x%p.\n", + task, task->sc, tcp_task, + tcp_task ? tcp_task->dd_data : NULL, tdata); + return; + } + log_debug(1 << CXGBI_DBG_ISCSI, "task 0x%p, skb 0x%p, itt 0x%x.\n", task, tdata->skb, task->hdr_itt); diff --git a/drivers/scsi/cxlflash/main.c b/drivers/scsi/cxlflash/main.c index 077f62e208aa..6a4367cc9caa 100644 --- a/drivers/scsi/cxlflash/main.c +++ b/drivers/scsi/cxlflash/main.c @@ -3401,9 +3401,10 @@ static int cxlflash_afu_debug(struct cxlflash_cfg *cfg, if (is_write) { req_flags |= SISL_REQ_FLAGS_HOST_WRITE; - rc = copy_from_user(kbuf, ubuf, ulen); - if (unlikely(rc)) + if (copy_from_user(kbuf, ubuf, ulen)) { + rc = -EFAULT; goto out; + } } } @@ -3431,8 +3432,10 @@ static int cxlflash_afu_debug(struct cxlflash_cfg *cfg, goto out; } - if (ulen && !is_write) - rc = copy_to_user(ubuf, kbuf, ulen); + if (ulen && !is_write) { + if (copy_to_user(ubuf, kbuf, ulen)) + rc = -EFAULT; + } out: kfree(buf); dev_dbg(dev, "%s: returning rc=%d\n", __func__, rc); diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 551d103c27f1..2bfea7082e3a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -1693,7 +1693,7 @@ static int prep_ssp_v2_hw(struct hisi_hba *hisi_hba, static int parse_trans_tx_err_code_v2_hw(u32 err_msk) { - const u8 trans_tx_err_code_prio[] = { + static const u8 trans_tx_err_code_prio[] = { TRANS_TX_OPEN_FAIL_WITH_IT_NEXUS_LOSS, TRANS_TX_ERR_PHY_NOT_ENABLE, TRANS_TX_OPEN_CNX_ERR_WRONG_DESTINATION, @@ -1738,7 +1738,7 @@ static int parse_trans_tx_err_code_v2_hw(u32 err_msk) static int parse_trans_rx_err_code_v2_hw(u32 err_msk) { - const u8 trans_rx_err_code_prio[] = { + static const u8 trans_rx_err_code_prio[] = { TRANS_RX_ERR_WITH_RXFRAME_CRC_ERR, TRANS_RX_ERR_WITH_RXFIS_8B10B_DISP_ERR, TRANS_RX_ERR_WITH_RXFRAME_HAVE_ERRPRM, @@ -1784,7 +1784,7 @@ static int parse_trans_rx_err_code_v2_hw(u32 err_msk) static int parse_dma_tx_err_code_v2_hw(u32 err_msk) { - const u8 dma_tx_err_code_prio[] = { + static const u8 dma_tx_err_code_prio[] = { DMA_TX_UNEXP_XFER_ERR, DMA_TX_UNEXP_RETRANS_ERR, DMA_TX_XFER_LEN_OVERFLOW, @@ -1810,7 +1810,7 @@ static int parse_dma_tx_err_code_v2_hw(u32 err_msk) static int parse_sipc_rx_err_code_v2_hw(u32 err_msk) { - const u8 sipc_rx_err_code_prio[] = { + static const u8 sipc_rx_err_code_prio[] = { SIPC_RX_FIS_STATUS_ERR_BIT_VLD, SIPC_RX_PIO_WRSETUP_STATUS_DRQ_ERR, SIPC_RX_FIS_STATUS_BSY_BIT_ERR, @@ -1836,7 +1836,7 @@ static int parse_sipc_rx_err_code_v2_hw(u32 err_msk) static int parse_dma_rx_err_code_v2_hw(u32 err_msk) { - const u8 dma_rx_err_code_prio[] = { + static const u8 dma_rx_err_code_prio[] = { DMA_RX_UNKNOWN_FRM_ERR, DMA_RX_DATA_LEN_OVERFLOW, DMA_RX_DATA_LEN_UNDERFLOW, diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c index 8914eab84337..4f7cdb28bd38 100644 --- a/drivers/scsi/hpsa.c +++ b/drivers/scsi/hpsa.c @@ -938,7 +938,7 @@ static struct scsi_host_template hpsa_driver_template = { #endif .sdev_attrs = hpsa_sdev_attrs, .shost_attrs = hpsa_shost_attrs, - .max_sectors = 8192, + .max_sectors = 1024, .no_write_same = 1, }; diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index b0c68d24db01..da5bdbdcce52 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3351,6 +3351,16 @@ static void ipr_worker_thread(struct work_struct *work) return; } + if (ioa_cfg->scsi_unblock) { + ioa_cfg->scsi_unblock = 0; + ioa_cfg->scsi_blocked = 0; + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + scsi_unblock_requests(ioa_cfg->host); + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + if (ioa_cfg->scsi_blocked) + scsi_block_requests(ioa_cfg->host); + } + if (!ioa_cfg->scan_enabled) { spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); return; @@ -7211,9 +7221,8 @@ static int ipr_ioa_bringdown_done(struct ipr_cmnd *ipr_cmd) ENTER; if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].removing_ioa) { ipr_trace; - spin_unlock_irq(ioa_cfg->host->host_lock); - scsi_unblock_requests(ioa_cfg->host); - spin_lock_irq(ioa_cfg->host->host_lock); + ioa_cfg->scsi_unblock = 1; + schedule_work(&ioa_cfg->work_q); } ioa_cfg->in_reset_reload = 0; @@ -7287,13 +7296,7 @@ static int ipr_ioa_reset_done(struct ipr_cmnd *ipr_cmd) list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); wake_up_all(&ioa_cfg->reset_wait_q); - spin_unlock(ioa_cfg->host->host_lock); - scsi_unblock_requests(ioa_cfg->host); - spin_lock(ioa_cfg->host->host_lock); - - if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].allow_cmds) - scsi_block_requests(ioa_cfg->host); - + ioa_cfg->scsi_unblock = 1; schedule_work(&ioa_cfg->work_q); LEAVE; return IPR_RC_JOB_RETURN; @@ -9249,8 +9252,11 @@ static void _ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg, spin_unlock(&ioa_cfg->hrrq[i]._lock); } wmb(); - if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].removing_ioa) + if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].removing_ioa) { + ioa_cfg->scsi_unblock = 0; + ioa_cfg->scsi_blocked = 1; scsi_block_requests(ioa_cfg->host); + } ipr_cmd = ipr_get_free_ipr_cmnd(ioa_cfg); ioa_cfg->reset_cmd = ipr_cmd; @@ -9306,9 +9312,8 @@ static void ipr_initiate_ioa_reset(struct ipr_ioa_cfg *ioa_cfg, wake_up_all(&ioa_cfg->reset_wait_q); if (!ioa_cfg->hrrq[IPR_INIT_HRRQ].removing_ioa) { - spin_unlock_irq(ioa_cfg->host->host_lock); - scsi_unblock_requests(ioa_cfg->host); - spin_lock_irq(ioa_cfg->host->host_lock); + ioa_cfg->scsi_unblock = 1; + schedule_work(&ioa_cfg->work_q); } return; } else { diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index e98a87a65335..c7f0e9e3cd7d 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -1488,6 +1488,8 @@ struct ipr_ioa_cfg { u8 cfg_locked:1; u8 clear_isr:1; u8 probe_done:1; + u8 scsi_unblock:1; + u8 scsi_blocked:1; u8 revid; diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c index 47f66e949745..ed197bc8e801 100644 --- a/drivers/scsi/isci/request.c +++ b/drivers/scsi/isci/request.c @@ -213,7 +213,7 @@ static void sci_task_request_build_ssp_task_iu(struct isci_request *ireq) * @task_context: * */ -static void scu_ssp_reqeust_construct_task_context( +static void scu_ssp_request_construct_task_context( struct isci_request *ireq, struct scu_task_context *task_context) { @@ -425,7 +425,7 @@ static void scu_ssp_io_request_construct_task_context(struct isci_request *ireq, u8 prot_type = scsi_get_prot_type(scmd); u8 prot_op = scsi_get_prot_op(scmd); - scu_ssp_reqeust_construct_task_context(ireq, task_context); + scu_ssp_request_construct_task_context(ireq, task_context); task_context->ssp_command_iu_length = sizeof(struct ssp_cmd_iu) / sizeof(u32); @@ -472,7 +472,7 @@ static void scu_ssp_task_request_construct_task_context(struct isci_request *ire { struct scu_task_context *task_context = ireq->tc; - scu_ssp_reqeust_construct_task_context(ireq, task_context); + scu_ssp_request_construct_task_context(ireq, task_context); task_context->control_frame = 1; task_context->priority = SCU_TASK_PRIORITY_HIGH; @@ -495,7 +495,7 @@ static void scu_ssp_task_request_construct_task_context(struct isci_request *ire * the command buffer is complete. none Revisit task context construction to * determine what is common for SSP/SMP/STP task context structures. */ -static void scu_sata_reqeust_construct_task_context( +static void scu_sata_request_construct_task_context( struct isci_request *ireq, struct scu_task_context *task_context) { @@ -562,7 +562,7 @@ static void scu_stp_raw_request_construct_task_context(struct isci_request *ireq { struct scu_task_context *task_context = ireq->tc; - scu_sata_reqeust_construct_task_context(ireq, task_context); + scu_sata_request_construct_task_context(ireq, task_context); task_context->control_frame = 0; task_context->priority = SCU_TASK_PRIORITY_NORMAL; @@ -613,7 +613,7 @@ static void sci_stp_optimized_request_construct(struct isci_request *ireq, struct scu_task_context *task_context = ireq->tc; /* Build the STP task context structure */ - scu_sata_reqeust_construct_task_context(ireq, task_context); + scu_sata_request_construct_task_context(ireq, task_context); /* Copy over the SGL elements */ sci_request_build_sgl(ireq); @@ -1401,7 +1401,7 @@ static enum sci_status sci_stp_request_pio_data_out_transmit_data(struct isci_re * @data_buffer: The buffer of data to be copied. * @length: The length of the data transfer. * - * Copy the data from the buffer for the length specified to the IO reqeust SGL + * Copy the data from the buffer for the length specified to the IO request SGL * specified data region. enum sci_status */ static enum sci_status diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index fd501f8dbb11..8660f923ace0 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -573,7 +573,7 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp, event = DISC_EV_FAILED; } if (error) - fc_disc_error(disc, fp); + fc_disc_error(disc, ERR_PTR(error)); else if (event != DISC_EV_NONE) fc_disc_done(disc, event); fc_frame_free(fp); diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 4ed48ed38e79..7ee1a94c0b33 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -205,8 +205,10 @@ lpfc_nvme_info_show(struct device *dev, struct device_attribute *attr, atomic_read(&tgtp->xmt_ls_rsp_error)); len += snprintf(buf+len, PAGE_SIZE-len, - "FCP: Rcv %08x Release %08x Drop %08x\n", + "FCP: Rcv %08x Defer %08x Release %08x " + "Drop %08x\n", atomic_read(&tgtp->rcv_fcp_cmd_in), + atomic_read(&tgtp->rcv_fcp_cmd_defer), atomic_read(&tgtp->xmt_fcp_release), atomic_read(&tgtp->rcv_fcp_cmd_drop)); diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 5cc8b0f7d885..744f3f395b64 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -782,8 +782,11 @@ lpfc_debugfs_nvmestat_data(struct lpfc_vport *vport, char *buf, int size) atomic_read(&tgtp->xmt_ls_rsp_error)); len += snprintf(buf + len, size - len, - "FCP: Rcv %08x Drop %08x\n", + "FCP: Rcv %08x Defer %08x Release %08x " + "Drop %08x\n", atomic_read(&tgtp->rcv_fcp_cmd_in), + atomic_read(&tgtp->rcv_fcp_cmd_defer), + atomic_read(&tgtp->xmt_fcp_release), atomic_read(&tgtp->rcv_fcp_cmd_drop)); if (atomic_read(&tgtp->rcv_fcp_cmd_in) != diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index fbeec344c6cc..bbbd0f84160d 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -841,12 +841,31 @@ lpfc_nvmet_xmt_fcp_release(struct nvmet_fc_target_port *tgtport, lpfc_nvmet_ctxbuf_post(phba, ctxp->ctxbuf); } +static void +lpfc_nvmet_defer_rcv(struct nvmet_fc_target_port *tgtport, + struct nvmefc_tgt_fcp_req *rsp) +{ + struct lpfc_nvmet_tgtport *tgtp; + struct lpfc_nvmet_rcv_ctx *ctxp = + container_of(rsp, struct lpfc_nvmet_rcv_ctx, ctx.fcp_req); + struct rqb_dmabuf *nvmebuf = ctxp->rqb_buffer; + struct lpfc_hba *phba = ctxp->phba; + + lpfc_nvmeio_data(phba, "NVMET DEFERRCV: xri x%x sz %d CPU %02x\n", + ctxp->oxid, ctxp->size, smp_processor_id()); + + tgtp = phba->targetport->private; + atomic_inc(&tgtp->rcv_fcp_cmd_defer); + lpfc_rq_buf_free(phba, &nvmebuf->hbuf); /* repost */ +} + static struct nvmet_fc_target_template lpfc_tgttemplate = { .targetport_delete = lpfc_nvmet_targetport_delete, .xmt_ls_rsp = lpfc_nvmet_xmt_ls_rsp, .fcp_op = lpfc_nvmet_xmt_fcp_op, .fcp_abort = lpfc_nvmet_xmt_fcp_abort, .fcp_req_release = lpfc_nvmet_xmt_fcp_release, + .defer_rcv = lpfc_nvmet_defer_rcv, .max_hw_queues = 1, .max_sgl_segments = LPFC_NVMET_DEFAULT_SEGS, @@ -1504,6 +1523,17 @@ lpfc_nvmet_unsol_fcp_buffer(struct lpfc_hba *phba, return; } + /* Processing of FCP command is deferred */ + if (rc == -EOVERFLOW) { + lpfc_nvmeio_data(phba, + "NVMET RCV BUSY: xri x%x sz %d from %06x\n", + oxid, size, sid); + /* defer reposting rcv buffer till .defer_rcv callback */ + ctxp->rqb_buffer = nvmebuf; + atomic_inc(&tgtp->rcv_fcp_cmd_out); + return; + } + atomic_inc(&tgtp->rcv_fcp_cmd_drop); lpfc_printf_log(phba, KERN_ERR, LOG_NVME_IOERR, "6159 FCP Drop IO x%x: err x%x: x%x x%x x%x\n", diff --git a/drivers/scsi/lpfc/lpfc_nvmet.h b/drivers/scsi/lpfc/lpfc_nvmet.h index e675ef17be08..48a76788b003 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.h +++ b/drivers/scsi/lpfc/lpfc_nvmet.h @@ -49,6 +49,7 @@ struct lpfc_nvmet_tgtport { atomic_t rcv_fcp_cmd_in; atomic_t rcv_fcp_cmd_out; atomic_t rcv_fcp_cmd_drop; + atomic_t rcv_fcp_cmd_defer; atomic_t xmt_fcp_release; /* Stats counters - lpfc_nvmet_xmt_fcp_op */ diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 316c3df0c3fd..71c4746341ea 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -6228,8 +6228,8 @@ static int megasas_probe_one(struct pci_dev *pdev, fail_start_aen: fail_io_attach: megasas_mgmt_info.count--; - megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL; megasas_mgmt_info.max_index--; + megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL; instance->instancet->disable_intr(instance); megasas_destroy_irqs(instance); diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index f990ab4d45e1..985510628f56 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -425,7 +425,7 @@ static int megasas_create_sg_sense_fusion(struct megasas_instance *instance) int megasas_alloc_cmdlist_fusion(struct megasas_instance *instance) { - u32 max_mpt_cmd, i; + u32 max_mpt_cmd, i, j; struct fusion_context *fusion; fusion = instance->ctrl_context; @@ -450,11 +450,15 @@ megasas_alloc_cmdlist_fusion(struct megasas_instance *instance) fusion->cmd_list[i] = kzalloc(sizeof(struct megasas_cmd_fusion), GFP_KERNEL); if (!fusion->cmd_list[i]) { + for (j = 0; j < i; j++) + kfree(fusion->cmd_list[j]); + kfree(fusion->cmd_list); dev_err(&instance->pdev->dev, "Failed from %s %d\n", __func__, __LINE__); return -ENOMEM; } } + return 0; } int diff --git a/drivers/scsi/qedf/qedf.h b/drivers/scsi/qedf/qedf.h index 4d038926a455..351f06dfc5a0 100644 --- a/drivers/scsi/qedf/qedf.h +++ b/drivers/scsi/qedf/qedf.h @@ -528,7 +528,8 @@ struct fip_vlan { #define QEDF_WRITE (1 << 0) #define MAX_FIBRE_LUNS 0xffffffff -#define QEDF_MAX_NUM_CQS 8 +#define MIN_NUM_CPUS_MSIX(x) min_t(u32, x->dev_info.num_cqs, \ + num_online_cpus()) /* * PCI function probe defines diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index b58bba4604e8..1d13c9ca517d 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -1227,7 +1227,7 @@ static void qedf_rport_event_handler(struct fc_lport *lport, if (rdata->spp_type != FC_TYPE_FCP) { QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, - "Not offlading since since spp type isn't FCP\n"); + "Not offloading since spp type isn't FCP\n"); break; } if (!(rdata->ids.roles & FC_RPORT_ROLE_FCP_TARGET)) { @@ -2760,11 +2760,9 @@ static int qedf_set_fcoe_pf_param(struct qedf_ctx *qedf) * we allocation is the minimum off: * * Number of CPUs - * Number of MSI-X vectors - * Max number allocated in hardware (QEDF_MAX_NUM_CQS) + * Number allocated by qed for our PCI function */ - qedf->num_queues = min((unsigned int)QEDF_MAX_NUM_CQS, - num_online_cpus()); + qedf->num_queues = MIN_NUM_CPUS_MSIX(qedf); QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Number of CQs is %d.\n", qedf->num_queues); @@ -2962,6 +2960,13 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) goto err1; } + /* Learn information crucial for qedf to progress */ + rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info); + if (rc) { + QEDF_ERR(&(qedf->dbg_ctx), "Failed to dev info.\n"); + goto err1; + } + /* queue allocation code should come here * order should be * slowpath_start @@ -2977,13 +2982,6 @@ static int __qedf_probe(struct pci_dev *pdev, int mode) } qed_ops->common->update_pf_params(qedf->cdev, &qedf->pf_params); - /* Learn information crucial for qedf to progress */ - rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info); - if (rc) { - QEDF_ERR(&(qedf->dbg_ctx), "Failed to dev info.\n"); - goto err1; - } - /* Record BDQ producer doorbell addresses */ qedf->bdq_primary_prod = qedf->dev_info.primary_dbq_rq_addr; qedf->bdq_secondary_prod = qedf->dev_info.secondary_bdq_rq_addr; diff --git a/drivers/scsi/qedi/Kconfig b/drivers/scsi/qedi/Kconfig index 21331453db7b..2ff753ce6e27 100644 --- a/drivers/scsi/qedi/Kconfig +++ b/drivers/scsi/qedi/Kconfig @@ -5,6 +5,7 @@ config QEDI select SCSI_ISCSI_ATTRS select QED_LL2 select QED_ISCSI + select ISCSI_BOOT_SYSFS ---help--- This driver supports iSCSI offload for the QLogic FastLinQ 41000 Series Converged Network Adapters. diff --git a/drivers/scsi/qedi/qedi.h b/drivers/scsi/qedi/qedi.h index 32632c9b2276..91d2f51c351b 100644 --- a/drivers/scsi/qedi/qedi.h +++ b/drivers/scsi/qedi/qedi.h @@ -23,11 +23,17 @@ #include <linux/qed/qed_iscsi_if.h> #include <linux/qed/qed_ll2_if.h> #include "qedi_version.h" +#include "qedi_nvm_iscsi_cfg.h" #define QEDI_MODULE_NAME "qedi" struct qedi_endpoint; +#ifndef GET_FIELD2 +#define GET_FIELD2(value, name) \ + (((value) & (name ## _MASK)) >> (name ## _OFFSET)) +#endif + /* * PCI function probe defines */ @@ -66,6 +72,11 @@ struct qedi_endpoint; #define QEDI_HW_DMA_BOUNDARY 0xfff #define QEDI_PATH_HANDLE 0xFE0000000UL +enum qedi_nvm_tgts { + QEDI_NVM_TGT_PRI, + QEDI_NVM_TGT_SEC, +}; + struct qedi_uio_ctrl { /* meta data */ u32 uio_hsi_version; @@ -283,6 +294,8 @@ struct qedi_ctx { void *bdq_pbl_list; dma_addr_t bdq_pbl_list_dma; u8 bdq_pbl_list_num_entries; + struct nvm_iscsi_cfg *iscsi_cfg; + dma_addr_t nvm_buf_dma; void __iomem *bdq_primary_prod; void __iomem *bdq_secondary_prod; u16 bdq_prod_idx; @@ -337,6 +350,10 @@ struct qedi_ctx { bool use_fast_sge; atomic_t num_offloads; +#define SYSFS_FLAG_FW_SEL_BOOT 2 +#define IPV6_LEN 41 +#define IPV4_LEN 17 + struct iscsi_boot_kset *boot_kset; }; struct qedi_work { diff --git a/drivers/scsi/qedi/qedi_fw.c b/drivers/scsi/qedi/qedi_fw.c index 19254bd739d9..93d54acd4a22 100644 --- a/drivers/scsi/qedi/qedi_fw.c +++ b/drivers/scsi/qedi/qedi_fw.c @@ -1411,7 +1411,7 @@ static void qedi_tmf_work(struct work_struct *work) list_work = kzalloc(sizeof(*list_work), GFP_ATOMIC); if (!list_work) { - QEDI_ERR(&qedi->dbg_ctx, "Memory alloction failed\n"); + QEDI_ERR(&qedi->dbg_ctx, "Memory allocation failed\n"); goto abort_ret; } diff --git a/drivers/scsi/qedi/qedi_iscsi.c b/drivers/scsi/qedi/qedi_iscsi.c index 80edd28b635f..37da9a8b43b1 100644 --- a/drivers/scsi/qedi/qedi_iscsi.c +++ b/drivers/scsi/qedi/qedi_iscsi.c @@ -824,7 +824,7 @@ qedi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, u32 iscsi_cid = QEDI_CID_RESERVED; u16 len = 0; char *buf = NULL; - int ret; + int ret, tmp; if (!shost) { ret = -ENXIO; @@ -940,10 +940,10 @@ qedi_ep_connect(struct Scsi_Host *shost, struct sockaddr *dst_addr, ep_rel_conn: qedi->ep_tbl[iscsi_cid] = NULL; - ret = qedi_ops->release_conn(qedi->cdev, qedi_ep->handle); - if (ret) + tmp = qedi_ops->release_conn(qedi->cdev, qedi_ep->handle); + if (tmp) QEDI_WARN(&qedi->dbg_ctx, "release_conn returned %d\n", - ret); + tmp); ep_free_sq: qedi_free_sq(qedi, qedi_ep); ep_conn_exit: diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c index 5f5a4ef2e529..2c3783684815 100644 --- a/drivers/scsi/qedi/qedi_main.c +++ b/drivers/scsi/qedi/qedi_main.c @@ -19,6 +19,7 @@ #include <linux/mm.h> #include <linux/if_vlan.h> #include <linux/cpu.h> +#include <linux/iscsi_boot_sysfs.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> @@ -1143,6 +1144,30 @@ exit_setup_int: return rc; } +static void qedi_free_nvm_iscsi_cfg(struct qedi_ctx *qedi) +{ + if (qedi->iscsi_cfg) + dma_free_coherent(&qedi->pdev->dev, + sizeof(struct nvm_iscsi_cfg), + qedi->iscsi_cfg, qedi->nvm_buf_dma); +} + +static int qedi_alloc_nvm_iscsi_cfg(struct qedi_ctx *qedi) +{ + qedi->iscsi_cfg = dma_zalloc_coherent(&qedi->pdev->dev, + sizeof(struct nvm_iscsi_cfg), + &qedi->nvm_buf_dma, GFP_KERNEL); + if (!qedi->iscsi_cfg) { + QEDI_ERR(&qedi->dbg_ctx, "Could not allocate NVM BUF.\n"); + return -ENOMEM; + } + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "NVM BUF addr=0x%p dma=0x%llx.\n", qedi->iscsi_cfg, + qedi->nvm_buf_dma); + + return 0; +} + static void qedi_free_bdq(struct qedi_ctx *qedi) { int i; @@ -1183,6 +1208,7 @@ static void qedi_free_global_queues(struct qedi_ctx *qedi) kfree(gl[i]); } qedi_free_bdq(qedi); + qedi_free_nvm_iscsi_cfg(qedi); } static int qedi_alloc_bdq(struct qedi_ctx *qedi) @@ -1309,6 +1335,11 @@ static int qedi_alloc_global_queues(struct qedi_ctx *qedi) if (rc) goto mem_alloc_failure; + /* Allocate DMA coherent buffers for NVM_ISCSI_CFG */ + rc = qedi_alloc_nvm_iscsi_cfg(qedi); + if (rc) + goto mem_alloc_failure; + /* Allocate a CQ and an associated PBL for each MSI-X * vector. */ @@ -1671,6 +1702,387 @@ void qedi_reset_host_mtu(struct qedi_ctx *qedi, u16 mtu) qedi_ops->ll2->start(qedi->cdev, ¶ms); } +/** + * qedi_get_nvram_block: - Scan through the iSCSI NVRAM block (while accounting + * for gaps) for the matching absolute-pf-id of the QEDI device. + */ +static struct nvm_iscsi_block * +qedi_get_nvram_block(struct qedi_ctx *qedi) +{ + int i; + u8 pf; + u32 flags; + struct nvm_iscsi_block *block; + + pf = qedi->dev_info.common.abs_pf_id; + block = &qedi->iscsi_cfg->block[0]; + for (i = 0; i < NUM_OF_ISCSI_PF_SUPPORTED; i++, block++) { + flags = ((block->id) & NVM_ISCSI_CFG_BLK_CTRL_FLAG_MASK) >> + NVM_ISCSI_CFG_BLK_CTRL_FLAG_OFFSET; + if (flags & (NVM_ISCSI_CFG_BLK_CTRL_FLAG_IS_NOT_EMPTY | + NVM_ISCSI_CFG_BLK_CTRL_FLAG_PF_MAPPED) && + (pf == (block->id & NVM_ISCSI_CFG_BLK_MAPPED_PF_ID_MASK) + >> NVM_ISCSI_CFG_BLK_MAPPED_PF_ID_OFFSET)) + return block; + } + return NULL; +} + +static ssize_t qedi_show_boot_eth_info(void *data, int type, char *buf) +{ + struct qedi_ctx *qedi = data; + struct nvm_iscsi_initiator *initiator; + char *str = buf; + int rc = 1; + u32 ipv6_en, dhcp_en, ip_len; + struct nvm_iscsi_block *block; + char *fmt, *ip, *sub, *gw; + + block = qedi_get_nvram_block(qedi); + if (!block) + return 0; + + initiator = &block->initiator; + ipv6_en = block->generic.ctrl_flags & + NVM_ISCSI_CFG_GEN_IPV6_ENABLED; + dhcp_en = block->generic.ctrl_flags & + NVM_ISCSI_CFG_GEN_DHCP_TCPIP_CONFIG_ENABLED; + /* Static IP assignments. */ + fmt = ipv6_en ? "%pI6\n" : "%pI4\n"; + ip = ipv6_en ? initiator->ipv6.addr.byte : initiator->ipv4.addr.byte; + ip_len = ipv6_en ? IPV6_LEN : IPV4_LEN; + sub = ipv6_en ? initiator->ipv6.subnet_mask.byte : + initiator->ipv4.subnet_mask.byte; + gw = ipv6_en ? initiator->ipv6.gateway.byte : + initiator->ipv4.gateway.byte; + /* DHCP IP adjustments. */ + fmt = dhcp_en ? "%s\n" : fmt; + if (dhcp_en) { + ip = ipv6_en ? "0::0" : "0.0.0.0"; + sub = ip; + gw = ip; + ip_len = ipv6_en ? 5 : 8; + } + + switch (type) { + case ISCSI_BOOT_ETH_IP_ADDR: + rc = snprintf(str, ip_len, fmt, ip); + break; + case ISCSI_BOOT_ETH_SUBNET_MASK: + rc = snprintf(str, ip_len, fmt, sub); + break; + case ISCSI_BOOT_ETH_GATEWAY: + rc = snprintf(str, ip_len, fmt, gw); + break; + case ISCSI_BOOT_ETH_FLAGS: + rc = snprintf(str, 3, "%hhd\n", + SYSFS_FLAG_FW_SEL_BOOT); + break; + case ISCSI_BOOT_ETH_INDEX: + rc = snprintf(str, 3, "0\n"); + break; + case ISCSI_BOOT_ETH_MAC: + rc = sysfs_format_mac(str, qedi->mac, ETH_ALEN); + break; + case ISCSI_BOOT_ETH_VLAN: + rc = snprintf(str, 12, "%d\n", + GET_FIELD2(initiator->generic_cont0, + NVM_ISCSI_CFG_INITIATOR_VLAN)); + break; + case ISCSI_BOOT_ETH_ORIGIN: + if (dhcp_en) + rc = snprintf(str, 3, "3\n"); + break; + default: + rc = 0; + break; + } + + return rc; +} + +static umode_t qedi_eth_get_attr_visibility(void *data, int type) +{ + int rc = 1; + + switch (type) { + case ISCSI_BOOT_ETH_FLAGS: + case ISCSI_BOOT_ETH_MAC: + case ISCSI_BOOT_ETH_INDEX: + case ISCSI_BOOT_ETH_IP_ADDR: + case ISCSI_BOOT_ETH_SUBNET_MASK: + case ISCSI_BOOT_ETH_GATEWAY: + case ISCSI_BOOT_ETH_ORIGIN: + case ISCSI_BOOT_ETH_VLAN: + rc = 0444; + break; + default: + rc = 0; + break; + } + return rc; +} + +static ssize_t qedi_show_boot_ini_info(void *data, int type, char *buf) +{ + struct qedi_ctx *qedi = data; + struct nvm_iscsi_initiator *initiator; + char *str = buf; + int rc; + struct nvm_iscsi_block *block; + + block = qedi_get_nvram_block(qedi); + if (!block) + return 0; + + initiator = &block->initiator; + + switch (type) { + case ISCSI_BOOT_INI_INITIATOR_NAME: + rc = snprintf(str, NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN, "%s\n", + initiator->initiator_name.byte); + break; + default: + rc = 0; + break; + } + return rc; +} + +static umode_t qedi_ini_get_attr_visibility(void *data, int type) +{ + int rc; + + switch (type) { + case ISCSI_BOOT_INI_INITIATOR_NAME: + rc = 0444; + break; + default: + rc = 0; + break; + } + return rc; +} + +static ssize_t +qedi_show_boot_tgt_info(struct qedi_ctx *qedi, int type, + char *buf, enum qedi_nvm_tgts idx) +{ + char *str = buf; + int rc = 1; + u32 ctrl_flags, ipv6_en, chap_en, mchap_en, ip_len; + struct nvm_iscsi_block *block; + char *chap_name, *chap_secret; + char *mchap_name, *mchap_secret; + + block = qedi_get_nvram_block(qedi); + if (!block) + goto exit_show_tgt_info; + + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_EVT, + "Port:%d, tgt_idx:%d\n", + GET_FIELD2(block->id, NVM_ISCSI_CFG_BLK_MAPPED_PF_ID), idx); + + ctrl_flags = block->target[idx].ctrl_flags & + NVM_ISCSI_CFG_TARGET_ENABLED; + + if (!ctrl_flags) { + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_EVT, + "Target disabled\n"); + goto exit_show_tgt_info; + } + + ipv6_en = block->generic.ctrl_flags & + NVM_ISCSI_CFG_GEN_IPV6_ENABLED; + ip_len = ipv6_en ? IPV6_LEN : IPV4_LEN; + chap_en = block->generic.ctrl_flags & + NVM_ISCSI_CFG_GEN_CHAP_ENABLED; + chap_name = chap_en ? block->initiator.chap_name.byte : NULL; + chap_secret = chap_en ? block->initiator.chap_password.byte : NULL; + + mchap_en = block->generic.ctrl_flags & + NVM_ISCSI_CFG_GEN_CHAP_MUTUAL_ENABLED; + mchap_name = mchap_en ? block->target[idx].chap_name.byte : NULL; + mchap_secret = mchap_en ? block->target[idx].chap_password.byte : NULL; + + switch (type) { + case ISCSI_BOOT_TGT_NAME: + rc = snprintf(str, NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN, "%s\n", + block->target[idx].target_name.byte); + break; + case ISCSI_BOOT_TGT_IP_ADDR: + if (ipv6_en) + rc = snprintf(str, ip_len, "%pI6\n", + block->target[idx].ipv6_addr.byte); + else + rc = snprintf(str, ip_len, "%pI4\n", + block->target[idx].ipv4_addr.byte); + break; + case ISCSI_BOOT_TGT_PORT: + rc = snprintf(str, 12, "%d\n", + GET_FIELD2(block->target[idx].generic_cont0, + NVM_ISCSI_CFG_TARGET_TCP_PORT)); + break; + case ISCSI_BOOT_TGT_LUN: + rc = snprintf(str, 22, "%.*d\n", + block->target[idx].lun.value[1], + block->target[idx].lun.value[0]); + break; + case ISCSI_BOOT_TGT_CHAP_NAME: + rc = snprintf(str, NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN, "%s\n", + chap_name); + break; + case ISCSI_BOOT_TGT_CHAP_SECRET: + rc = snprintf(str, NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN, "%s\n", + chap_secret); + break; + case ISCSI_BOOT_TGT_REV_CHAP_NAME: + rc = snprintf(str, NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN, "%s\n", + mchap_name); + break; + case ISCSI_BOOT_TGT_REV_CHAP_SECRET: + rc = snprintf(str, NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN, "%s\n", + mchap_secret); + break; + case ISCSI_BOOT_TGT_FLAGS: + rc = snprintf(str, 3, "%hhd\n", SYSFS_FLAG_FW_SEL_BOOT); + break; + case ISCSI_BOOT_TGT_NIC_ASSOC: + rc = snprintf(str, 3, "0\n"); + break; + default: + rc = 0; + break; + } + +exit_show_tgt_info: + return rc; +} + +static ssize_t qedi_show_boot_tgt_pri_info(void *data, int type, char *buf) +{ + struct qedi_ctx *qedi = data; + + return qedi_show_boot_tgt_info(qedi, type, buf, QEDI_NVM_TGT_PRI); +} + +static ssize_t qedi_show_boot_tgt_sec_info(void *data, int type, char *buf) +{ + struct qedi_ctx *qedi = data; + + return qedi_show_boot_tgt_info(qedi, type, buf, QEDI_NVM_TGT_SEC); +} + +static umode_t qedi_tgt_get_attr_visibility(void *data, int type) +{ + int rc; + + switch (type) { + case ISCSI_BOOT_TGT_NAME: + case ISCSI_BOOT_TGT_IP_ADDR: + case ISCSI_BOOT_TGT_PORT: + case ISCSI_BOOT_TGT_LUN: + case ISCSI_BOOT_TGT_CHAP_NAME: + case ISCSI_BOOT_TGT_CHAP_SECRET: + case ISCSI_BOOT_TGT_REV_CHAP_NAME: + case ISCSI_BOOT_TGT_REV_CHAP_SECRET: + case ISCSI_BOOT_TGT_NIC_ASSOC: + case ISCSI_BOOT_TGT_FLAGS: + rc = 0444; + break; + default: + rc = 0; + break; + } + return rc; +} + +static void qedi_boot_release(void *data) +{ + struct qedi_ctx *qedi = data; + + scsi_host_put(qedi->shost); +} + +static int qedi_get_boot_info(struct qedi_ctx *qedi) +{ + int ret = 1; + u16 len; + + len = sizeof(struct nvm_iscsi_cfg); + + QEDI_INFO(&qedi->dbg_ctx, QEDI_LOG_INFO, + "Get NVM iSCSI CFG image\n"); + ret = qedi_ops->common->nvm_get_image(qedi->cdev, + QED_NVM_IMAGE_ISCSI_CFG, + (char *)qedi->iscsi_cfg, len); + if (ret) + QEDI_ERR(&qedi->dbg_ctx, + "Could not get NVM image. ret = %d\n", ret); + + return ret; +} + +static int qedi_setup_boot_info(struct qedi_ctx *qedi) +{ + struct iscsi_boot_kobj *boot_kobj; + + if (qedi_get_boot_info(qedi)) + return -EPERM; + + qedi->boot_kset = iscsi_boot_create_host_kset(qedi->shost->host_no); + if (!qedi->boot_kset) + goto kset_free; + + if (!scsi_host_get(qedi->shost)) + goto kset_free; + + boot_kobj = iscsi_boot_create_target(qedi->boot_kset, 0, qedi, + qedi_show_boot_tgt_pri_info, + qedi_tgt_get_attr_visibility, + qedi_boot_release); + if (!boot_kobj) + goto put_host; + + if (!scsi_host_get(qedi->shost)) + goto kset_free; + + boot_kobj = iscsi_boot_create_target(qedi->boot_kset, 1, qedi, + qedi_show_boot_tgt_sec_info, + qedi_tgt_get_attr_visibility, + qedi_boot_release); + if (!boot_kobj) + goto put_host; + + if (!scsi_host_get(qedi->shost)) + goto kset_free; + + boot_kobj = iscsi_boot_create_initiator(qedi->boot_kset, 0, qedi, + qedi_show_boot_ini_info, + qedi_ini_get_attr_visibility, + qedi_boot_release); + if (!boot_kobj) + goto put_host; + + if (!scsi_host_get(qedi->shost)) + goto kset_free; + + boot_kobj = iscsi_boot_create_ethernet(qedi->boot_kset, 0, qedi, + qedi_show_boot_eth_info, + qedi_eth_get_attr_visibility, + qedi_boot_release); + if (!boot_kobj) + goto put_host; + + return 0; + +put_host: + scsi_host_put(qedi->shost); +kset_free: + iscsi_boot_destroy_kset(qedi->boot_kset); + return -ENOMEM; +} + static void __qedi_remove(struct pci_dev *pdev, int mode) { struct qedi_ctx *qedi = pci_get_drvdata(pdev); @@ -1724,6 +2136,9 @@ static void __qedi_remove(struct pci_dev *pdev, int mode) qedi->ll2_recv_thread = NULL; } qedi_ll2_free_skbs(qedi); + + if (qedi->boot_kset) + iscsi_boot_destroy_kset(qedi->boot_kset); } } @@ -1967,6 +2382,10 @@ static int __qedi_probe(struct pci_dev *pdev, int mode) /* F/w needs 1st task context memory entry for performance */ set_bit(QEDI_RESERVE_TASK_ID, qedi->task_idx_map); atomic_set(&qedi->num_offloads, 0); + + if (qedi_setup_boot_info(qedi)) + QEDI_ERR(&qedi->dbg_ctx, + "No iSCSI boot target configured\n"); } return 0; diff --git a/drivers/scsi/qedi/qedi_nvm_iscsi_cfg.h b/drivers/scsi/qedi/qedi_nvm_iscsi_cfg.h new file mode 100644 index 000000000000..df39b69b366d --- /dev/null +++ b/drivers/scsi/qedi/qedi_nvm_iscsi_cfg.h @@ -0,0 +1,210 @@ +/* + * QLogic iSCSI Offload Driver + * Copyright (c) 2016 Cavium Inc. + * + * This software is available 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. + */ + +#ifndef NVM_ISCSI_CFG_H +#define NVM_ISCSI_CFG_H + +#define NUM_OF_ISCSI_TARGET_PER_PF 4 /* Defined as per the + * ISCSI IBFT constraint + */ +#define NUM_OF_ISCSI_PF_SUPPORTED 4 /* One PF per Port - + * assuming 4 port card + */ + +#define NVM_ISCSI_CFG_DHCP_NAME_MAX_LEN 256 + +union nvm_iscsi_dhcp_vendor_id { + u32 value[NVM_ISCSI_CFG_DHCP_NAME_MAX_LEN / 4]; + u8 byte[NVM_ISCSI_CFG_DHCP_NAME_MAX_LEN]; +}; + +#define NVM_ISCSI_IPV4_ADDR_BYTE_LEN 4 +union nvm_iscsi_ipv4_addr { + u32 addr; + u8 byte[NVM_ISCSI_IPV4_ADDR_BYTE_LEN]; +}; + +#define NVM_ISCSI_IPV6_ADDR_BYTE_LEN 16 +union nvm_iscsi_ipv6_addr { + u32 addr[4]; + u8 byte[NVM_ISCSI_IPV6_ADDR_BYTE_LEN]; +}; + +struct nvm_iscsi_initiator_ipv4 { + union nvm_iscsi_ipv4_addr addr; /* 0x0 */ + union nvm_iscsi_ipv4_addr subnet_mask; /* 0x4 */ + union nvm_iscsi_ipv4_addr gateway; /* 0x8 */ + union nvm_iscsi_ipv4_addr primary_dns; /* 0xC */ + union nvm_iscsi_ipv4_addr secondary_dns; /* 0x10 */ + union nvm_iscsi_ipv4_addr dhcp_addr; /* 0x14 */ + + union nvm_iscsi_ipv4_addr isns_server; /* 0x18 */ + union nvm_iscsi_ipv4_addr slp_server; /* 0x1C */ + union nvm_iscsi_ipv4_addr primay_radius_server; /* 0x20 */ + union nvm_iscsi_ipv4_addr secondary_radius_server; /* 0x24 */ + + union nvm_iscsi_ipv4_addr rsvd[4]; /* 0x28 */ +}; + +struct nvm_iscsi_initiator_ipv6 { + union nvm_iscsi_ipv6_addr addr; /* 0x0 */ + union nvm_iscsi_ipv6_addr subnet_mask; /* 0x10 */ + union nvm_iscsi_ipv6_addr gateway; /* 0x20 */ + union nvm_iscsi_ipv6_addr primary_dns; /* 0x30 */ + union nvm_iscsi_ipv6_addr secondary_dns; /* 0x40 */ + union nvm_iscsi_ipv6_addr dhcp_addr; /* 0x50 */ + + union nvm_iscsi_ipv6_addr isns_server; /* 0x60 */ + union nvm_iscsi_ipv6_addr slp_server; /* 0x70 */ + union nvm_iscsi_ipv6_addr primay_radius_server; /* 0x80 */ + union nvm_iscsi_ipv6_addr secondary_radius_server; /* 0x90 */ + + union nvm_iscsi_ipv6_addr rsvd[3]; /* 0xA0 */ + + u32 config; /* 0xD0 */ +#define NVM_ISCSI_CFG_INITIATOR_IPV6_SUBNET_MASK_PREFIX_MASK 0x000000FF +#define NVM_ISCSI_CFG_INITIATOR_IPV6_SUBNET_MASK_PREFIX_OFFSET 0 + + u32 rsvd_1[3]; +}; + +#define NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN 256 +union nvm_iscsi_name { + u32 value[NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN / 4]; + u8 byte[NVM_ISCSI_CFG_ISCSI_NAME_MAX_LEN]; +}; + +#define NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN 256 +union nvm_iscsi_chap_name { + u32 value[NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN / 4]; + u8 byte[NVM_ISCSI_CFG_CHAP_NAME_MAX_LEN]; +}; + +#define NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN 16 /* md5 need per RFC1996 + * is 16 octets + */ +union nvm_iscsi_chap_password { + u32 value[NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN / 4]; + u8 byte[NVM_ISCSI_CFG_CHAP_PWD_MAX_LEN]; +}; + +union nvm_iscsi_lun { + u8 byte[8]; + u32 value[2]; +}; + +struct nvm_iscsi_generic { + u32 ctrl_flags; /* 0x0 */ +#define NVM_ISCSI_CFG_GEN_CHAP_ENABLED BIT(0) +#define NVM_ISCSI_CFG_GEN_DHCP_TCPIP_CONFIG_ENABLED BIT(1) +#define NVM_ISCSI_CFG_GEN_DHCP_ISCSI_CONFIG_ENABLED BIT(2) +#define NVM_ISCSI_CFG_GEN_IPV6_ENABLED BIT(3) +#define NVM_ISCSI_CFG_GEN_IPV4_FALLBACK_ENABLED BIT(4) +#define NVM_ISCSI_CFG_GEN_ISNS_WORLD_LOGIN BIT(5) +#define NVM_ISCSI_CFG_GEN_ISNS_SELECTIVE_LOGIN BIT(6) +#define NVM_ISCSI_CFG_GEN_ADDR_REDIRECT_ENABLED BIT(7) +#define NVM_ISCSI_CFG_GEN_CHAP_MUTUAL_ENABLED BIT(8) + + u32 timeout; /* 0x4 */ +#define NVM_ISCSI_CFG_GEN_DHCP_REQUEST_TIMEOUT_MASK 0x0000FFFF +#define NVM_ISCSI_CFG_GEN_DHCP_REQUEST_TIMEOUT_OFFSET 0 +#define NVM_ISCSI_CFG_GEN_PORT_LOGIN_TIMEOUT_MASK 0xFFFF0000 +#define NVM_ISCSI_CFG_GEN_PORT_LOGIN_TIMEOUT_OFFSET 16 + + union nvm_iscsi_dhcp_vendor_id dhcp_vendor_id; /* 0x8 */ + u32 rsvd[62]; /* 0x108 */ +}; + +struct nvm_iscsi_initiator { + struct nvm_iscsi_initiator_ipv4 ipv4; /* 0x0 */ + struct nvm_iscsi_initiator_ipv6 ipv6; /* 0x38 */ + + union nvm_iscsi_name initiator_name; /* 0x118 */ + union nvm_iscsi_chap_name chap_name; /* 0x218 */ + union nvm_iscsi_chap_password chap_password; /* 0x318 */ + + u32 generic_cont0; /* 0x398 */ +#define NVM_ISCSI_CFG_INITIATOR_VLAN_MASK 0x0000FFFF +#define NVM_ISCSI_CFG_INITIATOR_VLAN_OFFSET 0 +#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_MASK 0x00030000 +#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_OFFSET 16 +#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_4 1 +#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_6 2 +#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_4_AND_6 3 + + u32 ctrl_flags; +#define NVM_ISCSI_CFG_INITIATOR_IP_VERSION_PRIORITY_V6 BIT(0) +#define NVM_ISCSI_CFG_INITIATOR_VLAN_ENABLED BIT(1) + + u32 rsvd[116]; /* 0x32C */ +}; + +struct nvm_iscsi_target { + u32 ctrl_flags; /* 0x0 */ +#define NVM_ISCSI_CFG_TARGET_ENABLED BIT(0) +#define NVM_ISCSI_CFG_BOOT_TIME_LOGIN_STATUS BIT(1) + + u32 generic_cont0; /* 0x4 */ +#define NVM_ISCSI_CFG_TARGET_TCP_PORT_MASK 0x0000FFFF +#define NVM_ISCSI_CFG_TARGET_TCP_PORT_OFFSET 0 + + u32 ip_ver; +#define NVM_ISCSI_CFG_IPv4 4 +#define NVM_ISCSI_CFG_IPv6 6 + + u32 rsvd_1[7]; /* 0x24 */ + union nvm_iscsi_ipv4_addr ipv4_addr; /* 0x28 */ + union nvm_iscsi_ipv6_addr ipv6_addr; /* 0x2C */ + union nvm_iscsi_lun lun; /* 0x3C */ + + union nvm_iscsi_name target_name; /* 0x44 */ + union nvm_iscsi_chap_name chap_name; /* 0x144 */ + union nvm_iscsi_chap_password chap_password; /* 0x244 */ + + u32 rsvd_2[107]; /* 0x2C4 */ +}; + +struct nvm_iscsi_block { + u32 id; /* 0x0 */ +#define NVM_ISCSI_CFG_BLK_MAPPED_PF_ID_MASK 0x0000000F +#define NVM_ISCSI_CFG_BLK_MAPPED_PF_ID_OFFSET 0 +#define NVM_ISCSI_CFG_BLK_CTRL_FLAG_MASK 0x00000FF0 +#define NVM_ISCSI_CFG_BLK_CTRL_FLAG_OFFSET 4 +#define NVM_ISCSI_CFG_BLK_CTRL_FLAG_IS_NOT_EMPTY BIT(0) +#define NVM_ISCSI_CFG_BLK_CTRL_FLAG_PF_MAPPED BIT(1) + + u32 rsvd_1[5]; /* 0x4 */ + + struct nvm_iscsi_generic generic; /* 0x18 */ + struct nvm_iscsi_initiator initiator; /* 0x218 */ + struct nvm_iscsi_target target[NUM_OF_ISCSI_TARGET_PER_PF]; + /* 0x718 */ + + u32 rsvd_2[58]; /* 0x1718 */ + /* total size - 0x1800 - 6K block */ +}; + +struct nvm_iscsi_cfg { + u32 id; /* 0x0 */ +#define NVM_ISCSI_CFG_BLK_VERSION_MINOR_MASK 0x000000FF +#define NVM_ISCSI_CFG_BLK_VERSION_MAJOR_MASK 0x0000FF00 +#define NVM_ISCSI_CFG_BLK_SIGNATURE_MASK 0xFFFF0000 +#define NVM_ISCSI_CFG_BLK_SIGNATURE 0x49430000 /* IC - Iscsi + * Config + */ + +#define NVM_ISCSI_CFG_BLK_VERSION_MAJOR 0 +#define NVM_ISCSI_CFG_BLK_VERSION_MINOR 10 +#define NVM_ISCSI_CFG_BLK_VERSION ((NVM_ISCSI_CFG_BLK_VERSION_MAJOR << 8) | \ + NVM_ISCSI_CFG_BLK_VERSION_MINOR) + + struct nvm_iscsi_block block[NUM_OF_ISCSI_PF_SUPPORTED]; /* 0x4 */ +}; + +#endif diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index c2dc836dc484..e101cd3043b9 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -3727,7 +3727,7 @@ static struct qla_tgt_cmd *qlt_ctio_to_cmd(struct scsi_qla_host *vha, h &= QLA_CMD_HANDLE_MASK; if (h != QLA_TGT_NULL_HANDLE) { - if (unlikely(h > req->num_outstanding_cmds)) { + if (unlikely(h >= req->num_outstanding_cmds)) { ql_dbg(ql_dbg_tgt, vha, 0xe052, "qla_target(%d): Wrong handle %x received\n", vha->vp_idx, handle); diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c index 33142610882f..b18646d6057f 100644 --- a/drivers/scsi/qla2xxx/qla_tmpl.c +++ b/drivers/scsi/qla2xxx/qla_tmpl.c @@ -401,9 +401,6 @@ qla27xx_fwdt_entry_t263(struct scsi_qla_host *vha, for (i = 0; i < vha->hw->max_req_queues; i++) { struct req_que *req = vha->hw->req_q_map[i]; - if (!test_bit(i, vha->hw->req_qid_map)) - continue; - if (req || !buf) { length = req ? req->length : REQUEST_ENTRY_CNT_24XX; @@ -418,9 +415,6 @@ qla27xx_fwdt_entry_t263(struct scsi_qla_host *vha, for (i = 0; i < vha->hw->max_rsp_queues; i++) { struct rsp_que *rsp = vha->hw->rsp_q_map[i]; - if (!test_bit(i, vha->hw->rsp_qid_map)) - continue; - if (rsp || !buf) { length = rsp ? rsp->length : RESPONSE_ENTRY_CNT_MQ; @@ -660,9 +654,6 @@ qla27xx_fwdt_entry_t274(struct scsi_qla_host *vha, for (i = 0; i < vha->hw->max_req_queues; i++) { struct req_que *req = vha->hw->req_q_map[i]; - if (!test_bit(i, vha->hw->req_qid_map)) - continue; - if (req || !buf) { qla27xx_insert16(i, buf, len); qla27xx_insert16(1, buf, len); @@ -675,9 +666,6 @@ qla27xx_fwdt_entry_t274(struct scsi_qla_host *vha, for (i = 0; i < vha->hw->max_rsp_queues; i++) { struct rsp_que *rsp = vha->hw->rsp_q_map[i]; - if (!test_bit(i, vha->hw->rsp_qid_map)) - continue; - if (rsp || !buf) { qla27xx_insert16(i, buf, len); qla27xx_insert16(1, buf, len); diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index b20da0d27ad7..3f82ea1b72dc 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -500,7 +500,6 @@ static int tcm_qla2xxx_handle_cmd(scsi_qla_host_t *vha, struct qla_tgt_cmd *cmd, static void tcm_qla2xxx_handle_data_work(struct work_struct *work) { struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); - unsigned long flags; /* * Ensure that the complete FCP WRITE payload has been received. @@ -508,17 +507,6 @@ static void tcm_qla2xxx_handle_data_work(struct work_struct *work) */ cmd->cmd_in_wq = 0; - spin_lock_irqsave(&cmd->cmd_lock, flags); - cmd->data_work = 1; - if (cmd->aborted) { - cmd->data_work_free = 1; - spin_unlock_irqrestore(&cmd->cmd_lock, flags); - - tcm_qla2xxx_free_cmd(cmd); - return; - } - spin_unlock_irqrestore(&cmd->cmd_lock, flags); - cmd->qpair->tgt_counters.qla_core_ret_ctio++; if (!cmd->write_data_transferred) { /* @@ -765,31 +753,13 @@ static void tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd) qlt_xmit_tm_rsp(mcmd); } -#define DATA_WORK_NOT_FREE(_cmd) (_cmd->data_work && !_cmd->data_work_free) static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd) { struct qla_tgt_cmd *cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); - unsigned long flags; if (qlt_abort_cmd(cmd)) return; - - spin_lock_irqsave(&cmd->cmd_lock, flags); - if ((cmd->state == QLA_TGT_STATE_NEW)|| - ((cmd->state == QLA_TGT_STATE_DATA_IN) && - DATA_WORK_NOT_FREE(cmd))) { - cmd->data_work_free = 1; - spin_unlock_irqrestore(&cmd->cmd_lock, flags); - /* - * cmd has not reached fw, Use this trigger to free it. - */ - tcm_qla2xxx_free_cmd(cmd); - return; - } - spin_unlock_irqrestore(&cmd->cmd_lock, flags); - return; - } static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *, diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 3d38c6d463b8..1bf274e3b2b6 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -800,7 +800,11 @@ MODULE_LICENSE("GPL"); module_param(scsi_logging_level, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(scsi_logging_level, "a bit mask of logging levels"); +#ifdef CONFIG_SCSI_MQ_DEFAULT bool scsi_use_blk_mq = true; +#else +bool scsi_use_blk_mq = false; +#endif module_param_named(use_blk_mq, scsi_use_blk_mq, bool, S_IWUSR | S_IRUGO); static int __init init_scsi(void) diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 7e24aa30c3b0..892fbd9800d9 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -1286,7 +1286,7 @@ store_fc_vport_delete(struct device *dev, struct device_attribute *attr, unsigned long flags; spin_lock_irqsave(shost->host_lock, flags); - if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) { + if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING | FC_VPORT_DELETING)) { spin_unlock_irqrestore(shost->host_lock, flags); return -EBUSY; } @@ -2430,8 +2430,10 @@ fc_remove_host(struct Scsi_Host *shost) spin_lock_irqsave(shost->host_lock, flags); /* Remove any vports */ - list_for_each_entry_safe(vport, next_vport, &fc_host->vports, peers) + list_for_each_entry_safe(vport, next_vport, &fc_host->vports, peers) { + vport->flags |= FC_VPORT_DELETING; fc_queue_work(shost, &vport->vport_delete_work); + } /* Remove any remote ports */ list_for_each_entry_safe(rport, next_rport, diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index bea36adeee17..e2647f2d4430 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1277,6 +1277,9 @@ static void sd_uninit_command(struct scsi_cmnd *SCpnt) { struct request *rq = SCpnt->request; + if (SCpnt->flags & SCMD_ZONE_WRITE_LOCK) + sd_zbc_write_unlock_zone(SCpnt); + if (rq->rq_flags & RQF_SPECIAL_PAYLOAD) __free_page(rq->special_vec.bv_page); diff --git a/drivers/scsi/sd_zbc.c b/drivers/scsi/sd_zbc.c index 96855df9f49d..8aa54779aac1 100644 --- a/drivers/scsi/sd_zbc.c +++ b/drivers/scsi/sd_zbc.c @@ -294,6 +294,9 @@ int sd_zbc_write_lock_zone(struct scsi_cmnd *cmd) test_and_set_bit(zno, sdkp->zones_wlock)) return BLKPREP_DEFER; + WARN_ON_ONCE(cmd->flags & SCMD_ZONE_WRITE_LOCK); + cmd->flags |= SCMD_ZONE_WRITE_LOCK; + return BLKPREP_OK; } @@ -302,9 +305,10 @@ void sd_zbc_write_unlock_zone(struct scsi_cmnd *cmd) struct request *rq = cmd->request; struct scsi_disk *sdkp = scsi_disk(rq->rq_disk); - if (sdkp->zones_wlock) { + if (sdkp->zones_wlock && cmd->flags & SCMD_ZONE_WRITE_LOCK) { unsigned int zno = sd_zbc_zone_no(sdkp, blk_rq_pos(rq)); WARN_ON_ONCE(!test_bit(zno, sdkp->zones_wlock)); + cmd->flags &= ~SCMD_ZONE_WRITE_LOCK; clear_bit_unlock(zno, sdkp->zones_wlock); smp_mb__after_atomic(); } @@ -335,9 +339,6 @@ void sd_zbc_complete(struct scsi_cmnd *cmd, case REQ_OP_WRITE_ZEROES: case REQ_OP_WRITE_SAME: - /* Unlock the zone */ - sd_zbc_write_unlock_zone(cmd); - if (result && sshdr->sense_key == ILLEGAL_REQUEST && sshdr->asc == 0x21) diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index f1cdf32d7514..8927f9f54ad9 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -99,7 +99,7 @@ static int ses_recv_diag(struct scsi_device *sdev, int page_code, ret = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buf, bufflen, NULL, SES_TIMEOUT, SES_RETRIES, NULL); - if (unlikely(!ret)) + if (unlikely(ret)) return ret; recv_page_code = ((unsigned char *)buf)[0]; diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 21225d62b0c1..d7ff71e0c85c 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -751,29 +751,6 @@ sg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, return count; } -static bool sg_is_valid_dxfer(sg_io_hdr_t *hp) -{ - switch (hp->dxfer_direction) { - case SG_DXFER_NONE: - if (hp->dxferp || hp->dxfer_len > 0) - return false; - return true; - case SG_DXFER_TO_DEV: - case SG_DXFER_FROM_DEV: - case SG_DXFER_TO_FROM_DEV: - if (!hp->dxferp || hp->dxfer_len == 0) - return false; - return true; - case SG_DXFER_UNKNOWN: - if ((!hp->dxferp && hp->dxfer_len) || - (hp->dxferp && hp->dxfer_len == 0)) - return false; - return true; - default: - return false; - } -} - static int sg_common_write(Sg_fd * sfp, Sg_request * srp, unsigned char *cmnd, int timeout, int blocking) @@ -794,7 +771,7 @@ sg_common_write(Sg_fd * sfp, Sg_request * srp, "sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n", (int) cmnd[0], (int) hp->cmd_len)); - if (!sg_is_valid_dxfer(hp)) + if (hp->dxfer_len >= SZ_256M) return -EINVAL; k = sg_start_req(srp, cmnd); diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index 07ec8a8877de..e164ffade38a 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -690,7 +690,7 @@ struct pqi_config_table_heartbeat { #define PQI_MAX_OUTSTANDING_REQUESTS ((u32)~0) #define PQI_MAX_OUTSTANDING_REQUESTS_KDUMP 32 -#define PQI_MAX_TRANSFER_SIZE (4 * 1024U * 1024U) +#define PQI_MAX_TRANSFER_SIZE (1024U * 1024U) #define PQI_MAX_TRANSFER_SIZE_KDUMP (512 * 1024U) #define RAID_MAP_MAX_ENTRIES 1024 diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 8e5013d9cad4..94e402ed30f6 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -4299,11 +4299,11 @@ static int st_probe(struct device *dev) kref_init(&tpnt->kref); tpnt->disk = disk; disk->private_data = &tpnt->driver; - disk->queue = SDp->request_queue; /* SCSI tape doesn't register this gendisk via add_disk(). Manually * take queue reference that release_disk() expects. */ - if (!blk_get_queue(disk->queue)) + if (!blk_get_queue(SDp->request_queue)) goto out_put_disk; + disk->queue = SDp->request_queue; tpnt->driver = &st_template; tpnt->device = SDp; diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 8b93197daefe..9be211d68b15 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -837,6 +837,7 @@ static struct scsi_host_template virtscsi_host_template_multi = { .eh_abort_handler = virtscsi_abort, .eh_device_reset_handler = virtscsi_device_reset, .eh_timed_out = virtscsi_eh_timed_out, + .slave_alloc = virtscsi_device_alloc, .can_queue = 1024, .dma_boundary = UINT_MAX, diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 3039072911a5..afc7ecc3c187 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -200,16 +200,11 @@ static int imx7_pgc_domain_probe(struct platform_device *pdev) domain->dev = &pdev->dev; - ret = pm_genpd_init(&domain->genpd, NULL, true); - if (ret) { - dev_err(domain->dev, "Failed to init power domain\n"); - return ret; - } - domain->regulator = devm_regulator_get_optional(domain->dev, "power"); if (IS_ERR(domain->regulator)) { if (PTR_ERR(domain->regulator) != -ENODEV) { - dev_err(domain->dev, "Failed to get domain's regulator\n"); + if (PTR_ERR(domain->regulator) != -EPROBE_DEFER) + dev_err(domain->dev, "Failed to get domain's regulator\n"); return PTR_ERR(domain->regulator); } } else { @@ -217,6 +212,12 @@ static int imx7_pgc_domain_probe(struct platform_device *pdev) domain->voltage, domain->voltage); } + ret = pm_genpd_init(&domain->genpd, NULL, true); + if (ret) { + dev_err(domain->dev, "Failed to init power domain\n"); + return ret; + } + ret = of_genpd_add_provider_simple(domain->dev->of_node, &domain->genpd); if (ret) { diff --git a/drivers/soc/ti/knav_qmss_queue.c b/drivers/soc/ti/knav_qmss_queue.c index 279e7c5551dd..39225de9d7f1 100644 --- a/drivers/soc/ti/knav_qmss_queue.c +++ b/drivers/soc/ti/knav_qmss_queue.c @@ -745,6 +745,9 @@ void *knav_pool_create(const char *name, bool slot_found; int ret; + if (!kdev) + return ERR_PTR(-EPROBE_DEFER); + if (!kdev->dev) return ERR_PTR(-ENODEV); diff --git a/drivers/soc/ti/ti_sci_pm_domains.c b/drivers/soc/ti/ti_sci_pm_domains.c index b0b283810e72..de31b9389e2e 100644 --- a/drivers/soc/ti/ti_sci_pm_domains.c +++ b/drivers/soc/ti/ti_sci_pm_domains.c @@ -176,6 +176,8 @@ static int ti_sci_pm_domain_probe(struct platform_device *pdev) ti_sci_pd->dev = dev; + ti_sci_pd->pd.name = "ti_sci_pd"; + ti_sci_pd->pd.attach_dev = ti_sci_pd_attach_dev; ti_sci_pd->pd.detach_dev = ti_sci_pd_detach_dev; diff --git a/drivers/soc/zte/Kconfig b/drivers/soc/zte/Kconfig index 20bde38ce2f9..e9d750c510cd 100644 --- a/drivers/soc/zte/Kconfig +++ b/drivers/soc/zte/Kconfig @@ -2,6 +2,7 @@ # ZTE SoC drivers # menuconfig SOC_ZTE + depends on ARCH_ZX || COMPILE_TEST bool "ZTE SoC driver support" if SOC_ZTE diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 2afe3597982e..360b8218f322 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2015, 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 @@ -29,6 +29,7 @@ #define PMIC_ARB_VERSION 0x0000 #define PMIC_ARB_VERSION_V2_MIN 0x20010000 #define PMIC_ARB_VERSION_V3_MIN 0x30000000 +#define PMIC_ARB_VERSION_V5_MIN 0x50000000 #define PMIC_ARB_INT_EN 0x0004 /* PMIC Arbiter channel registers offsets */ @@ -39,7 +40,6 @@ #define PMIC_ARB_WDATA1 0x14 #define PMIC_ARB_RDATA0 0x18 #define PMIC_ARB_RDATA1 0x1C -#define PMIC_ARB_REG_CHNL(N) (0x800 + 0x4 * (N)) /* Mapping Table */ #define SPMI_MAPPING_TABLE_REG(N) (0x0B00 + (4 * (N))) @@ -51,7 +51,9 @@ #define SPMI_MAPPING_TABLE_TREE_DEPTH 16 /* Maximum of 16-bits */ #define PMIC_ARB_MAX_PPID BIT(12) /* PPID is 12bit */ -#define PMIC_ARB_CHAN_VALID BIT(15) +#define PMIC_ARB_APID_VALID BIT(15) +#define PMIC_ARB_CHAN_IS_IRQ_OWNER(reg) ((reg) & BIT(24)) +#define INVALID_EE 0xFF /* Ownership Table */ #define SPMI_OWNERSHIP_TABLE_REG(N) (0x0700 + (4 * (N))) @@ -86,6 +88,15 @@ enum pmic_arb_cmd_op_code { PMIC_ARB_OP_ZERO_WRITE = 16, }; +/* + * PMIC arbiter version 5 uses different register offsets for read/write vs + * observer channels. + */ +enum pmic_arb_channel { + PMIC_ARB_CHANNEL_RW, + PMIC_ARB_CHANNEL_OBS, +}; + /* Maximum number of support PMIC peripherals */ #define PMIC_ARB_MAX_PERIPHS 512 #define PMIC_ARB_TIMEOUT_US 100 @@ -97,22 +108,23 @@ enum pmic_arb_cmd_op_code { /* interrupt enable bit */ #define SPMI_PIC_ACC_ENABLE_BIT BIT(0) -#define HWIRQ(slave_id, periph_id, irq_id, apid) \ +#define spec_to_hwirq(slave_id, periph_id, irq_id, apid) \ ((((slave_id) & 0xF) << 28) | \ (((periph_id) & 0xFF) << 20) | \ (((irq_id) & 0x7) << 16) | \ (((apid) & 0x1FF) << 0)) -#define HWIRQ_SID(hwirq) (((hwirq) >> 28) & 0xF) -#define HWIRQ_PER(hwirq) (((hwirq) >> 20) & 0xFF) -#define HWIRQ_IRQ(hwirq) (((hwirq) >> 16) & 0x7) -#define HWIRQ_APID(hwirq) (((hwirq) >> 0) & 0x1FF) +#define hwirq_to_sid(hwirq) (((hwirq) >> 28) & 0xF) +#define hwirq_to_per(hwirq) (((hwirq) >> 20) & 0xFF) +#define hwirq_to_irq(hwirq) (((hwirq) >> 16) & 0x7) +#define hwirq_to_apid(hwirq) (((hwirq) >> 0) & 0x1FF) struct pmic_arb_ver_ops; struct apid_data { u16 ppid; - u8 owner; + u8 write_ee; + u8 irq_ee; }; /** @@ -128,13 +140,11 @@ struct apid_data { * @ee: the current Execution Environment * @min_apid: minimum APID (used for bounding IRQ search) * @max_apid: maximum APID - * @max_periph: maximum number of PMIC peripherals supported by HW. * @mapping_table: in-memory copy of PPID -> APID mapping table. * @domain: irq domain object for PMIC IRQ domain * @spmic: SPMI controller object * @ver_ops: version dependent operations. - * @ppid_to_apid in-memory copy of PPID -> channel (APID) mapping table. - * v2 only. + * @ppid_to_apid in-memory copy of PPID -> APID mapping table. */ struct spmi_pmic_arb { void __iomem *rd_base; @@ -149,7 +159,6 @@ struct spmi_pmic_arb { u8 ee; u16 min_apid; u16 max_apid; - u16 max_periph; u32 *mapping_table; DECLARE_BITMAP(mapping_table_valid, PMIC_ARB_MAX_PERIPHS); struct irq_domain *domain; @@ -165,92 +174,94 @@ struct spmi_pmic_arb { * * @ver_str: version string. * @ppid_to_apid: finds the apid for a given ppid. - * @mode: access rights to specified pmic peripheral. * @non_data_cmd: on v1 issues an spmi non-data command. * on v2 no HW support, returns -EOPNOTSUPP. * @offset: on v1 offset of per-ee channel. * on v2 offset of per-ee and per-ppid channel. * @fmt_cmd: formats a GENI/SPMI command. - * @owner_acc_status: on v1 offset of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn - * on v2 offset of SPMI_PIC_OWNERm_ACC_STATUSn. - * @acc_enable: on v1 offset of PMIC_ARB_SPMI_PIC_ACC_ENABLEn - * on v2 offset of SPMI_PIC_ACC_ENABLEn. - * @irq_status: on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_STATUSn - * on v2 offset of SPMI_PIC_IRQ_STATUSn. - * @irq_clear: on v1 offset of PMIC_ARB_SPMI_PIC_IRQ_CLEARn - * on v2 offset of SPMI_PIC_IRQ_CLEARn. + * @owner_acc_status: on v1 address of PMIC_ARB_SPMI_PIC_OWNERm_ACC_STATUSn + * on v2 address of SPMI_PIC_OWNERm_ACC_STATUSn. + * @acc_enable: on v1 address of PMIC_ARB_SPMI_PIC_ACC_ENABLEn + * on v2 address of SPMI_PIC_ACC_ENABLEn. + * @irq_status: on v1 address of PMIC_ARB_SPMI_PIC_IRQ_STATUSn + * on v2 address of SPMI_PIC_IRQ_STATUSn. + * @irq_clear: on v1 address of PMIC_ARB_SPMI_PIC_IRQ_CLEARn + * on v2 address of SPMI_PIC_IRQ_CLEARn. + * @apid_map_offset: offset of PMIC_ARB_REG_CHNLn */ struct pmic_arb_ver_ops { const char *ver_str; - int (*ppid_to_apid)(struct spmi_pmic_arb *pa, u8 sid, u16 addr, - u16 *apid); - int (*mode)(struct spmi_pmic_arb *dev, u8 sid, u16 addr, - mode_t *mode); + int (*ppid_to_apid)(struct spmi_pmic_arb *pmic_arb, u16 ppid); /* spmi commands (read_cmd, write_cmd, cmd) functionality */ - int (*offset)(struct spmi_pmic_arb *dev, u8 sid, u16 addr, - u32 *offset); + int (*offset)(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, + enum pmic_arb_channel ch_type); u32 (*fmt_cmd)(u8 opc, u8 sid, u16 addr, u8 bc); int (*non_data_cmd)(struct spmi_controller *ctrl, u8 opc, u8 sid); /* Interrupts controller functionality (offset of PIC registers) */ - u32 (*owner_acc_status)(u8 m, u16 n); - u32 (*acc_enable)(u16 n); - u32 (*irq_status)(u16 n); - u32 (*irq_clear)(u16 n); + void __iomem *(*owner_acc_status)(struct spmi_pmic_arb *pmic_arb, u8 m, + u16 n); + void __iomem *(*acc_enable)(struct spmi_pmic_arb *pmic_arb, u16 n); + void __iomem *(*irq_status)(struct spmi_pmic_arb *pmic_arb, u16 n); + void __iomem *(*irq_clear)(struct spmi_pmic_arb *pmic_arb, u16 n); + u32 (*apid_map_offset)(u16 n); }; -static inline void pmic_arb_base_write(struct spmi_pmic_arb *pa, +static inline void pmic_arb_base_write(struct spmi_pmic_arb *pmic_arb, u32 offset, u32 val) { - writel_relaxed(val, pa->wr_base + offset); + writel_relaxed(val, pmic_arb->wr_base + offset); } -static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pa, +static inline void pmic_arb_set_rd_cmd(struct spmi_pmic_arb *pmic_arb, u32 offset, u32 val) { - writel_relaxed(val, pa->rd_base + offset); + writel_relaxed(val, pmic_arb->rd_base + offset); } /** - * pa_read_data: reads pmic-arb's register and copy 1..4 bytes to buf + * pmic_arb_read_data: reads pmic-arb's register and copy 1..4 bytes to buf * @bc: byte count -1. range: 0..3 * @reg: register's address * @buf: output parameter, length must be bc + 1 */ -static void pa_read_data(struct spmi_pmic_arb *pa, u8 *buf, u32 reg, u8 bc) +static void +pmic_arb_read_data(struct spmi_pmic_arb *pmic_arb, u8 *buf, u32 reg, u8 bc) { - u32 data = __raw_readl(pa->rd_base + reg); + u32 data = __raw_readl(pmic_arb->rd_base + reg); memcpy(buf, &data, (bc & 3) + 1); } /** - * pa_write_data: write 1..4 bytes from buf to pmic-arb's register + * pmic_arb_write_data: write 1..4 bytes from buf to pmic-arb's register * @bc: byte-count -1. range: 0..3. * @reg: register's address. * @buf: buffer to write. length must be bc + 1. */ -static void -pa_write_data(struct spmi_pmic_arb *pa, const u8 *buf, u32 reg, u8 bc) +static void pmic_arb_write_data(struct spmi_pmic_arb *pmic_arb, const u8 *buf, + u32 reg, u8 bc) { u32 data = 0; memcpy(&data, buf, (bc & 3) + 1); - pmic_arb_base_write(pa, reg, data); + __raw_writel(data, pmic_arb->wr_base + reg); } static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, - void __iomem *base, u8 sid, u16 addr) + void __iomem *base, u8 sid, u16 addr, + enum pmic_arb_channel ch_type) { - struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); u32 status = 0; u32 timeout = PMIC_ARB_TIMEOUT_US; u32 offset; int rc; - rc = pa->ver_ops->offset(pa, sid, addr, &offset); - if (rc) + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, ch_type); + if (rc < 0) return rc; + offset = rc; offset += PMIC_ARB_STATUS; while (timeout--) { @@ -258,22 +269,19 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, if (status & PMIC_ARB_STATUS_DONE) { if (status & PMIC_ARB_STATUS_DENIED) { - dev_err(&ctrl->dev, - "%s: transaction denied (0x%x)\n", + dev_err(&ctrl->dev, "%s: transaction denied (0x%x)\n", __func__, status); return -EPERM; } if (status & PMIC_ARB_STATUS_FAILURE) { - dev_err(&ctrl->dev, - "%s: transaction failed (0x%x)\n", + dev_err(&ctrl->dev, "%s: transaction failed (0x%x)\n", __func__, status); return -EIO; } if (status & PMIC_ARB_STATUS_DROPPED) { - dev_err(&ctrl->dev, - "%s: transaction dropped (0x%x)\n", + dev_err(&ctrl->dev, "%s: transaction dropped (0x%x)\n", __func__, status); return -EIO; } @@ -283,8 +291,7 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, udelay(1); } - dev_err(&ctrl->dev, - "%s: timeout, status 0x%x\n", + dev_err(&ctrl->dev, "%s: timeout, status 0x%x\n", __func__, status); return -ETIMEDOUT; } @@ -292,22 +299,24 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, static int pmic_arb_non_data_cmd_v1(struct spmi_controller *ctrl, u8 opc, u8 sid) { - struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); unsigned long flags; u32 cmd; int rc; u32 offset; - rc = pa->ver_ops->offset(pa, sid, 0, &offset); - if (rc) + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, 0, PMIC_ARB_CHANNEL_RW); + if (rc < 0) return rc; + offset = rc; cmd = ((opc | 0x40) << 27) | ((sid & 0xf) << 20); - raw_spin_lock_irqsave(&pa->lock, flags); - pmic_arb_base_write(pa, offset + PMIC_ARB_CMD, cmd); - rc = pmic_arb_wait_for_done(ctrl, pa->wr_base, sid, 0); - raw_spin_unlock_irqrestore(&pa->lock, flags); + raw_spin_lock_irqsave(&pmic_arb->lock, flags); + pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd); + rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, 0, + PMIC_ARB_CHANNEL_RW); + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); return rc; } @@ -321,7 +330,7 @@ pmic_arb_non_data_cmd_v2(struct spmi_controller *ctrl, u8 opc, u8 sid) /* Non-data command */ static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) { - struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); dev_dbg(&ctrl->dev, "cmd op:0x%x sid:%d\n", opc, sid); @@ -329,38 +338,27 @@ static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP) return -EINVAL; - return pa->ver_ops->non_data_cmd(ctrl, opc, sid); + return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid); } static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, u16 addr, u8 *buf, size_t len) { - struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); unsigned long flags; u8 bc = len - 1; u32 cmd; int rc; u32 offset; - mode_t mode; - - rc = pa->ver_ops->offset(pa, sid, addr, &offset); - if (rc) - return rc; - rc = pa->ver_ops->mode(pa, sid, addr, &mode); - if (rc) + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, + PMIC_ARB_CHANNEL_OBS); + if (rc < 0) return rc; - if (!(mode & S_IRUSR)) { - dev_err(&pa->spmic->dev, - "error: impermissible read from peripheral sid:%d addr:0x%x\n", - sid, addr); - return -EPERM; - } - + offset = rc; if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { - dev_err(&ctrl->dev, - "pmic-arb supports 1..%d bytes per trans, but:%zu requested", + dev_err(&ctrl->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested", PMIC_ARB_MAX_TRANS_BYTES, len); return -EINVAL; } @@ -375,54 +373,45 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, else return -EINVAL; - cmd = pa->ver_ops->fmt_cmd(opc, sid, addr, bc); + cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); - raw_spin_lock_irqsave(&pa->lock, flags); - pmic_arb_set_rd_cmd(pa, offset + PMIC_ARB_CMD, cmd); - rc = pmic_arb_wait_for_done(ctrl, pa->rd_base, sid, addr); + raw_spin_lock_irqsave(&pmic_arb->lock, flags); + pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd); + rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr, + PMIC_ARB_CHANNEL_OBS); if (rc) goto done; - pa_read_data(pa, buf, offset + PMIC_ARB_RDATA0, + pmic_arb_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0, min_t(u8, bc, 3)); if (bc > 3) - pa_read_data(pa, buf + 4, offset + PMIC_ARB_RDATA1, bc - 4); + pmic_arb_read_data(pmic_arb, buf + 4, offset + PMIC_ARB_RDATA1, + bc - 4); done: - raw_spin_unlock_irqrestore(&pa->lock, flags); + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); return rc; } static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, - u16 addr, const u8 *buf, size_t len) + u16 addr, const u8 *buf, size_t len) { - struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); unsigned long flags; u8 bc = len - 1; u32 cmd; int rc; u32 offset; - mode_t mode; - rc = pa->ver_ops->offset(pa, sid, addr, &offset); - if (rc) - return rc; - - rc = pa->ver_ops->mode(pa, sid, addr, &mode); - if (rc) + rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, + PMIC_ARB_CHANNEL_RW); + if (rc < 0) return rc; - if (!(mode & S_IWUSR)) { - dev_err(&pa->spmic->dev, - "error: impermissible write to peripheral sid:%d addr:0x%x\n", - sid, addr); - return -EPERM; - } - + offset = rc; if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { - dev_err(&ctrl->dev, - "pmic-arb supports 1..%d bytes per trans, but:%zu requested", + dev_err(&ctrl->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested", PMIC_ARB_MAX_TRANS_BYTES, len); return -EINVAL; } @@ -430,7 +419,7 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, /* Check the opcode */ if (opc >= 0x40 && opc <= 0x5F) opc = PMIC_ARB_OP_WRITE; - else if (opc >= 0x00 && opc <= 0x0F) + else if (opc <= 0x0F) opc = PMIC_ARB_OP_EXT_WRITE; else if (opc >= 0x30 && opc <= 0x37) opc = PMIC_ARB_OP_EXT_WRITEL; @@ -439,18 +428,21 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, else return -EINVAL; - cmd = pa->ver_ops->fmt_cmd(opc, sid, addr, bc); + cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); /* Write data to FIFOs */ - raw_spin_lock_irqsave(&pa->lock, flags); - pa_write_data(pa, buf, offset + PMIC_ARB_WDATA0, min_t(u8, bc, 3)); + raw_spin_lock_irqsave(&pmic_arb->lock, flags); + pmic_arb_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0, + min_t(u8, bc, 3)); if (bc > 3) - pa_write_data(pa, buf + 4, offset + PMIC_ARB_WDATA1, bc - 4); + pmic_arb_write_data(pmic_arb, buf + 4, offset + PMIC_ARB_WDATA1, + bc - 4); /* Start the transaction */ - pmic_arb_base_write(pa, offset + PMIC_ARB_CMD, cmd); - rc = pmic_arb_wait_for_done(ctrl, pa->wr_base, sid, addr); - raw_spin_unlock_irqrestore(&pa->lock, flags); + pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd); + rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr, + PMIC_ARB_CHANNEL_RW); + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); return rc; } @@ -476,67 +468,64 @@ struct spmi_pmic_arb_qpnpint_type { static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, size_t len) { - struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 sid = HWIRQ_SID(d->hwirq); - u8 per = HWIRQ_PER(d->hwirq); + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); + u8 sid = hwirq_to_sid(d->hwirq); + u8 per = hwirq_to_per(d->hwirq); - if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, + if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid, (per << 8) + reg, buf, len)) - dev_err_ratelimited(&pa->spmic->dev, - "failed irqchip transaction on %x\n", + dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n", d->irq); } static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) { - struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 sid = HWIRQ_SID(d->hwirq); - u8 per = HWIRQ_PER(d->hwirq); + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); + u8 sid = hwirq_to_sid(d->hwirq); + u8 per = hwirq_to_per(d->hwirq); - if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid, + if (pmic_arb_read_cmd(pmic_arb->spmic, SPMI_CMD_EXT_READL, sid, (per << 8) + reg, buf, len)) - dev_err_ratelimited(&pa->spmic->dev, - "failed irqchip transaction on %x\n", + dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x\n", d->irq); } -static void cleanup_irq(struct spmi_pmic_arb *pa, u16 apid, int id) +static void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id) { - u16 ppid = pa->apid_data[apid].ppid; + u16 ppid = pmic_arb->apid_data[apid].ppid; u8 sid = ppid >> 8; u8 per = ppid & 0xFF; u8 irq_mask = BIT(id); - writel_relaxed(irq_mask, pa->intr + pa->ver_ops->irq_clear(apid)); + writel_relaxed(irq_mask, pmic_arb->ver_ops->irq_clear(pmic_arb, apid)); - if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, + if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid, (per << 8) + QPNPINT_REG_LATCHED_CLR, &irq_mask, 1)) - dev_err_ratelimited(&pa->spmic->dev, - "failed to ack irq_mask = 0x%x for ppid = %x\n", + dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n", irq_mask, ppid); - if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, + if (pmic_arb_write_cmd(pmic_arb->spmic, SPMI_CMD_EXT_WRITEL, sid, (per << 8) + QPNPINT_REG_EN_CLR, &irq_mask, 1)) - dev_err_ratelimited(&pa->spmic->dev, - "failed to ack irq_mask = 0x%x for ppid = %x\n", + dev_err_ratelimited(&pmic_arb->spmic->dev, "failed to ack irq_mask = 0x%x for ppid = %x\n", irq_mask, ppid); } -static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid) +static void periph_interrupt(struct spmi_pmic_arb *pmic_arb, u16 apid) { unsigned int irq; u32 status; int id; - u8 sid = (pa->apid_data[apid].ppid >> 8) & 0xF; - u8 per = pa->apid_data[apid].ppid & 0xFF; + u8 sid = (pmic_arb->apid_data[apid].ppid >> 8) & 0xF; + u8 per = pmic_arb->apid_data[apid].ppid & 0xFF; - status = readl_relaxed(pa->intr + pa->ver_ops->irq_status(apid)); + status = readl_relaxed(pmic_arb->ver_ops->irq_status(pmic_arb, apid)); while (status) { id = ffs(status) - 1; status &= ~BIT(id); - irq = irq_find_mapping(pa->domain, HWIRQ(sid, per, id, apid)); + irq = irq_find_mapping(pmic_arb->domain, + spec_to_hwirq(sid, per, id, apid)); if (irq == 0) { - cleanup_irq(pa, apid, id); + cleanup_irq(pmic_arb, apid, id); continue; } generic_handle_irq(irq); @@ -545,27 +534,28 @@ static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid) static void pmic_arb_chained_irq(struct irq_desc *desc) { - struct spmi_pmic_arb *pa = irq_desc_get_handler_data(desc); + struct spmi_pmic_arb *pmic_arb = irq_desc_get_handler_data(desc); + const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops; struct irq_chip *chip = irq_desc_get_chip(desc); - void __iomem *intr = pa->intr; - int first = pa->min_apid >> 5; - int last = pa->max_apid >> 5; + int first = pmic_arb->min_apid >> 5; + int last = pmic_arb->max_apid >> 5; + u8 ee = pmic_arb->ee; u32 status, enable; int i, id, apid; chained_irq_enter(chip, desc); for (i = first; i <= last; ++i) { - status = readl_relaxed(intr + - pa->ver_ops->owner_acc_status(pa->ee, i)); + status = readl_relaxed( + ver_ops->owner_acc_status(pmic_arb, ee, i)); while (status) { id = ffs(status) - 1; status &= ~BIT(id); apid = id + i * 32; - enable = readl_relaxed(intr + - pa->ver_ops->acc_enable(apid)); + enable = readl_relaxed( + ver_ops->acc_enable(pmic_arb, apid)); if (enable & SPMI_PIC_ACC_ENABLE_BIT) - periph_interrupt(pa, apid); + periph_interrupt(pmic_arb, apid); } } @@ -574,12 +564,12 @@ static void pmic_arb_chained_irq(struct irq_desc *desc) static void qpnpint_irq_ack(struct irq_data *d) { - struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 irq = HWIRQ_IRQ(d->hwirq); - u16 apid = HWIRQ_APID(d->hwirq); + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); + u8 irq = hwirq_to_irq(d->hwirq); + u16 apid = hwirq_to_apid(d->hwirq); u8 data; - writel_relaxed(BIT(irq), pa->intr + pa->ver_ops->irq_clear(apid)); + writel_relaxed(BIT(irq), pmic_arb->ver_ops->irq_clear(pmic_arb, apid)); data = BIT(irq); qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); @@ -587,7 +577,7 @@ static void qpnpint_irq_ack(struct irq_data *d) static void qpnpint_irq_mask(struct irq_data *d) { - u8 irq = HWIRQ_IRQ(d->hwirq); + u8 irq = hwirq_to_irq(d->hwirq); u8 data = BIT(irq); qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1); @@ -595,13 +585,14 @@ static void qpnpint_irq_mask(struct irq_data *d) static void qpnpint_irq_unmask(struct irq_data *d) { - struct spmi_pmic_arb *pa = irq_data_get_irq_chip_data(d); - u8 irq = HWIRQ_IRQ(d->hwirq); - u16 apid = HWIRQ_APID(d->hwirq); + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); + const struct pmic_arb_ver_ops *ver_ops = pmic_arb->ver_ops; + u8 irq = hwirq_to_irq(d->hwirq); + u16 apid = hwirq_to_apid(d->hwirq); u8 buf[2]; writel_relaxed(SPMI_PIC_ACC_ENABLE_BIT, - pa->intr + pa->ver_ops->acc_enable(apid)); + ver_ops->acc_enable(pmic_arb, apid)); qpnpint_spmi_read(d, QPNPINT_REG_EN_SET, &buf[0], 1); if (!(buf[0] & BIT(irq))) { @@ -619,44 +610,51 @@ static void qpnpint_irq_unmask(struct irq_data *d) static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) { struct spmi_pmic_arb_qpnpint_type type; - u8 irq = HWIRQ_IRQ(d->hwirq); - u8 bit_mask_irq = BIT(irq); + irq_flow_handler_t flow_handler; + u8 irq = hwirq_to_irq(d->hwirq); qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { - type.type |= bit_mask_irq; + type.type |= BIT(irq); if (flow_type & IRQF_TRIGGER_RISING) - type.polarity_high |= bit_mask_irq; + type.polarity_high |= BIT(irq); if (flow_type & IRQF_TRIGGER_FALLING) - type.polarity_low |= bit_mask_irq; + type.polarity_low |= BIT(irq); + + flow_handler = handle_edge_irq; } else { if ((flow_type & (IRQF_TRIGGER_HIGH)) && (flow_type & (IRQF_TRIGGER_LOW))) return -EINVAL; - type.type &= ~bit_mask_irq; /* level trig */ + type.type &= ~BIT(irq); /* level trig */ if (flow_type & IRQF_TRIGGER_HIGH) - type.polarity_high |= bit_mask_irq; + type.polarity_high |= BIT(irq); else - type.polarity_low |= bit_mask_irq; + type.polarity_low |= BIT(irq); + + flow_handler = handle_level_irq; } qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); - - if (flow_type & IRQ_TYPE_EDGE_BOTH) - irq_set_handler_locked(d, handle_edge_irq); - else - irq_set_handler_locked(d, handle_level_irq); + irq_set_handler_locked(d, flow_handler); return 0; } +static int qpnpint_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); + + return irq_set_irq_wake(pmic_arb->irq, on); +} + static int qpnpint_get_irqchip_state(struct irq_data *d, enum irqchip_irq_state which, bool *state) { - u8 irq = HWIRQ_IRQ(d->hwirq); + u8 irq = hwirq_to_irq(d->hwirq); u8 status = 0; if (which != IRQCHIP_STATE_LINE_LEVEL) @@ -668,15 +666,34 @@ static int qpnpint_get_irqchip_state(struct irq_data *d, return 0; } +static int qpnpint_irq_request_resources(struct irq_data *d) +{ + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); + u16 periph = hwirq_to_per(d->hwirq); + u16 apid = hwirq_to_apid(d->hwirq); + u16 sid = hwirq_to_sid(d->hwirq); + u16 irq = hwirq_to_irq(d->hwirq); + + if (pmic_arb->apid_data[apid].irq_ee != pmic_arb->ee) { + dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u: ee=%u but owner=%u\n", + sid, periph, irq, pmic_arb->ee, + pmic_arb->apid_data[apid].irq_ee); + return -ENODEV; + } + + return 0; +} + static struct irq_chip pmic_arb_irqchip = { .name = "pmic_arb", .irq_ack = qpnpint_irq_ack, .irq_mask = qpnpint_irq_mask, .irq_unmask = qpnpint_irq_unmask, .irq_set_type = qpnpint_irq_set_type, + .irq_set_wake = qpnpint_irq_set_wake, .irq_get_irqchip_state = qpnpint_get_irqchip_state, - .flags = IRQCHIP_MASK_ON_SUSPEND - | IRQCHIP_SKIP_SET_WAKE, + .irq_request_resources = qpnpint_irq_request_resources, + .flags = IRQCHIP_MASK_ON_SUSPEND, }; static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, @@ -686,12 +703,11 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, unsigned long *out_hwirq, unsigned int *out_type) { - struct spmi_pmic_arb *pa = d->host_data; + struct spmi_pmic_arb *pmic_arb = d->host_data; + u16 apid, ppid; int rc; - u16 apid; - dev_dbg(&pa->spmic->dev, - "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", + dev_dbg(&pmic_arb->spmic->dev, "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", intspec[0], intspec[1], intspec[2]); if (irq_domain_get_of_node(d) != controller) @@ -701,25 +717,25 @@ static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) return -EINVAL; - rc = pa->ver_ops->ppid_to_apid(pa, intspec[0], - (intspec[1] << 8), &apid); + ppid = intspec[0] << 8 | intspec[1]; + rc = pmic_arb->ver_ops->ppid_to_apid(pmic_arb, ppid); if (rc < 0) { - dev_err(&pa->spmic->dev, - "failed to xlate sid = 0x%x, periph = 0x%x, irq = %x rc = %d\n", + dev_err(&pmic_arb->spmic->dev, "failed to xlate sid = %#x, periph = %#x, irq = %u rc = %d\n", intspec[0], intspec[1], intspec[2], rc); return rc; } + apid = rc; /* Keep track of {max,min}_apid for bounding search during interrupt */ - if (apid > pa->max_apid) - pa->max_apid = apid; - if (apid < pa->min_apid) - pa->min_apid = apid; + if (apid > pmic_arb->max_apid) + pmic_arb->max_apid = apid; + if (apid < pmic_arb->min_apid) + pmic_arb->min_apid = apid; - *out_hwirq = HWIRQ(intspec[0], intspec[1], intspec[2], apid); + *out_hwirq = spec_to_hwirq(intspec[0], intspec[1], intspec[2], apid); *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; - dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); + dev_dbg(&pmic_arb->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); return 0; } @@ -728,9 +744,9 @@ static int qpnpint_irq_domain_map(struct irq_domain *d, unsigned int virq, irq_hw_number_t hwirq) { - struct spmi_pmic_arb *pa = d->host_data; + struct spmi_pmic_arb *pmic_arb = d->host_data; - dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); + dev_dbg(&pmic_arb->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); irq_set_chip_data(virq, d->host_data); @@ -738,24 +754,23 @@ static int qpnpint_irq_domain_map(struct irq_domain *d, return 0; } -static int -pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) +static int pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pmic_arb, u16 ppid) { - u16 ppid = sid << 8 | ((addr >> 8) & 0xFF); - u32 *mapping_table = pa->mapping_table; + u32 *mapping_table = pmic_arb->mapping_table; int index = 0, i; u16 apid_valid; + u16 apid; u32 data; - apid_valid = pa->ppid_to_apid[ppid]; - if (apid_valid & PMIC_ARB_CHAN_VALID) { - *apid = (apid_valid & ~PMIC_ARB_CHAN_VALID); - return 0; + apid_valid = pmic_arb->ppid_to_apid[ppid]; + if (apid_valid & PMIC_ARB_APID_VALID) { + apid = apid_valid & ~PMIC_ARB_APID_VALID; + return apid; } for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) { - if (!test_and_set_bit(index, pa->mapping_table_valid)) - mapping_table[index] = readl_relaxed(pa->cnfg + + if (!test_and_set_bit(index, pmic_arb->mapping_table_valid)) + mapping_table[index] = readl_relaxed(pmic_arb->cnfg + SPMI_MAPPING_TABLE_REG(index)); data = mapping_table[index]; @@ -764,21 +779,21 @@ pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) { index = SPMI_MAPPING_BIT_IS_1_RESULT(data); } else { - *apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); - pa->ppid_to_apid[ppid] - = *apid | PMIC_ARB_CHAN_VALID; - pa->apid_data[*apid].ppid = ppid; - return 0; + apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); + pmic_arb->ppid_to_apid[ppid] + = apid | PMIC_ARB_APID_VALID; + pmic_arb->apid_data[apid].ppid = ppid; + return apid; } } else { if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) { index = SPMI_MAPPING_BIT_IS_0_RESULT(data); } else { - *apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); - pa->ppid_to_apid[ppid] - = *apid | PMIC_ARB_CHAN_VALID; - pa->apid_data[*apid].ppid = ppid; - return 0; + apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); + pmic_arb->ppid_to_apid[ppid] + = apid | PMIC_ARB_APID_VALID; + pmic_arb->apid_data[apid].ppid = ppid; + return apid; } } } @@ -786,105 +801,178 @@ pmic_arb_ppid_to_apid_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) return -ENODEV; } -static int -pmic_arb_mode_v1_v3(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode) -{ - *mode = S_IRUSR | S_IWUSR; - return 0; -} - /* v1 offset per ee */ -static int -pmic_arb_offset_v1(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u32 *offset) +static int pmic_arb_offset_v1(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, + enum pmic_arb_channel ch_type) { - *offset = 0x800 + 0x80 * pa->channel; - return 0; + return 0x800 + 0x80 * pmic_arb->channel; } -static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pa, u16 ppid) +static u16 pmic_arb_find_apid(struct spmi_pmic_arb *pmic_arb, u16 ppid) { + struct apid_data *apidd = &pmic_arb->apid_data[pmic_arb->last_apid]; u32 regval, offset; - u16 apid; - u16 id; - - /* - * PMIC_ARB_REG_CHNL is a table in HW mapping channel to ppid. - * ppid_to_apid is an in-memory invert of that table. - */ - for (apid = pa->last_apid; apid < pa->max_periph; apid++) { - regval = readl_relaxed(pa->cnfg + - SPMI_OWNERSHIP_TABLE_REG(apid)); - pa->apid_data[apid].owner = SPMI_OWNERSHIP_PERIPH2OWNER(regval); + u16 id, apid; - offset = PMIC_ARB_REG_CHNL(apid); - if (offset >= pa->core_size) + for (apid = pmic_arb->last_apid; ; apid++, apidd++) { + offset = pmic_arb->ver_ops->apid_map_offset(apid); + if (offset >= pmic_arb->core_size) break; - regval = readl_relaxed(pa->core + offset); + regval = readl_relaxed(pmic_arb->cnfg + + SPMI_OWNERSHIP_TABLE_REG(apid)); + apidd->irq_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval); + apidd->write_ee = apidd->irq_ee; + + regval = readl_relaxed(pmic_arb->core + offset); if (!regval) continue; id = (regval >> 8) & PMIC_ARB_PPID_MASK; - pa->ppid_to_apid[id] = apid | PMIC_ARB_CHAN_VALID; - pa->apid_data[apid].ppid = id; + pmic_arb->ppid_to_apid[id] = apid | PMIC_ARB_APID_VALID; + apidd->ppid = id; if (id == ppid) { - apid |= PMIC_ARB_CHAN_VALID; + apid |= PMIC_ARB_APID_VALID; break; } } - pa->last_apid = apid & ~PMIC_ARB_CHAN_VALID; + pmic_arb->last_apid = apid & ~PMIC_ARB_APID_VALID; return apid; } - -static int -pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u16 *apid) +static int pmic_arb_ppid_to_apid_v2(struct spmi_pmic_arb *pmic_arb, u16 ppid) { - u16 ppid = (sid << 8) | (addr >> 8); u16 apid_valid; - apid_valid = pa->ppid_to_apid[ppid]; - if (!(apid_valid & PMIC_ARB_CHAN_VALID)) - apid_valid = pmic_arb_find_apid(pa, ppid); - if (!(apid_valid & PMIC_ARB_CHAN_VALID)) + apid_valid = pmic_arb->ppid_to_apid[ppid]; + if (!(apid_valid & PMIC_ARB_APID_VALID)) + apid_valid = pmic_arb_find_apid(pmic_arb, ppid); + if (!(apid_valid & PMIC_ARB_APID_VALID)) return -ENODEV; - *apid = (apid_valid & ~PMIC_ARB_CHAN_VALID); + return apid_valid & ~PMIC_ARB_APID_VALID; +} + +static int pmic_arb_read_apid_map_v5(struct spmi_pmic_arb *pmic_arb) +{ + struct apid_data *apidd = pmic_arb->apid_data; + struct apid_data *prev_apidd; + u16 i, apid, ppid; + bool valid, is_irq_ee; + u32 regval, offset; + + /* + * In order to allow multiple EEs to write to a single PPID in arbiter + * version 5, there is more than one APID mapped to each PPID. + * The owner field for each of these mappings specifies the EE which is + * allowed to write to the APID. The owner of the last (highest) APID + * for a given PPID will receive interrupts from the PPID. + */ + for (i = 0; ; i++, apidd++) { + offset = pmic_arb->ver_ops->apid_map_offset(i); + if (offset >= pmic_arb->core_size) + break; + + regval = readl_relaxed(pmic_arb->core + offset); + if (!regval) + continue; + ppid = (regval >> 8) & PMIC_ARB_PPID_MASK; + is_irq_ee = PMIC_ARB_CHAN_IS_IRQ_OWNER(regval); + + regval = readl_relaxed(pmic_arb->cnfg + + SPMI_OWNERSHIP_TABLE_REG(i)); + apidd->write_ee = SPMI_OWNERSHIP_PERIPH2OWNER(regval); + + apidd->irq_ee = is_irq_ee ? apidd->write_ee : INVALID_EE; + + valid = pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID; + apid = pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID; + prev_apidd = &pmic_arb->apid_data[apid]; + + if (valid && is_irq_ee && + prev_apidd->write_ee == pmic_arb->ee) { + /* + * Duplicate PPID mapping after the one for this EE; + * override the irq owner + */ + prev_apidd->irq_ee = apidd->irq_ee; + } else if (!valid || is_irq_ee) { + /* First PPID mapping or duplicate for another EE */ + pmic_arb->ppid_to_apid[ppid] = i | PMIC_ARB_APID_VALID; + } + + apidd->ppid = ppid; + pmic_arb->last_apid = i; + } + + /* Dump the mapping table for debug purposes. */ + dev_dbg(&pmic_arb->spmic->dev, "PPID APID Write-EE IRQ-EE\n"); + for (ppid = 0; ppid < PMIC_ARB_MAX_PPID; ppid++) { + apid = pmic_arb->ppid_to_apid[ppid]; + if (apid & PMIC_ARB_APID_VALID) { + apid &= ~PMIC_ARB_APID_VALID; + apidd = &pmic_arb->apid_data[apid]; + dev_dbg(&pmic_arb->spmic->dev, "%#03X %3u %2u %2u\n", + ppid, apid, apidd->write_ee, apidd->irq_ee); + } + } + return 0; } -static int -pmic_arb_mode_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, mode_t *mode) +static int pmic_arb_ppid_to_apid_v5(struct spmi_pmic_arb *pmic_arb, u16 ppid) +{ + if (!(pmic_arb->ppid_to_apid[ppid] & PMIC_ARB_APID_VALID)) + return -ENODEV; + + return pmic_arb->ppid_to_apid[ppid] & ~PMIC_ARB_APID_VALID; +} + +/* v2 offset per ppid and per ee */ +static int pmic_arb_offset_v2(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, + enum pmic_arb_channel ch_type) { u16 apid; + u16 ppid; int rc; - rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid); + ppid = sid << 8 | ((addr >> 8) & 0xFF); + rc = pmic_arb_ppid_to_apid_v2(pmic_arb, ppid); if (rc < 0) return rc; - *mode = 0; - *mode |= S_IRUSR; - - if (pa->ee == pa->apid_data[apid].owner) - *mode |= S_IWUSR; - return 0; + apid = rc; + return 0x1000 * pmic_arb->ee + 0x8000 * apid; } -/* v2 offset per ppid and per ee */ -static int -pmic_arb_offset_v2(struct spmi_pmic_arb *pa, u8 sid, u16 addr, u32 *offset) +/* + * v5 offset per ee and per apid for observer channels and per apid for + * read/write channels. + */ +static int pmic_arb_offset_v5(struct spmi_pmic_arb *pmic_arb, u8 sid, u16 addr, + enum pmic_arb_channel ch_type) { u16 apid; int rc; + u32 offset = 0; + u16 ppid = (sid << 8) | (addr >> 8); - rc = pmic_arb_ppid_to_apid_v2(pa, sid, addr, &apid); + rc = pmic_arb_ppid_to_apid_v5(pmic_arb, ppid); if (rc < 0) return rc; - *offset = 0x1000 * pa->ee + 0x8000 * apid; - return 0; + apid = rc; + switch (ch_type) { + case PMIC_ARB_CHANNEL_OBS: + offset = 0x10000 * pmic_arb->ee + 0x80 * apid; + break; + case PMIC_ARB_CHANNEL_RW: + offset = 0x10000 * apid; + break; + } + + return offset; } static u32 pmic_arb_fmt_cmd_v1(u8 opc, u8 sid, u16 addr, u8 bc) @@ -897,55 +985,97 @@ static u32 pmic_arb_fmt_cmd_v2(u8 opc, u8 sid, u16 addr, u8 bc) return (opc << 27) | ((addr & 0xff) << 4) | (bc & 0x7); } -static u32 pmic_arb_owner_acc_status_v1(u8 m, u16 n) +static void __iomem * +pmic_arb_owner_acc_status_v1(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) { - return 0x20 * m + 0x4 * n; + return pmic_arb->intr + 0x20 * m + 0x4 * n; } -static u32 pmic_arb_owner_acc_status_v2(u8 m, u16 n) +static void __iomem * +pmic_arb_owner_acc_status_v2(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) { - return 0x100000 + 0x1000 * m + 0x4 * n; + return pmic_arb->intr + 0x100000 + 0x1000 * m + 0x4 * n; } -static u32 pmic_arb_owner_acc_status_v3(u8 m, u16 n) +static void __iomem * +pmic_arb_owner_acc_status_v3(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) { - return 0x200000 + 0x1000 * m + 0x4 * n; + return pmic_arb->intr + 0x200000 + 0x1000 * m + 0x4 * n; } -static u32 pmic_arb_acc_enable_v1(u16 n) +static void __iomem * +pmic_arb_owner_acc_status_v5(struct spmi_pmic_arb *pmic_arb, u8 m, u16 n) { - return 0x200 + 0x4 * n; + return pmic_arb->intr + 0x10000 * m + 0x4 * n; } -static u32 pmic_arb_acc_enable_v2(u16 n) +static void __iomem * +pmic_arb_acc_enable_v1(struct spmi_pmic_arb *pmic_arb, u16 n) { - return 0x1000 * n; + return pmic_arb->intr + 0x200 + 0x4 * n; } -static u32 pmic_arb_irq_status_v1(u16 n) +static void __iomem * +pmic_arb_acc_enable_v2(struct spmi_pmic_arb *pmic_arb, u16 n) { - return 0x600 + 0x4 * n; + return pmic_arb->intr + 0x1000 * n; } -static u32 pmic_arb_irq_status_v2(u16 n) +static void __iomem * +pmic_arb_acc_enable_v5(struct spmi_pmic_arb *pmic_arb, u16 n) { - return 0x4 + 0x1000 * n; + return pmic_arb->wr_base + 0x100 + 0x10000 * n; } -static u32 pmic_arb_irq_clear_v1(u16 n) +static void __iomem * +pmic_arb_irq_status_v1(struct spmi_pmic_arb *pmic_arb, u16 n) { - return 0xA00 + 0x4 * n; + return pmic_arb->intr + 0x600 + 0x4 * n; } -static u32 pmic_arb_irq_clear_v2(u16 n) +static void __iomem * +pmic_arb_irq_status_v2(struct spmi_pmic_arb *pmic_arb, u16 n) { - return 0x8 + 0x1000 * n; + return pmic_arb->intr + 0x4 + 0x1000 * n; +} + +static void __iomem * +pmic_arb_irq_status_v5(struct spmi_pmic_arb *pmic_arb, u16 n) +{ + return pmic_arb->wr_base + 0x104 + 0x10000 * n; +} + +static void __iomem * +pmic_arb_irq_clear_v1(struct spmi_pmic_arb *pmic_arb, u16 n) +{ + return pmic_arb->intr + 0xA00 + 0x4 * n; +} + +static void __iomem * +pmic_arb_irq_clear_v2(struct spmi_pmic_arb *pmic_arb, u16 n) +{ + return pmic_arb->intr + 0x8 + 0x1000 * n; +} + +static void __iomem * +pmic_arb_irq_clear_v5(struct spmi_pmic_arb *pmic_arb, u16 n) +{ + return pmic_arb->wr_base + 0x108 + 0x10000 * n; +} + +static u32 pmic_arb_apid_map_offset_v2(u16 n) +{ + return 0x800 + 0x4 * n; +} + +static u32 pmic_arb_apid_map_offset_v5(u16 n) +{ + return 0x900 + 0x4 * n; } static const struct pmic_arb_ver_ops pmic_arb_v1 = { .ver_str = "v1", .ppid_to_apid = pmic_arb_ppid_to_apid_v1, - .mode = pmic_arb_mode_v1_v3, .non_data_cmd = pmic_arb_non_data_cmd_v1, .offset = pmic_arb_offset_v1, .fmt_cmd = pmic_arb_fmt_cmd_v1, @@ -953,12 +1083,12 @@ static const struct pmic_arb_ver_ops pmic_arb_v1 = { .acc_enable = pmic_arb_acc_enable_v1, .irq_status = pmic_arb_irq_status_v1, .irq_clear = pmic_arb_irq_clear_v1, + .apid_map_offset = pmic_arb_apid_map_offset_v2, }; static const struct pmic_arb_ver_ops pmic_arb_v2 = { .ver_str = "v2", .ppid_to_apid = pmic_arb_ppid_to_apid_v2, - .mode = pmic_arb_mode_v2, .non_data_cmd = pmic_arb_non_data_cmd_v2, .offset = pmic_arb_offset_v2, .fmt_cmd = pmic_arb_fmt_cmd_v2, @@ -966,12 +1096,12 @@ static const struct pmic_arb_ver_ops pmic_arb_v2 = { .acc_enable = pmic_arb_acc_enable_v2, .irq_status = pmic_arb_irq_status_v2, .irq_clear = pmic_arb_irq_clear_v2, + .apid_map_offset = pmic_arb_apid_map_offset_v2, }; static const struct pmic_arb_ver_ops pmic_arb_v3 = { .ver_str = "v3", .ppid_to_apid = pmic_arb_ppid_to_apid_v2, - .mode = pmic_arb_mode_v1_v3, .non_data_cmd = pmic_arb_non_data_cmd_v2, .offset = pmic_arb_offset_v2, .fmt_cmd = pmic_arb_fmt_cmd_v2, @@ -979,6 +1109,20 @@ static const struct pmic_arb_ver_ops pmic_arb_v3 = { .acc_enable = pmic_arb_acc_enable_v2, .irq_status = pmic_arb_irq_status_v2, .irq_clear = pmic_arb_irq_clear_v2, + .apid_map_offset = pmic_arb_apid_map_offset_v2, +}; + +static const struct pmic_arb_ver_ops pmic_arb_v5 = { + .ver_str = "v5", + .ppid_to_apid = pmic_arb_ppid_to_apid_v5, + .non_data_cmd = pmic_arb_non_data_cmd_v2, + .offset = pmic_arb_offset_v5, + .fmt_cmd = pmic_arb_fmt_cmd_v2, + .owner_acc_status = pmic_arb_owner_acc_status_v5, + .acc_enable = pmic_arb_acc_enable_v5, + .irq_status = pmic_arb_irq_status_v5, + .irq_clear = pmic_arb_irq_clear_v5, + .apid_map_offset = pmic_arb_apid_map_offset_v5, }; static const struct irq_domain_ops pmic_arb_irq_domain_ops = { @@ -988,97 +1132,91 @@ static const struct irq_domain_ops pmic_arb_irq_domain_ops = { static int spmi_pmic_arb_probe(struct platform_device *pdev) { - struct spmi_pmic_arb *pa; + struct spmi_pmic_arb *pmic_arb; struct spmi_controller *ctrl; struct resource *res; void __iomem *core; + u32 *mapping_table; u32 channel, ee, hw_ver; int err; - ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); + ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pmic_arb)); if (!ctrl) return -ENOMEM; - pa = spmi_controller_get_drvdata(ctrl); - pa->spmic = ctrl; + pmic_arb = spmi_controller_get_drvdata(ctrl); + pmic_arb->spmic = ctrl; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); - pa->core_size = resource_size(res); - if (pa->core_size <= 0x800) { - dev_err(&pdev->dev, "core_size is smaller than 0x800. Failing Probe\n"); - err = -EINVAL; - goto err_put_ctrl; - } - core = devm_ioremap_resource(&ctrl->dev, res); if (IS_ERR(core)) { err = PTR_ERR(core); goto err_put_ctrl; } + pmic_arb->core_size = resource_size(res); + + pmic_arb->ppid_to_apid = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PPID, + sizeof(*pmic_arb->ppid_to_apid), + GFP_KERNEL); + if (!pmic_arb->ppid_to_apid) { + err = -ENOMEM; + goto err_put_ctrl; + } + hw_ver = readl_relaxed(core + PMIC_ARB_VERSION); if (hw_ver < PMIC_ARB_VERSION_V2_MIN) { - pa->ver_ops = &pmic_arb_v1; - pa->wr_base = core; - pa->rd_base = core; + pmic_arb->ver_ops = &pmic_arb_v1; + pmic_arb->wr_base = core; + pmic_arb->rd_base = core; } else { - pa->core = core; + pmic_arb->core = core; if (hw_ver < PMIC_ARB_VERSION_V3_MIN) - pa->ver_ops = &pmic_arb_v2; + pmic_arb->ver_ops = &pmic_arb_v2; + else if (hw_ver < PMIC_ARB_VERSION_V5_MIN) + pmic_arb->ver_ops = &pmic_arb_v3; else - pa->ver_ops = &pmic_arb_v3; - - /* the apid to ppid table starts at PMIC_ARB_REG_CHNL(0) */ - pa->max_periph = (pa->core_size - PMIC_ARB_REG_CHNL(0)) / 4; + pmic_arb->ver_ops = &pmic_arb_v5; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "obsrvr"); - pa->rd_base = devm_ioremap_resource(&ctrl->dev, res); - if (IS_ERR(pa->rd_base)) { - err = PTR_ERR(pa->rd_base); + pmic_arb->rd_base = devm_ioremap_resource(&ctrl->dev, res); + if (IS_ERR(pmic_arb->rd_base)) { + err = PTR_ERR(pmic_arb->rd_base); goto err_put_ctrl; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "chnls"); - pa->wr_base = devm_ioremap_resource(&ctrl->dev, res); - if (IS_ERR(pa->wr_base)) { - err = PTR_ERR(pa->wr_base); - goto err_put_ctrl; - } - - pa->ppid_to_apid = devm_kcalloc(&ctrl->dev, - PMIC_ARB_MAX_PPID, - sizeof(*pa->ppid_to_apid), - GFP_KERNEL); - if (!pa->ppid_to_apid) { - err = -ENOMEM; + pmic_arb->wr_base = devm_ioremap_resource(&ctrl->dev, res); + if (IS_ERR(pmic_arb->wr_base)) { + err = PTR_ERR(pmic_arb->wr_base); goto err_put_ctrl; } } dev_info(&ctrl->dev, "PMIC arbiter version %s (0x%x)\n", - pa->ver_ops->ver_str, hw_ver); + pmic_arb->ver_ops->ver_str, hw_ver); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "intr"); - pa->intr = devm_ioremap_resource(&ctrl->dev, res); - if (IS_ERR(pa->intr)) { - err = PTR_ERR(pa->intr); + pmic_arb->intr = devm_ioremap_resource(&ctrl->dev, res); + if (IS_ERR(pmic_arb->intr)) { + err = PTR_ERR(pmic_arb->intr); goto err_put_ctrl; } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cnfg"); - pa->cnfg = devm_ioremap_resource(&ctrl->dev, res); - if (IS_ERR(pa->cnfg)) { - err = PTR_ERR(pa->cnfg); + pmic_arb->cnfg = devm_ioremap_resource(&ctrl->dev, res); + if (IS_ERR(pmic_arb->cnfg)) { + err = PTR_ERR(pmic_arb->cnfg); goto err_put_ctrl; } - pa->irq = platform_get_irq_byname(pdev, "periph_irq"); - if (pa->irq < 0) { - err = pa->irq; + pmic_arb->irq = platform_get_irq_byname(pdev, "periph_irq"); + if (pmic_arb->irq < 0) { + err = pmic_arb->irq; goto err_put_ctrl; } @@ -1095,7 +1233,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) goto err_put_ctrl; } - pa->channel = channel; + pmic_arb->channel = channel; err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee); if (err) { @@ -1109,39 +1247,47 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) goto err_put_ctrl; } - pa->ee = ee; - - pa->mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS - 1, - sizeof(*pa->mapping_table), GFP_KERNEL); - if (!pa->mapping_table) { + pmic_arb->ee = ee; + mapping_table = devm_kcalloc(&ctrl->dev, PMIC_ARB_MAX_PERIPHS, + sizeof(*mapping_table), GFP_KERNEL); + if (!mapping_table) { err = -ENOMEM; goto err_put_ctrl; } + pmic_arb->mapping_table = mapping_table; /* Initialize max_apid/min_apid to the opposite bounds, during * the irq domain translation, we are sure to update these */ - pa->max_apid = 0; - pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1; + pmic_arb->max_apid = 0; + pmic_arb->min_apid = PMIC_ARB_MAX_PERIPHS - 1; platform_set_drvdata(pdev, ctrl); - raw_spin_lock_init(&pa->lock); + raw_spin_lock_init(&pmic_arb->lock); ctrl->cmd = pmic_arb_cmd; ctrl->read_cmd = pmic_arb_read_cmd; ctrl->write_cmd = pmic_arb_write_cmd; + if (hw_ver >= PMIC_ARB_VERSION_V5_MIN) { + err = pmic_arb_read_apid_map_v5(pmic_arb); + if (err) { + dev_err(&pdev->dev, "could not read APID->PPID mapping table, rc= %d\n", + err); + goto err_put_ctrl; + } + } + dev_dbg(&pdev->dev, "adding irq domain\n"); - pa->domain = irq_domain_add_tree(pdev->dev.of_node, - &pmic_arb_irq_domain_ops, pa); - if (!pa->domain) { + pmic_arb->domain = irq_domain_add_tree(pdev->dev.of_node, + &pmic_arb_irq_domain_ops, pmic_arb); + if (!pmic_arb->domain) { dev_err(&pdev->dev, "unable to create irq_domain\n"); err = -ENOMEM; goto err_put_ctrl; } - irq_set_chained_handler_and_data(pa->irq, pmic_arb_chained_irq, pa); - enable_irq_wake(pa->irq); - + irq_set_chained_handler_and_data(pmic_arb->irq, pmic_arb_chained_irq, + pmic_arb); err = spmi_controller_add(ctrl); if (err) goto err_domain_remove; @@ -1149,8 +1295,8 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) return 0; err_domain_remove: - irq_set_chained_handler_and_data(pa->irq, NULL, NULL); - irq_domain_remove(pa->domain); + irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); + irq_domain_remove(pmic_arb->domain); err_put_ctrl: spmi_controller_put(ctrl); return err; @@ -1159,10 +1305,10 @@ err_put_ctrl: static int spmi_pmic_arb_remove(struct platform_device *pdev) { struct spmi_controller *ctrl = platform_get_drvdata(pdev); - struct spmi_pmic_arb *pa = spmi_controller_get_drvdata(ctrl); + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); spmi_controller_remove(ctrl); - irq_set_chained_handler_and_data(pa->irq, NULL, NULL); - irq_domain_remove(pa->domain); + irq_set_chained_handler_and_data(pmic_arb->irq, NULL, NULL); + irq_domain_remove(pmic_arb->domain); spmi_controller_put(ctrl); return 0; } diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index 2b9b0941d9eb..aa3edabc2b0f 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -365,11 +365,23 @@ static int spmi_drv_remove(struct device *dev) return 0; } +static int spmi_drv_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + int ret; + + ret = of_device_uevent_modalias(dev, env); + if (ret != -ENODEV) + return ret; + + return 0; +} + static struct bus_type spmi_bus_type = { .name = "spmi", .match = spmi_device_match, .probe = spmi_drv_probe, .remove = spmi_drv_remove, + .uevent = spmi_drv_uevent, }; /** @@ -454,27 +466,25 @@ static void of_spmi_register_devices(struct spmi_controller *ctrl) struct spmi_device *sdev; u32 reg[2]; - dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name); + dev_dbg(&ctrl->dev, "adding child %pOF\n", node); err = of_property_read_u32_array(node, "reg", reg, 2); if (err) { dev_err(&ctrl->dev, - "node %s err (%d) does not have 'reg' property\n", - node->full_name, err); + "node %pOF err (%d) does not have 'reg' property\n", + node, err); continue; } if (reg[1] != SPMI_USID) { dev_err(&ctrl->dev, - "node %s contains unsupported 'reg' entry\n", - node->full_name); + "node %pOF contains unsupported 'reg' entry\n", + node); continue; } if (reg[0] >= SPMI_MAX_SLAVE_ID) { - dev_err(&ctrl->dev, - "invalid usid on node %s\n", - node->full_name); + dev_err(&ctrl->dev, "invalid usid on node %pOF\n", node); continue; } diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 268d4e6ef48a..ef28a1cb64ae 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -110,4 +110,6 @@ source "drivers/staging/ccree/Kconfig" source "drivers/staging/typec/Kconfig" +source "drivers/staging/vboxvideo/Kconfig" + endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index b93e6f5f0f6e..2918580bdb9e 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -44,3 +44,4 @@ obj-$(CONFIG_KS7010) += ks7010/ obj-$(CONFIG_GREYBUS) += greybus/ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/ obj-$(CONFIG_CRYPTO_DEV_CCREE) += ccree/ +obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo/ diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index ca11be21f64b..34ca7823255d 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -2396,6 +2396,7 @@ static ssize_t comedi_write(struct file *file, const char __user *buf, continue; } + set_current_state(TASK_RUNNING); wp = async->buf_write_ptr; n1 = min(n, async->prealloc_bufsz - wp); n2 = n - n1; @@ -2528,6 +2529,8 @@ static ssize_t comedi_read(struct file *file, char __user *buf, size_t nbytes, } continue; } + + set_current_state(TASK_RUNNING); rp = async->buf_read_ptr; n1 = min(n, async->prealloc_bufsz - rp); n2 = n - n1; diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c index b2e382888981..2f7bfc1c59e5 100644 --- a/drivers/staging/comedi/drivers/ni_mio_common.c +++ b/drivers/staging/comedi/drivers/ni_mio_common.c @@ -3116,8 +3116,7 @@ static void ni_ao_cmd_set_update(struct comedi_device *dev, /* following line: 2-1 per STC */ ni_stc_writel(dev, 1, NISTC_AO_UI_LOADA_REG); ni_stc_writew(dev, NISTC_AO_CMD1_UI_LOAD, NISTC_AO_CMD1_REG); - /* following line: N-1 per STC */ - ni_stc_writel(dev, trigvar - 1, NISTC_AO_UI_LOADA_REG); + ni_stc_writel(dev, trigvar, NISTC_AO_UI_LOADA_REG); } else { /* TRIG_EXT */ /* FIXME: assert scan_begin_arg != 0, ret failure otherwise */ devpriv->ao_cmd2 |= NISTC_AO_CMD2_BC_GATE_ENA; diff --git a/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c b/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c index b37a6f48225f..8ea3920400a0 100644 --- a/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c +++ b/drivers/staging/fsl-mc/bus/fsl-mc-allocator.c @@ -16,9 +16,9 @@ static bool __must_check fsl_mc_is_allocatable(const char *obj_type) { - return strcmp(obj_type, "dpbp") || - strcmp(obj_type, "dpmcp") || - strcmp(obj_type, "dpcon"); + return strcmp(obj_type, "dpbp") == 0 || + strcmp(obj_type, "dpmcp") == 0 || + strcmp(obj_type, "dpcon") == 0; } /** diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c index a6a8393d6664..3e00df74b18c 100644 --- a/drivers/staging/iio/resolver/ad2s1210.c +++ b/drivers/staging/iio/resolver/ad2s1210.c @@ -472,7 +472,7 @@ static int ad2s1210_read_raw(struct iio_dev *indio_dev, long m) { struct ad2s1210_state *st = iio_priv(indio_dev); - bool negative; + u16 negative; int ret = 0; u16 pos; s16 vel; diff --git a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c index 85b242ec5f9b..8fc191d99927 100644 --- a/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c +++ b/drivers/staging/lustre/lnet/klnds/o2iblnd/o2iblnd_cb.c @@ -1640,8 +1640,13 @@ kiblnd_send(struct lnet_ni *ni, void *private, struct lnet_msg *lntmsg) ibmsg = tx->tx_msg; ibmsg->ibm_u.immediate.ibim_hdr = *hdr; - copy_from_iter(&ibmsg->ibm_u.immediate.ibim_payload, IBLND_MSG_SIZE, - &from); + rc = copy_from_iter(&ibmsg->ibm_u.immediate.ibim_payload, payload_nob, + &from); + if (rc != payload_nob) { + kiblnd_pool_free_node(&tx->tx_pool->tpo_pool, &tx->tx_list); + return -EFAULT; + } + nob = offsetof(struct kib_immediate_msg, ibim_payload[payload_nob]); kiblnd_init_tx_msg(ni, tx, IBLND_MSG_IMMEDIATE, nob); @@ -1741,8 +1746,14 @@ kiblnd_recv(struct lnet_ni *ni, void *private, struct lnet_msg *lntmsg, break; } - copy_to_iter(&rxmsg->ibm_u.immediate.ibim_payload, - IBLND_MSG_SIZE, to); + rc = copy_to_iter(&rxmsg->ibm_u.immediate.ibim_payload, rlen, + to); + if (rc != rlen) { + rc = -EFAULT; + break; + } + + rc = 0; lnet_finalize(ni, lntmsg, 0); break; diff --git a/drivers/staging/media/atomisp/i2c/ap1302.h b/drivers/staging/media/atomisp/i2c/ap1302.h index 9341232c580d..4d0b181a9671 100644 --- a/drivers/staging/media/atomisp/i2c/ap1302.h +++ b/drivers/staging/media/atomisp/i2c/ap1302.h @@ -158,8 +158,8 @@ struct ap1302_res_struct { }; struct ap1302_context_res { - s32 res_num; - s32 cur_res; + u32 res_num; + u32 cur_res; struct ap1302_res_struct *res_table; }; diff --git a/drivers/staging/media/atomisp/i2c/gc0310.h b/drivers/staging/media/atomisp/i2c/gc0310.h index f31eb277f542..7d8a0aeecb6c 100644 --- a/drivers/staging/media/atomisp/i2c/gc0310.h +++ b/drivers/staging/media/atomisp/i2c/gc0310.h @@ -454,6 +454,6 @@ struct gc0310_resolution gc0310_res_video[] = { #define N_RES_VIDEO (ARRAY_SIZE(gc0310_res_video)) static struct gc0310_resolution *gc0310_res = gc0310_res_preview; -static int N_RES = N_RES_PREVIEW; +static unsigned long N_RES = N_RES_PREVIEW; #endif diff --git a/drivers/staging/media/atomisp/i2c/gc2235.h b/drivers/staging/media/atomisp/i2c/gc2235.h index ccbc757045a5..7c3d994180cc 100644 --- a/drivers/staging/media/atomisp/i2c/gc2235.h +++ b/drivers/staging/media/atomisp/i2c/gc2235.h @@ -668,5 +668,5 @@ struct gc2235_resolution gc2235_res_video[] = { #define N_RES_VIDEO (ARRAY_SIZE(gc2235_res_video)) static struct gc2235_resolution *gc2235_res = gc2235_res_preview; -static int N_RES = N_RES_PREVIEW; +static unsigned long N_RES = N_RES_PREVIEW; #endif diff --git a/drivers/staging/media/atomisp/i2c/imx/imx.h b/drivers/staging/media/atomisp/i2c/imx/imx.h index 36b3f3a5a41f..41b4133ca995 100644 --- a/drivers/staging/media/atomisp/i2c/imx/imx.h +++ b/drivers/staging/media/atomisp/i2c/imx/imx.h @@ -480,7 +480,7 @@ struct imx_device { struct imx_vcm *vcm_driver; struct imx_otp *otp_driver; const struct imx_resolution *curr_res_table; - int entries_curr_table; + unsigned long entries_curr_table; const struct firmware *fw; struct imx_reg_addr *reg_addr; const struct imx_reg *param_hold; diff --git a/drivers/staging/media/atomisp/i2c/ov2680.h b/drivers/staging/media/atomisp/i2c/ov2680.h index 944fe8e3bcbf..ab8907e6c9ef 100644 --- a/drivers/staging/media/atomisp/i2c/ov2680.h +++ b/drivers/staging/media/atomisp/i2c/ov2680.h @@ -934,7 +934,6 @@ static struct ov2680_resolution ov2680_res_video[] = { #define N_RES_VIDEO (ARRAY_SIZE(ov2680_res_video)) static struct ov2680_resolution *ov2680_res = ov2680_res_preview; -static int N_RES = N_RES_PREVIEW; - +static unsigned long N_RES = N_RES_PREVIEW; #endif diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h index b0d40965d89e..73ecb1679718 100644 --- a/drivers/staging/media/atomisp/i2c/ov2722.h +++ b/drivers/staging/media/atomisp/i2c/ov2722.h @@ -1263,5 +1263,5 @@ struct ov2722_resolution ov2722_res_video[] = { #define N_RES_VIDEO (ARRAY_SIZE(ov2722_res_video)) static struct ov2722_resolution *ov2722_res = ov2722_res_preview; -static int N_RES = N_RES_PREVIEW; +static unsigned long N_RES = N_RES_PREVIEW; #endif diff --git a/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h b/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h index d88ac1777d86..8c2e6794463b 100644 --- a/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h +++ b/drivers/staging/media/atomisp/i2c/ov5693/ov5693.h @@ -1377,5 +1377,5 @@ struct ov5693_resolution ov5693_res_video[] = { #define N_RES_VIDEO (ARRAY_SIZE(ov5693_res_video)) static struct ov5693_resolution *ov5693_res = ov5693_res_preview; -static int N_RES = N_RES_PREVIEW; +static unsigned long N_RES = N_RES_PREVIEW; #endif diff --git a/drivers/staging/media/atomisp/i2c/ov8858.h b/drivers/staging/media/atomisp/i2c/ov8858.h index 9be6a0e63861..d3fde200c013 100644 --- a/drivers/staging/media/atomisp/i2c/ov8858.h +++ b/drivers/staging/media/atomisp/i2c/ov8858.h @@ -266,7 +266,7 @@ struct ov8858_device { const struct ov8858_reg *regs; struct ov8858_vcm *vcm_driver; const struct ov8858_resolution *curr_res_table; - int entries_curr_table; + unsigned long entries_curr_table; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *run_mode; diff --git a/drivers/staging/media/atomisp/i2c/ov8858_btns.h b/drivers/staging/media/atomisp/i2c/ov8858_btns.h index 09e3cdc1a394..f9a3cf8fbf1a 100644 --- a/drivers/staging/media/atomisp/i2c/ov8858_btns.h +++ b/drivers/staging/media/atomisp/i2c/ov8858_btns.h @@ -266,7 +266,7 @@ struct ov8858_device { const struct ov8858_reg *regs; struct ov8858_vcm *vcm_driver; const struct ov8858_resolution *curr_res_table; - int entries_curr_table; + unsigned long entries_curr_table; struct v4l2_ctrl_handler ctrl_handler; struct v4l2_ctrl *run_mode; diff --git a/drivers/staging/media/atomisp/pci/atomisp2/Makefile b/drivers/staging/media/atomisp/pci/atomisp2/Makefile index 726eaa293c55..2bd98f0667ec 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/Makefile +++ b/drivers/staging/media/atomisp/pci/atomisp2/Makefile @@ -354,7 +354,9 @@ ccflags-y += $(INCLUDES) $(DEFINES) -fno-common # HACK! While this driver is in bad shape, don't enable several warnings # that would be otherwise enabled with W=1 -ccflags-y += -Wno-unused-const-variable -Wno-missing-prototypes \ - -Wno-unused-but-set-variable -Wno-missing-declarations \ - -Wno-suggest-attribute=format -Wno-missing-prototypes \ - -Wno-implicit-fallthrough +ccflags-y += $(call cc-disable-warning, implicit-fallthrough) +ccflags-y += $(call cc-disable-warning, missing-prototypes) +ccflags-y += $(call cc-disable-warning, missing-declarations) +ccflags-y += $(call cc-disable-warning, suggest-attribute=format) +ccflags-y += $(call cc-disable-warning, unused-const-variable) +ccflags-y += $(call cc-disable-warning, unused-but-set-variable) diff --git a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_internal.h b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_internal.h index d3667132851b..c8e0c4fe3717 100644 --- a/drivers/staging/media/atomisp/pci/atomisp2/atomisp_internal.h +++ b/drivers/staging/media/atomisp/pci/atomisp2/atomisp_internal.h @@ -275,7 +275,7 @@ struct atomisp_device { */ struct mutex streamoff_mutex; - int input_cnt; + unsigned int input_cnt; struct atomisp_input_subdev inputs[ATOM_ISP_MAX_INPUTS]; struct v4l2_subdev *flash; struct v4l2_subdev *motor; diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c index 370ecb959543..f28916ea69f1 100644 --- a/drivers/staging/media/cxd2099/cxd2099.c +++ b/drivers/staging/media/cxd2099/cxd2099.c @@ -1,7 +1,7 @@ /* * cxd2099.c: Driver for the CXD2099AR Common Interface Controller * - * Copyright (C) 2010-2011 Digital Devices GmbH + * Copyright (C) 2010-2013 Digital Devices GmbH * * * This program is free software; you can redistribute it and/or @@ -33,7 +33,10 @@ #include "cxd2099.h" -#define MAX_BUFFER_SIZE 248 +/* comment this line to deactivate the cxd2099ar buffer mode */ +#define BUFFER_MODE 1 + +static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount); struct cxd { struct dvb_ca_en50221 en; @@ -48,6 +51,7 @@ struct cxd { int mode; int ready; int dr; + int write_busy; int slot_stat; u8 amem[1024]; @@ -55,6 +59,9 @@ struct cxd { int cammode; struct mutex lock; + + u8 rbuf[1028]; + u8 wbuf[1028]; }; static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, @@ -73,7 +80,7 @@ static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, } static int i2c_write(struct i2c_adapter *adapter, u8 adr, - u8 *data, u8 len) + u8 *data, u16 len) { struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; @@ -100,12 +107,12 @@ static int i2c_read_reg(struct i2c_adapter *adapter, u8 adr, } static int i2c_read(struct i2c_adapter *adapter, u8 adr, - u8 reg, u8 *data, u8 n) + u8 reg, u8 *data, u16 n) { struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = ®, .len = 1}, - {.addr = adr, .flags = I2C_M_RD, - .buf = data, .len = n} }; + .buf = ®, .len = 1}, + {.addr = adr, .flags = I2C_M_RD, + .buf = data, .len = n} }; if (i2c_transfer(adapter, msgs, 2) != 2) { dev_err(&adapter->dev, "error in i2c_read\n"); @@ -114,14 +121,26 @@ static int i2c_read(struct i2c_adapter *adapter, u8 adr, return 0; } -static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n) +static int read_block(struct cxd *ci, u8 adr, u8 *data, u16 n) { - int status; + int status = 0; - status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr); + if (ci->lastaddress != adr) + status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr); if (!status) { ci->lastaddress = adr; - status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, n); + + while (n) { + int len = n; + + if (ci->cfg.max_i2c && (len > ci->cfg.max_i2c)) + len = ci->cfg.max_i2c; + status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, len); + if (status) + return status; + data += len; + n -= len; + } } return status; } @@ -182,16 +201,16 @@ static int write_io(struct cxd *ci, u16 address, u8 val) static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask) { - int status; + int status = 0; - status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg); + if (ci->lastaddress != reg) + status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg); if (!status && reg >= 6 && reg <= 8 && mask != 0xff) status = i2c_read_reg(ci->i2c, ci->cfg.adr, 1, &ci->regs[reg]); + ci->lastaddress = reg; ci->regs[reg] = (ci->regs[reg] & (~mask)) | val; - if (!status) { - ci->lastaddress = reg; + if (!status) status = i2c_write_reg(ci->i2c, ci->cfg.adr, 1, ci->regs[reg]); - } if (reg == 0x20) ci->regs[reg] &= 0x7f; return status; @@ -203,16 +222,29 @@ static int write_reg(struct cxd *ci, u8 reg, u8 val) } #ifdef BUFFER_MODE -static int write_block(struct cxd *ci, u8 adr, u8 *data, int n) +static int write_block(struct cxd *ci, u8 adr, u8 *data, u16 n) { - int status; - u8 buf[256] = {1}; - - status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr); - if (!status) { - ci->lastaddress = adr; - memcpy(buf + 1, data, n); - status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1); + int status = 0; + u8 *buf = ci->wbuf; + + if (ci->lastaddress != adr) + status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr); + if (status) + return status; + + ci->lastaddress = adr; + buf[0] = 1; + while (n) { + int len = n; + + if (ci->cfg.max_i2c && (len + 1 > ci->cfg.max_i2c)) + len = ci->cfg.max_i2c - 1; + memcpy(buf + 1, data, len); + status = i2c_write(ci->i2c, ci->cfg.adr, buf, len + 1); + if (status) + return status; + n -= len; + data += len; } return status; } @@ -238,6 +270,8 @@ static void set_mode(struct cxd *ci, int mode) static void cam_mode(struct cxd *ci, int mode) { + u8 dummy; + if (mode == ci->cammode) return; @@ -246,16 +280,15 @@ static void cam_mode(struct cxd *ci, int mode) write_regm(ci, 0x20, 0x80, 0x80); break; case 0x01: -#ifdef BUFFER_MODE if (!ci->en.read_data) return; + ci->write_busy = 0; dev_info(&ci->i2c->dev, "enable cam buffer mode\n"); - /* write_reg(ci, 0x0d, 0x00); */ - /* write_reg(ci, 0x0e, 0x01); */ + write_reg(ci, 0x0d, 0x00); + write_reg(ci, 0x0e, 0x01); write_regm(ci, 0x08, 0x40, 0x40); - /* read_reg(ci, 0x12, &dummy); */ + read_reg(ci, 0x12, &dummy); write_regm(ci, 0x08, 0x80, 0x80); -#endif break; default: break; @@ -325,7 +358,10 @@ static int init(struct cxd *ci) if (status < 0) break; - if (ci->cfg.clock_mode) { + if (ci->cfg.clock_mode == 2) { + /* bitrate*2^13/ 72000 */ + u32 reg = ((ci->cfg.bitrate << 13) + 71999) / 72000; + if (ci->cfg.polarity) { status = write_reg(ci, 0x09, 0x6f); if (status < 0) @@ -335,6 +371,25 @@ static int init(struct cxd *ci) if (status < 0) break; } + status = write_reg(ci, 0x20, 0x08); + if (status < 0) + break; + status = write_reg(ci, 0x21, (reg >> 8) & 0xff); + if (status < 0) + break; + status = write_reg(ci, 0x22, reg & 0xff); + if (status < 0) + break; + } else if (ci->cfg.clock_mode == 1) { + if (ci->cfg.polarity) { + status = write_reg(ci, 0x09, 0x6f); /* D */ + if (status < 0) + break; + } else { + status = write_reg(ci, 0x09, 0x6d); + if (status < 0) + break; + } status = write_reg(ci, 0x20, 0x68); if (status < 0) break; @@ -346,7 +401,7 @@ static int init(struct cxd *ci) break; } else { if (ci->cfg.polarity) { - status = write_reg(ci, 0x09, 0x4f); + status = write_reg(ci, 0x09, 0x4f); /* C */ if (status < 0) break; } else { @@ -354,7 +409,6 @@ static int init(struct cxd *ci) if (status < 0) break; } - status = write_reg(ci, 0x20, 0x28); if (status < 0) break; @@ -401,7 +455,6 @@ static int read_attribute_mem(struct dvb_ca_en50221 *ca, set_mode(ci, 1); read_pccard(ci, address, &val, 1); mutex_unlock(&ci->lock); - /* printk(KERN_INFO "%02x:%02x\n", address,val); */ return val; } @@ -446,6 +499,9 @@ static int slot_reset(struct dvb_ca_en50221 *ca, int slot) { struct cxd *ci = ca->data; + if (ci->cammode) + read_data(ca, slot, ci->rbuf, 0); + mutex_lock(&ci->lock); cam_mode(ci, 0); write_reg(ci, 0x00, 0x21); @@ -465,7 +521,6 @@ static int slot_reset(struct dvb_ca_en50221 *ca, int slot) } } mutex_unlock(&ci->lock); - /* msleep(500); */ return 0; } @@ -474,11 +529,19 @@ static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) struct cxd *ci = ca->data; dev_info(&ci->i2c->dev, "%s\n", __func__); + if (ci->cammode) + read_data(ca, slot, ci->rbuf, 0); mutex_lock(&ci->lock); + write_reg(ci, 0x00, 0x21); + write_reg(ci, 0x06, 0x1F); + msleep(300); + write_regm(ci, 0x09, 0x08, 0x08); write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */ write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */ + ci->mode = -1; + ci->write_busy = 0; mutex_unlock(&ci->lock); return 0; } @@ -490,9 +553,7 @@ static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) mutex_lock(&ci->lock); write_regm(ci, 0x09, 0x00, 0x08); set_mode(ci, 0); -#ifdef BUFFER_MODE cam_mode(ci, 1); -#endif mutex_unlock(&ci->lock); return 0; } @@ -506,12 +567,10 @@ static int campoll(struct cxd *ci) return 0; write_reg(ci, 0x05, istat); - if (istat & 0x40) { + if (istat & 0x40) ci->dr = 1; - dev_info(&ci->i2c->dev, "DR\n"); - } if (istat & 0x20) - dev_info(&ci->i2c->dev, "WC\n"); + ci->write_busy = 0; if (istat & 2) { u8 slotstat; @@ -519,7 +578,8 @@ static int campoll(struct cxd *ci) read_reg(ci, 0x01, &slotstat); if (!(2 & slotstat)) { if (!ci->slot_stat) { - ci->slot_stat = DVB_CA_EN50221_POLL_CAM_PRESENT; + ci->slot_stat |= + DVB_CA_EN50221_POLL_CAM_PRESENT; write_regm(ci, 0x03, 0x08, 0x08); } @@ -531,8 +591,8 @@ static int campoll(struct cxd *ci) ci->ready = 0; } } - if (istat & 8 && - ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) { + if ((istat & 8) && + (ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT)) { ci->ready = 1; ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY; } @@ -553,7 +613,6 @@ static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) return ci->slot_stat; } -#ifdef BUFFER_MODE static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) { struct cxd *ci = ca->data; @@ -564,30 +623,38 @@ static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) campoll(ci); mutex_unlock(&ci->lock); - dev_info(&ci->i2c->dev, "%s\n", __func__); if (!ci->dr) return 0; mutex_lock(&ci->lock); read_reg(ci, 0x0f, &msb); read_reg(ci, 0x10, &lsb); - len = (msb << 8) | lsb; + len = ((u16)msb << 8) | lsb; + if (len > ecount || len < 2) { + /* read it anyway or cxd may hang */ + read_block(ci, 0x12, ci->rbuf, len); + mutex_unlock(&ci->lock); + return -EIO; + } read_block(ci, 0x12, ebuf, len); ci->dr = 0; mutex_unlock(&ci->lock); - return len; } +#ifdef BUFFER_MODE + static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) { struct cxd *ci = ca->data; + if (ci->write_busy) + return -EAGAIN; mutex_lock(&ci->lock); - dev_info(&ci->i2c->dev, "%s %d\n", __func__, ecount); write_reg(ci, 0x0d, ecount >> 8); write_reg(ci, 0x0e, ecount & 0xff); write_block(ci, 0x11, ebuf, ecount); + ci->write_busy = 1; mutex_unlock(&ci->lock); return ecount; } diff --git a/drivers/staging/media/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h index 0eb607c5b423..f4b29b1d6eb8 100644 --- a/drivers/staging/media/cxd2099/cxd2099.h +++ b/drivers/staging/media/cxd2099/cxd2099.h @@ -30,8 +30,10 @@ struct cxd2099_cfg { u32 bitrate; u8 adr; - u8 polarity:1; - u8 clock_mode:1; + u8 polarity; + u8 clock_mode; + + u32 max_i2c; }; #if defined(CONFIG_DVB_CXD2099) || \ diff --git a/drivers/staging/rtl8188eu/core/rtw_cmd.c b/drivers/staging/rtl8188eu/core/rtw_cmd.c index 002d09159896..a69007ef77bf 100644 --- a/drivers/staging/rtl8188eu/core/rtw_cmd.c +++ b/drivers/staging/rtl8188eu/core/rtw_cmd.c @@ -132,7 +132,7 @@ void rtw_free_cmd_obj(struct cmd_obj *pcmd) kfree(pcmd->parmbuf); } - if (!pcmd->rsp) { + if (pcmd->rsp) { if (pcmd->rspsz != 0) { /* free rsp in cmd_obj */ kfree(pcmd->rsp); diff --git a/drivers/staging/rtl8188eu/os_dep/usb_intf.c b/drivers/staging/rtl8188eu/os_dep/usb_intf.c index 963235fd7292..56cd4e5e51b2 100644 --- a/drivers/staging/rtl8188eu/os_dep/usb_intf.c +++ b/drivers/staging/rtl8188eu/os_dep/usb_intf.c @@ -43,7 +43,9 @@ static struct usb_device_id rtw_usb_id_tbl[] = { {USB_DEVICE(0x2001, 0x330F)}, /* DLink DWA-125 REV D1 */ {USB_DEVICE(0x2001, 0x3310)}, /* Dlink DWA-123 REV D1 */ {USB_DEVICE(0x2001, 0x3311)}, /* DLink GO-USB-N150 REV B1 */ + {USB_DEVICE(0x2357, 0x010c)}, /* TP-Link TL-WN722N v2 */ {USB_DEVICE(0x0df6, 0x0076)}, /* Sitecom N150 v2 */ + {USB_DEVICE(USB_VENDER_ID_REALTEK, 0xffef)}, /* Rosewill RNX-N150NUB */ {} /* Terminating entry */ }; diff --git a/drivers/staging/sm750fb/ddk750_chip.c b/drivers/staging/sm750fb/ddk750_chip.c index 944dd25924be..4754f7a20684 100644 --- a/drivers/staging/sm750fb/ddk750_chip.c +++ b/drivers/staging/sm750fb/ddk750_chip.c @@ -40,7 +40,7 @@ static unsigned int get_mxclk_freq(void) pll_reg = peek32(MXCLK_PLL_CTRL); M = (pll_reg & PLL_CTRL_M_MASK) >> PLL_CTRL_M_SHIFT; - N = (pll_reg & PLL_CTRL_N_MASK) >> PLL_CTRL_M_SHIFT; + N = (pll_reg & PLL_CTRL_N_MASK) >> PLL_CTRL_N_SHIFT; OD = (pll_reg & PLL_CTRL_OD_MASK) >> PLL_CTRL_OD_SHIFT; POD = (pll_reg & PLL_CTRL_POD_MASK) >> PLL_CTRL_POD_SHIFT; diff --git a/drivers/staging/sm750fb/sm750.c b/drivers/staging/sm750fb/sm750.c index 3aa4128703d5..67207b0554cd 100644 --- a/drivers/staging/sm750fb/sm750.c +++ b/drivers/staging/sm750fb/sm750.c @@ -1053,6 +1053,26 @@ release_fb: return err; } +static int lynxfb_kick_out_firmware_fb(struct pci_dev *pdev) +{ + struct apertures_struct *ap; + bool primary = false; + + ap = alloc_apertures(1); + if (!ap) + return -ENOMEM; + + ap->ranges[0].base = pci_resource_start(pdev, 0); + ap->ranges[0].size = pci_resource_len(pdev, 0); +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & + IORESOURCE_ROM_SHADOW; +#endif + remove_conflicting_framebuffers(ap, "sm750_fb1", primary); + kfree(ap); + return 0; +} + static int lynxfb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -1061,6 +1081,10 @@ static int lynxfb_pci_probe(struct pci_dev *pdev, int fbidx; int err; + err = lynxfb_kick_out_firmware_fb(pdev); + if (err) + return err; + /* enable device */ err = pcim_enable_device(pdev); if (err) diff --git a/drivers/staging/speakup/main.c b/drivers/staging/speakup/main.c index 82e5de248947..67956e24779c 100644 --- a/drivers/staging/speakup/main.c +++ b/drivers/staging/speakup/main.c @@ -2314,6 +2314,7 @@ static void __exit speakup_exit(void) mutex_lock(&spk_mutex); synth_release(); mutex_unlock(&spk_mutex); + spk_ttyio_unregister_ldisc(); speakup_kobj_exit(); @@ -2376,6 +2377,7 @@ static int __init speakup_init(void) if (err) goto error_kobjects; + spk_ttyio_register_ldisc(); synth_init(synth_name); speakup_register_devsynth(); /* diff --git a/drivers/staging/speakup/spk_priv.h b/drivers/staging/speakup/spk_priv.h index 87b6a0a4c54d..046040ac074c 100644 --- a/drivers/staging/speakup/spk_priv.h +++ b/drivers/staging/speakup/spk_priv.h @@ -48,6 +48,8 @@ void spk_stop_serial_interrupt(void); int spk_wait_for_xmitr(struct spk_synth *in_synth); void spk_serial_release(void); void spk_ttyio_release(void); +void spk_ttyio_register_ldisc(void); +void spk_ttyio_unregister_ldisc(void); void synth_buffer_skip_nonlatin1(void); u16 synth_buffer_getc(void); diff --git a/drivers/staging/speakup/spk_ttyio.c b/drivers/staging/speakup/spk_ttyio.c index ed8e96b06ead..fe340b07c482 100644 --- a/drivers/staging/speakup/spk_ttyio.c +++ b/drivers/staging/speakup/spk_ttyio.c @@ -154,12 +154,6 @@ static int spk_ttyio_initialise_ldisc(struct spk_synth *synth) struct ktermios tmp_termios; dev_t dev; - ret = tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops); - if (ret) { - pr_err("Error registering line discipline.\n"); - return ret; - } - ret = get_dev_to_use(synth, &dev); if (ret) return ret; @@ -196,10 +190,24 @@ static int spk_ttyio_initialise_ldisc(struct spk_synth *synth) tty_unlock(tty); ret = tty_set_ldisc(tty, N_SPEAKUP); + if (ret) + pr_err("speakup: Failed to set N_SPEAKUP on tty\n"); return ret; } +void spk_ttyio_register_ldisc(void) +{ + if (tty_register_ldisc(N_SPEAKUP, &spk_ttyio_ldisc_ops)) + pr_warn("speakup: Error registering line discipline. Most synths won't work.\n"); +} + +void spk_ttyio_unregister_ldisc(void) +{ + if (tty_unregister_ldisc(N_SPEAKUP)) + pr_warn("speakup: Couldn't unregister ldisc\n"); +} + static int spk_ttyio_out(struct spk_synth *in_synth, const char ch) { if (in_synth->alive && speakup_tty && speakup_tty->ops->write) { @@ -300,7 +308,7 @@ void spk_ttyio_release(void) tty_ldisc_flush(speakup_tty); tty_unlock(speakup_tty); - tty_ldisc_release(speakup_tty); + tty_release_struct(speakup_tty, speakup_tty->index); } EXPORT_SYMBOL_GPL(spk_ttyio_release); diff --git a/drivers/staging/vboxvideo/Kconfig b/drivers/staging/vboxvideo/Kconfig new file mode 100644 index 000000000000..a52746f9a670 --- /dev/null +++ b/drivers/staging/vboxvideo/Kconfig @@ -0,0 +1,12 @@ +config DRM_VBOXVIDEO + tristate "Virtual Box Graphics Card" + depends on DRM && X86 && PCI + select DRM_KMS_HELPER + help + This is a KMS driver for the virtual Graphics Card used in + Virtual Box virtual machines. + + Although it is possible to builtin this module, it is advised + to build this driver as a module, so that it can be updated + independently of the kernel. Select M to built this driver as a + module and add support for these devices via drm/kms interfaces. diff --git a/drivers/staging/vboxvideo/Makefile b/drivers/staging/vboxvideo/Makefile new file mode 100644 index 000000000000..2d0b3bc7ad73 --- /dev/null +++ b/drivers/staging/vboxvideo/Makefile @@ -0,0 +1,7 @@ +ccflags-y := -Iinclude/drm + +vboxvideo-y := hgsmi_base.o modesetting.o vbva_base.o \ + vbox_drv.o vbox_fb.o vbox_hgsmi.o vbox_irq.o vbox_main.o \ + vbox_mode.o vbox_prime.o vbox_ttm.o + +obj-$(CONFIG_DRM_VBOXVIDEO) += vboxvideo.o diff --git a/drivers/staging/vboxvideo/TODO b/drivers/staging/vboxvideo/TODO new file mode 100644 index 000000000000..ce764309b079 --- /dev/null +++ b/drivers/staging/vboxvideo/TODO @@ -0,0 +1,9 @@ +TODO: +-Move the driver over to the atomic API +-Stop using old load / unload drm_driver hooks +-Get a full review from the drm-maintainers on dri-devel done on this driver +-Extend this TODO with the results of that review + +Please send any patches to Greg Kroah-Hartman <gregkh@linuxfoundation.org>, +Hans de Goede <hdegoede@redhat.com> and +Michael Thayer <michael.thayer@oracle.com>. diff --git a/drivers/staging/vboxvideo/hgsmi_base.c b/drivers/staging/vboxvideo/hgsmi_base.c new file mode 100644 index 000000000000..15ff5f42e2cd --- /dev/null +++ b/drivers/staging/vboxvideo/hgsmi_base.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2006-2017 Oracle 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. + */ + +#include "vbox_drv.h" +#include "vbox_err.h" +#include "vboxvideo_guest.h" +#include "vboxvideo_vbe.h" +#include "hgsmi_channels.h" +#include "hgsmi_ch_setup.h" + +/** + * Inform the host of the location of the host flags in VRAM via an HGSMI cmd. + * @param ctx the context of the guest heap to use. + * @param location the offset chosen for the flags within guest VRAM. + * @returns 0 on success, -errno on failure + */ +int hgsmi_report_flags_location(struct gen_pool *ctx, u32 location) +{ + struct hgsmi_buffer_location *p; + + p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_HGSMI, + HGSMI_CC_HOST_FLAGS_LOCATION); + if (!p) + return -ENOMEM; + + p->buf_location = location; + p->buf_len = sizeof(struct hgsmi_host_flags); + + hgsmi_buffer_submit(ctx, p); + hgsmi_buffer_free(ctx, p); + + return 0; +} + +/** + * Notify the host of HGSMI-related guest capabilities via an HGSMI command. + * @param ctx the context of the guest heap to use. + * @param caps the capabilities to report, see vbva_caps. + * @returns 0 on success, -errno on failure + */ +int hgsmi_send_caps_info(struct gen_pool *ctx, u32 caps) +{ + struct vbva_caps *p; + + p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, VBVA_INFO_CAPS); + if (!p) + return -ENOMEM; + + p->rc = VERR_NOT_IMPLEMENTED; + p->caps = caps; + + hgsmi_buffer_submit(ctx, p); + + WARN_ON_ONCE(RT_FAILURE(p->rc)); + + hgsmi_buffer_free(ctx, p); + + return 0; +} + +int hgsmi_test_query_conf(struct gen_pool *ctx) +{ + u32 value = 0; + int ret; + + ret = hgsmi_query_conf(ctx, U32_MAX, &value); + if (ret) + return ret; + + return value == U32_MAX ? 0 : -EIO; +} + +/** + * Query the host for an HGSMI configuration parameter via an HGSMI command. + * @param ctx the context containing the heap used + * @param index the index of the parameter to query, + * @see vbva_conf32::index + * @param value_ret where to store the value of the parameter on success + * @returns 0 on success, -errno on failure + */ +int hgsmi_query_conf(struct gen_pool *ctx, u32 index, u32 *value_ret) +{ + struct vbva_conf32 *p; + + p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, + VBVA_QUERY_CONF32); + if (!p) + return -ENOMEM; + + p->index = index; + p->value = U32_MAX; + + hgsmi_buffer_submit(ctx, p); + + *value_ret = p->value; + + hgsmi_buffer_free(ctx, p); + + return 0; +} + +/** + * Pass the host a new mouse pointer shape via an HGSMI command. + * + * @param ctx the context containing the heap to be used + * @param flags cursor flags, @see VMMDevReqMousePointer::flags + * @param hot_x horizontal position of the hot spot + * @param hot_y vertical position of the hot spot + * @param width width in pixels of the cursor + * @param height height in pixels of the cursor + * @param pixels pixel data, @see VMMDevReqMousePointer for the format + * @param len size in bytes of the pixel data + * @returns 0 on success, -errno on failure + */ +int hgsmi_update_pointer_shape(struct gen_pool *ctx, u32 flags, + u32 hot_x, u32 hot_y, u32 width, u32 height, + u8 *pixels, u32 len) +{ + struct vbva_mouse_pointer_shape *p; + u32 pixel_len = 0; + int rc; + + if (flags & VBOX_MOUSE_POINTER_SHAPE) { + /* + * Size of the pointer data: + * sizeof (AND mask) + sizeof (XOR_MASK) + */ + pixel_len = ((((width + 7) / 8) * height + 3) & ~3) + + width * 4 * height; + if (pixel_len > len) + return -EINVAL; + + /* + * If shape is supplied, then always create the pointer visible. + * See comments in 'vboxUpdatePointerShape' + */ + flags |= VBOX_MOUSE_POINTER_VISIBLE; + } + + p = hgsmi_buffer_alloc(ctx, sizeof(*p) + pixel_len, HGSMI_CH_VBVA, + VBVA_MOUSE_POINTER_SHAPE); + if (!p) + return -ENOMEM; + + p->result = VINF_SUCCESS; + p->flags = flags; + p->hot_X = hot_x; + p->hot_y = hot_y; + p->width = width; + p->height = height; + if (pixel_len) + memcpy(p->data, pixels, pixel_len); + + hgsmi_buffer_submit(ctx, p); + + switch (p->result) { + case VINF_SUCCESS: + rc = 0; + break; + case VERR_NO_MEMORY: + rc = -ENOMEM; + break; + case VERR_NOT_SUPPORTED: + rc = -EBUSY; + break; + default: + rc = -EINVAL; + } + + hgsmi_buffer_free(ctx, p); + + return rc; +} + +/** + * Report the guest cursor position. The host may wish to use this information + * to re-position its own cursor (though this is currently unlikely). The + * current host cursor position is returned. + * @param ctx The context containing the heap used. + * @param report_position Are we reporting a position? + * @param x Guest cursor X position. + * @param y Guest cursor Y position. + * @param x_host Host cursor X position is stored here. Optional. + * @param y_host Host cursor Y position is stored here. Optional. + * @returns 0 on success, -errno on failure + */ +int hgsmi_cursor_position(struct gen_pool *ctx, bool report_position, + u32 x, u32 y, u32 *x_host, u32 *y_host) +{ + struct vbva_cursor_position *p; + + p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, + VBVA_CURSOR_POSITION); + if (!p) + return -ENOMEM; + + p->report_position = report_position; + p->x = x; + p->y = y; + + hgsmi_buffer_submit(ctx, p); + + *x_host = p->x; + *y_host = p->y; + + hgsmi_buffer_free(ctx, p); + + return 0; +} + +/** + * @todo Mouse pointer position to be read from VMMDev memory, address of the + * memory region can be queried from VMMDev via an IOCTL. This VMMDev memory + * region will contain host information which is needed by the guest. + * + * Reading will not cause a switch to the host. + * + * Have to take into account: + * * synchronization: host must write to the memory only from EMT, + * large structures must be read under flag, which tells the host + * that the guest is currently reading the memory (OWNER flag?). + * * guest writes: may be allocate a page for the host info and make + * the page readonly for the guest. + * * the information should be available only for additions drivers. + * * VMMDev additions driver will inform the host which version of the info + * it expects, host must support all versions. + */ diff --git a/drivers/staging/vboxvideo/hgsmi_ch_setup.h b/drivers/staging/vboxvideo/hgsmi_ch_setup.h new file mode 100644 index 000000000000..8e6d9e11a69c --- /dev/null +++ b/drivers/staging/vboxvideo/hgsmi_ch_setup.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2006-2017 Oracle 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. + */ + +#ifndef __HGSMI_CH_SETUP_H__ +#define __HGSMI_CH_SETUP_H__ + +/* + * Tell the host the location of hgsmi_host_flags structure, where the host + * can write information about pending buffers, etc, and which can be quickly + * polled by the guest without a need to port IO. + */ +#define HGSMI_CC_HOST_FLAGS_LOCATION 0 + +struct hgsmi_buffer_location { + u32 buf_location; + u32 buf_len; +} __packed; + +/* HGSMI setup and configuration data structures. */ +/* host->guest commands pending, should be accessed under FIFO lock only */ +#define HGSMIHOSTFLAGS_COMMANDS_PENDING 0x01u +/* IRQ is fired, should be accessed under VGAState::lock only */ +#define HGSMIHOSTFLAGS_IRQ 0x02u +/* vsync interrupt flag, should be accessed under VGAState::lock only */ +#define HGSMIHOSTFLAGS_VSYNC 0x10u +/** monitor hotplug flag, should be accessed under VGAState::lock only */ +#define HGSMIHOSTFLAGS_HOTPLUG 0x20u +/** + * Cursor capability state change flag, should be accessed under + * VGAState::lock only. @see vbva_conf32. + */ +#define HGSMIHOSTFLAGS_CURSOR_CAPABILITIES 0x40u + +struct hgsmi_host_flags { + /* + * Host flags can be accessed and modified in multiple threads + * concurrently, e.g. CrOpenGL HGCM and GUI threads when completing + * HGSMI 3D and Video Accel respectively, EMT thread when dealing with + * HGSMI command processing, etc. + * Besides settings/cleaning flags atomically, some flags have their + * own special sync restrictions, see comments for flags above. + */ + u32 host_flags; + u32 reserved[3]; +} __packed; + +#endif diff --git a/drivers/staging/vboxvideo/hgsmi_channels.h b/drivers/staging/vboxvideo/hgsmi_channels.h new file mode 100644 index 000000000000..a2a34b2167b4 --- /dev/null +++ b/drivers/staging/vboxvideo/hgsmi_channels.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2006-2017 Oracle 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. + */ + +#ifndef __HGSMI_CHANNELS_H__ +#define __HGSMI_CHANNELS_H__ + +/* + * Each channel has an 8 bit identifier. There are a number of predefined + * (hardcoded) channels. + * + * HGSMI_CH_HGSMI channel can be used to map a string channel identifier + * to a free 16 bit numerical value. values are allocated in range + * [HGSMI_CH_STRING_FIRST;HGSMI_CH_STRING_LAST]. + */ + +/* A reserved channel value */ +#define HGSMI_CH_RESERVED 0x00 +/* HGCMI: setup and configuration */ +#define HGSMI_CH_HGSMI 0x01 +/* Graphics: VBVA */ +#define HGSMI_CH_VBVA 0x02 +/* Graphics: Seamless with a single guest region */ +#define HGSMI_CH_SEAMLESS 0x03 +/* Graphics: Seamless with separate host windows */ +#define HGSMI_CH_SEAMLESS2 0x04 +/* Graphics: OpenGL HW acceleration */ +#define HGSMI_CH_OPENGL 0x05 + +/* The first channel index to be used for string mappings (inclusive) */ +#define HGSMI_CH_STRING_FIRST 0x20 +/* The last channel index for string mappings (inclusive) */ +#define HGSMI_CH_STRING_LAST 0xff + +#endif diff --git a/drivers/staging/vboxvideo/hgsmi_defs.h b/drivers/staging/vboxvideo/hgsmi_defs.h new file mode 100644 index 000000000000..5b21fb974d20 --- /dev/null +++ b/drivers/staging/vboxvideo/hgsmi_defs.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2006-2017 Oracle 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. + */ + +#ifndef __HGSMI_DEFS_H__ +#define __HGSMI_DEFS_H__ + +/* Buffer sequence type mask. */ +#define HGSMI_BUFFER_HEADER_F_SEQ_MASK 0x03 +/* Single buffer, not a part of a sequence. */ +#define HGSMI_BUFFER_HEADER_F_SEQ_SINGLE 0x00 +/* The first buffer in a sequence. */ +#define HGSMI_BUFFER_HEADER_F_SEQ_START 0x01 +/* A middle buffer in a sequence. */ +#define HGSMI_BUFFER_HEADER_F_SEQ_CONTINUE 0x02 +/* The last buffer in a sequence. */ +#define HGSMI_BUFFER_HEADER_F_SEQ_END 0x03 + +/* 16 bytes buffer header. */ +struct hgsmi_buffer_header { + u32 data_size; /* Size of data that follows the header. */ + u8 flags; /* HGSMI_BUFFER_HEADER_F_* */ + u8 channel; /* The channel the data must be routed to. */ + u16 channel_info; /* Opaque to the HGSMI, used by the channel. */ + + union { + /* Opaque placeholder to make the union 8 bytes. */ + u8 header_data[8]; + + /* HGSMI_BUFFER_HEADER_F_SEQ_SINGLE */ + struct { + u32 reserved1; /* A reserved field, initialize to 0. */ + u32 reserved2; /* A reserved field, initialize to 0. */ + } buffer; + + /* HGSMI_BUFFER_HEADER_F_SEQ_START */ + struct { + /* Must be the same for all buffers in the sequence. */ + u32 sequence_number; + /* The total size of the sequence. */ + u32 sequence_size; + } sequence_start; + + /* + * HGSMI_BUFFER_HEADER_F_SEQ_CONTINUE and + * HGSMI_BUFFER_HEADER_F_SEQ_END + */ + struct { + /* Must be the same for all buffers in the sequence. */ + u32 sequence_number; + /* Data offset in the entire sequence. */ + u32 sequence_offset; + } sequence_continue; + } u; +} __packed; + +/* 8 bytes buffer tail. */ +struct hgsmi_buffer_tail { + /* Reserved, must be initialized to 0. */ + u32 reserved; + /* + * One-at-a-Time Hash: http://www.burtleburtle.net/bob/hash/doobs.html + * Over the header, offset and for first 4 bytes of the tail. + */ + u32 checksum; +} __packed; + +/* + * The size of the array of channels. Array indexes are u8. + * Note: the value must not be changed. + */ +#define HGSMI_NUMBER_OF_CHANNELS 0x100 + +#endif diff --git a/drivers/staging/vboxvideo/modesetting.c b/drivers/staging/vboxvideo/modesetting.c new file mode 100644 index 000000000000..7616b8aab23a --- /dev/null +++ b/drivers/staging/vboxvideo/modesetting.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2006-2017 Oracle 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. + */ + +#include "vbox_drv.h" +#include "vbox_err.h" +#include "vboxvideo_guest.h" +#include "vboxvideo_vbe.h" +#include "hgsmi_channels.h" + +/** + * Set a video mode via an HGSMI request. The views must have been + * initialised first using @a VBoxHGSMISendViewInfo and if the mode is being + * set on the first display then it must be set first using registers. + * @param ctx The context containing the heap to use + * @param display The screen number + * @param origin_x The horizontal displacement relative to the first scrn + * @param origin_y The vertical displacement relative to the first screen + * @param start_offset The offset of the visible area of the framebuffer + * relative to the framebuffer start + * @param pitch The offset in bytes between the starts of two adjecent + * scan lines in video RAM + * @param width The mode width + * @param height The mode height + * @param bpp The colour depth of the mode + * @param flags Flags + */ +void hgsmi_process_display_info(struct gen_pool *ctx, u32 display, + s32 origin_x, s32 origin_y, u32 start_offset, + u32 pitch, u32 width, u32 height, + u16 bpp, u16 flags) +{ + struct vbva_infoscreen *p; + + p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, + VBVA_INFO_SCREEN); + if (!p) + return; + + p->view_index = display; + p->origin_x = origin_x; + p->origin_y = origin_y; + p->start_offset = start_offset; + p->line_size = pitch; + p->width = width; + p->height = height; + p->bits_per_pixel = bpp; + p->flags = flags; + + hgsmi_buffer_submit(ctx, p); + hgsmi_buffer_free(ctx, p); +} + +/** + * Report the rectangle relative to which absolute pointer events should be + * expressed. This information remains valid until the next VBVA resize event + * for any screen, at which time it is reset to the bounding rectangle of all + * virtual screens. + * @param ctx The context containing the heap to use. + * @param origin_x Upper left X co-ordinate relative to the first screen. + * @param origin_y Upper left Y co-ordinate relative to the first screen. + * @param width Rectangle width. + * @param height Rectangle height. + * @returns 0 on success, -errno on failure + */ +int hgsmi_update_input_mapping(struct gen_pool *ctx, s32 origin_x, s32 origin_y, + u32 width, u32 height) +{ + struct vbva_report_input_mapping *p; + + p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, + VBVA_REPORT_INPUT_MAPPING); + if (!p) + return -ENOMEM; + + p->x = origin_x; + p->y = origin_y; + p->cx = width; + p->cy = height; + + hgsmi_buffer_submit(ctx, p); + hgsmi_buffer_free(ctx, p); + + return 0; +} + +/** + * Get most recent video mode hints. + * @param ctx The context containing the heap to use. + * @param screens The number of screens to query hints for, starting at 0. + * @param hints Array of vbva_modehint structures for receiving the hints. + * @returns 0 on success, -errno on failure + */ +int hgsmi_get_mode_hints(struct gen_pool *ctx, unsigned int screens, + struct vbva_modehint *hints) +{ + struct vbva_query_mode_hints *p; + size_t size; + + if (WARN_ON(!hints)) + return -EINVAL; + + size = screens * sizeof(struct vbva_modehint); + p = hgsmi_buffer_alloc(ctx, sizeof(*p) + size, HGSMI_CH_VBVA, + VBVA_QUERY_MODE_HINTS); + if (!p) + return -ENOMEM; + + p->hints_queried_count = screens; + p->hint_structure_guest_size = sizeof(struct vbva_modehint); + p->rc = VERR_NOT_SUPPORTED; + + hgsmi_buffer_submit(ctx, p); + + if (RT_FAILURE(p->rc)) { + hgsmi_buffer_free(ctx, p); + return -EIO; + } + + memcpy(hints, ((u8 *)p) + sizeof(struct vbva_query_mode_hints), size); + hgsmi_buffer_free(ctx, p); + + return 0; +} diff --git a/drivers/staging/vboxvideo/vbox_drv.c b/drivers/staging/vboxvideo/vbox_drv.c new file mode 100644 index 000000000000..92ae1560a16d --- /dev/null +++ b/drivers/staging/vboxvideo/vbox_drv.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2013-2017 Oracle Corporation + * This file is based on ast_drv.c + * Copyright 2012 Red Hat 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, 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 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * Authors: Dave Airlie <airlied@redhat.com> + * Michael Thayer <michael.thayer@oracle.com, + * Hans de Goede <hdegoede@redhat.com> + */ +#include <linux/module.h> +#include <linux/console.h> +#include <linux/vt_kern.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> + +#include "vbox_drv.h" + +int vbox_modeset = -1; + +MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); +module_param_named(modeset, vbox_modeset, int, 0400); + +static struct drm_driver driver; + +static const struct pci_device_id pciidlist[] = { + { 0x80ee, 0xbeef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, 0, 0}, +}; +MODULE_DEVICE_TABLE(pci, pciidlist); + +static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + return drm_get_pci_dev(pdev, ent, &driver); +} + +static void vbox_pci_remove(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + + drm_put_dev(dev); +} + +static int vbox_drm_freeze(struct drm_device *dev) +{ + struct vbox_private *vbox = dev->dev_private; + + drm_kms_helper_poll_disable(dev); + + pci_save_state(dev->pdev); + + drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, true); + + return 0; +} + +static int vbox_drm_thaw(struct drm_device *dev) +{ + struct vbox_private *vbox = dev->dev_private; + + drm_mode_config_reset(dev); + drm_helper_resume_force_mode(dev); + drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, false); + + return 0; +} + +static int vbox_drm_resume(struct drm_device *dev) +{ + int ret; + + if (pci_enable_device(dev->pdev)) + return -EIO; + + ret = vbox_drm_thaw(dev); + if (ret) + return ret; + + drm_kms_helper_poll_enable(dev); + + return 0; +} + +static int vbox_pm_suspend(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *ddev = pci_get_drvdata(pdev); + int error; + + error = vbox_drm_freeze(ddev); + if (error) + return error; + + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +static int vbox_pm_resume(struct device *dev) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + + return vbox_drm_resume(ddev); +} + +static int vbox_pm_freeze(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *ddev = pci_get_drvdata(pdev); + + if (!ddev || !ddev->dev_private) + return -ENODEV; + + return vbox_drm_freeze(ddev); +} + +static int vbox_pm_thaw(struct device *dev) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + + return vbox_drm_thaw(ddev); +} + +static int vbox_pm_poweroff(struct device *dev) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + + return vbox_drm_freeze(ddev); +} + +static const struct dev_pm_ops vbox_pm_ops = { + .suspend = vbox_pm_suspend, + .resume = vbox_pm_resume, + .freeze = vbox_pm_freeze, + .thaw = vbox_pm_thaw, + .poweroff = vbox_pm_poweroff, + .restore = vbox_pm_resume, +}; + +static struct pci_driver vbox_pci_driver = { + .name = DRIVER_NAME, + .id_table = pciidlist, + .probe = vbox_pci_probe, + .remove = vbox_pci_remove, + .driver.pm = &vbox_pm_ops, +}; + +static const struct file_operations vbox_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + .mmap = vbox_mmap, + .poll = drm_poll, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .read = drm_read, +}; + +static int vbox_master_set(struct drm_device *dev, + struct drm_file *file_priv, bool from_open) +{ + struct vbox_private *vbox = dev->dev_private; + + /* + * We do not yet know whether the new owner can handle hotplug, so we + * do not advertise dynamic modes on the first query and send a + * tentative hotplug notification after that to see if they query again. + */ + vbox->initial_mode_queried = false; + + mutex_lock(&vbox->hw_mutex); + /* + * Disable VBVA when someone releases master in case the next person + * tries tries to do VESA. + */ + /** @todo work out if anyone is likely to and whether it will work. */ + /* + * Update: we also disable it because if the new master does not do + * dirty rectangle reporting (e.g. old versions of Plymouth) then at + * least the first screen will still be updated. We enable it as soon + * as we receive a dirty rectangle report. + */ + vbox_disable_accel(vbox); + mutex_unlock(&vbox->hw_mutex); + + return 0; +} + +static void vbox_master_drop(struct drm_device *dev, struct drm_file *file_priv) +{ + struct vbox_private *vbox = dev->dev_private; + + /* See vbox_master_set() */ + vbox->initial_mode_queried = false; + + mutex_lock(&vbox->hw_mutex); + vbox_disable_accel(vbox); + mutex_unlock(&vbox->hw_mutex); +} + +static struct drm_driver driver = { + .driver_features = + DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | + DRIVER_PRIME, + .dev_priv_size = 0, + + .load = vbox_driver_load, + .unload = vbox_driver_unload, + .lastclose = vbox_driver_lastclose, + .master_set = vbox_master_set, + .master_drop = vbox_master_drop, + .set_busid = drm_pci_set_busid, + + .fops = &vbox_fops, + .irq_handler = vbox_irq_handler, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, + .patchlevel = DRIVER_PATCHLEVEL, + + .gem_free_object = vbox_gem_free_object, + .dumb_create = vbox_dumb_create, + .dumb_map_offset = vbox_dumb_mmap_offset, + .dumb_destroy = drm_gem_dumb_destroy, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_pin = vbox_gem_prime_pin, + .gem_prime_unpin = vbox_gem_prime_unpin, + .gem_prime_get_sg_table = vbox_gem_prime_get_sg_table, + .gem_prime_import_sg_table = vbox_gem_prime_import_sg_table, + .gem_prime_vmap = vbox_gem_prime_vmap, + .gem_prime_vunmap = vbox_gem_prime_vunmap, + .gem_prime_mmap = vbox_gem_prime_mmap, +}; + +static int __init vbox_init(void) +{ +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force() && vbox_modeset == -1) + return -EINVAL; +#endif + + if (vbox_modeset == 0) + return -EINVAL; + + return drm_pci_init(&driver, &vbox_pci_driver); +} + +static void __exit vbox_exit(void) +{ + drm_pci_exit(&driver, &vbox_pci_driver); +} + +module_init(vbox_init); +module_exit(vbox_exit); + +MODULE_AUTHOR("Oracle Corporation"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/staging/vboxvideo/vbox_drv.h b/drivers/staging/vboxvideo/vbox_drv.h new file mode 100644 index 000000000000..4b9302703b36 --- /dev/null +++ b/drivers/staging/vboxvideo/vbox_drv.h @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2013-2017 Oracle Corporation + * This file is based on ast_drv.h + * Copyright 2012 Red Hat 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, 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 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * Authors: Dave Airlie <airlied@redhat.com> + * Michael Thayer <michael.thayer@oracle.com, + * Hans de Goede <hdegoede@redhat.com> + */ +#ifndef __VBOX_DRV_H__ +#define __VBOX_DRV_H__ + +#include <linux/genalloc.h> +#include <linux/io.h> +#include <linux/string.h> +#include <linux/version.h> + +#include <drm/drmP.h> +#include <drm/drm_encoder.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem.h> + +#include <drm/ttm/ttm_bo_api.h> +#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_memory.h> +#include <drm/ttm/ttm_module.h> + +#include "vboxvideo_guest.h" +#include "vboxvideo_vbe.h" +#include "hgsmi_ch_setup.h" + +#define DRIVER_NAME "vboxvideo" +#define DRIVER_DESC "Oracle VM VirtualBox Graphics Card" +#define DRIVER_DATE "20130823" + +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 +#define DRIVER_PATCHLEVEL 0 + +#define VBOX_MAX_CURSOR_WIDTH 64 +#define VBOX_MAX_CURSOR_HEIGHT 64 +#define CURSOR_PIXEL_COUNT (VBOX_MAX_CURSOR_WIDTH * VBOX_MAX_CURSOR_HEIGHT) +#define CURSOR_DATA_SIZE (CURSOR_PIXEL_COUNT * 4 + CURSOR_PIXEL_COUNT / 8) + +#define VBOX_MAX_SCREENS 32 + +#define GUEST_HEAP_OFFSET(vbox) ((vbox)->full_vram_size - \ + VBVA_ADAPTER_INFORMATION_SIZE) +#define GUEST_HEAP_SIZE VBVA_ADAPTER_INFORMATION_SIZE +#define GUEST_HEAP_USABLE_SIZE (VBVA_ADAPTER_INFORMATION_SIZE - \ + sizeof(struct hgsmi_host_flags)) +#define HOST_FLAGS_OFFSET GUEST_HEAP_USABLE_SIZE + +struct vbox_fbdev; + +struct vbox_private { + struct drm_device *dev; + + u8 __iomem *guest_heap; + u8 __iomem *vbva_buffers; + struct gen_pool *guest_pool; + struct vbva_buf_ctx *vbva_info; + bool any_pitch; + u32 num_crtcs; + /** Amount of available VRAM, including space used for buffers. */ + u32 full_vram_size; + /** Amount of available VRAM, not including space used for buffers. */ + u32 available_vram_size; + /** Array of structures for receiving mode hints. */ + struct vbva_modehint *last_mode_hints; + + struct vbox_fbdev *fbdev; + + int fb_mtrr; + + struct { + struct drm_global_reference mem_global_ref; + struct ttm_bo_global_ref bo_global_ref; + struct ttm_bo_device bdev; + } ttm; + + struct mutex hw_mutex; /* protects modeset and accel/vbva accesses */ + /** + * We decide whether or not user-space supports display hot-plug + * depending on whether they react to a hot-plug event after the initial + * mode query. + */ + bool initial_mode_queried; + struct work_struct hotplug_work; + u32 input_mapping_width; + u32 input_mapping_height; + /** + * Is user-space using an X.Org-style layout of one large frame-buffer + * encompassing all screen ones or is the fbdev console active? + */ + bool single_framebuffer; + u32 cursor_width; + u32 cursor_height; + u32 cursor_hot_x; + u32 cursor_hot_y; + size_t cursor_data_size; + u8 cursor_data[CURSOR_DATA_SIZE]; +}; + +#undef CURSOR_PIXEL_COUNT +#undef CURSOR_DATA_SIZE + +int vbox_driver_load(struct drm_device *dev, unsigned long flags); +void vbox_driver_unload(struct drm_device *dev); +void vbox_driver_lastclose(struct drm_device *dev); + +struct vbox_gem_object; + +struct vbox_connector { + struct drm_connector base; + char name[32]; + struct vbox_crtc *vbox_crtc; + struct { + u16 width; + u16 height; + bool disconnected; + } mode_hint; +}; + +struct vbox_crtc { + struct drm_crtc base; + bool blanked; + bool disconnected; + unsigned int crtc_id; + u32 fb_offset; + bool cursor_enabled; + u16 x_hint; + u16 y_hint; +}; + +struct vbox_encoder { + struct drm_encoder base; +}; + +struct vbox_framebuffer { + struct drm_framebuffer base; + struct drm_gem_object *obj; +}; + +struct vbox_fbdev { + struct drm_fb_helper helper; + struct vbox_framebuffer afb; + int size; + struct ttm_bo_kmap_obj mapping; + int x1, y1, x2, y2; /* dirty rect */ + spinlock_t dirty_lock; +}; + +#define to_vbox_crtc(x) container_of(x, struct vbox_crtc, base) +#define to_vbox_connector(x) container_of(x, struct vbox_connector, base) +#define to_vbox_encoder(x) container_of(x, struct vbox_encoder, base) +#define to_vbox_framebuffer(x) container_of(x, struct vbox_framebuffer, base) + +int vbox_mode_init(struct drm_device *dev); +void vbox_mode_fini(struct drm_device *dev); + +#define DRM_MODE_FB_CMD drm_mode_fb_cmd2 +#define CRTC_FB(crtc) ((crtc)->primary->fb) + +void vbox_enable_accel(struct vbox_private *vbox); +void vbox_disable_accel(struct vbox_private *vbox); +void vbox_report_caps(struct vbox_private *vbox); + +void vbox_framebuffer_dirty_rectangles(struct drm_framebuffer *fb, + struct drm_clip_rect *rects, + unsigned int num_rects); + +int vbox_framebuffer_init(struct drm_device *dev, + struct vbox_framebuffer *vbox_fb, + const struct DRM_MODE_FB_CMD *mode_cmd, + struct drm_gem_object *obj); + +int vbox_fbdev_init(struct drm_device *dev); +void vbox_fbdev_fini(struct drm_device *dev); +void vbox_fbdev_set_base(struct vbox_private *vbox, unsigned long gpu_addr); + +struct vbox_bo { + struct ttm_buffer_object bo; + struct ttm_placement placement; + struct ttm_bo_kmap_obj kmap; + struct drm_gem_object gem; + struct ttm_place placements[3]; + int pin_count; +}; + +#define gem_to_vbox_bo(gobj) container_of((gobj), struct vbox_bo, gem) + +static inline struct vbox_bo *vbox_bo(struct ttm_buffer_object *bo) +{ + return container_of(bo, struct vbox_bo, bo); +} + +#define to_vbox_obj(x) container_of(x, struct vbox_gem_object, base) + +int vbox_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args); + +void vbox_gem_free_object(struct drm_gem_object *obj); +int vbox_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + u32 handle, u64 *offset); + +#define DRM_FILE_PAGE_OFFSET (0x10000000ULL >> PAGE_SHIFT) + +int vbox_mm_init(struct vbox_private *vbox); +void vbox_mm_fini(struct vbox_private *vbox); + +int vbox_bo_create(struct drm_device *dev, int size, int align, + u32 flags, struct vbox_bo **pvboxbo); + +int vbox_gem_create(struct drm_device *dev, + u32 size, bool iskernel, struct drm_gem_object **obj); + +int vbox_bo_pin(struct vbox_bo *bo, u32 pl_flag, u64 *gpu_addr); +int vbox_bo_unpin(struct vbox_bo *bo); + +static inline int vbox_bo_reserve(struct vbox_bo *bo, bool no_wait) +{ + int ret; + + ret = ttm_bo_reserve(&bo->bo, true, no_wait, NULL); + if (ret) { + if (ret != -ERESTARTSYS && ret != -EBUSY) + DRM_ERROR("reserve failed %p\n", bo); + return ret; + } + return 0; +} + +static inline void vbox_bo_unreserve(struct vbox_bo *bo) +{ + ttm_bo_unreserve(&bo->bo); +} + +void vbox_ttm_placement(struct vbox_bo *bo, int domain); +int vbox_bo_push_sysram(struct vbox_bo *bo); +int vbox_mmap(struct file *filp, struct vm_area_struct *vma); + +/* vbox_prime.c */ +int vbox_gem_prime_pin(struct drm_gem_object *obj); +void vbox_gem_prime_unpin(struct drm_gem_object *obj); +struct sg_table *vbox_gem_prime_get_sg_table(struct drm_gem_object *obj); +struct drm_gem_object *vbox_gem_prime_import_sg_table( + struct drm_device *dev, struct dma_buf_attachment *attach, + struct sg_table *table); +void *vbox_gem_prime_vmap(struct drm_gem_object *obj); +void vbox_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr); +int vbox_gem_prime_mmap(struct drm_gem_object *obj, + struct vm_area_struct *area); + +/* vbox_irq.c */ +int vbox_irq_init(struct vbox_private *vbox); +void vbox_irq_fini(struct vbox_private *vbox); +void vbox_report_hotplug(struct vbox_private *vbox); +irqreturn_t vbox_irq_handler(int irq, void *arg); + +/* vbox_hgsmi.c */ +void *hgsmi_buffer_alloc(struct gen_pool *guest_pool, size_t size, + u8 channel, u16 channel_info); +void hgsmi_buffer_free(struct gen_pool *guest_pool, void *buf); +int hgsmi_buffer_submit(struct gen_pool *guest_pool, void *buf); + +static inline void vbox_write_ioport(u16 index, u16 data) +{ + outw(index, VBE_DISPI_IOPORT_INDEX); + outw(data, VBE_DISPI_IOPORT_DATA); +} + +#endif diff --git a/drivers/staging/vboxvideo/vbox_err.h b/drivers/staging/vboxvideo/vbox_err.h new file mode 100644 index 000000000000..562db8630eb0 --- /dev/null +++ b/drivers/staging/vboxvideo/vbox_err.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2017 Oracle 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. + */ + +#ifndef __VBOX_ERR_H__ +#define __VBOX_ERR_H__ + +/** + * @name VirtualBox virtual-hardware error macros + * @{ + */ + +#define VINF_SUCCESS 0 +#define VERR_INVALID_PARAMETER (-2) +#define VERR_INVALID_POINTER (-6) +#define VERR_NO_MEMORY (-8) +#define VERR_NOT_IMPLEMENTED (-12) +#define VERR_INVALID_FUNCTION (-36) +#define VERR_NOT_SUPPORTED (-37) +#define VERR_TOO_MUCH_DATA (-42) +#define VERR_INVALID_STATE (-79) +#define VERR_OUT_OF_RESOURCES (-80) +#define VERR_ALREADY_EXISTS (-105) +#define VERR_INTERNAL_ERROR (-225) + +#define RT_SUCCESS_NP(rc) ((int)(rc) >= VINF_SUCCESS) +#define RT_SUCCESS(rc) (likely(RT_SUCCESS_NP(rc))) +#define RT_FAILURE(rc) (unlikely(!RT_SUCCESS_NP(rc))) + +/** @} */ + +#endif diff --git a/drivers/staging/vboxvideo/vbox_fb.c b/drivers/staging/vboxvideo/vbox_fb.c new file mode 100644 index 000000000000..35f6d9f8c203 --- /dev/null +++ b/drivers/staging/vboxvideo/vbox_fb.c @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2013-2017 Oracle Corporation + * This file is based on ast_fb.c + * Copyright 2012 Red Hat 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, 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 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * Authors: Dave Airlie <airlied@redhat.com> + * Michael Thayer <michael.thayer@oracle.com, + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/sysrq.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_crtc_helper.h> + +#include "vbox_drv.h" +#include "vboxvideo.h" + +#define VBOX_DIRTY_DELAY (HZ / 30) +/** + * Tell the host about dirty rectangles to update. + */ +static void vbox_dirty_update(struct vbox_fbdev *fbdev, + int x, int y, int width, int height) +{ + struct drm_gem_object *obj; + struct vbox_bo *bo; + int ret = -EBUSY; + bool store_for_later = false; + int x2, y2; + unsigned long flags; + struct drm_clip_rect rect; + + obj = fbdev->afb.obj; + bo = gem_to_vbox_bo(obj); + + /* + * try and reserve the BO, if we fail with busy + * then the BO is being moved and we should + * store up the damage until later. + */ + if (drm_can_sleep()) + ret = vbox_bo_reserve(bo, true); + if (ret) { + if (ret != -EBUSY) + return; + + store_for_later = true; + } + + x2 = x + width - 1; + y2 = y + height - 1; + spin_lock_irqsave(&fbdev->dirty_lock, flags); + + if (fbdev->y1 < y) + y = fbdev->y1; + if (fbdev->y2 > y2) + y2 = fbdev->y2; + if (fbdev->x1 < x) + x = fbdev->x1; + if (fbdev->x2 > x2) + x2 = fbdev->x2; + + if (store_for_later) { + fbdev->x1 = x; + fbdev->x2 = x2; + fbdev->y1 = y; + fbdev->y2 = y2; + spin_unlock_irqrestore(&fbdev->dirty_lock, flags); + return; + } + + fbdev->x1 = INT_MAX; + fbdev->y1 = INT_MAX; + fbdev->x2 = 0; + fbdev->y2 = 0; + + spin_unlock_irqrestore(&fbdev->dirty_lock, flags); + + /* + * Not sure why the original code subtracted 1 here, but I will keep + * it that way to avoid unnecessary differences. + */ + rect.x1 = x; + rect.x2 = x2 + 1; + rect.y1 = y; + rect.y2 = y2 + 1; + vbox_framebuffer_dirty_rectangles(&fbdev->afb.base, &rect, 1); + + vbox_bo_unreserve(bo); +} + +#ifdef CONFIG_FB_DEFERRED_IO +static void vbox_deferred_io(struct fb_info *info, struct list_head *pagelist) +{ + struct vbox_fbdev *fbdev = info->par; + unsigned long start, end, min, max; + struct page *page; + int y1, y2; + + min = ULONG_MAX; + max = 0; + list_for_each_entry(page, pagelist, lru) { + start = page->index << PAGE_SHIFT; + end = start + PAGE_SIZE - 1; + min = min(min, start); + max = max(max, end); + } + + if (min < max) { + y1 = min / info->fix.line_length; + y2 = (max / info->fix.line_length) + 1; + DRM_INFO("%s: Calling dirty update: 0, %d, %d, %d\n", + __func__, y1, info->var.xres, y2 - y1 - 1); + vbox_dirty_update(fbdev, 0, y1, info->var.xres, y2 - y1 - 1); + } +} + +static struct fb_deferred_io vbox_defio = { + .delay = VBOX_DIRTY_DELAY, + .deferred_io = vbox_deferred_io, +}; +#endif + +static void vbox_fillrect(struct fb_info *info, const struct fb_fillrect *rect) +{ + struct vbox_fbdev *fbdev = info->par; + + sys_fillrect(info, rect); + vbox_dirty_update(fbdev, rect->dx, rect->dy, rect->width, rect->height); +} + +static void vbox_copyarea(struct fb_info *info, const struct fb_copyarea *area) +{ + struct vbox_fbdev *fbdev = info->par; + + sys_copyarea(info, area); + vbox_dirty_update(fbdev, area->dx, area->dy, area->width, area->height); +} + +static void vbox_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct vbox_fbdev *fbdev = info->par; + + sys_imageblit(info, image); + vbox_dirty_update(fbdev, image->dx, image->dy, image->width, + image->height); +} + +static struct fb_ops vboxfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_fillrect = vbox_fillrect, + .fb_copyarea = vbox_copyarea, + .fb_imageblit = vbox_imageblit, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_blank = drm_fb_helper_blank, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_debug_enter = drm_fb_helper_debug_enter, + .fb_debug_leave = drm_fb_helper_debug_leave, +}; + +static int vboxfb_create_object(struct vbox_fbdev *fbdev, + struct DRM_MODE_FB_CMD *mode_cmd, + struct drm_gem_object **gobj_p) +{ + struct drm_device *dev = fbdev->helper.dev; + u32 size; + struct drm_gem_object *gobj; + u32 pitch = mode_cmd->pitches[0]; + int ret; + + size = pitch * mode_cmd->height; + ret = vbox_gem_create(dev, size, true, &gobj); + if (ret) + return ret; + + *gobj_p = gobj; + + return 0; +} + +static int vboxfb_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct vbox_fbdev *fbdev = + container_of(helper, struct vbox_fbdev, helper); + struct drm_device *dev = fbdev->helper.dev; + struct DRM_MODE_FB_CMD mode_cmd; + struct drm_framebuffer *fb; + struct fb_info *info; + struct device *device = &dev->pdev->dev; + struct drm_gem_object *gobj; + struct vbox_bo *bo; + int size, ret; + u32 pitch; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + pitch = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + mode_cmd.pitches[0] = pitch; + + size = pitch * mode_cmd.height; + + ret = vboxfb_create_object(fbdev, &mode_cmd, &gobj); + if (ret) { + DRM_ERROR("failed to create fbcon backing object %d\n", ret); + return ret; + } + + ret = vbox_framebuffer_init(dev, &fbdev->afb, &mode_cmd, gobj); + if (ret) + return ret; + + bo = gem_to_vbox_bo(gobj); + + ret = vbox_bo_reserve(bo, false); + if (ret) + return ret; + + ret = vbox_bo_pin(bo, TTM_PL_FLAG_VRAM, NULL); + if (ret) { + vbox_bo_unreserve(bo); + return ret; + } + + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); + vbox_bo_unreserve(bo); + if (ret) { + DRM_ERROR("failed to kmap fbcon\n"); + return ret; + } + + info = framebuffer_alloc(0, device); + if (!info) + return -ENOMEM; + info->par = fbdev; + + fbdev->size = size; + + fb = &fbdev->afb.base; + fbdev->helper.fb = fb; + fbdev->helper.fbdev = info; + + strcpy(info->fix.id, "vboxdrmfb"); + + /* + * The last flag forces a mode set on VT switches even if the kernel + * does not think it is needed. + */ + info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT | + FBINFO_MISC_ALWAYS_SETPAR; + info->fbops = &vboxfb_ops; + + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret) + return -ENOMEM; + + /* + * This seems to be done for safety checking that the framebuffer + * is not registered twice by different drivers. + */ + info->apertures = alloc_apertures(1); + if (!info->apertures) + return -ENOMEM; + info->apertures->ranges[0].base = pci_resource_start(dev->pdev, 0); + info->apertures->ranges[0].size = pci_resource_len(dev->pdev, 0); + + drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); + drm_fb_helper_fill_var(info, &fbdev->helper, sizes->fb_width, + sizes->fb_height); + + info->screen_base = bo->kmap.virtual; + info->screen_size = size; + +#ifdef CONFIG_FB_DEFERRED_IO + info->fbdefio = &vbox_defio; + fb_deferred_io_init(info); +#endif + + info->pixmap.flags = FB_PIXMAP_SYSTEM; + + DRM_DEBUG_KMS("allocated %dx%d\n", fb->width, fb->height); + + return 0; +} + +static void vbox_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, + u16 blue, int regno) +{ +} + +static void vbox_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, + u16 *blue, int regno) +{ + *red = regno; + *green = regno; + *blue = regno; +} + +static struct drm_fb_helper_funcs vbox_fb_helper_funcs = { + .gamma_set = vbox_fb_gamma_set, + .gamma_get = vbox_fb_gamma_get, + .fb_probe = vboxfb_create, +}; + +void vbox_fbdev_fini(struct drm_device *dev) +{ + struct vbox_private *vbox = dev->dev_private; + struct vbox_fbdev *fbdev = vbox->fbdev; + struct vbox_framebuffer *afb = &fbdev->afb; + + drm_fb_helper_unregister_fbi(&fbdev->helper); + + if (afb->obj) { + struct vbox_bo *bo = gem_to_vbox_bo(afb->obj); + + if (!vbox_bo_reserve(bo, false)) { + if (bo->kmap.virtual) + ttm_bo_kunmap(&bo->kmap); + /* + * QXL does this, but is it really needed before + * freeing? + */ + if (bo->pin_count) + vbox_bo_unpin(bo); + vbox_bo_unreserve(bo); + } + drm_gem_object_unreference_unlocked(afb->obj); + afb->obj = NULL; + } + drm_fb_helper_fini(&fbdev->helper); + + drm_framebuffer_unregister_private(&afb->base); + drm_framebuffer_cleanup(&afb->base); +} + +int vbox_fbdev_init(struct drm_device *dev) +{ + struct vbox_private *vbox = dev->dev_private; + struct vbox_fbdev *fbdev; + int ret; + + fbdev = devm_kzalloc(dev->dev, sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) + return -ENOMEM; + + vbox->fbdev = fbdev; + spin_lock_init(&fbdev->dirty_lock); + + drm_fb_helper_prepare(dev, &fbdev->helper, &vbox_fb_helper_funcs); + ret = drm_fb_helper_init(dev, &fbdev->helper, vbox->num_crtcs); + if (ret) + return ret; + + ret = drm_fb_helper_single_add_all_connectors(&fbdev->helper); + if (ret) + goto err_fini; + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + + ret = drm_fb_helper_initial_config(&fbdev->helper, 32); + if (ret) + goto err_fini; + + return 0; + +err_fini: + drm_fb_helper_fini(&fbdev->helper); + return ret; +} + +void vbox_fbdev_set_base(struct vbox_private *vbox, unsigned long gpu_addr) +{ + struct fb_info *fbdev = vbox->fbdev->helper.fbdev; + + fbdev->fix.smem_start = fbdev->apertures->ranges[0].base + gpu_addr; + fbdev->fix.smem_len = vbox->available_vram_size - gpu_addr; +} diff --git a/drivers/staging/vboxvideo/vbox_hgsmi.c b/drivers/staging/vboxvideo/vbox_hgsmi.c new file mode 100644 index 000000000000..822fd31121cb --- /dev/null +++ b/drivers/staging/vboxvideo/vbox_hgsmi.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2017 Oracle 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, 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 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * Authors: Hans de Goede <hdegoede@redhat.com> + */ + +#include "vbox_drv.h" +#include "vboxvideo_vbe.h" +#include "hgsmi_defs.h" + +/* One-at-a-Time Hash from http://www.burtleburtle.net/bob/hash/doobs.html */ +static u32 hgsmi_hash_process(u32 hash, const u8 *data, int size) +{ + while (size--) { + hash += *data++; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + return hash; +} + +static u32 hgsmi_hash_end(u32 hash) +{ + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} + +/* Not really a checksum but that is the naming used in all vbox code */ +static u32 hgsmi_checksum(u32 offset, + const struct hgsmi_buffer_header *header, + const struct hgsmi_buffer_tail *tail) +{ + u32 checksum; + + checksum = hgsmi_hash_process(0, (u8 *)&offset, sizeof(offset)); + checksum = hgsmi_hash_process(checksum, (u8 *)header, sizeof(*header)); + /* 4 -> Do not checksum the checksum itself */ + checksum = hgsmi_hash_process(checksum, (u8 *)tail, 4); + + return hgsmi_hash_end(checksum); +} + +void *hgsmi_buffer_alloc(struct gen_pool *guest_pool, size_t size, + u8 channel, u16 channel_info) +{ + struct hgsmi_buffer_header *h; + struct hgsmi_buffer_tail *t; + size_t total_size; + dma_addr_t offset; + + total_size = size + sizeof(*h) + sizeof(*t); + h = gen_pool_dma_alloc(guest_pool, total_size, &offset); + if (!h) + return NULL; + + t = (struct hgsmi_buffer_tail *)((u8 *)h + sizeof(*h) + size); + + h->flags = HGSMI_BUFFER_HEADER_F_SEQ_SINGLE; + h->data_size = size; + h->channel = channel; + h->channel_info = channel_info; + memset(&h->u.header_data, 0, sizeof(h->u.header_data)); + + t->reserved = 0; + t->checksum = hgsmi_checksum(offset, h, t); + + return (u8 *)h + sizeof(*h); +} + +void hgsmi_buffer_free(struct gen_pool *guest_pool, void *buf) +{ + struct hgsmi_buffer_header *h = + (struct hgsmi_buffer_header *)((u8 *)buf - sizeof(*h)); + size_t total_size = h->data_size + sizeof(*h) + + sizeof(struct hgsmi_buffer_tail); + + gen_pool_free(guest_pool, (unsigned long)h, total_size); +} + +int hgsmi_buffer_submit(struct gen_pool *guest_pool, void *buf) +{ + phys_addr_t offset; + + offset = gen_pool_virt_to_phys(guest_pool, (unsigned long)buf - + sizeof(struct hgsmi_buffer_header)); + outl(offset, VGA_PORT_HGSMI_GUEST); + /* Make the compiler aware that the host has changed memory. */ + mb(); + + return 0; +} diff --git a/drivers/staging/vboxvideo/vbox_irq.c b/drivers/staging/vboxvideo/vbox_irq.c new file mode 100644 index 000000000000..3ca8bec62ac4 --- /dev/null +++ b/drivers/staging/vboxvideo/vbox_irq.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2016-2017 Oracle Corporation + * This file is based on qxl_irq.c + * Copyright 2013 Red Hat 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. + * + * Authors: Dave Airlie + * Alon Levy + * Michael Thayer <michael.thayer@oracle.com, + * Hans de Goede <hdegoede@redhat.com> + */ + +#include <drm/drm_crtc_helper.h> + +#include "vbox_drv.h" +#include "vboxvideo.h" + +static void vbox_clear_irq(void) +{ + outl((u32)~0, VGA_PORT_HGSMI_HOST); +} + +static u32 vbox_get_flags(struct vbox_private *vbox) +{ + return readl(vbox->guest_heap + HOST_FLAGS_OFFSET); +} + +void vbox_report_hotplug(struct vbox_private *vbox) +{ + schedule_work(&vbox->hotplug_work); +} + +irqreturn_t vbox_irq_handler(int irq, void *arg) +{ + struct drm_device *dev = (struct drm_device *)arg; + struct vbox_private *vbox = (struct vbox_private *)dev->dev_private; + u32 host_flags = vbox_get_flags(vbox); + + if (!(host_flags & HGSMIHOSTFLAGS_IRQ)) + return IRQ_NONE; + + /* + * Due to a bug in the initial host implementation of hot-plug irqs, + * the hot-plug and cursor capability flags were never cleared. + * Fortunately we can tell when they would have been set by checking + * that the VSYNC flag is not set. + */ + if (host_flags & + (HGSMIHOSTFLAGS_HOTPLUG | HGSMIHOSTFLAGS_CURSOR_CAPABILITIES) && + !(host_flags & HGSMIHOSTFLAGS_VSYNC)) + vbox_report_hotplug(vbox); + + vbox_clear_irq(); + + return IRQ_HANDLED; +} + +/** + * Check that the position hints provided by the host are suitable for GNOME + * shell (i.e. all screens disjoint and hints for all enabled screens) and if + * not replace them with default ones. Providing valid hints improves the + * chances that we will get a known screen layout for pointer mapping. + */ +static void validate_or_set_position_hints(struct vbox_private *vbox) +{ + struct vbva_modehint *hintsi, *hintsj; + bool valid = true; + u16 currentx = 0; + int i, j; + + for (i = 0; i < vbox->num_crtcs; ++i) { + for (j = 0; j < i; ++j) { + hintsi = &vbox->last_mode_hints[i]; + hintsj = &vbox->last_mode_hints[j]; + + if (hintsi->enabled && hintsj->enabled) { + if (hintsi->dx >= 0xffff || + hintsi->dy >= 0xffff || + hintsj->dx >= 0xffff || + hintsj->dy >= 0xffff || + (hintsi->dx < + hintsj->dx + (hintsj->cx & 0x8fff) && + hintsi->dx + (hintsi->cx & 0x8fff) > + hintsj->dx) || + (hintsi->dy < + hintsj->dy + (hintsj->cy & 0x8fff) && + hintsi->dy + (hintsi->cy & 0x8fff) > + hintsj->dy)) + valid = false; + } + } + } + if (!valid) + for (i = 0; i < vbox->num_crtcs; ++i) { + if (vbox->last_mode_hints[i].enabled) { + vbox->last_mode_hints[i].dx = currentx; + vbox->last_mode_hints[i].dy = 0; + currentx += + vbox->last_mode_hints[i].cx & 0x8fff; + } + } +} + +/** + * Query the host for the most recent video mode hints. + */ +static void vbox_update_mode_hints(struct vbox_private *vbox) +{ + struct drm_device *dev = vbox->dev; + struct drm_connector *connector; + struct vbox_connector *vbox_conn; + struct vbva_modehint *hints; + u16 flags; + bool disconnected; + unsigned int crtc_id; + int ret; + + ret = hgsmi_get_mode_hints(vbox->guest_pool, vbox->num_crtcs, + vbox->last_mode_hints); + if (ret) { + DRM_ERROR("vboxvideo: hgsmi_get_mode_hints failed: %d\n", ret); + return; + } + + validate_or_set_position_hints(vbox); + drm_modeset_lock_all(dev); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + vbox_conn = to_vbox_connector(connector); + + hints = &vbox->last_mode_hints[vbox_conn->vbox_crtc->crtc_id]; + if (hints->magic != VBVAMODEHINT_MAGIC) + continue; + + disconnected = !(hints->enabled); + crtc_id = vbox_conn->vbox_crtc->crtc_id; + vbox_conn->mode_hint.width = hints->cx & 0x8fff; + vbox_conn->mode_hint.height = hints->cy & 0x8fff; + vbox_conn->vbox_crtc->x_hint = hints->dx; + vbox_conn->vbox_crtc->y_hint = hints->dy; + vbox_conn->mode_hint.disconnected = disconnected; + + if (vbox_conn->vbox_crtc->disconnected == disconnected) + continue; + + if (disconnected) + flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_DISABLED; + else + flags = VBVA_SCREEN_F_ACTIVE | VBVA_SCREEN_F_BLANK; + + hgsmi_process_display_info(vbox->guest_pool, crtc_id, 0, 0, 0, + hints->cx * 4, hints->cx, + hints->cy, 0, flags); + + vbox_conn->vbox_crtc->disconnected = disconnected; + } + drm_modeset_unlock_all(dev); +} + +static void vbox_hotplug_worker(struct work_struct *work) +{ + struct vbox_private *vbox = container_of(work, struct vbox_private, + hotplug_work); + + vbox_update_mode_hints(vbox); + drm_kms_helper_hotplug_event(vbox->dev); +} + +int vbox_irq_init(struct vbox_private *vbox) +{ + INIT_WORK(&vbox->hotplug_work, vbox_hotplug_worker); + vbox_update_mode_hints(vbox); + + return drm_irq_install(vbox->dev, vbox->dev->pdev->irq); +} + +void vbox_irq_fini(struct vbox_private *vbox) +{ + drm_irq_uninstall(vbox->dev); + flush_work(&vbox->hotplug_work); +} diff --git a/drivers/staging/vboxvideo/vbox_main.c b/drivers/staging/vboxvideo/vbox_main.c new file mode 100644 index 000000000000..d0c6ec75a3c7 --- /dev/null +++ b/drivers/staging/vboxvideo/vbox_main.c @@ -0,0 +1,534 @@ +/* + * Copyright (C) 2013-2017 Oracle Corporation + * This file is based on ast_main.c + * Copyright 2012 Red Hat 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, 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 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * Authors: Dave Airlie <airlied@redhat.com>, + * Michael Thayer <michael.thayer@oracle.com, + * Hans de Goede <hdegoede@redhat.com> + */ +#include <drm/drm_fb_helper.h> +#include <drm/drm_crtc_helper.h> + +#include "vbox_drv.h" +#include "vbox_err.h" +#include "vboxvideo_guest.h" +#include "vboxvideo_vbe.h" + +static void vbox_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct vbox_framebuffer *vbox_fb = to_vbox_framebuffer(fb); + + if (vbox_fb->obj) + drm_gem_object_unreference_unlocked(vbox_fb->obj); + + drm_framebuffer_cleanup(fb); + kfree(fb); +} + +void vbox_enable_accel(struct vbox_private *vbox) +{ + unsigned int i; + struct vbva_buffer *vbva; + + if (!vbox->vbva_info || !vbox->vbva_buffers) { + /* Should never happen... */ + DRM_ERROR("vboxvideo: failed to set up VBVA.\n"); + return; + } + + for (i = 0; i < vbox->num_crtcs; ++i) { + if (vbox->vbva_info[i].vbva) + continue; + + vbva = (void *)vbox->vbva_buffers + i * VBVA_MIN_BUFFER_SIZE; + if (!vbva_enable(&vbox->vbva_info[i], + vbox->guest_pool, vbva, i)) { + /* very old host or driver error. */ + DRM_ERROR("vboxvideo: vbva_enable failed\n"); + return; + } + } +} + +void vbox_disable_accel(struct vbox_private *vbox) +{ + unsigned int i; + + for (i = 0; i < vbox->num_crtcs; ++i) + vbva_disable(&vbox->vbva_info[i], vbox->guest_pool, i); +} + +void vbox_report_caps(struct vbox_private *vbox) +{ + u32 caps = VBVACAPS_DISABLE_CURSOR_INTEGRATION | + VBVACAPS_IRQ | VBVACAPS_USE_VBVA_ONLY; + + if (vbox->initial_mode_queried) + caps |= VBVACAPS_VIDEO_MODE_HINTS; + + hgsmi_send_caps_info(vbox->guest_pool, caps); +} + +/** + * Send information about dirty rectangles to VBVA. If necessary we enable + * VBVA first, as this is normally disabled after a change of master in case + * the new master does not send dirty rectangle information (is this even + * allowed?) + */ +void vbox_framebuffer_dirty_rectangles(struct drm_framebuffer *fb, + struct drm_clip_rect *rects, + unsigned int num_rects) +{ + struct vbox_private *vbox = fb->dev->dev_private; + struct drm_crtc *crtc; + unsigned int i; + + mutex_lock(&vbox->hw_mutex); + list_for_each_entry(crtc, &fb->dev->mode_config.crtc_list, head) { + if (CRTC_FB(crtc) != fb) + continue; + + vbox_enable_accel(vbox); + + for (i = 0; i < num_rects; ++i) { + struct vbva_cmd_hdr cmd_hdr; + unsigned int crtc_id = to_vbox_crtc(crtc)->crtc_id; + + if ((rects[i].x1 > crtc->x + crtc->hwmode.hdisplay) || + (rects[i].y1 > crtc->y + crtc->hwmode.vdisplay) || + (rects[i].x2 < crtc->x) || + (rects[i].y2 < crtc->y)) + continue; + + cmd_hdr.x = (s16)rects[i].x1; + cmd_hdr.y = (s16)rects[i].y1; + cmd_hdr.w = (u16)rects[i].x2 - rects[i].x1; + cmd_hdr.h = (u16)rects[i].y2 - rects[i].y1; + + if (!vbva_buffer_begin_update(&vbox->vbva_info[crtc_id], + vbox->guest_pool)) + continue; + + vbva_write(&vbox->vbva_info[crtc_id], vbox->guest_pool, + &cmd_hdr, sizeof(cmd_hdr)); + vbva_buffer_end_update(&vbox->vbva_info[crtc_id]); + } + } + mutex_unlock(&vbox->hw_mutex); +} + +static int vbox_user_framebuffer_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int flags, unsigned int color, + struct drm_clip_rect *rects, + unsigned int num_rects) +{ + vbox_framebuffer_dirty_rectangles(fb, rects, num_rects); + + return 0; +} + +static const struct drm_framebuffer_funcs vbox_fb_funcs = { + .destroy = vbox_user_framebuffer_destroy, + .dirty = vbox_user_framebuffer_dirty, +}; + +int vbox_framebuffer_init(struct drm_device *dev, + struct vbox_framebuffer *vbox_fb, + const struct DRM_MODE_FB_CMD *mode_cmd, + struct drm_gem_object *obj) +{ + int ret; + + drm_helper_mode_fill_fb_struct(dev, &vbox_fb->base, mode_cmd); + vbox_fb->obj = obj; + ret = drm_framebuffer_init(dev, &vbox_fb->base, &vbox_fb_funcs); + if (ret) { + DRM_ERROR("framebuffer init failed %d\n", ret); + return ret; + } + + return 0; +} + +static struct drm_framebuffer *vbox_user_framebuffer_create( + struct drm_device *dev, + struct drm_file *filp, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *obj; + struct vbox_framebuffer *vbox_fb; + int ret = -ENOMEM; + + obj = drm_gem_object_lookup(filp, mode_cmd->handles[0]); + if (!obj) + return ERR_PTR(-ENOENT); + + vbox_fb = kzalloc(sizeof(*vbox_fb), GFP_KERNEL); + if (!vbox_fb) + goto err_unref_obj; + + ret = vbox_framebuffer_init(dev, vbox_fb, mode_cmd, obj); + if (ret) + goto err_free_vbox_fb; + + return &vbox_fb->base; + +err_free_vbox_fb: + kfree(vbox_fb); +err_unref_obj: + drm_gem_object_unreference_unlocked(obj); + return ERR_PTR(ret); +} + +static const struct drm_mode_config_funcs vbox_mode_funcs = { + .fb_create = vbox_user_framebuffer_create, +}; + +static int vbox_accel_init(struct vbox_private *vbox) +{ + unsigned int i; + + vbox->vbva_info = devm_kcalloc(vbox->dev->dev, vbox->num_crtcs, + sizeof(*vbox->vbva_info), GFP_KERNEL); + if (!vbox->vbva_info) + return -ENOMEM; + + /* Take a command buffer for each screen from the end of usable VRAM. */ + vbox->available_vram_size -= vbox->num_crtcs * VBVA_MIN_BUFFER_SIZE; + + vbox->vbva_buffers = pci_iomap_range(vbox->dev->pdev, 0, + vbox->available_vram_size, + vbox->num_crtcs * + VBVA_MIN_BUFFER_SIZE); + if (!vbox->vbva_buffers) + return -ENOMEM; + + for (i = 0; i < vbox->num_crtcs; ++i) + vbva_setup_buffer_context(&vbox->vbva_info[i], + vbox->available_vram_size + + i * VBVA_MIN_BUFFER_SIZE, + VBVA_MIN_BUFFER_SIZE); + + return 0; +} + +static void vbox_accel_fini(struct vbox_private *vbox) +{ + vbox_disable_accel(vbox); + pci_iounmap(vbox->dev->pdev, vbox->vbva_buffers); +} + +/** Do we support the 4.3 plus mode hint reporting interface? */ +static bool have_hgsmi_mode_hints(struct vbox_private *vbox) +{ + u32 have_hints, have_cursor; + int ret; + + ret = hgsmi_query_conf(vbox->guest_pool, + VBOX_VBVA_CONF32_MODE_HINT_REPORTING, + &have_hints); + if (ret) + return false; + + ret = hgsmi_query_conf(vbox->guest_pool, + VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING, + &have_cursor); + if (ret) + return false; + + return have_hints == VINF_SUCCESS && have_cursor == VINF_SUCCESS; +} + +static bool vbox_check_supported(u16 id) +{ + u16 dispi_id; + + vbox_write_ioport(VBE_DISPI_INDEX_ID, id); + dispi_id = inw(VBE_DISPI_IOPORT_DATA); + + return dispi_id == id; +} + +/** + * Set up our heaps and data exchange buffers in VRAM before handing the rest + * to the memory manager. + */ +static int vbox_hw_init(struct vbox_private *vbox) +{ + int ret = -ENOMEM; + + vbox->full_vram_size = inl(VBE_DISPI_IOPORT_DATA); + vbox->any_pitch = vbox_check_supported(VBE_DISPI_ID_ANYX); + + DRM_INFO("VRAM %08x\n", vbox->full_vram_size); + + /* Map guest-heap at end of vram */ + vbox->guest_heap = + pci_iomap_range(vbox->dev->pdev, 0, GUEST_HEAP_OFFSET(vbox), + GUEST_HEAP_SIZE); + if (!vbox->guest_heap) + return -ENOMEM; + + /* Create guest-heap mem-pool use 2^4 = 16 byte chunks */ + vbox->guest_pool = gen_pool_create(4, -1); + if (!vbox->guest_pool) + goto err_unmap_guest_heap; + + ret = gen_pool_add_virt(vbox->guest_pool, + (unsigned long)vbox->guest_heap, + GUEST_HEAP_OFFSET(vbox), + GUEST_HEAP_USABLE_SIZE, -1); + if (ret) + goto err_destroy_guest_pool; + + ret = hgsmi_test_query_conf(vbox->guest_pool); + if (ret) { + DRM_ERROR("vboxvideo: hgsmi_test_query_conf failed\n"); + goto err_destroy_guest_pool; + } + + /* Reduce available VRAM size to reflect the guest heap. */ + vbox->available_vram_size = GUEST_HEAP_OFFSET(vbox); + /* Linux drm represents monitors as a 32-bit array. */ + hgsmi_query_conf(vbox->guest_pool, VBOX_VBVA_CONF32_MONITOR_COUNT, + &vbox->num_crtcs); + vbox->num_crtcs = clamp_t(u32, vbox->num_crtcs, 1, VBOX_MAX_SCREENS); + + if (!have_hgsmi_mode_hints(vbox)) { + ret = -ENOTSUPP; + goto err_destroy_guest_pool; + } + + vbox->last_mode_hints = devm_kcalloc(vbox->dev->dev, vbox->num_crtcs, + sizeof(struct vbva_modehint), + GFP_KERNEL); + if (!vbox->last_mode_hints) { + ret = -ENOMEM; + goto err_destroy_guest_pool; + } + + ret = vbox_accel_init(vbox); + if (ret) + goto err_destroy_guest_pool; + + return 0; + +err_destroy_guest_pool: + gen_pool_destroy(vbox->guest_pool); +err_unmap_guest_heap: + pci_iounmap(vbox->dev->pdev, vbox->guest_heap); + return ret; +} + +static void vbox_hw_fini(struct vbox_private *vbox) +{ + vbox_accel_fini(vbox); + gen_pool_destroy(vbox->guest_pool); + pci_iounmap(vbox->dev->pdev, vbox->guest_heap); +} + +int vbox_driver_load(struct drm_device *dev, unsigned long flags) +{ + struct vbox_private *vbox; + int ret = 0; + + if (!vbox_check_supported(VBE_DISPI_ID_HGSMI)) + return -ENODEV; + + vbox = devm_kzalloc(dev->dev, sizeof(*vbox), GFP_KERNEL); + if (!vbox) + return -ENOMEM; + + dev->dev_private = vbox; + vbox->dev = dev; + + mutex_init(&vbox->hw_mutex); + + ret = vbox_hw_init(vbox); + if (ret) + return ret; + + ret = vbox_mm_init(vbox); + if (ret) + goto err_hw_fini; + + drm_mode_config_init(dev); + + dev->mode_config.funcs = (void *)&vbox_mode_funcs; + dev->mode_config.min_width = 64; + dev->mode_config.min_height = 64; + dev->mode_config.preferred_depth = 24; + dev->mode_config.max_width = VBE_DISPI_MAX_XRES; + dev->mode_config.max_height = VBE_DISPI_MAX_YRES; + + ret = vbox_mode_init(dev); + if (ret) + goto err_drm_mode_cleanup; + + ret = vbox_irq_init(vbox); + if (ret) + goto err_mode_fini; + + ret = vbox_fbdev_init(dev); + if (ret) + goto err_irq_fini; + + return 0; + +err_irq_fini: + vbox_irq_fini(vbox); +err_mode_fini: + vbox_mode_fini(dev); +err_drm_mode_cleanup: + drm_mode_config_cleanup(dev); + vbox_mm_fini(vbox); +err_hw_fini: + vbox_hw_fini(vbox); + return ret; +} + +void vbox_driver_unload(struct drm_device *dev) +{ + struct vbox_private *vbox = dev->dev_private; + + vbox_fbdev_fini(dev); + vbox_irq_fini(vbox); + vbox_mode_fini(dev); + drm_mode_config_cleanup(dev); + vbox_mm_fini(vbox); + vbox_hw_fini(vbox); +} + +/** + * @note this is described in the DRM framework documentation. AST does not + * have it, but we get an oops on driver unload if it is not present. + */ +void vbox_driver_lastclose(struct drm_device *dev) +{ + struct vbox_private *vbox = dev->dev_private; + + if (vbox->fbdev) + drm_fb_helper_restore_fbdev_mode_unlocked(&vbox->fbdev->helper); +} + +int vbox_gem_create(struct drm_device *dev, + u32 size, bool iskernel, struct drm_gem_object **obj) +{ + struct vbox_bo *vboxbo; + int ret; + + *obj = NULL; + + size = roundup(size, PAGE_SIZE); + if (size == 0) + return -EINVAL; + + ret = vbox_bo_create(dev, size, 0, 0, &vboxbo); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("failed to allocate GEM object\n"); + return ret; + } + + *obj = &vboxbo->gem; + + return 0; +} + +int vbox_dumb_create(struct drm_file *file, + struct drm_device *dev, struct drm_mode_create_dumb *args) +{ + int ret; + struct drm_gem_object *gobj; + u32 handle; + + args->pitch = args->width * ((args->bpp + 7) / 8); + args->size = args->pitch * args->height; + + ret = vbox_gem_create(dev, args->size, false, &gobj); + if (ret) + return ret; + + ret = drm_gem_handle_create(file, gobj, &handle); + drm_gem_object_unreference_unlocked(gobj); + if (ret) + return ret; + + args->handle = handle; + + return 0; +} + +static void vbox_bo_unref(struct vbox_bo **bo) +{ + struct ttm_buffer_object *tbo; + + if ((*bo) == NULL) + return; + + tbo = &((*bo)->bo); + ttm_bo_unref(&tbo); + if (!tbo) + *bo = NULL; +} + +void vbox_gem_free_object(struct drm_gem_object *obj) +{ + struct vbox_bo *vbox_bo = gem_to_vbox_bo(obj); + + vbox_bo_unref(&vbox_bo); +} + +static inline u64 vbox_bo_mmap_offset(struct vbox_bo *bo) +{ + return drm_vma_node_offset_addr(&bo->bo.vma_node); +} + +int +vbox_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + u32 handle, u64 *offset) +{ + struct drm_gem_object *obj; + int ret; + struct vbox_bo *bo; + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(file, handle); + if (!obj) { + ret = -ENOENT; + goto out_unlock; + } + + bo = gem_to_vbox_bo(obj); + *offset = vbox_bo_mmap_offset(bo); + + drm_gem_object_unreference(obj); + ret = 0; + +out_unlock: + mutex_unlock(&dev->struct_mutex); + return ret; +} diff --git a/drivers/staging/vboxvideo/vbox_mode.c b/drivers/staging/vboxvideo/vbox_mode.c new file mode 100644 index 000000000000..f2b85f3256fa --- /dev/null +++ b/drivers/staging/vboxvideo/vbox_mode.c @@ -0,0 +1,877 @@ +/* + * Copyright (C) 2013-2017 Oracle Corporation + * This file is based on ast_mode.c + * Copyright 2012 Red Hat Inc. + * Parts based on xf86-video-ast + * Copyright (c) 2005 ASPEED Technology 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, 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 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + * Michael Thayer <michael.thayer@oracle.com, + * Hans de Goede <hdegoede@redhat.com> + */ +#include <linux/export.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> + +#include "vbox_drv.h" +#include "vboxvideo.h" +#include "hgsmi_channels.h" + +static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, + u32 handle, u32 width, u32 height, + s32 hot_x, s32 hot_y); +static int vbox_cursor_move(struct drm_crtc *crtc, int x, int y); + +/** + * Set a graphics mode. Poke any required values into registers, do an HGSMI + * mode set and tell the host we support advanced graphics functions. + */ +static void vbox_do_modeset(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); + struct vbox_private *vbox; + int width, height, bpp, pitch; + unsigned int crtc_id; + u16 flags; + s32 x_offset, y_offset; + + vbox = crtc->dev->dev_private; + width = mode->hdisplay ? mode->hdisplay : 640; + height = mode->vdisplay ? mode->vdisplay : 480; + crtc_id = vbox_crtc->crtc_id; + bpp = crtc->enabled ? CRTC_FB(crtc)->format->cpp[0] * 8 : 32; + pitch = crtc->enabled ? CRTC_FB(crtc)->pitches[0] : width * bpp / 8; + x_offset = vbox->single_framebuffer ? crtc->x : vbox_crtc->x_hint; + y_offset = vbox->single_framebuffer ? crtc->y : vbox_crtc->y_hint; + + /* + * This is the old way of setting graphics modes. It assumed one screen + * and a frame-buffer at the start of video RAM. On older versions of + * VirtualBox, certain parts of the code still assume that the first + * screen is programmed this way, so try to fake it. + */ + if (vbox_crtc->crtc_id == 0 && crtc->enabled && + vbox_crtc->fb_offset / pitch < 0xffff - crtc->y && + vbox_crtc->fb_offset % (bpp / 8) == 0) { + vbox_write_ioport(VBE_DISPI_INDEX_XRES, width); + vbox_write_ioport(VBE_DISPI_INDEX_YRES, height); + vbox_write_ioport(VBE_DISPI_INDEX_VIRT_WIDTH, pitch * 8 / bpp); + vbox_write_ioport(VBE_DISPI_INDEX_BPP, + CRTC_FB(crtc)->format->cpp[0] * 8); + vbox_write_ioport(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED); + vbox_write_ioport( + VBE_DISPI_INDEX_X_OFFSET, + vbox_crtc->fb_offset % pitch / bpp * 8 + crtc->x); + vbox_write_ioport(VBE_DISPI_INDEX_Y_OFFSET, + vbox_crtc->fb_offset / pitch + crtc->y); + } + + flags = VBVA_SCREEN_F_ACTIVE; + flags |= (crtc->enabled && !vbox_crtc->blanked) ? + 0 : VBVA_SCREEN_F_BLANK; + flags |= vbox_crtc->disconnected ? VBVA_SCREEN_F_DISABLED : 0; + hgsmi_process_display_info(vbox->guest_pool, vbox_crtc->crtc_id, + x_offset, y_offset, + crtc->x * bpp / 8 + crtc->y * pitch, + pitch, width, height, + vbox_crtc->blanked ? 0 : bpp, flags); +} + +static int vbox_set_view(struct drm_crtc *crtc) +{ + struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); + struct vbox_private *vbox = crtc->dev->dev_private; + struct vbva_infoview *p; + + /* + * Tell the host about the view. This design originally targeted the + * Windows XP driver architecture and assumed that each screen would + * have a dedicated frame buffer with the command buffer following it, + * the whole being a "view". The host works out which screen a command + * buffer belongs to by checking whether it is in the first view, then + * whether it is in the second and so on. The first match wins. We + * cheat around this by making the first view be the managed memory + * plus the first command buffer, the second the same plus the second + * buffer and so on. + */ + p = hgsmi_buffer_alloc(vbox->guest_pool, sizeof(*p), + HGSMI_CH_VBVA, VBVA_INFO_VIEW); + if (!p) + return -ENOMEM; + + p->view_index = vbox_crtc->crtc_id; + p->view_offset = vbox_crtc->fb_offset; + p->view_size = vbox->available_vram_size - vbox_crtc->fb_offset + + vbox_crtc->crtc_id * VBVA_MIN_BUFFER_SIZE; + p->max_screen_size = vbox->available_vram_size - vbox_crtc->fb_offset; + + hgsmi_buffer_submit(vbox->guest_pool, p); + hgsmi_buffer_free(vbox->guest_pool, p); + + return 0; +} + +static void vbox_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static void vbox_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); + struct vbox_private *vbox = crtc->dev->dev_private; + + switch (mode) { + case DRM_MODE_DPMS_ON: + vbox_crtc->blanked = false; + break; + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + vbox_crtc->blanked = true; + break; + } + + mutex_lock(&vbox->hw_mutex); + vbox_do_modeset(crtc, &crtc->hwmode); + mutex_unlock(&vbox->hw_mutex); +} + +static bool vbox_crtc_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +/* + * Try to map the layout of virtual screens to the range of the input device. + * Return true if we need to re-set the crtc modes due to screen offset + * changes. + */ +static bool vbox_set_up_input_mapping(struct vbox_private *vbox) +{ + struct drm_crtc *crtci; + struct drm_connector *connectori; + struct drm_framebuffer *fb1 = NULL; + bool single_framebuffer = true; + bool old_single_framebuffer = vbox->single_framebuffer; + u16 width = 0, height = 0; + + /* + * Are we using an X.Org-style single large frame-buffer for all crtcs? + * If so then screen layout can be deduced from the crtc offsets. + * Same fall-back if this is the fbdev frame-buffer. + */ + list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, head) { + if (!fb1) { + fb1 = CRTC_FB(crtci); + if (to_vbox_framebuffer(fb1) == &vbox->fbdev->afb) + break; + } else if (CRTC_FB(crtci) && fb1 != CRTC_FB(crtci)) { + single_framebuffer = false; + } + } + if (single_framebuffer) { + list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, + head) { + if (to_vbox_crtc(crtci)->crtc_id != 0) + continue; + + vbox->single_framebuffer = true; + vbox->input_mapping_width = CRTC_FB(crtci)->width; + vbox->input_mapping_height = CRTC_FB(crtci)->height; + return old_single_framebuffer != + vbox->single_framebuffer; + } + } + /* Otherwise calculate the total span of all screens. */ + list_for_each_entry(connectori, &vbox->dev->mode_config.connector_list, + head) { + struct vbox_connector *vbox_connector = + to_vbox_connector(connectori); + struct vbox_crtc *vbox_crtc = vbox_connector->vbox_crtc; + + width = max_t(u16, width, vbox_crtc->x_hint + + vbox_connector->mode_hint.width); + height = max_t(u16, height, vbox_crtc->y_hint + + vbox_connector->mode_hint.height); + } + + vbox->single_framebuffer = false; + vbox->input_mapping_width = width; + vbox->input_mapping_height = height; + + return old_single_framebuffer != vbox->single_framebuffer; +} + +static int vbox_crtc_do_set_base(struct drm_crtc *crtc, + struct drm_framebuffer *old_fb, int x, int y) +{ + struct vbox_private *vbox = crtc->dev->dev_private; + struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); + struct drm_gem_object *obj; + struct vbox_framebuffer *vbox_fb; + struct vbox_bo *bo; + int ret; + u64 gpu_addr; + + /* Unpin the previous fb. */ + if (old_fb) { + vbox_fb = to_vbox_framebuffer(old_fb); + obj = vbox_fb->obj; + bo = gem_to_vbox_bo(obj); + ret = vbox_bo_reserve(bo, false); + if (ret) + return ret; + + vbox_bo_unpin(bo); + vbox_bo_unreserve(bo); + } + + vbox_fb = to_vbox_framebuffer(CRTC_FB(crtc)); + obj = vbox_fb->obj; + bo = gem_to_vbox_bo(obj); + + ret = vbox_bo_reserve(bo, false); + if (ret) + return ret; + + ret = vbox_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr); + if (ret) { + vbox_bo_unreserve(bo); + return ret; + } + + if (&vbox->fbdev->afb == vbox_fb) + vbox_fbdev_set_base(vbox, gpu_addr); + vbox_bo_unreserve(bo); + + /* vbox_set_start_address_crt1(crtc, (u32)gpu_addr); */ + vbox_crtc->fb_offset = gpu_addr; + if (vbox_set_up_input_mapping(vbox)) { + struct drm_crtc *crtci; + + list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, + head) { + vbox_set_view(crtc); + vbox_do_modeset(crtci, &crtci->mode); + } + } + + return 0; +} + +static int vbox_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + return vbox_crtc_do_set_base(crtc, old_fb, x, y); +} + +static int vbox_crtc_mode_set(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct vbox_private *vbox = crtc->dev->dev_private; + int ret; + + vbox_crtc_mode_set_base(crtc, x, y, old_fb); + + mutex_lock(&vbox->hw_mutex); + ret = vbox_set_view(crtc); + if (!ret) + vbox_do_modeset(crtc, mode); + hgsmi_update_input_mapping(vbox->guest_pool, 0, 0, + vbox->input_mapping_width, + vbox->input_mapping_height); + mutex_unlock(&vbox->hw_mutex); + + return ret; +} + +static void vbox_crtc_disable(struct drm_crtc *crtc) +{ +} + +static void vbox_crtc_prepare(struct drm_crtc *crtc) +{ +} + +static void vbox_crtc_commit(struct drm_crtc *crtc) +{ +} + +static const struct drm_crtc_helper_funcs vbox_crtc_helper_funcs = { + .dpms = vbox_crtc_dpms, + .mode_fixup = vbox_crtc_mode_fixup, + .mode_set = vbox_crtc_mode_set, + /* .mode_set_base = vbox_crtc_mode_set_base, */ + .disable = vbox_crtc_disable, + .load_lut = vbox_crtc_load_lut, + .prepare = vbox_crtc_prepare, + .commit = vbox_crtc_commit, +}; + +static void vbox_crtc_reset(struct drm_crtc *crtc) +{ +} + +static void vbox_crtc_destroy(struct drm_crtc *crtc) +{ + drm_crtc_cleanup(crtc); + kfree(crtc); +} + +static const struct drm_crtc_funcs vbox_crtc_funcs = { + .cursor_move = vbox_cursor_move, + .cursor_set2 = vbox_cursor_set2, + .reset = vbox_crtc_reset, + .set_config = drm_crtc_helper_set_config, + /* .gamma_set = vbox_crtc_gamma_set, */ + .destroy = vbox_crtc_destroy, +}; + +static struct vbox_crtc *vbox_crtc_init(struct drm_device *dev, unsigned int i) +{ + struct vbox_crtc *vbox_crtc; + + vbox_crtc = kzalloc(sizeof(*vbox_crtc), GFP_KERNEL); + if (!vbox_crtc) + return NULL; + + vbox_crtc->crtc_id = i; + + drm_crtc_init(dev, &vbox_crtc->base, &vbox_crtc_funcs); + drm_mode_crtc_set_gamma_size(&vbox_crtc->base, 256); + drm_crtc_helper_add(&vbox_crtc->base, &vbox_crtc_helper_funcs); + + return vbox_crtc; +} + +static void vbox_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + +static struct drm_encoder *vbox_best_single_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 NULL; +} + +static const struct drm_encoder_funcs vbox_enc_funcs = { + .destroy = vbox_encoder_destroy, +}; + +static void vbox_encoder_dpms(struct drm_encoder *encoder, int mode) +{ +} + +static bool vbox_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void vbox_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ +} + +static void vbox_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void vbox_encoder_commit(struct drm_encoder *encoder) +{ +} + +static const struct drm_encoder_helper_funcs vbox_enc_helper_funcs = { + .dpms = vbox_encoder_dpms, + .mode_fixup = vbox_mode_fixup, + .prepare = vbox_encoder_prepare, + .commit = vbox_encoder_commit, + .mode_set = vbox_encoder_mode_set, +}; + +static struct drm_encoder *vbox_encoder_init(struct drm_device *dev, + unsigned int i) +{ + struct vbox_encoder *vbox_encoder; + + vbox_encoder = kzalloc(sizeof(*vbox_encoder), GFP_KERNEL); + if (!vbox_encoder) + return NULL; + + drm_encoder_init(dev, &vbox_encoder->base, &vbox_enc_funcs, + DRM_MODE_ENCODER_DAC, NULL); + drm_encoder_helper_add(&vbox_encoder->base, &vbox_enc_helper_funcs); + + vbox_encoder->base.possible_crtcs = 1 << i; + return &vbox_encoder->base; +} + +/** + * Generate EDID data with a mode-unique serial number for the virtual + * monitor to try to persuade Unity that different modes correspond to + * different monitors and it should not try to force the same resolution on + * them. + */ +static void vbox_set_edid(struct drm_connector *connector, int width, + int height) +{ + enum { EDID_SIZE = 128 }; + unsigned char edid[EDID_SIZE] = { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, /* header */ + 0x58, 0x58, /* manufacturer (VBX) */ + 0x00, 0x00, /* product code */ + 0x00, 0x00, 0x00, 0x00, /* serial number goes here */ + 0x01, /* week of manufacture */ + 0x00, /* year of manufacture */ + 0x01, 0x03, /* EDID version */ + 0x80, /* capabilities - digital */ + 0x00, /* horiz. res in cm, zero for projectors */ + 0x00, /* vert. res in cm */ + 0x78, /* display gamma (120 == 2.2). */ + 0xEE, /* features (standby, suspend, off, RGB, std */ + /* colour space, preferred timing mode) */ + 0xEE, 0x91, 0xA3, 0x54, 0x4C, 0x99, 0x26, 0x0F, 0x50, 0x54, + /* chromaticity for standard colour space. */ + 0x00, 0x00, 0x00, /* no default timings */ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, /* no standard timings */ + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x02, 0x02, + 0x02, 0x02, + /* descriptor block 1 goes below */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* descriptor block 2, monitor ranges */ + 0x00, 0x00, 0x00, 0xFD, 0x00, + 0x00, 0xC8, 0x00, 0xC8, 0x64, 0x00, 0x0A, 0x20, 0x20, 0x20, + 0x20, 0x20, + /* 0-200Hz vertical, 0-200KHz horizontal, 1000MHz pixel clock */ + 0x20, + /* descriptor block 3, monitor name */ + 0x00, 0x00, 0x00, 0xFC, 0x00, + 'V', 'B', 'O', 'X', ' ', 'm', 'o', 'n', 'i', 't', 'o', 'r', + '\n', + /* descriptor block 4: dummy data */ + 0x00, 0x00, 0x00, 0x10, 0x00, + 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, + 0x00, /* number of extensions */ + 0x00 /* checksum goes here */ + }; + int clock = (width + 6) * (height + 6) * 60 / 10000; + unsigned int i, sum = 0; + + edid[12] = width & 0xff; + edid[13] = width >> 8; + edid[14] = height & 0xff; + edid[15] = height >> 8; + edid[54] = clock & 0xff; + edid[55] = clock >> 8; + edid[56] = width & 0xff; + edid[58] = (width >> 4) & 0xf0; + edid[59] = height & 0xff; + edid[61] = (height >> 4) & 0xf0; + for (i = 0; i < EDID_SIZE - 1; ++i) + sum += edid[i]; + edid[EDID_SIZE - 1] = (0x100 - (sum & 0xFF)) & 0xFF; + drm_mode_connector_update_edid_property(connector, (struct edid *)edid); +} + +static int vbox_get_modes(struct drm_connector *connector) +{ + struct vbox_connector *vbox_connector = NULL; + struct drm_display_mode *mode = NULL; + struct vbox_private *vbox = NULL; + unsigned int num_modes = 0; + int preferred_width, preferred_height; + + vbox_connector = to_vbox_connector(connector); + vbox = connector->dev->dev_private; + /* + * Heuristic: we do not want to tell the host that we support dynamic + * resizing unless we feel confident that the user space client using + * the video driver can handle hot-plug events. So the first time modes + * are queried after a "master" switch we tell the host that we do not, + * and immediately after we send the client a hot-plug notification as + * a test to see if they will respond and query again. + * That is also the reason why capabilities are reported to the host at + * this place in the code rather than elsewhere. + * We need to report the flags location before reporting the IRQ + * capability. + */ + hgsmi_report_flags_location(vbox->guest_pool, GUEST_HEAP_OFFSET(vbox) + + HOST_FLAGS_OFFSET); + if (vbox_connector->vbox_crtc->crtc_id == 0) + vbox_report_caps(vbox); + if (!vbox->initial_mode_queried) { + if (vbox_connector->vbox_crtc->crtc_id == 0) { + vbox->initial_mode_queried = true; + vbox_report_hotplug(vbox); + } + return drm_add_modes_noedid(connector, 800, 600); + } + num_modes = drm_add_modes_noedid(connector, 2560, 1600); + preferred_width = vbox_connector->mode_hint.width ? + vbox_connector->mode_hint.width : 1024; + preferred_height = vbox_connector->mode_hint.height ? + vbox_connector->mode_hint.height : 768; + mode = drm_cvt_mode(connector->dev, preferred_width, preferred_height, + 60, false, false, false); + if (mode) { + mode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + ++num_modes; + } + vbox_set_edid(connector, preferred_width, preferred_height); + drm_object_property_set_value( + &connector->base, vbox->dev->mode_config.suggested_x_property, + vbox_connector->vbox_crtc->x_hint); + drm_object_property_set_value( + &connector->base, vbox->dev->mode_config.suggested_y_property, + vbox_connector->vbox_crtc->y_hint); + + return num_modes; +} + +static int vbox_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static void vbox_connector_destroy(struct drm_connector *connector) +{ + struct vbox_connector *vbox_connector; + + vbox_connector = to_vbox_connector(connector); + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +static enum drm_connector_status +vbox_connector_detect(struct drm_connector *connector, bool force) +{ + struct vbox_connector *vbox_connector; + + vbox_connector = to_vbox_connector(connector); + + return vbox_connector->mode_hint.disconnected ? + connector_status_disconnected : connector_status_connected; +} + +static int vbox_fill_modes(struct drm_connector *connector, u32 max_x, + u32 max_y) +{ + struct vbox_connector *vbox_connector; + struct drm_device *dev; + struct drm_display_mode *mode, *iterator; + + vbox_connector = to_vbox_connector(connector); + dev = vbox_connector->base.dev; + list_for_each_entry_safe(mode, iterator, &connector->modes, head) { + list_del(&mode->head); + drm_mode_destroy(dev, mode); + } + + return drm_helper_probe_single_connector_modes(connector, max_x, max_y); +} + +static const struct drm_connector_helper_funcs vbox_connector_helper_funcs = { + .mode_valid = vbox_mode_valid, + .get_modes = vbox_get_modes, + .best_encoder = vbox_best_single_encoder, +}; + +static const struct drm_connector_funcs vbox_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = vbox_connector_detect, + .fill_modes = vbox_fill_modes, + .destroy = vbox_connector_destroy, +}; + +static int vbox_connector_init(struct drm_device *dev, + struct vbox_crtc *vbox_crtc, + struct drm_encoder *encoder) +{ + struct vbox_connector *vbox_connector; + struct drm_connector *connector; + + vbox_connector = kzalloc(sizeof(*vbox_connector), GFP_KERNEL); + if (!vbox_connector) + return -ENOMEM; + + connector = &vbox_connector->base; + vbox_connector->vbox_crtc = vbox_crtc; + + drm_connector_init(dev, connector, &vbox_connector_funcs, + DRM_MODE_CONNECTOR_VGA); + drm_connector_helper_add(connector, &vbox_connector_helper_funcs); + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_mode_create_suggested_offset_properties(dev); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_x_property, -1); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_y_property, -1); + drm_connector_register(connector); + + drm_mode_connector_attach_encoder(connector, encoder); + + return 0; +} + +int vbox_mode_init(struct drm_device *dev) +{ + struct vbox_private *vbox = dev->dev_private; + struct drm_encoder *encoder; + struct vbox_crtc *vbox_crtc; + unsigned int i; + int ret; + + /* vbox_cursor_init(dev); */ + for (i = 0; i < vbox->num_crtcs; ++i) { + vbox_crtc = vbox_crtc_init(dev, i); + if (!vbox_crtc) + return -ENOMEM; + encoder = vbox_encoder_init(dev, i); + if (!encoder) + return -ENOMEM; + ret = vbox_connector_init(dev, vbox_crtc, encoder); + if (ret) + return ret; + } + + return 0; +} + +void vbox_mode_fini(struct drm_device *dev) +{ + /* vbox_cursor_fini(dev); */ +} + +/** + * Copy the ARGB image and generate the mask, which is needed in case the host + * does not support ARGB cursors. The mask is a 1BPP bitmap with the bit set + * if the corresponding alpha value in the ARGB image is greater than 0xF0. + */ +static void copy_cursor_image(u8 *src, u8 *dst, u32 width, u32 height, + size_t mask_size) +{ + size_t line_size = (width + 7) / 8; + u32 i, j; + + memcpy(dst + mask_size, src, width * height * 4); + for (i = 0; i < height; ++i) + for (j = 0; j < width; ++j) + if (((u32 *)src)[i * width + j] > 0xf0000000) + dst[i * line_size + j / 8] |= (0x80 >> (j % 8)); +} + +static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, + u32 handle, u32 width, u32 height, + s32 hot_x, s32 hot_y) +{ + struct vbox_private *vbox = crtc->dev->dev_private; + struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc); + struct ttm_bo_kmap_obj uobj_map; + size_t data_size, mask_size; + struct drm_gem_object *obj; + u32 flags, caps = 0; + struct vbox_bo *bo; + bool src_isiomem; + u8 *dst = NULL; + u8 *src; + int ret; + + /* + * Re-set this regularly as in 5.0.20 and earlier the information was + * lost on save and restore. + */ + hgsmi_update_input_mapping(vbox->guest_pool, 0, 0, + vbox->input_mapping_width, + vbox->input_mapping_height); + if (!handle) { + bool cursor_enabled = false; + struct drm_crtc *crtci; + + /* Hide cursor. */ + vbox_crtc->cursor_enabled = false; + list_for_each_entry(crtci, &vbox->dev->mode_config.crtc_list, + head) { + if (to_vbox_crtc(crtci)->cursor_enabled) + cursor_enabled = true; + } + + if (!cursor_enabled) + hgsmi_update_pointer_shape(vbox->guest_pool, 0, 0, 0, + 0, 0, NULL, 0); + return 0; + } + + vbox_crtc->cursor_enabled = true; + + if (width > VBOX_MAX_CURSOR_WIDTH || height > VBOX_MAX_CURSOR_HEIGHT || + width == 0 || height == 0) + return -EINVAL; + + ret = hgsmi_query_conf(vbox->guest_pool, + VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &caps); + if (ret) + return ret; + + if (!(caps & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE)) { + /* + * -EINVAL means cursor_set2() not supported, -EAGAIN means + * retry at once. + */ + return -EBUSY; + } + + obj = drm_gem_object_lookup(file_priv, handle); + if (!obj) { + DRM_ERROR("Cannot find cursor object %x for crtc\n", handle); + return -ENOENT; + } + + bo = gem_to_vbox_bo(obj); + ret = vbox_bo_reserve(bo, false); + if (ret) + goto out_unref_obj; + + /* + * The mask must be calculated based on the alpha + * channel, one bit per ARGB word, and must be 32-bit + * padded. + */ + mask_size = ((width + 7) / 8 * height + 3) & ~3; + data_size = width * height * 4 + mask_size; + vbox->cursor_hot_x = min_t(u32, max(hot_x, 0), width); + vbox->cursor_hot_y = min_t(u32, max(hot_y, 0), height); + vbox->cursor_width = width; + vbox->cursor_height = height; + vbox->cursor_data_size = data_size; + dst = vbox->cursor_data; + + ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &uobj_map); + if (ret) { + vbox->cursor_data_size = 0; + goto out_unreserve_bo; + } + + src = ttm_kmap_obj_virtual(&uobj_map, &src_isiomem); + if (src_isiomem) { + DRM_ERROR("src cursor bo not in main memory\n"); + ret = -EIO; + goto out_unmap_bo; + } + + copy_cursor_image(src, dst, width, height, mask_size); + + flags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE | + VBOX_MOUSE_POINTER_ALPHA; + ret = hgsmi_update_pointer_shape(vbox->guest_pool, flags, + vbox->cursor_hot_x, vbox->cursor_hot_y, + width, height, dst, data_size); +out_unmap_bo: + ttm_bo_kunmap(&uobj_map); +out_unreserve_bo: + vbox_bo_unreserve(bo); +out_unref_obj: + drm_gem_object_unreference_unlocked(obj); + + return ret; +} + +static int vbox_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct vbox_private *vbox = crtc->dev->dev_private; + u32 flags = VBOX_MOUSE_POINTER_VISIBLE | + VBOX_MOUSE_POINTER_SHAPE | VBOX_MOUSE_POINTER_ALPHA; + s32 crtc_x = + vbox->single_framebuffer ? crtc->x : to_vbox_crtc(crtc)->x_hint; + s32 crtc_y = + vbox->single_framebuffer ? crtc->y : to_vbox_crtc(crtc)->y_hint; + u32 host_x, host_y; + u32 hot_x = 0; + u32 hot_y = 0; + int ret; + + /* + * We compare these to unsigned later and don't + * need to handle negative. + */ + if (x + crtc_x < 0 || y + crtc_y < 0 || vbox->cursor_data_size == 0) + return 0; + + ret = hgsmi_cursor_position(vbox->guest_pool, true, x + crtc_x, + y + crtc_y, &host_x, &host_y); + + /* + * The only reason we have vbox_cursor_move() is that some older clients + * might use DRM_IOCTL_MODE_CURSOR instead of DRM_IOCTL_MODE_CURSOR2 and + * use DRM_MODE_CURSOR_MOVE to set the hot-spot. + * + * However VirtualBox 5.0.20 and earlier has a bug causing it to return + * 0,0 as host cursor location after a save and restore. + * + * To work around this we ignore a 0, 0 return, since missing the odd + * time when it legitimately happens is not going to hurt much. + */ + if (ret || (host_x == 0 && host_y == 0)) + return ret; + + if (x + crtc_x < host_x) + hot_x = min(host_x - x - crtc_x, vbox->cursor_width); + if (y + crtc_y < host_y) + hot_y = min(host_y - y - crtc_y, vbox->cursor_height); + + if (hot_x == vbox->cursor_hot_x && hot_y == vbox->cursor_hot_y) + return 0; + + vbox->cursor_hot_x = hot_x; + vbox->cursor_hot_y = hot_y; + + return hgsmi_update_pointer_shape(vbox->guest_pool, flags, + hot_x, hot_y, vbox->cursor_width, vbox->cursor_height, + vbox->cursor_data, vbox->cursor_data_size); +} diff --git a/drivers/staging/vboxvideo/vbox_prime.c b/drivers/staging/vboxvideo/vbox_prime.c new file mode 100644 index 000000000000..b7453e427a1d --- /dev/null +++ b/drivers/staging/vboxvideo/vbox_prime.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2017 Oracle Corporation + * Copyright 2017 Canonical + * + * 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: Andreas Pokorny + */ + +#include "vbox_drv.h" + +/* + * Based on qxl_prime.c: + * Empty Implementations as there should not be any other driver for a virtual + * device that might share buffers with vboxvideo + */ + +int vbox_gem_prime_pin(struct drm_gem_object *obj) +{ + WARN_ONCE(1, "not implemented"); + return -ENOSYS; +} + +void vbox_gem_prime_unpin(struct drm_gem_object *obj) +{ + WARN_ONCE(1, "not implemented"); +} + +struct sg_table *vbox_gem_prime_get_sg_table(struct drm_gem_object *obj) +{ + WARN_ONCE(1, "not implemented"); + return ERR_PTR(-ENOSYS); +} + +struct drm_gem_object *vbox_gem_prime_import_sg_table( + struct drm_device *dev, struct dma_buf_attachment *attach, + struct sg_table *table) +{ + WARN_ONCE(1, "not implemented"); + return ERR_PTR(-ENOSYS); +} + +void *vbox_gem_prime_vmap(struct drm_gem_object *obj) +{ + WARN_ONCE(1, "not implemented"); + return ERR_PTR(-ENOSYS); +} + +void vbox_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) +{ + WARN_ONCE(1, "not implemented"); +} + +int vbox_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *area) +{ + WARN_ONCE(1, "not implemented"); + return -ENOSYS; +} diff --git a/drivers/staging/vboxvideo/vbox_ttm.c b/drivers/staging/vboxvideo/vbox_ttm.c new file mode 100644 index 000000000000..34a905d40735 --- /dev/null +++ b/drivers/staging/vboxvideo/vbox_ttm.c @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2013-2017 Oracle Corporation + * This file is based on ast_ttm.c + * Copyright 2012 Red Hat 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, 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 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * + * Authors: Dave Airlie <airlied@redhat.com> + * Michael Thayer <michael.thayer@oracle.com> + */ +#include "vbox_drv.h" +#include <ttm/ttm_page_alloc.h> + +static inline struct vbox_private *vbox_bdev(struct ttm_bo_device *bd) +{ + return container_of(bd, struct vbox_private, ttm.bdev); +} + +static int vbox_ttm_mem_global_init(struct drm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void vbox_ttm_mem_global_release(struct drm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +/** + * Adds the vbox memory manager object/structures to the global memory manager. + */ +static int vbox_ttm_global_init(struct vbox_private *vbox) +{ + struct drm_global_reference *global_ref; + int ret; + + global_ref = &vbox->ttm.mem_global_ref; + global_ref->global_type = DRM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &vbox_ttm_mem_global_init; + global_ref->release = &vbox_ttm_mem_global_release; + ret = drm_global_item_ref(global_ref); + if (ret) { + DRM_ERROR("Failed setting up TTM memory subsystem.\n"); + return ret; + } + + vbox->ttm.bo_global_ref.mem_glob = vbox->ttm.mem_global_ref.object; + global_ref = &vbox->ttm.bo_global_ref.ref; + global_ref->global_type = DRM_GLOBAL_TTM_BO; + global_ref->size = sizeof(struct ttm_bo_global); + global_ref->init = &ttm_bo_global_init; + global_ref->release = &ttm_bo_global_release; + + ret = drm_global_item_ref(global_ref); + if (ret) { + DRM_ERROR("Failed setting up TTM BO subsystem.\n"); + drm_global_item_unref(&vbox->ttm.mem_global_ref); + return ret; + } + + return 0; +} + +/** + * Removes the vbox memory manager object from the global memory manager. + */ +static void vbox_ttm_global_release(struct vbox_private *vbox) +{ + drm_global_item_unref(&vbox->ttm.bo_global_ref.ref); + drm_global_item_unref(&vbox->ttm.mem_global_ref); +} + +static void vbox_bo_ttm_destroy(struct ttm_buffer_object *tbo) +{ + struct vbox_bo *bo; + + bo = container_of(tbo, struct vbox_bo, bo); + + drm_gem_object_release(&bo->gem); + kfree(bo); +} + +static bool vbox_ttm_bo_is_vbox_bo(struct ttm_buffer_object *bo) +{ + if (bo->destroy == &vbox_bo_ttm_destroy) + return true; + + return false; +} + +static int +vbox_bo_init_mem_type(struct ttm_bo_device *bdev, u32 type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_SYSTEM: + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_MASK_CACHING; + man->default_caching = TTM_PL_FLAG_CACHED; + break; + case TTM_PL_VRAM: + man->func = &ttm_bo_manager_func; + man->flags = TTM_MEMTYPE_FLAG_FIXED | TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned int)type); + return -EINVAL; + } + + return 0; +} + +static void +vbox_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) +{ + struct vbox_bo *vboxbo = vbox_bo(bo); + + if (!vbox_ttm_bo_is_vbox_bo(bo)) + return; + + vbox_ttm_placement(vboxbo, TTM_PL_FLAG_SYSTEM); + *pl = vboxbo->placement; +} + +static int vbox_bo_verify_access(struct ttm_buffer_object *bo, + struct file *filp) +{ + return 0; +} + +static int vbox_ttm_io_mem_reserve(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ + struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; + struct vbox_private *vbox = vbox_bdev(bdev); + + mem->bus.addr = NULL; + mem->bus.offset = 0; + mem->bus.size = mem->num_pages << PAGE_SHIFT; + mem->bus.base = 0; + mem->bus.is_iomem = false; + if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) + return -EINVAL; + switch (mem->mem_type) { + case TTM_PL_SYSTEM: + /* system memory */ + return 0; + case TTM_PL_VRAM: + mem->bus.offset = mem->start << PAGE_SHIFT; + mem->bus.base = pci_resource_start(vbox->dev->pdev, 0); + mem->bus.is_iomem = true; + break; + default: + return -EINVAL; + } + return 0; +} + +static void vbox_ttm_io_mem_free(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem) +{ +} + +static int vbox_bo_move(struct ttm_buffer_object *bo, + bool evict, bool interruptible, + bool no_wait_gpu, struct ttm_mem_reg *new_mem) +{ + return ttm_bo_move_memcpy(bo, interruptible, no_wait_gpu, new_mem); +} + +static void vbox_ttm_backend_destroy(struct ttm_tt *tt) +{ + ttm_tt_fini(tt); + kfree(tt); +} + +static struct ttm_backend_func vbox_tt_backend_func = { + .destroy = &vbox_ttm_backend_destroy, +}; + +static struct ttm_tt *vbox_ttm_tt_create(struct ttm_bo_device *bdev, + unsigned long size, + u32 page_flags, + struct page *dummy_read_page) +{ + struct ttm_tt *tt; + + tt = kzalloc(sizeof(*tt), GFP_KERNEL); + if (!tt) + return NULL; + + tt->func = &vbox_tt_backend_func; + if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) { + kfree(tt); + return NULL; + } + + return tt; +} + +static int vbox_ttm_tt_populate(struct ttm_tt *ttm) +{ + return ttm_pool_populate(ttm); +} + +static void vbox_ttm_tt_unpopulate(struct ttm_tt *ttm) +{ + ttm_pool_unpopulate(ttm); +} + +struct ttm_bo_driver vbox_bo_driver = { + .ttm_tt_create = vbox_ttm_tt_create, + .ttm_tt_populate = vbox_ttm_tt_populate, + .ttm_tt_unpopulate = vbox_ttm_tt_unpopulate, + .init_mem_type = vbox_bo_init_mem_type, + .eviction_valuable = ttm_bo_eviction_valuable, + .evict_flags = vbox_bo_evict_flags, + .move = vbox_bo_move, + .verify_access = vbox_bo_verify_access, + .io_mem_reserve = &vbox_ttm_io_mem_reserve, + .io_mem_free = &vbox_ttm_io_mem_free, + .io_mem_pfn = ttm_bo_default_io_mem_pfn, +}; + +int vbox_mm_init(struct vbox_private *vbox) +{ + int ret; + struct drm_device *dev = vbox->dev; + struct ttm_bo_device *bdev = &vbox->ttm.bdev; + + ret = vbox_ttm_global_init(vbox); + if (ret) + return ret; + + ret = ttm_bo_device_init(&vbox->ttm.bdev, + vbox->ttm.bo_global_ref.ref.object, + &vbox_bo_driver, + dev->anon_inode->i_mapping, + DRM_FILE_PAGE_OFFSET, true); + if (ret) { + DRM_ERROR("Error initialising bo driver; %d\n", ret); + goto err_ttm_global_release; + } + + ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM, + vbox->available_vram_size >> PAGE_SHIFT); + if (ret) { + DRM_ERROR("Failed ttm VRAM init: %d\n", ret); + goto err_device_release; + } + +#ifdef DRM_MTRR_WC + vbox->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0), + DRM_MTRR_WC); +#else + vbox->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0)); +#endif + return 0; + +err_device_release: + ttm_bo_device_release(&vbox->ttm.bdev); +err_ttm_global_release: + vbox_ttm_global_release(vbox); + return ret; +} + +void vbox_mm_fini(struct vbox_private *vbox) +{ +#ifdef DRM_MTRR_WC + drm_mtrr_del(vbox->fb_mtrr, + pci_resource_start(vbox->dev->pdev, 0), + pci_resource_len(vbox->dev->pdev, 0), DRM_MTRR_WC); +#else + arch_phys_wc_del(vbox->fb_mtrr); +#endif + ttm_bo_device_release(&vbox->ttm.bdev); + vbox_ttm_global_release(vbox); +} + +void vbox_ttm_placement(struct vbox_bo *bo, int domain) +{ + unsigned int i; + u32 c = 0; + + bo->placement.placement = bo->placements; + bo->placement.busy_placement = bo->placements; + + if (domain & TTM_PL_FLAG_VRAM) + bo->placements[c++].flags = + TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM; + if (domain & TTM_PL_FLAG_SYSTEM) + bo->placements[c++].flags = + TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + if (!c) + bo->placements[c++].flags = + TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + + bo->placement.num_placement = c; + bo->placement.num_busy_placement = c; + + for (i = 0; i < c; ++i) { + bo->placements[i].fpfn = 0; + bo->placements[i].lpfn = 0; + } +} + +int vbox_bo_create(struct drm_device *dev, int size, int align, + u32 flags, struct vbox_bo **pvboxbo) +{ + struct vbox_private *vbox = dev->dev_private; + struct vbox_bo *vboxbo; + size_t acc_size; + int ret; + + vboxbo = kzalloc(sizeof(*vboxbo), GFP_KERNEL); + if (!vboxbo) + return -ENOMEM; + + ret = drm_gem_object_init(dev, &vboxbo->gem, size); + if (ret) + goto err_free_vboxbo; + + vboxbo->bo.bdev = &vbox->ttm.bdev; + + vbox_ttm_placement(vboxbo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM); + + acc_size = ttm_bo_dma_acc_size(&vbox->ttm.bdev, size, + sizeof(struct vbox_bo)); + + ret = ttm_bo_init(&vbox->ttm.bdev, &vboxbo->bo, size, + ttm_bo_type_device, &vboxbo->placement, + align >> PAGE_SHIFT, false, NULL, acc_size, + NULL, NULL, vbox_bo_ttm_destroy); + if (ret) + goto err_free_vboxbo; + + *pvboxbo = vboxbo; + + return 0; + +err_free_vboxbo: + kfree(vboxbo); + return ret; +} + +static inline u64 vbox_bo_gpu_offset(struct vbox_bo *bo) +{ + return bo->bo.offset; +} + +int vbox_bo_pin(struct vbox_bo *bo, u32 pl_flag, u64 *gpu_addr) +{ + int i, ret; + + if (bo->pin_count) { + bo->pin_count++; + if (gpu_addr) + *gpu_addr = vbox_bo_gpu_offset(bo); + + return 0; + } + + vbox_ttm_placement(bo, pl_flag); + + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); + if (ret) + return ret; + + bo->pin_count = 1; + + if (gpu_addr) + *gpu_addr = vbox_bo_gpu_offset(bo); + + return 0; +} + +int vbox_bo_unpin(struct vbox_bo *bo) +{ + int i, ret; + + if (!bo->pin_count) { + DRM_ERROR("unpin bad %p\n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); + if (ret) + return ret; + + return 0; +} + +/* + * Move a vbox-owned buffer object to system memory if no one else has it + * pinned. The caller must have pinned it previously, and this call will + * release the caller's pin. + */ +int vbox_bo_push_sysram(struct vbox_bo *bo) +{ + int i, ret; + + if (!bo->pin_count) { + DRM_ERROR("unpin bad %p\n", bo); + return 0; + } + bo->pin_count--; + if (bo->pin_count) + return 0; + + if (bo->kmap.virtual) + ttm_bo_kunmap(&bo->kmap); + + vbox_ttm_placement(bo, TTM_PL_FLAG_SYSTEM); + + for (i = 0; i < bo->placement.num_placement; i++) + bo->placements[i].flags |= TTM_PL_FLAG_NO_EVICT; + + ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false); + if (ret) { + DRM_ERROR("pushing to VRAM failed\n"); + return ret; + } + + return 0; +} + +int vbox_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *file_priv; + struct vbox_private *vbox; + + if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) + return -EINVAL; + + file_priv = filp->private_data; + vbox = file_priv->minor->dev->dev_private; + + return ttm_bo_mmap(filp, vma, &vbox->ttm.bdev); +} diff --git a/drivers/staging/vboxvideo/vboxvideo.h b/drivers/staging/vboxvideo/vboxvideo.h new file mode 100644 index 000000000000..d835d75d761c --- /dev/null +++ b/drivers/staging/vboxvideo/vboxvideo.h @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2006-2016 Oracle 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, 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 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. + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ + +#ifndef __VBOXVIDEO_H__ +#define __VBOXVIDEO_H__ + +/* + * This should be in sync with monitorCount <xsd:maxInclusive value="64"/> in + * src/VBox/Main/xml/VirtualBox-settings-common.xsd + */ +#define VBOX_VIDEO_MAX_SCREENS 64 + +/* + * The last 4096 bytes of the guest VRAM contains the generic info for all + * DualView chunks: sizes and offsets of chunks. This is filled by miniport. + * + * Last 4096 bytes of each chunk contain chunk specific data: framebuffer info, + * etc. This is used exclusively by the corresponding instance of a display + * driver. + * + * The VRAM layout: + * Last 4096 bytes - Adapter information area. + * 4096 bytes aligned miniport heap (value specified in the config rouded up). + * Slack - what left after dividing the VRAM. + * 4096 bytes aligned framebuffers: + * last 4096 bytes of each framebuffer is the display information area. + * + * The Virtual Graphics Adapter information in the guest VRAM is stored by the + * guest video driver using structures prepended by VBOXVIDEOINFOHDR. + * + * When the guest driver writes dword 0 to the VBE_DISPI_INDEX_VBOX_VIDEO + * the host starts to process the info. The first element at the start of + * the 4096 bytes region should be normally be a LINK that points to + * actual information chain. That way the guest driver can have some + * fixed layout of the information memory block and just rewrite + * the link to point to relevant memory chain. + * + * The processing stops at the END element. + * + * The host can access the memory only when the port IO is processed. + * All data that will be needed later must be copied from these 4096 bytes. + * But other VRAM can be used by host until the mode is disabled. + * + * The guest driver writes dword 0xffffffff to the VBE_DISPI_INDEX_VBOX_VIDEO + * to disable the mode. + * + * VBE_DISPI_INDEX_VBOX_VIDEO is used to read the configuration information + * from the host and issue commands to the host. + * + * The guest writes the VBE_DISPI_INDEX_VBOX_VIDEO index register, the the + * following operations with the VBE data register can be performed: + * + * Operation Result + * write 16 bit value NOP + * read 16 bit value count of monitors + * write 32 bit value set the vbox cmd value and the cmd processed by the host + * read 32 bit value result of the last vbox command is returned + */ + +/** + * VBVA command header. + * + * @todo Where does this fit in? + */ +struct vbva_cmd_hdr { + /** Coordinates of affected rectangle. */ + s16 x; + s16 y; + u16 w; + u16 h; +} __packed; + +/** @name VBVA ring defines. + * + * The VBVA ring buffer is suitable for transferring large (< 2GB) amount of + * data. For example big bitmaps which do not fit to the buffer. + * + * Guest starts writing to the buffer by initializing a record entry in the + * records queue. VBVA_F_RECORD_PARTIAL indicates that the record is being + * written. As data is written to the ring buffer, the guest increases + * free_offset. + * + * The host reads the records on flushes and processes all completed records. + * When host encounters situation when only a partial record presents and + * len_and_flags & ~VBVA_F_RECORD_PARTIAL >= VBVA_RING_BUFFER_SIZE - + * VBVA_RING_BUFFER_THRESHOLD, the host fetched all record data and updates + * data_offset. After that on each flush the host continues fetching the data + * until the record is completed. + * + */ +#define VBVA_RING_BUFFER_SIZE (4194304 - 1024) +#define VBVA_RING_BUFFER_THRESHOLD (4096) + +#define VBVA_MAX_RECORDS (64) + +#define VBVA_F_MODE_ENABLED 0x00000001u +#define VBVA_F_MODE_VRDP 0x00000002u +#define VBVA_F_MODE_VRDP_RESET 0x00000004u +#define VBVA_F_MODE_VRDP_ORDER_MASK 0x00000008u + +#define VBVA_F_STATE_PROCESSING 0x00010000u + +#define VBVA_F_RECORD_PARTIAL 0x80000000u + +/** + * VBVA record. + */ +struct vbva_record { + /** The length of the record. Changed by guest. */ + u32 len_and_flags; +} __packed; + +/* + * The minimum HGSMI heap size is PAGE_SIZE (4096 bytes) and is a restriction of + * the runtime heapsimple API. Use minimum 2 pages here, because the info area + * also may contain other data (for example hgsmi_host_flags structure). + */ +#define VBVA_ADAPTER_INFORMATION_SIZE 65536 +#define VBVA_MIN_BUFFER_SIZE 65536 + +/* The value for port IO to let the adapter to interpret the adapter memory. */ +#define VBOX_VIDEO_DISABLE_ADAPTER_MEMORY 0xFFFFFFFF + +/* The value for port IO to let the adapter to interpret the adapter memory. */ +#define VBOX_VIDEO_INTERPRET_ADAPTER_MEMORY 0x00000000 + +/* The value for port IO to let the adapter to interpret the display memory. + * The display number is encoded in low 16 bits. + */ +#define VBOX_VIDEO_INTERPRET_DISPLAY_MEMORY_BASE 0x00010000 + +struct vbva_host_flags { + u32 host_events; + u32 supported_orders; +} __packed; + +struct vbva_buffer { + struct vbva_host_flags host_flags; + + /* The offset where the data start in the buffer. */ + u32 data_offset; + /* The offset where next data must be placed in the buffer. */ + u32 free_offset; + + /* The queue of record descriptions. */ + struct vbva_record records[VBVA_MAX_RECORDS]; + u32 record_first_index; + u32 record_free_index; + + /* Space to leave free when large partial records are transferred. */ + u32 partial_write_tresh; + + u32 data_len; + /* variable size for the rest of the vbva_buffer area in VRAM. */ + u8 data[0]; +} __packed; + +#define VBVA_MAX_RECORD_SIZE (128 * 1024 * 1024) + +/* guest->host commands */ +#define VBVA_QUERY_CONF32 1 +#define VBVA_SET_CONF32 2 +#define VBVA_INFO_VIEW 3 +#define VBVA_INFO_HEAP 4 +#define VBVA_FLUSH 5 +#define VBVA_INFO_SCREEN 6 +#define VBVA_ENABLE 7 +#define VBVA_MOUSE_POINTER_SHAPE 8 +/* informs host about HGSMI caps. see vbva_caps below */ +#define VBVA_INFO_CAPS 12 +/* configures scanline, see VBVASCANLINECFG below */ +#define VBVA_SCANLINE_CFG 13 +/* requests scanline info, see VBVASCANLINEINFO below */ +#define VBVA_SCANLINE_INFO 14 +/* inform host about VBVA Command submission */ +#define VBVA_CMDVBVA_SUBMIT 16 +/* inform host about VBVA Command submission */ +#define VBVA_CMDVBVA_FLUSH 17 +/* G->H DMA command */ +#define VBVA_CMDVBVA_CTL 18 +/* Query most recent mode hints sent */ +#define VBVA_QUERY_MODE_HINTS 19 +/** + * Report the guest virtual desktop position and size for mapping host and + * guest pointer positions. + */ +#define VBVA_REPORT_INPUT_MAPPING 20 +/** Report the guest cursor position and query the host position. */ +#define VBVA_CURSOR_POSITION 21 + +/* host->guest commands */ +#define VBVAHG_EVENT 1 +#define VBVAHG_DISPLAY_CUSTOM 2 + +/* vbva_conf32::index */ +#define VBOX_VBVA_CONF32_MONITOR_COUNT 0 +#define VBOX_VBVA_CONF32_HOST_HEAP_SIZE 1 +/** + * Returns VINF_SUCCESS if the host can report mode hints via VBVA. + * Set value to VERR_NOT_SUPPORTED before calling. + */ +#define VBOX_VBVA_CONF32_MODE_HINT_REPORTING 2 +/** + * Returns VINF_SUCCESS if the host can report guest cursor enabled status via + * VBVA. Set value to VERR_NOT_SUPPORTED before calling. + */ +#define VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING 3 +/** + * Returns the currently available host cursor capabilities. Available if + * vbva_conf32::VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING returns success. + * @see VMMDevReqMouseStatus::mouseFeatures. + */ +#define VBOX_VBVA_CONF32_CURSOR_CAPABILITIES 4 +/** Returns the supported flags in vbva_infoscreen::flags. */ +#define VBOX_VBVA_CONF32_SCREEN_FLAGS 5 +/** Returns the max size of VBVA record. */ +#define VBOX_VBVA_CONF32_MAX_RECORD_SIZE 6 + +struct vbva_conf32 { + u32 index; + u32 value; +} __packed; + +/** Reserved for historical reasons. */ +#define VBOX_VBVA_CURSOR_CAPABILITY_RESERVED0 BIT(0) +/** + * Guest cursor capability: can the host show a hardware cursor at the host + * pointer location? + */ +#define VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE BIT(1) +/** Reserved for historical reasons. */ +#define VBOX_VBVA_CURSOR_CAPABILITY_RESERVED2 BIT(2) +/** Reserved for historical reasons. Must always be unset. */ +#define VBOX_VBVA_CURSOR_CAPABILITY_RESERVED3 BIT(3) +/** Reserved for historical reasons. */ +#define VBOX_VBVA_CURSOR_CAPABILITY_RESERVED4 BIT(4) +/** Reserved for historical reasons. */ +#define VBOX_VBVA_CURSOR_CAPABILITY_RESERVED5 BIT(5) + +struct vbva_infoview { + /* Index of the screen, assigned by the guest. */ + u32 view_index; + + /* The screen offset in VRAM, the framebuffer starts here. */ + u32 view_offset; + + /* The size of the VRAM memory that can be used for the view. */ + u32 view_size; + + /* The recommended maximum size of the VRAM memory for the screen. */ + u32 max_screen_size; +} __packed; + +struct vbva_flush { + u32 reserved; +} __packed; + +/* vbva_infoscreen::flags */ +#define VBVA_SCREEN_F_NONE 0x0000 +#define VBVA_SCREEN_F_ACTIVE 0x0001 +/** + * The virtual monitor has been disabled by the guest and should be removed + * by the host and ignored for purposes of pointer position calculation. + */ +#define VBVA_SCREEN_F_DISABLED 0x0002 +/** + * The virtual monitor has been blanked by the guest and should be blacked + * out by the host using width, height, etc values from the vbva_infoscreen + * request. + */ +#define VBVA_SCREEN_F_BLANK 0x0004 +/** + * The virtual monitor has been blanked by the guest and should be blacked + * out by the host using the previous mode values for width. height, etc. + */ +#define VBVA_SCREEN_F_BLANK2 0x0008 + +struct vbva_infoscreen { + /* Which view contains the screen. */ + u32 view_index; + + /* Physical X origin relative to the primary screen. */ + s32 origin_x; + + /* Physical Y origin relative to the primary screen. */ + s32 origin_y; + + /* Offset of visible framebuffer relative to the framebuffer start. */ + u32 start_offset; + + /* The scan line size in bytes. */ + u32 line_size; + + /* Width of the screen. */ + u32 width; + + /* Height of the screen. */ + u32 height; + + /* Color depth. */ + u16 bits_per_pixel; + + /* VBVA_SCREEN_F_* */ + u16 flags; +} __packed; + +/* vbva_enable::flags */ +#define VBVA_F_NONE 0x00000000 +#define VBVA_F_ENABLE 0x00000001 +#define VBVA_F_DISABLE 0x00000002 +/* extended VBVA to be used with WDDM */ +#define VBVA_F_EXTENDED 0x00000004 +/* vbva offset is absolute VRAM offset */ +#define VBVA_F_ABSOFFSET 0x00000008 + +struct vbva_enable { + u32 flags; + u32 offset; + s32 result; +} __packed; + +struct vbva_enable_ex { + struct vbva_enable base; + u32 screen_id; +} __packed; + +struct vbva_mouse_pointer_shape { + /* The host result. */ + s32 result; + + /* VBOX_MOUSE_POINTER_* bit flags. */ + u32 flags; + + /* X coordinate of the hot spot. */ + u32 hot_X; + + /* Y coordinate of the hot spot. */ + u32 hot_y; + + /* Width of the pointer in pixels. */ + u32 width; + + /* Height of the pointer in scanlines. */ + u32 height; + + /* Pointer data. + * + **** + * The data consists of 1 bpp AND mask followed by 32 bpp XOR (color) + * mask. + * + * For pointers without alpha channel the XOR mask pixels are 32 bit + * values: (lsb)BGR0(msb). For pointers with alpha channel the XOR mask + * consists of (lsb)BGRA(msb) 32 bit values. + * + * Guest driver must create the AND mask for pointers with alpha chan., + * so if host does not support alpha, the pointer could be displayed as + * a normal color pointer. The AND mask can be constructed from alpha + * values. For example alpha value >= 0xf0 means bit 0 in the AND mask. + * + * The AND mask is 1 bpp bitmap with byte aligned scanlines. Size of AND + * mask, therefore, is and_len = (width + 7) / 8 * height. The padding + * bits at the end of any scanline are undefined. + * + * The XOR mask follows the AND mask on the next 4 bytes aligned offset: + * u8 *xor = and + (and_len + 3) & ~3 + * Bytes in the gap between the AND and the XOR mask are undefined. + * XOR mask scanlines have no gap between them and size of XOR mask is: + * xor_len = width * 4 * height. + **** + * + * Preallocate 4 bytes for accessing actual data as p->data. + */ + u8 data[4]; +} __packed; + +/** + * @name vbva_mouse_pointer_shape::flags + * @note The VBOX_MOUSE_POINTER_* flags are used in the guest video driver, + * values must be <= 0x8000 and must not be changed. (try make more sense + * of this, please). + * @{ + */ + +/** pointer is visible */ +#define VBOX_MOUSE_POINTER_VISIBLE 0x0001 +/** pointer has alpha channel */ +#define VBOX_MOUSE_POINTER_ALPHA 0x0002 +/** pointerData contains new pointer shape */ +#define VBOX_MOUSE_POINTER_SHAPE 0x0004 + +/** @} */ + +/* + * The guest driver can handle asynch guest cmd completion by reading the + * command offset from io port. + */ +#define VBVACAPS_COMPLETEGCMD_BY_IOREAD 0x00000001 +/* the guest driver can handle video adapter IRQs */ +#define VBVACAPS_IRQ 0x00000002 +/** The guest can read video mode hints sent via VBVA. */ +#define VBVACAPS_VIDEO_MODE_HINTS 0x00000004 +/** The guest can switch to a software cursor on demand. */ +#define VBVACAPS_DISABLE_CURSOR_INTEGRATION 0x00000008 +/** The guest does not depend on host handling the VBE registers. */ +#define VBVACAPS_USE_VBVA_ONLY 0x00000010 + +struct vbva_caps { + s32 rc; + u32 caps; +} __packed; + +/** Query the most recent mode hints received from the host. */ +struct vbva_query_mode_hints { + /** The maximum number of screens to return hints for. */ + u16 hints_queried_count; + /** The size of the mode hint structures directly following this one. */ + u16 hint_structure_guest_size; + /** Return code for the operation. Initialise to VERR_NOT_SUPPORTED. */ + s32 rc; +} __packed; + +/** + * Structure in which a mode hint is returned. The guest allocates an array + * of these immediately after the vbva_query_mode_hints structure. + * To accommodate future extensions, the vbva_query_mode_hints structure + * specifies the size of the vbva_modehint structures allocated by the guest, + * and the host only fills out structure elements which fit into that size. The + * host should fill any unused members (e.g. dx, dy) or structure space on the + * end with ~0. The whole structure can legally be set to ~0 to skip a screen. + */ +struct vbva_modehint { + u32 magic; + u32 cx; + u32 cy; + u32 bpp; /* Which has never been used... */ + u32 display; + u32 dx; /**< X offset into the virtual frame-buffer. */ + u32 dy; /**< Y offset into the virtual frame-buffer. */ + u32 enabled; /* Not flags. Add new members for new flags. */ +} __packed; + +#define VBVAMODEHINT_MAGIC 0x0801add9u + +/** + * Report the rectangle relative to which absolute pointer events should be + * expressed. This information remains valid until the next VBVA resize event + * for any screen, at which time it is reset to the bounding rectangle of all + * virtual screens and must be re-set. + * @see VBVA_REPORT_INPUT_MAPPING. + */ +struct vbva_report_input_mapping { + s32 x; /**< Upper left X co-ordinate relative to the first screen. */ + s32 y; /**< Upper left Y co-ordinate relative to the first screen. */ + u32 cx; /**< Rectangle width. */ + u32 cy; /**< Rectangle height. */ +} __packed; + +/** + * Report the guest cursor position and query the host one. The host may wish + * to use the guest information to re-position its own cursor (though this is + * currently unlikely). + * @see VBVA_CURSOR_POSITION + */ +struct vbva_cursor_position { + u32 report_position; /**< Are we reporting a position? */ + u32 x; /**< Guest cursor X position */ + u32 y; /**< Guest cursor Y position */ +} __packed; + +#endif diff --git a/drivers/staging/vboxvideo/vboxvideo_guest.h b/drivers/staging/vboxvideo/vboxvideo_guest.h new file mode 100644 index 000000000000..d09da841711a --- /dev/null +++ b/drivers/staging/vboxvideo/vboxvideo_guest.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2006-2017 Oracle 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. + */ + +#ifndef __VBOXVIDEO_GUEST_H__ +#define __VBOXVIDEO_GUEST_H__ + +#include <linux/genalloc.h> +#include "vboxvideo.h" + +/** + * Structure grouping the context needed for sending graphics acceleration + * information to the host via VBVA. Each screen has its own VBVA buffer. + */ +struct vbva_buf_ctx { + /** Offset of the buffer in the VRAM section for the screen */ + u32 buffer_offset; + /** Length of the buffer in bytes */ + u32 buffer_length; + /** Set if we wrote to the buffer faster than the host could read it */ + bool buffer_overflow; + /** VBVA record that we are currently preparing for the host, or NULL */ + struct vbva_record *record; + /** + * Pointer to the VBVA buffer mapped into the current address space. + * Will be NULL if VBVA is not enabled. + */ + struct vbva_buffer *vbva; +}; + +/** + * @name Base HGSMI APIs + * @{ + */ +int hgsmi_report_flags_location(struct gen_pool *ctx, u32 location); +int hgsmi_send_caps_info(struct gen_pool *ctx, u32 caps); +int hgsmi_test_query_conf(struct gen_pool *ctx); +int hgsmi_query_conf(struct gen_pool *ctx, u32 index, u32 *value_ret); +int hgsmi_update_pointer_shape(struct gen_pool *ctx, u32 flags, + u32 hot_x, u32 hot_y, u32 width, u32 height, + u8 *pixels, u32 len); +int hgsmi_cursor_position(struct gen_pool *ctx, bool report_position, + u32 x, u32 y, u32 *x_host, u32 *y_host); +/** @} */ + +/** + * @name VBVA APIs + * @{ + */ +bool vbva_enable(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, + struct vbva_buffer *vbva, s32 screen); +void vbva_disable(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, + s32 screen); +bool vbva_buffer_begin_update(struct vbva_buf_ctx *vbva_ctx, + struct gen_pool *ctx); +void vbva_buffer_end_update(struct vbva_buf_ctx *vbva_ctx); +bool vbva_write(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, + const void *p, u32 len); +void vbva_setup_buffer_context(struct vbva_buf_ctx *vbva_ctx, + u32 buffer_offset, u32 buffer_length); +/** @} */ + +/** + * @name Modesetting APIs + * @{ + */ +void hgsmi_process_display_info(struct gen_pool *ctx, u32 display, + s32 origin_x, s32 origin_y, u32 start_offset, + u32 pitch, u32 width, u32 height, + u16 bpp, u16 flags); +int hgsmi_update_input_mapping(struct gen_pool *ctx, s32 origin_x, s32 origin_y, + u32 width, u32 height); +int hgsmi_get_mode_hints(struct gen_pool *ctx, unsigned int screens, + struct vbva_modehint *hints); +/** @} */ + +#endif diff --git a/drivers/staging/vboxvideo/vboxvideo_vbe.h b/drivers/staging/vboxvideo/vboxvideo_vbe.h new file mode 100644 index 000000000000..f842f4d9c80a --- /dev/null +++ b/drivers/staging/vboxvideo/vboxvideo_vbe.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2006-2017 Oracle 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. + */ + +#ifndef __VBOXVIDEO_VBE_H__ +#define __VBOXVIDEO_VBE_H__ + +/* GUEST <-> HOST Communication API */ + +/** + * @todo FIXME: Either dynamicly ask host for this or put somewhere high in + * physical memory like 0xE0000000. + */ + +#define VBE_DISPI_BANK_ADDRESS 0xA0000 +#define VBE_DISPI_BANK_SIZE_KB 64 + +#define VBE_DISPI_MAX_XRES 16384 +#define VBE_DISPI_MAX_YRES 16384 +#define VBE_DISPI_MAX_BPP 32 + +#define VBE_DISPI_IOPORT_INDEX 0x01CE +#define VBE_DISPI_IOPORT_DATA 0x01CF + +#define VBE_DISPI_IOPORT_DAC_WRITE_INDEX 0x03C8 +#define VBE_DISPI_IOPORT_DAC_DATA 0x03C9 + +#define VBE_DISPI_INDEX_ID 0x0 +#define VBE_DISPI_INDEX_XRES 0x1 +#define VBE_DISPI_INDEX_YRES 0x2 +#define VBE_DISPI_INDEX_BPP 0x3 +#define VBE_DISPI_INDEX_ENABLE 0x4 +#define VBE_DISPI_INDEX_BANK 0x5 +#define VBE_DISPI_INDEX_VIRT_WIDTH 0x6 +#define VBE_DISPI_INDEX_VIRT_HEIGHT 0x7 +#define VBE_DISPI_INDEX_X_OFFSET 0x8 +#define VBE_DISPI_INDEX_Y_OFFSET 0x9 +#define VBE_DISPI_INDEX_VBOX_VIDEO 0xa +#define VBE_DISPI_INDEX_FB_BASE_HI 0xb + +#define VBE_DISPI_ID0 0xB0C0 +#define VBE_DISPI_ID1 0xB0C1 +#define VBE_DISPI_ID2 0xB0C2 +#define VBE_DISPI_ID3 0xB0C3 +#define VBE_DISPI_ID4 0xB0C4 + +#define VBE_DISPI_ID_VBOX_VIDEO 0xBE00 +/* The VBOX interface id. Indicates support for VBVA shared memory interface. */ +#define VBE_DISPI_ID_HGSMI 0xBE01 +#define VBE_DISPI_ID_ANYX 0xBE02 + +#define VBE_DISPI_DISABLED 0x00 +#define VBE_DISPI_ENABLED 0x01 +#define VBE_DISPI_GETCAPS 0x02 +#define VBE_DISPI_8BIT_DAC 0x20 +/** + * @note this definition is a BOCHS legacy, used only in the video BIOS + * code and ignored by the emulated hardware. + */ +#define VBE_DISPI_LFB_ENABLED 0x40 +#define VBE_DISPI_NOCLEARMEM 0x80 + +#define VGA_PORT_HGSMI_HOST 0x3b0 +#define VGA_PORT_HGSMI_GUEST 0x3d0 + +#endif diff --git a/drivers/staging/vboxvideo/vbva_base.c b/drivers/staging/vboxvideo/vbva_base.c new file mode 100644 index 000000000000..c10c782f94e1 --- /dev/null +++ b/drivers/staging/vboxvideo/vbva_base.c @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2006-2017 Oracle 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. + */ + +#include "vbox_drv.h" +#include "vbox_err.h" +#include "vboxvideo_guest.h" +#include "hgsmi_channels.h" + +/* + * There is a hardware ring buffer in the graphics device video RAM, formerly + * in the VBox VMMDev PCI memory space. + * All graphics commands go there serialized by vbva_buffer_begin_update. + * and vbva_buffer_end_update. + * + * free_offset is writing position. data_offset is reading position. + * free_offset == data_offset means buffer is empty. + * There must be always gap between data_offset and free_offset when data + * are in the buffer. + * Guest only changes free_offset, host changes data_offset. + */ + +static u32 vbva_buffer_available(const struct vbva_buffer *vbva) +{ + s32 diff = vbva->data_offset - vbva->free_offset; + + return diff > 0 ? diff : vbva->data_len + diff; +} + +static void vbva_buffer_place_data_at(struct vbva_buf_ctx *vbva_ctx, + const void *p, u32 len, u32 offset) +{ + struct vbva_buffer *vbva = vbva_ctx->vbva; + u32 bytes_till_boundary = vbva->data_len - offset; + u8 *dst = &vbva->data[offset]; + s32 diff = len - bytes_till_boundary; + + if (diff <= 0) { + /* Chunk will not cross buffer boundary. */ + memcpy(dst, p, len); + } else { + /* Chunk crosses buffer boundary. */ + memcpy(dst, p, bytes_till_boundary); + memcpy(&vbva->data[0], (u8 *)p + bytes_till_boundary, diff); + } +} + +static void vbva_buffer_flush(struct gen_pool *ctx) +{ + struct vbva_flush *p; + + p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, VBVA_FLUSH); + if (!p) + return; + + p->reserved = 0; + + hgsmi_buffer_submit(ctx, p); + hgsmi_buffer_free(ctx, p); +} + +bool vbva_write(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, + const void *p, u32 len) +{ + struct vbva_record *record; + struct vbva_buffer *vbva; + u32 available; + + vbva = vbva_ctx->vbva; + record = vbva_ctx->record; + + if (!vbva || vbva_ctx->buffer_overflow || + !record || !(record->len_and_flags & VBVA_F_RECORD_PARTIAL)) + return false; + + available = vbva_buffer_available(vbva); + + while (len > 0) { + u32 chunk = len; + + if (chunk >= available) { + vbva_buffer_flush(ctx); + available = vbva_buffer_available(vbva); + } + + if (chunk >= available) { + if (WARN_ON(available <= vbva->partial_write_tresh)) { + vbva_ctx->buffer_overflow = true; + return false; + } + chunk = available - vbva->partial_write_tresh; + } + + vbva_buffer_place_data_at(vbva_ctx, p, chunk, + vbva->free_offset); + + vbva->free_offset = (vbva->free_offset + chunk) % + vbva->data_len; + record->len_and_flags += chunk; + available -= chunk; + len -= chunk; + p += chunk; + } + + return true; +} + +static bool vbva_inform_host(struct vbva_buf_ctx *vbva_ctx, + struct gen_pool *ctx, s32 screen, bool enable) +{ + struct vbva_enable_ex *p; + bool ret; + + p = hgsmi_buffer_alloc(ctx, sizeof(*p), HGSMI_CH_VBVA, VBVA_ENABLE); + if (!p) + return false; + + p->base.flags = enable ? VBVA_F_ENABLE : VBVA_F_DISABLE; + p->base.offset = vbva_ctx->buffer_offset; + p->base.result = VERR_NOT_SUPPORTED; + if (screen >= 0) { + p->base.flags |= VBVA_F_EXTENDED | VBVA_F_ABSOFFSET; + p->screen_id = screen; + } + + hgsmi_buffer_submit(ctx, p); + + if (enable) + ret = RT_SUCCESS(p->base.result); + else + ret = true; + + hgsmi_buffer_free(ctx, p); + + return ret; +} + +bool vbva_enable(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, + struct vbva_buffer *vbva, s32 screen) +{ + bool ret = false; + + memset(vbva, 0, sizeof(*vbva)); + vbva->partial_write_tresh = 256; + vbva->data_len = vbva_ctx->buffer_length - sizeof(struct vbva_buffer); + vbva_ctx->vbva = vbva; + + ret = vbva_inform_host(vbva_ctx, ctx, screen, true); + if (!ret) + vbva_disable(vbva_ctx, ctx, screen); + + return ret; +} + +void vbva_disable(struct vbva_buf_ctx *vbva_ctx, struct gen_pool *ctx, + s32 screen) +{ + vbva_ctx->buffer_overflow = false; + vbva_ctx->record = NULL; + vbva_ctx->vbva = NULL; + + vbva_inform_host(vbva_ctx, ctx, screen, false); +} + +bool vbva_buffer_begin_update(struct vbva_buf_ctx *vbva_ctx, + struct gen_pool *ctx) +{ + struct vbva_record *record; + u32 next; + + if (!vbva_ctx->vbva || + !(vbva_ctx->vbva->host_flags.host_events & VBVA_F_MODE_ENABLED)) + return false; + + WARN_ON(vbva_ctx->buffer_overflow || vbva_ctx->record); + + next = (vbva_ctx->vbva->record_free_index + 1) % VBVA_MAX_RECORDS; + + /* Flush if all slots in the records queue are used */ + if (next == vbva_ctx->vbva->record_first_index) + vbva_buffer_flush(ctx); + + /* If even after flush there is no place then fail the request */ + if (next == vbva_ctx->vbva->record_first_index) + return false; + + record = &vbva_ctx->vbva->records[vbva_ctx->vbva->record_free_index]; + record->len_and_flags = VBVA_F_RECORD_PARTIAL; + vbva_ctx->vbva->record_free_index = next; + /* Remember which record we are using. */ + vbva_ctx->record = record; + + return true; +} + +void vbva_buffer_end_update(struct vbva_buf_ctx *vbva_ctx) +{ + struct vbva_record *record = vbva_ctx->record; + + WARN_ON(!vbva_ctx->vbva || !record || + !(record->len_and_flags & VBVA_F_RECORD_PARTIAL)); + + /* Mark the record completed. */ + record->len_and_flags &= ~VBVA_F_RECORD_PARTIAL; + + vbva_ctx->buffer_overflow = false; + vbva_ctx->record = NULL; +} + +void vbva_setup_buffer_context(struct vbva_buf_ctx *vbva_ctx, + u32 buffer_offset, u32 buffer_length) +{ + vbva_ctx->buffer_offset = buffer_offset; + vbva_ctx->buffer_length = buffer_length; +} diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 030bec855d86..314ffac50bb8 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -3391,7 +3391,6 @@ static int vchiq_probe(struct platform_device *pdev) struct device_node *fw_node; struct rpi_firmware *fw; int err; - void *ptr_err; fw_node = of_parse_phandle(pdev->dev.of_node, "firmware", 0); if (!fw_node) { @@ -3427,14 +3426,14 @@ static int vchiq_probe(struct platform_device *pdev) /* create sysfs entries */ vchiq_class = class_create(THIS_MODULE, DEVICE_NAME); - ptr_err = vchiq_class; - if (IS_ERR(ptr_err)) + err = PTR_ERR(vchiq_class); + if (IS_ERR(vchiq_class)) goto failed_class_create; vchiq_dev = device_create(vchiq_class, NULL, vchiq_devid, NULL, "vchiq"); - ptr_err = vchiq_dev; - if (IS_ERR(ptr_err)) + err = PTR_ERR(vchiq_dev); + if (IS_ERR(vchiq_dev)) goto failed_device_create; /* create debugfs entries */ @@ -3455,7 +3454,6 @@ failed_device_create: class_destroy(vchiq_class); failed_class_create: cdev_del(&vchiq_cdev); - err = PTR_ERR(ptr_err); failed_cdev_add: unregister_chrdev_region(vchiq_devid, 1); failed_platform_init: diff --git a/drivers/target/iscsi/cxgbit/cxgbit_cm.c b/drivers/target/iscsi/cxgbit/cxgbit_cm.c index e583dd8a418b..d4fa41be80f9 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_cm.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_cm.c @@ -1510,11 +1510,13 @@ cxgbit_pass_open_rpl(struct cxgbit_device *cdev, struct sk_buff *skb) if (!cnp) { pr_info("%s stid %d lookup failure\n", __func__, stid); - return; + goto rel_skb; } cxgbit_wake_up(&cnp->com.wr_wait, __func__, rpl->status); cxgbit_put_cnp(cnp); +rel_skb: + __kfree_skb(skb); } static void @@ -1530,11 +1532,13 @@ cxgbit_close_listsrv_rpl(struct cxgbit_device *cdev, struct sk_buff *skb) if (!cnp) { pr_info("%s stid %d lookup failure\n", __func__, stid); - return; + goto rel_skb; } cxgbit_wake_up(&cnp->com.wr_wait, __func__, rpl->status); cxgbit_put_cnp(cnp); +rel_skb: + __kfree_skb(skb); } static void @@ -1819,12 +1823,16 @@ static void cxgbit_set_tcb_rpl(struct cxgbit_device *cdev, struct sk_buff *skb) struct tid_info *t = lldi->tids; csk = lookup_tid(t, tid); - if (unlikely(!csk)) + if (unlikely(!csk)) { pr_err("can't find connection for tid %u.\n", tid); - else + goto rel_skb; + } else { cxgbit_wake_up(&csk->com.wr_wait, __func__, rpl->status); + } cxgbit_put_csk(csk); +rel_skb: + __kfree_skb(skb); } static void cxgbit_rx_data(struct cxgbit_device *cdev, struct sk_buff *skb) diff --git a/drivers/target/iscsi/cxgbit/cxgbit_target.c b/drivers/target/iscsi/cxgbit/cxgbit_target.c index dda13f1af38e..514986b57c2d 100644 --- a/drivers/target/iscsi/cxgbit/cxgbit_target.c +++ b/drivers/target/iscsi/cxgbit/cxgbit_target.c @@ -827,7 +827,7 @@ cxgbit_put_login_tx(struct iscsi_conn *conn, struct iscsi_login *login, static void cxgbit_skb_copy_to_sg(struct sk_buff *skb, struct scatterlist *sg, - unsigned int nents) + unsigned int nents, u32 skip) { struct skb_seq_state st; const u8 *buf; @@ -846,7 +846,7 @@ cxgbit_skb_copy_to_sg(struct sk_buff *skb, struct scatterlist *sg, } consumed += sg_pcopy_from_buffer(sg, nents, (void *)buf, - buf_len, consumed); + buf_len, skip + consumed); } } @@ -912,7 +912,7 @@ cxgbit_handle_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr, struct scatterlist *sg = &cmd->se_cmd.t_data_sg[0]; u32 sg_nents = max(1UL, DIV_ROUND_UP(pdu_cb->dlen, PAGE_SIZE)); - cxgbit_skb_copy_to_sg(csk->skb, sg, sg_nents); + cxgbit_skb_copy_to_sg(csk->skb, sg, sg_nents, 0); } cmd->write_data_done += pdu_cb->dlen; @@ -1069,11 +1069,13 @@ static int cxgbit_handle_iscsi_dataout(struct cxgbit_sock *csk) cmd->se_cmd.data_length); if (!(pdu_cb->flags & PDUCBF_RX_DATA_DDPD)) { + u32 skip = data_offset % PAGE_SIZE; + sg_off = data_offset / PAGE_SIZE; sg_start = &cmd->se_cmd.t_data_sg[sg_off]; - sg_nents = max(1UL, DIV_ROUND_UP(data_len, PAGE_SIZE)); + sg_nents = max(1UL, DIV_ROUND_UP(skip + data_len, PAGE_SIZE)); - cxgbit_skb_copy_to_sg(csk->skb, sg_start, sg_nents); + cxgbit_skb_copy_to_sg(csk->skb, sg_start, sg_nents, skip); } check_payload: diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 74e4975dd1b1..5001261f5d69 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -418,6 +418,7 @@ int iscsit_reset_np_thread( return 0; } np->np_thread_state = ISCSI_NP_THREAD_RESET; + atomic_inc(&np->np_reset_count); if (np->np_thread) { spin_unlock_bh(&np->np_thread_lock); @@ -2167,6 +2168,7 @@ iscsit_setup_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, cmd->cmd_sn = be32_to_cpu(hdr->cmdsn); cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn); cmd->data_direction = DMA_NONE; + kfree(cmd->text_in_ptr); cmd->text_in_ptr = NULL; return 0; @@ -3487,9 +3489,9 @@ iscsit_build_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn, return text_length; if (completed) { - hdr->flags |= ISCSI_FLAG_CMD_FINAL; + hdr->flags = ISCSI_FLAG_CMD_FINAL; } else { - hdr->flags |= ISCSI_FLAG_TEXT_CONTINUE; + hdr->flags = ISCSI_FLAG_TEXT_CONTINUE; cmd->read_data_done += text_length; if (cmd->targ_xfer_tag == 0xFFFFFFFF) cmd->targ_xfer_tag = session_get_next_ttt(conn->sess); diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index e9bdc8b86e7d..dc13afbd4c88 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -1243,9 +1243,11 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) flush_signals(current); spin_lock_bh(&np->np_thread_lock); - if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { + if (atomic_dec_if_positive(&np->np_reset_count) >= 0) { np->np_thread_state = ISCSI_NP_THREAD_ACTIVE; + spin_unlock_bh(&np->np_thread_lock); complete(&np->np_restart_comp); + return 1; } else if (np->np_thread_state == ISCSI_NP_THREAD_SHUTDOWN) { spin_unlock_bh(&np->np_thread_lock); goto exit; @@ -1278,7 +1280,8 @@ static int __iscsi_target_login_thread(struct iscsi_np *np) goto exit; } else if (rc < 0) { spin_lock_bh(&np->np_thread_lock); - if (np->np_thread_state == ISCSI_NP_THREAD_RESET) { + if (atomic_dec_if_positive(&np->np_reset_count) >= 0) { + np->np_thread_state = ISCSI_NP_THREAD_ACTIVE; spin_unlock_bh(&np->np_thread_lock); complete(&np->np_restart_comp); iscsit_put_transport(conn->conn_transport); diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c index 36913734c6bc..02e8a5d86658 100644 --- a/drivers/target/target_core_tpg.c +++ b/drivers/target/target_core_tpg.c @@ -364,7 +364,7 @@ void core_tpg_del_initiator_node_acl(struct se_node_acl *acl) mutex_lock(&tpg->acl_node_mutex); if (acl->dynamic_node_acl) acl->dynamic_node_acl = 0; - list_del(&acl->acl_list); + list_del_init(&acl->acl_list); mutex_unlock(&tpg->acl_node_mutex); target_shutdown_sessions(acl); @@ -548,7 +548,7 @@ int core_tpg_deregister(struct se_portal_group *se_tpg) * in transport_deregister_session(). */ list_for_each_entry_safe(nacl, nacl_tmp, &node_list, acl_list) { - list_del(&nacl->acl_list); + list_del_init(&nacl->acl_list); core_tpg_wait_for_nacl_pr_ref(nacl); core_free_device_list_for_node(nacl, se_tpg); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 97fed9a298bd..836d552b0385 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -466,7 +466,7 @@ static void target_complete_nacl(struct kref *kref) } mutex_lock(&se_tpg->acl_node_mutex); - list_del(&nacl->acl_list); + list_del_init(&nacl->acl_list); mutex_unlock(&se_tpg->acl_node_mutex); core_tpg_wait_for_nacl_pr_ref(nacl); @@ -538,7 +538,7 @@ void transport_free_session(struct se_session *se_sess) spin_unlock_irqrestore(&se_nacl->nacl_sess_lock, flags); if (se_nacl->dynamic_stop) - list_del(&se_nacl->acl_list); + list_del_init(&se_nacl->acl_list); } mutex_unlock(&se_tpg->acl_node_mutex); diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 80ee130f8253..942d094269fb 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -563,8 +563,6 @@ static int scatter_data_area(struct tcmu_dev *udev, block_remaining); to_offset = get_block_offset_user(udev, dbi, block_remaining); - offset = DATA_BLOCK_SIZE - block_remaining; - to += offset; if (*iov_cnt != 0 && to_offset == iov_tail(*iov)) { @@ -575,8 +573,10 @@ static int scatter_data_area(struct tcmu_dev *udev, (*iov)->iov_len = copy_bytes; } if (copy_data) { - memcpy(to, from + sg->length - sg_remaining, - copy_bytes); + offset = DATA_BLOCK_SIZE - block_remaining; + memcpy(to + offset, + from + sg->length - sg_remaining, + copy_bytes); tcmu_flush_dcache_range(to, copy_bytes); } sg_remaining -= copy_bytes; @@ -637,9 +637,8 @@ static void gather_data_area(struct tcmu_dev *udev, struct tcmu_cmd *cmd, copy_bytes = min_t(size_t, sg_remaining, block_remaining); offset = DATA_BLOCK_SIZE - block_remaining; - from += offset; tcmu_flush_dcache_range(from, copy_bytes); - memcpy(to + sg->length - sg_remaining, from, + memcpy(to + sg->length - sg_remaining, from + offset, copy_bytes); sg_remaining -= copy_bytes; @@ -1433,6 +1432,8 @@ static int tcmu_update_uio_info(struct tcmu_dev *udev) if (udev->dev_config[0]) snprintf(str + used, size - used, "/%s", udev->dev_config); + /* If the old string exists, free it */ + kfree(info->name); info->name = str; return 0; diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 69c0232a22f8..fb40dd0588b9 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -804,7 +804,7 @@ struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route, req->request_type = TB_CFG_PKG_RESET; req->response = &reply; req->response_size = sizeof(reply); - req->response_type = sizeof(TB_CFG_PKG_RESET); + req->response_type = TB_CFG_PKG_RESET; res = tb_cfg_request_sync(ctl, req, timeout_msec); diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 308b6e17c88a..fe2f00ceafc5 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -333,6 +333,15 @@ static int tb_drom_parse_entry_port(struct tb_switch *sw, int res; enum tb_port_type type; + /* + * Some DROMs list more ports than the controller actually has + * so we skip those but allow the parser to continue. + */ + if (header->index > sw->config.max_port_number) { + dev_info_once(&sw->dev, "ignoring unnecessary extra entries in DROM\n"); + return 0; + } + port = &sw->ports[header->index]; port->disabled = header->port_disabled; if (port->disabled) diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 8ee340290219..bdaac1ff00a5 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -904,7 +904,14 @@ static int icm_driver_ready(struct tb *tb) static int icm_suspend(struct tb *tb) { - return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_SAVE_DEVS, 0); + int ret; + + ret = nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_SAVE_DEVS, 0); + if (ret) + tb_info(tb, "Ignoring mailbox command error (%d) in %s\n", + ret, __func__); + + return 0; } /* diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index ab3e8f410444..53f40c57df59 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -30,7 +30,7 @@ static DEFINE_IDA(nvm_ida); struct nvm_auth_status { struct list_head list; - uuid_be uuid; + uuid_t uuid; u32 status; }; @@ -47,7 +47,7 @@ static struct nvm_auth_status *__nvm_get_auth_status(const struct tb_switch *sw) struct nvm_auth_status *st; list_for_each_entry(st, &nvm_auth_status_cache, list) { - if (!uuid_be_cmp(st->uuid, *sw->uuid)) + if (uuid_equal(&st->uuid, sw->uuid)) return st; } @@ -281,9 +281,11 @@ static struct nvmem_device *register_nvmem(struct tb_switch *sw, int id, if (active) { config.name = "nvm_active"; config.reg_read = tb_switch_nvm_read; + config.read_only = true; } else { config.name = "nvm_non_active"; config.reg_write = tb_switch_nvm_write; + config.root_only = true; } config.id = id; @@ -292,7 +294,6 @@ static struct nvmem_device *register_nvmem(struct tb_switch *sw, int id, config.size = size; config.dev = &sw->dev; config.owner = THIS_MODULE; - config.root_only = true; config.priv = sw; return nvmem_register(&config); @@ -806,11 +807,11 @@ static ssize_t key_store(struct device *dev, struct device_attribute *attr, struct tb_switch *sw = tb_to_switch(dev); u8 key[TB_SWITCH_KEY_SIZE]; ssize_t ret = count; + bool clear = false; - if (count < 64) - return -EINVAL; - - if (hex2bin(key, buf, sizeof(key))) + if (!strcmp(buf, "\n")) + clear = true; + else if (hex2bin(key, buf, sizeof(key))) return -EINVAL; if (mutex_lock_interruptible(&switch_lock)) @@ -820,15 +821,19 @@ static ssize_t key_store(struct device *dev, struct device_attribute *attr, ret = -EBUSY; } else { kfree(sw->key); - sw->key = kmemdup(key, sizeof(key), GFP_KERNEL); - if (!sw->key) - ret = -ENOMEM; + if (clear) { + sw->key = NULL; + } else { + sw->key = kmemdup(key, sizeof(key), GFP_KERNEL); + if (!sw->key) + ret = -ENOMEM; + } } mutex_unlock(&switch_lock); return ret; } -static DEVICE_ATTR_RW(key); +static DEVICE_ATTR(key, 0600, key_show, key_store); static ssize_t nvm_authenticate_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1460,7 +1465,7 @@ struct tb_sw_lookup { struct tb *tb; u8 link; u8 depth; - const uuid_be *uuid; + const uuid_t *uuid; }; static int tb_switch_match(struct device *dev, void *data) @@ -1517,7 +1522,7 @@ struct tb_switch *tb_switch_find_by_link_depth(struct tb *tb, u8 link, u8 depth) * Returned switch has reference count increased so the caller needs to * call tb_switch_put() when done with the switch. */ -struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_be *uuid) +struct tb_switch *tb_switch_find_by_uuid(struct tb *tb, const uuid_t *uuid) { struct tb_sw_lookup lookup; struct device *dev; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 3d9f64676e58..e0deee4f1eb0 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -101,7 +101,7 @@ struct tb_switch { struct tb_dma_port *dma_port; struct tb *tb; u64 uid; - uuid_be *uuid; + uuid_t *uuid; u16 vendor; u16 device; const char *vendor_name; @@ -407,7 +407,7 @@ void tb_sw_set_unplugged(struct tb_switch *sw); struct tb_switch *get_switch_at_route(struct tb_switch *sw, u64 route); 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_be *uuid); +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) { diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index 85b6d33c0919..de6441e4a060 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -179,7 +179,7 @@ struct icm_fr_pkg_get_topology_response { struct icm_fr_event_device_connected { struct icm_pkg_header hdr; - uuid_be ep_uuid; + uuid_t ep_uuid; u8 connection_key; u8 connection_id; u16 link_info; @@ -193,7 +193,7 @@ struct icm_fr_event_device_connected { struct icm_fr_pkg_approve_device { struct icm_pkg_header hdr; - uuid_be ep_uuid; + uuid_t ep_uuid; u8 connection_key; u8 connection_id; u16 reserved; @@ -207,7 +207,7 @@ struct icm_fr_event_device_disconnected { struct icm_fr_pkg_add_device_key { struct icm_pkg_header hdr; - uuid_be ep_uuid; + uuid_t ep_uuid; u8 connection_key; u8 connection_id; u16 reserved; @@ -216,7 +216,7 @@ struct icm_fr_pkg_add_device_key { struct icm_fr_pkg_add_device_key_response { struct icm_pkg_header hdr; - uuid_be ep_uuid; + uuid_t ep_uuid; u8 connection_key; u8 connection_id; u16 reserved; @@ -224,7 +224,7 @@ struct icm_fr_pkg_add_device_key_response { struct icm_fr_pkg_challenge_device { struct icm_pkg_header hdr; - uuid_be ep_uuid; + uuid_t ep_uuid; u8 connection_key; u8 connection_id; u16 reserved; @@ -233,7 +233,7 @@ struct icm_fr_pkg_challenge_device { struct icm_fr_pkg_challenge_device_response { struct icm_pkg_header hdr; - uuid_be ep_uuid; + uuid_t ep_uuid; u8 connection_key; u8 connection_id; u16 reserved; diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index d1399aac05a1..a6d5164c33a9 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -69,13 +69,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp) #ifdef CONFIG_UNIX98_PTYS if (tty->driver == ptm_driver) { mutex_lock(&devpts_mutex); - if (tty->link->driver_data) { - struct path *path = tty->link->driver_data; - - devpts_pty_kill(path->dentry); - path_put(path); - kfree(path); - } + if (tty->link->driver_data) + devpts_pty_kill(tty->link->driver_data); mutex_unlock(&devpts_mutex); } #endif @@ -448,48 +443,6 @@ err: return retval; } -/** - * pty_open_peer - open the peer of a pty - * @tty: the peer of the pty being opened - * - * Open the cached dentry in tty->link, providing a safe way for userspace - * to get the slave end of a pty (where they have the master fd and cannot - * access or trust the mount namespace /dev/pts was mounted inside). - */ -static struct file *pty_open_peer(struct tty_struct *tty, int flags) -{ - if (tty->driver->subtype != PTY_TYPE_MASTER) - return ERR_PTR(-EIO); - return dentry_open(tty->link->driver_data, flags, current_cred()); -} - -static int pty_get_peer(struct tty_struct *tty, int flags) -{ - int fd = -1; - struct file *filp = NULL; - int retval = -EINVAL; - - fd = get_unused_fd_flags(0); - if (fd < 0) { - retval = fd; - goto err; - } - - filp = pty_open_peer(tty, flags); - if (IS_ERR(filp)) { - retval = PTR_ERR(filp); - goto err_put; - } - - fd_install(fd, filp); - return fd; - -err_put: - put_unused_fd(fd); -err: - return retval; -} - static void pty_cleanup(struct tty_struct *tty) { tty_port_put(tty->port); @@ -646,9 +599,58 @@ static inline void legacy_pty_init(void) { } /* Unix98 devices */ #ifdef CONFIG_UNIX98_PTYS - static struct cdev ptmx_cdev; +/** + * ptm_open_peer - open the peer of a pty + * @master: the open struct file of the ptmx device node + * @tty: the master of the pty being opened + * @flags: the flags for open + * + * Provide a race free way for userspace to open the slave end of a pty + * (where they have the master fd and cannot access or trust the mount + * namespace /dev/pts was mounted inside). + */ +int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags) +{ + int fd = -1; + struct file *filp; + int retval = -EINVAL; + struct path path; + + if (tty->driver != ptm_driver) + return -EIO; + + fd = get_unused_fd_flags(0); + if (fd < 0) { + retval = fd; + goto err; + } + + /* Compute the slave's path */ + path.mnt = devpts_mntget(master, tty->driver_data); + if (IS_ERR(path.mnt)) { + retval = PTR_ERR(path.mnt); + goto err_put; + } + path.dentry = tty->link->driver_data; + + filp = dentry_open(&path, flags, current_cred()); + mntput(path.mnt); + if (IS_ERR(filp)) { + retval = PTR_ERR(filp); + goto err_put; + } + + fd_install(fd, filp); + return fd; + +err_put: + put_unused_fd(fd); +err: + return retval; +} + static int pty_unix98_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { @@ -663,8 +665,6 @@ static int pty_unix98_ioctl(struct tty_struct *tty, return pty_get_pktmode(tty, (int __user *)arg); case TIOCGPTN: /* Get PT Number */ return put_user(tty->index, (unsigned int __user *)arg); - case TIOCGPTPEER: /* Open the other end */ - return pty_get_peer(tty, (int) arg); case TIOCSIG: /* Send signal to other side of pty */ return pty_signal(tty, (int) arg); } @@ -792,7 +792,6 @@ static int ptmx_open(struct inode *inode, struct file *filp) { struct pts_fs_info *fsi; struct tty_struct *tty; - struct path *pts_path; struct dentry *dentry; int retval; int index; @@ -846,26 +845,16 @@ static int ptmx_open(struct inode *inode, struct file *filp) retval = PTR_ERR(dentry); goto err_release; } - /* We need to cache a fake path for TIOCGPTPEER. */ - pts_path = kmalloc(sizeof(struct path), GFP_KERNEL); - if (!pts_path) - goto err_release; - pts_path->mnt = filp->f_path.mnt; - pts_path->dentry = dentry; - path_get(pts_path); - tty->link->driver_data = pts_path; + tty->link->driver_data = dentry; retval = ptm_driver->ops->open(tty, filp); if (retval) - goto err_path_put; + goto err_release; tty_debug_hangup(tty, "opening (count=%d)\n", tty->count); tty_unlock(tty); return 0; -err_path_put: - path_put(pts_path); - kfree(pts_path); err_release: tty_unlock(tty); // This will also put-ref the fsi diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index b5def356af63..1aab3010fbfa 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1043,13 +1043,24 @@ int serial8250_register_8250_port(struct uart_8250_port *up) if (up->dl_write) uart->dl_write = up->dl_write; - if (serial8250_isa_config != NULL) - serial8250_isa_config(0, &uart->port, - &uart->capabilities); + if (uart->port.type != PORT_8250_CIR) { + if (serial8250_isa_config != NULL) + serial8250_isa_config(0, &uart->port, + &uart->capabilities); + + ret = uart_add_one_port(&serial8250_reg, + &uart->port); + if (ret == 0) + ret = uart->port.line; + } else { + dev_info(uart->port.dev, + "skipping CIR port at 0x%lx / 0x%llx, IRQ %d\n", + uart->port.iobase, + (unsigned long long)uart->port.mapbase, + uart->port.irq); - ret = uart_add_one_port(&serial8250_reg, &uart->port); - if (ret == 0) - ret = uart->port.line; + ret = 0; + } } mutex_unlock(&serial_mutex); diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index b5c98e5bf524..c6360fbdf808 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -261,7 +261,7 @@ __xr17v35x_register_gpio(struct pci_dev *pcidev, } static const struct property_entry exar_gpio_properties[] = { - PROPERTY_ENTRY_U32("linux,first-pin", 0), + PROPERTY_ENTRY_U32("exar,first-pin", 0), PROPERTY_ENTRY_U32("ngpios", 16), { } }; @@ -326,7 +326,7 @@ static int iot2040_rs485_config(struct uart_port *port, } static const struct property_entry iot2040_gpio_properties[] = { - PROPERTY_ENTRY_U32("linux,first-pin", 10), + PROPERTY_ENTRY_U32("exar,first-pin", 10), PROPERTY_ENTRY_U32("ngpios", 1), { } }; diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 8a857bb34fbb..1888d168a41c 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -142,15 +142,7 @@ static struct vendor_data vendor_sbsa = { .fixed_options = true, }; -/* - * Erratum 44 for QDF2432v1 and QDF2400v1 SoCs describes the BUSY bit as - * occasionally getting stuck as 1. To avoid the potential for a hang, check - * TXFE == 0 instead of BUSY == 1. This may not be suitable for all UART - * implementations, so only do so if an affected platform is detected in - * parse_spcr(). - */ -static bool qdf2400_e44_present = false; - +#ifdef CONFIG_ACPI_SPCR_TABLE static struct vendor_data vendor_qdt_qdf2400_e44 = { .reg_offset = pl011_std_offsets, .fr_busy = UART011_FR_TXFE, @@ -165,6 +157,7 @@ static struct vendor_data vendor_qdt_qdf2400_e44 = { .always_enabled = true, .fixed_options = true, }; +#endif static u16 pl011_st_offsets[REG_ARRAY_SIZE] = { [REG_DR] = UART01x_DR, @@ -2375,12 +2368,14 @@ static int __init pl011_console_match(struct console *co, char *name, int idx, resource_size_t addr; int i; - if (strcmp(name, "qdf2400_e44") == 0) { - pr_info_once("UART: Working around QDF2400 SoC erratum 44"); - qdf2400_e44_present = true; - } else if (strcmp(name, "pl011") != 0) { + /* + * Systems affected by the Qualcomm Technologies QDF2400 E44 erratum + * have a distinct console name, so make sure we check for that. + * The actual implementation of the erratum occurs in the probe + * function. + */ + if ((strcmp(name, "qdf2400_e44") != 0) && (strcmp(name, "pl011") != 0)) return -ENODEV; - } if (uart_parse_earlycon(options, &iotype, &addr, &options)) return -ENODEV; @@ -2734,11 +2729,17 @@ static int sbsa_uart_probe(struct platform_device *pdev) } uap->port.irq = ret; - uap->reg_offset = vendor_sbsa.reg_offset; - uap->vendor = qdf2400_e44_present ? - &vendor_qdt_qdf2400_e44 : &vendor_sbsa; +#ifdef CONFIG_ACPI_SPCR_TABLE + if (qdf2400_e44_present) { + dev_info(&pdev->dev, "working around QDF2400 SoC erratum 44\n"); + uap->vendor = &vendor_qdt_qdf2400_e44; + } else +#endif + uap->vendor = &vendor_sbsa; + + uap->reg_offset = uap->vendor->reg_offset; uap->fifosize = 32; - uap->port.iotype = vendor_sbsa.access_32b ? UPIO_MEM32 : UPIO_MEM; + uap->port.iotype = uap->vendor->access_32b ? UPIO_MEM32 : UPIO_MEM; uap->port.ops = &sbsa_uart_pops; uap->fixed_baud = baudrate; diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 343de8c384b0..898dcb091a27 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -619,6 +619,12 @@ static unsigned int lpuart32_tx_empty(struct uart_port *port) TIOCSER_TEMT : 0; } +static bool lpuart_is_32(struct lpuart_port *sport) +{ + return sport->port.iotype == UPIO_MEM32 || + sport->port.iotype == UPIO_MEM32BE; +} + static irqreturn_t lpuart_txint(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; @@ -627,7 +633,7 @@ static irqreturn_t lpuart_txint(int irq, void *dev_id) spin_lock_irqsave(&sport->port.lock, flags); if (sport->port.x_char) { - if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) + if (lpuart_is_32(sport)) lpuart32_write(&sport->port, sport->port.x_char, UARTDATA); else writeb(sport->port.x_char, sport->port.membase + UARTDR); @@ -635,14 +641,14 @@ static irqreturn_t lpuart_txint(int irq, void *dev_id) } if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) { - if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) + if (lpuart_is_32(sport)) lpuart32_stop_tx(&sport->port); else lpuart_stop_tx(&sport->port); goto out; } - if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) + if (lpuart_is_32(sport)) lpuart32_transmit_buffer(sport); else lpuart_transmit_buffer(sport); @@ -1978,12 +1984,12 @@ static int __init lpuart_console_setup(struct console *co, char *options) if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); else - if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) + if (lpuart_is_32(sport)) lpuart32_console_get_options(sport, &baud, &parity, &bits); else lpuart_console_get_options(sport, &baud, &parity, &bits); - if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) + if (lpuart_is_32(sport)) lpuart32_setup_watermark(sport); else lpuart_setup_watermark(sport); @@ -2118,7 +2124,7 @@ static int lpuart_probe(struct platform_device *pdev) } sport->port.irq = ret; sport->port.iotype = sdata->iotype; - if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) + if (lpuart_is_32(sport)) sport->port.ops = &lpuart32_pops; else sport->port.ops = &lpuart_pops; @@ -2145,7 +2151,7 @@ static int lpuart_probe(struct platform_device *pdev) platform_set_drvdata(pdev, &sport->port); - if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) + if (lpuart_is_32(sport)) lpuart_reg.cons = LPUART32_CONSOLE; else lpuart_reg.cons = LPUART_CONSOLE; @@ -2198,7 +2204,7 @@ static int lpuart_suspend(struct device *dev) struct lpuart_port *sport = dev_get_drvdata(dev); unsigned long temp; - if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) { + if (lpuart_is_32(sport)) { /* disable Rx/Tx and interrupts */ temp = lpuart32_read(&sport->port, UARTCTRL); temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE); @@ -2249,7 +2255,7 @@ static int lpuart_resume(struct device *dev) if (sport->port.suspended && !sport->port.irq_wake) clk_prepare_enable(sport->clk); - if (sport->port.iotype & (UPIO_MEM32 | UPIO_MEM32BE)) { + if (lpuart_is_32(sport)) { lpuart32_setup_watermark(sport); temp = lpuart32_read(&sport->port, UARTCTRL); temp |= (UARTCTRL_RIE | UARTCTRL_TIE | UARTCTRL_RE | diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index 9e3162bf3bd1..80934e7bd67f 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -186,11 +186,6 @@ #define UART_NR 8 -/* RX DMA buffer periods */ -#define RX_DMA_PERIODS 4 -#define RX_BUF_SIZE (PAGE_SIZE) - - /* i.MX21 type uart runs on all i.mx except i.MX1 and i.MX6q */ enum imx_uart_type { IMX1_UART, @@ -226,7 +221,6 @@ struct imx_port { struct dma_chan *dma_chan_rx, *dma_chan_tx; struct scatterlist rx_sgl, tx_sgl[2]; void *rx_buf; - unsigned int rx_buf_size; struct circ_buf rx_ring; unsigned int rx_periods; dma_cookie_t rx_cookie; @@ -464,7 +458,7 @@ static inline void imx_transmit_buffer(struct imx_port *sport) } } - while (!uart_circ_empty(xmit) && + while (!uart_circ_empty(xmit) && !sport->dma_is_txing && !(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) { /* send xmit->buf[xmit->tail] * out the port here */ @@ -967,6 +961,8 @@ static void imx_timeout(unsigned long data) } } +#define RX_BUF_SIZE (PAGE_SIZE) + /* * There are two kinds of RX DMA interrupts(such as in the MX6Q): * [1] the RX DMA buffer is full. @@ -1049,6 +1045,9 @@ static void dma_rx_callback(void *data) } } +/* RX DMA buffer periods */ +#define RX_DMA_PERIODS 4 + static int start_rx_dma(struct imx_port *sport) { struct scatterlist *sgl = &sport->rx_sgl; @@ -1059,8 +1058,9 @@ static int start_rx_dma(struct imx_port *sport) sport->rx_ring.head = 0; sport->rx_ring.tail = 0; + sport->rx_periods = RX_DMA_PERIODS; - sg_init_one(sgl, sport->rx_buf, sport->rx_buf_size); + sg_init_one(sgl, sport->rx_buf, RX_BUF_SIZE); ret = dma_map_sg(dev, sgl, 1, DMA_FROM_DEVICE); if (ret == 0) { dev_err(dev, "DMA mapping error for RX.\n"); @@ -1171,7 +1171,7 @@ static int imx_uart_dma_init(struct imx_port *sport) goto err; } - sport->rx_buf = kzalloc(sport->rx_buf_size, GFP_KERNEL); + sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!sport->rx_buf) { ret = -ENOMEM; goto err; @@ -2036,7 +2036,6 @@ static int serial_imx_probe_dt(struct imx_port *sport, { struct device_node *np = pdev->dev.of_node; int ret; - u32 dma_buf_size[2]; sport->devdata = of_device_get_match_data(&pdev->dev); if (!sport->devdata) @@ -2060,14 +2059,6 @@ static int serial_imx_probe_dt(struct imx_port *sport, if (of_get_property(np, "rts-gpios", NULL)) sport->have_rtsgpio = 1; - if (!of_property_read_u32_array(np, "fsl,dma-size", dma_buf_size, 2)) { - sport->rx_buf_size = dma_buf_size[0] * dma_buf_size[1]; - sport->rx_periods = dma_buf_size[1]; - } else { - sport->rx_buf_size = RX_BUF_SIZE; - sport->rx_periods = RX_DMA_PERIODS; - } - return 0; } #else diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index da5ddfc14778..e08b16b070c0 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -1085,10 +1085,12 @@ static ssize_t rx_trigger_store(struct device *dev, { struct uart_port *port = dev_get_drvdata(dev); struct sci_port *sci = to_sci_port(port); + int ret; long r; - if (kstrtol(buf, 0, &r) == -EINVAL) - return -EINVAL; + ret = kstrtol(buf, 0, &r); + if (ret) + return ret; sci->rx_trigger = scif_set_rtrg(port, r); if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) @@ -1116,10 +1118,12 @@ static ssize_t rx_fifo_timeout_store(struct device *dev, { struct uart_port *port = dev_get_drvdata(dev); struct sci_port *sci = to_sci_port(port); + int ret; long r; - if (kstrtol(buf, 0, &r) == -EINVAL) - return -EINVAL; + ret = kstrtol(buf, 0, &r); + if (ret) + return ret; sci->rx_fifo_timeout = r; scif_set_rtrg(port, 1); if (r > 0) diff --git a/drivers/tty/serial/st-asc.c b/drivers/tty/serial/st-asc.c index f5335be344f6..6b0ca65027d0 100644 --- a/drivers/tty/serial/st-asc.c +++ b/drivers/tty/serial/st-asc.c @@ -758,6 +758,7 @@ static int asc_init_port(struct asc_port *ascport, if (IS_ERR(ascport->pinctrl)) { ret = PTR_ERR(ascport->pinctrl); dev_err(&pdev->dev, "Failed to get Pinctrl: %d\n", ret); + return ret; } ascport->states[DEFAULT] = diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 974b13d24401..10c4038c0e8d 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2518,6 +2518,9 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case TIOCSSERIAL: tty_warn_deprecated_flags(p); break; + case TIOCGPTPEER: + /* Special because the struct file is needed */ + return ptm_open_peer(file, tty, (int)arg); default: retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg); if (retval != -ENOIOCTLCMD) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5357d83bbda2..5e056064259c 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1829,6 +1829,9 @@ static const struct usb_device_id acm_ids[] = { { USB_DEVICE(0x1576, 0x03b1), /* Maretron USB100 */ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, + { USB_DEVICE(0xfff0, 0x0100), /* DATECS FP-2000 */ + .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ + }, { USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */ .driver_info = CLEAR_HALT_CONDITIONS, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index ab1bb3b538ac..7f277b092b5b 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1888,7 +1888,7 @@ void usb_hcd_flush_endpoint(struct usb_device *udev, /* No more submits can occur */ spin_lock_irq(&hcd_urb_list_lock); rescan: - list_for_each_entry (urb, &ep->urb_list, urb_list) { + list_for_each_entry_reverse(urb, &ep->urb_list, urb_list) { int is_in; if (urb->unlinked) @@ -2485,6 +2485,8 @@ void usb_hc_died (struct usb_hcd *hcd) } if (usb_hcd_is_primary_hcd(hcd) && hcd->shared_hcd) { hcd = hcd->shared_hcd; + clear_bit(HCD_FLAG_RH_RUNNING, &hcd->flags); + set_bit(HCD_FLAG_DEAD, &hcd->flags); if (hcd->rh_registered) { clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 6e6797d145dd..822f8c50e423 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4725,7 +4725,8 @@ hub_power_remaining(struct usb_hub *hub) static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange) { - int status, i; + int status = -ENODEV; + int i; unsigned unit_load; struct usb_device *hdev = hub->hdev; struct usb_hcd *hcd = bus_to_hcd(hdev->bus); @@ -4929,9 +4930,10 @@ loop: done: hub_port_disable(hub, port1, 1); - if (hcd->driver->relinquish_port && !hub->hdev->parent) - hcd->driver->relinquish_port(hcd, port1); - + if (hcd->driver->relinquish_port && !hub->hdev->parent) { + if (status != -ENOTCONN && status != -ENODEV) + hcd->driver->relinquish_port(hcd, port1); + } } /* Handle physical or logical connection change events. diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 3116edfcdc18..574da2b4529c 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -150,6 +150,9 @@ static const struct usb_device_id usb_quirk_list[] = { /* appletouch */ { USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Genesys Logic hub, internally used by Moshi USB to Ethernet Adapter */ + { USB_DEVICE(0x05e3, 0x0616), .driver_info = USB_QUIRK_NO_LPM }, + /* Avision AV600U */ { USB_DEVICE(0x0638, 0x0a13), .driver_info = USB_QUIRK_STRING_FETCH_255 }, @@ -249,6 +252,7 @@ static const struct usb_device_id usb_amd_resume_quirk_list[] = { { USB_DEVICE(0x093a, 0x2500), .driver_info = USB_QUIRK_RESET_RESUME }, { USB_DEVICE(0x093a, 0x2510), .driver_info = USB_QUIRK_RESET_RESUME }, { USB_DEVICE(0x093a, 0x2521), .driver_info = USB_QUIRK_RESET_RESUME }, + { USB_DEVICE(0x03f0, 0x2b4a), .driver_info = USB_QUIRK_RESET_RESUME }, /* Logitech Optical Mouse M90/M100 */ { USB_DEVICE(0x046d, 0xc05a), .driver_info = USB_QUIRK_RESET_RESUME }, diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index bc3b3fda5000..c4066cd77e47 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3573,6 +3573,9 @@ irq_retry: /* Report disconnection if it is not already done. */ dwc2_hsotg_disconnect(hsotg); + /* Reset device address to zero */ + __bic32(hsotg->regs + DCFG, DCFG_DEVADDR_MASK); + if (usb_status & GOTGCTL_BSESVLD && connected) dwc2_hsotg_core_init_disconnected(hsotg, true); } diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 326b302fc440..03474d3575ab 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -766,15 +766,15 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc->maximum_speed = USB_SPEED_HIGH; } - ret = dwc3_core_soft_reset(dwc); + ret = dwc3_core_get_phy(dwc); if (ret) goto err0; - ret = dwc3_phy_setup(dwc); + ret = dwc3_core_soft_reset(dwc); if (ret) goto err0; - ret = dwc3_core_get_phy(dwc); + ret = dwc3_phy_setup(dwc); if (ret) goto err0; diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 98926504b55b..f5aaa0cf3873 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -512,15 +512,6 @@ static int dwc3_omap_probe(struct platform_device *pdev) /* check the DMA Status */ reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG); - irq_set_status_flags(omap->irq, IRQ_NOAUTOEN); - ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt, - dwc3_omap_interrupt_thread, IRQF_SHARED, - "dwc3-omap", omap); - if (ret) { - dev_err(dev, "failed to request IRQ #%d --> %d\n", - omap->irq, ret); - goto err1; - } ret = dwc3_omap_extcon_register(omap); if (ret < 0) @@ -532,8 +523,15 @@ static int dwc3_omap_probe(struct platform_device *pdev) goto err1; } + ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt, + dwc3_omap_interrupt_thread, IRQF_SHARED, + "dwc3-omap", omap); + if (ret) { + dev_err(dev, "failed to request IRQ #%d --> %d\n", + omap->irq, ret); + goto err1; + } dwc3_omap_enable_irqs(omap); - enable_irq(omap->irq); return 0; err1: diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9e41605a276b..f064f1549333 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -191,14 +191,16 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, req->started = false; list_del(&req->list); - req->trb = NULL; req->remaining = 0; if (req->request.status == -EINPROGRESS) req->request.status = status; - usb_gadget_unmap_request_by_dev(dwc->sysdev, - &req->request, req->direction); + if (req->trb) + usb_gadget_unmap_request_by_dev(dwc->sysdev, + &req->request, req->direction); + + req->trb = NULL; trace_dwc3_gadget_giveback(req); @@ -894,9 +896,40 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, if (!node) { trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST; + /* + * USB Specification 2.0 Section 5.9.2 states that: "If + * there is only a single transaction in the microframe, + * only a DATA0 data packet PID is used. If there are + * two transactions per microframe, DATA1 is used for + * the first transaction data packet and DATA0 is used + * for the second transaction data packet. If there are + * three transactions per microframe, DATA2 is used for + * the first transaction data packet, DATA1 is used for + * the second, and DATA0 is used for the third." + * + * IOW, we should satisfy the following cases: + * + * 1) length <= maxpacket + * - DATA0 + * + * 2) maxpacket < length <= (2 * maxpacket) + * - DATA1, DATA0 + * + * 3) (2 * maxpacket) < length <= (3 * maxpacket) + * - DATA2, DATA1, DATA0 + */ if (speed == USB_SPEED_HIGH) { struct usb_ep *ep = &dep->endpoint; - trb->size |= DWC3_TRB_SIZE_PCM1(ep->mult - 1); + unsigned int mult = ep->mult - 1; + unsigned int maxp = usb_endpoint_maxp(ep->desc); + + if (length <= (2 * maxp)) + mult--; + + if (length <= maxp) + mult--; + + trb->size |= DWC3_TRB_SIZE_PCM1(mult); } } else { trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS; diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index e80b9c123a9d..f95bddd6513f 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2490,7 +2490,7 @@ static int fsg_main_thread(void *common_) int i; down_write(&common->filesem); - for (i = 0; i < ARRAY_SIZE(common->luns); --i) { + for (i = 0; i < ARRAY_SIZE(common->luns); i++) { struct fsg_lun *curlun = common->luns[i]; if (!curlun || !fsg_lun_is_open(curlun)) continue; diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 8656f84e17d9..29efbedc91f9 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -92,9 +92,9 @@ static struct uac_input_terminal_descriptor usb_out_it_desc = { .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_INPUT_TERMINAL, .bTerminalID = USB_OUT_IT_ID, - .wTerminalType = UAC_TERMINAL_STREAMING, + .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), .bAssocTerminal = 0, - .wChannelConfig = 0x3, + .wChannelConfig = cpu_to_le16(0x3), }; #define IO_OUT_OT_ID 2 @@ -103,7 +103,7 @@ static struct uac1_output_terminal_descriptor io_out_ot_desc = { .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, .bTerminalID = IO_OUT_OT_ID, - .wTerminalType = UAC_OUTPUT_TERMINAL_SPEAKER, + .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER), .bAssocTerminal = 0, .bSourceID = USB_OUT_IT_ID, }; @@ -114,9 +114,9 @@ static struct uac_input_terminal_descriptor io_in_it_desc = { .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_INPUT_TERMINAL, .bTerminalID = IO_IN_IT_ID, - .wTerminalType = UAC_INPUT_TERMINAL_MICROPHONE, + .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE), .bAssocTerminal = 0, - .wChannelConfig = 0x3, + .wChannelConfig = cpu_to_le16(0x3), }; #define USB_IN_OT_ID 4 @@ -125,7 +125,7 @@ static struct uac1_output_terminal_descriptor usb_in_ot_desc = { .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, .bTerminalID = USB_IN_OT_ID, - .wTerminalType = UAC_TERMINAL_STREAMING, + .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), .bAssocTerminal = 0, .bSourceID = IO_IN_IT_ID, }; @@ -174,7 +174,7 @@ static struct uac1_as_header_descriptor as_out_header_desc = { .bDescriptorSubtype = UAC_AS_GENERAL, .bTerminalLink = USB_OUT_IT_ID, .bDelay = 1, - .wFormatTag = UAC_FORMAT_TYPE_I_PCM, + .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM), }; static struct uac1_as_header_descriptor as_in_header_desc = { @@ -183,7 +183,7 @@ static struct uac1_as_header_descriptor as_in_header_desc = { .bDescriptorSubtype = UAC_AS_GENERAL, .bTerminalLink = USB_IN_OT_ID, .bDelay = 1, - .wFormatTag = UAC_FORMAT_TYPE_I_PCM, + .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM), }; DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(1); @@ -606,8 +606,8 @@ static int f_audio_bind(struct usb_configuration *c, struct usb_function *f) if (status) goto fail; - audio->out_ep_maxpsize = as_out_ep_desc.wMaxPacketSize; - audio->in_ep_maxpsize = as_in_ep_desc.wMaxPacketSize; + audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize); + audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize); audio->params.c_chmask = audio_opts->c_chmask; audio->params.c_srate = audio_opts->c_srate; audio->params.c_ssize = audio_opts->c_ssize; diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index 9082ce261e70..f05c3f3e6103 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -168,7 +168,7 @@ static struct uac2_input_terminal_descriptor usb_out_it_desc = { .bAssocTerminal = 0, .bCSourceID = USB_OUT_CLK_ID, .iChannelNames = 0, - .bmControls = (CONTROL_RDWR << COPY_CTRL), + .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), }; /* Input Terminal for I/O-In */ @@ -182,7 +182,7 @@ static struct uac2_input_terminal_descriptor io_in_it_desc = { .bAssocTerminal = 0, .bCSourceID = USB_IN_CLK_ID, .iChannelNames = 0, - .bmControls = (CONTROL_RDWR << COPY_CTRL), + .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), }; /* Ouput Terminal for USB_IN */ @@ -196,7 +196,7 @@ static struct uac2_output_terminal_descriptor usb_in_ot_desc = { .bAssocTerminal = 0, .bSourceID = IO_IN_IT_ID, .bCSourceID = USB_IN_CLK_ID, - .bmControls = (CONTROL_RDWR << COPY_CTRL), + .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), }; /* Ouput Terminal for I/O-Out */ @@ -210,7 +210,7 @@ static struct uac2_output_terminal_descriptor io_out_ot_desc = { .bAssocTerminal = 0, .bSourceID = USB_OUT_IT_ID, .bCSourceID = USB_OUT_CLK_ID, - .bmControls = (CONTROL_RDWR << COPY_CTRL), + .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), }; static struct uac2_ac_header_descriptor ac_hdr_desc = { @@ -220,9 +220,10 @@ static struct uac2_ac_header_descriptor ac_hdr_desc = { .bDescriptorSubtype = UAC_MS_HEADER, .bcdADC = cpu_to_le16(0x200), .bCategory = UAC2_FUNCTION_IO_BOX, - .wTotalLength = sizeof in_clk_src_desc + sizeof out_clk_src_desc - + sizeof usb_out_it_desc + sizeof io_in_it_desc - + sizeof usb_in_ot_desc + sizeof io_out_ot_desc, + .wTotalLength = cpu_to_le16(sizeof in_clk_src_desc + + sizeof out_clk_src_desc + sizeof usb_out_it_desc + + sizeof io_in_it_desc + sizeof usb_in_ot_desc + + sizeof io_out_ot_desc), .bmControls = 0, }; @@ -569,10 +570,12 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) return ret; } - agdev->in_ep_maxpsize = max(fs_epin_desc.wMaxPacketSize, - hs_epin_desc.wMaxPacketSize); - agdev->out_ep_maxpsize = max(fs_epout_desc.wMaxPacketSize, - hs_epout_desc.wMaxPacketSize); + agdev->in_ep_maxpsize = max_t(u16, + le16_to_cpu(fs_epin_desc.wMaxPacketSize), + le16_to_cpu(hs_epin_desc.wMaxPacketSize)); + agdev->out_ep_maxpsize = max_t(u16, + le16_to_cpu(fs_epout_desc.wMaxPacketSize), + le16_to_cpu(hs_epout_desc.wMaxPacketSize)); hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 9ffb11ec9ed9..7cd5c969fcbe 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -192,7 +192,7 @@ config USB_RENESAS_USBHS_UDC config USB_RENESAS_USB3 tristate 'Renesas USB3.0 Peripheral controller' depends on ARCH_RENESAS || COMPILE_TEST - depends on EXTCON + depends on EXTCON && HAS_DMA help Renesas USB3.0 Peripheral controller is a USB peripheral controller that supports super, high, and full speed USB 3.0 data transfers. @@ -257,6 +257,7 @@ config USB_MV_U3D config USB_SNP_CORE depends on (USB_AMD5536UDC || USB_SNP_UDC_PLAT) + depends on HAS_DMA tristate help This enables core driver support for Synopsys USB 2.0 Device @@ -271,7 +272,7 @@ config USB_SNP_CORE config USB_SNP_UDC_PLAT tristate "Synopsys USB 2.0 Device controller" - depends on (USB_GADGET && OF) + depends on USB_GADGET && OF && HAS_DMA select USB_GADGET_DUALSPEED select USB_SNP_CORE default ARCH_BCM_IPROC diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index d8278322d5ac..e1de8fe599a3 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -89,6 +89,9 @@ /* USB_COM_CON */ #define USB_COM_CON_CONF BIT(24) +#define USB_COM_CON_PN_WDATAIF_NL BIT(23) +#define USB_COM_CON_PN_RDATAIF_NL BIT(22) +#define USB_COM_CON_PN_LSTTR_PP BIT(21) #define USB_COM_CON_SPD_MODE BIT(17) #define USB_COM_CON_EP0_EN BIT(16) #define USB_COM_CON_DEV_ADDR_SHIFT 8 @@ -686,6 +689,9 @@ static void renesas_usb3_init_controller(struct renesas_usb3 *usb3) { usb3_init_axi_bridge(usb3); usb3_init_epc_registers(usb3); + usb3_set_bit(usb3, USB_COM_CON_PN_WDATAIF_NL | + USB_COM_CON_PN_RDATAIF_NL | USB_COM_CON_PN_LSTTR_PP, + USB3_USB_COM_CON); usb3_write(usb3, USB_OTG_IDMON, USB3_USB_OTG_INT_STA); usb3_write(usb3, USB_OTG_IDMON, USB3_USB_OTG_INT_ENA); @@ -832,21 +838,32 @@ static struct renesas_usb3_request *usb3_get_request(struct renesas_usb3_ep return usb3_req; } -static void usb3_request_done(struct renesas_usb3_ep *usb3_ep, - struct renesas_usb3_request *usb3_req, int status) +static void __usb3_request_done(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req, + int status) { struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); - unsigned long flags; dev_dbg(usb3_to_dev(usb3), "giveback: ep%2d, %u, %u, %d\n", usb3_ep->num, usb3_req->req.length, usb3_req->req.actual, status); usb3_req->req.status = status; - spin_lock_irqsave(&usb3->lock, flags); usb3_ep->started = false; list_del_init(&usb3_req->queue); - spin_unlock_irqrestore(&usb3->lock, flags); + spin_unlock(&usb3->lock); usb_gadget_giveback_request(&usb3_ep->ep, &usb3_req->req); + spin_lock(&usb3->lock); +} + +static void usb3_request_done(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req, int status) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + unsigned long flags; + + spin_lock_irqsave(&usb3->lock, flags); + __usb3_request_done(usb3_ep, usb3_req, status); + spin_unlock_irqrestore(&usb3->lock, flags); } static void usb3_irq_epc_pipe0_status_end(struct renesas_usb3 *usb3) @@ -1369,7 +1386,7 @@ static int renesas_usb3_dma_free_prd(struct renesas_usb3 *usb3, usb3_for_each_dma(usb3, dma, i) { if (dma->prd) { - dma_free_coherent(dev, USB3_DMA_MAX_XFER_SIZE, + dma_free_coherent(dev, USB3_DMA_PRD_SIZE, dma->prd, dma->prd_dma); dma->prd = NULL; } @@ -1409,12 +1426,12 @@ static void usb3_start_pipen(struct renesas_usb3_ep *usb3_ep, int ret = -EAGAIN; u32 enable_bits = 0; + spin_lock_irqsave(&usb3->lock, flags); if (usb3_ep->halt || usb3_ep->started) - return; + goto out; if (usb3_req != usb3_req_first) - return; + goto out; - spin_lock_irqsave(&usb3->lock, flags); if (usb3_pn_change(usb3, usb3_ep->num) < 0) goto out; diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index 0ce7b3ba60ff..e8a5fdaee37d 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -28,7 +28,7 @@ /* description */ #define UDC_MOD_DESCRIPTION "Synopsys UDC platform driver" -void start_udc(struct udc *udc) +static void start_udc(struct udc *udc) { if (udc->driver) { dev_info(udc->dev, "Connecting...\n"); @@ -38,7 +38,7 @@ void start_udc(struct udc *udc) } } -void stop_udc(struct udc *udc) +static void stop_udc(struct udc *udc) { int tmp; u32 reg; @@ -76,7 +76,7 @@ void stop_udc(struct udc *udc) dev_info(udc->dev, "Device disconnected\n"); } -void udc_drd_work(struct work_struct *work) +static void udc_drd_work(struct work_struct *work) { struct udc *udc; diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index a9a1e4c40480..c8f38649f749 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -77,6 +77,16 @@ #define USB_INTEL_USB3_PSSEN 0xD8 #define USB_INTEL_USB3PRM 0xDC +/* ASMEDIA quirk use */ +#define ASMT_DATA_WRITE0_REG 0xF8 +#define ASMT_DATA_WRITE1_REG 0xFC +#define ASMT_CONTROL_REG 0xE0 +#define ASMT_CONTROL_WRITE_BIT 0x02 +#define ASMT_WRITEREG_CMD 0x10423 +#define ASMT_FLOWCTL_ADDR 0xFA30 +#define ASMT_FLOWCTL_DATA 0xBA +#define ASMT_PSEUDO_DATA 0 + /* * amd_chipset_gen values represent AMD different chipset generations */ @@ -88,6 +98,7 @@ enum amd_chipset_gen { AMD_CHIPSET_HUDSON2, AMD_CHIPSET_BOLTON, AMD_CHIPSET_YANGTZE, + AMD_CHIPSET_TAISHAN, AMD_CHIPSET_UNKNOWN, }; @@ -131,6 +142,11 @@ static int amd_chipset_sb_type_init(struct amd_chipset_info *pinfo) pinfo->sb_type.gen = AMD_CHIPSET_SB700; else if (rev >= 0x40 && rev <= 0x4f) pinfo->sb_type.gen = AMD_CHIPSET_SB800; + } + pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, + 0x145c, NULL); + if (pinfo->smbus_dev) { + pinfo->sb_type.gen = AMD_CHIPSET_TAISHAN; } else { pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL); @@ -250,11 +266,12 @@ int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev) { /* Make sure amd chipset type has already been initialized */ usb_amd_find_chipset_info(); - if (amd_chipset.sb_type.gen != AMD_CHIPSET_YANGTZE) - return 0; - - dev_dbg(&pdev->dev, "QUIRK: Enable AMD remote wakeup fix\n"); - return 1; + if (amd_chipset.sb_type.gen == AMD_CHIPSET_YANGTZE || + amd_chipset.sb_type.gen == AMD_CHIPSET_TAISHAN) { + dev_dbg(&pdev->dev, "QUIRK: Enable AMD remote wakeup fix\n"); + return 1; + } + return 0; } EXPORT_SYMBOL_GPL(usb_hcd_amd_remote_wakeup_quirk); @@ -412,6 +429,50 @@ void usb_amd_quirk_pll_disable(void) } EXPORT_SYMBOL_GPL(usb_amd_quirk_pll_disable); +static int usb_asmedia_wait_write(struct pci_dev *pdev) +{ + unsigned long retry_count; + unsigned char value; + + for (retry_count = 1000; retry_count > 0; --retry_count) { + + pci_read_config_byte(pdev, ASMT_CONTROL_REG, &value); + + if (value == 0xff) { + dev_err(&pdev->dev, "%s: check_ready ERROR", __func__); + return -EIO; + } + + if ((value & ASMT_CONTROL_WRITE_BIT) == 0) + return 0; + + usleep_range(40, 60); + } + + dev_warn(&pdev->dev, "%s: check_write_ready timeout", __func__); + return -ETIMEDOUT; +} + +void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) +{ + if (usb_asmedia_wait_write(pdev) != 0) + return; + + /* send command and address to device */ + pci_write_config_dword(pdev, ASMT_DATA_WRITE0_REG, ASMT_WRITEREG_CMD); + pci_write_config_dword(pdev, ASMT_DATA_WRITE1_REG, ASMT_FLOWCTL_ADDR); + pci_write_config_byte(pdev, ASMT_CONTROL_REG, ASMT_CONTROL_WRITE_BIT); + + if (usb_asmedia_wait_write(pdev) != 0) + return; + + /* send data to device */ + pci_write_config_dword(pdev, ASMT_DATA_WRITE0_REG, ASMT_FLOWCTL_DATA); + pci_write_config_dword(pdev, ASMT_DATA_WRITE1_REG, ASMT_PSEUDO_DATA); + pci_write_config_byte(pdev, ASMT_CONTROL_REG, ASMT_CONTROL_WRITE_BIT); +} +EXPORT_SYMBOL_GPL(usb_asmedia_modifyflowcontrol); + void usb_amd_quirk_pll_enable(void) { usb_amd_quirk_pll(0); @@ -1096,3 +1157,23 @@ static void quirk_usb_early_handoff(struct pci_dev *pdev) } DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff); + +bool usb_xhci_needs_pci_reset(struct pci_dev *pdev) +{ + /* + * Our dear uPD72020{1,2} friend only partially resets when + * asked to via the XHCI interface, and may end up doing DMA + * at the wrong addresses, as it keeps the top 32bit of some + * addresses from its previous programming under obscure + * circumstances. + * Give it a good wack at probe time. Unfortunately, this + * needs to happen before we've had a chance to discover any + * quirk, or the system will be in a rather bad state. + */ + if (pdev->vendor == PCI_VENDOR_ID_RENESAS && + (pdev->device == 0x0014 || pdev->device == 0x0015)) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(usb_xhci_needs_pci_reset); diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h index 0222195bd5b0..5582cbafecd4 100644 --- a/drivers/usb/host/pci-quirks.h +++ b/drivers/usb/host/pci-quirks.h @@ -11,13 +11,16 @@ bool usb_amd_prefetch_quirk(void); void usb_amd_dev_put(void); void usb_amd_quirk_pll_disable(void); void usb_amd_quirk_pll_enable(void); +void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev); void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev); void usb_disable_xhci_ports(struct pci_dev *xhci_pdev); void sb800_prefetch(struct device *dev, int on); +bool usb_xhci_needs_pci_reset(struct pci_dev *pdev); #else struct pci_dev; static inline void usb_amd_quirk_pll_disable(void) {} static inline void usb_amd_quirk_pll_enable(void) {} +static inline void usb_asmedia_modifyflowcontrol(struct pci_dev *pdev) {} static inline void usb_amd_dev_put(void) {} static inline void usb_disable_xhci_ports(struct pci_dev *xhci_pdev) {} static inline void sb800_prefetch(struct device *dev, int on) {} diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 1adae9eab831..00721e8807ab 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -398,14 +398,21 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend) spin_lock_irqsave(&xhci->lock, flags); for (i = LAST_EP_INDEX; i > 0; i--) { if (virt_dev->eps[i].ring && virt_dev->eps[i].ring->dequeue) { + struct xhci_ep_ctx *ep_ctx; struct xhci_command *command; + + ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->out_ctx, i); + + /* Check ep is running, required by AMD SNPS 3.1 xHC */ + if (GET_EP_CTX_STATE(ep_ctx) != EP_STATE_RUNNING) + continue; + command = xhci_alloc_command(xhci, false, false, GFP_NOWAIT); if (!command) { spin_unlock_irqrestore(&xhci->lock, flags); xhci_free_command(xhci, cmd); return -ENOMEM; - } xhci_queue_stop_endpoint(xhci, command, slot_id, i, suspend); @@ -603,12 +610,14 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, /* Disable all Device Slots */ xhci_dbg(xhci, "Disable all slots\n"); + spin_unlock_irqrestore(&xhci->lock, *flags); for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) { retval = xhci_disable_slot(xhci, NULL, i); if (retval) xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n", i, retval); } + spin_lock_irqsave(&xhci->lock, *flags); /* Put all ports to the Disable state by clear PP */ xhci_dbg(xhci, "Disable all port (PP = 0)\n"); /* Power off USB3 ports*/ @@ -897,6 +906,9 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, clear_bit(wIndex, &bus_state->resuming_ports); set_bit(wIndex, &bus_state->rexit_ports); + + xhci_test_and_clear_bit(xhci, port_array, wIndex, + PORT_PLC); xhci_set_link_state(xhci, port_array, wIndex, XDEV_U0); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 53882e2babbb..8071c8fdd15e 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -59,6 +59,8 @@ #define PCI_DEVICE_ID_AMD_PROMONTORYA_2 0x43bb #define PCI_DEVICE_ID_AMD_PROMONTORYA_1 0x43bc +#define PCI_DEVICE_ID_ASMEDIA_1042A_XHCI 0x1142 + static const char hcd_name[] = "xhci_hcd"; static struct hc_driver __read_mostly xhci_pci_hc_driver; @@ -217,6 +219,10 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == 0x1142) xhci->quirks |= XHCI_TRUST_TX_LENGTH; + if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA && + pdev->device == PCI_DEVICE_ID_ASMEDIA_1042A_XHCI) + xhci->quirks |= XHCI_ASMEDIA_MODIFY_FLOWCONTROL; + if (pdev->vendor == PCI_VENDOR_ID_TI && pdev->device == 0x8241) xhci->quirks |= XHCI_LIMIT_ENDPOINT_INTERVAL_7; @@ -278,6 +284,13 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) driver = (struct hc_driver *)id->driver_data; + /* For some HW implementation, a XHCI reset is just not enough... */ + if (usb_xhci_needs_pci_reset(dev)) { + dev_info(&dev->dev, "Resetting\n"); + if (pci_reset_function_locked(dev)) + dev_warn(&dev->dev, "Reset failed"); + } + /* Prevent runtime suspending between USB-2 and USB-3 initialization */ pm_runtime_get_noresume(&dev->dev); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index c50c902d009e..cc368ad2b51e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -864,13 +864,16 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci, (ep->ep_state & EP_GETTING_NO_STREAMS)) { int stream_id; - for (stream_id = 0; stream_id < ep->stream_info->num_streams; + for (stream_id = 1; stream_id < ep->stream_info->num_streams; stream_id++) { + ring = ep->stream_info->stream_rings[stream_id]; + if (!ring) + continue; + xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Killing URBs for slot ID %u, ep index %u, stream %u", - slot_id, ep_index, stream_id + 1); - xhci_kill_ring_urbs(xhci, - ep->stream_info->stream_rings[stream_id]); + slot_id, ep_index, stream_id); + xhci_kill_ring_urbs(xhci, ring); } } else { ring = ep->ring; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 56f85df013db..b2ff1ff1a02f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -198,6 +198,9 @@ int xhci_reset(struct xhci_hcd *xhci) if (ret) return ret; + if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL) + usb_asmedia_modifyflowcontrol(to_pci_dev(xhci_to_hcd(xhci)->self.controller)); + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Wait for controller to be ready for doorbell rings"); /* @@ -622,8 +625,10 @@ int xhci_run(struct usb_hcd *hcd) if (!command) return -ENOMEM; - xhci_queue_vendor_command(xhci, command, 0, 0, 0, + ret = xhci_queue_vendor_command(xhci, command, 0, 0, 0, TRB_TYPE(TRB_NEC_GET_FW)); + if (ret) + xhci_free_command(xhci, command); } xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Finished xhci_run for USB2 roothub"); @@ -1085,6 +1090,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) if ((xhci->quirks & XHCI_COMP_MODE_QUIRK) && !comp_timer_running) compliance_mode_recovery_timer_init(xhci); + if (xhci->quirks & XHCI_ASMEDIA_MODIFY_FLOWCONTROL) + usb_asmedia_modifyflowcontrol(to_pci_dev(hcd->self.controller)); + /* Re-enable port polling. */ xhci_dbg(xhci, "%s: starting port polling.\n", __func__); set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3c6da1f93c84..e3e935291ed6 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1820,6 +1820,7 @@ struct xhci_hcd { #define XHCI_BROKEN_PORT_PED (1 << 25) #define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26) #define XHCI_U2_DISABLE_WAKE (1 << 27) +#define XHCI_ASMEDIA_MODIFY_FLOWCONTROL (1 << 28) unsigned int num_active_eps; unsigned int limit_active_eps; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 76decb8011eb..3344ffd5bb13 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -139,6 +139,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) "Could not flush host TX%d fifo: csr: %04x\n", ep->epnum, csr)) return; + mdelay(1); } } diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 8fb86a5f458e..3d0dd2f97415 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -197,6 +197,7 @@ struct msm_otg { struct regulator *v3p3; struct regulator *v1p8; struct regulator *vddcx; + struct regulator_bulk_data supplies[3]; struct reset_control *phy_rst; struct reset_control *link_rst; @@ -1731,7 +1732,6 @@ static int msm_otg_reboot_notify(struct notifier_block *this, static int msm_otg_probe(struct platform_device *pdev) { - struct regulator_bulk_data regs[3]; int ret = 0; struct device_node *np = pdev->dev.of_node; struct msm_otg_platform_data *pdata; @@ -1817,17 +1817,18 @@ static int msm_otg_probe(struct platform_device *pdev) return motg->irq; } - regs[0].supply = "vddcx"; - regs[1].supply = "v3p3"; - regs[2].supply = "v1p8"; + motg->supplies[0].supply = "vddcx"; + motg->supplies[1].supply = "v3p3"; + motg->supplies[2].supply = "v1p8"; - ret = devm_regulator_bulk_get(motg->phy.dev, ARRAY_SIZE(regs), regs); + ret = devm_regulator_bulk_get(motg->phy.dev, ARRAY_SIZE(motg->supplies), + motg->supplies); if (ret) return ret; - motg->vddcx = regs[0].consumer; - motg->v3p3 = regs[1].consumer; - motg->v1p8 = regs[2].consumer; + motg->vddcx = motg->supplies[0].consumer; + motg->v3p3 = motg->supplies[1].consumer; + motg->v1p8 = motg->supplies[2].consumer; clk_set_rate(motg->clk, 60000000); diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 623c51300393..f0ce304c5aaf 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -752,8 +752,10 @@ static int usbhsc_resume(struct device *dev) struct usbhs_priv *priv = dev_get_drvdata(dev); struct platform_device *pdev = usbhs_priv_to_pdev(priv); - if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) { usbhsc_power_ctrl(priv, 1); + usbhs_mod_autonomy_mode(priv); + } usbhs_platform_call(priv, phy_reset, pdev); diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 5bc7a6138855..2c8161bcf5b5 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -37,6 +37,7 @@ struct usbhsg_gpriv; struct usbhsg_uep { struct usb_ep ep; struct usbhs_pipe *pipe; + spinlock_t lock; /* protect the pipe */ char ep_name[EP_NAME_SIZE]; @@ -636,10 +637,13 @@ usbhsg_ep_enable_end: static int usbhsg_ep_disable(struct usb_ep *ep) { struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); - struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usbhs_pipe *pipe; + unsigned long flags; + spin_lock_irqsave(&uep->lock, flags); + pipe = usbhsg_uep_to_pipe(uep); if (!pipe) - return -EINVAL; + goto out; usbhsg_pipe_disable(uep); usbhs_pipe_free(pipe); @@ -647,6 +651,9 @@ static int usbhsg_ep_disable(struct usb_ep *ep) uep->pipe->mod_private = NULL; uep->pipe = NULL; +out: + spin_unlock_irqrestore(&uep->lock, flags); + return 0; } @@ -696,8 +703,11 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) { struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep); struct usbhsg_request *ureq = usbhsg_req_to_ureq(req); - struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); + struct usbhs_pipe *pipe; + unsigned long flags; + spin_lock_irqsave(&uep->lock, flags); + pipe = usbhsg_uep_to_pipe(uep); if (pipe) usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq)); @@ -706,6 +716,7 @@ static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) * even if the pipe is NULL. */ usbhsg_queue_pop(uep, ureq, -ECONNRESET); + spin_unlock_irqrestore(&uep->lock, flags); return 0; } @@ -852,10 +863,10 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) { struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv); struct usbhs_mod *mod = usbhs_mod_get_current(priv); - struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv); + struct usbhsg_uep *uep; struct device *dev = usbhs_priv_to_dev(priv); unsigned long flags; - int ret = 0; + int ret = 0, i; /******************** spin lock ********************/ usbhs_lock(priv, flags); @@ -887,7 +898,9 @@ static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status) usbhs_sys_set_test_mode(priv, 0); usbhs_sys_function_ctrl(priv, 0); - usbhsg_ep_disable(&dcp->ep); + /* disable all eps */ + usbhsg_for_each_uep_with_dcp(uep, gpriv, i) + usbhsg_ep_disable(&uep->ep); dev_dbg(dev, "stop gadget\n"); @@ -1069,6 +1082,7 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv) ret = -ENOMEM; goto usbhs_mod_gadget_probe_err_gpriv; } + spin_lock_init(&uep->lock); gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED); dev_info(dev, "%stransceiver found\n", diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c index d544b331c9f2..02b67abfc2a1 100644 --- a/drivers/usb/renesas_usbhs/rcar3.c +++ b/drivers/usb/renesas_usbhs/rcar3.c @@ -20,9 +20,13 @@ /* Low Power Status register (LPSTS) */ #define LPSTS_SUSPM 0x4000 -/* USB General control register 2 (UGCTRL2), bit[31:6] should be 0 */ +/* + * 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_OTG 0x00000030 +#define UGCTRL2_VBUSSEL 0x00000400 static void usbhs_write32(struct usbhs_priv *priv, u32 reg, u32 data) { @@ -34,7 +38,8 @@ static int usbhs_rcar3_power_ctrl(struct platform_device *pdev, { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); - usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | UGCTRL2_USB0SEL_OTG); + usbhs_write32(priv, UGCTRL2, UGCTRL2_RESERVED_3 | UGCTRL2_USB0SEL_OTG | + UGCTRL2_VBUSSEL); if (enable) { usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM); diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index f64e914a8985..2d945c9f975c 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -142,6 +142,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8998) }, /* KCF Technologies PRN */ { USB_DEVICE(0x10C4, 0x8A2A) }, /* HubZ dual ZigBee and Z-Wave dongle */ { USB_DEVICE(0x10C4, 0x8A5E) }, /* CEL EM3588 ZigBee USB Stick Long Range */ + { USB_DEVICE(0x10C4, 0x8B34) }, /* Qivicon ZigBee USB Radio Stick */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index ebe51f11105d..fe123153b1a5 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2025,6 +2025,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7d04, 0xff) }, /* D-Link DWM-158 */ { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e19, 0xff), /* D-Link DWM-221 B1 */ .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_INTERFACE_CLASS(0x2001, 0x7e35, 0xff), /* D-Link DWM-222 */ + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) }, /* D-Link DWM-152/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/C1 */ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) }, /* D-Link DWM-156/A3 */ diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index c9ebefd8f35f..a585b477415d 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -52,6 +52,8 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(IODATA_VENDOR_ID, IODATA_PRODUCT_ID_RSAQ5) }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID), .driver_info = PL2303_QUIRK_ENDPOINT_HACK }, + { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_UC485), + .driver_info = PL2303_QUIRK_ENDPOINT_HACK }, { USB_DEVICE(ATEN_VENDOR_ID, ATEN_PRODUCT_ID2) }, { USB_DEVICE(ATEN_VENDOR_ID2, ATEN_PRODUCT_ID) }, { USB_DEVICE(ELCOM_VENDOR_ID, ELCOM_PRODUCT_ID) }, diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 09d9be88209e..3b5a15d1dc0d 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -27,6 +27,7 @@ #define ATEN_VENDOR_ID 0x0557 #define ATEN_VENDOR_ID2 0x0547 #define ATEN_PRODUCT_ID 0x2008 +#define ATEN_PRODUCT_UC485 0x2021 #define ATEN_PRODUCT_ID2 0x2118 #define IODATA_VENDOR_ID 0x04bb diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index fba4005dd737..6a7720e66595 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1529,8 +1529,11 @@ static void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us) /* Make sure driver was initialized */ - if (us->extra == NULL) + if (us->extra == NULL) { usb_stor_dbg(us, "ERROR Driver not initialized\n"); + srb->result = DID_ERROR << 16; + return; + } scsi_set_resid(srb, 0); /* scsi_bufflen might change in protocol translation to ata */ diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index cbea9f329e71..cde115359793 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -124,9 +124,9 @@ UNUSUAL_DEV(0x0bc2, 0xab2a, 0x0000, 0x9999, /* Reported-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> */ UNUSUAL_DEV(0x13fd, 0x3940, 0x0000, 0x9999, "Initio Corporation", - "", + "INIC-3069", USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_NO_ATA_1X), + US_FL_NO_ATA_1X | US_FL_IGNORE_RESIDUE), /* Reported-by: Tom Arild Naess <tanaess@gmail.com> */ UNUSUAL_DEV(0x152d, 0x0539, 0x0000, 0x9999, diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 06615934fed1..0dceb9fa3a06 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -315,6 +315,7 @@ static int usb_stor_control_thread(void * __us) { struct us_data *us = (struct us_data *)__us; struct Scsi_Host *host = us_to_host(us); + struct scsi_cmnd *srb; for (;;) { usb_stor_dbg(us, "*** thread sleeping\n"); @@ -330,6 +331,7 @@ static int usb_stor_control_thread(void * __us) scsi_lock(host); /* When we are called with no command pending, we're done */ + srb = us->srb; if (us->srb == NULL) { scsi_unlock(host); mutex_unlock(&us->dev_mutex); @@ -398,14 +400,11 @@ static int usb_stor_control_thread(void * __us) /* lock access to the state */ scsi_lock(host); - /* indicate that the command is done */ - if (us->srb->result != DID_ABORT << 16) { - usb_stor_dbg(us, "scsi cmd done, result=0x%x\n", - us->srb->result); - us->srb->scsi_done(us->srb); - } else { + /* was the command aborted? */ + if (us->srb->result == DID_ABORT << 16) { SkipForAbort: usb_stor_dbg(us, "scsi command aborted\n"); + srb = NULL; /* Don't call srb->scsi_done() */ } /* @@ -429,6 +428,13 @@ SkipForAbort: /* unlock the device pointers */ mutex_unlock(&us->dev_mutex); + + /* now that the locks are released, notify the SCSI core */ + if (srb) { + usb_stor_dbg(us, "scsi cmd done, result=0x%x\n", + srb->result); + srb->scsi_done(srb); + } } /* for (;;) */ /* Wait until we are told to stop */ diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h index 6b0d2f0918c6..8a88f45822e3 100644 --- a/drivers/usb/typec/ucsi/ucsi.h +++ b/drivers/usb/typec/ucsi/ucsi.h @@ -3,6 +3,7 @@ #define __DRIVER_USB_TYPEC_UCSI_H #include <linux/bitops.h> +#include <linux/device.h> #include <linux/types.h> /* -------------------------------------------------------------------------- */ diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 063c1ce6fa42..f041b1a6cf66 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -226,7 +226,14 @@ static int vfio_pci_enable(struct vfio_pci_device *vdev) if (ret) return ret; - vdev->reset_works = (pci_reset_function(pdev) == 0); + /* If reset fails because of the device lock, fail this path entirely */ + ret = pci_try_reset_function(pdev); + if (ret == -EAGAIN) { + pci_disable_device(pdev); + return ret; + } + + vdev->reset_works = !ret; pci_save_state(pdev); vdev->pci_saved_state = pci_store_saved_state(pdev); if (!vdev->pci_saved_state) diff --git a/drivers/vfio/pci/vfio_pci_config.c b/drivers/vfio/pci/vfio_pci_config.c index 330a57024cbc..5628fe114347 100644 --- a/drivers/vfio/pci/vfio_pci_config.c +++ b/drivers/vfio/pci/vfio_pci_config.c @@ -839,7 +839,7 @@ static int vfio_exp_config_write(struct vfio_pci_device *vdev, int pos, /* Permissions for PCI Express capability */ static int __init init_pci_cap_exp_perm(struct perm_bits *perm) { - /* Alloc larger of two possible sizes */ + /* Alloc largest of possible sizes */ if (alloc_perm_bits(perm, PCI_CAP_EXP_ENDPOINT_SIZEOF_V2)) return -ENOMEM; @@ -1243,11 +1243,16 @@ static int vfio_cap_len(struct vfio_pci_device *vdev, u8 cap, u8 pos) vdev->extended_caps = (dword != 0); } - /* length based on version */ - if ((pcie_caps_reg(pdev) & PCI_EXP_FLAGS_VERS) == 1) + /* length based on version and type */ + if ((pcie_caps_reg(pdev) & PCI_EXP_FLAGS_VERS) == 1) { + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END) + return 0xc; /* "All Devices" only, no link */ return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1; - else + } else { + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END) + return 0x2c; /* No link */ return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2; + } case PCI_CAP_ID_HT: ret = pci_read_config_byte(pdev, pos + 3, &byte); if (ret) diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index e4613a3c362d..9cb3f722dce1 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -308,7 +308,6 @@ static void vhost_vq_reset(struct vhost_dev *dev, vq->avail = NULL; vq->used = NULL; vq->last_avail_idx = 0; - vq->last_used_event = 0; vq->avail_idx = 0; vq->last_used_idx = 0; vq->signalled_used = 0; @@ -1402,7 +1401,7 @@ long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp) r = -EINVAL; break; } - vq->last_avail_idx = vq->last_used_event = s.num; + vq->last_avail_idx = s.num; /* Forget the cached index value. */ vq->avail_idx = vq->last_avail_idx; break; @@ -2241,6 +2240,10 @@ static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) __u16 old, new; __virtio16 event; bool v; + /* Flush out used index updates. This is paired + * with the barrier that the Guest executes when enabling + * interrupts. */ + smp_mb(); if (vhost_has_feature(vq, VIRTIO_F_NOTIFY_ON_EMPTY) && unlikely(vq->avail_idx == vq->last_avail_idx)) @@ -2248,10 +2251,6 @@ static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) if (!vhost_has_feature(vq, VIRTIO_RING_F_EVENT_IDX)) { __virtio16 flags; - /* Flush out used index updates. This is paired - * with the barrier that the Guest executes when enabling - * interrupts. */ - smp_mb(); if (vhost_get_avail(vq, flags, &vq->avail->flags)) { vq_err(vq, "Failed to get flags"); return true; @@ -2266,26 +2265,11 @@ static bool vhost_notify(struct vhost_dev *dev, struct vhost_virtqueue *vq) if (unlikely(!v)) return true; - /* We're sure if the following conditions are met, there's no - * need to notify guest: - * 1) cached used event is ahead of new - * 2) old to new updating does not cross cached used event. */ - if (vring_need_event(vq->last_used_event, new + vq->num, new) && - !vring_need_event(vq->last_used_event, new, old)) - return false; - - /* Flush out used index updates. This is paired - * with the barrier that the Guest executes when enabling - * interrupts. */ - smp_mb(); - if (vhost_get_avail(vq, event, vhost_used_event(vq))) { vq_err(vq, "Failed to get used event idx"); return true; } - vq->last_used_event = vhost16_to_cpu(vq, event); - - return vring_need_event(vq->last_used_event, new, old); + return vring_need_event(vhost16_to_cpu(vq, event), new, old); } /* This actually signals the guest, using eventfd. */ diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index f72095868b93..bb7c29b8b9fc 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -115,9 +115,6 @@ struct vhost_virtqueue { /* Last index we used. */ u16 last_used_idx; - /* Last used evet we've seen */ - u16 last_used_event; - /* Used flags */ u16 used_flags; diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c index ff01bed7112f..1e784adb89b1 100644 --- a/drivers/video/fbdev/efifb.c +++ b/drivers/video/fbdev/efifb.c @@ -17,6 +17,7 @@ #include <asm/efi.h> static bool request_mem_succeeded = false; +static bool nowc = false; static struct fb_var_screeninfo efifb_defined = { .activate = FB_ACTIVATE_NOW, @@ -99,6 +100,8 @@ static int efifb_setup(char *options) screen_info.lfb_height = simple_strtoul(this_opt+7, NULL, 0); else if (!strncmp(this_opt, "width:", 6)) screen_info.lfb_width = simple_strtoul(this_opt+6, NULL, 0); + else if (!strcmp(this_opt, "nowc")) + nowc = true; } } @@ -255,7 +258,10 @@ static int efifb_probe(struct platform_device *dev) info->apertures->ranges[0].base = efifb_fix.smem_start; info->apertures->ranges[0].size = size_remap; - info->screen_base = ioremap_wc(efifb_fix.smem_start, efifb_fix.smem_len); + if (nowc) + info->screen_base = ioremap(efifb_fix.smem_start, efifb_fix.smem_len); + else + info->screen_base = ioremap_wc(efifb_fix.smem_start, efifb_fix.smem_len); if (!info->screen_base) { pr_err("efifb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n", efifb_fix.smem_len, efifb_fix.smem_start); diff --git a/drivers/video/fbdev/imxfb.c b/drivers/video/fbdev/imxfb.c index c166e0725be5..ba82f97fb42b 100644 --- a/drivers/video/fbdev/imxfb.c +++ b/drivers/video/fbdev/imxfb.c @@ -1073,20 +1073,16 @@ static int imxfb_remove(struct platform_device *pdev) imxfb_disable_controller(fbi); unregister_framebuffer(info); - + fb_dealloc_cmap(&info->cmap); pdata = dev_get_platdata(&pdev->dev); if (pdata && pdata->exit) pdata->exit(fbi->pdev); - - fb_dealloc_cmap(&info->cmap); - kfree(info->pseudo_palette); - framebuffer_release(info); - dma_free_wc(&pdev->dev, fbi->map_size, info->screen_base, fbi->map_dma); - iounmap(fbi->regs); release_mem_region(res->start, resource_size(res)); + kfree(info->pseudo_palette); + framebuffer_release(info); return 0; } diff --git a/drivers/video/fbdev/omap2/omapfb/dss/core.c b/drivers/video/fbdev/omap2/omapfb/dss/core.c index eecf695c16f4..09e5bb013d28 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/core.c @@ -193,7 +193,6 @@ static struct notifier_block omap_dss_pm_notif_block = { static int __init omap_dss_probe(struct platform_device *pdev) { - struct omap_dss_board_info *pdata = pdev->dev.platform_data; int r; core.pdev = pdev; diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 22caf808bfab..f0b3a0b9d42f 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -104,12 +104,6 @@ static u32 page_to_balloon_pfn(struct page *page) return pfn * VIRTIO_BALLOON_PAGES_PER_PAGE; } -static struct page *balloon_pfn_to_page(u32 pfn) -{ - BUG_ON(pfn % VIRTIO_BALLOON_PAGES_PER_PAGE); - return pfn_to_page(pfn / VIRTIO_BALLOON_PAGES_PER_PAGE); -} - static void balloon_ack(struct virtqueue *vq) { struct virtio_balloon *vb = vq->vdev->priv; @@ -138,8 +132,10 @@ static void set_page_pfns(struct virtio_balloon *vb, { unsigned int i; - /* Set balloon pfns pointing at this page. - * Note that the first pfn points at start of the page. */ + /* + * Set balloon pfns pointing at this page. + * Note that the first pfn points at start of the page. + */ for (i = 0; i < VIRTIO_BALLOON_PAGES_PER_PAGE; i++) pfns[i] = cpu_to_virtio32(vb->vdev, page_to_balloon_pfn(page) + i); @@ -182,18 +178,16 @@ static unsigned fill_balloon(struct virtio_balloon *vb, size_t num) return num_allocated_pages; } -static void release_pages_balloon(struct virtio_balloon *vb) +static void release_pages_balloon(struct virtio_balloon *vb, + struct list_head *pages) { - unsigned int i; - struct page *page; + struct page *page, *next; - /* Find pfns pointing at start of each page, get pages and free them. */ - for (i = 0; i < vb->num_pfns; i += VIRTIO_BALLOON_PAGES_PER_PAGE) { - page = balloon_pfn_to_page(virtio32_to_cpu(vb->vdev, - vb->pfns[i])); + list_for_each_entry_safe(page, next, pages, lru) { if (!virtio_has_feature(vb->vdev, VIRTIO_BALLOON_F_DEFLATE_ON_OOM)) adjust_managed_page_count(page, 1); + list_del(&page->lru); put_page(page); /* balloon reference */ } } @@ -203,6 +197,7 @@ static unsigned leak_balloon(struct virtio_balloon *vb, size_t num) unsigned num_freed_pages; struct page *page; struct balloon_dev_info *vb_dev_info = &vb->vb_dev_info; + LIST_HEAD(pages); /* We can only do one array worth at a time. */ num = min(num, ARRAY_SIZE(vb->pfns)); @@ -216,6 +211,7 @@ static unsigned leak_balloon(struct virtio_balloon *vb, size_t num) if (!page) break; set_page_pfns(vb, vb->pfns + vb->num_pfns, page); + list_add(&page->lru, &pages); vb->num_pages -= VIRTIO_BALLOON_PAGES_PER_PAGE; } @@ -227,7 +223,7 @@ static unsigned leak_balloon(struct virtio_balloon *vb, size_t num) */ if (vb->num_pfns != 0) tell_host(vb, vb->deflate_vq); - release_pages_balloon(vb); + release_pages_balloon(vb, &pages); mutex_unlock(&vb->balloon_lock); return num_freed_pages; } diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index 007a4f366086..1c4797e53f68 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -107,6 +107,7 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, { struct virtio_pci_device *vp_dev = to_vp_device(vdev); const char *name = dev_name(&vp_dev->vdev.dev); + unsigned flags = PCI_IRQ_MSIX; unsigned i, v; int err = -ENOMEM; @@ -126,10 +127,13 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, GFP_KERNEL)) goto error; + if (desc) { + flags |= PCI_IRQ_AFFINITY; + desc->pre_vectors++; /* virtio config vector */ + } + err = pci_alloc_irq_vectors_affinity(vp_dev->pci_dev, nvectors, - nvectors, PCI_IRQ_MSIX | - (desc ? PCI_IRQ_AFFINITY : 0), - desc); + nvectors, flags, desc); if (err < 0) goto error; vp_dev->msix_enabled = 1; diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c index 3612542b6044..83fc9aab34e8 100644 --- a/drivers/w1/masters/omap_hdq.c +++ b/drivers/w1/masters/omap_hdq.c @@ -704,7 +704,8 @@ static int omap_hdq_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { - ret = -ENXIO; + dev_dbg(&pdev->dev, "Failed to get IRQ: %d\n", irq); + ret = irq; goto err_irq; } diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 95ea7e6b1d99..74471e7aa5cc 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -728,6 +728,7 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) memcpy(&sl->reg_num, rn, sizeof(sl->reg_num)); atomic_set(&sl->refcnt, 1); atomic_inc(&sl->master->refcnt); + dev->slave_count++; /* slave modules need to be loaded in a context with unlocked mutex */ mutex_unlock(&dev->mutex); @@ -747,11 +748,11 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) sl->family = f; - err = __w1_attach_slave_device(sl); if (err < 0) { dev_err(&dev->dev, "%s: Attaching %s failed.\n", __func__, sl->name); + dev->slave_count--; w1_family_put(sl->family); atomic_dec(&sl->master->refcnt); kfree(sl); @@ -759,7 +760,6 @@ int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn) } sl->ttl = dev->slave_ttl; - dev->slave_count++; memcpy(msg.id.id, rn, sizeof(msg.id)); msg.type = W1_SLAVE_ADD; diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 8feab810aed9..7f188b8d0c67 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -7,9 +7,6 @@ obj-y += xenbus/ nostackp := $(call cc-option, -fno-stack-protector) CFLAGS_features.o := $(nostackp) -CFLAGS_efi.o += -fshort-wchar -LDFLAGS += $(call ld-option, --no-wchar-size-warning) - dom0-$(CONFIG_ARM64) += arm-device.o dom0-$(CONFIG_PCI) += pci.o dom0-$(CONFIG_USB_SUPPORT) += dbgp.o diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 50dcb68d8070..ab609255a0f3 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -780,6 +780,9 @@ static int __init balloon_init(void) } #endif + /* Init the xen-balloon driver. */ + xen_balloon_init(); + return 0; } subsys_initcall(balloon_init); diff --git a/drivers/xen/biomerge.c b/drivers/xen/biomerge.c index 4da69dbf7dca..1bdd02a6d6ac 100644 --- a/drivers/xen/biomerge.c +++ b/drivers/xen/biomerge.c @@ -10,8 +10,7 @@ bool xen_biovec_phys_mergeable(const struct bio_vec *vec1, unsigned long bfn1 = pfn_to_bfn(page_to_pfn(vec1->bv_page)); unsigned long bfn2 = pfn_to_bfn(page_to_pfn(vec2->bv_page)); - return __BIOVEC_PHYS_MERGEABLE(vec1, vec2) && - ((bfn1 == bfn2) || ((bfn1+1) == bfn2)); + return bfn1 + PFN_DOWN(vec1->bv_offset + vec1->bv_len) == bfn2; #else /* * XXX: Add support for merging bio_vec when using different page diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index b241bfa529ce..2d43118077e4 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -343,14 +343,6 @@ static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu) info->cpu = cpu; } -static void xen_evtchn_mask_all(void) -{ - unsigned int evtchn; - - for (evtchn = 0; evtchn < xen_evtchn_nr_channels(); evtchn++) - mask_evtchn(evtchn); -} - /** * notify_remote_via_irq - send event to remote end of event channel via irq * @irq: irq of event channel to send event to @@ -582,7 +574,7 @@ static void shutdown_pirq(struct irq_data *data) static void enable_pirq(struct irq_data *data) { - startup_pirq(data); + enable_dynirq(data); } static void disable_pirq(struct irq_data *data) @@ -1573,7 +1565,6 @@ void xen_irq_resume(void) struct irq_info *info; /* New event-channel space is not 'live' yet. */ - xen_evtchn_mask_all(); xen_evtchn_resume(); /* No IRQ <-> event-channel mappings. */ @@ -1681,6 +1672,7 @@ module_param(fifo_events, bool, 0); void __init xen_init_IRQ(void) { int ret = -EINVAL; + unsigned int evtchn; if (fifo_events) ret = xen_evtchn_fifo_init(); @@ -1692,7 +1684,8 @@ void __init xen_init_IRQ(void) BUG_ON(!evtchn_to_irq); /* No event channels are 'live' right now. */ - xen_evtchn_mask_all(); + for (evtchn = 0; evtchn < xen_evtchn_nr_channels(); evtchn++) + mask_evtchn(evtchn); pirq_needs_eoi = pirq_needs_eoi_flag; diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index d6786b87e13b..2c6a9114d332 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -42,6 +42,7 @@ #include <linux/delay.h> #include <linux/hardirq.h> #include <linux/workqueue.h> +#include <linux/ratelimit.h> #include <xen/xen.h> #include <xen/interface/xen.h> @@ -1072,8 +1073,14 @@ static int gnttab_expand(unsigned int req_entries) cur = nr_grant_frames; extra = ((req_entries + (grefs_per_grant_frame-1)) / grefs_per_grant_frame); - if (cur + extra > gnttab_max_grant_frames()) + if (cur + extra > gnttab_max_grant_frames()) { + pr_warn_ratelimited("xen/grant-table: max_grant_frames reached" + " cur=%u extra=%u limit=%u" + " gnttab_free_count=%u req_entries=%u\n", + cur, extra, gnttab_max_grant_frames(), + gnttab_free_count, req_entries); return -ENOSPC; + } rc = gnttab_map(cur, cur + extra - 1); if (rc == 0) diff --git a/drivers/xen/xen-balloon.c b/drivers/xen/xen-balloon.c index e7715cb62eef..e89136ab851e 100644 --- a/drivers/xen/xen-balloon.c +++ b/drivers/xen/xen-balloon.c @@ -59,6 +59,8 @@ static void watch_target(struct xenbus_watch *watch, { unsigned long long new_target; int err; + static bool watch_fired; + static long target_diff; err = xenbus_scanf(XBT_NIL, "memory", "target", "%llu", &new_target); if (err != 1) { @@ -69,7 +71,14 @@ static void watch_target(struct xenbus_watch *watch, /* The given memory/target value is in KiB, so it needs converting to * pages. PAGE_SHIFT converts bytes to pages, hence PAGE_SHIFT - 10. */ - balloon_set_new_target(new_target >> (PAGE_SHIFT - 10)); + new_target >>= PAGE_SHIFT - 10; + if (watch_fired) { + balloon_set_new_target(new_target - target_diff); + return; + } + + watch_fired = true; + target_diff = new_target - balloon_stats.target_pages; } static struct xenbus_watch target_watch = { .node = "memory/target", @@ -94,22 +103,15 @@ static struct notifier_block xenstore_notifier = { .notifier_call = balloon_init_watcher, }; -static int __init balloon_init(void) +void xen_balloon_init(void) { - if (!xen_domain()) - return -ENODEV; - - pr_info("Initialising balloon driver\n"); - register_balloon(&balloon_dev); register_xen_selfballooning(&balloon_dev); register_xenstore_notifier(&xenstore_notifier); - - return 0; } -subsys_initcall(balloon_init); +EXPORT_SYMBOL_GPL(xen_balloon_init); #define BALLOON_SHOW(name, format, args...) \ static ssize_t show_##name(struct device *dev, \ diff --git a/drivers/xen/xen-selfballoon.c b/drivers/xen/xen-selfballoon.c index 66620713242a..a67e955cacd1 100644 --- a/drivers/xen/xen-selfballoon.c +++ b/drivers/xen/xen-selfballoon.c @@ -151,8 +151,8 @@ static unsigned long frontswap_inertia_counter; static void frontswap_selfshrink(void) { static unsigned long cur_frontswap_pages; - static unsigned long last_frontswap_pages; - static unsigned long tgt_frontswap_pages; + unsigned long last_frontswap_pages; + unsigned long tgt_frontswap_pages; last_frontswap_pages = cur_frontswap_pages; cur_frontswap_pages = frontswap_curr_pages(); diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index e46080214955..3e59590c7254 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -857,6 +857,8 @@ static int xenwatch_thread(void *unused) struct list_head *ent; struct xs_watch_event *event; + xenwatch_pid = current->pid; + for (;;) { wait_event_interruptible(watch_events_waitq, !list_empty(&watch_events)); @@ -925,7 +927,6 @@ int xs_init(void) task = kthread_run(xenwatch_thread, NULL, "xenwatch"); if (IS_ERR(task)) return PTR_ERR(task); - xenwatch_pid = task->pid; /* shutdown watches for kexec boot */ xs_reset_watches(); diff --git a/drivers/xen/xenfs/super.c b/drivers/xen/xenfs/super.c index 967f069385d0..71ddfb4cf61c 100644 --- a/drivers/xen/xenfs/super.c +++ b/drivers/xen/xenfs/super.c @@ -87,7 +87,6 @@ static int __init xenfs_init(void) if (xen_domain()) return register_filesystem(&xenfs_type); - pr_info("not registering filesystem on non-xen platform\n"); return 0; } |