Merge branches 'acpi_pad-bugzilla-42981', 'apei-bugzilla-43282', 'video-bugzilla-43168', 'bugzilla-40002' and 'bugfix-misc' into release
bug fixes
This commit is contained in:
@@ -140,4 +140,12 @@ source "drivers/virt/Kconfig"
|
||||
|
||||
source "drivers/devfreq/Kconfig"
|
||||
|
||||
source "drivers/extcon/Kconfig"
|
||||
|
||||
source "drivers/memory/Kconfig"
|
||||
|
||||
source "drivers/iio/Kconfig"
|
||||
|
||||
source "drivers/vme/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
+5
-2
@@ -18,7 +18,7 @@ obj-$(CONFIG_SFI) += sfi/
|
||||
# PnP must come after ACPI since it will eventually need to check if acpi
|
||||
# was used and do nothing if so
|
||||
obj-$(CONFIG_PNP) += pnp/
|
||||
obj-$(CONFIG_ARM_AMBA) += amba/
|
||||
obj-y += amba/
|
||||
# Many drivers will want to use DMA so this has to be made available
|
||||
# really early.
|
||||
obj-$(CONFIG_DMA_ENGINE) += dma/
|
||||
@@ -92,7 +92,6 @@ obj-$(CONFIG_BT) += bluetooth/
|
||||
obj-$(CONFIG_ACCESSIBILITY) += accessibility/
|
||||
obj-$(CONFIG_ISDN) += isdn/
|
||||
obj-$(CONFIG_EDAC) += edac/
|
||||
obj-$(CONFIG_MCA) += mca/
|
||||
obj-$(CONFIG_EISA) += eisa/
|
||||
obj-y += lguest/
|
||||
obj-$(CONFIG_CPU_FREQ) += cpufreq/
|
||||
@@ -134,3 +133,7 @@ obj-$(CONFIG_VIRT_DRIVERS) += virt/
|
||||
obj-$(CONFIG_HYPERV) += hv/
|
||||
|
||||
obj-$(CONFIG_PM_DEVFREQ) += devfreq/
|
||||
obj-$(CONFIG_EXTCON) += extcon/
|
||||
obj-$(CONFIG_MEMORY) += memory/
|
||||
obj-$(CONFIG_IIO) += iio/
|
||||
obj-$(CONFIG_VME_BUS) += vme/
|
||||
|
||||
@@ -208,7 +208,7 @@ config ACPI_IPMI
|
||||
|
||||
config ACPI_HOTPLUG_CPU
|
||||
bool
|
||||
depends on ACPI_PROCESSOR && HOTPLUG_CPU
|
||||
depends on EXPERIMENTAL && ACPI_PROCESSOR && HOTPLUG_CPU
|
||||
select ACPI_CONTAINER
|
||||
default y
|
||||
|
||||
|
||||
@@ -243,7 +243,7 @@ static int pre_map_gar_callback(struct apei_exec_context *ctx,
|
||||
u8 ins = entry->instruction;
|
||||
|
||||
if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
|
||||
return acpi_os_map_generic_address(&entry->register_region);
|
||||
return apei_map_generic_address(&entry->register_region);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -276,7 +276,7 @@ static int post_unmap_gar_callback(struct apei_exec_context *ctx,
|
||||
u8 ins = entry->instruction;
|
||||
|
||||
if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)
|
||||
acpi_os_unmap_generic_address(&entry->register_region);
|
||||
apei_unmap_generic_address(&entry->register_region);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -606,6 +606,19 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int apei_map_generic_address(struct acpi_generic_address *reg)
|
||||
{
|
||||
int rc;
|
||||
u32 access_bit_width;
|
||||
u64 address;
|
||||
|
||||
rc = apei_check_gar(reg, &address, &access_bit_width);
|
||||
if (rc)
|
||||
return rc;
|
||||
return acpi_os_map_generic_address(reg);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(apei_map_generic_address);
|
||||
|
||||
/* read GAR in interrupt (including NMI) or process context */
|
||||
int apei_read(u64 *val, struct acpi_generic_address *reg)
|
||||
{
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#define APEI_INTERNAL_H
|
||||
|
||||
#include <linux/cper.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/acpi_io.h>
|
||||
|
||||
struct apei_exec_context;
|
||||
|
||||
@@ -68,6 +70,13 @@ static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 actio
|
||||
/* IP has been set in instruction function */
|
||||
#define APEI_EXEC_SET_IP 1
|
||||
|
||||
int apei_map_generic_address(struct acpi_generic_address *reg);
|
||||
|
||||
static inline void apei_unmap_generic_address(struct acpi_generic_address *reg)
|
||||
{
|
||||
acpi_os_unmap_generic_address(reg);
|
||||
}
|
||||
|
||||
int apei_read(u64 *val, struct acpi_generic_address *reg);
|
||||
int apei_write(u64 val, struct acpi_generic_address *reg);
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic)
|
||||
if (!ghes)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ghes->generic = generic;
|
||||
rc = acpi_os_map_generic_address(&generic->error_status_address);
|
||||
rc = apei_map_generic_address(&generic->error_status_address);
|
||||
if (rc)
|
||||
goto err_free;
|
||||
error_block_length = generic->error_block_length;
|
||||
@@ -321,7 +321,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic)
|
||||
return ghes;
|
||||
|
||||
err_unmap:
|
||||
acpi_os_unmap_generic_address(&generic->error_status_address);
|
||||
apei_unmap_generic_address(&generic->error_status_address);
|
||||
err_free:
|
||||
kfree(ghes);
|
||||
return ERR_PTR(rc);
|
||||
@@ -330,7 +330,7 @@ err_free:
|
||||
static void ghes_fini(struct ghes *ghes)
|
||||
{
|
||||
kfree(ghes->estatus);
|
||||
acpi_os_unmap_generic_address(&ghes->generic->error_status_address);
|
||||
apei_unmap_generic_address(&ghes->generic->error_status_address);
|
||||
}
|
||||
|
||||
enum {
|
||||
|
||||
@@ -643,11 +643,19 @@ static int acpi_battery_update(struct acpi_battery *battery)
|
||||
|
||||
static void acpi_battery_refresh(struct acpi_battery *battery)
|
||||
{
|
||||
int power_unit;
|
||||
|
||||
if (!battery->bat.dev)
|
||||
return;
|
||||
|
||||
power_unit = battery->power_unit;
|
||||
|
||||
acpi_battery_get_info(battery);
|
||||
/* The battery may have changed its reporting units. */
|
||||
|
||||
if (power_unit == battery->power_unit)
|
||||
return;
|
||||
|
||||
/* The battery has changed its reporting units. */
|
||||
sysfs_remove_battery(battery);
|
||||
sysfs_add_battery(battery);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/io.h>
|
||||
#include <acpi/acpi.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
|
||||
|
||||
+57
-31
@@ -182,41 +182,66 @@ EXPORT_SYMBOL(acpi_bus_get_private_data);
|
||||
Power Management
|
||||
-------------------------------------------------------------------------- */
|
||||
|
||||
static const char *state_string(int state)
|
||||
{
|
||||
switch (state) {
|
||||
case ACPI_STATE_D0:
|
||||
return "D0";
|
||||
case ACPI_STATE_D1:
|
||||
return "D1";
|
||||
case ACPI_STATE_D2:
|
||||
return "D2";
|
||||
case ACPI_STATE_D3_HOT:
|
||||
return "D3hot";
|
||||
case ACPI_STATE_D3_COLD:
|
||||
return "D3";
|
||||
default:
|
||||
return "(unknown)";
|
||||
}
|
||||
}
|
||||
|
||||
static int __acpi_bus_get_power(struct acpi_device *device, int *state)
|
||||
{
|
||||
int result = 0;
|
||||
acpi_status status = 0;
|
||||
unsigned long long psc = 0;
|
||||
int result = ACPI_STATE_UNKNOWN;
|
||||
|
||||
if (!device || !state)
|
||||
return -EINVAL;
|
||||
|
||||
*state = ACPI_STATE_UNKNOWN;
|
||||
|
||||
if (device->flags.power_manageable) {
|
||||
/*
|
||||
* Get the device's power state either directly (via _PSC) or
|
||||
* indirectly (via power resources).
|
||||
*/
|
||||
if (device->power.flags.power_resources) {
|
||||
result = acpi_power_get_inferred_state(device, state);
|
||||
if (result)
|
||||
return result;
|
||||
} else if (device->power.flags.explicit_get) {
|
||||
status = acpi_evaluate_integer(device->handle, "_PSC",
|
||||
NULL, &psc);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
*state = (int)psc;
|
||||
}
|
||||
} else {
|
||||
if (!device->flags.power_manageable) {
|
||||
/* TBD: Non-recursive algorithm for walking up hierarchy. */
|
||||
*state = device->parent ?
|
||||
device->parent->power.state : ACPI_STATE_D0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n",
|
||||
device->pnp.bus_id, *state));
|
||||
/*
|
||||
* Get the device's power state either directly (via _PSC) or
|
||||
* indirectly (via power resources).
|
||||
*/
|
||||
if (device->power.flags.explicit_get) {
|
||||
unsigned long long psc;
|
||||
acpi_status status = acpi_evaluate_integer(device->handle,
|
||||
"_PSC", NULL, &psc);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
result = psc;
|
||||
}
|
||||
/* The test below covers ACPI_STATE_UNKNOWN too. */
|
||||
if (result <= ACPI_STATE_D2) {
|
||||
; /* Do nothing. */
|
||||
} else if (device->power.flags.power_resources) {
|
||||
int error = acpi_power_get_inferred_state(device, &result);
|
||||
if (error)
|
||||
return error;
|
||||
} else if (result == ACPI_STATE_D3_HOT) {
|
||||
result = ACPI_STATE_D3;
|
||||
}
|
||||
*state = result;
|
||||
|
||||
out:
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is %s\n",
|
||||
device->pnp.bus_id, state_string(*state)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -234,13 +259,14 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state)
|
||||
/* Make sure this is a valid target state */
|
||||
|
||||
if (state == device->power.state) {
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n",
|
||||
state));
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at %s\n",
|
||||
state_string(state)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!device->power.states[state].flags.valid) {
|
||||
printk(KERN_WARNING PREFIX "Device does not support D%d\n", state);
|
||||
printk(KERN_WARNING PREFIX "Device does not support %s\n",
|
||||
state_string(state));
|
||||
return -ENODEV;
|
||||
}
|
||||
if (device->parent && (state < device->parent->power.state)) {
|
||||
@@ -294,13 +320,13 @@ static int __acpi_bus_set_power(struct acpi_device *device, int state)
|
||||
end:
|
||||
if (result)
|
||||
printk(KERN_WARNING PREFIX
|
||||
"Device [%s] failed to transition to D%d\n",
|
||||
device->pnp.bus_id, state);
|
||||
"Device [%s] failed to transition to %s\n",
|
||||
device->pnp.bus_id, state_string(state));
|
||||
else {
|
||||
device->power.state = state;
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"Device [%s] transitioned to D%d\n",
|
||||
device->pnp.bus_id, state));
|
||||
"Device [%s] transitioned to %s\n",
|
||||
device->pnp.bus_id, state_string(state)));
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -69,6 +69,7 @@ static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type)
|
||||
up_read(&bus_type_sem);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_acpi_bus_type);
|
||||
|
||||
static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle)
|
||||
{
|
||||
@@ -85,6 +86,7 @@ static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle)
|
||||
up_read(&bus_type_sem);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);
|
||||
|
||||
/* Get device's handler per its address under its parent */
|
||||
struct acpi_find_child {
|
||||
|
||||
@@ -720,21 +720,21 @@ static int acpi_pci_link_add(struct acpi_device *device)
|
||||
acpi_device_bid(device));
|
||||
for (i = 0; i < link->irq.possible_count; i++) {
|
||||
if (link->irq.active == link->irq.possible[i]) {
|
||||
printk(" *%d", link->irq.possible[i]);
|
||||
printk(KERN_CONT " *%d", link->irq.possible[i]);
|
||||
found = 1;
|
||||
} else
|
||||
printk(" %d", link->irq.possible[i]);
|
||||
printk(KERN_CONT " %d", link->irq.possible[i]);
|
||||
}
|
||||
|
||||
printk(")");
|
||||
printk(KERN_CONT ")");
|
||||
|
||||
if (!found)
|
||||
printk(" *%d", link->irq.active);
|
||||
printk(KERN_CONT " *%d", link->irq.active);
|
||||
|
||||
if (!link->device->status.enabled)
|
||||
printk(", disabled.");
|
||||
printk(KERN_CONT ", disabled.");
|
||||
|
||||
printk("\n");
|
||||
printk(KERN_CONT "\n");
|
||||
|
||||
list_add_tail(&link->list, &acpi_link_list);
|
||||
|
||||
|
||||
@@ -631,7 +631,7 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
|
||||
* We know a device's inferred power state when all the resources
|
||||
* required for a given D-state are 'on'.
|
||||
*/
|
||||
for (i = ACPI_STATE_D0; i < ACPI_STATE_D3_HOT; i++) {
|
||||
for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
|
||||
list = &device->power.states[i].resources;
|
||||
if (list->count < 1)
|
||||
continue;
|
||||
|
||||
@@ -333,6 +333,7 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr)
|
||||
struct acpi_buffer state = { 0, NULL };
|
||||
union acpi_object *pss = NULL;
|
||||
int i;
|
||||
int last_invalid = -1;
|
||||
|
||||
|
||||
status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
|
||||
@@ -394,14 +395,33 @@ static int acpi_processor_get_performance_states(struct acpi_processor *pr)
|
||||
((u32)(px->core_frequency * 1000) !=
|
||||
(px->core_frequency * 1000))) {
|
||||
printk(KERN_ERR FW_BUG PREFIX
|
||||
"Invalid BIOS _PSS frequency: 0x%llx MHz\n",
|
||||
px->core_frequency);
|
||||
result = -EFAULT;
|
||||
kfree(pr->performance->states);
|
||||
goto end;
|
||||
"Invalid BIOS _PSS frequency found for processor %d: 0x%llx MHz\n",
|
||||
pr->id, px->core_frequency);
|
||||
if (last_invalid == -1)
|
||||
last_invalid = i;
|
||||
} else {
|
||||
if (last_invalid != -1) {
|
||||
/*
|
||||
* Copy this valid entry over last_invalid entry
|
||||
*/
|
||||
memcpy(&(pr->performance->states[last_invalid]),
|
||||
px, sizeof(struct acpi_processor_px));
|
||||
++last_invalid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (last_invalid == 0) {
|
||||
printk(KERN_ERR FW_BUG PREFIX
|
||||
"No valid BIOS _PSS frequency found for processor %d\n", pr->id);
|
||||
result = -EFAULT;
|
||||
kfree(pr->performance->states);
|
||||
pr->performance->states = NULL;
|
||||
}
|
||||
|
||||
if (last_invalid > 0)
|
||||
pr->performance->state_count = last_invalid;
|
||||
|
||||
end:
|
||||
kfree(buffer.pointer);
|
||||
|
||||
|
||||
@@ -1567,6 +1567,7 @@ static int acpi_bus_scan_fixed(void)
|
||||
ACPI_BUS_TYPE_POWER_BUTTON,
|
||||
ACPI_STA_DEFAULT,
|
||||
&ops);
|
||||
device_init_wakeup(&device->dev, true);
|
||||
}
|
||||
|
||||
if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) {
|
||||
|
||||
+51
-12
@@ -57,6 +57,7 @@ MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend.");
|
||||
MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".);
|
||||
|
||||
static u8 sleep_states[ACPI_S_STATE_COUNT];
|
||||
static bool pwr_btn_event_pending;
|
||||
|
||||
static void acpi_sleep_tts_switch(u32 acpi_state)
|
||||
{
|
||||
@@ -93,11 +94,9 @@ static int acpi_sleep_prepare(u32 acpi_state)
|
||||
#ifdef CONFIG_ACPI_SLEEP
|
||||
/* do we have a wakeup address for S2 and S3? */
|
||||
if (acpi_state == ACPI_STATE_S3) {
|
||||
if (!acpi_wakeup_address) {
|
||||
if (!acpi_wakeup_address)
|
||||
return -EFAULT;
|
||||
}
|
||||
acpi_set_firmware_waking_vector(
|
||||
(acpi_physical_address)acpi_wakeup_address);
|
||||
acpi_set_firmware_waking_vector(acpi_wakeup_address);
|
||||
|
||||
}
|
||||
ACPI_FLUSH_CPU_CACHE();
|
||||
@@ -186,6 +185,14 @@ static int acpi_pm_prepare(void)
|
||||
return error;
|
||||
}
|
||||
|
||||
static int find_powerf_dev(struct device *dev, void *data)
|
||||
{
|
||||
struct acpi_device *device = to_acpi_device(dev);
|
||||
const char *hid = acpi_device_hid(device);
|
||||
|
||||
return !strcmp(hid, ACPI_BUTTON_HID_POWERF);
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_pm_finish - Instruct the platform to leave a sleep state.
|
||||
*
|
||||
@@ -194,6 +201,7 @@ static int acpi_pm_prepare(void)
|
||||
*/
|
||||
static void acpi_pm_finish(void)
|
||||
{
|
||||
struct device *pwr_btn_dev;
|
||||
u32 acpi_state = acpi_target_sleep_state;
|
||||
|
||||
acpi_ec_unblock_transactions();
|
||||
@@ -211,6 +219,23 @@ static void acpi_pm_finish(void)
|
||||
acpi_set_firmware_waking_vector((acpi_physical_address) 0);
|
||||
|
||||
acpi_target_sleep_state = ACPI_STATE_S0;
|
||||
|
||||
/* If we were woken with the fixed power button, provide a small
|
||||
* hint to userspace in the form of a wakeup event on the fixed power
|
||||
* button device (if it can be found).
|
||||
*
|
||||
* We delay the event generation til now, as the PM layer requires
|
||||
* timekeeping to be running before we generate events. */
|
||||
if (!pwr_btn_event_pending)
|
||||
return;
|
||||
|
||||
pwr_btn_event_pending = false;
|
||||
pwr_btn_dev = bus_find_device(&acpi_bus_type, NULL, NULL,
|
||||
find_powerf_dev);
|
||||
if (pwr_btn_dev) {
|
||||
pm_wakeup_event(pwr_btn_dev, 0);
|
||||
put_device(pwr_btn_dev);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,9 +325,23 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
|
||||
/* ACPI 3.0 specs (P62) says that it's the responsibility
|
||||
* of the OSPM to clear the status bit [ implying that the
|
||||
* POWER_BUTTON event should not reach userspace ]
|
||||
*
|
||||
* However, we do generate a small hint for userspace in the form of
|
||||
* a wakeup event. We flag this condition for now and generate the
|
||||
* event later, as we're currently too early in resume to be able to
|
||||
* generate wakeup events.
|
||||
*/
|
||||
if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3))
|
||||
acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
|
||||
if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) {
|
||||
acpi_event_status pwr_btn_status;
|
||||
|
||||
acpi_get_event_status(ACPI_EVENT_POWER_BUTTON, &pwr_btn_status);
|
||||
|
||||
if (pwr_btn_status & ACPI_EVENT_FLAG_SET) {
|
||||
acpi_clear_event(ACPI_EVENT_POWER_BUTTON);
|
||||
/* Flag for later */
|
||||
pwr_btn_event_pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable and clear GPE status before interrupt is enabled. Some GPEs
|
||||
@@ -732,8 +771,8 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
|
||||
* can wake the system. _S0W may be valid, too.
|
||||
*/
|
||||
if (acpi_target_sleep_state == ACPI_STATE_S0 ||
|
||||
(device_may_wakeup(dev) &&
|
||||
adev->wakeup.sleep_state <= acpi_target_sleep_state)) {
|
||||
(device_may_wakeup(dev) && adev->wakeup.flags.valid &&
|
||||
adev->wakeup.sleep_state >= acpi_target_sleep_state)) {
|
||||
acpi_status status;
|
||||
|
||||
acpi_method[3] = 'W';
|
||||
@@ -887,7 +926,7 @@ int __init acpi_sleep_init(void)
|
||||
status = acpi_get_sleep_type_data(i, &type_a, &type_b);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
sleep_states[i] = 1;
|
||||
printk(" S%d", i);
|
||||
printk(KERN_CONT " S%d", i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -901,7 +940,7 @@ int __init acpi_sleep_init(void)
|
||||
hibernation_set_ops(old_suspend_ordering ?
|
||||
&acpi_hibernation_ops_old : &acpi_hibernation_ops);
|
||||
sleep_states[ACPI_STATE_S4] = 1;
|
||||
printk(" S4");
|
||||
printk(KERN_CONT " S4");
|
||||
if (!nosigcheck) {
|
||||
acpi_get_table(ACPI_SIG_FACS, 1,
|
||||
(struct acpi_table_header **)&facs);
|
||||
@@ -914,11 +953,11 @@ int __init acpi_sleep_init(void)
|
||||
status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
sleep_states[ACPI_STATE_S5] = 1;
|
||||
printk(" S5");
|
||||
printk(KERN_CONT " S5");
|
||||
pm_power_off_prepare = acpi_power_off_prepare;
|
||||
pm_power_off = acpi_power_off;
|
||||
}
|
||||
printk(")\n");
|
||||
printk(KERN_CONT ")\n");
|
||||
/*
|
||||
* Register the tts_notifier to reboot notifier list so that the _TTS
|
||||
* object can also be evaluated when the system enters S5.
|
||||
|
||||
@@ -173,7 +173,7 @@ static int param_set_trace_state(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (!strncmp(val, "enable", strlen("enable") - 1)) {
|
||||
if (!strncmp(val, "enable", strlen("enable"))) {
|
||||
result = acpi_debug_trace(trace_method_name, trace_debug_level,
|
||||
trace_debug_layer, 0);
|
||||
if (result)
|
||||
@@ -181,7 +181,7 @@ static int param_set_trace_state(const char *val, struct kernel_param *kp)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!strncmp(val, "disable", strlen("disable") - 1)) {
|
||||
if (!strncmp(val, "disable", strlen("disable"))) {
|
||||
int name = 0;
|
||||
result = acpi_debug_trace((char *)&name, trace_debug_level,
|
||||
trace_debug_layer, 0);
|
||||
|
||||
@@ -382,3 +382,33 @@ acpi_evaluate_reference(acpi_handle handle,
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(acpi_evaluate_reference);
|
||||
|
||||
acpi_status
|
||||
acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld *pld)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *output;
|
||||
|
||||
status = acpi_evaluate_object(handle, "_PLD", NULL, &buffer);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
output = buffer.pointer;
|
||||
|
||||
if (!output || output->type != ACPI_TYPE_PACKAGE
|
||||
|| !output->package.count
|
||||
|| output->package.elements[0].type != ACPI_TYPE_BUFFER
|
||||
|| output->package.elements[0].buffer.length > sizeof(*pld)) {
|
||||
status = AE_TYPE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(pld, output->package.elements[0].buffer.pointer,
|
||||
output->package.elements[0].buffer.length);
|
||||
out:
|
||||
kfree(buffer.pointer);
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_get_physical_device_location);
|
||||
|
||||
+24
-11
@@ -558,6 +558,8 @@ acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
|
||||
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
|
||||
struct acpi_object_list args = { 1, &arg0 };
|
||||
|
||||
if (!video->cap._DOS)
|
||||
return 0;
|
||||
|
||||
if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1)
|
||||
return -EINVAL;
|
||||
@@ -1687,10 +1689,6 @@ static int acpi_video_bus_add(struct acpi_device *device)
|
||||
set_bit(KEY_BRIGHTNESS_ZERO, input->keybit);
|
||||
set_bit(KEY_DISPLAY_OFF, input->keybit);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
goto err_stop_video;
|
||||
|
||||
printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n",
|
||||
ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
|
||||
video->flags.multihead ? "yes" : "no",
|
||||
@@ -1701,12 +1699,16 @@ static int acpi_video_bus_add(struct acpi_device *device)
|
||||
video->pm_nb.priority = 0;
|
||||
error = register_pm_notifier(&video->pm_nb);
|
||||
if (error)
|
||||
goto err_unregister_input_dev;
|
||||
goto err_stop_video;
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
goto err_unregister_pm_notifier;
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister_input_dev:
|
||||
input_unregister_device(input);
|
||||
err_unregister_pm_notifier:
|
||||
unregister_pm_notifier(&video->pm_nb);
|
||||
err_stop_video:
|
||||
acpi_video_bus_stop_devices(video);
|
||||
err_free_input_dev:
|
||||
@@ -1743,9 +1745,18 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init is_i740(struct pci_dev *dev)
|
||||
{
|
||||
if (dev->device == 0x00D1)
|
||||
return 1;
|
||||
if (dev->device == 0x7000)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init intel_opregion_present(void)
|
||||
{
|
||||
#if defined(CONFIG_DRM_I915) || defined(CONFIG_DRM_I915_MODULE)
|
||||
int opregion = 0;
|
||||
struct pci_dev *dev = NULL;
|
||||
u32 address;
|
||||
|
||||
@@ -1754,13 +1765,15 @@ static int __init intel_opregion_present(void)
|
||||
continue;
|
||||
if (dev->vendor != PCI_VENDOR_ID_INTEL)
|
||||
continue;
|
||||
/* We don't want to poke around undefined i740 registers */
|
||||
if (is_i740(dev))
|
||||
continue;
|
||||
pci_read_config_dword(dev, 0xfc, &address);
|
||||
if (!address)
|
||||
continue;
|
||||
return 1;
|
||||
opregion = 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
return opregion;
|
||||
}
|
||||
|
||||
int acpi_video_register(void)
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
obj-y += bus.o
|
||||
|
||||
obj-$(CONFIG_ARM_AMBA) += bus.o
|
||||
obj-$(CONFIG_TEGRA_AHB) += tegra-ahb.o
|
||||
|
||||
+51
-2
@@ -527,9 +527,9 @@ int amba_device_add(struct amba_device *dev, struct resource *parent)
|
||||
if (ret)
|
||||
goto err_release;
|
||||
|
||||
if (dev->irq[0] && dev->irq[0] != NO_IRQ)
|
||||
if (dev->irq[0])
|
||||
ret = device_create_file(&dev->dev, &dev_attr_irq0);
|
||||
if (ret == 0 && dev->irq[1] && dev->irq[1] != NO_IRQ)
|
||||
if (ret == 0 && dev->irq[1])
|
||||
ret = device_create_file(&dev->dev, &dev_attr_irq1);
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
@@ -543,6 +543,55 @@ int amba_device_add(struct amba_device *dev, struct resource *parent)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amba_device_add);
|
||||
|
||||
static struct amba_device *
|
||||
amba_aphb_device_add(struct device *parent, const char *name,
|
||||
resource_size_t base, size_t size, int irq1, int irq2,
|
||||
void *pdata, unsigned int periphid, u64 dma_mask)
|
||||
{
|
||||
struct amba_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = amba_device_alloc(name, base, size);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev->dma_mask = dma_mask;
|
||||
dev->dev.coherent_dma_mask = dma_mask;
|
||||
dev->irq[0] = irq1;
|
||||
dev->irq[1] = irq2;
|
||||
dev->periphid = periphid;
|
||||
dev->dev.platform_data = pdata;
|
||||
dev->dev.parent = parent;
|
||||
|
||||
ret = amba_device_add(dev, &iomem_resource);
|
||||
if (ret) {
|
||||
amba_device_put(dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
struct amba_device *
|
||||
amba_apb_device_add(struct device *parent, const char *name,
|
||||
resource_size_t base, size_t size, int irq1, int irq2,
|
||||
void *pdata, unsigned int periphid)
|
||||
{
|
||||
return amba_aphb_device_add(parent, name, base, size, irq1, irq2, pdata,
|
||||
periphid, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amba_apb_device_add);
|
||||
|
||||
struct amba_device *
|
||||
amba_ahb_device_add(struct device *parent, const char *name,
|
||||
resource_size_t base, size_t size, int irq1, int irq2,
|
||||
void *pdata, unsigned int periphid)
|
||||
{
|
||||
return amba_aphb_device_add(parent, name, base, size, irq1, irq2, pdata,
|
||||
periphid, ~0ULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(amba_ahb_device_add);
|
||||
|
||||
static void amba_device_initialize(struct amba_device *dev, const char *name)
|
||||
{
|
||||
device_initialize(&dev->dev);
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (C) 2011 Google, Inc.
|
||||
*
|
||||
* Author:
|
||||
* Jay Cheng <jacheng@nvidia.com>
|
||||
* James Wylder <james.wylder@motorola.com>
|
||||
* Benoit Goby <benoit@android.com>
|
||||
* Colin Cross <ccross@android.com>
|
||||
* Hiroshi DOYU <hdoyu@nvidia.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_NAME "tegra-ahb"
|
||||
|
||||
#define AHB_ARBITRATION_DISABLE 0x00
|
||||
#define AHB_ARBITRATION_PRIORITY_CTRL 0x04
|
||||
#define AHB_PRIORITY_WEIGHT(x) (((x) & 0x7) << 29)
|
||||
#define PRIORITY_SELECT_USB BIT(6)
|
||||
#define PRIORITY_SELECT_USB2 BIT(18)
|
||||
#define PRIORITY_SELECT_USB3 BIT(17)
|
||||
|
||||
#define AHB_GIZMO_AHB_MEM 0x0c
|
||||
#define ENB_FAST_REARBITRATE BIT(2)
|
||||
#define DONT_SPLIT_AHB_WR BIT(7)
|
||||
|
||||
#define AHB_GIZMO_APB_DMA 0x10
|
||||
#define AHB_GIZMO_IDE 0x18
|
||||
#define AHB_GIZMO_USB 0x1c
|
||||
#define AHB_GIZMO_AHB_XBAR_BRIDGE 0x20
|
||||
#define AHB_GIZMO_CPU_AHB_BRIDGE 0x24
|
||||
#define AHB_GIZMO_COP_AHB_BRIDGE 0x28
|
||||
#define AHB_GIZMO_XBAR_APB_CTLR 0x2c
|
||||
#define AHB_GIZMO_VCP_AHB_BRIDGE 0x30
|
||||
#define AHB_GIZMO_NAND 0x3c
|
||||
#define AHB_GIZMO_SDMMC4 0x44
|
||||
#define AHB_GIZMO_XIO 0x48
|
||||
#define AHB_GIZMO_BSEV 0x60
|
||||
#define AHB_GIZMO_BSEA 0x70
|
||||
#define AHB_GIZMO_NOR 0x74
|
||||
#define AHB_GIZMO_USB2 0x78
|
||||
#define AHB_GIZMO_USB3 0x7c
|
||||
#define IMMEDIATE BIT(18)
|
||||
|
||||
#define AHB_GIZMO_SDMMC1 0x80
|
||||
#define AHB_GIZMO_SDMMC2 0x84
|
||||
#define AHB_GIZMO_SDMMC3 0x88
|
||||
#define AHB_MEM_PREFETCH_CFG_X 0xd8
|
||||
#define AHB_ARBITRATION_XBAR_CTRL 0xdc
|
||||
#define AHB_MEM_PREFETCH_CFG3 0xe0
|
||||
#define AHB_MEM_PREFETCH_CFG4 0xe4
|
||||
#define AHB_MEM_PREFETCH_CFG1 0xec
|
||||
#define AHB_MEM_PREFETCH_CFG2 0xf0
|
||||
#define PREFETCH_ENB BIT(31)
|
||||
#define MST_ID(x) (((x) & 0x1f) << 26)
|
||||
#define AHBDMA_MST_ID MST_ID(5)
|
||||
#define USB_MST_ID MST_ID(6)
|
||||
#define USB2_MST_ID MST_ID(18)
|
||||
#define USB3_MST_ID MST_ID(17)
|
||||
#define ADDR_BNDRY(x) (((x) & 0xf) << 21)
|
||||
#define INACTIVITY_TIMEOUT(x) (((x) & 0xffff) << 0)
|
||||
|
||||
#define AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID 0xf8
|
||||
|
||||
#define AHB_ARBITRATION_XBAR_CTRL_SMMU_INIT_DONE BIT(17)
|
||||
|
||||
static struct platform_driver tegra_ahb_driver;
|
||||
|
||||
static const u32 tegra_ahb_gizmo[] = {
|
||||
AHB_ARBITRATION_DISABLE,
|
||||
AHB_ARBITRATION_PRIORITY_CTRL,
|
||||
AHB_GIZMO_AHB_MEM,
|
||||
AHB_GIZMO_APB_DMA,
|
||||
AHB_GIZMO_IDE,
|
||||
AHB_GIZMO_USB,
|
||||
AHB_GIZMO_AHB_XBAR_BRIDGE,
|
||||
AHB_GIZMO_CPU_AHB_BRIDGE,
|
||||
AHB_GIZMO_COP_AHB_BRIDGE,
|
||||
AHB_GIZMO_XBAR_APB_CTLR,
|
||||
AHB_GIZMO_VCP_AHB_BRIDGE,
|
||||
AHB_GIZMO_NAND,
|
||||
AHB_GIZMO_SDMMC4,
|
||||
AHB_GIZMO_XIO,
|
||||
AHB_GIZMO_BSEV,
|
||||
AHB_GIZMO_BSEA,
|
||||
AHB_GIZMO_NOR,
|
||||
AHB_GIZMO_USB2,
|
||||
AHB_GIZMO_USB3,
|
||||
AHB_GIZMO_SDMMC1,
|
||||
AHB_GIZMO_SDMMC2,
|
||||
AHB_GIZMO_SDMMC3,
|
||||
AHB_MEM_PREFETCH_CFG_X,
|
||||
AHB_ARBITRATION_XBAR_CTRL,
|
||||
AHB_MEM_PREFETCH_CFG3,
|
||||
AHB_MEM_PREFETCH_CFG4,
|
||||
AHB_MEM_PREFETCH_CFG1,
|
||||
AHB_MEM_PREFETCH_CFG2,
|
||||
AHB_ARBITRATION_AHB_MEM_WRQUE_MST_ID,
|
||||
};
|
||||
|
||||
struct tegra_ahb {
|
||||
void __iomem *regs;
|
||||
struct device *dev;
|
||||
u32 ctx[0];
|
||||
};
|
||||
|
||||
static inline u32 gizmo_readl(struct tegra_ahb *ahb, u32 offset)
|
||||
{
|
||||
return readl(ahb->regs + offset);
|
||||
}
|
||||
|
||||
static inline void gizmo_writel(struct tegra_ahb *ahb, u32 value, u32 offset)
|
||||
{
|
||||
writel(value, ahb->regs + offset);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_3x_SOC
|
||||
static int tegra_ahb_match_by_smmu(struct device *dev, void *data)
|
||||
{
|
||||
struct tegra_ahb *ahb = dev_get_drvdata(dev);
|
||||
struct device_node *dn = data;
|
||||
|
||||
return (ahb->dev->of_node == dn) ? 1 : 0;
|
||||
}
|
||||
|
||||
int tegra_ahb_enable_smmu(struct device_node *dn)
|
||||
{
|
||||
struct device *dev;
|
||||
u32 val;
|
||||
struct tegra_ahb *ahb;
|
||||
|
||||
dev = driver_find_device(&tegra_ahb_driver.driver, NULL, dn,
|
||||
tegra_ahb_match_by_smmu);
|
||||
if (!dev)
|
||||
return -EPROBE_DEFER;
|
||||
ahb = dev_get_drvdata(dev);
|
||||
val = gizmo_readl(ahb, AHB_ARBITRATION_XBAR_CTRL);
|
||||
val |= AHB_ARBITRATION_XBAR_CTRL_SMMU_INIT_DONE;
|
||||
gizmo_writel(ahb, val, AHB_ARBITRATION_XBAR_CTRL);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(tegra_ahb_enable_smmu);
|
||||
#endif
|
||||
|
||||
static int tegra_ahb_suspend(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct tegra_ahb *ahb = dev_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra_ahb_gizmo); i++)
|
||||
ahb->ctx[i] = gizmo_readl(ahb, tegra_ahb_gizmo[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_ahb_resume(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
struct tegra_ahb *ahb = dev_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tegra_ahb_gizmo); i++)
|
||||
gizmo_writel(ahb, ahb->ctx[i], tegra_ahb_gizmo[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static UNIVERSAL_DEV_PM_OPS(tegra_ahb_pm,
|
||||
tegra_ahb_suspend,
|
||||
tegra_ahb_resume, NULL);
|
||||
|
||||
static void tegra_ahb_gizmo_init(struct tegra_ahb *ahb)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = gizmo_readl(ahb, AHB_GIZMO_AHB_MEM);
|
||||
val |= ENB_FAST_REARBITRATE | IMMEDIATE | DONT_SPLIT_AHB_WR;
|
||||
gizmo_writel(ahb, val, AHB_GIZMO_AHB_MEM);
|
||||
|
||||
val = gizmo_readl(ahb, AHB_GIZMO_USB);
|
||||
val |= IMMEDIATE;
|
||||
gizmo_writel(ahb, val, AHB_GIZMO_USB);
|
||||
|
||||
val = gizmo_readl(ahb, AHB_GIZMO_USB2);
|
||||
val |= IMMEDIATE;
|
||||
gizmo_writel(ahb, val, AHB_GIZMO_USB2);
|
||||
|
||||
val = gizmo_readl(ahb, AHB_GIZMO_USB3);
|
||||
val |= IMMEDIATE;
|
||||
gizmo_writel(ahb, val, AHB_GIZMO_USB3);
|
||||
|
||||
val = gizmo_readl(ahb, AHB_ARBITRATION_PRIORITY_CTRL);
|
||||
val |= PRIORITY_SELECT_USB |
|
||||
PRIORITY_SELECT_USB2 |
|
||||
PRIORITY_SELECT_USB3 |
|
||||
AHB_PRIORITY_WEIGHT(7);
|
||||
gizmo_writel(ahb, val, AHB_ARBITRATION_PRIORITY_CTRL);
|
||||
|
||||
val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG1);
|
||||
val &= ~MST_ID(~0);
|
||||
val |= PREFETCH_ENB |
|
||||
AHBDMA_MST_ID |
|
||||
ADDR_BNDRY(0xc) |
|
||||
INACTIVITY_TIMEOUT(0x1000);
|
||||
gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG1);
|
||||
|
||||
val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG2);
|
||||
val &= ~MST_ID(~0);
|
||||
val |= PREFETCH_ENB |
|
||||
USB_MST_ID |
|
||||
ADDR_BNDRY(0xc) |
|
||||
INACTIVITY_TIMEOUT(0x1000);
|
||||
gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG2);
|
||||
|
||||
val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG3);
|
||||
val &= ~MST_ID(~0);
|
||||
val |= PREFETCH_ENB |
|
||||
USB3_MST_ID |
|
||||
ADDR_BNDRY(0xc) |
|
||||
INACTIVITY_TIMEOUT(0x1000);
|
||||
gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG3);
|
||||
|
||||
val = gizmo_readl(ahb, AHB_MEM_PREFETCH_CFG4);
|
||||
val &= ~MST_ID(~0);
|
||||
val |= PREFETCH_ENB |
|
||||
USB2_MST_ID |
|
||||
ADDR_BNDRY(0xc) |
|
||||
INACTIVITY_TIMEOUT(0x1000);
|
||||
gizmo_writel(ahb, val, AHB_MEM_PREFETCH_CFG4);
|
||||
}
|
||||
|
||||
static int __devinit tegra_ahb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct tegra_ahb *ahb;
|
||||
size_t bytes;
|
||||
|
||||
bytes = sizeof(*ahb) + sizeof(u32) * ARRAY_SIZE(tegra_ahb_gizmo);
|
||||
ahb = devm_kzalloc(&pdev->dev, bytes, GFP_KERNEL);
|
||||
if (!ahb)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
ahb->regs = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!ahb->regs)
|
||||
return -EBUSY;
|
||||
|
||||
ahb->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, ahb);
|
||||
tegra_ahb_gizmo_init(ahb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit tegra_ahb_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id tegra_ahb_of_match[] __devinitconst = {
|
||||
{ .compatible = "nvidia,tegra30-ahb", },
|
||||
{ .compatible = "nvidia,tegra20-ahb", },
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_ahb_driver = {
|
||||
.probe = tegra_ahb_probe,
|
||||
.remove = __devexit_p(tegra_ahb_remove),
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = tegra_ahb_of_match,
|
||||
.pm = &tegra_ahb_pm,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra_ahb_driver);
|
||||
|
||||
MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra AHB driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
@@ -416,6 +416,15 @@ config PATA_EFAR
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config PATA_EP93XX
|
||||
tristate "Cirrus Logic EP93xx PATA support"
|
||||
depends on ARCH_EP93XX
|
||||
help
|
||||
This option enables support for the PATA controller in
|
||||
the Cirrus Logic EP9312 and EP9315 ARM CPU.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config PATA_HPT366
|
||||
tristate "HPT 366/368 PATA support"
|
||||
depends on PCI
|
||||
|
||||
@@ -43,6 +43,7 @@ obj-$(CONFIG_PATA_CS5535) += pata_cs5535.o
|
||||
obj-$(CONFIG_PATA_CS5536) += pata_cs5536.o
|
||||
obj-$(CONFIG_PATA_CYPRESS) += pata_cypress.o
|
||||
obj-$(CONFIG_PATA_EFAR) += pata_efar.o
|
||||
obj-$(CONFIG_PATA_EP93XX) += pata_ep93xx.o
|
||||
obj-$(CONFIG_PATA_HPT366) += pata_hpt366.o
|
||||
obj-$(CONFIG_PATA_HPT37X) += pata_hpt37x.o
|
||||
obj-$(CONFIG_PATA_HPT3X2N) += pata_hpt3x2n.o
|
||||
|
||||
@@ -177,7 +177,7 @@ static int ata_generic_init_one(struct pci_dev *dev, const struct pci_device_id
|
||||
if ((id->driver_data & ATA_GEN_CLASS_MATCH) && all_generic_ide == 0)
|
||||
return -ENODEV;
|
||||
|
||||
if (id->driver_data & ATA_GEN_INTEL_IDER)
|
||||
if ((id->driver_data & ATA_GEN_INTEL_IDER) && !all_generic_ide)
|
||||
if (!is_intel_ider(dev))
|
||||
return -ENODEV;
|
||||
|
||||
|
||||
@@ -1554,6 +1554,39 @@ static bool piix_broken_system_poweroff(struct pci_dev *pdev)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int prefer_ms_hyperv = 1;
|
||||
module_param(prefer_ms_hyperv, int, 0);
|
||||
|
||||
static void piix_ignore_devices_quirk(struct ata_host *host)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_HYPERV_STORAGE)
|
||||
static const struct dmi_system_id ignore_hyperv[] = {
|
||||
{
|
||||
/* On Hyper-V hypervisors the disks are exposed on
|
||||
* both the emulated SATA controller and on the
|
||||
* paravirtualised drivers. The CD/DVD devices
|
||||
* are only exposed on the emulated controller.
|
||||
* Request we ignore ATA devices on this host.
|
||||
*/
|
||||
.ident = "Hyper-V Virtual Machine",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"Microsoft Corporation"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
|
||||
},
|
||||
},
|
||||
{ } /* terminate list */
|
||||
};
|
||||
const struct dmi_system_id *dmi = dmi_first_match(ignore_hyperv);
|
||||
|
||||
if (dmi && prefer_ms_hyperv) {
|
||||
host->flags |= ATA_HOST_IGNORE_ATA;
|
||||
dev_info(host->dev, "%s detected, ATA device ignore set\n",
|
||||
dmi->ident);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* piix_init_one - Register PIIX ATA PCI device with kernel services
|
||||
* @pdev: PCI device to register
|
||||
@@ -1669,6 +1702,9 @@ static int __devinit piix_init_one(struct pci_dev *pdev,
|
||||
}
|
||||
host->flags |= ATA_HOST_PARALLEL_SCAN;
|
||||
|
||||
/* Allow hosts to specify device types to ignore when scanning. */
|
||||
piix_ignore_devices_quirk(host);
|
||||
|
||||
pci_set_master(pdev);
|
||||
return ata_pci_sff_activate_host(host, ata_bmdma_interrupt, sht);
|
||||
}
|
||||
|
||||
@@ -1973,6 +1973,12 @@ retry:
|
||||
if (class == ATA_DEV_ATA) {
|
||||
if (!ata_id_is_ata(id) && !ata_id_is_cfa(id))
|
||||
goto err_out;
|
||||
if (ap->host->flags & ATA_HOST_IGNORE_ATA &&
|
||||
ata_id_is_ata(id)) {
|
||||
ata_dev_dbg(dev,
|
||||
"host indicates ignore ATA devices, ignored\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
} else {
|
||||
if (ata_id_is_ata(id))
|
||||
goto err_out;
|
||||
@@ -4051,6 +4057,7 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = {
|
||||
{ "_NEC DV5800A", NULL, ATA_HORKAGE_NODMA },
|
||||
{ "SAMSUNG CD-ROM SN-124", "N001", ATA_HORKAGE_NODMA },
|
||||
{ "Seagate STT20000A", NULL, ATA_HORKAGE_NODMA },
|
||||
{ "2GB ATA Flash Disk", "ADMA428M", ATA_HORKAGE_NODMA },
|
||||
/* Odd clown on sil3726/4726 PMPs */
|
||||
{ "Config Disk", NULL, ATA_HORKAGE_DISABLE },
|
||||
|
||||
|
||||
+21
-3
@@ -2046,6 +2046,26 @@ static unsigned int ata_eh_speed_down(struct ata_device *dev,
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* ata_eh_worth_retry - analyze error and decide whether to retry
|
||||
* @qc: qc to possibly retry
|
||||
*
|
||||
* Look at the cause of the error and decide if a retry
|
||||
* might be useful or not. We don't want to retry media errors
|
||||
* because the drive itself has probably already taken 10-30 seconds
|
||||
* doing its own internal retries before reporting the failure.
|
||||
*/
|
||||
static inline int ata_eh_worth_retry(struct ata_queued_cmd *qc)
|
||||
{
|
||||
if (qc->flags & AC_ERR_MEDIA)
|
||||
return 0; /* don't retry media errors */
|
||||
if (qc->flags & ATA_QCFLAG_IO)
|
||||
return 1; /* otherwise retry anything from fs stack */
|
||||
if (qc->err_mask & AC_ERR_INVALID)
|
||||
return 0; /* don't retry these */
|
||||
return qc->err_mask != AC_ERR_DEV; /* retry if not dev error */
|
||||
}
|
||||
|
||||
/**
|
||||
* ata_eh_link_autopsy - analyze error and determine recovery action
|
||||
* @link: host link to perform autopsy on
|
||||
@@ -2120,9 +2140,7 @@ static void ata_eh_link_autopsy(struct ata_link *link)
|
||||
qc->err_mask &= ~(AC_ERR_DEV | AC_ERR_OTHER);
|
||||
|
||||
/* determine whether the command is worth retrying */
|
||||
if (qc->flags & ATA_QCFLAG_IO ||
|
||||
(!(qc->err_mask & AC_ERR_INVALID) &&
|
||||
qc->err_mask != AC_ERR_DEV))
|
||||
if (ata_eh_worth_retry(qc))
|
||||
qc->flags |= ATA_QCFLAG_RETRY;
|
||||
|
||||
/* accumulate error info */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+36
-4
@@ -553,6 +553,7 @@ struct mv_host_priv {
|
||||
|
||||
#if defined(CONFIG_HAVE_CLK)
|
||||
struct clk *clk;
|
||||
struct clk **port_clks;
|
||||
#endif
|
||||
/*
|
||||
* These consistent DMA memory pools give us guaranteed
|
||||
@@ -4027,6 +4028,9 @@ static int mv_platform_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
int n_ports = 0;
|
||||
int rc;
|
||||
#if defined(CONFIG_HAVE_CLK)
|
||||
int port;
|
||||
#endif
|
||||
|
||||
ata_print_version_once(&pdev->dev, DRV_VERSION);
|
||||
|
||||
@@ -4054,6 +4058,13 @@ static int mv_platform_probe(struct platform_device *pdev)
|
||||
|
||||
if (!host || !hpriv)
|
||||
return -ENOMEM;
|
||||
#if defined(CONFIG_HAVE_CLK)
|
||||
hpriv->port_clks = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct clk *) * n_ports,
|
||||
GFP_KERNEL);
|
||||
if (!hpriv->port_clks)
|
||||
return -ENOMEM;
|
||||
#endif
|
||||
host->private_data = hpriv;
|
||||
hpriv->n_ports = n_ports;
|
||||
hpriv->board_idx = chip_soc;
|
||||
@@ -4066,9 +4077,17 @@ static int mv_platform_probe(struct platform_device *pdev)
|
||||
#if defined(CONFIG_HAVE_CLK)
|
||||
hpriv->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(hpriv->clk))
|
||||
dev_notice(&pdev->dev, "cannot get clkdev\n");
|
||||
dev_notice(&pdev->dev, "cannot get optional clkdev\n");
|
||||
else
|
||||
clk_enable(hpriv->clk);
|
||||
clk_prepare_enable(hpriv->clk);
|
||||
|
||||
for (port = 0; port < n_ports; port++) {
|
||||
char port_number[16];
|
||||
sprintf(port_number, "%d", port);
|
||||
hpriv->port_clks[port] = clk_get(&pdev->dev, port_number);
|
||||
if (!IS_ERR(hpriv->port_clks[port]))
|
||||
clk_prepare_enable(hpriv->port_clks[port]);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
@@ -4098,9 +4117,15 @@ static int mv_platform_probe(struct platform_device *pdev)
|
||||
err:
|
||||
#if defined(CONFIG_HAVE_CLK)
|
||||
if (!IS_ERR(hpriv->clk)) {
|
||||
clk_disable(hpriv->clk);
|
||||
clk_disable_unprepare(hpriv->clk);
|
||||
clk_put(hpriv->clk);
|
||||
}
|
||||
for (port = 0; port < n_ports; port++) {
|
||||
if (!IS_ERR(hpriv->port_clks[port])) {
|
||||
clk_disable_unprepare(hpriv->port_clks[port]);
|
||||
clk_put(hpriv->port_clks[port]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
@@ -4119,14 +4144,21 @@ static int __devexit mv_platform_remove(struct platform_device *pdev)
|
||||
struct ata_host *host = platform_get_drvdata(pdev);
|
||||
#if defined(CONFIG_HAVE_CLK)
|
||||
struct mv_host_priv *hpriv = host->private_data;
|
||||
int port;
|
||||
#endif
|
||||
ata_host_detach(host);
|
||||
|
||||
#if defined(CONFIG_HAVE_CLK)
|
||||
if (!IS_ERR(hpriv->clk)) {
|
||||
clk_disable(hpriv->clk);
|
||||
clk_disable_unprepare(hpriv->clk);
|
||||
clk_put(hpriv->clk);
|
||||
}
|
||||
for (port = 0; port < host->n_ports; port++) {
|
||||
if (!IS_ERR(hpriv->port_clks[port])) {
|
||||
clk_disable_unprepare(hpriv->port_clks[port]);
|
||||
clk_put(hpriv->port_clks[port]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -802,7 +802,7 @@ static void fill_rx_pool (amb_dev * dev, unsigned char pool,
|
||||
}
|
||||
// cast needed as there is no %? for pointer differences
|
||||
PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li",
|
||||
skb, skb->head, (long) (skb_end_pointer(skb) - skb->head));
|
||||
skb, skb->head, (long) skb_end_offset(skb));
|
||||
rx.handle = virt_to_bus (skb);
|
||||
rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
|
||||
if (rx_give (dev, &rx, pool))
|
||||
|
||||
@@ -2182,7 +2182,6 @@ static int hrz_open (struct atm_vcc *atm_vcc)
|
||||
default:
|
||||
PRINTD (DBG_QOS|DBG_VCC, "Bad AAL!");
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
// TX traffic parameters
|
||||
@@ -2357,7 +2356,6 @@ static int hrz_open (struct atm_vcc *atm_vcc)
|
||||
default: {
|
||||
PRINTD (DBG_QOS, "unsupported TX traffic class");
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2433,7 +2431,6 @@ static int hrz_open (struct atm_vcc *atm_vcc)
|
||||
default: {
|
||||
PRINTD (DBG_QOS, "unsupported RX traffic class");
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2581,7 +2578,6 @@ static int hrz_getsockopt (struct atm_vcc * atm_vcc, int level, int optname,
|
||||
// break;
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
break;
|
||||
};
|
||||
break;
|
||||
}
|
||||
@@ -2601,7 +2597,6 @@ static int hrz_setsockopt (struct atm_vcc * atm_vcc, int level, int optname,
|
||||
// break;
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
break;
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1258,7 +1258,7 @@ idt77252_rx_raw(struct idt77252_dev *card)
|
||||
tail = readl(SAR_REG_RAWCT);
|
||||
|
||||
pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(queue),
|
||||
skb_end_pointer(queue) - queue->head - 16,
|
||||
skb_end_offset(queue) - 16,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
while (head != tail) {
|
||||
|
||||
@@ -984,6 +984,7 @@ static uint32_t fpga_tx(struct solos_card *card)
|
||||
} else if (skb && card->using_dma) {
|
||||
SKB_CB(skb)->dma_addr = pci_map_single(card->dev, skb->data,
|
||||
skb->len, PCI_DMA_TODEVICE);
|
||||
card->tx_skb[port] = skb;
|
||||
iowrite32(SKB_CB(skb)->dma_addr,
|
||||
card->config_regs + TX_DMA_ADDR(port));
|
||||
}
|
||||
@@ -1152,7 +1153,8 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
db_fpga_upgrade = db_firmware_upgrade = 0;
|
||||
}
|
||||
|
||||
if (card->fpga_version >= DMA_SUPPORTED){
|
||||
if (card->fpga_version >= DMA_SUPPORTED) {
|
||||
pci_set_master(dev);
|
||||
card->using_dma = 1;
|
||||
} else {
|
||||
card->using_dma = 0;
|
||||
|
||||
@@ -192,4 +192,93 @@ config DMA_SHARED_BUFFER
|
||||
APIs extension; the file's descriptor can then be passed on to other
|
||||
driver.
|
||||
|
||||
config CMA
|
||||
bool "Contiguous Memory Allocator (EXPERIMENTAL)"
|
||||
depends on HAVE_DMA_CONTIGUOUS && HAVE_MEMBLOCK && EXPERIMENTAL
|
||||
select MIGRATION
|
||||
help
|
||||
This enables the Contiguous Memory Allocator which allows drivers
|
||||
to allocate big physically-contiguous blocks of memory for use with
|
||||
hardware components that do not support I/O map nor scatter-gather.
|
||||
|
||||
For more information see <include/linux/dma-contiguous.h>.
|
||||
If unsure, say "n".
|
||||
|
||||
if CMA
|
||||
|
||||
config CMA_DEBUG
|
||||
bool "CMA debug messages (DEVELOPMENT)"
|
||||
depends on DEBUG_KERNEL
|
||||
help
|
||||
Turns on debug messages in CMA. This produces KERN_DEBUG
|
||||
messages for every CMA call as well as various messages while
|
||||
processing calls such as dma_alloc_from_contiguous().
|
||||
This option does not affect warning and error messages.
|
||||
|
||||
comment "Default contiguous memory area size:"
|
||||
|
||||
config CMA_SIZE_MBYTES
|
||||
int "Size in Mega Bytes"
|
||||
depends on !CMA_SIZE_SEL_PERCENTAGE
|
||||
default 16
|
||||
help
|
||||
Defines the size (in MiB) of the default memory area for Contiguous
|
||||
Memory Allocator.
|
||||
|
||||
config CMA_SIZE_PERCENTAGE
|
||||
int "Percentage of total memory"
|
||||
depends on !CMA_SIZE_SEL_MBYTES
|
||||
default 10
|
||||
help
|
||||
Defines the size of the default memory area for Contiguous Memory
|
||||
Allocator as a percentage of the total memory in the system.
|
||||
|
||||
choice
|
||||
prompt "Selected region size"
|
||||
default CMA_SIZE_SEL_ABSOLUTE
|
||||
|
||||
config CMA_SIZE_SEL_MBYTES
|
||||
bool "Use mega bytes value only"
|
||||
|
||||
config CMA_SIZE_SEL_PERCENTAGE
|
||||
bool "Use percentage value only"
|
||||
|
||||
config CMA_SIZE_SEL_MIN
|
||||
bool "Use lower value (minimum)"
|
||||
|
||||
config CMA_SIZE_SEL_MAX
|
||||
bool "Use higher value (maximum)"
|
||||
|
||||
endchoice
|
||||
|
||||
config CMA_ALIGNMENT
|
||||
int "Maximum PAGE_SIZE order of alignment for contiguous buffers"
|
||||
range 4 9
|
||||
default 8
|
||||
help
|
||||
DMA mapping framework by default aligns all buffers to the smallest
|
||||
PAGE_SIZE order which is greater than or equal to the requested buffer
|
||||
size. This works well for buffers up to a few hundreds kilobytes, but
|
||||
for larger buffers it just a memory waste. With this parameter you can
|
||||
specify the maximum PAGE_SIZE order for contiguous buffers. Larger
|
||||
buffers will be aligned only to this specified order. The order is
|
||||
expressed as a power of two multiplied by the PAGE_SIZE.
|
||||
|
||||
For example, if your system defaults to 4KiB pages, the order value
|
||||
of 8 means that the buffers will be aligned up to 1MiB only.
|
||||
|
||||
If unsure, leave the default value "8".
|
||||
|
||||
config CMA_AREAS
|
||||
int "Maximum count of the CMA device-private areas"
|
||||
default 7
|
||||
help
|
||||
CMA allows to create CMA areas for particular devices. This parameter
|
||||
sets the maximum number of such device private CMA areas in the
|
||||
system.
|
||||
|
||||
If unsure, leave the default value "7".
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -6,6 +6,7 @@ obj-y := core.o bus.o dd.o syscore.o \
|
||||
attribute_container.o transport_class.o \
|
||||
topology.o
|
||||
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
|
||||
obj-$(CONFIG_CMA) += dma-contiguous.o
|
||||
obj-y += power/
|
||||
obj-$(CONFIG_HAS_DMA) += dma-mapping.o
|
||||
obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
|
||||
|
||||
+1
-2
@@ -21,8 +21,7 @@
|
||||
#include "power/power.h"
|
||||
|
||||
/* /sys/devices/system */
|
||||
/* FIXME: make static after drivers/base/sys.c is deleted */
|
||||
struct kset *system_kset;
|
||||
static struct kset *system_kset;
|
||||
|
||||
#define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
|
||||
|
||||
|
||||
+52
-6
@@ -25,6 +25,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/async.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
#include "base.h"
|
||||
#include "power/power.h"
|
||||
@@ -65,7 +66,7 @@ static inline int device_is_not_partition(struct device *dev)
|
||||
* @dev: struct device to get the name of
|
||||
*
|
||||
* Will return the device's driver's name if it is bound to a device. If
|
||||
* the device is not bound to a device, it will return the name of the bus
|
||||
* the device is not bound to a driver, it will return the name of the bus
|
||||
* it is attached to. If it is not attached to a bus either, an empty
|
||||
* string will be returned.
|
||||
*/
|
||||
@@ -878,8 +879,8 @@ EXPORT_SYMBOL_GPL(dev_set_name);
|
||||
* to NULL prevents an entry from being created. class->dev_kobj must
|
||||
* be set (or cleared) before any devices are registered to the class
|
||||
* otherwise device_create_sys_dev_entry() and
|
||||
* device_remove_sys_dev_entry() will disagree about the the presence
|
||||
* of the link.
|
||||
* device_remove_sys_dev_entry() will disagree about the presence of
|
||||
* the link.
|
||||
*/
|
||||
static struct kobject *device_to_dev_kobj(struct device *dev)
|
||||
{
|
||||
@@ -1843,15 +1844,60 @@ void device_shutdown(void)
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_PRINTK
|
||||
|
||||
int __dev_printk(const char *level, const struct device *dev,
|
||||
struct va_format *vaf)
|
||||
{
|
||||
char dict[128];
|
||||
size_t dictlen = 0;
|
||||
const char *subsys;
|
||||
|
||||
if (!dev)
|
||||
return printk("%s(NULL device *): %pV", level, vaf);
|
||||
|
||||
return printk("%s%s %s: %pV",
|
||||
level, dev_driver_string(dev), dev_name(dev), vaf);
|
||||
if (dev->class)
|
||||
subsys = dev->class->name;
|
||||
else if (dev->bus)
|
||||
subsys = dev->bus->name;
|
||||
else
|
||||
goto skip;
|
||||
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"SUBSYSTEM=%s", subsys);
|
||||
|
||||
/*
|
||||
* Add device identifier DEVICE=:
|
||||
* b12:8 block dev_t
|
||||
* c127:3 char dev_t
|
||||
* n8 netdev ifindex
|
||||
* +sound:card0 subsystem:devname
|
||||
*/
|
||||
if (MAJOR(dev->devt)) {
|
||||
char c;
|
||||
|
||||
if (strcmp(subsys, "block") == 0)
|
||||
c = 'b';
|
||||
else
|
||||
c = 'c';
|
||||
dictlen++;
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"DEVICE=%c%u:%u",
|
||||
c, MAJOR(dev->devt), MINOR(dev->devt));
|
||||
} else if (strcmp(subsys, "net") == 0) {
|
||||
struct net_device *net = to_net_dev(dev);
|
||||
|
||||
dictlen++;
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"DEVICE=n%u", net->ifindex);
|
||||
} else {
|
||||
dictlen++;
|
||||
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
|
||||
"DEVICE=+%s:%s", subsys, dev_name(dev));
|
||||
}
|
||||
skip:
|
||||
return printk_emit(0, level[1] - '0',
|
||||
dictlen ? dict : NULL, dictlen,
|
||||
"%s %s: %pV",
|
||||
dev_driver_string(dev), dev_name(dev), vaf);
|
||||
}
|
||||
EXPORT_SYMBOL(__dev_printk);
|
||||
|
||||
|
||||
@@ -330,8 +330,4 @@ void __init cpu_dev_init(void)
|
||||
panic("Failed to register CPU subsystem");
|
||||
|
||||
cpu_dev_register_generic();
|
||||
|
||||
#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
|
||||
sched_create_sysfs_power_savings_entries(cpu_subsys.dev_root);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -309,6 +309,10 @@ EXPORT_SYMBOL_GPL(devres_remove);
|
||||
* which @match returns 1. If @match is NULL, it's considered to
|
||||
* match all. If found, the resource is removed atomically and freed.
|
||||
*
|
||||
* Note that the release function for the resource will not be called,
|
||||
* only the devres-allocated data will be freed. The caller becomes
|
||||
* responsible for freeing any other data.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 if devres is found and freed, -ENOENT if not found.
|
||||
*/
|
||||
@@ -326,6 +330,37 @@ int devres_destroy(struct device *dev, dr_release_t release,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devres_destroy);
|
||||
|
||||
|
||||
/**
|
||||
* devres_release - Find a device resource and destroy it, calling release
|
||||
* @dev: Device to find resource from
|
||||
* @release: Look for resources associated with this release function
|
||||
* @match: Match function (optional)
|
||||
* @match_data: Data for the match function
|
||||
*
|
||||
* Find the latest devres of @dev associated with @release and for
|
||||
* which @match returns 1. If @match is NULL, it's considered to
|
||||
* match all. If found, the resource is removed atomically, the
|
||||
* release function called and the resource freed.
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 if devres is found and freed, -ENOENT if not found.
|
||||
*/
|
||||
int devres_release(struct device *dev, dr_release_t release,
|
||||
dr_match_t match, void *match_data)
|
||||
{
|
||||
void *res;
|
||||
|
||||
res = devres_remove(dev, release, match, match_data);
|
||||
if (unlikely(!res))
|
||||
return -ENOENT;
|
||||
|
||||
(*release)(dev, res);
|
||||
devres_free(res);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devres_release);
|
||||
|
||||
static int remove_nodes(struct device *dev,
|
||||
struct list_head *first, struct list_head *end,
|
||||
struct list_head *todo)
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
* devtmpfs, a tmpfs-based filesystem is created. Every driver-core
|
||||
* device which requests a device node, will add a node in this
|
||||
* filesystem.
|
||||
* By default, all devices are named after the the name of the
|
||||
* device, owned by root and have a default mode of 0600. Subsystems
|
||||
* can overwrite the default setting if needed.
|
||||
* By default, all devices are named after the name of the device,
|
||||
* owned by root and have a default mode of 0600. Subsystems can
|
||||
* overwrite the default setting if needed.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
+104
-7
@@ -44,8 +44,26 @@ static int dma_buf_release(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dma_buf_mmap_internal(struct file *file, struct vm_area_struct *vma)
|
||||
{
|
||||
struct dma_buf *dmabuf;
|
||||
|
||||
if (!is_dma_buf_file(file))
|
||||
return -EINVAL;
|
||||
|
||||
dmabuf = file->private_data;
|
||||
|
||||
/* check for overflowing the buffer's size */
|
||||
if (vma->vm_pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) >
|
||||
dmabuf->size >> PAGE_SHIFT)
|
||||
return -EINVAL;
|
||||
|
||||
return dmabuf->ops->mmap(dmabuf, vma);
|
||||
}
|
||||
|
||||
static const struct file_operations dma_buf_fops = {
|
||||
.release = dma_buf_release,
|
||||
.mmap = dma_buf_mmap_internal,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -82,7 +100,8 @@ struct dma_buf *dma_buf_export(void *priv, const struct dma_buf_ops *ops,
|
||||
|| !ops->unmap_dma_buf
|
||||
|| !ops->release
|
||||
|| !ops->kmap_atomic
|
||||
|| !ops->kmap)) {
|
||||
|| !ops->kmap
|
||||
|| !ops->mmap)) {
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
@@ -293,7 +312,7 @@ EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment);
|
||||
* cpu in the kernel context. Calls begin_cpu_access to allow exporter-specific
|
||||
* preparations. Coherency is only guaranteed in the specified range for the
|
||||
* specified access direction.
|
||||
* @dma_buf: [in] buffer to prepare cpu access for.
|
||||
* @dmabuf: [in] buffer to prepare cpu access for.
|
||||
* @start: [in] start of range for cpu access.
|
||||
* @len: [in] length of range for cpu access.
|
||||
* @direction: [in] length of range for cpu access.
|
||||
@@ -320,7 +339,7 @@ EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access);
|
||||
* cpu in the kernel context. Calls end_cpu_access to allow exporter-specific
|
||||
* actions. Coherency is only guaranteed in the specified range for the
|
||||
* specified access direction.
|
||||
* @dma_buf: [in] buffer to complete cpu access for.
|
||||
* @dmabuf: [in] buffer to complete cpu access for.
|
||||
* @start: [in] start of range for cpu access.
|
||||
* @len: [in] length of range for cpu access.
|
||||
* @direction: [in] length of range for cpu access.
|
||||
@@ -340,7 +359,7 @@ EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access);
|
||||
/**
|
||||
* dma_buf_kmap_atomic - Map a page of the buffer object into kernel address
|
||||
* space. The same restrictions as for kmap_atomic and friends apply.
|
||||
* @dma_buf: [in] buffer to map page from.
|
||||
* @dmabuf: [in] buffer to map page from.
|
||||
* @page_num: [in] page in PAGE_SIZE units to map.
|
||||
*
|
||||
* This call must always succeed, any necessary preparations that might fail
|
||||
@@ -356,7 +375,7 @@ EXPORT_SYMBOL_GPL(dma_buf_kmap_atomic);
|
||||
|
||||
/**
|
||||
* dma_buf_kunmap_atomic - Unmap a page obtained by dma_buf_kmap_atomic.
|
||||
* @dma_buf: [in] buffer to unmap page from.
|
||||
* @dmabuf: [in] buffer to unmap page from.
|
||||
* @page_num: [in] page in PAGE_SIZE units to unmap.
|
||||
* @vaddr: [in] kernel space pointer obtained from dma_buf_kmap_atomic.
|
||||
*
|
||||
@@ -375,7 +394,7 @@ EXPORT_SYMBOL_GPL(dma_buf_kunmap_atomic);
|
||||
/**
|
||||
* dma_buf_kmap - Map a page of the buffer object into kernel address space. The
|
||||
* same restrictions as for kmap and friends apply.
|
||||
* @dma_buf: [in] buffer to map page from.
|
||||
* @dmabuf: [in] buffer to map page from.
|
||||
* @page_num: [in] page in PAGE_SIZE units to map.
|
||||
*
|
||||
* This call must always succeed, any necessary preparations that might fail
|
||||
@@ -391,7 +410,7 @@ EXPORT_SYMBOL_GPL(dma_buf_kmap);
|
||||
|
||||
/**
|
||||
* dma_buf_kunmap - Unmap a page obtained by dma_buf_kmap.
|
||||
* @dma_buf: [in] buffer to unmap page from.
|
||||
* @dmabuf: [in] buffer to unmap page from.
|
||||
* @page_num: [in] page in PAGE_SIZE units to unmap.
|
||||
* @vaddr: [in] kernel space pointer obtained from dma_buf_kmap.
|
||||
*
|
||||
@@ -406,3 +425,81 @@ void dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long page_num,
|
||||
dmabuf->ops->kunmap(dmabuf, page_num, vaddr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_kunmap);
|
||||
|
||||
|
||||
/**
|
||||
* dma_buf_mmap - Setup up a userspace mmap with the given vma
|
||||
* @dmabuf: [in] buffer that should back the vma
|
||||
* @vma: [in] vma for the mmap
|
||||
* @pgoff: [in] offset in pages where this mmap should start within the
|
||||
* dma-buf buffer.
|
||||
*
|
||||
* This function adjusts the passed in vma so that it points at the file of the
|
||||
* dma_buf operation. It alsog adjusts the starting pgoff and does bounds
|
||||
* checking on the size of the vma. Then it calls the exporters mmap function to
|
||||
* set up the mapping.
|
||||
*
|
||||
* Can return negative error values, returns 0 on success.
|
||||
*/
|
||||
int dma_buf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma,
|
||||
unsigned long pgoff)
|
||||
{
|
||||
if (WARN_ON(!dmabuf || !vma))
|
||||
return -EINVAL;
|
||||
|
||||
/* check for offset overflow */
|
||||
if (pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) < pgoff)
|
||||
return -EOVERFLOW;
|
||||
|
||||
/* check for overflowing the buffer's size */
|
||||
if (pgoff + ((vma->vm_end - vma->vm_start) >> PAGE_SHIFT) >
|
||||
dmabuf->size >> PAGE_SHIFT)
|
||||
return -EINVAL;
|
||||
|
||||
/* readjust the vma */
|
||||
if (vma->vm_file)
|
||||
fput(vma->vm_file);
|
||||
|
||||
vma->vm_file = dmabuf->file;
|
||||
get_file(vma->vm_file);
|
||||
|
||||
vma->vm_pgoff = pgoff;
|
||||
|
||||
return dmabuf->ops->mmap(dmabuf, vma);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_mmap);
|
||||
|
||||
/**
|
||||
* dma_buf_vmap - Create virtual mapping for the buffer object into kernel
|
||||
* address space. Same restrictions as for vmap and friends apply.
|
||||
* @dmabuf: [in] buffer to vmap
|
||||
*
|
||||
* This call may fail due to lack of virtual mapping address space.
|
||||
* These calls are optional in drivers. The intended use for them
|
||||
* is for mapping objects linear in kernel space for high use objects.
|
||||
* Please attempt to use kmap/kunmap before thinking about these interfaces.
|
||||
*/
|
||||
void *dma_buf_vmap(struct dma_buf *dmabuf)
|
||||
{
|
||||
if (WARN_ON(!dmabuf))
|
||||
return NULL;
|
||||
|
||||
if (dmabuf->ops->vmap)
|
||||
return dmabuf->ops->vmap(dmabuf);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_vmap);
|
||||
|
||||
/**
|
||||
* dma_buf_vunmap - Unmap a vmap obtained by dma_buf_vmap.
|
||||
* @dmabuf: [in] buffer to vunmap
|
||||
*/
|
||||
void dma_buf_vunmap(struct dma_buf *dmabuf, void *vaddr)
|
||||
{
|
||||
if (WARN_ON(!dmabuf))
|
||||
return;
|
||||
|
||||
if (dmabuf->ops->vunmap)
|
||||
dmabuf->ops->vunmap(dmabuf, vaddr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_buf_vunmap);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
struct dma_coherent_mem {
|
||||
void *virt_base;
|
||||
dma_addr_t device_base;
|
||||
phys_addr_t pfn_base;
|
||||
int size;
|
||||
int flags;
|
||||
unsigned long *bitmap;
|
||||
@@ -44,6 +45,7 @@ int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr,
|
||||
|
||||
dev->dma_mem->virt_base = mem_base;
|
||||
dev->dma_mem->device_base = device_addr;
|
||||
dev->dma_mem->pfn_base = PFN_DOWN(bus_addr);
|
||||
dev->dma_mem->size = pages;
|
||||
dev->dma_mem->flags = flags;
|
||||
|
||||
@@ -176,3 +178,43 @@ int dma_release_from_coherent(struct device *dev, int order, void *vaddr)
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_release_from_coherent);
|
||||
|
||||
/**
|
||||
* dma_mmap_from_coherent() - try to mmap the memory allocated from
|
||||
* per-device coherent memory pool to userspace
|
||||
* @dev: device from which the memory was allocated
|
||||
* @vma: vm_area for the userspace memory
|
||||
* @vaddr: cpu address returned by dma_alloc_from_coherent
|
||||
* @size: size of the memory buffer allocated by dma_alloc_from_coherent
|
||||
*
|
||||
* This checks whether the memory was allocated from the per-device
|
||||
* coherent memory pool and if so, maps that memory to the provided vma.
|
||||
*
|
||||
* Returns 1 if we correctly mapped the memory, or 0 if
|
||||
* dma_release_coherent() should proceed with mapping memory from
|
||||
* generic pools.
|
||||
*/
|
||||
int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma,
|
||||
void *vaddr, size_t size, int *ret)
|
||||
{
|
||||
struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
|
||||
|
||||
if (mem && vaddr >= mem->virt_base && vaddr + size <=
|
||||
(mem->virt_base + (mem->size << PAGE_SHIFT))) {
|
||||
unsigned long off = vma->vm_pgoff;
|
||||
int start = (vaddr - mem->virt_base) >> PAGE_SHIFT;
|
||||
int user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
|
||||
int count = size >> PAGE_SHIFT;
|
||||
|
||||
*ret = -ENXIO;
|
||||
if (off < count && user_count <= count - off) {
|
||||
unsigned pfn = mem->pfn_base + start + off;
|
||||
*ret = remap_pfn_range(vma, vma->vm_start, pfn,
|
||||
user_count << PAGE_SHIFT,
|
||||
vma->vm_page_prot);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_mmap_from_coherent);
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Contiguous Memory Allocator for DMA mapping framework
|
||||
* Copyright (c) 2010-2011 by Samsung Electronics.
|
||||
* Written by:
|
||||
* Marek Szyprowski <m.szyprowski@samsung.com>
|
||||
* Michal Nazarewicz <mina86@mina86.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 optional) any later version of the license.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "cma: " fmt
|
||||
|
||||
#ifdef CONFIG_CMA_DEBUG
|
||||
#ifndef DEBUG
|
||||
# define DEBUG
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/dma-contiguous.h>
|
||||
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/page-isolation.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
|
||||
#ifndef SZ_1M
|
||||
#define SZ_1M (1 << 20)
|
||||
#endif
|
||||
|
||||
struct cma {
|
||||
unsigned long base_pfn;
|
||||
unsigned long count;
|
||||
unsigned long *bitmap;
|
||||
};
|
||||
|
||||
struct cma *dma_contiguous_default_area;
|
||||
|
||||
#ifdef CONFIG_CMA_SIZE_MBYTES
|
||||
#define CMA_SIZE_MBYTES CONFIG_CMA_SIZE_MBYTES
|
||||
#else
|
||||
#define CMA_SIZE_MBYTES 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Default global CMA area size can be defined in kernel's .config.
|
||||
* This is usefull mainly for distro maintainers to create a kernel
|
||||
* that works correctly for most supported systems.
|
||||
* The size can be set in bytes or as a percentage of the total memory
|
||||
* in the system.
|
||||
*
|
||||
* Users, who want to set the size of global CMA area for their system
|
||||
* should use cma= kernel parameter.
|
||||
*/
|
||||
static const unsigned long size_bytes = CMA_SIZE_MBYTES * SZ_1M;
|
||||
static long size_cmdline = -1;
|
||||
|
||||
static int __init early_cma(char *p)
|
||||
{
|
||||
pr_debug("%s(%s)\n", __func__, p);
|
||||
size_cmdline = memparse(p, &p);
|
||||
return 0;
|
||||
}
|
||||
early_param("cma", early_cma);
|
||||
|
||||
#ifdef CONFIG_CMA_SIZE_PERCENTAGE
|
||||
|
||||
static unsigned long __init __maybe_unused cma_early_percent_memory(void)
|
||||
{
|
||||
struct memblock_region *reg;
|
||||
unsigned long total_pages = 0;
|
||||
|
||||
/*
|
||||
* We cannot use memblock_phys_mem_size() here, because
|
||||
* memblock_analyze() has not been called yet.
|
||||
*/
|
||||
for_each_memblock(memory, reg)
|
||||
total_pages += memblock_region_memory_end_pfn(reg) -
|
||||
memblock_region_memory_base_pfn(reg);
|
||||
|
||||
return (total_pages * CONFIG_CMA_SIZE_PERCENTAGE / 100) << PAGE_SHIFT;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline __maybe_unused unsigned long cma_early_percent_memory(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* dma_contiguous_reserve() - reserve area for contiguous memory handling
|
||||
* @limit: End address of the reserved memory (optional, 0 for any).
|
||||
*
|
||||
* This function reserves memory from early allocator. It should be
|
||||
* called by arch specific code once the early allocator (memblock or bootmem)
|
||||
* has been activated and all other subsystems have already allocated/reserved
|
||||
* memory.
|
||||
*/
|
||||
void __init dma_contiguous_reserve(phys_addr_t limit)
|
||||
{
|
||||
unsigned long selected_size = 0;
|
||||
|
||||
pr_debug("%s(limit %08lx)\n", __func__, (unsigned long)limit);
|
||||
|
||||
if (size_cmdline != -1) {
|
||||
selected_size = size_cmdline;
|
||||
} else {
|
||||
#ifdef CONFIG_CMA_SIZE_SEL_MBYTES
|
||||
selected_size = size_bytes;
|
||||
#elif defined(CONFIG_CMA_SIZE_SEL_PERCENTAGE)
|
||||
selected_size = cma_early_percent_memory();
|
||||
#elif defined(CONFIG_CMA_SIZE_SEL_MIN)
|
||||
selected_size = min(size_bytes, cma_early_percent_memory());
|
||||
#elif defined(CONFIG_CMA_SIZE_SEL_MAX)
|
||||
selected_size = max(size_bytes, cma_early_percent_memory());
|
||||
#endif
|
||||
}
|
||||
|
||||
if (selected_size) {
|
||||
pr_debug("%s: reserving %ld MiB for global area\n", __func__,
|
||||
selected_size / SZ_1M);
|
||||
|
||||
dma_declare_contiguous(NULL, selected_size, 0, limit);
|
||||
}
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(cma_mutex);
|
||||
|
||||
static __init int cma_activate_area(unsigned long base_pfn, unsigned long count)
|
||||
{
|
||||
unsigned long pfn = base_pfn;
|
||||
unsigned i = count >> pageblock_order;
|
||||
struct zone *zone;
|
||||
|
||||
WARN_ON_ONCE(!pfn_valid(pfn));
|
||||
zone = page_zone(pfn_to_page(pfn));
|
||||
|
||||
do {
|
||||
unsigned j;
|
||||
base_pfn = pfn;
|
||||
for (j = pageblock_nr_pages; j; --j, pfn++) {
|
||||
WARN_ON_ONCE(!pfn_valid(pfn));
|
||||
if (page_zone(pfn_to_page(pfn)) != zone)
|
||||
return -EINVAL;
|
||||
}
|
||||
init_cma_reserved_pageblock(pfn_to_page(base_pfn));
|
||||
} while (--i);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __init struct cma *cma_create_area(unsigned long base_pfn,
|
||||
unsigned long count)
|
||||
{
|
||||
int bitmap_size = BITS_TO_LONGS(count) * sizeof(long);
|
||||
struct cma *cma;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
pr_debug("%s(base %08lx, count %lx)\n", __func__, base_pfn, count);
|
||||
|
||||
cma = kmalloc(sizeof *cma, GFP_KERNEL);
|
||||
if (!cma)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
cma->base_pfn = base_pfn;
|
||||
cma->count = count;
|
||||
cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
|
||||
|
||||
if (!cma->bitmap)
|
||||
goto no_mem;
|
||||
|
||||
ret = cma_activate_area(base_pfn, count);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
pr_debug("%s: returned %p\n", __func__, (void *)cma);
|
||||
return cma;
|
||||
|
||||
error:
|
||||
kfree(cma->bitmap);
|
||||
no_mem:
|
||||
kfree(cma);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static struct cma_reserved {
|
||||
phys_addr_t start;
|
||||
unsigned long size;
|
||||
struct device *dev;
|
||||
} cma_reserved[MAX_CMA_AREAS] __initdata;
|
||||
static unsigned cma_reserved_count __initdata;
|
||||
|
||||
static int __init cma_init_reserved_areas(void)
|
||||
{
|
||||
struct cma_reserved *r = cma_reserved;
|
||||
unsigned i = cma_reserved_count;
|
||||
|
||||
pr_debug("%s()\n", __func__);
|
||||
|
||||
for (; i; --i, ++r) {
|
||||
struct cma *cma;
|
||||
cma = cma_create_area(PFN_DOWN(r->start),
|
||||
r->size >> PAGE_SHIFT);
|
||||
if (!IS_ERR(cma))
|
||||
dev_set_cma_area(r->dev, cma);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
core_initcall(cma_init_reserved_areas);
|
||||
|
||||
/**
|
||||
* dma_declare_contiguous() - reserve area for contiguous memory handling
|
||||
* for particular device
|
||||
* @dev: Pointer to device structure.
|
||||
* @size: Size of the reserved memory.
|
||||
* @base: Start address of the reserved memory (optional, 0 for any).
|
||||
* @limit: End address of the reserved memory (optional, 0 for any).
|
||||
*
|
||||
* This function reserves memory for specified device. It should be
|
||||
* called by board specific code when early allocator (memblock or bootmem)
|
||||
* is still activate.
|
||||
*/
|
||||
int __init dma_declare_contiguous(struct device *dev, unsigned long size,
|
||||
phys_addr_t base, phys_addr_t limit)
|
||||
{
|
||||
struct cma_reserved *r = &cma_reserved[cma_reserved_count];
|
||||
unsigned long alignment;
|
||||
|
||||
pr_debug("%s(size %lx, base %08lx, limit %08lx)\n", __func__,
|
||||
(unsigned long)size, (unsigned long)base,
|
||||
(unsigned long)limit);
|
||||
|
||||
/* Sanity checks */
|
||||
if (cma_reserved_count == ARRAY_SIZE(cma_reserved)) {
|
||||
pr_err("Not enough slots for CMA reserved regions!\n");
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (!size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Sanitise input arguments */
|
||||
alignment = PAGE_SIZE << max(MAX_ORDER, pageblock_order);
|
||||
base = ALIGN(base, alignment);
|
||||
size = ALIGN(size, alignment);
|
||||
limit &= ~(alignment - 1);
|
||||
|
||||
/* Reserve memory */
|
||||
if (base) {
|
||||
if (memblock_is_region_reserved(base, size) ||
|
||||
memblock_reserve(base, size) < 0) {
|
||||
base = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Use __memblock_alloc_base() since
|
||||
* memblock_alloc_base() panic()s.
|
||||
*/
|
||||
phys_addr_t addr = __memblock_alloc_base(size, alignment, limit);
|
||||
if (!addr) {
|
||||
base = -ENOMEM;
|
||||
goto err;
|
||||
} else if (addr + size > ~(unsigned long)0) {
|
||||
memblock_free(addr, size);
|
||||
base = -EINVAL;
|
||||
goto err;
|
||||
} else {
|
||||
base = addr;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Each reserved area must be initialised later, when more kernel
|
||||
* subsystems (like slab allocator) are available.
|
||||
*/
|
||||
r->start = base;
|
||||
r->size = size;
|
||||
r->dev = dev;
|
||||
cma_reserved_count++;
|
||||
pr_info("CMA: reserved %ld MiB at %08lx\n", size / SZ_1M,
|
||||
(unsigned long)base);
|
||||
|
||||
/* Architecture specific contiguous memory fixup. */
|
||||
dma_contiguous_early_fixup(base, size);
|
||||
return 0;
|
||||
err:
|
||||
pr_err("CMA: failed to reserve %ld MiB\n", size / SZ_1M);
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_alloc_from_contiguous() - allocate pages from contiguous area
|
||||
* @dev: Pointer to device for which the allocation is performed.
|
||||
* @count: Requested number of pages.
|
||||
* @align: Requested alignment of pages (in PAGE_SIZE order).
|
||||
*
|
||||
* This function allocates memory buffer for specified device. It uses
|
||||
* device specific contiguous memory area if available or the default
|
||||
* global one. Requires architecture specific get_dev_cma_area() helper
|
||||
* function.
|
||||
*/
|
||||
struct page *dma_alloc_from_contiguous(struct device *dev, int count,
|
||||
unsigned int align)
|
||||
{
|
||||
unsigned long mask, pfn, pageno, start = 0;
|
||||
struct cma *cma = dev_get_cma_area(dev);
|
||||
int ret;
|
||||
|
||||
if (!cma || !cma->count)
|
||||
return NULL;
|
||||
|
||||
if (align > CONFIG_CMA_ALIGNMENT)
|
||||
align = CONFIG_CMA_ALIGNMENT;
|
||||
|
||||
pr_debug("%s(cma %p, count %d, align %d)\n", __func__, (void *)cma,
|
||||
count, align);
|
||||
|
||||
if (!count)
|
||||
return NULL;
|
||||
|
||||
mask = (1 << align) - 1;
|
||||
|
||||
mutex_lock(&cma_mutex);
|
||||
|
||||
for (;;) {
|
||||
pageno = bitmap_find_next_zero_area(cma->bitmap, cma->count,
|
||||
start, count, mask);
|
||||
if (pageno >= cma->count) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
pfn = cma->base_pfn + pageno;
|
||||
ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA);
|
||||
if (ret == 0) {
|
||||
bitmap_set(cma->bitmap, pageno, count);
|
||||
break;
|
||||
} else if (ret != -EBUSY) {
|
||||
goto error;
|
||||
}
|
||||
pr_debug("%s(): memory range at %p is busy, retrying\n",
|
||||
__func__, pfn_to_page(pfn));
|
||||
/* try again with a bit different memory target */
|
||||
start = pageno + mask + 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&cma_mutex);
|
||||
|
||||
pr_debug("%s(): returned %p\n", __func__, pfn_to_page(pfn));
|
||||
return pfn_to_page(pfn);
|
||||
error:
|
||||
mutex_unlock(&cma_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_release_from_contiguous() - release allocated pages
|
||||
* @dev: Pointer to device for which the pages were allocated.
|
||||
* @pages: Allocated pages.
|
||||
* @count: Number of allocated pages.
|
||||
*
|
||||
* This function releases memory allocated by dma_alloc_from_contiguous().
|
||||
* It returns false when provided pages do not belong to contiguous area and
|
||||
* true otherwise.
|
||||
*/
|
||||
bool dma_release_from_contiguous(struct device *dev, struct page *pages,
|
||||
int count)
|
||||
{
|
||||
struct cma *cma = dev_get_cma_area(dev);
|
||||
unsigned long pfn;
|
||||
|
||||
if (!cma || !pages)
|
||||
return false;
|
||||
|
||||
pr_debug("%s(page %p)\n", __func__, (void *)pages);
|
||||
|
||||
pfn = page_to_pfn(pages);
|
||||
|
||||
if (pfn < cma->base_pfn || pfn >= cma->base_pfn + cma->count)
|
||||
return false;
|
||||
|
||||
VM_BUG_ON(pfn + count > cma->base_pfn + cma->count);
|
||||
|
||||
mutex_lock(&cma_mutex);
|
||||
bitmap_clear(cma->bitmap, pfn - cma->base_pfn, count);
|
||||
free_contig_range(pfn, count);
|
||||
mutex_unlock(&cma_mutex);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -80,7 +80,7 @@ struct device *driver_find_device(struct device_driver *drv,
|
||||
struct klist_iter i;
|
||||
struct device *dev;
|
||||
|
||||
if (!drv)
|
||||
if (!drv || !drv->p)
|
||||
return NULL;
|
||||
|
||||
klist_iter_init_node(&drv->p->klist_devices, &i,
|
||||
|
||||
+3
-5
@@ -592,11 +592,9 @@ static ssize_t print_nodes_state(enum node_states state, char *buf)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = nodelist_scnprintf(buf, PAGE_SIZE, node_states[state]);
|
||||
if (n > 0 && PAGE_SIZE > n + 1) {
|
||||
*(buf + n++) = '\n';
|
||||
*(buf + n++) = '\0';
|
||||
}
|
||||
n = nodelist_scnprintf(buf, PAGE_SIZE-2, node_states[state]);
|
||||
buf[n++] = '\n';
|
||||
buf[n] = '\0';
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
+131
-45
@@ -11,6 +11,7 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_domain.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
@@ -38,11 +39,13 @@
|
||||
ktime_t __start = ktime_get(); \
|
||||
type __retval = GENPD_DEV_CALLBACK(genpd, type, callback, dev); \
|
||||
s64 __elapsed = ktime_to_ns(ktime_sub(ktime_get(), __start)); \
|
||||
struct generic_pm_domain_data *__gpd_data = dev_gpd_data(dev); \
|
||||
if (__elapsed > __gpd_data->td.field) { \
|
||||
__gpd_data->td.field = __elapsed; \
|
||||
struct gpd_timing_data *__td = &dev_gpd_data(dev)->td; \
|
||||
if (!__retval && __elapsed > __td->field) { \
|
||||
__td->field = __elapsed; \
|
||||
dev_warn(dev, name " latency exceeded, new value %lld ns\n", \
|
||||
__elapsed); \
|
||||
genpd->max_off_time_changed = true; \
|
||||
__td->constraint_changed = true; \
|
||||
} \
|
||||
__retval; \
|
||||
})
|
||||
@@ -211,6 +214,7 @@ int __pm_genpd_poweron(struct generic_pm_domain *genpd)
|
||||
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
|
||||
if (elapsed_ns > genpd->power_on_latency_ns) {
|
||||
genpd->power_on_latency_ns = elapsed_ns;
|
||||
genpd->max_off_time_changed = true;
|
||||
if (genpd->name)
|
||||
pr_warning("%s: Power-on latency exceeded, "
|
||||
"new value %lld ns\n", genpd->name,
|
||||
@@ -247,6 +251,53 @@ int pm_genpd_poweron(struct generic_pm_domain *genpd)
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
|
||||
unsigned long val, void *ptr)
|
||||
{
|
||||
struct generic_pm_domain_data *gpd_data;
|
||||
struct device *dev;
|
||||
|
||||
gpd_data = container_of(nb, struct generic_pm_domain_data, nb);
|
||||
|
||||
mutex_lock(&gpd_data->lock);
|
||||
dev = gpd_data->base.dev;
|
||||
if (!dev) {
|
||||
mutex_unlock(&gpd_data->lock);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
mutex_unlock(&gpd_data->lock);
|
||||
|
||||
for (;;) {
|
||||
struct generic_pm_domain *genpd;
|
||||
struct pm_domain_data *pdd;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
|
||||
pdd = dev->power.subsys_data ?
|
||||
dev->power.subsys_data->domain_data : NULL;
|
||||
if (pdd) {
|
||||
to_gpd_data(pdd)->td.constraint_changed = true;
|
||||
genpd = dev_to_genpd(dev);
|
||||
} else {
|
||||
genpd = ERR_PTR(-ENODATA);
|
||||
}
|
||||
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
if (!IS_ERR(genpd)) {
|
||||
mutex_lock(&genpd->lock);
|
||||
genpd->max_off_time_changed = true;
|
||||
mutex_unlock(&genpd->lock);
|
||||
}
|
||||
|
||||
dev = dev->parent;
|
||||
if (!dev || dev->power.ignore_children)
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* __pm_genpd_save_device - Save the pre-suspend state of a device.
|
||||
* @pdd: Domain data of the device to save the state of.
|
||||
@@ -435,6 +486,7 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
|
||||
elapsed_ns = ktime_to_ns(ktime_sub(ktime_get(), time_start));
|
||||
if (elapsed_ns > genpd->power_off_latency_ns) {
|
||||
genpd->power_off_latency_ns = elapsed_ns;
|
||||
genpd->max_off_time_changed = true;
|
||||
if (genpd->name)
|
||||
pr_warning("%s: Power-off latency exceeded, "
|
||||
"new value %lld ns\n", genpd->name,
|
||||
@@ -443,17 +495,6 @@ static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
|
||||
}
|
||||
|
||||
genpd->status = GPD_STATE_POWER_OFF;
|
||||
genpd->power_off_time = ktime_get();
|
||||
|
||||
/* Update PM QoS information for devices in the domain. */
|
||||
list_for_each_entry_reverse(pdd, &genpd->dev_list, list_node) {
|
||||
struct gpd_timing_data *td = &to_gpd_data(pdd)->td;
|
||||
|
||||
pm_runtime_update_max_time_suspended(pdd->dev,
|
||||
td->start_latency_ns +
|
||||
td->restore_state_latency_ns +
|
||||
genpd->power_on_latency_ns);
|
||||
}
|
||||
|
||||
list_for_each_entry(link, &genpd->slave_links, slave_node) {
|
||||
genpd_sd_counter_dec(link->master);
|
||||
@@ -514,9 +555,6 @@ static int pm_genpd_runtime_suspend(struct device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_update_max_time_suspended(dev,
|
||||
dev_gpd_data(dev)->td.start_latency_ns);
|
||||
|
||||
/*
|
||||
* If power.irq_safe is set, this routine will be run with interrupts
|
||||
* off, so it can't use mutexes.
|
||||
@@ -613,6 +651,12 @@ void pm_genpd_poweroff_unused(void)
|
||||
|
||||
#else
|
||||
|
||||
static inline int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
|
||||
unsigned long val, void *ptr)
|
||||
{
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static inline void genpd_power_off_work_fn(struct work_struct *work) {}
|
||||
|
||||
#define pm_genpd_runtime_suspend NULL
|
||||
@@ -1209,12 +1253,15 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
|
||||
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
|
||||
return -EINVAL;
|
||||
|
||||
genpd_acquire_lock(genpd);
|
||||
gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
|
||||
if (!gpd_data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (genpd->status == GPD_STATE_POWER_OFF) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mutex_init(&gpd_data->lock);
|
||||
gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier;
|
||||
dev_pm_qos_add_notifier(dev, &gpd_data->nb);
|
||||
|
||||
genpd_acquire_lock(genpd);
|
||||
|
||||
if (genpd->prepared_count > 0) {
|
||||
ret = -EAGAIN;
|
||||
@@ -1227,26 +1274,35 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev,
|
||||
goto out;
|
||||
}
|
||||
|
||||
gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL);
|
||||
if (!gpd_data) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
genpd->device_count++;
|
||||
genpd->max_off_time_changed = true;
|
||||
|
||||
dev->pm_domain = &genpd->domain;
|
||||
dev_pm_get_subsys_data(dev);
|
||||
|
||||
mutex_lock(&gpd_data->lock);
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
dev->pm_domain = &genpd->domain;
|
||||
dev->power.subsys_data->domain_data = &gpd_data->base;
|
||||
gpd_data->base.dev = dev;
|
||||
gpd_data->need_restore = false;
|
||||
list_add_tail(&gpd_data->base.list_node, &genpd->dev_list);
|
||||
gpd_data->need_restore = genpd->status == GPD_STATE_POWER_OFF;
|
||||
if (td)
|
||||
gpd_data->td = *td;
|
||||
|
||||
gpd_data->td.constraint_changed = true;
|
||||
gpd_data->td.effective_constraint_ns = -1;
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
mutex_unlock(&gpd_data->lock);
|
||||
|
||||
genpd_release_lock(genpd);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
genpd_release_lock(genpd);
|
||||
|
||||
dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
|
||||
kfree(gpd_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1290,12 +1346,15 @@ int __pm_genpd_of_add_device(struct device_node *genpd_node, struct device *dev,
|
||||
int pm_genpd_remove_device(struct generic_pm_domain *genpd,
|
||||
struct device *dev)
|
||||
{
|
||||
struct generic_pm_domain_data *gpd_data;
|
||||
struct pm_domain_data *pdd;
|
||||
int ret = -EINVAL;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
|
||||
if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)
|
||||
|| IS_ERR_OR_NULL(dev->pm_domain)
|
||||
|| pd_to_genpd(dev->pm_domain) != genpd)
|
||||
return -EINVAL;
|
||||
|
||||
genpd_acquire_lock(genpd);
|
||||
@@ -1305,21 +1364,27 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd,
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(pdd, &genpd->dev_list, list_node) {
|
||||
if (pdd->dev != dev)
|
||||
continue;
|
||||
genpd->device_count--;
|
||||
genpd->max_off_time_changed = true;
|
||||
|
||||
list_del_init(&pdd->list_node);
|
||||
pdd->dev = NULL;
|
||||
dev_pm_put_subsys_data(dev);
|
||||
dev->pm_domain = NULL;
|
||||
kfree(to_gpd_data(pdd));
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
dev->pm_domain = NULL;
|
||||
pdd = dev->power.subsys_data->domain_data;
|
||||
list_del_init(&pdd->list_node);
|
||||
dev->power.subsys_data->domain_data = NULL;
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
genpd->device_count--;
|
||||
gpd_data = to_gpd_data(pdd);
|
||||
mutex_lock(&gpd_data->lock);
|
||||
pdd->dev = NULL;
|
||||
mutex_unlock(&gpd_data->lock);
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
genpd_release_lock(genpd);
|
||||
|
||||
dev_pm_qos_remove_notifier(dev, &gpd_data->nb);
|
||||
kfree(gpd_data);
|
||||
dev_pm_put_subsys_data(dev);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
genpd_release_lock(genpd);
|
||||
@@ -1347,6 +1412,26 @@ void pm_genpd_dev_always_on(struct device *dev, bool val)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_genpd_dev_always_on);
|
||||
|
||||
/**
|
||||
* pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag.
|
||||
* @dev: Device to set/unset the flag for.
|
||||
* @val: The new value of the device's "need restore" flag.
|
||||
*/
|
||||
void pm_genpd_dev_need_restore(struct device *dev, bool val)
|
||||
{
|
||||
struct pm_subsys_data *psd;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->power.lock, flags);
|
||||
|
||||
psd = dev_to_psd(dev);
|
||||
if (psd && psd->domain_data)
|
||||
to_gpd_data(psd->domain_data)->need_restore = val;
|
||||
|
||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore);
|
||||
|
||||
/**
|
||||
* pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
|
||||
* @genpd: Master PM domain to add the subdomain to.
|
||||
@@ -1378,7 +1463,7 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
|
||||
goto out;
|
||||
}
|
||||
|
||||
list_for_each_entry(link, &genpd->slave_links, slave_node) {
|
||||
list_for_each_entry(link, &genpd->master_links, master_node) {
|
||||
if (link->slave == subdomain && link->master == genpd) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
@@ -1690,6 +1775,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
|
||||
genpd->resume_count = 0;
|
||||
genpd->device_count = 0;
|
||||
genpd->max_off_time_ns = -1;
|
||||
genpd->max_off_time_changed = true;
|
||||
genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
|
||||
genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
|
||||
genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
|
||||
|
||||
@@ -14,6 +14,31 @@
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
|
||||
static int dev_update_qos_constraint(struct device *dev, void *data)
|
||||
{
|
||||
s64 *constraint_ns_p = data;
|
||||
s32 constraint_ns = -1;
|
||||
|
||||
if (dev->power.subsys_data && dev->power.subsys_data->domain_data)
|
||||
constraint_ns = dev_gpd_data(dev)->td.effective_constraint_ns;
|
||||
|
||||
if (constraint_ns < 0) {
|
||||
constraint_ns = dev_pm_qos_read_value(dev);
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
}
|
||||
if (constraint_ns == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* constraint_ns cannot be negative here, because the device has been
|
||||
* suspended.
|
||||
*/
|
||||
if (constraint_ns < *constraint_ns_p || *constraint_ns_p == 0)
|
||||
*constraint_ns_p = constraint_ns;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* default_stop_ok - Default PM domain governor routine for stopping devices.
|
||||
* @dev: Device to check.
|
||||
@@ -21,14 +46,52 @@
|
||||
bool default_stop_ok(struct device *dev)
|
||||
{
|
||||
struct gpd_timing_data *td = &dev_gpd_data(dev)->td;
|
||||
unsigned long flags;
|
||||
s64 constraint_ns;
|
||||
|
||||
dev_dbg(dev, "%s()\n", __func__);
|
||||
|
||||
if (dev->power.max_time_suspended_ns < 0 || td->break_even_ns == 0)
|
||||
return true;
|
||||
spin_lock_irqsave(&dev->power.lock, flags);
|
||||
|
||||
return td->stop_latency_ns + td->start_latency_ns < td->break_even_ns
|
||||
&& td->break_even_ns < dev->power.max_time_suspended_ns;
|
||||
if (!td->constraint_changed) {
|
||||
bool ret = td->cached_stop_ok;
|
||||
|
||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||
return ret;
|
||||
}
|
||||
td->constraint_changed = false;
|
||||
td->cached_stop_ok = false;
|
||||
td->effective_constraint_ns = -1;
|
||||
constraint_ns = __dev_pm_qos_read_value(dev);
|
||||
|
||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||
|
||||
if (constraint_ns < 0)
|
||||
return false;
|
||||
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
/*
|
||||
* We can walk the children without any additional locking, because
|
||||
* they all have been suspended at this point and their
|
||||
* effective_constraint_ns fields won't be modified in parallel with us.
|
||||
*/
|
||||
if (!dev->power.ignore_children)
|
||||
device_for_each_child(dev, &constraint_ns,
|
||||
dev_update_qos_constraint);
|
||||
|
||||
if (constraint_ns > 0) {
|
||||
constraint_ns -= td->start_latency_ns;
|
||||
if (constraint_ns == 0)
|
||||
return false;
|
||||
}
|
||||
td->effective_constraint_ns = constraint_ns;
|
||||
td->cached_stop_ok = constraint_ns > td->stop_latency_ns ||
|
||||
constraint_ns == 0;
|
||||
/*
|
||||
* The children have been suspended already, so we don't need to take
|
||||
* their stop latencies into account here.
|
||||
*/
|
||||
return td->cached_stop_ok;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,9 +105,27 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
|
||||
struct generic_pm_domain *genpd = pd_to_genpd(pd);
|
||||
struct gpd_link *link;
|
||||
struct pm_domain_data *pdd;
|
||||
s64 min_dev_off_time_ns;
|
||||
s64 min_off_time_ns;
|
||||
s64 off_on_time_ns;
|
||||
ktime_t time_now = ktime_get();
|
||||
|
||||
if (genpd->max_off_time_changed) {
|
||||
struct gpd_link *link;
|
||||
|
||||
/*
|
||||
* We have to invalidate the cached results for the masters, so
|
||||
* use the observation that default_power_down_ok() is not
|
||||
* going to be called for any master until this instance
|
||||
* returns.
|
||||
*/
|
||||
list_for_each_entry(link, &genpd->slave_links, slave_node)
|
||||
link->master->max_off_time_changed = true;
|
||||
|
||||
genpd->max_off_time_changed = false;
|
||||
genpd->cached_power_down_ok = false;
|
||||
genpd->max_off_time_ns = -1;
|
||||
} else {
|
||||
return genpd->cached_power_down_ok;
|
||||
}
|
||||
|
||||
off_on_time_ns = genpd->power_off_latency_ns +
|
||||
genpd->power_on_latency_ns;
|
||||
@@ -61,6 +142,7 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
|
||||
to_gpd_data(pdd)->td.save_state_latency_ns;
|
||||
}
|
||||
|
||||
min_off_time_ns = -1;
|
||||
/*
|
||||
* Check if subdomains can be off for enough time.
|
||||
*
|
||||
@@ -73,8 +155,6 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
|
||||
if (sd_max_off_ns < 0)
|
||||
continue;
|
||||
|
||||
sd_max_off_ns -= ktime_to_ns(ktime_sub(time_now,
|
||||
sd->power_off_time));
|
||||
/*
|
||||
* Check if the subdomain is allowed to be off long enough for
|
||||
* the current domain to turn off and on (that's how much time
|
||||
@@ -82,60 +162,64 @@ static bool default_power_down_ok(struct dev_pm_domain *pd)
|
||||
*/
|
||||
if (sd_max_off_ns <= off_on_time_ns)
|
||||
return false;
|
||||
|
||||
if (min_off_time_ns > sd_max_off_ns || min_off_time_ns < 0)
|
||||
min_off_time_ns = sd_max_off_ns;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the devices in the domain can be off enough time.
|
||||
*/
|
||||
min_dev_off_time_ns = -1;
|
||||
list_for_each_entry(pdd, &genpd->dev_list, list_node) {
|
||||
struct gpd_timing_data *td;
|
||||
struct device *dev = pdd->dev;
|
||||
s64 dev_off_time_ns;
|
||||
s64 constraint_ns;
|
||||
|
||||
if (!dev->driver || dev->power.max_time_suspended_ns < 0)
|
||||
if (!pdd->dev->driver)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* Check if the device is allowed to be off long enough for the
|
||||
* domain to turn off and on (that's how much time it will
|
||||
* have to wait worst case).
|
||||
*/
|
||||
td = &to_gpd_data(pdd)->td;
|
||||
dev_off_time_ns = dev->power.max_time_suspended_ns -
|
||||
(td->start_latency_ns + td->restore_state_latency_ns +
|
||||
ktime_to_ns(ktime_sub(time_now,
|
||||
dev->power.suspend_time)));
|
||||
if (dev_off_time_ns <= off_on_time_ns)
|
||||
constraint_ns = td->effective_constraint_ns;
|
||||
/* default_stop_ok() need not be called before us. */
|
||||
if (constraint_ns < 0) {
|
||||
constraint_ns = dev_pm_qos_read_value(pdd->dev);
|
||||
constraint_ns *= NSEC_PER_USEC;
|
||||
}
|
||||
if (constraint_ns == 0)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* constraint_ns cannot be negative here, because the device has
|
||||
* been suspended.
|
||||
*/
|
||||
constraint_ns -= td->restore_state_latency_ns;
|
||||
if (constraint_ns <= off_on_time_ns)
|
||||
return false;
|
||||
|
||||
if (min_dev_off_time_ns > dev_off_time_ns
|
||||
|| min_dev_off_time_ns < 0)
|
||||
min_dev_off_time_ns = dev_off_time_ns;
|
||||
if (min_off_time_ns > constraint_ns || min_off_time_ns < 0)
|
||||
min_off_time_ns = constraint_ns;
|
||||
}
|
||||
|
||||
if (min_dev_off_time_ns < 0) {
|
||||
/*
|
||||
* There are no latency constraints, so the domain can spend
|
||||
* arbitrary time in the "off" state.
|
||||
*/
|
||||
genpd->max_off_time_ns = -1;
|
||||
genpd->cached_power_down_ok = true;
|
||||
|
||||
/*
|
||||
* If the computed minimum device off time is negative, there are no
|
||||
* latency constraints, so the domain can spend arbitrary time in the
|
||||
* "off" state.
|
||||
*/
|
||||
if (min_off_time_ns < 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The difference between the computed minimum delta and the time needed
|
||||
* to turn the domain on is the maximum theoretical time this domain can
|
||||
* spend in the "off" state.
|
||||
* The difference between the computed minimum subdomain or device off
|
||||
* time and the time needed to turn the domain on is the maximum
|
||||
* theoretical time this domain can spend in the "off" state.
|
||||
*/
|
||||
min_dev_off_time_ns -= genpd->power_on_latency_ns;
|
||||
|
||||
/*
|
||||
* If the difference between the computed minimum delta and the time
|
||||
* needed to turn the domain off and back on on is smaller than the
|
||||
* domain's power break even time, removing power from the domain is not
|
||||
* worth it.
|
||||
*/
|
||||
if (genpd->break_even_ns >
|
||||
min_dev_off_time_ns - genpd->power_off_latency_ns)
|
||||
return false;
|
||||
|
||||
genpd->max_off_time_ns = min_dev_off_time_ns;
|
||||
genpd->max_off_time_ns = min_off_time_ns - genpd->power_on_latency_ns;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -889,6 +889,11 @@ static int dpm_suspend_noirq(pm_message_t state)
|
||||
if (!list_empty(&dev->power.entry))
|
||||
list_move(&dev->power.entry, &dpm_noirq_list);
|
||||
put_device(dev);
|
||||
|
||||
if (pm_wakeup_pending()) {
|
||||
error = -EBUSY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
if (error)
|
||||
@@ -962,6 +967,11 @@ static int dpm_suspend_late(pm_message_t state)
|
||||
if (!list_empty(&dev->power.entry))
|
||||
list_move(&dev->power.entry, &dpm_late_early_list);
|
||||
put_device(dev);
|
||||
|
||||
if (pm_wakeup_pending()) {
|
||||
error = -EBUSY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&dpm_list_mtx);
|
||||
if (error)
|
||||
|
||||
@@ -352,21 +352,26 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_remove_request);
|
||||
*
|
||||
* Will register the notifier into a notification chain that gets called
|
||||
* upon changes to the target value for the device.
|
||||
*
|
||||
* If the device's constraints object doesn't exist when this routine is called,
|
||||
* it will be created (or error code will be returned if that fails).
|
||||
*/
|
||||
int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier)
|
||||
{
|
||||
int retval = 0;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&dev_pm_qos_mtx);
|
||||
|
||||
/* Silently return if the constraints object is not present. */
|
||||
if (dev->power.constraints)
|
||||
retval = blocking_notifier_chain_register(
|
||||
dev->power.constraints->notifiers,
|
||||
notifier);
|
||||
if (!dev->power.constraints)
|
||||
ret = dev->power.power_state.event != PM_EVENT_INVALID ?
|
||||
dev_pm_qos_constraints_allocate(dev) : -ENODEV;
|
||||
|
||||
if (!ret)
|
||||
ret = blocking_notifier_chain_register(
|
||||
dev->power.constraints->notifiers, notifier);
|
||||
|
||||
mutex_unlock(&dev_pm_qos_mtx);
|
||||
return retval;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_qos_add_notifier);
|
||||
|
||||
|
||||
@@ -282,47 +282,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev)
|
||||
return retval != -EACCES ? retval : -EIO;
|
||||
}
|
||||
|
||||
struct rpm_qos_data {
|
||||
ktime_t time_now;
|
||||
s64 constraint_ns;
|
||||
};
|
||||
|
||||
/**
|
||||
* rpm_update_qos_constraint - Update a given PM QoS constraint data.
|
||||
* @dev: Device whose timing data to use.
|
||||
* @data: PM QoS constraint data to update.
|
||||
*
|
||||
* Use the suspend timing data of @dev to update PM QoS constraint data pointed
|
||||
* to by @data.
|
||||
*/
|
||||
static int rpm_update_qos_constraint(struct device *dev, void *data)
|
||||
{
|
||||
struct rpm_qos_data *qos = data;
|
||||
unsigned long flags;
|
||||
s64 delta_ns;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(&dev->power.lock, flags);
|
||||
|
||||
if (dev->power.max_time_suspended_ns < 0)
|
||||
goto out;
|
||||
|
||||
delta_ns = dev->power.max_time_suspended_ns -
|
||||
ktime_to_ns(ktime_sub(qos->time_now, dev->power.suspend_time));
|
||||
if (delta_ns <= 0) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qos->constraint_ns > delta_ns || qos->constraint_ns == 0)
|
||||
qos->constraint_ns = delta_ns;
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* rpm_suspend - Carry out runtime suspend of given device.
|
||||
* @dev: Device to suspend.
|
||||
@@ -349,7 +308,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||
{
|
||||
int (*callback)(struct device *);
|
||||
struct device *parent = NULL;
|
||||
struct rpm_qos_data qos;
|
||||
int retval;
|
||||
|
||||
trace_rpm_suspend(dev, rpmflags);
|
||||
@@ -445,38 +403,14 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||
goto out;
|
||||
}
|
||||
|
||||
qos.constraint_ns = __dev_pm_qos_read_value(dev);
|
||||
if (qos.constraint_ns < 0) {
|
||||
/* Negative constraint means "never suspend". */
|
||||
if (__dev_pm_qos_read_value(dev) < 0) {
|
||||
/* Negative PM QoS constraint means "never suspend". */
|
||||
retval = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
qos.constraint_ns *= NSEC_PER_USEC;
|
||||
qos.time_now = ktime_get();
|
||||
|
||||
__update_runtime_status(dev, RPM_SUSPENDING);
|
||||
|
||||
if (!dev->power.ignore_children) {
|
||||
if (dev->power.irq_safe)
|
||||
spin_unlock(&dev->power.lock);
|
||||
else
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
retval = device_for_each_child(dev, &qos,
|
||||
rpm_update_qos_constraint);
|
||||
|
||||
if (dev->power.irq_safe)
|
||||
spin_lock(&dev->power.lock);
|
||||
else
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
|
||||
if (retval)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dev->power.suspend_time = qos.time_now;
|
||||
dev->power.max_time_suspended_ns = qos.constraint_ns ? : -1;
|
||||
|
||||
if (dev->pm_domain)
|
||||
callback = dev->pm_domain->ops.runtime_suspend;
|
||||
else if (dev->type && dev->type->pm)
|
||||
@@ -529,8 +463,6 @@ static int rpm_suspend(struct device *dev, int rpmflags)
|
||||
|
||||
fail:
|
||||
__update_runtime_status(dev, RPM_ACTIVE);
|
||||
dev->power.suspend_time = ktime_set(0, 0);
|
||||
dev->power.max_time_suspended_ns = -1;
|
||||
dev->power.deferred_resume = false;
|
||||
wake_up_all(&dev->power.wait_queue);
|
||||
|
||||
@@ -704,9 +636,6 @@ static int rpm_resume(struct device *dev, int rpmflags)
|
||||
if (dev->power.no_callbacks)
|
||||
goto no_callback; /* Assume success. */
|
||||
|
||||
dev->power.suspend_time = ktime_set(0, 0);
|
||||
dev->power.max_time_suspended_ns = -1;
|
||||
|
||||
__update_runtime_status(dev, RPM_RESUMING);
|
||||
|
||||
if (dev->pm_domain)
|
||||
@@ -1369,9 +1298,6 @@ void pm_runtime_init(struct device *dev)
|
||||
setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn,
|
||||
(unsigned long)dev);
|
||||
|
||||
dev->power.suspend_time = ktime_set(0, 0);
|
||||
dev->power.max_time_suspended_ns = -1;
|
||||
|
||||
init_waitqueue_head(&dev->power.wait_queue);
|
||||
}
|
||||
|
||||
@@ -1389,28 +1315,3 @@ void pm_runtime_remove(struct device *dev)
|
||||
if (dev->power.irq_safe && dev->parent)
|
||||
pm_runtime_put_sync(dev->parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_runtime_update_max_time_suspended - Update device's suspend time data.
|
||||
* @dev: Device to handle.
|
||||
* @delta_ns: Value to subtract from the device's max_time_suspended_ns field.
|
||||
*
|
||||
* Update the device's power.max_time_suspended_ns field by subtracting
|
||||
* @delta_ns from it. The resulting value of power.max_time_suspended_ns is
|
||||
* never negative.
|
||||
*/
|
||||
void pm_runtime_update_max_time_suspended(struct device *dev, s64 delta_ns)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev->power.lock, flags);
|
||||
|
||||
if (delta_ns > 0 && dev->power.max_time_suspended_ns > 0) {
|
||||
if (dev->power.max_time_suspended_ns > delta_ns)
|
||||
dev->power.max_time_suspended_ns -= delta_ns;
|
||||
else
|
||||
dev->power.max_time_suspended_ns = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->power.lock, flags);
|
||||
}
|
||||
|
||||
@@ -314,22 +314,41 @@ static ssize_t wakeup_active_count_show(struct device *dev,
|
||||
|
||||
static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL);
|
||||
|
||||
static ssize_t wakeup_hit_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
static ssize_t wakeup_abort_count_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
unsigned long count = 0;
|
||||
bool enabled = false;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
if (dev->power.wakeup) {
|
||||
count = dev->power.wakeup->hit_count;
|
||||
count = dev->power.wakeup->wakeup_count;
|
||||
enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_hit_count, 0444, wakeup_hit_count_show, NULL);
|
||||
static DEVICE_ATTR(wakeup_abort_count, 0444, wakeup_abort_count_show, NULL);
|
||||
|
||||
static ssize_t wakeup_expire_count_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
unsigned long count = 0;
|
||||
bool enabled = false;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
if (dev->power.wakeup) {
|
||||
count = dev->power.wakeup->expire_count;
|
||||
enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_expire_count, 0444, wakeup_expire_count_show, NULL);
|
||||
|
||||
static ssize_t wakeup_active_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
@@ -398,6 +417,27 @@ static ssize_t wakeup_last_time_show(struct device *dev,
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL);
|
||||
|
||||
#ifdef CONFIG_PM_AUTOSLEEP
|
||||
static ssize_t wakeup_prevent_sleep_time_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
s64 msec = 0;
|
||||
bool enabled = false;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
if (dev->power.wakeup) {
|
||||
msec = ktime_to_ms(dev->power.wakeup->prevent_sleep_time);
|
||||
enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(wakeup_prevent_sleep_time_ms, 0444,
|
||||
wakeup_prevent_sleep_time_show, NULL);
|
||||
#endif /* CONFIG_PM_AUTOSLEEP */
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
#ifdef CONFIG_PM_ADVANCED_DEBUG
|
||||
@@ -486,11 +526,15 @@ static struct attribute *wakeup_attrs[] = {
|
||||
&dev_attr_wakeup.attr,
|
||||
&dev_attr_wakeup_count.attr,
|
||||
&dev_attr_wakeup_active_count.attr,
|
||||
&dev_attr_wakeup_hit_count.attr,
|
||||
&dev_attr_wakeup_abort_count.attr,
|
||||
&dev_attr_wakeup_expire_count.attr,
|
||||
&dev_attr_wakeup_active.attr,
|
||||
&dev_attr_wakeup_total_time_ms.attr,
|
||||
&dev_attr_wakeup_max_time_ms.attr,
|
||||
&dev_attr_wakeup_last_time_ms.attr,
|
||||
#ifdef CONFIG_PM_AUTOSLEEP
|
||||
&dev_attr_wakeup_prevent_sleep_time_ms.attr,
|
||||
#endif
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
+120
-54
@@ -14,16 +14,15 @@
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <trace/events/power.h>
|
||||
|
||||
#include "power.h"
|
||||
|
||||
#define TIMEOUT 100
|
||||
|
||||
/*
|
||||
* If set, the suspend/hibernate code will abort transitions to a sleep state
|
||||
* if wakeup events are registered during or immediately before the transition.
|
||||
*/
|
||||
bool events_check_enabled;
|
||||
bool events_check_enabled __read_mostly;
|
||||
|
||||
/*
|
||||
* Combined counters of registered wakeup events and wakeup events in progress.
|
||||
@@ -52,6 +51,8 @@ static void pm_wakeup_timer_fn(unsigned long data);
|
||||
|
||||
static LIST_HEAD(wakeup_sources);
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(wakeup_count_wait_queue);
|
||||
|
||||
/**
|
||||
* wakeup_source_prepare - Prepare a new wakeup source for initialization.
|
||||
* @ws: Wakeup source to prepare.
|
||||
@@ -132,6 +133,7 @@ void wakeup_source_add(struct wakeup_source *ws)
|
||||
spin_lock_init(&ws->lock);
|
||||
setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws);
|
||||
ws->active = false;
|
||||
ws->last_time = ktime_get();
|
||||
|
||||
spin_lock_irq(&events_lock);
|
||||
list_add_rcu(&ws->entry, &wakeup_sources);
|
||||
@@ -374,12 +376,33 @@ EXPORT_SYMBOL_GPL(device_set_wakeup_enable);
|
||||
*/
|
||||
static void wakeup_source_activate(struct wakeup_source *ws)
|
||||
{
|
||||
unsigned int cec;
|
||||
|
||||
ws->active = true;
|
||||
ws->active_count++;
|
||||
ws->last_time = ktime_get();
|
||||
if (ws->autosleep_enabled)
|
||||
ws->start_prevent_time = ws->last_time;
|
||||
|
||||
/* Increment the counter of events in progress. */
|
||||
atomic_inc(&combined_event_count);
|
||||
cec = atomic_inc_return(&combined_event_count);
|
||||
|
||||
trace_wakeup_source_activate(ws->name, cec);
|
||||
}
|
||||
|
||||
/**
|
||||
* wakeup_source_report_event - Report wakeup event using the given source.
|
||||
* @ws: Wakeup source to report the event for.
|
||||
*/
|
||||
static void wakeup_source_report_event(struct wakeup_source *ws)
|
||||
{
|
||||
ws->event_count++;
|
||||
/* This is racy, but the counter is approximate anyway. */
|
||||
if (events_check_enabled)
|
||||
ws->wakeup_count++;
|
||||
|
||||
if (!ws->active)
|
||||
wakeup_source_activate(ws);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -397,10 +420,7 @@ void __pm_stay_awake(struct wakeup_source *ws)
|
||||
|
||||
spin_lock_irqsave(&ws->lock, flags);
|
||||
|
||||
ws->event_count++;
|
||||
if (!ws->active)
|
||||
wakeup_source_activate(ws);
|
||||
|
||||
wakeup_source_report_event(ws);
|
||||
del_timer(&ws->timer);
|
||||
ws->timer_expires = 0;
|
||||
|
||||
@@ -432,6 +452,17 @@ void pm_stay_awake(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_stay_awake);
|
||||
|
||||
#ifdef CONFIG_PM_AUTOSLEEP
|
||||
static void update_prevent_sleep_time(struct wakeup_source *ws, ktime_t now)
|
||||
{
|
||||
ktime_t delta = ktime_sub(now, ws->start_prevent_time);
|
||||
ws->prevent_sleep_time = ktime_add(ws->prevent_sleep_time, delta);
|
||||
}
|
||||
#else
|
||||
static inline void update_prevent_sleep_time(struct wakeup_source *ws,
|
||||
ktime_t now) {}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* wakup_source_deactivate - Mark given wakeup source as inactive.
|
||||
* @ws: Wakeup source to handle.
|
||||
@@ -442,6 +473,7 @@ EXPORT_SYMBOL_GPL(pm_stay_awake);
|
||||
*/
|
||||
static void wakeup_source_deactivate(struct wakeup_source *ws)
|
||||
{
|
||||
unsigned int cnt, inpr, cec;
|
||||
ktime_t duration;
|
||||
ktime_t now;
|
||||
|
||||
@@ -468,14 +500,23 @@ static void wakeup_source_deactivate(struct wakeup_source *ws)
|
||||
if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time))
|
||||
ws->max_time = duration;
|
||||
|
||||
ws->last_time = now;
|
||||
del_timer(&ws->timer);
|
||||
ws->timer_expires = 0;
|
||||
|
||||
if (ws->autosleep_enabled)
|
||||
update_prevent_sleep_time(ws, now);
|
||||
|
||||
/*
|
||||
* Increment the counter of registered wakeup events and decrement the
|
||||
* couter of wakeup events in progress simultaneously.
|
||||
*/
|
||||
atomic_add(MAX_IN_PROGRESS, &combined_event_count);
|
||||
cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count);
|
||||
trace_wakeup_source_deactivate(ws->name, cec);
|
||||
|
||||
split_counters(&cnt, &inpr);
|
||||
if (!inpr && waitqueue_active(&wakeup_count_wait_queue))
|
||||
wake_up(&wakeup_count_wait_queue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -536,8 +577,10 @@ static void pm_wakeup_timer_fn(unsigned long data)
|
||||
spin_lock_irqsave(&ws->lock, flags);
|
||||
|
||||
if (ws->active && ws->timer_expires
|
||||
&& time_after_eq(jiffies, ws->timer_expires))
|
||||
&& time_after_eq(jiffies, ws->timer_expires)) {
|
||||
wakeup_source_deactivate(ws);
|
||||
ws->expire_count++;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&ws->lock, flags);
|
||||
}
|
||||
@@ -564,9 +607,7 @@ void __pm_wakeup_event(struct wakeup_source *ws, unsigned int msec)
|
||||
|
||||
spin_lock_irqsave(&ws->lock, flags);
|
||||
|
||||
ws->event_count++;
|
||||
if (!ws->active)
|
||||
wakeup_source_activate(ws);
|
||||
wakeup_source_report_event(ws);
|
||||
|
||||
if (!msec) {
|
||||
wakeup_source_deactivate(ws);
|
||||
@@ -608,24 +649,6 @@ void pm_wakeup_event(struct device *dev, unsigned int msec)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pm_wakeup_event);
|
||||
|
||||
/**
|
||||
* pm_wakeup_update_hit_counts - Update hit counts of all active wakeup sources.
|
||||
*/
|
||||
static void pm_wakeup_update_hit_counts(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct wakeup_source *ws;
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
|
||||
spin_lock_irqsave(&ws->lock, flags);
|
||||
if (ws->active)
|
||||
ws->hit_count++;
|
||||
spin_unlock_irqrestore(&ws->lock, flags);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_wakeup_pending - Check if power transition in progress should be aborted.
|
||||
*
|
||||
@@ -648,32 +671,38 @@ bool pm_wakeup_pending(void)
|
||||
events_check_enabled = !ret;
|
||||
}
|
||||
spin_unlock_irqrestore(&events_lock, flags);
|
||||
if (ret)
|
||||
pm_wakeup_update_hit_counts();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* pm_get_wakeup_count - Read the number of registered wakeup events.
|
||||
* @count: Address to store the value at.
|
||||
* @block: Whether or not to block.
|
||||
*
|
||||
* Store the number of registered wakeup events at the address in @count. Block
|
||||
* if the current number of wakeup events being processed is nonzero.
|
||||
* Store the number of registered wakeup events at the address in @count. If
|
||||
* @block is set, block until the current number of wakeup events being
|
||||
* processed is zero.
|
||||
*
|
||||
* Return 'false' if the wait for the number of wakeup events being processed to
|
||||
* drop down to zero has been interrupted by a signal (and the current number
|
||||
* of wakeup events being processed is still nonzero). Otherwise return 'true'.
|
||||
* Return 'false' if the current number of wakeup events being processed is
|
||||
* nonzero. Otherwise return 'true'.
|
||||
*/
|
||||
bool pm_get_wakeup_count(unsigned int *count)
|
||||
bool pm_get_wakeup_count(unsigned int *count, bool block)
|
||||
{
|
||||
unsigned int cnt, inpr;
|
||||
|
||||
for (;;) {
|
||||
split_counters(&cnt, &inpr);
|
||||
if (inpr == 0 || signal_pending(current))
|
||||
break;
|
||||
pm_wakeup_update_hit_counts();
|
||||
schedule_timeout_interruptible(msecs_to_jiffies(TIMEOUT));
|
||||
if (block) {
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
for (;;) {
|
||||
prepare_to_wait(&wakeup_count_wait_queue, &wait,
|
||||
TASK_INTERRUPTIBLE);
|
||||
split_counters(&cnt, &inpr);
|
||||
if (inpr == 0 || signal_pending(current))
|
||||
break;
|
||||
|
||||
schedule();
|
||||
}
|
||||
finish_wait(&wakeup_count_wait_queue, &wait);
|
||||
}
|
||||
|
||||
split_counters(&cnt, &inpr);
|
||||
@@ -703,11 +732,37 @@ bool pm_save_wakeup_count(unsigned int count)
|
||||
events_check_enabled = true;
|
||||
}
|
||||
spin_unlock_irq(&events_lock);
|
||||
if (!events_check_enabled)
|
||||
pm_wakeup_update_hit_counts();
|
||||
return events_check_enabled;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_AUTOSLEEP
|
||||
/**
|
||||
* pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources.
|
||||
* @enabled: Whether to set or to clear the autosleep_enabled flags.
|
||||
*/
|
||||
void pm_wakep_autosleep_enabled(bool set)
|
||||
{
|
||||
struct wakeup_source *ws;
|
||||
ktime_t now = ktime_get();
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
|
||||
spin_lock_irq(&ws->lock);
|
||||
if (ws->autosleep_enabled != set) {
|
||||
ws->autosleep_enabled = set;
|
||||
if (ws->active) {
|
||||
if (set)
|
||||
ws->start_prevent_time = now;
|
||||
else
|
||||
update_prevent_sleep_time(ws, now);
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&ws->lock);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
#endif /* CONFIG_PM_AUTOSLEEP */
|
||||
|
||||
static struct dentry *wakeup_sources_stats_dentry;
|
||||
|
||||
/**
|
||||
@@ -723,27 +778,37 @@ static int print_wakeup_source_stats(struct seq_file *m,
|
||||
ktime_t max_time;
|
||||
unsigned long active_count;
|
||||
ktime_t active_time;
|
||||
ktime_t prevent_sleep_time;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&ws->lock, flags);
|
||||
|
||||
total_time = ws->total_time;
|
||||
max_time = ws->max_time;
|
||||
prevent_sleep_time = ws->prevent_sleep_time;
|
||||
active_count = ws->active_count;
|
||||
if (ws->active) {
|
||||
active_time = ktime_sub(ktime_get(), ws->last_time);
|
||||
ktime_t now = ktime_get();
|
||||
|
||||
active_time = ktime_sub(now, ws->last_time);
|
||||
total_time = ktime_add(total_time, active_time);
|
||||
if (active_time.tv64 > max_time.tv64)
|
||||
max_time = active_time;
|
||||
|
||||
if (ws->autosleep_enabled)
|
||||
prevent_sleep_time = ktime_add(prevent_sleep_time,
|
||||
ktime_sub(now, ws->start_prevent_time));
|
||||
} else {
|
||||
active_time = ktime_set(0, 0);
|
||||
}
|
||||
|
||||
ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t"
|
||||
"%lld\t\t%lld\t\t%lld\t\t%lld\n",
|
||||
ws->name, active_count, ws->event_count, ws->hit_count,
|
||||
ret = seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t"
|
||||
"%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n",
|
||||
ws->name, active_count, ws->event_count,
|
||||
ws->wakeup_count, ws->expire_count,
|
||||
ktime_to_ms(active_time), ktime_to_ms(total_time),
|
||||
ktime_to_ms(max_time), ktime_to_ms(ws->last_time));
|
||||
ktime_to_ms(max_time), ktime_to_ms(ws->last_time),
|
||||
ktime_to_ms(prevent_sleep_time));
|
||||
|
||||
spin_unlock_irqrestore(&ws->lock, flags);
|
||||
|
||||
@@ -758,8 +823,9 @@ static int wakeup_sources_stats_show(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct wakeup_source *ws;
|
||||
|
||||
seq_puts(m, "name\t\tactive_count\tevent_count\thit_count\t"
|
||||
"active_since\ttotal_time\tmax_time\tlast_change\n");
|
||||
seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t"
|
||||
"expire_count\tactive_since\ttotal_time\tmax_time\t"
|
||||
"last_change\tprevent_suspend_time\n");
|
||||
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(ws, &wakeup_sources, entry)
|
||||
|
||||
@@ -6,6 +6,7 @@ config REGMAP
|
||||
default y if (REGMAP_I2C || REGMAP_SPI)
|
||||
select LZO_COMPRESS
|
||||
select LZO_DECOMPRESS
|
||||
select IRQ_DOMAIN if REGMAP_IRQ
|
||||
bool
|
||||
|
||||
config REGMAP_I2C
|
||||
@@ -14,5 +15,8 @@ config REGMAP_I2C
|
||||
config REGMAP_SPI
|
||||
tristate
|
||||
|
||||
config REGMAP_MMIO
|
||||
tristate
|
||||
|
||||
config REGMAP_IRQ
|
||||
bool
|
||||
|
||||
@@ -3,4 +3,5 @@ obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o
|
||||
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
|
||||
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
|
||||
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
|
||||
obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o
|
||||
obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
|
||||
|
||||
@@ -26,21 +26,30 @@ struct regmap_format {
|
||||
size_t val_bytes;
|
||||
void (*format_write)(struct regmap *map,
|
||||
unsigned int reg, unsigned int val);
|
||||
void (*format_reg)(void *buf, unsigned int reg);
|
||||
void (*format_val)(void *buf, unsigned int val);
|
||||
void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
|
||||
void (*format_val)(void *buf, unsigned int val, unsigned int shift);
|
||||
unsigned int (*parse_val)(void *buf);
|
||||
};
|
||||
|
||||
typedef void (*regmap_lock)(struct regmap *map);
|
||||
typedef void (*regmap_unlock)(struct regmap *map);
|
||||
|
||||
struct regmap {
|
||||
struct mutex lock;
|
||||
struct mutex mutex;
|
||||
spinlock_t spinlock;
|
||||
regmap_lock lock;
|
||||
regmap_unlock unlock;
|
||||
|
||||
struct device *dev; /* Device we do I/O on */
|
||||
void *work_buf; /* Scratch buffer used to format I/O */
|
||||
struct regmap_format format; /* Buffer format */
|
||||
const struct regmap_bus *bus;
|
||||
void *bus_context;
|
||||
const char *name;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs;
|
||||
const char *debugfs_name;
|
||||
#endif
|
||||
|
||||
unsigned int max_register;
|
||||
@@ -52,6 +61,10 @@ struct regmap {
|
||||
u8 read_flag_mask;
|
||||
u8 write_flag_mask;
|
||||
|
||||
/* number of bits to (left) shift the reg value when formatting*/
|
||||
int reg_shift;
|
||||
int reg_stride;
|
||||
|
||||
/* regcache specific members */
|
||||
const struct regcache_ops *cache_ops;
|
||||
enum regcache_type cache_type;
|
||||
@@ -79,6 +92,9 @@ struct regmap {
|
||||
|
||||
struct reg_default *patch;
|
||||
int patch_regs;
|
||||
|
||||
/* if set, converts bulk rw to single rw */
|
||||
bool use_single_rw;
|
||||
};
|
||||
|
||||
struct regcache_ops {
|
||||
@@ -101,11 +117,11 @@ int _regmap_write(struct regmap *map, unsigned int reg,
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
extern void regmap_debugfs_initcall(void);
|
||||
extern void regmap_debugfs_init(struct regmap *map);
|
||||
extern void regmap_debugfs_init(struct regmap *map, const char *name);
|
||||
extern void regmap_debugfs_exit(struct regmap *map);
|
||||
#else
|
||||
static inline void regmap_debugfs_initcall(void) { }
|
||||
static inline void regmap_debugfs_init(struct regmap *map) { }
|
||||
static inline void regmap_debugfs_init(struct regmap *map, const char *name) { }
|
||||
static inline void regmap_debugfs_exit(struct regmap *map) { }
|
||||
#endif
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ static int regcache_lzo_decompress_cache_block(struct regmap *map,
|
||||
static inline int regcache_lzo_get_blkindex(struct regmap *map,
|
||||
unsigned int reg)
|
||||
{
|
||||
return (reg * map->cache_word_size) /
|
||||
return ((reg / map->reg_stride) * map->cache_word_size) /
|
||||
DIV_ROUND_UP(map->cache_size_raw,
|
||||
regcache_lzo_block_count(map));
|
||||
}
|
||||
@@ -116,9 +116,10 @@ static inline int regcache_lzo_get_blkindex(struct regmap *map,
|
||||
static inline int regcache_lzo_get_blkpos(struct regmap *map,
|
||||
unsigned int reg)
|
||||
{
|
||||
return reg % (DIV_ROUND_UP(map->cache_size_raw,
|
||||
regcache_lzo_block_count(map)) /
|
||||
map->cache_word_size);
|
||||
return (reg / map->reg_stride) %
|
||||
(DIV_ROUND_UP(map->cache_size_raw,
|
||||
regcache_lzo_block_count(map)) /
|
||||
map->cache_word_size);
|
||||
}
|
||||
|
||||
static inline int regcache_lzo_get_blksize(struct regmap *map)
|
||||
@@ -322,7 +323,7 @@ static int regcache_lzo_write(struct regmap *map,
|
||||
}
|
||||
|
||||
/* set the bit so we know we have to sync this register */
|
||||
set_bit(reg, lzo_block->sync_bmp);
|
||||
set_bit(reg / map->reg_stride, lzo_block->sync_bmp);
|
||||
kfree(tmp_dst);
|
||||
kfree(lzo_block->src);
|
||||
return 0;
|
||||
|
||||
@@ -39,11 +39,12 @@ struct regcache_rbtree_ctx {
|
||||
};
|
||||
|
||||
static inline void regcache_rbtree_get_base_top_reg(
|
||||
struct regmap *map,
|
||||
struct regcache_rbtree_node *rbnode,
|
||||
unsigned int *base, unsigned int *top)
|
||||
{
|
||||
*base = rbnode->base_reg;
|
||||
*top = rbnode->base_reg + rbnode->blklen - 1;
|
||||
*top = rbnode->base_reg + ((rbnode->blklen - 1) * map->reg_stride);
|
||||
}
|
||||
|
||||
static unsigned int regcache_rbtree_get_register(
|
||||
@@ -70,7 +71,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
|
||||
|
||||
rbnode = rbtree_ctx->cached_rbnode;
|
||||
if (rbnode) {
|
||||
regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
|
||||
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
|
||||
&top_reg);
|
||||
if (reg >= base_reg && reg <= top_reg)
|
||||
return rbnode;
|
||||
}
|
||||
@@ -78,7 +80,8 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
|
||||
node = rbtree_ctx->root.rb_node;
|
||||
while (node) {
|
||||
rbnode = container_of(node, struct regcache_rbtree_node, node);
|
||||
regcache_rbtree_get_base_top_reg(rbnode, &base_reg, &top_reg);
|
||||
regcache_rbtree_get_base_top_reg(map, rbnode, &base_reg,
|
||||
&top_reg);
|
||||
if (reg >= base_reg && reg <= top_reg) {
|
||||
rbtree_ctx->cached_rbnode = rbnode;
|
||||
return rbnode;
|
||||
@@ -92,7 +95,7 @@ static struct regcache_rbtree_node *regcache_rbtree_lookup(struct regmap *map,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int regcache_rbtree_insert(struct rb_root *root,
|
||||
static int regcache_rbtree_insert(struct regmap *map, struct rb_root *root,
|
||||
struct regcache_rbtree_node *rbnode)
|
||||
{
|
||||
struct rb_node **new, *parent;
|
||||
@@ -106,7 +109,7 @@ static int regcache_rbtree_insert(struct rb_root *root,
|
||||
rbnode_tmp = container_of(*new, struct regcache_rbtree_node,
|
||||
node);
|
||||
/* base and top registers of the current rbnode */
|
||||
regcache_rbtree_get_base_top_reg(rbnode_tmp, &base_reg_tmp,
|
||||
regcache_rbtree_get_base_top_reg(map, rbnode_tmp, &base_reg_tmp,
|
||||
&top_reg_tmp);
|
||||
/* base register of the rbnode to be added */
|
||||
base_reg = rbnode->base_reg;
|
||||
@@ -138,19 +141,20 @@ static int rbtree_show(struct seq_file *s, void *ignored)
|
||||
unsigned int base, top;
|
||||
int nodes = 0;
|
||||
int registers = 0;
|
||||
int average;
|
||||
int this_registers, average;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
for (node = rb_first(&rbtree_ctx->root); node != NULL;
|
||||
node = rb_next(node)) {
|
||||
n = container_of(node, struct regcache_rbtree_node, node);
|
||||
|
||||
regcache_rbtree_get_base_top_reg(n, &base, &top);
|
||||
seq_printf(s, "%x-%x (%d)\n", base, top, top - base + 1);
|
||||
regcache_rbtree_get_base_top_reg(map, n, &base, &top);
|
||||
this_registers = ((top - base) / map->reg_stride) + 1;
|
||||
seq_printf(s, "%x-%x (%d)\n", base, top, this_registers);
|
||||
|
||||
nodes++;
|
||||
registers += top - base + 1;
|
||||
registers += this_registers;
|
||||
}
|
||||
|
||||
if (nodes)
|
||||
@@ -161,7 +165,7 @@ static int rbtree_show(struct seq_file *s, void *ignored)
|
||||
seq_printf(s, "%d nodes, %d registers, average %d registers\n",
|
||||
nodes, registers, average);
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -255,7 +259,7 @@ static int regcache_rbtree_read(struct regmap *map,
|
||||
|
||||
rbnode = regcache_rbtree_lookup(map, reg);
|
||||
if (rbnode) {
|
||||
reg_tmp = reg - rbnode->base_reg;
|
||||
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
|
||||
*value = regcache_rbtree_get_register(rbnode, reg_tmp,
|
||||
map->cache_word_size);
|
||||
} else {
|
||||
@@ -310,7 +314,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
|
||||
*/
|
||||
rbnode = regcache_rbtree_lookup(map, reg);
|
||||
if (rbnode) {
|
||||
reg_tmp = reg - rbnode->base_reg;
|
||||
reg_tmp = (reg - rbnode->base_reg) / map->reg_stride;
|
||||
val = regcache_rbtree_get_register(rbnode, reg_tmp,
|
||||
map->cache_word_size);
|
||||
if (val == value)
|
||||
@@ -321,13 +325,15 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
|
||||
/* look for an adjacent register to the one we are about to add */
|
||||
for (node = rb_first(&rbtree_ctx->root); node;
|
||||
node = rb_next(node)) {
|
||||
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node, node);
|
||||
rbnode_tmp = rb_entry(node, struct regcache_rbtree_node,
|
||||
node);
|
||||
for (i = 0; i < rbnode_tmp->blklen; i++) {
|
||||
reg_tmp = rbnode_tmp->base_reg + i;
|
||||
if (abs(reg_tmp - reg) != 1)
|
||||
reg_tmp = rbnode_tmp->base_reg +
|
||||
(i * map->reg_stride);
|
||||
if (abs(reg_tmp - reg) != map->reg_stride)
|
||||
continue;
|
||||
/* decide where in the block to place our register */
|
||||
if (reg_tmp + 1 == reg)
|
||||
if (reg_tmp + map->reg_stride == reg)
|
||||
pos = i + 1;
|
||||
else
|
||||
pos = i;
|
||||
@@ -357,7 +363,7 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
|
||||
return -ENOMEM;
|
||||
}
|
||||
regcache_rbtree_set_register(rbnode, 0, value, map->cache_word_size);
|
||||
regcache_rbtree_insert(&rbtree_ctx->root, rbnode);
|
||||
regcache_rbtree_insert(map, &rbtree_ctx->root, rbnode);
|
||||
rbtree_ctx->cached_rbnode = rbnode;
|
||||
}
|
||||
|
||||
@@ -397,7 +403,7 @@ static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
|
||||
end = rbnode->blklen;
|
||||
|
||||
for (i = base; i < end; i++) {
|
||||
regtmp = rbnode->base_reg + i;
|
||||
regtmp = rbnode->base_reg + (i * map->reg_stride);
|
||||
val = regcache_rbtree_get_register(rbnode, i,
|
||||
map->cache_word_size);
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ static int regcache_hw_init(struct regmap *map)
|
||||
for (count = 0, i = 0; i < map->num_reg_defaults_raw; i++) {
|
||||
val = regcache_get_val(map->reg_defaults_raw,
|
||||
i, map->cache_word_size);
|
||||
if (regmap_volatile(map, i))
|
||||
if (regmap_volatile(map, i * map->reg_stride))
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
@@ -76,9 +76,9 @@ static int regcache_hw_init(struct regmap *map)
|
||||
for (i = 0, j = 0; i < map->num_reg_defaults_raw; i++) {
|
||||
val = regcache_get_val(map->reg_defaults_raw,
|
||||
i, map->cache_word_size);
|
||||
if (regmap_volatile(map, i))
|
||||
if (regmap_volatile(map, i * map->reg_stride))
|
||||
continue;
|
||||
map->reg_defaults[j].reg = i;
|
||||
map->reg_defaults[j].reg = i * map->reg_stride;
|
||||
map->reg_defaults[j].def = val;
|
||||
j++;
|
||||
}
|
||||
@@ -98,6 +98,10 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
|
||||
int i;
|
||||
void *tmp_buf;
|
||||
|
||||
for (i = 0; i < config->num_reg_defaults; i++)
|
||||
if (config->reg_defaults[i].reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
if (map->cache_type == REGCACHE_NONE) {
|
||||
map->cache_bypass = true;
|
||||
return 0;
|
||||
@@ -264,7 +268,7 @@ int regcache_sync(struct regmap *map)
|
||||
|
||||
BUG_ON(!map->cache_ops || !map->cache_ops->sync);
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
/* Remember the initial bypass state */
|
||||
bypass = map->cache_bypass;
|
||||
dev_dbg(map->dev, "Syncing %s cache\n",
|
||||
@@ -278,6 +282,10 @@ int regcache_sync(struct regmap *map)
|
||||
/* Apply any patch first */
|
||||
map->cache_bypass = 1;
|
||||
for (i = 0; i < map->patch_regs; i++) {
|
||||
if (map->patch[i].reg % map->reg_stride) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def);
|
||||
if (ret != 0) {
|
||||
dev_err(map->dev, "Failed to write %x = %x: %d\n",
|
||||
@@ -296,7 +304,7 @@ out:
|
||||
trace_regcache_sync(map->dev, name, "stop");
|
||||
/* Restore the bypass state */
|
||||
map->cache_bypass = bypass;
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -323,7 +331,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
|
||||
|
||||
BUG_ON(!map->cache_ops || !map->cache_ops->sync);
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
/* Remember the initial bypass state */
|
||||
bypass = map->cache_bypass;
|
||||
@@ -342,7 +350,7 @@ out:
|
||||
trace_regcache_sync(map->dev, name, "stop region");
|
||||
/* Restore the bypass state */
|
||||
map->cache_bypass = bypass;
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -362,11 +370,11 @@ EXPORT_SYMBOL_GPL(regcache_sync_region);
|
||||
*/
|
||||
void regcache_cache_only(struct regmap *map, bool enable)
|
||||
{
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
WARN_ON(map->cache_bypass && enable);
|
||||
map->cache_only = enable;
|
||||
trace_regmap_cache_only(map->dev, enable);
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regcache_cache_only);
|
||||
|
||||
@@ -381,9 +389,9 @@ EXPORT_SYMBOL_GPL(regcache_cache_only);
|
||||
*/
|
||||
void regcache_mark_dirty(struct regmap *map)
|
||||
{
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
map->cache_dirty = true;
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regcache_mark_dirty);
|
||||
|
||||
@@ -400,11 +408,11 @@ EXPORT_SYMBOL_GPL(regcache_mark_dirty);
|
||||
*/
|
||||
void regcache_cache_bypass(struct regmap *map, bool enable)
|
||||
{
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
WARN_ON(map->cache_only && enable);
|
||||
map->cache_bypass = enable;
|
||||
trace_regmap_cache_bypass(map->dev, enable);
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
|
||||
val_len = 2 * map->format.val_bytes;
|
||||
tot_len = reg_len + val_len + 3; /* : \n */
|
||||
|
||||
for (i = 0; i < map->max_register + 1; i++) {
|
||||
for (i = 0; i <= map->max_register; i += map->reg_stride) {
|
||||
if (!regmap_readable(map, i))
|
||||
continue;
|
||||
|
||||
@@ -197,7 +197,7 @@ static ssize_t regmap_access_read_file(struct file *file,
|
||||
reg_len = regmap_calc_reg_len(map->max_register, buf, count);
|
||||
tot_len = reg_len + 10; /* ': R W V P\n' */
|
||||
|
||||
for (i = 0; i < map->max_register + 1; i++) {
|
||||
for (i = 0; i <= map->max_register; i += map->reg_stride) {
|
||||
/* Ignore registers which are neither readable nor writable */
|
||||
if (!regmap_readable(map, i) && !regmap_writeable(map, i))
|
||||
continue;
|
||||
@@ -242,10 +242,17 @@ static const struct file_operations regmap_access_fops = {
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
void regmap_debugfs_init(struct regmap *map)
|
||||
void regmap_debugfs_init(struct regmap *map, const char *name)
|
||||
{
|
||||
map->debugfs = debugfs_create_dir(dev_name(map->dev),
|
||||
regmap_debugfs_root);
|
||||
if (name) {
|
||||
map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
|
||||
dev_name(map->dev), name);
|
||||
name = map->debugfs_name;
|
||||
} else {
|
||||
name = dev_name(map->dev);
|
||||
}
|
||||
|
||||
map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
|
||||
if (!map->debugfs) {
|
||||
dev_warn(map->dev, "Failed to create debugfs directory\n");
|
||||
return;
|
||||
@@ -274,6 +281,7 @@ void regmap_debugfs_init(struct regmap *map)
|
||||
void regmap_debugfs_exit(struct regmap *map)
|
||||
{
|
||||
debugfs_remove_recursive(map->debugfs);
|
||||
kfree(map->debugfs_name);
|
||||
}
|
||||
|
||||
void regmap_debugfs_initcall(void)
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
static int regmap_i2c_write(struct device *dev, const void *data, size_t count)
|
||||
static int regmap_i2c_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
@@ -29,10 +30,11 @@ static int regmap_i2c_write(struct device *dev, const void *data, size_t count)
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int regmap_i2c_gather_write(struct device *dev,
|
||||
static int regmap_i2c_gather_write(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
const void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct i2c_msg xfer[2];
|
||||
int ret;
|
||||
@@ -40,7 +42,7 @@ static int regmap_i2c_gather_write(struct device *dev,
|
||||
/* If the I2C controller can't do a gather tell the core, it
|
||||
* will substitute in a linear write for us.
|
||||
*/
|
||||
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_PROTOCOL_MANGLING))
|
||||
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_NOSTART))
|
||||
return -ENOTSUPP;
|
||||
|
||||
xfer[0].addr = i2c->addr;
|
||||
@@ -62,10 +64,11 @@ static int regmap_i2c_gather_write(struct device *dev,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int regmap_i2c_read(struct device *dev,
|
||||
static int regmap_i2c_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct i2c_client *i2c = to_i2c_client(dev);
|
||||
struct i2c_msg xfer[2];
|
||||
int ret;
|
||||
@@ -107,7 +110,7 @@ static struct regmap_bus regmap_i2c = {
|
||||
struct regmap *regmap_init_i2c(struct i2c_client *i2c,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return regmap_init(&i2c->dev, ®map_i2c, config);
|
||||
return regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_init_i2c);
|
||||
|
||||
@@ -124,7 +127,7 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c);
|
||||
struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return devm_regmap_init(&i2c->dev, ®map_i2c, config);
|
||||
return devm_regmap_init(&i2c->dev, ®map_i2c, &i2c->dev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "internal.h"
|
||||
@@ -26,18 +27,20 @@ struct regmap_irq_chip_data {
|
||||
struct regmap_irq_chip *chip;
|
||||
|
||||
int irq_base;
|
||||
struct irq_domain *domain;
|
||||
|
||||
void *status_reg_buf;
|
||||
unsigned int *status_buf;
|
||||
unsigned int *mask_buf;
|
||||
unsigned int *mask_buf_def;
|
||||
|
||||
unsigned int irq_reg_stride;
|
||||
};
|
||||
|
||||
static inline const
|
||||
struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
|
||||
int irq)
|
||||
{
|
||||
return &data->chip->irqs[irq - data->irq_base];
|
||||
return &data->chip->irqs[irq];
|
||||
}
|
||||
|
||||
static void regmap_irq_lock(struct irq_data *data)
|
||||
@@ -50,6 +53,7 @@ static void regmap_irq_lock(struct irq_data *data)
|
||||
static void regmap_irq_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
|
||||
struct regmap *map = d->map;
|
||||
int i, ret;
|
||||
|
||||
/*
|
||||
@@ -58,11 +62,13 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
|
||||
* suppress pointless writes.
|
||||
*/
|
||||
for (i = 0; i < d->chip->num_regs; i++) {
|
||||
ret = regmap_update_bits(d->map, d->chip->mask_base + i,
|
||||
ret = regmap_update_bits(d->map, d->chip->mask_base +
|
||||
(i * map->reg_stride *
|
||||
d->irq_reg_stride),
|
||||
d->mask_buf_def[i], d->mask_buf[i]);
|
||||
if (ret != 0)
|
||||
dev_err(d->map->dev, "Failed to sync masks in %x\n",
|
||||
d->chip->mask_base + i);
|
||||
d->chip->mask_base + (i * map->reg_stride));
|
||||
}
|
||||
|
||||
mutex_unlock(&d->lock);
|
||||
@@ -71,17 +77,19 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
|
||||
static void regmap_irq_enable(struct irq_data *data)
|
||||
{
|
||||
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
|
||||
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq);
|
||||
struct regmap *map = d->map;
|
||||
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
|
||||
|
||||
d->mask_buf[irq_data->reg_offset] &= ~irq_data->mask;
|
||||
d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask;
|
||||
}
|
||||
|
||||
static void regmap_irq_disable(struct irq_data *data)
|
||||
{
|
||||
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
|
||||
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq);
|
||||
struct regmap *map = d->map;
|
||||
const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
|
||||
|
||||
d->mask_buf[irq_data->reg_offset] |= irq_data->mask;
|
||||
d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
|
||||
}
|
||||
|
||||
static struct irq_chip regmap_irq_chip = {
|
||||
@@ -98,18 +106,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
|
||||
struct regmap_irq_chip *chip = data->chip;
|
||||
struct regmap *map = data->map;
|
||||
int ret, i;
|
||||
u8 *buf8 = data->status_reg_buf;
|
||||
u16 *buf16 = data->status_reg_buf;
|
||||
u32 *buf32 = data->status_reg_buf;
|
||||
bool handled = false;
|
||||
|
||||
ret = regmap_bulk_read(map, chip->status_base, data->status_reg_buf,
|
||||
chip->num_regs);
|
||||
if (ret != 0) {
|
||||
dev_err(map->dev, "Failed to read IRQ status: %d\n", ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore masked IRQs and ack if we need to; we ack early so
|
||||
* there is no race between handling and acknowleding the
|
||||
@@ -118,36 +116,34 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
|
||||
* doing a write per register.
|
||||
*/
|
||||
for (i = 0; i < data->chip->num_regs; i++) {
|
||||
switch (map->format.val_bytes) {
|
||||
case 1:
|
||||
data->status_buf[i] = buf8[i];
|
||||
break;
|
||||
case 2:
|
||||
data->status_buf[i] = buf16[i];
|
||||
break;
|
||||
case 4:
|
||||
data->status_buf[i] = buf32[i];
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
ret = regmap_read(map, chip->status_base + (i * map->reg_stride
|
||||
* data->irq_reg_stride),
|
||||
&data->status_buf[i]);
|
||||
|
||||
if (ret != 0) {
|
||||
dev_err(map->dev, "Failed to read IRQ status: %d\n",
|
||||
ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
data->status_buf[i] &= ~data->mask_buf[i];
|
||||
|
||||
if (data->status_buf[i] && chip->ack_base) {
|
||||
ret = regmap_write(map, chip->ack_base + i,
|
||||
ret = regmap_write(map, chip->ack_base +
|
||||
(i * map->reg_stride *
|
||||
data->irq_reg_stride),
|
||||
data->status_buf[i]);
|
||||
if (ret != 0)
|
||||
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
|
||||
chip->ack_base + i, ret);
|
||||
chip->ack_base + (i * map->reg_stride),
|
||||
ret);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < chip->num_irqs; i++) {
|
||||
if (data->status_buf[chip->irqs[i].reg_offset] &
|
||||
chip->irqs[i].mask) {
|
||||
handle_nested_irq(data->irq_base + i);
|
||||
if (data->status_buf[chip->irqs[i].reg_offset /
|
||||
map->reg_stride] & chip->irqs[i].mask) {
|
||||
handle_nested_irq(irq_find_mapping(data->domain, i));
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
@@ -158,6 +154,31 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
|
||||
irq_hw_number_t hw)
|
||||
{
|
||||
struct regmap_irq_chip_data *data = h->host_data;
|
||||
|
||||
irq_set_chip_data(virq, data);
|
||||
irq_set_chip_and_handler(virq, ®map_irq_chip, handle_edge_irq);
|
||||
irq_set_nested_thread(virq, 1);
|
||||
|
||||
/* ARM needs us to explicitly flag the IRQ as valid
|
||||
* and will set them noprobe when we do so. */
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(virq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(virq);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct irq_domain_ops regmap_domain_ops = {
|
||||
.map = regmap_irq_map,
|
||||
.xlate = irq_domain_xlate_twocell,
|
||||
};
|
||||
|
||||
/**
|
||||
* regmap_add_irq_chip(): Use standard regmap IRQ controller handling
|
||||
*
|
||||
@@ -178,30 +199,37 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
struct regmap_irq_chip_data **data)
|
||||
{
|
||||
struct regmap_irq_chip_data *d;
|
||||
int cur_irq, i;
|
||||
int i;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
|
||||
if (irq_base < 0) {
|
||||
dev_warn(map->dev, "Failed to allocate IRQs: %d\n",
|
||||
irq_base);
|
||||
return irq_base;
|
||||
for (i = 0; i < chip->num_irqs; i++) {
|
||||
if (chip->irqs[i].reg_offset % map->reg_stride)
|
||||
return -EINVAL;
|
||||
if (chip->irqs[i].reg_offset / map->reg_stride >=
|
||||
chip->num_regs)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (irq_base) {
|
||||
irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
|
||||
if (irq_base < 0) {
|
||||
dev_warn(map->dev, "Failed to allocate IRQs: %d\n",
|
||||
irq_base);
|
||||
return irq_base;
|
||||
}
|
||||
}
|
||||
|
||||
d = kzalloc(sizeof(*d), GFP_KERNEL);
|
||||
if (!d)
|
||||
return -ENOMEM;
|
||||
|
||||
*data = d;
|
||||
|
||||
d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
|
||||
GFP_KERNEL);
|
||||
if (!d->status_buf)
|
||||
goto err_alloc;
|
||||
|
||||
d->status_reg_buf = kzalloc(map->format.val_bytes * chip->num_regs,
|
||||
GFP_KERNEL);
|
||||
if (!d->status_reg_buf)
|
||||
goto err_alloc;
|
||||
|
||||
d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
|
||||
GFP_KERNEL);
|
||||
if (!d->mask_buf)
|
||||
@@ -215,54 +243,59 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
|
||||
d->map = map;
|
||||
d->chip = chip;
|
||||
d->irq_base = irq_base;
|
||||
|
||||
if (chip->irq_reg_stride)
|
||||
d->irq_reg_stride = chip->irq_reg_stride;
|
||||
else
|
||||
d->irq_reg_stride = 1;
|
||||
|
||||
mutex_init(&d->lock);
|
||||
|
||||
for (i = 0; i < chip->num_irqs; i++)
|
||||
d->mask_buf_def[chip->irqs[i].reg_offset]
|
||||
d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride]
|
||||
|= chip->irqs[i].mask;
|
||||
|
||||
/* Mask all the interrupts by default */
|
||||
for (i = 0; i < chip->num_regs; i++) {
|
||||
d->mask_buf[i] = d->mask_buf_def[i];
|
||||
ret = regmap_write(map, chip->mask_base + i, d->mask_buf[i]);
|
||||
ret = regmap_write(map, chip->mask_base + (i * map->reg_stride
|
||||
* d->irq_reg_stride),
|
||||
d->mask_buf[i]);
|
||||
if (ret != 0) {
|
||||
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
|
||||
chip->mask_base + i, ret);
|
||||
chip->mask_base + (i * map->reg_stride), ret);
|
||||
goto err_alloc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Register them with genirq */
|
||||
for (cur_irq = irq_base;
|
||||
cur_irq < chip->num_irqs + irq_base;
|
||||
cur_irq++) {
|
||||
irq_set_chip_data(cur_irq, d);
|
||||
irq_set_chip_and_handler(cur_irq, ®map_irq_chip,
|
||||
handle_edge_irq);
|
||||
irq_set_nested_thread(cur_irq, 1);
|
||||
|
||||
/* ARM needs us to explicitly flag the IRQ as valid
|
||||
* and will set them noprobe when we do so. */
|
||||
#ifdef CONFIG_ARM
|
||||
set_irq_flags(cur_irq, IRQF_VALID);
|
||||
#else
|
||||
irq_set_noprobe(cur_irq);
|
||||
#endif
|
||||
if (irq_base)
|
||||
d->domain = irq_domain_add_legacy(map->dev->of_node,
|
||||
chip->num_irqs, irq_base, 0,
|
||||
®map_domain_ops, d);
|
||||
else
|
||||
d->domain = irq_domain_add_linear(map->dev->of_node,
|
||||
chip->num_irqs,
|
||||
®map_domain_ops, d);
|
||||
if (!d->domain) {
|
||||
dev_err(map->dev, "Failed to create IRQ domain\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags,
|
||||
chip->name, d);
|
||||
if (ret != 0) {
|
||||
dev_err(map->dev, "Failed to request IRQ %d: %d\n", irq, ret);
|
||||
goto err_alloc;
|
||||
goto err_domain;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_domain:
|
||||
/* Should really dispose of the domain but... */
|
||||
err_alloc:
|
||||
kfree(d->mask_buf_def);
|
||||
kfree(d->mask_buf);
|
||||
kfree(d->status_reg_buf);
|
||||
kfree(d->status_buf);
|
||||
kfree(d);
|
||||
return ret;
|
||||
@@ -281,9 +314,9 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
|
||||
return;
|
||||
|
||||
free_irq(irq, d);
|
||||
/* We should unmap the domain but... */
|
||||
kfree(d->mask_buf_def);
|
||||
kfree(d->mask_buf);
|
||||
kfree(d->status_reg_buf);
|
||||
kfree(d->status_buf);
|
||||
kfree(d);
|
||||
}
|
||||
@@ -298,6 +331,21 @@ EXPORT_SYMBOL_GPL(regmap_del_irq_chip);
|
||||
*/
|
||||
int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data)
|
||||
{
|
||||
WARN_ON(!data->irq_base);
|
||||
return data->irq_base;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base);
|
||||
|
||||
/**
|
||||
* regmap_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
|
||||
*
|
||||
* Useful for drivers to request their own IRQs.
|
||||
*
|
||||
* @data: regmap_irq controller to operate on.
|
||||
* @irq: index of the interrupt requested in the chip IRQs
|
||||
*/
|
||||
int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq)
|
||||
{
|
||||
return irq_create_mapping(data->domain, irq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_irq_get_virq);
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Register map access API - MMIO support
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct regmap_mmio_context {
|
||||
void __iomem *regs;
|
||||
unsigned val_bytes;
|
||||
};
|
||||
|
||||
static int regmap_mmio_gather_write(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
const void *val, size_t val_size)
|
||||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
u32 offset;
|
||||
|
||||
BUG_ON(reg_size != 4);
|
||||
|
||||
offset = be32_to_cpup(reg);
|
||||
|
||||
while (val_size) {
|
||||
switch (ctx->val_bytes) {
|
||||
case 1:
|
||||
writeb(*(u8 *)val, ctx->regs + offset);
|
||||
break;
|
||||
case 2:
|
||||
writew(be16_to_cpup(val), ctx->regs + offset);
|
||||
break;
|
||||
case 4:
|
||||
writel(be32_to_cpup(val), ctx->regs + offset);
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
writeq(be64_to_cpup(val), ctx->regs + offset);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* Should be caught by regmap_mmio_check_config */
|
||||
BUG();
|
||||
}
|
||||
val_size -= ctx->val_bytes;
|
||||
val += ctx->val_bytes;
|
||||
offset += ctx->val_bytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int regmap_mmio_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
BUG_ON(count < 4);
|
||||
|
||||
return regmap_mmio_gather_write(context, data, 4, data + 4, count - 4);
|
||||
}
|
||||
|
||||
static int regmap_mmio_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct regmap_mmio_context *ctx = context;
|
||||
u32 offset;
|
||||
|
||||
BUG_ON(reg_size != 4);
|
||||
|
||||
offset = be32_to_cpup(reg);
|
||||
|
||||
while (val_size) {
|
||||
switch (ctx->val_bytes) {
|
||||
case 1:
|
||||
*(u8 *)val = readb(ctx->regs + offset);
|
||||
break;
|
||||
case 2:
|
||||
*(u16 *)val = cpu_to_be16(readw(ctx->regs + offset));
|
||||
break;
|
||||
case 4:
|
||||
*(u32 *)val = cpu_to_be32(readl(ctx->regs + offset));
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 8:
|
||||
*(u64 *)val = cpu_to_be32(readq(ctx->regs + offset));
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* Should be caught by regmap_mmio_check_config */
|
||||
BUG();
|
||||
}
|
||||
val_size -= ctx->val_bytes;
|
||||
val += ctx->val_bytes;
|
||||
offset += ctx->val_bytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void regmap_mmio_free_context(void *context)
|
||||
{
|
||||
kfree(context);
|
||||
}
|
||||
|
||||
static struct regmap_bus regmap_mmio = {
|
||||
.fast_io = true,
|
||||
.write = regmap_mmio_write,
|
||||
.gather_write = regmap_mmio_gather_write,
|
||||
.read = regmap_mmio_read,
|
||||
.free_context = regmap_mmio_free_context,
|
||||
};
|
||||
|
||||
struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap_mmio_context *ctx;
|
||||
int min_stride;
|
||||
|
||||
if (config->reg_bits != 32)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (config->pad_bits)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
switch (config->val_bits) {
|
||||
case 8:
|
||||
/* The core treats 0 as 1 */
|
||||
min_stride = 0;
|
||||
break;
|
||||
case 16:
|
||||
min_stride = 2;
|
||||
break;
|
||||
case 32:
|
||||
min_stride = 4;
|
||||
break;
|
||||
#ifdef CONFIG_64BIT
|
||||
case 64:
|
||||
min_stride = 8;
|
||||
break;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (config->reg_stride < min_stride)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
ctx = kzalloc(GFP_KERNEL, sizeof(*ctx));
|
||||
if (!ctx)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ctx->regs = regs;
|
||||
ctx->val_bytes = config->val_bits / 8;
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* regmap_init_mmio(): Initialise register map
|
||||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @regs: Pointer to memory-mapped IO region
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer to
|
||||
* a struct regmap.
|
||||
*/
|
||||
struct regmap *regmap_init_mmio(struct device *dev,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap_mmio_context *ctx;
|
||||
|
||||
ctx = regmap_mmio_gen_context(regs, config);
|
||||
if (IS_ERR(ctx))
|
||||
return ERR_CAST(ctx);
|
||||
|
||||
return regmap_init(dev, ®map_mmio, ctx, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_init_mmio);
|
||||
|
||||
/**
|
||||
* devm_regmap_init_mmio(): Initialise managed register map
|
||||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @regs: Pointer to memory-mapped IO region
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer
|
||||
* to a struct regmap. The regmap will be automatically freed by the
|
||||
* device management code.
|
||||
*/
|
||||
struct regmap *devm_regmap_init_mmio(struct device *dev,
|
||||
void __iomem *regs,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap_mmio_context *ctx;
|
||||
|
||||
ctx = regmap_mmio_gen_context(regs, config);
|
||||
if (IS_ERR(ctx))
|
||||
return ERR_CAST(ctx);
|
||||
|
||||
return devm_regmap_init(dev, ®map_mmio, ctx, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_mmio);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
@@ -15,17 +15,19 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static int regmap_spi_write(struct device *dev, const void *data, size_t count)
|
||||
static int regmap_spi_write(void *context, const void *data, size_t count)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
return spi_write(spi, data, count);
|
||||
}
|
||||
|
||||
static int regmap_spi_gather_write(struct device *dev,
|
||||
static int regmap_spi_gather_write(void *context,
|
||||
const void *reg, size_t reg_len,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct spi_message m;
|
||||
struct spi_transfer t[2] = { { .tx_buf = reg, .len = reg_len, },
|
||||
@@ -38,10 +40,11 @@ static int regmap_spi_gather_write(struct device *dev,
|
||||
return spi_sync(spi, &m);
|
||||
}
|
||||
|
||||
static int regmap_spi_read(struct device *dev,
|
||||
static int regmap_spi_read(void *context,
|
||||
const void *reg, size_t reg_size,
|
||||
void *val, size_t val_size)
|
||||
{
|
||||
struct device *dev = context;
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
|
||||
return spi_write_then_read(spi, reg, reg_size, val, val_size);
|
||||
@@ -66,7 +69,7 @@ static struct regmap_bus regmap_spi = {
|
||||
struct regmap *regmap_init_spi(struct spi_device *spi,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return regmap_init(&spi->dev, ®map_spi, config);
|
||||
return regmap_init(&spi->dev, ®map_spi, &spi->dev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_init_spi);
|
||||
|
||||
@@ -83,7 +86,7 @@ EXPORT_SYMBOL_GPL(regmap_init_spi);
|
||||
struct regmap *devm_regmap_init_spi(struct spi_device *spi,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
return devm_regmap_init(&spi->dev, ®map_spi, config);
|
||||
return devm_regmap_init(&spi->dev, ®map_spi, &spi->dev, config);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_regmap_init_spi);
|
||||
|
||||
|
||||
+231
-45
@@ -112,25 +112,36 @@ static void regmap_format_10_14_write(struct regmap *map,
|
||||
out[0] = reg >> 2;
|
||||
}
|
||||
|
||||
static void regmap_format_8(void *buf, unsigned int val)
|
||||
static void regmap_format_8(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
u8 *b = buf;
|
||||
|
||||
b[0] = val;
|
||||
b[0] = val << shift;
|
||||
}
|
||||
|
||||
static void regmap_format_16(void *buf, unsigned int val)
|
||||
static void regmap_format_16(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
__be16 *b = buf;
|
||||
|
||||
b[0] = cpu_to_be16(val);
|
||||
b[0] = cpu_to_be16(val << shift);
|
||||
}
|
||||
|
||||
static void regmap_format_32(void *buf, unsigned int val)
|
||||
static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
u8 *b = buf;
|
||||
|
||||
val <<= shift;
|
||||
|
||||
b[0] = val >> 16;
|
||||
b[1] = val >> 8;
|
||||
b[2] = val;
|
||||
}
|
||||
|
||||
static void regmap_format_32(void *buf, unsigned int val, unsigned int shift)
|
||||
{
|
||||
__be32 *b = buf;
|
||||
|
||||
b[0] = cpu_to_be32(val);
|
||||
b[0] = cpu_to_be32(val << shift);
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_8(void *buf)
|
||||
@@ -149,6 +160,16 @@ static unsigned int regmap_parse_16(void *buf)
|
||||
return b[0];
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_24(void *buf)
|
||||
{
|
||||
u8 *b = buf;
|
||||
unsigned int ret = b[2];
|
||||
ret |= ((unsigned int)b[1]) << 8;
|
||||
ret |= ((unsigned int)b[0]) << 16;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int regmap_parse_32(void *buf)
|
||||
{
|
||||
__be32 *b = buf;
|
||||
@@ -158,11 +179,41 @@ static unsigned int regmap_parse_32(void *buf)
|
||||
return b[0];
|
||||
}
|
||||
|
||||
static void regmap_lock_mutex(struct regmap *map)
|
||||
{
|
||||
mutex_lock(&map->mutex);
|
||||
}
|
||||
|
||||
static void regmap_unlock_mutex(struct regmap *map)
|
||||
{
|
||||
mutex_unlock(&map->mutex);
|
||||
}
|
||||
|
||||
static void regmap_lock_spinlock(struct regmap *map)
|
||||
{
|
||||
spin_lock(&map->spinlock);
|
||||
}
|
||||
|
||||
static void regmap_unlock_spinlock(struct regmap *map)
|
||||
{
|
||||
spin_unlock(&map->spinlock);
|
||||
}
|
||||
|
||||
static void dev_get_regmap_release(struct device *dev, void *res)
|
||||
{
|
||||
/*
|
||||
* We don't actually have anything to do here; the goal here
|
||||
* is not to manage the regmap but to provide a simple way to
|
||||
* get the regmap back given a struct device.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* regmap_init(): Initialise register map
|
||||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @bus: Bus-specific callbacks to use with device
|
||||
* @bus_context: Data passed to bus-specific callbacks
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer to
|
||||
@@ -171,9 +222,10 @@ static unsigned int regmap_parse_32(void *buf)
|
||||
*/
|
||||
struct regmap *regmap_init(struct device *dev,
|
||||
const struct regmap_bus *bus,
|
||||
void *bus_context,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap *map;
|
||||
struct regmap *map, **m;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!bus || !config)
|
||||
@@ -185,20 +237,36 @@ struct regmap *regmap_init(struct device *dev,
|
||||
goto err;
|
||||
}
|
||||
|
||||
mutex_init(&map->lock);
|
||||
if (bus->fast_io) {
|
||||
spin_lock_init(&map->spinlock);
|
||||
map->lock = regmap_lock_spinlock;
|
||||
map->unlock = regmap_unlock_spinlock;
|
||||
} else {
|
||||
mutex_init(&map->mutex);
|
||||
map->lock = regmap_lock_mutex;
|
||||
map->unlock = regmap_unlock_mutex;
|
||||
}
|
||||
map->format.buf_size = (config->reg_bits + config->val_bits) / 8;
|
||||
map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
|
||||
map->format.pad_bytes = config->pad_bits / 8;
|
||||
map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
|
||||
map->format.buf_size += map->format.pad_bytes;
|
||||
map->reg_shift = config->pad_bits % 8;
|
||||
if (config->reg_stride)
|
||||
map->reg_stride = config->reg_stride;
|
||||
else
|
||||
map->reg_stride = 1;
|
||||
map->use_single_rw = config->use_single_rw;
|
||||
map->dev = dev;
|
||||
map->bus = bus;
|
||||
map->bus_context = bus_context;
|
||||
map->max_register = config->max_register;
|
||||
map->writeable_reg = config->writeable_reg;
|
||||
map->readable_reg = config->readable_reg;
|
||||
map->volatile_reg = config->volatile_reg;
|
||||
map->precious_reg = config->precious_reg;
|
||||
map->cache_type = config->cache_type;
|
||||
map->name = config->name;
|
||||
|
||||
if (config->read_flag_mask || config->write_flag_mask) {
|
||||
map->read_flag_mask = config->read_flag_mask;
|
||||
@@ -207,7 +275,7 @@ struct regmap *regmap_init(struct device *dev,
|
||||
map->read_flag_mask = bus->read_flag_mask;
|
||||
}
|
||||
|
||||
switch (config->reg_bits) {
|
||||
switch (config->reg_bits + map->reg_shift) {
|
||||
case 2:
|
||||
switch (config->val_bits) {
|
||||
case 6:
|
||||
@@ -273,12 +341,19 @@ struct regmap *regmap_init(struct device *dev,
|
||||
map->format.format_val = regmap_format_16;
|
||||
map->format.parse_val = regmap_parse_16;
|
||||
break;
|
||||
case 24:
|
||||
map->format.format_val = regmap_format_24;
|
||||
map->format.parse_val = regmap_parse_24;
|
||||
break;
|
||||
case 32:
|
||||
map->format.format_val = regmap_format_32;
|
||||
map->format.parse_val = regmap_parse_32;
|
||||
break;
|
||||
}
|
||||
|
||||
if (map->format.format_write)
|
||||
map->use_single_rw = true;
|
||||
|
||||
if (!map->format.format_write &&
|
||||
!(map->format.format_reg && map->format.format_val))
|
||||
goto err_map;
|
||||
@@ -289,14 +364,25 @@ struct regmap *regmap_init(struct device *dev,
|
||||
goto err_map;
|
||||
}
|
||||
|
||||
regmap_debugfs_init(map);
|
||||
regmap_debugfs_init(map, config->name);
|
||||
|
||||
ret = regcache_init(map, config);
|
||||
if (ret < 0)
|
||||
goto err_free_workbuf;
|
||||
|
||||
/* Add a devres resource for dev_get_regmap() */
|
||||
m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL);
|
||||
if (!m) {
|
||||
ret = -ENOMEM;
|
||||
goto err_cache;
|
||||
}
|
||||
*m = map;
|
||||
devres_add(dev, m);
|
||||
|
||||
return map;
|
||||
|
||||
err_cache:
|
||||
regcache_exit(map);
|
||||
err_free_workbuf:
|
||||
kfree(map->work_buf);
|
||||
err_map:
|
||||
@@ -316,6 +402,7 @@ static void devm_regmap_release(struct device *dev, void *res)
|
||||
*
|
||||
* @dev: Device that will be interacted with
|
||||
* @bus: Bus-specific callbacks to use with device
|
||||
* @bus_context: Data passed to bus-specific callbacks
|
||||
* @config: Configuration for register map
|
||||
*
|
||||
* The return value will be an ERR_PTR() on error or a valid pointer
|
||||
@@ -325,6 +412,7 @@ static void devm_regmap_release(struct device *dev, void *res)
|
||||
*/
|
||||
struct regmap *devm_regmap_init(struct device *dev,
|
||||
const struct regmap_bus *bus,
|
||||
void *bus_context,
|
||||
const struct regmap_config *config)
|
||||
{
|
||||
struct regmap **ptr, *regmap;
|
||||
@@ -333,7 +421,7 @@ struct regmap *devm_regmap_init(struct device *dev,
|
||||
if (!ptr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
regmap = regmap_init(dev, bus, config);
|
||||
regmap = regmap_init(dev, bus, bus_context, config);
|
||||
if (!IS_ERR(regmap)) {
|
||||
*ptr = regmap;
|
||||
devres_add(dev, ptr);
|
||||
@@ -360,7 +448,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
regcache_exit(map);
|
||||
regmap_debugfs_exit(map);
|
||||
@@ -372,14 +460,14 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
|
||||
map->precious_reg = config->precious_reg;
|
||||
map->cache_type = config->cache_type;
|
||||
|
||||
regmap_debugfs_init(map);
|
||||
regmap_debugfs_init(map, config->name);
|
||||
|
||||
map->cache_bypass = false;
|
||||
map->cache_only = false;
|
||||
|
||||
ret = regcache_init(map, config);
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -391,11 +479,51 @@ void regmap_exit(struct regmap *map)
|
||||
{
|
||||
regcache_exit(map);
|
||||
regmap_debugfs_exit(map);
|
||||
if (map->bus->free_context)
|
||||
map->bus->free_context(map->bus_context);
|
||||
kfree(map->work_buf);
|
||||
kfree(map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_exit);
|
||||
|
||||
static int dev_get_regmap_match(struct device *dev, void *res, void *data)
|
||||
{
|
||||
struct regmap **r = res;
|
||||
if (!r || !*r) {
|
||||
WARN_ON(!r || !*r);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the user didn't specify a name match any */
|
||||
if (data)
|
||||
return (*r)->name == data;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_get_regmap(): Obtain the regmap (if any) for a device
|
||||
*
|
||||
* @dev: Device to retrieve the map for
|
||||
* @name: Optional name for the register map, usually NULL.
|
||||
*
|
||||
* Returns the regmap for the device if one is present, or NULL. If
|
||||
* name is specified then it must match the name specified when
|
||||
* registering the device, if it is NULL then the first regmap found
|
||||
* will be used. Devices with multiple register maps are very rare,
|
||||
* generic code should normally not need to specify a name.
|
||||
*/
|
||||
struct regmap *dev_get_regmap(struct device *dev, const char *name)
|
||||
{
|
||||
struct regmap **r = devres_find(dev, dev_get_regmap_release,
|
||||
dev_get_regmap_match, (void *)name);
|
||||
|
||||
if (!r)
|
||||
return NULL;
|
||||
return *r;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_get_regmap);
|
||||
|
||||
static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
const void *val, size_t val_len)
|
||||
{
|
||||
@@ -408,7 +536,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
/* Check for unwritable registers before we start */
|
||||
if (map->writeable_reg)
|
||||
for (i = 0; i < val_len / map->format.val_bytes; i++)
|
||||
if (!map->writeable_reg(map->dev, reg + i))
|
||||
if (!map->writeable_reg(map->dev,
|
||||
reg + (i * map->reg_stride)))
|
||||
return -EINVAL;
|
||||
|
||||
if (!map->cache_bypass && map->format.parse_val) {
|
||||
@@ -417,7 +546,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
for (i = 0; i < val_len / val_bytes; i++) {
|
||||
memcpy(map->work_buf, val + (i * val_bytes), val_bytes);
|
||||
ival = map->format.parse_val(map->work_buf);
|
||||
ret = regcache_write(map, reg + i, ival);
|
||||
ret = regcache_write(map, reg + (i * map->reg_stride),
|
||||
ival);
|
||||
if (ret) {
|
||||
dev_err(map->dev,
|
||||
"Error in caching of register: %u ret: %d\n",
|
||||
@@ -431,7 +561,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
}
|
||||
}
|
||||
|
||||
map->format.format_reg(map->work_buf, reg);
|
||||
map->format.format_reg(map->work_buf, reg, map->reg_shift);
|
||||
|
||||
u8[0] |= map->write_flag_mask;
|
||||
|
||||
@@ -444,12 +574,12 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
*/
|
||||
if (val == (map->work_buf + map->format.pad_bytes +
|
||||
map->format.reg_bytes))
|
||||
ret = map->bus->write(map->dev, map->work_buf,
|
||||
ret = map->bus->write(map->bus_context, map->work_buf,
|
||||
map->format.reg_bytes +
|
||||
map->format.pad_bytes +
|
||||
val_len);
|
||||
else if (map->bus->gather_write)
|
||||
ret = map->bus->gather_write(map->dev, map->work_buf,
|
||||
ret = map->bus->gather_write(map->bus_context, map->work_buf,
|
||||
map->format.reg_bytes +
|
||||
map->format.pad_bytes,
|
||||
val, val_len);
|
||||
@@ -464,7 +594,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
memcpy(buf, map->work_buf, map->format.reg_bytes);
|
||||
memcpy(buf + map->format.reg_bytes + map->format.pad_bytes,
|
||||
val, val_len);
|
||||
ret = map->bus->write(map->dev, buf, len);
|
||||
ret = map->bus->write(map->bus_context, buf, len);
|
||||
|
||||
kfree(buf);
|
||||
}
|
||||
@@ -498,7 +628,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
|
||||
|
||||
trace_regmap_hw_write_start(map->dev, reg, 1);
|
||||
|
||||
ret = map->bus->write(map->dev, map->work_buf,
|
||||
ret = map->bus->write(map->bus_context, map->work_buf,
|
||||
map->format.buf_size);
|
||||
|
||||
trace_regmap_hw_write_done(map->dev, reg, 1);
|
||||
@@ -506,7 +636,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
|
||||
return ret;
|
||||
} else {
|
||||
map->format.format_val(map->work_buf + map->format.reg_bytes
|
||||
+ map->format.pad_bytes, val);
|
||||
+ map->format.pad_bytes, val, 0);
|
||||
return _regmap_raw_write(map, reg,
|
||||
map->work_buf +
|
||||
map->format.reg_bytes +
|
||||
@@ -529,11 +659,14 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
map->lock(map);
|
||||
|
||||
ret = _regmap_write(map, reg, val);
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -560,11 +693,16 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
if (val_len % map->format.val_bytes)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
map->lock(map);
|
||||
|
||||
ret = _regmap_raw_write(map, reg, val, val_len);
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -593,8 +731,10 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
|
||||
|
||||
if (!map->format.parse_val)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
/* No formatting is require if val_byte is 1 */
|
||||
if (val_bytes == 1) {
|
||||
@@ -609,13 +749,28 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
|
||||
for (i = 0; i < val_count * val_bytes; i += val_bytes)
|
||||
map->format.parse_val(wval + i);
|
||||
}
|
||||
ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
|
||||
/*
|
||||
* Some devices does not support bulk write, for
|
||||
* them we have a series of single write operations.
|
||||
*/
|
||||
if (map->use_single_rw) {
|
||||
for (i = 0; i < val_count; i++) {
|
||||
ret = regmap_raw_write(map,
|
||||
reg + (i * map->reg_stride),
|
||||
val + (i * val_bytes),
|
||||
val_bytes);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
|
||||
}
|
||||
|
||||
if (val_bytes != 1)
|
||||
kfree(wval);
|
||||
|
||||
out:
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(regmap_bulk_write);
|
||||
@@ -626,7 +781,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
||||
u8 *u8 = map->work_buf;
|
||||
int ret;
|
||||
|
||||
map->format.format_reg(map->work_buf, reg);
|
||||
map->format.format_reg(map->work_buf, reg, map->reg_shift);
|
||||
|
||||
/*
|
||||
* Some buses or devices flag reads by setting the high bits in the
|
||||
@@ -639,7 +794,7 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
||||
trace_regmap_hw_read_start(map->dev, reg,
|
||||
val_len / map->format.val_bytes);
|
||||
|
||||
ret = map->bus->read(map->dev, map->work_buf,
|
||||
ret = map->bus->read(map->bus_context, map->work_buf,
|
||||
map->format.reg_bytes + map->format.pad_bytes,
|
||||
val, val_len);
|
||||
|
||||
@@ -672,6 +827,9 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
|
||||
trace_regmap_reg_read(map->dev, reg, *val);
|
||||
}
|
||||
|
||||
if (ret == 0 && !map->cache_bypass)
|
||||
regcache_write(map, reg, *val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -689,11 +847,14 @@ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
map->lock(map);
|
||||
|
||||
ret = _regmap_read(map, reg, val);
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -718,7 +879,12 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
||||
unsigned int v;
|
||||
int ret, i;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
if (val_len % map->format.val_bytes)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
map->lock(map);
|
||||
|
||||
if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass ||
|
||||
map->cache_type == REGCACHE_NONE) {
|
||||
@@ -730,16 +896,17 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
|
||||
* cost as we expect to hit the cache.
|
||||
*/
|
||||
for (i = 0; i < val_count; i++) {
|
||||
ret = _regmap_read(map, reg + i, &v);
|
||||
ret = _regmap_read(map, reg + (i * map->reg_stride),
|
||||
&v);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
map->format.format_val(val + (i * val_bytes), v);
|
||||
map->format.format_val(val + (i * val_bytes), v, 0);
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -765,18 +932,37 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
|
||||
|
||||
if (!map->format.parse_val)
|
||||
return -EINVAL;
|
||||
if (reg % map->reg_stride)
|
||||
return -EINVAL;
|
||||
|
||||
if (vol || map->cache_type == REGCACHE_NONE) {
|
||||
ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
/*
|
||||
* Some devices does not support bulk read, for
|
||||
* them we have a series of single read operations.
|
||||
*/
|
||||
if (map->use_single_rw) {
|
||||
for (i = 0; i < val_count; i++) {
|
||||
ret = regmap_raw_read(map,
|
||||
reg + (i * map->reg_stride),
|
||||
val + (i * val_bytes),
|
||||
val_bytes);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = regmap_raw_read(map, reg, val,
|
||||
val_bytes * val_count);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < val_count * val_bytes; i += val_bytes)
|
||||
map->format.parse_val(val + i);
|
||||
} else {
|
||||
for (i = 0; i < val_count; i++) {
|
||||
unsigned int ival;
|
||||
ret = regmap_read(map, reg + i, &ival);
|
||||
ret = regmap_read(map, reg + (i * map->reg_stride),
|
||||
&ival);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
memcpy(val + (i * val_bytes), &ival, val_bytes);
|
||||
@@ -794,7 +980,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
|
||||
int ret;
|
||||
unsigned int tmp, orig;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
ret = _regmap_read(map, reg, &orig);
|
||||
if (ret != 0)
|
||||
@@ -811,7 +997,7 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -878,7 +1064,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
|
||||
if (map->patch)
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&map->lock);
|
||||
map->lock(map);
|
||||
|
||||
bypass = map->cache_bypass;
|
||||
|
||||
@@ -906,7 +1092,7 @@ int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
|
||||
out:
|
||||
map->cache_bypass = bypass;
|
||||
|
||||
mutex_unlock(&map->lock);
|
||||
map->unlock(map);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ struct device *soc_device_to_device(struct soc_device *soc_dev)
|
||||
return &soc_dev->dev;
|
||||
}
|
||||
|
||||
static mode_t soc_attribute_mode(struct kobject *kobj,
|
||||
static umode_t soc_attribute_mode(struct kobject *kobj,
|
||||
struct attribute *attr,
|
||||
int index)
|
||||
{
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@ void bcma_core_disable(struct bcma_device *core, u32 flags)
|
||||
udelay(10);
|
||||
|
||||
bcma_awrite32(core, BCMA_RESET_CTL, BCMA_RESET_CTL_RESET);
|
||||
bcma_aread32(core, BCMA_RESET_CTL);
|
||||
udelay(1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_core_disable);
|
||||
@@ -77,7 +78,7 @@ void bcma_core_set_clockmode(struct bcma_device *core,
|
||||
pr_err("HT force timeout\n");
|
||||
break;
|
||||
case BCMA_CLKMODE_DYNAMIC:
|
||||
pr_warn("Dynamic clockmode not supported yet!\n");
|
||||
bcma_set32(core, BCMA_CLKCTLST, ~BCMA_CLKCTLST_FORCEHT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,12 @@ u32 bcma_pcie_read(struct bcma_drv_pci *pc, u32 address)
|
||||
return pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_DATA);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void bcma_pcie_write(struct bcma_drv_pci *pc, u32 address, u32 data)
|
||||
{
|
||||
pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_ADDR, address);
|
||||
pcicore_read32(pc, BCMA_CORE_PCI_PCIEIND_ADDR);
|
||||
pcicore_write32(pc, BCMA_CORE_PCI_PCIEIND_DATA, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void bcma_pcie_mdio_set_phy(struct bcma_drv_pci *pc, u8 phy)
|
||||
{
|
||||
@@ -170,13 +168,50 @@ static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc)
|
||||
tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN);
|
||||
}
|
||||
|
||||
static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc)
|
||||
{
|
||||
struct bcma_device *core = pc->core;
|
||||
u16 val16, core_index;
|
||||
uint regoff;
|
||||
|
||||
regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET);
|
||||
core_index = (u16)core->core_index;
|
||||
|
||||
val16 = pcicore_read16(pc, regoff);
|
||||
if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT)
|
||||
!= core_index) {
|
||||
val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) |
|
||||
(val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK);
|
||||
pcicore_write16(pc, regoff, val16);
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
|
||||
/* Needs to happen when coming out of 'standby'/'hibernate' */
|
||||
static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc)
|
||||
{
|
||||
u16 val16;
|
||||
uint regoff;
|
||||
|
||||
regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_MISC_CONFIG);
|
||||
|
||||
val16 = pcicore_read16(pc, regoff);
|
||||
|
||||
if (!(val16 & BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST)) {
|
||||
val16 |= BCMA_CORE_PCI_SPROM_L23READY_EXIT_NOPERST;
|
||||
pcicore_write16(pc, regoff, val16);
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************
|
||||
* Init.
|
||||
**************************************************/
|
||||
|
||||
static void __devinit bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc)
|
||||
{
|
||||
bcma_core_pci_fixcfg(pc);
|
||||
bcma_pcicore_serdes_workaround(pc);
|
||||
bcma_core_pci_config_fixup(pc);
|
||||
}
|
||||
|
||||
void __devinit bcma_core_pci_init(struct bcma_drv_pci *pc)
|
||||
@@ -224,3 +259,17 @@ out:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_core_pci_irq_ctl);
|
||||
|
||||
void bcma_core_pci_extend_L1timer(struct bcma_drv_pci *pc, bool extend)
|
||||
{
|
||||
u32 w;
|
||||
|
||||
w = bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
|
||||
if (extend)
|
||||
w |= BCMA_CORE_PCI_ASPMTIMER_EXTEND;
|
||||
else
|
||||
w &= ~BCMA_CORE_PCI_ASPMTIMER_EXTEND;
|
||||
bcma_pcie_write(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG, w);
|
||||
bcma_pcie_read(pc, BCMA_CORE_PCI_DLLP_PMTHRESHREG);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bcma_core_pci_extend_L1timer);
|
||||
|
||||
@@ -119,7 +119,7 @@ static int bcma_extpci_read_config(struct bcma_drv_pci *pc, unsigned int dev,
|
||||
if (unlikely(!addr))
|
||||
goto out;
|
||||
err = -ENOMEM;
|
||||
mmio = ioremap_nocache(addr, len);
|
||||
mmio = ioremap_nocache(addr, sizeof(val));
|
||||
if (!mmio)
|
||||
goto out;
|
||||
|
||||
@@ -171,7 +171,7 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev,
|
||||
addr = pc->core->addr + BCMA_CORE_PCI_PCICFG0;
|
||||
addr |= (func << 8);
|
||||
addr |= (off & 0xfc);
|
||||
mmio = ioremap_nocache(addr, len);
|
||||
mmio = ioremap_nocache(addr, sizeof(val));
|
||||
if (!mmio)
|
||||
goto out;
|
||||
}
|
||||
@@ -180,7 +180,7 @@ static int bcma_extpci_write_config(struct bcma_drv_pci *pc, unsigned int dev,
|
||||
if (unlikely(!addr))
|
||||
goto out;
|
||||
err = -ENOMEM;
|
||||
mmio = ioremap_nocache(addr, len);
|
||||
mmio = ioremap_nocache(addr, sizeof(val));
|
||||
if (!mmio)
|
||||
goto out;
|
||||
|
||||
@@ -491,8 +491,8 @@ void __devinit bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc)
|
||||
/* Ok, ready to run, register it to the system.
|
||||
* The following needs change, if we want to port hostmode
|
||||
* to non-MIPS platform. */
|
||||
io_map_base = (unsigned long)ioremap_nocache(BCMA_SOC_PCI_MEM,
|
||||
0x04000000);
|
||||
io_map_base = (unsigned long)ioremap_nocache(pc_host->mem_resource.start,
|
||||
resource_size(&pc_host->mem_resource));
|
||||
pc_host->pci_controller.io_map_base = io_map_base;
|
||||
set_io_port_base(pc_host->pci_controller.io_map_base);
|
||||
/* Give some time to the PCI controller to configure itself with the new
|
||||
|
||||
@@ -201,6 +201,9 @@ static int __devinit bcma_host_pci_probe(struct pci_dev *dev,
|
||||
bus->hosttype = BCMA_HOSTTYPE_PCI;
|
||||
bus->ops = &bcma_host_pci_ops;
|
||||
|
||||
bus->boardinfo.vendor = bus->host_pci->subsystem_vendor;
|
||||
bus->boardinfo.type = bus->host_pci->subsystem_device;
|
||||
|
||||
/* Register */
|
||||
err = bcma_bus_register(bus);
|
||||
if (err)
|
||||
@@ -222,7 +225,7 @@ err_kfree_bus:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void bcma_host_pci_remove(struct pci_dev *dev)
|
||||
static void __devexit bcma_host_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct bcma_bus *bus = pci_get_drvdata(dev);
|
||||
|
||||
@@ -277,7 +280,7 @@ static struct pci_driver bcma_pci_bridge_driver = {
|
||||
.name = "bcma-pci-bridge",
|
||||
.id_table = bcma_pci_bridge_tbl,
|
||||
.probe = bcma_host_pci_probe,
|
||||
.remove = bcma_host_pci_remove,
|
||||
.remove = __devexit_p(bcma_host_pci_remove),
|
||||
.driver.pm = BCMA_PM_OPS,
|
||||
};
|
||||
|
||||
|
||||
+59
-16
@@ -19,7 +19,14 @@ struct bcma_device_id_name {
|
||||
u16 id;
|
||||
const char *name;
|
||||
};
|
||||
struct bcma_device_id_name bcma_device_names[] = {
|
||||
|
||||
static const struct bcma_device_id_name bcma_arm_device_names[] = {
|
||||
{ BCMA_CORE_ARM_1176, "ARM 1176" },
|
||||
{ BCMA_CORE_ARM_7TDMI, "ARM 7TDMI" },
|
||||
{ BCMA_CORE_ARM_CM3, "ARM CM3" },
|
||||
};
|
||||
|
||||
static const struct bcma_device_id_name bcma_bcm_device_names[] = {
|
||||
{ BCMA_CORE_OOB_ROUTER, "OOB Router" },
|
||||
{ BCMA_CORE_INVALID, "Invalid" },
|
||||
{ BCMA_CORE_CHIPCOMMON, "ChipCommon" },
|
||||
@@ -27,7 +34,6 @@ struct bcma_device_id_name bcma_device_names[] = {
|
||||
{ BCMA_CORE_SRAM, "SRAM" },
|
||||
{ BCMA_CORE_SDRAM, "SDRAM" },
|
||||
{ BCMA_CORE_PCI, "PCI" },
|
||||
{ BCMA_CORE_MIPS, "MIPS" },
|
||||
{ BCMA_CORE_ETHERNET, "Fast Ethernet" },
|
||||
{ BCMA_CORE_V90, "V90" },
|
||||
{ BCMA_CORE_USB11_HOSTDEV, "USB 1.1 Hostdev" },
|
||||
@@ -44,7 +50,6 @@ struct bcma_device_id_name bcma_device_names[] = {
|
||||
{ BCMA_CORE_PHY_A, "PHY A" },
|
||||
{ BCMA_CORE_PHY_B, "PHY B" },
|
||||
{ BCMA_CORE_PHY_G, "PHY G" },
|
||||
{ BCMA_CORE_MIPS_3302, "MIPS 3302" },
|
||||
{ BCMA_CORE_USB11_HOST, "USB 1.1 Host" },
|
||||
{ BCMA_CORE_USB11_DEV, "USB 1.1 Device" },
|
||||
{ BCMA_CORE_USB20_HOST, "USB 2.0 Host" },
|
||||
@@ -58,15 +63,11 @@ struct bcma_device_id_name bcma_device_names[] = {
|
||||
{ BCMA_CORE_PHY_N, "PHY N" },
|
||||
{ BCMA_CORE_SRAM_CTL, "SRAM Controller" },
|
||||
{ BCMA_CORE_MINI_MACPHY, "Mini MACPHY" },
|
||||
{ BCMA_CORE_ARM_1176, "ARM 1176" },
|
||||
{ BCMA_CORE_ARM_7TDMI, "ARM 7TDMI" },
|
||||
{ BCMA_CORE_PHY_LP, "PHY LP" },
|
||||
{ BCMA_CORE_PMU, "PMU" },
|
||||
{ BCMA_CORE_PHY_SSN, "PHY SSN" },
|
||||
{ BCMA_CORE_SDIO_DEV, "SDIO Device" },
|
||||
{ BCMA_CORE_ARM_CM3, "ARM CM3" },
|
||||
{ BCMA_CORE_PHY_HT, "PHY HT" },
|
||||
{ BCMA_CORE_MIPS_74K, "MIPS 74K" },
|
||||
{ BCMA_CORE_MAC_GBIT, "GBit MAC" },
|
||||
{ BCMA_CORE_DDR12_MEM_CTL, "DDR1/DDR2 Memory Controller" },
|
||||
{ BCMA_CORE_PCIE_RC, "PCIe Root Complex" },
|
||||
@@ -79,16 +80,41 @@ struct bcma_device_id_name bcma_device_names[] = {
|
||||
{ BCMA_CORE_SHIM, "SHIM" },
|
||||
{ BCMA_CORE_DEFAULT, "Default" },
|
||||
};
|
||||
const char *bcma_device_name(struct bcma_device_id *id)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (id->manuf == BCMA_MANUF_BCM) {
|
||||
for (i = 0; i < ARRAY_SIZE(bcma_device_names); i++) {
|
||||
if (bcma_device_names[i].id == id->id)
|
||||
return bcma_device_names[i].name;
|
||||
}
|
||||
static const struct bcma_device_id_name bcma_mips_device_names[] = {
|
||||
{ BCMA_CORE_MIPS, "MIPS" },
|
||||
{ BCMA_CORE_MIPS_3302, "MIPS 3302" },
|
||||
{ BCMA_CORE_MIPS_74K, "MIPS 74K" },
|
||||
};
|
||||
|
||||
static const char *bcma_device_name(const struct bcma_device_id *id)
|
||||
{
|
||||
const struct bcma_device_id_name *names;
|
||||
int size, i;
|
||||
|
||||
/* search manufacturer specific names */
|
||||
switch (id->manuf) {
|
||||
case BCMA_MANUF_ARM:
|
||||
names = bcma_arm_device_names;
|
||||
size = ARRAY_SIZE(bcma_arm_device_names);
|
||||
break;
|
||||
case BCMA_MANUF_BCM:
|
||||
names = bcma_bcm_device_names;
|
||||
size = ARRAY_SIZE(bcma_bcm_device_names);
|
||||
break;
|
||||
case BCMA_MANUF_MIPS:
|
||||
names = bcma_mips_device_names;
|
||||
size = ARRAY_SIZE(bcma_mips_device_names);
|
||||
break;
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (names[i].id == id->id)
|
||||
return names[i].name;
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
@@ -297,6 +323,23 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
/* First Slave Address Descriptor should be port 0:
|
||||
* the main register space for the core
|
||||
*/
|
||||
tmp = bcma_erom_get_addr_desc(bus, eromptr, SCAN_ADDR_TYPE_SLAVE, 0);
|
||||
if (tmp <= 0) {
|
||||
/* Try again to see if it is a bridge */
|
||||
tmp = bcma_erom_get_addr_desc(bus, eromptr,
|
||||
SCAN_ADDR_TYPE_BRIDGE, 0);
|
||||
if (tmp <= 0) {
|
||||
return -EILSEQ;
|
||||
} else {
|
||||
pr_info("Bridge found\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
}
|
||||
core->addr = tmp;
|
||||
|
||||
/* get & parse slave ports */
|
||||
for (i = 0; i < ports[1]; i++) {
|
||||
for (j = 0; ; j++) {
|
||||
@@ -309,7 +352,7 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr,
|
||||
break;
|
||||
} else {
|
||||
if (i == 0 && j == 0)
|
||||
core->addr = tmp;
|
||||
core->addr1 = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+148
-1
@@ -181,6 +181,22 @@ static int bcma_sprom_valid(const u16 *sprom)
|
||||
#define SPEX(_field, _offset, _mask, _shift) \
|
||||
bus->sprom._field = ((sprom[SPOFF(_offset)] & (_mask)) >> (_shift))
|
||||
|
||||
#define SPEX32(_field, _offset, _mask, _shift) \
|
||||
bus->sprom._field = ((((u32)sprom[SPOFF((_offset)+2)] << 16 | \
|
||||
sprom[SPOFF(_offset)]) & (_mask)) >> (_shift))
|
||||
|
||||
#define SPEX_ARRAY8(_field, _offset, _mask, _shift) \
|
||||
do { \
|
||||
SPEX(_field[0], _offset + 0, _mask, _shift); \
|
||||
SPEX(_field[1], _offset + 2, _mask, _shift); \
|
||||
SPEX(_field[2], _offset + 4, _mask, _shift); \
|
||||
SPEX(_field[3], _offset + 6, _mask, _shift); \
|
||||
SPEX(_field[4], _offset + 8, _mask, _shift); \
|
||||
SPEX(_field[5], _offset + 10, _mask, _shift); \
|
||||
SPEX(_field[6], _offset + 12, _mask, _shift); \
|
||||
SPEX(_field[7], _offset + 14, _mask, _shift); \
|
||||
} while (0)
|
||||
|
||||
static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
|
||||
{
|
||||
u16 v, o;
|
||||
@@ -243,7 +259,8 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
|
||||
SPEX(boardflags2_lo, SSB_SPROM8_BFL2LO, ~0, 0);
|
||||
SPEX(boardflags2_hi, SSB_SPROM8_BFL2HI, ~0, 0);
|
||||
|
||||
SPEX(country_code, SSB_SPROM8_CCODE, ~0, 0);
|
||||
SPEX(alpha2[0], SSB_SPROM8_CCODE, 0xff00, 8);
|
||||
SPEX(alpha2[1], SSB_SPROM8_CCODE, 0x00ff, 0);
|
||||
|
||||
/* Extract cores power info info */
|
||||
for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) {
|
||||
@@ -298,6 +315,136 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom)
|
||||
SSB_SROM8_FEM_TR_ISO_SHIFT);
|
||||
SPEX(fem.ghz5.antswlut, SSB_SPROM8_FEM5G, SSB_SROM8_FEM_ANTSWLUT,
|
||||
SSB_SROM8_FEM_ANTSWLUT_SHIFT);
|
||||
|
||||
SPEX(ant_available_a, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_A,
|
||||
SSB_SPROM8_ANTAVAIL_A_SHIFT);
|
||||
SPEX(ant_available_bg, SSB_SPROM8_ANTAVAIL, SSB_SPROM8_ANTAVAIL_BG,
|
||||
SSB_SPROM8_ANTAVAIL_BG_SHIFT);
|
||||
SPEX(maxpwr_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_MAXP_BG_MASK, 0);
|
||||
SPEX(itssi_bg, SSB_SPROM8_MAXP_BG, SSB_SPROM8_ITSSI_BG,
|
||||
SSB_SPROM8_ITSSI_BG_SHIFT);
|
||||
SPEX(maxpwr_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_MAXP_A_MASK, 0);
|
||||
SPEX(itssi_a, SSB_SPROM8_MAXP_A, SSB_SPROM8_ITSSI_A,
|
||||
SSB_SPROM8_ITSSI_A_SHIFT);
|
||||
SPEX(maxpwr_ah, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AH_MASK, 0);
|
||||
SPEX(maxpwr_al, SSB_SPROM8_MAXP_AHL, SSB_SPROM8_MAXP_AL_MASK,
|
||||
SSB_SPROM8_MAXP_AL_SHIFT);
|
||||
SPEX(gpio0, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P0, 0);
|
||||
SPEX(gpio1, SSB_SPROM8_GPIOA, SSB_SPROM8_GPIOA_P1,
|
||||
SSB_SPROM8_GPIOA_P1_SHIFT);
|
||||
SPEX(gpio2, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P2, 0);
|
||||
SPEX(gpio3, SSB_SPROM8_GPIOB, SSB_SPROM8_GPIOB_P3,
|
||||
SSB_SPROM8_GPIOB_P3_SHIFT);
|
||||
SPEX(tri2g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI2G, 0);
|
||||
SPEX(tri5g, SSB_SPROM8_TRI25G, SSB_SPROM8_TRI5G,
|
||||
SSB_SPROM8_TRI5G_SHIFT);
|
||||
SPEX(tri5gl, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GL, 0);
|
||||
SPEX(tri5gh, SSB_SPROM8_TRI5GHL, SSB_SPROM8_TRI5GH,
|
||||
SSB_SPROM8_TRI5GH_SHIFT);
|
||||
SPEX(rxpo2g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO2G,
|
||||
SSB_SPROM8_RXPO2G_SHIFT);
|
||||
SPEX(rxpo5g, SSB_SPROM8_RXPO, SSB_SPROM8_RXPO5G,
|
||||
SSB_SPROM8_RXPO5G_SHIFT);
|
||||
SPEX(rssismf2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMF2G, 0);
|
||||
SPEX(rssismc2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISMC2G,
|
||||
SSB_SPROM8_RSSISMC2G_SHIFT);
|
||||
SPEX(rssisav2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_RSSISAV2G,
|
||||
SSB_SPROM8_RSSISAV2G_SHIFT);
|
||||
SPEX(bxa2g, SSB_SPROM8_RSSIPARM2G, SSB_SPROM8_BXA2G,
|
||||
SSB_SPROM8_BXA2G_SHIFT);
|
||||
SPEX(rssismf5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMF5G, 0);
|
||||
SPEX(rssismc5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISMC5G,
|
||||
SSB_SPROM8_RSSISMC5G_SHIFT);
|
||||
SPEX(rssisav5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_RSSISAV5G,
|
||||
SSB_SPROM8_RSSISAV5G_SHIFT);
|
||||
SPEX(bxa5g, SSB_SPROM8_RSSIPARM5G, SSB_SPROM8_BXA5G,
|
||||
SSB_SPROM8_BXA5G_SHIFT);
|
||||
|
||||
SPEX(pa0b0, SSB_SPROM8_PA0B0, ~0, 0);
|
||||
SPEX(pa0b1, SSB_SPROM8_PA0B1, ~0, 0);
|
||||
SPEX(pa0b2, SSB_SPROM8_PA0B2, ~0, 0);
|
||||
SPEX(pa1b0, SSB_SPROM8_PA1B0, ~0, 0);
|
||||
SPEX(pa1b1, SSB_SPROM8_PA1B1, ~0, 0);
|
||||
SPEX(pa1b2, SSB_SPROM8_PA1B2, ~0, 0);
|
||||
SPEX(pa1lob0, SSB_SPROM8_PA1LOB0, ~0, 0);
|
||||
SPEX(pa1lob1, SSB_SPROM8_PA1LOB1, ~0, 0);
|
||||
SPEX(pa1lob2, SSB_SPROM8_PA1LOB2, ~0, 0);
|
||||
SPEX(pa1hib0, SSB_SPROM8_PA1HIB0, ~0, 0);
|
||||
SPEX(pa1hib1, SSB_SPROM8_PA1HIB1, ~0, 0);
|
||||
SPEX(pa1hib2, SSB_SPROM8_PA1HIB2, ~0, 0);
|
||||
SPEX(cck2gpo, SSB_SPROM8_CCK2GPO, ~0, 0);
|
||||
SPEX32(ofdm2gpo, SSB_SPROM8_OFDM2GPO, ~0, 0);
|
||||
SPEX32(ofdm5glpo, SSB_SPROM8_OFDM5GLPO, ~0, 0);
|
||||
SPEX32(ofdm5gpo, SSB_SPROM8_OFDM5GPO, ~0, 0);
|
||||
SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, ~0, 0);
|
||||
|
||||
/* Extract the antenna gain values. */
|
||||
SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01,
|
||||
SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT);
|
||||
SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01,
|
||||
SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT);
|
||||
SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23,
|
||||
SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT);
|
||||
SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23,
|
||||
SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT);
|
||||
|
||||
SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON,
|
||||
SSB_SPROM8_LEDDC_ON_SHIFT);
|
||||
SPEX(leddc_off_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_OFF,
|
||||
SSB_SPROM8_LEDDC_OFF_SHIFT);
|
||||
|
||||
SPEX(txchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_TXCHAIN,
|
||||
SSB_SPROM8_TXRXC_TXCHAIN_SHIFT);
|
||||
SPEX(rxchain, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_RXCHAIN,
|
||||
SSB_SPROM8_TXRXC_RXCHAIN_SHIFT);
|
||||
SPEX(antswitch, SSB_SPROM8_TXRXC, SSB_SPROM8_TXRXC_SWITCH,
|
||||
SSB_SPROM8_TXRXC_SWITCH_SHIFT);
|
||||
|
||||
SPEX(opo, SSB_SPROM8_OFDM2GPO, 0x00ff, 0);
|
||||
|
||||
SPEX_ARRAY8(mcs2gpo, SSB_SPROM8_2G_MCSPO, ~0, 0);
|
||||
SPEX_ARRAY8(mcs5gpo, SSB_SPROM8_5G_MCSPO, ~0, 0);
|
||||
SPEX_ARRAY8(mcs5glpo, SSB_SPROM8_5GL_MCSPO, ~0, 0);
|
||||
SPEX_ARRAY8(mcs5ghpo, SSB_SPROM8_5GH_MCSPO, ~0, 0);
|
||||
|
||||
SPEX(rawtempsense, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_RAWTEMP,
|
||||
SSB_SPROM8_RAWTS_RAWTEMP_SHIFT);
|
||||
SPEX(measpower, SSB_SPROM8_RAWTS, SSB_SPROM8_RAWTS_MEASPOWER,
|
||||
SSB_SPROM8_RAWTS_MEASPOWER_SHIFT);
|
||||
SPEX(tempsense_slope, SSB_SPROM8_OPT_CORRX,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_SLOPE,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_SLOPE_SHIFT);
|
||||
SPEX(tempcorrx, SSB_SPROM8_OPT_CORRX, SSB_SPROM8_OPT_CORRX_TEMPCORRX,
|
||||
SSB_SPROM8_OPT_CORRX_TEMPCORRX_SHIFT);
|
||||
SPEX(tempsense_option, SSB_SPROM8_OPT_CORRX,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_OPTION,
|
||||
SSB_SPROM8_OPT_CORRX_TEMP_OPTION_SHIFT);
|
||||
SPEX(freqoffset_corr, SSB_SPROM8_HWIQ_IQSWP,
|
||||
SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR,
|
||||
SSB_SPROM8_HWIQ_IQSWP_FREQ_CORR_SHIFT);
|
||||
SPEX(iqcal_swp_dis, SSB_SPROM8_HWIQ_IQSWP,
|
||||
SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP,
|
||||
SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT);
|
||||
SPEX(hw_iqcal_en, SSB_SPROM8_HWIQ_IQSWP, SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL,
|
||||
SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT);
|
||||
|
||||
SPEX(bw40po, SSB_SPROM8_BW40PO, ~0, 0);
|
||||
SPEX(cddpo, SSB_SPROM8_CDDPO, ~0, 0);
|
||||
SPEX(stbcpo, SSB_SPROM8_STBCPO, ~0, 0);
|
||||
SPEX(bwduppo, SSB_SPROM8_BWDUPPO, ~0, 0);
|
||||
|
||||
SPEX(tempthresh, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_TRESH,
|
||||
SSB_SPROM8_THERMAL_TRESH_SHIFT);
|
||||
SPEX(tempoffset, SSB_SPROM8_THERMAL, SSB_SPROM8_THERMAL_OFFSET,
|
||||
SSB_SPROM8_THERMAL_OFFSET_SHIFT);
|
||||
SPEX(phycal_tempdelta, SSB_SPROM8_TEMPDELTA,
|
||||
SSB_SPROM8_TEMPDELTA_PHYCAL,
|
||||
SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT);
|
||||
SPEX(temps_period, SSB_SPROM8_TEMPDELTA, SSB_SPROM8_TEMPDELTA_PERIOD,
|
||||
SSB_SPROM8_TEMPDELTA_PERIOD_SHIFT);
|
||||
SPEX(temps_hysteresis, SSB_SPROM8_TEMPDELTA,
|
||||
SSB_SPROM8_TEMPDELTA_HYSTERESIS,
|
||||
SSB_SPROM8_TEMPDELTA_HYSTERESIS_SHIFT);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -65,39 +65,80 @@ struct drbd_atodb_wait {
|
||||
|
||||
int w_al_write_transaction(struct drbd_conf *, struct drbd_work *, int);
|
||||
|
||||
void *drbd_md_get_buffer(struct drbd_conf *mdev)
|
||||
{
|
||||
int r;
|
||||
|
||||
wait_event(mdev->misc_wait,
|
||||
(r = atomic_cmpxchg(&mdev->md_io_in_use, 0, 1)) == 0 ||
|
||||
mdev->state.disk <= D_FAILED);
|
||||
|
||||
return r ? NULL : page_address(mdev->md_io_page);
|
||||
}
|
||||
|
||||
void drbd_md_put_buffer(struct drbd_conf *mdev)
|
||||
{
|
||||
if (atomic_dec_and_test(&mdev->md_io_in_use))
|
||||
wake_up(&mdev->misc_wait);
|
||||
}
|
||||
|
||||
static bool md_io_allowed(struct drbd_conf *mdev)
|
||||
{
|
||||
enum drbd_disk_state ds = mdev->state.disk;
|
||||
return ds >= D_NEGOTIATING || ds == D_ATTACHING;
|
||||
}
|
||||
|
||||
void wait_until_done_or_disk_failure(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
|
||||
unsigned int *done)
|
||||
{
|
||||
long dt = bdev->dc.disk_timeout * HZ / 10;
|
||||
if (dt == 0)
|
||||
dt = MAX_SCHEDULE_TIMEOUT;
|
||||
|
||||
dt = wait_event_timeout(mdev->misc_wait, *done || !md_io_allowed(mdev), dt);
|
||||
if (dt == 0)
|
||||
dev_err(DEV, "meta-data IO operation timed out\n");
|
||||
}
|
||||
|
||||
static int _drbd_md_sync_page_io(struct drbd_conf *mdev,
|
||||
struct drbd_backing_dev *bdev,
|
||||
struct page *page, sector_t sector,
|
||||
int rw, int size)
|
||||
{
|
||||
struct bio *bio;
|
||||
struct drbd_md_io md_io;
|
||||
int ok;
|
||||
|
||||
md_io.mdev = mdev;
|
||||
init_completion(&md_io.event);
|
||||
md_io.error = 0;
|
||||
mdev->md_io.done = 0;
|
||||
mdev->md_io.error = -ENODEV;
|
||||
|
||||
if ((rw & WRITE) && !test_bit(MD_NO_FUA, &mdev->flags))
|
||||
rw |= REQ_FUA | REQ_FLUSH;
|
||||
rw |= REQ_SYNC;
|
||||
|
||||
bio = bio_alloc(GFP_NOIO, 1);
|
||||
bio = bio_alloc_drbd(GFP_NOIO);
|
||||
bio->bi_bdev = bdev->md_bdev;
|
||||
bio->bi_sector = sector;
|
||||
ok = (bio_add_page(bio, page, size, 0) == size);
|
||||
if (!ok)
|
||||
goto out;
|
||||
bio->bi_private = &md_io;
|
||||
bio->bi_private = &mdev->md_io;
|
||||
bio->bi_end_io = drbd_md_io_complete;
|
||||
bio->bi_rw = rw;
|
||||
|
||||
if (!get_ldev_if_state(mdev, D_ATTACHING)) { /* Corresponding put_ldev in drbd_md_io_complete() */
|
||||
dev_err(DEV, "ASSERT FAILED: get_ldev_if_state() == 1 in _drbd_md_sync_page_io()\n");
|
||||
ok = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bio_get(bio); /* one bio_put() is in the completion handler */
|
||||
atomic_inc(&mdev->md_io_in_use); /* drbd_md_put_buffer() is in the completion handler */
|
||||
if (drbd_insert_fault(mdev, (rw & WRITE) ? DRBD_FAULT_MD_WR : DRBD_FAULT_MD_RD))
|
||||
bio_endio(bio, -EIO);
|
||||
else
|
||||
submit_bio(rw, bio);
|
||||
wait_for_completion(&md_io.event);
|
||||
ok = bio_flagged(bio, BIO_UPTODATE) && md_io.error == 0;
|
||||
wait_until_done_or_disk_failure(mdev, bdev, &mdev->md_io.done);
|
||||
ok = bio_flagged(bio, BIO_UPTODATE) && mdev->md_io.error == 0;
|
||||
|
||||
out:
|
||||
bio_put(bio);
|
||||
@@ -111,7 +152,7 @@ int drbd_md_sync_page_io(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
|
||||
int offset = 0;
|
||||
struct page *iop = mdev->md_io_page;
|
||||
|
||||
D_ASSERT(mutex_is_locked(&mdev->md_io_mutex));
|
||||
D_ASSERT(atomic_read(&mdev->md_io_in_use) == 1);
|
||||
|
||||
BUG_ON(!bdev->md_bdev);
|
||||
|
||||
@@ -328,8 +369,13 @@ w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused)
|
||||
return 1;
|
||||
}
|
||||
|
||||
mutex_lock(&mdev->md_io_mutex); /* protects md_io_buffer, al_tr_cycle, ... */
|
||||
buffer = (struct al_transaction *)page_address(mdev->md_io_page);
|
||||
buffer = drbd_md_get_buffer(mdev); /* protects md_io_buffer, al_tr_cycle, ... */
|
||||
if (!buffer) {
|
||||
dev_err(DEV, "disk failed while waiting for md_io buffer\n");
|
||||
complete(&((struct update_al_work *)w)->event);
|
||||
put_ldev(mdev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
buffer->magic = __constant_cpu_to_be32(DRBD_MAGIC);
|
||||
buffer->tr_number = cpu_to_be32(mdev->al_tr_number);
|
||||
@@ -374,7 +420,7 @@ w_al_write_transaction(struct drbd_conf *mdev, struct drbd_work *w, int unused)
|
||||
D_ASSERT(mdev->al_tr_pos < MD_AL_MAX_SIZE);
|
||||
mdev->al_tr_number++;
|
||||
|
||||
mutex_unlock(&mdev->md_io_mutex);
|
||||
drbd_md_put_buffer(mdev);
|
||||
|
||||
complete(&((struct update_al_work *)w)->event);
|
||||
put_ldev(mdev);
|
||||
@@ -443,8 +489,9 @@ int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
|
||||
/* lock out all other meta data io for now,
|
||||
* and make sure the page is mapped.
|
||||
*/
|
||||
mutex_lock(&mdev->md_io_mutex);
|
||||
buffer = page_address(mdev->md_io_page);
|
||||
buffer = drbd_md_get_buffer(mdev);
|
||||
if (!buffer)
|
||||
return 0;
|
||||
|
||||
/* Find the valid transaction in the log */
|
||||
for (i = 0; i <= mx; i++) {
|
||||
@@ -452,7 +499,7 @@ int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
|
||||
if (rv == 0)
|
||||
continue;
|
||||
if (rv == -1) {
|
||||
mutex_unlock(&mdev->md_io_mutex);
|
||||
drbd_md_put_buffer(mdev);
|
||||
return 0;
|
||||
}
|
||||
cnr = be32_to_cpu(buffer->tr_number);
|
||||
@@ -478,7 +525,7 @@ int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
|
||||
|
||||
if (!found_valid) {
|
||||
dev_warn(DEV, "No usable activity log found.\n");
|
||||
mutex_unlock(&mdev->md_io_mutex);
|
||||
drbd_md_put_buffer(mdev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -493,7 +540,7 @@ int drbd_al_read_log(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
|
||||
rv = drbd_al_read_tr(mdev, bdev, buffer, i);
|
||||
ERR_IF(rv == 0) goto cancel;
|
||||
if (rv == -1) {
|
||||
mutex_unlock(&mdev->md_io_mutex);
|
||||
drbd_md_put_buffer(mdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -534,7 +581,7 @@ cancel:
|
||||
mdev->al_tr_pos = 0;
|
||||
|
||||
/* ok, we are done with it */
|
||||
mutex_unlock(&mdev->md_io_mutex);
|
||||
drbd_md_put_buffer(mdev);
|
||||
|
||||
dev_info(DEV, "Found %d transactions (%d active extents) in activity log.\n",
|
||||
transactions, active_extents);
|
||||
@@ -671,16 +718,20 @@ static void drbd_try_clear_on_disk_bm(struct drbd_conf *mdev, sector_t sector,
|
||||
else
|
||||
ext->rs_failed += count;
|
||||
if (ext->rs_left < ext->rs_failed) {
|
||||
dev_err(DEV, "BAD! sector=%llus enr=%u rs_left=%d "
|
||||
"rs_failed=%d count=%d\n",
|
||||
dev_warn(DEV, "BAD! sector=%llus enr=%u rs_left=%d "
|
||||
"rs_failed=%d count=%d cstate=%s\n",
|
||||
(unsigned long long)sector,
|
||||
ext->lce.lc_number, ext->rs_left,
|
||||
ext->rs_failed, count);
|
||||
dump_stack();
|
||||
ext->rs_failed, count,
|
||||
drbd_conn_str(mdev->state.conn));
|
||||
|
||||
lc_put(mdev->resync, &ext->lce);
|
||||
drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
|
||||
return;
|
||||
/* We don't expect to be able to clear more bits
|
||||
* than have been set when we originally counted
|
||||
* the set bits to cache that value in ext->rs_left.
|
||||
* Whatever the reason (disconnect during resync,
|
||||
* delayed local completion of an application write),
|
||||
* try to fix it up by recounting here. */
|
||||
ext->rs_left = drbd_bm_e_weight(mdev, enr);
|
||||
}
|
||||
} else {
|
||||
/* Normally this element should be in the cache,
|
||||
@@ -1192,6 +1243,7 @@ int drbd_rs_del_all(struct drbd_conf *mdev)
|
||||
put_ldev(mdev);
|
||||
}
|
||||
spin_unlock_irq(&mdev->al_lock);
|
||||
wake_up(&mdev->al_wait);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ void drbd_bm_unlock(struct drbd_conf *mdev)
|
||||
static void bm_store_page_idx(struct page *page, unsigned long idx)
|
||||
{
|
||||
BUG_ON(0 != (idx & ~BM_PAGE_IDX_MASK));
|
||||
page_private(page) |= idx;
|
||||
set_page_private(page, idx);
|
||||
}
|
||||
|
||||
static unsigned long bm_page_to_idx(struct page *page)
|
||||
@@ -886,12 +886,21 @@ void drbd_bm_clear_all(struct drbd_conf *mdev)
|
||||
struct bm_aio_ctx {
|
||||
struct drbd_conf *mdev;
|
||||
atomic_t in_flight;
|
||||
struct completion done;
|
||||
unsigned int done;
|
||||
unsigned flags;
|
||||
#define BM_AIO_COPY_PAGES 1
|
||||
int error;
|
||||
struct kref kref;
|
||||
};
|
||||
|
||||
static void bm_aio_ctx_destroy(struct kref *kref)
|
||||
{
|
||||
struct bm_aio_ctx *ctx = container_of(kref, struct bm_aio_ctx, kref);
|
||||
|
||||
put_ldev(ctx->mdev);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
/* bv_page may be a copy, or may be the original */
|
||||
static void bm_async_io_complete(struct bio *bio, int error)
|
||||
{
|
||||
@@ -930,20 +939,21 @@ static void bm_async_io_complete(struct bio *bio, int error)
|
||||
|
||||
bm_page_unlock_io(mdev, idx);
|
||||
|
||||
/* FIXME give back to page pool */
|
||||
if (ctx->flags & BM_AIO_COPY_PAGES)
|
||||
put_page(bio->bi_io_vec[0].bv_page);
|
||||
mempool_free(bio->bi_io_vec[0].bv_page, drbd_md_io_page_pool);
|
||||
|
||||
bio_put(bio);
|
||||
|
||||
if (atomic_dec_and_test(&ctx->in_flight))
|
||||
complete(&ctx->done);
|
||||
if (atomic_dec_and_test(&ctx->in_flight)) {
|
||||
ctx->done = 1;
|
||||
wake_up(&mdev->misc_wait);
|
||||
kref_put(&ctx->kref, &bm_aio_ctx_destroy);
|
||||
}
|
||||
}
|
||||
|
||||
static void bm_page_io_async(struct bm_aio_ctx *ctx, int page_nr, int rw) __must_hold(local)
|
||||
{
|
||||
/* we are process context. we always get a bio */
|
||||
struct bio *bio = bio_alloc(GFP_KERNEL, 1);
|
||||
struct bio *bio = bio_alloc_drbd(GFP_NOIO);
|
||||
struct drbd_conf *mdev = ctx->mdev;
|
||||
struct drbd_bitmap *b = mdev->bitmap;
|
||||
struct page *page;
|
||||
@@ -966,10 +976,8 @@ static void bm_page_io_async(struct bm_aio_ctx *ctx, int page_nr, int rw) __must
|
||||
bm_set_page_unchanged(b->bm_pages[page_nr]);
|
||||
|
||||
if (ctx->flags & BM_AIO_COPY_PAGES) {
|
||||
/* FIXME alloc_page is good enough for now, but actually needs
|
||||
* to use pre-allocated page pool */
|
||||
void *src, *dest;
|
||||
page = alloc_page(__GFP_HIGHMEM|__GFP_WAIT);
|
||||
page = mempool_alloc(drbd_md_io_page_pool, __GFP_HIGHMEM|__GFP_WAIT);
|
||||
dest = kmap_atomic(page);
|
||||
src = kmap_atomic(b->bm_pages[page_nr]);
|
||||
memcpy(dest, src, PAGE_SIZE);
|
||||
@@ -981,6 +989,8 @@ static void bm_page_io_async(struct bm_aio_ctx *ctx, int page_nr, int rw) __must
|
||||
|
||||
bio->bi_bdev = mdev->ldev->md_bdev;
|
||||
bio->bi_sector = on_disk_sector;
|
||||
/* bio_add_page of a single page to an empty bio will always succeed,
|
||||
* according to api. Do we want to assert that? */
|
||||
bio_add_page(bio, page, len, 0);
|
||||
bio->bi_private = ctx;
|
||||
bio->bi_end_io = bm_async_io_complete;
|
||||
@@ -999,14 +1009,9 @@ static void bm_page_io_async(struct bm_aio_ctx *ctx, int page_nr, int rw) __must
|
||||
/*
|
||||
* bm_rw: read/write the whole bitmap from/to its on disk location.
|
||||
*/
|
||||
static int bm_rw(struct drbd_conf *mdev, int rw, unsigned lazy_writeout_upper_idx) __must_hold(local)
|
||||
static int bm_rw(struct drbd_conf *mdev, int rw, unsigned flags, unsigned lazy_writeout_upper_idx) __must_hold(local)
|
||||
{
|
||||
struct bm_aio_ctx ctx = {
|
||||
.mdev = mdev,
|
||||
.in_flight = ATOMIC_INIT(1),
|
||||
.done = COMPLETION_INITIALIZER_ONSTACK(ctx.done),
|
||||
.flags = lazy_writeout_upper_idx ? BM_AIO_COPY_PAGES : 0,
|
||||
};
|
||||
struct bm_aio_ctx *ctx;
|
||||
struct drbd_bitmap *b = mdev->bitmap;
|
||||
int num_pages, i, count = 0;
|
||||
unsigned long now;
|
||||
@@ -1021,7 +1026,27 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned lazy_writeout_upper_id
|
||||
* For lazy writeout, we don't care for ongoing changes to the bitmap,
|
||||
* as we submit copies of pages anyways.
|
||||
*/
|
||||
if (!ctx.flags)
|
||||
|
||||
ctx = kmalloc(sizeof(struct bm_aio_ctx), GFP_NOIO);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
*ctx = (struct bm_aio_ctx) {
|
||||
.mdev = mdev,
|
||||
.in_flight = ATOMIC_INIT(1),
|
||||
.done = 0,
|
||||
.flags = flags,
|
||||
.error = 0,
|
||||
.kref = { ATOMIC_INIT(2) },
|
||||
};
|
||||
|
||||
if (!get_ldev_if_state(mdev, D_ATTACHING)) { /* put is in bm_aio_ctx_destroy() */
|
||||
dev_err(DEV, "ASSERT FAILED: get_ldev_if_state() == 1 in bm_rw()\n");
|
||||
kfree(ctx);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!ctx->flags)
|
||||
WARN_ON(!(BM_LOCKED_MASK & b->bm_flags));
|
||||
|
||||
num_pages = b->bm_number_of_pages;
|
||||
@@ -1046,29 +1071,38 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned lazy_writeout_upper_id
|
||||
continue;
|
||||
}
|
||||
}
|
||||
atomic_inc(&ctx.in_flight);
|
||||
bm_page_io_async(&ctx, i, rw);
|
||||
atomic_inc(&ctx->in_flight);
|
||||
bm_page_io_async(ctx, i, rw);
|
||||
++count;
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
/*
|
||||
* We initialize ctx.in_flight to one to make sure bm_async_io_complete
|
||||
* will not complete() early, and decrement / test it here. If there
|
||||
* We initialize ctx->in_flight to one to make sure bm_async_io_complete
|
||||
* will not set ctx->done early, and decrement / test it here. If there
|
||||
* are still some bios in flight, we need to wait for them here.
|
||||
* If all IO is done already (or nothing had been submitted), there is
|
||||
* no need to wait. Still, we need to put the kref associated with the
|
||||
* "in_flight reached zero, all done" event.
|
||||
*/
|
||||
if (!atomic_dec_and_test(&ctx.in_flight))
|
||||
wait_for_completion(&ctx.done);
|
||||
if (!atomic_dec_and_test(&ctx->in_flight))
|
||||
wait_until_done_or_disk_failure(mdev, mdev->ldev, &ctx->done);
|
||||
else
|
||||
kref_put(&ctx->kref, &bm_aio_ctx_destroy);
|
||||
|
||||
dev_info(DEV, "bitmap %s of %u pages took %lu jiffies\n",
|
||||
rw == WRITE ? "WRITE" : "READ",
|
||||
count, jiffies - now);
|
||||
|
||||
if (ctx.error) {
|
||||
if (ctx->error) {
|
||||
dev_alert(DEV, "we had at least one MD IO ERROR during bitmap IO\n");
|
||||
drbd_chk_io_error(mdev, 1, true);
|
||||
err = -EIO; /* ctx.error ? */
|
||||
err = -EIO; /* ctx->error ? */
|
||||
}
|
||||
|
||||
if (atomic_read(&ctx->in_flight))
|
||||
err = -EIO; /* Disk failed during IO... */
|
||||
|
||||
now = jiffies;
|
||||
if (rw == WRITE) {
|
||||
drbd_md_flush(mdev);
|
||||
@@ -1082,6 +1116,7 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned lazy_writeout_upper_id
|
||||
dev_info(DEV, "%s (%lu bits) marked out-of-sync by on disk bit-map.\n",
|
||||
ppsize(ppb, now << (BM_BLOCK_SHIFT-10)), now);
|
||||
|
||||
kref_put(&ctx->kref, &bm_aio_ctx_destroy);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1091,7 +1126,7 @@ static int bm_rw(struct drbd_conf *mdev, int rw, unsigned lazy_writeout_upper_id
|
||||
*/
|
||||
int drbd_bm_read(struct drbd_conf *mdev) __must_hold(local)
|
||||
{
|
||||
return bm_rw(mdev, READ, 0);
|
||||
return bm_rw(mdev, READ, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1102,7 +1137,7 @@ int drbd_bm_read(struct drbd_conf *mdev) __must_hold(local)
|
||||
*/
|
||||
int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local)
|
||||
{
|
||||
return bm_rw(mdev, WRITE, 0);
|
||||
return bm_rw(mdev, WRITE, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1112,7 +1147,23 @@ int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local)
|
||||
*/
|
||||
int drbd_bm_write_lazy(struct drbd_conf *mdev, unsigned upper_idx) __must_hold(local)
|
||||
{
|
||||
return bm_rw(mdev, WRITE, upper_idx);
|
||||
return bm_rw(mdev, WRITE, BM_AIO_COPY_PAGES, upper_idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* drbd_bm_write_copy_pages() - Write the whole bitmap to its on disk location.
|
||||
* @mdev: DRBD device.
|
||||
*
|
||||
* Will only write pages that have changed since last IO.
|
||||
* In contrast to drbd_bm_write(), this will copy the bitmap pages
|
||||
* to temporary writeout pages. It is intended to trigger a full write-out
|
||||
* while still allowing the bitmap to change, for example if a resync or online
|
||||
* verify is aborted due to a failed peer disk, while local IO continues, or
|
||||
* pending resync acks are still being processed.
|
||||
*/
|
||||
int drbd_bm_write_copy_pages(struct drbd_conf *mdev) __must_hold(local)
|
||||
{
|
||||
return bm_rw(mdev, WRITE, BM_AIO_COPY_PAGES, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1130,28 +1181,45 @@ int drbd_bm_write_lazy(struct drbd_conf *mdev, unsigned upper_idx) __must_hold(l
|
||||
*/
|
||||
int drbd_bm_write_page(struct drbd_conf *mdev, unsigned int idx) __must_hold(local)
|
||||
{
|
||||
struct bm_aio_ctx ctx = {
|
||||
.mdev = mdev,
|
||||
.in_flight = ATOMIC_INIT(1),
|
||||
.done = COMPLETION_INITIALIZER_ONSTACK(ctx.done),
|
||||
.flags = BM_AIO_COPY_PAGES,
|
||||
};
|
||||
struct bm_aio_ctx *ctx;
|
||||
int err;
|
||||
|
||||
if (bm_test_page_unchanged(mdev->bitmap->bm_pages[idx])) {
|
||||
dynamic_dev_dbg(DEV, "skipped bm page write for idx %u\n", idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bm_page_io_async(&ctx, idx, WRITE_SYNC);
|
||||
wait_for_completion(&ctx.done);
|
||||
ctx = kmalloc(sizeof(struct bm_aio_ctx), GFP_NOIO);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ctx.error)
|
||||
*ctx = (struct bm_aio_ctx) {
|
||||
.mdev = mdev,
|
||||
.in_flight = ATOMIC_INIT(1),
|
||||
.done = 0,
|
||||
.flags = BM_AIO_COPY_PAGES,
|
||||
.error = 0,
|
||||
.kref = { ATOMIC_INIT(2) },
|
||||
};
|
||||
|
||||
if (!get_ldev_if_state(mdev, D_ATTACHING)) { /* put is in bm_aio_ctx_destroy() */
|
||||
dev_err(DEV, "ASSERT FAILED: get_ldev_if_state() == 1 in drbd_bm_write_page()\n");
|
||||
kfree(ctx);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bm_page_io_async(ctx, idx, WRITE_SYNC);
|
||||
wait_until_done_or_disk_failure(mdev, mdev->ldev, &ctx->done);
|
||||
|
||||
if (ctx->error)
|
||||
drbd_chk_io_error(mdev, 1, true);
|
||||
/* that should force detach, so the in memory bitmap will be
|
||||
* gone in a moment as well. */
|
||||
|
||||
mdev->bm_writ_cnt++;
|
||||
return ctx.error;
|
||||
err = atomic_read(&ctx->in_flight) ? -EIO : ctx->error;
|
||||
kref_put(&ctx->kref, &bm_aio_ctx_destroy);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* NOTE
|
||||
|
||||
@@ -712,7 +712,6 @@ struct drbd_request {
|
||||
struct list_head tl_requests; /* ring list in the transfer log */
|
||||
struct bio *master_bio; /* master bio pointer */
|
||||
unsigned long rq_state; /* see comments above _req_mod() */
|
||||
int seq_num;
|
||||
unsigned long start_time;
|
||||
};
|
||||
|
||||
@@ -851,6 +850,7 @@ enum {
|
||||
NEW_CUR_UUID, /* Create new current UUID when thawing IO */
|
||||
AL_SUSPENDED, /* Activity logging is currently suspended. */
|
||||
AHEAD_TO_SYNC_SOURCE, /* Ahead -> SyncSource queued */
|
||||
STATE_SENT, /* Do not change state/UUIDs while this is set */
|
||||
};
|
||||
|
||||
struct drbd_bitmap; /* opaque for drbd_conf */
|
||||
@@ -862,31 +862,30 @@ enum bm_flag {
|
||||
BM_P_VMALLOCED = 0x10000, /* internal use only, will be masked out */
|
||||
|
||||
/* currently locked for bulk operation */
|
||||
BM_LOCKED_MASK = 0x7,
|
||||
BM_LOCKED_MASK = 0xf,
|
||||
|
||||
/* in detail, that is: */
|
||||
BM_DONT_CLEAR = 0x1,
|
||||
BM_DONT_SET = 0x2,
|
||||
BM_DONT_TEST = 0x4,
|
||||
|
||||
/* so we can mark it locked for bulk operation,
|
||||
* and still allow all non-bulk operations */
|
||||
BM_IS_LOCKED = 0x8,
|
||||
|
||||
/* (test bit, count bit) allowed (common case) */
|
||||
BM_LOCKED_TEST_ALLOWED = 0x3,
|
||||
BM_LOCKED_TEST_ALLOWED = BM_DONT_CLEAR | BM_DONT_SET | BM_IS_LOCKED,
|
||||
|
||||
/* testing bits, as well as setting new bits allowed, but clearing bits
|
||||
* would be unexpected. Used during bitmap receive. Setting new bits
|
||||
* requires sending of "out-of-sync" information, though. */
|
||||
BM_LOCKED_SET_ALLOWED = 0x1,
|
||||
BM_LOCKED_SET_ALLOWED = BM_DONT_CLEAR | BM_IS_LOCKED,
|
||||
|
||||
/* clear is not expected while bitmap is locked for bulk operation */
|
||||
/* for drbd_bm_write_copy_pages, everything is allowed,
|
||||
* only concurrent bulk operations are locked out. */
|
||||
BM_LOCKED_CHANGE_ALLOWED = BM_IS_LOCKED,
|
||||
};
|
||||
|
||||
|
||||
/* TODO sort members for performance
|
||||
* MAYBE group them further */
|
||||
|
||||
/* THINK maybe we actually want to use the default "event/%s" worker threads
|
||||
* or similar in linux 2.6, which uses per cpu data and threads.
|
||||
*/
|
||||
struct drbd_work_queue {
|
||||
struct list_head q;
|
||||
struct semaphore s; /* producers up it, worker down()s it */
|
||||
@@ -938,8 +937,7 @@ struct drbd_backing_dev {
|
||||
};
|
||||
|
||||
struct drbd_md_io {
|
||||
struct drbd_conf *mdev;
|
||||
struct completion event;
|
||||
unsigned int done;
|
||||
int error;
|
||||
};
|
||||
|
||||
@@ -1022,6 +1020,7 @@ struct drbd_conf {
|
||||
struct drbd_tl_epoch *newest_tle;
|
||||
struct drbd_tl_epoch *oldest_tle;
|
||||
struct list_head out_of_sequence_requests;
|
||||
struct list_head barrier_acked_requests;
|
||||
struct hlist_head *tl_hash;
|
||||
unsigned int tl_hash_s;
|
||||
|
||||
@@ -1056,6 +1055,8 @@ struct drbd_conf {
|
||||
struct crypto_hash *csums_tfm;
|
||||
struct crypto_hash *verify_tfm;
|
||||
|
||||
unsigned long last_reattach_jif;
|
||||
unsigned long last_reconnect_jif;
|
||||
struct drbd_thread receiver;
|
||||
struct drbd_thread worker;
|
||||
struct drbd_thread asender;
|
||||
@@ -1094,7 +1095,8 @@ struct drbd_conf {
|
||||
wait_queue_head_t ee_wait;
|
||||
struct page *md_io_page; /* one page buffer for md_io */
|
||||
struct page *md_io_tmpp; /* for logical_block_size != 512 */
|
||||
struct mutex md_io_mutex; /* protects the md_io_buffer */
|
||||
struct drbd_md_io md_io;
|
||||
atomic_t md_io_in_use; /* protects the md_io, md_io_page and md_io_tmpp */
|
||||
spinlock_t al_lock;
|
||||
wait_queue_head_t al_wait;
|
||||
struct lru_cache *act_log; /* activity log */
|
||||
@@ -1228,8 +1230,8 @@ extern int drbd_send_uuids(struct drbd_conf *mdev);
|
||||
extern int drbd_send_uuids_skip_initial_sync(struct drbd_conf *mdev);
|
||||
extern int drbd_gen_and_send_sync_uuid(struct drbd_conf *mdev);
|
||||
extern int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags flags);
|
||||
extern int _drbd_send_state(struct drbd_conf *mdev);
|
||||
extern int drbd_send_state(struct drbd_conf *mdev);
|
||||
extern int drbd_send_state(struct drbd_conf *mdev, union drbd_state s);
|
||||
extern int drbd_send_current_state(struct drbd_conf *mdev);
|
||||
extern int _drbd_send_cmd(struct drbd_conf *mdev, struct socket *sock,
|
||||
enum drbd_packets cmd, struct p_header80 *h,
|
||||
size_t size, unsigned msg_flags);
|
||||
@@ -1461,6 +1463,7 @@ extern int drbd_bm_e_weight(struct drbd_conf *mdev, unsigned long enr);
|
||||
extern int drbd_bm_write_page(struct drbd_conf *mdev, unsigned int idx) __must_hold(local);
|
||||
extern int drbd_bm_read(struct drbd_conf *mdev) __must_hold(local);
|
||||
extern int drbd_bm_write(struct drbd_conf *mdev) __must_hold(local);
|
||||
extern int drbd_bm_write_copy_pages(struct drbd_conf *mdev) __must_hold(local);
|
||||
extern unsigned long drbd_bm_ALe_set_all(struct drbd_conf *mdev,
|
||||
unsigned long al_enr);
|
||||
extern size_t drbd_bm_words(struct drbd_conf *mdev);
|
||||
@@ -1493,11 +1496,38 @@ extern struct kmem_cache *drbd_al_ext_cache; /* activity log extents */
|
||||
extern mempool_t *drbd_request_mempool;
|
||||
extern mempool_t *drbd_ee_mempool;
|
||||
|
||||
extern struct page *drbd_pp_pool; /* drbd's page pool */
|
||||
/* drbd's page pool, used to buffer data received from the peer,
|
||||
* or data requested by the peer.
|
||||
*
|
||||
* This does not have an emergency reserve.
|
||||
*
|
||||
* When allocating from this pool, it first takes pages from the pool.
|
||||
* Only if the pool is depleted will try to allocate from the system.
|
||||
*
|
||||
* The assumption is that pages taken from this pool will be processed,
|
||||
* and given back, "quickly", and then can be recycled, so we can avoid
|
||||
* frequent calls to alloc_page(), and still will be able to make progress even
|
||||
* under memory pressure.
|
||||
*/
|
||||
extern struct page *drbd_pp_pool;
|
||||
extern spinlock_t drbd_pp_lock;
|
||||
extern int drbd_pp_vacant;
|
||||
extern wait_queue_head_t drbd_pp_wait;
|
||||
|
||||
/* We also need a standard (emergency-reserve backed) page pool
|
||||
* for meta data IO (activity log, bitmap).
|
||||
* We can keep it global, as long as it is used as "N pages at a time".
|
||||
* 128 should be plenty, currently we probably can get away with as few as 1.
|
||||
*/
|
||||
#define DRBD_MIN_POOL_PAGES 128
|
||||
extern mempool_t *drbd_md_io_page_pool;
|
||||
|
||||
/* We also need to make sure we get a bio
|
||||
* when we need it for housekeeping purposes */
|
||||
extern struct bio_set *drbd_md_io_bio_set;
|
||||
/* to allocate from that set */
|
||||
extern struct bio *bio_alloc_drbd(gfp_t gfp_mask);
|
||||
|
||||
extern rwlock_t global_state_lock;
|
||||
|
||||
extern struct drbd_conf *drbd_new_device(unsigned int minor);
|
||||
@@ -1536,8 +1566,12 @@ extern void resume_next_sg(struct drbd_conf *mdev);
|
||||
extern void suspend_other_sg(struct drbd_conf *mdev);
|
||||
extern int drbd_resync_finished(struct drbd_conf *mdev);
|
||||
/* maybe rather drbd_main.c ? */
|
||||
extern void *drbd_md_get_buffer(struct drbd_conf *mdev);
|
||||
extern void drbd_md_put_buffer(struct drbd_conf *mdev);
|
||||
extern int drbd_md_sync_page_io(struct drbd_conf *mdev,
|
||||
struct drbd_backing_dev *bdev, sector_t sector, int rw);
|
||||
struct drbd_backing_dev *bdev, sector_t sector, int rw);
|
||||
extern void wait_until_done_or_disk_failure(struct drbd_conf *mdev, struct drbd_backing_dev *bdev,
|
||||
unsigned int *done);
|
||||
extern void drbd_ov_oos_found(struct drbd_conf*, sector_t, int);
|
||||
extern void drbd_rs_controller_reset(struct drbd_conf *mdev);
|
||||
|
||||
@@ -1754,19 +1788,6 @@ static inline struct page *page_chain_next(struct page *page)
|
||||
#define page_chain_for_each_safe(page, n) \
|
||||
for (; page && ({ n = page_chain_next(page); 1; }); page = n)
|
||||
|
||||
static inline int drbd_bio_has_active_page(struct bio *bio)
|
||||
{
|
||||
struct bio_vec *bvec;
|
||||
int i;
|
||||
|
||||
__bio_for_each_segment(bvec, bio, i, 0) {
|
||||
if (page_count(bvec->bv_page) > 1)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int drbd_ee_has_active_page(struct drbd_epoch_entry *e)
|
||||
{
|
||||
struct page *page = e->pages;
|
||||
@@ -1777,7 +1798,6 @@ static inline int drbd_ee_has_active_page(struct drbd_epoch_entry *e)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static inline void drbd_state_lock(struct drbd_conf *mdev)
|
||||
{
|
||||
wait_event(mdev->misc_wait,
|
||||
@@ -2230,7 +2250,7 @@ static inline void drbd_get_syncer_progress(struct drbd_conf *mdev,
|
||||
* Note: currently we don't support such large bitmaps on 32bit
|
||||
* arch anyways, but no harm done to be prepared for it here.
|
||||
*/
|
||||
unsigned int shift = mdev->rs_total >= (1ULL << 32) ? 16 : 10;
|
||||
unsigned int shift = mdev->rs_total > UINT_MAX ? 16 : 10;
|
||||
unsigned long left = *bits_left >> shift;
|
||||
unsigned long total = 1UL + (mdev->rs_total >> shift);
|
||||
unsigned long tmp = 1000UL - left * 1000UL/total;
|
||||
@@ -2306,12 +2326,12 @@ static inline int drbd_state_is_stable(struct drbd_conf *mdev)
|
||||
case D_OUTDATED:
|
||||
case D_CONSISTENT:
|
||||
case D_UP_TO_DATE:
|
||||
case D_FAILED:
|
||||
/* disk state is stable as well. */
|
||||
break;
|
||||
|
||||
/* no new io accepted during tansitional states */
|
||||
case D_ATTACHING:
|
||||
case D_FAILED:
|
||||
case D_NEGOTIATING:
|
||||
case D_UNKNOWN:
|
||||
case D_MASK:
|
||||
|
||||
+265
-84
@@ -139,6 +139,8 @@ struct kmem_cache *drbd_bm_ext_cache; /* bitmap extents */
|
||||
struct kmem_cache *drbd_al_ext_cache; /* activity log extents */
|
||||
mempool_t *drbd_request_mempool;
|
||||
mempool_t *drbd_ee_mempool;
|
||||
mempool_t *drbd_md_io_page_pool;
|
||||
struct bio_set *drbd_md_io_bio_set;
|
||||
|
||||
/* I do not use a standard mempool, because:
|
||||
1) I want to hand out the pre-allocated objects first.
|
||||
@@ -159,7 +161,24 @@ static const struct block_device_operations drbd_ops = {
|
||||
.release = drbd_release,
|
||||
};
|
||||
|
||||
#define ARRY_SIZE(A) (sizeof(A)/sizeof(A[0]))
|
||||
static void bio_destructor_drbd(struct bio *bio)
|
||||
{
|
||||
bio_free(bio, drbd_md_io_bio_set);
|
||||
}
|
||||
|
||||
struct bio *bio_alloc_drbd(gfp_t gfp_mask)
|
||||
{
|
||||
struct bio *bio;
|
||||
|
||||
if (!drbd_md_io_bio_set)
|
||||
return bio_alloc(gfp_mask, 1);
|
||||
|
||||
bio = bio_alloc_bioset(gfp_mask, 1, drbd_md_io_bio_set);
|
||||
if (!bio)
|
||||
return NULL;
|
||||
bio->bi_destructor = bio_destructor_drbd;
|
||||
return bio;
|
||||
}
|
||||
|
||||
#ifdef __CHECKER__
|
||||
/* When checking with sparse, and this is an inline function, sparse will
|
||||
@@ -208,6 +227,7 @@ static int tl_init(struct drbd_conf *mdev)
|
||||
mdev->oldest_tle = b;
|
||||
mdev->newest_tle = b;
|
||||
INIT_LIST_HEAD(&mdev->out_of_sequence_requests);
|
||||
INIT_LIST_HEAD(&mdev->barrier_acked_requests);
|
||||
|
||||
mdev->tl_hash = NULL;
|
||||
mdev->tl_hash_s = 0;
|
||||
@@ -246,9 +266,7 @@ void _tl_add_barrier(struct drbd_conf *mdev, struct drbd_tl_epoch *new)
|
||||
new->n_writes = 0;
|
||||
|
||||
newest_before = mdev->newest_tle;
|
||||
/* never send a barrier number == 0, because that is special-cased
|
||||
* when using TCQ for our write ordering code */
|
||||
new->br_number = (newest_before->br_number+1) ?: 1;
|
||||
new->br_number = newest_before->br_number+1;
|
||||
if (mdev->newest_tle != new) {
|
||||
mdev->newest_tle->next = new;
|
||||
mdev->newest_tle = new;
|
||||
@@ -311,7 +329,7 @@ void tl_release(struct drbd_conf *mdev, unsigned int barrier_nr,
|
||||
These have been list_move'd to the out_of_sequence_requests list in
|
||||
_req_mod(, barrier_acked) above.
|
||||
*/
|
||||
list_del_init(&b->requests);
|
||||
list_splice_init(&b->requests, &mdev->barrier_acked_requests);
|
||||
|
||||
nob = b->next;
|
||||
if (test_and_clear_bit(CREATE_BARRIER, &mdev->flags)) {
|
||||
@@ -411,6 +429,23 @@ static void _tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
|
||||
b = tmp;
|
||||
list_splice(&carry_reads, &b->requests);
|
||||
}
|
||||
|
||||
/* Actions operating on the disk state, also want to work on
|
||||
requests that got barrier acked. */
|
||||
switch (what) {
|
||||
case fail_frozen_disk_io:
|
||||
case restart_frozen_disk_io:
|
||||
list_for_each_safe(le, tle, &mdev->barrier_acked_requests) {
|
||||
req = list_entry(le, struct drbd_request, tl_requests);
|
||||
_req_mod(req, what);
|
||||
}
|
||||
|
||||
case connection_lost_while_pending:
|
||||
case resend:
|
||||
break;
|
||||
default:
|
||||
dev_err(DEV, "what = %d in _tl_restart()\n", what);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -457,6 +492,38 @@ void tl_restart(struct drbd_conf *mdev, enum drbd_req_event what)
|
||||
spin_unlock_irq(&mdev->req_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* tl_abort_disk_io() - Abort disk I/O for all requests for a certain mdev in the TL
|
||||
* @mdev: DRBD device.
|
||||
*/
|
||||
void tl_abort_disk_io(struct drbd_conf *mdev)
|
||||
{
|
||||
struct drbd_tl_epoch *b;
|
||||
struct list_head *le, *tle;
|
||||
struct drbd_request *req;
|
||||
|
||||
spin_lock_irq(&mdev->req_lock);
|
||||
b = mdev->oldest_tle;
|
||||
while (b) {
|
||||
list_for_each_safe(le, tle, &b->requests) {
|
||||
req = list_entry(le, struct drbd_request, tl_requests);
|
||||
if (!(req->rq_state & RQ_LOCAL_PENDING))
|
||||
continue;
|
||||
_req_mod(req, abort_disk_io);
|
||||
}
|
||||
b = b->next;
|
||||
}
|
||||
|
||||
list_for_each_safe(le, tle, &mdev->barrier_acked_requests) {
|
||||
req = list_entry(le, struct drbd_request, tl_requests);
|
||||
if (!(req->rq_state & RQ_LOCAL_PENDING))
|
||||
continue;
|
||||
_req_mod(req, abort_disk_io);
|
||||
}
|
||||
|
||||
spin_unlock_irq(&mdev->req_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* cl_wide_st_chg() - true if the state change is a cluster wide one
|
||||
* @mdev: DRBD device.
|
||||
@@ -470,7 +537,7 @@ static int cl_wide_st_chg(struct drbd_conf *mdev,
|
||||
((os.role != R_PRIMARY && ns.role == R_PRIMARY) ||
|
||||
(os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) ||
|
||||
(os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S) ||
|
||||
(os.disk != D_DISKLESS && ns.disk == D_DISKLESS))) ||
|
||||
(os.disk != D_FAILED && ns.disk == D_FAILED))) ||
|
||||
(os.conn >= C_CONNECTED && ns.conn == C_DISCONNECTING) ||
|
||||
(os.conn == C_CONNECTED && ns.conn == C_VERIFY_S);
|
||||
}
|
||||
@@ -509,8 +576,16 @@ static enum drbd_state_rv is_valid_state(struct drbd_conf *, union drbd_state);
|
||||
static enum drbd_state_rv is_valid_state_transition(struct drbd_conf *,
|
||||
union drbd_state,
|
||||
union drbd_state);
|
||||
enum sanitize_state_warnings {
|
||||
NO_WARNING,
|
||||
ABORTED_ONLINE_VERIFY,
|
||||
ABORTED_RESYNC,
|
||||
CONNECTION_LOST_NEGOTIATING,
|
||||
IMPLICITLY_UPGRADED_DISK,
|
||||
IMPLICITLY_UPGRADED_PDSK,
|
||||
};
|
||||
static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os,
|
||||
union drbd_state ns, const char **warn_sync_abort);
|
||||
union drbd_state ns, enum sanitize_state_warnings *warn);
|
||||
int drbd_send_state_req(struct drbd_conf *,
|
||||
union drbd_state, union drbd_state);
|
||||
|
||||
@@ -785,6 +860,13 @@ is_valid_state_transition(struct drbd_conf *mdev, union drbd_state ns,
|
||||
if (ns.conn == os.conn && ns.conn == C_WF_REPORT_PARAMS)
|
||||
rv = SS_IN_TRANSIENT_STATE;
|
||||
|
||||
/* While establishing a connection only allow cstate to change.
|
||||
Delay/refuse role changes, detach attach etc... */
|
||||
if (test_bit(STATE_SENT, &mdev->flags) &&
|
||||
!(os.conn == C_WF_REPORT_PARAMS ||
|
||||
(ns.conn == C_WF_REPORT_PARAMS && os.conn == C_WF_CONNECTION)))
|
||||
rv = SS_IN_TRANSIENT_STATE;
|
||||
|
||||
if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && os.conn < C_CONNECTED)
|
||||
rv = SS_NEED_CONNECTION;
|
||||
|
||||
@@ -803,6 +885,21 @@ is_valid_state_transition(struct drbd_conf *mdev, union drbd_state ns,
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void print_sanitize_warnings(struct drbd_conf *mdev, enum sanitize_state_warnings warn)
|
||||
{
|
||||
static const char *msg_table[] = {
|
||||
[NO_WARNING] = "",
|
||||
[ABORTED_ONLINE_VERIFY] = "Online-verify aborted.",
|
||||
[ABORTED_RESYNC] = "Resync aborted.",
|
||||
[CONNECTION_LOST_NEGOTIATING] = "Connection lost while negotiating, no data!",
|
||||
[IMPLICITLY_UPGRADED_DISK] = "Implicitly upgraded disk",
|
||||
[IMPLICITLY_UPGRADED_PDSK] = "Implicitly upgraded pdsk",
|
||||
};
|
||||
|
||||
if (warn != NO_WARNING)
|
||||
dev_warn(DEV, "%s\n", msg_table[warn]);
|
||||
}
|
||||
|
||||
/**
|
||||
* sanitize_state() - Resolves implicitly necessary additional changes to a state transition
|
||||
* @mdev: DRBD device.
|
||||
@@ -814,11 +911,14 @@ is_valid_state_transition(struct drbd_conf *mdev, union drbd_state ns,
|
||||
* to D_UNKNOWN. This rule and many more along those lines are in this function.
|
||||
*/
|
||||
static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state os,
|
||||
union drbd_state ns, const char **warn_sync_abort)
|
||||
union drbd_state ns, enum sanitize_state_warnings *warn)
|
||||
{
|
||||
enum drbd_fencing_p fp;
|
||||
enum drbd_disk_state disk_min, disk_max, pdsk_min, pdsk_max;
|
||||
|
||||
if (warn)
|
||||
*warn = NO_WARNING;
|
||||
|
||||
fp = FP_DONT_CARE;
|
||||
if (get_ldev(mdev)) {
|
||||
fp = mdev->ldev->dc.fencing;
|
||||
@@ -833,18 +933,13 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state
|
||||
/* After a network error (+C_TEAR_DOWN) only C_UNCONNECTED or C_DISCONNECTING can follow.
|
||||
* If you try to go into some Sync* state, that shall fail (elsewhere). */
|
||||
if (os.conn >= C_TIMEOUT && os.conn <= C_TEAR_DOWN &&
|
||||
ns.conn != C_UNCONNECTED && ns.conn != C_DISCONNECTING && ns.conn <= C_TEAR_DOWN)
|
||||
ns.conn != C_UNCONNECTED && ns.conn != C_DISCONNECTING && ns.conn <= C_CONNECTED)
|
||||
ns.conn = os.conn;
|
||||
|
||||
/* we cannot fail (again) if we already detached */
|
||||
if (ns.disk == D_FAILED && os.disk == D_DISKLESS)
|
||||
ns.disk = D_DISKLESS;
|
||||
|
||||
/* if we are only D_ATTACHING yet,
|
||||
* we can (and should) go directly to D_DISKLESS. */
|
||||
if (ns.disk == D_FAILED && os.disk == D_ATTACHING)
|
||||
ns.disk = D_DISKLESS;
|
||||
|
||||
/* After C_DISCONNECTING only C_STANDALONE may follow */
|
||||
if (os.conn == C_DISCONNECTING && ns.conn != C_STANDALONE)
|
||||
ns.conn = os.conn;
|
||||
@@ -863,10 +958,9 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state
|
||||
/* Abort resync if a disk fails/detaches */
|
||||
if (os.conn > C_CONNECTED && ns.conn > C_CONNECTED &&
|
||||
(ns.disk <= D_FAILED || ns.pdsk <= D_FAILED)) {
|
||||
if (warn_sync_abort)
|
||||
*warn_sync_abort =
|
||||
os.conn == C_VERIFY_S || os.conn == C_VERIFY_T ?
|
||||
"Online-verify" : "Resync";
|
||||
if (warn)
|
||||
*warn = os.conn == C_VERIFY_S || os.conn == C_VERIFY_T ?
|
||||
ABORTED_ONLINE_VERIFY : ABORTED_RESYNC;
|
||||
ns.conn = C_CONNECTED;
|
||||
}
|
||||
|
||||
@@ -877,7 +971,8 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state
|
||||
ns.disk = mdev->new_state_tmp.disk;
|
||||
ns.pdsk = mdev->new_state_tmp.pdsk;
|
||||
} else {
|
||||
dev_alert(DEV, "Connection lost while negotiating, no data!\n");
|
||||
if (warn)
|
||||
*warn = CONNECTION_LOST_NEGOTIATING;
|
||||
ns.disk = D_DISKLESS;
|
||||
ns.pdsk = D_UNKNOWN;
|
||||
}
|
||||
@@ -959,16 +1054,16 @@ static union drbd_state sanitize_state(struct drbd_conf *mdev, union drbd_state
|
||||
ns.disk = disk_max;
|
||||
|
||||
if (ns.disk < disk_min) {
|
||||
dev_warn(DEV, "Implicitly set disk from %s to %s\n",
|
||||
drbd_disk_str(ns.disk), drbd_disk_str(disk_min));
|
||||
if (warn)
|
||||
*warn = IMPLICITLY_UPGRADED_DISK;
|
||||
ns.disk = disk_min;
|
||||
}
|
||||
if (ns.pdsk > pdsk_max)
|
||||
ns.pdsk = pdsk_max;
|
||||
|
||||
if (ns.pdsk < pdsk_min) {
|
||||
dev_warn(DEV, "Implicitly set pdsk from %s to %s\n",
|
||||
drbd_disk_str(ns.pdsk), drbd_disk_str(pdsk_min));
|
||||
if (warn)
|
||||
*warn = IMPLICITLY_UPGRADED_PDSK;
|
||||
ns.pdsk = pdsk_min;
|
||||
}
|
||||
|
||||
@@ -1045,12 +1140,12 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
|
||||
{
|
||||
union drbd_state os;
|
||||
enum drbd_state_rv rv = SS_SUCCESS;
|
||||
const char *warn_sync_abort = NULL;
|
||||
enum sanitize_state_warnings ssw;
|
||||
struct after_state_chg_work *ascw;
|
||||
|
||||
os = mdev->state;
|
||||
|
||||
ns = sanitize_state(mdev, os, ns, &warn_sync_abort);
|
||||
ns = sanitize_state(mdev, os, ns, &ssw);
|
||||
|
||||
if (ns.i == os.i)
|
||||
return SS_NOTHING_TO_DO;
|
||||
@@ -1076,8 +1171,7 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (warn_sync_abort)
|
||||
dev_warn(DEV, "%s aborted.\n", warn_sync_abort);
|
||||
print_sanitize_warnings(mdev, ssw);
|
||||
|
||||
{
|
||||
char *pbp, pb[300];
|
||||
@@ -1243,7 +1337,7 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
|
||||
drbd_thread_stop_nowait(&mdev->receiver);
|
||||
|
||||
/* Upon network failure, we need to restart the receiver. */
|
||||
if (os.conn > C_TEAR_DOWN &&
|
||||
if (os.conn > C_WF_CONNECTION &&
|
||||
ns.conn <= C_TEAR_DOWN && ns.conn >= C_TIMEOUT)
|
||||
drbd_thread_restart_nowait(&mdev->receiver);
|
||||
|
||||
@@ -1251,6 +1345,15 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns,
|
||||
if (os.conn < C_CONNECTED && ns.conn >= C_CONNECTED)
|
||||
drbd_resume_al(mdev);
|
||||
|
||||
/* remember last connect and attach times so request_timer_fn() won't
|
||||
* kill newly established sessions while we are still trying to thaw
|
||||
* previously frozen IO */
|
||||
if (os.conn != C_WF_REPORT_PARAMS && ns.conn == C_WF_REPORT_PARAMS)
|
||||
mdev->last_reconnect_jif = jiffies;
|
||||
if ((os.disk == D_ATTACHING || os.disk == D_NEGOTIATING) &&
|
||||
ns.disk > D_NEGOTIATING)
|
||||
mdev->last_reattach_jif = jiffies;
|
||||
|
||||
ascw = kmalloc(sizeof(*ascw), GFP_ATOMIC);
|
||||
if (ascw) {
|
||||
ascw->os = os;
|
||||
@@ -1354,12 +1457,16 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
|
||||
/* Here we have the actions that are performed after a
|
||||
state change. This function might sleep */
|
||||
|
||||
if (os.disk <= D_NEGOTIATING && ns.disk > D_NEGOTIATING)
|
||||
mod_timer(&mdev->request_timer, jiffies + HZ);
|
||||
|
||||
nsm.i = -1;
|
||||
if (ns.susp_nod) {
|
||||
if (os.conn < C_CONNECTED && ns.conn >= C_CONNECTED)
|
||||
what = resend;
|
||||
|
||||
if (os.disk == D_ATTACHING && ns.disk > D_ATTACHING)
|
||||
if ((os.disk == D_ATTACHING || os.disk == D_NEGOTIATING) &&
|
||||
ns.disk > D_NEGOTIATING)
|
||||
what = restart_frozen_disk_io;
|
||||
|
||||
if (what != nothing)
|
||||
@@ -1408,7 +1515,7 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
|
||||
/* Do not change the order of the if above and the two below... */
|
||||
if (os.pdsk == D_DISKLESS && ns.pdsk > D_DISKLESS) { /* attach on the peer */
|
||||
drbd_send_uuids(mdev);
|
||||
drbd_send_state(mdev);
|
||||
drbd_send_state(mdev, ns);
|
||||
}
|
||||
/* No point in queuing send_bitmap if we don't have a connection
|
||||
* anymore, so check also the _current_ state, not only the new state
|
||||
@@ -1441,11 +1548,11 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
|
||||
}
|
||||
|
||||
if (ns.pdsk < D_INCONSISTENT && get_ldev(mdev)) {
|
||||
if (ns.peer == R_PRIMARY && mdev->ldev->md.uuid[UI_BITMAP] == 0) {
|
||||
if (os.peer == R_SECONDARY && ns.peer == R_PRIMARY &&
|
||||
mdev->ldev->md.uuid[UI_BITMAP] == 0 && ns.disk >= D_UP_TO_DATE) {
|
||||
drbd_uuid_new_current(mdev);
|
||||
drbd_send_uuids(mdev);
|
||||
}
|
||||
|
||||
/* D_DISKLESS Peer becomes secondary */
|
||||
if (os.peer == R_PRIMARY && ns.peer == R_SECONDARY)
|
||||
/* We may still be Primary ourselves.
|
||||
@@ -1473,14 +1580,14 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
|
||||
os.disk == D_ATTACHING && ns.disk == D_NEGOTIATING) {
|
||||
drbd_send_sizes(mdev, 0, 0); /* to start sync... */
|
||||
drbd_send_uuids(mdev);
|
||||
drbd_send_state(mdev);
|
||||
drbd_send_state(mdev, ns);
|
||||
}
|
||||
|
||||
/* We want to pause/continue resync, tell peer. */
|
||||
if (ns.conn >= C_CONNECTED &&
|
||||
((os.aftr_isp != ns.aftr_isp) ||
|
||||
(os.user_isp != ns.user_isp)))
|
||||
drbd_send_state(mdev);
|
||||
drbd_send_state(mdev, ns);
|
||||
|
||||
/* In case one of the isp bits got set, suspend other devices. */
|
||||
if ((!os.aftr_isp && !os.peer_isp && !os.user_isp) &&
|
||||
@@ -1490,10 +1597,10 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
|
||||
/* Make sure the peer gets informed about eventual state
|
||||
changes (ISP bits) while we were in WFReportParams. */
|
||||
if (os.conn == C_WF_REPORT_PARAMS && ns.conn >= C_CONNECTED)
|
||||
drbd_send_state(mdev);
|
||||
drbd_send_state(mdev, ns);
|
||||
|
||||
if (os.conn != C_AHEAD && ns.conn == C_AHEAD)
|
||||
drbd_send_state(mdev);
|
||||
drbd_send_state(mdev, ns);
|
||||
|
||||
/* We are in the progress to start a full sync... */
|
||||
if ((os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) ||
|
||||
@@ -1513,33 +1620,38 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
|
||||
/* first half of local IO error, failure to attach,
|
||||
* or administrative detach */
|
||||
if (os.disk != D_FAILED && ns.disk == D_FAILED) {
|
||||
enum drbd_io_error_p eh;
|
||||
int was_io_error;
|
||||
enum drbd_io_error_p eh = EP_PASS_ON;
|
||||
int was_io_error = 0;
|
||||
/* corresponding get_ldev was in __drbd_set_state, to serialize
|
||||
* our cleanup here with the transition to D_DISKLESS,
|
||||
* so it is safe to dreference ldev here. */
|
||||
eh = mdev->ldev->dc.on_io_error;
|
||||
was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags);
|
||||
* our cleanup here with the transition to D_DISKLESS.
|
||||
* But is is still not save to dreference ldev here, since
|
||||
* we might come from an failed Attach before ldev was set. */
|
||||
if (mdev->ldev) {
|
||||
eh = mdev->ldev->dc.on_io_error;
|
||||
was_io_error = test_and_clear_bit(WAS_IO_ERROR, &mdev->flags);
|
||||
|
||||
/* current state still has to be D_FAILED,
|
||||
* there is only one way out: to D_DISKLESS,
|
||||
* and that may only happen after our put_ldev below. */
|
||||
if (mdev->state.disk != D_FAILED)
|
||||
dev_err(DEV,
|
||||
"ASSERT FAILED: disk is %s during detach\n",
|
||||
drbd_disk_str(mdev->state.disk));
|
||||
/* Immediately allow completion of all application IO, that waits
|
||||
for completion from the local disk. */
|
||||
tl_abort_disk_io(mdev);
|
||||
|
||||
if (drbd_send_state(mdev))
|
||||
dev_warn(DEV, "Notified peer that I am detaching my disk\n");
|
||||
else
|
||||
dev_err(DEV, "Sending state for detaching disk failed\n");
|
||||
/* current state still has to be D_FAILED,
|
||||
* there is only one way out: to D_DISKLESS,
|
||||
* and that may only happen after our put_ldev below. */
|
||||
if (mdev->state.disk != D_FAILED)
|
||||
dev_err(DEV,
|
||||
"ASSERT FAILED: disk is %s during detach\n",
|
||||
drbd_disk_str(mdev->state.disk));
|
||||
|
||||
drbd_rs_cancel_all(mdev);
|
||||
if (ns.conn >= C_CONNECTED)
|
||||
drbd_send_state(mdev, ns);
|
||||
|
||||
/* In case we want to get something to stable storage still,
|
||||
* this may be the last chance.
|
||||
* Following put_ldev may transition to D_DISKLESS. */
|
||||
drbd_md_sync(mdev);
|
||||
drbd_rs_cancel_all(mdev);
|
||||
|
||||
/* In case we want to get something to stable storage still,
|
||||
* this may be the last chance.
|
||||
* Following put_ldev may transition to D_DISKLESS. */
|
||||
drbd_md_sync(mdev);
|
||||
}
|
||||
put_ldev(mdev);
|
||||
|
||||
if (was_io_error && eh == EP_CALL_HELPER)
|
||||
@@ -1561,16 +1673,17 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
|
||||
mdev->rs_failed = 0;
|
||||
atomic_set(&mdev->rs_pending_cnt, 0);
|
||||
|
||||
if (drbd_send_state(mdev))
|
||||
dev_warn(DEV, "Notified peer that I'm now diskless.\n");
|
||||
if (ns.conn >= C_CONNECTED)
|
||||
drbd_send_state(mdev, ns);
|
||||
|
||||
/* corresponding get_ldev in __drbd_set_state
|
||||
* this may finally trigger drbd_ldev_destroy. */
|
||||
put_ldev(mdev);
|
||||
}
|
||||
|
||||
/* Notify peer that I had a local IO error, and did not detached.. */
|
||||
if (os.disk == D_UP_TO_DATE && ns.disk == D_INCONSISTENT)
|
||||
drbd_send_state(mdev);
|
||||
if (os.disk == D_UP_TO_DATE && ns.disk == D_INCONSISTENT && ns.conn >= C_CONNECTED)
|
||||
drbd_send_state(mdev, ns);
|
||||
|
||||
/* Disks got bigger while they were detached */
|
||||
if (ns.disk > D_NEGOTIATING && ns.pdsk > D_NEGOTIATING &&
|
||||
@@ -1588,7 +1701,13 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
|
||||
/* sync target done with resync. Explicitly notify peer, even though
|
||||
* it should (at least for non-empty resyncs) already know itself. */
|
||||
if (os.disk < D_UP_TO_DATE && os.conn >= C_SYNC_SOURCE && ns.conn == C_CONNECTED)
|
||||
drbd_send_state(mdev);
|
||||
drbd_send_state(mdev, ns);
|
||||
|
||||
/* Wake up role changes, that were delayed because of connection establishing */
|
||||
if (os.conn == C_WF_REPORT_PARAMS && ns.conn != C_WF_REPORT_PARAMS) {
|
||||
clear_bit(STATE_SENT, &mdev->flags);
|
||||
wake_up(&mdev->state_wait);
|
||||
}
|
||||
|
||||
/* This triggers bitmap writeout of potentially still unwritten pages
|
||||
* if the resync finished cleanly, or aborted because of peer disk
|
||||
@@ -1598,8 +1717,8 @@ static void after_state_ch(struct drbd_conf *mdev, union drbd_state os,
|
||||
* No harm done if some bits change during this phase.
|
||||
*/
|
||||
if (os.conn > C_CONNECTED && ns.conn <= C_CONNECTED && get_ldev(mdev)) {
|
||||
drbd_queue_bitmap_io(mdev, &drbd_bm_write, NULL,
|
||||
"write from resync_finished", BM_LOCKED_SET_ALLOWED);
|
||||
drbd_queue_bitmap_io(mdev, &drbd_bm_write_copy_pages, NULL,
|
||||
"write from resync_finished", BM_LOCKED_CHANGE_ALLOWED);
|
||||
put_ldev(mdev);
|
||||
}
|
||||
|
||||
@@ -2057,7 +2176,11 @@ int drbd_gen_and_send_sync_uuid(struct drbd_conf *mdev)
|
||||
|
||||
D_ASSERT(mdev->state.disk == D_UP_TO_DATE);
|
||||
|
||||
uuid = mdev->ldev->md.uuid[UI_BITMAP] + UUID_NEW_BM_OFFSET;
|
||||
uuid = mdev->ldev->md.uuid[UI_BITMAP];
|
||||
if (uuid && uuid != UUID_JUST_CREATED)
|
||||
uuid = uuid + UUID_NEW_BM_OFFSET;
|
||||
else
|
||||
get_random_bytes(&uuid, sizeof(u64));
|
||||
drbd_uuid_set(mdev, UI_BITMAP, uuid);
|
||||
drbd_print_uuids(mdev, "updated sync UUID");
|
||||
drbd_md_sync(mdev);
|
||||
@@ -2089,6 +2212,10 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
|
||||
max_bio_size = DRBD_MAX_BIO_SIZE; /* ... multiple BIOs per peer_request */
|
||||
}
|
||||
|
||||
/* Never allow old drbd (up to 8.3.7) to see more than 32KiB */
|
||||
if (mdev->agreed_pro_version <= 94)
|
||||
max_bio_size = min_t(int, max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
|
||||
|
||||
p.d_size = cpu_to_be64(d_size);
|
||||
p.u_size = cpu_to_be64(u_size);
|
||||
p.c_size = cpu_to_be64(trigger_reply ? 0 : drbd_get_capacity(mdev->this_bdev));
|
||||
@@ -2102,10 +2229,10 @@ int drbd_send_sizes(struct drbd_conf *mdev, int trigger_reply, enum dds_flags fl
|
||||
}
|
||||
|
||||
/**
|
||||
* drbd_send_state() - Sends the drbd state to the peer
|
||||
* drbd_send_current_state() - Sends the drbd state to the peer
|
||||
* @mdev: DRBD device.
|
||||
*/
|
||||
int drbd_send_state(struct drbd_conf *mdev)
|
||||
int drbd_send_current_state(struct drbd_conf *mdev)
|
||||
{
|
||||
struct socket *sock;
|
||||
struct p_state p;
|
||||
@@ -2131,6 +2258,37 @@ int drbd_send_state(struct drbd_conf *mdev)
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* drbd_send_state() - After a state change, sends the new state to the peer
|
||||
* @mdev: DRBD device.
|
||||
* @state: the state to send, not necessarily the current state.
|
||||
*
|
||||
* Each state change queues an "after_state_ch" work, which will eventually
|
||||
* send the resulting new state to the peer. If more state changes happen
|
||||
* between queuing and processing of the after_state_ch work, we still
|
||||
* want to send each intermediary state in the order it occurred.
|
||||
*/
|
||||
int drbd_send_state(struct drbd_conf *mdev, union drbd_state state)
|
||||
{
|
||||
struct socket *sock;
|
||||
struct p_state p;
|
||||
int ok = 0;
|
||||
|
||||
mutex_lock(&mdev->data.mutex);
|
||||
|
||||
p.state = cpu_to_be32(state.i);
|
||||
sock = mdev->data.socket;
|
||||
|
||||
if (likely(sock != NULL)) {
|
||||
ok = _drbd_send_cmd(mdev, sock, P_STATE,
|
||||
(struct p_header80 *)&p, sizeof(p), 0);
|
||||
}
|
||||
|
||||
mutex_unlock(&mdev->data.mutex);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int drbd_send_state_req(struct drbd_conf *mdev,
|
||||
union drbd_state mask, union drbd_state val)
|
||||
{
|
||||
@@ -2615,7 +2773,7 @@ static int _drbd_send_bio(struct drbd_conf *mdev, struct bio *bio)
|
||||
struct bio_vec *bvec;
|
||||
int i;
|
||||
/* hint all but last page with MSG_MORE */
|
||||
__bio_for_each_segment(bvec, bio, i, 0) {
|
||||
bio_for_each_segment(bvec, bio, i) {
|
||||
if (!_drbd_no_send_page(mdev, bvec->bv_page,
|
||||
bvec->bv_offset, bvec->bv_len,
|
||||
i == bio->bi_vcnt -1 ? 0 : MSG_MORE))
|
||||
@@ -2629,7 +2787,7 @@ static int _drbd_send_zc_bio(struct drbd_conf *mdev, struct bio *bio)
|
||||
struct bio_vec *bvec;
|
||||
int i;
|
||||
/* hint all but last page with MSG_MORE */
|
||||
__bio_for_each_segment(bvec, bio, i, 0) {
|
||||
bio_for_each_segment(bvec, bio, i) {
|
||||
if (!_drbd_send_page(mdev, bvec->bv_page,
|
||||
bvec->bv_offset, bvec->bv_len,
|
||||
i == bio->bi_vcnt -1 ? 0 : MSG_MORE))
|
||||
@@ -2695,8 +2853,7 @@ int drbd_send_dblock(struct drbd_conf *mdev, struct drbd_request *req)
|
||||
|
||||
p.sector = cpu_to_be64(req->sector);
|
||||
p.block_id = (unsigned long)req;
|
||||
p.seq_num = cpu_to_be32(req->seq_num =
|
||||
atomic_add_return(1, &mdev->packet_seq));
|
||||
p.seq_num = cpu_to_be32(atomic_add_return(1, &mdev->packet_seq));
|
||||
|
||||
dp_flags = bio_flags_to_wire(mdev, req->master_bio->bi_rw);
|
||||
|
||||
@@ -2987,8 +3144,8 @@ void drbd_init_set_defaults(struct drbd_conf *mdev)
|
||||
atomic_set(&mdev->rs_sect_in, 0);
|
||||
atomic_set(&mdev->rs_sect_ev, 0);
|
||||
atomic_set(&mdev->ap_in_flight, 0);
|
||||
atomic_set(&mdev->md_io_in_use, 0);
|
||||
|
||||
mutex_init(&mdev->md_io_mutex);
|
||||
mutex_init(&mdev->data.mutex);
|
||||
mutex_init(&mdev->meta.mutex);
|
||||
sema_init(&mdev->data.work.s, 0);
|
||||
@@ -3126,6 +3283,10 @@ static void drbd_destroy_mempools(void)
|
||||
|
||||
/* D_ASSERT(atomic_read(&drbd_pp_vacant)==0); */
|
||||
|
||||
if (drbd_md_io_bio_set)
|
||||
bioset_free(drbd_md_io_bio_set);
|
||||
if (drbd_md_io_page_pool)
|
||||
mempool_destroy(drbd_md_io_page_pool);
|
||||
if (drbd_ee_mempool)
|
||||
mempool_destroy(drbd_ee_mempool);
|
||||
if (drbd_request_mempool)
|
||||
@@ -3139,6 +3300,8 @@ static void drbd_destroy_mempools(void)
|
||||
if (drbd_al_ext_cache)
|
||||
kmem_cache_destroy(drbd_al_ext_cache);
|
||||
|
||||
drbd_md_io_bio_set = NULL;
|
||||
drbd_md_io_page_pool = NULL;
|
||||
drbd_ee_mempool = NULL;
|
||||
drbd_request_mempool = NULL;
|
||||
drbd_ee_cache = NULL;
|
||||
@@ -3162,6 +3325,8 @@ static int drbd_create_mempools(void)
|
||||
drbd_bm_ext_cache = NULL;
|
||||
drbd_al_ext_cache = NULL;
|
||||
drbd_pp_pool = NULL;
|
||||
drbd_md_io_page_pool = NULL;
|
||||
drbd_md_io_bio_set = NULL;
|
||||
|
||||
/* caches */
|
||||
drbd_request_cache = kmem_cache_create(
|
||||
@@ -3185,6 +3350,16 @@ static int drbd_create_mempools(void)
|
||||
goto Enomem;
|
||||
|
||||
/* mempools */
|
||||
#ifdef COMPAT_HAVE_BIOSET_CREATE
|
||||
drbd_md_io_bio_set = bioset_create(DRBD_MIN_POOL_PAGES, 0);
|
||||
if (drbd_md_io_bio_set == NULL)
|
||||
goto Enomem;
|
||||
#endif
|
||||
|
||||
drbd_md_io_page_pool = mempool_create_page_pool(DRBD_MIN_POOL_PAGES, 0);
|
||||
if (drbd_md_io_page_pool == NULL)
|
||||
goto Enomem;
|
||||
|
||||
drbd_request_mempool = mempool_create(number,
|
||||
mempool_alloc_slab, mempool_free_slab, drbd_request_cache);
|
||||
if (drbd_request_mempool == NULL)
|
||||
@@ -3262,6 +3437,8 @@ static void drbd_delete_device(unsigned int minor)
|
||||
if (!mdev)
|
||||
return;
|
||||
|
||||
del_timer_sync(&mdev->request_timer);
|
||||
|
||||
/* paranoia asserts */
|
||||
if (mdev->open_cnt != 0)
|
||||
dev_err(DEV, "open_cnt = %d in %s:%u", mdev->open_cnt,
|
||||
@@ -3666,8 +3843,10 @@ void drbd_md_sync(struct drbd_conf *mdev)
|
||||
if (!get_ldev_if_state(mdev, D_FAILED))
|
||||
return;
|
||||
|
||||
mutex_lock(&mdev->md_io_mutex);
|
||||
buffer = (struct meta_data_on_disk *)page_address(mdev->md_io_page);
|
||||
buffer = drbd_md_get_buffer(mdev);
|
||||
if (!buffer)
|
||||
goto out;
|
||||
|
||||
memset(buffer, 0, 512);
|
||||
|
||||
buffer->la_size = cpu_to_be64(drbd_get_capacity(mdev->this_bdev));
|
||||
@@ -3698,7 +3877,8 @@ void drbd_md_sync(struct drbd_conf *mdev)
|
||||
* since we updated it on metadata. */
|
||||
mdev->ldev->md.la_size_sect = drbd_get_capacity(mdev->this_bdev);
|
||||
|
||||
mutex_unlock(&mdev->md_io_mutex);
|
||||
drbd_md_put_buffer(mdev);
|
||||
out:
|
||||
put_ldev(mdev);
|
||||
}
|
||||
|
||||
@@ -3718,8 +3898,9 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
|
||||
if (!get_ldev_if_state(mdev, D_ATTACHING))
|
||||
return ERR_IO_MD_DISK;
|
||||
|
||||
mutex_lock(&mdev->md_io_mutex);
|
||||
buffer = (struct meta_data_on_disk *)page_address(mdev->md_io_page);
|
||||
buffer = drbd_md_get_buffer(mdev);
|
||||
if (!buffer)
|
||||
goto out;
|
||||
|
||||
if (!drbd_md_sync_page_io(mdev, bdev, bdev->md.md_offset, READ)) {
|
||||
/* NOTE: can't do normal error processing here as this is
|
||||
@@ -3780,7 +3961,8 @@ int drbd_md_read(struct drbd_conf *mdev, struct drbd_backing_dev *bdev)
|
||||
mdev->sync_conf.al_extents = 127;
|
||||
|
||||
err:
|
||||
mutex_unlock(&mdev->md_io_mutex);
|
||||
drbd_md_put_buffer(mdev);
|
||||
out:
|
||||
put_ldev(mdev);
|
||||
|
||||
return rv;
|
||||
@@ -4183,12 +4365,11 @@ const char *drbd_buildtag(void)
|
||||
static char buildtag[38] = "\0uilt-in";
|
||||
|
||||
if (buildtag[0] == 0) {
|
||||
#ifdef CONFIG_MODULES
|
||||
if (THIS_MODULE != NULL)
|
||||
sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion);
|
||||
else
|
||||
#ifdef MODULE
|
||||
sprintf(buildtag, "srcversion: %-24s", THIS_MODULE->srcversion);
|
||||
#else
|
||||
buildtag[0] = 'b';
|
||||
#endif
|
||||
buildtag[0] = 'b';
|
||||
}
|
||||
|
||||
return buildtag;
|
||||
|
||||
@@ -289,7 +289,7 @@ static int _try_outdate_peer_async(void *data)
|
||||
*/
|
||||
spin_lock_irq(&mdev->req_lock);
|
||||
ns = mdev->state;
|
||||
if (ns.conn < C_WF_REPORT_PARAMS) {
|
||||
if (ns.conn < C_WF_REPORT_PARAMS && !test_bit(STATE_SENT, &mdev->flags)) {
|
||||
ns.pdsk = nps;
|
||||
_drbd_set_state(mdev, ns, CS_VERBOSE, NULL);
|
||||
}
|
||||
@@ -432,7 +432,7 @@ drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force)
|
||||
/* if this was forced, we should consider sync */
|
||||
if (forced)
|
||||
drbd_send_uuids(mdev);
|
||||
drbd_send_state(mdev);
|
||||
drbd_send_current_state(mdev);
|
||||
}
|
||||
|
||||
drbd_md_sync(mdev);
|
||||
@@ -845,9 +845,10 @@ void drbd_reconsider_max_bio_size(struct drbd_conf *mdev)
|
||||
Because new from 8.3.8 onwards the peer can use multiple
|
||||
BIOs for a single peer_request */
|
||||
if (mdev->state.conn >= C_CONNECTED) {
|
||||
if (mdev->agreed_pro_version < 94)
|
||||
peer = mdev->peer_max_bio_size;
|
||||
else if (mdev->agreed_pro_version == 94)
|
||||
if (mdev->agreed_pro_version < 94) {
|
||||
peer = min_t(int, mdev->peer_max_bio_size, DRBD_MAX_SIZE_H80_PACKET);
|
||||
/* Correct old drbd (up to 8.3.7) if it believes it can do more than 32KiB */
|
||||
} else if (mdev->agreed_pro_version == 94)
|
||||
peer = DRBD_MAX_SIZE_H80_PACKET;
|
||||
else /* drbd 8.3.8 onwards */
|
||||
peer = DRBD_MAX_BIO_SIZE;
|
||||
@@ -1032,7 +1033,7 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
|
||||
dev_err(DEV, "max capacity %llu smaller than disk size %llu\n",
|
||||
(unsigned long long) drbd_get_max_capacity(nbc),
|
||||
(unsigned long long) nbc->dc.disk_size);
|
||||
retcode = ERR_DISK_TO_SMALL;
|
||||
retcode = ERR_DISK_TOO_SMALL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -1046,7 +1047,7 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
|
||||
}
|
||||
|
||||
if (drbd_get_capacity(nbc->md_bdev) < min_md_device_sectors) {
|
||||
retcode = ERR_MD_DISK_TO_SMALL;
|
||||
retcode = ERR_MD_DISK_TOO_SMALL;
|
||||
dev_warn(DEV, "refusing attach: md-device too small, "
|
||||
"at least %llu sectors needed for this meta-disk type\n",
|
||||
(unsigned long long) min_md_device_sectors);
|
||||
@@ -1057,7 +1058,7 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
|
||||
* (we may currently be R_PRIMARY with no local disk...) */
|
||||
if (drbd_get_max_capacity(nbc) <
|
||||
drbd_get_capacity(mdev->this_bdev)) {
|
||||
retcode = ERR_DISK_TO_SMALL;
|
||||
retcode = ERR_DISK_TOO_SMALL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -1138,7 +1139,7 @@ static int drbd_nl_disk_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp
|
||||
if (drbd_md_test_flag(nbc, MDF_CONSISTENT) &&
|
||||
drbd_new_dev_size(mdev, nbc, 0) < nbc->md.la_size_sect) {
|
||||
dev_warn(DEV, "refusing to truncate a consistent device\n");
|
||||
retcode = ERR_DISK_TO_SMALL;
|
||||
retcode = ERR_DISK_TOO_SMALL;
|
||||
goto force_diskless_dec;
|
||||
}
|
||||
|
||||
@@ -1336,17 +1337,34 @@ static int drbd_nl_detach(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
|
||||
{
|
||||
enum drbd_ret_code retcode;
|
||||
int ret;
|
||||
struct detach dt = {};
|
||||
|
||||
if (!detach_from_tags(mdev, nlp->tag_list, &dt)) {
|
||||
reply->ret_code = ERR_MANDATORY_TAG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dt.detach_force) {
|
||||
drbd_force_state(mdev, NS(disk, D_FAILED));
|
||||
reply->ret_code = SS_SUCCESS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
drbd_suspend_io(mdev); /* so no-one is stuck in drbd_al_begin_io */
|
||||
drbd_md_get_buffer(mdev); /* make sure there is no in-flight meta-data IO */
|
||||
retcode = drbd_request_state(mdev, NS(disk, D_FAILED));
|
||||
drbd_md_put_buffer(mdev);
|
||||
/* D_FAILED will transition to DISKLESS. */
|
||||
ret = wait_event_interruptible(mdev->misc_wait,
|
||||
mdev->state.disk != D_FAILED);
|
||||
drbd_resume_io(mdev);
|
||||
|
||||
if ((int)retcode == (int)SS_IS_DISKLESS)
|
||||
retcode = SS_NOTHING_TO_DO;
|
||||
if (ret)
|
||||
retcode = ERR_INTR;
|
||||
reply->ret_code = retcode;
|
||||
out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1711,7 +1729,7 @@ static int drbd_nl_resize(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
|
||||
|
||||
if (rs.no_resync && mdev->agreed_pro_version < 93) {
|
||||
retcode = ERR_NEED_APV_93;
|
||||
goto fail;
|
||||
goto fail_ldev;
|
||||
}
|
||||
|
||||
if (mdev->ldev->known_size != drbd_get_capacity(mdev->ldev->backing_bdev))
|
||||
@@ -1738,6 +1756,10 @@ static int drbd_nl_resize(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
|
||||
fail:
|
||||
reply->ret_code = retcode;
|
||||
return 0;
|
||||
|
||||
fail_ldev:
|
||||
put_ldev(mdev);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
static int drbd_nl_syncer_conf(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
|
||||
@@ -1941,6 +1963,7 @@ static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
|
||||
|
||||
/* If there is still bitmap IO pending, probably because of a previous
|
||||
* resync just being finished, wait for it before requesting a new resync. */
|
||||
drbd_suspend_io(mdev);
|
||||
wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
|
||||
|
||||
retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T), CS_ORDERED);
|
||||
@@ -1959,6 +1982,7 @@ static int drbd_nl_invalidate(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nl
|
||||
|
||||
retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_T));
|
||||
}
|
||||
drbd_resume_io(mdev);
|
||||
|
||||
reply->ret_code = retcode;
|
||||
return 0;
|
||||
@@ -1980,6 +2004,7 @@ static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_re
|
||||
|
||||
/* If there is still bitmap IO pending, probably because of a previous
|
||||
* resync just being finished, wait for it before requesting a new resync. */
|
||||
drbd_suspend_io(mdev);
|
||||
wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
|
||||
|
||||
retcode = _drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S), CS_ORDERED);
|
||||
@@ -1998,6 +2023,7 @@ static int drbd_nl_invalidate_peer(struct drbd_conf *mdev, struct drbd_nl_cfg_re
|
||||
} else
|
||||
retcode = drbd_request_state(mdev, NS(conn, C_STARTING_SYNC_S));
|
||||
}
|
||||
drbd_resume_io(mdev);
|
||||
|
||||
reply->ret_code = retcode;
|
||||
return 0;
|
||||
@@ -2170,11 +2196,13 @@ static int drbd_nl_start_ov(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
|
||||
|
||||
/* If there is still bitmap IO pending, e.g. previous resync or verify
|
||||
* just being finished, wait for it before requesting a new resync. */
|
||||
drbd_suspend_io(mdev);
|
||||
wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
|
||||
|
||||
/* w_make_ov_request expects position to be aligned */
|
||||
mdev->ov_start_sector = args.start_sector & ~BM_SECT_PER_BIT;
|
||||
reply->ret_code = drbd_request_state(mdev,NS(conn,C_VERIFY_S));
|
||||
drbd_resume_io(mdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ void seq_printf_with_thousands_grouping(struct seq_file *seq, long v)
|
||||
if (unlikely(v >= 1000000)) {
|
||||
/* cool: > GiByte/s */
|
||||
seq_printf(seq, "%ld,", v / 1000000);
|
||||
v /= 1000000;
|
||||
v %= 1000000;
|
||||
seq_printf(seq, "%03ld,%03ld", v/1000, v % 1000);
|
||||
} else if (likely(v >= 1000))
|
||||
seq_printf(seq, "%ld,%03ld", v/1000, v % 1000);
|
||||
|
||||
@@ -466,6 +466,7 @@ static int drbd_accept(struct drbd_conf *mdev, const char **what,
|
||||
goto out;
|
||||
}
|
||||
(*newsock)->ops = sock->ops;
|
||||
__module_get((*newsock)->ops->owner);
|
||||
|
||||
out:
|
||||
return err;
|
||||
@@ -664,7 +665,7 @@ static struct socket *drbd_wait_for_connect(struct drbd_conf *mdev)
|
||||
timeo = mdev->net_conf->try_connect_int * HZ;
|
||||
timeo += (random32() & 1) ? timeo / 7 : -timeo / 7; /* 28.5% random jitter */
|
||||
|
||||
s_listen->sk->sk_reuse = 1; /* SO_REUSEADDR */
|
||||
s_listen->sk->sk_reuse = SK_CAN_REUSE; /* SO_REUSEADDR */
|
||||
s_listen->sk->sk_rcvtimeo = timeo;
|
||||
s_listen->sk->sk_sndtimeo = timeo;
|
||||
drbd_setbufsize(s_listen, mdev->net_conf->sndbuf_size,
|
||||
@@ -750,6 +751,7 @@ static int drbd_connect(struct drbd_conf *mdev)
|
||||
{
|
||||
struct socket *s, *sock, *msock;
|
||||
int try, h, ok;
|
||||
enum drbd_state_rv rv;
|
||||
|
||||
D_ASSERT(!mdev->data.socket);
|
||||
|
||||
@@ -841,8 +843,8 @@ retry:
|
||||
}
|
||||
} while (1);
|
||||
|
||||
msock->sk->sk_reuse = 1; /* SO_REUSEADDR */
|
||||
sock->sk->sk_reuse = 1; /* SO_REUSEADDR */
|
||||
msock->sk->sk_reuse = SK_CAN_REUSE; /* SO_REUSEADDR */
|
||||
sock->sk->sk_reuse = SK_CAN_REUSE; /* SO_REUSEADDR */
|
||||
|
||||
sock->sk->sk_allocation = GFP_NOIO;
|
||||
msock->sk->sk_allocation = GFP_NOIO;
|
||||
@@ -888,25 +890,32 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
if (drbd_request_state(mdev, NS(conn, C_WF_REPORT_PARAMS)) < SS_SUCCESS)
|
||||
return 0;
|
||||
|
||||
sock->sk->sk_sndtimeo = mdev->net_conf->timeout*HZ/10;
|
||||
sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
|
||||
|
||||
atomic_set(&mdev->packet_seq, 0);
|
||||
mdev->peer_seq = 0;
|
||||
|
||||
drbd_thread_start(&mdev->asender);
|
||||
|
||||
if (drbd_send_protocol(mdev) == -1)
|
||||
return -1;
|
||||
set_bit(STATE_SENT, &mdev->flags);
|
||||
drbd_send_sync_param(mdev, &mdev->sync_conf);
|
||||
drbd_send_sizes(mdev, 0, 0);
|
||||
drbd_send_uuids(mdev);
|
||||
drbd_send_state(mdev);
|
||||
drbd_send_current_state(mdev);
|
||||
clear_bit(USE_DEGR_WFC_T, &mdev->flags);
|
||||
clear_bit(RESIZE_PENDING, &mdev->flags);
|
||||
|
||||
spin_lock_irq(&mdev->req_lock);
|
||||
rv = _drbd_set_state(_NS(mdev, conn, C_WF_REPORT_PARAMS), CS_VERBOSE, NULL);
|
||||
if (mdev->state.conn != C_WF_REPORT_PARAMS)
|
||||
clear_bit(STATE_SENT, &mdev->flags);
|
||||
spin_unlock_irq(&mdev->req_lock);
|
||||
|
||||
if (rv < SS_SUCCESS)
|
||||
return 0;
|
||||
|
||||
drbd_thread_start(&mdev->asender);
|
||||
mod_timer(&mdev->request_timer, jiffies + HZ); /* just start it here. */
|
||||
|
||||
return 1;
|
||||
@@ -957,7 +966,7 @@ static void drbd_flush(struct drbd_conf *mdev)
|
||||
rv = blkdev_issue_flush(mdev->ldev->backing_bdev, GFP_KERNEL,
|
||||
NULL);
|
||||
if (rv) {
|
||||
dev_err(DEV, "local disk flush failed with status %d\n", rv);
|
||||
dev_info(DEV, "local disk flush failed with status %d\n", rv);
|
||||
/* would rather check on EOPNOTSUPP, but that is not reliable.
|
||||
* don't try again for ANY return value != 0
|
||||
* if (rv == -EOPNOTSUPP) */
|
||||
@@ -1001,13 +1010,14 @@ static enum finish_epoch drbd_may_finish_epoch(struct drbd_conf *mdev,
|
||||
|
||||
if (epoch_size != 0 &&
|
||||
atomic_read(&epoch->active) == 0 &&
|
||||
test_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags)) {
|
||||
(test_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags) || ev & EV_CLEANUP)) {
|
||||
if (!(ev & EV_CLEANUP)) {
|
||||
spin_unlock(&mdev->epoch_lock);
|
||||
drbd_send_b_ack(mdev, epoch->barrier_nr, epoch_size);
|
||||
spin_lock(&mdev->epoch_lock);
|
||||
}
|
||||
dec_unacked(mdev);
|
||||
if (test_bit(DE_HAVE_BARRIER_NUMBER, &epoch->flags))
|
||||
dec_unacked(mdev);
|
||||
|
||||
if (mdev->current_epoch != epoch) {
|
||||
next_epoch = list_entry(epoch->list.next, struct drbd_epoch, list);
|
||||
@@ -1096,7 +1106,11 @@ int drbd_submit_ee(struct drbd_conf *mdev, struct drbd_epoch_entry *e,
|
||||
/* In most cases, we will only need one bio. But in case the lower
|
||||
* level restrictions happen to be different at this offset on this
|
||||
* side than those of the sending peer, we may need to submit the
|
||||
* request in more than one bio. */
|
||||
* request in more than one bio.
|
||||
*
|
||||
* Plain bio_alloc is good enough here, this is no DRBD internally
|
||||
* generated bio, but a bio allocated on behalf of the peer.
|
||||
*/
|
||||
next_bio:
|
||||
bio = bio_alloc(GFP_NOIO, nr_pages);
|
||||
if (!bio) {
|
||||
@@ -1583,6 +1597,24 @@ static int e_send_discard_ack(struct drbd_conf *mdev, struct drbd_work *w, int u
|
||||
return ok;
|
||||
}
|
||||
|
||||
static bool overlapping_resync_write(struct drbd_conf *mdev, struct drbd_epoch_entry *data_e)
|
||||
{
|
||||
|
||||
struct drbd_epoch_entry *rs_e;
|
||||
bool rv = 0;
|
||||
|
||||
spin_lock_irq(&mdev->req_lock);
|
||||
list_for_each_entry(rs_e, &mdev->sync_ee, w.list) {
|
||||
if (overlaps(data_e->sector, data_e->size, rs_e->sector, rs_e->size)) {
|
||||
rv = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&mdev->req_lock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Called from receive_Data.
|
||||
* Synchronize packets on sock with packets on msock.
|
||||
*
|
||||
@@ -1826,6 +1858,9 @@ static int receive_Data(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
|
||||
list_add(&e->w.list, &mdev->active_ee);
|
||||
spin_unlock_irq(&mdev->req_lock);
|
||||
|
||||
if (mdev->state.conn == C_SYNC_TARGET)
|
||||
wait_event(mdev->ee_wait, !overlapping_resync_write(mdev, e));
|
||||
|
||||
switch (mdev->net_conf->wire_protocol) {
|
||||
case DRBD_PROT_C:
|
||||
inc_unacked(mdev);
|
||||
@@ -2420,7 +2455,7 @@ static int drbd_uuid_compare(struct drbd_conf *mdev, int *rule_nr) __must_hold(l
|
||||
mdev->p_uuid[UI_BITMAP] = mdev->p_uuid[UI_HISTORY_START];
|
||||
mdev->p_uuid[UI_HISTORY_START] = mdev->p_uuid[UI_HISTORY_START + 1];
|
||||
|
||||
dev_info(DEV, "Did not got last syncUUID packet, corrected:\n");
|
||||
dev_info(DEV, "Lost last syncUUID packet, corrected:\n");
|
||||
drbd_uuid_dump(mdev, "peer", mdev->p_uuid, mdev->p_uuid[UI_SIZE], mdev->p_uuid[UI_FLAGS]);
|
||||
|
||||
return -1;
|
||||
@@ -2806,10 +2841,10 @@ static int receive_SyncParam(struct drbd_conf *mdev, enum drbd_packets cmd, unsi
|
||||
|
||||
if (apv >= 88) {
|
||||
if (apv == 88) {
|
||||
if (data_size > SHARED_SECRET_MAX) {
|
||||
dev_err(DEV, "verify-alg too long, "
|
||||
"peer wants %u, accepting only %u byte\n",
|
||||
data_size, SHARED_SECRET_MAX);
|
||||
if (data_size > SHARED_SECRET_MAX || data_size == 0) {
|
||||
dev_err(DEV, "verify-alg of wrong size, "
|
||||
"peer wants %u, accepting only up to %u byte\n",
|
||||
data_size, SHARED_SECRET_MAX);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3168,9 +3203,20 @@ static int receive_state(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
|
||||
os = ns = mdev->state;
|
||||
spin_unlock_irq(&mdev->req_lock);
|
||||
|
||||
/* peer says his disk is uptodate, while we think it is inconsistent,
|
||||
* and this happens while we think we have a sync going on. */
|
||||
if (os.pdsk == D_INCONSISTENT && real_peer_disk == D_UP_TO_DATE &&
|
||||
/* If some other part of the code (asender thread, timeout)
|
||||
* already decided to close the connection again,
|
||||
* we must not "re-establish" it here. */
|
||||
if (os.conn <= C_TEAR_DOWN)
|
||||
return false;
|
||||
|
||||
/* If this is the "end of sync" confirmation, usually the peer disk
|
||||
* transitions from D_INCONSISTENT to D_UP_TO_DATE. For empty (0 bits
|
||||
* set) resync started in PausedSyncT, or if the timing of pause-/
|
||||
* unpause-sync events has been "just right", the peer disk may
|
||||
* transition from D_CONSISTENT to D_UP_TO_DATE as well.
|
||||
*/
|
||||
if ((os.pdsk == D_INCONSISTENT || os.pdsk == D_CONSISTENT) &&
|
||||
real_peer_disk == D_UP_TO_DATE &&
|
||||
os.conn > C_CONNECTED && os.disk == D_UP_TO_DATE) {
|
||||
/* If we are (becoming) SyncSource, but peer is still in sync
|
||||
* preparation, ignore its uptodate-ness to avoid flapping, it
|
||||
@@ -3288,7 +3334,7 @@ static int receive_state(struct drbd_conf *mdev, enum drbd_packets cmd, unsigned
|
||||
/* Nowadays only used when forcing a node into primary role and
|
||||
setting its disk to UpToDate with that */
|
||||
drbd_send_uuids(mdev);
|
||||
drbd_send_state(mdev);
|
||||
drbd_send_current_state(mdev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3776,6 +3822,13 @@ static void drbd_disconnect(struct drbd_conf *mdev)
|
||||
if (mdev->state.conn == C_STANDALONE)
|
||||
return;
|
||||
|
||||
/* We are about to start the cleanup after connection loss.
|
||||
* Make sure drbd_make_request knows about that.
|
||||
* Usually we should be in some network failure state already,
|
||||
* but just in case we are not, we fix it up here.
|
||||
*/
|
||||
drbd_force_state(mdev, NS(conn, C_NETWORK_FAILURE));
|
||||
|
||||
/* asender does not clean up anything. it must not interfere, either */
|
||||
drbd_thread_stop(&mdev->asender);
|
||||
drbd_free_sock(mdev);
|
||||
@@ -3803,8 +3856,6 @@ static void drbd_disconnect(struct drbd_conf *mdev)
|
||||
atomic_set(&mdev->rs_pending_cnt, 0);
|
||||
wake_up(&mdev->misc_wait);
|
||||
|
||||
del_timer(&mdev->request_timer);
|
||||
|
||||
/* make sure syncer is stopped and w_resume_next_sg queued */
|
||||
del_timer_sync(&mdev->resync_timer);
|
||||
resync_timer_fn((unsigned long)mdev);
|
||||
@@ -4433,7 +4484,7 @@ static int got_BarrierAck(struct drbd_conf *mdev, struct p_header80 *h)
|
||||
|
||||
if (mdev->state.conn == C_AHEAD &&
|
||||
atomic_read(&mdev->ap_in_flight) == 0 &&
|
||||
!test_and_set_bit(AHEAD_TO_SYNC_SOURCE, &mdev->current_epoch->flags)) {
|
||||
!test_and_set_bit(AHEAD_TO_SYNC_SOURCE, &mdev->flags)) {
|
||||
mdev->start_resync_timer.expires = jiffies + HZ;
|
||||
add_timer(&mdev->start_resync_timer);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ static void _drbd_start_io_acct(struct drbd_conf *mdev, struct drbd_request *req
|
||||
const int rw = bio_data_dir(bio);
|
||||
int cpu;
|
||||
cpu = part_stat_lock();
|
||||
part_round_stats(cpu, &mdev->vdisk->part0);
|
||||
part_stat_inc(cpu, &mdev->vdisk->part0, ios[rw]);
|
||||
part_stat_add(cpu, &mdev->vdisk->part0, sectors[rw], bio_sectors(bio));
|
||||
part_inc_in_flight(&mdev->vdisk->part0, rw);
|
||||
@@ -214,8 +215,7 @@ void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m)
|
||||
{
|
||||
const unsigned long s = req->rq_state;
|
||||
struct drbd_conf *mdev = req->mdev;
|
||||
/* only WRITES may end up here without a master bio (on barrier ack) */
|
||||
int rw = req->master_bio ? bio_data_dir(req->master_bio) : WRITE;
|
||||
int rw = req->rq_state & RQ_WRITE ? WRITE : READ;
|
||||
|
||||
/* we must not complete the master bio, while it is
|
||||
* still being processed by _drbd_send_zc_bio (drbd_send_dblock)
|
||||
@@ -230,7 +230,7 @@ void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m)
|
||||
return;
|
||||
if (s & RQ_NET_PENDING)
|
||||
return;
|
||||
if (s & RQ_LOCAL_PENDING)
|
||||
if (s & RQ_LOCAL_PENDING && !(s & RQ_LOCAL_ABORTED))
|
||||
return;
|
||||
|
||||
if (req->master_bio) {
|
||||
@@ -277,6 +277,9 @@ void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m)
|
||||
req->master_bio = NULL;
|
||||
}
|
||||
|
||||
if (s & RQ_LOCAL_PENDING)
|
||||
return;
|
||||
|
||||
if ((s & RQ_NET_MASK) == 0 || (s & RQ_NET_DONE)) {
|
||||
/* this is disconnected (local only) operation,
|
||||
* or protocol C P_WRITE_ACK,
|
||||
@@ -429,7 +432,7 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
||||
break;
|
||||
|
||||
case completed_ok:
|
||||
if (bio_data_dir(req->master_bio) == WRITE)
|
||||
if (req->rq_state & RQ_WRITE)
|
||||
mdev->writ_cnt += req->size>>9;
|
||||
else
|
||||
mdev->read_cnt += req->size>>9;
|
||||
@@ -438,7 +441,14 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
||||
req->rq_state &= ~RQ_LOCAL_PENDING;
|
||||
|
||||
_req_may_be_done_not_susp(req, m);
|
||||
put_ldev(mdev);
|
||||
break;
|
||||
|
||||
case abort_disk_io:
|
||||
req->rq_state |= RQ_LOCAL_ABORTED;
|
||||
if (req->rq_state & RQ_WRITE)
|
||||
_req_may_be_done_not_susp(req, m);
|
||||
else
|
||||
goto goto_queue_for_net_read;
|
||||
break;
|
||||
|
||||
case write_completed_with_error:
|
||||
@@ -447,7 +457,6 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
||||
|
||||
__drbd_chk_io_error(mdev, false);
|
||||
_req_may_be_done_not_susp(req, m);
|
||||
put_ldev(mdev);
|
||||
break;
|
||||
|
||||
case read_ahead_completed_with_error:
|
||||
@@ -455,7 +464,6 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
||||
req->rq_state |= RQ_LOCAL_COMPLETED;
|
||||
req->rq_state &= ~RQ_LOCAL_PENDING;
|
||||
_req_may_be_done_not_susp(req, m);
|
||||
put_ldev(mdev);
|
||||
break;
|
||||
|
||||
case read_completed_with_error:
|
||||
@@ -467,7 +475,8 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
||||
D_ASSERT(!(req->rq_state & RQ_NET_MASK));
|
||||
|
||||
__drbd_chk_io_error(mdev, false);
|
||||
put_ldev(mdev);
|
||||
|
||||
goto_queue_for_net_read:
|
||||
|
||||
/* no point in retrying if there is no good remote data,
|
||||
* or we have no connection. */
|
||||
@@ -556,10 +565,8 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
||||
drbd_queue_work(&mdev->data.work, &req->w);
|
||||
break;
|
||||
|
||||
case oos_handed_to_network:
|
||||
/* actually the same */
|
||||
case read_retry_remote_canceled:
|
||||
case send_canceled:
|
||||
/* treat it the same */
|
||||
case send_failed:
|
||||
/* real cleanup will be done from tl_clear. just update flags
|
||||
* so it is no longer marked as on the worker queue */
|
||||
@@ -589,17 +596,17 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
||||
}
|
||||
req->rq_state &= ~RQ_NET_QUEUED;
|
||||
req->rq_state |= RQ_NET_SENT;
|
||||
/* because _drbd_send_zc_bio could sleep, and may want to
|
||||
* dereference the bio even after the "write_acked_by_peer" and
|
||||
* "completed_ok" events came in, once we return from
|
||||
* _drbd_send_zc_bio (drbd_send_dblock), we have to check
|
||||
* whether it is done already, and end it. */
|
||||
_req_may_be_done_not_susp(req, m);
|
||||
break;
|
||||
|
||||
case read_retry_remote_canceled:
|
||||
case oos_handed_to_network:
|
||||
/* Was not set PENDING, no longer QUEUED, so is now DONE
|
||||
* as far as this connection is concerned. */
|
||||
req->rq_state &= ~RQ_NET_QUEUED;
|
||||
/* fall through, in case we raced with drbd_disconnect */
|
||||
req->rq_state |= RQ_NET_DONE;
|
||||
_req_may_be_done_not_susp(req, m);
|
||||
break;
|
||||
|
||||
case connection_lost_while_pending:
|
||||
/* transfer log cleanup after connection loss */
|
||||
/* assert something? */
|
||||
@@ -616,8 +623,6 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
||||
_req_may_be_done(req, m); /* Allowed while state.susp */
|
||||
break;
|
||||
|
||||
case write_acked_by_peer_and_sis:
|
||||
req->rq_state |= RQ_NET_SIS;
|
||||
case conflict_discarded_by_peer:
|
||||
/* for discarded conflicting writes of multiple primaries,
|
||||
* there is no need to keep anything in the tl, potential
|
||||
@@ -628,18 +633,15 @@ int __req_mod(struct drbd_request *req, enum drbd_req_event what,
|
||||
(unsigned long long)req->sector, req->size);
|
||||
req->rq_state |= RQ_NET_DONE;
|
||||
/* fall through */
|
||||
case write_acked_by_peer_and_sis:
|
||||
case write_acked_by_peer:
|
||||
if (what == write_acked_by_peer_and_sis)
|
||||
req->rq_state |= RQ_NET_SIS;
|
||||
/* protocol C; successfully written on peer.
|
||||
* Nothing to do here.
|
||||
* Nothing more to do here.
|
||||
* We want to keep the tl in place for all protocols, to cater
|
||||
* for volatile write-back caches on lower level devices.
|
||||
*
|
||||
* A barrier request is expected to have forced all prior
|
||||
* requests onto stable storage, so completion of a barrier
|
||||
* request could set NET_DONE right here, and not wait for the
|
||||
* P_BARRIER_ACK, but that is an unnecessary optimization. */
|
||||
* for volatile write-back caches on lower level devices. */
|
||||
|
||||
/* this makes it effectively the same as for: */
|
||||
case recv_acked_by_peer:
|
||||
/* protocol B; pretends to be successfully written on peer.
|
||||
* see also notes above in handed_over_to_network about
|
||||
@@ -773,6 +775,7 @@ static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio, uns
|
||||
int local, remote, send_oos = 0;
|
||||
int err = -EIO;
|
||||
int ret = 0;
|
||||
union drbd_state s;
|
||||
|
||||
/* allocate outside of all locks; */
|
||||
req = drbd_req_new(mdev, bio);
|
||||
@@ -834,8 +837,9 @@ static int drbd_make_request_common(struct drbd_conf *mdev, struct bio *bio, uns
|
||||
drbd_al_begin_io(mdev, sector);
|
||||
}
|
||||
|
||||
remote = remote && drbd_should_do_remote(mdev->state);
|
||||
send_oos = rw == WRITE && drbd_should_send_oos(mdev->state);
|
||||
s = mdev->state;
|
||||
remote = remote && drbd_should_do_remote(s);
|
||||
send_oos = rw == WRITE && drbd_should_send_oos(s);
|
||||
D_ASSERT(!(remote && send_oos));
|
||||
|
||||
if (!(local || remote) && !is_susp(mdev->state)) {
|
||||
@@ -867,7 +871,7 @@ allocate_barrier:
|
||||
|
||||
if (is_susp(mdev->state)) {
|
||||
/* If we got suspended, use the retry mechanism of
|
||||
generic_make_request() to restart processing of this
|
||||
drbd_make_request() to restart processing of this
|
||||
bio. In the next call to drbd_make_request
|
||||
we sleep in inc_ap_bio() */
|
||||
ret = 1;
|
||||
@@ -1091,7 +1095,6 @@ void drbd_make_request(struct request_queue *q, struct bio *bio)
|
||||
*/
|
||||
D_ASSERT(bio->bi_size > 0);
|
||||
D_ASSERT((bio->bi_size & 0x1ff) == 0);
|
||||
D_ASSERT(bio->bi_idx == 0);
|
||||
|
||||
/* to make some things easier, force alignment of requests within the
|
||||
* granularity of our hash tables */
|
||||
@@ -1099,8 +1102,9 @@ void drbd_make_request(struct request_queue *q, struct bio *bio)
|
||||
e_enr = (bio->bi_sector+(bio->bi_size>>9)-1) >> HT_SHIFT;
|
||||
|
||||
if (likely(s_enr == e_enr)) {
|
||||
inc_ap_bio(mdev, 1);
|
||||
drbd_make_request_common(mdev, bio, start_time);
|
||||
do {
|
||||
inc_ap_bio(mdev, 1);
|
||||
} while (drbd_make_request_common(mdev, bio, start_time));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1196,36 +1200,66 @@ void request_timer_fn(unsigned long data)
|
||||
struct drbd_conf *mdev = (struct drbd_conf *) data;
|
||||
struct drbd_request *req; /* oldest request */
|
||||
struct list_head *le;
|
||||
unsigned long et = 0; /* effective timeout = ko_count * timeout */
|
||||
unsigned long ent = 0, dt = 0, et, nt; /* effective timeout = ko_count * timeout */
|
||||
unsigned long now;
|
||||
|
||||
if (get_net_conf(mdev)) {
|
||||
et = mdev->net_conf->timeout*HZ/10 * mdev->net_conf->ko_count;
|
||||
if (mdev->state.conn >= C_WF_REPORT_PARAMS)
|
||||
ent = mdev->net_conf->timeout*HZ/10
|
||||
* mdev->net_conf->ko_count;
|
||||
put_net_conf(mdev);
|
||||
}
|
||||
if (!et || mdev->state.conn < C_WF_REPORT_PARAMS)
|
||||
if (get_ldev(mdev)) { /* implicit state.disk >= D_INCONSISTENT */
|
||||
dt = mdev->ldev->dc.disk_timeout * HZ / 10;
|
||||
put_ldev(mdev);
|
||||
}
|
||||
et = min_not_zero(dt, ent);
|
||||
|
||||
if (!et)
|
||||
return; /* Recurring timer stopped */
|
||||
|
||||
now = jiffies;
|
||||
|
||||
spin_lock_irq(&mdev->req_lock);
|
||||
le = &mdev->oldest_tle->requests;
|
||||
if (list_empty(le)) {
|
||||
spin_unlock_irq(&mdev->req_lock);
|
||||
mod_timer(&mdev->request_timer, jiffies + et);
|
||||
mod_timer(&mdev->request_timer, now + et);
|
||||
return;
|
||||
}
|
||||
|
||||
le = le->prev;
|
||||
req = list_entry(le, struct drbd_request, tl_requests);
|
||||
if (time_is_before_eq_jiffies(req->start_time + et)) {
|
||||
if (req->rq_state & RQ_NET_PENDING) {
|
||||
dev_warn(DEV, "Remote failed to finish a request within ko-count * timeout\n");
|
||||
_drbd_set_state(_NS(mdev, conn, C_TIMEOUT), CS_VERBOSE, NULL);
|
||||
} else {
|
||||
dev_warn(DEV, "Local backing block device frozen?\n");
|
||||
mod_timer(&mdev->request_timer, jiffies + et);
|
||||
}
|
||||
} else {
|
||||
mod_timer(&mdev->request_timer, req->start_time + et);
|
||||
}
|
||||
|
||||
/* The request is considered timed out, if
|
||||
* - we have some effective timeout from the configuration,
|
||||
* with above state restrictions applied,
|
||||
* - the oldest request is waiting for a response from the network
|
||||
* resp. the local disk,
|
||||
* - the oldest request is in fact older than the effective timeout,
|
||||
* - the connection was established (resp. disk was attached)
|
||||
* for longer than the timeout already.
|
||||
* Note that for 32bit jiffies and very stable connections/disks,
|
||||
* we may have a wrap around, which is catched by
|
||||
* !time_in_range(now, last_..._jif, last_..._jif + timeout).
|
||||
*
|
||||
* Side effect: once per 32bit wrap-around interval, which means every
|
||||
* ~198 days with 250 HZ, we have a window where the timeout would need
|
||||
* to expire twice (worst case) to become effective. Good enough.
|
||||
*/
|
||||
if (ent && req->rq_state & RQ_NET_PENDING &&
|
||||
time_after(now, req->start_time + ent) &&
|
||||
!time_in_range(now, mdev->last_reconnect_jif, mdev->last_reconnect_jif + ent)) {
|
||||
dev_warn(DEV, "Remote failed to finish a request within ko-count * timeout\n");
|
||||
_drbd_set_state(_NS(mdev, conn, C_TIMEOUT), CS_VERBOSE | CS_HARD, NULL);
|
||||
}
|
||||
if (dt && req->rq_state & RQ_LOCAL_PENDING &&
|
||||
time_after(now, req->start_time + dt) &&
|
||||
!time_in_range(now, mdev->last_reattach_jif, mdev->last_reattach_jif + dt)) {
|
||||
dev_warn(DEV, "Local backing device failed to meet the disk-timeout\n");
|
||||
__drbd_chk_io_error(mdev, 1);
|
||||
}
|
||||
nt = (time_after(now, req->start_time + et) ? now : req->start_time) + et;
|
||||
spin_unlock_irq(&mdev->req_lock);
|
||||
mod_timer(&mdev->request_timer, nt);
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ enum drbd_req_event {
|
||||
read_completed_with_error,
|
||||
read_ahead_completed_with_error,
|
||||
write_completed_with_error,
|
||||
abort_disk_io,
|
||||
completed_ok,
|
||||
resend,
|
||||
fail_frozen_disk_io,
|
||||
@@ -118,18 +119,21 @@ enum drbd_req_event {
|
||||
* same time, so we should hold the request lock anyways.
|
||||
*/
|
||||
enum drbd_req_state_bits {
|
||||
/* 210
|
||||
* 000: no local possible
|
||||
* 001: to be submitted
|
||||
/* 3210
|
||||
* 0000: no local possible
|
||||
* 0001: to be submitted
|
||||
* UNUSED, we could map: 011: submitted, completion still pending
|
||||
* 110: completed ok
|
||||
* 010: completed with error
|
||||
* 0110: completed ok
|
||||
* 0010: completed with error
|
||||
* 1001: Aborted (before completion)
|
||||
* 1x10: Aborted and completed -> free
|
||||
*/
|
||||
__RQ_LOCAL_PENDING,
|
||||
__RQ_LOCAL_COMPLETED,
|
||||
__RQ_LOCAL_OK,
|
||||
__RQ_LOCAL_ABORTED,
|
||||
|
||||
/* 76543
|
||||
/* 87654
|
||||
* 00000: no network possible
|
||||
* 00001: to be send
|
||||
* 00011: to be send, on worker queue
|
||||
@@ -199,8 +203,9 @@ enum drbd_req_state_bits {
|
||||
#define RQ_LOCAL_PENDING (1UL << __RQ_LOCAL_PENDING)
|
||||
#define RQ_LOCAL_COMPLETED (1UL << __RQ_LOCAL_COMPLETED)
|
||||
#define RQ_LOCAL_OK (1UL << __RQ_LOCAL_OK)
|
||||
#define RQ_LOCAL_ABORTED (1UL << __RQ_LOCAL_ABORTED)
|
||||
|
||||
#define RQ_LOCAL_MASK ((RQ_LOCAL_OK << 1)-1) /* 0x07 */
|
||||
#define RQ_LOCAL_MASK ((RQ_LOCAL_ABORTED << 1)-1)
|
||||
|
||||
#define RQ_NET_PENDING (1UL << __RQ_NET_PENDING)
|
||||
#define RQ_NET_QUEUED (1UL << __RQ_NET_QUEUED)
|
||||
|
||||
@@ -70,11 +70,29 @@ rwlock_t global_state_lock;
|
||||
void drbd_md_io_complete(struct bio *bio, int error)
|
||||
{
|
||||
struct drbd_md_io *md_io;
|
||||
struct drbd_conf *mdev;
|
||||
|
||||
md_io = (struct drbd_md_io *)bio->bi_private;
|
||||
mdev = container_of(md_io, struct drbd_conf, md_io);
|
||||
|
||||
md_io->error = error;
|
||||
|
||||
complete(&md_io->event);
|
||||
/* We grabbed an extra reference in _drbd_md_sync_page_io() to be able
|
||||
* to timeout on the lower level device, and eventually detach from it.
|
||||
* If this io completion runs after that timeout expired, this
|
||||
* drbd_md_put_buffer() may allow us to finally try and re-attach.
|
||||
* During normal operation, this only puts that extra reference
|
||||
* down to 1 again.
|
||||
* Make sure we first drop the reference, and only then signal
|
||||
* completion, or we may (in drbd_al_read_log()) cycle so fast into the
|
||||
* next drbd_md_sync_page_io(), that we trigger the
|
||||
* ASSERT(atomic_read(&mdev->md_io_in_use) == 1) there.
|
||||
*/
|
||||
drbd_md_put_buffer(mdev);
|
||||
md_io->done = 1;
|
||||
wake_up(&mdev->misc_wait);
|
||||
bio_put(bio);
|
||||
put_ldev(mdev);
|
||||
}
|
||||
|
||||
/* reads on behalf of the partner,
|
||||
@@ -226,6 +244,7 @@ void drbd_endio_pri(struct bio *bio, int error)
|
||||
spin_lock_irqsave(&mdev->req_lock, flags);
|
||||
__req_mod(req, what, &m);
|
||||
spin_unlock_irqrestore(&mdev->req_lock, flags);
|
||||
put_ldev(mdev);
|
||||
|
||||
if (m.bio)
|
||||
complete_master_bio(mdev, &m);
|
||||
@@ -290,7 +309,7 @@ void drbd_csum_bio(struct drbd_conf *mdev, struct crypto_hash *tfm, struct bio *
|
||||
sg_init_table(&sg, 1);
|
||||
crypto_hash_init(&desc);
|
||||
|
||||
__bio_for_each_segment(bvec, bio, i, 0) {
|
||||
bio_for_each_segment(bvec, bio, i) {
|
||||
sg_set_page(&sg, bvec->bv_page, bvec->bv_len, bvec->bv_offset);
|
||||
crypto_hash_update(&desc, &sg, sg.length);
|
||||
}
|
||||
@@ -728,7 +747,7 @@ int w_start_resync(struct drbd_conf *mdev, struct drbd_work *w, int cancel)
|
||||
}
|
||||
|
||||
drbd_start_resync(mdev, C_SYNC_SOURCE);
|
||||
clear_bit(AHEAD_TO_SYNC_SOURCE, &mdev->current_epoch->flags);
|
||||
clear_bit(AHEAD_TO_SYNC_SOURCE, &mdev->flags);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1519,14 +1538,14 @@ void drbd_start_resync(struct drbd_conf *mdev, enum drbd_conns side)
|
||||
}
|
||||
|
||||
drbd_state_lock(mdev);
|
||||
|
||||
write_lock_irq(&global_state_lock);
|
||||
if (!get_ldev_if_state(mdev, D_NEGOTIATING)) {
|
||||
write_unlock_irq(&global_state_lock);
|
||||
drbd_state_unlock(mdev);
|
||||
return;
|
||||
}
|
||||
|
||||
write_lock_irq(&global_state_lock);
|
||||
ns = mdev->state;
|
||||
ns.i = mdev->state.i;
|
||||
|
||||
ns.aftr_isp = !_drbd_may_sync_now(mdev);
|
||||
|
||||
|
||||
+77
-84
@@ -551,7 +551,7 @@ static void floppy_ready(void);
|
||||
static void floppy_start(void);
|
||||
static void process_fd_request(void);
|
||||
static void recalibrate_floppy(void);
|
||||
static void floppy_shutdown(unsigned long);
|
||||
static void floppy_shutdown(struct work_struct *);
|
||||
|
||||
static int floppy_request_regions(int);
|
||||
static void floppy_release_regions(int);
|
||||
@@ -588,6 +588,8 @@ static int buffer_max = -1;
|
||||
static struct floppy_fdc_state fdc_state[N_FDC];
|
||||
static int fdc; /* current fdc */
|
||||
|
||||
static struct workqueue_struct *floppy_wq;
|
||||
|
||||
static struct floppy_struct *_floppy = floppy_type;
|
||||
static unsigned char current_drive;
|
||||
static long current_count_sectors;
|
||||
@@ -629,16 +631,15 @@ static inline void set_debugt(void) { }
|
||||
static inline void debugt(const char *func, const char *msg) { }
|
||||
#endif /* DEBUGT */
|
||||
|
||||
typedef void (*timeout_fn)(unsigned long);
|
||||
static DEFINE_TIMER(fd_timeout, floppy_shutdown, 0, 0);
|
||||
|
||||
static DECLARE_DELAYED_WORK(fd_timeout, floppy_shutdown);
|
||||
static const char *timeout_message;
|
||||
|
||||
static void is_alive(const char *func, const char *message)
|
||||
{
|
||||
/* this routine checks whether the floppy driver is "alive" */
|
||||
if (test_bit(0, &fdc_busy) && command_status < 2 &&
|
||||
!timer_pending(&fd_timeout)) {
|
||||
!delayed_work_pending(&fd_timeout)) {
|
||||
DPRINT("%s: timeout handler died. %s\n", func, message);
|
||||
}
|
||||
}
|
||||
@@ -666,15 +667,18 @@ static int output_log_pos;
|
||||
|
||||
static void __reschedule_timeout(int drive, const char *message)
|
||||
{
|
||||
unsigned long delay;
|
||||
|
||||
if (drive == current_reqD)
|
||||
drive = current_drive;
|
||||
del_timer(&fd_timeout);
|
||||
|
||||
if (drive < 0 || drive >= N_DRIVE) {
|
||||
fd_timeout.expires = jiffies + 20UL * HZ;
|
||||
delay = 20UL * HZ;
|
||||
drive = 0;
|
||||
} else
|
||||
fd_timeout.expires = jiffies + UDP->timeout;
|
||||
add_timer(&fd_timeout);
|
||||
delay = UDP->timeout;
|
||||
|
||||
queue_delayed_work(floppy_wq, &fd_timeout, delay);
|
||||
if (UDP->flags & FD_DEBUG)
|
||||
DPRINT("reschedule timeout %s\n", message);
|
||||
timeout_message = message;
|
||||
@@ -872,7 +876,7 @@ static int lock_fdc(int drive, bool interruptible)
|
||||
|
||||
command_status = FD_COMMAND_NONE;
|
||||
|
||||
__reschedule_timeout(drive, "lock fdc");
|
||||
reschedule_timeout(drive, "lock fdc");
|
||||
set_fdc(drive);
|
||||
return 0;
|
||||
}
|
||||
@@ -880,23 +884,15 @@ static int lock_fdc(int drive, bool interruptible)
|
||||
/* unlocks the driver */
|
||||
static void unlock_fdc(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
raw_cmd = NULL;
|
||||
if (!test_bit(0, &fdc_busy))
|
||||
DPRINT("FDC access conflict!\n");
|
||||
|
||||
if (do_floppy)
|
||||
DPRINT("device interrupt still active at FDC release: %pf!\n",
|
||||
do_floppy);
|
||||
raw_cmd = NULL;
|
||||
command_status = FD_COMMAND_NONE;
|
||||
spin_lock_irqsave(&floppy_lock, flags);
|
||||
del_timer(&fd_timeout);
|
||||
__cancel_delayed_work(&fd_timeout);
|
||||
do_floppy = NULL;
|
||||
cont = NULL;
|
||||
clear_bit(0, &fdc_busy);
|
||||
if (current_req || set_next_request())
|
||||
do_fd_request(current_req->q);
|
||||
spin_unlock_irqrestore(&floppy_lock, flags);
|
||||
wake_up(&fdc_wait);
|
||||
}
|
||||
|
||||
@@ -968,26 +964,24 @@ static DECLARE_WORK(floppy_work, NULL);
|
||||
|
||||
static void schedule_bh(void (*handler)(void))
|
||||
{
|
||||
WARN_ON(work_pending(&floppy_work));
|
||||
|
||||
PREPARE_WORK(&floppy_work, (work_func_t)handler);
|
||||
schedule_work(&floppy_work);
|
||||
queue_work(floppy_wq, &floppy_work);
|
||||
}
|
||||
|
||||
static DEFINE_TIMER(fd_timer, NULL, 0, 0);
|
||||
static DECLARE_DELAYED_WORK(fd_timer, NULL);
|
||||
|
||||
static void cancel_activity(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&floppy_lock, flags);
|
||||
do_floppy = NULL;
|
||||
PREPARE_WORK(&floppy_work, (work_func_t)empty);
|
||||
del_timer(&fd_timer);
|
||||
spin_unlock_irqrestore(&floppy_lock, flags);
|
||||
cancel_delayed_work_sync(&fd_timer);
|
||||
cancel_work_sync(&floppy_work);
|
||||
}
|
||||
|
||||
/* this function makes sure that the disk stays in the drive during the
|
||||
* transfer */
|
||||
static void fd_watchdog(void)
|
||||
static void fd_watchdog(struct work_struct *arg)
|
||||
{
|
||||
debug_dcl(DP->flags, "calling disk change from watchdog\n");
|
||||
|
||||
@@ -997,21 +991,20 @@ static void fd_watchdog(void)
|
||||
cont->done(0);
|
||||
reset_fdc();
|
||||
} else {
|
||||
del_timer(&fd_timer);
|
||||
fd_timer.function = (timeout_fn)fd_watchdog;
|
||||
fd_timer.expires = jiffies + HZ / 10;
|
||||
add_timer(&fd_timer);
|
||||
cancel_delayed_work(&fd_timer);
|
||||
PREPARE_DELAYED_WORK(&fd_timer, fd_watchdog);
|
||||
queue_delayed_work(floppy_wq, &fd_timer, HZ / 10);
|
||||
}
|
||||
}
|
||||
|
||||
static void main_command_interrupt(void)
|
||||
{
|
||||
del_timer(&fd_timer);
|
||||
cancel_delayed_work(&fd_timer);
|
||||
cont->interrupt();
|
||||
}
|
||||
|
||||
/* waits for a delay (spinup or select) to pass */
|
||||
static int fd_wait_for_completion(unsigned long delay, timeout_fn function)
|
||||
static int fd_wait_for_completion(unsigned long expires, work_func_t function)
|
||||
{
|
||||
if (FDCS->reset) {
|
||||
reset_fdc(); /* do the reset during sleep to win time
|
||||
@@ -1020,11 +1013,10 @@ static int fd_wait_for_completion(unsigned long delay, timeout_fn function)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (time_before(jiffies, delay)) {
|
||||
del_timer(&fd_timer);
|
||||
fd_timer.function = function;
|
||||
fd_timer.expires = delay;
|
||||
add_timer(&fd_timer);
|
||||
if (time_before(jiffies, expires)) {
|
||||
cancel_delayed_work(&fd_timer);
|
||||
PREPARE_DELAYED_WORK(&fd_timer, function);
|
||||
queue_delayed_work(floppy_wq, &fd_timer, expires - jiffies);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@@ -1342,7 +1334,7 @@ static int fdc_dtr(void)
|
||||
*/
|
||||
FDCS->dtr = raw_cmd->rate & 3;
|
||||
return fd_wait_for_completion(jiffies + 2UL * HZ / 100,
|
||||
(timeout_fn)floppy_ready);
|
||||
(work_func_t)floppy_ready);
|
||||
} /* fdc_dtr */
|
||||
|
||||
static void tell_sector(void)
|
||||
@@ -1447,7 +1439,7 @@ static void setup_rw_floppy(void)
|
||||
int flags;
|
||||
int dflags;
|
||||
unsigned long ready_date;
|
||||
timeout_fn function;
|
||||
work_func_t function;
|
||||
|
||||
flags = raw_cmd->flags;
|
||||
if (flags & (FD_RAW_READ | FD_RAW_WRITE))
|
||||
@@ -1461,9 +1453,9 @@ static void setup_rw_floppy(void)
|
||||
*/
|
||||
if (time_after(ready_date, jiffies + DP->select_delay)) {
|
||||
ready_date -= DP->select_delay;
|
||||
function = (timeout_fn)floppy_start;
|
||||
function = (work_func_t)floppy_start;
|
||||
} else
|
||||
function = (timeout_fn)setup_rw_floppy;
|
||||
function = (work_func_t)setup_rw_floppy;
|
||||
|
||||
/* wait until the floppy is spinning fast enough */
|
||||
if (fd_wait_for_completion(ready_date, function))
|
||||
@@ -1493,7 +1485,7 @@ static void setup_rw_floppy(void)
|
||||
inr = result();
|
||||
cont->interrupt();
|
||||
} else if (flags & FD_RAW_NEED_DISK)
|
||||
fd_watchdog();
|
||||
fd_watchdog(NULL);
|
||||
}
|
||||
|
||||
static int blind_seek;
|
||||
@@ -1802,20 +1794,22 @@ static void show_floppy(void)
|
||||
pr_info("do_floppy=%pf\n", do_floppy);
|
||||
if (work_pending(&floppy_work))
|
||||
pr_info("floppy_work.func=%pf\n", floppy_work.func);
|
||||
if (timer_pending(&fd_timer))
|
||||
pr_info("fd_timer.function=%pf\n", fd_timer.function);
|
||||
if (timer_pending(&fd_timeout)) {
|
||||
pr_info("timer_function=%pf\n", fd_timeout.function);
|
||||
pr_info("expires=%lu\n", fd_timeout.expires - jiffies);
|
||||
pr_info("now=%lu\n", jiffies);
|
||||
}
|
||||
if (delayed_work_pending(&fd_timer))
|
||||
pr_info("delayed work.function=%p expires=%ld\n",
|
||||
fd_timer.work.func,
|
||||
fd_timer.timer.expires - jiffies);
|
||||
if (delayed_work_pending(&fd_timeout))
|
||||
pr_info("timer_function=%p expires=%ld\n",
|
||||
fd_timeout.work.func,
|
||||
fd_timeout.timer.expires - jiffies);
|
||||
|
||||
pr_info("cont=%p\n", cont);
|
||||
pr_info("current_req=%p\n", current_req);
|
||||
pr_info("command_status=%d\n", command_status);
|
||||
pr_info("\n");
|
||||
}
|
||||
|
||||
static void floppy_shutdown(unsigned long data)
|
||||
static void floppy_shutdown(struct work_struct *arg)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
@@ -1868,7 +1862,7 @@ static int start_motor(void (*function)(void))
|
||||
|
||||
/* wait_for_completion also schedules reset if needed. */
|
||||
return fd_wait_for_completion(DRS->select_date + DP->select_delay,
|
||||
(timeout_fn)function);
|
||||
(work_func_t)function);
|
||||
}
|
||||
|
||||
static void floppy_ready(void)
|
||||
@@ -2821,7 +2815,6 @@ do_request:
|
||||
spin_lock_irq(&floppy_lock);
|
||||
pending = set_next_request();
|
||||
spin_unlock_irq(&floppy_lock);
|
||||
|
||||
if (!pending) {
|
||||
do_floppy = NULL;
|
||||
unlock_fdc();
|
||||
@@ -2898,13 +2891,15 @@ static void do_fd_request(struct request_queue *q)
|
||||
current_req->cmd_flags))
|
||||
return;
|
||||
|
||||
if (test_bit(0, &fdc_busy)) {
|
||||
if (test_and_set_bit(0, &fdc_busy)) {
|
||||
/* fdc busy, this new request will be treated when the
|
||||
current one is done */
|
||||
is_alive(__func__, "old request running");
|
||||
return;
|
||||
}
|
||||
lock_fdc(MAXTIMEOUT, false);
|
||||
command_status = FD_COMMAND_NONE;
|
||||
__reschedule_timeout(MAXTIMEOUT, "fd_request");
|
||||
set_fdc(0);
|
||||
process_fd_request();
|
||||
is_alive(__func__, "");
|
||||
}
|
||||
@@ -3612,9 +3607,7 @@ static int floppy_release(struct gendisk *disk, fmode_t mode)
|
||||
|
||||
mutex_lock(&floppy_mutex);
|
||||
mutex_lock(&open_lock);
|
||||
if (UDRS->fd_ref < 0)
|
||||
UDRS->fd_ref = 0;
|
||||
else if (!UDRS->fd_ref--) {
|
||||
if (!UDRS->fd_ref--) {
|
||||
DPRINT("floppy_release with fd_ref == 0");
|
||||
UDRS->fd_ref = 0;
|
||||
}
|
||||
@@ -3650,13 +3643,7 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
|
||||
set_bit(FD_VERIFY_BIT, &UDRS->flags);
|
||||
}
|
||||
|
||||
if (UDRS->fd_ref == -1 || (UDRS->fd_ref && (mode & FMODE_EXCL)))
|
||||
goto out2;
|
||||
|
||||
if (mode & FMODE_EXCL)
|
||||
UDRS->fd_ref = -1;
|
||||
else
|
||||
UDRS->fd_ref++;
|
||||
UDRS->fd_ref++;
|
||||
|
||||
opened_bdev[drive] = bdev;
|
||||
|
||||
@@ -3719,10 +3706,8 @@ static int floppy_open(struct block_device *bdev, fmode_t mode)
|
||||
mutex_unlock(&floppy_mutex);
|
||||
return 0;
|
||||
out:
|
||||
if (UDRS->fd_ref < 0)
|
||||
UDRS->fd_ref = 0;
|
||||
else
|
||||
UDRS->fd_ref--;
|
||||
UDRS->fd_ref--;
|
||||
|
||||
if (!UDRS->fd_ref)
|
||||
opened_bdev[drive] = NULL;
|
||||
out2:
|
||||
@@ -4159,10 +4144,16 @@ static int __init floppy_init(void)
|
||||
goto out_put_disk;
|
||||
}
|
||||
|
||||
floppy_wq = alloc_ordered_workqueue("floppy", 0);
|
||||
if (!floppy_wq) {
|
||||
err = -ENOMEM;
|
||||
goto out_put_disk;
|
||||
}
|
||||
|
||||
disks[dr]->queue = blk_init_queue(do_fd_request, &floppy_lock);
|
||||
if (!disks[dr]->queue) {
|
||||
err = -ENOMEM;
|
||||
goto out_put_disk;
|
||||
goto out_destroy_workq;
|
||||
}
|
||||
|
||||
blk_queue_max_hw_sectors(disks[dr]->queue, 64);
|
||||
@@ -4213,7 +4204,7 @@ static int __init floppy_init(void)
|
||||
use_virtual_dma = can_use_virtual_dma & 1;
|
||||
fdc_state[0].address = FDC1;
|
||||
if (fdc_state[0].address == -1) {
|
||||
del_timer_sync(&fd_timeout);
|
||||
cancel_delayed_work(&fd_timeout);
|
||||
err = -ENODEV;
|
||||
goto out_unreg_region;
|
||||
}
|
||||
@@ -4224,7 +4215,7 @@ static int __init floppy_init(void)
|
||||
fdc = 0; /* reset fdc in case of unexpected interrupt */
|
||||
err = floppy_grab_irq_and_dma();
|
||||
if (err) {
|
||||
del_timer_sync(&fd_timeout);
|
||||
cancel_delayed_work(&fd_timeout);
|
||||
err = -EBUSY;
|
||||
goto out_unreg_region;
|
||||
}
|
||||
@@ -4281,13 +4272,13 @@ static int __init floppy_init(void)
|
||||
user_reset_fdc(-1, FD_RESET_ALWAYS, false);
|
||||
}
|
||||
fdc = 0;
|
||||
del_timer_sync(&fd_timeout);
|
||||
cancel_delayed_work(&fd_timeout);
|
||||
current_drive = 0;
|
||||
initialized = true;
|
||||
if (have_no_fdc) {
|
||||
DPRINT("no floppy controllers found\n");
|
||||
err = have_no_fdc;
|
||||
goto out_flush_work;
|
||||
goto out_release_dma;
|
||||
}
|
||||
|
||||
for (drive = 0; drive < N_DRIVE; drive++) {
|
||||
@@ -4302,7 +4293,7 @@ static int __init floppy_init(void)
|
||||
|
||||
err = platform_device_register(&floppy_device[drive]);
|
||||
if (err)
|
||||
goto out_flush_work;
|
||||
goto out_release_dma;
|
||||
|
||||
err = device_create_file(&floppy_device[drive].dev,
|
||||
&dev_attr_cmos);
|
||||
@@ -4320,13 +4311,14 @@ static int __init floppy_init(void)
|
||||
|
||||
out_unreg_platform_dev:
|
||||
platform_device_unregister(&floppy_device[drive]);
|
||||
out_flush_work:
|
||||
flush_work_sync(&floppy_work);
|
||||
out_release_dma:
|
||||
if (atomic_read(&usage_count))
|
||||
floppy_release_irq_and_dma();
|
||||
out_unreg_region:
|
||||
blk_unregister_region(MKDEV(FLOPPY_MAJOR, 0), 256);
|
||||
platform_driver_unregister(&floppy_driver);
|
||||
out_destroy_workq:
|
||||
destroy_workqueue(floppy_wq);
|
||||
out_unreg_blkdev:
|
||||
unregister_blkdev(FLOPPY_MAJOR, "fd");
|
||||
out_put_disk:
|
||||
@@ -4397,7 +4389,7 @@ static int floppy_grab_irq_and_dma(void)
|
||||
* We might have scheduled a free_irq(), wait it to
|
||||
* drain first:
|
||||
*/
|
||||
flush_work_sync(&floppy_work);
|
||||
flush_workqueue(floppy_wq);
|
||||
|
||||
if (fd_request_irq()) {
|
||||
DPRINT("Unable to grab IRQ%d for the floppy driver\n",
|
||||
@@ -4488,9 +4480,9 @@ static void floppy_release_irq_and_dma(void)
|
||||
pr_info("motor off timer %d still active\n", drive);
|
||||
#endif
|
||||
|
||||
if (timer_pending(&fd_timeout))
|
||||
if (delayed_work_pending(&fd_timeout))
|
||||
pr_info("floppy timer still active:%s\n", timeout_message);
|
||||
if (timer_pending(&fd_timer))
|
||||
if (delayed_work_pending(&fd_timer))
|
||||
pr_info("auxiliary floppy timer still active\n");
|
||||
if (work_pending(&floppy_work))
|
||||
pr_info("work still pending\n");
|
||||
@@ -4560,8 +4552,9 @@ static void __exit floppy_module_exit(void)
|
||||
put_disk(disks[drive]);
|
||||
}
|
||||
|
||||
del_timer_sync(&fd_timeout);
|
||||
del_timer_sync(&fd_timer);
|
||||
cancel_delayed_work_sync(&fd_timeout);
|
||||
cancel_delayed_work_sync(&fd_timer);
|
||||
destroy_workqueue(floppy_wq);
|
||||
|
||||
if (atomic_read(&usage_count))
|
||||
floppy_release_irq_and_dma();
|
||||
|
||||
+175
-107
@@ -294,18 +294,16 @@ static int hba_reset_nosleep(struct driver_data *dd)
|
||||
*/
|
||||
static inline void mtip_issue_ncq_command(struct mtip_port *port, int tag)
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
|
||||
atomic_set(&port->commands[tag].active, 1);
|
||||
|
||||
spin_lock_irqsave(&port->cmd_issue_lock, flags);
|
||||
spin_lock(&port->cmd_issue_lock);
|
||||
|
||||
writel((1 << MTIP_TAG_BIT(tag)),
|
||||
port->s_active[MTIP_TAG_INDEX(tag)]);
|
||||
writel((1 << MTIP_TAG_BIT(tag)),
|
||||
port->cmd_issue[MTIP_TAG_INDEX(tag)]);
|
||||
|
||||
spin_unlock_irqrestore(&port->cmd_issue_lock, flags);
|
||||
spin_unlock(&port->cmd_issue_lock);
|
||||
|
||||
/* Set the command's timeout value.*/
|
||||
port->commands[tag].comp_time = jiffies + msecs_to_jiffies(
|
||||
@@ -436,8 +434,7 @@ static void mtip_init_port(struct mtip_port *port)
|
||||
writel(0xFFFFFFFF, port->completed[i]);
|
||||
|
||||
/* Clear any pending interrupts for this port */
|
||||
writel(readl(port->dd->mmio + PORT_IRQ_STAT),
|
||||
port->dd->mmio + PORT_IRQ_STAT);
|
||||
writel(readl(port->mmio + PORT_IRQ_STAT), port->mmio + PORT_IRQ_STAT);
|
||||
|
||||
/* Clear any pending interrupts on the HBA. */
|
||||
writel(readl(port->dd->mmio + HOST_IRQ_STAT),
|
||||
@@ -782,13 +779,24 @@ static void mtip_handle_tfe(struct driver_data *dd)
|
||||
|
||||
/* Stop the timer to prevent command timeouts. */
|
||||
del_timer(&port->cmd_timer);
|
||||
set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
|
||||
|
||||
if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags) &&
|
||||
test_bit(MTIP_TAG_INTERNAL, port->allocated)) {
|
||||
cmd = &port->commands[MTIP_TAG_INTERNAL];
|
||||
dbg_printk(MTIP_DRV_NAME " TFE for the internal command\n");
|
||||
|
||||
atomic_inc(&cmd->active); /* active > 1 indicates error */
|
||||
if (cmd->comp_data && cmd->comp_func) {
|
||||
cmd->comp_func(port, MTIP_TAG_INTERNAL,
|
||||
cmd->comp_data, PORT_IRQ_TF_ERR);
|
||||
}
|
||||
goto handle_tfe_exit;
|
||||
}
|
||||
|
||||
/* clear the tag accumulator */
|
||||
memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long));
|
||||
|
||||
/* Set eh_active */
|
||||
set_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
|
||||
|
||||
/* Loop through all the groups */
|
||||
for (group = 0; group < dd->slot_groups; group++) {
|
||||
completed = readl(port->completed[group]);
|
||||
@@ -940,6 +948,7 @@ static void mtip_handle_tfe(struct driver_data *dd)
|
||||
}
|
||||
print_tags(dd, "reissued (TFE)", tagaccum, cmd_cnt);
|
||||
|
||||
handle_tfe_exit:
|
||||
/* clear eh_active */
|
||||
clear_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags);
|
||||
wake_up_interruptible(&port->svc_wait);
|
||||
@@ -961,6 +970,8 @@ static inline void mtip_process_sdbf(struct driver_data *dd)
|
||||
/* walk all bits in all slot groups */
|
||||
for (group = 0; group < dd->slot_groups; group++) {
|
||||
completed = readl(port->completed[group]);
|
||||
if (!completed)
|
||||
continue;
|
||||
|
||||
/* clear completed status register in the hardware.*/
|
||||
writel(completed, port->completed[group]);
|
||||
@@ -1329,22 +1340,6 @@ static int mtip_exec_internal_command(struct mtip_port *port,
|
||||
}
|
||||
rv = -EAGAIN;
|
||||
}
|
||||
|
||||
if (readl(port->cmd_issue[MTIP_TAG_INTERNAL])
|
||||
& (1 << MTIP_TAG_INTERNAL)) {
|
||||
dev_warn(&port->dd->pdev->dev,
|
||||
"Retiring internal command but CI is 1.\n");
|
||||
if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
|
||||
&port->dd->dd_flag)) {
|
||||
hba_reset_nosleep(port->dd);
|
||||
rv = -ENXIO;
|
||||
} else {
|
||||
mtip_restart_port(port);
|
||||
rv = -EAGAIN;
|
||||
}
|
||||
goto exec_ic_exit;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Spin for <timeout> checking if command still outstanding */
|
||||
timeout = jiffies + msecs_to_jiffies(timeout);
|
||||
@@ -1361,23 +1356,27 @@ static int mtip_exec_internal_command(struct mtip_port *port,
|
||||
rv = -ENXIO;
|
||||
goto exec_ic_exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (readl(port->cmd_issue[MTIP_TAG_INTERNAL])
|
||||
& (1 << MTIP_TAG_INTERNAL)) {
|
||||
dev_err(&port->dd->pdev->dev,
|
||||
"Internal command did not complete [atomic]\n");
|
||||
rv = -EAGAIN;
|
||||
if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
|
||||
&port->dd->dd_flag)) {
|
||||
hba_reset_nosleep(port->dd);
|
||||
rv = -ENXIO;
|
||||
} else {
|
||||
mtip_restart_port(port);
|
||||
rv = -EAGAIN;
|
||||
if (readl(port->mmio + PORT_IRQ_STAT) & PORT_IRQ_ERR) {
|
||||
atomic_inc(&int_cmd->active); /* error */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (atomic_read(&int_cmd->active) > 1) {
|
||||
dev_err(&port->dd->pdev->dev,
|
||||
"Internal command [%02X] failed\n", fis->command);
|
||||
rv = -EIO;
|
||||
}
|
||||
if (readl(port->cmd_issue[MTIP_TAG_INTERNAL])
|
||||
& (1 << MTIP_TAG_INTERNAL)) {
|
||||
rv = -ENXIO;
|
||||
if (!test_bit(MTIP_DDF_REMOVE_PENDING_BIT,
|
||||
&port->dd->dd_flag)) {
|
||||
mtip_restart_port(port);
|
||||
rv = -EAGAIN;
|
||||
}
|
||||
}
|
||||
exec_ic_exit:
|
||||
/* Clear the allocated and active bits for the internal command. */
|
||||
atomic_set(&int_cmd->active, 0);
|
||||
@@ -1893,13 +1892,33 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
|
||||
void __user *user_buffer)
|
||||
{
|
||||
struct host_to_dev_fis fis;
|
||||
struct host_to_dev_fis *reply = (port->rxfis + RX_FIS_D2H_REG);
|
||||
struct host_to_dev_fis *reply;
|
||||
u8 *buf = NULL;
|
||||
dma_addr_t dma_addr = 0;
|
||||
int rv = 0, xfer_sz = command[3];
|
||||
|
||||
if (xfer_sz) {
|
||||
if (user_buffer)
|
||||
return -EFAULT;
|
||||
|
||||
buf = dmam_alloc_coherent(&port->dd->pdev->dev,
|
||||
ATA_SECT_SIZE * xfer_sz,
|
||||
&dma_addr,
|
||||
GFP_KERNEL);
|
||||
if (!buf) {
|
||||
dev_err(&port->dd->pdev->dev,
|
||||
"Memory allocation failed (%d bytes)\n",
|
||||
ATA_SECT_SIZE * xfer_sz);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(buf, 0, ATA_SECT_SIZE * xfer_sz);
|
||||
}
|
||||
|
||||
/* Build the FIS. */
|
||||
memset(&fis, 0, sizeof(struct host_to_dev_fis));
|
||||
fis.type = 0x27;
|
||||
fis.opts = 1 << 7;
|
||||
fis.command = command[0];
|
||||
fis.type = 0x27;
|
||||
fis.opts = 1 << 7;
|
||||
fis.command = command[0];
|
||||
fis.features = command[2];
|
||||
fis.sect_count = command[3];
|
||||
if (fis.command == ATA_CMD_SMART) {
|
||||
@@ -1908,6 +1927,11 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
|
||||
fis.cyl_hi = 0xC2;
|
||||
}
|
||||
|
||||
if (xfer_sz)
|
||||
reply = (port->rxfis + RX_FIS_PIO_SETUP);
|
||||
else
|
||||
reply = (port->rxfis + RX_FIS_D2H_REG);
|
||||
|
||||
dbg_printk(MTIP_DRV_NAME
|
||||
" %s: User Command: cmd %x, sect %x, "
|
||||
"feat %x, sectcnt %x\n",
|
||||
@@ -1917,43 +1941,46 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
|
||||
command[2],
|
||||
command[3]);
|
||||
|
||||
memset(port->sector_buffer, 0x00, ATA_SECT_SIZE);
|
||||
|
||||
/* Execute the command. */
|
||||
if (mtip_exec_internal_command(port,
|
||||
&fis,
|
||||
5,
|
||||
port->sector_buffer_dma,
|
||||
(command[3] != 0) ? ATA_SECT_SIZE : 0,
|
||||
(xfer_sz ? dma_addr : 0),
|
||||
(xfer_sz ? ATA_SECT_SIZE * xfer_sz : 0),
|
||||
0,
|
||||
GFP_KERNEL,
|
||||
MTIP_IOCTL_COMMAND_TIMEOUT_MS)
|
||||
< 0) {
|
||||
return -1;
|
||||
rv = -EFAULT;
|
||||
goto exit_drive_command;
|
||||
}
|
||||
|
||||
/* Collect the completion status. */
|
||||
command[0] = reply->command; /* Status*/
|
||||
command[1] = reply->features; /* Error*/
|
||||
command[2] = command[3];
|
||||
command[2] = reply->sect_count;
|
||||
|
||||
dbg_printk(MTIP_DRV_NAME
|
||||
" %s: Completion Status: stat %x, "
|
||||
"err %x, cmd %x\n",
|
||||
"err %x, nsect %x\n",
|
||||
__func__,
|
||||
command[0],
|
||||
command[1],
|
||||
command[2]);
|
||||
|
||||
if (user_buffer && command[3]) {
|
||||
if (xfer_sz) {
|
||||
if (copy_to_user(user_buffer,
|
||||
port->sector_buffer,
|
||||
buf,
|
||||
ATA_SECT_SIZE * command[3])) {
|
||||
return -EFAULT;
|
||||
rv = -EFAULT;
|
||||
goto exit_drive_command;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
exit_drive_command:
|
||||
if (buf)
|
||||
dmam_free_coherent(&port->dd->pdev->dev,
|
||||
ATA_SECT_SIZE * xfer_sz, buf, dma_addr);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2003,6 +2030,32 @@ static unsigned int implicit_sector(unsigned char command,
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void mtip_set_timeout(struct host_to_dev_fis *fis, unsigned int *timeout)
|
||||
{
|
||||
switch (fis->command) {
|
||||
case ATA_CMD_DOWNLOAD_MICRO:
|
||||
*timeout = 120000; /* 2 minutes */
|
||||
break;
|
||||
case ATA_CMD_SEC_ERASE_UNIT:
|
||||
case 0xFC:
|
||||
*timeout = 240000; /* 4 minutes */
|
||||
break;
|
||||
case ATA_CMD_STANDBYNOW1:
|
||||
*timeout = 10000; /* 10 seconds */
|
||||
break;
|
||||
case 0xF7:
|
||||
case 0xFA:
|
||||
*timeout = 60000; /* 60 seconds */
|
||||
break;
|
||||
case ATA_CMD_SMART:
|
||||
*timeout = 15000; /* 15 seconds */
|
||||
break;
|
||||
default:
|
||||
*timeout = MTIP_IOCTL_COMMAND_TIMEOUT_MS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Executes a taskfile
|
||||
* See ide_taskfile_ioctl() for derivation
|
||||
@@ -2023,7 +2076,7 @@ static int exec_drive_taskfile(struct driver_data *dd,
|
||||
unsigned int taskin = 0;
|
||||
unsigned int taskout = 0;
|
||||
u8 nsect = 0;
|
||||
unsigned int timeout = MTIP_IOCTL_COMMAND_TIMEOUT_MS;
|
||||
unsigned int timeout;
|
||||
unsigned int force_single_sector;
|
||||
unsigned int transfer_size;
|
||||
unsigned long task_file_data;
|
||||
@@ -2153,32 +2206,7 @@ static int exec_drive_taskfile(struct driver_data *dd,
|
||||
fis.lba_hi,
|
||||
fis.device);
|
||||
|
||||
switch (fis.command) {
|
||||
case ATA_CMD_DOWNLOAD_MICRO:
|
||||
/* Change timeout for Download Microcode to 2 minutes */
|
||||
timeout = 120000;
|
||||
break;
|
||||
case ATA_CMD_SEC_ERASE_UNIT:
|
||||
/* Change timeout for Security Erase Unit to 4 minutes.*/
|
||||
timeout = 240000;
|
||||
break;
|
||||
case ATA_CMD_STANDBYNOW1:
|
||||
/* Change timeout for standby immediate to 10 seconds.*/
|
||||
timeout = 10000;
|
||||
break;
|
||||
case 0xF7:
|
||||
case 0xFA:
|
||||
/* Change timeout for vendor unique command to 10 secs */
|
||||
timeout = 10000;
|
||||
break;
|
||||
case ATA_CMD_SMART:
|
||||
/* Change timeout for vendor unique command to 15 secs */
|
||||
timeout = 15000;
|
||||
break;
|
||||
default:
|
||||
timeout = MTIP_IOCTL_COMMAND_TIMEOUT_MS;
|
||||
break;
|
||||
}
|
||||
mtip_set_timeout(&fis, &timeout);
|
||||
|
||||
/* Determine the correct transfer size.*/
|
||||
if (force_single_sector)
|
||||
@@ -2295,13 +2323,12 @@ static int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd,
|
||||
{
|
||||
switch (cmd) {
|
||||
case HDIO_GET_IDENTITY:
|
||||
if (mtip_get_identify(dd->port, (void __user *) arg) < 0) {
|
||||
dev_warn(&dd->pdev->dev,
|
||||
"Unable to read identity\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
{
|
||||
if (copy_to_user((void __user *)arg, dd->port->identify,
|
||||
sizeof(u16) * ATA_ID_WORDS))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
case HDIO_DRIVE_CMD:
|
||||
{
|
||||
u8 drive_command[4];
|
||||
@@ -2537,40 +2564,58 @@ static ssize_t mtip_hw_show_registers(struct device *dev,
|
||||
int size = 0;
|
||||
int n;
|
||||
|
||||
size += sprintf(&buf[size], "S ACTive:\n");
|
||||
size += sprintf(&buf[size], "Hardware\n--------\n");
|
||||
size += sprintf(&buf[size], "S ACTive : [ 0x");
|
||||
|
||||
for (n = 0; n < dd->slot_groups; n++)
|
||||
size += sprintf(&buf[size], "0x%08x\n",
|
||||
for (n = dd->slot_groups-1; n >= 0; n--)
|
||||
size += sprintf(&buf[size], "%08X ",
|
||||
readl(dd->port->s_active[n]));
|
||||
|
||||
size += sprintf(&buf[size], "Command Issue:\n");
|
||||
size += sprintf(&buf[size], "]\n");
|
||||
size += sprintf(&buf[size], "Command Issue : [ 0x");
|
||||
|
||||
for (n = 0; n < dd->slot_groups; n++)
|
||||
size += sprintf(&buf[size], "0x%08x\n",
|
||||
for (n = dd->slot_groups-1; n >= 0; n--)
|
||||
size += sprintf(&buf[size], "%08X ",
|
||||
readl(dd->port->cmd_issue[n]));
|
||||
|
||||
size += sprintf(&buf[size], "Allocated:\n");
|
||||
size += sprintf(&buf[size], "]\n");
|
||||
size += sprintf(&buf[size], "Completed : [ 0x");
|
||||
|
||||
for (n = 0; n < dd->slot_groups; n++) {
|
||||
for (n = dd->slot_groups-1; n >= 0; n--)
|
||||
size += sprintf(&buf[size], "%08X ",
|
||||
readl(dd->port->completed[n]));
|
||||
|
||||
size += sprintf(&buf[size], "]\n");
|
||||
size += sprintf(&buf[size], "PORT IRQ STAT : [ 0x%08X ]\n",
|
||||
readl(dd->port->mmio + PORT_IRQ_STAT));
|
||||
size += sprintf(&buf[size], "HOST IRQ STAT : [ 0x%08X ]\n",
|
||||
readl(dd->mmio + HOST_IRQ_STAT));
|
||||
size += sprintf(&buf[size], "\n");
|
||||
|
||||
size += sprintf(&buf[size], "Local\n-----\n");
|
||||
size += sprintf(&buf[size], "Allocated : [ 0x");
|
||||
|
||||
for (n = dd->slot_groups-1; n >= 0; n--) {
|
||||
if (sizeof(long) > sizeof(u32))
|
||||
group_allocated =
|
||||
dd->port->allocated[n/2] >> (32*(n&1));
|
||||
else
|
||||
group_allocated = dd->port->allocated[n];
|
||||
size += sprintf(&buf[size], "0x%08x\n",
|
||||
group_allocated);
|
||||
size += sprintf(&buf[size], "%08X ", group_allocated);
|
||||
}
|
||||
size += sprintf(&buf[size], "]\n");
|
||||
|
||||
size += sprintf(&buf[size], "Completed:\n");
|
||||
size += sprintf(&buf[size], "Commands in Q: [ 0x");
|
||||
|
||||
for (n = 0; n < dd->slot_groups; n++)
|
||||
size += sprintf(&buf[size], "0x%08x\n",
|
||||
readl(dd->port->completed[n]));
|
||||
|
||||
size += sprintf(&buf[size], "PORT IRQ STAT : 0x%08x\n",
|
||||
readl(dd->port->mmio + PORT_IRQ_STAT));
|
||||
size += sprintf(&buf[size], "HOST IRQ STAT : 0x%08x\n",
|
||||
readl(dd->mmio + HOST_IRQ_STAT));
|
||||
for (n = dd->slot_groups-1; n >= 0; n--) {
|
||||
if (sizeof(long) > sizeof(u32))
|
||||
group_allocated =
|
||||
dd->port->cmds_to_issue[n/2] >> (32*(n&1));
|
||||
else
|
||||
group_allocated = dd->port->cmds_to_issue[n];
|
||||
size += sprintf(&buf[size], "%08X ", group_allocated);
|
||||
}
|
||||
size += sprintf(&buf[size], "]\n");
|
||||
|
||||
return size;
|
||||
}
|
||||
@@ -2592,8 +2637,24 @@ static ssize_t mtip_hw_show_status(struct device *dev,
|
||||
return size;
|
||||
}
|
||||
|
||||
static ssize_t mtip_hw_show_flags(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct driver_data *dd = dev_to_disk(dev)->private_data;
|
||||
int size = 0;
|
||||
|
||||
size += sprintf(&buf[size], "Flag in port struct : [ %08lX ]\n",
|
||||
dd->port->flags);
|
||||
size += sprintf(&buf[size], "Flag in dd struct : [ %08lX ]\n",
|
||||
dd->dd_flag);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(registers, S_IRUGO, mtip_hw_show_registers, NULL);
|
||||
static DEVICE_ATTR(status, S_IRUGO, mtip_hw_show_status, NULL);
|
||||
static DEVICE_ATTR(flags, S_IRUGO, mtip_hw_show_flags, NULL);
|
||||
|
||||
/*
|
||||
* Create the sysfs related attributes.
|
||||
@@ -2616,6 +2677,9 @@ static int mtip_hw_sysfs_init(struct driver_data *dd, struct kobject *kobj)
|
||||
if (sysfs_create_file(kobj, &dev_attr_status.attr))
|
||||
dev_warn(&dd->pdev->dev,
|
||||
"Error creating 'status' sysfs entry\n");
|
||||
if (sysfs_create_file(kobj, &dev_attr_flags.attr))
|
||||
dev_warn(&dd->pdev->dev,
|
||||
"Error creating 'flags' sysfs entry\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2636,6 +2700,7 @@ static int mtip_hw_sysfs_exit(struct driver_data *dd, struct kobject *kobj)
|
||||
|
||||
sysfs_remove_file(kobj, &dev_attr_registers.attr);
|
||||
sysfs_remove_file(kobj, &dev_attr_status.attr);
|
||||
sysfs_remove_file(kobj, &dev_attr_flags.attr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -3634,7 +3699,10 @@ skip_create_disk:
|
||||
set_bit(QUEUE_FLAG_NONROT, &dd->queue->queue_flags);
|
||||
blk_queue_max_segments(dd->queue, MTIP_MAX_SG);
|
||||
blk_queue_physical_block_size(dd->queue, 4096);
|
||||
blk_queue_max_hw_sectors(dd->queue, 0xffff);
|
||||
blk_queue_max_segment_size(dd->queue, 0x400000);
|
||||
blk_queue_io_min(dd->queue, 4096);
|
||||
|
||||
/*
|
||||
* write back cache is not supported in the device. FUA depends on
|
||||
* write back cache support, hence setting flush support to zero.
|
||||
|
||||
@@ -113,33 +113,35 @@
|
||||
|
||||
#define __force_bit2int (unsigned int __force)
|
||||
|
||||
/* below are bit numbers in 'flags' defined in mtip_port */
|
||||
#define MTIP_PF_IC_ACTIVE_BIT 0 /* pio/ioctl */
|
||||
#define MTIP_PF_EH_ACTIVE_BIT 1 /* error handling */
|
||||
#define MTIP_PF_SE_ACTIVE_BIT 2 /* secure erase */
|
||||
#define MTIP_PF_DM_ACTIVE_BIT 3 /* download microcde */
|
||||
#define MTIP_PF_PAUSE_IO ((1 << MTIP_PF_IC_ACTIVE_BIT) | \
|
||||
enum {
|
||||
/* below are bit numbers in 'flags' defined in mtip_port */
|
||||
MTIP_PF_IC_ACTIVE_BIT = 0, /* pio/ioctl */
|
||||
MTIP_PF_EH_ACTIVE_BIT = 1, /* error handling */
|
||||
MTIP_PF_SE_ACTIVE_BIT = 2, /* secure erase */
|
||||
MTIP_PF_DM_ACTIVE_BIT = 3, /* download microcde */
|
||||
MTIP_PF_PAUSE_IO = ((1 << MTIP_PF_IC_ACTIVE_BIT) | \
|
||||
(1 << MTIP_PF_EH_ACTIVE_BIT) | \
|
||||
(1 << MTIP_PF_SE_ACTIVE_BIT) | \
|
||||
(1 << MTIP_PF_DM_ACTIVE_BIT))
|
||||
(1 << MTIP_PF_DM_ACTIVE_BIT)),
|
||||
|
||||
#define MTIP_PF_SVC_THD_ACTIVE_BIT 4
|
||||
#define MTIP_PF_ISSUE_CMDS_BIT 5
|
||||
#define MTIP_PF_REBUILD_BIT 6
|
||||
#define MTIP_PF_SVC_THD_STOP_BIT 8
|
||||
MTIP_PF_SVC_THD_ACTIVE_BIT = 4,
|
||||
MTIP_PF_ISSUE_CMDS_BIT = 5,
|
||||
MTIP_PF_REBUILD_BIT = 6,
|
||||
MTIP_PF_SVC_THD_STOP_BIT = 8,
|
||||
|
||||
/* below are bit numbers in 'dd_flag' defined in driver_data */
|
||||
#define MTIP_DDF_REMOVE_PENDING_BIT 1
|
||||
#define MTIP_DDF_OVER_TEMP_BIT 2
|
||||
#define MTIP_DDF_WRITE_PROTECT_BIT 3
|
||||
#define MTIP_DDF_STOP_IO ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \
|
||||
/* below are bit numbers in 'dd_flag' defined in driver_data */
|
||||
MTIP_DDF_REMOVE_PENDING_BIT = 1,
|
||||
MTIP_DDF_OVER_TEMP_BIT = 2,
|
||||
MTIP_DDF_WRITE_PROTECT_BIT = 3,
|
||||
MTIP_DDF_STOP_IO = ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \
|
||||
(1 << MTIP_DDF_OVER_TEMP_BIT) | \
|
||||
(1 << MTIP_DDF_WRITE_PROTECT_BIT))
|
||||
(1 << MTIP_DDF_WRITE_PROTECT_BIT)),
|
||||
|
||||
#define MTIP_DDF_CLEANUP_BIT 5
|
||||
#define MTIP_DDF_RESUME_BIT 6
|
||||
#define MTIP_DDF_INIT_DONE_BIT 7
|
||||
#define MTIP_DDF_REBUILD_FAILED_BIT 8
|
||||
MTIP_DDF_CLEANUP_BIT = 5,
|
||||
MTIP_DDF_RESUME_BIT = 6,
|
||||
MTIP_DDF_INIT_DONE_BIT = 7,
|
||||
MTIP_DDF_REBUILD_FAILED_BIT = 8,
|
||||
};
|
||||
|
||||
__packed struct smart_attr{
|
||||
u8 attr_id;
|
||||
|
||||
+29
-43
@@ -141,7 +141,7 @@ struct rbd_request {
|
||||
struct rbd_snap {
|
||||
struct device dev;
|
||||
const char *name;
|
||||
size_t size;
|
||||
u64 size;
|
||||
struct list_head node;
|
||||
u64 id;
|
||||
};
|
||||
@@ -175,8 +175,7 @@ struct rbd_device {
|
||||
/* protects updating the header */
|
||||
struct rw_semaphore header_rwsem;
|
||||
char snap_name[RBD_MAX_SNAP_NAME_LEN];
|
||||
u32 cur_snap; /* index+1 of current snapshot within snap context
|
||||
0 - for the head */
|
||||
u64 snap_id; /* current snapshot id */
|
||||
int read_only;
|
||||
|
||||
struct list_head node;
|
||||
@@ -241,7 +240,7 @@ static void rbd_put_dev(struct rbd_device *rbd_dev)
|
||||
put_device(&rbd_dev->dev);
|
||||
}
|
||||
|
||||
static int __rbd_update_snaps(struct rbd_device *rbd_dev);
|
||||
static int __rbd_refresh_header(struct rbd_device *rbd_dev);
|
||||
|
||||
static int rbd_open(struct block_device *bdev, fmode_t mode)
|
||||
{
|
||||
@@ -450,7 +449,9 @@ static void rbd_client_release(struct kref *kref)
|
||||
struct rbd_client *rbdc = container_of(kref, struct rbd_client, kref);
|
||||
|
||||
dout("rbd_release_client %p\n", rbdc);
|
||||
spin_lock(&rbd_client_list_lock);
|
||||
list_del(&rbdc->node);
|
||||
spin_unlock(&rbd_client_list_lock);
|
||||
|
||||
ceph_destroy_client(rbdc->client);
|
||||
kfree(rbdc->rbd_opts);
|
||||
@@ -463,9 +464,7 @@ static void rbd_client_release(struct kref *kref)
|
||||
*/
|
||||
static void rbd_put_client(struct rbd_device *rbd_dev)
|
||||
{
|
||||
spin_lock(&rbd_client_list_lock);
|
||||
kref_put(&rbd_dev->rbd_client->kref, rbd_client_release);
|
||||
spin_unlock(&rbd_client_list_lock);
|
||||
rbd_dev->rbd_client = NULL;
|
||||
}
|
||||
|
||||
@@ -487,16 +486,18 @@ static void rbd_coll_release(struct kref *kref)
|
||||
*/
|
||||
static int rbd_header_from_disk(struct rbd_image_header *header,
|
||||
struct rbd_image_header_ondisk *ondisk,
|
||||
int allocated_snaps,
|
||||
u32 allocated_snaps,
|
||||
gfp_t gfp_flags)
|
||||
{
|
||||
int i;
|
||||
u32 snap_count;
|
||||
u32 i, snap_count;
|
||||
|
||||
if (memcmp(ondisk, RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT)))
|
||||
return -ENXIO;
|
||||
|
||||
snap_count = le32_to_cpu(ondisk->snap_count);
|
||||
if (snap_count > (UINT_MAX - sizeof(struct ceph_snap_context))
|
||||
/ sizeof (*ondisk))
|
||||
return -EINVAL;
|
||||
header->snapc = kmalloc(sizeof(struct ceph_snap_context) +
|
||||
snap_count * sizeof (*ondisk),
|
||||
gfp_flags);
|
||||
@@ -506,11 +507,11 @@ static int rbd_header_from_disk(struct rbd_image_header *header,
|
||||
header->snap_names_len = le64_to_cpu(ondisk->snap_names_len);
|
||||
if (snap_count) {
|
||||
header->snap_names = kmalloc(header->snap_names_len,
|
||||
GFP_KERNEL);
|
||||
gfp_flags);
|
||||
if (!header->snap_names)
|
||||
goto err_snapc;
|
||||
header->snap_sizes = kmalloc(snap_count * sizeof(u64),
|
||||
GFP_KERNEL);
|
||||
gfp_flags);
|
||||
if (!header->snap_sizes)
|
||||
goto err_names;
|
||||
} else {
|
||||
@@ -552,21 +553,6 @@ err_snapc:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int snap_index(struct rbd_image_header *header, int snap_num)
|
||||
{
|
||||
return header->total_snaps - snap_num;
|
||||
}
|
||||
|
||||
static u64 cur_snap_id(struct rbd_device *rbd_dev)
|
||||
{
|
||||
struct rbd_image_header *header = &rbd_dev->header;
|
||||
|
||||
if (!rbd_dev->cur_snap)
|
||||
return 0;
|
||||
|
||||
return header->snapc->snaps[snap_index(header, rbd_dev->cur_snap)];
|
||||
}
|
||||
|
||||
static int snap_by_name(struct rbd_image_header *header, const char *snap_name,
|
||||
u64 *seq, u64 *size)
|
||||
{
|
||||
@@ -605,7 +591,7 @@ static int rbd_header_set_snap(struct rbd_device *dev, u64 *size)
|
||||
snapc->seq = header->snap_seq;
|
||||
else
|
||||
snapc->seq = 0;
|
||||
dev->cur_snap = 0;
|
||||
dev->snap_id = CEPH_NOSNAP;
|
||||
dev->read_only = 0;
|
||||
if (size)
|
||||
*size = header->image_size;
|
||||
@@ -613,8 +599,7 @@ static int rbd_header_set_snap(struct rbd_device *dev, u64 *size)
|
||||
ret = snap_by_name(header, dev->snap_name, &snapc->seq, size);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
dev->cur_snap = header->total_snaps - ret;
|
||||
dev->snap_id = snapc->seq;
|
||||
dev->read_only = 1;
|
||||
}
|
||||
|
||||
@@ -935,7 +920,6 @@ static int rbd_do_request(struct request *rq,
|
||||
layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
|
||||
layout->fl_stripe_count = cpu_to_le32(1);
|
||||
layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER);
|
||||
layout->fl_pg_preferred = cpu_to_le32(-1);
|
||||
layout->fl_pg_pool = cpu_to_le32(dev->poolid);
|
||||
ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno,
|
||||
req, ops);
|
||||
@@ -1168,7 +1152,7 @@ static int rbd_req_read(struct request *rq,
|
||||
int coll_index)
|
||||
{
|
||||
return rbd_do_op(rq, rbd_dev, NULL,
|
||||
(snapid ? snapid : CEPH_NOSNAP),
|
||||
snapid,
|
||||
CEPH_OSD_OP_READ,
|
||||
CEPH_OSD_FLAG_READ,
|
||||
2,
|
||||
@@ -1187,7 +1171,7 @@ static int rbd_req_sync_read(struct rbd_device *dev,
|
||||
u64 *ver)
|
||||
{
|
||||
return rbd_req_sync_op(dev, NULL,
|
||||
(snapid ? snapid : CEPH_NOSNAP),
|
||||
snapid,
|
||||
CEPH_OSD_OP_READ,
|
||||
CEPH_OSD_FLAG_READ,
|
||||
NULL,
|
||||
@@ -1238,7 +1222,7 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data)
|
||||
dout("rbd_watch_cb %s notify_id=%lld opcode=%d\n", dev->obj_md_name,
|
||||
notify_id, (int)opcode);
|
||||
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
|
||||
rc = __rbd_update_snaps(dev);
|
||||
rc = __rbd_refresh_header(dev);
|
||||
mutex_unlock(&ctl_mutex);
|
||||
if (rc)
|
||||
pr_warning(RBD_DRV_NAME "%d got notification but failed to "
|
||||
@@ -1521,7 +1505,7 @@ static void rbd_rq_fn(struct request_queue *q)
|
||||
coll, cur_seg);
|
||||
else
|
||||
rbd_req_read(rq, rbd_dev,
|
||||
cur_snap_id(rbd_dev),
|
||||
rbd_dev->snap_id,
|
||||
ofs,
|
||||
op_size, bio,
|
||||
coll, cur_seg);
|
||||
@@ -1592,7 +1576,7 @@ static int rbd_read_header(struct rbd_device *rbd_dev,
|
||||
{
|
||||
ssize_t rc;
|
||||
struct rbd_image_header_ondisk *dh;
|
||||
int snap_count = 0;
|
||||
u32 snap_count = 0;
|
||||
u64 ver;
|
||||
size_t len;
|
||||
|
||||
@@ -1656,7 +1640,7 @@ static int rbd_header_add_snap(struct rbd_device *dev,
|
||||
struct ceph_mon_client *monc;
|
||||
|
||||
/* we should create a snapshot only if we're pointing at the head */
|
||||
if (dev->cur_snap)
|
||||
if (dev->snap_id != CEPH_NOSNAP)
|
||||
return -EINVAL;
|
||||
|
||||
monc = &dev->rbd_client->client->monc;
|
||||
@@ -1683,7 +1667,9 @@ static int rbd_header_add_snap(struct rbd_device *dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev->header.snapc->seq = new_snapid;
|
||||
down_write(&dev->header_rwsem);
|
||||
dev->header.snapc->seq = new_snapid;
|
||||
up_write(&dev->header_rwsem);
|
||||
|
||||
return 0;
|
||||
bad:
|
||||
@@ -1703,7 +1689,7 @@ static void __rbd_remove_all_snaps(struct rbd_device *rbd_dev)
|
||||
/*
|
||||
* only read the first part of the ondisk header, without the snaps info
|
||||
*/
|
||||
static int __rbd_update_snaps(struct rbd_device *rbd_dev)
|
||||
static int __rbd_refresh_header(struct rbd_device *rbd_dev)
|
||||
{
|
||||
int ret;
|
||||
struct rbd_image_header h;
|
||||
@@ -1890,7 +1876,7 @@ static ssize_t rbd_image_refresh(struct device *dev,
|
||||
|
||||
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
|
||||
|
||||
rc = __rbd_update_snaps(rbd_dev);
|
||||
rc = __rbd_refresh_header(rbd_dev);
|
||||
if (rc < 0)
|
||||
ret = rc;
|
||||
|
||||
@@ -1949,7 +1935,7 @@ static ssize_t rbd_snap_size_show(struct device *dev,
|
||||
{
|
||||
struct rbd_snap *snap = container_of(dev, struct rbd_snap, dev);
|
||||
|
||||
return sprintf(buf, "%zd\n", snap->size);
|
||||
return sprintf(buf, "%llu\n", (unsigned long long)snap->size);
|
||||
}
|
||||
|
||||
static ssize_t rbd_snap_id_show(struct device *dev,
|
||||
@@ -1958,7 +1944,7 @@ static ssize_t rbd_snap_id_show(struct device *dev,
|
||||
{
|
||||
struct rbd_snap *snap = container_of(dev, struct rbd_snap, dev);
|
||||
|
||||
return sprintf(buf, "%llu\n", (unsigned long long) snap->id);
|
||||
return sprintf(buf, "%llu\n", (unsigned long long)snap->id);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(snap_size, S_IRUGO, rbd_snap_size_show, NULL);
|
||||
@@ -2173,7 +2159,7 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev)
|
||||
rbd_dev->header.obj_version);
|
||||
if (ret == -ERANGE) {
|
||||
mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
|
||||
rc = __rbd_update_snaps(rbd_dev);
|
||||
rc = __rbd_refresh_header(rbd_dev);
|
||||
mutex_unlock(&ctl_mutex);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
@@ -2558,7 +2544,7 @@ static ssize_t rbd_snap_add(struct device *dev,
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
ret = __rbd_update_snaps(rbd_dev);
|
||||
ret = __rbd_refresh_header(rbd_dev);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
|
||||
+11
-10
@@ -29,9 +29,6 @@ struct virtio_blk
|
||||
/* The disk structure for the kernel. */
|
||||
struct gendisk *disk;
|
||||
|
||||
/* Request tracking. */
|
||||
struct list_head reqs;
|
||||
|
||||
mempool_t *pool;
|
||||
|
||||
/* Process context for config space updates */
|
||||
@@ -55,7 +52,6 @@ struct virtio_blk
|
||||
|
||||
struct virtblk_req
|
||||
{
|
||||
struct list_head list;
|
||||
struct request *req;
|
||||
struct virtio_blk_outhdr out_hdr;
|
||||
struct virtio_scsi_inhdr in_hdr;
|
||||
@@ -99,7 +95,6 @@ static void blk_done(struct virtqueue *vq)
|
||||
}
|
||||
|
||||
__blk_end_request_all(vbr->req, error);
|
||||
list_del(&vbr->list);
|
||||
mempool_free(vbr, vblk->pool);
|
||||
}
|
||||
/* In case queue is stopped waiting for more buffers. */
|
||||
@@ -184,7 +179,6 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk,
|
||||
return false;
|
||||
}
|
||||
|
||||
list_add_tail(&vbr->list, &vblk->reqs);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -437,7 +431,6 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
|
||||
goto out_free_index;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&vblk->reqs);
|
||||
spin_lock_init(&vblk->lock);
|
||||
vblk->vdev = vdev;
|
||||
vblk->sg_elems = sg_elems;
|
||||
@@ -583,21 +576,29 @@ static void __devexit virtblk_remove(struct virtio_device *vdev)
|
||||
{
|
||||
struct virtio_blk *vblk = vdev->priv;
|
||||
int index = vblk->index;
|
||||
struct virtblk_req *vbr;
|
||||
unsigned long flags;
|
||||
|
||||
/* Prevent config work handler from accessing the device. */
|
||||
mutex_lock(&vblk->config_lock);
|
||||
vblk->config_enable = false;
|
||||
mutex_unlock(&vblk->config_lock);
|
||||
|
||||
/* Nothing should be pending. */
|
||||
BUG_ON(!list_empty(&vblk->reqs));
|
||||
|
||||
/* Stop all the virtqueues. */
|
||||
vdev->config->reset(vdev);
|
||||
|
||||
flush_work(&vblk->config_work);
|
||||
|
||||
del_gendisk(vblk->disk);
|
||||
|
||||
/* Abort requests dispatched to driver. */
|
||||
spin_lock_irqsave(&vblk->lock, flags);
|
||||
while ((vbr = virtqueue_detach_unused_buf(vblk->vq))) {
|
||||
__blk_end_request_all(vbr->req, -EIO);
|
||||
mempool_free(vbr, vblk->pool);
|
||||
}
|
||||
spin_unlock_irqrestore(&vblk->lock, flags);
|
||||
|
||||
blk_cleanup_queue(vblk->disk->queue);
|
||||
put_disk(vblk->disk);
|
||||
mempool_destroy(vblk->pool);
|
||||
|
||||
@@ -526,6 +526,14 @@ static int xen_translate_vdev(int vdevice, int *minor, unsigned int *offset)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *encode_disk_name(char *ptr, unsigned int n)
|
||||
{
|
||||
if (n >= 26)
|
||||
ptr = encode_disk_name(ptr, n / 26 - 1);
|
||||
*ptr = 'a' + n % 26;
|
||||
return ptr + 1;
|
||||
}
|
||||
|
||||
static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
|
||||
struct blkfront_info *info,
|
||||
u16 vdisk_info, u16 sector_size)
|
||||
@@ -536,6 +544,7 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
|
||||
unsigned int offset;
|
||||
int minor;
|
||||
int nr_parts;
|
||||
char *ptr;
|
||||
|
||||
BUG_ON(info->gd != NULL);
|
||||
BUG_ON(info->rq != NULL);
|
||||
@@ -560,7 +569,11 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
|
||||
"emulated IDE disks,\n\t choose an xvd device name"
|
||||
"from xvde on\n", info->vdevice);
|
||||
}
|
||||
err = -ENODEV;
|
||||
if (minor >> MINORBITS) {
|
||||
pr_warn("blkfront: %#x's minor (%#x) out of range; ignoring\n",
|
||||
info->vdevice, minor);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if ((minor % nr_parts) == 0)
|
||||
nr_minors = nr_parts;
|
||||
@@ -574,23 +587,14 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity,
|
||||
if (gd == NULL)
|
||||
goto release;
|
||||
|
||||
if (nr_minors > 1) {
|
||||
if (offset < 26)
|
||||
sprintf(gd->disk_name, "%s%c", DEV_NAME, 'a' + offset);
|
||||
else
|
||||
sprintf(gd->disk_name, "%s%c%c", DEV_NAME,
|
||||
'a' + ((offset / 26)-1), 'a' + (offset % 26));
|
||||
} else {
|
||||
if (offset < 26)
|
||||
sprintf(gd->disk_name, "%s%c%d", DEV_NAME,
|
||||
'a' + offset,
|
||||
minor & (nr_parts - 1));
|
||||
else
|
||||
sprintf(gd->disk_name, "%s%c%c%d", DEV_NAME,
|
||||
'a' + ((offset / 26) - 1),
|
||||
'a' + (offset % 26),
|
||||
minor & (nr_parts - 1));
|
||||
}
|
||||
strcpy(gd->disk_name, DEV_NAME);
|
||||
ptr = encode_disk_name(gd->disk_name + sizeof(DEV_NAME) - 1, offset);
|
||||
BUG_ON(ptr >= gd->disk_name + DISK_NAME_LEN);
|
||||
if (nr_minors > 1)
|
||||
*ptr = 0;
|
||||
else
|
||||
snprintf(ptr, gd->disk_name + DISK_NAME_LEN - ptr,
|
||||
"%d", minor & (nr_parts - 1));
|
||||
|
||||
gd->major = XENVBD_MAJOR;
|
||||
gd->first_minor = minor;
|
||||
@@ -1496,7 +1500,9 @@ module_init(xlblk_init);
|
||||
|
||||
static void __exit xlblk_exit(void)
|
||||
{
|
||||
return xenbus_unregister_driver(&blkfront_driver);
|
||||
xenbus_unregister_driver(&blkfront_driver);
|
||||
unregister_blkdev(XENVBD_MAJOR, DEV_NAME);
|
||||
kfree(minors);
|
||||
}
|
||||
module_exit(xlblk_exit);
|
||||
|
||||
|
||||
@@ -81,6 +81,9 @@ static struct usb_device_id ath3k_table[] = {
|
||||
/* Atheros AR5BBU12 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xE02C) },
|
||||
|
||||
/* Atheros AR5BBU22 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xE03C) },
|
||||
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
@@ -99,6 +102,9 @@ static struct usb_device_id ath3k_blist_tbl[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
|
||||
|
||||
/* Atheros AR5BBU22 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
|
||||
|
||||
{ } /* Terminating entry */
|
||||
};
|
||||
|
||||
@@ -439,6 +445,7 @@ static struct usb_driver ath3k_driver = {
|
||||
.probe = ath3k_probe,
|
||||
.disconnect = ath3k_disconnect,
|
||||
.id_table = ath3k_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(ath3k_driver);
|
||||
|
||||
@@ -279,6 +279,7 @@ static struct usb_driver bcm203x_driver = {
|
||||
.probe = bcm203x_probe,
|
||||
.disconnect = bcm203x_disconnect,
|
||||
.id_table = bcm203x_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(bcm203x_driver);
|
||||
|
||||
@@ -749,6 +749,7 @@ static struct usb_driver bfusb_driver = {
|
||||
.probe = bfusb_probe,
|
||||
.disconnect = bfusb_disconnect,
|
||||
.id_table = bfusb_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(bfusb_driver);
|
||||
|
||||
@@ -508,6 +508,7 @@ static struct usb_driver bpa10x_driver = {
|
||||
.probe = bpa10x_probe,
|
||||
.disconnect = bpa10x_disconnect,
|
||||
.id_table = bpa10x_table,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(bpa10x_driver);
|
||||
|
||||
@@ -67,6 +67,7 @@ struct btmrvl_adapter {
|
||||
u8 wakeup_tries;
|
||||
wait_queue_head_t cmd_wait_q;
|
||||
u8 cmd_complete;
|
||||
bool is_suspended;
|
||||
};
|
||||
|
||||
struct btmrvl_private {
|
||||
@@ -139,8 +140,10 @@ void btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb);
|
||||
int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb);
|
||||
|
||||
int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd);
|
||||
int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv);
|
||||
int btmrvl_enable_ps(struct btmrvl_private *priv);
|
||||
int btmrvl_prepare_command(struct btmrvl_private *priv);
|
||||
int btmrvl_enable_hs(struct btmrvl_private *priv);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void btmrvl_debugfs_init(struct hci_dev *hdev);
|
||||
|
||||
@@ -200,6 +200,36 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd);
|
||||
|
||||
int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct btmrvl_cmd *cmd;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
BT_ERR("No free skb");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
|
||||
cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF,
|
||||
BT_CMD_HOST_SLEEP_CONFIG));
|
||||
cmd->length = 2;
|
||||
cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
|
||||
cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);
|
||||
|
||||
bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
|
||||
|
||||
skb->dev = (void *) priv->btmrvl_dev.hcidev;
|
||||
skb_queue_head(&priv->adapter->tx_queue, skb);
|
||||
|
||||
BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x", cmd->data[0],
|
||||
cmd->data[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd);
|
||||
|
||||
int btmrvl_enable_ps(struct btmrvl_private *priv)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
@@ -232,7 +262,7 @@ int btmrvl_enable_ps(struct btmrvl_private *priv)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmrvl_enable_ps);
|
||||
|
||||
static int btmrvl_enable_hs(struct btmrvl_private *priv)
|
||||
int btmrvl_enable_hs(struct btmrvl_private *priv)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct btmrvl_cmd *cmd;
|
||||
@@ -268,35 +298,15 @@ static int btmrvl_enable_hs(struct btmrvl_private *priv)
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btmrvl_enable_hs);
|
||||
|
||||
int btmrvl_prepare_command(struct btmrvl_private *priv)
|
||||
{
|
||||
struct sk_buff *skb = NULL;
|
||||
struct btmrvl_cmd *cmd;
|
||||
int ret = 0;
|
||||
|
||||
if (priv->btmrvl_dev.hscfgcmd) {
|
||||
priv->btmrvl_dev.hscfgcmd = 0;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC);
|
||||
if (skb == NULL) {
|
||||
BT_ERR("No free skb");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd));
|
||||
cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_CONFIG));
|
||||
cmd->length = 2;
|
||||
cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8;
|
||||
cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff);
|
||||
|
||||
bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT;
|
||||
|
||||
skb->dev = (void *) priv->btmrvl_dev.hcidev;
|
||||
skb_queue_head(&priv->adapter->tx_queue, skb);
|
||||
|
||||
BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x",
|
||||
cmd->data[0], cmd->data[1]);
|
||||
btmrvl_send_hscfg_cmd(priv);
|
||||
}
|
||||
|
||||
if (priv->btmrvl_dev.pscmd) {
|
||||
|
||||
@@ -339,9 +339,7 @@ static int btmrvl_sdio_download_helper(struct btmrvl_sdio_card *card)
|
||||
|
||||
done:
|
||||
kfree(tmphlprbuf);
|
||||
if (fw_helper)
|
||||
release_firmware(fw_helper);
|
||||
|
||||
release_firmware(fw_helper);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -484,10 +482,7 @@ static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card)
|
||||
|
||||
done:
|
||||
kfree(tmpfwbuf);
|
||||
|
||||
if (fw_firmware)
|
||||
release_firmware(fw_firmware);
|
||||
|
||||
release_firmware(fw_firmware);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1013,6 +1008,9 @@ static int btmrvl_sdio_probe(struct sdio_func *func,
|
||||
priv->btmrvl_dev.psmode = 1;
|
||||
btmrvl_enable_ps(priv);
|
||||
|
||||
priv->btmrvl_dev.gpio_gap = 0xffff;
|
||||
btmrvl_send_hscfg_cmd(priv);
|
||||
|
||||
return 0;
|
||||
|
||||
disable_host_int:
|
||||
@@ -1048,11 +1046,111 @@ static void btmrvl_sdio_remove(struct sdio_func *func)
|
||||
}
|
||||
}
|
||||
|
||||
static int btmrvl_sdio_suspend(struct device *dev)
|
||||
{
|
||||
struct sdio_func *func = dev_to_sdio_func(dev);
|
||||
struct btmrvl_sdio_card *card;
|
||||
struct btmrvl_private *priv;
|
||||
mmc_pm_flag_t pm_flags;
|
||||
struct hci_dev *hcidev;
|
||||
|
||||
if (func) {
|
||||
pm_flags = sdio_get_host_pm_caps(func);
|
||||
BT_DBG("%s: suspend: PM flags = 0x%x", sdio_func_id(func),
|
||||
pm_flags);
|
||||
if (!(pm_flags & MMC_PM_KEEP_POWER)) {
|
||||
BT_ERR("%s: cannot remain alive while suspended",
|
||||
sdio_func_id(func));
|
||||
return -ENOSYS;
|
||||
}
|
||||
card = sdio_get_drvdata(func);
|
||||
if (!card || !card->priv) {
|
||||
BT_ERR("card or priv structure is not valid");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
BT_ERR("sdio_func is not specified");
|
||||
return 0;
|
||||
}
|
||||
|
||||
priv = card->priv;
|
||||
|
||||
if (priv->adapter->hs_state != HS_ACTIVATED) {
|
||||
if (btmrvl_enable_hs(priv)) {
|
||||
BT_ERR("HS not actived, suspend failed!");
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
hcidev = priv->btmrvl_dev.hcidev;
|
||||
BT_DBG("%s: SDIO suspend", hcidev->name);
|
||||
hci_suspend_dev(hcidev);
|
||||
skb_queue_purge(&priv->adapter->tx_queue);
|
||||
|
||||
priv->adapter->is_suspended = true;
|
||||
|
||||
/* We will keep the power when hs enabled successfully */
|
||||
if (priv->adapter->hs_state == HS_ACTIVATED) {
|
||||
BT_DBG("suspend with MMC_PM_KEEP_POWER");
|
||||
return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
|
||||
} else {
|
||||
BT_DBG("suspend without MMC_PM_KEEP_POWER");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int btmrvl_sdio_resume(struct device *dev)
|
||||
{
|
||||
struct sdio_func *func = dev_to_sdio_func(dev);
|
||||
struct btmrvl_sdio_card *card;
|
||||
struct btmrvl_private *priv;
|
||||
mmc_pm_flag_t pm_flags;
|
||||
struct hci_dev *hcidev;
|
||||
|
||||
if (func) {
|
||||
pm_flags = sdio_get_host_pm_caps(func);
|
||||
BT_DBG("%s: resume: PM flags = 0x%x", sdio_func_id(func),
|
||||
pm_flags);
|
||||
card = sdio_get_drvdata(func);
|
||||
if (!card || !card->priv) {
|
||||
BT_ERR("card or priv structure is not valid");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
BT_ERR("sdio_func is not specified");
|
||||
return 0;
|
||||
}
|
||||
priv = card->priv;
|
||||
|
||||
if (!priv->adapter->is_suspended) {
|
||||
BT_DBG("device already resumed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
priv->adapter->is_suspended = false;
|
||||
hcidev = priv->btmrvl_dev.hcidev;
|
||||
BT_DBG("%s: SDIO resume", hcidev->name);
|
||||
hci_resume_dev(hcidev);
|
||||
priv->hw_wakeup_firmware(priv);
|
||||
priv->adapter->hs_state = HS_DEACTIVATED;
|
||||
BT_DBG("%s: HS DEACTIVATED in resume!", hcidev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops btmrvl_sdio_pm_ops = {
|
||||
.suspend = btmrvl_sdio_suspend,
|
||||
.resume = btmrvl_sdio_resume,
|
||||
};
|
||||
|
||||
static struct sdio_driver bt_mrvl_sdio = {
|
||||
.name = "btmrvl_sdio",
|
||||
.id_table = btmrvl_sdio_ids,
|
||||
.probe = btmrvl_sdio_probe,
|
||||
.remove = btmrvl_sdio_remove,
|
||||
.drv = {
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &btmrvl_sdio_pm_ops,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init btmrvl_sdio_init_module(void)
|
||||
|
||||
@@ -143,6 +143,9 @@ static struct usb_device_id blacklist_table[] = {
|
||||
/* Atheros AR5BBU12 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
|
||||
|
||||
/* Atheros AR5BBU12 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xe03c), .driver_info = BTUSB_ATH3012 },
|
||||
|
||||
/* Broadcom BCM2035 */
|
||||
{ USB_DEVICE(0x0a5c, 0x2035), .driver_info = BTUSB_WRONG_SCO_MTU },
|
||||
{ USB_DEVICE(0x0a5c, 0x200a), .driver_info = BTUSB_WRONG_SCO_MTU },
|
||||
@@ -855,6 +858,7 @@ static void btusb_work(struct work_struct *work)
|
||||
{
|
||||
struct btusb_data *data = container_of(work, struct btusb_data, work);
|
||||
struct hci_dev *hdev = data->hdev;
|
||||
int new_alts;
|
||||
int err;
|
||||
|
||||
if (hdev->conn_hash.sco_num > 0) {
|
||||
@@ -868,11 +872,19 @@ static void btusb_work(struct work_struct *work)
|
||||
|
||||
set_bit(BTUSB_DID_ISO_RESUME, &data->flags);
|
||||
}
|
||||
if (data->isoc_altsetting != 2) {
|
||||
|
||||
if (hdev->voice_setting & 0x0020) {
|
||||
static const int alts[3] = { 2, 4, 5 };
|
||||
new_alts = alts[hdev->conn_hash.sco_num - 1];
|
||||
} else {
|
||||
new_alts = hdev->conn_hash.sco_num;
|
||||
}
|
||||
|
||||
if (data->isoc_altsetting != new_alts) {
|
||||
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
|
||||
usb_kill_anchored_urbs(&data->isoc_anchor);
|
||||
|
||||
if (__set_isoc_interface(hdev, 2) < 0)
|
||||
if (__set_isoc_interface(hdev, new_alts) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1218,6 +1230,7 @@ static struct usb_driver btusb_driver = {
|
||||
#endif
|
||||
.id_table = btusb_table,
|
||||
.supports_autosuspend = 1,
|
||||
.disable_hub_initiated_lpm = 1,
|
||||
};
|
||||
|
||||
module_usb_driver(btusb_driver);
|
||||
|
||||
@@ -388,7 +388,7 @@ static int hci_uart_register_dev(struct hci_uart *hu)
|
||||
hdev->close = hci_uart_close;
|
||||
hdev->flush = hci_uart_flush;
|
||||
hdev->send = hci_uart_send_frame;
|
||||
hdev->parent = hu->tty->dev;
|
||||
SET_HCIDEV_DEV(hdev, hu->tty->dev);
|
||||
|
||||
if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags))
|
||||
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
||||
|
||||
@@ -252,8 +252,9 @@ static int vhci_open(struct inode *inode, struct file *file)
|
||||
}
|
||||
|
||||
file->private_data = data;
|
||||
nonseekable_open(inode, file);
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vhci_release(struct inode *inode, struct file *file)
|
||||
|
||||
@@ -585,14 +585,6 @@ config DEVPORT
|
||||
|
||||
source "drivers/s390/char/Kconfig"
|
||||
|
||||
config RAMOOPS
|
||||
tristate "Log panic/oops to a RAM buffer"
|
||||
depends on HAS_IOMEM
|
||||
default n
|
||||
help
|
||||
This enables panic and oops messages to be logged to a circular
|
||||
buffer in RAM where it can be read back at some later point.
|
||||
|
||||
config MSM_SMD_PKT
|
||||
bool "Enable device interface for some SMD packet ports"
|
||||
default n
|
||||
|
||||
@@ -58,7 +58,6 @@ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o
|
||||
obj-$(CONFIG_TCG_TPM) += tpm/
|
||||
|
||||
obj-$(CONFIG_PS3_FLASH) += ps3flash.o
|
||||
obj-$(CONFIG_RAMOOPS) += ramoops.o
|
||||
|
||||
obj-$(CONFIG_JS_RTC) += js-rtc.o
|
||||
js-rtc-y = rtc.o
|
||||
|
||||
@@ -958,7 +958,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
|
||||
if (set_memory_uc((unsigned long)table, 1 << page_order))
|
||||
printk(KERN_WARNING "Could not set GATT table memory to UC!\n");
|
||||
|
||||
bridge->gatt_table = (void *)table;
|
||||
bridge->gatt_table = (u32 __iomem *)table;
|
||||
#else
|
||||
bridge->gatt_table = ioremap_nocache(virt_to_phys(table),
|
||||
(PAGE_SIZE * (1 << page_order)));
|
||||
@@ -1010,7 +1010,6 @@ int agp_generic_free_gatt_table(struct agp_bridge_data *bridge)
|
||||
case LVL2_APER_SIZE:
|
||||
/* The generic routines can't deal with 2 level gatt's */
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
page_order = 0;
|
||||
break;
|
||||
@@ -1077,7 +1076,6 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type)
|
||||
case LVL2_APER_SIZE:
|
||||
/* The generic routines can't deal with 2 level gatt's */
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
num_entries = 0;
|
||||
break;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user