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:
Len Brown
2012-06-30 00:53:50 -04:00
8980 changed files with 532780 additions and 325999 deletions
+8
View File
@@ -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
View File
@@ -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/
+1 -1
View File
@@ -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
+15 -2
View File
@@ -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)
{
+9
View File
@@ -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);
+3 -3
View File
@@ -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 {
+9 -1
View File
@@ -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);
}
+1
View File
@@ -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
View File
@@ -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;
+2
View File
@@ -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 {
+6 -6
View File
@@ -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);
+1 -1
View File
@@ -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;
+25 -5
View File
@@ -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);
+1
View File
@@ -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
View File
@@ -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.
+2 -2
View File
@@ -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);
+30
View File
@@ -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
View File
@@ -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)
+2 -2
View File
@@ -1,2 +1,2 @@
obj-y += bus.o
obj-$(CONFIG_ARM_AMBA) += bus.o
obj-$(CONFIG_TEGRA_AHB) += tegra-ahb.o
+51 -2
View File
@@ -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);
+293
View File
@@ -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);
+9
View File
@@ -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
+1
View File
@@ -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
+1 -1
View File
@@ -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;
+36
View File
@@ -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);
}
+7
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+1 -1
View File
@@ -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))
-5
View File
@@ -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;
}
+1 -1
View File
@@ -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) {
+3 -1
View File
@@ -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;
+89
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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);
-4
View File
@@ -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
}
+35
View File
@@ -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)
+3 -3
View File
@@ -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
View File
@@ -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);
+42
View File
@@ -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);
+401
View File
@@ -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;
}
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+127 -43
View File
@@ -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;
}
+10
View File
@@ -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)
+12 -7
View File
@@ -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);
+2 -101
View File
@@ -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);
}
+49 -5
View File
@@ -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
View File
@@ -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)
+4
View File
@@ -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
+1
View File
@@ -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
+21 -5
View File
@@ -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
+6 -5
View File
@@ -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;
+25 -19
View File
@@ -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);
+21 -13
View File
@@ -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);
+13 -5
View File
@@ -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)
+9 -6
View File
@@ -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, &regmap_i2c, config);
return regmap_init(&i2c->dev, &regmap_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, &regmap_i2c, config);
return devm_regmap_init(&i2c->dev, &regmap_i2c, &i2c->dev, config);
}
EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
+116 -68
View File
@@ -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, &regmap_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, &regmap_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,
&regmap_domain_ops, d);
else
d->domain = irq_domain_add_linear(map->dev->of_node,
chip->num_irqs,
&regmap_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);
+224
View File
@@ -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, &regmap_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, &regmap_mmio, ctx, config);
}
EXPORT_SYMBOL_GPL(devm_regmap_init_mmio);
MODULE_LICENSE("GPL v2");
+8 -5
View File
@@ -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, &regmap_spi, config);
return regmap_init(&spi->dev, &regmap_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, &regmap_spi, config);
return devm_regmap_init(&spi->dev, &regmap_spi, &spi->dev, config);
}
EXPORT_SYMBOL_GPL(devm_regmap_init_spi);
+231 -45
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
+51 -2
View File
@@ -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);
+5 -5
View File
@@ -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
+5 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
/*
+77 -25
View File
@@ -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;
}
+108 -40
View File
@@ -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
+55 -35
View File
@@ -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
View File
@@ -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;
+38 -10
View File
@@ -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;
}
+1 -1
View File
@@ -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);
+76 -25
View File
@@ -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);
}
+83 -49
View File
@@ -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);
}
+12 -7
View File
@@ -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)
+25 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+23 -21
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+25 -19
View File
@@ -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);
+7
View File
@@ -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);
+1
View File
@@ -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);
+1
View File
@@ -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);
+1
View File
@@ -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);
+3
View File
@@ -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);
+33 -23
View File
@@ -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) {
+105 -7
View File
@@ -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)
+15 -2
View File
@@ -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);
+1 -1
View File
@@ -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);
+2 -1
View File
@@ -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)
-8
View 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
-1
View File
@@ -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
+1 -3
View File
@@ -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