diff options
Diffstat (limited to 'drivers')
134 files changed, 8285 insertions, 2559 deletions
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 7edaccce664..a51df968131 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -71,9 +71,6 @@ enum ec_command { #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ #define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ -#define ACPI_EC_STORM_THRESHOLD 8 /* number of false interrupts - per one transaction */ - enum { EC_FLAGS_QUERY_PENDING, /* Query is pending */ EC_FLAGS_GPE_STORM, /* GPE storm detected */ @@ -87,6 +84,15 @@ static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; module_param(ec_delay, uint, 0644); MODULE_PARM_DESC(ec_delay, "Timeout(ms) waited until an EC command completes"); +/* + * If the number of false interrupts per one transaction exceeds + * this threshold, will think there is a GPE storm happened and + * will disable the GPE for normal transaction. + */ +static unsigned int ec_storm_threshold __read_mostly = 8; +module_param(ec_storm_threshold, uint, 0644); +MODULE_PARM_DESC(ec_storm_threshold, "Maxim false GPE numbers not considered as GPE storm"); + /* If we find an EC via the ECDT, we need to keep a ptr to its context */ /* External interfaces use first EC only, so remember */ typedef int (*acpi_ec_query_func) (void *data); @@ -319,7 +325,7 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) msleep(1); /* It is safe to enable the GPE outside of the transaction. */ acpi_enable_gpe(NULL, ec->gpe); - } else if (t->irq_count > ACPI_EC_STORM_THRESHOLD) { + } else if (t->irq_count > ec_storm_threshold) { pr_info(PREFIX "GPE storm detected, " "transactions will use polling mode\n"); set_bit(EC_FLAGS_GPE_STORM, &ec->flags); @@ -924,6 +930,17 @@ static int ec_flag_msi(const struct dmi_system_id *id) return 0; } +/* + * Clevo M720 notebook actually works ok with IRQ mode, if we lifted + * the GPE storm threshold back to 20 + */ +static int ec_enlarge_storm_threshold(const struct dmi_system_id *id) +{ + pr_debug("Setting the EC GPE storm threshold to 20\n"); + ec_storm_threshold = 20; + return 0; +} + static struct dmi_system_id __initdata ec_dmi_table[] = { { ec_skip_dsdt_scan, "Compal JFL92", { @@ -955,10 +972,13 @@ static struct dmi_system_id __initdata ec_dmi_table[] = { { ec_validate_ecdt, "ASUS hardware", { DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc.") }, NULL}, + { + ec_enlarge_storm_threshold, "CLEVO hardware", { + DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."), + DMI_MATCH(DMI_PRODUCT_NAME, "M720T/M730T"),}, NULL}, {}, }; - int __init acpi_ec_ecdt_probe(void) { acpi_status status; diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 3655ab92381..e8086c72530 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1132,7 +1132,7 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) int acpi_processor_hotplug(struct acpi_processor *pr) { int ret = 0; - struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id); + struct cpuidle_device *dev; if (disabled_by_idle_boot_param()) return 0; @@ -1147,6 +1147,7 @@ int acpi_processor_hotplug(struct acpi_processor *pr) if (!pr->flags.power_setup_done) return -ENODEV; + dev = per_cpu(acpi_cpuidle_device, pr->id); cpuidle_pause_and_lock(); cpuidle_disable_device(dev); acpi_processor_get_power_info(pr); diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index edda74a4340..804204d4199 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -708,6 +708,40 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal, return -EINVAL; } +static int thermal_get_trend(struct thermal_zone_device *thermal, + int trip, enum thermal_trend *trend) +{ + struct acpi_thermal *tz = thermal->devdata; + enum thermal_trip_type type; + int i; + + if (thermal_get_trip_type(thermal, trip, &type)) + return -EINVAL; + + if (type == THERMAL_TRIP_ACTIVE) { + /* aggressive active cooling */ + *trend = THERMAL_TREND_RAISING; + return 0; + } + + /* + * tz->temperature has already been updated by generic thermal layer, + * before this callback being invoked + */ + i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) + + (tz->trips.passive.tc2 + * (tz->temperature - tz->trips.passive.temperature)); + + if (i > 0) + *trend = THERMAL_TREND_RAISING; + else if (i < 0) + *trend = THERMAL_TREND_DROPPING; + else + *trend = THERMAL_TREND_STABLE; + return 0; +} + + static int thermal_notify(struct thermal_zone_device *thermal, int trip, enum thermal_trip_type trip_type) { @@ -731,11 +765,9 @@ static int thermal_notify(struct thermal_zone_device *thermal, int trip, return 0; } -typedef int (*cb)(struct thermal_zone_device *, int, - struct thermal_cooling_device *); static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev, - cb action) + bool bind) { struct acpi_device *device = cdev->devdata; struct acpi_thermal *tz = thermal->devdata; @@ -759,11 +791,19 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, i++) { handle = tz->trips.passive.devices.handles[i]; status = acpi_bus_get_device(handle, &dev); - if (ACPI_SUCCESS(status) && (dev == device)) { - result = action(thermal, trip, cdev); - if (result) - goto failed; - } + if (ACPI_FAILURE(status) || dev != device) + continue; + if (bind) + result = + thermal_zone_bind_cooling_device + (thermal, trip, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); + else + result = + thermal_zone_unbind_cooling_device + (thermal, trip, cdev); + if (result) + goto failed; } } @@ -776,11 +816,17 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, j++) { handle = tz->trips.active[i].devices.handles[j]; status = acpi_bus_get_device(handle, &dev); - if (ACPI_SUCCESS(status) && (dev == device)) { - result = action(thermal, trip, cdev); - if (result) - goto failed; - } + if (ACPI_FAILURE(status) || dev != device) + continue; + if (bind) + result = thermal_zone_bind_cooling_device + (thermal, trip, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT); + else + result = thermal_zone_unbind_cooling_device + (thermal, trip, cdev); + if (result) + goto failed; } } @@ -788,7 +834,14 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, handle = tz->devices.handles[i]; status = acpi_bus_get_device(handle, &dev); if (ACPI_SUCCESS(status) && (dev == device)) { - result = action(thermal, -1, cdev); + if (bind) + result = thermal_zone_bind_cooling_device + (thermal, -1, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT); + else + result = thermal_zone_unbind_cooling_device + (thermal, -1, cdev); if (result) goto failed; } @@ -802,16 +855,14 @@ static int acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev) { - return acpi_thermal_cooling_device_cb(thermal, cdev, - thermal_zone_bind_cooling_device); + return acpi_thermal_cooling_device_cb(thermal, cdev, true); } static int acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, struct thermal_cooling_device *cdev) { - return acpi_thermal_cooling_device_cb(thermal, cdev, - thermal_zone_unbind_cooling_device); + return acpi_thermal_cooling_device_cb(thermal, cdev, false); } static const struct thermal_zone_device_ops acpi_thermal_zone_ops = { @@ -823,6 +874,7 @@ static const struct thermal_zone_device_ops acpi_thermal_zone_ops = { .get_trip_type = thermal_get_trip_type, .get_trip_temp = thermal_get_trip_temp, .get_crit_temp = thermal_get_crit_temp, + .get_trend = thermal_get_trend, .notify = thermal_notify, }; @@ -849,15 +901,12 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz, &acpi_thermal_zone_ops, - tz->trips.passive.tc1, - tz->trips.passive.tc2, tz->trips.passive.tsp*100, tz->polling_frequency*100); else tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz, - &acpi_thermal_zone_ops, - 0, 0, 0, + &acpi_thermal_zone_ops, 0, tz->polling_frequency*100); if (IS_ERR(tz->thermal_zone)) return -ENODEV; diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index f26afdb1a70..93211df52aa 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1182,17 +1182,20 @@ ssize_t tpm_write(struct file *file, const char __user *buf, size_t size, loff_t *off) { struct tpm_chip *chip = file->private_data; - size_t in_size = size, out_size; + size_t in_size = size; + ssize_t out_size; /* cannot perform a write until the read has cleared - either via tpm_read or a user_read_timer timeout */ - while (atomic_read(&chip->data_pending) != 0) - msleep(TPM_TIMEOUT); - - mutex_lock(&chip->buffer_mutex); + either via tpm_read or a user_read_timer timeout. + This also prevents splitted buffered writes from blocking here. + */ + if (atomic_read(&chip->data_pending) != 0) + return -EBUSY; if (in_size > TPM_BUFSIZE) - in_size = TPM_BUFSIZE; + return -E2BIG; + + mutex_lock(&chip->buffer_mutex); if (copy_from_user (chip->data_buffer, (void __user *) buf, in_size)) { @@ -1202,6 +1205,10 @@ ssize_t tpm_write(struct file *file, const char __user *buf, /* atomic tpm command send and result receive */ out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE); + if (out_size < 0) { + mutex_unlock(&chip->buffer_mutex); + return out_size; + } atomic_set(&chip->data_pending, out_size); mutex_unlock(&chip->buffer_mutex); @@ -1259,6 +1266,7 @@ void tpm_remove_hardware(struct device *dev) misc_deregister(&chip->vendor.miscdev); sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); + tpm_remove_ppi(&dev->kobj); tpm_bios_log_teardown(chip->bios_dir); /* write it this way to be explicit (chip->dev == dev) */ @@ -1476,7 +1484,7 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, goto put_device; } - if (sys_add_ppi(&dev->kobj)) { + if (tpm_add_ppi(&dev->kobj)) { misc_deregister(&chip->vendor.miscdev); goto put_device; } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 02c266aa2bf..8ef7649a50a 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -329,10 +329,15 @@ extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, wait_queue_head_t *); #ifdef CONFIG_ACPI -extern ssize_t sys_add_ppi(struct kobject *parent); +extern int tpm_add_ppi(struct kobject *); +extern void tpm_remove_ppi(struct kobject *); #else -static inline ssize_t sys_add_ppi(struct kobject *parent) +static inline int tpm_add_ppi(struct kobject *parent) { return 0; } + +static inline void tpm_remove_ppi(struct kobject *parent) +{ +} #endif diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index f27b58cfae9..720ebcf29fd 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -444,18 +444,20 @@ static struct attribute *ppi_attrs[] = { &dev_attr_vs_operations.attr, NULL, }; static struct attribute_group ppi_attr_grp = { + .name = "ppi", .attrs = ppi_attrs }; -ssize_t sys_add_ppi(struct kobject *parent) +int tpm_add_ppi(struct kobject *parent) { - struct kobject *ppi; - ppi = kobject_create_and_add("ppi", parent); - if (sysfs_create_group(ppi, &ppi_attr_grp)) - return -EFAULT; - else - return 0; + return sysfs_create_group(parent, &ppi_attr_grp); +} +EXPORT_SYMBOL_GPL(tpm_add_ppi); + +void tpm_remove_ppi(struct kobject *parent) +{ + sysfs_remove_group(parent, &ppi_attr_grp); } -EXPORT_SYMBOL_GPL(sys_add_ppi); +EXPORT_SYMBOL_GPL(tpm_remove_ppi); MODULE_LICENSE("GPL"); diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index e28f6ea46f1..7f15b8514a1 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -368,7 +368,7 @@ EXPORT_SYMBOL_GPL(cpuidle_enable_device); */ void cpuidle_disable_device(struct cpuidle_device *dev) { - if (!dev->enabled) + if (!dev || !dev->enabled) return; if (!cpuidle_get_driver() || !cpuidle_curr_governor) return; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c74e73b2069..c4633de6446 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -334,16 +334,6 @@ config SENSORS_DA9052_ADC This driver can also be built as module. If so, the module will be called da9052-hwmon. -config SENSORS_EXYNOS4_TMU - tristate "Temperature sensor on Samsung EXYNOS4" - depends on ARCH_EXYNOS4 - help - If you say yes here you get support for TMU (Thermal Management - Unit) on SAMSUNG EXYNOS4 series of SoC. - - This driver can also be built as a module. If so, the module - will be called exynos4-tmu. - config SENSORS_I5K_AMB tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets" depends on PCI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index a62ce17ddbf..8d5fcb5e8e9 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -50,7 +50,6 @@ obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o -obj-$(CONFIG_SENSORS_EXYNOS4_TMU) += exynos4_tmu.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o obj-$(CONFIG_SENSORS_F75375S) += f75375s.o diff --git a/drivers/hwmon/exynos4_tmu.c b/drivers/hwmon/exynos4_tmu.c deleted file mode 100644 index e912059140c..00000000000 --- a/drivers/hwmon/exynos4_tmu.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * exynos4_tmu.c - Samsung EXYNOS4 TMU (Thermal Management Unit) - * - * Copyright (C) 2011 Samsung Electronics - * Donggeun Kim <dg77.kim@samsung.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <linux/module.h> -#include <linux/err.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/clk.h> -#include <linux/workqueue.h> -#include <linux/sysfs.h> -#include <linux/kobject.h> -#include <linux/io.h> -#include <linux/mutex.h> - -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> - -#include <linux/platform_data/exynos4_tmu.h> - -#define EXYNOS4_TMU_REG_TRIMINFO 0x0 -#define EXYNOS4_TMU_REG_CONTROL 0x20 -#define EXYNOS4_TMU_REG_STATUS 0x28 -#define EXYNOS4_TMU_REG_CURRENT_TEMP 0x40 -#define EXYNOS4_TMU_REG_THRESHOLD_TEMP 0x44 -#define EXYNOS4_TMU_REG_TRIG_LEVEL0 0x50 -#define EXYNOS4_TMU_REG_TRIG_LEVEL1 0x54 -#define EXYNOS4_TMU_REG_TRIG_LEVEL2 0x58 -#define EXYNOS4_TMU_REG_TRIG_LEVEL3 0x5C -#define EXYNOS4_TMU_REG_PAST_TEMP0 0x60 -#define EXYNOS4_TMU_REG_PAST_TEMP1 0x64 -#define EXYNOS4_TMU_REG_PAST_TEMP2 0x68 -#define EXYNOS4_TMU_REG_PAST_TEMP3 0x6C -#define EXYNOS4_TMU_REG_INTEN 0x70 -#define EXYNOS4_TMU_REG_INTSTAT 0x74 -#define EXYNOS4_TMU_REG_INTCLEAR 0x78 - -#define EXYNOS4_TMU_GAIN_SHIFT 8 -#define EXYNOS4_TMU_REF_VOLTAGE_SHIFT 24 - -#define EXYNOS4_TMU_TRIM_TEMP_MASK 0xff -#define EXYNOS4_TMU_CORE_ON 3 -#define EXYNOS4_TMU_CORE_OFF 2 -#define EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET 50 -#define EXYNOS4_TMU_TRIG_LEVEL0_MASK 0x1 -#define EXYNOS4_TMU_TRIG_LEVEL1_MASK 0x10 -#define EXYNOS4_TMU_TRIG_LEVEL2_MASK 0x100 -#define EXYNOS4_TMU_TRIG_LEVEL3_MASK 0x1000 -#define EXYNOS4_TMU_INTCLEAR_VAL 0x1111 - -struct exynos4_tmu_data { - struct exynos4_tmu_platform_data *pdata; - struct device *hwmon_dev; - struct resource *mem; - void __iomem *base; - int irq; - struct work_struct irq_work; - struct mutex lock; - struct clk *clk; - u8 temp_error1, temp_error2; -}; - -/* - * TMU treats temperature as a mapped temperature code. - * The temperature is converted differently depending on the calibration type. - */ -static int temp_to_code(struct exynos4_tmu_data *data, u8 temp) -{ - struct exynos4_tmu_platform_data *pdata = data->pdata; - int temp_code; - - /* temp should range between 25 and 125 */ - if (temp < 25 || temp > 125) { - temp_code = -EINVAL; - goto out; - } - - switch (pdata->cal_type) { - case TYPE_TWO_POINT_TRIMMING: - temp_code = (temp - 25) * - (data->temp_error2 - data->temp_error1) / - (85 - 25) + data->temp_error1; - break; - case TYPE_ONE_POINT_TRIMMING: - temp_code = temp + data->temp_error1 - 25; - break; - default: - temp_code = temp + EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET; - break; - } -out: - return temp_code; -} - -/* - * Calculate a temperature value from a temperature code. - * The unit of the temperature is degree Celsius. - */ -static int code_to_temp(struct exynos4_tmu_data *data, u8 temp_code) -{ - struct exynos4_tmu_platform_data *pdata = data->pdata; - int temp; - - /* temp_code should range between 75 and 175 */ - if (temp_code < 75 || temp_code > 175) { - temp = -ENODATA; - goto out; - } - - switch (pdata->cal_type) { - case TYPE_TWO_POINT_TRIMMING: - temp = (temp_code - data->temp_error1) * (85 - 25) / - (data->temp_error2 - data->temp_error1) + 25; - break; - case TYPE_ONE_POINT_TRIMMING: - temp = temp_code - data->temp_error1 + 25; - break; - default: - temp = temp_code - EXYNOS4_TMU_DEF_CODE_TO_TEMP_OFFSET; - break; - } -out: - return temp; -} - -static int exynos4_tmu_initialize(struct platform_device *pdev) -{ - struct exynos4_tmu_data *data = platform_get_drvdata(pdev); - struct exynos4_tmu_platform_data *pdata = data->pdata; - unsigned int status, trim_info; - int ret = 0, threshold_code; - - mutex_lock(&data->lock); - clk_enable(data->clk); - - status = readb(data->base + EXYNOS4_TMU_REG_STATUS); - if (!status) { - ret = -EBUSY; - goto out; - } - - /* Save trimming info in order to perform calibration */ - trim_info = readl(data->base + EXYNOS4_TMU_REG_TRIMINFO); - data->temp_error1 = trim_info & EXYNOS4_TMU_TRIM_TEMP_MASK; - data->temp_error2 = ((trim_info >> 8) & EXYNOS4_TMU_TRIM_TEMP_MASK); - - /* Write temperature code for threshold */ - threshold_code = temp_to_code(data, pdata->threshold); - if (threshold_code < 0) { - ret = threshold_code; - goto out; - } - writeb(threshold_code, - data->base + EXYNOS4_TMU_REG_THRESHOLD_TEMP); - - writeb(pdata->trigger_levels[0], - data->base + EXYNOS4_TMU_REG_TRIG_LEVEL0); - writeb(pdata->trigger_levels[1], - data->base + EXYNOS4_TMU_REG_TRIG_LEVEL1); - writeb(pdata->trigger_levels[2], - data->base + EXYNOS4_TMU_REG_TRIG_LEVEL2); - writeb(pdata->trigger_levels[3], - data->base + EXYNOS4_TMU_REG_TRIG_LEVEL3); - - writel(EXYNOS4_TMU_INTCLEAR_VAL, - data->base + EXYNOS4_TMU_REG_INTCLEAR); -out: - clk_disable(data->clk); - mutex_unlock(&data->lock); - - return ret; -} - -static void exynos4_tmu_control(struct platform_device *pdev, bool on) -{ - struct exynos4_tmu_data *data = platform_get_drvdata(pdev); - struct exynos4_tmu_platform_data *pdata = data->pdata; - unsigned int con, interrupt_en; - - mutex_lock(&data->lock); - clk_enable(data->clk); - - con = pdata->reference_voltage << EXYNOS4_TMU_REF_VOLTAGE_SHIFT | - pdata->gain << EXYNOS4_TMU_GAIN_SHIFT; - if (on) { - con |= EXYNOS4_TMU_CORE_ON; - interrupt_en = pdata->trigger_level3_en << 12 | - pdata->trigger_level2_en << 8 | - pdata->trigger_level1_en << 4 | - pdata->trigger_level0_en; - } else { - con |= EXYNOS4_TMU_CORE_OFF; - interrupt_en = 0; /* Disable all interrupts */ - } - writel(interrupt_en, data->base + EXYNOS4_TMU_REG_INTEN); - writel(con, data->base + EXYNOS4_TMU_REG_CONTROL); - - clk_disable(data->clk); - mutex_unlock(&data->lock); -} - -static int exynos4_tmu_read(struct exynos4_tmu_data *data) -{ - u8 temp_code; - int temp; - - mutex_lock(&data->lock); - clk_enable(data->clk); - - temp_code = readb(data->base + EXYNOS4_TMU_REG_CURRENT_TEMP); - temp = code_to_temp(data, temp_code); - - clk_disable(data->clk); - mutex_unlock(&data->lock); - - return temp; -} - -static void exynos4_tmu_work(struct work_struct *work) -{ - struct exynos4_tmu_data *data = container_of(work, - struct exynos4_tmu_data, irq_work); - - mutex_lock(&data->lock); - clk_enable(data->clk); - - writel(EXYNOS4_TMU_INTCLEAR_VAL, data->base + EXYNOS4_TMU_REG_INTCLEAR); - - kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE); - - enable_irq(data->irq); - - clk_disable(data->clk); - mutex_unlock(&data->lock); -} - -static irqreturn_t exynos4_tmu_irq(int irq, void *id) -{ - struct exynos4_tmu_data *data = id; - - disable_irq_nosync(irq); - schedule_work(&data->irq_work); - - return IRQ_HANDLED; -} - -static ssize_t exynos4_tmu_show_name(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "exynos4-tmu\n"); -} - -static ssize_t exynos4_tmu_show_temp(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct exynos4_tmu_data *data = dev_get_drvdata(dev); - int ret; - - ret = exynos4_tmu_read(data); - if (ret < 0) - return ret; - - /* convert from degree Celsius to millidegree Celsius */ - return sprintf(buf, "%d\n", ret * 1000); -} - -static ssize_t exynos4_tmu_show_alarm(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct exynos4_tmu_data *data = dev_get_drvdata(dev); - struct exynos4_tmu_platform_data *pdata = data->pdata; - int temp; - unsigned int trigger_level; - - temp = exynos4_tmu_read(data); - if (temp < 0) - return temp; - - trigger_level = pdata->threshold + pdata->trigger_levels[attr->index]; - - return sprintf(buf, "%d\n", !!(temp > trigger_level)); -} - -static ssize_t exynos4_tmu_show_level(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct exynos4_tmu_data *data = dev_get_drvdata(dev); - struct exynos4_tmu_platform_data *pdata = data->pdata; - unsigned int temp = pdata->threshold + - pdata->trigger_levels[attr->index]; - - return sprintf(buf, "%u\n", temp * 1000); -} - -static DEVICE_ATTR(name, S_IRUGO, exynos4_tmu_show_name, NULL); -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, exynos4_tmu_show_temp, NULL, 0); - -static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, - exynos4_tmu_show_alarm, NULL, 1); -static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, - exynos4_tmu_show_alarm, NULL, 2); -static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, - exynos4_tmu_show_alarm, NULL, 3); - -static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, exynos4_tmu_show_level, NULL, 1); -static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, exynos4_tmu_show_level, NULL, 2); -static SENSOR_DEVICE_ATTR(temp1_emergency, S_IRUGO, - exynos4_tmu_show_level, NULL, 3); - -static struct attribute *exynos4_tmu_attributes[] = { - &dev_attr_name.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_emergency.dev_attr.attr, - NULL, -}; - -static const struct attribute_group exynos4_tmu_attr_group = { - .attrs = exynos4_tmu_attributes, -}; - -static int __devinit exynos4_tmu_probe(struct platform_device *pdev) -{ - struct exynos4_tmu_data *data; - struct exynos4_tmu_platform_data *pdata = pdev->dev.platform_data; - int ret; - - if (!pdata) { - dev_err(&pdev->dev, "No platform init data supplied.\n"); - return -ENODEV; - } - - data = kzalloc(sizeof(struct exynos4_tmu_data), GFP_KERNEL); - if (!data) { - dev_err(&pdev->dev, "Failed to allocate driver structure\n"); - return -ENOMEM; - } - - data->irq = platform_get_irq(pdev, 0); - if (data->irq < 0) { - ret = data->irq; - dev_err(&pdev->dev, "Failed to get platform irq\n"); - goto err_free; - } - - INIT_WORK(&data->irq_work, exynos4_tmu_work); - - data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!data->mem) { - ret = -ENOENT; - dev_err(&pdev->dev, "Failed to get platform resource\n"); - goto err_free; - } - - data->mem = request_mem_region(data->mem->start, - resource_size(data->mem), pdev->name); - if (!data->mem) { - ret = -ENODEV; - dev_err(&pdev->dev, "Failed to request memory region\n"); - goto err_free; - } - - data->base = ioremap(data->mem->start, resource_size(data->mem)); - if (!data->base) { - ret = -ENODEV; - dev_err(&pdev->dev, "Failed to ioremap memory\n"); - goto err_mem_region; - } - - ret = request_irq(data->irq, exynos4_tmu_irq, - IRQF_TRIGGER_RISING, - "exynos4-tmu", data); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); - goto err_io_remap; - } - - data->clk = clk_get(NULL, "tmu_apbif"); - if (IS_ERR(data->clk)) { - ret = PTR_ERR(data->clk); - dev_err(&pdev->dev, "Failed to get clock\n"); - goto err_irq; - } - - data->pdata = pdata; - platform_set_drvdata(pdev, data); - mutex_init(&data->lock); - - ret = exynos4_tmu_initialize(pdev); - if (ret) { - dev_err(&pdev->dev, "Failed to initialize TMU\n"); - goto err_clk; - } - - ret = sysfs_create_group(&pdev->dev.kobj, &exynos4_tmu_attr_group); - if (ret) { - dev_err(&pdev->dev, "Failed to create sysfs group\n"); - goto err_clk; - } - - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - dev_err(&pdev->dev, "Failed to register hwmon device\n"); - goto err_create_group; - } - - exynos4_tmu_control(pdev, true); - - return 0; - -err_create_group: - sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group); -err_clk: - platform_set_drvdata(pdev, NULL); - clk_put(data->clk); -err_irq: - free_irq(data->irq, data); -err_io_remap: - iounmap(data->base); -err_mem_region: - release_mem_region(data->mem->start, resource_size(data->mem)); -err_free: - kfree(data); - - return ret; -} - -static int __devexit exynos4_tmu_remove(struct platform_device *pdev) -{ - struct exynos4_tmu_data *data = platform_get_drvdata(pdev); - - exynos4_tmu_control(pdev, false); - - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &exynos4_tmu_attr_group); - - clk_put(data->clk); - - free_irq(data->irq, data); - - iounmap(data->base); - release_mem_region(data->mem->start, resource_size(data->mem)); - - platform_set_drvdata(pdev, NULL); - - kfree(data); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int exynos4_tmu_suspend(struct device *dev) -{ - exynos4_tmu_control(to_platform_device(dev), false); - - return 0; -} - -static int exynos4_tmu_resume(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - - exynos4_tmu_initialize(pdev); - exynos4_tmu_control(pdev, true); - - return 0; -} - -static SIMPLE_DEV_PM_OPS(exynos4_tmu_pm, - exynos4_tmu_suspend, exynos4_tmu_resume); -#define EXYNOS4_TMU_PM &exynos4_tmu_pm -#else -#define EXYNOS4_TMU_PM NULL -#endif - -static struct platform_driver exynos4_tmu_driver = { - .driver = { - .name = "exynos4-tmu", - .owner = THIS_MODULE, - .pm = EXYNOS4_TMU_PM, - }, - .probe = exynos4_tmu_probe, - .remove = __devexit_p(exynos4_tmu_remove), -}; - -module_platform_driver(exynos4_tmu_driver); - -MODULE_DESCRIPTION("EXYNOS4 TMU Driver"); -MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:exynos4-tmu"); diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 118d0300f1f..6ae2ac47c9c 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -23,11 +23,11 @@ #include <linux/input/mt.h> #include <linux/major.h> #include <linux/device.h> +#include <linux/cdev.h> #include "input-compat.h" struct evdev { int open; - int minor; struct input_handle handle; wait_queue_head_t wait; struct evdev_client __rcu *grab; @@ -35,6 +35,7 @@ struct evdev { spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; + struct cdev cdev; bool exist; }; @@ -51,9 +52,6 @@ struct evdev_client { struct input_event buffer[]; }; -static struct evdev *evdev_table[EVDEV_MINORS]; -static DEFINE_MUTEX(evdev_table_mutex); - static void __pass_event(struct evdev_client *client, const struct input_event *event) { @@ -310,35 +308,16 @@ static unsigned int evdev_compute_buffer_size(struct input_dev *dev) static int evdev_open(struct inode *inode, struct file *file) { - struct evdev *evdev; + struct evdev *evdev = container_of(inode->i_cdev, struct evdev, cdev); + unsigned int bufsize = evdev_compute_buffer_size(evdev->handle.dev); struct evdev_client *client; - int i = iminor(inode) - EVDEV_MINOR_BASE; - unsigned int bufsize; int error; - if (i >= EVDEV_MINORS) - return -ENODEV; - - error = mutex_lock_interruptible(&evdev_table_mutex); - if (error) - return error; - evdev = evdev_table[i]; - if (evdev) - get_device(&evdev->dev); - mutex_unlock(&evdev_table_mutex); - - if (!evdev) - return -ENODEV; - - bufsize = evdev_compute_buffer_size(evdev->handle.dev); - client = kzalloc(sizeof(struct evdev_client) + bufsize * sizeof(struct input_event), GFP_KERNEL); - if (!client) { - error = -ENOMEM; - goto err_put_evdev; - } + if (!client) + return -ENOMEM; client->bufsize = bufsize; spin_lock_init(&client->buffer_lock); @@ -352,13 +331,12 @@ static int evdev_open(struct inode *inode, struct file *file) file->private_data = client; nonseekable_open(inode, file); + get_device(&evdev->dev); return 0; err_free_client: evdev_detach_client(evdev, client); kfree(client); - err_put_evdev: - put_device(&evdev->dev); return error; } @@ -942,26 +920,6 @@ static const struct file_operations evdev_fops = { .llseek = no_llseek, }; -static int evdev_install_chrdev(struct evdev *evdev) -{ - /* - * No need to do any locking here as calls to connect and - * disconnect are serialized by the input core - */ - evdev_table[evdev->minor] = evdev; - return 0; -} - -static void evdev_remove_chrdev(struct evdev *evdev) -{ - /* - * Lock evdev table to prevent race with evdev_open() - */ - mutex_lock(&evdev_table_mutex); - evdev_table[evdev->minor] = NULL; - mutex_unlock(&evdev_table_mutex); -} - /* * Mark device non-existent. This disables writes, ioctls and * prevents new users from opening the device. Already posted @@ -980,7 +938,8 @@ static void evdev_cleanup(struct evdev *evdev) evdev_mark_dead(evdev); evdev_hangup(evdev); - evdev_remove_chrdev(evdev); + + cdev_del(&evdev->cdev); /* evdev is marked dead so no one else accesses evdev->open */ if (evdev->open) { @@ -991,43 +950,47 @@ static void evdev_cleanup(struct evdev *evdev) /* * Create new evdev device. Note that input core serializes calls - * to connect and disconnect so we don't need to lock evdev_table here. + * to connect and disconnect. */ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct evdev *evdev; int minor; + int dev_no; int error; - for (minor = 0; minor < EVDEV_MINORS; minor++) - if (!evdev_table[minor]) - break; - - if (minor == EVDEV_MINORS) { - pr_err("no more free evdev devices\n"); - return -ENFILE; + minor = input_get_new_minor(EVDEV_MINOR_BASE, EVDEV_MINORS, true); + if (minor < 0) { + error = minor; + pr_err("failed to reserve new minor: %d\n", error); + return error; } evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL); - if (!evdev) - return -ENOMEM; + if (!evdev) { + error = -ENOMEM; + goto err_free_minor; + } INIT_LIST_HEAD(&evdev->client_list); spin_lock_init(&evdev->client_lock); mutex_init(&evdev->mutex); init_waitqueue_head(&evdev->wait); - - dev_set_name(&evdev->dev, "event%d", minor); evdev->exist = true; - evdev->minor = minor; + + dev_no = minor; + /* Normalize device number if it falls into legacy range */ + if (dev_no < EVDEV_MINOR_BASE + EVDEV_MINORS) + dev_no -= EVDEV_MINOR_BASE; + dev_set_name(&evdev->dev, "event%d", dev_no); evdev->handle.dev = input_get_device(dev); evdev->handle.name = dev_name(&evdev->dev); evdev->handle.handler = handler; evdev->handle.private = evdev; - evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor); + evdev->dev.devt = MKDEV(INPUT_MAJOR, minor); evdev->dev.class = &input_class; evdev->dev.parent = &dev->dev; evdev->dev.release = evdev_free; @@ -1037,7 +1000,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, if (error) goto err_free_evdev; - error = evdev_install_chrdev(evdev); + cdev_init(&evdev->cdev, &evdev_fops); + error = cdev_add(&evdev->cdev, evdev->dev.devt, 1); if (error) goto err_unregister_handle; @@ -1053,6 +1017,8 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, input_unregister_handle(&evdev->handle); err_free_evdev: put_device(&evdev->dev); + err_free_minor: + input_free_minor(minor); return error; } @@ -1062,6 +1028,7 @@ static void evdev_disconnect(struct input_handle *handle) device_del(&evdev->dev); evdev_cleanup(evdev); + input_free_minor(MINOR(evdev->dev.devt)); input_unregister_handle(handle); put_device(&evdev->dev); } @@ -1078,7 +1045,7 @@ static struct input_handler evdev_handler = { .events = evdev_events, .connect = evdev_connect, .disconnect = evdev_disconnect, - .fops = &evdev_fops, + .legacy_minors = true, .minor = EVDEV_MINOR_BASE, .name = "evdev", .id_table = evdev_ids, diff --git a/drivers/input/input.c b/drivers/input/input.c index ace3f7c4226..53a0ddee787 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -14,6 +14,7 @@ #include <linux/init.h> #include <linux/types.h> +#include <linux/idr.h> #include <linux/input/mt.h> #include <linux/module.h> #include <linux/slab.h> @@ -32,7 +33,9 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>"); MODULE_DESCRIPTION("Input core"); MODULE_LICENSE("GPL"); -#define INPUT_DEVICES 256 +#define INPUT_MAX_CHAR_DEVICES 1024 +#define INPUT_FIRST_DYNAMIC_DEV 256 +static DEFINE_IDA(input_ida); static LIST_HEAD(input_dev_list); static LIST_HEAD(input_handler_list); @@ -45,8 +48,6 @@ static LIST_HEAD(input_handler_list); */ static DEFINE_MUTEX(input_mutex); -static struct input_handler *input_table[8]; - static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 }; static inline int is_event_supported(unsigned int code, @@ -1218,7 +1219,7 @@ static int input_handlers_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "N: Number=%u Name=%s", state->pos, handler->name); if (handler->filter) seq_puts(seq, " (filter)"); - if (handler->fops) + if (handler->legacy_minors) seq_printf(seq, " Minor=%d", handler->minor); seq_putc(seq, '\n'); @@ -2016,22 +2017,14 @@ EXPORT_SYMBOL(input_unregister_device); int input_register_handler(struct input_handler *handler) { struct input_dev *dev; - int retval; + int error; - retval = mutex_lock_interruptible(&input_mutex); - if (retval) - return retval; + error = mutex_lock_interruptible(&input_mutex); + if (error) + return error; INIT_LIST_HEAD(&handler->h_list); - if (handler->fops != NULL) { - if (input_table[handler->minor >> 5]) { - retval = -EBUSY; - goto out; - } - input_table[handler->minor >> 5] = handler; - } - list_add_tail(&handler->node, &input_handler_list); list_for_each_entry(dev, &input_dev_list, node) @@ -2039,9 +2032,8 @@ int input_register_handler(struct input_handler *handler) input_wakeup_procfs_readers(); - out: mutex_unlock(&input_mutex); - return retval; + return 0; } EXPORT_SYMBOL(input_register_handler); @@ -2064,9 +2056,6 @@ void input_unregister_handler(struct input_handler *handler) list_del_init(&handler->node); - if (handler->fops != NULL) - input_table[handler->minor >> 5] = NULL; - input_wakeup_procfs_readers(); mutex_unlock(&input_mutex); @@ -2183,51 +2172,52 @@ void input_unregister_handle(struct input_handle *handle) } EXPORT_SYMBOL(input_unregister_handle); -static int input_open_file(struct inode *inode, struct file *file) +/** + * input_get_new_minor - allocates a new input minor number + * @legacy_base: beginning or the legacy range to be searched + * @legacy_num: size of legacy range + * @allow_dynamic: whether we can also take ID from the dynamic range + * + * This function allocates a new device minor for from input major namespace. + * Caller can request legacy minor by specifying @legacy_base and @legacy_num + * parameters and whether ID can be allocated from dynamic range if there are + * no free IDs in legacy range. + */ +int input_get_new_minor(int legacy_base, unsigned int legacy_num, + bool allow_dynamic) { - struct input_handler *handler; - const struct file_operations *old_fops, *new_fops = NULL; - int err; - - err = mutex_lock_interruptible(&input_mutex); - if (err) - return err; - - /* No load-on-demand here? */ - handler = input_table[iminor(inode) >> 5]; - if (handler) - new_fops = fops_get(handler->fops); - - mutex_unlock(&input_mutex); - /* - * That's _really_ odd. Usually NULL ->open means "nothing special", - * not "no device". Oh, well... + * This function should be called from input handler's ->connect() + * methods, which are serialized with input_mutex, so no additional + * locking is needed here. */ - if (!new_fops || !new_fops->open) { - fops_put(new_fops); - err = -ENODEV; - goto out; + if (legacy_base >= 0) { + int minor = ida_simple_get(&input_ida, + legacy_base, + legacy_base + legacy_num, + GFP_KERNEL); + if (minor >= 0 || !allow_dynamic) + return minor; } - old_fops = file->f_op; - file->f_op = new_fops; - - err = new_fops->open(inode, file); - if (err) { - fops_put(file->f_op); - file->f_op = fops_get(old_fops); - } - fops_put(old_fops); -out: - return err; + return ida_simple_get(&input_ida, + INPUT_FIRST_DYNAMIC_DEV, INPUT_MAX_CHAR_DEVICES, + GFP_KERNEL); } +EXPORT_SYMBOL(input_get_new_minor); -static const struct file_operations input_fops = { - .owner = THIS_MODULE, - .open = input_open_file, - .llseek = noop_llseek, -}; +/** + * input_free_minor - release previously allocated minor + * @minor: minor to be released + * + * This function releases previously allocated input minor so that it can be + * reused later. + */ +void input_free_minor(unsigned int minor) +{ + ida_simple_remove(&input_ida, minor); +} +EXPORT_SYMBOL(input_free_minor); static int __init input_init(void) { @@ -2243,7 +2233,8 @@ static int __init input_init(void) if (err) goto fail1; - err = register_chrdev(INPUT_MAJOR, "input", &input_fops); + err = register_chrdev_region(MKDEV(INPUT_MAJOR, 0), + INPUT_MAX_CHAR_DEVICES, "input"); if (err) { pr_err("unable to register char major %d", INPUT_MAJOR); goto fail2; @@ -2259,7 +2250,8 @@ static int __init input_init(void) static void __exit input_exit(void) { input_proc_exit(); - unregister_chrdev(INPUT_MAJOR, "input"); + unregister_chrdev_region(MKDEV(INPUT_MAJOR, 0), + INPUT_MAX_CHAR_DEVICES); class_unregister(&input_class); } diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 78f323ea1e4..b62b5891f39 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -27,6 +27,7 @@ #include <linux/poll.h> #include <linux/init.h> #include <linux/device.h> +#include <linux/cdev.h> MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Joystick device interfaces"); @@ -39,13 +40,13 @@ MODULE_LICENSE("GPL"); struct joydev { int open; - int minor; struct input_handle handle; wait_queue_head_t wait; struct list_head client_list; spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; + struct cdev cdev; bool exist; struct js_corr corr[ABS_CNT]; @@ -70,9 +71,6 @@ struct joydev_client { struct list_head node; }; -static struct joydev *joydev_table[JOYDEV_MINORS]; -static DEFINE_MUTEX(joydev_table_mutex); - static int joydev_correct(int value, struct js_corr *corr) { switch (corr->type) { @@ -252,30 +250,14 @@ static int joydev_release(struct inode *inode, struct file *file) static int joydev_open(struct inode *inode, struct file *file) { + struct joydev *joydev = + container_of(inode->i_cdev, struct joydev, cdev); struct joydev_client *client; - struct joydev *joydev; - int i = iminor(inode) - JOYDEV_MINOR_BASE; int error; - if (i >= JOYDEV_MINORS) - return -ENODEV; - - error = mutex_lock_interruptible(&joydev_table_mutex); - if (error) - return error; - joydev = joydev_table[i]; - if (joydev) - get_device(&joydev->dev); - mutex_unlock(&joydev_table_mutex); - - if (!joydev) - return -ENODEV; - client = kzalloc(sizeof(struct joydev_client), GFP_KERNEL); - if (!client) { - error = -ENOMEM; - goto err_put_joydev; - } + if (!client) + return -ENOMEM; spin_lock_init(&client->buffer_lock); client->joydev = joydev; @@ -288,13 +270,12 @@ static int joydev_open(struct inode *inode, struct file *file) file->private_data = client; nonseekable_open(inode, file); + get_device(&joydev->dev); return 0; err_free_client: joydev_detach_client(joydev, client); kfree(client); - err_put_joydev: - put_device(&joydev->dev); return error; } @@ -742,19 +723,6 @@ static const struct file_operations joydev_fops = { .llseek = no_llseek, }; -static int joydev_install_chrdev(struct joydev *joydev) -{ - joydev_table[joydev->minor] = joydev; - return 0; -} - -static void joydev_remove_chrdev(struct joydev *joydev) -{ - mutex_lock(&joydev_table_mutex); - joydev_table[joydev->minor] = NULL; - mutex_unlock(&joydev_table_mutex); -} - /* * Mark device non-existent. This disables writes, ioctls and * prevents new users from opening the device. Already posted @@ -773,7 +741,8 @@ static void joydev_cleanup(struct joydev *joydev) joydev_mark_dead(joydev); joydev_hangup(joydev); - joydev_remove_chrdev(joydev); + + cdev_del(&joydev->cdev); /* joydev is marked dead so no one else accesses joydev->open */ if (joydev->open) @@ -798,30 +767,33 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id) { struct joydev *joydev; - int i, j, t, minor; + int i, j, t, minor, dev_no; int error; - for (minor = 0; minor < JOYDEV_MINORS; minor++) - if (!joydev_table[minor]) - break; - - if (minor == JOYDEV_MINORS) { - pr_err("no more free joydev devices\n"); - return -ENFILE; + minor = input_get_new_minor(JOYDEV_MINOR_BASE, JOYDEV_MINORS, true); + if (minor < 0) { + error = minor; + pr_err("failed to reserve new minor: %d\n", error); + return error; } joydev = kzalloc(sizeof(struct joydev), GFP_KERNEL); - if (!joydev) - return -ENOMEM; + if (!joydev) { + error = -ENOMEM; + goto err_free_minor; + } INIT_LIST_HEAD(&joydev->client_list); spin_lock_init(&joydev->client_lock); mutex_init(&joydev->mutex); init_waitqueue_head(&joydev->wait); - - dev_set_name(&joydev->dev, "js%d", minor); joydev->exist = true; - joydev->minor = minor; + + dev_no = minor; + /* Normalize device number if it falls into legacy range */ + if (dev_no < JOYDEV_MINOR_BASE + JOYDEV_MINORS) + dev_no -= JOYDEV_MINOR_BASE; + dev_set_name(&joydev->dev, "js%d", dev_no); joydev->handle.dev = input_get_device(dev); joydev->handle.name = dev_name(&joydev->dev); @@ -875,7 +847,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, } } - joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor); + joydev->dev.devt = MKDEV(INPUT_MAJOR, minor); joydev->dev.class = &input_class; joydev->dev.parent = &dev->dev; joydev->dev.release = joydev_free; @@ -885,7 +857,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, if (error) goto err_free_joydev; - error = joydev_install_chrdev(joydev); + cdev_init(&joydev->cdev, &joydev_fops); + error = cdev_add(&joydev->cdev, joydev->dev.devt, 1); if (error) goto err_unregister_handle; @@ -901,6 +874,8 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, input_unregister_handle(&joydev->handle); err_free_joydev: put_device(&joydev->dev); + err_free_minor: + input_free_minor(minor); return error; } @@ -910,6 +885,7 @@ static void joydev_disconnect(struct input_handle *handle) device_del(&joydev->dev); joydev_cleanup(joydev); + input_free_minor(MINOR(joydev->dev.devt)); input_unregister_handle(handle); put_device(&joydev->dev); } @@ -961,7 +937,7 @@ static struct input_handler joydev_handler = { .match = joydev_match, .connect = joydev_connect, .disconnect = joydev_disconnect, - .fops = &joydev_fops, + .legacy_minors = true, .minor = JOYDEV_MINOR_BASE, .name = "joydev", .id_table = joydev_ids, diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 277e26dc910..9d7a111486f 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -431,6 +431,12 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) goto err_unmap_base; } + error = clk_prepare(keypad->clk); + if (error) { + dev_err(&pdev->dev, "keypad clock prepare failed\n"); + goto err_put_clk; + } + keypad->input_dev = input_dev; keypad->pdev = pdev; keypad->row_shift = row_shift; @@ -461,7 +467,7 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) keypad->keycodes, input_dev); if (error) { dev_err(&pdev->dev, "failed to build keymap\n"); - goto err_put_clk; + goto err_unprepare_clk; } input_set_capability(input_dev, EV_MSC, MSC_SCAN); @@ -503,6 +509,8 @@ err_free_irq: pm_runtime_disable(&pdev->dev); device_init_wakeup(&pdev->dev, 0); platform_set_drvdata(pdev, NULL); +err_unprepare_clk: + clk_unprepare(keypad->clk); err_put_clk: clk_put(keypad->clk); samsung_keypad_dt_gpio_free(keypad); @@ -531,6 +539,7 @@ static int __devexit samsung_keypad_remove(struct platform_device *pdev) */ free_irq(keypad->irq, keypad); + clk_unprepare(keypad->clk); clk_put(keypad->clk); samsung_keypad_dt_gpio_free(keypad); diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index 964e43d81e2..a1b4c37956b 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -24,10 +24,8 @@ #include <linux/random.h> #include <linux/major.h> #include <linux/device.h> +#include <linux/cdev.h> #include <linux/kernel.h> -#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX -#include <linux/miscdevice.h> -#endif MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>"); MODULE_DESCRIPTION("Mouse (ExplorerPS/2) device interfaces"); @@ -61,17 +59,18 @@ struct mousedev_hw_data { struct mousedev { int open; - int minor; struct input_handle handle; wait_queue_head_t wait; struct list_head client_list; spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; + struct cdev cdev; bool exist; + bool is_mixdev; struct list_head mixdev_node; - int mixdev_open; + bool opened_by_mixdev; struct mousedev_hw_data packet; unsigned int pkt_count; @@ -114,10 +113,6 @@ struct mousedev_client { static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 }; static unsigned char mousedev_imex_seq[] = { 0xf3, 200, 0xf3, 200, 0xf3, 80 }; -static struct input_handler mousedev_handler; - -static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; -static DEFINE_MUTEX(mousedev_table_mutex); static struct mousedev *mousedev_mix; static LIST_HEAD(mousedev_mix_list); @@ -433,7 +428,7 @@ static int mousedev_open_device(struct mousedev *mousedev) if (retval) return retval; - if (mousedev->minor == MOUSEDEV_MIX) + if (mousedev->is_mixdev) mixdev_open_devices(); else if (!mousedev->exist) retval = -ENODEV; @@ -451,7 +446,7 @@ static void mousedev_close_device(struct mousedev *mousedev) { mutex_lock(&mousedev->mutex); - if (mousedev->minor == MOUSEDEV_MIX) + if (mousedev->is_mixdev) mixdev_close_devices(); else if (mousedev->exist && !--mousedev->open) input_close_device(&mousedev->handle); @@ -472,11 +467,11 @@ static void mixdev_open_devices(void) return; list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { - if (!mousedev->mixdev_open) { + if (!mousedev->opened_by_mixdev) { if (mousedev_open_device(mousedev)) continue; - mousedev->mixdev_open = 1; + mousedev->opened_by_mixdev = true; } } } @@ -494,8 +489,8 @@ static void mixdev_close_devices(void) return; list_for_each_entry(mousedev, &mousedev_mix_list, mixdev_node) { - if (mousedev->mixdev_open) { - mousedev->mixdev_open = 0; + if (mousedev->opened_by_mixdev) { + mousedev->opened_by_mixdev = false; mousedev_close_device(mousedev); } } @@ -538,35 +533,17 @@ static int mousedev_open(struct inode *inode, struct file *file) struct mousedev_client *client; struct mousedev *mousedev; int error; - int i; #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX if (imajor(inode) == MISC_MAJOR) - i = MOUSEDEV_MIX; + mousedev = mousedev_mix; else #endif - i = iminor(inode) - MOUSEDEV_MINOR_BASE; - - if (i >= MOUSEDEV_MINORS) - return -ENODEV; - - error = mutex_lock_interruptible(&mousedev_table_mutex); - if (error) - return error; - - mousedev = mousedev_table[i]; - if (mousedev) - get_device(&mousedev->dev); - mutex_unlock(&mousedev_table_mutex); - - if (!mousedev) - return -ENODEV; + mousedev = container_of(inode->i_cdev, struct mousedev, cdev); client = kzalloc(sizeof(struct mousedev_client), GFP_KERNEL); - if (!client) { - error = -ENOMEM; - goto err_put_mousedev; - } + if (!client) + return -ENOMEM; spin_lock_init(&client->packet_lock); client->pos_x = xres / 2; @@ -579,13 +556,14 @@ static int mousedev_open(struct inode *inode, struct file *file) goto err_free_client; file->private_data = client; + nonseekable_open(inode, file); + + get_device(&mousedev->dev); return 0; err_free_client: mousedev_detach_client(mousedev, client); kfree(client); - err_put_mousedev: - put_device(&mousedev->dev); return error; } @@ -785,29 +763,16 @@ static unsigned int mousedev_poll(struct file *file, poll_table *wait) } static const struct file_operations mousedev_fops = { - .owner = THIS_MODULE, - .read = mousedev_read, - .write = mousedev_write, - .poll = mousedev_poll, - .open = mousedev_open, - .release = mousedev_release, - .fasync = mousedev_fasync, - .llseek = noop_llseek, + .owner = THIS_MODULE, + .read = mousedev_read, + .write = mousedev_write, + .poll = mousedev_poll, + .open = mousedev_open, + .release = mousedev_release, + .fasync = mousedev_fasync, + .llseek = noop_llseek, }; -static int mousedev_install_chrdev(struct mousedev *mousedev) -{ - mousedev_table[mousedev->minor] = mousedev; - return 0; -} - -static void mousedev_remove_chrdev(struct mousedev *mousedev) -{ - mutex_lock(&mousedev_table_mutex); - mousedev_table[mousedev->minor] = NULL; - mutex_unlock(&mousedev_table_mutex); -} - /* * Mark device non-existent. This disables writes, ioctls and * prevents new users from opening the device. Already posted @@ -842,24 +807,50 @@ static void mousedev_cleanup(struct mousedev *mousedev) mousedev_mark_dead(mousedev); mousedev_hangup(mousedev); - mousedev_remove_chrdev(mousedev); + + cdev_del(&mousedev->cdev); /* mousedev is marked dead so no one else accesses mousedev->open */ if (mousedev->open) input_close_device(handle); } +static int mousedev_reserve_minor(bool mixdev) +{ + int minor; + + if (mixdev) { + minor = input_get_new_minor(MOUSEDEV_MIX, 1, false); + if (minor < 0) + pr_err("failed to reserve mixdev minor: %d\n", minor); + } else { + minor = input_get_new_minor(MOUSEDEV_MINOR_BASE, + MOUSEDEV_MINORS, true); + if (minor < 0) + pr_err("failed to reserve new minor: %d\n", minor); + } + + return minor; +} + static struct mousedev *mousedev_create(struct input_dev *dev, struct input_handler *handler, - int minor) + bool mixdev) { struct mousedev *mousedev; + int minor; int error; + minor = mousedev_reserve_minor(mixdev); + if (minor < 0) { + error = minor; + goto err_out; + } + mousedev = kzalloc(sizeof(struct mousedev), GFP_KERNEL); if (!mousedev) { error = -ENOMEM; - goto err_out; + goto err_free_minor; } INIT_LIST_HEAD(&mousedev->client_list); @@ -867,16 +858,21 @@ static struct mousedev *mousedev_create(struct input_dev *dev, spin_lock_init(&mousedev->client_lock); mutex_init(&mousedev->mutex); lockdep_set_subclass(&mousedev->mutex, - minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0); + mixdev ? SINGLE_DEPTH_NESTING : 0); init_waitqueue_head(&mousedev->wait); - if (minor == MOUSEDEV_MIX) + if (mixdev) { dev_set_name(&mousedev->dev, "mice"); - else - dev_set_name(&mousedev->dev, "mouse%d", minor); + } else { + int dev_no = minor; + /* Normalize device number if it falls into legacy range */ + if (dev_no < MOUSEDEV_MINOR_BASE + MOUSEDEV_MINORS) + dev_no -= MOUSEDEV_MINOR_BASE; + dev_set_name(&mousedev->dev, "mouse%d", dev_no); + } - mousedev->minor = minor; mousedev->exist = true; + mousedev->is_mixdev = mixdev; mousedev->handle.dev = input_get_device(dev); mousedev->handle.name = dev_name(&mousedev->dev); mousedev->handle.handler = handler; @@ -885,17 +881,18 @@ static struct mousedev *mousedev_create(struct input_dev *dev, mousedev->dev.class = &input_class; if (dev) mousedev->dev.parent = &dev->dev; - mousedev->dev.devt = MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + minor); + mousedev->dev.devt = MKDEV(INPUT_MAJOR, minor); mousedev->dev.release = mousedev_free; device_initialize(&mousedev->dev); - if (minor != MOUSEDEV_MIX) { + if (!mixdev) { error = input_register_handle(&mousedev->handle); if (error) goto err_free_mousedev; } - error = mousedev_install_chrdev(mousedev); + cdev_init(&mousedev->cdev, &mousedev_fops); + error = cdev_add(&mousedev->cdev, mousedev->dev.devt, 1); if (error) goto err_unregister_handle; @@ -908,10 +905,12 @@ static struct mousedev *mousedev_create(struct input_dev *dev, err_cleanup_mousedev: mousedev_cleanup(mousedev); err_unregister_handle: - if (minor != MOUSEDEV_MIX) + if (!mixdev) input_unregister_handle(&mousedev->handle); err_free_mousedev: put_device(&mousedev->dev); + err_free_minor: + input_free_minor(minor); err_out: return ERR_PTR(error); } @@ -920,7 +919,8 @@ static void mousedev_destroy(struct mousedev *mousedev) { device_del(&mousedev->dev); mousedev_cleanup(mousedev); - if (mousedev->minor != MOUSEDEV_MIX) + input_free_minor(MINOR(mousedev->dev.devt)); + if (!mousedev->is_mixdev) input_unregister_handle(&mousedev->handle); put_device(&mousedev->dev); } @@ -938,7 +938,7 @@ static int mixdev_add_device(struct mousedev *mousedev) if (retval) goto out; - mousedev->mixdev_open = 1; + mousedev->opened_by_mixdev = true; } get_device(&mousedev->dev); @@ -953,8 +953,8 @@ static void mixdev_remove_device(struct mousedev *mousedev) { mutex_lock(&mousedev_mix->mutex); - if (mousedev->mixdev_open) { - mousedev->mixdev_open = 0; + if (mousedev->opened_by_mixdev) { + mousedev->opened_by_mixdev = false; mousedev_close_device(mousedev); } @@ -969,19 +969,9 @@ static int mousedev_connect(struct input_handler *handler, const struct input_device_id *id) { struct mousedev *mousedev; - int minor; int error; - for (minor = 0; minor < MOUSEDEV_MINORS; minor++) - if (!mousedev_table[minor]) - break; - - if (minor == MOUSEDEV_MINORS) { - pr_err("no more free mousedev devices\n"); - return -ENFILE; - } - - mousedev = mousedev_create(dev, handler, minor); + mousedev = mousedev_create(dev, handler, false); if (IS_ERR(mousedev)) return PTR_ERR(mousedev); @@ -1054,27 +1044,53 @@ static const struct input_device_id mousedev_ids[] = { MODULE_DEVICE_TABLE(input, mousedev_ids); static struct input_handler mousedev_handler = { - .event = mousedev_event, - .connect = mousedev_connect, - .disconnect = mousedev_disconnect, - .fops = &mousedev_fops, - .minor = MOUSEDEV_MINOR_BASE, - .name = "mousedev", - .id_table = mousedev_ids, + .event = mousedev_event, + .connect = mousedev_connect, + .disconnect = mousedev_disconnect, + .legacy_minors = true, + .minor = MOUSEDEV_MINOR_BASE, + .name = "mousedev", + .id_table = mousedev_ids, }; #ifdef CONFIG_INPUT_MOUSEDEV_PSAUX +#include <linux/miscdevice.h> + static struct miscdevice psaux_mouse = { - PSMOUSE_MINOR, "psaux", &mousedev_fops + .minor = PSMOUSE_MINOR, + .name = "psaux", + .fops = &mousedev_fops, }; -static int psaux_registered; + +static bool psaux_registered; + +static void __init mousedev_psaux_register(void) +{ + int error; + + error = misc_register(&psaux_mouse); + if (error) + pr_warn("could not register psaux device, error: %d\n", + error); + else + psaux_registered = true; +} + +static void __exit mousedev_psaux_unregister(void) +{ + if (psaux_registered) + misc_deregister(&psaux_mouse); +} +#else +static inline void mousedev_psaux_register(void) { } +static inline void mousedev_psaux_unregister(void) { } #endif static int __init mousedev_init(void) { int error; - mousedev_mix = mousedev_create(NULL, &mousedev_handler, MOUSEDEV_MIX); + mousedev_mix = mousedev_create(NULL, &mousedev_handler, true); if (IS_ERR(mousedev_mix)) return PTR_ERR(mousedev_mix); @@ -1084,14 +1100,7 @@ static int __init mousedev_init(void) return error; } -#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX - error = misc_register(&psaux_mouse); - if (error) - pr_warn("could not register psaux device, error: %d\n", - error); - else - psaux_registered = 1; -#endif + mousedev_psaux_register(); pr_info("PS/2 mouse device common for all mice\n"); @@ -1100,10 +1109,7 @@ static int __init mousedev_init(void) static void __exit mousedev_exit(void) { -#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX - if (psaux_registered) - misc_deregister(&psaux_mouse); -#endif + mousedev_psaux_unregister(); input_unregister_handler(&mousedev_handler); mousedev_destroy(mousedev_mix); } diff --git a/drivers/input/tablet/wacom_sys.c b/drivers/input/tablet/wacom_sys.c index 0d3219f2974..9edf9806cff 100644 --- a/drivers/input/tablet/wacom_sys.c +++ b/drivers/input/tablet/wacom_sys.c @@ -172,6 +172,76 @@ static void wacom_close(struct input_dev *dev) } /* + * Calculate the resolution of the X or Y axis, given appropriate HID data. + * This function is little more than hidinput_calc_abs_res stripped down. + */ +static int wacom_calc_hid_res(int logical_extents, int physical_extents, + unsigned char unit, unsigned char exponent) +{ + int prev, unit_exponent; + + /* Check if the extents are sane */ + if (logical_extents <= 0 || physical_extents <= 0) + return 0; + + /* Get signed value of nybble-sized twos-compliment exponent */ + unit_exponent = exponent; + if (unit_exponent > 7) + unit_exponent -= 16; + + /* Convert physical_extents to millimeters */ + if (unit == 0x11) { /* If centimeters */ + unit_exponent += 1; + } else if (unit == 0x13) { /* If inches */ + prev = physical_extents; + physical_extents *= 254; + if (physical_extents < prev) + return 0; + unit_exponent -= 1; + } else { + return 0; + } + + /* Apply negative unit exponent */ + for (; unit_exponent < 0; unit_exponent++) { + prev = logical_extents; + logical_extents *= 10; + if (logical_extents < prev) + return 0; + } + /* Apply positive unit exponent */ + for (; unit_exponent > 0; unit_exponent--) { + prev = physical_extents; + physical_extents *= 10; + if (physical_extents < prev) + return 0; + } + + /* Calculate resolution */ + return logical_extents / physical_extents; +} + +/* + * The physical dimension specified by the HID descriptor is likely not in + * the "100th of a mm" units expected by wacom_calculate_touch_res. This + * function adjusts the value of [xy]_phy based on the unit and exponent + * provided by the HID descriptor. If an error occurs durring conversion + * (e.g. from the unit being left unspecified) [xy]_phy is not modified. + */ +static void wacom_fix_phy_from_hid(struct wacom_features *features) +{ + int xres = wacom_calc_hid_res(features->x_max, features->x_phy, + features->unit, features->unitExpo); + int yres = wacom_calc_hid_res(features->y_max, features->y_phy, + features->unit, features->unitExpo); + + if (xres > 0 && yres > 0) { + features->x_phy = (100 * features->x_max) / xres; + features->y_phy = (100 * features->y_max) / yres; + } +} + +/* * Static values for max X/Y and resolution of Pen interface is stored in * features. This mean physical size of active area can be computed. * This is useful to do when Pen and Touch have same active area of tablet. @@ -432,56 +502,52 @@ static int wacom_parse_hid(struct usb_interface *intf, return result; } -static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_features *features) +static int wacom_set_device_mode(struct usb_interface *intf, int report_id, int length, int mode) { unsigned char *rep_data; - int limit = 0, report_id = 2; - int error = -ENOMEM; + int error = -ENOMEM, limit = 0; - rep_data = kmalloc(4, GFP_KERNEL); + rep_data = kzalloc(length, GFP_KERNEL); if (!rep_data) return error; - /* ask to report Wacom data */ + rep_data[0] = report_id; + rep_data[1] = mode; + + do { + error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, + report_id, rep_data, length, 1); + if (error >= 0) + error = wacom_get_report(intf, WAC_HID_FEATURE_REPORT, + report_id, rep_data, length, 1); + } while ((error < 0 || rep_data[1] != mode) && limit++ < WAC_MSG_RETRIES); + + kfree(rep_data); + + return error < 0 ? error : 0; +} + +/* + * Switch the tablet into its most-capable mode. Wacom tablets are + * typically configured to power-up in a mode which sends mouse-like + * reports to the OS. To get absolute position, pressure data, etc. + * from the tablet, it is necessary to switch the tablet out of this + * mode and into one which sends the full range of tablet data. + */ +static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_features *features) +{ if (features->device_type == BTN_TOOL_FINGER) { - /* if it is an MT Tablet PC touch */ if (features->type > TABLETPC) { - do { - rep_data[0] = 3; - rep_data[1] = 4; - rep_data[2] = 0; - rep_data[3] = 0; - report_id = 3; - error = wacom_set_report(intf, - WAC_HID_FEATURE_REPORT, - report_id, - rep_data, 4, 1); - if (error >= 0) - error = wacom_get_report(intf, - WAC_HID_FEATURE_REPORT, - report_id, - rep_data, 4, 1); - } while ((error < 0 || rep_data[1] != 4) && - limit++ < WAC_MSG_RETRIES); + /* MT Tablet PC touch */ + return wacom_set_device_mode(intf, 3, 4, 4); + } + } else if (features->device_type == BTN_TOOL_PEN) { + if (features->type <= BAMBOO_PT && features->type != WIRELESS) { + return wacom_set_device_mode(intf, 2, 2, 2); } - } else if (features->type <= BAMBOO_PT && - features->type != WIRELESS && - features->device_type == BTN_TOOL_PEN) { - do { - rep_data[0] = 2; - rep_data[1] = 2; - error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT, - report_id, rep_data, 2, 1); - if (error >= 0) - error = wacom_get_report(intf, - WAC_HID_FEATURE_REPORT, - report_id, rep_data, 2, 1); - } while ((error < 0 || rep_data[1] != 2) && limit++ < WAC_MSG_RETRIES); } - kfree(rep_data); - - return error < 0 ? error : 0; + return 0; } static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, @@ -531,6 +597,7 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf, error = wacom_parse_hid(intf, hid_desc, features); if (error) goto out; + wacom_fix_phy_from_hid(features); out: return error; diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 08b462b6c0d..c3468c8dbd8 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -25,6 +25,11 @@ #define WACOM_INTUOS_RES 100 #define WACOM_INTUOS3_RES 200 +/* Scale factor relating reported contact size to logical contact area. + * 2^14/pi is a good approximation on Intuos5 and 3rd-gen Bamboo + */ +#define WACOM_CONTACT_AREA_SCALE 2607 + static int wacom_penpartner_irq(struct wacom_wac *wacom) { unsigned char *data = wacom->data; @@ -326,7 +331,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) /* Enter report */ if ((data[1] & 0xfc) == 0xc0) { - if (features->type >= INTUOS5S && features->type <= INTUOS5L) + if (features->quirks == WACOM_QUIRK_MULTI_INPUT) wacom->shared->stylus_in_proximity = true; /* serial number of the tool */ @@ -414,7 +419,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) /* Exit report */ if ((data[1] & 0xfe) == 0x80) { - if (features->type >= INTUOS5S && features->type <= INTUOS5L) + if (features->quirks == WACOM_QUIRK_MULTI_INPUT) wacom->shared->stylus_in_proximity = false; /* @@ -1043,11 +1048,19 @@ static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) if (touch) { int x = (data[2] << 4) | (data[4] >> 4); int y = (data[3] << 4) | (data[4] & 0x0f); - int w = data[6]; + int a = data[5]; + + // "a" is a scaled-down area which we assume is roughly + // circular and which can be described as: a=(pi*r^2)/C. + int x_res = input_abs_get_res(input, ABS_X); + int y_res = input_abs_get_res(input, ABS_Y); + int width = 2 * int_sqrt(a * WACOM_CONTACT_AREA_SCALE); + int height = width * y_res / x_res; input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); - input_report_abs(input, ABS_MT_TOUCH_MAJOR, w); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, width); + input_report_abs(input, ABS_MT_TOUCH_MINOR, height); } } @@ -1533,7 +1546,9 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, input_mt_init_slots(input_dev, features->touch_max, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, 255, 0, 0); + 0, features->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, + 0, features->y_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, features->x_max, @@ -1641,7 +1656,10 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, 255, 0, 0); + 0, features->x_max, 0, 0); + input_set_abs_params(input_dev, + ABS_MT_TOUCH_MINOR, + 0, features->y_max, 0, 0); } input_set_abs_params(input_dev, ABS_MT_POSITION_X, diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index e92615d0b1b..1df2396af00 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -320,10 +320,8 @@ static bool mxt_object_writable(unsigned int type) static void mxt_dump_message(struct device *dev, struct mxt_message *message) { - dev_dbg(dev, "reportid: %u\tmessage: %02x %02x %02x %02x %02x %02x %02x\n", - message->reportid, message->message[0], message->message[1], - message->message[2], message->message[3], message->message[4], - message->message[5], message->message[6]); + dev_dbg(dev, "reportid: %u\tmessage: %*ph\n", + message->reportid, 7, message->message); } static int mxt_check_bootloader(struct i2c_client *client, diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index a1e76015082..61d78fa03b1 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -595,7 +595,7 @@ isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) j = ipc->num / (sizeof(long) * 8); i = ipc->num % (sizeof(long) * 8); if (j < 8) - protos[j] |= (0x1 << i); + protos[j] |= (1UL << i); ipc = ipc->next; } if ((r = set_arg(argp, protos, 8 * sizeof(long)))) diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index d949b781f6f..91a02eeeb31 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -216,6 +216,13 @@ config DM_BUFIO as a cache, holding recently-read blocks in memory and performing delayed writes. +config DM_BIO_PRISON + tristate + depends on BLK_DEV_DM && EXPERIMENTAL + ---help--- + Some bio locking schemes used by other device-mapper targets + including thin provisioning. + source "drivers/md/persistent-data/Kconfig" config DM_CRYPT @@ -247,6 +254,7 @@ config DM_THIN_PROVISIONING tristate "Thin provisioning target (EXPERIMENTAL)" depends on BLK_DEV_DM && EXPERIMENTAL select DM_PERSISTENT_DATA + select DM_BIO_PRISON ---help--- Provides thin provisioning and snapshots that share a data store. diff --git a/drivers/md/Makefile b/drivers/md/Makefile index 8b2e0dffe82..94dce8b4932 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_MD_FAULTY) += faulty.o obj-$(CONFIG_BLK_DEV_MD) += md-mod.o obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o obj-$(CONFIG_DM_BUFIO) += dm-bufio.o +obj-$(CONFIG_DM_BIO_PRISON) += dm-bio-prison.o obj-$(CONFIG_DM_CRYPT) += dm-crypt.o obj-$(CONFIG_DM_DELAY) += dm-delay.o obj-$(CONFIG_DM_FLAKEY) += dm-flakey.o diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 94e7f6ba2e1..7155945f8eb 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -163,20 +163,17 @@ static struct md_rdev *next_active_rdev(struct md_rdev *rdev, struct mddev *mdde * As devices are only added or removed when raid_disk is < 0 and * nr_pending is 0 and In_sync is clear, the entries we return will * still be in the same position on the list when we re-enter - * list_for_each_continue_rcu. + * list_for_each_entry_continue_rcu. */ - struct list_head *pos; rcu_read_lock(); if (rdev == NULL) /* start at the beginning */ - pos = &mddev->disks; + rdev = list_entry_rcu(&mddev->disks, struct md_rdev, same_set); else { /* release the previous rdev and start from there. */ rdev_dec_pending(rdev, mddev); - pos = &rdev->same_set; } - list_for_each_continue_rcu(pos, &mddev->disks) { - rdev = list_entry(pos, struct md_rdev, same_set); + list_for_each_entry_continue_rcu(rdev, &mddev->disks, same_set) { if (rdev->raid_disk >= 0 && !test_bit(Faulty, &rdev->flags)) { /* this is a usable devices */ @@ -473,14 +470,10 @@ static int bitmap_new_disk_sb(struct bitmap *bitmap) { bitmap_super_t *sb; unsigned long chunksize, daemon_sleep, write_behind; - int err = -EINVAL; bitmap->storage.sb_page = alloc_page(GFP_KERNEL); - if (IS_ERR(bitmap->storage.sb_page)) { - err = PTR_ERR(bitmap->storage.sb_page); - bitmap->storage.sb_page = NULL; - return err; - } + if (bitmap->storage.sb_page == NULL) + return -ENOMEM; bitmap->storage.sb_page->index = 0; sb = kmap_atomic(bitmap->storage.sb_page); diff --git a/drivers/md/dm-bio-prison.c b/drivers/md/dm-bio-prison.c new file mode 100644 index 00000000000..e4e84156745 --- /dev/null +++ b/drivers/md/dm-bio-prison.c @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2012 Red Hat, Inc. + * + * This file is released under the GPL. + */ + +#include "dm.h" +#include "dm-bio-prison.h" + +#include <linux/spinlock.h> +#include <linux/mempool.h> +#include <linux/module.h> +#include <linux/slab.h> + +/*----------------------------------------------------------------*/ + +struct dm_bio_prison_cell { + struct hlist_node list; + struct dm_bio_prison *prison; + struct dm_cell_key key; + struct bio *holder; + struct bio_list bios; +}; + +struct dm_bio_prison { + spinlock_t lock; + mempool_t *cell_pool; + + unsigned nr_buckets; + unsigned hash_mask; + struct hlist_head *cells; +}; + +/*----------------------------------------------------------------*/ + +static uint32_t calc_nr_buckets(unsigned nr_cells) +{ + uint32_t n = 128; + + nr_cells /= 4; + nr_cells = min(nr_cells, 8192u); + + while (n < nr_cells) + n <<= 1; + + return n; +} + +static struct kmem_cache *_cell_cache; + +/* + * @nr_cells should be the number of cells you want in use _concurrently_. + * Don't confuse it with the number of distinct keys. + */ +struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells) +{ + unsigned i; + uint32_t nr_buckets = calc_nr_buckets(nr_cells); + size_t len = sizeof(struct dm_bio_prison) + + (sizeof(struct hlist_head) * nr_buckets); + struct dm_bio_prison *prison = kmalloc(len, GFP_KERNEL); + + if (!prison) + return NULL; + + spin_lock_init(&prison->lock); + prison->cell_pool = mempool_create_slab_pool(nr_cells, _cell_cache); + if (!prison->cell_pool) { + kfree(prison); + return NULL; + } + + prison->nr_buckets = nr_buckets; + prison->hash_mask = nr_buckets - 1; + prison->cells = (struct hlist_head *) (prison + 1); + for (i = 0; i < nr_buckets; i++) + INIT_HLIST_HEAD(prison->cells + i); + + return prison; +} +EXPORT_SYMBOL_GPL(dm_bio_prison_create); + +void dm_bio_prison_destroy(struct dm_bio_prison *prison) +{ + mempool_destroy(prison->cell_pool); + kfree(prison); +} +EXPORT_SYMBOL_GPL(dm_bio_prison_destroy); + +static uint32_t hash_key(struct dm_bio_prison *prison, struct dm_cell_key *key) +{ + const unsigned long BIG_PRIME = 4294967291UL; + uint64_t hash = key->block * BIG_PRIME; + + return (uint32_t) (hash & prison->hash_mask); +} + +static int keys_equal(struct dm_cell_key *lhs, struct dm_cell_key *rhs) +{ + return (lhs->virtual == rhs->virtual) && + (lhs->dev == rhs->dev) && + (lhs->block == rhs->block); +} + +static struct dm_bio_prison_cell *__search_bucket(struct hlist_head *bucket, + struct dm_cell_key *key) +{ + struct dm_bio_prison_cell *cell; + struct hlist_node *tmp; + + hlist_for_each_entry(cell, tmp, bucket, list) + if (keys_equal(&cell->key, key)) + return cell; + + return NULL; +} + +/* + * This may block if a new cell needs allocating. You must ensure that + * cells will be unlocked even if the calling thread is blocked. + * + * Returns 1 if the cell was already held, 0 if @inmate is the new holder. + */ +int dm_bio_detain(struct dm_bio_prison *prison, struct dm_cell_key *key, + struct bio *inmate, struct dm_bio_prison_cell **ref) +{ + int r = 1; + unsigned long flags; + uint32_t hash = hash_key(prison, key); + struct dm_bio_prison_cell *cell, *cell2; + + BUG_ON(hash > prison->nr_buckets); + + spin_lock_irqsave(&prison->lock, flags); + + cell = __search_bucket(prison->cells + hash, key); + if (cell) { + bio_list_add(&cell->bios, inmate); + goto out; + } + + /* + * Allocate a new cell + */ + spin_unlock_irqrestore(&prison->lock, flags); + cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO); + spin_lock_irqsave(&prison->lock, flags); + + /* + * We've been unlocked, so we have to double check that + * nobody else has inserted this cell in the meantime. + */ + cell = __search_bucket(prison->cells + hash, key); + if (cell) { + mempool_free(cell2, prison->cell_pool); + bio_list_add(&cell->bios, inmate); + goto out; + } + + /* + * Use new cell. + */ + cell = cell2; + + cell->prison = prison; + memcpy(&cell->key, key, sizeof(cell->key)); + cell->holder = inmate; + bio_list_init(&cell->bios); + hlist_add_head(&cell->list, prison->cells + hash); + + r = 0; + +out: + spin_unlock_irqrestore(&prison->lock, flags); + + *ref = cell; + + return r; +} +EXPORT_SYMBOL_GPL(dm_bio_detain); + +/* + * @inmates must have been initialised prior to this call + */ +static void __cell_release(struct dm_bio_prison_cell *cell, struct bio_list *inmates) +{ + struct dm_bio_prison *prison = cell->prison; + + hlist_del(&cell->list); + + if (inmates) { + bio_list_add(inmates, cell->holder); + bio_list_merge(inmates, &cell->bios); + } + + mempool_free(cell, prison->cell_pool); +} + +void dm_cell_release(struct dm_bio_prison_cell *cell, struct bio_list *bios) +{ + unsigned long flags; + struct dm_bio_prison *prison = cell->prison; + + spin_lock_irqsave(&prison->lock, flags); + __cell_release(cell, bios); + spin_unlock_irqrestore(&prison->lock, flags); +} +EXPORT_SYMBOL_GPL(dm_cell_release); + +/* + * There are a couple of places where we put a bio into a cell briefly + * before taking it out again. In these situations we know that no other + * bio may be in the cell. This function releases the cell, and also does + * a sanity check. + */ +static void __cell_release_singleton(struct dm_bio_prison_cell *cell, struct bio *bio) +{ + BUG_ON(cell->holder != bio); + BUG_ON(!bio_list_empty(&cell->bios)); + + __cell_release(cell, NULL); +} + +void dm_cell_release_singleton(struct dm_bio_prison_cell *cell, struct bio *bio) +{ + unsigned long flags; + struct dm_bio_prison *prison = cell->prison; + + spin_lock_irqsave(&prison->lock, flags); + __cell_release_singleton(cell, bio); + spin_unlock_irqrestore(&prison->lock, flags); +} +EXPORT_SYMBOL_GPL(dm_cell_release_singleton); + +/* + * Sometimes we don't want the holder, just the additional bios. + */ +static void __cell_release_no_holder(struct dm_bio_prison_cell *cell, struct bio_list *inmates) +{ + struct dm_bio_prison *prison = cell->prison; + + hlist_del(&cell->list); + bio_list_merge(inmates, &cell->bios); + + mempool_free(cell, prison->cell_pool); +} + +void dm_cell_release_no_holder(struct dm_bio_prison_cell *cell, struct bio_list *inmates) +{ + unsigned long flags; + struct dm_bio_prison *prison = cell->prison; + + spin_lock_irqsave(&prison->lock, flags); + __cell_release_no_holder(cell, inmates); + spin_unlock_irqrestore(&prison->lock, flags); +} +EXPORT_SYMBOL_GPL(dm_cell_release_no_holder); + +void dm_cell_error(struct dm_bio_prison_cell *cell) +{ + struct dm_bio_prison *prison = cell->prison; + struct bio_list bios; + struct bio *bio; + unsigned long flags; + + bio_list_init(&bios); + + spin_lock_irqsave(&prison->lock, flags); + __cell_release(cell, &bios); + spin_unlock_irqrestore(&prison->lock, flags); + + while ((bio = bio_list_pop(&bios))) + bio_io_error(bio); +} +EXPORT_SYMBOL_GPL(dm_cell_error); + +/*----------------------------------------------------------------*/ + +#define DEFERRED_SET_SIZE 64 + +struct dm_deferred_entry { + struct dm_deferred_set *ds; + unsigned count; + struct list_head work_items; +}; + +struct dm_deferred_set { + spinlock_t lock; + unsigned current_entry; + unsigned sweeper; + struct dm_deferred_entry entries[DEFERRED_SET_SIZE]; +}; + +struct dm_deferred_set *dm_deferred_set_create(void) +{ + int i; + struct dm_deferred_set *ds; + + ds = kmalloc(sizeof(*ds), GFP_KERNEL); + if (!ds) + return NULL; + + spin_lock_init(&ds->lock); + ds->current_entry = 0; + ds->sweeper = 0; + for (i = 0; i < DEFERRED_SET_SIZE; i++) { + ds->entries[i].ds = ds; + ds->entries[i].count = 0; + INIT_LIST_HEAD(&ds->entries[i].work_items); + } + + return ds; +} +EXPORT_SYMBOL_GPL(dm_deferred_set_create); + +void dm_deferred_set_destroy(struct dm_deferred_set *ds) +{ + kfree(ds); +} +EXPORT_SYMBOL_GPL(dm_deferred_set_destroy); + +struct dm_deferred_entry *dm_deferred_entry_inc(struct dm_deferred_set *ds) +{ + unsigned long flags; + struct dm_deferred_entry *entry; + + spin_lock_irqsave(&ds->lock, flags); + entry = ds->entries + ds->current_entry; + entry->count++; + spin_unlock_irqrestore(&ds->lock, flags); + + return entry; +} +EXPORT_SYMBOL_GPL(dm_deferred_entry_inc); + +static unsigned ds_next(unsigned index) +{ + return (index + 1) % DEFERRED_SET_SIZE; +} + +static void __sweep(struct dm_deferred_set *ds, struct list_head *head) +{ + while ((ds->sweeper != ds->current_entry) && + !ds->entries[ds->sweeper].count) { + list_splice_init(&ds->entries[ds->sweeper].work_items, head); + ds->sweeper = ds_next(ds->sweeper); + } + + if ((ds->sweeper == ds->current_entry) && !ds->entries[ds->sweeper].count) + list_splice_init(&ds->entries[ds->sweeper].work_items, head); +} + +void dm_deferred_entry_dec(struct dm_deferred_entry *entry, struct list_head *head) +{ + unsigned long flags; + + spin_lock_irqsave(&entry->ds->lock, flags); + BUG_ON(!entry->count); + --entry->count; + __sweep(entry->ds, head); + spin_unlock_irqrestore(&entry->ds->lock, flags); +} +EXPORT_SYMBOL_GPL(dm_deferred_entry_dec); + +/* + * Returns 1 if deferred or 0 if no pending items to delay job. + */ +int dm_deferred_set_add_work(struct dm_deferred_set *ds, struct list_head *work) +{ + int r = 1; + unsigned long flags; + unsigned next_entry; + + spin_lock_irqsave(&ds->lock, flags); + if ((ds->sweeper == ds->current_entry) && + !ds->entries[ds->current_entry].count) + r = 0; + else { + list_add(work, &ds->entries[ds->current_entry].work_items); + next_entry = ds_next(ds->current_entry); + if (!ds->entries[next_entry].count) + ds->current_entry = next_entry; + } + spin_unlock_irqrestore(&ds->lock, flags); + + return r; +} +EXPORT_SYMBOL_GPL(dm_deferred_set_add_work); + +/*----------------------------------------------------------------*/ + +static int __init dm_bio_prison_init(void) +{ + _cell_cache = KMEM_CACHE(dm_bio_prison_cell, 0); + if (!_cell_cache) + return -ENOMEM; + + return 0; +} + +static void __exit dm_bio_prison_exit(void) +{ + kmem_cache_destroy(_cell_cache); + _cell_cache = NULL; +} + +/* + * module hooks + */ +module_init(dm_bio_prison_init); +module_exit(dm_bio_prison_exit); + +MODULE_DESCRIPTION(DM_NAME " bio prison"); +MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/md/dm-bio-prison.h b/drivers/md/dm-bio-prison.h new file mode 100644 index 00000000000..4e0ac376700 --- /dev/null +++ b/drivers/md/dm-bio-prison.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2011-2012 Red Hat, Inc. + * + * This file is released under the GPL. + */ + +#ifndef DM_BIO_PRISON_H +#define DM_BIO_PRISON_H + +#include "persistent-data/dm-block-manager.h" /* FIXME: for dm_block_t */ +#include "dm-thin-metadata.h" /* FIXME: for dm_thin_id */ + +#include <linux/list.h> +#include <linux/bio.h> + +/*----------------------------------------------------------------*/ + +/* + * Sometimes we can't deal with a bio straight away. We put them in prison + * where they can't cause any mischief. Bios are put in a cell identified + * by a key, multiple bios can be in the same cell. When the cell is + * subsequently unlocked the bios become available. + */ +struct dm_bio_prison; +struct dm_bio_prison_cell; + +/* FIXME: this needs to be more abstract */ +struct dm_cell_key { + int virtual; + dm_thin_id dev; + dm_block_t block; +}; + +struct dm_bio_prison *dm_bio_prison_create(unsigned nr_cells); +void dm_bio_prison_destroy(struct dm_bio_prison *prison); + +/* + * This may block if a new cell needs allocating. You must ensure that + * cells will be unlocked even if the calling thread is blocked. + * + * Returns 1 if the cell was already held, 0 if @inmate is the new holder. + */ +int dm_bio_detain(struct dm_bio_prison *prison, struct dm_cell_key *key, + struct bio *inmate, struct dm_bio_prison_cell **ref); + +void dm_cell_release(struct dm_bio_prison_cell *cell, struct bio_list *bios); +void dm_cell_release_singleton(struct dm_bio_prison_cell *cell, struct bio *bio); // FIXME: bio arg not needed +void dm_cell_release_no_holder(struct dm_bio_prison_cell *cell, struct bio_list *inmates); +void dm_cell_error(struct dm_bio_prison_cell *cell); + +/*----------------------------------------------------------------*/ + +/* + * We use the deferred set to keep track of pending reads to shared blocks. + * We do this to ensure the new mapping caused by a write isn't performed + * until these prior reads have completed. Otherwise the insertion of the + * new mapping could free the old block that the read bios are mapped to. + */ + +struct dm_deferred_set; +struct dm_deferred_entry; + +struct dm_deferred_set *dm_deferred_set_create(void); +void dm_deferred_set_destroy(struct dm_deferred_set *ds); + +struct dm_deferred_entry *dm_deferred_entry_inc(struct dm_deferred_set *ds); +void dm_deferred_entry_dec(struct dm_deferred_entry *entry, struct list_head *head); +int dm_deferred_set_add_work(struct dm_deferred_set *ds, struct list_head *work); + +/*----------------------------------------------------------------*/ + +#endif diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c index cc06a1e5242..651ca79881d 100644 --- a/drivers/md/dm-bufio.c +++ b/drivers/md/dm-bufio.c @@ -280,9 +280,7 @@ static void __cache_size_refresh(void) BUG_ON(!mutex_is_locked(&dm_bufio_clients_lock)); BUG_ON(dm_bufio_client_count < 0); - dm_bufio_cache_size_latch = dm_bufio_cache_size; - - barrier(); + dm_bufio_cache_size_latch = ACCESS_ONCE(dm_bufio_cache_size); /* * Use default if set to 0 and report the actual cache size used. @@ -441,8 +439,7 @@ static void __relink_lru(struct dm_buffer *b, int dirty) c->n_buffers[b->list_mode]--; c->n_buffers[dirty]++; b->list_mode = dirty; - list_del(&b->lru_list); - list_add(&b->lru_list, &c->lru[dirty]); + list_move(&b->lru_list, &c->lru[dirty]); } /*---------------------------------------------------------------- @@ -813,7 +810,7 @@ static void __get_memory_limit(struct dm_bufio_client *c, { unsigned long buffers; - if (dm_bufio_cache_size != dm_bufio_cache_size_latch) { + if (ACCESS_ONCE(dm_bufio_cache_size) != dm_bufio_cache_size_latch) { mutex_lock(&dm_bufio_clients_lock); __cache_size_refresh(); mutex_unlock(&dm_bufio_clients_lock); @@ -1591,11 +1588,9 @@ EXPORT_SYMBOL_GPL(dm_bufio_client_destroy); static void cleanup_old_buffers(void) { - unsigned long max_age = dm_bufio_max_age; + unsigned long max_age = ACCESS_ONCE(dm_bufio_max_age); struct dm_bufio_client *c; - barrier(); - if (max_age > ULONG_MAX / HZ) max_age = ULONG_MAX / HZ; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index d778563a4ff..573bd04591b 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -1309,13 +1309,14 @@ static int multipath_end_io(struct dm_target *ti, struct request *clone, { struct multipath *m = ti->private; struct dm_mpath_io *mpio = map_context->ptr; - struct pgpath *pgpath = mpio->pgpath; + struct pgpath *pgpath; struct path_selector *ps; int r; BUG_ON(!mpio); r = do_end_io(m, clone, error, mpio); + pgpath = mpio->pgpath; if (pgpath) { ps = &pgpath->pg->ps; if (ps->type->end_io) diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 982e3e390c4..45d94a7e7f6 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -338,6 +338,84 @@ static int validate_region_size(struct raid_set *rs, unsigned long region_size) } /* + * validate_rebuild_devices + * @rs + * + * Determine if the devices specified for rebuild can result in a valid + * usable array that is capable of rebuilding the given devices. + * + * Returns: 0 on success, -EINVAL on failure. + */ +static int validate_rebuild_devices(struct raid_set *rs) +{ + unsigned i, rebuild_cnt = 0; + unsigned rebuilds_per_group, copies, d; + + if (!(rs->print_flags & DMPF_REBUILD)) + return 0; + + for (i = 0; i < rs->md.raid_disks; i++) + if (!test_bit(In_sync, &rs->dev[i].rdev.flags)) + rebuild_cnt++; + + switch (rs->raid_type->level) { + case 1: + if (rebuild_cnt >= rs->md.raid_disks) + goto too_many; + break; + case 4: + case 5: + case 6: + if (rebuild_cnt > rs->raid_type->parity_devs) + goto too_many; + break; + case 10: + copies = raid10_md_layout_to_copies(rs->md.layout); + if (rebuild_cnt < copies) + break; + + /* + * It is possible to have a higher rebuild count for RAID10, + * as long as the failed devices occur in different mirror + * groups (i.e. different stripes). + * + * Right now, we only allow for "near" copies. When other + * formats are added, we will have to check those too. + * + * When checking "near" format, make sure no adjacent devices + * have failed beyond what can be handled. In addition to the + * simple case where the number of devices is a multiple of the + * number of copies, we must also handle cases where the number + * of devices is not a multiple of the number of copies. + * E.g. dev1 dev2 dev3 dev4 dev5 + * A A B B C + * C D D E E + */ + rebuilds_per_group = 0; + for (i = 0; i < rs->md.raid_disks * copies; i++) { + d = i % rs->md.raid_disks; + if (!test_bit(In_sync, &rs->dev[d].rdev.flags) && + (++rebuilds_per_group >= copies)) + goto too_many; + if (!((i + 1) % copies)) + rebuilds_per_group = 0; + } + break; + default: + DMERR("The rebuild parameter is not supported for %s", + rs->raid_type->name); + rs->ti->error = "Rebuild not supported for this RAID type"; + return -EINVAL; + } + + return 0; + +too_many: + rs->ti->error = "Too many rebuild devices specified"; + return -EINVAL; +} + +/* * Possible arguments are... * <chunk_size> [optional_args] * @@ -365,7 +443,7 @@ static int parse_raid_params(struct raid_set *rs, char **argv, { char *raid10_format = "near"; unsigned raid10_copies = 2; - unsigned i, rebuild_cnt = 0; + unsigned i; unsigned long value, region_size = 0; sector_t sectors_per_dev = rs->ti->len; sector_t max_io_len; @@ -461,31 +539,7 @@ static int parse_raid_params(struct raid_set *rs, char **argv, /* Parameters that take a numeric value are checked here */ if (!strcasecmp(key, "rebuild")) { - rebuild_cnt++; - - switch (rs->raid_type->level) { - case 1: - if (rebuild_cnt >= rs->md.raid_disks) { - rs->ti->error = "Too many rebuild devices specified"; - return -EINVAL; - } - break; - case 4: - case 5: - case 6: - if (rebuild_cnt > rs->raid_type->parity_devs) { - rs->ti->error = "Too many rebuild devices specified for given RAID type"; - return -EINVAL; - } - break; - case 10: - default: - DMERR("The rebuild parameter is not supported for %s", rs->raid_type->name); - rs->ti->error = "Rebuild not supported for this RAID type"; - return -EINVAL; - } - - if (value > rs->md.raid_disks) { + if (value >= rs->md.raid_disks) { rs->ti->error = "Invalid rebuild index given"; return -EINVAL; } @@ -608,6 +662,9 @@ static int parse_raid_params(struct raid_set *rs, char **argv, } rs->md.dev_sectors = sectors_per_dev; + if (validate_rebuild_devices(rs)) + return -EINVAL; + /* Assume there are no metadata devices until the drives are parsed */ rs->md.persistent = 0; rs->md.external = 1; @@ -960,6 +1017,19 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs) freshest = NULL; rdev_for_each_safe(rdev, tmp, mddev) { + /* + * Skipping super_load due to DMPF_SYNC will cause + * the array to undergo initialization again as + * though it were new. This is the intended effect + * of the "sync" directive. + * + * When reshaping capability is added, we must ensure + * that the "sync" directive is disallowed during the + * reshape. + */ + if (rs->print_flags & DMPF_SYNC) + continue; + if (!rdev->meta_bdev) continue; @@ -1360,7 +1430,7 @@ static void raid_resume(struct dm_target *ti) static struct target_type raid_target = { .name = "raid", - .version = {1, 3, 0}, + .version = {1, 3, 1}, .module = THIS_MODULE, .ctr = raid_ctr, .dtr = raid_dtr, diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index c29410af1e2..058acf3a5ba 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -5,6 +5,7 @@ */ #include "dm-thin-metadata.h" +#include "dm-bio-prison.h" #include "dm.h" #include <linux/device-mapper.h> @@ -21,7 +22,6 @@ * Tunable constants */ #define ENDIO_HOOK_POOL_SIZE 1024 -#define DEFERRED_SET_SIZE 64 #define MAPPING_POOL_SIZE 1024 #define PRISON_CELLS 1024 #define COMMIT_PERIOD HZ @@ -58,7 +58,7 @@ * i) plug io further to this physical block. (see bio_prison code). * * ii) quiesce any read io to that shared data block. Obviously - * including all devices that share this block. (see deferred_set code) + * including all devices that share this block. (see dm_deferred_set code) * * iii) copy the data block to a newly allocate block. This step can be * missed out if the io covers the block. (schedule_copy). @@ -99,381 +99,10 @@ /*----------------------------------------------------------------*/ /* - * Sometimes we can't deal with a bio straight away. We put them in prison - * where they can't cause any mischief. Bios are put in a cell identified - * by a key, multiple bios can be in the same cell. When the cell is - * subsequently unlocked the bios become available. - */ -struct bio_prison; - -struct cell_key { - int virtual; - dm_thin_id dev; - dm_block_t block; -}; - -struct dm_bio_prison_cell { - struct hlist_node list; - struct bio_prison *prison; - struct cell_key key; - struct bio *holder; - struct bio_list bios; -}; - -struct bio_prison { - spinlock_t lock; - mempool_t *cell_pool; - - unsigned nr_buckets; - unsigned hash_mask; - struct hlist_head *cells; -}; - -static uint32_t calc_nr_buckets(unsigned nr_cells) -{ - uint32_t n = 128; - - nr_cells /= 4; - nr_cells = min(nr_cells, 8192u); - - while (n < nr_cells) - n <<= 1; - - return n; -} - -static struct kmem_cache *_cell_cache; - -/* - * @nr_cells should be the number of cells you want in use _concurrently_. - * Don't confuse it with the number of distinct keys. - */ -static struct bio_prison *prison_create(unsigned nr_cells) -{ - unsigned i; - uint32_t nr_buckets = calc_nr_buckets(nr_cells); - size_t len = sizeof(struct bio_prison) + - (sizeof(struct hlist_head) * nr_buckets); - struct bio_prison *prison = kmalloc(len, GFP_KERNEL); - - if (!prison) - return NULL; - - spin_lock_init(&prison->lock); - prison->cell_pool = mempool_create_slab_pool(nr_cells, _cell_cache); - if (!prison->cell_pool) { - kfree(prison); - return NULL; - } - - prison->nr_buckets = nr_buckets; - prison->hash_mask = nr_buckets - 1; - prison->cells = (struct hlist_head *) (prison + 1); - for (i = 0; i < nr_buckets; i++) - INIT_HLIST_HEAD(prison->cells + i); - - return prison; -} - -static void prison_destroy(struct bio_prison *prison) -{ - mempool_destroy(prison->cell_pool); - kfree(prison); -} - -static uint32_t hash_key(struct bio_prison *prison, struct cell_key *key) -{ - const unsigned long BIG_PRIME = 4294967291UL; - uint64_t hash = key->block * BIG_PRIME; - - return (uint32_t) (hash & prison->hash_mask); -} - -static int keys_equal(struct cell_key *lhs, struct cell_key *rhs) -{ - return (lhs->virtual == rhs->virtual) && - (lhs->dev == rhs->dev) && - (lhs->block == rhs->block); -} - -static struct dm_bio_prison_cell *__search_bucket(struct hlist_head *bucket, - struct cell_key *key) -{ - struct dm_bio_prison_cell *cell; - struct hlist_node *tmp; - - hlist_for_each_entry(cell, tmp, bucket, list) - if (keys_equal(&cell->key, key)) - return cell; - - return NULL; -} - -/* - * This may block if a new cell needs allocating. You must ensure that - * cells will be unlocked even if the calling thread is blocked. - * - * Returns 1 if the cell was already held, 0 if @inmate is the new holder. - */ -static int bio_detain(struct bio_prison *prison, struct cell_key *key, - struct bio *inmate, struct dm_bio_prison_cell **ref) -{ - int r = 1; - unsigned long flags; - uint32_t hash = hash_key(prison, key); - struct dm_bio_prison_cell *cell, *cell2; - - BUG_ON(hash > prison->nr_buckets); - - spin_lock_irqsave(&prison->lock, flags); - - cell = __search_bucket(prison->cells + hash, key); - if (cell) { - bio_list_add(&cell->bios, inmate); - goto out; - } - - /* - * Allocate a new cell - */ - spin_unlock_irqrestore(&prison->lock, flags); - cell2 = mempool_alloc(prison->cell_pool, GFP_NOIO); - spin_lock_irqsave(&prison->lock, flags); - - /* - * We've been unlocked, so we have to double check that - * nobody else has inserted this cell in the meantime. - */ - cell = __search_bucket(prison->cells + hash, key); - if (cell) { - mempool_free(cell2, prison->cell_pool); - bio_list_add(&cell->bios, inmate); - goto out; - } - - /* - * Use new cell. - */ - cell = cell2; - - cell->prison = prison; - memcpy(&cell->key, key, sizeof(cell->key)); - cell->holder = inmate; - bio_list_init(&cell->bios); - hlist_add_head(&cell->list, prison->cells + hash); - - r = 0; - -out: - spin_unlock_irqrestore(&prison->lock, flags); - - *ref = cell; - - return r; -} - -/* - * @inmates must have been initialised prior to this call - */ -static void __cell_release(struct dm_bio_prison_cell *cell, struct bio_list *inmates) -{ - struct bio_prison *prison = cell->prison; - - hlist_del(&cell->list); - - if (inmates) { - bio_list_add(inmates, cell->holder); - bio_list_merge(inmates, &cell->bios); - } - - mempool_free(cell, prison->cell_pool); -} - -static void cell_release(struct dm_bio_prison_cell *cell, struct bio_list *bios) -{ - unsigned long flags; - struct bio_prison *prison = cell->prison; - - spin_lock_irqsave(&prison->lock, flags); - __cell_release(cell, bios); - spin_unlock_irqrestore(&prison->lock, flags); -} - -/* - * There are a couple of places where we put a bio into a cell briefly - * before taking it out again. In these situations we know that no other - * bio may be in the cell. This function releases the cell, and also does - * a sanity check. - */ -static void __cell_release_singleton(struct dm_bio_prison_cell *cell, struct bio *bio) -{ - BUG_ON(cell->holder != bio); - BUG_ON(!bio_list_empty(&cell->bios)); - - __cell_release(cell, NULL); -} - -static void cell_release_singleton(struct dm_bio_prison_cell *cell, struct bio *bio) -{ - unsigned long flags; - struct bio_prison *prison = cell->prison; - - spin_lock_irqsave(&prison->lock, flags); - __cell_release_singleton(cell, bio); - spin_unlock_irqrestore(&prison->lock, flags); -} - -/* - * Sometimes we don't want the holder, just the additional bios. - */ -static void __cell_release_no_holder(struct dm_bio_prison_cell *cell, - struct bio_list *inmates) -{ - struct bio_prison *prison = cell->prison; - - hlist_del(&cell->list); - bio_list_merge(inmates, &cell->bios); - - mempool_free(cell, prison->cell_pool); -} - -static void cell_release_no_holder(struct dm_bio_prison_cell *cell, - struct bio_list *inmates) -{ - unsigned long flags; - struct bio_prison *prison = cell->prison; - - spin_lock_irqsave(&prison->lock, flags); - __cell_release_no_holder(cell, inmates); - spin_unlock_irqrestore(&prison->lock, flags); -} - -static void cell_error(struct dm_bio_prison_cell *cell) -{ - struct bio_prison *prison = cell->prison; - struct bio_list bios; - struct bio *bio; - unsigned long flags; - - bio_list_init(&bios); - - spin_lock_irqsave(&prison->lock, flags); - __cell_release(cell, &bios); - spin_unlock_irqrestore(&prison->lock, flags); - - while ((bio = bio_list_pop(&bios))) - bio_io_error(bio); -} - -/*----------------------------------------------------------------*/ - -/* - * We use the deferred set to keep track of pending reads to shared blocks. - * We do this to ensure the new mapping caused by a write isn't performed - * until these prior reads have completed. Otherwise the insertion of the - * new mapping could free the old block that the read bios are mapped to. - */ - -struct deferred_set; -struct deferred_entry { - struct deferred_set *ds; - unsigned count; - struct list_head work_items; -}; - -struct deferred_set { - spinlock_t lock; - unsigned current_entry; - unsigned sweeper; - struct deferred_entry entries[DEFERRED_SET_SIZE]; -}; - -static void ds_init(struct deferred_set *ds) -{ - int i; - - spin_lock_init(&ds->lock); - ds->current_entry = 0; - ds->sweeper = 0; - for (i = 0; i < DEFERRED_SET_SIZE; i++) { - ds->entries[i].ds = ds; - ds->entries[i].count = 0; - INIT_LIST_HEAD(&ds->entries[i].work_items); - } -} - -static struct deferred_entry *ds_inc(struct deferred_set *ds) -{ - unsigned long flags; - struct deferred_entry *entry; - - spin_lock_irqsave(&ds->lock, flags); - entry = ds->entries + ds->current_entry; - entry->count++; - spin_unlock_irqrestore(&ds->lock, flags); - - return entry; -} - -static unsigned ds_next(unsigned index) -{ - return (index + 1) % DEFERRED_SET_SIZE; -} - -static void __sweep(struct deferred_set *ds, struct list_head *head) -{ - while ((ds->sweeper != ds->current_entry) && - !ds->entries[ds->sweeper].count) { - list_splice_init(&ds->entries[ds->sweeper].work_items, head); - ds->sweeper = ds_next(ds->sweeper); - } - - if ((ds->sweeper == ds->current_entry) && !ds->entries[ds->sweeper].count) - list_splice_init(&ds->entries[ds->sweeper].work_items, head); -} - -static void ds_dec(struct deferred_entry *entry, struct list_head *head) -{ - unsigned long flags; - - spin_lock_irqsave(&entry->ds->lock, flags); - BUG_ON(!entry->count); - --entry->count; - __sweep(entry->ds, head); - spin_unlock_irqrestore(&entry->ds->lock, flags); -} - -/* - * Returns 1 if deferred or 0 if no pending items to delay job. - */ -static int ds_add_work(struct deferred_set *ds, struct list_head *work) -{ - int r = 1; - unsigned long flags; - unsigned next_entry; - - spin_lock_irqsave(&ds->lock, flags); - if ((ds->sweeper == ds->current_entry) && - !ds->entries[ds->current_entry].count) - r = 0; - else { - list_add(work, &ds->entries[ds->current_entry].work_items); - next_entry = ds_next(ds->current_entry); - if (!ds->entries[next_entry].count) - ds->current_entry = next_entry; - } - spin_unlock_irqrestore(&ds->lock, flags); - - return r; -} - -/*----------------------------------------------------------------*/ - -/* * Key building. */ static void build_data_key(struct dm_thin_device *td, - dm_block_t b, struct cell_key *key) + dm_block_t b, struct dm_cell_key *key) { key->virtual = 0; key->dev = dm_thin_dev_id(td); @@ -481,7 +110,7 @@ static void build_data_key(struct dm_thin_device *td, } static void build_virtual_key(struct dm_thin_device *td, dm_block_t b, - struct cell_key *key) + struct dm_cell_key *key) { key->virtual = 1; key->dev = dm_thin_dev_id(td); @@ -534,7 +163,7 @@ struct pool { unsigned low_water_triggered:1; /* A dm event has been sent */ unsigned no_free_space:1; /* A -ENOSPC warning has been issued */ - struct bio_prison *prison; + struct dm_bio_prison *prison; struct dm_kcopyd_client *copier; struct workqueue_struct *wq; @@ -552,8 +181,8 @@ struct pool { struct bio_list retry_on_resume_list; - struct deferred_set shared_read_ds; - struct deferred_set all_io_ds; + struct dm_deferred_set *shared_read_ds; + struct dm_deferred_set *all_io_ds; struct dm_thin_new_mapping *next_mapping; mempool_t *mapping_pool; @@ -660,8 +289,8 @@ static struct pool *__pool_table_lookup_metadata_dev(struct block_device *md_dev struct dm_thin_endio_hook { struct thin_c *tc; - struct deferred_entry *shared_read_entry; - struct deferred_entry *all_io_entry; + struct dm_deferred_entry *shared_read_entry; + struct dm_deferred_entry *all_io_entry; struct dm_thin_new_mapping *overwrite_mapping; }; @@ -877,7 +506,7 @@ static void cell_defer(struct thin_c *tc, struct dm_bio_prison_cell *cell, unsigned long flags; spin_lock_irqsave(&pool->lock, flags); - cell_release(cell, &pool->deferred_bios); + dm_cell_release(cell, &pool->deferred_bios); spin_unlock_irqrestore(&tc->pool->lock, flags); wake_worker(pool); @@ -896,7 +525,7 @@ static void cell_defer_except(struct thin_c *tc, struct dm_bio_prison_cell *cell bio_list_init(&bios); spin_lock_irqsave(&pool->lock, flags); - cell_release_no_holder(cell, &pool->deferred_bios); + dm_cell_release_no_holder(cell, &pool->deferred_bios); spin_unlock_irqrestore(&pool->lock, flags); wake_worker(pool); @@ -906,7 +535,7 @@ static void process_prepared_mapping_fail(struct dm_thin_new_mapping *m) { if (m->bio) m->bio->bi_end_io = m->saved_bi_end_io; - cell_error(m->cell); + dm_cell_error(m->cell); list_del(&m->list); mempool_free(m, m->tc->pool->mapping_pool); } @@ -921,7 +550,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m) bio->bi_end_io = m->saved_bi_end_io; if (m->err) { - cell_error(m->cell); + dm_cell_error(m->cell); goto out; } @@ -933,7 +562,7 @@ static void process_prepared_mapping(struct dm_thin_new_mapping *m) r = dm_thin_insert_block(tc->td, m->virt_block, m->data_block); if (r) { DMERR("dm_thin_insert_block() failed"); - cell_error(m->cell); + dm_cell_error(m->cell); goto out; } @@ -1067,7 +696,7 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block, m->err = 0; m->bio = NULL; - if (!ds_add_work(&pool->shared_read_ds, &m->list)) + if (!dm_deferred_set_add_work(pool->shared_read_ds, &m->list)) m->quiesced = 1; /* @@ -1099,7 +728,7 @@ static void schedule_copy(struct thin_c *tc, dm_block_t virt_block, if (r < 0) { mempool_free(m, pool->mapping_pool); DMERR("dm_kcopyd_copy() failed"); - cell_error(cell); + dm_cell_error(cell); } } } @@ -1164,7 +793,7 @@ static void schedule_zero(struct thin_c *tc, dm_block_t virt_block, if (r < 0) { mempool_free(m, pool->mapping_pool); DMERR("dm_kcopyd_zero() failed"); - cell_error(cell); + dm_cell_error(cell); } } } @@ -1276,7 +905,7 @@ static void no_space(struct dm_bio_prison_cell *cell) struct bio_list bios; bio_list_init(&bios); - cell_release(cell, &bios); + dm_cell_release(cell, &bios); while ((bio = bio_list_pop(&bios))) retry_on_resume(bio); @@ -1288,13 +917,13 @@ static void process_discard(struct thin_c *tc, struct bio *bio) unsigned long flags; struct pool *pool = tc->pool; struct dm_bio_prison_cell *cell, *cell2; - struct cell_key key, key2; + struct dm_cell_key key, key2; dm_block_t block = get_bio_block(tc, bio); struct dm_thin_lookup_result lookup_result; struct dm_thin_new_mapping *m; build_virtual_key(tc->td, block, &key); - if (bio_detain(tc->pool->prison, &key, bio, &cell)) + if (dm_bio_detain(tc->pool->prison, &key, bio, &cell)) return; r = dm_thin_find_block(tc->td, block, 1, &lookup_result); @@ -1306,8 +935,8 @@ static void process_discard(struct thin_c *tc, struct bio *bio) * on this block. */ build_data_key(tc->td, lookup_result.block, &key2); - if (bio_detain(tc->pool->prison, &key2, bio, &cell2)) { - cell_release_singleton(cell, bio); + if (dm_bio_detain(tc->pool->prison, &key2, bio, &cell2)) { + dm_cell_release_singleton(cell, bio); break; } @@ -1326,7 +955,7 @@ static void process_discard(struct thin_c *tc, struct bio *bio) m->err = 0; m->bio = bio; - if (!ds_add_work(&pool->all_io_ds, &m->list)) { + if (!dm_deferred_set_add_work(pool->all_io_ds, &m->list)) { spin_lock_irqsave(&pool->lock, flags); list_add(&m->list, &pool->prepared_discards); spin_unlock_irqrestore(&pool->lock, flags); @@ -1338,8 +967,8 @@ static void process_discard(struct thin_c *tc, struct bio *bio) * a block boundary. So we submit the discard of a * partial block appropriately. */ - cell_release_singleton(cell, bio); - cell_release_singleton(cell2, bio); + dm_cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell2, bio); if ((!lookup_result.shared) && pool->pf.discard_passdown) remap_and_issue(tc, bio, lookup_result.block); else @@ -1351,20 +980,20 @@ static void process_discard(struct thin_c *tc, struct bio *bio) /* * It isn't provisioned, just forget it. */ - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); bio_endio(bio, 0); break; default: DMERR("discard: find block unexpectedly returned %d", r); - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); bio_io_error(bio); break; } } static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block, - struct cell_key *key, + struct dm_cell_key *key, struct dm_thin_lookup_result *lookup_result, struct dm_bio_prison_cell *cell) { @@ -1384,7 +1013,7 @@ static void break_sharing(struct thin_c *tc, struct bio *bio, dm_block_t block, default: DMERR("%s: alloc_data_block() failed, error = %d", __func__, r); - cell_error(cell); + dm_cell_error(cell); break; } } @@ -1395,14 +1024,14 @@ static void process_shared_bio(struct thin_c *tc, struct bio *bio, { struct dm_bio_prison_cell *cell; struct pool *pool = tc->pool; - struct cell_key key; + struct dm_cell_key key; /* * If cell is already occupied, then sharing is already in the process * of being broken so we have nothing further to do here. */ build_data_key(tc->td, lookup_result->block, &key); - if (bio_detain(pool->prison, &key, bio, &cell)) + if (dm_bio_detain(pool->prison, &key, bio, &cell)) return; if (bio_data_dir(bio) == WRITE && bio->bi_size) @@ -1410,9 +1039,9 @@ static void process_shared_bio(struct thin_c *tc, struct bio *bio, else { struct dm_thin_endio_hook *h = dm_get_mapinfo(bio)->ptr; - h->shared_read_entry = ds_inc(&pool->shared_read_ds); + h->shared_read_entry = dm_deferred_entry_inc(pool->shared_read_ds); - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); remap_and_issue(tc, bio, lookup_result->block); } } @@ -1427,7 +1056,7 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block * Remap empty bios (flushes) immediately, without provisioning. */ if (!bio->bi_size) { - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); remap_and_issue(tc, bio, 0); return; } @@ -1437,7 +1066,7 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block */ if (bio_data_dir(bio) == READ) { zero_fill_bio(bio); - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); bio_endio(bio, 0); return; } @@ -1458,7 +1087,7 @@ static void provision_block(struct thin_c *tc, struct bio *bio, dm_block_t block default: DMERR("%s: alloc_data_block() failed, error = %d", __func__, r); set_pool_mode(tc->pool, PM_READ_ONLY); - cell_error(cell); + dm_cell_error(cell); break; } } @@ -1468,7 +1097,7 @@ static void process_bio(struct thin_c *tc, struct bio *bio) int r; dm_block_t block = get_bio_block(tc, bio); struct dm_bio_prison_cell *cell; - struct cell_key key; + struct dm_cell_key key; struct dm_thin_lookup_result lookup_result; /* @@ -1476,7 +1105,7 @@ static void process_bio(struct thin_c *tc, struct bio *bio) * being provisioned so we have nothing further to do here. */ build_virtual_key(tc->td, block, &key); - if (bio_detain(tc->pool->prison, &key, bio, &cell)) + if (dm_bio_detain(tc->pool->prison, &key, bio, &cell)) return; r = dm_thin_find_block(tc->td, block, 1, &lookup_result); @@ -1491,7 +1120,7 @@ static void process_bio(struct thin_c *tc, struct bio *bio) * TODO: this will probably have to change when discard goes * back in. */ - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); if (lookup_result.shared) process_shared_bio(tc, bio, block, &lookup_result); @@ -1501,7 +1130,7 @@ static void process_bio(struct thin_c *tc, struct bio *bio) case -ENODATA: if (bio_data_dir(bio) == READ && tc->origin_dev) { - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); remap_to_origin_and_issue(tc, bio); } else provision_block(tc, bio, block, cell); @@ -1509,7 +1138,7 @@ static void process_bio(struct thin_c *tc, struct bio *bio) default: DMERR("dm_thin_find_block() failed, error = %d", r); - cell_release_singleton(cell, bio); + dm_cell_release_singleton(cell, bio); bio_io_error(bio); break; } @@ -1718,7 +1347,7 @@ static struct dm_thin_endio_hook *thin_hook_bio(struct thin_c *tc, struct bio *b h->tc = tc; h->shared_read_entry = NULL; - h->all_io_entry = bio->bi_rw & REQ_DISCARD ? NULL : ds_inc(&pool->all_io_ds); + h->all_io_entry = bio->bi_rw & REQ_DISCARD ? NULL : dm_deferred_entry_inc(pool->all_io_ds); h->overwrite_mapping = NULL; return h; @@ -1928,7 +1557,7 @@ static void __pool_destroy(struct pool *pool) if (dm_pool_metadata_close(pool->pmd) < 0) DMWARN("%s: dm_pool_metadata_close() failed.", __func__); - prison_destroy(pool->prison); + dm_bio_prison_destroy(pool->prison); dm_kcopyd_client_destroy(pool->copier); if (pool->wq) @@ -1938,6 +1567,8 @@ static void __pool_destroy(struct pool *pool) mempool_free(pool->next_mapping, pool->mapping_pool); mempool_destroy(pool->mapping_pool); mempool_destroy(pool->endio_hook_pool); + dm_deferred_set_destroy(pool->shared_read_ds); + dm_deferred_set_destroy(pool->all_io_ds); kfree(pool); } @@ -1976,7 +1607,7 @@ static struct pool *pool_create(struct mapped_device *pool_md, pool->sectors_per_block_shift = __ffs(block_size); pool->low_water_blocks = 0; pool_features_init(&pool->pf); - pool->prison = prison_create(PRISON_CELLS); + pool->prison = dm_bio_prison_create(PRISON_CELLS); if (!pool->prison) { *error = "Error creating pool's bio prison"; err_p = ERR_PTR(-ENOMEM); @@ -2012,8 +1643,20 @@ static struct pool *pool_create(struct mapped_device *pool_md, pool->low_water_triggered = 0; pool->no_free_space = 0; bio_list_init(&pool->retry_on_resume_list); - ds_init(&pool->shared_read_ds); - ds_init(&pool->all_io_ds); + + pool->shared_read_ds = dm_deferred_set_create(); + if (!pool->shared_read_ds) { + *error = "Error creating pool's shared read deferred set"; + err_p = ERR_PTR(-ENOMEM); + goto bad_shared_read_ds; + } + + pool->all_io_ds = dm_deferred_set_create(); + if (!pool->all_io_ds) { + *error = "Error creating pool's all io deferred set"; + err_p = ERR_PTR(-ENOMEM); + goto bad_all_io_ds; + } pool->next_mapping = NULL; pool->mapping_pool = mempool_create_slab_pool(MAPPING_POOL_SIZE, @@ -2042,11 +1685,15 @@ static struct pool *pool_create(struct mapped_device *pool_md, bad_endio_hook_pool: mempool_destroy(pool->mapping_pool); bad_mapping_pool: + dm_deferred_set_destroy(pool->all_io_ds); +bad_all_io_ds: + dm_deferred_set_destroy(pool->shared_read_ds); +bad_shared_read_ds: destroy_workqueue(pool->wq); bad_wq: dm_kcopyd_client_destroy(pool->copier); bad_kcopyd_client: - prison_destroy(pool->prison); + dm_bio_prison_destroy(pool->prison); bad_prison: kfree(pool); bad_pool: @@ -2272,15 +1919,6 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv) goto out_flags_changed; } - /* - * The block layer requires discard_granularity to be a power of 2. - */ - if (pf.discard_enabled && !is_power_of_2(block_size)) { - ti->error = "Discard support must be disabled when the block size is not a power of 2"; - r = -EINVAL; - goto out_flags_changed; - } - pt->pool = pool; pt->ti = ti; pt->metadata_dev = metadata_dev; @@ -2762,6 +2400,11 @@ static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm, return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); } +static bool block_size_is_power_of_two(struct pool *pool) +{ + return pool->sectors_per_block_shift >= 0; +} + static void set_discard_limits(struct pool_c *pt, struct queue_limits *limits) { struct pool *pool = pt->pool; @@ -2775,8 +2418,15 @@ static void set_discard_limits(struct pool_c *pt, struct queue_limits *limits) if (pt->adjusted_pf.discard_passdown) { data_limits = &bdev_get_queue(pt->data_dev->bdev)->limits; limits->discard_granularity = data_limits->discard_granularity; - } else + } else if (block_size_is_power_of_two(pool)) limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT; + else + /* + * Use largest power of 2 that is a factor of sectors_per_block + * but at least DATA_DEV_BLOCK_SIZE_MIN_SECTORS. + */ + limits->discard_granularity = max(1 << (ffs(pool->sectors_per_block) - 1), + DATA_DEV_BLOCK_SIZE_MIN_SECTORS) << SECTOR_SHIFT; } static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits) @@ -2804,7 +2454,7 @@ static struct target_type pool_target = { .name = "thin-pool", .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE | DM_TARGET_IMMUTABLE, - .version = {1, 4, 0}, + .version = {1, 5, 0}, .module = THIS_MODULE, .ctr = pool_ctr, .dtr = pool_dtr, @@ -2979,7 +2629,7 @@ static int thin_endio(struct dm_target *ti, if (h->shared_read_entry) { INIT_LIST_HEAD(&work); - ds_dec(h->shared_read_entry, &work); + dm_deferred_entry_dec(h->shared_read_entry, &work); spin_lock_irqsave(&pool->lock, flags); list_for_each_entry_safe(m, tmp, &work, list) { @@ -2992,7 +2642,7 @@ static int thin_endio(struct dm_target *ti, if (h->all_io_entry) { INIT_LIST_HEAD(&work); - ds_dec(h->all_io_entry, &work); + dm_deferred_entry_dec(h->all_io_entry, &work); spin_lock_irqsave(&pool->lock, flags); list_for_each_entry_safe(m, tmp, &work, list) list_add(&m->list, &pool->prepared_discards); @@ -3095,7 +2745,7 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits) static struct target_type thin_target = { .name = "thin", - .version = {1, 4, 0}, + .version = {1, 5, 0}, .module = THIS_MODULE, .ctr = thin_ctr, .dtr = thin_dtr, @@ -3125,10 +2775,6 @@ static int __init dm_thin_init(void) r = -ENOMEM; - _cell_cache = KMEM_CACHE(dm_bio_prison_cell, 0); - if (!_cell_cache) - goto bad_cell_cache; - _new_mapping_cache = KMEM_CACHE(dm_thin_new_mapping, 0); if (!_new_mapping_cache) goto bad_new_mapping_cache; @@ -3142,8 +2788,6 @@ static int __init dm_thin_init(void) bad_endio_hook_cache: kmem_cache_destroy(_new_mapping_cache); bad_new_mapping_cache: - kmem_cache_destroy(_cell_cache); -bad_cell_cache: dm_unregister_target(&pool_target); bad_pool_target: dm_unregister_target(&thin_target); @@ -3156,7 +2800,6 @@ static void dm_thin_exit(void) dm_unregister_target(&thin_target); dm_unregister_target(&pool_target); - kmem_cache_destroy(_cell_cache); kmem_cache_destroy(_new_mapping_cache); kmem_cache_destroy(_endio_hook_cache); } diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c index 892ae2766aa..9e7328bb403 100644 --- a/drivers/md/dm-verity.c +++ b/drivers/md/dm-verity.c @@ -438,7 +438,7 @@ static void verity_prefetch_io(struct dm_verity *v, struct dm_verity_io *io) verity_hash_at_level(v, io->block, i, &hash_block_start, NULL); verity_hash_at_level(v, io->block + io->n_blocks - 1, i, &hash_block_end, NULL); if (!i) { - unsigned cluster = *(volatile unsigned *)&dm_verity_prefetch_cluster; + unsigned cluster = ACCESS_ONCE(dm_verity_prefetch_cluster); cluster >>= v->data_dev_block_bits; if (unlikely(!cluster)) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 66ceaff6455..02db9183ca0 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -71,6 +71,7 @@ struct dm_target_io { struct dm_io *io; struct dm_target *ti; union map_info info; + struct bio clone; }; /* @@ -214,7 +215,6 @@ struct dm_md_mempools { #define MIN_IOS 256 static struct kmem_cache *_io_cache; -static struct kmem_cache *_tio_cache; static struct kmem_cache *_rq_tio_cache; /* @@ -232,14 +232,9 @@ static int __init local_init(void) if (!_io_cache) return r; - /* allocate a slab for the target ios */ - _tio_cache = KMEM_CACHE(dm_target_io, 0); - if (!_tio_cache) - goto out_free_io_cache; - _rq_tio_cache = KMEM_CACHE(dm_rq_target_io, 0); if (!_rq_tio_cache) - goto out_free_tio_cache; + goto out_free_io_cache; _rq_bio_info_cache = KMEM_CACHE(dm_rq_clone_bio_info, 0); if (!_rq_bio_info_cache) @@ -265,8 +260,6 @@ out_free_rq_bio_info_cache: kmem_cache_destroy(_rq_bio_info_cache); out_free_rq_tio_cache: kmem_cache_destroy(_rq_tio_cache); -out_free_tio_cache: - kmem_cache_destroy(_tio_cache); out_free_io_cache: kmem_cache_destroy(_io_cache); @@ -277,7 +270,6 @@ static void local_exit(void) { kmem_cache_destroy(_rq_bio_info_cache); kmem_cache_destroy(_rq_tio_cache); - kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_io_cache); unregister_blkdev(_major, _name); dm_uevent_exit(); @@ -463,7 +455,7 @@ static void free_io(struct mapped_device *md, struct dm_io *io) static void free_tio(struct mapped_device *md, struct dm_target_io *tio) { - mempool_free(tio, md->tio_pool); + bio_put(&tio->clone); } static struct dm_rq_target_io *alloc_rq_tio(struct mapped_device *md, @@ -682,7 +674,6 @@ static void clone_endio(struct bio *bio, int error) } free_tio(md, tio); - bio_put(bio); dec_pending(io, error); } @@ -1002,12 +993,12 @@ int dm_set_target_max_io_len(struct dm_target *ti, sector_t len) } EXPORT_SYMBOL_GPL(dm_set_target_max_io_len); -static void __map_bio(struct dm_target *ti, struct bio *clone, - struct dm_target_io *tio) +static void __map_bio(struct dm_target *ti, struct dm_target_io *tio) { int r; sector_t sector; struct mapped_device *md; + struct bio *clone = &tio->clone; clone->bi_end_io = clone_endio; clone->bi_private = tio; @@ -1031,7 +1022,6 @@ static void __map_bio(struct dm_target *ti, struct bio *clone, /* error the io and bail out, or requeue it if needed */ md = tio->io->md; dec_pending(tio->io, r); - bio_put(clone); free_tio(md, tio); } else if (r) { DMWARN("unimplemented target map return value: %d", r); @@ -1052,14 +1042,13 @@ struct clone_info { /* * Creates a little bio that just does part of a bvec. */ -static struct bio *split_bvec(struct bio *bio, sector_t sector, - unsigned short idx, unsigned int offset, - unsigned int len, struct bio_set *bs) +static void split_bvec(struct dm_target_io *tio, struct bio *bio, + sector_t sector, unsigned short idx, unsigned int offset, + unsigned int len, struct bio_set *bs) { - struct bio *clone; + struct bio *clone = &tio->clone; struct bio_vec *bv = bio->bi_io_vec + idx; - clone = bio_alloc_bioset(GFP_NOIO, 1, bs); *clone->bi_io_vec = *bv; clone->bi_sector = sector; @@ -1076,20 +1065,18 @@ static struct bio *split_bvec(struct bio *bio, sector_t sector, bio_integrity_trim(clone, bio_sector_offset(bio, idx, offset), len); } - - return clone; } /* * Creates a bio that consists of range of complete bvecs. */ -static struct bio *clone_bio(struct bio *bio, sector_t sector, - unsigned short idx, unsigned short bv_count, - unsigned int len, struct bio_set *bs) +static void clone_bio(struct dm_target_io *tio, struct bio *bio, + sector_t sector, unsigned short idx, + unsigned short bv_count, unsigned int len, + struct bio_set *bs) { - struct bio *clone; + struct bio *clone = &tio->clone; - clone = bio_alloc_bioset(GFP_NOIO, bio->bi_max_vecs, bs); __bio_clone(clone, bio); clone->bi_sector = sector; clone->bi_idx = idx; @@ -1104,14 +1091,16 @@ static struct bio *clone_bio(struct bio *bio, sector_t sector, bio_integrity_trim(clone, bio_sector_offset(bio, idx, 0), len); } - - return clone; } static struct dm_target_io *alloc_tio(struct clone_info *ci, - struct dm_target *ti) + struct dm_target *ti, int nr_iovecs) { - struct dm_target_io *tio = mempool_alloc(ci->md->tio_pool, GFP_NOIO); + struct dm_target_io *tio; + struct bio *clone; + + clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, ci->md->bs); + tio = container_of(clone, struct dm_target_io, clone); tio->io = ci->io; tio->ti = ti; @@ -1123,8 +1112,8 @@ static struct dm_target_io *alloc_tio(struct clone_info *ci, static void __issue_target_request(struct clone_info *ci, struct dm_target *ti, unsigned request_nr, sector_t len) { - struct dm_target_io *tio = alloc_tio(ci, ti); - struct bio *clone; + struct dm_target_io *tio = alloc_tio(ci, ti, ci->bio->bi_max_vecs); + struct bio *clone = &tio->clone; tio->info.target_request_nr = request_nr; @@ -1133,14 +1122,14 @@ static void __issue_target_request(struct clone_info *ci, struct dm_target *ti, * ci->bio->bi_max_vecs is BIO_INLINE_VECS anyway, for both flush * and discard, so no need for concern about wasted bvec allocations. */ - clone = bio_clone_bioset(ci->bio, GFP_NOIO, ci->md->bs); + __bio_clone(clone, ci->bio); if (len) { clone->bi_sector = ci->sector; clone->bi_size = to_bytes(len); } - __map_bio(ti, clone, tio); + __map_bio(ti, tio); } static void __issue_target_requests(struct clone_info *ci, struct dm_target *ti, @@ -1169,14 +1158,13 @@ static int __clone_and_map_empty_flush(struct clone_info *ci) */ static void __clone_and_map_simple(struct clone_info *ci, struct dm_target *ti) { - struct bio *clone, *bio = ci->bio; + struct bio *bio = ci->bio; struct dm_target_io *tio; - tio = alloc_tio(ci, ti); - clone = clone_bio(bio, ci->sector, ci->idx, - bio->bi_vcnt - ci->idx, ci->sector_count, - ci->md->bs); - __map_bio(ti, clone, tio); + tio = alloc_tio(ci, ti, bio->bi_max_vecs); + clone_bio(tio, bio, ci->sector, ci->idx, bio->bi_vcnt - ci->idx, + ci->sector_count, ci->md->bs); + __map_bio(ti, tio); ci->sector_count = 0; } @@ -1214,7 +1202,7 @@ static int __clone_and_map_discard(struct clone_info *ci) static int __clone_and_map(struct clone_info *ci) { - struct bio *clone, *bio = ci->bio; + struct bio *bio = ci->bio; struct dm_target *ti; sector_t len = 0, max; struct dm_target_io *tio; @@ -1254,10 +1242,10 @@ static int __clone_and_map(struct clone_info *ci) len += bv_len; } - tio = alloc_tio(ci, ti); - clone = clone_bio(bio, ci->sector, ci->idx, i - ci->idx, len, - ci->md->bs); - __map_bio(ti, clone, tio); + tio = alloc_tio(ci, ti, bio->bi_max_vecs); + clone_bio(tio, bio, ci->sector, ci->idx, i - ci->idx, len, + ci->md->bs); + __map_bio(ti, tio); ci->sector += len; ci->sector_count -= len; @@ -1282,12 +1270,11 @@ static int __clone_and_map(struct clone_info *ci) len = min(remaining, max); - tio = alloc_tio(ci, ti); - clone = split_bvec(bio, ci->sector, ci->idx, - bv->bv_offset + offset, len, - ci->md->bs); + tio = alloc_tio(ci, ti, 1); + split_bvec(tio, bio, ci->sector, ci->idx, + bv->bv_offset + offset, len, ci->md->bs); - __map_bio(ti, clone, tio); + __map_bio(ti, tio); ci->sector += len; ci->sector_count -= len; @@ -1955,7 +1942,7 @@ static void __bind_mempools(struct mapped_device *md, struct dm_table *t) { struct dm_md_mempools *p; - if (md->io_pool && md->tio_pool && md->bs) + if (md->io_pool && (md->tio_pool || dm_table_get_type(t) == DM_TYPE_BIO_BASED) && md->bs) /* the md already has necessary mempools */ goto out; @@ -2732,14 +2719,16 @@ struct dm_md_mempools *dm_alloc_md_mempools(unsigned type, unsigned integrity) if (!pools->io_pool) goto free_pools_and_out; - pools->tio_pool = (type == DM_TYPE_BIO_BASED) ? - mempool_create_slab_pool(MIN_IOS, _tio_cache) : - mempool_create_slab_pool(MIN_IOS, _rq_tio_cache); - if (!pools->tio_pool) - goto free_io_pool_and_out; + pools->tio_pool = NULL; + if (type == DM_TYPE_REQUEST_BASED) { + pools->tio_pool = mempool_create_slab_pool(MIN_IOS, _rq_tio_cache); + if (!pools->tio_pool) + goto free_io_pool_and_out; + } pools->bs = (type == DM_TYPE_BIO_BASED) ? - bioset_create(pool_size, 0) : + bioset_create(pool_size, + offsetof(struct dm_target_io, clone)) : bioset_create(pool_size, offsetof(struct dm_rq_clone_bio_info, clone)); if (!pools->bs) @@ -2754,7 +2743,8 @@ free_bioset_and_out: bioset_free(pools->bs); free_tio_pool_and_out: - mempool_destroy(pools->tio_pool); + if (pools->tio_pool) + mempool_destroy(pools->tio_pool); free_io_pool_and_out: mempool_destroy(pools->io_pool); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index fa211d80fc0..21014836bdb 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -138,6 +138,7 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) struct linear_conf *conf; struct md_rdev *rdev; int i, cnt; + bool discard_supported = false; conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(struct dev_info), GFP_KERNEL); @@ -171,6 +172,8 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) conf->array_sectors += rdev->sectors; cnt++; + if (blk_queue_discard(bdev_get_queue(rdev->bdev))) + discard_supported = true; } if (cnt != raid_disks) { printk(KERN_ERR "md/linear:%s: not enough drives present. Aborting!\n", @@ -178,6 +181,11 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) goto out; } + if (!discard_supported) + queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); + else + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); + /* * Here we calculate the device offsets. */ @@ -244,7 +252,9 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev) if (!newconf) return -ENOMEM; - oldconf = rcu_dereference(mddev->private); + oldconf = rcu_dereference_protected(mddev->private, + lockdep_is_held( + &mddev->reconfig_mutex)); mddev->raid_disks++; rcu_assign_pointer(mddev->private, newconf); md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); @@ -256,7 +266,10 @@ static int linear_add(struct mddev *mddev, struct md_rdev *rdev) static int linear_stop (struct mddev *mddev) { - struct linear_conf *conf = mddev->private; + struct linear_conf *conf = + rcu_dereference_protected(mddev->private, + lockdep_is_held( + &mddev->reconfig_mutex)); /* * We do not require rcu protection here since @@ -326,6 +339,14 @@ static void linear_make_request(struct mddev *mddev, struct bio *bio) bio->bi_sector = bio->bi_sector - start_sector + tmp_dev->rdev->data_offset; rcu_read_unlock(); + + if (unlikely((bio->bi_rw & REQ_DISCARD) && + !blk_queue_discard(bdev_get_queue(bio->bi_bdev)))) { + /* Just ignore it */ + bio_endio(bio, 0); + return; + } + generic_make_request(bio); } diff --git a/drivers/md/md.c b/drivers/md/md.c index 95c88012a3b..9ab768acfb6 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -674,7 +674,18 @@ static struct md_rdev * find_rdev_nr(struct mddev *mddev, int nr) return NULL; } -static struct md_rdev * find_rdev(struct mddev * mddev, dev_t dev) +static struct md_rdev *find_rdev_nr_rcu(struct mddev *mddev, int nr) +{ + struct md_rdev *rdev; + + rdev_for_each_rcu(rdev, mddev) + if (rdev->desc_nr == nr) + return rdev; + + return NULL; +} + +static struct md_rdev *find_rdev(struct mddev *mddev, dev_t dev) { struct md_rdev *rdev; @@ -685,6 +696,17 @@ static struct md_rdev * find_rdev(struct mddev * mddev, dev_t dev) return NULL; } +static struct md_rdev *find_rdev_rcu(struct mddev *mddev, dev_t dev) +{ + struct md_rdev *rdev; + + rdev_for_each_rcu(rdev, mddev) + if (rdev->bdev->bd_dev == dev) + return rdev; + + return NULL; +} + static struct md_personality *find_pers(int level, char *clevel) { struct md_personality *pers; @@ -2022,8 +2044,14 @@ EXPORT_SYMBOL(md_integrity_register); /* Disable data integrity if non-capable/non-matching disk is being added */ void md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev) { - struct blk_integrity *bi_rdev = bdev_get_integrity(rdev->bdev); - struct blk_integrity *bi_mddev = blk_get_integrity(mddev->gendisk); + struct blk_integrity *bi_rdev; + struct blk_integrity *bi_mddev; + + if (!mddev->gendisk) + return; + + bi_rdev = bdev_get_integrity(rdev->bdev); + bi_mddev = blk_get_integrity(mddev->gendisk); if (!bi_mddev) /* nothing to do */ return; @@ -3754,6 +3782,8 @@ resync_start_store(struct mddev *mddev, const char *buf, size_t len) return -EINVAL; mddev->recovery_cp = n; + if (mddev->pers) + set_bit(MD_CHANGE_CLEAN, &mddev->flags); return len; } static struct md_sysfs_entry md_resync_start = @@ -4231,6 +4261,13 @@ action_store(struct mddev *mddev, const char *page, size_t len) set_bit(MD_RECOVERY_REQUESTED, &mddev->recovery); set_bit(MD_RECOVERY_SYNC, &mddev->recovery); } + if (mddev->ro == 2) { + /* A write to sync_action is enough to justify + * canceling read-auto mode + */ + mddev->ro = 0; + md_wakeup_thread(mddev->sync_thread); + } set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); md_wakeup_thread(mddev->thread); sysfs_notify_dirent_safe(mddev->sysfs_action); @@ -4241,7 +4278,8 @@ static ssize_t mismatch_cnt_show(struct mddev *mddev, char *page) { return sprintf(page, "%llu\n", - (unsigned long long) mddev->resync_mismatches); + (unsigned long long) + atomic64_read(&mddev->resync_mismatches)); } static struct md_sysfs_entry md_scan_mode = @@ -4362,6 +4400,10 @@ sync_completed_show(struct mddev *mddev, char *page) if (!test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) return sprintf(page, "none\n"); + if (mddev->curr_resync == 1 || + mddev->curr_resync == 2) + return sprintf(page, "delayed\n"); + if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) || test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) max_sectors = mddev->resync_max_sectors; @@ -5207,7 +5249,7 @@ static void md_clean(struct mddev *mddev) mddev->new_layout = 0; mddev->new_chunk_sectors = 0; mddev->curr_resync = 0; - mddev->resync_mismatches = 0; + atomic64_set(&mddev->resync_mismatches, 0); mddev->suspend_lo = mddev->suspend_hi = 0; mddev->sync_speed_min = mddev->sync_speed_max = 0; mddev->recovery = 0; @@ -5509,8 +5551,9 @@ static int get_array_info(struct mddev * mddev, void __user * arg) int nr,working,insync,failed,spare; struct md_rdev *rdev; - nr=working=insync=failed=spare=0; - rdev_for_each(rdev, mddev) { + nr = working = insync = failed = spare = 0; + rcu_read_lock(); + rdev_for_each_rcu(rdev, mddev) { nr++; if (test_bit(Faulty, &rdev->flags)) failed++; @@ -5522,6 +5565,7 @@ static int get_array_info(struct mddev * mddev, void __user * arg) spare++; } } + rcu_read_unlock(); info.major_version = mddev->major_version; info.minor_version = mddev->minor_version; @@ -5605,7 +5649,8 @@ static int get_disk_info(struct mddev * mddev, void __user * arg) if (copy_from_user(&info, arg, sizeof(info))) return -EFAULT; - rdev = find_rdev_nr(mddev, info.number); + rcu_read_lock(); + rdev = find_rdev_nr_rcu(mddev, info.number); if (rdev) { info.major = MAJOR(rdev->bdev->bd_dev); info.minor = MINOR(rdev->bdev->bd_dev); @@ -5624,6 +5669,7 @@ static int get_disk_info(struct mddev * mddev, void __user * arg) info.raid_disk = -1; info.state = (1<<MD_DISK_REMOVED); } + rcu_read_unlock(); if (copy_to_user(arg, &info, sizeof(info))) return -EFAULT; @@ -6232,18 +6278,22 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info) static int set_disk_faulty(struct mddev *mddev, dev_t dev) { struct md_rdev *rdev; + int err = 0; if (mddev->pers == NULL) return -ENODEV; - rdev = find_rdev(mddev, dev); + rcu_read_lock(); + rdev = find_rdev_rcu(mddev, dev); if (!rdev) - return -ENODEV; - - md_error(mddev, rdev); - if (!test_bit(Faulty, &rdev->flags)) - return -EBUSY; - return 0; + err = -ENODEV; + else { + md_error(mddev, rdev); + if (!test_bit(Faulty, &rdev->flags)) + err = -EBUSY; + } + rcu_read_unlock(); + return err; } /* @@ -6315,6 +6365,27 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, goto abort; } + /* Some actions do not requires the mutex */ + switch (cmd) { + case GET_ARRAY_INFO: + if (!mddev->raid_disks && !mddev->external) + err = -ENODEV; + else + err = get_array_info(mddev, argp); + goto abort; + + case GET_DISK_INFO: + if (!mddev->raid_disks && !mddev->external) + err = -ENODEV; + else + err = get_disk_info(mddev, argp); + goto abort; + + case SET_DISK_FAULTY: + err = set_disk_faulty(mddev, new_decode_dev(arg)); + goto abort; + } + err = mddev_lock(mddev); if (err) { printk(KERN_INFO @@ -6387,18 +6458,10 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, */ switch (cmd) { - case GET_ARRAY_INFO: - err = get_array_info(mddev, argp); - goto done_unlock; - case GET_BITMAP_FILE: err = get_bitmap_file(mddev, argp); goto done_unlock; - case GET_DISK_INFO: - err = get_disk_info(mddev, argp); - goto done_unlock; - case RESTART_ARRAY_RW: err = restart_array(mddev); goto done_unlock; @@ -6480,10 +6543,6 @@ static int md_ioctl(struct block_device *bdev, fmode_t mode, err = hot_add_disk(mddev, new_decode_dev(arg)); goto done_unlock; - case SET_DISK_FAULTY: - err = set_disk_faulty(mddev, new_decode_dev(arg)); - goto done_unlock; - case RUN_ARRAY: err = do_md_run(mddev); goto done_unlock; @@ -6641,7 +6700,7 @@ static int md_thread(void * arg) clear_bit(THREAD_WAKEUP, &thread->flags); if (!kthread_should_stop()) - thread->run(thread->mddev); + thread->run(thread); } return 0; @@ -6656,8 +6715,8 @@ void md_wakeup_thread(struct md_thread *thread) } } -struct md_thread *md_register_thread(void (*run) (struct mddev *), struct mddev *mddev, - const char *name) +struct md_thread *md_register_thread(void (*run) (struct md_thread *), + struct mddev *mddev, const char *name) { struct md_thread *thread; @@ -6752,7 +6811,11 @@ static void status_resync(struct seq_file *seq, struct mddev * mddev) int scale; unsigned int per_milli; - resync = mddev->curr_resync - atomic_read(&mddev->recovery_active); + if (mddev->curr_resync <= 3) + resync = 0; + else + resync = mddev->curr_resync + - atomic_read(&mddev->recovery_active); if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) || test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) @@ -6978,7 +7041,7 @@ static int md_seq_show(struct seq_file *seq, void *v) if (mddev->curr_resync > 2) { status_resync(seq, mddev); seq_printf(seq, "\n "); - } else if (mddev->curr_resync == 1 || mddev->curr_resync == 2) + } else if (mddev->curr_resync >= 1) seq_printf(seq, "\tresync=DELAYED\n "); else if (mddev->recovery_cp < MaxSector) seq_printf(seq, "\tresync=PENDING\n "); @@ -7206,8 +7269,9 @@ EXPORT_SYMBOL_GPL(md_allow_write); #define SYNC_MARKS 10 #define SYNC_MARK_STEP (3*HZ) -void md_do_sync(struct mddev *mddev) +void md_do_sync(struct md_thread *thread) { + struct mddev *mddev = thread->mddev; struct mddev *mddev2; unsigned int currspeed = 0, window; @@ -7311,7 +7375,7 @@ void md_do_sync(struct mddev *mddev) * which defaults to physical size, but can be virtual size */ max_sectors = mddev->resync_max_sectors; - mddev->resync_mismatches = 0; + atomic64_set(&mddev->resync_mismatches, 0); /* we don't use the checkpoint if there's a bitmap */ if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) j = mddev->resync_min; @@ -7367,8 +7431,11 @@ void md_do_sync(struct mddev *mddev) "md: resuming %s of %s from checkpoint.\n", desc, mdname(mddev)); mddev->curr_resync = j; - } + } else + mddev->curr_resync = 3; /* no longer delayed */ mddev->curr_resync_completed = j; + sysfs_notify(&mddev->kobj, NULL, "sync_completed"); + md_new_event(mddev); blk_start_plug(&plug); while (j < max_sectors) { @@ -7421,7 +7488,8 @@ void md_do_sync(struct mddev *mddev) break; j += sectors; - if (j>1) mddev->curr_resync = j; + if (j > 2) + mddev->curr_resync = j; mddev->curr_mark_cnt = io_sectors; if (last_check == 0) /* this is the earliest that rebuild will be @@ -7543,8 +7611,6 @@ static int remove_and_add_spares(struct mddev *mddev) int spares = 0; int removed = 0; - mddev->curr_resync_completed = 0; - rdev_for_each(rdev, mddev) if (rdev->raid_disk >= 0 && !test_bit(Blocked, &rdev->flags) && @@ -7739,6 +7805,7 @@ void md_check_recovery(struct mddev *mddev) /* Set RUNNING before clearing NEEDED to avoid * any transients in the value of "sync_action". */ + mddev->curr_resync_completed = 0; set_bit(MD_RECOVERY_RUNNING, &mddev->recovery); /* Clear some bits that don't mean anything, but * might be left set @@ -7752,7 +7819,7 @@ void md_check_recovery(struct mddev *mddev) /* no recovery is running. * remove any failed drives, then * add spares if possible. - * Spare are also removed and re-added, to allow + * Spares are also removed and re-added, to allow * the personality to fail the re-add. */ diff --git a/drivers/md/md.h b/drivers/md/md.h index f385b038589..af443ab868d 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -282,7 +282,7 @@ struct mddev { sector_t resync_max_sectors; /* may be set by personality */ - sector_t resync_mismatches; /* count of sectors where + atomic64_t resync_mismatches; /* count of sectors where * parity/replica mismatch found */ @@ -540,12 +540,13 @@ static inline void sysfs_unlink_rdev(struct mddev *mddev, struct md_rdev *rdev) list_for_each_entry_rcu(rdev, &((mddev)->disks), same_set) struct md_thread { - void (*run) (struct mddev *mddev); + void (*run) (struct md_thread *thread); struct mddev *mddev; wait_queue_head_t wqueue; unsigned long flags; struct task_struct *tsk; unsigned long timeout; + void *private; }; #define THREAD_WAKEUP 0 @@ -584,7 +585,7 @@ static inline void safe_put_page(struct page *p) extern int register_md_personality(struct md_personality *p); extern int unregister_md_personality(struct md_personality *p); extern struct md_thread *md_register_thread( - void (*run)(struct mddev *mddev), + void (*run)(struct md_thread *thread), struct mddev *mddev, const char *name); extern void md_unregister_thread(struct md_thread **threadp); @@ -603,7 +604,7 @@ extern void md_super_write(struct mddev *mddev, struct md_rdev *rdev, extern void md_super_wait(struct mddev *mddev); extern int sync_page_io(struct md_rdev *rdev, sector_t sector, int size, struct page *page, int rw, bool metadata_op); -extern void md_do_sync(struct mddev *mddev); +extern void md_do_sync(struct md_thread *thread); extern void md_new_event(struct mddev *mddev); extern int md_allow_write(struct mddev *mddev); extern void md_wait_for_blocked_rdev(struct md_rdev *rdev, struct mddev *mddev); diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 61a1833ebaf..1642eae75a3 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -335,8 +335,9 @@ abort: * 3. Performs writes following reads for array syncronising. */ -static void multipathd (struct mddev *mddev) +static void multipathd(struct md_thread *thread) { + struct mddev *mddev = thread->mddev; struct multipath_bh *mp_bh; struct bio *bio; unsigned long flags; diff --git a/drivers/md/persistent-data/dm-space-map-common.c b/drivers/md/persistent-data/dm-space-map-common.c index d77602d63c8..f3a9af8cdec 100644 --- a/drivers/md/persistent-data/dm-space-map-common.c +++ b/drivers/md/persistent-data/dm-space-map-common.c @@ -434,14 +434,14 @@ int sm_ll_insert(struct ll_disk *ll, dm_block_t b, if (ref_count && !old) { *ev = SM_ALLOC; ll->nr_allocated++; - ie_disk.nr_free = cpu_to_le32(le32_to_cpu(ie_disk.nr_free) - 1); + le32_add_cpu(&ie_disk.nr_free, -1); if (le32_to_cpu(ie_disk.none_free_before) == bit) ie_disk.none_free_before = cpu_to_le32(bit + 1); } else if (old && !ref_count) { *ev = SM_FREE; ll->nr_allocated--; - ie_disk.nr_free = cpu_to_le32(le32_to_cpu(ie_disk.nr_free) + 1); + le32_add_cpu(&ie_disk.nr_free, 1); ie_disk.none_free_before = cpu_to_le32(min(le32_to_cpu(ie_disk.none_free_before), bit)); } diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index a9e4fa95dfa..24b359717a7 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -88,6 +88,7 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) char b[BDEVNAME_SIZE]; char b2[BDEVNAME_SIZE]; struct r0conf *conf = kzalloc(sizeof(*conf), GFP_KERNEL); + bool discard_supported = false; if (!conf) return -ENOMEM; @@ -195,6 +196,9 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) if (!smallest || (rdev1->sectors < smallest->sectors)) smallest = rdev1; cnt++; + + if (blk_queue_discard(bdev_get_queue(rdev1->bdev))) + discard_supported = true; } if (cnt != mddev->raid_disks) { printk(KERN_ERR "md/raid0:%s: too few disks (%d of %d) - " @@ -272,6 +276,11 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf) blk_queue_io_opt(mddev->queue, (mddev->chunk_sectors << 9) * mddev->raid_disks); + if (!discard_supported) + queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); + else + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); + pr_debug("md/raid0:%s: done.\n", mdname(mddev)); *private_conf = conf; @@ -423,6 +432,7 @@ static int raid0_run(struct mddev *mddev) return -EINVAL; blk_queue_max_hw_sectors(mddev->queue, mddev->chunk_sectors); blk_queue_max_write_same_sectors(mddev->queue, mddev->chunk_sectors); + blk_queue_max_discard_sectors(mddev->queue, mddev->chunk_sectors); /* if private is not null, we are here after takeover */ if (mddev->private == NULL) { @@ -510,7 +520,7 @@ static void raid0_make_request(struct mddev *mddev, struct bio *bio) sector_t sector = bio->bi_sector; struct bio_pair *bp; /* Sanity check -- queue functions should prevent this happening */ - if (bio->bi_vcnt != 1 || + if ((bio->bi_vcnt != 1 && bio->bi_vcnt != 0) || bio->bi_idx != 0) goto bad_map; /* This is a one page bio that upper layers @@ -536,6 +546,13 @@ static void raid0_make_request(struct mddev *mddev, struct bio *bio) bio->bi_sector = sector_offset + zone->dev_start + tmp_dev->data_offset; + if (unlikely((bio->bi_rw & REQ_DISCARD) && + !blk_queue_discard(bdev_get_queue(bio->bi_bdev)))) { + /* Just ignore it */ + bio_endio(bio, 0); + return; + } + generic_make_request(bio); return; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 611b5f79761..8034fbd6190 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -333,9 +333,10 @@ static void raid1_end_read_request(struct bio *bio, int error) spin_unlock_irqrestore(&conf->device_lock, flags); } - if (uptodate) + if (uptodate) { raid_end_bio_io(r1_bio); - else { + rdev_dec_pending(conf->mirrors[mirror].rdev, conf->mddev); + } else { /* * oops, read error: */ @@ -349,9 +350,8 @@ static void raid1_end_read_request(struct bio *bio, int error) (unsigned long long)r1_bio->sector); set_bit(R1BIO_ReadError, &r1_bio->state); reschedule_retry(r1_bio); + /* don't drop the reference on read_disk yet */ } - - rdev_dec_pending(conf->mirrors[mirror].rdev, conf->mddev); } static void close_write(struct r1bio *r1_bio) @@ -781,7 +781,12 @@ static void flush_pending_writes(struct r1conf *conf) while (bio) { /* submit pending writes */ struct bio *next = bio->bi_next; bio->bi_next = NULL; - generic_make_request(bio); + if (unlikely((bio->bi_rw & REQ_DISCARD) && + !blk_queue_discard(bdev_get_queue(bio->bi_bdev)))) + /* Just ignore it */ + bio_endio(bio, 0); + else + generic_make_request(bio); bio = next; } } else @@ -994,6 +999,8 @@ static void make_request(struct mddev *mddev, struct bio * bio) const int rw = bio_data_dir(bio); const unsigned long do_sync = (bio->bi_rw & REQ_SYNC); const unsigned long do_flush_fua = (bio->bi_rw & (REQ_FLUSH | REQ_FUA)); + const unsigned long do_discard = (bio->bi_rw + & (REQ_DISCARD | REQ_SECURE)); struct md_rdev *blocked_rdev; struct blk_plug_cb *cb; struct raid1_plug_cb *plug = NULL; @@ -1295,7 +1302,7 @@ read_again: conf->mirrors[i].rdev->data_offset); mbio->bi_bdev = conf->mirrors[i].rdev->bdev; mbio->bi_end_io = raid1_end_write_request; - mbio->bi_rw = WRITE | do_flush_fua | do_sync; + mbio->bi_rw = WRITE | do_flush_fua | do_sync | do_discard; mbio->bi_private = r1_bio; atomic_inc(&r1_bio->remaining); @@ -1549,6 +1556,8 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev) clear_bit(Unmerged, &rdev->flags); } md_integrity_add_rdev(rdev, mddev); + if (blk_queue_discard(bdev_get_queue(rdev->bdev))) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); print_conf(conf); return err; } @@ -1867,7 +1876,7 @@ static int process_checks(struct r1bio *r1_bio) } else j = 0; if (j >= 0) - mddev->resync_mismatches += r1_bio->sectors; + atomic64_add(r1_bio->sectors, &mddev->resync_mismatches); if (j < 0 || (test_bit(MD_RECOVERY_CHECK, &mddev->recovery) && test_bit(BIO_UPTODATE, &sbio->bi_flags))) { /* No need to write to this device. */ @@ -2220,6 +2229,7 @@ static void handle_read_error(struct r1conf *conf, struct r1bio *r1_bio) unfreeze_array(conf); } else md_error(mddev, conf->mirrors[r1_bio->read_disk].rdev); + rdev_dec_pending(conf->mirrors[r1_bio->read_disk].rdev, conf->mddev); bio = r1_bio->bios[r1_bio->read_disk]; bdevname(bio->bi_bdev, b); @@ -2285,8 +2295,9 @@ read_more: } } -static void raid1d(struct mddev *mddev) +static void raid1d(struct md_thread *thread) { + struct mddev *mddev = thread->mddev; struct r1bio *r1_bio; unsigned long flags; struct r1conf *conf = mddev->private; @@ -2783,6 +2794,7 @@ static int run(struct mddev *mddev) int i; struct md_rdev *rdev; int ret; + bool discard_supported = false; if (mddev->level != 1) { printk(KERN_ERR "md/raid1:%s: raid level not set to mirroring (%d)\n", @@ -2812,6 +2824,8 @@ static int run(struct mddev *mddev) continue; disk_stack_limits(mddev->gendisk, rdev->bdev, rdev->data_offset << 9); + if (blk_queue_discard(bdev_get_queue(rdev->bdev))) + discard_supported = true; } mddev->degraded = 0; @@ -2846,6 +2860,13 @@ static int run(struct mddev *mddev) mddev->queue->backing_dev_info.congested_fn = raid1_congested; mddev->queue->backing_dev_info.congested_data = mddev; blk_queue_merge_bvec(mddev->queue, raid1_mergeable_bvec); + + if (discard_supported) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, + mddev->queue); + else + queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, + mddev->queue); } ret = md_integrity_register(mddev); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 0138a727c1f..906ccbd0f7d 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -911,7 +911,12 @@ static void flush_pending_writes(struct r10conf *conf) while (bio) { /* submit pending writes */ struct bio *next = bio->bi_next; bio->bi_next = NULL; - generic_make_request(bio); + if (unlikely((bio->bi_rw & REQ_DISCARD) && + !blk_queue_discard(bdev_get_queue(bio->bi_bdev)))) + /* Just ignore it */ + bio_endio(bio, 0); + else + generic_make_request(bio); bio = next; } } else @@ -1050,6 +1055,44 @@ static sector_t choose_data_offset(struct r10bio *r10_bio, return rdev->new_data_offset; } +struct raid10_plug_cb { + struct blk_plug_cb cb; + struct bio_list pending; + int pending_cnt; +}; + +static void raid10_unplug(struct blk_plug_cb *cb, bool from_schedule) +{ + struct raid10_plug_cb *plug = container_of(cb, struct raid10_plug_cb, + cb); + struct mddev *mddev = plug->cb.data; + struct r10conf *conf = mddev->private; + struct bio *bio; + + if (from_schedule) { + spin_lock_irq(&conf->device_lock); + bio_list_merge(&conf->pending_bio_list, &plug->pending); + conf->pending_count += plug->pending_cnt; + spin_unlock_irq(&conf->device_lock); + md_wakeup_thread(mddev->thread); + kfree(plug); + return; + } + + /* we aren't scheduling, so we can do the write-out directly. */ + bio = bio_list_get(&plug->pending); + bitmap_unplug(mddev->bitmap); + wake_up(&conf->wait_barrier); + + while (bio) { /* submit pending writes */ + struct bio *next = bio->bi_next; + bio->bi_next = NULL; + generic_make_request(bio); + bio = next; + } + kfree(plug); +} + static void make_request(struct mddev *mddev, struct bio * bio) { struct r10conf *conf = mddev->private; @@ -1061,8 +1104,12 @@ static void make_request(struct mddev *mddev, struct bio * bio) const int rw = bio_data_dir(bio); const unsigned long do_sync = (bio->bi_rw & REQ_SYNC); const unsigned long do_fua = (bio->bi_rw & REQ_FUA); + const unsigned long do_discard = (bio->bi_rw + & (REQ_DISCARD | REQ_SECURE)); unsigned long flags; struct md_rdev *blocked_rdev; + struct blk_plug_cb *cb; + struct raid10_plug_cb *plug = NULL; int sectors_handled; int max_sectors; int sectors; @@ -1081,7 +1128,7 @@ static void make_request(struct mddev *mddev, struct bio * bio) || conf->prev.near_copies < conf->prev.raid_disks))) { struct bio_pair *bp; /* Sanity check -- queue functions should prevent this happening */ - if (bio->bi_vcnt != 1 || + if ((bio->bi_vcnt != 1 && bio->bi_vcnt != 0) || bio->bi_idx != 0) goto bad_map; /* This is a one page bio that upper layers @@ -1410,15 +1457,26 @@ retry_write: conf->mirrors[d].rdev)); mbio->bi_bdev = conf->mirrors[d].rdev->bdev; mbio->bi_end_io = raid10_end_write_request; - mbio->bi_rw = WRITE | do_sync | do_fua; + mbio->bi_rw = WRITE | do_sync | do_fua | do_discard; mbio->bi_private = r10_bio; atomic_inc(&r10_bio->remaining); + + cb = blk_check_plugged(raid10_unplug, mddev, sizeof(*plug)); + if (cb) + plug = container_of(cb, struct raid10_plug_cb, cb); + else + plug = NULL; spin_lock_irqsave(&conf->device_lock, flags); - bio_list_add(&conf->pending_bio_list, mbio); - conf->pending_count++; + if (plug) { + bio_list_add(&plug->pending, mbio); + plug->pending_cnt++; + } else { + bio_list_add(&conf->pending_bio_list, mbio); + conf->pending_count++; + } spin_unlock_irqrestore(&conf->device_lock, flags); - if (!mddev_check_plugged(mddev)) + if (!plug) md_wakeup_thread(mddev->thread); if (!r10_bio->devs[i].repl_bio) @@ -1439,7 +1497,7 @@ retry_write: conf->mirrors[d].replacement)); mbio->bi_bdev = conf->mirrors[d].replacement->bdev; mbio->bi_end_io = raid10_end_write_request; - mbio->bi_rw = WRITE | do_sync | do_fua; + mbio->bi_rw = WRITE | do_sync | do_fua | do_discard; mbio->bi_private = r10_bio; atomic_inc(&r10_bio->remaining); @@ -1638,7 +1696,7 @@ static int raid10_spare_active(struct mddev *mddev) && !test_bit(Faulty, &tmp->rdev->flags) && !test_and_set_bit(In_sync, &tmp->rdev->flags)) { count++; - sysfs_notify_dirent(tmp->rdev->sysfs_state); + sysfs_notify_dirent_safe(tmp->rdev->sysfs_state); } } spin_lock_irqsave(&conf->device_lock, flags); @@ -1725,6 +1783,9 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev) clear_bit(Unmerged, &rdev->flags); } md_integrity_add_rdev(rdev, mddev); + if (blk_queue_discard(bdev_get_queue(rdev->bdev))) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); + print_conf(conf); return err; } @@ -1952,7 +2013,7 @@ static void sync_request_write(struct mddev *mddev, struct r10bio *r10_bio) break; if (j == vcnt) continue; - mddev->resync_mismatches += r10_bio->sectors; + atomic64_add(r10_bio->sectors, &mddev->resync_mismatches); if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery)) /* Don't fix anything. */ continue; @@ -2673,8 +2734,9 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio) } } -static void raid10d(struct mddev *mddev) +static void raid10d(struct md_thread *thread) { + struct mddev *mddev = thread->mddev; struct r10bio *r10_bio; unsigned long flags; struct r10conf *conf = mddev->private; @@ -3158,7 +3220,7 @@ static sector_t sync_request(struct mddev *mddev, sector_t sector_nr, else { bad_sectors -= (sector - first_bad); if (max_sync > bad_sectors) - max_sync = max_sync; + max_sync = bad_sectors; continue; } } @@ -3482,6 +3544,7 @@ static int run(struct mddev *mddev) sector_t size; sector_t min_offset_diff = 0; int first = 1; + bool discard_supported = false; if (mddev->private == NULL) { conf = setup_conf(mddev); @@ -3498,6 +3561,8 @@ static int run(struct mddev *mddev) chunk_size = mddev->chunk_sectors << 9; if (mddev->queue) { + blk_queue_max_discard_sectors(mddev->queue, + mddev->chunk_sectors); blk_queue_io_min(mddev->queue, chunk_size); if (conf->geo.raid_disks % conf->geo.near_copies) blk_queue_io_opt(mddev->queue, chunk_size * conf->geo.raid_disks); @@ -3543,8 +3608,16 @@ static int run(struct mddev *mddev) rdev->data_offset << 9); disk->head_position = 0; + + if (blk_queue_discard(bdev_get_queue(rdev->bdev))) + discard_supported = true; } + if (discard_supported) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); + else + queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); + /* need to check that every block has at least one working mirror */ if (!enough(conf, -1)) { printk(KERN_ERR "md/raid10:%s: not enough operational mirrors.\n", diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 0689173fd9f..c5439dce029 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -551,6 +551,8 @@ static void ops_run_io(struct stripe_head *sh, struct stripe_head_state *s) rw = WRITE_FUA; else rw = WRITE; + if (test_bit(R5_Discard, &sh->dev[i].flags)) + rw |= REQ_DISCARD; } else if (test_and_clear_bit(R5_Wantread, &sh->dev[i].flags)) rw = READ; else if (test_and_clear_bit(R5_WantReplace, @@ -1174,8 +1176,11 @@ ops_run_biodrain(struct stripe_head *sh, struct dma_async_tx_descriptor *tx) set_bit(R5_WantFUA, &dev->flags); if (wbi->bi_rw & REQ_SYNC) set_bit(R5_SyncIO, &dev->flags); - tx = async_copy_data(1, wbi, dev->page, - dev->sector, tx); + if (wbi->bi_rw & REQ_DISCARD) + set_bit(R5_Discard, &dev->flags); + else + tx = async_copy_data(1, wbi, dev->page, + dev->sector, tx); wbi = r5_next_bio(wbi, dev->sector); } } @@ -1191,7 +1196,7 @@ static void ops_complete_reconstruct(void *stripe_head_ref) int pd_idx = sh->pd_idx; int qd_idx = sh->qd_idx; int i; - bool fua = false, sync = false; + bool fua = false, sync = false, discard = false; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); @@ -1199,13 +1204,15 @@ static void ops_complete_reconstruct(void *stripe_head_ref) for (i = disks; i--; ) { fua |= test_bit(R5_WantFUA, &sh->dev[i].flags); sync |= test_bit(R5_SyncIO, &sh->dev[i].flags); + discard |= test_bit(R5_Discard, &sh->dev[i].flags); } for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; if (dev->written || i == pd_idx || i == qd_idx) { - set_bit(R5_UPTODATE, &dev->flags); + if (!discard) + set_bit(R5_UPTODATE, &dev->flags); if (fua) set_bit(R5_WantFUA, &dev->flags); if (sync) @@ -1241,6 +1248,18 @@ ops_run_reconstruct5(struct stripe_head *sh, struct raid5_percpu *percpu, pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); + for (i = 0; i < sh->disks; i++) { + if (pd_idx == i) + continue; + if (!test_bit(R5_Discard, &sh->dev[i].flags)) + break; + } + if (i >= sh->disks) { + atomic_inc(&sh->count); + set_bit(R5_Discard, &sh->dev[pd_idx].flags); + ops_complete_reconstruct(sh); + return; + } /* check if prexor is active which means only process blocks * that are part of a read-modify-write (written) */ @@ -1285,10 +1304,24 @@ ops_run_reconstruct6(struct stripe_head *sh, struct raid5_percpu *percpu, { struct async_submit_ctl submit; struct page **blocks = percpu->scribble; - int count; + int count, i; pr_debug("%s: stripe %llu\n", __func__, (unsigned long long)sh->sector); + for (i = 0; i < sh->disks; i++) { + if (sh->pd_idx == i || sh->qd_idx == i) + continue; + if (!test_bit(R5_Discard, &sh->dev[i].flags)) + break; + } + if (i >= sh->disks) { + atomic_inc(&sh->count); + set_bit(R5_Discard, &sh->dev[sh->pd_idx].flags); + set_bit(R5_Discard, &sh->dev[sh->qd_idx].flags); + ops_complete_reconstruct(sh); + return; + } + count = set_syndrome_sources(blocks, sh); atomic_inc(&sh->count); @@ -2408,11 +2441,11 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in if (sector >= sh->dev[dd_idx].sector + STRIPE_SECTORS) set_bit(R5_OVERWRITE, &sh->dev[dd_idx].flags); } - spin_unlock_irq(&sh->stripe_lock); pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n", (unsigned long long)(*bip)->bi_sector, (unsigned long long)sh->sector, dd_idx); + spin_unlock_irq(&sh->stripe_lock); if (conf->mddev->bitmap && firstwrite) { bitmap_startwrite(conf->mddev->bitmap, sh->sector, @@ -2479,10 +2512,8 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh, bi = sh->dev[i].towrite; sh->dev[i].towrite = NULL; spin_unlock_irq(&sh->stripe_lock); - if (bi) { - s->to_write--; + if (bi) bitmap_end = 1; - } if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags)) wake_up(&conf->wait_for_overlap); @@ -2524,11 +2555,12 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh, if (!test_bit(R5_Wantfill, &sh->dev[i].flags) && (!test_bit(R5_Insync, &sh->dev[i].flags) || test_bit(R5_ReadError, &sh->dev[i].flags))) { + spin_lock_irq(&sh->stripe_lock); bi = sh->dev[i].toread; sh->dev[i].toread = NULL; + spin_unlock_irq(&sh->stripe_lock); if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags)) wake_up(&conf->wait_for_overlap); - if (bi) s->to_read--; while (bi && bi->bi_sector < sh->dev[i].sector + STRIPE_SECTORS) { struct bio *nextbi = @@ -2741,7 +2773,8 @@ static void handle_stripe_clean_event(struct r5conf *conf, if (sh->dev[i].written) { dev = &sh->dev[i]; if (!test_bit(R5_LOCKED, &dev->flags) && - test_bit(R5_UPTODATE, &dev->flags)) { + (test_bit(R5_UPTODATE, &dev->flags) || + test_and_clear_bit(R5_Discard, &dev->flags))) { /* We can return any write requests */ struct bio *wbi, *wbi2; pr_debug("Return write for disc %d\n", i); @@ -2775,12 +2808,25 @@ static void handle_stripe_dirtying(struct r5conf *conf, int disks) { int rmw = 0, rcw = 0, i; - if (conf->max_degraded == 2) { - /* RAID6 requires 'rcw' in current implementation - * Calculate the real rcw later - for now fake it + sector_t recovery_cp = conf->mddev->recovery_cp; + + /* RAID6 requires 'rcw' in current implementation. + * Otherwise, check whether resync is now happening or should start. + * If yes, then the array is dirty (after unclean shutdown or + * initial creation), so parity in some stripes might be inconsistent. + * In this case, we need to always do reconstruct-write, to ensure + * that in case of drive failure or read-error correction, we + * generate correct data from the parity. + */ + if (conf->max_degraded == 2 || + (recovery_cp < MaxSector && sh->sector >= recovery_cp)) { + /* Calculate the real rcw later - for now make it * look like rcw is cheaper */ rcw = 1; rmw = 2; + pr_debug("force RCW max_degraded=%u, recovery_cp=%llu sh->sector=%llu\n", + conf->max_degraded, (unsigned long long)recovery_cp, + (unsigned long long)sh->sector); } else for (i = disks; i--; ) { /* would I have to read this buffer for read_modify_write */ struct r5dev *dev = &sh->dev[i]; @@ -2932,7 +2978,7 @@ static void handle_parity_checks5(struct r5conf *conf, struct stripe_head *sh, */ set_bit(STRIPE_INSYNC, &sh->state); else { - conf->mddev->resync_mismatches += STRIPE_SECTORS; + atomic64_add(STRIPE_SECTORS, &conf->mddev->resync_mismatches); if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery)) /* don't try to repair!! */ set_bit(STRIPE_INSYNC, &sh->state); @@ -3084,7 +3130,7 @@ static void handle_parity_checks6(struct r5conf *conf, struct stripe_head *sh, */ } } else { - conf->mddev->resync_mismatches += STRIPE_SECTORS; + atomic64_add(STRIPE_SECTORS, &conf->mddev->resync_mismatches); if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery)) /* don't try to repair!! */ set_bit(STRIPE_INSYNC, &sh->state); @@ -3459,10 +3505,12 @@ static void handle_stripe(struct stripe_head *sh) if (s.written && (s.p_failed || ((test_bit(R5_Insync, &pdev->flags) && !test_bit(R5_LOCKED, &pdev->flags) - && test_bit(R5_UPTODATE, &pdev->flags)))) && + && (test_bit(R5_UPTODATE, &pdev->flags) || + test_bit(R5_Discard, &pdev->flags))))) && (s.q_failed || ((test_bit(R5_Insync, &qdev->flags) && !test_bit(R5_LOCKED, &qdev->flags) - && test_bit(R5_UPTODATE, &qdev->flags))))) + && (test_bit(R5_UPTODATE, &qdev->flags) || + test_bit(R5_Discard, &qdev->flags)))))) handle_stripe_clean_event(conf, sh, disks, &s.return_bi); /* Now we might consider reading some blocks, either to check/generate @@ -3489,9 +3537,11 @@ static void handle_stripe(struct stripe_head *sh) /* All the 'written' buffers and the parity block are ready to * be written back to disk */ - BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags)); + BUG_ON(!test_bit(R5_UPTODATE, &sh->dev[sh->pd_idx].flags) && + !test_bit(R5_Discard, &sh->dev[sh->pd_idx].flags)); BUG_ON(sh->qd_idx >= 0 && - !test_bit(R5_UPTODATE, &sh->dev[sh->qd_idx].flags)); + !test_bit(R5_UPTODATE, &sh->dev[sh->qd_idx].flags) && + !test_bit(R5_Discard, &sh->dev[sh->qd_idx].flags)); for (i = disks; i--; ) { struct r5dev *dev = &sh->dev[i]; if (test_bit(R5_LOCKED, &dev->flags) && @@ -4072,6 +4122,88 @@ static void release_stripe_plug(struct mddev *mddev, release_stripe(sh); } +static void make_discard_request(struct mddev *mddev, struct bio *bi) +{ + struct r5conf *conf = mddev->private; + sector_t logical_sector, last_sector; + struct stripe_head *sh; + int remaining; + int stripe_sectors; + + if (mddev->reshape_position != MaxSector) + /* Skip discard while reshape is happening */ + return; + + logical_sector = bi->bi_sector & ~((sector_t)STRIPE_SECTORS-1); + last_sector = bi->bi_sector + (bi->bi_size>>9); + + bi->bi_next = NULL; + bi->bi_phys_segments = 1; /* over-loaded to count active stripes */ + + stripe_sectors = conf->chunk_sectors * + (conf->raid_disks - conf->max_degraded); + logical_sector = DIV_ROUND_UP_SECTOR_T(logical_sector, + stripe_sectors); + sector_div(last_sector, stripe_sectors); + + logical_sector *= conf->chunk_sectors; + last_sector *= conf->chunk_sectors; + + for (; logical_sector < last_sector; + logical_sector += STRIPE_SECTORS) { + DEFINE_WAIT(w); + int d; + again: + sh = get_active_stripe(conf, logical_sector, 0, 0, 0); + prepare_to_wait(&conf->wait_for_overlap, &w, + TASK_UNINTERRUPTIBLE); + spin_lock_irq(&sh->stripe_lock); + for (d = 0; d < conf->raid_disks; d++) { + if (d == sh->pd_idx || d == sh->qd_idx) + continue; + if (sh->dev[d].towrite || sh->dev[d].toread) { + set_bit(R5_Overlap, &sh->dev[d].flags); + spin_unlock_irq(&sh->stripe_lock); + release_stripe(sh); + schedule(); + goto again; + } + } + finish_wait(&conf->wait_for_overlap, &w); + for (d = 0; d < conf->raid_disks; d++) { + if (d == sh->pd_idx || d == sh->qd_idx) + continue; + sh->dev[d].towrite = bi; + set_bit(R5_OVERWRITE, &sh->dev[d].flags); + raid5_inc_bi_active_stripes(bi); + } + spin_unlock_irq(&sh->stripe_lock); + if (conf->mddev->bitmap) { + for (d = 0; + d < conf->raid_disks - conf->max_degraded; + d++) + bitmap_startwrite(mddev->bitmap, + sh->sector, + STRIPE_SECTORS, + 0); + sh->bm_seq = conf->seq_flush + 1; + set_bit(STRIPE_BIT_DELAY, &sh->state); + } + + set_bit(STRIPE_HANDLE, &sh->state); + clear_bit(STRIPE_DELAYED, &sh->state); + if (!test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) + atomic_inc(&conf->preread_active_stripes); + release_stripe_plug(mddev, sh); + } + + remaining = raid5_dec_bi_active_stripes(bi); + if (remaining == 0) { + md_write_end(mddev); + bio_endio(bi, 0); + } +} + static void make_request(struct mddev *mddev, struct bio * bi) { struct r5conf *conf = mddev->private; @@ -4094,6 +4226,11 @@ static void make_request(struct mddev *mddev, struct bio * bi) chunk_aligned_read(mddev,bi)) return; + if (unlikely(bi->bi_rw & REQ_DISCARD)) { + make_discard_request(mddev, bi); + return; + } + logical_sector = bi->bi_sector & ~((sector_t)STRIPE_SECTORS-1); last_sector = bi->bi_sector + (bi->bi_size>>9); bi->bi_next = NULL; @@ -4630,8 +4767,9 @@ static int handle_active_stripes(struct r5conf *conf) * During the scan, completed stripes are saved for us by the interrupt * handler, so that they will not have to wait for our next wakeup. */ -static void raid5d(struct mddev *mddev) +static void raid5d(struct md_thread *thread) { + struct mddev *mddev = thread->mddev; struct r5conf *conf = mddev->private; int handled; struct blk_plug plug; @@ -5366,6 +5504,7 @@ static int run(struct mddev *mddev) if (mddev->queue) { int chunk_size; + bool discard_supported = true; /* read-ahead size must cover two whole stripes, which * is 2 * (datadisks) * chunksize where 'n' is the * number of raid devices @@ -5385,13 +5524,48 @@ static int run(struct mddev *mddev) blk_queue_io_min(mddev->queue, chunk_size); blk_queue_io_opt(mddev->queue, chunk_size * (conf->raid_disks - conf->max_degraded)); + /* + * We can only discard a whole stripe. It doesn't make sense to + * discard data disk but write parity disk + */ + stripe = stripe * PAGE_SIZE; + mddev->queue->limits.discard_alignment = stripe; + mddev->queue->limits.discard_granularity = stripe; + /* + * unaligned part of discard request will be ignored, so can't + * guarantee discard_zerors_data + */ + mddev->queue->limits.discard_zeroes_data = 0; rdev_for_each(rdev, mddev) { disk_stack_limits(mddev->gendisk, rdev->bdev, rdev->data_offset << 9); disk_stack_limits(mddev->gendisk, rdev->bdev, rdev->new_data_offset << 9); + /* + * discard_zeroes_data is required, otherwise data + * could be lost. Consider a scenario: discard a stripe + * (the stripe could be inconsistent if + * discard_zeroes_data is 0); write one disk of the + * stripe (the stripe could be inconsistent again + * depending on which disks are used to calculate + * parity); the disk is broken; The stripe data of this + * disk is lost. + */ + if (!blk_queue_discard(bdev_get_queue(rdev->bdev)) || + !bdev_get_queue(rdev->bdev)-> + limits.discard_zeroes_data) + discard_supported = false; } + + if (discard_supported && + mddev->queue->limits.max_discard_sectors >= stripe && + mddev->queue->limits.discard_granularity >= stripe) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, + mddev->queue); + else + queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, + mddev->queue); } return 0; @@ -5702,7 +5876,8 @@ static int check_reshape(struct mddev *mddev) if (!check_stripe_cache(mddev)) return -ENOSPC; - return resize_stripes(conf, conf->raid_disks + mddev->delta_disks); + return resize_stripes(conf, (conf->previous_raid_disks + + mddev->delta_disks)); } static int raid5_start_reshape(struct mddev *mddev) diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index a9fc24901ed..18b2c4a8a1f 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -298,6 +298,7 @@ enum r5dev_flags { R5_WantReplace, /* We need to update the replacement, we have read * data in, and now is a good time to write it out. */ + R5_Discard, /* Discard the stripe */ }; /* diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index bb4c2bf04d0..80d1e6d4b0a 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -525,7 +525,7 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); writel(BM_SSP_CTRL1_SDIO_IRQ_EN, - host->base + HW_SSP_CTRL1(host) + STMP_OFFSET_REG_SET); + ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_SET); } else { writel(BM_SSP_CTRL0_SDIO_IRQ_CHECK, ssp->base + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index cb3356c9af8..04668b47a1d 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -175,13 +175,13 @@ struct e1000_info; /* * in the case of WTHRESH, it appears at least the 82571/2 hardware * writes back 4 descriptors when WTHRESH=5, and 3 descriptors when - * WTHRESH=4, and since we want 64 bytes at a time written back, set - * it to 5 + * WTHRESH=4, so a setting of 5 gives the most efficient bus + * utilization but to avoid possible Tx stalls, set it to 1 */ #define E1000_TXDCTL_DMA_BURST_ENABLE \ (E1000_TXDCTL_GRAN | /* set descriptor granularity */ \ E1000_TXDCTL_COUNT_DESC | \ - (5 << 16) | /* wthresh must be +1 more than desired */\ + (1 << 16) | /* wthresh must be +1 more than desired */\ (1 << 8) | /* hthresh */ \ 0x1f) /* pthresh */ diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index de57a2ba6bd..f444eb0b76d 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -2831,7 +2831,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) * set up some performance related parameters to encourage the * hardware to use the bus more efficiently in bursts, depends * on the tx_int_delay to be enabled, - * wthresh = 5 ==> burst write a cacheline (64 bytes) at a time + * wthresh = 1 ==> burst write is disabled to avoid Tx stalls * hthresh = 1 ==> prefetch when one or more available * pthresh = 0x1f ==> prefetch if internal cache 31 or less * BEWARE: this seems to work but should be considered first if diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index c911d883c27..f8064df10cc 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/pci.h> +#include <linux/pci-aspm.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/ethtool.h> @@ -2973,6 +2974,9 @@ jme_init_one(struct pci_dev *pdev, /* * set up PCI device basics */ + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); + rc = pci_enable_device(pdev); if (rc) { pr_err("Cannot enable PCI device\n"); diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c index 434d5af8e6f..c81e278629f 100644 --- a/drivers/net/usb/cdc_eem.c +++ b/drivers/net/usb/cdc_eem.c @@ -244,8 +244,12 @@ static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) * - suspend: peripheral ready to suspend * - response: suggest N millisec polling * - response complete: suggest N sec polling + * + * Suspend is reported and maybe heeded. */ case 2: /* Suspend hint */ + usbnet_device_suggests_idle(dev); + continue; case 3: /* Response hint */ case 4: /* Response complete hint */ continue; diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index c75e11e1b38..afb117c16d2 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -424,7 +424,7 @@ static int kaweth_download_firmware(struct kaweth_device *kaweth, netdev_dbg(kaweth->net, "Downloading firmware at %p to kaweth device at %p\n", - fw->data, kaweth); + kaweth->firmware_buf, kaweth); netdev_dbg(kaweth->net, "Firmware length: %d\n", data_len); return kaweth_control(kaweth, diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c index 03c2d8d653d..cc7e72010ac 100644 --- a/drivers/net/usb/mcs7830.c +++ b/drivers/net/usb/mcs7830.c @@ -117,6 +117,7 @@ enum { struct mcs7830_data { u8 multi_filter[8]; u8 config; + u8 link_counter; }; static const char driver_name[] = "MOSCHIP usb-ethernet driver"; @@ -632,20 +633,31 @@ static int mcs7830_rx_fixup(struct usbnet *dev, struct sk_buff *skb) static void mcs7830_status(struct usbnet *dev, struct urb *urb) { u8 *buf = urb->transfer_buffer; - bool link; + bool link, link_changed; + struct mcs7830_data *data = mcs7830_get_data(dev); if (urb->actual_length < 16) return; link = !(buf[1] & 0x20); - if (netif_carrier_ok(dev->net) != link) { - if (link) { - netif_carrier_on(dev->net); - usbnet_defer_kevent(dev, EVENT_LINK_RESET); - } else - netif_carrier_off(dev->net); - netdev_dbg(dev->net, "Link Status is: %d\n", link); - } + link_changed = netif_carrier_ok(dev->net) != link; + if (link_changed) { + data->link_counter++; + /* + track link state 20 times to guard against erroneous + link state changes reported sometimes by the chip + */ + if (data->link_counter > 20) { + data->link_counter = 0; + if (link) { + netif_carrier_on(dev->net); + usbnet_defer_kevent(dev, EVENT_LINK_RESET); + } else + netif_carrier_off(dev->net); + netdev_dbg(dev->net, "Link Status is: %d\n", link); + } + } else + data->link_counter = 0; } static const struct driver_info moschip_info = { diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index fc9f578a1e2..f9819d10b1f 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1588,10 +1588,27 @@ int usbnet_resume (struct usb_interface *intf) tasklet_schedule (&dev->bh); } } + + if (test_and_clear_bit(EVENT_DEVICE_REPORT_IDLE, &dev->flags)) + usb_autopm_get_interface_no_resume(intf); + return 0; } EXPORT_SYMBOL_GPL(usbnet_resume); +/* + * Either a subdriver implements manage_power, then it is assumed to always + * be ready to be suspended or it reports the readiness to be suspended + * explicitly + */ +void usbnet_device_suggests_idle(struct usbnet *dev) +{ + if (!test_and_set_bit(EVENT_DEVICE_REPORT_IDLE, &dev->flags)) { + dev->intf->needs_remote_wakeup = 1; + usb_autopm_put_interface_async(dev->intf); + } +} +EXPORT_SYMBOL(usbnet_device_suggests_idle); /*-------------------------------------------------------------------------*/ diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 8be9bf07bd3..607976c0016 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -106,6 +106,8 @@ struct vxlan_dev { __be32 gaddr; /* multicast group */ __be32 saddr; /* source address */ unsigned int link; /* link to multicast over */ + __u16 port_min; /* source port range */ + __u16 port_max; __u8 tos; /* TOS override */ __u8 ttl; bool learn; @@ -228,9 +230,9 @@ static u32 eth_hash(const unsigned char *addr) /* only want 6 bytes */ #ifdef __BIG_ENDIAN - value <<= 16; -#else value >>= 16; +#else + value <<= 16; #endif return hash_64(value, FDB_HASH_BITS); } @@ -535,7 +537,6 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) } __skb_pull(skb, sizeof(struct vxlanhdr)); - skb_postpull_rcsum(skb, eth_hdr(skb), sizeof(struct vxlanhdr)); /* Is this VNI defined? */ vni = ntohl(vxh->vx_vni) >> 8; @@ -554,7 +555,6 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) /* Re-examine inner Ethernet packet */ oip = ip_hdr(skb); skb->protocol = eth_type_trans(skb, vxlan->dev); - skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); /* Ignore packet loops (and multicast echo) */ if (compare_ether_addr(eth_hdr(skb)->h_source, @@ -566,6 +566,7 @@ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) __skb_tunnel_rx(skb, vxlan->dev); skb_reset_network_header(skb); + skb->ip_summed = CHECKSUM_NONE; err = IP_ECN_decapsulate(oip, skb); if (unlikely(err)) { @@ -621,46 +622,89 @@ static inline u8 vxlan_ecn_encap(u8 tos, return INET_ECN_encapsulate(tos, inner); } +static __be32 vxlan_find_dst(struct vxlan_dev *vxlan, struct sk_buff *skb) +{ + const struct ethhdr *eth = (struct ethhdr *) skb->data; + const struct vxlan_fdb *f; + + if (is_multicast_ether_addr(eth->h_dest)) + return vxlan->gaddr; + + f = vxlan_find_mac(vxlan, eth->h_dest); + if (f) + return f->remote_ip; + else + return vxlan->gaddr; + +} + +static void vxlan_sock_free(struct sk_buff *skb) +{ + sock_put(skb->sk); +} + +/* On transmit, associate with the tunnel socket */ +static void vxlan_set_owner(struct net_device *dev, struct sk_buff *skb) +{ + struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id); + struct sock *sk = vn->sock->sk; + + skb_orphan(skb); + sock_hold(sk); + skb->sk = sk; + skb->destructor = vxlan_sock_free; +} + +/* Compute source port for outgoing packet + * first choice to use L4 flow hash since it will spread + * better and maybe available from hardware + * secondary choice is to use jhash on the Ethernet header + */ +static u16 vxlan_src_port(const struct vxlan_dev *vxlan, struct sk_buff *skb) +{ + unsigned int range = (vxlan->port_max - vxlan->port_min) + 1; + u32 hash; + + hash = skb_get_rxhash(skb); + if (!hash) + hash = jhash(skb->data, 2 * ETH_ALEN, + (__force u32) skb->protocol); + + return (((u64) hash * range) >> 32) + vxlan->port_min; +} + /* Transmit local packets over Vxlan * * Outer IP header inherits ECN and DF from inner header. * Outer UDP destination is the VXLAN assigned port. - * source port is based on hash of flow if available - * otherwise use a random value + * source port is based on hash of flow */ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); struct rtable *rt; - const struct ethhdr *eth; const struct iphdr *old_iph; struct iphdr *iph; struct vxlanhdr *vxh; struct udphdr *uh; struct flowi4 fl4; - struct vxlan_fdb *f; unsigned int pkt_len = skb->len; - u32 hash; __be32 dst; + __u16 src_port; __be16 df = 0; __u8 tos, ttl; int err; + dst = vxlan_find_dst(vxlan, skb); + if (!dst) + goto drop; + /* Need space for new headers (invalidates iph ptr) */ if (skb_cow_head(skb, VXLAN_HEADROOM)) goto drop; - eth = (void *)skb->data; old_iph = ip_hdr(skb); - if (!is_multicast_ether_addr(eth->h_dest) && - (f = vxlan_find_mac(vxlan, eth->h_dest))) - dst = f->remote_ip; - else if (vxlan->gaddr) { - dst = vxlan->gaddr; - } else - goto drop; - ttl = vxlan->ttl; if (!ttl && IN_MULTICAST(ntohl(dst))) ttl = 1; @@ -669,11 +713,15 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) if (tos == 1) tos = vxlan_get_dsfield(old_iph, skb); - hash = skb_get_rxhash(skb); + src_port = vxlan_src_port(vxlan, skb); + + memset(&fl4, 0, sizeof(fl4)); + fl4.flowi4_oif = vxlan->link; + fl4.flowi4_tos = RT_TOS(tos); + fl4.daddr = dst; + fl4.saddr = vxlan->saddr; - rt = ip_route_output_gre(dev_net(dev), &fl4, dst, - vxlan->saddr, vxlan->vni, - RT_TOS(tos), vxlan->link); + rt = ip_route_output_key(dev_net(dev), &fl4); if (IS_ERR(rt)) { netdev_dbg(dev, "no route to %pI4\n", &dst); dev->stats.tx_carrier_errors++; @@ -702,7 +750,7 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) uh = udp_hdr(skb); uh->dest = htons(vxlan_port); - uh->source = hash ? :random32(); + uh->source = htons(src_port); uh->len = htons(skb->len); uh->check = 0; @@ -715,10 +763,12 @@ static netdev_tx_t vxlan_xmit(struct sk_buff *skb, struct net_device *dev) iph->frag_off = df; iph->protocol = IPPROTO_UDP; iph->tos = vxlan_ecn_encap(tos, old_iph, skb); - iph->daddr = fl4.daddr; + iph->daddr = dst; iph->saddr = fl4.saddr; iph->ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); + vxlan_set_owner(dev, skb); + /* See __IPTUNNEL_XMIT */ skb->ip_summed = CHECKSUM_NONE; ip_select_ident(iph, &rt->dst, NULL); @@ -928,9 +978,11 @@ static void vxlan_setup(struct net_device *dev) { struct vxlan_dev *vxlan = netdev_priv(dev); unsigned h; + int low, high; eth_hw_addr_random(dev); ether_setup(dev); + dev->hard_header_len = ETH_HLEN + VXLAN_HEADROOM; dev->netdev_ops = &vxlan_netdev_ops; dev->destructor = vxlan_free; @@ -947,6 +999,10 @@ static void vxlan_setup(struct net_device *dev) vxlan->age_timer.function = vxlan_cleanup; vxlan->age_timer.data = (unsigned long) vxlan; + inet_get_local_port_range(&low, &high); + vxlan->port_min = low; + vxlan->port_max = high; + vxlan->dev = dev; for (h = 0; h < FDB_HASH_SIZE; ++h) @@ -963,6 +1019,7 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_LEARNING] = { .type = NLA_U8 }, [IFLA_VXLAN_AGEING] = { .type = NLA_U32 }, [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 }, + [IFLA_VXLAN_PORT_RANGE] = { .len = sizeof(struct ifla_vxlan_port_range) }, }; static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) @@ -995,6 +1052,18 @@ static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) return -EADDRNOTAVAIL; } } + + if (data[IFLA_VXLAN_PORT_RANGE]) { + const struct ifla_vxlan_port_range *p + = nla_data(data[IFLA_VXLAN_PORT_RANGE]); + + if (ntohs(p->high) < ntohs(p->low)) { + pr_debug("port range %u .. %u not valid\n", + ntohs(p->low), ntohs(p->high)); + return -EINVAL; + } + } + return 0; } @@ -1021,14 +1090,18 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, if (data[IFLA_VXLAN_LOCAL]) vxlan->saddr = nla_get_be32(data[IFLA_VXLAN_LOCAL]); - if (data[IFLA_VXLAN_LINK]) { - vxlan->link = nla_get_u32(data[IFLA_VXLAN_LINK]); + if (data[IFLA_VXLAN_LINK] && + (vxlan->link = nla_get_u32(data[IFLA_VXLAN_LINK]))) { + struct net_device *lowerdev + = __dev_get_by_index(net, vxlan->link); - if (!tb[IFLA_MTU]) { - struct net_device *lowerdev; - lowerdev = __dev_get_by_index(net, vxlan->link); - dev->mtu = lowerdev->mtu - VXLAN_HEADROOM; + if (!lowerdev) { + pr_info("ifindex %d does not exist\n", vxlan->link); + return -ENODEV; } + + if (!tb[IFLA_MTU]) + dev->mtu = lowerdev->mtu - VXLAN_HEADROOM; } if (data[IFLA_VXLAN_TOS]) @@ -1045,6 +1118,13 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, if (data[IFLA_VXLAN_LIMIT]) vxlan->addrmax = nla_get_u32(data[IFLA_VXLAN_LIMIT]); + if (data[IFLA_VXLAN_PORT_RANGE]) { + const struct ifla_vxlan_port_range *p + = nla_data(data[IFLA_VXLAN_PORT_RANGE]); + vxlan->port_min = ntohs(p->low); + vxlan->port_max = ntohs(p->high); + } + err = register_netdevice(dev); if (!err) hlist_add_head_rcu(&vxlan->hlist, vni_head(net, vxlan->vni)); @@ -1073,12 +1153,17 @@ static size_t vxlan_get_size(const struct net_device *dev) nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_LEARNING */ nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_AGEING */ nla_total_size(sizeof(__u32)) + /* IFLA_VXLAN_LIMIT */ + nla_total_size(sizeof(struct ifla_vxlan_port_range)) + 0; } static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) { const struct vxlan_dev *vxlan = netdev_priv(dev); + struct ifla_vxlan_port_range ports = { + .low = htons(vxlan->port_min), + .high = htons(vxlan->port_max), + }; if (nla_put_u32(skb, IFLA_VXLAN_ID, vxlan->vni)) goto nla_put_failure; @@ -1099,6 +1184,9 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u32(skb, IFLA_VXLAN_LIMIT, vxlan->addrmax)) goto nla_put_failure; + if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports)) + goto nla_put_failure; + return 0; nla_put_failure: diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 9fd6d9a9942..9f31cfa56cc 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1804,7 +1804,7 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { int ret; struct ath5k_hw *ah = hw->priv; - struct ath5k_vif *avf = (void *)vif->drv_priv; + struct ath5k_vif *avf; struct sk_buff *skb; if (WARN_ON(!vif)) { @@ -1819,6 +1819,7 @@ ath5k_beacon_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif) goto out; } + avf = (void *)vif->drv_priv; ath5k_txbuf_free_skb(ah, avf->bbuf); avf->bbuf->skb = skb; ret = ath5k_beacon_setup(ah, avf->bbuf); diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 76f07d8c272..1b48414dca9 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -120,7 +120,7 @@ static void ath9k_tx_cabq(struct ieee80211_hw *hw, struct sk_buff *skb) if (ath_tx_start(hw, skb, &txctl) != 0) { ath_dbg(common, XMIT, "CABQ TX failed\n"); - dev_kfree_skb_any(skb); + ieee80211_free_txskb(hw, skb); } } diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index f9a6ec5cf47..8e1559aba49 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1450,9 +1450,14 @@ static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type) REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_EN | AR_RTC_FORCE_WAKE_ON_INT); + if (!ah->reset_power_on) + type = ATH9K_RESET_POWER_ON; + switch (type) { case ATH9K_RESET_POWER_ON: ret = ath9k_hw_set_reset_power_on(ah); + if (!ret) + ah->reset_power_on = true; break; case ATH9K_RESET_WARM: case ATH9K_RESET_COLD: diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 566a4ce4f15..dbc1b7a4cbf 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -741,6 +741,7 @@ struct ath_hw { u32 rfkill_polarity; u32 ah_flags; + bool reset_power_on; bool htc_reset_init; enum nl80211_iftype opmode; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 31ab82e3ba8..dd45edfa6ba 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -639,8 +639,7 @@ static int ath9k_start(struct ieee80211_hw *hw) ath_err(common, "Unable to reset hardware; reset status %d (freq %u MHz)\n", r, curchan->center_freq); - spin_unlock_bh(&sc->sc_pcu_lock); - goto mutex_unlock; + ah->reset_power_on = false; } /* Setup our intr mask. */ @@ -665,11 +664,8 @@ static int ath9k_start(struct ieee80211_hw *hw) clear_bit(SC_OP_INVALID, &sc->sc_flags); sc->sc_ah->is_monitoring = false; - if (!ath_complete_reset(sc, false)) { - r = -EIO; - spin_unlock_bh(&sc->sc_pcu_lock); - goto mutex_unlock; - } + if (!ath_complete_reset(sc, false)) + ah->reset_power_on = false; if (ah->led_pin >= 0) { ath9k_hw_cfg_output(ah, ah->led_pin, @@ -688,12 +684,11 @@ static int ath9k_start(struct ieee80211_hw *hw) if (ah->caps.pcie_lcr_extsync_en && common->bus_ops->extn_synch_en) common->bus_ops->extn_synch_en(common); -mutex_unlock: mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); - return r; + return 0; } static void ath9k_tx(struct ieee80211_hw *hw, @@ -770,7 +765,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, return; exit: - dev_kfree_skb_any(skb); + ieee80211_free_txskb(hw, skb); } static void ath9k_stop(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 0e630a99b68..f088f4bf9a2 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -324,6 +324,10 @@ static int ath_pci_suspend(struct device *device) static int ath_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); + struct ieee80211_hw *hw = pci_get_drvdata(pdev); + struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); u32 val; /* @@ -335,6 +339,9 @@ static int ath_pci_resume(struct device *device) if ((val & 0x0000ff00) != 0) pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + ath_pci_aspm_init(common); + ah->reset_power_on = false; + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 36618e3a5e6..378bd70256b 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -66,8 +66,7 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, - struct sk_buff *skb, - bool dequeue); + struct sk_buff *skb); enum { MCS_HT20, @@ -176,7 +175,15 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid) fi = get_frame_info(skb); bf = fi->bf; - if (bf && fi->retries) { + if (!bf) { + bf = ath_tx_setup_buffer(sc, txq, tid, skb); + if (!bf) { + ieee80211_free_txskb(sc->hw, skb); + continue; + } + } + + if (fi->retries) { list_add_tail(&bf->list, &bf_head); ath_tx_update_baw(sc, tid, bf->bf_state.seqno); ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0); @@ -785,10 +792,13 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, fi = get_frame_info(skb); bf = fi->bf; if (!fi->bf) - bf = ath_tx_setup_buffer(sc, txq, tid, skb, true); + bf = ath_tx_setup_buffer(sc, txq, tid, skb); - if (!bf) + if (!bf) { + __skb_unlink(skb, &tid->buf_q); + ieee80211_free_txskb(sc->hw, skb); continue; + } bf->bf_state.bf_type = BUF_AMPDU | BUF_AGGR; seqno = bf->bf_state.seqno; @@ -1731,9 +1741,11 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, return; } - bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb, false); - if (!bf) + bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb); + if (!bf) { + ieee80211_free_txskb(sc->hw, skb); return; + } bf->bf_state.bf_type = BUF_AMPDU; INIT_LIST_HEAD(&bf_head); @@ -1757,11 +1769,6 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf; bf = fi->bf; - if (!bf) - bf = ath_tx_setup_buffer(sc, txq, tid, skb, false); - - if (!bf) - return; INIT_LIST_HEAD(&bf_head); list_add_tail(&bf->list, &bf_head); @@ -1839,8 +1846,7 @@ u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate) static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, - struct sk_buff *skb, - bool dequeue) + struct sk_buff *skb) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_frame_info *fi = get_frame_info(skb); @@ -1852,7 +1858,7 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, bf = ath_tx_get_buffer(sc); if (!bf) { ath_dbg(common, XMIT, "TX buffers are full\n"); - goto error; + return NULL; } ATH_TXBUF_RESET(bf); @@ -1881,18 +1887,12 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, ath_err(ath9k_hw_common(sc->sc_ah), "dma_mapping_error() on TX\n"); ath_tx_return_buffer(sc, bf); - goto error; + return NULL; } fi->bf = bf; return bf; - -error: - if (dequeue) - __skb_unlink(skb, &tid->buf_q); - dev_kfree_skb_any(skb); - return NULL; } /* FIXME: tx power */ @@ -1921,9 +1921,14 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct sk_buff *skb, */ ath_tx_send_ampdu(sc, tid, skb, txctl); } else { - bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb, false); - if (!bf) + bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb); + if (!bf) { + if (txctl->paprd) + dev_kfree_skb_any(skb); + else + ieee80211_free_txskb(sc->hw, skb); return; + } bf->bf_state.bfs_paprd = txctl->paprd; diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index 2aa4a59c72c..2df17f1e49e 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -303,6 +303,7 @@ struct ar9170 { unsigned long queue_stop_timeout[__AR9170_NUM_TXQ]; unsigned long max_queue_stop_timeout[__AR9170_NUM_TXQ]; bool needs_full_reset; + bool force_usb_reset; atomic_t pending_restarts; /* interface mode settings */ diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index 67997b39aba..25a1e2f4f73 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -465,27 +465,26 @@ static void carl9170_restart_work(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, restart_work); - int err; + int err = -EIO; ar->usedkeys = 0; ar->filter_state = 0; carl9170_cancel_worker(ar); mutex_lock(&ar->mutex); - err = carl9170_usb_restart(ar); - if (net_ratelimit()) { - if (err) { - dev_err(&ar->udev->dev, "Failed to restart device " - " (%d).\n", err); - } else { - dev_info(&ar->udev->dev, "device restarted " - "successfully.\n"); + if (!ar->force_usb_reset) { + err = carl9170_usb_restart(ar); + if (net_ratelimit()) { + if (err) + dev_err(&ar->udev->dev, "Failed to restart device (%d).\n", err); + else + dev_info(&ar->udev->dev, "device restarted successfully.\n"); } } - carl9170_zap_queues(ar); mutex_unlock(&ar->mutex); - if (!err) { + + if (!err && !ar->force_usb_reset) { ar->restart_counter++; atomic_set(&ar->pending_restarts, 0); @@ -526,10 +525,10 @@ void carl9170_restart(struct ar9170 *ar, const enum carl9170_restart_reasons r) if (!ar->registered) return; - if (IS_ACCEPTING_CMD(ar) && !ar->needs_full_reset) - ieee80211_queue_work(ar->hw, &ar->restart_work); - else - carl9170_usb_reset(ar); + if (!IS_ACCEPTING_CMD(ar) || ar->needs_full_reset) + ar->force_usb_reset = true; + + ieee80211_queue_work(ar->hw, &ar->restart_work); /* * At this point, the device instance might have vanished/disabled. diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 2691620393e..0679458a1ba 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1596,8 +1596,9 @@ done: } } - if (mwifiex_bss_start(priv, bss, &req_ssid)) - return -EFAULT; + ret = mwifiex_bss_start(priv, bss, &req_ssid); + if (ret) + return ret; if (mode == NL80211_IFTYPE_ADHOC) { /* Inform the BSS information to kernel, otherwise @@ -1652,9 +1653,19 @@ done: "info: association to bssid %pM failed\n", priv->cfg_bssid); memset(priv->cfg_bssid, 0, ETH_ALEN); + + if (ret > 0) + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, NULL, 0, ret, + GFP_KERNEL); + else + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); } - return ret; + return 0; } /* @@ -1802,7 +1813,7 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, { struct net_device *dev = request->wdev->netdev; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int i, offset; + int i, offset, ret; struct ieee80211_channel *chan; struct ieee_types_header *ie; @@ -1855,8 +1866,12 @@ mwifiex_cfg80211_scan(struct wiphy *wiphy, priv->user_scan_cfg->chan_list[i].scan_time = 0; } - if (mwifiex_scan_networks(priv, priv->user_scan_cfg)) - return -EFAULT; + + ret = mwifiex_scan_networks(priv, priv->user_scan_cfg); + if (ret) { + dev_err(priv->adapter->dev, "scan failed: %d\n", ret); + return ret; + } if (request->ie && request->ie_len) { for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 82e63cee1e9..7b0858af8f5 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -1180,16 +1180,18 @@ int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result; struct mwifiex_bssdescriptor *bss_desc; + u16 reason_code; adhoc_result = &resp->params.adhoc_result; bss_desc = priv->attempted_bss_desc; /* Join result code 0 --> SUCCESS */ - if (le16_to_cpu(resp->result)) { + reason_code = le16_to_cpu(resp->result); + if (reason_code) { dev_err(priv->adapter->dev, "ADHOC_RESP: failed\n"); if (priv->media_connected) - mwifiex_reset_connect_state(priv); + mwifiex_reset_connect_state(priv, reason_code); memset(&priv->curr_bss_params.bss_descriptor, 0x00, sizeof(struct mwifiex_bssdescriptor)); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index bfb3fa69805..c2d0ab146af 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -847,7 +847,7 @@ int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc); int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, struct host_cmd_ds_command *resp); -void mwifiex_reset_connect_state(struct mwifiex_private *priv); +void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason); u8 mwifiex_band_to_radio_type(u8 band); int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac); int mwifiex_adhoc_start(struct mwifiex_private *priv, diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index e36a75988f8..00b658d3b6e 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1296,7 +1296,7 @@ mwifiex_radio_type_to_band(u8 radio_type) int mwifiex_scan_networks(struct mwifiex_private *priv, const struct mwifiex_user_scan_cfg *user_scan_in) { - int ret = 0; + int ret; struct mwifiex_adapter *adapter = priv->adapter; struct cmd_ctrl_node *cmd_node; union mwifiex_scan_cmd_config_tlv *scan_cfg_out; @@ -1309,25 +1309,26 @@ int mwifiex_scan_networks(struct mwifiex_private *priv, unsigned long flags; if (adapter->scan_processing) { - dev_dbg(adapter->dev, "cmd: Scan already in process...\n"); - return ret; + dev_err(adapter->dev, "cmd: Scan already in process...\n"); + return -EBUSY; } - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = true; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - if (priv->scan_block) { - dev_dbg(adapter->dev, + dev_err(adapter->dev, "cmd: Scan is blocked during association...\n"); - return ret; + return -EBUSY; } + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = true; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv), GFP_KERNEL); if (!scan_cfg_out) { dev_err(adapter->dev, "failed to alloc scan_cfg_out\n"); - return -ENOMEM; + ret = -ENOMEM; + goto done; } buf_size = sizeof(struct mwifiex_chan_scan_param_set) * @@ -1336,7 +1337,8 @@ int mwifiex_scan_networks(struct mwifiex_private *priv, if (!scan_chan_list) { dev_err(adapter->dev, "failed to alloc scan_chan_list\n"); kfree(scan_cfg_out); - return -ENOMEM; + ret = -ENOMEM; + goto done; } mwifiex_config_scan(priv, user_scan_in, &scan_cfg_out->config, @@ -1364,14 +1366,16 @@ int mwifiex_scan_networks(struct mwifiex_private *priv, spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); } - } else { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = true; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); } kfree(scan_cfg_out); kfree(scan_chan_list); +done: + if (ret) { + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + } return ret; } @@ -1430,8 +1434,8 @@ int mwifiex_check_network_compatibility(struct mwifiex_private *priv, ret = mwifiex_is_network_compatible(priv, bss_desc, priv->bss_mode); if (ret) - dev_err(priv->adapter->dev, "cannot find ssid " - "%s\n", bss_desc->ssid.ssid); + dev_err(priv->adapter->dev, + "Incompatible network settings\n"); break; default: ret = 0; diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index e380171c4c5..09e6a267f56 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -545,7 +545,7 @@ static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv, if (!memcmp(resp->params.deauth.mac_addr, &priv->curr_bss_params.bss_descriptor.mac_address, sizeof(resp->params.deauth.mac_addr))) - mwifiex_reset_connect_state(priv); + mwifiex_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING); return 0; } @@ -558,7 +558,7 @@ static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv, static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv, struct host_cmd_ds_command *resp) { - mwifiex_reset_connect_state(priv); + mwifiex_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING); return 0; } diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index aafde30e714..8132119e1a2 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -41,7 +41,7 @@ * - Sends a disconnect event to upper layers/applications. */ void -mwifiex_reset_connect_state(struct mwifiex_private *priv) +mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) { struct mwifiex_adapter *adapter = priv->adapter; @@ -117,10 +117,10 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv) priv->media_connected = false; dev_dbg(adapter->dev, "info: successfully disconnected from %pM: reason code %d\n", - priv->cfg_bssid, WLAN_REASON_DEAUTH_LEAVING); + priv->cfg_bssid, reason_code); if (priv->bss_mode == NL80211_IFTYPE_STATION) { - cfg80211_disconnected(priv->netdev, WLAN_REASON_DEAUTH_LEAVING, - NULL, 0, GFP_KERNEL); + cfg80211_disconnected(priv->netdev, reason_code, NULL, 0, + GFP_KERNEL); } memset(priv->cfg_bssid, 0, ETH_ALEN); @@ -186,7 +186,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) struct mwifiex_adapter *adapter = priv->adapter; int ret = 0; u32 eventcause = adapter->event_cause; - u16 ctrl; + u16 ctrl, reason_code; switch (eventcause) { case EVENT_DUMMY_HOST_WAKEUP_SIGNAL: @@ -204,22 +204,31 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) case EVENT_DEAUTHENTICATED: dev_dbg(adapter->dev, "event: Deauthenticated\n"); adapter->dbg.num_event_deauth++; - if (priv->media_connected) - mwifiex_reset_connect_state(priv); + if (priv->media_connected) { + reason_code = + le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_reset_connect_state(priv, reason_code); + } break; case EVENT_DISASSOCIATED: dev_dbg(adapter->dev, "event: Disassociated\n"); adapter->dbg.num_event_disassoc++; - if (priv->media_connected) - mwifiex_reset_connect_state(priv); + if (priv->media_connected) { + reason_code = + le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_reset_connect_state(priv, reason_code); + } break; case EVENT_LINK_LOST: dev_dbg(adapter->dev, "event: Link lost\n"); adapter->dbg.num_event_link_lost++; - if (priv->media_connected) - mwifiex_reset_connect_state(priv); + if (priv->media_connected) { + reason_code = + le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_reset_connect_state(priv, reason_code); + } break; case EVENT_PS_SLEEP: diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 540c94f8505..01dc8891070 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -2252,9 +2252,9 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, */ if (rt2x00_rt(rt2x00dev, RT3352)) { rt2800_bbp_write(rt2x00dev, 27, 0x0); - rt2800_bbp_write(rt2x00dev, 62, 0x26 + rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain); rt2800_bbp_write(rt2x00dev, 27, 0x20); - rt2800_bbp_write(rt2x00dev, 62, 0x26 + rt2x00dev->lna_gain); + rt2800_bbp_write(rt2x00dev, 66, 0x26 + rt2x00dev->lna_gain); } else { rt2800_bbp_write(rt2x00dev, 62, 0x37 - rt2x00dev->lna_gain); rt2800_bbp_write(rt2x00dev, 63, 0x37 - rt2x00dev->lna_gain); diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 4ebfcf3d8a3..f2d6b78d901 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -335,21 +335,35 @@ unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb) for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { unsigned long size = skb_frag_size(&skb_shinfo(skb)->frags[i]); + unsigned long offset = skb_shinfo(skb)->frags[i].page_offset; unsigned long bytes; + + offset &= ~PAGE_MASK; + while (size > 0) { + BUG_ON(offset >= PAGE_SIZE); BUG_ON(copy_off > MAX_BUFFER_OFFSET); - if (start_new_rx_buffer(copy_off, size, 0)) { + bytes = PAGE_SIZE - offset; + + if (bytes > size) + bytes = size; + + if (start_new_rx_buffer(copy_off, bytes, 0)) { count++; copy_off = 0; } - bytes = size; if (copy_off + bytes > MAX_BUFFER_OFFSET) bytes = MAX_BUFFER_OFFSET - copy_off; copy_off += bytes; + + offset += bytes; size -= bytes; + + if (offset == PAGE_SIZE) + offset = 0; } } return count; @@ -403,14 +417,24 @@ static void netbk_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb, unsigned long bytes; /* Data must not cross a page boundary. */ - BUG_ON(size + offset > PAGE_SIZE); + BUG_ON(size + offset > PAGE_SIZE<<compound_order(page)); meta = npo->meta + npo->meta_prod - 1; + /* Skip unused frames from start of page */ + page += offset >> PAGE_SHIFT; + offset &= ~PAGE_MASK; + while (size > 0) { + BUG_ON(offset >= PAGE_SIZE); BUG_ON(npo->copy_off > MAX_BUFFER_OFFSET); - if (start_new_rx_buffer(npo->copy_off, size, *head)) { + bytes = PAGE_SIZE - offset; + + if (bytes > size) + bytes = size; + + if (start_new_rx_buffer(npo->copy_off, bytes, *head)) { /* * Netfront requires there to be some data in the head * buffer. @@ -420,7 +444,6 @@ static void netbk_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb, meta = get_next_rx_buffer(vif, npo); } - bytes = size; if (npo->copy_off + bytes > MAX_BUFFER_OFFSET) bytes = MAX_BUFFER_OFFSET - npo->copy_off; @@ -453,6 +476,13 @@ static void netbk_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb, offset += bytes; size -= bytes; + /* Next frame */ + if (offset == PAGE_SIZE && size) { + BUG_ON(!PageCompound(page)); + page++; + offset = 0; + } + /* Leave a gap for the GSO descriptor. */ if (*head && skb_shinfo(skb)->gso_size && !vif->gso_prefix) vif->rx.req_cons++; diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 39abb150bdd..84c56881ba8 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -329,7 +329,8 @@ static int acerhdf_bind(struct thermal_zone_device *thermal, if (cdev != cl_dev) return 0; - if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) { + if (thermal_zone_bind_cooling_device(thermal, 0, cdev, + THERMAL_NO_LIMIT, THERMAL_NO_LIMIT)) { pr_err("error binding cooling dev\n"); return -EINVAL; } @@ -661,7 +662,7 @@ static int acerhdf_register_thermal(void) return -EINVAL; thz_dev = thermal_zone_device_register("acerhdf", 1, 0, NULL, - &acerhdf_dev_ops, 0, 0, 0, + &acerhdf_dev_ops, 0, (kernelmode) ? interval*1000 : 0); if (IS_ERR(thz_dev)) return -EINVAL; diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 3a27113deda..c8097616dd6 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -502,7 +502,7 @@ static int mid_thermal_probe(struct platform_device *pdev) goto err; } pinfo->tzd[i] = thermal_zone_device_register(name[i], - 0, 0, td_info, &tzd_ops, 0, 0, 0, 0); + 0, 0, td_info, &tzd_ops, 0, 0); if (IS_ERR(pinfo->tzd[i])) { kfree(td_info); ret = PTR_ERR(pinfo->tzd[i]); diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 08cc8a3c15a..2436f135001 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -201,7 +201,7 @@ static int psy_register_thermal(struct power_supply *psy) for (i = 0; i < psy->num_properties; i++) { if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) { psy->tzd = thermal_zone_device_register(psy->name, 0, 0, - psy, &psy_tzd_ops, 0, 0, 0, 0); + psy, &psy_tzd_ops, 0, 0); if (IS_ERR(psy->tzd)) return PTR_ERR(psy->tzd); break; diff --git a/drivers/scsi/bfa/bfa_core.c b/drivers/scsi/bfa/bfa_core.c index b7c326f7a6d..342d7d9c099 100644 --- a/drivers/scsi/bfa/bfa_core.c +++ b/drivers/scsi/bfa/bfa_core.c @@ -165,6 +165,16 @@ bfa_com_phy_attach(struct bfa_s *bfa, bfa_boolean_t mincfg) bfa_phy_memclaim(phy, phy_dma->kva_curp, phy_dma->dma_curp, mincfg); } +static void +bfa_com_fru_attach(struct bfa_s *bfa, bfa_boolean_t mincfg) +{ + struct bfa_fru_s *fru = BFA_FRU(bfa); + struct bfa_mem_dma_s *fru_dma = BFA_MEM_FRU_DMA(bfa); + + bfa_fru_attach(fru, &bfa->ioc, bfa, bfa->trcmod, mincfg); + bfa_fru_memclaim(fru, fru_dma->kva_curp, fru_dma->dma_curp, mincfg); +} + /* * BFA IOC FC related definitions */ @@ -274,6 +284,15 @@ bfa_iocfc_sm_initing(struct bfa_iocfc_s *iocfc, enum iocfc_event event) case IOCFC_E_IOC_ENABLED: bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_read); break; + + case IOCFC_E_DISABLE: + bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling); + break; + + case IOCFC_E_STOP: + bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping); + break; + case IOCFC_E_IOC_FAILED: bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed); break; @@ -298,6 +317,15 @@ bfa_iocfc_sm_dconf_read(struct bfa_iocfc_s *iocfc, enum iocfc_event event) case IOCFC_E_DCONF_DONE: bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_cfg_wait); break; + + case IOCFC_E_DISABLE: + bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling); + break; + + case IOCFC_E_STOP: + bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping); + break; + case IOCFC_E_IOC_FAILED: bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed); break; @@ -322,6 +350,15 @@ bfa_iocfc_sm_init_cfg_wait(struct bfa_iocfc_s *iocfc, enum iocfc_event event) case IOCFC_E_CFG_DONE: bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_cfg_done); break; + + case IOCFC_E_DISABLE: + bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling); + break; + + case IOCFC_E_STOP: + bfa_fsm_set_state(iocfc, bfa_iocfc_sm_stopping); + break; + case IOCFC_E_IOC_FAILED: bfa_fsm_set_state(iocfc, bfa_iocfc_sm_init_failed); break; @@ -433,6 +470,12 @@ bfa_iocfc_sm_stopping(struct bfa_iocfc_s *iocfc, enum iocfc_event event) bfa_cb_queue(iocfc->bfa, &iocfc->bfa->iocfc.stop_hcb_qe, bfa_iocfc_stop_cb, iocfc->bfa); break; + + case IOCFC_E_IOC_ENABLED: + case IOCFC_E_DCONF_DONE: + case IOCFC_E_CFG_DONE: + break; + default: bfa_sm_fault(iocfc->bfa, event); break; @@ -454,6 +497,15 @@ bfa_iocfc_sm_enabling(struct bfa_iocfc_s *iocfc, enum iocfc_event event) case IOCFC_E_IOC_ENABLED: bfa_fsm_set_state(iocfc, bfa_iocfc_sm_cfg_wait); break; + + case IOCFC_E_DISABLE: + bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling); + break; + + case IOCFC_E_STOP: + bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write); + break; + case IOCFC_E_IOC_FAILED: bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed); @@ -493,6 +545,13 @@ bfa_iocfc_sm_cfg_wait(struct bfa_iocfc_s *iocfc, enum iocfc_event event) bfa_iocfc_enable_cb, iocfc->bfa); iocfc->bfa->iocfc.cb_reqd = BFA_FALSE; break; + case IOCFC_E_DISABLE: + bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabling); + break; + + case IOCFC_E_STOP: + bfa_fsm_set_state(iocfc, bfa_iocfc_sm_dconf_write); + break; case IOCFC_E_IOC_FAILED: bfa_fsm_set_state(iocfc, bfa_iocfc_sm_failed); if (iocfc->bfa->iocfc.cb_reqd == BFA_FALSE) @@ -524,6 +583,10 @@ bfa_iocfc_sm_disabling(struct bfa_iocfc_s *iocfc, enum iocfc_event event) case IOCFC_E_IOC_DISABLED: bfa_fsm_set_state(iocfc, bfa_iocfc_sm_disabled); break; + case IOCFC_E_IOC_ENABLED: + case IOCFC_E_DCONF_DONE: + case IOCFC_E_CFG_DONE: + break; default: bfa_sm_fault(iocfc->bfa, event); break; @@ -785,19 +848,20 @@ void bfa_isr_enable(struct bfa_s *bfa) { u32 umsk; - int pci_func = bfa_ioc_pcifn(&bfa->ioc); + int port_id = bfa_ioc_portid(&bfa->ioc); - bfa_trc(bfa, pci_func); + bfa_trc(bfa, bfa_ioc_pcifn(&bfa->ioc)); + bfa_trc(bfa, port_id); bfa_msix_ctrl_install(bfa); if (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id)) { umsk = __HFN_INT_ERR_MASK_CT2; - umsk |= pci_func == 0 ? + umsk |= port_id == 0 ? __HFN_INT_FN0_MASK_CT2 : __HFN_INT_FN1_MASK_CT2; } else { umsk = __HFN_INT_ERR_MASK; - umsk |= pci_func == 0 ? __HFN_INT_FN0_MASK : __HFN_INT_FN1_MASK; + umsk |= port_id == 0 ? __HFN_INT_FN0_MASK : __HFN_INT_FN1_MASK; } writel(umsk, bfa->iocfc.bfa_regs.intr_status); @@ -930,7 +994,8 @@ bfa_iocfc_send_cfg(void *bfa_arg) cfg_info->single_msix_vec = 1; cfg_info->endian_sig = BFI_IOC_ENDIAN_SIG; cfg_info->num_cqs = cfg->fwcfg.num_cqs; - cfg_info->num_ioim_reqs = cpu_to_be16(cfg->fwcfg.num_ioim_reqs); + cfg_info->num_ioim_reqs = cpu_to_be16(bfa_fcpim_get_throttle_cfg(bfa, + cfg->fwcfg.num_ioim_reqs)); cfg_info->num_fwtio_reqs = cpu_to_be16(cfg->fwcfg.num_fwtio_reqs); bfa_dma_be_addr_set(cfg_info->cfgrsp_addr, iocfc->cfgrsp_dma.pa); @@ -1192,10 +1257,14 @@ bfa_iocfc_qreg(struct bfa_s *bfa, struct bfi_iocfc_qreg_s *qreg) static void bfa_iocfc_res_recfg(struct bfa_s *bfa, struct bfa_iocfc_fwcfg_s *fwcfg) { + struct bfa_iocfc_s *iocfc = &bfa->iocfc; + struct bfi_iocfc_cfg_s *cfg_info = iocfc->cfginfo; + bfa_fcxp_res_recfg(bfa, fwcfg->num_fcxp_reqs); bfa_uf_res_recfg(bfa, fwcfg->num_uf_bufs); bfa_rport_res_recfg(bfa, fwcfg->num_rports); - bfa_fcp_res_recfg(bfa, fwcfg->num_ioim_reqs); + bfa_fcp_res_recfg(bfa, cpu_to_be16(cfg_info->num_ioim_reqs), + fwcfg->num_ioim_reqs); bfa_tskim_res_recfg(bfa, fwcfg->num_tskim_reqs); } @@ -1693,6 +1762,7 @@ bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo, struct bfa_mem_dma_s *flash_dma = BFA_MEM_FLASH_DMA(bfa); struct bfa_mem_dma_s *diag_dma = BFA_MEM_DIAG_DMA(bfa); struct bfa_mem_dma_s *phy_dma = BFA_MEM_PHY_DMA(bfa); + struct bfa_mem_dma_s *fru_dma = BFA_MEM_FRU_DMA(bfa); WARN_ON((cfg == NULL) || (meminfo == NULL)); @@ -1717,6 +1787,8 @@ bfa_cfg_get_meminfo(struct bfa_iocfc_cfg_s *cfg, struct bfa_meminfo_s *meminfo, bfa_mem_dma_setup(meminfo, diag_dma, bfa_diag_meminfo()); bfa_mem_dma_setup(meminfo, phy_dma, bfa_phy_meminfo(cfg->drvcfg.min_cfg)); + bfa_mem_dma_setup(meminfo, fru_dma, + bfa_fru_meminfo(cfg->drvcfg.min_cfg)); } /* @@ -1789,6 +1861,7 @@ bfa_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, bfa_com_flash_attach(bfa, cfg->drvcfg.min_cfg); bfa_com_diag_attach(bfa); bfa_com_phy_attach(bfa, cfg->drvcfg.min_cfg); + bfa_com_fru_attach(bfa, cfg->drvcfg.min_cfg); } /* diff --git a/drivers/scsi/bfa/bfa_defs.h b/drivers/scsi/bfa/bfa_defs.h index b5a1595cc0a..0efdf312b42 100644 --- a/drivers/scsi/bfa/bfa_defs.h +++ b/drivers/scsi/bfa/bfa_defs.h @@ -159,10 +159,13 @@ enum bfa_status { BFA_STATUS_BEACON_ON = 72, /* Port Beacon already on */ BFA_STATUS_ENOFSAVE = 78, /* No saved firmware trace */ BFA_STATUS_IOC_DISABLED = 82, /* IOC is already disabled */ + BFA_STATUS_ERROR_TRL_ENABLED = 87, /* TRL is enabled */ + BFA_STATUS_ERROR_QOS_ENABLED = 88, /* QoS is enabled */ BFA_STATUS_NO_SFP_DEV = 89, /* No SFP device check or replace SFP */ BFA_STATUS_MEMTEST_FAILED = 90, /* Memory test failed contact support */ BFA_STATUS_LEDTEST_OP = 109, /* LED test is operating */ BFA_STATUS_INVALID_MAC = 134, /* Invalid MAC address */ + BFA_STATUS_CMD_NOTSUPP_CNA = 146, /* Command not supported for CNA */ BFA_STATUS_PBC = 154, /* Operation not allowed for pre-boot * configuration */ BFA_STATUS_BAD_FWCFG = 156, /* Bad firmware configuration */ @@ -184,6 +187,17 @@ enum bfa_status { BFA_STATUS_FAA_ACQ_ADDR = 200, /* Acquiring addr */ BFA_STATUS_ERROR_TRUNK_ENABLED = 203, /* Trunk enabled on adapter */ BFA_STATUS_MAX_ENTRY_REACHED = 212, /* MAX entry reached */ + BFA_STATUS_TOPOLOGY_LOOP = 230, /* Topology is set to Loop */ + BFA_STATUS_LOOP_UNSUPP_MEZZ = 231, /* Loop topology is not supported + * on mezz cards */ + BFA_STATUS_INVALID_BW = 233, /* Invalid bandwidth value */ + BFA_STATUS_QOS_BW_INVALID = 234, /* Invalid QOS bandwidth + * configuration */ + BFA_STATUS_DPORT_ENABLED = 235, /* D-port mode is already enabled */ + BFA_STATUS_DPORT_DISABLED = 236, /* D-port mode is already disabled */ + BFA_STATUS_CMD_NOTSUPP_MEZZ = 239, /* Cmd not supported for MEZZ card */ + BFA_STATUS_FRU_NOT_PRESENT = 240, /* fru module not present */ + BFA_STATUS_DPORT_ERR = 245, /* D-port mode is enabled */ BFA_STATUS_MAX_VAL /* Unknown error code */ }; #define bfa_status_t enum bfa_status @@ -249,6 +263,10 @@ struct bfa_adapter_attr_s { u8 is_mezz; u8 trunk_capable; + u8 mfg_day; /* manufacturing day */ + u8 mfg_month; /* manufacturing month */ + u16 mfg_year; /* manufacturing year */ + u16 rsvd; }; /* @@ -499,6 +517,17 @@ struct bfa_ioc_aen_data_s { }; /* + * D-port states + * +*/ +enum bfa_dport_state { + BFA_DPORT_ST_DISABLED = 0, /* D-port is Disabled */ + BFA_DPORT_ST_DISABLING = 1, /* D-port is Disabling */ + BFA_DPORT_ST_ENABLING = 2, /* D-port is Enabling */ + BFA_DPORT_ST_ENABLED = 3, /* D-port is Enabled */ +}; + +/* * ---------------------- mfg definitions ------------ */ @@ -722,7 +751,8 @@ struct bfa_ablk_cfg_pf_s { u8 rsvd[1]; u16 num_qpairs; u16 num_vectors; - u32 bw; + u16 bw_min; + u16 bw_max; }; struct bfa_ablk_cfg_port_s { @@ -889,11 +919,40 @@ struct sfp_diag_ext_s { u8 ext_status_ctl[2]; }; +/* + * Diagnostic: Data Fields -- Address A2h + * General Use Fields: User Writable Table - Features's Control Registers + * Total 32 bytes + */ +struct sfp_usr_eeprom_s { + u8 rsvd1[2]; /* 128-129 */ + u8 ewrap; /* 130 */ + u8 rsvd2[2]; /* */ + u8 owrap; /* 133 */ + u8 rsvd3[2]; /* */ + u8 prbs; /* 136: PRBS 7 generator */ + u8 rsvd4[2]; /* */ + u8 tx_eqz_16; /* 139: TX Equalizer (16xFC) */ + u8 tx_eqz_8; /* 140: TX Equalizer (8xFC) */ + u8 rsvd5[2]; /* */ + u8 rx_emp_16; /* 143: RX Emphasis (16xFC) */ + u8 rx_emp_8; /* 144: RX Emphasis (8xFC) */ + u8 rsvd6[2]; /* */ + u8 tx_eye_adj; /* 147: TX eye Threshold Adjust */ + u8 rsvd7[3]; /* */ + u8 tx_eye_qctl; /* 151: TX eye Quality Control */ + u8 tx_eye_qres; /* 152: TX eye Quality Result */ + u8 rsvd8[2]; /* */ + u8 poh[3]; /* 155-157: Power On Hours */ + u8 rsvd9[2]; /* */ +}; + struct sfp_mem_s { struct sfp_srlid_base_s srlid_base; struct sfp_srlid_ext_s srlid_ext; struct sfp_diag_base_s diag_base; struct sfp_diag_ext_s diag_ext; + struct sfp_usr_eeprom_s usr_eeprom; }; /* diff --git a/drivers/scsi/bfa/bfa_defs_svc.h b/drivers/scsi/bfa/bfa_defs_svc.h index 36756ce0e58..ec03c8cd8da 100644 --- a/drivers/scsi/bfa/bfa_defs_svc.h +++ b/drivers/scsi/bfa/bfa_defs_svc.h @@ -258,6 +258,7 @@ struct bfa_fw_port_lksm_stats_s { u32 hwsm_lrr_rx; /* No. of times LRR rx-ed by HWSM */ u32 hwsm_lr_rx; /* No. of times LR rx-ed by HWSM */ u32 bbsc_lr; /* LKSM LR tx for credit recovery */ + u32 rsvd; }; struct bfa_fw_port_snsm_stats_s { @@ -270,6 +271,9 @@ struct bfa_fw_port_snsm_stats_s { u32 sync_lost; /* Sync loss count */ u32 sig_lost; /* Signal loss count */ u32 asn8g_attempts; /* SNSM HWSM at 8Gbps attempts */ + u32 adapt_success; /* SNSM adaptation success */ + u32 adapt_fails; /* SNSM adaptation failures */ + u32 adapt_ign_fails; /* SNSM adaptation failures ignored */ }; struct bfa_fw_port_physm_stats_s { @@ -324,12 +328,46 @@ struct bfa_fw_fcoe_port_stats_s { struct bfa_fw_fip_stats_s fip_stats; }; +/** + * @brief LPSM statistics + */ +struct bfa_fw_lpsm_stats_s { + u32 cls_rx; /* LPSM cls_rx */ + u32 cls_tx; /* LPSM cls_tx */ + u32 arbf0_rx; /* LPSM abrf0 rcvd */ + u32 arbf0_tx; /* LPSM abrf0 xmit */ + u32 init_rx; /* LPSM loop init start */ + u32 unexp_hwst; /* LPSM unknown hw state */ + u32 unexp_frame; /* LPSM unknown_frame */ + u32 unexp_prim; /* LPSM unexpected primitive */ + u32 prev_alpa_unavail; /* LPSM prev alpa unavailable */ + u32 alpa_unavail; /* LPSM alpa not available */ + u32 lip_rx; /* LPSM lip rcvd */ + u32 lip_f7f7_rx; /* LPSM lip f7f7 rcvd */ + u32 lip_f8_rx; /* LPSM lip f8 rcvd */ + u32 lip_f8f7_rx; /* LPSM lip f8f7 rcvd */ + u32 lip_other_rx; /* LPSM lip other rcvd */ + u32 lip_tx; /* LPSM lip xmit */ + u32 retry_tov; /* LPSM retry TOV */ + u32 lip_tov; /* LPSM LIP wait TOV */ + u32 idle_tov; /* LPSM idle wait TOV */ + u32 arbf0_tov; /* LPSM arbfo wait TOV */ + u32 stop_loop_tov; /* LPSM stop loop wait TOV */ + u32 lixa_tov; /* LPSM lisa wait TOV */ + u32 lixx_tov; /* LPSM lilp/lirp wait TOV */ + u32 cls_tov; /* LPSM cls wait TOV */ + u32 sler; /* LPSM SLER recvd */ + u32 failed; /* LPSM failed */ + u32 success; /* LPSM online */ +}; + /* * IOC firmware FC uport stats */ struct bfa_fw_fc_uport_stats_s { struct bfa_fw_port_snsm_stats_s snsm_stats; struct bfa_fw_port_lksm_stats_s lksm_stats; + struct bfa_fw_lpsm_stats_s lpsm_stats; }; /* @@ -357,11 +395,6 @@ struct bfa_fw_fcxchg_stats_s { u32 ua_state_inv; }; -struct bfa_fw_lpsm_stats_s { - u32 cls_rx; - u32 cls_tx; -}; - /* * Trunk statistics */ @@ -454,7 +487,6 @@ struct bfa_fw_stats_s { struct bfa_fw_io_stats_s io_stats; struct bfa_fw_port_stats_s port_stats; struct bfa_fw_fcxchg_stats_s fcxchg_stats; - struct bfa_fw_lpsm_stats_s lpsm_stats; struct bfa_fw_lps_stats_s lps_stats; struct bfa_fw_trunk_stats_s trunk_stats; struct bfa_fw_advsm_stats_s advsm_stats; @@ -494,13 +526,23 @@ enum bfa_qos_bw_alloc { BFA_QOS_BW_LOW = 10, /* bandwidth allocation for Low */ }; #pragma pack(1) + +struct bfa_qos_bw_s { + u8 qos_bw_set; + u8 high; + u8 med; + u8 low; +}; + /* * QoS attribute returned in QoS Query */ struct bfa_qos_attr_s { - u8 state; /* QoS current state */ - u8 rsvd[3]; - u32 total_bb_cr; /* Total BB Credits */ + u8 state; /* QoS current state */ + u8 rsvd1[3]; + u32 total_bb_cr; /* Total BB Credits */ + struct bfa_qos_bw_s qos_bw; /* QOS bw cfg */ + struct bfa_qos_bw_s qos_bw_op; /* QOS bw operational */ }; /* @@ -692,7 +734,8 @@ enum bfa_port_states { BFA_PORT_ST_FWMISMATCH = 12, BFA_PORT_ST_PREBOOT_DISABLED = 13, BFA_PORT_ST_TOGGLING_QWAIT = 14, - BFA_PORT_ST_ACQ_ADDR = 15, + BFA_PORT_ST_FAA_MISCONFIG = 15, + BFA_PORT_ST_DPORT = 16, BFA_PORT_ST_MAX_STATE, }; @@ -714,9 +757,11 @@ enum bfa_port_type { */ enum bfa_port_topology { BFA_PORT_TOPOLOGY_NONE = 0, /* No valid topology */ - BFA_PORT_TOPOLOGY_P2P = 1, /* P2P only */ - BFA_PORT_TOPOLOGY_LOOP = 2, /* LOOP topology */ - BFA_PORT_TOPOLOGY_AUTO = 3, /* auto topology selection */ + BFA_PORT_TOPOLOGY_P2P_OLD_VER = 1, /* P2P def for older ver */ + BFA_PORT_TOPOLOGY_LOOP = 2, /* LOOP topology */ + BFA_PORT_TOPOLOGY_AUTO_OLD_VER = 3, /* auto def for older ver */ + BFA_PORT_TOPOLOGY_AUTO = 4, /* auto topology selection */ + BFA_PORT_TOPOLOGY_P2P = 5, /* P2P only */ }; /* @@ -760,6 +805,7 @@ enum bfa_port_linkstate_rsn { BFA_PORT_LINKSTATE_RSN_LOCAL_FAULT = 9, BFA_PORT_LINKSTATE_RSN_REMOTE_FAULT = 10, BFA_PORT_LINKSTATE_RSN_TIMEOUT = 11, + BFA_PORT_LINKSTATE_RSN_FAA_MISCONFIG = 12, @@ -833,6 +879,19 @@ struct bfa_lunmask_cfg_s { struct bfa_lun_mask_s lun_list[MAX_LUN_MASK_CFG]; }; +struct bfa_throttle_cfg_s { + u16 is_valid; + u16 value; + u32 rsvd; +}; + +struct bfa_defs_fcpim_throttle_s { + u16 max_value; + u16 cur_value; + u16 cfg_value; + u16 rsvd; +}; + /* * Physical port configuration */ @@ -851,9 +910,10 @@ struct bfa_port_cfg_s { u8 bb_scn; /* BB_SCN value from FLOGI Exchg */ u8 bb_scn_state; /* Config state of BB_SCN */ u8 faa_state; /* FAA enabled/disabled */ - u8 rsvd[1]; + u8 rsvd1; u16 path_tov; /* device path timeout */ u16 q_depth; /* SCSI Queue depth */ + struct bfa_qos_bw_s qos_bw; /* QOS bandwidth */ }; #pragma pack() @@ -901,7 +961,7 @@ struct bfa_port_attr_s { /* FCoE specific */ u16 fcoe_vlan; - u8 rsvd1[2]; + u8 rsvd1[6]; }; /* @@ -971,6 +1031,13 @@ struct bfa_trunk_vc_attr_s { u16 vc_credits[8]; }; +struct bfa_fcport_loop_info_s { + u8 myalpa; /* alpa claimed */ + u8 alpabm_val; /* alpa bitmap valid or not (1 or 0) */ + u8 resvd[6]; + struct fc_alpabm_s alpabm; /* alpa bitmap */ +}; + /* * Link state information */ @@ -981,13 +1048,18 @@ struct bfa_port_link_s { u8 speed; /* Link speed (1/2/4/8 G) */ u32 linkstate_opt; /* Linkstate optional data (debug) */ u8 trunked; /* Trunked or not (1 or 0) */ - u8 resvd[3]; + u8 resvd[7]; struct bfa_qos_attr_s qos_attr; /* QoS Attributes */ union { - struct bfa_qos_vc_attr_s qos_vc_attr; /* VC info from ELP */ - struct bfa_trunk_vc_attr_s trunk_vc_attr; - struct bfa_fcport_fcf_s fcf; /* FCF information (for FCoE) */ - } vc_fcf; + struct bfa_fcport_loop_info_s loop_info; + union { + struct bfa_qos_vc_attr_s qos_vc_attr; + /* VC info from ELP */ + struct bfa_trunk_vc_attr_s trunk_vc_attr; + struct bfa_fcport_fcf_s fcf; + /* FCF information (for FCoE) */ + } vc_fcf; + } attr; }; #pragma pack() @@ -1112,6 +1184,9 @@ struct bfa_port_fc_stats_s { u64 tx_frames; /* Tx frames */ u64 tx_words; /* Tx words */ u64 tx_lip; /* Tx LIP */ + u64 tx_lip_f7f7; /* Tx LIP_F7F7 */ + u64 tx_lip_f8f7; /* Tx LIP_F8F7 */ + u64 tx_arbf0; /* Tx ARB F0 */ u64 tx_nos; /* Tx NOS */ u64 tx_ols; /* Tx OLS */ u64 tx_lr; /* Tx LR */ @@ -1119,6 +1194,9 @@ struct bfa_port_fc_stats_s { u64 rx_frames; /* Rx frames */ u64 rx_words; /* Rx words */ u64 lip_count; /* Rx LIP */ + u64 rx_lip_f7f7; /* Rx LIP_F7F7 */ + u64 rx_lip_f8f7; /* Rx LIP_F8F7 */ + u64 rx_arbf0; /* Rx ARB F0 */ u64 nos_count; /* Rx NOS */ u64 ols_count; /* Rx OLS */ u64 lr_count; /* Rx LR */ @@ -1140,6 +1218,7 @@ struct bfa_port_fc_stats_s { u64 bbsc_frames_lost; /* Credit Recovery-Frames Lost */ u64 bbsc_credits_lost; /* Credit Recovery-Credits Lost */ u64 bbsc_link_resets; /* Credit Recovery-Link Resets */ + u64 loop_timeouts; /* Loop timeouts */ }; /* diff --git a/drivers/scsi/bfa/bfa_fc.h b/drivers/scsi/bfa/bfa_fc.h index e0beb4d7e26..bea821b9803 100644 --- a/drivers/scsi/bfa/bfa_fc.h +++ b/drivers/scsi/bfa/bfa_fc.h @@ -24,6 +24,7 @@ typedef u64 wwn_t; #define WWN_NULL (0) #define FC_SYMNAME_MAX 256 /* max name server symbolic name size */ +#define FC_ALPA_MAX 128 #pragma pack(1) @@ -1015,6 +1016,10 @@ struct fc_symname_s { u8 symname[FC_SYMNAME_MAX]; }; +struct fc_alpabm_s { + u8 alpa_bm[FC_ALPA_MAX / 8]; +}; + /* * protocol default timeout values */ diff --git a/drivers/scsi/bfa/bfa_fcbuild.c b/drivers/scsi/bfa/bfa_fcbuild.c index 273cee90b3b..dce787f6cca 100644 --- a/drivers/scsi/bfa/bfa_fcbuild.c +++ b/drivers/scsi/bfa/bfa_fcbuild.c @@ -228,6 +228,10 @@ fc_plogi_x_build(struct fchs_s *fchs, void *pld, u32 d_id, u32 s_id, memcpy(plogi, &plogi_tmpl, sizeof(struct fc_logi_s)); + /* For FC AL bb_cr is 0 and altbbcred is 1 */ + if (!bb_cr) + plogi->csp.altbbcred = 1; + plogi->els_cmd.els_code = els_code; if (els_code == FC_ELS_PLOGI) fc_els_req_build(fchs, d_id, s_id, ox_id); diff --git a/drivers/scsi/bfa/bfa_fcpim.c b/drivers/scsi/bfa/bfa_fcpim.c index 1633963c66c..27b56096235 100644 --- a/drivers/scsi/bfa/bfa_fcpim.c +++ b/drivers/scsi/bfa/bfa_fcpim.c @@ -158,6 +158,7 @@ enum bfa_tskim_event { BFA_TSKIM_SM_IOS_DONE = 7, /* IO and sub TM completions */ BFA_TSKIM_SM_CLEANUP = 8, /* TM cleanup on ITN offline */ BFA_TSKIM_SM_CLEANUP_DONE = 9, /* TM abort completion */ + BFA_TSKIM_SM_UTAG = 10, /* TM completion unknown tag */ }; /* @@ -3036,7 +3037,7 @@ bfa_ioim_abort(struct bfa_ioim_s *ioim) static void bfa_tskim_sm_uninit(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) { - bfa_trc(tskim->bfa, event); + bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event); switch (event) { case BFA_TSKIM_SM_START: @@ -3074,7 +3075,7 @@ bfa_tskim_sm_uninit(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) static void bfa_tskim_sm_active(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) { - bfa_trc(tskim->bfa, event); + bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event); switch (event) { case BFA_TSKIM_SM_DONE: @@ -3110,7 +3111,7 @@ bfa_tskim_sm_active(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) static void bfa_tskim_sm_cleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) { - bfa_trc(tskim->bfa, event); + bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event); switch (event) { case BFA_TSKIM_SM_DONE: @@ -3119,6 +3120,7 @@ bfa_tskim_sm_cleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) */ break; + case BFA_TSKIM_SM_UTAG: case BFA_TSKIM_SM_CLEANUP_DONE: bfa_sm_set_state(tskim, bfa_tskim_sm_iocleanup); bfa_tskim_cleanup_ios(tskim); @@ -3138,7 +3140,7 @@ bfa_tskim_sm_cleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) static void bfa_tskim_sm_iocleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) { - bfa_trc(tskim->bfa, event); + bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event); switch (event) { case BFA_TSKIM_SM_IOS_DONE: @@ -3170,7 +3172,7 @@ bfa_tskim_sm_iocleanup(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) static void bfa_tskim_sm_qfull(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) { - bfa_trc(tskim->bfa, event); + bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event); switch (event) { case BFA_TSKIM_SM_QRESUME: @@ -3207,7 +3209,7 @@ static void bfa_tskim_sm_cleanup_qfull(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) { - bfa_trc(tskim->bfa, event); + bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event); switch (event) { case BFA_TSKIM_SM_DONE: @@ -3238,7 +3240,7 @@ bfa_tskim_sm_cleanup_qfull(struct bfa_tskim_s *tskim, static void bfa_tskim_sm_hcb(struct bfa_tskim_s *tskim, enum bfa_tskim_event event) { - bfa_trc(tskim->bfa, event); + bfa_trc(tskim->bfa, tskim->tsk_tag << 16 | event); switch (event) { case BFA_TSKIM_SM_HCB: @@ -3560,6 +3562,8 @@ bfa_tskim_isr(struct bfa_s *bfa, struct bfi_msg_s *m) if (rsp->tsk_status == BFI_TSKIM_STS_ABORTED) { bfa_stats(tskim->itnim, tm_cleanup_comps); bfa_sm_send_event(tskim, BFA_TSKIM_SM_CLEANUP_DONE); + } else if (rsp->tsk_status == BFI_TSKIM_STS_UTAG) { + bfa_sm_send_event(tskim, BFA_TSKIM_SM_UTAG); } else { bfa_stats(tskim->itnim, tm_fw_rsps); bfa_sm_send_event(tskim, BFA_TSKIM_SM_DONE); @@ -3699,6 +3703,7 @@ bfa_fcp_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, struct bfa_mem_dma_s *seg_ptr; u16 idx, nsegs, num_io_req; + fcp->max_ioim_reqs = cfg->fwcfg.num_ioim_reqs; fcp->num_ioim_reqs = cfg->fwcfg.num_ioim_reqs; fcp->num_fwtio_reqs = cfg->fwcfg.num_fwtio_reqs; fcp->num_itns = cfg->fwcfg.num_rports; @@ -3721,6 +3726,7 @@ bfa_fcp_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, bfa_iocfc_set_snsbase(bfa, idx, fcp->snsbase[idx].pa); } + fcp->throttle_update_required = 1; bfa_fcpim_attach(fcp, bfad, cfg, pcidev); bfa_iotag_attach(fcp); @@ -3759,23 +3765,33 @@ bfa_fcp_iocdisable(struct bfa_s *bfa) { struct bfa_fcp_mod_s *fcp = BFA_FCP_MOD(bfa); - /* Enqueue unused ioim resources to free_q */ - list_splice_tail_init(&fcp->iotag_unused_q, &fcp->iotag_ioim_free_q); - bfa_fcpim_iocdisable(fcp); } void -bfa_fcp_res_recfg(struct bfa_s *bfa, u16 num_ioim_fw) +bfa_fcp_res_recfg(struct bfa_s *bfa, u16 num_ioim_fw, u16 max_ioim_fw) { struct bfa_fcp_mod_s *mod = BFA_FCP_MOD(bfa); struct list_head *qe; int i; + /* Update io throttle value only once during driver load time */ + if (!mod->throttle_update_required) + return; + for (i = 0; i < (mod->num_ioim_reqs - num_ioim_fw); i++) { bfa_q_deq_tail(&mod->iotag_ioim_free_q, &qe); list_add_tail(qe, &mod->iotag_unused_q); } + + if (mod->num_ioim_reqs != num_ioim_fw) { + bfa_trc(bfa, mod->num_ioim_reqs); + bfa_trc(bfa, num_ioim_fw); + } + + mod->max_ioim_reqs = max_ioim_fw; + mod->num_ioim_reqs = num_ioim_fw; + mod->throttle_update_required = 0; } void @@ -3833,3 +3849,88 @@ bfa_iotag_attach(struct bfa_fcp_mod_s *fcp) bfa_mem_kva_curp(fcp) = (u8 *) iotag; } + + +/** + * To send config req, first try to use throttle value from flash + * If 0, then use driver parameter + * We need to use min(flash_val, drv_val) because + * memory allocation was done based on this cfg'd value + */ +u16 +bfa_fcpim_get_throttle_cfg(struct bfa_s *bfa, u16 drv_cfg_param) +{ + u16 tmp; + struct bfa_fcp_mod_s *fcp = BFA_FCP_MOD(bfa); + + /* + * If throttle value from flash is already in effect after driver is + * loaded then until next load, always return current value instead + * of actual flash value + */ + if (!fcp->throttle_update_required) + return (u16)fcp->num_ioim_reqs; + + tmp = bfa_dconf_read_data_valid(bfa) ? bfa_fcpim_read_throttle(bfa) : 0; + if (!tmp || (tmp > drv_cfg_param)) + tmp = drv_cfg_param; + + return tmp; +} + +bfa_status_t +bfa_fcpim_write_throttle(struct bfa_s *bfa, u16 value) +{ + if (!bfa_dconf_get_min_cfg(bfa)) { + BFA_DCONF_MOD(bfa)->dconf->throttle_cfg.value = value; + BFA_DCONF_MOD(bfa)->dconf->throttle_cfg.is_valid = 1; + return BFA_STATUS_OK; + } + + return BFA_STATUS_FAILED; +} + +u16 +bfa_fcpim_read_throttle(struct bfa_s *bfa) +{ + struct bfa_throttle_cfg_s *throttle_cfg = + &(BFA_DCONF_MOD(bfa)->dconf->throttle_cfg); + + return ((!bfa_dconf_get_min_cfg(bfa)) ? + ((throttle_cfg->is_valid == 1) ? (throttle_cfg->value) : 0) : 0); +} + +bfa_status_t +bfa_fcpim_throttle_set(struct bfa_s *bfa, u16 value) +{ + /* in min cfg no commands should run. */ + if ((bfa_dconf_get_min_cfg(bfa) == BFA_TRUE) || + (!bfa_dconf_read_data_valid(bfa))) + return BFA_STATUS_FAILED; + + bfa_fcpim_write_throttle(bfa, value); + + return bfa_dconf_update(bfa); +} + +bfa_status_t +bfa_fcpim_throttle_get(struct bfa_s *bfa, void *buf) +{ + struct bfa_fcpim_s *fcpim = BFA_FCPIM(bfa); + struct bfa_defs_fcpim_throttle_s throttle; + + if ((bfa_dconf_get_min_cfg(bfa) == BFA_TRUE) || + (!bfa_dconf_read_data_valid(bfa))) + return BFA_STATUS_FAILED; + + memset(&throttle, 0, sizeof(struct bfa_defs_fcpim_throttle_s)); + + throttle.cur_value = (u16)(fcpim->fcp->num_ioim_reqs); + throttle.cfg_value = bfa_fcpim_read_throttle(bfa); + if (!throttle.cfg_value) + throttle.cfg_value = throttle.cur_value; + throttle.max_value = (u16)(fcpim->fcp->max_ioim_reqs); + memcpy(buf, &throttle, sizeof(struct bfa_defs_fcpim_throttle_s)); + + return BFA_STATUS_OK; +} diff --git a/drivers/scsi/bfa/bfa_fcpim.h b/drivers/scsi/bfa/bfa_fcpim.h index 36f26da80f7..e693af6e593 100644 --- a/drivers/scsi/bfa/bfa_fcpim.h +++ b/drivers/scsi/bfa/bfa_fcpim.h @@ -42,7 +42,7 @@ void bfa_itn_create(struct bfa_s *bfa, struct bfa_rport_s *rport, void (*isr)(struct bfa_s *bfa, struct bfi_msg_s *m)); void bfa_itn_isr(struct bfa_s *bfa, struct bfi_msg_s *m); void bfa_iotag_attach(struct bfa_fcp_mod_s *fcp); -void bfa_fcp_res_recfg(struct bfa_s *bfa, u16 num_ioim_fw); +void bfa_fcp_res_recfg(struct bfa_s *bfa, u16 num_ioim_fw, u16 max_ioim_fw); #define BFA_FCP_MOD(_hal) (&(_hal)->modules.fcp_mod) #define BFA_MEM_FCP_KVA(__bfa) (&(BFA_FCP_MOD(__bfa)->kva_seg)) @@ -51,7 +51,9 @@ void bfa_fcp_res_recfg(struct bfa_s *bfa, u16 num_ioim_fw); #define BFA_ITN_FROM_TAG(_fcp, _tag) \ ((_fcp)->itn_arr + ((_tag) & ((_fcp)->num_itns - 1))) #define BFA_SNSINFO_FROM_TAG(_fcp, _tag) \ - bfa_mem_get_dmabuf_kva(_fcp, _tag, BFI_IOIM_SNSLEN) + bfa_mem_get_dmabuf_kva(_fcp, (_tag & BFA_IOIM_IOTAG_MASK), \ + BFI_IOIM_SNSLEN) + #define BFA_ITNIM_MIN 32 #define BFA_ITNIM_MAX 1024 @@ -148,6 +150,7 @@ struct bfa_fcp_mod_s { struct list_head iotag_unused_q; /* unused IO resources*/ struct bfa_iotag_s *iotag_arr; struct bfa_itn_s *itn_arr; + int max_ioim_reqs; int num_ioim_reqs; int num_fwtio_reqs; int num_itns; @@ -155,6 +158,7 @@ struct bfa_fcp_mod_s { struct bfa_fcpim_s fcpim; struct bfa_mem_dma_s dma_seg[BFA_FCP_DMA_SEGS]; struct bfa_mem_kva_s kva_seg; + int throttle_update_required; }; /* @@ -416,5 +420,10 @@ bfa_status_t bfa_fcpim_lunmask_delete(struct bfa_s *bfa, u16 vf_id, bfa_status_t bfa_fcpim_lunmask_add(struct bfa_s *bfa, u16 vf_id, wwn_t *pwwn, wwn_t rpwwn, struct scsi_lun lun); bfa_status_t bfa_fcpim_lunmask_clear(struct bfa_s *bfa); +u16 bfa_fcpim_read_throttle(struct bfa_s *bfa); +bfa_status_t bfa_fcpim_write_throttle(struct bfa_s *bfa, u16 value); +bfa_status_t bfa_fcpim_throttle_set(struct bfa_s *bfa, u16 value); +bfa_status_t bfa_fcpim_throttle_get(struct bfa_s *bfa, void *buf); +u16 bfa_fcpim_get_throttle_cfg(struct bfa_s *bfa, u16 drv_cfg_param); #endif /* __BFA_FCPIM_H__ */ diff --git a/drivers/scsi/bfa/bfa_fcs.c b/drivers/scsi/bfa/bfa_fcs.c index fd3e84d32bd..d428808fb37 100644 --- a/drivers/scsi/bfa/bfa_fcs.c +++ b/drivers/scsi/bfa/bfa_fcs.c @@ -303,16 +303,30 @@ static void bfa_fcs_fabric_sm_created(struct bfa_fcs_fabric_s *fabric, enum bfa_fcs_fabric_event event) { + struct bfa_s *bfa = fabric->fcs->bfa; + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); bfa_trc(fabric->fcs, event); switch (event) { case BFA_FCS_FABRIC_SM_START: - if (bfa_fcport_is_linkup(fabric->fcs->bfa)) { + if (!bfa_fcport_is_linkup(fabric->fcs->bfa)) { + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + break; + } + if (bfa_fcport_get_topology(bfa) == + BFA_PORT_TOPOLOGY_LOOP) { + fabric->fab_type = BFA_FCS_FABRIC_LOOP; + fabric->bport.pid = bfa_fcport_get_myalpa(bfa); + fabric->bport.pid = bfa_hton3b(fabric->bport.pid); + bfa_sm_set_state(fabric, + bfa_fcs_fabric_sm_online); + bfa_fcs_fabric_set_opertype(fabric); + bfa_fcs_lport_online(&fabric->bport); + } else { bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi); bfa_fcs_fabric_login(fabric); - } else - bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); + } break; case BFA_FCS_FABRIC_SM_LINK_UP: @@ -337,16 +351,28 @@ static void bfa_fcs_fabric_sm_linkdown(struct bfa_fcs_fabric_s *fabric, enum bfa_fcs_fabric_event event) { + struct bfa_s *bfa = fabric->fcs->bfa; + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); bfa_trc(fabric->fcs, event); switch (event) { case BFA_FCS_FABRIC_SM_LINK_UP: - bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi); - bfa_fcs_fabric_login(fabric); + if (bfa_fcport_get_topology(bfa) != BFA_PORT_TOPOLOGY_LOOP) { + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_flogi); + bfa_fcs_fabric_login(fabric); + break; + } + fabric->fab_type = BFA_FCS_FABRIC_LOOP; + fabric->bport.pid = bfa_fcport_get_myalpa(bfa); + fabric->bport.pid = bfa_hton3b(fabric->bport.pid); + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_online); + bfa_fcs_fabric_set_opertype(fabric); + bfa_fcs_lport_online(&fabric->bport); break; case BFA_FCS_FABRIC_SM_RETRY_OP: + case BFA_FCS_FABRIC_SM_LOOPBACK: break; case BFA_FCS_FABRIC_SM_DELETE: @@ -595,14 +621,20 @@ void bfa_fcs_fabric_sm_online(struct bfa_fcs_fabric_s *fabric, enum bfa_fcs_fabric_event event) { + struct bfa_s *bfa = fabric->fcs->bfa; + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); bfa_trc(fabric->fcs, event); switch (event) { case BFA_FCS_FABRIC_SM_LINK_DOWN: bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_linkdown); - bfa_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE); - bfa_fcs_fabric_notify_offline(fabric); + if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP) { + bfa_fcs_lport_offline(&fabric->bport); + } else { + bfa_sm_send_event(fabric->lps, BFA_LPS_SM_OFFLINE); + bfa_fcs_fabric_notify_offline(fabric); + } break; case BFA_FCS_FABRIC_SM_DELETE: @@ -719,20 +751,29 @@ static void bfa_fcs_fabric_sm_stopping(struct bfa_fcs_fabric_s *fabric, enum bfa_fcs_fabric_event event) { + struct bfa_s *bfa = fabric->fcs->bfa; + bfa_trc(fabric->fcs, fabric->bport.port_cfg.pwwn); bfa_trc(fabric->fcs, event); switch (event) { case BFA_FCS_FABRIC_SM_STOPCOMP: - bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_cleanup); - bfa_sm_send_event(fabric->lps, BFA_LPS_SM_LOGOUT); + if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP) { + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created); + } else { + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_cleanup); + bfa_sm_send_event(fabric->lps, BFA_LPS_SM_LOGOUT); + } break; case BFA_FCS_FABRIC_SM_LINK_UP: break; case BFA_FCS_FABRIC_SM_LINK_DOWN: - bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_cleanup); + if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP) + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_created); + else + bfa_sm_set_state(fabric, bfa_fcs_fabric_sm_cleanup); break; default: @@ -975,9 +1016,6 @@ bfa_fcs_fabric_login(struct bfa_fcs_fabric_s *fabric) struct bfa_lport_cfg_s *pcfg = &fabric->bport.port_cfg; u8 alpa = 0, bb_scn = 0; - if (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP) - alpa = bfa_fcport_get_myalpa(bfa); - if (bfa_fcs_fabric_is_bbscn_enabled(fabric) && (!fabric->fcs->bbscn_flogi_rjt)) bb_scn = BFA_FCS_PORT_DEF_BB_SCN; diff --git a/drivers/scsi/bfa/bfa_fcs.h b/drivers/scsi/bfa/bfa_fcs.h index 6c4377cb287..a449706c6bc 100644 --- a/drivers/scsi/bfa/bfa_fcs.h +++ b/drivers/scsi/bfa/bfa_fcs.h @@ -118,9 +118,9 @@ struct bfa_fcs_lport_fab_s { #define MAX_ALPA_COUNT 127 struct bfa_fcs_lport_loop_s { - u8 num_alpa; /* Num of ALPA entries in the map */ - u8 alpa_pos_map[MAX_ALPA_COUNT]; /* ALPA Positional - *Map */ + u8 num_alpa; /* Num of ALPA entries in the map */ + u8 alpabm_valid; /* alpa bitmap valid or not (1 or 0) */ + u8 alpa_pos_map[MAX_ALPA_COUNT]; /* ALPA Positional Map */ struct bfa_fcs_lport_s *port; /* parent port */ }; @@ -175,6 +175,7 @@ enum bfa_fcs_fabric_type { BFA_FCS_FABRIC_UNKNOWN = 0, BFA_FCS_FABRIC_SWITCHED = 1, BFA_FCS_FABRIC_N2N = 2, + BFA_FCS_FABRIC_LOOP = 3, }; @@ -350,9 +351,10 @@ void bfa_fcs_lport_ns_util_send_rspn_id(void *cbarg, struct bfa_fcxp_s *fcxp_alloced); void bfa_fcs_lport_scn_init(struct bfa_fcs_lport_s *vport); void bfa_fcs_lport_scn_offline(struct bfa_fcs_lport_s *vport); -void bfa_fcs_lport_scn_online(struct bfa_fcs_lport_s *vport); +void bfa_fcs_lport_fab_scn_online(struct bfa_fcs_lport_s *vport); void bfa_fcs_lport_scn_process_rscn(struct bfa_fcs_lport_s *port, struct fchs_s *rx_frame, u32 len); +void bfa_fcs_lport_lip_scn_online(bfa_fcs_lport_t *port); struct bfa_fcs_vport_s { struct list_head qe; /* queue elem */ @@ -453,6 +455,7 @@ struct bfa_fcs_rport_s { struct bfa_rport_stats_s stats; /* rport stats */ enum bfa_rport_function scsi_function; /* Initiator/Target */ struct bfa_fcs_rpf_s rpf; /* Rport features module */ + bfa_boolean_t scn_online; /* SCN online flag */ }; static inline struct bfa_rport_s * @@ -639,9 +642,9 @@ struct bfa_fcs_fdmi_hba_attr_s { u8 model[16]; u8 model_desc[256]; u8 hw_version[8]; - u8 driver_version[8]; + u8 driver_version[BFA_VERSION_LEN]; u8 option_rom_ver[BFA_VERSION_LEN]; - u8 fw_version[8]; + u8 fw_version[BFA_VERSION_LEN]; u8 os_name[256]; __be32 max_ct_pyld; }; @@ -733,7 +736,7 @@ enum rport_event { RPSM_EVENT_LOGO_IMP = 5, /* implicit logo for SLER */ RPSM_EVENT_FCXP_SENT = 6, /* Frame from has been sent */ RPSM_EVENT_DELETE = 7, /* RPORT delete request */ - RPSM_EVENT_SCN = 8, /* state change notification */ + RPSM_EVENT_FAB_SCN = 8, /* state change notification */ RPSM_EVENT_ACCEPTED = 9, /* Good response from remote device */ RPSM_EVENT_FAILED = 10, /* Request to rport failed. */ RPSM_EVENT_TIMEOUT = 11, /* Rport SM timeout event */ @@ -744,7 +747,9 @@ enum rport_event { RPSM_EVENT_ADDRESS_DISC = 16, /* Need to Discover rport's PID */ RPSM_EVENT_PRLO_RCVD = 17, /* PRLO from remote device */ RPSM_EVENT_PLOGI_RETRY = 18, /* Retry PLOGI continuously */ - RPSM_EVENT_FC4_FCS_ONLINE = 19, /*!< FC-4 FCS online complete */ + RPSM_EVENT_SCN_OFFLINE = 19, /* loop scn offline */ + RPSM_EVENT_SCN_ONLINE = 20, /* loop scn online */ + RPSM_EVENT_FC4_FCS_ONLINE = 21, /* FC-4 FCS online complete */ }; /* @@ -763,7 +768,7 @@ enum bfa_fcs_itnim_event { BFA_FCS_ITNIM_SM_DELETE = 10, /* delete event from rport */ BFA_FCS_ITNIM_SM_PRLO = 11, /* delete event from rport */ BFA_FCS_ITNIM_SM_RSP_NOT_SUPP = 12, /* cmd not supported rsp */ - BFA_FCS_ITNIM_SM_HAL_ONLINE = 13, /*!< bfa rport online event */ + BFA_FCS_ITNIM_SM_HAL_ONLINE = 13, /* bfa rport online event */ }; /* diff --git a/drivers/scsi/bfa/bfa_fcs_lport.c b/drivers/scsi/bfa/bfa_fcs_lport.c index 3b75f6fb2de..1224d0462a4 100644 --- a/drivers/scsi/bfa/bfa_fcs_lport.c +++ b/drivers/scsi/bfa/bfa_fcs_lport.c @@ -23,6 +23,34 @@ BFA_TRC_FILE(FCS, PORT); +/* + * ALPA to LIXA bitmap mapping + * + * ALPA 0x00 (Word 0, Bit 30) is invalid for N_Ports. Also Word 0 Bit 31 + * is for L_bit (login required) and is filled as ALPA 0x00 here. + */ +static const u8 loop_alpa_map[] = { + 0x00, 0x00, 0x01, 0x02, 0x04, 0x08, 0x0F, 0x10, /* Word 0 Bits 31..24 */ + 0x17, 0x18, 0x1B, 0x1D, 0x1E, 0x1F, 0x23, 0x25, /* Word 0 Bits 23..16 */ + 0x26, 0x27, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, /* Word 0 Bits 15..08 */ + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x39, 0x3A, /* Word 0 Bits 07..00 */ + + 0x3C, 0x43, 0x45, 0x46, 0x47, 0x49, 0x4A, 0x4B, /* Word 1 Bits 31..24 */ + 0x4C, 0x4D, 0x4E, 0x51, 0x52, 0x53, 0x54, 0x55, /* Word 1 Bits 23..16 */ + 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x67, /* Word 1 Bits 15..08 */ + 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x71, 0x72, /* Word 1 Bits 07..00 */ + + 0x73, 0x74, 0x75, 0x76, 0x79, 0x7A, 0x7C, 0x80, /* Word 2 Bits 31..24 */ + 0x81, 0x82, 0x84, 0x88, 0x8F, 0x90, 0x97, 0x98, /* Word 2 Bits 23..16 */ + 0x9B, 0x9D, 0x9E, 0x9F, 0xA3, 0xA5, 0xA6, 0xA7, /* Word 2 Bits 15..08 */ + 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xB1, 0xB2, /* Word 2 Bits 07..00 */ + + 0xB3, 0xB4, 0xB5, 0xB6, 0xB9, 0xBA, 0xBC, 0xC3, /* Word 3 Bits 31..24 */ + 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, /* Word 3 Bits 23..16 */ + 0xCE, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD9, /* Word 3 Bits 15..08 */ + 0xDA, 0xDC, 0xE0, 0xE1, 0xE2, 0xE4, 0xE8, 0xEF, /* Word 3 Bits 07..00 */ +}; + static void bfa_fcs_lport_send_ls_rjt(struct bfa_fcs_lport_s *port, struct fchs_s *rx_fchs, u8 reason_code, u8 reason_code_expl); @@ -51,6 +79,10 @@ static void bfa_fcs_lport_n2n_init(struct bfa_fcs_lport_s *port); static void bfa_fcs_lport_n2n_online(struct bfa_fcs_lport_s *port); static void bfa_fcs_lport_n2n_offline(struct bfa_fcs_lport_s *port); +static void bfa_fcs_lport_loop_init(struct bfa_fcs_lport_s *port); +static void bfa_fcs_lport_loop_online(struct bfa_fcs_lport_s *port); +static void bfa_fcs_lport_loop_offline(struct bfa_fcs_lport_s *port); + static struct { void (*init) (struct bfa_fcs_lport_s *port); void (*online) (struct bfa_fcs_lport_s *port); @@ -62,7 +94,9 @@ static struct { bfa_fcs_lport_fab_init, bfa_fcs_lport_fab_online, bfa_fcs_lport_fab_offline}, { bfa_fcs_lport_n2n_init, bfa_fcs_lport_n2n_online, - bfa_fcs_lport_n2n_offline}, + bfa_fcs_lport_n2n_offline}, { + bfa_fcs_lport_loop_init, bfa_fcs_lport_loop_online, + bfa_fcs_lport_loop_offline}, }; /* @@ -1127,7 +1161,7 @@ static void bfa_fcs_lport_fab_online(struct bfa_fcs_lport_s *port) { bfa_fcs_lport_ns_online(port); - bfa_fcs_lport_scn_online(port); + bfa_fcs_lport_fab_scn_online(port); } /* @@ -1221,6 +1255,98 @@ bfa_fcs_lport_n2n_offline(struct bfa_fcs_lport_s *port) n2n_port->reply_oxid = 0; } +void +bfa_fcport_get_loop_attr(struct bfa_fcs_lport_s *port) +{ + int i = 0, j = 0, bit = 0, alpa_bit = 0; + u8 k = 0; + struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(port->fcs->bfa); + + port->port_topo.ploop.alpabm_valid = fcport->alpabm_valid; + port->pid = fcport->myalpa; + port->pid = bfa_hton3b(port->pid); + + for (i = 0; i < (FC_ALPA_MAX / 8); i++) { + for (j = 0, alpa_bit = 0; j < 8; j++, alpa_bit++) { + bfa_trc(port->fcs->bfa, fcport->alpabm.alpa_bm[i]); + bit = (fcport->alpabm.alpa_bm[i] & (1 << (7 - j))); + if (bit) { + port->port_topo.ploop.alpa_pos_map[k] = + loop_alpa_map[(i * 8) + alpa_bit]; + k++; + bfa_trc(port->fcs->bfa, k); + bfa_trc(port->fcs->bfa, + port->port_topo.ploop.alpa_pos_map[k]); + } + } + } + port->port_topo.ploop.num_alpa = k; +} + +/* + * Called by fcs/port to initialize Loop topology. + */ +static void +bfa_fcs_lport_loop_init(struct bfa_fcs_lport_s *port) +{ +} + +/* + * Called by fcs/port to notify transition to online state. + */ +static void +bfa_fcs_lport_loop_online(struct bfa_fcs_lport_s *port) +{ + u8 num_alpa = 0, alpabm_valid = 0; + struct bfa_fcs_rport_s *rport; + u8 *alpa_map = NULL; + int i = 0; + u32 pid; + + bfa_fcport_get_loop_attr(port); + + num_alpa = port->port_topo.ploop.num_alpa; + alpabm_valid = port->port_topo.ploop.alpabm_valid; + alpa_map = port->port_topo.ploop.alpa_pos_map; + + bfa_trc(port->fcs->bfa, port->pid); + bfa_trc(port->fcs->bfa, num_alpa); + if (alpabm_valid == 1) { + for (i = 0; i < num_alpa; i++) { + bfa_trc(port->fcs->bfa, alpa_map[i]); + if (alpa_map[i] != bfa_hton3b(port->pid)) { + pid = alpa_map[i]; + bfa_trc(port->fcs->bfa, pid); + rport = bfa_fcs_lport_get_rport_by_pid(port, + bfa_hton3b(pid)); + if (!rport) + rport = bfa_fcs_rport_create(port, + bfa_hton3b(pid)); + } + } + } else { + for (i = 0; i < MAX_ALPA_COUNT; i++) { + if (alpa_map[i] != port->pid) { + pid = loop_alpa_map[i]; + bfa_trc(port->fcs->bfa, pid); + rport = bfa_fcs_lport_get_rport_by_pid(port, + bfa_hton3b(pid)); + if (!rport) + rport = bfa_fcs_rport_create(port, + bfa_hton3b(pid)); + } + } + } +} + +/* + * Called by fcs/port to notify transition to offline state. + */ +static void +bfa_fcs_lport_loop_offline(struct bfa_fcs_lport_s *port) +{ +} + #define BFA_FCS_FDMI_CMD_MAX_RETRIES 2 /* @@ -1888,13 +2014,10 @@ bfa_fcs_lport_fdmi_build_rhba_pyld(struct bfa_fcs_lport_fdmi_s *fdmi, u8 *pyld) sizeof(templen)); } - /* - * f/w Version = driver version - */ attr = (struct fdmi_attr_s *) curr_ptr; attr->type = cpu_to_be16(FDMI_HBA_ATTRIB_FW_VERSION); - templen = (u16) strlen(fcs_hba_attr->driver_version); - memcpy(attr->value, fcs_hba_attr->driver_version, templen); + templen = (u16) strlen(fcs_hba_attr->fw_version); + memcpy(attr->value, fcs_hba_attr->fw_version, templen); templen = fc_roundup(templen, sizeof(u32)); curr_ptr += sizeof(attr->type) + sizeof(templen) + templen; len += templen; @@ -2296,6 +2419,7 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi, { struct bfa_fcs_lport_s *port = fdmi->ms->port; struct bfa_fcs_driver_info_s *driver_info = &port->fcs->driver_info; + struct bfa_fcs_fdmi_port_attr_s fcs_port_attr; memset(hba_attr, 0, sizeof(struct bfa_fcs_fdmi_hba_attr_s)); @@ -2331,7 +2455,9 @@ bfa_fcs_fdmi_get_hbaattr(struct bfa_fcs_lport_fdmi_s *fdmi, sizeof(driver_info->host_os_patch)); } - hba_attr->max_ct_pyld = cpu_to_be32(FC_MAX_PDUSZ); + /* Retrieve the max frame size from the port attr */ + bfa_fcs_fdmi_get_portattr(fdmi, &fcs_port_attr); + hba_attr->max_ct_pyld = fcs_port_attr.max_frm_size; } static void @@ -2391,7 +2517,7 @@ bfa_fcs_fdmi_get_portattr(struct bfa_fcs_lport_fdmi_s *fdmi, /* * Max PDU Size. */ - port_attr->max_frm_size = cpu_to_be32(FC_MAX_PDUSZ); + port_attr->max_frm_size = cpu_to_be32(pport_attr.pport_cfg.maxfrsize); /* * OS device Name @@ -5199,7 +5325,7 @@ bfa_fcs_lport_scn_offline(struct bfa_fcs_lport_s *port) } void -bfa_fcs_lport_scn_online(struct bfa_fcs_lport_s *port) +bfa_fcs_lport_fab_scn_online(struct bfa_fcs_lport_s *port) { struct bfa_fcs_lport_scn_s *scn = BFA_FCS_GET_SCN_FROM_PORT(port); @@ -5621,6 +5747,15 @@ bfa_fcs_lport_clear_stats(struct bfa_fcs_lport_s *fcs_port) } /* + * Let new loop map create missing rports + */ +void +bfa_fcs_lport_lip_scn_online(struct bfa_fcs_lport_s *port) +{ + bfa_fcs_lport_loop_online(port); +} + +/* * FCS virtual port state machine */ diff --git a/drivers/scsi/bfa/bfa_fcs_rport.c b/drivers/scsi/bfa/bfa_fcs_rport.c index cc43b2a58ce..58ac643ba9f 100644 --- a/drivers/scsi/bfa/bfa_fcs_rport.c +++ b/drivers/scsi/bfa/bfa_fcs_rport.c @@ -106,9 +106,13 @@ static void bfa_fcs_rport_sm_nsquery_sending(struct bfa_fcs_rport_s *rport, enum rport_event event); static void bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, enum rport_event event); -static void bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport, - enum rport_event event); -static void bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, +static void bfa_fcs_rport_sm_adisc_online_sending( + struct bfa_fcs_rport_s *rport, enum rport_event event); +static void bfa_fcs_rport_sm_adisc_online(struct bfa_fcs_rport_s *rport, + enum rport_event event); +static void bfa_fcs_rport_sm_adisc_offline_sending(struct bfa_fcs_rport_s + *rport, enum rport_event event); +static void bfa_fcs_rport_sm_adisc_offline(struct bfa_fcs_rport_s *rport, enum rport_event event); static void bfa_fcs_rport_sm_fc4_logorcv(struct bfa_fcs_rport_s *rport, enum rport_event event); @@ -150,8 +154,10 @@ static struct bfa_sm_table_s rport_sm_table[] = { {BFA_SM(bfa_fcs_rport_sm_online), BFA_RPORT_ONLINE}, {BFA_SM(bfa_fcs_rport_sm_nsquery_sending), BFA_RPORT_NSQUERY}, {BFA_SM(bfa_fcs_rport_sm_nsquery), BFA_RPORT_NSQUERY}, - {BFA_SM(bfa_fcs_rport_sm_adisc_sending), BFA_RPORT_ADISC}, - {BFA_SM(bfa_fcs_rport_sm_adisc), BFA_RPORT_ADISC}, + {BFA_SM(bfa_fcs_rport_sm_adisc_online_sending), BFA_RPORT_ADISC}, + {BFA_SM(bfa_fcs_rport_sm_adisc_online), BFA_RPORT_ADISC}, + {BFA_SM(bfa_fcs_rport_sm_adisc_offline_sending), BFA_RPORT_ADISC}, + {BFA_SM(bfa_fcs_rport_sm_adisc_offline), BFA_RPORT_ADISC}, {BFA_SM(bfa_fcs_rport_sm_fc4_logorcv), BFA_RPORT_LOGORCV}, {BFA_SM(bfa_fcs_rport_sm_fc4_logosend), BFA_RPORT_LOGO}, {BFA_SM(bfa_fcs_rport_sm_fc4_offline), BFA_RPORT_OFFLINE}, @@ -231,10 +237,19 @@ bfa_fcs_rport_sm_plogi_sending(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_send_plogiacc(rport, NULL); break; + case RPSM_EVENT_SCN_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; case RPSM_EVENT_ADDRESS_CHANGE: - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: /* query the NS */ bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + WARN_ON(!(bfa_fcport_get_topology(rport->port->fcs->bfa) != + BFA_PORT_TOPOLOGY_LOOP)); bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); rport->ns_retries = 0; bfa_fcs_rport_send_nsdisc(rport, NULL); @@ -280,12 +295,20 @@ bfa_fcs_rport_sm_plogiacc_sending(struct bfa_fcs_rport_s *rport, case RPSM_EVENT_PLOGI_RCVD: case RPSM_EVENT_PLOGI_COMP: - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: /* * Ignore, SCN is possibly online notification. */ break; + case RPSM_EVENT_SCN_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + case RPSM_EVENT_ADDRESS_CHANGE: bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); @@ -346,9 +369,19 @@ bfa_fcs_rport_sm_plogi_retry(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_send_plogiacc(rport, NULL); break; + case RPSM_EVENT_SCN_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_stop(&rport->timer); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + case RPSM_EVENT_ADDRESS_CHANGE: - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: bfa_timer_stop(&rport->timer); + WARN_ON(!(bfa_fcport_get_topology(rport->port->fcs->bfa) != + BFA_PORT_TOPOLOGY_LOOP)); bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); rport->ns_retries = 0; bfa_fcs_rport_send_nsdisc(rport, NULL); @@ -422,7 +455,18 @@ bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event) } break; - case RPSM_EVENT_PLOGI_RETRY: + case RPSM_EVENT_SCN_ONLINE: + break; + + case RPSM_EVENT_SCN_OFFLINE: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_discard(rport->fcxp); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_PLOGI_RETRY: rport->plogi_retries = 0; bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_retry); bfa_timer_start(rport->fcs->bfa, &rport->timer, @@ -440,8 +484,10 @@ bfa_fcs_rport_sm_plogi(struct bfa_fcs_rport_s *rport, enum rport_event event) break; case RPSM_EVENT_ADDRESS_CHANGE: - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: bfa_fcxp_discard(rport->fcxp); + WARN_ON(!(bfa_fcport_get_topology(rport->port->fcs->bfa) != + BFA_PORT_TOPOLOGY_LOOP)); bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); rport->ns_retries = 0; bfa_fcs_rport_send_nsdisc(rport, NULL); @@ -512,7 +558,8 @@ bfa_fcs_rport_sm_fc4_fcs_online(struct bfa_fcs_rport_s *rport, case RPSM_EVENT_PLOGI_COMP: case RPSM_EVENT_LOGO_IMP: case RPSM_EVENT_ADDRESS_CHANGE: - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: + case RPSM_EVENT_SCN_OFFLINE: bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); bfa_fcs_rport_fcs_offline_action(rport); break; @@ -561,9 +608,10 @@ bfa_fcs_rport_sm_hal_online(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_fcs_offline_action(rport); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: case RPSM_EVENT_LOGO_IMP: case RPSM_EVENT_ADDRESS_CHANGE: + case RPSM_EVENT_SCN_OFFLINE: bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); bfa_fcs_rport_fcs_offline_action(rport); break; @@ -595,14 +643,15 @@ bfa_fcs_rport_sm_online(struct bfa_fcs_rport_s *rport, enum rport_event event) bfa_trc(rport->fcs, event); switch (event) { - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: if (bfa_fcs_fabric_is_switched(rport->port->fabric)) { bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsquery_sending); rport->ns_retries = 0; bfa_fcs_rport_send_nsdisc(rport, NULL); } else { - bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_sending); + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_adisc_online_sending); bfa_fcs_rport_send_adisc(rport, NULL); } break; @@ -610,6 +659,7 @@ bfa_fcs_rport_sm_online(struct bfa_fcs_rport_s *rport, enum rport_event event) case RPSM_EVENT_PLOGI_RCVD: case RPSM_EVENT_LOGO_IMP: case RPSM_EVENT_ADDRESS_CHANGE: + case RPSM_EVENT_SCN_OFFLINE: bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_offline); bfa_fcs_rport_hal_offline_action(rport); break; @@ -625,6 +675,7 @@ bfa_fcs_rport_sm_online(struct bfa_fcs_rport_s *rport, enum rport_event event) bfa_fcs_rport_hal_offline_action(rport); break; + case RPSM_EVENT_SCN_ONLINE: case RPSM_EVENT_PLOGI_COMP: break; @@ -656,7 +707,7 @@ bfa_fcs_rport_sm_nsquery_sending(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_hal_offline_action(rport); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: /* * ignore SCN, wait for response to query itself */ @@ -696,7 +747,7 @@ bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, enum rport_event event) switch (event) { case RPSM_EVENT_ACCEPTED: - bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_sending); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_online_sending); bfa_fcs_rport_send_adisc(rport, NULL); break; @@ -718,7 +769,7 @@ bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, enum rport_event event) bfa_fcs_rport_hal_offline_action(rport); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: break; case RPSM_EVENT_LOGO_RCVD: @@ -747,7 +798,7 @@ bfa_fcs_rport_sm_nsquery(struct bfa_fcs_rport_s *rport, enum rport_event event) * authenticating with rport. FC-4s are paused. */ static void -bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport, +bfa_fcs_rport_sm_adisc_online_sending(struct bfa_fcs_rport_s *rport, enum rport_event event) { bfa_trc(rport->fcs, rport->pwwn); @@ -756,7 +807,7 @@ bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport, switch (event) { case RPSM_EVENT_FCXP_SENT: - bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_online); break; case RPSM_EVENT_DELETE: @@ -779,7 +830,7 @@ bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_hal_offline_action(rport); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: break; case RPSM_EVENT_PLOGI_RCVD: @@ -798,7 +849,8 @@ bfa_fcs_rport_sm_adisc_sending(struct bfa_fcs_rport_s *rport, * FC-4s are paused. */ static void -bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, enum rport_event event) +bfa_fcs_rport_sm_adisc_online(struct bfa_fcs_rport_s *rport, + enum rport_event event) { bfa_trc(rport->fcs, rport->pwwn); bfa_trc(rport->fcs, rport->pid); @@ -831,7 +883,7 @@ bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, enum rport_event event) bfa_fcs_rport_hal_offline_action(rport); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: /* * already processing RSCN */ @@ -856,7 +908,96 @@ bfa_fcs_rport_sm_adisc(struct bfa_fcs_rport_s *rport, enum rport_event event) } /* - * Rport has sent LOGO. Awaiting FC-4 offline completion callback. + * ADISC is being sent for authenticating with rport + * Already did offline actions. + */ +static void +bfa_fcs_rport_sm_adisc_offline_sending(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_FCXP_SENT: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_adisc_offline); + break; + + case RPSM_EVENT_DELETE: + case RPSM_EVENT_SCN_OFFLINE: + case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_PRLO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, + &rport->fcxp_wqe); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcxp_walloc_cancel(rport->fcs->bfa, &rport->fcxp_wqe); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + default: + bfa_sm_fault(rport->fcs, event); + } +} + +/* + * ADISC to rport + * Already did offline actions + */ +static void +bfa_fcs_rport_sm_adisc_offline(struct bfa_fcs_rport_s *rport, + enum rport_event event) +{ + bfa_trc(rport->fcs, rport->pwwn); + bfa_trc(rport->fcs, rport->pid); + bfa_trc(rport->fcs, event); + + switch (event) { + case RPSM_EVENT_ACCEPTED: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_hal_online); + bfa_fcs_rport_hal_online(rport); + break; + + case RPSM_EVENT_PLOGI_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogiacc_sending); + bfa_fcxp_discard(rport->fcxp); + bfa_fcs_rport_send_plogiacc(rport, NULL); + break; + + case RPSM_EVENT_FAILED: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + case RPSM_EVENT_DELETE: + case RPSM_EVENT_SCN_OFFLINE: + case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_LOGO_RCVD: + case RPSM_EVENT_PRLO_RCVD: + bfa_sm_set_state(rport, bfa_fcs_rport_sm_offline); + bfa_fcxp_discard(rport->fcxp); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + break; + + default: + bfa_sm_fault(rport->fcs, event); + } +} + +/* + * Rport has sent LOGO. Awaiting FC-4 offline completion callback. */ static void bfa_fcs_rport_sm_fc4_logorcv(struct bfa_fcs_rport_s *rport, @@ -881,6 +1022,8 @@ bfa_fcs_rport_sm_fc4_logorcv(struct bfa_fcs_rport_s *rport, bfa_sm_set_state(rport, bfa_fcs_rport_sm_fc4_off_delete); break; + case RPSM_EVENT_SCN_ONLINE: + case RPSM_EVENT_SCN_OFFLINE: case RPSM_EVENT_HCB_ONLINE: case RPSM_EVENT_LOGO_RCVD: case RPSM_EVENT_PRLO_RCVD: @@ -945,6 +1088,8 @@ bfa_fcs_rport_sm_fc4_offline(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_hal_offline(rport); break; + case RPSM_EVENT_SCN_ONLINE: + break; case RPSM_EVENT_LOGO_RCVD: /* * Rport is going offline. Just ack the logo @@ -956,8 +1101,9 @@ bfa_fcs_rport_sm_fc4_offline(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_send_prlo_acc(rport); break; + case RPSM_EVENT_SCN_OFFLINE: case RPSM_EVENT_HCB_ONLINE: - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: case RPSM_EVENT_LOGO_IMP: case RPSM_EVENT_ADDRESS_CHANGE: /* @@ -1015,6 +1161,19 @@ bfa_fcs_rport_sm_hcb_offline(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_sm_nsdisc_sending); rport->ns_retries = 0; bfa_fcs_rport_send_nsdisc(rport, NULL); + } else if (bfa_fcport_get_topology(rport->port->fcs->bfa) == + BFA_PORT_TOPOLOGY_LOOP) { + if (rport->scn_online) { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_adisc_offline_sending); + bfa_fcs_rport_send_adisc(rport, NULL); + } else { + bfa_sm_set_state(rport, + bfa_fcs_rport_sm_offline); + bfa_timer_start(rport->fcs->bfa, &rport->timer, + bfa_fcs_rport_timeout, rport, + bfa_fcs_rport_del_timeout); + } } else { bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); rport->plogi_retries = 0; @@ -1027,7 +1186,9 @@ bfa_fcs_rport_sm_hcb_offline(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_free(rport); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_SCN_ONLINE: + case RPSM_EVENT_SCN_OFFLINE: + case RPSM_EVENT_FAB_SCN: case RPSM_EVENT_LOGO_RCVD: case RPSM_EVENT_PRLO_RCVD: case RPSM_EVENT_PLOGI_RCVD: @@ -1106,6 +1267,8 @@ bfa_fcs_rport_sm_hcb_logorcv(struct bfa_fcs_rport_s *rport, bfa_sm_set_state(rport, bfa_fcs_rport_sm_hcb_offline); break; + case RPSM_EVENT_SCN_ONLINE: + case RPSM_EVENT_SCN_OFFLINE: case RPSM_EVENT_LOGO_RCVD: case RPSM_EVENT_PRLO_RCVD: /* @@ -1146,6 +1309,8 @@ bfa_fcs_rport_sm_hcb_logosend(struct bfa_fcs_rport_s *rport, bfa_sm_set_state(rport, bfa_fcs_rport_sm_delete_pending); break; + case RPSM_EVENT_SCN_ONLINE: + case RPSM_EVENT_SCN_OFFLINE: case RPSM_EVENT_ADDRESS_CHANGE: break; @@ -1172,7 +1337,9 @@ bfa_fcs_rport_sm_logo_sending(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_free(rport); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_SCN_ONLINE: + case RPSM_EVENT_SCN_OFFLINE: + case RPSM_EVENT_FAB_SCN: case RPSM_EVENT_ADDRESS_CHANGE: break; @@ -1209,10 +1376,12 @@ bfa_fcs_rport_sm_offline(struct bfa_fcs_rport_s *rport, enum rport_event event) bfa_fcs_rport_free(rport); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: case RPSM_EVENT_ADDRESS_CHANGE: - bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); bfa_timer_stop(&rport->timer); + WARN_ON(!(bfa_fcport_get_topology(rport->port->fcs->bfa) != + BFA_PORT_TOPOLOGY_LOOP)); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); rport->ns_retries = 0; bfa_fcs_rport_send_nsdisc(rport, NULL); break; @@ -1232,6 +1401,7 @@ bfa_fcs_rport_sm_offline(struct bfa_fcs_rport_s *rport, enum rport_event event) case RPSM_EVENT_LOGO_RCVD: case RPSM_EVENT_PRLO_RCVD: case RPSM_EVENT_LOGO_IMP: + case RPSM_EVENT_SCN_OFFLINE: break; case RPSM_EVENT_PLOGI_COMP: @@ -1240,6 +1410,12 @@ bfa_fcs_rport_sm_offline(struct bfa_fcs_rport_s *rport, enum rport_event event) bfa_fcs_rport_fcs_online_action(rport); break; + case RPSM_EVENT_SCN_ONLINE: + bfa_timer_stop(&rport->timer); + bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); + bfa_fcs_rport_send_plogi(rport, NULL); + break; + case RPSM_EVENT_PLOGI_SEND: bfa_timer_stop(&rport->timer); bfa_sm_set_state(rport, bfa_fcs_rport_sm_plogi_sending); @@ -1280,7 +1456,7 @@ bfa_fcs_rport_sm_nsdisc_sending(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_send_plogiacc(rport, NULL); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: case RPSM_EVENT_LOGO_RCVD: case RPSM_EVENT_PRLO_RCVD: case RPSM_EVENT_PLOGI_SEND: @@ -1326,7 +1502,7 @@ bfa_fcs_rport_sm_nsdisc_retry(struct bfa_fcs_rport_s *rport, bfa_fcs_rport_send_nsdisc(rport, NULL); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: case RPSM_EVENT_ADDRESS_CHANGE: bfa_sm_set_state(rport, bfa_fcs_rport_sm_nsdisc_sending); bfa_timer_stop(&rport->timer); @@ -1439,7 +1615,7 @@ bfa_fcs_rport_sm_nsdisc_sent(struct bfa_fcs_rport_s *rport, case RPSM_EVENT_PRLO_RCVD: bfa_fcs_rport_send_prlo_acc(rport); break; - case RPSM_EVENT_SCN: + case RPSM_EVENT_FAB_SCN: /* * ignore, wait for NS query response */ @@ -2546,7 +2722,7 @@ void bfa_fcs_rport_scn(struct bfa_fcs_rport_s *rport) { rport->stats.rscns++; - bfa_sm_send_event(rport, RPSM_EVENT_SCN); + bfa_sm_send_event(rport, RPSM_EVENT_FAB_SCN); } /* @@ -2621,6 +2797,48 @@ bfa_cb_rport_qos_scn_flowid(void *cbarg, bfa_fcs_rport_aen_post(rport, BFA_RPORT_AEN_QOS_FLOWID, &aen_data); } +void +bfa_cb_rport_scn_online(struct bfa_s *bfa) +{ + struct bfa_fcs_s *fcs = &((struct bfad_s *)bfa->bfad)->bfa_fcs; + struct bfa_fcs_lport_s *port = bfa_fcs_get_base_port(fcs); + struct bfa_fcs_rport_s *rp; + struct list_head *qe; + + list_for_each(qe, &port->rport_q) { + rp = (struct bfa_fcs_rport_s *) qe; + bfa_sm_send_event(rp, RPSM_EVENT_SCN_ONLINE); + rp->scn_online = BFA_TRUE; + } + + if (bfa_fcs_lport_is_online(port)) + bfa_fcs_lport_lip_scn_online(port); +} + +void +bfa_cb_rport_scn_no_dev(void *rport) +{ + struct bfa_fcs_rport_s *rp = rport; + + bfa_sm_send_event(rp, RPSM_EVENT_SCN_OFFLINE); + rp->scn_online = BFA_FALSE; +} + +void +bfa_cb_rport_scn_offline(struct bfa_s *bfa) +{ + struct bfa_fcs_s *fcs = &((struct bfad_s *)bfa->bfad)->bfa_fcs; + struct bfa_fcs_lport_s *port = bfa_fcs_get_base_port(fcs); + struct bfa_fcs_rport_s *rp; + struct list_head *qe; + + list_for_each(qe, &port->rport_q) { + rp = (struct bfa_fcs_rport_s *) qe; + bfa_sm_send_event(rp, RPSM_EVENT_SCN_OFFLINE); + rp->scn_online = BFA_FALSE; + } +} + /* * brief * This routine is a static BFA callback when there is a QoS priority @@ -2808,6 +3026,9 @@ bfa_fcs_rport_get_attr(struct bfa_fcs_rport_s *rport, struct bfa_rport_qos_attr_s qos_attr; struct bfa_fcs_lport_s *port = rport->port; bfa_port_speed_t rport_speed = rport->rpf.rpsc_speed; + struct bfa_port_attr_s port_attr; + + bfa_fcport_get_attr(rport->fcs->bfa, &port_attr); memset(rport_attr, 0, sizeof(struct bfa_rport_attr_s)); memset(&qos_attr, 0, sizeof(struct bfa_rport_qos_attr_s)); @@ -2838,7 +3059,8 @@ bfa_fcs_rport_get_attr(struct bfa_fcs_rport_s *rport, rport_speed = bfa_fcport_get_ratelim_speed(rport->fcs->bfa); - if (rport_speed < bfa_fcs_lport_get_rport_max_speed(port)) + if ((bfa_fcs_lport_get_rport_max_speed(port) != + BFA_PORT_SPEED_UNKNOWN) && (rport_speed < port_attr.speed)) rport_attr->trl_enforced = BFA_TRUE; } } diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c index 75ca8752b9f..0116c1032e2 100644 --- a/drivers/scsi/bfa/bfa_ioc.c +++ b/drivers/scsi/bfa/bfa_ioc.c @@ -731,8 +731,7 @@ bfa_iocpf_sm_fwcheck_entry(struct bfa_iocpf_s *iocpf) /* * Unlock the hw semaphore. Should be here only once per boot. */ - readl(iocpf->ioc->ioc_regs.ioc_sem_reg); - writel(1, iocpf->ioc->ioc_regs.ioc_sem_reg); + bfa_ioc_ownership_reset(iocpf->ioc); /* * unlock init semaphore. @@ -1751,6 +1750,7 @@ bfa_ioc_getattr_reply(struct bfa_ioc_s *ioc) attr->card_type = be32_to_cpu(attr->card_type); attr->maxfrsize = be16_to_cpu(attr->maxfrsize); ioc->fcmode = (attr->port_mode == BFI_PORT_MODE_FC); + attr->mfg_year = be16_to_cpu(attr->mfg_year); bfa_fsm_send_event(ioc, IOC_E_FWRSP_GETATTR); } @@ -2497,6 +2497,9 @@ bfa_ioc_get_adapter_attr(struct bfa_ioc_s *ioc, ad_attr->cna_capable = bfa_ioc_is_cna(ioc); ad_attr->trunk_capable = (ad_attr->nports > 1) && !bfa_ioc_is_cna(ioc) && !ad_attr->is_mezz; + ad_attr->mfg_day = ioc_attr->mfg_day; + ad_attr->mfg_month = ioc_attr->mfg_month; + ad_attr->mfg_year = ioc_attr->mfg_year; } enum bfa_ioc_type_e @@ -2923,7 +2926,7 @@ bfa_ioc_poll_fwinit(struct bfa_ioc_s *ioc) return; } - if (ioc->iocpf.poll_time >= BFA_IOC_TOV) + if (ioc->iocpf.poll_time >= (3 * BFA_IOC_TOV)) bfa_iocpf_timeout(ioc); else { ioc->iocpf.poll_time += BFA_IOC_POLL_TOV; @@ -3016,7 +3019,6 @@ bfa_ablk_config_swap(struct bfa_ablk_cfg_s *cfg) struct bfa_ablk_cfg_inst_s *cfg_inst; int i, j; u16 be16; - u32 be32; for (i = 0; i < BFA_ABLK_MAX; i++) { cfg_inst = &cfg->inst[i]; @@ -3027,8 +3029,10 @@ bfa_ablk_config_swap(struct bfa_ablk_cfg_s *cfg) cfg_inst->pf_cfg[j].num_qpairs = be16_to_cpu(be16); be16 = cfg_inst->pf_cfg[j].num_vectors; cfg_inst->pf_cfg[j].num_vectors = be16_to_cpu(be16); - be32 = cfg_inst->pf_cfg[j].bw; - cfg_inst->pf_cfg[j].bw = be16_to_cpu(be32); + be16 = cfg_inst->pf_cfg[j].bw_min; + cfg_inst->pf_cfg[j].bw_min = be16_to_cpu(be16); + be16 = cfg_inst->pf_cfg[j].bw_max; + cfg_inst->pf_cfg[j].bw_max = be16_to_cpu(be16); } } } @@ -3170,7 +3174,8 @@ bfa_ablk_query(struct bfa_ablk_s *ablk, struct bfa_ablk_cfg_s *ablk_cfg, bfa_status_t bfa_ablk_pf_create(struct bfa_ablk_s *ablk, u16 *pcifn, - u8 port, enum bfi_pcifn_class personality, int bw, + u8 port, enum bfi_pcifn_class personality, + u16 bw_min, u16 bw_max, bfa_ablk_cbfn_t cbfn, void *cbarg) { struct bfi_ablk_h2i_pf_req_s *m; @@ -3194,7 +3199,8 @@ bfa_ablk_pf_create(struct bfa_ablk_s *ablk, u16 *pcifn, bfi_h2i_set(m->mh, BFI_MC_ABLK, BFI_ABLK_H2I_PF_CREATE, bfa_ioc_portid(ablk->ioc)); m->pers = cpu_to_be16((u16)personality); - m->bw = cpu_to_be32(bw); + m->bw_min = cpu_to_be16(bw_min); + m->bw_max = cpu_to_be16(bw_max); m->port = port; bfa_ioc_mbox_queue(ablk->ioc, &ablk->mb); @@ -3294,8 +3300,8 @@ bfa_ablk_port_config(struct bfa_ablk_s *ablk, int port, enum bfa_mode_s mode, } bfa_status_t -bfa_ablk_pf_update(struct bfa_ablk_s *ablk, int pcifn, int bw, - bfa_ablk_cbfn_t cbfn, void *cbarg) +bfa_ablk_pf_update(struct bfa_ablk_s *ablk, int pcifn, u16 bw_min, + u16 bw_max, bfa_ablk_cbfn_t cbfn, void *cbarg) { struct bfi_ablk_h2i_pf_req_s *m; @@ -3317,7 +3323,8 @@ bfa_ablk_pf_update(struct bfa_ablk_s *ablk, int pcifn, int bw, bfi_h2i_set(m->mh, BFI_MC_ABLK, BFI_ABLK_H2I_PF_UPDATE, bfa_ioc_portid(ablk->ioc)); m->pcifn = (u8)pcifn; - m->bw = cpu_to_be32(bw); + m->bw_min = cpu_to_be16(bw_min); + m->bw_max = cpu_to_be16(bw_max); bfa_ioc_mbox_queue(ablk->ioc, &ablk->mb); return BFA_STATUS_OK; @@ -4680,22 +4687,25 @@ diag_tempsensor_comp(struct bfa_diag_s *diag, bfi_diag_ts_rsp_t *rsp) diag->tsensor.temp->temp = be16_to_cpu(rsp->temp); diag->tsensor.temp->ts_junc = rsp->ts_junc; diag->tsensor.temp->ts_brd = rsp->ts_brd; - diag->tsensor.temp->status = BFA_STATUS_OK; if (rsp->ts_brd) { + /* tsensor.temp->status is brd_temp status */ + diag->tsensor.temp->status = rsp->status; if (rsp->status == BFA_STATUS_OK) { diag->tsensor.temp->brd_temp = be16_to_cpu(rsp->brd_temp); - } else { - bfa_trc(diag, rsp->status); + } else diag->tsensor.temp->brd_temp = 0; - diag->tsensor.temp->status = BFA_STATUS_DEVBUSY; - } } + + bfa_trc(diag, rsp->status); bfa_trc(diag, rsp->ts_junc); bfa_trc(diag, rsp->temp); bfa_trc(diag, rsp->ts_brd); bfa_trc(diag, rsp->brd_temp); + + /* tsensor status is always good bcos we always have junction temp */ + diag->tsensor.status = BFA_STATUS_OK; diag->tsensor.cbfn(diag->tsensor.cbarg, diag->tsensor.status); diag->tsensor.lock = 0; } @@ -4924,6 +4934,7 @@ bfa_diag_tsensor_query(struct bfa_diag_s *diag, diag->tsensor.temp = result; diag->tsensor.cbfn = cbfn; diag->tsensor.cbarg = cbarg; + diag->tsensor.status = BFA_STATUS_OK; /* Send msg to fw */ diag_tempsensor_send(diag); @@ -5615,7 +5626,7 @@ bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event) } bfa_sm_set_state(dconf, bfa_dconf_sm_flash_read); bfa_timer_start(dconf->bfa, &dconf->timer, - bfa_dconf_timer, dconf, BFA_DCONF_UPDATE_TOV); + bfa_dconf_timer, dconf, 2 * BFA_DCONF_UPDATE_TOV); bfa_status = bfa_flash_read_part(BFA_FLASH(dconf->bfa), BFA_FLASH_PART_DRV, dconf->instance, dconf->dconf, @@ -5655,7 +5666,7 @@ bfa_dconf_sm_flash_read(struct bfa_dconf_mod_s *dconf, break; case BFA_DCONF_SM_TIMEOUT: bfa_sm_set_state(dconf, bfa_dconf_sm_ready); - bfa_fsm_send_event(&dconf->bfa->iocfc, IOCFC_E_IOC_FAILED); + bfa_ioc_suspend(&dconf->bfa->ioc); break; case BFA_DCONF_SM_EXIT: bfa_timer_stop(&dconf->timer); @@ -5853,7 +5864,6 @@ bfa_dconf_init_cb(void *arg, bfa_status_t status) struct bfa_s *bfa = arg; struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa); - bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP); if (status == BFA_STATUS_OK) { bfa_dconf_read_data_valid(bfa) = BFA_TRUE; if (dconf->dconf->hdr.signature != BFI_DCONF_SIGNATURE) @@ -5861,6 +5871,7 @@ bfa_dconf_init_cb(void *arg, bfa_status_t status) if (dconf->dconf->hdr.version != BFI_DCONF_VERSION) dconf->dconf->hdr.version = BFI_DCONF_VERSION; } + bfa_sm_send_event(dconf, BFA_DCONF_SM_FLASH_COMP); bfa_fsm_send_event(&bfa->iocfc, IOCFC_E_DCONF_DONE); } @@ -5945,3 +5956,448 @@ bfa_dconf_modexit(struct bfa_s *bfa) struct bfa_dconf_mod_s *dconf = BFA_DCONF_MOD(bfa); bfa_sm_send_event(dconf, BFA_DCONF_SM_EXIT); } + +/* + * FRU specific functions + */ + +#define BFA_FRU_DMA_BUF_SZ 0x02000 /* 8k dma buffer */ +#define BFA_FRU_CHINOOK_MAX_SIZE 0x10000 +#define BFA_FRU_LIGHTNING_MAX_SIZE 0x200 + +static void +bfa_fru_notify(void *cbarg, enum bfa_ioc_event_e event) +{ + struct bfa_fru_s *fru = cbarg; + + bfa_trc(fru, event); + + switch (event) { + case BFA_IOC_E_DISABLED: + case BFA_IOC_E_FAILED: + if (fru->op_busy) { + fru->status = BFA_STATUS_IOC_FAILURE; + fru->cbfn(fru->cbarg, fru->status); + fru->op_busy = 0; + } + break; + + default: + break; + } +} + +/* + * Send fru write request. + * + * @param[in] cbarg - callback argument + */ +static void +bfa_fru_write_send(void *cbarg, enum bfi_fru_h2i_msgs msg_type) +{ + struct bfa_fru_s *fru = cbarg; + struct bfi_fru_write_req_s *msg = + (struct bfi_fru_write_req_s *) fru->mb.msg; + u32 len; + + msg->offset = cpu_to_be32(fru->addr_off + fru->offset); + len = (fru->residue < BFA_FRU_DMA_BUF_SZ) ? + fru->residue : BFA_FRU_DMA_BUF_SZ; + msg->length = cpu_to_be32(len); + + /* + * indicate if it's the last msg of the whole write operation + */ + msg->last = (len == fru->residue) ? 1 : 0; + + bfi_h2i_set(msg->mh, BFI_MC_FRU, msg_type, bfa_ioc_portid(fru->ioc)); + bfa_alen_set(&msg->alen, len, fru->dbuf_pa); + + memcpy(fru->dbuf_kva, fru->ubuf + fru->offset, len); + bfa_ioc_mbox_queue(fru->ioc, &fru->mb); + + fru->residue -= len; + fru->offset += len; +} + +/* + * Send fru read request. + * + * @param[in] cbarg - callback argument + */ +static void +bfa_fru_read_send(void *cbarg, enum bfi_fru_h2i_msgs msg_type) +{ + struct bfa_fru_s *fru = cbarg; + struct bfi_fru_read_req_s *msg = + (struct bfi_fru_read_req_s *) fru->mb.msg; + u32 len; + + msg->offset = cpu_to_be32(fru->addr_off + fru->offset); + len = (fru->residue < BFA_FRU_DMA_BUF_SZ) ? + fru->residue : BFA_FRU_DMA_BUF_SZ; + msg->length = cpu_to_be32(len); + bfi_h2i_set(msg->mh, BFI_MC_FRU, msg_type, bfa_ioc_portid(fru->ioc)); + bfa_alen_set(&msg->alen, len, fru->dbuf_pa); + bfa_ioc_mbox_queue(fru->ioc, &fru->mb); +} + +/* + * Flash memory info API. + * + * @param[in] mincfg - minimal cfg variable + */ +u32 +bfa_fru_meminfo(bfa_boolean_t mincfg) +{ + /* min driver doesn't need fru */ + if (mincfg) + return 0; + + return BFA_ROUNDUP(BFA_FRU_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ); +} + +/* + * Flash attach API. + * + * @param[in] fru - fru structure + * @param[in] ioc - ioc structure + * @param[in] dev - device structure + * @param[in] trcmod - trace module + * @param[in] logmod - log module + */ +void +bfa_fru_attach(struct bfa_fru_s *fru, struct bfa_ioc_s *ioc, void *dev, + struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg) +{ + fru->ioc = ioc; + fru->trcmod = trcmod; + fru->cbfn = NULL; + fru->cbarg = NULL; + fru->op_busy = 0; + + bfa_ioc_mbox_regisr(fru->ioc, BFI_MC_FRU, bfa_fru_intr, fru); + bfa_q_qe_init(&fru->ioc_notify); + bfa_ioc_notify_init(&fru->ioc_notify, bfa_fru_notify, fru); + list_add_tail(&fru->ioc_notify.qe, &fru->ioc->notify_q); + + /* min driver doesn't need fru */ + if (mincfg) { + fru->dbuf_kva = NULL; + fru->dbuf_pa = 0; + } +} + +/* + * Claim memory for fru + * + * @param[in] fru - fru structure + * @param[in] dm_kva - pointer to virtual memory address + * @param[in] dm_pa - frusical memory address + * @param[in] mincfg - minimal cfg variable + */ +void +bfa_fru_memclaim(struct bfa_fru_s *fru, u8 *dm_kva, u64 dm_pa, + bfa_boolean_t mincfg) +{ + if (mincfg) + return; + + fru->dbuf_kva = dm_kva; + fru->dbuf_pa = dm_pa; + memset(fru->dbuf_kva, 0, BFA_FRU_DMA_BUF_SZ); + dm_kva += BFA_ROUNDUP(BFA_FRU_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ); + dm_pa += BFA_ROUNDUP(BFA_FRU_DMA_BUF_SZ, BFA_DMA_ALIGN_SZ); +} + +/* + * Update fru vpd image. + * + * @param[in] fru - fru structure + * @param[in] buf - update data buffer + * @param[in] len - data buffer length + * @param[in] offset - offset relative to starting address + * @param[in] cbfn - callback function + * @param[in] cbarg - callback argument + * + * Return status. + */ +bfa_status_t +bfa_fruvpd_update(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset, + bfa_cb_fru_t cbfn, void *cbarg) +{ + bfa_trc(fru, BFI_FRUVPD_H2I_WRITE_REQ); + bfa_trc(fru, len); + bfa_trc(fru, offset); + + if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2) + return BFA_STATUS_FRU_NOT_PRESENT; + + if (fru->ioc->attr->card_type != BFA_MFG_TYPE_CHINOOK) + return BFA_STATUS_CMD_NOTSUPP; + + if (!bfa_ioc_is_operational(fru->ioc)) + return BFA_STATUS_IOC_NON_OP; + + if (fru->op_busy) { + bfa_trc(fru, fru->op_busy); + return BFA_STATUS_DEVBUSY; + } + + fru->op_busy = 1; + + fru->cbfn = cbfn; + fru->cbarg = cbarg; + fru->residue = len; + fru->offset = 0; + fru->addr_off = offset; + fru->ubuf = buf; + + bfa_fru_write_send(fru, BFI_FRUVPD_H2I_WRITE_REQ); + + return BFA_STATUS_OK; +} + +/* + * Read fru vpd image. + * + * @param[in] fru - fru structure + * @param[in] buf - read data buffer + * @param[in] len - data buffer length + * @param[in] offset - offset relative to starting address + * @param[in] cbfn - callback function + * @param[in] cbarg - callback argument + * + * Return status. + */ +bfa_status_t +bfa_fruvpd_read(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset, + bfa_cb_fru_t cbfn, void *cbarg) +{ + bfa_trc(fru, BFI_FRUVPD_H2I_READ_REQ); + bfa_trc(fru, len); + bfa_trc(fru, offset); + + if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2) + return BFA_STATUS_FRU_NOT_PRESENT; + + if (fru->ioc->attr->card_type != BFA_MFG_TYPE_CHINOOK) + return BFA_STATUS_CMD_NOTSUPP; + + if (!bfa_ioc_is_operational(fru->ioc)) + return BFA_STATUS_IOC_NON_OP; + + if (fru->op_busy) { + bfa_trc(fru, fru->op_busy); + return BFA_STATUS_DEVBUSY; + } + + fru->op_busy = 1; + + fru->cbfn = cbfn; + fru->cbarg = cbarg; + fru->residue = len; + fru->offset = 0; + fru->addr_off = offset; + fru->ubuf = buf; + bfa_fru_read_send(fru, BFI_FRUVPD_H2I_READ_REQ); + + return BFA_STATUS_OK; +} + +/* + * Get maximum size fru vpd image. + * + * @param[in] fru - fru structure + * @param[out] size - maximum size of fru vpd data + * + * Return status. + */ +bfa_status_t +bfa_fruvpd_get_max_size(struct bfa_fru_s *fru, u32 *max_size) +{ + if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2) + return BFA_STATUS_FRU_NOT_PRESENT; + + if (!bfa_ioc_is_operational(fru->ioc)) + return BFA_STATUS_IOC_NON_OP; + + if (fru->ioc->attr->card_type == BFA_MFG_TYPE_CHINOOK) + *max_size = BFA_FRU_CHINOOK_MAX_SIZE; + else + return BFA_STATUS_CMD_NOTSUPP; + return BFA_STATUS_OK; +} +/* + * tfru write. + * + * @param[in] fru - fru structure + * @param[in] buf - update data buffer + * @param[in] len - data buffer length + * @param[in] offset - offset relative to starting address + * @param[in] cbfn - callback function + * @param[in] cbarg - callback argument + * + * Return status. + */ +bfa_status_t +bfa_tfru_write(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset, + bfa_cb_fru_t cbfn, void *cbarg) +{ + bfa_trc(fru, BFI_TFRU_H2I_WRITE_REQ); + bfa_trc(fru, len); + bfa_trc(fru, offset); + bfa_trc(fru, *((u8 *) buf)); + + if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2) + return BFA_STATUS_FRU_NOT_PRESENT; + + if (!bfa_ioc_is_operational(fru->ioc)) + return BFA_STATUS_IOC_NON_OP; + + if (fru->op_busy) { + bfa_trc(fru, fru->op_busy); + return BFA_STATUS_DEVBUSY; + } + + fru->op_busy = 1; + + fru->cbfn = cbfn; + fru->cbarg = cbarg; + fru->residue = len; + fru->offset = 0; + fru->addr_off = offset; + fru->ubuf = buf; + + bfa_fru_write_send(fru, BFI_TFRU_H2I_WRITE_REQ); + + return BFA_STATUS_OK; +} + +/* + * tfru read. + * + * @param[in] fru - fru structure + * @param[in] buf - read data buffer + * @param[in] len - data buffer length + * @param[in] offset - offset relative to starting address + * @param[in] cbfn - callback function + * @param[in] cbarg - callback argument + * + * Return status. + */ +bfa_status_t +bfa_tfru_read(struct bfa_fru_s *fru, void *buf, u32 len, u32 offset, + bfa_cb_fru_t cbfn, void *cbarg) +{ + bfa_trc(fru, BFI_TFRU_H2I_READ_REQ); + bfa_trc(fru, len); + bfa_trc(fru, offset); + + if (fru->ioc->asic_gen != BFI_ASIC_GEN_CT2) + return BFA_STATUS_FRU_NOT_PRESENT; + + if (!bfa_ioc_is_operational(fru->ioc)) + return BFA_STATUS_IOC_NON_OP; + + if (fru->op_busy) { + bfa_trc(fru, fru->op_busy); + return BFA_STATUS_DEVBUSY; + } + + fru->op_busy = 1; + + fru->cbfn = cbfn; + fru->cbarg = cbarg; + fru->residue = len; + fru->offset = 0; + fru->addr_off = offset; + fru->ubuf = buf; + bfa_fru_read_send(fru, BFI_TFRU_H2I_READ_REQ); + + return BFA_STATUS_OK; +} + +/* + * Process fru response messages upon receiving interrupts. + * + * @param[in] fruarg - fru structure + * @param[in] msg - message structure + */ +void +bfa_fru_intr(void *fruarg, struct bfi_mbmsg_s *msg) +{ + struct bfa_fru_s *fru = fruarg; + struct bfi_fru_rsp_s *rsp = (struct bfi_fru_rsp_s *)msg; + u32 status; + + bfa_trc(fru, msg->mh.msg_id); + + if (!fru->op_busy) { + /* + * receiving response after ioc failure + */ + bfa_trc(fru, 0x9999); + return; + } + + switch (msg->mh.msg_id) { + case BFI_FRUVPD_I2H_WRITE_RSP: + case BFI_TFRU_I2H_WRITE_RSP: + status = be32_to_cpu(rsp->status); + bfa_trc(fru, status); + + if (status != BFA_STATUS_OK || fru->residue == 0) { + fru->status = status; + fru->op_busy = 0; + if (fru->cbfn) + fru->cbfn(fru->cbarg, fru->status); + } else { + bfa_trc(fru, fru->offset); + if (msg->mh.msg_id == BFI_FRUVPD_I2H_WRITE_RSP) + bfa_fru_write_send(fru, + BFI_FRUVPD_H2I_WRITE_REQ); + else + bfa_fru_write_send(fru, + BFI_TFRU_H2I_WRITE_REQ); + } + break; + case BFI_FRUVPD_I2H_READ_RSP: + case BFI_TFRU_I2H_READ_RSP: + status = be32_to_cpu(rsp->status); + bfa_trc(fru, status); + + if (status != BFA_STATUS_OK) { + fru->status = status; + fru->op_busy = 0; + if (fru->cbfn) + fru->cbfn(fru->cbarg, fru->status); + } else { + u32 len = be32_to_cpu(rsp->length); + + bfa_trc(fru, fru->offset); + bfa_trc(fru, len); + + memcpy(fru->ubuf + fru->offset, fru->dbuf_kva, len); + fru->residue -= len; + fru->offset += len; + + if (fru->residue == 0) { + fru->status = status; + fru->op_busy = 0; + if (fru->cbfn) + fru->cbfn(fru->cbarg, fru->status); + } else { + if (msg->mh.msg_id == BFI_FRUVPD_I2H_READ_RSP) + bfa_fru_read_send(fru, + BFI_FRUVPD_H2I_READ_REQ); + else + bfa_fru_read_send(fru, + BFI_TFRU_H2I_READ_REQ); + } + } + break; + default: + WARN_ON(1); + } +} diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h index b2856f96567..23a90e7b710 100644 --- a/drivers/scsi/bfa/bfa_ioc.h +++ b/drivers/scsi/bfa/bfa_ioc.h @@ -702,6 +702,55 @@ void bfa_phy_memclaim(struct bfa_phy_s *phy, void bfa_phy_intr(void *phyarg, struct bfi_mbmsg_s *msg); /* + * FRU module specific + */ +typedef void (*bfa_cb_fru_t) (void *cbarg, bfa_status_t status); + +struct bfa_fru_s { + struct bfa_ioc_s *ioc; /* back pointer to ioc */ + struct bfa_trc_mod_s *trcmod; /* trace module */ + u8 op_busy; /* operation busy flag */ + u8 rsv[3]; + u32 residue; /* residual length */ + u32 offset; /* offset */ + bfa_status_t status; /* status */ + u8 *dbuf_kva; /* dma buf virtual address */ + u64 dbuf_pa; /* dma buf physical address */ + struct bfa_reqq_wait_s reqq_wait; /* to wait for room in reqq */ + bfa_cb_fru_t cbfn; /* user callback function */ + void *cbarg; /* user callback arg */ + u8 *ubuf; /* user supplied buffer */ + struct bfa_cb_qe_s hcb_qe; /* comp: BFA callback qelem */ + u32 addr_off; /* fru address offset */ + struct bfa_mbox_cmd_s mb; /* mailbox */ + struct bfa_ioc_notify_s ioc_notify; /* ioc event notify */ + struct bfa_mem_dma_s fru_dma; +}; + +#define BFA_FRU(__bfa) (&(__bfa)->modules.fru) +#define BFA_MEM_FRU_DMA(__bfa) (&(BFA_FRU(__bfa)->fru_dma)) + +bfa_status_t bfa_fruvpd_update(struct bfa_fru_s *fru, + void *buf, u32 len, u32 offset, + bfa_cb_fru_t cbfn, void *cbarg); +bfa_status_t bfa_fruvpd_read(struct bfa_fru_s *fru, + void *buf, u32 len, u32 offset, + bfa_cb_fru_t cbfn, void *cbarg); +bfa_status_t bfa_fruvpd_get_max_size(struct bfa_fru_s *fru, u32 *max_size); +bfa_status_t bfa_tfru_write(struct bfa_fru_s *fru, + void *buf, u32 len, u32 offset, + bfa_cb_fru_t cbfn, void *cbarg); +bfa_status_t bfa_tfru_read(struct bfa_fru_s *fru, + void *buf, u32 len, u32 offset, + bfa_cb_fru_t cbfn, void *cbarg); +u32 bfa_fru_meminfo(bfa_boolean_t mincfg); +void bfa_fru_attach(struct bfa_fru_s *fru, struct bfa_ioc_s *ioc, + void *dev, struct bfa_trc_mod_s *trcmod, bfa_boolean_t mincfg); +void bfa_fru_memclaim(struct bfa_fru_s *fru, + u8 *dm_kva, u64 dm_pa, bfa_boolean_t mincfg); +void bfa_fru_intr(void *fruarg, struct bfi_mbmsg_s *msg); + +/* * Driver Config( dconf) specific */ #define BFI_DCONF_SIGNATURE 0xabcdabcd @@ -716,6 +765,7 @@ struct bfa_dconf_hdr_s { struct bfa_dconf_s { struct bfa_dconf_hdr_s hdr; struct bfa_lunmask_cfg_s lun_mask; + struct bfa_throttle_cfg_s throttle_cfg; }; #pragma pack() @@ -738,6 +788,8 @@ struct bfa_dconf_mod_s { #define bfa_dconf_read_data_valid(__bfa) \ (BFA_DCONF_MOD(__bfa)->read_data_valid) #define BFA_DCONF_UPDATE_TOV 5000 /* memtest timeout in msec */ +#define bfa_dconf_get_min_cfg(__bfa) \ + (BFA_DCONF_MOD(__bfa)->min_cfg) void bfa_dconf_modinit(struct bfa_s *bfa); void bfa_dconf_modexit(struct bfa_s *bfa); @@ -761,7 +813,8 @@ bfa_status_t bfa_dconf_update(struct bfa_s *bfa); #define bfa_ioc_maxfrsize(__ioc) ((__ioc)->attr->maxfrsize) #define bfa_ioc_rx_bbcredit(__ioc) ((__ioc)->attr->rx_bbcredit) #define bfa_ioc_speed_sup(__ioc) \ - BFI_ADAPTER_GETP(SPEED, (__ioc)->attr->adapter_prop) + ((bfa_ioc_is_cna(__ioc)) ? BFA_PORT_SPEED_10GBPS : \ + BFI_ADAPTER_GETP(SPEED, (__ioc)->attr->adapter_prop)) #define bfa_ioc_get_nports(__ioc) \ BFI_ADAPTER_GETP(NPORTS, (__ioc)->attr->adapter_prop) @@ -885,12 +938,12 @@ bfa_status_t bfa_ablk_port_config(struct bfa_ablk_s *ablk, int port, enum bfa_mode_s mode, int max_pf, int max_vf, bfa_ablk_cbfn_t cbfn, void *cbarg); bfa_status_t bfa_ablk_pf_create(struct bfa_ablk_s *ablk, u16 *pcifn, - u8 port, enum bfi_pcifn_class personality, int bw, - bfa_ablk_cbfn_t cbfn, void *cbarg); + u8 port, enum bfi_pcifn_class personality, + u16 bw_min, u16 bw_max, bfa_ablk_cbfn_t cbfn, void *cbarg); bfa_status_t bfa_ablk_pf_delete(struct bfa_ablk_s *ablk, int pcifn, bfa_ablk_cbfn_t cbfn, void *cbarg); -bfa_status_t bfa_ablk_pf_update(struct bfa_ablk_s *ablk, int pcifn, int bw, - bfa_ablk_cbfn_t cbfn, void *cbarg); +bfa_status_t bfa_ablk_pf_update(struct bfa_ablk_s *ablk, int pcifn, + u16 bw_min, u16 bw_max, bfa_ablk_cbfn_t cbfn, void *cbarg); bfa_status_t bfa_ablk_optrom_en(struct bfa_ablk_s *ablk, bfa_ablk_cbfn_t cbfn, void *cbarg); bfa_status_t bfa_ablk_optrom_dis(struct bfa_ablk_s *ablk, diff --git a/drivers/scsi/bfa/bfa_ioc_ct.c b/drivers/scsi/bfa/bfa_ioc_ct.c index 2eb0c6a2938..de4e726a126 100644 --- a/drivers/scsi/bfa/bfa_ioc_ct.c +++ b/drivers/scsi/bfa/bfa_ioc_ct.c @@ -57,13 +57,6 @@ bfa_ioc_ct_firmware_lock(struct bfa_ioc_s *ioc) u32 usecnt; struct bfi_ioc_image_hdr_s fwhdr; - /* - * If bios boot (flash based) -- do not increment usage count - */ - if (bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc)) < - BFA_IOC_FWIMG_MINSZ) - return BFA_TRUE; - bfa_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg); usecnt = readl(ioc->ioc_regs.ioc_usage_reg); @@ -115,13 +108,6 @@ bfa_ioc_ct_firmware_unlock(struct bfa_ioc_s *ioc) u32 usecnt; /* - * If bios boot (flash based) -- do not decrement usage count - */ - if (bfa_cb_image_get_size(bfa_ioc_asic_gen(ioc)) < - BFA_IOC_FWIMG_MINSZ) - return; - - /* * decrement usage count */ bfa_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg); @@ -400,13 +386,12 @@ static void bfa_ioc_ct_ownership_reset(struct bfa_ioc_s *ioc) { - if (bfa_ioc_is_cna(ioc)) { - bfa_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg); - writel(0, ioc->ioc_regs.ioc_usage_reg); - readl(ioc->ioc_regs.ioc_usage_sem_reg); - writel(1, ioc->ioc_regs.ioc_usage_sem_reg); - } + bfa_ioc_sem_get(ioc->ioc_regs.ioc_usage_sem_reg); + writel(0, ioc->ioc_regs.ioc_usage_reg); + readl(ioc->ioc_regs.ioc_usage_sem_reg); + writel(1, ioc->ioc_regs.ioc_usage_sem_reg); + writel(0, ioc->ioc_regs.ioc_fail_sync); /* * Read the hw sem reg to make sure that it is locked * before we clear it. If it is not locked, writing 1 @@ -759,25 +744,6 @@ bfa_ioc_ct2_mem_init(void __iomem *rb) void bfa_ioc_ct2_mac_reset(void __iomem *rb) { - u32 r32; - - bfa_ioc_ct2_sclk_init(rb); - bfa_ioc_ct2_lclk_init(rb); - - /* - * release soft reset on s_clk & l_clk - */ - r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG)); - writel(r32 & ~__APP_PLL_SCLK_LOGIC_SOFT_RESET, - (rb + CT2_APP_PLL_SCLK_CTL_REG)); - - /* - * release soft reset on s_clk & l_clk - */ - r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG)); - writel(r32 & ~__APP_PLL_LCLK_LOGIC_SOFT_RESET, - (rb + CT2_APP_PLL_LCLK_CTL_REG)); - /* put port0, port1 MAC & AHB in reset */ writel((__CSI_MAC_RESET | __CSI_MAC_AHB_RESET), rb + CT2_CSI_MAC_CONTROL_REG(0)); @@ -785,8 +751,21 @@ bfa_ioc_ct2_mac_reset(void __iomem *rb) rb + CT2_CSI_MAC_CONTROL_REG(1)); } +static void +bfa_ioc_ct2_enable_flash(void __iomem *rb) +{ + u32 r32; + + r32 = readl((rb + PSS_GPIO_OUT_REG)); + writel(r32 & ~1, (rb + PSS_GPIO_OUT_REG)); + r32 = readl((rb + PSS_GPIO_OE_REG)); + writel(r32 | 1, (rb + PSS_GPIO_OE_REG)); +} + #define CT2_NFC_MAX_DELAY 1000 -#define CT2_NFC_VER_VALID 0x143 +#define CT2_NFC_PAUSE_MAX_DELAY 4000 +#define CT2_NFC_VER_VALID 0x147 +#define CT2_NFC_STATE_RUNNING 0x20000001 #define BFA_IOC_PLL_POLL 1000000 static bfa_boolean_t @@ -802,6 +781,20 @@ bfa_ioc_ct2_nfc_halted(void __iomem *rb) } static void +bfa_ioc_ct2_nfc_halt(void __iomem *rb) +{ + int i; + + writel(__HALT_NFC_CONTROLLER, rb + CT2_NFC_CSR_SET_REG); + for (i = 0; i < CT2_NFC_MAX_DELAY; i++) { + if (bfa_ioc_ct2_nfc_halted(rb)) + break; + udelay(1000); + } + WARN_ON(!bfa_ioc_ct2_nfc_halted(rb)); +} + +static void bfa_ioc_ct2_nfc_resume(void __iomem *rb) { u32 r32; @@ -817,105 +810,142 @@ bfa_ioc_ct2_nfc_resume(void __iomem *rb) WARN_ON(1); } -bfa_status_t -bfa_ioc_ct2_pll_init(void __iomem *rb, enum bfi_asic_mode mode) +static void +bfa_ioc_ct2_clk_reset(void __iomem *rb) { - u32 wgn, r32, nfc_ver, i; + u32 r32; - wgn = readl(rb + CT2_WGN_STATUS); - nfc_ver = readl(rb + CT2_RSC_GPR15_REG); + bfa_ioc_ct2_sclk_init(rb); + bfa_ioc_ct2_lclk_init(rb); - if ((wgn == (__A2T_AHB_LOAD | __WGN_READY)) && - (nfc_ver >= CT2_NFC_VER_VALID)) { - if (bfa_ioc_ct2_nfc_halted(rb)) - bfa_ioc_ct2_nfc_resume(rb); + /* + * release soft reset on s_clk & l_clk + */ + r32 = readl((rb + CT2_APP_PLL_SCLK_CTL_REG)); + writel(r32 & ~__APP_PLL_SCLK_LOGIC_SOFT_RESET, + (rb + CT2_APP_PLL_SCLK_CTL_REG)); - writel(__RESET_AND_START_SCLK_LCLK_PLLS, - rb + CT2_CSI_FW_CTL_SET_REG); + r32 = readl((rb + CT2_APP_PLL_LCLK_CTL_REG)); + writel(r32 & ~__APP_PLL_LCLK_LOGIC_SOFT_RESET, + (rb + CT2_APP_PLL_LCLK_CTL_REG)); - for (i = 0; i < BFA_IOC_PLL_POLL; i++) { - r32 = readl(rb + CT2_APP_PLL_LCLK_CTL_REG); - if (r32 & __RESET_AND_START_SCLK_LCLK_PLLS) - break; - } +} - WARN_ON(!(r32 & __RESET_AND_START_SCLK_LCLK_PLLS)); +static void +bfa_ioc_ct2_nfc_clk_reset(void __iomem *rb) +{ + u32 r32, i; - for (i = 0; i < BFA_IOC_PLL_POLL; i++) { - r32 = readl(rb + CT2_APP_PLL_LCLK_CTL_REG); - if (!(r32 & __RESET_AND_START_SCLK_LCLK_PLLS)) - break; - } + r32 = readl((rb + PSS_CTL_REG)); + r32 |= (__PSS_LPU0_RESET | __PSS_LPU1_RESET); + writel(r32, (rb + PSS_CTL_REG)); + + writel(__RESET_AND_START_SCLK_LCLK_PLLS, rb + CT2_CSI_FW_CTL_SET_REG); - WARN_ON(r32 & __RESET_AND_START_SCLK_LCLK_PLLS); + for (i = 0; i < BFA_IOC_PLL_POLL; i++) { + r32 = readl(rb + CT2_NFC_FLASH_STS_REG); + + if ((r32 & __FLASH_PLL_INIT_AND_RESET_IN_PROGRESS)) + break; + } + WARN_ON(!(r32 & __FLASH_PLL_INIT_AND_RESET_IN_PROGRESS)); + + for (i = 0; i < BFA_IOC_PLL_POLL; i++) { + r32 = readl(rb + CT2_NFC_FLASH_STS_REG); + + if (!(r32 & __FLASH_PLL_INIT_AND_RESET_IN_PROGRESS)) + break; + } + WARN_ON((r32 & __FLASH_PLL_INIT_AND_RESET_IN_PROGRESS)); + + r32 = readl(rb + CT2_CSI_FW_CTL_REG); + WARN_ON((r32 & __RESET_AND_START_SCLK_LCLK_PLLS)); +} + +static void +bfa_ioc_ct2_wait_till_nfc_running(void __iomem *rb) +{ + u32 r32; + int i; + + if (bfa_ioc_ct2_nfc_halted(rb)) + bfa_ioc_ct2_nfc_resume(rb); + for (i = 0; i < CT2_NFC_PAUSE_MAX_DELAY; i++) { + r32 = readl(rb + CT2_NFC_STS_REG); + if (r32 == CT2_NFC_STATE_RUNNING) + return; udelay(1000); + } - r32 = readl(rb + CT2_CSI_FW_CTL_REG); - WARN_ON(r32 & __RESET_AND_START_SCLK_LCLK_PLLS); - } else { - writel(__HALT_NFC_CONTROLLER, rb + CT2_NFC_CSR_SET_REG); - for (i = 0; i < CT2_NFC_MAX_DELAY; i++) { - r32 = readl(rb + CT2_NFC_CSR_SET_REG); - if (r32 & __NFC_CONTROLLER_HALTED) - break; - udelay(1000); - } + r32 = readl(rb + CT2_NFC_STS_REG); + WARN_ON(!(r32 == CT2_NFC_STATE_RUNNING)); +} - bfa_ioc_ct2_mac_reset(rb); - bfa_ioc_ct2_sclk_init(rb); - bfa_ioc_ct2_lclk_init(rb); +bfa_status_t +bfa_ioc_ct2_pll_init(void __iomem *rb, enum bfi_asic_mode mode) +{ + u32 wgn, r32, nfc_ver; - /* - * release soft reset on s_clk & l_clk - */ - r32 = readl(rb + CT2_APP_PLL_SCLK_CTL_REG); - writel(r32 & ~__APP_PLL_SCLK_LOGIC_SOFT_RESET, - (rb + CT2_APP_PLL_SCLK_CTL_REG)); + wgn = readl(rb + CT2_WGN_STATUS); + if (wgn == (__WGN_READY | __GLBL_PF_VF_CFG_RDY)) { /* - * release soft reset on s_clk & l_clk + * If flash is corrupted, enable flash explicitly */ - r32 = readl(rb + CT2_APP_PLL_LCLK_CTL_REG); - writel(r32 & ~__APP_PLL_LCLK_LOGIC_SOFT_RESET, - (rb + CT2_APP_PLL_LCLK_CTL_REG)); - } + bfa_ioc_ct2_clk_reset(rb); + bfa_ioc_ct2_enable_flash(rb); - /* - * Announce flash device presence, if flash was corrupted. - */ - if (wgn == (__WGN_READY | __GLBL_PF_VF_CFG_RDY)) { - r32 = readl(rb + PSS_GPIO_OUT_REG); - writel(r32 & ~1, (rb + PSS_GPIO_OUT_REG)); - r32 = readl(rb + PSS_GPIO_OE_REG); - writel(r32 | 1, (rb + PSS_GPIO_OE_REG)); + bfa_ioc_ct2_mac_reset(rb); + + bfa_ioc_ct2_clk_reset(rb); + bfa_ioc_ct2_enable_flash(rb); + + } else { + nfc_ver = readl(rb + CT2_RSC_GPR15_REG); + + if ((nfc_ver >= CT2_NFC_VER_VALID) && + (wgn == (__A2T_AHB_LOAD | __WGN_READY))) { + + bfa_ioc_ct2_wait_till_nfc_running(rb); + + bfa_ioc_ct2_nfc_clk_reset(rb); + } else { + bfa_ioc_ct2_nfc_halt(rb); + + bfa_ioc_ct2_clk_reset(rb); + bfa_ioc_ct2_mac_reset(rb); + bfa_ioc_ct2_clk_reset(rb); + + } } /* * Mask the interrupts and clear any - * pending interrupts. + * pending interrupts left by BIOS/EFI */ + writel(1, (rb + CT2_LPU0_HOSTFN_MBOX0_MSK)); writel(1, (rb + CT2_LPU1_HOSTFN_MBOX0_MSK)); /* For first time initialization, no need to clear interrupts */ r32 = readl(rb + HOST_SEM5_REG); if (r32 & 0x1) { - r32 = readl(rb + CT2_LPU0_HOSTFN_CMD_STAT); + r32 = readl((rb + CT2_LPU0_HOSTFN_CMD_STAT)); if (r32 == 1) { - writel(1, rb + CT2_LPU0_HOSTFN_CMD_STAT); + writel(1, (rb + CT2_LPU0_HOSTFN_CMD_STAT)); readl((rb + CT2_LPU0_HOSTFN_CMD_STAT)); } - r32 = readl(rb + CT2_LPU1_HOSTFN_CMD_STAT); + r32 = readl((rb + CT2_LPU1_HOSTFN_CMD_STAT)); if (r32 == 1) { - writel(1, rb + CT2_LPU1_HOSTFN_CMD_STAT); - readl(rb + CT2_LPU1_HOSTFN_CMD_STAT); + writel(1, (rb + CT2_LPU1_HOSTFN_CMD_STAT)); + readl((rb + CT2_LPU1_HOSTFN_CMD_STAT)); } } bfa_ioc_ct2_mem_init(rb); - writel(BFI_IOC_UNINIT, rb + CT2_BFA_IOC0_STATE_REG); - writel(BFI_IOC_UNINIT, rb + CT2_BFA_IOC1_STATE_REG); + writel(BFI_IOC_UNINIT, (rb + CT2_BFA_IOC0_STATE_REG)); + writel(BFI_IOC_UNINIT, (rb + CT2_BFA_IOC1_STATE_REG)); return BFA_STATUS_OK; } diff --git a/drivers/scsi/bfa/bfa_modules.h b/drivers/scsi/bfa/bfa_modules.h index 189fff71e3c..a14c784ff3f 100644 --- a/drivers/scsi/bfa/bfa_modules.h +++ b/drivers/scsi/bfa/bfa_modules.h @@ -45,6 +45,7 @@ struct bfa_modules_s { struct bfa_diag_s diag_mod; /* diagnostics module */ struct bfa_phy_s phy; /* phy module */ struct bfa_dconf_mod_s dconf_mod; /* DCONF common module */ + struct bfa_fru_s fru; /* fru module */ }; /* diff --git a/drivers/scsi/bfa/bfa_port.c b/drivers/scsi/bfa/bfa_port.c index 95e4ad8759a..8ea7697deb9 100644 --- a/drivers/scsi/bfa/bfa_port.c +++ b/drivers/scsi/bfa/bfa_port.c @@ -250,6 +250,12 @@ bfa_port_enable(struct bfa_port_s *port, bfa_port_endis_cbfn_t cbfn, return BFA_STATUS_IOC_FAILURE; } + /* if port is d-port enabled, return error */ + if (port->dport_enabled) { + bfa_trc(port, BFA_STATUS_DPORT_ERR); + return BFA_STATUS_DPORT_ERR; + } + if (port->endis_pending) { bfa_trc(port, BFA_STATUS_DEVBUSY); return BFA_STATUS_DEVBUSY; @@ -300,6 +306,12 @@ bfa_port_disable(struct bfa_port_s *port, bfa_port_endis_cbfn_t cbfn, return BFA_STATUS_IOC_FAILURE; } + /* if port is d-port enabled, return error */ + if (port->dport_enabled) { + bfa_trc(port, BFA_STATUS_DPORT_ERR); + return BFA_STATUS_DPORT_ERR; + } + if (port->endis_pending) { bfa_trc(port, BFA_STATUS_DEVBUSY); return BFA_STATUS_DEVBUSY; @@ -431,6 +443,10 @@ bfa_port_notify(void *arg, enum bfa_ioc_event_e event) port->endis_cbfn = NULL; port->endis_pending = BFA_FALSE; } + + /* clear D-port mode */ + if (port->dport_enabled) + bfa_port_set_dportenabled(port, BFA_FALSE); break; default: break; @@ -467,6 +483,7 @@ bfa_port_attach(struct bfa_port_s *port, struct bfa_ioc_s *ioc, port->stats_cbfn = NULL; port->endis_cbfn = NULL; port->pbc_disabled = BFA_FALSE; + port->dport_enabled = BFA_FALSE; bfa_ioc_mbox_regisr(port->ioc, BFI_MC_PORT, bfa_port_isr, port); bfa_q_qe_init(&port->ioc_notify); @@ -483,6 +500,21 @@ bfa_port_attach(struct bfa_port_s *port, struct bfa_ioc_s *ioc, } /* + * bfa_port_set_dportenabled(); + * + * Port module- set pbc disabled flag + * + * @param[in] port - Pointer to the Port module data structure + * + * @return void + */ +void +bfa_port_set_dportenabled(struct bfa_port_s *port, bfa_boolean_t enabled) +{ + port->dport_enabled = enabled; +} + +/* * CEE module specific definitions */ diff --git a/drivers/scsi/bfa/bfa_port.h b/drivers/scsi/bfa/bfa_port.h index 947f897328d..2fcab6bc628 100644 --- a/drivers/scsi/bfa/bfa_port.h +++ b/drivers/scsi/bfa/bfa_port.h @@ -45,6 +45,7 @@ struct bfa_port_s { bfa_status_t endis_status; struct bfa_ioc_notify_s ioc_notify; bfa_boolean_t pbc_disabled; + bfa_boolean_t dport_enabled; struct bfa_mem_dma_s port_dma; }; @@ -66,6 +67,8 @@ bfa_status_t bfa_port_disable(struct bfa_port_s *port, u32 bfa_port_meminfo(void); void bfa_port_mem_claim(struct bfa_port_s *port, u8 *dma_kva, u64 dma_pa); +void bfa_port_set_dportenabled(struct bfa_port_s *port, + bfa_boolean_t enabled); /* * CEE declaration diff --git a/drivers/scsi/bfa/bfa_svc.c b/drivers/scsi/bfa/bfa_svc.c index b2538d60db3..299c1c889b3 100644 --- a/drivers/scsi/bfa/bfa_svc.c +++ b/drivers/scsi/bfa/bfa_svc.c @@ -67,6 +67,9 @@ enum bfa_fcport_sm_event { BFA_FCPORT_SM_LINKDOWN = 7, /* firmware linkup down */ BFA_FCPORT_SM_QRESUME = 8, /* CQ space available */ BFA_FCPORT_SM_HWFAIL = 9, /* IOC h/w failure */ + BFA_FCPORT_SM_DPORTENABLE = 10, /* enable dport */ + BFA_FCPORT_SM_DPORTDISABLE = 11,/* disable dport */ + BFA_FCPORT_SM_FAA_MISCONFIG = 12, /* FAA misconfiguratin */ }; /* @@ -197,6 +200,10 @@ static void bfa_fcport_sm_iocdown(struct bfa_fcport_s *fcport, enum bfa_fcport_sm_event event); static void bfa_fcport_sm_iocfail(struct bfa_fcport_s *fcport, enum bfa_fcport_sm_event event); +static void bfa_fcport_sm_dport(struct bfa_fcport_s *fcport, + enum bfa_fcport_sm_event event); +static void bfa_fcport_sm_faa_misconfig(struct bfa_fcport_s *fcport, + enum bfa_fcport_sm_event event); static void bfa_fcport_ln_sm_dn(struct bfa_fcport_ln_s *ln, enum bfa_fcport_ln_sm_event event); @@ -226,6 +233,8 @@ static struct bfa_sm_table_s hal_port_sm_table[] = { {BFA_SM(bfa_fcport_sm_stopped), BFA_PORT_ST_STOPPED}, {BFA_SM(bfa_fcport_sm_iocdown), BFA_PORT_ST_IOCDOWN}, {BFA_SM(bfa_fcport_sm_iocfail), BFA_PORT_ST_IOCDOWN}, + {BFA_SM(bfa_fcport_sm_dport), BFA_PORT_ST_DPORT}, + {BFA_SM(bfa_fcport_sm_faa_misconfig), BFA_PORT_ST_FAA_MISCONFIG}, }; @@ -1244,6 +1253,12 @@ bfa_lps_sm_init(struct bfa_lps_s *lps, enum bfa_lps_event event) * Just ignore */ break; + case BFA_LPS_SM_SET_N2N_PID: + /* + * When topology is set to loop, bfa_lps_set_n2n_pid() sends + * this event. Ignore this event. + */ + break; default: bfa_sm_fault(lps->bfa, event); @@ -1261,6 +1276,7 @@ bfa_lps_sm_login(struct bfa_lps_s *lps, enum bfa_lps_event event) switch (event) { case BFA_LPS_SM_FWRSP: + case BFA_LPS_SM_OFFLINE: if (lps->status == BFA_STATUS_OK) { bfa_sm_set_state(lps, bfa_lps_sm_online); if (lps->fdisc) @@ -1289,7 +1305,6 @@ bfa_lps_sm_login(struct bfa_lps_s *lps, enum bfa_lps_event event) bfa_lps_login_comp(lps); break; - case BFA_LPS_SM_OFFLINE: case BFA_LPS_SM_DELETE: bfa_sm_set_state(lps, bfa_lps_sm_init); break; @@ -2169,6 +2184,12 @@ bfa_fcport_sm_enabling_qwait(struct bfa_fcport_s *fcport, bfa_sm_set_state(fcport, bfa_fcport_sm_iocdown); break; + case BFA_FCPORT_SM_FAA_MISCONFIG: + bfa_fcport_reset_linkinfo(fcport); + bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT); + bfa_sm_set_state(fcport, bfa_fcport_sm_faa_misconfig); + break; + default: bfa_sm_fault(fcport->bfa, event); } @@ -2225,6 +2246,12 @@ bfa_fcport_sm_enabling(struct bfa_fcport_s *fcport, bfa_sm_set_state(fcport, bfa_fcport_sm_iocdown); break; + case BFA_FCPORT_SM_FAA_MISCONFIG: + bfa_fcport_reset_linkinfo(fcport); + bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT); + bfa_sm_set_state(fcport, bfa_fcport_sm_faa_misconfig); + break; + default: bfa_sm_fault(fcport->bfa, event); } @@ -2250,11 +2277,11 @@ bfa_fcport_sm_linkdown(struct bfa_fcport_s *fcport, if (!bfa_ioc_get_fcmode(&fcport->bfa->ioc)) { bfa_trc(fcport->bfa, - pevent->link_state.vc_fcf.fcf.fipenabled); + pevent->link_state.attr.vc_fcf.fcf.fipenabled); bfa_trc(fcport->bfa, - pevent->link_state.vc_fcf.fcf.fipfailed); + pevent->link_state.attr.vc_fcf.fcf.fipfailed); - if (pevent->link_state.vc_fcf.fcf.fipfailed) + if (pevent->link_state.attr.vc_fcf.fcf.fipfailed) bfa_plog_str(fcport->bfa->plog, BFA_PL_MID_HAL, BFA_PL_EID_FIP_FCF_DISC, 0, "FIP FCF Discovery Failed"); @@ -2311,6 +2338,12 @@ bfa_fcport_sm_linkdown(struct bfa_fcport_s *fcport, bfa_sm_set_state(fcport, bfa_fcport_sm_iocdown); break; + case BFA_FCPORT_SM_FAA_MISCONFIG: + bfa_fcport_reset_linkinfo(fcport); + bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT); + bfa_sm_set_state(fcport, bfa_fcport_sm_faa_misconfig); + break; + default: bfa_sm_fault(fcport->bfa, event); } @@ -2404,6 +2437,12 @@ bfa_fcport_sm_linkup(struct bfa_fcport_s *fcport, } break; + case BFA_FCPORT_SM_FAA_MISCONFIG: + bfa_fcport_reset_linkinfo(fcport); + bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT); + bfa_sm_set_state(fcport, bfa_fcport_sm_faa_misconfig); + break; + default: bfa_sm_fault(fcport->bfa, event); } @@ -2449,6 +2488,12 @@ bfa_fcport_sm_disabling_qwait(struct bfa_fcport_s *fcport, bfa_reqq_wcancel(&fcport->reqq_wait); break; + case BFA_FCPORT_SM_FAA_MISCONFIG: + bfa_fcport_reset_linkinfo(fcport); + bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISCONNECT); + bfa_sm_set_state(fcport, bfa_fcport_sm_faa_misconfig); + break; + default: bfa_sm_fault(fcport->bfa, event); } @@ -2600,6 +2645,10 @@ bfa_fcport_sm_disabled(struct bfa_fcport_s *fcport, bfa_sm_set_state(fcport, bfa_fcport_sm_iocfail); break; + case BFA_FCPORT_SM_DPORTENABLE: + bfa_sm_set_state(fcport, bfa_fcport_sm_dport); + break; + default: bfa_sm_fault(fcport->bfa, event); } @@ -2680,6 +2729,81 @@ bfa_fcport_sm_iocfail(struct bfa_fcport_s *fcport, } } +static void +bfa_fcport_sm_dport(struct bfa_fcport_s *fcport, enum bfa_fcport_sm_event event) +{ + bfa_trc(fcport->bfa, event); + + switch (event) { + case BFA_FCPORT_SM_DPORTENABLE: + case BFA_FCPORT_SM_DISABLE: + case BFA_FCPORT_SM_ENABLE: + case BFA_FCPORT_SM_START: + /* + * Ignore event for a port that is dport + */ + break; + + case BFA_FCPORT_SM_STOP: + bfa_sm_set_state(fcport, bfa_fcport_sm_stopped); + break; + + case BFA_FCPORT_SM_HWFAIL: + bfa_sm_set_state(fcport, bfa_fcport_sm_iocfail); + break; + + case BFA_FCPORT_SM_DPORTDISABLE: + bfa_sm_set_state(fcport, bfa_fcport_sm_disabled); + break; + + default: + bfa_sm_fault(fcport->bfa, event); + } +} + +static void +bfa_fcport_sm_faa_misconfig(struct bfa_fcport_s *fcport, + enum bfa_fcport_sm_event event) +{ + bfa_trc(fcport->bfa, event); + + switch (event) { + case BFA_FCPORT_SM_DPORTENABLE: + case BFA_FCPORT_SM_ENABLE: + case BFA_FCPORT_SM_START: + /* + * Ignore event for a port as there is FAA misconfig + */ + break; + + case BFA_FCPORT_SM_DISABLE: + if (bfa_fcport_send_disable(fcport)) + bfa_sm_set_state(fcport, bfa_fcport_sm_disabling); + else + bfa_sm_set_state(fcport, bfa_fcport_sm_disabling_qwait); + + bfa_fcport_reset_linkinfo(fcport); + bfa_fcport_scn(fcport, BFA_PORT_LINKDOWN, BFA_FALSE); + bfa_plog_str(fcport->bfa->plog, BFA_PL_MID_HAL, + BFA_PL_EID_PORT_DISABLE, 0, "Port Disable"); + bfa_fcport_aen_post(fcport, BFA_PORT_AEN_DISABLE); + break; + + case BFA_FCPORT_SM_STOP: + bfa_sm_set_state(fcport, bfa_fcport_sm_stopped); + break; + + case BFA_FCPORT_SM_HWFAIL: + bfa_fcport_reset_linkinfo(fcport); + bfa_fcport_scn(fcport, BFA_PORT_LINKDOWN, BFA_FALSE); + bfa_sm_set_state(fcport, bfa_fcport_sm_iocdown); + break; + + default: + bfa_sm_fault(fcport->bfa, event); + } +} + /* * Link state is down */ @@ -2943,6 +3067,7 @@ bfa_fcport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, */ do_gettimeofday(&tv); fcport->stats_reset_time = tv.tv_sec; + fcport->stats_dma_ready = BFA_FALSE; /* * initialize and set default configuration @@ -2953,6 +3078,9 @@ bfa_fcport_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, port_cfg->maxfrsize = 0; port_cfg->trl_def_speed = BFA_PORT_SPEED_1GBPS; + port_cfg->qos_bw.high = BFA_QOS_BW_HIGH; + port_cfg->qos_bw.med = BFA_QOS_BW_MED; + port_cfg->qos_bw.low = BFA_QOS_BW_LOW; INIT_LIST_HEAD(&fcport->stats_pending_q); INIT_LIST_HEAD(&fcport->statsclr_pending_q); @@ -2996,6 +3124,21 @@ bfa_fcport_iocdisable(struct bfa_s *bfa) bfa_trunk_iocdisable(bfa); } +/* + * Update loop info in fcport for SCN online + */ +static void +bfa_fcport_update_loop_info(struct bfa_fcport_s *fcport, + struct bfa_fcport_loop_info_s *loop_info) +{ + fcport->myalpa = loop_info->myalpa; + fcport->alpabm_valid = + loop_info->alpabm_val; + memcpy(fcport->alpabm.alpa_bm, + loop_info->alpabm.alpa_bm, + sizeof(struct fc_alpabm_s)); +} + static void bfa_fcport_update_linkinfo(struct bfa_fcport_s *fcport) { @@ -3005,12 +3148,15 @@ bfa_fcport_update_linkinfo(struct bfa_fcport_s *fcport) fcport->speed = pevent->link_state.speed; fcport->topology = pevent->link_state.topology; - if (fcport->topology == BFA_PORT_TOPOLOGY_LOOP) - fcport->myalpa = 0; + if (fcport->topology == BFA_PORT_TOPOLOGY_LOOP) { + bfa_fcport_update_loop_info(fcport, + &pevent->link_state.attr.loop_info); + return; + } /* QoS Details */ fcport->qos_attr = pevent->link_state.qos_attr; - fcport->qos_vc_attr = pevent->link_state.vc_fcf.qos_vc_attr; + fcport->qos_vc_attr = pevent->link_state.attr.vc_fcf.qos_vc_attr; /* * update trunk state if applicable @@ -3019,7 +3165,8 @@ bfa_fcport_update_linkinfo(struct bfa_fcport_s *fcport) trunk->attr.state = BFA_TRUNK_DISABLED; /* update FCoE specific */ - fcport->fcoe_vlan = be16_to_cpu(pevent->link_state.vc_fcf.fcf.vlan); + fcport->fcoe_vlan = + be16_to_cpu(pevent->link_state.attr.vc_fcf.fcf.vlan); bfa_trc(fcport->bfa, fcport->speed); bfa_trc(fcport->bfa, fcport->topology); @@ -3453,6 +3600,7 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) case BFI_FCPORT_I2H_ENABLE_RSP: if (fcport->msgtag == i2hmsg.penable_rsp->msgtag) { + fcport->stats_dma_ready = BFA_TRUE; if (fcport->use_flash_cfg) { fcport->cfg = i2hmsg.penable_rsp->port_cfg; fcport->cfg.maxfrsize = @@ -3468,6 +3616,8 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) else fcport->trunk.attr.state = BFA_TRUNK_DISABLED; + fcport->qos_attr.qos_bw = + i2hmsg.penable_rsp->port_cfg.qos_bw; fcport->use_flash_cfg = BFA_FALSE; } @@ -3476,6 +3626,9 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) else fcport->qos_attr.state = BFA_QOS_DISABLED; + fcport->qos_attr.qos_bw_op = + i2hmsg.penable_rsp->port_cfg.qos_bw; + bfa_sm_send_event(fcport, BFA_FCPORT_SM_FWRSP); } break; @@ -3488,8 +3641,17 @@ bfa_fcport_isr(struct bfa_s *bfa, struct bfi_msg_s *msg) case BFI_FCPORT_I2H_EVENT: if (i2hmsg.event->link_state.linkstate == BFA_PORT_LINKUP) bfa_sm_send_event(fcport, BFA_FCPORT_SM_LINKUP); - else - bfa_sm_send_event(fcport, BFA_FCPORT_SM_LINKDOWN); + else { + if (i2hmsg.event->link_state.linkstate_rsn == + BFA_PORT_LINKSTATE_RSN_FAA_MISCONFIG) + bfa_sm_send_event(fcport, + BFA_FCPORT_SM_FAA_MISCONFIG); + else + bfa_sm_send_event(fcport, + BFA_FCPORT_SM_LINKDOWN); + } + fcport->qos_attr.qos_bw_op = + i2hmsg.event->link_state.qos_attr.qos_bw_op; break; case BFI_FCPORT_I2H_TRUNK_SCN: @@ -3609,6 +3771,9 @@ bfa_fcport_cfg_speed(struct bfa_s *bfa, enum bfa_port_speed speed) if (fcport->cfg.trunked == BFA_TRUE) return BFA_STATUS_TRUNK_ENABLED; + if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) && + (speed == BFA_PORT_SPEED_16GBPS)) + return BFA_STATUS_UNSUPP_SPEED; if ((speed != BFA_PORT_SPEED_AUTO) && (speed > fcport->speed_sup)) { bfa_trc(bfa, fcport->speed_sup); return BFA_STATUS_UNSUPP_SPEED; @@ -3663,7 +3828,26 @@ bfa_fcport_cfg_topology(struct bfa_s *bfa, enum bfa_port_topology topology) switch (topology) { case BFA_PORT_TOPOLOGY_P2P: + break; + case BFA_PORT_TOPOLOGY_LOOP: + if ((bfa_fcport_is_qos_enabled(bfa) != BFA_FALSE) || + (fcport->qos_attr.state != BFA_QOS_DISABLED)) + return BFA_STATUS_ERROR_QOS_ENABLED; + if (fcport->cfg.ratelimit != BFA_FALSE) + return BFA_STATUS_ERROR_TRL_ENABLED; + if ((bfa_fcport_is_trunk_enabled(bfa) != BFA_FALSE) || + (fcport->trunk.attr.state != BFA_TRUNK_DISABLED)) + return BFA_STATUS_ERROR_TRUNK_ENABLED; + if ((bfa_fcport_get_speed(bfa) == BFA_PORT_SPEED_16GBPS) || + (fcport->cfg.speed == BFA_PORT_SPEED_16GBPS)) + return BFA_STATUS_UNSUPP_SPEED; + if (bfa_mfg_is_mezz(bfa->ioc.attr->card_type)) + return BFA_STATUS_LOOP_UNSUPP_MEZZ; + if (bfa_fcport_is_dport(bfa) != BFA_FALSE) + return BFA_STATUS_DPORT_ERR; + break; + case BFA_PORT_TOPOLOGY_AUTO: break; @@ -3686,6 +3870,17 @@ bfa_fcport_get_topology(struct bfa_s *bfa) return fcport->topology; } +/** + * Get config topology. + */ +enum bfa_port_topology +bfa_fcport_get_cfg_topology(struct bfa_s *bfa) +{ + struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa); + + return fcport->cfg.topology; +} + bfa_status_t bfa_fcport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa) { @@ -3761,9 +3956,11 @@ bfa_fcport_get_maxfrsize(struct bfa_s *bfa) u8 bfa_fcport_get_rx_bbcredit(struct bfa_s *bfa) { - struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa); + if (bfa_fcport_get_topology(bfa) != BFA_PORT_TOPOLOGY_LOOP) + return (BFA_FCPORT_MOD(bfa))->cfg.rx_bbcredit; - return fcport->cfg.rx_bbcredit; + else + return 0; } void @@ -3850,8 +4047,9 @@ bfa_fcport_get_stats(struct bfa_s *bfa, struct bfa_cb_pending_q_s *cb) { struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa); - if (bfa_ioc_is_disabled(&bfa->ioc)) - return BFA_STATUS_IOC_DISABLED; + if (!bfa_iocfc_is_operational(bfa) || + !fcport->stats_dma_ready) + return BFA_STATUS_IOC_NON_OP; if (!list_empty(&fcport->statsclr_pending_q)) return BFA_STATUS_DEVBUSY; @@ -3876,6 +4074,10 @@ bfa_fcport_clear_stats(struct bfa_s *bfa, struct bfa_cb_pending_q_s *cb) { struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa); + if (!bfa_iocfc_is_operational(bfa) || + !fcport->stats_dma_ready) + return BFA_STATUS_IOC_NON_OP; + if (!list_empty(&fcport->stats_pending_q)) return BFA_STATUS_DEVBUSY; @@ -3905,6 +4107,40 @@ bfa_fcport_is_disabled(struct bfa_s *bfa) } bfa_boolean_t +bfa_fcport_is_dport(struct bfa_s *bfa) +{ + struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa); + + return (bfa_sm_to_state(hal_port_sm_table, fcport->sm) == + BFA_PORT_ST_DPORT); +} + +bfa_status_t +bfa_fcport_set_qos_bw(struct bfa_s *bfa, struct bfa_qos_bw_s *qos_bw) +{ + struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa); + enum bfa_ioc_type_e ioc_type = bfa_get_type(bfa); + + bfa_trc(bfa, ioc_type); + + if ((qos_bw->high == 0) || (qos_bw->med == 0) || (qos_bw->low == 0)) + return BFA_STATUS_QOS_BW_INVALID; + + if ((qos_bw->high + qos_bw->med + qos_bw->low) != 100) + return BFA_STATUS_QOS_BW_INVALID; + + if ((qos_bw->med > qos_bw->high) || (qos_bw->low > qos_bw->med) || + (qos_bw->low > qos_bw->high)) + return BFA_STATUS_QOS_BW_INVALID; + + if ((ioc_type == BFA_IOC_TYPE_FC) && + (fcport->cfg.topology != BFA_PORT_TOPOLOGY_LOOP)) + fcport->cfg.qos_bw = *qos_bw; + + return BFA_STATUS_OK; +} + +bfa_boolean_t bfa_fcport_is_ratelim(struct bfa_s *bfa) { struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(bfa); @@ -3981,6 +4217,26 @@ bfa_fcport_is_trunk_enabled(struct bfa_s *bfa) return fcport->cfg.trunked; } +void +bfa_fcport_dportenable(struct bfa_s *bfa) +{ + /* + * Assume caller check for port is in disable state + */ + bfa_sm_send_event(BFA_FCPORT_MOD(bfa), BFA_FCPORT_SM_DPORTENABLE); + bfa_port_set_dportenabled(&bfa->modules.port, BFA_TRUE); +} + +void +bfa_fcport_dportdisable(struct bfa_s *bfa) +{ + /* + * Assume caller check for port is in disable state + */ + bfa_sm_send_event(BFA_FCPORT_MOD(bfa), BFA_FCPORT_SM_DPORTDISABLE); + bfa_port_set_dportenabled(&bfa->modules.port, BFA_FALSE); +} + /* * Rport State machine functions */ @@ -4707,6 +4963,21 @@ bfa_rport_isr(struct bfa_s *bfa, struct bfi_msg_s *m) bfa_sm_send_event(rp, BFA_RPORT_SM_QOS_SCN); break; + case BFI_RPORT_I2H_LIP_SCN_ONLINE: + bfa_fcport_update_loop_info(BFA_FCPORT_MOD(bfa), + &msg.lip_scn->loop_info); + bfa_cb_rport_scn_online(bfa); + break; + + case BFI_RPORT_I2H_LIP_SCN_OFFLINE: + bfa_cb_rport_scn_offline(bfa); + break; + + case BFI_RPORT_I2H_NO_DEV: + rp = BFA_RPORT_FROM_TAG(bfa, msg.lip_scn->bfa_handle); + bfa_cb_rport_scn_no_dev(rp->rport_drv); + break; + default: bfa_trc(bfa, m->mhdr.msg_id); WARN_ON(1); @@ -5348,6 +5619,37 @@ bfa_uf_res_recfg(struct bfa_s *bfa, u16 num_uf_fw) } /* + * Dport forward declaration + */ + +/* + * BFA DPORT state machine events + */ +enum bfa_dport_sm_event { + BFA_DPORT_SM_ENABLE = 1, /* dport enable event */ + BFA_DPORT_SM_DISABLE = 2, /* dport disable event */ + BFA_DPORT_SM_FWRSP = 3, /* fw enable/disable rsp */ + BFA_DPORT_SM_QRESUME = 4, /* CQ space available */ + BFA_DPORT_SM_HWFAIL = 5, /* IOC h/w failure */ +}; + +static void bfa_dport_sm_disabled(struct bfa_dport_s *dport, + enum bfa_dport_sm_event event); +static void bfa_dport_sm_enabling_qwait(struct bfa_dport_s *dport, + enum bfa_dport_sm_event event); +static void bfa_dport_sm_enabling(struct bfa_dport_s *dport, + enum bfa_dport_sm_event event); +static void bfa_dport_sm_enabled(struct bfa_dport_s *dport, + enum bfa_dport_sm_event event); +static void bfa_dport_sm_disabling_qwait(struct bfa_dport_s *dport, + enum bfa_dport_sm_event event); +static void bfa_dport_sm_disabling(struct bfa_dport_s *dport, + enum bfa_dport_sm_event event); +static void bfa_dport_qresume(void *cbarg); +static void bfa_dport_req_comp(struct bfa_dport_s *dport, + bfi_diag_dport_rsp_t *msg); + +/* * BFA fcdiag module */ #define BFA_DIAG_QTEST_TOV 1000 /* msec */ @@ -5377,15 +5679,24 @@ bfa_fcdiag_attach(struct bfa_s *bfa, void *bfad, struct bfa_iocfc_cfg_s *cfg, struct bfa_pcidev_s *pcidev) { struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa); + struct bfa_dport_s *dport = &fcdiag->dport; + fcdiag->bfa = bfa; fcdiag->trcmod = bfa->trcmod; /* The common DIAG attach bfa_diag_attach() will do all memory claim */ + dport->bfa = bfa; + bfa_sm_set_state(dport, bfa_dport_sm_disabled); + bfa_reqq_winit(&dport->reqq_wait, bfa_dport_qresume, dport); + dport->cbfn = NULL; + dport->cbarg = NULL; } static void bfa_fcdiag_iocdisable(struct bfa_s *bfa) { struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa); + struct bfa_dport_s *dport = &fcdiag->dport; + bfa_trc(fcdiag, fcdiag->lb.lock); if (fcdiag->lb.lock) { fcdiag->lb.status = BFA_STATUS_IOC_FAILURE; @@ -5393,6 +5704,8 @@ bfa_fcdiag_iocdisable(struct bfa_s *bfa) fcdiag->lb.lock = 0; bfa_fcdiag_set_busy_status(fcdiag); } + + bfa_sm_send_event(dport, BFA_DPORT_SM_HWFAIL); } static void @@ -5577,6 +5890,9 @@ bfa_fcdiag_intr(struct bfa_s *bfa, struct bfi_msg_s *msg) case BFI_DIAG_I2H_QTEST: bfa_fcdiag_queuetest_comp(fcdiag, (bfi_diag_qtest_rsp_t *)msg); break; + case BFI_DIAG_I2H_DPORT: + bfa_dport_req_comp(&fcdiag->dport, (bfi_diag_dport_rsp_t *)msg); + break; default: bfa_trc(fcdiag, msg->mhdr.msg_id); WARN_ON(1); @@ -5646,12 +5962,18 @@ bfa_fcdiag_loopback(struct bfa_s *bfa, enum bfa_port_opmode opmode, } } + /* + * For CT2, 1G is not supported + */ + if ((speed == BFA_PORT_SPEED_1GBPS) && + (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id))) { + bfa_trc(fcdiag, speed); + return BFA_STATUS_UNSUPP_SPEED; + } + /* For Mezz card, port speed entered needs to be checked */ if (bfa_mfg_is_mezz(bfa->ioc.attr->card_type)) { if (bfa_ioc_get_type(&bfa->ioc) == BFA_IOC_TYPE_FC) { - if ((speed == BFA_PORT_SPEED_1GBPS) && - (bfa_asic_id_ct2(bfa->ioc.pcidev.device_id))) - return BFA_STATUS_UNSUPP_SPEED; if (!(speed == BFA_PORT_SPEED_1GBPS || speed == BFA_PORT_SPEED_2GBPS || speed == BFA_PORT_SPEED_4GBPS || @@ -5764,3 +6086,379 @@ bfa_fcdiag_lb_is_running(struct bfa_s *bfa) struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa); return fcdiag->lb.lock ? BFA_STATUS_DIAG_BUSY : BFA_STATUS_OK; } + +/* + * D-port + */ +static bfa_boolean_t bfa_dport_send_req(struct bfa_dport_s *dport, + enum bfi_dport_req req); +static void +bfa_cb_fcdiag_dport(struct bfa_dport_s *dport, bfa_status_t bfa_status) +{ + if (dport->cbfn != NULL) { + dport->cbfn(dport->cbarg, bfa_status); + dport->cbfn = NULL; + dport->cbarg = NULL; + } +} + +static void +bfa_dport_sm_disabled(struct bfa_dport_s *dport, enum bfa_dport_sm_event event) +{ + bfa_trc(dport->bfa, event); + + switch (event) { + case BFA_DPORT_SM_ENABLE: + bfa_fcport_dportenable(dport->bfa); + if (bfa_dport_send_req(dport, BFI_DPORT_ENABLE)) + bfa_sm_set_state(dport, bfa_dport_sm_enabling); + else + bfa_sm_set_state(dport, bfa_dport_sm_enabling_qwait); + break; + + case BFA_DPORT_SM_DISABLE: + /* Already disabled */ + break; + + case BFA_DPORT_SM_HWFAIL: + /* ignore */ + break; + + default: + bfa_sm_fault(dport->bfa, event); + } +} + +static void +bfa_dport_sm_enabling_qwait(struct bfa_dport_s *dport, + enum bfa_dport_sm_event event) +{ + bfa_trc(dport->bfa, event); + + switch (event) { + case BFA_DPORT_SM_QRESUME: + bfa_sm_set_state(dport, bfa_dport_sm_enabling); + bfa_dport_send_req(dport, BFI_DPORT_ENABLE); + break; + + case BFA_DPORT_SM_HWFAIL: + bfa_reqq_wcancel(&dport->reqq_wait); + bfa_sm_set_state(dport, bfa_dport_sm_disabled); + bfa_cb_fcdiag_dport(dport, BFA_STATUS_FAILED); + break; + + default: + bfa_sm_fault(dport->bfa, event); + } +} + +static void +bfa_dport_sm_enabling(struct bfa_dport_s *dport, enum bfa_dport_sm_event event) +{ + bfa_trc(dport->bfa, event); + + switch (event) { + case BFA_DPORT_SM_FWRSP: + bfa_sm_set_state(dport, bfa_dport_sm_enabled); + break; + + case BFA_DPORT_SM_HWFAIL: + bfa_sm_set_state(dport, bfa_dport_sm_disabled); + bfa_cb_fcdiag_dport(dport, BFA_STATUS_FAILED); + break; + + default: + bfa_sm_fault(dport->bfa, event); + } +} + +static void +bfa_dport_sm_enabled(struct bfa_dport_s *dport, enum bfa_dport_sm_event event) +{ + bfa_trc(dport->bfa, event); + + switch (event) { + case BFA_DPORT_SM_ENABLE: + /* Already enabled */ + break; + + case BFA_DPORT_SM_DISABLE: + bfa_fcport_dportdisable(dport->bfa); + if (bfa_dport_send_req(dport, BFI_DPORT_DISABLE)) + bfa_sm_set_state(dport, bfa_dport_sm_disabling); + else + bfa_sm_set_state(dport, bfa_dport_sm_disabling_qwait); + break; + + case BFA_DPORT_SM_HWFAIL: + bfa_sm_set_state(dport, bfa_dport_sm_disabled); + break; + + default: + bfa_sm_fault(dport->bfa, event); + } +} + +static void +bfa_dport_sm_disabling_qwait(struct bfa_dport_s *dport, + enum bfa_dport_sm_event event) +{ + bfa_trc(dport->bfa, event); + + switch (event) { + case BFA_DPORT_SM_QRESUME: + bfa_sm_set_state(dport, bfa_dport_sm_disabling); + bfa_dport_send_req(dport, BFI_DPORT_DISABLE); + break; + + case BFA_DPORT_SM_HWFAIL: + bfa_sm_set_state(dport, bfa_dport_sm_disabled); + bfa_reqq_wcancel(&dport->reqq_wait); + bfa_cb_fcdiag_dport(dport, BFA_STATUS_OK); + break; + + default: + bfa_sm_fault(dport->bfa, event); + } +} + +static void +bfa_dport_sm_disabling(struct bfa_dport_s *dport, enum bfa_dport_sm_event event) +{ + bfa_trc(dport->bfa, event); + + switch (event) { + case BFA_DPORT_SM_FWRSP: + bfa_sm_set_state(dport, bfa_dport_sm_disabled); + break; + + case BFA_DPORT_SM_HWFAIL: + bfa_sm_set_state(dport, bfa_dport_sm_disabled); + bfa_cb_fcdiag_dport(dport, BFA_STATUS_OK); + break; + + default: + bfa_sm_fault(dport->bfa, event); + } +} + + +static bfa_boolean_t +bfa_dport_send_req(struct bfa_dport_s *dport, enum bfi_dport_req req) +{ + struct bfi_diag_dport_req_s *m; + + /* + * Increment message tag before queue check, so that responses to old + * requests are discarded. + */ + dport->msgtag++; + + /* + * check for room in queue to send request now + */ + m = bfa_reqq_next(dport->bfa, BFA_REQQ_DIAG); + if (!m) { + bfa_reqq_wait(dport->bfa, BFA_REQQ_PORT, &dport->reqq_wait); + return BFA_FALSE; + } + + bfi_h2i_set(m->mh, BFI_MC_DIAG, BFI_DIAG_H2I_DPORT, + bfa_fn_lpu(dport->bfa)); + m->req = req; + m->msgtag = dport->msgtag; + + /* + * queue I/O message to firmware + */ + bfa_reqq_produce(dport->bfa, BFA_REQQ_DIAG, m->mh); + + return BFA_TRUE; +} + +static void +bfa_dport_qresume(void *cbarg) +{ + struct bfa_dport_s *dport = cbarg; + + bfa_sm_send_event(dport, BFA_DPORT_SM_QRESUME); +} + +static void +bfa_dport_req_comp(struct bfa_dport_s *dport, bfi_diag_dport_rsp_t *msg) +{ + bfa_sm_send_event(dport, BFA_DPORT_SM_FWRSP); + bfa_cb_fcdiag_dport(dport, msg->status); +} + +/* + * Dport enable + * + * @param[in] *bfa - bfa data struct + */ +bfa_status_t +bfa_dport_enable(struct bfa_s *bfa, bfa_cb_diag_t cbfn, void *cbarg) +{ + struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa); + struct bfa_dport_s *dport = &fcdiag->dport; + + /* + * Dport is not support in MEZZ card + */ + if (bfa_mfg_is_mezz(dport->bfa->ioc.attr->card_type)) { + bfa_trc(dport->bfa, BFA_STATUS_PBC); + return BFA_STATUS_CMD_NOTSUPP_MEZZ; + } + + /* + * Check to see if IOC is down + */ + if (!bfa_iocfc_is_operational(bfa)) + return BFA_STATUS_IOC_NON_OP; + + /* if port is PBC disabled, return error */ + if (bfa_fcport_is_pbcdisabled(bfa)) { + bfa_trc(dport->bfa, BFA_STATUS_PBC); + return BFA_STATUS_PBC; + } + + /* + * Check if port mode is FC port + */ + if (bfa_ioc_get_type(&bfa->ioc) != BFA_IOC_TYPE_FC) { + bfa_trc(dport->bfa, bfa_ioc_get_type(&bfa->ioc)); + return BFA_STATUS_CMD_NOTSUPP_CNA; + } + + /* + * Check if port is in LOOP mode + */ + if ((bfa_fcport_get_cfg_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP) || + (bfa_fcport_get_topology(bfa) == BFA_PORT_TOPOLOGY_LOOP)) { + bfa_trc(dport->bfa, 0); + return BFA_STATUS_TOPOLOGY_LOOP; + } + + /* + * Check if port is TRUNK mode + */ + if (bfa_fcport_is_trunk_enabled(bfa)) { + bfa_trc(dport->bfa, 0); + return BFA_STATUS_ERROR_TRUNK_ENABLED; + } + + /* + * Check to see if port is disable or in dport state + */ + if ((bfa_fcport_is_disabled(bfa) == BFA_FALSE) && + (bfa_fcport_is_dport(bfa) == BFA_FALSE)) { + bfa_trc(dport->bfa, 0); + return BFA_STATUS_PORT_NOT_DISABLED; + } + + /* + * Check if dport is busy + */ + if (bfa_sm_cmp_state(dport, bfa_dport_sm_enabling) || + bfa_sm_cmp_state(dport, bfa_dport_sm_enabling_qwait) || + bfa_sm_cmp_state(dport, bfa_dport_sm_disabling) || + bfa_sm_cmp_state(dport, bfa_dport_sm_disabling_qwait)) { + return BFA_STATUS_DEVBUSY; + } + + /* + * Check if dport is already enabled + */ + if (bfa_sm_cmp_state(dport, bfa_dport_sm_enabled)) { + bfa_trc(dport->bfa, 0); + return BFA_STATUS_DPORT_ENABLED; + } + + dport->cbfn = cbfn; + dport->cbarg = cbarg; + + bfa_sm_send_event(dport, BFA_DPORT_SM_ENABLE); + return BFA_STATUS_OK; +} + +/* + * Dport disable + * + * @param[in] *bfa - bfa data struct + */ +bfa_status_t +bfa_dport_disable(struct bfa_s *bfa, bfa_cb_diag_t cbfn, void *cbarg) +{ + struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa); + struct bfa_dport_s *dport = &fcdiag->dport; + + if (bfa_ioc_is_disabled(&bfa->ioc)) + return BFA_STATUS_IOC_DISABLED; + + /* if port is PBC disabled, return error */ + if (bfa_fcport_is_pbcdisabled(bfa)) { + bfa_trc(dport->bfa, BFA_STATUS_PBC); + return BFA_STATUS_PBC; + } + + /* + * Check to see if port is disable or in dport state + */ + if ((bfa_fcport_is_disabled(bfa) == BFA_FALSE) && + (bfa_fcport_is_dport(bfa) == BFA_FALSE)) { + bfa_trc(dport->bfa, 0); + return BFA_STATUS_PORT_NOT_DISABLED; + } + + /* + * Check if dport is busy + */ + if (bfa_sm_cmp_state(dport, bfa_dport_sm_enabling) || + bfa_sm_cmp_state(dport, bfa_dport_sm_enabling_qwait) || + bfa_sm_cmp_state(dport, bfa_dport_sm_disabling) || + bfa_sm_cmp_state(dport, bfa_dport_sm_disabling_qwait)) + return BFA_STATUS_DEVBUSY; + + /* + * Check if dport is already disabled + */ + if (bfa_sm_cmp_state(dport, bfa_dport_sm_disabled)) { + bfa_trc(dport->bfa, 0); + return BFA_STATUS_DPORT_DISABLED; + } + + dport->cbfn = cbfn; + dport->cbarg = cbarg; + + bfa_sm_send_event(dport, BFA_DPORT_SM_DISABLE); + return BFA_STATUS_OK; +} + +/* + * Get D-port state + * + * @param[in] *bfa - bfa data struct + */ + +bfa_status_t +bfa_dport_get_state(struct bfa_s *bfa, enum bfa_dport_state *state) +{ + struct bfa_fcdiag_s *fcdiag = BFA_FCDIAG_MOD(bfa); + struct bfa_dport_s *dport = &fcdiag->dport; + + if (bfa_sm_cmp_state(dport, bfa_dport_sm_enabled)) + *state = BFA_DPORT_ST_ENABLED; + else if (bfa_sm_cmp_state(dport, bfa_dport_sm_enabling) || + bfa_sm_cmp_state(dport, bfa_dport_sm_enabling_qwait)) + *state = BFA_DPORT_ST_ENABLING; + else if (bfa_sm_cmp_state(dport, bfa_dport_sm_disabled)) + *state = BFA_DPORT_ST_DISABLED; + else if (bfa_sm_cmp_state(dport, bfa_dport_sm_disabling) || + bfa_sm_cmp_state(dport, bfa_dport_sm_disabling_qwait)) + *state = BFA_DPORT_ST_DISABLING; + else { + bfa_trc(dport->bfa, BFA_STATUS_EINVAL); + return BFA_STATUS_EINVAL; + } + return BFA_STATUS_OK; +} diff --git a/drivers/scsi/bfa/bfa_svc.h b/drivers/scsi/bfa/bfa_svc.h index 1abcf7c5166..8d7fbecfcb2 100644 --- a/drivers/scsi/bfa/bfa_svc.h +++ b/drivers/scsi/bfa/bfa_svc.h @@ -474,8 +474,10 @@ struct bfa_fcport_s { /* supported speeds */ enum bfa_port_speed speed; /* current speed */ enum bfa_port_topology topology; /* current topology */ - u8 myalpa; /* my ALPA in LOOP topology */ u8 rsvd[3]; + u8 myalpa; /* my ALPA in LOOP topology */ + u8 alpabm_valid; /* alpa bitmap valid or not */ + struct fc_alpabm_s alpabm; /* alpa bitmap */ struct bfa_port_cfg_s cfg; /* current port configuration */ bfa_boolean_t use_flash_cfg; /* get port cfg from flash */ struct bfa_qos_attr_s qos_attr; /* QoS Attributes */ @@ -512,6 +514,7 @@ struct bfa_fcport_s { struct bfa_fcport_trunk_s trunk; u16 fcoe_vlan; struct bfa_mem_dma_s fcport_dma; + bfa_boolean_t stats_dma_ready; }; #define BFA_FCPORT_MOD(__bfa) (&(__bfa)->modules.fcport) @@ -534,6 +537,7 @@ enum bfa_port_speed bfa_fcport_get_speed(struct bfa_s *bfa); bfa_status_t bfa_fcport_cfg_topology(struct bfa_s *bfa, enum bfa_port_topology topo); enum bfa_port_topology bfa_fcport_get_topology(struct bfa_s *bfa); +enum bfa_port_topology bfa_fcport_get_cfg_topology(struct bfa_s *bfa); bfa_status_t bfa_fcport_cfg_hardalpa(struct bfa_s *bfa, u8 alpa); bfa_boolean_t bfa_fcport_get_hardalpa(struct bfa_s *bfa, u8 *alpa); u8 bfa_fcport_get_myalpa(struct bfa_s *bfa); @@ -547,6 +551,9 @@ void bfa_fcport_event_register(struct bfa_s *bfa, void (*event_cbfn) (void *cbarg, enum bfa_port_linkstate event), void *event_cbarg); bfa_boolean_t bfa_fcport_is_disabled(struct bfa_s *bfa); +bfa_boolean_t bfa_fcport_is_dport(struct bfa_s *bfa); +bfa_status_t bfa_fcport_set_qos_bw(struct bfa_s *bfa, + struct bfa_qos_bw_s *qos_bw); enum bfa_port_speed bfa_fcport_get_ratelim_speed(struct bfa_s *bfa); void bfa_fcport_set_tx_bbcredit(struct bfa_s *bfa, u16 tx_bbcredit, u8 bb_scn); @@ -560,6 +567,8 @@ bfa_status_t bfa_fcport_clear_stats(struct bfa_s *bfa, struct bfa_cb_pending_q_s *cb); bfa_boolean_t bfa_fcport_is_qos_enabled(struct bfa_s *bfa); bfa_boolean_t bfa_fcport_is_trunk_enabled(struct bfa_s *bfa); +void bfa_fcport_dportenable(struct bfa_s *bfa); +void bfa_fcport_dportdisable(struct bfa_s *bfa); bfa_status_t bfa_fcport_is_pbcdisabled(struct bfa_s *bfa); void bfa_fcport_cfg_faa(struct bfa_s *bfa, u8 state); @@ -575,6 +584,9 @@ void bfa_cb_rport_offline(void *rport); void bfa_cb_rport_qos_scn_flowid(void *rport, struct bfa_rport_qos_attr_s old_qos_attr, struct bfa_rport_qos_attr_s new_qos_attr); +void bfa_cb_rport_scn_online(struct bfa_s *bfa); +void bfa_cb_rport_scn_offline(struct bfa_s *bfa); +void bfa_cb_rport_scn_no_dev(void *rp); void bfa_cb_rport_qos_scn_prio(void *rport, struct bfa_rport_qos_attr_s old_qos_attr, struct bfa_rport_qos_attr_s new_qos_attr); @@ -697,11 +709,21 @@ struct bfa_fcdiag_lb_s { u32 status; }; +struct bfa_dport_s { + struct bfa_s *bfa; /* Back pointer to BFA */ + bfa_sm_t sm; /* finite state machine */ + u32 msgtag; /* firmware msg tag for reply */ + struct bfa_reqq_wait_s reqq_wait; + bfa_cb_diag_t cbfn; + void *cbarg; +}; + struct bfa_fcdiag_s { struct bfa_s *bfa; /* Back pointer to BFA */ struct bfa_trc_mod_s *trcmod; struct bfa_fcdiag_lb_s lb; struct bfa_fcdiag_qtest_s qtest; + struct bfa_dport_s dport; }; #define BFA_FCDIAG_MOD(__bfa) (&(__bfa)->modules.fcdiag) @@ -717,5 +739,11 @@ bfa_status_t bfa_fcdiag_queuetest(struct bfa_s *bfa, u32 ignore, u32 queue, struct bfa_diag_qtest_result_s *result, bfa_cb_diag_t cbfn, void *cbarg); bfa_status_t bfa_fcdiag_lb_is_running(struct bfa_s *bfa); +bfa_status_t bfa_dport_enable(struct bfa_s *bfa, bfa_cb_diag_t cbfn, + void *cbarg); +bfa_status_t bfa_dport_disable(struct bfa_s *bfa, bfa_cb_diag_t cbfn, + void *cbarg); +bfa_status_t bfa_dport_get_state(struct bfa_s *bfa, + enum bfa_dport_state *state); #endif /* __BFA_SVC_H__ */ diff --git a/drivers/scsi/bfa/bfad.c b/drivers/scsi/bfa/bfad.c index c37494916a1..895b0e516e0 100644 --- a/drivers/scsi/bfa/bfad.c +++ b/drivers/scsi/bfa/bfad.c @@ -63,9 +63,9 @@ int max_rport_logins = BFA_FCS_MAX_RPORT_LOGINS; u32 bfi_image_cb_size, bfi_image_ct_size, bfi_image_ct2_size; u32 *bfi_image_cb, *bfi_image_ct, *bfi_image_ct2; -#define BFAD_FW_FILE_CB "cbfw.bin" -#define BFAD_FW_FILE_CT "ctfw.bin" -#define BFAD_FW_FILE_CT2 "ct2fw.bin" +#define BFAD_FW_FILE_CB "cbfw-3.1.0.0.bin" +#define BFAD_FW_FILE_CT "ctfw-3.1.0.0.bin" +#define BFAD_FW_FILE_CT2 "ct2fw-3.1.0.0.bin" static u32 *bfad_load_fwimg(struct pci_dev *pdev); static void bfad_free_fwimg(void); diff --git a/drivers/scsi/bfa/bfad_bsg.c b/drivers/scsi/bfa/bfad_bsg.c index 0afa39076ce..555e7db94a1 100644 --- a/drivers/scsi/bfa/bfad_bsg.c +++ b/drivers/scsi/bfa/bfad_bsg.c @@ -33,7 +33,7 @@ bfad_iocmd_ioc_enable(struct bfad_s *bfad, void *cmd) /* If IOC is not in disabled state - return */ if (!bfa_ioc_is_disabled(&bfad->bfa.ioc)) { spin_unlock_irqrestore(&bfad->bfad_lock, flags); - iocmd->status = BFA_STATUS_IOC_FAILURE; + iocmd->status = BFA_STATUS_OK; return rc; } @@ -54,6 +54,12 @@ bfad_iocmd_ioc_disable(struct bfad_s *bfad, void *cmd) unsigned long flags; spin_lock_irqsave(&bfad->bfad_lock, flags); + if (bfa_ioc_is_disabled(&bfad->bfa.ioc)) { + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + iocmd->status = BFA_STATUS_OK; + return rc; + } + if (bfad->disable_active) { spin_unlock_irqrestore(&bfad->bfad_lock, flags); return -EBUSY; @@ -101,9 +107,10 @@ bfad_iocmd_ioc_get_info(struct bfad_s *bfad, void *cmd) /* set adapter hw path */ strcpy(iocmd->adapter_hwpath, bfad->pci_name); - i = strlen(iocmd->adapter_hwpath) - 1; - while (iocmd->adapter_hwpath[i] != '.') - i--; + for (i = 0; iocmd->adapter_hwpath[i] != ':' && i < BFA_STRING_32; i++) + ; + for (; iocmd->adapter_hwpath[++i] != ':' && i < BFA_STRING_32; ) + ; iocmd->adapter_hwpath[i] = '\0'; iocmd->status = BFA_STATUS_OK; return 0; @@ -880,6 +887,19 @@ out: } int +bfad_iocmd_qos_set_bw(struct bfad_s *bfad, void *pcmd) +{ + struct bfa_bsg_qos_bw_s *iocmd = (struct bfa_bsg_qos_bw_s *)pcmd; + unsigned long flags; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + iocmd->status = bfa_fcport_set_qos_bw(&bfad->bfa, &iocmd->qos_bw); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + return 0; +} + +int bfad_iocmd_ratelim(struct bfad_s *bfad, unsigned int cmd, void *pcmd) { struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)pcmd; @@ -888,16 +908,22 @@ bfad_iocmd_ratelim(struct bfad_s *bfad, unsigned int cmd, void *pcmd) spin_lock_irqsave(&bfad->bfad_lock, flags); - if (cmd == IOCMD_RATELIM_ENABLE) - fcport->cfg.ratelimit = BFA_TRUE; - else if (cmd == IOCMD_RATELIM_DISABLE) - fcport->cfg.ratelimit = BFA_FALSE; + if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) && + (fcport->topology == BFA_PORT_TOPOLOGY_LOOP)) + iocmd->status = BFA_STATUS_TOPOLOGY_LOOP; + else { + if (cmd == IOCMD_RATELIM_ENABLE) + fcport->cfg.ratelimit = BFA_TRUE; + else if (cmd == IOCMD_RATELIM_DISABLE) + fcport->cfg.ratelimit = BFA_FALSE; - if (fcport->cfg.trl_def_speed == BFA_PORT_SPEED_UNKNOWN) - fcport->cfg.trl_def_speed = BFA_PORT_SPEED_1GBPS; + if (fcport->cfg.trl_def_speed == BFA_PORT_SPEED_UNKNOWN) + fcport->cfg.trl_def_speed = BFA_PORT_SPEED_1GBPS; + + iocmd->status = BFA_STATUS_OK; + } spin_unlock_irqrestore(&bfad->bfad_lock, flags); - iocmd->status = BFA_STATUS_OK; return 0; } @@ -919,8 +945,13 @@ bfad_iocmd_ratelim_speed(struct bfad_s *bfad, unsigned int cmd, void *pcmd) return 0; } - fcport->cfg.trl_def_speed = iocmd->speed; - iocmd->status = BFA_STATUS_OK; + if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) && + (fcport->topology == BFA_PORT_TOPOLOGY_LOOP)) + iocmd->status = BFA_STATUS_TOPOLOGY_LOOP; + else { + fcport->cfg.trl_def_speed = iocmd->speed; + iocmd->status = BFA_STATUS_OK; + } spin_unlock_irqrestore(&bfad->bfad_lock, flags); return 0; @@ -1167,8 +1198,8 @@ bfad_iocmd_pcifn_create(struct bfad_s *bfad, void *cmd) spin_lock_irqsave(&bfad->bfad_lock, flags); iocmd->status = bfa_ablk_pf_create(&bfad->bfa.modules.ablk, &iocmd->pcifn_id, iocmd->port, - iocmd->pcifn_class, iocmd->bandwidth, - bfad_hcb_comp, &fcomp); + iocmd->pcifn_class, iocmd->bw_min, + iocmd->bw_max, bfad_hcb_comp, &fcomp); spin_unlock_irqrestore(&bfad->bfad_lock, flags); if (iocmd->status != BFA_STATUS_OK) goto out; @@ -1211,8 +1242,8 @@ bfad_iocmd_pcifn_bw(struct bfad_s *bfad, void *cmd) init_completion(&fcomp.comp); spin_lock_irqsave(&bfad->bfad_lock, flags); iocmd->status = bfa_ablk_pf_update(&bfad->bfa.modules.ablk, - iocmd->pcifn_id, iocmd->bandwidth, - bfad_hcb_comp, &fcomp); + iocmd->pcifn_id, iocmd->bw_min, + iocmd->bw_max, bfad_hcb_comp, &fcomp); spin_unlock_irqrestore(&bfad->bfad_lock, flags); bfa_trc(bfad, iocmd->status); if (iocmd->status != BFA_STATUS_OK) @@ -1736,6 +1767,52 @@ bfad_iocmd_diag_lb_stat(struct bfad_s *bfad, void *cmd) } int +bfad_iocmd_diag_cfg_dport(struct bfad_s *bfad, unsigned int cmd, void *pcmd) +{ + struct bfa_bsg_gen_s *iocmd = (struct bfa_bsg_gen_s *)pcmd; + unsigned long flags; + struct bfad_hal_comp fcomp; + + init_completion(&fcomp.comp); + spin_lock_irqsave(&bfad->bfad_lock, flags); + if (cmd == IOCMD_DIAG_DPORT_ENABLE) + iocmd->status = bfa_dport_enable(&bfad->bfa, + bfad_hcb_comp, &fcomp); + else if (cmd == IOCMD_DIAG_DPORT_DISABLE) + iocmd->status = bfa_dport_disable(&bfad->bfa, + bfad_hcb_comp, &fcomp); + else { + bfa_trc(bfad, 0); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + return -EINVAL; + } + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + if (iocmd->status != BFA_STATUS_OK) + bfa_trc(bfad, iocmd->status); + else { + wait_for_completion(&fcomp.comp); + iocmd->status = fcomp.status; + } + + return 0; +} + +int +bfad_iocmd_diag_dport_get_state(struct bfad_s *bfad, void *pcmd) +{ + struct bfa_bsg_diag_dport_get_state_s *iocmd = + (struct bfa_bsg_diag_dport_get_state_s *)pcmd; + unsigned long flags; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + iocmd->status = bfa_dport_get_state(&bfad->bfa, &iocmd->state); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + return 0; +} + +int bfad_iocmd_phy_get_attr(struct bfad_s *bfad, void *cmd) { struct bfa_bsg_phy_attr_s *iocmd = @@ -2052,7 +2129,7 @@ bfad_iocmd_boot_cfg(struct bfad_s *bfad, void *cmd) init_completion(&fcomp.comp); spin_lock_irqsave(&bfad->bfad_lock, flags); iocmd->status = bfa_flash_update_part(BFA_FLASH(&bfad->bfa), - BFA_FLASH_PART_BOOT, PCI_FUNC(bfad->pcidev->devfn), + BFA_FLASH_PART_BOOT, bfad->bfa.ioc.port_id, &iocmd->cfg, sizeof(struct bfa_boot_cfg_s), 0, bfad_hcb_comp, &fcomp); spin_unlock_irqrestore(&bfad->bfad_lock, flags); @@ -2074,7 +2151,7 @@ bfad_iocmd_boot_query(struct bfad_s *bfad, void *cmd) init_completion(&fcomp.comp); spin_lock_irqsave(&bfad->bfad_lock, flags); iocmd->status = bfa_flash_read_part(BFA_FLASH(&bfad->bfa), - BFA_FLASH_PART_BOOT, PCI_FUNC(bfad->pcidev->devfn), + BFA_FLASH_PART_BOOT, bfad->bfa.ioc.port_id, &iocmd->cfg, sizeof(struct bfa_boot_cfg_s), 0, bfad_hcb_comp, &fcomp); spin_unlock_irqrestore(&bfad->bfad_lock, flags); @@ -2161,22 +2238,31 @@ bfad_iocmd_cfg_trunk(struct bfad_s *bfad, void *cmd, unsigned int v_cmd) spin_lock_irqsave(&bfad->bfad_lock, flags); - if (v_cmd == IOCMD_TRUNK_ENABLE) { - trunk->attr.state = BFA_TRUNK_OFFLINE; - bfa_fcport_disable(&bfad->bfa); - fcport->cfg.trunked = BFA_TRUE; - } else if (v_cmd == IOCMD_TRUNK_DISABLE) { - trunk->attr.state = BFA_TRUNK_DISABLED; - bfa_fcport_disable(&bfad->bfa); - fcport->cfg.trunked = BFA_FALSE; - } + if (bfa_fcport_is_dport(&bfad->bfa)) + return BFA_STATUS_DPORT_ERR; - if (!bfa_fcport_is_disabled(&bfad->bfa)) - bfa_fcport_enable(&bfad->bfa); + if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) || + (fcport->topology == BFA_PORT_TOPOLOGY_LOOP)) + iocmd->status = BFA_STATUS_TOPOLOGY_LOOP; + else { + if (v_cmd == IOCMD_TRUNK_ENABLE) { + trunk->attr.state = BFA_TRUNK_OFFLINE; + bfa_fcport_disable(&bfad->bfa); + fcport->cfg.trunked = BFA_TRUE; + } else if (v_cmd == IOCMD_TRUNK_DISABLE) { + trunk->attr.state = BFA_TRUNK_DISABLED; + bfa_fcport_disable(&bfad->bfa); + fcport->cfg.trunked = BFA_FALSE; + } + + if (!bfa_fcport_is_disabled(&bfad->bfa)) + bfa_fcport_enable(&bfad->bfa); + + iocmd->status = BFA_STATUS_OK; + } spin_unlock_irqrestore(&bfad->bfad_lock, flags); - iocmd->status = BFA_STATUS_OK; return 0; } @@ -2189,12 +2275,17 @@ bfad_iocmd_trunk_get_attr(struct bfad_s *bfad, void *cmd) unsigned long flags; spin_lock_irqsave(&bfad->bfad_lock, flags); - memcpy((void *)&iocmd->attr, (void *)&trunk->attr, - sizeof(struct bfa_trunk_attr_s)); - iocmd->attr.port_id = bfa_lps_get_base_pid(&bfad->bfa); + if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) || + (fcport->topology == BFA_PORT_TOPOLOGY_LOOP)) + iocmd->status = BFA_STATUS_TOPOLOGY_LOOP; + else { + memcpy((void *)&iocmd->attr, (void *)&trunk->attr, + sizeof(struct bfa_trunk_attr_s)); + iocmd->attr.port_id = bfa_lps_get_base_pid(&bfad->bfa); + iocmd->status = BFA_STATUS_OK; + } spin_unlock_irqrestore(&bfad->bfad_lock, flags); - iocmd->status = BFA_STATUS_OK; return 0; } @@ -2207,14 +2298,22 @@ bfad_iocmd_qos(struct bfad_s *bfad, void *cmd, unsigned int v_cmd) spin_lock_irqsave(&bfad->bfad_lock, flags); if (bfa_ioc_get_type(&bfad->bfa.ioc) == BFA_IOC_TYPE_FC) { - if (v_cmd == IOCMD_QOS_ENABLE) - fcport->cfg.qos_enabled = BFA_TRUE; - else if (v_cmd == IOCMD_QOS_DISABLE) - fcport->cfg.qos_enabled = BFA_FALSE; + if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) && + (fcport->topology == BFA_PORT_TOPOLOGY_LOOP)) + iocmd->status = BFA_STATUS_TOPOLOGY_LOOP; + else { + if (v_cmd == IOCMD_QOS_ENABLE) + fcport->cfg.qos_enabled = BFA_TRUE; + else if (v_cmd == IOCMD_QOS_DISABLE) { + fcport->cfg.qos_enabled = BFA_FALSE; + fcport->cfg.qos_bw.high = BFA_QOS_BW_HIGH; + fcport->cfg.qos_bw.med = BFA_QOS_BW_MED; + fcport->cfg.qos_bw.low = BFA_QOS_BW_LOW; + } + } } spin_unlock_irqrestore(&bfad->bfad_lock, flags); - iocmd->status = BFA_STATUS_OK; return 0; } @@ -2226,11 +2325,21 @@ bfad_iocmd_qos_get_attr(struct bfad_s *bfad, void *cmd) unsigned long flags; spin_lock_irqsave(&bfad->bfad_lock, flags); - iocmd->attr.state = fcport->qos_attr.state; - iocmd->attr.total_bb_cr = be32_to_cpu(fcport->qos_attr.total_bb_cr); + if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) && + (fcport->topology == BFA_PORT_TOPOLOGY_LOOP)) + iocmd->status = BFA_STATUS_TOPOLOGY_LOOP; + else { + iocmd->attr.state = fcport->qos_attr.state; + iocmd->attr.total_bb_cr = + be32_to_cpu(fcport->qos_attr.total_bb_cr); + iocmd->attr.qos_bw.high = fcport->cfg.qos_bw.high; + iocmd->attr.qos_bw.med = fcport->cfg.qos_bw.med; + iocmd->attr.qos_bw.low = fcport->cfg.qos_bw.low; + iocmd->attr.qos_bw_op = fcport->qos_attr.qos_bw_op; + iocmd->status = BFA_STATUS_OK; + } spin_unlock_irqrestore(&bfad->bfad_lock, flags); - iocmd->status = BFA_STATUS_OK; return 0; } @@ -2274,6 +2383,7 @@ bfad_iocmd_qos_get_stats(struct bfad_s *bfad, void *cmd) struct bfad_hal_comp fcomp; unsigned long flags; struct bfa_cb_pending_q_s cb_qe; + struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa); init_completion(&fcomp.comp); bfa_pending_q_init(&cb_qe, (bfa_cb_cbfn_t)bfad_hcb_comp, @@ -2281,7 +2391,11 @@ bfad_iocmd_qos_get_stats(struct bfad_s *bfad, void *cmd) spin_lock_irqsave(&bfad->bfad_lock, flags); WARN_ON(!bfa_ioc_get_fcmode(&bfad->bfa.ioc)); - iocmd->status = bfa_fcport_get_stats(&bfad->bfa, &cb_qe); + if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) && + (fcport->topology == BFA_PORT_TOPOLOGY_LOOP)) + iocmd->status = BFA_STATUS_TOPOLOGY_LOOP; + else + iocmd->status = bfa_fcport_get_stats(&bfad->bfa, &cb_qe); spin_unlock_irqrestore(&bfad->bfad_lock, flags); if (iocmd->status != BFA_STATUS_OK) { bfa_trc(bfad, iocmd->status); @@ -2300,6 +2414,7 @@ bfad_iocmd_qos_reset_stats(struct bfad_s *bfad, void *cmd) struct bfad_hal_comp fcomp; unsigned long flags; struct bfa_cb_pending_q_s cb_qe; + struct bfa_fcport_s *fcport = BFA_FCPORT_MOD(&bfad->bfa); init_completion(&fcomp.comp); bfa_pending_q_init(&cb_qe, (bfa_cb_cbfn_t)bfad_hcb_comp, @@ -2307,7 +2422,11 @@ bfad_iocmd_qos_reset_stats(struct bfad_s *bfad, void *cmd) spin_lock_irqsave(&bfad->bfad_lock, flags); WARN_ON(!bfa_ioc_get_fcmode(&bfad->bfa.ioc)); - iocmd->status = bfa_fcport_clear_stats(&bfad->bfa, &cb_qe); + if ((fcport->cfg.topology == BFA_PORT_TOPOLOGY_LOOP) && + (fcport->topology == BFA_PORT_TOPOLOGY_LOOP)) + iocmd->status = BFA_STATUS_TOPOLOGY_LOOP; + else + iocmd->status = bfa_fcport_clear_stats(&bfad->bfa, &cb_qe); spin_unlock_irqrestore(&bfad->bfad_lock, flags); if (iocmd->status != BFA_STATUS_OK) { bfa_trc(bfad, iocmd->status); @@ -2435,6 +2554,139 @@ bfad_iocmd_fcpim_cfg_lunmask(struct bfad_s *bfad, void *cmd, unsigned int v_cmd) return 0; } +int +bfad_iocmd_fcpim_throttle_query(struct bfad_s *bfad, void *cmd) +{ + struct bfa_bsg_fcpim_throttle_s *iocmd = + (struct bfa_bsg_fcpim_throttle_s *)cmd; + unsigned long flags; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + iocmd->status = bfa_fcpim_throttle_get(&bfad->bfa, + (void *)&iocmd->throttle); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + return 0; +} + +int +bfad_iocmd_fcpim_throttle_set(struct bfad_s *bfad, void *cmd) +{ + struct bfa_bsg_fcpim_throttle_s *iocmd = + (struct bfa_bsg_fcpim_throttle_s *)cmd; + unsigned long flags; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + iocmd->status = bfa_fcpim_throttle_set(&bfad->bfa, + iocmd->throttle.cfg_value); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + return 0; +} + +int +bfad_iocmd_tfru_read(struct bfad_s *bfad, void *cmd) +{ + struct bfa_bsg_tfru_s *iocmd = + (struct bfa_bsg_tfru_s *)cmd; + struct bfad_hal_comp fcomp; + unsigned long flags = 0; + + init_completion(&fcomp.comp); + spin_lock_irqsave(&bfad->bfad_lock, flags); + iocmd->status = bfa_tfru_read(BFA_FRU(&bfad->bfa), + &iocmd->data, iocmd->len, iocmd->offset, + bfad_hcb_comp, &fcomp); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + if (iocmd->status == BFA_STATUS_OK) { + wait_for_completion(&fcomp.comp); + iocmd->status = fcomp.status; + } + + return 0; +} + +int +bfad_iocmd_tfru_write(struct bfad_s *bfad, void *cmd) +{ + struct bfa_bsg_tfru_s *iocmd = + (struct bfa_bsg_tfru_s *)cmd; + struct bfad_hal_comp fcomp; + unsigned long flags = 0; + + init_completion(&fcomp.comp); + spin_lock_irqsave(&bfad->bfad_lock, flags); + iocmd->status = bfa_tfru_write(BFA_FRU(&bfad->bfa), + &iocmd->data, iocmd->len, iocmd->offset, + bfad_hcb_comp, &fcomp); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + if (iocmd->status == BFA_STATUS_OK) { + wait_for_completion(&fcomp.comp); + iocmd->status = fcomp.status; + } + + return 0; +} + +int +bfad_iocmd_fruvpd_read(struct bfad_s *bfad, void *cmd) +{ + struct bfa_bsg_fruvpd_s *iocmd = + (struct bfa_bsg_fruvpd_s *)cmd; + struct bfad_hal_comp fcomp; + unsigned long flags = 0; + + init_completion(&fcomp.comp); + spin_lock_irqsave(&bfad->bfad_lock, flags); + iocmd->status = bfa_fruvpd_read(BFA_FRU(&bfad->bfa), + &iocmd->data, iocmd->len, iocmd->offset, + bfad_hcb_comp, &fcomp); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + if (iocmd->status == BFA_STATUS_OK) { + wait_for_completion(&fcomp.comp); + iocmd->status = fcomp.status; + } + + return 0; +} + +int +bfad_iocmd_fruvpd_update(struct bfad_s *bfad, void *cmd) +{ + struct bfa_bsg_fruvpd_s *iocmd = + (struct bfa_bsg_fruvpd_s *)cmd; + struct bfad_hal_comp fcomp; + unsigned long flags = 0; + + init_completion(&fcomp.comp); + spin_lock_irqsave(&bfad->bfad_lock, flags); + iocmd->status = bfa_fruvpd_update(BFA_FRU(&bfad->bfa), + &iocmd->data, iocmd->len, iocmd->offset, + bfad_hcb_comp, &fcomp); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + if (iocmd->status == BFA_STATUS_OK) { + wait_for_completion(&fcomp.comp); + iocmd->status = fcomp.status; + } + + return 0; +} + +int +bfad_iocmd_fruvpd_get_max_size(struct bfad_s *bfad, void *cmd) +{ + struct bfa_bsg_fruvpd_max_size_s *iocmd = + (struct bfa_bsg_fruvpd_max_size_s *)cmd; + unsigned long flags = 0; + + spin_lock_irqsave(&bfad->bfad_lock, flags); + iocmd->status = bfa_fruvpd_get_max_size(BFA_FRU(&bfad->bfa), + &iocmd->max_size); + spin_unlock_irqrestore(&bfad->bfad_lock, flags); + + return 0; +} + static int bfad_iocmd_handler(struct bfad_s *bfad, unsigned int cmd, void *iocmd, unsigned int payload_len) @@ -2660,6 +2912,13 @@ bfad_iocmd_handler(struct bfad_s *bfad, unsigned int cmd, void *iocmd, case IOCMD_DIAG_LB_STAT: rc = bfad_iocmd_diag_lb_stat(bfad, iocmd); break; + case IOCMD_DIAG_DPORT_ENABLE: + case IOCMD_DIAG_DPORT_DISABLE: + rc = bfad_iocmd_diag_cfg_dport(bfad, cmd, iocmd); + break; + case IOCMD_DIAG_DPORT_GET_STATE: + rc = bfad_iocmd_diag_dport_get_state(bfad, iocmd); + break; case IOCMD_PHY_GET_ATTR: rc = bfad_iocmd_phy_get_attr(bfad, iocmd); break; @@ -2741,6 +3000,9 @@ bfad_iocmd_handler(struct bfad_s *bfad, unsigned int cmd, void *iocmd, case IOCMD_QOS_RESET_STATS: rc = bfad_iocmd_qos_reset_stats(bfad, iocmd); break; + case IOCMD_QOS_SET_BW: + rc = bfad_iocmd_qos_set_bw(bfad, iocmd); + break; case IOCMD_VF_GET_STATS: rc = bfad_iocmd_vf_get_stats(bfad, iocmd); break; @@ -2759,6 +3021,29 @@ bfad_iocmd_handler(struct bfad_s *bfad, unsigned int cmd, void *iocmd, case IOCMD_FCPIM_LUNMASK_DELETE: rc = bfad_iocmd_fcpim_cfg_lunmask(bfad, iocmd, cmd); break; + case IOCMD_FCPIM_THROTTLE_QUERY: + rc = bfad_iocmd_fcpim_throttle_query(bfad, iocmd); + break; + case IOCMD_FCPIM_THROTTLE_SET: + rc = bfad_iocmd_fcpim_throttle_set(bfad, iocmd); + break; + /* TFRU */ + case IOCMD_TFRU_READ: + rc = bfad_iocmd_tfru_read(bfad, iocmd); + break; + case IOCMD_TFRU_WRITE: + rc = bfad_iocmd_tfru_write(bfad, iocmd); + break; + /* FRU */ + case IOCMD_FRUVPD_READ: + rc = bfad_iocmd_fruvpd_read(bfad, iocmd); + break; + case IOCMD_FRUVPD_UPDATE: + rc = bfad_iocmd_fruvpd_update(bfad, iocmd); + break; + case IOCMD_FRUVPD_GET_MAX_SIZE: + rc = bfad_iocmd_fruvpd_get_max_size(bfad, iocmd); + break; default: rc = -EINVAL; break; diff --git a/drivers/scsi/bfa/bfad_bsg.h b/drivers/scsi/bfa/bfad_bsg.h index 8c569ddb750..15e1fc8e796 100644 --- a/drivers/scsi/bfa/bfad_bsg.h +++ b/drivers/scsi/bfa/bfad_bsg.h @@ -141,6 +141,17 @@ enum { IOCMD_FCPIM_LUNMASK_QUERY, IOCMD_FCPIM_LUNMASK_ADD, IOCMD_FCPIM_LUNMASK_DELETE, + IOCMD_DIAG_DPORT_ENABLE, + IOCMD_DIAG_DPORT_DISABLE, + IOCMD_DIAG_DPORT_GET_STATE, + IOCMD_QOS_SET_BW, + IOCMD_FCPIM_THROTTLE_QUERY, + IOCMD_FCPIM_THROTTLE_SET, + IOCMD_TFRU_READ, + IOCMD_TFRU_WRITE, + IOCMD_FRUVPD_READ, + IOCMD_FRUVPD_UPDATE, + IOCMD_FRUVPD_GET_MAX_SIZE, }; struct bfa_bsg_gen_s { @@ -463,7 +474,8 @@ struct bfa_bsg_pcifn_s { bfa_status_t status; u16 bfad_num; u16 pcifn_id; - u32 bandwidth; + u16 bw_min; + u16 bw_max; u8 port; enum bfi_pcifn_class pcifn_class; u8 rsvd[1]; @@ -613,6 +625,13 @@ struct bfa_bsg_diag_lb_stat_s { u16 rsvd; }; +struct bfa_bsg_diag_dport_get_state_s { + bfa_status_t status; + u16 bfad_num; + u16 rsvd; + enum bfa_dport_state state; +}; + struct bfa_bsg_phy_attr_s { bfa_status_t status; u16 bfad_num; @@ -694,6 +713,13 @@ struct bfa_bsg_qos_vc_attr_s { struct bfa_qos_vc_attr_s attr; }; +struct bfa_bsg_qos_bw_s { + bfa_status_t status; + u16 bfad_num; + u16 rsvd; + struct bfa_qos_bw_s qos_bw; +}; + struct bfa_bsg_vf_stats_s { bfa_status_t status; u16 bfad_num; @@ -722,6 +748,41 @@ struct bfa_bsg_fcpim_lunmask_s { struct scsi_lun lun; }; +struct bfa_bsg_fcpim_throttle_s { + bfa_status_t status; + u16 bfad_num; + u16 vf_id; + struct bfa_defs_fcpim_throttle_s throttle; +}; + +#define BFA_TFRU_DATA_SIZE 64 +#define BFA_MAX_FRUVPD_TRANSFER_SIZE 0x1000 + +struct bfa_bsg_tfru_s { + bfa_status_t status; + u16 bfad_num; + u16 rsvd; + u32 offset; + u32 len; + u8 data[BFA_TFRU_DATA_SIZE]; +}; + +struct bfa_bsg_fruvpd_s { + bfa_status_t status; + u16 bfad_num; + u16 rsvd; + u32 offset; + u32 len; + u8 data[BFA_MAX_FRUVPD_TRANSFER_SIZE]; +}; + +struct bfa_bsg_fruvpd_max_size_s { + bfa_status_t status; + u16 bfad_num; + u16 rsvd; + u32 max_size; +}; + struct bfa_bsg_fcpt_s { bfa_status_t status; u16 vf_id; diff --git a/drivers/scsi/bfa/bfad_drv.h b/drivers/scsi/bfa/bfad_drv.h index 1840651ce1d..0c64a04f01f 100644 --- a/drivers/scsi/bfa/bfad_drv.h +++ b/drivers/scsi/bfa/bfad_drv.h @@ -57,7 +57,7 @@ #ifdef BFA_DRIVER_VERSION #define BFAD_DRIVER_VERSION BFA_DRIVER_VERSION #else -#define BFAD_DRIVER_VERSION "3.1.2.0" +#define BFAD_DRIVER_VERSION "3.1.2.1" #endif #define BFAD_PROTO_NAME FCPI_NAME diff --git a/drivers/scsi/bfa/bfi.h b/drivers/scsi/bfa/bfi.h index b2ba0b2e91b..57b146bca18 100644 --- a/drivers/scsi/bfa/bfi.h +++ b/drivers/scsi/bfa/bfi.h @@ -210,7 +210,8 @@ enum bfi_mclass { BFI_MC_PORT = 21, /* Physical port */ BFI_MC_SFP = 22, /* SFP module */ BFI_MC_PHY = 25, /* External PHY message class */ - BFI_MC_MAX = 32 + BFI_MC_FRU = 34, + BFI_MC_MAX = 35 }; #define BFI_IOC_MAX_CQS 4 @@ -288,6 +289,9 @@ struct bfi_ioc_attr_s { char optrom_version[BFA_VERSION_LEN]; struct bfa_mfg_vpd_s vpd; u32 card_type; /* card type */ + u8 mfg_day; /* manufacturing day */ + u8 mfg_month; /* manufacturing month */ + u16 mfg_year; /* manufacturing year */ }; /* @@ -687,7 +691,8 @@ struct bfi_ablk_h2i_pf_req_s { u8 pcifn; u8 port; u16 pers; - u32 bw; + u16 bw_min; /* percent BW @ max speed */ + u16 bw_max; /* percent BW @ max speed */ }; /* BFI_ABLK_H2I_OPTROM_ENABLE, BFI_ABLK_H2I_OPTROM_DISABLE */ @@ -957,6 +962,7 @@ enum bfi_diag_h2i { BFI_DIAG_H2I_TEMPSENSOR = 4, BFI_DIAG_H2I_LEDTEST = 5, BFI_DIAG_H2I_QTEST = 6, + BFI_DIAG_H2I_DPORT = 7, }; enum bfi_diag_i2h { @@ -966,6 +972,7 @@ enum bfi_diag_i2h { BFI_DIAG_I2H_TEMPSENSOR = BFA_I2HM(BFI_DIAG_H2I_TEMPSENSOR), BFI_DIAG_I2H_LEDTEST = BFA_I2HM(BFI_DIAG_H2I_LEDTEST), BFI_DIAG_I2H_QTEST = BFA_I2HM(BFI_DIAG_H2I_QTEST), + BFI_DIAG_I2H_DPORT = BFA_I2HM(BFI_DIAG_H2I_DPORT), }; #define BFI_DIAG_MAX_SGES 2 @@ -1052,6 +1059,23 @@ struct bfi_diag_qtest_req_s { #define bfi_diag_qtest_rsp_t struct bfi_diag_qtest_req_s /* + * D-port test + */ +enum bfi_dport_req { + BFI_DPORT_DISABLE = 0, /* disable dport request */ + BFI_DPORT_ENABLE = 1, /* enable dport request */ +}; + +struct bfi_diag_dport_req_s { + struct bfi_mhdr_s mh; /* 4 bytes */ + u8 req; /* request 1: enable 0: disable */ + u8 status; /* reply status */ + u8 rsvd[2]; + u32 msgtag; /* msgtag for reply */ +}; +#define bfi_diag_dport_rsp_t struct bfi_diag_dport_req_s + +/* * PHY module specific */ enum bfi_phy_h2i_msgs_e { @@ -1147,6 +1171,50 @@ struct bfi_phy_write_rsp_s { u32 length; }; +enum bfi_fru_h2i_msgs { + BFI_FRUVPD_H2I_WRITE_REQ = 1, + BFI_FRUVPD_H2I_READ_REQ = 2, + BFI_TFRU_H2I_WRITE_REQ = 3, + BFI_TFRU_H2I_READ_REQ = 4, +}; + +enum bfi_fru_i2h_msgs { + BFI_FRUVPD_I2H_WRITE_RSP = BFA_I2HM(1), + BFI_FRUVPD_I2H_READ_RSP = BFA_I2HM(2), + BFI_TFRU_I2H_WRITE_RSP = BFA_I2HM(3), + BFI_TFRU_I2H_READ_RSP = BFA_I2HM(4), +}; + +/* + * FRU write request + */ +struct bfi_fru_write_req_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u8 last; + u8 rsv[3]; + u32 offset; + u32 length; + struct bfi_alen_s alen; +}; + +/* + * FRU read request + */ +struct bfi_fru_read_req_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u32 offset; + u32 length; + struct bfi_alen_s alen; +}; + +/* + * FRU response + */ +struct bfi_fru_rsp_s { + struct bfi_mhdr_s mh; /* Common msg header */ + u32 status; + u32 length; +}; #pragma pack() #endif /* __BFI_H__ */ diff --git a/drivers/scsi/bfa/bfi_ms.h b/drivers/scsi/bfa/bfi_ms.h index d4220e13caf..5ae2c167b2c 100644 --- a/drivers/scsi/bfa/bfi_ms.h +++ b/drivers/scsi/bfa/bfi_ms.h @@ -426,6 +426,7 @@ struct bfi_lps_login_req_s { u8 auth_en; u8 lps_role; u8 bb_scn; + u32 vvl_flag; }; struct bfi_lps_login_rsp_s { @@ -499,6 +500,9 @@ enum bfi_rport_i2h_msgs { BFI_RPORT_I2H_CREATE_RSP = BFA_I2HM(1), BFI_RPORT_I2H_DELETE_RSP = BFA_I2HM(2), BFI_RPORT_I2H_QOS_SCN = BFA_I2HM(3), + BFI_RPORT_I2H_LIP_SCN_ONLINE = BFA_I2HM(4), + BFI_RPORT_I2H_LIP_SCN_OFFLINE = BFA_I2HM(5), + BFI_RPORT_I2H_NO_DEV = BFA_I2HM(6), }; struct bfi_rport_create_req_s { @@ -551,6 +555,14 @@ struct bfi_rport_qos_scn_s { struct bfa_rport_qos_attr_s new_qos_attr; /* New QoS Attributes */ }; +struct bfi_rport_lip_scn_s { + struct bfi_mhdr_s mh; /*!< common msg header */ + u16 bfa_handle; /*!< host rport handle */ + u8 status; /*!< scn online status */ + u8 rsvd; + struct bfa_fcport_loop_info_s loop_info; +}; + union bfi_rport_h2i_msg_u { struct bfi_msg_s *msg; struct bfi_rport_create_req_s *create_req; @@ -563,6 +575,7 @@ union bfi_rport_i2h_msg_u { struct bfi_rport_create_rsp_s *create_rsp; struct bfi_rport_delete_rsp_s *delete_rsp; struct bfi_rport_qos_scn_s *qos_scn_evt; + struct bfi_rport_lip_scn_s *lip_scn; }; /* @@ -828,6 +841,7 @@ enum bfi_tskim_status { */ BFI_TSKIM_STS_TIMEOUT = 10, /* TM request timedout */ BFI_TSKIM_STS_ABORTED = 11, /* Aborted on host request */ + BFI_TSKIM_STS_UTAG = 12, /* unknown tag for request */ }; struct bfi_tskim_rsp_s { diff --git a/drivers/scsi/bfa/bfi_reg.h b/drivers/scsi/bfa/bfi_reg.h index ed5f159e186..99133bcf53f 100644 --- a/drivers/scsi/bfa/bfi_reg.h +++ b/drivers/scsi/bfa/bfi_reg.h @@ -338,6 +338,7 @@ enum { #define __A2T_AHB_LOAD 0x00000800 #define __WGN_READY 0x00000400 #define __GLBL_PF_VF_CFG_RDY 0x00000200 +#define CT2_NFC_STS_REG 0x00027410 #define CT2_NFC_CSR_CLR_REG 0x00027420 #define CT2_NFC_CSR_SET_REG 0x00027424 #define __HALT_NFC_CONTROLLER 0x00000002 @@ -355,6 +356,8 @@ enum { (CT2_CSI_MAC0_CONTROL_REG + \ (__n) * (CT2_CSI_MAC1_CONTROL_REG - CT2_CSI_MAC0_CONTROL_REG)) +#define CT2_NFC_FLASH_STS_REG 0x00014834 +#define __FLASH_PLL_INIT_AND_RESET_IN_PROGRESS 0x00000020 /* * Name semaphore registers based on usage */ diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index 078d262ac7c..666b7ac4475 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1643,7 +1643,7 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) skb_reset_network_header(skb); skb->mac_len = elen; skb->protocol = htons(ETH_P_FCOE); - skb->priority = port->priority; + skb->priority = fcoe->priority; if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN && fcoe->realdev->features & NETIF_F_HW_VLAN_TX) { @@ -1917,7 +1917,6 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier, struct fcoe_ctlr *ctlr; struct fcoe_interface *fcoe; struct net_device *netdev; - struct fcoe_port *port; int prio; if (entry->app.selector != DCB_APP_IDTYPE_ETHTYPE) @@ -1946,10 +1945,8 @@ static int fcoe_dcb_app_notification(struct notifier_block *notifier, entry->app.protocol == ETH_P_FCOE) ctlr->priority = prio; - if (entry->app.protocol == ETH_P_FCOE) { - port = lport_priv(ctlr->lp); - port->priority = prio; - } + if (entry->app.protocol == ETH_P_FCOE) + fcoe->priority = prio; return NOTIFY_OK; } @@ -2180,7 +2177,6 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe) u8 fup, up; struct net_device *netdev = fcoe->realdev; struct fcoe_ctlr *ctlr = fcoe_to_ctlr(fcoe); - struct fcoe_port *port = lport_priv(ctlr->lp); struct dcb_app app = { .priority = 0, .protocol = ETH_P_FCOE @@ -2202,8 +2198,8 @@ static void fcoe_dcb_create(struct fcoe_interface *fcoe) fup = dcb_getapp(netdev, &app); } - port->priority = ffs(up) ? ffs(up) - 1 : 0; - ctlr->priority = ffs(fup) ? ffs(fup) - 1 : port->priority; + fcoe->priority = ffs(up) ? ffs(up) - 1 : 0; + ctlr->priority = ffs(fup) ? ffs(fup) - 1 : fcoe->priority; } #endif } diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index a624add4f8e..b42dc32cb5e 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -71,6 +71,7 @@ do { \ * @oem: The offload exchange manager for all local port * instances associated with this port * @removed: Indicates fcoe interface removed from net device + * @priority: Priority for the FCoE packet (DCB) * This structure is 1:1 with a net device. */ struct fcoe_interface { @@ -81,6 +82,7 @@ struct fcoe_interface { struct packet_type fip_packet_type; struct fc_exch_mgr *oem; u8 removed; + u8 priority; }; #define fcoe_to_ctlr(x) \ diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index 14243fa5f8e..fcb9d0b20ee 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -851,7 +851,8 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) fc_rp_info = (struct fcp_resp_rsp_info *)(rp_ex + 1); if (flags & FCP_RSP_LEN_VAL) { respl = ntohl(rp_ex->fr_rsp_len); - if (respl != sizeof(*fc_rp_info)) + if ((respl != FCP_RESP_RSP_INFO_LEN4) && + (respl != FCP_RESP_RSP_INFO_LEN8)) goto len_err; if (fsp->wait_for_comp) { /* Abuse cdb_status for rsp code */ diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index a184c2443a6..69b59935b53 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -27,6 +27,8 @@ struct lpfc_sli2_slim; +#define ELX_MODEL_NAME_SIZE 80 + #define LPFC_PCI_DEV_LP 0x1 #define LPFC_PCI_DEV_OC 0x2 diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index b032562aa0d..ad16e54ac38 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -3935,6 +3935,12 @@ MODULE_PARM_DESC(lpfc_fcp_look_ahead, "Look ahead for completions"); # - Only meaningful if BG is turned on (lpfc_enable_bg=1). # - Allows you to ultimately specify which profiles to use # - Default will result in registering capabilities for all profiles. +# - SHOST_DIF_TYPE1_PROTECTION 1 +# HBA supports T10 DIF Type 1: HBA to Target Type 1 Protection +# - SHOST_DIX_TYPE0_PROTECTION 8 +# HBA supports DIX Type 0: Host to HBA protection only +# - SHOST_DIX_TYPE1_PROTECTION 16 +# HBA supports DIX Type 1: Host to HBA Type 1 protection # */ unsigned int lpfc_prot_mask = SHOST_DIF_TYPE1_PROTECTION | @@ -3947,7 +3953,7 @@ MODULE_PARM_DESC(lpfc_prot_mask, "host protection mask"); /* # lpfc_prot_guard: i # - Bit mask of protection guard types to register with the SCSI mid-layer -# - Guard types are currently either 1) IP checksum 2) T10-DIF CRC +# - Guard types are currently either 1) T10-DIF CRC 2) IP checksum # - Allows you to ultimately specify which profiles to use # - Default will result in registering capabilities for all guard types # diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index e470c489de0..4380a44000b 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -467,3 +467,4 @@ int lpfc_sli4_read_config(struct lpfc_hba *); void lpfc_sli4_node_prep(struct lpfc_hba *); int lpfc_sli4_xri_sgl_update(struct lpfc_hba *); void lpfc_free_sgl_list(struct lpfc_hba *, struct list_head *); +uint32_t lpfc_sli_port_speed_get(struct lpfc_hba *); diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index cfe533bc979..f19e9b6f9f1 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -809,6 +809,8 @@ lpfc_cmpl_els_flogi_nport(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, phba->fc_ratov = FF_DEF_RATOV; rc = memcmp(&vport->fc_portname, &sp->portName, sizeof(vport->fc_portname)); + memcpy(&phba->fc_fabparam, sp, sizeof(struct serv_parm)); + if (rc >= 0) { /* This side will initiate the PLOGI */ spin_lock_irq(shost->host_lock); @@ -3160,7 +3162,8 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, retry = 1; break; } - if (cmd == ELS_CMD_PLOGI) { + if ((cmd == ELS_CMD_PLOGI) || + (cmd == ELS_CMD_PRLI)) { delay = 1000; maxretry = lpfc_max_els_tries + 1; retry = 1; @@ -3305,7 +3308,7 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, ndlp->nlp_prev_state = ndlp->nlp_state; if (cmd == ELS_CMD_PRLI) lpfc_nlp_set_state(vport, ndlp, - NLP_STE_REG_LOGIN_ISSUE); + NLP_STE_PRLI_ISSUE); else lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE); diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index e9845d2ecf1..d7096ad94d3 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1506,9 +1506,10 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba, } } - /* If FCF not available return 0 */ + /* FCF not valid/available or solicitation in progress */ if (!bf_get(lpfc_fcf_record_fcf_avail, new_fcf_record) || - !bf_get(lpfc_fcf_record_fcf_valid, new_fcf_record)) + !bf_get(lpfc_fcf_record_fcf_valid, new_fcf_record) || + bf_get(lpfc_fcf_record_fcf_sol, new_fcf_record)) return 0; if (!(phba->hba_flag & HBA_FIP_SUPPORT)) { @@ -1842,6 +1843,7 @@ lpfc_sli4_log_fcf_record_info(struct lpfc_hba *phba, "\tFCF_Index : x%x\n" "\tFCF_Avail : x%x\n" "\tFCF_Valid : x%x\n" + "\tFCF_SOL : x%x\n" "\tFIP_Priority : x%x\n" "\tMAC_Provider : x%x\n" "\tLowest VLANID : x%x\n" @@ -1852,6 +1854,7 @@ lpfc_sli4_log_fcf_record_info(struct lpfc_hba *phba, bf_get(lpfc_fcf_record_fcf_index, fcf_record), bf_get(lpfc_fcf_record_fcf_avail, fcf_record), bf_get(lpfc_fcf_record_fcf_valid, fcf_record), + bf_get(lpfc_fcf_record_fcf_sol, fcf_record), fcf_record->fip_priority, bf_get(lpfc_fcf_record_mac_addr_prov, fcf_record), vlan_id, @@ -2185,12 +2188,14 @@ lpfc_mbx_cmpl_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) new_fcf_record)); lpfc_printf_log(phba, KERN_WARNING, LOG_FIP, "2781 FCF (x%x) failed connection " - "list check: (x%x/x%x)\n", + "list check: (x%x/x%x/%x)\n", bf_get(lpfc_fcf_record_fcf_index, new_fcf_record), bf_get(lpfc_fcf_record_fcf_avail, new_fcf_record), bf_get(lpfc_fcf_record_fcf_valid, + new_fcf_record), + bf_get(lpfc_fcf_record_fcf_sol, new_fcf_record)); if ((phba->fcf.fcf_flag & FCF_IN_USE) && lpfc_sli4_fcf_record_match(phba, &phba->fcf.current_rec, diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index 834b699cac7..2cdeb5434fb 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -1305,6 +1305,11 @@ struct lpfc_mbx_mq_create_ext { #define lpfc_mbx_mq_create_ext_async_evt_link_SHIFT LPFC_TRAILER_CODE_LINK #define lpfc_mbx_mq_create_ext_async_evt_link_MASK 0x00000001 #define lpfc_mbx_mq_create_ext_async_evt_link_WORD async_evt_bmap +#define LPFC_EVT_CODE_LINK_NO_LINK 0x0 +#define LPFC_EVT_CODE_LINK_10_MBIT 0x1 +#define LPFC_EVT_CODE_LINK_100_MBIT 0x2 +#define LPFC_EVT_CODE_LINK_1_GBIT 0x3 +#define LPFC_EVT_CODE_LINK_10_GBIT 0x4 #define lpfc_mbx_mq_create_ext_async_evt_fip_SHIFT LPFC_TRAILER_CODE_FCOE #define lpfc_mbx_mq_create_ext_async_evt_fip_MASK 0x00000001 #define lpfc_mbx_mq_create_ext_async_evt_fip_WORD async_evt_bmap @@ -1314,6 +1319,13 @@ struct lpfc_mbx_mq_create_ext { #define lpfc_mbx_mq_create_ext_async_evt_fc_SHIFT LPFC_TRAILER_CODE_FC #define lpfc_mbx_mq_create_ext_async_evt_fc_MASK 0x00000001 #define lpfc_mbx_mq_create_ext_async_evt_fc_WORD async_evt_bmap +#define LPFC_EVT_CODE_FC_NO_LINK 0x0 +#define LPFC_EVT_CODE_FC_1_GBAUD 0x1 +#define LPFC_EVT_CODE_FC_2_GBAUD 0x2 +#define LPFC_EVT_CODE_FC_4_GBAUD 0x4 +#define LPFC_EVT_CODE_FC_8_GBAUD 0x8 +#define LPFC_EVT_CODE_FC_10_GBAUD 0xA +#define LPFC_EVT_CODE_FC_16_GBAUD 0x10 #define lpfc_mbx_mq_create_ext_async_evt_sli_SHIFT LPFC_TRAILER_CODE_SLI #define lpfc_mbx_mq_create_ext_async_evt_sli_MASK 0x00000001 #define lpfc_mbx_mq_create_ext_async_evt_sli_WORD async_evt_bmap @@ -1695,8 +1707,14 @@ struct fcf_record { #define lpfc_fcf_record_fc_map_2_MASK 0x000000FF #define lpfc_fcf_record_fc_map_2_WORD word7 #define lpfc_fcf_record_fcf_valid_SHIFT 24 -#define lpfc_fcf_record_fcf_valid_MASK 0x000000FF +#define lpfc_fcf_record_fcf_valid_MASK 0x00000001 #define lpfc_fcf_record_fcf_valid_WORD word7 +#define lpfc_fcf_record_fcf_fc_SHIFT 25 +#define lpfc_fcf_record_fcf_fc_MASK 0x00000001 +#define lpfc_fcf_record_fcf_fc_WORD word7 +#define lpfc_fcf_record_fcf_sol_SHIFT 31 +#define lpfc_fcf_record_fcf_sol_MASK 0x00000001 +#define lpfc_fcf_record_fcf_sol_WORD word7 uint32_t word8; #define lpfc_fcf_record_fcf_index_SHIFT 0 #define lpfc_fcf_record_fcf_index_MASK 0x0000FFFF diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 8a55a586dd6..7dc4218d9c4 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -1892,8 +1892,10 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) max_speed = 4; else if (phba->lmt & LMT_2Gb) max_speed = 2; - else + else if (phba->lmt & LMT_1Gb) max_speed = 1; + else + max_speed = 0; vp = &phba->vpd; @@ -2078,9 +2080,13 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) if (descp && descp[0] == '\0') { if (oneConnect) snprintf(descp, 255, - "Emulex OneConnect %s, %s Initiator, Port %s", + "Emulex OneConnect %s, %s Initiator %s", m.name, m.function, phba->Port); + else if (max_speed == 0) + snprintf(descp, 255, + "Emulex %s %s %s ", + m.name, m.bus, m.function); else snprintf(descp, 255, "Emulex %s %d%s %s %s", @@ -3502,6 +3508,119 @@ lpfc_sli4_parse_latt_link_speed(struct lpfc_hba *phba, } /** + * lpfc_sli_port_speed_get - Get sli3 link speed code to link speed + * @phba: pointer to lpfc hba data structure. + * + * This routine is to get an SLI3 FC port's link speed in Mbps. + * + * Return: link speed in terms of Mbps. + **/ +uint32_t +lpfc_sli_port_speed_get(struct lpfc_hba *phba) +{ + uint32_t link_speed; + + if (!lpfc_is_link_up(phba)) + return 0; + + switch (phba->fc_linkspeed) { + case LPFC_LINK_SPEED_1GHZ: + link_speed = 1000; + break; + case LPFC_LINK_SPEED_2GHZ: + link_speed = 2000; + break; + case LPFC_LINK_SPEED_4GHZ: + link_speed = 4000; + break; + case LPFC_LINK_SPEED_8GHZ: + link_speed = 8000; + break; + case LPFC_LINK_SPEED_10GHZ: + link_speed = 10000; + break; + case LPFC_LINK_SPEED_16GHZ: + link_speed = 16000; + break; + default: + link_speed = 0; + } + return link_speed; +} + +/** + * lpfc_sli4_port_speed_parse - Parse async evt link speed code to link speed + * @phba: pointer to lpfc hba data structure. + * @evt_code: asynchronous event code. + * @speed_code: asynchronous event link speed code. + * + * This routine is to parse the giving SLI4 async event link speed code into + * value of Mbps for the link speed. + * + * Return: link speed in terms of Mbps. + **/ +static uint32_t +lpfc_sli4_port_speed_parse(struct lpfc_hba *phba, uint32_t evt_code, + uint8_t speed_code) +{ + uint32_t port_speed; + + switch (evt_code) { + case LPFC_TRAILER_CODE_LINK: + switch (speed_code) { + case LPFC_EVT_CODE_LINK_NO_LINK: + port_speed = 0; + break; + case LPFC_EVT_CODE_LINK_10_MBIT: + port_speed = 10; + break; + case LPFC_EVT_CODE_LINK_100_MBIT: + port_speed = 100; + break; + case LPFC_EVT_CODE_LINK_1_GBIT: + port_speed = 1000; + break; + case LPFC_EVT_CODE_LINK_10_GBIT: + port_speed = 10000; + break; + default: + port_speed = 0; + } + break; + case LPFC_TRAILER_CODE_FC: + switch (speed_code) { + case LPFC_EVT_CODE_FC_NO_LINK: + port_speed = 0; + break; + case LPFC_EVT_CODE_FC_1_GBAUD: + port_speed = 1000; + break; + case LPFC_EVT_CODE_FC_2_GBAUD: + port_speed = 2000; + break; + case LPFC_EVT_CODE_FC_4_GBAUD: + port_speed = 4000; + break; + case LPFC_EVT_CODE_FC_8_GBAUD: + port_speed = 8000; + break; + case LPFC_EVT_CODE_FC_10_GBAUD: + port_speed = 10000; + break; + case LPFC_EVT_CODE_FC_16_GBAUD: + port_speed = 16000; + break; + default: + port_speed = 0; + } + break; + default: + port_speed = 0; + } + return port_speed; +} + +/** * lpfc_sli4_async_link_evt - Process the asynchronous FCoE link event * @phba: pointer to lpfc hba data structure. * @acqe_link: pointer to the async link completion queue entry. @@ -3558,7 +3677,8 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba, /* Keep the link status for extra SLI4 state machine reference */ phba->sli4_hba.link_state.speed = - bf_get(lpfc_acqe_link_speed, acqe_link); + lpfc_sli4_port_speed_parse(phba, LPFC_TRAILER_CODE_LINK, + bf_get(lpfc_acqe_link_speed, acqe_link)); phba->sli4_hba.link_state.duplex = bf_get(lpfc_acqe_link_duplex, acqe_link); phba->sli4_hba.link_state.status = @@ -3570,7 +3690,8 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba, phba->sli4_hba.link_state.fault = bf_get(lpfc_acqe_link_fault, acqe_link); phba->sli4_hba.link_state.logical_speed = - bf_get(lpfc_acqe_logical_link_speed, acqe_link); + bf_get(lpfc_acqe_logical_link_speed, acqe_link) * 10; + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, "2900 Async FC/FCoE Link event - Speed:%dGBit " "duplex:x%x LA Type:x%x Port Type:%d Port Number:%d " @@ -3580,7 +3701,7 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba, phba->sli4_hba.link_state.status, phba->sli4_hba.link_state.type, phba->sli4_hba.link_state.number, - phba->sli4_hba.link_state.logical_speed * 10, + phba->sli4_hba.link_state.logical_speed, phba->sli4_hba.link_state.fault); /* * For FC Mode: issue the READ_TOPOLOGY mailbox command to fetch @@ -3652,7 +3773,8 @@ lpfc_sli4_async_fc_evt(struct lpfc_hba *phba, struct lpfc_acqe_fc_la *acqe_fc) } /* Keep the link status for extra SLI4 state machine reference */ phba->sli4_hba.link_state.speed = - bf_get(lpfc_acqe_fc_la_speed, acqe_fc); + lpfc_sli4_port_speed_parse(phba, LPFC_TRAILER_CODE_FC, + bf_get(lpfc_acqe_fc_la_speed, acqe_fc)); phba->sli4_hba.link_state.duplex = LPFC_ASYNC_LINK_DUPLEX_FULL; phba->sli4_hba.link_state.topology = bf_get(lpfc_acqe_fc_la_topology, acqe_fc); @@ -3665,7 +3787,7 @@ lpfc_sli4_async_fc_evt(struct lpfc_hba *phba, struct lpfc_acqe_fc_la *acqe_fc) phba->sli4_hba.link_state.fault = bf_get(lpfc_acqe_link_fault, acqe_fc); phba->sli4_hba.link_state.logical_speed = - bf_get(lpfc_acqe_fc_la_llink_spd, acqe_fc); + bf_get(lpfc_acqe_fc_la_llink_spd, acqe_fc) * 10; lpfc_printf_log(phba, KERN_INFO, LOG_SLI, "2896 Async FC event - Speed:%dGBaud Topology:x%x " "LA Type:x%x Port Type:%d Port Number:%d Logical speed:" @@ -3675,7 +3797,7 @@ lpfc_sli4_async_fc_evt(struct lpfc_hba *phba, struct lpfc_acqe_fc_la *acqe_fc) phba->sli4_hba.link_state.status, phba->sli4_hba.link_state.type, phba->sli4_hba.link_state.number, - phba->sli4_hba.link_state.logical_speed * 10, + phba->sli4_hba.link_state.logical_speed, phba->sli4_hba.link_state.fault); pmb = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmb) { @@ -3783,14 +3905,18 @@ lpfc_sli4_async_sli_evt(struct lpfc_hba *phba, struct lpfc_acqe_sli *acqe_sli) case LPFC_SLI_EVENT_STATUS_VALID: return; /* no message if the sfp is okay */ case LPFC_SLI_EVENT_STATUS_NOT_PRESENT: - sprintf(message, "Not installed"); + sprintf(message, "Optics faulted/incorrectly installed/not " \ + "installed - Reseat optics, if issue not " + "resolved, replace."); break; case LPFC_SLI_EVENT_STATUS_WRONG_TYPE: sprintf(message, - "Optics of two types installed"); + "Optics of two types installed - Remove one optic or " \ + "install matching pair of optics."); break; case LPFC_SLI_EVENT_STATUS_UNSUPPORTED: - sprintf(message, "Incompatible optics"); + sprintf(message, "Incompatible optics - Replace with " \ + "compatible optics for card to function."); break; default: /* firmware is reporting a status we don't know about */ @@ -4161,11 +4287,11 @@ lpfc_sli4_async_grp5_evt(struct lpfc_hba *phba, phba->fcoe_eventtag = acqe_grp5->event_tag; prev_ll_spd = phba->sli4_hba.link_state.logical_speed; phba->sli4_hba.link_state.logical_speed = - (bf_get(lpfc_acqe_grp5_llink_spd, acqe_grp5)); + (bf_get(lpfc_acqe_grp5_llink_spd, acqe_grp5)) * 10; lpfc_printf_log(phba, KERN_INFO, LOG_SLI, "2789 GRP5 Async Event: Updating logical link speed " - "from %dMbps to %dMbps\n", (prev_ll_spd * 10), - (phba->sli4_hba.link_state.logical_speed*10)); + "from %dMbps to %dMbps\n", prev_ll_spd, + phba->sli4_hba.link_state.logical_speed); } /** @@ -4947,7 +5073,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba) } phba->sli4_hba.msix_entries = kzalloc((sizeof(struct msix_entry) * - phba->sli4_hba.cfg_eqn), GFP_KERNEL); + phba->cfg_fcp_io_channel), GFP_KERNEL); if (!phba->sli4_hba.msix_entries) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2573 Failed allocate memory for msi-x " @@ -6559,7 +6685,8 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) i++; } if (i < cfg_fcp_io_channel) { - lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, + lpfc_printf_log(phba, + KERN_ERR, LOG_INIT, "3188 Reducing IO channels to match number of " "CPUs: from %d to %d\n", cfg_fcp_io_channel, i); cfg_fcp_io_channel = i; @@ -6567,8 +6694,8 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) if (cfg_fcp_io_channel > phba->sli4_hba.max_cfg_param.max_eq) { - cfg_fcp_io_channel = phba->sli4_hba.max_cfg_param.max_eq; - if (cfg_fcp_io_channel < LPFC_FCP_IO_CHAN_MIN) { + if (phba->sli4_hba.max_cfg_param.max_eq < + LPFC_FCP_IO_CHAN_MIN) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "2574 Not enough EQs (%d) from the " "pci function for supporting FCP " @@ -6577,13 +6704,12 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) phba->cfg_fcp_io_channel); goto out_error; } - lpfc_printf_log(phba, KERN_WARNING, LOG_INIT, - "2575 Not enough EQs (%d) from the pci " - "function for supporting the requested " - "FCP EQs (%d), the actual FCP EQs can " - "be supported: %d\n", - phba->sli4_hba.max_cfg_param.max_eq, - phba->cfg_fcp_io_channel, cfg_fcp_io_channel); + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "2575 Reducing IO channels to match number of " + "available EQs: from %d to %d\n", + cfg_fcp_io_channel, + phba->sli4_hba.max_cfg_param.max_eq); + cfg_fcp_io_channel = phba->sli4_hba.max_cfg_param.max_eq; } /* Eventually cfg_fcp_eq_count / cfg_fcp_wq_count will be depricated */ @@ -6592,7 +6718,6 @@ lpfc_sli4_queue_verify(struct lpfc_hba *phba) phba->cfg_fcp_eq_count = cfg_fcp_io_channel; phba->cfg_fcp_wq_count = cfg_fcp_io_channel; phba->cfg_fcp_io_channel = cfg_fcp_io_channel; - phba->sli4_hba.cfg_eqn = cfg_fcp_io_channel; /* Get EQ depth from module parameter, fake the default for now */ phba->sli4_hba.eq_esize = LPFC_EQE_SIZE_4B; @@ -8095,11 +8220,11 @@ lpfc_sli4_enable_msix(struct lpfc_hba *phba) int vectors, rc, index; /* Set up MSI-X multi-message vectors */ - for (index = 0; index < phba->sli4_hba.cfg_eqn; index++) + for (index = 0; index < phba->cfg_fcp_io_channel; index++) phba->sli4_hba.msix_entries[index].entry = index; /* Configure MSI-X capability structure */ - vectors = phba->sli4_hba.cfg_eqn; + vectors = phba->cfg_fcp_io_channel; enable_msix_vectors: rc = pci_enable_msix(phba->pcidev, phba->sli4_hba.msix_entries, vectors); @@ -8142,8 +8267,14 @@ enable_msix_vectors: goto cfg_fail_out; } } - phba->sli4_hba.msix_vec_nr = vectors; + if (vectors != phba->cfg_fcp_io_channel) { + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3238 Reducing IO channels to match number of " + "MSI-X vectors, requested %d got %d\n", + phba->cfg_fcp_io_channel, vectors); + phba->cfg_fcp_io_channel = vectors; + } return rc; cfg_fail_out: @@ -8171,7 +8302,7 @@ lpfc_sli4_disable_msix(struct lpfc_hba *phba) int index; /* Free up MSI-X multi-message vectors */ - for (index = 0; index < phba->sli4_hba.msix_vec_nr; index++) + for (index = 0; index < phba->cfg_fcp_io_channel; index++) free_irq(phba->sli4_hba.msix_entries[index].vector, &phba->sli4_hba.fcp_eq_hdl[index]); @@ -9304,23 +9435,28 @@ lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba) /** * lpfc_write_firmware - attempt to write a firmware image to the port - * @phba: pointer to lpfc hba data structure. * @fw: pointer to firmware image returned from request_firmware. + * @phba: pointer to lpfc hba data structure. * - * returns the number of bytes written if write is successful. - * returns a negative error value if there were errors. - * returns 0 if firmware matches currently active firmware on port. **/ -int -lpfc_write_firmware(struct lpfc_hba *phba, const struct firmware *fw) +static void +lpfc_write_firmware(const struct firmware *fw, void *context) { + struct lpfc_hba *phba = (struct lpfc_hba *)context; char fwrev[FW_REV_STR_SIZE]; - struct lpfc_grp_hdr *image = (struct lpfc_grp_hdr *)fw->data; + struct lpfc_grp_hdr *image; struct list_head dma_buffer_list; int i, rc = 0; struct lpfc_dmabuf *dmabuf, *next; uint32_t offset = 0, temp_offset = 0; + /* It can be null, sanity check */ + if (!fw) { + rc = -ENXIO; + goto out; + } + image = (struct lpfc_grp_hdr *)fw->data; + INIT_LIST_HEAD(&dma_buffer_list); if ((be32_to_cpu(image->magic_number) != LPFC_GROUP_OJECT_MAGIC_NUM) || (bf_get_be32(lpfc_grp_hdr_file_type, image) != @@ -9333,12 +9469,13 @@ lpfc_write_firmware(struct lpfc_hba *phba, const struct firmware *fw) be32_to_cpu(image->magic_number), bf_get_be32(lpfc_grp_hdr_file_type, image), bf_get_be32(lpfc_grp_hdr_id, image)); - return -EINVAL; + rc = -EINVAL; + goto release_out; } lpfc_decode_firmware_rev(phba, fwrev, 1); if (strncmp(fwrev, image->revision, strnlen(image->revision, 16))) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3023 Updating Firmware. Current Version:%s " + "3023 Updating Firmware, Current Version:%s " "New Version:%s\n", fwrev, image->revision); for (i = 0; i < LPFC_MBX_WR_CONFIG_MAX_BDE; i++) { @@ -9346,7 +9483,7 @@ lpfc_write_firmware(struct lpfc_hba *phba, const struct firmware *fw) GFP_KERNEL); if (!dmabuf) { rc = -ENOMEM; - goto out; + goto release_out; } dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev, SLI4_PAGE_SIZE, @@ -9355,7 +9492,7 @@ lpfc_write_firmware(struct lpfc_hba *phba, const struct firmware *fw) if (!dmabuf->virt) { kfree(dmabuf); rc = -ENOMEM; - goto out; + goto release_out; } list_add_tail(&dmabuf->list, &dma_buffer_list); } @@ -9375,23 +9512,24 @@ lpfc_write_firmware(struct lpfc_hba *phba, const struct firmware *fw) } rc = lpfc_wr_object(phba, &dma_buffer_list, (fw->size - offset), &offset); - if (rc) { - lpfc_printf_log(phba, KERN_ERR, LOG_INIT, - "3024 Firmware update failed. " - "%d\n", rc); - goto out; - } + if (rc) + goto release_out; } rc = offset; } -out: + +release_out: list_for_each_entry_safe(dmabuf, next, &dma_buffer_list, list) { list_del(&dmabuf->list); dma_free_coherent(&phba->pcidev->dev, SLI4_PAGE_SIZE, dmabuf->virt, dmabuf->phys); kfree(dmabuf); } - return rc; + release_firmware(fw); +out: + lpfc_printf_log(phba, KERN_ERR, LOG_INIT, + "3024 Firmware update done: %d.", rc); + return; } /** @@ -9418,12 +9556,11 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) struct lpfc_hba *phba; struct lpfc_vport *vport = NULL; struct Scsi_Host *shost = NULL; - int error; + int error, ret; uint32_t cfg_mode, intr_mode; int mcnt; int adjusted_fcp_io_channel; - const struct firmware *fw; - uint8_t file_name[16]; + uint8_t file_name[ELX_MODEL_NAME_SIZE]; /* Allocate memory for HBA structure */ phba = lpfc_hba_alloc(pdev); @@ -9525,9 +9662,6 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) /* Default to single EQ for non-MSI-X */ if (phba->intr_type != MSIX) adjusted_fcp_io_channel = 1; - else if (phba->sli4_hba.msix_vec_nr < - phba->cfg_fcp_io_channel) - adjusted_fcp_io_channel = phba->sli4_hba.msix_vec_nr; else adjusted_fcp_io_channel = phba->cfg_fcp_io_channel; phba->cfg_fcp_io_channel = adjusted_fcp_io_channel; @@ -9572,12 +9706,12 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid) /* check for firmware upgrade or downgrade (if_type 2 only) */ if (bf_get(lpfc_sli_intf_if_type, &phba->sli4_hba.sli_intf) == LPFC_SLI_INTF_IF_TYPE_2) { - snprintf(file_name, 16, "%s.grp", phba->ModelName); - error = request_firmware(&fw, file_name, &phba->pcidev->dev); - if (!error) { - lpfc_write_firmware(phba, fw); - release_firmware(fw); - } + snprintf(file_name, ELX_MODEL_NAME_SIZE, "%s.grp", + phba->ModelName); + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + file_name, &phba->pcidev->dev, + GFP_KERNEL, (void *)phba, + lpfc_write_firmware); } /* Check if there are static vports to be created. */ diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 64013f3097a..7f45ac9964a 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -3829,9 +3829,9 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, cmd->scsi_done(cmd); if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) { - spin_lock_irq(&phba->hbalock); + spin_lock_irqsave(&phba->hbalock, flags); lpfc_cmd->pCmd = NULL; - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, flags); /* * If there is a thread waiting for command completion @@ -3871,9 +3871,9 @@ lpfc_scsi_cmd_iocb_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *pIocbIn, } } - spin_lock_irq(&phba->hbalock); + spin_lock_irqsave(&phba->hbalock, flags); lpfc_cmd->pCmd = NULL; - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, flags); /* * If there is a thread waiting for command completion @@ -4163,7 +4163,7 @@ lpfc_info(struct Scsi_Host *host) { struct lpfc_vport *vport = (struct lpfc_vport *) host->hostdata; struct lpfc_hba *phba = vport->phba; - int len; + int len, link_speed = 0; static char lpfcinfobuf[384]; memset(lpfcinfobuf,0,384); @@ -4184,12 +4184,18 @@ lpfc_info(struct Scsi_Host *host) phba->Port); } len = strlen(lpfcinfobuf); - if (phba->sli4_hba.link_state.logical_speed) { - snprintf(lpfcinfobuf + len, - 384-len, - " Logical Link Speed: %d Mbps", - phba->sli4_hba.link_state.logical_speed * 10); + if (phba->sli_rev <= LPFC_SLI_REV3) { + link_speed = lpfc_sli_port_speed_get(phba); + } else { + if (phba->sli4_hba.link_state.logical_speed) + link_speed = + phba->sli4_hba.link_state.logical_speed; + else + link_speed = phba->sli4_hba.link_state.speed; } + if (link_speed != 0) + snprintf(lpfcinfobuf + len, 384-len, + " Logical Link Speed: %d Mbps", link_speed); } return lpfcinfobuf; } @@ -4398,16 +4404,17 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) struct lpfc_scsi_buf *lpfc_cmd; IOCB_t *cmd, *icmd; int ret = SUCCESS, status = 0; + unsigned long flags; DECLARE_WAIT_QUEUE_HEAD_ONSTACK(waitq); status = fc_block_scsi_eh(cmnd); if (status != 0 && status != SUCCESS) return status; - spin_lock_irq(&phba->hbalock); + spin_lock_irqsave(&phba->hbalock, flags); /* driver queued commands are in process of being flushed */ if (phba->hba_flag & HBA_FCP_IOQ_FLUSH) { - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "3168 SCSI Layer abort requested I/O has been " "flushed by LLD.\n"); @@ -4416,7 +4423,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) lpfc_cmd = (struct lpfc_scsi_buf *)cmnd->host_scribble; if (!lpfc_cmd || !lpfc_cmd->pCmd) { - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "2873 SCSI Layer I/O Abort Request IO CMPL Status " "x%x ID %d LUN %d\n", @@ -4427,7 +4434,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) iocb = &lpfc_cmd->cur_iocbq; /* the command is in process of being cancelled */ if (!(iocb->iocb_flag & LPFC_IO_ON_TXCMPLQ)) { - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "3169 SCSI Layer abort requested I/O has been " "cancelled by LLD.\n"); @@ -4484,7 +4491,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) abtsiocb->iocb_cmpl = lpfc_sli_abort_fcp_cmpl; abtsiocb->vport = vport; /* no longer need the lock after this point */ - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, flags); if (lpfc_sli_issue_iocb(phba, LPFC_FCP_RING, abtsiocb, 0) == IOCB_ERROR) { @@ -4516,7 +4523,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd) goto out; out_unlock: - spin_unlock_irq(&phba->hbalock); + spin_unlock_irqrestore(&phba->hbalock, flags); out: lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP, "0749 SCSI Layer I/O Abort Request Status x%x ID %d " diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 219bf534ef9..d7f3313ef88 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -3964,9 +3964,9 @@ lpfc_sli4_brdreset(struct lpfc_hba *phba) pci_write_config_word(phba->pcidev, PCI_COMMAND, (cfg_value & ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR))); - /* Perform FCoE PCI function reset */ - lpfc_sli4_queue_destroy(phba); + /* Perform FCoE PCI function reset before freeing queue memory */ rc = lpfc_pci_function_reset(phba); + lpfc_sli4_queue_destroy(phba); /* Restore PCI cmd register */ pci_write_config_word(phba->pcidev, PCI_COMMAND, cfg_value); @@ -7072,6 +7072,40 @@ lpfc_sli4_async_mbox_unblock(struct lpfc_hba *phba) } /** + * lpfc_sli4_wait_bmbx_ready - Wait for bootstrap mailbox register ready + * @phba: Pointer to HBA context object. + * @mboxq: Pointer to mailbox object. + * + * The function waits for the bootstrap mailbox register ready bit from + * port for twice the regular mailbox command timeout value. + * + * 0 - no timeout on waiting for bootstrap mailbox register ready. + * MBXERR_ERROR - wait for bootstrap mailbox register timed out. + **/ +static int +lpfc_sli4_wait_bmbx_ready(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) +{ + uint32_t db_ready; + unsigned long timeout; + struct lpfc_register bmbx_reg; + + timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, mboxq) + * 1000) + jiffies; + + do { + bmbx_reg.word0 = readl(phba->sli4_hba.BMBXregaddr); + db_ready = bf_get(lpfc_bmbx_rdy, &bmbx_reg); + if (!db_ready) + msleep(2); + + if (time_after(jiffies, timeout)) + return MBXERR_ERROR; + } while (!db_ready); + + return 0; +} + +/** * lpfc_sli4_post_sync_mbox - Post an SLI4 mailbox to the bootstrap mailbox * @phba: Pointer to HBA context object. * @mboxq: Pointer to mailbox object. @@ -7092,15 +7126,12 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) { int rc = MBX_SUCCESS; unsigned long iflag; - uint32_t db_ready; uint32_t mcqe_status; uint32_t mbx_cmnd; - unsigned long timeout; struct lpfc_sli *psli = &phba->sli; struct lpfc_mqe *mb = &mboxq->u.mqe; struct lpfc_bmbx_create *mbox_rgn; struct dma_address *dma_address; - struct lpfc_register bmbx_reg; /* * Only one mailbox can be active to the bootstrap mailbox region @@ -7124,6 +7155,11 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) phba->sli.mbox_active = mboxq; spin_unlock_irqrestore(&phba->hbalock, iflag); + /* wait for bootstrap mbox register for readyness */ + rc = lpfc_sli4_wait_bmbx_ready(phba, mboxq); + if (rc) + goto exit; + /* * Initialize the bootstrap memory region to avoid stale data areas * in the mailbox post. Then copy the caller's mailbox contents to @@ -7138,35 +7174,18 @@ lpfc_sli4_post_sync_mbox(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq) dma_address = &phba->sli4_hba.bmbx.dma_address; writel(dma_address->addr_hi, phba->sli4_hba.BMBXregaddr); - timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, mboxq) - * 1000) + jiffies; - do { - bmbx_reg.word0 = readl(phba->sli4_hba.BMBXregaddr); - db_ready = bf_get(lpfc_bmbx_rdy, &bmbx_reg); - if (!db_ready) - msleep(2); - - if (time_after(jiffies, timeout)) { - rc = MBXERR_ERROR; - goto exit; - } - } while (!db_ready); + /* wait for bootstrap mbox register for hi-address write done */ + rc = lpfc_sli4_wait_bmbx_ready(phba, mboxq); + if (rc) + goto exit; /* Post the low mailbox dma address to the port. */ writel(dma_address->addr_lo, phba->sli4_hba.BMBXregaddr); - timeout = msecs_to_jiffies(lpfc_mbox_tmo_val(phba, mboxq) - * 1000) + jiffies; - do { - bmbx_reg.word0 = readl(phba->sli4_hba.BMBXregaddr); - db_ready = bf_get(lpfc_bmbx_rdy, &bmbx_reg); - if (!db_ready) - msleep(2); - if (time_after(jiffies, timeout)) { - rc = MBXERR_ERROR; - goto exit; - } - } while (!db_ready); + /* wait for bootstrap mbox register for low address write done */ + rc = lpfc_sli4_wait_bmbx_ready(phba, mboxq); + if (rc) + goto exit; /* * Read the CQ to ensure the mailbox has completed. @@ -8090,6 +8109,8 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq, bf_set(wqe_lenloc, &wqe->fcp_icmd.wqe_com, LPFC_WQE_LENLOC_NONE); bf_set(wqe_ebde_cnt, &wqe->fcp_icmd.wqe_com, 0); + bf_set(wqe_erp, &wqe->fcp_icmd.wqe_com, + iocbq->iocb.ulpFCP2Rcvy); break; case CMD_GEN_REQUEST64_CR: /* For this command calculate the xmit length of the @@ -12099,6 +12120,7 @@ lpfc_modify_fcp_eq_delay(struct lpfc_hba *phba, uint16_t startq) struct lpfc_queue *eq; int cnt, rc, length, status = 0; uint32_t shdr_status, shdr_add_status; + uint32_t result; int fcp_eqidx; union lpfc_sli4_cfg_shdr *shdr; uint16_t dmult; @@ -12117,8 +12139,11 @@ lpfc_modify_fcp_eq_delay(struct lpfc_hba *phba, uint16_t startq) eq_delay = &mbox->u.mqe.un.eq_delay; /* Calculate delay multiper from maximum interrupt per second */ - dmult = phba->cfg_fcp_imax / phba->cfg_fcp_io_channel; - dmult = LPFC_DMULT_CONST/dmult - 1; + result = phba->cfg_fcp_imax / phba->cfg_fcp_io_channel; + if (result > LPFC_DMULT_CONST) + dmult = 0; + else + dmult = LPFC_DMULT_CONST/result - 1; cnt = 0; for (fcp_eqidx = startq; fcp_eqidx < phba->cfg_fcp_io_channel; @@ -12174,7 +12199,7 @@ lpfc_modify_fcp_eq_delay(struct lpfc_hba *phba, uint16_t startq) * fails this function will return -ENXIO. **/ uint32_t -lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax) +lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint32_t imax) { struct lpfc_mbx_eq_create *eq_create; LPFC_MBOXQ_t *mbox; @@ -12206,7 +12231,10 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax) LPFC_EQE_SIZE); bf_set(lpfc_eq_context_valid, &eq_create->u.request.context, 1); /* Calculate delay multiper from maximum interrupt per second */ - dmult = LPFC_DMULT_CONST/imax - 1; + if (imax > LPFC_DMULT_CONST) + dmult = 0; + else + dmult = LPFC_DMULT_CONST/imax - 1; bf_set(lpfc_eq_context_delay_multi, &eq_create->u.request.context, dmult); switch (eq->entry_count) { diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h index bd4bc4342ae..f44a06a4c6e 100644 --- a/drivers/scsi/lpfc/lpfc_sli4.h +++ b/drivers/scsi/lpfc/lpfc_sli4.h @@ -37,7 +37,7 @@ /* Multi-queue arrangement for FCP EQ/CQ/WQ tuples */ #define LPFC_FCP_IO_CHAN_DEF 4 #define LPFC_FCP_IO_CHAN_MIN 1 -#define LPFC_FCP_IO_CHAN_MAX 8 +#define LPFC_FCP_IO_CHAN_MAX 16 /* * Provide the default FCF Record attributes used by the driver @@ -168,7 +168,7 @@ struct lpfc_queue { }; struct lpfc_sli4_link { - uint8_t speed; + uint16_t speed; uint8_t duplex; uint8_t status; uint8_t type; @@ -490,8 +490,6 @@ struct lpfc_sli4_hba { struct lpfc_pc_sli4_params pc_sli4_params; struct msix_entry *msix_entries; uint8_t handler_name[LPFC_FCP_IO_CHAN_MAX][LPFC_SLI4_HANDLER_NAME_SZ]; - uint32_t cfg_eqn; - uint32_t msix_vec_nr; struct lpfc_fcp_eq_hdl *fcp_eq_hdl; /* FCP per-WQ handle */ /* Pointers to the constructed SLI4 queues */ @@ -626,7 +624,7 @@ void lpfc_sli4_hba_reset(struct lpfc_hba *); struct lpfc_queue *lpfc_sli4_queue_alloc(struct lpfc_hba *, uint32_t, uint32_t); void lpfc_sli4_queue_free(struct lpfc_queue *); -uint32_t lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint16_t); +uint32_t lpfc_eq_create(struct lpfc_hba *, struct lpfc_queue *, uint32_t); uint32_t lpfc_modify_fcp_eq_delay(struct lpfc_hba *, uint16_t); uint32_t lpfc_cq_create(struct lpfc_hba *, struct lpfc_queue *, struct lpfc_queue *, uint32_t, uint32_t); diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 04265a1c4e5..0c2149189dd 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -18,7 +18,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "8.3.34" +#define LPFC_DRIVER_VERSION "8.3.35" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index fcb005fa4bd..16b7a72a70c 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -1,7 +1,7 @@ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2011 LSI Corporation. + * Copyright (c) 2003-2012 LSI Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -33,9 +33,9 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "00.00.06.18-rc1" -#define MEGASAS_RELDATE "Jun. 17, 2012" -#define MEGASAS_EXT_VERSION "Tue. Jun. 17 17:00:00 PDT 2012" +#define MEGASAS_VERSION "06.504.01.00-rc1" +#define MEGASAS_RELDATE "Oct. 1, 2012" +#define MEGASAS_EXT_VERSION "Mon. Oct. 1 17:00:00 PDT 2012" /* * Device IDs diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 0393ec478cd..d2c5366aff7 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -1,7 +1,7 @@ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2011 LSI Corporation. + * Copyright (c) 2003-2012 LSI Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -18,7 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * FILE: megaraid_sas_base.c - * Version : v00.00.06.18-rc1 + * Version : v06.504.01.00-rc1 * * Authors: LSI Corporation * Sreenivas Bagalkote @@ -71,6 +71,10 @@ static int msix_disable; module_param(msix_disable, int, S_IRUGO); MODULE_PARM_DESC(msix_disable, "Disable MSI-X interrupt handling. Default: 0"); +static unsigned int msix_vectors; +module_param(msix_vectors, int, S_IRUGO); +MODULE_PARM_DESC(msix_vectors, "MSI-X max vector count. Default: Set by FW"); + static int throttlequeuedepth = MEGASAS_THROTTLE_QUEUE_DEPTH; module_param(throttlequeuedepth, int, S_IRUGO); MODULE_PARM_DESC(throttlequeuedepth, @@ -3520,6 +3524,10 @@ static int megasas_init_fw(struct megasas_instance *instance) instance->msix_vectors = (readl(&instance->reg_set-> outbound_scratch_pad_2 ) & 0x1F) + 1; + if (msix_vectors) + instance->msix_vectors = + min(msix_vectors, + instance->msix_vectors); } else instance->msix_vectors = 1; /* Don't bother allocating more MSI-X vectors than cpus */ @@ -5233,7 +5241,6 @@ megasas_aen_polling(struct work_struct *work) case MR_EVT_PD_REMOVED: if (megasas_get_pd_list(instance) == 0) { - megasas_get_pd_list(instance); for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c index e3d251a2e26..a11df82474e 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fp.c +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -1,7 +1,7 @@ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2011 LSI Corporation. + * Copyright (c) 2009-2012 LSI Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c index ddf094e7d0a..74030aff69a 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.c +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -1,7 +1,7 @@ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2011 LSI Corporation. + * Copyright (c) 2009-2012 LSI Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -1184,8 +1184,6 @@ megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len, io_request->CDB.EEDP32.PrimaryReferenceTag = cpu_to_be32(ref_tag); io_request->CDB.EEDP32.PrimaryApplicationTagMask = 0xffff; - - io_request->DataLength = num_blocks * 512; io_request->IoFlags = 32; /* Specify 32-byte cdb */ /* Transfer length */ @@ -1329,7 +1327,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, struct megasas_cmd_fusion *cmd) { u8 fp_possible; - u32 start_lba_lo, start_lba_hi, device_id; + u32 start_lba_lo, start_lba_hi, device_id, datalength = 0; struct MPI2_RAID_SCSI_IO_REQUEST *io_request; union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; struct IO_REQUEST_INFO io_info; @@ -1355,7 +1353,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, * 6-byte READ(0x08) or WRITE(0x0A) cdb */ if (scp->cmd_len == 6) { - io_request->DataLength = (u32) scp->cmnd[4]; + datalength = (u32) scp->cmnd[4]; start_lba_lo = ((u32) scp->cmnd[1] << 16) | ((u32) scp->cmnd[2] << 8) | (u32) scp->cmnd[3]; @@ -1366,7 +1364,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, * 10-byte READ(0x28) or WRITE(0x2A) cdb */ else if (scp->cmd_len == 10) { - io_request->DataLength = (u32) scp->cmnd[8] | + datalength = (u32) scp->cmnd[8] | ((u32) scp->cmnd[7] << 8); start_lba_lo = ((u32) scp->cmnd[2] << 24) | ((u32) scp->cmnd[3] << 16) | @@ -1377,7 +1375,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, * 12-byte READ(0xA8) or WRITE(0xAA) cdb */ else if (scp->cmd_len == 12) { - io_request->DataLength = ((u32) scp->cmnd[6] << 24) | + datalength = ((u32) scp->cmnd[6] << 24) | ((u32) scp->cmnd[7] << 16) | ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9]; start_lba_lo = ((u32) scp->cmnd[2] << 24) | @@ -1389,7 +1387,7 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, * 16-byte READ(0x88) or WRITE(0x8A) cdb */ else if (scp->cmd_len == 16) { - io_request->DataLength = ((u32) scp->cmnd[10] << 24) | + datalength = ((u32) scp->cmnd[10] << 24) | ((u32) scp->cmnd[11] << 16) | ((u32) scp->cmnd[12] << 8) | (u32) scp->cmnd[13]; start_lba_lo = ((u32) scp->cmnd[6] << 24) | @@ -1403,8 +1401,9 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, memset(&io_info, 0, sizeof(struct IO_REQUEST_INFO)); io_info.ldStartBlock = ((u64)start_lba_hi << 32) | start_lba_lo; - io_info.numBlocks = io_request->DataLength; + io_info.numBlocks = datalength; io_info.ldTgtId = device_id; + io_request->DataLength = scsi_bufflen(scp); if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) io_info.isRead = 1; @@ -1431,7 +1430,6 @@ megasas_build_ldio_fusion(struct megasas_instance *instance, if (fp_possible) { megasas_set_pd_lba(io_request, scp->cmd_len, &io_info, scp, local_map_ptr, start_lba_lo); - io_request->DataLength = scsi_bufflen(scp); io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; cmd->request_desc->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY @@ -1510,7 +1508,8 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance, local_map_ptr = fusion->ld_map[(instance->map_id & 1)]; /* Check if this is a system PD I/O */ - if (instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) { + if (scmd->device->channel < MEGASAS_MAX_PD_CHANNELS && + instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) { io_request->Function = 0; io_request->DevHandle = local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl; @@ -1525,6 +1524,8 @@ megasas_build_dcdb_fusion(struct megasas_instance *instance, cmd->request_desc->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + cmd->request_desc->SCSIIO.DevHandle = + local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl; } else { io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST; io_request->DevHandle = device_id; @@ -1732,8 +1733,6 @@ complete_cmd_fusion(struct megasas_instance *instance, u32 MSIxIndex) if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) return IRQ_NONE; - d_val.word = desc->Words; - num_completed = 0; while ((d_val.u.low != UINT_MAX) && (d_val.u.high != UINT_MAX)) { @@ -1855,10 +1854,8 @@ megasas_complete_cmd_dpc_fusion(unsigned long instance_addr) } spin_unlock_irqrestore(&instance->hba_lock, flags); - spin_lock_irqsave(&instance->completion_lock, flags); for (MSIxIndex = 0 ; MSIxIndex < count; MSIxIndex++) complete_cmd_fusion(instance, MSIxIndex); - spin_unlock_irqrestore(&instance->completion_lock, flags); } /** diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h index 088c9f91da9..a7c64f05199 100644 --- a/drivers/scsi/megaraid/megaraid_sas_fusion.h +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -1,7 +1,7 @@ /* * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2009-2011 LSI Corporation. + * Copyright (c) 2009-2012 LSI Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License diff --git a/drivers/scsi/mvumi.c b/drivers/scsi/mvumi.c index 783edc7c6b9..c585a925b3c 100644 --- a/drivers/scsi/mvumi.c +++ b/drivers/scsi/mvumi.c @@ -35,10 +35,12 @@ #include <linux/io.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> #include <scsi/scsi_host.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_eh.h> #include <linux/uaccess.h> +#include <linux/kthread.h> #include "mvumi.h" @@ -48,6 +50,7 @@ MODULE_DESCRIPTION("Marvell UMI Driver"); static DEFINE_PCI_DEVICE_TABLE(mvumi_pci_table) = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_2, PCI_DEVICE_ID_MARVELL_MV9143) }, + { PCI_DEVICE(PCI_VENDOR_ID_MARVELL_2, PCI_DEVICE_ID_MARVELL_MV9580) }, { 0 } }; @@ -118,7 +121,7 @@ static int mvumi_map_pci_addr(struct pci_dev *dev, void **addr_array) static struct mvumi_res *mvumi_alloc_mem_resource(struct mvumi_hba *mhba, enum resource_type type, unsigned int size) { - struct mvumi_res *res = kzalloc(sizeof(*res), GFP_KERNEL); + struct mvumi_res *res = kzalloc(sizeof(*res), GFP_ATOMIC); if (!res) { dev_err(&mhba->pdev->dev, @@ -128,7 +131,7 @@ static struct mvumi_res *mvumi_alloc_mem_resource(struct mvumi_hba *mhba, switch (type) { case RESOURCE_CACHED_MEMORY: - res->virt_addr = kzalloc(size, GFP_KERNEL); + res->virt_addr = kzalloc(size, GFP_ATOMIC); if (!res->virt_addr) { dev_err(&mhba->pdev->dev, "unable to allocate memory,size = %d.\n", size); @@ -222,11 +225,11 @@ static int mvumi_make_sgl(struct mvumi_hba *mhba, struct scsi_cmnd *scmd, m_sg->baseaddr_l = cpu_to_le32(lower_32_bits(busaddr)); m_sg->baseaddr_h = cpu_to_le32(upper_32_bits(busaddr)); m_sg->flags = 0; - m_sg->size = cpu_to_le32(sg_dma_len(&sg[i])); + sgd_setsz(mhba, m_sg, cpu_to_le32(sg_dma_len(&sg[i]))); if ((i + 1) == *sg_count) - m_sg->flags |= SGD_EOT; + m_sg->flags |= 1U << mhba->eot_flag; - m_sg++; + sgd_inc(mhba, m_sg); } } else { scmd->SCp.dma_handle = scsi_bufflen(scmd) ? @@ -237,8 +240,8 @@ static int mvumi_make_sgl(struct mvumi_hba *mhba, struct scsi_cmnd *scmd, busaddr = scmd->SCp.dma_handle; m_sg->baseaddr_l = cpu_to_le32(lower_32_bits(busaddr)); m_sg->baseaddr_h = cpu_to_le32(upper_32_bits(busaddr)); - m_sg->flags = SGD_EOT; - m_sg->size = cpu_to_le32(scsi_bufflen(scmd)); + m_sg->flags = 1U << mhba->eot_flag; + sgd_setsz(mhba, m_sg, cpu_to_le32(scsi_bufflen(scmd))); *sg_count = 1; } @@ -267,8 +270,8 @@ static int mvumi_internal_cmd_sgl(struct mvumi_hba *mhba, struct mvumi_cmd *cmd, m_sg->baseaddr_l = cpu_to_le32(lower_32_bits(phy_addr)); m_sg->baseaddr_h = cpu_to_le32(upper_32_bits(phy_addr)); - m_sg->flags = SGD_EOT; - m_sg->size = cpu_to_le32(size); + m_sg->flags = 1U << mhba->eot_flag; + sgd_setsz(mhba, m_sg, cpu_to_le32(size)); return 0; } @@ -285,7 +288,8 @@ static struct mvumi_cmd *mvumi_create_internal_cmd(struct mvumi_hba *mhba, } INIT_LIST_HEAD(&cmd->queue_pointer); - cmd->frame = kzalloc(mhba->ib_max_size, GFP_KERNEL); + cmd->frame = pci_alloc_consistent(mhba->pdev, + mhba->ib_max_size, &cmd->frame_phys); if (!cmd->frame) { dev_err(&mhba->pdev->dev, "failed to allocate memory for FW" " frame,size = %d.\n", mhba->ib_max_size); @@ -297,7 +301,8 @@ static struct mvumi_cmd *mvumi_create_internal_cmd(struct mvumi_hba *mhba, if (mvumi_internal_cmd_sgl(mhba, cmd, buf_size)) { dev_err(&mhba->pdev->dev, "failed to allocate memory" " for internal frame\n"); - kfree(cmd->frame); + pci_free_consistent(mhba->pdev, mhba->ib_max_size, + cmd->frame, cmd->frame_phys); kfree(cmd); return NULL; } @@ -317,7 +322,7 @@ static void mvumi_delete_internal_cmd(struct mvumi_hba *mhba, if (cmd && cmd->frame) { if (cmd->frame->sg_counts) { m_sg = (struct mvumi_sgl *) &cmd->frame->payload[0]; - size = m_sg->size; + sgd_getsz(mhba, m_sg, size); phy_addr = (dma_addr_t) m_sg->baseaddr_l | (dma_addr_t) ((m_sg->baseaddr_h << 16) << 16); @@ -325,7 +330,8 @@ static void mvumi_delete_internal_cmd(struct mvumi_hba *mhba, pci_free_consistent(mhba->pdev, size, cmd->data_buf, phy_addr); } - kfree(cmd->frame); + pci_free_consistent(mhba->pdev, mhba->ib_max_size, + cmd->frame, cmd->frame_phys); kfree(cmd); } } @@ -374,7 +380,8 @@ static void mvumi_free_cmds(struct mvumi_hba *mhba) cmd = list_first_entry(&mhba->cmd_pool, struct mvumi_cmd, queue_pointer); list_del(&cmd->queue_pointer); - kfree(cmd->frame); + if (!(mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC)) + kfree(cmd->frame); kfree(cmd); } } @@ -396,7 +403,12 @@ static int mvumi_alloc_cmds(struct mvumi_hba *mhba) INIT_LIST_HEAD(&cmd->queue_pointer); list_add_tail(&cmd->queue_pointer, &mhba->cmd_pool); - cmd->frame = kzalloc(mhba->ib_max_size, GFP_KERNEL); + if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) { + cmd->frame = mhba->ib_frame + i * mhba->ib_max_size; + cmd->frame_phys = mhba->ib_frame_phys + + i * mhba->ib_max_size; + } else + cmd->frame = kzalloc(mhba->ib_max_size, GFP_KERNEL); if (!cmd->frame) goto err_exit; } @@ -409,48 +421,71 @@ err_exit: cmd = list_first_entry(&mhba->cmd_pool, struct mvumi_cmd, queue_pointer); list_del(&cmd->queue_pointer); - kfree(cmd->frame); + if (!(mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC)) + kfree(cmd->frame); kfree(cmd); } return -ENOMEM; } -static int mvumi_get_ib_list_entry(struct mvumi_hba *mhba, void **ib_entry) +static unsigned int mvumi_check_ib_list_9143(struct mvumi_hba *mhba) { - unsigned int ib_rp_reg, cur_ib_entry; + unsigned int ib_rp_reg; + struct mvumi_hw_regs *regs = mhba->regs; + + ib_rp_reg = ioread32(mhba->regs->inb_read_pointer); + if (unlikely(((ib_rp_reg & regs->cl_slot_num_mask) == + (mhba->ib_cur_slot & regs->cl_slot_num_mask)) && + ((ib_rp_reg & regs->cl_pointer_toggle) + != (mhba->ib_cur_slot & regs->cl_pointer_toggle)))) { + dev_warn(&mhba->pdev->dev, "no free slot to use.\n"); + return 0; + } if (atomic_read(&mhba->fw_outstanding) >= mhba->max_io) { dev_warn(&mhba->pdev->dev, "firmware io overflow.\n"); - return -1; + return 0; + } else { + return mhba->max_io - atomic_read(&mhba->fw_outstanding); } - ib_rp_reg = ioread32(mhba->mmio + CLA_INB_READ_POINTER); +} - if (unlikely(((ib_rp_reg & CL_SLOT_NUM_MASK) == - (mhba->ib_cur_slot & CL_SLOT_NUM_MASK)) && - ((ib_rp_reg & CL_POINTER_TOGGLE) != - (mhba->ib_cur_slot & CL_POINTER_TOGGLE)))) { - dev_warn(&mhba->pdev->dev, "no free slot to use.\n"); - return -1; - } +static unsigned int mvumi_check_ib_list_9580(struct mvumi_hba *mhba) +{ + unsigned int count; + if (atomic_read(&mhba->fw_outstanding) >= (mhba->max_io - 1)) + return 0; + count = ioread32(mhba->ib_shadow); + if (count == 0xffff) + return 0; + return count; +} + +static void mvumi_get_ib_list_entry(struct mvumi_hba *mhba, void **ib_entry) +{ + unsigned int cur_ib_entry; - cur_ib_entry = mhba->ib_cur_slot & CL_SLOT_NUM_MASK; + cur_ib_entry = mhba->ib_cur_slot & mhba->regs->cl_slot_num_mask; cur_ib_entry++; if (cur_ib_entry >= mhba->list_num_io) { cur_ib_entry -= mhba->list_num_io; - mhba->ib_cur_slot ^= CL_POINTER_TOGGLE; + mhba->ib_cur_slot ^= mhba->regs->cl_pointer_toggle; + } + mhba->ib_cur_slot &= ~mhba->regs->cl_slot_num_mask; + mhba->ib_cur_slot |= (cur_ib_entry & mhba->regs->cl_slot_num_mask); + if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) { + *ib_entry = mhba->ib_list + cur_ib_entry * + sizeof(struct mvumi_dyn_list_entry); + } else { + *ib_entry = mhba->ib_list + cur_ib_entry * mhba->ib_max_size; } - mhba->ib_cur_slot &= ~CL_SLOT_NUM_MASK; - mhba->ib_cur_slot |= (cur_ib_entry & CL_SLOT_NUM_MASK); - *ib_entry = mhba->ib_list + cur_ib_entry * mhba->ib_max_size; atomic_inc(&mhba->fw_outstanding); - - return 0; } static void mvumi_send_ib_list_entry(struct mvumi_hba *mhba) { - iowrite32(0xfff, mhba->ib_shadow); - iowrite32(mhba->ib_cur_slot, mhba->mmio + CLA_INB_WRITE_POINTER); + iowrite32(0xffff, mhba->ib_shadow); + iowrite32(mhba->ib_cur_slot, mhba->regs->inb_write_pointer); } static char mvumi_check_ob_frame(struct mvumi_hba *mhba, @@ -480,31 +515,59 @@ static char mvumi_check_ob_frame(struct mvumi_hba *mhba, return 0; } -static void mvumi_receive_ob_list_entry(struct mvumi_hba *mhba) +static int mvumi_check_ob_list_9143(struct mvumi_hba *mhba, + unsigned int *cur_obf, unsigned int *assign_obf_end) { - unsigned int ob_write_reg, ob_write_shadow_reg; - unsigned int cur_obf, assign_obf_end, i; - struct mvumi_ob_data *ob_data; - struct mvumi_rsp_frame *p_outb_frame; + unsigned int ob_write, ob_write_shadow; + struct mvumi_hw_regs *regs = mhba->regs; do { - ob_write_reg = ioread32(mhba->mmio + CLA_OUTB_COPY_POINTER); - ob_write_shadow_reg = ioread32(mhba->ob_shadow); - } while ((ob_write_reg & CL_SLOT_NUM_MASK) != ob_write_shadow_reg); + ob_write = ioread32(regs->outb_copy_pointer); + ob_write_shadow = ioread32(mhba->ob_shadow); + } while ((ob_write & regs->cl_slot_num_mask) != ob_write_shadow); - cur_obf = mhba->ob_cur_slot & CL_SLOT_NUM_MASK; - assign_obf_end = ob_write_reg & CL_SLOT_NUM_MASK; + *cur_obf = mhba->ob_cur_slot & mhba->regs->cl_slot_num_mask; + *assign_obf_end = ob_write & mhba->regs->cl_slot_num_mask; - if ((ob_write_reg & CL_POINTER_TOGGLE) != - (mhba->ob_cur_slot & CL_POINTER_TOGGLE)) { - assign_obf_end += mhba->list_num_io; + if ((ob_write & regs->cl_pointer_toggle) != + (mhba->ob_cur_slot & regs->cl_pointer_toggle)) { + *assign_obf_end += mhba->list_num_io; } + return 0; +} + +static int mvumi_check_ob_list_9580(struct mvumi_hba *mhba, + unsigned int *cur_obf, unsigned int *assign_obf_end) +{ + unsigned int ob_write; + struct mvumi_hw_regs *regs = mhba->regs; + + ob_write = ioread32(regs->outb_read_pointer); + ob_write = ioread32(regs->outb_copy_pointer); + *cur_obf = mhba->ob_cur_slot & mhba->regs->cl_slot_num_mask; + *assign_obf_end = ob_write & mhba->regs->cl_slot_num_mask; + if (*assign_obf_end < *cur_obf) + *assign_obf_end += mhba->list_num_io; + else if (*assign_obf_end == *cur_obf) + return -1; + return 0; +} + +static void mvumi_receive_ob_list_entry(struct mvumi_hba *mhba) +{ + unsigned int cur_obf, assign_obf_end, i; + struct mvumi_ob_data *ob_data; + struct mvumi_rsp_frame *p_outb_frame; + struct mvumi_hw_regs *regs = mhba->regs; + + if (mhba->instancet->check_ob_list(mhba, &cur_obf, &assign_obf_end)) + return; for (i = (assign_obf_end - cur_obf); i != 0; i--) { cur_obf++; if (cur_obf >= mhba->list_num_io) { cur_obf -= mhba->list_num_io; - mhba->ob_cur_slot ^= CL_POINTER_TOGGLE; + mhba->ob_cur_slot ^= regs->cl_pointer_toggle; } p_outb_frame = mhba->ob_list + cur_obf * mhba->ob_max_size; @@ -528,7 +591,7 @@ static void mvumi_receive_ob_list_entry(struct mvumi_hba *mhba) ob_data = NULL; if (cur_obf == 0) { cur_obf = mhba->list_num_io - 1; - mhba->ob_cur_slot ^= CL_POINTER_TOGGLE; + mhba->ob_cur_slot ^= regs->cl_pointer_toggle; } else cur_obf -= 1; break; @@ -539,18 +602,20 @@ static void mvumi_receive_ob_list_entry(struct mvumi_hba *mhba) list_add_tail(&ob_data->list, &mhba->free_ob_list); } - mhba->ob_cur_slot &= ~CL_SLOT_NUM_MASK; - mhba->ob_cur_slot |= (cur_obf & CL_SLOT_NUM_MASK); - iowrite32(mhba->ob_cur_slot, mhba->mmio + CLA_OUTB_READ_POINTER); + mhba->ob_cur_slot &= ~regs->cl_slot_num_mask; + mhba->ob_cur_slot |= (cur_obf & regs->cl_slot_num_mask); + iowrite32(mhba->ob_cur_slot, regs->outb_read_pointer); } -static void mvumi_reset(void *regs) +static void mvumi_reset(struct mvumi_hba *mhba) { - iowrite32(0, regs + CPU_ENPOINTA_MASK_REG); - if (ioread32(regs + CPU_ARM_TO_PCIEA_MSG1) != HANDSHAKE_DONESTATE) + struct mvumi_hw_regs *regs = mhba->regs; + + iowrite32(0, regs->enpointa_mask_reg); + if (ioread32(regs->arm_to_pciea_msg1) != HANDSHAKE_DONESTATE) return; - iowrite32(DRBL_SOFT_RESET, regs + CPU_PCIEA_TO_ARM_DRBL_REG); + iowrite32(DRBL_SOFT_RESET, regs->pciea_to_arm_drbl_reg); } static unsigned char mvumi_start(struct mvumi_hba *mhba); @@ -558,7 +623,7 @@ static unsigned char mvumi_start(struct mvumi_hba *mhba); static int mvumi_wait_for_outstanding(struct mvumi_hba *mhba) { mhba->fw_state = FW_STATE_ABORT; - mvumi_reset(mhba->mmio); + mvumi_reset(mhba); if (mvumi_start(mhba)) return FAILED; @@ -566,6 +631,98 @@ static int mvumi_wait_for_outstanding(struct mvumi_hba *mhba) return SUCCESS; } +static int mvumi_wait_for_fw(struct mvumi_hba *mhba) +{ + struct mvumi_hw_regs *regs = mhba->regs; + u32 tmp; + unsigned long before; + before = jiffies; + + iowrite32(0, regs->enpointa_mask_reg); + tmp = ioread32(regs->arm_to_pciea_msg1); + while (tmp != HANDSHAKE_READYSTATE) { + iowrite32(DRBL_MU_RESET, regs->pciea_to_arm_drbl_reg); + if (time_after(jiffies, before + FW_MAX_DELAY * HZ)) { + dev_err(&mhba->pdev->dev, + "FW reset failed [0x%x].\n", tmp); + return FAILED; + } + + msleep(500); + rmb(); + tmp = ioread32(regs->arm_to_pciea_msg1); + } + + return SUCCESS; +} + +static void mvumi_backup_bar_addr(struct mvumi_hba *mhba) +{ + unsigned char i; + + for (i = 0; i < MAX_BASE_ADDRESS; i++) { + pci_read_config_dword(mhba->pdev, 0x10 + i * 4, + &mhba->pci_base[i]); + } +} + +static void mvumi_restore_bar_addr(struct mvumi_hba *mhba) +{ + unsigned char i; + + for (i = 0; i < MAX_BASE_ADDRESS; i++) { + if (mhba->pci_base[i]) + pci_write_config_dword(mhba->pdev, 0x10 + i * 4, + mhba->pci_base[i]); + } +} + +static unsigned int mvumi_pci_set_master(struct pci_dev *pdev) +{ + unsigned int ret = 0; + pci_set_master(pdev); + + if (IS_DMA64) { + if (pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + } else + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + + return ret; +} + +static int mvumi_reset_host_9580(struct mvumi_hba *mhba) +{ + mhba->fw_state = FW_STATE_ABORT; + + iowrite32(0, mhba->regs->reset_enable); + iowrite32(0xf, mhba->regs->reset_request); + + iowrite32(0x10, mhba->regs->reset_enable); + iowrite32(0x10, mhba->regs->reset_request); + msleep(100); + pci_disable_device(mhba->pdev); + + if (pci_enable_device(mhba->pdev)) { + dev_err(&mhba->pdev->dev, "enable device failed\n"); + return FAILED; + } + if (mvumi_pci_set_master(mhba->pdev)) { + dev_err(&mhba->pdev->dev, "set master failed\n"); + return FAILED; + } + mvumi_restore_bar_addr(mhba); + if (mvumi_wait_for_fw(mhba) == FAILED) + return FAILED; + + return mvumi_wait_for_outstanding(mhba); +} + +static int mvumi_reset_host_9143(struct mvumi_hba *mhba) +{ + return mvumi_wait_for_outstanding(mhba); +} + static int mvumi_host_reset(struct scsi_cmnd *scmd) { struct mvumi_hba *mhba; @@ -575,7 +732,7 @@ static int mvumi_host_reset(struct scsi_cmnd *scmd) scmd_printk(KERN_NOTICE, scmd, "RESET -%ld cmd=%x retries=%x\n", scmd->serial_number, scmd->cmnd[0], scmd->retries); - return mvumi_wait_for_outstanding(mhba); + return mhba->instancet->reset_host(mhba); } static int mvumi_issue_blocked_cmd(struct mvumi_hba *mhba, @@ -628,7 +785,9 @@ static void mvumi_release_fw(struct mvumi_hba *mhba) mvumi_free_cmds(mhba); mvumi_release_mem_resource(mhba); mvumi_unmap_pci_addr(mhba->pdev, mhba->base_addr); - kfree(mhba->handshake_page); + pci_free_consistent(mhba->pdev, HSP_MAX_SIZE, + mhba->handshake_page, mhba->handshake_page_phys); + kfree(mhba->regs); pci_release_regions(mhba->pdev); } @@ -665,6 +824,7 @@ get_cmd: cmd = mvumi_create_internal_cmd(mhba, 0); frame->cdb_length = MAX_COMMAND_SIZE; memset(frame->cdb, 0, MAX_COMMAND_SIZE); frame->cdb[0] = SCSI_CMD_MARVELL_SPECIFIC; + frame->cdb[1] = CDB_CORE_MODULE; frame->cdb[2] = CDB_CORE_SHUTDOWN; mvumi_issue_blocked_cmd(mhba, cmd); @@ -695,7 +855,7 @@ mvumi_calculate_checksum(struct mvumi_hs_header *p_header, return ret; } -void mvumi_hs_build_page(struct mvumi_hba *mhba, +static void mvumi_hs_build_page(struct mvumi_hba *mhba, struct mvumi_hs_header *hs_header) { struct mvumi_hs_page2 *hs_page2; @@ -710,6 +870,8 @@ void mvumi_hs_build_page(struct mvumi_hba *mhba, hs_header->frame_length = sizeof(*hs_page2) - 4; memset(hs_header->frame_content, 0, hs_header->frame_length); hs_page2->host_type = 3; /* 3 mean linux*/ + if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) + hs_page2->host_cap = 0x08;/* host dynamic source mode */ hs_page2->host_ver.ver_major = VER_MAJOR; hs_page2->host_ver.ver_minor = VER_MINOR; hs_page2->host_ver.ver_oem = VER_OEM; @@ -745,8 +907,18 @@ void mvumi_hs_build_page(struct mvumi_hba *mhba, hs_page4->ob_baseaddr_h = upper_32_bits(mhba->ob_list_phys); hs_page4->ib_entry_size = mhba->ib_max_size_setting; hs_page4->ob_entry_size = mhba->ob_max_size_setting; - hs_page4->ob_depth = mhba->list_num_io; - hs_page4->ib_depth = mhba->list_num_io; + if (mhba->hba_capability + & HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF) { + hs_page4->ob_depth = find_first_bit((unsigned long *) + &mhba->list_num_io, + BITS_PER_LONG); + hs_page4->ib_depth = find_first_bit((unsigned long *) + &mhba->list_num_io, + BITS_PER_LONG); + } else { + hs_page4->ob_depth = (u8) mhba->list_num_io; + hs_page4->ib_depth = (u8) mhba->list_num_io; + } hs_header->checksum = mvumi_calculate_checksum(hs_header, hs_header->frame_length); break; @@ -774,8 +946,11 @@ static int mvumi_init_data(struct mvumi_hba *mhba) return 0; tmp_size = mhba->ib_max_size * mhba->max_io; + if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) + tmp_size += sizeof(struct mvumi_dyn_list_entry) * mhba->max_io; + tmp_size += 128 + mhba->ob_max_size * mhba->max_io; - tmp_size += 8 + sizeof(u32) + 16; + tmp_size += 8 + sizeof(u32)*2 + 16; res_mgnt = mvumi_alloc_mem_resource(mhba, RESOURCE_UNCACHED_MEMORY, tmp_size); @@ -793,24 +968,41 @@ static int mvumi_init_data(struct mvumi_hba *mhba) v += offset; mhba->ib_list = v; mhba->ib_list_phys = p; + if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) { + v += sizeof(struct mvumi_dyn_list_entry) * mhba->max_io; + p += sizeof(struct mvumi_dyn_list_entry) * mhba->max_io; + mhba->ib_frame = v; + mhba->ib_frame_phys = p; + } v += mhba->ib_max_size * mhba->max_io; p += mhba->ib_max_size * mhba->max_io; + /* ib shadow */ offset = round_up(p, 8) - p; p += offset; v += offset; mhba->ib_shadow = v; mhba->ib_shadow_phys = p; - p += sizeof(u32); - v += sizeof(u32); + p += sizeof(u32)*2; + v += sizeof(u32)*2; /* ob shadow */ - offset = round_up(p, 8) - p; - p += offset; - v += offset; - mhba->ob_shadow = v; - mhba->ob_shadow_phys = p; - p += 8; - v += 8; + if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580) { + offset = round_up(p, 8) - p; + p += offset; + v += offset; + mhba->ob_shadow = v; + mhba->ob_shadow_phys = p; + p += 8; + v += 8; + } else { + offset = round_up(p, 4) - p; + p += offset; + v += offset; + mhba->ob_shadow = v; + mhba->ob_shadow_phys = p; + p += 4; + v += 4; + } /* ob list */ offset = round_up(p, 128) - p; @@ -902,6 +1094,12 @@ static int mvumi_hs_process_page(struct mvumi_hba *mhba, dev_dbg(&mhba->pdev->dev, "FW version:%d\n", hs_page1->fw_ver.ver_build); + if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_COMPACT_SG) + mhba->eot_flag = 22; + else + mhba->eot_flag = 27; + if (mhba->hba_capability & HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF) + mhba->list_num_io = 1 << hs_page1->cl_inout_list_depth; break; default: dev_err(&mhba->pdev->dev, "handshake: page code error\n"); @@ -923,12 +1121,12 @@ static int mvumi_handshake(struct mvumi_hba *mhba) { unsigned int hs_state, tmp, hs_fun; struct mvumi_hs_header *hs_header; - void *regs = mhba->mmio; + struct mvumi_hw_regs *regs = mhba->regs; if (mhba->fw_state == FW_STATE_STARTING) hs_state = HS_S_START; else { - tmp = ioread32(regs + CPU_ARM_TO_PCIEA_MSG0); + tmp = ioread32(regs->arm_to_pciea_msg0); hs_state = HS_GET_STATE(tmp); dev_dbg(&mhba->pdev->dev, "handshake state[0x%x].\n", hs_state); if (HS_GET_STATUS(tmp) != HS_STATUS_OK) { @@ -943,21 +1141,20 @@ static int mvumi_handshake(struct mvumi_hba *mhba) mhba->fw_state = FW_STATE_HANDSHAKING; HS_SET_STATUS(hs_fun, HS_STATUS_OK); HS_SET_STATE(hs_fun, HS_S_RESET); - iowrite32(HANDSHAKE_SIGNATURE, regs + CPU_PCIEA_TO_ARM_MSG1); - iowrite32(hs_fun, regs + CPU_PCIEA_TO_ARM_MSG0); - iowrite32(DRBL_HANDSHAKE, regs + CPU_PCIEA_TO_ARM_DRBL_REG); + iowrite32(HANDSHAKE_SIGNATURE, regs->pciea_to_arm_msg1); + iowrite32(hs_fun, regs->pciea_to_arm_msg0); + iowrite32(DRBL_HANDSHAKE, regs->pciea_to_arm_drbl_reg); break; case HS_S_RESET: iowrite32(lower_32_bits(mhba->handshake_page_phys), - regs + CPU_PCIEA_TO_ARM_MSG1); + regs->pciea_to_arm_msg1); iowrite32(upper_32_bits(mhba->handshake_page_phys), - regs + CPU_ARM_TO_PCIEA_MSG1); + regs->arm_to_pciea_msg1); HS_SET_STATUS(hs_fun, HS_STATUS_OK); HS_SET_STATE(hs_fun, HS_S_PAGE_ADDR); - iowrite32(hs_fun, regs + CPU_PCIEA_TO_ARM_MSG0); - iowrite32(DRBL_HANDSHAKE, regs + CPU_PCIEA_TO_ARM_DRBL_REG); - + iowrite32(hs_fun, regs->pciea_to_arm_msg0); + iowrite32(DRBL_HANDSHAKE, regs->pciea_to_arm_drbl_reg); break; case HS_S_PAGE_ADDR: @@ -997,30 +1194,37 @@ static int mvumi_handshake(struct mvumi_hba *mhba) HS_SET_STATE(hs_fun, HS_S_END); HS_SET_STATUS(hs_fun, HS_STATUS_OK); - iowrite32(hs_fun, regs + CPU_PCIEA_TO_ARM_MSG0); - iowrite32(DRBL_HANDSHAKE, regs + CPU_PCIEA_TO_ARM_DRBL_REG); + iowrite32(hs_fun, regs->pciea_to_arm_msg0); + iowrite32(DRBL_HANDSHAKE, regs->pciea_to_arm_drbl_reg); break; case HS_S_END: /* Set communication list ISR */ - tmp = ioread32(regs + CPU_ENPOINTA_MASK_REG); - tmp |= INT_MAP_COMAOUT | INT_MAP_COMAERR; - iowrite32(tmp, regs + CPU_ENPOINTA_MASK_REG); + tmp = ioread32(regs->enpointa_mask_reg); + tmp |= regs->int_comaout | regs->int_comaerr; + iowrite32(tmp, regs->enpointa_mask_reg); iowrite32(mhba->list_num_io, mhba->ib_shadow); /* Set InBound List Available count shadow */ iowrite32(lower_32_bits(mhba->ib_shadow_phys), - regs + CLA_INB_AVAL_COUNT_BASEL); + regs->inb_aval_count_basel); iowrite32(upper_32_bits(mhba->ib_shadow_phys), - regs + CLA_INB_AVAL_COUNT_BASEH); - - /* Set OutBound List Available count shadow */ - iowrite32((mhba->list_num_io-1) | CL_POINTER_TOGGLE, - mhba->ob_shadow); - iowrite32(lower_32_bits(mhba->ob_shadow_phys), regs + 0x5B0); - iowrite32(upper_32_bits(mhba->ob_shadow_phys), regs + 0x5B4); + regs->inb_aval_count_baseh); + + if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9143) { + /* Set OutBound List Available count shadow */ + iowrite32((mhba->list_num_io-1) | + regs->cl_pointer_toggle, + mhba->ob_shadow); + iowrite32(lower_32_bits(mhba->ob_shadow_phys), + regs->outb_copy_basel); + iowrite32(upper_32_bits(mhba->ob_shadow_phys), + regs->outb_copy_baseh); + } - mhba->ib_cur_slot = (mhba->list_num_io - 1) | CL_POINTER_TOGGLE; - mhba->ob_cur_slot = (mhba->list_num_io - 1) | CL_POINTER_TOGGLE; + mhba->ib_cur_slot = (mhba->list_num_io - 1) | + regs->cl_pointer_toggle; + mhba->ob_cur_slot = (mhba->list_num_io - 1) | + regs->cl_pointer_toggle; mhba->fw_state = FW_STATE_STARTED; break; @@ -1040,7 +1244,7 @@ static unsigned char mvumi_handshake_event(struct mvumi_hba *mhba) before = jiffies; mvumi_handshake(mhba); do { - isr_status = mhba->instancet->read_fw_status_reg(mhba->mmio); + isr_status = mhba->instancet->read_fw_status_reg(mhba); if (mhba->fw_state == FW_STATE_STARTED) return 0; @@ -1062,16 +1266,15 @@ static unsigned char mvumi_handshake_event(struct mvumi_hba *mhba) static unsigned char mvumi_check_handshake(struct mvumi_hba *mhba) { - void *regs = mhba->mmio; unsigned int tmp; unsigned long before; before = jiffies; - tmp = ioread32(regs + CPU_ARM_TO_PCIEA_MSG1); + tmp = ioread32(mhba->regs->arm_to_pciea_msg1); while ((tmp != HANDSHAKE_READYSTATE) && (tmp != HANDSHAKE_DONESTATE)) { if (tmp != HANDSHAKE_READYSTATE) iowrite32(DRBL_MU_RESET, - regs + CPU_PCIEA_TO_ARM_DRBL_REG); + mhba->regs->pciea_to_arm_drbl_reg); if (time_after(jiffies, before + FW_MAX_DELAY * HZ)) { dev_err(&mhba->pdev->dev, "invalid signature [0x%x].\n", tmp); @@ -1079,7 +1282,7 @@ static unsigned char mvumi_check_handshake(struct mvumi_hba *mhba) } usleep_range(1000, 2000); rmb(); - tmp = ioread32(regs + CPU_ARM_TO_PCIEA_MSG1); + tmp = ioread32(mhba->regs->arm_to_pciea_msg1); } mhba->fw_state = FW_STATE_STARTING; @@ -1100,15 +1303,17 @@ static unsigned char mvumi_check_handshake(struct mvumi_hba *mhba) static unsigned char mvumi_start(struct mvumi_hba *mhba) { - void *regs = mhba->mmio; unsigned int tmp; + struct mvumi_hw_regs *regs = mhba->regs; + /* clear Door bell */ - tmp = ioread32(regs + CPU_ARM_TO_PCIEA_DRBL_REG); - iowrite32(tmp, regs + CPU_ARM_TO_PCIEA_DRBL_REG); + tmp = ioread32(regs->arm_to_pciea_drbl_reg); + iowrite32(tmp, regs->arm_to_pciea_drbl_reg); - iowrite32(0x3FFFFFFF, regs + CPU_ARM_TO_PCIEA_MASK_REG); - tmp = ioread32(regs + CPU_ENPOINTA_MASK_REG) | INT_MAP_DL_CPU2PCIEA; - iowrite32(tmp, regs + CPU_ENPOINTA_MASK_REG); + iowrite32(regs->int_drbl_int_mask, regs->arm_to_pciea_mask_reg); + tmp = ioread32(regs->enpointa_mask_reg) | regs->int_dl_cpu2pciea; + iowrite32(tmp, regs->enpointa_mask_reg); + msleep(100); if (mvumi_check_handshake(mhba)) return -1; @@ -1166,6 +1371,7 @@ static void mvumi_complete_cmd(struct mvumi_hba *mhba, struct mvumi_cmd *cmd, cmd->scmd->scsi_done(scmd); mvumi_return_cmd(mhba, cmd); } + static void mvumi_complete_internal_cmd(struct mvumi_hba *mhba, struct mvumi_cmd *cmd, struct mvumi_rsp_frame *ob_frame) @@ -1210,6 +1416,304 @@ static void mvumi_show_event(struct mvumi_hba *mhba, } } +static int mvumi_handle_hotplug(struct mvumi_hba *mhba, u16 devid, int status) +{ + struct scsi_device *sdev; + int ret = -1; + + if (status == DEVICE_OFFLINE) { + sdev = scsi_device_lookup(mhba->shost, 0, devid, 0); + if (sdev) { + dev_dbg(&mhba->pdev->dev, "remove disk %d-%d-%d.\n", 0, + sdev->id, 0); + scsi_remove_device(sdev); + scsi_device_put(sdev); + ret = 0; + } else + dev_err(&mhba->pdev->dev, " no disk[%d] to remove\n", + devid); + } else if (status == DEVICE_ONLINE) { + sdev = scsi_device_lookup(mhba->shost, 0, devid, 0); + if (!sdev) { + scsi_add_device(mhba->shost, 0, devid, 0); + dev_dbg(&mhba->pdev->dev, " add disk %d-%d-%d.\n", 0, + devid, 0); + ret = 0; + } else { + dev_err(&mhba->pdev->dev, " don't add disk %d-%d-%d.\n", + 0, devid, 0); + scsi_device_put(sdev); + } + } + return ret; +} + +static u64 mvumi_inquiry(struct mvumi_hba *mhba, + unsigned int id, struct mvumi_cmd *cmd) +{ + struct mvumi_msg_frame *frame; + u64 wwid = 0; + int cmd_alloc = 0; + int data_buf_len = 64; + + if (!cmd) { + cmd = mvumi_create_internal_cmd(mhba, data_buf_len); + if (cmd) + cmd_alloc = 1; + else + return 0; + } else { + memset(cmd->data_buf, 0, data_buf_len); + } + cmd->scmd = NULL; + cmd->cmd_status = REQ_STATUS_PENDING; + atomic_set(&cmd->sync_cmd, 0); + frame = cmd->frame; + frame->device_id = (u16) id; + frame->cmd_flag = CMD_FLAG_DATA_IN; + frame->req_function = CL_FUN_SCSI_CMD; + frame->cdb_length = 6; + frame->data_transfer_length = MVUMI_INQUIRY_LENGTH; + memset(frame->cdb, 0, frame->cdb_length); + frame->cdb[0] = INQUIRY; + frame->cdb[4] = frame->data_transfer_length; + + mvumi_issue_blocked_cmd(mhba, cmd); + + if (cmd->cmd_status == SAM_STAT_GOOD) { + if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9143) + wwid = id + 1; + else + memcpy((void *)&wwid, + (cmd->data_buf + MVUMI_INQUIRY_UUID_OFF), + MVUMI_INQUIRY_UUID_LEN); + dev_dbg(&mhba->pdev->dev, + "inquiry device(0:%d:0) wwid(%llx)\n", id, wwid); + } else { + wwid = 0; + } + if (cmd_alloc) + mvumi_delete_internal_cmd(mhba, cmd); + + return wwid; +} + +static void mvumi_detach_devices(struct mvumi_hba *mhba) +{ + struct mvumi_device *mv_dev = NULL , *dev_next; + struct scsi_device *sdev = NULL; + + mutex_lock(&mhba->device_lock); + + /* detach Hard Disk */ + list_for_each_entry_safe(mv_dev, dev_next, + &mhba->shost_dev_list, list) { + mvumi_handle_hotplug(mhba, mv_dev->id, DEVICE_OFFLINE); + list_del_init(&mv_dev->list); + dev_dbg(&mhba->pdev->dev, "release device(0:%d:0) wwid(%llx)\n", + mv_dev->id, mv_dev->wwid); + kfree(mv_dev); + } + list_for_each_entry_safe(mv_dev, dev_next, &mhba->mhba_dev_list, list) { + list_del_init(&mv_dev->list); + dev_dbg(&mhba->pdev->dev, "release device(0:%d:0) wwid(%llx)\n", + mv_dev->id, mv_dev->wwid); + kfree(mv_dev); + } + + /* detach virtual device */ + if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580) + sdev = scsi_device_lookup(mhba->shost, 0, + mhba->max_target_id - 1, 0); + + if (sdev) { + scsi_remove_device(sdev); + scsi_device_put(sdev); + } + + mutex_unlock(&mhba->device_lock); +} + +static void mvumi_rescan_devices(struct mvumi_hba *mhba, int id) +{ + struct scsi_device *sdev; + + sdev = scsi_device_lookup(mhba->shost, 0, id, 0); + if (sdev) { + scsi_rescan_device(&sdev->sdev_gendev); + scsi_device_put(sdev); + } +} + +static int mvumi_match_devices(struct mvumi_hba *mhba, int id, u64 wwid) +{ + struct mvumi_device *mv_dev = NULL; + + list_for_each_entry(mv_dev, &mhba->shost_dev_list, list) { + if (mv_dev->wwid == wwid) { + if (mv_dev->id != id) { + dev_err(&mhba->pdev->dev, + "%s has same wwid[%llx] ," + " but different id[%d %d]\n", + __func__, mv_dev->wwid, mv_dev->id, id); + return -1; + } else { + if (mhba->pdev->device == + PCI_DEVICE_ID_MARVELL_MV9143) + mvumi_rescan_devices(mhba, id); + return 1; + } + } + } + return 0; +} + +static void mvumi_remove_devices(struct mvumi_hba *mhba, int id) +{ + struct mvumi_device *mv_dev = NULL, *dev_next; + + list_for_each_entry_safe(mv_dev, dev_next, + &mhba->shost_dev_list, list) { + if (mv_dev->id == id) { + dev_dbg(&mhba->pdev->dev, + "detach device(0:%d:0) wwid(%llx) from HOST\n", + mv_dev->id, mv_dev->wwid); + mvumi_handle_hotplug(mhba, mv_dev->id, DEVICE_OFFLINE); + list_del_init(&mv_dev->list); + kfree(mv_dev); + } + } +} + +static int mvumi_probe_devices(struct mvumi_hba *mhba) +{ + int id, maxid; + u64 wwid = 0; + struct mvumi_device *mv_dev = NULL; + struct mvumi_cmd *cmd = NULL; + int found = 0; + + cmd = mvumi_create_internal_cmd(mhba, 64); + if (!cmd) + return -1; + + if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9143) + maxid = mhba->max_target_id; + else + maxid = mhba->max_target_id - 1; + + for (id = 0; id < maxid; id++) { + wwid = mvumi_inquiry(mhba, id, cmd); + if (!wwid) { + /* device no response, remove it */ + mvumi_remove_devices(mhba, id); + } else { + /* device response, add it */ + found = mvumi_match_devices(mhba, id, wwid); + if (!found) { + mvumi_remove_devices(mhba, id); + mv_dev = kzalloc(sizeof(struct mvumi_device), + GFP_KERNEL); + if (!mv_dev) { + dev_err(&mhba->pdev->dev, + "%s alloc mv_dev failed\n", + __func__); + continue; + } + mv_dev->id = id; + mv_dev->wwid = wwid; + mv_dev->sdev = NULL; + INIT_LIST_HEAD(&mv_dev->list); + list_add_tail(&mv_dev->list, + &mhba->mhba_dev_list); + dev_dbg(&mhba->pdev->dev, + "probe a new device(0:%d:0)" + " wwid(%llx)\n", id, mv_dev->wwid); + } else if (found == -1) + return -1; + else + continue; + } + } + + if (cmd) + mvumi_delete_internal_cmd(mhba, cmd); + + return 0; +} + +static int mvumi_rescan_bus(void *data) +{ + int ret = 0; + struct mvumi_hba *mhba = (struct mvumi_hba *) data; + struct mvumi_device *mv_dev = NULL , *dev_next; + + while (!kthread_should_stop()) { + + set_current_state(TASK_INTERRUPTIBLE); + if (!atomic_read(&mhba->pnp_count)) + schedule(); + msleep(1000); + atomic_set(&mhba->pnp_count, 0); + __set_current_state(TASK_RUNNING); + + mutex_lock(&mhba->device_lock); + ret = mvumi_probe_devices(mhba); + if (!ret) { + list_for_each_entry_safe(mv_dev, dev_next, + &mhba->mhba_dev_list, list) { + if (mvumi_handle_hotplug(mhba, mv_dev->id, + DEVICE_ONLINE)) { + dev_err(&mhba->pdev->dev, + "%s add device(0:%d:0) failed" + "wwid(%llx) has exist\n", + __func__, + mv_dev->id, mv_dev->wwid); + list_del_init(&mv_dev->list); + kfree(mv_dev); + } else { + list_move_tail(&mv_dev->list, + &mhba->shost_dev_list); + } + } + } + mutex_unlock(&mhba->device_lock); + } + return 0; +} + +static void mvumi_proc_msg(struct mvumi_hba *mhba, + struct mvumi_hotplug_event *param) +{ + u16 size = param->size; + const unsigned long *ar_bitmap; + const unsigned long *re_bitmap; + int index; + + if (mhba->fw_flag & MVUMI_FW_ATTACH) { + index = -1; + ar_bitmap = (const unsigned long *) param->bitmap; + re_bitmap = (const unsigned long *) ¶m->bitmap[size >> 3]; + + mutex_lock(&mhba->sas_discovery_mutex); + do { + index = find_next_zero_bit(ar_bitmap, size, index + 1); + if (index >= size) + break; + mvumi_handle_hotplug(mhba, index, DEVICE_ONLINE); + } while (1); + + index = -1; + do { + index = find_next_zero_bit(re_bitmap, size, index + 1); + if (index >= size) + break; + mvumi_handle_hotplug(mhba, index, DEVICE_OFFLINE); + } while (1); + mutex_unlock(&mhba->sas_discovery_mutex); + } +} + static void mvumi_notification(struct mvumi_hba *mhba, u8 msg, void *buffer) { if (msg == APICDB1_EVENT_GETEVENT) { @@ -1227,6 +1731,8 @@ static void mvumi_notification(struct mvumi_hba *mhba, u8 msg, void *buffer) param = &er->events[i]; mvumi_show_event(mhba, param); } + } else if (msg == APICDB1_HOST_GETEVENT) { + mvumi_proc_msg(mhba, buffer); } } @@ -1271,17 +1777,27 @@ static void mvumi_scan_events(struct work_struct *work) kfree(mu_ev); } -static void mvumi_launch_events(struct mvumi_hba *mhba, u8 msg) +static void mvumi_launch_events(struct mvumi_hba *mhba, u32 isr_status) { struct mvumi_events_wq *mu_ev; - mu_ev = kzalloc(sizeof(*mu_ev), GFP_ATOMIC); - if (mu_ev) { - INIT_WORK(&mu_ev->work_q, mvumi_scan_events); - mu_ev->mhba = mhba; - mu_ev->event = msg; - mu_ev->param = NULL; - schedule_work(&mu_ev->work_q); + while (isr_status & (DRBL_BUS_CHANGE | DRBL_EVENT_NOTIFY)) { + if (isr_status & DRBL_BUS_CHANGE) { + atomic_inc(&mhba->pnp_count); + wake_up_process(mhba->dm_thread); + isr_status &= ~(DRBL_BUS_CHANGE); + continue; + } + + mu_ev = kzalloc(sizeof(*mu_ev), GFP_ATOMIC); + if (mu_ev) { + INIT_WORK(&mu_ev->work_q, mvumi_scan_events); + mu_ev->mhba = mhba; + mu_ev->event = APICDB1_EVENT_GETEVENT; + isr_status &= ~(DRBL_EVENT_NOTIFY); + mu_ev->param = NULL; + schedule_work(&mu_ev->work_q); + } } } @@ -1322,16 +1838,17 @@ static irqreturn_t mvumi_isr_handler(int irq, void *devp) return IRQ_NONE; } - if (mhba->global_isr & INT_MAP_DL_CPU2PCIEA) { + if (mhba->global_isr & mhba->regs->int_dl_cpu2pciea) { + if (mhba->isr_status & (DRBL_BUS_CHANGE | DRBL_EVENT_NOTIFY)) + mvumi_launch_events(mhba, mhba->isr_status); if (mhba->isr_status & DRBL_HANDSHAKE_ISR) { dev_warn(&mhba->pdev->dev, "enter handshake again!\n"); mvumi_handshake(mhba); } - if (mhba->isr_status & DRBL_EVENT_NOTIFY) - mvumi_launch_events(mhba, APICDB1_EVENT_GETEVENT); + } - if (mhba->global_isr & INT_MAP_COMAOUT) + if (mhba->global_isr & mhba->regs->int_comaout) mvumi_receive_ob_list_entry(mhba); mhba->global_isr = 0; @@ -1358,8 +1875,7 @@ static enum mvumi_qc_result mvumi_send_command(struct mvumi_hba *mhba, dev_dbg(&mhba->pdev->dev, "no free tag.\n"); return MV_QUEUE_COMMAND_RESULT_NO_RESOURCE; } - if (mvumi_get_ib_list_entry(mhba, &ib_entry)) - return MV_QUEUE_COMMAND_RESULT_NO_RESOURCE; + mvumi_get_ib_list_entry(mhba, &ib_entry); cmd->frame->tag = tag_get_one(mhba, &mhba->tag_pool); cmd->frame->request_id = mhba->io_seq++; @@ -1367,21 +1883,35 @@ static enum mvumi_qc_result mvumi_send_command(struct mvumi_hba *mhba, mhba->tag_cmd[cmd->frame->tag] = cmd; frame_len = sizeof(*ib_frame) - 4 + ib_frame->sg_counts * sizeof(struct mvumi_sgl); - memcpy(ib_entry, ib_frame, frame_len); + if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) { + struct mvumi_dyn_list_entry *dle; + dle = ib_entry; + dle->src_low_addr = + cpu_to_le32(lower_32_bits(cmd->frame_phys)); + dle->src_high_addr = + cpu_to_le32(upper_32_bits(cmd->frame_phys)); + dle->if_length = (frame_len >> 2) & 0xFFF; + } else { + memcpy(ib_entry, ib_frame, frame_len); + } return MV_QUEUE_COMMAND_RESULT_SENT; } static void mvumi_fire_cmd(struct mvumi_hba *mhba, struct mvumi_cmd *cmd) { unsigned short num_of_cl_sent = 0; + unsigned int count; enum mvumi_qc_result result; if (cmd) list_add_tail(&cmd->queue_pointer, &mhba->waiting_req_list); + count = mhba->instancet->check_ib_list(mhba); + if (list_empty(&mhba->waiting_req_list) || !count) + return; - while (!list_empty(&mhba->waiting_req_list)) { + do { cmd = list_first_entry(&mhba->waiting_req_list, - struct mvumi_cmd, queue_pointer); + struct mvumi_cmd, queue_pointer); list_del_init(&cmd->queue_pointer); result = mvumi_send_command(mhba, cmd); switch (result) { @@ -1395,65 +1925,77 @@ static void mvumi_fire_cmd(struct mvumi_hba *mhba, struct mvumi_cmd *cmd) return; } - } + } while (!list_empty(&mhba->waiting_req_list) && count--); + if (num_of_cl_sent > 0) mvumi_send_ib_list_entry(mhba); } /** * mvumi_enable_intr - Enables interrupts - * @regs: FW register set + * @mhba: Adapter soft state */ -static void mvumi_enable_intr(void *regs) +static void mvumi_enable_intr(struct mvumi_hba *mhba) { unsigned int mask; + struct mvumi_hw_regs *regs = mhba->regs; - iowrite32(0x3FFFFFFF, regs + CPU_ARM_TO_PCIEA_MASK_REG); - mask = ioread32(regs + CPU_ENPOINTA_MASK_REG); - mask |= INT_MAP_DL_CPU2PCIEA | INT_MAP_COMAOUT | INT_MAP_COMAERR; - iowrite32(mask, regs + CPU_ENPOINTA_MASK_REG); + iowrite32(regs->int_drbl_int_mask, regs->arm_to_pciea_mask_reg); + mask = ioread32(regs->enpointa_mask_reg); + mask |= regs->int_dl_cpu2pciea | regs->int_comaout | regs->int_comaerr; + iowrite32(mask, regs->enpointa_mask_reg); } /** * mvumi_disable_intr -Disables interrupt - * @regs: FW register set + * @mhba: Adapter soft state */ -static void mvumi_disable_intr(void *regs) +static void mvumi_disable_intr(struct mvumi_hba *mhba) { unsigned int mask; + struct mvumi_hw_regs *regs = mhba->regs; - iowrite32(0, regs + CPU_ARM_TO_PCIEA_MASK_REG); - mask = ioread32(regs + CPU_ENPOINTA_MASK_REG); - mask &= ~(INT_MAP_DL_CPU2PCIEA | INT_MAP_COMAOUT | INT_MAP_COMAERR); - iowrite32(mask, regs + CPU_ENPOINTA_MASK_REG); + iowrite32(0, regs->arm_to_pciea_mask_reg); + mask = ioread32(regs->enpointa_mask_reg); + mask &= ~(regs->int_dl_cpu2pciea | regs->int_comaout | + regs->int_comaerr); + iowrite32(mask, regs->enpointa_mask_reg); } static int mvumi_clear_intr(void *extend) { struct mvumi_hba *mhba = (struct mvumi_hba *) extend; unsigned int status, isr_status = 0, tmp = 0; - void *regs = mhba->mmio; + struct mvumi_hw_regs *regs = mhba->regs; - status = ioread32(regs + CPU_MAIN_INT_CAUSE_REG); - if (!(status & INT_MAP_MU) || status == 0xFFFFFFFF) + status = ioread32(regs->main_int_cause_reg); + if (!(status & regs->int_mu) || status == 0xFFFFFFFF) return 1; - if (unlikely(status & INT_MAP_COMAERR)) { - tmp = ioread32(regs + CLA_ISR_CAUSE); - if (tmp & (CLIC_IN_ERR_IRQ | CLIC_OUT_ERR_IRQ)) - iowrite32(tmp & (CLIC_IN_ERR_IRQ | CLIC_OUT_ERR_IRQ), - regs + CLA_ISR_CAUSE); - status ^= INT_MAP_COMAERR; + if (unlikely(status & regs->int_comaerr)) { + tmp = ioread32(regs->outb_isr_cause); + if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580) { + if (tmp & regs->clic_out_err) { + iowrite32(tmp & regs->clic_out_err, + regs->outb_isr_cause); + } + } else { + if (tmp & (regs->clic_in_err | regs->clic_out_err)) + iowrite32(tmp & (regs->clic_in_err | + regs->clic_out_err), + regs->outb_isr_cause); + } + status ^= mhba->regs->int_comaerr; /* inbound or outbound parity error, command will timeout */ } - if (status & INT_MAP_COMAOUT) { - tmp = ioread32(regs + CLA_ISR_CAUSE); - if (tmp & CLIC_OUT_IRQ) - iowrite32(tmp & CLIC_OUT_IRQ, regs + CLA_ISR_CAUSE); + if (status & regs->int_comaout) { + tmp = ioread32(regs->outb_isr_cause); + if (tmp & regs->clic_irq) + iowrite32(tmp & regs->clic_irq, regs->outb_isr_cause); } - if (status & INT_MAP_DL_CPU2PCIEA) { - isr_status = ioread32(regs + CPU_ARM_TO_PCIEA_DRBL_REG); + if (status & regs->int_dl_cpu2pciea) { + isr_status = ioread32(regs->arm_to_pciea_drbl_reg); if (isr_status) - iowrite32(isr_status, regs + CPU_ARM_TO_PCIEA_DRBL_REG); + iowrite32(isr_status, regs->arm_to_pciea_drbl_reg); } mhba->global_isr = status; @@ -1464,24 +2006,38 @@ static int mvumi_clear_intr(void *extend) /** * mvumi_read_fw_status_reg - returns the current FW status value - * @regs: FW register set + * @mhba: Adapter soft state */ -static unsigned int mvumi_read_fw_status_reg(void *regs) +static unsigned int mvumi_read_fw_status_reg(struct mvumi_hba *mhba) { unsigned int status; - status = ioread32(regs + CPU_ARM_TO_PCIEA_DRBL_REG); + status = ioread32(mhba->regs->arm_to_pciea_drbl_reg); if (status) - iowrite32(status, regs + CPU_ARM_TO_PCIEA_DRBL_REG); + iowrite32(status, mhba->regs->arm_to_pciea_drbl_reg); return status; } -static struct mvumi_instance_template mvumi_instance_template = { +static struct mvumi_instance_template mvumi_instance_9143 = { .fire_cmd = mvumi_fire_cmd, .enable_intr = mvumi_enable_intr, .disable_intr = mvumi_disable_intr, .clear_intr = mvumi_clear_intr, .read_fw_status_reg = mvumi_read_fw_status_reg, + .check_ib_list = mvumi_check_ib_list_9143, + .check_ob_list = mvumi_check_ob_list_9143, + .reset_host = mvumi_reset_host_9143, +}; + +static struct mvumi_instance_template mvumi_instance_9580 = { + .fire_cmd = mvumi_fire_cmd, + .enable_intr = mvumi_enable_intr, + .disable_intr = mvumi_disable_intr, + .clear_intr = mvumi_clear_intr, + .read_fw_status_reg = mvumi_read_fw_status_reg, + .check_ib_list = mvumi_check_ib_list_9580, + .check_ob_list = mvumi_check_ob_list_9580, + .reset_host = mvumi_reset_host_9580, }; static int mvumi_slave_configure(struct scsi_device *sdev) @@ -1681,6 +2237,124 @@ static struct scsi_transport_template mvumi_transport_template = { .eh_timed_out = mvumi_timed_out, }; +static int mvumi_cfg_hw_reg(struct mvumi_hba *mhba) +{ + void *base = NULL; + struct mvumi_hw_regs *regs; + + switch (mhba->pdev->device) { + case PCI_DEVICE_ID_MARVELL_MV9143: + mhba->mmio = mhba->base_addr[0]; + base = mhba->mmio; + if (!mhba->regs) { + mhba->regs = kzalloc(sizeof(*regs), GFP_KERNEL); + if (mhba->regs == NULL) + return -ENOMEM; + } + regs = mhba->regs; + + /* For Arm */ + regs->ctrl_sts_reg = base + 0x20104; + regs->rstoutn_mask_reg = base + 0x20108; + regs->sys_soft_rst_reg = base + 0x2010C; + regs->main_int_cause_reg = base + 0x20200; + regs->enpointa_mask_reg = base + 0x2020C; + regs->rstoutn_en_reg = base + 0xF1400; + /* For Doorbell */ + regs->pciea_to_arm_drbl_reg = base + 0x20400; + regs->arm_to_pciea_drbl_reg = base + 0x20408; + regs->arm_to_pciea_mask_reg = base + 0x2040C; + regs->pciea_to_arm_msg0 = base + 0x20430; + regs->pciea_to_arm_msg1 = base + 0x20434; + regs->arm_to_pciea_msg0 = base + 0x20438; + regs->arm_to_pciea_msg1 = base + 0x2043C; + + /* For Message Unit */ + + regs->inb_aval_count_basel = base + 0x508; + regs->inb_aval_count_baseh = base + 0x50C; + regs->inb_write_pointer = base + 0x518; + regs->inb_read_pointer = base + 0x51C; + regs->outb_coal_cfg = base + 0x568; + regs->outb_copy_basel = base + 0x5B0; + regs->outb_copy_baseh = base + 0x5B4; + regs->outb_copy_pointer = base + 0x544; + regs->outb_read_pointer = base + 0x548; + regs->outb_isr_cause = base + 0x560; + regs->outb_coal_cfg = base + 0x568; + /* Bit setting for HW */ + regs->int_comaout = 1 << 8; + regs->int_comaerr = 1 << 6; + regs->int_dl_cpu2pciea = 1 << 1; + regs->cl_pointer_toggle = 1 << 12; + regs->clic_irq = 1 << 1; + regs->clic_in_err = 1 << 8; + regs->clic_out_err = 1 << 12; + regs->cl_slot_num_mask = 0xFFF; + regs->int_drbl_int_mask = 0x3FFFFFFF; + regs->int_mu = regs->int_dl_cpu2pciea | regs->int_comaout | + regs->int_comaerr; + break; + case PCI_DEVICE_ID_MARVELL_MV9580: + mhba->mmio = mhba->base_addr[2]; + base = mhba->mmio; + if (!mhba->regs) { + mhba->regs = kzalloc(sizeof(*regs), GFP_KERNEL); + if (mhba->regs == NULL) + return -ENOMEM; + } + regs = mhba->regs; + /* For Arm */ + regs->ctrl_sts_reg = base + 0x20104; + regs->rstoutn_mask_reg = base + 0x1010C; + regs->sys_soft_rst_reg = base + 0x10108; + regs->main_int_cause_reg = base + 0x10200; + regs->enpointa_mask_reg = base + 0x1020C; + regs->rstoutn_en_reg = base + 0xF1400; + + /* For Doorbell */ + regs->pciea_to_arm_drbl_reg = base + 0x10460; + regs->arm_to_pciea_drbl_reg = base + 0x10480; + regs->arm_to_pciea_mask_reg = base + 0x10484; + regs->pciea_to_arm_msg0 = base + 0x10400; + regs->pciea_to_arm_msg1 = base + 0x10404; + regs->arm_to_pciea_msg0 = base + 0x10420; + regs->arm_to_pciea_msg1 = base + 0x10424; + + /* For reset*/ + regs->reset_request = base + 0x10108; + regs->reset_enable = base + 0x1010c; + + /* For Message Unit */ + regs->inb_aval_count_basel = base + 0x4008; + regs->inb_aval_count_baseh = base + 0x400C; + regs->inb_write_pointer = base + 0x4018; + regs->inb_read_pointer = base + 0x401C; + regs->outb_copy_basel = base + 0x4058; + regs->outb_copy_baseh = base + 0x405C; + regs->outb_copy_pointer = base + 0x406C; + regs->outb_read_pointer = base + 0x4070; + regs->outb_coal_cfg = base + 0x4080; + regs->outb_isr_cause = base + 0x4088; + /* Bit setting for HW */ + regs->int_comaout = 1 << 4; + regs->int_dl_cpu2pciea = 1 << 12; + regs->int_comaerr = 1 << 29; + regs->cl_pointer_toggle = 1 << 14; + regs->cl_slot_num_mask = 0x3FFF; + regs->clic_irq = 1 << 0; + regs->clic_out_err = 1 << 1; + regs->int_drbl_int_mask = 0x3FFFFFFF; + regs->int_mu = regs->int_dl_cpu2pciea | regs->int_comaout; + break; + default: + return -1; + break; + } + + return 0; +} + /** * mvumi_init_fw - Initializes the FW * @mhba: Adapter soft state @@ -1699,15 +2373,18 @@ static int mvumi_init_fw(struct mvumi_hba *mhba) if (ret) goto fail_ioremap; - mhba->mmio = mhba->base_addr[0]; - switch (mhba->pdev->device) { case PCI_DEVICE_ID_MARVELL_MV9143: - mhba->instancet = &mvumi_instance_template; + mhba->instancet = &mvumi_instance_9143; mhba->io_seq = 0; mhba->max_sge = MVUMI_MAX_SG_ENTRY; mhba->request_id_enabled = 1; break; + case PCI_DEVICE_ID_MARVELL_MV9580: + mhba->instancet = &mvumi_instance_9580; + mhba->io_seq = 0; + mhba->max_sge = MVUMI_MAX_SG_ENTRY; + break; default: dev_err(&mhba->pdev->dev, "device 0x%x not supported!\n", mhba->pdev->device); @@ -1717,15 +2394,21 @@ static int mvumi_init_fw(struct mvumi_hba *mhba) } dev_dbg(&mhba->pdev->dev, "device id : %04X is found.\n", mhba->pdev->device); - - mhba->handshake_page = kzalloc(HSP_MAX_SIZE, GFP_KERNEL); + ret = mvumi_cfg_hw_reg(mhba); + if (ret) { + dev_err(&mhba->pdev->dev, + "failed to allocate memory for reg\n"); + ret = -ENOMEM; + goto fail_alloc_mem; + } + mhba->handshake_page = pci_alloc_consistent(mhba->pdev, HSP_MAX_SIZE, + &mhba->handshake_page_phys); if (!mhba->handshake_page) { dev_err(&mhba->pdev->dev, "failed to allocate memory for handshake\n"); ret = -ENOMEM; - goto fail_alloc_mem; + goto fail_alloc_page; } - mhba->handshake_page_phys = virt_to_phys(mhba->handshake_page); if (mvumi_start(mhba)) { ret = -EINVAL; @@ -1739,7 +2422,10 @@ static int mvumi_init_fw(struct mvumi_hba *mhba) fail_ready_state: mvumi_release_mem_resource(mhba); - kfree(mhba->handshake_page); + pci_free_consistent(mhba->pdev, HSP_MAX_SIZE, + mhba->handshake_page, mhba->handshake_page_phys); +fail_alloc_page: + kfree(mhba->regs); fail_alloc_mem: mvumi_unmap_pci_addr(mhba->pdev, mhba->base_addr); fail_ioremap: @@ -1755,6 +2441,7 @@ fail_ioremap: static int mvumi_io_attach(struct mvumi_hba *mhba) { struct Scsi_Host *host = mhba->shost; + struct scsi_device *sdev = NULL; int ret; unsigned int max_sg = (mhba->ib_max_size + 4 - sizeof(struct mvumi_msg_frame)) / sizeof(struct mvumi_sgl); @@ -1764,7 +2451,7 @@ static int mvumi_io_attach(struct mvumi_hba *mhba) host->can_queue = (mhba->max_io - 1) ? (mhba->max_io - 1) : 1; host->sg_tablesize = mhba->max_sge > max_sg ? max_sg : mhba->max_sge; host->max_sectors = mhba->max_transfer_size / 512; - host->cmd_per_lun = (mhba->max_io - 1) ? (mhba->max_io - 1) : 1; + host->cmd_per_lun = (mhba->max_io - 1) ? (mhba->max_io - 1) : 1; host->max_id = mhba->max_target_id; host->max_cmd_len = MAX_COMMAND_SIZE; host->transportt = &mvumi_transport_template; @@ -1775,9 +2462,43 @@ static int mvumi_io_attach(struct mvumi_hba *mhba) return ret; } mhba->fw_flag |= MVUMI_FW_ATTACH; - scsi_scan_host(host); + mutex_lock(&mhba->sas_discovery_mutex); + if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580) + ret = scsi_add_device(host, 0, mhba->max_target_id - 1, 0); + else + ret = 0; + if (ret) { + dev_err(&mhba->pdev->dev, "add virtual device failed\n"); + mutex_unlock(&mhba->sas_discovery_mutex); + goto fail_add_device; + } + + mhba->dm_thread = kthread_create(mvumi_rescan_bus, + mhba, "mvumi_scanthread"); + if (IS_ERR(mhba->dm_thread)) { + dev_err(&mhba->pdev->dev, + "failed to create device scan thread\n"); + mutex_unlock(&mhba->sas_discovery_mutex); + goto fail_create_thread; + } + atomic_set(&mhba->pnp_count, 1); + wake_up_process(mhba->dm_thread); + + mutex_unlock(&mhba->sas_discovery_mutex); return 0; + +fail_create_thread: + if (mhba->pdev->device == PCI_DEVICE_ID_MARVELL_MV9580) + sdev = scsi_device_lookup(mhba->shost, 0, + mhba->max_target_id - 1, 0); + if (sdev) { + scsi_remove_device(sdev); + scsi_device_put(sdev); + } +fail_add_device: + scsi_remove_host(mhba->shost); + return ret; } /** @@ -1828,8 +2549,12 @@ static int __devinit mvumi_probe_one(struct pci_dev *pdev, INIT_LIST_HEAD(&mhba->free_ob_list); INIT_LIST_HEAD(&mhba->res_list); INIT_LIST_HEAD(&mhba->waiting_req_list); + mutex_init(&mhba->device_lock); + INIT_LIST_HEAD(&mhba->mhba_dev_list); + INIT_LIST_HEAD(&mhba->shost_dev_list); atomic_set(&mhba->fw_outstanding, 0); init_waitqueue_head(&mhba->int_cmd_wait_q); + mutex_init(&mhba->sas_discovery_mutex); mhba->pdev = pdev; mhba->shost = host; @@ -1845,19 +2570,22 @@ static int __devinit mvumi_probe_one(struct pci_dev *pdev, dev_err(&pdev->dev, "failed to register IRQ\n"); goto fail_init_irq; } - mhba->instancet->enable_intr(mhba->mmio); + + mhba->instancet->enable_intr(mhba); pci_set_drvdata(pdev, mhba); ret = mvumi_io_attach(mhba); if (ret) goto fail_io_attach; + + mvumi_backup_bar_addr(mhba); dev_dbg(&pdev->dev, "probe mvumi driver successfully.\n"); return 0; fail_io_attach: pci_set_drvdata(pdev, NULL); - mhba->instancet->disable_intr(mhba->mmio); + mhba->instancet->disable_intr(mhba); free_irq(mhba->pdev->irq, mhba); fail_init_irq: mvumi_release_fw(mhba); @@ -1877,11 +2605,17 @@ static void mvumi_detach_one(struct pci_dev *pdev) struct mvumi_hba *mhba; mhba = pci_get_drvdata(pdev); + if (mhba->dm_thread) { + kthread_stop(mhba->dm_thread); + mhba->dm_thread = NULL; + } + + mvumi_detach_devices(mhba); host = mhba->shost; scsi_remove_host(mhba->shost); mvumi_flush_cache(mhba); - mhba->instancet->disable_intr(mhba->mmio); + mhba->instancet->disable_intr(mhba); free_irq(mhba->pdev->irq, mhba); mvumi_release_fw(mhba); scsi_host_put(host); @@ -1909,7 +2643,7 @@ static int mvumi_suspend(struct pci_dev *pdev, pm_message_t state) mvumi_flush_cache(mhba); pci_set_drvdata(pdev, mhba); - mhba->instancet->disable_intr(mhba->mmio); + mhba->instancet->disable_intr(mhba); free_irq(mhba->pdev->irq, mhba); mvumi_unmap_pci_addr(pdev, mhba->base_addr); pci_release_regions(pdev); @@ -1956,8 +2690,13 @@ static int mvumi_resume(struct pci_dev *pdev) if (ret) goto release_regions; + if (mvumi_cfg_hw_reg(mhba)) { + ret = -EINVAL; + goto unmap_pci_addr; + } + mhba->mmio = mhba->base_addr[0]; - mvumi_reset(mhba->mmio); + mvumi_reset(mhba); if (mvumi_start(mhba)) { ret = -EINVAL; @@ -1970,7 +2709,7 @@ static int mvumi_resume(struct pci_dev *pdev) dev_err(&pdev->dev, "failed to register IRQ\n"); goto unmap_pci_addr; } - mhba->instancet->enable_intr(mhba->mmio); + mhba->instancet->enable_intr(mhba); return 0; diff --git a/drivers/scsi/mvumi.h b/drivers/scsi/mvumi.h index 10b9237566f..e360135fd1b 100644 --- a/drivers/scsi/mvumi.h +++ b/drivers/scsi/mvumi.h @@ -34,51 +34,87 @@ #define MV_DRIVER_NAME "mvumi" #define PCI_VENDOR_ID_MARVELL_2 0x1b4b #define PCI_DEVICE_ID_MARVELL_MV9143 0x9143 +#define PCI_DEVICE_ID_MARVELL_MV9580 0x9580 #define MVUMI_INTERNAL_CMD_WAIT_TIME 45 +#define MVUMI_INQUIRY_LENGTH 44 +#define MVUMI_INQUIRY_UUID_OFF 36 +#define MVUMI_INQUIRY_UUID_LEN 8 #define IS_DMA64 (sizeof(dma_addr_t) == 8) enum mvumi_qc_result { - MV_QUEUE_COMMAND_RESULT_SENT = 0, + MV_QUEUE_COMMAND_RESULT_SENT = 0, MV_QUEUE_COMMAND_RESULT_NO_RESOURCE, }; -enum { - /*******************************************/ - - /* ARM Mbus Registers Map */ - - /*******************************************/ - CPU_MAIN_INT_CAUSE_REG = 0x20200, - CPU_MAIN_IRQ_MASK_REG = 0x20204, - CPU_MAIN_FIQ_MASK_REG = 0x20208, - CPU_ENPOINTA_MASK_REG = 0x2020C, - CPU_ENPOINTB_MASK_REG = 0x20210, - - INT_MAP_COMAERR = 1 << 6, - INT_MAP_COMAIN = 1 << 7, - INT_MAP_COMAOUT = 1 << 8, - INT_MAP_COMBERR = 1 << 9, - INT_MAP_COMBIN = 1 << 10, - INT_MAP_COMBOUT = 1 << 11, - - INT_MAP_COMAINT = (INT_MAP_COMAOUT | INT_MAP_COMAERR), - INT_MAP_COMBINT = (INT_MAP_COMBOUT | INT_MAP_COMBIN | INT_MAP_COMBERR), - - INT_MAP_DL_PCIEA2CPU = 1 << 0, - INT_MAP_DL_CPU2PCIEA = 1 << 1, - - /***************************************/ +struct mvumi_hw_regs { + /* For CPU */ + void *main_int_cause_reg; + void *enpointa_mask_reg; + void *enpointb_mask_reg; + void *rstoutn_en_reg; + void *ctrl_sts_reg; + void *rstoutn_mask_reg; + void *sys_soft_rst_reg; + + /* For Doorbell */ + void *pciea_to_arm_drbl_reg; + void *arm_to_pciea_drbl_reg; + void *arm_to_pciea_mask_reg; + void *pciea_to_arm_msg0; + void *pciea_to_arm_msg1; + void *arm_to_pciea_msg0; + void *arm_to_pciea_msg1; + + /* reset register */ + void *reset_request; + void *reset_enable; + + /* For Message Unit */ + void *inb_list_basel; + void *inb_list_baseh; + void *inb_aval_count_basel; + void *inb_aval_count_baseh; + void *inb_write_pointer; + void *inb_read_pointer; + void *outb_list_basel; + void *outb_list_baseh; + void *outb_copy_basel; + void *outb_copy_baseh; + void *outb_copy_pointer; + void *outb_read_pointer; + void *inb_isr_cause; + void *outb_isr_cause; + void *outb_coal_cfg; + void *outb_coal_timeout; + + /* Bit setting for HW */ + u32 int_comaout; + u32 int_comaerr; + u32 int_dl_cpu2pciea; + u32 int_mu; + u32 int_drbl_int_mask; + u32 int_main_int_mask; + u32 cl_pointer_toggle; + u32 cl_slot_num_mask; + u32 clic_irq; + u32 clic_in_err; + u32 clic_out_err; +}; - /* ARM Doorbell Registers Map */ +struct mvumi_dyn_list_entry { + u32 src_low_addr; + u32 src_high_addr; + u32 if_length; + u32 reserve; +}; - /***************************************/ - CPU_PCIEA_TO_ARM_DRBL_REG = 0x20400, - CPU_PCIEA_TO_ARM_MASK_REG = 0x20404, - CPU_ARM_TO_PCIEA_DRBL_REG = 0x20408, - CPU_ARM_TO_PCIEA_MASK_REG = 0x2040C, +#define SCSI_CMD_MARVELL_SPECIFIC 0xE1 +#define CDB_CORE_MODULE 0x1 +#define CDB_CORE_SHUTDOWN 0xB +enum { DRBL_HANDSHAKE = 1 << 0, DRBL_SOFT_RESET = 1 << 1, DRBL_BUS_CHANGE = 1 << 2, @@ -86,46 +122,6 @@ enum { DRBL_MU_RESET = 1 << 4, DRBL_HANDSHAKE_ISR = DRBL_HANDSHAKE, - CPU_PCIEA_TO_ARM_MSG0 = 0x20430, - CPU_PCIEA_TO_ARM_MSG1 = 0x20434, - CPU_ARM_TO_PCIEA_MSG0 = 0x20438, - CPU_ARM_TO_PCIEA_MSG1 = 0x2043C, - - /*******************************************/ - - /* ARM Communication List Registers Map */ - - /*******************************************/ - CLA_INB_LIST_BASEL = 0x500, - CLA_INB_LIST_BASEH = 0x504, - CLA_INB_AVAL_COUNT_BASEL = 0x508, - CLA_INB_AVAL_COUNT_BASEH = 0x50C, - CLA_INB_DESTI_LIST_BASEL = 0x510, - CLA_INB_DESTI_LIST_BASEH = 0x514, - CLA_INB_WRITE_POINTER = 0x518, - CLA_INB_READ_POINTER = 0x51C, - - CLA_OUTB_LIST_BASEL = 0x530, - CLA_OUTB_LIST_BASEH = 0x534, - CLA_OUTB_SOURCE_LIST_BASEL = 0x538, - CLA_OUTB_SOURCE_LIST_BASEH = 0x53C, - CLA_OUTB_COPY_POINTER = 0x544, - CLA_OUTB_READ_POINTER = 0x548, - - CLA_ISR_CAUSE = 0x560, - CLA_ISR_MASK = 0x564, - - INT_MAP_MU = (INT_MAP_DL_CPU2PCIEA | INT_MAP_COMAINT), - - CL_POINTER_TOGGLE = 1 << 12, - - CLIC_IN_IRQ = 1 << 0, - CLIC_OUT_IRQ = 1 << 1, - CLIC_IN_ERR_IRQ = 1 << 8, - CLIC_OUT_ERR_IRQ = 1 << 12, - - CL_SLOT_NUM_MASK = 0xFFF, - /* * Command flag is the flag for the CDB command itself */ @@ -137,15 +133,23 @@ enum { CMD_FLAG_DATA_IN = 1 << 3, /* 1-host write data */ CMD_FLAG_DATA_OUT = 1 << 4, - - SCSI_CMD_MARVELL_SPECIFIC = 0xE1, - CDB_CORE_SHUTDOWN = 0xB, + CMD_FLAG_PRDT_IN_HOST = 1 << 5, }; #define APICDB0_EVENT 0xF4 #define APICDB1_EVENT_GETEVENT 0 +#define APICDB1_HOST_GETEVENT 1 #define MAX_EVENTS_RETURNED 6 +#define DEVICE_OFFLINE 0 +#define DEVICE_ONLINE 1 + +struct mvumi_hotplug_event { + u16 size; + u8 dummy[2]; + u8 bitmap[0]; +}; + struct mvumi_driver_event { u32 time_stamp; u32 sequence_no; @@ -172,8 +176,14 @@ struct mvumi_events_wq { void *param; }; +#define HS_CAPABILITY_SUPPORT_COMPACT_SG (1U << 4) +#define HS_CAPABILITY_SUPPORT_PRD_HOST (1U << 5) +#define HS_CAPABILITY_SUPPORT_DYN_SRC (1U << 6) +#define HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF (1U << 14) + #define MVUMI_MAX_SG_ENTRY 32 #define SGD_EOT (1L << 27) +#define SGD_EOT_CP (1L << 22) struct mvumi_sgl { u32 baseaddr_l; @@ -181,6 +191,39 @@ struct mvumi_sgl { u32 flags; u32 size; }; +struct mvumi_compact_sgl { + u32 baseaddr_l; + u32 baseaddr_h; + u32 flags; +}; + +#define GET_COMPACT_SGD_SIZE(sgd) \ + ((((struct mvumi_compact_sgl *)(sgd))->flags) & 0x3FFFFFL) + +#define SET_COMPACT_SGD_SIZE(sgd, sz) do { \ + (((struct mvumi_compact_sgl *)(sgd))->flags) &= ~0x3FFFFFL; \ + (((struct mvumi_compact_sgl *)(sgd))->flags) |= (sz); \ +} while (0) +#define sgd_getsz(_mhba, sgd, sz) do { \ + if (_mhba->hba_capability & HS_CAPABILITY_SUPPORT_COMPACT_SG) \ + (sz) = GET_COMPACT_SGD_SIZE(sgd); \ + else \ + (sz) = (sgd)->size; \ +} while (0) + +#define sgd_setsz(_mhba, sgd, sz) do { \ + if (_mhba->hba_capability & HS_CAPABILITY_SUPPORT_COMPACT_SG) \ + SET_COMPACT_SGD_SIZE(sgd, sz); \ + else \ + (sgd)->size = (sz); \ +} while (0) + +#define sgd_inc(_mhba, sgd) do { \ + if (_mhba->hba_capability & HS_CAPABILITY_SUPPORT_COMPACT_SG) \ + sgd = (struct mvumi_sgl *)(((unsigned char *) (sgd)) + 12); \ + else \ + sgd = (struct mvumi_sgl *)(((unsigned char *) (sgd)) + 16); \ +} while (0) struct mvumi_res { struct list_head entry; @@ -197,7 +240,7 @@ enum resource_type { }; struct mvumi_sense_data { - u8 error_eode:7; + u8 error_code:7; u8 valid:1; u8 segment_number; u8 sense_key:4; @@ -220,6 +263,7 @@ struct mvumi_sense_data { struct mvumi_cmd { struct list_head queue_pointer; struct mvumi_msg_frame *frame; + dma_addr_t frame_phys; struct scsi_cmnd *scmd; atomic_t sync_cmd; void *data_buf; @@ -393,7 +437,8 @@ struct mvumi_hs_page2 { u16 frame_length; u8 host_type; - u8 reserved[3]; + u8 host_cap; + u8 reserved[2]; struct version_info host_ver; u32 system_io_bus; u32 slot_number; @@ -435,8 +480,17 @@ struct mvumi_tag { unsigned short size; }; +struct mvumi_device { + struct list_head list; + struct scsi_device *sdev; + u64 wwid; + u8 dev_type; + int id; +}; + struct mvumi_hba { void *base_addr[MAX_BASE_ADDRESS]; + u32 pci_base[MAX_BASE_ADDRESS]; void *mmio; struct list_head cmd_pool; struct Scsi_Host *shost; @@ -449,6 +503,9 @@ struct mvumi_hba { void *ib_list; dma_addr_t ib_list_phys; + void *ib_frame; + dma_addr_t ib_frame_phys; + void *ob_list; dma_addr_t ob_list_phys; @@ -477,12 +534,14 @@ struct mvumi_hba { unsigned char hba_total_pages; unsigned char fw_flag; unsigned char request_id_enabled; + unsigned char eot_flag; unsigned short hba_capability; unsigned short io_seq; unsigned int ib_cur_slot; unsigned int ob_cur_slot; unsigned int fw_state; + struct mutex sas_discovery_mutex; struct list_head ob_data_list; struct list_head free_ob_list; @@ -491,14 +550,24 @@ struct mvumi_hba { struct mvumi_tag tag_pool; struct mvumi_cmd **tag_cmd; + struct mvumi_hw_regs *regs; + struct mutex device_lock; + struct list_head mhba_dev_list; + struct list_head shost_dev_list; + struct task_struct *dm_thread; + atomic_t pnp_count; }; struct mvumi_instance_template { - void (*fire_cmd)(struct mvumi_hba *, struct mvumi_cmd *); - void (*enable_intr)(void *) ; - void (*disable_intr)(void *); - int (*clear_intr)(void *); - unsigned int (*read_fw_status_reg)(void *); + void (*fire_cmd) (struct mvumi_hba *, struct mvumi_cmd *); + void (*enable_intr) (struct mvumi_hba *); + void (*disable_intr) (struct mvumi_hba *); + int (*clear_intr) (void *); + unsigned int (*read_fw_status_reg) (struct mvumi_hba *); + unsigned int (*check_ib_list) (struct mvumi_hba *); + int (*check_ob_list) (struct mvumi_hba *, unsigned int *, + unsigned int *); + int (*reset_host) (struct mvumi_hba *); }; extern struct timezone sys_tz; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 799a58bb985..48fca47384b 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -2080,6 +2080,7 @@ qla2x00_configure_hba(scsi_qla_host_t *vha) uint8_t domain; char connect_type[22]; struct qla_hw_data *ha = vha->hw; + unsigned long flags; /* Get host addresses. */ rval = qla2x00_get_adapter_id(vha, @@ -2154,9 +2155,9 @@ qla2x00_configure_hba(scsi_qla_host_t *vha) vha->d_id.b.area = area; vha->d_id.b.al_pa = al_pa; - spin_lock(&ha->vport_slock); + spin_lock_irqsave(&ha->vport_slock, flags); qlt_update_vp_map(vha, SET_AL_PA); - spin_unlock(&ha->vport_slock); + spin_unlock_irqrestore(&ha->vport_slock, flags); if (!vha->flags.init_done) ql_log(ql_log_info, vha, 0x2010, diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 57fbd5a3d4e..5cda11c07c6 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -2055,7 +2055,7 @@ static void unmap_region(sector_t lba, unsigned int len) block = lba + alignment; rem = do_div(block, granularity); - if (rem == 0 && lba + granularity <= end && block < map_size) { + if (rem == 0 && lba + granularity < end && block < map_size) { clear_bit(block, map_storep); if (scsi_debug_lbprz) memset(fake_storep + diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index de2337f255a..c1b05a83d40 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -789,7 +789,6 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, int cmnd_size, int timeout, unsigned sense_bytes) { struct scsi_device *sdev = scmd->device; - struct scsi_driver *sdrv = scsi_cmd_to_driver(scmd); struct Scsi_Host *shost = sdev->host; DECLARE_COMPLETION_ONSTACK(done); unsigned long timeleft; @@ -845,8 +844,11 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, scsi_eh_restore_cmnd(scmd, &ses); - if (sdrv && sdrv->eh_action) - rtn = sdrv->eh_action(scmd, cmnd, cmnd_size, rtn); + if (scmd->request->cmd_type != REQ_TYPE_BLOCK_PC) { + struct scsi_driver *sdrv = scsi_cmd_to_driver(scmd); + if (sdrv->eh_action) + rtn = sdrv->eh_action(scmd, cmnd, cmnd_size, rtn); + } return rtn; } diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c index 528d52beaa1..01440782feb 100644 --- a/drivers/scsi/storvsc_drv.c +++ b/drivers/scsi/storvsc_drv.c @@ -1221,7 +1221,12 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd) /* * At this point, all outstanding requests in the adapter * should have been flushed out and return to us + * There is a potential race here where the host may be in + * the process of responding when we return from here. + * Just wait for all in-transit packets to be accounted for + * before we return from here. */ + storvsc_wait_to_drain(stor_device); return SUCCESS; } diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 3e79a2f0004..595af1ae442 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -219,7 +219,7 @@ static int virtscsi_kick_event(struct virtio_scsi *vscsi, struct scatterlist sg; unsigned long flags; - sg_set_buf(&sg, &event_node->event, sizeof(struct virtio_scsi_event)); + sg_init_one(&sg, &event_node->event, sizeof(struct virtio_scsi_event)); spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags); @@ -279,6 +279,31 @@ static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi, } } +static void virtscsi_handle_param_change(struct virtio_scsi *vscsi, + struct virtio_scsi_event *event) +{ + struct scsi_device *sdev; + struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev); + unsigned int target = event->lun[1]; + unsigned int lun = (event->lun[2] << 8) | event->lun[3]; + u8 asc = event->reason & 255; + u8 ascq = event->reason >> 8; + + sdev = scsi_device_lookup(shost, 0, target, lun); + if (!sdev) { + pr_err("SCSI device %d 0 %d %d not found\n", + shost->host_no, target, lun); + return; + } + + /* Handle "Parameters changed", "Mode parameters changed", and + "Capacity data has changed". */ + if (asc == 0x2a && (ascq == 0x00 || ascq == 0x01 || ascq == 0x09)) + scsi_rescan_device(&sdev->sdev_gendev); + + scsi_device_put(sdev); +} + static void virtscsi_handle_event(struct work_struct *work) { struct virtio_scsi_event_node *event_node = @@ -297,6 +322,9 @@ static void virtscsi_handle_event(struct work_struct *work) case VIRTIO_SCSI_T_TRANSPORT_RESET: virtscsi_handle_transport_reset(vscsi, event); break; + case VIRTIO_SCSI_T_PARAM_CHANGE: + virtscsi_handle_param_change(vscsi, event); + break; default: pr_err("Unsupport virtio scsi event %x\n", event->event); } @@ -677,7 +705,11 @@ static int __devinit virtscsi_probe(struct virtio_device *vdev) cmd_per_lun = virtscsi_config_get(vdev, cmd_per_lun) ?: 1; shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue); shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF; - shost->max_lun = virtscsi_config_get(vdev, max_lun) + 1; + + /* LUNs > 256 are reported with format 1, so they go in the range + * 16640-32767. + */ + shost->max_lun = virtscsi_config_get(vdev, max_lun) + 1 + 0x4000; shost->max_id = num_targets; shost->max_channel = 0; shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE; @@ -733,7 +765,8 @@ static struct virtio_device_id id_table[] = { }; static unsigned int features[] = { - VIRTIO_SCSI_F_HOTPLUG + VIRTIO_SCSI_F_HOTPLUG, + VIRTIO_SCSI_F_CHANGE, }; static struct virtio_driver virtio_scsi_driver = { diff --git a/drivers/staging/omap-thermal/omap-thermal-common.c b/drivers/staging/omap-thermal/omap-thermal-common.c index 46ee0a9f49d..5c0c203b887 100644 --- a/drivers/staging/omap-thermal/omap-thermal-common.c +++ b/drivers/staging/omap-thermal/omap-thermal-common.c @@ -126,7 +126,9 @@ static int omap_thermal_bind(struct thermal_zone_device *thermal, /* TODO: bind with min and max states */ /* Simple thing, two trips, one passive another critical */ - return thermal_zone_bind_cooling_device(thermal, 0, cdev); + return thermal_zone_bind_cooling_device(thermal, 0, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT); } /* Unbind callback functions for thermal zone */ @@ -268,7 +270,6 @@ int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id, /* Create thermal zone */ data->omap_thermal = thermal_zone_device_register(domain, OMAP_TRIP_NUMBER, 0, data, &omap_thermal_ops, - 1, 2, /*TODO: remove this when FW allows */ FAST_TEMP_MONITORING_RATE, FAST_TEMP_MONITORING_RATE); if (IS_ERR_OR_NULL(data->omap_thermal)) { diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 3ab2bd540b5..edfd67d2501 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -19,6 +19,17 @@ config THERMAL_HWMON depends on HWMON=y || HWMON=THERMAL default y +config CPU_THERMAL + bool "generic cpu cooling support" + depends on THERMAL && CPU_FREQ + help + This implements the generic cpu cooling mechanism through frequency + reduction, cpu hotplug and any other ways of reducing temperature. An + ACPI version of this already exists(drivers/acpi/processor_thermal.c). + This will be useful for platforms using the generic thermal interface + and not the ACPI interface. + If you want this support, you should say Y here. + config SPEAR_THERMAL bool "SPEAr thermal sensor driver" depends on THERMAL @@ -27,3 +38,18 @@ config SPEAR_THERMAL help Enable this to plug the SPEAr thermal sensor driver into the Linux thermal framework + +config RCAR_THERMAL + tristate "Renesas R-Car thermal driver" + depends on THERMAL + depends on ARCH_SHMOBILE + help + Enable this to plug the R-Car thermal sensor driver into the Linux + thermal framework + +config EXYNOS_THERMAL + tristate "Temperature sensor on Samsung EXYNOS" + depends on (ARCH_EXYNOS4 || ARCH_EXYNOS5) && THERMAL + help + If you say yes here you get support for TMU (Thermal Managment + Unit) on SAMSUNG EXYNOS series of SoC. diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index a9fff0bf4b1..885550dc64b 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -3,4 +3,7 @@ # obj-$(CONFIG_THERMAL) += thermal_sys.o -obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
\ No newline at end of file +obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o +obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o +obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o +obj-$(CONFIG_EXYNOS_THERMAL) += exynos_thermal.o diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c new file mode 100644 index 00000000000..cc1c930a90e --- /dev/null +++ b/drivers/thermal/cpu_cooling.c @@ -0,0 +1,449 @@ +/* + * linux/drivers/thermal/cpu_cooling.c + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com) + * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/thermal.h> +#include <linux/platform_device.h> +#include <linux/cpufreq.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/cpu.h> +#include <linux/cpu_cooling.h> + +/** + * struct cpufreq_cooling_device + * @id: unique integer value corresponding to each cpufreq_cooling_device + * registered. + * @cool_dev: thermal_cooling_device pointer to keep track of the the + * egistered cooling device. + * @cpufreq_state: integer value representing the current state of cpufreq + * cooling devices. + * @cpufreq_val: integer value representing the absolute value of the clipped + * frequency. + * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device. + * @node: list_head to link all cpufreq_cooling_device together. + * + * This structure is required for keeping information of each + * cpufreq_cooling_device registered as a list whose head is represented by + * cooling_cpufreq_list. In order to prevent corruption of this list a + * mutex lock cooling_cpufreq_lock is used. + */ +struct cpufreq_cooling_device { + int id; + struct thermal_cooling_device *cool_dev; + unsigned int cpufreq_state; + unsigned int cpufreq_val; + struct cpumask allowed_cpus; + struct list_head node; +}; +static LIST_HEAD(cooling_cpufreq_list); +static DEFINE_IDR(cpufreq_idr); + +static struct mutex cooling_cpufreq_lock; + +/* notify_table passes value to the CPUFREQ_ADJUST callback function. */ +#define NOTIFY_INVALID NULL +struct cpufreq_cooling_device *notify_device; + +/** + * get_idr - function to get a unique id. + * @idr: struct idr * handle used to create a id. + * @id: int * value generated by this function. + */ +static int get_idr(struct idr *idr, int *id) +{ + int err; +again: + if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0)) + return -ENOMEM; + + mutex_lock(&cooling_cpufreq_lock); + err = idr_get_new(idr, NULL, id); + mutex_unlock(&cooling_cpufreq_lock); + + if (unlikely(err == -EAGAIN)) + goto again; + else if (unlikely(err)) + return err; + + *id = *id & MAX_IDR_MASK; + return 0; +} + +/** + * release_idr - function to free the unique id. + * @idr: struct idr * handle used for creating the id. + * @id: int value representing the unique id. + */ +static void release_idr(struct idr *idr, int id) +{ + mutex_lock(&cooling_cpufreq_lock); + idr_remove(idr, id); + mutex_unlock(&cooling_cpufreq_lock); +} + +/* Below code defines functions to be used for cpufreq as cooling device */ + +/** + * is_cpufreq_valid - function to check if a cpu has frequency transition policy. + * @cpu: cpu for which check is needed. + */ +static int is_cpufreq_valid(int cpu) +{ + struct cpufreq_policy policy; + return !cpufreq_get_policy(&policy, cpu); +} + +/** + * get_cpu_frequency - get the absolute value of frequency from level. + * @cpu: cpu for which frequency is fetched. + * @level: level of frequency of the CPU + * e.g level=1 --> 1st MAX FREQ, LEVEL=2 ---> 2nd MAX FREQ, .... etc + */ +static unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level) +{ + int ret = 0, i = 0; + unsigned long level_index; + bool descend = false; + struct cpufreq_frequency_table *table = + cpufreq_frequency_get_table(cpu); + if (!table) + return ret; + + while (table[i].frequency != CPUFREQ_TABLE_END) { + if (table[i].frequency == CPUFREQ_ENTRY_INVALID) + continue; + + /*check if table in ascending or descending order*/ + if ((table[i + 1].frequency != CPUFREQ_TABLE_END) && + (table[i + 1].frequency < table[i].frequency) + && !descend) { + descend = true; + } + + /*return if level matched and table in descending order*/ + if (descend && i == level) + return table[i].frequency; + i++; + } + i--; + + if (level > i || descend) + return ret; + level_index = i - level; + + /*Scan the table in reverse order and match the level*/ + while (i >= 0) { + if (table[i].frequency == CPUFREQ_ENTRY_INVALID) + continue; + /*return if level matched*/ + if (i == level_index) + return table[i].frequency; + i--; + } + return ret; +} + +/** + * cpufreq_apply_cooling - function to apply frequency clipping. + * @cpufreq_device: cpufreq_cooling_device pointer containing frequency + * clipping data. + * @cooling_state: value of the cooling state. + */ +static int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device, + unsigned long cooling_state) +{ + unsigned int cpuid, clip_freq; + struct cpumask *maskPtr = &cpufreq_device->allowed_cpus; + unsigned int cpu = cpumask_any(maskPtr); + + + /* Check if the old cooling action is same as new cooling action */ + if (cpufreq_device->cpufreq_state == cooling_state) + return 0; + + clip_freq = get_cpu_frequency(cpu, cooling_state); + if (!clip_freq) + return -EINVAL; + + cpufreq_device->cpufreq_state = cooling_state; + cpufreq_device->cpufreq_val = clip_freq; + notify_device = cpufreq_device; + + for_each_cpu(cpuid, maskPtr) { + if (is_cpufreq_valid(cpuid)) + cpufreq_update_policy(cpuid); + } + + notify_device = NOTIFY_INVALID; + + return 0; +} + +/** + * cpufreq_thermal_notifier - notifier callback for cpufreq policy change. + * @nb: struct notifier_block * with callback info. + * @event: value showing cpufreq event for which this function invoked. + * @data: callback-specific data + */ +static int cpufreq_thermal_notifier(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct cpufreq_policy *policy = data; + unsigned long max_freq = 0; + + if (event != CPUFREQ_ADJUST || notify_device == NOTIFY_INVALID) + return 0; + + if (cpumask_test_cpu(policy->cpu, ¬ify_device->allowed_cpus)) + max_freq = notify_device->cpufreq_val; + + /* Never exceed user_policy.max*/ + if (max_freq > policy->user_policy.max) + max_freq = policy->user_policy.max; + + if (policy->max != max_freq) + cpufreq_verify_within_limits(policy, 0, max_freq); + + return 0; +} + +/* + * cpufreq cooling device callback functions are defined below + */ + +/** + * cpufreq_get_max_state - callback function to get the max cooling state. + * @cdev: thermal cooling device pointer. + * @state: fill this variable with the max cooling state. + */ +static int cpufreq_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + int ret = -EINVAL, i = 0; + struct cpufreq_cooling_device *cpufreq_device; + struct cpumask *maskPtr; + unsigned int cpu; + struct cpufreq_frequency_table *table; + + mutex_lock(&cooling_cpufreq_lock); + list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) { + if (cpufreq_device && cpufreq_device->cool_dev == cdev) + break; + } + if (cpufreq_device == NULL) + goto return_get_max_state; + + maskPtr = &cpufreq_device->allowed_cpus; + cpu = cpumask_any(maskPtr); + table = cpufreq_frequency_get_table(cpu); + if (!table) { + *state = 0; + ret = 0; + goto return_get_max_state; + } + + while (table[i].frequency != CPUFREQ_TABLE_END) { + if (table[i].frequency == CPUFREQ_ENTRY_INVALID) + continue; + i++; + } + if (i > 0) { + *state = --i; + ret = 0; + } + +return_get_max_state: + mutex_unlock(&cooling_cpufreq_lock); + return ret; +} + +/** + * cpufreq_get_cur_state - callback function to get the current cooling state. + * @cdev: thermal cooling device pointer. + * @state: fill this variable with the current cooling state. + */ +static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + int ret = -EINVAL; + struct cpufreq_cooling_device *cpufreq_device; + + mutex_lock(&cooling_cpufreq_lock); + list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) { + if (cpufreq_device && cpufreq_device->cool_dev == cdev) { + *state = cpufreq_device->cpufreq_state; + ret = 0; + break; + } + } + mutex_unlock(&cooling_cpufreq_lock); + + return ret; +} + +/** + * cpufreq_set_cur_state - callback function to set the current cooling state. + * @cdev: thermal cooling device pointer. + * @state: set this variable to the current cooling state. + */ +static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + int ret = -EINVAL; + struct cpufreq_cooling_device *cpufreq_device; + + mutex_lock(&cooling_cpufreq_lock); + list_for_each_entry(cpufreq_device, &cooling_cpufreq_list, node) { + if (cpufreq_device && cpufreq_device->cool_dev == cdev) { + ret = 0; + break; + } + } + if (!ret) + ret = cpufreq_apply_cooling(cpufreq_device, state); + + mutex_unlock(&cooling_cpufreq_lock); + + return ret; +} + +/* Bind cpufreq callbacks to thermal cooling device ops */ +static struct thermal_cooling_device_ops const cpufreq_cooling_ops = { + .get_max_state = cpufreq_get_max_state, + .get_cur_state = cpufreq_get_cur_state, + .set_cur_state = cpufreq_set_cur_state, +}; + +/* Notifier for cpufreq policy change */ +static struct notifier_block thermal_cpufreq_notifier_block = { + .notifier_call = cpufreq_thermal_notifier, +}; + +/** + * cpufreq_cooling_register - function to create cpufreq cooling device. + * @clip_cpus: cpumask of cpus where the frequency constraints will happen. + */ +struct thermal_cooling_device *cpufreq_cooling_register( + struct cpumask *clip_cpus) +{ + struct thermal_cooling_device *cool_dev; + struct cpufreq_cooling_device *cpufreq_dev = NULL; + unsigned int cpufreq_dev_count = 0, min = 0, max = 0; + char dev_name[THERMAL_NAME_LENGTH]; + int ret = 0, i; + struct cpufreq_policy policy; + + list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node) + cpufreq_dev_count++; + + /*Verify that all the clip cpus have same freq_min, freq_max limit*/ + for_each_cpu(i, clip_cpus) { + /*continue if cpufreq policy not found and not return error*/ + if (!cpufreq_get_policy(&policy, i)) + continue; + if (min == 0 && max == 0) { + min = policy.cpuinfo.min_freq; + max = policy.cpuinfo.max_freq; + } else { + if (min != policy.cpuinfo.min_freq || + max != policy.cpuinfo.max_freq) + return ERR_PTR(-EINVAL); +} + } + cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device), + GFP_KERNEL); + if (!cpufreq_dev) + return ERR_PTR(-ENOMEM); + + cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus); + + if (cpufreq_dev_count == 0) + mutex_init(&cooling_cpufreq_lock); + + ret = get_idr(&cpufreq_idr, &cpufreq_dev->id); + if (ret) { + kfree(cpufreq_dev); + return ERR_PTR(-EINVAL); + } + + sprintf(dev_name, "thermal-cpufreq-%d", cpufreq_dev->id); + + cool_dev = thermal_cooling_device_register(dev_name, cpufreq_dev, + &cpufreq_cooling_ops); + if (!cool_dev) { + release_idr(&cpufreq_idr, cpufreq_dev->id); + kfree(cpufreq_dev); + return ERR_PTR(-EINVAL); + } + cpufreq_dev->cool_dev = cool_dev; + cpufreq_dev->cpufreq_state = 0; + mutex_lock(&cooling_cpufreq_lock); + list_add_tail(&cpufreq_dev->node, &cooling_cpufreq_list); + + /* Register the notifier for first cpufreq cooling device */ + if (cpufreq_dev_count == 0) + cpufreq_register_notifier(&thermal_cpufreq_notifier_block, + CPUFREQ_POLICY_NOTIFIER); + + mutex_unlock(&cooling_cpufreq_lock); + return cool_dev; +} +EXPORT_SYMBOL(cpufreq_cooling_register); + +/** + * cpufreq_cooling_unregister - function to remove cpufreq cooling device. + * @cdev: thermal cooling device pointer. + */ +void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) +{ + struct cpufreq_cooling_device *cpufreq_dev = NULL; + unsigned int cpufreq_dev_count = 0; + + mutex_lock(&cooling_cpufreq_lock); + list_for_each_entry(cpufreq_dev, &cooling_cpufreq_list, node) { + if (cpufreq_dev && cpufreq_dev->cool_dev == cdev) + break; + cpufreq_dev_count++; + } + + if (!cpufreq_dev || cpufreq_dev->cool_dev != cdev) { + mutex_unlock(&cooling_cpufreq_lock); + return; + } + + list_del(&cpufreq_dev->node); + + /* Unregister the notifier for the last cpufreq cooling device */ + if (cpufreq_dev_count == 1) { + cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, + CPUFREQ_POLICY_NOTIFIER); + } + mutex_unlock(&cooling_cpufreq_lock); + thermal_cooling_device_unregister(cpufreq_dev->cool_dev); + release_idr(&cpufreq_idr, cpufreq_dev->id); + if (cpufreq_dev_count == 1) + mutex_destroy(&cooling_cpufreq_lock); + kfree(cpufreq_dev); +} +EXPORT_SYMBOL(cpufreq_cooling_unregister); diff --git a/drivers/thermal/exynos_thermal.c b/drivers/thermal/exynos_thermal.c new file mode 100644 index 00000000000..fd03e8581af --- /dev/null +++ b/drivers/thermal/exynos_thermal.c @@ -0,0 +1,997 @@ +/* + * exynos_thermal.c - Samsung EXYNOS TMU (Thermal Management Unit) + * + * Copyright (C) 2011 Samsung Electronics + * Donggeun Kim <dg77.kim@samsung.com> + * Amit Daniel Kachhap <amit.kachhap@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/workqueue.h> +#include <linux/sysfs.h> +#include <linux/kobject.h> +#include <linux/io.h> +#include <linux/mutex.h> +#include <linux/platform_data/exynos_thermal.h> +#include <linux/thermal.h> +#include <linux/cpufreq.h> +#include <linux/cpu_cooling.h> +#include <linux/of.h> + +#include <plat/cpu.h> + +/* Exynos generic registers */ +#define EXYNOS_TMU_REG_TRIMINFO 0x0 +#define EXYNOS_TMU_REG_CONTROL 0x20 +#define EXYNOS_TMU_REG_STATUS 0x28 +#define EXYNOS_TMU_REG_CURRENT_TEMP 0x40 +#define EXYNOS_TMU_REG_INTEN 0x70 +#define EXYNOS_TMU_REG_INTSTAT 0x74 +#define EXYNOS_TMU_REG_INTCLEAR 0x78 + +#define EXYNOS_TMU_TRIM_TEMP_MASK 0xff +#define EXYNOS_TMU_GAIN_SHIFT 8 +#define EXYNOS_TMU_REF_VOLTAGE_SHIFT 24 +#define EXYNOS_TMU_CORE_ON 3 +#define EXYNOS_TMU_CORE_OFF 2 +#define EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET 50 + +/* Exynos4210 specific registers */ +#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP 0x44 +#define EXYNOS4210_TMU_REG_TRIG_LEVEL0 0x50 +#define EXYNOS4210_TMU_REG_TRIG_LEVEL1 0x54 +#define EXYNOS4210_TMU_REG_TRIG_LEVEL2 0x58 +#define EXYNOS4210_TMU_REG_TRIG_LEVEL3 0x5C +#define EXYNOS4210_TMU_REG_PAST_TEMP0 0x60 +#define EXYNOS4210_TMU_REG_PAST_TEMP1 0x64 +#define EXYNOS4210_TMU_REG_PAST_TEMP2 0x68 +#define EXYNOS4210_TMU_REG_PAST_TEMP3 0x6C + +#define EXYNOS4210_TMU_TRIG_LEVEL0_MASK 0x1 +#define EXYNOS4210_TMU_TRIG_LEVEL1_MASK 0x10 +#define EXYNOS4210_TMU_TRIG_LEVEL2_MASK 0x100 +#define EXYNOS4210_TMU_TRIG_LEVEL3_MASK 0x1000 +#define EXYNOS4210_TMU_INTCLEAR_VAL 0x1111 + +/* Exynos5250 and Exynos4412 specific registers */ +#define EXYNOS_TMU_TRIMINFO_CON 0x14 +#define EXYNOS_THD_TEMP_RISE 0x50 +#define EXYNOS_THD_TEMP_FALL 0x54 +#define EXYNOS_EMUL_CON 0x80 + +#define EXYNOS_TRIMINFO_RELOAD 0x1 +#define EXYNOS_TMU_CLEAR_RISE_INT 0x111 +#define EXYNOS_TMU_CLEAR_FALL_INT (0x111 << 16) +#define EXYNOS_MUX_ADDR_VALUE 6 +#define EXYNOS_MUX_ADDR_SHIFT 20 +#define EXYNOS_TMU_TRIP_MODE_SHIFT 13 + +#define EFUSE_MIN_VALUE 40 +#define EFUSE_MAX_VALUE 100 + +/* In-kernel thermal framework related macros & definations */ +#define SENSOR_NAME_LEN 16 +#define MAX_TRIP_COUNT 8 +#define MAX_COOLING_DEVICE 4 + +#define ACTIVE_INTERVAL 500 +#define IDLE_INTERVAL 10000 +#define MCELSIUS 1000 + +/* CPU Zone information */ +#define PANIC_ZONE 4 +#define WARN_ZONE 3 +#define MONITOR_ZONE 2 +#define SAFE_ZONE 1 + +#define GET_ZONE(trip) (trip + 2) +#define GET_TRIP(zone) (zone - 2) + +#define EXYNOS_ZONE_COUNT 3 + +struct exynos_tmu_data { + struct exynos_tmu_platform_data *pdata; + struct resource *mem; + void __iomem *base; + int irq; + enum soc_type soc; + struct work_struct irq_work; + struct mutex lock; + struct clk *clk; + u8 temp_error1, temp_error2; +}; + +struct thermal_trip_point_conf { + int trip_val[MAX_TRIP_COUNT]; + int trip_count; +}; + +struct thermal_cooling_conf { + struct freq_clip_table freq_data[MAX_TRIP_COUNT]; + int freq_clip_count; +}; + +struct thermal_sensor_conf { + char name[SENSOR_NAME_LEN]; + int (*read_temperature)(void *data); + struct thermal_trip_point_conf trip_data; + struct thermal_cooling_conf cooling_data; + void *private_data; +}; + +struct exynos_thermal_zone { + enum thermal_device_mode mode; + struct thermal_zone_device *therm_dev; + struct thermal_cooling_device *cool_dev[MAX_COOLING_DEVICE]; + unsigned int cool_dev_size; + struct platform_device *exynos4_dev; + struct thermal_sensor_conf *sensor_conf; + bool bind; +}; + +static struct exynos_thermal_zone *th_zone; +static void exynos_unregister_thermal(void); +static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf); + +/* Get mode callback functions for thermal zone */ +static int exynos_get_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode *mode) +{ + if (th_zone) + *mode = th_zone->mode; + return 0; +} + +/* Set mode callback functions for thermal zone */ +static int exynos_set_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode mode) +{ + if (!th_zone->therm_dev) { + pr_notice("thermal zone not registered\n"); + return 0; + } + + mutex_lock(&th_zone->therm_dev->lock); + + if (mode == THERMAL_DEVICE_ENABLED) + th_zone->therm_dev->polling_delay = IDLE_INTERVAL; + else + th_zone->therm_dev->polling_delay = 0; + + mutex_unlock(&th_zone->therm_dev->lock); + + th_zone->mode = mode; + thermal_zone_device_update(th_zone->therm_dev); + pr_info("thermal polling set for duration=%d msec\n", + th_zone->therm_dev->polling_delay); + return 0; +} + + +/* Get trip type callback functions for thermal zone */ +static int exynos_get_trip_type(struct thermal_zone_device *thermal, int trip, + enum thermal_trip_type *type) +{ + switch (GET_ZONE(trip)) { + case MONITOR_ZONE: + case WARN_ZONE: + *type = THERMAL_TRIP_ACTIVE; + break; + case PANIC_ZONE: + *type = THERMAL_TRIP_CRITICAL; + break; + default: + return -EINVAL; + } + return 0; +} + +/* Get trip temperature callback functions for thermal zone */ +static int exynos_get_trip_temp(struct thermal_zone_device *thermal, int trip, + unsigned long *temp) +{ + if (trip < GET_TRIP(MONITOR_ZONE) || trip > GET_TRIP(PANIC_ZONE)) + return -EINVAL; + + *temp = th_zone->sensor_conf->trip_data.trip_val[trip]; + /* convert the temperature into millicelsius */ + *temp = *temp * MCELSIUS; + + return 0; +} + +/* Get critical temperature callback functions for thermal zone */ +static int exynos_get_crit_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + int ret; + /* Panic zone */ + ret = exynos_get_trip_temp(thermal, GET_TRIP(PANIC_ZONE), temp); + return ret; +} + +static int exynos_get_frequency_level(unsigned int cpu, unsigned int freq) +{ + int i = 0, ret = -EINVAL; + struct cpufreq_frequency_table *table = NULL; +#ifdef CONFIG_CPU_FREQ + table = cpufreq_frequency_get_table(cpu); +#endif + if (!table) + return ret; + + while (table[i].frequency != CPUFREQ_TABLE_END) { + if (table[i].frequency == CPUFREQ_ENTRY_INVALID) + continue; + if (table[i].frequency == freq) + return i; + i++; + } + return ret; +} + +/* Bind callback functions for thermal zone */ +static int exynos_bind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + int ret = 0, i, tab_size, level; + struct freq_clip_table *tab_ptr, *clip_data; + struct thermal_sensor_conf *data = th_zone->sensor_conf; + + tab_ptr = (struct freq_clip_table *)data->cooling_data.freq_data; + tab_size = data->cooling_data.freq_clip_count; + + if (tab_ptr == NULL || tab_size == 0) + return -EINVAL; + + /* find the cooling device registered*/ + for (i = 0; i < th_zone->cool_dev_size; i++) + if (cdev == th_zone->cool_dev[i]) + break; + + /* No matching cooling device */ + if (i == th_zone->cool_dev_size) + return 0; + + /* Bind the thermal zone to the cpufreq cooling device */ + for (i = 0; i < tab_size; i++) { + clip_data = (struct freq_clip_table *)&(tab_ptr[i]); + level = exynos_get_frequency_level(0, clip_data->freq_clip_max); + if (level < 0) + return 0; + switch (GET_ZONE(i)) { + case MONITOR_ZONE: + case WARN_ZONE: + if (thermal_zone_bind_cooling_device(thermal, i, cdev, + level, level)) { + pr_err("error binding cdev inst %d\n", i); + ret = -EINVAL; + } + th_zone->bind = true; + break; + default: + ret = -EINVAL; + } + } + + return ret; +} + +/* Unbind callback functions for thermal zone */ +static int exynos_unbind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + int ret = 0, i, tab_size; + struct thermal_sensor_conf *data = th_zone->sensor_conf; + + if (th_zone->bind == false) + return 0; + + tab_size = data->cooling_data.freq_clip_count; + + if (tab_size == 0) + return -EINVAL; + + /* find the cooling device registered*/ + for (i = 0; i < th_zone->cool_dev_size; i++) + if (cdev == th_zone->cool_dev[i]) + break; + + /* No matching cooling device */ + if (i == th_zone->cool_dev_size) + return 0; + + /* Bind the thermal zone to the cpufreq cooling device */ + for (i = 0; i < tab_size; i++) { + switch (GET_ZONE(i)) { + case MONITOR_ZONE: + case WARN_ZONE: + if (thermal_zone_unbind_cooling_device(thermal, i, + cdev)) { + pr_err("error unbinding cdev inst=%d\n", i); + ret = -EINVAL; + } + th_zone->bind = false; + break; + default: + ret = -EINVAL; + } + } + return ret; +} + +/* Get temperature callback functions for thermal zone */ +static int exynos_get_temp(struct thermal_zone_device *thermal, + unsigned long *temp) +{ + void *data; + + if (!th_zone->sensor_conf) { + pr_info("Temperature sensor not initialised\n"); + return -EINVAL; + } + data = th_zone->sensor_conf->private_data; + *temp = th_zone->sensor_conf->read_temperature(data); + /* convert the temperature into millicelsius */ + *temp = *temp * MCELSIUS; + return 0; +} + +/* Get the temperature trend */ +static int exynos_get_trend(struct thermal_zone_device *thermal, + int trip, enum thermal_trend *trend) +{ + if (thermal->temperature >= trip) + *trend = THERMAL_TREND_RAISING; + else + *trend = THERMAL_TREND_DROPPING; + + return 0; +} +/* Operation callback functions for thermal zone */ +static struct thermal_zone_device_ops const exynos_dev_ops = { + .bind = exynos_bind, + .unbind = exynos_unbind, + .get_temp = exynos_get_temp, + .get_trend = exynos_get_trend, + .get_mode = exynos_get_mode, + .set_mode = exynos_set_mode, + .get_trip_type = exynos_get_trip_type, + .get_trip_temp = exynos_get_trip_temp, + .get_crit_temp = exynos_get_crit_temp, +}; + +/* + * This function may be called from interrupt based temperature sensor + * when threshold is changed. + */ +static void exynos_report_trigger(void) +{ + unsigned int i; + char data[10]; + char *envp[] = { data, NULL }; + + if (!th_zone || !th_zone->therm_dev) + return; + if (th_zone->bind == false) { + for (i = 0; i < th_zone->cool_dev_size; i++) { + if (!th_zone->cool_dev[i]) + continue; + exynos_bind(th_zone->therm_dev, + th_zone->cool_dev[i]); + } + } + + thermal_zone_device_update(th_zone->therm_dev); + + mutex_lock(&th_zone->therm_dev->lock); + /* Find the level for which trip happened */ + for (i = 0; i < th_zone->sensor_conf->trip_data.trip_count; i++) { + if (th_zone->therm_dev->last_temperature < + th_zone->sensor_conf->trip_data.trip_val[i] * MCELSIUS) + break; + } + + if (th_zone->mode == THERMAL_DEVICE_ENABLED) { + if (i > 0) + th_zone->therm_dev->polling_delay = ACTIVE_INTERVAL; + else + th_zone->therm_dev->polling_delay = IDLE_INTERVAL; + } + + snprintf(data, sizeof(data), "%u", i); + kobject_uevent_env(&th_zone->therm_dev->device.kobj, KOBJ_CHANGE, envp); + mutex_unlock(&th_zone->therm_dev->lock); +} + +/* Register with the in-kernel thermal management */ +static int exynos_register_thermal(struct thermal_sensor_conf *sensor_conf) +{ + int ret; + struct cpumask mask_val; + + if (!sensor_conf || !sensor_conf->read_temperature) { + pr_err("Temperature sensor not initialised\n"); + return -EINVAL; + } + + th_zone = kzalloc(sizeof(struct exynos_thermal_zone), GFP_KERNEL); + if (!th_zone) + return -ENOMEM; + + th_zone->sensor_conf = sensor_conf; + cpumask_set_cpu(0, &mask_val); + th_zone->cool_dev[0] = cpufreq_cooling_register(&mask_val); + if (IS_ERR(th_zone->cool_dev[0])) { + pr_err("Failed to register cpufreq cooling device\n"); + ret = -EINVAL; + goto err_unregister; + } + th_zone->cool_dev_size++; + + th_zone->therm_dev = thermal_zone_device_register(sensor_conf->name, + EXYNOS_ZONE_COUNT, 0, NULL, &exynos_dev_ops, 0, + IDLE_INTERVAL); + + if (IS_ERR(th_zone->therm_dev)) { + pr_err("Failed to register thermal zone device\n"); + ret = -EINVAL; + goto err_unregister; + } + th_zone->mode = THERMAL_DEVICE_ENABLED; + + pr_info("Exynos: Kernel Thermal management registered\n"); + + return 0; + +err_unregister: + exynos_unregister_thermal(); + return ret; +} + +/* Un-Register with the in-kernel thermal management */ +static void exynos_unregister_thermal(void) +{ + int i; + + if (!th_zone) + return; + + if (th_zone->therm_dev) + thermal_zone_device_unregister(th_zone->therm_dev); + + for (i = 0; i < th_zone->cool_dev_size; i++) { + if (th_zone->cool_dev[i]) + cpufreq_cooling_unregister(th_zone->cool_dev[i]); + } + + kfree(th_zone); + pr_info("Exynos: Kernel Thermal management unregistered\n"); +} + +/* + * TMU treats temperature as a mapped temperature code. + * The temperature is converted differently depending on the calibration type. + */ +static int temp_to_code(struct exynos_tmu_data *data, u8 temp) +{ + struct exynos_tmu_platform_data *pdata = data->pdata; + int temp_code; + + if (data->soc == SOC_ARCH_EXYNOS4210) + /* temp should range between 25 and 125 */ + if (temp < 25 || temp > 125) { + temp_code = -EINVAL; + goto out; + } + + switch (pdata->cal_type) { + case TYPE_TWO_POINT_TRIMMING: + temp_code = (temp - 25) * + (data->temp_error2 - data->temp_error1) / + (85 - 25) + data->temp_error1; + break; + case TYPE_ONE_POINT_TRIMMING: + temp_code = temp + data->temp_error1 - 25; + break; + default: + temp_code = temp + EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET; + break; + } +out: + return temp_code; +} + +/* + * Calculate a temperature value from a temperature code. + * The unit of the temperature is degree Celsius. + */ +static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code) +{ + struct exynos_tmu_platform_data *pdata = data->pdata; + int temp; + + if (data->soc == SOC_ARCH_EXYNOS4210) + /* temp_code should range between 75 and 175 */ + if (temp_code < 75 || temp_code > 175) { + temp = -ENODATA; + goto out; + } + + switch (pdata->cal_type) { + case TYPE_TWO_POINT_TRIMMING: + temp = (temp_code - data->temp_error1) * (85 - 25) / + (data->temp_error2 - data->temp_error1) + 25; + break; + case TYPE_ONE_POINT_TRIMMING: + temp = temp_code - data->temp_error1 + 25; + break; + default: + temp = temp_code - EXYNOS_TMU_DEF_CODE_TO_TEMP_OFFSET; + break; + } +out: + return temp; +} + +static int exynos_tmu_initialize(struct platform_device *pdev) +{ + struct exynos_tmu_data *data = platform_get_drvdata(pdev); + struct exynos_tmu_platform_data *pdata = data->pdata; + unsigned int status, trim_info, rising_threshold; + int ret = 0, threshold_code; + + mutex_lock(&data->lock); + clk_enable(data->clk); + + status = readb(data->base + EXYNOS_TMU_REG_STATUS); + if (!status) { + ret = -EBUSY; + goto out; + } + + if (data->soc == SOC_ARCH_EXYNOS) { + __raw_writel(EXYNOS_TRIMINFO_RELOAD, + data->base + EXYNOS_TMU_TRIMINFO_CON); + } + /* Save trimming info in order to perform calibration */ + trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO); + data->temp_error1 = trim_info & EXYNOS_TMU_TRIM_TEMP_MASK; + data->temp_error2 = ((trim_info >> 8) & EXYNOS_TMU_TRIM_TEMP_MASK); + + if ((EFUSE_MIN_VALUE > data->temp_error1) || + (data->temp_error1 > EFUSE_MAX_VALUE) || + (data->temp_error2 != 0)) + data->temp_error1 = pdata->efuse_value; + + if (data->soc == SOC_ARCH_EXYNOS4210) { + /* Write temperature code for threshold */ + threshold_code = temp_to_code(data, pdata->threshold); + if (threshold_code < 0) { + ret = threshold_code; + goto out; + } + writeb(threshold_code, + data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP); + + writeb(pdata->trigger_levels[0], + data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL0); + writeb(pdata->trigger_levels[1], + data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL1); + writeb(pdata->trigger_levels[2], + data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL2); + writeb(pdata->trigger_levels[3], + data->base + EXYNOS4210_TMU_REG_TRIG_LEVEL3); + + writel(EXYNOS4210_TMU_INTCLEAR_VAL, + data->base + EXYNOS_TMU_REG_INTCLEAR); + } else if (data->soc == SOC_ARCH_EXYNOS) { + /* Write temperature code for threshold */ + threshold_code = temp_to_code(data, pdata->trigger_levels[0]); + if (threshold_code < 0) { + ret = threshold_code; + goto out; + } + rising_threshold = threshold_code; + threshold_code = temp_to_code(data, pdata->trigger_levels[1]); + if (threshold_code < 0) { + ret = threshold_code; + goto out; + } + rising_threshold |= (threshold_code << 8); + threshold_code = temp_to_code(data, pdata->trigger_levels[2]); + if (threshold_code < 0) { + ret = threshold_code; + goto out; + } + rising_threshold |= (threshold_code << 16); + + writel(rising_threshold, + data->base + EXYNOS_THD_TEMP_RISE); + writel(0, data->base + EXYNOS_THD_TEMP_FALL); + + writel(EXYNOS_TMU_CLEAR_RISE_INT|EXYNOS_TMU_CLEAR_FALL_INT, + data->base + EXYNOS_TMU_REG_INTCLEAR); + } +out: + clk_disable(data->clk); + mutex_unlock(&data->lock); + + return ret; +} + +static void exynos_tmu_control(struct platform_device *pdev, bool on) +{ + struct exynos_tmu_data *data = platform_get_drvdata(pdev); + struct exynos_tmu_platform_data *pdata = data->pdata; + unsigned int con, interrupt_en; + + mutex_lock(&data->lock); + clk_enable(data->clk); + + con = pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT | + pdata->gain << EXYNOS_TMU_GAIN_SHIFT; + + if (data->soc == SOC_ARCH_EXYNOS) { + con |= pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT; + con |= (EXYNOS_MUX_ADDR_VALUE << EXYNOS_MUX_ADDR_SHIFT); + } + + if (on) { + con |= EXYNOS_TMU_CORE_ON; + interrupt_en = pdata->trigger_level3_en << 12 | + pdata->trigger_level2_en << 8 | + pdata->trigger_level1_en << 4 | + pdata->trigger_level0_en; + } else { + con |= EXYNOS_TMU_CORE_OFF; + interrupt_en = 0; /* Disable all interrupts */ + } + writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN); + writel(con, data->base + EXYNOS_TMU_REG_CONTROL); + + clk_disable(data->clk); + mutex_unlock(&data->lock); +} + +static int exynos_tmu_read(struct exynos_tmu_data *data) +{ + u8 temp_code; + int temp; + + mutex_lock(&data->lock); + clk_enable(data->clk); + + temp_code = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP); + temp = code_to_temp(data, temp_code); + + clk_disable(data->clk); + mutex_unlock(&data->lock); + + return temp; +} + +static void exynos_tmu_work(struct work_struct *work) +{ + struct exynos_tmu_data *data = container_of(work, + struct exynos_tmu_data, irq_work); + + mutex_lock(&data->lock); + clk_enable(data->clk); + + + if (data->soc == SOC_ARCH_EXYNOS) + writel(EXYNOS_TMU_CLEAR_RISE_INT, + data->base + EXYNOS_TMU_REG_INTCLEAR); + else + writel(EXYNOS4210_TMU_INTCLEAR_VAL, + data->base + EXYNOS_TMU_REG_INTCLEAR); + + clk_disable(data->clk); + mutex_unlock(&data->lock); + exynos_report_trigger(); + enable_irq(data->irq); +} + +static irqreturn_t exynos_tmu_irq(int irq, void *id) +{ + struct exynos_tmu_data *data = id; + + disable_irq_nosync(irq); + schedule_work(&data->irq_work); + + return IRQ_HANDLED; +} +static struct thermal_sensor_conf exynos_sensor_conf = { + .name = "exynos-therm", + .read_temperature = (int (*)(void *))exynos_tmu_read, +}; + +#if defined(CONFIG_CPU_EXYNOS4210) +static struct exynos_tmu_platform_data const exynos4210_default_tmu_data = { + .threshold = 80, + .trigger_levels[0] = 5, + .trigger_levels[1] = 20, + .trigger_levels[2] = 30, + .trigger_level0_en = 1, + .trigger_level1_en = 1, + .trigger_level2_en = 1, + .trigger_level3_en = 0, + .gain = 15, + .reference_voltage = 7, + .cal_type = TYPE_ONE_POINT_TRIMMING, + .freq_tab[0] = { + .freq_clip_max = 800 * 1000, + .temp_level = 85, + }, + .freq_tab[1] = { + .freq_clip_max = 200 * 1000, + .temp_level = 100, + }, + .freq_tab_count = 2, + .type = SOC_ARCH_EXYNOS4210, +}; +#define EXYNOS4210_TMU_DRV_DATA (&exynos4210_default_tmu_data) +#else +#define EXYNOS4210_TMU_DRV_DATA (NULL) +#endif + +#if defined(CONFIG_SOC_EXYNOS5250) || defined(CONFIG_SOC_EXYNOS4412) +static struct exynos_tmu_platform_data const exynos_default_tmu_data = { + .trigger_levels[0] = 85, + .trigger_levels[1] = 103, + .trigger_levels[2] = 110, + .trigger_level0_en = 1, + .trigger_level1_en = 1, + .trigger_level2_en = 1, + .trigger_level3_en = 0, + .gain = 8, + .reference_voltage = 16, + .noise_cancel_mode = 4, + .cal_type = TYPE_ONE_POINT_TRIMMING, + .efuse_value = 55, + .freq_tab[0] = { + .freq_clip_max = 800 * 1000, + .temp_level = 85, + }, + .freq_tab[1] = { + .freq_clip_max = 200 * 1000, + .temp_level = 103, + }, + .freq_tab_count = 2, + .type = SOC_ARCH_EXYNOS, +}; +#define EXYNOS_TMU_DRV_DATA (&exynos_default_tmu_data) +#else +#define EXYNOS_TMU_DRV_DATA (NULL) +#endif + +#ifdef CONFIG_OF +static const struct of_device_id exynos_tmu_match[] = { + { + .compatible = "samsung,exynos4210-tmu", + .data = (void *)EXYNOS4210_TMU_DRV_DATA, + }, + { + .compatible = "samsung,exynos5250-tmu", + .data = (void *)EXYNOS_TMU_DRV_DATA, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_tmu_match); +#else +#define exynos_tmu_match NULL +#endif + +static struct platform_device_id exynos_tmu_driver_ids[] = { + { + .name = "exynos4210-tmu", + .driver_data = (kernel_ulong_t)EXYNOS4210_TMU_DRV_DATA, + }, + { + .name = "exynos5250-tmu", + .driver_data = (kernel_ulong_t)EXYNOS_TMU_DRV_DATA, + }, + { }, +}; +MODULE_DEVICE_TABLE(platform, exynos4_tmu_driver_ids); + +static inline struct exynos_tmu_platform_data *exynos_get_driver_data( + struct platform_device *pdev) +{ +#ifdef CONFIG_OF + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(exynos_tmu_match, pdev->dev.of_node); + if (!match) + return NULL; + return (struct exynos_tmu_platform_data *) match->data; + } +#endif + return (struct exynos_tmu_platform_data *) + platform_get_device_id(pdev)->driver_data; +} +static int __devinit exynos_tmu_probe(struct platform_device *pdev) +{ + struct exynos_tmu_data *data; + struct exynos_tmu_platform_data *pdata = pdev->dev.platform_data; + int ret, i; + + if (!pdata) + pdata = exynos_get_driver_data(pdev); + + if (!pdata) { + dev_err(&pdev->dev, "No platform init data supplied.\n"); + return -ENODEV; + } + data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data), + GFP_KERNEL); + if (!data) { + dev_err(&pdev->dev, "Failed to allocate driver structure\n"); + return -ENOMEM; + } + + data->irq = platform_get_irq(pdev, 0); + if (data->irq < 0) { + dev_err(&pdev->dev, "Failed to get platform irq\n"); + return data->irq; + } + + INIT_WORK(&data->irq_work, exynos_tmu_work); + + data->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!data->mem) { + dev_err(&pdev->dev, "Failed to get platform resource\n"); + return -ENOENT; + } + + data->base = devm_request_and_ioremap(&pdev->dev, data->mem); + if (!data->base) { + dev_err(&pdev->dev, "Failed to ioremap memory\n"); + return -ENODEV; + } + + ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq, + IRQF_TRIGGER_RISING, "exynos-tmu", data); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq); + return ret; + } + + data->clk = clk_get(NULL, "tmu_apbif"); + if (IS_ERR(data->clk)) { + dev_err(&pdev->dev, "Failed to get clock\n"); + return PTR_ERR(data->clk); + } + + if (pdata->type == SOC_ARCH_EXYNOS || + pdata->type == SOC_ARCH_EXYNOS4210) + data->soc = pdata->type; + else { + ret = -EINVAL; + dev_err(&pdev->dev, "Platform not supported\n"); + goto err_clk; + } + + data->pdata = pdata; + platform_set_drvdata(pdev, data); + mutex_init(&data->lock); + + ret = exynos_tmu_initialize(pdev); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize TMU\n"); + goto err_clk; + } + + exynos_tmu_control(pdev, true); + + /* Register the sensor with thermal management interface */ + (&exynos_sensor_conf)->private_data = data; + exynos_sensor_conf.trip_data.trip_count = pdata->trigger_level0_en + + pdata->trigger_level1_en + pdata->trigger_level2_en + + pdata->trigger_level3_en; + + for (i = 0; i < exynos_sensor_conf.trip_data.trip_count; i++) + exynos_sensor_conf.trip_data.trip_val[i] = + pdata->threshold + pdata->trigger_levels[i]; + + exynos_sensor_conf.cooling_data.freq_clip_count = + pdata->freq_tab_count; + for (i = 0; i < pdata->freq_tab_count; i++) { + exynos_sensor_conf.cooling_data.freq_data[i].freq_clip_max = + pdata->freq_tab[i].freq_clip_max; + exynos_sensor_conf.cooling_data.freq_data[i].temp_level = + pdata->freq_tab[i].temp_level; + } + + ret = exynos_register_thermal(&exynos_sensor_conf); + if (ret) { + dev_err(&pdev->dev, "Failed to register thermal interface\n"); + goto err_clk; + } + return 0; +err_clk: + platform_set_drvdata(pdev, NULL); + clk_put(data->clk); + return ret; +} + +static int __devexit exynos_tmu_remove(struct platform_device *pdev) +{ + struct exynos_tmu_data *data = platform_get_drvdata(pdev); + + exynos_tmu_control(pdev, false); + + exynos_unregister_thermal(); + + clk_put(data->clk); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int exynos_tmu_suspend(struct device *dev) +{ + exynos_tmu_control(to_platform_device(dev), false); + + return 0; +} + +static int exynos_tmu_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + exynos_tmu_initialize(pdev); + exynos_tmu_control(pdev, true); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(exynos_tmu_pm, + exynos_tmu_suspend, exynos_tmu_resume); +#define EXYNOS_TMU_PM (&exynos_tmu_pm) +#else +#define EXYNOS_TMU_PM NULL +#endif + +static struct platform_driver exynos_tmu_driver = { + .driver = { + .name = "exynos-tmu", + .owner = THIS_MODULE, + .pm = EXYNOS_TMU_PM, + .of_match_table = exynos_tmu_match, + }, + .probe = exynos_tmu_probe, + .remove = __devexit_p(exynos_tmu_remove), + .id_table = exynos_tmu_driver_ids, +}; + +module_platform_driver(exynos_tmu_driver); + +MODULE_DESCRIPTION("EXYNOS TMU Driver"); +MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:exynos-tmu"); diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c new file mode 100644 index 00000000000..d4452716aaa --- /dev/null +++ b/drivers/thermal/rcar_thermal.c @@ -0,0 +1,260 @@ +/* + * R-Car THS/TSC thermal sensor driver + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/thermal.h> + +#define THSCR 0x2c +#define THSSR 0x30 + +/* THSCR */ +#define CPTAP 0xf + +/* THSSR */ +#define CTEMP 0x3f + + +struct rcar_thermal_priv { + void __iomem *base; + struct device *dev; + spinlock_t lock; + u32 comp; +}; + +/* + * basic functions + */ +static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) +{ + unsigned long flags; + u32 ret; + + spin_lock_irqsave(&priv->lock, flags); + + ret = ioread32(priv->base + reg); + + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +#if 0 /* no user at this point */ +static void rcar_thermal_write(struct rcar_thermal_priv *priv, + u32 reg, u32 data) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + iowrite32(data, priv->base + reg); + + spin_unlock_irqrestore(&priv->lock, flags); +} +#endif + +static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, + u32 mask, u32 data) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->lock, flags); + + val = ioread32(priv->base + reg); + val &= ~mask; + val |= (data & mask); + iowrite32(val, priv->base + reg); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +/* + * zone device functions + */ +static int rcar_thermal_get_temp(struct thermal_zone_device *zone, + unsigned long *temp) +{ + struct rcar_thermal_priv *priv = zone->devdata; + int val, min, max, tmp; + + tmp = -200; /* default */ + while (1) { + if (priv->comp < 1 || priv->comp > 12) { + dev_err(priv->dev, + "THSSR invalid data (%d)\n", priv->comp); + priv->comp = 4; /* for next thermal */ + return -EINVAL; + } + + /* + * THS comparator offset and the reference temperature + * + * Comparator | reference | Temperature field + * offset | temperature | measurement + * | (degrees C) | (degrees C) + * -------------+---------------+------------------- + * 1 | -45 | -45 to -30 + * 2 | -30 | -30 to -15 + * 3 | -15 | -15 to 0 + * 4 | 0 | 0 to +15 + * 5 | +15 | +15 to +30 + * 6 | +30 | +30 to +45 + * 7 | +45 | +45 to +60 + * 8 | +60 | +60 to +75 + * 9 | +75 | +75 to +90 + * 10 | +90 | +90 to +105 + * 11 | +105 | +105 to +120 + * 12 | +120 | +120 to +135 + */ + + /* calculate thermal limitation */ + min = (priv->comp * 15) - 60; + max = min + 15; + + /* + * we need to wait 300us after changing comparator offset + * to get stable temperature. + * see "Usage Notes" on datasheet + */ + rcar_thermal_bset(priv, THSCR, CPTAP, priv->comp); + udelay(300); + + /* calculate current temperature */ + val = rcar_thermal_read(priv, THSSR) & CTEMP; + val = (val * 5) - 65; + + dev_dbg(priv->dev, "comp/min/max/val = %d/%d/%d/%d\n", + priv->comp, min, max, val); + + /* + * If val is same as min/max, then, + * it should try again on next comparator. + * But the val might be correct temperature. + * Keep it on "tmp" and compare with next val. + */ + if (tmp == val) + break; + + if (val <= min) { + tmp = min; + priv->comp--; /* try again */ + } else if (val >= max) { + tmp = max; + priv->comp++; /* try again */ + } else { + tmp = val; + break; + } + } + + *temp = tmp; + return 0; +} + +static struct thermal_zone_device_ops rcar_thermal_zone_ops = { + .get_temp = rcar_thermal_get_temp, +}; + +/* + * platform functions + */ +static int rcar_thermal_probe(struct platform_device *pdev) +{ + struct thermal_zone_device *zone; + struct rcar_thermal_priv *priv; + struct resource *res; + int ret; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Could not get platform resource\n"); + return -ENODEV; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "Could not allocate priv\n"); + return -ENOMEM; + } + + priv->comp = 4; /* basic setup */ + priv->dev = &pdev->dev; + spin_lock_init(&priv->lock); + priv->base = devm_ioremap_nocache(&pdev->dev, + res->start, resource_size(res)); + if (!priv->base) { + dev_err(&pdev->dev, "Unable to ioremap thermal register\n"); + ret = -ENOMEM; + goto error_free_priv; + } + + zone = thermal_zone_device_register("rcar_thermal", 0, priv, + &rcar_thermal_zone_ops, 0, 0); + if (IS_ERR(zone)) { + dev_err(&pdev->dev, "thermal zone device is NULL\n"); + ret = PTR_ERR(zone); + goto error_iounmap; + } + + platform_set_drvdata(pdev, zone); + + dev_info(&pdev->dev, "proved\n"); + + return 0; + +error_iounmap: + devm_iounmap(&pdev->dev, priv->base); +error_free_priv: + devm_kfree(&pdev->dev, priv); + + return ret; +} + +static int rcar_thermal_remove(struct platform_device *pdev) +{ + struct thermal_zone_device *zone = platform_get_drvdata(pdev); + struct rcar_thermal_priv *priv = zone->devdata; + + thermal_zone_device_unregister(zone); + platform_set_drvdata(pdev, NULL); + + devm_iounmap(&pdev->dev, priv->base); + devm_kfree(&pdev->dev, priv); + + return 0; +} + +static struct platform_driver rcar_thermal_driver = { + .driver = { + .name = "rcar_thermal", + }, + .probe = rcar_thermal_probe, + .remove = rcar_thermal_remove, +}; +module_platform_driver(rcar_thermal_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("R-Car THS/TSC thermal sensor driver"); +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/drivers/thermal/spear_thermal.c b/drivers/thermal/spear_thermal.c index 5f8ee39f200..9bc969261d0 100644 --- a/drivers/thermal/spear_thermal.c +++ b/drivers/thermal/spear_thermal.c @@ -147,7 +147,7 @@ static int spear_thermal_probe(struct platform_device *pdev) writel_relaxed(stdev->flags, stdev->thermal_base); spear_thermal = thermal_zone_device_register("spear_thermal", 0, 0, - stdev, &ops, 0, 0, 0, 0); + stdev, &ops, 0, 0); if (IS_ERR(spear_thermal)) { dev_err(&pdev->dev, "thermal zone device is NULL\n"); ret = PTR_ERR(spear_thermal); diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index efd81bb25e0..9ee42ca4d28 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -41,15 +41,25 @@ MODULE_AUTHOR("Zhang Rui"); MODULE_DESCRIPTION("Generic thermal management sysfs support"); MODULE_LICENSE("GPL"); -struct thermal_cooling_device_instance { +#define THERMAL_NO_TARGET -1UL +/* + * This structure is used to describe the behavior of + * a certain cooling device on a certain trip point + * in a certain thermal zone + */ +struct thermal_instance { int id; char name[THERMAL_NAME_LENGTH]; struct thermal_zone_device *tz; struct thermal_cooling_device *cdev; int trip; + unsigned long upper; /* Highest cooling state for this trip point */ + unsigned long lower; /* Lowest cooling state for this trip point */ + unsigned long target; /* expected cooling state */ char attr_name[THERMAL_NAME_LENGTH]; struct device_attribute attr; - struct list_head node; + struct list_head tz_node; /* node in tz->thermal_instances */ + struct list_head cdev_node; /* node in cdev->thermal_instances */ }; static DEFINE_IDR(thermal_tz_idr); @@ -308,8 +318,9 @@ passive_store(struct device *dev, struct device_attribute *attr, if (!strncmp("Processor", cdev->type, sizeof("Processor"))) thermal_zone_bind_cooling_device(tz, - THERMAL_TRIPS_NONE, - cdev); + THERMAL_TRIPS_NONE, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT); } mutex_unlock(&thermal_list_lock); if (!tz->passive_delay) @@ -327,9 +338,6 @@ passive_store(struct device *dev, struct device_attribute *attr, tz->passive_delay = 0; } - tz->tc1 = 1; - tz->tc2 = 1; - tz->forced_passive = state; thermal_zone_device_update(tz); @@ -425,10 +433,10 @@ static ssize_t thermal_cooling_device_trip_point_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct thermal_cooling_device_instance *instance; + struct thermal_instance *instance; instance = - container_of(attr, struct thermal_cooling_device_instance, attr); + container_of(attr, struct thermal_instance, attr); if (instance->trip == THERMAL_TRIPS_NONE) return sprintf(buf, "-1\n"); @@ -590,7 +598,7 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) temp->tz = tz; hwmon->count++; - snprintf(temp->temp_input.name, THERMAL_NAME_LENGTH, + snprintf(temp->temp_input.name, sizeof(temp->temp_input.name), "temp%d_input", hwmon->count); temp->temp_input.attr.attr.name = temp->temp_input.name; temp->temp_input.attr.attr.mode = 0444; @@ -603,7 +611,8 @@ thermal_add_hwmon_sysfs(struct thermal_zone_device *tz) if (tz->ops->get_crit_temp) { unsigned long temperature; if (!tz->ops->get_crit_temp(tz, &temperature)) { - snprintf(temp->temp_crit.name, THERMAL_NAME_LENGTH, + snprintf(temp->temp_crit.name, + sizeof(temp->temp_crit.name), "temp%d_crit", hwmon->count); temp->temp_crit.attr.attr.name = temp->temp_crit.name; temp->temp_crit.attr.attr.mode = 0444; @@ -704,74 +713,6 @@ static void thermal_zone_device_set_polling(struct thermal_zone_device *tz, cancel_delayed_work(&tz->poll_queue); } -static void thermal_zone_device_passive(struct thermal_zone_device *tz, - int temp, int trip_temp, int trip) -{ - int trend = 0; - struct thermal_cooling_device_instance *instance; - struct thermal_cooling_device *cdev; - long state, max_state; - - /* - * Above Trip? - * ----------- - * Calculate the thermal trend (using the passive cooling equation) - * and modify the performance limit for all passive cooling devices - * accordingly. Note that we assume symmetry. - */ - if (temp >= trip_temp) { - tz->passive = true; - - trend = (tz->tc1 * (temp - tz->last_temperature)) + - (tz->tc2 * (temp - trip_temp)); - - /* Heating up? */ - if (trend > 0) { - list_for_each_entry(instance, &tz->cooling_devices, - node) { - if (instance->trip != trip) - continue; - cdev = instance->cdev; - cdev->ops->get_cur_state(cdev, &state); - cdev->ops->get_max_state(cdev, &max_state); - if (state++ < max_state) - cdev->ops->set_cur_state(cdev, state); - } - } else if (trend < 0) { /* Cooling off? */ - list_for_each_entry(instance, &tz->cooling_devices, - node) { - if (instance->trip != trip) - continue; - cdev = instance->cdev; - cdev->ops->get_cur_state(cdev, &state); - cdev->ops->get_max_state(cdev, &max_state); - if (state > 0) - cdev->ops->set_cur_state(cdev, --state); - } - } - return; - } - - /* - * Below Trip? - * ----------- - * Implement passive cooling hysteresis to slowly increase performance - * and avoid thrashing around the passive trip point. Note that we - * assume symmetry. - */ - list_for_each_entry(instance, &tz->cooling_devices, node) { - if (instance->trip != trip) - continue; - cdev = instance->cdev; - cdev->ops->get_cur_state(cdev, &state); - cdev->ops->get_max_state(cdev, &max_state); - if (state > 0) - cdev->ops->set_cur_state(cdev, --state); - if (state == 0) - tz->passive = false; - } -} - static void thermal_zone_device_check(struct work_struct *work) { struct thermal_zone_device *tz = container_of(work, struct @@ -791,12 +732,14 @@ static void thermal_zone_device_check(struct work_struct *work) */ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, int trip, - struct thermal_cooling_device *cdev) + struct thermal_cooling_device *cdev, + unsigned long upper, unsigned long lower) { - struct thermal_cooling_device_instance *dev; - struct thermal_cooling_device_instance *pos; + struct thermal_instance *dev; + struct thermal_instance *pos; struct thermal_zone_device *pos1; struct thermal_cooling_device *pos2; + unsigned long max_state; int result; if (trip >= tz->trips || (trip < 0 && trip != THERMAL_TRIPS_NONE)) @@ -814,13 +757,26 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, if (tz != pos1 || cdev != pos2) return -EINVAL; + cdev->ops->get_max_state(cdev, &max_state); + + /* lower default 0, upper default max_state */ + lower = lower == THERMAL_NO_LIMIT ? 0 : lower; + upper = upper == THERMAL_NO_LIMIT ? max_state : upper; + + if (lower > upper || upper > max_state) + return -EINVAL; + dev = - kzalloc(sizeof(struct thermal_cooling_device_instance), GFP_KERNEL); + kzalloc(sizeof(struct thermal_instance), GFP_KERNEL); if (!dev) return -ENOMEM; dev->tz = tz; dev->cdev = cdev; dev->trip = trip; + dev->upper = upper; + dev->lower = lower; + dev->target = THERMAL_NO_TARGET; + result = get_idr(&tz->idr, &tz->lock, &dev->id); if (result) goto free_mem; @@ -841,13 +797,17 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, goto remove_symbol_link; mutex_lock(&tz->lock); - list_for_each_entry(pos, &tz->cooling_devices, node) + mutex_lock(&cdev->lock); + list_for_each_entry(pos, &tz->thermal_instances, tz_node) if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { result = -EEXIST; break; } - if (!result) - list_add_tail(&dev->node, &tz->cooling_devices); + if (!result) { + list_add_tail(&dev->tz_node, &tz->thermal_instances); + list_add_tail(&dev->cdev_node, &cdev->thermal_instances); + } + mutex_unlock(&cdev->lock); mutex_unlock(&tz->lock); if (!result) @@ -877,16 +837,20 @@ int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz, int trip, struct thermal_cooling_device *cdev) { - struct thermal_cooling_device_instance *pos, *next; + struct thermal_instance *pos, *next; mutex_lock(&tz->lock); - list_for_each_entry_safe(pos, next, &tz->cooling_devices, node) { + mutex_lock(&cdev->lock); + list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) { if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) { - list_del(&pos->node); + list_del(&pos->tz_node); + list_del(&pos->cdev_node); + mutex_unlock(&cdev->lock); mutex_unlock(&tz->lock); goto unbind; } } + mutex_unlock(&cdev->lock); mutex_unlock(&tz->lock); return -ENODEV; @@ -934,7 +898,7 @@ thermal_cooling_device_register(char *type, void *devdata, struct thermal_zone_device *pos; int result; - if (strlen(type) >= THERMAL_NAME_LENGTH) + if (type && strlen(type) >= THERMAL_NAME_LENGTH) return ERR_PTR(-EINVAL); if (!ops || !ops->get_max_state || !ops->get_cur_state || @@ -951,8 +915,11 @@ thermal_cooling_device_register(char *type, void *devdata, return ERR_PTR(result); } - strcpy(cdev->type, type); + strcpy(cdev->type, type ? : ""); + mutex_init(&cdev->lock); + INIT_LIST_HEAD(&cdev->thermal_instances); cdev->ops = ops; + cdev->updated = true; cdev->device.class = &thermal_class; cdev->devdata = devdata; dev_set_name(&cdev->device, "cooling_device%d", cdev->id); @@ -1044,6 +1011,136 @@ void thermal_cooling_device_unregister(struct } EXPORT_SYMBOL(thermal_cooling_device_unregister); +static void thermal_cdev_do_update(struct thermal_cooling_device *cdev) +{ + struct thermal_instance *instance; + unsigned long target = 0; + + /* cooling device is updated*/ + if (cdev->updated) + return; + + mutex_lock(&cdev->lock); + /* Make sure cdev enters the deepest cooling state */ + list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { + if (instance->target == THERMAL_NO_TARGET) + continue; + if (instance->target > target) + target = instance->target; + } + mutex_unlock(&cdev->lock); + cdev->ops->set_cur_state(cdev, target); + cdev->updated = true; +} + +static void thermal_zone_do_update(struct thermal_zone_device *tz) +{ + struct thermal_instance *instance; + + list_for_each_entry(instance, &tz->thermal_instances, tz_node) + thermal_cdev_do_update(instance->cdev); +} + +/* + * Cooling algorithm for both active and passive cooling + * + * 1. if the temperature is higher than a trip point, + * a. if the trend is THERMAL_TREND_RAISING, use higher cooling + * state for this trip point + * b. if the trend is THERMAL_TREND_DROPPING, use lower cooling + * state for this trip point + * + * 2. if the temperature is lower than a trip point, use lower + * cooling state for this trip point + * + * Note that this behaves the same as the previous passive cooling + * algorithm. + */ + +static void thermal_zone_trip_update(struct thermal_zone_device *tz, + int trip, long temp) +{ + struct thermal_instance *instance; + struct thermal_cooling_device *cdev = NULL; + unsigned long cur_state, max_state; + long trip_temp; + enum thermal_trip_type trip_type; + enum thermal_trend trend; + + if (trip == THERMAL_TRIPS_NONE) { + trip_temp = tz->forced_passive; + trip_type = THERMAL_TRIPS_NONE; + } else { + tz->ops->get_trip_temp(tz, trip, &trip_temp); + tz->ops->get_trip_type(tz, trip, &trip_type); + } + + if (!tz->ops->get_trend || tz->ops->get_trend(tz, trip, &trend)) { + /* + * compare the current temperature and previous temperature + * to get the thermal trend, if no special requirement + */ + if (tz->temperature > tz->last_temperature) + trend = THERMAL_TREND_RAISING; + else if (tz->temperature < tz->last_temperature) + trend = THERMAL_TREND_DROPPING; + else + trend = THERMAL_TREND_STABLE; + } + + if (temp >= trip_temp) { + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + if (instance->trip != trip) + continue; + + cdev = instance->cdev; + + cdev->ops->get_cur_state(cdev, &cur_state); + cdev->ops->get_max_state(cdev, &max_state); + + if (trend == THERMAL_TREND_RAISING) { + cur_state = cur_state < instance->upper ? + (cur_state + 1) : instance->upper; + } else if (trend == THERMAL_TREND_DROPPING) { + cur_state = cur_state > instance->lower ? + (cur_state - 1) : instance->lower; + } + + /* activate a passive thermal instance */ + if ((trip_type == THERMAL_TRIP_PASSIVE || + trip_type == THERMAL_TRIPS_NONE) && + instance->target == THERMAL_NO_TARGET) + tz->passive++; + + instance->target = cur_state; + cdev->updated = false; /* cooling device needs update */ + } + } else { /* below trip */ + list_for_each_entry(instance, &tz->thermal_instances, tz_node) { + if (instance->trip != trip) + continue; + + /* Do not use the inactive thermal instance */ + if (instance->target == THERMAL_NO_TARGET) + continue; + cdev = instance->cdev; + cdev->ops->get_cur_state(cdev, &cur_state); + + cur_state = cur_state > instance->lower ? + (cur_state - 1) : THERMAL_NO_TARGET; + + /* deactivate a passive thermal instance */ + if ((trip_type == THERMAL_TRIP_PASSIVE || + trip_type == THERMAL_TRIPS_NONE) && + cur_state == THERMAL_NO_TARGET) + tz->passive--; + instance->target = cur_state; + cdev->updated = false; /* cooling device needs update */ + } + } + + return; +} /** * thermal_zone_device_update - force an update of a thermal zone's state * @ttz: the thermal zone to update @@ -1054,8 +1151,6 @@ void thermal_zone_device_update(struct thermal_zone_device *tz) int count, ret = 0; long temp, trip_temp; enum thermal_trip_type trip_type; - struct thermal_cooling_device_instance *instance; - struct thermal_cooling_device *cdev; mutex_lock(&tz->lock); @@ -1065,6 +1160,9 @@ void thermal_zone_device_update(struct thermal_zone_device *tz) goto leave; } + tz->last_temperature = tz->temperature; + tz->temperature = temp; + for (count = 0; count < tz->trips; count++) { tz->ops->get_trip_type(tz, count, &trip_type); tz->ops->get_trip_temp(tz, count, &trip_temp); @@ -1088,32 +1186,18 @@ void thermal_zone_device_update(struct thermal_zone_device *tz) tz->ops->notify(tz, count, trip_type); break; case THERMAL_TRIP_ACTIVE: - list_for_each_entry(instance, &tz->cooling_devices, - node) { - if (instance->trip != count) - continue; - - cdev = instance->cdev; - - if (temp >= trip_temp) - cdev->ops->set_cur_state(cdev, 1); - else - cdev->ops->set_cur_state(cdev, 0); - } + thermal_zone_trip_update(tz, count, temp); break; case THERMAL_TRIP_PASSIVE: if (temp >= trip_temp || tz->passive) - thermal_zone_device_passive(tz, temp, - trip_temp, count); + thermal_zone_trip_update(tz, count, temp); break; } } if (tz->forced_passive) - thermal_zone_device_passive(tz, temp, tz->forced_passive, - THERMAL_TRIPS_NONE); - - tz->last_temperature = temp; + thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE, temp); + thermal_zone_do_update(tz); leave: if (tz->passive) @@ -1236,8 +1320,6 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) * @mask: a bit string indicating the writeablility of trip points * @devdata: private device data * @ops: standard thermal zone device callbacks - * @tc1: thermal coefficient 1 for passive calculations - * @tc2: thermal coefficient 2 for passive calculations * @passive_delay: number of milliseconds to wait between polls when * performing passive cooling * @polling_delay: number of milliseconds to wait between polls when checking @@ -1245,13 +1327,12 @@ static void remove_trip_attrs(struct thermal_zone_device *tz) * driven systems) * * thermal_zone_device_unregister() must be called when the device is no - * longer needed. The passive cooling formula uses tc1 and tc2 as described in - * section 11.1.5.1 of the ACPI specification 3.0. + * longer needed. The passive cooling depends on the .get_trend() return value. */ struct thermal_zone_device *thermal_zone_device_register(const char *type, int trips, int mask, void *devdata, const struct thermal_zone_device_ops *ops, - int tc1, int tc2, int passive_delay, int polling_delay) + int passive_delay, int polling_delay) { struct thermal_zone_device *tz; struct thermal_cooling_device *pos; @@ -1260,7 +1341,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, int count; int passive = 0; - if (strlen(type) >= THERMAL_NAME_LENGTH) + if (type && strlen(type) >= THERMAL_NAME_LENGTH) return ERR_PTR(-EINVAL); if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips) @@ -1273,7 +1354,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, if (!tz) return ERR_PTR(-ENOMEM); - INIT_LIST_HEAD(&tz->cooling_devices); + INIT_LIST_HEAD(&tz->thermal_instances); idr_init(&tz->idr); mutex_init(&tz->lock); result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id); @@ -1282,13 +1363,11 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, return ERR_PTR(result); } - strcpy(tz->type, type); + strcpy(tz->type, type ? : ""); tz->ops = ops; tz->device.class = &thermal_class; tz->devdata = devdata; tz->trips = trips; - tz->tc1 = tc1; - tz->tc2 = tc2; tz->passive_delay = passive_delay; tz->polling_delay = polling_delay; diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 2944ff88fdc..f4abfe238f9 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c @@ -478,7 +478,6 @@ static void xencons_backend_changed(struct xenbus_device *dev, case XenbusStateInitialising: case XenbusStateInitialised: case XenbusStateUnknown: - case XenbusStateClosed: break; case XenbusStateInitWait: @@ -488,6 +487,10 @@ static void xencons_backend_changed(struct xenbus_device *dev, xenbus_switch_state(dev, XenbusStateConnected); break; + case XenbusStateClosed: + if (dev->state == XenbusStateClosed) + break; + /* Missed the backend's CLOSING state -- fallthrough */ case XenbusStateClosing: xenbus_frontend_closed(dev); break; diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c index c0b334327d9..10020547c60 100644 --- a/drivers/tty/serial/kgdboc.c +++ b/drivers/tty/serial/kgdboc.c @@ -97,7 +97,8 @@ static void kgdboc_restore_input(void) static int kgdboc_register_kbd(char **cptr) { - if (strncmp(*cptr, "kbd", 3) == 0) { + if (strncmp(*cptr, "kbd", 3) == 0 || + strncmp(*cptr, "kdb", 3) == 0) { if (kdb_poll_idx < KDB_POLL_FUNC_MAX) { kdb_poll_funcs[kdb_poll_idx] = kdb_get_kbd_char; kdb_poll_idx++; diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 999ca63afde..f87d7e8964b 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3442,6 +3442,19 @@ int con_debug_enter(struct vc_data *vc) kdb_set(2, setargs); } } + if (vc->vc_cols < 999) { + int colcount; + char cols[4]; + const char *setargs[3] = { + "set", + "COLUMNS", + cols, + }; + if (kdbgetintenv(setargs[0], &colcount)) { + snprintf(cols, 4, "%i", vc->vc_cols); + kdb_set(2, setargs); + } + } #endif /* CONFIG_KGDB_KDB */ return ret; } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 36f2be4def2..981f2132d12 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -1551,6 +1551,9 @@ static const struct usb_device_id acm_ids[] = { Maybe we should define a new quirk for this. */ }, + { USB_DEVICE(0x0572, 0x1340), /* Conexant CX93010-2x UCMxx */ + .driver_info = NO_UNION_NORMAL, + }, { USB_DEVICE(0x1bbb, 0x0003), /* Alcatel OT-I650 */ .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c index 131dec04794..48220e129f8 100644 --- a/drivers/xen/xenbus/xenbus_xs.c +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -48,6 +48,7 @@ #include <xen/xenbus.h> #include <xen/xen.h> #include "xenbus_comms.h" +#include <asm/xen/hypervisor.h> struct xs_stored_msg { struct list_head list; @@ -618,7 +619,24 @@ static struct xenbus_watch *find_watch(const char *token) return NULL; } +/* + * Certain older XenBus toolstack cannot handle reading values that are + * not populated. Some Xen 3.4 installation are incapable of doing this + * so if we are running on anything older than 4 do not attempt to read + * control/platform-feature-xs_reset_watches. + */ +static bool xen_strict_xenbus_quirk() +{ + uint32_t eax, ebx, ecx, edx, base; + + base = xen_cpuid_base(); + cpuid(base + 1, &eax, &ebx, &ecx, &edx); + if ((eax >> 16) < 4) + return true; + return false; + +} static void xs_reset_watches(void) { int err, supported = 0; @@ -626,6 +644,9 @@ static void xs_reset_watches(void) if (!xen_hvm_domain() || xen_initial_domain()) return; + if (xen_strict_xenbus_quirk()) + return; + err = xenbus_scanf(XBT_NIL, "control", "platform-feature-xs_reset_watches", "%d", &supported); if (err != 1 || !supported) |