diff options
author | John Stultz <john.stultz@linaro.org> | 2011-08-11 12:01:11 -0700 |
---|---|---|
committer | John Stultz <john.stultz@linaro.org> | 2011-08-11 12:01:11 -0700 |
commit | 3ca5168c598bb5207dc70a3145c8093ffcdabfd6 (patch) | |
tree | b83515985abe1894607839afd211c2c25d1d790c | |
parent | e91e82388c2ed1739b082c2afd63ba62a312ec36 (diff) | |
parent | 9abd59b0df155835a970c2b9c8f93367eb793797 (diff) |
Merge branch 'upstream/android-3.0' into linaro-android-3.0
159 files changed, 2512 insertions, 3089 deletions
@@ -1,6 +1,6 @@ VERSION = 3 PATCHLEVEL = 0 -SUBLEVEL = 0 +SUBLEVEL = 1 EXTRAVERSION = NAME = Sneaky Weasel diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index 818e74ed45d..f20d1b5396b 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -91,7 +91,7 @@ DEFINE_PER_CPU(u8, irq_work_pending); #define test_irq_work_pending() __get_cpu_var(irq_work_pending) #define clear_irq_work_pending() __get_cpu_var(irq_work_pending) = 0 -void set_irq_work_pending(void) +void arch_irq_work_raise(void) { set_irq_work_pending_flag(); } diff --git a/arch/arm/mach-pxa/cm-x300.c b/arch/arm/mach-pxa/cm-x300.c index b2248e76ec8..8a034872ac7 100644 --- a/arch/arm/mach-pxa/cm-x300.c +++ b/arch/arm/mach-pxa/cm-x300.c @@ -161,10 +161,10 @@ static mfp_cfg_t cm_x3xx_mfp_cfg[] __initdata = { GPIO99_GPIO, /* Ethernet IRQ */ /* RTC GPIOs */ - GPIO95_GPIO, /* RTC CS */ - GPIO96_GPIO, /* RTC WR */ - GPIO97_GPIO, /* RTC RD */ - GPIO98_GPIO, /* RTC IO */ + GPIO95_GPIO | MFP_LPM_DRIVE_HIGH, /* RTC CS */ + GPIO96_GPIO | MFP_LPM_DRIVE_HIGH, /* RTC WR */ + GPIO97_GPIO | MFP_LPM_DRIVE_HIGH, /* RTC RD */ + GPIO98_GPIO, /* RTC IO */ /* Standard I2C */ GPIO21_I2C_SCL, diff --git a/arch/powerpc/kernel/crash.c b/arch/powerpc/kernel/crash.c index 4e6ee944495..cc6a9d5d69a 100644 --- a/arch/powerpc/kernel/crash.c +++ b/arch/powerpc/kernel/crash.c @@ -242,12 +242,8 @@ static void crash_kexec_wait_realmode(int cpu) while (paca[i].kexec_state < KEXEC_STATE_REAL_MODE) { barrier(); - if (!cpu_possible(i)) { + if (!cpu_possible(i) || !cpu_online(i) || (msecs <= 0)) break; - } - if (!cpu_online(i)) { - break; - } msecs--; mdelay(1); } diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index f33acfd872a..03b29a6759a 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -544,7 +544,7 @@ DEFINE_PER_CPU(u8, irq_work_pending); #endif /* 32 vs 64 bit */ -void set_irq_work_pending(void) +void arch_irq_work_raise(void) { preempt_disable(); set_irq_work_pending_flag(); diff --git a/arch/powerpc/platforms/pseries/hvconsole.c b/arch/powerpc/platforms/pseries/hvconsole.c index 3f6a89b0981..041e87ca189 100644 --- a/arch/powerpc/platforms/pseries/hvconsole.c +++ b/arch/powerpc/platforms/pseries/hvconsole.c @@ -73,7 +73,7 @@ int hvc_put_chars(uint32_t vtermno, const char *buf, int count) if (ret == H_SUCCESS) return count; if (ret == H_BUSY) - return 0; + return -EAGAIN; return -EIO; } diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 485b4f1f079..23a9d898baa 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -259,6 +259,9 @@ #define MSR_IA32_TEMPERATURE_TARGET 0x000001a2 #define MSR_IA32_ENERGY_PERF_BIAS 0x000001b0 +#define ENERGY_PERF_BIAS_PERFORMANCE 0 +#define ENERGY_PERF_BIAS_NORMAL 6 +#define ENERGY_PERF_BIAS_POWERSWAVE 15 #define MSR_IA32_PACKAGE_THERM_STATUS 0x000001b1 diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index 1edf5ba4fb2..da0d779ecd9 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -456,6 +456,24 @@ static void __cpuinit init_intel(struct cpuinfo_x86 *c) if (cpu_has(c, X86_FEATURE_VMX)) detect_vmx_virtcap(c); + + /* + * Initialize MSR_IA32_ENERGY_PERF_BIAS if BIOS did not. + * x86_energy_perf_policy(8) is available to change it at run-time + */ + if (cpu_has(c, X86_FEATURE_EPB)) { + u64 epb; + + rdmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb); + if ((epb & 0xF) == 0) { + printk_once(KERN_WARNING, "x86: updated energy_perf_bias" + " to 'normal' from 'performance'\n" + "You can view and update epb via utility," + " such as x86_energy_perf_policy(8)\n"); + epb = (epb & ~0xF) | ENERGY_PERF_BIAS_NORMAL; + wrmsrl(MSR_IA32_ENERGY_PERF_BIAS, epb); + } + } } #ifdef CONFIG_X86_32 diff --git a/arch/x86/kernel/relocate_kernel_32.S b/arch/x86/kernel/relocate_kernel_32.S index 41235531b11..36818f8ec2b 100644 --- a/arch/x86/kernel/relocate_kernel_32.S +++ b/arch/x86/kernel/relocate_kernel_32.S @@ -97,6 +97,8 @@ relocate_kernel: ret identity_mapped: + /* set return address to 0 if not preserving context */ + pushl $0 /* store the start address on the stack */ pushl %edx diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S index 4de8f5b3d47..7a6f3b3be3c 100644 --- a/arch/x86/kernel/relocate_kernel_64.S +++ b/arch/x86/kernel/relocate_kernel_64.S @@ -100,6 +100,8 @@ relocate_kernel: ret identity_mapped: + /* set return address to 0 if not preserving context */ + pushq $0 /* store the start address on the stack */ pushq %rdx diff --git a/arch/x86/oprofile/backtrace.c b/arch/x86/oprofile/backtrace.c index a5b64ab4cd6..32f78eb4674 100644 --- a/arch/x86/oprofile/backtrace.c +++ b/arch/x86/oprofile/backtrace.c @@ -11,10 +11,12 @@ #include <linux/oprofile.h> #include <linux/sched.h> #include <linux/mm.h> +#include <linux/compat.h> +#include <linux/highmem.h> + #include <asm/ptrace.h> #include <asm/uaccess.h> #include <asm/stacktrace.h> -#include <linux/compat.h> static int backtrace_stack(void *data, char *name) { @@ -36,17 +38,53 @@ static struct stacktrace_ops backtrace_ops = { .walk_stack = print_context_stack, }; +/* from arch/x86/kernel/cpu/perf_event.c: */ + +/* + * best effort, GUP based copy_from_user() that assumes IRQ or NMI context + */ +static unsigned long +copy_from_user_nmi(void *to, const void __user *from, unsigned long n) +{ + unsigned long offset, addr = (unsigned long)from; + unsigned long size, len = 0; + struct page *page; + void *map; + int ret; + + do { + ret = __get_user_pages_fast(addr, 1, 0, &page); + if (!ret) + break; + + offset = addr & (PAGE_SIZE - 1); + size = min(PAGE_SIZE - offset, n - len); + + map = kmap_atomic(page); + memcpy(to, map+offset, size); + kunmap_atomic(map); + put_page(page); + + len += size; + to += size; + addr += size; + + } while (len < n); + + return len; +} + #ifdef CONFIG_COMPAT static struct stack_frame_ia32 * dump_user_backtrace_32(struct stack_frame_ia32 *head) { + /* Also check accessibility of one struct frame_head beyond: */ struct stack_frame_ia32 bufhead[2]; struct stack_frame_ia32 *fp; + unsigned long bytes; - /* Also check accessibility of one struct frame_head beyond */ - if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) - return NULL; - if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) + bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); + if (bytes != sizeof(bufhead)) return NULL; fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame); @@ -87,12 +125,12 @@ x86_backtrace_32(struct pt_regs * const regs, unsigned int depth) static struct stack_frame *dump_user_backtrace(struct stack_frame *head) { + /* Also check accessibility of one struct frame_head beyond: */ struct stack_frame bufhead[2]; + unsigned long bytes; - /* Also check accessibility of one struct stack_frame beyond */ - if (!access_ok(VERIFY_READ, head, sizeof(bufhead))) - return NULL; - if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead))) + bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead)); + if (bytes != sizeof(bufhead)) return NULL; oprofile_add_trace(bufhead[0].return_address); diff --git a/arch/xtensa/kernel/ptrace.c b/arch/xtensa/kernel/ptrace.c index c72c9473ef9..a0d042aa296 100644 --- a/arch/xtensa/kernel/ptrace.c +++ b/arch/xtensa/kernel/ptrace.c @@ -147,6 +147,9 @@ int ptrace_setxregs(struct task_struct *child, void __user *uregs) elf_xtregs_t *xtregs = uregs; int ret = 0; + if (!access_ok(VERIFY_READ, uregs, sizeof(elf_xtregs_t))) + return -EFAULT; + #if XTENSA_HAVE_COPROCESSORS /* Flush all coprocessors before we overwrite them. */ coprocessor_flush_all(ti); diff --git a/block/blk-core.c b/block/blk-core.c index d2f8f4049ab..1d49e1c7c90 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -839,6 +839,9 @@ struct request *blk_get_request(struct request_queue *q, int rw, gfp_t gfp_mask) { struct request *rq; + if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) + return NULL; + BUG_ON(rw != READ && rw != WRITE); spin_lock_irq(q->queue_lock); diff --git a/block/blk-exec.c b/block/blk-exec.c index 8a0e7ec056e..a1ebceb332f 100644 --- a/block/blk-exec.c +++ b/block/blk-exec.c @@ -50,6 +50,13 @@ void blk_execute_rq_nowait(struct request_queue *q, struct gendisk *bd_disk, { int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK; + if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags))) { + rq->errors = -ENXIO; + if (rq->end_io) + rq->end_io(rq, rq->errors); + return; + } + rq->rq_disk = bd_disk; rq->end_io = done; WARN_ON(irqs_disabled()); diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h index 16b4d58d84d..c049548e68b 100644 --- a/drivers/block/cciss.h +++ b/drivers/block/cciss.h @@ -223,7 +223,7 @@ static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c) h->ctlr, c->busaddr); #endif /* CCISS_DEBUG */ writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET); - readl(h->vaddr + SA5_REQUEST_PORT_OFFSET); + readl(h->vaddr + SA5_SCRATCHPAD_OFFSET); h->commands_outstanding++; if ( h->commands_outstanding > h->max_outstanding) h->max_outstanding = h->commands_outstanding; diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c index b1c11775839..e6ad3bb6c1a 100644 --- a/drivers/firewire/core-cdev.c +++ b/drivers/firewire/core-cdev.c @@ -253,14 +253,11 @@ static int fw_device_op_open(struct inode *inode, struct file *file) init_waitqueue_head(&client->wait); init_waitqueue_head(&client->tx_flush_wait); INIT_LIST_HEAD(&client->phy_receiver_link); + INIT_LIST_HEAD(&client->link); kref_init(&client->kref); file->private_data = client; - mutex_lock(&device->client_list_mutex); - list_add_tail(&client->link, &device->client_list); - mutex_unlock(&device->client_list_mutex); - return nonseekable_open(inode, file); } @@ -451,15 +448,20 @@ static int ioctl_get_info(struct client *client, union ioctl_arg *arg) if (ret != 0) return -EFAULT; + mutex_lock(&client->device->client_list_mutex); + client->bus_reset_closure = a->bus_reset_closure; if (a->bus_reset != 0) { fill_bus_reset_event(&bus_reset, client); - if (copy_to_user(u64_to_uptr(a->bus_reset), - &bus_reset, sizeof(bus_reset))) - return -EFAULT; + ret = copy_to_user(u64_to_uptr(a->bus_reset), + &bus_reset, sizeof(bus_reset)); } + if (ret == 0 && list_empty(&client->link)) + list_add_tail(&client->link, &client->device->client_list); - return 0; + mutex_unlock(&client->device->client_list_mutex); + + return ret ? -EFAULT : 0; } static int add_client_resource(struct client *client, @@ -1583,7 +1585,7 @@ static int dispatch_ioctl(struct client *client, if (_IOC_TYPE(cmd) != '#' || _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers) || _IOC_SIZE(cmd) > sizeof(buffer)) - return -EINVAL; + return -ENOTTY; if (_IOC_DIR(cmd) == _IOC_READ) memset(&buffer, 0, _IOC_SIZE(cmd)); diff --git a/drivers/firmware/sigma.c b/drivers/firmware/sigma.c index c19cd2c39fa..f10fc521951 100644 --- a/drivers/firmware/sigma.c +++ b/drivers/firmware/sigma.c @@ -11,6 +11,7 @@ #include <linux/firmware.h> #include <linux/kernel.h> #include <linux/i2c.h> +#include <linux/module.h> #include <linux/sigma.h> /* Return: 0==OK, <0==error, =1 ==no more actions */ @@ -113,3 +114,5 @@ int process_sigma_firmware(struct i2c_client *client, const char *name) return ret; } EXPORT_SYMBOL(process_sigma_firmware); + +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 8c0f9e36ff8..645b84b3d20 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -627,6 +627,7 @@ struct radeon_dp_link_train_info { u8 train_set[4]; u8 link_status[DP_LINK_STATUS_SIZE]; u8 tries; + bool use_dpencoder; }; static void radeon_dp_update_vs_emph(struct radeon_dp_link_train_info *dp_info) @@ -646,7 +647,7 @@ static void radeon_dp_set_tp(struct radeon_dp_link_train_info *dp_info, int tp) int rtp = 0; /* set training pattern on the source */ - if (ASIC_IS_DCE4(dp_info->rdev)) { + if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) { switch (tp) { case DP_TRAINING_PATTERN_1: rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1; @@ -706,7 +707,7 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info) radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LINK_BW_SET, tmp); /* start training on the source */ - if (ASIC_IS_DCE4(dp_info->rdev)) + if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) atombios_dig_encoder_setup(dp_info->encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0); else @@ -731,7 +732,7 @@ static int radeon_dp_link_train_finish(struct radeon_dp_link_train_info *dp_info DP_TRAINING_PATTERN_DISABLE); /* disable the training pattern on the source */ - if (ASIC_IS_DCE4(dp_info->rdev)) + if (ASIC_IS_DCE4(dp_info->rdev) || !dp_info->use_dpencoder) atombios_dig_encoder_setup(dp_info->encoder, ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0); else @@ -869,7 +870,8 @@ void radeon_dp_link_train(struct drm_encoder *encoder, struct radeon_connector *radeon_connector; struct radeon_connector_atom_dig *dig_connector; struct radeon_dp_link_train_info dp_info; - u8 tmp; + int index; + u8 tmp, frev, crev; if (!radeon_encoder->enc_priv) return; @@ -884,6 +886,18 @@ void radeon_dp_link_train(struct drm_encoder *encoder, (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_eDP)) return; + /* DPEncoderService newer than 1.1 can't program properly the + * training pattern. When facing such version use the + * DIGXEncoderControl (X== 1 | 2) + */ + dp_info.use_dpencoder = true; + index = GetIndexIntoMasterTable(COMMAND, DPEncoderService); + if (atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) { + if (crev > 1) { + dp_info.use_dpencoder = false; + } + } + dp_info.enc_id = 0; if (dig->dig_encoder) dp_info.enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER; diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index e4594676a07..a74217cd192 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -779,7 +779,8 @@ void radeon_combios_i2c_init(struct radeon_device *rdev) } } } - } else if (rdev->family >= CHIP_R200) { + } else if ((rdev->family == CHIP_R200) || + (rdev->family >= CHIP_R300)) { /* 0x68 */ i2c = combios_setup_i2c_bus(rdev, DDC_MONID, 0, 0); rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID"); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index aaa19dc418a..6fabe89fa6a 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -594,6 +594,9 @@ int radeon_pm_init(struct radeon_device *rdev) if (rdev->pm.default_vddc) radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, SET_VOLTAGE_TYPE_ASIC_VDDC); + if (rdev->pm.default_vddci) + radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, + SET_VOLTAGE_TYPE_ASIC_VDDCI); if (rdev->pm.default_sclk) radeon_set_engine_clock(rdev, rdev->pm.default_sclk); if (rdev->pm.default_mclk) diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index 9cb5b25bb11..37b23af0550 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -131,7 +131,7 @@ static void ion_buffer_add(struct ion_device *dev, } /* this function should only be called while dev->lock is held */ -struct ion_buffer *ion_buffer_create(struct ion_heap *heap, +static struct ion_buffer *ion_buffer_create(struct ion_heap *heap, struct ion_device *dev, unsigned long len, unsigned long align, @@ -181,7 +181,7 @@ static int ion_buffer_put(struct ion_buffer *buffer) return kref_put(&buffer->ref, ion_buffer_destroy); } -struct ion_handle *ion_handle_create(struct ion_client *client, +static struct ion_handle *ion_handle_create(struct ion_client *client, struct ion_buffer *buffer) { struct ion_handle *handle; @@ -190,6 +190,7 @@ struct ion_handle *ion_handle_create(struct ion_client *client, if (!handle) return ERR_PTR(-ENOMEM); kref_init(&handle->ref); + rb_init_node(&handle->node); handle->client = client; ion_buffer_get(buffer); handle->buffer = buffer; @@ -205,7 +206,8 @@ static void ion_handle_destroy(struct kref *kref) */ ion_buffer_put(handle->buffer); mutex_lock(&handle->client->lock); - rb_erase(&handle->node, &handle->client->handles); + if (!RB_EMPTY_NODE(&handle->node)) + rb_erase(&handle->node, &handle->client->handles); mutex_unlock(&handle->client->lock); kfree(handle); } @@ -239,7 +241,7 @@ static struct ion_handle *ion_handle_lookup(struct ion_client *client, return NULL; } -bool ion_handle_validate(struct ion_client *client, struct ion_handle *handle) +static bool ion_handle_validate(struct ion_client *client, struct ion_handle *handle) { struct rb_node *n = client->handles.rb_node; @@ -351,7 +353,7 @@ void ion_free(struct ion_client *client, struct ion_handle *handle) static void ion_client_get(struct ion_client *client); static int ion_client_put(struct ion_client *client); -bool _ion_map(int *buffer_cnt, int *handle_cnt) +static bool _ion_map(int *buffer_cnt, int *handle_cnt) { bool map; @@ -367,7 +369,7 @@ bool _ion_map(int *buffer_cnt, int *handle_cnt) return map; } -bool _ion_unmap(int *buffer_cnt, int *handle_cnt) +static bool _ion_unmap(int *buffer_cnt, int *handle_cnt) { BUG_ON(*handle_cnt == 0); (*handle_cnt)--; @@ -522,7 +524,7 @@ struct ion_buffer *ion_share(struct ion_client *client, return ERR_PTR(-EINVAL); } - /* don't not take an extra refernce here, the burden is on the caller + /* do not take an extra reference here, the burden is on the caller * to make sure the buffer doesn't go away while it's passing it * to another client -- ion_free should not be called on this handle * until the buffer has been imported into the other client @@ -897,7 +899,7 @@ err1: /* drop the reference to the handle */ ion_handle_put(handle); err: - /* drop the refernce to the client */ + /* drop the reference to the client */ ion_client_put(client); return ret; } diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index ee165fdcb59..7d5109bbd1a 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -2127,6 +2127,8 @@ static ssize_t srp_create_target(struct device *dev, return -ENOMEM; target_host->transportt = ib_srp_transport_template; + target_host->max_channel = 0; + target_host->max_id = 1; target_host->max_lun = SRP_MAX_LUN; target_host->max_cmd_len = sizeof ((struct srp_cmd *) (void *) 0L)->cdb; diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 2067288f61f..ad2eba40e31 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -38,6 +38,8 @@ struct io { struct dm_io_client *client; io_notify_fn callback; void *context; + void *vma_invalidate_address; + unsigned long vma_invalidate_size; } __attribute__((aligned(DM_IO_MAX_REGIONS))); static struct kmem_cache *_dm_io_cache; @@ -116,6 +118,10 @@ static void dec_count(struct io *io, unsigned int region, int error) set_bit(region, &io->error_bits); if (atomic_dec_and_test(&io->count)) { + if (io->vma_invalidate_size) + invalidate_kernel_vmap_range(io->vma_invalidate_address, + io->vma_invalidate_size); + if (io->sleeper) wake_up_process(io->sleeper); @@ -159,6 +165,9 @@ struct dpages { unsigned context_u; void *context_ptr; + + void *vma_invalidate_address; + unsigned long vma_invalidate_size; }; /* @@ -377,6 +386,9 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions, io->sleeper = current; io->client = client; + io->vma_invalidate_address = dp->vma_invalidate_address; + io->vma_invalidate_size = dp->vma_invalidate_size; + dispatch_io(rw, num_regions, where, dp, io, 1); while (1) { @@ -415,13 +427,21 @@ static int async_io(struct dm_io_client *client, unsigned int num_regions, io->callback = fn; io->context = context; + io->vma_invalidate_address = dp->vma_invalidate_address; + io->vma_invalidate_size = dp->vma_invalidate_size; + dispatch_io(rw, num_regions, where, dp, io, 0); return 0; } -static int dp_init(struct dm_io_request *io_req, struct dpages *dp) +static int dp_init(struct dm_io_request *io_req, struct dpages *dp, + unsigned long size) { /* Set up dpages based on memory type */ + + dp->vma_invalidate_address = NULL; + dp->vma_invalidate_size = 0; + switch (io_req->mem.type) { case DM_IO_PAGE_LIST: list_dp_init(dp, io_req->mem.ptr.pl, io_req->mem.offset); @@ -432,6 +452,11 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp) break; case DM_IO_VMA: + flush_kernel_vmap_range(io_req->mem.ptr.vma, size); + if ((io_req->bi_rw & RW_MASK) == READ) { + dp->vma_invalidate_address = io_req->mem.ptr.vma; + dp->vma_invalidate_size = size; + } vm_dp_init(dp, io_req->mem.ptr.vma); break; @@ -460,7 +485,7 @@ int dm_io(struct dm_io_request *io_req, unsigned num_regions, int r; struct dpages dp; - r = dp_init(io_req, &dp); + r = dp_init(io_req, &dp, (unsigned long)where->count << SECTOR_SHIFT); if (r) return r; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index aa4e570c2cb..209991bebd3 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -807,6 +807,11 @@ static int parse_features(struct arg_set *as, struct multipath *m) if (!argc) return 0; + if (argc > as->argc) { + ti->error = "not enough arguments for features"; + return -EINVAL; + } + do { param_name = shift(as); argc--; diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c index 135c2f1fdbf..e4ecadf0548 100644 --- a/drivers/md/dm-snap-persistent.c +++ b/drivers/md/dm-snap-persistent.c @@ -753,7 +753,7 @@ static int persistent_commit_merge(struct dm_exception_store *store, for (i = 0; i < nr_merged; i++) clear_exception(ps, ps->current_committed - 1 - i); - r = area_io(ps, WRITE); + r = area_io(ps, WRITE_FLUSH_FUA); if (r < 0) return r; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 0cf68b47887..41abc6dd481 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -37,6 +37,8 @@ static const char *_name = DM_NAME; static unsigned int major = 0; static unsigned int _major = 0; +static DEFINE_IDR(_minor_idr); + static DEFINE_SPINLOCK(_minor_lock); /* * For bio-based dm. @@ -313,6 +315,12 @@ static void __exit dm_exit(void) while (i--) _exits[i](); + + /* + * Should be empty by this point. + */ + idr_remove_all(&_minor_idr); + idr_destroy(&_minor_idr); } /* @@ -1705,8 +1713,6 @@ static int dm_any_congested(void *congested_data, int bdi_bits) /*----------------------------------------------------------------- * An IDR is used to keep track of allocated minor numbers. *---------------------------------------------------------------*/ -static DEFINE_IDR(_minor_idr); - static void free_minor(int minor) { spin_lock(&_minor_lock); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b575799d7d0..68f367184ab 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -256,8 +256,7 @@ config SGI_XP config CS5535_MFGPT tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support" - depends on PCI - depends on X86 + depends on PCI && X86 && MFD_CS5535 default n help This driver provides access to MFGPT functionality for other diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index a19967d0bfc..ba31abee948 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -74,7 +74,7 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) if (boarddata && gpio_is_valid(boarddata->cd_gpio) && gpio_get_value(boarddata->cd_gpio)) /* no card, if a valid gpio says so... */ - val &= SDHCI_CARD_PRESENT; + val &= ~SDHCI_CARD_PRESENT; else /* ... in all other cases assume card is present */ val |= SDHCI_CARD_PRESENT; diff --git a/drivers/net/jme.c b/drivers/net/jme.c index b5b174a8c14..19738143aa9 100644 --- a/drivers/net/jme.c +++ b/drivers/net/jme.c @@ -753,20 +753,28 @@ jme_make_new_rx_buf(struct jme_adapter *jme, int i) struct jme_ring *rxring = &(jme->rxring[0]); struct jme_buffer_info *rxbi = rxring->bufinf + i; struct sk_buff *skb; + dma_addr_t mapping; skb = netdev_alloc_skb(jme->dev, jme->dev->mtu + RX_EXTRA_LEN); if (unlikely(!skb)) return -ENOMEM; + mapping = pci_map_page(jme->pdev, virt_to_page(skb->data), + offset_in_page(skb->data), skb_tailroom(skb), + PCI_DMA_FROMDEVICE); + if (unlikely(pci_dma_mapping_error(jme->pdev, mapping))) { + dev_kfree_skb(skb); + return -ENOMEM; + } + + if (likely(rxbi->mapping)) + pci_unmap_page(jme->pdev, rxbi->mapping, + rxbi->len, PCI_DMA_FROMDEVICE); + rxbi->skb = skb; rxbi->len = skb_tailroom(skb); - rxbi->mapping = pci_map_page(jme->pdev, - virt_to_page(skb->data), - offset_in_page(skb->data), - rxbi->len, - PCI_DMA_FROMDEVICE); - + rxbi->mapping = mapping; return 0; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 10d71f7d3fc..1f992497186 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -629,8 +629,7 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, rxs->rs_status |= ATH9K_RXERR_DECRYPT; else if (rxsp->status11 & AR_MichaelErr) rxs->rs_status |= ATH9K_RXERR_MIC; - - if (rxsp->status11 & AR_KeyMiss) + else if (rxsp->status11 & AR_KeyMiss) rxs->rs_status |= ATH9K_RXERR_DECRYPT; } diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index c2091f1f409..b6b523a897e 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -645,8 +645,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, rs->rs_status |= ATH9K_RXERR_DECRYPT; else if (ads.ds_rxstatus8 & AR_MichaelErr) rs->rs_status |= ATH9K_RXERR_MIC; - - if (ads.ds_rxstatus8 & AR_KeyMiss) + else if (ads.ds_rxstatus8 & AR_KeyMiss) rs->rs_status |= ATH9K_RXERR_DECRYPT; } diff --git a/drivers/net/wireless/bcm4329/bcmspibrcm.c b/drivers/net/wireless/bcm4329/bcmspibrcm.c deleted file mode 100644 index 0f131a40f4b..00000000000 --- a/drivers/net/wireless/bcm4329/bcmspibrcm.c +++ /dev/null @@ -1,1726 +0,0 @@ -/* - * Broadcom BCMSDH to gSPI Protocol Conversion Layer - * - * Copyright (C) 2010, Broadcom Corporation - * All Rights Reserved. - * - * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation; - * the contents of this file may not be disclosed to third parties, copied - * or duplicated in any form, in whole or in part, without the prior - * written permission of Broadcom Corporation. - * - * $Id: bcmspibrcm.c,v 1.11.2.10.2.9.6.11 2009/05/21 13:21:57 Exp $ - */ - -#define HSMODE - -#include <typedefs.h> - -#include <bcmdevs.h> -#include <bcmendian.h> -#include <bcmutils.h> -#include <osl.h> -#include <hndsoc.h> -#include <siutils.h> -#include <sbchipc.h> -#include <sbsdio.h> -#include <spid.h> - -#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */ -#include <sdiovar.h> /* ioctl/iovars */ -#include <sdio.h> - -#include <pcicfg.h> - - -#include <bcmspibrcm.h> -#include <bcmspi.h> - -#define F0_RESPONSE_DELAY 16 -#define F1_RESPONSE_DELAY 16 -#define F2_RESPONSE_DELAY F0_RESPONSE_DELAY - -#define CMDLEN 4 - -#define DWORDMODE_ON (sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 2) && (sd->dwordmode == TRUE) - -/* Globals */ -uint sd_msglevel = 0; - -uint sd_hiok = FALSE; /* Use hi-speed mode if available? */ -uint sd_sdmode = SDIOH_MODE_SPI; /* Use SD4 mode by default */ -uint sd_f2_blocksize = 64; /* Default blocksize */ - - -uint sd_divisor = 2; -uint sd_power = 1; /* Default to SD Slot powered ON */ -uint sd_clock = 1; /* Default to SD Clock turned ON */ -uint sd_crc = 0; /* Default to SPI CRC Check turned OFF */ - -uint8 spi_outbuf[SPI_MAX_PKT_LEN]; -uint8 spi_inbuf[SPI_MAX_PKT_LEN]; - -/* 128bytes buffer is enough to clear data-not-available and program response-delay F0 bits - * assuming we will not exceed F0 response delay > 100 bytes at 48MHz. - */ -#define BUF2_PKT_LEN 128 -uint8 spi_outbuf2[BUF2_PKT_LEN]; -uint8 spi_inbuf2[BUF2_PKT_LEN]; - -/* Prototypes */ -static bool bcmspi_test_card(sdioh_info_t *sd); -static bool bcmspi_host_device_init_adapt(sdioh_info_t *sd); -static int bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode); -static int bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg, - uint32 *data, uint32 datalen); -static int bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, - int regsize, uint32 *data); -static int bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, - int regsize, uint32 data); -static int bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr, - uint8 *data); -static int bcmspi_driver_init(sdioh_info_t *sd); -static int bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, - uint32 addr, int nbytes, uint32 *data); -static int bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, - uint32 *data); -static void bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer); -static int bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg); - -/* - * Public entry points & extern's - */ -extern sdioh_info_t * -sdioh_attach(osl_t *osh, void *bar0, uint irq) -{ - sdioh_info_t *sd; - - sd_trace(("%s\n", __FUNCTION__)); - if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) { - sd_err(("%s: out of memory, malloced %d bytes\n", __FUNCTION__, MALLOCED(osh))); - return NULL; - } - bzero((char *)sd, sizeof(sdioh_info_t)); - sd->osh = osh; - if (spi_osinit(sd) != 0) { - sd_err(("%s: spi_osinit() failed\n", __FUNCTION__)); - MFREE(sd->osh, sd, sizeof(sdioh_info_t)); - return NULL; - } - - sd->bar0 = bar0; - sd->irq = irq; - sd->intr_handler = NULL; - sd->intr_handler_arg = NULL; - sd->intr_handler_valid = FALSE; - - /* Set defaults */ - sd->use_client_ints = TRUE; - sd->sd_use_dma = FALSE; /* DMA Not supported */ - - /* Spi device default is 16bit mode, change to 4 when device is changed to 32bit - * mode - */ - sd->wordlen = 2; - - if (!spi_hw_attach(sd)) { - sd_err(("%s: spi_hw_attach() failed\n", __FUNCTION__)); - spi_osfree(sd); - MFREE(sd->osh, sd, sizeof(sdioh_info_t)); - return (NULL); - } - - if (bcmspi_driver_init(sd) != SUCCESS) { - sd_err(("%s: bcmspi_driver_init() failed()\n", __FUNCTION__)); - spi_hw_detach(sd); - spi_osfree(sd); - MFREE(sd->osh, sd, sizeof(sdioh_info_t)); - return (NULL); - } - - if (spi_register_irq(sd, irq) != SUCCESS) { - sd_err(("%s: spi_register_irq() failed for irq = %d\n", __FUNCTION__, irq)); - spi_hw_detach(sd); - spi_osfree(sd); - MFREE(sd->osh, sd, sizeof(sdioh_info_t)); - return (NULL); - } - - sd_trace(("%s: Done\n", __FUNCTION__)); - - return sd; -} - -extern SDIOH_API_RC -sdioh_detach(osl_t *osh, sdioh_info_t *sd) -{ - sd_trace(("%s\n", __FUNCTION__)); - if (sd) { - sd_err(("%s: detaching from hardware\n", __FUNCTION__)); - spi_free_irq(sd->irq, sd); - spi_hw_detach(sd); - spi_osfree(sd); - MFREE(sd->osh, sd, sizeof(sdioh_info_t)); - } - return SDIOH_API_RC_SUCCESS; -} - -/* Configure callback to client when we recieve client interrupt */ -extern SDIOH_API_RC -sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh) -{ - sd_trace(("%s: Entering\n", __FUNCTION__)); - sd->intr_handler = fn; - sd->intr_handler_arg = argh; - sd->intr_handler_valid = TRUE; - return SDIOH_API_RC_SUCCESS; -} - -extern SDIOH_API_RC -sdioh_interrupt_deregister(sdioh_info_t *sd) -{ - sd_trace(("%s: Entering\n", __FUNCTION__)); - sd->intr_handler_valid = FALSE; - sd->intr_handler = NULL; - sd->intr_handler_arg = NULL; - return SDIOH_API_RC_SUCCESS; -} - -extern SDIOH_API_RC -sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff) -{ - sd_trace(("%s: Entering\n", __FUNCTION__)); - *onoff = sd->client_intr_enabled; - return SDIOH_API_RC_SUCCESS; -} - -#if defined(DHD_DEBUG) -extern bool -sdioh_interrupt_pending(sdioh_info_t *sd) -{ - return 0; -} -#endif - -extern SDIOH_API_RC -sdioh_query_device(sdioh_info_t *sd) -{ - /* Return a BRCM ID appropriate to the dongle class */ - return (sd->num_funcs > 1) ? BCM4329_D11NDUAL_ID : BCM4318_D11G_ID; -} - -/* Provide dstatus bits of spi-transaction for dhd layers. */ -extern uint32 -sdioh_get_dstatus(sdioh_info_t *sd) -{ - return sd->card_dstatus; -} - -extern void -sdioh_chipinfo(sdioh_info_t *sd, uint32 chip, uint32 chiprev) -{ - sd->chip = chip; - sd->chiprev = chiprev; -} - -extern void -sdioh_dwordmode(sdioh_info_t *sd, bool set) -{ - uint8 reg = 0; - int status; - - if ((status = sdioh_request_byte(sd, SDIOH_READ, SPI_FUNC_0, SPID_STATUS_ENABLE, ®)) != - SUCCESS) { - sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__)); - return; - } - - if (set) { - reg |= DWORD_PKT_LEN_EN; - sd->dwordmode = TRUE; - sd->client_block_size[SPI_FUNC_2] = 4096; /* h2spi's limit is 4KB, we support 8KB */ - } else { - reg &= ~DWORD_PKT_LEN_EN; - sd->dwordmode = FALSE; - sd->client_block_size[SPI_FUNC_2] = 2048; - } - - if ((status = sdioh_request_byte(sd, SDIOH_WRITE, SPI_FUNC_0, SPID_STATUS_ENABLE, ®)) != - SUCCESS) { - sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__)); - return; - } -} - - -uint -sdioh_query_iofnum(sdioh_info_t *sd) -{ - return sd->num_funcs; -} - -/* IOVar table */ -enum { - IOV_MSGLEVEL = 1, - IOV_BLOCKMODE, - IOV_BLOCKSIZE, - IOV_DMA, - IOV_USEINTS, - IOV_NUMINTS, - IOV_NUMLOCALINTS, - IOV_HOSTREG, - IOV_DEVREG, - IOV_DIVISOR, - IOV_SDMODE, - IOV_HISPEED, - IOV_HCIREGS, - IOV_POWER, - IOV_CLOCK, - IOV_SPIERRSTATS, - IOV_RESP_DELAY_ALL -}; - -const bcm_iovar_t sdioh_iovars[] = { - {"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0 }, - {"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0 }, /* ((fn << 16) | size) */ - {"sd_dma", IOV_DMA, 0, IOVT_BOOL, 0 }, - {"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0 }, - {"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0 }, - {"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32, 0 }, - {"sd_hostreg", IOV_HOSTREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, - {"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(sdreg_t) }, - {"sd_divisor", IOV_DIVISOR, 0, IOVT_UINT32, 0 }, - {"sd_power", IOV_POWER, 0, IOVT_UINT32, 0 }, - {"sd_clock", IOV_CLOCK, 0, IOVT_UINT32, 0 }, - {"sd_mode", IOV_SDMODE, 0, IOVT_UINT32, 100}, - {"sd_highspeed", IOV_HISPEED, 0, IOVT_UINT32, 0}, - {"spi_errstats", IOV_SPIERRSTATS, 0, IOVT_BUFFER, sizeof(struct spierrstats_t) }, - {"spi_respdelay", IOV_RESP_DELAY_ALL, 0, IOVT_BOOL, 0 }, - {NULL, 0, 0, 0, 0 } -}; - -int -sdioh_iovar_op(sdioh_info_t *si, const char *name, - void *params, int plen, void *arg, int len, bool set) -{ - const bcm_iovar_t *vi = NULL; - int bcmerror = 0; - int val_size; - int32 int_val = 0; - bool bool_val; - uint32 actionid; -/* - sdioh_regs_t *regs; -*/ - - ASSERT(name); - ASSERT(len >= 0); - - /* Get must have return space; Set does not take qualifiers */ - ASSERT(set || (arg && len)); - ASSERT(!set || (!params && !plen)); - - sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name)); - - if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) { - bcmerror = BCME_UNSUPPORTED; - goto exit; - } - - if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 0) - goto exit; - - /* Set up params so get and set can share the convenience variables */ - if (params == NULL) { - params = arg; - plen = len; - } - - if (vi->type == IOVT_VOID) - val_size = 0; - else if (vi->type == IOVT_BUFFER) - val_size = len; - else - val_size = sizeof(int); - - if (plen >= (int)sizeof(int_val)) - bcopy(params, &int_val, sizeof(int_val)); - - bool_val = (int_val != 0) ? TRUE : FALSE; - - actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid); - switch (actionid) { - case IOV_GVAL(IOV_MSGLEVEL): - int_val = (int32)sd_msglevel; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_MSGLEVEL): - sd_msglevel = int_val; - break; - - case IOV_GVAL(IOV_BLOCKSIZE): - if ((uint32)int_val > si->num_funcs) { - bcmerror = BCME_BADARG; - break; - } - int_val = (int32)si->client_block_size[int_val]; - bcopy(&int_val, arg, val_size); - break; - - case IOV_GVAL(IOV_DMA): - int_val = (int32)si->sd_use_dma; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_DMA): - si->sd_use_dma = (bool)int_val; - break; - - case IOV_GVAL(IOV_USEINTS): - int_val = (int32)si->use_client_ints; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_USEINTS): - break; - - case IOV_GVAL(IOV_DIVISOR): - int_val = (uint32)sd_divisor; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_DIVISOR): - sd_divisor = int_val; - if (!spi_start_clock(si, (uint16)sd_divisor)) { - sd_err(("%s: set clock failed\n", __FUNCTION__)); - bcmerror = BCME_ERROR; - } - break; - - case IOV_GVAL(IOV_POWER): - int_val = (uint32)sd_power; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_POWER): - sd_power = int_val; - break; - - case IOV_GVAL(IOV_CLOCK): - int_val = (uint32)sd_clock; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_CLOCK): - sd_clock = int_val; - break; - - case IOV_GVAL(IOV_SDMODE): - int_val = (uint32)sd_sdmode; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_SDMODE): - sd_sdmode = int_val; - break; - - case IOV_GVAL(IOV_HISPEED): - int_val = (uint32)sd_hiok; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_HISPEED): - sd_hiok = int_val; - - if (!bcmspi_set_highspeed_mode(si, (bool)sd_hiok)) { - sd_err(("%s: Failed changing highspeed mode to %d.\n", - __FUNCTION__, sd_hiok)); - bcmerror = BCME_ERROR; - return ERROR; - } - break; - - case IOV_GVAL(IOV_NUMINTS): - int_val = (int32)si->intrcount; - bcopy(&int_val, arg, val_size); - break; - - case IOV_GVAL(IOV_NUMLOCALINTS): - int_val = (int32)si->local_intrcount; - bcopy(&int_val, arg, val_size); - break; - case IOV_GVAL(IOV_DEVREG): - { - sdreg_t *sd_ptr = (sdreg_t *)params; - uint8 data; - - if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) { - bcmerror = BCME_SDIO_ERROR; - break; - } - - int_val = (int)data; - bcopy(&int_val, arg, sizeof(int_val)); - break; - } - - case IOV_SVAL(IOV_DEVREG): - { - sdreg_t *sd_ptr = (sdreg_t *)params; - uint8 data = (uint8)sd_ptr->value; - - if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) { - bcmerror = BCME_SDIO_ERROR; - break; - } - break; - } - - - case IOV_GVAL(IOV_SPIERRSTATS): - { - bcopy(&si->spierrstats, arg, sizeof(struct spierrstats_t)); - break; - } - - case IOV_SVAL(IOV_SPIERRSTATS): - { - bzero(&si->spierrstats, sizeof(struct spierrstats_t)); - break; - } - - case IOV_GVAL(IOV_RESP_DELAY_ALL): - int_val = (int32)si->resp_delay_all; - bcopy(&int_val, arg, val_size); - break; - - case IOV_SVAL(IOV_RESP_DELAY_ALL): - si->resp_delay_all = (bool)int_val; - int_val = STATUS_ENABLE|INTR_WITH_STATUS; - if (si->resp_delay_all) - int_val |= RESP_DELAY_ALL; - else { - if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_RESPONSE_DELAY, 1, - F1_RESPONSE_DELAY) != SUCCESS) { - sd_err(("%s: Unable to set response delay.\n", __FUNCTION__)); - bcmerror = BCME_SDIO_ERROR; - break; - } - } - - if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_STATUS_ENABLE, 1, int_val) - != SUCCESS) { - sd_err(("%s: Unable to set response delay.\n", __FUNCTION__)); - bcmerror = BCME_SDIO_ERROR; - break; - } - break; - - default: - bcmerror = BCME_UNSUPPORTED; - break; - } -exit: - - return bcmerror; -} - -extern SDIOH_API_RC -sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) -{ - SDIOH_API_RC status; - /* No lock needed since sdioh_request_byte does locking */ - status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data); - return status; -} - -extern SDIOH_API_RC -sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data) -{ - /* No lock needed since sdioh_request_byte does locking */ - SDIOH_API_RC status; - - if ((fnc_num == SPI_FUNC_1) && (addr == SBSDIO_FUNC1_FRAMECTRL)) { - uint8 dummy_data; - status = sdioh_cfg_read(sd, fnc_num, addr, &dummy_data); - if (status) { - sd_err(("sdioh_cfg_read() failed.\n")); - return status; - } - } - - status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data); - return status; -} - -extern SDIOH_API_RC -sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length) -{ - uint32 count; - int offset; - uint32 cis_byte; - uint16 *cis = (uint16 *)cisd; - uint bar0 = SI_ENUM_BASE; - int status; - uint8 data; - - sd_trace(("%s: Func %d\n", __FUNCTION__, func)); - - spi_lock(sd); - - /* Set sb window address to 0x18000000 */ - data = (bar0 >> 8) & SBSDIO_SBADDRLOW_MASK; - status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, &data); - if (status == SUCCESS) { - data = (bar0 >> 16) & SBSDIO_SBADDRMID_MASK; - status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, &data); - } else { - sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__)); - spi_unlock(sd); - return (BCME_ERROR); - } - if (status == SUCCESS) { - data = (bar0 >> 24) & SBSDIO_SBADDRHIGH_MASK; - status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, &data); - } else { - sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__)); - spi_unlock(sd); - return (BCME_ERROR); - } - - offset = CC_OTP; /* OTP offset in chipcommon. */ - for (count = 0; count < length/2; count++) { - if (bcmspi_card_regread (sd, SDIO_FUNC_1, offset, 2, &cis_byte) < 0) { - sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__)); - spi_unlock(sd); - return (BCME_ERROR); - } - - *cis = (uint16)cis_byte; - cis++; - offset += 2; - } - - spi_unlock(sd); - - return (BCME_OK); -} - -extern SDIOH_API_RC -sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte) -{ - int status; - uint32 cmd_arg; - uint32 dstatus; - uint32 data = (uint32)(*byte); - - spi_lock(sd); - - cmd_arg = 0; - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, rw == SDIOH_READ ? 0 : 1); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, rw, func, - regaddr, data)); - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, - cmd_arg, &data, 1)) != SUCCESS) { - spi_unlock(sd); - return status; - } - - if (rw == SDIOH_READ) - *byte = (uint8)data; - - bcmspi_cmd_getdstatus(sd, &dstatus); - if (dstatus) - sd_trace(("dstatus =0x%x\n", dstatus)); - - spi_unlock(sd); - return SDIOH_API_RC_SUCCESS; -} - -extern SDIOH_API_RC -sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr, - uint32 *word, uint nbytes) -{ - int status; - - spi_lock(sd); - - if (rw == SDIOH_READ) - status = bcmspi_card_regread(sd, func, addr, nbytes, word); - else - status = bcmspi_card_regwrite(sd, func, addr, nbytes, *word); - - spi_unlock(sd); - return (status == SUCCESS ? SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL); -} - -extern SDIOH_API_RC -sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint rw, uint func, - uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt) -{ - int len; - int buflen = (int)buflen_u; - bool fifo = (fix_inc == SDIOH_DATA_FIX); - - spi_lock(sd); - - ASSERT(reg_width == 4); - ASSERT(buflen_u < (1 << 30)); - ASSERT(sd->client_block_size[func]); - - sd_data(("%s: %c len %d r_cnt %d t_cnt %d, pkt @0x%p\n", - __FUNCTION__, rw == SDIOH_READ ? 'R' : 'W', - buflen_u, sd->r_cnt, sd->t_cnt, pkt)); - - /* Break buffer down into blocksize chunks. */ - while (buflen > 0) { - len = MIN(sd->client_block_size[func], buflen); - if (bcmspi_card_buf(sd, rw, func, fifo, addr, len, (uint32 *)buffer) != SUCCESS) { - sd_err(("%s: bcmspi_card_buf %s failed\n", - __FUNCTION__, rw == SDIOH_READ ? "Read" : "Write")); - spi_unlock(sd); - return SDIOH_API_RC_FAIL; - } - buffer += len; - buflen -= len; - if (!fifo) - addr += len; - } - spi_unlock(sd); - return SDIOH_API_RC_SUCCESS; -} - -/* This function allows write to gspi bus when another rd/wr function is deep down the call stack. - * Its main aim is to have simpler spi writes rather than recursive writes. - * e.g. When there is a need to program response delay on the fly after detecting the SPI-func - * this call will allow to program the response delay. - */ -static int -bcmspi_card_byterewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 byte) -{ - uint32 cmd_arg; - uint32 datalen = 1; - uint32 hostlen; - - cmd_arg = 0; - - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, datalen); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - - - /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen - * according to the wordlen mode(16/32bit) the device is in. - */ - ASSERT(sd->wordlen == 4 || sd->wordlen == 2); - datalen = ROUNDUP(datalen, sd->wordlen); - - /* Start by copying command in the spi-outbuffer */ - if (sd->wordlen == 4) { /* 32bit spid */ - *(uint32 *)spi_outbuf2 = bcmswap32(cmd_arg); - if (datalen & 0x3) - datalen += (4 - (datalen & 0x3)); - } else if (sd->wordlen == 2) { /* 16bit spid */ - *(uint16 *)spi_outbuf2 = bcmswap16(cmd_arg & 0xffff); - *(uint16 *)&spi_outbuf2[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16); - if (datalen & 0x1) - datalen++; - } else { - sd_err(("%s: Host is %d bit spid, could not create SPI command.\n", - __FUNCTION__, 8 * sd->wordlen)); - return ERROR; - } - - /* for Write, put the data into the output buffer */ - if (datalen != 0) { - if (sd->wordlen == 4) { /* 32bit spid */ - *(uint32 *)&spi_outbuf2[CMDLEN] = bcmswap32(byte); - } else if (sd->wordlen == 2) { /* 16bit spid */ - *(uint16 *)&spi_outbuf2[CMDLEN] = bcmswap16(byte & 0xffff); - *(uint16 *)&spi_outbuf2[CMDLEN + 2] = - bcmswap16((byte & 0xffff0000) >> 16); - } - } - - /* +4 for cmd, +4 for dstatus */ - hostlen = datalen + 8; - hostlen += (4 - (hostlen & 0x3)); - spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, hostlen); - - /* Last 4bytes are dstatus. Device is configured to return status bits. */ - if (sd->wordlen == 4) { /* 32bit spid */ - sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]); - } else if (sd->wordlen == 2) { /* 16bit spid */ - sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) | - (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16)); - } else { - sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n", - __FUNCTION__, 8 * sd->wordlen)); - return ERROR; - } - - if (sd->card_dstatus) - sd_trace(("dstatus after byte rewrite = 0x%x\n", sd->card_dstatus)); - - return (BCME_OK); -} - -/* Program the response delay corresponding to the spi function */ -static int -bcmspi_prog_resp_delay(sdioh_info_t *sd, int func, uint8 resp_delay) -{ - if (sd->resp_delay_all == FALSE) - return (BCME_OK); - - if (sd->prev_fun == func) - return (BCME_OK); - - if (F0_RESPONSE_DELAY == F1_RESPONSE_DELAY) - return (BCME_OK); - - bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_RESPONSE_DELAY, resp_delay); - - /* Remember function for which to avoid reprogramming resp-delay in next iteration */ - sd->prev_fun = func; - - return (BCME_OK); - -} - -#define GSPI_RESYNC_PATTERN 0x0 - -/* A resync pattern is a 32bit MOSI line with all zeros. Its a special command in gSPI. - * It resets the spi-bkplane logic so that all F1 related ping-pong buffer logic is - * synchronised and all queued resuests are cancelled. - */ -static int -bcmspi_resync_f1(sdioh_info_t *sd) -{ - uint32 cmd_arg = GSPI_RESYNC_PATTERN, data = 0, datalen = 0; - - - /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen - * according to the wordlen mode(16/32bit) the device is in. - */ - ASSERT(sd->wordlen == 4 || sd->wordlen == 2); - datalen = ROUNDUP(datalen, sd->wordlen); - - /* Start by copying command in the spi-outbuffer */ - *(uint32 *)spi_outbuf2 = cmd_arg; - - /* for Write, put the data into the output buffer */ - *(uint32 *)&spi_outbuf2[CMDLEN] = data; - - /* +4 for cmd, +4 for dstatus */ - spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, datalen + 8); - - /* Last 4bytes are dstatus. Device is configured to return status bits. */ - if (sd->wordlen == 4) { /* 32bit spid */ - sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]); - } else if (sd->wordlen == 2) { /* 16bit spid */ - sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) | - (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16)); - } else { - sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n", - __FUNCTION__, 8 * sd->wordlen)); - return ERROR; - } - - if (sd->card_dstatus) - sd_trace(("dstatus after resync pattern write = 0x%x\n", sd->card_dstatus)); - - return (BCME_OK); -} - -uint32 dstatus_count = 0; - -static int -bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg) -{ - uint32 dstatus = sd->card_dstatus; - struct spierrstats_t *spierrstats = &sd->spierrstats; - int err = SUCCESS; - - sd_trace(("cmd = 0x%x, dstatus = 0x%x\n", cmd_arg, dstatus)); - - /* Store dstatus of last few gSPI transactions */ - spierrstats->dstatus[dstatus_count % NUM_PREV_TRANSACTIONS] = dstatus; - spierrstats->spicmd[dstatus_count % NUM_PREV_TRANSACTIONS] = cmd_arg; - dstatus_count++; - - if (sd->card_init_done == FALSE) - return err; - - if (dstatus & STATUS_DATA_NOT_AVAILABLE) { - spierrstats->dna++; - sd_trace(("Read data not available on F1 addr = 0x%x\n", - GFIELD(cmd_arg, SPI_REG_ADDR))); - /* Clear dna bit */ - bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, DATA_UNAVAILABLE); - } - - if (dstatus & STATUS_UNDERFLOW) { - spierrstats->rdunderflow++; - sd_err(("FIFO underflow happened due to current F2 read command.\n")); - } - - if (dstatus & STATUS_OVERFLOW) { - spierrstats->wroverflow++; - sd_err(("FIFO overflow happened due to current (F1/F2) write command.\n")); - if ((sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 0)) { - bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, F1_OVERFLOW); - bcmspi_resync_f1(sd); - sd_err(("Recovering from F1 FIFO overflow.\n")); - } else { - err = ERROR_OF; - } - } - - if (dstatus & STATUS_F2_INTR) { - spierrstats->f2interrupt++; - sd_trace(("Interrupt from F2. SW should clear corresponding IntStatus bits\n")); - } - - if (dstatus & STATUS_F3_INTR) { - spierrstats->f3interrupt++; - sd_err(("Interrupt from F3. SW should clear corresponding IntStatus bits\n")); - } - - if (dstatus & STATUS_HOST_CMD_DATA_ERR) { - spierrstats->hostcmddataerr++; - sd_err(("Error in CMD or Host data, detected by CRC/Checksum (optional)\n")); - } - - if (dstatus & STATUS_F2_PKT_AVAILABLE) { - spierrstats->f2pktavailable++; - sd_trace(("Packet is available/ready in F2 TX FIFO\n")); - sd_trace(("Packet length = %d\n", sd->dwordmode ? - ((dstatus & STATUS_F2_PKT_LEN_MASK) >> (STATUS_F2_PKT_LEN_SHIFT - 2)) : - ((dstatus & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT))); - } - - if (dstatus & STATUS_F3_PKT_AVAILABLE) { - spierrstats->f3pktavailable++; - sd_err(("Packet is available/ready in F3 TX FIFO\n")); - sd_err(("Packet length = %d\n", - (dstatus & STATUS_F3_PKT_LEN_MASK) >> STATUS_F3_PKT_LEN_SHIFT)); - } - - return err; -} - -extern int -sdioh_abort(sdioh_info_t *sd, uint func) -{ - return 0; -} - -int -sdioh_start(sdioh_info_t *sd, int stage) -{ - return SUCCESS; -} - -int -sdioh_stop(sdioh_info_t *sd) -{ - return SUCCESS; -} - - - -/* - * Private/Static work routines - */ -static int -bcmspi_host_init(sdioh_info_t *sd) -{ - - /* Default power on mode */ - sd->sd_mode = SDIOH_MODE_SPI; - sd->polled_mode = TRUE; - sd->host_init_done = TRUE; - sd->card_init_done = FALSE; - sd->adapter_slot = 1; - - return (SUCCESS); -} - -static int -get_client_blocksize(sdioh_info_t *sd) -{ - uint32 regdata[2]; - int status; - - /* Find F1/F2/F3 max packet size */ - if ((status = bcmspi_card_regread(sd, 0, SPID_F1_INFO_REG, - 8, regdata)) != SUCCESS) { - return status; - } - - sd_trace(("pkt_size regdata[0] = 0x%x, regdata[1] = 0x%x\n", - regdata[0], regdata[1])); - - sd->client_block_size[1] = (regdata[0] & F1_MAX_PKT_SIZE) >> 2; - sd_trace(("Func1 blocksize = %d\n", sd->client_block_size[1])); - ASSERT(sd->client_block_size[1] == BLOCK_SIZE_F1); - - sd->client_block_size[2] = ((regdata[0] >> 16) & F2_MAX_PKT_SIZE) >> 2; - sd_trace(("Func2 blocksize = %d\n", sd->client_block_size[2])); - ASSERT(sd->client_block_size[2] == BLOCK_SIZE_F2); - - sd->client_block_size[3] = (regdata[1] & F3_MAX_PKT_SIZE) >> 2; - sd_trace(("Func3 blocksize = %d\n", sd->client_block_size[3])); - ASSERT(sd->client_block_size[3] == BLOCK_SIZE_F3); - - return 0; -} - -static int -bcmspi_client_init(sdioh_info_t *sd) -{ - uint32 status_en_reg = 0; - sd_trace(("%s: Powering up slot %d\n", __FUNCTION__, sd->adapter_slot)); - -#ifdef HSMODE - if (!spi_start_clock(sd, (uint16)sd_divisor)) { - sd_err(("spi_start_clock failed\n")); - return ERROR; - } -#else - /* Start at ~400KHz clock rate for initialization */ - if (!spi_start_clock(sd, 128)) { - sd_err(("spi_start_clock failed\n")); - return ERROR; - } -#endif /* HSMODE */ - - if (!bcmspi_host_device_init_adapt(sd)) { - sd_err(("bcmspi_host_device_init_adapt failed\n")); - return ERROR; - } - - if (!bcmspi_test_card(sd)) { - sd_err(("bcmspi_test_card failed\n")); - return ERROR; - } - - sd->num_funcs = SPI_MAX_IOFUNCS; - - get_client_blocksize(sd); - - /* Apply resync pattern cmd with all zeros to reset spi-bkplane F1 logic */ - bcmspi_resync_f1(sd); - - sd->dwordmode = FALSE; - - bcmspi_card_regread(sd, 0, SPID_STATUS_ENABLE, 1, &status_en_reg); - - sd_trace(("%s: Enabling interrupt with dstatus \n", __FUNCTION__)); - status_en_reg |= INTR_WITH_STATUS; - - - if (bcmspi_card_regwrite(sd, SPI_FUNC_0, SPID_STATUS_ENABLE, 1, - status_en_reg & 0xff) != SUCCESS) { - sd_err(("%s: Unable to set response delay for all fun's.\n", __FUNCTION__)); - return ERROR; - } - - -#ifndef HSMODE - /* After configuring for High-Speed mode, set the desired clock rate. */ - if (!spi_start_clock(sd, 4)) { - sd_err(("spi_start_clock failed\n")); - return ERROR; - } -#endif /* HSMODE */ - - sd->card_init_done = TRUE; - - - return SUCCESS; -} - -static int -bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode) -{ - uint32 regdata; - int status; - - if ((status = bcmspi_card_regread(sd, 0, SPID_CONFIG, - 4, ®data)) != SUCCESS) - return status; - - sd_trace(("In %s spih-ctrl = 0x%x \n", __FUNCTION__, regdata)); - - - if (hsmode == TRUE) { - sd_trace(("Attempting to enable High-Speed mode.\n")); - - if (regdata & HIGH_SPEED_MODE) { - sd_trace(("Device is already in High-Speed mode.\n")); - return status; - } else { - regdata |= HIGH_SPEED_MODE; - sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG)); - if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG, - 4, regdata)) != SUCCESS) { - return status; - } - } - } else { - sd_trace(("Attempting to disable High-Speed mode.\n")); - - if (regdata & HIGH_SPEED_MODE) { - regdata &= ~HIGH_SPEED_MODE; - sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG)); - if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG, - 4, regdata)) != SUCCESS) - return status; - } - else { - sd_trace(("Device is already in Low-Speed mode.\n")); - return status; - } - } - - spi_controller_highspeed_mode(sd, hsmode); - - return TRUE; -} - -#define bcmspi_find_curr_mode(sd) { \ - sd->wordlen = 2; \ - status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, ®data); \ - regdata &= 0xff; \ - if ((regdata == 0xad) || (regdata == 0x5b) || \ - (regdata == 0x5d) || (regdata == 0x5a)) \ - break; \ - sd->wordlen = 4; \ - status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, ®data); \ - regdata &= 0xff; \ - if ((regdata == 0xad) || (regdata == 0x5b) || \ - (regdata == 0x5d) || (regdata == 0x5a)) \ - break; \ - sd_trace(("Silicon testability issue: regdata = 0x%x." \ - " Expected 0xad, 0x5a, 0x5b or 0x5d.\n", regdata)); \ - OSL_DELAY(100000); \ -} - -#define INIT_ADAPT_LOOP 100 - -/* Adapt clock-phase-speed-bitwidth between host and device */ -static bool -bcmspi_host_device_init_adapt(sdioh_info_t *sd) -{ - uint32 wrregdata, regdata = 0; - int status; - int i; - - /* Due to a silicon testability issue, the first command from the Host - * to the device will get corrupted (first bit will be lost). So the - * Host should poll the device with a safe read request. ie: The Host - * should try to read F0 addr 0x14 using the Fixed address mode - * (This will prevent a unintended write command to be detected by device) - */ - for (i = 0; i < INIT_ADAPT_LOOP; i++) { - /* If device was not power-cycled it will stay in 32bit mode with - * response-delay-all bit set. Alternate the iteration so that - * read either with or without response-delay for F0 to succeed. - */ - bcmspi_find_curr_mode(sd); - sd->resp_delay_all = (i & 0x1) ? TRUE : FALSE; - - bcmspi_find_curr_mode(sd); - sd->dwordmode = TRUE; - - bcmspi_find_curr_mode(sd); - sd->dwordmode = FALSE; - } - - /* Bail out, device not detected */ - if (i == INIT_ADAPT_LOOP) - return FALSE; - - /* Softreset the spid logic */ - if ((sd->dwordmode) || (sd->wordlen == 4)) { - bcmspi_card_regwrite(sd, 0, SPID_RESET_BP, 1, RESET_ON_WLAN_BP_RESET|RESET_SPI); - bcmspi_card_regread(sd, 0, SPID_RESET_BP, 1, ®data); - sd_trace(("reset reg read = 0x%x\n", regdata)); - sd_trace(("dwordmode = %d, wordlen = %d, resp_delay_all = %d\n", sd->dwordmode, - sd->wordlen, sd->resp_delay_all)); - /* Restore default state after softreset */ - sd->wordlen = 2; - sd->dwordmode = FALSE; - } - - if (sd->wordlen == 4) { - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != - SUCCESS) - return FALSE; - if (regdata == TEST_RO_DATA_32BIT_LE) { - sd_trace(("Spid is already in 32bit LE mode. Value read = 0x%x\n", - regdata)); - sd_trace(("Spid power was left on.\n")); - } else { - sd_err(("Spid power was left on but signature read failed." - " Value read = 0x%x\n", regdata)); - return FALSE; - } - } else { - sd->wordlen = 2; - -#define CTRL_REG_DEFAULT 0x00010430 /* according to the host m/c */ - - wrregdata = (CTRL_REG_DEFAULT); - sd->resp_delay_all = TRUE; - if (sd->resp_delay_all == TRUE) { - /* Enable response delay for all */ - wrregdata |= (RESP_DELAY_ALL << 16); - /* Program response delay value */ - wrregdata &= 0xffff00ff; - wrregdata |= (F1_RESPONSE_DELAY << 8); - sd->prev_fun = SPI_FUNC_1; - bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata); - } - - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != SUCCESS) - return FALSE; - sd_trace(("(we are still in 16bit mode) 32bit READ LE regdata = 0x%x\n", regdata)); - -#ifndef HSMODE - wrregdata |= (CLOCK_PHASE | CLOCK_POLARITY); - wrregdata &= ~HIGH_SPEED_MODE; - bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata); -#endif /* HSMODE */ - - for (i = 0; i < INIT_ADAPT_LOOP; i++) { - if ((regdata == 0xfdda7d5b) || (regdata == 0xfdda7d5a)) { - sd_trace(("0xfeedbead was leftshifted by 1-bit.\n")); - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, - ®data)) != SUCCESS) - return FALSE; - } - OSL_DELAY(1000); - } - - - /* Change to host controller intr-polarity of active-low */ - wrregdata &= ~INTR_POLARITY; - sd_trace(("(we are still in 16bit mode) 32bit Write LE reg-ctrl-data = 0x%x\n", - wrregdata)); - /* Change to 32bit mode */ - wrregdata |= WORD_LENGTH_32; - bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata); - - /* Change command/data packaging in 32bit LE mode */ - sd->wordlen = 4; - - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != SUCCESS) - return FALSE; - - if (regdata == TEST_RO_DATA_32BIT_LE) { - sd_trace(("Read spid passed. Value read = 0x%x\n", regdata)); - sd_trace(("Spid had power-on cycle OR spi was soft-resetted \n")); - } else { - sd_err(("Stale spid reg values read as it was kept powered. Value read =" - "0x%x\n", regdata)); - return FALSE; - } - } - - - return TRUE; -} - -static bool -bcmspi_test_card(sdioh_info_t *sd) -{ - uint32 regdata; - int status; - - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, ®data)) != SUCCESS) - return FALSE; - - if (regdata == (TEST_RO_DATA_32BIT_LE)) - sd_trace(("32bit LE regdata = 0x%x\n", regdata)); - else { - sd_trace(("Incorrect 32bit LE regdata = 0x%x\n", regdata)); - return FALSE; - } - - -#define RW_PATTERN1 0xA0A1A2A3 -#define RW_PATTERN2 0x4B5B6B7B - - regdata = RW_PATTERN1; - if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS) - return FALSE; - regdata = 0; - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, ®data)) != SUCCESS) - return FALSE; - if (regdata != RW_PATTERN1) { - sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n", - RW_PATTERN1, regdata)); - return FALSE; - } else - sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata)); - - regdata = RW_PATTERN2; - if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS) - return FALSE; - regdata = 0; - if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, ®data)) != SUCCESS) - return FALSE; - if (regdata != RW_PATTERN2) { - sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n", - RW_PATTERN2, regdata)); - return FALSE; - } else - sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata)); - - return TRUE; -} - -static int -bcmspi_driver_init(sdioh_info_t *sd) -{ - sd_trace(("%s\n", __FUNCTION__)); - if ((bcmspi_host_init(sd)) != SUCCESS) { - return ERROR; - } - - if (bcmspi_client_init(sd) != SUCCESS) { - return ERROR; - } - - return SUCCESS; -} - -/* Read device reg */ -static int -bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data) -{ - int status; - uint32 cmd_arg, dstatus; - - ASSERT(regsize); - - if (func == 2) - sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n")); - - cmd_arg = 0; - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func, - regaddr, *data)); - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize)) - != SUCCESS) - return status; - - bcmspi_cmd_getdstatus(sd, &dstatus); - if (dstatus) - sd_trace(("dstatus =0x%x\n", dstatus)); - - return SUCCESS; -} - -static int -bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data) -{ - - int status; - uint32 cmd_arg; - uint32 dstatus; - - ASSERT(regsize); - - if (func == 2) - sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n")); - - cmd_arg = 0; - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0); /* Fixed access */ - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize)) - != SUCCESS) - return status; - - sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func, - regaddr, *data)); - - bcmspi_cmd_getdstatus(sd, &dstatus); - sd_trace(("dstatus =0x%x\n", dstatus)); - return SUCCESS; -} - -/* write a device register */ -static int -bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data) -{ - int status; - uint32 cmd_arg, dstatus; - - ASSERT(regsize); - - cmd_arg = 0; - - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 1, func, - regaddr, data)); - - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, &data, regsize)) - != SUCCESS) - return status; - - bcmspi_cmd_getdstatus(sd, &dstatus); - if (dstatus) - sd_trace(("dstatus =0x%x\n", dstatus)); - - return SUCCESS; -} - -/* write a device register - 1 byte */ -static int -bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 *byte) -{ - int status; - uint32 cmd_arg; - uint32 dstatus; - uint32 data = (uint32)(*byte); - - cmd_arg = 0; - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); /* Incremental access */ - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr); - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1); - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - sd_trace(("%s: func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, func, - regaddr, data)); - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, - cmd_arg, &data, 1)) != SUCCESS) { - return status; - } - - bcmspi_cmd_getdstatus(sd, &dstatus); - if (dstatus) - sd_trace(("dstatus =0x%x\n", dstatus)); - - return SUCCESS; -} - -void -bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer) -{ - *dstatus_buffer = sd->card_dstatus; -} - -/* 'data' is of type uint32 whereas other buffers are of type uint8 */ -static int -bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg, - uint32 *data, uint32 datalen) -{ - uint32 i, j; - uint8 resp_delay = 0; - int err = SUCCESS; - uint32 hostlen; - uint32 spilen = 0; - uint32 dstatus_idx = 0; - uint16 templen, buslen, len, *ptr = NULL; - - sd_trace(("spi cmd = 0x%x\n", cmd_arg)); - - if (DWORDMODE_ON) { - spilen = GFIELD(cmd_arg, SPI_LEN); - if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_0) || - (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_1)) - dstatus_idx = spilen * 3; - - if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) && - (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) { - spilen = spilen << 2; - dstatus_idx = (spilen % 16) ? (16 - (spilen % 16)) : 0; - /* convert len to mod16 size */ - spilen = ROUNDUP(spilen, 16); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2)); - } - } - - /* Set up and issue the SPI command. MSByte goes out on bus first. Increase datalen - * according to the wordlen mode(16/32bit) the device is in. - */ - if (sd->wordlen == 4) { /* 32bit spid */ - *(uint32 *)spi_outbuf = bcmswap32(cmd_arg); - if (datalen & 0x3) - datalen += (4 - (datalen & 0x3)); - } else if (sd->wordlen == 2) { /* 16bit spid */ - *(uint16 *)spi_outbuf = bcmswap16(cmd_arg & 0xffff); - *(uint16 *)&spi_outbuf[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16); - if (datalen & 0x1) - datalen++; - if (datalen < 4) - datalen = ROUNDUP(datalen, 4); - } else { - sd_err(("Host is %d bit spid, could not create SPI command.\n", - 8 * sd->wordlen)); - return ERROR; - } - - /* for Write, put the data into the output buffer */ - if (GFIELD(cmd_arg, SPI_RW_FLAG) == 1) { - /* We send len field of hw-header always a mod16 size, both from host and dongle */ - if (DWORDMODE_ON) { - if (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) { - ptr = (uint16 *)&data[0]; - templen = *ptr; - /* ASSERT(*ptr == ~*(ptr + 1)); */ - templen = ROUNDUP(templen, 16); - *ptr = templen; - sd_trace(("actual tx len = %d\n", (uint16)(~*(ptr+1)))); - } - } - - if (datalen != 0) { - for (i = 0; i < datalen/4; i++) { - if (sd->wordlen == 4) { /* 32bit spid */ - *(uint32 *)&spi_outbuf[i * 4 + CMDLEN] = - bcmswap32(data[i]); - } else if (sd->wordlen == 2) { /* 16bit spid */ - *(uint16 *)&spi_outbuf[i * 4 + CMDLEN] = - bcmswap16(data[i] & 0xffff); - *(uint16 *)&spi_outbuf[i * 4 + CMDLEN + 2] = - bcmswap16((data[i] & 0xffff0000) >> 16); - } - } - } - } - - /* Append resp-delay number of bytes and clock them out for F0/1/2 reads. */ - if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) { - int func = GFIELD(cmd_arg, SPI_FUNCTION); - switch (func) { - case 0: - resp_delay = sd->resp_delay_all ? F0_RESPONSE_DELAY : 0; - break; - case 1: - resp_delay = F1_RESPONSE_DELAY; - break; - case 2: - resp_delay = sd->resp_delay_all ? F2_RESPONSE_DELAY : 0; - break; - default: - ASSERT(0); - break; - } - /* Program response delay */ - bcmspi_prog_resp_delay(sd, func, resp_delay); - } - - /* +4 for cmd and +4 for dstatus */ - hostlen = datalen + 8 + resp_delay; - hostlen += dstatus_idx; - hostlen += (4 - (hostlen & 0x3)); - spi_sendrecv(sd, spi_outbuf, spi_inbuf, hostlen); - - /* for Read, get the data into the input buffer */ - if (datalen != 0) { - if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) { /* if read cmd */ - for (j = 0; j < datalen/4; j++) { - if (sd->wordlen == 4) { /* 32bit spid */ - data[j] = bcmswap32(*(uint32 *)&spi_inbuf[j * 4 + - CMDLEN + resp_delay]); - } else if (sd->wordlen == 2) { /* 16bit spid */ - data[j] = (bcmswap16(*(uint16 *)&spi_inbuf[j * 4 + - CMDLEN + resp_delay])) | - ((bcmswap16(*(uint16 *)&spi_inbuf[j * 4 + - CMDLEN + resp_delay + 2])) << 16); - } - } - - if ((DWORDMODE_ON) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) { - ptr = (uint16 *)&data[0]; - templen = *ptr; - buslen = len = ~(*(ptr + 1)); - buslen = ROUNDUP(buslen, 16); - /* populate actual len in hw-header */ - if (templen == buslen) - *ptr = len; - } - } - } - - /* Restore back the len field of the hw header */ - if (DWORDMODE_ON) { - if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) && - (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) { - ptr = (uint16 *)&data[0]; - *ptr = (uint16)(~*(ptr+1)); - } - } - - dstatus_idx += (datalen + CMDLEN + resp_delay); - /* Last 4bytes are dstatus. Device is configured to return status bits. */ - if (sd->wordlen == 4) { /* 32bit spid */ - sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf[dstatus_idx]); - } else if (sd->wordlen == 2) { /* 16bit spid */ - sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx]) | - (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx + 2]) << 16)); - } else { - sd_err(("Host is %d bit machine, could not read SPI dstatus.\n", - 8 * sd->wordlen)); - return ERROR; - } - if (sd->card_dstatus == 0xffffffff) { - sd_err(("looks like not a GSPI device or device is not powered.\n")); - } - - err = bcmspi_update_stats(sd, cmd_arg); - - return err; - -} - -static int -bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo, - uint32 addr, int nbytes, uint32 *data) -{ - int status; - uint32 cmd_arg; - bool write = rw == SDIOH_READ ? 0 : 1; - uint retries = 0; - - bool enable; - uint32 spilen; - - cmd_arg = 0; - - ASSERT(nbytes); - ASSERT(nbytes <= sd->client_block_size[func]); - - if (write) sd->t_cnt++; else sd->r_cnt++; - - if (func == 2) { - /* Frame len check limited by gSPI. */ - if ((nbytes > 2000) && write) { - sd_trace((">2KB write: F2 wr of %d bytes\n", nbytes)); - } - /* ASSERT(nbytes <= 2048); Fix bigger len gspi issue and uncomment. */ - /* If F2 fifo on device is not ready to receive data, don't do F2 transfer */ - if (write) { - uint32 dstatus; - /* check F2 ready with cached one */ - bcmspi_cmd_getdstatus(sd, &dstatus); - if ((dstatus & STATUS_F2_RX_READY) == 0) { - retries = WAIT_F2RXFIFORDY; - enable = 0; - while (retries-- && !enable) { - OSL_DELAY(WAIT_F2RXFIFORDY_DELAY * 1000); - bcmspi_card_regread(sd, SPI_FUNC_0, SPID_STATUS_REG, 4, - &dstatus); - if (dstatus & STATUS_F2_RX_READY) - enable = TRUE; - } - if (!enable) { - struct spierrstats_t *spierrstats = &sd->spierrstats; - spierrstats->f2rxnotready++; - sd_err(("F2 FIFO is not ready to receive data.\n")); - return ERROR; - } - sd_trace(("No of retries on F2 ready %d\n", - (WAIT_F2RXFIFORDY - retries))); - } - } - } - - /* F2 transfers happen on 0 addr */ - addr = (func == 2) ? 0 : addr; - - /* In pio mode buffer is read using fixed address fifo in func 1 */ - if ((func == 1) && (fifo)) - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0); - else - cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1); - - cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func); - cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, addr); - cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, write); - spilen = sd->data_xfer_count = MIN(sd->client_block_size[func], nbytes); - if ((sd->dwordmode == TRUE) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) { - /* convert len to mod4 size */ - spilen = spilen + ((spilen & 0x3) ? (4 - (spilen & 0x3)): 0); - cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2)); - } else - cmd_arg = SFIELD(cmd_arg, SPI_LEN, spilen); - - if ((func == 2) && (fifo == 1)) { - sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n", - __FUNCTION__, write ? "Wr" : "Rd", func, "INCR", - addr, nbytes, sd->r_cnt, sd->t_cnt)); - } - - sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg)); - sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n", - __FUNCTION__, write ? "Wd" : "Rd", func, "INCR", - addr, nbytes, sd->r_cnt, sd->t_cnt)); - - - if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, - data, nbytes)) != SUCCESS) { - sd_err(("%s: cmd_issue failed for %s\n", __FUNCTION__, - (write ? "write" : "read"))); - return status; - } - - /* gSPI expects that hw-header-len is equal to spi-command-len */ - if ((func == 2) && (rw == SDIOH_WRITE) && (sd->dwordmode == FALSE)) { - ASSERT((uint16)sd->data_xfer_count == (uint16)(*data & 0xffff)); - ASSERT((uint16)sd->data_xfer_count == (uint16)(~((*data & 0xffff0000) >> 16))); - } - - if ((nbytes > 2000) && !write) { - sd_trace((">2KB read: F2 rd of %d bytes\n", nbytes)); - } - - return SUCCESS; -} - -/* Reset and re-initialize the device */ -int -sdioh_sdio_reset(sdioh_info_t *si) -{ - si->card_init_done = FALSE; - return bcmspi_client_init(si); -} diff --git a/drivers/net/wireless/bcmdhd/Makefile b/drivers/net/wireless/bcmdhd/Makefile index 9984363f11d..56500b7220c 100644 --- a/drivers/net/wireless/bcmdhd/Makefile +++ b/drivers/net/wireless/bcmdhd/Makefile @@ -26,4 +26,6 @@ bcmdhd-objs += wl_cfg80211.o wl_cfgp2p.o dhd_linux_mon.o DHDCFLAGS += -DWL_CFG80211 endif EXTRA_CFLAGS = $(DHDCFLAGS) +ifeq ($(CONFIG_BCMDHD),m) EXTRA_LDFLAGS += --strip-debug +endif diff --git a/drivers/net/wireless/bcmdhd/bcmevent.c b/drivers/net/wireless/bcmdhd/bcmevent.c index 3cee7c214e3..24581ddd353 100644 --- a/drivers/net/wireless/bcmdhd/bcmevent.c +++ b/drivers/net/wireless/bcmdhd/bcmevent.c @@ -29,7 +29,7 @@ #include <proto/bcmeth.h> #include <proto/bcmevent.h> -#if WLC_E_LAST != 84 +#if WLC_E_LAST != 85 #error "You need to add an entry to bcmevent_names[] for the new event" #endif @@ -115,7 +115,10 @@ const bcmevent_name_t bcmevent_names[] = { { WLC_E_CSA_COMPLETE_IND, "WLC_E_CSA_COMPLETE_IND" }, { WLC_E_EXCESS_PM_WAKE_EVENT, "EXCESS_PM_WAKE_EVENT" }, { WLC_E_PFN_SCAN_NONE, "PFN_SCAN_NONE" }, - { WLC_E_PFN_SCAN_ALLGONE, "PFN_SCAN_ALLGONE" } + { WLC_E_PFN_SCAN_ALLGONE, "PFN_SCAN_ALLGONE" }, +#ifdef SOFTAP + { WLC_E_GTK_PLUMBED, "GTK_PLUMBED" } +#endif }; diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index bca75354370..ea8f67ce8b4 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -682,6 +682,7 @@ extern void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar); extern void dhd_wait_event_wakeup(dhd_pub_t*dhd); #ifdef ARP_OFFLOAD_SUPPORT +#define MAX_IPV4_ENTRIES 8 /* dhd_commn arp offload wrapers */ void dhd_aoe_hostip_clr(dhd_pub_t *dhd); void dhd_aoe_arp_clr(dhd_pub_t *dhd); diff --git a/drivers/net/wireless/bcmdhd/dhd_bus.h b/drivers/net/wireless/bcmdhd/dhd_bus.h index bae0713948b..294322026e9 100644 --- a/drivers/net/wireless/bcmdhd/dhd_bus.h +++ b/drivers/net/wireless/bcmdhd/dhd_bus.h @@ -59,6 +59,7 @@ extern int dhd_bus_rxctl(struct dhd_bus *bus, uchar *msg, uint msglen); /* Watchdog timer function */ extern bool dhd_bus_watchdog(dhd_pub_t *dhd); +extern void dhd_disable_intr(dhd_pub_t *dhd); #if defined(DHD_DEBUG) /* Device console input function */ diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c index 2e6a53ed9ca..c50f05325f6 100644 --- a/drivers/net/wireless/bcmdhd/dhd_common.c +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -92,7 +92,7 @@ bool ap_cfg_running = FALSE; bool ap_fw_loaded = FALSE; #if defined(KEEP_ALIVE) -int dhd_keep_alive_onoff(dhd_pub_t *dhd, int ka_on); +int dhd_keep_alive_onoff(dhd_pub_t *dhd); #endif /* KEEP_ALIVE */ /* Packet alignment for most efficient SDIO (can change based on platform) */ @@ -1486,11 +1486,8 @@ void dhd_arp_offload_add_ip(dhd_pub_t *dhd, uint32 ipaddr) __FUNCTION__)); } - int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen) { -#define MAX_IPV4_ENTRIES 8 - int retcode, i; int iov_len = 0; uint32 *ptr32 = buf; @@ -1502,27 +1499,28 @@ int dhd_arp_get_arp_hostip_table(dhd_pub_t *dhd, void *buf, int buflen) iov_len = bcm_mkiovar("arp_hostip", 0, 0, buf, buflen); retcode = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, buflen, TRUE, 0); + if (retcode) { + DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n", + __FUNCTION__, retcode)); + + return -1; + } + /* clean up the buf, ascii reminder */ for (i = 0; i < MAX_IPV4_ENTRIES; i++) { - if (!clr_bottom) { if (*ptr32 == 0) - clr_bottom = TRUE; + clr_bottom = TRUE; } else { *ptr32 = 0; } ptr32++; } - if (retcode) { - DHD_TRACE(("%s: ioctl WLC_GET_VAR error %d\n", - __FUNCTION__, retcode)); - - return -1; - } return 0; } #endif /* ARP_OFFLOAD_SUPPORT */ + int dhd_preinit_ioctls(dhd_pub_t *dhd) { @@ -1674,7 +1672,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(SOFTAP) if (ap_fw_loaded == FALSE) #endif - if ((res = dhd_keep_alive_onoff(dhd, 1)) < 0) + if ((res = dhd_keep_alive_onoff(dhd)) < 0) DHD_ERROR(("%s set keeplive failed %d\n", __FUNCTION__, res)); } @@ -2260,49 +2258,39 @@ dhd_pno_get_status(dhd_pub_t *dhd) #endif /* PNO_SUPPORT */ #if defined(KEEP_ALIVE) -int dhd_keep_alive_onoff(dhd_pub_t *dhd, int ka_on) +int dhd_keep_alive_onoff(dhd_pub_t *dhd) { - char buf[256]; - char *buf_ptr = buf; - wl_keep_alive_pkt_t keep_alive_pkt; - char * str; - int str_len, buf_len; - int res = -1; - int keep_alive_period = KEEP_ALIVE_PERIOD; /* in ms */ - - DHD_TRACE(("%s: param=%d\n", __FUNCTION__, ka_on)); - - if (ka_on) { /* on suspend */ - keep_alive_pkt.period_msec = keep_alive_period; + char buf[256]; + const char *str; + wl_mkeep_alive_pkt_t mkeep_alive_pkt; + wl_mkeep_alive_pkt_t *mkeep_alive_pktp; + int buf_len; + int str_len; + int res = -1; - } else { - /* on resume, turn off keep_alive packets */ - keep_alive_pkt.period_msec = 0; - } + DHD_ERROR(("%s Enter\n", __FUNCTION__)); - /* IOC var name */ - str = "keep_alive"; + str = "mkeep_alive"; str_len = strlen(str); strncpy(buf, str, str_len); - buf[str_len] = '\0'; + buf[ str_len ] = '\0'; + mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) (buf + str_len + 1); + mkeep_alive_pkt.period_msec = KEEP_ALIVE_PERIOD; buf_len = str_len + 1; + mkeep_alive_pkt.version = htod16(WL_MKEEP_ALIVE_VERSION); + mkeep_alive_pkt.length = htod16(WL_MKEEP_ALIVE_FIXED_LEN); + /* Setup keep alive zero for null packet generation */ + mkeep_alive_pkt.keep_alive_id = 0; + mkeep_alive_pkt.len_bytes = 0; + buf_len += WL_MKEEP_ALIVE_FIXED_LEN; + /* Keep-alive attributes are set in local variable (mkeep_alive_pkt), and + * then memcpy'ed into buffer (mkeep_alive_pktp) since there is no + * guarantee that the buffer is properly aligned. + */ + memcpy((char *)mkeep_alive_pktp, &mkeep_alive_pkt, WL_MKEEP_ALIVE_FIXED_LEN); - /* set ptr to IOCTL payload after the var name */ - buf_ptr += buf_len; /* include term Z */ - - /* copy Keep-alive attributes from local var keep_alive_pkt */ - str = NULL_PKT_STR; - keep_alive_pkt.len_bytes = strlen(str); - - memcpy(buf_ptr, &keep_alive_pkt, WL_KEEP_ALIVE_FIXED_LEN); - buf_ptr += WL_KEEP_ALIVE_FIXED_LEN; - - /* copy packet data */ - memcpy(buf_ptr, str, keep_alive_pkt.len_bytes); - buf_len += (WL_KEEP_ALIVE_FIXED_LEN + keep_alive_pkt.len_bytes); -/* res = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, buf_len, TRUE, 0); -*/ + return res; } #endif /* defined(KEEP_ALIVE) */ diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index e318017ff80..c6e7095f6af 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -967,13 +967,18 @@ dhd_op_if(dhd_if_t *ifp) } if (ret == 0) { strncpy(ifp->net->name, ifp->name, IFNAMSIZ); + ifp->net->name[IFNAMSIZ - 1] = '\0'; + memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd)); #ifdef WL_CFG80211 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) - wl_cfg80211_notify_ifadd(ifp->net); + if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx, + dhd_net_attach)) { + ifp->state = 0; + return; + } + #endif - ifp->net->name[IFNAMSIZ - 1] = '\0'; - memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd)); if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) { DHD_ERROR(("%s: dhd_net_attach failed, err %d\n", __FUNCTION__, err)); @@ -1381,7 +1386,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) struct sk_buff *skb; uchar *eth; uint len; - void *data, *pnext, *save_pktbuf; + void *data, *pnext = NULL, *save_pktbuf; int i; dhd_if_t *ifp; wl_event_msg_t event; @@ -1394,6 +1399,16 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) struct ether_header *eh; struct dot11_llc_snap_header *lsh; + ifp = dhd->iflist[ifidx]; + + /* Dropping packets before registering net device to avoid kernel panic */ + if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) { + DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n", + __FUNCTION__)); + PKTFREE(dhdp->osh, pktbuf, TRUE); + continue; + } + pnext = PKTNEXT(dhdp->osh, pktbuf); PKTSETNEXT(wl->sh.osh, pktbuf, NULL); @@ -1483,14 +1498,6 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) dhdp->dstats.rx_bytes += skb->len; dhdp->rx_packets++; /* Local count */ - /* Dropping packets before registering net device to avoid kernel panic */ - if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) { - DHD_ERROR(("%s: net device is NOT registered yet. drop [%s] packet\n", - __FUNCTION__, (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) ? "event" : "data")); - PKTFREE(dhdp->osh, pktbuf, TRUE); - continue; - } - if (in_interrupt()) { netif_rx(skb); } else { @@ -2351,6 +2358,7 @@ dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name, if (handle == NULL) { ifp->state = WLC_E_IF_ADD; ifp->idx = ifidx; + ifp->bssidx = bssidx; ASSERT(&dhd->thr_sysioc_ctl.thr_pid >= 0); up(&dhd->thr_sysioc_ctl.sema); } else @@ -2800,20 +2808,27 @@ int dhd_change_mtu(dhd_pub_t *dhdp, int new_mtu, int ifidx) /* add or remove AOE host ip(s) (up to 8 IPs on the interface) */ void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add) { - u32 ipv4_buf[8]; /* temp save for AOE host_ip table */ + u32 ipv4_buf[MAX_IPV4_ENTRIES]; /* temp save for AOE host_ip table */ int i; + int ret; bzero(ipv4_buf, sizeof(ipv4_buf)); /* display what we've got */ - dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf)); + ret = dhd_arp_get_arp_hostip_table(dhd_pub, ipv4_buf, sizeof(ipv4_buf)); DHD_ARPOE(("%s: hostip table read from Dongle:\n", __FUNCTION__)); +#ifdef AOE_DBG dhd_print_buf(ipv4_buf, 32, 4); /* max 8 IPs 4b each */ - +#endif /* now we saved hoste_ip table, clr it in the dongle AOE */ dhd_aoe_hostip_clr(dhd_pub); - for (i = 0; i < 8; i++) { + if (ret) { + DHD_ERROR(("%s failed\n", __FUNCTION__)); + return; + } + + for (i = 0; i < MAX_IPV4_ENTRIES; i++) { if (add && (ipv4_buf[i] == 0)) { @@ -3046,6 +3061,7 @@ void dhd_detach(dhd_pub_t *dhdp) { dhd_info_t *dhd; unsigned long flags; + int timer_valid = FALSE; if (!dhdp) return; @@ -3107,17 +3123,23 @@ void dhd_detach(dhd_pub_t *dhdp) if (ifp->net->netdev_ops == &dhd_ops_pri) #endif { - unregister_netdev(ifp->net); + if (ifp->net) { + unregister_netdev(ifp->net); + free_netdev(ifp->net); + ifp->net = NULL; + } MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); - + dhd->iflist[0] = NULL; } } /* Clear the watchdog timer */ flags = dhd_os_spin_lock(&dhd->pub); + timer_valid = dhd->wd_timer_valid; dhd->wd_timer_valid = FALSE; dhd_os_spin_unlock(&dhd->pub, flags); - del_timer_sync(&dhd->timer); + if (timer_valid) + del_timer_sync(&dhd->timer); if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) { #ifdef DHDTHREAD @@ -3248,6 +3270,9 @@ dhd_module_init(void) goto fail_2; } #endif +#if defined(WL_CFG80211) + error = wl_android_post_init(); +#endif return error; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && 1 @@ -4116,6 +4141,30 @@ int net_os_wake_unlock(struct net_device *dev) return ret; } +int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) +{ + int ifidx; + int ret = 0; + dhd_info_t *dhd = NULL; + + if (!net || !netdev_priv(net)) { + DHD_ERROR(("%s invalid parameter\n", __FUNCTION__)); + return -EINVAL; + } + + dhd = *(dhd_info_t **)netdev_priv(net); + ifidx = dhd_net2idx(dhd, net); + if (ifidx == DHD_BAD_IF) { + DHD_ERROR(("%s bad ifidx\n", __FUNCTION__)); + return -ENODEV; + } + + DHD_OS_WAKE_LOCK(&dhd->pub); + ret = dhd_wl_ioctl(&dhd->pub, ifidx, ioc, ioc->buf, ioc->len); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + + return ret; +} #ifdef PROP_TXSTATUS extern int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t action, uint8 ifid, diff --git a/drivers/net/wireless/bcmdhd/dhd_linux_mon.c b/drivers/net/wireless/bcmdhd/dhd_linux_mon.c index a51db01a870..6c1ff4d8ad4 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux_mon.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux_mon.c @@ -30,6 +30,7 @@ #include <linux/etherdevice.h> #include <linux/if_arp.h> #include <linux/ieee80211.h> +#include <linux/rtnetlink.h> #include <net/ieee80211_radiotap.h> #include <wlioctl.h> @@ -39,10 +40,14 @@ #include <dngl_stats.h> #include <dhd.h> -/* - * Some external functions, TODO: move them to dhd_linux.h - */ -int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); +typedef enum monitor_states +{ + MONITOR_STATE_DEINIT = 0x0, + MONITOR_STATE_INIT = 0x1, + MONITOR_STATE_INTERFACE_ADDED = 0x2, + MONITOR_STATE_INTERFACE_DELETED = 0x4 +} monitor_states_t; +extern int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); /** * Local declarations and defintions (not exposed) @@ -58,9 +63,9 @@ typedef struct monitor_interface { typedef struct dhd_linux_monitor { void *dhd_pub; + monitor_states_t monitor_state; monitor_interface mon_if[DHD_MAX_IFS]; - int count; /* Number of total monitor interface */ - struct mutex lock; /* lock to protect count and mon_if */ + struct mutex lock; /* lock to protect mon_if */ } dhd_linux_monitor_t; static dhd_linux_monitor_t g_monitor; @@ -145,6 +150,7 @@ static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *n int dot11_hdr_len = 24; int snap_len = 6; unsigned char *pdata; + unsigned short frame_ctl; unsigned char src_mac_addr[6]; unsigned char dst_mac_addr[6]; struct ieee80211_hdr *dot11_hdr; @@ -176,35 +182,35 @@ static int dhd_mon_if_subif_start_xmit(struct sk_buff *skb, struct net_device *n skb_pull(skb, rtap_len); dot11_hdr = (struct ieee80211_hdr *)skb->data; - + frame_ctl = le16_to_cpu(dot11_hdr->frame_control); /* Check if the QoS bit is set */ - if (dot11_hdr->frame_control & 0x0080) - qos_len = 2; - - /* Check if this ia a Wireless Distribution System (WDS) frame - * which has 4 MAC addresses - */ - if ((dot11_hdr->frame_control & 0x0300) == 0x0300) - dot11_hdr_len += 6; - - memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr)); - memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr)); - - /* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for - * for two MAC addresses - */ - skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2); - pdata = (unsigned char*)skb->data; - memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr)); - memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr)); - - MON_PRINT("if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); - - /* Use the real net device to transmit the packet */ - ret = dhd_start_xmit(skb, mon_if->real_ndev); - - return ret; - + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { + /* Check if this ia a Wireless Distribution System (WDS) frame + * which has 4 MAC addresses + */ + if (dot11_hdr->frame_control & 0x0080) + qos_len = 2; + if ((dot11_hdr->frame_control & 0x0300) == 0x0300) + dot11_hdr_len += 6; + + memcpy(dst_mac_addr, dot11_hdr->addr1, sizeof(dst_mac_addr)); + memcpy(src_mac_addr, dot11_hdr->addr2, sizeof(src_mac_addr)); + + /* Skip the 802.11 header, QoS (if any) and SNAP, but leave spaces for + * for two MAC addresses + */ + skb_pull(skb, dot11_hdr_len + qos_len + snap_len - sizeof(src_mac_addr) * 2); + pdata = (unsigned char*)skb->data; + memcpy(pdata, dst_mac_addr, sizeof(dst_mac_addr)); + memcpy(pdata + sizeof(dst_mac_addr), src_mac_addr, sizeof(src_mac_addr)); + + MON_PRINT("if name: %s, matched if name %s\n", ndev->name, mon_if->real_ndev->name); + + /* Use the real net device to transmit the packet */ + ret = dhd_start_xmit(skb, mon_if->real_ndev); + + return ret; + } fail: dev_kfree_skb(skb); return 0; @@ -242,7 +248,8 @@ static int dhd_mon_if_change_mac(struct net_device *ndev, void *addr) int dhd_add_monitor(char *name, struct net_device **new_ndev) { - int idx; + int i; + int idx = -1; int ret = 0; struct net_device* ndev = NULL; dhd_linux_monitor_t **dhd_mon; @@ -256,7 +263,15 @@ int dhd_add_monitor(char *name, struct net_device **new_ndev) goto out; } - if (g_monitor.count >= DHD_MAX_IFS) { + /* + * Find a vacancy + */ + for (i = 0; i < DHD_MAX_IFS; i++) + if (g_monitor.mon_if[i].mon_ndev == NULL) { + idx = i; + break; + } + if (idx == -1) { MON_PRINT("exceeds maximum interfaces\n"); ret = -EFAULT; goto out; @@ -281,14 +296,12 @@ int dhd_add_monitor(char *name, struct net_device **new_ndev) } *new_ndev = ndev; - idx = g_monitor.count; g_monitor.mon_if[idx].radiotap_enabled = TRUE; g_monitor.mon_if[idx].mon_ndev = ndev; g_monitor.mon_if[idx].real_ndev = lookup_real_netdev(name); - g_monitor.count++; dhd_mon = (dhd_linux_monitor_t **)netdev_priv(ndev); *dhd_mon = &g_monitor; - + g_monitor.monitor_state = MONITOR_STATE_INTERFACE_ADDED; MON_PRINT("net device returned: 0x%p\n", ndev); MON_PRINT("found a matched net device, name %s\n", g_monitor.mon_if[idx].real_ndev->name); @@ -301,10 +314,49 @@ out: } +int dhd_del_monitor(struct net_device *ndev) +{ + int i; + bool rollback_lock = false; + if (!ndev) + return -EINVAL; + mutex_lock(&g_monitor.lock); + for (i = 0; i < DHD_MAX_IFS; i++) { + if (g_monitor.mon_if[i].mon_ndev == ndev || + g_monitor.mon_if[i].real_ndev == ndev) { + g_monitor.mon_if[i].real_ndev = NULL; + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + unregister_netdev(g_monitor.mon_if[i].mon_ndev); + free_netdev(g_monitor.mon_if[i].mon_ndev); + g_monitor.mon_if[i].mon_ndev = NULL; + g_monitor.monitor_state = MONITOR_STATE_INTERFACE_DELETED; + break; + } + } + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + + if (g_monitor.monitor_state != + MONITOR_STATE_INTERFACE_DELETED) + MON_PRINT("interface not found in monitor IF array, is this a monitor IF? 0x%p\n", + ndev); + mutex_unlock(&g_monitor.lock); + + return 0; +} + int dhd_monitor_init(void *dhd_pub) { - g_monitor.dhd_pub = dhd_pub; - mutex_init(&g_monitor.lock); + if (g_monitor.monitor_state == MONITOR_STATE_DEINIT) { + g_monitor.dhd_pub = dhd_pub; + mutex_init(&g_monitor.lock); + g_monitor.monitor_state = MONITOR_STATE_INIT; + } return 0; } @@ -312,20 +364,28 @@ int dhd_monitor_uninit(void) { int i; struct net_device *ndev; - + bool rollback_lock = false; mutex_lock(&g_monitor.lock); - - for (i = 0; i < DHD_MAX_IFS; i++) { - ndev = g_monitor.mon_if[i].mon_ndev; - if (ndev) { - unregister_netdev(ndev); - free_netdev(ndev); - g_monitor.mon_if[i].real_ndev = NULL; - g_monitor.mon_if[i].mon_ndev = NULL; - g_monitor.count--; + if (g_monitor.monitor_state != MONITOR_STATE_DEINIT) { + for (i = 0; i < DHD_MAX_IFS; i++) { + ndev = g_monitor.mon_if[i].mon_ndev; + if (ndev) { + if (rtnl_is_locked()) { + rtnl_unlock(); + rollback_lock = true; + } + unregister_netdev(ndev); + free_netdev(ndev); + g_monitor.mon_if[i].real_ndev = NULL; + g_monitor.mon_if[i].mon_ndev = NULL; + if (rollback_lock) { + rtnl_lock(); + rollback_lock = false; + } + } } + g_monitor.monitor_state = MONITOR_STATE_DEINIT; } - mutex_unlock(&g_monitor.lock); return 0; } diff --git a/drivers/net/wireless/bcmdhd/dhd_sdio.c b/drivers/net/wireless/bcmdhd/dhd_sdio.c index 2dbb8027569..9f7c416bf3f 100644 --- a/drivers/net/wireless/bcmdhd/dhd_sdio.c +++ b/drivers/net/wireless/bcmdhd/dhd_sdio.c @@ -395,7 +395,7 @@ static bool dhd_readahead; (((uint8)(bus->tx_max - bus->tx_seq) > 1) && \ (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) -/* To check if there's window offered for ctrl frame*/ +/* To check if there's window offered for ctrl frame */ #define TXCTLOK(bus) \ (((uint8)(bus->tx_max - bus->tx_seq) != 0) && \ (((uint8)(bus->tx_max - bus->tx_seq) & 0x80) == 0)) @@ -1026,7 +1026,9 @@ dhdsdio_txpkt(dhd_bus_t *bus, void *pkt, uint chan, bool free_pkt) htol32_ua_store(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader)); #ifdef DHD_DEBUG - tx_packets[PKTPRIO(pkt)]++; + if (PKTPRIO(pkt) < ARRAYSIZE(tx_packets)) { + tx_packets[PKTPRIO(pkt)]++; + } if (DHD_BYTES_ON() && (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) || (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) { @@ -4598,7 +4600,7 @@ clkwait: framecnt = dhdsdio_sendfromq(bus, framecnt); txlimit -= framecnt; } - /* Resched the DPC if ctrl cmd is pending on bus credit*/ + /* Resched the DPC if ctrl cmd is pending on bus credit */ if (bus->ctrl_frame_stat) resched = TRUE; @@ -4958,6 +4960,14 @@ dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq) } #endif /* SDTEST */ +extern void +dhd_disable_intr(dhd_pub_t *dhdp) +{ + dhd_bus_t *bus; + bus = dhdp->bus; + bcmsdh_intr_disable(bus->sdh); +} + extern bool dhd_bus_watchdog(dhd_pub_t *dhdp) { @@ -6067,6 +6077,7 @@ _dhdsdio_download_firmware(struct dhd_bus *bus) /* External nvram takes precedence if specified */ if (dhdsdio_download_nvram(bus)) { DHD_ERROR(("%s: dongle nvram file download failed\n", __FUNCTION__)); + goto err; } /* Take arm out of reset */ diff --git a/drivers/net/wireless/bcmdhd/include/epivers.h b/drivers/net/wireless/bcmdhd/include/epivers.h index 3dfcc150301..b94174eff0c 100644 --- a/drivers/net/wireless/bcmdhd/include/epivers.h +++ b/drivers/net/wireless/bcmdhd/include/epivers.h @@ -33,17 +33,17 @@ #define EPI_RC_NUMBER 125 -#define EPI_INCREMENTAL_NUMBER 48 +#define EPI_INCREMENTAL_NUMBER 60 #define EPI_BUILD_NUMBER 0 -#define EPI_VERSION 5, 90, 125, 48 +#define EPI_VERSION 5, 90, 125, 60 -#define EPI_VERSION_NUM 0x055a7d30 +#define EPI_VERSION_NUM 0x055a7d3c #define EPI_VERSION_DEV 5.90.125 -#define EPI_VERSION_STR "5.90.125.48" +#define EPI_VERSION_STR "5.90.125.60" #endif diff --git a/drivers/net/wireless/bcmdhd/include/linuxver.h b/drivers/net/wireless/bcmdhd/include/linuxver.h index b64d0bb6a00..e1c62b73f15 100644 --- a/drivers/net/wireless/bcmdhd/include/linuxver.h +++ b/drivers/net/wireless/bcmdhd/include/linuxver.h @@ -70,6 +70,7 @@ #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/netdevice.h> +#include <linux/semaphore.h> #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) #undef IP_TOS #endif @@ -123,11 +124,9 @@ typedef irqreturn_t(*FN_ISR) (int irq, void *dev_id, struct pt_regs *ptregs); #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 67) -#ifndef SANDGATE2G #define MOD_INC_USE_COUNT #define MOD_DEC_USE_COUNT #endif -#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) #include <linux/sched.h> diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h index 7f51faa0d3d..30ec848c40a 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h @@ -181,7 +181,8 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event { #define WLC_E_EXCESS_PM_WAKE_EVENT 81 #define WLC_E_PFN_SCAN_NONE 82 #define WLC_E_PFN_SCAN_ALLGONE 83 -#define WLC_E_LAST 84 +#define WLC_E_GTK_PLUMBED 84 +#define WLC_E_LAST 85 typedef struct { diff --git a/drivers/net/wireless/bcmdhd/include/wlioctl.h b/drivers/net/wireless/bcmdhd/include/wlioctl.h index f1af5b1841b..a09dd269264 100644 --- a/drivers/net/wireless/bcmdhd/include/wlioctl.h +++ b/drivers/net/wireless/bcmdhd/include/wlioctl.h @@ -190,6 +190,7 @@ typedef struct wlc_ssid { #define WL_SCANFLAGS_RESERVED 0x02 #define WL_SCANFLAGS_PROHIBITED 0x04 +#define WL_SCAN_PARAMS_SSID_MAX 10 typedef struct wl_scan_params { wlc_ssid_t ssid; struct ether_addr bssid; @@ -1747,7 +1748,18 @@ struct wl_msglevel2 { uint32 high; }; - +typedef struct wl_mkeep_alive_pkt { + uint16 version; + uint16 length; + uint32 period_msec; + uint16 len_bytes; + uint8 keep_alive_id; + uint8 data[1]; +} wl_mkeep_alive_pkt_t; + +#define WL_MKEEP_ALIVE_VERSION 1 +#define WL_MKEEP_ALIVE_FIXED_LEN OFFSETOF(wl_mkeep_alive_pkt_t, data) +#define WL_MKEEP_ALIVE_PRECISION 500 #define WLC_ROAM_TRIGGER_DEFAULT 0 #define WLC_ROAM_TRIGGER_BANDWIDTH 1 diff --git a/drivers/net/wireless/bcmdhd/wl_android.c b/drivers/net/wireless/bcmdhd/wl_android.c index 876fc080ac1..d6471d99106 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.c +++ b/drivers/net/wireless/bcmdhd/wl_android.c @@ -92,7 +92,7 @@ typedef struct cmd_tlv { char subver; char reserved; } cmd_tlv_t; -#endif +#endif /* PNO_SUPPORT */ typedef struct android_wifi_priv_cmd { char *buf; @@ -149,22 +149,25 @@ static int wl_android_get_link_speed(struct net_device *net, char *command, int static int wl_android_get_rssi(struct net_device *net, char *command, int total_len) { - wlc_ssid_t ssid; + wlc_ssid_t ssid = {0}; int rssi; - int bytes_written; + int bytes_written = 0; int error; error = wldev_get_rssi(net, &rssi); if (error) return -1; - error = wldev_get_ssid(net, &ssid); if (error) return -1; - memcpy(command, ssid.SSID, ssid.SSID_len); - bytes_written = ssid.SSID_len; + if ((ssid.SSID_len == 0) || (ssid.SSID_len > DOT11_MAX_SSID_LEN)) { + DHD_ERROR(("%s: wldev_get_ssid failed\n", __FUNCTION__)); + } else { + memcpy(command, ssid.SSID, ssid.SSID_len); + bytes_written = ssid.SSID_len; + } bytes_written += snprintf(&command[bytes_written], total_len, " rssi %d", rssi); - DHD_INFO(("%s: command result is %s \n", __FUNCTION__, command)); + DHD_INFO(("%s: command result is %s (%d)\n", __FUNCTION__, command, bytes_written)); return bytes_written; } @@ -216,7 +219,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t int pno_repeat = 0; int pno_freq_expo_max = 0; - DHD_ERROR(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); + DHD_INFO(("%s: command=%s, len=%d\n", __FUNCTION__, command, total_len)); if (total_len < (strlen(CMD_PNOSETUP_SET) + sizeof(cmd_tlv_t))) { DHD_ERROR(("%s argument=%d less min size\n", __FUNCTION__, total_len)); @@ -280,7 +283,7 @@ static int wl_android_set_pno_setup(struct net_device *dev, char *command, int t exit_proc: return res; } -#endif +#endif /* PNO_SUPPORT */ static int wl_android_get_p2p_dev_addr(struct net_device *ndev, char *command, int total_len) { @@ -335,10 +338,10 @@ int wl_android_wifi_off(struct net_device *dev) dhd_net_if_lock(dev); if (g_wifi_on) { dhd_dev_reset(dev, 1); - dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); sdioh_stop(NULL); /* clean up dtim_skip setting */ net_os_set_dtim_skip(dev, TRUE); + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); g_wifi_on = 0; } dhd_net_if_unlock(dev); @@ -367,24 +370,26 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) int ret = 0; char *command = NULL; int bytes_written = 0; - android_wifi_priv_cmd *priv_cmd; + android_wifi_priv_cmd priv_cmd; net_os_wake_lock(net); - priv_cmd = (android_wifi_priv_cmd*)ifr->ifr_data; - if (!priv_cmd) - { + if (!ifr->ifr_data) { ret = -EINVAL; goto exit; } - command = kmalloc(priv_cmd->total_len, GFP_KERNEL); + if (copy_from_user(&priv_cmd, ifr->ifr_data, sizeof(android_wifi_priv_cmd))) { + ret = -EFAULT; + goto exit; + } + command = kmalloc(priv_cmd.total_len, GFP_KERNEL); if (!command) { DHD_ERROR(("%s: failed to allocate memory\n", __FUNCTION__)); ret = -ENOMEM; goto exit; } - if (copy_from_user(command, priv_cmd->buf, priv_cmd->total_len)) { + if (copy_from_user(command, priv_cmd.buf, priv_cmd.total_len)) { ret = -EFAULT; goto exit; } @@ -396,12 +401,12 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) bytes_written = wl_android_wifi_on(net); } else if (strnicmp(command, CMD_SETFWPATH, strlen(CMD_SETFWPATH)) == 0) { - bytes_written = wl_android_set_fwpath(net, command, priv_cmd->total_len); + bytes_written = wl_android_set_fwpath(net, command, priv_cmd.total_len); } if (!g_wifi_on) { DHD_ERROR(("%s: Ignore private cmd \"%s\" - iface %s is down\n", - __FUNCTION__, command, ifr->ifr_name)); + __FUNCTION__, command, ifr->ifr_name)); ret = 0; goto exit; } @@ -416,10 +421,10 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) /* TBD: SCAN-PASSIVE */ } else if (strnicmp(command, CMD_RSSI, strlen(CMD_RSSI)) == 0) { - bytes_written = wl_android_get_rssi(net, command, priv_cmd->total_len); + bytes_written = wl_android_get_rssi(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_LINKSPEED, strlen(CMD_LINKSPEED)) == 0) { - bytes_written = wl_android_get_link_speed(net, command, priv_cmd->total_len); + bytes_written = wl_android_get_link_speed(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_RXFILTER_START, strlen(CMD_RXFILTER_START)) == 0) { bytes_written = net_os_set_packet_filter(net, 1); @@ -445,14 +450,14 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) /* TBD: BTCOEXMODE */ } else if (strnicmp(command, CMD_SETSUSPENDOPT, strlen(CMD_SETSUSPENDOPT)) == 0) { - bytes_written = wl_android_set_suspendopt(net, command, priv_cmd->total_len); + bytes_written = wl_android_set_suspendopt(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_SETBAND, strlen(CMD_SETBAND)) == 0) { uint band = *(command + strlen(CMD_SETBAND) + 1) - '0'; bytes_written = wldev_set_band(net, band); } else if (strnicmp(command, CMD_GETBAND, strlen(CMD_GETBAND)) == 0) { - bytes_written = wl_android_get_band(net, command, priv_cmd->total_len); + bytes_written = wl_android_get_band(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_COUNTRY, strlen(CMD_COUNTRY)) == 0) { char *country_code = command + strlen(CMD_COUNTRY) + 1; @@ -463,7 +468,7 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) bytes_written = dhd_dev_pno_reset(net); } else if (strnicmp(command, CMD_PNOSETUP_SET, strlen(CMD_PNOSETUP_SET)) == 0) { - bytes_written = wl_android_set_pno_setup(net, command, priv_cmd->total_len); + bytes_written = wl_android_set_pno_setup(net, command, priv_cmd.total_len); } else if (strnicmp(command, CMD_PNOENABLE_SET, strlen(CMD_PNOENABLE_SET)) == 0) { uint pfn_enabled = *(command + strlen(CMD_PNOENABLE_SET) + 1) - '0'; @@ -471,7 +476,7 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) } #endif else if (strnicmp(command, CMD_P2P_DEV_ADDR, strlen(CMD_P2P_DEV_ADDR)) == 0) { - bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd->total_len); + bytes_written = wl_android_get_p2p_dev_addr(net, command, priv_cmd.total_len); } else { DHD_ERROR(("Unknown PRIVATE command %s - ignored\n", command)); snprintf(command, 3, "OK"); @@ -479,9 +484,14 @@ int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd) } if (bytes_written > 0) { - bytes_written++; - priv_cmd->used_len = bytes_written; - if (copy_to_user(priv_cmd->buf, command, bytes_written)) { + if (bytes_written > priv_cmd.total_len) { + DHD_ERROR(("%s: bytes_written = %d\n", __FUNCTION__, bytes_written)); + bytes_written = priv_cmd.total_len; + } else { + bytes_written++; + } + priv_cmd.used_len = bytes_written; + if (copy_to_user(priv_cmd.buf, command, bytes_written)) { DHD_ERROR(("%s: failed to copy data to user buffer\n", __FUNCTION__)); ret = -EFAULT; } @@ -520,6 +530,17 @@ int wl_android_exit(void) return ret; } +int wl_android_post_init(void) +{ + int ret = 0; + if (!dhd_download_fw_on_driverload) { + /* Call customer gpio to turn off power with WL_REG_ON signal */ + dhd_customer_gpio_wlan_ctrl(WLAN_RESET_OFF); + g_wifi_on = 0; + + } + return ret; +} /** * Functions for Android WiFi card detection diff --git a/drivers/net/wireless/bcmdhd/wl_android.h b/drivers/net/wireless/bcmdhd/wl_android.h index 3c75bfb820d..17373b7f6d5 100644 --- a/drivers/net/wireless/bcmdhd/wl_android.h +++ b/drivers/net/wireless/bcmdhd/wl_android.h @@ -1,9 +1,27 @@ /* * Linux cfg80211 driver - Android related functions * - * $Copyright Open Broadcom Corporation$ + * Copyright (C) 1999-2011, Broadcom Corporation * - * $Id: wl_android.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 linm Exp $ + * Unless you and Broadcom execute a separate written software license + * agreement governing use of this software, this software is licensed to you + * under the terms of the GNU General Public License version 2 (the "GPL"), + * available at http://www.broadcom.com/licenses/GPLv2.php, with the + * following added to such license: + * + * As a special exception, the copyright holders of this software give you + * permission to link this software with independent modules, and to copy and + * distribute the resulting executable under terms of your choice, provided that + * you also meet, for each linked independent module, the terms and conditions of + * the license of that module. An independent module is a module which is not + * derived from this software. The special exception does not apply to any + * modifications of the software. + * + * Notwithstanding the above, under no circumstances may you combine this + * software in any way with any other Broadcom software provided under a license + * other than the GPL, without Broadcom's express prior written consent. + * + * $Id: wl_android.c,v 1.1.4.1.2.14 2011/02/09 01:40:07 Exp $ */ #include <linux/module.h> @@ -22,7 +40,7 @@ */ int wl_android_init(void); int wl_android_exit(void); - +int wl_android_post_init(void); int wl_android_wifi_on(struct net_device *dev); int wl_android_wifi_off(struct net_device *dev); int wl_android_priv_cmd(struct net_device *net, struct ifreq *ifr, int cmd); @@ -36,4 +54,4 @@ int wifi_get_irq_number(unsigned long *irq_flags_ptr); int wifi_set_power(int on, unsigned long msec); int wifi_get_mac_addr(unsigned char *buf); void *wifi_get_country_code(char *ccode); -#endif /* CONFIG_WIFI_CONTROL_FUNC */
\ No newline at end of file +#endif /* CONFIG_WIFI_CONTROL_FUNC */ diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.c b/drivers/net/wireless/bcmdhd/wl_cfg80211.c index 201cb618844..ed2e5dd6994 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.c +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.c @@ -32,9 +32,7 @@ /* * sys proc file will be REMOVED in next release */ -#undef CONFIG_SYSCTL - -#ifdef CONFIG_SYSCTL +#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) #include <linux/sysctl.h> #endif @@ -85,10 +83,10 @@ u32 wl_dbg_level = WL_DBG_ERR; #define WL_TRACE(a) printk("%s ", __FUNCTION__); printk a #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" -#define MAX_WAIT_TIME 3000 +#define MAX_WAIT_TIME 1500 static s8 ioctlbuf[WLC_IOCTL_MAXLEN]; -#ifdef CONFIG_SYSCTL +#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) #define MAC_STRING_LEN (sizeof(u8) * 17) u8 wl_sysctl_macstring[2][MAC_STRING_LEN]; @@ -123,7 +121,7 @@ static ctl_table wl_sysctl_table[] = { {0} }; static struct ctl_table_header *wl_sysctl_hdr; -#endif /* CONFIG_SYSCTL */ +#endif /* CONFIG_SYSCTL */ /* This is to override regulatory domains defined in cfg80211 module (reg.c) * By default world regulatory domain defined in reg.c puts the flags NL80211_RRF_PASSIVE_SCAN @@ -138,28 +136,25 @@ static const struct ieee80211_regdomain brcm_regdom = { /* IEEE 802.11b/g, channels 1..11 */ REG_RULE(2412-10, 2462+10, 40, 6, 20, 0), /* IEEE 802.11b/g, channels 12..13. No HT40 - * channel fits here. */ + * channel fits here. + */ REG_RULE(2467-10, 2472+10, 20, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS), + NL80211_RRF_PASSIVE_SCAN | + NL80211_RRF_NO_IBSS), /* IEEE 802.11 channel 14 - Only JP enables - * this and for 802.11b only */ + * this and for 802.11b only + */ REG_RULE(2484-10, 2484+10, 20, 6, 20, - NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS | - NL80211_RRF_NO_OFDM), + NL80211_RRF_PASSIVE_SCAN | + NL80211_RRF_NO_IBSS | + NL80211_RRF_NO_OFDM), /* IEEE 802.11a, channel 36..48 */ - REG_RULE(5180-10, 5240+10, 40, 6, 20, 0 - /*NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS*/), + REG_RULE(5180-10, 5240+10, 40, 6, 20, 0), /* NB: 5260 MHz - 5700 MHz requies DFS */ /* IEEE 802.11a, channel 149..165 */ - REG_RULE(5745-10, 5825+10, 40, 6, 20, 0 - /*NL80211_RRF_PASSIVE_SCAN | - NL80211_RRF_NO_IBSS*/), - } + REG_RULE(5745-10, 5825+10, 40, 6, 20, 0), } }; @@ -170,8 +165,32 @@ static const struct ieee80211_regdomain brcm_regdom = { #define WPS_ID_VERSION 0x104A #define WPS_ID_DEVICE_PWD_ID 0x1012 #define WPS_ID_REQ_DEV_TYPE 0x106A +#define WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS 0x1053 #define WPS_ID_PRIM_DEV_TYPE 0x1054 +/* Device Password ID */ +#define DEV_PW_DEFAULT 0x0000 +#define DEV_PW_USER_SPECIFIED 0x0001, +#define DEV_PW_MACHINE_SPECIFIED 0x0002 +#define DEV_PW_REKEY 0x0003 +#define DEV_PW_PUSHBUTTON 0x0004 +#define DEV_PW_REGISTRAR_SPECIFIED 0x0005 + +/* Config Methods */ +#define WPS_CONFIG_USBA 0x0001 +#define WPS_CONFIG_ETHERNET 0x0002 +#define WPS_CONFIG_LABEL 0x0004 +#define WPS_CONFIG_DISPLAY 0x0008 +#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010 +#define WPS_CONFIG_INT_NFC_TOKEN 0x0020 +#define WPS_CONFIG_NFC_INTERFACE 0x0040 +#define WPS_CONFIG_PUSHBUTTON 0x0080 +#define WPS_CONFIG_KEYPAD 0x0100 +#define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280 +#define WPS_CONFIG_PHY_PUSHBUTTON 0x0480 +#define WPS_CONFIG_VIRT_DISPLAY 0x2008 +#define WPS_CONFIG_PHY_DISPLAY 0x4008 + /* * cfg80211_ops api/callback list */ @@ -194,10 +213,6 @@ static s32 wl_cfg80211_get_station(struct wiphy *wiphy, static s32 wl_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, bool enabled, s32 timeout); -static s32 wl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, - struct net_device *dev, - const u8 *addr, - const struct cfg80211_bitrate_mask *mask); static int wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme); static s32 wl_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, @@ -264,7 +279,6 @@ static s32 wl_bss_roaming_done(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); static s32 wl_notify_mic_status(struct wl_priv *wl, struct net_device *ndev, const wl_event_msg_t *e, void *data); - /* * register/deregister sdio function */ @@ -407,7 +421,7 @@ static s32 wl_config_dongle(struct wl_priv *wl, bool need_lock); */ static void wl_iscan_timer(unsigned long data); static void wl_term_iscan(struct wl_priv *wl); -static s32 wl_init_iscan(struct wl_priv *wl); +static s32 wl_init_scan(struct wl_priv *wl); static s32 wl_iscan_thread(void *data); static s32 wl_run_iscan(struct wl_iscan_ctrl *iscan, struct wlc_ssid *ssid, u16 action); @@ -448,13 +462,16 @@ static void wl_debugfs_remove_netdev(struct wl_priv *wl); /* * rfkill support */ -static int wl_setup_rfkill(struct wl_priv *wl); +static int wl_setup_rfkill(struct wl_priv *wl, bool setup); static int wl_rfkill_set(void *data, bool blocked); /* * Some external functions, TODO: move them to dhd_linux.h */ int dhd_add_monitor(char *name, struct net_device **new_ndev); +int dhd_del_monitor(struct net_device *ndev); +int dhd_monitor_init(void *dhd_pub); +int dhd_monitor_uninit(void); int dhd_start_xmit(struct sk_buff *skb, struct net_device *net); #define WL_PRIV_GET() \ @@ -478,9 +495,11 @@ do { \ } \ } while (0) + #define IS_WPA_AKM(akm) ((akm) == RSN_AKM_NONE || \ - (akm) == RSN_AKM_UNSPECIFIED || \ - (akm) == RSN_AKM_PSK) + (akm) == RSN_AKM_UNSPECIFIED || \ + (akm) == RSN_AKM_PSK) + extern int dhd_wait_pend8021x(struct net_device *dev); @@ -762,7 +781,7 @@ static void swap_key_to_BE(struct wl_wsec_key *key) /* For debug: Dump the contents of the encoded wps ie buffe */ static void -wl_dbg_dump_wps_ie(char *wps_ie) +wl_validate_wps_ie(char *wps_ie, bool *pbc) { #define WPS_IE_FIXED_LEN 6 u16 len = (u16) wps_ie[TLV_LEN_OFF]; @@ -808,6 +827,7 @@ wl_dbg_dump_wps_ie(char *wps_ie) valptr[0] = *subel; valptr[1] = *(subel + 1); WL_DBG((" attr WPS_ID_DEVICE_PWD_ID: %u\n", HTON16(val))); + *pbc = (HTON16(val) == DEV_PW_PUSHBUTTON) ? true : false; } else if (subelt_id == WPS_ID_PRIM_DEV_TYPE) { valptr[0] = *subel; valptr[1] = *(subel + 1); @@ -822,6 +842,11 @@ wl_dbg_dump_wps_ie(char *wps_ie) valptr[0] = *(subel + 6); valptr[1] = *(subel + 7); WL_DBG((" attr WPS_ID_REQ_DEV_TYPE: subcat=%u\n", HTON16(val))); + } else if (subelt_id == WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS) { + valptr[0] = *subel; + valptr[1] = *(subel + 1); + WL_DBG((" attr WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS" + ": cat=%u\n", HTON16(val))); } else { WL_DBG((" unknown attr 0x%x\n", subelt_id)); } @@ -836,7 +861,7 @@ static struct net_device* wl_cfg80211_add_monitor_if(char *name) struct net_device* ndev = NULL; ret = dhd_add_monitor(name, &ndev); - WL_ERR(("wl_cfg80211_add_monitor_if net device returned: 0x%p\n", ndev)); + WL_INFO(("wl_cfg80211_add_monitor_if net device returned: 0x%p\n", ndev)); return ndev; } @@ -854,6 +879,7 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, struct wl_priv *wl = WL_PRIV_GET(); struct net_device *_ndev; dhd_pub_t *dhd = (dhd_pub_t *)(wl->pub); + int (*net_attach)(dhd_pub_t *dhdp, int ifidx); WL_DBG(("if name: %s, type: %d\n", name, type)); switch (type) { @@ -918,53 +944,64 @@ wl_cfg80211_add_virtual_iface(struct wiphy *wiphy, char *name, strncpy(wl->p2p->vir_ifname, name, IFNAMSIZ - 1); wl_cfgp2p_generate_bss_mac(&dhd->mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); - /* Temporary use channel 11, in case GO will be changed with set_channel API */ - chspec = wf_chspec_aton(WL_P2P_TEMP_CHAN); + /* Temporary use channel 11, in case GO will be changed with set_channel API */ + chspec = wf_chspec_aton(WL_P2P_TEMP_CHAN); - /* For P2P mode, use P2P-specific driver features to create the - * bss: "wl p2p_ifadd" - */ - wl_set_p2p_status(wl, IF_ADD); + /* For P2P mode, use P2P-specific driver features to create the + * bss: "wl p2p_ifadd" + */ + wl_set_p2p_status(wl, IF_ADD); err = wl_cfgp2p_ifadd(wl, &wl->p2p->int_addr, htod32(wlif_type), chspec); - if (unlikely(err)) - return ERR_PTR(-ENOMEM); + if (unlikely(err)) + return ERR_PTR(-ENOMEM); - timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, + timeout = wait_event_interruptible_timeout(wl->dongle_event_wait, (wl_get_p2p_status(wl, IF_ADD) == false), - msecs_to_jiffies(MAX_WAIT_TIME)); - if (timeout > 0 && (!wl_get_p2p_status(wl, IF_ADD))) { - - struct wireless_dev *vwdev; - vwdev = kzalloc(sizeof(*vwdev), GFP_KERNEL); - if (unlikely(!vwdev)) { - WL_ERR(("Could not allocate wireless device\n")); - return ERR_PTR(-ENOMEM); - } - vwdev->wiphy = wl->wdev->wiphy; + msecs_to_jiffies(MAX_WAIT_TIME)); + if (timeout > 0 && (!wl_get_p2p_status(wl, IF_ADD))) { + + struct wireless_dev *vwdev; + vwdev = kzalloc(sizeof(*vwdev), GFP_KERNEL); + if (unlikely(!vwdev)) { + WL_ERR(("Could not allocate wireless device\n")); + return ERR_PTR(-ENOMEM); + } + vwdev->wiphy = wl->wdev->wiphy; WL_INFO((" virtual interface(%s) is created \n", wl->p2p->vir_ifname)); - index = alloc_idx_vwdev(wl); - wl->vwdev[index] = vwdev; - vwdev->iftype = - (wlif_type == WL_P2P_IF_CLIENT) ? NL80211_IFTYPE_STATION - : NL80211_IFTYPE_AP; - _ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); - _ndev->ieee80211_ptr = vwdev; - SET_NETDEV_DEV(_ndev, wiphy_dev(vwdev->wiphy)); - vwdev->netdev = _ndev; + index = alloc_idx_vwdev(wl); + wl->vwdev[index] = vwdev; + vwdev->iftype = + (wlif_type == WL_P2P_IF_CLIENT) ? NL80211_IFTYPE_STATION + : NL80211_IFTYPE_AP; + _ndev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); + _ndev->ieee80211_ptr = vwdev; + SET_NETDEV_DEV(_ndev, wiphy_dev(vwdev->wiphy)); + vwdev->netdev = _ndev; wl_set_drv_status(wl, READY); wl->p2p->vif_created = true; - set_mode_by_netdev(wl, _ndev, mode); - wl = wdev_to_wl(vwdev); - return _ndev; + set_mode_by_netdev(wl, _ndev, mode); + wl = wdev_to_wl(vwdev); + net_attach = wl_to_p2p_bss_private(wl, P2PAPI_BSSCFG_CONNECTION); + rtnl_unlock(); + if (net_attach && !net_attach(dhd, _ndev->ifindex)) + WL_DBG((" virtual interface(%s) is " + "created\n", wl->p2p->vir_ifname)); + else { + rtnl_lock(); + goto fail; + } + rtnl_lock(); + return _ndev; - } else { - wl_clr_p2p_status(wl, IF_ADD); + } else { + wl_clr_p2p_status(wl, IF_ADD); WL_ERR((" virtual interface(%s) is not created \n", wl->p2p->vir_ifname)); memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); wl->p2p->vif_created = false; } } +fail: return ERR_PTR(-ENODEV); } @@ -975,6 +1012,7 @@ wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) struct ether_addr p2p_mac; struct wl_priv *wl = WL_PRIV_GET(); s32 timeout = -1; + s32 ret = 0; if (wl->p2p_supported) { memcpy(p2p_mac.octet, wl->p2p->int_addr.octet, ETHER_ADDR_LEN); @@ -996,10 +1034,10 @@ wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, struct net_device *dev) } else { WL_ERR(("IFDEL didn't complete properly")); } + ret = dhd_del_monitor(dev); } } - - return 0; + return ret; } static s32 @@ -1080,11 +1118,12 @@ wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev, } s32 -wl_cfg80211_notify_ifadd(struct net_device *net) +wl_cfg80211_notify_ifadd(struct net_device *net, s32 idx, s32 bssidx, +int (*_net_attach)(dhd_pub_t *dhdp, int ifidx)) { struct wl_priv *wl = WL_PRIV_GET(); s32 ret = BCME_OK; - if (!net || !net->name) { + if (!net) { WL_ERR(("net is NULL\n")); return 0; } @@ -1094,9 +1133,11 @@ wl_cfg80211_notify_ifadd(struct net_device *net) /* Assign the net device to CONNECT BSSCFG */ strncpy(net->name, wl->p2p->vir_ifname, IFNAMSIZ - 1); wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION) = net; - wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = - P2PAPI_BSSCFG_CONNECTION; + wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_CONNECTION) = bssidx; + wl_to_p2p_bss_private(wl, P2PAPI_BSSCFG_CONNECTION) = _net_attach; + net->ifindex = idx; wl_clr_p2p_status(wl, IF_ADD); + wake_up_interruptible(&wl->dongle_event_wait); } return ret; @@ -1137,7 +1178,7 @@ wl_cfg80211_notify_ifdel(struct net_device *net) if (wl->p2p->vif_created) { s32 index = 0; - WL_DBG(("IF_DEL event called from dongle, _net name: %s, vif name: %s\n", + WL_DBG(("IF_DEL event called from dongle, _net name: %s, vif name: %s\n", net->name, wl->p2p->vir_ifname)); memset(wl->p2p->vir_ifname, '\0', IFNAMSIZ); @@ -1155,10 +1196,10 @@ wl_cfg80211_notify_ifdel(struct net_device *net) } } - wl_clr_p2p_status(wl, IF_DELETING); + wl_clr_p2p_status(wl, IF_DELETING); /* Wake up any waiting thread */ - wake_up_interruptible(&wl->dongle_event_wait); + wake_up_interruptible(&wl->dongle_event_wait); return 0; } @@ -2033,19 +2074,27 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, size_t join_params_size; s32 err = 0; - WL_TRACE(("In\n")); + WL_DBG(("In\n")); CHECK_SYS_UP(); + + /* + * Cancel ongoing scan to sync up with sme state machine of cfg80211. + */ + if (wl->scan_request) { + wl_cfg80211_scan_abort(wl, dev); + } + if (IS_P2P_SSID(sme->ssid) && (dev != wl_to_prmry_ndev(wl))) { /* we only allow to connect using virtual interface in case of P2P */ if (p2p_on(wl) && is_wps_conn(sme)) { WL_DBG(("p2p index : %d\n", wl_cfgp2p_find_idx(wl, dev))); /* Have to apply WPS IE + P2P IE in assoc req frame */ - wl_cfgp2p_set_managment_ie(wl, dev, + wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), VNDR_IE_PRBREQ_FLAG, wl_to_p2p_bss_saved_ie(wl, P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie, wl_to_p2p_bss_saved_ie(wl, P2PAPI_BSSCFG_DEVICE).p2p_probe_req_ie_len); - wl_cfgp2p_set_managment_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), + wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); } else if (p2p_on(wl) && (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)) { /* This is the connect req after WPS is done [credentials exchanged] @@ -2054,7 +2103,7 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, * the newly received IEs from Supplicant. This will remove the WPS IE from * the Assoc Req. */ - wl_cfgp2p_set_managment_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), + wl_cfgp2p_set_management_ie(wl, dev, wl_cfgp2p_find_idx(wl, dev), VNDR_IE_ASSOCREQ_FLAG, sme->ie, sme->ie_len); } @@ -2104,7 +2153,10 @@ wl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, memcpy(&join_params.ssid.SSID, sme->ssid, join_params.ssid.SSID_len); join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len); wl_update_prof(wl, NULL, &join_params.ssid, WL_PROF_SSID); - memcpy(&join_params.params.bssid, ðer_bcast, ETHER_ADDR_LEN); + if (sme->bssid) + memcpy(&join_params.params.bssid, sme->bssid, ETH_ALEN); + else + memcpy(&join_params.params.bssid, ðer_bcast, ETH_ALEN); wl_ch_to_chanspec(wl->channel, &join_params, &join_params_size); WL_DBG(("join_param_size %d\n", join_params_size)); @@ -2666,68 +2718,12 @@ static __used u32 wl_find_msb(u16 bit16) return ret; } -static s32 -wl_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, - const u8 *addr, - const struct cfg80211_bitrate_mask *mask) -{ - struct wl_rateset rateset; - s32 rate; - s32 val; - s32 err_bg; - s32 err_a; - u32 legacy; - s32 err = 0; - - CHECK_SYS_UP(); - /* addr param is always NULL. ignore it */ - /* Get current rateset */ - err = wldev_ioctl(dev, WLC_GET_CURR_RATESET, &rateset, - sizeof(rateset), false); - if (unlikely(err)) { - WL_ERR(("could not get current rateset (%d)\n", err)); - return err; - } - - rateset.count = dtoh32(rateset.count); - - legacy = wl_find_msb(mask->control[IEEE80211_BAND_2GHZ].legacy); - if (!legacy) - legacy = wl_find_msb(mask->control[IEEE80211_BAND_5GHZ].legacy); - - val = wl_g_rates[legacy - 1].bitrate * 100000; - - if (val < rateset.count) { - /* Select rate by rateset index */ - rate = rateset.rates[val] & 0x7f; - } else { - /* Specified rate in bps */ - rate = val / 500000; - } - - WL_DBG(("rate %d mbps\n", (rate / 2))); - - /* - * - * Set rate override, - * Since the is a/b/g-blind, both a/bg_rate are enforced. - */ - err_bg = wl_dev_intvar_set(dev, "bg_rate", rate); - err_a = wl_dev_intvar_set(dev, "a_rate", rate); - if (unlikely(err_bg && err_a)) { - WL_ERR(("could not set fixed rate (%d) (%d)\n", err_bg, err_a)); - return err_bg | err_a; - } - - return err; -} - static s32 wl_cfg80211_resume(struct wiphy *wiphy) { struct wl_priv *wl = WL_PRIV_GET(); s32 err = 0; - if (unlikely(!test_bit(WL_STATUS_READY, &wl->status))) { + if (unlikely(!wl_get_drv_status(wl, READY))) { WL_INFO(("device is not ready : status (%d)\n", (int)wl->status)); return 0; @@ -2747,7 +2743,7 @@ static s32 wl_cfg80211_suspend(struct wiphy *wiphy) struct wl_priv *wl = WL_PRIV_GET(); s32 err = 0; - if (unlikely(!test_bit(WL_STATUS_READY, &wl->status))) { + if (unlikely(!wl_get_drv_status(wl, READY))) { WL_INFO(("device is not ready : status (%d)\n", (int)wl->status)); return 0; @@ -2983,12 +2979,12 @@ wl_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, * without turning on P2P */ + p2p_on(wl) = true; err = wl_cfgp2p_enable_discovery(wl, dev, NULL, 0); if (unlikely(err)) { goto exit; } - p2p_on(wl) = true; } if (p2p_on(wl)) wl_cfgp2p_discover_listen(wl, target_channel, duration); @@ -3040,9 +3036,9 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, } if (wl->p2p_supported && p2p_on(wl)) { wl_cfgp2p_generate_bss_mac(&dhd->mac, &wl->p2p->dev_addr, &wl->p2p->int_addr); - /* Suspend P2P discovery search-listen to prevent it from changing the - * channel. - */ + /* Suspend P2P discovery search-listen to prevent it from changing the + * channel. + */ if ((err = wl_cfgp2p_discover_enable_search(wl, false)) < 0) { WL_ERR(("Can not disable discovery mode\n")); return -EFAULT; @@ -3076,17 +3072,15 @@ wl_cfg80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, */ wpsie_len = wps_ie->length + sizeof(wps_ie->length) + sizeof(wps_ie->tag); - wl_cfgp2p_set_managment_ie(wl, dev, bssidx, + wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_PRBRSP_FLAG, (u8 *)wps_ie, wpsie_len + p2pie_len); /* remove WLC_E_PROBREQ_MSG event to prevent HOSTAPD * from responding many probe request */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, false); } } cfg80211_mgmt_tx_status(dev, *cookie, buf, len, true, GFP_KERNEL); - goto exit; } else { /* Abort the dwell time of any previous off-channel action frame that may @@ -3484,6 +3478,7 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, wifi_p2p_ie_t *p2p_ie; bool is_bssup = false; bool update_bss = false; + bool pbc = false; u16 wpsie_len = 0; u16 p2pie_len = 0; u8 beacon_ie[IE_MAX_LEN]; @@ -3521,8 +3516,7 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, /* * Should be compared with saved ie before saving it */ - if (wl_dbg_level & WL_DBG_INFO) - wl_dbg_dump_wps_ie((char *) wps_ie); + wl_validate_wps_ie((char *) wps_ie, &pbc); memcpy(beacon_ie, wps_ie, wpsie_len); } else { WL_ERR(("No WPSIE in beacon \n")); @@ -3544,9 +3538,9 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, } else { WL_ERR(("No P2PIE in beacon \n")); } - wl_cfgp2p_set_managment_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, + wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, beacon_ie, wpsie_len + p2pie_len); - wl_cfgp2p_set_managment_ie(wl, dev, bssidx, VNDR_IE_ASSOCRSP_FLAG, + wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_ASSOCRSP_FLAG, beacon_ie, wpsie_len + p2pie_len); /* find the RSN_IE */ @@ -3634,16 +3628,15 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, /* * Should be compared with saved ie before saving it */ - if (wl_dbg_level & WL_DBG_INFO) - wl_dbg_dump_wps_ie((char *) wps_ie); + wl_validate_wps_ie((char *) wps_ie, &pbc); memcpy(beacon_ie, wps_ie, wpsie_len); - wl_cfgp2p_set_managment_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, + wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, beacon_ie, wpsie_len); wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, true); + wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); } else { - WL_ERR(("No WPSIE in beacon \n")); + WL_DBG(("No WPSIE in beacon \n")); } wldev_ioctl(dev, WLC_UP, &ap, sizeof(s32), false); @@ -3667,10 +3660,9 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, /* * Should be compared with saved ie before saving it */ - if (wl_dbg_level & WL_DBG_INFO) - wl_dbg_dump_wps_ie((char *) wps_ie); + wl_validate_wps_ie((char *) wps_ie, &pbc); memcpy(beacon_ie, wps_ie, wpsie_len); - wl_cfgp2p_set_managment_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, + wl_cfgp2p_set_management_ie(wl, dev, bssidx, VNDR_IE_BEACON_FLAG, beacon_ie, wpsie_len); if (wl->ap_info->wps_ie && memcmp(wl->ap_info->wps_ie, wps_ie, wpsie_len)) { @@ -3678,12 +3670,12 @@ wl_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, kfree(wl->ap_info->wps_ie); wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, true); + wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); } else if (wl->ap_info->wps_ie == NULL) { WL_DBG((" WPS IE is added\n")); wl->ap_info->wps_ie = kmemdup(wps_ie, wpsie_len, GFP_KERNEL); /* add WLC_E_PROBREQ_MSG event to respose probe_request from STA */ - wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, true); + wl_dongle_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, pbc); } /* find the RSN_IE */ if ((wpa2_ie = bcm_parse_tlvs((u8 *)info->tail, info->tail_len, @@ -3803,7 +3795,6 @@ static struct cfg80211_ops wl_cfg80211_ops = { .set_default_key = wl_cfg80211_config_default_key, .set_default_mgmt_key = wl_cfg80211_config_default_mgmt_key, .set_power_mgmt = wl_cfg80211_set_power_mgmt, - .set_bitrate_mask = wl_cfg80211_set_bitrate_mask, .connect = wl_cfg80211_connect, .disconnect = wl_cfg80211_disconnect, .suspend = wl_cfg80211_suspend, @@ -3925,9 +3916,9 @@ static void wl_free_wdev(struct wl_priv *wl) } } wiphy_unregister(wdev->wiphy); + wdev->wiphy->dev.parent = NULL; wiphy_free(wdev->wiphy); kfree(wdev); - wl_to_wdev(wl) = NULL; } static s32 wl_inform_bss(struct wl_priv *wl) @@ -3938,11 +3929,6 @@ static s32 wl_inform_bss(struct wl_priv *wl) s32 i; bss_list = wl->bss_list; - if (unlikely(bss_list->version != WL_BSS_INFO_VERSION)) { - WL_ERR(("Version %d != WL_BSS_INFO_VERSION\n", - bss_list->version)); - return -EOPNOTSUPP; - } WL_DBG(("scanned AP count (%d)\n", bss_list->count)); bi = next_bss(bss_list, bi); for_each_bss(bss_list, bi, i) { @@ -4042,7 +4028,8 @@ static s32 wl_inform_single_bss(struct wl_priv *wl, struct wl_bss_info *bi) static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net_device *ndev) { u32 event = ntoh32(e->event_type); - uint32 status = ntoh32(e->status); + u32 status = ntoh32(e->status); + u16 flags = ntoh16(e->flags); WL_DBG(("event %d, status %d\n", event, status)); if (event == WLC_E_SET_SSID) { @@ -4050,6 +4037,9 @@ static bool wl_is_linkup(struct wl_priv *wl, const wl_event_msg_t *e, struct net if (!wl_is_ibssmode(wl, ndev)) return true; } + } else if (event == WLC_E_LINK) { + if (flags & WLC_EVENT_MSG_LINK) + return true; } WL_DBG(("wl_is_linkup false\n")); @@ -4061,7 +4051,10 @@ static bool wl_is_linkdown(struct wl_priv *wl, const wl_event_msg_t *e) u32 event = ntoh32(e->event_type); u16 flags = ntoh16(e->flags); - if (event == WLC_E_DEAUTH_IND || event == WLC_E_DISASSOC_IND) { + if (event == WLC_E_DEAUTH_IND || + event == WLC_E_DISASSOC_IND || + event == WLC_E_DISASSOC || + event == WLC_E_DEAUTH) { return true; } else if (event == WLC_E_LINK) { if (!(flags & WLC_EVENT_MSG_LINK)) @@ -4185,10 +4178,15 @@ wl_notify_connect_status(struct wl_priv *wl, struct net_device *ndev, } else if (wl_is_linkdown(wl, e)) { if (wl_get_drv_status(wl, CONNECTED)) { printk("link down, call cfg80211_disconnected "); + rtnl_lock(); cfg80211_disconnected(ndev, 0, NULL, 0, GFP_KERNEL); wl_clr_drv_status(wl, CONNECTED); wl_link_down(wl); wl_init_prof(wl->profile); + rtnl_unlock(); + } else if (wl_get_drv_status(wl, CONNECTING)) { + printk("link down, during connecting"); + wl_bss_connect_done(wl, ndev, e, data, false); } } else if (wl_is_nonetwork(wl, e)) { printk("connect failed e->status 0x%x", (int)ntoh32(e->status)); @@ -4211,11 +4209,18 @@ wl_notify_roaming_status(struct wl_priv *wl, struct net_device *ndev, { bool act; s32 err = 0; + u32 event = be32_to_cpu(e->event_type); + u32 status = be32_to_cpu(e->status); WL_DBG(("Enter \n")); - wl_bss_roaming_done(wl, ndev, e, data); - act = true; - wl_update_prof(wl, e, &act, WL_PROF_ACT); + if (event == WLC_E_ROAM && status == WLC_E_STATUS_SUCCESS) { + if (test_bit(WL_STATUS_CONNECTED, &wl->status)) + wl_bss_roaming_done(wl, ndev, e, data); + else + wl_bss_connect_done(wl, ndev, e, data, true); + act = true; + wl_update_prof(wl, e, &act, WL_PROF_ACT); + } return err; } @@ -4270,6 +4275,14 @@ static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) assoc_info.req_len = htod32(assoc_info.req_len); assoc_info.resp_len = htod32(assoc_info.resp_len); assoc_info.flags = htod32(assoc_info.flags); + if (conn_info->req_ie_len) { + conn_info->req_ie_len = 0; + bzero(conn_info->req_ie, sizeof(conn_info->req_ie)); + } + if (conn_info->resp_ie_len) { + conn_info->resp_ie_len = 0; + bzero(conn_info->resp_ie, sizeof(conn_info->resp_ie)); + } if (assoc_info.req_len) { err = wl_dev_bufvar_get(ndev, "assoc_req_ies", wl->extra_buf, WL_ASSOC_INFO_MAX); @@ -4281,11 +4294,15 @@ static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) if (assoc_info.flags & WLC_ASSOC_REQ_IS_REASSOC) { conn_info->req_ie_len -= ETHER_ADDR_LEN; } - conn_info->req_ie = - kmemdup(wl->extra_buf, conn_info->req_ie_len, GFP_KERNEL); + if (conn_info->req_ie_len <= MAX_REQ_LINE) + memcpy(conn_info->req_ie, wl->extra_buf, conn_info->req_ie_len); + else { + WL_ERR(("%s IE size %d above max %d size \n", + __FUNCTION__, conn_info->req_ie_len, MAX_REQ_LINE)); + return err; + } } else { conn_info->req_ie_len = 0; - conn_info->req_ie = NULL; } if (assoc_info.resp_len) { err = wl_dev_bufvar_get(ndev, "assoc_resp_ies", wl->extra_buf, @@ -4295,11 +4312,15 @@ static s32 wl_get_assoc_ies(struct wl_priv *wl, struct net_device *ndev) return err; } conn_info->resp_ie_len = assoc_info.resp_len -sizeof(struct dot11_assoc_resp); - conn_info->resp_ie = - kmemdup(wl->extra_buf, conn_info->resp_ie_len, GFP_KERNEL); + if (conn_info->resp_ie_len <= MAX_REQ_LINE) + memcpy(conn_info->resp_ie, wl->extra_buf, conn_info->resp_ie_len); + else { + WL_ERR(("%s IE size %d above max %d size \n", + __FUNCTION__, conn_info->resp_ie_len, MAX_REQ_LINE)); + return err; + } } else { conn_info->resp_ie_len = 0; - conn_info->resp_ie = NULL; } WL_DBG(("req len (%d) resp len (%d)\n", conn_info->req_ie_len, conn_info->resp_ie_len)); @@ -4450,11 +4471,14 @@ wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, s32 err = 0; WL_DBG((" enter\n")); - wl_get_assoc_ies(wl, ndev); - memcpy(&wl->bssid, &e->addr, ETHER_ADDR_LEN); - wl_update_bss_info(wl, ndev); + if (wl_get_drv_status(wl, CONNECTING)) { wl_clr_drv_status(wl, CONNECTING); + if (completed) { + wl_get_assoc_ies(wl, ndev); + memcpy(&wl->bssid, &e->addr, ETHER_ADDR_LEN); + wl_update_bss_info(wl, ndev); + } cfg80211_connect_result(ndev, (u8 *)&wl->bssid, conn_info->req_ie, @@ -4465,19 +4489,14 @@ wl_bss_connect_done(struct wl_priv *wl, struct net_device *ndev, GFP_KERNEL); WL_DBG(("Report connect result - connection %s\n", completed ? "succeeded" : "failed")); - } else { - wl_clr_drv_status(wl, CONNECTING); - cfg80211_roamed(ndev, -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) - NULL, -#endif - (u8 *)&wl->bssid, - conn_info->req_ie, conn_info->req_ie_len, - conn_info->resp_ie, conn_info->resp_ie_len, - GFP_KERNEL); - WL_DBG(("Report roaming result\n")); } - wl_set_drv_status(wl, CONNECTED); + if (completed) + wl_set_drv_status(wl, CONNECTED); + else { + if (wl->scan_request) { + wl_cfg80211_scan_abort(wl, ndev); + } + } return err; } @@ -4521,8 +4540,6 @@ wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, return -EINVAL; } wl_clr_drv_status(wl, SCANNING); - if (unlikely(!wl->scan_request)) { - } rtnl_lock(); err = wldev_ioctl(ndev, WLC_GET_CHANNEL, &channel_inform, sizeof(channel_inform), false); @@ -4556,7 +4573,7 @@ wl_notify_scan_status(struct wl_priv *wl, struct net_device *ndev, scan_done_out: if (wl->scan_request) { - WL_ERR(("cfg80211_scan_done\n")); + WL_DBG(("cfg80211_scan_done\n")); cfg80211_scan_done(wl->scan_request, false); wl->scan_request = NULL; } @@ -4738,7 +4755,7 @@ static s32 wl_init_priv_mem(struct wl_priv *wl) WL_ERR(("Ioctl buf alloc failed\n")); goto init_priv_mem_out; } - wl->escan_ioctl_buf = (void *)kzalloc(WL_IOCTL_LEN_MAX, GFP_KERNEL); + wl->escan_ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL); if (unlikely(!wl->escan_ioctl_buf)) { WL_ERR(("Ioctl buf alloc failed\n")); goto init_priv_mem_out; @@ -4807,24 +4824,20 @@ static void wl_deinit_priv_mem(struct wl_priv *wl) static s32 wl_create_event_handler(struct wl_priv *wl) { + int ret = 0; WL_DBG(("Enter \n")); - sema_init(&wl->event_sync, 0); - wl->event_tsk = kthread_run(wl_event_handler, wl, "wl_event_handler"); - if (IS_ERR(wl->event_tsk)) { - wl->event_tsk = NULL; - WL_ERR(("failed to create event thread\n")); - return -ENOMEM; - } - return 0; + + wl->event_tsk.thr_pid = DHD_PID_KT_INVALID; + PROC_START(wl_event_handler, wl, &wl->event_tsk, 0); + if (wl->event_tsk.thr_pid < 0) + ret = -ENOMEM; + return ret; } static void wl_destroy_event_handler(struct wl_priv *wl) { - if (wl->event_tsk) { - send_sig(SIGTERM, wl->event_tsk, 1); - kthread_stop(wl->event_tsk); - wl->event_tsk = NULL; - } + if (wl->event_tsk.thr_pid >= 0) + PROC_STOP(&wl->event_tsk); } static void wl_term_iscan(struct wl_priv *wl) @@ -5048,7 +5061,7 @@ static void wl_notify_escan_complete(struct wl_priv *wl, bool aborted) } wl_clr_drv_status(wl, SCANNING); if (wl->p2p_supported && p2p_on(wl)) - wl_clr_p2p_status(wl, SCANNING); + wl_clr_p2p_status(wl, SCANNING); if (likely(wl->scan_request)) { cfg80211_scan_done(wl->scan_request, aborted); @@ -5079,11 +5092,19 @@ static s32 wl_escan_handler(struct wl_priv *wl, if (status == WLC_E_STATUS_PARTIAL) { WL_INFO(("WLC_E_STATUS_PARTIAL \n")); escan_result = (wl_escan_result_t *) data; + if (!escan_result) { + WL_ERR(("Invalid escan result (NULL pointer)\n")); + goto exit; + } if (dtoh16(escan_result->bss_count) != 1) { WL_ERR(("Invalid bss_count %d: ignoring\n", escan_result->bss_count)); goto exit; } bi = escan_result->bss_info; + if (!bi) { + WL_ERR(("Invalid escan bss info (NULL pointer)\n")); + goto exit; + } bi_length = dtoh32(bi->length); if (bi_length != (dtoh32(escan_result->buflen) - WL_ESCAN_RESULTS_FIXED_SIZE)) { WL_ERR(("Invalid bss_info length %d: ignoring\n", bi_length)); @@ -5151,7 +5172,7 @@ exit: return err; } -static s32 wl_init_iscan(struct wl_priv *wl) +static s32 wl_init_scan(struct wl_priv *wl) { struct wl_iscan_ctrl *iscan = wl_to_iscan(wl); int err = 0; @@ -5213,7 +5234,7 @@ static s32 wl_init_priv(struct wl_priv *wl) return -ENOMEM; wl_init_event_handler(wl); mutex_init(&wl->usr_sync); - err = wl_init_iscan(wl); + err = wl_init_scan(wl); if (unlikely(err)) return err; wl_init_fw(wl->fw); @@ -5234,7 +5255,7 @@ static void wl_deinit_priv(struct wl_priv *wl) wl_deinit_priv_mem(wl); } -#ifdef CONFIG_SYSCTL +#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) s32 wl_cfg80211_sysctl_export_devaddr(void *data) { /* Export the p2p_dev_addr via sysctl interface @@ -5270,7 +5291,7 @@ s32 wl_cfg80211_attach_post(struct net_device *ndev) BIT(NL80211_IFTYPE_P2P_GO)); if ((err = wl_cfgp2p_init_priv(wl)) != 0) goto fail; -#ifdef CONFIG_SYSCTL +#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) wl_cfg80211_sysctl_export_devaddr(wl->pub); #endif wl->p2p_supported = true; @@ -5321,13 +5342,13 @@ s32 wl_cfg80211_attach(struct net_device *ndev, void *data) goto cfg80211_attach_out; } - err = wl_setup_rfkill(wl); + err = wl_setup_rfkill(wl, TRUE); if (unlikely(err)) { WL_ERR(("Failed to setup rfkill %d\n", err)); goto cfg80211_attach_out; } -#ifdef CONFIG_SYSCTL +#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) if (!(wl_sysctl_hdr = register_sysctl_table(wl_sysctl_table))) { WL_ERR(("%s: sysctl register failed!! \n", __func__)); goto cfg80211_attach_out; @@ -5337,7 +5358,7 @@ s32 wl_cfg80211_attach(struct net_device *ndev, void *data) return err; cfg80211_attach_out: - err = wl_setup_rfkill(wl); + err = wl_setup_rfkill(wl, FALSE); wl_free_wdev(wl); return err; } @@ -5349,39 +5370,40 @@ void wl_cfg80211_detach(void) wl = WL_PRIV_GET(); WL_TRACE(("In\n")); - -#ifdef CONFIG_SYSCTL +#if defined(DHD_P2P_DEV_ADDR_FROM_SYSFS) && defined(CONFIG_SYSCTL) if (wl_sysctl_hdr) unregister_sysctl_table(wl_sysctl_hdr); #endif - rfkill_unregister(wl->rfkill); - rfkill_destroy(wl->rfkill); + wl_setup_rfkill(wl, FALSE); if (wl->p2p_supported) wl_cfgp2p_deinit_priv(wl); wl_deinit_priv(wl); - wl_free_wdev(wl); wl_set_drvdata(wl_cfg80211_dev, NULL); kfree(wl_cfg80211_dev); wl_cfg80211_dev = NULL; wl_clear_sdio_func(); + wl_free_wdev(wl); } static void wl_wakeup_event(struct wl_priv *wl) { - up(&wl->event_sync); + if (wl->event_tsk.thr_pid >= 0) + up(&wl->event_tsk.sema); } static s32 wl_event_handler(void *data) { struct net_device *netdev; - struct wl_priv *wl = (struct wl_priv *)data; - struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1 }; + struct wl_priv *wl = NULL; struct wl_event_q *e; + tsk_ctl_t *tsk = (tsk_ctl_t *)data; - sched_setscheduler(current, SCHED_FIFO, ¶m); - allow_signal(SIGTERM); - while (likely(!down_interruptible(&wl->event_sync))) { - if (kthread_should_stop()) + wl = (struct wl_priv *)tsk->parent; + complete(&tsk->completed); + + while (down_interruptible (&tsk->sema) == 0) { + SMP_RD_BARRIER_DEPENDS(); + if (tsk->terminated) break; e = wl_deq_event(wl); if (unlikely(!e)) { @@ -5392,14 +5414,16 @@ static s32 wl_event_handler(void *data) netdev = dhd_idx2net((struct dhd_pub *)(wl->pub), e->emsg.ifidx); if (!netdev) netdev = wl_to_prmry_ndev(wl); - if (wl->evt_handler[e->etype]) { + if (e->etype < WLC_E_LAST && wl->evt_handler[e->etype]) { wl->evt_handler[e->etype] (wl, netdev, &e->emsg, e->edata); } else { WL_DBG(("Unknown Event (%d): ignoring\n", e->etype)); } wl_put_event(e); } + WL_DBG(("%s was terminated\n", __func__)); + complete_and_exit(&tsk->completed, 0); return 0; } @@ -5912,35 +5936,51 @@ s32 wl_config_dongle(struct wl_priv *wl, bool need_lock) s32 err = 0; WL_TRACE(("In\n")); - if (wl->dongle_up) + if (wl->dongle_up) { + WL_ERR(("Dongle is already up\n")); return err; + } ndev = wl_to_prmry_ndev(wl); wdev = ndev->ieee80211_ptr; if (need_lock) rtnl_lock(); err = wl_dongle_eventmsg(ndev); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("wl_dongle_eventmsg failed\n")); goto default_conf_out; + } #ifndef EMBEDDED_PLATFORM err = wl_dongle_up(ndev, 0); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("wl_dongle_up failed\n")); goto default_conf_out; + } err = wl_dongle_country(ndev, 0); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("wl_dongle_country failed\n")); goto default_conf_out; + } err = wl_dongle_power(ndev, PM_FAST); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("wl_dongle_power failed\n")); goto default_conf_out; + } err = wl_dongle_glom(ndev, 0, DHD_SDALIGN); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("wl_dongle_glom failed\n")); goto default_conf_out; + } err = wl_dongle_roam(ndev, (wl->roam_on ? 0 : 1), 3); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("wl_dongle_roam failed\n")); goto default_conf_out; + } err = wl_dongle_eventmsg(ndev); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("wl_dongle_eventmsg failed\n")); goto default_conf_out; + } wl_dongle_scantime(ndev, 40, 80); wl_dongle_offload(ndev, 1, 0xf); @@ -5948,11 +5988,15 @@ s32 wl_config_dongle(struct wl_priv *wl, bool need_lock) #endif /* !EMBEDDED_PLATFORM */ err = wl_dongle_mode(wl, ndev, wdev->iftype); - if (unlikely(err && err != -EINPROGRESS)) + if (unlikely(err && err != -EINPROGRESS)) { + WL_ERR(("wl_dongle_mode failed\n")); goto default_conf_out; + } err = wl_dongle_probecap(wl); - if (unlikely(err)) + if (unlikely(err)) { + WL_ERR(("wl_dongle_probecap failed\n")); goto default_conf_out; + } /* -EINPROGRESS: Call commit handler */ @@ -5982,7 +6026,10 @@ static s32 wl_update_wiphybands(struct wl_priv *wl) phy = ((char *)&phy_list)[1]; WL_DBG(("%c phy\n", phy)); - if (phy == 'n' || phy == 'a') { + if (phy == 'a') { + wiphy = wl_to_wiphy(wl); + wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; + } else if (phy == 'n') { wiphy = wl_to_wiphy(wl); wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n; } @@ -6000,7 +6047,7 @@ static s32 __wl_cfg80211_up(struct wl_priv *wl) err = wl_config_dongle(wl, false); if (unlikely(err)) return err; - + dhd_monitor_init(wl->pub); wl_invoke_iscan(wl); wl_set_drv_status(wl, READY); return err; @@ -6037,6 +6084,7 @@ static s32 __wl_cfg80211_down(struct wl_priv *wl) wl_link_down(wl); if (wl->p2p_supported) wl_cfgp2p_down(wl); + dhd_monitor_uninit(); wl_debugfs_remove_netdev(wl); @@ -6053,6 +6101,8 @@ s32 wl_cfg80211_up(void) mutex_lock(&wl->usr_sync); wl_cfg80211_attach_post(wl_to_prmry_ndev(wl)); err = __wl_cfg80211_up(wl); + if (err) + WL_ERR(("__wl_cfg80211_up failed\n")); mutex_unlock(&wl->usr_sync); return err; @@ -6071,7 +6121,6 @@ s32 wl_cfg80211_down(void) return err; } - static s32 wl_dongle_probecap(struct wl_priv *wl) { s32 err = 0; @@ -6233,11 +6282,7 @@ static void wl_link_down(struct wl_priv *wl) WL_DBG(("In\n")); wl->link_up = false; - kfree(conn_info->req_ie); - conn_info->req_ie = NULL; conn_info->req_ie_len = 0; - kfree(conn_info->resp_ie); - conn_info->resp_ie = NULL; conn_info->resp_ie_len = 0; } @@ -6384,6 +6429,7 @@ s32 wl_cfg80211_get_p2p_dev_addr(struct net_device *net, struct ether_addr *p2pd static __used void wl_dongle_poweron(struct wl_priv *wl) { + WL_DBG(("Enter \n")); dhd_customer_gpio_wlan_ctrl(WLAN_RESET_ON); @@ -6398,6 +6444,8 @@ static __used void wl_dongle_poweron(struct wl_priv *wl) static __used void wl_dongle_poweroff(struct wl_priv *wl) { + + WL_DBG(("Enter \n")); #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 39) wl_cfg80211_suspend(wl_to_wiphy(wl), NULL); @@ -6455,29 +6503,44 @@ static int wl_rfkill_set(void *data, bool blocked) WL_DBG(("Enter \n")); WL_DBG(("RF %s\n", blocked ? "blocked" : "unblocked")); + if (!wl) + return -EINVAL; + wl->rf_blocked = blocked; return 0; } -static int wl_setup_rfkill(struct wl_priv *wl) +static int wl_setup_rfkill(struct wl_priv *wl, bool setup) { - s32 err; + s32 err = 0; WL_DBG(("Enter \n")); - wl->rfkill = rfkill_alloc("brcmfmac-wifi", - &wl_cfg80211_get_sdio_func()->dev, - RFKILL_TYPE_WLAN, &wl_rfkill_ops, (void *)wl); + if (!wl) + return -EINVAL; + if (setup) { + wl->rfkill = rfkill_alloc("brcmfmac-wifi", + &wl_cfg80211_get_sdio_func()->dev, + RFKILL_TYPE_WLAN, &wl_rfkill_ops, (void *)wl); + + if (!wl->rfkill) { + err = -ENOMEM; + goto err_out; + } - if (!wl->rfkill) { - err = -ENOMEM; - goto err_out; - } + err = rfkill_register(wl->rfkill); - err = rfkill_register(wl->rfkill); + if (err) + rfkill_destroy(wl->rfkill); + } else { + if (!wl->rfkill) { + err = -ENOMEM; + goto err_out; + } - if (err) + rfkill_unregister(wl->rfkill); rfkill_destroy(wl->rfkill); + } err_out: return err; diff --git a/drivers/net/wireless/bcmdhd/wl_cfg80211.h b/drivers/net/wireless/bcmdhd/wl_cfg80211.h index f9de60f0ca3..961107614c0 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfg80211.h +++ b/drivers/net/wireless/bcmdhd/wl_cfg80211.h @@ -274,10 +274,11 @@ struct wl_iscan_ctrl { }; /* association inform */ +#define MAX_REQ_LINE 1024 struct wl_connect_info { - u8 *req_ie; + u8 req_ie[MAX_REQ_LINE]; s32 req_ie_len; - u8 *resp_ie; + u8 resp_ie[MAX_REQ_LINE]; s32 resp_ie_len; }; @@ -347,7 +348,6 @@ struct wl_priv { struct ether_addr bssid; /* bssid of currently engaged network */ /* for synchronization of main event thread */ - struct semaphore event_sync; struct wl_profile *profile; /* holding dongle profile */ struct wl_iscan_ctrl *iscan; /* iscan controller */ @@ -357,7 +357,7 @@ struct wl_priv { /* control firwmare and nvram paramter downloading */ struct wl_fw_ctrl *fw; struct wl_pmk_list *pmk_list; /* wpa2 pmk list */ - struct task_struct *event_tsk; /* task of main event handler thread */ + tsk_ctl_t event_tsk; /* task of main event handler thread */ unsigned long status; /* current dongle status */ void *pub; u32 channel; /* current channel */ @@ -499,7 +499,8 @@ extern void wl_cfg80211_set_sdio_func(void *func); /* set sdio function info */ extern struct sdio_func *wl_cfg80211_get_sdio_func(void); /* set sdio function info */ extern s32 wl_cfg80211_up(void); /* dongle up */ extern s32 wl_cfg80211_down(void); /* dongle down */ -extern s32 wl_cfg80211_notify_ifadd(struct net_device *net); +extern s32 wl_cfg80211_notify_ifadd(struct net_device *net, s32 idx, s32 bssidx, +int (*_net_attach)(dhd_pub_t *dhdp, int ifidx)); extern s32 wl_cfg80211_ifdel_ops(struct net_device *net); extern s32 wl_cfg80211_notify_ifdel(struct net_device *net); extern s32 wl_cfg80211_is_progress_ifadd(void); diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c index fa88d4b4cf8..a261a72c9e2 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.c +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.c @@ -21,11 +21,9 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wl_cfg80211.c,v 1.1.4.1.2.14 2011-02-09 01:40:07 Exp $ - * $Id$ + * $Id: wl_cfgp2p.c,v 1.1.4.1.2.14 2011-02-09 01:40:07 Exp $ + * */ - - #include <typedefs.h> #include <linuxver.h> #include <osl.h> @@ -202,7 +200,7 @@ wl_cfgp2p_ifchange(struct wl_priv *wl, struct ether_addr *mac, u8 if_type, { wl_p2p_if_t ifreq; s32 err; - struct net_device *netdev = wl_to_prmry_ndev(wl); + struct net_device *netdev = wl_to_p2p_bss_ndev(wl, P2PAPI_BSSCFG_CONNECTION); ifreq.type = if_type; ifreq.chspec = chspec; @@ -436,7 +434,7 @@ wl_cfgp2p_enable_discovery(struct wl_priv *wl, struct net_device *dev, const u8 CFGP2P_ERR((" wsec error %d\n", ret)); } set_ie: - ret = wl_cfgp2p_set_managment_ie(wl, dev, + ret = wl_cfgp2p_set_management_ie(wl, dev, wl_to_p2p_bss_bssidx(wl, P2PAPI_BSSCFG_DEVICE), VNDR_IE_PRBREQ_FLAG, ie, ie_len); @@ -604,7 +602,7 @@ wl_cfgp2p_escan(struct wl_priv *wl, struct net_device *dev, u16 active, */ s32 -wl_cfgp2p_set_managment_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, +wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len) { /* Vendor-specific Information Element ID */ @@ -677,7 +675,7 @@ wl_cfgp2p_set_managment_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssi } /* Add if there is any extra IE */ if (vndr_ie && vndr_ie_len) { - CFGP2P_ERR(("Request has extra IE")); + CFGP2P_INFO(("Request has extra IE")); if (vndr_ie_len > mgmt_ie_buf_len) { CFGP2P_ERR(("extra IE size too big\n")); ret = -ENOMEM; @@ -898,7 +896,7 @@ wl_cfgp2p_find_idx(struct wl_priv *wl, struct net_device *ndev) return P2PAPI_BSSCFG_PRIMARY; } for (i = 0; i < P2PAPI_BSSCFG_MAX; i++) { - if (ndev == wl_to_p2p_bss_ndev(wl, i)) { + if (ndev == wl_to_p2p_bss_ndev(wl, i)) { index = wl_to_p2p_bss_bssidx(wl, i); break; } @@ -1015,11 +1013,11 @@ wl_cfgp2p_discover_enable_search(struct wl_priv *wl, u8 enable) CFGP2P_DBG((" Enter\n")); if (!wl_get_p2p_status(wl, DISCOVERY_ON)) { - CFGP2P_ERR((" do nothing, discovery is off\n")); + CFGP2P_DBG((" do nothing, discovery is off\n")); return ret; } if (wl_get_p2p_status(wl, SEARCH_ENABLED) == enable) { - CFGP2P_ERR(("already : %d\n", enable)); + CFGP2P_DBG(("already : %d\n", enable)); return ret; } @@ -1254,7 +1252,7 @@ wl_cfgp2p_supported(struct wl_priv *wl, struct net_device *ndev) ret = wldev_iovar_getint(ndev, "p2p", &p2p_supported); if (ret < 0) { - CFGP2P_ERR(("wl p2p error %d\n", ret)); + CFGP2P_ERR(("wl p2p error %d\n", ret)); return 0; } if (p2p_supported == 1) { diff --git a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h index 1b28fa9b80a..d62f5422708 100644 --- a/drivers/net/wireless/bcmdhd/wl_cfgp2p.h +++ b/drivers/net/wireless/bcmdhd/wl_cfgp2p.h @@ -21,7 +21,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: wl_cfg80211.h,v 1.1.4.1.2.8 2011/02/09 01:37:52 Exp $ + * $Id: wl_cfgp2p.h,v 1.1.4.1.2.8 2011/02/09 01:37:52 Exp $ */ #ifndef _wl_cfgp2p_h_ #define _wl_cfgp2p_h_ @@ -61,6 +61,7 @@ struct p2p_bss { u32 bssidx; struct net_device *dev; struct p2p_saved_ie saved_ie; + void *private_data; }; struct p2p_info { @@ -94,6 +95,7 @@ enum wl_cfgp2p_status { #define wl_to_p2p_bss_ndev(w, type) ((wl)->p2p->bss_idx[type].dev) #define wl_to_p2p_bss_bssidx(w, type) ((wl)->p2p->bss_idx[type].bssidx) #define wl_to_p2p_bss_saved_ie(w, type) ((wl)->p2p->bss_idx[type].saved_ie) +#define wl_to_p2p_bss_private(w, type) ((wl)->p2p->bss_idx[type].private_data) #define wl_to_p2p_bss(wl, type) ((wl)->p2p->bss_idx[type]) #define wl_get_p2p_status(wl, stat) ((!(wl)->p2p_supported) ? 0 : test_bit(WLP2P_STATUS_ ## stat, \ &(wl)->p2p->status)) @@ -176,7 +178,7 @@ extern wifi_p2p_ie_t * wl_cfgp2p_find_p2pie(u8 *parse, u32 len); extern s32 -wl_cfgp2p_set_managment_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, +wl_cfgp2p_set_management_ie(struct wl_priv *wl, struct net_device *ndev, s32 bssidx, s32 pktflag, const u8 *vndr_ie, u32 vndr_ie_len); extern s32 wl_cfgp2p_clear_management_ie(struct wl_priv *wl, s32 bssidx); diff --git a/drivers/net/wireless/bcmdhd/wl_iw.c b/drivers/net/wireless/bcmdhd/wl_iw.c index 494538cea18..ae28c6d0215 100644 --- a/drivers/net/wireless/bcmdhd/wl_iw.c +++ b/drivers/net/wireless/bcmdhd/wl_iw.c @@ -1261,7 +1261,6 @@ wl_iw_set_dtim_skip( &iovbuf, sizeof(iovbuf))) >= 0) { p += snprintf(p, MAX_WX_STRING, "OK"); - net_os_set_dtim_skip(dev, bcn_li_dtim); WL_TRACE(("%s: set dtim_skip %d OK\n", __FUNCTION__, @@ -1471,8 +1470,8 @@ wl_iw_set_pno_set( } if (wrqu->data.length < (strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t))) { - WL_ERROR(("%s aggument=%d less %d\n", __FUNCTION__, - wrqu->data.length, strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t))); + WL_ERROR(("%s argument=%d less %d\n", __FUNCTION__, + wrqu->data.length, (int)(strlen(PNOSETUP_SET_CMD) + sizeof(cmd_tlv_t)))); goto exit_proc; } @@ -1959,6 +1958,7 @@ static int iwpriv_get_assoc_list(struct net_device *dev, return -EINVAL; } + iw = *(wl_iw_t **)netdev_priv(dev); net_os_wake_lock(dev); @@ -1985,6 +1985,7 @@ static int iwpriv_get_assoc_list(struct net_device *dev, WL_SOFTAP(("%s: got %d stations\n", __FUNCTION__, sta_maclist->count)); + memset(mac_lst, 0, sizeof(mac_lst)); p_mac_str = mac_lst; @@ -3119,7 +3120,6 @@ wl_iw_force_specific_scan(iscan_info_t *iscan) static void wl_iw_send_scan_complete(iscan_info_t *iscan) { -#ifndef SANDGATE2G union iwreq_data wrqu; memset(&wrqu, 0, sizeof(wrqu)); @@ -3131,7 +3131,6 @@ wl_iw_send_scan_complete(iscan_info_t *iscan) g_first_broadcast_scan = BROADCAST_SCAN_FIRST_RESULT_READY; #endif WL_TRACE(("Send Event ISCAN complete\n")); -#endif } static int @@ -6094,8 +6093,8 @@ wl_iw_set_cscan( } if (wrqu->data.length < (strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t))) { - WL_ERROR(("%s aggument=%d less %d\n", __FUNCTION__, - wrqu->data.length, strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t))); + WL_ERROR(("%s argument=%d less %d\n", __FUNCTION__, + wrqu->data.length, (int)(strlen(CSCAN_COMMAND) + sizeof(cscan_tlv_t)))); return -1; } @@ -6274,7 +6273,7 @@ thr_wait_for_2nd_eth_dev(void *data) } DHD_OS_WAKE_LOCK(iw->pub); complete(&tsk_ctl->completed); - if (down_timeout(&tsk_ctl->sema, msecs_to_jiffies(1000)) != 0) { + if (down_timeout(&tsk_ctl->sema, msecs_to_jiffies(1000)) != 0) { #else if (down_interruptible(&tsk_ctl->sema) != 0) { #endif @@ -7335,69 +7334,68 @@ wl_iw_set_priv( return -EFAULT; } - if (strnicmp(extra, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) { + if (strnicmp(extra, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) { #ifdef ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS WL_TRACE(("%s: active scan setting suppressed\n", dev->name)); #else ret = wl_iw_set_active_scan(dev, info, (union iwreq_data *)dwrq, extra); #endif - } - else if (strnicmp(extra, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0) + } + else if (strnicmp(extra, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0) #ifdef ENABLE_ACTIVE_PASSIVE_SCAN_SUPPRESS WL_TRACE(("%s: passive scan setting suppressed\n", dev->name)); #else ret = wl_iw_set_passive_scan(dev, info, (union iwreq_data *)dwrq, extra); #endif - else if (strnicmp(extra, "RSSI", strlen("RSSI")) == 0) + else if (strnicmp(extra, "RSSI", strlen("RSSI")) == 0) ret = wl_iw_get_rssi(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, "LINKSPEED", strlen("LINKSPEED")) == 0) + else if (strnicmp(extra, "LINKSPEED", strlen("LINKSPEED")) == 0) ret = wl_iw_get_link_speed(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, "MACADDR", strlen("MACADDR")) == 0) + else if (strnicmp(extra, "MACADDR", strlen("MACADDR")) == 0) ret = wl_iw_get_macaddr(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, "COUNTRY", strlen("COUNTRY")) == 0) + else if (strnicmp(extra, "COUNTRY", strlen("COUNTRY")) == 0) ret = wl_iw_set_country(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, "STOP", strlen("STOP")) == 0) + else if (strnicmp(extra, "STOP", strlen("STOP")) == 0) ret = wl_iw_control_wl_off(dev, info); - else if (strnicmp(extra, BAND_GET_CMD, strlen(BAND_GET_CMD)) == 0) + else if (strnicmp(extra, BAND_GET_CMD, strlen(BAND_GET_CMD)) == 0) ret = wl_iw_get_band(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, BAND_SET_CMD, strlen(BAND_SET_CMD)) == 0) + else if (strnicmp(extra, BAND_SET_CMD, strlen(BAND_SET_CMD)) == 0) ret = wl_iw_set_band(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, DTIM_SKIP_GET_CMD, strlen(DTIM_SKIP_GET_CMD)) == 0) + else if (strnicmp(extra, DTIM_SKIP_GET_CMD, strlen(DTIM_SKIP_GET_CMD)) == 0) ret = wl_iw_get_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, DTIM_SKIP_SET_CMD, strlen(DTIM_SKIP_SET_CMD)) == 0) + else if (strnicmp(extra, DTIM_SKIP_SET_CMD, strlen(DTIM_SKIP_SET_CMD)) == 0) ret = wl_iw_set_dtim_skip(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, SETSUSPEND_CMD, strlen(SETSUSPEND_CMD)) == 0) + else if (strnicmp(extra, SETSUSPEND_CMD, strlen(SETSUSPEND_CMD)) == 0) ret = wl_iw_set_suspend(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, TXPOWER_SET_CMD, strlen(TXPOWER_SET_CMD)) == 0) + else if (strnicmp(extra, TXPOWER_SET_CMD, strlen(TXPOWER_SET_CMD)) == 0) ret = wl_iw_set_txpower(dev, info, (union iwreq_data *)dwrq, extra); #if defined(PNO_SUPPORT) - else if (strnicmp(extra, PNOSSIDCLR_SET_CMD, strlen(PNOSSIDCLR_SET_CMD)) == 0) + else if (strnicmp(extra, PNOSSIDCLR_SET_CMD, strlen(PNOSSIDCLR_SET_CMD)) == 0) ret = wl_iw_set_pno_reset(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, PNOSETUP_SET_CMD, strlen(PNOSETUP_SET_CMD)) == 0) + else if (strnicmp(extra, PNOSETUP_SET_CMD, strlen(PNOSETUP_SET_CMD)) == 0) ret = wl_iw_set_pno_set(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, PNOENABLE_SET_CMD, strlen(PNOENABLE_SET_CMD)) == 0) + else if (strnicmp(extra, PNOENABLE_SET_CMD, strlen(PNOENABLE_SET_CMD)) == 0) ret = wl_iw_set_pno_enable(dev, info, (union iwreq_data *)dwrq, extra); #endif #if defined(CSCAN) - else if (strnicmp(extra, CSCAN_COMMAND, strlen(CSCAN_COMMAND)) == 0) + else if (strnicmp(extra, CSCAN_COMMAND, strlen(CSCAN_COMMAND)) == 0) ret = wl_iw_set_cscan(dev, info, (union iwreq_data *)dwrq, extra); #endif - else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0) + else if (strnicmp(extra, "POWERMODE", strlen("POWERMODE")) == 0) ret = wl_iw_set_power_mode(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) + else if (strnicmp(extra, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) ret = wl_iw_set_btcoex_dhcp(dev, info, (union iwreq_data *)dwrq, extra); - else if (strnicmp(extra, "GETPOWER", strlen("GETPOWER")) == 0) + else if (strnicmp(extra, "GETPOWER", strlen("GETPOWER")) == 0) ret = wl_iw_get_power_mode(dev, info, (union iwreq_data *)dwrq, extra); #ifdef SOFTAP - else if (strnicmp(extra, "ASCII_CMD", strlen("ASCII_CMD")) == 0) { - - wl_iw_process_private_ascii_cmd(dev, info, (union iwreq_data *)dwrq, extra); - } + else if (strnicmp(extra, "ASCII_CMD", strlen("ASCII_CMD")) == 0) { + wl_iw_process_private_ascii_cmd(dev, info, (union iwreq_data *)dwrq, extra); + } else if (strnicmp(extra, "AP_MAC_LIST_SET", strlen("AP_MAC_LIST_SET")) == 0) { WL_SOFTAP(("penguin, set AP_MAC_LIST_SET\n")); set_ap_mac_list(dev, (extra + PROFILE_OFFSET)); - } + } #endif else { WL_ERROR(("Unknown PRIVATE command %s - ignored\n", extra)); @@ -8130,9 +8128,7 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) iwpmkidcand->flags |= IW_PMKID_CAND_PREAUTH; bcopy(&pmkidcand->BSSID, &iwpmkidcand->bssid.sa_data, ETHER_ADDR_LEN); -#ifndef SANDGATE2G wireless_send_event(dev, cmd, &wrqu, extra); -#endif pmkidcand++; count--; } @@ -8182,14 +8178,12 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) WL_TRACE(("Unknown Event %d: ignoring\n", event_type)); break; } -#ifndef SANDGATE2G if (cmd) { if (cmd == SIOCGIWSCAN) wireless_send_event(dev, cmd, &wrqu, NULL); else wireless_send_event(dev, cmd, &wrqu, extra); } -#endif #if WIRELESS_EXT > 14 @@ -8197,9 +8191,7 @@ wl_iw_event(struct net_device *dev, wl_event_msg_t *e, void* data) if (wl_iw_check_conn_fail(e, extra, sizeof(extra))) { cmd = IWEVCUSTOM; wrqu.data.length = strlen(extra); -#ifndef SANDGATE2G wireless_send_event(dev, cmd, &wrqu, extra); -#endif } #endif diff --git a/drivers/net/wireless/bcmdhd/wldev_common.c b/drivers/net/wireless/bcmdhd/wldev_common.c index 31e8f3cb86f..b01e4a2b8c9 100644 --- a/drivers/net/wireless/bcmdhd/wldev_common.c +++ b/drivers/net/wireless/bcmdhd/wldev_common.c @@ -38,32 +38,21 @@ #define htodchanspec(i) i #define dtohchanspec(i) i +extern int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd); + s32 wldev_ioctl( struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set) { s32 ret = 0; - struct ifreq ifr; struct wl_ioctl ioc; - mm_segment_t fs; - s32 err = 0; memset(&ioc, 0, sizeof(ioc)); ioc.cmd = cmd; ioc.buf = arg; ioc.len = len; ioc.set = set; - strcpy(ifr.ifr_name, dev->name); - ifr.ifr_data = (caddr_t)&ioc; - - fs = get_fs(); - set_fs(get_ds()); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) - err = dev->do_ioctl(dev, &ifr, SIOCDEVPRIVATE); -#else - err = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE); -#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */ - set_fs(fs); + ret = dhd_ioctl_entry_local(dev, &ioc, cmd); return ret; } diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index cc4a54f571b..55cd3e1f75b 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -1158,6 +1158,7 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = { #endif #ifdef CONFIG_RT2800PCI_RT53XX { PCI_DEVICE(0x1814, 0x5390) }, + { PCI_DEVICE(0x1814, 0x539f) }, #endif { 0, } }; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c index c7576ec4744..3bee79eab74 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c @@ -104,7 +104,7 @@ void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, tx_agc[RF90_PATH_A] = 0x10101010; tx_agc[RF90_PATH_B] = 0x10101010; } else if (rtlpriv->dm.dynamic_txhighpower_lvl == - TXHIGHPWRLEVEL_LEVEL1) { + TXHIGHPWRLEVEL_LEVEL2) { tx_agc[RF90_PATH_A] = 0x00000000; tx_agc[RF90_PATH_B] = 0x00000000; } else{ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 692671b1166..d549bbc93cd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1905,7 +1905,7 @@ void pci_enable_ari(struct pci_dev *dev) { int pos; u32 cap; - u16 ctrl; + u16 flags, ctrl; struct pci_dev *bridge; if (!pci_is_pcie(dev) || dev->devfn) @@ -1923,6 +1923,11 @@ void pci_enable_ari(struct pci_dev *dev) if (!pos) return; + /* ARI is a PCIe v2 feature */ + pci_read_config_word(bridge, pos + PCI_EXP_FLAGS, &flags); + if ((flags & PCI_EXP_FLAGS_VERS) < 2) + return; + pci_read_config_dword(bridge, pos + PCI_EXP_DEVCAP2, &cap); if (!(cap & PCI_EXP_DEVCAP2_ARI)) return; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 02145e9697a..1196f61a4ab 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2758,6 +2758,29 @@ static void ricoh_mmc_fixup_r5c832(struct pci_dev *dev) dev_notice(&dev->dev, "proprietary Ricoh MMC controller disabled (via firewire function)\n"); dev_notice(&dev->dev, "MMC cards are now supported by standard SDHCI controller\n"); + + /* + * RICOH 0xe823 SD/MMC card reader fails to recognize + * certain types of SD/MMC cards. Lowering the SD base + * clock frequency from 200Mhz to 50Mhz fixes this issue. + * + * 0x150 - SD2.0 mode enable for changing base clock + * frequency to 50Mhz + * 0xe1 - Base clock frequency + * 0x32 - 50Mhz new clock frequency + * 0xf9 - Key register for 0x150 + * 0xfc - key register for 0xe1 + */ + if (dev->device == PCI_DEVICE_ID_RICOH_R5CE823) { + pci_write_config_byte(dev, 0xf9, 0xfc); + pci_write_config_byte(dev, 0x150, 0x10); + pci_write_config_byte(dev, 0xf9, 0x00); + pci_write_config_byte(dev, 0xfc, 0x01); + pci_write_config_byte(dev, 0xe1, 0x32); + pci_write_config_byte(dev, 0xfc, 0x00); + + dev_notice(&dev->dev, "MMC controller base frequency changed to 50Mhz.\n"); + } } DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, ricoh_mmc_fixup_r5c832); DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, ricoh_mmc_fixup_r5c832); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index df68618f6db..3195dbd3ec3 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -636,6 +636,29 @@ void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task) } EXPORT_SYMBOL_GPL(rtc_irq_unregister); +static int rtc_update_hrtimer(struct rtc_device *rtc, int enabled) +{ + /* + * We unconditionally cancel the timer here, because otherwise + * we could run into BUG_ON(timer->state != HRTIMER_STATE_CALLBACK); + * when we manage to start the timer before the callback + * returns HRTIMER_RESTART. + * + * We cannot use hrtimer_cancel() here as a running callback + * could be blocked on rtc->irq_task_lock and hrtimer_cancel() + * would spin forever. + */ + if (hrtimer_try_to_cancel(&rtc->pie_timer) < 0) + return -1; + + if (enabled) { + ktime_t period = ktime_set(0, NSEC_PER_SEC / rtc->irq_freq); + + hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL); + } + return 0; +} + /** * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs * @rtc: the rtc device @@ -651,21 +674,21 @@ int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled int err = 0; unsigned long flags; +retry: spin_lock_irqsave(&rtc->irq_task_lock, flags); if (rtc->irq_task != NULL && task == NULL) err = -EBUSY; if (rtc->irq_task != task) err = -EACCES; - - if (enabled) { - ktime_t period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); - hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL); - } else { - hrtimer_cancel(&rtc->pie_timer); + if (!err) { + if (rtc_update_hrtimer(rtc, enabled) < 0) { + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); + cpu_relax(); + goto retry; + } + rtc->pie_enabled = enabled; } - rtc->pie_enabled = enabled; spin_unlock_irqrestore(&rtc->irq_task_lock, flags); - return err; } EXPORT_SYMBOL_GPL(rtc_irq_set_state); @@ -685,22 +708,20 @@ int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq) int err = 0; unsigned long flags; - if (freq <= 0) + if (freq <= 0 || freq > 5000) return -EINVAL; - +retry: spin_lock_irqsave(&rtc->irq_task_lock, flags); if (rtc->irq_task != NULL && task == NULL) err = -EBUSY; if (rtc->irq_task != task) err = -EACCES; - if (err == 0) { + if (!err) { rtc->irq_freq = freq; - if (rtc->pie_enabled) { - ktime_t period; - hrtimer_cancel(&rtc->pie_timer); - period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq); - hrtimer_start(&rtc->pie_timer, period, - HRTIMER_MODE_REL); + if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) { + spin_unlock_irqrestore(&rtc->irq_task_lock, flags); + cpu_relax(); + goto retry; } } spin_unlock_irqrestore(&rtc->irq_task_lock, flags); diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c index 2fc31aac3f4..75259fe3860 100644 --- a/drivers/rtc/rtc-tegra.c +++ b/drivers/rtc/rtc-tegra.c @@ -343,7 +343,7 @@ static int __devinit tegra_rtc_probe(struct platform_device *pdev) /* set context info. */ info->pdev = pdev; - info->tegra_rtc_lock = __SPIN_LOCK_UNLOCKED(info->tegra_rtc_lock); + spin_lock_init(&info->tegra_rtc_lock); platform_set_drvdata(pdev, info); diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h index 6d8dcd4dd06..7f53ceaa723 100644 --- a/drivers/scsi/hpsa.h +++ b/drivers/scsi/hpsa.h @@ -214,7 +214,7 @@ static void SA5_submit_command(struct ctlr_info *h, dev_dbg(&h->pdev->dev, "Sending %x, tag = %x\n", c->busaddr, c->Header.Tag.lower); writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET); - (void) readl(h->vaddr + SA5_REQUEST_PORT_OFFSET); + (void) readl(h->vaddr + SA5_SCRATCHPAD_OFFSET); h->commands_outstanding++; if (h->commands_outstanding > h->max_outstanding) h->max_outstanding = h->commands_outstanding; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 874e29d9533..f84084bba2f 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -849,6 +849,9 @@ static struct domain_device *sas_ex_discover_expander( res = sas_discover_expander(child); if (res) { + spin_lock_irq(&parent->port->dev_list_lock); + list_del(&child->dev_list_node); + spin_unlock_irq(&parent->port->dev_list_lock); kfree(child); return NULL; } diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index fca6a895307..d079f9a3c6b 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -3871,6 +3871,9 @@ static long pmcraid_ioctl_passthrough( pmcraid_err("couldn't build passthrough ioadls\n"); goto out_free_buffer; } + } else if (request_size < 0) { + rc = -EINVAL; + goto out_free_buffer; } /* If data is being written into the device, copy the data from user diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 82e9e5c0476..cf8dfab9489 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -197,6 +197,7 @@ static struct { {"IBM", "ProFibre 4000R", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, {"IBM", "2105", NULL, BLIST_RETRY_HWERROR}, {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN}, + {"IOMEGA", "ZIP", NULL, BLIST_NOTQ | BLIST_NOLUN}, {"IOMEGA", "Io20S *F", NULL, BLIST_KEY}, {"INSITE", "Floptical F*8I", NULL, BLIST_KEY}, {"INSITE", "I325VM", NULL, BLIST_KEY}, @@ -243,6 +244,7 @@ static struct { {"Tornado-", "F4", "*", BLIST_NOREPORTLUN}, {"TOSHIBA", "CDROM", NULL, BLIST_ISROM}, {"TOSHIBA", "CD-ROM", NULL, BLIST_ISROM}, + {"Traxdata", "CDR4120", NULL, BLIST_NOLUN}, /* locks up */ {"USB2.0", "SMARTMEDIA/XD", NULL, BLIST_FORCELUN | BLIST_INQUIRY_36}, {"WangDAT", "Model 2600", "01.7", BLIST_SELECT_NO_ATN}, {"WangDAT", "Model 3200", "02.2", BLIST_SELECT_NO_ATN}, diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index ec1803a4872..28d9c9d6b4b 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -213,6 +213,8 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, int ret = DRIVER_ERROR << 24; req = blk_get_request(sdev->request_queue, write, __GFP_WAIT); + if (!req) + return ret; if (bufflen && blk_rq_map_kern(sdev->request_queue, req, buffer, bufflen, __GFP_WAIT)) diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c index eb7a3e85304..eba183c428c 100644 --- a/drivers/scsi/ses.c +++ b/drivers/scsi/ses.c @@ -160,6 +160,10 @@ static unsigned char *ses_get_page2_descriptor(struct enclosure_device *edev, return NULL; } +/* For device slot and array device slot elements, byte 3 bit 6 + * is "fault sensed" while byte 3 bit 5 is "fault reqstd". As this + * code stands these bits are shifted 4 positions right so in + * sysfs they will appear as bits 2 and 1 respectively. Strange. */ static void ses_get_fault(struct enclosure_device *edev, struct enclosure_component *ecomp) { @@ -181,7 +185,7 @@ static int ses_set_fault(struct enclosure_device *edev, /* zero is disabled */ break; case ENCLOSURE_SETTING_ENABLED: - desc[2] = 0x02; + desc[3] = 0x20; break; default: /* SES doesn't do the SGPIO blink settings */ diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index 4778e270716..5fc97d2ba2f 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -221,14 +221,33 @@ static unsigned int sr_check_events(struct cdrom_device_info *cdi, return 0; events = sr_get_events(cd->device); + cd->get_event_changed |= events & DISK_EVENT_MEDIA_CHANGE; + + /* + * If earlier GET_EVENT_STATUS_NOTIFICATION and TUR did not agree + * for several times in a row. We rely on TUR only for this likely + * broken device, to prevent generating incorrect media changed + * events for every open(). + */ + if (cd->ignore_get_event) { + events &= ~DISK_EVENT_MEDIA_CHANGE; + goto do_tur; + } + /* * GET_EVENT_STATUS_NOTIFICATION is enough unless MEDIA_CHANGE * is being cleared. Note that there are devices which hang * if asked to execute TUR repeatedly. */ - if (!(clearing & DISK_EVENT_MEDIA_CHANGE)) - goto skip_tur; + if (cd->device->changed) { + events |= DISK_EVENT_MEDIA_CHANGE; + cd->device->changed = 0; + cd->tur_changed = true; + } + if (!(clearing & DISK_EVENT_MEDIA_CHANGE)) + return events; +do_tur: /* let's see whether the media is there with TUR */ last_present = cd->media_present; ret = scsi_test_unit_ready(cd->device, SR_TIMEOUT, MAX_RETRIES, &sshdr); @@ -242,12 +261,31 @@ static unsigned int sr_check_events(struct cdrom_device_info *cdi, (scsi_sense_valid(&sshdr) && sshdr.asc != 0x3a); if (last_present != cd->media_present) - events |= DISK_EVENT_MEDIA_CHANGE; -skip_tur: + cd->device->changed = 1; + if (cd->device->changed) { events |= DISK_EVENT_MEDIA_CHANGE; cd->device->changed = 0; + cd->tur_changed = true; + } + + if (cd->ignore_get_event) + return events; + + /* check whether GET_EVENT is reporting spurious MEDIA_CHANGE */ + if (!cd->tur_changed) { + if (cd->get_event_changed) { + if (cd->tur_mismatch++ > 8) { + sdev_printk(KERN_WARNING, cd->device, + "GET_EVENT and TUR disagree continuously, suppress GET_EVENT events\n"); + cd->ignore_get_event = true; + } + } else { + cd->tur_mismatch = 0; + } } + cd->tur_changed = false; + cd->get_event_changed = false; return events; } diff --git a/drivers/scsi/sr.h b/drivers/scsi/sr.h index e036f1dc83c..37c8f6b1751 100644 --- a/drivers/scsi/sr.h +++ b/drivers/scsi/sr.h @@ -41,6 +41,13 @@ typedef struct scsi_cd { unsigned readcd_known:1; /* drive supports READ_CD (0xbe) */ unsigned readcd_cdda:1; /* reading audio data using READ_CD */ unsigned media_present:1; /* media is present */ + + /* GET_EVENT spurious event handling, blk layer guarantees exclusion */ + int tur_mismatch; /* nr of get_event TUR mismatches */ + bool tur_changed:1; /* changed according to TUR */ + bool get_event_changed:1; /* changed according to GET_EVENT */ + bool ignore_get_event:1; /* GET_EVENT is unreliable, use TUR */ + struct cdrom_device_info cdi; /* We hold gendisk and scsi_device references on probe and use * the refs on this kref to decide when to release them */ diff --git a/drivers/staging/ath6kl/os/linux/ar6000_drv.c b/drivers/staging/ath6kl/os/linux/ar6000_drv.c index 48dd9e36359..aa97efc0e4e 100644 --- a/drivers/staging/ath6kl/os/linux/ar6000_drv.c +++ b/drivers/staging/ath6kl/os/linux/ar6000_drv.c @@ -954,9 +954,13 @@ ar6000_transfer_bin_file(struct ar6_softc *ar, AR6K_BIN_FILE file, u32 address, const char *filename; const struct firmware *fw_entry; u32 fw_entry_size; + u8 **buf; + size_t *buf_len; switch (file) { case AR6K_OTP_FILE: + buf = &ar->fw_otp; + buf_len = &ar->fw_otp_len; if (ar->arVersion.target_ver == AR6003_REV1_VERSION) { filename = AR6003_REV1_OTP_FILE; } else if (ar->arVersion.target_ver == AR6003_REV2_VERSION) { @@ -970,6 +974,8 @@ ar6000_transfer_bin_file(struct ar6_softc *ar, AR6K_BIN_FILE file, u32 address, break; case AR6K_FIRMWARE_FILE: + buf = &ar->fw; + buf_len = &ar->fw_len; if (ar->arVersion.target_ver == AR6003_REV1_VERSION) { filename = AR6003_REV1_FIRMWARE_FILE; } else if (ar->arVersion.target_ver == AR6003_REV2_VERSION) { @@ -1028,6 +1034,8 @@ ar6000_transfer_bin_file(struct ar6_softc *ar, AR6K_BIN_FILE file, u32 address, break; case AR6K_PATCH_FILE: + buf = &ar->fw_patch; + buf_len = &ar->fw_patch_len; if (ar->arVersion.target_ver == AR6003_REV1_VERSION) { filename = AR6003_REV1_PATCH_FILE; } else if (ar->arVersion.target_ver == AR6003_REV2_VERSION) { @@ -1041,6 +1049,8 @@ ar6000_transfer_bin_file(struct ar6_softc *ar, AR6K_BIN_FILE file, u32 address, break; case AR6K_BOARD_DATA_FILE: + buf = &ar->fw_data; + buf_len = &ar->fw_data_len; if (ar->arVersion.target_ver == AR6003_REV1_VERSION) { filename = AR6003_REV1_BOARD_DATA_FILE; } else if (ar->arVersion.target_ver == AR6003_REV2_VERSION) { @@ -1057,23 +1067,29 @@ ar6000_transfer_bin_file(struct ar6_softc *ar, AR6K_BIN_FILE file, u32 address, AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Unknown file type: %d\n", file)); return A_ERROR; } - if ((A_REQUEST_FIRMWARE(&fw_entry, filename, ((struct device *)ar->osDevInfo.pOSDevice))) != 0) - { - AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to get %s\n", filename)); - return A_ENOENT; + + if (*buf == NULL) { + if ((A_REQUEST_FIRMWARE(&fw_entry, filename, ((struct device *)ar->osDevInfo.pOSDevice))) != 0) { + AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Failed to get %s\n", filename)); + return A_ENOENT; + } + + *buf = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); + *buf_len = fw_entry->size; + A_RELEASE_FIRMWARE(fw_entry); } #ifdef SOFTMAC_FILE_USED - if (file==AR6K_BOARD_DATA_FILE && fw_entry->data) { - ar6000_softmac_update(ar, (u8 *)fw_entry->data, fw_entry->size); + if (file==AR6K_BOARD_DATA_FILE && *buf_len) { + ar6000_softmac_update(ar, *buf, *buf_len); } #endif - fw_entry_size = fw_entry->size; + fw_entry_size = *buf_len; /* Load extended board data for AR6003 */ - if ((file==AR6K_BOARD_DATA_FILE) && (fw_entry->data)) { + if ((file==AR6K_BOARD_DATA_FILE) && *buf) { u32 board_ext_address; u32 board_ext_data_size; u32 board_data_size; @@ -1089,14 +1105,13 @@ ar6000_transfer_bin_file(struct ar6_softc *ar, AR6K_BIN_FILE file, u32 address, AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("Board extended Data download address: 0x%x\n", board_ext_address)); /* check whether the target has allocated memory for extended board data and file contains extended board data */ - if ((board_ext_address) && (fw_entry->size == (board_data_size + board_ext_data_size))) { + if ((board_ext_address) && (*buf_len == (board_data_size + board_ext_data_size))) { u32 param; - status = BMIWriteMemory(ar->arHifDevice, board_ext_address, (u8 *)(fw_entry->data + board_data_size), board_ext_data_size); + status = BMIWriteMemory(ar->arHifDevice, board_ext_address, (u8 *)(*buf + board_data_size), board_ext_data_size); if (status) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("BMI operation failed: %d\n", __LINE__)); - A_RELEASE_FIRMWARE(fw_entry); return A_ERROR; } @@ -1110,17 +1125,16 @@ ar6000_transfer_bin_file(struct ar6_softc *ar, AR6K_BIN_FILE file, u32 address, } if (compressed) { - status = BMIFastDownload(ar->arHifDevice, address, (u8 *)fw_entry->data, fw_entry_size); + status = BMIFastDownload(ar->arHifDevice, address, *buf, fw_entry_size); } else { - status = BMIWriteMemory(ar->arHifDevice, address, (u8 *)fw_entry->data, fw_entry_size); + status = BMIWriteMemory(ar->arHifDevice, address, *buf, fw_entry_size); } if (status) { AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("BMI operation failed: %d\n", __LINE__)); - A_RELEASE_FIRMWARE(fw_entry); return A_ERROR; } - A_RELEASE_FIRMWARE(fw_entry); + return 0; } @@ -2088,6 +2102,11 @@ ar6000_destroy(struct net_device *dev, unsigned int unregister) ar6000_remove_ap_interface(); #endif /*CONFIG_AP_VIRTUAL_ADAPTER_SUPPORT */ + kfree(ar->fw_otp); + kfree(ar->fw); + kfree(ar->fw_patch); + kfree(ar->fw_data); + AR_DEBUG_PRINTF(ATH_DEBUG_INFO,("-ar6000_destroy \n")); } diff --git a/drivers/staging/ath6kl/os/linux/cfg80211.c b/drivers/staging/ath6kl/os/linux/cfg80211.c index d3a774dbb7e..32e319782f2 100644 --- a/drivers/staging/ath6kl/os/linux/cfg80211.c +++ b/drivers/staging/ath6kl/os/linux/cfg80211.c @@ -867,26 +867,31 @@ ar6k_cfg80211_scanComplete_event(struct ar6_softc *ar, int status) AR_DEBUG_PRINTF(ATH_DEBUG_INFO, ("%s: status %d\n", __func__, status)); - if(ar->scan_request) - { - /* Translate data to cfg80211 mgmt format */ - if (ar->arWmi) - wmi_iterate_nodes(ar->arWmi, ar6k_cfg80211_scan_node, ar->wdev->wiphy); + if (!ar->scan_request) + return; + + if ((status == A_ECANCELED) || (status == A_EBUSY)) { + cfg80211_scan_done(ar->scan_request, true); + goto out; + } + + /* Translate data to cfg80211 mgmt format */ + wmi_iterate_nodes(ar->arWmi, ar6k_cfg80211_scan_node, ar->wdev->wiphy); - cfg80211_scan_done(ar->scan_request, - ((status & A_ECANCELED) || (status & A_EBUSY)) ? true : false); + cfg80211_scan_done(ar->scan_request, false); - if(ar->scan_request->n_ssids && - ar->scan_request->ssids[0].ssid_len) { + if(ar->scan_request->n_ssids && + ar->scan_request->ssids[0].ssid_len) { u8 i; for (i = 0; i < ar->scan_request->n_ssids; i++) { - wmi_probedSsid_cmd(ar->arWmi, i+1, DISABLE_SSID_FLAG, - 0, NULL); + wmi_probedSsid_cmd(ar->arWmi, i+1, DISABLE_SSID_FLAG, + 0, NULL); } - } - ar->scan_request = NULL; } + +out: + ar->scan_request = NULL; } static int diff --git a/drivers/staging/ath6kl/os/linux/include/ar6000_drv.h b/drivers/staging/ath6kl/os/linux/include/ar6000_drv.h index 22453b0873e..2911ea00a81 100644 --- a/drivers/staging/ath6kl/os/linux/include/ar6000_drv.h +++ b/drivers/staging/ath6kl/os/linux/include/ar6000_drv.h @@ -651,6 +651,15 @@ struct ar6_softc { void *arApDev; #endif u8 arAutoAuthStage; + + u8 *fw_otp; + size_t fw_otp_len; + u8 *fw; + size_t fw_len; + u8 *fw_patch; + size_t fw_patch_len; + u8 *fw_data; + size_t fw_data_len; }; #ifdef CONFIG_AP_VIRTUAL_ADAPTER_SUPPORT diff --git a/drivers/staging/brcm80211/brcmsmac/wl_mac80211.c b/drivers/staging/brcm80211/brcmsmac/wl_mac80211.c index 6c6236c969b..aa0d1274279 100644 --- a/drivers/staging/brcm80211/brcmsmac/wl_mac80211.c +++ b/drivers/staging/brcm80211/brcmsmac/wl_mac80211.c @@ -449,11 +449,6 @@ wl_ops_bss_info_changed(struct ieee80211_hw *hw, wiphy_err(wiphy, "%s: qos enabled: %s (implement)\n", __func__, info->qos ? "true" : "false"); } - if (changed & BSS_CHANGED_IDLE) { - /* Idle changed for this BSS/interface */ - wiphy_err(wiphy, "%s: BSS idle: %s (implement)\n", __func__, - info->idle ? "true" : "false"); - } return; } diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c index e7e72b8d8cd..c20694e6515 100644 --- a/drivers/staging/comedi/comedi_fops.c +++ b/drivers/staging/comedi/comedi_fops.c @@ -383,8 +383,8 @@ static int do_devinfo_ioctl(struct comedi_device *dev, /* fill devinfo structure */ devinfo.version_code = COMEDI_VERSION_CODE; devinfo.n_subdevs = dev->n_subdevices; - memcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN); - memcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN); + strlcpy(devinfo.driver_name, dev->driver->driver_name, COMEDI_NAMELEN); + strlcpy(devinfo.board_name, dev->board_name, COMEDI_NAMELEN); if (read_subdev) devinfo.read_subdevice = read_subdev - dev->subdevices; diff --git a/drivers/staging/hv/channel.c b/drivers/staging/hv/channel.c index f655e59a9a8..d971bab87e9 100644 --- a/drivers/staging/hv/channel.c +++ b/drivers/staging/hv/channel.c @@ -212,7 +212,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, if (ret != 0) goto Cleanup; - t = wait_for_completion_timeout(&openInfo->waitevent, HZ); + t = wait_for_completion_timeout(&openInfo->waitevent, 5*HZ); if (t == 0) { err = -ETIMEDOUT; goto errorout; diff --git a/drivers/staging/hv/channel_mgmt.c b/drivers/staging/hv/channel_mgmt.c index 957d61ee4ce..e2e7d057f6a 100644 --- a/drivers/staging/hv/channel_mgmt.c +++ b/drivers/staging/hv/channel_mgmt.c @@ -773,7 +773,7 @@ int vmbus_request_offers(void) goto cleanup; } - t = wait_for_completion_timeout(&msginfo->waitevent, HZ); + t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; goto cleanup; diff --git a/drivers/staging/hv/connection.c b/drivers/staging/hv/connection.c index 37bbf770ef1..91c65ad3509 100644 --- a/drivers/staging/hv/connection.c +++ b/drivers/staging/hv/connection.c @@ -135,7 +135,7 @@ int vmbus_connect(void) } /* Wait for the connection response */ - t = wait_for_completion_timeout(&msginfo->waitevent, HZ); + t = wait_for_completion_timeout(&msginfo->waitevent, 5*HZ); if (t == 0) { spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); diff --git a/drivers/staging/hv/netvsc.c b/drivers/staging/hv/netvsc.c index 41cbb26eccb..4742b684ca2 100644 --- a/drivers/staging/hv/netvsc.c +++ b/drivers/staging/hv/netvsc.c @@ -270,7 +270,7 @@ static int netvsc_init_recv_buf(struct hv_device *device) goto cleanup; } - t = wait_for_completion_timeout(&net_device->channel_init_wait, HZ); + t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); BUG_ON(t == 0); @@ -513,7 +513,7 @@ static int netvsc_connect_vsp(struct hv_device *device) if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&net_device->channel_init_wait, HZ); + t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; diff --git a/drivers/staging/hv/rndis_filter.c b/drivers/staging/hv/rndis_filter.c index 60ebdb1b608..b47ebb3513f 100644 --- a/drivers/staging/hv/rndis_filter.c +++ b/drivers/staging/hv/rndis_filter.c @@ -467,7 +467,7 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, if (ret != 0) goto Cleanup; - t = wait_for_completion_timeout(&request->wait_event, HZ); + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; goto Cleanup; @@ -543,7 +543,7 @@ static int rndis_filter_set_packet_filter(struct rndis_device *dev, if (ret != 0) goto Cleanup; - t = wait_for_completion_timeout(&request->wait_event, HZ); + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { ret = -1; @@ -600,7 +600,7 @@ static int rndis_filter_init_device(struct rndis_device *dev) } - t = wait_for_completion_timeout(&request->wait_event, HZ); + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; diff --git a/drivers/staging/hv/storvsc.c b/drivers/staging/hv/storvsc.c index 06cd3276813..30297861194 100644 --- a/drivers/staging/hv/storvsc.c +++ b/drivers/staging/hv/storvsc.c @@ -135,7 +135,7 @@ static int storvsc_channel_init(struct hv_device *device) if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, HZ); + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; goto cleanup; @@ -163,7 +163,7 @@ static int storvsc_channel_init(struct hv_device *device) if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, HZ); + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; goto cleanup; @@ -192,7 +192,7 @@ static int storvsc_channel_init(struct hv_device *device) if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, HZ); + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; goto cleanup; @@ -222,7 +222,7 @@ static int storvsc_channel_init(struct hv_device *device) if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, HZ); + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; goto cleanup; diff --git a/drivers/staging/hv/storvsc_drv.c b/drivers/staging/hv/storvsc_drv.c index 942cc5f98db..cb4a25b0831 100644 --- a/drivers/staging/hv/storvsc_drv.c +++ b/drivers/staging/hv/storvsc_drv.c @@ -393,7 +393,7 @@ static int storvsc_host_reset(struct hv_device *device) if (ret != 0) goto cleanup; - t = wait_for_completion_timeout(&request->wait_event, HZ); + t = wait_for_completion_timeout(&request->wait_event, 5*HZ); if (t == 0) { ret = -ETIMEDOUT; goto cleanup; diff --git a/drivers/staging/rtl8192e/r8192E_core.c b/drivers/staging/rtl8192e/r8192E_core.c index 58d800f1b5e..cd98f89079b 100644 --- a/drivers/staging/rtl8192e/r8192E_core.c +++ b/drivers/staging/rtl8192e/r8192E_core.c @@ -4532,6 +4532,7 @@ static int __devinit rtl8192_pci_probe(struct pci_dev *pdev, u8 unit = 0; int ret = -ENODEV; unsigned long pmem_start, pmem_len, pmem_flags; + u8 revisionid; RT_TRACE(COMP_INIT,"Configuring chip resources\n"); @@ -4592,6 +4593,11 @@ static int __devinit rtl8192_pci_probe(struct pci_dev *pdev, pci_write_config_byte(pdev, 0x41, 0x00); + pci_read_config_byte(pdev, 0x08, &revisionid); + /* If the revisionid is 0x10, the device uses rtl8192se. */ + if (pdev->device == 0x8192 && revisionid == 0x10) + goto fail1; + pci_read_config_byte(pdev, 0x05, &unit); pci_write_config_byte(pdev, 0x05, unit & (~0x04)); diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c index a76e8fa69b6..76d7485f4e2 100644 --- a/drivers/staging/usbip/vhci_hcd.c +++ b/drivers/staging/usbip/vhci_hcd.c @@ -846,9 +846,9 @@ static void vhci_shutdown_connection(struct usbip_device *ud) } /* kill threads related to this sdev, if v.c. exists */ - if (vdev->ud.tcp_rx) + if (vdev->ud.tcp_rx && !task_is_dead(vdev->ud.tcp_rx)) kthread_stop(vdev->ud.tcp_rx); - if (vdev->ud.tcp_tx) + if (vdev->ud.tcp_tx && !task_is_dead(vdev->ud.tcp_tx)) kthread_stop(vdev->ud.tcp_tx); pr_info("stop threads\n"); diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c index e9cba13ee80..aa84555048b 100644 --- a/drivers/tty/hvc/hvc_console.c +++ b/drivers/tty/hvc/hvc_console.c @@ -163,8 +163,10 @@ static void hvc_console_print(struct console *co, const char *b, } else { r = cons_ops[index]->put_chars(vtermnos[index], c, i); if (r <= 0) { - /* throw away chars on error */ - i = 0; + /* throw away characters on error + * but spin in case of -EAGAIN */ + if (r != -EAGAIN) + i = 0; } else if (r > 0) { i -= r; if (i > 0) @@ -448,7 +450,7 @@ static int hvc_push(struct hvc_struct *hp) n = hp->ops->put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf); if (n <= 0) { - if (n == 0) { + if (n == 0 || n == -EAGAIN) { hp->do_wakeup = 1; return 0; } diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 19b4ae052af..c0d34addc2d 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -1823,10 +1823,6 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c) break; case GSM_FCS: /* FCS follows the packet */ gsm->received_fcs = c; - if (c == GSM0_SOF) { - gsm->state = GSM_SEARCH; - break; - } gsm_queue(gsm); gsm->state = GSM_SSOF; break; diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c index b4129f53fb1..d32b5bb65ec 100644 --- a/drivers/tty/serial/8250.c +++ b/drivers/tty/serial/8250.c @@ -1107,7 +1107,7 @@ static void autoconfig_16550a(struct uart_8250_port *up) */ DEBUG_AUTOCONF("Xscale "); up->port.type = PORT_XSCALE; - up->capabilities |= UART_CAP_UUE; + up->capabilities |= UART_CAP_UUE | UART_CAP_RTOIE; return; } } else { diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 636144cea93..b3692e6e3c1 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1419,7 +1419,7 @@ config SERIAL_SC26XX config SERIAL_SC26XX_CONSOLE bool "Console on SC2681/SC2692 serial port" - depends on SERIAL_SC26XX + depends on SERIAL_SC26XX=y select SERIAL_CORE_CONSOLE help Support for Console on SC2681/SC2692 serial ports. diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f8030ee928e..9ff9abc7e3a 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -94,7 +94,8 @@ static const char hcd_name [] = "ehci_hcd"; #define EHCI_IAA_MSECS 10 /* arbitrary */ #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ -#define EHCI_SHRINK_FRAMES 5 /* async qh unlink delay */ +#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) + /* 200-ms async qh unlink delay */ /* Initial IRQ latency: faster than hw default */ static int log2_irq_thresh = 0; // 0 to 6 @@ -152,10 +153,7 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) break; /* case TIMER_ASYNC_SHRINK: */ default: - /* add a jiffie since we synch against the - * 8 KHz uframe counter. - */ - t = DIV_ROUND_UP(EHCI_SHRINK_FRAMES * HZ, 1000) + 1; + t = EHCI_SHRINK_JIFFIES; break; } mod_timer(&ehci->watchdog, t + jiffies); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index ea6184bf48d..88cfb8fadb7 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -891,10 +891,11 @@ static int ehci_hub_control ( * power switching; they're allowed to just limit the * current. khubd will turn the power back on. */ - if (HCS_PPC (ehci->hcs_params)){ + if ((temp & PORT_OC) && HCS_PPC(ehci->hcs_params)) { ehci_writel(ehci, temp & ~(PORT_RWC_BITS | PORT_POWER), status_reg); + temp = ehci_readl(ehci, status_reg); } } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 5d6bc624c96..0917e3a3246 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -103,7 +103,7 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd) if (!(hw->hw_info1 & cpu_to_hc32(ehci, 1 << 14))) { unsigned is_out, epnum; - is_out = !(qtd->hw_token & cpu_to_hc32(ehci, 1 << 8)); + is_out = qh->is_out; epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f; if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE); @@ -946,6 +946,7 @@ done: hw = qh->hw; hw->hw_info1 = cpu_to_hc32(ehci, info1); hw->hw_info2 = cpu_to_hc32(ehci, info2); + qh->is_out = !is_input; usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); qh_refresh (ehci, qh); return qh; @@ -1231,6 +1232,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) prev->hw->hw_next = qh->hw->hw_next; prev->qh_next = qh->qh_next; + if (ehci->qh_scan_next == qh) + ehci->qh_scan_next = qh->qh_next.qh; wmb (); /* If the controller isn't running, we don't have to wait for it */ @@ -1256,53 +1259,49 @@ static void scan_async (struct ehci_hcd *ehci) struct ehci_qh *qh; enum ehci_timer_action action = TIMER_IO_WATCHDOG; - ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index); timer_action_done (ehci, TIMER_ASYNC_SHRINK); -rescan: stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state); - qh = ehci->async->qh_next.qh; - if (likely (qh != NULL)) { - do { - /* clean any finished work for this qh */ - if (!list_empty(&qh->qtd_list) && (stopped || - qh->stamp != ehci->stamp)) { - int temp; - - /* unlinks could happen here; completion - * reporting drops the lock. rescan using - * the latest schedule, but don't rescan - * qhs we already finished (no looping) - * unless the controller is stopped. - */ - qh = qh_get (qh); - qh->stamp = ehci->stamp; - temp = qh_completions (ehci, qh); - if (qh->needs_rescan) - unlink_async(ehci, qh); - qh_put (qh); - if (temp != 0) { - goto rescan; - } - } - /* unlink idle entries, reducing DMA usage as well - * as HCD schedule-scanning costs. delay for any qh - * we just scanned, there's a not-unusual case that it - * doesn't stay idle for long. - * (plus, avoids some kind of re-activation race.) + ehci->qh_scan_next = ehci->async->qh_next.qh; + while (ehci->qh_scan_next) { + qh = ehci->qh_scan_next; + ehci->qh_scan_next = qh->qh_next.qh; + rescan: + /* clean any finished work for this qh */ + if (!list_empty(&qh->qtd_list)) { + int temp; + + /* + * Unlinks could happen here; completion reporting + * drops the lock. That's why ehci->qh_scan_next + * always holds the next qh to scan; if the next qh + * gets unlinked then ehci->qh_scan_next is adjusted + * in start_unlink_async(). */ - if (list_empty(&qh->qtd_list) - && qh->qh_state == QH_STATE_LINKED) { - if (!ehci->reclaim && (stopped || - ((ehci->stamp - qh->stamp) & 0x1fff) - >= EHCI_SHRINK_FRAMES * 8)) - start_unlink_async(ehci, qh); - else - action = TIMER_ASYNC_SHRINK; - } + qh = qh_get(qh); + temp = qh_completions(ehci, qh); + if (qh->needs_rescan) + unlink_async(ehci, qh); + qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; + qh_put(qh); + if (temp != 0) + goto rescan; + } - qh = qh->qh_next.qh; - } while (qh); + /* unlink idle entries, reducing DMA usage as well + * as HCD schedule-scanning costs. delay for any qh + * we just scanned, there's a not-unusual case that it + * doesn't stay idle for long. + * (plus, avoids some kind of re-activation race.) + */ + if (list_empty(&qh->qtd_list) + && qh->qh_state == QH_STATE_LINKED) { + if (!ehci->reclaim && (stopped || + time_after_eq(jiffies, qh->unlink_time))) + start_unlink_async(ehci, qh); + else + action = TIMER_ASYNC_SHRINK; + } } if (action == TIMER_ASYNC_SHRINK) timer_action (ehci, TIMER_ASYNC_SHRINK); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bd6ff489baf..989e0a8e01f 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -75,6 +75,7 @@ struct ehci_hcd { /* one per controller */ struct ehci_qh *async; struct ehci_qh *dummy; /* For AMD quirk use */ struct ehci_qh *reclaim; + struct ehci_qh *qh_scan_next; unsigned scanning : 1; /* periodic schedule support */ @@ -117,7 +118,6 @@ struct ehci_hcd { /* one per controller */ struct timer_list iaa_watchdog; struct timer_list watchdog; unsigned long actions; - unsigned stamp; unsigned periodic_stamp; unsigned random_frame; unsigned long next_statechange; @@ -343,6 +343,7 @@ struct ehci_qh { struct ehci_qh *reclaim; /* next to reclaim */ struct ehci_hcd *ehci; + unsigned long unlink_time; /* * Do NOT use atomic operations for QH refcounting. On some CPUs @@ -374,6 +375,7 @@ struct ehci_qh { #define NO_FRAME ((unsigned short)~0) /* pick new start */ struct usb_device *dev; /* access to TT */ + unsigned is_out:1; /* bulk or intr OUT */ unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ }; diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index fd930618c28..04b90ad319d 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -35,6 +35,8 @@ #define OHCI_INTRSTATUS 0x0c #define OHCI_INTRENABLE 0x10 #define OHCI_INTRDISABLE 0x14 +#define OHCI_FMINTERVAL 0x34 +#define OHCI_HCR (1 << 0) /* host controller reset */ #define OHCI_OCR (1 << 3) /* ownership change request */ #define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ #define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ @@ -497,6 +499,32 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) /* reset controller, preserving RWC (and possibly IR) */ writel(control & OHCI_CTRL_MASK, base + OHCI_CONTROL); + readl(base + OHCI_CONTROL); + + /* Some NVIDIA controllers stop working if kept in RESET for too long */ + if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) { + u32 fminterval; + int cnt; + + /* drive reset for at least 50 ms (7.1.7.5) */ + msleep(50); + + /* software reset of the controller, preserving HcFmInterval */ + fminterval = readl(base + OHCI_FMINTERVAL); + writel(OHCI_HCR, base + OHCI_CMDSTATUS); + + /* reset requires max 10 us delay */ + for (cnt = 30; cnt > 0; --cnt) { /* ... allow extra time */ + if ((readl(base + OHCI_CMDSTATUS) & OHCI_HCR) == 0) + break; + udelay(1); + } + writel(fminterval, base + OHCI_FMINTERVAL); + + /* Now we're in the SUSPEND state with all devices reset + * and wakeups and interrupts disabled + */ + } /* * disable interrupts diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index c71b0372786..dce7182e1df 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2329,6 +2329,7 @@ static void musb_restore_context(struct musb *musb) musb->context.index_regs[i].rxhubport); } } + musb_writeb(musb_base, MUSB_INDEX, musb->context.index); } static int musb_suspend(struct device *dev) diff --git a/drivers/usb/otg/otg_id.c b/drivers/usb/otg/otg_id.c index 2398b1a951d..ce22b462130 100644 --- a/drivers/usb/otg/otg_id.c +++ b/drivers/usb/otg/otg_id.c @@ -44,16 +44,24 @@ static void __otg_id_notify(void) { int ret; struct otg_id_notifier_block *otg_id_nb; - + bool proxy_wait = false; if (plist_head_empty(&otg_id_plist)) return; plist_for_each_entry(otg_id_nb, &otg_id_plist, p) { - ret = otg_id_nb->detect(otg_id_nb); + if (proxy_wait) { + if (otg_id_nb->proxy_wait) + ret = otg_id_nb->proxy_wait(otg_id_nb); + } else { + ret = otg_id_nb->detect(otg_id_nb); + } if (ret == OTG_ID_HANDLED) { otg_id_active = otg_id_nb; return; } + if (ret == OTG_ID_PROXY_WAIT) + proxy_wait = true; + } WARN(1, "otg id event not handled"); diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 30461fcc220..0c208314d6e 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -91,6 +91,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(SONY_VENDOR_ID, SONY_QN3USB_PRODUCT_ID) }, { USB_DEVICE(SANWA_VENDOR_ID, SANWA_PRODUCT_ID) }, { USB_DEVICE(ADLINK_VENDOR_ID, ADLINK_ND6530_PRODUCT_ID) }, + { USB_DEVICE(WINCHIPHEAD_VENDOR_ID, WINCHIPHEAD_USBSER_PRODUCT_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 1b025f75daf..ca0d237683b 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -144,3 +144,7 @@ /* ADLINK ND-6530 RS232,RS485 and RS422 adapter */ #define ADLINK_VENDOR_ID 0x0b63 #define ADLINK_ND6530_PRODUCT_ID 0x6530 + +/* WinChipHead USB->RS 232 adapter */ +#define WINCHIPHEAD_VENDOR_ID 0x4348 +#define WINCHIPHEAD_USBSER_PRODUCT_ID 0x5523 diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index db84f2322d1..a267dc078da 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c @@ -64,7 +64,7 @@ * misses its deadline, the kernel timer will allow the WDT to overflow. */ static int clock_division_ratio = WTCSR_CKS_4096; -#define next_ping_period(cks) msecs_to_jiffies(cks - 4) +#define next_ping_period(cks) (jiffies + msecs_to_jiffies(cks - 4)) static const struct watchdog_info sh_wdt_info; static struct platform_device *sh_wdt_dev; diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index fa8c21d913b..d8d26f334ca 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -641,7 +641,7 @@ lookup_out: static int cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd) { - if (nd->flags & LOOKUP_RCU) + if (nd && (nd->flags & LOOKUP_RCU)) return -ECHILD; if (direntry->d_inode) { diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 7349ade17de..4a4fad7fb85 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -69,6 +69,7 @@ static int ecryptfs_inode_set(struct inode *inode, void *opaque) inode->i_ino = lower_inode->i_ino; inode->i_version++; inode->i_mapping->a_ops = &ecryptfs_aops; + inode->i_mapping->backing_dev_info = inode->i_sb->s_bdi; if (S_ISLNK(inode->i_mode)) inode->i_op = &ecryptfs_symlink_iops; diff --git a/fs/ecryptfs/keystore.c b/fs/ecryptfs/keystore.c index 27a7fefb83e..89dc18e7e95 100644 --- a/fs/ecryptfs/keystore.c +++ b/fs/ecryptfs/keystore.c @@ -1868,11 +1868,6 @@ int ecryptfs_parse_packet_set(struct ecryptfs_crypt_stat *crypt_stat, * just one will be sufficient to decrypt to get the FEK. */ find_next_matching_auth_tok: found_auth_tok = 0; - if (auth_tok_key) { - up_write(&(auth_tok_key->sem)); - key_put(auth_tok_key); - auth_tok_key = NULL; - } list_for_each_entry(auth_tok_list_item, &auth_tok_list, list) { candidate_auth_tok = &auth_tok_list_item->auth_tok; if (unlikely(ecryptfs_verbosity > 0)) { @@ -1909,14 +1904,22 @@ found_matching_auth_tok: memcpy(&(candidate_auth_tok->token.private_key), &(matching_auth_tok->token.private_key), sizeof(struct ecryptfs_private_key)); + up_write(&(auth_tok_key->sem)); + key_put(auth_tok_key); rc = decrypt_pki_encrypted_session_key(candidate_auth_tok, crypt_stat); } else if (candidate_auth_tok->token_type == ECRYPTFS_PASSWORD) { memcpy(&(candidate_auth_tok->token.password), &(matching_auth_tok->token.password), sizeof(struct ecryptfs_password)); + up_write(&(auth_tok_key->sem)); + key_put(auth_tok_key); rc = decrypt_passphrase_encrypted_session_key( candidate_auth_tok, crypt_stat); + } else { + up_write(&(auth_tok_key->sem)); + key_put(auth_tok_key); + rc = -EINVAL; } if (rc) { struct ecryptfs_auth_tok_list_item *auth_tok_list_item_tmp; @@ -1956,15 +1959,12 @@ found_matching_auth_tok: out_wipe_list: wipe_auth_tok_list(&auth_tok_list); out: - if (auth_tok_key) { - up_write(&(auth_tok_key->sem)); - key_put(auth_tok_key); - } return rc; } static int -pki_encrypt_session_key(struct ecryptfs_auth_tok *auth_tok, +pki_encrypt_session_key(struct key *auth_tok_key, + struct ecryptfs_auth_tok *auth_tok, struct ecryptfs_crypt_stat *crypt_stat, struct ecryptfs_key_record *key_rec) { @@ -1979,6 +1979,8 @@ pki_encrypt_session_key(struct ecryptfs_auth_tok *auth_tok, crypt_stat->cipher, crypt_stat->key_size), crypt_stat, &payload, &payload_len); + up_write(&(auth_tok_key->sem)); + key_put(auth_tok_key); if (rc) { ecryptfs_printk(KERN_ERR, "Error generating tag 66 packet\n"); goto out; @@ -2008,6 +2010,8 @@ out: * write_tag_1_packet - Write an RFC2440-compatible tag 1 (public key) packet * @dest: Buffer into which to write the packet * @remaining_bytes: Maximum number of bytes that can be writtn + * @auth_tok_key: The authentication token key to unlock and put when done with + * @auth_tok * @auth_tok: The authentication token used for generating the tag 1 packet * @crypt_stat: The cryptographic context * @key_rec: The key record struct for the tag 1 packet @@ -2018,7 +2022,7 @@ out: */ static int write_tag_1_packet(char *dest, size_t *remaining_bytes, - struct ecryptfs_auth_tok *auth_tok, + struct key *auth_tok_key, struct ecryptfs_auth_tok *auth_tok, struct ecryptfs_crypt_stat *crypt_stat, struct ecryptfs_key_record *key_rec, size_t *packet_size) { @@ -2039,12 +2043,15 @@ write_tag_1_packet(char *dest, size_t *remaining_bytes, memcpy(key_rec->enc_key, auth_tok->session_key.encrypted_key, auth_tok->session_key.encrypted_key_size); + up_write(&(auth_tok_key->sem)); + key_put(auth_tok_key); goto encrypted_session_key_set; } if (auth_tok->session_key.encrypted_key_size == 0) auth_tok->session_key.encrypted_key_size = auth_tok->token.private_key.key_size; - rc = pki_encrypt_session_key(auth_tok, crypt_stat, key_rec); + rc = pki_encrypt_session_key(auth_tok_key, auth_tok, crypt_stat, + key_rec); if (rc) { printk(KERN_ERR "Failed to encrypt session key via a key " "module; rc = [%d]\n", rc); @@ -2421,6 +2428,8 @@ ecryptfs_generate_key_packet_set(char *dest_base, &max, auth_tok, crypt_stat, key_rec, &written); + up_write(&(auth_tok_key->sem)); + key_put(auth_tok_key); if (rc) { ecryptfs_printk(KERN_WARNING, "Error " "writing tag 3 packet\n"); @@ -2438,8 +2447,8 @@ ecryptfs_generate_key_packet_set(char *dest_base, } (*len) += written; } else if (auth_tok->token_type == ECRYPTFS_PRIVATE_KEY) { - rc = write_tag_1_packet(dest_base + (*len), - &max, auth_tok, + rc = write_tag_1_packet(dest_base + (*len), &max, + auth_tok_key, auth_tok, crypt_stat, key_rec, &written); if (rc) { ecryptfs_printk(KERN_WARNING, "Error " @@ -2448,14 +2457,13 @@ ecryptfs_generate_key_packet_set(char *dest_base, } (*len) += written; } else { + up_write(&(auth_tok_key->sem)); + key_put(auth_tok_key); ecryptfs_printk(KERN_WARNING, "Unsupported " "authentication token type\n"); rc = -EINVAL; goto out_free; } - up_write(&(auth_tok_key->sem)); - key_put(auth_tok_key); - auth_tok_key = NULL; } if (likely(max > 0)) { dest_base[(*len)] = 0x00; @@ -2468,11 +2476,6 @@ out_free: out: if (rc) (*len) = 0; - if (auth_tok_key) { - up_write(&(auth_tok_key->sem)); - key_put(auth_tok_key); - } - mutex_unlock(&crypt_stat->keysig_list_mutex); return rc; } diff --git a/fs/ext3/xattr.c b/fs/ext3/xattr.c index 32e6cc23bd9..d565759d82e 100644 --- a/fs/ext3/xattr.c +++ b/fs/ext3/xattr.c @@ -803,8 +803,16 @@ inserted: /* We need to allocate a new block */ ext3_fsblk_t goal = ext3_group_first_block_no(sb, EXT3_I(inode)->i_block_group); - ext3_fsblk_t block = ext3_new_block(handle, inode, - goal, &error); + ext3_fsblk_t block; + + /* + * Protect us agaist concurrent allocations to the + * same inode from ext3_..._writepage(). Reservation + * code does not expect racing allocations. + */ + mutex_lock(&EXT3_I(inode)->truncate_mutex); + block = ext3_new_block(handle, inode, goal, &error); + mutex_unlock(&EXT3_I(inode)->truncate_mutex); if (error) goto cleanup; ea_idebug(inode, "creating block %d", block); diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 1921392cd70..354619a1aed 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -526,6 +526,7 @@ struct ext4_new_group_data { #define EXT4_FREE_BLOCKS_METADATA 0x0001 #define EXT4_FREE_BLOCKS_FORGET 0x0002 #define EXT4_FREE_BLOCKS_VALIDATED 0x0004 +#define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008 /* * ioctl commands diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index f815cc81e7a..f3aacb32059 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -3596,17 +3596,18 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, } err = check_eofblocks_fl(handle, inode, map->m_lblk, path, ar.len); - if (err) - goto out2; - - err = ext4_ext_insert_extent(handle, inode, path, &newex, flags); + if (!err) + err = ext4_ext_insert_extent(handle, inode, path, + &newex, flags); if (err) { + int fb_flags = flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE ? + EXT4_FREE_BLOCKS_NO_QUOT_UPDATE : 0; /* free data blocks we just allocated */ /* not a good idea to call discard here directly, * but otherwise we'd need to call it every free() */ ext4_discard_preallocations(inode); ext4_free_blocks(handle, inode, NULL, ext4_ext_pblock(&newex), - ext4_ext_get_actual_len(&newex), 0); + ext4_ext_get_actual_len(&newex), fb_flags); goto out2; } diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 6ed859d5685..0f1be7f1637 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -4637,7 +4637,7 @@ do_more: } ext4_mark_super_dirty(sb); error_return: - if (freed) + if (freed && !(flags & EXT4_FREE_BLOCKS_NO_QUOT_UPDATE)) dquot_free_block(inode, freed); brelse(bitmap_bh); ext4_std_error(sb, err); diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 2a77071fb7b..fa780e66691 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -1018,13 +1018,13 @@ hostdata_error: fsname++; if (lm->lm_mount == NULL) { fs_info(sdp, "Now mounting FS...\n"); - complete(&sdp->sd_locking_init); + complete_all(&sdp->sd_locking_init); return 0; } ret = lm->lm_mount(sdp, fsname); if (ret == 0) fs_info(sdp, "Joined cluster. Now mounting FS...\n"); - complete(&sdp->sd_locking_init); + complete_all(&sdp->sd_locking_init); return ret; } diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index dd25c2aec37..321a66bc384 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -398,12 +398,11 @@ int nfs_inode_return_delegation(struct inode *inode) return err; } -static void nfs_mark_return_delegation(struct nfs_delegation *delegation) +static void nfs_mark_return_delegation(struct nfs_server *server, + struct nfs_delegation *delegation) { - struct nfs_client *clp = NFS_SERVER(delegation->inode)->nfs_client; - set_bit(NFS_DELEGATION_RETURN, &delegation->flags); - set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); + set_bit(NFS4CLNT_DELEGRETURN, &server->nfs_client->cl_state); } /** @@ -441,7 +440,7 @@ static void nfs_mark_return_all_delegation_types(struct nfs_server *server, if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) continue; if (delegation->type & flags) - nfs_mark_return_delegation(delegation); + nfs_mark_return_delegation(server, delegation); } } @@ -508,7 +507,7 @@ static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) list_for_each_entry_rcu(delegation, &server->delegations, super_list) { if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) continue; - nfs_mark_return_delegation(delegation); + nfs_mark_return_delegation(server, delegation); } } @@ -539,7 +538,8 @@ void nfs_expire_unreferenced_delegations(struct nfs_client *clp) int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid) { - struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_client *clp = server->nfs_client; struct nfs_delegation *delegation; rcu_read_lock(); @@ -549,7 +549,7 @@ int nfs_async_inode_return_delegation(struct inode *inode, rcu_read_unlock(); return -ENOENT; } - nfs_mark_return_delegation(delegation); + nfs_mark_return_delegation(server, delegation); rcu_read_unlock(); nfs_delegation_run_state_manager(clp); diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index ededdbd0db3..f91c62d48ff 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -134,18 +134,19 @@ const struct inode_operations nfs4_dir_inode_operations = { #endif /* CONFIG_NFS_V4 */ -static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct rpc_cred *cred) +static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred) { struct nfs_open_dir_context *ctx; ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); if (ctx != NULL) { ctx->duped = 0; + ctx->attr_gencount = NFS_I(dir)->attr_gencount; ctx->dir_cookie = 0; ctx->dup_cookie = 0; ctx->cred = get_rpccred(cred); - } else - ctx = ERR_PTR(-ENOMEM); - return ctx; + return ctx; + } + return ERR_PTR(-ENOMEM); } static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx) @@ -173,7 +174,7 @@ nfs_opendir(struct inode *inode, struct file *filp) cred = rpc_lookup_cred(); if (IS_ERR(cred)) return PTR_ERR(cred); - ctx = alloc_nfs_open_dir_context(cred); + ctx = alloc_nfs_open_dir_context(inode, cred); if (IS_ERR(ctx)) { res = PTR_ERR(ctx); goto out; @@ -323,7 +324,6 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri { loff_t diff = desc->file->f_pos - desc->current_index; unsigned int index; - struct nfs_open_dir_context *ctx = desc->file->private_data; if (diff < 0) goto out_eof; @@ -336,7 +336,6 @@ int nfs_readdir_search_for_pos(struct nfs_cache_array *array, nfs_readdir_descri index = (unsigned int)diff; *desc->dir_cookie = array->array[index].cookie; desc->cache_entry_index = index; - ctx->duped = 0; return 0; out_eof: desc->eof = 1; @@ -349,14 +348,33 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des int i; loff_t new_pos; int status = -EAGAIN; - struct nfs_open_dir_context *ctx = desc->file->private_data; for (i = 0; i < array->size; i++) { if (array->array[i].cookie == *desc->dir_cookie) { + struct nfs_inode *nfsi = NFS_I(desc->file->f_path.dentry->d_inode); + struct nfs_open_dir_context *ctx = desc->file->private_data; + new_pos = desc->current_index + i; - if (new_pos < desc->file->f_pos) { + if (ctx->attr_gencount != nfsi->attr_gencount + || (nfsi->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))) { + ctx->duped = 0; + ctx->attr_gencount = nfsi->attr_gencount; + } else if (new_pos < desc->file->f_pos) { + if (ctx->duped > 0 + && ctx->dup_cookie == *desc->dir_cookie) { + if (printk_ratelimit()) { + pr_notice("NFS: directory %s/%s contains a readdir loop." + "Please contact your server vendor. " + "Offending cookie: %llu\n", + desc->file->f_dentry->d_parent->d_name.name, + desc->file->f_dentry->d_name.name, + *desc->dir_cookie); + } + status = -ELOOP; + goto out; + } ctx->dup_cookie = *desc->dir_cookie; - ctx->duped = 1; + ctx->duped = -1; } desc->file->f_pos = new_pos; desc->cache_entry_index = i; @@ -368,6 +386,7 @@ int nfs_readdir_search_for_cookie(struct nfs_cache_array *array, nfs_readdir_des if (*desc->dir_cookie == array->last_cookie) desc->eof = 1; } +out: return status; } @@ -740,19 +759,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, struct nfs_cache_array *array = NULL; struct nfs_open_dir_context *ctx = file->private_data; - if (ctx->duped != 0 && ctx->dup_cookie == *desc->dir_cookie) { - if (printk_ratelimit()) { - pr_notice("NFS: directory %s/%s contains a readdir loop. " - "Please contact your server vendor. " - "Offending cookie: %llu\n", - file->f_dentry->d_parent->d_name.name, - file->f_dentry->d_name.name, - *desc->dir_cookie); - } - res = -ELOOP; - goto out; - } - array = nfs_readdir_get_array(desc->page); if (IS_ERR(array)) { res = PTR_ERR(array); @@ -774,6 +780,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent, *desc->dir_cookie = array->array[i+1].cookie; else *desc->dir_cookie = array->last_cookie; + if (ctx->duped != 0) + ctx->duped = 1; } if (array->eof_index >= 0) desc->eof = 1; @@ -805,6 +813,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, struct page *page = NULL; int status; struct inode *inode = desc->file->f_path.dentry->d_inode; + struct nfs_open_dir_context *ctx = desc->file->private_data; dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n", (unsigned long long)*desc->dir_cookie); @@ -818,6 +827,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent, desc->page_index = 0; desc->last_cookie = *desc->dir_cookie; desc->page = page; + ctx->duped = 0; status = nfs_readdir_xdr_to_array(desc, page, inode); if (status < 0) diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index f9d03abcd04..614c4d287d7 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c @@ -170,7 +170,7 @@ filelayout_set_layoutcommit(struct nfs_write_data *wdata) pnfs_set_layoutcommit(wdata); dprintk("%s ionde %lu pls_end_pos %lu\n", __func__, wdata->inode->i_ino, - (unsigned long) wdata->lseg->pls_end_pos); + (unsigned long) NFS_I(wdata->inode)->layout->plh_lwb); } /* diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 5879b23e0c9..92cfd2e1131 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -5850,9 +5850,15 @@ nfs4_layoutcommit_done(struct rpc_task *task, void *calldata) static void nfs4_layoutcommit_release(void *calldata) { struct nfs4_layoutcommit_data *data = calldata; + struct pnfs_layout_segment *lseg, *tmp; /* Matched by references in pnfs_set_layoutcommit */ - put_lseg(data->lseg); + list_for_each_entry_safe(lseg, tmp, &data->lseg_list, pls_lc_list) { + list_del_init(&lseg->pls_lc_list); + if (test_and_clear_bit(NFS_LSEG_LAYOUTCOMMIT, + &lseg->pls_flags)) + put_lseg(lseg); + } put_rpccred(data->cred); kfree(data); } diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index e6e8f3b9a1d..fc97fd5399a 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1888,7 +1888,7 @@ encode_layoutcommit(struct xdr_stream *xdr, *p++ = cpu_to_be32(OP_LAYOUTCOMMIT); /* Only whole file layouts */ p = xdr_encode_hyper(p, 0); /* offset */ - p = xdr_encode_hyper(p, NFS4_MAX_UINT64); /* length */ + p = xdr_encode_hyper(p, args->lastbytewritten + 1); /* length */ *p++ = cpu_to_be32(0); /* reclaim */ p = xdr_encode_opaque_fixed(p, args->stateid.data, NFS4_STATEID_SIZE); *p++ = cpu_to_be32(1); /* newoffset = TRUE */ diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c index 29c0ca7fc34..a726c0afa76 100644 --- a/fs/nfs/pnfs.c +++ b/fs/nfs/pnfs.c @@ -189,6 +189,7 @@ static void pnfs_free_layout_hdr(struct pnfs_layout_hdr *lo) { struct pnfs_layoutdriver_type *ld = NFS_SERVER(lo->plh_inode)->pnfs_curr_ld; + put_rpccred(lo->plh_lc_cred); return ld->alloc_layout_hdr ? ld->free_layout_hdr(lo) : kfree(lo); } @@ -223,6 +224,7 @@ static void init_lseg(struct pnfs_layout_hdr *lo, struct pnfs_layout_segment *lseg) { INIT_LIST_HEAD(&lseg->pls_list); + INIT_LIST_HEAD(&lseg->pls_lc_list); atomic_set(&lseg->pls_refcount, 1); smp_mb(); set_bit(NFS_LSEG_VALID, &lseg->pls_flags); @@ -805,7 +807,9 @@ out: } static struct pnfs_layout_hdr * -alloc_init_layout_hdr(struct inode *ino, gfp_t gfp_flags) +alloc_init_layout_hdr(struct inode *ino, + struct nfs_open_context *ctx, + gfp_t gfp_flags) { struct pnfs_layout_hdr *lo; @@ -817,11 +821,14 @@ alloc_init_layout_hdr(struct inode *ino, gfp_t gfp_flags) INIT_LIST_HEAD(&lo->plh_segs); INIT_LIST_HEAD(&lo->plh_bulk_recall); lo->plh_inode = ino; + lo->plh_lc_cred = get_rpccred(ctx->state->owner->so_cred); return lo; } static struct pnfs_layout_hdr * -pnfs_find_alloc_layout(struct inode *ino, gfp_t gfp_flags) +pnfs_find_alloc_layout(struct inode *ino, + struct nfs_open_context *ctx, + gfp_t gfp_flags) { struct nfs_inode *nfsi = NFS_I(ino); struct pnfs_layout_hdr *new = NULL; @@ -836,7 +843,7 @@ pnfs_find_alloc_layout(struct inode *ino, gfp_t gfp_flags) return nfsi->layout; } spin_unlock(&ino->i_lock); - new = alloc_init_layout_hdr(ino, gfp_flags); + new = alloc_init_layout_hdr(ino, ctx, gfp_flags); spin_lock(&ino->i_lock); if (likely(nfsi->layout == NULL)) /* Won the race? */ @@ -928,7 +935,7 @@ pnfs_update_layout(struct inode *ino, if (!pnfs_enabled_sb(NFS_SERVER(ino))) return NULL; spin_lock(&ino->i_lock); - lo = pnfs_find_alloc_layout(ino, gfp_flags); + lo = pnfs_find_alloc_layout(ino, ctx, gfp_flags); if (lo == NULL) { dprintk("%s ERROR: can't get pnfs_layout_hdr\n", __func__); goto out_unlock; @@ -1195,16 +1202,17 @@ pnfs_try_to_read_data(struct nfs_read_data *rdata, } /* - * Currently there is only one (whole file) write lseg. + * There can be multiple RW segments. */ -static struct pnfs_layout_segment *pnfs_list_write_lseg(struct inode *inode) +static void pnfs_list_write_lseg(struct inode *inode, struct list_head *listp) { - struct pnfs_layout_segment *lseg, *rv = NULL; + struct pnfs_layout_segment *lseg; - list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) - if (lseg->pls_range.iomode == IOMODE_RW) - rv = lseg; - return rv; + list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list) { + if (lseg->pls_range.iomode == IOMODE_RW && + test_bit(NFS_LSEG_LAYOUTCOMMIT, &lseg->pls_flags)) + list_add(&lseg->pls_lc_list, listp); + } } void @@ -1216,17 +1224,19 @@ pnfs_set_layoutcommit(struct nfs_write_data *wdata) spin_lock(&nfsi->vfs_inode.i_lock); if (!test_and_set_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) { - /* references matched in nfs4_layoutcommit_release */ - get_lseg(wdata->lseg); - wdata->lseg->pls_lc_cred = - get_rpccred(wdata->args.context->state->owner->so_cred); mark_as_dirty = true; dprintk("%s: Set layoutcommit for inode %lu ", __func__, wdata->inode->i_ino); } - if (end_pos > wdata->lseg->pls_end_pos) - wdata->lseg->pls_end_pos = end_pos; + if (!test_and_set_bit(NFS_LSEG_LAYOUTCOMMIT, &wdata->lseg->pls_flags)) { + /* references matched in nfs4_layoutcommit_release */ + get_lseg(wdata->lseg); + } + if (end_pos > nfsi->layout->plh_lwb) + nfsi->layout->plh_lwb = end_pos; spin_unlock(&nfsi->vfs_inode.i_lock); + dprintk("%s: lseg %p end_pos %llu\n", + __func__, wdata->lseg, nfsi->layout->plh_lwb); /* if pnfs_layoutcommit_inode() runs between inode locks, the next one * will be a noop because NFS_INO_LAYOUTCOMMIT will not be set */ @@ -1248,8 +1258,6 @@ pnfs_layoutcommit_inode(struct inode *inode, bool sync) { struct nfs4_layoutcommit_data *data; struct nfs_inode *nfsi = NFS_I(inode); - struct pnfs_layout_segment *lseg; - struct rpc_cred *cred; loff_t end_pos; int status = 0; @@ -1266,30 +1274,25 @@ pnfs_layoutcommit_inode(struct inode *inode, bool sync) goto out; } + INIT_LIST_HEAD(&data->lseg_list); spin_lock(&inode->i_lock); if (!test_and_clear_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) { spin_unlock(&inode->i_lock); kfree(data); goto out; } - /* - * Currently only one (whole file) write lseg which is referenced - * in pnfs_set_layoutcommit and will be found. - */ - lseg = pnfs_list_write_lseg(inode); - end_pos = lseg->pls_end_pos; - cred = lseg->pls_lc_cred; - lseg->pls_end_pos = 0; - lseg->pls_lc_cred = NULL; + pnfs_list_write_lseg(inode, &data->lseg_list); + + end_pos = nfsi->layout->plh_lwb; + nfsi->layout->plh_lwb = 0; memcpy(&data->args.stateid.data, nfsi->layout->plh_stateid.data, sizeof(nfsi->layout->plh_stateid.data)); spin_unlock(&inode->i_lock); data->args.inode = inode; - data->lseg = lseg; - data->cred = cred; + data->cred = get_rpccred(nfsi->layout->plh_lc_cred); nfs_fattr_init(&data->fattr); data->args.bitmask = NFS_SERVER(inode)->cache_consistency_bitmask; data->res.fattr = &data->fattr; diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h index 96bf4e6f45b..9d147d963bd 100644 --- a/fs/nfs/pnfs.h +++ b/fs/nfs/pnfs.h @@ -36,16 +36,16 @@ enum { NFS_LSEG_VALID = 0, /* cleared when lseg is recalled/returned */ NFS_LSEG_ROC, /* roc bit received from server */ + NFS_LSEG_LAYOUTCOMMIT, /* layoutcommit bit set for layoutcommit */ }; struct pnfs_layout_segment { struct list_head pls_list; + struct list_head pls_lc_list; struct pnfs_layout_range pls_range; atomic_t pls_refcount; unsigned long pls_flags; struct pnfs_layout_hdr *pls_layout; - struct rpc_cred *pls_lc_cred; /* LAYOUTCOMMIT credential */ - loff_t pls_end_pos; /* LAYOUTCOMMIT write end */ }; enum pnfs_try_status { @@ -124,6 +124,8 @@ struct pnfs_layout_hdr { unsigned long plh_block_lgets; /* block LAYOUTGET if >0 */ u32 plh_barrier; /* ignore lower seqids */ unsigned long plh_flags; + loff_t plh_lwb; /* last write byte for layoutcommit */ + struct rpc_cred *plh_lc_cred; /* layoutcommit cred */ struct inode *plh_inode; }; diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e98f3c2e949..3b8ad35561b 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -381,14 +381,6 @@ static int nfs4_access_to_omode(u32 access) BUG(); } -static int nfs4_access_bmap_to_omode(struct nfs4_stateid *stp) -{ - unsigned int access; - - set_access(&access, stp->st_access_bmap); - return nfs4_access_to_omode(access); -} - static void unhash_generic_stateid(struct nfs4_stateid *stp) { list_del(&stp->st_hash); @@ -398,11 +390,14 @@ static void unhash_generic_stateid(struct nfs4_stateid *stp) static void free_generic_stateid(struct nfs4_stateid *stp) { - int oflag; + int i; if (stp->st_access_bmap) { - oflag = nfs4_access_bmap_to_omode(stp); - nfs4_file_put_access(stp->st_file, oflag); + for (i = 1; i < 4; i++) { + if (test_bit(i, &stp->st_access_bmap)) + nfs4_file_put_access(stp->st_file, + nfs4_access_to_omode(i)); + } } put_nfs4_file(stp->st_file); kmem_cache_free(stateid_slab, stp); @@ -2337,15 +2332,6 @@ out: return ret; } -static inline void -nfs4_file_downgrade(struct nfs4_file *fp, unsigned int share_access) -{ - if (share_access & NFS4_SHARE_ACCESS_WRITE) - nfs4_file_put_access(fp, O_WRONLY); - if (share_access & NFS4_SHARE_ACCESS_READ) - nfs4_file_put_access(fp, O_RDONLY); -} - static void nfsd_break_one_deleg(struct nfs4_delegation *dp) { /* We're assuming the state code never drops its reference @@ -2556,12 +2542,18 @@ static inline int nfs4_access_to_access(u32 nfs4_access) return flags; } -static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file -*fp, struct svc_fh *cur_fh, u32 nfs4_access) +static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, + struct svc_fh *cur_fh, struct nfsd4_open *open) { __be32 status; - int oflag = nfs4_access_to_omode(nfs4_access); - int access = nfs4_access_to_access(nfs4_access); + int oflag = nfs4_access_to_omode(open->op_share_access); + int access = nfs4_access_to_access(open->op_share_access); + + /* CLAIM_DELEGATE_CUR is used in response to a broken lease; + * allowing it to break the lease and return EAGAIN leaves the + * client unable to make progress in returning the delegation */ + if (open->op_claim_type == NFS4_OPEN_CLAIM_DELEGATE_CUR) + access |= NFSD_MAY_NOT_BREAK_LEASE; if (!fp->fi_fds[oflag]) { status = nfsd_open(rqstp, cur_fh, S_IFREG, access, @@ -2586,7 +2578,7 @@ nfs4_new_open(struct svc_rqst *rqstp, struct nfs4_stateid **stpp, if (stp == NULL) return nfserr_resource; - status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open->op_share_access); + status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open); if (status) { kmem_cache_free(stateid_slab, stp); return status; @@ -2619,14 +2611,14 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *c new_access = !test_bit(op_share_access, &stp->st_access_bmap); if (new_access) { - status = nfs4_get_vfs_file(rqstp, fp, cur_fh, op_share_access); + status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open); if (status) return status; } status = nfsd4_truncate(rqstp, cur_fh, open); if (status) { if (new_access) { - int oflag = nfs4_access_to_omode(new_access); + int oflag = nfs4_access_to_omode(op_share_access); nfs4_file_put_access(fp, oflag); } return status; @@ -3384,18 +3376,15 @@ out: return status; } - -/* - * unset all bits in union bitmap (bmap) that - * do not exist in share (from successful OPEN_DOWNGRADE) - */ -static void -reset_union_bmap_access(unsigned long access, unsigned long *bmap) +static inline void nfs4_file_downgrade(struct nfs4_stateid *stp, unsigned int to_access) { int i; + for (i = 1; i < 4; i++) { - if ((i & access) != i) - __clear_bit(i, bmap); + if (test_bit(i, &stp->st_access_bmap) && !(i & to_access)) { + nfs4_file_put_access(stp->st_file, i); + __clear_bit(i, &stp->st_access_bmap); + } } } @@ -3416,7 +3405,6 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, { __be32 status; struct nfs4_stateid *stp; - unsigned int share_access; dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3445,10 +3433,8 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, stp->st_deny_bmap, od->od_share_deny); goto out; } - set_access(&share_access, stp->st_access_bmap); - nfs4_file_downgrade(stp->st_file, share_access & ~od->od_share_access); + nfs4_file_downgrade(stp, od->od_share_access); - reset_union_bmap_access(od->od_share_access, &stp->st_access_bmap); reset_union_bmap_deny(od->od_share_deny, &stp->st_deny_bmap); update_stateid(&stp->st_stateid); diff --git a/fs/proc/base.c b/fs/proc/base.c index cc9c0c3bf6f..efb304854b7 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2747,9 +2747,16 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole) { struct task_io_accounting acct = task->ioac; unsigned long flags; + int result; - if (!ptrace_may_access(task, PTRACE_MODE_READ)) - return -EACCES; + result = mutex_lock_killable(&task->signal->cred_guard_mutex); + if (result) + return result; + + if (!ptrace_may_access(task, PTRACE_MODE_READ)) { + result = -EACCES; + goto out_unlock; + } if (whole && lock_task_sighand(task, &flags)) { struct task_struct *t = task; @@ -2760,7 +2767,7 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole) unlock_task_sighand(task, &flags); } - return sprintf(buffer, + result = sprintf(buffer, "rchar: %llu\n" "wchar: %llu\n" "syscr: %llu\n" @@ -2775,6 +2782,9 @@ static int do_io_accounting(struct task_struct *task, char *buffer, int whole) (unsigned long long)acct.read_bytes, (unsigned long long)acct.write_bytes, (unsigned long long)acct.cancelled_write_bytes); +out_unlock: + mutex_unlock(&task->signal->cred_guard_mutex); + return result; } static int proc_tid_io_accounting(struct task_struct *task, char *buffer) diff --git a/include/linux/firewire-cdev.h b/include/linux/firewire-cdev.h index 4ff09889c5c..55814aa33be 100644 --- a/include/linux/firewire-cdev.h +++ b/include/linux/firewire-cdev.h @@ -475,6 +475,9 @@ union fw_cdev_event { * of the bus. This does not cause a bus reset to happen. * @bus_reset_closure: Value of &closure in this and subsequent bus reset events * @card: The index of the card this device belongs to + * + * As a side effect, reception of %FW_CDEV_EVENT_BUS_RESET events to be read(2) + * is started by this ioctl. */ struct fw_cdev_get_info { __u32 version; diff --git a/include/linux/ion.h b/include/linux/ion.h index 111982f48dc..aed8349279e 100644 --- a/include/linux/ion.h +++ b/include/linux/ion.h @@ -186,9 +186,14 @@ void ion_unmap_dma(struct ion_client *client, struct ion_handle *handle); * @client: the client * @handle: the handle to share * - * Given a handle, return a buffer which exists in a global name - * space and can be passed to other clients. Should be passed into ion_import + * Given a handle, return a buffer, which exists in a global name + * space, and can be passed to other clients. Should be passed into ion_import * to obtain a new handle for this buffer. + * + * NOTE: This function does do not an extra reference. The burden is on the + * caller to make sure the buffer doesn't go away while it's being passed to + * another client. That is, ion_free should not be called on this handle until + * the buffer has been imported into the other client. */ struct ion_buffer *ion_share(struct ion_client *client, struct ion_handle *handle); diff --git a/include/linux/mm.h b/include/linux/mm.h index de20025243e..f8213919ec9 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -986,6 +986,8 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, int get_user_pages_fast(unsigned long start, int nr_pages, int write, struct page **pages); struct page *get_dump_page(unsigned long addr); +extern int fixup_user_fault(struct task_struct *tsk, struct mm_struct *mm, + unsigned long address, unsigned int fault_flags); extern int try_to_release_page(struct page * page, gfp_t gfp_mask); extern void do_invalidatepage(struct page *page, unsigned long offset); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9e19477991a..33b5968e738 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1688,9 +1688,12 @@ static inline int skb_gro_header_hard(struct sk_buff *skb, unsigned int hlen) static inline void *skb_gro_header_slow(struct sk_buff *skb, unsigned int hlen, unsigned int offset) { + if (!pskb_may_pull(skb, hlen)) + return NULL; + NAPI_GRO_CB(skb)->frag0 = NULL; NAPI_GRO_CB(skb)->frag0_len = 0; - return pskb_may_pull(skb, hlen) ? skb->data + offset : NULL; + return skb->data + offset; } static inline void *skb_gro_mac_header(struct sk_buff *skb) diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 1b93b9c60e5..b522370fcc2 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h @@ -99,9 +99,10 @@ struct nfs_open_context { struct nfs_open_dir_context { struct rpc_cred *cred; + unsigned long attr_gencount; __u64 dir_cookie; __u64 dup_cookie; - int duped; + signed char duped; }; /* diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 00848d86ffb..be2eba7725a 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -262,7 +262,7 @@ struct nfs4_layoutcommit_res { struct nfs4_layoutcommit_data { struct rpc_task task; struct nfs_fattr fattr; - struct pnfs_layout_segment *lseg; + struct list_head lseg_list; struct rpc_cred *cred; struct nfs4_layoutcommit_args args; struct nfs4_layoutcommit_res res; diff --git a/include/linux/usb/otg_id.h b/include/linux/usb/otg_id.h index b686ab06795..46a44637c11 100644 --- a/include/linux/usb/otg_id.h +++ b/include/linux/usb/otg_id.h @@ -28,6 +28,10 @@ * get called first. * @detect: Called during otg_id_notify. Return OTG_ID_HANDLED if the USB cable * has been identified + * @proxy_wait: Called during otg_id_notify if a previous handler returns + * OTG_ID_PROXY_WAIT. This should wait on ID change then call otg_id_notify. + * This is used when a handler knows what's connected but can't detect + * the change itself. * @cancel: Called after detect has returned OTG_ID_HANDLED to ask it to * release detection resources to allow a new identification to occur. */ @@ -35,10 +39,12 @@ struct otg_id_notifier_block { int priority; int (*detect)(struct otg_id_notifier_block *otg_id_nb); + int (*proxy_wait)(struct otg_id_notifier_block *otg_id_nb); void (*cancel)(struct otg_id_notifier_block *otg_id_nb); struct plist_node p; }; +#define OTG_ID_PROXY_WAIT 2 #define OTG_ID_HANDLED 1 #define OTG_ID_UNHANDLED 0 diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 582e4ae7075..cbc6bb0a683 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -8,7 +8,7 @@ #define TEMP_VALID_LIFETIME (7*86400) #define TEMP_PREFERRED_LIFETIME (86400) -#define REGEN_MAX_RETRY (5) +#define REGEN_MAX_RETRY (3) #define MAX_DESYNC_FACTOR (600) #define ADDR_CHECK_FREQUENCY (120*HZ) diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 11cf373970a..51a7031b4aa 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -41,6 +41,7 @@ struct inet6_ifaddr { struct in6_addr addr; __u32 prefix_len; + /* In seconds, relative to tstamp. Expiry is at tstamp + HZ * lft. */ __u32 valid_lft; __u32 prefered_lft; atomic_t refcnt; diff --git a/ipc/sem.c b/ipc/sem.c index 34193ed69fb..e68a8f57682 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -1456,15 +1456,24 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, } sma = sem_lock(ns, semid); + + /* + * Wait until it's guaranteed that no wakeup_sem_queue_do() is ongoing. + */ + error = get_queue_result(&queue); + + /* + * Array removed? If yes, leave without sem_unlock(). + */ if (IS_ERR(sma)) { error = -EIDRM; goto out_free; } - error = get_queue_result(&queue); /* - * If queue.status != -EINTR we are woken up by another process + * If queue.status != -EINTR we are woken up by another process. + * Leave without unlink_queue(), but with sem_unlock(). */ if (error != -EINTR) { diff --git a/kernel/events/core.c b/kernel/events/core.c index 9efe7108cca..32a61513f89 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -5016,11 +5016,8 @@ static int __perf_event_overflow(struct perf_event *event, int nmi, if (events && atomic_dec_and_test(&event->event_limit)) { ret = 1; event->pending_kill = POLL_HUP; - if (nmi) { - event->pending_disable = 1; - irq_work_queue(&event->pending); - } else - perf_event_disable(event); + event->pending_disable = 1; + irq_work_queue(&event->pending); } if (event->overflow_handler) diff --git a/kernel/futex.c b/kernel/futex.c index 3fbc76cbb9a..0a308970c24 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -355,8 +355,8 @@ static int fault_in_user_writeable(u32 __user *uaddr) int ret; down_read(&mm->mmap_sem); - ret = get_user_pages(current, mm, (unsigned long)uaddr, - 1, 1, 0, NULL, NULL); + ret = fixup_user_fault(current, mm, (unsigned long)uaddr, + FAULT_FLAG_WRITE); up_read(&mm->mmap_sem); return ret < 0 ? ret : 0; diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index 85c13082b0c..f323a4cd58e 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -72,7 +72,9 @@ int check_wakeup_irqs(void) if (irqd_is_wakeup_set(&desc->irq_data)) { if (desc->istate & IRQS_PENDING) { pr_info("Wakeup IRQ %d %s pending, suspend aborted\n", - irq, desc->name ? desc->name : ""); + irq, + desc->action && desc->action->name ? + desc->action->name : ""); return -EBUSY; } continue; diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c index c10d0ee7907..2ee459fe445 100644 --- a/kernel/power/wakelock.c +++ b/kernel/power/wakelock.c @@ -249,7 +249,7 @@ long has_wake_lock(int type) unsigned long irqflags; spin_lock_irqsave(&list_lock, irqflags); ret = has_wake_lock_locked(type); - if (ret && (debug_mask & DEBUG_SUSPEND) && type == WAKE_LOCK_SUSPEND) + if (ret && (debug_mask & DEBUG_WAKEUP) && type == WAKE_LOCK_SUSPEND) print_active_locks(type); spin_unlock_irqrestore(&list_lock, irqflags); return ret; @@ -311,7 +311,7 @@ static int power_suspend_late(struct device *dev) { int ret = has_wake_lock(WAKE_LOCK_SUSPEND) ? -EAGAIN : 0; #ifdef CONFIG_WAKELOCK_STAT - wait_for_wakeup = 1; + wait_for_wakeup = !ret; #endif if (debug_mask & DEBUG_SUSPEND) pr_info("power_suspend_late return %d\n", ret); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 229f8591f61..f8074072d11 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -677,6 +677,7 @@ struct event_subsystem { struct dentry *entry; struct event_filter *filter; int nr_events; + int ref_count; }; #define FILTER_PRED_INVALID ((unsigned short)-1) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 686ec399f2a..3e2a7c91c54 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -244,6 +244,35 @@ static void ftrace_clear_events(void) mutex_unlock(&event_mutex); } +static void __put_system(struct event_subsystem *system) +{ + struct event_filter *filter = system->filter; + + WARN_ON_ONCE(system->ref_count == 0); + if (--system->ref_count) + return; + + if (filter) { + kfree(filter->filter_string); + kfree(filter); + } + kfree(system->name); + kfree(system); +} + +static void __get_system(struct event_subsystem *system) +{ + WARN_ON_ONCE(system->ref_count == 0); + system->ref_count++; +} + +static void put_system(struct event_subsystem *system) +{ + mutex_lock(&event_mutex); + __put_system(system); + mutex_unlock(&event_mutex); +} + /* * __ftrace_set_clr_event(NULL, NULL, NULL, set) will set/unset all events. */ @@ -528,7 +557,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { const char set_to_char[4] = { '?', '0', '1', 'X' }; - const char *system = filp->private_data; + struct event_subsystem *system = filp->private_data; struct ftrace_event_call *call; char buf[2]; int set = 0; @@ -539,7 +568,7 @@ system_enable_read(struct file *filp, char __user *ubuf, size_t cnt, if (!call->name || !call->class || !call->class->reg) continue; - if (system && strcmp(call->class->system, system) != 0) + if (system && strcmp(call->class->system, system->name) != 0) continue; /* @@ -569,7 +598,8 @@ static ssize_t system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - const char *system = filp->private_data; + struct event_subsystem *system = filp->private_data; + const char *name = NULL; unsigned long val; char buf[64]; ssize_t ret; @@ -593,7 +623,14 @@ system_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, if (val != 0 && val != 1) return -EINVAL; - ret = __ftrace_set_clr_event(NULL, system, NULL, val); + /* + * Opening of "enable" adds a ref count to system, + * so the name is safe to use. + */ + if (system) + name = system->name; + + ret = __ftrace_set_clr_event(NULL, name, NULL, val); if (ret) goto out; @@ -826,6 +863,52 @@ event_filter_write(struct file *filp, const char __user *ubuf, size_t cnt, return cnt; } +static LIST_HEAD(event_subsystems); + +static int subsystem_open(struct inode *inode, struct file *filp) +{ + struct event_subsystem *system = NULL; + int ret; + + if (!inode->i_private) + goto skip_search; + + /* Make sure the system still exists */ + mutex_lock(&event_mutex); + list_for_each_entry(system, &event_subsystems, list) { + if (system == inode->i_private) { + /* Don't open systems with no events */ + if (!system->nr_events) { + system = NULL; + break; + } + __get_system(system); + break; + } + } + mutex_unlock(&event_mutex); + + if (system != inode->i_private) + return -ENODEV; + + skip_search: + ret = tracing_open_generic(inode, filp); + if (ret < 0 && system) + put_system(system); + + return ret; +} + +static int subsystem_release(struct inode *inode, struct file *file) +{ + struct event_subsystem *system = inode->i_private; + + if (system) + put_system(system); + + return 0; +} + static ssize_t subsystem_filter_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) @@ -963,17 +1046,19 @@ static const struct file_operations ftrace_event_filter_fops = { }; static const struct file_operations ftrace_subsystem_filter_fops = { - .open = tracing_open_generic, + .open = subsystem_open, .read = subsystem_filter_read, .write = subsystem_filter_write, .llseek = default_llseek, + .release = subsystem_release, }; static const struct file_operations ftrace_system_enable_fops = { - .open = tracing_open_generic, + .open = subsystem_open, .read = system_enable_read, .write = system_enable_write, .llseek = default_llseek, + .release = subsystem_release, }; static const struct file_operations ftrace_show_header_fops = { @@ -1002,8 +1087,6 @@ static struct dentry *event_trace_events_dir(void) return d_events; } -static LIST_HEAD(event_subsystems); - static struct dentry * event_subsystem_dir(const char *name, struct dentry *d_events) { @@ -1013,6 +1096,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) /* First see if we did not already create this dir */ list_for_each_entry(system, &event_subsystems, list) { if (strcmp(system->name, name) == 0) { + __get_system(system); system->nr_events++; return system->entry; } @@ -1035,6 +1119,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) } system->nr_events = 1; + system->ref_count = 1; system->name = kstrdup(name, GFP_KERNEL); if (!system->name) { debugfs_remove(system->entry); @@ -1062,8 +1147,7 @@ event_subsystem_dir(const char *name, struct dentry *d_events) "'%s/filter' entry\n", name); } - trace_create_file("enable", 0644, system->entry, - (void *)system->name, + trace_create_file("enable", 0644, system->entry, system, &ftrace_system_enable_fops); return system->entry; @@ -1184,16 +1268,9 @@ static void remove_subsystem_dir(const char *name) list_for_each_entry(system, &event_subsystems, list) { if (strcmp(system->name, name) == 0) { if (!--system->nr_events) { - struct event_filter *filter = system->filter; - debugfs_remove_recursive(system->entry); list_del(&system->list); - if (filter) { - kfree(filter->filter_string); - kfree(filter); - } - kfree(system->name); - kfree(system); + __put_system(system); } break; } diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 8008ddcfbf2..256764ecccd 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1886,6 +1886,12 @@ int apply_subsystem_event_filter(struct event_subsystem *system, mutex_lock(&event_mutex); + /* Make sure the system still has events */ + if (!system->nr_events) { + err = -ENODEV; + goto out_unlock; + } + if (!strcmp(strstrip(filter_string), "0")) { filter_free_subsystem_preds(system); remove_filter_string(system->filter); diff --git a/lib/xz/xz_private.h b/lib/xz/xz_private.h index a65633e0696..482b90f363f 100644 --- a/lib/xz/xz_private.h +++ b/lib/xz/xz_private.h @@ -12,7 +12,7 @@ #ifdef __KERNEL__ # include <linux/xz.h> -# include <asm/byteorder.h> +# include <linux/kernel.h> # include <asm/unaligned.h> /* XZ_PREBOOT may be defined only via decompress_unxz.c. */ # ifndef XZ_PREBOOT diff --git a/mm/backing-dev.c b/mm/backing-dev.c index f032e6e1e09..e56fe35cef0 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -606,6 +606,7 @@ static void bdi_prune_sb(struct backing_dev_info *bdi) void bdi_unregister(struct backing_dev_info *bdi) { if (bdi->dev) { + bdi_set_min_ratio(bdi, 0); trace_writeback_bdi_unregister(bdi); bdi_prune_sb(bdi); del_timer_sync(&bdi->wb.wakeup_timer); diff --git a/mm/memcontrol.c b/mm/memcontrol.c index e013b8e57d2..59ac5d6de47 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -1730,7 +1730,7 @@ static int mem_cgroup_hierarchical_reclaim(struct mem_cgroup *root_mem, excess = res_counter_soft_limit_excess(&root_mem->res) >> PAGE_SHIFT; /* If memsw_is_minimum==1, swap-out is of-no-use. */ - if (!check_soft && root_mem->memsw_is_minimum) + if (!check_soft && !shrink && root_mem->memsw_is_minimum) noswap = true; while (1) { diff --git a/mm/memory.c b/mm/memory.c index 9b8a01d941c..d961e1914d1 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1816,7 +1816,63 @@ next_page: } EXPORT_SYMBOL(__get_user_pages); -/** +/* + * fixup_user_fault() - manually resolve a user page fault + * @tsk: the task_struct to use for page fault accounting, or + * NULL if faults are not to be recorded. + * @mm: mm_struct of target mm + * @address: user address + * @fault_flags:flags to pass down to handle_mm_fault() + * + * This is meant to be called in the specific scenario where for locking reasons + * we try to access user memory in atomic context (within a pagefault_disable() + * section), this returns -EFAULT, and we want to resolve the user fault before + * trying again. + * + * Typically this is meant to be used by the futex code. + * + * The main difference with get_user_pages() is that this function will + * unconditionally call handle_mm_fault() which will in turn perform all the + * necessary SW fixup of the dirty and young bits in the PTE, while + * handle_mm_fault() only guarantees to update these in the struct page. + * + * This is important for some architectures where those bits also gate the + * access permission to the page because they are maintained in software. On + * such architectures, gup() will not be enough to make a subsequent access + * succeed. + * + * This should be called with the mm_sem held for read. + */ +int fixup_user_fault(struct task_struct *tsk, struct mm_struct *mm, + unsigned long address, unsigned int fault_flags) +{ + struct vm_area_struct *vma; + int ret; + + vma = find_extend_vma(mm, address); + if (!vma || address < vma->vm_start) + return -EFAULT; + + ret = handle_mm_fault(mm, vma, address, fault_flags); + if (ret & VM_FAULT_ERROR) { + if (ret & VM_FAULT_OOM) + return -ENOMEM; + if (ret & (VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE)) + return -EHWPOISON; + if (ret & VM_FAULT_SIGBUS) + return -EFAULT; + BUG(); + } + if (tsk) { + if (ret & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; + } + return 0; +} + +/* * get_user_pages() - pin user pages in memory * @tsk: the task_struct to use for page fault accounting, or * NULL if faults are not to be recorded. diff --git a/mm/oom_kill.c b/mm/oom_kill.c index e4b0991ca35..8093fc766d1 100644 --- a/mm/oom_kill.c +++ b/mm/oom_kill.c @@ -303,7 +303,7 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, do_each_thread(g, p) { unsigned int points; - if (!p->mm) + if (p->exit_state) continue; if (oom_unkillable_task(p, mem, nodemask)) continue; @@ -319,6 +319,8 @@ static struct task_struct *select_bad_process(unsigned int *ppoints, */ if (test_tsk_thread_flag(p, TIF_MEMDIE)) return ERR_PTR(-1UL); + if (!p->mm) + continue; if (p->flags & PF_EXITING) { /* diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index e87d15da882..7c73a10d7ed 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -532,9 +532,8 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) BT_DBG("sk %p", sk); add_wait_queue(sk_sleep(sk), &wait); + set_current_state(TASK_INTERRUPTIBLE); while (sk->sk_state != state) { - set_current_state(TASK_INTERRUPTIBLE); - if (!timeo) { err = -EINPROGRESS; break; @@ -548,12 +547,13 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); + set_current_state(TASK_INTERRUPTIBLE); err = sock_error(sk); if (err) break; } - set_current_state(TASK_RUNNING); + __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); return err; } diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index ca39fcf010c..7e8ff3c2494 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -484,9 +484,11 @@ static int bnep_session(void *arg) init_waitqueue_entry(&wait, current); add_wait_queue(sk_sleep(sk), &wait); - while (!kthread_should_stop()) { + while (1) { set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) + break; /* RX */ while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); @@ -504,7 +506,7 @@ static int bnep_session(void *arg) schedule(); } - set_current_state(TASK_RUNNING); + __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); /* Cleanup session */ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ed602042a95..5a0ce738751 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1159,9 +1159,8 @@ int __l2cap_wait_ack(struct sock *sk) int timeo = HZ/5; add_wait_queue(sk_sleep(sk), &wait); - while ((chan->unacked_frames > 0 && chan->conn)) { - set_current_state(TASK_INTERRUPTIBLE); - + set_current_state(TASK_INTERRUPTIBLE); + while (chan->unacked_frames > 0 && chan->conn) { if (!timeo) timeo = HZ/5; @@ -1173,6 +1172,7 @@ int __l2cap_wait_ack(struct sock *sk) release_sock(sk); timeo = schedule_timeout(timeo); lock_sock(sk); + set_current_state(TASK_INTERRUPTIBLE); err = sock_error(sk); if (err) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 5c36b3e8739..61f1f623091 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -235,30 +235,26 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int fl lock_sock_nested(sk, SINGLE_DEPTH_NESTING); - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - goto done; - } - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); BT_DBG("sk %p timeo %ld", sk, timeo); /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (!(nsk = bt_accept_dequeue(sk, newsock))) { + while (1) { set_current_state(TASK_INTERRUPTIBLE); - if (!timeo) { - err = -EAGAIN; + + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; break; } - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock_nested(sk, SINGLE_DEPTH_NESTING); + nsk = bt_accept_dequeue(sk, newsock); + if (nsk) + break; - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; + if (!timeo) { + err = -EAGAIN; break; } @@ -266,8 +262,12 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int fl err = sock_intr_errno(timeo); break; } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock_nested(sk, SINGLE_DEPTH_NESTING); } - set_current_state(TASK_RUNNING); + __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); if (err) @@ -993,7 +993,7 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, int p INIT_LIST_HEAD(&bt_sk(sk)->accept_q); sk->sk_destruct = l2cap_sock_destruct; - sk->sk_sndtimeo = msecs_to_jiffies(L2CAP_CONN_TIMEOUT); + sk->sk_sndtimeo = L2CAP_CONN_TIMEOUT; sock_reset_flag(sk, SOCK_ZAPPED); diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 8c2e79672d9..c2486a53714 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -62,7 +62,6 @@ static DEFINE_MUTEX(rfcomm_mutex); #define rfcomm_lock() mutex_lock(&rfcomm_mutex) #define rfcomm_unlock() mutex_unlock(&rfcomm_mutex) -static unsigned long rfcomm_event; static LIST_HEAD(session_list); @@ -120,7 +119,6 @@ static inline void rfcomm_schedule(void) { if (!rfcomm_thread) return; - set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event); wake_up_process(rfcomm_thread); } @@ -2037,19 +2035,18 @@ static int rfcomm_run(void *unused) rfcomm_add_listener(BDADDR_ANY); - while (!kthread_should_stop()) { + while (1) { set_current_state(TASK_INTERRUPTIBLE); - if (!test_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) { - /* No pending events. Let's sleep. - * Incoming connections and data will wake us up. */ - schedule(); - } - set_current_state(TASK_RUNNING); + + if (kthread_should_stop()) + break; /* Process stuff */ - clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event); rfcomm_process_sessions(); + + schedule(); } + __set_current_state(TASK_RUNNING); rfcomm_kill_listener(); diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 7b3638b4043..b02f0d47ab8 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -485,11 +485,6 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f lock_sock(sk); - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - goto done; - } - if (sk->sk_type != SOCK_STREAM) { err = -EINVAL; goto done; @@ -501,19 +496,20 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (!(nsk = bt_accept_dequeue(sk, newsock))) { + while (1) { set_current_state(TASK_INTERRUPTIBLE); - if (!timeo) { - err = -EAGAIN; + + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; break; } - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); + nsk = bt_accept_dequeue(sk, newsock); + if (nsk) + break; - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; + if (!timeo) { + err = -EAGAIN; break; } @@ -521,8 +517,12 @@ static int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int f err = sock_intr_errno(timeo); break; } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); } - set_current_state(TASK_RUNNING); + __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); if (err) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index febc83a5c22..d3d48b5b542 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -579,30 +579,26 @@ static int sco_sock_accept(struct socket *sock, struct socket *newsock, int flag lock_sock(sk); - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; - goto done; - } - timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK); BT_DBG("sk %p timeo %ld", sk, timeo); /* Wait for an incoming connection. (wake-one). */ add_wait_queue_exclusive(sk_sleep(sk), &wait); - while (!(ch = bt_accept_dequeue(sk, newsock))) { + while (1) { set_current_state(TASK_INTERRUPTIBLE); - if (!timeo) { - err = -EAGAIN; + + if (sk->sk_state != BT_LISTEN) { + err = -EBADFD; break; } - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); + ch = bt_accept_dequeue(sk, newsock); + if (ch) + break; - if (sk->sk_state != BT_LISTEN) { - err = -EBADFD; + if (!timeo) { + err = -EAGAIN; break; } @@ -610,8 +606,12 @@ static int sco_sock_accept(struct socket *sock, struct socket *newsock, int flag err = sock_intr_errno(timeo); break; } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); } - set_current_state(TASK_RUNNING); + __set_current_state(TASK_RUNNING); remove_wait_queue(sk_sleep(sk), &wait); if (err) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 54578f274d8..78cc364997d 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -124,6 +124,7 @@ struct net_bridge_port bridge_id designated_bridge; u32 path_cost; u32 designated_cost; + unsigned long designated_age; struct timer_list forward_delay_timer; struct timer_list hold_timer; diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c index bb4383e84de..fcff6225154 100644 --- a/net/bridge/br_stp.c +++ b/net/bridge/br_stp.c @@ -164,8 +164,7 @@ void br_transmit_config(struct net_bridge_port *p) else { struct net_bridge_port *root = br_get_port(br, br->root_port); - bpdu.message_age = br->max_age - - (root->message_age_timer.expires - jiffies) + bpdu.message_age = (jiffies - root->designated_age) + MESSAGE_AGE_INCR; } bpdu.max_age = br->max_age; @@ -189,6 +188,7 @@ static inline void br_record_config_information(struct net_bridge_port *p, p->designated_cost = bpdu->root_path_cost; p->designated_bridge = bpdu->bridge_id; p->designated_port = bpdu->port_id; + p->designated_age = jiffies + bpdu->message_age; mod_timer(&p->message_age_timer, jiffies + (p->br->max_age - bpdu->message_age)); diff --git a/net/core/ethtool.c b/net/core/ethtool.c index fd14116ad7f..4fb77049e83 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -1227,7 +1227,7 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) regs.len = reglen; regbuf = vzalloc(reglen); - if (!regbuf) + if (reglen && !regbuf) return -ENOMEM; ops->get_regs(dev, ®s, regbuf); @@ -1236,7 +1236,7 @@ static int ethtool_get_regs(struct net_device *dev, char __user *useraddr) if (copy_to_user(useraddr, ®s, sizeof(regs))) goto out; useraddr += offsetof(struct ethtool_regs, data); - if (copy_to_user(useraddr, regbuf, regs.len)) + if (regbuf && copy_to_user(useraddr, regbuf, regs.len)) goto out; ret = 0; diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 498b927f68b..cf2cf62f33f 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -824,12 +824,13 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i { struct inet6_dev *idev = ifp->idev; struct in6_addr addr, *tmpaddr; - unsigned long tmp_prefered_lft, tmp_valid_lft, tmp_cstamp, tmp_tstamp, age; + unsigned long tmp_prefered_lft, tmp_valid_lft, tmp_tstamp, age; unsigned long regen_advance; int tmp_plen; int ret = 0; int max_addresses; u32 addr_flags; + unsigned long now = jiffies; write_lock(&idev->lock); if (ift) { @@ -874,7 +875,7 @@ retry: goto out; } memcpy(&addr.s6_addr[8], idev->rndid, 8); - age = (jiffies - ifp->tstamp) / HZ; + age = (now - ifp->tstamp) / HZ; tmp_valid_lft = min_t(__u32, ifp->valid_lft, idev->cnf.temp_valid_lft + age); @@ -884,7 +885,6 @@ retry: idev->cnf.max_desync_factor); tmp_plen = ifp->prefix_len; max_addresses = idev->cnf.max_addresses; - tmp_cstamp = ifp->cstamp; tmp_tstamp = ifp->tstamp; spin_unlock_bh(&ifp->lock); @@ -929,7 +929,7 @@ retry: ift->ifpub = ifp; ift->valid_lft = tmp_valid_lft; ift->prefered_lft = tmp_prefered_lft; - ift->cstamp = tmp_cstamp; + ift->cstamp = now; ift->tstamp = tmp_tstamp; spin_unlock_bh(&ift->lock); @@ -1988,25 +1988,50 @@ ok: #ifdef CONFIG_IPV6_PRIVACY read_lock_bh(&in6_dev->lock); /* update all temporary addresses in the list */ - list_for_each_entry(ift, &in6_dev->tempaddr_list, tmp_list) { - /* - * When adjusting the lifetimes of an existing - * temporary address, only lower the lifetimes. - * Implementations must not increase the - * lifetimes of an existing temporary address - * when processing a Prefix Information Option. - */ + list_for_each_entry(ift, &in6_dev->tempaddr_list, + tmp_list) { + int age, max_valid, max_prefered; + if (ifp != ift->ifpub) continue; + /* + * RFC 4941 section 3.3: + * If a received option will extend the lifetime + * of a public address, the lifetimes of + * temporary addresses should be extended, + * subject to the overall constraint that no + * temporary addresses should ever remain + * "valid" or "preferred" for a time longer than + * (TEMP_VALID_LIFETIME) or + * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), + * respectively. + */ + age = (now - ift->cstamp) / HZ; + max_valid = in6_dev->cnf.temp_valid_lft - age; + if (max_valid < 0) + max_valid = 0; + + max_prefered = in6_dev->cnf.temp_prefered_lft - + in6_dev->cnf.max_desync_factor - + age; + if (max_prefered < 0) + max_prefered = 0; + + if (valid_lft > max_valid) + valid_lft = max_valid; + + if (prefered_lft > max_prefered) + prefered_lft = max_prefered; + spin_lock(&ift->lock); flags = ift->flags; - if (ift->valid_lft > valid_lft && - ift->valid_lft - valid_lft > (jiffies - ift->tstamp) / HZ) - ift->valid_lft = valid_lft + (jiffies - ift->tstamp) / HZ; - if (ift->prefered_lft > prefered_lft && - ift->prefered_lft - prefered_lft > (jiffies - ift->tstamp) / HZ) - ift->prefered_lft = prefered_lft + (jiffies - ift->tstamp) / HZ; + ift->valid_lft = valid_lft; + ift->prefered_lft = prefered_lft; + ift->tstamp = now; + if (prefered_lft > 0) + ift->flags &= ~IFA_F_DEPRECATED; + spin_unlock(&ift->lock); if (!(flags&IFA_F_TENTATIVE)) ipv6_ifa_notify(0, ift); @@ -2014,9 +2039,11 @@ ok: if ((create || list_empty(&in6_dev->tempaddr_list)) && in6_dev->cnf.use_tempaddr > 0) { /* - * When a new public address is created as described in [ADDRCONF], - * also create a new temporary address. Also create a temporary - * address if it's enabled but no temporary address currently exists. + * When a new public address is created as + * described in [ADDRCONF], also create a new + * temporary address. Also create a temporary + * address if it's enabled but no temporary + * address currently exists. */ read_unlock_bh(&in6_dev->lock); ipv6_create_tempaddr(ifp, NULL); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d595265d6c2..7a334fdd8d6 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2200,6 +2200,9 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + if (!ifmgd->associated) + return; + if (test_and_clear_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running)) add_timer(&ifmgd->timer); if (test_and_clear_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running)) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 22552c9b81c..b04b4710574 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -8,8 +8,36 @@ * published by the Free Software Foundation. */ -/* TODO: support ipv6 for iface_stat. - * Currently if an iface is only v6 it will not have stats collected. */ +/* #define DEBUG */ +/* #define IDEBUG */ +/* #define MDEBUG */ +/* #define RDEBUG */ +/* #define CDEBUG */ + +/* Iface handling */ +#ifdef IDEBUG +#define IF_DEBUG(...) pr_debug(__VA_ARGS__) +#else +#define IF_DEBUG(...) no_printk(__VA_ARGS__) +#endif +/* Iptable Matching */ +#ifdef MDEBUG +#define MT_DEBUG(...) pr_debug(__VA_ARGS__) +#else +#define MT_DEBUG(...) no_printk(__VA_ARGS__) +#endif +/* Red-black tree handling */ +#ifdef RDEBUG +#define RB_DEBUG(...) pr_debug(__VA_ARGS__) +#else +#define RB_DEBUG(...) no_printk(__VA_ARGS__) +#endif +/* procfs ctrl/stats handling */ +#ifdef CDEBUG +#define CT_DEBUG(...) pr_debug(__VA_ARGS__) +#else +#define CT_DEBUG(...) no_printk(__VA_ARGS__) +#endif #include <linux/file.h> #include <linux/inetdevice.h> @@ -18,13 +46,16 @@ #include <linux/netfilter/xt_qtaguid.h> #include <linux/skbuff.h> #include <linux/workqueue.h> +#include <net/addrconf.h> #include <net/sock.h> #include <net/tcp.h> #include <net/udp.h> #include <linux/netfilter/xt_socket.h> -/* We only use the xt_socket funcs within a similar context to avoid unexpected - * return values. */ +/* + * We only use the xt_socket funcs within a similar context to avoid unexpected + * return values. + */ #define XT_SOCKET_SUPPORTED_HOOKS \ ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_IN)) @@ -61,7 +92,8 @@ module_param_named(stats_readall_gid, proc_stats_readall_gid, uint, module_param_named(ctrl_write_gid, proc_ctrl_write_gid, uint, S_IRUGO | S_IWUSR); -/* After the kernel has initiallized this module, it is still possible +/* + * After the kernel has initiallized this module, it is still possible * to make it passive: * - do not register it via iptables. * the matching code will not be invoked. @@ -106,6 +138,14 @@ typedef uint64_t tag_t; /* Only used via accessors */ static const char *iface_stat_procdirname = "iface_stat"; static struct proc_dir_entry *iface_stat_procdir; + +/* + * For now we only track 2 sets of counters. + * The default set is 0. + * Userspace can activate another set for a given uid being tracked. + */ +#define IFS_MAX_COUNTER_SETS 2 + enum ifs_tx_rx { IFS_TX, IFS_RX, @@ -126,16 +166,22 @@ struct byte_packet_counters { }; struct data_counters { - struct byte_packet_counters bpc[IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; + struct byte_packet_counters bpc[IFS_MAX_COUNTER_SETS][IFS_MAX_DIRECTIONS][IFS_MAX_PROTOS]; }; -struct tag_stat { +/* Generic tag based node used as a base for rb_tree ops. */ +struct tag_node { struct rb_node node; tag_t tag; +}; +struct tag_stat { + struct tag_node tn; struct data_counters counters; - /* If this tag is acct_tag based, we need to count against the - * matching parent uid_tag. */ + /* + * If this tag is acct_tag based, we need to count against the + * matching parent uid_tag. + */ struct data_counters *parent_counters; struct proc_dir_entry *proc_ptr; }; @@ -157,8 +203,14 @@ struct iface_stat { static LIST_HEAD(iface_stat_list); static DEFINE_SPINLOCK(iface_stat_list_lock); +/* This is needed to create proc_dir_entries from atomic context. */ +struct iface_stat_work { + struct work_struct iface_work; + struct iface_stat *iface_entry; +}; + /* - * Track tag that this socket is transferring data for, and not necesseraly + * Track tag that this socket is transferring data for, and not necessarily * the uid that owns the socket. * This is the tag against which tag_stat.counters will be billed. */ @@ -171,6 +223,15 @@ struct sock_tag { static struct rb_root sock_tag_tree = RB_ROOT; static DEFINE_SPINLOCK(sock_tag_list_lock); +/* Track the set active_set for the given tag. */ +struct tag_counter_set { + struct tag_node tn; + int active_set; +}; + +static struct rb_root tag_counter_set_tree = RB_ROOT; +static DEFINE_SPINLOCK(tag_counter_set_list_lock); + static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par); /*----------------------------------------------*/ @@ -179,7 +240,6 @@ static inline int tag_compare(tag_t t1, tag_t t2) return t1 < t2 ? -1 : t1 == t2 ? 0 : 1; } - static inline tag_t combine_atag_with_uid(tag_t acct_tag, uid_t uid) { return acct_tag | uid; @@ -206,40 +266,42 @@ static inline bool valid_atag(tag_t tag) return !(tag & 0xFFFFFFFFULL); } -static inline void dc_add_byte_packets(struct data_counters *counters, +static inline void dc_add_byte_packets(struct data_counters *counters, int set, enum ifs_tx_rx direction, enum ifs_proto ifs_proto, int bytes, int packets) { - counters->bpc[direction][ifs_proto].bytes += bytes; - counters->bpc[direction][ifs_proto].packets += packets; + counters->bpc[set][direction][ifs_proto].bytes += bytes; + counters->bpc[set][direction][ifs_proto].packets += packets; } static inline uint64_t dc_sum_bytes(struct data_counters *counters, + int set, enum ifs_tx_rx direction) { - return counters->bpc[direction][IFS_TCP].bytes - + counters->bpc[direction][IFS_UDP].bytes - + counters->bpc[direction][IFS_PROTO_OTHER].bytes; + return counters->bpc[set][direction][IFS_TCP].bytes + + counters->bpc[set][direction][IFS_UDP].bytes + + counters->bpc[set][direction][IFS_PROTO_OTHER].bytes; } static inline uint64_t dc_sum_packets(struct data_counters *counters, + int set, enum ifs_tx_rx direction) { - return counters->bpc[direction][IFS_TCP].packets - + counters->bpc[direction][IFS_UDP].packets - + counters->bpc[direction][IFS_PROTO_OTHER].packets; + return counters->bpc[set][direction][IFS_TCP].packets + + counters->bpc[set][direction][IFS_UDP].packets + + counters->bpc[set][direction][IFS_PROTO_OTHER].packets; } -static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) +static struct tag_node *tag_node_tree_search(struct rb_root *root, tag_t tag) { struct rb_node *node = root->rb_node; while (node) { - struct tag_stat *data = rb_entry(node, struct tag_stat, node); + struct tag_node *data = rb_entry(node, struct tag_node, node); int result = tag_compare(tag, data->tag); - pr_debug("qtaguid: tag_stat_tree_search(): tag=0x%llx" + RB_DEBUG("qtaguid: tag_node_tree_search(): tag=0x%llx" " (uid=%d)\n", data->tag, get_uid_from_tag(data->tag)); @@ -254,16 +316,16 @@ static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) return NULL; } -static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) +static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) { struct rb_node **new = &(root->rb_node), *parent = NULL; /* Figure out where to put new node */ while (*new) { - struct tag_stat *this = rb_entry(*new, struct tag_stat, + struct tag_node *this = rb_entry(*new, struct tag_node, node); int result = tag_compare(data->tag, this->tag); - pr_debug("qtaguid: tag_stat_tree_insert(): tag=0x%llx" + RB_DEBUG("qtaguid: tag_node_tree_insert(): tag=0x%llx" " (uid=%d)\n", this->tag, get_uid_from_tag(this->tag)); @@ -281,6 +343,28 @@ static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) rb_insert_color(&data->node, root); } +static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) +{ + tag_node_tree_insert((struct tag_node *)data, root); +} + +static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) +{ + return (struct tag_stat *)tag_node_tree_search(root, tag); +} + +static void tag_counter_set_tree_insert(struct tag_counter_set *data, + struct rb_root *root) +{ + tag_node_tree_insert((struct tag_node *)data, root); +} + +static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, + tag_t tag) +{ + return (struct tag_counter_set *)tag_node_tree_search(root, tag); +} + static struct sock_tag *sock_tag_tree_search(struct rb_root *root, const struct sock *sk) { @@ -357,189 +441,328 @@ static int read_proc_bool(char *page, char **start, off_t off, return len; } -/* Find the entry for tracking the specified interface. */ -static struct iface_stat *get_iface_stat(const char *ifname) +static int get_active_counter_set(tag_t tag) +{ + int active_set = 0; + struct tag_counter_set *tcs; + + MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)" + " (uid=%d)\n", + tag, get_uid_from_tag(tag)); + /* For now we only handle UID tags for active sets */ + tag = get_utag_from_tag(tag); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs) + active_set = tcs->active_set; + spin_unlock_bh(&tag_counter_set_list_lock); + return active_set; +} + +/* + * Find the entry for tracking the specified interface. + * Caller must hold iface_stat_list_lock + */ +static struct iface_stat *get_iface_entry(const char *ifname) { - unsigned long flags; struct iface_stat *iface_entry; - if (!ifname) + + /* Find the entry for tracking the specified tag within the interface */ + if (ifname == NULL) { + pr_info("iface_stat: NULL device name\n"); return NULL; + } + - spin_lock_irqsave(&iface_stat_list_lock, flags); + /* Iterate over interfaces */ list_for_each_entry(iface_entry, &iface_stat_list, list) { - if (!strcmp(iface_entry->ifname, ifname)) + if (!strcmp(ifname, iface_entry->ifname)) goto done; } iface_entry = NULL; done: - spin_unlock_irqrestore(&iface_stat_list_lock, flags); return iface_entry; } +static void iface_create_proc_worker(struct work_struct *work) +{ + struct proc_dir_entry *proc_entry; + struct iface_stat_work *isw = container_of(work, struct iface_stat_work, + iface_work); + struct iface_stat *new_iface = isw->iface_entry; + + /* iface_entries are not deleted, so safe to manipulate. */ + proc_entry = proc_mkdir(new_iface->ifname, iface_stat_procdir); + if (IS_ERR_OR_NULL(proc_entry)) { + pr_err("qtaguid: iface_stat: create_proc(): alloc failed.\n"); + kfree(isw); + return; + } + + new_iface->proc_ptr = proc_entry; + + create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->tx_bytes); + create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->rx_bytes); + create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->tx_packets); + create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, + read_proc_u64, &new_iface->rx_packets); + create_proc_read_entry("active", proc_iface_perms, proc_entry, + read_proc_bool, &new_iface->active); + + IF_DEBUG("qtaguid: iface_stat: create_proc(): done " + "entry=%p dev=%s\n", new_iface, new_iface->ifname); + kfree(isw); +} + +/* Caller must hold iface_stat_list_lock */ +static struct iface_stat *iface_alloc(const char *ifname) +{ + struct iface_stat *new_iface; + struct iface_stat_work *isw; + + new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); + if (new_iface == NULL) { + pr_err("qtaguid: iface_stat: create(): failed to alloc iface_stat\n"); + return NULL; + } + new_iface->ifname = kstrdup(ifname, GFP_ATOMIC); + if (new_iface->ifname == NULL) { + pr_err("qtaguid: iface_stat: create(): failed to alloc ifname\n"); + kfree(new_iface); + return NULL; + } + spin_lock_init(&new_iface->tag_stat_list_lock); + new_iface->active = true; + new_iface->tag_stat_tree = RB_ROOT; + + /* + * ipv6 notifier chains are atomic :( + * No create_proc_read_entry() for you! + */ + isw = kmalloc(sizeof(*isw), GFP_ATOMIC); + if (!isw) { + pr_err("qtaguid: iface_stat: create(): " + "failed to alloc work for %s\n", new_iface->ifname); + kfree(new_iface->ifname); + kfree(new_iface); + return NULL; + } + isw->iface_entry = new_iface; + INIT_WORK(&isw->iface_work, iface_create_proc_worker); + schedule_work(&isw->iface_work); + list_add(&new_iface->list, &iface_stat_list); + return new_iface; +} + /* * Create a new entry for tracking the specified interface. * Do nothing if the entry already exists. * Called when an interface is configured with a valid IP address. */ -void iface_stat_create(const struct net_device *net_dev) +void iface_stat_create(const struct net_device *net_dev, + struct in_ifaddr *ifa) { - struct in_device *in_dev; - unsigned long flags; - struct iface_stat *new_iface; - struct proc_dir_entry *proc_entry; + struct in_device *in_dev = NULL; const char *ifname; struct iface_stat *entry; __be32 ipaddr = 0; - struct in_ifaddr *ifa = NULL; - - ASSERT_RTNL(); /* No need for separate locking */ + struct iface_stat *new_iface; - pr_debug("iface_stat: create(): netdev=%p->name=%s\n", - net_dev, net_dev ? net_dev->name : ""); + IF_DEBUG("qtaguid: iface_stat: create(): ifa=%p netdev=%p->name=%s\n", + ifa, net_dev, net_dev ? net_dev->name : ""); if (!net_dev) { - pr_err("iface_stat: create(): no net dev!\n"); + pr_err("qtaguid: iface_stat: create(): no net dev!\n"); return; } - in_dev = __in_dev_get_rtnl(net_dev); - if (!in_dev) { - pr_err("iface_stat: create(): no inet dev!\n"); - return; + ifname = net_dev->name; + if (!ifa) { + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create(): " + "no inet dev for %s!\n", ifname); + return; + } + IF_DEBUG("qtaguid: iface_stat: create(): in_dev=%p ifname=%p\n", + in_dev, ifname); + for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { + IF_DEBUG("qtaguid: iface_stat: create(): " + "ifa=%p ifname=%s ifa_label=%s\n", + ifa, ifname, + ifa->ifa_label ? ifa->ifa_label : "(null)"); + if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) + break; + } } - pr_debug("iface_stat: create(): in_dev=%p\n", in_dev); - ifname = net_dev->name; - pr_debug("iface_stat: create(): ifname=%p\n", ifname); - for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { - pr_debug("iface_stat: create(): for(): ifa=%p ifname=%p\n", - ifa, ifname); - pr_debug("iface_stat: create(): ifname=%s ifa_label=%s\n", - ifname, ifa->ifa_label ? ifa->ifa_label : "(null)"); - if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) - break; - } - - if (ifa) { - ipaddr = ifa->ifa_local; - } else { - pr_err("iface_stat: create(): dev %s has no matching IP\n", - ifname); - return; + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create(): " + "dev %s has no matching IP\n", + ifname); + goto done_put; } + ipaddr = ifa->ifa_local; - entry = get_iface_stat(net_dev->name); + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); if (entry != NULL) { - pr_debug("iface_stat: create(): dev %s entry=%p\n", ifname, - entry); + IF_DEBUG("qtaguid: iface_stat: create(): dev %s entry=%p\n", + ifname, entry); if (ipv4_is_loopback(ipaddr)) { entry->active = false; - pr_debug("iface_stat: create(): disable tracking of " - "loopback dev %s\n", ifname); + IF_DEBUG("qtaguid: iface_stat: create(): " + "disable tracking of loopback dev %s\n", + ifname); } else { entry->active = true; - pr_debug("iface_stat: create(): enable tracking of " - "dev %s with ip=%pI4\n", + IF_DEBUG("qtaguid: iface_stat: create(): " + "enable tracking of dev %s with ip=%pI4\n", ifname, &ipaddr); } - return; + goto done_unlock_put; } else if (ipv4_is_loopback(ipaddr)) { - pr_debug("iface_stat: create(): ignore loopback dev %s" + IF_DEBUG("qtaguid: iface_stat: create(): ignore loopback dev %s" " ip=%pI4\n", ifname, &ipaddr); - return; + goto done_unlock_put; } - new_iface = kzalloc(sizeof(*new_iface), GFP_KERNEL); - if (new_iface == NULL) { - pr_err("iface_stat: create(): failed to alloc iface_stat\n"); + new_iface = iface_alloc(ifname); + IF_DEBUG("qtaguid: iface_stat: create(): done " + "entry=%p dev=%s ip=%pI4\n", new_iface, ifname, &ipaddr); + +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + if (in_dev) + in_dev_put(in_dev); +} + +void iface_stat_create_ipv6(const struct net_device *net_dev, + struct inet6_ifaddr *ifa) +{ + struct in_device *in_dev; + const char *ifname; + struct iface_stat *entry; + struct iface_stat *new_iface; + int addr_type; + + IF_DEBUG("qtaguid: iface_stat: create6(): ifa=%p netdev=%p->name=%s\n", + ifa, net_dev, net_dev ? net_dev->name : ""); + if (!net_dev) { + pr_err("qtaguid: iface_stat: create6(): no net dev!\n"); return; } - new_iface->ifname = kstrdup(ifname, GFP_KERNEL); - if (new_iface->ifname == NULL) { - pr_err("iface_stat: create(): failed to alloc ifname\n"); - kfree(new_iface); + ifname = net_dev->name; + + in_dev = in_dev_get(net_dev); + if (!in_dev) { + pr_err("qtaguid: iface_stat: create6(): no inet dev for %s!\n", + ifname); return; } - spin_lock_init(&new_iface->tag_stat_list_lock); - new_iface->active = true; + IF_DEBUG("qtaguid: iface_stat: create6(): in_dev=%p ifname=%p\n", + in_dev, ifname); - new_iface->tag_stat_tree = RB_ROOT; - spin_lock_irqsave(&iface_stat_list_lock, flags); - list_add(&new_iface->list, &iface_stat_list); - spin_unlock_irqrestore(&iface_stat_list_lock, flags); + if (!ifa) { + IF_DEBUG("qtaguid: iface_stat: create6(): " + "dev %s has no matching IP\n", + ifname); + goto done_put; + } + addr_type = ipv6_addr_type(&ifa->addr); - proc_entry = proc_mkdir(ifname, iface_stat_procdir); - new_iface->proc_ptr = proc_entry; + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(ifname); + if (entry != NULL) { + IF_DEBUG("qtaguid: iface_stat: create6(): dev %s entry=%p\n", + ifname, entry); + if (addr_type & IPV6_ADDR_LOOPBACK) { + entry->active = false; + IF_DEBUG("qtaguid: iface_stat: create6(): " + "disable tracking of loopback dev %s\n", + ifname); + } else { + entry->active = true; + IF_DEBUG("qtaguid: iface_stat: create6(): " + "enable tracking of dev %s with ip=%pI6c\n", + ifname, &ifa->addr); + } + goto done_unlock_put; + } else if (addr_type & IPV6_ADDR_LOOPBACK) { + IF_DEBUG("qtaguid: iface_stat: create6(): " + "ignore loopback dev %s ip=%pI6c\n", + ifname, &ifa->addr); + goto done_unlock_put; + } - /* TODO: make root access only */ - create_proc_read_entry("tx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->tx_bytes); - create_proc_read_entry("rx_bytes", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->rx_bytes); - create_proc_read_entry("tx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->tx_packets); - create_proc_read_entry("rx_packets", proc_iface_perms, proc_entry, - read_proc_u64, &new_iface->rx_packets); - create_proc_read_entry("active", proc_iface_perms, proc_entry, - read_proc_bool, &new_iface->active); + new_iface = iface_alloc(ifname); + IF_DEBUG("qtaguid: iface_stat: create6(): done " + "entry=%p dev=%s ip=%pI6c\n", new_iface, ifname, &ifa->addr); - pr_debug("iface_stat: create(): done entry=%p dev=%s ip=%pI4\n", - new_iface, ifname, &ipaddr); +done_unlock_put: + spin_unlock_bh(&iface_stat_list_lock); +done_put: + in_dev_put(in_dev); } static struct sock_tag *get_sock_stat_nl(const struct sock *sk) { - pr_debug("xt_qtaguid: get_sock_stat_nl(sk=%p)\n", sk); + MT_DEBUG("qtaguid: get_sock_stat_nl(sk=%p)\n", sk); return sock_tag_tree_search(&sock_tag_tree, sk); } static struct sock_tag *get_sock_stat(const struct sock *sk) { - unsigned long flags; struct sock_tag *sock_tag_entry; - pr_debug("xt_qtaguid: get_sock_stat(sk=%p)\n", sk); + MT_DEBUG("qtaguid: get_sock_stat(sk=%p)\n", sk); if (!sk) return NULL; - spin_lock_irqsave(&sock_tag_list_lock, flags); + spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(sk); - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); return sock_tag_entry; } static void -data_counters_update(struct data_counters *dc, enum ifs_tx_rx direction, - int proto, int bytes) +data_counters_update(struct data_counters *dc, int set, + enum ifs_tx_rx direction, int proto, int bytes) { switch (proto) { case IPPROTO_TCP: - dc_add_byte_packets(dc, direction, IFS_TCP, bytes, 1); + dc_add_byte_packets(dc, set, direction, IFS_TCP, bytes, 1); break; case IPPROTO_UDP: - dc_add_byte_packets(dc, direction, IFS_UDP, bytes, 1); + dc_add_byte_packets(dc, set, direction, IFS_UDP, bytes, 1); break; case IPPROTO_IP: default: - dc_add_byte_packets(dc, direction, IFS_PROTO_OTHER, bytes, 1); + dc_add_byte_packets(dc, set, direction, IFS_PROTO_OTHER, bytes, + 1); break; } } - /* * Update stats for the specified interface. Do nothing if the entry * does not exist (when a device was never configured with an IP address). * Called when an device is being unregistered. */ -void iface_stat_update(struct net_device *dev) +static void iface_stat_update(struct net_device *dev) { struct rtnl_link_stats64 dev_stats, *stats; struct iface_stat *entry; - stats = dev_get_stats(dev, &dev_stats); - ASSERT_RTNL(); - entry = get_iface_stat(dev->name); + stats = dev_get_stats(dev, &dev_stats); + spin_lock_bh(&iface_stat_list_lock); + entry = get_iface_entry(dev->name); if (entry == NULL) { - pr_debug("iface_stat: dev %s monitor not found\n", dev->name); + IF_DEBUG("qtaguid: iface_stat: dev %s monitor not found\n", + dev->name); + spin_unlock_bh(&iface_stat_list_lock); return; } if (entry->active) { @@ -548,74 +771,54 @@ void iface_stat_update(struct net_device *dev) entry->rx_bytes += stats->rx_bytes; entry->rx_packets += stats->rx_packets; entry->active = false; - pr_debug("iface_stat: Updating stats for " + IF_DEBUG("qtaguid: iface_stat: Updating stats for " "dev %s which went down\n", dev->name); } else { - pr_debug("iface_stat: Did not update stats for " + IF_DEBUG("qtaguid: iface_stat: Did not update stats for " "dev %s which went down\n", dev->name); } + spin_unlock_bh(&iface_stat_list_lock); } - static void tag_stat_update(struct tag_stat *tag_entry, enum ifs_tx_rx direction, int proto, int bytes) { - pr_debug("xt_qtaguid: tag_stat_update(tag=0x%llx (uid=%d) dir=%d " - "proto=%d bytes=%d)\n", - tag_entry->tag, get_uid_from_tag(tag_entry->tag), direction, - proto, bytes); - data_counters_update(&tag_entry->counters, direction, proto, bytes); + int active_set; + active_set = get_active_counter_set(tag_entry->tn.tag); + MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%d) set=%d " + "dir=%d proto=%d bytes=%d)\n", + tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag), + active_set, direction, proto, bytes); + data_counters_update(&tag_entry->counters, active_set, direction, + proto, bytes); if (tag_entry->parent_counters) - data_counters_update(tag_entry->parent_counters, direction, - proto, bytes); + data_counters_update(tag_entry->parent_counters, active_set, + direction, proto, bytes); } - -/* Create a new entry for tracking the specified {acct_tag,uid_tag} within +/* + * Create a new entry for tracking the specified {acct_tag,uid_tag} within * the interface. - * iface_entry->tag_stat_list_lock should be held. */ + * iface_entry->tag_stat_list_lock should be held. + */ static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, tag_t tag) { struct tag_stat *new_tag_stat_entry = NULL; - pr_debug("iface_stat: create_if_tag_stat(): ife=%p tag=0x%llx" + IF_DEBUG("qtaguid: iface_stat: create_if_tag_stat(): ife=%p tag=0x%llx" " (uid=%d)\n", iface_entry, tag, get_uid_from_tag(tag)); new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); if (!new_tag_stat_entry) { - pr_err("iface_stat: failed to alloc new tag entry\n"); + pr_err("qtaguid: iface_stat: failed to alloc new tag entry\n"); goto done; } - new_tag_stat_entry->tag = tag; + new_tag_stat_entry->tn.tag = tag; tag_stat_tree_insert(new_tag_stat_entry, &iface_entry->tag_stat_tree); done: return new_tag_stat_entry; } -static struct iface_stat *get_iface_entry(const char *ifname) -{ - struct iface_stat *iface_entry; - unsigned long flags; - - /* Find the entry for tracking the specified tag within the interface */ - if (ifname == NULL) { - pr_info("iface_stat: NULL device name\n"); - return NULL; - } - - - /* Iterate over interfaces */ - spin_lock_irqsave(&iface_stat_list_lock, flags); - list_for_each_entry(iface_entry, &iface_stat_list, list) { - if (!strcmp(ifname, iface_entry->ifname)) - goto done; - } - iface_entry = NULL; -done: - spin_unlock_irqrestore(&iface_stat_list_lock, flags); - return iface_entry; -} - static void if_tag_stat_update(const char *ifname, uid_t uid, const struct sock *sk, enum ifs_tx_rx direction, int proto, int bytes) @@ -626,25 +829,26 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, struct data_counters *uid_tag_counters; struct sock_tag *sock_tag_entry; struct iface_stat *iface_entry; - unsigned long flags; struct tag_stat *new_tag_stat; - pr_debug("xt_qtaguid: if_tag_stat_update(ifname=%s " + MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " "uid=%d sk=%p dir=%d proto=%d bytes=%d)\n", ifname, uid, sk, direction, proto, bytes); iface_entry = get_iface_entry(ifname); if (!iface_entry) { - pr_err("iface_stat: interface %s not found\n", ifname); + pr_err("qtaguid: iface_stat: interface %s not found\n", ifname); return; } - /* else { If the iface_entry becomes inactive, it is still ok - * to process the data. } */ + /* It is ok to process data when an iface_entry is inactive */ - pr_debug("iface_stat: stat_update() got entry=%p\n", iface_entry); + MT_DEBUG("qtaguid: iface_stat: stat_update() got entry=%p\n", + iface_entry); - /* Look for a tagged sock. - * It will have an acct_uid. */ + /* + * Look for a tagged sock. + * It will have an acct_uid. + */ sock_tag_entry = get_sock_stat(sk); if (sock_tag_entry) { tag = sock_tag_entry->tag; @@ -655,19 +859,21 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, acct_tag = 0; tag = combine_atag_with_uid(acct_tag, uid); } - pr_debug("iface_stat: stat_update(): looking for tag=0x%llx (uid=%d)" - " in ife=%p\n", + MT_DEBUG("qtaguid: iface_stat: stat_update(): " + " looking for tag=0x%llx (uid=%d) in ife=%p\n", tag, get_uid_from_tag(tag), iface_entry); /* Loop over tag list under this interface for {acct_tag,uid_tag} */ - spin_lock_irqsave(&iface_entry->tag_stat_list_lock, flags); + spin_lock_bh(&iface_entry->tag_stat_list_lock); tag_stat_entry = tag_stat_tree_search(&iface_entry->tag_stat_tree, tag); if (tag_stat_entry) { - /* Updating the {acct_tag, uid_tag} entry handles both stats: - * {0, uid_tag} will also get updated. */ + /* + * Updating the {acct_tag, uid_tag} entry handles both stats: + * {0, uid_tag} will also get updated. + */ tag_stat_update(tag_stat_entry, direction, proto, bytes); - spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, flags); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); return; } @@ -690,7 +896,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, new_tag_stat = create_if_tag_stat(iface_entry, tag); new_tag_stat->parent_counters = uid_tag_counters; } - spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, flags); + spin_unlock_bh(&iface_entry->tag_stat_list_lock); tag_stat_update(new_tag_stat, direction, proto, bytes); } @@ -701,7 +907,8 @@ static int iface_netdev_event_handler(struct notifier_block *nb, if (unlikely(module_passive)) return NOTIFY_DONE; - pr_debug("iface_stat: netdev_event(): ev=0x%lx netdev=%p->name=%s\n", + IF_DEBUG("qtaguid: iface_stat: netdev_event(): " + "ev=0x%lx netdev=%p->name=%s\n", event, dev, dev ? dev->name : ""); switch (event) { @@ -712,7 +919,7 @@ static int iface_netdev_event_handler(struct notifier_block *nb, case NETDEV_CHANGEADDR: /* MAC addr change */ case NETDEV_CHANGENAME: case NETDEV_FEAT_CHANGE: /* Might be usefull when cell type changes */ - iface_stat_create(dev); + iface_stat_create(dev, NULL); break; case NETDEV_UNREGISTER: iface_stat_update(dev); @@ -721,22 +928,47 @@ static int iface_netdev_event_handler(struct notifier_block *nb, return NOTIFY_DONE; } -static int iface_inetaddr_event_handler(struct notifier_block *nb, - unsigned long event, void *ptr) { +static int iface_inet6addr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct inet6_ifaddr *ifa = ptr; + struct net_device *dev; + + if (unlikely(module_passive)) + return NOTIFY_DONE; + IF_DEBUG("qtaguid: iface_stat: inet6addr_event(): " + "ev=0x%lx ifa=%p\n", + event, ifa); + + switch (event) { + case NETDEV_UP: + BUG_ON(!ifa || !ifa->idev); + dev = (struct net_device *)ifa->idev->dev; + iface_stat_create_ipv6(dev, ifa); + break; + } + return NOTIFY_DONE; +} + +static int iface_inetaddr_event_handler(struct notifier_block *nb, + unsigned long event, void *ptr) +{ struct in_ifaddr *ifa = ptr; - struct in_device *in_dev = ifa->ifa_dev; - struct net_device *dev = in_dev->dev; + struct net_device *dev; if (unlikely(module_passive)) return NOTIFY_DONE; - pr_debug("iface_stat: inetaddr_event(): ev=0x%lx netdev=%p->name=%s\n", - event, dev, dev ? dev->name : ""); + IF_DEBUG("qtaguid: iface_stat: inetaddr_event(): " + "ev=0x%lx ifa=%p\n", + event, ifa); switch (event) { case NETDEV_UP: - iface_stat_create(dev); + BUG_ON(!ifa || !ifa->ifa_dev); + dev = ifa->ifa_dev->dev; + iface_stat_create(dev, ifa); break; } return NOTIFY_DONE; @@ -750,28 +982,42 @@ static struct notifier_block iface_inetaddr_notifier_blk = { .notifier_call = iface_inetaddr_event_handler, }; +static struct notifier_block iface_inet6addr_notifier_blk = { + .notifier_call = iface_inet6addr_event_handler, +}; + static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) { int err; iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); if (!iface_stat_procdir) { - pr_err("iface_stat: failed to create proc entry\n"); + pr_err("qtaguid: iface_stat: failed to create proc entry\n"); err = -1; goto err; } err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { - pr_err("iface_stat: failed to register dev event handler\n"); - goto err_unreg_nd; + pr_err("qtaguid: iface_stat: failed to register dev event handler\n"); + goto err_zap_entry; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { - pr_err("iface_stat: failed to register dev event handler\n"); - goto err_zap_entry; + pr_err("qtaguid: iface_stat: " + "failed to register ipv4 dev event handler\n"); + goto err_unreg_nd; + } + + err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk); + if (err) { + pr_err("qtaguid: iface_stat: " + "failed to register ipv6 dev event handler\n"); + goto err_unreg_ip4_addr; } return 0; +err_unreg_ip4_addr: + unregister_inetaddr_notifier(&iface_inetaddr_notifier_blk); err_unreg_nd: unregister_netdevice_notifier(&iface_netdev_notifier_blk); err_zap_entry: @@ -786,11 +1032,13 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, struct sock *sk; unsigned int hook_mask = (1 << par->hooknum); - pr_debug("xt_qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, + MT_DEBUG("qtaguid: find_sk(skb=%p) hooknum=%d family=%d\n", skb, par->hooknum, par->family); - /* Let's not abuse the the xt_socket_get*_sk(), or else it will - * return garbage SKs. */ + /* + * Let's not abuse the the xt_socket_get*_sk(), or else it will + * return garbage SKs. + */ if (!(hook_mask & XT_SOCKET_SUPPORTED_HOOKS)) return NULL; @@ -805,12 +1053,13 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, return NULL; } - /* Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. + /* + * Seems to be issues on the file ptr for TCP_TIME_WAIT SKs. * http://kerneltrap.org/mailarchive/linux-netdev/2010/10/21/6287959 * Not fixed in 3.0-r3 :( */ if (sk) { - pr_debug("xt_qtaguid: %p->sk_proto=%u " + MT_DEBUG("qtaguid: %p->sk_proto=%u " "->sk_state=%d\n", sk, sk->sk_protocol, sk->sk_state); if (sk->sk_state == TCP_TIME_WAIT) { xt_socket_put_sk(sk); @@ -827,14 +1076,14 @@ static void account_for_uid(const struct sk_buff *skb, const struct net_device *el_dev; if (!skb->dev) { - pr_debug("xt_qtaguid[%d]: no skb->dev\n", par->hooknum); + MT_DEBUG("qtaguid[%d]: no skb->dev\n", par->hooknum); el_dev = par->in ? : par->out; } else { const struct net_device *other_dev; el_dev = skb->dev; other_dev = par->in ? : par->out; if (el_dev != other_dev) { - pr_debug("xt_qtaguid[%d]: skb->dev=%p %s vs " + MT_DEBUG("qtaguid[%d]: skb->dev=%p %s vs " "par->(in/out)=%p %s\n", par->hooknum, el_dev, el_dev->name, other_dev, other_dev->name); @@ -842,14 +1091,14 @@ static void account_for_uid(const struct sk_buff *skb, } if (unlikely(!el_dev)) { - pr_info("xt_qtaguid[%d]: no par->in/out?!!\n", par->hooknum); + pr_info("qtaguid[%d]: no par->in/out?!!\n", par->hooknum); } else if (unlikely(!el_dev->name)) { - pr_info("xt_qtaguid[%d]: no dev->name?!!\n", par->hooknum); + pr_info("qtaguid[%d]: no dev->name?!!\n", par->hooknum); } else { - pr_debug("xt_qtaguid[%d]: dev name=%s type=%d\n", - par->hooknum, - el_dev->name, - el_dev->type); + MT_DEBUG("qtaguid[%d]: dev name=%s type=%d\n", + par->hooknum, + el_dev->name, + el_dev->type); if_tag_stat_update(el_dev->name, uid, skb->sk ? skb->sk : alternate_sk, @@ -867,7 +1116,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) uid_t sock_uid; bool res; - pr_debug("xt_qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", + MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", par->hooknum, skb, par->in, par->out, par->family); if (skb == NULL) { @@ -878,35 +1127,42 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) sk = skb->sk; if (sk == NULL) { - /* A missing sk->sk_socket happens when packets are in-flight + /* + * A missing sk->sk_socket happens when packets are in-flight * and the matching socket is already closed and gone. */ sk = qtaguid_find_sk(skb, par); - /* If we got the socket from the find_sk(), we will need to put - * it back, as nf_tproxy_get_sock_v4() got it. */ + /* + * If we got the socket from the find_sk(), we will need to put + * it back, as nf_tproxy_get_sock_v4() got it. + */ got_sock = sk; } - pr_debug("xt_qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", + MT_DEBUG("qtaguid[%d]: sk=%p got_sock=%d proto=%d\n", par->hooknum, sk, got_sock, ip_hdr(skb)->protocol); if (sk != NULL) { - pr_debug("xt_qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", + MT_DEBUG("qtaguid[%d]: sk=%p->sk_socket=%p->file=%p\n", par->hooknum, sk, sk->sk_socket, sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); filp = sk->sk_socket ? sk->sk_socket->file : NULL; - pr_debug("xt_qtaguid[%d]: filp...uid=%d\n", + MT_DEBUG("qtaguid[%d]: filp...uid=%d\n", par->hooknum, filp ? filp->f_cred->fsuid : -1); } if (sk == NULL || sk->sk_socket == NULL) { - /* Here, the qtaguid_find_sk() using connection tracking + /* + * Here, the qtaguid_find_sk() using connection tracking * couldn't find the owner, so for now we just count them - * against the system. */ - /* TODO: unhack how to force just accounting. + * against the system. + */ + /* + * TODO: unhack how to force just accounting. * For now we only do iface stats when the uid-owner is not - * requested */ + * requested. + */ if (!(info->match & XT_QTAGUID_UID)) account_for_uid(skb, sk, 0, par); - pr_debug("xt_qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", + MT_DEBUG("qtaguid[%d]: leaving (sk?sk->sk_socket)=%p\n", par->hooknum, sk ? sk->sk_socket : NULL); res = (info->match ^ info->invert) == 0; @@ -917,18 +1173,21 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) } filp = sk->sk_socket->file; if (filp == NULL) { - pr_debug("xt_qtaguid[%d]: leaving filp=NULL\n", par->hooknum); + MT_DEBUG("qtaguid[%d]: leaving filp=NULL\n", par->hooknum); res = ((info->match ^ info->invert) & (XT_QTAGUID_UID | XT_QTAGUID_GID)) == 0; goto put_sock_ret_res; } sock_uid = filp->f_cred->fsuid; - /* TODO: unhack how to force just accounting. - * For now we only do iface stats when the uid-owner is not requested */ + /* + * TODO: unhack how to force just accounting. + * For now we only do iface stats when the uid-owner is not requested + */ if (!(info->match & XT_QTAGUID_UID)) account_for_uid(skb, sk, sock_uid, par); - /* The following two tests fail the match when: + /* + * The following two tests fail the match when: * id not in range AND no inverted condition requested * or id in range AND inverted condition requested * Thus (!a && b) || (a && !b) == a ^ b @@ -937,7 +1196,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) if ((filp->f_cred->fsuid >= info->uid_min && filp->f_cred->fsuid <= info->uid_max) ^ !(info->invert & XT_QTAGUID_UID)) { - pr_debug("xt_qtaguid[%d]: leaving uid not matching\n", + MT_DEBUG("qtaguid[%d]: leaving uid not matching\n", par->hooknum); res = false; goto put_sock_ret_res; @@ -946,20 +1205,20 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) if ((filp->f_cred->fsgid >= info->gid_min && filp->f_cred->fsgid <= info->gid_max) ^ !(info->invert & XT_QTAGUID_GID)) { - pr_debug("xt_qtaguid[%d]: leaving gid not matching\n", + MT_DEBUG("qtaguid[%d]: leaving gid not matching\n", par->hooknum); res = false; goto put_sock_ret_res; } - pr_debug("xt_qtaguid[%d]: leaving matched\n", par->hooknum); + MT_DEBUG("qtaguid[%d]: leaving matched\n", par->hooknum); res = true; put_sock_ret_res: if (got_sock) xt_socket_put_sk(sk); ret_res: - pr_debug("xt_qtaguid[%d]: left %d\n", par->hooknum, res); + MT_DEBUG("qtaguid[%d]: left %d\n", par->hooknum, res); return res; } @@ -973,7 +1232,6 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, { char *outp = page; int len; - unsigned long flags; uid_t uid; struct sock_tag *sock_tag_entry; struct rb_node *node; @@ -984,13 +1242,13 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, return 0; } - pr_debug("xt_qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", + CT_DEBUG("qtaguid: proc ctrl page=%p off=%ld char_count=%d *eof=%d\n", page, items_to_skip, char_count, *eof); if (*eof) return 0; - spin_lock_irqsave(&sock_tag_list_lock, flags); + spin_lock_bh(&sock_tag_list_lock); for (node = rb_first(&sock_tag_tree); node; node = rb_next(node)) { @@ -998,7 +1256,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, continue; sock_tag_entry = rb_entry(node, struct sock_tag, node); uid = get_uid_from_tag(sock_tag_entry->tag); - pr_debug("xt_qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", + CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", sock_tag_entry->sk, sock_tag_entry->tag, uid); @@ -1006,7 +1264,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, "sock=%p tag=0x%llx (uid=%u)\n", sock_tag_entry->sk, sock_tag_entry->tag, uid); if (len >= char_count) { - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); *outp = '\0'; return outp - page; } @@ -1014,44 +1272,52 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, char_count -= len; (*num_items_returned)++; } - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); *eof = 1; return outp - page; } -int can_impersonate_uid(uid_t uid) +static bool can_manipulate_uids(void) { - return uid == current_fsuid() - || !proc_ctrl_write_gid + /* root pwnd */ + return unlikely(!current_fsuid()) || unlikely(!proc_ctrl_write_gid) || in_egroup_p(proc_ctrl_write_gid); } -int can_read_other_uid_stats(uid_t uid) +static bool can_impersonate_uid(uid_t uid) +{ + return uid == current_fsuid() || can_manipulate_uids(); +} + +static bool can_read_other_uid_stats(uid_t uid) { - return uid == current_fsuid() - || !proc_ctrl_write_gid + /* root pwnd */ + return unlikely(!current_fsuid()) || uid == current_fsuid() + || unlikely(!proc_stats_readall_gid) || in_egroup_p(proc_stats_readall_gid); } -/* Delete socket tags, and stat tags associated with a given - * accouting tag and uid. */ +/* + * Delete socket tags, and stat tags associated with a given + * accouting tag and uid. + */ static int ctrl_cmd_delete(const char *input) { char cmd; - uid_t uid = 0; + uid_t uid; uid_t entry_uid; - tag_t acct_tag = 0; + tag_t acct_tag; tag_t tag; int res, argc; - unsigned long flags, flags2; struct iface_stat *iface_entry; struct rb_node *node; struct sock_tag *st_entry; struct tag_stat *ts_entry; + struct tag_counter_set *tcs_entry; - pr_debug("xt_qtaguid: ctrl_delete(%s): entered\n", input); + CT_DEBUG("qtaguid: ctrl_delete(%s): entered\n", input); argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); - pr_debug("xt_qtaguid: ctrl_delete(%s): argc=%d cmd=%c " + CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " "acct_tag=0x%llx uid=%u\n", input, argc, cmd, acct_tag, uid); if (argc < 2) { @@ -1059,20 +1325,21 @@ static int ctrl_cmd_delete(const char *input) goto err; } if (!valid_atag(acct_tag)) { - pr_info("xt_qtaguid: ctrl_delete(%s): invalid tag\n", input); + pr_info("qtaguid: ctrl_delete(%s): invalid tag\n", input); res = -EINVAL; goto err; } if (argc < 3) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { - pr_info("xt_qtaguid: ctrl_delete(%s): insuficient priv\n", + pr_info("qtaguid: ctrl_delete(%s): insuficient priv\n", input); res = -EPERM; goto err; } - spin_lock_irqsave(&sock_tag_list_lock, flags); + /* Delete socket tags */ + spin_lock_bh(&sock_tag_list_lock); node = rb_first(&sock_tag_tree); while (node) { st_entry = rb_entry(node, struct sock_tag, node); @@ -1082,55 +1349,128 @@ static int ctrl_cmd_delete(const char *input) continue; if (!acct_tag || st_entry->tag == tag) { - pr_debug("xt_qtaguid: ctrl_delete(): " - "erase sk=%p tag=0x%llx (uid=%d)\n", + CT_DEBUG("qtaguid: ctrl_delete(): " + "erase st: sk=%p tag=0x%llx (uid=%d)\n", st_entry->sk, st_entry->tag, entry_uid); - rb_erase(&ts_entry->node, &sock_tag_tree); + rb_erase(&st_entry->node, &sock_tag_tree); kfree(st_entry); } } - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); - /* If acct_tag is 0, then all entries belonging to uid are - * erased. */ tag = combine_atag_with_uid(acct_tag, uid); - spin_lock_irqsave(&iface_stat_list_lock, flags); - list_for_each_entry(iface_entry, &iface_stat_list, list) { - spin_lock_irqsave(&iface_entry->tag_stat_list_lock, flags2); + /* Delete tag counter-sets */ + spin_lock_bh(&tag_counter_set_list_lock); + tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (tcs_entry) { + CT_DEBUG("qtaguid: ctrl_delete(): " + "erase tcs: tag=0x%llx (uid=%d) set=%d\n", + tcs_entry->tn.tag, + get_uid_from_tag(tcs_entry->tn.tag), + tcs_entry->active_set); + rb_erase(&tcs_entry->tn.node, &tag_counter_set_tree); + kfree(tcs_entry); + } + spin_unlock_bh(&tag_counter_set_list_lock); + + /* + * If acct_tag is 0, then all entries belonging to uid are + * erased. + */ + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(iface_entry, &iface_stat_list, list) { + spin_lock_bh(&iface_entry->tag_stat_list_lock); node = rb_first(&iface_entry->tag_stat_tree); while (node) { - ts_entry = rb_entry(node, struct tag_stat, node); - entry_uid = get_uid_from_tag(ts_entry->tag); + ts_entry = rb_entry(node, struct tag_stat, tn.node); + entry_uid = get_uid_from_tag(ts_entry->tn.tag); node = rb_next(node); if (entry_uid != uid) continue; - if (!acct_tag || ts_entry->tag == tag) { - pr_debug("xt_qtaguid: ctrl_delete(): erase " - "%s 0x%llx %u\n", + if (!acct_tag || ts_entry->tn.tag == tag) { + CT_DEBUG("qtaguid: ctrl_delete(): " + "erase ts: %s 0x%llx %u\n", iface_entry->ifname, - get_atag_from_tag(ts_entry->tag), + get_atag_from_tag(ts_entry->tn.tag), entry_uid); - rb_erase(&ts_entry->node, + rb_erase(&ts_entry->tn.node, &iface_entry->tag_stat_tree); kfree(ts_entry); } } - spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, - flags2); - + spin_unlock_bh(&iface_entry->tag_stat_list_lock); } - spin_unlock_irqrestore(&iface_stat_list_lock, flags); + spin_unlock_bh(&iface_stat_list_lock); res = 0; err: - pr_debug("xt_qtaguid: ctrl_delete(%s) res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_delete(%s) res=%d\n", input, res); return res; } +static int ctrl_cmd_counter_set(const char *input) +{ + char cmd; + uid_t uid = 0; + tag_t tag; + int res, argc; + struct tag_counter_set *tcs; + int counter_set; + + CT_DEBUG("qtaguid: ctrl_counterset(%s): entered\n", input); + argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); + CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " + "set=%d uid=%u\n", input, argc, cmd, + counter_set, uid); + if (argc != 3) { + res = -EINVAL; + goto err; + } + if (counter_set < 0 || counter_set >= IFS_MAX_COUNTER_SETS) { + pr_info("qtaguid: ctrl_counterset(%s): invalid counter_set range\n", + input); + res = -EINVAL; + goto err; + } + if (!can_manipulate_uids()) { + pr_info("qtaguid: ctrl_counterset(%s): insufficient priv\n", + input); + res = -EPERM; + goto err; + } + + tag = make_tag_from_uid(uid); + spin_lock_bh(&tag_counter_set_list_lock); + tcs = tag_counter_set_tree_search(&tag_counter_set_tree, tag); + if (!tcs) { + tcs = kzalloc(sizeof(*tcs), GFP_ATOMIC); + if (!tcs) { + spin_unlock_bh(&tag_counter_set_list_lock); + pr_err("qtaguid: ctrl_counterset(%s): " + "failed to alloc counter set\n", + input); + res = -ENOMEM; + goto err; + } + tcs->tn.tag = tag; + tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); + CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " + "(uid=%d) set=%d\n", + input, tag, get_uid_from_tag(tag), counter_set); + } + tcs->active_set = counter_set; + spin_unlock_bh(&tag_counter_set_list_lock); + + res = 0; + +err: + CT_DEBUG("qtaguid: ctrl_counterset(%s) res=%d\n", input, res); + return res; +} static int ctrl_cmd_tag(const char *input) { @@ -1141,11 +1481,10 @@ static int ctrl_cmd_tag(const char *input) struct socket *el_socket; int res, argc; struct sock_tag *sock_tag_entry; - unsigned long flags; /* Unassigned args will get defaulted later. */ argc = sscanf(input, "%c %d %llu %u", &cmd, &sock_fd, &acct_tag, &uid); - pr_debug("xt_qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " + CT_DEBUG("qtaguid: ctrl_tag(%s): argc=%d cmd=%c sock_fd=%d " "acct_tag=0x%llx uid=%u\n", input, argc, cmd, sock_fd, acct_tag, uid); if (argc < 2) { @@ -1154,35 +1493,34 @@ static int ctrl_cmd_tag(const char *input) } el_socket = sockfd_lookup(sock_fd, &res); if (!el_socket) { - pr_info("xt_qtaguid: ctrl_tag(%s): failed to lookup" + pr_info("qtaguid: ctrl_tag(%s): failed to lookup" " sock_fd=%d err=%d\n", input, sock_fd, res); goto err; } if (argc < 3) { acct_tag = 0; } else if (!valid_atag(acct_tag)) { - pr_info("xt_qtaguid: ctrl_tag(%s): invalid tag\n", input); + pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); res = -EINVAL; goto err; } if (argc < 4) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { - pr_info("xt_qtaguid: ctrl_tag(%s): insuficient priv\n", + pr_info("qtaguid: ctrl_tag(%s): insuficient priv\n", input); res = -EPERM; goto err; } - spin_lock_irqsave(&sock_tag_list_lock, flags); + spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (sock_tag_entry) { sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); } else { - spin_unlock_irqrestore(&sock_tag_list_lock, flags); sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), - GFP_KERNEL); + GFP_ATOMIC); if (!sock_tag_entry) { res = -ENOMEM; goto err; @@ -1190,23 +1528,21 @@ static int ctrl_cmd_tag(const char *input) sock_tag_entry->sk = el_socket->sk; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); - spin_lock_irqsave(&sock_tag_list_lock, flags); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); } - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); - pr_debug("xt_qtaguid: tag: sock_tag_entry->sk=%p " + CT_DEBUG("qtaguid: tag: sock_tag_entry->sk=%p " "...->tag=0x%llx (uid=%u)\n", sock_tag_entry->sk, sock_tag_entry->tag, get_uid_from_tag(sock_tag_entry->tag)); res = 0; err: - pr_debug("xt_qtaguid: ctrl_tag(%s) res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_tag(%s) res=%d\n", input, res); return res; } - static int ctrl_cmd_untag(const char *input) { char cmd; @@ -1214,11 +1550,10 @@ static int ctrl_cmd_untag(const char *input) struct socket *el_socket; int res, argc; struct sock_tag *sock_tag_entry; - unsigned long flags; - pr_debug("xt_qtaguid: ctrl_untag(%s): entered\n", input); + CT_DEBUG("qtaguid: ctrl_untag(%s): entered\n", input); argc = sscanf(input, "%c %d", &cmd, &sock_fd); - pr_debug("xt_qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", + CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", input, argc, cmd, sock_fd); if (argc < 2) { res = -EINVAL; @@ -1226,26 +1561,26 @@ static int ctrl_cmd_untag(const char *input) } el_socket = sockfd_lookup(sock_fd, &res); if (!el_socket) { - pr_info("xt_qtaguid: ctrl_untag(%s): failed to lookup" + pr_info("qtaguid: ctrl_untag(%s): failed to lookup" " sock_fd=%d err=%d\n", input, sock_fd, res); goto err; } - spin_lock_irqsave(&sock_tag_list_lock, flags); + spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (!sock_tag_entry) { - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); res = -EINVAL; goto err; } /* The socket already belongs to the current process * so it can do whatever it wants to it. */ rb_erase(&sock_tag_entry->node, &sock_tag_tree); - spin_unlock_irqrestore(&sock_tag_list_lock, flags); + spin_unlock_bh(&sock_tag_list_lock); kfree(sock_tag_entry); res = 0; err: - pr_debug("xt_qtaguid: ctrl_untag(%s): res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_untag(%s): res=%d\n", input, res); return res; } @@ -1254,7 +1589,7 @@ static int qtaguid_ctrl_parse(const char *input, int count) char cmd; int res; - pr_debug("xt_qtaguid: ctrl(%s): entered\n", input); + CT_DEBUG("qtaguid: ctrl(%s): entered\n", input); cmd = input[0]; /* Collect params for commands */ switch (cmd) { @@ -1262,6 +1597,10 @@ static int qtaguid_ctrl_parse(const char *input, int count) res = ctrl_cmd_delete(input); break; + case 's': + res = ctrl_cmd_counter_set(input); + break; + case 't': res = ctrl_cmd_tag(input); break; @@ -1277,7 +1616,7 @@ static int qtaguid_ctrl_parse(const char *input, int count) if (!res) res = count; err: - pr_debug("xt_qtaguid: ctrl(%s): res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl(%s): res=%d\n", input, res); return res; } @@ -1300,14 +1639,22 @@ static int qtaguid_ctrl_proc_write(struct file *file, const char __user *buffer, return qtaguid_ctrl_parse(input_buf, count); } -static int print_stats_line(char *outp, int char_count, int item_index, - char *ifname, tag_t tag, - struct data_counters *counters) +struct proc_print_info { + char *outp; + char **num_items_returned; + struct iface_stat *iface_entry; + struct tag_stat *ts_entry; + int item_index; + int char_count; +}; + +static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) { int len; - if (!item_index) { - len = snprintf(outp, char_count, - "idx iface acct_tag_hex uid_tag_int " + struct data_counters *cnts; + if (!ppi->item_index) { + len = snprintf(ppi->outp, ppi->char_count, + "idx iface acct_tag_hex uid_tag_int cnt_set " "rx_bytes rx_packets " "tx_bytes tx_packets " "rx_tcp_packets rx_tcp_bytes " @@ -1317,47 +1664,71 @@ static int print_stats_line(char *outp, int char_count, int item_index, "tx_udp_packets tx_udp_bytes " "tx_other_packets tx_other_bytes\n"); } else { + tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); if (!can_read_other_uid_stats(stat_uid)) { - pr_debug("xt_qtaguid: insufficient priv for stat line:" + CT_DEBUG("qtaguid: insufficient priv for stat line:" "%s 0x%llx %u\n", - ifname, get_atag_from_tag(tag), stat_uid); + ppi->iface_entry->ifname, + get_atag_from_tag(tag), stat_uid); return 0; } - len = snprintf(outp, char_count, - "%d %s 0x%llx %u " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu " - "%llu %llu\n", - item_index, - ifname, - get_atag_from_tag(tag), - stat_uid, - dc_sum_bytes(counters, IFS_RX), - dc_sum_packets(counters, IFS_RX), - dc_sum_bytes(counters, IFS_TX), - dc_sum_packets(counters, IFS_TX), - counters->bpc[IFS_RX][IFS_TCP].bytes, - counters->bpc[IFS_RX][IFS_TCP].packets, - counters->bpc[IFS_RX][IFS_UDP].bytes, - counters->bpc[IFS_RX][IFS_UDP].packets, - counters->bpc[IFS_RX][IFS_PROTO_OTHER].bytes, - counters->bpc[IFS_RX][IFS_PROTO_OTHER].packets, - counters->bpc[IFS_TX][IFS_TCP].bytes, - counters->bpc[IFS_TX][IFS_TCP].packets, - counters->bpc[IFS_TX][IFS_UDP].bytes, - counters->bpc[IFS_TX][IFS_UDP].packets, - counters->bpc[IFS_TX][IFS_PROTO_OTHER].bytes, - counters->bpc[IFS_TX][IFS_PROTO_OTHER].packets); + cnts = &ppi->ts_entry->counters; + len = snprintf( + ppi->outp, ppi->char_count, + "%d %s 0x%llx %u %u " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu " + "%llu %llu\n", + ppi->item_index, + ppi->iface_entry->ifname, + get_atag_from_tag(tag), + stat_uid, + cnt_set, + dc_sum_bytes(cnts, cnt_set, IFS_RX), + dc_sum_packets(cnts, cnt_set, IFS_RX), + dc_sum_bytes(cnts, cnt_set, IFS_TX), + dc_sum_packets(cnts, cnt_set, IFS_TX), + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_RX][IFS_PROTO_OTHER].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_TCP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, + cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); } return len; } +bool pp_sets(struct proc_print_info *ppi) +{ + int len; + int counter_set; + for (counter_set = 0; counter_set < IFS_MAX_COUNTER_SETS; + counter_set++) { + len = pp_stats_line(ppi, counter_set); + if (len >= ppi->char_count) { + *ppi->outp = '\0'; + return false; + } + if (len) { + ppi->outp += len; + ppi->char_count -= len; + (*ppi->num_items_returned)++; + } + } + return true; +} /* * Procfs reader to get all tag stats using style "1)" as described in @@ -1368,19 +1739,20 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, off_t items_to_skip, int char_count, int *eof, void *data) { - char *outp = page; + struct proc_print_info ppi; int len; - unsigned long flags, flags2; - struct iface_stat *iface_entry; - struct tag_stat *ts_entry; - int item_index = 0; + + ppi.outp = page; + ppi.item_index = 0; + ppi.char_count = char_count; + ppi.num_items_returned = num_items_returned; if (unlikely(module_passive)) { *eof = 1; return 0; } - pr_debug("xt_qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " + CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " "char_count=%d *eof=%d\n", page, *num_items_returned, items_to_skip, char_count, *eof); @@ -1389,53 +1761,39 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, if (!items_to_skip) { /* The idx is there to help debug when things go belly up. */ - len = print_stats_line(outp, char_count, /*index*/0, NULL, - make_tag_from_uid(0), NULL); + len = pp_stats_line(&ppi, 0); /* Don't advance the outp unless the whole line was printed */ - if (len >= char_count) { - *outp = '\0'; - return outp - page; + if (len >= ppi.char_count) { + *ppi.outp = '\0'; + return ppi.outp - page; } - outp += len; - char_count -= len; + ppi.outp += len; + ppi.char_count -= len; } - spin_lock_irqsave(&iface_stat_list_lock, flags); - list_for_each_entry(iface_entry, &iface_stat_list, list) { + + spin_lock_bh(&iface_stat_list_lock); + list_for_each_entry(ppi.iface_entry, &iface_stat_list, list) { struct rb_node *node; - spin_lock_irqsave(&iface_entry->tag_stat_list_lock, flags2); - for (node = rb_first(&iface_entry->tag_stat_tree); + spin_lock_bh(&ppi.iface_entry->tag_stat_list_lock); + for (node = rb_first(&ppi.iface_entry->tag_stat_tree); node; node = rb_next(node)) { - ts_entry = rb_entry(node, struct tag_stat, node); - if (item_index++ < items_to_skip) + ppi.ts_entry = rb_entry(node, struct tag_stat, tn.node); + if (ppi.item_index++ < items_to_skip) continue; - len = print_stats_line(outp, char_count, - item_index, - iface_entry->ifname, - ts_entry->tag, - &ts_entry->counters); - if (len >= char_count) { - *outp = '\0'; - spin_unlock_irqrestore( - &iface_entry->tag_stat_list_lock, - flags2); - spin_unlock_irqrestore( - &iface_stat_list_lock, flags); - return outp - page; - } - if (len) { - outp += len; - char_count -= len; - (*num_items_returned)++; + if (!pp_sets(&ppi)) { + spin_unlock_bh( + &ppi.iface_entry->tag_stat_list_lock); + spin_unlock_bh(&iface_stat_list_lock); + return ppi.outp - page; } } - spin_unlock_irqrestore(&iface_entry->tag_stat_list_lock, - flags2); + spin_unlock_bh(&ppi.iface_entry->tag_stat_list_lock); } - spin_unlock_irqrestore(&iface_stat_list_lock, flags); + spin_unlock_bh(&iface_stat_list_lock); *eof = 1; - return outp - page; + return ppi.outp - page; } /*------------------------------------------*/ @@ -1444,7 +1802,7 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) int ret; *res_procdir = proc_mkdir(module_procdirname, init_net.proc_net); if (!*res_procdir) { - pr_err("xt_qtaguid: failed to create proc/.../xt_qtaguid\n"); + pr_err("qtaguid: failed to create proc/.../xt_qtaguid\n"); ret = -ENOMEM; goto no_dir; } @@ -1452,7 +1810,7 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) xt_qtaguid_ctrl_file = create_proc_entry("ctrl", proc_ctrl_perms, *res_procdir); if (!xt_qtaguid_ctrl_file) { - pr_err("xt_qtaguid: failed to create xt_qtaguid/ctrl " + pr_err("qtaguid: failed to create xt_qtaguid/ctrl " " file\n"); ret = -ENOMEM; goto no_ctrl_entry; @@ -1463,7 +1821,7 @@ static int __init qtaguid_proc_register(struct proc_dir_entry **res_procdir) xt_qtaguid_stats_file = create_proc_entry("stats", proc_stats_perms, *res_procdir); if (!xt_qtaguid_stats_file) { - pr_err("xt_qtaguid: failed to create xt_qtaguid/stats " + pr_err("qtaguid: failed to create xt_qtaguid/stats " "file\n"); ret = -ENOMEM; goto no_stats_entry; @@ -1505,7 +1863,8 @@ static int __init qtaguid_mt_init(void) return 0; } -/* TODO: allow unloading of the module. +/* + * TODO: allow unloading of the module. * For now stats are permanent. * Kconfig forces'y/n' and never an 'm'. */ diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index ab86b7927f8..bd31208bbb6 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -902,12 +902,13 @@ void svc_delete_xprt(struct svc_xprt *xprt) if (!test_and_set_bit(XPT_DETACHED, &xprt->xpt_flags)) list_del_init(&xprt->xpt_list); /* - * We used to delete the transport from whichever list - * it's sk_xprt.xpt_ready node was on, but we don't actually - * need to. This is because the only time we're called - * while still attached to a queue, the queue itself - * is about to be destroyed (in svc_destroy). + * The only time we're called while xpt_ready is still on a list + * is while the list itself is about to be destroyed (in + * svc_destroy). BUT svc_xprt_enqueue could still be attempting + * to add new entries to the sp_sockets list, so we can't leave + * a freed xprt on it. */ + list_del_init(&xprt->xpt_ready); if (test_bit(XPT_TEMP, &xprt->xpt_flags)) serv->sv_tmpcnt--; spin_unlock_bh(&serv->sv_lock); diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 1ad0f39fe09..4453eb721e1 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -1125,12 +1125,13 @@ void wiphy_update_regulatory(struct wiphy *wiphy, enum ieee80211_band band; if (ignore_reg_update(wiphy, initiator)) - goto out; + return; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) handle_band(wiphy, band, initiator); } -out: + reg_process_beacons(wiphy); reg_process_ht_flags(wiphy); if (wiphy->reg_notifier) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index c825c6e0b63..78adc4303ef 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -73,7 +73,6 @@ static int may_change_ptraced_domain(struct task_struct *task, cred = get_task_cred(tracer); tracerp = aa_cred_profile(cred); } - rcu_read_unlock(); /* not ptraced */ if (!tracer || unconfined(tracerp)) @@ -82,6 +81,7 @@ static int may_change_ptraced_domain(struct task_struct *task, error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH); out: + rcu_read_unlock(); if (cred) put_cred(cred); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 3d2fd141dff..37832026e58 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -127,7 +127,7 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, *inheritable = cred->cap_inheritable; *permitted = cred->cap_permitted; - if (!unconfined(profile)) { + if (!unconfined(profile) && !COMPLAIN_MODE(profile)) { *effective = cap_intersect(*effective, profile->caps.allow); *permitted = cap_intersect(*permitted, profile->caps.allow); } diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 5fb2e28e796..91cdf9435fe 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -342,7 +342,7 @@ static int snd_pcm_ioctl_xfern_compat(struct snd_pcm_substream *substream, kfree(bufs); return -EFAULT; } - bufs[ch] = compat_ptr(ptr); + bufs[i] = compat_ptr(ptr); bufptr++; } if (dir == SNDRV_PCM_STREAM_PLAYBACK) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b48fb43b544..524ff26417e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1578,13 +1578,15 @@ static void alc_init_auto_hp(struct hda_codec *codec) if (present == 3) spec->automute_hp_lo = 1; /* both HP and LO automute */ - if (!cfg->speaker_pins[0]) { + if (!cfg->speaker_pins[0] && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { memcpy(cfg->speaker_pins, cfg->line_out_pins, sizeof(cfg->speaker_pins)); cfg->speaker_outs = cfg->line_outs; } - if (!cfg->hp_pins[0]) { + if (!cfg->hp_pins[0] && + cfg->line_out_type == AUTO_PIN_HP_OUT) { memcpy(cfg->hp_pins, cfg->line_out_pins, sizeof(cfg->hp_pins)); cfg->hp_outs = cfg->line_outs; @@ -1603,6 +1605,7 @@ static void alc_init_auto_hp(struct hda_codec *codec) spec->automute_mode = ALC_AUTOMUTE_PIN; } if (spec->automute && cfg->line_out_pins[0] && + cfg->speaker_pins[0] && cfg->line_out_pins[0] != cfg->hp_pins[0] && cfg->line_out_pins[0] != cfg->speaker_pins[0]) { for (i = 0; i < cfg->line_outs; i++) { diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c index 54cad38ec30..32d096c98f5 100644 --- a/sound/pci/oxygen/xonar_pcm179x.c +++ b/sound/pci/oxygen/xonar_pcm179x.c @@ -327,8 +327,10 @@ static void pcm1796_init(struct oxygen *chip) { struct xonar_pcm179x *data = chip->model_data; - data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_MUTE | + data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD; + if (!data->broken_i2c) + data->pcm1796_regs[0][18 - PCM1796_REG_BASE] |= PCM1796_MUTE; data->pcm1796_regs[0][19 - PCM1796_REG_BASE] = PCM1796_FLT_SHARP | PCM1796_ATS_1; data->pcm1796_regs[0][20 - PCM1796_REG_BASE] = @@ -1123,6 +1125,7 @@ int __devinit get_xonar_pcm179x_model(struct oxygen *chip, chip->model.control_filter = xonar_st_h6_control_filter; chip->model.dac_channels_pcm = 8; chip->model.dac_channels_mixer = 8; + chip->model.dac_volume_min = 255; chip->model.dac_mclks = OXYGEN_MCLKS(256, 128, 128); break; } diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index 9259f1f3489..1f11525d97e 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -62,9 +62,9 @@ static void davinci_vcif_start(struct snd_pcm_substream *substream) w = readl(davinci_vc->base + DAVINCI_VC_CTRL); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); else - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); writel(w, davinci_vc->base + DAVINCI_VC_CTRL); } @@ -80,9 +80,9 @@ static void davinci_vcif_stop(struct snd_pcm_substream *substream) /* Reset transmitter/receiver and sample rate/frame sync generators */ w = readl(davinci_vc->base + DAVINCI_VC_CTRL); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); else - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); writel(w, davinci_vc->base + DAVINCI_VC_CTRL); } @@ -159,6 +159,7 @@ static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: davinci_vcif_start(substream); + break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 3b127009db8..493ae7c4c04 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1124,6 +1124,7 @@ int snd_soc_suspend(struct device *dev) case SND_SOC_BIAS_OFF: codec->driver->suspend(codec, PMSG_SUSPEND); codec->suspended = 1; + codec->cache_sync = 1; break; default: dev_dbg(codec->dev, "CODEC is on over suspend\n"); diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 940257b5774..c16836611d2 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -52,7 +52,10 @@ ifeq ($(ARCH),i386) endif ifeq ($(ARCH),x86_64) ARCH := x86 - IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1) + IS_X86_64 := 0 + ifeq (, $(findstring m32,$(EXTRA_CFLAGS))) + IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1) + endif ifeq (${IS_X86_64}, 1) RAW_ARCH := x86_64 ARCH_CFLAGS := -DARCH_X86_64 diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index afb0849fe53..cb2959a3fb4 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -877,9 +877,12 @@ int perf_session__read_header(struct perf_session *session, int fd) struct perf_evsel *evsel; off_t tmp; - if (perf_header__getbuffer64(header, fd, &f_attr, sizeof(f_attr))) + if (readn(fd, &f_attr, sizeof(f_attr)) <= 0) goto out_errno; + if (header->needs_swap) + perf_event__attr_swap(&f_attr.attr); + tmp = lseek(fd, 0, SEEK_CUR); evsel = perf_evsel__new(&f_attr.attr, i); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f5a8fbdd3f7..2dbf0abd8e1 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -407,20 +407,26 @@ static void perf_event__read_swap(union perf_event *event) event->read.id = bswap_64(event->read.id); } -static void perf_event__attr_swap(union perf_event *event) +/* exported for swapping attributes in file header */ +void perf_event__attr_swap(struct perf_event_attr *attr) +{ + attr->type = bswap_32(attr->type); + attr->size = bswap_32(attr->size); + attr->config = bswap_64(attr->config); + attr->sample_period = bswap_64(attr->sample_period); + attr->sample_type = bswap_64(attr->sample_type); + attr->read_format = bswap_64(attr->read_format); + attr->wakeup_events = bswap_32(attr->wakeup_events); + attr->bp_type = bswap_32(attr->bp_type); + attr->bp_addr = bswap_64(attr->bp_addr); + attr->bp_len = bswap_64(attr->bp_len); +} + +static void perf_event__hdr_attr_swap(union perf_event *event) { size_t size; - event->attr.attr.type = bswap_32(event->attr.attr.type); - event->attr.attr.size = bswap_32(event->attr.attr.size); - event->attr.attr.config = bswap_64(event->attr.attr.config); - event->attr.attr.sample_period = bswap_64(event->attr.attr.sample_period); - event->attr.attr.sample_type = bswap_64(event->attr.attr.sample_type); - event->attr.attr.read_format = bswap_64(event->attr.attr.read_format); - event->attr.attr.wakeup_events = bswap_32(event->attr.attr.wakeup_events); - event->attr.attr.bp_type = bswap_32(event->attr.attr.bp_type); - event->attr.attr.bp_addr = bswap_64(event->attr.attr.bp_addr); - event->attr.attr.bp_len = bswap_64(event->attr.attr.bp_len); + perf_event__attr_swap(&event->attr.attr); size = event->header.size; size -= (void *)&event->attr.id - (void *)event; @@ -448,7 +454,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { [PERF_RECORD_LOST] = perf_event__all64_swap, [PERF_RECORD_READ] = perf_event__read_swap, [PERF_RECORD_SAMPLE] = perf_event__all64_swap, - [PERF_RECORD_HEADER_ATTR] = perf_event__attr_swap, + [PERF_RECORD_HEADER_ATTR] = perf_event__hdr_attr_swap, [PERF_RECORD_HEADER_EVENT_TYPE] = perf_event__event_type_swap, [PERF_RECORD_HEADER_TRACING_DATA] = perf_event__tracing_data_swap, [PERF_RECORD_HEADER_BUILD_ID] = NULL, diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 66d4e149087..b84c003189f 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -112,6 +112,7 @@ int perf_session__set_kallsyms_ref_reloc_sym(struct map **maps, u64 addr); void mem_bswap_64(void *src, int byte_size); +void perf_event__attr_swap(struct perf_event_attr *attr); int perf_session__create_kernel_maps(struct perf_session *self); |