diff options
-rw-r--r-- | arch/powerpc/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/gadget/dwc_otg/dwc_otg_hcd.c | 24 | ||||
-rw-r--r-- | drivers/usb/gadget/dwc_otg/dwc_otg_hcd_intr.c | 8 | ||||
-rw-r--r-- | kernel/Kconfig.exit | 6 | ||||
-rw-r--r-- | kernel/Makefile | 1 | ||||
-rw-r--r-- | kernel/exit_profile.c | 162 | ||||
-rw-r--r-- | kernel/exit_profile.h | 7 | ||||
-rw-r--r-- | kernel/ksysfs.c | 5 |
8 files changed, 211 insertions, 4 deletions
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index e1830f35dee..6a226a5c50a 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -244,6 +244,8 @@ source "init/Kconfig" source "kernel/Kconfig.freezer" +source "kernel/Kconfig.exit" + source "arch/powerpc/sysdev/Kconfig" source "arch/powerpc/platforms/Kconfig" diff --git a/drivers/usb/gadget/dwc_otg/dwc_otg_hcd.c b/drivers/usb/gadget/dwc_otg/dwc_otg_hcd.c index 6b83ee46440..2fdf0ca99ea 100644 --- a/drivers/usb/gadget/dwc_otg/dwc_otg_hcd.c +++ b/drivers/usb/gadget/dwc_otg/dwc_otg_hcd.c @@ -273,6 +273,7 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *_p) dwc_hc_t * channel; dwc_otg_hc_regs_t * hc_regs; hcchar_data_t hcchar; + unsigned long flags; num_channels = dwc_otg_hcd->core_if->core_params->host_channels; if (!dwc_otg_hcd->core_if->dma_enable) { /* Flush out any channel requests in slave mode. */ @@ -303,6 +304,10 @@ static int32_t dwc_otg_hcd_disconnect_cb(void *_p) dwc_otg_hc_cleanup(dwc_otg_hcd->core_if,channel); list_add_tail(&channel->hc_list_entry, &dwc_otg_hcd->free_hc_list); + local_irq_save(flags); + dwc_otg_hcd->available_host_channels ++; + local_irq_restore(flags); + } } } @@ -1042,23 +1047,38 @@ void dwc_otg_hcd_endpoint_disable(struct usb_hcd *_hcd, { dwc_otg_qh_t * qh; dwc_otg_hcd_t * dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); + unsigned long flags; + int retry = 0; + DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, " "endpoint=%d\n", _ep->desc.bEndpointAddress, dwc_ep_addr_to_endpoint(_ep->desc.bEndpointAddress)); + +rescan: + local_irq_save(flags); + qh = (dwc_otg_qh_t *) (_ep->hcpriv); if (qh != NULL) { -#ifdef CONFIG_DWC_DEBUG /** Check that the QTD list is really empty */ if (!list_empty(&qh->qtd_list)) { + if (retry++ < 250) { + local_irq_restore(flags); + schedule_timeout_uninterruptible(1); + goto rescan; + } +#ifdef CONFIG_DWC_DEBUG DWC_WARN("DWC OTG HCD EP DISABLE:" " QTD List for this endpoint is not empty\n"); +#endif /* */ } -#endif /* */ dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd, qh); _ep->hcpriv = NULL; } + + local_irq_restore(flags); + return; } diff --git a/drivers/usb/gadget/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/gadget/dwc_otg/dwc_otg_hcd_intr.c index 153c9a98c4a..595ea57e534 100644 --- a/drivers/usb/gadget/dwc_otg/dwc_otg_hcd_intr.c +++ b/drivers/usb/gadget/dwc_otg/dwc_otg_hcd_intr.c @@ -665,6 +665,7 @@ static dwc_otg_halt_status_e update_isoc_urb_state(dwc_otg_hcd_t * _hcd, frame_desc->status = -EPROTO; frame_desc->actual_length = get_actual_xfer_length(_hc, _hc_regs, _qtd, _halt_status,NULL); + break; default: DWC_ERROR("%s: Unhandled _halt_status (%d)\n", __func__, _halt_status); BUG(); @@ -752,7 +753,8 @@ static void release_channel(dwc_otg_hcd_t * _hcd, DWC_ERROR("%s: No halt_status, channel %d\n", __func__, _hc->hc_num); #endif - free_qtd = 0; + free_qtd = 1; + dwc_otg_hcd_complete_urb(_hcd, _qtd->urb, -EPROTO); break; default: free_qtd = 0; @@ -1197,6 +1199,7 @@ static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * _hcd, halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_ACK, must_free); } } else { +#if 0 _qtd->error_count = 0; if (_hc->qh->ping_state) { _hc->qh->ping_state = 0; @@ -1212,6 +1215,7 @@ static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * _hcd, } else { halt_channel(_hcd, _hc, _qtd, _hc->halt_status, must_free); } +#endif } /* @@ -1638,7 +1642,7 @@ static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * _hcd, __func__, _hc->hc_num, hcint.b.nyet, hcint.d32, dwc_read_reg32(&_hcd->core_if->core_global_regs->gintsts)); #endif - halt_channel(_hcd, _hc, _qtd, _hc->halt_status, must_free); + halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS, must_free); } } } diff --git a/kernel/Kconfig.exit b/kernel/Kconfig.exit new file mode 100644 index 00000000000..4d631659b59 --- /dev/null +++ b/kernel/Kconfig.exit @@ -0,0 +1,6 @@ +config EXIT_PROFILE + bool "Add profiling features to process exit" + default n + depends on PROFILING + help + Capture information to the system log during process exit. diff --git a/kernel/Makefile b/kernel/Makefile index d7c13d249b2..35c459b81ad 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -96,6 +96,7 @@ obj-$(CONFIG_SMP) += sched_cpupri.o obj-$(CONFIG_SLOW_WORK) += slow-work.o obj-$(CONFIG_SLOW_WORK_DEBUG) += slow-work-debugfs.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o +obj-$(CONFIG_EXIT_PROFILE) += exit_profile.o ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is diff --git a/kernel/exit_profile.c b/kernel/exit_profile.c new file mode 100644 index 00000000000..e260ac222da --- /dev/null +++ b/kernel/exit_profile.c @@ -0,0 +1,162 @@ +/* + * @file exit_profile.c + * + * @remark Copyright (c) 2012 Western Digital Corporation, Inc. + */ + +#include <linux/kobject.h> +#include <linux/notifier.h> +#include <linux/profile.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/debugfs.h> + +#include "exit_profile.h" + +static int exit_profile_enabled = 0; + +static unsigned long long boot_time = 0; + + +/* + * log the process' statistics + */ +static int task_exit_notify(struct notifier_block *self, unsigned long val, void *data) +{ + struct task_struct *tsk = data; + struct mm_struct *mm; + + unsigned long long start_time, this_time; + struct timespec thistime; + + /* see the comment in fs/proc/task_mmu.c */ + unsigned long hiwater_rss; + + /* somewhat from fs/proc/array.c */ + char tcomm[sizeof(tsk->comm)]; + + pr_debug("%s: called data %p, val %lu\n", __FUNCTION__, data, val); + + if (tsk == NULL) { + printk(KERN_WARNING "%s: val %lu; data %p\n", __FUNCTION__, + val, data); + return 0; + } + start_time = (unsigned long long)tsk->real_start_time.tv_sec * NSEC_PER_SEC + + tsk->real_start_time.tv_nsec; + start_time = nsec_to_clock_t(start_time); /* clock_t because there's no jiffies to nsec */ + thistime = CURRENT_TIME; + this_time = (unsigned long long)thistime.tv_sec * NSEC_PER_SEC + + thistime.tv_nsec; + this_time = nsec_to_clock_t(this_time); /* clock_t because there's no jiffies to nsec */ + this_time -= boot_time; + + get_task_comm(tcomm, tsk); + mm = tsk->mm; + if (mm == NULL) { + printk(KERN_WARNING "%s: tsk %p; mm %p\n", __FUNCTION__, + tsk, mm); + return 0; + } + + hiwater_rss = get_mm_rss(mm); + if (hiwater_rss < mm->hiwater_rss) { + hiwater_rss = mm->hiwater_rss; + } + + printk(KERN_INFO "%s: Name %16s; VmHWM %8lu kB; start %llu; end %llu", __FUNCTION__, + tcomm, + hiwater_rss << (PAGE_SHIFT-10), + start_time, + this_time); + + if (exit_profile_enabled == 1) { + printk("\n"); + } else if (exit_profile_enabled == 2) { + printk("; pid %d; ppid %d\n", + tsk->pid, + (tsk->real_parent == NULL ? 0 : tsk->real_parent->pid)); + + if ((tsk->group_leader == NULL) || + (tsk->group_leader->pid == tsk->pid)) { + printk("\n"); + } else { + printk("; tgid %d\n", tsk->group_leader->pid); + } + } else { + if (tsk->real_parent != NULL) { + get_task_comm(tcomm, tsk->real_parent); + } + printk("; pid %d; ppid %d (%s)", + tsk->pid, + (tsk->real_parent == NULL ? 0 : tsk->real_parent->pid), + (tsk->real_parent == NULL ? "none" : tcomm)); + + if ((tsk->group_leader == NULL) || + (tsk->group_leader->pid == tsk->pid)) { + printk("\n"); + } else { + get_task_comm(tcomm, tsk->group_leader); + printk("; tgid %d (%s)\n", tsk->group_leader->pid, tcomm); + } + } + + return 0; +} +static struct notifier_block task_exit_nb = { + .notifier_call = task_exit_notify, +}; + + +/* + * attach through /sys interfaces + */ +static ssize_t exit_profile_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", exit_profile_enabled); +} + +static ssize_t exit_profile_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + int value; + + if (sscanf(buf, "%du", &value) != 1) { + printk(KERN_WARNING "%s: %s is not an integer\n", + __FUNCTION__, buf); + } + + if (value != exit_profile_enabled) { + if (exit_profile_enabled == 0) { + int err; + struct timespec boottime; + + /* do this once for all subsequent uses */ + getboottime(&boottime); + boot_time = (unsigned long long)boottime.tv_sec * NSEC_PER_SEC + + boottime.tv_nsec; + boot_time = nsec_to_clock_t(boot_time); /* clock_t because there's no jiffies to nsec */ + pr_debug("%s: boot time %llu\n", __FUNCTION__, boot_time); + + err = profile_event_register(PROFILE_TASK_EXIT, &task_exit_nb); + if (err) { + printk(KERN_WARNING "%s: failed to register task exit notify (%d)\n", + __FUNCTION__, err); + } else { + exit_profile_enabled = value; + } + } else if (value == 0) { + profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb); + exit_profile_enabled = 0; + } else { /* just update the value to change the information set */ + exit_profile_enabled = value; + } + } + + return count; +} +struct kobj_attribute exit_profile_attr = + __ATTR(exit_profile, 0666, exit_profile_show, exit_profile_store); +/* EXPORT_SYMBOL_GPL(exit_profile_attr); */ diff --git a/kernel/exit_profile.h b/kernel/exit_profile.h new file mode 100644 index 00000000000..59e663b324b --- /dev/null +++ b/kernel/exit_profile.h @@ -0,0 +1,7 @@ +/* + * @file exit_profile.c + * + * @remark Copyright (c) 2012 Western Digital Corporation, Inc. + */ + +extern struct kobj_attribute exit_profile_attr; diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index 528dd78e7e7..656d28dc6bb 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -17,6 +17,8 @@ #include <linux/profile.h> #include <linux/sched.h> +#include "exit_profile.h" + #define KERNEL_ATTR_RO(_name) \ static struct kobj_attribute _name##_attr = __ATTR_RO(_name) @@ -149,6 +151,9 @@ static struct attribute * kernel_attrs[] = { &kexec_crash_loaded_attr.attr, &vmcoreinfo_attr.attr, #endif +#ifdef CONFIG_EXIT_PROFILE + &exit_profile_attr.attr, +#endif NULL }; |