From 2a4ceb6d3e6a566cb4a9dc8f974177f031d27cd7 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 27 Jul 2009 10:27:29 +0100 Subject: agp: Switch mask_memory() method to take address argument again, not page In commit 07613ba2 ("agp: switch AGP to use page array instead of unsigned long array") we switched the mask_memory() method to take a 'struct page *' instead of an address. This is painful, because in some cases it has to be an IOMMU-mapped virtual bus address (in fact, shouldn't it _always_ be a dma_addr_t returned from pci_map_xxx(), and we just happen to get lucky most of the time?) Signed-off-by: David Woodhouse --- drivers/char/agp/agp.h | 4 ++-- drivers/char/agp/amd-k7-agp.c | 4 +++- drivers/char/agp/amd64-agp.c | 3 ++- drivers/char/agp/ati-agp.c | 3 ++- drivers/char/agp/backend.c | 4 ++-- drivers/char/agp/generic.c | 7 ++++--- drivers/char/agp/hp-agp.c | 4 +--- drivers/char/agp/i460-agp.c | 13 +++---------- drivers/char/agp/intel-agp.c | 14 +++++++------- drivers/char/agp/nvidia-agp.c | 2 +- drivers/char/agp/parisc-agp.c | 12 ++---------- drivers/char/agp/sgi-agp.c | 8 ++++---- drivers/char/agp/sworks-agp.c | 4 +++- 13 files changed, 36 insertions(+), 46 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 178e2e9e9f09..ce110a3bf298 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -107,7 +107,7 @@ struct agp_bridge_driver { void (*agp_enable)(struct agp_bridge_data *, u32); void (*cleanup)(void); void (*tlb_flush)(struct agp_memory *); - unsigned long (*mask_memory)(struct agp_bridge_data *, struct page *, int); + unsigned long (*mask_memory)(struct agp_bridge_data *, dma_addr_t, int); void (*cache_flush)(void); int (*create_gatt_table)(struct agp_bridge_data *); int (*free_gatt_table)(struct agp_bridge_data *); @@ -291,7 +291,7 @@ int agp_3_5_enable(struct agp_bridge_data *bridge); void global_cache_flush(void); void get_agp_version(struct agp_bridge_data *bridge); unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type); + dma_addr_t phys, int type); int agp_generic_type_to_mask_type(struct agp_bridge_data *bridge, int type); struct agp_bridge_data *agp_generic_find_bridge(struct pci_dev *pdev); diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index ba9bde71eaaf..542a87895ae9 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -325,7 +325,9 @@ static int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type) addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); writel(agp_generic_mask_memory(agp_bridge, - mem->pages[i], mem->type), cur_gatt+GET_GATT_OFF(addr)); + phys_to_gart(page_to_phys(mem->pages[i])), + mem->type), + cur_gatt+GET_GATT_OFF(addr)); readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */ } amd_irongate_tlbflush(mem); diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 3bf5dda90f4a..e85a5b3e952e 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -79,7 +79,8 @@ static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type) for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { tmp = agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type); + phys_to_gart(page_to_phys(mem->pages[i])), + mask_type); BUG_ON(tmp & 0xffffff0000000ffcULL); pte = (tmp & 0x000000ff00000000ULL) >> 28; diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 33656e144cc5..59ebd60c1b60 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -302,7 +302,8 @@ static int ati_insert_memory(struct agp_memory * mem, addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mem->type), + phys_to_gart(page_to_phys(mem->pages[i])), + mem->type), cur_gatt+GET_GATT_OFF(addr)); } readl(GET_GATT(agp_bridge->gart_bus_addr)); /* PCI posting */ diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index cfa5a649dfe7..3bd7e503de41 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -150,8 +150,8 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) } bridge->scratch_page_real = phys_to_gart(page_to_phys(page)); - bridge->scratch_page = - bridge->driver->mask_memory(bridge, page, 0); + bridge->scratch_page = bridge->driver->mask_memory(bridge, + phys_to_gart(page_to_phys(page)), 0); } size_value = bridge->driver->fetch_size(); diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 1e8b461b91f1..a3bcc7ef42f9 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -1132,7 +1132,9 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(bridge->driver->mask_memory(bridge, mem->pages[i], mask_type), + writel(bridge->driver->mask_memory(bridge, + phys_to_gart(page_to_phys(mem->pages[i])), + mask_type), bridge->gatt_table+j); } readl(bridge->gatt_table+j-1); /* PCI Posting. */ @@ -1347,9 +1349,8 @@ void global_cache_flush(void) EXPORT_SYMBOL(global_cache_flush); unsigned long agp_generic_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) + dma_addr_t addr, int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); /* memory type is ignored in the generic routine */ if (bridge->driver->masks) return addr | bridge->driver->masks[0].mask; diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 8f3d4c184914..64dbf4b1cf2f 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -394,10 +394,8 @@ hp_zx1_remove_memory (struct agp_memory *mem, off_t pg_start, int type) } static unsigned long -hp_zx1_mask_memory (struct agp_bridge_data *bridge, - struct page *page, int type) +hp_zx1_mask_memory (struct agp_bridge_data *bridge, dma_addr_t addr, int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); return HP_ZX1_PDIR_VALID_BIT | addr; } diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index 60cc35bb5db7..54191f860539 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -61,7 +61,7 @@ #define WR_FLUSH_GATT(index) RD_GATT(index) static unsigned long i460_mask_memory (struct agp_bridge_data *bridge, - unsigned long addr, int type); + dma_addr_t addr, int type); static struct { void *gatt; /* ioremap'd GATT area */ @@ -546,20 +546,13 @@ static void i460_destroy_page (struct page *page, int flags) #endif /* I460_LARGE_IO_PAGES */ static unsigned long i460_mask_memory (struct agp_bridge_data *bridge, - unsigned long addr, int type) + dma_addr_t addr, int type) { /* Make sure the returned address is a valid GATT entry */ return bridge->driver->masks[0].mask | (((addr & ~((1 << I460_IO_PAGE_SHIFT) - 1)) & 0xfffff000) >> 12); } -static unsigned long i460_page_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) -{ - unsigned long addr = phys_to_gart(page_to_phys(page)); - return i460_mask_memory(bridge, addr, type); -} - const struct agp_bridge_driver intel_i460_driver = { .owner = THIS_MODULE, .aperture_sizes = i460_sizes, @@ -569,7 +562,7 @@ const struct agp_bridge_driver intel_i460_driver = { .fetch_size = i460_fetch_size, .cleanup = i460_cleanup, .tlb_flush = i460_tlb_flush, - .mask_memory = i460_page_mask_memory, + .mask_memory = i460_mask_memory, .masks = i460_masks, .agp_enable = agp_generic_enable, .cache_flush = global_cache_flush, diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 8c9d50db5c3a..21983456d672 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -343,7 +343,7 @@ static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, global_cache_flush(); for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], + phys_to_gart(page_to_phys(mem->pages[i])), mask_type), intel_private.registers+I810_PTE_BASE+(j*4)); } @@ -461,9 +461,8 @@ static void intel_i810_free_by_type(struct agp_memory *curr) } static unsigned long intel_i810_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) + dma_addr_t addr, int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); /* Type checking must be done elsewhere */ return addr | bridge->driver->masks[type].mask; } @@ -851,7 +850,7 @@ static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type), + phys_to_gart(page_to_phys(mem->pages[i])), mask_type), intel_private.registers+I810_PTE_BASE+(j*4)); } readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); @@ -1081,7 +1080,9 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type), intel_private.gtt+j); + phys_to_gart(page_to_phys(mem->pages[i])), + mask_type), + intel_private.gtt+j); } readl(intel_private.gtt+j-1); @@ -1196,9 +1197,8 @@ static int intel_i915_create_gatt_table(struct agp_bridge_data *bridge) * this conditional. */ static unsigned long intel_i965_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) + dma_addr_t addr, int type) { - dma_addr_t addr = phys_to_gart(page_to_phys(page)); /* Shift high bits down */ addr |= (addr >> 28) & 0xf0; diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index 263d71dd441c..cedacee30ec3 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -225,7 +225,7 @@ static int nvidia_insert_memory(struct agp_memory *mem, off_t pg_start, int type } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - mem->pages[i], mask_type), + phys_to_gart(page_to_phys(mem->pages[i])), mask_type), agp_bridge->gatt_table+nvidia_private.pg_offset+j); } diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index f4bb43fb8016..1c129211302d 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -32,7 +32,7 @@ #define AGP8X_MODE (1 << AGP8X_MODE_BIT) static unsigned long -parisc_agp_mask_memory(struct agp_bridge_data *bridge, unsigned long addr, +parisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, int type); static struct _parisc_agp_info { @@ -189,20 +189,12 @@ parisc_agp_remove_memory(struct agp_memory *mem, off_t pg_start, int type) } static unsigned long -parisc_agp_mask_memory(struct agp_bridge_data *bridge, unsigned long addr, +parisc_agp_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, int type) { return SBA_PDIR_VALID_BIT | addr; } -static unsigned long -parisc_agp_page_mask_memory(struct agp_bridge_data *bridge, struct page *page, - int type) -{ - unsigned long addr = phys_to_gart(page_to_phys(page)); - return SBA_PDIR_VALID_BIT | addr; -} - static void parisc_agp_enable(struct agp_bridge_data *bridge, u32 mode) { diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c index d3ea2e4226b5..0d47fa847404 100644 --- a/drivers/char/agp/sgi-agp.c +++ b/drivers/char/agp/sgi-agp.c @@ -70,10 +70,9 @@ static void sgi_tioca_tlbflush(struct agp_memory *mem) * entry. */ static unsigned long -sgi_tioca_mask_memory(struct agp_bridge_data *bridge, - struct page *page, int type) +sgi_tioca_mask_memory(struct agp_bridge_data *bridge, dma_addr_t addr, + int type) { - unsigned long addr = phys_to_gart(page_to_phys(page)); return tioca_physpage_to_gart(addr); } @@ -190,7 +189,8 @@ static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { table[j] = - bridge->driver->mask_memory(bridge, mem->pages[i], + bridge->driver->mask_memory(bridge, + phys_to_gart(page_to_phys(mem->pages[i])), mem->type); } diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index b964a2199329..07259952fc32 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -349,7 +349,9 @@ static int serverworks_insert_memory(struct agp_memory *mem, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = SVRWRKS_GET_GATT(addr); - writel(agp_bridge->driver->mask_memory(agp_bridge, mem->pages[i], mem->type), cur_gatt+GET_GATT_OFF(addr)); + writel(agp_bridge->driver->mask_memory(agp_bridge, + phys_to_gart(page_to_phys(mem->pages[i])), mem->type), + cur_gatt+GET_GATT_OFF(addr)); } serverworks_tlbflush(mem); return 0; -- cgit v1.2.3 From ff663cf8705bea101d5f73cf471855c85242575e Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Thu, 23 Jul 2009 17:25:49 +0100 Subject: agp: Add generic support for graphics dma remapping New driver hooks for support graphics memory dma remapping are introduced in this patch. It makes generic code can tell if current device needs dma remapping, then call driver provided interfaces for mapping and unmapping. Change has also been made to handle scratch_page in remapping case. Signed-off-by: Zhenyu Wang Signed-off-by: David Woodhouse --- drivers/char/agp/agp.h | 6 ++++++ drivers/char/agp/backend.c | 20 ++++++++++++++++++++ drivers/char/agp/generic.c | 9 +++++++++ include/linux/agp_backend.h | 6 +++++- 4 files changed, 40 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index ce110a3bf298..17e6d0d3ba36 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -121,6 +121,11 @@ struct agp_bridge_driver { void (*agp_destroy_pages)(struct agp_memory *); int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); void (*chipset_flush)(struct agp_bridge_data *); + + int (*agp_map_page)(void *addr, dma_addr_t *ret); + void (*agp_unmap_page)(void *addr, dma_addr_t dma); + int (*agp_map_memory)(struct agp_memory *mem); + void (*agp_unmap_memory)(struct agp_memory *mem); }; struct agp_bridge_data { @@ -135,6 +140,7 @@ struct agp_bridge_data { u32 *gatt_table_real; unsigned long scratch_page; unsigned long scratch_page_real; + dma_addr_t scratch_page_dma; unsigned long gart_bus_addr; unsigned long gatt_bus_addr; u32 mode; diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 3bd7e503de41..19ac3663acdc 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -152,6 +152,15 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) bridge->scratch_page_real = phys_to_gart(page_to_phys(page)); bridge->scratch_page = bridge->driver->mask_memory(bridge, phys_to_gart(page_to_phys(page)), 0); + + if (bridge->driver->agp_map_page && + bridge->driver->agp_map_page(phys_to_virt(page_to_phys(page)), + &bridge->scratch_page_dma)) { + dev_err(&bridge->dev->dev, + "unable to dma-map scratch page\n"); + rc = -ENOMEM; + goto err_out_nounmap; + } } size_value = bridge->driver->fetch_size(); @@ -191,6 +200,13 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) return 0; err_out: + if (bridge->driver->needs_scratch_page && + bridge->driver->agp_unmap_page) { + void *va = gart_to_virt(bridge->scratch_page_real); + + bridge->driver->agp_unmap_page(va, bridge->scratch_page_dma); + } +err_out_nounmap: if (bridge->driver->needs_scratch_page) { void *va = gart_to_virt(bridge->scratch_page_real); @@ -221,6 +237,10 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) bridge->driver->needs_scratch_page) { void *va = gart_to_virt(bridge->scratch_page_real); + if (bridge->driver->agp_unmap_page) + bridge->driver->agp_unmap_page(va, + bridge->scratch_page_dma); + bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); } diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index a3bcc7ef42f9..28f0208c66a6 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -437,6 +437,12 @@ int agp_bind_memory(struct agp_memory *curr, off_t pg_start) curr->bridge->driver->cache_flush(); curr->is_flushed = true; } + + if (curr->bridge->driver->agp_map_memory) { + ret_val = curr->bridge->driver->agp_map_memory(curr); + if (ret_val) + return ret_val; + } ret_val = curr->bridge->driver->insert_memory(curr, pg_start, curr->type); if (ret_val != 0) @@ -478,6 +484,9 @@ int agp_unbind_memory(struct agp_memory *curr) if (ret_val != 0) return ret_val; + if (curr->bridge->driver->agp_unmap_memory) + curr->bridge->driver->agp_unmap_memory(curr); + curr->is_bound = false; curr->pg_start = 0; spin_lock(&curr->bridge->mapped_lock); diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h index 76fa794fdac0..8a294d65b9b1 100644 --- a/include/linux/agp_backend.h +++ b/include/linux/agp_backend.h @@ -79,9 +79,13 @@ struct agp_memory { u32 physical; bool is_bound; bool is_flushed; - bool vmalloc_flag; + bool vmalloc_flag; + bool sg_vmalloc_flag; /* list of agp_memory mapped to the aperture */ struct list_head mapped_list; + /* DMA-mapped addresses */ + struct scatterlist *sg_list; + int num_sg; }; #define AGP_NORMAL_MEMORY 0 -- cgit v1.2.3 From 176616814d700f19914d8509d9f65dec51a6ebf7 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Mon, 27 Jul 2009 12:59:57 +0100 Subject: intel_agp: Use PCI DMA API correctly on chipsets new enough to have IOMMU When graphics dma remapping engine is active, we must fill gart table with dma address from dmar engine, as now graphics device access to graphics memory must go through dma remapping table to get real physical address. Add this support to all drivers which use intel_i915_insert_entries() Signed-off-by: Zhenyu Wang Signed-off-by: David Woodhouse --- drivers/char/agp/intel-agp.c | 174 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 162 insertions(+), 12 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 21983456d672..20fe82b99fdb 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -10,6 +10,16 @@ #include #include "agp.h" +/* + * If we have Intel graphics, we're not going to have anything other than + * an Intel IOMMU. So make the correct use of the PCI DMA API contingent + * on the Intel IOMMU support (CONFIG_DMAR). + * Only newer chipsets need to bother with this, of course. + */ +#ifdef CONFIG_DMAR +#define USE_PCI_DMA_API 1 +#endif + #define PCI_DEVICE_ID_INTEL_E7221_HB 0x2588 #define PCI_DEVICE_ID_INTEL_E7221_IG 0x258a #define PCI_DEVICE_ID_INTEL_82946GZ_HB 0x2970 @@ -170,6 +180,131 @@ static struct _intel_private { int resource_valid; } intel_private; +#ifdef USE_PCI_DMA_API +static int intel_agp_map_page(void *addr, dma_addr_t *ret) +{ + *ret = pci_map_single(intel_private.pcidev, addr, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(intel_private.pcidev, *ret)) + return -EINVAL; + return 0; +} + +static void intel_agp_unmap_page(void *addr, dma_addr_t dma) +{ + pci_unmap_single(intel_private.pcidev, dma, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); +} + +static int intel_agp_map_memory(struct agp_memory *mem) +{ + struct scatterlist *sg; + int i; + + DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); + + if ((mem->page_count * sizeof(*mem->sg_list)) < 2*PAGE_SIZE) + mem->sg_list = kcalloc(mem->page_count, sizeof(*mem->sg_list), + GFP_KERNEL); + + if (mem->sg_list == NULL) { + mem->sg_list = vmalloc(mem->page_count * sizeof(*mem->sg_list)); + mem->sg_vmalloc_flag = 1; + } + + if (!mem->sg_list) { + mem->sg_vmalloc_flag = 0; + return -ENOMEM; + } + sg_init_table(mem->sg_list, mem->page_count); + + sg = mem->sg_list; + for (i = 0 ; i < mem->page_count; i++, sg = sg_next(sg)) + sg_set_page(sg, mem->pages[i], PAGE_SIZE, 0); + + mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list, + mem->page_count, PCI_DMA_BIDIRECTIONAL); + if (!mem->num_sg) { + if (mem->sg_vmalloc_flag) + vfree(mem->sg_list); + else + kfree(mem->sg_list); + mem->sg_list = NULL; + mem->sg_vmalloc_flag = 0; + return -ENOMEM; + } + return 0; +} + +static void intel_agp_unmap_memory(struct agp_memory *mem) +{ + DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count); + + pci_unmap_sg(intel_private.pcidev, mem->sg_list, + mem->page_count, PCI_DMA_BIDIRECTIONAL); + if (mem->sg_vmalloc_flag) + vfree(mem->sg_list); + else + kfree(mem->sg_list); + mem->sg_vmalloc_flag = 0; + mem->sg_list = NULL; + mem->num_sg = 0; +} + +static void intel_agp_insert_sg_entries(struct agp_memory *mem, + off_t pg_start, int mask_type) +{ + struct scatterlist *sg; + int i, j; + + j = pg_start; + + WARN_ON(!mem->num_sg); + + if (mem->num_sg == mem->page_count) { + for_each_sg(mem->sg_list, sg, mem->page_count, i) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + sg_dma_address(sg), mask_type), + intel_private.gtt+j); + j++; + } + } else { + /* sg may merge pages, but we have to seperate + * per-page addr for GTT */ + unsigned int len, m; + + for_each_sg(mem->sg_list, sg, mem->num_sg, i) { + len = sg_dma_len(sg) / PAGE_SIZE; + for (m = 0; m < len; m++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + sg_dma_address(sg) + m * PAGE_SIZE, + mask_type), + intel_private.gtt+j); + j++; + } + } + } + readl(intel_private.gtt+j-1); +} + +#else + +static void intel_agp_insert_sg_entries(struct agp_memory *mem, + off_t pg_start, int mask_type) +{ + int i, j; + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + writel(agp_bridge->driver->mask_memory(agp_bridge, + phys_to_gart(page_to_phys(mem->pages[i])), mask_type), + intel_private.gtt+j); + } + + readl(intel_private.gtt+j-1); +} + +#endif + static int intel_i810_fetch_size(void) { u32 smram_miscc; @@ -1003,9 +1138,13 @@ static int intel_i915_configure(void) writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ +#ifndef USE_PCI_DMA_API + agp_bridge->scratch_page_dma = agp_bridge->scratch_page; +#endif + if (agp_bridge->driver->needs_scratch_page) { for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { - writel(agp_bridge->scratch_page, intel_private.gtt+i); + writel(agp_bridge->scratch_page_dma, intel_private.gtt+i); } readl(intel_private.gtt+i-1); /* PCI Posting. */ } @@ -1038,7 +1177,7 @@ static void intel_i915_chipset_flush(struct agp_bridge_data *bridge) static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, int type) { - int i, j, num_entries; + int num_entries; void *temp; int ret = -EINVAL; int mask_type; @@ -1062,7 +1201,7 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, if ((pg_start + mem->page_count) > num_entries) goto out_err; - /* The i915 can't check the GTT for entries since its read only, + /* The i915 can't check the GTT for entries since it's read only; * depend on the caller to make the correct offset decisions. */ @@ -1078,14 +1217,7 @@ static int intel_i915_insert_entries(struct agp_memory *mem, off_t pg_start, if (!mem->is_flushed) global_cache_flush(); - for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { - writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), - mask_type), - intel_private.gtt+j); - } - - readl(intel_private.gtt+j-1); + intel_agp_insert_sg_entries(mem, pg_start, mask_type); agp_bridge->driver->tlb_flush(mem); out: @@ -1110,7 +1242,7 @@ static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start, } for (i = pg_start; i < (mem->page_count + pg_start); i++) - writel(agp_bridge->scratch_page, intel_private.gtt+i); + writel(agp_bridge->scratch_page_dma, intel_private.gtt+i); readl(intel_private.gtt+i-1); @@ -2003,6 +2135,12 @@ static const struct agp_bridge_driver intel_915_driver = { .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif }; static const struct agp_bridge_driver intel_i965_driver = { @@ -2031,6 +2169,12 @@ static const struct agp_bridge_driver intel_i965_driver = { .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif }; static const struct agp_bridge_driver intel_7505_driver = { @@ -2085,6 +2229,12 @@ static const struct agp_bridge_driver intel_g33_driver = { .agp_destroy_pages = agp_generic_destroy_pages, .agp_type_to_mask_type = intel_i830_type_to_mask_type, .chipset_flush = intel_i915_chipset_flush, +#ifdef USE_PCI_DMA_API + .agp_map_page = intel_agp_map_page, + .agp_unmap_page = intel_agp_unmap_page, + .agp_map_memory = intel_agp_map_memory, + .agp_unmap_memory = intel_agp_unmap_memory, +#endif }; static int find_gmch(u16 device) -- cgit v1.2.3 From 56ec4c1e72865c6d99f643b6574e6e074c3e8823 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 27 Jul 2009 16:44:32 +0100 Subject: agp: tidy up handling of scratch pages w.r.t. DMA API Signed-off-by: David Woodhouse --- drivers/char/agp/backend.c | 23 +++++++++++++---------- drivers/char/agp/intel-agp.c | 8 ++------ 2 files changed, 15 insertions(+), 16 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 19ac3663acdc..3c3a487f7b9d 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -150,17 +150,20 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) } bridge->scratch_page_real = phys_to_gart(page_to_phys(page)); - bridge->scratch_page = bridge->driver->mask_memory(bridge, - phys_to_gart(page_to_phys(page)), 0); - - if (bridge->driver->agp_map_page && - bridge->driver->agp_map_page(phys_to_virt(page_to_phys(page)), - &bridge->scratch_page_dma)) { - dev_err(&bridge->dev->dev, - "unable to dma-map scratch page\n"); - rc = -ENOMEM; - goto err_out_nounmap; + if (bridge->driver->agp_map_page) { + if (bridge->driver->agp_map_page(phys_to_virt(page_to_phys(page)), + &bridge->scratch_page_dma)) { + dev_err(&bridge->dev->dev, + "unable to dma-map scratch page\n"); + rc = -ENOMEM; + goto err_out_nounmap; + } + } else { + bridge->scratch_page_dma = phys_to_gart(page_to_phys(page)); } + + bridge->scratch_page = bridge->driver->mask_memory(bridge, + bridge->scratch_page_dma, 0); } size_value = bridge->driver->fetch_size(); diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 20fe82b99fdb..b8f2c75b98d1 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -1138,13 +1138,9 @@ static int intel_i915_configure(void) writel(agp_bridge->gatt_bus_addr|I810_PGETBL_ENABLED, intel_private.registers+I810_PGETBL_CTL); readl(intel_private.registers+I810_PGETBL_CTL); /* PCI Posting. */ -#ifndef USE_PCI_DMA_API - agp_bridge->scratch_page_dma = agp_bridge->scratch_page; -#endif - if (agp_bridge->driver->needs_scratch_page) { for (i = intel_private.gtt_entries; i < current_size->num_entries; i++) { - writel(agp_bridge->scratch_page_dma, intel_private.gtt+i); + writel(agp_bridge->scratch_page, intel_private.gtt+i); } readl(intel_private.gtt+i-1); /* PCI Posting. */ } @@ -1242,7 +1238,7 @@ static int intel_i915_remove_entries(struct agp_memory *mem, off_t pg_start, } for (i = pg_start; i < (mem->page_count + pg_start); i++) - writel(agp_bridge->scratch_page_dma, intel_private.gtt+i); + writel(agp_bridge->scratch_page, intel_private.gtt+i); readl(intel_private.gtt+i-1); -- cgit v1.2.3 From c2980d8c2961113f24863f70d8ad016f55224c81 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 29 Jul 2009 08:39:26 +0100 Subject: agp: Switch agp_{un,}map_page() to take struct page * argument Signed-off-by: David Woodhouse --- drivers/char/agp/agp.h | 6 +++--- drivers/char/agp/backend.c | 17 ++++++++--------- drivers/char/agp/intel-agp.c | 12 ++++++------ 3 files changed, 17 insertions(+), 18 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 17e6d0d3ba36..4c6e5079d870 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -122,8 +122,8 @@ struct agp_bridge_driver { int (*agp_type_to_mask_type) (struct agp_bridge_data *, int); void (*chipset_flush)(struct agp_bridge_data *); - int (*agp_map_page)(void *addr, dma_addr_t *ret); - void (*agp_unmap_page)(void *addr, dma_addr_t dma); + int (*agp_map_page)(struct page *page, dma_addr_t *ret); + void (*agp_unmap_page)(struct page *page, dma_addr_t dma); int (*agp_map_memory)(struct agp_memory *mem); void (*agp_unmap_memory)(struct agp_memory *mem); }; @@ -139,7 +139,7 @@ struct agp_bridge_data { u32 __iomem *gatt_table; u32 *gatt_table_real; unsigned long scratch_page; - unsigned long scratch_page_real; + struct page *scratch_page_page; dma_addr_t scratch_page_dma; unsigned long gart_bus_addr; unsigned long gatt_bus_addr; diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 3c3a487f7b9d..343f102090a0 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -149,9 +149,9 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) return -ENOMEM; } - bridge->scratch_page_real = phys_to_gart(page_to_phys(page)); + bridge->scratch_page_page = page; if (bridge->driver->agp_map_page) { - if (bridge->driver->agp_map_page(phys_to_virt(page_to_phys(page)), + if (bridge->driver->agp_map_page(page, &bridge->scratch_page_dma)) { dev_err(&bridge->dev->dev, "unable to dma-map scratch page\n"); @@ -205,13 +205,12 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) err_out: if (bridge->driver->needs_scratch_page && bridge->driver->agp_unmap_page) { - void *va = gart_to_virt(bridge->scratch_page_real); - - bridge->driver->agp_unmap_page(va, bridge->scratch_page_dma); + bridge->driver->agp_unmap_page(bridge->scratch_page_page, + bridge->scratch_page_dma); } err_out_nounmap: if (bridge->driver->needs_scratch_page) { - void *va = gart_to_virt(bridge->scratch_page_real); + void *va = page_address(bridge->scratch_page_page); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); @@ -238,11 +237,11 @@ static void agp_backend_cleanup(struct agp_bridge_data *bridge) if (bridge->driver->agp_destroy_page && bridge->driver->needs_scratch_page) { - void *va = gart_to_virt(bridge->scratch_page_real); + void *va = page_address(bridge->scratch_page_page); if (bridge->driver->agp_unmap_page) - bridge->driver->agp_unmap_page(va, - bridge->scratch_page_dma); + bridge->driver->agp_unmap_page(bridge->scratch_page_page, + bridge->scratch_page_dma); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_UNMAP); bridge->driver->agp_destroy_page(va, AGP_PAGE_DESTROY_FREE); diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index b8f2c75b98d1..148d7e38fddf 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -181,19 +181,19 @@ static struct _intel_private { } intel_private; #ifdef USE_PCI_DMA_API -static int intel_agp_map_page(void *addr, dma_addr_t *ret) +static int intel_agp_map_page(struct page *page, dma_addr_t *ret) { - *ret = pci_map_single(intel_private.pcidev, addr, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + *ret = pci_map_page(intel_private.pcidev, page, 0, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); if (pci_dma_mapping_error(intel_private.pcidev, *ret)) return -EINVAL; return 0; } -static void intel_agp_unmap_page(void *addr, dma_addr_t dma) +static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) { - pci_unmap_single(intel_private.pcidev, dma, - PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); + pci_unmap_page(intel_private.pcidev, dma, + PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); } static int intel_agp_map_memory(struct agp_memory *mem) -- cgit v1.2.3 From 91b8e3056bf9107b688eb076c9b804171364db71 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 29 Jul 2009 08:49:12 +0100 Subject: intel-agp: Move repeated sglist free into separate function Signed-off-by: David Woodhouse --- drivers/char/agp/intel-agp.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 148d7e38fddf..b9d9886ff3c3 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -196,6 +196,18 @@ static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); } +static void intel_agp_free_sglist(struct agp_memory *mem) +{ + + if (mem->sg_vmalloc_flag) + vfree(mem->sg_list); + else + kfree(mem->sg_list); + mem->sg_vmalloc_flag = 0; + mem->sg_list = NULL; + mem->num_sg = 0; +} + static int intel_agp_map_memory(struct agp_memory *mem) { struct scatterlist *sg; @@ -224,13 +236,8 @@ static int intel_agp_map_memory(struct agp_memory *mem) mem->num_sg = pci_map_sg(intel_private.pcidev, mem->sg_list, mem->page_count, PCI_DMA_BIDIRECTIONAL); - if (!mem->num_sg) { - if (mem->sg_vmalloc_flag) - vfree(mem->sg_list); - else - kfree(mem->sg_list); - mem->sg_list = NULL; - mem->sg_vmalloc_flag = 0; + if (unlikely(!mem->num_sg)) { + intel_agp_free_sglist(mem); return -ENOMEM; } return 0; @@ -242,13 +249,7 @@ static void intel_agp_unmap_memory(struct agp_memory *mem) pci_unmap_sg(intel_private.pcidev, mem->sg_list, mem->page_count, PCI_DMA_BIDIRECTIONAL); - if (mem->sg_vmalloc_flag) - vfree(mem->sg_list); - else - kfree(mem->sg_list); - mem->sg_vmalloc_flag = 0; - mem->sg_list = NULL; - mem->num_sg = 0; + intel_agp_free_sglist(mem); } static void intel_agp_insert_sg_entries(struct agp_memory *mem, -- cgit v1.2.3 From f692775d7e0a22477143cd884e45c955448ac7d2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 29 Jul 2009 09:28:45 +0100 Subject: intel-agp: fix sglist allocation to avoid vmalloc() Signed-off-by: David Woodhouse --- drivers/char/agp/intel-agp.c | 29 ++++++++++------------------- include/linux/agp_backend.h | 1 - 2 files changed, 10 insertions(+), 20 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index b9d9886ff3c3..d8c80d8be5e2 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -198,39 +198,30 @@ static void intel_agp_unmap_page(struct page *page, dma_addr_t dma) static void intel_agp_free_sglist(struct agp_memory *mem) { + struct sg_table st; + + st.sgl = mem->sg_list; + st.orig_nents = st.nents = mem->page_count; + + sg_free_table(&st); - if (mem->sg_vmalloc_flag) - vfree(mem->sg_list); - else - kfree(mem->sg_list); - mem->sg_vmalloc_flag = 0; mem->sg_list = NULL; mem->num_sg = 0; } static int intel_agp_map_memory(struct agp_memory *mem) { + struct sg_table st; struct scatterlist *sg; int i; DBG("try mapping %lu pages\n", (unsigned long)mem->page_count); - if ((mem->page_count * sizeof(*mem->sg_list)) < 2*PAGE_SIZE) - mem->sg_list = kcalloc(mem->page_count, sizeof(*mem->sg_list), - GFP_KERNEL); - - if (mem->sg_list == NULL) { - mem->sg_list = vmalloc(mem->page_count * sizeof(*mem->sg_list)); - mem->sg_vmalloc_flag = 1; - } - - if (!mem->sg_list) { - mem->sg_vmalloc_flag = 0; + if (sg_alloc_table(&st, mem->page_count, GFP_KERNEL)) return -ENOMEM; - } - sg_init_table(mem->sg_list, mem->page_count); - sg = mem->sg_list; + mem->sg_list = sg = st.sgl; + for (i = 0 ; i < mem->page_count; i++, sg = sg_next(sg)) sg_set_page(sg, mem->pages[i], PAGE_SIZE, 0); diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h index 8a294d65b9b1..880130f7311f 100644 --- a/include/linux/agp_backend.h +++ b/include/linux/agp_backend.h @@ -80,7 +80,6 @@ struct agp_memory { bool is_bound; bool is_flushed; bool vmalloc_flag; - bool sg_vmalloc_flag; /* list of agp_memory mapped to the aperture */ struct list_head mapped_list; /* DMA-mapped addresses */ -- cgit v1.2.3 From 6a12235c7d2d75c7d94b9afcaaecd422ff845ce0 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 29 Jul 2009 10:25:58 +0100 Subject: agp: kill phys_to_gart() and gart_to_phys() There seems to be no reason for these -- they're a 1:1 mapping on all platforms. Signed-off-by: David Woodhouse --- arch/alpha/include/asm/agp.h | 4 ---- arch/ia64/include/asm/agp.h | 4 ---- arch/parisc/include/asm/agp.h | 4 ---- arch/powerpc/include/asm/agp.h | 4 ---- arch/sparc/include/asm/agp.h | 4 ---- arch/x86/include/asm/agp.h | 4 ---- drivers/char/agp/agp.h | 3 --- drivers/char/agp/ali-agp.c | 4 ++-- drivers/char/agp/amd-k7-agp.c | 8 ++++---- drivers/char/agp/amd64-agp.c | 6 +++--- drivers/char/agp/ati-agp.c | 6 +++--- drivers/char/agp/backend.c | 2 +- drivers/char/agp/efficeon-agp.c | 4 ++-- drivers/char/agp/generic.c | 6 +++--- drivers/char/agp/hp-agp.c | 4 ++-- drivers/char/agp/i460-agp.c | 4 ++-- drivers/char/agp/intel-agp.c | 7 +++---- drivers/char/agp/nvidia-agp.c | 2 +- drivers/char/agp/sgi-agp.c | 2 +- drivers/char/agp/sworks-agp.c | 8 ++++---- drivers/char/agp/uninorth-agp.c | 2 +- 21 files changed, 32 insertions(+), 60 deletions(-) (limited to 'drivers/char') diff --git a/arch/alpha/include/asm/agp.h b/arch/alpha/include/asm/agp.h index 26c179135293..a94d48b8677f 100644 --- a/arch/alpha/include/asm/agp.h +++ b/arch/alpha/include/asm/agp.h @@ -9,10 +9,6 @@ #define unmap_page_from_agp(page) #define flush_agp_cache() mb() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/arch/ia64/include/asm/agp.h b/arch/ia64/include/asm/agp.h index c11fdd8ab4d7..01d09c401c5c 100644 --- a/arch/ia64/include/asm/agp.h +++ b/arch/ia64/include/asm/agp.h @@ -17,10 +17,6 @@ #define unmap_page_from_agp(page) /* nothing */ #define flush_agp_cache() mb() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/arch/parisc/include/asm/agp.h b/arch/parisc/include/asm/agp.h index 9651660da639..d226ffa8fc12 100644 --- a/arch/parisc/include/asm/agp.h +++ b/arch/parisc/include/asm/agp.h @@ -11,10 +11,6 @@ #define unmap_page_from_agp(page) /* nothing */ #define flush_agp_cache() mb() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/arch/powerpc/include/asm/agp.h b/arch/powerpc/include/asm/agp.h index 86455c4c31ee..416e12c2d505 100644 --- a/arch/powerpc/include/asm/agp.h +++ b/arch/powerpc/include/asm/agp.h @@ -8,10 +8,6 @@ #define unmap_page_from_agp(page) #define flush_agp_cache() mb() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/arch/sparc/include/asm/agp.h b/arch/sparc/include/asm/agp.h index c2456870b05c..70f52c1661bc 100644 --- a/arch/sparc/include/asm/agp.h +++ b/arch/sparc/include/asm/agp.h @@ -7,10 +7,6 @@ #define unmap_page_from_agp(page) #define flush_agp_cache() mb() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/arch/x86/include/asm/agp.h b/arch/x86/include/asm/agp.h index 9825cd64c9b6..eec2a70d4376 100644 --- a/arch/x86/include/asm/agp.h +++ b/arch/x86/include/asm/agp.h @@ -22,10 +22,6 @@ */ #define flush_agp_cache() wbinvd() -/* Convert a physical address to an address suitable for the GART. */ -#define phys_to_gart(x) (x) -#define gart_to_phys(x) (x) - /* GATT allocation. Returns/accepts GATT kernel virtual address. */ #define alloc_gatt_pages(order) \ ((char *)__get_free_pages(GFP_KERNEL, (order))) diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 4c6e5079d870..d6f36c004d9b 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -318,9 +318,6 @@ void agp3_generic_cleanup(void); #define AGP_GENERIC_SIZES_ENTRIES 11 extern const struct aper_size_info_16 agp3_generic_sizes[]; -#define virt_to_gart(x) (phys_to_gart(virt_to_phys(x))) -#define gart_to_virt(x) (phys_to_virt(gart_to_phys(x))) - extern int agp_off; extern int agp_try_unsupported_boot; diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c index 201ef3ffd484..d2ce68f27e4b 100644 --- a/drivers/char/agp/ali-agp.c +++ b/drivers/char/agp/ali-agp.c @@ -152,7 +152,7 @@ static struct page *m1541_alloc_page(struct agp_bridge_data *bridge) pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp); pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | - phys_to_gart(page_to_phys(page))) | ALI_CACHE_FLUSH_EN )); + page_to_phys(page)) | ALI_CACHE_FLUSH_EN )); return page; } @@ -180,7 +180,7 @@ static void m1541_destroy_page(struct page *page, int flags) pci_read_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, &temp); pci_write_config_dword(agp_bridge->dev, ALI_CACHE_FLUSH_CTRL, (((temp & ALI_CACHE_FLUSH_ADDR_MASK) | - phys_to_gart(page_to_phys(page))) | ALI_CACHE_FLUSH_EN)); + page_to_phys(page)) | ALI_CACHE_FLUSH_EN)); } agp_generic_destroy_page(page, flags); } diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index 542a87895ae9..73dbf40c874d 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -44,7 +44,7 @@ static int amd_create_page_map(struct amd_page_map *page_map) #ifndef CONFIG_X86 SetPageReserved(virt_to_page(page_map->real)); global_cache_flush(); - page_map->remapped = ioremap_nocache(virt_to_gart(page_map->real), + page_map->remapped = ioremap_nocache(virt_to_phys(page_map->real), PAGE_SIZE); if (page_map->remapped == NULL) { ClearPageReserved(virt_to_page(page_map->real)); @@ -160,7 +160,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) agp_bridge->gatt_table_real = (u32 *)page_dir.real; agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped; - agp_bridge->gatt_bus_addr = virt_to_gart(page_dir.real); + agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); /* Get the address for the gart region. * This is a bus address even on the alpha, b/c its @@ -173,7 +173,7 @@ static int amd_create_gatt_table(struct agp_bridge_data *bridge) /* Calculate the agp offset */ for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { - writel(virt_to_gart(amd_irongate_private.gatt_pages[i]->real) | 1, + writel(virt_to_phys(amd_irongate_private.gatt_pages[i]->real) | 1, page_dir.remapped+GET_PAGE_DIR_OFF(addr)); readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ } @@ -325,7 +325,7 @@ static int amd_insert_memory(struct agp_memory *mem, off_t pg_start, int type) addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); writel(agp_generic_mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), + page_to_phys(mem->pages[i]), mem->type), cur_gatt+GET_GATT_OFF(addr)); readl(cur_gatt+GET_GATT_OFF(addr)); /* PCI Posting. */ diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index e85a5b3e952e..2fb2e6cc322a 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -79,7 +79,7 @@ static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type) for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { tmp = agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), + page_to_phys(mem->pages[i]), mask_type); BUG_ON(tmp & 0xffffff0000000ffcULL); @@ -178,7 +178,7 @@ static const struct aper_size_info_32 amd_8151_sizes[7] = static int amd_8151_configure(void) { - unsigned long gatt_bus = virt_to_gart(agp_bridge->gatt_table_real); + unsigned long gatt_bus = virt_to_phys(agp_bridge->gatt_table_real); int i; /* Configure AGP regs in each x86-64 host bridge. */ @@ -558,7 +558,7 @@ static void __devexit agp_amd64_remove(struct pci_dev *pdev) { struct agp_bridge_data *bridge = pci_get_drvdata(pdev); - release_mem_region(virt_to_gart(bridge->gatt_table_real), + release_mem_region(virt_to_phys(bridge->gatt_table_real), amd64_aperture_sizes[bridge->aperture_size_idx].size); agp_remove_bridge(bridge); agp_put_bridge(bridge); diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 59ebd60c1b60..3b2ecbe86ebe 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -302,7 +302,7 @@ static int ati_insert_memory(struct agp_memory * mem, addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = GET_GATT(addr); writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), + page_to_phys(mem->pages[i]), mem->type), cur_gatt+GET_GATT_OFF(addr)); } @@ -360,7 +360,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) agp_bridge->gatt_table_real = (u32 *)page_dir.real; agp_bridge->gatt_table = (u32 __iomem *) page_dir.remapped; - agp_bridge->gatt_bus_addr = virt_to_gart(page_dir.real); + agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); /* Write out the size register */ current_size = A_SIZE_LVL2(agp_bridge->current_size); @@ -390,7 +390,7 @@ static int ati_create_gatt_table(struct agp_bridge_data *bridge) /* Calculate the agp offset */ for (i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { - writel(virt_to_gart(ati_generic_private.gatt_pages[i]->real) | 1, + writel(virt_to_phys(ati_generic_private.gatt_pages[i]->real) | 1, page_dir.remapped+GET_PAGE_DIR_OFF(addr)); readl(page_dir.remapped+GET_PAGE_DIR_OFF(addr)); /* PCI Posting. */ } diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index 343f102090a0..ad87753f6de4 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -159,7 +159,7 @@ static int agp_backend_initialize(struct agp_bridge_data *bridge) goto err_out_nounmap; } } else { - bridge->scratch_page_dma = phys_to_gart(page_to_phys(page)); + bridge->scratch_page_dma = page_to_phys(page); } bridge->scratch_page = bridge->driver->mask_memory(bridge, diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index 35d50f2861b6..793f39ea9618 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -67,7 +67,7 @@ static const struct gatt_mask efficeon_generic_masks[] = /* This function does the same thing as mask_memory() for this chipset... */ static inline unsigned long efficeon_mask_memory(struct page *page) { - unsigned long addr = phys_to_gart(page_to_phys(page)); + unsigned long addr = page_to_phys(page); return addr | 0x00000001; } @@ -226,7 +226,7 @@ static int efficeon_create_gatt_table(struct agp_bridge_data *bridge) efficeon_private.l1_table[index] = page; - value = virt_to_gart((unsigned long *)page) | pati | present | index; + value = virt_to_phys((unsigned long *)page) | pati | present | index; pci_write_config_dword(agp_bridge->dev, EFFICEON_ATTPAGE, value); diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 28f0208c66a6..c50543966eb2 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -988,7 +988,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge) set_memory_uc((unsigned long)table, 1 << page_order); bridge->gatt_table = (void *)table; #else - bridge->gatt_table = ioremap_nocache(virt_to_gart(table), + bridge->gatt_table = ioremap_nocache(virt_to_phys(table), (PAGE_SIZE * (1 << page_order))); bridge->driver->cache_flush(); #endif @@ -1001,7 +1001,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge) return -ENOMEM; } - bridge->gatt_bus_addr = virt_to_gart(bridge->gatt_table_real); + bridge->gatt_bus_addr = virt_to_phys(bridge->gatt_table_real); /* AK: bogus, should encode addresses > 4GB */ for (i = 0; i < num_entries; i++) { @@ -1142,7 +1142,7 @@ int agp_generic_insert_memory(struct agp_memory * mem, off_t pg_start, int type) for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(bridge->driver->mask_memory(bridge, - phys_to_gart(page_to_phys(mem->pages[i])), + page_to_phys(mem->pages[i]), mask_type), bridge->gatt_table+j); } diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 64dbf4b1cf2f..501e293e5ad0 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -107,7 +107,7 @@ static int __init hp_zx1_ioc_shared(void) hp->gart_size = HP_ZX1_GART_SIZE; hp->gatt_entries = hp->gart_size / hp->io_page_size; - hp->io_pdir = gart_to_virt(readq(hp->ioc_regs+HP_ZX1_PDIR_BASE)); + hp->io_pdir = phys_to_virt(readq(hp->ioc_regs+HP_ZX1_PDIR_BASE)); hp->gatt = &hp->io_pdir[HP_ZX1_IOVA_TO_PDIR(hp->gart_base)]; if (hp->gatt[0] != HP_ZX1_SBA_IOMMU_COOKIE) { @@ -246,7 +246,7 @@ hp_zx1_configure (void) agp_bridge->mode = readl(hp->lba_regs+hp->lba_cap_offset+PCI_AGP_STATUS); if (hp->io_pdir_owner) { - writel(virt_to_gart(hp->io_pdir), hp->ioc_regs+HP_ZX1_PDIR_BASE); + writel(virt_to_phys(hp->io_pdir), hp->ioc_regs+HP_ZX1_PDIR_BASE); readl(hp->ioc_regs+HP_ZX1_PDIR_BASE); writel(hp->io_tlb_ps, hp->ioc_regs+HP_ZX1_TCNFG); readl(hp->ioc_regs+HP_ZX1_TCNFG); diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index 54191f860539..e763d3312ce7 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -325,7 +325,7 @@ static int i460_insert_memory_small_io_page (struct agp_memory *mem, io_page_size = 1UL << I460_IO_PAGE_SHIFT; for (i = 0, j = io_pg_start; i < mem->page_count; i++) { - paddr = phys_to_gart(page_to_phys(mem->pages[i])); + paddr = page_to_phys(mem->pages[i]); for (k = 0; k < I460_IOPAGES_PER_KPAGE; k++, j++, paddr += io_page_size) WR_GATT(j, i460_mask_memory(agp_bridge, paddr, mem->type)); } @@ -382,7 +382,7 @@ static int i460_alloc_large_page (struct lp_desc *lp) return -ENOMEM; } - lp->paddr = phys_to_gart(page_to_phys(lp->page)); + lp->paddr = page_to_phys(lp->page); lp->refcount = 0; atomic_add(I460_KPAGES_PER_IOPAGE, &agp_bridge->current_memory_agp); return 0; diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index d8c80d8be5e2..aa8889e8afc8 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -288,7 +288,7 @@ static void intel_agp_insert_sg_entries(struct agp_memory *mem, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), mask_type), + page_to_phys(mem->pages[i]), mask_type), intel_private.gtt+j); } @@ -470,8 +470,7 @@ static int intel_i810_insert_entries(struct agp_memory *mem, off_t pg_start, global_cache_flush(); for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), - mask_type), + page_to_phys(mem->pages[i]), mask_type), intel_private.registers+I810_PTE_BASE+(j*4)); } readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); @@ -977,7 +976,7 @@ static int intel_i830_insert_entries(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), mask_type), + page_to_phys(mem->pages[i]), mask_type), intel_private.registers+I810_PTE_BASE+(j*4)); } readl(intel_private.registers+I810_PTE_BASE+((j-1)*4)); diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index cedacee30ec3..7e36d2b4f9d4 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -225,7 +225,7 @@ static int nvidia_insert_memory(struct agp_memory *mem, off_t pg_start, int type } for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), mask_type), + page_to_phys(mem->pages[i]), mask_type), agp_bridge->gatt_table+nvidia_private.pg_offset+j); } diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c index 0d47fa847404..0d426ae39c85 100644 --- a/drivers/char/agp/sgi-agp.c +++ b/drivers/char/agp/sgi-agp.c @@ -190,7 +190,7 @@ static int sgi_tioca_insert_memory(struct agp_memory *mem, off_t pg_start, for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { table[j] = bridge->driver->mask_memory(bridge, - phys_to_gart(page_to_phys(mem->pages[i])), + page_to_phys(mem->pages[i]), mem->type); } diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index 07259952fc32..13acaaf64edb 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -155,7 +155,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge) /* Create a fake scratch directory */ for (i = 0; i < 1024; i++) { writel(agp_bridge->scratch_page, serverworks_private.scratch_dir.remapped+i); - writel(virt_to_gart(serverworks_private.scratch_dir.real) | 1, page_dir.remapped+i); + writel(virt_to_phys(serverworks_private.scratch_dir.real) | 1, page_dir.remapped+i); } retval = serverworks_create_gatt_pages(value->num_entries / 1024); @@ -167,7 +167,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge) agp_bridge->gatt_table_real = (u32 *)page_dir.real; agp_bridge->gatt_table = (u32 __iomem *)page_dir.remapped; - agp_bridge->gatt_bus_addr = virt_to_gart(page_dir.real); + agp_bridge->gatt_bus_addr = virt_to_phys(page_dir.real); /* Get the address for the gart region. * This is a bus address even on the alpha, b/c its @@ -179,7 +179,7 @@ static int serverworks_create_gatt_table(struct agp_bridge_data *bridge) /* Calculate the agp offset */ for (i = 0; i < value->num_entries / 1024; i++) - writel(virt_to_gart(serverworks_private.gatt_pages[i]->real)|1, page_dir.remapped+i); + writel(virt_to_phys(serverworks_private.gatt_pages[i]->real)|1, page_dir.remapped+i); return 0; } @@ -350,7 +350,7 @@ static int serverworks_insert_memory(struct agp_memory *mem, addr = (j * PAGE_SIZE) + agp_bridge->gart_bus_addr; cur_gatt = SVRWRKS_GET_GATT(addr); writel(agp_bridge->driver->mask_memory(agp_bridge, - phys_to_gart(page_to_phys(mem->pages[i])), mem->type), + page_to_phys(mem->pages[i]), mem->type), cur_gatt+GET_GATT_OFF(addr)); } serverworks_tlbflush(mem); diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index f192c3b9ad41..2e993112ab88 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -431,7 +431,7 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) bridge->gatt_table_real = (u32 *) table; bridge->gatt_table = (u32 *)table; - bridge->gatt_bus_addr = virt_to_gart(table); + bridge->gatt_bus_addr = virt_to_phys(table); for (i = 0; i < num_entries; i++) bridge->gatt_table[i] = 0; -- cgit v1.2.3 From ba3139f2577eee24479db73b8dfc7d78eaf4c486 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 5 Aug 2009 08:12:40 +0100 Subject: intel-agp: Set dma mask for i915 If DMAR is configured in but absent, we really do want to make sure that the dma mask is set appropriately. Otherwise we get mapping failures on highmem. Spotted by Zhenyu Wang. Signed-off-by: David Woodhouse --- drivers/char/agp/intel-agp.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index aa8889e8afc8..9bc3a0b82b96 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -1140,6 +1140,12 @@ static int intel_i915_configure(void) intel_i9xx_setup_flush(); +#ifdef USE_PCI_DMA_API + if (pci_set_dma_mask(intel_private.pcidev, DMA_BIT_MASK(36))) + dev_err(&intel_private.pcidev->dev, + "set gfx device dma mask 36bit failed!\n"); +#endif + return 0; } -- cgit v1.2.3 From 5e8d6b8bf94f1ffcb7e3c31b73284c20f297f191 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 6 Aug 2009 20:20:43 +1000 Subject: agp: fix uninorth build Signed-off-by: Dave Airlie --- drivers/char/agp/uninorth-agp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 2e993112ab88..4317a5588daf 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -135,7 +135,7 @@ static int uninorth_configure(void) if (is_u3) { pci_write_config_dword(agp_bridge->dev, UNI_N_CFG_GART_DUMMY_PAGE, - agp_bridge->scratch_page_real >> 12); + page_to_phys(agp_bridge->scratch_page_page) >> 12); } return 0; -- cgit v1.2.3 From 5b36f1deefa63ef71cd3c3933781318ac61c5469 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 12 Jul 2009 10:03:42 +0000 Subject: hvc_console: Drop unnecessary NULL test The result of container_of should not be NULL. In particular, in this case the argument to the enclosing function has passed though INIT_WORK, which dereferences it, implying that its container cannot be NULL. A simplified version of the semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @@ identifier fn,work,x,fld; type T; expression E1,E2; statement S; @@ static fn(struct work_struct *work) { ... when != work = E1 x = container_of(work,T,fld) ... when != x = E2 - if (x == NULL) S ... } // Signed-off-by: Julia Lawall Signed-off-by: Benjamin Herrenschmidt --- drivers/char/hvc_console.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index d97779ef72cb..25ce15bb1c08 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -516,8 +516,6 @@ static void hvc_set_winsz(struct work_struct *work) struct winsize ws; hp = container_of(work, struct hvc_struct, tty_resize); - if (!hp) - return; spin_lock_irqsave(&hp->lock, hvc_flags); if (!hp->tty) { -- cgit v1.2.3 From 52f072cb084bbb460d3a4ae09f0b6efc3e7e8a8c Mon Sep 17 00:00:00 2001 From: Michel Dänzer Date: Tue, 4 Aug 2009 11:51:03 +0000 Subject: agp/uninorth: Allow larger aperture sizes on pre-U3 bridges. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the radeon KMS test functionality, I verified that the AGP bridge of the Intrepid2 chipset in my PowerBook supports aperture sizes up to 256M. So allow aperture sizes up to 256M on pre-U3 bridges as well, and bump the default size to 256M. It's possible that older revisions only support smaller sizes, but it'll be easy to verify that with the raden KMS test functionality. Also, there's only a problem on an actual attempt to access the aperture beyond the maximum size supported by the hardware, and non-KMS X still defaults to using only 32M. Also use ARRAY_SIZE for the aperture size arrays. Signed-off-by: Michel Dänzer Signed-off-by: Benjamin Herrenschmidt --- drivers/char/agp/uninorth-agp.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index f192c3b9ad41..37ff3bd56d60 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -27,6 +27,8 @@ static int uninorth_rev; static int is_u3; +#define DEFAULT_APERTURE_SIZE 256 +#define DEFAULT_APERTURE_STRING "256" static char *aperture = NULL; static int uninorth_fetch_size(void) @@ -55,7 +57,7 @@ static int uninorth_fetch_size(void) if (!size) { for (i = 0; i < agp_bridge->driver->num_aperture_sizes; i++) - if (values[i].size == 32) + if (values[i].size == DEFAULT_APERTURE_SIZE) break; } @@ -474,13 +476,11 @@ void null_cache_flush(void) /* Setup function */ -static const struct aper_size_info_32 uninorth_sizes[7] = +static const struct aper_size_info_32 uninorth_sizes[] = { -#if 0 /* Not sure uninorth supports that high aperture sizes */ {256, 65536, 6, 64}, {128, 32768, 5, 32}, {64, 16384, 4, 16}, -#endif {32, 8192, 3, 8}, {16, 4096, 2, 4}, {8, 2048, 1, 2}, @@ -491,7 +491,7 @@ static const struct aper_size_info_32 uninorth_sizes[7] = * Not sure that u3 supports that high aperture sizes but it * would strange if it did not :) */ -static const struct aper_size_info_32 u3_sizes[8] = +static const struct aper_size_info_32 u3_sizes[] = { {512, 131072, 7, 128}, {256, 65536, 6, 64}, @@ -507,7 +507,7 @@ const struct agp_bridge_driver uninorth_agp_driver = { .owner = THIS_MODULE, .aperture_sizes = (void *)uninorth_sizes, .size_type = U32_APER_SIZE, - .num_aperture_sizes = 4, + .num_aperture_sizes = ARRAY_SIZE(uninorth_sizes), .configure = uninorth_configure, .fetch_size = uninorth_fetch_size, .cleanup = uninorth_cleanup, @@ -534,7 +534,7 @@ const struct agp_bridge_driver u3_agp_driver = { .owner = THIS_MODULE, .aperture_sizes = (void *)u3_sizes, .size_type = U32_APER_SIZE, - .num_aperture_sizes = 8, + .num_aperture_sizes = ARRAY_SIZE(u3_sizes), .configure = uninorth_configure, .fetch_size = uninorth_fetch_size, .cleanup = uninorth_cleanup, @@ -717,7 +717,7 @@ module_param(aperture, charp, 0); MODULE_PARM_DESC(aperture, "Aperture size, must be power of two between 4MB and an\n" "\t\tupper limit specific to the UniNorth revision.\n" - "\t\tDefault: 32M"); + "\t\tDefault: " DEFAULT_APERTURE_STRING "M"); MODULE_AUTHOR("Ben Herrenschmidt & Paul Mackerras"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e8a5f900148d058bce2d7bdce3d6bcbcb40267ec Mon Sep 17 00:00:00 2001 From: Michel Dänzer Date: Tue, 4 Aug 2009 11:51:04 +0000 Subject: agp/uninorth: Simplify cache flushing. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Map the GART table uncached, so we don't always need to flush the CPU caches explicitly after updates. Signed-off-by: Michel Dänzer Signed-off-by: Benjamin Herrenschmidt --- drivers/char/agp/uninorth-agp.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 37ff3bd56d60..bba29abe917f 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -181,8 +182,6 @@ static int uninorth_insert_memory(struct agp_memory *mem, off_t pg_start, } (void)in_le32((volatile u32*)&agp_bridge->gatt_table[pg_start]); mb(); - flush_dcache_range((unsigned long)&agp_bridge->gatt_table[pg_start], - (unsigned long)&agp_bridge->gatt_table[pg_start + mem->page_count]); uninorth_tlbflush(mem); return 0; @@ -226,7 +225,6 @@ static int u3_insert_memory(struct agp_memory *mem, off_t pg_start, int type) (unsigned long)__va(page_to_phys(mem->pages[i]))+0x1000); } mb(); - flush_dcache_range((unsigned long)gp, (unsigned long) &gp[i]); uninorth_tlbflush(mem); return 0; @@ -245,7 +243,6 @@ int u3_remove_memory(struct agp_memory *mem, off_t pg_start, int type) for (i = 0; i < mem->page_count; ++i) gp[i] = 0; mb(); - flush_dcache_range((unsigned long)gp, (unsigned long) &gp[i]); uninorth_tlbflush(mem); return 0; @@ -398,6 +395,7 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) int i; void *temp; struct page *page; + struct page **pages; /* We can't handle 2 level gatt's */ if (bridge->driver->size_type == LVL2_APER_SIZE) @@ -426,21 +424,39 @@ static int uninorth_create_gatt_table(struct agp_bridge_data *bridge) if (table == NULL) return -ENOMEM; + pages = kmalloc((1 << page_order) * sizeof(struct page*), GFP_KERNEL); + if (pages == NULL) + goto enomem; + table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); - for (page = virt_to_page(table); page <= virt_to_page(table_end); page++) + for (page = virt_to_page(table), i = 0; page <= virt_to_page(table_end); + page++, i++) { SetPageReserved(page); + pages[i] = page; + } bridge->gatt_table_real = (u32 *) table; - bridge->gatt_table = (u32 *)table; + /* Need to clear out any dirty data still sitting in caches */ + flush_dcache_range((unsigned long)table, + (unsigned long)(table_end + PAGE_SIZE)); + bridge->gatt_table = vmap(pages, (1 << page_order), 0, PAGE_KERNEL_NCG); + + if (bridge->gatt_table == NULL) + goto enomem; + bridge->gatt_bus_addr = virt_to_gart(table); for (i = 0; i < num_entries; i++) bridge->gatt_table[i] = 0; - flush_dcache_range((unsigned long)table, (unsigned long)table_end); - return 0; + +enomem: + kfree(pages); + if (table) + free_pages((unsigned long)table, page_order); + return -ENOMEM; } static int uninorth_free_gatt_table(struct agp_bridge_data *bridge) @@ -458,6 +474,7 @@ static int uninorth_free_gatt_table(struct agp_bridge_data *bridge) * from the table. */ + vunmap(bridge->gatt_table); table = (char *) bridge->gatt_table_real; table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); -- cgit v1.2.3 From 728656506447b3b349d082a7fb99445f9cb0caaa Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Thu, 6 Aug 2009 13:00:37 +0000 Subject: powerpc/hvsi: Avoid calculating possibly-invalid address Check whether index is within bounds prior to calculating a possibly-invalid address. Signed-off-by: Roel Kluin Cc: Bernd Petrovitsch Cc: Benjamin Herrenschmidt Signed-off-by: Andrew Morton Signed-off-by: Benjamin Herrenschmidt --- drivers/char/hvsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c index 2989056a9e39..793b236c9266 100644 --- a/drivers/char/hvsi.c +++ b/drivers/char/hvsi.c @@ -1230,11 +1230,12 @@ static struct tty_driver *hvsi_console_device(struct console *console, static int __init hvsi_console_setup(struct console *console, char *options) { - struct hvsi_struct *hp = &hvsi_ports[console->index]; + struct hvsi_struct *hp; int ret; if (console->index < 0 || console->index >= hvsi_count) return -1; + hp = &hvsi_ports[console->index]; /* give the FSP a chance to change the baud rate when we re-open */ hvsi_close_protocol(hp); -- cgit v1.2.3 From 15b8dd53f5ffaf8e2d9095c423f713423f576c0f Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 29 Jun 2009 13:39:29 +0800 Subject: ACPICA: Major update for acpi_get_object_info external interface Completed a major update for the acpi_get_object_info external interface. Changes include: - Support for variable, unlimited length HID, UID, and CID strings - Support Processor objects the same as Devices (HID,UID,CID,ADR,STA, etc.) - Call the _SxW power methods on behalf of a device object - Determine if a device is a PCI root bridge - Change the ACPI_BUFFER parameter to ACPI_DEVICE_INFO. These changes will require an update to all callers of this interface. See the ACPICA Programmer Reference for details. Also, update all invocations of acpi_get_object_info interface Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- arch/ia64/hp/common/sba_iommu.c | 7 +- drivers/acpi/acpi_memhotplug.c | 11 +- drivers/acpi/acpica/Makefile | 2 +- drivers/acpi/acpica/acconfig.h | 5 + drivers/acpi/acpica/acglobal.h | 3 +- drivers/acpi/acpica/acinterp.h | 4 +- drivers/acpi/acpica/acutils.h | 24 ++- drivers/acpi/acpica/evrgnini.c | 45 +---- drivers/acpi/acpica/exutils.c | 53 +++-- drivers/acpi/acpica/nsdumpdv.c | 7 +- drivers/acpi/acpica/nsxfeval.c | 23 ++- drivers/acpi/acpica/nsxfname.c | 237 +++++++++++++++++------ drivers/acpi/acpica/uteval.c | 375 ++++-------------------------------- drivers/acpi/acpica/utglobal.c | 10 +- drivers/acpi/acpica/utids.c | 382 +++++++++++++++++++++++++++++++++++++ drivers/acpi/acpica/utmisc.c | 28 +++ drivers/acpi/container.c | 11 +- drivers/acpi/dock.c | 8 +- drivers/acpi/glue.c | 6 +- drivers/acpi/scan.c | 153 +++++++++------ drivers/char/agp/hp-agp.c | 9 +- drivers/ide/ide-acpi.c | 5 +- drivers/pci/hotplug/acpiphp_ibm.c | 12 +- drivers/platform/x86/sony-laptop.c | 7 +- drivers/pnp/pnpacpi/core.c | 6 +- include/acpi/acpi_bus.h | 8 +- include/acpi/acpixf.h | 3 +- include/acpi/actypes.h | 87 +++++---- 28 files changed, 901 insertions(+), 630 deletions(-) create mode 100644 drivers/acpi/acpica/utids.c (limited to 'drivers/char') diff --git a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c index 8cfb001092ab..674a8374c6d9 100644 --- a/arch/ia64/hp/common/sba_iommu.c +++ b/arch/ia64/hp/common/sba_iommu.c @@ -2026,24 +2026,21 @@ acpi_sba_ioc_add(struct acpi_device *device) struct ioc *ioc; acpi_status status; u64 hpa, length; - struct acpi_buffer buffer; struct acpi_device_info *dev_info; status = hp_acpi_csr_space(device->handle, &hpa, &length); if (ACPI_FAILURE(status)) return 1; - buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; - status = acpi_get_object_info(device->handle, &buffer); + status = acpi_get_object_info(device->handle, &dev_info); if (ACPI_FAILURE(status)) return 1; - dev_info = buffer.pointer; /* * For HWP0001, only SBA appears in ACPI namespace. It encloses the PCI * root bridges, and its CSR space includes the IOC function. */ - if (strncmp("HWP0001", dev_info->hardware_id.value, 7) == 0) { + if (strncmp("HWP0001", dev_info->hardware_id.string, 7) == 0) { hpa += ZX1_IOC_OFFSET; /* zx1 based systems default to kernel page size iommu pages */ if (!iovp_shift) diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 9a62224cc278..80eacbe157e2 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -481,26 +481,23 @@ static acpi_status is_memory_device(acpi_handle handle) { char *hardware_id; acpi_status status; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_device_info *info; - - status = acpi_get_object_info(handle, &buffer); + status = acpi_get_object_info(handle, &info); if (ACPI_FAILURE(status)) return status; - info = buffer.pointer; if (!(info->valid & ACPI_VALID_HID)) { - kfree(buffer.pointer); + kfree(info); return AE_ERROR; } - hardware_id = info->hardware_id.value; + hardware_id = info->hardware_id.string; if ((hardware_id == NULL) || (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID))) status = AE_ERROR; - kfree(buffer.pointer); + kfree(info); return status; } diff --git a/drivers/acpi/acpica/Makefile b/drivers/acpi/acpica/Makefile index 72ac28da14e3..0e7d56185f6d 100644 --- a/drivers/acpi/acpica/Makefile +++ b/drivers/acpi/acpica/Makefile @@ -44,4 +44,4 @@ acpi-y += tbxface.o tbinstal.o tbutils.o tbfind.o tbfadt.o tbxfroot.o acpi-y += utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \ utcopy.o utdelete.o utglobal.o utmath.o utobject.o \ - utstate.o utmutex.o utobject.o utresrc.o utlock.o + utstate.o utmutex.o utobject.o utresrc.o utlock.o utids.o diff --git a/drivers/acpi/acpica/acconfig.h b/drivers/acpi/acpica/acconfig.h index e6777fb883d2..6c1fb2d9f4d5 100644 --- a/drivers/acpi/acpica/acconfig.h +++ b/drivers/acpi/acpica/acconfig.h @@ -203,6 +203,11 @@ #define ACPI_SMBUS_BUFFER_SIZE 34 +/* _sx_d and _sx_w control methods */ + +#define ACPI_NUM_sx_d_METHODS 4 +#define ACPI_NUM_sx_w_METHODS 5 + /****************************************************************************** * * ACPI AML Debugger diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 0b73b31c1b53..6389f7c1de59 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -265,7 +265,8 @@ ACPI_EXTERN u8 acpi_gbl_osi_data; extern u8 acpi_gbl_shutdown; extern u32 acpi_gbl_startup_flags; extern const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT]; -extern const char *acpi_gbl_highest_dstate_names[4]; +extern const char *acpi_gbl_lowest_dstate_names[ACPI_NUM_sx_w_METHODS]; +extern const char *acpi_gbl_highest_dstate_names[ACPI_NUM_sx_d_METHODS]; extern const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES]; extern const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS]; diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index e8db7a3143a5..5db9f2916f7c 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -461,9 +461,9 @@ void acpi_ex_acquire_global_lock(u32 rule); void acpi_ex_release_global_lock(u32 rule); -void acpi_ex_eisa_id_to_string(u32 numeric_id, char *out_string); +void acpi_ex_eisa_id_to_string(char *dest, acpi_integer compressed_id); -void acpi_ex_unsigned_integer_to_string(acpi_integer value, char *out_string); +void acpi_ex_integer_to_string(char *dest, acpi_integer value); /* * exregion - default op_region handlers diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 897810ba0ccc..b0add85de308 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -324,26 +324,30 @@ acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, acpi_status acpi_ut_evaluate_numeric_object(char *object_name, struct acpi_namespace_node *device_node, - acpi_integer * address); + acpi_integer *value); acpi_status -acpi_ut_execute_HID(struct acpi_namespace_node *device_node, - struct acpica_device_id *hid); +acpi_ut_execute_STA(struct acpi_namespace_node *device_node, u32 *status_flags); acpi_status -acpi_ut_execute_CID(struct acpi_namespace_node *device_node, - struct acpi_compatible_id_list **return_cid_list); +acpi_ut_execute_power_methods(struct acpi_namespace_node *device_node, + const char **method_names, + u8 method_count, u8 *out_values); +/* + * utids - device ID support + */ acpi_status -acpi_ut_execute_STA(struct acpi_namespace_node *device_node, - u32 * status_flags); +acpi_ut_execute_HID(struct acpi_namespace_node *device_node, + struct acpica_device_id **return_id); acpi_status acpi_ut_execute_UID(struct acpi_namespace_node *device_node, - struct acpica_device_id *uid); + struct acpica_device_id **return_id); acpi_status -acpi_ut_execute_sxds(struct acpi_namespace_node *device_node, u8 * highest); +acpi_ut_execute_CID(struct acpi_namespace_node *device_node, + struct acpica_device_id_list **return_cid_list); /* * utlock - reader/writer locks @@ -445,6 +449,8 @@ acpi_ut_short_divide(acpi_integer in_dividend, */ const char *acpi_ut_validate_exception(acpi_status status); +u8 acpi_ut_is_pci_root_bridge(char *id); + u8 acpi_ut_is_aml_table(struct acpi_table_header *table); acpi_status acpi_ut_allocate_owner_id(acpi_owner_id * owner_id); diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index 284a7becbe96..cf29c4953028 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -50,8 +50,6 @@ ACPI_MODULE_NAME("evrgnini") /* Local prototypes */ -static u8 acpi_ev_match_pci_root_bridge(char *id); - static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node); /******************************************************************************* @@ -330,37 +328,6 @@ acpi_ev_pci_config_region_setup(acpi_handle handle, return_ACPI_STATUS(AE_OK); } -/******************************************************************************* - * - * FUNCTION: acpi_ev_match_pci_root_bridge - * - * PARAMETERS: Id - The HID/CID in string format - * - * RETURN: TRUE if the Id is a match for a PCI/PCI-Express Root Bridge - * - * DESCRIPTION: Determine if the input ID is a PCI Root Bridge ID. - * - ******************************************************************************/ - -static u8 acpi_ev_match_pci_root_bridge(char *id) -{ - - /* - * Check if this is a PCI root. - * ACPI 3.0+: check for a PCI Express root also. - */ - if (!(ACPI_STRNCMP(id, - PCI_ROOT_HID_STRING, - sizeof(PCI_ROOT_HID_STRING))) || - !(ACPI_STRNCMP(id, - PCI_EXPRESS_ROOT_HID_STRING, - sizeof(PCI_EXPRESS_ROOT_HID_STRING)))) { - return (TRUE); - } - - return (FALSE); -} - /******************************************************************************* * * FUNCTION: acpi_ev_is_pci_root_bridge @@ -377,9 +344,10 @@ static u8 acpi_ev_match_pci_root_bridge(char *id) static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node) { acpi_status status; - struct acpica_device_id hid; - struct acpi_compatible_id_list *cid; + struct acpica_device_id *hid; + struct acpica_device_id_list *cid; u32 i; + u8 match; /* Get the _HID and check for a PCI Root Bridge */ @@ -388,7 +356,10 @@ static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node) return (FALSE); } - if (acpi_ev_match_pci_root_bridge(hid.value)) { + match = acpi_ut_is_pci_root_bridge(hid->string); + ACPI_FREE(hid); + + if (match) { return (TRUE); } @@ -402,7 +373,7 @@ static u8 acpi_ev_is_pci_root_bridge(struct acpi_namespace_node *node) /* Check all _CIDs in the returned list */ for (i = 0; i < cid->count; i++) { - if (acpi_ev_match_pci_root_bridge(cid->id[i].value)) { + if (acpi_ut_is_pci_root_bridge(cid->ids[i].string)) { ACPI_FREE(cid); return (TRUE); } diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c index 87730e944132..7d41f99f7052 100644 --- a/drivers/acpi/acpica/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -358,50 +358,67 @@ static u32 acpi_ex_digits_needed(acpi_integer value, u32 base) * * FUNCTION: acpi_ex_eisa_id_to_string * - * PARAMETERS: numeric_id - EISA ID to be converted + * PARAMETERS: compressed_id - EISAID to be converted * out_string - Where to put the converted string (8 bytes) * * RETURN: None * - * DESCRIPTION: Convert a numeric EISA ID to string representation + * DESCRIPTION: Convert a numeric EISAID to string representation. Return + * buffer must be large enough to hold the string. The string + * returned is always exactly of length ACPI_EISAID_STRING_SIZE + * (includes null terminator). The EISAID is always 32 bits. * ******************************************************************************/ -void acpi_ex_eisa_id_to_string(u32 numeric_id, char *out_string) +void acpi_ex_eisa_id_to_string(char *out_string, acpi_integer compressed_id) { - u32 eisa_id; + u32 swapped_id; ACPI_FUNCTION_ENTRY(); + /* The EISAID should be a 32-bit integer */ + + if (compressed_id > ACPI_UINT32_MAX) { + ACPI_WARNING((AE_INFO, + "Expected EISAID is larger than 32 bits: 0x%8.8X%8.8X, truncating", + ACPI_FORMAT_UINT64(compressed_id))); + } + /* Swap ID to big-endian to get contiguous bits */ - eisa_id = acpi_ut_dword_byte_swap(numeric_id); + swapped_id = acpi_ut_dword_byte_swap((u32)compressed_id); - out_string[0] = (char)('@' + (((unsigned long)eisa_id >> 26) & 0x1f)); - out_string[1] = (char)('@' + ((eisa_id >> 21) & 0x1f)); - out_string[2] = (char)('@' + ((eisa_id >> 16) & 0x1f)); - out_string[3] = acpi_ut_hex_to_ascii_char((acpi_integer) eisa_id, 12); - out_string[4] = acpi_ut_hex_to_ascii_char((acpi_integer) eisa_id, 8); - out_string[5] = acpi_ut_hex_to_ascii_char((acpi_integer) eisa_id, 4); - out_string[6] = acpi_ut_hex_to_ascii_char((acpi_integer) eisa_id, 0); + /* First 3 bytes are uppercase letters. Next 4 bytes are hexadecimal */ + + out_string[0] = + (char)(0x40 + (((unsigned long)swapped_id >> 26) & 0x1F)); + out_string[1] = (char)(0x40 + ((swapped_id >> 21) & 0x1F)); + out_string[2] = (char)(0x40 + ((swapped_id >> 16) & 0x1F)); + out_string[3] = acpi_ut_hex_to_ascii_char((acpi_integer)swapped_id, 12); + out_string[4] = acpi_ut_hex_to_ascii_char((acpi_integer)swapped_id, 8); + out_string[5] = acpi_ut_hex_to_ascii_char((acpi_integer)swapped_id, 4); + out_string[6] = acpi_ut_hex_to_ascii_char((acpi_integer)swapped_id, 0); out_string[7] = 0; } /******************************************************************************* * - * FUNCTION: acpi_ex_unsigned_integer_to_string + * FUNCTION: acpi_ex_integer_to_string * - * PARAMETERS: Value - Value to be converted - * out_string - Where to put the converted string (8 bytes) + * PARAMETERS: out_string - Where to put the converted string. At least + * 21 bytes are needed to hold the largest + * possible 64-bit integer. + * Value - Value to be converted * * RETURN: None, string * - * DESCRIPTION: Convert a number to string representation. Assumes string - * buffer is large enough to hold the string. + * DESCRIPTION: Convert a 64-bit integer to decimal string representation. + * Assumes string buffer is large enough to hold the string. The + * largest string is (ACPI_MAX64_DECIMAL_DIGITS + 1). * ******************************************************************************/ -void acpi_ex_unsigned_integer_to_string(acpi_integer value, char *out_string) +void acpi_ex_integer_to_string(char *out_string, acpi_integer value) { u32 count; u32 digits_needed; diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c index 41994fe7fbb8..0fe87f1aef16 100644 --- a/drivers/acpi/acpica/nsdumpdv.c +++ b/drivers/acpi/acpica/nsdumpdv.c @@ -70,7 +70,6 @@ static acpi_status acpi_ns_dump_one_device(acpi_handle obj_handle, u32 level, void *context, void **return_value) { - struct acpi_buffer buffer; struct acpi_device_info *info; acpi_status status; u32 i; @@ -80,17 +79,15 @@ acpi_ns_dump_one_device(acpi_handle obj_handle, status = acpi_ns_dump_one_object(obj_handle, level, context, return_value); - buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; - status = acpi_get_object_info(obj_handle, &buffer); + status = acpi_get_object_info(obj_handle, &info); if (ACPI_SUCCESS(status)) { - info = buffer.pointer; for (i = 0; i < level; i++) { ACPI_DEBUG_PRINT_RAW((ACPI_DB_TABLES, " ")); } ACPI_DEBUG_PRINT_RAW((ACPI_DB_TABLES, " HID: %s, ADR: %8.8X%8.8X, Status: %X\n", - info->hardware_id.value, + info->hardware_id.string, ACPI_FORMAT_UINT64(info->address), info->current_status)); ACPI_FREE(info); diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index daf4ad37896d..4929dbdbc8f0 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -535,10 +535,11 @@ acpi_ns_get_device_callback(acpi_handle obj_handle, acpi_status status; struct acpi_namespace_node *node; u32 flags; - struct acpica_device_id hid; - struct acpi_compatible_id_list *cid; + struct acpica_device_id *hid; + struct acpica_device_id_list *cid; u32 i; - int found; + u8 found; + int no_match; status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { @@ -582,10 +583,14 @@ acpi_ns_get_device_callback(acpi_handle obj_handle, return (AE_CTRL_DEPTH); } - if (ACPI_STRNCMP(hid.value, info->hid, sizeof(hid.value)) != 0) { - - /* Get the list of Compatible IDs */ + no_match = ACPI_STRCMP(hid->string, info->hid); + ACPI_FREE(hid); + if (no_match) { + /* + * HID does not match, attempt match within the + * list of Compatible IDs (CIDs) + */ status = acpi_ut_execute_CID(node, &cid); if (status == AE_NOT_FOUND) { return (AE_OK); @@ -597,10 +602,8 @@ acpi_ns_get_device_callback(acpi_handle obj_handle, found = 0; for (i = 0; i < cid->count; i++) { - if (ACPI_STRNCMP(cid->id[i].value, info->hid, - sizeof(struct - acpi_compatible_id)) == - 0) { + if (ACPI_STRCMP(cid->ids[i].string, info->hid) + == 0) { found = 1; break; } diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index f23593d6add4..ddc84af6336e 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -51,6 +51,11 @@ #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsxfname") +/* Local prototypes */ +static char *acpi_ns_copy_device_id(struct acpica_device_id *dest, + struct acpica_device_id *source, + char *string_area); + /****************************************************************************** * * FUNCTION: acpi_get_handle @@ -68,6 +73,7 @@ ACPI_MODULE_NAME("nsxfname") * namespace handle. * ******************************************************************************/ + acpi_status acpi_get_handle(acpi_handle parent, acpi_string pathname, acpi_handle * ret_handle) @@ -208,12 +214,40 @@ acpi_get_name(acpi_handle handle, u32 name_type, struct acpi_buffer * buffer) ACPI_EXPORT_SYMBOL(acpi_get_name) +/****************************************************************************** + * + * FUNCTION: acpi_ns_copy_device_id + * + * PARAMETERS: Dest - Pointer to the destination DEVICE_ID + * Source - Pointer to the source DEVICE_ID + * string_area - Pointer to where to copy the dest string + * + * RETURN: Pointer to the next string area + * + * DESCRIPTION: Copy a single DEVICE_ID, including the string data. + * + ******************************************************************************/ +static char *acpi_ns_copy_device_id(struct acpica_device_id *dest, + struct acpica_device_id *source, + char *string_area) +{ + /* Create the destination DEVICE_ID */ + + dest->string = string_area; + dest->length = source->length; + + /* Copy actual string and return a pointer to the next string area */ + + ACPI_MEMCPY(string_area, source->string, source->length); + return (string_area + source->length); +} + /****************************************************************************** * * FUNCTION: acpi_get_object_info * - * PARAMETERS: Handle - Object Handle - * Buffer - Where the info is returned + * PARAMETERS: Handle - Object Handle + * return_buffer - Where the info is returned * * RETURN: Status * @@ -221,33 +255,37 @@ ACPI_EXPORT_SYMBOL(acpi_get_name) * namespace node and possibly by running several standard * control methods (Such as in the case of a device.) * + * For Device and Processor objects, run the Device _HID, _UID, _CID, _STA, + * _ADR, _sx_w, and _sx_d methods. + * + * Note: Allocates the return buffer, must be freed by the caller. + * ******************************************************************************/ + acpi_status -acpi_get_object_info(acpi_handle handle, struct acpi_buffer * buffer) +acpi_get_object_info(acpi_handle handle, + struct acpi_device_info **return_buffer) { - acpi_status status; struct acpi_namespace_node *node; struct acpi_device_info *info; - struct acpi_device_info *return_info; - struct acpi_compatible_id_list *cid_list = NULL; - acpi_size size; + struct acpica_device_id_list *cid_list = NULL; + struct acpica_device_id *hid = NULL; + struct acpica_device_id *uid = NULL; + char *next_id_string; + acpi_object_type type; + acpi_name name; + u8 param_count = 0; + u8 valid = 0; + u32 info_size; + u32 i; + acpi_status status; /* Parameter validation */ - if (!handle || !buffer) { + if (!handle || !return_buffer) { return (AE_BAD_PARAMETER); } - status = acpi_ut_validate_buffer(buffer); - if (ACPI_FAILURE(status)) { - return (status); - } - - info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_device_info)); - if (!info) { - return (AE_NO_MEMORY); - } - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { goto cleanup; @@ -256,66 +294,91 @@ acpi_get_object_info(acpi_handle handle, struct acpi_buffer * buffer) node = acpi_ns_map_handle_to_node(handle); if (!node) { (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - status = AE_BAD_PARAMETER; - goto cleanup; + return (AE_BAD_PARAMETER); } - /* Init return structure */ - - size = sizeof(struct acpi_device_info); + /* Get the namespace node data while the namespace is locked */ - info->type = node->type; - info->name = node->name.integer; - info->valid = 0; + info_size = sizeof(struct acpi_device_info); + type = node->type; + name = node->name.integer; if (node->type == ACPI_TYPE_METHOD) { - info->param_count = node->object->method.param_count; + param_count = node->object->method.param_count; } status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); if (ACPI_FAILURE(status)) { - goto cleanup; + return (status); } - /* If not a device, we are all done */ - - if (info->type == ACPI_TYPE_DEVICE) { + if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) { /* - * Get extra info for ACPI Devices objects only: - * Run the Device _HID, _UID, _CID, _STA, _ADR and _sx_d methods. + * Get extra info for ACPI Device/Processor objects only: + * Run the Device _HID, _UID, and _CID methods. * * Note: none of these methods are required, so they may or may - * not be present for this device. The Info->Valid bitfield is used - * to indicate which methods were found and ran successfully. + * not be present for this device. The Info->Valid bitfield is used + * to indicate which methods were found and run successfully. */ /* Execute the Device._HID method */ - status = acpi_ut_execute_HID(node, &info->hardware_id); + status = acpi_ut_execute_HID(node, &hid); if (ACPI_SUCCESS(status)) { - info->valid |= ACPI_VALID_HID; + info_size += hid->length; + valid |= ACPI_VALID_HID; } /* Execute the Device._UID method */ - status = acpi_ut_execute_UID(node, &info->unique_id); + status = acpi_ut_execute_UID(node, &uid); if (ACPI_SUCCESS(status)) { - info->valid |= ACPI_VALID_UID; + info_size += uid->length; + valid |= ACPI_VALID_UID; } /* Execute the Device._CID method */ status = acpi_ut_execute_CID(node, &cid_list); if (ACPI_SUCCESS(status)) { - size += cid_list->size; - info->valid |= ACPI_VALID_CID; + + /* Add size of CID strings and CID pointer array */ + + info_size += + (cid_list->list_size - + sizeof(struct acpica_device_id_list)); + valid |= ACPI_VALID_CID; } + } + + /* + * Now that we have the variable-length data, we can allocate the + * return buffer + */ + info = ACPI_ALLOCATE_ZEROED(info_size); + if (!info) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Get the fixed-length data */ + + if ((type == ACPI_TYPE_DEVICE) || (type == ACPI_TYPE_PROCESSOR)) { + /* + * Get extra info for ACPI Device/Processor objects only: + * Run the _STA, _ADR and, sx_w, and _sx_d methods. + * + * Note: none of these methods are required, so they may or may + * not be present for this device. The Info->Valid bitfield is used + * to indicate which methods were found and run successfully. + */ /* Execute the Device._STA method */ status = acpi_ut_execute_STA(node, &info->current_status); if (ACPI_SUCCESS(status)) { - info->valid |= ACPI_VALID_STA; + valid |= ACPI_VALID_STA; } /* Execute the Device._ADR method */ @@ -323,36 +386,100 @@ acpi_get_object_info(acpi_handle handle, struct acpi_buffer * buffer) status = acpi_ut_evaluate_numeric_object(METHOD_NAME__ADR, node, &info->address); if (ACPI_SUCCESS(status)) { - info->valid |= ACPI_VALID_ADR; + valid |= ACPI_VALID_ADR; + } + + /* Execute the Device._sx_w methods */ + + status = acpi_ut_execute_power_methods(node, + acpi_gbl_lowest_dstate_names, + ACPI_NUM_sx_w_METHODS, + info->lowest_dstates); + if (ACPI_SUCCESS(status)) { + valid |= ACPI_VALID_SXWS; } /* Execute the Device._sx_d methods */ - status = acpi_ut_execute_sxds(node, info->highest_dstates); + status = acpi_ut_execute_power_methods(node, + acpi_gbl_highest_dstate_names, + ACPI_NUM_sx_d_METHODS, + info->highest_dstates); if (ACPI_SUCCESS(status)) { - info->valid |= ACPI_VALID_SXDS; + valid |= ACPI_VALID_SXDS; } } - /* Validate/Allocate/Clear caller buffer */ + /* + * Create a pointer to the string area of the return buffer. + * Point to the end of the base struct acpi_device_info structure. + */ + next_id_string = ACPI_CAST_PTR(char, info->compatible_id_list.ids); + if (cid_list) { - status = acpi_ut_initialize_buffer(buffer, size); - if (ACPI_FAILURE(status)) { - goto cleanup; + /* Point past the CID DEVICE_ID array */ + + next_id_string += + ((acpi_size) cid_list->count * + sizeof(struct acpica_device_id)); } - /* Populate the return buffer */ + /* + * Copy the HID, UID, and CIDs to the return buffer. The variable-length + * strings are copied to the reserved area at the end of the buffer. + * + * For HID and CID, check if the ID is a PCI Root Bridge. + */ + if (hid) { + next_id_string = acpi_ns_copy_device_id(&info->hardware_id, + hid, next_id_string); + + if (acpi_ut_is_pci_root_bridge(hid->string)) { + info->flags |= ACPI_PCI_ROOT_BRIDGE; + } + } - return_info = buffer->pointer; - ACPI_MEMCPY(return_info, info, sizeof(struct acpi_device_info)); + if (uid) { + next_id_string = acpi_ns_copy_device_id(&info->unique_id, + uid, next_id_string); + } if (cid_list) { - ACPI_MEMCPY(&return_info->compatibility_id, cid_list, - cid_list->size); + info->compatible_id_list.count = cid_list->count; + info->compatible_id_list.list_size = cid_list->list_size; + + /* Copy each CID */ + + for (i = 0; i < cid_list->count; i++) { + next_id_string = + acpi_ns_copy_device_id(&info->compatible_id_list. + ids[i], &cid_list->ids[i], + next_id_string); + + if (acpi_ut_is_pci_root_bridge(cid_list->ids[i].string)) { + info->flags |= ACPI_PCI_ROOT_BRIDGE; + } + } } + /* Copy the fixed-length data */ + + info->info_size = info_size; + info->type = type; + info->name = name; + info->param_count = param_count; + info->valid = valid; + + *return_buffer = info; + status = AE_OK; + cleanup: - ACPI_FREE(info); + if (hid) { + ACPI_FREE(hid); + } + if (uid) { + ACPI_FREE(uid); + } if (cid_list) { ACPI_FREE(cid_list); } diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c index 006b16c26017..5503307b8bb7 100644 --- a/drivers/acpi/acpica/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -44,19 +44,10 @@ #include #include "accommon.h" #include "acnamesp.h" -#include "acinterp.h" #define _COMPONENT ACPI_UTILITIES ACPI_MODULE_NAME("uteval") -/* Local prototypes */ -static void -acpi_ut_copy_id_string(char *destination, char *source, acpi_size max_length); - -static acpi_status -acpi_ut_translate_one_cid(union acpi_operand_object *obj_desc, - struct acpi_compatible_id *one_cid); - /* * Strings supported by the _OSI predefined (internal) method. * @@ -213,7 +204,7 @@ acpi_status acpi_osi_invalidate(char *interface) * RETURN: Status * * DESCRIPTION: Evaluates a namespace object and verifies the type of the - * return object. Common code that simplifies accessing objects + * return object. Common code that simplifies accessing objects * that have required return objects of fixed types. * * NOTE: Internal function, no parameter validation @@ -298,7 +289,7 @@ acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, if ((acpi_gbl_enable_interpreter_slack) && (!expected_return_btypes)) { /* - * We received a return object, but one was not expected. This can + * We received a return object, but one was not expected. This can * happen frequently if the "implicit return" feature is enabled. * Just delete the return object and return AE_OK. */ @@ -340,12 +331,12 @@ acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, * * PARAMETERS: object_name - Object name to be evaluated * device_node - Node for the device - * Address - Where the value is returned + * Value - Where the value is returned * * RETURN: Status * * DESCRIPTION: Evaluates a numeric namespace object for a selected device - * and stores result in *Address. + * and stores result in *Value. * * NOTE: Internal function, no parameter validation * @@ -354,7 +345,7 @@ acpi_ut_evaluate_object(struct acpi_namespace_node *prefix_node, acpi_status acpi_ut_evaluate_numeric_object(char *object_name, struct acpi_namespace_node *device_node, - acpi_integer * address) + acpi_integer *value) { union acpi_operand_object *obj_desc; acpi_status status; @@ -369,295 +360,7 @@ acpi_ut_evaluate_numeric_object(char *object_name, /* Get the returned Integer */ - *address = obj_desc->integer.value; - - /* On exit, we must delete the return object */ - - acpi_ut_remove_reference(obj_desc); - return_ACPI_STATUS(status); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_copy_id_string - * - * PARAMETERS: Destination - Where to copy the string - * Source - Source string - * max_length - Length of the destination buffer - * - * RETURN: None - * - * DESCRIPTION: Copies an ID string for the _HID, _CID, and _UID methods. - * Performs removal of a leading asterisk if present -- workaround - * for a known issue on a bunch of machines. - * - ******************************************************************************/ - -static void -acpi_ut_copy_id_string(char *destination, char *source, acpi_size max_length) -{ - - /* - * Workaround for ID strings that have a leading asterisk. This construct - * is not allowed by the ACPI specification (ID strings must be - * alphanumeric), but enough existing machines have this embedded in their - * ID strings that the following code is useful. - */ - if (*source == '*') { - source++; - } - - /* Do the actual copy */ - - ACPI_STRNCPY(destination, source, max_length); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_execute_HID - * - * PARAMETERS: device_node - Node for the device - * Hid - Where the HID is returned - * - * RETURN: Status - * - * DESCRIPTION: Executes the _HID control method that returns the hardware - * ID of the device. - * - * NOTE: Internal function, no parameter validation - * - ******************************************************************************/ - -acpi_status -acpi_ut_execute_HID(struct acpi_namespace_node *device_node, - struct acpica_device_id *hid) -{ - union acpi_operand_object *obj_desc; - acpi_status status; - - ACPI_FUNCTION_TRACE(ut_execute_HID); - - status = acpi_ut_evaluate_object(device_node, METHOD_NAME__HID, - ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, - &obj_desc); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - if (obj_desc->common.type == ACPI_TYPE_INTEGER) { - - /* Convert the Numeric HID to string */ - - acpi_ex_eisa_id_to_string((u32) obj_desc->integer.value, - hid->value); - } else { - /* Copy the String HID from the returned object */ - - acpi_ut_copy_id_string(hid->value, obj_desc->string.pointer, - sizeof(hid->value)); - } - - /* On exit, we must delete the return object */ - - acpi_ut_remove_reference(obj_desc); - return_ACPI_STATUS(status); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_translate_one_cid - * - * PARAMETERS: obj_desc - _CID object, must be integer or string - * one_cid - Where the CID string is returned - * - * RETURN: Status - * - * DESCRIPTION: Return a numeric or string _CID value as a string. - * (Compatible ID) - * - * NOTE: Assumes a maximum _CID string length of - * ACPI_MAX_CID_LENGTH. - * - ******************************************************************************/ - -static acpi_status -acpi_ut_translate_one_cid(union acpi_operand_object *obj_desc, - struct acpi_compatible_id *one_cid) -{ - - switch (obj_desc->common.type) { - case ACPI_TYPE_INTEGER: - - /* Convert the Numeric CID to string */ - - acpi_ex_eisa_id_to_string((u32) obj_desc->integer.value, - one_cid->value); - return (AE_OK); - - case ACPI_TYPE_STRING: - - if (obj_desc->string.length > ACPI_MAX_CID_LENGTH) { - return (AE_AML_STRING_LIMIT); - } - - /* Copy the String CID from the returned object */ - - acpi_ut_copy_id_string(one_cid->value, obj_desc->string.pointer, - ACPI_MAX_CID_LENGTH); - return (AE_OK); - - default: - - return (AE_TYPE); - } -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_execute_CID - * - * PARAMETERS: device_node - Node for the device - * return_cid_list - Where the CID list is returned - * - * RETURN: Status - * - * DESCRIPTION: Executes the _CID control method that returns one or more - * compatible hardware IDs for the device. - * - * NOTE: Internal function, no parameter validation - * - ******************************************************************************/ - -acpi_status -acpi_ut_execute_CID(struct acpi_namespace_node * device_node, - struct acpi_compatible_id_list ** return_cid_list) -{ - union acpi_operand_object *obj_desc; - acpi_status status; - u32 count; - u32 size; - struct acpi_compatible_id_list *cid_list; - u32 i; - - ACPI_FUNCTION_TRACE(ut_execute_CID); - - /* Evaluate the _CID method for this device */ - - status = acpi_ut_evaluate_object(device_node, METHOD_NAME__CID, - ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING - | ACPI_BTYPE_PACKAGE, &obj_desc); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - /* Get the number of _CIDs returned */ - - count = 1; - if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { - count = obj_desc->package.count; - } - - /* Allocate a worst-case buffer for the _CIDs */ - - size = (((count - 1) * sizeof(struct acpi_compatible_id)) + - sizeof(struct acpi_compatible_id_list)); - - cid_list = ACPI_ALLOCATE_ZEROED((acpi_size) size); - if (!cid_list) { - return_ACPI_STATUS(AE_NO_MEMORY); - } - - /* Init CID list */ - - cid_list->count = count; - cid_list->size = size; - - /* - * A _CID can return either a single compatible ID or a package of - * compatible IDs. Each compatible ID can be one of the following: - * 1) Integer (32 bit compressed EISA ID) or - * 2) String (PCI ID format, e.g. "PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss") - */ - - /* The _CID object can be either a single CID or a package (list) of CIDs */ - - if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { - - /* Translate each package element */ - - for (i = 0; i < count; i++) { - status = - acpi_ut_translate_one_cid(obj_desc->package. - elements[i], - &cid_list->id[i]); - if (ACPI_FAILURE(status)) { - break; - } - } - } else { - /* Only one CID, translate to a string */ - - status = acpi_ut_translate_one_cid(obj_desc, cid_list->id); - } - - /* Cleanup on error */ - - if (ACPI_FAILURE(status)) { - ACPI_FREE(cid_list); - } else { - *return_cid_list = cid_list; - } - - /* On exit, we must delete the _CID return object */ - - acpi_ut_remove_reference(obj_desc); - return_ACPI_STATUS(status); -} - -/******************************************************************************* - * - * FUNCTION: acpi_ut_execute_UID - * - * PARAMETERS: device_node - Node for the device - * Uid - Where the UID is returned - * - * RETURN: Status - * - * DESCRIPTION: Executes the _UID control method that returns the hardware - * ID of the device. - * - * NOTE: Internal function, no parameter validation - * - ******************************************************************************/ - -acpi_status -acpi_ut_execute_UID(struct acpi_namespace_node *device_node, - struct acpica_device_id *uid) -{ - union acpi_operand_object *obj_desc; - acpi_status status; - - ACPI_FUNCTION_TRACE(ut_execute_UID); - - status = acpi_ut_evaluate_object(device_node, METHOD_NAME__UID, - ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, - &obj_desc); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - if (obj_desc->common.type == ACPI_TYPE_INTEGER) { - - /* Convert the Numeric UID to string */ - - acpi_ex_unsigned_integer_to_string(obj_desc->integer.value, - uid->value); - } else { - /* Copy the String UID from the returned object */ - - acpi_ut_copy_id_string(uid->value, obj_desc->string.pointer, - sizeof(uid->value)); - } + *value = obj_desc->integer.value; /* On exit, we must delete the return object */ @@ -716,60 +419,64 @@ acpi_ut_execute_STA(struct acpi_namespace_node *device_node, u32 * flags) /******************************************************************************* * - * FUNCTION: acpi_ut_execute_Sxds + * FUNCTION: acpi_ut_execute_power_methods * * PARAMETERS: device_node - Node for the device - * Flags - Where the status flags are returned + * method_names - Array of power method names + * method_count - Number of methods to execute + * out_values - Where the power method values are returned * - * RETURN: Status + * RETURN: Status, out_values * - * DESCRIPTION: Executes _STA for selected device and stores results in - * *Flags. + * DESCRIPTION: Executes the specified power methods for the device and returns + * the result(s). * * NOTE: Internal function, no parameter validation * - ******************************************************************************/ +******************************************************************************/ acpi_status -acpi_ut_execute_sxds(struct acpi_namespace_node *device_node, u8 * highest) +acpi_ut_execute_power_methods(struct acpi_namespace_node *device_node, + const char **method_names, + u8 method_count, u8 *out_values) { union acpi_operand_object *obj_desc; acpi_status status; + acpi_status final_status = AE_NOT_FOUND; u32 i; - ACPI_FUNCTION_TRACE(ut_execute_sxds); + ACPI_FUNCTION_TRACE(ut_execute_power_methods); - for (i = 0; i < 4; i++) { - highest[i] = 0xFF; + for (i = 0; i < method_count; i++) { + /* + * Execute the power method (_sx_d or _sx_w). The only allowable + * return type is an Integer. + */ status = acpi_ut_evaluate_object(device_node, ACPI_CAST_PTR(char, - acpi_gbl_highest_dstate_names - [i]), + method_names[i]), ACPI_BTYPE_INTEGER, &obj_desc); - if (ACPI_FAILURE(status)) { - if (status != AE_NOT_FOUND) { - ACPI_DEBUG_PRINT((ACPI_DB_EXEC, - "%s on Device %4.4s, %s\n", - ACPI_CAST_PTR(char, - acpi_gbl_highest_dstate_names - [i]), - acpi_ut_get_node_name - (device_node), - acpi_format_exception - (status))); - - return_ACPI_STATUS(status); - } - } else { - /* Extract the Dstate value */ - - highest[i] = (u8) obj_desc->integer.value; + if (ACPI_SUCCESS(status)) { + out_values[i] = (u8)obj_desc->integer.value; /* Delete the return object */ acpi_ut_remove_reference(obj_desc); + final_status = AE_OK; /* At least one value is valid */ + continue; } + + out_values[i] = ACPI_UINT8_MAX; + if (status == AE_NOT_FOUND) { + continue; /* Ignore if not found */ + } + + ACPI_DEBUG_PRINT((ACPI_DB_EXEC, + "Failed %s on Device %4.4s, %s\n", + ACPI_CAST_PTR(char, method_names[i]), + acpi_ut_get_node_name(device_node), + acpi_format_exception(status))); } - return_ACPI_STATUS(AE_OK); + return_ACPI_STATUS(final_status); } diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index 59e46f257c02..ed7a33c67fbe 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -90,7 +90,15 @@ const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT] = { "\\_S5_" }; -const char *acpi_gbl_highest_dstate_names[4] = { +const char *acpi_gbl_lowest_dstate_names[ACPI_NUM_sx_w_METHODS] = { + "_S0W", + "_S1W", + "_S2W", + "_S3W", + "_S4W" +}; + +const char *acpi_gbl_highest_dstate_names[ACPI_NUM_sx_d_METHODS] = { "_S1D", "_S2D", "_S3D", diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c new file mode 100644 index 000000000000..52eaae404554 --- /dev/null +++ b/drivers/acpi/acpica/utids.c @@ -0,0 +1,382 @@ +/****************************************************************************** + * + * Module Name: utids - support for device IDs - HID, UID, CID + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2009, Intel Corp. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + */ + +#include +#include "accommon.h" +#include "acinterp.h" + +#define _COMPONENT ACPI_UTILITIES +ACPI_MODULE_NAME("utids") + +/* Local prototypes */ +static void acpi_ut_copy_id_string(char *destination, char *source); + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_id_string + * + * PARAMETERS: Destination - Where to copy the string + * Source - Source string + * + * RETURN: None + * + * DESCRIPTION: Copies an ID string for the _HID, _CID, and _UID methods. + * Performs removal of a leading asterisk if present -- workaround + * for a known issue on a bunch of machines. + * + ******************************************************************************/ + +static void acpi_ut_copy_id_string(char *destination, char *source) +{ + + /* + * Workaround for ID strings that have a leading asterisk. This construct + * is not allowed by the ACPI specification (ID strings must be + * alphanumeric), but enough existing machines have this embedded in their + * ID strings that the following code is useful. + */ + if (*source == '*') { + source++; + } + + /* Do the actual copy */ + + ACPI_STRCPY(destination, source); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_HID + * + * PARAMETERS: device_node - Node for the device + * return_id - Where the string HID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _HID control method that returns the hardware + * ID of the device. The HID is either an 32-bit encoded EISAID + * Integer or a String. A string is always returned. An EISAID + * is converted to a string. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_HID(struct acpi_namespace_node *device_node, + struct acpica_device_id **return_id) +{ + union acpi_operand_object *obj_desc; + struct acpica_device_id *hid; + u32 length; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_execute_HID); + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__HID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, + &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get the size of the String to be returned, includes null terminator */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + length = ACPI_EISAID_STRING_SIZE; + } else { + length = obj_desc->string.length + 1; + } + + /* Allocate a buffer for the HID */ + + hid = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpica_device_id) + + (acpi_size) length); + if (!hid) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Area for the string starts after DEVICE_ID struct */ + + hid->string = ACPI_ADD_PTR(char, hid, sizeof(struct acpica_device_id)); + + /* Convert EISAID to a string or simply copy existing string */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + acpi_ex_eisa_id_to_string(hid->string, obj_desc->integer.value); + } else { + acpi_ut_copy_id_string(hid->string, obj_desc->string.pointer); + } + + hid->length = length; + *return_id = hid; + +cleanup: + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_UID + * + * PARAMETERS: device_node - Node for the device + * return_id - Where the string UID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _UID control method that returns the unique + * ID of the device. The UID is either a 64-bit Integer (NOT an + * EISAID) or a string. Always returns a string. A 64-bit integer + * is converted to a decimal string. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_UID(struct acpi_namespace_node *device_node, + struct acpica_device_id **return_id) +{ + union acpi_operand_object *obj_desc; + struct acpica_device_id *uid; + u32 length; + acpi_status status; + + ACPI_FUNCTION_TRACE(ut_execute_UID); + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__UID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, + &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* Get the size of the String to be returned, includes null terminator */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + length = ACPI_MAX64_DECIMAL_DIGITS + 1; + } else { + length = obj_desc->string.length + 1; + } + + /* Allocate a buffer for the UID */ + + uid = + ACPI_ALLOCATE_ZEROED(sizeof(struct acpica_device_id) + + (acpi_size) length); + if (!uid) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Area for the string starts after DEVICE_ID struct */ + + uid->string = ACPI_ADD_PTR(char, uid, sizeof(struct acpica_device_id)); + + /* Convert an Integer to string, or just copy an existing string */ + + if (obj_desc->common.type == ACPI_TYPE_INTEGER) { + acpi_ex_integer_to_string(uid->string, obj_desc->integer.value); + } else { + acpi_ut_copy_id_string(uid->string, obj_desc->string.pointer); + } + + uid->length = length; + *return_id = uid; + +cleanup: + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_CID + * + * PARAMETERS: device_node - Node for the device + * return_cid_list - Where the CID list is returned + * + * RETURN: Status, list of CID strings + * + * DESCRIPTION: Executes the _CID control method that returns one or more + * compatible hardware IDs for the device. + * + * NOTE: Internal function, no parameter validation + * + * A _CID method can return either a single compatible ID or a package of + * compatible IDs. Each compatible ID can be one of the following: + * 1) Integer (32 bit compressed EISA ID) or + * 2) String (PCI ID format, e.g. "PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss") + * + * The Integer CIDs are converted to string format by this function. + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_CID(struct acpi_namespace_node *device_node, + struct acpica_device_id_list **return_cid_list) +{ + union acpi_operand_object **cid_objects; + union acpi_operand_object *obj_desc; + struct acpica_device_id_list *cid_list; + char *next_id_string; + u32 string_area_size; + u32 length; + u32 cid_list_size; + acpi_status status; + u32 count; + u32 i; + + ACPI_FUNCTION_TRACE(ut_execute_CID); + + /* Evaluate the _CID method for this device */ + + status = acpi_ut_evaluate_object(device_node, METHOD_NAME__CID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING + | ACPI_BTYPE_PACKAGE, &obj_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* + * Get the count and size of the returned _CIDs. _CID can return either + * a Package of Integers/Strings or a single Integer or String. + * Note: This section also validates that all CID elements are of the + * correct type (Integer or String). + */ + if (obj_desc->common.type == ACPI_TYPE_PACKAGE) { + count = obj_desc->package.count; + cid_objects = obj_desc->package.elements; + } else { /* Single Integer or String CID */ + + count = 1; + cid_objects = &obj_desc; + } + + string_area_size = 0; + for (i = 0; i < count; i++) { + + /* String lengths include null terminator */ + + switch (cid_objects[i]->common.type) { + case ACPI_TYPE_INTEGER: + string_area_size += ACPI_EISAID_STRING_SIZE; + break; + + case ACPI_TYPE_STRING: + string_area_size += cid_objects[i]->string.length + 1; + break; + + default: + status = AE_TYPE; + goto cleanup; + } + } + + /* + * Now that we know the length of the CIDs, allocate return buffer: + * 1) Size of the base structure + + * 2) Size of the CID DEVICE_ID array + + * 3) Size of the actual CID strings + */ + cid_list_size = sizeof(struct acpica_device_id_list) + + ((count - 1) * sizeof(struct acpica_device_id)) + string_area_size; + + cid_list = ACPI_ALLOCATE_ZEROED(cid_list_size); + if (!cid_list) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Area for CID strings starts after the CID DEVICE_ID array */ + + next_id_string = ACPI_CAST_PTR(char, cid_list->ids) + + ((acpi_size) count * sizeof(struct acpica_device_id)); + + /* Copy/convert the CIDs to the return buffer */ + + for (i = 0; i < count; i++) { + if (cid_objects[i]->common.type == ACPI_TYPE_INTEGER) { + + /* Convert the Integer (EISAID) CID to a string */ + + acpi_ex_eisa_id_to_string(next_id_string, + cid_objects[i]->integer. + value); + length = ACPI_EISAID_STRING_SIZE; + } else { /* ACPI_TYPE_STRING */ + + /* Copy the String CID from the returned object */ + + acpi_ut_copy_id_string(next_id_string, + cid_objects[i]->string.pointer); + length = cid_objects[i]->string.length + 1; + } + + cid_list->ids[i].string = next_id_string; + cid_list->ids[i].length = length; + next_id_string += length; + } + + /* Finish the CID list */ + + cid_list->count = count; + cid_list->list_size = cid_list_size; + *return_cid_list = cid_list; + +cleanup: + + /* On exit, we must delete the _CID return object */ + + acpi_ut_remove_reference(obj_desc); + return_ACPI_STATUS(status); +} diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index fbe782348b0b..9cd65334ca75 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -118,6 +118,34 @@ const char *acpi_ut_validate_exception(acpi_status status) return (ACPI_CAST_PTR(const char, exception)); } +/******************************************************************************* + * + * FUNCTION: acpi_ut_is_pci_root_bridge + * + * PARAMETERS: Id - The HID/CID in string format + * + * RETURN: TRUE if the Id is a match for a PCI/PCI-Express Root Bridge + * + * DESCRIPTION: Determine if the input ID is a PCI Root Bridge ID. + * + ******************************************************************************/ + +u8 acpi_ut_is_pci_root_bridge(char *id) +{ + + /* + * Check if this is a PCI root bridge. + * ACPI 3.0+: check for a PCI Express root also. + */ + if (!(ACPI_STRCMP(id, + PCI_ROOT_HID_STRING)) || + !(ACPI_STRCMP(id, PCI_EXPRESS_ROOT_HID_STRING))) { + return (TRUE); + } + + return (FALSE); +} + /******************************************************************************* * * FUNCTION: acpi_ut_is_aml_table diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index fe0cdf83641a..2aee8c24dc56 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -200,20 +200,17 @@ container_walk_namespace_cb(acpi_handle handle, u32 lvl, void *context, void **rv) { char *hid = NULL; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_device_info *info; acpi_status status; int *action = context; - - status = acpi_get_object_info(handle, &buffer); - if (ACPI_FAILURE(status) || !buffer.pointer) { + status = acpi_get_object_info(handle, &info); + if (ACPI_FAILURE(status)) { return AE_OK; } - info = buffer.pointer; if (info->valid & ACPI_VALID_HID) - hid = info->hardware_id.value; + hid = info->hardware_id.string; if (hid == NULL) { goto end; @@ -240,7 +237,7 @@ container_walk_namespace_cb(acpi_handle handle, } end: - kfree(buffer.pointer); + kfree(info); return AE_OK; } diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index efb959d6c8a9..39536b80bce7 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -231,18 +231,16 @@ static int is_ata(acpi_handle handle) static int is_battery(acpi_handle handle) { struct acpi_device_info *info; - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; int ret = 1; - if (!ACPI_SUCCESS(acpi_get_object_info(handle, &buffer))) + if (!ACPI_SUCCESS(acpi_get_object_info(handle, &info))) return 0; - info = buffer.pointer; if (!(info->valid & ACPI_VALID_HID)) ret = 0; else - ret = !strcmp("PNP0C0A", info->hardware_id.value); + ret = !strcmp("PNP0C0A", info->hardware_id.string); - kfree(buffer.pointer); + kfree(info); return ret; } diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index a8a5c29958c8..27a7072347ea 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -93,15 +93,13 @@ do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv) { acpi_status status; struct acpi_device_info *info; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_find_child *find = context; - status = acpi_get_object_info(handle, &buffer); + status = acpi_get_object_info(handle, &info); if (ACPI_SUCCESS(status)) { - info = buffer.pointer; if (info->address == find->address) find->handle = handle; - kfree(buffer.pointer); + kfree(info); } return AE_OK; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 781435d7e369..0ab526de7c55 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -60,13 +60,13 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias, } if (acpi_dev->flags.compatible_ids) { - struct acpi_compatible_id_list *cid_list; + struct acpica_device_id_list *cid_list; int i; cid_list = acpi_dev->pnp.cid_list; for (i = 0; i < cid_list->count; i++) { count = snprintf(&modalias[len], size, "%s:", - cid_list->id[i].value); + cid_list->ids[i].string); if (count < 0 || count >= size) { printk(KERN_ERR PREFIX "%s cid[%i] exceeds event buffer size", acpi_dev->pnp.device_name, i); @@ -287,14 +287,14 @@ int acpi_match_device_ids(struct acpi_device *device, } if (device->flags.compatible_ids) { - struct acpi_compatible_id_list *cid_list = device->pnp.cid_list; + struct acpica_device_id_list *cid_list = device->pnp.cid_list; int i; for (id = ids; id->id[0]; id++) { /* compare multiple _CID entries against driver ids */ for (i = 0; i < cid_list->count; i++) { if (!strcmp((char*)id->id, - cid_list->id[i].value)) + cid_list->ids[i].string)) return 0; } } @@ -999,33 +999,89 @@ static int acpi_dock_match(struct acpi_device *device) return acpi_get_handle(device->handle, "_DCK", &tmp); } +static struct acpica_device_id_list* +acpi_add_cid( + struct acpi_device_info *info, + struct acpica_device_id *new_cid) +{ + struct acpica_device_id_list *cid; + char *next_id_string; + acpi_size cid_length; + acpi_size new_cid_length; + u32 i; + + + /* Allocate new CID list with room for the new CID */ + + if (!new_cid) + new_cid_length = info->compatible_id_list.list_size; + else if (info->compatible_id_list.list_size) + new_cid_length = info->compatible_id_list.list_size + + new_cid->length + sizeof(struct acpica_device_id); + else + new_cid_length = sizeof(struct acpica_device_id_list) + new_cid->length; + + cid = ACPI_ALLOCATE_ZEROED(new_cid_length); + if (!cid) { + return NULL; + } + + cid->list_size = new_cid_length; + cid->count = info->compatible_id_list.count; + if (new_cid) + cid->count++; + next_id_string = (char *) cid->ids + (cid->count * sizeof(struct acpica_device_id)); + + /* Copy all existing CIDs */ + + for (i = 0; i < info->compatible_id_list.count; i++) { + cid_length = info->compatible_id_list.ids[i].length; + cid->ids[i].string = next_id_string; + cid->ids[i].length = cid_length; + + ACPI_MEMCPY(next_id_string, info->compatible_id_list.ids[i].string, + cid_length); + + next_id_string += cid_length; + } + + /* Append the new CID */ + + if (new_cid) { + cid->ids[i].string = next_id_string; + cid->ids[i].length = new_cid->length; + + ACPI_MEMCPY(next_id_string, new_cid->string, new_cid->length); + } + + return cid; +} + static void acpi_device_set_id(struct acpi_device *device, struct acpi_device *parent, acpi_handle handle, int type) { - struct acpi_device_info *info; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_device_info *info = NULL; char *hid = NULL; char *uid = NULL; - struct acpi_compatible_id_list *cid_list = NULL; - const char *cid_add = NULL; + struct acpica_device_id_list *cid_list = NULL; + char *cid_add = NULL; acpi_status status; switch (type) { case ACPI_BUS_TYPE_DEVICE: - status = acpi_get_object_info(handle, &buffer); + status = acpi_get_object_info(handle, &info); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX "%s: Error reading device info\n", __func__); return; } - info = buffer.pointer; if (info->valid & ACPI_VALID_HID) - hid = info->hardware_id.value; + hid = info->hardware_id.string; if (info->valid & ACPI_VALID_UID) - uid = info->unique_id.value; + uid = info->unique_id.string; if (info->valid & ACPI_VALID_CID) - cid_list = &info->compatibility_id; + cid_list = &info->compatible_id_list; if (info->valid & ACPI_VALID_ADR) { device->pnp.bus_address = info->address; device->flags.bus_address = 1; @@ -1076,55 +1132,44 @@ static void acpi_device_set_id(struct acpi_device *device, } if (hid) { - strcpy(device->pnp.hardware_id, hid); - device->flags.hardware_id = 1; - } + device->pnp.hardware_id = ACPI_ALLOCATE_ZEROED(strlen (hid) + 1); + if (device->pnp.hardware_id) { + strcpy(device->pnp.hardware_id, hid); + device->flags.hardware_id = 1; + } + } else + device->pnp.hardware_id = NULL; + if (uid) { - strcpy(device->pnp.unique_id, uid); - device->flags.unique_id = 1; - } + device->pnp.unique_id = ACPI_ALLOCATE_ZEROED(strlen (uid) + 1); + if (device->pnp.unique_id) { + strcpy(device->pnp.unique_id, uid); + device->flags.unique_id = 1; + } + } else + device->pnp.unique_id = NULL; + if (cid_list || cid_add) { - struct acpi_compatible_id_list *list; - int size = 0; - int count = 0; - - if (cid_list) { - size = cid_list->size; - } else if (cid_add) { - size = sizeof(struct acpi_compatible_id_list); - cid_list = ACPI_ALLOCATE_ZEROED((acpi_size) size); - if (!cid_list) { - printk(KERN_ERR "Memory allocation error\n"); - kfree(buffer.pointer); - return; - } else { - cid_list->count = 0; - cid_list->size = size; - } + struct acpica_device_id_list *list; + + if (cid_add) { + struct acpica_device_id cid; + cid.length = strlen (cid_add) + 1; + cid.string = cid_add; + + list = acpi_add_cid(info, &cid); + } else { + list = acpi_add_cid(info, NULL); } - if (cid_add) - size += sizeof(struct acpi_compatible_id); - list = kmalloc(size, GFP_KERNEL); if (list) { - if (cid_list) { - memcpy(list, cid_list, cid_list->size); - count = cid_list->count; - } - if (cid_add) { - strncpy(list->id[count].value, cid_add, - ACPI_MAX_CID_LENGTH); - count++; - device->flags.compatible_ids = 1; - } - list->size = size; - list->count = count; device->pnp.cid_list = list; - } else - printk(KERN_ERR PREFIX "Memory allocation error\n"); + if (cid_add) + device->flags.compatible_ids = 1; + } } - kfree(buffer.pointer); + kfree(info); } static int acpi_device_set_context(struct acpi_device *device, int type) diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 8f3d4c184914..7bead4c816ca 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -478,7 +478,6 @@ zx1_gart_probe (acpi_handle obj, u32 depth, void *context, void **ret) { acpi_handle handle, parent; acpi_status status; - struct acpi_buffer buffer; struct acpi_device_info *info; u64 lba_hpa, sba_hpa, length; int match; @@ -490,13 +489,11 @@ zx1_gart_probe (acpi_handle obj, u32 depth, void *context, void **ret) /* Look for an enclosing IOC scope and find its CSR space */ handle = obj; do { - buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; - status = acpi_get_object_info(handle, &buffer); + status = acpi_get_object_info(handle, &info); if (ACPI_SUCCESS(status)) { /* TBD check _CID also */ - info = buffer.pointer; - info->hardware_id.value[sizeof(info->hardware_id)-1] = '\0'; - match = (strcmp(info->hardware_id.value, "HWP0001") == 0); + info->hardware_id.string[sizeof(info->hardware_id.length)-1] = '\0'; + match = (strcmp(info->hardware_id.string, "HWP0001") == 0); kfree(info); if (match) { status = hp_acpi_csr_space(handle, &sba_hpa, &length); diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c index c509c9916464..c0cf45a11b93 100644 --- a/drivers/ide/ide-acpi.c +++ b/drivers/ide/ide-acpi.c @@ -114,8 +114,6 @@ static int ide_get_dev_handle(struct device *dev, acpi_handle *handle, unsigned int bus, devnum, func; acpi_integer addr; acpi_handle dev_handle; - struct acpi_buffer buffer = {.length = ACPI_ALLOCATE_BUFFER, - .pointer = NULL}; acpi_status status; struct acpi_device_info *dinfo = NULL; int ret = -ENODEV; @@ -134,12 +132,11 @@ static int ide_get_dev_handle(struct device *dev, acpi_handle *handle, goto err; } - status = acpi_get_object_info(dev_handle, &buffer); + status = acpi_get_object_info(dev_handle, &dinfo); if (ACPI_FAILURE(status)) { DEBPRINT("get_object_info for device failed\n"); goto err; } - dinfo = buffer.pointer; if (dinfo && (dinfo->valid & ACPI_VALID_ADR) && dinfo->address == addr) { *pcidevfn = addr; diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 5befa7e379b7..a9d926b7d805 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -398,23 +398,21 @@ static acpi_status __init ibm_find_acpi_device(acpi_handle handle, acpi_handle *phandle = (acpi_handle *)context; acpi_status status; struct acpi_device_info *info; - struct acpi_buffer info_buffer = { ACPI_ALLOCATE_BUFFER, NULL }; int retval = 0; - status = acpi_get_object_info(handle, &info_buffer); + status = acpi_get_object_info(handle, &info); if (ACPI_FAILURE(status)) { err("%s: Failed to get device information status=0x%x\n", __func__, status); return retval; } - info = info_buffer.pointer; - info->hardware_id.value[sizeof(info->hardware_id.value) - 1] = '\0'; + info->hardware_id.string[sizeof(info->hardware_id.length) - 1] = '\0'; if (info->current_status && (info->valid & ACPI_VALID_HID) && - (!strcmp(info->hardware_id.value, IBM_HARDWARE_ID1) || - !strcmp(info->hardware_id.value, IBM_HARDWARE_ID2))) { + (!strcmp(info->hardware_id.string, IBM_HARDWARE_ID1) || + !strcmp(info->hardware_id.string, IBM_HARDWARE_ID2))) { dbg("found hardware: %s, handle: %p\n", - info->hardware_id.value, handle); + info->hardware_id.string, handle); *phandle = handle; /* returning non-zero causes the search to stop * and returns this value to the caller of diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index dafaa4a92df5..f9f68e0e7344 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -976,15 +976,12 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, void *context, void **return_value) { struct acpi_device_info *info; - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; - - if (ACPI_SUCCESS(acpi_get_object_info(handle, &buffer))) { - info = buffer.pointer; + if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) { printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", (char *)&info->name, info->param_count); - kfree(buffer.pointer); + kfree(info); } return AE_OK; diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 9496494f340e..c07fdb94d665 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -194,13 +194,13 @@ static int __init pnpacpi_add_device(struct acpi_device *device) pnpacpi_parse_resource_option_data(dev); if (device->flags.compatible_ids) { - struct acpi_compatible_id_list *cid_list = device->pnp.cid_list; + struct acpica_device_id_list *cid_list = device->pnp.cid_list; int i; for (i = 0; i < cid_list->count; i++) { - if (!ispnpidacpi(cid_list->id[i].value)) + if (!ispnpidacpi(cid_list->ids[i].string)) continue; - pnp_add_id(dev, cid_list->id[i].value); + pnp_add_id(dev, cid_list->ids[i].string); } } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index c65e4ce6c3af..b91420b52c6f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -173,17 +173,15 @@ struct acpi_device_dir { typedef char acpi_bus_id[8]; typedef unsigned long acpi_bus_address; -typedef char acpi_hardware_id[15]; -typedef char acpi_unique_id[9]; typedef char acpi_device_name[40]; typedef char acpi_device_class[20]; struct acpi_device_pnp { acpi_bus_id bus_id; /* Object name */ acpi_bus_address bus_address; /* _ADR */ - acpi_hardware_id hardware_id; /* _HID */ - struct acpi_compatible_id_list *cid_list; /* _CIDs */ - acpi_unique_id unique_id; /* _UID */ + char *hardware_id; /* _HID */ + struct acpica_device_id_list *cid_list; /* _CIDs */ + char *unique_id; /* _UID */ acpi_device_name device_name; /* Driver-determined */ acpi_device_class device_class; /* " */ }; diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index b450a195319a..04904c7f1aa1 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -200,7 +200,8 @@ acpi_evaluate_object_typed(acpi_handle object, acpi_object_type return_type); acpi_status -acpi_get_object_info(acpi_handle handle, struct acpi_buffer *return_buffer); +acpi_get_object_info(acpi_handle handle, + struct acpi_device_info **return_buffer); acpi_status acpi_install_method(u8 *buffer); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 37ba576d06e8..7a4ff79e238c 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -338,7 +338,7 @@ typedef u32 acpi_physical_address; /* PM Timer ticks per second (HZ) */ -#define PM_TIMER_FREQUENCY 3579545 +#define PM_TIMER_FREQUENCY 3579545 /******************************************************************************* * @@ -969,38 +969,60 @@ acpi_status(*acpi_walk_callback) (acpi_handle obj_handle, #define ACPI_INTERRUPT_NOT_HANDLED 0x00 #define ACPI_INTERRUPT_HANDLED 0x01 -/* Length of _HID, _UID, _CID, and UUID values */ +/* Length of 32-bit EISAID values when converted back to a string */ + +#define ACPI_EISAID_STRING_SIZE 8 /* Includes null terminator */ + +/* Length of UUID (string) values */ -#define ACPI_DEVICE_ID_LENGTH 0x09 -#define ACPI_MAX_CID_LENGTH 48 #define ACPI_UUID_LENGTH 16 -/* Common string version of device HIDs and UIDs */ +/* Structures used for device/processor HID, UID, CID */ struct acpica_device_id { - char value[ACPI_DEVICE_ID_LENGTH]; + u32 length; /* Length of string + null */ + char *string; }; -/* Common string version of device CIDs */ - -struct acpi_compatible_id { - char value[ACPI_MAX_CID_LENGTH]; +struct acpica_device_id_list { + u32 count; /* Number of IDs in Ids array */ + u32 list_size; /* Size of list, including ID strings */ + struct acpica_device_id ids[1]; /* ID array */ }; -struct acpi_compatible_id_list { - u32 count; - u32 size; - struct acpi_compatible_id id[1]; +/* + * Structure returned from acpi_get_object_info. + * Optimized for both 32- and 64-bit builds + */ +struct acpi_device_info { + u32 info_size; /* Size of info, including ID strings */ + u32 name; /* ACPI object Name */ + acpi_object_type type; /* ACPI object Type */ + u8 param_count; /* If a method, required parameter count */ + u8 valid; /* Indicates which optional fields are valid */ + u8 flags; /* Miscellaneous info */ + u8 highest_dstates[4]; /* _sx_d values: 0xFF indicates not valid */ + u8 lowest_dstates[5]; /* _sx_w values: 0xFF indicates not valid */ + u32 current_status; /* _STA value */ + acpi_integer address; /* _ADR value */ + struct acpica_device_id hardware_id; /* _HID value */ + struct acpica_device_id unique_id; /* _UID value */ + struct acpica_device_id_list compatible_id_list; /* _CID list */ }; -/* Structure and flags for acpi_get_object_info */ +/* Values for Flags field above (acpi_get_object_info) */ + +#define ACPI_PCI_ROOT_BRIDGE 0x01 -#define ACPI_VALID_STA 0x0001 -#define ACPI_VALID_ADR 0x0002 -#define ACPI_VALID_HID 0x0004 -#define ACPI_VALID_UID 0x0008 -#define ACPI_VALID_CID 0x0010 -#define ACPI_VALID_SXDS 0x0020 +/* Flags for Valid field above (acpi_get_object_info) */ + +#define ACPI_VALID_STA 0x01 +#define ACPI_VALID_ADR 0x02 +#define ACPI_VALID_HID 0x04 +#define ACPI_VALID_UID 0x08 +#define ACPI_VALID_CID 0x10 +#define ACPI_VALID_SXDS 0x20 +#define ACPI_VALID_SXWS 0x40 /* Flags for _STA method */ @@ -1011,29 +1033,6 @@ struct acpi_compatible_id_list { #define ACPI_STA_DEVICE_OK 0x08 /* Synonym */ #define ACPI_STA_BATTERY_PRESENT 0x10 -#define ACPI_COMMON_OBJ_INFO \ - acpi_object_type type; /* ACPI object type */ \ - acpi_name name /* ACPI object Name */ - -struct acpi_obj_info_header { - ACPI_COMMON_OBJ_INFO; -}; - -/* Structure returned from Get Object Info */ - -struct acpi_device_info { - ACPI_COMMON_OBJ_INFO; - - u32 param_count; /* If a method, required parameter count */ - u32 valid; /* Indicates which fields below are valid */ - u32 current_status; /* _STA value */ - acpi_integer address; /* _ADR value if any */ - struct acpica_device_id hardware_id; /* _HID value if any */ - struct acpica_device_id unique_id; /* _UID value if any */ - u8 highest_dstates[4]; /* _sx_d values: 0xFF indicates not valid */ - struct acpi_compatible_id_list compatibility_id; /* List of _CIDs if any */ -}; - /* Context structs for address space handlers */ struct acpi_pci_id { -- cgit v1.2.3 From 948c28fe3001f2c9d852dff2a0b2c69fe7cec91b Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Thu, 20 Aug 2009 11:14:06 +0000 Subject: hvc_console: Add __init and __exit to hvc_vio Trivial patch which adds the __init/__exit macros to the module_init/ module_exit functions of char/hvc_vio.c Please have a look at the small patch and either pull it through your tree, or please ack' it so Jiri can pull it through the trivial tree. linux version 2.6.31-rc6 - linus git tree, Do 20. Aug 22:26:06 CEST 2009 Signed-off-by: Peter Huewe Signed-off-by: Benjamin Herrenschmidt --- drivers/char/hvc_vio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index c72b994652ac..10be343d6ae7 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -120,7 +120,7 @@ static struct vio_driver hvc_vio_driver = { } }; -static int hvc_vio_init(void) +static int __init hvc_vio_init(void) { int rc; @@ -134,7 +134,7 @@ static int hvc_vio_init(void) } module_init(hvc_vio_init); /* after drivers/char/hvc_console.c */ -static void hvc_vio_exit(void) +static void __exit hvc_vio_exit(void) { vio_unregister_driver(&hvc_vio_driver); } -- cgit v1.2.3 From 4c5d502d8b2db8947c44dc44bdc67dbe55cce2b9 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Mon, 31 Aug 2009 19:50:48 +0000 Subject: hdlc: convert to netdev_tx_t Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/char/pcmcia/synclink_cs.c | 7 +++---- drivers/char/synclink.c | 7 +++---- drivers/char/synclink_gt.c | 7 +++---- drivers/char/synclinkmp.c | 7 +++---- include/linux/hdlc.h | 8 ++++---- 5 files changed, 16 insertions(+), 20 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 77b364889224..caf6e4d19469 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -4005,10 +4005,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, * * skb socket buffer containing HDLC frame * dev pointer to network device structure - * - * returns 0 if success, otherwise error code */ -static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) { MGSLPC_INFO *info = dev_to_port(dev); unsigned long flags; @@ -4043,7 +4042,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) } spin_unlock_irqrestore(&info->lock,flags); - return 0; + return NETDEV_TX_OK; } /** diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c index 813552f14884..4846b73ef28d 100644 --- a/drivers/char/synclink.c +++ b/drivers/char/synclink.c @@ -7697,10 +7697,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, * * skb socket buffer containing HDLC frame * dev pointer to network device structure - * - * returns 0 if success, otherwise error code */ -static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) { struct mgsl_struct *info = dev_to_port(dev); unsigned long flags; @@ -7731,7 +7730,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) usc_start_transmitter(info); spin_unlock_irqrestore(&info->irq_spinlock,flags); - return 0; + return NETDEV_TX_OK; } /** diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 91f20a92fddf..8678f0c8699d 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -1497,10 +1497,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, * * skb socket buffer containing HDLC frame * dev pointer to network device structure - * - * returns 0 if success, otherwise error code */ -static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) { struct slgt_info *info = dev_to_port(dev); unsigned long flags; @@ -1529,7 +1528,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) update_tx_timer(info); spin_unlock_irqrestore(&info->lock,flags); - return 0; + return NETDEV_TX_OK; } /** diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c index 8d4a2a8a0a70..2b18adc4ee19 100644 --- a/drivers/char/synclinkmp.c +++ b/drivers/char/synclinkmp.c @@ -1608,10 +1608,9 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding, * * skb socket buffer containing HDLC frame * dev pointer to network device structure - * - * returns 0 if success, otherwise error code */ -static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) +static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb, + struct net_device *dev) { SLMP_INFO *info = dev_to_port(dev); unsigned long flags; @@ -1642,7 +1641,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) tx_start(info); spin_unlock_irqrestore(&info->lock,flags); - return 0; + return NETDEV_TX_OK; } /** diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h index 6a6e701f1631..ee275c8b3df1 100644 --- a/include/linux/hdlc.h +++ b/include/linux/hdlc.h @@ -38,7 +38,7 @@ struct hdlc_proto { int (*ioctl)(struct net_device *dev, struct ifreq *ifr); __be16 (*type_trans)(struct sk_buff *skb, struct net_device *dev); int (*netif_rx)(struct sk_buff *skb); - int (*xmit)(struct sk_buff *skb, struct net_device *dev); + netdev_tx_t (*xmit)(struct sk_buff *skb, struct net_device *dev); struct module *module; struct hdlc_proto *next; /* next protocol in the list */ }; @@ -51,7 +51,7 @@ typedef struct hdlc_device { unsigned short encoding, unsigned short parity); /* hardware driver must handle this instead of dev->hard_start_xmit */ - int (*xmit)(struct sk_buff *skb, struct net_device *dev); + netdev_tx_t (*xmit)(struct sk_buff *skb, struct net_device *dev); /* Things below are for HDLC layer internal use only */ const struct hdlc_proto *proto; @@ -60,7 +60,7 @@ typedef struct hdlc_device { spinlock_t state_lock; void *state; void *priv; -}hdlc_device; +} hdlc_device; @@ -106,7 +106,7 @@ void hdlc_close(struct net_device *dev); /* May be used by hardware driver */ int hdlc_change_mtu(struct net_device *dev, int new_mtu); /* Must be pointed to by hw driver's dev->netdev_ops->ndo_start_xmit */ -int hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev); +netdev_tx_t hdlc_start_xmit(struct sk_buff *skb, struct net_device *dev); int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto, size_t size); -- cgit v1.2.3 From 38d8a95621b20ed7868e232a35a26ee61bdcae6f Mon Sep 17 00:00:00 2001 From: Fabian Henze Date: Tue, 8 Sep 2009 00:59:58 +0800 Subject: agp/intel: Add B43 chipset support Signed-off-by: Fabian Henze [Fix reversed HB & IG ids for B43] Signed-off-by: Zhenyu Wang Signed-off-by: Eric Anholt --- drivers/char/agp/intel-agp.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index c58557790585..c17291715031 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -36,6 +36,8 @@ #define PCI_DEVICE_ID_INTEL_Q35_IG 0x29B2 #define PCI_DEVICE_ID_INTEL_Q33_HB 0x29D0 #define PCI_DEVICE_ID_INTEL_Q33_IG 0x29D2 +#define PCI_DEVICE_ID_INTEL_B43_HB 0x2E40 +#define PCI_DEVICE_ID_INTEL_B43_IG 0x2E42 #define PCI_DEVICE_ID_INTEL_GM45_HB 0x2A40 #define PCI_DEVICE_ID_INTEL_GM45_IG 0x2A42 #define PCI_DEVICE_ID_INTEL_IGD_E_HB 0x2E00 @@ -81,6 +83,7 @@ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G45_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_GM45_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_G41_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_B43_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_D_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_M_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_IGDNG_MA_HB) @@ -1216,6 +1219,7 @@ static void intel_i965_get_gtt_range(int *gtt_offset, int *gtt_size) case PCI_DEVICE_ID_INTEL_Q45_HB: case PCI_DEVICE_ID_INTEL_G45_HB: case PCI_DEVICE_ID_INTEL_G41_HB: + case PCI_DEVICE_ID_INTEL_B43_HB: case PCI_DEVICE_ID_INTEL_IGDNG_D_HB: case PCI_DEVICE_ID_INTEL_IGDNG_M_HB: case PCI_DEVICE_ID_INTEL_IGDNG_MA_HB: @@ -2192,6 +2196,8 @@ static const struct intel_driver_description { "Q45/Q43", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_G45_HB, PCI_DEVICE_ID_INTEL_G45_IG, 0, "G45/G43", NULL, &intel_i965_driver }, + { PCI_DEVICE_ID_INTEL_B43_HB, PCI_DEVICE_ID_INTEL_B43_IG, 0, + "B43", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_G41_HB, PCI_DEVICE_ID_INTEL_G41_IG, 0, "G41", NULL, &intel_i965_driver }, { PCI_DEVICE_ID_INTEL_IGDNG_D_HB, PCI_DEVICE_ID_INTEL_IGDNG_D_IG, 0, @@ -2401,6 +2407,7 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_Q45_HB), ID(PCI_DEVICE_ID_INTEL_G45_HB), ID(PCI_DEVICE_ID_INTEL_G41_HB), + ID(PCI_DEVICE_ID_INTEL_B43_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_D_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_M_HB), ID(PCI_DEVICE_ID_INTEL_IGDNG_MA_HB), -- cgit v1.2.3 From d331d8305cba713605854aab63a000fb892353a7 Mon Sep 17 00:00:00 2001 From: Martyn Welch Date: Thu, 13 Aug 2009 09:03:02 +0100 Subject: powerpc/nvram: Enable use Generic NVRAM driver for different size chips Remove the reliance on a staticly defined NVRAM size, allowing platforms to support NVRAMs with sizes differing from the standard. A fall back value is provided for platforms not supporting this extension. Signed-off-by: Martyn Welch Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/include/asm/nvram.h | 3 +++ arch/powerpc/kernel/setup_32.c | 8 ++++++++ drivers/char/generic_nvram.c | 27 ++++++++++++++++++++------- 3 files changed, 31 insertions(+), 7 deletions(-) (limited to 'drivers/char') diff --git a/arch/powerpc/include/asm/nvram.h b/arch/powerpc/include/asm/nvram.h index efde5ac82f7b..6c587eddee59 100644 --- a/arch/powerpc/include/asm/nvram.h +++ b/arch/powerpc/include/asm/nvram.h @@ -107,6 +107,9 @@ extern void pmac_xpram_write(int xpaddr, u8 data); /* Synchronize NVRAM */ extern void nvram_sync(void); +/* Determine NVRAM size */ +extern ssize_t nvram_get_size(void); + /* Normal access to NVRAM */ extern unsigned char nvram_read_byte(int i); extern void nvram_write_byte(unsigned char c, int i); diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c index e1e3059cf34b..53bcf3d792db 100644 --- a/arch/powerpc/kernel/setup_32.c +++ b/arch/powerpc/kernel/setup_32.c @@ -210,6 +210,14 @@ void nvram_write_byte(unsigned char val, int addr) } EXPORT_SYMBOL(nvram_write_byte); +ssize_t nvram_get_size(void) +{ + if (ppc_md.nvram_size) + return ppc_md.nvram_size(); + return -1; +} +EXPORT_SYMBOL(nvram_get_size); + void nvram_sync(void) { if (ppc_md.nvram_sync) diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c index a00869c650d5..ef31738c2cbe 100644 --- a/drivers/char/generic_nvram.c +++ b/drivers/char/generic_nvram.c @@ -2,7 +2,7 @@ * Generic /dev/nvram driver for architectures providing some * "generic" hooks, that is : * - * nvram_read_byte, nvram_write_byte, nvram_sync + * nvram_read_byte, nvram_write_byte, nvram_sync, nvram_get_size * * Note that an additional hook is supported for PowerMac only * for getting the nvram "partition" informations @@ -28,6 +28,8 @@ #define NVRAM_SIZE 8192 +static ssize_t nvram_len; + static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) { lock_kernel(); @@ -36,7 +38,7 @@ static loff_t nvram_llseek(struct file *file, loff_t offset, int origin) offset += file->f_pos; break; case 2: - offset += NVRAM_SIZE; + offset += nvram_len; break; } if (offset < 0) { @@ -56,9 +58,9 @@ static ssize_t read_nvram(struct file *file, char __user *buf, if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; - if (*ppos >= NVRAM_SIZE) + if (*ppos >= nvram_len) return 0; - for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) + for (i = *ppos; count > 0 && i < nvram_len; ++i, ++p, --count) if (__put_user(nvram_read_byte(i), p)) return -EFAULT; *ppos = i; @@ -74,9 +76,9 @@ static ssize_t write_nvram(struct file *file, const char __user *buf, if (!access_ok(VERIFY_READ, buf, count)) return -EFAULT; - if (*ppos >= NVRAM_SIZE) + if (*ppos >= nvram_len) return 0; - for (i = *ppos; count > 0 && i < NVRAM_SIZE; ++i, ++p, --count) { + for (i = *ppos; count > 0 && i < nvram_len; ++i, ++p, --count) { if (__get_user(c, p)) return -EFAULT; nvram_write_byte(c, i); @@ -133,9 +135,20 @@ static struct miscdevice nvram_dev = { int __init nvram_init(void) { + int ret = 0; + printk(KERN_INFO "Generic non-volatile memory driver v%s\n", NVRAM_VERSION); - return misc_register(&nvram_dev); + ret = misc_register(&nvram_dev); + if (ret != 0) + goto out; + + nvram_len = nvram_get_size(); + if (nvram_len < 0) + nvram_len = NVRAM_SIZE; + +out: + return ret; } void __exit nvram_cleanup(void) -- cgit v1.2.3 From e517a5e97080bbe52857bd0d7df9b66602d53c4d Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Thu, 10 Sep 2009 17:48:48 -0700 Subject: agp/intel: Fix the pre-9xx chipset flush. Ever since we enabled GEM, the pre-9xx chipsets (particularly 865) have had serious stability issues. Back in May a wbinvd was added to the DRM to work around much of the problem. Some failure remained -- easily visible by dragging a window around on an X -retro desktop, or by looking at bugzilla. The chipset flush was on the right track -- hitting the right amount of memory, and it appears to be the only way to flush on these chipsets, but the flush page was mapped uncached. As a result, the writes trying to clear the writeback cache ended up bypassing the cache, and not flushing anything! The wbinvd would flush out other writeback data and often cause the data we wanted to get flushed, but not always. By removing the setting of the page to UC and instead just clflushing the data we write to try to flush it, we get the desired behavior with no wbinvd. This exports clflush_cache_range(), which was laying around and happened to basically match the code I was otherwise going to copy from the DRM. Signed-off-by: Eric Anholt Signed-off-by: Brice Goglin Cc: stable@kernel.org --- arch/x86/mm/pageattr.c | 1 + drivers/char/agp/intel-agp.c | 30 +++++++++++++++++++++++------- drivers/gpu/drm/i915/i915_gem.c | 10 ---------- 3 files changed, 24 insertions(+), 17 deletions(-) (limited to 'drivers/char') diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c index 7e600c1962db..5866b28eede1 100644 --- a/arch/x86/mm/pageattr.c +++ b/arch/x86/mm/pageattr.c @@ -143,6 +143,7 @@ void clflush_cache_range(void *vaddr, unsigned int size) mb(); } +EXPORT_SYMBOL_GPL(clflush_cache_range); static void __cpa_flush_all(void *arg) { diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index c17291715031..e8dc75fc33cc 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -682,23 +682,39 @@ static void intel_i830_setup_flush(void) if (!intel_private.i8xx_page) return; - /* make page uncached */ - map_page_into_agp(intel_private.i8xx_page); - intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page); if (!intel_private.i8xx_flush_page) intel_i830_fini_flush(); } +static void +do_wbinvd(void *null) +{ + wbinvd(); +} + +/* The chipset_flush interface needs to get data that has already been + * flushed out of the CPU all the way out to main memory, because the GPU + * doesn't snoop those buffers. + * + * The 8xx series doesn't have the same lovely interface for flushing the + * chipset write buffers that the later chips do. According to the 865 + * specs, it's 64 octwords, or 1KB. So, to get those previous things in + * that buffer out, we just fill 1KB and clflush it out, on the assumption + * that it'll push whatever was in there out. It appears to work. + */ static void intel_i830_chipset_flush(struct agp_bridge_data *bridge) { unsigned int *pg = intel_private.i8xx_flush_page; - int i; - for (i = 0; i < 256; i += 2) - *(pg + i) = i; + memset(pg, 0, 1024); - wmb(); + if (cpu_has_clflush) { + clflush_cache_range(pg, 1024); + } else { + if (on_each_cpu(do_wbinvd, NULL, 1) != 0) + printk(KERN_ERR "Timed out waiting for cache flush.\n"); + } } /* The intel i830 automatically initializes the agp aperture during POST. diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f3758f9fc979..30ea4b6b0219 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2511,16 +2511,6 @@ i915_gem_clflush_object(struct drm_gem_object *obj) if (obj_priv->pages == NULL) return; - /* XXX: The 865 in particular appears to be weird in how it handles - * cache flushing. We haven't figured it out, but the - * clflush+agp_chipset_flush doesn't appear to successfully get the - * data visible to the PGU, while wbinvd + agp_chipset_flush does. - */ - if (IS_I865G(obj->dev)) { - wbinvd(); - return; - } - drm_clflush_pages(obj_priv->pages, obj->size / PAGE_SIZE); } -- cgit v1.2.3 From 121264827656f5f06328b17983c796af17dc5949 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Mon, 14 Sep 2009 10:47:06 +0800 Subject: agp/intel: remove restore in resume As early pci resume has already restored config for host bridge and graphics device, don't need to restore it again, This removes an original order hack for graphics device restore. This fixed the resume hang issue found by Alan Stern on 845G, caused by extra config restore on graphics device. Cc: Stable Team Cc: Alan Stern Signed-off-by: Zhenyu Wang Signed-off-by: Dave Airlie --- drivers/char/agp/intel-agp.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 9bc3a0b82b96..5eeaeeeaa2cc 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -2451,15 +2451,6 @@ static int agp_intel_resume(struct pci_dev *pdev) struct agp_bridge_data *bridge = pci_get_drvdata(pdev); int ret_val; - pci_restore_state(pdev); - - /* We should restore our graphics device's config space, - * as host bridge (00:00) resumes before graphics device (02:00), - * then our access to its pci space can work right. - */ - if (intel_private.pcidev) - pci_restore_state(intel_private.pcidev); - if (bridge->driver == &intel_generic_driver) intel_configure(); else if (bridge->driver == &intel_850_driver) -- cgit v1.2.3 From eef99380679e20e7edc096aa4d8a98b875404d79 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 20 Aug 2009 17:43:41 +0200 Subject: vfs: Rename generic_file_aio_write_nolock generic_file_aio_write_nolock() is now used only by block devices and raw character device. Filesystems should use __generic_file_aio_write() in case generic_file_aio_write() doesn't suit them. So rename the function to blkdev_aio_write() and move it to fs/blockdev.c. Signed-off-by: Christoph Hellwig Signed-off-by: Jan Kara --- drivers/char/raw.c | 2 +- fs/block_dev.c | 29 ++++++++++++++++++++++++++++- include/linux/fs.h | 6 ++++-- mm/filemap.c | 39 --------------------------------------- 4 files changed, 33 insertions(+), 43 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 05f9d18b9361..40268db02e22 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -246,7 +246,7 @@ static const struct file_operations raw_fops = { .read = do_sync_read, .aio_read = generic_file_aio_read, .write = do_sync_write, - .aio_write = generic_file_aio_write_nolock, + .aio_write = blkdev_aio_write, .open = raw_open, .release= raw_release, .ioctl = raw_ioctl, diff --git a/fs/block_dev.c b/fs/block_dev.c index 94dfda24c06e..3581a4e53942 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1404,6 +1404,33 @@ static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg) return blkdev_ioctl(bdev, mode, cmd, arg); } +/* + * Write data to the block device. Only intended for the block device itself + * and the raw driver which basically is a fake block device. + * + * Does not take i_mutex for the write and thus is not for general purpose + * use. + */ +ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos) +{ + struct file *file = iocb->ki_filp; + ssize_t ret; + + BUG_ON(iocb->ki_pos != pos); + + ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos); + if (ret > 0 || ret == -EIOCBQUEUED) { + ssize_t err; + + err = generic_write_sync(file, pos, ret); + if (err < 0 && ret > 0) + ret = err; + } + return ret; +} +EXPORT_SYMBOL_GPL(blkdev_aio_write); + /* * Try to release a page associated with block device when the system * is under memory pressure. @@ -1436,7 +1463,7 @@ const struct file_operations def_blk_fops = { .read = do_sync_read, .write = do_sync_write, .aio_read = generic_file_aio_read, - .aio_write = generic_file_aio_write_nolock, + .aio_write = blkdev_aio_write, .mmap = generic_file_mmap, .fsync = block_fsync, .unlocked_ioctl = block_ioctl, diff --git a/include/linux/fs.h b/include/linux/fs.h index ea099d3a18d9..6c1be3a4edea 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2207,8 +2207,6 @@ extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsig extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t *); extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t); -extern ssize_t generic_file_aio_write_nolock(struct kiocb *, const struct iovec *, - unsigned long, loff_t); extern ssize_t generic_file_direct_write(struct kiocb *, const struct iovec *, unsigned long *, loff_t, loff_t *, size_t, size_t); extern ssize_t generic_file_buffered_write(struct kiocb *, const struct iovec *, @@ -2218,6 +2216,10 @@ extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t l extern int generic_segment_checks(const struct iovec *iov, unsigned long *nr_segs, size_t *count, int access_flags); +/* fs/block_dev.c */ +extern ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov, + unsigned long nr_segs, loff_t pos); + /* fs/splice.c */ extern ssize_t generic_file_splice_read(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); diff --git a/mm/filemap.c b/mm/filemap.c index f863e1d7e227..3587554f45ef 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2462,45 +2462,6 @@ out: } EXPORT_SYMBOL(__generic_file_aio_write); - -/** - * generic_file_aio_write_nolock - write data, usually to a device - * @iocb: IO state structure - * @iov: vector with data to write - * @nr_segs: number of segments in the vector - * @pos: position in file where to write - * - * This is a wrapper around __generic_file_aio_write() which takes care of - * syncing the file in case of O_SYNC file. It does not take i_mutex for the - * write itself but may do so during syncing. It is meant for users like block - * devices which do not need i_mutex during write. If your filesystem needs to - * do a write but already holds i_mutex, use __generic_file_aio_write() - * directly and then sync the file like generic_file_aio_write(). - */ -ssize_t generic_file_aio_write_nolock(struct kiocb *iocb, - const struct iovec *iov, unsigned long nr_segs, loff_t pos) -{ - struct file *file = iocb->ki_filp; - struct address_space *mapping = file->f_mapping; - struct inode *inode = mapping->host; - ssize_t ret; - - BUG_ON(iocb->ki_pos != pos); - - ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos); - - if ((ret > 0 || ret == -EIOCBQUEUED) && - ((file->f_flags & O_SYNC) || IS_SYNC(inode))) { - ssize_t err; - - err = sync_page_range_nolock(inode, mapping, pos, ret); - if (err < 0 && ret > 0) - ret = err; - } - return ret; -} -EXPORT_SYMBOL(generic_file_aio_write_nolock); - /** * generic_file_aio_write - write data to a file * @iocb: IO state structure -- cgit v1.2.3 From 353f6dd2dec992ddd34620a94b051b0f76227379 Mon Sep 17 00:00:00 2001 From: Anirban Sinha Date: Mon, 14 Sep 2009 11:13:37 -0700 Subject: cleanup console_print() console_print() is an old legacy interface mostly unused in the entire kernel tree. It's best to clean up its existing use and let developers use their own implementation of it as they feel fit. Signed-off-by: Anirban Sinha Signed-off-by: Linus Torvalds --- arch/ia64/kernel/head.S | 1 + arch/ia64/kernel/head.h | 1 + arch/ia64/kernel/process.c | 7 +++++++ drivers/char/serial167.c | 5 ++--- include/linux/dtlk.h | 19 ------------------- include/linux/tty.h | 4 ---- kernel/printk.c | 6 ------ 7 files changed, 11 insertions(+), 32 deletions(-) create mode 100644 arch/ia64/kernel/head.h (limited to 'drivers/char') diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S index 23f846de62d5..e6c5c3d5e1f8 100644 --- a/arch/ia64/kernel/head.S +++ b/arch/ia64/kernel/head.S @@ -34,6 +34,7 @@ #include #include #include +#include "head.h" #ifdef CONFIG_HOTPLUG_CPU #define SAL_PSR_BITS_TO_SET \ diff --git a/arch/ia64/kernel/head.h b/arch/ia64/kernel/head.h new file mode 100644 index 000000000000..2e2ac6824e65 --- /dev/null +++ b/arch/ia64/kernel/head.h @@ -0,0 +1 @@ +extern void console_print(const char *s); diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index 89969e950045..9bcec9945c12 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -161,6 +161,13 @@ show_regs (struct pt_regs *regs) show_stack(NULL, NULL); } +/* local support for deprecated console_print */ +void +console_print(const char *s) +{ + printk(KERN_EMERG "%s", s); +} + void do_notify_resume_user(sigset_t *unused, struct sigscratch *scr, long in_syscall) { diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c index 51e7a46787be..5942a9d674c0 100644 --- a/drivers/char/serial167.c +++ b/drivers/char/serial167.c @@ -171,7 +171,6 @@ static int startup(struct cyclades_port *); static void cy_throttle(struct tty_struct *); static void cy_unthrottle(struct tty_struct *); static void config_setup(struct cyclades_port *); -extern void console_print(const char *); #ifdef CYCLOM_SHOW_STATUS static void show_status(int); #endif @@ -245,7 +244,7 @@ void SP(char *data) { unsigned long flags; local_irq_save(flags); - console_print(data); + printk(KERN_EMERG "%s", data); local_irq_restore(flags); } @@ -255,7 +254,7 @@ void CP(char data) unsigned long flags; local_irq_save(flags); scrn[0] = data; - console_print(scrn); + printk(KERN_EMERG "%c", scrn); local_irq_restore(flags); } /* CP */ diff --git a/include/linux/dtlk.h b/include/linux/dtlk.h index 2896d90118a9..22a7b9a5f5d1 100644 --- a/include/linux/dtlk.h +++ b/include/linux/dtlk.h @@ -1,22 +1,3 @@ -#if 0 - -#define TRACE_TXT(text) \ - { \ - if(dtlk_trace) \ - { \ - console_print(text); \ - console_print("\n"); \ - } \ - } - -#define TRACE_CHR(chr) \ - { \ - if(dtlk_trace) \ - console_print(chr); \ - } \ - -#endif - #define DTLK_MINOR 0 #define DTLK_IO_EXTENT 0x02 diff --git a/include/linux/tty.h b/include/linux/tty.h index 0d3974f59c53..a916a318004e 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -519,10 +519,6 @@ extern void serial_console_init(void); extern int pcxe_open(struct tty_struct *tty, struct file *filp); -/* printk.c */ - -extern void console_print(const char *); - /* vt.c */ extern int vt_ioctl(struct tty_struct *tty, struct file *file, diff --git a/kernel/printk.c b/kernel/printk.c index e10d193a833a..602033acd6c7 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -1075,12 +1075,6 @@ void __sched console_conditional_schedule(void) } EXPORT_SYMBOL(console_conditional_schedule); -void console_print(const char *s) -{ - printk(KERN_EMERG "%s", s); -} -EXPORT_SYMBOL(console_print); - void console_unblank(void) { struct console *c; -- cgit v1.2.3 From a81a8f580b6b525112aba19319dcc5dd3db4dfee Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 4 Sep 2009 16:58:05 -0700 Subject: [IA64] mbcs: fix printk format warnings drivers/char/mbcs.c:719: warning: format '%lx' expects type 'long unsigned int', but argument 3 has type 'uint64_t' drivers/char/mbcs.c:719: warning: format '%lx' expects type 'long unsigned int', but argument 4 has type 'uint64_t' Signed-off-by: Randy Dunlap Cc: Bruce Losure Signed-off-by: Tony Luck --- drivers/char/mbcs.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c index acd8e9ed474a..87c67b42bc08 100644 --- a/drivers/char/mbcs.c +++ b/drivers/char/mbcs.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -715,8 +716,8 @@ static ssize_t show_algo(struct device *dev, struct device_attribute *attr, char */ debug0 = *(uint64_t *) soft->debug_addr; - return sprintf(buf, "0x%lx 0x%lx\n", - (debug0 >> 32), (debug0 & 0xffffffff)); + return sprintf(buf, "0x%x 0x%x\n", + upper_32_bits(debug0), lower_32_bits(debug0)); } static ssize_t store_algo(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -- cgit v1.2.3 From 389e0cb9a1dec22ec37a104dec5009dd7a33dd59 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sat, 4 Jul 2009 16:51:29 +0200 Subject: mem_class: use minor as index instead of searching the array Declare the device list with the minor numbers as the index, which saves us from searching for a matching list entry. Remove old devfs permissions declaration. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 82 ++++++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 42 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 645237bda682..bed3503184e4 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -864,71 +864,67 @@ static const struct file_operations kmsg_fops = { .write = kmsg_write, }; -static const struct { - unsigned int minor; - char *name; - umode_t mode; - const struct file_operations *fops; - struct backing_dev_info *dev_info; -} devlist[] = { /* list of minor devices */ - {1, "mem", S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops, - &directly_mappable_cdev_bdi}, +static const struct memdev { + const char *name; + const struct file_operations *fops; + struct backing_dev_info *dev_info; +} devlist[] = { + [ 1] = { "mem", &mem_fops, &directly_mappable_cdev_bdi }, #ifdef CONFIG_DEVKMEM - {2, "kmem", S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops, - &directly_mappable_cdev_bdi}, + [ 2] = { "kmem", &kmem_fops, &directly_mappable_cdev_bdi }, #endif - {3, "null", S_IRUGO | S_IWUGO, &null_fops, NULL}, + [ 3] = {"null", &null_fops, NULL }, #ifdef CONFIG_DEVPORT - {4, "port", S_IRUSR | S_IWUSR | S_IRGRP, &port_fops, NULL}, + [ 4] = { "port", &port_fops, NULL }, #endif - {5, "zero", S_IRUGO | S_IWUGO, &zero_fops, &zero_bdi}, - {7, "full", S_IRUGO | S_IWUGO, &full_fops, NULL}, - {8, "random", S_IRUGO | S_IWUSR, &random_fops, NULL}, - {9, "urandom", S_IRUGO | S_IWUSR, &urandom_fops, NULL}, - {11,"kmsg", S_IRUGO | S_IWUSR, &kmsg_fops, NULL}, + [ 5] = { "zero", &zero_fops, &zero_bdi }, + [ 6] = { "full", &full_fops, NULL }, + [ 7] = { "random", &random_fops, NULL }, + [ 9] = { "urandom", &urandom_fops, NULL }, + [11] = { "kmsg", &kmsg_fops, NULL }, #ifdef CONFIG_CRASH_DUMP - {12,"oldmem", S_IRUSR | S_IWUSR | S_IRGRP, &oldmem_fops, NULL}, + [12] = { "oldmem", &oldmem_fops, NULL }, #endif }; static int memory_open(struct inode *inode, struct file *filp) { - int ret = 0; - int i; + int minor; + const struct memdev *dev; + int ret = -ENXIO; lock_kernel(); - for (i = 0; i < ARRAY_SIZE(devlist); i++) { - if (devlist[i].minor == iminor(inode)) { - filp->f_op = devlist[i].fops; - if (devlist[i].dev_info) { - filp->f_mapping->backing_dev_info = - devlist[i].dev_info; - } + minor = iminor(inode); + if (minor >= ARRAY_SIZE(devlist)) + goto out; - break; - } - } + dev = &devlist[minor]; + if (!dev->fops) + goto out; - if (i == ARRAY_SIZE(devlist)) - ret = -ENXIO; - else - if (filp->f_op && filp->f_op->open) - ret = filp->f_op->open(inode, filp); + filp->f_op = dev->fops; + if (dev->dev_info) + filp->f_mapping->backing_dev_info = dev->dev_info; + if (dev->fops->open) + ret = dev->fops->open(inode, filp); + else + ret = 0; +out: unlock_kernel(); return ret; } static const struct file_operations memory_fops = { - .open = memory_open, /* just a selector for the real open */ + .open = memory_open, }; static struct class *mem_class; static int __init chr_dev_init(void) { - int i; + int minor; int err; err = bdi_init(&zero_bdi); @@ -939,10 +935,12 @@ static int __init chr_dev_init(void) printk("unable to get major %d for memory devs\n", MEM_MAJOR); mem_class = class_create(THIS_MODULE, "mem"); - for (i = 0; i < ARRAY_SIZE(devlist); i++) - device_create(mem_class, NULL, - MKDEV(MEM_MAJOR, devlist[i].minor), NULL, - devlist[i].name); + for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) { + if (!devlist[minor].name) + continue; + device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor), + NULL, devlist[minor].name); + } return 0; } -- cgit v1.2.3 From 162dd4212409fd2d36ff22547ea821bf3e86bcc9 Mon Sep 17 00:00:00 2001 From: Jin Dongming Date: Mon, 14 Sep 2009 16:02:26 +0900 Subject: mem_class: fix bug When I build and boot -next on fedora 10, I can not login anymore. When I input the user name and password, the system does not output any message and requires user to input the user name and password again and again. I find the patch which caused this problem with "GIT BISECT" command. And the patch is commit 7c4b7daa1878972ed0137c95f23569124bd6e2b1 "mem_class: use minor as index instead of searching the array". Though I don't know the real reason why user could not login, I confirmed the patch I made as following could resolve the problem on fedora 10. Signed-off-by: Jin Dongming Acked-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/char/mem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index bed3503184e4..0491cdf63f2a 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -878,8 +878,8 @@ static const struct memdev { [ 4] = { "port", &port_fops, NULL }, #endif [ 5] = { "zero", &zero_fops, &zero_bdi }, - [ 6] = { "full", &full_fops, NULL }, - [ 7] = { "random", &random_fops, NULL }, + [ 7] = { "full", &full_fops, NULL }, + [ 8] = { "random", &random_fops, NULL }, [ 9] = { "urandom", &urandom_fops, NULL }, [11] = { "kmsg", &kmsg_fops, NULL }, #ifdef CONFIG_CRASH_DUMP -- cgit v1.2.3 From 09d3f3f0e02c8a900d076c302c5c02227f33572d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 15 Sep 2009 17:04:38 -0700 Subject: sparc: Kill PROM console driver. Many years ago when this driver was written, it had a use, but these days it's nothing but trouble and distributions should not enable it in any situation. Pretty much every console device a sparc machine could see has a bonafide real driver, making the PROM console hack unnecessary. If any new device shows up, we should write a driver instead of depending upon this crutch to save us. We've been able to take care of this even when no chip documentation exists (sunxvr500, sunxvr2500) so there are no excuses. Signed-off-by: David S. Miller --- Documentation/dontdiff | 1 - arch/sparc/kernel/setup_32.c | 2 - arch/sparc/kernel/setup_64.c | 2 - drivers/char/vt.c | 3 - drivers/video/console/.gitignore | 2 - drivers/video/console/Kconfig | 9 +- drivers/video/console/Makefile | 12 - drivers/video/console/prom.uni | 11 - drivers/video/console/promcon.c | 598 --------------------------------------- scripts/Makefile | 1 - 10 files changed, 1 insertion(+), 640 deletions(-) delete mode 100644 drivers/video/console/.gitignore delete mode 100644 drivers/video/console/prom.uni delete mode 100644 drivers/video/console/promcon.c (limited to 'drivers/char') diff --git a/Documentation/dontdiff b/Documentation/dontdiff index 88519daab6e9..e1efc400bed6 100644 --- a/Documentation/dontdiff +++ b/Documentation/dontdiff @@ -152,7 +152,6 @@ piggy.gz piggyback pnmtologo ppc_defs.h* -promcon_tbl.c pss_boot.h qconf raid6altivec*.c diff --git a/arch/sparc/kernel/setup_32.c b/arch/sparc/kernel/setup_32.c index 16a47ffe03c1..9be2af55c5cd 100644 --- a/arch/sparc/kernel/setup_32.c +++ b/arch/sparc/kernel/setup_32.c @@ -268,8 +268,6 @@ void __init setup_arch(char **cmdline_p) #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; -#elif defined(CONFIG_PROM_CONSOLE) - conswitchp = &prom_con; #endif boot_flags_init(*cmdline_p); diff --git a/arch/sparc/kernel/setup_64.c b/arch/sparc/kernel/setup_64.c index f2bcfd2967d7..21180339cb09 100644 --- a/arch/sparc/kernel/setup_64.c +++ b/arch/sparc/kernel/setup_64.c @@ -295,8 +295,6 @@ void __init setup_arch(char **cmdline_p) #ifdef CONFIG_DUMMY_CONSOLE conswitchp = &dummy_con; -#elif defined(CONFIG_PROM_CONSOLE) - conswitchp = &prom_con; #endif idprom_init(); diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 404f4c1ee431..6aa88f50b039 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2948,9 +2948,6 @@ int __init vty_init(const struct file_operations *console_fops) panic("Couldn't register console driver\n"); kbd_init(); console_map_init(); -#ifdef CONFIG_PROM_CONSOLE - prom_con_init(); -#endif #ifdef CONFIG_MDA_CONSOLE mda_console_init(); #endif diff --git a/drivers/video/console/.gitignore b/drivers/video/console/.gitignore deleted file mode 100644 index 0c258b45439c..000000000000 --- a/drivers/video/console/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# conmakehash generated file -promcon_tbl.c diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index 2f50a80b413e..fc7d9bbb548c 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -67,16 +67,9 @@ config SGI_NEWPORT_CONSOLE # bool 'IODC console' CONFIG_IODC_CONSOLE -config PROM_CONSOLE - bool "PROM console" - depends on SPARC - help - Say Y to build a console driver for Sun machines that uses the - terminal emulation built into their console PROMS. - config DUMMY_CONSOLE bool - depends on PROM_CONSOLE!=y || VGA_CONSOLE!=y || SGI_NEWPORT_CONSOLE!=y + depends on VGA_CONSOLE!=y || SGI_NEWPORT_CONSOLE!=y default y config DUMMY_CONSOLE_COLUMNS diff --git a/drivers/video/console/Makefile b/drivers/video/console/Makefile index ac46cc3f6a2a..a862e9173ebe 100644 --- a/drivers/video/console/Makefile +++ b/drivers/video/console/Makefile @@ -22,7 +22,6 @@ font-objs += $(font-objs-y) obj-$(CONFIG_DUMMY_CONSOLE) += dummycon.o obj-$(CONFIG_SGI_NEWPORT_CONSOLE) += newport_con.o font.o -obj-$(CONFIG_PROM_CONSOLE) += promcon.o promcon_tbl.o obj-$(CONFIG_STI_CONSOLE) += sticon.o sticore.o font.o obj-$(CONFIG_VGA_CONSOLE) += vgacon.o obj-$(CONFIG_MDA_CONSOLE) += mdacon.o @@ -40,14 +39,3 @@ obj-$(CONFIG_FB_STI) += sticore.o font.o ifeq ($(CONFIG_USB_SISUSBVGA_CON),y) obj-$(CONFIG_USB_SISUSBVGA) += font.o endif - -# Targets that kbuild needs to know about -targets := promcon_tbl.c - -quiet_cmd_conmakehash = CNMKHSH $@ - cmd_conmakehash = scripts/conmakehash $< | \ - sed -e '/\#include <[^>]*>/p' -e 's/types/init/' \ - -e 's/dfont\(_uni.*\]\)/promfont\1 /' > $@ - -$(obj)/promcon_tbl.c: $(src)/prom.uni - $(call cmd,conmakehash) diff --git a/drivers/video/console/prom.uni b/drivers/video/console/prom.uni deleted file mode 100644 index 58f9c04ed9d3..000000000000 --- a/drivers/video/console/prom.uni +++ /dev/null @@ -1,11 +0,0 @@ -# -# Unicode mapping table for font in Sun PROM -# -# -0x20-0x7e idem -0xa0-0xff idem -# -0x7c U+2502 -0x2d U+2500 -0x2b U+250c U+2510 U+2514 U+2518 U+251c U+2524 U+252c U+2534 U+253c -0xa4 U+fffd diff --git a/drivers/video/console/promcon.c b/drivers/video/console/promcon.c deleted file mode 100644 index ae02e4eb18e7..000000000000 --- a/drivers/video/console/promcon.c +++ /dev/null @@ -1,598 +0,0 @@ -/* $Id: promcon.c,v 1.17 2000/07/26 23:02:52 davem Exp $ - * Console driver utilizing PROM sun terminal emulation - * - * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) - * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static short pw = 80 - 1, ph = 34 - 1; -static short px, py; -static unsigned long promcon_uni_pagedir[2]; - -extern u8 promfont_unicount[]; -extern u16 promfont_unitable[]; - -#define PROMCON_COLOR 0 - -#if PROMCON_COLOR -#define inverted(s) ((((s) & 0x7700) == 0x0700) ? 0 : 1) -#else -#define inverted(s) (((s) & 0x0800) ? 1 : 0) -#endif - -static __inline__ void -promcon_puts(char *buf, int cnt) -{ - prom_printf("%*.*s", cnt, cnt, buf); -} - -static int -promcon_start(struct vc_data *conp, char *b) -{ - unsigned short *s = (unsigned short *) - (conp->vc_origin + py * conp->vc_size_row + (px << 1)); - u16 cs; - - cs = scr_readw(s); - if (px == pw) { - unsigned short *t = s - 1; - u16 ct = scr_readw(t); - - if (inverted(cs) && inverted(ct)) - return sprintf(b, "\b\033[7m%c\b\033[@%c\033[m", cs, - ct); - else if (inverted(cs)) - return sprintf(b, "\b\033[7m%c\033[m\b\033[@%c", cs, - ct); - else if (inverted(ct)) - return sprintf(b, "\b%c\b\033[@\033[7m%c\033[m", cs, - ct); - else - return sprintf(b, "\b%c\b\033[@%c", cs, ct); - } - - if (inverted(cs)) - return sprintf(b, "\033[7m%c\033[m\b", cs); - else - return sprintf(b, "%c\b", cs); -} - -static int -promcon_end(struct vc_data *conp, char *b) -{ - unsigned short *s = (unsigned short *) - (conp->vc_origin + py * conp->vc_size_row + (px << 1)); - char *p = b; - u16 cs; - - b += sprintf(b, "\033[%d;%dH", py + 1, px + 1); - - cs = scr_readw(s); - if (px == pw) { - unsigned short *t = s - 1; - u16 ct = scr_readw(t); - - if (inverted(cs) && inverted(ct)) - b += sprintf(b, "\b%c\b\033[@\033[7m%c\033[m", cs, ct); - else if (inverted(cs)) - b += sprintf(b, "\b%c\b\033[@%c", cs, ct); - else if (inverted(ct)) - b += sprintf(b, "\b\033[7m%c\b\033[@%c\033[m", cs, ct); - else - b += sprintf(b, "\b\033[7m%c\033[m\b\033[@%c", cs, ct); - return b - p; - } - - if (inverted(cs)) - b += sprintf(b, "%c\b", cs); - else - b += sprintf(b, "\033[7m%c\033[m\b", cs); - return b - p; -} - -const char *promcon_startup(void) -{ - const char *display_desc = "PROM"; - int node; - char buf[40]; - - node = prom_getchild(prom_root_node); - node = prom_searchsiblings(node, "options"); - if (prom_getproperty(node, "screen-#columns", buf, 40) != -1) { - pw = simple_strtoul(buf, NULL, 0); - if (pw < 10 || pw > 256) - pw = 80; - pw--; - } - if (prom_getproperty(node, "screen-#rows", buf, 40) != -1) { - ph = simple_strtoul(buf, NULL, 0); - if (ph < 10 || ph > 256) - ph = 34; - ph--; - } - promcon_puts("\033[H\033[J", 6); - return display_desc; -} - -static void -promcon_init_unimap(struct vc_data *conp) -{ - mm_segment_t old_fs = get_fs(); - struct unipair *p, *p1; - u16 *q; - int i, j, k; - - p = kmalloc(256*sizeof(struct unipair), GFP_KERNEL); - if (!p) return; - - q = promfont_unitable; - p1 = p; - k = 0; - for (i = 0; i < 256; i++) - for (j = promfont_unicount[i]; j; j--) { - p1->unicode = *q++; - p1->fontpos = i; - p1++; - k++; - } - set_fs(KERNEL_DS); - con_clear_unimap(conp, NULL); - con_set_unimap(conp, k, p); - con_protect_unimap(conp, 1); - set_fs(old_fs); - kfree(p); -} - -static void -promcon_init(struct vc_data *conp, int init) -{ - unsigned long p; - - conp->vc_can_do_color = PROMCON_COLOR; - if (init) { - conp->vc_cols = pw + 1; - conp->vc_rows = ph + 1; - } - p = *conp->vc_uni_pagedir_loc; - if (conp->vc_uni_pagedir_loc == &conp->vc_uni_pagedir || - !--conp->vc_uni_pagedir_loc[1]) - con_free_unimap(conp); - conp->vc_uni_pagedir_loc = promcon_uni_pagedir; - promcon_uni_pagedir[1]++; - if (!promcon_uni_pagedir[0] && p) { - promcon_init_unimap(conp); - } - if (!init) { - if (conp->vc_cols != pw + 1 || conp->vc_rows != ph + 1) - vc_resize(conp, pw + 1, ph + 1); - } -} - -static void -promcon_deinit(struct vc_data *conp) -{ - /* When closing the last console, reset video origin */ - if (!--promcon_uni_pagedir[1]) - con_free_unimap(conp); - conp->vc_uni_pagedir_loc = &conp->vc_uni_pagedir; - con_set_default_unimap(conp); -} - -static int -promcon_switch(struct vc_data *conp) -{ - return 1; -} - -static unsigned short * -promcon_repaint_line(unsigned short *s, unsigned char *buf, unsigned char **bp) -{ - int cnt = pw + 1; - int attr = -1; - unsigned char *b = *bp; - - while (cnt--) { - u16 c = scr_readw(s); - if (attr != inverted(c)) { - attr = inverted(c); - if (attr) { - strcpy (b, "\033[7m"); - b += 4; - } else { - strcpy (b, "\033[m"); - b += 3; - } - } - *b++ = c; - s++; - if (b - buf >= 224) { - promcon_puts(buf, b - buf); - b = buf; - } - } - *bp = b; - return s; -} - -static void -promcon_putcs(struct vc_data *conp, const unsigned short *s, - int count, int y, int x) -{ - unsigned char buf[256], *b = buf; - unsigned short attr = scr_readw(s); - unsigned char save; - int i, last = 0; - - if (console_blanked) - return; - - if (count <= 0) - return; - - b += promcon_start(conp, b); - - if (x + count >= pw + 1) { - if (count == 1) { - x -= 1; - save = scr_readw((unsigned short *)(conp->vc_origin - + y * conp->vc_size_row - + (x << 1))); - - if (px != x || py != y) { - b += sprintf(b, "\033[%d;%dH", y + 1, x + 1); - px = x; - py = y; - } - - if (inverted(attr)) - b += sprintf(b, "\033[7m%c\033[m", scr_readw(s++)); - else - b += sprintf(b, "%c", scr_readw(s++)); - - strcpy(b, "\b\033[@"); - b += 4; - - if (inverted(save)) - b += sprintf(b, "\033[7m%c\033[m", save); - else - b += sprintf(b, "%c", save); - - px++; - - b += promcon_end(conp, b); - promcon_puts(buf, b - buf); - return; - } else { - last = 1; - count = pw - x - 1; - } - } - - if (inverted(attr)) { - strcpy(b, "\033[7m"); - b += 4; - } - - if (px != x || py != y) { - b += sprintf(b, "\033[%d;%dH", y + 1, x + 1); - px = x; - py = y; - } - - for (i = 0; i < count; i++) { - if (b - buf >= 224) { - promcon_puts(buf, b - buf); - b = buf; - } - *b++ = scr_readw(s++); - } - - px += count; - - if (last) { - save = scr_readw(s++); - b += sprintf(b, "%c\b\033[@%c", scr_readw(s++), save); - px++; - } - - if (inverted(attr)) { - strcpy(b, "\033[m"); - b += 3; - } - - b += promcon_end(conp, b); - promcon_puts(buf, b - buf); -} - -static void -promcon_putc(struct vc_data *conp, int c, int y, int x) -{ - unsigned short s; - - if (console_blanked) - return; - - scr_writew(c, &s); - promcon_putcs(conp, &s, 1, y, x); -} - -static void -promcon_clear(struct vc_data *conp, int sy, int sx, int height, int width) -{ - unsigned char buf[256], *b = buf; - int i, j; - - if (console_blanked) - return; - - b += promcon_start(conp, b); - - if (!sx && width == pw + 1) { - - if (!sy && height == ph + 1) { - strcpy(b, "\033[H\033[J"); - b += 6; - b += promcon_end(conp, b); - promcon_puts(buf, b - buf); - return; - } else if (sy + height == ph + 1) { - b += sprintf(b, "\033[%dH\033[J", sy + 1); - b += promcon_end(conp, b); - promcon_puts(buf, b - buf); - return; - } - - b += sprintf(b, "\033[%dH", sy + 1); - for (i = 1; i < height; i++) { - strcpy(b, "\033[K\n"); - b += 4; - } - - strcpy(b, "\033[K"); - b += 3; - - b += promcon_end(conp, b); - promcon_puts(buf, b - buf); - return; - - } else if (sx + width == pw + 1) { - - b += sprintf(b, "\033[%d;%dH", sy + 1, sx + 1); - for (i = 1; i < height; i++) { - strcpy(b, "\033[K\n"); - b += 4; - } - - strcpy(b, "\033[K"); - b += 3; - - b += promcon_end(conp, b); - promcon_puts(buf, b - buf); - return; - } - - for (i = sy + 1; i <= sy + height; i++) { - b += sprintf(b, "\033[%d;%dH", i, sx + 1); - for (j = 0; j < width; j++) - *b++ = ' '; - if (b - buf + width >= 224) { - promcon_puts(buf, b - buf); - b = buf; - } - } - - b += promcon_end(conp, b); - promcon_puts(buf, b - buf); -} - -static void -promcon_bmove(struct vc_data *conp, int sy, int sx, int dy, int dx, - int height, int width) -{ - char buf[256], *b = buf; - - if (console_blanked) - return; - - b += promcon_start(conp, b); - if (sy == dy && height == 1) { - if (dx > sx && dx + width == conp->vc_cols) - b += sprintf(b, "\033[%d;%dH\033[%d@\033[%d;%dH", - sy + 1, sx + 1, dx - sx, py + 1, px + 1); - else if (dx < sx && sx + width == conp->vc_cols) - b += sprintf(b, "\033[%d;%dH\033[%dP\033[%d;%dH", - dy + 1, dx + 1, sx - dx, py + 1, px + 1); - - b += promcon_end(conp, b); - promcon_puts(buf, b - buf); - return; - } - - /* - * FIXME: What to do here??? - * Current console.c should not call it like that ever. - */ - prom_printf("\033[7mFIXME: bmove not handled\033[m\n"); -} - -static void -promcon_cursor(struct vc_data *conp, int mode) -{ - char buf[32], *b = buf; - - switch (mode) { - case CM_ERASE: - break; - - case CM_MOVE: - case CM_DRAW: - b += promcon_start(conp, b); - if (px != conp->vc_x || py != conp->vc_y) { - px = conp->vc_x; - py = conp->vc_y; - b += sprintf(b, "\033[%d;%dH", py + 1, px + 1); - } - promcon_puts(buf, b - buf); - break; - } -} - -static int -promcon_blank(struct vc_data *conp, int blank, int mode_switch) -{ - if (blank) { - promcon_puts("\033[H\033[J\033[7m \033[m\b", 15); - return 0; - } else { - /* Let console.c redraw */ - return 1; - } -} - -static int -promcon_scroll(struct vc_data *conp, int t, int b, int dir, int count) -{ - unsigned char buf[256], *p = buf; - unsigned short *s; - int i; - - if (console_blanked) - return 0; - - p += promcon_start(conp, p); - - switch (dir) { - case SM_UP: - if (b == ph + 1) { - p += sprintf(p, "\033[%dH\033[%dM", t + 1, count); - px = 0; - py = t; - p += promcon_end(conp, p); - promcon_puts(buf, p - buf); - break; - } - - s = (unsigned short *)(conp->vc_origin - + (t + count) * conp->vc_size_row); - - p += sprintf(p, "\033[%dH", t + 1); - - for (i = t; i < b - count; i++) - s = promcon_repaint_line(s, buf, &p); - - for (; i < b - 1; i++) { - strcpy(p, "\033[K\n"); - p += 4; - if (p - buf >= 224) { - promcon_puts(buf, p - buf); - p = buf; - } - } - - strcpy(p, "\033[K"); - p += 3; - - p += promcon_end(conp, p); - promcon_puts(buf, p - buf); - break; - - case SM_DOWN: - if (b == ph + 1) { - p += sprintf(p, "\033[%dH\033[%dL", t + 1, count); - px = 0; - py = t; - p += promcon_end(conp, p); - promcon_puts(buf, p - buf); - break; - } - - s = (unsigned short *)(conp->vc_origin + t * conp->vc_size_row); - - p += sprintf(p, "\033[%dH", t + 1); - - for (i = t; i < t + count; i++) { - strcpy(p, "\033[K\n"); - p += 4; - if (p - buf >= 224) { - promcon_puts(buf, p - buf); - p = buf; - } - } - - for (; i < b; i++) - s = promcon_repaint_line(s, buf, &p); - - p += promcon_end(conp, p); - promcon_puts(buf, p - buf); - break; - } - - return 0; -} - -#if !(PROMCON_COLOR) -static u8 promcon_build_attr(struct vc_data *conp, u8 _color, u8 _intensity, - u8 _blink, u8 _underline, u8 _reverse, u8 _italic) -{ - return (_reverse) ? 0xf : 0x7; -} -#endif - -/* - * The console 'switch' structure for the VGA based console - */ - -static int promcon_dummy(void) -{ - return 0; -} - -#define DUMMY (void *) promcon_dummy - -const struct consw prom_con = { - .owner = THIS_MODULE, - .con_startup = promcon_startup, - .con_init = promcon_init, - .con_deinit = promcon_deinit, - .con_clear = promcon_clear, - .con_putc = promcon_putc, - .con_putcs = promcon_putcs, - .con_cursor = promcon_cursor, - .con_scroll = promcon_scroll, - .con_bmove = promcon_bmove, - .con_switch = promcon_switch, - .con_blank = promcon_blank, - .con_set_palette = DUMMY, - .con_scrolldelta = DUMMY, -#if !(PROMCON_COLOR) - .con_build_attr = promcon_build_attr, -#endif -}; - -void __init prom_con_init(void) -{ -#ifdef CONFIG_DUMMY_CONSOLE - if (conswitchp == &dummy_con) - take_over_console(&prom_con, 0, MAX_NR_CONSOLES-1, 1); - else -#endif - if (conswitchp == &prom_con) - promcon_init_unimap(vc_cons[fg_console].d); -} diff --git a/scripts/Makefile b/scripts/Makefile index 9dd5b25a1d53..842dbc2d5aed 100644 --- a/scripts/Makefile +++ b/scripts/Makefile @@ -10,7 +10,6 @@ hostprogs-$(CONFIG_KALLSYMS) += kallsyms hostprogs-$(CONFIG_LOGO) += pnmtologo hostprogs-$(CONFIG_VT) += conmakehash -hostprogs-$(CONFIG_PROM_CONSOLE) += conmakehash hostprogs-$(CONFIG_IKCONFIG) += bin2c always := $(hostprogs-y) $(hostprogs-m) -- cgit v1.2.3 From f7557dc8215a2e7eb22da583d03e1aef72c58b3c Mon Sep 17 00:00:00 2001 From: David Daney Date: Thu, 20 Aug 2009 14:10:23 -0700 Subject: MIPS: hw_random: Add hardware RNG for Octeon SOCs. Signed-off-by: David Daney Acked-by: Herbert Xu Signed-off-by: Ralf Baechle --- drivers/char/hw_random/Kconfig | 13 ++++ drivers/char/hw_random/Makefile | 1 + drivers/char/hw_random/octeon-rng.c | 147 ++++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 drivers/char/hw_random/octeon-rng.c (limited to 'drivers/char') diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index ce66a70184f7..87060266ef91 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -126,6 +126,19 @@ config HW_RANDOM_OMAP If unsure, say Y. +config HW_RANDOM_OCTEON + tristate "Octeon Random Number Generator support" + depends on HW_RANDOM && CPU_CAVIUM_OCTEON + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on Octeon processors. + + To compile this driver as a module, choose M here: the + module will be called octeon-rng. + + If unsure, say Y. + config HW_RANDOM_PASEMI tristate "PA Semi HW Random Number Generator support" depends on HW_RANDOM && PPC_PASEMI diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 676828ba8123..5eeb1303f0d0 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_HW_RANDOM_PASEMI) += pasemi-rng.o obj-$(CONFIG_HW_RANDOM_VIRTIO) += virtio-rng.o obj-$(CONFIG_HW_RANDOM_TX4939) += tx4939-rng.o obj-$(CONFIG_HW_RANDOM_MXC_RNGA) += mxc-rnga.o +obj-$(CONFIG_HW_RANDOM_OCTEON) += octeon-rng.o diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c new file mode 100644 index 000000000000..54b0d9ba65cf --- /dev/null +++ b/drivers/char/hw_random/octeon-rng.c @@ -0,0 +1,147 @@ +/* + * Hardware Random Number Generator support for Cavium Networks + * Octeon processor family. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2009 Cavium Networks + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct octeon_rng { + struct hwrng ops; + void __iomem *control_status; + void __iomem *result; +}; + +static int octeon_rng_init(struct hwrng *rng) +{ + union cvmx_rnm_ctl_status ctl; + struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); + + ctl.u64 = 0; + ctl.s.ent_en = 1; /* Enable the entropy source. */ + ctl.s.rng_en = 1; /* Enable the RNG hardware. */ + cvmx_write_csr((u64)p->control_status, ctl.u64); + return 0; +} + +static void octeon_rng_cleanup(struct hwrng *rng) +{ + union cvmx_rnm_ctl_status ctl; + struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); + + ctl.u64 = 0; + /* Disable everything. */ + cvmx_write_csr((u64)p->control_status, ctl.u64); +} + +static int octeon_rng_data_read(struct hwrng *rng, u32 *data) +{ + struct octeon_rng *p = container_of(rng, struct octeon_rng, ops); + + *data = cvmx_read64_uint32((u64)p->result); + return sizeof(u32); +} + +static int __devinit octeon_rng_probe(struct platform_device *pdev) +{ + struct resource *res_ports; + struct resource *res_result; + struct octeon_rng *rng; + int ret; + struct hwrng ops = { + .name = "octeon", + .init = octeon_rng_init, + .cleanup = octeon_rng_cleanup, + .data_read = octeon_rng_data_read + }; + + rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL); + if (!rng) + return -ENOMEM; + + res_ports = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_ports) + goto err_ports; + + res_result = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res_result) + goto err_ports; + + + rng->control_status = devm_ioremap_nocache(&pdev->dev, + res_ports->start, + sizeof(u64)); + if (!rng->control_status) + goto err_ports; + + rng->result = devm_ioremap_nocache(&pdev->dev, + res_result->start, + sizeof(u64)); + if (!rng->result) + goto err_r; + + rng->ops = ops; + + dev_set_drvdata(&pdev->dev, &rng->ops); + ret = hwrng_register(&rng->ops); + if (ret) + goto err; + + dev_info(&pdev->dev, "Octeon Random Number Generator\n"); + + return 0; +err: + devm_iounmap(&pdev->dev, rng->control_status); +err_r: + devm_iounmap(&pdev->dev, rng->result); +err_ports: + devm_kfree(&pdev->dev, rng); + return -ENOENT; +} + +static int __exit octeon_rng_remove(struct platform_device *pdev) +{ + struct hwrng *rng = dev_get_drvdata(&pdev->dev); + + hwrng_unregister(rng); + + return 0; +} + +static struct platform_driver octeon_rng_driver = { + .driver = { + .name = "octeon_rng", + .owner = THIS_MODULE, + }, + .probe = octeon_rng_probe, + .remove = __exit_p(octeon_rng_remove), +}; + +static int __init octeon_rng_mod_init(void) +{ + return platform_driver_register(&octeon_rng_driver); +} + +static void __exit octeon_rng_mod_exit(void) +{ + platform_driver_unregister(&octeon_rng_driver); +} + +module_init(octeon_rng_mod_init); +module_exit(octeon_rng_mod_exit); + +MODULE_AUTHOR("David Daney"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 202c4675c55ddf6b443c7e057d2dff6b42ef71aa Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 18 Sep 2009 07:05:58 -0700 Subject: pty_write: don't do a tty_wakeup() when the buffers are full Commit ac89a9174 ("pty: don't limit the writes to 'pty_space()' inside 'pty_write()'") removed the pty_space() checking, in order to let the regular tty buffer code limit the buffering itself. That was all good, but as a subtle side effect it meant that we'd be doing a tty_wakeup() even in the case where the buffers were all filled up, and didn't actually make any progress on the write. Which sounds innocuous, but it interacts very badly with the ppp_async code, which has an infinite loop in ppp_async_push() that tries to push out data to the tty. When we call tty_wakeup(), that loop ends up thinking that progress was made (see the subtle interactions between XMIT_WAKEUP and 'tty_stuffed' for details). End result: one unhappy ppp user. Fixed by noticing when tty_insert_flip_string() didn't actually do anything, and then not doing any more processing (including, very much not calling tty_wakeup()). Bisected-and-tested-by: Peter Volkov Cc: stable@kernel.org (2.6.31) Signed-off-by: Linus Torvalds --- drivers/char/pty.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/pty.c b/drivers/char/pty.c index b33d6688e910..53761cefa915 100644 --- a/drivers/char/pty.c +++ b/drivers/char/pty.c @@ -120,8 +120,10 @@ static int pty_write(struct tty_struct *tty, const unsigned char *buf, int c) /* Stuff the data into the input queue of the other end */ c = tty_insert_flip_string(to, buf, c); /* And shovel */ - tty_flip_buffer_push(to); - tty_wakeup(tty); + if (c) { + tty_flip_buffer_push(to); + tty_wakeup(tty); + } } return c; } -- cgit v1.2.3 From e454cea20bdcff10ee698d11b8882662a0153a47 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 18 Sep 2009 23:01:12 +0200 Subject: Driver-Core: extend devnode callbacks to provide permissions This allows subsytems to provide devtmpfs with non-default permissions for the device node. Instead of the default mode of 0600, null, zero, random, urandom, full, tty, ptmx now have a mode of 0666, which allows non-privileged processes to access standard device nodes in case no other userspace process applies the expected permissions. This also fixes a wrong assignment in pktcdvd and a checkpatch.pl complain. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpuid.c | 4 ++-- arch/x86/kernel/microcode_core.c | 2 +- arch/x86/kernel/msr.c | 4 ++-- block/bsg.c | 4 ++-- block/genhd.c | 8 ++++---- drivers/base/core.c | 19 ++++++++++++------- drivers/base/devtmpfs.c | 24 ++++++++++++++++-------- drivers/block/aoe/aoechr.c | 4 ++-- drivers/block/pktcdvd.c | 6 +++--- drivers/char/hw_random/core.c | 2 +- drivers/char/mem.c | 29 +++++++++++++++++++---------- drivers/char/misc.c | 10 ++++++---- drivers/char/raw.c | 4 ++-- drivers/char/tty_io.c | 11 +++++++++++ drivers/gpu/drm/drm_sysfs.c | 4 ++-- drivers/hid/usbhid/hiddev.c | 4 ++-- drivers/input/input.c | 4 ++-- drivers/md/dm-ioctl.c | 2 +- drivers/media/dvb/dvb-core/dvbdev.c | 4 ++-- drivers/net/tun.c | 2 +- drivers/usb/class/usblp.c | 4 ++-- drivers/usb/core/file.c | 8 ++++---- drivers/usb/core/usb.c | 4 ++-- drivers/usb/misc/iowarrior.c | 4 ++-- drivers/usb/misc/legousbtower.c | 4 ++-- include/linux/device.h | 7 ++++--- include/linux/genhd.h | 2 +- include/linux/miscdevice.h | 3 ++- include/linux/usb.h | 4 ++-- sound/sound_core.c | 4 ++-- 30 files changed, 116 insertions(+), 79 deletions(-) (limited to 'drivers/char') diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c index b07af8861244..6a52d4b36a30 100644 --- a/arch/x86/kernel/cpuid.c +++ b/arch/x86/kernel/cpuid.c @@ -182,7 +182,7 @@ static struct notifier_block __refdata cpuid_class_cpu_notifier = .notifier_call = cpuid_class_cpu_callback, }; -static char *cpuid_nodename(struct device *dev) +static char *cpuid_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "cpu/%u/cpuid", MINOR(dev->devt)); } @@ -203,7 +203,7 @@ static int __init cpuid_init(void) err = PTR_ERR(cpuid_class); goto out_chrdev; } - cpuid_class->nodename = cpuid_nodename; + cpuid_class->devnode = cpuid_devnode; for_each_online_cpu(i) { err = cpuid_device_create(i); if (err != 0) diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c index 9371448290ac..0db7969b0dde 100644 --- a/arch/x86/kernel/microcode_core.c +++ b/arch/x86/kernel/microcode_core.c @@ -236,7 +236,7 @@ static const struct file_operations microcode_fops = { static struct miscdevice microcode_dev = { .minor = MICROCODE_MINOR, .name = "microcode", - .devnode = "cpu/microcode", + .nodename = "cpu/microcode", .fops = µcode_fops, }; diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c index 7dd950094178..6a3cefc7dda1 100644 --- a/arch/x86/kernel/msr.c +++ b/arch/x86/kernel/msr.c @@ -241,7 +241,7 @@ static struct notifier_block __refdata msr_class_cpu_notifier = { .notifier_call = msr_class_cpu_callback, }; -static char *msr_nodename(struct device *dev) +static char *msr_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "cpu/%u/msr", MINOR(dev->devt)); } @@ -262,7 +262,7 @@ static int __init msr_init(void) err = PTR_ERR(msr_class); goto out_chrdev; } - msr_class->nodename = msr_nodename; + msr_class->devnode = msr_devnode; for_each_online_cpu(i) { err = msr_device_create(i); if (err != 0) diff --git a/block/bsg.c b/block/bsg.c index 5f184bb3ff9e..0676301f16d0 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -1062,7 +1062,7 @@ EXPORT_SYMBOL_GPL(bsg_register_queue); static struct cdev bsg_cdev; -static char *bsg_nodename(struct device *dev) +static char *bsg_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "bsg/%s", dev_name(dev)); } @@ -1087,7 +1087,7 @@ static int __init bsg_init(void) ret = PTR_ERR(bsg_class); goto destroy_kmemcache; } - bsg_class->nodename = bsg_nodename; + bsg_class->devnode = bsg_devnode; ret = alloc_chrdev_region(&devid, 0, BSG_MAX_DEVS, "bsg"); if (ret) diff --git a/block/genhd.c b/block/genhd.c index 2ad91ddad8e2..517e4332cb37 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -998,12 +998,12 @@ struct class block_class = { .name = "block", }; -static char *block_nodename(struct device *dev) +static char *block_devnode(struct device *dev, mode_t *mode) { struct gendisk *disk = dev_to_disk(dev); - if (disk->nodename) - return disk->nodename(disk); + if (disk->devnode) + return disk->devnode(disk, mode); return NULL; } @@ -1011,7 +1011,7 @@ static struct device_type disk_type = { .name = "disk", .groups = disk_attr_groups, .release = disk_release, - .nodename = block_nodename, + .devnode = block_devnode, }; #ifdef CONFIG_PROC_FS diff --git a/drivers/base/core.c b/drivers/base/core.c index 390e664ec1c7..6bee6af8d8e1 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -166,13 +166,16 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, if (MAJOR(dev->devt)) { const char *tmp; const char *name; + mode_t mode = 0; add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt)); add_uevent_var(env, "MINOR=%u", MINOR(dev->devt)); - name = device_get_nodename(dev, &tmp); + name = device_get_devnode(dev, &mode, &tmp); if (name) { add_uevent_var(env, "DEVNAME=%s", name); kfree(tmp); + if (mode) + add_uevent_var(env, "DEVMODE=%#o", mode & 0777); } } @@ -1148,8 +1151,9 @@ static struct device *next_device(struct klist_iter *i) } /** - * device_get_nodename - path of device node file + * device_get_devnode - path of device node file * @dev: device + * @mode: returned file access mode * @tmp: possibly allocated string * * Return the relative path of a possible device node. @@ -1157,21 +1161,22 @@ static struct device *next_device(struct klist_iter *i) * a name. This memory is returned in tmp and needs to be * freed by the caller. */ -const char *device_get_nodename(struct device *dev, const char **tmp) +const char *device_get_devnode(struct device *dev, + mode_t *mode, const char **tmp) { char *s; *tmp = NULL; /* the device type may provide a specific name */ - if (dev->type && dev->type->nodename) - *tmp = dev->type->nodename(dev); + if (dev->type && dev->type->devnode) + *tmp = dev->type->devnode(dev, mode); if (*tmp) return *tmp; /* the class may provide a specific name */ - if (dev->class && dev->class->nodename) - *tmp = dev->class->nodename(dev); + if (dev->class && dev->class->devnode) + *tmp = dev->class->devnode(dev, mode); if (*tmp) return *tmp; diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index fd488ad4263a..a1cb5afe6801 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -6,9 +6,10 @@ * During bootup, before any driver core device is registered, * devtmpfs, a tmpfs-based filesystem is created. Every driver-core * device which requests a device node, will add a node in this - * filesystem. The node is named after the the name of the device, - * or the susbsytem can provide a custom name. All devices are - * owned by root and have a mode of 0600. + * filesystem. + * By default, all devices are named after the the name of the + * device, owned by root and have a default mode of 0600. Subsystems + * can overwrite the default setting if needed. */ #include @@ -20,6 +21,7 @@ #include #include #include +#include #include static struct vfsmount *dev_mnt; @@ -134,7 +136,7 @@ int devtmpfs_create_node(struct device *dev) const char *tmp = NULL; const char *nodename; const struct cred *curr_cred; - mode_t mode; + mode_t mode = 0; struct nameidata nd; struct dentry *dentry; int err; @@ -142,14 +144,16 @@ int devtmpfs_create_node(struct device *dev) if (!dev_mnt) return 0; - nodename = device_get_nodename(dev, &tmp); + nodename = device_get_devnode(dev, &mode, &tmp); if (!nodename) return -ENOMEM; + if (mode == 0) + mode = 0600; if (is_blockdev(dev)) - mode = S_IFBLK|0600; + mode |= S_IFBLK; else - mode = S_IFCHR|0600; + mode |= S_IFCHR; curr_cred = override_creds(&init_cred); err = vfs_path_lookup(dev_mnt->mnt_root, dev_mnt, @@ -165,8 +169,12 @@ int devtmpfs_create_node(struct device *dev) dentry = lookup_create(&nd, 0); if (!IS_ERR(dentry)) { + int umask; + + umask = sys_umask(0000); err = vfs_mknod(nd.path.dentry->d_inode, dentry, mode, dev->devt); + sys_umask(umask); /* mark as kernel created inode */ if (!err) dentry->d_inode->i_private = &dev_mnt; @@ -271,7 +279,7 @@ int devtmpfs_delete_node(struct device *dev) if (!dev_mnt) return 0; - nodename = device_get_nodename(dev, &tmp); + nodename = device_get_devnode(dev, NULL, &tmp); if (!nodename) return -ENOMEM; diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c index 19888354188f..62141ec09a22 100644 --- a/drivers/block/aoe/aoechr.c +++ b/drivers/block/aoe/aoechr.c @@ -266,7 +266,7 @@ static const struct file_operations aoe_fops = { .owner = THIS_MODULE, }; -static char *aoe_nodename(struct device *dev) +static char *aoe_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "etherd/%s", dev_name(dev)); } @@ -288,7 +288,7 @@ aoechr_init(void) unregister_chrdev(AOE_MAJOR, "aoechr"); return PTR_ERR(aoe_class); } - aoe_class->nodename = aoe_nodename; + aoe_class->devnode = aoe_devnode; for (i = 0; i < ARRAY_SIZE(chardevs); ++i) device_create(aoe_class, NULL, diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 95f11cdef203..fd5bb8ad59a9 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2857,7 +2857,7 @@ static struct block_device_operations pktcdvd_ops = { .media_changed = pkt_media_changed, }; -static char *pktcdvd_nodename(struct gendisk *gd) +static char *pktcdvd_devnode(struct gendisk *gd, mode_t *mode) { return kasprintf(GFP_KERNEL, "pktcdvd/%s", gd->disk_name); } @@ -2914,7 +2914,7 @@ static int pkt_setup_dev(dev_t dev, dev_t* pkt_dev) disk->fops = &pktcdvd_ops; disk->flags = GENHD_FL_REMOVABLE; strcpy(disk->disk_name, pd->name); - disk->nodename = pktcdvd_nodename; + disk->devnode = pktcdvd_devnode; disk->private_data = pd; disk->queue = blk_alloc_queue(GFP_KERNEL); if (!disk->queue) @@ -3070,7 +3070,7 @@ static const struct file_operations pkt_ctl_fops = { static struct miscdevice pkt_misc = { .minor = MISC_DYNAMIC_MINOR, .name = DRIVER_NAME, - .name = "pktcdvd/control", + .nodename = "pktcdvd/control", .fops = &pkt_ctl_fops }; diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index fc93e2fc7c71..1573aebd54b5 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -153,7 +153,7 @@ static const struct file_operations rng_chrdev_ops = { static struct miscdevice rng_miscdev = { .minor = RNG_MISCDEV_MINOR, .name = RNG_MODULE_NAME, - .devnode = "hwrng", + .nodename = "hwrng", .fops = &rng_chrdev_ops, }; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 0491cdf63f2a..0aede1d6a9ea 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -866,24 +866,25 @@ static const struct file_operations kmsg_fops = { static const struct memdev { const char *name; + mode_t mode; const struct file_operations *fops; struct backing_dev_info *dev_info; } devlist[] = { - [ 1] = { "mem", &mem_fops, &directly_mappable_cdev_bdi }, + [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi }, #ifdef CONFIG_DEVKMEM - [ 2] = { "kmem", &kmem_fops, &directly_mappable_cdev_bdi }, + [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi }, #endif - [ 3] = {"null", &null_fops, NULL }, + [3] = { "null", 0666, &null_fops, NULL }, #ifdef CONFIG_DEVPORT - [ 4] = { "port", &port_fops, NULL }, + [4] = { "port", 0, &port_fops, NULL }, #endif - [ 5] = { "zero", &zero_fops, &zero_bdi }, - [ 7] = { "full", &full_fops, NULL }, - [ 8] = { "random", &random_fops, NULL }, - [ 9] = { "urandom", &urandom_fops, NULL }, - [11] = { "kmsg", &kmsg_fops, NULL }, + [5] = { "zero", 0666, &zero_fops, &zero_bdi }, + [7] = { "full", 0666, &full_fops, NULL }, + [8] = { "random", 0666, &random_fops, NULL }, + [9] = { "urandom", 0666, &urandom_fops, NULL }, + [11] = { "kmsg", 0, &kmsg_fops, NULL }, #ifdef CONFIG_CRASH_DUMP - [12] = { "oldmem", &oldmem_fops, NULL }, + [12] = { "oldmem", 0, &oldmem_fops, NULL }, #endif }; @@ -920,6 +921,13 @@ static const struct file_operations memory_fops = { .open = memory_open, }; +static char *mem_devnode(struct device *dev, mode_t *mode) +{ + if (mode && devlist[MINOR(dev->devt)].mode) + *mode = devlist[MINOR(dev->devt)].mode; + return NULL; +} + static struct class *mem_class; static int __init chr_dev_init(void) @@ -935,6 +943,7 @@ static int __init chr_dev_init(void) printk("unable to get major %d for memory devs\n", MEM_MAJOR); mem_class = class_create(THIS_MODULE, "mem"); + mem_class->devnode = mem_devnode; for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) { if (!devlist[minor].name) continue; diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 62c99fa59e2b..1ee27cc23426 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -263,12 +263,14 @@ int misc_deregister(struct miscdevice *misc) EXPORT_SYMBOL(misc_register); EXPORT_SYMBOL(misc_deregister); -static char *misc_nodename(struct device *dev) +static char *misc_devnode(struct device *dev, mode_t *mode) { struct miscdevice *c = dev_get_drvdata(dev); - if (c->devnode) - return kstrdup(c->devnode, GFP_KERNEL); + if (mode && c->mode) + *mode = c->mode; + if (c->nodename) + return kstrdup(c->nodename, GFP_KERNEL); return NULL; } @@ -287,7 +289,7 @@ static int __init misc_init(void) err = -EIO; if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) goto fail_printk; - misc_class->nodename = misc_nodename; + misc_class->devnode = misc_devnode; return 0; fail_printk: diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 40268db02e22..64acd05f71c8 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -261,7 +261,7 @@ static const struct file_operations raw_ctl_fops = { static struct cdev raw_cdev; -static char *raw_nodename(struct device *dev) +static char *raw_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "raw/%s", dev_name(dev)); } @@ -289,7 +289,7 @@ static int __init raw_init(void) ret = PTR_ERR(raw_class); goto error_region; } - raw_class->nodename = raw_nodename; + raw_class->devnode = raw_devnode; device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl"); return 0; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index a3afa0c387cd..c70d9dabefae 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -3056,11 +3056,22 @@ void __init console_init(void) } } +static char *tty_devnode(struct device *dev, mode_t *mode) +{ + if (!mode) + return NULL; + if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) || + dev->devt == MKDEV(TTYAUX_MAJOR, 2)) + *mode = 0666; + return NULL; +} + static int __init tty_class_init(void) { tty_class = class_create(THIS_MODULE, "tty"); if (IS_ERR(tty_class)) return PTR_ERR(tty_class); + tty_class->devnode = tty_devnode; return 0; } diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index f7a615b80c70..5301f226cb1c 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -76,7 +76,7 @@ static ssize_t version_show(struct class *dev, char *buf) CORE_MINOR, CORE_PATCHLEVEL, CORE_DATE); } -static char *drm_nodename(struct device *dev) +static char *drm_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "dri/%s", dev_name(dev)); } @@ -112,7 +112,7 @@ struct class *drm_sysfs_create(struct module *owner, char *name) if (err) goto err_out_class; - class->nodename = drm_nodename; + class->devnode = drm_devnode; return class; diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index 4d1dc0cf1401..8b6ee247bfe4 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -852,14 +852,14 @@ static const struct file_operations hiddev_fops = { #endif }; -static char *hiddev_nodename(struct device *dev) +static char *hiddev_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); } static struct usb_class_driver hiddev_class = { .name = "hiddev%d", - .nodename = hiddev_nodename, + .devnode = hiddev_devnode, .fops = &hiddev_fops, .minor_base = HIDDEV_MINOR_BASE, }; diff --git a/drivers/input/input.c b/drivers/input/input.c index 851791d955f3..556539d617a4 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1265,14 +1265,14 @@ static struct device_type input_dev_type = { .uevent = input_dev_uevent, }; -static char *input_nodename(struct device *dev) +static char *input_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "input/%s", dev_name(dev)); } struct class input_class = { .name = "input", - .nodename = input_nodename, + .devnode = input_devnode, }; EXPORT_SYMBOL_GPL(input_class); diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 7f77f18fcafa..a67942931582 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1532,7 +1532,7 @@ static const struct file_operations _ctl_fops = { static struct miscdevice _dm_misc = { .minor = MISC_DYNAMIC_MINOR, .name = DM_NAME, - .devnode = "mapper/control", + .nodename = "mapper/control", .fops = &_ctl_fops }; diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c index 479dd05762a5..94159b90f733 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.c +++ b/drivers/media/dvb/dvb-core/dvbdev.c @@ -447,7 +447,7 @@ static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env) return 0; } -static char *dvb_nodename(struct device *dev) +static char *dvb_devnode(struct device *dev, mode_t *mode) { struct dvb_device *dvbdev = dev_get_drvdata(dev); @@ -478,7 +478,7 @@ static int __init init_dvbdev(void) goto error; } dvb_class->dev_uevent = dvb_uevent; - dvb_class->nodename = dvb_nodename; + dvb_class->devnode = dvb_devnode; return 0; error: diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3f5d28851aa2..d3ee1994b02f 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1370,7 +1370,7 @@ static const struct file_operations tun_fops = { static struct miscdevice tun_miscdev = { .minor = TUN_MINOR, .name = "tun", - .devnode = "net/tun", + .nodename = "net/tun", .fops = &tun_fops, }; diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 26c09f0257db..9bc112ee7803 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -1057,14 +1057,14 @@ static const struct file_operations usblp_fops = { .release = usblp_release, }; -static char *usblp_nodename(struct device *dev) +static char *usblp_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); } static struct usb_class_driver usblp_class = { .name = "lp%d", - .nodename = usblp_nodename, + .devnode = usblp_devnode, .fops = &usblp_fops, .minor_base = USBLP_MINOR_BASE, }; diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 5cef88929b3e..222ee07ea680 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -67,14 +67,14 @@ static struct usb_class { struct class *class; } *usb_class; -static char *usb_nodename(struct device *dev) +static char *usb_devnode(struct device *dev, mode_t *mode) { struct usb_class_driver *drv; drv = dev_get_drvdata(dev); - if (!drv || !drv->nodename) + if (!drv || !drv->devnode) return NULL; - return drv->nodename(dev); + return drv->devnode(dev, mode); } static int init_usb_class(void) @@ -100,7 +100,7 @@ static int init_usb_class(void) kfree(usb_class); usb_class = NULL; } - usb_class->class->nodename = usb_nodename; + usb_class->class->devnode = usb_devnode; exit: return result; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index a26f73880c32..43ee943d757a 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -311,7 +311,7 @@ static struct dev_pm_ops usb_device_pm_ops = { #endif /* CONFIG_PM */ -static char *usb_nodename(struct device *dev) +static char *usb_devnode(struct device *dev, mode_t *mode) { struct usb_device *usb_dev; @@ -324,7 +324,7 @@ struct device_type usb_device_type = { .name = "usb_device", .release = usb_release_dev, .uevent = usb_dev_uevent, - .nodename = usb_nodename, + .devnode = usb_devnode, .pm = &usb_device_pm_ops, }; diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c index 90e1a8dedfa9..e75bb87ee92b 100644 --- a/drivers/usb/misc/iowarrior.c +++ b/drivers/usb/misc/iowarrior.c @@ -727,7 +727,7 @@ static const struct file_operations iowarrior_fops = { .poll = iowarrior_poll, }; -static char *iowarrior_nodename(struct device *dev) +static char *iowarrior_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); } @@ -738,7 +738,7 @@ static char *iowarrior_nodename(struct device *dev) */ static struct usb_class_driver iowarrior_class = { .name = "iowarrior%d", - .nodename = iowarrior_nodename, + .devnode = iowarrior_devnode, .fops = &iowarrior_fops, .minor_base = IOWARRIOR_MINOR_BASE, }; diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c index c1e2433f640d..97efeaec4d52 100644 --- a/drivers/usb/misc/legousbtower.c +++ b/drivers/usb/misc/legousbtower.c @@ -266,7 +266,7 @@ static const struct file_operations tower_fops = { .llseek = tower_llseek, }; -static char *legousbtower_nodename(struct device *dev) +static char *legousbtower_devnode(struct device *dev, mode_t *mode) { return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); } @@ -277,7 +277,7 @@ static char *legousbtower_nodename(struct device *dev) */ static struct usb_class_driver tower_class = { .name = "legousbtower%d", - .nodename = legousbtower_nodename, + .devnode = legousbtower_devnode, .fops = &tower_fops, .minor_base = LEGO_USB_TOWER_MINOR_BASE, }; diff --git a/include/linux/device.h b/include/linux/device.h index 847b763e40e9..aca31bf7d8ed 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -193,7 +193,7 @@ struct class { struct kobject *dev_kobj; int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); - char *(*nodename)(struct device *dev); + char *(*devnode)(struct device *dev, mode_t *mode); void (*class_release)(struct class *class); void (*dev_release)(struct device *dev); @@ -298,7 +298,7 @@ struct device_type { const char *name; const struct attribute_group **groups; int (*uevent)(struct device *dev, struct kobj_uevent_env *env); - char *(*nodename)(struct device *dev); + char *(*devnode)(struct device *dev, mode_t *mode); void (*release)(struct device *dev); const struct dev_pm_ops *pm; @@ -487,7 +487,8 @@ extern struct device *device_find_child(struct device *dev, void *data, extern int device_rename(struct device *dev, char *new_name); extern int device_move(struct device *dev, struct device *new_parent, enum dpm_order dpm_order); -extern const char *device_get_nodename(struct device *dev, const char **tmp); +extern const char *device_get_devnode(struct device *dev, + mode_t *mode, const char **tmp); extern void *dev_get_drvdata(const struct device *dev); extern void dev_set_drvdata(struct device *dev, void *data); diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 44263cb27121..109d179adb93 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -142,7 +142,7 @@ struct gendisk { * disks that can't be partitioned. */ char disk_name[DISK_NAME_LEN]; /* name of major driver */ - char *(*nodename)(struct gendisk *gd); + char *(*devnode)(struct gendisk *gd, mode_t *mode); /* Array of pointers to partitions indexed by partno. * Protected with matching bdev lock but stat and other * non-critical accesses use RCU. Always access through diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 052117744629..adaf3c15e449 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -41,7 +41,8 @@ struct miscdevice { struct list_head list; struct device *parent; struct device *this_device; - const char *devnode; + const char *nodename; + mode_t mode; }; extern int misc_register(struct miscdevice * misc); diff --git a/include/linux/usb.h b/include/linux/usb.h index b1e3c2fbfe11..a8fe05f224e5 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -922,7 +922,7 @@ extern struct bus_type usb_bus_type; /** * struct usb_class_driver - identifies a USB driver that wants to use the USB major number * @name: the usb class device name for this driver. Will show up in sysfs. - * @nodename: Callback to provide a naming hint for a possible + * @devnode: Callback to provide a naming hint for a possible * device node to create. * @fops: pointer to the struct file_operations of this driver. * @minor_base: the start of the minor range for this driver. @@ -933,7 +933,7 @@ extern struct bus_type usb_bus_type; */ struct usb_class_driver { char *name; - char *(*nodename)(struct device *dev); + char *(*devnode)(struct device *dev, mode_t *mode); const struct file_operations *fops; int minor_base; }; diff --git a/sound/sound_core.c b/sound/sound_core.c index bb4b88e606bb..49c998186592 100644 --- a/sound/sound_core.c +++ b/sound/sound_core.c @@ -29,7 +29,7 @@ MODULE_DESCRIPTION("Core sound module"); MODULE_AUTHOR("Alan Cox"); MODULE_LICENSE("GPL"); -static char *sound_nodename(struct device *dev) +static char *sound_devnode(struct device *dev, mode_t *mode) { if (MAJOR(dev->devt) == SOUND_MAJOR) return NULL; @@ -50,7 +50,7 @@ static int __init init_soundcore(void) return PTR_ERR(sound_class); } - sound_class->nodename = sound_nodename; + sound_class->devnode = sound_devnode; return 0; } -- cgit v1.2.3 From f0de0e8d3565ee7feba6228d99763d4cea2087a6 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 3 Aug 2009 16:00:15 -0700 Subject: tty-ldisc: make /proc/tty/ldiscs use ldisc_ops instead of ldiscs The /proc/tty/ldiscs file is totally and utterly un-interested in the "struct tty_ldisc" structures, and only cares about the underlying ldisc operations. So don't make it create a dummy 'struct ldisc' only to get a pointer to the operations, and then destroy it. Instead, we split up the function 'tty_ldisc_try_get()', and create a 'get_ldops()' helper that just looks up the ldisc operations based on the ldisc number. That makes the code simpler to read (smaller and more well-defined helper functions), and allows the /proc functions to avoid creating that useless dummy only to immediately free it again. Signed-off-by: Linus Torvalds Tested-by: Sergey Senozhatsky Cc: Alan Cox Cc: OGAWA Hirofumi Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_ldisc.c | 65 +++++++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 26 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index e48af9f79219..cbfacc0bbea5 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c @@ -145,6 +145,34 @@ int tty_unregister_ldisc(int disc) } EXPORT_SYMBOL(tty_unregister_ldisc); +static struct tty_ldisc_ops *get_ldops(int disc) +{ + unsigned long flags; + struct tty_ldisc_ops *ldops, *ret; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ret = ERR_PTR(-EINVAL); + ldops = tty_ldiscs[disc]; + if (ldops) { + ret = ERR_PTR(-EAGAIN); + if (try_module_get(ldops->owner)) { + ldops->refcount++; + ret = ldops; + } + } + spin_unlock_irqrestore(&tty_ldisc_lock, flags); + return ret; +} + +static void put_ldops(struct tty_ldisc_ops *ldops) +{ + unsigned long flags; + + spin_lock_irqsave(&tty_ldisc_lock, flags); + ldops->refcount--; + module_put(ldops->owner); + spin_unlock_irqrestore(&tty_ldisc_lock, flags); +} /** * tty_ldisc_try_get - try and reference an ldisc @@ -156,36 +184,21 @@ EXPORT_SYMBOL(tty_unregister_ldisc); static struct tty_ldisc *tty_ldisc_try_get(int disc) { - unsigned long flags; struct tty_ldisc *ld; struct tty_ldisc_ops *ldops; - int err = -EINVAL; ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); if (ld == NULL) return ERR_PTR(-ENOMEM); - spin_lock_irqsave(&tty_ldisc_lock, flags); - ld->ops = NULL; - ldops = tty_ldiscs[disc]; - /* Check the entry is defined */ - if (ldops) { - /* If the module is being unloaded we can't use it */ - if (!try_module_get(ldops->owner)) - err = -EAGAIN; - else { - /* lock it */ - ldops->refcount++; - ld->ops = ldops; - atomic_set(&ld->users, 1); - err = 0; - } - } - spin_unlock_irqrestore(&tty_ldisc_lock, flags); - if (err) { + ldops = get_ldops(disc); + if (IS_ERR(ldops)) { kfree(ld); - return ERR_PTR(err); + return ERR_CAST(ldops); } + + ld->ops = ldops; + atomic_set(&ld->users, 1); return ld; } @@ -234,13 +247,13 @@ static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) static int tty_ldiscs_seq_show(struct seq_file *m, void *v) { int i = *(loff_t *)v; - struct tty_ldisc *ld; + struct tty_ldisc_ops *ldops; - ld = tty_ldisc_try_get(i); - if (IS_ERR(ld)) + ldops = get_ldops(i); + if (IS_ERR(ldops)) return 0; - seq_printf(m, "%-10s %2d\n", ld->ops->name ? ld->ops->name : "???", i); - put_ldisc(ld); + seq_printf(m, "%-10s %2d\n", ldops->name ? ldops->name : "???", i); + put_ldops(ldops); return 0; } -- cgit v1.2.3 From 182274f85fc26ec3aa8c78dba8b0e7763af3c586 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 3 Aug 2009 16:01:28 -0700 Subject: tty-ldisc: get rid of tty_ldisc_try_get() helper function Now that the /proc/tty/ldiscs handling doesn't play games with 'struct ldisc' any more, the only remaining user of 'tty_ldisc_try_get()' is 'tty_ldisc_get()' (note the lack of 'try'). And we're actually much better off folding the logic directly into that file, since the 'try' part was always about trying to get the ldisc operations, not the ldisc itself: and making that explicit inside of 'tty_ldisc_get()' clarifies the whole semantics. Signed-off-by: Linus Torvalds Cc: Alan Cox Cc: OGAWA Hirofumi , Tested-by: Sergey Senozhatsky Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_ldisc.c | 51 +++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 31 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index cbfacc0bbea5..aafdbaebc16a 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c @@ -174,34 +174,6 @@ static void put_ldops(struct tty_ldisc_ops *ldops) spin_unlock_irqrestore(&tty_ldisc_lock, flags); } -/** - * tty_ldisc_try_get - try and reference an ldisc - * @disc: ldisc number - * - * Attempt to open and lock a line discipline into place. Return - * the line discipline refcounted or an error. - */ - -static struct tty_ldisc *tty_ldisc_try_get(int disc) -{ - struct tty_ldisc *ld; - struct tty_ldisc_ops *ldops; - - ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); - if (ld == NULL) - return ERR_PTR(-ENOMEM); - - ldops = get_ldops(disc); - if (IS_ERR(ldops)) { - kfree(ld); - return ERR_CAST(ldops); - } - - ld->ops = ldops; - atomic_set(&ld->users, 1); - return ld; -} - /** * tty_ldisc_get - take a reference to an ldisc * @disc: ldisc number @@ -218,14 +190,31 @@ static struct tty_ldisc *tty_ldisc_try_get(int disc) static struct tty_ldisc *tty_ldisc_get(int disc) { struct tty_ldisc *ld; + struct tty_ldisc_ops *ldops; if (disc < N_TTY || disc >= NR_LDISCS) return ERR_PTR(-EINVAL); - ld = tty_ldisc_try_get(disc); - if (IS_ERR(ld)) { + + /* + * Get the ldisc ops - we may need to request them to be loaded + * dynamically and try again. + */ + ldops = get_ldops(disc); + if (IS_ERR(ldops)) { request_module("tty-ldisc-%d", disc); - ld = tty_ldisc_try_get(disc); + ldops = get_ldops(disc); + if (IS_ERR(ldops)) + return ERR_CAST(ldops); + } + + ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); + if (ld == NULL) { + put_ldops(ldops); + return ERR_PTR(-ENOMEM); } + + ld->ops = ldops; + atomic_set(&ld->users, 1); return ld; } -- cgit v1.2.3 From 60479ed59444de658d25c4d4000fa45b61b491a3 Mon Sep 17 00:00:00 2001 From: Jaswinder Singh Rajput Date: Thu, 13 Aug 2009 13:56:20 +0530 Subject: tty: includecheck fix: drivers/char, vt.c fix the following 'make includecheck' warning: drivers/char/vt.c: linux/device.h is included more than once. Signed-off-by: Jaswinder Singh Rajput Signed-off-by: Greg Kroah-Hartman --- drivers/char/vt.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 6aa88f50b039..e47a4c88976b 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2955,7 +2955,6 @@ int __init vty_init(const struct file_operations *console_fops) } #ifndef VT_SINGLE_DRIVER -#include static struct class *vtconsole_class; -- cgit v1.2.3 From d13549f804d2965a9f279a8ff867f35d949572c8 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:12 -0700 Subject: cyclades: add tty refcounting While this is not problem for Y card handlers (they are protected by card_lock), Z handlers and other functions may dereference NULL at any point after hangup/close. Even if (tty == NULL) was already performed in the handler. Note that it's not an issue for Y cards just for now. After switching to tty_port_close_* et al. this will be a problem. So add refcounting to them all. Also proc .show doesn't take a tty reference and it should (along with a ldisc one). While at it and changing prototypes (adding tty param), prepend cy_ to functions which don't have it yet. Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 172 ++++++++++++++++++++++++++---------------------- 1 file changed, 93 insertions(+), 79 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 2dafc2da0648..e2f731b70763 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -850,7 +850,7 @@ MODULE_DEVICE_TABLE(pci, cy_pci_dev_id); #endif static void cy_start(struct tty_struct *); -static void set_line_char(struct cyclades_port *); +static void cy_set_line_char(struct cyclades_port *, struct tty_struct *); static int cyz_issue_cmd(struct cyclades_card *, __u32, __u8, __u32); #ifdef CONFIG_ISA static unsigned detect_isa_irq(void __iomem *); @@ -1011,8 +1011,9 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, save_car = readb(base_addr + (CyCAR << index)); cy_writeb(base_addr + (CyCAR << index), save_xir); + tty = tty_port_tty_get(&info->port); /* if there is nowhere to put the data, discard it */ - if (info->port.tty == NULL) { + if (tty == NULL) { if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) == CyIVRRxEx) { /* exception */ data = readb(base_addr + (CyRDSR << index)); @@ -1024,7 +1025,6 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, goto end; } /* there is an open port for this data */ - tty = info->port.tty; if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) == CyIVRRxEx) { /* exception */ data = readb(base_addr + (CyRDSR << index)); @@ -1041,6 +1041,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, if (data & info->ignore_status_mask) { info->icount.rx++; + tty_kref_put(tty); return; } if (tty_buffer_request_room(tty, 1)) { @@ -1121,6 +1122,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, info->idle_stats.recv_idle = jiffies; } tty_schedule_flip(tty); + tty_kref_put(tty); end: /* end of service */ cy_writeb(base_addr + (CyRIR << index), save_xir & 0x3f); @@ -1131,6 +1133,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, void __iomem *base_addr) { struct cyclades_port *info; + struct tty_struct *tty; int char_count, index = cinfo->bus_index; u8 save_xir, channel, save_car, outch; @@ -1154,7 +1157,8 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, goto end; } info = &cinfo->ports[channel + chip * 4]; - if (info->port.tty == NULL) { + tty = tty_port_tty_get(&info->port); + if (tty == NULL) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); goto end; @@ -1205,7 +1209,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, ~CyTxRdy); goto done; } - if (info->port.tty->stopped || info->port.tty->hw_stopped) { + if (tty->stopped || tty->hw_stopped) { cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); @@ -1241,7 +1245,8 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, } done: - tty_wakeup(info->port.tty); + tty_wakeup(tty); + tty_kref_put(tty); end: /* end of service */ cy_writeb(base_addr + (CyTIR << index), save_xir & 0x3f); @@ -1252,6 +1257,7 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, void __iomem *base_addr) { struct cyclades_port *info; + struct tty_struct *tty; int index = cinfo->bus_index; u8 save_xir, channel, save_car, mdm_change, mdm_status; @@ -1265,7 +1271,8 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, mdm_change = readb(base_addr + (CyMISR << index)); mdm_status = readb(base_addr + (CyMSVR1 << index)); - if (!info->port.tty) + tty = tty_port_tty_get(&info->port); + if (!tty) goto end; if (mdm_change & CyANY_DELTA) { @@ -1284,27 +1291,27 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) { if (!(mdm_status & CyDCD)) { - tty_hangup(info->port.tty); + tty_hangup(tty); info->port.flags &= ~ASYNC_NORMAL_ACTIVE; } wake_up_interruptible(&info->port.open_wait); } if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) { - if (info->port.tty->hw_stopped) { + if (tty->hw_stopped) { if (mdm_status & CyCTS) { /* cy_start isn't used because... !!! */ - info->port.tty->hw_stopped = 0; + tty->hw_stopped = 0; cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyTxRdy); - tty_wakeup(info->port.tty); + tty_wakeup(tty); } } else { if (!(mdm_status & CyCTS)) { /* cy_stop isn't used because ... !!! */ - info->port.tty->hw_stopped = 1; + tty->hw_stopped = 1; cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) & ~CyTxRdy); @@ -1315,6 +1322,7 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, } if (mdm_change & CyRI) { }*/ + tty_kref_put(tty); end: /* end of service */ cy_writeb(base_addr + (CyMIR << index), save_xir & 0x3f); @@ -1449,11 +1457,10 @@ cyz_issue_cmd(struct cyclades_card *cinfo, return 0; } /* cyz_issue_cmd */ -static void cyz_handle_rx(struct cyclades_port *info, +static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty, struct BUF_CTRL __iomem *buf_ctrl) { struct cyclades_card *cinfo = info->card; - struct tty_struct *tty = info->port.tty; unsigned int char_count; int len; #ifdef BLOCKMOVE @@ -1542,11 +1549,10 @@ static void cyz_handle_rx(struct cyclades_port *info, } } -static void cyz_handle_tx(struct cyclades_port *info, +static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty, struct BUF_CTRL __iomem *buf_ctrl) { struct cyclades_card *cinfo = info->card; - struct tty_struct *tty = info->port.tty; u8 data; unsigned int char_count; #ifdef BLOCKMOVE @@ -1642,7 +1648,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) special_count = 0; delta_count = 0; info = &cinfo->ports[channel]; - tty = info->port.tty; + tty = tty_port_tty_get(&info->port); if (tty == NULL) continue; @@ -1674,7 +1680,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) C_RS_DCD) { wake_up_interruptible(&info->port.open_wait); } else { - tty_hangup(info->port.tty); + tty_hangup(tty); wake_up_interruptible(&info->port.open_wait); info->port.flags &= ~ASYNC_NORMAL_ACTIVE; } @@ -1706,7 +1712,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, " "port %ld\n", info->card, channel); #endif - cyz_handle_rx(info, buf_ctrl); + cyz_handle_rx(info, tty, buf_ctrl); break; case C_CM_TXBEMPTY: case C_CM_TXLOWWM: @@ -1716,7 +1722,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, " "port %ld\n", info->card, channel); #endif - cyz_handle_tx(info, buf_ctrl); + cyz_handle_tx(info, tty, buf_ctrl); break; #endif /* CONFIG_CYZ_INTR */ case C_CM_FATAL: @@ -1729,6 +1735,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) wake_up_interruptible(&info->delta_msr_wait); if (special_count) tty_schedule_flip(tty); + tty_kref_put(tty); } } @@ -1774,7 +1781,6 @@ static void cyz_poll(unsigned long arg) { struct cyclades_card *cinfo; struct cyclades_port *info; - struct tty_struct *tty; struct FIRM_ID __iomem *firm_id; struct ZFW_CTRL __iomem *zfw_ctrl; struct BUF_CTRL __iomem *buf_ctrl; @@ -1802,13 +1808,19 @@ static void cyz_poll(unsigned long arg) cyz_handle_cmd(cinfo); for (port = 0; port < cinfo->nports; port++) { + struct tty_struct *tty; + info = &cinfo->ports[port]; - tty = info->port.tty; buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); + tty = tty_port_tty_get(&info->port); + /* OK to pass NULL to the handle functions below. + They need to drop the data in that case. */ + if (!info->throttle) - cyz_handle_rx(info, buf_ctrl); - cyz_handle_tx(info, buf_ctrl); + cyz_handle_rx(info, tty, buf_ctrl); + cyz_handle_tx(info, tty, buf_ctrl); + tty_kref_put(tty); } /* poll every 'cyz_polling_cycle' period */ expires = jiffies + cyz_polling_cycle; @@ -1824,7 +1836,7 @@ static void cyz_poll(unsigned long arg) /* This is called whenever a port becomes active; interrupts are enabled and DTR & RTS are turned on. */ -static int startup(struct cyclades_port *info) +static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) { struct cyclades_card *card; unsigned long flags; @@ -1848,8 +1860,7 @@ static int startup(struct cyclades_port *info) } if (!info->type) { - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); free_page(page); goto errout; } @@ -1861,7 +1872,7 @@ static int startup(struct cyclades_port *info) spin_unlock_irqrestore(&card->card_lock, flags); - set_line_char(info); + cy_set_line_char(info, tty); if (!cy_is_Z(card)) { chip = channel >> 2; @@ -1900,8 +1911,7 @@ static int startup(struct cyclades_port *info) readb(base_addr + (CySRER << index)) | CyRxData); info->port.flags |= ASYNC_INITIALIZED; - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; info->breakon = info->breakoff = 0; memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); @@ -1984,8 +1994,7 @@ static int startup(struct cyclades_port *info) /* enable send, recv, modem !!! */ info->port.flags |= ASYNC_INITIALIZED; - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; info->breakon = info->breakoff = 0; memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); @@ -2047,7 +2056,7 @@ static void start_xmit(struct cyclades_port *info) * This routine shuts down a serial port; interrupts are disabled, * and DTR is dropped if the hangup on close termio flag is on. */ -static void shutdown(struct cyclades_port *info) +static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) { struct cyclades_card *card; unsigned long flags; @@ -2083,7 +2092,7 @@ static void shutdown(struct cyclades_port *info) free_page((unsigned long)temp); } cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { + if (tty->termios->c_cflag & HUPCL) { cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); cy_writeb(base_addr + (CyMSVR2 << index), ~CyDTR); #ifdef CY_DEBUG_DTR @@ -2097,8 +2106,7 @@ static void shutdown(struct cyclades_port *info) /* it may be appropriate to clear _XMIT at some later date (after testing)!!! */ - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); } else { @@ -2132,7 +2140,7 @@ static void shutdown(struct cyclades_port *info) free_page((unsigned long)temp); } - if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL)) { + if (tty->termios->c_cflag & HUPCL) { cy_writel(&ch_ctrl[channel].rs_control, (__u32)(readl(&ch_ctrl[channel].rs_control) & ~(C_RS_RTS | C_RS_DTR))); @@ -2147,8 +2155,7 @@ static void shutdown(struct cyclades_port *info) #endif } - if (info->port.tty) - set_bit(TTY_IO_ERROR, &info->port.tty->flags); + set_bit(TTY_IO_ERROR, &tty->flags); info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); @@ -2436,7 +2443,6 @@ static int cy_open(struct tty_struct *tty, struct file *filp) printk(KERN_DEBUG "cyc:cy_open ttyC%d\n", info->line); #endif tty->driver_data = info; - info->port.tty = tty; if (serial_paranoia_check(info, tty->name, "cy_open")) return -ENODEV; @@ -2462,7 +2468,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) /* * Start up serial port */ - retval = startup(info); + retval = cy_startup(info, tty); if (retval) return retval; @@ -2476,6 +2482,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) } info->throttle = 0; + tty_port_tty_set(&info->port, tty); #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:cy_open done\n"); @@ -2705,13 +2712,13 @@ static void cy_close(struct tty_struct *tty, struct file *filp) } spin_unlock_irqrestore(&card->card_lock, flags); - shutdown(info); + cy_shutdown(info, tty); cy_flush_buffer(tty); tty_ldisc_flush(tty); spin_lock_irqsave(&card->card_lock, flags); tty->closing = 0; - info->port.tty = NULL; + tty_port_tty_set(&info->port, NULL); if (info->port.blocked_open) { spin_unlock_irqrestore(&card->card_lock, flags); if (info->port.close_delay) { @@ -2957,7 +2964,7 @@ static void cyy_baud_calc(struct cyclades_port *info, __u32 baud) * This routine finds or computes the various line characteristics. * It used to be called config_setup */ -static void set_line_char(struct cyclades_port *info) +static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) { struct cyclades_card *card; unsigned long flags; @@ -2967,28 +2974,26 @@ static void set_line_char(struct cyclades_port *info) int baud, baud_rate = 0; int i; - if (!info->port.tty || !info->port.tty->termios) + if (!tty->termios) /* XXX can this happen at all? */ return; if (info->line == -1) return; - cflag = info->port.tty->termios->c_cflag; - iflag = info->port.tty->termios->c_iflag; + cflag = tty->termios->c_cflag; + iflag = tty->termios->c_iflag; /* * Set up the tty->alt_speed kludge */ - if (info->port.tty) { - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) - info->port.tty->alt_speed = 57600; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) - info->port.tty->alt_speed = 115200; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) - info->port.tty->alt_speed = 230400; - if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) - info->port.tty->alt_speed = 460800; - } + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + tty->alt_speed = 57600; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + tty->alt_speed = 115200; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + tty->alt_speed = 230400; + if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + tty->alt_speed = 460800; card = info->card; channel = info->line - card->first_line; @@ -2998,7 +3003,7 @@ static void set_line_char(struct cyclades_port *info) index = card->bus_index; /* baud rate */ - baud = tty_get_baud_rate(info->port.tty); + baud = tty_get_baud_rate(tty); if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { if (info->custom_divisor) @@ -3123,9 +3128,8 @@ static void set_line_char(struct cyclades_port *info) /* set line characteristics according configuration */ - cy_writeb(base_addr + (CySCHR1 << index), - START_CHAR(info->port.tty)); - cy_writeb(base_addr + (CySCHR2 << index), STOP_CHAR(info->port.tty)); + cy_writeb(base_addr + (CySCHR1 << index), START_CHAR(tty)); + cy_writeb(base_addr + (CySCHR2 << index), STOP_CHAR(tty)); cy_writeb(base_addr + (CyCOR1 << index), info->cor1); cy_writeb(base_addr + (CyCOR2 << index), info->cor2); cy_writeb(base_addr + (CyCOR3 << index), info->cor3); @@ -3141,7 +3145,7 @@ static void set_line_char(struct cyclades_port *info) (info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ - if (C_CLOCAL(info->port.tty)) { + if (C_CLOCAL(tty)) { /* without modem intr */ cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyMdmCh); @@ -3204,8 +3208,7 @@ static void set_line_char(struct cyclades_port *info) #endif } - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); spin_unlock_irqrestore(&card->card_lock, flags); } else { @@ -3224,7 +3227,7 @@ static void set_line_char(struct cyclades_port *info) ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); /* baud rate */ - baud = tty_get_baud_rate(info->port.tty); + baud = tty_get_baud_rate(tty); if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) { if (info->custom_divisor) @@ -3335,8 +3338,7 @@ static void set_line_char(struct cyclades_port *info) "was %x\n", info->line, retval); } - if (info->port.tty) - clear_bit(TTY_IO_ERROR, &info->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); } } /* set_line_char */ @@ -3365,7 +3367,7 @@ get_serial_info(struct cyclades_port *info, } /* get_serial_info */ static int -set_serial_info(struct cyclades_port *info, +cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty, struct serial_struct __user *new_info) { struct serial_struct new_serial; @@ -3403,10 +3405,10 @@ set_serial_info(struct cyclades_port *info, check_and_exit: if (info->port.flags & ASYNC_INITIALIZED) { - set_line_char(info); + cy_set_line_char(info, tty); return 0; } else { - return startup(info); + return cy_startup(info, tty); } } /* set_serial_info */ @@ -3955,7 +3957,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file, ret_val = get_serial_info(info, argp); break; case TIOCSSERIAL: - ret_val = set_serial_info(info, argp); + ret_val = cy_set_serial_info(info, tty, argp); break; case TIOCSERGETLSR: /* Get line status register */ ret_val = get_lsr_info(info, argp); @@ -4055,7 +4057,7 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios) printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line); #endif - set_line_char(info); + cy_set_line_char(info, tty); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { @@ -4299,13 +4301,13 @@ static void cy_hangup(struct tty_struct *tty) return; cy_flush_buffer(tty); - shutdown(info); + cy_shutdown(info, tty); info->port.count = 0; #ifdef CY_DEBUG_COUNT printk(KERN_DEBUG "cyc:cy_hangup (%d): setting count to 0\n", current->pid); #endif - info->port.tty = NULL; + tty_port_tty_set(&info->port, NULL); info->port.flags &= ~ASYNC_NORMAL_ACTIVE; wake_up_interruptible(&info->port.open_wait); } /* cy_hangup */ @@ -5191,18 +5193,30 @@ static int cyclades_proc_show(struct seq_file *m, void *v) for (j = 0; j < cy_card[i].nports; j++) { info = &cy_card[i].ports[j]; - if (info->port.count) + if (info->port.count) { + /* XXX is the ldisc num worth this? */ + struct tty_struct *tty; + struct tty_ldisc *ld; + int num = 0; + tty = tty_port_tty_get(&info->port); + if (tty) { + ld = tty_ldisc_ref(tty); + if (ld) { + num = ld->ops->num; + tty_ldisc_deref(ld); + } + tty_kref_put(tty); + } seq_printf(m, "%3d %8lu %10lu %8lu " - "%10lu %8lu %9lu %6ld\n", info->line, + "%10lu %8lu %9lu %6d\n", info->line, (cur_jifs - info->idle_stats.in_use) / HZ, info->idle_stats.xmit_bytes, (cur_jifs - info->idle_stats.xmit_idle)/ HZ, info->idle_stats.recv_bytes, (cur_jifs - info->idle_stats.recv_idle)/ HZ, info->idle_stats.overruns, - /* FIXME: double check locking */ - (long)info->port.tty->ldisc->ops->num); - else + num); + } else seq_printf(m, "%3d %8lu %10lu %8lu " "%10lu %8lu %9lu %6ld\n", info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L); -- cgit v1.2.3 From f0737579424dd2c4e68bdd54c718455a3f42c7b5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:12 -0700 Subject: cyclades: remove block_til_ready Use a tty_port common instead. This saves lots of .text and makes the code a lot more readable. This involves separation of a dtr_rts handling, next patches will use that to not duplicate the code all over the place. Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 302 +++++++++++++++++------------------------------- 1 file changed, 108 insertions(+), 194 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index e2f731b70763..9e0cd7020aef 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -2172,199 +2172,6 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) * ------------------------------------------------------------ */ -static int -block_til_ready(struct tty_struct *tty, struct file *filp, - struct cyclades_port *info) -{ - DECLARE_WAITQUEUE(wait, current); - struct cyclades_card *cinfo; - unsigned long flags; - int chip, channel, index; - int retval; - void __iomem *base_addr; - - cinfo = info->card; - channel = info->line - cinfo->first_line; - - /* - * If the device is in the middle of being closed, then block - * until it's done, and then try again. - */ - if (tty_hung_up_p(filp) || (info->port.flags & ASYNC_CLOSING)) { - wait_event_interruptible(info->port.close_wait, - !(info->port.flags & ASYNC_CLOSING)); - return (info->port.flags & ASYNC_HUP_NOTIFY) ? -EAGAIN: -ERESTARTSYS; - } - - /* - * If non-blocking mode is set, then make the check up front - * and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - info->port.flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - /* - * Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->port.count is dropped by one, so that - * cy_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - retval = 0; - add_wait_queue(&info->port.open_wait, &wait); -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc block_til_ready before block: ttyC%d, " - "count = %d\n", info->line, info->port.count); -#endif - spin_lock_irqsave(&cinfo->card_lock, flags); - if (!tty_hung_up_p(filp)) - info->port.count--; - spin_unlock_irqrestore(&cinfo->card_lock, flags); -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc block_til_ready: (%d): decrementing count to " - "%d\n", current->pid, info->port.count); -#endif - info->port.blocked_open++; - - if (!cy_is_Z(cinfo)) { - chip = channel >> 2; - channel &= 0x03; - index = cinfo->bus_index; - base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); - - while (1) { - spin_lock_irqsave(&cinfo->card_lock, flags); - if ((tty->termios->c_cflag & CBAUD)) { - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - cy_writeb(base_addr + (CyMSVR1 << index), - CyRTS); - cy_writeb(base_addr + (CyMSVR2 << index), - CyDTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:block_til_ready raising " - "DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - } - spin_unlock_irqrestore(&cinfo->card_lock, flags); - - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->port.flags & ASYNC_INITIALIZED)) { - retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - break; - } - - spin_lock_irqsave(&cinfo->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (!(info->port.flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || - (readb(base_addr + - (CyMSVR1 << index)) & CyDCD))) { - spin_unlock_irqrestore(&cinfo->card_lock, flags); - break; - } - spin_unlock_irqrestore(&cinfo->card_lock, flags); - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc block_til_ready blocking: " - "ttyC%d, count = %d\n", - info->line, info->port.count); -#endif - schedule(); - } - } else { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; - struct CH_CTRL __iomem *ch_ctrl; - - base_addr = cinfo->base_addr; - firm_id = base_addr + ID_ADDRESS; - if (!cyz_is_loaded(cinfo)) { - __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->port.open_wait, &wait); - return -EINVAL; - } - - zfw_ctrl = base_addr + (readl(&firm_id->zfwctrl_addr) - & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = zfw_ctrl->ch_ctrl; - - while (1) { - if ((tty->termios->c_cflag & CBAUD)) { - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) | - C_RS_RTS | C_RS_DTR); - retval = cyz_issue_cmd(cinfo, - channel, C_CM_IOCTLM, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:block_til_ready " - "retval on ttyC%d was %x\n", - info->line, retval); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:block_til_ready raising " - "Z DTR\n"); -#endif - } - - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(info->port.flags & ASYNC_INITIALIZED)) { - retval = ((info->port.flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS); - break; - } - if (!(info->port.flags & ASYNC_CLOSING) && (C_CLOCAL(tty) || - (readl(&ch_ctrl[channel].rs_status) & - C_RS_DCD))) { - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc block_til_ready blocking: " - "ttyC%d, count = %d\n", - info->line, info->port.count); -#endif - schedule(); - } - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&info->port.open_wait, &wait); - if (!tty_hung_up_p(filp)) { - info->port.count++; -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc:block_til_ready (%d): incrementing " - "count to %d\n", current->pid, info->port.count); -#endif - } - info->port.blocked_open--; -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc:block_til_ready after blocking: ttyC%d, " - "count = %d\n", info->line, info->port.count); -#endif - if (retval) - return retval; - info->port.flags |= ASYNC_NORMAL_ACTIVE; - return 0; -} /* block_til_ready */ - /* * This routine is called whenever a serial port is opened. It * performs the serial-specific initialization for the tty structure. @@ -2472,7 +2279,7 @@ static int cy_open(struct tty_struct *tty, struct file *filp) if (retval) return retval; - retval = block_til_ready(tty, filp, info); + retval = tty_port_block_til_ready(&info->port, tty, filp); if (retval) { #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc:cy_open returning after block_til_ready " @@ -4312,6 +4119,111 @@ static void cy_hangup(struct tty_struct *tty) wake_up_interruptible(&info->port.open_wait); } /* cy_hangup */ +static int cyy_carrier_raised(struct tty_port *port) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *cinfo = info->card; + void __iomem *base = cinfo->base_addr; + unsigned long flags; + int channel = info->line - cinfo->first_line; + int chip = channel >> 2, index = cinfo->bus_index; + u32 cd; + + channel &= 0x03; + base += cy_chip_offset[chip] << index; + + spin_lock_irqsave(&cinfo->card_lock, flags); + cy_writeb(base + (CyCAR << index), (u8)channel); + cd = readb(base + (CyMSVR1 << index)) & CyDCD; + spin_unlock_irqrestore(&cinfo->card_lock, flags); + + return cd; +} + +static void cyy_dtr_rts(struct tty_port *port, int raise) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *cinfo = info->card; + void __iomem *base = cinfo->base_addr; + unsigned long flags; + int channel = info->line - cinfo->first_line; + int chip = channel >> 2, index = cinfo->bus_index; + + channel &= 0x03; + base += cy_chip_offset[chip] << index; + + spin_lock_irqsave(&cinfo->card_lock, flags); + cy_writeb(base + (CyCAR << index), (u8)channel); + cy_writeb(base + (CyMSVR1 << index), raise ? CyRTS : ~CyRTS); + cy_writeb(base + (CyMSVR2 << index), raise ? CyDTR : ~CyDTR); +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "%s: raising DTR\n", __func__); + printk(KERN_DEBUG " status: 0x%x, 0x%x\n", + readb(base + (CyMSVR1 << index)), + readb(base + (CyMSVR2 << index))); +#endif + spin_unlock_irqrestore(&cinfo->card_lock, flags); +} + +static int cyz_carrier_raised(struct tty_port *port) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *cinfo = info->card; + void __iomem *base = cinfo->base_addr; + struct FIRM_ID __iomem *firm_id = base + ID_ADDRESS; + struct ZFW_CTRL __iomem *zfw_ctrl; + struct CH_CTRL __iomem *ch_ctrl; + int channel = info->line - cinfo->first_line; + + zfw_ctrl = base + (readl(&firm_id->zfwctrl_addr) & 0xfffff); + ch_ctrl = zfw_ctrl->ch_ctrl; + + return readl(&ch_ctrl[channel].rs_status) & C_RS_DCD; +} + +static void cyz_dtr_rts(struct tty_port *port, int raise) +{ + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); + struct cyclades_card *cinfo = info->card; + void __iomem *base = cinfo->base_addr; + struct FIRM_ID __iomem *firm_id = base + ID_ADDRESS; + struct ZFW_CTRL __iomem *zfw_ctrl; + struct CH_CTRL __iomem *ch_ctrl; + int ret, channel = info->line - cinfo->first_line; + u32 rs; + + zfw_ctrl = base + (readl(&firm_id->zfwctrl_addr) & 0xfffff); + ch_ctrl = zfw_ctrl->ch_ctrl; + + rs = readl(&ch_ctrl[channel].rs_control); + if (raise) + rs |= C_RS_RTS | C_RS_DTR; + else + rs &= ~(C_RS_RTS | C_RS_DTR); + cy_writel(&ch_ctrl[channel].rs_control, rs); + ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L); + if (ret != 0) + printk(KERN_ERR "%s: retval on ttyC%d was %x\n", + __func__, info->line, ret); +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "%s: raising Z DTR\n", __func__); +#endif +} + +static const struct tty_port_operations cyy_port_ops = { + .carrier_raised = cyy_carrier_raised, + .dtr_rts = cyy_dtr_rts, +}; + +static const struct tty_port_operations cyz_port_ops = { + .carrier_raised = cyz_carrier_raised, + .dtr_rts = cyz_dtr_rts, +}; + /* * --------------------------------------------------------------------- * cy_init() and friends @@ -4351,6 +4263,7 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) init_waitqueue_head(&info->delta_msr_wait); if (cy_is_Z(cinfo)) { + info->port.ops = &cyz_port_ops; info->type = PORT_STARTECH; if (cinfo->hw_ver == ZO_V1) info->xmit_fifo_size = CYZ_FIFO_SIZE; @@ -4362,6 +4275,7 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) #endif } else { int index = cinfo->bus_index; + info->port.ops = &cyy_port_ops; info->type = PORT_CIRRUS; info->xmit_fifo_size = CyMAX_CHAR_FIFO; info->cor1 = CyPARITY_NONE | Cy_1_STOP | Cy_8_BITS; -- cgit v1.2.3 From f0eefdc30e55e761facf645bd1be1339b21c30e6 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:13 -0700 Subject: cyclades: avoid addresses recomputation Don't fetch firmware address and recompute channel control on each port access. Precompute the values on init and use them later all the time. The same for board control. This simplify code and improves readability. Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 241 ++++++++++++++--------------------------------- include/linux/cyclades.h | 10 ++ 2 files changed, 81 insertions(+), 170 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 9e0cd7020aef..7a7092ae5d39 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -814,7 +814,7 @@ static char rflow_thr[] = { /* rflow threshold */ /* The Cyclom-Ye has placed the sequential chips in non-sequential * address order. This look-up table overcomes that problem. */ -static int cy_chip_offset[] = { 0x0000, +static const unsigned int cy_chip_offset[] = { 0x0000, 0x0400, 0x0800, 0x0C00, @@ -1406,15 +1406,9 @@ static int cyz_fetch_msg(struct cyclades_card *cinfo, __u32 *channel, __u8 *cmd, __u32 *param) { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; + struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; unsigned long loc_doorbell; - firm_id = cinfo->base_addr + ID_ADDRESS; - zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - loc_doorbell = readl(&cinfo->ctl_addr.p9060->loc_doorbell); if (loc_doorbell) { *cmd = (char)(0xff & loc_doorbell); @@ -1430,19 +1424,13 @@ static int cyz_issue_cmd(struct cyclades_card *cinfo, __u32 channel, __u8 cmd, __u32 param) { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; + struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; __u32 __iomem *pci_doorbell; unsigned int index; - firm_id = cinfo->base_addr + ID_ADDRESS; if (!cyz_is_loaded(cinfo)) return -1; - zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - index = 0; pci_doorbell = &cinfo->ctl_addr.p9060->pci_doorbell; while ((readl(pci_doorbell) & 0xff) != 0) { @@ -1457,9 +1445,9 @@ cyz_issue_cmd(struct cyclades_card *cinfo, return 0; } /* cyz_issue_cmd */ -static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty, - struct BUF_CTRL __iomem *buf_ctrl) +static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty) { + struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; struct cyclades_card *cinfo = info->card; unsigned int char_count; int len; @@ -1549,9 +1537,9 @@ static void cyz_handle_rx(struct cyclades_port *info, struct tty_struct *tty, } } -static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty, - struct BUF_CTRL __iomem *buf_ctrl) +static void cyz_handle_tx(struct cyclades_port *info, struct tty_struct *tty) { + struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; struct cyclades_card *cinfo = info->card; u8 data; unsigned int char_count; @@ -1627,21 +1615,14 @@ ztxdone: static void cyz_handle_cmd(struct cyclades_card *cinfo) { + struct BOARD_CTRL __iomem *board_ctrl = cinfo->board_ctrl; struct tty_struct *tty; struct cyclades_port *info; - static struct FIRM_ID __iomem *firm_id; - static struct ZFW_CTRL __iomem *zfw_ctrl; - static struct BOARD_CTRL __iomem *board_ctrl; - static struct CH_CTRL __iomem *ch_ctrl; - static struct BUF_CTRL __iomem *buf_ctrl; __u32 channel, param, fw_ver; __u8 cmd; int special_count; int delta_count; - firm_id = cinfo->base_addr + ID_ADDRESS; - zfw_ctrl = cinfo->base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; fw_ver = readl(&board_ctrl->fw_version); while (cyz_fetch_msg(cinfo, &channel, &cmd, ¶m) == 1) { @@ -1652,9 +1633,6 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) if (tty == NULL) continue; - ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); - buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); - switch (cmd) { case C_CM_PR_ERROR: tty_insert_flip_char(tty, 0, TTY_PARITY); @@ -1675,9 +1653,9 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) info->icount.dcd++; delta_count++; if (info->port.flags & ASYNC_CHECK_CD) { - if ((fw_ver > 241 ? ((u_long) param) : - readl(&ch_ctrl->rs_status)) & - C_RS_DCD) { + u32 dcd = fw_ver > 241 ? param : + readl(&info->u.cyz.ch_ctrl->rs_status); + if (dcd & C_RS_DCD) { wake_up_interruptible(&info->port.open_wait); } else { tty_hangup(tty); @@ -1712,7 +1690,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) printk(KERN_DEBUG "cyz_interrupt: rcvd intr, card %d, " "port %ld\n", info->card, channel); #endif - cyz_handle_rx(info, tty, buf_ctrl); + cyz_handle_rx(info, tty); break; case C_CM_TXBEMPTY: case C_CM_TXLOWWM: @@ -1722,7 +1700,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) printk(KERN_DEBUG "cyz_interrupt: xmit intr, card %d, " "port %ld\n", info->card, channel); #endif - cyz_handle_tx(info, tty, buf_ctrl); + cyz_handle_tx(info, tty); break; #endif /* CONFIG_CYZ_INTR */ case C_CM_FATAL: @@ -1781,9 +1759,6 @@ static void cyz_poll(unsigned long arg) { struct cyclades_card *cinfo; struct cyclades_port *info; - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BUF_CTRL __iomem *buf_ctrl; unsigned long expires = jiffies + HZ; unsigned int port, card; @@ -1795,10 +1770,6 @@ static void cyz_poll(unsigned long arg) if (!cyz_is_loaded(cinfo)) continue; - firm_id = cinfo->base_addr + ID_ADDRESS; - zfw_ctrl = cinfo->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - /* Skip first polling cycle to avoid racing conditions with the FW */ if (!cinfo->intr_enabled) { cinfo->intr_enabled = 1; @@ -1811,15 +1782,13 @@ static void cyz_poll(unsigned long arg) struct tty_struct *tty; info = &cinfo->ports[port]; - buf_ctrl = &(zfw_ctrl->buf_ctrl[port]); - tty = tty_port_tty_get(&info->port); /* OK to pass NULL to the handle functions below. They need to drop the data in that case. */ if (!info->throttle) - cyz_handle_rx(info, tty, buf_ctrl); - cyz_handle_tx(info, tty, buf_ctrl); + cyz_handle_rx(info, tty); + cyz_handle_tx(info, tty); tty_kref_put(tty); } /* poll every 'cyz_polling_cycle' period */ @@ -1922,45 +1891,34 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) spin_unlock_irqrestore(&card->card_lock, flags); } else { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; - struct CH_CTRL __iomem *ch_ctrl; - - base_addr = card->base_addr; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - firm_id = base_addr + ID_ADDRESS; if (!cyz_is_loaded(card)) return -ENODEV; - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = zfw_ctrl->ch_ctrl; - #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc startup Z card %d, channel %d, " - "base_addr %p\n", card, channel, base_addr); + "base_addr %p\n", card, channel, card->base_addr); #endif spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE); + cy_writel(&ch_ctrl->op_mode, C_CH_ENABLE); #ifdef Z_WAKE #ifdef CONFIG_CYZ_INTR - cy_writel(&ch_ctrl[channel].intr_enable, + cy_writel(&ch_ctrl->intr_enable, C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | C_IN_RXNNDT | C_IN_IOCTLW | C_IN_MDCD); #else - cy_writel(&ch_ctrl[channel].intr_enable, + cy_writel(&ch_ctrl->intr_enable, C_IN_IOCTLW | C_IN_MDCD); #endif /* CONFIG_CYZ_INTR */ #else #ifdef CONFIG_CYZ_INTR - cy_writel(&ch_ctrl[channel].intr_enable, + cy_writel(&ch_ctrl->intr_enable, C_IN_TXBEMPTY | C_IN_TXLOWWM | C_IN_RXHIWM | C_IN_RXNNDT | C_IN_MDCD); #else - cy_writel(&ch_ctrl[channel].intr_enable, C_IN_MDCD); + cy_writel(&ch_ctrl->intr_enable, C_IN_MDCD); #endif /* CONFIG_CYZ_INTR */ #endif /* Z_WAKE */ @@ -1979,9 +1937,8 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) /* set timeout !!! */ /* set RTS and DTR !!! */ - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) | C_RS_RTS | - C_RS_DTR); + cy_writel(&ch_ctrl->rs_control, readl(&ch_ctrl->rs_control) | + C_RS_RTS | C_RS_DTR); retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); if (retval != 0) { printk(KERN_ERR "cyc:startup(3) retval on ttyC%d was " @@ -2110,27 +2067,17 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); } else { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; - struct CH_CTRL __iomem *ch_ctrl; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; int retval; - base_addr = card->base_addr; #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, " - "base_addr %p\n", card, channel, base_addr); + "base_addr %p\n", card, channel, card->base_addr); #endif - firm_id = base_addr + ID_ADDRESS; if (!cyz_is_loaded(card)) return; - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = zfw_ctrl->ch_ctrl; - spin_lock_irqsave(&card->card_lock, flags); if (info->port.xmit_buf) { @@ -2141,9 +2088,9 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) } if (tty->termios->c_cflag & HUPCL) { - cy_writel(&ch_ctrl[channel].rs_control, - (__u32)(readl(&ch_ctrl[channel].rs_control) & - ~(C_RS_RTS | C_RS_DTR))); + cy_writel(&ch_ctrl->rs_control, + readl(&ch_ctrl->rs_control) & + ~(C_RS_RTS | C_RS_DTR)); retval = cyz_issue_cmd(info->card, channel, C_CM_IOCTLM, 0L); if (retval != 0) { @@ -2497,15 +2444,11 @@ static void cy_close(struct tty_struct *tty, struct file *filp) #ifdef Z_WAKE /* Waiting for on-board buffers to be empty before closing the port */ - void __iomem *base_addr = card->base_addr; - struct FIRM_ID __iomem *firm_id = base_addr + ID_ADDRESS; - struct ZFW_CTRL __iomem *zfw_ctrl = - base_addr + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - struct CH_CTRL __iomem *ch_ctrl = zfw_ctrl->ch_ctrl; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; int channel = info->line - card->first_line; int retval; - if (readl(&ch_ctrl[channel].flow_status) != C_FS_TXIDLE) { + if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) { retval = cyz_issue_cmd(card, channel, C_CM_IOCTLW, 0L); if (retval != 0) { printk(KERN_DEBUG "cyc:cy_close retval on " @@ -2685,18 +2628,13 @@ static int cy_write_room(struct tty_struct *tty) static int cy_chars_in_buffer(struct tty_struct *tty) { - struct cyclades_card *card; struct cyclades_port *info = tty->driver_data; - int channel; if (serial_paranoia_check(info, tty->name, "cy_chars_in_buffer")) return 0; - card = info->card; - channel = (info->line) - (card->first_line); - #ifdef Z_EXT_CHARS_IN_BUFFER - if (!cy_is_Z(card)) { + if (!cy_is_Z(info->card)) { #endif /* Z_EXT_CHARS_IN_BUFFER */ #ifdef CY_DEBUG_IO printk(KERN_DEBUG "cyc:cy_chars_in_buffer ttyC%d %d\n", @@ -2705,20 +2643,11 @@ static int cy_chars_in_buffer(struct tty_struct *tty) return info->xmit_cnt; #ifdef Z_EXT_CHARS_IN_BUFFER } else { - static struct FIRM_ID *firm_id; - static struct ZFW_CTRL *zfw_ctrl; - static struct CH_CTRL *ch_ctrl; - static struct BUF_CTRL *buf_ctrl; + struct BUF_CTRL __iomem *buf_ctrl = info->u.cyz.buf_ctrl; int char_count; __u32 tx_put, tx_get, tx_bufsize; lock_kernel(); - firm_id = card->base_addr + ID_ADDRESS; - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); - buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]); - tx_get = readl(&buf_ctrl->tx_get); tx_put = readl(&buf_ctrl->tx_put); tx_bufsize = readl(&buf_ctrl->tx_bufsize); @@ -3019,20 +2948,13 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) spin_unlock_irqrestore(&card->card_lock, flags); } else { - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct CH_CTRL __iomem *ch_ctrl; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; __u32 sw_flow; int retval; - firm_id = card->base_addr + ID_ADDRESS; if (!cyz_is_loaded(card)) return; - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]); - /* baud rate */ baud = tty_get_baud_rate(tty); if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == @@ -3268,10 +3190,6 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file) unsigned char status; unsigned long lstatus; unsigned int result; - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; - struct CH_CTRL __iomem *ch_ctrl; if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; @@ -3304,14 +3222,8 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file) ((status & CyDSR) ? TIOCM_DSR : 0) | ((status & CyCTS) ? TIOCM_CTS : 0); } else { - base_addr = card->base_addr; - firm_id = card->base_addr + ID_ADDRESS; if (cyz_is_loaded(card)) { - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = zfw_ctrl->ch_ctrl; - lstatus = readl(&ch_ctrl[channel].rs_status); + lstatus = readl(&info->u.cyz.ch_ctrl->rs_status); result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) | @@ -3336,12 +3248,7 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; int chip, channel, index; - void __iomem *base_addr; unsigned long flags; - struct FIRM_ID __iomem *firm_id; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct BOARD_CTRL __iomem *board_ctrl; - struct CH_CTRL __iomem *ch_ctrl; int retval; if (serial_paranoia_check(info, tty->name, __func__)) @@ -3350,6 +3257,7 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, card = info->card; channel = (info->line) - (card->first_line); if (!cy_is_Z(card)) { + void __iomem *base_addr; chip = channel >> 2; channel &= 0x03; index = card->bus_index; @@ -3421,34 +3329,26 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, spin_unlock_irqrestore(&card->card_lock, flags); } } else { - base_addr = card->base_addr; - - firm_id = card->base_addr + ID_ADDRESS; if (cyz_is_loaded(card)) { - zfw_ctrl = card->base_addr + - (readl(&firm_id->zfwctrl_addr) & 0xfffff); - board_ctrl = &zfw_ctrl->board_ctrl; - ch_ctrl = zfw_ctrl->ch_ctrl; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; if (set & TIOCM_RTS) { spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) | - C_RS_RTS); + cy_writel(&ch_ctrl->rs_control, + readl(&ch_ctrl->rs_control) | C_RS_RTS); spin_unlock_irqrestore(&card->card_lock, flags); } if (clear & TIOCM_RTS) { spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) & + cy_writel(&ch_ctrl->rs_control, + readl(&ch_ctrl->rs_control) & ~C_RS_RTS); spin_unlock_irqrestore(&card->card_lock, flags); } if (set & TIOCM_DTR) { spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) | - C_RS_DTR); + cy_writel(&ch_ctrl->rs_control, + readl(&ch_ctrl->rs_control) | C_RS_DTR); #ifdef CY_DEBUG_DTR printk(KERN_DEBUG "cyc:set_modem_info raising " "Z DTR\n"); @@ -3457,8 +3357,8 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, } if (clear & TIOCM_DTR) { spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl[channel].rs_control, - readl(&ch_ctrl[channel].rs_control) & + cy_writel(&ch_ctrl->rs_control, + readl(&ch_ctrl->rs_control) & ~C_RS_DTR); #ifdef CY_DEBUG_DTR printk(KERN_DEBUG "cyc:set_modem_info clearing " @@ -4171,17 +4071,8 @@ static int cyz_carrier_raised(struct tty_port *port) { struct cyclades_port *info = container_of(port, struct cyclades_port, port); - struct cyclades_card *cinfo = info->card; - void __iomem *base = cinfo->base_addr; - struct FIRM_ID __iomem *firm_id = base + ID_ADDRESS; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct CH_CTRL __iomem *ch_ctrl; - int channel = info->line - cinfo->first_line; - zfw_ctrl = base + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - ch_ctrl = zfw_ctrl->ch_ctrl; - - return readl(&ch_ctrl[channel].rs_status) & C_RS_DCD; + return readl(&info->u.cyz.ch_ctrl->rs_status) & C_RS_DCD; } static void cyz_dtr_rts(struct tty_port *port, int raise) @@ -4189,22 +4080,16 @@ static void cyz_dtr_rts(struct tty_port *port, int raise) struct cyclades_port *info = container_of(port, struct cyclades_port, port); struct cyclades_card *cinfo = info->card; - void __iomem *base = cinfo->base_addr; - struct FIRM_ID __iomem *firm_id = base + ID_ADDRESS; - struct ZFW_CTRL __iomem *zfw_ctrl; - struct CH_CTRL __iomem *ch_ctrl; + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; int ret, channel = info->line - cinfo->first_line; u32 rs; - zfw_ctrl = base + (readl(&firm_id->zfwctrl_addr) & 0xfffff); - ch_ctrl = zfw_ctrl->ch_ctrl; - - rs = readl(&ch_ctrl[channel].rs_control); + rs = readl(&ch_ctrl->rs_control); if (raise) rs |= C_RS_RTS | C_RS_DTR; else rs &= ~(C_RS_RTS | C_RS_DTR); - cy_writel(&ch_ctrl[channel].rs_control, rs); + cy_writel(&ch_ctrl->rs_control, rs); ret = cyz_issue_cmd(cinfo, channel, C_CM_IOCTLM, 0L); if (ret != 0) printk(KERN_ERR "%s: retval on ttyC%d was %x\n", @@ -4235,8 +4120,7 @@ static const struct tty_port_operations cyz_port_ops = { static int __devinit cy_init_card(struct cyclades_card *cinfo) { struct cyclades_port *info; - unsigned int port; - unsigned short chip_number; + unsigned int channel, port; spin_lock_init(&cinfo->card_lock); cinfo->intr_enabled = 0; @@ -4248,9 +4132,9 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) return -ENOMEM; } - for (port = cinfo->first_line; port < cinfo->first_line + cinfo->nports; - port++) { - info = &cinfo->ports[port - cinfo->first_line]; + for (channel = 0, port = cinfo->first_line; channel < cinfo->nports; + channel++, port++) { + info = &cinfo->ports[channel]; tty_port_init(&info->port); info->magic = CYCLADES_MAGIC; info->card = cinfo; @@ -4263,8 +4147,17 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) init_waitqueue_head(&info->delta_msr_wait); if (cy_is_Z(cinfo)) { + struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS; + struct ZFW_CTRL *zfw_ctrl; + info->port.ops = &cyz_port_ops; info->type = PORT_STARTECH; + + zfw_ctrl = cinfo->base_addr + + (readl(&firm_id->zfwctrl_addr) & 0xfffff); + info->u.cyz.ch_ctrl = &zfw_ctrl->ch_ctrl[channel]; + info->u.cyz.buf_ctrl = &zfw_ctrl->buf_ctrl[channel]; + if (cinfo->hw_ver == ZO_V1) info->xmit_fifo_size = CYZ_FIFO_SIZE; else @@ -4274,7 +4167,9 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) cyz_rx_restart, (unsigned long)info); #endif } else { + unsigned short chip_number; int index = cinfo->bus_index; + info->port.ops = &cyy_port_ops; info->type = PORT_CIRRUS; info->xmit_fifo_size = CyMAX_CHAR_FIFO; @@ -4282,7 +4177,7 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) info->cor2 = CyETC; info->cor3 = 0x08; /* _very_ small rcv threshold */ - chip_number = (port - cinfo->first_line) / 4; + chip_number = channel / CyPORTS_PER_CHIP; info->chip_rev = readb(cinfo->base_addr + (cy_chip_offset[chip_number] << index) + (CyGFRCR << index)); @@ -4976,8 +4871,14 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, } cy_card[card_no].num_chips = nchan / CyPORTS_PER_CHIP; } else { + struct FIRM_ID __iomem *firm_id = addr2 + ID_ADDRESS; + struct ZFW_CTRL __iomem *zfw_ctrl; + + zfw_ctrl = addr2 + (readl(&firm_id->zfwctrl_addr) & 0xfffff); + cy_card[card_no].hw_ver = mailbox; cy_card[card_no].num_chips = (unsigned int)-1; + cy_card[card_no].board_ctrl = &zfw_ctrl->board_ctrl; #ifdef CONFIG_CYZ_INTR /* allocate IRQ only if board has an IRQ */ if (irq != 0 && irq != 255) { diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index 1fbdea4f08eb..1eb87a6a2f6b 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -499,6 +499,7 @@ struct cyclades_card { void __iomem *p9050; struct RUNTIME_9060 __iomem *p9060; } ctl_addr; + struct BOARD_CTRL __iomem *board_ctrl; /* cyz specific */ int irq; unsigned int num_chips; /* 0 if card absent, -1 if Z/PCI, else Y */ unsigned int first_line; /* minor number of first channel on card */ @@ -541,6 +542,15 @@ struct cyclades_port { int magic; struct tty_port port; struct cyclades_card *card; + union { + struct { + int filler; + } cyy; + struct { + struct CH_CTRL __iomem *ch_ctrl; + struct BUF_CTRL __iomem *buf_ctrl; + } cyz; + } u; int line; int flags; /* defined in tty.h */ int type; /* UART type */ -- cgit v1.2.3 From 174e6fe01e7881caaa350b5e98e4c6189b6cb593 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:13 -0700 Subject: cyclades: switch to tty_port_hangup Do not duplicate common tty_port_hangup code. Use it instead. Also do not unset ASYNC_NORMAL_ACTIVE and wake up from the tty_hangup() caller. It makes no sense since we don't check that flag in sleepers. tty_port_hangup() performed later will do the right job. Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 7a7092ae5d39..226175d605c3 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1290,11 +1290,10 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, } if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) { - if (!(mdm_status & CyDCD)) { + if (mdm_status & CyDCD) + wake_up_interruptible(&info->port.open_wait); + else tty_hangup(tty); - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - } - wake_up_interruptible(&info->port.open_wait); } if ((mdm_change & CyCTS) && (info->port.flags & ASYNC_CTS_FLOW)) { if (tty->hw_stopped) { @@ -1655,13 +1654,10 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) if (info->port.flags & ASYNC_CHECK_CD) { u32 dcd = fw_ver > 241 ? param : readl(&info->u.cyz.ch_ctrl->rs_status); - if (dcd & C_RS_DCD) { + if (dcd & C_RS_DCD) wake_up_interruptible(&info->port.open_wait); - } else { + else tty_hangup(tty); - wake_up_interruptible(&info->port.open_wait); - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - } } break; case C_CM_MCTS: @@ -4009,14 +4005,7 @@ static void cy_hangup(struct tty_struct *tty) cy_flush_buffer(tty); cy_shutdown(info, tty); - info->port.count = 0; -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc:cy_hangup (%d): setting count to 0\n", - current->pid); -#endif - tty_port_tty_set(&info->port, NULL); - info->port.flags &= ~ASYNC_NORMAL_ACTIVE; - wake_up_interruptible(&info->port.open_wait); + tty_port_hangup(&info->port); } /* cy_hangup */ static int cyy_carrier_raised(struct tty_port *port) -- cgit v1.2.3 From 23342262964722b41c7d06cfadc215039e48881b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:13 -0700 Subject: cyclades: close cleanup Use new tty helpers for close, which allows much code removal. The only real change is locking. card_lock for protecting was used inappropriately (just to have a critical section, no matter which lock is used), so the change to port->lock is fine. Remove also useless debug printks while being there. Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 70 ++----------------------------------------------- 1 file changed, 2 insertions(+), 68 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 226175d605c3..87a40bc75646 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -2361,61 +2361,13 @@ static void cy_close(struct tty_struct *tty, struct file *filp) struct cyclades_card *card; unsigned long flags; -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_close ttyC%d\n", info->line); -#endif - if (!info || serial_paranoia_check(info, tty->name, "cy_close")) return; card = info->card; - spin_lock_irqsave(&card->card_lock, flags); - /* If the TTY is being hung up, nothing to do */ - if (tty_hung_up_p(filp)) { - spin_unlock_irqrestore(&card->card_lock, flags); + if (!tty_port_close_start(&info->port, tty, filp)) return; - } -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc:cy_close ttyC%d, count = %d\n", info->line, - info->port.count); -#endif - if ((tty->count == 1) && (info->port.count != 1)) { - /* - * Uh, oh. tty->count is 1, which means that the tty - * structure will be freed. Info->count should always - * be one in these conditions. If it's greater than - * one, we've got real problems, since it means the - * serial port won't be shutdown. - */ - printk(KERN_ERR "cyc:cy_close: bad serial port count; " - "tty->count is 1, info->port.count is %d\n", info->port.count); - info->port.count = 1; - } -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc:cy_close at (%d): decrementing count to %d\n", - current->pid, info->port.count - 1); -#endif - if (--info->port.count < 0) { -#ifdef CY_DEBUG_COUNT - printk(KERN_DEBUG "cyc:cyc_close setting count to 0\n"); -#endif - info->port.count = 0; - } - if (info->port.count) { - spin_unlock_irqrestore(&card->card_lock, flags); - return; - } - info->port.flags |= ASYNC_CLOSING; - - /* - * Now we wait for the transmit buffer to clear; and we notify - * the line discipline to only process XON/XOFF characters. - */ - tty->closing = 1; - spin_unlock_irqrestore(&card->card_lock, flags); - if (info->port.closing_wait != CY_CLOSING_WAIT_NONE) - tty_wait_until_sent(tty, info->port.closing_wait); spin_lock_irqsave(&card->card_lock, flags); @@ -2460,28 +2412,10 @@ static void cy_close(struct tty_struct *tty, struct file *filp) spin_unlock_irqrestore(&card->card_lock, flags); cy_shutdown(info, tty); cy_flush_buffer(tty); - tty_ldisc_flush(tty); - spin_lock_irqsave(&card->card_lock, flags); - tty->closing = 0; tty_port_tty_set(&info->port, NULL); - if (info->port.blocked_open) { - spin_unlock_irqrestore(&card->card_lock, flags); - if (info->port.close_delay) { - msleep_interruptible(jiffies_to_msecs - (info->port.close_delay)); - } - wake_up_interruptible(&info->port.open_wait); - spin_lock_irqsave(&card->card_lock, flags); - } - info->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); - wake_up_interruptible(&info->port.close_wait); -#ifdef CY_DEBUG_OTHER - printk(KERN_DEBUG "cyc:cy_close done\n"); -#endif - - spin_unlock_irqrestore(&card->card_lock, flags); + tty_port_close_end(&info->port, tty); } /* cy_close */ /* This routine gets called when tty_write has put something into -- cgit v1.2.3 From ebdb513596c0eb1dcb3bad8f53865964a2207ca9 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:14 -0700 Subject: cyclades: overall cleanup - remove changelog from the file. we don't care about ancient history - update copyright year - update version - constify some stuff - empty lines removal - unused variables and macros removal - remove some asm/ includes, they are sucked by linux/ variants Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 598 +----------------------------------------------- 1 file changed, 12 insertions(+), 586 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 87a40bc75646..3884ea439935 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -11,7 +11,7 @@ * Initially written by Randolph Bentson . * Modified and maintained by Marcio Saito . * - * Copyright (C) 2007 Jiri Slaby + * Copyright (C) 2007-2009 Jiri Slaby * * Much of the design and some of the code came from serial.c * which was copyright (C) 1991, 1992 Linus Torvalds. It was @@ -19,577 +19,9 @@ * and then fixed as suggested by Michael K. Johnson 12/12/92. * Converted to pci probing and cleaned up by Jiri Slaby. * - * This version supports shared IRQ's (only for PCI boards). - * - * Prevent users from opening non-existing Z ports. - * - * Revision 2.3.2.8 2000/07/06 18:14:16 ivan - * Fixed the PCI detection function to work properly on Alpha systems. - * Implemented support for TIOCSERGETLSR ioctl. - * Implemented full support for non-standard baud rates. - * - * Revision 2.3.2.7 2000/06/01 18:26:34 ivan - * Request PLX I/O region, although driver doesn't use it, to avoid - * problems with other drivers accessing it. - * Removed count for on-board buffer characters in cy_chars_in_buffer - * (Cyclades-Z only). - * - * Revision 2.3.2.6 2000/05/05 13:56:05 ivan - * Driver now reports physical instead of virtual memory addresses. - * Masks were added to some Cyclades-Z read accesses. - * Implemented workaround for PLX9050 bug that would cause a system lockup - * in certain systems, depending on the MMIO addresses allocated to the - * board. - * Changed the Tx interrupt programming in the CD1400 chips to boost up - * performance (Cyclom-Y only). - * Code is now compliant with the new module interface (module_[init|exit]). - * Make use of the PCI helper functions to access PCI resources. - * Did some code "housekeeping". - * - * Revision 2.3.2.5 2000/01/19 14:35:33 ivan - * Fixed bug in cy_set_termios on CRTSCTS flag turnoff. - * - * Revision 2.3.2.4 2000/01/17 09:19:40 ivan - * Fixed SMP locking in Cyclom-Y interrupt handler. - * - * Revision 2.3.2.3 1999/12/28 12:11:39 ivan - * Added a new cyclades_card field called nports to allow the driver to - * know the exact number of ports found by the Z firmware after its load; - * RX buffer contention prevention logic on interrupt op mode revisited - * (Cyclades-Z only); - * Revisited printk's for Z debug; - * Driver now makes sure that the constant SERIAL_XMIT_SIZE is defined; - * - * Revision 2.3.2.2 1999/10/01 11:27:43 ivan - * Fixed bug in cyz_poll that would make all ports but port 0 - * unable to transmit/receive data (Cyclades-Z only); - * Implemented logic to prevent the RX buffer from being stuck with data - * due to a driver / firmware race condition in interrupt op mode - * (Cyclades-Z only); - * Fixed bug in block_til_ready logic that would lead to a system crash; - * Revisited cy_close spinlock usage; - * - * Revision 2.3.2.1 1999/09/28 11:01:22 ivan - * Revisited CONFIG_PCI conditional compilation for PCI board support; - * Implemented TIOCGICOUNT and TIOCMIWAIT ioctl support; - * _Major_ cleanup on the Cyclades-Z interrupt support code / logic; - * Removed CTS handling from the driver -- this is now completely handled - * by the firmware (Cyclades-Z only); - * Flush RX on-board buffers on a port open (Cyclades-Z only); - * Fixed handling of ASYNC_SPD_* TTY flags; - * Module unload now unmaps all memory area allocated by ioremap; - * - * Revision 2.3.1.1 1999/07/15 16:45:53 ivan - * Removed CY_PROC conditional compilation; - * Implemented SMP-awareness for the driver; - * Implemented a new ISA IRQ autoprobe that uses the irq_probe_[on|off] - * functions; - * The driver now accepts memory addresses (maddr=0xMMMMM) and IRQs - * (irq=NN) as parameters (only for ISA boards); - * Fixed bug in set_line_char that would prevent the Cyclades-Z - * ports from being configured at speeds above 115.2Kbps; - * Fixed bug in cy_set_termios that would prevent XON/XOFF flow control - * switching from working properly; - * The driver now only prints IRQ info for the Cyclades-Z if it's - * configured to work in interrupt mode; - * - * Revision 2.2.2.3 1999/06/28 11:13:29 ivan - * Added support for interrupt mode operation for the Z cards; - * Removed the driver inactivity control for the Z; - * Added a missing MOD_DEC_USE_COUNT in the cy_open function for when - * the Z firmware is not loaded yet; - * Replaced the "manual" Z Tx flush buffer by a call to a FW command of - * same functionality; - * Implemented workaround for IRQ setting loss on the PCI configuration - * registers after a PCI bridge EEPROM reload (affects PLX9060 only); - * - * Revision 2.2.2.2 1999/05/14 17:18:15 ivan - * /proc entry location changed to /proc/tty/driver/cyclades; - * Added support to shared IRQ's (only for PCI boards); - * Added support for Cobalt Qube2 systems; - * IRQ [de]allocation scheme revisited; - * BREAK implementation changed in order to make use of the 'break_ctl' - * TTY facility; - * Fixed typo in TTY structure field 'driver_name'; - * Included a PCI bridge reset and EEPROM reload in the board - * initialization code (for both Y and Z series). - * - * Revision 2.2.2.1 1999/04/08 16:17:43 ivan - * Fixed a bug in cy_wait_until_sent that was preventing the port to be - * closed properly after a SIGINT; - * Module usage counter scheme revisited; - * Added support to the upcoming Y PCI boards (i.e., support to additional - * PCI Device ID's). - * - * Revision 2.2.1.10 1999/01/20 16:14:29 ivan - * Removed all unnecessary page-alignement operations in ioremap calls - * (ioremap is currently safe for these operations). - * - * Revision 2.2.1.9 1998/12/30 18:18:30 ivan - * Changed access to PLX PCI bridge registers from I/O to MMIO, in - * order to make PLX9050-based boards work with certain motherboards. - * - * Revision 2.2.1.8 1998/11/13 12:46:20 ivan - * cy_close function now resets (correctly) the tty->closing flag; - * JIFFIES_DIFF macro fixed. - * - * Revision 2.2.1.7 1998/09/03 12:07:28 ivan - * Fixed bug in cy_close function, which was not informing HW of - * which port should have the reception disabled before doing so; - * fixed Cyclom-8YoP hardware detection bug. - * - * Revision 2.2.1.6 1998/08/20 17:15:39 ivan - * Fixed bug in cy_close function, which causes malfunction - * of one of the first 4 ports when a higher port is closed - * (Cyclom-Y only). - * - * Revision 2.2.1.5 1998/08/10 18:10:28 ivan - * Fixed Cyclom-4Yo hardware detection bug. - * - * Revision 2.2.1.4 1998/08/04 11:02:50 ivan - * /proc/cyclades implementation with great collaboration of - * Marc Lewis ; - * cyy_interrupt was changed to avoid occurrence of kernel oopses - * during PPP operation. - * - * Revision 2.2.1.3 1998/06/01 12:09:10 ivan - * General code review in order to comply with 2.1 kernel standards; - * data loss prevention for slow devices revisited (cy_wait_until_sent - * was created); - * removed conditional compilation for new/old PCI structure support - * (now the driver only supports the new PCI structure). - * - * Revision 2.2.1.1 1998/03/19 16:43:12 ivan - * added conditional compilation for new/old PCI structure support; - * removed kernel series (2.0.x / 2.1.x) conditional compilation. - * - * Revision 2.1.1.3 1998/03/16 18:01:12 ivan - * cleaned up the data loss fix; - * fixed XON/XOFF handling once more (Cyclades-Z); - * general review of the driver routines; - * introduction of a mechanism to prevent data loss with slow - * printers, by forcing a delay before closing the port. - * - * Revision 2.1.1.2 1998/02/17 16:50:00 ivan - * fixed detection/handling of new CD1400 in Ye boards; - * fixed XON/XOFF handling (Cyclades-Z); - * fixed data loss caused by a premature port close; - * introduction of a flag that holds the CD1400 version ID per port - * (used by the CYGETCD1400VER new ioctl). - * - * Revision 2.1.1.1 1997/12/03 17:31:19 ivan - * Code review for the module cleanup routine; - * fixed RTS and DTR status report for new CD1400's in get_modem_info; - * includes anonymous changes regarding signal_pending. - * - * Revision 2.1 1997/11/01 17:42:41 ivan - * Changes in the driver to support Alpha systems (except 8Zo V_1); - * BREAK fix for the Cyclades-Z boards; - * driver inactivity control by FW implemented; - * introduction of flag that allows driver to take advantage of - * a special CD1400 feature related to HW flow control; - * added support for the CD1400 rev. J (Cyclom-Y boards); - * introduction of ioctls to: - * - control the rtsdtr_inv flag (Cyclom-Y); - * - control the rflow flag (Cyclom-Y); - * - adjust the polling interval (Cyclades-Z); - * - * Revision 1.36.4.33 1997/06/27 19:00:00 ivan - * Fixes related to kernel version conditional - * compilation. - * - * Revision 1.36.4.32 1997/06/14 19:30:00 ivan - * Compatibility issues between kernels 2.0.x and - * 2.1.x (mainly related to clear_bit function). - * - * Revision 1.36.4.31 1997/06/03 15:30:00 ivan - * Changes to define the memory window according to the - * board type. - * - * Revision 1.36.4.30 1997/05/16 15:30:00 daniel - * Changes to support new cycladesZ boards. - * - * Revision 1.36.4.29 1997/05/12 11:30:00 daniel - * Merge of Bentson's and Daniel's version 1.36.4.28. - * Corrects bug in cy_detect_pci: check if there are more - * ports than the number of static structs allocated. - * Warning message during initialization if this driver is - * used with the new generation of cycladesZ boards. Those - * will be supported only in next release of the driver. - * Corrects bug in cy_detect_pci and cy_detect_isa that - * returned wrong number of VALID boards, when a cyclomY - * was found with no serial modules connected. - * Changes to use current (2.1.x) kernel subroutine names - * and created macros for compilation with 2.0.x kernel, - * instead of the other way around. - * - * Revision 1.36.4.28 1997/05/?? ??:00:00 bentson - * Change queue_task_irq_off to queue_task_irq. - * The inline function queue_task_irq_off (tqueue.h) - * was removed from latest releases of 2.1.x kernel. - * Use of macro __init to mark the initialization - * routines, so memory can be reused. - * Also incorporate implementation of critical region - * in function cleanup_module() created by anonymous - * linuxer. - * - * Revision 1.36.4.28 1997/04/25 16:00:00 daniel - * Change to support new firmware that solves DCD problem: - * application could fail to receive SIGHUP signal when DCD - * varying too fast. - * - * Revision 1.36.4.27 1997/03/26 10:30:00 daniel - * Changed for support linux versions 2.1.X. - * Backward compatible with linux versions 2.0.X. - * Corrected illegal use of filler field in - * CH_CTRL struct. - * Deleted some debug messages. - * - * Revision 1.36.4.26 1997/02/27 12:00:00 daniel - * Included check for NULL tty pointer in cyz_poll. - * - * Revision 1.36.4.25 1997/02/26 16:28:30 bentson - * Bill Foster at Blarg! Online services noticed that - * some of the switch elements of -Z modem control - * lacked a closing "break;" - * - * Revision 1.36.4.24 1997/02/24 11:00:00 daniel - * Changed low water threshold for buffer xmit_buf - * - * Revision 1.36.4.23 1996/12/02 21:50:16 bentson - * Marcio provided fix to modem status fetch for -Z - * - * Revision 1.36.4.22 1996/10/28 22:41:17 bentson - * improve mapping of -Z control page (thanks to Steve - * Price for help on this) - * - * Revision 1.36.4.21 1996/09/10 17:00:10 bentson - * shift from CPU-bound to memcopy in cyz_polling operation - * - * Revision 1.36.4.20 1996/09/09 18:30:32 Bentson - * Added support to set and report higher speeds. - * - * Revision 1.36.4.19c 1996/08/09 10:00:00 Marcio Saito - * Some fixes in the HW flow control for the BETA release. - * Don't try to register the IRQ. - * - * Revision 1.36.4.19 1996/08/08 16:23:18 Bentson - * make sure "cyc" appears in all kernel messages; all soft interrupts - * handled by same routine; recognize out-of-band reception; comment - * out some diagnostic messages; leave RTS/CTS flow control to hardware; - * fix race condition in -Z buffer management; only -Y needs to explicitly - * flush chars; tidy up some startup messages; - * - * Revision 1.36.4.18 1996/07/25 18:57:31 bentson - * shift MOD_INC_USE_COUNT location to match - * serial.c; purge some diagnostic messages; - * - * Revision 1.36.4.17 1996/07/25 18:01:08 bentson - * enable modem status messages and fetch & process them; note - * time of last activity type for each port; set_line_char now - * supports more than line 0 and treats 0 baud correctly; - * get_modem_info senses rs_status; - * - * Revision 1.36.4.16 1996/07/20 08:43:15 bentson - * barely works--now's time to turn on - * more features 'til it breaks - * - * Revision 1.36.4.15 1996/07/19 22:30:06 bentson - * check more -Z board status; shorten boot message - * - * Revision 1.36.4.14 1996/07/19 22:20:37 bentson - * fix reference to ch_ctrl in startup; verify return - * values from cyz_issue_cmd and cyz_update_channel; - * more stuff to get modem control correct; - * - * Revision 1.36.4.13 1996/07/11 19:53:33 bentson - * more -Z stuff folded in; re-order changes to put -Z stuff - * after -Y stuff (to make changes clearer) - * - * Revision 1.36.4.12 1996/07/11 15:40:55 bentson - * Add code to poll Cyclades-Z. Add code to get & set RS-232 control. - * Add code to send break. Clear firmware ID word at startup (so - * that other code won't talk to inactive board). - * - * Revision 1.36.4.11 1996/07/09 05:28:29 bentson - * add code for -Z in set_line_char - * - * Revision 1.36.4.10 1996/07/08 19:28:37 bentson - * fold more -Z stuff (or in some cases, error messages) - * into driver; add text to "don't know what to do" messages. - * - * Revision 1.36.4.9 1996/07/08 18:38:38 bentson - * moved compile-time flags near top of file; cosmetic changes - * to narrow text (to allow 2-up printing); changed many declarations - * to "static" to limit external symbols; shuffled code order to - * coalesce -Y and -Z specific code, also to put internal functions - * in order of tty_driver structure; added code to recognize -Z - * ports (and for moment, do nothing or report error); add cy_startup - * to parse boot command line for extra base addresses for ISA probes; - * - * Revision 1.36.4.8 1996/06/25 17:40:19 bentson - * reorder some code, fix types of some vars (int vs. long), - * add cy_setup to support user declared ISA addresses - * - * Revision 1.36.4.7 1996/06/21 23:06:18 bentson - * dump ioctl based firmware load (it's now a user level - * program); ensure uninitialzed ports cannot be used - * - * Revision 1.36.4.6 1996/06/20 23:17:19 bentson - * rename vars and restructure some code - * - * Revision 1.36.4.5 1996/06/14 15:09:44 bentson - * get right status back after boot load - * - * Revision 1.36.4.4 1996/06/13 19:51:44 bentson - * successfully loads firmware - * - * Revision 1.36.4.3 1996/06/13 06:08:33 bentson - * add more of the code for the boot/load ioctls - * - * Revision 1.36.4.2 1996/06/11 21:00:51 bentson - * start to add Z functionality--starting with ioctl - * for loading firmware - * - * Revision 1.36.4.1 1996/06/10 18:03:02 bentson - * added code to recognize Z/PCI card at initialization; report - * presence, but card is not initialized (because firmware needs - * to be loaded) - * - * Revision 1.36.3.8 1996/06/07 16:29:00 bentson - * starting minor number at zero; added missing verify_area - * as noted by Heiko Eißfeldt - * - * Revision 1.36.3.7 1996/04/19 21:06:18 bentson - * remove unneeded boot message & fix CLOCAL hardware flow - * control (Miquel van Smoorenburg ); - * remove unused diagnostic statements; minor 0 is first; - * - * Revision 1.36.3.6 1996/03/13 13:21:17 marcio - * The kernel function vremap (available only in later 1.3.xx kernels) - * allows the access to memory addresses above the RAM. This revision - * of the driver supports PCI boards below 1Mb (device id 0x100) and - * above 1Mb (device id 0x101). - * - * Revision 1.36.3.5 1996/03/07 15:20:17 bentson - * Some global changes to interrupt handling spilled into - * this driver--mostly unused arguments in system function - * calls. Also added change by Marcio Saito which should - * reduce lost interrupts at startup by fast processors. - * - * Revision 1.36.3.4 1995/11/13 20:45:10 bentson - * Changes by Corey Minyard distributed - * in 1.3.41 kernel to remove a possible race condition, extend - * some error messages, and let the driver run as a loadable module - * Change by Alan Wendt to remove a - * possible race condition. - * Change by Marcio Saito to fix PCI addressing. - * - * Revision 1.36.3.3 1995/11/13 19:44:48 bentson - * Changes by Linus Torvalds in 1.3.33 kernel distribution - * required due to reordering of driver initialization. - * Drivers are now initialized *after* memory management. - * - * Revision 1.36.3.2 1995/09/08 22:07:14 bentson - * remove printk from ISR; fix typo - * - * Revision 1.36.3.1 1995/09/01 12:00:42 marcio - * Minor fixes in the PCI board support. PCI function calls in - * conditional compilation (CONFIG_PCI). Thanks to Jim Duncan - * . "bad serial count" message removed. - * - * Revision 1.36.3 1995/08/22 09:19:42 marcio - * Cyclom-Y/PCI support added. Changes in the cy_init routine and - * board initialization. Changes in the boot messages. The driver - * supports up to 4 boards and 64 ports by default. - * - * Revision 1.36.1.4 1995/03/29 06:14:14 bentson - * disambiguate between Cyclom-16Y and Cyclom-32Ye; - * - * Revision 1.36.1.3 1995/03/23 22:15:35 bentson - * add missing break in modem control block in ioctl switch statement - * (discovered by Michael Edward Chastain ); - * - * Revision 1.36.1.2 1995/03/22 19:16:22 bentson - * make sure CTS flow control is set as soon as possible (thanks - * to note from David Lambert ); - * - * Revision 1.36.1.1 1995/03/13 15:44:43 bentson - * initialize defaults for receive threshold and stale data timeout; - * cosmetic changes; - * - * Revision 1.36 1995/03/10 23:33:53 bentson - * added support of chips 4-7 in 32 port Cyclom-Ye; - * fix cy_interrupt pointer dereference problem - * (Joe Portman ); - * give better error response if open is attempted on non-existent port - * (Zachariah Vaum ); - * correct command timeout (Kenneth Lerman ); - * conditional compilation for -16Y on systems with fast, noisy bus; - * comment out diagnostic print function; - * cleaned up table of base addresses; - * set receiver time-out period register to correct value, - * set receive threshold to better default values, - * set chip timer to more accurate 200 Hz ticking, - * add code to monitor and modify receive parameters - * (Rik Faith Nick Simicich - * ); - * - * Revision 1.35 1994/12/16 13:54:18 steffen - * additional patch by Marcio Saito for board detection - * Accidently left out in 1.34 - * - * Revision 1.34 1994/12/10 12:37:12 steffen - * This is the corrected version as suggested by Marcio Saito - * - * Revision 1.33 1994/12/01 22:41:18 bentson - * add hooks to support more high speeds directly; add tytso - * patch regarding CLOCAL wakeups - * - * Revision 1.32 1994/11/23 19:50:04 bentson - * allow direct kernel control of higher signalling rates; - * look for cards at additional locations - * - * Revision 1.31 1994/11/16 04:33:28 bentson - * ANOTHER fix from Corey Minyard, minyard@wf-rch.cirr.com-- - * a problem in chars_in_buffer has been resolved by some - * small changes; this should yield smoother output - * - * Revision 1.30 1994/11/16 04:28:05 bentson - * Fix from Corey Minyard, Internet: minyard@metronet.com, - * UUCP: minyard@wf-rch.cirr.com, WORK: minyardbnr.ca, to - * cy_hangup that appears to clear up much (all?) of the - * DTR glitches; also he's added/cleaned-up diagnostic messages - * - * Revision 1.29 1994/11/16 04:16:07 bentson - * add change proposed by Ralph Sims, ralphs@halcyon.com, to - * operate higher speeds in same way as other serial ports; - * add more serial ports (for up to two 16-port muxes). - * - * Revision 1.28 1994/11/04 00:13:16 root - * turn off diagnostic messages - * - * Revision 1.27 1994/11/03 23:46:37 root - * bunch of changes to bring driver into greater conformance - * with the serial.c driver (looking for missed fixes) - * - * Revision 1.26 1994/11/03 22:40:36 root - * automatic interrupt probing fixed. - * - * Revision 1.25 1994/11/03 20:17:02 root - * start to implement auto-irq - * - * Revision 1.24 1994/11/03 18:01:55 root - * still working on modem signals--trying not to drop DTR - * during the getty/login processes - * - * Revision 1.23 1994/11/03 17:51:36 root - * extend baud rate support; set receive threshold as function - * of baud rate; fix some problems with RTS/CTS; - * - * Revision 1.22 1994/11/02 18:05:35 root - * changed arguments to udelay to type long to get - * delays to be of correct duration - * - * Revision 1.21 1994/11/02 17:37:30 root - * employ udelay (after calibrating loops_per_second earlier - * in init/main.c) instead of using home-grown delay routines - * - * Revision 1.20 1994/11/02 03:11:38 root - * cy_chars_in_buffer forces a return value of 0 to let - * login work (don't know why it does); some functions - * that were returning EFAULT, now executes the code; - * more work on deciding when to disable xmit interrupts; - * - * Revision 1.19 1994/11/01 20:10:14 root - * define routine to start transmission interrupts (by enabling - * transmit interrupts); directly enable/disable modem interrupts; - * - * Revision 1.18 1994/11/01 18:40:45 bentson - * Don't always enable transmit interrupts in startup; interrupt on - * TxMpty instead of TxRdy to help characters get out before shutdown; - * restructure xmit interrupt to check for chars first and quit if - * none are ready to go; modem status (MXVRx) is upright, _not_ inverted - * (to my view); - * - * Revision 1.17 1994/10/30 04:39:45 bentson - * rename serial_driver and callout_driver to cy_serial_driver and - * cy_callout_driver to avoid linkage interference; initialize - * info->type to PORT_CIRRUS; ruggedize paranoia test; elide ->port - * from cyclades_port structure; add paranoia check to cy_close; - * - * Revision 1.16 1994/10/30 01:14:33 bentson - * change major numbers; add some _early_ return statements; - * - * Revision 1.15 1994/10/29 06:43:15 bentson - * final tidying up for clean compile; enable some error reporting - * - * Revision 1.14 1994/10/28 20:30:22 Bentson - * lots of changes to drag the driver towards the new tty_io - * structures and operation. not expected to work, but may - * compile cleanly. - * - * Revision 1.13 1994/07/21 23:08:57 Bentson - * add some diagnostic cruft; support 24 lines (for testing - * both -8Y and -16Y cards; be more thorough in servicing all - * chips during interrupt; add "volatile" a few places to - * circumvent compiler optimizations; fix base & offset - * computations in block_til_ready (was causing chip 0 to - * stop operation) - * - * Revision 1.12 1994/07/19 16:42:11 Bentson - * add some hackery for kernel version 1.1.8; expand - * error messages; refine timing for delay loops and - * declare loop params volatile - * - * Revision 1.11 1994/06/11 21:53:10 bentson - * get use of save_car right in transmit interrupt service - * - * Revision 1.10.1.1 1994/06/11 21:31:18 bentson - * add some diagnostic printing; try to fix save_car stuff - * - * Revision 1.10 1994/06/11 20:36:08 bentson - * clean up compiler warnings - * - * Revision 1.9 1994/06/11 19:42:46 bentson - * added a bunch of code to support modem signalling - * - * Revision 1.8 1994/06/11 17:57:07 bentson - * recognize break & parity error - * - * Revision 1.7 1994/06/05 05:51:34 bentson - * Reorder baud table to be monotonic; add cli to CP; discard - * incoming characters and status if the line isn't open; start to - * fold code into cy_throttle; start to port get_serial_info, - * set_serial_info, get_modem_info, set_modem_info, and send_break - * from serial.c; expand cy_ioctl; relocate and expand config_setup; - * get flow control characters from tty struct; invalidate ports w/o - * hardware; - * - * Revision 1.6 1994/05/31 18:42:21 bentson - * add a loop-breaker in the interrupt service routine; - * note when port is initialized so that it can be shut - * down under the right conditions; receive works without - * any obvious errors - * - * Revision 1.5 1994/05/30 00:55:02 bentson - * transmit works without obvious errors - * - * Revision 1.4 1994/05/27 18:46:27 bentson - * incorporated more code from lib_y.c; can now print short - * strings under interrupt control to port zero; seems to - * select ports/channels/lines correctly - * - * Revision 1.3 1994/05/25 22:12:44 bentson - * shifting from multi-port on a card to proper multiplexor - * data structures; added skeletons of most routines - * - * Revision 1.2 1994/05/19 13:21:43 bentson - * start to crib from other sources - * */ -#define CY_VERSION "2.5" +#define CY_VERSION "2.6" /* If you need to install more boards than NR_CARDS, change the constant in the definition below. No other change is necessary to support up to @@ -648,9 +80,7 @@ #include #include -#include #include -#include #include #include @@ -666,7 +96,6 @@ static void cy_send_xchar(struct tty_struct *tty, char ch); #ifndef SERIAL_XMIT_SIZE #define SERIAL_XMIT_SIZE (min(PAGE_SIZE, 4096)) #endif -#define WAKEUP_CHARS 256 #define STD_COM_FLAGS (0) @@ -756,25 +185,25 @@ static int cy_next_channel; /* next minor available */ * HI VHI * 20 */ -static int baud_table[] = { +static const int baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 76800, 115200, 150000, 230400, 0 }; -static char baud_co_25[] = { /* 25 MHz clock option table */ +static const char baud_co_25[] = { /* 25 MHz clock option table */ /* value => 00 01 02 03 04 */ /* divide by 8 32 128 512 2048 */ 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static char baud_bpr_25[] = { /* 25 MHz baud rate period table */ +static const char baud_bpr_25[] = { /* 25 MHz baud rate period table */ 0x00, 0xf5, 0xa3, 0x6f, 0x5c, 0x51, 0xf5, 0xa3, 0x51, 0xa3, 0x6d, 0x51, 0xa3, 0x51, 0xa3, 0x51, 0x36, 0x29, 0x1b, 0x15 }; -static char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ +static const char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ /* value => 00 01 02 03 04 */ /* divide by 8 32 128 512 2048 */ 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, @@ -782,13 +211,13 @@ static char baud_co_60[] = { /* 60 MHz clock option table (CD1400 J) */ 0x00 }; -static char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ +static const char baud_bpr_60[] = { /* 60 MHz baud rate period table (CD1400 J) */ 0x00, 0x82, 0x21, 0xff, 0xdb, 0xc3, 0x92, 0x62, 0xc3, 0x62, 0x41, 0xc3, 0x62, 0xc3, 0x62, 0xc3, 0x82, 0x62, 0x41, 0x32, 0x21 }; -static char baud_cor3[] = { /* receive threshold */ +static const char baud_cor3[] = { /* receive threshold */ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07 @@ -805,7 +234,7 @@ static char baud_cor3[] = { /* receive threshold */ * cables. */ -static char rflow_thr[] = { /* rflow threshold */ +static const char rflow_thr[] = { /* rflow threshold */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a @@ -827,7 +256,7 @@ static const unsigned int cy_chip_offset[] = { 0x0000, /* PCI related definitions */ #ifdef CONFIG_PCI -static struct pci_device_id cy_pci_dev_id[] __devinitdata = { +static const struct pci_device_id cy_pci_dev_id[] = { /* PCI < 1Mb */ { PCI_DEVICE(PCI_VENDOR_ID_CYCLADES, PCI_DEVICE_ID_CYCLOM_Y_Lo) }, /* PCI > 1Mb */ @@ -893,7 +322,7 @@ static inline bool cyz_is_loaded(struct cyclades_card *card) } static inline int serial_paranoia_check(struct cyclades_port *info, - char *name, const char *routine) + const char *name, const char *routine) { #ifdef SERIAL_PARANOIA_CHECK if (!info) { @@ -909,7 +338,7 @@ static inline int serial_paranoia_check(struct cyclades_port *info, } #endif return 0; -} /* serial_paranoia_check */ +} /***********************************************************/ /********* Start of block of Cyclom-Y specific code ********/ @@ -3030,11 +2459,9 @@ cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty, struct serial_struct __user *new_info) { struct serial_struct new_serial; - struct cyclades_port old_info; if (copy_from_user(&new_serial, new_info, sizeof(new_serial))) return -EFAULT; - old_info = *info; if (!capable(CAP_SYS_ADMIN)) { if (new_serial.close_delay != info->port.close_delay || @@ -3376,7 +2803,6 @@ static int cy_break(struct tty_struct *tty, int break_state) static int get_mon_info(struct cyclades_port *info, struct cyclades_monitor __user *mon) { - if (copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor))) return -EFAULT; info->mon.int_count = 0; -- cgit v1.2.3 From f6e208c1119206e2382ef7df6e47aaee18eb7f10 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:14 -0700 Subject: cyclades: sleep instead busy-wait Avoid long busy loops (5 ms) which may be replaced by sleeps. Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 3884ea439935..b6d40ad662ff 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -389,7 +389,7 @@ static unsigned detect_isa_irq(void __iomem *address) irqs = probe_irq_on(); /* Wait ... */ - udelay(5000L); + msleep(5); /* Enable the Tx interrupts on the CD1400 */ local_irq_save(flags); @@ -402,7 +402,7 @@ static unsigned detect_isa_irq(void __iomem *address) local_irq_restore(flags); /* Wait ... */ - udelay(5000L); + msleep(5); /* Check which interrupt is in use */ irq = probe_irq_off(irqs); -- cgit v1.2.3 From 4d7682005ca88a37667c4af03908798e188b5224 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:15 -0700 Subject: cyclades: use dtr_rts helpers For Z cards, use tty helpers for dtr_rts. If we did the same for Y cards, it will cause a deadlock, because cyy_dtr_rts takes a lock which we already hold. Instead, we introduce a Y helper expecting card lock to be held. It may then be called with set/clear masks from other places. Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 280 ++++++++++++++---------------------------------- 1 file changed, 79 insertions(+), 201 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index b6d40ad662ff..74627950f901 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -825,6 +825,66 @@ static irqreturn_t cyy_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } /* cyy_interrupt */ +static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, + unsigned int clear) +{ + struct cyclades_card *card = info->card; + void __iomem *base_addr; + int chip, channel, index; + + channel = info->line - card->first_line; + chip = channel >> 2; + channel &= 0x03; + index = card->bus_index; + base_addr = card->base_addr + (cy_chip_offset[chip] << index); + + if (set & TIOCM_RTS) { + cy_writeb(base_addr + (CyCAR << index), (u_char) channel); + if (info->rtsdtr_inv) { + cy_writeb(base_addr + (CyMSVR2 << index), CyDTR); + } else { + cy_writeb(base_addr + (CyMSVR1 << index), CyRTS); + } + } + if (clear & TIOCM_RTS) { + cy_writeb(base_addr + (CyCAR << index), (u_char) channel); + if (info->rtsdtr_inv) { + cy_writeb(base_addr + (CyMSVR2 << index), ~CyDTR); + } else { + cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); + } + } + if (set & TIOCM_DTR) { + cy_writeb(base_addr + (CyCAR << index), (u_char) channel); + if (info->rtsdtr_inv) { + cy_writeb(base_addr + (CyMSVR1 << index), CyRTS); + } else { + cy_writeb(base_addr + (CyMSVR2 << index), CyDTR); + } +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n"); + printk(KERN_DEBUG " status: 0x%x, 0x%x\n", + readb(base_addr + (CyMSVR1 << index)), + readb(base_addr + (CyMSVR2 << index))); +#endif + } + if (clear & TIOCM_DTR) { + cy_writeb(base_addr + (CyCAR << index), (u_char) channel); + if (info->rtsdtr_inv) { + cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); + } else { + cy_writeb(base_addr + (CyMSVR2 << index), ~CyDTR); + } + +#ifdef CY_DEBUG_DTR + printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n"); + printk(KERN_DEBUG " status: 0x%x, 0x%x\n", + readb(base_addr + (CyMSVR1 << index)), + readb(base_addr + (CyMSVR2 << index))); +#endif + } +} + /***********************************************************/ /********* End of block of Cyclom-Y specific code **********/ /******** Start of block of Cyclades-Z specific code *******/ @@ -1290,16 +1350,7 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) cyy_issue_cmd(base_addr, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR, index); - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - cy_writeb(base_addr + (CyMSVR1 << index), CyRTS); - cy_writeb(base_addr + (CyMSVR2 << index), CyDTR); - -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:startup raising DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif + cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0); cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyRxData); @@ -1362,16 +1413,7 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) /* set timeout !!! */ /* set RTS and DTR !!! */ - cy_writel(&ch_ctrl->rs_control, readl(&ch_ctrl->rs_control) | - C_RS_RTS | C_RS_DTR); - retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); - if (retval != 0) { - printk(KERN_ERR "cyc:startup(3) retval on ttyC%d was " - "%x\n", info->line, retval); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:startup raising Z DTR\n"); -#endif + tty_port_raise_dtr_rts(&info->port); /* enable send, recv, modem !!! */ @@ -1473,17 +1515,9 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) info->port.xmit_buf = NULL; free_page((unsigned long)temp); } - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (tty->termios->c_cflag & HUPCL) { - cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); - cy_writeb(base_addr + (CyMSVR2 << index), ~CyDTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc shutdown dropping DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - } + if (tty->termios->c_cflag & HUPCL) + cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR); + cyy_issue_cmd(base_addr, CyCHAN_CTL | CyDIS_RCVR, index); /* it may be appropriate to clear _XMIT at some later date (after testing)!!! */ @@ -1492,9 +1526,6 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) info->port.flags &= ~ASYNC_INITIALIZED; spin_unlock_irqrestore(&card->card_lock, flags); } else { - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - int retval; - #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc shutdown Z card %d, channel %d, " "base_addr %p\n", card, channel, card->base_addr); @@ -1512,20 +1543,8 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) free_page((unsigned long)temp); } - if (tty->termios->c_cflag & HUPCL) { - cy_writel(&ch_ctrl->rs_control, - readl(&ch_ctrl->rs_control) & - ~(C_RS_RTS | C_RS_DTR)); - retval = cyz_issue_cmd(info->card, channel, - C_CM_IOCTLM, 0L); - if (retval != 0) { - printk(KERN_ERR"cyc:shutdown retval on ttyC%d " - "was %x\n", info->line, retval); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:shutdown dropping Z DTR\n"); -#endif - } + if (tty->termios->c_cflag & HUPCL) + tty_port_lower_dtr_rts(&info->port); set_bit(TTY_IO_ERROR, &tty->flags); info->port.flags &= ~ASYNC_INITIALIZED; @@ -2273,35 +2292,10 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) CyDSR | CyCTS | CyRI | CyDCD); } - if (i == 0) { /* baud rate is zero, turn off line */ - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR1 << index), - ~CyRTS); - } else { - cy_writeb(base_addr + (CyMSVR2 << index), - ~CyDTR); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_line_char dropping DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - } else { - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR1 << index), - CyRTS); - } else { - cy_writeb(base_addr + (CyMSVR2 << index), - CyDTR); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_line_char raising DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - } + if (i == 0) /* baud rate is zero, turn off line */ + cyy_change_rts_dtr(info, 0, TIOCM_DTR); + else + cyy_change_rts_dtr(info, TIOCM_DTR, 0); clear_bit(TTY_IO_ERROR, &tty->flags); spin_unlock_irqrestore(&card->card_lock, flags); @@ -2604,9 +2598,8 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, { struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; - int chip, channel, index; unsigned long flags; - int retval; + int channel, retval; if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; @@ -2614,77 +2607,9 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, card = info->card; channel = (info->line) - (card->first_line); if (!cy_is_Z(card)) { - void __iomem *base_addr; - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - - if (set & TIOCM_RTS) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR2 << index), - CyDTR); - } else { - cy_writeb(base_addr + (CyMSVR1 << index), - CyRTS); - } - spin_unlock_irqrestore(&card->card_lock, flags); - } - if (clear & TIOCM_RTS) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR2 << index), - ~CyDTR); - } else { - cy_writeb(base_addr + (CyMSVR1 << index), - ~CyRTS); - } - spin_unlock_irqrestore(&card->card_lock, flags); - } - if (set & TIOCM_DTR) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR1 << index), - CyRTS); - } else { - cy_writeb(base_addr + (CyMSVR2 << index), - CyDTR); - } -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - spin_unlock_irqrestore(&card->card_lock, flags); - } - if (clear & TIOCM_DTR) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR1 << index), - ~CyRTS); - } else { - cy_writeb(base_addr + (CyMSVR2 << index), - ~CyDTR); - } - -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n"); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); -#endif - spin_unlock_irqrestore(&card->card_lock, flags); - } + spin_lock_irqsave(&card->card_lock, flags); + cyy_change_rts_dtr(info, set, clear); + spin_unlock_irqrestore(&card->card_lock, flags); } else { if (cyz_is_loaded(card)) { struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; @@ -3177,8 +3102,6 @@ static void cy_throttle(struct tty_struct *tty) struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; unsigned long flags; - void __iomem *base_addr; - int chip, channel, index; #ifdef CY_DEBUG_THROTTLE char buf[64]; @@ -3200,24 +3123,9 @@ static void cy_throttle(struct tty_struct *tty) } if (tty->termios->c_cflag & CRTSCTS) { - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + - (cy_chip_offset[chip] << index); - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR2 << index), - ~CyDTR); - } else { - cy_writeb(base_addr + (CyMSVR1 << index), - ~CyRTS); - } + cyy_change_rts_dtr(info, 0, TIOCM_RTS); spin_unlock_irqrestore(&card->card_lock, flags); } else { info->throttle = 1; @@ -3235,8 +3143,6 @@ static void cy_unthrottle(struct tty_struct *tty) struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; unsigned long flags; - void __iomem *base_addr; - int chip, channel, index; #ifdef CY_DEBUG_THROTTLE char buf[64]; @@ -3257,24 +3163,9 @@ static void cy_unthrottle(struct tty_struct *tty) if (tty->termios->c_cflag & CRTSCTS) { card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + - (cy_chip_offset[chip] << index); - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR2 << index), - CyDTR); - } else { - cy_writeb(base_addr + (CyMSVR1 << index), - CyRTS); - } + cyy_change_rts_dtr(info, TIOCM_RTS, 0); spin_unlock_irqrestore(&card->card_lock, flags); } else { info->throttle = 0; @@ -3395,24 +3286,11 @@ static void cyy_dtr_rts(struct tty_port *port, int raise) struct cyclades_port *info = container_of(port, struct cyclades_port, port); struct cyclades_card *cinfo = info->card; - void __iomem *base = cinfo->base_addr; unsigned long flags; - int channel = info->line - cinfo->first_line; - int chip = channel >> 2, index = cinfo->bus_index; - - channel &= 0x03; - base += cy_chip_offset[chip] << index; spin_lock_irqsave(&cinfo->card_lock, flags); - cy_writeb(base + (CyCAR << index), (u8)channel); - cy_writeb(base + (CyMSVR1 << index), raise ? CyRTS : ~CyRTS); - cy_writeb(base + (CyMSVR2 << index), raise ? CyDTR : ~CyDTR); -#ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "%s: raising DTR\n", __func__); - printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base + (CyMSVR1 << index)), - readb(base + (CyMSVR2 << index))); -#endif + cyy_change_rts_dtr(info, raise ? TIOCM_RTS | TIOCM_DTR : 0, + raise ? 0 : TIOCM_RTS | TIOCM_DTR); spin_unlock_irqrestore(&cinfo->card_lock, flags); } -- cgit v1.2.3 From cc7fdf49d6f06efdf0cb7da8d7abe7eff663aa9b Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:15 -0700 Subject: cyclades: merge cy_startup tails There is a duplicated code for Y and Z in cy_startup, merge the paths. Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 45 ++++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 29 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 74627950f901..47cd7b8d4d97 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1296,7 +1296,7 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) unsigned long flags; int retval = 0; void __iomem *base_addr; - int chip, channel, index; + int channel; unsigned long page; card = info->card; @@ -1308,14 +1308,11 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) spin_lock_irqsave(&card->card_lock, flags); - if (info->port.flags & ASYNC_INITIALIZED) { - free_page(page); + if (info->port.flags & ASYNC_INITIALIZED) goto errout; - } if (!info->type) { set_bit(TTY_IO_ERROR, &tty->flags); - free_page(page); goto errout; } @@ -1329,9 +1326,9 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) cy_set_line_char(info, tty); if (!cy_is_Z(card)) { - chip = channel >> 2; + int chip = channel >> 2; + int index = card->bus_index; channel &= 0x03; - index = card->bus_index; base_addr = card->base_addr + (cy_chip_offset[chip] << index); #ifdef CY_DEBUG_OPEN @@ -1354,18 +1351,6 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) cy_writeb(base_addr + (CySRER << index), readb(base_addr + (CySRER << index)) | CyRxData); - info->port.flags |= ASYNC_INITIALIZED; - - clear_bit(TTY_IO_ERROR, &tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - info->breakon = info->breakoff = 0; - memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); - info->idle_stats.in_use = - info->idle_stats.recv_idle = - info->idle_stats.xmit_idle = jiffies; - - spin_unlock_irqrestore(&card->card_lock, flags); - } else { struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; @@ -1416,18 +1401,19 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) tty_port_raise_dtr_rts(&info->port); /* enable send, recv, modem !!! */ + } - info->port.flags |= ASYNC_INITIALIZED; - clear_bit(TTY_IO_ERROR, &tty->flags); - info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; - info->breakon = info->breakoff = 0; - memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); - info->idle_stats.in_use = - info->idle_stats.recv_idle = - info->idle_stats.xmit_idle = jiffies; + info->port.flags |= ASYNC_INITIALIZED; - spin_unlock_irqrestore(&card->card_lock, flags); - } + clear_bit(TTY_IO_ERROR, &tty->flags); + info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; + info->breakon = info->breakoff = 0; + memset((char *)&info->idle_stats, 0, sizeof(info->idle_stats)); + info->idle_stats.in_use = + info->idle_stats.recv_idle = + info->idle_stats.xmit_idle = jiffies; + + spin_unlock_irqrestore(&card->card_lock, flags); #ifdef CY_DEBUG_OPEN printk(KERN_DEBUG "cyc startup done\n"); @@ -1436,6 +1422,7 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) errout: spin_unlock_irqrestore(&card->card_lock, flags); + free_page(page); return retval; } /* startup */ -- cgit v1.2.3 From 6c28181cf8b7d5af5f20a7bd102452033e14d946 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:15 -0700 Subject: cyclades: ioctls cleanup - add a cy_ prefix to functions with changed prototypes - cy_get_serial_info: initialize serial_struct by initializer, save a memset - inline simple functions (get_mon_info, {s,g}et_default_threshold, {s,g}et_default_timeout) directly in the ioctl handler - add a cy_cflags_changed helper to not copy its code by wait_event_interruptible - remove some ret_val = 0 assignments, it's preset to 0 - TIOCGICOUNT: don't do many put_user's, do one copy_to_user Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 179 ++++++++++++++++++------------------------------ 1 file changed, 67 insertions(+), 112 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 47cd7b8d4d97..e1637ad9390e 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -2411,29 +2411,25 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) } } /* set_line_char */ -static int -get_serial_info(struct cyclades_port *info, +static int cy_get_serial_info(struct cyclades_port *info, struct serial_struct __user *retinfo) { - struct serial_struct tmp; struct cyclades_card *cinfo = info->card; - - if (!retinfo) - return -EFAULT; - memset(&tmp, 0, sizeof(tmp)); - tmp.type = info->type; - tmp.line = info->line; - tmp.port = (info->card - cy_card) * 0x100 + info->line - - cinfo->first_line; - tmp.irq = cinfo->irq; - tmp.flags = info->port.flags; - tmp.close_delay = info->port.close_delay; - tmp.closing_wait = info->port.closing_wait; - tmp.baud_base = info->baud; - tmp.custom_divisor = info->custom_divisor; - tmp.hub6 = 0; /*!!! */ + struct serial_struct tmp = { + .type = info->type, + .line = info->line, + .port = (info->card - cy_card) * 0x100 + info->line - + cinfo->first_line, + .irq = cinfo->irq, + .flags = info->port.flags, + .close_delay = info->port.close_delay, + .closing_wait = info->port.closing_wait, + .baud_base = info->baud, + .custom_divisor = info->custom_divisor, + .hub6 = 0, /*!!! */ + }; return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0; -} /* get_serial_info */ +} static int cy_set_serial_info(struct cyclades_port *info, struct tty_struct *tty, @@ -2712,18 +2708,6 @@ static int cy_break(struct tty_struct *tty, int break_state) return retval; } /* cy_break */ -static int get_mon_info(struct cyclades_port *info, - struct cyclades_monitor __user *mon) -{ - if (copy_to_user(mon, &info->mon, sizeof(struct cyclades_monitor))) - return -EFAULT; - info->mon.int_count = 0; - info->mon.char_count = 0; - info->mon.char_max = 0; - info->mon.char_last = 0; - return 0; -} /* get_mon_info */ - static int set_threshold(struct cyclades_port *info, unsigned long value) { struct cyclades_card *card; @@ -2773,19 +2757,6 @@ static int get_threshold(struct cyclades_port *info, return 0; } /* get_threshold */ -static int set_default_threshold(struct cyclades_port *info, - unsigned long value) -{ - info->default_threshold = value & 0x0f; - return 0; -} /* set_default_threshold */ - -static int get_default_threshold(struct cyclades_port *info, - unsigned long __user *value) -{ - return put_user(info->default_threshold, value); -} /* get_default_threshold */ - static int set_timeout(struct cyclades_port *info, unsigned long value) { struct cyclades_card *card; @@ -2830,17 +2801,26 @@ static int get_timeout(struct cyclades_port *info, return 0; } /* get_timeout */ -static int set_default_timeout(struct cyclades_port *info, unsigned long value) +static int cy_cflags_changed(struct cyclades_port *info, unsigned long arg, + struct cyclades_icount *cprev) { - info->default_timeout = value & 0xff; - return 0; -} /* set_default_timeout */ + struct cyclades_icount cnow; + unsigned long flags; + int ret; -static int get_default_timeout(struct cyclades_port *info, - unsigned long __user *value) -{ - return put_user(info->default_timeout, value); -} /* get_default_timeout */ + spin_lock_irqsave(&info->card->card_lock, flags); + cnow = info->icount; /* atomic copy */ + spin_unlock_irqrestore(&info->card->card_lock, flags); + + ret = ((arg & TIOCM_RNG) && (cnow.rng != cprev->rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev->dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev->dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev->cts)); + + *cprev = cnow; + + return ret; +} /* * This routine allows the tty driver to implement device- @@ -2852,8 +2832,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) { struct cyclades_port *info = tty->driver_data; - struct cyclades_icount cprev, cnow; /* kernel counter temps */ - struct serial_icounter_struct __user *p_cuser; /* user space */ + struct cyclades_icount cnow; /* kernel counter temps */ int ret_val = 0; unsigned long flags; void __user *argp = (void __user *)arg; @@ -2869,7 +2848,11 @@ cy_ioctl(struct tty_struct *tty, struct file *file, switch (cmd) { case CYGETMON: - ret_val = get_mon_info(info, argp); + if (copy_to_user(argp, &info->mon, sizeof(info->mon))) { + ret_val = -EFAULT; + break; + } + memset(&info->mon, 0, sizeof(info->mon)); break; case CYGETTHRESH: ret_val = get_threshold(info, argp); @@ -2878,10 +2861,11 @@ cy_ioctl(struct tty_struct *tty, struct file *file, ret_val = set_threshold(info, arg); break; case CYGETDEFTHRESH: - ret_val = get_default_threshold(info, argp); + ret_val = put_user(info->default_threshold, + (unsigned long __user *)argp); break; case CYSETDEFTHRESH: - ret_val = set_default_threshold(info, arg); + info->default_threshold = arg & 0x0f; break; case CYGETTIMEOUT: ret_val = get_timeout(info, argp); @@ -2890,21 +2874,20 @@ cy_ioctl(struct tty_struct *tty, struct file *file, ret_val = set_timeout(info, arg); break; case CYGETDEFTIMEOUT: - ret_val = get_default_timeout(info, argp); + ret_val = put_user(info->default_timeout, + (unsigned long __user *)argp); break; case CYSETDEFTIMEOUT: - ret_val = set_default_timeout(info, arg); + info->default_timeout = arg & 0xff; break; case CYSETRFLOW: info->rflow = (int)arg; - ret_val = 0; break; case CYGETRFLOW: ret_val = info->rflow; break; case CYSETRTSDTR_INV: info->rtsdtr_inv = (int)arg; - ret_val = 0; break; case CYGETRTSDTR_INV: ret_val = info->rtsdtr_inv; @@ -2915,7 +2898,6 @@ cy_ioctl(struct tty_struct *tty, struct file *file, #ifndef CONFIG_CYZ_INTR case CYZSETPOLLCYCLE: cyz_polling_cycle = (arg * HZ) / 1000; - ret_val = 0; break; case CYZGETPOLLCYCLE: ret_val = (cyz_polling_cycle * 1000) / HZ; @@ -2923,13 +2905,12 @@ cy_ioctl(struct tty_struct *tty, struct file *file, #endif /* CONFIG_CYZ_INTR */ case CYSETWAIT: info->port.closing_wait = (unsigned short)arg * HZ / 100; - ret_val = 0; break; case CYGETWAIT: ret_val = info->port.closing_wait / (HZ / 100); break; case TIOCGSERIAL: - ret_val = get_serial_info(info, argp); + ret_val = cy_get_serial_info(info, argp); break; case TIOCSSERIAL: ret_val = cy_set_serial_info(info, tty, argp); @@ -2948,17 +2929,8 @@ cy_ioctl(struct tty_struct *tty, struct file *file, /* note the counters on entry */ cnow = info->icount; spin_unlock_irqrestore(&info->card->card_lock, flags); - ret_val = wait_event_interruptible(info->delta_msr_wait, ({ - cprev = cnow; - spin_lock_irqsave(&info->card->card_lock, flags); - cnow = info->icount; /* atomic copy */ - spin_unlock_irqrestore(&info->card->card_lock, flags); - - ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)); - })); + ret_val = wait_event_interruptible(info->delta_msr_wait, + cy_cflags_changed(info, arg, &cnow)); break; /* @@ -2967,46 +2939,29 @@ cy_ioctl(struct tty_struct *tty, struct file *file, * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */ - case TIOCGICOUNT: + case TIOCGICOUNT: { + struct serial_icounter_struct sic = { }; + spin_lock_irqsave(&info->card->card_lock, flags); cnow = info->icount; spin_unlock_irqrestore(&info->card->card_lock, flags); - p_cuser = argp; - ret_val = put_user(cnow.cts, &p_cuser->cts); - if (ret_val) - break; - ret_val = put_user(cnow.dsr, &p_cuser->dsr); - if (ret_val) - break; - ret_val = put_user(cnow.rng, &p_cuser->rng); - if (ret_val) - break; - ret_val = put_user(cnow.dcd, &p_cuser->dcd); - if (ret_val) - break; - ret_val = put_user(cnow.rx, &p_cuser->rx); - if (ret_val) - break; - ret_val = put_user(cnow.tx, &p_cuser->tx); - if (ret_val) - break; - ret_val = put_user(cnow.frame, &p_cuser->frame); - if (ret_val) - break; - ret_val = put_user(cnow.overrun, &p_cuser->overrun); - if (ret_val) - break; - ret_val = put_user(cnow.parity, &p_cuser->parity); - if (ret_val) - break; - ret_val = put_user(cnow.brk, &p_cuser->brk); - if (ret_val) - break; - ret_val = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); - if (ret_val) - break; - ret_val = 0; + + sic.cts = cnow.cts; + sic.dsr = cnow.dsr; + sic.rng = cnow.rng; + sic.dcd = cnow.dcd; + sic.rx = cnow.rx; + sic.tx = cnow.tx; + sic.frame = cnow.frame; + sic.overrun = cnow.overrun; + sic.parity = cnow.parity; + sic.brk = cnow.brk; + sic.buf_overrun = cnow.buf_overrun; + + if (copy_to_user(argp, &sic, sizeof(sic))) + ret_val = -EFAULT; break; + } default: ret_val = -ENOIOCTLCMD; } -- cgit v1.2.3 From 0d3487294e4e175eb6371c8df8ef44b45964e0f6 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:16 -0700 Subject: cyclades: tiocm cleanup - save one indent level by inverting !fw_loaded condition - read rs_status on Z and write it after we change all the flags, don't do that separately - remove Y inverted rts/dtr branching, precompute registers and use them Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 153 +++++++++++++++++++++--------------------------- 1 file changed, 66 insertions(+), 87 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index e1637ad9390e..ed66c3ab230d 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -831,6 +831,7 @@ static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, struct cyclades_card *card = info->card; void __iomem *base_addr; int chip, channel, index; + u32 rts, dtr, msvrr, msvrd; channel = info->line - card->first_line; chip = channel >> 2; @@ -838,29 +839,28 @@ static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, index = card->bus_index; base_addr = card->base_addr + (cy_chip_offset[chip] << index); + if (info->rtsdtr_inv) { + msvrr = CyMSVR2; + msvrd = CyMSVR1; + rts = CyDTR; + dtr = CyRTS; + } else { + msvrr = CyMSVR1; + msvrd = CyMSVR2; + rts = CyRTS; + dtr = CyDTR; + } if (set & TIOCM_RTS) { - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR2 << index), CyDTR); - } else { - cy_writeb(base_addr + (CyMSVR1 << index), CyRTS); - } + cy_writeb(base_addr + (CyCAR << index), (u8)channel); + cy_writeb(base_addr + (msvrr << index), rts); } if (clear & TIOCM_RTS) { - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR2 << index), ~CyDTR); - } else { - cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); - } + cy_writeb(base_addr + (CyCAR << index), (u8)channel); + cy_writeb(base_addr + (msvrr << index), ~rts); } if (set & TIOCM_DTR) { - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR1 << index), CyRTS); - } else { - cy_writeb(base_addr + (CyMSVR2 << index), CyDTR); - } + cy_writeb(base_addr + (CyCAR << index), (u8)channel); + cy_writeb(base_addr + (msvrd << index), dtr); #ifdef CY_DEBUG_DTR printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n"); printk(KERN_DEBUG " status: 0x%x, 0x%x\n", @@ -869,13 +869,8 @@ static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, #endif } if (clear & TIOCM_DTR) { - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - if (info->rtsdtr_inv) { - cy_writeb(base_addr + (CyMSVR1 << index), ~CyRTS); - } else { - cy_writeb(base_addr + (CyMSVR2 << index), ~CyDTR); - } - + cy_writeb(base_addr + (CyCAR << index), (u8)channel); + cy_writeb(base_addr + (msvrd << index), ~dtr); #ifdef CY_DEBUG_DTR printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n"); printk(KERN_DEBUG " status: 0x%x, 0x%x\n", @@ -2518,28 +2513,27 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file) { struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; - int chip, channel, index; void __iomem *base_addr; - unsigned long flags; - unsigned char status; - unsigned long lstatus; - unsigned int result; + int result, channel; if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; - lock_kernel(); - card = info->card; channel = info->line - card->first_line; + + lock_kernel(); if (!cy_is_Z(card)) { - chip = channel >> 2; + unsigned long flags; + unsigned char status; + int chip = channel >> 2; + int index = card->bus_index; + channel &= 0x03; - index = card->bus_index; base_addr = card->base_addr + (cy_chip_offset[chip] << index); spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); + cy_writeb(base_addr + (CyCAR << index), (u8)channel); status = readb(base_addr + (CyMSVR1 << index)); status |= readb(base_addr + (CyMSVR2 << index)); spin_unlock_irqrestore(&card->card_lock, flags); @@ -2556,21 +2550,22 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file) ((status & CyDSR) ? TIOCM_DSR : 0) | ((status & CyCTS) ? TIOCM_CTS : 0); } else { - if (cyz_is_loaded(card)) { - lstatus = readl(&info->u.cyz.ch_ctrl->rs_status); - result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | - ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | - ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) | - ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) | - ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) | - ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); - } else { - result = 0; - unlock_kernel(); - return -ENODEV; + u32 lstatus; + + if (!cyz_is_loaded(card)) { + result = -ENODEV; + goto end; } + lstatus = readl(&info->u.cyz.ch_ctrl->rs_status); + result = ((lstatus & C_RS_RTS) ? TIOCM_RTS : 0) | + ((lstatus & C_RS_DTR) ? TIOCM_DTR : 0) | + ((lstatus & C_RS_DCD) ? TIOCM_CAR : 0) | + ((lstatus & C_RS_RI) ? TIOCM_RNG : 0) | + ((lstatus & C_RS_DSR) ? TIOCM_DSR : 0) | + ((lstatus & C_RS_CTS) ? TIOCM_CTS : 0); } +end: unlock_kernel(); return result; } /* cy_tiomget */ @@ -2582,68 +2577,52 @@ cy_tiocmset(struct tty_struct *tty, struct file *file, struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; unsigned long flags; - int channel, retval; if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; card = info->card; - channel = (info->line) - (card->first_line); if (!cy_is_Z(card)) { spin_lock_irqsave(&card->card_lock, flags); cyy_change_rts_dtr(info, set, clear); spin_unlock_irqrestore(&card->card_lock, flags); } else { - if (cyz_is_loaded(card)) { - struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - - if (set & TIOCM_RTS) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl->rs_control, - readl(&ch_ctrl->rs_control) | C_RS_RTS); - spin_unlock_irqrestore(&card->card_lock, flags); - } - if (clear & TIOCM_RTS) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl->rs_control, - readl(&ch_ctrl->rs_control) & - ~C_RS_RTS); - spin_unlock_irqrestore(&card->card_lock, flags); - } - if (set & TIOCM_DTR) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl->rs_control, - readl(&ch_ctrl->rs_control) | C_RS_DTR); + struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; + int retval, channel = info->line - card->first_line; + u32 rs; + + if (!cyz_is_loaded(card)) + return -ENODEV; + + spin_lock_irqsave(&card->card_lock, flags); + rs = readl(&ch_ctrl->rs_control); + if (set & TIOCM_RTS) + rs |= C_RS_RTS; + if (clear & TIOCM_RTS) + rs &= ~C_RS_RTS; + if (set & TIOCM_DTR) { + rs |= C_RS_DTR; #ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info raising " - "Z DTR\n"); + printk(KERN_DEBUG "cyc:set_modem_info raising Z DTR\n"); #endif - spin_unlock_irqrestore(&card->card_lock, flags); - } - if (clear & TIOCM_DTR) { - spin_lock_irqsave(&card->card_lock, flags); - cy_writel(&ch_ctrl->rs_control, - readl(&ch_ctrl->rs_control) & - ~C_RS_DTR); + } + if (clear & TIOCM_DTR) { + rs &= ~C_RS_DTR; #ifdef CY_DEBUG_DTR - printk(KERN_DEBUG "cyc:set_modem_info clearing " - "Z DTR\n"); + printk(KERN_DEBUG "cyc:set_modem_info clearing " + "Z DTR\n"); #endif - spin_unlock_irqrestore(&card->card_lock, flags); - } - } else { - return -ENODEV; } - spin_lock_irqsave(&card->card_lock, flags); + cy_writel(&ch_ctrl->rs_control, rs); retval = cyz_issue_cmd(card, channel, C_CM_IOCTLM, 0L); + spin_unlock_irqrestore(&card->card_lock, flags); if (retval != 0) { printk(KERN_ERR "cyc:set_modem_info retval on ttyC%d " "was %x\n", info->line, retval); } - spin_unlock_irqrestore(&card->card_lock, flags); } return 0; -} /* cy_tiocmset */ +} /* * cy_break() --- routine which turns the break handling on or off -- cgit v1.2.3 From 3aeea5b92210083c7cffd4f08a0bb141d3f2d574 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:16 -0700 Subject: cyclades: introduce cyy_readb/writeb Add helpers for io operations, so that we can eliminate huge amount of supporting code. It is now centralized in those helpers and used values are precomputed in the init phase. Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 448 +++++++++++++++++------------------------------ include/linux/cyclades.h | 2 +- 2 files changed, 165 insertions(+), 285 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index ed66c3ab230d..cf2874a89c8c 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -90,7 +90,6 @@ #include #include -static void cy_throttle(struct tty_struct *tty); static void cy_send_xchar(struct tty_struct *tty, char ch); #ifndef SERIAL_XMIT_SIZE @@ -298,6 +297,20 @@ static void cyz_rx_restart(unsigned long); static struct timer_list cyz_rx_full_timer[NR_PORTS]; #endif /* CONFIG_CYZ_INTR */ +static inline void cyy_writeb(struct cyclades_port *port, u32 reg, u8 val) +{ + struct cyclades_card *card = port->card; + + cy_writeb(port->u.cyy.base_addr + (reg << card->bus_index), val); +} + +static inline u8 cyy_readb(struct cyclades_port *port, u32 reg) +{ + struct cyclades_card *card = port->card; + + return readb(port->u.cyy.base_addr + (reg << card->bus_index)); +} + static inline bool cy_is_Z(struct cyclades_card *card) { return card->num_chips == (unsigned int)-1; @@ -350,13 +363,14 @@ static inline int serial_paranoia_check(struct cyclades_port *info, This function is only called from inside spinlock-protected code. */ -static int cyy_issue_cmd(void __iomem *base_addr, u_char cmd, int index) +static int __cyy_issue_cmd(void __iomem *base_addr, u8 cmd, int index) { + void __iomem *ccr = base_addr + (CyCCR << index); unsigned int i; /* Check to see that the previous command has completed */ for (i = 0; i < 100; i++) { - if (readb(base_addr + (CyCCR << index)) == 0) + if (readb(ccr) == 0) break; udelay(10L); } @@ -366,10 +380,16 @@ static int cyy_issue_cmd(void __iomem *base_addr, u_char cmd, int index) return -1; /* Issue the new command */ - cy_writeb(base_addr + (CyCCR << index), cmd); + cy_writeb(ccr, cmd); return 0; -} /* cyy_issue_cmd */ +} + +static inline int cyy_issue_cmd(struct cyclades_port *port, u8 cmd) +{ + return __cyy_issue_cmd(port->u.cyy.base_addr, cmd, + port->card->bus_index); +} #ifdef CONFIG_ISA /* ISA interrupt detection code */ @@ -394,7 +414,7 @@ static unsigned detect_isa_irq(void __iomem *address) /* Enable the Tx interrupts on the CD1400 */ local_irq_save(flags); cy_writeb(address + (CyCAR << index), 0); - cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index); + __cyy_issue_cmd(address, CyCHAN_CTL | CyENB_XMTR, index); cy_writeb(address + (CyCAR << index), 0); cy_writeb(address + (CySRER << index), @@ -428,7 +448,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, struct cyclades_port *info; struct tty_struct *tty; int len, index = cinfo->bus_index; - u8 save_xir, channel, save_car, data, char_count; + u8 ivr, save_xir, channel, save_car, data, char_count; #ifdef CY_DEBUG_INTERRUPTS printk(KERN_DEBUG "cyy_interrupt: rcvd intr, chip %d\n", chip); @@ -437,26 +457,25 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, save_xir = readb(base_addr + (CyRIR << index)); channel = save_xir & CyIRChannel; info = &cinfo->ports[channel + chip * 4]; - save_car = readb(base_addr + (CyCAR << index)); - cy_writeb(base_addr + (CyCAR << index), save_xir); + save_car = cyy_readb(info, CyCAR); + cyy_writeb(info, CyCAR, save_xir); + ivr = cyy_readb(info, CyRIVR) & CyIVRMask; tty = tty_port_tty_get(&info->port); /* if there is nowhere to put the data, discard it */ if (tty == NULL) { - if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) == - CyIVRRxEx) { /* exception */ - data = readb(base_addr + (CyRDSR << index)); + if (ivr == CyIVRRxEx) { /* exception */ + data = cyy_readb(info, CyRDSR); } else { /* normal character reception */ - char_count = readb(base_addr + (CyRDCR << index)); + char_count = cyy_readb(info, CyRDCR); while (char_count--) - data = readb(base_addr + (CyRDSR << index)); + data = cyy_readb(info, CyRDSR); } goto end; } /* there is an open port for this data */ - if ((readb(base_addr + (CyRIVR << index)) & CyIVRMask) == - CyIVRRxEx) { /* exception */ - data = readb(base_addr + (CyRDSR << index)); + if (ivr == CyIVRRxEx) { /* exception */ + data = cyy_readb(info, CyRDSR); /* For statistics only */ if (data & CyBREAK) @@ -477,22 +496,22 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, if (data & info->read_status_mask) { if (data & CyBREAK) { tty_insert_flip_char(tty, - readb(base_addr + (CyRDSR << - index)), TTY_BREAK); + cyy_readb(info, CyRDSR), + TTY_BREAK); info->icount.rx++; if (info->port.flags & ASYNC_SAK) do_SAK(tty); } else if (data & CyFRAME) { tty_insert_flip_char(tty, - readb(base_addr + (CyRDSR << - index)), TTY_FRAME); + cyy_readb(info, CyRDSR), + TTY_FRAME); info->icount.rx++; info->idle_stats.frame_errs++; } else if (data & CyPARITY) { /* Pieces of seven... */ tty_insert_flip_char(tty, - readb(base_addr + (CyRDSR << - index)), TTY_PARITY); + cyy_readb(info, CyRDSR), + TTY_PARITY); info->icount.rx++; info->idle_stats.parity_errs++; } else if (data & CyOVERRUN) { @@ -504,8 +523,8 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, the next incoming character. */ tty_insert_flip_char(tty, - readb(base_addr + (CyRDSR << - index)), TTY_FRAME); + cyy_readb(info, CyRDSR), + TTY_FRAME); info->icount.rx++; info->idle_stats.overruns++; /* These two conditions may imply */ @@ -529,7 +548,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, } } else { /* normal character reception */ /* load # chars available from the chip */ - char_count = readb(base_addr + (CyRDCR << index)); + char_count = cyy_readb(info, CyRDCR); #ifdef CY_ENABLE_MONITORING ++info->mon.int_count; @@ -540,7 +559,7 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, #endif len = tty_buffer_request_room(tty, char_count); while (len--) { - data = readb(base_addr + (CyRDSR << index)); + data = cyy_readb(info, CyRDSR); tty_insert_flip_char(tty, data, TTY_NORMAL); info->idle_stats.recv_bytes++; info->icount.rx++; @@ -554,8 +573,8 @@ static void cyy_chip_rx(struct cyclades_card *cinfo, int chip, tty_kref_put(tty); end: /* end of service */ - cy_writeb(base_addr + (CyRIR << index), save_xir & 0x3f); - cy_writeb(base_addr + (CyCAR << index), save_car); + cyy_writeb(info, CyRIR, save_xir & 0x3f); + cyy_writeb(info, CyCAR, save_car); } static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, @@ -588,8 +607,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, info = &cinfo->ports[channel + chip * 4]; tty = tty_port_tty_get(&info->port); if (tty == NULL) { - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & ~CyTxRdy); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); goto end; } @@ -598,7 +616,7 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, if (info->x_char) { /* send special char */ outch = info->x_char; - cy_writeb(base_addr + (CyTDR << index), outch); + cyy_writeb(info, CyTDR, outch); char_count--; info->icount.tx++; info->x_char = 0; @@ -606,14 +624,14 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, if (info->breakon || info->breakoff) { if (info->breakon) { - cy_writeb(base_addr + (CyTDR << index), 0); - cy_writeb(base_addr + (CyTDR << index), 0x81); + cyy_writeb(info, CyTDR, 0); + cyy_writeb(info, CyTDR, 0x81); info->breakon = 0; char_count -= 2; } if (info->breakoff) { - cy_writeb(base_addr + (CyTDR << index), 0); - cy_writeb(base_addr + (CyTDR << index), 0x83); + cyy_writeb(info, CyTDR, 0); + cyy_writeb(info, CyTDR, 0x83); info->breakoff = 0; char_count -= 2; } @@ -621,27 +639,23 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, while (char_count-- > 0) { if (!info->xmit_cnt) { - if (readb(base_addr + (CySRER << index)) & CyTxMpty) { - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & - ~CyTxMpty); + if (cyy_readb(info, CySRER) & CyTxMpty) { + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxMpty); } else { - cy_writeb(base_addr + (CySRER << index), - (readb(base_addr + (CySRER << index)) & - ~CyTxRdy) | CyTxMpty); + cyy_writeb(info, CySRER, CyTxMpty | + (cyy_readb(info, CySRER) & ~CyTxRdy)); } goto done; } if (info->port.xmit_buf == NULL) { - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & - ~CyTxRdy); + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxRdy); goto done; } if (tty->stopped || tty->hw_stopped) { - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & - ~CyTxRdy); + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxRdy); goto done; } /* Because the Embedded Transmit Commands have been enabled, @@ -658,15 +672,15 @@ static void cyy_chip_tx(struct cyclades_card *cinfo, unsigned int chip, info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); - cy_writeb(base_addr + (CyTDR << index), outch); + cyy_writeb(info, CyTDR, outch); info->icount.tx++; } else { if (char_count > 1) { info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); - cy_writeb(base_addr + (CyTDR << index), outch); - cy_writeb(base_addr + (CyTDR << index), 0); + cyy_writeb(info, CyTDR, outch); + cyy_writeb(info, CyTDR, 0); info->icount.tx++; char_count--; } @@ -678,8 +692,8 @@ done: tty_kref_put(tty); end: /* end of service */ - cy_writeb(base_addr + (CyTIR << index), save_xir & 0x3f); - cy_writeb(base_addr + (CyCAR << index), save_car); + cyy_writeb(info, CyTIR, save_xir & 0x3f); + cyy_writeb(info, CyCAR, save_car); } static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, @@ -694,11 +708,11 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, save_xir = readb(base_addr + (CyMIR << index)); channel = save_xir & CyIRChannel; info = &cinfo->ports[channel + chip * 4]; - save_car = readb(base_addr + (CyCAR << index)); - cy_writeb(base_addr + (CyCAR << index), save_xir); + save_car = cyy_readb(info, CyCAR); + cyy_writeb(info, CyCAR, save_xir); - mdm_change = readb(base_addr + (CyMISR << index)); - mdm_status = readb(base_addr + (CyMSVR1 << index)); + mdm_change = cyy_readb(info, CyMISR); + mdm_status = cyy_readb(info, CyMSVR1); tty = tty_port_tty_get(&info->port); if (!tty) @@ -730,9 +744,8 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, /* cy_start isn't used because... !!! */ tty->hw_stopped = 0; - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) | - CyTxRdy); + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) | CyTxRdy); tty_wakeup(tty); } } else { @@ -740,9 +753,8 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, /* cy_stop isn't used because ... !!! */ tty->hw_stopped = 1; - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & - ~CyTxRdy); + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) & ~CyTxRdy); } } } @@ -753,8 +765,8 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, tty_kref_put(tty); end: /* end of service */ - cy_writeb(base_addr + (CyMIR << index), save_xir & 0x3f); - cy_writeb(base_addr + (CyCAR << index), save_car); + cyy_writeb(info, CyMIR, save_xir & 0x3f); + cyy_writeb(info, CyCAR, save_car); } /* The real interrupt service routine is called @@ -829,15 +841,10 @@ static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, unsigned int clear) { struct cyclades_card *card = info->card; - void __iomem *base_addr; - int chip, channel, index; + int channel = info->line - card->first_line; u32 rts, dtr, msvrr, msvrd; - channel = info->line - card->first_line; - chip = channel >> 2; channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); if (info->rtsdtr_inv) { msvrr = CyMSVR2; @@ -851,31 +858,31 @@ static void cyy_change_rts_dtr(struct cyclades_port *info, unsigned int set, dtr = CyDTR; } if (set & TIOCM_RTS) { - cy_writeb(base_addr + (CyCAR << index), (u8)channel); - cy_writeb(base_addr + (msvrr << index), rts); + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrr, rts); } if (clear & TIOCM_RTS) { - cy_writeb(base_addr + (CyCAR << index), (u8)channel); - cy_writeb(base_addr + (msvrr << index), ~rts); + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrr, ~rts); } if (set & TIOCM_DTR) { - cy_writeb(base_addr + (CyCAR << index), (u8)channel); - cy_writeb(base_addr + (msvrd << index), dtr); + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrd, dtr); #ifdef CY_DEBUG_DTR printk(KERN_DEBUG "cyc:set_modem_info raising DTR\n"); printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); + cyy_readb(info, CyMSVR1), + cyy_readb(info, CyMSVR2)); #endif } if (clear & TIOCM_DTR) { - cy_writeb(base_addr + (CyCAR << index), (u8)channel); - cy_writeb(base_addr + (msvrd << index), ~dtr); + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, msvrd, ~dtr); #ifdef CY_DEBUG_DTR printk(KERN_DEBUG "cyc:set_modem_info dropping DTR\n"); printk(KERN_DEBUG " status: 0x%x, 0x%x\n", - readb(base_addr + (CyMSVR1 << index)), - readb(base_addr + (CyMSVR2 << index))); + cyy_readb(info, CyMSVR1), + cyy_readb(info, CyMSVR2)); #endif } } @@ -1290,7 +1297,6 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) struct cyclades_card *card; unsigned long flags; int retval = 0; - void __iomem *base_addr; int channel; unsigned long page; @@ -1321,31 +1327,21 @@ static int cy_startup(struct cyclades_port *info, struct tty_struct *tty) cy_set_line_char(info, tty); if (!cy_is_Z(card)) { - int chip = channel >> 2; - int index = card->bus_index; channel &= 0x03; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc startup card %d, chip %d, channel %d, " - "base_addr %p\n", - card, chip, channel, base_addr); -#endif spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); + cyy_writeb(info, CyCAR, channel); - cy_writeb(base_addr + (CyRTPR << index), + cyy_writeb(info, CyRTPR, (info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ - cyy_issue_cmd(base_addr, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR, - index); + cyy_issue_cmd(info, CyCHAN_CTL | CyENB_RCVR | CyENB_XMTR); cyy_change_rts_dtr(info, TIOCM_RTS | TIOCM_DTR, 0); - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) | CyRxData); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyRxData); } else { struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; @@ -1423,23 +1419,14 @@ errout: static void start_xmit(struct cyclades_port *info) { - struct cyclades_card *card; + struct cyclades_card *card = info->card; unsigned long flags; - void __iomem *base_addr; - int chip, channel, index; + int channel = info->line - card->first_line; - card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), channel); - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) | CyTxRdy); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); spin_unlock_irqrestore(&card->card_lock, flags); } else { #ifdef CONFIG_CYZ_INTR @@ -1466,8 +1453,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) { struct cyclades_card *card; unsigned long flags; - void __iomem *base_addr; - int chip, channel, index; + int channel; if (!(info->port.flags & ASYNC_INITIALIZED)) return; @@ -1475,17 +1461,6 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) card = info->card; channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - -#ifdef CY_DEBUG_OPEN - printk(KERN_DEBUG "cyc shutdown Y card %d, chip %d, " - "channel %d, base_addr %p\n", - card, chip, channel, base_addr); -#endif - spin_lock_irqsave(&card->card_lock, flags); /* Clear delta_msr_wait queue to avoid mem leaks. */ @@ -1500,7 +1475,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) if (tty->termios->c_cflag & HUPCL) cyy_change_rts_dtr(info, 0, TIOCM_RTS | TIOCM_DTR); - cyy_issue_cmd(base_addr, CyCHAN_CTL | CyDIS_RCVR, index); + cyy_issue_cmd(info, CyCHAN_CTL | CyDIS_RCVR); /* it may be appropriate to clear _XMIT at some later date (after testing)!!! */ @@ -1677,8 +1652,6 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) { struct cyclades_card *card; struct cyclades_port *info = tty->driver_data; - void __iomem *base_addr; - int chip, channel, index; unsigned long orig_jiffies; int char_time; @@ -1722,13 +1695,8 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout) timeout, char_time, jiffies); #endif card = info->card; - channel = (info->line) - (card->first_line); if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - while (readb(base_addr + (CySRER << index)) & CyTxRdy) { + while (cyy_readb(info, CySRER) & CyTxRdy) { #ifdef CY_DEBUG_WAIT_UNTIL_SENT printk(KERN_DEBUG "Not clean (jiff=%lu)...", jiffies); #endif @@ -1790,6 +1758,7 @@ static void cy_close(struct tty_struct *tty, struct file *filp) struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; unsigned long flags; + int channel; if (!info || serial_paranoia_check(info, tty->name, "cy_close")) return; @@ -1799,18 +1768,13 @@ static void cy_close(struct tty_struct *tty, struct file *filp) if (!tty_port_close_start(&info->port, tty, filp)) return; + channel = info->line - card->first_line; spin_lock_irqsave(&card->card_lock, flags); if (!cy_is_Z(card)) { - int channel = info->line - card->first_line; - int index = card->bus_index; - void __iomem *base_addr = card->base_addr + - (cy_chip_offset[channel >> 2] << index); /* Stop accepting input */ - channel &= 0x03; - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & ~CyRxData); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyRxData); if (info->port.flags & ASYNC_INITIALIZED) { /* Waiting for on-board buffers to be empty before closing the port */ @@ -1823,7 +1787,6 @@ static void cy_close(struct tty_struct *tty, struct file *filp) /* Waiting for on-board buffers to be empty before closing the port */ struct CH_CTRL __iomem *ch_ctrl = info->u.cyz.ch_ctrl; - int channel = info->line - card->first_line; int retval; if (readl(&ch_ctrl->flow_status) != C_FS_TXIDLE) { @@ -2064,8 +2027,7 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) { struct cyclades_card *card; unsigned long flags; - void __iomem *base_addr; - int chip, channel, index; + int channel; unsigned cflag, iflag; int baud, baud_rate = 0; int i; @@ -2095,9 +2057,6 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) channel = info->line - card->first_line; if (!cy_is_Z(card)) { - - index = card->bus_index; - /* baud rate */ baud = tty_get_baud_rate(tty); if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == @@ -2208,70 +2167,67 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) cable. Contact Marcio Saito for details. ***********************************************/ - chip = channel >> 2; channel &= 0x03; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); + cyy_writeb(info, CyCAR, channel); /* tx and rx baud rate */ - cy_writeb(base_addr + (CyTCOR << index), info->tco); - cy_writeb(base_addr + (CyTBPR << index), info->tbpr); - cy_writeb(base_addr + (CyRCOR << index), info->rco); - cy_writeb(base_addr + (CyRBPR << index), info->rbpr); + cyy_writeb(info, CyTCOR, info->tco); + cyy_writeb(info, CyTBPR, info->tbpr); + cyy_writeb(info, CyRCOR, info->rco); + cyy_writeb(info, CyRBPR, info->rbpr); /* set line characteristics according configuration */ - cy_writeb(base_addr + (CySCHR1 << index), START_CHAR(tty)); - cy_writeb(base_addr + (CySCHR2 << index), STOP_CHAR(tty)); - cy_writeb(base_addr + (CyCOR1 << index), info->cor1); - cy_writeb(base_addr + (CyCOR2 << index), info->cor2); - cy_writeb(base_addr + (CyCOR3 << index), info->cor3); - cy_writeb(base_addr + (CyCOR4 << index), info->cor4); - cy_writeb(base_addr + (CyCOR5 << index), info->cor5); + cyy_writeb(info, CySCHR1, START_CHAR(tty)); + cyy_writeb(info, CySCHR2, STOP_CHAR(tty)); + cyy_writeb(info, CyCOR1, info->cor1); + cyy_writeb(info, CyCOR2, info->cor2); + cyy_writeb(info, CyCOR3, info->cor3); + cyy_writeb(info, CyCOR4, info->cor4); + cyy_writeb(info, CyCOR5, info->cor5); - cyy_issue_cmd(base_addr, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch | - CyCOR3ch, index); + cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR1ch | CyCOR2ch | + CyCOR3ch); /* !!! Is this needed? */ - cy_writeb(base_addr + (CyCAR << index), (u_char) channel); - cy_writeb(base_addr + (CyRTPR << index), + cyy_writeb(info, CyCAR, channel); + cyy_writeb(info, CyRTPR, (info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ if (C_CLOCAL(tty)) { /* without modem intr */ - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) | CyMdmCh); + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) | CyMdmCh); /* act on 1->0 modem transitions */ if ((cflag & CRTSCTS) && info->rflow) { - cy_writeb(base_addr + (CyMCOR1 << index), + cyy_writeb(info, CyMCOR1, (CyCTS | rflow_thr[i])); } else { - cy_writeb(base_addr + (CyMCOR1 << index), + cyy_writeb(info, CyMCOR1, CyCTS); } /* act on 0->1 modem transitions */ - cy_writeb(base_addr + (CyMCOR2 << index), CyCTS); + cyy_writeb(info, CyMCOR2, CyCTS); } else { /* without modem intr */ - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + - (CySRER << index)) | CyMdmCh); + cyy_writeb(info, CySRER, + cyy_readb(info, CySRER) | CyMdmCh); /* act on 1->0 modem transitions */ if ((cflag & CRTSCTS) && info->rflow) { - cy_writeb(base_addr + (CyMCOR1 << index), + cyy_writeb(info, CyMCOR1, (CyDSR | CyCTS | CyRI | CyDCD | rflow_thr[i])); } else { - cy_writeb(base_addr + (CyMCOR1 << index), - CyDSR | CyCTS | CyRI | CyDCD); + cyy_writeb(info, CyMCOR1, + CyDSR | CyCTS | CyRI | CyDCD); } /* act on 0->1 modem transitions */ - cy_writeb(base_addr + (CyMCOR2 << index), - CyDSR | CyCTS | CyRI | CyDCD); + cyy_writeb(info, CyMCOR2, + CyDSR | CyCTS | CyRI | CyDCD); } if (i == 0) /* baud rate is zero, turn off line */ @@ -2482,24 +2438,14 @@ check_and_exit: */ static int get_lsr_info(struct cyclades_port *info, unsigned int __user *value) { - struct cyclades_card *card; - int chip, channel, index; - unsigned char status; + struct cyclades_card *card = info->card; unsigned int result; unsigned long flags; - void __iomem *base_addr; + u8 status; - card = info->card; - channel = (info->line) - (card->first_line); if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - spin_lock_irqsave(&card->card_lock, flags); - status = readb(base_addr + (CySRER << index)) & - (CyTxRdy | CyTxMpty); + status = cyy_readb(info, CySRER) & (CyTxRdy | CyTxMpty); spin_unlock_irqrestore(&card->card_lock, flags); result = (status ? 0 : TIOCSER_TEMT); } else { @@ -2513,29 +2459,23 @@ static int cy_tiocmget(struct tty_struct *tty, struct file *file) { struct cyclades_port *info = tty->driver_data; struct cyclades_card *card; - void __iomem *base_addr; - int result, channel; + int result; if (serial_paranoia_check(info, tty->name, __func__)) return -ENODEV; card = info->card; - channel = info->line - card->first_line; lock_kernel(); if (!cy_is_Z(card)) { unsigned long flags; - unsigned char status; - int chip = channel >> 2; - int index = card->bus_index; - - channel &= 0x03; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); + int channel = info->line - card->first_line; + u8 status; spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), (u8)channel); - status = readb(base_addr + (CyMSVR1 << index)); - status |= readb(base_addr + (CyMSVR2 << index)); + cyy_writeb(info, CyCAR, channel & 0x03); + status = cyy_readb(info, CyMSVR1); + status |= cyy_readb(info, CyMSVR2); spin_unlock_irqrestore(&card->card_lock, flags); if (info->rtsdtr_inv) { @@ -2689,26 +2629,16 @@ static int cy_break(struct tty_struct *tty, int break_state) static int set_threshold(struct cyclades_port *info, unsigned long value) { - struct cyclades_card *card; - void __iomem *base_addr; - int channel, chip, index; + struct cyclades_card *card = info->card; unsigned long flags; - card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = - card->base_addr + (cy_chip_offset[chip] << index); - info->cor3 &= ~CyREC_FIFO; info->cor3 |= value & CyREC_FIFO; spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyCOR3 << index), info->cor3); - cyy_issue_cmd(base_addr, CyCOR_CHANGE | CyCOR3ch, index); + cyy_writeb(info, CyCOR3, info->cor3); + cyy_issue_cmd(info, CyCOR_CHANGE | CyCOR3ch); spin_unlock_irqrestore(&card->card_lock, flags); } return 0; @@ -2717,20 +2647,10 @@ static int set_threshold(struct cyclades_port *info, unsigned long value) static int get_threshold(struct cyclades_port *info, unsigned long __user *value) { - struct cyclades_card *card; - void __iomem *base_addr; - int channel, chip, index; - unsigned long tmp; + struct cyclades_card *card = info->card; - card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - - tmp = readb(base_addr + (CyCOR3 << index)) & CyREC_FIFO; + u8 tmp = cyy_readb(info, CyCOR3) & CyREC_FIFO; return put_user(tmp, value); } return 0; @@ -2738,21 +2658,12 @@ static int get_threshold(struct cyclades_port *info, static int set_timeout(struct cyclades_port *info, unsigned long value) { - struct cyclades_card *card; - void __iomem *base_addr; - int channel, chip, index; + struct cyclades_card *card = info->card; unsigned long flags; - card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - spin_lock_irqsave(&card->card_lock, flags); - cy_writeb(base_addr + (CyRTPR << index), value & 0xff); + cyy_writeb(info, CyRTPR, value & 0xff); spin_unlock_irqrestore(&card->card_lock, flags); } return 0; @@ -2761,20 +2672,10 @@ static int set_timeout(struct cyclades_port *info, unsigned long value) static int get_timeout(struct cyclades_port *info, unsigned long __user *value) { - struct cyclades_card *card; - void __iomem *base_addr; - int channel, chip, index; - unsigned long tmp; + struct cyclades_card *card = info->card; - card = info->card; - channel = info->line - card->first_line; if (!cy_is_Z(card)) { - chip = channel >> 2; - channel &= 0x03; - index = card->bus_index; - base_addr = card->base_addr + (cy_chip_offset[chip] << index); - - tmp = readb(base_addr + (CyRTPR << index)); + u8 tmp = cyy_readb(info, CyRTPR); return put_user(tmp, value); } return 0; @@ -3101,8 +3002,7 @@ static void cy_stop(struct tty_struct *tty) { struct cyclades_card *cinfo; struct cyclades_port *info = tty->driver_data; - void __iomem *base_addr; - int chip, channel, index; + int channel; unsigned long flags; #ifdef CY_DEBUG_OTHER @@ -3115,16 +3015,9 @@ static void cy_stop(struct tty_struct *tty) cinfo = info->card; channel = info->line - cinfo->first_line; if (!cy_is_Z(cinfo)) { - index = cinfo->bus_index; - chip = channel >> 2; - channel &= 0x03; - base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); - spin_lock_irqsave(&cinfo->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char)(channel & 0x0003)); /* index channel */ - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) & ~CyTxRdy); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) & ~CyTxRdy); spin_unlock_irqrestore(&cinfo->card_lock, flags); } } /* cy_stop */ @@ -3133,8 +3026,7 @@ static void cy_start(struct tty_struct *tty) { struct cyclades_card *cinfo; struct cyclades_port *info = tty->driver_data; - void __iomem *base_addr; - int chip, channel, index; + int channel; unsigned long flags; #ifdef CY_DEBUG_OTHER @@ -3146,17 +3038,10 @@ static void cy_start(struct tty_struct *tty) cinfo = info->card; channel = info->line - cinfo->first_line; - index = cinfo->bus_index; if (!cy_is_Z(cinfo)) { - chip = channel >> 2; - channel &= 0x03; - base_addr = cinfo->base_addr + (cy_chip_offset[chip] << index); - spin_lock_irqsave(&cinfo->card_lock, flags); - cy_writeb(base_addr + (CyCAR << index), - (u_char) (channel & 0x0003)); /* index channel */ - cy_writeb(base_addr + (CySRER << index), - readb(base_addr + (CySRER << index)) | CyTxRdy); + cyy_writeb(info, CyCAR, channel & 0x03); + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyTxRdy); spin_unlock_irqrestore(&cinfo->card_lock, flags); } } /* cy_start */ @@ -3185,18 +3070,13 @@ static int cyy_carrier_raised(struct tty_port *port) struct cyclades_port *info = container_of(port, struct cyclades_port, port); struct cyclades_card *cinfo = info->card; - void __iomem *base = cinfo->base_addr; unsigned long flags; int channel = info->line - cinfo->first_line; - int chip = channel >> 2, index = cinfo->bus_index; u32 cd; - channel &= 0x03; - base += cy_chip_offset[chip] << index; - spin_lock_irqsave(&cinfo->card_lock, flags); - cy_writeb(base + (CyCAR << index), (u8)channel); - cd = readb(base + (CyMSVR1 << index)) & CyDCD; + cyy_writeb(info, CyCAR, channel & 0x03); + cd = cyy_readb(info, CyMSVR1) & CyDCD; spin_unlock_irqrestore(&cinfo->card_lock, flags); return cd; @@ -3326,9 +3206,9 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) info->cor3 = 0x08; /* _very_ small rcv threshold */ chip_number = channel / CyPORTS_PER_CHIP; - info->chip_rev = readb(cinfo->base_addr + - (cy_chip_offset[chip_number] << index) + - (CyGFRCR << index)); + info->u.cyy.base_addr = cinfo->base_addr + + (cy_chip_offset[chip_number] << index); + info->chip_rev = cyy_readb(info, CyGFRCR); if (info->chip_rev >= CD1400_REV_J) { /* It is a CD1400 rev. J or later */ diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index 1eb87a6a2f6b..bbebef7713b3 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -544,7 +544,7 @@ struct cyclades_port { struct cyclades_card *card; union { struct { - int filler; + void __iomem *base_addr; } cyy; struct { struct CH_CTRL __iomem *ch_ctrl; -- cgit v1.2.3 From 46fb782522092f772c6ce2b129f201ca6e1e15a2 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Sep 2009 13:13:17 -0700 Subject: cyclades: remove more duplicated code Remove duplicated code from cy_set_line_char. There were 2 if branches with same contents except flags. Branch only for the flags computation and use them in the only copy of the code. Signed-off-by: Jiri Slaby Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index cf2874a89c8c..f518e0b5b47d 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -2057,6 +2057,8 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) channel = info->line - card->first_line; if (!cy_is_Z(card)) { + u32 cflags; + /* baud rate */ baud = tty_get_baud_rate(tty); if (baud == 38400 && (info->port.flags & ASYNC_SPD_MASK) == @@ -2198,37 +2200,18 @@ static void cy_set_line_char(struct cyclades_port *info, struct tty_struct *tty) (info->default_timeout ? info->default_timeout : 0x02)); /* 10ms rx timeout */ - if (C_CLOCAL(tty)) { - /* without modem intr */ - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) | CyMdmCh); - /* act on 1->0 modem transitions */ - if ((cflag & CRTSCTS) && info->rflow) { - cyy_writeb(info, CyMCOR1, - (CyCTS | rflow_thr[i])); - } else { - cyy_writeb(info, CyMCOR1, - CyCTS); - } - /* act on 0->1 modem transitions */ - cyy_writeb(info, CyMCOR2, CyCTS); - } else { - /* without modem intr */ - cyy_writeb(info, CySRER, - cyy_readb(info, CySRER) | CyMdmCh); - /* act on 1->0 modem transitions */ - if ((cflag & CRTSCTS) && info->rflow) { - cyy_writeb(info, CyMCOR1, - (CyDSR | CyCTS | CyRI | CyDCD | - rflow_thr[i])); - } else { - cyy_writeb(info, CyMCOR1, - CyDSR | CyCTS | CyRI | CyDCD); - } - /* act on 0->1 modem transitions */ - cyy_writeb(info, CyMCOR2, - CyDSR | CyCTS | CyRI | CyDCD); - } + cflags = CyCTS; + if (!C_CLOCAL(tty)) + cflags |= CyDSR | CyRI | CyDCD; + /* without modem intr */ + cyy_writeb(info, CySRER, cyy_readb(info, CySRER) | CyMdmCh); + /* act on 1->0 modem transitions */ + if ((cflag & CRTSCTS) && info->rflow) + cyy_writeb(info, CyMCOR1, cflags | rflow_thr[i]); + else + cyy_writeb(info, CyMCOR1, cflags); + /* act on 0->1 modem transitions */ + cyy_writeb(info, CyMCOR2, cflags); if (i == 0) /* baud rate is zero, turn off line */ cyy_change_rts_dtr(info, 0, TIOCM_DTR); -- cgit v1.2.3 From 6146b9af84cc771198195104b432eb13b96a9799 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:19 -0700 Subject: tty: Fix a typo noted in passing Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index a3afa0c387cd..938448067738 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -2085,7 +2085,7 @@ static int tioccons(struct file *file) * the generic functionality existed. This piece of history is preserved * in the expected tty API of posix OS's. * - * Locking: none, the open fle handle ensures it won't go away. + * Locking: none, the open file handle ensures it won't go away. */ static int fionbio(struct file *file, int __user *p) -- cgit v1.2.3 From 7ca0ff9ab3218ec443a7a9ad247e4650373ed41e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:20 -0700 Subject: tty: Add a full port_close function Now we are extracting out methods for shutdown and the like we can add a proper tty_port_close method that knows all the innards of the tty closing process and hides the lot from the caller. At some point in the future this will be paired with a similar open() helper and the drivers can stick to hardware management. Signed-off-by: Alan Cox Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_port.c | 29 +++++++++++++++++++++++++++-- include/linux/tty.h | 8 +++++++- 2 files changed, 34 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 9769b1149f76..549bd0fa8bb6 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -96,6 +96,14 @@ void tty_port_tty_set(struct tty_port *port, struct tty_struct *tty) } EXPORT_SYMBOL(tty_port_tty_set); +static void tty_port_shutdown(struct tty_port *port) +{ + if (port->ops->shutdown && + test_and_clear_bit(ASYNC_INITIALIZED, &port->flags)) + port->ops->shutdown(port); + +} + /** * tty_port_hangup - hangup helper * @port: tty port @@ -116,6 +124,7 @@ void tty_port_hangup(struct tty_port *port) port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); wake_up_interruptible(&port->open_wait); + tty_port_shutdown(port); } EXPORT_SYMBOL(tty_port_hangup); @@ -296,15 +305,17 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f if (port->count) { spin_unlock_irqrestore(&port->lock, flags); + if (port->ops->drop) + port->ops->drop(port); return 0; } - port->flags |= ASYNC_CLOSING; + set_bit(ASYNC_CLOSING, &port->flags); tty->closing = 1; spin_unlock_irqrestore(&port->lock, flags); /* Don't block on a stalled port, just pull the chain */ if (tty->flow_stopped) tty_driver_flush_buffer(tty); - if (port->flags & ASYNC_INITIALIZED && + if (test_bit(ASYNCB_INITIALIZED, &port->flags) && port->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, port->closing_wait); if (port->drain_delay) { @@ -318,6 +329,9 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f timeout = 2 * HZ; schedule_timeout_interruptible(timeout); } + /* Don't call port->drop for the last reference. Callers will want + to drop the last active reference in ->shutdown() or the tty + shutdown path */ return 1; } EXPORT_SYMBOL(tty_port_close_start); @@ -348,3 +362,14 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty) spin_unlock_irqrestore(&port->lock, flags); } EXPORT_SYMBOL(tty_port_close_end); + +void tty_port_close(struct tty_port *port, struct tty_struct *tty, + struct file *filp) +{ + if (tty_port_close_start(port, tty, filp) == 0) + return; + tty_port_shutdown(port); + tty_port_close_end(port, tty); + tty_port_tty_set(port, NULL); +} +EXPORT_SYMBOL(tty_port_close); diff --git a/include/linux/tty.h b/include/linux/tty.h index a916a318004e..ecb3d1ba3017 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -187,7 +187,12 @@ struct tty_port; struct tty_port_operations { /* Return 1 if the carrier is raised */ int (*carrier_raised)(struct tty_port *port); + /* Control the DTR line */ void (*dtr_rts)(struct tty_port *port, int raise); + /* Called when the last close completes or a hangup finishes + IFF the port was initialized. Do not use to free resources */ + void (*shutdown)(struct tty_port *port); + void (*drop)(struct tty_port *port); }; struct tty_port { @@ -459,7 +464,8 @@ extern int tty_port_block_til_ready(struct tty_port *port, extern int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp); extern void tty_port_close_end(struct tty_port *port, struct tty_struct *tty); - +extern void tty_port_close(struct tty_port *port, + struct tty_struct *tty, struct file *filp); extern int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); extern int tty_unregister_ldisc(int disc); extern int tty_set_ldisc(struct tty_struct *tty, int ldisc); -- cgit v1.2.3 From c146942573c84b16ccb480a9cfa885a7581585a8 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:21 -0700 Subject: riscom8: split open and close methods up Moving towards a tty_port method for open/close Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/riscom8.c | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 171711acf5cd..949999120258 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -921,20 +921,12 @@ static void rc_flush_buffer(struct tty_struct *tty) tty_wakeup(tty); } -static void rc_close(struct tty_struct *tty, struct file *filp) +static void rc_close_port(struct tty_struct *tty, struct tty_port *port) { - struct riscom_port *port = tty->driver_data; - struct riscom_board *bp; unsigned long flags; + struct riscom_port *rp = container_of(port, struct riscom_port, port); + struct riscom_board *bp = port_Board(rp); unsigned long timeout; - - if (!port || rc_paranoia_check(port, tty->name, "close")) - return; - - bp = port_Board(port); - - if (tty_port_close_start(&port->port, tty, filp) == 0) - return; /* * At this point we stop accepting input. To do this, we @@ -944,29 +936,42 @@ static void rc_close(struct tty_struct *tty, struct file *filp) */ spin_lock_irqsave(&riscom_lock, flags); - port->IER &= ~IER_RXD; - if (port->port.flags & ASYNC_INITIALIZED) { - port->IER &= ~IER_TXRDY; - port->IER |= IER_TXEMPTY; - rc_out(bp, CD180_CAR, port_No(port)); - rc_out(bp, CD180_IER, port->IER); + rp->IER &= ~IER_RXD; + if (port->flags & ASYNC_INITIALIZED) { + rp->IER &= ~IER_TXRDY; + rp->IER |= IER_TXEMPTY; + rc_out(bp, CD180_CAR, port_No(rp)); + rc_out(bp, CD180_IER, rp->IER); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ timeout = jiffies + HZ; - while (port->IER & IER_TXEMPTY) { + while (rp->IER & IER_TXEMPTY) { spin_unlock_irqrestore(&riscom_lock, flags); - msleep_interruptible(jiffies_to_msecs(port->timeout)); + msleep_interruptible(jiffies_to_msecs(rp->timeout)); spin_lock_irqsave(&riscom_lock, flags); if (time_after(jiffies, timeout)) break; } } - rc_shutdown_port(tty, bp, port); - rc_flush_buffer(tty); + rc_shutdown_port(tty, bp, rp); spin_unlock_irqrestore(&riscom_lock, flags); +} + +static void rc_close(struct tty_struct *tty, struct file *filp) +{ + struct riscom_port *port = tty->driver_data; + + if (!port || rc_paranoia_check(port, tty->name, "close")) + return; + + if (tty_port_close_start(&port->port, tty, filp) == 0) + return; + + rc_close_port(tty, &port->port); + rc_flush_buffer(tty); tty_port_close_end(&port->port, tty); } -- cgit v1.2.3 From 1e2b025453d45d35b07d8105bea7fc9d339ff562 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:21 -0700 Subject: mxser: Split close ready for a standard tty_port_close method Prepare for the tty_port_close function by splitting out methods Signed-off-by: Alan Cox --- drivers/char/mxser.c | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index dbf8d52f31d0..30544ca5e956 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -1073,34 +1073,17 @@ static void mxser_flush_buffer(struct tty_struct *tty) } -/* - * This routine is called when the serial port gets closed. First, we - * wait for the last remaining data to be sent. Then, we unlink its - * async structure from the interrupt chain if necessary, and we free - * that IRQ if nothing is left in the chain. - */ -static void mxser_close(struct tty_struct *tty, struct file *filp) +static void mxser_close_port(struct tty_struct *tty, struct tty_port *port) { - struct mxser_port *info = tty->driver_data; - struct tty_port *port = &info->port; - + struct mxser_port *info = container_of(port, struct mxser_port, port); unsigned long timeout; - - if (tty->index == MXSER_PORTS) - return; - if (!info) - return; - - if (tty_port_close_start(port, tty, filp) == 0) - return; - /* * Save the termios structure, since this port may have * separate termios for callout and dialin. * * FIXME: Can this go ? */ - if (info->port.flags & ASYNC_NORMAL_ACTIVE) + if (port->flags & ASYNC_NORMAL_ACTIVE) info->normal_termios = *tty->termios; /* * At this point we stop accepting input. To do this, we @@ -1112,7 +1095,7 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) if (info->board->chip_flag) info->IER &= ~MOXA_MUST_RECV_ISR; - if (info->port.flags & ASYNC_INITIALIZED) { + if (port->flags & ASYNC_INITIALIZED) { outb(info->IER, info->ioaddr + UART_IER); /* * Before we drop DTR, make sure the UART transmitter @@ -1127,8 +1110,26 @@ static void mxser_close(struct tty_struct *tty, struct file *filp) } } mxser_shutdown(tty); - mxser_flush_buffer(tty); +} + +/* + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * async structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + */ +static void mxser_close(struct tty_struct *tty, struct file *filp) +{ + struct mxser_port *info = tty->driver_data; + struct tty_port *port = &info->port; + + if (tty->index == MXSER_PORTS) + return; + if (tty_port_close_start(port, tty, filp) == 0) + return; + mxser_close_port(tty, port); + mxser_flush_buffer(tty); /* Right now the tty_port set is done outside of the close_end helper as we don't yet have everyone using refcounts */ tty_port_close_end(port, tty); -- cgit v1.2.3 From 6f6412b4c76441f2060e580b8d5cbda07dabde37 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:21 -0700 Subject: isicom: Split the close hardware bits out Start to extract and build a model for a common tty_port_close() Signed-off-by: Alan Cox --- drivers/char/isicom.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 4f1f4cd670da..08f574333a57 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -952,19 +952,12 @@ static void isicom_flush_buffer(struct tty_struct *tty) tty_wakeup(tty); } -static void isicom_close(struct tty_struct *tty, struct file *filp) +static void isicom_close_port(struct tty_port *port) { - struct isi_port *ip = tty->driver_data; - struct tty_port *port = &ip->port; - struct isi_board *card; + struct isi_port *ip = container_of(port, struct isi_port, port); + struct isi_board *card = ip->card; unsigned long flags; - BUG_ON(!ip); - - card = ip->card; - if (isicom_paranoia_check(ip, tty->name, "isicom_close")) - return; - /* indicate to the card that no more data can be received on this port */ spin_lock_irqsave(&card->card_lock, flags); @@ -974,9 +967,19 @@ static void isicom_close(struct tty_struct *tty, struct file *filp) } isicom_shutdown_port(ip); spin_unlock_irqrestore(&card->card_lock, flags); +} +static void isicom_close(struct tty_struct *tty, struct file *filp) +{ + struct isi_port *ip = tty->driver_data; + struct tty_port *port = &ip->port; + if (isicom_paranoia_check(ip, tty->name, "isicom_close")) + return; + + if (tty_port_close_start(port, tty, filp) == 0) + return; + isicom_close_port(port); isicom_flush_buffer(tty); - tty_port_close_end(port, tty); } -- cgit v1.2.3 From 6ff1ab28a2b0a8d863c8e0d4af79a758697f6ecc Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:22 -0700 Subject: tty: riscom8 kref and tty_port_close We need to kref this driver in order to use port_close Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/riscom8.c | 131 ++++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 72 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 949999120258..7b5623b01903 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -343,7 +343,7 @@ static void rc_receive_exc(struct riscom_board const *bp) if (port == NULL) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); #ifdef RC_REPORT_OVERRUN status = rc_in(bp, CD180_RCSR); @@ -355,18 +355,18 @@ static void rc_receive_exc(struct riscom_board const *bp) #endif ch = rc_in(bp, CD180_RDR); if (!status) - return; + goto out; if (status & RCSR_TOUT) { printk(KERN_WARNING "rc%d: port %d: Receiver timeout. " "Hardware problems ?\n", board_No(bp), port_No(port)); - return; + goto out; } else if (status & RCSR_BREAK) { printk(KERN_INFO "rc%d: port %d: Handling break...\n", board_No(bp), port_No(port)); flag = TTY_BREAK; - if (port->port.flags & ASYNC_SAK) + if (tty && (port->port.flags & ASYNC_SAK)) do_SAK(tty); } else if (status & RCSR_PE) @@ -380,8 +380,12 @@ static void rc_receive_exc(struct riscom_board const *bp) else flag = TTY_NORMAL; - tty_insert_flip_char(tty, ch, flag); - tty_flip_buffer_push(tty); + if (tty) { + tty_insert_flip_char(tty, ch, flag); + tty_flip_buffer_push(tty); + } +out: + tty_kref_put(tty); } static void rc_receive(struct riscom_board const *bp) @@ -394,7 +398,7 @@ static void rc_receive(struct riscom_board const *bp) if (port == NULL) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); count = rc_in(bp, CD180_RDCR); @@ -403,15 +407,14 @@ static void rc_receive(struct riscom_board const *bp) #endif while (count--) { - if (tty_buffer_request_room(tty, 1) == 0) { - printk(KERN_WARNING "rc%d: port %d: Working around " - "flip buffer overflow.\n", - board_No(bp), port_No(port)); - break; - } - tty_insert_flip_char(tty, rc_in(bp, CD180_RDR), TTY_NORMAL); + u8 ch = rc_in(bp, CD180_RDR); + if (tty) + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } + if (tty) { + tty_flip_buffer_push(tty); + tty_kref_put(tty); } - tty_flip_buffer_push(tty); } static void rc_transmit(struct riscom_board const *bp) @@ -424,22 +427,22 @@ static void rc_transmit(struct riscom_board const *bp) if (port == NULL) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); if (port->IER & IER_TXEMPTY) { /* FIFO drained */ rc_out(bp, CD180_CAR, port_No(port)); port->IER &= ~IER_TXEMPTY; rc_out(bp, CD180_IER, port->IER); - return; + goto out; } if ((port->xmit_cnt <= 0 && !port->break_length) - || tty->stopped || tty->hw_stopped) { + || (tty && (tty->stopped || tty->hw_stopped))) { rc_out(bp, CD180_CAR, port_No(port)); port->IER &= ~IER_TXRDY; rc_out(bp, CD180_IER, port->IER); - return; + goto out; } if (port->break_length) { @@ -480,8 +483,10 @@ static void rc_transmit(struct riscom_board const *bp) port->IER &= ~IER_TXRDY; rc_out(bp, CD180_IER, port->IER); } - if (port->xmit_cnt <= port->wakeup_chars) + if (tty && port->xmit_cnt <= port->wakeup_chars) tty_wakeup(tty); +out: + tty_kref_put(tty); } static void rc_check_modem(struct riscom_board const *bp) @@ -494,37 +499,43 @@ static void rc_check_modem(struct riscom_board const *bp) if (port == NULL) return; - tty = port->port.tty; + tty = tty_port_tty_get(&port->port); mcr = rc_in(bp, CD180_MCR); if (mcr & MCR_CDCHG) { if (rc_in(bp, CD180_MSVR) & MSVR_CD) wake_up_interruptible(&port->port.open_wait); - else + else if (tty) tty_hangup(tty); } #ifdef RISCOM_BRAIN_DAMAGED_CTS if (mcr & MCR_CTSCHG) { if (rc_in(bp, CD180_MSVR) & MSVR_CTS) { - tty->hw_stopped = 0; port->IER |= IER_TXRDY; - if (port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); + if (tty) { + tty->hw_stopped = 0; + if (port->xmit_cnt <= port->wakeup_chars) + tty_wakeup(tty); + } } else { - tty->hw_stopped = 1; + if (tty) + tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; } rc_out(bp, CD180_IER, port->IER); } if (mcr & MCR_DSRCHG) { if (rc_in(bp, CD180_MSVR) & MSVR_DSR) { - tty->hw_stopped = 0; port->IER |= IER_TXRDY; - if (port->xmit_cnt <= port->wakeup_chars) - tty_wakeup(tty); + if (tty) { + tty->hw_stopped = 0; + if (port->xmit_cnt <= port->wakeup_chars) + tty_wakeup(tty); + } } else { - tty->hw_stopped = 1; + if (tty) + tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; } rc_out(bp, CD180_IER, port->IER); @@ -533,6 +544,7 @@ static void rc_check_modem(struct riscom_board const *bp) /* Clear change bits */ rc_out(bp, CD180_MCR, 0); + tty_kref_put(tty); } /* The main interrupt processing routine */ @@ -632,9 +644,9 @@ static void rc_shutdown_board(struct riscom_board *bp) * Setting up port characteristics. * Must be called with disabled interrupts */ -static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) +static void rc_change_speed(struct tty_struct *tty, struct riscom_board *bp, + struct riscom_port *port) { - struct tty_struct *tty = port->port.tty; unsigned long baud; long tmp; unsigned char cor1 = 0, cor3 = 0; @@ -781,7 +793,8 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port) } /* Must be called with interrupts enabled */ -static int rc_setup_port(struct riscom_board *bp, struct riscom_port *port) +static int rc_setup_port(struct tty_struct *tty, struct riscom_board *bp, + struct riscom_port *port) { unsigned long flags; @@ -793,11 +806,11 @@ static int rc_setup_port(struct riscom_board *bp, struct riscom_port *port) spin_lock_irqsave(&riscom_lock, flags); - clear_bit(TTY_IO_ERROR, &port->port.tty->flags); + clear_bit(TTY_IO_ERROR, &tty->flags); if (port->port.count == 1) bp->count++; port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - rc_change_speed(bp, port); + rc_change_speed(tty, bp, port); port->port.flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore(&riscom_lock, flags); @@ -898,9 +911,9 @@ static int rc_open(struct tty_struct *tty, struct file *filp) port->port.count++; tty->driver_data = port; - port->port.tty = tty; + tty_port_tty_set(&port->port, tty); - error = rc_setup_port(bp, port); + error = rc_setup_port(tty, bp, port); if (error == 0) error = tty_port_block_til_ready(&port->port, tty, filp); return error; @@ -921,7 +934,7 @@ static void rc_flush_buffer(struct tty_struct *tty) tty_wakeup(tty); } -static void rc_close_port(struct tty_struct *tty, struct tty_port *port) +static void rc_close_port(struct tty_port *port, struct tty_struct *tty) { unsigned long flags; struct riscom_port *rp = container_of(port, struct riscom_port, port); @@ -966,14 +979,7 @@ static void rc_close(struct tty_struct *tty, struct file *filp) if (!port || rc_paranoia_check(port, tty->name, "close")) return; - - if (tty_port_close_start(&port->port, tty, filp) == 0) - return; - - rc_close_port(tty, &port->port); - rc_flush_buffer(tty); - - tty_port_close_end(&port->port, tty); + tty_port_close(&port->port, tty, filp); } static int rc_write(struct tty_struct *tty, @@ -1175,7 +1181,7 @@ static int rc_send_break(struct tty_struct *tty, int length) return 0; } -static int rc_set_serial_info(struct riscom_port *port, +static int rc_set_serial_info(struct tty_struct *tty, struct riscom_port *port, struct serial_struct __user *newinfo) { struct serial_struct tmp; @@ -1185,17 +1191,6 @@ static int rc_set_serial_info(struct riscom_port *port, if (copy_from_user(&tmp, newinfo, sizeof(tmp))) return -EFAULT; -#if 0 - if ((tmp.irq != bp->irq) || - (tmp.port != bp->base) || - (tmp.type != PORT_CIRRUS) || - (tmp.baud_base != (RC_OSCFREQ + CD180_TPC/2) / CD180_TPC) || - (tmp.custom_divisor != 0) || - (tmp.xmit_fifo_size != CD180_NFIFO) || - (tmp.flags & ~RISCOM_LEGAL_FLAGS)) - return -EINVAL; -#endif - change_speed = ((port->port.flags & ASYNC_SPD_MASK) != (tmp.flags & ASYNC_SPD_MASK)); @@ -1217,7 +1212,7 @@ static int rc_set_serial_info(struct riscom_port *port, unsigned long flags; spin_lock_irqsave(&riscom_lock, flags); - rc_change_speed(bp, port); + rc_change_speed(tty, bp, port); spin_unlock_irqrestore(&riscom_lock, flags); } return 0; @@ -1260,7 +1255,7 @@ static int rc_ioctl(struct tty_struct *tty, struct file *filp, break; case TIOCSSERIAL: lock_kernel(); - retval = rc_set_serial_info(port, argp); + retval = rc_set_serial_info(tty, port, argp); unlock_kernel(); break; default: @@ -1355,21 +1350,12 @@ static void rc_start(struct tty_struct *tty) static void rc_hangup(struct tty_struct *tty) { struct riscom_port *port = tty->driver_data; - struct riscom_board *bp; - unsigned long flags; if (rc_paranoia_check(port, tty->name, "rc_hangup")) return; - bp = port_Board(port); - - rc_shutdown_port(tty, bp, port); - spin_lock_irqsave(&port->port.lock, flags); - port->port.count = 0; - port->port.flags &= ~ASYNC_NORMAL_ACTIVE; - port->port.tty = NULL; - wake_up_interruptible(&port->port.open_wait); - spin_unlock_irqrestore(&port->port.lock, flags); + rc_shutdown_port(tty, port_Board(port), port); + tty_port_hangup(&port->port); } static void rc_set_termios(struct tty_struct *tty, @@ -1382,7 +1368,7 @@ static void rc_set_termios(struct tty_struct *tty, return; spin_lock_irqsave(&riscom_lock, flags); - rc_change_speed(port_Board(port), port); + rc_change_speed(tty, port_Board(port), port); spin_unlock_irqrestore(&riscom_lock, flags); if ((old_termios->c_cflag & CRTSCTS) && @@ -1415,6 +1401,7 @@ static const struct tty_operations riscom_ops = { static const struct tty_port_operations riscom_port_ops = { .carrier_raised = carrier_raised, + .shutdown = rc_close_port, }; -- cgit v1.2.3 From e936ffd5cb2b4c7ee04925c9b92b616c01f1e022 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:22 -0700 Subject: cyclades: use the full port_close function Convert cyclades to use the full tty_port_close helper Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/cyclades.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index f518e0b5b47d..70bd61b2a7d7 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -1750,24 +1750,15 @@ static void cy_flush_buffer(struct tty_struct *tty) } /* cy_flush_buffer */ -/* - * This routine is called when a particular tty device is closed. - */ -static void cy_close(struct tty_struct *tty, struct file *filp) +static void cy_do_close(struct tty_port *port) { - struct cyclades_port *info = tty->driver_data; + struct cyclades_port *info = container_of(port, struct cyclades_port, + port); struct cyclades_card *card; unsigned long flags; int channel; - if (!info || serial_paranoia_check(info, tty->name, "cy_close")) - return; - card = info->card; - - if (!tty_port_close_start(&info->port, tty, filp)) - return; - channel = info->line - card->first_line; spin_lock_irqsave(&card->card_lock, flags); @@ -1779,7 +1770,7 @@ static void cy_close(struct tty_struct *tty, struct file *filp) /* Waiting for on-board buffers to be empty before closing the port */ spin_unlock_irqrestore(&card->card_lock, flags); - cy_wait_until_sent(tty, info->timeout); + cy_wait_until_sent(port->tty, info->timeout); spin_lock_irqsave(&card->card_lock, flags); } } else { @@ -1801,14 +1792,19 @@ static void cy_close(struct tty_struct *tty, struct file *filp) } #endif } - spin_unlock_irqrestore(&card->card_lock, flags); - cy_shutdown(info, tty); - cy_flush_buffer(tty); - - tty_port_tty_set(&info->port, NULL); + cy_shutdown(info, port->tty); +} - tty_port_close_end(&info->port, tty); +/* + * This routine is called when a particular tty device is closed. + */ +static void cy_close(struct tty_struct *tty, struct file *filp) +{ + struct cyclades_port *info = tty->driver_data; + if (!info || serial_paranoia_check(info, tty->name, "cy_close")) + return; + tty_port_close(&info->port, tty, filp); } /* cy_close */ /* This routine gets called when tty_write has put something into @@ -3113,11 +3109,13 @@ static void cyz_dtr_rts(struct tty_port *port, int raise) static const struct tty_port_operations cyy_port_ops = { .carrier_raised = cyy_carrier_raised, .dtr_rts = cyy_dtr_rts, + .shutdown = cy_do_close, }; static const struct tty_port_operations cyz_port_ops = { .carrier_raised = cyz_carrier_raised, .dtr_rts = cyz_dtr_rts, + .shutdown = cy_do_close, }; /* -- cgit v1.2.3 From b50989dc444599c8b21edc23536fc305f4e9b7d5 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:22 -0700 Subject: tty: make the kref destructor occur asynchronously We want to be able to sleep in the destructor for USB at least. It isn't a hot path so just pushing it to a work queue doesn't really cause any difficulty. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_io.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 938448067738..385cca7074da 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1386,10 +1386,14 @@ EXPORT_SYMBOL(tty_shutdown); * tty_mutex - sometimes only * takes the file list lock internally when working on the list * of ttys that the driver keeps. + * + * This method gets called from a work queue so that the driver private + * shutdown ops can sleep (needed for USB at least) */ -static void release_one_tty(struct kref *kref) +static void release_one_tty(struct work_struct *work) { - struct tty_struct *tty = container_of(kref, struct tty_struct, kref); + struct tty_struct *tty = + container_of(work, struct tty_struct, hangup_work); struct tty_driver *driver = tty->driver; if (tty->ops->shutdown) @@ -1407,6 +1411,15 @@ static void release_one_tty(struct kref *kref) free_tty_struct(tty); } +static void queue_release_one_tty(struct kref *kref) +{ + struct tty_struct *tty = container_of(kref, struct tty_struct, kref); + /* The hangup queue is now free so we can reuse it rather than + waste a chunk of memory for each port */ + INIT_WORK(&tty->hangup_work, release_one_tty); + schedule_work(&tty->hangup_work); +} + /** * tty_kref_put - release a tty kref * @tty: tty device @@ -1418,7 +1431,7 @@ static void release_one_tty(struct kref *kref) void tty_kref_put(struct tty_struct *tty) { if (tty) - kref_put(&tty->kref, release_one_tty); + kref_put(&tty->kref, queue_release_one_tty); } EXPORT_SYMBOL(tty_kref_put); -- cgit v1.2.3 From 8b92e87d39bfd046e7581e1fe0f40eac40f88608 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:24 -0700 Subject: vt: add an event interface This is needed and requested in various forms for ConsoleKit, screenblank handling and the like so do the job with a single interface. Also build the interface so that unlike VT_WAITACTIVE and friends it won't miss events. FIXME: Should this be a waitactive ioctl or a new device file you can poll and read events from. We need the code anyway to fix up the existing broken wait for console switch logic but the ConsoleKit people would prefer the new device to the ioctl we have here Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/vt.c | 4 +- drivers/char/vt_ioctl.c | 185 +++++++++++++++++++++++++++++++++++------------- include/linux/vt.h | 14 ++++ include/linux/vt_kern.h | 3 +- 4 files changed, 154 insertions(+), 52 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/vt.c b/drivers/char/vt.c index e47a4c88976b..33214d92ca4c 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -252,7 +252,6 @@ static void notify_update(struct vc_data *vc) struct vt_notifier_param param = { .vc = vc }; atomic_notifier_call_chain(&vt_notifier_list, VT_UPDATE, ¶m); } - /* * Low-Level Functions */ @@ -935,6 +934,7 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, if (CON_IS_VISIBLE(vc)) update_screen(vc); + vt_event_post(VT_EVENT_RESIZE, vc->vc_num, vc->vc_num); return err; } @@ -3637,6 +3637,7 @@ void do_blank_screen(int entering_gfx) blank_state = blank_vesa_wait; mod_timer(&console_timer, jiffies + vesa_off_interval); } + vt_event_post(VT_EVENT_BLANK, vc->vc_num, vc->vc_num); } EXPORT_SYMBOL(do_blank_screen); @@ -3681,6 +3682,7 @@ void do_unblank_screen(int leaving_gfx) console_blank_hook(0); set_palette(vc); set_cursor(vc); + vt_event_post(VT_EVENT_UNBLANK, vc->vc_num, vc->vc_num); } EXPORT_SYMBOL(do_unblank_screen); diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 95189f288f8c..35ad94e65d0d 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -61,6 +61,133 @@ extern struct tty_driver *console_driver; static void complete_change_console(struct vc_data *vc); +/* + * User space VT_EVENT handlers + */ + +struct vt_event_wait { + struct list_head list; + struct vt_event event; + int done; +}; + +static LIST_HEAD(vt_events); +static DEFINE_SPINLOCK(vt_event_lock); +static DECLARE_WAIT_QUEUE_HEAD(vt_event_waitqueue); + +/** + * vt_event_post + * @event: the event that occurred + * @old: old console + * @new: new console + * + * Post an VT event to interested VT handlers + */ + +void vt_event_post(unsigned int event, unsigned int old, unsigned int new) +{ + struct list_head *pos, *head; + unsigned long flags; + int wake = 0; + + spin_lock_irqsave(&vt_event_lock, flags); + head = &vt_events; + + list_for_each(pos, head) { + struct vt_event_wait *ve = list_entry(pos, + struct vt_event_wait, list); + if (!(ve->event.event & event)) + continue; + ve->event.event = event; + /* kernel view is consoles 0..n-1, user space view is + console 1..n with 0 meaning current, so we must bias */ + ve->event.old = old + 1; + ve->event.new = new + 1; + wake = 1; + ve->done = 1; + } + spin_unlock_irqrestore(&vt_event_lock, flags); + if (wake) + wake_up_interruptible(&vt_event_waitqueue); +} + +/** + * vt_event_wait - wait for an event + * @vw: our event + * + * Waits for an event to occur which completes our vt_event_wait + * structure. On return the structure has wv->done set to 1 for success + * or 0 if some event such as a signal ended the wait. + */ + +static void vt_event_wait(struct vt_event_wait *vw) +{ + unsigned long flags; + /* Prepare the event */ + INIT_LIST_HEAD(&vw->list); + vw->done = 0; + /* Queue our event */ + spin_lock_irqsave(&vt_event_lock, flags); + list_add(&vw->list, &vt_events); + spin_unlock_irqrestore(&vt_event_lock, flags); + /* Wait for it to pass */ + wait_event_interruptible(vt_event_waitqueue, vw->done); + /* Dequeue it */ + spin_lock_irqsave(&vt_event_lock, flags); + list_del(&vw->list); + spin_unlock_irqrestore(&vt_event_lock, flags); +} + +/** + * vt_event_wait_ioctl - event ioctl handler + * @arg: argument to ioctl + * + * Implement the VT_WAITEVENT ioctl using the VT event interface + */ + +static int vt_event_wait_ioctl(struct vt_event __user *event) +{ + struct vt_event_wait vw; + + if (copy_from_user(&vw.event, event, sizeof(struct vt_event))) + return -EFAULT; + /* Highest supported event for now */ + if (vw.event.event & ~VT_MAX_EVENT) + return -EINVAL; + + vt_event_wait(&vw); + /* If it occurred report it */ + if (vw.done) { + if (copy_to_user(event, &vw.event, sizeof(struct vt_event))) + return -EFAULT; + return 0; + } + return -EINTR; +} + +/** + * vt_waitactive - active console wait + * @event: event code + * @n: new console + * + * Helper for event waits. Used to implement the legacy + * event waiting ioctls in terms of events + */ + +int vt_waitactive(int n) +{ + struct vt_event_wait vw; + do { + if (n == fg_console + 1) + break; + vw.event.event = VT_EVENT_SWITCH; + vt_event_wait(&vw); + if (vw.done == 0) + return -EINTR; + } while (vw.event.new != n); + return 0; +} + /* * these are the valid i/o ports we're allowed to change. they map all the * video ports @@ -360,6 +487,8 @@ do_unimap_ioctl(int cmd, struct unimapdesc __user *user_ud, int perm, struct vc_ return 0; } + + /* * We handle the console-specific ioctl's here. We allow the * capability to modify any console, not just the fg_console. @@ -851,7 +980,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, if (arg == 0 || arg > MAX_NR_CONSOLES) ret = -ENXIO; else - ret = vt_waitactive(arg - 1); + ret = vt_waitactive(arg); break; /* @@ -1159,6 +1288,9 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, ret = put_user(vc->vc_hi_font_mask, (unsigned short __user *)arg); break; + case VT_WAITEVENT: + ret = vt_event_wait_ioctl((struct vt_event __user *)arg); + break; default: ret = -ENOIOCTLCMD; } @@ -1170,54 +1302,6 @@ eperm: goto out; } -/* - * Sometimes we want to wait until a particular VT has been activated. We - * do it in a very simple manner. Everybody waits on a single queue and - * get woken up at once. Those that are satisfied go on with their business, - * while those not ready go back to sleep. Seems overkill to add a wait - * to each vt just for this - usually this does nothing! - */ -static DECLARE_WAIT_QUEUE_HEAD(vt_activate_queue); - -/* - * Sleeps until a vt is activated, or the task is interrupted. Returns - * 0 if activation, -EINTR if interrupted by a signal handler. - */ -int vt_waitactive(int vt) -{ - int retval; - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&vt_activate_queue, &wait); - for (;;) { - retval = 0; - - /* - * Synchronize with redraw_screen(). By acquiring the console - * semaphore we make sure that the console switch is completed - * before we return. If we didn't wait for the semaphore, we - * could return at a point where fg_console has already been - * updated, but the console switch hasn't been completed. - */ - acquire_console_sem(); - set_current_state(TASK_INTERRUPTIBLE); - if (vt == fg_console) { - release_console_sem(); - break; - } - release_console_sem(); - retval = -ERESTARTNOHAND; - if (signal_pending(current)) - break; - schedule(); - } - remove_wait_queue(&vt_activate_queue, &wait); - __set_current_state(TASK_RUNNING); - return retval; -} - -#define vt_wake_waitactive() wake_up(&vt_activate_queue) - void reset_vc(struct vc_data *vc) { vc->vc_mode = KD_TEXT; @@ -1262,6 +1346,7 @@ void vc_SAK(struct work_struct *work) static void complete_change_console(struct vc_data *vc) { unsigned char old_vc_mode; + int old = fg_console; last_console = fg_console; @@ -1325,7 +1410,7 @@ static void complete_change_console(struct vc_data *vc) /* * Wake anyone waiting for their VT to activate */ - vt_wake_waitactive(); + vt_event_post(VT_EVENT_SWITCH, old, vc->vc_num); return; } diff --git a/include/linux/vt.h b/include/linux/vt.h index 02c1c0288770..89c03a11e193 100644 --- a/include/linux/vt.h +++ b/include/linux/vt.h @@ -74,4 +74,18 @@ struct vt_consize { #define VT_UNLOCKSWITCH 0x560C /* allow vt switching */ #define VT_GETHIFONTMASK 0x560D /* return hi font mask */ +struct vt_event { + unsigned int event; +#define VT_EVENT_SWITCH 0x0001 /* Console switch */ +#define VT_EVENT_BLANK 0x0002 /* Screen blank */ +#define VT_EVENT_UNBLANK 0x0004 /* Screen unblank */ +#define VT_EVENT_RESIZE 0x0008 /* Resize display */ +#define VT_MAX_EVENT 0x000F + unsigned int old; /* Old console */ + unsigned int new; /* New console (if changing) */ + unsigned int pad[4]; /* Padding for expansion */ +}; + +#define VT_WAITEVENT 0x560E /* Wait for an event */ + #endif /* _LINUX_VT_H */ diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index 2f1113467f70..f8c797d57c8b 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -91,7 +91,8 @@ int con_copy_unimap(struct vc_data *dst_vc, struct vc_data *src_vc); #endif /* vt.c */ -int vt_waitactive(int vt); +void vt_event_post(unsigned int event, unsigned int old, unsigned int new); +int vt_waitactive(int n); void change_console(struct vc_data *new_vc); void reset_vc(struct vc_data *vc); extern int unbind_con_driver(const struct consw *csw, int first, int last, -- cgit v1.2.3 From 8d233558cd99a888571bb5a88a74970879e0aba4 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:25 -0700 Subject: vt: remove power stuff from kernel/power In the past someone gratuitiously borrowed chunks of kernel internal vt code and dumped them in kernel/power. They have all sorts of deep relations with the vt code so put them in the vt tree instead Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/vt_ioctl.c | 56 +++++++++++++++++++++++++++++++++++++++++++ include/linux/vt_kern.h | 1 + kernel/power/console.c | 63 ++++--------------------------------------------- 3 files changed, 62 insertions(+), 58 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 35ad94e65d0d..d29fbd44bdca 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -1483,3 +1484,58 @@ void change_console(struct vc_data *new_vc) complete_change_console(new_vc); } + +/* Perform a kernel triggered VT switch for suspend/resume */ + +static int disable_vt_switch; + +int vt_move_to_console(unsigned int vt, int alloc) +{ + int prev; + + acquire_console_sem(); + /* Graphics mode - up to X */ + if (disable_vt_switch) { + release_console_sem(); + return 0; + } + prev = fg_console; + + if (alloc && vc_allocate(vt)) { + /* we can't have a free VC for now. Too bad, + * we don't want to mess the screen for now. */ + release_console_sem(); + return -ENOSPC; + } + + if (set_console(vt)) { + /* + * We're unable to switch to the SUSPEND_CONSOLE. + * Let the calling function know so it can decide + * what to do. + */ + release_console_sem(); + return -EIO; + } + release_console_sem(); + if (vt_waitactive(vt)) { + pr_debug("Suspend: Can't switch VCs."); + return -EINTR; + } + return prev; +} + +/* + * Normally during a suspend, we allocate a new console and switch to it. + * When we resume, we switch back to the original console. This switch + * can be slow, so on systems where the framebuffer can handle restoration + * of video registers anyways, there's little point in doing the console + * switch. This function allows you to disable it by passing it '0'. + */ +void pm_set_vt_switch(int do_switch) +{ + acquire_console_sem(); + disable_vt_switch = !do_switch; + release_console_sem(); +} +EXPORT_SYMBOL(pm_set_vt_switch); diff --git a/include/linux/vt_kern.h b/include/linux/vt_kern.h index f8c797d57c8b..5b53efe41b5c 100644 --- a/include/linux/vt_kern.h +++ b/include/linux/vt_kern.h @@ -117,4 +117,5 @@ struct vt_spawn_console { }; extern struct vt_spawn_console vt_spawn_con; +extern int vt_move_to_console(unsigned int vt, int alloc); #endif /* _VT_KERN_H */ diff --git a/kernel/power/console.c b/kernel/power/console.c index a3961b205de7..5187136fe1de 100644 --- a/kernel/power/console.c +++ b/kernel/power/console.c @@ -14,56 +14,13 @@ #define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1) static int orig_fgconsole, orig_kmsg; -static int disable_vt_switch; - -/* - * Normally during a suspend, we allocate a new console and switch to it. - * When we resume, we switch back to the original console. This switch - * can be slow, so on systems where the framebuffer can handle restoration - * of video registers anyways, there's little point in doing the console - * switch. This function allows you to disable it by passing it '0'. - */ -void pm_set_vt_switch(int do_switch) -{ - acquire_console_sem(); - disable_vt_switch = !do_switch; - release_console_sem(); -} -EXPORT_SYMBOL(pm_set_vt_switch); int pm_prepare_console(void) { - acquire_console_sem(); - - if (disable_vt_switch) { - release_console_sem(); - return 0; - } - - orig_fgconsole = fg_console; - - if (vc_allocate(SUSPEND_CONSOLE)) { - /* we can't have a free VC for now. Too bad, - * we don't want to mess the screen for now. */ - release_console_sem(); + orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1); + if (orig_fgconsole < 0) return 1; - } - if (set_console(SUSPEND_CONSOLE)) { - /* - * We're unable to switch to the SUSPEND_CONSOLE. - * Let the calling function know so it can decide - * what to do. - */ - release_console_sem(); - return 1; - } - release_console_sem(); - - if (vt_waitactive(SUSPEND_CONSOLE)) { - pr_debug("Suspend: Can't switch VCs."); - return 1; - } orig_kmsg = kmsg_redirect; kmsg_redirect = SUSPEND_CONSOLE; return 0; @@ -71,19 +28,9 @@ int pm_prepare_console(void) void pm_restore_console(void) { - acquire_console_sem(); - if (disable_vt_switch) { - release_console_sem(); - return; - } - set_console(orig_fgconsole); - release_console_sem(); - - if (vt_waitactive(orig_fgconsole)) { - pr_debug("Resume: Can't switch VCs."); - return; + if (orig_fgconsole >= 0) { + vt_move_to_console(orig_fgconsole, 0); + kmsg_redirect = orig_kmsg; } - - kmsg_redirect = orig_kmsg; } #endif -- cgit v1.2.3 From d3b5cffcf84a8bdc7073dce4745d67c72629af85 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:26 -0700 Subject: vt: add an activate and lock X and other graphical interfaces need to be able to flip to a console and lock it into graphics mode without races. Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/vt_ioctl.c | 38 +++++++++++++++++++++++++++++++++++++- include/linux/vt.h | 7 +++++++ 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index d29fbd44bdca..0fceb8f4d6f2 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -972,6 +972,41 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, } break; + case VT_SETACTIVATE: + { + struct vt_setactivate vsa; + + if (!perm) + goto eperm; + + if (copy_from_user(&vsa, (struct vt_setactivate __user *)arg, + sizeof(struct vt_setactivate))) + return -EFAULT; + if (vsa.console == 0 || vsa.console > MAX_NR_CONSOLES) + ret = -ENXIO; + else { + vsa.console--; + acquire_console_sem(); + ret = vc_allocate(vsa.console); + if (ret == 0) { + struct vc_data *nvc; + /* This is safe providing we don't drop the + console sem between vc_allocate and + finishing referencing nvc */ + nvc = vc_cons[vsa.console].d; + nvc->vt_mode = vsa.mode; + nvc->vt_mode.frsig = 0; + put_pid(nvc->vt_pid); + nvc->vt_pid = get_pid(task_pid(current)); + } + release_console_sem(); + if (ret) + break; + /* Commence switch and lock */ + set_console(arg); + } + } + /* * wait until the specified VT has been activated */ @@ -1342,7 +1377,8 @@ void vc_SAK(struct work_struct *work) } /* - * Performs the back end of a vt switch + * Performs the back end of a vt switch. Called under the console + * semaphore. */ static void complete_change_console(struct vc_data *vc) { diff --git a/include/linux/vt.h b/include/linux/vt.h index 831daf64b90c..7afca0d72139 100644 --- a/include/linux/vt.h +++ b/include/linux/vt.h @@ -77,4 +77,11 @@ struct vt_event { #define VT_WAITEVENT 0x560E /* Wait for an event */ +struct vt_setactivate { + unsigned int console; + struct vt_mode mode; +}; + +#define VT_SETACTIVATE 0x560F /* Activate and set the mode of a console */ + #endif /* _LINUX_VT_H */ -- cgit v1.2.3 From 11d85d7b2ecc72fe752bba55389e7d11907528af Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:27 -0700 Subject: isicom: split the open method for the isicom device Again moving towards being able to add a common open method Signed-off-by: Alan Cox --- drivers/char/isicom.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 08f574333a57..426bfdd7f3e0 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -846,37 +846,53 @@ static int isicom_carrier_raised(struct tty_port *port) return (ip->status & ISI_DCD)?1 : 0; } -static int isicom_open(struct tty_struct *tty, struct file *filp) +static struct tty_port *isicom_find_port(struct tty_struct *tty) { struct isi_port *port; struct isi_board *card; unsigned int board; - int error, line; + int line = tty->index; - line = tty->index; if (line < 0 || line > PORT_COUNT-1) - return -ENODEV; + return NULL; board = BOARD(line); card = &isi_card[board]; if (!(card->status & FIRMWARE_LOADED)) - return -ENODEV; + return NULL; /* open on a port greater than the port count for the card !!! */ if (line > ((board * 16) + card->port_count - 1)) - return -ENODEV; + return NULL; port = &isi_ports[line]; if (isicom_paranoia_check(port, tty->name, "isicom_open")) - return -ENODEV; + return NULL; + + return &port->port; +} + +static int isicom_open(struct tty_struct *tty, struct file *filp) +{ + struct isi_port *port; + struct isi_board *card; + struct tty_port *tport; + int error = 0; + tport = isicom_find_port(tty); + if (tport == NULL) + return -ENODEV; + port = container_of(tport, struct isi_port, port); + card = &isi_card[BOARD(tty->index)]; isicom_setup_board(card); /* FIXME: locking on port.count etc */ port->port.count++; tty->driver_data = port; tty_port_tty_set(&port->port, tty); - error = isicom_setup_port(tty); + /* FIXME: Locking on Initialized flag */ + if (!test_bit(ASYNCB_INITIALIZED, &tport->flags)) + error = isicom_setup_port(tty); if (error == 0) error = tty_port_block_til_ready(&port->port, tty, filp); return error; -- cgit v1.2.3 From bdc04e3174e18f475289fa8f4144f66686326b7e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:31 -0700 Subject: serial: move delta_msr_wait into the tty_port This is used by various drivers not just serial and can be extracted as commonality Signed-off-by: Alan Cox --- drivers/char/cyclades.c | 9 ++++----- drivers/char/esp.c | 7 +++---- drivers/char/mxser.c | 8 +++----- drivers/char/tty_port.c | 2 ++ drivers/serial/8250.c | 2 +- drivers/serial/amba-pl010.c | 2 +- drivers/serial/amba-pl011.c | 2 +- drivers/serial/atmel_serial.c | 2 +- drivers/serial/icom.c | 2 +- drivers/serial/imx.c | 4 ++-- drivers/serial/ioc3_serial.c | 8 ++++---- drivers/serial/ioc4_serial.c | 8 ++++---- drivers/serial/ip22zilog.c | 2 +- drivers/serial/msm_serial.c | 2 +- drivers/serial/pmac_zilog.c | 2 +- drivers/serial/pnx8xxx_uart.c | 2 +- drivers/serial/pxa.c | 2 +- drivers/serial/sa1100.c | 2 +- drivers/serial/sb1250-duart.c | 2 +- drivers/serial/serial_core.c | 18 +++++++++++------- drivers/serial/serial_ks8695.c | 2 +- drivers/serial/serial_lh7a40x.c | 2 +- drivers/serial/sunsab.c | 2 +- drivers/serial/sunsu.c | 2 +- drivers/serial/sunzilog.c | 2 +- drivers/serial/timbuart.c | 2 +- drivers/serial/vr41xx_siu.c | 2 +- drivers/serial/zs.c | 2 +- include/linux/cyclades.h | 1 - include/linux/hayesesp.h | 1 - include/linux/serial_core.h | 1 - include/linux/tty.h | 1 + 32 files changed, 54 insertions(+), 54 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 70bd61b2a7d7..df5038bbcbc2 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -729,7 +729,7 @@ static void cyy_chip_modem(struct cyclades_card *cinfo, int chip, if (mdm_change & CyRI) info->icount.rng++; - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); } if ((mdm_change & CyDCD) && (info->port.flags & ASYNC_CHECK_CD)) { @@ -1197,7 +1197,7 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo) break; } if (delta_count) - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); if (special_count) tty_schedule_flip(tty); tty_kref_put(tty); @@ -1464,7 +1464,7 @@ static void cy_shutdown(struct cyclades_port *info, struct tty_struct *tty) spin_lock_irqsave(&card->card_lock, flags); /* Clear delta_msr_wait queue to avoid mem leaks. */ - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); if (info->port.xmit_buf) { unsigned char *temp; @@ -2788,7 +2788,7 @@ cy_ioctl(struct tty_struct *tty, struct file *file, /* note the counters on entry */ cnow = info->icount; spin_unlock_irqrestore(&info->card->card_lock, flags); - ret_val = wait_event_interruptible(info->delta_msr_wait, + ret_val = wait_event_interruptible(info->port.delta_msr_wait, cy_cflags_changed(info, arg, &cnow)); break; @@ -3153,7 +3153,6 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo) info->port.close_delay = 5 * HZ / 10; info->port.flags = STD_COM_FLAGS; init_completion(&info->shutdown_wait); - init_waitqueue_head(&info->delta_msr_wait); if (cy_is_Z(cinfo)) { struct FIRM_ID *firm_id = cinfo->base_addr + ID_ADDRESS; diff --git a/drivers/char/esp.c b/drivers/char/esp.c index a5c59fc2b0ff..b19d43cd9542 100644 --- a/drivers/char/esp.c +++ b/drivers/char/esp.c @@ -572,7 +572,7 @@ static void check_modem_status(struct esp_struct *info) info->icount.dcd++; if (status & UART_MSR_DCTS) info->icount.cts++; - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); } if ((info->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { @@ -927,7 +927,7 @@ static void shutdown(struct esp_struct *info) * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be waken up */ - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); wake_up_interruptible(&info->break_wait); /* stop a DMA transfer on the port being closed */ @@ -1800,7 +1800,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file *file, spin_unlock_irqrestore(&info->lock, flags); while (1) { /* FIXME: convert to new style wakeup */ - interruptible_sleep_on(&info->delta_msr_wait); + interruptible_sleep_on(&info->port.delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; @@ -2452,7 +2452,6 @@ static int __init espserial_init(void) info->config.flow_off = flow_off; info->config.pio_threshold = pio_threshold; info->next_port = ports; - init_waitqueue_head(&info->delta_msr_wait); init_waitqueue_head(&info->break_wait); ports = info; printk(KERN_INFO "ttyP%d at 0x%04x (irq = %d) is an ESP ", diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 30544ca5e956..37058ff7da7d 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -258,7 +258,6 @@ struct mxser_port { struct mxser_mon mon_data; spinlock_t slock; - wait_queue_head_t delta_msr_wait; }; struct mxser_board { @@ -818,7 +817,7 @@ static void mxser_check_modem_status(struct tty_struct *tty, if (status & UART_MSR_DCTS) port->icount.cts++; port->mon_data.modem_status = status; - wake_up_interruptible(&port->delta_msr_wait); + wake_up_interruptible(&port->port.delta_msr_wait); if ((port->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { if (status & UART_MSR_DCD) @@ -973,7 +972,7 @@ static void mxser_shutdown(struct tty_struct *tty) * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be waken up */ - wake_up_interruptible(&info->delta_msr_wait); + wake_up_interruptible(&info->port.delta_msr_wait); /* * Free the IRQ, if necessary @@ -1762,7 +1761,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, cnow = info->icount; /* note the counters on entry */ spin_unlock_irqrestore(&info->slock, flags); - return wait_event_interruptible(info->delta_msr_wait, + return wait_event_interruptible(info->port.delta_msr_wait, mxser_cflags_changed(info, arg, &cnow)); /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) @@ -2414,7 +2413,6 @@ static int __devinit mxser_initbrd(struct mxser_board *brd, info->port.close_delay = 5 * HZ / 10; info->port.closing_wait = 30 * HZ; info->normal_termios = mxvar_sdriver->init_termios; - init_waitqueue_head(&info->delta_msr_wait); memset(&info->mon_data, 0, sizeof(struct mxser_mon)); info->err_shadow = 0; spin_lock_init(&info->slock); diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 549bd0fa8bb6..c767e30a1425 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -23,6 +23,7 @@ void tty_port_init(struct tty_port *port) memset(port, 0, sizeof(*port)); init_waitqueue_head(&port->open_wait); init_waitqueue_head(&port->close_wait); + init_waitqueue_head(&port->delta_msr_wait); mutex_init(&port->mutex); spin_lock_init(&port->lock); port->close_delay = (50 * HZ) / 100; @@ -124,6 +125,7 @@ void tty_port_hangup(struct tty_port *port) port->tty = NULL; spin_unlock_irqrestore(&port->lock, flags); wake_up_interruptible(&port->open_wait); + wake_up_interruptible(&port->delta_msr_wait); tty_port_shutdown(port); } EXPORT_SYMBOL(tty_port_hangup); diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index e415c5eca599..2209620d2349 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1510,7 +1510,7 @@ static unsigned int check_modem_status(struct uart_8250_port *up) if (status & UART_MSR_DCTS) uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - wake_up_interruptible(&up->port.state->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } return status; diff --git a/drivers/serial/amba-pl010.c b/drivers/serial/amba-pl010.c index 39032413d4a1..429a8ae86933 100644 --- a/drivers/serial/amba-pl010.c +++ b/drivers/serial/amba-pl010.c @@ -225,7 +225,7 @@ static void pl010_modem_status(struct uart_amba_port *uap) if (delta & UART01x_FR_CTS) uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); - wake_up_interruptible(&uap->port.state->delta_msr_wait); + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); } static irqreturn_t pl010_int(int irq, void *dev_id) diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c index ef82a34baf0f..ef7adc8135dd 100644 --- a/drivers/serial/amba-pl011.c +++ b/drivers/serial/amba-pl011.c @@ -226,7 +226,7 @@ static void pl011_modem_status(struct uart_amba_port *uap) if (delta & UART01x_FR_CTS) uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS); - wake_up_interruptible(&uap->port.state->delta_msr_wait); + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); } static irqreturn_t pl011_int(int irq, void *dev_id) diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index 963e3c12af41..3551c5cb7094 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -776,7 +776,7 @@ static void atmel_tasklet_func(unsigned long data) if (status_change & ATMEL_US_CTS) uart_handle_cts_change(port, !(status & ATMEL_US_CTS)); - wake_up_interruptible(&port->state->delta_msr_wait); + wake_up_interruptible(&port->state->port.delta_msr_wait); atmel_port->irq_status_prev = status; } diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c index f86c47e08a06..2d7feecaf492 100644 --- a/drivers/serial/icom.c +++ b/drivers/serial/icom.c @@ -695,7 +695,7 @@ static inline void check_modem_status(struct icom_port *icom_port) delta_status & ICOM_CTS); wake_up_interruptible(&icom_port->uart_port.state-> - delta_msr_wait); + port.delta_msr_wait); old_status = status; } spin_unlock(&icom_port->uart_port.lock); diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index 1febeafcb97a..18130f11238e 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -224,7 +224,7 @@ static void imx_mctrl_check(struct imx_port *sport) if (changed & TIOCM_CTS) uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - wake_up_interruptible(&sport->port.state->delta_msr_wait); + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); } /* @@ -388,7 +388,7 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id) writel(USR1_RTSD, sport->port.membase + USR1); uart_handle_cts_change(&sport->port, !!val); - wake_up_interruptible(&sport->port.state->delta_msr_wait); + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); spin_unlock_irqrestore(&sport->port.lock, flags); return IRQ_HANDLED; diff --git a/drivers/serial/ioc3_serial.c b/drivers/serial/ioc3_serial.c index de4ab1bfee8d..d8983dd5c4b2 100644 --- a/drivers/serial/ioc3_serial.c +++ b/drivers/serial/ioc3_serial.c @@ -1287,7 +1287,7 @@ static inline int do_read(struct uart_port *the_port, char *buf, int len) (port->ip_port, 0); wake_up_interruptible (&the_port->state-> - delta_msr_wait); + port.delta_msr_wait); } /* If we had any data to return, we @@ -1491,7 +1491,7 @@ ioc3uart_intr_one(struct ioc3_submodule *is, uart_handle_dcd_change(the_port, shadow & SHADOW_DCD); wake_up_interruptible - (&the_port->state->delta_msr_wait); + (&the_port->state->port.delta_msr_wait); } else if ((port->ip_notify & N_DDCD) && !(shadow & SHADOW_DCD)) { /* Flag delta DCD/no DCD */ @@ -1511,7 +1511,7 @@ ioc3uart_intr_one(struct ioc3_submodule *is, uart_handle_cts_change(the_port, shadow & SHADOW_CTS); wake_up_interruptible - (&the_port->state->delta_msr_wait); + (&the_port->state->port.delta_msr_wait); } } @@ -1728,7 +1728,7 @@ static void ic3_shutdown(struct uart_port *the_port) return; state = the_port->state; - wake_up_interruptible(&state->delta_msr_wait); + wake_up_interruptible(&state->port.delta_msr_wait); spin_lock_irqsave(&the_port->lock, port_flags); set_notification(port, N_ALL, 0); diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c index 2055d323f15f..2e02c3026d24 100644 --- a/drivers/serial/ioc4_serial.c +++ b/drivers/serial/ioc4_serial.c @@ -1882,7 +1882,7 @@ static void handle_intr(void *arg, uint32_t sio_ir) the_port = port->ip_port; the_port->icount.dcd = 1; wake_up_interruptible - (&the_port->state->delta_msr_wait); + (&the_port->state->port.delta_msr_wait); } else if ((port->ip_notify & N_DDCD) && !(shadow & IOC4_SHADOW_DCD)) { /* Flag delta DCD/no DCD */ @@ -1904,7 +1904,7 @@ static void handle_intr(void *arg, uint32_t sio_ir) the_port->icount.cts = (shadow & IOC4_SHADOW_CTS) ? 1 : 0; wake_up_interruptible - (&the_port->state->delta_msr_wait); + (&the_port->state->port.delta_msr_wait); } } @@ -2237,7 +2237,7 @@ static inline int do_read(struct uart_port *the_port, unsigned char *buf, the_port->icount.dcd = 0; wake_up_interruptible (&the_port->state-> - delta_msr_wait); + port.delta_msr_wait); } /* If we had any data to return, we @@ -2439,7 +2439,7 @@ static void ic4_shutdown(struct uart_port *the_port) state = the_port->state; port->ip_port = NULL; - wake_up_interruptible(&state->delta_msr_wait); + wake_up_interruptible(&state->port.delta_msr_wait); if (state->port.tty) set_bit(TTY_IO_ERROR, &state->port.tty->flags); diff --git a/drivers/serial/ip22zilog.c b/drivers/serial/ip22zilog.c index 2e847deb41dc..ebff4a1d4bcc 100644 --- a/drivers/serial/ip22zilog.c +++ b/drivers/serial/ip22zilog.c @@ -354,7 +354,7 @@ static void ip22zilog_status_handle(struct uart_ip22zilog_port *up, uart_handle_cts_change(&up->port, (status & CTS)); - wake_up_interruptible(&up->port.state->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } up->prev_status = status; diff --git a/drivers/serial/msm_serial.c b/drivers/serial/msm_serial.c index ff18d50c99c1..b05c5aa02cb4 100644 --- a/drivers/serial/msm_serial.c +++ b/drivers/serial/msm_serial.c @@ -169,7 +169,7 @@ static void handle_delta_cts(struct uart_port *port) { msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); port->icount.cts++; - wake_up_interruptible(&port->state->delta_msr_wait); + wake_up_interruptible(&port->state->port.delta_msr_wait); } static irqreturn_t msm_irq(int irq, void *dev_id) diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 0dc786835dca..0700cd10b97c 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -369,7 +369,7 @@ static void pmz_status_handle(struct uart_pmac_port *uap) uart_handle_cts_change(&uap->port, !(status & CTS)); - wake_up_interruptible(&uap->port.state->delta_msr_wait); + wake_up_interruptible(&uap->port.state->port.delta_msr_wait); } if (status & BRK_ABRT) diff --git a/drivers/serial/pnx8xxx_uart.c b/drivers/serial/pnx8xxx_uart.c index 2da747635275..0aa75a97531c 100644 --- a/drivers/serial/pnx8xxx_uart.c +++ b/drivers/serial/pnx8xxx_uart.c @@ -100,7 +100,7 @@ static void pnx8xxx_mctrl_check(struct pnx8xxx_port *sport) if (changed & TIOCM_CTS) uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - wake_up_interruptible(&sport->port.state->delta_msr_wait); + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); } /* diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c index ad48919c0415..6443b7ff274a 100644 --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -220,7 +220,7 @@ static inline void check_modem_status(struct uart_pxa_port *up) if (status & UART_MSR_DCTS) uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - wake_up_interruptible(&up->port.state->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } /* diff --git a/drivers/serial/sa1100.c b/drivers/serial/sa1100.c index 61ef3ae24927..7f5e26873220 100644 --- a/drivers/serial/sa1100.c +++ b/drivers/serial/sa1100.c @@ -117,7 +117,7 @@ static void sa1100_mctrl_check(struct sa1100_port *sport) if (changed & TIOCM_CTS) uart_handle_cts_change(&sport->port, status & TIOCM_CTS); - wake_up_interruptible(&sport->port.state->delta_msr_wait); + wake_up_interruptible(&sport->port.state->port.delta_msr_wait); } /* diff --git a/drivers/serial/sb1250-duart.c b/drivers/serial/sb1250-duart.c index fa5f303b36d3..a2f2b3254499 100644 --- a/drivers/serial/sb1250-duart.c +++ b/drivers/serial/sb1250-duart.c @@ -440,7 +440,7 @@ static void sbd_status_handle(struct sbd_port *sport) if (delta & ((M_DUART_IN_PIN2_VAL | M_DUART_IN_PIN0_VAL) << S_DUART_IN_PIN_CHNG)) - wake_up_interruptible(&uport->state->delta_msr_wait); + wake_up_interruptible(&uport->state->port.delta_msr_wait); } static irqreturn_t sbd_interrupt(int irq, void *dev_id) diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c index 9d42e57e1971..e16d15343dfd 100644 --- a/drivers/serial/serial_core.c +++ b/drivers/serial/serial_core.c @@ -215,7 +215,8 @@ static int uart_startup(struct uart_state *state, int init_hw) static void uart_shutdown(struct uart_state *state) { struct uart_port *uport = state->uart_port; - struct tty_struct *tty = state->port.tty; + struct tty_port *port = &state->port; + struct tty_struct *tty = port->tty; /* * Set the TTY IO error marker @@ -223,7 +224,7 @@ static void uart_shutdown(struct uart_state *state) if (tty) set_bit(TTY_IO_ERROR, &tty->flags); - if (test_and_clear_bit(ASYNCB_INITIALIZED, &state->port.flags)) { + if (test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) { /* * Turn off DTR and RTS early. */ @@ -237,7 +238,7 @@ static void uart_shutdown(struct uart_state *state) * any outstanding file descriptors should be pointing at * hung_up_tty_fops now. */ - wake_up_interruptible(&state->delta_msr_wait); + wake_up_interruptible(&port->delta_msr_wait); /* * Free the IRQ and disable the port. @@ -1004,11 +1005,15 @@ static int uart_do_autoconfig(struct uart_state *state) * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was + * + * FIXME: This wants extracting into a common all driver implementation + * of TIOCMWAIT using tty_port. */ static int uart_wait_modem_status(struct uart_state *state, unsigned long arg) { struct uart_port *uport = state->uart_port; + struct tty_port *port = &state->port; DECLARE_WAITQUEUE(wait, current); struct uart_icount cprev, cnow; int ret; @@ -1025,7 +1030,7 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) uport->ops->enable_ms(uport); spin_unlock_irq(&uport->lock); - add_wait_queue(&state->delta_msr_wait, &wait); + add_wait_queue(&port->delta_msr_wait, &wait); for (;;) { spin_lock_irq(&uport->lock); memcpy(&cnow, &uport->icount, sizeof(struct uart_icount)); @@ -1053,7 +1058,7 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg) } current->state = TASK_RUNNING; - remove_wait_queue(&state->delta_msr_wait, &wait); + remove_wait_queue(&port->delta_msr_wait, &wait); return ret; } @@ -1430,7 +1435,7 @@ static void uart_hangup(struct tty_struct *tty) clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags); port->tty = NULL; wake_up_interruptible(&port->open_wait); - wake_up_interruptible(&state->delta_msr_wait); + wake_up_interruptible(&port->delta_msr_wait); } mutex_unlock(&port->mutex); } @@ -2378,7 +2383,6 @@ int uart_register_driver(struct uart_driver *drv) tty_port_init(port); port->close_delay = 500; /* .5 seconds */ port->closing_wait = 30000; /* 30 seconds */ - init_waitqueue_head(&state->delta_msr_wait); tasklet_init(&state->tlet, uart_tasklet_action, (unsigned long)state); } diff --git a/drivers/serial/serial_ks8695.c b/drivers/serial/serial_ks8695.c index 4560b2e70685..2e71bbc04dac 100644 --- a/drivers/serial/serial_ks8695.c +++ b/drivers/serial/serial_ks8695.c @@ -266,7 +266,7 @@ static irqreturn_t ks8695uart_modem_status(int irq, void *dev_id) if (status & URMS_URTERI) port->icount.rng++; - wake_up_interruptible(&port->state->delta_msr_wait); + wake_up_interruptible(&port->state->port.delta_msr_wait); return IRQ_HANDLED; } diff --git a/drivers/serial/serial_lh7a40x.c b/drivers/serial/serial_lh7a40x.c index 057fc5e8cc8d..ea744707c4d6 100644 --- a/drivers/serial/serial_lh7a40x.c +++ b/drivers/serial/serial_lh7a40x.c @@ -241,7 +241,7 @@ static void lh7a40xuart_modem_status (struct uart_port* port) if (delta & CTS) uart_handle_cts_change (port, status & CTS); - wake_up_interruptible (&port->state->delta_msr_wait); + wake_up_interruptible (&port->state->port.delta_msr_wait); } static irqreturn_t lh7a40xuart_int (int irq, void* dev_id) diff --git a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c index 7c4f2fe8e246..d1ad34128635 100644 --- a/drivers/serial/sunsab.c +++ b/drivers/serial/sunsab.c @@ -297,7 +297,7 @@ static void check_status(struct uart_sunsab_port *up, up->port.icount.dsr++; } - wake_up_interruptible(&up->port.state->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } static irqreturn_t sunsab_interrupt(int irq, void *dev_id) diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index 5a32365b58ad..68d262b15749 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -441,7 +441,7 @@ static void check_modem_status(struct uart_sunsu_port *up) if (status & UART_MSR_DCTS) uart_handle_cts_change(&up->port, status & UART_MSR_CTS); - wake_up_interruptible(&up->port.state->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } static irqreturn_t sunsu_serial_interrupt(int irq, void *dev_id) diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c index 055034d12b1c..ef693ae22e7f 100644 --- a/drivers/serial/sunzilog.c +++ b/drivers/serial/sunzilog.c @@ -451,7 +451,7 @@ static void sunzilog_status_handle(struct uart_sunzilog_port *up, uart_handle_cts_change(&up->port, (status & CTS)); - wake_up_interruptible(&up->port.state->delta_msr_wait); + wake_up_interruptible(&up->port.state->port.delta_msr_wait); } up->prev_status = status; diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c index 3d40be6f389f..34b31da01d09 100644 --- a/drivers/serial/timbuart.c +++ b/drivers/serial/timbuart.c @@ -231,7 +231,7 @@ static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier) iowrite32(CTS_DELTA, port->membase + TIMBUART_ISR); cts = timbuart_get_mctrl(port); uart_handle_cts_change(port, cts & TIOCM_CTS); - wake_up_interruptible(&port->state->delta_msr_wait); + wake_up_interruptible(&port->state->port.delta_msr_wait); } *ier |= CTS_DELTA; diff --git a/drivers/serial/vr41xx_siu.c b/drivers/serial/vr41xx_siu.c index cf4410e6d53b..3beb6ab4fa68 100644 --- a/drivers/serial/vr41xx_siu.c +++ b/drivers/serial/vr41xx_siu.c @@ -386,7 +386,7 @@ static inline void check_modem_status(struct uart_port *port) if (msr & UART_MSR_DCTS) uart_handle_cts_change(port, msr & UART_MSR_CTS); - wake_up_interruptible(&port->state->delta_msr_wait); + wake_up_interruptible(&port->state->port.delta_msr_wait); } static inline void transmit_chars(struct uart_port *port) diff --git a/drivers/serial/zs.c b/drivers/serial/zs.c index b9c9fb9198d6..1a7fd3e70315 100644 --- a/drivers/serial/zs.c +++ b/drivers/serial/zs.c @@ -686,7 +686,7 @@ static void zs_status_handle(struct zs_port *zport, struct zs_port *zport_a) uport->icount.rng++; if (delta) - wake_up_interruptible(&uport->state->delta_msr_wait); + wake_up_interruptible(&uport->state->port.delta_msr_wait); spin_lock(&scc->zlock); } diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h index bbebef7713b3..a5049eaf782d 100644 --- a/include/linux/cyclades.h +++ b/include/linux/cyclades.h @@ -578,7 +578,6 @@ struct cyclades_port { struct cyclades_idle_stats idle_stats; struct cyclades_icount icount; struct completion shutdown_wait; - wait_queue_head_t delta_msr_wait; int throttle; }; diff --git a/include/linux/hayesesp.h b/include/linux/hayesesp.h index 940aeb51d53f..92b08cfe4a75 100644 --- a/include/linux/hayesesp.h +++ b/include/linux/hayesesp.h @@ -96,7 +96,6 @@ struct esp_struct { int xmit_head; int xmit_tail; int xmit_cnt; - wait_queue_head_t delta_msr_wait; wait_queue_head_t break_wait; struct async_icount icount; /* kernel counters for the 4 input interrupts */ struct hayes_esp_config config; /* port configuration */ diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 27767ea5fa29..bcafecd3b7c1 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -349,7 +349,6 @@ struct uart_state { struct circ_buf xmit; struct tasklet_struct tlet; - wait_queue_head_t delta_msr_wait; struct uart_port *uart_port; }; diff --git a/include/linux/tty.h b/include/linux/tty.h index 9fdc3d84baad..0daa8a72b176 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -203,6 +203,7 @@ struct tty_port { int count; /* Usage count */ wait_queue_head_t open_wait; /* Open waiters */ wait_queue_head_t close_wait; /* Close waiters */ + wait_queue_head_t delta_msr_wait; /* Modem status change */ unsigned long flags; /* TTY flags ASY_*/ struct mutex mutex; /* Locking */ unsigned char *xmit_buf; /* Optional buffer */ -- cgit v1.2.3 From fe1ae7fdd2ee603f2d95f04e09a68f7f79045127 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 19 Sep 2009 13:13:33 -0700 Subject: tty: USB serial termios bits Various drivers have hacks to mangle termios structures. This stems from the fact there is no nice setup hook for configuring the termios settings when the port is created Signed-off-by: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_io.c | 1 + drivers/usb/serial/ark3116.c | 46 ++++++++++------------------------------ drivers/usb/serial/cypress_m8.c | 12 +++-------- drivers/usb/serial/empeg.c | 12 +++-------- drivers/usb/serial/iuu_phoenix.c | 31 ++++++++++++--------------- drivers/usb/serial/kobil_sct.c | 22 +++++++++---------- drivers/usb/serial/oti6858.c | 21 +++++++++--------- drivers/usb/serial/spcp8x5.c | 21 +++++++++--------- drivers/usb/serial/usb-serial.c | 38 ++++++++++++++++++++++++++++++++- drivers/usb/serial/whiteheat.c | 6 +++--- include/linux/usb/serial.h | 3 +++ 11 files changed, 106 insertions(+), 107 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 385cca7074da..05f443c47bbe 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1184,6 +1184,7 @@ int tty_init_termios(struct tty_struct *tty) tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios); return 0; } +EXPORT_SYMBOL_GPL(tty_init_termios); /** * tty_driver_install_tty() - install a tty entry in the driver diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 7ddde4ddfb4b..5d25d3e52bf6 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -35,11 +35,6 @@ static struct usb_device_id id_table [] = { }; MODULE_DEVICE_TABLE(usb, id_table); -struct ark3116_private { - spinlock_t lock; - u8 termios_initialized; -}; - static inline void ARK3116_SND(struct usb_serial *serial, int seq, __u8 request, __u8 requesttype, __u16 value, __u16 index) @@ -82,22 +77,11 @@ static inline void ARK3116_RCV_QUIET(struct usb_serial *serial, static int ark3116_attach(struct usb_serial *serial) { char *buf; - struct ark3116_private *priv; - int i; - - for (i = 0; i < serial->num_ports; ++i) { - priv = kzalloc(sizeof(struct ark3116_private), GFP_KERNEL); - if (!priv) - goto cleanup; - spin_lock_init(&priv->lock); - - usb_set_serial_port_data(serial->port[i], priv); - } buf = kmalloc(1, GFP_KERNEL); if (!buf) { dbg("error kmalloc -> out of mem?"); - goto cleanup; + return -ENOMEM; } /* 3 */ @@ -149,13 +133,16 @@ static int ark3116_attach(struct usb_serial *serial) kfree(buf); return 0; +} -cleanup: - for (--i; i >= 0; --i) { - kfree(usb_get_serial_port_data(serial->port[i])); - usb_set_serial_port_data(serial->port[i], NULL); - } - return -ENOMEM; +static void ark3116_init_termios(struct tty_struct *tty) +{ + struct ktermios *termios = tty->termios; + *termios = tty_std_termios; + termios->c_cflag = B9600 | CS8 + | CREAD | HUPCL | CLOCAL; + termios->c_ispeed = 9600; + termios->c_ospeed = 9600; } static void ark3116_set_termios(struct tty_struct *tty, @@ -163,10 +150,8 @@ static void ark3116_set_termios(struct tty_struct *tty, struct ktermios *old_termios) { struct usb_serial *serial = port->serial; - struct ark3116_private *priv = usb_get_serial_port_data(port); struct ktermios *termios = tty->termios; unsigned int cflag = termios->c_cflag; - unsigned long flags; int baud; int ark3116_baud; char *buf; @@ -176,16 +161,6 @@ static void ark3116_set_termios(struct tty_struct *tty, dbg("%s - port %d", __func__, port->number); - spin_lock_irqsave(&priv->lock, flags); - if (!priv->termios_initialized) { - *termios = tty_std_termios; - termios->c_cflag = B9600 | CS8 - | CREAD | HUPCL | CLOCAL; - termios->c_ispeed = 9600; - termios->c_ospeed = 9600; - priv->termios_initialized = 1; - } - spin_unlock_irqrestore(&priv->lock, flags); cflag = termios->c_cflag; termios->c_cflag &= ~(CMSPAR|CRTSCTS); @@ -454,6 +429,7 @@ static struct usb_serial_driver ark3116_device = { .num_ports = 1, .attach = ark3116_attach, .set_termios = ark3116_set_termios, + .init_termios = ark3116_init_termios, .ioctl = ark3116_ioctl, .tiocmget = ark3116_tiocmget, .open = ark3116_open, diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index df1adb9a4367..e0a8b715f2f2 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -657,15 +657,7 @@ static int cypress_open(struct tty_struct *tty, struct usb_serial_port *port) spin_unlock_irqrestore(&priv->lock, flags); /* Set termios */ - result = cypress_write(tty, port, NULL, 0); - - if (result) { - dev_err(&port->dev, - "%s - failed setting the control lines - error %d\n", - __func__, result); - return result; - } else - dbg("%s - success setting the control lines", __func__); + cypress_send(port); if (tty) cypress_set_termios(tty, port, &priv->tmp_termios); @@ -1003,6 +995,8 @@ static void cypress_set_termios(struct tty_struct *tty, dbg("%s - port %d", __func__, port->number); spin_lock_irqsave(&priv->lock, flags); + /* We can't clean this one up as we don't know the device type + early enough */ if (!priv->termios_initialized) { if (priv->chiptype == CT_EARTHMATE) { *(tty->termios) = tty_std_termios; diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c index da559a773b51..33c9e9cf9eb2 100644 --- a/drivers/usb/serial/empeg.c +++ b/drivers/usb/serial/empeg.c @@ -89,8 +89,7 @@ static int empeg_chars_in_buffer(struct tty_struct *tty); static void empeg_throttle(struct tty_struct *tty); static void empeg_unthrottle(struct tty_struct *tty); static int empeg_startup(struct usb_serial *serial); -static void empeg_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, struct ktermios *old_termios); +static void empeg_init_termios(struct tty_struct *tty); static void empeg_write_bulk_callback(struct urb *urb); static void empeg_read_bulk_callback(struct urb *urb); @@ -122,7 +121,7 @@ static struct usb_serial_driver empeg_device = { .throttle = empeg_throttle, .unthrottle = empeg_unthrottle, .attach = empeg_startup, - .set_termios = empeg_set_termios, + .init_termios = empeg_init_termios, .write = empeg_write, .write_room = empeg_write_room, .chars_in_buffer = empeg_chars_in_buffer, @@ -148,9 +147,6 @@ static int empeg_open(struct tty_struct *tty,struct usb_serial_port *port) dbg("%s - port %d", __func__, port->number); - /* Force default termio settings */ - empeg_set_termios(tty, port, NULL); - bytes_in = 0; bytes_out = 0; @@ -423,11 +419,9 @@ static int empeg_startup(struct usb_serial *serial) } -static void empeg_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, struct ktermios *old_termios) +static void empeg_init_termios(struct tty_struct *tty) { struct ktermios *termios = tty->termios; - dbg("%s - port %d", __func__, port->number); /* * The empeg-car player wants these particular tty settings. diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c index f3f9be0469c5..6138c1cda35f 100644 --- a/drivers/usb/serial/iuu_phoenix.c +++ b/drivers/usb/serial/iuu_phoenix.c @@ -71,7 +71,6 @@ struct iuu_private { spinlock_t lock; /* store irq state */ wait_queue_head_t delta_msr_wait; u8 line_status; - u8 termios_initialized; int tiostatus; /* store IUART SIGNAL for tiocmget call */ u8 reset; /* if 1 reset is needed */ int poll; /* number of poll */ @@ -1018,13 +1017,24 @@ static void iuu_close(struct usb_serial_port *port) } } +static void iuu_init_termios(struct tty_struct *tty) +{ + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 + | TIOCM_CTS | CSTOPB | PARENB; + tty->termios->c_ispeed = 9600; + tty->termios->c_ospeed = 9600; + tty->termios->c_lflag = 0; + tty->termios->c_oflag = 0; + tty->termios->c_iflag = 0; +} + static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) { struct usb_serial *serial = port->serial; u8 *buf; int result; u32 actual; - unsigned long flags; struct iuu_private *priv = usb_get_serial_port_data(port); dbg("%s - port %d", __func__, port->number); @@ -1063,21 +1073,7 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port) port->bulk_in_buffer, 512, NULL, NULL); - /* set the termios structure */ - spin_lock_irqsave(&priv->lock, flags); - if (tty && !priv->termios_initialized) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = CLOCAL | CREAD | CS8 | B9600 - | TIOCM_CTS | CSTOPB | PARENB; - tty->termios->c_ispeed = 9600; - tty->termios->c_ospeed = 9600; - tty->termios->c_lflag = 0; - tty->termios->c_oflag = 0; - tty->termios->c_iflag = 0; - priv->termios_initialized = 1; - priv->poll = 0; - } - spin_unlock_irqrestore(&priv->lock, flags); + priv->poll = 0; /* initialize writebuf */ #define FISH(a, b, c, d) do { \ @@ -1200,6 +1196,7 @@ static struct usb_serial_driver iuu_device = { .tiocmget = iuu_tiocmget, .tiocmset = iuu_tiocmset, .set_termios = iuu_set_termios, + .init_termios = iuu_init_termios, .attach = iuu_startup, .release = iuu_release, }; diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c index 97901578343a..45ea694b3ae6 100644 --- a/drivers/usb/serial/kobil_sct.c +++ b/drivers/usb/serial/kobil_sct.c @@ -84,7 +84,7 @@ static void kobil_read_int_callback(struct urb *urb); static void kobil_write_callback(struct urb *purb); static void kobil_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); - +static void kobil_init_termios(struct tty_struct *tty); static struct usb_device_id id_table [] = { { USB_DEVICE(KOBIL_VENDOR_ID, KOBIL_ADAPTER_B_PRODUCT_ID) }, @@ -119,6 +119,7 @@ static struct usb_serial_driver kobil_device = { .release = kobil_release, .ioctl = kobil_ioctl, .set_termios = kobil_set_termios, + .init_termios = kobil_init_termios, .tiocmget = kobil_tiocmget, .tiocmset = kobil_tiocmset, .open = kobil_open, @@ -209,6 +210,15 @@ static void kobil_release(struct usb_serial *serial) kfree(usb_get_serial_port_data(serial->port[i])); } +static void kobil_init_termios(struct tty_struct *tty) +{ + /* Default to echo off and other sane device settings */ + tty->termios->c_lflag = 0; + tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | XCASE); + tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; + /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */ + tty->termios->c_oflag &= ~ONLCR; +} static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) { @@ -224,16 +234,6 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port) /* someone sets the dev to 0 if the close method has been called */ port->interrupt_in_urb->dev = port->serial->dev; - if (tty) { - - /* Default to echo off and other sane device settings */ - tty->termios->c_lflag = 0; - tty->termios->c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN | - XCASE); - tty->termios->c_iflag = IGNBRK | IGNPAR | IXOFF; - /* do NOT translate CR to CR-NL (0x0A -> 0x0A 0x0D) */ - tty->termios->c_oflag &= ~ONLCR; - } /* allocate memory for transfer buffer */ transfer_buffer = kzalloc(transfer_buffer_length, GFP_KERNEL); if (!transfer_buffer) diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c index e90f88a3b040..0f4a70ce3823 100644 --- a/drivers/usb/serial/oti6858.c +++ b/drivers/usb/serial/oti6858.c @@ -145,6 +145,7 @@ static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port); static void oti6858_close(struct usb_serial_port *port); static void oti6858_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old); +static void oti6858_init_termios(struct tty_struct *tty); static int oti6858_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); static void oti6858_read_int_callback(struct urb *urb); @@ -185,6 +186,7 @@ static struct usb_serial_driver oti6858_device = { .write = oti6858_write, .ioctl = oti6858_ioctl, .set_termios = oti6858_set_termios, + .init_termios = oti6858_init_termios, .tiocmget = oti6858_tiocmget, .tiocmset = oti6858_tiocmset, .read_bulk_callback = oti6858_read_bulk_callback, @@ -205,7 +207,6 @@ struct oti6858_private { struct { u8 read_urb_in_use; u8 write_urb_in_use; - u8 termios_initialized; } flags; struct delayed_work delayed_write_work; @@ -446,6 +447,14 @@ static int oti6858_chars_in_buffer(struct tty_struct *tty) return chars; } +static void oti6858_init_termios(struct tty_struct *tty) +{ + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 38400; + tty->termios->c_ospeed = 38400; +} + static void oti6858_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios) { @@ -463,16 +472,6 @@ static void oti6858_set_termios(struct tty_struct *tty, return; } - spin_lock_irqsave(&priv->lock, flags); - if (!priv->flags.termios_initialized) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B38400 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 38400; - tty->termios->c_ospeed = 38400; - priv->flags.termios_initialized = 1; - } - spin_unlock_irqrestore(&priv->lock, flags); - cflag = tty->termios->c_cflag; spin_lock_irqsave(&priv->lock, flags); diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 8b312a05a353..61e7c40b94fb 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -299,7 +299,6 @@ struct spcp8x5_private { wait_queue_head_t delta_msr_wait; u8 line_control; u8 line_status; - u8 termios_initialized; }; /* desc : when device plug in,this function would be called. @@ -498,6 +497,15 @@ static void spcp8x5_close(struct usb_serial_port *port) dev_dbg(&port->dev, "usb_unlink_urb(read_urb) = %d\n", result); } +static void spcp8x5_init_termios(struct tty_struct *tty) +{ + /* for the 1st time call this function */ + *(tty->termios) = tty_std_termios; + tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; + tty->termios->c_ispeed = 115200; + tty->termios->c_ospeed = 115200; +} + /* set the serial param for transfer. we should check if we really need to * transfer. if we set flow control we should do this too. */ static void spcp8x5_set_termios(struct tty_struct *tty, @@ -514,16 +522,6 @@ static void spcp8x5_set_termios(struct tty_struct *tty, int i; u8 control; - /* for the 1st time call this function */ - spin_lock_irqsave(&priv->lock, flags); - if (!priv->termios_initialized) { - *(tty->termios) = tty_std_termios; - tty->termios->c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL; - tty->termios->c_ispeed = 115200; - tty->termios->c_ospeed = 115200; - priv->termios_initialized = 1; - } - spin_unlock_irqrestore(&priv->lock, flags); /* check that they really want us to change something */ if (!tty_termios_hw_change(tty->termios, old_termios)) @@ -1008,6 +1006,7 @@ static struct usb_serial_driver spcp8x5_device = { .carrier_raised = spcp8x5_carrier_raised, .write = spcp8x5_write, .set_termios = spcp8x5_set_termios, + .init_termios = spcp8x5_init_termios, .ioctl = spcp8x5_ioctl, .tiocmget = spcp8x5_tiocmget, .tiocmset = spcp8x5_tiocmset, diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 3dda6841e724..80c1f4d8e910 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -721,6 +721,41 @@ static const struct tty_port_operations serial_port_ops = { .dtr_rts = serial_dtr_rts, }; +/** + * serial_install - install tty + * @driver: the driver (USB in our case) + * @tty: the tty being created + * + * Create the termios objects for this tty. We use the default USB + * serial ones but permit them to be overriddenby serial->type->termios. + * This lets us remove all the ugly hackery + */ + +static int serial_install(struct tty_driver *driver, struct tty_struct *tty) +{ + int idx = tty->index; + struct usb_serial *serial; + int retval; + + /* If the termios setup has yet to be done */ + if (tty->driver->termios[idx] == NULL) { + /* perform the standard setup */ + retval = tty_init_termios(tty); + if (retval) + return retval; + /* allow the driver to update it */ + serial = usb_serial_get_by_index(tty->index); + if (serial->type->init_termios) + serial->type->init_termios(tty); + usb_serial_put(serial); + } + /* Final install (we use the default method) */ + tty_driver_kref_get(driver); + tty->count++; + driver->ttys[idx] = tty; + return 0; +} + int usb_serial_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -1228,7 +1263,8 @@ static const struct tty_operations serial_ops = { .chars_in_buffer = serial_chars_in_buffer, .tiocmget = serial_tiocmget, .tiocmset = serial_tiocmset, - .shutdown = serial_do_free, + .shutdown = serial_do_free, + .install = serial_install, .proc_fops = &serial_proc_fops, }; diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 81f2ae505966..62424eec33ec 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -259,7 +259,7 @@ static int firm_send_command(struct usb_serial_port *port, __u8 command, __u8 *data, __u8 datasize); static int firm_open(struct usb_serial_port *port); static int firm_close(struct usb_serial_port *port); -static int firm_setup_port(struct tty_struct *tty); +static void firm_setup_port(struct tty_struct *tty); static int firm_set_rts(struct usb_serial_port *port, __u8 onoff); static int firm_set_dtr(struct usb_serial_port *port, __u8 onoff); static int firm_set_break(struct usb_serial_port *port, __u8 onoff); @@ -1210,7 +1210,7 @@ static int firm_close(struct usb_serial_port *port) } -static int firm_setup_port(struct tty_struct *tty) +static void firm_setup_port(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct whiteheat_port_settings port_settings; @@ -1285,7 +1285,7 @@ static int firm_setup_port(struct tty_struct *tty) port_settings.lloop = 0; /* now send the message to the device */ - return firm_send_command(port, WHITEHEAT_SETUP_PORT, + firm_send_command(port, WHITEHEAT_SETUP_PORT, (__u8 *)&port_settings, sizeof(port_settings)); } diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 1cbaf4a6b449..7b85e327af91 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -260,6 +260,9 @@ struct usb_serial_driver { be an attached tty at this point */ void (*dtr_rts)(struct usb_serial_port *port, int on); int (*carrier_raised)(struct usb_serial_port *port); + /* Called by the usb serial hooks to allow the user to rework the + termios state */ + void (*init_termios)(struct tty_struct *tty); /* USB events */ void (*read_int_callback)(struct urb *urb); void (*write_int_callback)(struct urb *urb); -- cgit v1.2.3 From ee5aa7b8b98774f408d20a2f61f97a89ac66c29b Mon Sep 17 00:00:00 2001 From: Joe Peterson Date: Wed, 9 Sep 2009 15:03:13 -0600 Subject: n_tty: honor opost flag for echoes Fixes the following bug: http://bugs.linuxbase.org/show_bug.cgi?id=2692 Causes processing of echoed characters (output from the echo buffer) to honor the O_OPOST flag, which is consistent with the old behavior. Note that this and the next patch ("n_tty: move echoctl check and clean up logic") were verified together by the bug reporters, and the test now passes. Signed-off-by: Joe Peterson Cc: Linux Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/char/n_tty.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index 4e28b35024ec..e6eeeb234e5d 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -272,7 +272,8 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty) * * This is a helper function that handles one output character * (including special characters like TAB, CR, LF, etc.), - * putting the results in the tty driver's write buffer. + * doing OPOST processing and putting the results in the + * tty driver's write buffer. * * Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY * and NLDLY. They simply aren't relevant in the world today. @@ -350,8 +351,9 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) * @c: character (or partial unicode symbol) * @tty: terminal device * - * Perform OPOST processing. Returns -1 when the output device is - * full and the character must be retried. + * Output one character with OPOST processing. + * Returns -1 when the output device is full and the character + * must be retried. * * Locking: output_lock to protect column state and space left * (also, this is called from n_tty_write under the @@ -377,8 +379,11 @@ static int process_output(unsigned char c, struct tty_struct *tty) /** * process_output_block - block post processor * @tty: terminal device - * @inbuf: user buffer - * @nr: number of bytes + * @buf: character buffer + * @nr: number of bytes to output + * + * Output a block of characters with OPOST processing. + * Returns the number of characters output. * * This path is used to speed up block console writes, among other * things when processing blocks of output data. It handles only @@ -605,12 +610,18 @@ static void process_echoes(struct tty_struct *tty) if (no_space_left) break; } else { - int retval; - - retval = do_output_char(c, tty, space); - if (retval < 0) - break; - space -= retval; + if (O_OPOST(tty) && + !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) { + int retval = do_output_char(c, tty, space); + if (retval < 0) + break; + space -= retval; + } else { + if (!space) + break; + tty_put_char(tty, c); + space -= 1; + } cp += 1; nr -= 1; } -- cgit v1.2.3 From 62b263585bb5005d44a764c90d80f9c4bb8188c1 Mon Sep 17 00:00:00 2001 From: Joe Peterson Date: Wed, 9 Sep 2009 15:03:47 -0600 Subject: n_tty: move echoctl check and clean up logic Check L_ECHOCTL before insertting a character in the echo buffer (rather than as the buffer is processed), to be more consistent with when all other L_ flags are checked. Also cleaned up the related logic. Note that this and the previous patch ("n_tty: honor opost flag for echoes") were verified together by the reporters of the bug that patch addresses (http://bugs.linuxbase.org/show_bug.cgi?id=2692), and the test now passes. Signed-off-by: Joe Peterson Cc: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- drivers/char/n_tty.c | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c index e6eeeb234e5d..2e50f4dfc79c 100644 --- a/drivers/char/n_tty.c +++ b/drivers/char/n_tty.c @@ -576,33 +576,23 @@ static void process_echoes(struct tty_struct *tty) break; default: - if (iscntrl(op)) { - if (L_ECHOCTL(tty)) { - /* - * Ensure there is enough space - * for the whole ctrl pair. - */ - if (space < 2) { - no_space_left = 1; - break; - } - tty_put_char(tty, '^'); - tty_put_char(tty, op ^ 0100); - tty->column += 2; - space -= 2; - } else { - if (!space) { - no_space_left = 1; - break; - } - tty_put_char(tty, op); - space--; - } - } /* - * If above falls through, this was an - * undefined op. + * If the op is not a special byte code, + * it is a ctrl char tagged to be echoed + * as "^X" (where X is the letter + * representing the control char). + * Note that we must ensure there is + * enough space for the whole ctrl pair. + * */ + if (space < 2) { + no_space_left = 1; + break; + } + tty_put_char(tty, '^'); + tty_put_char(tty, op ^ 0100); + tty->column += 2; + space -= 2; cp += 2; nr -= 2; } @@ -809,8 +799,8 @@ static void echo_char_raw(unsigned char c, struct tty_struct *tty) * Echo user input back onto the screen. This must be called only when * L_ECHO(tty) is true. Called from the driver receive_buf path. * - * This variant tags control characters to be possibly echoed as - * as "^X" (where X is the letter representing the control char). + * This variant tags control characters to be echoed as "^X" + * (where X is the letter representing the control char). * * Locking: echo_lock to protect the echo buffer */ @@ -823,7 +813,7 @@ static void echo_char(unsigned char c, struct tty_struct *tty) add_echo_byte(ECHO_OP_START, tty); add_echo_byte(ECHO_OP_START, tty); } else { - if (iscntrl(c) && c != '\t') + if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') add_echo_byte(ECHO_OP_START, tty); add_echo_byte(c, tty); } -- cgit v1.2.3 From e92166517e3ca9bfb416f91e69cf0373b55b6ede Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 6 Aug 2009 15:09:28 +0200 Subject: tty: handle VT specific compat ioctls in vt driver The VT specific compat_ioctl handlers are the only ones in common code that require the BKL. Moving them into the vt driver lets us remove the BKL from the other handlers and cleans up the code. Signed-off-by: Arnd Bergmann Signed-off-by: Greg Kroah-Hartman --- drivers/char/vt.c | 3 + drivers/char/vt_ioctl.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/tty.h | 3 + 3 files changed, 209 insertions(+) (limited to 'drivers/char') diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 33214d92ca4c..5dfbfa7d7606 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2910,6 +2910,9 @@ static const struct tty_operations con_ops = { .flush_chars = con_flush_chars, .chars_in_buffer = con_chars_in_buffer, .ioctl = vt_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vt_compat_ioctl, +#endif .stop = con_stop, .start = con_start, .throttle = con_throttle, diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 0fceb8f4d6f2..30b5d128037c 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -1376,6 +1377,208 @@ void vc_SAK(struct work_struct *work) release_console_sem(); } +#ifdef CONFIG_COMPAT + +struct compat_consolefontdesc { + unsigned short charcount; /* characters in font (256 or 512) */ + unsigned short charheight; /* scan lines per character (1-32) */ + compat_caddr_t chardata; /* font data in expanded form */ +}; + +static inline int +compat_fontx_ioctl(int cmd, struct compat_consolefontdesc __user *user_cfd, + int perm, struct console_font_op *op) +{ + struct compat_consolefontdesc cfdarg; + int i; + + if (copy_from_user(&cfdarg, user_cfd, sizeof(struct compat_consolefontdesc))) + return -EFAULT; + + switch (cmd) { + case PIO_FONTX: + if (!perm) + return -EPERM; + op->op = KD_FONT_OP_SET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = compat_ptr(cfdarg.chardata); + return con_font_op(vc_cons[fg_console].d, op); + case GIO_FONTX: + op->op = KD_FONT_OP_GET; + op->flags = KD_FONT_FLAG_OLD; + op->width = 8; + op->height = cfdarg.charheight; + op->charcount = cfdarg.charcount; + op->data = compat_ptr(cfdarg.chardata); + i = con_font_op(vc_cons[fg_console].d, op); + if (i) + return i; + cfdarg.charheight = op->height; + cfdarg.charcount = op->charcount; + if (copy_to_user(user_cfd, &cfdarg, sizeof(struct compat_consolefontdesc))) + return -EFAULT; + return 0; + } + return -EINVAL; +} + +struct compat_console_font_op { + compat_uint_t op; /* operation code KD_FONT_OP_* */ + compat_uint_t flags; /* KD_FONT_FLAG_* */ + compat_uint_t width, height; /* font size */ + compat_uint_t charcount; + compat_caddr_t data; /* font data with height fixed to 32 */ +}; + +static inline int +compat_kdfontop_ioctl(struct compat_console_font_op __user *fontop, + int perm, struct console_font_op *op, struct vc_data *vc) +{ + int i; + + if (copy_from_user(op, fontop, sizeof(struct compat_console_font_op))) + return -EFAULT; + if (!perm && op->op != KD_FONT_OP_GET) + return -EPERM; + op->data = compat_ptr(((struct compat_console_font_op *)op)->data); + op->flags |= KD_FONT_FLAG_OLD; + i = con_font_op(vc, op); + if (i) + return i; + ((struct compat_console_font_op *)op)->data = (unsigned long)op->data; + if (copy_to_user(fontop, op, sizeof(struct compat_console_font_op))) + return -EFAULT; + return 0; +} + +struct compat_unimapdesc { + unsigned short entry_ct; + compat_caddr_t entries; +}; + +static inline int +compat_unimap_ioctl(unsigned int cmd, struct compat_unimapdesc __user *user_ud, + int perm, struct vc_data *vc) +{ + struct compat_unimapdesc tmp; + struct unipair __user *tmp_entries; + + if (copy_from_user(&tmp, user_ud, sizeof tmp)) + return -EFAULT; + tmp_entries = compat_ptr(tmp.entries); + if (tmp_entries) + if (!access_ok(VERIFY_WRITE, tmp_entries, + tmp.entry_ct*sizeof(struct unipair))) + return -EFAULT; + switch (cmd) { + case PIO_UNIMAP: + if (!perm) + return -EPERM; + return con_set_unimap(vc, tmp.entry_ct, tmp_entries); + case GIO_UNIMAP: + if (!perm && fg_console != vc->vc_num) + return -EPERM; + return con_get_unimap(vc, tmp.entry_ct, &(user_ud->entry_ct), tmp_entries); + } + return 0; +} + +long vt_compat_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct vc_data *vc = tty->driver_data; + struct console_font_op op; /* used in multiple places here */ + struct kbd_struct *kbd; + unsigned int console; + void __user *up = (void __user *)arg; + int perm; + int ret = 0; + + console = vc->vc_num; + + lock_kernel(); + + if (!vc_cons_allocated(console)) { /* impossible? */ + ret = -ENOIOCTLCMD; + goto out; + } + + /* + * To have permissions to do most of the vt ioctls, we either have + * to be the owner of the tty, or have CAP_SYS_TTY_CONFIG. + */ + perm = 0; + if (current->signal->tty == tty || capable(CAP_SYS_TTY_CONFIG)) + perm = 1; + + kbd = kbd_table + console; + switch (cmd) { + /* + * these need special handlers for incompatible data structures + */ + case PIO_FONTX: + case GIO_FONTX: + ret = compat_fontx_ioctl(cmd, up, perm, &op); + break; + + case KDFONTOP: + ret = compat_kdfontop_ioctl(up, perm, &op, vc); + break; + + case PIO_UNIMAP: + case GIO_UNIMAP: + ret = do_unimap_ioctl(cmd, up, perm, vc); + break; + + /* + * all these treat 'arg' as an integer + */ + case KIOCSOUND: + case KDMKTONE: +#ifdef CONFIG_X86 + case KDADDIO: + case KDDELIO: +#endif + case KDSETMODE: + case KDMAPDISP: + case KDUNMAPDISP: + case KDSKBMODE: + case KDSKBMETA: + case KDSKBLED: + case KDSETLED: + case KDSIGACCEPT: + case VT_ACTIVATE: + case VT_WAITACTIVE: + case VT_RELDISP: + case VT_DISALLOCATE: + case VT_RESIZE: + case VT_RESIZEX: + goto fallback; + + /* + * the rest has a compatible data structure behind arg, + * but we have to convert it to a proper 64 bit pointer. + */ + default: + arg = (unsigned long)compat_ptr(arg); + goto fallback; + } +out: + unlock_kernel(); + return ret; + +fallback: + unlock_kernel(); + return vt_ioctl(tty, file, cmd, arg); +} + + +#endif /* CONFIG_COMPAT */ + + /* * Performs the back end of a vt switch. Called under the console * semaphore. diff --git a/include/linux/tty.h b/include/linux/tty.h index a0e03309a379..f0f43d08d8b8 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -536,5 +536,8 @@ extern int pcxe_open(struct tty_struct *tty, struct file *filp); extern int vt_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg); +extern long vt_compat_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg); + #endif /* __KERNEL__ */ #endif -- cgit v1.2.3 From 9074d963f4a5ab9c45e9edea32c3df4960bc7490 Mon Sep 17 00:00:00 2001 From: Marcin Slusarz Date: Sun, 9 Aug 2009 21:54:03 +0200 Subject: tty: vt: use printk_once Signed-off-by: Marcin Slusarz Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_ioctl.c | 4 +--- drivers/char/vt.c | 6 +----- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index ad6ba4ed2808..8e67d5c642a4 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -393,9 +393,7 @@ void tty_termios_encode_baud_rate(struct ktermios *termios, termios->c_cflag |= (BOTHER << IBSHIFT); #else if (ifound == -1 || ofound == -1) { - static int warned; - if (!warned++) - printk(KERN_WARNING "tty: Unable to return correct " + printk_once(KERN_WARNING "tty: Unable to return correct " "speed data as your architecture needs updating.\n"); } #endif diff --git a/drivers/char/vt.c b/drivers/char/vt.c index 5dfbfa7d7606..0c80c68cd047 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -2129,11 +2129,7 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co currcons = vc->vc_num; if (!vc_cons_allocated(currcons)) { /* could this happen? */ - static int error = 0; - if (!error) { - error = 1; - printk("con_write: tty %d not allocated\n", currcons+1); - } + printk_once("con_write: tty %d not allocated\n", currcons+1); release_console_sem(); return 0; } -- cgit v1.2.3 From 797938b5e33991dadf4dd9228b932cc69c3e905a Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 11 Aug 2009 23:20:41 +0200 Subject: tty: Power: fix suspend vt regression vt_waitactive no longer accepts console parameter as console-1 since commit "vt: add an event interface". It expects console number directly (as viewed by userspace -- counting from 1). Fix a deadlock suspend regression by redefining adding one to vt in vt_move_to_console. Signed-off-by: Jiri Slaby Cc: Alan Cox Cc: Greg Kroah-Hartman Cc: "Rafael J. Wysocki" Signed-off-by: Greg Kroah-Hartman --- drivers/char/vt_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 30b5d128037c..29c651ab0d78 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -1757,7 +1757,7 @@ int vt_move_to_console(unsigned int vt, int alloc) return -EIO; } release_console_sem(); - if (vt_waitactive(vt)) { + if (vt_waitactive(vt + 1)) { pr_debug("Suspend: Can't switch VCs."); return -EINTR; } -- cgit v1.2.3 From 1f5c13fad4ec5617b610e12205902c06298c096a Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 20 Aug 2009 15:23:47 -0400 Subject: TTY: fix typos This patch (as1282) fixes some obvious typos in the TTY core. Signed-off-by: Alan Stern CC: Alan Cox Signed-off-by: Greg Kroah-Hartman --- drivers/char/tty_port.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index c767e30a1425..a4bbb28f10be 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -100,7 +100,7 @@ EXPORT_SYMBOL(tty_port_tty_set); static void tty_port_shutdown(struct tty_port *port) { if (port->ops->shutdown && - test_and_clear_bit(ASYNC_INITIALIZED, &port->flags)) + test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags)) port->ops->shutdown(port); } @@ -311,7 +311,7 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f port->ops->drop(port); return 0; } - set_bit(ASYNC_CLOSING, &port->flags); + set_bit(ASYNCB_CLOSING, &port->flags); tty->closing = 1; spin_unlock_irqrestore(&port->lock, flags); /* Don't block on a stalled port, just pull the chain */ -- cgit v1.2.3 From 90387f5eb08e50d79ab305dab170b4a437d5802c Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 22 Aug 2009 09:04:42 +0200 Subject: tty: riscom8, fix shutdown declaration tty_port_ops.shutdown takes only one parameter: tty port. Remove the second one and use port->tty where needed instead. Signed-off-by: Jiri Slaby Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/char/riscom8.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 7b5623b01903..3c7cf2cf3ea2 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -934,7 +934,7 @@ static void rc_flush_buffer(struct tty_struct *tty) tty_wakeup(tty); } -static void rc_close_port(struct tty_port *port, struct tty_struct *tty) +static void rc_close_port(struct tty_port *port) { unsigned long flags; struct riscom_port *rp = container_of(port, struct riscom_port, port); @@ -969,7 +969,7 @@ static void rc_close_port(struct tty_port *port, struct tty_struct *tty) break; } } - rc_shutdown_port(tty, bp, rp); + rc_shutdown_port(port->tty, bp, rp); spin_unlock_irqrestore(&riscom_lock, flags); } -- cgit v1.2.3 From 7e63d0c453aa3fae714bc679f6768203b5dc9c32 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 6 Sep 2009 23:10:09 +0200 Subject: tty: riscom8, fix tty refcnt Stanse found a tty refcnt leak on one fail path in rc_transmit. Fix that by jumping to the 'out' label. http://stanse.fi.muni.cz/ Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/char/riscom8.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 3c7cf2cf3ea2..3cfa22d469e0 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -467,7 +467,7 @@ static void rc_transmit(struct riscom_board const *bp) rc_out(bp, CD180_CCR, CCR_CORCHG2); port->break_length = 0; } - return; + goto out; } count = CD180_NFIFO; -- cgit v1.2.3 From 502f295f6ca5cd034c69b0662b251ffdeed95d33 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 10 Sep 2009 12:20:07 +0200 Subject: tty: Char: mxser, add support for CP112UL Add support for MOXA:0x1120 pci device. It's a 2-port device and differs in no way from the others. So this turns out to be a trivial pci_device_id change. Increase also the version number. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/char/mxser.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 37058ff7da7d..e218ae29b482 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -48,7 +48,7 @@ #include "mxser.h" -#define MXSER_VERSION "2.0.4" /* 1.12 */ +#define MXSER_VERSION "2.0.5" /* 1.14 */ #define MXSERMAJOR 174 #define MXSER_BOARDS 4 /* Max. boards */ @@ -69,6 +69,7 @@ #define PCI_DEVICE_ID_POS104UL 0x1044 #define PCI_DEVICE_ID_CB108 0x1080 #define PCI_DEVICE_ID_CP102UF 0x1023 +#define PCI_DEVICE_ID_CP112UL 0x1120 #define PCI_DEVICE_ID_CB114 0x1142 #define PCI_DEVICE_ID_CP114UL 0x1143 #define PCI_DEVICE_ID_CB134I 0x1341 @@ -139,7 +140,8 @@ static const struct mxser_cardinfo mxser_cards[] = { { "CP-138U series", 8, }, { "POS-104UL series", 4, }, { "CP-114UL series", 4, }, -/*30*/ { "CP-102UF series", 2, } +/*30*/ { "CP-102UF series", 2, }, + { "CP-112UL series", 2, }, }; /* driver_data correspond to the lines in the structure above @@ -170,6 +172,7 @@ static struct pci_device_id mxser_pcibrds[] = { { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_POS104UL), .driver_data = 28 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP114UL), .driver_data = 29 }, { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP102UF), .driver_data = 30 }, + { PCI_VDEVICE(MOXA, PCI_DEVICE_ID_CP112UL), .driver_data = 31 }, { } }; MODULE_DEVICE_TABLE(pci, mxser_pcibrds); -- cgit v1.2.3 From a75b7b68ef73685784781d6d2bc416b6dac20969 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Thu, 10 Sep 2009 12:20:08 +0200 Subject: tty: Char: mxser, use THRE for ASPP_OQUEUE ioctl In moxa specific ASPP_OQUEUE ioctl command, they apparently want only know whether there is space in transmitter hold register. So switch UART_LSR_TEMT to UART_LSR_THRE in that specific case according to the change in 1.14 moxa drivers. Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/char/mxser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index e218ae29b482..5e28d39b9e81 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -1806,7 +1806,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, lock_kernel(); len = mxser_chars_in_buffer(tty); - lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT; + lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE; len += (lsr ? 0 : 1); unlock_kernel(); -- cgit v1.2.3 From cdd6c482c9ff9c55475ee7392ec8f672eddb7be6 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 21 Sep 2009 12:02:48 +0200 Subject: perf: Do the big rename: Performance Counters -> Performance Events Bye-bye Performance Counters, welcome Performance Events! In the past few months the perfcounters subsystem has grown out its initial role of counting hardware events, and has become (and is becoming) a much broader generic event enumeration, reporting, logging, monitoring, analysis facility. Naming its core object 'perf_counter' and naming the subsystem 'perfcounters' has become more and more of a misnomer. With pending code like hw-breakpoints support the 'counter' name is less and less appropriate. All in one, we've decided to rename the subsystem to 'performance events' and to propagate this rename through all fields, variables and API names. (in an ABI compatible fashion) The word 'event' is also a bit shorter than 'counter' - which makes it slightly more convenient to write/handle as well. Thanks goes to Stephane Eranian who first observed this misnomer and suggested a rename. User-space tooling and ABI compatibility is not affected - this patch should be function-invariant. (Also, defconfigs were not touched to keep the size down.) This patch has been generated via the following script: FILES=$(find * -type f | grep -vE 'oprofile|[^K]config') sed -i \ -e 's/PERF_EVENT_/PERF_RECORD_/g' \ -e 's/PERF_COUNTER/PERF_EVENT/g' \ -e 's/perf_counter/perf_event/g' \ -e 's/nb_counters/nb_events/g' \ -e 's/swcounter/swevent/g' \ -e 's/tpcounter_event/tp_event/g' \ $FILES for N in $(find . -name perf_counter.[ch]); do M=$(echo $N | sed 's/perf_counter/perf_event/g') mv $N $M done FILES=$(find . -name perf_event.*) sed -i \ -e 's/COUNTER_MASK/REG_MASK/g' \ -e 's/COUNTER/EVENT/g' \ -e 's/\/event_id/g' \ -e 's/counter/event/g' \ -e 's/Counter/Event/g' \ $FILES ... to keep it as correct as possible. This script can also be used by anyone who has pending perfcounters patches - it converts a Linux kernel tree over to the new naming. We tried to time this change to the point in time where the amount of pending patches is the smallest: the end of the merge window. Namespace clashes were fixed up in a preparatory patch - and some stylistic fallout will be fixed up in a subsequent patch. ( NOTE: 'counters' are still the proper terminology when we deal with hardware registers - and these sed scripts are a bit over-eager in renaming them. I've undone some of that, but in case there's something left where 'counter' would be better than 'event' we can undo that on an individual basis instead of touching an otherwise nicely automated patch. ) Suggested-by: Stephane Eranian Acked-by: Peter Zijlstra Acked-by: Paul Mackerras Reviewed-by: Arjan van de Ven Cc: Mike Galbraith Cc: Arnaldo Carvalho de Melo Cc: Frederic Weisbecker Cc: Steven Rostedt Cc: Benjamin Herrenschmidt Cc: David Howells Cc: Kyle McMartin Cc: Martin Schwidefsky Cc: "David S. Miller" Cc: Thomas Gleixner Cc: "H. Peter Anvin" Cc: LKML-Reference: Signed-off-by: Ingo Molnar --- arch/arm/include/asm/unistd.h | 2 +- arch/arm/kernel/calls.S | 2 +- arch/blackfin/include/asm/unistd.h | 2 +- arch/blackfin/mach-common/entry.S | 2 +- arch/frv/Kconfig | 2 +- arch/frv/include/asm/perf_counter.h | 17 - arch/frv/include/asm/perf_event.h | 17 + arch/frv/include/asm/unistd.h | 2 +- arch/frv/kernel/entry.S | 2 +- arch/frv/lib/Makefile | 2 +- arch/frv/lib/perf_counter.c | 19 - arch/frv/lib/perf_event.c | 19 + arch/m68k/include/asm/unistd.h | 2 +- arch/m68k/kernel/entry.S | 2 +- arch/m68knommu/kernel/syscalltable.S | 2 +- arch/microblaze/include/asm/unistd.h | 2 +- arch/microblaze/kernel/syscall_table.S | 2 +- arch/mips/include/asm/unistd.h | 6 +- arch/mips/kernel/scall32-o32.S | 2 +- arch/mips/kernel/scall64-64.S | 2 +- arch/mips/kernel/scall64-n32.S | 2 +- arch/mips/kernel/scall64-o32.S | 2 +- arch/mn10300/include/asm/unistd.h | 2 +- arch/mn10300/kernel/entry.S | 2 +- arch/parisc/Kconfig | 2 +- arch/parisc/include/asm/perf_counter.h | 7 - arch/parisc/include/asm/perf_event.h | 7 + arch/parisc/include/asm/unistd.h | 4 +- arch/parisc/kernel/syscall_table.S | 2 +- arch/powerpc/Kconfig | 2 +- arch/powerpc/include/asm/hw_irq.h | 22 +- arch/powerpc/include/asm/paca.h | 2 +- arch/powerpc/include/asm/perf_counter.h | 110 - arch/powerpc/include/asm/perf_event.h | 110 + arch/powerpc/include/asm/systbl.h | 2 +- arch/powerpc/include/asm/unistd.h | 2 +- arch/powerpc/kernel/Makefile | 2 +- arch/powerpc/kernel/asm-offsets.c | 2 +- arch/powerpc/kernel/entry_64.S | 8 +- arch/powerpc/kernel/irq.c | 8 +- arch/powerpc/kernel/mpc7450-pmu.c | 2 +- arch/powerpc/kernel/perf_callchain.c | 2 +- arch/powerpc/kernel/perf_counter.c | 1315 -------- arch/powerpc/kernel/perf_event.c | 1315 ++++++++ arch/powerpc/kernel/power4-pmu.c | 2 +- arch/powerpc/kernel/power5+-pmu.c | 2 +- arch/powerpc/kernel/power5-pmu.c | 2 +- arch/powerpc/kernel/power6-pmu.c | 2 +- arch/powerpc/kernel/power7-pmu.c | 2 +- arch/powerpc/kernel/ppc970-pmu.c | 2 +- arch/powerpc/kernel/time.c | 30 +- arch/powerpc/mm/fault.c | 8 +- arch/powerpc/platforms/Kconfig.cputype | 4 +- arch/s390/Kconfig | 2 +- arch/s390/include/asm/perf_counter.h | 10 - arch/s390/include/asm/perf_event.h | 10 + arch/s390/include/asm/unistd.h | 2 +- arch/s390/kernel/compat_wrapper.S | 8 +- arch/s390/kernel/syscalls.S | 2 +- arch/s390/mm/fault.c | 8 +- arch/sh/Kconfig | 2 +- arch/sh/include/asm/perf_counter.h | 9 - arch/sh/include/asm/perf_event.h | 9 + arch/sh/include/asm/unistd_32.h | 2 +- arch/sh/include/asm/unistd_64.h | 2 +- arch/sh/kernel/syscalls_32.S | 2 +- arch/sh/kernel/syscalls_64.S | 2 +- arch/sh/mm/fault_32.c | 8 +- arch/sh/mm/tlbflush_64.c | 8 +- arch/sparc/Kconfig | 4 +- arch/sparc/include/asm/perf_counter.h | 14 - arch/sparc/include/asm/perf_event.h | 14 + arch/sparc/include/asm/unistd.h | 2 +- arch/sparc/kernel/Makefile | 2 +- arch/sparc/kernel/nmi.c | 4 +- arch/sparc/kernel/pcr.c | 10 +- arch/sparc/kernel/perf_counter.c | 556 ---- arch/sparc/kernel/perf_event.c | 556 ++++ arch/sparc/kernel/systbls_32.S | 2 +- arch/sparc/kernel/systbls_64.S | 4 +- arch/x86/Kconfig | 2 +- arch/x86/ia32/ia32entry.S | 2 +- arch/x86/include/asm/entry_arch.h | 2 +- arch/x86/include/asm/perf_counter.h | 108 - arch/x86/include/asm/perf_event.h | 108 + arch/x86/include/asm/unistd_32.h | 2 +- arch/x86/include/asm/unistd_64.h | 4 +- arch/x86/kernel/apic/apic.c | 6 +- arch/x86/kernel/cpu/Makefile | 2 +- arch/x86/kernel/cpu/common.c | 4 +- arch/x86/kernel/cpu/perf_counter.c | 2298 -------------- arch/x86/kernel/cpu/perf_event.c | 2298 ++++++++++++++ arch/x86/kernel/cpu/perfctr-watchdog.c | 2 +- arch/x86/kernel/entry_64.S | 2 +- arch/x86/kernel/irqinit.c | 2 +- arch/x86/kernel/syscall_table_32.S | 2 +- arch/x86/mm/fault.c | 8 +- arch/x86/oprofile/op_model_ppro.c | 4 +- arch/x86/oprofile/op_x86_model.h | 2 +- drivers/char/sysrq.c | 4 +- fs/exec.c | 6 +- include/asm-generic/unistd.h | 4 +- include/linux/init_task.h | 14 +- include/linux/perf_counter.h | 858 ------ include/linux/perf_event.h | 858 ++++++ include/linux/prctl.h | 4 +- include/linux/sched.h | 12 +- include/linux/syscalls.h | 6 +- include/trace/ftrace.h | 10 +- init/Kconfig | 8 +- kernel/Makefile | 2 +- kernel/exit.c | 8 +- kernel/fork.c | 8 +- kernel/perf_counter.c | 5000 ------------------------------- kernel/perf_event.c | 5000 +++++++++++++++++++++++++++++++ kernel/sched.c | 14 +- kernel/sys.c | 10 +- kernel/sys_ni.c | 2 +- kernel/sysctl.c | 22 +- kernel/timer.c | 4 +- kernel/trace/trace_syscalls.c | 6 +- mm/mmap.c | 6 +- mm/mprotect.c | 4 +- tools/perf/Makefile | 2 +- tools/perf/builtin-annotate.c | 28 +- tools/perf/builtin-record.c | 22 +- tools/perf/builtin-report.c | 48 +- tools/perf/builtin-sched.c | 20 +- tools/perf/builtin-stat.c | 10 +- tools/perf/builtin-timechart.c | 14 +- tools/perf/builtin-top.c | 12 +- tools/perf/builtin-trace.c | 22 +- tools/perf/design.txt | 58 +- tools/perf/perf.h | 12 +- tools/perf/util/event.h | 4 +- tools/perf/util/header.c | 6 +- tools/perf/util/header.h | 8 +- tools/perf/util/parse-events.c | 32 +- tools/perf/util/parse-events.h | 2 +- tools/perf/util/trace-event-info.c | 8 +- tools/perf/util/trace-event.h | 2 +- 141 files changed, 10694 insertions(+), 10694 deletions(-) delete mode 100644 arch/frv/include/asm/perf_counter.h create mode 100644 arch/frv/include/asm/perf_event.h delete mode 100644 arch/frv/lib/perf_counter.c create mode 100644 arch/frv/lib/perf_event.c delete mode 100644 arch/parisc/include/asm/perf_counter.h create mode 100644 arch/parisc/include/asm/perf_event.h delete mode 100644 arch/powerpc/include/asm/perf_counter.h create mode 100644 arch/powerpc/include/asm/perf_event.h delete mode 100644 arch/powerpc/kernel/perf_counter.c create mode 100644 arch/powerpc/kernel/perf_event.c delete mode 100644 arch/s390/include/asm/perf_counter.h create mode 100644 arch/s390/include/asm/perf_event.h delete mode 100644 arch/sh/include/asm/perf_counter.h create mode 100644 arch/sh/include/asm/perf_event.h delete mode 100644 arch/sparc/include/asm/perf_counter.h create mode 100644 arch/sparc/include/asm/perf_event.h delete mode 100644 arch/sparc/kernel/perf_counter.c create mode 100644 arch/sparc/kernel/perf_event.c delete mode 100644 arch/x86/include/asm/perf_counter.h create mode 100644 arch/x86/include/asm/perf_event.h delete mode 100644 arch/x86/kernel/cpu/perf_counter.c create mode 100644 arch/x86/kernel/cpu/perf_event.c delete mode 100644 include/linux/perf_counter.h create mode 100644 include/linux/perf_event.h delete mode 100644 kernel/perf_counter.c create mode 100644 kernel/perf_event.c (limited to 'drivers/char') diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h index 9122c9ee18fb..89f7eade20af 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h @@ -390,7 +390,7 @@ #define __NR_preadv (__NR_SYSCALL_BASE+361) #define __NR_pwritev (__NR_SYSCALL_BASE+362) #define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE+363) -#define __NR_perf_counter_open (__NR_SYSCALL_BASE+364) +#define __NR_perf_event_open (__NR_SYSCALL_BASE+364) /* * The following SWIs are ARM private. diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S index ecfa98954d1d..fafce1b5c69f 100644 --- a/arch/arm/kernel/calls.S +++ b/arch/arm/kernel/calls.S @@ -373,7 +373,7 @@ CALL(sys_preadv) CALL(sys_pwritev) CALL(sys_rt_tgsigqueueinfo) - CALL(sys_perf_counter_open) + CALL(sys_perf_event_open) #ifndef syscalls_counted .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls #define syscalls_counted diff --git a/arch/blackfin/include/asm/unistd.h b/arch/blackfin/include/asm/unistd.h index c8e7ee4768cd..02b1529dad57 100644 --- a/arch/blackfin/include/asm/unistd.h +++ b/arch/blackfin/include/asm/unistd.h @@ -381,7 +381,7 @@ #define __NR_preadv 366 #define __NR_pwritev 367 #define __NR_rt_tgsigqueueinfo 368 -#define __NR_perf_counter_open 369 +#define __NR_perf_event_open 369 #define __NR_syscall 370 #define NR_syscalls __NR_syscall diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S index 01af24cde362..1e7cac23e25f 100644 --- a/arch/blackfin/mach-common/entry.S +++ b/arch/blackfin/mach-common/entry.S @@ -1620,7 +1620,7 @@ ENTRY(_sys_call_table) .long _sys_preadv .long _sys_pwritev .long _sys_rt_tgsigqueueinfo - .long _sys_perf_counter_open + .long _sys_perf_event_open .rept NR_syscalls-(.-_sys_call_table)/4 .long _sys_ni_syscall diff --git a/arch/frv/Kconfig b/arch/frv/Kconfig index b86e19c9b5b0..4b5830bcbe2e 100644 --- a/arch/frv/Kconfig +++ b/arch/frv/Kconfig @@ -7,7 +7,7 @@ config FRV default y select HAVE_IDE select HAVE_ARCH_TRACEHOOK - select HAVE_PERF_COUNTERS + select HAVE_PERF_EVENTS config ZONE_DMA bool diff --git a/arch/frv/include/asm/perf_counter.h b/arch/frv/include/asm/perf_counter.h deleted file mode 100644 index ccf726e61b2e..000000000000 --- a/arch/frv/include/asm/perf_counter.h +++ /dev/null @@ -1,17 +0,0 @@ -/* FRV performance counter support - * - * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. - */ - -#ifndef _ASM_PERF_COUNTER_H -#define _ASM_PERF_COUNTER_H - -#define PERF_COUNTER_INDEX_OFFSET 0 - -#endif /* _ASM_PERF_COUNTER_H */ diff --git a/arch/frv/include/asm/perf_event.h b/arch/frv/include/asm/perf_event.h new file mode 100644 index 000000000000..a69e0155d146 --- /dev/null +++ b/arch/frv/include/asm/perf_event.h @@ -0,0 +1,17 @@ +/* FRV performance event support + * + * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _ASM_PERF_EVENT_H +#define _ASM_PERF_EVENT_H + +#define PERF_EVENT_INDEX_OFFSET 0 + +#endif /* _ASM_PERF_EVENT_H */ diff --git a/arch/frv/include/asm/unistd.h b/arch/frv/include/asm/unistd.h index 4a8fb427ce0a..be6ef0f5cd42 100644 --- a/arch/frv/include/asm/unistd.h +++ b/arch/frv/include/asm/unistd.h @@ -342,7 +342,7 @@ #define __NR_preadv 333 #define __NR_pwritev 334 #define __NR_rt_tgsigqueueinfo 335 -#define __NR_perf_counter_open 336 +#define __NR_perf_event_open 336 #ifdef __KERNEL__ diff --git a/arch/frv/kernel/entry.S b/arch/frv/kernel/entry.S index fde1e446b440..189397ec012a 100644 --- a/arch/frv/kernel/entry.S +++ b/arch/frv/kernel/entry.S @@ -1525,6 +1525,6 @@ sys_call_table: .long sys_preadv .long sys_pwritev .long sys_rt_tgsigqueueinfo /* 335 */ - .long sys_perf_counter_open + .long sys_perf_event_open syscall_table_size = (. - sys_call_table) diff --git a/arch/frv/lib/Makefile b/arch/frv/lib/Makefile index 0a377210c89b..f4709756d0d9 100644 --- a/arch/frv/lib/Makefile +++ b/arch/frv/lib/Makefile @@ -5,4 +5,4 @@ lib-y := \ __ashldi3.o __lshrdi3.o __muldi3.o __ashrdi3.o __negdi2.o __ucmpdi2.o \ checksum.o memcpy.o memset.o atomic-ops.o atomic64-ops.o \ - outsl_ns.o outsl_sw.o insl_ns.o insl_sw.o cache.o perf_counter.o + outsl_ns.o outsl_sw.o insl_ns.o insl_sw.o cache.o perf_event.o diff --git a/arch/frv/lib/perf_counter.c b/arch/frv/lib/perf_counter.c deleted file mode 100644 index 2000feecd571..000000000000 --- a/arch/frv/lib/perf_counter.c +++ /dev/null @@ -1,19 +0,0 @@ -/* Performance counter handling - * - * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public Licence - * as published by the Free Software Foundation; either version - * 2 of the Licence, or (at your option) any later version. - */ - -#include - -/* - * mark the performance counter as pending - */ -void set_perf_counter_pending(void) -{ -} diff --git a/arch/frv/lib/perf_event.c b/arch/frv/lib/perf_event.c new file mode 100644 index 000000000000..9ac5acfd2e91 --- /dev/null +++ b/arch/frv/lib/perf_event.c @@ -0,0 +1,19 @@ +/* Performance event handling + * + * Copyright (C) 2009 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include + +/* + * mark the performance event as pending + */ +void set_perf_event_pending(void) +{ +} diff --git a/arch/m68k/include/asm/unistd.h b/arch/m68k/include/asm/unistd.h index 946d8691f2b0..48b87f5ced50 100644 --- a/arch/m68k/include/asm/unistd.h +++ b/arch/m68k/include/asm/unistd.h @@ -335,7 +335,7 @@ #define __NR_preadv 329 #define __NR_pwritev 330 #define __NR_rt_tgsigqueueinfo 331 -#define __NR_perf_counter_open 332 +#define __NR_perf_event_open 332 #ifdef __KERNEL__ diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index 922f52e7ed1a..c5b33634c980 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -756,5 +756,5 @@ sys_call_table: .long sys_preadv .long sys_pwritev /* 330 */ .long sys_rt_tgsigqueueinfo - .long sys_perf_counter_open + .long sys_perf_event_open diff --git a/arch/m68knommu/kernel/syscalltable.S b/arch/m68knommu/kernel/syscalltable.S index 0ae123e08985..23535cc415ae 100644 --- a/arch/m68knommu/kernel/syscalltable.S +++ b/arch/m68knommu/kernel/syscalltable.S @@ -350,7 +350,7 @@ ENTRY(sys_call_table) .long sys_preadv .long sys_pwritev /* 330 */ .long sys_rt_tgsigqueueinfo - .long sys_perf_counter_open + .long sys_perf_event_open .rept NR_syscalls-(.-sys_call_table)/4 .long sys_ni_syscall diff --git a/arch/microblaze/include/asm/unistd.h b/arch/microblaze/include/asm/unistd.h index 0b852327c0e7..cb05a07e55e9 100644 --- a/arch/microblaze/include/asm/unistd.h +++ b/arch/microblaze/include/asm/unistd.h @@ -381,7 +381,7 @@ #define __NR_preadv 363 /* new */ #define __NR_pwritev 364 /* new */ #define __NR_rt_tgsigqueueinfo 365 /* new */ -#define __NR_perf_counter_open 366 /* new */ +#define __NR_perf_event_open 366 /* new */ #define __NR_syscalls 367 diff --git a/arch/microblaze/kernel/syscall_table.S b/arch/microblaze/kernel/syscall_table.S index 457216097dfd..ecec19155135 100644 --- a/arch/microblaze/kernel/syscall_table.S +++ b/arch/microblaze/kernel/syscall_table.S @@ -370,4 +370,4 @@ ENTRY(sys_call_table) .long sys_ni_syscall .long sys_ni_syscall .long sys_rt_tgsigqueueinfo /* 365 */ - .long sys_perf_counter_open + .long sys_perf_event_open diff --git a/arch/mips/include/asm/unistd.h b/arch/mips/include/asm/unistd.h index e753a777949b..8c9dfa9e9018 100644 --- a/arch/mips/include/asm/unistd.h +++ b/arch/mips/include/asm/unistd.h @@ -353,7 +353,7 @@ #define __NR_preadv (__NR_Linux + 330) #define __NR_pwritev (__NR_Linux + 331) #define __NR_rt_tgsigqueueinfo (__NR_Linux + 332) -#define __NR_perf_counter_open (__NR_Linux + 333) +#define __NR_perf_event_open (__NR_Linux + 333) #define __NR_accept4 (__NR_Linux + 334) /* @@ -664,7 +664,7 @@ #define __NR_preadv (__NR_Linux + 289) #define __NR_pwritev (__NR_Linux + 290) #define __NR_rt_tgsigqueueinfo (__NR_Linux + 291) -#define __NR_perf_counter_open (__NR_Linux + 292) +#define __NR_perf_event_open (__NR_Linux + 292) #define __NR_accept4 (__NR_Linux + 293) /* @@ -979,7 +979,7 @@ #define __NR_preadv (__NR_Linux + 293) #define __NR_pwritev (__NR_Linux + 294) #define __NR_rt_tgsigqueueinfo (__NR_Linux + 295) -#define __NR_perf_counter_open (__NR_Linux + 296) +#define __NR_perf_event_open (__NR_Linux + 296) #define __NR_accept4 (__NR_Linux + 297) /* diff --git a/arch/mips/kernel/scall32-o32.S b/arch/mips/kernel/scall32-o32.S index 7c2de4f091c4..fd2a9bb620d6 100644 --- a/arch/mips/kernel/scall32-o32.S +++ b/arch/mips/kernel/scall32-o32.S @@ -581,7 +581,7 @@ einval: li v0, -ENOSYS sys sys_preadv 6 /* 4330 */ sys sys_pwritev 6 sys sys_rt_tgsigqueueinfo 4 - sys sys_perf_counter_open 5 + sys sys_perf_event_open 5 sys sys_accept4 4 .endm diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S index b97b993846d6..18bf7f32c5e4 100644 --- a/arch/mips/kernel/scall64-64.S +++ b/arch/mips/kernel/scall64-64.S @@ -418,6 +418,6 @@ sys_call_table: PTR sys_preadv PTR sys_pwritev /* 5390 */ PTR sys_rt_tgsigqueueinfo - PTR sys_perf_counter_open + PTR sys_perf_event_open PTR sys_accept4 .size sys_call_table,.-sys_call_table diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 1a6ae124635b..6ebc07976694 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -416,6 +416,6 @@ EXPORT(sysn32_call_table) PTR sys_preadv PTR sys_pwritev PTR compat_sys_rt_tgsigqueueinfo /* 5295 */ - PTR sys_perf_counter_open + PTR sys_perf_event_open PTR sys_accept4 .size sysn32_call_table,.-sysn32_call_table diff --git a/arch/mips/kernel/scall64-o32.S b/arch/mips/kernel/scall64-o32.S index cd31087a651f..9bbf9775e0bd 100644 --- a/arch/mips/kernel/scall64-o32.S +++ b/arch/mips/kernel/scall64-o32.S @@ -536,6 +536,6 @@ sys_call_table: PTR compat_sys_preadv /* 4330 */ PTR compat_sys_pwritev PTR compat_sys_rt_tgsigqueueinfo - PTR sys_perf_counter_open + PTR sys_perf_event_open PTR sys_accept4 .size sys_call_table,.-sys_call_table diff --git a/arch/mn10300/include/asm/unistd.h b/arch/mn10300/include/asm/unistd.h index fad68616af32..2a983931c11f 100644 --- a/arch/mn10300/include/asm/unistd.h +++ b/arch/mn10300/include/asm/unistd.h @@ -347,7 +347,7 @@ #define __NR_preadv 334 #define __NR_pwritev 335 #define __NR_rt_tgsigqueueinfo 336 -#define __NR_perf_counter_open 337 +#define __NR_perf_event_open 337 #ifdef __KERNEL__ diff --git a/arch/mn10300/kernel/entry.S b/arch/mn10300/kernel/entry.S index e0d2563af4f2..a94e7ea3faa6 100644 --- a/arch/mn10300/kernel/entry.S +++ b/arch/mn10300/kernel/entry.S @@ -723,7 +723,7 @@ ENTRY(sys_call_table) .long sys_preadv .long sys_pwritev /* 335 */ .long sys_rt_tgsigqueueinfo - .long sys_perf_counter_open + .long sys_perf_event_open nr_syscalls=(.-sys_call_table)/4 diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index 06f8d5b5b0f9..f388dc68f605 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -16,7 +16,7 @@ config PARISC select RTC_DRV_GENERIC select INIT_ALL_POSSIBLE select BUG - select HAVE_PERF_COUNTERS + select HAVE_PERF_EVENTS select GENERIC_ATOMIC64 if !64BIT help The PA-RISC microprocessor is designed by Hewlett-Packard and used diff --git a/arch/parisc/include/asm/perf_counter.h b/arch/parisc/include/asm/perf_counter.h deleted file mode 100644 index dc9e829f7013..000000000000 --- a/arch/parisc/include/asm/perf_counter.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef __ASM_PARISC_PERF_COUNTER_H -#define __ASM_PARISC_PERF_COUNTER_H - -/* parisc only supports software counters through this interface. */ -static inline void set_perf_counter_pending(void) { } - -#endif /* __ASM_PARISC_PERF_COUNTER_H */ diff --git a/arch/parisc/include/asm/perf_event.h b/arch/parisc/include/asm/perf_event.h new file mode 100644 index 000000000000..cc146427d8f9 --- /dev/null +++ b/arch/parisc/include/asm/perf_event.h @@ -0,0 +1,7 @@ +#ifndef __ASM_PARISC_PERF_EVENT_H +#define __ASM_PARISC_PERF_EVENT_H + +/* parisc only supports software events through this interface. */ +static inline void set_perf_event_pending(void) { } + +#endif /* __ASM_PARISC_PERF_EVENT_H */ diff --git a/arch/parisc/include/asm/unistd.h b/arch/parisc/include/asm/unistd.h index f3d3b8b012c4..cda158318c62 100644 --- a/arch/parisc/include/asm/unistd.h +++ b/arch/parisc/include/asm/unistd.h @@ -810,9 +810,9 @@ #define __NR_preadv (__NR_Linux + 315) #define __NR_pwritev (__NR_Linux + 316) #define __NR_rt_tgsigqueueinfo (__NR_Linux + 317) -#define __NR_perf_counter_open (__NR_Linux + 318) +#define __NR_perf_event_open (__NR_Linux + 318) -#define __NR_Linux_syscalls (__NR_perf_counter_open + 1) +#define __NR_Linux_syscalls (__NR_perf_event_open + 1) #define __IGNORE_select /* newselect */ diff --git a/arch/parisc/kernel/syscall_table.S b/arch/parisc/kernel/syscall_table.S index cf145eb026b3..843f423dec67 100644 --- a/arch/parisc/kernel/syscall_table.S +++ b/arch/parisc/kernel/syscall_table.S @@ -416,7 +416,7 @@ ENTRY_COMP(preadv) /* 315 */ ENTRY_COMP(pwritev) ENTRY_COMP(rt_tgsigqueueinfo) - ENTRY_SAME(perf_counter_open) + ENTRY_SAME(perf_event_open) /* Nothing yet */ diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index 8250902265c6..4fd479059d65 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -129,7 +129,7 @@ config PPC select HAVE_OPROFILE select HAVE_SYSCALL_WRAPPERS if PPC64 select GENERIC_ATOMIC64 if PPC32 - select HAVE_PERF_COUNTERS + select HAVE_PERF_EVENTS config EARLY_PRINTK bool diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h index e73d554538dd..abbc2aaaced5 100644 --- a/arch/powerpc/include/asm/hw_irq.h +++ b/arch/powerpc/include/asm/hw_irq.h @@ -135,43 +135,43 @@ static inline int irqs_disabled_flags(unsigned long flags) */ struct irq_chip; -#ifdef CONFIG_PERF_COUNTERS +#ifdef CONFIG_PERF_EVENTS #ifdef CONFIG_PPC64 -static inline unsigned long test_perf_counter_pending(void) +static inline unsigned long test_perf_event_pending(void) { unsigned long x; asm volatile("lbz %0,%1(13)" : "=r" (x) - : "i" (offsetof(struct paca_struct, perf_counter_pending))); + : "i" (offsetof(struct paca_struct, perf_event_pending))); return x; } -static inline void set_perf_counter_pending(void) +static inline void set_perf_event_pending(void) { asm volatile("stb %0,%1(13)" : : "r" (1), - "i" (offsetof(struct paca_struct, perf_counter_pending))); + "i" (offsetof(struct paca_struct, perf_event_pending))); } -static inline void clear_perf_counter_pending(void) +static inline void clear_perf_event_pending(void) { asm volatile("stb %0,%1(13)" : : "r" (0), - "i" (offsetof(struct paca_struct, perf_counter_pending))); + "i" (offsetof(struct paca_struct, perf_event_pending))); } #endif /* CONFIG_PPC64 */ -#else /* CONFIG_PERF_COUNTERS */ +#else /* CONFIG_PERF_EVENTS */ -static inline unsigned long test_perf_counter_pending(void) +static inline unsigned long test_perf_event_pending(void) { return 0; } -static inline void clear_perf_counter_pending(void) {} -#endif /* CONFIG_PERF_COUNTERS */ +static inline void clear_perf_event_pending(void) {} +#endif /* CONFIG_PERF_EVENTS */ #endif /* __KERNEL__ */ #endif /* _ASM_POWERPC_HW_IRQ_H */ diff --git a/arch/powerpc/include/asm/paca.h b/arch/powerpc/include/asm/paca.h index b634456ea893..154f405b642f 100644 --- a/arch/powerpc/include/asm/paca.h +++ b/arch/powerpc/include/asm/paca.h @@ -122,7 +122,7 @@ struct paca_struct { u8 soft_enabled; /* irq soft-enable flag */ u8 hard_enabled; /* set if irqs are enabled in MSR */ u8 io_sync; /* writel() needs spin_unlock sync */ - u8 perf_counter_pending; /* PM interrupt while soft-disabled */ + u8 perf_event_pending; /* PM interrupt while soft-disabled */ /* Stuff for accurate time accounting */ u64 user_time; /* accumulated usermode TB ticks */ diff --git a/arch/powerpc/include/asm/perf_counter.h b/arch/powerpc/include/asm/perf_counter.h deleted file mode 100644 index 0ea0639fcf75..000000000000 --- a/arch/powerpc/include/asm/perf_counter.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Performance counter support - PowerPC-specific definitions. - * - * Copyright 2008-2009 Paul Mackerras, IBM Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -#include - -#include - -#define MAX_HWCOUNTERS 8 -#define MAX_EVENT_ALTERNATIVES 8 -#define MAX_LIMITED_HWCOUNTERS 2 - -/* - * This struct provides the constants and functions needed to - * describe the PMU on a particular POWER-family CPU. - */ -struct power_pmu { - const char *name; - int n_counter; - int max_alternatives; - unsigned long add_fields; - unsigned long test_adder; - int (*compute_mmcr)(u64 events[], int n_ev, - unsigned int hwc[], unsigned long mmcr[]); - int (*get_constraint)(u64 event, unsigned long *mskp, - unsigned long *valp); - int (*get_alternatives)(u64 event, unsigned int flags, - u64 alt[]); - void (*disable_pmc)(unsigned int pmc, unsigned long mmcr[]); - int (*limited_pmc_event)(u64 event); - u32 flags; - int n_generic; - int *generic_events; - int (*cache_events)[PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX]; -}; - -/* - * Values for power_pmu.flags - */ -#define PPMU_LIMITED_PMC5_6 1 /* PMC5/6 have limited function */ -#define PPMU_ALT_SIPR 2 /* uses alternate posn for SIPR/HV */ - -/* - * Values for flags to get_alternatives() - */ -#define PPMU_LIMITED_PMC_OK 1 /* can put this on a limited PMC */ -#define PPMU_LIMITED_PMC_REQD 2 /* have to put this on a limited PMC */ -#define PPMU_ONLY_COUNT_RUN 4 /* only counting in run state */ - -extern int register_power_pmu(struct power_pmu *); - -struct pt_regs; -extern unsigned long perf_misc_flags(struct pt_regs *regs); -extern unsigned long perf_instruction_pointer(struct pt_regs *regs); - -#define PERF_COUNTER_INDEX_OFFSET 1 - -/* - * Only override the default definitions in include/linux/perf_counter.h - * if we have hardware PMU support. - */ -#ifdef CONFIG_PPC_PERF_CTRS -#define perf_misc_flags(regs) perf_misc_flags(regs) -#endif - -/* - * The power_pmu.get_constraint function returns a 32/64-bit value and - * a 32/64-bit mask that express the constraints between this event and - * other events. - * - * The value and mask are divided up into (non-overlapping) bitfields - * of three different types: - * - * Select field: this expresses the constraint that some set of bits - * in MMCR* needs to be set to a specific value for this event. For a - * select field, the mask contains 1s in every bit of the field, and - * the value contains a unique value for each possible setting of the - * MMCR* bits. The constraint checking code will ensure that two events - * that set the same field in their masks have the same value in their - * value dwords. - * - * Add field: this expresses the constraint that there can be at most - * N events in a particular class. A field of k bits can be used for - * N <= 2^(k-1) - 1. The mask has the most significant bit of the field - * set (and the other bits 0), and the value has only the least significant - * bit of the field set. In addition, the 'add_fields' and 'test_adder' - * in the struct power_pmu for this processor come into play. The - * add_fields value contains 1 in the LSB of the field, and the - * test_adder contains 2^(k-1) - 1 - N in the field. - * - * NAND field: this expresses the constraint that you may not have events - * in all of a set of classes. (For example, on PPC970, you can't select - * events from the FPU, ISU and IDU simultaneously, although any two are - * possible.) For N classes, the field is N+1 bits wide, and each class - * is assigned one bit from the least-significant N bits. The mask has - * only the most-significant bit set, and the value has only the bit - * for the event's class set. The test_adder has the least significant - * bit set in the field. - * - * If an event is not subject to the constraint expressed by a particular - * field, then it will have 0 in both the mask and value for that field. - */ diff --git a/arch/powerpc/include/asm/perf_event.h b/arch/powerpc/include/asm/perf_event.h new file mode 100644 index 000000000000..2499aaadaeb9 --- /dev/null +++ b/arch/powerpc/include/asm/perf_event.h @@ -0,0 +1,110 @@ +/* + * Performance event support - PowerPC-specific definitions. + * + * Copyright 2008-2009 Paul Mackerras, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include + +#include + +#define MAX_HWEVENTS 8 +#define MAX_EVENT_ALTERNATIVES 8 +#define MAX_LIMITED_HWEVENTS 2 + +/* + * This struct provides the constants and functions needed to + * describe the PMU on a particular POWER-family CPU. + */ +struct power_pmu { + const char *name; + int n_event; + int max_alternatives; + unsigned long add_fields; + unsigned long test_adder; + int (*compute_mmcr)(u64 events[], int n_ev, + unsigned int hwc[], unsigned long mmcr[]); + int (*get_constraint)(u64 event_id, unsigned long *mskp, + unsigned long *valp); + int (*get_alternatives)(u64 event_id, unsigned int flags, + u64 alt[]); + void (*disable_pmc)(unsigned int pmc, unsigned long mmcr[]); + int (*limited_pmc_event)(u64 event_id); + u32 flags; + int n_generic; + int *generic_events; + int (*cache_events)[PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX]; +}; + +/* + * Values for power_pmu.flags + */ +#define PPMU_LIMITED_PMC5_6 1 /* PMC5/6 have limited function */ +#define PPMU_ALT_SIPR 2 /* uses alternate posn for SIPR/HV */ + +/* + * Values for flags to get_alternatives() + */ +#define PPMU_LIMITED_PMC_OK 1 /* can put this on a limited PMC */ +#define PPMU_LIMITED_PMC_REQD 2 /* have to put this on a limited PMC */ +#define PPMU_ONLY_COUNT_RUN 4 /* only counting in run state */ + +extern int register_power_pmu(struct power_pmu *); + +struct pt_regs; +extern unsigned long perf_misc_flags(struct pt_regs *regs); +extern unsigned long perf_instruction_pointer(struct pt_regs *regs); + +#define PERF_EVENT_INDEX_OFFSET 1 + +/* + * Only override the default definitions in include/linux/perf_event.h + * if we have hardware PMU support. + */ +#ifdef CONFIG_PPC_PERF_CTRS +#define perf_misc_flags(regs) perf_misc_flags(regs) +#endif + +/* + * The power_pmu.get_constraint function returns a 32/64-bit value and + * a 32/64-bit mask that express the constraints between this event_id and + * other events. + * + * The value and mask are divided up into (non-overlapping) bitfields + * of three different types: + * + * Select field: this expresses the constraint that some set of bits + * in MMCR* needs to be set to a specific value for this event_id. For a + * select field, the mask contains 1s in every bit of the field, and + * the value contains a unique value for each possible setting of the + * MMCR* bits. The constraint checking code will ensure that two events + * that set the same field in their masks have the same value in their + * value dwords. + * + * Add field: this expresses the constraint that there can be at most + * N events in a particular class. A field of k bits can be used for + * N <= 2^(k-1) - 1. The mask has the most significant bit of the field + * set (and the other bits 0), and the value has only the least significant + * bit of the field set. In addition, the 'add_fields' and 'test_adder' + * in the struct power_pmu for this processor come into play. The + * add_fields value contains 1 in the LSB of the field, and the + * test_adder contains 2^(k-1) - 1 - N in the field. + * + * NAND field: this expresses the constraint that you may not have events + * in all of a set of classes. (For example, on PPC970, you can't select + * events from the FPU, ISU and IDU simultaneously, although any two are + * possible.) For N classes, the field is N+1 bits wide, and each class + * is assigned one bit from the least-significant N bits. The mask has + * only the most-significant bit set, and the value has only the bit + * for the event_id's class set. The test_adder has the least significant + * bit set in the field. + * + * If an event_id is not subject to the constraint expressed by a particular + * field, then it will have 0 in both the mask and value for that field. + */ diff --git a/arch/powerpc/include/asm/systbl.h b/arch/powerpc/include/asm/systbl.h index ed24bd92fe49..c7d671a7d9a1 100644 --- a/arch/powerpc/include/asm/systbl.h +++ b/arch/powerpc/include/asm/systbl.h @@ -322,7 +322,7 @@ SYSCALL_SPU(epoll_create1) SYSCALL_SPU(dup3) SYSCALL_SPU(pipe2) SYSCALL(inotify_init1) -SYSCALL_SPU(perf_counter_open) +SYSCALL_SPU(perf_event_open) COMPAT_SYS_SPU(preadv) COMPAT_SYS_SPU(pwritev) COMPAT_SYS(rt_tgsigqueueinfo) diff --git a/arch/powerpc/include/asm/unistd.h b/arch/powerpc/include/asm/unistd.h index cef080bfc607..f6ca76176766 100644 --- a/arch/powerpc/include/asm/unistd.h +++ b/arch/powerpc/include/asm/unistd.h @@ -341,7 +341,7 @@ #define __NR_dup3 316 #define __NR_pipe2 317 #define __NR_inotify_init1 318 -#define __NR_perf_counter_open 319 +#define __NR_perf_event_open 319 #define __NR_preadv 320 #define __NR_pwritev 321 #define __NR_rt_tgsigqueueinfo 322 diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile index 569f79ccd310..b23664a0b86c 100644 --- a/arch/powerpc/kernel/Makefile +++ b/arch/powerpc/kernel/Makefile @@ -97,7 +97,7 @@ obj64-$(CONFIG_AUDIT) += compat_audit.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o -obj-$(CONFIG_PPC_PERF_CTRS) += perf_counter.o perf_callchain.o +obj-$(CONFIG_PPC_PERF_CTRS) += perf_event.o perf_callchain.o obj64-$(CONFIG_PPC_PERF_CTRS) += power4-pmu.o ppc970-pmu.o power5-pmu.o \ power5+-pmu.o power6-pmu.o power7-pmu.o obj32-$(CONFIG_PPC_PERF_CTRS) += mpc7450-pmu.o diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index f0df285f0f87..0812b0f414bb 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -133,7 +133,7 @@ int main(void) DEFINE(PACAKMSR, offsetof(struct paca_struct, kernel_msr)); DEFINE(PACASOFTIRQEN, offsetof(struct paca_struct, soft_enabled)); DEFINE(PACAHARDIRQEN, offsetof(struct paca_struct, hard_enabled)); - DEFINE(PACAPERFPEND, offsetof(struct paca_struct, perf_counter_pending)); + DEFINE(PACAPERFPEND, offsetof(struct paca_struct, perf_event_pending)); DEFINE(PACACONTEXTID, offsetof(struct paca_struct, context.id)); #ifdef CONFIG_PPC_MM_SLICES DEFINE(PACALOWSLICESPSIZE, offsetof(struct paca_struct, diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S index 66bcda34a6bb..900e0eea0099 100644 --- a/arch/powerpc/kernel/entry_64.S +++ b/arch/powerpc/kernel/entry_64.S @@ -556,14 +556,14 @@ ALT_FW_FTR_SECTION_END_IFCLR(FW_FEATURE_ISERIES) 2: TRACE_AND_RESTORE_IRQ(r5); -#ifdef CONFIG_PERF_COUNTERS - /* check paca->perf_counter_pending if we're enabling ints */ +#ifdef CONFIG_PERF_EVENTS + /* check paca->perf_event_pending if we're enabling ints */ lbz r3,PACAPERFPEND(r13) and. r3,r3,r5 beq 27f - bl .perf_counter_do_pending + bl .perf_event_do_pending 27: -#endif /* CONFIG_PERF_COUNTERS */ +#endif /* CONFIG_PERF_EVENTS */ /* extract EE bit and use it to restore paca->hard_enabled */ ld r3,_MSR(r1) diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c index f7f376ea7b17..e5d121177984 100644 --- a/arch/powerpc/kernel/irq.c +++ b/arch/powerpc/kernel/irq.c @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include #include @@ -138,9 +138,9 @@ notrace void raw_local_irq_restore(unsigned long en) } #endif /* CONFIG_PPC_STD_MMU_64 */ - if (test_perf_counter_pending()) { - clear_perf_counter_pending(); - perf_counter_do_pending(); + if (test_perf_event_pending()) { + clear_perf_event_pending(); + perf_event_do_pending(); } /* diff --git a/arch/powerpc/kernel/mpc7450-pmu.c b/arch/powerpc/kernel/mpc7450-pmu.c index cc466d039af6..09d72028f317 100644 --- a/arch/powerpc/kernel/mpc7450-pmu.c +++ b/arch/powerpc/kernel/mpc7450-pmu.c @@ -9,7 +9,7 @@ * 2 of the License, or (at your option) any later version. */ #include -#include +#include #include #include diff --git a/arch/powerpc/kernel/perf_callchain.c b/arch/powerpc/kernel/perf_callchain.c index f74b62c67511..0a03cf70d247 100644 --- a/arch/powerpc/kernel/perf_callchain.c +++ b/arch/powerpc/kernel/perf_callchain.c @@ -10,7 +10,7 @@ */ #include #include -#include +#include #include #include #include diff --git a/arch/powerpc/kernel/perf_counter.c b/arch/powerpc/kernel/perf_counter.c deleted file mode 100644 index 5ccf9bca96c0..000000000000 --- a/arch/powerpc/kernel/perf_counter.c +++ /dev/null @@ -1,1315 +0,0 @@ -/* - * Performance counter support - powerpc architecture code - * - * Copyright 2008-2009 Paul Mackerras, IBM Corporation. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct cpu_hw_counters { - int n_counters; - int n_percpu; - int disabled; - int n_added; - int n_limited; - u8 pmcs_enabled; - struct perf_counter *counter[MAX_HWCOUNTERS]; - u64 events[MAX_HWCOUNTERS]; - unsigned int flags[MAX_HWCOUNTERS]; - unsigned long mmcr[3]; - struct perf_counter *limited_counter[MAX_LIMITED_HWCOUNTERS]; - u8 limited_hwidx[MAX_LIMITED_HWCOUNTERS]; - u64 alternatives[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES]; - unsigned long amasks[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES]; - unsigned long avalues[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES]; -}; -DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters); - -struct power_pmu *ppmu; - -/* - * Normally, to ignore kernel events we set the FCS (freeze counters - * in supervisor mode) bit in MMCR0, but if the kernel runs with the - * hypervisor bit set in the MSR, or if we are running on a processor - * where the hypervisor bit is forced to 1 (as on Apple G5 processors), - * then we need to use the FCHV bit to ignore kernel events. - */ -static unsigned int freeze_counters_kernel = MMCR0_FCS; - -/* - * 32-bit doesn't have MMCRA but does have an MMCR2, - * and a few other names are different. - */ -#ifdef CONFIG_PPC32 - -#define MMCR0_FCHV 0 -#define MMCR0_PMCjCE MMCR0_PMCnCE - -#define SPRN_MMCRA SPRN_MMCR2 -#define MMCRA_SAMPLE_ENABLE 0 - -static inline unsigned long perf_ip_adjust(struct pt_regs *regs) -{ - return 0; -} -static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp) { } -static inline u32 perf_get_misc_flags(struct pt_regs *regs) -{ - return 0; -} -static inline void perf_read_regs(struct pt_regs *regs) { } -static inline int perf_intr_is_nmi(struct pt_regs *regs) -{ - return 0; -} - -#endif /* CONFIG_PPC32 */ - -/* - * Things that are specific to 64-bit implementations. - */ -#ifdef CONFIG_PPC64 - -static inline unsigned long perf_ip_adjust(struct pt_regs *regs) -{ - unsigned long mmcra = regs->dsisr; - - if ((mmcra & MMCRA_SAMPLE_ENABLE) && !(ppmu->flags & PPMU_ALT_SIPR)) { - unsigned long slot = (mmcra & MMCRA_SLOT) >> MMCRA_SLOT_SHIFT; - if (slot > 1) - return 4 * (slot - 1); - } - return 0; -} - -/* - * The user wants a data address recorded. - * If we're not doing instruction sampling, give them the SDAR - * (sampled data address). If we are doing instruction sampling, then - * only give them the SDAR if it corresponds to the instruction - * pointed to by SIAR; this is indicated by the [POWER6_]MMCRA_SDSYNC - * bit in MMCRA. - */ -static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp) -{ - unsigned long mmcra = regs->dsisr; - unsigned long sdsync = (ppmu->flags & PPMU_ALT_SIPR) ? - POWER6_MMCRA_SDSYNC : MMCRA_SDSYNC; - - if (!(mmcra & MMCRA_SAMPLE_ENABLE) || (mmcra & sdsync)) - *addrp = mfspr(SPRN_SDAR); -} - -static inline u32 perf_get_misc_flags(struct pt_regs *regs) -{ - unsigned long mmcra = regs->dsisr; - - if (TRAP(regs) != 0xf00) - return 0; /* not a PMU interrupt */ - - if (ppmu->flags & PPMU_ALT_SIPR) { - if (mmcra & POWER6_MMCRA_SIHV) - return PERF_EVENT_MISC_HYPERVISOR; - return (mmcra & POWER6_MMCRA_SIPR) ? - PERF_EVENT_MISC_USER : PERF_EVENT_MISC_KERNEL; - } - if (mmcra & MMCRA_SIHV) - return PERF_EVENT_MISC_HYPERVISOR; - return (mmcra & MMCRA_SIPR) ? PERF_EVENT_MISC_USER : - PERF_EVENT_MISC_KERNEL; -} - -/* - * Overload regs->dsisr to store MMCRA so we only need to read it once - * on each interrupt. - */ -static inline void perf_read_regs(struct pt_regs *regs) -{ - regs->dsisr = mfspr(SPRN_MMCRA); -} - -/* - * If interrupts were soft-disabled when a PMU interrupt occurs, treat - * it as an NMI. - */ -static inline int perf_intr_is_nmi(struct pt_regs *regs) -{ - return !regs->softe; -} - -#endif /* CONFIG_PPC64 */ - -static void perf_counter_interrupt(struct pt_regs *regs); - -void perf_counter_print_debug(void) -{ -} - -/* - * Read one performance monitor counter (PMC). - */ -static unsigned long read_pmc(int idx) -{ - unsigned long val; - - switch (idx) { - case 1: - val = mfspr(SPRN_PMC1); - break; - case 2: - val = mfspr(SPRN_PMC2); - break; - case 3: - val = mfspr(SPRN_PMC3); - break; - case 4: - val = mfspr(SPRN_PMC4); - break; - case 5: - val = mfspr(SPRN_PMC5); - break; - case 6: - val = mfspr(SPRN_PMC6); - break; -#ifdef CONFIG_PPC64 - case 7: - val = mfspr(SPRN_PMC7); - break; - case 8: - val = mfspr(SPRN_PMC8); - break; -#endif /* CONFIG_PPC64 */ - default: - printk(KERN_ERR "oops trying to read PMC%d\n", idx); - val = 0; - } - return val; -} - -/* - * Write one PMC. - */ -static void write_pmc(int idx, unsigned long val) -{ - switch (idx) { - case 1: - mtspr(SPRN_PMC1, val); - break; - case 2: - mtspr(SPRN_PMC2, val); - break; - case 3: - mtspr(SPRN_PMC3, val); - break; - case 4: - mtspr(SPRN_PMC4, val); - break; - case 5: - mtspr(SPRN_PMC5, val); - break; - case 6: - mtspr(SPRN_PMC6, val); - break; -#ifdef CONFIG_PPC64 - case 7: - mtspr(SPRN_PMC7, val); - break; - case 8: - mtspr(SPRN_PMC8, val); - break; -#endif /* CONFIG_PPC64 */ - default: - printk(KERN_ERR "oops trying to write PMC%d\n", idx); - } -} - -/* - * Check if a set of events can all go on the PMU at once. - * If they can't, this will look at alternative codes for the events - * and see if any combination of alternative codes is feasible. - * The feasible set is returned in event[]. - */ -static int power_check_constraints(struct cpu_hw_counters *cpuhw, - u64 event[], unsigned int cflags[], - int n_ev) -{ - unsigned long mask, value, nv; - unsigned long smasks[MAX_HWCOUNTERS], svalues[MAX_HWCOUNTERS]; - int n_alt[MAX_HWCOUNTERS], choice[MAX_HWCOUNTERS]; - int i, j; - unsigned long addf = ppmu->add_fields; - unsigned long tadd = ppmu->test_adder; - - if (n_ev > ppmu->n_counter) - return -1; - - /* First see if the events will go on as-is */ - for (i = 0; i < n_ev; ++i) { - if ((cflags[i] & PPMU_LIMITED_PMC_REQD) - && !ppmu->limited_pmc_event(event[i])) { - ppmu->get_alternatives(event[i], cflags[i], - cpuhw->alternatives[i]); - event[i] = cpuhw->alternatives[i][0]; - } - if (ppmu->get_constraint(event[i], &cpuhw->amasks[i][0], - &cpuhw->avalues[i][0])) - return -1; - } - value = mask = 0; - for (i = 0; i < n_ev; ++i) { - nv = (value | cpuhw->avalues[i][0]) + - (value & cpuhw->avalues[i][0] & addf); - if ((((nv + tadd) ^ value) & mask) != 0 || - (((nv + tadd) ^ cpuhw->avalues[i][0]) & - cpuhw->amasks[i][0]) != 0) - break; - value = nv; - mask |= cpuhw->amasks[i][0]; - } - if (i == n_ev) - return 0; /* all OK */ - - /* doesn't work, gather alternatives... */ - if (!ppmu->get_alternatives) - return -1; - for (i = 0; i < n_ev; ++i) { - choice[i] = 0; - n_alt[i] = ppmu->get_alternatives(event[i], cflags[i], - cpuhw->alternatives[i]); - for (j = 1; j < n_alt[i]; ++j) - ppmu->get_constraint(cpuhw->alternatives[i][j], - &cpuhw->amasks[i][j], - &cpuhw->avalues[i][j]); - } - - /* enumerate all possibilities and see if any will work */ - i = 0; - j = -1; - value = mask = nv = 0; - while (i < n_ev) { - if (j >= 0) { - /* we're backtracking, restore context */ - value = svalues[i]; - mask = smasks[i]; - j = choice[i]; - } - /* - * See if any alternative k for event i, - * where k > j, will satisfy the constraints. - */ - while (++j < n_alt[i]) { - nv = (value | cpuhw->avalues[i][j]) + - (value & cpuhw->avalues[i][j] & addf); - if ((((nv + tadd) ^ value) & mask) == 0 && - (((nv + tadd) ^ cpuhw->avalues[i][j]) - & cpuhw->amasks[i][j]) == 0) - break; - } - if (j >= n_alt[i]) { - /* - * No feasible alternative, backtrack - * to event i-1 and continue enumerating its - * alternatives from where we got up to. - */ - if (--i < 0) - return -1; - } else { - /* - * Found a feasible alternative for event i, - * remember where we got up to with this event, - * go on to the next event, and start with - * the first alternative for it. - */ - choice[i] = j; - svalues[i] = value; - smasks[i] = mask; - value = nv; - mask |= cpuhw->amasks[i][j]; - ++i; - j = -1; - } - } - - /* OK, we have a feasible combination, tell the caller the solution */ - for (i = 0; i < n_ev; ++i) - event[i] = cpuhw->alternatives[i][choice[i]]; - return 0; -} - -/* - * Check if newly-added counters have consistent settings for - * exclude_{user,kernel,hv} with each other and any previously - * added counters. - */ -static int check_excludes(struct perf_counter **ctrs, unsigned int cflags[], - int n_prev, int n_new) -{ - int eu = 0, ek = 0, eh = 0; - int i, n, first; - struct perf_counter *counter; - - n = n_prev + n_new; - if (n <= 1) - return 0; - - first = 1; - for (i = 0; i < n; ++i) { - if (cflags[i] & PPMU_LIMITED_PMC_OK) { - cflags[i] &= ~PPMU_LIMITED_PMC_REQD; - continue; - } - counter = ctrs[i]; - if (first) { - eu = counter->attr.exclude_user; - ek = counter->attr.exclude_kernel; - eh = counter->attr.exclude_hv; - first = 0; - } else if (counter->attr.exclude_user != eu || - counter->attr.exclude_kernel != ek || - counter->attr.exclude_hv != eh) { - return -EAGAIN; - } - } - - if (eu || ek || eh) - for (i = 0; i < n; ++i) - if (cflags[i] & PPMU_LIMITED_PMC_OK) - cflags[i] |= PPMU_LIMITED_PMC_REQD; - - return 0; -} - -static void power_pmu_read(struct perf_counter *counter) -{ - s64 val, delta, prev; - - if (!counter->hw.idx) - return; - /* - * Performance monitor interrupts come even when interrupts - * are soft-disabled, as long as interrupts are hard-enabled. - * Therefore we treat them like NMIs. - */ - do { - prev = atomic64_read(&counter->hw.prev_count); - barrier(); - val = read_pmc(counter->hw.idx); - } while (atomic64_cmpxchg(&counter->hw.prev_count, prev, val) != prev); - - /* The counters are only 32 bits wide */ - delta = (val - prev) & 0xfffffffful; - atomic64_add(delta, &counter->count); - atomic64_sub(delta, &counter->hw.period_left); -} - -/* - * On some machines, PMC5 and PMC6 can't be written, don't respect - * the freeze conditions, and don't generate interrupts. This tells - * us if `counter' is using such a PMC. - */ -static int is_limited_pmc(int pmcnum) -{ - return (ppmu->flags & PPMU_LIMITED_PMC5_6) - && (pmcnum == 5 || pmcnum == 6); -} - -static void freeze_limited_counters(struct cpu_hw_counters *cpuhw, - unsigned long pmc5, unsigned long pmc6) -{ - struct perf_counter *counter; - u64 val, prev, delta; - int i; - - for (i = 0; i < cpuhw->n_limited; ++i) { - counter = cpuhw->limited_counter[i]; - if (!counter->hw.idx) - continue; - val = (counter->hw.idx == 5) ? pmc5 : pmc6; - prev = atomic64_read(&counter->hw.prev_count); - counter->hw.idx = 0; - delta = (val - prev) & 0xfffffffful; - atomic64_add(delta, &counter->count); - } -} - -static void thaw_limited_counters(struct cpu_hw_counters *cpuhw, - unsigned long pmc5, unsigned long pmc6) -{ - struct perf_counter *counter; - u64 val; - int i; - - for (i = 0; i < cpuhw->n_limited; ++i) { - counter = cpuhw->limited_counter[i]; - counter->hw.idx = cpuhw->limited_hwidx[i]; - val = (counter->hw.idx == 5) ? pmc5 : pmc6; - atomic64_set(&counter->hw.prev_count, val); - perf_counter_update_userpage(counter); - } -} - -/* - * Since limited counters don't respect the freeze conditions, we - * have to read them immediately after freezing or unfreezing the - * other counters. We try to keep the values from the limited - * counters as consistent as possible by keeping the delay (in - * cycles and instructions) between freezing/unfreezing and reading - * the limited counters as small and consistent as possible. - * Therefore, if any limited counters are in use, we read them - * both, and always in the same order, to minimize variability, - * and do it inside the same asm that writes MMCR0. - */ -static void write_mmcr0(struct cpu_hw_counters *cpuhw, unsigned long mmcr0) -{ - unsigned long pmc5, pmc6; - - if (!cpuhw->n_limited) { - mtspr(SPRN_MMCR0, mmcr0); - return; - } - - /* - * Write MMCR0, then read PMC5 and PMC6 immediately. - * To ensure we don't get a performance monitor interrupt - * between writing MMCR0 and freezing/thawing the limited - * counters, we first write MMCR0 with the counter overflow - * interrupt enable bits turned off. - */ - asm volatile("mtspr %3,%2; mfspr %0,%4; mfspr %1,%5" - : "=&r" (pmc5), "=&r" (pmc6) - : "r" (mmcr0 & ~(MMCR0_PMC1CE | MMCR0_PMCjCE)), - "i" (SPRN_MMCR0), - "i" (SPRN_PMC5), "i" (SPRN_PMC6)); - - if (mmcr0 & MMCR0_FC) - freeze_limited_counters(cpuhw, pmc5, pmc6); - else - thaw_limited_counters(cpuhw, pmc5, pmc6); - - /* - * Write the full MMCR0 including the counter overflow interrupt - * enable bits, if necessary. - */ - if (mmcr0 & (MMCR0_PMC1CE | MMCR0_PMCjCE)) - mtspr(SPRN_MMCR0, mmcr0); -} - -/* - * Disable all counters to prevent PMU interrupts and to allow - * counters to be added or removed. - */ -void hw_perf_disable(void) -{ - struct cpu_hw_counters *cpuhw; - unsigned long flags; - - if (!ppmu) - return; - local_irq_save(flags); - cpuhw = &__get_cpu_var(cpu_hw_counters); - - if (!cpuhw->disabled) { - cpuhw->disabled = 1; - cpuhw->n_added = 0; - - /* - * Check if we ever enabled the PMU on this cpu. - */ - if (!cpuhw->pmcs_enabled) { - ppc_enable_pmcs(); - cpuhw->pmcs_enabled = 1; - } - - /* - * Disable instruction sampling if it was enabled - */ - if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { - mtspr(SPRN_MMCRA, - cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); - mb(); - } - - /* - * Set the 'freeze counters' bit. - * The barrier is to make sure the mtspr has been - * executed and the PMU has frozen the counters - * before we return. - */ - write_mmcr0(cpuhw, mfspr(SPRN_MMCR0) | MMCR0_FC); - mb(); - } - local_irq_restore(flags); -} - -/* - * Re-enable all counters if disable == 0. - * If we were previously disabled and counters were added, then - * put the new config on the PMU. - */ -void hw_perf_enable(void) -{ - struct perf_counter *counter; - struct cpu_hw_counters *cpuhw; - unsigned long flags; - long i; - unsigned long val; - s64 left; - unsigned int hwc_index[MAX_HWCOUNTERS]; - int n_lim; - int idx; - - if (!ppmu) - return; - local_irq_save(flags); - cpuhw = &__get_cpu_var(cpu_hw_counters); - if (!cpuhw->disabled) { - local_irq_restore(flags); - return; - } - cpuhw->disabled = 0; - - /* - * If we didn't change anything, or only removed counters, - * no need to recalculate MMCR* settings and reset the PMCs. - * Just reenable the PMU with the current MMCR* settings - * (possibly updated for removal of counters). - */ - if (!cpuhw->n_added) { - mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); - mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); - if (cpuhw->n_counters == 0) - ppc_set_pmu_inuse(0); - goto out_enable; - } - - /* - * Compute MMCR* values for the new set of counters - */ - if (ppmu->compute_mmcr(cpuhw->events, cpuhw->n_counters, hwc_index, - cpuhw->mmcr)) { - /* shouldn't ever get here */ - printk(KERN_ERR "oops compute_mmcr failed\n"); - goto out; - } - - /* - * Add in MMCR0 freeze bits corresponding to the - * attr.exclude_* bits for the first counter. - * We have already checked that all counters have the - * same values for these bits as the first counter. - */ - counter = cpuhw->counter[0]; - if (counter->attr.exclude_user) - cpuhw->mmcr[0] |= MMCR0_FCP; - if (counter->attr.exclude_kernel) - cpuhw->mmcr[0] |= freeze_counters_kernel; - if (counter->attr.exclude_hv) - cpuhw->mmcr[0] |= MMCR0_FCHV; - - /* - * Write the new configuration to MMCR* with the freeze - * bit set and set the hardware counters to their initial values. - * Then unfreeze the counters. - */ - ppc_set_pmu_inuse(1); - mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); - mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); - mtspr(SPRN_MMCR0, (cpuhw->mmcr[0] & ~(MMCR0_PMC1CE | MMCR0_PMCjCE)) - | MMCR0_FC); - - /* - * Read off any pre-existing counters that need to move - * to another PMC. - */ - for (i = 0; i < cpuhw->n_counters; ++i) { - counter = cpuhw->counter[i]; - if (counter->hw.idx && counter->hw.idx != hwc_index[i] + 1) { - power_pmu_read(counter); - write_pmc(counter->hw.idx, 0); - counter->hw.idx = 0; - } - } - - /* - * Initialize the PMCs for all the new and moved counters. - */ - cpuhw->n_limited = n_lim = 0; - for (i = 0; i < cpuhw->n_counters; ++i) { - counter = cpuhw->counter[i]; - if (counter->hw.idx) - continue; - idx = hwc_index[i] + 1; - if (is_limited_pmc(idx)) { - cpuhw->limited_counter[n_lim] = counter; - cpuhw->limited_hwidx[n_lim] = idx; - ++n_lim; - continue; - } - val = 0; - if (counter->hw.sample_period) { - left = atomic64_read(&counter->hw.period_left); - if (left < 0x80000000L) - val = 0x80000000L - left; - } - atomic64_set(&counter->hw.prev_count, val); - counter->hw.idx = idx; - write_pmc(idx, val); - perf_counter_update_userpage(counter); - } - cpuhw->n_limited = n_lim; - cpuhw->mmcr[0] |= MMCR0_PMXE | MMCR0_FCECE; - - out_enable: - mb(); - write_mmcr0(cpuhw, cpuhw->mmcr[0]); - - /* - * Enable instruction sampling if necessary - */ - if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { - mb(); - mtspr(SPRN_MMCRA, cpuhw->mmcr[2]); - } - - out: - local_irq_restore(flags); -} - -static int collect_events(struct perf_counter *group, int max_count, - struct perf_counter *ctrs[], u64 *events, - unsigned int *flags) -{ - int n = 0; - struct perf_counter *counter; - - if (!is_software_counter(group)) { - if (n >= max_count) - return -1; - ctrs[n] = group; - flags[n] = group->hw.counter_base; - events[n++] = group->hw.config; - } - list_for_each_entry(counter, &group->sibling_list, list_entry) { - if (!is_software_counter(counter) && - counter->state != PERF_COUNTER_STATE_OFF) { - if (n >= max_count) - return -1; - ctrs[n] = counter; - flags[n] = counter->hw.counter_base; - events[n++] = counter->hw.config; - } - } - return n; -} - -static void counter_sched_in(struct perf_counter *counter, int cpu) -{ - counter->state = PERF_COUNTER_STATE_ACTIVE; - counter->oncpu = cpu; - counter->tstamp_running += counter->ctx->time - counter->tstamp_stopped; - if (is_software_counter(counter)) - counter->pmu->enable(counter); -} - -/* - * Called to enable a whole group of counters. - * Returns 1 if the group was enabled, or -EAGAIN if it could not be. - * Assumes the caller has disabled interrupts and has - * frozen the PMU with hw_perf_save_disable. - */ -int hw_perf_group_sched_in(struct perf_counter *group_leader, - struct perf_cpu_context *cpuctx, - struct perf_counter_context *ctx, int cpu) -{ - struct cpu_hw_counters *cpuhw; - long i, n, n0; - struct perf_counter *sub; - - if (!ppmu) - return 0; - cpuhw = &__get_cpu_var(cpu_hw_counters); - n0 = cpuhw->n_counters; - n = collect_events(group_leader, ppmu->n_counter - n0, - &cpuhw->counter[n0], &cpuhw->events[n0], - &cpuhw->flags[n0]); - if (n < 0) - return -EAGAIN; - if (check_excludes(cpuhw->counter, cpuhw->flags, n0, n)) - return -EAGAIN; - i = power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n + n0); - if (i < 0) - return -EAGAIN; - cpuhw->n_counters = n0 + n; - cpuhw->n_added += n; - - /* - * OK, this group can go on; update counter states etc., - * and enable any software counters - */ - for (i = n0; i < n0 + n; ++i) - cpuhw->counter[i]->hw.config = cpuhw->events[i]; - cpuctx->active_oncpu += n; - n = 1; - counter_sched_in(group_leader, cpu); - list_for_each_entry(sub, &group_leader->sibling_list, list_entry) { - if (sub->state != PERF_COUNTER_STATE_OFF) { - counter_sched_in(sub, cpu); - ++n; - } - } - ctx->nr_active += n; - - return 1; -} - -/* - * Add a counter to the PMU. - * If all counters are not already frozen, then we disable and - * re-enable the PMU in order to get hw_perf_enable to do the - * actual work of reconfiguring the PMU. - */ -static int power_pmu_enable(struct perf_counter *counter) -{ - struct cpu_hw_counters *cpuhw; - unsigned long flags; - int n0; - int ret = -EAGAIN; - - local_irq_save(flags); - perf_disable(); - - /* - * Add the counter to the list (if there is room) - * and check whether the total set is still feasible. - */ - cpuhw = &__get_cpu_var(cpu_hw_counters); - n0 = cpuhw->n_counters; - if (n0 >= ppmu->n_counter) - goto out; - cpuhw->counter[n0] = counter; - cpuhw->events[n0] = counter->hw.config; - cpuhw->flags[n0] = counter->hw.counter_base; - if (check_excludes(cpuhw->counter, cpuhw->flags, n0, 1)) - goto out; - if (power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n0 + 1)) - goto out; - - counter->hw.config = cpuhw->events[n0]; - ++cpuhw->n_counters; - ++cpuhw->n_added; - - ret = 0; - out: - perf_enable(); - local_irq_restore(flags); - return ret; -} - -/* - * Remove a counter from the PMU. - */ -static void power_pmu_disable(struct perf_counter *counter) -{ - struct cpu_hw_counters *cpuhw; - long i; - unsigned long flags; - - local_irq_save(flags); - perf_disable(); - - power_pmu_read(counter); - - cpuhw = &__get_cpu_var(cpu_hw_counters); - for (i = 0; i < cpuhw->n_counters; ++i) { - if (counter == cpuhw->counter[i]) { - while (++i < cpuhw->n_counters) - cpuhw->counter[i-1] = cpuhw->counter[i]; - --cpuhw->n_counters; - ppmu->disable_pmc(counter->hw.idx - 1, cpuhw->mmcr); - if (counter->hw.idx) { - write_pmc(counter->hw.idx, 0); - counter->hw.idx = 0; - } - perf_counter_update_userpage(counter); - break; - } - } - for (i = 0; i < cpuhw->n_limited; ++i) - if (counter == cpuhw->limited_counter[i]) - break; - if (i < cpuhw->n_limited) { - while (++i < cpuhw->n_limited) { - cpuhw->limited_counter[i-1] = cpuhw->limited_counter[i]; - cpuhw->limited_hwidx[i-1] = cpuhw->limited_hwidx[i]; - } - --cpuhw->n_limited; - } - if (cpuhw->n_counters == 0) { - /* disable exceptions if no counters are running */ - cpuhw->mmcr[0] &= ~(MMCR0_PMXE | MMCR0_FCECE); - } - - perf_enable(); - local_irq_restore(flags); -} - -/* - * Re-enable interrupts on a counter after they were throttled - * because they were coming too fast. - */ -static void power_pmu_unthrottle(struct perf_counter *counter) -{ - s64 val, left; - unsigned long flags; - - if (!counter->hw.idx || !counter->hw.sample_period) - return; - local_irq_save(flags); - perf_disable(); - power_pmu_read(counter); - left = counter->hw.sample_period; - counter->hw.last_period = left; - val = 0; - if (left < 0x80000000L) - val = 0x80000000L - left; - write_pmc(counter->hw.idx, val); - atomic64_set(&counter->hw.prev_count, val); - atomic64_set(&counter->hw.period_left, left); - perf_counter_update_userpage(counter); - perf_enable(); - local_irq_restore(flags); -} - -struct pmu power_pmu = { - .enable = power_pmu_enable, - .disable = power_pmu_disable, - .read = power_pmu_read, - .unthrottle = power_pmu_unthrottle, -}; - -/* - * Return 1 if we might be able to put counter on a limited PMC, - * or 0 if not. - * A counter can only go on a limited PMC if it counts something - * that a limited PMC can count, doesn't require interrupts, and - * doesn't exclude any processor mode. - */ -static int can_go_on_limited_pmc(struct perf_counter *counter, u64 ev, - unsigned int flags) -{ - int n; - u64 alt[MAX_EVENT_ALTERNATIVES]; - - if (counter->attr.exclude_user - || counter->attr.exclude_kernel - || counter->attr.exclude_hv - || counter->attr.sample_period) - return 0; - - if (ppmu->limited_pmc_event(ev)) - return 1; - - /* - * The requested event isn't on a limited PMC already; - * see if any alternative code goes on a limited PMC. - */ - if (!ppmu->get_alternatives) - return 0; - - flags |= PPMU_LIMITED_PMC_OK | PPMU_LIMITED_PMC_REQD; - n = ppmu->get_alternatives(ev, flags, alt); - - return n > 0; -} - -/* - * Find an alternative event that goes on a normal PMC, if possible, - * and return the event code, or 0 if there is no such alternative. - * (Note: event code 0 is "don't count" on all machines.) - */ -static u64 normal_pmc_alternative(u64 ev, unsigned long flags) -{ - u64 alt[MAX_EVENT_ALTERNATIVES]; - int n; - - flags &= ~(PPMU_LIMITED_PMC_OK | PPMU_LIMITED_PMC_REQD); - n = ppmu->get_alternatives(ev, flags, alt); - if (!n) - return 0; - return alt[0]; -} - -/* Number of perf_counters counting hardware events */ -static atomic_t num_counters; -/* Used to avoid races in calling reserve/release_pmc_hardware */ -static DEFINE_MUTEX(pmc_reserve_mutex); - -/* - * Release the PMU if this is the last perf_counter. - */ -static void hw_perf_counter_destroy(struct perf_counter *counter) -{ - if (!atomic_add_unless(&num_counters, -1, 1)) { - mutex_lock(&pmc_reserve_mutex); - if (atomic_dec_return(&num_counters) == 0) - release_pmc_hardware(); - mutex_unlock(&pmc_reserve_mutex); - } -} - -/* - * Translate a generic cache event config to a raw event code. - */ -static int hw_perf_cache_event(u64 config, u64 *eventp) -{ - unsigned long type, op, result; - int ev; - - if (!ppmu->cache_events) - return -EINVAL; - - /* unpack config */ - type = config & 0xff; - op = (config >> 8) & 0xff; - result = (config >> 16) & 0xff; - - if (type >= PERF_COUNT_HW_CACHE_MAX || - op >= PERF_COUNT_HW_CACHE_OP_MAX || - result >= PERF_COUNT_HW_CACHE_RESULT_MAX) - return -EINVAL; - - ev = (*ppmu->cache_events)[type][op][result]; - if (ev == 0) - return -EOPNOTSUPP; - if (ev == -1) - return -EINVAL; - *eventp = ev; - return 0; -} - -const struct pmu *hw_perf_counter_init(struct perf_counter *counter) -{ - u64 ev; - unsigned long flags; - struct perf_counter *ctrs[MAX_HWCOUNTERS]; - u64 events[MAX_HWCOUNTERS]; - unsigned int cflags[MAX_HWCOUNTERS]; - int n; - int err; - struct cpu_hw_counters *cpuhw; - - if (!ppmu) - return ERR_PTR(-ENXIO); - switch (counter->attr.type) { - case PERF_TYPE_HARDWARE: - ev = counter->attr.config; - if (ev >= ppmu->n_generic || ppmu->generic_events[ev] == 0) - return ERR_PTR(-EOPNOTSUPP); - ev = ppmu->generic_events[ev]; - break; - case PERF_TYPE_HW_CACHE: - err = hw_perf_cache_event(counter->attr.config, &ev); - if (err) - return ERR_PTR(err); - break; - case PERF_TYPE_RAW: - ev = counter->attr.config; - break; - default: - return ERR_PTR(-EINVAL); - } - counter->hw.config_base = ev; - counter->hw.idx = 0; - - /* - * If we are not running on a hypervisor, force the - * exclude_hv bit to 0 so that we don't care what - * the user set it to. - */ - if (!firmware_has_feature(FW_FEATURE_LPAR)) - counter->attr.exclude_hv = 0; - - /* - * If this is a per-task counter, then we can use - * PM_RUN_* events interchangeably with their non RUN_* - * equivalents, e.g. PM_RUN_CYC instead of PM_CYC. - * XXX we should check if the task is an idle task. - */ - flags = 0; - if (counter->ctx->task) - flags |= PPMU_ONLY_COUNT_RUN; - - /* - * If this machine has limited counters, check whether this - * event could go on a limited counter. - */ - if (ppmu->flags & PPMU_LIMITED_PMC5_6) { - if (can_go_on_limited_pmc(counter, ev, flags)) { - flags |= PPMU_LIMITED_PMC_OK; - } else if (ppmu->limited_pmc_event(ev)) { - /* - * The requested event is on a limited PMC, - * but we can't use a limited PMC; see if any - * alternative goes on a normal PMC. - */ - ev = normal_pmc_alternative(ev, flags); - if (!ev) - return ERR_PTR(-EINVAL); - } - } - - /* - * If this is in a group, check if it can go on with all the - * other hardware counters in the group. We assume the counter - * hasn't been linked into its leader's sibling list at this point. - */ - n = 0; - if (counter->group_leader != counter) { - n = collect_events(counter->group_leader, ppmu->n_counter - 1, - ctrs, events, cflags); - if (n < 0) - return ERR_PTR(-EINVAL); - } - events[n] = ev; - ctrs[n] = counter; - cflags[n] = flags; - if (check_excludes(ctrs, cflags, n, 1)) - return ERR_PTR(-EINVAL); - - cpuhw = &get_cpu_var(cpu_hw_counters); - err = power_check_constraints(cpuhw, events, cflags, n + 1); - put_cpu_var(cpu_hw_counters); - if (err) - return ERR_PTR(-EINVAL); - - counter->hw.config = events[n]; - counter->hw.counter_base = cflags[n]; - counter->hw.last_period = counter->hw.sample_period; - atomic64_set(&counter->hw.period_left, counter->hw.last_period); - - /* - * See if we need to reserve the PMU. - * If no counters are currently in use, then we have to take a - * mutex to ensure that we don't race with another task doing - * reserve_pmc_hardware or release_pmc_hardware. - */ - err = 0; - if (!atomic_inc_not_zero(&num_counters)) { - mutex_lock(&pmc_reserve_mutex); - if (atomic_read(&num_counters) == 0 && - reserve_pmc_hardware(perf_counter_interrupt)) - err = -EBUSY; - else - atomic_inc(&num_counters); - mutex_unlock(&pmc_reserve_mutex); - } - counter->destroy = hw_perf_counter_destroy; - - if (err) - return ERR_PTR(err); - return &power_pmu; -} - -/* - * A counter has overflowed; update its count and record - * things if requested. Note that interrupts are hard-disabled - * here so there is no possibility of being interrupted. - */ -static void record_and_restart(struct perf_counter *counter, unsigned long val, - struct pt_regs *regs, int nmi) -{ - u64 period = counter->hw.sample_period; - s64 prev, delta, left; - int record = 0; - - /* we don't have to worry about interrupts here */ - prev = atomic64_read(&counter->hw.prev_count); - delta = (val - prev) & 0xfffffffful; - atomic64_add(delta, &counter->count); - - /* - * See if the total period for this counter has expired, - * and update for the next period. - */ - val = 0; - left = atomic64_read(&counter->hw.period_left) - delta; - if (period) { - if (left <= 0) { - left += period; - if (left <= 0) - left = period; - record = 1; - } - if (left < 0x80000000LL) - val = 0x80000000LL - left; - } - - /* - * Finally record data if requested. - */ - if (record) { - struct perf_sample_data data = { - .addr = 0, - .period = counter->hw.last_period, - }; - - if (counter->attr.sample_type & PERF_SAMPLE_ADDR) - perf_get_data_addr(regs, &data.addr); - - if (perf_counter_overflow(counter, nmi, &data, regs)) { - /* - * Interrupts are coming too fast - throttle them - * by setting the counter to 0, so it will be - * at least 2^30 cycles until the next interrupt - * (assuming each counter counts at most 2 counts - * per cycle). - */ - val = 0; - left = ~0ULL >> 1; - } - } - - write_pmc(counter->hw.idx, val); - atomic64_set(&counter->hw.prev_count, val); - atomic64_set(&counter->hw.period_left, left); - perf_counter_update_userpage(counter); -} - -/* - * Called from generic code to get the misc flags (i.e. processor mode) - * for an event. - */ -unsigned long perf_misc_flags(struct pt_regs *regs) -{ - u32 flags = perf_get_misc_flags(regs); - - if (flags) - return flags; - return user_mode(regs) ? PERF_EVENT_MISC_USER : - PERF_EVENT_MISC_KERNEL; -} - -/* - * Called from generic code to get the instruction pointer - * for an event. - */ -unsigned long perf_instruction_pointer(struct pt_regs *regs) -{ - unsigned long ip; - - if (TRAP(regs) != 0xf00) - return regs->nip; /* not a PMU interrupt */ - - ip = mfspr(SPRN_SIAR) + perf_ip_adjust(regs); - return ip; -} - -/* - * Performance monitor interrupt stuff - */ -static void perf_counter_interrupt(struct pt_regs *regs) -{ - int i; - struct cpu_hw_counters *cpuhw = &__get_cpu_var(cpu_hw_counters); - struct perf_counter *counter; - unsigned long val; - int found = 0; - int nmi; - - if (cpuhw->n_limited) - freeze_limited_counters(cpuhw, mfspr(SPRN_PMC5), - mfspr(SPRN_PMC6)); - - perf_read_regs(regs); - - nmi = perf_intr_is_nmi(regs); - if (nmi) - nmi_enter(); - else - irq_enter(); - - for (i = 0; i < cpuhw->n_counters; ++i) { - counter = cpuhw->counter[i]; - if (!counter->hw.idx || is_limited_pmc(counter->hw.idx)) - continue; - val = read_pmc(counter->hw.idx); - if ((int)val < 0) { - /* counter has overflowed */ - found = 1; - record_and_restart(counter, val, regs, nmi); - } - } - - /* - * In case we didn't find and reset the counter that caused - * the interrupt, scan all counters and reset any that are - * negative, to avoid getting continual interrupts. - * Any that we processed in the previous loop will not be negative. - */ - if (!found) { - for (i = 0; i < ppmu->n_counter; ++i) { - if (is_limited_pmc(i + 1)) - continue; - val = read_pmc(i + 1); - if ((int)val < 0) - write_pmc(i + 1, 0); - } - } - - /* - * Reset MMCR0 to its normal value. This will set PMXE and - * clear FC (freeze counters) and PMAO (perf mon alert occurred) - * and thus allow interrupts to occur again. - * XXX might want to use MSR.PM to keep the counters frozen until - * we get back out of this interrupt. - */ - write_mmcr0(cpuhw, cpuhw->mmcr[0]); - - if (nmi) - nmi_exit(); - else - irq_exit(); -} - -void hw_perf_counter_setup(int cpu) -{ - struct cpu_hw_counters *cpuhw = &per_cpu(cpu_hw_counters, cpu); - - if (!ppmu) - return; - memset(cpuhw, 0, sizeof(*cpuhw)); - cpuhw->mmcr[0] = MMCR0_FC; -} - -int register_power_pmu(struct power_pmu *pmu) -{ - if (ppmu) - return -EBUSY; /* something's already registered */ - - ppmu = pmu; - pr_info("%s performance monitor hardware support registered\n", - pmu->name); - -#ifdef MSR_HV - /* - * Use FCHV to ignore kernel events if MSR.HV is set. - */ - if (mfmsr() & MSR_HV) - freeze_counters_kernel = MMCR0_FCHV; -#endif /* CONFIG_PPC64 */ - - return 0; -} diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c new file mode 100644 index 000000000000..c98321fcb459 --- /dev/null +++ b/arch/powerpc/kernel/perf_event.c @@ -0,0 +1,1315 @@ +/* + * Performance event support - powerpc architecture code + * + * Copyright 2008-2009 Paul Mackerras, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct cpu_hw_events { + int n_events; + int n_percpu; + int disabled; + int n_added; + int n_limited; + u8 pmcs_enabled; + struct perf_event *event[MAX_HWEVENTS]; + u64 events[MAX_HWEVENTS]; + unsigned int flags[MAX_HWEVENTS]; + unsigned long mmcr[3]; + struct perf_event *limited_event[MAX_LIMITED_HWEVENTS]; + u8 limited_hwidx[MAX_LIMITED_HWEVENTS]; + u64 alternatives[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; + unsigned long amasks[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; + unsigned long avalues[MAX_HWEVENTS][MAX_EVENT_ALTERNATIVES]; +}; +DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events); + +struct power_pmu *ppmu; + +/* + * Normally, to ignore kernel events we set the FCS (freeze events + * in supervisor mode) bit in MMCR0, but if the kernel runs with the + * hypervisor bit set in the MSR, or if we are running on a processor + * where the hypervisor bit is forced to 1 (as on Apple G5 processors), + * then we need to use the FCHV bit to ignore kernel events. + */ +static unsigned int freeze_events_kernel = MMCR0_FCS; + +/* + * 32-bit doesn't have MMCRA but does have an MMCR2, + * and a few other names are different. + */ +#ifdef CONFIG_PPC32 + +#define MMCR0_FCHV 0 +#define MMCR0_PMCjCE MMCR0_PMCnCE + +#define SPRN_MMCRA SPRN_MMCR2 +#define MMCRA_SAMPLE_ENABLE 0 + +static inline unsigned long perf_ip_adjust(struct pt_regs *regs) +{ + return 0; +} +static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp) { } +static inline u32 perf_get_misc_flags(struct pt_regs *regs) +{ + return 0; +} +static inline void perf_read_regs(struct pt_regs *regs) { } +static inline int perf_intr_is_nmi(struct pt_regs *regs) +{ + return 0; +} + +#endif /* CONFIG_PPC32 */ + +/* + * Things that are specific to 64-bit implementations. + */ +#ifdef CONFIG_PPC64 + +static inline unsigned long perf_ip_adjust(struct pt_regs *regs) +{ + unsigned long mmcra = regs->dsisr; + + if ((mmcra & MMCRA_SAMPLE_ENABLE) && !(ppmu->flags & PPMU_ALT_SIPR)) { + unsigned long slot = (mmcra & MMCRA_SLOT) >> MMCRA_SLOT_SHIFT; + if (slot > 1) + return 4 * (slot - 1); + } + return 0; +} + +/* + * The user wants a data address recorded. + * If we're not doing instruction sampling, give them the SDAR + * (sampled data address). If we are doing instruction sampling, then + * only give them the SDAR if it corresponds to the instruction + * pointed to by SIAR; this is indicated by the [POWER6_]MMCRA_SDSYNC + * bit in MMCRA. + */ +static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp) +{ + unsigned long mmcra = regs->dsisr; + unsigned long sdsync = (ppmu->flags & PPMU_ALT_SIPR) ? + POWER6_MMCRA_SDSYNC : MMCRA_SDSYNC; + + if (!(mmcra & MMCRA_SAMPLE_ENABLE) || (mmcra & sdsync)) + *addrp = mfspr(SPRN_SDAR); +} + +static inline u32 perf_get_misc_flags(struct pt_regs *regs) +{ + unsigned long mmcra = regs->dsisr; + + if (TRAP(regs) != 0xf00) + return 0; /* not a PMU interrupt */ + + if (ppmu->flags & PPMU_ALT_SIPR) { + if (mmcra & POWER6_MMCRA_SIHV) + return PERF_RECORD_MISC_HYPERVISOR; + return (mmcra & POWER6_MMCRA_SIPR) ? + PERF_RECORD_MISC_USER : PERF_RECORD_MISC_KERNEL; + } + if (mmcra & MMCRA_SIHV) + return PERF_RECORD_MISC_HYPERVISOR; + return (mmcra & MMCRA_SIPR) ? PERF_RECORD_MISC_USER : + PERF_RECORD_MISC_KERNEL; +} + +/* + * Overload regs->dsisr to store MMCRA so we only need to read it once + * on each interrupt. + */ +static inline void perf_read_regs(struct pt_regs *regs) +{ + regs->dsisr = mfspr(SPRN_MMCRA); +} + +/* + * If interrupts were soft-disabled when a PMU interrupt occurs, treat + * it as an NMI. + */ +static inline int perf_intr_is_nmi(struct pt_regs *regs) +{ + return !regs->softe; +} + +#endif /* CONFIG_PPC64 */ + +static void perf_event_interrupt(struct pt_regs *regs); + +void perf_event_print_debug(void) +{ +} + +/* + * Read one performance monitor event (PMC). + */ +static unsigned long read_pmc(int idx) +{ + unsigned long val; + + switch (idx) { + case 1: + val = mfspr(SPRN_PMC1); + break; + case 2: + val = mfspr(SPRN_PMC2); + break; + case 3: + val = mfspr(SPRN_PMC3); + break; + case 4: + val = mfspr(SPRN_PMC4); + break; + case 5: + val = mfspr(SPRN_PMC5); + break; + case 6: + val = mfspr(SPRN_PMC6); + break; +#ifdef CONFIG_PPC64 + case 7: + val = mfspr(SPRN_PMC7); + break; + case 8: + val = mfspr(SPRN_PMC8); + break; +#endif /* CONFIG_PPC64 */ + default: + printk(KERN_ERR "oops trying to read PMC%d\n", idx); + val = 0; + } + return val; +} + +/* + * Write one PMC. + */ +static void write_pmc(int idx, unsigned long val) +{ + switch (idx) { + case 1: + mtspr(SPRN_PMC1, val); + break; + case 2: + mtspr(SPRN_PMC2, val); + break; + case 3: + mtspr(SPRN_PMC3, val); + break; + case 4: + mtspr(SPRN_PMC4, val); + break; + case 5: + mtspr(SPRN_PMC5, val); + break; + case 6: + mtspr(SPRN_PMC6, val); + break; +#ifdef CONFIG_PPC64 + case 7: + mtspr(SPRN_PMC7, val); + break; + case 8: + mtspr(SPRN_PMC8, val); + break; +#endif /* CONFIG_PPC64 */ + default: + printk(KERN_ERR "oops trying to write PMC%d\n", idx); + } +} + +/* + * Check if a set of events can all go on the PMU at once. + * If they can't, this will look at alternative codes for the events + * and see if any combination of alternative codes is feasible. + * The feasible set is returned in event_id[]. + */ +static int power_check_constraints(struct cpu_hw_events *cpuhw, + u64 event_id[], unsigned int cflags[], + int n_ev) +{ + unsigned long mask, value, nv; + unsigned long smasks[MAX_HWEVENTS], svalues[MAX_HWEVENTS]; + int n_alt[MAX_HWEVENTS], choice[MAX_HWEVENTS]; + int i, j; + unsigned long addf = ppmu->add_fields; + unsigned long tadd = ppmu->test_adder; + + if (n_ev > ppmu->n_event) + return -1; + + /* First see if the events will go on as-is */ + for (i = 0; i < n_ev; ++i) { + if ((cflags[i] & PPMU_LIMITED_PMC_REQD) + && !ppmu->limited_pmc_event(event_id[i])) { + ppmu->get_alternatives(event_id[i], cflags[i], + cpuhw->alternatives[i]); + event_id[i] = cpuhw->alternatives[i][0]; + } + if (ppmu->get_constraint(event_id[i], &cpuhw->amasks[i][0], + &cpuhw->avalues[i][0])) + return -1; + } + value = mask = 0; + for (i = 0; i < n_ev; ++i) { + nv = (value | cpuhw->avalues[i][0]) + + (value & cpuhw->avalues[i][0] & addf); + if ((((nv + tadd) ^ value) & mask) != 0 || + (((nv + tadd) ^ cpuhw->avalues[i][0]) & + cpuhw->amasks[i][0]) != 0) + break; + value = nv; + mask |= cpuhw->amasks[i][0]; + } + if (i == n_ev) + return 0; /* all OK */ + + /* doesn't work, gather alternatives... */ + if (!ppmu->get_alternatives) + return -1; + for (i = 0; i < n_ev; ++i) { + choice[i] = 0; + n_alt[i] = ppmu->get_alternatives(event_id[i], cflags[i], + cpuhw->alternatives[i]); + for (j = 1; j < n_alt[i]; ++j) + ppmu->get_constraint(cpuhw->alternatives[i][j], + &cpuhw->amasks[i][j], + &cpuhw->avalues[i][j]); + } + + /* enumerate all possibilities and see if any will work */ + i = 0; + j = -1; + value = mask = nv = 0; + while (i < n_ev) { + if (j >= 0) { + /* we're backtracking, restore context */ + value = svalues[i]; + mask = smasks[i]; + j = choice[i]; + } + /* + * See if any alternative k for event_id i, + * where k > j, will satisfy the constraints. + */ + while (++j < n_alt[i]) { + nv = (value | cpuhw->avalues[i][j]) + + (value & cpuhw->avalues[i][j] & addf); + if ((((nv + tadd) ^ value) & mask) == 0 && + (((nv + tadd) ^ cpuhw->avalues[i][j]) + & cpuhw->amasks[i][j]) == 0) + break; + } + if (j >= n_alt[i]) { + /* + * No feasible alternative, backtrack + * to event_id i-1 and continue enumerating its + * alternatives from where we got up to. + */ + if (--i < 0) + return -1; + } else { + /* + * Found a feasible alternative for event_id i, + * remember where we got up to with this event_id, + * go on to the next event_id, and start with + * the first alternative for it. + */ + choice[i] = j; + svalues[i] = value; + smasks[i] = mask; + value = nv; + mask |= cpuhw->amasks[i][j]; + ++i; + j = -1; + } + } + + /* OK, we have a feasible combination, tell the caller the solution */ + for (i = 0; i < n_ev; ++i) + event_id[i] = cpuhw->alternatives[i][choice[i]]; + return 0; +} + +/* + * Check if newly-added events have consistent settings for + * exclude_{user,kernel,hv} with each other and any previously + * added events. + */ +static int check_excludes(struct perf_event **ctrs, unsigned int cflags[], + int n_prev, int n_new) +{ + int eu = 0, ek = 0, eh = 0; + int i, n, first; + struct perf_event *event; + + n = n_prev + n_new; + if (n <= 1) + return 0; + + first = 1; + for (i = 0; i < n; ++i) { + if (cflags[i] & PPMU_LIMITED_PMC_OK) { + cflags[i] &= ~PPMU_LIMITED_PMC_REQD; + continue; + } + event = ctrs[i]; + if (first) { + eu = event->attr.exclude_user; + ek = event->attr.exclude_kernel; + eh = event->attr.exclude_hv; + first = 0; + } else if (event->attr.exclude_user != eu || + event->attr.exclude_kernel != ek || + event->attr.exclude_hv != eh) { + return -EAGAIN; + } + } + + if (eu || ek || eh) + for (i = 0; i < n; ++i) + if (cflags[i] & PPMU_LIMITED_PMC_OK) + cflags[i] |= PPMU_LIMITED_PMC_REQD; + + return 0; +} + +static void power_pmu_read(struct perf_event *event) +{ + s64 val, delta, prev; + + if (!event->hw.idx) + return; + /* + * Performance monitor interrupts come even when interrupts + * are soft-disabled, as long as interrupts are hard-enabled. + * Therefore we treat them like NMIs. + */ + do { + prev = atomic64_read(&event->hw.prev_count); + barrier(); + val = read_pmc(event->hw.idx); + } while (atomic64_cmpxchg(&event->hw.prev_count, prev, val) != prev); + + /* The events are only 32 bits wide */ + delta = (val - prev) & 0xfffffffful; + atomic64_add(delta, &event->count); + atomic64_sub(delta, &event->hw.period_left); +} + +/* + * On some machines, PMC5 and PMC6 can't be written, don't respect + * the freeze conditions, and don't generate interrupts. This tells + * us if `event' is using such a PMC. + */ +static int is_limited_pmc(int pmcnum) +{ + return (ppmu->flags & PPMU_LIMITED_PMC5_6) + && (pmcnum == 5 || pmcnum == 6); +} + +static void freeze_limited_events(struct cpu_hw_events *cpuhw, + unsigned long pmc5, unsigned long pmc6) +{ + struct perf_event *event; + u64 val, prev, delta; + int i; + + for (i = 0; i < cpuhw->n_limited; ++i) { + event = cpuhw->limited_event[i]; + if (!event->hw.idx) + continue; + val = (event->hw.idx == 5) ? pmc5 : pmc6; + prev = atomic64_read(&event->hw.prev_count); + event->hw.idx = 0; + delta = (val - prev) & 0xfffffffful; + atomic64_add(delta, &event->count); + } +} + +static void thaw_limited_events(struct cpu_hw_events *cpuhw, + unsigned long pmc5, unsigned long pmc6) +{ + struct perf_event *event; + u64 val; + int i; + + for (i = 0; i < cpuhw->n_limited; ++i) { + event = cpuhw->limited_event[i]; + event->hw.idx = cpuhw->limited_hwidx[i]; + val = (event->hw.idx == 5) ? pmc5 : pmc6; + atomic64_set(&event->hw.prev_count, val); + perf_event_update_userpage(event); + } +} + +/* + * Since limited events don't respect the freeze conditions, we + * have to read them immediately after freezing or unfreezing the + * other events. We try to keep the values from the limited + * events as consistent as possible by keeping the delay (in + * cycles and instructions) between freezing/unfreezing and reading + * the limited events as small and consistent as possible. + * Therefore, if any limited events are in use, we read them + * both, and always in the same order, to minimize variability, + * and do it inside the same asm that writes MMCR0. + */ +static void write_mmcr0(struct cpu_hw_events *cpuhw, unsigned long mmcr0) +{ + unsigned long pmc5, pmc6; + + if (!cpuhw->n_limited) { + mtspr(SPRN_MMCR0, mmcr0); + return; + } + + /* + * Write MMCR0, then read PMC5 and PMC6 immediately. + * To ensure we don't get a performance monitor interrupt + * between writing MMCR0 and freezing/thawing the limited + * events, we first write MMCR0 with the event overflow + * interrupt enable bits turned off. + */ + asm volatile("mtspr %3,%2; mfspr %0,%4; mfspr %1,%5" + : "=&r" (pmc5), "=&r" (pmc6) + : "r" (mmcr0 & ~(MMCR0_PMC1CE | MMCR0_PMCjCE)), + "i" (SPRN_MMCR0), + "i" (SPRN_PMC5), "i" (SPRN_PMC6)); + + if (mmcr0 & MMCR0_FC) + freeze_limited_events(cpuhw, pmc5, pmc6); + else + thaw_limited_events(cpuhw, pmc5, pmc6); + + /* + * Write the full MMCR0 including the event overflow interrupt + * enable bits, if necessary. + */ + if (mmcr0 & (MMCR0_PMC1CE | MMCR0_PMCjCE)) + mtspr(SPRN_MMCR0, mmcr0); +} + +/* + * Disable all events to prevent PMU interrupts and to allow + * events to be added or removed. + */ +void hw_perf_disable(void) +{ + struct cpu_hw_events *cpuhw; + unsigned long flags; + + if (!ppmu) + return; + local_irq_save(flags); + cpuhw = &__get_cpu_var(cpu_hw_events); + + if (!cpuhw->disabled) { + cpuhw->disabled = 1; + cpuhw->n_added = 0; + + /* + * Check if we ever enabled the PMU on this cpu. + */ + if (!cpuhw->pmcs_enabled) { + ppc_enable_pmcs(); + cpuhw->pmcs_enabled = 1; + } + + /* + * Disable instruction sampling if it was enabled + */ + if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { + mtspr(SPRN_MMCRA, + cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); + mb(); + } + + /* + * Set the 'freeze events' bit. + * The barrier is to make sure the mtspr has been + * executed and the PMU has frozen the events + * before we return. + */ + write_mmcr0(cpuhw, mfspr(SPRN_MMCR0) | MMCR0_FC); + mb(); + } + local_irq_restore(flags); +} + +/* + * Re-enable all events if disable == 0. + * If we were previously disabled and events were added, then + * put the new config on the PMU. + */ +void hw_perf_enable(void) +{ + struct perf_event *event; + struct cpu_hw_events *cpuhw; + unsigned long flags; + long i; + unsigned long val; + s64 left; + unsigned int hwc_index[MAX_HWEVENTS]; + int n_lim; + int idx; + + if (!ppmu) + return; + local_irq_save(flags); + cpuhw = &__get_cpu_var(cpu_hw_events); + if (!cpuhw->disabled) { + local_irq_restore(flags); + return; + } + cpuhw->disabled = 0; + + /* + * If we didn't change anything, or only removed events, + * no need to recalculate MMCR* settings and reset the PMCs. + * Just reenable the PMU with the current MMCR* settings + * (possibly updated for removal of events). + */ + if (!cpuhw->n_added) { + mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); + mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); + if (cpuhw->n_events == 0) + ppc_set_pmu_inuse(0); + goto out_enable; + } + + /* + * Compute MMCR* values for the new set of events + */ + if (ppmu->compute_mmcr(cpuhw->events, cpuhw->n_events, hwc_index, + cpuhw->mmcr)) { + /* shouldn't ever get here */ + printk(KERN_ERR "oops compute_mmcr failed\n"); + goto out; + } + + /* + * Add in MMCR0 freeze bits corresponding to the + * attr.exclude_* bits for the first event. + * We have already checked that all events have the + * same values for these bits as the first event. + */ + event = cpuhw->event[0]; + if (event->attr.exclude_user) + cpuhw->mmcr[0] |= MMCR0_FCP; + if (event->attr.exclude_kernel) + cpuhw->mmcr[0] |= freeze_events_kernel; + if (event->attr.exclude_hv) + cpuhw->mmcr[0] |= MMCR0_FCHV; + + /* + * Write the new configuration to MMCR* with the freeze + * bit set and set the hardware events to their initial values. + * Then unfreeze the events. + */ + ppc_set_pmu_inuse(1); + mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE); + mtspr(SPRN_MMCR1, cpuhw->mmcr[1]); + mtspr(SPRN_MMCR0, (cpuhw->mmcr[0] & ~(MMCR0_PMC1CE | MMCR0_PMCjCE)) + | MMCR0_FC); + + /* + * Read off any pre-existing events that need to move + * to another PMC. + */ + for (i = 0; i < cpuhw->n_events; ++i) { + event = cpuhw->event[i]; + if (event->hw.idx && event->hw.idx != hwc_index[i] + 1) { + power_pmu_read(event); + write_pmc(event->hw.idx, 0); + event->hw.idx = 0; + } + } + + /* + * Initialize the PMCs for all the new and moved events. + */ + cpuhw->n_limited = n_lim = 0; + for (i = 0; i < cpuhw->n_events; ++i) { + event = cpuhw->event[i]; + if (event->hw.idx) + continue; + idx = hwc_index[i] + 1; + if (is_limited_pmc(idx)) { + cpuhw->limited_event[n_lim] = event; + cpuhw->limited_hwidx[n_lim] = idx; + ++n_lim; + continue; + } + val = 0; + if (event->hw.sample_period) { + left = atomic64_read(&event->hw.period_left); + if (left < 0x80000000L) + val = 0x80000000L - left; + } + atomic64_set(&event->hw.prev_count, val); + event->hw.idx = idx; + write_pmc(idx, val); + perf_event_update_userpage(event); + } + cpuhw->n_limited = n_lim; + cpuhw->mmcr[0] |= MMCR0_PMXE | MMCR0_FCECE; + + out_enable: + mb(); + write_mmcr0(cpuhw, cpuhw->mmcr[0]); + + /* + * Enable instruction sampling if necessary + */ + if (cpuhw->mmcr[2] & MMCRA_SAMPLE_ENABLE) { + mb(); + mtspr(SPRN_MMCRA, cpuhw->mmcr[2]); + } + + out: + local_irq_restore(flags); +} + +static int collect_events(struct perf_event *group, int max_count, + struct perf_event *ctrs[], u64 *events, + unsigned int *flags) +{ + int n = 0; + struct perf_event *event; + + if (!is_software_event(group)) { + if (n >= max_count) + return -1; + ctrs[n] = group; + flags[n] = group->hw.event_base; + events[n++] = group->hw.config; + } + list_for_each_entry(event, &group->sibling_list, list_entry) { + if (!is_software_event(event) && + event->state != PERF_EVENT_STATE_OFF) { + if (n >= max_count) + return -1; + ctrs[n] = event; + flags[n] = event->hw.event_base; + events[n++] = event->hw.config; + } + } + return n; +} + +static void event_sched_in(struct perf_event *event, int cpu) +{ + event->state = PERF_EVENT_STATE_ACTIVE; + event->oncpu = cpu; + event->tstamp_running += event->ctx->time - event->tstamp_stopped; + if (is_software_event(event)) + event->pmu->enable(event); +} + +/* + * Called to enable a whole group of events. + * Returns 1 if the group was enabled, or -EAGAIN if it could not be. + * Assumes the caller has disabled interrupts and has + * frozen the PMU with hw_perf_save_disable. + */ +int hw_perf_group_sched_in(struct perf_event *group_leader, + struct perf_cpu_context *cpuctx, + struct perf_event_context *ctx, int cpu) +{ + struct cpu_hw_events *cpuhw; + long i, n, n0; + struct perf_event *sub; + + if (!ppmu) + return 0; + cpuhw = &__get_cpu_var(cpu_hw_events); + n0 = cpuhw->n_events; + n = collect_events(group_leader, ppmu->n_event - n0, + &cpuhw->event[n0], &cpuhw->events[n0], + &cpuhw->flags[n0]); + if (n < 0) + return -EAGAIN; + if (check_excludes(cpuhw->event, cpuhw->flags, n0, n)) + return -EAGAIN; + i = power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n + n0); + if (i < 0) + return -EAGAIN; + cpuhw->n_events = n0 + n; + cpuhw->n_added += n; + + /* + * OK, this group can go on; update event states etc., + * and enable any software events + */ + for (i = n0; i < n0 + n; ++i) + cpuhw->event[i]->hw.config = cpuhw->events[i]; + cpuctx->active_oncpu += n; + n = 1; + event_sched_in(group_leader, cpu); + list_for_each_entry(sub, &group_leader->sibling_list, list_entry) { + if (sub->state != PERF_EVENT_STATE_OFF) { + event_sched_in(sub, cpu); + ++n; + } + } + ctx->nr_active += n; + + return 1; +} + +/* + * Add a event to the PMU. + * If all events are not already frozen, then we disable and + * re-enable the PMU in order to get hw_perf_enable to do the + * actual work of reconfiguring the PMU. + */ +static int power_pmu_enable(struct perf_event *event) +{ + struct cpu_hw_events *cpuhw; + unsigned long flags; + int n0; + int ret = -EAGAIN; + + local_irq_save(flags); + perf_disable(); + + /* + * Add the event to the list (if there is room) + * and check whether the total set is still feasible. + */ + cpuhw = &__get_cpu_var(cpu_hw_events); + n0 = cpuhw->n_events; + if (n0 >= ppmu->n_event) + goto out; + cpuhw->event[n0] = event; + cpuhw->events[n0] = event->hw.config; + cpuhw->flags[n0] = event->hw.event_base; + if (check_excludes(cpuhw->event, cpuhw->flags, n0, 1)) + goto out; + if (power_check_constraints(cpuhw, cpuhw->events, cpuhw->flags, n0 + 1)) + goto out; + + event->hw.config = cpuhw->events[n0]; + ++cpuhw->n_events; + ++cpuhw->n_added; + + ret = 0; + out: + perf_enable(); + local_irq_restore(flags); + return ret; +} + +/* + * Remove a event from the PMU. + */ +static void power_pmu_disable(struct perf_event *event) +{ + struct cpu_hw_events *cpuhw; + long i; + unsigned long flags; + + local_irq_save(flags); + perf_disable(); + + power_pmu_read(event); + + cpuhw = &__get_cpu_var(cpu_hw_events); + for (i = 0; i < cpuhw->n_events; ++i) { + if (event == cpuhw->event[i]) { + while (++i < cpuhw->n_events) + cpuhw->event[i-1] = cpuhw->event[i]; + --cpuhw->n_events; + ppmu->disable_pmc(event->hw.idx - 1, cpuhw->mmcr); + if (event->hw.idx) { + write_pmc(event->hw.idx, 0); + event->hw.idx = 0; + } + perf_event_update_userpage(event); + break; + } + } + for (i = 0; i < cpuhw->n_limited; ++i) + if (event == cpuhw->limited_event[i]) + break; + if (i < cpuhw->n_limited) { + while (++i < cpuhw->n_limited) { + cpuhw->limited_event[i-1] = cpuhw->limited_event[i]; + cpuhw->limited_hwidx[i-1] = cpuhw->limited_hwidx[i]; + } + --cpuhw->n_limited; + } + if (cpuhw->n_events == 0) { + /* disable exceptions if no events are running */ + cpuhw->mmcr[0] &= ~(MMCR0_PMXE | MMCR0_FCECE); + } + + perf_enable(); + local_irq_restore(flags); +} + +/* + * Re-enable interrupts on a event after they were throttled + * because they were coming too fast. + */ +static void power_pmu_unthrottle(struct perf_event *event) +{ + s64 val, left; + unsigned long flags; + + if (!event->hw.idx || !event->hw.sample_period) + return; + local_irq_save(flags); + perf_disable(); + power_pmu_read(event); + left = event->hw.sample_period; + event->hw.last_period = left; + val = 0; + if (left < 0x80000000L) + val = 0x80000000L - left; + write_pmc(event->hw.idx, val); + atomic64_set(&event->hw.prev_count, val); + atomic64_set(&event->hw.period_left, left); + perf_event_update_userpage(event); + perf_enable(); + local_irq_restore(flags); +} + +struct pmu power_pmu = { + .enable = power_pmu_enable, + .disable = power_pmu_disable, + .read = power_pmu_read, + .unthrottle = power_pmu_unthrottle, +}; + +/* + * Return 1 if we might be able to put event on a limited PMC, + * or 0 if not. + * A event can only go on a limited PMC if it counts something + * that a limited PMC can count, doesn't require interrupts, and + * doesn't exclude any processor mode. + */ +static int can_go_on_limited_pmc(struct perf_event *event, u64 ev, + unsigned int flags) +{ + int n; + u64 alt[MAX_EVENT_ALTERNATIVES]; + + if (event->attr.exclude_user + || event->attr.exclude_kernel + || event->attr.exclude_hv + || event->attr.sample_period) + return 0; + + if (ppmu->limited_pmc_event(ev)) + return 1; + + /* + * The requested event_id isn't on a limited PMC already; + * see if any alternative code goes on a limited PMC. + */ + if (!ppmu->get_alternatives) + return 0; + + flags |= PPMU_LIMITED_PMC_OK | PPMU_LIMITED_PMC_REQD; + n = ppmu->get_alternatives(ev, flags, alt); + + return n > 0; +} + +/* + * Find an alternative event_id that goes on a normal PMC, if possible, + * and return the event_id code, or 0 if there is no such alternative. + * (Note: event_id code 0 is "don't count" on all machines.) + */ +static u64 normal_pmc_alternative(u64 ev, unsigned long flags) +{ + u64 alt[MAX_EVENT_ALTERNATIVES]; + int n; + + flags &= ~(PPMU_LIMITED_PMC_OK | PPMU_LIMITED_PMC_REQD); + n = ppmu->get_alternatives(ev, flags, alt); + if (!n) + return 0; + return alt[0]; +} + +/* Number of perf_events counting hardware events */ +static atomic_t num_events; +/* Used to avoid races in calling reserve/release_pmc_hardware */ +static DEFINE_MUTEX(pmc_reserve_mutex); + +/* + * Release the PMU if this is the last perf_event. + */ +static void hw_perf_event_destroy(struct perf_event *event) +{ + if (!atomic_add_unless(&num_events, -1, 1)) { + mutex_lock(&pmc_reserve_mutex); + if (atomic_dec_return(&num_events) == 0) + release_pmc_hardware(); + mutex_unlock(&pmc_reserve_mutex); + } +} + +/* + * Translate a generic cache event_id config to a raw event_id code. + */ +static int hw_perf_cache_event(u64 config, u64 *eventp) +{ + unsigned long type, op, result; + int ev; + + if (!ppmu->cache_events) + return -EINVAL; + + /* unpack config */ + type = config & 0xff; + op = (config >> 8) & 0xff; + result = (config >> 16) & 0xff; + + if (type >= PERF_COUNT_HW_CACHE_MAX || + op >= PERF_COUNT_HW_CACHE_OP_MAX || + result >= PERF_COUNT_HW_CACHE_RESULT_MAX) + return -EINVAL; + + ev = (*ppmu->cache_events)[type][op][result]; + if (ev == 0) + return -EOPNOTSUPP; + if (ev == -1) + return -EINVAL; + *eventp = ev; + return 0; +} + +const struct pmu *hw_perf_event_init(struct perf_event *event) +{ + u64 ev; + unsigned long flags; + struct perf_event *ctrs[MAX_HWEVENTS]; + u64 events[MAX_HWEVENTS]; + unsigned int cflags[MAX_HWEVENTS]; + int n; + int err; + struct cpu_hw_events *cpuhw; + + if (!ppmu) + return ERR_PTR(-ENXIO); + switch (event->attr.type) { + case PERF_TYPE_HARDWARE: + ev = event->attr.config; + if (ev >= ppmu->n_generic || ppmu->generic_events[ev] == 0) + return ERR_PTR(-EOPNOTSUPP); + ev = ppmu->generic_events[ev]; + break; + case PERF_TYPE_HW_CACHE: + err = hw_perf_cache_event(event->attr.config, &ev); + if (err) + return ERR_PTR(err); + break; + case PERF_TYPE_RAW: + ev = event->attr.config; + break; + default: + return ERR_PTR(-EINVAL); + } + event->hw.config_base = ev; + event->hw.idx = 0; + + /* + * If we are not running on a hypervisor, force the + * exclude_hv bit to 0 so that we don't care what + * the user set it to. + */ + if (!firmware_has_feature(FW_FEATURE_LPAR)) + event->attr.exclude_hv = 0; + + /* + * If this is a per-task event, then we can use + * PM_RUN_* events interchangeably with their non RUN_* + * equivalents, e.g. PM_RUN_CYC instead of PM_CYC. + * XXX we should check if the task is an idle task. + */ + flags = 0; + if (event->ctx->task) + flags |= PPMU_ONLY_COUNT_RUN; + + /* + * If this machine has limited events, check whether this + * event_id could go on a limited event. + */ + if (ppmu->flags & PPMU_LIMITED_PMC5_6) { + if (can_go_on_limited_pmc(event, ev, flags)) { + flags |= PPMU_LIMITED_PMC_OK; + } else if (ppmu->limited_pmc_event(ev)) { + /* + * The requested event_id is on a limited PMC, + * but we can't use a limited PMC; see if any + * alternative goes on a normal PMC. + */ + ev = normal_pmc_alternative(ev, flags); + if (!ev) + return ERR_PTR(-EINVAL); + } + } + + /* + * If this is in a group, check if it can go on with all the + * other hardware events in the group. We assume the event + * hasn't been linked into its leader's sibling list at this point. + */ + n = 0; + if (event->group_leader != event) { + n = collect_events(event->group_leader, ppmu->n_event - 1, + ctrs, events, cflags); + if (n < 0) + return ERR_PTR(-EINVAL); + } + events[n] = ev; + ctrs[n] = event; + cflags[n] = flags; + if (check_excludes(ctrs, cflags, n, 1)) + return ERR_PTR(-EINVAL); + + cpuhw = &get_cpu_var(cpu_hw_events); + err = power_check_constraints(cpuhw, events, cflags, n + 1); + put_cpu_var(cpu_hw_events); + if (err) + return ERR_PTR(-EINVAL); + + event->hw.config = events[n]; + event->hw.event_base = cflags[n]; + event->hw.last_period = event->hw.sample_period; + atomic64_set(&event->hw.period_left, event->hw.last_period); + + /* + * See if we need to reserve the PMU. + * If no events are currently in use, then we have to take a + * mutex to ensure that we don't race with another task doing + * reserve_pmc_hardware or release_pmc_hardware. + */ + err = 0; + if (!atomic_inc_not_zero(&num_events)) { + mutex_lock(&pmc_reserve_mutex); + if (atomic_read(&num_events) == 0 && + reserve_pmc_hardware(perf_event_interrupt)) + err = -EBUSY; + else + atomic_inc(&num_events); + mutex_unlock(&pmc_reserve_mutex); + } + event->destroy = hw_perf_event_destroy; + + if (err) + return ERR_PTR(err); + return &power_pmu; +} + +/* + * A event has overflowed; update its count and record + * things if requested. Note that interrupts are hard-disabled + * here so there is no possibility of being interrupted. + */ +static void record_and_restart(struct perf_event *event, unsigned long val, + struct pt_regs *regs, int nmi) +{ + u64 period = event->hw.sample_period; + s64 prev, delta, left; + int record = 0; + + /* we don't have to worry about interrupts here */ + prev = atomic64_read(&event->hw.prev_count); + delta = (val - prev) & 0xfffffffful; + atomic64_add(delta, &event->count); + + /* + * See if the total period for this event has expired, + * and update for the next period. + */ + val = 0; + left = atomic64_read(&event->hw.period_left) - delta; + if (period) { + if (left <= 0) { + left += period; + if (left <= 0) + left = period; + record = 1; + } + if (left < 0x80000000LL) + val = 0x80000000LL - left; + } + + /* + * Finally record data if requested. + */ + if (record) { + struct perf_sample_data data = { + .addr = 0, + .period = event->hw.last_period, + }; + + if (event->attr.sample_type & PERF_SAMPLE_ADDR) + perf_get_data_addr(regs, &data.addr); + + if (perf_event_overflow(event, nmi, &data, regs)) { + /* + * Interrupts are coming too fast - throttle them + * by setting the event to 0, so it will be + * at least 2^30 cycles until the next interrupt + * (assuming each event counts at most 2 counts + * per cycle). + */ + val = 0; + left = ~0ULL >> 1; + } + } + + write_pmc(event->hw.idx, val); + atomic64_set(&event->hw.prev_count, val); + atomic64_set(&event->hw.period_left, left); + perf_event_update_userpage(event); +} + +/* + * Called from generic code to get the misc flags (i.e. processor mode) + * for an event_id. + */ +unsigned long perf_misc_flags(struct pt_regs *regs) +{ + u32 flags = perf_get_misc_flags(regs); + + if (flags) + return flags; + return user_mode(regs) ? PERF_RECORD_MISC_USER : + PERF_RECORD_MISC_KERNEL; +} + +/* + * Called from generic code to get the instruction pointer + * for an event_id. + */ +unsigned long perf_instruction_pointer(struct pt_regs *regs) +{ + unsigned long ip; + + if (TRAP(regs) != 0xf00) + return regs->nip; /* not a PMU interrupt */ + + ip = mfspr(SPRN_SIAR) + perf_ip_adjust(regs); + return ip; +} + +/* + * Performance monitor interrupt stuff + */ +static void perf_event_interrupt(struct pt_regs *regs) +{ + int i; + struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events); + struct perf_event *event; + unsigned long val; + int found = 0; + int nmi; + + if (cpuhw->n_limited) + freeze_limited_events(cpuhw, mfspr(SPRN_PMC5), + mfspr(SPRN_PMC6)); + + perf_read_regs(regs); + + nmi = perf_intr_is_nmi(regs); + if (nmi) + nmi_enter(); + else + irq_enter(); + + for (i = 0; i < cpuhw->n_events; ++i) { + event = cpuhw->event[i]; + if (!event->hw.idx || is_limited_pmc(event->hw.idx)) + continue; + val = read_pmc(event->hw.idx); + if ((int)val < 0) { + /* event has overflowed */ + found = 1; + record_and_restart(event, val, regs, nmi); + } + } + + /* + * In case we didn't find and reset the event that caused + * the interrupt, scan all events and reset any that are + * negative, to avoid getting continual interrupts. + * Any that we processed in the previous loop will not be negative. + */ + if (!found) { + for (i = 0; i < ppmu->n_event; ++i) { + if (is_limited_pmc(i + 1)) + continue; + val = read_pmc(i + 1); + if ((int)val < 0) + write_pmc(i + 1, 0); + } + } + + /* + * Reset MMCR0 to its normal value. This will set PMXE and + * clear FC (freeze events) and PMAO (perf mon alert occurred) + * and thus allow interrupts to occur again. + * XXX might want to use MSR.PM to keep the events frozen until + * we get back out of this interrupt. + */ + write_mmcr0(cpuhw, cpuhw->mmcr[0]); + + if (nmi) + nmi_exit(); + else + irq_exit(); +} + +void hw_perf_event_setup(int cpu) +{ + struct cpu_hw_events *cpuhw = &per_cpu(cpu_hw_events, cpu); + + if (!ppmu) + return; + memset(cpuhw, 0, sizeof(*cpuhw)); + cpuhw->mmcr[0] = MMCR0_FC; +} + +int register_power_pmu(struct power_pmu *pmu) +{ + if (ppmu) + return -EBUSY; /* something's already registered */ + + ppmu = pmu; + pr_info("%s performance monitor hardware support registered\n", + pmu->name); + +#ifdef MSR_HV + /* + * Use FCHV to ignore kernel events if MSR.HV is set. + */ + if (mfmsr() & MSR_HV) + freeze_events_kernel = MMCR0_FCHV; +#endif /* CONFIG_PPC64 */ + + return 0; +} diff --git a/arch/powerpc/kernel/power4-pmu.c b/arch/powerpc/kernel/power4-pmu.c index 3c90a3d9173e..2a361cdda635 100644 --- a/arch/powerpc/kernel/power4-pmu.c +++ b/arch/powerpc/kernel/power4-pmu.c @@ -9,7 +9,7 @@ * 2 of the License, or (at your option) any later version. */ #include -#include +#include #include #include #include diff --git a/arch/powerpc/kernel/power5+-pmu.c b/arch/powerpc/kernel/power5+-pmu.c index 31918af3e355..0f4c1c73a6ad 100644 --- a/arch/powerpc/kernel/power5+-pmu.c +++ b/arch/powerpc/kernel/power5+-pmu.c @@ -9,7 +9,7 @@ * 2 of the License, or (at your option) any later version. */ #include -#include +#include #include #include #include diff --git a/arch/powerpc/kernel/power5-pmu.c b/arch/powerpc/kernel/power5-pmu.c index 867f6f663963..c351b3a57fbb 100644 --- a/arch/powerpc/kernel/power5-pmu.c +++ b/arch/powerpc/kernel/power5-pmu.c @@ -9,7 +9,7 @@ * 2 of the License, or (at your option) any later version. */ #include -#include +#include #include #include #include diff --git a/arch/powerpc/kernel/power6-pmu.c b/arch/powerpc/kernel/power6-pmu.c index fa21890531da..ca399ba5034c 100644 --- a/arch/powerpc/kernel/power6-pmu.c +++ b/arch/powerpc/kernel/power6-pmu.c @@ -9,7 +9,7 @@ * 2 of the License, or (at your option) any later version. */ #include -#include +#include #include #include #include diff --git a/arch/powerpc/kernel/power7-pmu.c b/arch/powerpc/kernel/power7-pmu.c index 018d094d92f9..28a4daacdc02 100644 --- a/arch/powerpc/kernel/power7-pmu.c +++ b/arch/powerpc/kernel/power7-pmu.c @@ -9,7 +9,7 @@ * 2 of the License, or (at your option) any later version. */ #include -#include +#include #include #include #include diff --git a/arch/powerpc/kernel/ppc970-pmu.c b/arch/powerpc/kernel/ppc970-pmu.c index 75dccb71a043..479574413a93 100644 --- a/arch/powerpc/kernel/ppc970-pmu.c +++ b/arch/powerpc/kernel/ppc970-pmu.c @@ -9,7 +9,7 @@ * 2 of the License, or (at your option) any later version. */ #include -#include +#include #include #include diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c index 465e498bcb33..df45a7449a66 100644 --- a/arch/powerpc/kernel/time.c +++ b/arch/powerpc/kernel/time.c @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include #include @@ -527,25 +527,25 @@ void __init iSeries_time_init_early(void) } #endif /* CONFIG_PPC_ISERIES */ -#if defined(CONFIG_PERF_COUNTERS) && defined(CONFIG_PPC32) -DEFINE_PER_CPU(u8, perf_counter_pending); +#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_PPC32) +DEFINE_PER_CPU(u8, perf_event_pending); -void set_perf_counter_pending(void) +void set_perf_event_pending(void) { - get_cpu_var(perf_counter_pending) = 1; + get_cpu_var(perf_event_pending) = 1; set_dec(1); - put_cpu_var(perf_counter_pending); + put_cpu_var(perf_event_pending); } -#define test_perf_counter_pending() __get_cpu_var(perf_counter_pending) -#define clear_perf_counter_pending() __get_cpu_var(perf_counter_pending) = 0 +#define test_perf_event_pending() __get_cpu_var(perf_event_pending) +#define clear_perf_event_pending() __get_cpu_var(perf_event_pending) = 0 -#else /* CONFIG_PERF_COUNTERS && CONFIG_PPC32 */ +#else /* CONFIG_PERF_EVENTS && CONFIG_PPC32 */ -#define test_perf_counter_pending() 0 -#define clear_perf_counter_pending() +#define test_perf_event_pending() 0 +#define clear_perf_event_pending() -#endif /* CONFIG_PERF_COUNTERS && CONFIG_PPC32 */ +#endif /* CONFIG_PERF_EVENTS && CONFIG_PPC32 */ /* * For iSeries shared processors, we have to let the hypervisor @@ -573,9 +573,9 @@ void timer_interrupt(struct pt_regs * regs) set_dec(DECREMENTER_MAX); #ifdef CONFIG_PPC32 - if (test_perf_counter_pending()) { - clear_perf_counter_pending(); - perf_counter_do_pending(); + if (test_perf_event_pending()) { + clear_perf_event_pending(); + perf_event_do_pending(); } if (atomic_read(&ppc_n_lost_interrupts) != 0) do_IRQ(regs); diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 830bef0a1131..e7dae82c1285 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include @@ -171,7 +171,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, die("Weird page fault", regs, SIGSEGV); } - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); /* When running in the kernel we expect faults to occur only to * addresses in user space. All other faults represent errors in the @@ -312,7 +312,7 @@ good_area: } if (ret & VM_FAULT_MAJOR) { current->maj_flt++; - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, regs, address); #ifdef CONFIG_PPC_SMLPAR if (firmware_has_feature(FW_FEATURE_CMO)) { @@ -323,7 +323,7 @@ good_area: #endif } else { current->min_flt++; - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, regs, address); } up_read(&mm->mmap_sem); diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype index 9efc8bda01b4..e382cae678b8 100644 --- a/arch/powerpc/platforms/Kconfig.cputype +++ b/arch/powerpc/platforms/Kconfig.cputype @@ -280,9 +280,9 @@ config PPC_HAVE_PMU_SUPPORT config PPC_PERF_CTRS def_bool y - depends on PERF_COUNTERS && PPC_HAVE_PMU_SUPPORT + depends on PERF_EVENTS && PPC_HAVE_PMU_SUPPORT help - This enables the powerpc-specific perf_counter back-end. + This enables the powerpc-specific perf_event back-end. config SMP depends on PPC_BOOK3S || PPC_BOOK3E || FSL_BOOKE diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 1c866efd217d..43c0acad7160 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -94,7 +94,7 @@ config S390 select HAVE_KVM if 64BIT select HAVE_ARCH_TRACEHOOK select INIT_ALL_POSSIBLE - select HAVE_PERF_COUNTERS + select HAVE_PERF_EVENTS config SCHED_OMIT_FRAME_POINTER bool diff --git a/arch/s390/include/asm/perf_counter.h b/arch/s390/include/asm/perf_counter.h deleted file mode 100644 index 7015188c2cc2..000000000000 --- a/arch/s390/include/asm/perf_counter.h +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Performance counter support - s390 specific definitions. - * - * Copyright 2009 Martin Schwidefsky, IBM Corporation. - */ - -static inline void set_perf_counter_pending(void) {} -static inline void clear_perf_counter_pending(void) {} - -#define PERF_COUNTER_INDEX_OFFSET 0 diff --git a/arch/s390/include/asm/perf_event.h b/arch/s390/include/asm/perf_event.h new file mode 100644 index 000000000000..3840cbe77637 --- /dev/null +++ b/arch/s390/include/asm/perf_event.h @@ -0,0 +1,10 @@ +/* + * Performance event support - s390 specific definitions. + * + * Copyright 2009 Martin Schwidefsky, IBM Corporation. + */ + +static inline void set_perf_event_pending(void) {} +static inline void clear_perf_event_pending(void) {} + +#define PERF_EVENT_INDEX_OFFSET 0 diff --git a/arch/s390/include/asm/unistd.h b/arch/s390/include/asm/unistd.h index c80602d7c880..cb5232df151e 100644 --- a/arch/s390/include/asm/unistd.h +++ b/arch/s390/include/asm/unistd.h @@ -268,7 +268,7 @@ #define __NR_preadv 328 #define __NR_pwritev 329 #define __NR_rt_tgsigqueueinfo 330 -#define __NR_perf_counter_open 331 +#define __NR_perf_event_open 331 #define NR_syscalls 332 /* diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S index 88a83366819f..624790042d41 100644 --- a/arch/s390/kernel/compat_wrapper.S +++ b/arch/s390/kernel/compat_wrapper.S @@ -1832,11 +1832,11 @@ compat_sys_rt_tgsigqueueinfo_wrapper: llgtr %r5,%r5 # struct compat_siginfo * jg compat_sys_rt_tgsigqueueinfo_wrapper # branch to system call - .globl sys_perf_counter_open_wrapper -sys_perf_counter_open_wrapper: - llgtr %r2,%r2 # const struct perf_counter_attr * + .globl sys_perf_event_open_wrapper +sys_perf_event_open_wrapper: + llgtr %r2,%r2 # const struct perf_event_attr * lgfr %r3,%r3 # pid_t lgfr %r4,%r4 # int lgfr %r5,%r5 # int llgfr %r6,%r6 # unsigned long - jg sys_perf_counter_open # branch to system call + jg sys_perf_event_open # branch to system call diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S index ad1acd200385..0b5083681e77 100644 --- a/arch/s390/kernel/syscalls.S +++ b/arch/s390/kernel/syscalls.S @@ -339,4 +339,4 @@ SYSCALL(sys_epoll_create1,sys_epoll_create1,sys_epoll_create1_wrapper) SYSCALL(sys_preadv,sys_preadv,compat_sys_preadv_wrapper) SYSCALL(sys_pwritev,sys_pwritev,compat_sys_pwritev_wrapper) SYSCALL(sys_rt_tgsigqueueinfo,sys_rt_tgsigqueueinfo,compat_sys_rt_tgsigqueueinfo_wrapper) /* 330 */ -SYSCALL(sys_perf_counter_open,sys_perf_counter_open,sys_perf_counter_open_wrapper) +SYSCALL(sys_perf_event_open,sys_perf_event_open,sys_perf_event_open_wrapper) diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 1abbadd497e1..6d507462967a 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -10,7 +10,7 @@ * Copyright (C) 1995 Linus Torvalds */ -#include +#include #include #include #include @@ -306,7 +306,7 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int write) * interrupts again and then search the VMAs */ local_irq_enable(); - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); down_read(&mm->mmap_sem); si_code = SEGV_MAPERR; @@ -366,11 +366,11 @@ good_area: } if (fault & VM_FAULT_MAJOR) { tsk->maj_flt++; - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, regs, address); } else { tsk->min_flt++; - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, regs, address); } up_read(&mm->mmap_sem); diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 4df3570fe511..b940424f8ccc 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -16,7 +16,7 @@ config SUPERH select HAVE_IOREMAP_PROT if MMU select HAVE_ARCH_TRACEHOOK select HAVE_DMA_API_DEBUG - select HAVE_PERF_COUNTERS + select HAVE_PERF_EVENTS select HAVE_KERNEL_GZIP select HAVE_KERNEL_BZIP2 select HAVE_KERNEL_LZMA diff --git a/arch/sh/include/asm/perf_counter.h b/arch/sh/include/asm/perf_counter.h deleted file mode 100644 index d8e6bb9c0ccc..000000000000 --- a/arch/sh/include/asm/perf_counter.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef __ASM_SH_PERF_COUNTER_H -#define __ASM_SH_PERF_COUNTER_H - -/* SH only supports software counters through this interface. */ -static inline void set_perf_counter_pending(void) {} - -#define PERF_COUNTER_INDEX_OFFSET 0 - -#endif /* __ASM_SH_PERF_COUNTER_H */ diff --git a/arch/sh/include/asm/perf_event.h b/arch/sh/include/asm/perf_event.h new file mode 100644 index 000000000000..11a302297ab7 --- /dev/null +++ b/arch/sh/include/asm/perf_event.h @@ -0,0 +1,9 @@ +#ifndef __ASM_SH_PERF_EVENT_H +#define __ASM_SH_PERF_EVENT_H + +/* SH only supports software events through this interface. */ +static inline void set_perf_event_pending(void) {} + +#define PERF_EVENT_INDEX_OFFSET 0 + +#endif /* __ASM_SH_PERF_EVENT_H */ diff --git a/arch/sh/include/asm/unistd_32.h b/arch/sh/include/asm/unistd_32.h index 925dd40d9d55..f3fd1b9eb6b1 100644 --- a/arch/sh/include/asm/unistd_32.h +++ b/arch/sh/include/asm/unistd_32.h @@ -344,7 +344,7 @@ #define __NR_preadv 333 #define __NR_pwritev 334 #define __NR_rt_tgsigqueueinfo 335 -#define __NR_perf_counter_open 336 +#define __NR_perf_event_open 336 #define NR_syscalls 337 diff --git a/arch/sh/include/asm/unistd_64.h b/arch/sh/include/asm/unistd_64.h index 2b84bc916bc5..343ce8f073ea 100644 --- a/arch/sh/include/asm/unistd_64.h +++ b/arch/sh/include/asm/unistd_64.h @@ -384,7 +384,7 @@ #define __NR_preadv 361 #define __NR_pwritev 362 #define __NR_rt_tgsigqueueinfo 363 -#define __NR_perf_counter_open 364 +#define __NR_perf_event_open 364 #ifdef __KERNEL__ diff --git a/arch/sh/kernel/syscalls_32.S b/arch/sh/kernel/syscalls_32.S index 16ba225ede89..19fd11dd9871 100644 --- a/arch/sh/kernel/syscalls_32.S +++ b/arch/sh/kernel/syscalls_32.S @@ -352,4 +352,4 @@ ENTRY(sys_call_table) .long sys_preadv .long sys_pwritev .long sys_rt_tgsigqueueinfo /* 335 */ - .long sys_perf_counter_open + .long sys_perf_event_open diff --git a/arch/sh/kernel/syscalls_64.S b/arch/sh/kernel/syscalls_64.S index af6fb7410c21..5bfde6c77498 100644 --- a/arch/sh/kernel/syscalls_64.S +++ b/arch/sh/kernel/syscalls_64.S @@ -390,4 +390,4 @@ sys_call_table: .long sys_preadv .long sys_pwritev .long sys_rt_tgsigqueueinfo - .long sys_perf_counter_open + .long sys_perf_event_open diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c index 781b413ff82d..47530104e0ad 100644 --- a/arch/sh/mm/fault_32.c +++ b/arch/sh/mm/fault_32.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include @@ -157,7 +157,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, if ((regs->sr & SR_IMASK) != SR_IMASK) local_irq_enable(); - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); /* * If we're in an interrupt, have no user context or are running @@ -208,11 +208,11 @@ survive: } if (fault & VM_FAULT_MAJOR) { tsk->maj_flt++; - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, regs, address); } else { tsk->min_flt++; - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, regs, address); } diff --git a/arch/sh/mm/tlbflush_64.c b/arch/sh/mm/tlbflush_64.c index 2dcc48528f7a..de0b0e881823 100644 --- a/arch/sh/mm/tlbflush_64.c +++ b/arch/sh/mm/tlbflush_64.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -116,7 +116,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess, /* Not an IO address, so reenable interrupts */ local_irq_enable(); - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); /* * If we're in an interrupt or have no user @@ -201,11 +201,11 @@ survive: if (fault & VM_FAULT_MAJOR) { tsk->maj_flt++; - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, regs, address); } else { tsk->min_flt++; - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, regs, address); } diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 86b82348b97c..97fca4695e0b 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -25,7 +25,7 @@ config SPARC select ARCH_WANT_OPTIONAL_GPIOLIB select RTC_CLASS select RTC_DRV_M48T59 - select HAVE_PERF_COUNTERS + select HAVE_PERF_EVENTS select HAVE_DMA_ATTRS select HAVE_DMA_API_DEBUG @@ -47,7 +47,7 @@ config SPARC64 select RTC_DRV_BQ4802 select RTC_DRV_SUN4V select RTC_DRV_STARFIRE - select HAVE_PERF_COUNTERS + select HAVE_PERF_EVENTS config ARCH_DEFCONFIG string diff --git a/arch/sparc/include/asm/perf_counter.h b/arch/sparc/include/asm/perf_counter.h deleted file mode 100644 index 5d7a8ca0e491..000000000000 --- a/arch/sparc/include/asm/perf_counter.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __ASM_SPARC_PERF_COUNTER_H -#define __ASM_SPARC_PERF_COUNTER_H - -extern void set_perf_counter_pending(void); - -#define PERF_COUNTER_INDEX_OFFSET 0 - -#ifdef CONFIG_PERF_COUNTERS -extern void init_hw_perf_counters(void); -#else -static inline void init_hw_perf_counters(void) { } -#endif - -#endif diff --git a/arch/sparc/include/asm/perf_event.h b/arch/sparc/include/asm/perf_event.h new file mode 100644 index 000000000000..7e2669894ce8 --- /dev/null +++ b/arch/sparc/include/asm/perf_event.h @@ -0,0 +1,14 @@ +#ifndef __ASM_SPARC_PERF_EVENT_H +#define __ASM_SPARC_PERF_EVENT_H + +extern void set_perf_event_pending(void); + +#define PERF_EVENT_INDEX_OFFSET 0 + +#ifdef CONFIG_PERF_EVENTS +extern void init_hw_perf_events(void); +#else +static inline void init_hw_perf_events(void) { } +#endif + +#endif diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h index 706df669f3b8..42f2316c3eaa 100644 --- a/arch/sparc/include/asm/unistd.h +++ b/arch/sparc/include/asm/unistd.h @@ -395,7 +395,7 @@ #define __NR_preadv 324 #define __NR_pwritev 325 #define __NR_rt_tgsigqueueinfo 326 -#define __NR_perf_counter_open 327 +#define __NR_perf_event_open 327 #define NR_SYSCALLS 328 diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 247cc620cee5..3a048fad7ee2 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -104,5 +104,5 @@ obj-$(CONFIG_AUDIT) += audit.o audit--$(CONFIG_AUDIT) := compat_audit.o obj-$(CONFIG_COMPAT) += $(audit--y) -pc--$(CONFIG_PERF_COUNTERS) := perf_counter.o +pc--$(CONFIG_PERF_EVENTS) := perf_event.o obj-$(CONFIG_SPARC64) += $(pc--y) diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c index 378eb53e0776..b129611590a4 100644 --- a/arch/sparc/kernel/nmi.c +++ b/arch/sparc/kernel/nmi.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include @@ -265,7 +265,7 @@ int __init nmi_init(void) } } if (!err) - init_hw_perf_counters(); + init_hw_perf_events(); return err; } diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c index 68ff00107073..2d94e7a03af5 100644 --- a/arch/sparc/kernel/pcr.c +++ b/arch/sparc/kernel/pcr.c @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include @@ -15,7 +15,7 @@ /* This code is shared between various users of the performance * counters. Users will be oprofile, pseudo-NMI watchdog, and the - * perf_counter support layer. + * perf_event support layer. */ #define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE) @@ -42,14 +42,14 @@ void deferred_pcr_work_irq(int irq, struct pt_regs *regs) old_regs = set_irq_regs(regs); irq_enter(); -#ifdef CONFIG_PERF_COUNTERS - perf_counter_do_pending(); +#ifdef CONFIG_PERF_EVENTS + perf_event_do_pending(); #endif irq_exit(); set_irq_regs(old_regs); } -void set_perf_counter_pending(void) +void set_perf_event_pending(void) { set_softint(1 << PIL_DEFERRED_PCR_WORK); } diff --git a/arch/sparc/kernel/perf_counter.c b/arch/sparc/kernel/perf_counter.c deleted file mode 100644 index b1265ce8a053..000000000000 --- a/arch/sparc/kernel/perf_counter.c +++ /dev/null @@ -1,556 +0,0 @@ -/* Performance counter support for sparc64. - * - * Copyright (C) 2009 David S. Miller - * - * This code is based almost entirely upon the x86 perf counter - * code, which is: - * - * Copyright (C) 2008 Thomas Gleixner - * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar - * Copyright (C) 2009 Jaswinder Singh Rajput - * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter - * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* Sparc64 chips have two performance counters, 32-bits each, with - * overflow interrupts generated on transition from 0xffffffff to 0. - * The counters are accessed in one go using a 64-bit register. - * - * Both counters are controlled using a single control register. The - * only way to stop all sampling is to clear all of the context (user, - * supervisor, hypervisor) sampling enable bits. But these bits apply - * to both counters, thus the two counters can't be enabled/disabled - * individually. - * - * The control register has two event fields, one for each of the two - * counters. It's thus nearly impossible to have one counter going - * while keeping the other one stopped. Therefore it is possible to - * get overflow interrupts for counters not currently "in use" and - * that condition must be checked in the overflow interrupt handler. - * - * So we use a hack, in that we program inactive counters with the - * "sw_count0" and "sw_count1" events. These count how many times - * the instruction "sethi %hi(0xfc000), %g0" is executed. It's an - * unusual way to encode a NOP and therefore will not trigger in - * normal code. - */ - -#define MAX_HWCOUNTERS 2 -#define MAX_PERIOD ((1UL << 32) - 1) - -#define PIC_UPPER_INDEX 0 -#define PIC_LOWER_INDEX 1 - -struct cpu_hw_counters { - struct perf_counter *counters[MAX_HWCOUNTERS]; - unsigned long used_mask[BITS_TO_LONGS(MAX_HWCOUNTERS)]; - unsigned long active_mask[BITS_TO_LONGS(MAX_HWCOUNTERS)]; - int enabled; -}; -DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters) = { .enabled = 1, }; - -struct perf_event_map { - u16 encoding; - u8 pic_mask; -#define PIC_NONE 0x00 -#define PIC_UPPER 0x01 -#define PIC_LOWER 0x02 -}; - -struct sparc_pmu { - const struct perf_event_map *(*event_map)(int); - int max_events; - int upper_shift; - int lower_shift; - int event_mask; - int hv_bit; - int irq_bit; - int upper_nop; - int lower_nop; -}; - -static const struct perf_event_map ultra3i_perfmon_event_map[] = { - [PERF_COUNT_HW_CPU_CYCLES] = { 0x0000, PIC_UPPER | PIC_LOWER }, - [PERF_COUNT_HW_INSTRUCTIONS] = { 0x0001, PIC_UPPER | PIC_LOWER }, - [PERF_COUNT_HW_CACHE_REFERENCES] = { 0x0009, PIC_LOWER }, - [PERF_COUNT_HW_CACHE_MISSES] = { 0x0009, PIC_UPPER }, -}; - -static const struct perf_event_map *ultra3i_event_map(int event) -{ - return &ultra3i_perfmon_event_map[event]; -} - -static const struct sparc_pmu ultra3i_pmu = { - .event_map = ultra3i_event_map, - .max_events = ARRAY_SIZE(ultra3i_perfmon_event_map), - .upper_shift = 11, - .lower_shift = 4, - .event_mask = 0x3f, - .upper_nop = 0x1c, - .lower_nop = 0x14, -}; - -static const struct perf_event_map niagara2_perfmon_event_map[] = { - [PERF_COUNT_HW_CPU_CYCLES] = { 0x02ff, PIC_UPPER | PIC_LOWER }, - [PERF_COUNT_HW_INSTRUCTIONS] = { 0x02ff, PIC_UPPER | PIC_LOWER }, - [PERF_COUNT_HW_CACHE_REFERENCES] = { 0x0208, PIC_UPPER | PIC_LOWER }, - [PERF_COUNT_HW_CACHE_MISSES] = { 0x0302, PIC_UPPER | PIC_LOWER }, - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { 0x0201, PIC_UPPER | PIC_LOWER }, - [PERF_COUNT_HW_BRANCH_MISSES] = { 0x0202, PIC_UPPER | PIC_LOWER }, -}; - -static const struct perf_event_map *niagara2_event_map(int event) -{ - return &niagara2_perfmon_event_map[event]; -} - -static const struct sparc_pmu niagara2_pmu = { - .event_map = niagara2_event_map, - .max_events = ARRAY_SIZE(niagara2_perfmon_event_map), - .upper_shift = 19, - .lower_shift = 6, - .event_mask = 0xfff, - .hv_bit = 0x8, - .irq_bit = 0x03, - .upper_nop = 0x220, - .lower_nop = 0x220, -}; - -static const struct sparc_pmu *sparc_pmu __read_mostly; - -static u64 event_encoding(u64 event, int idx) -{ - if (idx == PIC_UPPER_INDEX) - event <<= sparc_pmu->upper_shift; - else - event <<= sparc_pmu->lower_shift; - return event; -} - -static u64 mask_for_index(int idx) -{ - return event_encoding(sparc_pmu->event_mask, idx); -} - -static u64 nop_for_index(int idx) -{ - return event_encoding(idx == PIC_UPPER_INDEX ? - sparc_pmu->upper_nop : - sparc_pmu->lower_nop, idx); -} - -static inline void sparc_pmu_enable_counter(struct hw_perf_counter *hwc, - int idx) -{ - u64 val, mask = mask_for_index(idx); - - val = pcr_ops->read(); - pcr_ops->write((val & ~mask) | hwc->config); -} - -static inline void sparc_pmu_disable_counter(struct hw_perf_counter *hwc, - int idx) -{ - u64 mask = mask_for_index(idx); - u64 nop = nop_for_index(idx); - u64 val = pcr_ops->read(); - - pcr_ops->write((val & ~mask) | nop); -} - -void hw_perf_enable(void) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - u64 val; - int i; - - if (cpuc->enabled) - return; - - cpuc->enabled = 1; - barrier(); - - val = pcr_ops->read(); - - for (i = 0; i < MAX_HWCOUNTERS; i++) { - struct perf_counter *cp = cpuc->counters[i]; - struct hw_perf_counter *hwc; - - if (!cp) - continue; - hwc = &cp->hw; - val |= hwc->config_base; - } - - pcr_ops->write(val); -} - -void hw_perf_disable(void) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - u64 val; - - if (!cpuc->enabled) - return; - - cpuc->enabled = 0; - - val = pcr_ops->read(); - val &= ~(PCR_UTRACE | PCR_STRACE | - sparc_pmu->hv_bit | sparc_pmu->irq_bit); - pcr_ops->write(val); -} - -static u32 read_pmc(int idx) -{ - u64 val; - - read_pic(val); - if (idx == PIC_UPPER_INDEX) - val >>= 32; - - return val & 0xffffffff; -} - -static void write_pmc(int idx, u64 val) -{ - u64 shift, mask, pic; - - shift = 0; - if (idx == PIC_UPPER_INDEX) - shift = 32; - - mask = ((u64) 0xffffffff) << shift; - val <<= shift; - - read_pic(pic); - pic &= ~mask; - pic |= val; - write_pic(pic); -} - -static int sparc_perf_counter_set_period(struct perf_counter *counter, - struct hw_perf_counter *hwc, int idx) -{ - s64 left = atomic64_read(&hwc->period_left); - s64 period = hwc->sample_period; - int ret = 0; - - if (unlikely(left <= -period)) { - left = period; - atomic64_set(&hwc->period_left, left); - hwc->last_period = period; - ret = 1; - } - - if (unlikely(left <= 0)) { - left += period; - atomic64_set(&hwc->period_left, left); - hwc->last_period = period; - ret = 1; - } - if (left > MAX_PERIOD) - left = MAX_PERIOD; - - atomic64_set(&hwc->prev_count, (u64)-left); - - write_pmc(idx, (u64)(-left) & 0xffffffff); - - perf_counter_update_userpage(counter); - - return ret; -} - -static int sparc_pmu_enable(struct perf_counter *counter) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - struct hw_perf_counter *hwc = &counter->hw; - int idx = hwc->idx; - - if (test_and_set_bit(idx, cpuc->used_mask)) - return -EAGAIN; - - sparc_pmu_disable_counter(hwc, idx); - - cpuc->counters[idx] = counter; - set_bit(idx, cpuc->active_mask); - - sparc_perf_counter_set_period(counter, hwc, idx); - sparc_pmu_enable_counter(hwc, idx); - perf_counter_update_userpage(counter); - return 0; -} - -static u64 sparc_perf_counter_update(struct perf_counter *counter, - struct hw_perf_counter *hwc, int idx) -{ - int shift = 64 - 32; - u64 prev_raw_count, new_raw_count; - s64 delta; - -again: - prev_raw_count = atomic64_read(&hwc->prev_count); - new_raw_count = read_pmc(idx); - - if (atomic64_cmpxchg(&hwc->prev_count, prev_raw_count, - new_raw_count) != prev_raw_count) - goto again; - - delta = (new_raw_count << shift) - (prev_raw_count << shift); - delta >>= shift; - - atomic64_add(delta, &counter->count); - atomic64_sub(delta, &hwc->period_left); - - return new_raw_count; -} - -static void sparc_pmu_disable(struct perf_counter *counter) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - struct hw_perf_counter *hwc = &counter->hw; - int idx = hwc->idx; - - clear_bit(idx, cpuc->active_mask); - sparc_pmu_disable_counter(hwc, idx); - - barrier(); - - sparc_perf_counter_update(counter, hwc, idx); - cpuc->counters[idx] = NULL; - clear_bit(idx, cpuc->used_mask); - - perf_counter_update_userpage(counter); -} - -static void sparc_pmu_read(struct perf_counter *counter) -{ - struct hw_perf_counter *hwc = &counter->hw; - sparc_perf_counter_update(counter, hwc, hwc->idx); -} - -static void sparc_pmu_unthrottle(struct perf_counter *counter) -{ - struct hw_perf_counter *hwc = &counter->hw; - sparc_pmu_enable_counter(hwc, hwc->idx); -} - -static atomic_t active_counters = ATOMIC_INIT(0); -static DEFINE_MUTEX(pmc_grab_mutex); - -void perf_counter_grab_pmc(void) -{ - if (atomic_inc_not_zero(&active_counters)) - return; - - mutex_lock(&pmc_grab_mutex); - if (atomic_read(&active_counters) == 0) { - if (atomic_read(&nmi_active) > 0) { - on_each_cpu(stop_nmi_watchdog, NULL, 1); - BUG_ON(atomic_read(&nmi_active) != 0); - } - atomic_inc(&active_counters); - } - mutex_unlock(&pmc_grab_mutex); -} - -void perf_counter_release_pmc(void) -{ - if (atomic_dec_and_mutex_lock(&active_counters, &pmc_grab_mutex)) { - if (atomic_read(&nmi_active) == 0) - on_each_cpu(start_nmi_watchdog, NULL, 1); - mutex_unlock(&pmc_grab_mutex); - } -} - -static void hw_perf_counter_destroy(struct perf_counter *counter) -{ - perf_counter_release_pmc(); -} - -static int __hw_perf_counter_init(struct perf_counter *counter) -{ - struct perf_counter_attr *attr = &counter->attr; - struct hw_perf_counter *hwc = &counter->hw; - const struct perf_event_map *pmap; - u64 enc; - - if (atomic_read(&nmi_active) < 0) - return -ENODEV; - - if (attr->type != PERF_TYPE_HARDWARE) - return -EOPNOTSUPP; - - if (attr->config >= sparc_pmu->max_events) - return -EINVAL; - - perf_counter_grab_pmc(); - counter->destroy = hw_perf_counter_destroy; - - /* We save the enable bits in the config_base. So to - * turn off sampling just write 'config', and to enable - * things write 'config | config_base'. - */ - hwc->config_base = sparc_pmu->irq_bit; - if (!attr->exclude_user) - hwc->config_base |= PCR_UTRACE; - if (!attr->exclude_kernel) - hwc->config_base |= PCR_STRACE; - if (!attr->exclude_hv) - hwc->config_base |= sparc_pmu->hv_bit; - - if (!hwc->sample_period) { - hwc->sample_period = MAX_PERIOD; - hwc->last_period = hwc->sample_period; - atomic64_set(&hwc->period_left, hwc->sample_period); - } - - pmap = sparc_pmu->event_map(attr->config); - - enc = pmap->encoding; - if (pmap->pic_mask & PIC_UPPER) { - hwc->idx = PIC_UPPER_INDEX; - enc <<= sparc_pmu->upper_shift; - } else { - hwc->idx = PIC_LOWER_INDEX; - enc <<= sparc_pmu->lower_shift; - } - - hwc->config |= enc; - return 0; -} - -static const struct pmu pmu = { - .enable = sparc_pmu_enable, - .disable = sparc_pmu_disable, - .read = sparc_pmu_read, - .unthrottle = sparc_pmu_unthrottle, -}; - -const struct pmu *hw_perf_counter_init(struct perf_counter *counter) -{ - int err = __hw_perf_counter_init(counter); - - if (err) - return ERR_PTR(err); - return &pmu; -} - -void perf_counter_print_debug(void) -{ - unsigned long flags; - u64 pcr, pic; - int cpu; - - if (!sparc_pmu) - return; - - local_irq_save(flags); - - cpu = smp_processor_id(); - - pcr = pcr_ops->read(); - read_pic(pic); - - pr_info("\n"); - pr_info("CPU#%d: PCR[%016llx] PIC[%016llx]\n", - cpu, pcr, pic); - - local_irq_restore(flags); -} - -static int __kprobes perf_counter_nmi_handler(struct notifier_block *self, - unsigned long cmd, void *__args) -{ - struct die_args *args = __args; - struct perf_sample_data data; - struct cpu_hw_counters *cpuc; - struct pt_regs *regs; - int idx; - - if (!atomic_read(&active_counters)) - return NOTIFY_DONE; - - switch (cmd) { - case DIE_NMI: - break; - - default: - return NOTIFY_DONE; - } - - regs = args->regs; - - data.addr = 0; - - cpuc = &__get_cpu_var(cpu_hw_counters); - for (idx = 0; idx < MAX_HWCOUNTERS; idx++) { - struct perf_counter *counter = cpuc->counters[idx]; - struct hw_perf_counter *hwc; - u64 val; - - if (!test_bit(idx, cpuc->active_mask)) - continue; - hwc = &counter->hw; - val = sparc_perf_counter_update(counter, hwc, idx); - if (val & (1ULL << 31)) - continue; - - data.period = counter->hw.last_period; - if (!sparc_perf_counter_set_period(counter, hwc, idx)) - continue; - - if (perf_counter_overflow(counter, 1, &data, regs)) - sparc_pmu_disable_counter(hwc, idx); - } - - return NOTIFY_STOP; -} - -static __read_mostly struct notifier_block perf_counter_nmi_notifier = { - .notifier_call = perf_counter_nmi_handler, -}; - -static bool __init supported_pmu(void) -{ - if (!strcmp(sparc_pmu_type, "ultra3i")) { - sparc_pmu = &ultra3i_pmu; - return true; - } - if (!strcmp(sparc_pmu_type, "niagara2")) { - sparc_pmu = &niagara2_pmu; - return true; - } - return false; -} - -void __init init_hw_perf_counters(void) -{ - pr_info("Performance counters: "); - - if (!supported_pmu()) { - pr_cont("No support for PMU type '%s'\n", sparc_pmu_type); - return; - } - - pr_cont("Supported PMU type is '%s'\n", sparc_pmu_type); - - /* All sparc64 PMUs currently have 2 counters. But this simple - * driver only supports one active counter at a time. - */ - perf_max_counters = 1; - - register_die_notifier(&perf_counter_nmi_notifier); -} diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c new file mode 100644 index 000000000000..2d6a1b10c81d --- /dev/null +++ b/arch/sparc/kernel/perf_event.c @@ -0,0 +1,556 @@ +/* Performance event support for sparc64. + * + * Copyright (C) 2009 David S. Miller + * + * This code is based almost entirely upon the x86 perf event + * code, which is: + * + * Copyright (C) 2008 Thomas Gleixner + * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2009 Jaswinder Singh Rajput + * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter + * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Sparc64 chips have two performance counters, 32-bits each, with + * overflow interrupts generated on transition from 0xffffffff to 0. + * The counters are accessed in one go using a 64-bit register. + * + * Both counters are controlled using a single control register. The + * only way to stop all sampling is to clear all of the context (user, + * supervisor, hypervisor) sampling enable bits. But these bits apply + * to both counters, thus the two counters can't be enabled/disabled + * individually. + * + * The control register has two event fields, one for each of the two + * counters. It's thus nearly impossible to have one counter going + * while keeping the other one stopped. Therefore it is possible to + * get overflow interrupts for counters not currently "in use" and + * that condition must be checked in the overflow interrupt handler. + * + * So we use a hack, in that we program inactive counters with the + * "sw_count0" and "sw_count1" events. These count how many times + * the instruction "sethi %hi(0xfc000), %g0" is executed. It's an + * unusual way to encode a NOP and therefore will not trigger in + * normal code. + */ + +#define MAX_HWEVENTS 2 +#define MAX_PERIOD ((1UL << 32) - 1) + +#define PIC_UPPER_INDEX 0 +#define PIC_LOWER_INDEX 1 + +struct cpu_hw_events { + struct perf_event *events[MAX_HWEVENTS]; + unsigned long used_mask[BITS_TO_LONGS(MAX_HWEVENTS)]; + unsigned long active_mask[BITS_TO_LONGS(MAX_HWEVENTS)]; + int enabled; +}; +DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { .enabled = 1, }; + +struct perf_event_map { + u16 encoding; + u8 pic_mask; +#define PIC_NONE 0x00 +#define PIC_UPPER 0x01 +#define PIC_LOWER 0x02 +}; + +struct sparc_pmu { + const struct perf_event_map *(*event_map)(int); + int max_events; + int upper_shift; + int lower_shift; + int event_mask; + int hv_bit; + int irq_bit; + int upper_nop; + int lower_nop; +}; + +static const struct perf_event_map ultra3i_perfmon_event_map[] = { + [PERF_COUNT_HW_CPU_CYCLES] = { 0x0000, PIC_UPPER | PIC_LOWER }, + [PERF_COUNT_HW_INSTRUCTIONS] = { 0x0001, PIC_UPPER | PIC_LOWER }, + [PERF_COUNT_HW_CACHE_REFERENCES] = { 0x0009, PIC_LOWER }, + [PERF_COUNT_HW_CACHE_MISSES] = { 0x0009, PIC_UPPER }, +}; + +static const struct perf_event_map *ultra3i_event_map(int event_id) +{ + return &ultra3i_perfmon_event_map[event_id]; +} + +static const struct sparc_pmu ultra3i_pmu = { + .event_map = ultra3i_event_map, + .max_events = ARRAY_SIZE(ultra3i_perfmon_event_map), + .upper_shift = 11, + .lower_shift = 4, + .event_mask = 0x3f, + .upper_nop = 0x1c, + .lower_nop = 0x14, +}; + +static const struct perf_event_map niagara2_perfmon_event_map[] = { + [PERF_COUNT_HW_CPU_CYCLES] = { 0x02ff, PIC_UPPER | PIC_LOWER }, + [PERF_COUNT_HW_INSTRUCTIONS] = { 0x02ff, PIC_UPPER | PIC_LOWER }, + [PERF_COUNT_HW_CACHE_REFERENCES] = { 0x0208, PIC_UPPER | PIC_LOWER }, + [PERF_COUNT_HW_CACHE_MISSES] = { 0x0302, PIC_UPPER | PIC_LOWER }, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { 0x0201, PIC_UPPER | PIC_LOWER }, + [PERF_COUNT_HW_BRANCH_MISSES] = { 0x0202, PIC_UPPER | PIC_LOWER }, +}; + +static const struct perf_event_map *niagara2_event_map(int event_id) +{ + return &niagara2_perfmon_event_map[event_id]; +} + +static const struct sparc_pmu niagara2_pmu = { + .event_map = niagara2_event_map, + .max_events = ARRAY_SIZE(niagara2_perfmon_event_map), + .upper_shift = 19, + .lower_shift = 6, + .event_mask = 0xfff, + .hv_bit = 0x8, + .irq_bit = 0x03, + .upper_nop = 0x220, + .lower_nop = 0x220, +}; + +static const struct sparc_pmu *sparc_pmu __read_mostly; + +static u64 event_encoding(u64 event_id, int idx) +{ + if (idx == PIC_UPPER_INDEX) + event_id <<= sparc_pmu->upper_shift; + else + event_id <<= sparc_pmu->lower_shift; + return event_id; +} + +static u64 mask_for_index(int idx) +{ + return event_encoding(sparc_pmu->event_mask, idx); +} + +static u64 nop_for_index(int idx) +{ + return event_encoding(idx == PIC_UPPER_INDEX ? + sparc_pmu->upper_nop : + sparc_pmu->lower_nop, idx); +} + +static inline void sparc_pmu_enable_event(struct hw_perf_event *hwc, + int idx) +{ + u64 val, mask = mask_for_index(idx); + + val = pcr_ops->read(); + pcr_ops->write((val & ~mask) | hwc->config); +} + +static inline void sparc_pmu_disable_event(struct hw_perf_event *hwc, + int idx) +{ + u64 mask = mask_for_index(idx); + u64 nop = nop_for_index(idx); + u64 val = pcr_ops->read(); + + pcr_ops->write((val & ~mask) | nop); +} + +void hw_perf_enable(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + u64 val; + int i; + + if (cpuc->enabled) + return; + + cpuc->enabled = 1; + barrier(); + + val = pcr_ops->read(); + + for (i = 0; i < MAX_HWEVENTS; i++) { + struct perf_event *cp = cpuc->events[i]; + struct hw_perf_event *hwc; + + if (!cp) + continue; + hwc = &cp->hw; + val |= hwc->config_base; + } + + pcr_ops->write(val); +} + +void hw_perf_disable(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + u64 val; + + if (!cpuc->enabled) + return; + + cpuc->enabled = 0; + + val = pcr_ops->read(); + val &= ~(PCR_UTRACE | PCR_STRACE | + sparc_pmu->hv_bit | sparc_pmu->irq_bit); + pcr_ops->write(val); +} + +static u32 read_pmc(int idx) +{ + u64 val; + + read_pic(val); + if (idx == PIC_UPPER_INDEX) + val >>= 32; + + return val & 0xffffffff; +} + +static void write_pmc(int idx, u64 val) +{ + u64 shift, mask, pic; + + shift = 0; + if (idx == PIC_UPPER_INDEX) + shift = 32; + + mask = ((u64) 0xffffffff) << shift; + val <<= shift; + + read_pic(pic); + pic &= ~mask; + pic |= val; + write_pic(pic); +} + +static int sparc_perf_event_set_period(struct perf_event *event, + struct hw_perf_event *hwc, int idx) +{ + s64 left = atomic64_read(&hwc->period_left); + s64 period = hwc->sample_period; + int ret = 0; + + if (unlikely(left <= -period)) { + left = period; + atomic64_set(&hwc->period_left, left); + hwc->last_period = period; + ret = 1; + } + + if (unlikely(left <= 0)) { + left += period; + atomic64_set(&hwc->period_left, left); + hwc->last_period = period; + ret = 1; + } + if (left > MAX_PERIOD) + left = MAX_PERIOD; + + atomic64_set(&hwc->prev_count, (u64)-left); + + write_pmc(idx, (u64)(-left) & 0xffffffff); + + perf_event_update_userpage(event); + + return ret; +} + +static int sparc_pmu_enable(struct perf_event *event) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + + if (test_and_set_bit(idx, cpuc->used_mask)) + return -EAGAIN; + + sparc_pmu_disable_event(hwc, idx); + + cpuc->events[idx] = event; + set_bit(idx, cpuc->active_mask); + + sparc_perf_event_set_period(event, hwc, idx); + sparc_pmu_enable_event(hwc, idx); + perf_event_update_userpage(event); + return 0; +} + +static u64 sparc_perf_event_update(struct perf_event *event, + struct hw_perf_event *hwc, int idx) +{ + int shift = 64 - 32; + u64 prev_raw_count, new_raw_count; + s64 delta; + +again: + prev_raw_count = atomic64_read(&hwc->prev_count); + new_raw_count = read_pmc(idx); + + if (atomic64_cmpxchg(&hwc->prev_count, prev_raw_count, + new_raw_count) != prev_raw_count) + goto again; + + delta = (new_raw_count << shift) - (prev_raw_count << shift); + delta >>= shift; + + atomic64_add(delta, &event->count); + atomic64_sub(delta, &hwc->period_left); + + return new_raw_count; +} + +static void sparc_pmu_disable(struct perf_event *event) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + + clear_bit(idx, cpuc->active_mask); + sparc_pmu_disable_event(hwc, idx); + + barrier(); + + sparc_perf_event_update(event, hwc, idx); + cpuc->events[idx] = NULL; + clear_bit(idx, cpuc->used_mask); + + perf_event_update_userpage(event); +} + +static void sparc_pmu_read(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + sparc_perf_event_update(event, hwc, hwc->idx); +} + +static void sparc_pmu_unthrottle(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + sparc_pmu_enable_event(hwc, hwc->idx); +} + +static atomic_t active_events = ATOMIC_INIT(0); +static DEFINE_MUTEX(pmc_grab_mutex); + +void perf_event_grab_pmc(void) +{ + if (atomic_inc_not_zero(&active_events)) + return; + + mutex_lock(&pmc_grab_mutex); + if (atomic_read(&active_events) == 0) { + if (atomic_read(&nmi_active) > 0) { + on_each_cpu(stop_nmi_watchdog, NULL, 1); + BUG_ON(atomic_read(&nmi_active) != 0); + } + atomic_inc(&active_events); + } + mutex_unlock(&pmc_grab_mutex); +} + +void perf_event_release_pmc(void) +{ + if (atomic_dec_and_mutex_lock(&active_events, &pmc_grab_mutex)) { + if (atomic_read(&nmi_active) == 0) + on_each_cpu(start_nmi_watchdog, NULL, 1); + mutex_unlock(&pmc_grab_mutex); + } +} + +static void hw_perf_event_destroy(struct perf_event *event) +{ + perf_event_release_pmc(); +} + +static int __hw_perf_event_init(struct perf_event *event) +{ + struct perf_event_attr *attr = &event->attr; + struct hw_perf_event *hwc = &event->hw; + const struct perf_event_map *pmap; + u64 enc; + + if (atomic_read(&nmi_active) < 0) + return -ENODEV; + + if (attr->type != PERF_TYPE_HARDWARE) + return -EOPNOTSUPP; + + if (attr->config >= sparc_pmu->max_events) + return -EINVAL; + + perf_event_grab_pmc(); + event->destroy = hw_perf_event_destroy; + + /* We save the enable bits in the config_base. So to + * turn off sampling just write 'config', and to enable + * things write 'config | config_base'. + */ + hwc->config_base = sparc_pmu->irq_bit; + if (!attr->exclude_user) + hwc->config_base |= PCR_UTRACE; + if (!attr->exclude_kernel) + hwc->config_base |= PCR_STRACE; + if (!attr->exclude_hv) + hwc->config_base |= sparc_pmu->hv_bit; + + if (!hwc->sample_period) { + hwc->sample_period = MAX_PERIOD; + hwc->last_period = hwc->sample_period; + atomic64_set(&hwc->period_left, hwc->sample_period); + } + + pmap = sparc_pmu->event_map(attr->config); + + enc = pmap->encoding; + if (pmap->pic_mask & PIC_UPPER) { + hwc->idx = PIC_UPPER_INDEX; + enc <<= sparc_pmu->upper_shift; + } else { + hwc->idx = PIC_LOWER_INDEX; + enc <<= sparc_pmu->lower_shift; + } + + hwc->config |= enc; + return 0; +} + +static const struct pmu pmu = { + .enable = sparc_pmu_enable, + .disable = sparc_pmu_disable, + .read = sparc_pmu_read, + .unthrottle = sparc_pmu_unthrottle, +}; + +const struct pmu *hw_perf_event_init(struct perf_event *event) +{ + int err = __hw_perf_event_init(event); + + if (err) + return ERR_PTR(err); + return &pmu; +} + +void perf_event_print_debug(void) +{ + unsigned long flags; + u64 pcr, pic; + int cpu; + + if (!sparc_pmu) + return; + + local_irq_save(flags); + + cpu = smp_processor_id(); + + pcr = pcr_ops->read(); + read_pic(pic); + + pr_info("\n"); + pr_info("CPU#%d: PCR[%016llx] PIC[%016llx]\n", + cpu, pcr, pic); + + local_irq_restore(flags); +} + +static int __kprobes perf_event_nmi_handler(struct notifier_block *self, + unsigned long cmd, void *__args) +{ + struct die_args *args = __args; + struct perf_sample_data data; + struct cpu_hw_events *cpuc; + struct pt_regs *regs; + int idx; + + if (!atomic_read(&active_events)) + return NOTIFY_DONE; + + switch (cmd) { + case DIE_NMI: + break; + + default: + return NOTIFY_DONE; + } + + regs = args->regs; + + data.addr = 0; + + cpuc = &__get_cpu_var(cpu_hw_events); + for (idx = 0; idx < MAX_HWEVENTS; idx++) { + struct perf_event *event = cpuc->events[idx]; + struct hw_perf_event *hwc; + u64 val; + + if (!test_bit(idx, cpuc->active_mask)) + continue; + hwc = &event->hw; + val = sparc_perf_event_update(event, hwc, idx); + if (val & (1ULL << 31)) + continue; + + data.period = event->hw.last_period; + if (!sparc_perf_event_set_period(event, hwc, idx)) + continue; + + if (perf_event_overflow(event, 1, &data, regs)) + sparc_pmu_disable_event(hwc, idx); + } + + return NOTIFY_STOP; +} + +static __read_mostly struct notifier_block perf_event_nmi_notifier = { + .notifier_call = perf_event_nmi_handler, +}; + +static bool __init supported_pmu(void) +{ + if (!strcmp(sparc_pmu_type, "ultra3i")) { + sparc_pmu = &ultra3i_pmu; + return true; + } + if (!strcmp(sparc_pmu_type, "niagara2")) { + sparc_pmu = &niagara2_pmu; + return true; + } + return false; +} + +void __init init_hw_perf_events(void) +{ + pr_info("Performance events: "); + + if (!supported_pmu()) { + pr_cont("No support for PMU type '%s'\n", sparc_pmu_type); + return; + } + + pr_cont("Supported PMU type is '%s'\n", sparc_pmu_type); + + /* All sparc64 PMUs currently have 2 events. But this simple + * driver only supports one active event at a time. + */ + perf_max_events = 1; + + register_die_notifier(&perf_event_nmi_notifier); +} diff --git a/arch/sparc/kernel/systbls_32.S b/arch/sparc/kernel/systbls_32.S index 04181577cb65..0f1658d37490 100644 --- a/arch/sparc/kernel/systbls_32.S +++ b/arch/sparc/kernel/systbls_32.S @@ -82,5 +82,5 @@ sys_call_table: /*310*/ .long sys_utimensat, sys_signalfd, sys_timerfd_create, sys_eventfd, sys_fallocate /*315*/ .long sys_timerfd_settime, sys_timerfd_gettime, sys_signalfd4, sys_eventfd2, sys_epoll_create1 /*320*/ .long sys_dup3, sys_pipe2, sys_inotify_init1, sys_accept4, sys_preadv -/*325*/ .long sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_counter_open +/*325*/ .long sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S index 91b06b7f7acf..009825f6e73c 100644 --- a/arch/sparc/kernel/systbls_64.S +++ b/arch/sparc/kernel/systbls_64.S @@ -83,7 +83,7 @@ sys_call_table32: /*310*/ .word compat_sys_utimensat, compat_sys_signalfd, sys_timerfd_create, sys_eventfd, compat_sys_fallocate .word compat_sys_timerfd_settime, compat_sys_timerfd_gettime, compat_sys_signalfd4, sys_eventfd2, sys_epoll_create1 /*320*/ .word sys_dup3, sys_pipe2, sys_inotify_init1, sys_accept4, compat_sys_preadv - .word compat_sys_pwritev, compat_sys_rt_tgsigqueueinfo, sys_perf_counter_open + .word compat_sys_pwritev, compat_sys_rt_tgsigqueueinfo, sys_perf_event_open #endif /* CONFIG_COMPAT */ @@ -158,4 +158,4 @@ sys_call_table: /*310*/ .word sys_utimensat, sys_signalfd, sys_timerfd_create, sys_eventfd, sys_fallocate .word sys_timerfd_settime, sys_timerfd_gettime, sys_signalfd4, sys_eventfd2, sys_epoll_create1 /*320*/ .word sys_dup3, sys_pipe2, sys_inotify_init1, sys_accept4, sys_preadv - .word sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_counter_open + .word sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 51c59015b280..e4ff5d1280ca 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -24,7 +24,7 @@ config X86 select HAVE_UNSTABLE_SCHED_CLOCK select HAVE_IDE select HAVE_OPROFILE - select HAVE_PERF_COUNTERS if (!M386 && !M486) + select HAVE_PERF_EVENTS if (!M386 && !M486) select HAVE_IOREMAP_PROT select HAVE_KPROBES select ARCH_WANT_OPTIONAL_GPIOLIB diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index ba331bfd1112..74619c4f9fda 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -831,5 +831,5 @@ ia32_sys_call_table: .quad compat_sys_preadv .quad compat_sys_pwritev .quad compat_sys_rt_tgsigqueueinfo /* 335 */ - .quad sys_perf_counter_open + .quad sys_perf_event_open ia32_syscall_end: diff --git a/arch/x86/include/asm/entry_arch.h b/arch/x86/include/asm/entry_arch.h index 5e3f2044f0d3..f5693c81a1db 100644 --- a/arch/x86/include/asm/entry_arch.h +++ b/arch/x86/include/asm/entry_arch.h @@ -49,7 +49,7 @@ BUILD_INTERRUPT(apic_timer_interrupt,LOCAL_TIMER_VECTOR) BUILD_INTERRUPT(error_interrupt,ERROR_APIC_VECTOR) BUILD_INTERRUPT(spurious_interrupt,SPURIOUS_APIC_VECTOR) -#ifdef CONFIG_PERF_COUNTERS +#ifdef CONFIG_PERF_EVENTS BUILD_INTERRUPT(perf_pending_interrupt, LOCAL_PENDING_VECTOR) #endif diff --git a/arch/x86/include/asm/perf_counter.h b/arch/x86/include/asm/perf_counter.h deleted file mode 100644 index e7b7c938ae27..000000000000 --- a/arch/x86/include/asm/perf_counter.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef _ASM_X86_PERF_COUNTER_H -#define _ASM_X86_PERF_COUNTER_H - -/* - * Performance counter hw details: - */ - -#define X86_PMC_MAX_GENERIC 8 -#define X86_PMC_MAX_FIXED 3 - -#define X86_PMC_IDX_GENERIC 0 -#define X86_PMC_IDX_FIXED 32 -#define X86_PMC_IDX_MAX 64 - -#define MSR_ARCH_PERFMON_PERFCTR0 0xc1 -#define MSR_ARCH_PERFMON_PERFCTR1 0xc2 - -#define MSR_ARCH_PERFMON_EVENTSEL0 0x186 -#define MSR_ARCH_PERFMON_EVENTSEL1 0x187 - -#define ARCH_PERFMON_EVENTSEL0_ENABLE (1 << 22) -#define ARCH_PERFMON_EVENTSEL_INT (1 << 20) -#define ARCH_PERFMON_EVENTSEL_OS (1 << 17) -#define ARCH_PERFMON_EVENTSEL_USR (1 << 16) - -/* - * Includes eventsel and unit mask as well: - */ -#define ARCH_PERFMON_EVENT_MASK 0xffff - -#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL 0x3c -#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8) -#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX 0 -#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT \ - (1 << (ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX)) - -#define ARCH_PERFMON_BRANCH_MISSES_RETIRED 6 - -/* - * Intel "Architectural Performance Monitoring" CPUID - * detection/enumeration details: - */ -union cpuid10_eax { - struct { - unsigned int version_id:8; - unsigned int num_counters:8; - unsigned int bit_width:8; - unsigned int mask_length:8; - } split; - unsigned int full; -}; - -union cpuid10_edx { - struct { - unsigned int num_counters_fixed:4; - unsigned int reserved:28; - } split; - unsigned int full; -}; - - -/* - * Fixed-purpose performance counters: - */ - -/* - * All 3 fixed-mode PMCs are configured via this single MSR: - */ -#define MSR_ARCH_PERFMON_FIXED_CTR_CTRL 0x38d - -/* - * The counts are available in three separate MSRs: - */ - -/* Instr_Retired.Any: */ -#define MSR_ARCH_PERFMON_FIXED_CTR0 0x309 -#define X86_PMC_IDX_FIXED_INSTRUCTIONS (X86_PMC_IDX_FIXED + 0) - -/* CPU_CLK_Unhalted.Core: */ -#define MSR_ARCH_PERFMON_FIXED_CTR1 0x30a -#define X86_PMC_IDX_FIXED_CPU_CYCLES (X86_PMC_IDX_FIXED + 1) - -/* CPU_CLK_Unhalted.Ref: */ -#define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b -#define X86_PMC_IDX_FIXED_BUS_CYCLES (X86_PMC_IDX_FIXED + 2) - -/* - * We model BTS tracing as another fixed-mode PMC. - * - * We choose a value in the middle of the fixed counter range, since lower - * values are used by actual fixed counters and higher values are used - * to indicate other overflow conditions in the PERF_GLOBAL_STATUS msr. - */ -#define X86_PMC_IDX_FIXED_BTS (X86_PMC_IDX_FIXED + 16) - - -#ifdef CONFIG_PERF_COUNTERS -extern void init_hw_perf_counters(void); -extern void perf_counters_lapic_init(void); - -#define PERF_COUNTER_INDEX_OFFSET 0 - -#else -static inline void init_hw_perf_counters(void) { } -static inline void perf_counters_lapic_init(void) { } -#endif - -#endif /* _ASM_X86_PERF_COUNTER_H */ diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h new file mode 100644 index 000000000000..ad7ce3fd5065 --- /dev/null +++ b/arch/x86/include/asm/perf_event.h @@ -0,0 +1,108 @@ +#ifndef _ASM_X86_PERF_EVENT_H +#define _ASM_X86_PERF_EVENT_H + +/* + * Performance event hw details: + */ + +#define X86_PMC_MAX_GENERIC 8 +#define X86_PMC_MAX_FIXED 3 + +#define X86_PMC_IDX_GENERIC 0 +#define X86_PMC_IDX_FIXED 32 +#define X86_PMC_IDX_MAX 64 + +#define MSR_ARCH_PERFMON_PERFCTR0 0xc1 +#define MSR_ARCH_PERFMON_PERFCTR1 0xc2 + +#define MSR_ARCH_PERFMON_EVENTSEL0 0x186 +#define MSR_ARCH_PERFMON_EVENTSEL1 0x187 + +#define ARCH_PERFMON_EVENTSEL0_ENABLE (1 << 22) +#define ARCH_PERFMON_EVENTSEL_INT (1 << 20) +#define ARCH_PERFMON_EVENTSEL_OS (1 << 17) +#define ARCH_PERFMON_EVENTSEL_USR (1 << 16) + +/* + * Includes eventsel and unit mask as well: + */ +#define ARCH_PERFMON_EVENT_MASK 0xffff + +#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_SEL 0x3c +#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_UMASK (0x00 << 8) +#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX 0 +#define ARCH_PERFMON_UNHALTED_CORE_CYCLES_PRESENT \ + (1 << (ARCH_PERFMON_UNHALTED_CORE_CYCLES_INDEX)) + +#define ARCH_PERFMON_BRANCH_MISSES_RETIRED 6 + +/* + * Intel "Architectural Performance Monitoring" CPUID + * detection/enumeration details: + */ +union cpuid10_eax { + struct { + unsigned int version_id:8; + unsigned int num_events:8; + unsigned int bit_width:8; + unsigned int mask_length:8; + } split; + unsigned int full; +}; + +union cpuid10_edx { + struct { + unsigned int num_events_fixed:4; + unsigned int reserved:28; + } split; + unsigned int full; +}; + + +/* + * Fixed-purpose performance events: + */ + +/* + * All 3 fixed-mode PMCs are configured via this single MSR: + */ +#define MSR_ARCH_PERFMON_FIXED_CTR_CTRL 0x38d + +/* + * The counts are available in three separate MSRs: + */ + +/* Instr_Retired.Any: */ +#define MSR_ARCH_PERFMON_FIXED_CTR0 0x309 +#define X86_PMC_IDX_FIXED_INSTRUCTIONS (X86_PMC_IDX_FIXED + 0) + +/* CPU_CLK_Unhalted.Core: */ +#define MSR_ARCH_PERFMON_FIXED_CTR1 0x30a +#define X86_PMC_IDX_FIXED_CPU_CYCLES (X86_PMC_IDX_FIXED + 1) + +/* CPU_CLK_Unhalted.Ref: */ +#define MSR_ARCH_PERFMON_FIXED_CTR2 0x30b +#define X86_PMC_IDX_FIXED_BUS_CYCLES (X86_PMC_IDX_FIXED + 2) + +/* + * We model BTS tracing as another fixed-mode PMC. + * + * We choose a value in the middle of the fixed event range, since lower + * values are used by actual fixed events and higher values are used + * to indicate other overflow conditions in the PERF_GLOBAL_STATUS msr. + */ +#define X86_PMC_IDX_FIXED_BTS (X86_PMC_IDX_FIXED + 16) + + +#ifdef CONFIG_PERF_EVENTS +extern void init_hw_perf_events(void); +extern void perf_events_lapic_init(void); + +#define PERF_EVENT_INDEX_OFFSET 0 + +#else +static inline void init_hw_perf_events(void) { } +static inline void perf_events_lapic_init(void) { } +#endif + +#endif /* _ASM_X86_PERF_EVENT_H */ diff --git a/arch/x86/include/asm/unistd_32.h b/arch/x86/include/asm/unistd_32.h index 8deaada61bc8..6fb3c209a7e3 100644 --- a/arch/x86/include/asm/unistd_32.h +++ b/arch/x86/include/asm/unistd_32.h @@ -341,7 +341,7 @@ #define __NR_preadv 333 #define __NR_pwritev 334 #define __NR_rt_tgsigqueueinfo 335 -#define __NR_perf_counter_open 336 +#define __NR_perf_event_open 336 #ifdef __KERNEL__ diff --git a/arch/x86/include/asm/unistd_64.h b/arch/x86/include/asm/unistd_64.h index b9f3c60de5f7..8d3ad0adbc68 100644 --- a/arch/x86/include/asm/unistd_64.h +++ b/arch/x86/include/asm/unistd_64.h @@ -659,8 +659,8 @@ __SYSCALL(__NR_preadv, sys_preadv) __SYSCALL(__NR_pwritev, sys_pwritev) #define __NR_rt_tgsigqueueinfo 297 __SYSCALL(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo) -#define __NR_perf_counter_open 298 -__SYSCALL(__NR_perf_counter_open, sys_perf_counter_open) +#define __NR_perf_event_open 298 +__SYSCALL(__NR_perf_event_open, sys_perf_event_open) #ifndef __NO_STUBS #define __ARCH_WANT_OLD_READDIR diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index a34601f52987..754174d09deb 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -14,7 +14,7 @@ * Mikael Pettersson : PM converted to driver model. */ -#include +#include #include #include #include @@ -35,7 +35,7 @@ #include #include -#include +#include #include #include #include @@ -1189,7 +1189,7 @@ void __cpuinit setup_local_APIC(void) apic_write(APIC_ESR, 0); } #endif - perf_counters_lapic_init(); + perf_events_lapic_init(); preempt_disable(); diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile index 8dd30638fe44..68537e957a9b 100644 --- a/arch/x86/kernel/cpu/Makefile +++ b/arch/x86/kernel/cpu/Makefile @@ -27,7 +27,7 @@ obj-$(CONFIG_CPU_SUP_CENTAUR) += centaur.o obj-$(CONFIG_CPU_SUP_TRANSMETA_32) += transmeta.o obj-$(CONFIG_CPU_SUP_UMC_32) += umc.o -obj-$(CONFIG_PERF_COUNTERS) += perf_counter.o +obj-$(CONFIG_PERF_EVENTS) += perf_event.o obj-$(CONFIG_X86_MCE) += mcheck/ obj-$(CONFIG_MTRR) += mtrr/ diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 2fea97eccf77..cc25c2b4a567 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -13,7 +13,7 @@ #include #include -#include +#include #include #include #include @@ -869,7 +869,7 @@ void __init identify_boot_cpu(void) #else vgetcpu_set_mode(); #endif - init_hw_perf_counters(); + init_hw_perf_events(); } void __cpuinit identify_secondary_cpu(struct cpuinfo_x86 *c) diff --git a/arch/x86/kernel/cpu/perf_counter.c b/arch/x86/kernel/cpu/perf_counter.c deleted file mode 100644 index b1f115696c84..000000000000 --- a/arch/x86/kernel/cpu/perf_counter.c +++ /dev/null @@ -1,2298 +0,0 @@ -/* - * Performance counter x86 architecture code - * - * Copyright (C) 2008 Thomas Gleixner - * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar - * Copyright (C) 2009 Jaswinder Singh Rajput - * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter - * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra - * Copyright (C) 2009 Intel Corporation, - * - * For licencing details see kernel-base/COPYING - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static u64 perf_counter_mask __read_mostly; - -/* The maximal number of PEBS counters: */ -#define MAX_PEBS_COUNTERS 4 - -/* The size of a BTS record in bytes: */ -#define BTS_RECORD_SIZE 24 - -/* The size of a per-cpu BTS buffer in bytes: */ -#define BTS_BUFFER_SIZE (BTS_RECORD_SIZE * 2048) - -/* The BTS overflow threshold in bytes from the end of the buffer: */ -#define BTS_OVFL_TH (BTS_RECORD_SIZE * 128) - - -/* - * Bits in the debugctlmsr controlling branch tracing. - */ -#define X86_DEBUGCTL_TR (1 << 6) -#define X86_DEBUGCTL_BTS (1 << 7) -#define X86_DEBUGCTL_BTINT (1 << 8) -#define X86_DEBUGCTL_BTS_OFF_OS (1 << 9) -#define X86_DEBUGCTL_BTS_OFF_USR (1 << 10) - -/* - * A debug store configuration. - * - * We only support architectures that use 64bit fields. - */ -struct debug_store { - u64 bts_buffer_base; - u64 bts_index; - u64 bts_absolute_maximum; - u64 bts_interrupt_threshold; - u64 pebs_buffer_base; - u64 pebs_index; - u64 pebs_absolute_maximum; - u64 pebs_interrupt_threshold; - u64 pebs_counter_reset[MAX_PEBS_COUNTERS]; -}; - -struct cpu_hw_counters { - struct perf_counter *counters[X86_PMC_IDX_MAX]; - unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; - unsigned long active_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; - unsigned long interrupts; - int enabled; - struct debug_store *ds; -}; - -/* - * struct x86_pmu - generic x86 pmu - */ -struct x86_pmu { - const char *name; - int version; - int (*handle_irq)(struct pt_regs *); - void (*disable_all)(void); - void (*enable_all)(void); - void (*enable)(struct hw_perf_counter *, int); - void (*disable)(struct hw_perf_counter *, int); - unsigned eventsel; - unsigned perfctr; - u64 (*event_map)(int); - u64 (*raw_event)(u64); - int max_events; - int num_counters; - int num_counters_fixed; - int counter_bits; - u64 counter_mask; - int apic; - u64 max_period; - u64 intel_ctrl; - void (*enable_bts)(u64 config); - void (*disable_bts)(void); -}; - -static struct x86_pmu x86_pmu __read_mostly; - -static DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters) = { - .enabled = 1, -}; - -/* - * Not sure about some of these - */ -static const u64 p6_perfmon_event_map[] = -{ - [PERF_COUNT_HW_CPU_CYCLES] = 0x0079, - [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, - [PERF_COUNT_HW_CACHE_REFERENCES] = 0x0f2e, - [PERF_COUNT_HW_CACHE_MISSES] = 0x012e, - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4, - [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5, - [PERF_COUNT_HW_BUS_CYCLES] = 0x0062, -}; - -static u64 p6_pmu_event_map(int hw_event) -{ - return p6_perfmon_event_map[hw_event]; -} - -/* - * Counter setting that is specified not to count anything. - * We use this to effectively disable a counter. - * - * L2_RQSTS with 0 MESI unit mask. - */ -#define P6_NOP_COUNTER 0x0000002EULL - -static u64 p6_pmu_raw_event(u64 hw_event) -{ -#define P6_EVNTSEL_EVENT_MASK 0x000000FFULL -#define P6_EVNTSEL_UNIT_MASK 0x0000FF00ULL -#define P6_EVNTSEL_EDGE_MASK 0x00040000ULL -#define P6_EVNTSEL_INV_MASK 0x00800000ULL -#define P6_EVNTSEL_COUNTER_MASK 0xFF000000ULL - -#define P6_EVNTSEL_MASK \ - (P6_EVNTSEL_EVENT_MASK | \ - P6_EVNTSEL_UNIT_MASK | \ - P6_EVNTSEL_EDGE_MASK | \ - P6_EVNTSEL_INV_MASK | \ - P6_EVNTSEL_COUNTER_MASK) - - return hw_event & P6_EVNTSEL_MASK; -} - - -/* - * Intel PerfMon v3. Used on Core2 and later. - */ -static const u64 intel_perfmon_event_map[] = -{ - [PERF_COUNT_HW_CPU_CYCLES] = 0x003c, - [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, - [PERF_COUNT_HW_CACHE_REFERENCES] = 0x4f2e, - [PERF_COUNT_HW_CACHE_MISSES] = 0x412e, - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4, - [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5, - [PERF_COUNT_HW_BUS_CYCLES] = 0x013c, -}; - -static u64 intel_pmu_event_map(int hw_event) -{ - return intel_perfmon_event_map[hw_event]; -} - -/* - * Generalized hw caching related hw_event table, filled - * in on a per model basis. A value of 0 means - * 'not supported', -1 means 'hw_event makes no sense on - * this CPU', any other value means the raw hw_event - * ID. - */ - -#define C(x) PERF_COUNT_HW_CACHE_##x - -static u64 __read_mostly hw_cache_event_ids - [PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX]; - -static const u64 nehalem_hw_cache_event_ids - [PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX] = -{ - [ C(L1D) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI */ - [ C(RESULT_MISS) ] = 0x0140, /* L1D_CACHE_LD.I_STATE */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI */ - [ C(RESULT_MISS) ] = 0x0141, /* L1D_CACHE_ST.I_STATE */ - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0x014e, /* L1D_PREFETCH.REQUESTS */ - [ C(RESULT_MISS) ] = 0x024e, /* L1D_PREFETCH.MISS */ - }, - }, - [ C(L1I ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0380, /* L1I.READS */ - [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0x0, - [ C(RESULT_MISS) ] = 0x0, - }, - }, - [ C(LL ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0324, /* L2_RQSTS.LOADS */ - [ C(RESULT_MISS) ] = 0x0224, /* L2_RQSTS.LD_MISS */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x0c24, /* L2_RQSTS.RFOS */ - [ C(RESULT_MISS) ] = 0x0824, /* L2_RQSTS.RFO_MISS */ - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0x4f2e, /* LLC Reference */ - [ C(RESULT_MISS) ] = 0x412e, /* LLC Misses */ - }, - }, - [ C(DTLB) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI (alias) */ - [ C(RESULT_MISS) ] = 0x0108, /* DTLB_LOAD_MISSES.ANY */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI (alias) */ - [ C(RESULT_MISS) ] = 0x010c, /* MEM_STORE_RETIRED.DTLB_MISS */ - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0x0, - [ C(RESULT_MISS) ] = 0x0, - }, - }, - [ C(ITLB) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x01c0, /* INST_RETIRED.ANY_P */ - [ C(RESULT_MISS) ] = 0x20c8, /* ITLB_MISS_RETIRED */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - }, - [ C(BPU ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ALL_BRANCHES */ - [ C(RESULT_MISS) ] = 0x03e8, /* BPU_CLEARS.ANY */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - }, -}; - -static const u64 core2_hw_cache_event_ids - [PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX] = -{ - [ C(L1D) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI */ - [ C(RESULT_MISS) ] = 0x0140, /* L1D_CACHE_LD.I_STATE */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI */ - [ C(RESULT_MISS) ] = 0x0141, /* L1D_CACHE_ST.I_STATE */ - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0x104e, /* L1D_PREFETCH.REQUESTS */ - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(L1I ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0080, /* L1I.READS */ - [ C(RESULT_MISS) ] = 0x0081, /* L1I.MISSES */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0, - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(LL ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x4f29, /* L2_LD.MESI */ - [ C(RESULT_MISS) ] = 0x4129, /* L2_LD.ISTATE */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x4f2A, /* L2_ST.MESI */ - [ C(RESULT_MISS) ] = 0x412A, /* L2_ST.ISTATE */ - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0, - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(DTLB) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI (alias) */ - [ C(RESULT_MISS) ] = 0x0208, /* DTLB_MISSES.MISS_LD */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI (alias) */ - [ C(RESULT_MISS) ] = 0x0808, /* DTLB_MISSES.MISS_ST */ - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0, - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(ITLB) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x00c0, /* INST_RETIRED.ANY_P */ - [ C(RESULT_MISS) ] = 0x1282, /* ITLBMISSES */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - }, - [ C(BPU ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ANY */ - [ C(RESULT_MISS) ] = 0x00c5, /* BP_INST_RETIRED.MISPRED */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - }, -}; - -static const u64 atom_hw_cache_event_ids - [PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX] = -{ - [ C(L1D) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x2140, /* L1D_CACHE.LD */ - [ C(RESULT_MISS) ] = 0, - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x2240, /* L1D_CACHE.ST */ - [ C(RESULT_MISS) ] = 0, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0x0, - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(L1I ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0380, /* L1I.READS */ - [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0, - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(LL ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x4f29, /* L2_LD.MESI */ - [ C(RESULT_MISS) ] = 0x4129, /* L2_LD.ISTATE */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x4f2A, /* L2_ST.MESI */ - [ C(RESULT_MISS) ] = 0x412A, /* L2_ST.ISTATE */ - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0, - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(DTLB) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x2140, /* L1D_CACHE_LD.MESI (alias) */ - [ C(RESULT_MISS) ] = 0x0508, /* DTLB_MISSES.MISS_LD */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x2240, /* L1D_CACHE_ST.MESI (alias) */ - [ C(RESULT_MISS) ] = 0x0608, /* DTLB_MISSES.MISS_ST */ - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0, - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(ITLB) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x00c0, /* INST_RETIRED.ANY_P */ - [ C(RESULT_MISS) ] = 0x0282, /* ITLB.MISSES */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - }, - [ C(BPU ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ANY */ - [ C(RESULT_MISS) ] = 0x00c5, /* BP_INST_RETIRED.MISPRED */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - }, -}; - -static u64 intel_pmu_raw_event(u64 hw_event) -{ -#define CORE_EVNTSEL_EVENT_MASK 0x000000FFULL -#define CORE_EVNTSEL_UNIT_MASK 0x0000FF00ULL -#define CORE_EVNTSEL_EDGE_MASK 0x00040000ULL -#define CORE_EVNTSEL_INV_MASK 0x00800000ULL -#define CORE_EVNTSEL_COUNTER_MASK 0xFF000000ULL - -#define CORE_EVNTSEL_MASK \ - (CORE_EVNTSEL_EVENT_MASK | \ - CORE_EVNTSEL_UNIT_MASK | \ - CORE_EVNTSEL_EDGE_MASK | \ - CORE_EVNTSEL_INV_MASK | \ - CORE_EVNTSEL_COUNTER_MASK) - - return hw_event & CORE_EVNTSEL_MASK; -} - -static const u64 amd_hw_cache_event_ids - [PERF_COUNT_HW_CACHE_MAX] - [PERF_COUNT_HW_CACHE_OP_MAX] - [PERF_COUNT_HW_CACHE_RESULT_MAX] = -{ - [ C(L1D) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0040, /* Data Cache Accesses */ - [ C(RESULT_MISS) ] = 0x0041, /* Data Cache Misses */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x0142, /* Data Cache Refills :system */ - [ C(RESULT_MISS) ] = 0, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0x0267, /* Data Prefetcher :attempts */ - [ C(RESULT_MISS) ] = 0x0167, /* Data Prefetcher :cancelled */ - }, - }, - [ C(L1I ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0080, /* Instruction cache fetches */ - [ C(RESULT_MISS) ] = 0x0081, /* Instruction cache misses */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0x014B, /* Prefetch Instructions :Load */ - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(LL ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x037D, /* Requests to L2 Cache :IC+DC */ - [ C(RESULT_MISS) ] = 0x037E, /* L2 Cache Misses : IC+DC */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0x017F, /* L2 Fill/Writeback */ - [ C(RESULT_MISS) ] = 0, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0, - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(DTLB) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0040, /* Data Cache Accesses */ - [ C(RESULT_MISS) ] = 0x0046, /* L1 DTLB and L2 DLTB Miss */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = 0, - [ C(RESULT_MISS) ] = 0, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = 0, - [ C(RESULT_MISS) ] = 0, - }, - }, - [ C(ITLB) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x0080, /* Instruction fecthes */ - [ C(RESULT_MISS) ] = 0x0085, /* Instr. fetch ITLB misses */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - }, - [ C(BPU ) ] = { - [ C(OP_READ) ] = { - [ C(RESULT_ACCESS) ] = 0x00c2, /* Retired Branch Instr. */ - [ C(RESULT_MISS) ] = 0x00c3, /* Retired Mispredicted BI */ - }, - [ C(OP_WRITE) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - [ C(OP_PREFETCH) ] = { - [ C(RESULT_ACCESS) ] = -1, - [ C(RESULT_MISS) ] = -1, - }, - }, -}; - -/* - * AMD Performance Monitor K7 and later. - */ -static const u64 amd_perfmon_event_map[] = -{ - [PERF_COUNT_HW_CPU_CYCLES] = 0x0076, - [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, - [PERF_COUNT_HW_CACHE_REFERENCES] = 0x0080, - [PERF_COUNT_HW_CACHE_MISSES] = 0x0081, - [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4, - [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5, -}; - -static u64 amd_pmu_event_map(int hw_event) -{ - return amd_perfmon_event_map[hw_event]; -} - -static u64 amd_pmu_raw_event(u64 hw_event) -{ -#define K7_EVNTSEL_EVENT_MASK 0x7000000FFULL -#define K7_EVNTSEL_UNIT_MASK 0x00000FF00ULL -#define K7_EVNTSEL_EDGE_MASK 0x000040000ULL -#define K7_EVNTSEL_INV_MASK 0x000800000ULL -#define K7_EVNTSEL_COUNTER_MASK 0x0FF000000ULL - -#define K7_EVNTSEL_MASK \ - (K7_EVNTSEL_EVENT_MASK | \ - K7_EVNTSEL_UNIT_MASK | \ - K7_EVNTSEL_EDGE_MASK | \ - K7_EVNTSEL_INV_MASK | \ - K7_EVNTSEL_COUNTER_MASK) - - return hw_event & K7_EVNTSEL_MASK; -} - -/* - * Propagate counter elapsed time into the generic counter. - * Can only be executed on the CPU where the counter is active. - * Returns the delta events processed. - */ -static u64 -x86_perf_counter_update(struct perf_counter *counter, - struct hw_perf_counter *hwc, int idx) -{ - int shift = 64 - x86_pmu.counter_bits; - u64 prev_raw_count, new_raw_count; - s64 delta; - - if (idx == X86_PMC_IDX_FIXED_BTS) - return 0; - - /* - * Careful: an NMI might modify the previous counter value. - * - * Our tactic to handle this is to first atomically read and - * exchange a new raw count - then add that new-prev delta - * count to the generic counter atomically: - */ -again: - prev_raw_count = atomic64_read(&hwc->prev_count); - rdmsrl(hwc->counter_base + idx, new_raw_count); - - if (atomic64_cmpxchg(&hwc->prev_count, prev_raw_count, - new_raw_count) != prev_raw_count) - goto again; - - /* - * Now we have the new raw value and have updated the prev - * timestamp already. We can now calculate the elapsed delta - * (counter-)time and add that to the generic counter. - * - * Careful, not all hw sign-extends above the physical width - * of the count. - */ - delta = (new_raw_count << shift) - (prev_raw_count << shift); - delta >>= shift; - - atomic64_add(delta, &counter->count); - atomic64_sub(delta, &hwc->period_left); - - return new_raw_count; -} - -static atomic_t active_counters; -static DEFINE_MUTEX(pmc_reserve_mutex); - -static bool reserve_pmc_hardware(void) -{ -#ifdef CONFIG_X86_LOCAL_APIC - int i; - - if (nmi_watchdog == NMI_LOCAL_APIC) - disable_lapic_nmi_watchdog(); - - for (i = 0; i < x86_pmu.num_counters; i++) { - if (!reserve_perfctr_nmi(x86_pmu.perfctr + i)) - goto perfctr_fail; - } - - for (i = 0; i < x86_pmu.num_counters; i++) { - if (!reserve_evntsel_nmi(x86_pmu.eventsel + i)) - goto eventsel_fail; - } -#endif - - return true; - -#ifdef CONFIG_X86_LOCAL_APIC -eventsel_fail: - for (i--; i >= 0; i--) - release_evntsel_nmi(x86_pmu.eventsel + i); - - i = x86_pmu.num_counters; - -perfctr_fail: - for (i--; i >= 0; i--) - release_perfctr_nmi(x86_pmu.perfctr + i); - - if (nmi_watchdog == NMI_LOCAL_APIC) - enable_lapic_nmi_watchdog(); - - return false; -#endif -} - -static void release_pmc_hardware(void) -{ -#ifdef CONFIG_X86_LOCAL_APIC - int i; - - for (i = 0; i < x86_pmu.num_counters; i++) { - release_perfctr_nmi(x86_pmu.perfctr + i); - release_evntsel_nmi(x86_pmu.eventsel + i); - } - - if (nmi_watchdog == NMI_LOCAL_APIC) - enable_lapic_nmi_watchdog(); -#endif -} - -static inline bool bts_available(void) -{ - return x86_pmu.enable_bts != NULL; -} - -static inline void init_debug_store_on_cpu(int cpu) -{ - struct debug_store *ds = per_cpu(cpu_hw_counters, cpu).ds; - - if (!ds) - return; - - wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, - (u32)((u64)(unsigned long)ds), - (u32)((u64)(unsigned long)ds >> 32)); -} - -static inline void fini_debug_store_on_cpu(int cpu) -{ - if (!per_cpu(cpu_hw_counters, cpu).ds) - return; - - wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0); -} - -static void release_bts_hardware(void) -{ - int cpu; - - if (!bts_available()) - return; - - get_online_cpus(); - - for_each_online_cpu(cpu) - fini_debug_store_on_cpu(cpu); - - for_each_possible_cpu(cpu) { - struct debug_store *ds = per_cpu(cpu_hw_counters, cpu).ds; - - if (!ds) - continue; - - per_cpu(cpu_hw_counters, cpu).ds = NULL; - - kfree((void *)(unsigned long)ds->bts_buffer_base); - kfree(ds); - } - - put_online_cpus(); -} - -static int reserve_bts_hardware(void) -{ - int cpu, err = 0; - - if (!bts_available()) - return 0; - - get_online_cpus(); - - for_each_possible_cpu(cpu) { - struct debug_store *ds; - void *buffer; - - err = -ENOMEM; - buffer = kzalloc(BTS_BUFFER_SIZE, GFP_KERNEL); - if (unlikely(!buffer)) - break; - - ds = kzalloc(sizeof(*ds), GFP_KERNEL); - if (unlikely(!ds)) { - kfree(buffer); - break; - } - - ds->bts_buffer_base = (u64)(unsigned long)buffer; - ds->bts_index = ds->bts_buffer_base; - ds->bts_absolute_maximum = - ds->bts_buffer_base + BTS_BUFFER_SIZE; - ds->bts_interrupt_threshold = - ds->bts_absolute_maximum - BTS_OVFL_TH; - - per_cpu(cpu_hw_counters, cpu).ds = ds; - err = 0; - } - - if (err) - release_bts_hardware(); - else { - for_each_online_cpu(cpu) - init_debug_store_on_cpu(cpu); - } - - put_online_cpus(); - - return err; -} - -static void hw_perf_counter_destroy(struct perf_counter *counter) -{ - if (atomic_dec_and_mutex_lock(&active_counters, &pmc_reserve_mutex)) { - release_pmc_hardware(); - release_bts_hardware(); - mutex_unlock(&pmc_reserve_mutex); - } -} - -static inline int x86_pmu_initialized(void) -{ - return x86_pmu.handle_irq != NULL; -} - -static inline int -set_ext_hw_attr(struct hw_perf_counter *hwc, struct perf_counter_attr *attr) -{ - unsigned int cache_type, cache_op, cache_result; - u64 config, val; - - config = attr->config; - - cache_type = (config >> 0) & 0xff; - if (cache_type >= PERF_COUNT_HW_CACHE_MAX) - return -EINVAL; - - cache_op = (config >> 8) & 0xff; - if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX) - return -EINVAL; - - cache_result = (config >> 16) & 0xff; - if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) - return -EINVAL; - - val = hw_cache_event_ids[cache_type][cache_op][cache_result]; - - if (val == 0) - return -ENOENT; - - if (val == -1) - return -EINVAL; - - hwc->config |= val; - - return 0; -} - -static void intel_pmu_enable_bts(u64 config) -{ - unsigned long debugctlmsr; - - debugctlmsr = get_debugctlmsr(); - - debugctlmsr |= X86_DEBUGCTL_TR; - debugctlmsr |= X86_DEBUGCTL_BTS; - debugctlmsr |= X86_DEBUGCTL_BTINT; - - if (!(config & ARCH_PERFMON_EVENTSEL_OS)) - debugctlmsr |= X86_DEBUGCTL_BTS_OFF_OS; - - if (!(config & ARCH_PERFMON_EVENTSEL_USR)) - debugctlmsr |= X86_DEBUGCTL_BTS_OFF_USR; - - update_debugctlmsr(debugctlmsr); -} - -static void intel_pmu_disable_bts(void) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - unsigned long debugctlmsr; - - if (!cpuc->ds) - return; - - debugctlmsr = get_debugctlmsr(); - - debugctlmsr &= - ~(X86_DEBUGCTL_TR | X86_DEBUGCTL_BTS | X86_DEBUGCTL_BTINT | - X86_DEBUGCTL_BTS_OFF_OS | X86_DEBUGCTL_BTS_OFF_USR); - - update_debugctlmsr(debugctlmsr); -} - -/* - * Setup the hardware configuration for a given attr_type - */ -static int __hw_perf_counter_init(struct perf_counter *counter) -{ - struct perf_counter_attr *attr = &counter->attr; - struct hw_perf_counter *hwc = &counter->hw; - u64 config; - int err; - - if (!x86_pmu_initialized()) - return -ENODEV; - - err = 0; - if (!atomic_inc_not_zero(&active_counters)) { - mutex_lock(&pmc_reserve_mutex); - if (atomic_read(&active_counters) == 0) { - if (!reserve_pmc_hardware()) - err = -EBUSY; - else - err = reserve_bts_hardware(); - } - if (!err) - atomic_inc(&active_counters); - mutex_unlock(&pmc_reserve_mutex); - } - if (err) - return err; - - counter->destroy = hw_perf_counter_destroy; - - /* - * Generate PMC IRQs: - * (keep 'enabled' bit clear for now) - */ - hwc->config = ARCH_PERFMON_EVENTSEL_INT; - - /* - * Count user and OS events unless requested not to. - */ - if (!attr->exclude_user) - hwc->config |= ARCH_PERFMON_EVENTSEL_USR; - if (!attr->exclude_kernel) - hwc->config |= ARCH_PERFMON_EVENTSEL_OS; - - if (!hwc->sample_period) { - hwc->sample_period = x86_pmu.max_period; - hwc->last_period = hwc->sample_period; - atomic64_set(&hwc->period_left, hwc->sample_period); - } else { - /* - * If we have a PMU initialized but no APIC - * interrupts, we cannot sample hardware - * counters (user-space has to fall back and - * sample via a hrtimer based software counter): - */ - if (!x86_pmu.apic) - return -EOPNOTSUPP; - } - - /* - * Raw hw_event type provide the config in the hw_event structure - */ - if (attr->type == PERF_TYPE_RAW) { - hwc->config |= x86_pmu.raw_event(attr->config); - return 0; - } - - if (attr->type == PERF_TYPE_HW_CACHE) - return set_ext_hw_attr(hwc, attr); - - if (attr->config >= x86_pmu.max_events) - return -EINVAL; - - /* - * The generic map: - */ - config = x86_pmu.event_map(attr->config); - - if (config == 0) - return -ENOENT; - - if (config == -1LL) - return -EINVAL; - - /* - * Branch tracing: - */ - if ((attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS) && - (hwc->sample_period == 1)) { - /* BTS is not supported by this architecture. */ - if (!bts_available()) - return -EOPNOTSUPP; - - /* BTS is currently only allowed for user-mode. */ - if (hwc->config & ARCH_PERFMON_EVENTSEL_OS) - return -EOPNOTSUPP; - } - - hwc->config |= config; - - return 0; -} - -static void p6_pmu_disable_all(void) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - u64 val; - - if (!cpuc->enabled) - return; - - cpuc->enabled = 0; - barrier(); - - /* p6 only has one enable register */ - rdmsrl(MSR_P6_EVNTSEL0, val); - val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE; - wrmsrl(MSR_P6_EVNTSEL0, val); -} - -static void intel_pmu_disable_all(void) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - - if (!cpuc->enabled) - return; - - cpuc->enabled = 0; - barrier(); - - wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); - - if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) - intel_pmu_disable_bts(); -} - -static void amd_pmu_disable_all(void) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - int idx; - - if (!cpuc->enabled) - return; - - cpuc->enabled = 0; - /* - * ensure we write the disable before we start disabling the - * counters proper, so that amd_pmu_enable_counter() does the - * right thing. - */ - barrier(); - - for (idx = 0; idx < x86_pmu.num_counters; idx++) { - u64 val; - - if (!test_bit(idx, cpuc->active_mask)) - continue; - rdmsrl(MSR_K7_EVNTSEL0 + idx, val); - if (!(val & ARCH_PERFMON_EVENTSEL0_ENABLE)) - continue; - val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE; - wrmsrl(MSR_K7_EVNTSEL0 + idx, val); - } -} - -void hw_perf_disable(void) -{ - if (!x86_pmu_initialized()) - return; - return x86_pmu.disable_all(); -} - -static void p6_pmu_enable_all(void) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - unsigned long val; - - if (cpuc->enabled) - return; - - cpuc->enabled = 1; - barrier(); - - /* p6 only has one enable register */ - rdmsrl(MSR_P6_EVNTSEL0, val); - val |= ARCH_PERFMON_EVENTSEL0_ENABLE; - wrmsrl(MSR_P6_EVNTSEL0, val); -} - -static void intel_pmu_enable_all(void) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - - if (cpuc->enabled) - return; - - cpuc->enabled = 1; - barrier(); - - wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); - - if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { - struct perf_counter *counter = - cpuc->counters[X86_PMC_IDX_FIXED_BTS]; - - if (WARN_ON_ONCE(!counter)) - return; - - intel_pmu_enable_bts(counter->hw.config); - } -} - -static void amd_pmu_enable_all(void) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - int idx; - - if (cpuc->enabled) - return; - - cpuc->enabled = 1; - barrier(); - - for (idx = 0; idx < x86_pmu.num_counters; idx++) { - struct perf_counter *counter = cpuc->counters[idx]; - u64 val; - - if (!test_bit(idx, cpuc->active_mask)) - continue; - - val = counter->hw.config; - val |= ARCH_PERFMON_EVENTSEL0_ENABLE; - wrmsrl(MSR_K7_EVNTSEL0 + idx, val); - } -} - -void hw_perf_enable(void) -{ - if (!x86_pmu_initialized()) - return; - x86_pmu.enable_all(); -} - -static inline u64 intel_pmu_get_status(void) -{ - u64 status; - - rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); - - return status; -} - -static inline void intel_pmu_ack_status(u64 ack) -{ - wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); -} - -static inline void x86_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) -{ - (void)checking_wrmsrl(hwc->config_base + idx, - hwc->config | ARCH_PERFMON_EVENTSEL0_ENABLE); -} - -static inline void x86_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) -{ - (void)checking_wrmsrl(hwc->config_base + idx, hwc->config); -} - -static inline void -intel_pmu_disable_fixed(struct hw_perf_counter *hwc, int __idx) -{ - int idx = __idx - X86_PMC_IDX_FIXED; - u64 ctrl_val, mask; - - mask = 0xfULL << (idx * 4); - - rdmsrl(hwc->config_base, ctrl_val); - ctrl_val &= ~mask; - (void)checking_wrmsrl(hwc->config_base, ctrl_val); -} - -static inline void -p6_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - u64 val = P6_NOP_COUNTER; - - if (cpuc->enabled) - val |= ARCH_PERFMON_EVENTSEL0_ENABLE; - - (void)checking_wrmsrl(hwc->config_base + idx, val); -} - -static inline void -intel_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) -{ - if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { - intel_pmu_disable_bts(); - return; - } - - if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { - intel_pmu_disable_fixed(hwc, idx); - return; - } - - x86_pmu_disable_counter(hwc, idx); -} - -static inline void -amd_pmu_disable_counter(struct hw_perf_counter *hwc, int idx) -{ - x86_pmu_disable_counter(hwc, idx); -} - -static DEFINE_PER_CPU(u64 [X86_PMC_IDX_MAX], pmc_prev_left); - -/* - * Set the next IRQ period, based on the hwc->period_left value. - * To be called with the counter disabled in hw: - */ -static int -x86_perf_counter_set_period(struct perf_counter *counter, - struct hw_perf_counter *hwc, int idx) -{ - s64 left = atomic64_read(&hwc->period_left); - s64 period = hwc->sample_period; - int err, ret = 0; - - if (idx == X86_PMC_IDX_FIXED_BTS) - return 0; - - /* - * If we are way outside a reasoable range then just skip forward: - */ - if (unlikely(left <= -period)) { - left = period; - atomic64_set(&hwc->period_left, left); - hwc->last_period = period; - ret = 1; - } - - if (unlikely(left <= 0)) { - left += period; - atomic64_set(&hwc->period_left, left); - hwc->last_period = period; - ret = 1; - } - /* - * Quirk: certain CPUs dont like it if just 1 hw_event is left: - */ - if (unlikely(left < 2)) - left = 2; - - if (left > x86_pmu.max_period) - left = x86_pmu.max_period; - - per_cpu(pmc_prev_left[idx], smp_processor_id()) = left; - - /* - * The hw counter starts counting from this counter offset, - * mark it to be able to extra future deltas: - */ - atomic64_set(&hwc->prev_count, (u64)-left); - - err = checking_wrmsrl(hwc->counter_base + idx, - (u64)(-left) & x86_pmu.counter_mask); - - perf_counter_update_userpage(counter); - - return ret; -} - -static inline void -intel_pmu_enable_fixed(struct hw_perf_counter *hwc, int __idx) -{ - int idx = __idx - X86_PMC_IDX_FIXED; - u64 ctrl_val, bits, mask; - int err; - - /* - * Enable IRQ generation (0x8), - * and enable ring-3 counting (0x2) and ring-0 counting (0x1) - * if requested: - */ - bits = 0x8ULL; - if (hwc->config & ARCH_PERFMON_EVENTSEL_USR) - bits |= 0x2; - if (hwc->config & ARCH_PERFMON_EVENTSEL_OS) - bits |= 0x1; - bits <<= (idx * 4); - mask = 0xfULL << (idx * 4); - - rdmsrl(hwc->config_base, ctrl_val); - ctrl_val &= ~mask; - ctrl_val |= bits; - err = checking_wrmsrl(hwc->config_base, ctrl_val); -} - -static void p6_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - u64 val; - - val = hwc->config; - if (cpuc->enabled) - val |= ARCH_PERFMON_EVENTSEL0_ENABLE; - - (void)checking_wrmsrl(hwc->config_base + idx, val); -} - - -static void intel_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) -{ - if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { - if (!__get_cpu_var(cpu_hw_counters).enabled) - return; - - intel_pmu_enable_bts(hwc->config); - return; - } - - if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { - intel_pmu_enable_fixed(hwc, idx); - return; - } - - x86_pmu_enable_counter(hwc, idx); -} - -static void amd_pmu_enable_counter(struct hw_perf_counter *hwc, int idx) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - - if (cpuc->enabled) - x86_pmu_enable_counter(hwc, idx); -} - -static int -fixed_mode_idx(struct perf_counter *counter, struct hw_perf_counter *hwc) -{ - unsigned int hw_event; - - hw_event = hwc->config & ARCH_PERFMON_EVENT_MASK; - - if (unlikely((hw_event == - x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS)) && - (hwc->sample_period == 1))) - return X86_PMC_IDX_FIXED_BTS; - - if (!x86_pmu.num_counters_fixed) - return -1; - - if (unlikely(hw_event == x86_pmu.event_map(PERF_COUNT_HW_INSTRUCTIONS))) - return X86_PMC_IDX_FIXED_INSTRUCTIONS; - if (unlikely(hw_event == x86_pmu.event_map(PERF_COUNT_HW_CPU_CYCLES))) - return X86_PMC_IDX_FIXED_CPU_CYCLES; - if (unlikely(hw_event == x86_pmu.event_map(PERF_COUNT_HW_BUS_CYCLES))) - return X86_PMC_IDX_FIXED_BUS_CYCLES; - - return -1; -} - -/* - * Find a PMC slot for the freshly enabled / scheduled in counter: - */ -static int x86_pmu_enable(struct perf_counter *counter) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - struct hw_perf_counter *hwc = &counter->hw; - int idx; - - idx = fixed_mode_idx(counter, hwc); - if (idx == X86_PMC_IDX_FIXED_BTS) { - /* BTS is already occupied. */ - if (test_and_set_bit(idx, cpuc->used_mask)) - return -EAGAIN; - - hwc->config_base = 0; - hwc->counter_base = 0; - hwc->idx = idx; - } else if (idx >= 0) { - /* - * Try to get the fixed counter, if that is already taken - * then try to get a generic counter: - */ - if (test_and_set_bit(idx, cpuc->used_mask)) - goto try_generic; - - hwc->config_base = MSR_ARCH_PERFMON_FIXED_CTR_CTRL; - /* - * We set it so that counter_base + idx in wrmsr/rdmsr maps to - * MSR_ARCH_PERFMON_FIXED_CTR0 ... CTR2: - */ - hwc->counter_base = - MSR_ARCH_PERFMON_FIXED_CTR0 - X86_PMC_IDX_FIXED; - hwc->idx = idx; - } else { - idx = hwc->idx; - /* Try to get the previous generic counter again */ - if (test_and_set_bit(idx, cpuc->used_mask)) { -try_generic: - idx = find_first_zero_bit(cpuc->used_mask, - x86_pmu.num_counters); - if (idx == x86_pmu.num_counters) - return -EAGAIN; - - set_bit(idx, cpuc->used_mask); - hwc->idx = idx; - } - hwc->config_base = x86_pmu.eventsel; - hwc->counter_base = x86_pmu.perfctr; - } - - perf_counters_lapic_init(); - - x86_pmu.disable(hwc, idx); - - cpuc->counters[idx] = counter; - set_bit(idx, cpuc->active_mask); - - x86_perf_counter_set_period(counter, hwc, idx); - x86_pmu.enable(hwc, idx); - - perf_counter_update_userpage(counter); - - return 0; -} - -static void x86_pmu_unthrottle(struct perf_counter *counter) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - struct hw_perf_counter *hwc = &counter->hw; - - if (WARN_ON_ONCE(hwc->idx >= X86_PMC_IDX_MAX || - cpuc->counters[hwc->idx] != counter)) - return; - - x86_pmu.enable(hwc, hwc->idx); -} - -void perf_counter_print_debug(void) -{ - u64 ctrl, status, overflow, pmc_ctrl, pmc_count, prev_left, fixed; - struct cpu_hw_counters *cpuc; - unsigned long flags; - int cpu, idx; - - if (!x86_pmu.num_counters) - return; - - local_irq_save(flags); - - cpu = smp_processor_id(); - cpuc = &per_cpu(cpu_hw_counters, cpu); - - if (x86_pmu.version >= 2) { - rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); - rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); - rdmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow); - rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, fixed); - - pr_info("\n"); - pr_info("CPU#%d: ctrl: %016llx\n", cpu, ctrl); - pr_info("CPU#%d: status: %016llx\n", cpu, status); - pr_info("CPU#%d: overflow: %016llx\n", cpu, overflow); - pr_info("CPU#%d: fixed: %016llx\n", cpu, fixed); - } - pr_info("CPU#%d: used: %016llx\n", cpu, *(u64 *)cpuc->used_mask); - - for (idx = 0; idx < x86_pmu.num_counters; idx++) { - rdmsrl(x86_pmu.eventsel + idx, pmc_ctrl); - rdmsrl(x86_pmu.perfctr + idx, pmc_count); - - prev_left = per_cpu(pmc_prev_left[idx], cpu); - - pr_info("CPU#%d: gen-PMC%d ctrl: %016llx\n", - cpu, idx, pmc_ctrl); - pr_info("CPU#%d: gen-PMC%d count: %016llx\n", - cpu, idx, pmc_count); - pr_info("CPU#%d: gen-PMC%d left: %016llx\n", - cpu, idx, prev_left); - } - for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) { - rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, pmc_count); - - pr_info("CPU#%d: fixed-PMC%d count: %016llx\n", - cpu, idx, pmc_count); - } - local_irq_restore(flags); -} - -static void intel_pmu_drain_bts_buffer(struct cpu_hw_counters *cpuc) -{ - struct debug_store *ds = cpuc->ds; - struct bts_record { - u64 from; - u64 to; - u64 flags; - }; - struct perf_counter *counter = cpuc->counters[X86_PMC_IDX_FIXED_BTS]; - struct bts_record *at, *top; - struct perf_output_handle handle; - struct perf_event_header header; - struct perf_sample_data data; - struct pt_regs regs; - - if (!counter) - return; - - if (!ds) - return; - - at = (struct bts_record *)(unsigned long)ds->bts_buffer_base; - top = (struct bts_record *)(unsigned long)ds->bts_index; - - if (top <= at) - return; - - ds->bts_index = ds->bts_buffer_base; - - - data.period = counter->hw.last_period; - data.addr = 0; - regs.ip = 0; - - /* - * Prepare a generic sample, i.e. fill in the invariant fields. - * We will overwrite the from and to address before we output - * the sample. - */ - perf_prepare_sample(&header, &data, counter, ®s); - - if (perf_output_begin(&handle, counter, - header.size * (top - at), 1, 1)) - return; - - for (; at < top; at++) { - data.ip = at->from; - data.addr = at->to; - - perf_output_sample(&handle, &header, &data, counter); - } - - perf_output_end(&handle); - - /* There's new data available. */ - counter->hw.interrupts++; - counter->pending_kill = POLL_IN; -} - -static void x86_pmu_disable(struct perf_counter *counter) -{ - struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters); - struct hw_perf_counter *hwc = &counter->hw; - int idx = hwc->idx; - - /* - * Must be done before we disable, otherwise the nmi handler - * could reenable again: - */ - clear_bit(idx, cpuc->active_mask); - x86_pmu.disable(hwc, idx); - - /* - * Make sure the cleared pointer becomes visible before we - * (potentially) free the counter: - */ - barrier(); - - /* - * Drain the remaining delta count out of a counter - * that we are disabling: - */ - x86_perf_counter_update(counter, hwc, idx); - - /* Drain the remaining BTS records. */ - if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) - intel_pmu_drain_bts_buffer(cpuc); - - cpuc->counters[idx] = NULL; - clear_bit(idx, cpuc->used_mask); - - perf_counter_update_userpage(counter); -} - -/* - * Save and restart an expired counter. Called by NMI contexts, - * so it has to be careful about preempting normal counter ops: - */ -static int intel_pmu_save_and_restart(struct perf_counter *counter) -{ - struct hw_perf_counter *hwc = &counter->hw; - int idx = hwc->idx; - int ret; - - x86_perf_counter_update(counter, hwc, idx); - ret = x86_perf_counter_set_period(counter, hwc, idx); - - if (counter->state == PERF_COUNTER_STATE_ACTIVE) - intel_pmu_enable_counter(hwc, idx); - - return ret; -} - -static void intel_pmu_reset(void) -{ - struct debug_store *ds = __get_cpu_var(cpu_hw_counters).ds; - unsigned long flags; - int idx; - - if (!x86_pmu.num_counters) - return; - - local_irq_save(flags); - - printk("clearing PMU state on CPU#%d\n", smp_processor_id()); - - for (idx = 0; idx < x86_pmu.num_counters; idx++) { - checking_wrmsrl(x86_pmu.eventsel + idx, 0ull); - checking_wrmsrl(x86_pmu.perfctr + idx, 0ull); - } - for (idx = 0; idx < x86_pmu.num_counters_fixed; idx++) { - checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull); - } - if (ds) - ds->bts_index = ds->bts_buffer_base; - - local_irq_restore(flags); -} - -static int p6_pmu_handle_irq(struct pt_regs *regs) -{ - struct perf_sample_data data; - struct cpu_hw_counters *cpuc; - struct perf_counter *counter; - struct hw_perf_counter *hwc; - int idx, handled = 0; - u64 val; - - data.addr = 0; - - cpuc = &__get_cpu_var(cpu_hw_counters); - - for (idx = 0; idx < x86_pmu.num_counters; idx++) { - if (!test_bit(idx, cpuc->active_mask)) - continue; - - counter = cpuc->counters[idx]; - hwc = &counter->hw; - - val = x86_perf_counter_update(counter, hwc, idx); - if (val & (1ULL << (x86_pmu.counter_bits - 1))) - continue; - - /* - * counter overflow - */ - handled = 1; - data.period = counter->hw.last_period; - - if (!x86_perf_counter_set_period(counter, hwc, idx)) - continue; - - if (perf_counter_overflow(counter, 1, &data, regs)) - p6_pmu_disable_counter(hwc, idx); - } - - if (handled) - inc_irq_stat(apic_perf_irqs); - - return handled; -} - -/* - * This handler is triggered by the local APIC, so the APIC IRQ handling - * rules apply: - */ -static int intel_pmu_handle_irq(struct pt_regs *regs) -{ - struct perf_sample_data data; - struct cpu_hw_counters *cpuc; - int bit, loops; - u64 ack, status; - - data.addr = 0; - - cpuc = &__get_cpu_var(cpu_hw_counters); - - perf_disable(); - intel_pmu_drain_bts_buffer(cpuc); - status = intel_pmu_get_status(); - if (!status) { - perf_enable(); - return 0; - } - - loops = 0; -again: - if (++loops > 100) { - WARN_ONCE(1, "perfcounters: irq loop stuck!\n"); - perf_counter_print_debug(); - intel_pmu_reset(); - perf_enable(); - return 1; - } - - inc_irq_stat(apic_perf_irqs); - ack = status; - for_each_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) { - struct perf_counter *counter = cpuc->counters[bit]; - - clear_bit(bit, (unsigned long *) &status); - if (!test_bit(bit, cpuc->active_mask)) - continue; - - if (!intel_pmu_save_and_restart(counter)) - continue; - - data.period = counter->hw.last_period; - - if (perf_counter_overflow(counter, 1, &data, regs)) - intel_pmu_disable_counter(&counter->hw, bit); - } - - intel_pmu_ack_status(ack); - - /* - * Repeat if there is more work to be done: - */ - status = intel_pmu_get_status(); - if (status) - goto again; - - perf_enable(); - - return 1; -} - -static int amd_pmu_handle_irq(struct pt_regs *regs) -{ - struct perf_sample_data data; - struct cpu_hw_counters *cpuc; - struct perf_counter *counter; - struct hw_perf_counter *hwc; - int idx, handled = 0; - u64 val; - - data.addr = 0; - - cpuc = &__get_cpu_var(cpu_hw_counters); - - for (idx = 0; idx < x86_pmu.num_counters; idx++) { - if (!test_bit(idx, cpuc->active_mask)) - continue; - - counter = cpuc->counters[idx]; - hwc = &counter->hw; - - val = x86_perf_counter_update(counter, hwc, idx); - if (val & (1ULL << (x86_pmu.counter_bits - 1))) - continue; - - /* - * counter overflow - */ - handled = 1; - data.period = counter->hw.last_period; - - if (!x86_perf_counter_set_period(counter, hwc, idx)) - continue; - - if (perf_counter_overflow(counter, 1, &data, regs)) - amd_pmu_disable_counter(hwc, idx); - } - - if (handled) - inc_irq_stat(apic_perf_irqs); - - return handled; -} - -void smp_perf_pending_interrupt(struct pt_regs *regs) -{ - irq_enter(); - ack_APIC_irq(); - inc_irq_stat(apic_pending_irqs); - perf_counter_do_pending(); - irq_exit(); -} - -void set_perf_counter_pending(void) -{ -#ifdef CONFIG_X86_LOCAL_APIC - apic->send_IPI_self(LOCAL_PENDING_VECTOR); -#endif -} - -void perf_counters_lapic_init(void) -{ -#ifdef CONFIG_X86_LOCAL_APIC - if (!x86_pmu.apic || !x86_pmu_initialized()) - return; - - /* - * Always use NMI for PMU - */ - apic_write(APIC_LVTPC, APIC_DM_NMI); -#endif -} - -static int __kprobes -perf_counter_nmi_handler(struct notifier_block *self, - unsigned long cmd, void *__args) -{ - struct die_args *args = __args; - struct pt_regs *regs; - - if (!atomic_read(&active_counters)) - return NOTIFY_DONE; - - switch (cmd) { - case DIE_NMI: - case DIE_NMI_IPI: - break; - - default: - return NOTIFY_DONE; - } - - regs = args->regs; - -#ifdef CONFIG_X86_LOCAL_APIC - apic_write(APIC_LVTPC, APIC_DM_NMI); -#endif - /* - * Can't rely on the handled return value to say it was our NMI, two - * counters could trigger 'simultaneously' raising two back-to-back NMIs. - * - * If the first NMI handles both, the latter will be empty and daze - * the CPU. - */ - x86_pmu.handle_irq(regs); - - return NOTIFY_STOP; -} - -static __read_mostly struct notifier_block perf_counter_nmi_notifier = { - .notifier_call = perf_counter_nmi_handler, - .next = NULL, - .priority = 1 -}; - -static struct x86_pmu p6_pmu = { - .name = "p6", - .handle_irq = p6_pmu_handle_irq, - .disable_all = p6_pmu_disable_all, - .enable_all = p6_pmu_enable_all, - .enable = p6_pmu_enable_counter, - .disable = p6_pmu_disable_counter, - .eventsel = MSR_P6_EVNTSEL0, - .perfctr = MSR_P6_PERFCTR0, - .event_map = p6_pmu_event_map, - .raw_event = p6_pmu_raw_event, - .max_events = ARRAY_SIZE(p6_perfmon_event_map), - .apic = 1, - .max_period = (1ULL << 31) - 1, - .version = 0, - .num_counters = 2, - /* - * Counters have 40 bits implemented. However they are designed such - * that bits [32-39] are sign extensions of bit 31. As such the - * effective width of a counter for P6-like PMU is 32 bits only. - * - * See IA-32 Intel Architecture Software developer manual Vol 3B - */ - .counter_bits = 32, - .counter_mask = (1ULL << 32) - 1, -}; - -static struct x86_pmu intel_pmu = { - .name = "Intel", - .handle_irq = intel_pmu_handle_irq, - .disable_all = intel_pmu_disable_all, - .enable_all = intel_pmu_enable_all, - .enable = intel_pmu_enable_counter, - .disable = intel_pmu_disable_counter, - .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, - .perfctr = MSR_ARCH_PERFMON_PERFCTR0, - .event_map = intel_pmu_event_map, - .raw_event = intel_pmu_raw_event, - .max_events = ARRAY_SIZE(intel_perfmon_event_map), - .apic = 1, - /* - * Intel PMCs cannot be accessed sanely above 32 bit width, - * so we install an artificial 1<<31 period regardless of - * the generic counter period: - */ - .max_period = (1ULL << 31) - 1, - .enable_bts = intel_pmu_enable_bts, - .disable_bts = intel_pmu_disable_bts, -}; - -static struct x86_pmu amd_pmu = { - .name = "AMD", - .handle_irq = amd_pmu_handle_irq, - .disable_all = amd_pmu_disable_all, - .enable_all = amd_pmu_enable_all, - .enable = amd_pmu_enable_counter, - .disable = amd_pmu_disable_counter, - .eventsel = MSR_K7_EVNTSEL0, - .perfctr = MSR_K7_PERFCTR0, - .event_map = amd_pmu_event_map, - .raw_event = amd_pmu_raw_event, - .max_events = ARRAY_SIZE(amd_perfmon_event_map), - .num_counters = 4, - .counter_bits = 48, - .counter_mask = (1ULL << 48) - 1, - .apic = 1, - /* use highest bit to detect overflow */ - .max_period = (1ULL << 47) - 1, -}; - -static int p6_pmu_init(void) -{ - switch (boot_cpu_data.x86_model) { - case 1: - case 3: /* Pentium Pro */ - case 5: - case 6: /* Pentium II */ - case 7: - case 8: - case 11: /* Pentium III */ - break; - case 9: - case 13: - /* Pentium M */ - break; - default: - pr_cont("unsupported p6 CPU model %d ", - boot_cpu_data.x86_model); - return -ENODEV; - } - - x86_pmu = p6_pmu; - - if (!cpu_has_apic) { - pr_info("no APIC, boot with the \"lapic\" boot parameter to force-enable it.\n"); - pr_info("no hardware sampling interrupt available.\n"); - x86_pmu.apic = 0; - } - - return 0; -} - -static int intel_pmu_init(void) -{ - union cpuid10_edx edx; - union cpuid10_eax eax; - unsigned int unused; - unsigned int ebx; - int version; - - if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) { - /* check for P6 processor family */ - if (boot_cpu_data.x86 == 6) { - return p6_pmu_init(); - } else { - return -ENODEV; - } - } - - /* - * Check whether the Architectural PerfMon supports - * Branch Misses Retired hw_event or not. - */ - cpuid(10, &eax.full, &ebx, &unused, &edx.full); - if (eax.split.mask_length <= ARCH_PERFMON_BRANCH_MISSES_RETIRED) - return -ENODEV; - - version = eax.split.version_id; - if (version < 2) - return -ENODEV; - - x86_pmu = intel_pmu; - x86_pmu.version = version; - x86_pmu.num_counters = eax.split.num_counters; - x86_pmu.counter_bits = eax.split.bit_width; - x86_pmu.counter_mask = (1ULL << eax.split.bit_width) - 1; - - /* - * Quirk: v2 perfmon does not report fixed-purpose counters, so - * assume at least 3 counters: - */ - x86_pmu.num_counters_fixed = max((int)edx.split.num_counters_fixed, 3); - - /* - * Install the hw-cache-events table: - */ - switch (boot_cpu_data.x86_model) { - case 15: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */ - case 22: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */ - case 23: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */ - case 29: /* six-core 45 nm xeon "Dunnington" */ - memcpy(hw_cache_event_ids, core2_hw_cache_event_ids, - sizeof(hw_cache_event_ids)); - - pr_cont("Core2 events, "); - break; - default: - case 26: - memcpy(hw_cache_event_ids, nehalem_hw_cache_event_ids, - sizeof(hw_cache_event_ids)); - - pr_cont("Nehalem/Corei7 events, "); - break; - case 28: - memcpy(hw_cache_event_ids, atom_hw_cache_event_ids, - sizeof(hw_cache_event_ids)); - - pr_cont("Atom events, "); - break; - } - return 0; -} - -static int amd_pmu_init(void) -{ - /* Performance-monitoring supported from K7 and later: */ - if (boot_cpu_data.x86 < 6) - return -ENODEV; - - x86_pmu = amd_pmu; - - /* Events are common for all AMDs */ - memcpy(hw_cache_event_ids, amd_hw_cache_event_ids, - sizeof(hw_cache_event_ids)); - - return 0; -} - -void __init init_hw_perf_counters(void) -{ - int err; - - pr_info("Performance Counters: "); - - switch (boot_cpu_data.x86_vendor) { - case X86_VENDOR_INTEL: - err = intel_pmu_init(); - break; - case X86_VENDOR_AMD: - err = amd_pmu_init(); - break; - default: - return; - } - if (err != 0) { - pr_cont("no PMU driver, software counters only.\n"); - return; - } - - pr_cont("%s PMU driver.\n", x86_pmu.name); - - if (x86_pmu.num_counters > X86_PMC_MAX_GENERIC) { - WARN(1, KERN_ERR "hw perf counters %d > max(%d), clipping!", - x86_pmu.num_counters, X86_PMC_MAX_GENERIC); - x86_pmu.num_counters = X86_PMC_MAX_GENERIC; - } - perf_counter_mask = (1 << x86_pmu.num_counters) - 1; - perf_max_counters = x86_pmu.num_counters; - - if (x86_pmu.num_counters_fixed > X86_PMC_MAX_FIXED) { - WARN(1, KERN_ERR "hw perf counters fixed %d > max(%d), clipping!", - x86_pmu.num_counters_fixed, X86_PMC_MAX_FIXED); - x86_pmu.num_counters_fixed = X86_PMC_MAX_FIXED; - } - - perf_counter_mask |= - ((1LL << x86_pmu.num_counters_fixed)-1) << X86_PMC_IDX_FIXED; - x86_pmu.intel_ctrl = perf_counter_mask; - - perf_counters_lapic_init(); - register_die_notifier(&perf_counter_nmi_notifier); - - pr_info("... version: %d\n", x86_pmu.version); - pr_info("... bit width: %d\n", x86_pmu.counter_bits); - pr_info("... generic counters: %d\n", x86_pmu.num_counters); - pr_info("... value mask: %016Lx\n", x86_pmu.counter_mask); - pr_info("... max period: %016Lx\n", x86_pmu.max_period); - pr_info("... fixed-purpose counters: %d\n", x86_pmu.num_counters_fixed); - pr_info("... counter mask: %016Lx\n", perf_counter_mask); -} - -static inline void x86_pmu_read(struct perf_counter *counter) -{ - x86_perf_counter_update(counter, &counter->hw, counter->hw.idx); -} - -static const struct pmu pmu = { - .enable = x86_pmu_enable, - .disable = x86_pmu_disable, - .read = x86_pmu_read, - .unthrottle = x86_pmu_unthrottle, -}; - -const struct pmu *hw_perf_counter_init(struct perf_counter *counter) -{ - int err; - - err = __hw_perf_counter_init(counter); - if (err) { - if (counter->destroy) - counter->destroy(counter); - return ERR_PTR(err); - } - - return &pmu; -} - -/* - * callchain support - */ - -static inline -void callchain_store(struct perf_callchain_entry *entry, u64 ip) -{ - if (entry->nr < PERF_MAX_STACK_DEPTH) - entry->ip[entry->nr++] = ip; -} - -static DEFINE_PER_CPU(struct perf_callchain_entry, pmc_irq_entry); -static DEFINE_PER_CPU(struct perf_callchain_entry, pmc_nmi_entry); -static DEFINE_PER_CPU(int, in_nmi_frame); - - -static void -backtrace_warning_symbol(void *data, char *msg, unsigned long symbol) -{ - /* Ignore warnings */ -} - -static void backtrace_warning(void *data, char *msg) -{ - /* Ignore warnings */ -} - -static int backtrace_stack(void *data, char *name) -{ - per_cpu(in_nmi_frame, smp_processor_id()) = - x86_is_stack_id(NMI_STACK, name); - - return 0; -} - -static void backtrace_address(void *data, unsigned long addr, int reliable) -{ - struct perf_callchain_entry *entry = data; - - if (per_cpu(in_nmi_frame, smp_processor_id())) - return; - - if (reliable) - callchain_store(entry, addr); -} - -static const struct stacktrace_ops backtrace_ops = { - .warning = backtrace_warning, - .warning_symbol = backtrace_warning_symbol, - .stack = backtrace_stack, - .address = backtrace_address, -}; - -#include "../dumpstack.h" - -static void -perf_callchain_kernel(struct pt_regs *regs, struct perf_callchain_entry *entry) -{ - callchain_store(entry, PERF_CONTEXT_KERNEL); - callchain_store(entry, regs->ip); - - dump_trace(NULL, regs, NULL, 0, &backtrace_ops, entry); -} - -/* - * 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; - int type = in_nmi() ? KM_NMI : KM_IRQ0; - 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, type); - memcpy(to, map+offset, size); - kunmap_atomic(map, type); - put_page(page); - - len += size; - to += size; - addr += size; - - } while (len < n); - - return len; -} - -static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) -{ - unsigned long bytes; - - bytes = copy_from_user_nmi(frame, fp, sizeof(*frame)); - - return bytes == sizeof(*frame); -} - -static void -perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry) -{ - struct stack_frame frame; - const void __user *fp; - - if (!user_mode(regs)) - regs = task_pt_regs(current); - - fp = (void __user *)regs->bp; - - callchain_store(entry, PERF_CONTEXT_USER); - callchain_store(entry, regs->ip); - - while (entry->nr < PERF_MAX_STACK_DEPTH) { - frame.next_frame = NULL; - frame.return_address = 0; - - if (!copy_stack_frame(fp, &frame)) - break; - - if ((unsigned long)fp < regs->sp) - break; - - callchain_store(entry, frame.return_address); - fp = frame.next_frame; - } -} - -static void -perf_do_callchain(struct pt_regs *regs, struct perf_callchain_entry *entry) -{ - int is_user; - - if (!regs) - return; - - is_user = user_mode(regs); - - if (!current || current->pid == 0) - return; - - if (is_user && current->state != TASK_RUNNING) - return; - - if (!is_user) - perf_callchain_kernel(regs, entry); - - if (current->mm) - perf_callchain_user(regs, entry); -} - -struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) -{ - struct perf_callchain_entry *entry; - - if (in_nmi()) - entry = &__get_cpu_var(pmc_nmi_entry); - else - entry = &__get_cpu_var(pmc_irq_entry); - - entry->nr = 0; - - perf_do_callchain(regs, entry); - - return entry; -} - -void hw_perf_counter_setup_online(int cpu) -{ - init_debug_store_on_cpu(cpu); -} diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c new file mode 100644 index 000000000000..0d03629fb1a5 --- /dev/null +++ b/arch/x86/kernel/cpu/perf_event.c @@ -0,0 +1,2298 @@ +/* + * Performance events x86 architecture code + * + * Copyright (C) 2008 Thomas Gleixner + * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2009 Jaswinder Singh Rajput + * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter + * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra + * Copyright (C) 2009 Intel Corporation, + * + * For licencing details see kernel-base/COPYING + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static u64 perf_event_mask __read_mostly; + +/* The maximal number of PEBS events: */ +#define MAX_PEBS_EVENTS 4 + +/* The size of a BTS record in bytes: */ +#define BTS_RECORD_SIZE 24 + +/* The size of a per-cpu BTS buffer in bytes: */ +#define BTS_BUFFER_SIZE (BTS_RECORD_SIZE * 2048) + +/* The BTS overflow threshold in bytes from the end of the buffer: */ +#define BTS_OVFL_TH (BTS_RECORD_SIZE * 128) + + +/* + * Bits in the debugctlmsr controlling branch tracing. + */ +#define X86_DEBUGCTL_TR (1 << 6) +#define X86_DEBUGCTL_BTS (1 << 7) +#define X86_DEBUGCTL_BTINT (1 << 8) +#define X86_DEBUGCTL_BTS_OFF_OS (1 << 9) +#define X86_DEBUGCTL_BTS_OFF_USR (1 << 10) + +/* + * A debug store configuration. + * + * We only support architectures that use 64bit fields. + */ +struct debug_store { + u64 bts_buffer_base; + u64 bts_index; + u64 bts_absolute_maximum; + u64 bts_interrupt_threshold; + u64 pebs_buffer_base; + u64 pebs_index; + u64 pebs_absolute_maximum; + u64 pebs_interrupt_threshold; + u64 pebs_event_reset[MAX_PEBS_EVENTS]; +}; + +struct cpu_hw_events { + struct perf_event *events[X86_PMC_IDX_MAX]; + unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; + unsigned long active_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)]; + unsigned long interrupts; + int enabled; + struct debug_store *ds; +}; + +/* + * struct x86_pmu - generic x86 pmu + */ +struct x86_pmu { + const char *name; + int version; + int (*handle_irq)(struct pt_regs *); + void (*disable_all)(void); + void (*enable_all)(void); + void (*enable)(struct hw_perf_event *, int); + void (*disable)(struct hw_perf_event *, int); + unsigned eventsel; + unsigned perfctr; + u64 (*event_map)(int); + u64 (*raw_event)(u64); + int max_events; + int num_events; + int num_events_fixed; + int event_bits; + u64 event_mask; + int apic; + u64 max_period; + u64 intel_ctrl; + void (*enable_bts)(u64 config); + void (*disable_bts)(void); +}; + +static struct x86_pmu x86_pmu __read_mostly; + +static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { + .enabled = 1, +}; + +/* + * Not sure about some of these + */ +static const u64 p6_perfmon_event_map[] = +{ + [PERF_COUNT_HW_CPU_CYCLES] = 0x0079, + [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0x0f2e, + [PERF_COUNT_HW_CACHE_MISSES] = 0x012e, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4, + [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5, + [PERF_COUNT_HW_BUS_CYCLES] = 0x0062, +}; + +static u64 p6_pmu_event_map(int hw_event) +{ + return p6_perfmon_event_map[hw_event]; +} + +/* + * Event setting that is specified not to count anything. + * We use this to effectively disable a counter. + * + * L2_RQSTS with 0 MESI unit mask. + */ +#define P6_NOP_EVENT 0x0000002EULL + +static u64 p6_pmu_raw_event(u64 hw_event) +{ +#define P6_EVNTSEL_EVENT_MASK 0x000000FFULL +#define P6_EVNTSEL_UNIT_MASK 0x0000FF00ULL +#define P6_EVNTSEL_EDGE_MASK 0x00040000ULL +#define P6_EVNTSEL_INV_MASK 0x00800000ULL +#define P6_EVNTSEL_REG_MASK 0xFF000000ULL + +#define P6_EVNTSEL_MASK \ + (P6_EVNTSEL_EVENT_MASK | \ + P6_EVNTSEL_UNIT_MASK | \ + P6_EVNTSEL_EDGE_MASK | \ + P6_EVNTSEL_INV_MASK | \ + P6_EVNTSEL_REG_MASK) + + return hw_event & P6_EVNTSEL_MASK; +} + + +/* + * Intel PerfMon v3. Used on Core2 and later. + */ +static const u64 intel_perfmon_event_map[] = +{ + [PERF_COUNT_HW_CPU_CYCLES] = 0x003c, + [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0x4f2e, + [PERF_COUNT_HW_CACHE_MISSES] = 0x412e, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4, + [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5, + [PERF_COUNT_HW_BUS_CYCLES] = 0x013c, +}; + +static u64 intel_pmu_event_map(int hw_event) +{ + return intel_perfmon_event_map[hw_event]; +} + +/* + * Generalized hw caching related hw_event table, filled + * in on a per model basis. A value of 0 means + * 'not supported', -1 means 'hw_event makes no sense on + * this CPU', any other value means the raw hw_event + * ID. + */ + +#define C(x) PERF_COUNT_HW_CACHE_##x + +static u64 __read_mostly hw_cache_event_ids + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX]; + +static const u64 nehalem_hw_cache_event_ids + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = +{ + [ C(L1D) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI */ + [ C(RESULT_MISS) ] = 0x0140, /* L1D_CACHE_LD.I_STATE */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI */ + [ C(RESULT_MISS) ] = 0x0141, /* L1D_CACHE_ST.I_STATE */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x014e, /* L1D_PREFETCH.REQUESTS */ + [ C(RESULT_MISS) ] = 0x024e, /* L1D_PREFETCH.MISS */ + }, + }, + [ C(L1I ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0380, /* L1I.READS */ + [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0x0, + }, + }, + [ C(LL ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0324, /* L2_RQSTS.LOADS */ + [ C(RESULT_MISS) ] = 0x0224, /* L2_RQSTS.LD_MISS */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x0c24, /* L2_RQSTS.RFOS */ + [ C(RESULT_MISS) ] = 0x0824, /* L2_RQSTS.RFO_MISS */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x4f2e, /* LLC Reference */ + [ C(RESULT_MISS) ] = 0x412e, /* LLC Misses */ + }, + }, + [ C(DTLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI (alias) */ + [ C(RESULT_MISS) ] = 0x0108, /* DTLB_LOAD_MISSES.ANY */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI (alias) */ + [ C(RESULT_MISS) ] = 0x010c, /* MEM_STORE_RETIRED.DTLB_MISS */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0x0, + }, + }, + [ C(ITLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x01c0, /* INST_RETIRED.ANY_P */ + [ C(RESULT_MISS) ] = 0x20c8, /* ITLB_MISS_RETIRED */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, + [ C(BPU ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ALL_BRANCHES */ + [ C(RESULT_MISS) ] = 0x03e8, /* BPU_CLEARS.ANY */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, +}; + +static const u64 core2_hw_cache_event_ids + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = +{ + [ C(L1D) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI */ + [ C(RESULT_MISS) ] = 0x0140, /* L1D_CACHE_LD.I_STATE */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI */ + [ C(RESULT_MISS) ] = 0x0141, /* L1D_CACHE_ST.I_STATE */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x104e, /* L1D_PREFETCH.REQUESTS */ + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(L1I ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0080, /* L1I.READS */ + [ C(RESULT_MISS) ] = 0x0081, /* L1I.MISSES */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(LL ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x4f29, /* L2_LD.MESI */ + [ C(RESULT_MISS) ] = 0x4129, /* L2_LD.ISTATE */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x4f2A, /* L2_ST.MESI */ + [ C(RESULT_MISS) ] = 0x412A, /* L2_ST.ISTATE */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(DTLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0f40, /* L1D_CACHE_LD.MESI (alias) */ + [ C(RESULT_MISS) ] = 0x0208, /* DTLB_MISSES.MISS_LD */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x0f41, /* L1D_CACHE_ST.MESI (alias) */ + [ C(RESULT_MISS) ] = 0x0808, /* DTLB_MISSES.MISS_ST */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(ITLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x00c0, /* INST_RETIRED.ANY_P */ + [ C(RESULT_MISS) ] = 0x1282, /* ITLBMISSES */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, + [ C(BPU ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ANY */ + [ C(RESULT_MISS) ] = 0x00c5, /* BP_INST_RETIRED.MISPRED */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, +}; + +static const u64 atom_hw_cache_event_ids + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = +{ + [ C(L1D) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x2140, /* L1D_CACHE.LD */ + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x2240, /* L1D_CACHE.ST */ + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0, + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(L1I ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0380, /* L1I.READS */ + [ C(RESULT_MISS) ] = 0x0280, /* L1I.MISSES */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(LL ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x4f29, /* L2_LD.MESI */ + [ C(RESULT_MISS) ] = 0x4129, /* L2_LD.ISTATE */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x4f2A, /* L2_ST.MESI */ + [ C(RESULT_MISS) ] = 0x412A, /* L2_ST.ISTATE */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(DTLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x2140, /* L1D_CACHE_LD.MESI (alias) */ + [ C(RESULT_MISS) ] = 0x0508, /* DTLB_MISSES.MISS_LD */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x2240, /* L1D_CACHE_ST.MESI (alias) */ + [ C(RESULT_MISS) ] = 0x0608, /* DTLB_MISSES.MISS_ST */ + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(ITLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x00c0, /* INST_RETIRED.ANY_P */ + [ C(RESULT_MISS) ] = 0x0282, /* ITLB.MISSES */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, + [ C(BPU ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x00c4, /* BR_INST_RETIRED.ANY */ + [ C(RESULT_MISS) ] = 0x00c5, /* BP_INST_RETIRED.MISPRED */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, +}; + +static u64 intel_pmu_raw_event(u64 hw_event) +{ +#define CORE_EVNTSEL_EVENT_MASK 0x000000FFULL +#define CORE_EVNTSEL_UNIT_MASK 0x0000FF00ULL +#define CORE_EVNTSEL_EDGE_MASK 0x00040000ULL +#define CORE_EVNTSEL_INV_MASK 0x00800000ULL +#define CORE_EVNTSEL_REG_MASK 0xFF000000ULL + +#define CORE_EVNTSEL_MASK \ + (CORE_EVNTSEL_EVENT_MASK | \ + CORE_EVNTSEL_UNIT_MASK | \ + CORE_EVNTSEL_EDGE_MASK | \ + CORE_EVNTSEL_INV_MASK | \ + CORE_EVNTSEL_REG_MASK) + + return hw_event & CORE_EVNTSEL_MASK; +} + +static const u64 amd_hw_cache_event_ids + [PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = +{ + [ C(L1D) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0040, /* Data Cache Accesses */ + [ C(RESULT_MISS) ] = 0x0041, /* Data Cache Misses */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x0142, /* Data Cache Refills :system */ + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x0267, /* Data Prefetcher :attempts */ + [ C(RESULT_MISS) ] = 0x0167, /* Data Prefetcher :cancelled */ + }, + }, + [ C(L1I ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0080, /* Instruction cache fetches */ + [ C(RESULT_MISS) ] = 0x0081, /* Instruction cache misses */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0x014B, /* Prefetch Instructions :Load */ + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(LL ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x037D, /* Requests to L2 Cache :IC+DC */ + [ C(RESULT_MISS) ] = 0x037E, /* L2 Cache Misses : IC+DC */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0x017F, /* L2 Fill/Writeback */ + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(DTLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0040, /* Data Cache Accesses */ + [ C(RESULT_MISS) ] = 0x0046, /* L1 DTLB and L2 DLTB Miss */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = 0, + [ C(RESULT_MISS) ] = 0, + }, + }, + [ C(ITLB) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x0080, /* Instruction fecthes */ + [ C(RESULT_MISS) ] = 0x0085, /* Instr. fetch ITLB misses */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, + [ C(BPU ) ] = { + [ C(OP_READ) ] = { + [ C(RESULT_ACCESS) ] = 0x00c2, /* Retired Branch Instr. */ + [ C(RESULT_MISS) ] = 0x00c3, /* Retired Mispredicted BI */ + }, + [ C(OP_WRITE) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + [ C(OP_PREFETCH) ] = { + [ C(RESULT_ACCESS) ] = -1, + [ C(RESULT_MISS) ] = -1, + }, + }, +}; + +/* + * AMD Performance Monitor K7 and later. + */ +static const u64 amd_perfmon_event_map[] = +{ + [PERF_COUNT_HW_CPU_CYCLES] = 0x0076, + [PERF_COUNT_HW_INSTRUCTIONS] = 0x00c0, + [PERF_COUNT_HW_CACHE_REFERENCES] = 0x0080, + [PERF_COUNT_HW_CACHE_MISSES] = 0x0081, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x00c4, + [PERF_COUNT_HW_BRANCH_MISSES] = 0x00c5, +}; + +static u64 amd_pmu_event_map(int hw_event) +{ + return amd_perfmon_event_map[hw_event]; +} + +static u64 amd_pmu_raw_event(u64 hw_event) +{ +#define K7_EVNTSEL_EVENT_MASK 0x7000000FFULL +#define K7_EVNTSEL_UNIT_MASK 0x00000FF00ULL +#define K7_EVNTSEL_EDGE_MASK 0x000040000ULL +#define K7_EVNTSEL_INV_MASK 0x000800000ULL +#define K7_EVNTSEL_REG_MASK 0x0FF000000ULL + +#define K7_EVNTSEL_MASK \ + (K7_EVNTSEL_EVENT_MASK | \ + K7_EVNTSEL_UNIT_MASK | \ + K7_EVNTSEL_EDGE_MASK | \ + K7_EVNTSEL_INV_MASK | \ + K7_EVNTSEL_REG_MASK) + + return hw_event & K7_EVNTSEL_MASK; +} + +/* + * Propagate event elapsed time into the generic event. + * Can only be executed on the CPU where the event is active. + * Returns the delta events processed. + */ +static u64 +x86_perf_event_update(struct perf_event *event, + struct hw_perf_event *hwc, int idx) +{ + int shift = 64 - x86_pmu.event_bits; + u64 prev_raw_count, new_raw_count; + s64 delta; + + if (idx == X86_PMC_IDX_FIXED_BTS) + return 0; + + /* + * Careful: an NMI might modify the previous event value. + * + * Our tactic to handle this is to first atomically read and + * exchange a new raw count - then add that new-prev delta + * count to the generic event atomically: + */ +again: + prev_raw_count = atomic64_read(&hwc->prev_count); + rdmsrl(hwc->event_base + idx, new_raw_count); + + if (atomic64_cmpxchg(&hwc->prev_count, prev_raw_count, + new_raw_count) != prev_raw_count) + goto again; + + /* + * Now we have the new raw value and have updated the prev + * timestamp already. We can now calculate the elapsed delta + * (event-)time and add that to the generic event. + * + * Careful, not all hw sign-extends above the physical width + * of the count. + */ + delta = (new_raw_count << shift) - (prev_raw_count << shift); + delta >>= shift; + + atomic64_add(delta, &event->count); + atomic64_sub(delta, &hwc->period_left); + + return new_raw_count; +} + +static atomic_t active_events; +static DEFINE_MUTEX(pmc_reserve_mutex); + +static bool reserve_pmc_hardware(void) +{ +#ifdef CONFIG_X86_LOCAL_APIC + int i; + + if (nmi_watchdog == NMI_LOCAL_APIC) + disable_lapic_nmi_watchdog(); + + for (i = 0; i < x86_pmu.num_events; i++) { + if (!reserve_perfctr_nmi(x86_pmu.perfctr + i)) + goto perfctr_fail; + } + + for (i = 0; i < x86_pmu.num_events; i++) { + if (!reserve_evntsel_nmi(x86_pmu.eventsel + i)) + goto eventsel_fail; + } +#endif + + return true; + +#ifdef CONFIG_X86_LOCAL_APIC +eventsel_fail: + for (i--; i >= 0; i--) + release_evntsel_nmi(x86_pmu.eventsel + i); + + i = x86_pmu.num_events; + +perfctr_fail: + for (i--; i >= 0; i--) + release_perfctr_nmi(x86_pmu.perfctr + i); + + if (nmi_watchdog == NMI_LOCAL_APIC) + enable_lapic_nmi_watchdog(); + + return false; +#endif +} + +static void release_pmc_hardware(void) +{ +#ifdef CONFIG_X86_LOCAL_APIC + int i; + + for (i = 0; i < x86_pmu.num_events; i++) { + release_perfctr_nmi(x86_pmu.perfctr + i); + release_evntsel_nmi(x86_pmu.eventsel + i); + } + + if (nmi_watchdog == NMI_LOCAL_APIC) + enable_lapic_nmi_watchdog(); +#endif +} + +static inline bool bts_available(void) +{ + return x86_pmu.enable_bts != NULL; +} + +static inline void init_debug_store_on_cpu(int cpu) +{ + struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; + + if (!ds) + return; + + wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, + (u32)((u64)(unsigned long)ds), + (u32)((u64)(unsigned long)ds >> 32)); +} + +static inline void fini_debug_store_on_cpu(int cpu) +{ + if (!per_cpu(cpu_hw_events, cpu).ds) + return; + + wrmsr_on_cpu(cpu, MSR_IA32_DS_AREA, 0, 0); +} + +static void release_bts_hardware(void) +{ + int cpu; + + if (!bts_available()) + return; + + get_online_cpus(); + + for_each_online_cpu(cpu) + fini_debug_store_on_cpu(cpu); + + for_each_possible_cpu(cpu) { + struct debug_store *ds = per_cpu(cpu_hw_events, cpu).ds; + + if (!ds) + continue; + + per_cpu(cpu_hw_events, cpu).ds = NULL; + + kfree((void *)(unsigned long)ds->bts_buffer_base); + kfree(ds); + } + + put_online_cpus(); +} + +static int reserve_bts_hardware(void) +{ + int cpu, err = 0; + + if (!bts_available()) + return 0; + + get_online_cpus(); + + for_each_possible_cpu(cpu) { + struct debug_store *ds; + void *buffer; + + err = -ENOMEM; + buffer = kzalloc(BTS_BUFFER_SIZE, GFP_KERNEL); + if (unlikely(!buffer)) + break; + + ds = kzalloc(sizeof(*ds), GFP_KERNEL); + if (unlikely(!ds)) { + kfree(buffer); + break; + } + + ds->bts_buffer_base = (u64)(unsigned long)buffer; + ds->bts_index = ds->bts_buffer_base; + ds->bts_absolute_maximum = + ds->bts_buffer_base + BTS_BUFFER_SIZE; + ds->bts_interrupt_threshold = + ds->bts_absolute_maximum - BTS_OVFL_TH; + + per_cpu(cpu_hw_events, cpu).ds = ds; + err = 0; + } + + if (err) + release_bts_hardware(); + else { + for_each_online_cpu(cpu) + init_debug_store_on_cpu(cpu); + } + + put_online_cpus(); + + return err; +} + +static void hw_perf_event_destroy(struct perf_event *event) +{ + if (atomic_dec_and_mutex_lock(&active_events, &pmc_reserve_mutex)) { + release_pmc_hardware(); + release_bts_hardware(); + mutex_unlock(&pmc_reserve_mutex); + } +} + +static inline int x86_pmu_initialized(void) +{ + return x86_pmu.handle_irq != NULL; +} + +static inline int +set_ext_hw_attr(struct hw_perf_event *hwc, struct perf_event_attr *attr) +{ + unsigned int cache_type, cache_op, cache_result; + u64 config, val; + + config = attr->config; + + cache_type = (config >> 0) & 0xff; + if (cache_type >= PERF_COUNT_HW_CACHE_MAX) + return -EINVAL; + + cache_op = (config >> 8) & 0xff; + if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX) + return -EINVAL; + + cache_result = (config >> 16) & 0xff; + if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) + return -EINVAL; + + val = hw_cache_event_ids[cache_type][cache_op][cache_result]; + + if (val == 0) + return -ENOENT; + + if (val == -1) + return -EINVAL; + + hwc->config |= val; + + return 0; +} + +static void intel_pmu_enable_bts(u64 config) +{ + unsigned long debugctlmsr; + + debugctlmsr = get_debugctlmsr(); + + debugctlmsr |= X86_DEBUGCTL_TR; + debugctlmsr |= X86_DEBUGCTL_BTS; + debugctlmsr |= X86_DEBUGCTL_BTINT; + + if (!(config & ARCH_PERFMON_EVENTSEL_OS)) + debugctlmsr |= X86_DEBUGCTL_BTS_OFF_OS; + + if (!(config & ARCH_PERFMON_EVENTSEL_USR)) + debugctlmsr |= X86_DEBUGCTL_BTS_OFF_USR; + + update_debugctlmsr(debugctlmsr); +} + +static void intel_pmu_disable_bts(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + unsigned long debugctlmsr; + + if (!cpuc->ds) + return; + + debugctlmsr = get_debugctlmsr(); + + debugctlmsr &= + ~(X86_DEBUGCTL_TR | X86_DEBUGCTL_BTS | X86_DEBUGCTL_BTINT | + X86_DEBUGCTL_BTS_OFF_OS | X86_DEBUGCTL_BTS_OFF_USR); + + update_debugctlmsr(debugctlmsr); +} + +/* + * Setup the hardware configuration for a given attr_type + */ +static int __hw_perf_event_init(struct perf_event *event) +{ + struct perf_event_attr *attr = &event->attr; + struct hw_perf_event *hwc = &event->hw; + u64 config; + int err; + + if (!x86_pmu_initialized()) + return -ENODEV; + + err = 0; + if (!atomic_inc_not_zero(&active_events)) { + mutex_lock(&pmc_reserve_mutex); + if (atomic_read(&active_events) == 0) { + if (!reserve_pmc_hardware()) + err = -EBUSY; + else + err = reserve_bts_hardware(); + } + if (!err) + atomic_inc(&active_events); + mutex_unlock(&pmc_reserve_mutex); + } + if (err) + return err; + + event->destroy = hw_perf_event_destroy; + + /* + * Generate PMC IRQs: + * (keep 'enabled' bit clear for now) + */ + hwc->config = ARCH_PERFMON_EVENTSEL_INT; + + /* + * Count user and OS events unless requested not to. + */ + if (!attr->exclude_user) + hwc->config |= ARCH_PERFMON_EVENTSEL_USR; + if (!attr->exclude_kernel) + hwc->config |= ARCH_PERFMON_EVENTSEL_OS; + + if (!hwc->sample_period) { + hwc->sample_period = x86_pmu.max_period; + hwc->last_period = hwc->sample_period; + atomic64_set(&hwc->period_left, hwc->sample_period); + } else { + /* + * If we have a PMU initialized but no APIC + * interrupts, we cannot sample hardware + * events (user-space has to fall back and + * sample via a hrtimer based software event): + */ + if (!x86_pmu.apic) + return -EOPNOTSUPP; + } + + /* + * Raw hw_event type provide the config in the hw_event structure + */ + if (attr->type == PERF_TYPE_RAW) { + hwc->config |= x86_pmu.raw_event(attr->config); + return 0; + } + + if (attr->type == PERF_TYPE_HW_CACHE) + return set_ext_hw_attr(hwc, attr); + + if (attr->config >= x86_pmu.max_events) + return -EINVAL; + + /* + * The generic map: + */ + config = x86_pmu.event_map(attr->config); + + if (config == 0) + return -ENOENT; + + if (config == -1LL) + return -EINVAL; + + /* + * Branch tracing: + */ + if ((attr->config == PERF_COUNT_HW_BRANCH_INSTRUCTIONS) && + (hwc->sample_period == 1)) { + /* BTS is not supported by this architecture. */ + if (!bts_available()) + return -EOPNOTSUPP; + + /* BTS is currently only allowed for user-mode. */ + if (hwc->config & ARCH_PERFMON_EVENTSEL_OS) + return -EOPNOTSUPP; + } + + hwc->config |= config; + + return 0; +} + +static void p6_pmu_disable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + u64 val; + + if (!cpuc->enabled) + return; + + cpuc->enabled = 0; + barrier(); + + /* p6 only has one enable register */ + rdmsrl(MSR_P6_EVNTSEL0, val); + val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE; + wrmsrl(MSR_P6_EVNTSEL0, val); +} + +static void intel_pmu_disable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + if (!cpuc->enabled) + return; + + cpuc->enabled = 0; + barrier(); + + wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, 0); + + if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) + intel_pmu_disable_bts(); +} + +static void amd_pmu_disable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + int idx; + + if (!cpuc->enabled) + return; + + cpuc->enabled = 0; + /* + * ensure we write the disable before we start disabling the + * events proper, so that amd_pmu_enable_event() does the + * right thing. + */ + barrier(); + + for (idx = 0; idx < x86_pmu.num_events; idx++) { + u64 val; + + if (!test_bit(idx, cpuc->active_mask)) + continue; + rdmsrl(MSR_K7_EVNTSEL0 + idx, val); + if (!(val & ARCH_PERFMON_EVENTSEL0_ENABLE)) + continue; + val &= ~ARCH_PERFMON_EVENTSEL0_ENABLE; + wrmsrl(MSR_K7_EVNTSEL0 + idx, val); + } +} + +void hw_perf_disable(void) +{ + if (!x86_pmu_initialized()) + return; + return x86_pmu.disable_all(); +} + +static void p6_pmu_enable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + unsigned long val; + + if (cpuc->enabled) + return; + + cpuc->enabled = 1; + barrier(); + + /* p6 only has one enable register */ + rdmsrl(MSR_P6_EVNTSEL0, val); + val |= ARCH_PERFMON_EVENTSEL0_ENABLE; + wrmsrl(MSR_P6_EVNTSEL0, val); +} + +static void intel_pmu_enable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + if (cpuc->enabled) + return; + + cpuc->enabled = 1; + barrier(); + + wrmsrl(MSR_CORE_PERF_GLOBAL_CTRL, x86_pmu.intel_ctrl); + + if (test_bit(X86_PMC_IDX_FIXED_BTS, cpuc->active_mask)) { + struct perf_event *event = + cpuc->events[X86_PMC_IDX_FIXED_BTS]; + + if (WARN_ON_ONCE(!event)) + return; + + intel_pmu_enable_bts(event->hw.config); + } +} + +static void amd_pmu_enable_all(void) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + int idx; + + if (cpuc->enabled) + return; + + cpuc->enabled = 1; + barrier(); + + for (idx = 0; idx < x86_pmu.num_events; idx++) { + struct perf_event *event = cpuc->events[idx]; + u64 val; + + if (!test_bit(idx, cpuc->active_mask)) + continue; + + val = event->hw.config; + val |= ARCH_PERFMON_EVENTSEL0_ENABLE; + wrmsrl(MSR_K7_EVNTSEL0 + idx, val); + } +} + +void hw_perf_enable(void) +{ + if (!x86_pmu_initialized()) + return; + x86_pmu.enable_all(); +} + +static inline u64 intel_pmu_get_status(void) +{ + u64 status; + + rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); + + return status; +} + +static inline void intel_pmu_ack_status(u64 ack) +{ + wrmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, ack); +} + +static inline void x86_pmu_enable_event(struct hw_perf_event *hwc, int idx) +{ + (void)checking_wrmsrl(hwc->config_base + idx, + hwc->config | ARCH_PERFMON_EVENTSEL0_ENABLE); +} + +static inline void x86_pmu_disable_event(struct hw_perf_event *hwc, int idx) +{ + (void)checking_wrmsrl(hwc->config_base + idx, hwc->config); +} + +static inline void +intel_pmu_disable_fixed(struct hw_perf_event *hwc, int __idx) +{ + int idx = __idx - X86_PMC_IDX_FIXED; + u64 ctrl_val, mask; + + mask = 0xfULL << (idx * 4); + + rdmsrl(hwc->config_base, ctrl_val); + ctrl_val &= ~mask; + (void)checking_wrmsrl(hwc->config_base, ctrl_val); +} + +static inline void +p6_pmu_disable_event(struct hw_perf_event *hwc, int idx) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + u64 val = P6_NOP_EVENT; + + if (cpuc->enabled) + val |= ARCH_PERFMON_EVENTSEL0_ENABLE; + + (void)checking_wrmsrl(hwc->config_base + idx, val); +} + +static inline void +intel_pmu_disable_event(struct hw_perf_event *hwc, int idx) +{ + if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { + intel_pmu_disable_bts(); + return; + } + + if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { + intel_pmu_disable_fixed(hwc, idx); + return; + } + + x86_pmu_disable_event(hwc, idx); +} + +static inline void +amd_pmu_disable_event(struct hw_perf_event *hwc, int idx) +{ + x86_pmu_disable_event(hwc, idx); +} + +static DEFINE_PER_CPU(u64 [X86_PMC_IDX_MAX], pmc_prev_left); + +/* + * Set the next IRQ period, based on the hwc->period_left value. + * To be called with the event disabled in hw: + */ +static int +x86_perf_event_set_period(struct perf_event *event, + struct hw_perf_event *hwc, int idx) +{ + s64 left = atomic64_read(&hwc->period_left); + s64 period = hwc->sample_period; + int err, ret = 0; + + if (idx == X86_PMC_IDX_FIXED_BTS) + return 0; + + /* + * If we are way outside a reasoable range then just skip forward: + */ + if (unlikely(left <= -period)) { + left = period; + atomic64_set(&hwc->period_left, left); + hwc->last_period = period; + ret = 1; + } + + if (unlikely(left <= 0)) { + left += period; + atomic64_set(&hwc->period_left, left); + hwc->last_period = period; + ret = 1; + } + /* + * Quirk: certain CPUs dont like it if just 1 hw_event is left: + */ + if (unlikely(left < 2)) + left = 2; + + if (left > x86_pmu.max_period) + left = x86_pmu.max_period; + + per_cpu(pmc_prev_left[idx], smp_processor_id()) = left; + + /* + * The hw event starts counting from this event offset, + * mark it to be able to extra future deltas: + */ + atomic64_set(&hwc->prev_count, (u64)-left); + + err = checking_wrmsrl(hwc->event_base + idx, + (u64)(-left) & x86_pmu.event_mask); + + perf_event_update_userpage(event); + + return ret; +} + +static inline void +intel_pmu_enable_fixed(struct hw_perf_event *hwc, int __idx) +{ + int idx = __idx - X86_PMC_IDX_FIXED; + u64 ctrl_val, bits, mask; + int err; + + /* + * Enable IRQ generation (0x8), + * and enable ring-3 counting (0x2) and ring-0 counting (0x1) + * if requested: + */ + bits = 0x8ULL; + if (hwc->config & ARCH_PERFMON_EVENTSEL_USR) + bits |= 0x2; + if (hwc->config & ARCH_PERFMON_EVENTSEL_OS) + bits |= 0x1; + bits <<= (idx * 4); + mask = 0xfULL << (idx * 4); + + rdmsrl(hwc->config_base, ctrl_val); + ctrl_val &= ~mask; + ctrl_val |= bits; + err = checking_wrmsrl(hwc->config_base, ctrl_val); +} + +static void p6_pmu_enable_event(struct hw_perf_event *hwc, int idx) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + u64 val; + + val = hwc->config; + if (cpuc->enabled) + val |= ARCH_PERFMON_EVENTSEL0_ENABLE; + + (void)checking_wrmsrl(hwc->config_base + idx, val); +} + + +static void intel_pmu_enable_event(struct hw_perf_event *hwc, int idx) +{ + if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) { + if (!__get_cpu_var(cpu_hw_events).enabled) + return; + + intel_pmu_enable_bts(hwc->config); + return; + } + + if (unlikely(hwc->config_base == MSR_ARCH_PERFMON_FIXED_CTR_CTRL)) { + intel_pmu_enable_fixed(hwc, idx); + return; + } + + x86_pmu_enable_event(hwc, idx); +} + +static void amd_pmu_enable_event(struct hw_perf_event *hwc, int idx) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + + if (cpuc->enabled) + x86_pmu_enable_event(hwc, idx); +} + +static int +fixed_mode_idx(struct perf_event *event, struct hw_perf_event *hwc) +{ + unsigned int hw_event; + + hw_event = hwc->config & ARCH_PERFMON_EVENT_MASK; + + if (unlikely((hw_event == + x86_pmu.event_map(PERF_COUNT_HW_BRANCH_INSTRUCTIONS)) && + (hwc->sample_period == 1))) + return X86_PMC_IDX_FIXED_BTS; + + if (!x86_pmu.num_events_fixed) + return -1; + + if (unlikely(hw_event == x86_pmu.event_map(PERF_COUNT_HW_INSTRUCTIONS))) + return X86_PMC_IDX_FIXED_INSTRUCTIONS; + if (unlikely(hw_event == x86_pmu.event_map(PERF_COUNT_HW_CPU_CYCLES))) + return X86_PMC_IDX_FIXED_CPU_CYCLES; + if (unlikely(hw_event == x86_pmu.event_map(PERF_COUNT_HW_BUS_CYCLES))) + return X86_PMC_IDX_FIXED_BUS_CYCLES; + + return -1; +} + +/* + * Find a PMC slot for the freshly enabled / scheduled in event: + */ +static int x86_pmu_enable(struct perf_event *event) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct hw_perf_event *hwc = &event->hw; + int idx; + + idx = fixed_mode_idx(event, hwc); + if (idx == X86_PMC_IDX_FIXED_BTS) { + /* BTS is already occupied. */ + if (test_and_set_bit(idx, cpuc->used_mask)) + return -EAGAIN; + + hwc->config_base = 0; + hwc->event_base = 0; + hwc->idx = idx; + } else if (idx >= 0) { + /* + * Try to get the fixed event, if that is already taken + * then try to get a generic event: + */ + if (test_and_set_bit(idx, cpuc->used_mask)) + goto try_generic; + + hwc->config_base = MSR_ARCH_PERFMON_FIXED_CTR_CTRL; + /* + * We set it so that event_base + idx in wrmsr/rdmsr maps to + * MSR_ARCH_PERFMON_FIXED_CTR0 ... CTR2: + */ + hwc->event_base = + MSR_ARCH_PERFMON_FIXED_CTR0 - X86_PMC_IDX_FIXED; + hwc->idx = idx; + } else { + idx = hwc->idx; + /* Try to get the previous generic event again */ + if (test_and_set_bit(idx, cpuc->used_mask)) { +try_generic: + idx = find_first_zero_bit(cpuc->used_mask, + x86_pmu.num_events); + if (idx == x86_pmu.num_events) + return -EAGAIN; + + set_bit(idx, cpuc->used_mask); + hwc->idx = idx; + } + hwc->config_base = x86_pmu.eventsel; + hwc->event_base = x86_pmu.perfctr; + } + + perf_events_lapic_init(); + + x86_pmu.disable(hwc, idx); + + cpuc->events[idx] = event; + set_bit(idx, cpuc->active_mask); + + x86_perf_event_set_period(event, hwc, idx); + x86_pmu.enable(hwc, idx); + + perf_event_update_userpage(event); + + return 0; +} + +static void x86_pmu_unthrottle(struct perf_event *event) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct hw_perf_event *hwc = &event->hw; + + if (WARN_ON_ONCE(hwc->idx >= X86_PMC_IDX_MAX || + cpuc->events[hwc->idx] != event)) + return; + + x86_pmu.enable(hwc, hwc->idx); +} + +void perf_event_print_debug(void) +{ + u64 ctrl, status, overflow, pmc_ctrl, pmc_count, prev_left, fixed; + struct cpu_hw_events *cpuc; + unsigned long flags; + int cpu, idx; + + if (!x86_pmu.num_events) + return; + + local_irq_save(flags); + + cpu = smp_processor_id(); + cpuc = &per_cpu(cpu_hw_events, cpu); + + if (x86_pmu.version >= 2) { + rdmsrl(MSR_CORE_PERF_GLOBAL_CTRL, ctrl); + rdmsrl(MSR_CORE_PERF_GLOBAL_STATUS, status); + rdmsrl(MSR_CORE_PERF_GLOBAL_OVF_CTRL, overflow); + rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR_CTRL, fixed); + + pr_info("\n"); + pr_info("CPU#%d: ctrl: %016llx\n", cpu, ctrl); + pr_info("CPU#%d: status: %016llx\n", cpu, status); + pr_info("CPU#%d: overflow: %016llx\n", cpu, overflow); + pr_info("CPU#%d: fixed: %016llx\n", cpu, fixed); + } + pr_info("CPU#%d: used: %016llx\n", cpu, *(u64 *)cpuc->used_mask); + + for (idx = 0; idx < x86_pmu.num_events; idx++) { + rdmsrl(x86_pmu.eventsel + idx, pmc_ctrl); + rdmsrl(x86_pmu.perfctr + idx, pmc_count); + + prev_left = per_cpu(pmc_prev_left[idx], cpu); + + pr_info("CPU#%d: gen-PMC%d ctrl: %016llx\n", + cpu, idx, pmc_ctrl); + pr_info("CPU#%d: gen-PMC%d count: %016llx\n", + cpu, idx, pmc_count); + pr_info("CPU#%d: gen-PMC%d left: %016llx\n", + cpu, idx, prev_left); + } + for (idx = 0; idx < x86_pmu.num_events_fixed; idx++) { + rdmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, pmc_count); + + pr_info("CPU#%d: fixed-PMC%d count: %016llx\n", + cpu, idx, pmc_count); + } + local_irq_restore(flags); +} + +static void intel_pmu_drain_bts_buffer(struct cpu_hw_events *cpuc) +{ + struct debug_store *ds = cpuc->ds; + struct bts_record { + u64 from; + u64 to; + u64 flags; + }; + struct perf_event *event = cpuc->events[X86_PMC_IDX_FIXED_BTS]; + struct bts_record *at, *top; + struct perf_output_handle handle; + struct perf_event_header header; + struct perf_sample_data data; + struct pt_regs regs; + + if (!event) + return; + + if (!ds) + return; + + at = (struct bts_record *)(unsigned long)ds->bts_buffer_base; + top = (struct bts_record *)(unsigned long)ds->bts_index; + + if (top <= at) + return; + + ds->bts_index = ds->bts_buffer_base; + + + data.period = event->hw.last_period; + data.addr = 0; + regs.ip = 0; + + /* + * Prepare a generic sample, i.e. fill in the invariant fields. + * We will overwrite the from and to address before we output + * the sample. + */ + perf_prepare_sample(&header, &data, event, ®s); + + if (perf_output_begin(&handle, event, + header.size * (top - at), 1, 1)) + return; + + for (; at < top; at++) { + data.ip = at->from; + data.addr = at->to; + + perf_output_sample(&handle, &header, &data, event); + } + + perf_output_end(&handle); + + /* There's new data available. */ + event->hw.interrupts++; + event->pending_kill = POLL_IN; +} + +static void x86_pmu_disable(struct perf_event *event) +{ + struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events); + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + + /* + * Must be done before we disable, otherwise the nmi handler + * could reenable again: + */ + clear_bit(idx, cpuc->active_mask); + x86_pmu.disable(hwc, idx); + + /* + * Make sure the cleared pointer becomes visible before we + * (potentially) free the event: + */ + barrier(); + + /* + * Drain the remaining delta count out of a event + * that we are disabling: + */ + x86_perf_event_update(event, hwc, idx); + + /* Drain the remaining BTS records. */ + if (unlikely(idx == X86_PMC_IDX_FIXED_BTS)) + intel_pmu_drain_bts_buffer(cpuc); + + cpuc->events[idx] = NULL; + clear_bit(idx, cpuc->used_mask); + + perf_event_update_userpage(event); +} + +/* + * Save and restart an expired event. Called by NMI contexts, + * so it has to be careful about preempting normal event ops: + */ +static int intel_pmu_save_and_restart(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + int idx = hwc->idx; + int ret; + + x86_perf_event_update(event, hwc, idx); + ret = x86_perf_event_set_period(event, hwc, idx); + + if (event->state == PERF_EVENT_STATE_ACTIVE) + intel_pmu_enable_event(hwc, idx); + + return ret; +} + +static void intel_pmu_reset(void) +{ + struct debug_store *ds = __get_cpu_var(cpu_hw_events).ds; + unsigned long flags; + int idx; + + if (!x86_pmu.num_events) + return; + + local_irq_save(flags); + + printk("clearing PMU state on CPU#%d\n", smp_processor_id()); + + for (idx = 0; idx < x86_pmu.num_events; idx++) { + checking_wrmsrl(x86_pmu.eventsel + idx, 0ull); + checking_wrmsrl(x86_pmu.perfctr + idx, 0ull); + } + for (idx = 0; idx < x86_pmu.num_events_fixed; idx++) { + checking_wrmsrl(MSR_ARCH_PERFMON_FIXED_CTR0 + idx, 0ull); + } + if (ds) + ds->bts_index = ds->bts_buffer_base; + + local_irq_restore(flags); +} + +static int p6_pmu_handle_irq(struct pt_regs *regs) +{ + struct perf_sample_data data; + struct cpu_hw_events *cpuc; + struct perf_event *event; + struct hw_perf_event *hwc; + int idx, handled = 0; + u64 val; + + data.addr = 0; + + cpuc = &__get_cpu_var(cpu_hw_events); + + for (idx = 0; idx < x86_pmu.num_events; idx++) { + if (!test_bit(idx, cpuc->active_mask)) + continue; + + event = cpuc->events[idx]; + hwc = &event->hw; + + val = x86_perf_event_update(event, hwc, idx); + if (val & (1ULL << (x86_pmu.event_bits - 1))) + continue; + + /* + * event overflow + */ + handled = 1; + data.period = event->hw.last_period; + + if (!x86_perf_event_set_period(event, hwc, idx)) + continue; + + if (perf_event_overflow(event, 1, &data, regs)) + p6_pmu_disable_event(hwc, idx); + } + + if (handled) + inc_irq_stat(apic_perf_irqs); + + return handled; +} + +/* + * This handler is triggered by the local APIC, so the APIC IRQ handling + * rules apply: + */ +static int intel_pmu_handle_irq(struct pt_regs *regs) +{ + struct perf_sample_data data; + struct cpu_hw_events *cpuc; + int bit, loops; + u64 ack, status; + + data.addr = 0; + + cpuc = &__get_cpu_var(cpu_hw_events); + + perf_disable(); + intel_pmu_drain_bts_buffer(cpuc); + status = intel_pmu_get_status(); + if (!status) { + perf_enable(); + return 0; + } + + loops = 0; +again: + if (++loops > 100) { + WARN_ONCE(1, "perfevents: irq loop stuck!\n"); + perf_event_print_debug(); + intel_pmu_reset(); + perf_enable(); + return 1; + } + + inc_irq_stat(apic_perf_irqs); + ack = status; + for_each_bit(bit, (unsigned long *)&status, X86_PMC_IDX_MAX) { + struct perf_event *event = cpuc->events[bit]; + + clear_bit(bit, (unsigned long *) &status); + if (!test_bit(bit, cpuc->active_mask)) + continue; + + if (!intel_pmu_save_and_restart(event)) + continue; + + data.period = event->hw.last_period; + + if (perf_event_overflow(event, 1, &data, regs)) + intel_pmu_disable_event(&event->hw, bit); + } + + intel_pmu_ack_status(ack); + + /* + * Repeat if there is more work to be done: + */ + status = intel_pmu_get_status(); + if (status) + goto again; + + perf_enable(); + + return 1; +} + +static int amd_pmu_handle_irq(struct pt_regs *regs) +{ + struct perf_sample_data data; + struct cpu_hw_events *cpuc; + struct perf_event *event; + struct hw_perf_event *hwc; + int idx, handled = 0; + u64 val; + + data.addr = 0; + + cpuc = &__get_cpu_var(cpu_hw_events); + + for (idx = 0; idx < x86_pmu.num_events; idx++) { + if (!test_bit(idx, cpuc->active_mask)) + continue; + + event = cpuc->events[idx]; + hwc = &event->hw; + + val = x86_perf_event_update(event, hwc, idx); + if (val & (1ULL << (x86_pmu.event_bits - 1))) + continue; + + /* + * event overflow + */ + handled = 1; + data.period = event->hw.last_period; + + if (!x86_perf_event_set_period(event, hwc, idx)) + continue; + + if (perf_event_overflow(event, 1, &data, regs)) + amd_pmu_disable_event(hwc, idx); + } + + if (handled) + inc_irq_stat(apic_perf_irqs); + + return handled; +} + +void smp_perf_pending_interrupt(struct pt_regs *regs) +{ + irq_enter(); + ack_APIC_irq(); + inc_irq_stat(apic_pending_irqs); + perf_event_do_pending(); + irq_exit(); +} + +void set_perf_event_pending(void) +{ +#ifdef CONFIG_X86_LOCAL_APIC + apic->send_IPI_self(LOCAL_PENDING_VECTOR); +#endif +} + +void perf_events_lapic_init(void) +{ +#ifdef CONFIG_X86_LOCAL_APIC + if (!x86_pmu.apic || !x86_pmu_initialized()) + return; + + /* + * Always use NMI for PMU + */ + apic_write(APIC_LVTPC, APIC_DM_NMI); +#endif +} + +static int __kprobes +perf_event_nmi_handler(struct notifier_block *self, + unsigned long cmd, void *__args) +{ + struct die_args *args = __args; + struct pt_regs *regs; + + if (!atomic_read(&active_events)) + return NOTIFY_DONE; + + switch (cmd) { + case DIE_NMI: + case DIE_NMI_IPI: + break; + + default: + return NOTIFY_DONE; + } + + regs = args->regs; + +#ifdef CONFIG_X86_LOCAL_APIC + apic_write(APIC_LVTPC, APIC_DM_NMI); +#endif + /* + * Can't rely on the handled return value to say it was our NMI, two + * events could trigger 'simultaneously' raising two back-to-back NMIs. + * + * If the first NMI handles both, the latter will be empty and daze + * the CPU. + */ + x86_pmu.handle_irq(regs); + + return NOTIFY_STOP; +} + +static __read_mostly struct notifier_block perf_event_nmi_notifier = { + .notifier_call = perf_event_nmi_handler, + .next = NULL, + .priority = 1 +}; + +static struct x86_pmu p6_pmu = { + .name = "p6", + .handle_irq = p6_pmu_handle_irq, + .disable_all = p6_pmu_disable_all, + .enable_all = p6_pmu_enable_all, + .enable = p6_pmu_enable_event, + .disable = p6_pmu_disable_event, + .eventsel = MSR_P6_EVNTSEL0, + .perfctr = MSR_P6_PERFCTR0, + .event_map = p6_pmu_event_map, + .raw_event = p6_pmu_raw_event, + .max_events = ARRAY_SIZE(p6_perfmon_event_map), + .apic = 1, + .max_period = (1ULL << 31) - 1, + .version = 0, + .num_events = 2, + /* + * Events have 40 bits implemented. However they are designed such + * that bits [32-39] are sign extensions of bit 31. As such the + * effective width of a event for P6-like PMU is 32 bits only. + * + * See IA-32 Intel Architecture Software developer manual Vol 3B + */ + .event_bits = 32, + .event_mask = (1ULL << 32) - 1, +}; + +static struct x86_pmu intel_pmu = { + .name = "Intel", + .handle_irq = intel_pmu_handle_irq, + .disable_all = intel_pmu_disable_all, + .enable_all = intel_pmu_enable_all, + .enable = intel_pmu_enable_event, + .disable = intel_pmu_disable_event, + .eventsel = MSR_ARCH_PERFMON_EVENTSEL0, + .perfctr = MSR_ARCH_PERFMON_PERFCTR0, + .event_map = intel_pmu_event_map, + .raw_event = intel_pmu_raw_event, + .max_events = ARRAY_SIZE(intel_perfmon_event_map), + .apic = 1, + /* + * Intel PMCs cannot be accessed sanely above 32 bit width, + * so we install an artificial 1<<31 period regardless of + * the generic event period: + */ + .max_period = (1ULL << 31) - 1, + .enable_bts = intel_pmu_enable_bts, + .disable_bts = intel_pmu_disable_bts, +}; + +static struct x86_pmu amd_pmu = { + .name = "AMD", + .handle_irq = amd_pmu_handle_irq, + .disable_all = amd_pmu_disable_all, + .enable_all = amd_pmu_enable_all, + .enable = amd_pmu_enable_event, + .disable = amd_pmu_disable_event, + .eventsel = MSR_K7_EVNTSEL0, + .perfctr = MSR_K7_PERFCTR0, + .event_map = amd_pmu_event_map, + .raw_event = amd_pmu_raw_event, + .max_events = ARRAY_SIZE(amd_perfmon_event_map), + .num_events = 4, + .event_bits = 48, + .event_mask = (1ULL << 48) - 1, + .apic = 1, + /* use highest bit to detect overflow */ + .max_period = (1ULL << 47) - 1, +}; + +static int p6_pmu_init(void) +{ + switch (boot_cpu_data.x86_model) { + case 1: + case 3: /* Pentium Pro */ + case 5: + case 6: /* Pentium II */ + case 7: + case 8: + case 11: /* Pentium III */ + break; + case 9: + case 13: + /* Pentium M */ + break; + default: + pr_cont("unsupported p6 CPU model %d ", + boot_cpu_data.x86_model); + return -ENODEV; + } + + x86_pmu = p6_pmu; + + if (!cpu_has_apic) { + pr_info("no APIC, boot with the \"lapic\" boot parameter to force-enable it.\n"); + pr_info("no hardware sampling interrupt available.\n"); + x86_pmu.apic = 0; + } + + return 0; +} + +static int intel_pmu_init(void) +{ + union cpuid10_edx edx; + union cpuid10_eax eax; + unsigned int unused; + unsigned int ebx; + int version; + + if (!cpu_has(&boot_cpu_data, X86_FEATURE_ARCH_PERFMON)) { + /* check for P6 processor family */ + if (boot_cpu_data.x86 == 6) { + return p6_pmu_init(); + } else { + return -ENODEV; + } + } + + /* + * Check whether the Architectural PerfMon supports + * Branch Misses Retired hw_event or not. + */ + cpuid(10, &eax.full, &ebx, &unused, &edx.full); + if (eax.split.mask_length <= ARCH_PERFMON_BRANCH_MISSES_RETIRED) + return -ENODEV; + + version = eax.split.version_id; + if (version < 2) + return -ENODEV; + + x86_pmu = intel_pmu; + x86_pmu.version = version; + x86_pmu.num_events = eax.split.num_events; + x86_pmu.event_bits = eax.split.bit_width; + x86_pmu.event_mask = (1ULL << eax.split.bit_width) - 1; + + /* + * Quirk: v2 perfmon does not report fixed-purpose events, so + * assume at least 3 events: + */ + x86_pmu.num_events_fixed = max((int)edx.split.num_events_fixed, 3); + + /* + * Install the hw-cache-events table: + */ + switch (boot_cpu_data.x86_model) { + case 15: /* original 65 nm celeron/pentium/core2/xeon, "Merom"/"Conroe" */ + case 22: /* single-core 65 nm celeron/core2solo "Merom-L"/"Conroe-L" */ + case 23: /* current 45 nm celeron/core2/xeon "Penryn"/"Wolfdale" */ + case 29: /* six-core 45 nm xeon "Dunnington" */ + memcpy(hw_cache_event_ids, core2_hw_cache_event_ids, + sizeof(hw_cache_event_ids)); + + pr_cont("Core2 events, "); + break; + default: + case 26: + memcpy(hw_cache_event_ids, nehalem_hw_cache_event_ids, + sizeof(hw_cache_event_ids)); + + pr_cont("Nehalem/Corei7 events, "); + break; + case 28: + memcpy(hw_cache_event_ids, atom_hw_cache_event_ids, + sizeof(hw_cache_event_ids)); + + pr_cont("Atom events, "); + break; + } + return 0; +} + +static int amd_pmu_init(void) +{ + /* Performance-monitoring supported from K7 and later: */ + if (boot_cpu_data.x86 < 6) + return -ENODEV; + + x86_pmu = amd_pmu; + + /* Events are common for all AMDs */ + memcpy(hw_cache_event_ids, amd_hw_cache_event_ids, + sizeof(hw_cache_event_ids)); + + return 0; +} + +void __init init_hw_perf_events(void) +{ + int err; + + pr_info("Performance Events: "); + + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_INTEL: + err = intel_pmu_init(); + break; + case X86_VENDOR_AMD: + err = amd_pmu_init(); + break; + default: + return; + } + if (err != 0) { + pr_cont("no PMU driver, software events only.\n"); + return; + } + + pr_cont("%s PMU driver.\n", x86_pmu.name); + + if (x86_pmu.num_events > X86_PMC_MAX_GENERIC) { + WARN(1, KERN_ERR "hw perf events %d > max(%d), clipping!", + x86_pmu.num_events, X86_PMC_MAX_GENERIC); + x86_pmu.num_events = X86_PMC_MAX_GENERIC; + } + perf_event_mask = (1 << x86_pmu.num_events) - 1; + perf_max_events = x86_pmu.num_events; + + if (x86_pmu.num_events_fixed > X86_PMC_MAX_FIXED) { + WARN(1, KERN_ERR "hw perf events fixed %d > max(%d), clipping!", + x86_pmu.num_events_fixed, X86_PMC_MAX_FIXED); + x86_pmu.num_events_fixed = X86_PMC_MAX_FIXED; + } + + perf_event_mask |= + ((1LL << x86_pmu.num_events_fixed)-1) << X86_PMC_IDX_FIXED; + x86_pmu.intel_ctrl = perf_event_mask; + + perf_events_lapic_init(); + register_die_notifier(&perf_event_nmi_notifier); + + pr_info("... version: %d\n", x86_pmu.version); + pr_info("... bit width: %d\n", x86_pmu.event_bits); + pr_info("... generic events: %d\n", x86_pmu.num_events); + pr_info("... value mask: %016Lx\n", x86_pmu.event_mask); + pr_info("... max period: %016Lx\n", x86_pmu.max_period); + pr_info("... fixed-purpose events: %d\n", x86_pmu.num_events_fixed); + pr_info("... event mask: %016Lx\n", perf_event_mask); +} + +static inline void x86_pmu_read(struct perf_event *event) +{ + x86_perf_event_update(event, &event->hw, event->hw.idx); +} + +static const struct pmu pmu = { + .enable = x86_pmu_enable, + .disable = x86_pmu_disable, + .read = x86_pmu_read, + .unthrottle = x86_pmu_unthrottle, +}; + +const struct pmu *hw_perf_event_init(struct perf_event *event) +{ + int err; + + err = __hw_perf_event_init(event); + if (err) { + if (event->destroy) + event->destroy(event); + return ERR_PTR(err); + } + + return &pmu; +} + +/* + * callchain support + */ + +static inline +void callchain_store(struct perf_callchain_entry *entry, u64 ip) +{ + if (entry->nr < PERF_MAX_STACK_DEPTH) + entry->ip[entry->nr++] = ip; +} + +static DEFINE_PER_CPU(struct perf_callchain_entry, pmc_irq_entry); +static DEFINE_PER_CPU(struct perf_callchain_entry, pmc_nmi_entry); +static DEFINE_PER_CPU(int, in_nmi_frame); + + +static void +backtrace_warning_symbol(void *data, char *msg, unsigned long symbol) +{ + /* Ignore warnings */ +} + +static void backtrace_warning(void *data, char *msg) +{ + /* Ignore warnings */ +} + +static int backtrace_stack(void *data, char *name) +{ + per_cpu(in_nmi_frame, smp_processor_id()) = + x86_is_stack_id(NMI_STACK, name); + + return 0; +} + +static void backtrace_address(void *data, unsigned long addr, int reliable) +{ + struct perf_callchain_entry *entry = data; + + if (per_cpu(in_nmi_frame, smp_processor_id())) + return; + + if (reliable) + callchain_store(entry, addr); +} + +static const struct stacktrace_ops backtrace_ops = { + .warning = backtrace_warning, + .warning_symbol = backtrace_warning_symbol, + .stack = backtrace_stack, + .address = backtrace_address, +}; + +#include "../dumpstack.h" + +static void +perf_callchain_kernel(struct pt_regs *regs, struct perf_callchain_entry *entry) +{ + callchain_store(entry, PERF_CONTEXT_KERNEL); + callchain_store(entry, regs->ip); + + dump_trace(NULL, regs, NULL, 0, &backtrace_ops, entry); +} + +/* + * 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; + int type = in_nmi() ? KM_NMI : KM_IRQ0; + 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, type); + memcpy(to, map+offset, size); + kunmap_atomic(map, type); + put_page(page); + + len += size; + to += size; + addr += size; + + } while (len < n); + + return len; +} + +static int copy_stack_frame(const void __user *fp, struct stack_frame *frame) +{ + unsigned long bytes; + + bytes = copy_from_user_nmi(frame, fp, sizeof(*frame)); + + return bytes == sizeof(*frame); +} + +static void +perf_callchain_user(struct pt_regs *regs, struct perf_callchain_entry *entry) +{ + struct stack_frame frame; + const void __user *fp; + + if (!user_mode(regs)) + regs = task_pt_regs(current); + + fp = (void __user *)regs->bp; + + callchain_store(entry, PERF_CONTEXT_USER); + callchain_store(entry, regs->ip); + + while (entry->nr < PERF_MAX_STACK_DEPTH) { + frame.next_frame = NULL; + frame.return_address = 0; + + if (!copy_stack_frame(fp, &frame)) + break; + + if ((unsigned long)fp < regs->sp) + break; + + callchain_store(entry, frame.return_address); + fp = frame.next_frame; + } +} + +static void +perf_do_callchain(struct pt_regs *regs, struct perf_callchain_entry *entry) +{ + int is_user; + + if (!regs) + return; + + is_user = user_mode(regs); + + if (!current || current->pid == 0) + return; + + if (is_user && current->state != TASK_RUNNING) + return; + + if (!is_user) + perf_callchain_kernel(regs, entry); + + if (current->mm) + perf_callchain_user(regs, entry); +} + +struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) +{ + struct perf_callchain_entry *entry; + + if (in_nmi()) + entry = &__get_cpu_var(pmc_nmi_entry); + else + entry = &__get_cpu_var(pmc_irq_entry); + + entry->nr = 0; + + perf_do_callchain(regs, entry); + + return entry; +} + +void hw_perf_event_setup_online(int cpu) +{ + init_debug_store_on_cpu(cpu); +} diff --git a/arch/x86/kernel/cpu/perfctr-watchdog.c b/arch/x86/kernel/cpu/perfctr-watchdog.c index 392bea43b890..fab786f60ed6 100644 --- a/arch/x86/kernel/cpu/perfctr-watchdog.c +++ b/arch/x86/kernel/cpu/perfctr-watchdog.c @@ -20,7 +20,7 @@ #include #include -#include +#include struct nmi_watchdog_ctlblk { unsigned int cccr_msr; diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index d59fe323807e..681c3fda7391 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -1021,7 +1021,7 @@ apicinterrupt ERROR_APIC_VECTOR \ apicinterrupt SPURIOUS_APIC_VECTOR \ spurious_interrupt smp_spurious_interrupt -#ifdef CONFIG_PERF_COUNTERS +#ifdef CONFIG_PERF_EVENTS apicinterrupt LOCAL_PENDING_VECTOR \ perf_pending_interrupt smp_perf_pending_interrupt #endif diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c index 300883112e3d..40f30773fb29 100644 --- a/arch/x86/kernel/irqinit.c +++ b/arch/x86/kernel/irqinit.c @@ -208,7 +208,7 @@ static void __init apic_intr_init(void) alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt); /* Performance monitoring interrupts: */ -# ifdef CONFIG_PERF_COUNTERS +# ifdef CONFIG_PERF_EVENTS alloc_intr_gate(LOCAL_PENDING_VECTOR, perf_pending_interrupt); # endif diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S index d51321ddafda..0157cd26d7cc 100644 --- a/arch/x86/kernel/syscall_table_32.S +++ b/arch/x86/kernel/syscall_table_32.S @@ -335,4 +335,4 @@ ENTRY(sys_call_table) .long sys_preadv .long sys_pwritev .long sys_rt_tgsigqueueinfo /* 335 */ - .long sys_perf_counter_open + .long sys_perf_event_open diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 775a020990a5..82728f2c6d55 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c @@ -10,7 +10,7 @@ #include /* max_low_pfn */ #include /* __kprobes, ... */ #include /* kmmio_handler, ... */ -#include /* perf_swcounter_event */ +#include /* perf_sw_event */ #include /* dotraplinkage, ... */ #include /* pgd_*(), ... */ @@ -1017,7 +1017,7 @@ do_page_fault(struct pt_regs *regs, unsigned long error_code) if (unlikely(error_code & PF_RSVD)) pgtable_bad(regs, error_code, address); - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); /* * If we're in an interrupt, have no user context or are running @@ -1114,11 +1114,11 @@ good_area: if (fault & VM_FAULT_MAJOR) { tsk->maj_flt++; - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, regs, address); } else { tsk->min_flt++; - perf_swcounter_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, regs, address); } diff --git a/arch/x86/oprofile/op_model_ppro.c b/arch/x86/oprofile/op_model_ppro.c index 4899215999de..8eb05878554c 100644 --- a/arch/x86/oprofile/op_model_ppro.c +++ b/arch/x86/oprofile/op_model_ppro.c @@ -234,11 +234,11 @@ static void arch_perfmon_setup_counters(void) if (eax.split.version_id == 0 && current_cpu_data.x86 == 6 && current_cpu_data.x86_model == 15) { eax.split.version_id = 2; - eax.split.num_counters = 2; + eax.split.num_events = 2; eax.split.bit_width = 40; } - num_counters = eax.split.num_counters; + num_counters = eax.split.num_events; op_arch_perfmon_spec.num_counters = num_counters; op_arch_perfmon_spec.num_controls = num_counters; diff --git a/arch/x86/oprofile/op_x86_model.h b/arch/x86/oprofile/op_x86_model.h index b83776180c7f..7b8e75d16081 100644 --- a/arch/x86/oprofile/op_x86_model.h +++ b/arch/x86/oprofile/op_x86_model.h @@ -13,7 +13,7 @@ #define OP_X86_MODEL_H #include -#include +#include struct op_msr { unsigned long addr; diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index 50eecfe1d724..44203ff599da 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include #include @@ -252,7 +252,7 @@ static void sysrq_handle_showregs(int key, struct tty_struct *tty) struct pt_regs *regs = get_irq_regs(); if (regs) show_regs(regs); - perf_counter_print_debug(); + perf_event_print_debug(); } static struct sysrq_key_op sysrq_showregs_op = { .handler = sysrq_handle_showregs, diff --git a/fs/exec.c b/fs/exec.c index 172ceb6edde4..434dba778ccc 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #include #include #include @@ -923,7 +923,7 @@ void set_task_comm(struct task_struct *tsk, char *buf) task_lock(tsk); strlcpy(tsk->comm, buf, sizeof(tsk->comm)); task_unlock(tsk); - perf_counter_comm(tsk); + perf_event_comm(tsk); } int flush_old_exec(struct linux_binprm * bprm) @@ -997,7 +997,7 @@ int flush_old_exec(struct linux_binprm * bprm) * security domain: */ if (!get_dumpable(current->mm)) - perf_counter_exit_task(current); + perf_event_exit_task(current); /* An exec changes our domain. We are no longer part of the thread group */ diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h index 1125e5a1ee5d..d76b66acea95 100644 --- a/include/asm-generic/unistd.h +++ b/include/asm-generic/unistd.h @@ -620,8 +620,8 @@ __SYSCALL(__NR_move_pages, sys_move_pages) #define __NR_rt_tgsigqueueinfo 240 __SYSCALL(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo) -#define __NR_perf_counter_open 241 -__SYSCALL(__NR_perf_counter_open, sys_perf_counter_open) +#define __NR_perf_event_open 241 +__SYSCALL(__NR_perf_event_open, sys_perf_event_open) #undef __NR_syscalls #define __NR_syscalls 242 diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 9e7f2e8fc66e..21a6f5d9af22 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -106,13 +106,13 @@ extern struct group_info init_groups; extern struct cred init_cred; -#ifdef CONFIG_PERF_COUNTERS -# define INIT_PERF_COUNTERS(tsk) \ - .perf_counter_mutex = \ - __MUTEX_INITIALIZER(tsk.perf_counter_mutex), \ - .perf_counter_list = LIST_HEAD_INIT(tsk.perf_counter_list), +#ifdef CONFIG_PERF_EVENTS +# define INIT_PERF_EVENTS(tsk) \ + .perf_event_mutex = \ + __MUTEX_INITIALIZER(tsk.perf_event_mutex), \ + .perf_event_list = LIST_HEAD_INIT(tsk.perf_event_list), #else -# define INIT_PERF_COUNTERS(tsk) +# define INIT_PERF_EVENTS(tsk) #endif /* @@ -178,7 +178,7 @@ extern struct cred init_cred; }, \ .dirties = INIT_PROP_LOCAL_SINGLE(dirties), \ INIT_IDS \ - INIT_PERF_COUNTERS(tsk) \ + INIT_PERF_EVENTS(tsk) \ INIT_TRACE_IRQFLAGS \ INIT_LOCKDEP \ INIT_FTRACE_GRAPH \ diff --git a/include/linux/perf_counter.h b/include/linux/perf_counter.h deleted file mode 100644 index f64862732673..000000000000 --- a/include/linux/perf_counter.h +++ /dev/null @@ -1,858 +0,0 @@ -/* - * Performance counters: - * - * Copyright (C) 2008-2009, Thomas Gleixner - * Copyright (C) 2008-2009, Red Hat, Inc., Ingo Molnar - * Copyright (C) 2008-2009, Red Hat, Inc., Peter Zijlstra - * - * Data type definitions, declarations, prototypes. - * - * Started by: Thomas Gleixner and Ingo Molnar - * - * For licencing details see kernel-base/COPYING - */ -#ifndef _LINUX_PERF_COUNTER_H -#define _LINUX_PERF_COUNTER_H - -#include -#include -#include - -/* - * User-space ABI bits: - */ - -/* - * attr.type - */ -enum perf_type_id { - PERF_TYPE_HARDWARE = 0, - PERF_TYPE_SOFTWARE = 1, - PERF_TYPE_TRACEPOINT = 2, - PERF_TYPE_HW_CACHE = 3, - PERF_TYPE_RAW = 4, - - PERF_TYPE_MAX, /* non-ABI */ -}; - -/* - * Generalized performance counter event types, used by the - * attr.event_id parameter of the sys_perf_counter_open() - * syscall: - */ -enum perf_hw_id { - /* - * Common hardware events, generalized by the kernel: - */ - PERF_COUNT_HW_CPU_CYCLES = 0, - PERF_COUNT_HW_INSTRUCTIONS = 1, - PERF_COUNT_HW_CACHE_REFERENCES = 2, - PERF_COUNT_HW_CACHE_MISSES = 3, - PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, - PERF_COUNT_HW_BRANCH_MISSES = 5, - PERF_COUNT_HW_BUS_CYCLES = 6, - - PERF_COUNT_HW_MAX, /* non-ABI */ -}; - -/* - * Generalized hardware cache counters: - * - * { L1-D, L1-I, LLC, ITLB, DTLB, BPU } x - * { read, write, prefetch } x - * { accesses, misses } - */ -enum perf_hw_cache_id { - PERF_COUNT_HW_CACHE_L1D = 0, - PERF_COUNT_HW_CACHE_L1I = 1, - PERF_COUNT_HW_CACHE_LL = 2, - PERF_COUNT_HW_CACHE_DTLB = 3, - PERF_COUNT_HW_CACHE_ITLB = 4, - PERF_COUNT_HW_CACHE_BPU = 5, - - PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ -}; - -enum perf_hw_cache_op_id { - PERF_COUNT_HW_CACHE_OP_READ = 0, - PERF_COUNT_HW_CACHE_OP_WRITE = 1, - PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, - - PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ -}; - -enum perf_hw_cache_op_result_id { - PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, - PERF_COUNT_HW_CACHE_RESULT_MISS = 1, - - PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ -}; - -/* - * Special "software" counters provided by the kernel, even if the hardware - * does not support performance counters. These counters measure various - * physical and sw events of the kernel (and allow the profiling of them as - * well): - */ -enum perf_sw_ids { - PERF_COUNT_SW_CPU_CLOCK = 0, - PERF_COUNT_SW_TASK_CLOCK = 1, - PERF_COUNT_SW_PAGE_FAULTS = 2, - PERF_COUNT_SW_CONTEXT_SWITCHES = 3, - PERF_COUNT_SW_CPU_MIGRATIONS = 4, - PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, - PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, - - PERF_COUNT_SW_MAX, /* non-ABI */ -}; - -/* - * Bits that can be set in attr.sample_type to request information - * in the overflow packets. - */ -enum perf_counter_sample_format { - PERF_SAMPLE_IP = 1U << 0, - PERF_SAMPLE_TID = 1U << 1, - PERF_SAMPLE_TIME = 1U << 2, - PERF_SAMPLE_ADDR = 1U << 3, - PERF_SAMPLE_READ = 1U << 4, - PERF_SAMPLE_CALLCHAIN = 1U << 5, - PERF_SAMPLE_ID = 1U << 6, - PERF_SAMPLE_CPU = 1U << 7, - PERF_SAMPLE_PERIOD = 1U << 8, - PERF_SAMPLE_STREAM_ID = 1U << 9, - PERF_SAMPLE_RAW = 1U << 10, - - PERF_SAMPLE_MAX = 1U << 11, /* non-ABI */ -}; - -/* - * The format of the data returned by read() on a perf counter fd, - * as specified by attr.read_format: - * - * struct read_format { - * { u64 value; - * { u64 time_enabled; } && PERF_FORMAT_ENABLED - * { u64 time_running; } && PERF_FORMAT_RUNNING - * { u64 id; } && PERF_FORMAT_ID - * } && !PERF_FORMAT_GROUP - * - * { u64 nr; - * { u64 time_enabled; } && PERF_FORMAT_ENABLED - * { u64 time_running; } && PERF_FORMAT_RUNNING - * { u64 value; - * { u64 id; } && PERF_FORMAT_ID - * } cntr[nr]; - * } && PERF_FORMAT_GROUP - * }; - */ -enum perf_counter_read_format { - PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0, - PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1, - PERF_FORMAT_ID = 1U << 2, - PERF_FORMAT_GROUP = 1U << 3, - - PERF_FORMAT_MAX = 1U << 4, /* non-ABI */ -}; - -#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ - -/* - * Hardware event to monitor via a performance monitoring counter: - */ -struct perf_counter_attr { - - /* - * Major type: hardware/software/tracepoint/etc. - */ - __u32 type; - - /* - * Size of the attr structure, for fwd/bwd compat. - */ - __u32 size; - - /* - * Type specific configuration information. - */ - __u64 config; - - union { - __u64 sample_period; - __u64 sample_freq; - }; - - __u64 sample_type; - __u64 read_format; - - __u64 disabled : 1, /* off by default */ - inherit : 1, /* children inherit it */ - pinned : 1, /* must always be on PMU */ - exclusive : 1, /* only group on PMU */ - exclude_user : 1, /* don't count user */ - exclude_kernel : 1, /* ditto kernel */ - exclude_hv : 1, /* ditto hypervisor */ - exclude_idle : 1, /* don't count when idle */ - mmap : 1, /* include mmap data */ - comm : 1, /* include comm data */ - freq : 1, /* use freq, not period */ - inherit_stat : 1, /* per task counts */ - enable_on_exec : 1, /* next exec enables */ - task : 1, /* trace fork/exit */ - watermark : 1, /* wakeup_watermark */ - - __reserved_1 : 49; - - union { - __u32 wakeup_events; /* wakeup every n events */ - __u32 wakeup_watermark; /* bytes before wakeup */ - }; - __u32 __reserved_2; - - __u64 __reserved_3; -}; - -/* - * Ioctls that can be done on a perf counter fd: - */ -#define PERF_COUNTER_IOC_ENABLE _IO ('$', 0) -#define PERF_COUNTER_IOC_DISABLE _IO ('$', 1) -#define PERF_COUNTER_IOC_REFRESH _IO ('$', 2) -#define PERF_COUNTER_IOC_RESET _IO ('$', 3) -#define PERF_COUNTER_IOC_PERIOD _IOW('$', 4, u64) -#define PERF_COUNTER_IOC_SET_OUTPUT _IO ('$', 5) - -enum perf_counter_ioc_flags { - PERF_IOC_FLAG_GROUP = 1U << 0, -}; - -/* - * Structure of the page that can be mapped via mmap - */ -struct perf_counter_mmap_page { - __u32 version; /* version number of this structure */ - __u32 compat_version; /* lowest version this is compat with */ - - /* - * Bits needed to read the hw counters in user-space. - * - * u32 seq; - * s64 count; - * - * do { - * seq = pc->lock; - * - * barrier() - * if (pc->index) { - * count = pmc_read(pc->index - 1); - * count += pc->offset; - * } else - * goto regular_read; - * - * barrier(); - * } while (pc->lock != seq); - * - * NOTE: for obvious reason this only works on self-monitoring - * processes. - */ - __u32 lock; /* seqlock for synchronization */ - __u32 index; /* hardware counter identifier */ - __s64 offset; /* add to hardware counter value */ - __u64 time_enabled; /* time counter active */ - __u64 time_running; /* time counter on cpu */ - - /* - * Hole for extension of the self monitor capabilities - */ - - __u64 __reserved[123]; /* align to 1k */ - - /* - * Control data for the mmap() data buffer. - * - * User-space reading the @data_head value should issue an rmb(), on - * SMP capable platforms, after reading this value -- see - * perf_counter_wakeup(). - * - * When the mapping is PROT_WRITE the @data_tail value should be - * written by userspace to reflect the last read data. In this case - * the kernel will not over-write unread data. - */ - __u64 data_head; /* head in the data section */ - __u64 data_tail; /* user-space written tail */ -}; - -#define PERF_EVENT_MISC_CPUMODE_MASK (3 << 0) -#define PERF_EVENT_MISC_CPUMODE_UNKNOWN (0 << 0) -#define PERF_EVENT_MISC_KERNEL (1 << 0) -#define PERF_EVENT_MISC_USER (2 << 0) -#define PERF_EVENT_MISC_HYPERVISOR (3 << 0) - -struct perf_event_header { - __u32 type; - __u16 misc; - __u16 size; -}; - -enum perf_event_type { - - /* - * The MMAP events record the PROT_EXEC mappings so that we can - * correlate userspace IPs to code. They have the following structure: - * - * struct { - * struct perf_event_header header; - * - * u32 pid, tid; - * u64 addr; - * u64 len; - * u64 pgoff; - * char filename[]; - * }; - */ - PERF_EVENT_MMAP = 1, - - /* - * struct { - * struct perf_event_header header; - * u64 id; - * u64 lost; - * }; - */ - PERF_EVENT_LOST = 2, - - /* - * struct { - * struct perf_event_header header; - * - * u32 pid, tid; - * char comm[]; - * }; - */ - PERF_EVENT_COMM = 3, - - /* - * struct { - * struct perf_event_header header; - * u32 pid, ppid; - * u32 tid, ptid; - * u64 time; - * }; - */ - PERF_EVENT_EXIT = 4, - - /* - * struct { - * struct perf_event_header header; - * u64 time; - * u64 id; - * u64 stream_id; - * }; - */ - PERF_EVENT_THROTTLE = 5, - PERF_EVENT_UNTHROTTLE = 6, - - /* - * struct { - * struct perf_event_header header; - * u32 pid, ppid; - * u32 tid, ptid; - * { u64 time; } && PERF_SAMPLE_TIME - * }; - */ - PERF_EVENT_FORK = 7, - - /* - * struct { - * struct perf_event_header header; - * u32 pid, tid; - * - * struct read_format values; - * }; - */ - PERF_EVENT_READ = 8, - - /* - * struct { - * struct perf_event_header header; - * - * { u64 ip; } && PERF_SAMPLE_IP - * { u32 pid, tid; } && PERF_SAMPLE_TID - * { u64 time; } && PERF_SAMPLE_TIME - * { u64 addr; } && PERF_SAMPLE_ADDR - * { u64 id; } && PERF_SAMPLE_ID - * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID - * { u32 cpu, res; } && PERF_SAMPLE_CPU - * { u64 period; } && PERF_SAMPLE_PERIOD - * - * { struct read_format values; } && PERF_SAMPLE_READ - * - * { u64 nr, - * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN - * - * # - * # The RAW record below is opaque data wrt the ABI - * # - * # That is, the ABI doesn't make any promises wrt to - * # the stability of its content, it may vary depending - * # on event, hardware, kernel version and phase of - * # the moon. - * # - * # In other words, PERF_SAMPLE_RAW contents are not an ABI. - * # - * - * { u32 size; - * char data[size];}&& PERF_SAMPLE_RAW - * }; - */ - PERF_EVENT_SAMPLE = 9, - - PERF_EVENT_MAX, /* non-ABI */ -}; - -enum perf_callchain_context { - PERF_CONTEXT_HV = (__u64)-32, - PERF_CONTEXT_KERNEL = (__u64)-128, - PERF_CONTEXT_USER = (__u64)-512, - - PERF_CONTEXT_GUEST = (__u64)-2048, - PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, - PERF_CONTEXT_GUEST_USER = (__u64)-2560, - - PERF_CONTEXT_MAX = (__u64)-4095, -}; - -#define PERF_FLAG_FD_NO_GROUP (1U << 0) -#define PERF_FLAG_FD_OUTPUT (1U << 1) - -#ifdef __KERNEL__ -/* - * Kernel-internal data types and definitions: - */ - -#ifdef CONFIG_PERF_COUNTERS -# include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PERF_MAX_STACK_DEPTH 255 - -struct perf_callchain_entry { - __u64 nr; - __u64 ip[PERF_MAX_STACK_DEPTH]; -}; - -struct perf_raw_record { - u32 size; - void *data; -}; - -struct task_struct; - -/** - * struct hw_perf_counter - performance counter hardware details: - */ -struct hw_perf_counter { -#ifdef CONFIG_PERF_COUNTERS - union { - struct { /* hardware */ - u64 config; - unsigned long config_base; - unsigned long counter_base; - int idx; - }; - union { /* software */ - atomic64_t count; - struct hrtimer hrtimer; - }; - }; - atomic64_t prev_count; - u64 sample_period; - u64 last_period; - atomic64_t period_left; - u64 interrupts; - - u64 freq_count; - u64 freq_interrupts; - u64 freq_stamp; -#endif -}; - -struct perf_counter; - -/** - * struct pmu - generic performance monitoring unit - */ -struct pmu { - int (*enable) (struct perf_counter *counter); - void (*disable) (struct perf_counter *counter); - void (*read) (struct perf_counter *counter); - void (*unthrottle) (struct perf_counter *counter); -}; - -/** - * enum perf_counter_active_state - the states of a counter - */ -enum perf_counter_active_state { - PERF_COUNTER_STATE_ERROR = -2, - PERF_COUNTER_STATE_OFF = -1, - PERF_COUNTER_STATE_INACTIVE = 0, - PERF_COUNTER_STATE_ACTIVE = 1, -}; - -struct file; - -struct perf_mmap_data { - struct rcu_head rcu_head; - int nr_pages; /* nr of data pages */ - int writable; /* are we writable */ - int nr_locked; /* nr pages mlocked */ - - atomic_t poll; /* POLL_ for wakeups */ - atomic_t events; /* event limit */ - - atomic_long_t head; /* write position */ - atomic_long_t done_head; /* completed head */ - - atomic_t lock; /* concurrent writes */ - atomic_t wakeup; /* needs a wakeup */ - atomic_t lost; /* nr records lost */ - - long watermark; /* wakeup watermark */ - - struct perf_counter_mmap_page *user_page; - void *data_pages[0]; -}; - -struct perf_pending_entry { - struct perf_pending_entry *next; - void (*func)(struct perf_pending_entry *); -}; - -/** - * struct perf_counter - performance counter kernel representation: - */ -struct perf_counter { -#ifdef CONFIG_PERF_COUNTERS - struct list_head group_entry; - struct list_head event_entry; - struct list_head sibling_list; - int nr_siblings; - struct perf_counter *group_leader; - struct perf_counter *output; - const struct pmu *pmu; - - enum perf_counter_active_state state; - atomic64_t count; - - /* - * These are the total time in nanoseconds that the counter - * has been enabled (i.e. eligible to run, and the task has - * been scheduled in, if this is a per-task counter) - * and running (scheduled onto the CPU), respectively. - * - * They are computed from tstamp_enabled, tstamp_running and - * tstamp_stopped when the counter is in INACTIVE or ACTIVE state. - */ - u64 total_time_enabled; - u64 total_time_running; - - /* - * These are timestamps used for computing total_time_enabled - * and total_time_running when the counter is in INACTIVE or - * ACTIVE state, measured in nanoseconds from an arbitrary point - * in time. - * tstamp_enabled: the notional time when the counter was enabled - * tstamp_running: the notional time when the counter was scheduled on - * tstamp_stopped: in INACTIVE state, the notional time when the - * counter was scheduled off. - */ - u64 tstamp_enabled; - u64 tstamp_running; - u64 tstamp_stopped; - - struct perf_counter_attr attr; - struct hw_perf_counter hw; - - struct perf_counter_context *ctx; - struct file *filp; - - /* - * These accumulate total time (in nanoseconds) that children - * counters have been enabled and running, respectively. - */ - atomic64_t child_total_time_enabled; - atomic64_t child_total_time_running; - - /* - * Protect attach/detach and child_list: - */ - struct mutex child_mutex; - struct list_head child_list; - struct perf_counter *parent; - - int oncpu; - int cpu; - - struct list_head owner_entry; - struct task_struct *owner; - - /* mmap bits */ - struct mutex mmap_mutex; - atomic_t mmap_count; - struct perf_mmap_data *data; - - /* poll related */ - wait_queue_head_t waitq; - struct fasync_struct *fasync; - - /* delayed work for NMIs and such */ - int pending_wakeup; - int pending_kill; - int pending_disable; - struct perf_pending_entry pending; - - atomic_t event_limit; - - void (*destroy)(struct perf_counter *); - struct rcu_head rcu_head; - - struct pid_namespace *ns; - u64 id; -#endif -}; - -/** - * struct perf_counter_context - counter context structure - * - * Used as a container for task counters and CPU counters as well: - */ -struct perf_counter_context { - /* - * Protect the states of the counters in the list, - * nr_active, and the list: - */ - spinlock_t lock; - /* - * Protect the list of counters. Locking either mutex or lock - * is sufficient to ensure the list doesn't change; to change - * the list you need to lock both the mutex and the spinlock. - */ - struct mutex mutex; - - struct list_head group_list; - struct list_head event_list; - int nr_counters; - int nr_active; - int is_active; - int nr_stat; - atomic_t refcount; - struct task_struct *task; - - /* - * Context clock, runs when context enabled. - */ - u64 time; - u64 timestamp; - - /* - * These fields let us detect when two contexts have both - * been cloned (inherited) from a common ancestor. - */ - struct perf_counter_context *parent_ctx; - u64 parent_gen; - u64 generation; - int pin_count; - struct rcu_head rcu_head; -}; - -/** - * struct perf_counter_cpu_context - per cpu counter context structure - */ -struct perf_cpu_context { - struct perf_counter_context ctx; - struct perf_counter_context *task_ctx; - int active_oncpu; - int max_pertask; - int exclusive; - - /* - * Recursion avoidance: - * - * task, softirq, irq, nmi context - */ - int recursion[4]; -}; - -struct perf_output_handle { - struct perf_counter *counter; - struct perf_mmap_data *data; - unsigned long head; - unsigned long offset; - int nmi; - int sample; - int locked; - unsigned long flags; -}; - -#ifdef CONFIG_PERF_COUNTERS - -/* - * Set by architecture code: - */ -extern int perf_max_counters; - -extern const struct pmu *hw_perf_counter_init(struct perf_counter *counter); - -extern void perf_counter_task_sched_in(struct task_struct *task, int cpu); -extern void perf_counter_task_sched_out(struct task_struct *task, - struct task_struct *next, int cpu); -extern void perf_counter_task_tick(struct task_struct *task, int cpu); -extern int perf_counter_init_task(struct task_struct *child); -extern void perf_counter_exit_task(struct task_struct *child); -extern void perf_counter_free_task(struct task_struct *task); -extern void set_perf_counter_pending(void); -extern void perf_counter_do_pending(void); -extern void perf_counter_print_debug(void); -extern void __perf_disable(void); -extern bool __perf_enable(void); -extern void perf_disable(void); -extern void perf_enable(void); -extern int perf_counter_task_disable(void); -extern int perf_counter_task_enable(void); -extern int hw_perf_group_sched_in(struct perf_counter *group_leader, - struct perf_cpu_context *cpuctx, - struct perf_counter_context *ctx, int cpu); -extern void perf_counter_update_userpage(struct perf_counter *counter); - -struct perf_sample_data { - u64 type; - - u64 ip; - struct { - u32 pid; - u32 tid; - } tid_entry; - u64 time; - u64 addr; - u64 id; - u64 stream_id; - struct { - u32 cpu; - u32 reserved; - } cpu_entry; - u64 period; - struct perf_callchain_entry *callchain; - struct perf_raw_record *raw; -}; - -extern void perf_output_sample(struct perf_output_handle *handle, - struct perf_event_header *header, - struct perf_sample_data *data, - struct perf_counter *counter); -extern void perf_prepare_sample(struct perf_event_header *header, - struct perf_sample_data *data, - struct perf_counter *counter, - struct pt_regs *regs); - -extern int perf_counter_overflow(struct perf_counter *counter, int nmi, - struct perf_sample_data *data, - struct pt_regs *regs); - -/* - * Return 1 for a software counter, 0 for a hardware counter - */ -static inline int is_software_counter(struct perf_counter *counter) -{ - return (counter->attr.type != PERF_TYPE_RAW) && - (counter->attr.type != PERF_TYPE_HARDWARE) && - (counter->attr.type != PERF_TYPE_HW_CACHE); -} - -extern atomic_t perf_swcounter_enabled[PERF_COUNT_SW_MAX]; - -extern void __perf_swcounter_event(u32, u64, int, struct pt_regs *, u64); - -static inline void -perf_swcounter_event(u32 event, u64 nr, int nmi, struct pt_regs *regs, u64 addr) -{ - if (atomic_read(&perf_swcounter_enabled[event])) - __perf_swcounter_event(event, nr, nmi, regs, addr); -} - -extern void __perf_counter_mmap(struct vm_area_struct *vma); - -static inline void perf_counter_mmap(struct vm_area_struct *vma) -{ - if (vma->vm_flags & VM_EXEC) - __perf_counter_mmap(vma); -} - -extern void perf_counter_comm(struct task_struct *tsk); -extern void perf_counter_fork(struct task_struct *tsk); - -extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs); - -extern int sysctl_perf_counter_paranoid; -extern int sysctl_perf_counter_mlock; -extern int sysctl_perf_counter_sample_rate; - -extern void perf_counter_init(void); -extern void perf_tpcounter_event(int event_id, u64 addr, u64 count, - void *record, int entry_size); - -#ifndef perf_misc_flags -#define perf_misc_flags(regs) (user_mode(regs) ? PERF_EVENT_MISC_USER : \ - PERF_EVENT_MISC_KERNEL) -#define perf_instruction_pointer(regs) instruction_pointer(regs) -#endif - -extern int perf_output_begin(struct perf_output_handle *handle, - struct perf_counter *counter, unsigned int size, - int nmi, int sample); -extern void perf_output_end(struct perf_output_handle *handle); -extern void perf_output_copy(struct perf_output_handle *handle, - const void *buf, unsigned int len); -#else -static inline void -perf_counter_task_sched_in(struct task_struct *task, int cpu) { } -static inline void -perf_counter_task_sched_out(struct task_struct *task, - struct task_struct *next, int cpu) { } -static inline void -perf_counter_task_tick(struct task_struct *task, int cpu) { } -static inline int perf_counter_init_task(struct task_struct *child) { return 0; } -static inline void perf_counter_exit_task(struct task_struct *child) { } -static inline void perf_counter_free_task(struct task_struct *task) { } -static inline void perf_counter_do_pending(void) { } -static inline void perf_counter_print_debug(void) { } -static inline void perf_disable(void) { } -static inline void perf_enable(void) { } -static inline int perf_counter_task_disable(void) { return -EINVAL; } -static inline int perf_counter_task_enable(void) { return -EINVAL; } - -static inline void -perf_swcounter_event(u32 event, u64 nr, int nmi, - struct pt_regs *regs, u64 addr) { } - -static inline void perf_counter_mmap(struct vm_area_struct *vma) { } -static inline void perf_counter_comm(struct task_struct *tsk) { } -static inline void perf_counter_fork(struct task_struct *tsk) { } -static inline void perf_counter_init(void) { } - -#endif - -#define perf_output_put(handle, x) \ - perf_output_copy((handle), &(x), sizeof(x)) - -#endif /* __KERNEL__ */ -#endif /* _LINUX_PERF_COUNTER_H */ diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h new file mode 100644 index 000000000000..ae9d9ed6df2a --- /dev/null +++ b/include/linux/perf_event.h @@ -0,0 +1,858 @@ +/* + * Performance events: + * + * Copyright (C) 2008-2009, Thomas Gleixner + * Copyright (C) 2008-2009, Red Hat, Inc., Ingo Molnar + * Copyright (C) 2008-2009, Red Hat, Inc., Peter Zijlstra + * + * Data type definitions, declarations, prototypes. + * + * Started by: Thomas Gleixner and Ingo Molnar + * + * For licencing details see kernel-base/COPYING + */ +#ifndef _LINUX_PERF_EVENT_H +#define _LINUX_PERF_EVENT_H + +#include +#include +#include + +/* + * User-space ABI bits: + */ + +/* + * attr.type + */ +enum perf_type_id { + PERF_TYPE_HARDWARE = 0, + PERF_TYPE_SOFTWARE = 1, + PERF_TYPE_TRACEPOINT = 2, + PERF_TYPE_HW_CACHE = 3, + PERF_TYPE_RAW = 4, + + PERF_TYPE_MAX, /* non-ABI */ +}; + +/* + * Generalized performance event event_id types, used by the + * attr.event_id parameter of the sys_perf_event_open() + * syscall: + */ +enum perf_hw_id { + /* + * Common hardware events, generalized by the kernel: + */ + PERF_COUNT_HW_CPU_CYCLES = 0, + PERF_COUNT_HW_INSTRUCTIONS = 1, + PERF_COUNT_HW_CACHE_REFERENCES = 2, + PERF_COUNT_HW_CACHE_MISSES = 3, + PERF_COUNT_HW_BRANCH_INSTRUCTIONS = 4, + PERF_COUNT_HW_BRANCH_MISSES = 5, + PERF_COUNT_HW_BUS_CYCLES = 6, + + PERF_COUNT_HW_MAX, /* non-ABI */ +}; + +/* + * Generalized hardware cache events: + * + * { L1-D, L1-I, LLC, ITLB, DTLB, BPU } x + * { read, write, prefetch } x + * { accesses, misses } + */ +enum perf_hw_cache_id { + PERF_COUNT_HW_CACHE_L1D = 0, + PERF_COUNT_HW_CACHE_L1I = 1, + PERF_COUNT_HW_CACHE_LL = 2, + PERF_COUNT_HW_CACHE_DTLB = 3, + PERF_COUNT_HW_CACHE_ITLB = 4, + PERF_COUNT_HW_CACHE_BPU = 5, + + PERF_COUNT_HW_CACHE_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_id { + PERF_COUNT_HW_CACHE_OP_READ = 0, + PERF_COUNT_HW_CACHE_OP_WRITE = 1, + PERF_COUNT_HW_CACHE_OP_PREFETCH = 2, + + PERF_COUNT_HW_CACHE_OP_MAX, /* non-ABI */ +}; + +enum perf_hw_cache_op_result_id { + PERF_COUNT_HW_CACHE_RESULT_ACCESS = 0, + PERF_COUNT_HW_CACHE_RESULT_MISS = 1, + + PERF_COUNT_HW_CACHE_RESULT_MAX, /* non-ABI */ +}; + +/* + * Special "software" events provided by the kernel, even if the hardware + * does not support performance events. These events measure various + * physical and sw events of the kernel (and allow the profiling of them as + * well): + */ +enum perf_sw_ids { + PERF_COUNT_SW_CPU_CLOCK = 0, + PERF_COUNT_SW_TASK_CLOCK = 1, + PERF_COUNT_SW_PAGE_FAULTS = 2, + PERF_COUNT_SW_CONTEXT_SWITCHES = 3, + PERF_COUNT_SW_CPU_MIGRATIONS = 4, + PERF_COUNT_SW_PAGE_FAULTS_MIN = 5, + PERF_COUNT_SW_PAGE_FAULTS_MAJ = 6, + + PERF_COUNT_SW_MAX, /* non-ABI */ +}; + +/* + * Bits that can be set in attr.sample_type to request information + * in the overflow packets. + */ +enum perf_event_sample_format { + PERF_SAMPLE_IP = 1U << 0, + PERF_SAMPLE_TID = 1U << 1, + PERF_SAMPLE_TIME = 1U << 2, + PERF_SAMPLE_ADDR = 1U << 3, + PERF_SAMPLE_READ = 1U << 4, + PERF_SAMPLE_CALLCHAIN = 1U << 5, + PERF_SAMPLE_ID = 1U << 6, + PERF_SAMPLE_CPU = 1U << 7, + PERF_SAMPLE_PERIOD = 1U << 8, + PERF_SAMPLE_STREAM_ID = 1U << 9, + PERF_SAMPLE_RAW = 1U << 10, + + PERF_SAMPLE_MAX = 1U << 11, /* non-ABI */ +}; + +/* + * The format of the data returned by read() on a perf event fd, + * as specified by attr.read_format: + * + * struct read_format { + * { u64 value; + * { u64 time_enabled; } && PERF_FORMAT_ENABLED + * { u64 time_running; } && PERF_FORMAT_RUNNING + * { u64 id; } && PERF_FORMAT_ID + * } && !PERF_FORMAT_GROUP + * + * { u64 nr; + * { u64 time_enabled; } && PERF_FORMAT_ENABLED + * { u64 time_running; } && PERF_FORMAT_RUNNING + * { u64 value; + * { u64 id; } && PERF_FORMAT_ID + * } cntr[nr]; + * } && PERF_FORMAT_GROUP + * }; + */ +enum perf_event_read_format { + PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0, + PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1, + PERF_FORMAT_ID = 1U << 2, + PERF_FORMAT_GROUP = 1U << 3, + + PERF_FORMAT_MAX = 1U << 4, /* non-ABI */ +}; + +#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */ + +/* + * Hardware event_id to monitor via a performance monitoring event: + */ +struct perf_event_attr { + + /* + * Major type: hardware/software/tracepoint/etc. + */ + __u32 type; + + /* + * Size of the attr structure, for fwd/bwd compat. + */ + __u32 size; + + /* + * Type specific configuration information. + */ + __u64 config; + + union { + __u64 sample_period; + __u64 sample_freq; + }; + + __u64 sample_type; + __u64 read_format; + + __u64 disabled : 1, /* off by default */ + inherit : 1, /* children inherit it */ + pinned : 1, /* must always be on PMU */ + exclusive : 1, /* only group on PMU */ + exclude_user : 1, /* don't count user */ + exclude_kernel : 1, /* ditto kernel */ + exclude_hv : 1, /* ditto hypervisor */ + exclude_idle : 1, /* don't count when idle */ + mmap : 1, /* include mmap data */ + comm : 1, /* include comm data */ + freq : 1, /* use freq, not period */ + inherit_stat : 1, /* per task counts */ + enable_on_exec : 1, /* next exec enables */ + task : 1, /* trace fork/exit */ + watermark : 1, /* wakeup_watermark */ + + __reserved_1 : 49; + + union { + __u32 wakeup_events; /* wakeup every n events */ + __u32 wakeup_watermark; /* bytes before wakeup */ + }; + __u32 __reserved_2; + + __u64 __reserved_3; +}; + +/* + * Ioctls that can be done on a perf event fd: + */ +#define PERF_EVENT_IOC_ENABLE _IO ('$', 0) +#define PERF_EVENT_IOC_DISABLE _IO ('$', 1) +#define PERF_EVENT_IOC_REFRESH _IO ('$', 2) +#define PERF_EVENT_IOC_RESET _IO ('$', 3) +#define PERF_EVENT_IOC_PERIOD _IOW('$', 4, u64) +#define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) + +enum perf_event_ioc_flags { + PERF_IOC_FLAG_GROUP = 1U << 0, +}; + +/* + * Structure of the page that can be mapped via mmap + */ +struct perf_event_mmap_page { + __u32 version; /* version number of this structure */ + __u32 compat_version; /* lowest version this is compat with */ + + /* + * Bits needed to read the hw events in user-space. + * + * u32 seq; + * s64 count; + * + * do { + * seq = pc->lock; + * + * barrier() + * if (pc->index) { + * count = pmc_read(pc->index - 1); + * count += pc->offset; + * } else + * goto regular_read; + * + * barrier(); + * } while (pc->lock != seq); + * + * NOTE: for obvious reason this only works on self-monitoring + * processes. + */ + __u32 lock; /* seqlock for synchronization */ + __u32 index; /* hardware event identifier */ + __s64 offset; /* add to hardware event value */ + __u64 time_enabled; /* time event active */ + __u64 time_running; /* time event on cpu */ + + /* + * Hole for extension of the self monitor capabilities + */ + + __u64 __reserved[123]; /* align to 1k */ + + /* + * Control data for the mmap() data buffer. + * + * User-space reading the @data_head value should issue an rmb(), on + * SMP capable platforms, after reading this value -- see + * perf_event_wakeup(). + * + * When the mapping is PROT_WRITE the @data_tail value should be + * written by userspace to reflect the last read data. In this case + * the kernel will not over-write unread data. + */ + __u64 data_head; /* head in the data section */ + __u64 data_tail; /* user-space written tail */ +}; + +#define PERF_RECORD_MISC_CPUMODE_MASK (3 << 0) +#define PERF_RECORD_MISC_CPUMODE_UNKNOWN (0 << 0) +#define PERF_RECORD_MISC_KERNEL (1 << 0) +#define PERF_RECORD_MISC_USER (2 << 0) +#define PERF_RECORD_MISC_HYPERVISOR (3 << 0) + +struct perf_event_header { + __u32 type; + __u16 misc; + __u16 size; +}; + +enum perf_event_type { + + /* + * The MMAP events record the PROT_EXEC mappings so that we can + * correlate userspace IPs to code. They have the following structure: + * + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * u64 addr; + * u64 len; + * u64 pgoff; + * char filename[]; + * }; + */ + PERF_RECORD_MMAP = 1, + + /* + * struct { + * struct perf_event_header header; + * u64 id; + * u64 lost; + * }; + */ + PERF_RECORD_LOST = 2, + + /* + * struct { + * struct perf_event_header header; + * + * u32 pid, tid; + * char comm[]; + * }; + */ + PERF_RECORD_COMM = 3, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, ppid; + * u32 tid, ptid; + * u64 time; + * }; + */ + PERF_RECORD_EXIT = 4, + + /* + * struct { + * struct perf_event_header header; + * u64 time; + * u64 id; + * u64 stream_id; + * }; + */ + PERF_RECORD_THROTTLE = 5, + PERF_RECORD_UNTHROTTLE = 6, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, ppid; + * u32 tid, ptid; + * { u64 time; } && PERF_SAMPLE_TIME + * }; + */ + PERF_RECORD_FORK = 7, + + /* + * struct { + * struct perf_event_header header; + * u32 pid, tid; + * + * struct read_format values; + * }; + */ + PERF_RECORD_READ = 8, + + /* + * struct { + * struct perf_event_header header; + * + * { u64 ip; } && PERF_SAMPLE_IP + * { u32 pid, tid; } && PERF_SAMPLE_TID + * { u64 time; } && PERF_SAMPLE_TIME + * { u64 addr; } && PERF_SAMPLE_ADDR + * { u64 id; } && PERF_SAMPLE_ID + * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID + * { u32 cpu, res; } && PERF_SAMPLE_CPU + * { u64 period; } && PERF_SAMPLE_PERIOD + * + * { struct read_format values; } && PERF_SAMPLE_READ + * + * { u64 nr, + * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN + * + * # + * # The RAW record below is opaque data wrt the ABI + * # + * # That is, the ABI doesn't make any promises wrt to + * # the stability of its content, it may vary depending + * # on event_id, hardware, kernel version and phase of + * # the moon. + * # + * # In other words, PERF_SAMPLE_RAW contents are not an ABI. + * # + * + * { u32 size; + * char data[size];}&& PERF_SAMPLE_RAW + * }; + */ + PERF_RECORD_SAMPLE = 9, + + PERF_RECORD_MAX, /* non-ABI */ +}; + +enum perf_callchain_context { + PERF_CONTEXT_HV = (__u64)-32, + PERF_CONTEXT_KERNEL = (__u64)-128, + PERF_CONTEXT_USER = (__u64)-512, + + PERF_CONTEXT_GUEST = (__u64)-2048, + PERF_CONTEXT_GUEST_KERNEL = (__u64)-2176, + PERF_CONTEXT_GUEST_USER = (__u64)-2560, + + PERF_CONTEXT_MAX = (__u64)-4095, +}; + +#define PERF_FLAG_FD_NO_GROUP (1U << 0) +#define PERF_FLAG_FD_OUTPUT (1U << 1) + +#ifdef __KERNEL__ +/* + * Kernel-internal data types and definitions: + */ + +#ifdef CONFIG_PERF_EVENTS +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PERF_MAX_STACK_DEPTH 255 + +struct perf_callchain_entry { + __u64 nr; + __u64 ip[PERF_MAX_STACK_DEPTH]; +}; + +struct perf_raw_record { + u32 size; + void *data; +}; + +struct task_struct; + +/** + * struct hw_perf_event - performance event hardware details: + */ +struct hw_perf_event { +#ifdef CONFIG_PERF_EVENTS + union { + struct { /* hardware */ + u64 config; + unsigned long config_base; + unsigned long event_base; + int idx; + }; + union { /* software */ + atomic64_t count; + struct hrtimer hrtimer; + }; + }; + atomic64_t prev_count; + u64 sample_period; + u64 last_period; + atomic64_t period_left; + u64 interrupts; + + u64 freq_count; + u64 freq_interrupts; + u64 freq_stamp; +#endif +}; + +struct perf_event; + +/** + * struct pmu - generic performance monitoring unit + */ +struct pmu { + int (*enable) (struct perf_event *event); + void (*disable) (struct perf_event *event); + void (*read) (struct perf_event *event); + void (*unthrottle) (struct perf_event *event); +}; + +/** + * enum perf_event_active_state - the states of a event + */ +enum perf_event_active_state { + PERF_EVENT_STATE_ERROR = -2, + PERF_EVENT_STATE_OFF = -1, + PERF_EVENT_STATE_INACTIVE = 0, + PERF_EVENT_STATE_ACTIVE = 1, +}; + +struct file; + +struct perf_mmap_data { + struct rcu_head rcu_head; + int nr_pages; /* nr of data pages */ + int writable; /* are we writable */ + int nr_locked; /* nr pages mlocked */ + + atomic_t poll; /* POLL_ for wakeups */ + atomic_t events; /* event_id limit */ + + atomic_long_t head; /* write position */ + atomic_long_t done_head; /* completed head */ + + atomic_t lock; /* concurrent writes */ + atomic_t wakeup; /* needs a wakeup */ + atomic_t lost; /* nr records lost */ + + long watermark; /* wakeup watermark */ + + struct perf_event_mmap_page *user_page; + void *data_pages[0]; +}; + +struct perf_pending_entry { + struct perf_pending_entry *next; + void (*func)(struct perf_pending_entry *); +}; + +/** + * struct perf_event - performance event kernel representation: + */ +struct perf_event { +#ifdef CONFIG_PERF_EVENTS + struct list_head group_entry; + struct list_head event_entry; + struct list_head sibling_list; + int nr_siblings; + struct perf_event *group_leader; + struct perf_event *output; + const struct pmu *pmu; + + enum perf_event_active_state state; + atomic64_t count; + + /* + * These are the total time in nanoseconds that the event + * has been enabled (i.e. eligible to run, and the task has + * been scheduled in, if this is a per-task event) + * and running (scheduled onto the CPU), respectively. + * + * They are computed from tstamp_enabled, tstamp_running and + * tstamp_stopped when the event is in INACTIVE or ACTIVE state. + */ + u64 total_time_enabled; + u64 total_time_running; + + /* + * These are timestamps used for computing total_time_enabled + * and total_time_running when the event is in INACTIVE or + * ACTIVE state, measured in nanoseconds from an arbitrary point + * in time. + * tstamp_enabled: the notional time when the event was enabled + * tstamp_running: the notional time when the event was scheduled on + * tstamp_stopped: in INACTIVE state, the notional time when the + * event was scheduled off. + */ + u64 tstamp_enabled; + u64 tstamp_running; + u64 tstamp_stopped; + + struct perf_event_attr attr; + struct hw_perf_event hw; + + struct perf_event_context *ctx; + struct file *filp; + + /* + * These accumulate total time (in nanoseconds) that children + * events have been enabled and running, respectively. + */ + atomic64_t child_total_time_enabled; + atomic64_t child_total_time_running; + + /* + * Protect attach/detach and child_list: + */ + struct mutex child_mutex; + struct list_head child_list; + struct perf_event *parent; + + int oncpu; + int cpu; + + struct list_head owner_entry; + struct task_struct *owner; + + /* mmap bits */ + struct mutex mmap_mutex; + atomic_t mmap_count; + struct perf_mmap_data *data; + + /* poll related */ + wait_queue_head_t waitq; + struct fasync_struct *fasync; + + /* delayed work for NMIs and such */ + int pending_wakeup; + int pending_kill; + int pending_disable; + struct perf_pending_entry pending; + + atomic_t event_limit; + + void (*destroy)(struct perf_event *); + struct rcu_head rcu_head; + + struct pid_namespace *ns; + u64 id; +#endif +}; + +/** + * struct perf_event_context - event context structure + * + * Used as a container for task events and CPU events as well: + */ +struct perf_event_context { + /* + * Protect the states of the events in the list, + * nr_active, and the list: + */ + spinlock_t lock; + /* + * Protect the list of events. Locking either mutex or lock + * is sufficient to ensure the list doesn't change; to change + * the list you need to lock both the mutex and the spinlock. + */ + struct mutex mutex; + + struct list_head group_list; + struct list_head event_list; + int nr_events; + int nr_active; + int is_active; + int nr_stat; + atomic_t refcount; + struct task_struct *task; + + /* + * Context clock, runs when context enabled. + */ + u64 time; + u64 timestamp; + + /* + * These fields let us detect when two contexts have both + * been cloned (inherited) from a common ancestor. + */ + struct perf_event_context *parent_ctx; + u64 parent_gen; + u64 generation; + int pin_count; + struct rcu_head rcu_head; +}; + +/** + * struct perf_event_cpu_context - per cpu event context structure + */ +struct perf_cpu_context { + struct perf_event_context ctx; + struct perf_event_context *task_ctx; + int active_oncpu; + int max_pertask; + int exclusive; + + /* + * Recursion avoidance: + * + * task, softirq, irq, nmi context + */ + int recursion[4]; +}; + +struct perf_output_handle { + struct perf_event *event; + struct perf_mmap_data *data; + unsigned long head; + unsigned long offset; + int nmi; + int sample; + int locked; + unsigned long flags; +}; + +#ifdef CONFIG_PERF_EVENTS + +/* + * Set by architecture code: + */ +extern int perf_max_events; + +extern const struct pmu *hw_perf_event_init(struct perf_event *event); + +extern void perf_event_task_sched_in(struct task_struct *task, int cpu); +extern void perf_event_task_sched_out(struct task_struct *task, + struct task_struct *next, int cpu); +extern void perf_event_task_tick(struct task_struct *task, int cpu); +extern int perf_event_init_task(struct task_struct *child); +extern void perf_event_exit_task(struct task_struct *child); +extern void perf_event_free_task(struct task_struct *task); +extern void set_perf_event_pending(void); +extern void perf_event_do_pending(void); +extern void perf_event_print_debug(void); +extern void __perf_disable(void); +extern bool __perf_enable(void); +extern void perf_disable(void); +extern void perf_enable(void); +extern int perf_event_task_disable(void); +extern int perf_event_task_enable(void); +extern int hw_perf_group_sched_in(struct perf_event *group_leader, + struct perf_cpu_context *cpuctx, + struct perf_event_context *ctx, int cpu); +extern void perf_event_update_userpage(struct perf_event *event); + +struct perf_sample_data { + u64 type; + + u64 ip; + struct { + u32 pid; + u32 tid; + } tid_entry; + u64 time; + u64 addr; + u64 id; + u64 stream_id; + struct { + u32 cpu; + u32 reserved; + } cpu_entry; + u64 period; + struct perf_callchain_entry *callchain; + struct perf_raw_record *raw; +}; + +extern void perf_output_sample(struct perf_output_handle *handle, + struct perf_event_header *header, + struct perf_sample_data *data, + struct perf_event *event); +extern void perf_prepare_sample(struct perf_event_header *header, + struct perf_sample_data *data, + struct perf_event *event, + struct pt_regs *regs); + +extern int perf_event_overflow(struct perf_event *event, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs); + +/* + * Return 1 for a software event, 0 for a hardware event + */ +static inline int is_software_event(struct perf_event *event) +{ + return (event->attr.type != PERF_TYPE_RAW) && + (event->attr.type != PERF_TYPE_HARDWARE) && + (event->attr.type != PERF_TYPE_HW_CACHE); +} + +extern atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX]; + +extern void __perf_sw_event(u32, u64, int, struct pt_regs *, u64); + +static inline void +perf_sw_event(u32 event_id, u64 nr, int nmi, struct pt_regs *regs, u64 addr) +{ + if (atomic_read(&perf_swevent_enabled[event_id])) + __perf_sw_event(event_id, nr, nmi, regs, addr); +} + +extern void __perf_event_mmap(struct vm_area_struct *vma); + +static inline void perf_event_mmap(struct vm_area_struct *vma) +{ + if (vma->vm_flags & VM_EXEC) + __perf_event_mmap(vma); +} + +extern void perf_event_comm(struct task_struct *tsk); +extern void perf_event_fork(struct task_struct *tsk); + +extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs); + +extern int sysctl_perf_event_paranoid; +extern int sysctl_perf_event_mlock; +extern int sysctl_perf_event_sample_rate; + +extern void perf_event_init(void); +extern void perf_tp_event(int event_id, u64 addr, u64 count, + void *record, int entry_size); + +#ifndef perf_misc_flags +#define perf_misc_flags(regs) (user_mode(regs) ? PERF_RECORD_MISC_USER : \ + PERF_RECORD_MISC_KERNEL) +#define perf_instruction_pointer(regs) instruction_pointer(regs) +#endif + +extern int perf_output_begin(struct perf_output_handle *handle, + struct perf_event *event, unsigned int size, + int nmi, int sample); +extern void perf_output_end(struct perf_output_handle *handle); +extern void perf_output_copy(struct perf_output_handle *handle, + const void *buf, unsigned int len); +#else +static inline void +perf_event_task_sched_in(struct task_struct *task, int cpu) { } +static inline void +perf_event_task_sched_out(struct task_struct *task, + struct task_struct *next, int cpu) { } +static inline void +perf_event_task_tick(struct task_struct *task, int cpu) { } +static inline int perf_event_init_task(struct task_struct *child) { return 0; } +static inline void perf_event_exit_task(struct task_struct *child) { } +static inline void perf_event_free_task(struct task_struct *task) { } +static inline void perf_event_do_pending(void) { } +static inline void perf_event_print_debug(void) { } +static inline void perf_disable(void) { } +static inline void perf_enable(void) { } +static inline int perf_event_task_disable(void) { return -EINVAL; } +static inline int perf_event_task_enable(void) { return -EINVAL; } + +static inline void +perf_sw_event(u32 event_id, u64 nr, int nmi, + struct pt_regs *regs, u64 addr) { } + +static inline void perf_event_mmap(struct vm_area_struct *vma) { } +static inline void perf_event_comm(struct task_struct *tsk) { } +static inline void perf_event_fork(struct task_struct *tsk) { } +static inline void perf_event_init(void) { } + +#endif + +#define perf_output_put(handle, x) \ + perf_output_copy((handle), &(x), sizeof(x)) + +#endif /* __KERNEL__ */ +#endif /* _LINUX_PERF_EVENT_H */ diff --git a/include/linux/prctl.h b/include/linux/prctl.h index b00df4c79c63..07bff666e65b 100644 --- a/include/linux/prctl.h +++ b/include/linux/prctl.h @@ -85,7 +85,7 @@ #define PR_SET_TIMERSLACK 29 #define PR_GET_TIMERSLACK 30 -#define PR_TASK_PERF_COUNTERS_DISABLE 31 -#define PR_TASK_PERF_COUNTERS_ENABLE 32 +#define PR_TASK_PERF_EVENTS_DISABLE 31 +#define PR_TASK_PERF_EVENTS_ENABLE 32 #endif /* _LINUX_PRCTL_H */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 8af3d249170e..8b265a8986d0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -100,7 +100,7 @@ struct robust_list_head; struct bio; struct fs_struct; struct bts_context; -struct perf_counter_context; +struct perf_event_context; /* * List of flags we want to share for kernel threads, @@ -701,7 +701,7 @@ struct user_struct { #endif #endif -#ifdef CONFIG_PERF_COUNTERS +#ifdef CONFIG_PERF_EVENTS atomic_long_t locked_vm; #endif }; @@ -1449,10 +1449,10 @@ struct task_struct { struct list_head pi_state_list; struct futex_pi_state *pi_state_cache; #endif -#ifdef CONFIG_PERF_COUNTERS - struct perf_counter_context *perf_counter_ctxp; - struct mutex perf_counter_mutex; - struct list_head perf_counter_list; +#ifdef CONFIG_PERF_EVENTS + struct perf_event_context *perf_event_ctxp; + struct mutex perf_event_mutex; + struct list_head perf_event_list; #endif #ifdef CONFIG_NUMA struct mempolicy *mempolicy; /* Protected by alloc_lock */ diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index a8e37821cc60..02f19f9a76c6 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -55,7 +55,7 @@ struct compat_timeval; struct robust_list_head; struct getcpu_cache; struct old_linux_dirent; -struct perf_counter_attr; +struct perf_event_attr; #include #include @@ -885,7 +885,7 @@ asmlinkage long sys_ppoll(struct pollfd __user *, unsigned int, int kernel_execve(const char *filename, char *const argv[], char *const envp[]); -asmlinkage long sys_perf_counter_open( - struct perf_counter_attr __user *attr_uptr, +asmlinkage long sys_perf_event_open( + struct perf_event_attr __user *attr_uptr, pid_t pid, int cpu, int group_fd, unsigned long flags); #endif diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h index 72a3b437b829..ec91e78244f0 100644 --- a/include/trace/ftrace.h +++ b/include/trace/ftrace.h @@ -378,7 +378,7 @@ static inline int ftrace_get_offsets_##call( \ #ifdef CONFIG_EVENT_PROFILE /* - * Generate the functions needed for tracepoint perf_counter support. + * Generate the functions needed for tracepoint perf_event support. * * NOTE: The insertion profile callback (ftrace_profile_) is defined later * @@ -656,7 +656,7 @@ __attribute__((section("_ftrace_events"))) event_##call = { \ * { * struct ftrace_data_offsets_ __maybe_unused __data_offsets; * struct ftrace_event_call *event_call = &event_; - * extern void perf_tpcounter_event(int, u64, u64, void *, int); + * extern void perf_tp_event(int, u64, u64, void *, int); * struct ftrace_raw_##call *entry; * u64 __addr = 0, __count = 1; * unsigned long irq_flags; @@ -691,7 +691,7 @@ __attribute__((section("_ftrace_events"))) event_##call = { \ * * <- affect our values * - * perf_tpcounter_event(event_call->id, __addr, __count, entry, + * perf_tp_event(event_call->id, __addr, __count, entry, * __entry_size); <- submit them to perf counter * } while (0); * @@ -712,7 +712,7 @@ static void ftrace_profile_##call(proto) \ { \ struct ftrace_data_offsets_##call __maybe_unused __data_offsets;\ struct ftrace_event_call *event_call = &event_##call; \ - extern void perf_tpcounter_event(int, u64, u64, void *, int); \ + extern void perf_tp_event(int, u64, u64, void *, int); \ struct ftrace_raw_##call *entry; \ u64 __addr = 0, __count = 1; \ unsigned long irq_flags; \ @@ -742,7 +742,7 @@ static void ftrace_profile_##call(proto) \ \ { assign; } \ \ - perf_tpcounter_event(event_call->id, __addr, __count, entry,\ + perf_tp_event(event_call->id, __addr, __count, entry,\ __entry_size); \ } while (0); \ \ diff --git a/init/Kconfig b/init/Kconfig index 8e8b76d8a272..cfdf5c322806 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -915,17 +915,17 @@ config AIO by some high performance threaded applications. Disabling this option saves about 7k. -config HAVE_PERF_COUNTERS +config HAVE_PERF_EVENTS bool help See tools/perf/design.txt for details. menu "Performance Counters" -config PERF_COUNTERS +config PERF_EVENTS bool "Kernel Performance Counters" default y if PROFILING - depends on HAVE_PERF_COUNTERS + depends on HAVE_PERF_EVENTS select ANON_INODES help Enable kernel support for performance counter hardware. @@ -947,7 +947,7 @@ config PERF_COUNTERS config EVENT_PROFILE bool "Tracepoint profiling sources" - depends on PERF_COUNTERS && EVENT_TRACING + depends on PERF_EVENTS && EVENT_TRACING default y help Allow the use of tracepoints as software performance counters. diff --git a/kernel/Makefile b/kernel/Makefile index 3d9c7e27e3f9..e26a546eac44 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -96,7 +96,7 @@ obj-$(CONFIG_X86_DS) += trace/ obj-$(CONFIG_RING_BUFFER) += trace/ obj-$(CONFIG_SMP) += sched_cpupri.o obj-$(CONFIG_SLOW_WORK) += slow-work.o -obj-$(CONFIG_PERF_COUNTERS) += perf_counter.o +obj-$(CONFIG_PERF_EVENTS) += perf_event.o ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) # According to Alan Modra , the -fno-omit-frame-pointer is diff --git a/kernel/exit.c b/kernel/exit.c index ae5d8660ddff..e47ee8a06135 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include @@ -154,8 +154,8 @@ static void delayed_put_task_struct(struct rcu_head *rhp) { struct task_struct *tsk = container_of(rhp, struct task_struct, rcu); -#ifdef CONFIG_PERF_COUNTERS - WARN_ON_ONCE(tsk->perf_counter_ctxp); +#ifdef CONFIG_PERF_EVENTS + WARN_ON_ONCE(tsk->perf_event_ctxp); #endif trace_sched_process_free(tsk); put_task_struct(tsk); @@ -981,7 +981,7 @@ NORET_TYPE void do_exit(long code) * Flush inherited counters to the parent - before the parent * gets woken up by child-exit notifications. */ - perf_counter_exit_task(tsk); + perf_event_exit_task(tsk); exit_notify(tsk, group_dead); #ifdef CONFIG_NUMA diff --git a/kernel/fork.c b/kernel/fork.c index bfee931ee3fb..2cebfb23b0b8 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -61,7 +61,7 @@ #include #include #include -#include +#include #include #include @@ -1078,7 +1078,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, /* Perform scheduler related setup. Assign this task to a CPU. */ sched_fork(p, clone_flags); - retval = perf_counter_init_task(p); + retval = perf_event_init_task(p); if (retval) goto bad_fork_cleanup_policy; @@ -1253,7 +1253,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, write_unlock_irq(&tasklist_lock); proc_fork_connector(p); cgroup_post_fork(p); - perf_counter_fork(p); + perf_event_fork(p); return p; bad_fork_free_pid: @@ -1280,7 +1280,7 @@ bad_fork_cleanup_semundo: bad_fork_cleanup_audit: audit_free(p); bad_fork_cleanup_policy: - perf_counter_free_task(p); + perf_event_free_task(p); #ifdef CONFIG_NUMA mpol_put(p->mempolicy); bad_fork_cleanup_cgroup: diff --git a/kernel/perf_counter.c b/kernel/perf_counter.c deleted file mode 100644 index 62de0db8092b..000000000000 --- a/kernel/perf_counter.c +++ /dev/null @@ -1,5000 +0,0 @@ -/* - * Performance counter core code - * - * Copyright (C) 2008 Thomas Gleixner - * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar - * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra - * Copyright © 2009 Paul Mackerras, IBM Corp. - * - * For licensing details see kernel-base/COPYING - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* - * Each CPU has a list of per CPU counters: - */ -DEFINE_PER_CPU(struct perf_cpu_context, perf_cpu_context); - -int perf_max_counters __read_mostly = 1; -static int perf_reserved_percpu __read_mostly; -static int perf_overcommit __read_mostly = 1; - -static atomic_t nr_counters __read_mostly; -static atomic_t nr_mmap_counters __read_mostly; -static atomic_t nr_comm_counters __read_mostly; -static atomic_t nr_task_counters __read_mostly; - -/* - * perf counter paranoia level: - * -1 - not paranoid at all - * 0 - disallow raw tracepoint access for unpriv - * 1 - disallow cpu counters for unpriv - * 2 - disallow kernel profiling for unpriv - */ -int sysctl_perf_counter_paranoid __read_mostly = 1; - -static inline bool perf_paranoid_tracepoint_raw(void) -{ - return sysctl_perf_counter_paranoid > -1; -} - -static inline bool perf_paranoid_cpu(void) -{ - return sysctl_perf_counter_paranoid > 0; -} - -static inline bool perf_paranoid_kernel(void) -{ - return sysctl_perf_counter_paranoid > 1; -} - -int sysctl_perf_counter_mlock __read_mostly = 512; /* 'free' kb per user */ - -/* - * max perf counter sample rate - */ -int sysctl_perf_counter_sample_rate __read_mostly = 100000; - -static atomic64_t perf_counter_id; - -/* - * Lock for (sysadmin-configurable) counter reservations: - */ -static DEFINE_SPINLOCK(perf_resource_lock); - -/* - * Architecture provided APIs - weak aliases: - */ -extern __weak const struct pmu *hw_perf_counter_init(struct perf_counter *counter) -{ - return NULL; -} - -void __weak hw_perf_disable(void) { barrier(); } -void __weak hw_perf_enable(void) { barrier(); } - -void __weak hw_perf_counter_setup(int cpu) { barrier(); } -void __weak hw_perf_counter_setup_online(int cpu) { barrier(); } - -int __weak -hw_perf_group_sched_in(struct perf_counter *group_leader, - struct perf_cpu_context *cpuctx, - struct perf_counter_context *ctx, int cpu) -{ - return 0; -} - -void __weak perf_counter_print_debug(void) { } - -static DEFINE_PER_CPU(int, perf_disable_count); - -void __perf_disable(void) -{ - __get_cpu_var(perf_disable_count)++; -} - -bool __perf_enable(void) -{ - return !--__get_cpu_var(perf_disable_count); -} - -void perf_disable(void) -{ - __perf_disable(); - hw_perf_disable(); -} - -void perf_enable(void) -{ - if (__perf_enable()) - hw_perf_enable(); -} - -static void get_ctx(struct perf_counter_context *ctx) -{ - WARN_ON(!atomic_inc_not_zero(&ctx->refcount)); -} - -static void free_ctx(struct rcu_head *head) -{ - struct perf_counter_context *ctx; - - ctx = container_of(head, struct perf_counter_context, rcu_head); - kfree(ctx); -} - -static void put_ctx(struct perf_counter_context *ctx) -{ - if (atomic_dec_and_test(&ctx->refcount)) { - if (ctx->parent_ctx) - put_ctx(ctx->parent_ctx); - if (ctx->task) - put_task_struct(ctx->task); - call_rcu(&ctx->rcu_head, free_ctx); - } -} - -static void unclone_ctx(struct perf_counter_context *ctx) -{ - if (ctx->parent_ctx) { - put_ctx(ctx->parent_ctx); - ctx->parent_ctx = NULL; - } -} - -/* - * If we inherit counters we want to return the parent counter id - * to userspace. - */ -static u64 primary_counter_id(struct perf_counter *counter) -{ - u64 id = counter->id; - - if (counter->parent) - id = counter->parent->id; - - return id; -} - -/* - * Get the perf_counter_context for a task and lock it. - * This has to cope with with the fact that until it is locked, - * the context could get moved to another task. - */ -static struct perf_counter_context * -perf_lock_task_context(struct task_struct *task, unsigned long *flags) -{ - struct perf_counter_context *ctx; - - rcu_read_lock(); - retry: - ctx = rcu_dereference(task->perf_counter_ctxp); - if (ctx) { - /* - * If this context is a clone of another, it might - * get swapped for another underneath us by - * perf_counter_task_sched_out, though the - * rcu_read_lock() protects us from any context - * getting freed. Lock the context and check if it - * got swapped before we could get the lock, and retry - * if so. If we locked the right context, then it - * can't get swapped on us any more. - */ - spin_lock_irqsave(&ctx->lock, *flags); - if (ctx != rcu_dereference(task->perf_counter_ctxp)) { - spin_unlock_irqrestore(&ctx->lock, *flags); - goto retry; - } - - if (!atomic_inc_not_zero(&ctx->refcount)) { - spin_unlock_irqrestore(&ctx->lock, *flags); - ctx = NULL; - } - } - rcu_read_unlock(); - return ctx; -} - -/* - * Get the context for a task and increment its pin_count so it - * can't get swapped to another task. This also increments its - * reference count so that the context can't get freed. - */ -static struct perf_counter_context *perf_pin_task_context(struct task_struct *task) -{ - struct perf_counter_context *ctx; - unsigned long flags; - - ctx = perf_lock_task_context(task, &flags); - if (ctx) { - ++ctx->pin_count; - spin_unlock_irqrestore(&ctx->lock, flags); - } - return ctx; -} - -static void perf_unpin_context(struct perf_counter_context *ctx) -{ - unsigned long flags; - - spin_lock_irqsave(&ctx->lock, flags); - --ctx->pin_count; - spin_unlock_irqrestore(&ctx->lock, flags); - put_ctx(ctx); -} - -/* - * Add a counter from the lists for its context. - * Must be called with ctx->mutex and ctx->lock held. - */ -static void -list_add_counter(struct perf_counter *counter, struct perf_counter_context *ctx) -{ - struct perf_counter *group_leader = counter->group_leader; - - /* - * Depending on whether it is a standalone or sibling counter, - * add it straight to the context's counter list, or to the group - * leader's sibling list: - */ - if (group_leader == counter) - list_add_tail(&counter->group_entry, &ctx->group_list); - else { - list_add_tail(&counter->group_entry, &group_leader->sibling_list); - group_leader->nr_siblings++; - } - - list_add_rcu(&counter->event_entry, &ctx->event_list); - ctx->nr_counters++; - if (counter->attr.inherit_stat) - ctx->nr_stat++; -} - -/* - * Remove a counter from the lists for its context. - * Must be called with ctx->mutex and ctx->lock held. - */ -static void -list_del_counter(struct perf_counter *counter, struct perf_counter_context *ctx) -{ - struct perf_counter *sibling, *tmp; - - if (list_empty(&counter->group_entry)) - return; - ctx->nr_counters--; - if (counter->attr.inherit_stat) - ctx->nr_stat--; - - list_del_init(&counter->group_entry); - list_del_rcu(&counter->event_entry); - - if (counter->group_leader != counter) - counter->group_leader->nr_siblings--; - - /* - * If this was a group counter with sibling counters then - * upgrade the siblings to singleton counters by adding them - * to the context list directly: - */ - list_for_each_entry_safe(sibling, tmp, &counter->sibling_list, group_entry) { - - list_move_tail(&sibling->group_entry, &ctx->group_list); - sibling->group_leader = sibling; - } -} - -static void -counter_sched_out(struct perf_counter *counter, - struct perf_cpu_context *cpuctx, - struct perf_counter_context *ctx) -{ - if (counter->state != PERF_COUNTER_STATE_ACTIVE) - return; - - counter->state = PERF_COUNTER_STATE_INACTIVE; - if (counter->pending_disable) { - counter->pending_disable = 0; - counter->state = PERF_COUNTER_STATE_OFF; - } - counter->tstamp_stopped = ctx->time; - counter->pmu->disable(counter); - counter->oncpu = -1; - - if (!is_software_counter(counter)) - cpuctx->active_oncpu--; - ctx->nr_active--; - if (counter->attr.exclusive || !cpuctx->active_oncpu) - cpuctx->exclusive = 0; -} - -static void -group_sched_out(struct perf_counter *group_counter, - struct perf_cpu_context *cpuctx, - struct perf_counter_context *ctx) -{ - struct perf_counter *counter; - - if (group_counter->state != PERF_COUNTER_STATE_ACTIVE) - return; - - counter_sched_out(group_counter, cpuctx, ctx); - - /* - * Schedule out siblings (if any): - */ - list_for_each_entry(counter, &group_counter->sibling_list, group_entry) - counter_sched_out(counter, cpuctx, ctx); - - if (group_counter->attr.exclusive) - cpuctx->exclusive = 0; -} - -/* - * Cross CPU call to remove a performance counter - * - * We disable the counter on the hardware level first. After that we - * remove it from the context list. - */ -static void __perf_counter_remove_from_context(void *info) -{ - struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); - struct perf_counter *counter = info; - struct perf_counter_context *ctx = counter->ctx; - - /* - * If this is a task context, we need to check whether it is - * the current task context of this cpu. If not it has been - * scheduled out before the smp call arrived. - */ - if (ctx->task && cpuctx->task_ctx != ctx) - return; - - spin_lock(&ctx->lock); - /* - * Protect the list operation against NMI by disabling the - * counters on a global level. - */ - perf_disable(); - - counter_sched_out(counter, cpuctx, ctx); - - list_del_counter(counter, ctx); - - if (!ctx->task) { - /* - * Allow more per task counters with respect to the - * reservation: - */ - cpuctx->max_pertask = - min(perf_max_counters - ctx->nr_counters, - perf_max_counters - perf_reserved_percpu); - } - - perf_enable(); - spin_unlock(&ctx->lock); -} - - -/* - * Remove the counter from a task's (or a CPU's) list of counters. - * - * Must be called with ctx->mutex held. - * - * CPU counters are removed with a smp call. For task counters we only - * call when the task is on a CPU. - * - * If counter->ctx is a cloned context, callers must make sure that - * every task struct that counter->ctx->task could possibly point to - * remains valid. This is OK when called from perf_release since - * that only calls us on the top-level context, which can't be a clone. - * When called from perf_counter_exit_task, it's OK because the - * context has been detached from its task. - */ -static void perf_counter_remove_from_context(struct perf_counter *counter) -{ - struct perf_counter_context *ctx = counter->ctx; - struct task_struct *task = ctx->task; - - if (!task) { - /* - * Per cpu counters are removed via an smp call and - * the removal is always sucessful. - */ - smp_call_function_single(counter->cpu, - __perf_counter_remove_from_context, - counter, 1); - return; - } - -retry: - task_oncpu_function_call(task, __perf_counter_remove_from_context, - counter); - - spin_lock_irq(&ctx->lock); - /* - * If the context is active we need to retry the smp call. - */ - if (ctx->nr_active && !list_empty(&counter->group_entry)) { - spin_unlock_irq(&ctx->lock); - goto retry; - } - - /* - * The lock prevents that this context is scheduled in so we - * can remove the counter safely, if the call above did not - * succeed. - */ - if (!list_empty(&counter->group_entry)) { - list_del_counter(counter, ctx); - } - spin_unlock_irq(&ctx->lock); -} - -static inline u64 perf_clock(void) -{ - return cpu_clock(smp_processor_id()); -} - -/* - * Update the record of the current time in a context. - */ -static void update_context_time(struct perf_counter_context *ctx) -{ - u64 now = perf_clock(); - - ctx->time += now - ctx->timestamp; - ctx->timestamp = now; -} - -/* - * Update the total_time_enabled and total_time_running fields for a counter. - */ -static void update_counter_times(struct perf_counter *counter) -{ - struct perf_counter_context *ctx = counter->ctx; - u64 run_end; - - if (counter->state < PERF_COUNTER_STATE_INACTIVE || - counter->group_leader->state < PERF_COUNTER_STATE_INACTIVE) - return; - - counter->total_time_enabled = ctx->time - counter->tstamp_enabled; - - if (counter->state == PERF_COUNTER_STATE_INACTIVE) - run_end = counter->tstamp_stopped; - else - run_end = ctx->time; - - counter->total_time_running = run_end - counter->tstamp_running; -} - -/* - * Update total_time_enabled and total_time_running for all counters in a group. - */ -static void update_group_times(struct perf_counter *leader) -{ - struct perf_counter *counter; - - update_counter_times(leader); - list_for_each_entry(counter, &leader->sibling_list, group_entry) - update_counter_times(counter); -} - -/* - * Cross CPU call to disable a performance counter - */ -static void __perf_counter_disable(void *info) -{ - struct perf_counter *counter = info; - struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); - struct perf_counter_context *ctx = counter->ctx; - - /* - * If this is a per-task counter, need to check whether this - * counter's task is the current task on this cpu. - */ - if (ctx->task && cpuctx->task_ctx != ctx) - return; - - spin_lock(&ctx->lock); - - /* - * If the counter is on, turn it off. - * If it is in error state, leave it in error state. - */ - if (counter->state >= PERF_COUNTER_STATE_INACTIVE) { - update_context_time(ctx); - update_group_times(counter); - if (counter == counter->group_leader) - group_sched_out(counter, cpuctx, ctx); - else - counter_sched_out(counter, cpuctx, ctx); - counter->state = PERF_COUNTER_STATE_OFF; - } - - spin_unlock(&ctx->lock); -} - -/* - * Disable a counter. - * - * If counter->ctx is a cloned context, callers must make sure that - * every task struct that counter->ctx->task could possibly point to - * remains valid. This condition is satisifed when called through - * perf_counter_for_each_child or perf_counter_for_each because they - * hold the top-level counter's child_mutex, so any descendant that - * goes to exit will block in sync_child_counter. - * When called from perf_pending_counter it's OK because counter->ctx - * is the current context on this CPU and preemption is disabled, - * hence we can't get into perf_counter_task_sched_out for this context. - */ -static void perf_counter_disable(struct perf_counter *counter) -{ - struct perf_counter_context *ctx = counter->ctx; - struct task_struct *task = ctx->task; - - if (!task) { - /* - * Disable the counter on the cpu that it's on - */ - smp_call_function_single(counter->cpu, __perf_counter_disable, - counter, 1); - return; - } - - retry: - task_oncpu_function_call(task, __perf_counter_disable, counter); - - spin_lock_irq(&ctx->lock); - /* - * If the counter is still active, we need to retry the cross-call. - */ - if (counter->state == PERF_COUNTER_STATE_ACTIVE) { - spin_unlock_irq(&ctx->lock); - goto retry; - } - - /* - * Since we have the lock this context can't be scheduled - * in, so we can change the state safely. - */ - if (counter->state == PERF_COUNTER_STATE_INACTIVE) { - update_group_times(counter); - counter->state = PERF_COUNTER_STATE_OFF; - } - - spin_unlock_irq(&ctx->lock); -} - -static int -counter_sched_in(struct perf_counter *counter, - struct perf_cpu_context *cpuctx, - struct perf_counter_context *ctx, - int cpu) -{ - if (counter->state <= PERF_COUNTER_STATE_OFF) - return 0; - - counter->state = PERF_COUNTER_STATE_ACTIVE; - counter->oncpu = cpu; /* TODO: put 'cpu' into cpuctx->cpu */ - /* - * The new state must be visible before we turn it on in the hardware: - */ - smp_wmb(); - - if (counter->pmu->enable(counter)) { - counter->state = PERF_COUNTER_STATE_INACTIVE; - counter->oncpu = -1; - return -EAGAIN; - } - - counter->tstamp_running += ctx->time - counter->tstamp_stopped; - - if (!is_software_counter(counter)) - cpuctx->active_oncpu++; - ctx->nr_active++; - - if (counter->attr.exclusive) - cpuctx->exclusive = 1; - - return 0; -} - -static int -group_sched_in(struct perf_counter *group_counter, - struct perf_cpu_context *cpuctx, - struct perf_counter_context *ctx, - int cpu) -{ - struct perf_counter *counter, *partial_group; - int ret; - - if (group_counter->state == PERF_COUNTER_STATE_OFF) - return 0; - - ret = hw_perf_group_sched_in(group_counter, cpuctx, ctx, cpu); - if (ret) - return ret < 0 ? ret : 0; - - if (counter_sched_in(group_counter, cpuctx, ctx, cpu)) - return -EAGAIN; - - /* - * Schedule in siblings as one group (if any): - */ - list_for_each_entry(counter, &group_counter->sibling_list, group_entry) { - if (counter_sched_in(counter, cpuctx, ctx, cpu)) { - partial_group = counter; - goto group_error; - } - } - - return 0; - -group_error: - /* - * Groups can be scheduled in as one unit only, so undo any - * partial group before returning: - */ - list_for_each_entry(counter, &group_counter->sibling_list, group_entry) { - if (counter == partial_group) - break; - counter_sched_out(counter, cpuctx, ctx); - } - counter_sched_out(group_counter, cpuctx, ctx); - - return -EAGAIN; -} - -/* - * Return 1 for a group consisting entirely of software counters, - * 0 if the group contains any hardware counters. - */ -static int is_software_only_group(struct perf_counter *leader) -{ - struct perf_counter *counter; - - if (!is_software_counter(leader)) - return 0; - - list_for_each_entry(counter, &leader->sibling_list, group_entry) - if (!is_software_counter(counter)) - return 0; - - return 1; -} - -/* - * Work out whether we can put this counter group on the CPU now. - */ -static int group_can_go_on(struct perf_counter *counter, - struct perf_cpu_context *cpuctx, - int can_add_hw) -{ - /* - * Groups consisting entirely of software counters can always go on. - */ - if (is_software_only_group(counter)) - return 1; - /* - * If an exclusive group is already on, no other hardware - * counters can go on. - */ - if (cpuctx->exclusive) - return 0; - /* - * If this group is exclusive and there are already - * counters on the CPU, it can't go on. - */ - if (counter->attr.exclusive && cpuctx->active_oncpu) - return 0; - /* - * Otherwise, try to add it if all previous groups were able - * to go on. - */ - return can_add_hw; -} - -static void add_counter_to_ctx(struct perf_counter *counter, - struct perf_counter_context *ctx) -{ - list_add_counter(counter, ctx); - counter->tstamp_enabled = ctx->time; - counter->tstamp_running = ctx->time; - counter->tstamp_stopped = ctx->time; -} - -/* - * Cross CPU call to install and enable a performance counter - * - * Must be called with ctx->mutex held - */ -static void __perf_install_in_context(void *info) -{ - struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); - struct perf_counter *counter = info; - struct perf_counter_context *ctx = counter->ctx; - struct perf_counter *leader = counter->group_leader; - int cpu = smp_processor_id(); - int err; - - /* - * If this is a task context, we need to check whether it is - * the current task context of this cpu. If not it has been - * scheduled out before the smp call arrived. - * Or possibly this is the right context but it isn't - * on this cpu because it had no counters. - */ - if (ctx->task && cpuctx->task_ctx != ctx) { - if (cpuctx->task_ctx || ctx->task != current) - return; - cpuctx->task_ctx = ctx; - } - - spin_lock(&ctx->lock); - ctx->is_active = 1; - update_context_time(ctx); - - /* - * Protect the list operation against NMI by disabling the - * counters on a global level. NOP for non NMI based counters. - */ - perf_disable(); - - add_counter_to_ctx(counter, ctx); - - /* - * Don't put the counter on if it is disabled or if - * it is in a group and the group isn't on. - */ - if (counter->state != PERF_COUNTER_STATE_INACTIVE || - (leader != counter && leader->state != PERF_COUNTER_STATE_ACTIVE)) - goto unlock; - - /* - * An exclusive counter can't go on if there are already active - * hardware counters, and no hardware counter can go on if there - * is already an exclusive counter on. - */ - if (!group_can_go_on(counter, cpuctx, 1)) - err = -EEXIST; - else - err = counter_sched_in(counter, cpuctx, ctx, cpu); - - if (err) { - /* - * This counter couldn't go on. If it is in a group - * then we have to pull the whole group off. - * If the counter group is pinned then put it in error state. - */ - if (leader != counter) - group_sched_out(leader, cpuctx, ctx); - if (leader->attr.pinned) { - update_group_times(leader); - leader->state = PERF_COUNTER_STATE_ERROR; - } - } - - if (!err && !ctx->task && cpuctx->max_pertask) - cpuctx->max_pertask--; - - unlock: - perf_enable(); - - spin_unlock(&ctx->lock); -} - -/* - * Attach a performance counter to a context - * - * First we add the counter to the list with the hardware enable bit - * in counter->hw_config cleared. - * - * If the counter is attached to a task which is on a CPU we use a smp - * call to enable it in the task context. The task might have been - * scheduled away, but we check this in the smp call again. - * - * Must be called with ctx->mutex held. - */ -static void -perf_install_in_context(struct perf_counter_context *ctx, - struct perf_counter *counter, - int cpu) -{ - struct task_struct *task = ctx->task; - - if (!task) { - /* - * Per cpu counters are installed via an smp call and - * the install is always sucessful. - */ - smp_call_function_single(cpu, __perf_install_in_context, - counter, 1); - return; - } - -retry: - task_oncpu_function_call(task, __perf_install_in_context, - counter); - - spin_lock_irq(&ctx->lock); - /* - * we need to retry the smp call. - */ - if (ctx->is_active && list_empty(&counter->group_entry)) { - spin_unlock_irq(&ctx->lock); - goto retry; - } - - /* - * The lock prevents that this context is scheduled in so we - * can add the counter safely, if it the call above did not - * succeed. - */ - if (list_empty(&counter->group_entry)) - add_counter_to_ctx(counter, ctx); - spin_unlock_irq(&ctx->lock); -} - -/* - * Put a counter into inactive state and update time fields. - * Enabling the leader of a group effectively enables all - * the group members that aren't explicitly disabled, so we - * have to update their ->tstamp_enabled also. - * Note: this works for group members as well as group leaders - * since the non-leader members' sibling_lists will be empty. - */ -static void __perf_counter_mark_enabled(struct perf_counter *counter, - struct perf_counter_context *ctx) -{ - struct perf_counter *sub; - - counter->state = PERF_COUNTER_STATE_INACTIVE; - counter->tstamp_enabled = ctx->time - counter->total_time_enabled; - list_for_each_entry(sub, &counter->sibling_list, group_entry) - if (sub->state >= PERF_COUNTER_STATE_INACTIVE) - sub->tstamp_enabled = - ctx->time - sub->total_time_enabled; -} - -/* - * Cross CPU call to enable a performance counter - */ -static void __perf_counter_enable(void *info) -{ - struct perf_counter *counter = info; - struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); - struct perf_counter_context *ctx = counter->ctx; - struct perf_counter *leader = counter->group_leader; - int err; - - /* - * If this is a per-task counter, need to check whether this - * counter's task is the current task on this cpu. - */ - if (ctx->task && cpuctx->task_ctx != ctx) { - if (cpuctx->task_ctx || ctx->task != current) - return; - cpuctx->task_ctx = ctx; - } - - spin_lock(&ctx->lock); - ctx->is_active = 1; - update_context_time(ctx); - - if (counter->state >= PERF_COUNTER_STATE_INACTIVE) - goto unlock; - __perf_counter_mark_enabled(counter, ctx); - - /* - * If the counter is in a group and isn't the group leader, - * then don't put it on unless the group is on. - */ - if (leader != counter && leader->state != PERF_COUNTER_STATE_ACTIVE) - goto unlock; - - if (!group_can_go_on(counter, cpuctx, 1)) { - err = -EEXIST; - } else { - perf_disable(); - if (counter == leader) - err = group_sched_in(counter, cpuctx, ctx, - smp_processor_id()); - else - err = counter_sched_in(counter, cpuctx, ctx, - smp_processor_id()); - perf_enable(); - } - - if (err) { - /* - * If this counter can't go on and it's part of a - * group, then the whole group has to come off. - */ - if (leader != counter) - group_sched_out(leader, cpuctx, ctx); - if (leader->attr.pinned) { - update_group_times(leader); - leader->state = PERF_COUNTER_STATE_ERROR; - } - } - - unlock: - spin_unlock(&ctx->lock); -} - -/* - * Enable a counter. - * - * If counter->ctx is a cloned context, callers must make sure that - * every task struct that counter->ctx->task could possibly point to - * remains valid. This condition is satisfied when called through - * perf_counter_for_each_child or perf_counter_for_each as described - * for perf_counter_disable. - */ -static void perf_counter_enable(struct perf_counter *counter) -{ - struct perf_counter_context *ctx = counter->ctx; - struct task_struct *task = ctx->task; - - if (!task) { - /* - * Enable the counter on the cpu that it's on - */ - smp_call_function_single(counter->cpu, __perf_counter_enable, - counter, 1); - return; - } - - spin_lock_irq(&ctx->lock); - if (counter->state >= PERF_COUNTER_STATE_INACTIVE) - goto out; - - /* - * If the counter is in error state, clear that first. - * That way, if we see the counter in error state below, we - * know that it has gone back into error state, as distinct - * from the task having been scheduled away before the - * cross-call arrived. - */ - if (counter->state == PERF_COUNTER_STATE_ERROR) - counter->state = PERF_COUNTER_STATE_OFF; - - retry: - spin_unlock_irq(&ctx->lock); - task_oncpu_function_call(task, __perf_counter_enable, counter); - - spin_lock_irq(&ctx->lock); - - /* - * If the context is active and the counter is still off, - * we need to retry the cross-call. - */ - if (ctx->is_active && counter->state == PERF_COUNTER_STATE_OFF) - goto retry; - - /* - * Since we have the lock this context can't be scheduled - * in, so we can change the state safely. - */ - if (counter->state == PERF_COUNTER_STATE_OFF) - __perf_counter_mark_enabled(counter, ctx); - - out: - spin_unlock_irq(&ctx->lock); -} - -static int perf_counter_refresh(struct perf_counter *counter, int refresh) -{ - /* - * not supported on inherited counters - */ - if (counter->attr.inherit) - return -EINVAL; - - atomic_add(refresh, &counter->event_limit); - perf_counter_enable(counter); - - return 0; -} - -void __perf_counter_sched_out(struct perf_counter_context *ctx, - struct perf_cpu_context *cpuctx) -{ - struct perf_counter *counter; - - spin_lock(&ctx->lock); - ctx->is_active = 0; - if (likely(!ctx->nr_counters)) - goto out; - update_context_time(ctx); - - perf_disable(); - if (ctx->nr_active) { - list_for_each_entry(counter, &ctx->group_list, group_entry) { - if (counter != counter->group_leader) - counter_sched_out(counter, cpuctx, ctx); - else - group_sched_out(counter, cpuctx, ctx); - } - } - perf_enable(); - out: - spin_unlock(&ctx->lock); -} - -/* - * Test whether two contexts are equivalent, i.e. whether they - * have both been cloned from the same version of the same context - * and they both have the same number of enabled counters. - * If the number of enabled counters is the same, then the set - * of enabled counters should be the same, because these are both - * inherited contexts, therefore we can't access individual counters - * in them directly with an fd; we can only enable/disable all - * counters via prctl, or enable/disable all counters in a family - * via ioctl, which will have the same effect on both contexts. - */ -static int context_equiv(struct perf_counter_context *ctx1, - struct perf_counter_context *ctx2) -{ - return ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx - && ctx1->parent_gen == ctx2->parent_gen - && !ctx1->pin_count && !ctx2->pin_count; -} - -static void __perf_counter_read(void *counter); - -static void __perf_counter_sync_stat(struct perf_counter *counter, - struct perf_counter *next_counter) -{ - u64 value; - - if (!counter->attr.inherit_stat) - return; - - /* - * Update the counter value, we cannot use perf_counter_read() - * because we're in the middle of a context switch and have IRQs - * disabled, which upsets smp_call_function_single(), however - * we know the counter must be on the current CPU, therefore we - * don't need to use it. - */ - switch (counter->state) { - case PERF_COUNTER_STATE_ACTIVE: - __perf_counter_read(counter); - break; - - case PERF_COUNTER_STATE_INACTIVE: - update_counter_times(counter); - break; - - default: - break; - } - - /* - * In order to keep per-task stats reliable we need to flip the counter - * values when we flip the contexts. - */ - value = atomic64_read(&next_counter->count); - value = atomic64_xchg(&counter->count, value); - atomic64_set(&next_counter->count, value); - - swap(counter->total_time_enabled, next_counter->total_time_enabled); - swap(counter->total_time_running, next_counter->total_time_running); - - /* - * Since we swizzled the values, update the user visible data too. - */ - perf_counter_update_userpage(counter); - perf_counter_update_userpage(next_counter); -} - -#define list_next_entry(pos, member) \ - list_entry(pos->member.next, typeof(*pos), member) - -static void perf_counter_sync_stat(struct perf_counter_context *ctx, - struct perf_counter_context *next_ctx) -{ - struct perf_counter *counter, *next_counter; - - if (!ctx->nr_stat) - return; - - counter = list_first_entry(&ctx->event_list, - struct perf_counter, event_entry); - - next_counter = list_first_entry(&next_ctx->event_list, - struct perf_counter, event_entry); - - while (&counter->event_entry != &ctx->event_list && - &next_counter->event_entry != &next_ctx->event_list) { - - __perf_counter_sync_stat(counter, next_counter); - - counter = list_next_entry(counter, event_entry); - next_counter = list_next_entry(next_counter, event_entry); - } -} - -/* - * Called from scheduler to remove the counters of the current task, - * with interrupts disabled. - * - * We stop each counter and update the counter value in counter->count. - * - * This does not protect us against NMI, but disable() - * sets the disabled bit in the control field of counter _before_ - * accessing the counter control register. If a NMI hits, then it will - * not restart the counter. - */ -void perf_counter_task_sched_out(struct task_struct *task, - struct task_struct *next, int cpu) -{ - struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); - struct perf_counter_context *ctx = task->perf_counter_ctxp; - struct perf_counter_context *next_ctx; - struct perf_counter_context *parent; - struct pt_regs *regs; - int do_switch = 1; - - regs = task_pt_regs(task); - perf_swcounter_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, regs, 0); - - if (likely(!ctx || !cpuctx->task_ctx)) - return; - - update_context_time(ctx); - - rcu_read_lock(); - parent = rcu_dereference(ctx->parent_ctx); - next_ctx = next->perf_counter_ctxp; - if (parent && next_ctx && - rcu_dereference(next_ctx->parent_ctx) == parent) { - /* - * Looks like the two contexts are clones, so we might be - * able to optimize the context switch. We lock both - * contexts and check that they are clones under the - * lock (including re-checking that neither has been - * uncloned in the meantime). It doesn't matter which - * order we take the locks because no other cpu could - * be trying to lock both of these tasks. - */ - spin_lock(&ctx->lock); - spin_lock_nested(&next_ctx->lock, SINGLE_DEPTH_NESTING); - if (context_equiv(ctx, next_ctx)) { - /* - * XXX do we need a memory barrier of sorts - * wrt to rcu_dereference() of perf_counter_ctxp - */ - task->perf_counter_ctxp = next_ctx; - next->perf_counter_ctxp = ctx; - ctx->task = next; - next_ctx->task = task; - do_switch = 0; - - perf_counter_sync_stat(ctx, next_ctx); - } - spin_unlock(&next_ctx->lock); - spin_unlock(&ctx->lock); - } - rcu_read_unlock(); - - if (do_switch) { - __perf_counter_sched_out(ctx, cpuctx); - cpuctx->task_ctx = NULL; - } -} - -/* - * Called with IRQs disabled - */ -static void __perf_counter_task_sched_out(struct perf_counter_context *ctx) -{ - struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); - - if (!cpuctx->task_ctx) - return; - - if (WARN_ON_ONCE(ctx != cpuctx->task_ctx)) - return; - - __perf_counter_sched_out(ctx, cpuctx); - cpuctx->task_ctx = NULL; -} - -/* - * Called with IRQs disabled - */ -static void perf_counter_cpu_sched_out(struct perf_cpu_context *cpuctx) -{ - __perf_counter_sched_out(&cpuctx->ctx, cpuctx); -} - -static void -__perf_counter_sched_in(struct perf_counter_context *ctx, - struct perf_cpu_context *cpuctx, int cpu) -{ - struct perf_counter *counter; - int can_add_hw = 1; - - spin_lock(&ctx->lock); - ctx->is_active = 1; - if (likely(!ctx->nr_counters)) - goto out; - - ctx->timestamp = perf_clock(); - - perf_disable(); - - /* - * First go through the list and put on any pinned groups - * in order to give them the best chance of going on. - */ - list_for_each_entry(counter, &ctx->group_list, group_entry) { - if (counter->state <= PERF_COUNTER_STATE_OFF || - !counter->attr.pinned) - continue; - if (counter->cpu != -1 && counter->cpu != cpu) - continue; - - if (counter != counter->group_leader) - counter_sched_in(counter, cpuctx, ctx, cpu); - else { - if (group_can_go_on(counter, cpuctx, 1)) - group_sched_in(counter, cpuctx, ctx, cpu); - } - - /* - * If this pinned group hasn't been scheduled, - * put it in error state. - */ - if (counter->state == PERF_COUNTER_STATE_INACTIVE) { - update_group_times(counter); - counter->state = PERF_COUNTER_STATE_ERROR; - } - } - - list_for_each_entry(counter, &ctx->group_list, group_entry) { - /* - * Ignore counters in OFF or ERROR state, and - * ignore pinned counters since we did them already. - */ - if (counter->state <= PERF_COUNTER_STATE_OFF || - counter->attr.pinned) - continue; - - /* - * Listen to the 'cpu' scheduling filter constraint - * of counters: - */ - if (counter->cpu != -1 && counter->cpu != cpu) - continue; - - if (counter != counter->group_leader) { - if (counter_sched_in(counter, cpuctx, ctx, cpu)) - can_add_hw = 0; - } else { - if (group_can_go_on(counter, cpuctx, can_add_hw)) { - if (group_sched_in(counter, cpuctx, ctx, cpu)) - can_add_hw = 0; - } - } - } - perf_enable(); - out: - spin_unlock(&ctx->lock); -} - -/* - * Called from scheduler to add the counters of the current task - * with interrupts disabled. - * - * We restore the counter value and then enable it. - * - * This does not protect us against NMI, but enable() - * sets the enabled bit in the control field of counter _before_ - * accessing the counter control register. If a NMI hits, then it will - * keep the counter running. - */ -void perf_counter_task_sched_in(struct task_struct *task, int cpu) -{ - struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); - struct perf_counter_context *ctx = task->perf_counter_ctxp; - - if (likely(!ctx)) - return; - if (cpuctx->task_ctx == ctx) - return; - __perf_counter_sched_in(ctx, cpuctx, cpu); - cpuctx->task_ctx = ctx; -} - -static void perf_counter_cpu_sched_in(struct perf_cpu_context *cpuctx, int cpu) -{ - struct perf_counter_context *ctx = &cpuctx->ctx; - - __perf_counter_sched_in(ctx, cpuctx, cpu); -} - -#define MAX_INTERRUPTS (~0ULL) - -static void perf_log_throttle(struct perf_counter *counter, int enable); - -static void perf_adjust_period(struct perf_counter *counter, u64 events) -{ - struct hw_perf_counter *hwc = &counter->hw; - u64 period, sample_period; - s64 delta; - - events *= hwc->sample_period; - period = div64_u64(events, counter->attr.sample_freq); - - delta = (s64)(period - hwc->sample_period); - delta = (delta + 7) / 8; /* low pass filter */ - - sample_period = hwc->sample_period + delta; - - if (!sample_period) - sample_period = 1; - - hwc->sample_period = sample_period; -} - -static void perf_ctx_adjust_freq(struct perf_counter_context *ctx) -{ - struct perf_counter *counter; - struct hw_perf_counter *hwc; - u64 interrupts, freq; - - spin_lock(&ctx->lock); - list_for_each_entry(counter, &ctx->group_list, group_entry) { - if (counter->state != PERF_COUNTER_STATE_ACTIVE) - continue; - - hwc = &counter->hw; - - interrupts = hwc->interrupts; - hwc->interrupts = 0; - - /* - * unthrottle counters on the tick - */ - if (interrupts == MAX_INTERRUPTS) { - perf_log_throttle(counter, 1); - counter->pmu->unthrottle(counter); - interrupts = 2*sysctl_perf_counter_sample_rate/HZ; - } - - if (!counter->attr.freq || !counter->attr.sample_freq) - continue; - - /* - * if the specified freq < HZ then we need to skip ticks - */ - if (counter->attr.sample_freq < HZ) { - freq = counter->attr.sample_freq; - - hwc->freq_count += freq; - hwc->freq_interrupts += interrupts; - - if (hwc->freq_count < HZ) - continue; - - interrupts = hwc->freq_interrupts; - hwc->freq_interrupts = 0; - hwc->freq_count -= HZ; - } else - freq = HZ; - - perf_adjust_period(counter, freq * interrupts); - - /* - * In order to avoid being stalled by an (accidental) huge - * sample period, force reset the sample period if we didn't - * get any events in this freq period. - */ - if (!interrupts) { - perf_disable(); - counter->pmu->disable(counter); - atomic64_set(&hwc->period_left, 0); - counter->pmu->enable(counter); - perf_enable(); - } - } - spin_unlock(&ctx->lock); -} - -/* - * Round-robin a context's counters: - */ -static void rotate_ctx(struct perf_counter_context *ctx) -{ - struct perf_counter *counter; - - if (!ctx->nr_counters) - return; - - spin_lock(&ctx->lock); - /* - * Rotate the first entry last (works just fine for group counters too): - */ - perf_disable(); - list_for_each_entry(counter, &ctx->group_list, group_entry) { - list_move_tail(&counter->group_entry, &ctx->group_list); - break; - } - perf_enable(); - - spin_unlock(&ctx->lock); -} - -void perf_counter_task_tick(struct task_struct *curr, int cpu) -{ - struct perf_cpu_context *cpuctx; - struct perf_counter_context *ctx; - - if (!atomic_read(&nr_counters)) - return; - - cpuctx = &per_cpu(perf_cpu_context, cpu); - ctx = curr->perf_counter_ctxp; - - perf_ctx_adjust_freq(&cpuctx->ctx); - if (ctx) - perf_ctx_adjust_freq(ctx); - - perf_counter_cpu_sched_out(cpuctx); - if (ctx) - __perf_counter_task_sched_out(ctx); - - rotate_ctx(&cpuctx->ctx); - if (ctx) - rotate_ctx(ctx); - - perf_counter_cpu_sched_in(cpuctx, cpu); - if (ctx) - perf_counter_task_sched_in(curr, cpu); -} - -/* - * Enable all of a task's counters that have been marked enable-on-exec. - * This expects task == current. - */ -static void perf_counter_enable_on_exec(struct task_struct *task) -{ - struct perf_counter_context *ctx; - struct perf_counter *counter; - unsigned long flags; - int enabled = 0; - - local_irq_save(flags); - ctx = task->perf_counter_ctxp; - if (!ctx || !ctx->nr_counters) - goto out; - - __perf_counter_task_sched_out(ctx); - - spin_lock(&ctx->lock); - - list_for_each_entry(counter, &ctx->group_list, group_entry) { - if (!counter->attr.enable_on_exec) - continue; - counter->attr.enable_on_exec = 0; - if (counter->state >= PERF_COUNTER_STATE_INACTIVE) - continue; - __perf_counter_mark_enabled(counter, ctx); - enabled = 1; - } - - /* - * Unclone this context if we enabled any counter. - */ - if (enabled) - unclone_ctx(ctx); - - spin_unlock(&ctx->lock); - - perf_counter_task_sched_in(task, smp_processor_id()); - out: - local_irq_restore(flags); -} - -/* - * Cross CPU call to read the hardware counter - */ -static void __perf_counter_read(void *info) -{ - struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); - struct perf_counter *counter = info; - struct perf_counter_context *ctx = counter->ctx; - unsigned long flags; - - /* - * If this is a task context, we need to check whether it is - * the current task context of this cpu. If not it has been - * scheduled out before the smp call arrived. In that case - * counter->count would have been updated to a recent sample - * when the counter was scheduled out. - */ - if (ctx->task && cpuctx->task_ctx != ctx) - return; - - local_irq_save(flags); - if (ctx->is_active) - update_context_time(ctx); - counter->pmu->read(counter); - update_counter_times(counter); - local_irq_restore(flags); -} - -static u64 perf_counter_read(struct perf_counter *counter) -{ - /* - * If counter is enabled and currently active on a CPU, update the - * value in the counter structure: - */ - if (counter->state == PERF_COUNTER_STATE_ACTIVE) { - smp_call_function_single(counter->oncpu, - __perf_counter_read, counter, 1); - } else if (counter->state == PERF_COUNTER_STATE_INACTIVE) { - update_counter_times(counter); - } - - return atomic64_read(&counter->count); -} - -/* - * Initialize the perf_counter context in a task_struct: - */ -static void -__perf_counter_init_context(struct perf_counter_context *ctx, - struct task_struct *task) -{ - memset(ctx, 0, sizeof(*ctx)); - spin_lock_init(&ctx->lock); - mutex_init(&ctx->mutex); - INIT_LIST_HEAD(&ctx->group_list); - INIT_LIST_HEAD(&ctx->event_list); - atomic_set(&ctx->refcount, 1); - ctx->task = task; -} - -static struct perf_counter_context *find_get_context(pid_t pid, int cpu) -{ - struct perf_counter_context *ctx; - struct perf_cpu_context *cpuctx; - struct task_struct *task; - unsigned long flags; - int err; - - /* - * If cpu is not a wildcard then this is a percpu counter: - */ - if (cpu != -1) { - /* Must be root to operate on a CPU counter: */ - if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) - return ERR_PTR(-EACCES); - - if (cpu < 0 || cpu > num_possible_cpus()) - return ERR_PTR(-EINVAL); - - /* - * We could be clever and allow to attach a counter to an - * offline CPU and activate it when the CPU comes up, but - * that's for later. - */ - if (!cpu_isset(cpu, cpu_online_map)) - return ERR_PTR(-ENODEV); - - cpuctx = &per_cpu(perf_cpu_context, cpu); - ctx = &cpuctx->ctx; - get_ctx(ctx); - - return ctx; - } - - rcu_read_lock(); - if (!pid) - task = current; - else - task = find_task_by_vpid(pid); - if (task) - get_task_struct(task); - rcu_read_unlock(); - - if (!task) - return ERR_PTR(-ESRCH); - - /* - * Can't attach counters to a dying task. - */ - err = -ESRCH; - if (task->flags & PF_EXITING) - goto errout; - - /* Reuse ptrace permission checks for now. */ - err = -EACCES; - if (!ptrace_may_access(task, PTRACE_MODE_READ)) - goto errout; - - retry: - ctx = perf_lock_task_context(task, &flags); - if (ctx) { - unclone_ctx(ctx); - spin_unlock_irqrestore(&ctx->lock, flags); - } - - if (!ctx) { - ctx = kmalloc(sizeof(struct perf_counter_context), GFP_KERNEL); - err = -ENOMEM; - if (!ctx) - goto errout; - __perf_counter_init_context(ctx, task); - get_ctx(ctx); - if (cmpxchg(&task->perf_counter_ctxp, NULL, ctx)) { - /* - * We raced with some other task; use - * the context they set. - */ - kfree(ctx); - goto retry; - } - get_task_struct(task); - } - - put_task_struct(task); - return ctx; - - errout: - put_task_struct(task); - return ERR_PTR(err); -} - -static void free_counter_rcu(struct rcu_head *head) -{ - struct perf_counter *counter; - - counter = container_of(head, struct perf_counter, rcu_head); - if (counter->ns) - put_pid_ns(counter->ns); - kfree(counter); -} - -static void perf_pending_sync(struct perf_counter *counter); - -static void free_counter(struct perf_counter *counter) -{ - perf_pending_sync(counter); - - if (!counter->parent) { - atomic_dec(&nr_counters); - if (counter->attr.mmap) - atomic_dec(&nr_mmap_counters); - if (counter->attr.comm) - atomic_dec(&nr_comm_counters); - if (counter->attr.task) - atomic_dec(&nr_task_counters); - } - - if (counter->output) { - fput(counter->output->filp); - counter->output = NULL; - } - - if (counter->destroy) - counter->destroy(counter); - - put_ctx(counter->ctx); - call_rcu(&counter->rcu_head, free_counter_rcu); -} - -/* - * Called when the last reference to the file is gone. - */ -static int perf_release(struct inode *inode, struct file *file) -{ - struct perf_counter *counter = file->private_data; - struct perf_counter_context *ctx = counter->ctx; - - file->private_data = NULL; - - WARN_ON_ONCE(ctx->parent_ctx); - mutex_lock(&ctx->mutex); - perf_counter_remove_from_context(counter); - mutex_unlock(&ctx->mutex); - - mutex_lock(&counter->owner->perf_counter_mutex); - list_del_init(&counter->owner_entry); - mutex_unlock(&counter->owner->perf_counter_mutex); - put_task_struct(counter->owner); - - free_counter(counter); - - return 0; -} - -static int perf_counter_read_size(struct perf_counter *counter) -{ - int entry = sizeof(u64); /* value */ - int size = 0; - int nr = 1; - - if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) - size += sizeof(u64); - - if (counter->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) - size += sizeof(u64); - - if (counter->attr.read_format & PERF_FORMAT_ID) - entry += sizeof(u64); - - if (counter->attr.read_format & PERF_FORMAT_GROUP) { - nr += counter->group_leader->nr_siblings; - size += sizeof(u64); - } - - size += entry * nr; - - return size; -} - -static u64 perf_counter_read_value(struct perf_counter *counter) -{ - struct perf_counter *child; - u64 total = 0; - - total += perf_counter_read(counter); - list_for_each_entry(child, &counter->child_list, child_list) - total += perf_counter_read(child); - - return total; -} - -static int perf_counter_read_entry(struct perf_counter *counter, - u64 read_format, char __user *buf) -{ - int n = 0, count = 0; - u64 values[2]; - - values[n++] = perf_counter_read_value(counter); - if (read_format & PERF_FORMAT_ID) - values[n++] = primary_counter_id(counter); - - count = n * sizeof(u64); - - if (copy_to_user(buf, values, count)) - return -EFAULT; - - return count; -} - -static int perf_counter_read_group(struct perf_counter *counter, - u64 read_format, char __user *buf) -{ - struct perf_counter *leader = counter->group_leader, *sub; - int n = 0, size = 0, err = -EFAULT; - u64 values[3]; - - values[n++] = 1 + leader->nr_siblings; - if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { - values[n++] = leader->total_time_enabled + - atomic64_read(&leader->child_total_time_enabled); - } - if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { - values[n++] = leader->total_time_running + - atomic64_read(&leader->child_total_time_running); - } - - size = n * sizeof(u64); - - if (copy_to_user(buf, values, size)) - return -EFAULT; - - err = perf_counter_read_entry(leader, read_format, buf + size); - if (err < 0) - return err; - - size += err; - - list_for_each_entry(sub, &leader->sibling_list, group_entry) { - err = perf_counter_read_entry(sub, read_format, - buf + size); - if (err < 0) - return err; - - size += err; - } - - return size; -} - -static int perf_counter_read_one(struct perf_counter *counter, - u64 read_format, char __user *buf) -{ - u64 values[4]; - int n = 0; - - values[n++] = perf_counter_read_value(counter); - if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { - values[n++] = counter->total_time_enabled + - atomic64_read(&counter->child_total_time_enabled); - } - if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { - values[n++] = counter->total_time_running + - atomic64_read(&counter->child_total_time_running); - } - if (read_format & PERF_FORMAT_ID) - values[n++] = primary_counter_id(counter); - - if (copy_to_user(buf, values, n * sizeof(u64))) - return -EFAULT; - - return n * sizeof(u64); -} - -/* - * Read the performance counter - simple non blocking version for now - */ -static ssize_t -perf_read_hw(struct perf_counter *counter, char __user *buf, size_t count) -{ - u64 read_format = counter->attr.read_format; - int ret; - - /* - * Return end-of-file for a read on a counter that is in - * error state (i.e. because it was pinned but it couldn't be - * scheduled on to the CPU at some point). - */ - if (counter->state == PERF_COUNTER_STATE_ERROR) - return 0; - - if (count < perf_counter_read_size(counter)) - return -ENOSPC; - - WARN_ON_ONCE(counter->ctx->parent_ctx); - mutex_lock(&counter->child_mutex); - if (read_format & PERF_FORMAT_GROUP) - ret = perf_counter_read_group(counter, read_format, buf); - else - ret = perf_counter_read_one(counter, read_format, buf); - mutex_unlock(&counter->child_mutex); - - return ret; -} - -static ssize_t -perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - struct perf_counter *counter = file->private_data; - - return perf_read_hw(counter, buf, count); -} - -static unsigned int perf_poll(struct file *file, poll_table *wait) -{ - struct perf_counter *counter = file->private_data; - struct perf_mmap_data *data; - unsigned int events = POLL_HUP; - - rcu_read_lock(); - data = rcu_dereference(counter->data); - if (data) - events = atomic_xchg(&data->poll, 0); - rcu_read_unlock(); - - poll_wait(file, &counter->waitq, wait); - - return events; -} - -static void perf_counter_reset(struct perf_counter *counter) -{ - (void)perf_counter_read(counter); - atomic64_set(&counter->count, 0); - perf_counter_update_userpage(counter); -} - -/* - * Holding the top-level counter's child_mutex means that any - * descendant process that has inherited this counter will block - * in sync_child_counter if it goes to exit, thus satisfying the - * task existence requirements of perf_counter_enable/disable. - */ -static void perf_counter_for_each_child(struct perf_counter *counter, - void (*func)(struct perf_counter *)) -{ - struct perf_counter *child; - - WARN_ON_ONCE(counter->ctx->parent_ctx); - mutex_lock(&counter->child_mutex); - func(counter); - list_for_each_entry(child, &counter->child_list, child_list) - func(child); - mutex_unlock(&counter->child_mutex); -} - -static void perf_counter_for_each(struct perf_counter *counter, - void (*func)(struct perf_counter *)) -{ - struct perf_counter_context *ctx = counter->ctx; - struct perf_counter *sibling; - - WARN_ON_ONCE(ctx->parent_ctx); - mutex_lock(&ctx->mutex); - counter = counter->group_leader; - - perf_counter_for_each_child(counter, func); - func(counter); - list_for_each_entry(sibling, &counter->sibling_list, group_entry) - perf_counter_for_each_child(counter, func); - mutex_unlock(&ctx->mutex); -} - -static int perf_counter_period(struct perf_counter *counter, u64 __user *arg) -{ - struct perf_counter_context *ctx = counter->ctx; - unsigned long size; - int ret = 0; - u64 value; - - if (!counter->attr.sample_period) - return -EINVAL; - - size = copy_from_user(&value, arg, sizeof(value)); - if (size != sizeof(value)) - return -EFAULT; - - if (!value) - return -EINVAL; - - spin_lock_irq(&ctx->lock); - if (counter->attr.freq) { - if (value > sysctl_perf_counter_sample_rate) { - ret = -EINVAL; - goto unlock; - } - - counter->attr.sample_freq = value; - } else { - counter->attr.sample_period = value; - counter->hw.sample_period = value; - } -unlock: - spin_unlock_irq(&ctx->lock); - - return ret; -} - -int perf_counter_set_output(struct perf_counter *counter, int output_fd); - -static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct perf_counter *counter = file->private_data; - void (*func)(struct perf_counter *); - u32 flags = arg; - - switch (cmd) { - case PERF_COUNTER_IOC_ENABLE: - func = perf_counter_enable; - break; - case PERF_COUNTER_IOC_DISABLE: - func = perf_counter_disable; - break; - case PERF_COUNTER_IOC_RESET: - func = perf_counter_reset; - break; - - case PERF_COUNTER_IOC_REFRESH: - return perf_counter_refresh(counter, arg); - - case PERF_COUNTER_IOC_PERIOD: - return perf_counter_period(counter, (u64 __user *)arg); - - case PERF_COUNTER_IOC_SET_OUTPUT: - return perf_counter_set_output(counter, arg); - - default: - return -ENOTTY; - } - - if (flags & PERF_IOC_FLAG_GROUP) - perf_counter_for_each(counter, func); - else - perf_counter_for_each_child(counter, func); - - return 0; -} - -int perf_counter_task_enable(void) -{ - struct perf_counter *counter; - - mutex_lock(¤t->perf_counter_mutex); - list_for_each_entry(counter, ¤t->perf_counter_list, owner_entry) - perf_counter_for_each_child(counter, perf_counter_enable); - mutex_unlock(¤t->perf_counter_mutex); - - return 0; -} - -int perf_counter_task_disable(void) -{ - struct perf_counter *counter; - - mutex_lock(¤t->perf_counter_mutex); - list_for_each_entry(counter, ¤t->perf_counter_list, owner_entry) - perf_counter_for_each_child(counter, perf_counter_disable); - mutex_unlock(¤t->perf_counter_mutex); - - return 0; -} - -#ifndef PERF_COUNTER_INDEX_OFFSET -# define PERF_COUNTER_INDEX_OFFSET 0 -#endif - -static int perf_counter_index(struct perf_counter *counter) -{ - if (counter->state != PERF_COUNTER_STATE_ACTIVE) - return 0; - - return counter->hw.idx + 1 - PERF_COUNTER_INDEX_OFFSET; -} - -/* - * Callers need to ensure there can be no nesting of this function, otherwise - * the seqlock logic goes bad. We can not serialize this because the arch - * code calls this from NMI context. - */ -void perf_counter_update_userpage(struct perf_counter *counter) -{ - struct perf_counter_mmap_page *userpg; - struct perf_mmap_data *data; - - rcu_read_lock(); - data = rcu_dereference(counter->data); - if (!data) - goto unlock; - - userpg = data->user_page; - - /* - * Disable preemption so as to not let the corresponding user-space - * spin too long if we get preempted. - */ - preempt_disable(); - ++userpg->lock; - barrier(); - userpg->index = perf_counter_index(counter); - userpg->offset = atomic64_read(&counter->count); - if (counter->state == PERF_COUNTER_STATE_ACTIVE) - userpg->offset -= atomic64_read(&counter->hw.prev_count); - - userpg->time_enabled = counter->total_time_enabled + - atomic64_read(&counter->child_total_time_enabled); - - userpg->time_running = counter->total_time_running + - atomic64_read(&counter->child_total_time_running); - - barrier(); - ++userpg->lock; - preempt_enable(); -unlock: - rcu_read_unlock(); -} - -static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct perf_counter *counter = vma->vm_file->private_data; - struct perf_mmap_data *data; - int ret = VM_FAULT_SIGBUS; - - if (vmf->flags & FAULT_FLAG_MKWRITE) { - if (vmf->pgoff == 0) - ret = 0; - return ret; - } - - rcu_read_lock(); - data = rcu_dereference(counter->data); - if (!data) - goto unlock; - - if (vmf->pgoff == 0) { - vmf->page = virt_to_page(data->user_page); - } else { - int nr = vmf->pgoff - 1; - - if ((unsigned)nr > data->nr_pages) - goto unlock; - - if (vmf->flags & FAULT_FLAG_WRITE) - goto unlock; - - vmf->page = virt_to_page(data->data_pages[nr]); - } - - get_page(vmf->page); - vmf->page->mapping = vma->vm_file->f_mapping; - vmf->page->index = vmf->pgoff; - - ret = 0; -unlock: - rcu_read_unlock(); - - return ret; -} - -static int perf_mmap_data_alloc(struct perf_counter *counter, int nr_pages) -{ - struct perf_mmap_data *data; - unsigned long size; - int i; - - WARN_ON(atomic_read(&counter->mmap_count)); - - size = sizeof(struct perf_mmap_data); - size += nr_pages * sizeof(void *); - - data = kzalloc(size, GFP_KERNEL); - if (!data) - goto fail; - - data->user_page = (void *)get_zeroed_page(GFP_KERNEL); - if (!data->user_page) - goto fail_user_page; - - for (i = 0; i < nr_pages; i++) { - data->data_pages[i] = (void *)get_zeroed_page(GFP_KERNEL); - if (!data->data_pages[i]) - goto fail_data_pages; - } - - data->nr_pages = nr_pages; - atomic_set(&data->lock, -1); - - if (counter->attr.watermark) { - data->watermark = min_t(long, PAGE_SIZE * nr_pages, - counter->attr.wakeup_watermark); - } - if (!data->watermark) - data->watermark = max(PAGE_SIZE, PAGE_SIZE * nr_pages / 4); - - rcu_assign_pointer(counter->data, data); - - return 0; - -fail_data_pages: - for (i--; i >= 0; i--) - free_page((unsigned long)data->data_pages[i]); - - free_page((unsigned long)data->user_page); - -fail_user_page: - kfree(data); - -fail: - return -ENOMEM; -} - -static void perf_mmap_free_page(unsigned long addr) -{ - struct page *page = virt_to_page((void *)addr); - - page->mapping = NULL; - __free_page(page); -} - -static void __perf_mmap_data_free(struct rcu_head *rcu_head) -{ - struct perf_mmap_data *data; - int i; - - data = container_of(rcu_head, struct perf_mmap_data, rcu_head); - - perf_mmap_free_page((unsigned long)data->user_page); - for (i = 0; i < data->nr_pages; i++) - perf_mmap_free_page((unsigned long)data->data_pages[i]); - - kfree(data); -} - -static void perf_mmap_data_free(struct perf_counter *counter) -{ - struct perf_mmap_data *data = counter->data; - - WARN_ON(atomic_read(&counter->mmap_count)); - - rcu_assign_pointer(counter->data, NULL); - call_rcu(&data->rcu_head, __perf_mmap_data_free); -} - -static void perf_mmap_open(struct vm_area_struct *vma) -{ - struct perf_counter *counter = vma->vm_file->private_data; - - atomic_inc(&counter->mmap_count); -} - -static void perf_mmap_close(struct vm_area_struct *vma) -{ - struct perf_counter *counter = vma->vm_file->private_data; - - WARN_ON_ONCE(counter->ctx->parent_ctx); - if (atomic_dec_and_mutex_lock(&counter->mmap_count, &counter->mmap_mutex)) { - struct user_struct *user = current_user(); - - atomic_long_sub(counter->data->nr_pages + 1, &user->locked_vm); - vma->vm_mm->locked_vm -= counter->data->nr_locked; - perf_mmap_data_free(counter); - mutex_unlock(&counter->mmap_mutex); - } -} - -static struct vm_operations_struct perf_mmap_vmops = { - .open = perf_mmap_open, - .close = perf_mmap_close, - .fault = perf_mmap_fault, - .page_mkwrite = perf_mmap_fault, -}; - -static int perf_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct perf_counter *counter = file->private_data; - unsigned long user_locked, user_lock_limit; - struct user_struct *user = current_user(); - unsigned long locked, lock_limit; - unsigned long vma_size; - unsigned long nr_pages; - long user_extra, extra; - int ret = 0; - - if (!(vma->vm_flags & VM_SHARED)) - return -EINVAL; - - vma_size = vma->vm_end - vma->vm_start; - nr_pages = (vma_size / PAGE_SIZE) - 1; - - /* - * If we have data pages ensure they're a power-of-two number, so we - * can do bitmasks instead of modulo. - */ - if (nr_pages != 0 && !is_power_of_2(nr_pages)) - return -EINVAL; - - if (vma_size != PAGE_SIZE * (1 + nr_pages)) - return -EINVAL; - - if (vma->vm_pgoff != 0) - return -EINVAL; - - WARN_ON_ONCE(counter->ctx->parent_ctx); - mutex_lock(&counter->mmap_mutex); - if (counter->output) { - ret = -EINVAL; - goto unlock; - } - - if (atomic_inc_not_zero(&counter->mmap_count)) { - if (nr_pages != counter->data->nr_pages) - ret = -EINVAL; - goto unlock; - } - - user_extra = nr_pages + 1; - user_lock_limit = sysctl_perf_counter_mlock >> (PAGE_SHIFT - 10); - - /* - * Increase the limit linearly with more CPUs: - */ - user_lock_limit *= num_online_cpus(); - - user_locked = atomic_long_read(&user->locked_vm) + user_extra; - - extra = 0; - if (user_locked > user_lock_limit) - extra = user_locked - user_lock_limit; - - lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur; - lock_limit >>= PAGE_SHIFT; - locked = vma->vm_mm->locked_vm + extra; - - if ((locked > lock_limit) && perf_paranoid_tracepoint_raw() && - !capable(CAP_IPC_LOCK)) { - ret = -EPERM; - goto unlock; - } - - WARN_ON(counter->data); - ret = perf_mmap_data_alloc(counter, nr_pages); - if (ret) - goto unlock; - - atomic_set(&counter->mmap_count, 1); - atomic_long_add(user_extra, &user->locked_vm); - vma->vm_mm->locked_vm += extra; - counter->data->nr_locked = extra; - if (vma->vm_flags & VM_WRITE) - counter->data->writable = 1; - -unlock: - mutex_unlock(&counter->mmap_mutex); - - vma->vm_flags |= VM_RESERVED; - vma->vm_ops = &perf_mmap_vmops; - - return ret; -} - -static int perf_fasync(int fd, struct file *filp, int on) -{ - struct inode *inode = filp->f_path.dentry->d_inode; - struct perf_counter *counter = filp->private_data; - int retval; - - mutex_lock(&inode->i_mutex); - retval = fasync_helper(fd, filp, on, &counter->fasync); - mutex_unlock(&inode->i_mutex); - - if (retval < 0) - return retval; - - return 0; -} - -static const struct file_operations perf_fops = { - .release = perf_release, - .read = perf_read, - .poll = perf_poll, - .unlocked_ioctl = perf_ioctl, - .compat_ioctl = perf_ioctl, - .mmap = perf_mmap, - .fasync = perf_fasync, -}; - -/* - * Perf counter wakeup - * - * If there's data, ensure we set the poll() state and publish everything - * to user-space before waking everybody up. - */ - -void perf_counter_wakeup(struct perf_counter *counter) -{ - wake_up_all(&counter->waitq); - - if (counter->pending_kill) { - kill_fasync(&counter->fasync, SIGIO, counter->pending_kill); - counter->pending_kill = 0; - } -} - -/* - * Pending wakeups - * - * Handle the case where we need to wakeup up from NMI (or rq->lock) context. - * - * The NMI bit means we cannot possibly take locks. Therefore, maintain a - * single linked list and use cmpxchg() to add entries lockless. - */ - -static void perf_pending_counter(struct perf_pending_entry *entry) -{ - struct perf_counter *counter = container_of(entry, - struct perf_counter, pending); - - if (counter->pending_disable) { - counter->pending_disable = 0; - __perf_counter_disable(counter); - } - - if (counter->pending_wakeup) { - counter->pending_wakeup = 0; - perf_counter_wakeup(counter); - } -} - -#define PENDING_TAIL ((struct perf_pending_entry *)-1UL) - -static DEFINE_PER_CPU(struct perf_pending_entry *, perf_pending_head) = { - PENDING_TAIL, -}; - -static void perf_pending_queue(struct perf_pending_entry *entry, - void (*func)(struct perf_pending_entry *)) -{ - struct perf_pending_entry **head; - - if (cmpxchg(&entry->next, NULL, PENDING_TAIL) != NULL) - return; - - entry->func = func; - - head = &get_cpu_var(perf_pending_head); - - do { - entry->next = *head; - } while (cmpxchg(head, entry->next, entry) != entry->next); - - set_perf_counter_pending(); - - put_cpu_var(perf_pending_head); -} - -static int __perf_pending_run(void) -{ - struct perf_pending_entry *list; - int nr = 0; - - list = xchg(&__get_cpu_var(perf_pending_head), PENDING_TAIL); - while (list != PENDING_TAIL) { - void (*func)(struct perf_pending_entry *); - struct perf_pending_entry *entry = list; - - list = list->next; - - func = entry->func; - entry->next = NULL; - /* - * Ensure we observe the unqueue before we issue the wakeup, - * so that we won't be waiting forever. - * -- see perf_not_pending(). - */ - smp_wmb(); - - func(entry); - nr++; - } - - return nr; -} - -static inline int perf_not_pending(struct perf_counter *counter) -{ - /* - * If we flush on whatever cpu we run, there is a chance we don't - * need to wait. - */ - get_cpu(); - __perf_pending_run(); - put_cpu(); - - /* - * Ensure we see the proper queue state before going to sleep - * so that we do not miss the wakeup. -- see perf_pending_handle() - */ - smp_rmb(); - return counter->pending.next == NULL; -} - -static void perf_pending_sync(struct perf_counter *counter) -{ - wait_event(counter->waitq, perf_not_pending(counter)); -} - -void perf_counter_do_pending(void) -{ - __perf_pending_run(); -} - -/* - * Callchain support -- arch specific - */ - -__weak struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) -{ - return NULL; -} - -/* - * Output - */ -static bool perf_output_space(struct perf_mmap_data *data, unsigned long tail, - unsigned long offset, unsigned long head) -{ - unsigned long mask; - - if (!data->writable) - return true; - - mask = (data->nr_pages << PAGE_SHIFT) - 1; - - offset = (offset - tail) & mask; - head = (head - tail) & mask; - - if ((int)(head - offset) < 0) - return false; - - return true; -} - -static void perf_output_wakeup(struct perf_output_handle *handle) -{ - atomic_set(&handle->data->poll, POLL_IN); - - if (handle->nmi) { - handle->counter->pending_wakeup = 1; - perf_pending_queue(&handle->counter->pending, - perf_pending_counter); - } else - perf_counter_wakeup(handle->counter); -} - -/* - * Curious locking construct. - * - * We need to ensure a later event doesn't publish a head when a former - * event isn't done writing. However since we need to deal with NMIs we - * cannot fully serialize things. - * - * What we do is serialize between CPUs so we only have to deal with NMI - * nesting on a single CPU. - * - * We only publish the head (and generate a wakeup) when the outer-most - * event completes. - */ -static void perf_output_lock(struct perf_output_handle *handle) -{ - struct perf_mmap_data *data = handle->data; - int cpu; - - handle->locked = 0; - - local_irq_save(handle->flags); - cpu = smp_processor_id(); - - if (in_nmi() && atomic_read(&data->lock) == cpu) - return; - - while (atomic_cmpxchg(&data->lock, -1, cpu) != -1) - cpu_relax(); - - handle->locked = 1; -} - -static void perf_output_unlock(struct perf_output_handle *handle) -{ - struct perf_mmap_data *data = handle->data; - unsigned long head; - int cpu; - - data->done_head = data->head; - - if (!handle->locked) - goto out; - -again: - /* - * The xchg implies a full barrier that ensures all writes are done - * before we publish the new head, matched by a rmb() in userspace when - * reading this position. - */ - while ((head = atomic_long_xchg(&data->done_head, 0))) - data->user_page->data_head = head; - - /* - * NMI can happen here, which means we can miss a done_head update. - */ - - cpu = atomic_xchg(&data->lock, -1); - WARN_ON_ONCE(cpu != smp_processor_id()); - - /* - * Therefore we have to validate we did not indeed do so. - */ - if (unlikely(atomic_long_read(&data->done_head))) { - /* - * Since we had it locked, we can lock it again. - */ - while (atomic_cmpxchg(&data->lock, -1, cpu) != -1) - cpu_relax(); - - goto again; - } - - if (atomic_xchg(&data->wakeup, 0)) - perf_output_wakeup(handle); -out: - local_irq_restore(handle->flags); -} - -void perf_output_copy(struct perf_output_handle *handle, - const void *buf, unsigned int len) -{ - unsigned int pages_mask; - unsigned int offset; - unsigned int size; - void **pages; - - offset = handle->offset; - pages_mask = handle->data->nr_pages - 1; - pages = handle->data->data_pages; - - do { - unsigned int page_offset; - int nr; - - nr = (offset >> PAGE_SHIFT) & pages_mask; - page_offset = offset & (PAGE_SIZE - 1); - size = min_t(unsigned int, PAGE_SIZE - page_offset, len); - - memcpy(pages[nr] + page_offset, buf, size); - - len -= size; - buf += size; - offset += size; - } while (len); - - handle->offset = offset; - - /* - * Check we didn't copy past our reservation window, taking the - * possible unsigned int wrap into account. - */ - WARN_ON_ONCE(((long)(handle->head - handle->offset)) < 0); -} - -int perf_output_begin(struct perf_output_handle *handle, - struct perf_counter *counter, unsigned int size, - int nmi, int sample) -{ - struct perf_counter *output_counter; - struct perf_mmap_data *data; - unsigned long tail, offset, head; - int have_lost; - struct { - struct perf_event_header header; - u64 id; - u64 lost; - } lost_event; - - rcu_read_lock(); - /* - * For inherited counters we send all the output towards the parent. - */ - if (counter->parent) - counter = counter->parent; - - output_counter = rcu_dereference(counter->output); - if (output_counter) - counter = output_counter; - - data = rcu_dereference(counter->data); - if (!data) - goto out; - - handle->data = data; - handle->counter = counter; - handle->nmi = nmi; - handle->sample = sample; - - if (!data->nr_pages) - goto fail; - - have_lost = atomic_read(&data->lost); - if (have_lost) - size += sizeof(lost_event); - - perf_output_lock(handle); - - do { - /* - * Userspace could choose to issue a mb() before updating the - * tail pointer. So that all reads will be completed before the - * write is issued. - */ - tail = ACCESS_ONCE(data->user_page->data_tail); - smp_rmb(); - offset = head = atomic_long_read(&data->head); - head += size; - if (unlikely(!perf_output_space(data, tail, offset, head))) - goto fail; - } while (atomic_long_cmpxchg(&data->head, offset, head) != offset); - - handle->offset = offset; - handle->head = head; - - if (head - tail > data->watermark) - atomic_set(&data->wakeup, 1); - - if (have_lost) { - lost_event.header.type = PERF_EVENT_LOST; - lost_event.header.misc = 0; - lost_event.header.size = sizeof(lost_event); - lost_event.id = counter->id; - lost_event.lost = atomic_xchg(&data->lost, 0); - - perf_output_put(handle, lost_event); - } - - return 0; - -fail: - atomic_inc(&data->lost); - perf_output_unlock(handle); -out: - rcu_read_unlock(); - - return -ENOSPC; -} - -void perf_output_end(struct perf_output_handle *handle) -{ - struct perf_counter *counter = handle->counter; - struct perf_mmap_data *data = handle->data; - - int wakeup_events = counter->attr.wakeup_events; - - if (handle->sample && wakeup_events) { - int events = atomic_inc_return(&data->events); - if (events >= wakeup_events) { - atomic_sub(wakeup_events, &data->events); - atomic_set(&data->wakeup, 1); - } - } - - perf_output_unlock(handle); - rcu_read_unlock(); -} - -static u32 perf_counter_pid(struct perf_counter *counter, struct task_struct *p) -{ - /* - * only top level counters have the pid namespace they were created in - */ - if (counter->parent) - counter = counter->parent; - - return task_tgid_nr_ns(p, counter->ns); -} - -static u32 perf_counter_tid(struct perf_counter *counter, struct task_struct *p) -{ - /* - * only top level counters have the pid namespace they were created in - */ - if (counter->parent) - counter = counter->parent; - - return task_pid_nr_ns(p, counter->ns); -} - -static void perf_output_read_one(struct perf_output_handle *handle, - struct perf_counter *counter) -{ - u64 read_format = counter->attr.read_format; - u64 values[4]; - int n = 0; - - values[n++] = atomic64_read(&counter->count); - if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { - values[n++] = counter->total_time_enabled + - atomic64_read(&counter->child_total_time_enabled); - } - if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { - values[n++] = counter->total_time_running + - atomic64_read(&counter->child_total_time_running); - } - if (read_format & PERF_FORMAT_ID) - values[n++] = primary_counter_id(counter); - - perf_output_copy(handle, values, n * sizeof(u64)); -} - -/* - * XXX PERF_FORMAT_GROUP vs inherited counters seems difficult. - */ -static void perf_output_read_group(struct perf_output_handle *handle, - struct perf_counter *counter) -{ - struct perf_counter *leader = counter->group_leader, *sub; - u64 read_format = counter->attr.read_format; - u64 values[5]; - int n = 0; - - values[n++] = 1 + leader->nr_siblings; - - if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) - values[n++] = leader->total_time_enabled; - - if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) - values[n++] = leader->total_time_running; - - if (leader != counter) - leader->pmu->read(leader); - - values[n++] = atomic64_read(&leader->count); - if (read_format & PERF_FORMAT_ID) - values[n++] = primary_counter_id(leader); - - perf_output_copy(handle, values, n * sizeof(u64)); - - list_for_each_entry(sub, &leader->sibling_list, group_entry) { - n = 0; - - if (sub != counter) - sub->pmu->read(sub); - - values[n++] = atomic64_read(&sub->count); - if (read_format & PERF_FORMAT_ID) - values[n++] = primary_counter_id(sub); - - perf_output_copy(handle, values, n * sizeof(u64)); - } -} - -static void perf_output_read(struct perf_output_handle *handle, - struct perf_counter *counter) -{ - if (counter->attr.read_format & PERF_FORMAT_GROUP) - perf_output_read_group(handle, counter); - else - perf_output_read_one(handle, counter); -} - -void perf_output_sample(struct perf_output_handle *handle, - struct perf_event_header *header, - struct perf_sample_data *data, - struct perf_counter *counter) -{ - u64 sample_type = data->type; - - perf_output_put(handle, *header); - - if (sample_type & PERF_SAMPLE_IP) - perf_output_put(handle, data->ip); - - if (sample_type & PERF_SAMPLE_TID) - perf_output_put(handle, data->tid_entry); - - if (sample_type & PERF_SAMPLE_TIME) - perf_output_put(handle, data->time); - - if (sample_type & PERF_SAMPLE_ADDR) - perf_output_put(handle, data->addr); - - if (sample_type & PERF_SAMPLE_ID) - perf_output_put(handle, data->id); - - if (sample_type & PERF_SAMPLE_STREAM_ID) - perf_output_put(handle, data->stream_id); - - if (sample_type & PERF_SAMPLE_CPU) - perf_output_put(handle, data->cpu_entry); - - if (sample_type & PERF_SAMPLE_PERIOD) - perf_output_put(handle, data->period); - - if (sample_type & PERF_SAMPLE_READ) - perf_output_read(handle, counter); - - if (sample_type & PERF_SAMPLE_CALLCHAIN) { - if (data->callchain) { - int size = 1; - - if (data->callchain) - size += data->callchain->nr; - - size *= sizeof(u64); - - perf_output_copy(handle, data->callchain, size); - } else { - u64 nr = 0; - perf_output_put(handle, nr); - } - } - - if (sample_type & PERF_SAMPLE_RAW) { - if (data->raw) { - perf_output_put(handle, data->raw->size); - perf_output_copy(handle, data->raw->data, - data->raw->size); - } else { - struct { - u32 size; - u32 data; - } raw = { - .size = sizeof(u32), - .data = 0, - }; - perf_output_put(handle, raw); - } - } -} - -void perf_prepare_sample(struct perf_event_header *header, - struct perf_sample_data *data, - struct perf_counter *counter, - struct pt_regs *regs) -{ - u64 sample_type = counter->attr.sample_type; - - data->type = sample_type; - - header->type = PERF_EVENT_SAMPLE; - header->size = sizeof(*header); - - header->misc = 0; - header->misc |= perf_misc_flags(regs); - - if (sample_type & PERF_SAMPLE_IP) { - data->ip = perf_instruction_pointer(regs); - - header->size += sizeof(data->ip); - } - - if (sample_type & PERF_SAMPLE_TID) { - /* namespace issues */ - data->tid_entry.pid = perf_counter_pid(counter, current); - data->tid_entry.tid = perf_counter_tid(counter, current); - - header->size += sizeof(data->tid_entry); - } - - if (sample_type & PERF_SAMPLE_TIME) { - data->time = perf_clock(); - - header->size += sizeof(data->time); - } - - if (sample_type & PERF_SAMPLE_ADDR) - header->size += sizeof(data->addr); - - if (sample_type & PERF_SAMPLE_ID) { - data->id = primary_counter_id(counter); - - header->size += sizeof(data->id); - } - - if (sample_type & PERF_SAMPLE_STREAM_ID) { - data->stream_id = counter->id; - - header->size += sizeof(data->stream_id); - } - - if (sample_type & PERF_SAMPLE_CPU) { - data->cpu_entry.cpu = raw_smp_processor_id(); - data->cpu_entry.reserved = 0; - - header->size += sizeof(data->cpu_entry); - } - - if (sample_type & PERF_SAMPLE_PERIOD) - header->size += sizeof(data->period); - - if (sample_type & PERF_SAMPLE_READ) - header->size += perf_counter_read_size(counter); - - if (sample_type & PERF_SAMPLE_CALLCHAIN) { - int size = 1; - - data->callchain = perf_callchain(regs); - - if (data->callchain) - size += data->callchain->nr; - - header->size += size * sizeof(u64); - } - - if (sample_type & PERF_SAMPLE_RAW) { - int size = sizeof(u32); - - if (data->raw) - size += data->raw->size; - else - size += sizeof(u32); - - WARN_ON_ONCE(size & (sizeof(u64)-1)); - header->size += size; - } -} - -static void perf_counter_output(struct perf_counter *counter, int nmi, - struct perf_sample_data *data, - struct pt_regs *regs) -{ - struct perf_output_handle handle; - struct perf_event_header header; - - perf_prepare_sample(&header, data, counter, regs); - - if (perf_output_begin(&handle, counter, header.size, nmi, 1)) - return; - - perf_output_sample(&handle, &header, data, counter); - - perf_output_end(&handle); -} - -/* - * read event - */ - -struct perf_read_event { - struct perf_event_header header; - - u32 pid; - u32 tid; -}; - -static void -perf_counter_read_event(struct perf_counter *counter, - struct task_struct *task) -{ - struct perf_output_handle handle; - struct perf_read_event read_event = { - .header = { - .type = PERF_EVENT_READ, - .misc = 0, - .size = sizeof(read_event) + perf_counter_read_size(counter), - }, - .pid = perf_counter_pid(counter, task), - .tid = perf_counter_tid(counter, task), - }; - int ret; - - ret = perf_output_begin(&handle, counter, read_event.header.size, 0, 0); - if (ret) - return; - - perf_output_put(&handle, read_event); - perf_output_read(&handle, counter); - - perf_output_end(&handle); -} - -/* - * task tracking -- fork/exit - * - * enabled by: attr.comm | attr.mmap | attr.task - */ - -struct perf_task_event { - struct task_struct *task; - struct perf_counter_context *task_ctx; - - struct { - struct perf_event_header header; - - u32 pid; - u32 ppid; - u32 tid; - u32 ptid; - u64 time; - } event; -}; - -static void perf_counter_task_output(struct perf_counter *counter, - struct perf_task_event *task_event) -{ - struct perf_output_handle handle; - int size; - struct task_struct *task = task_event->task; - int ret; - - size = task_event->event.header.size; - ret = perf_output_begin(&handle, counter, size, 0, 0); - - if (ret) - return; - - task_event->event.pid = perf_counter_pid(counter, task); - task_event->event.ppid = perf_counter_pid(counter, current); - - task_event->event.tid = perf_counter_tid(counter, task); - task_event->event.ptid = perf_counter_tid(counter, current); - - task_event->event.time = perf_clock(); - - perf_output_put(&handle, task_event->event); - - perf_output_end(&handle); -} - -static int perf_counter_task_match(struct perf_counter *counter) -{ - if (counter->attr.comm || counter->attr.mmap || counter->attr.task) - return 1; - - return 0; -} - -static void perf_counter_task_ctx(struct perf_counter_context *ctx, - struct perf_task_event *task_event) -{ - struct perf_counter *counter; - - if (system_state != SYSTEM_RUNNING || list_empty(&ctx->event_list)) - return; - - rcu_read_lock(); - list_for_each_entry_rcu(counter, &ctx->event_list, event_entry) { - if (perf_counter_task_match(counter)) - perf_counter_task_output(counter, task_event); - } - rcu_read_unlock(); -} - -static void perf_counter_task_event(struct perf_task_event *task_event) -{ - struct perf_cpu_context *cpuctx; - struct perf_counter_context *ctx = task_event->task_ctx; - - cpuctx = &get_cpu_var(perf_cpu_context); - perf_counter_task_ctx(&cpuctx->ctx, task_event); - put_cpu_var(perf_cpu_context); - - rcu_read_lock(); - if (!ctx) - ctx = rcu_dereference(task_event->task->perf_counter_ctxp); - if (ctx) - perf_counter_task_ctx(ctx, task_event); - rcu_read_unlock(); -} - -static void perf_counter_task(struct task_struct *task, - struct perf_counter_context *task_ctx, - int new) -{ - struct perf_task_event task_event; - - if (!atomic_read(&nr_comm_counters) && - !atomic_read(&nr_mmap_counters) && - !atomic_read(&nr_task_counters)) - return; - - task_event = (struct perf_task_event){ - .task = task, - .task_ctx = task_ctx, - .event = { - .header = { - .type = new ? PERF_EVENT_FORK : PERF_EVENT_EXIT, - .misc = 0, - .size = sizeof(task_event.event), - }, - /* .pid */ - /* .ppid */ - /* .tid */ - /* .ptid */ - }, - }; - - perf_counter_task_event(&task_event); -} - -void perf_counter_fork(struct task_struct *task) -{ - perf_counter_task(task, NULL, 1); -} - -/* - * comm tracking - */ - -struct perf_comm_event { - struct task_struct *task; - char *comm; - int comm_size; - - struct { - struct perf_event_header header; - - u32 pid; - u32 tid; - } event; -}; - -static void perf_counter_comm_output(struct perf_counter *counter, - struct perf_comm_event *comm_event) -{ - struct perf_output_handle handle; - int size = comm_event->event.header.size; - int ret = perf_output_begin(&handle, counter, size, 0, 0); - - if (ret) - return; - - comm_event->event.pid = perf_counter_pid(counter, comm_event->task); - comm_event->event.tid = perf_counter_tid(counter, comm_event->task); - - perf_output_put(&handle, comm_event->event); - perf_output_copy(&handle, comm_event->comm, - comm_event->comm_size); - perf_output_end(&handle); -} - -static int perf_counter_comm_match(struct perf_counter *counter) -{ - if (counter->attr.comm) - return 1; - - return 0; -} - -static void perf_counter_comm_ctx(struct perf_counter_context *ctx, - struct perf_comm_event *comm_event) -{ - struct perf_counter *counter; - - if (system_state != SYSTEM_RUNNING || list_empty(&ctx->event_list)) - return; - - rcu_read_lock(); - list_for_each_entry_rcu(counter, &ctx->event_list, event_entry) { - if (perf_counter_comm_match(counter)) - perf_counter_comm_output(counter, comm_event); - } - rcu_read_unlock(); -} - -static void perf_counter_comm_event(struct perf_comm_event *comm_event) -{ - struct perf_cpu_context *cpuctx; - struct perf_counter_context *ctx; - unsigned int size; - char comm[TASK_COMM_LEN]; - - memset(comm, 0, sizeof(comm)); - strncpy(comm, comm_event->task->comm, sizeof(comm)); - size = ALIGN(strlen(comm)+1, sizeof(u64)); - - comm_event->comm = comm; - comm_event->comm_size = size; - - comm_event->event.header.size = sizeof(comm_event->event) + size; - - cpuctx = &get_cpu_var(perf_cpu_context); - perf_counter_comm_ctx(&cpuctx->ctx, comm_event); - put_cpu_var(perf_cpu_context); - - rcu_read_lock(); - /* - * doesn't really matter which of the child contexts the - * events ends up in. - */ - ctx = rcu_dereference(current->perf_counter_ctxp); - if (ctx) - perf_counter_comm_ctx(ctx, comm_event); - rcu_read_unlock(); -} - -void perf_counter_comm(struct task_struct *task) -{ - struct perf_comm_event comm_event; - - if (task->perf_counter_ctxp) - perf_counter_enable_on_exec(task); - - if (!atomic_read(&nr_comm_counters)) - return; - - comm_event = (struct perf_comm_event){ - .task = task, - /* .comm */ - /* .comm_size */ - .event = { - .header = { - .type = PERF_EVENT_COMM, - .misc = 0, - /* .size */ - }, - /* .pid */ - /* .tid */ - }, - }; - - perf_counter_comm_event(&comm_event); -} - -/* - * mmap tracking - */ - -struct perf_mmap_event { - struct vm_area_struct *vma; - - const char *file_name; - int file_size; - - struct { - struct perf_event_header header; - - u32 pid; - u32 tid; - u64 start; - u64 len; - u64 pgoff; - } event; -}; - -static void perf_counter_mmap_output(struct perf_counter *counter, - struct perf_mmap_event *mmap_event) -{ - struct perf_output_handle handle; - int size = mmap_event->event.header.size; - int ret = perf_output_begin(&handle, counter, size, 0, 0); - - if (ret) - return; - - mmap_event->event.pid = perf_counter_pid(counter, current); - mmap_event->event.tid = perf_counter_tid(counter, current); - - perf_output_put(&handle, mmap_event->event); - perf_output_copy(&handle, mmap_event->file_name, - mmap_event->file_size); - perf_output_end(&handle); -} - -static int perf_counter_mmap_match(struct perf_counter *counter, - struct perf_mmap_event *mmap_event) -{ - if (counter->attr.mmap) - return 1; - - return 0; -} - -static void perf_counter_mmap_ctx(struct perf_counter_context *ctx, - struct perf_mmap_event *mmap_event) -{ - struct perf_counter *counter; - - if (system_state != SYSTEM_RUNNING || list_empty(&ctx->event_list)) - return; - - rcu_read_lock(); - list_for_each_entry_rcu(counter, &ctx->event_list, event_entry) { - if (perf_counter_mmap_match(counter, mmap_event)) - perf_counter_mmap_output(counter, mmap_event); - } - rcu_read_unlock(); -} - -static void perf_counter_mmap_event(struct perf_mmap_event *mmap_event) -{ - struct perf_cpu_context *cpuctx; - struct perf_counter_context *ctx; - struct vm_area_struct *vma = mmap_event->vma; - struct file *file = vma->vm_file; - unsigned int size; - char tmp[16]; - char *buf = NULL; - const char *name; - - memset(tmp, 0, sizeof(tmp)); - - if (file) { - /* - * d_path works from the end of the buffer backwards, so we - * need to add enough zero bytes after the string to handle - * the 64bit alignment we do later. - */ - buf = kzalloc(PATH_MAX + sizeof(u64), GFP_KERNEL); - if (!buf) { - name = strncpy(tmp, "//enomem", sizeof(tmp)); - goto got_name; - } - name = d_path(&file->f_path, buf, PATH_MAX); - if (IS_ERR(name)) { - name = strncpy(tmp, "//toolong", sizeof(tmp)); - goto got_name; - } - } else { - if (arch_vma_name(mmap_event->vma)) { - name = strncpy(tmp, arch_vma_name(mmap_event->vma), - sizeof(tmp)); - goto got_name; - } - - if (!vma->vm_mm) { - name = strncpy(tmp, "[vdso]", sizeof(tmp)); - goto got_name; - } - - name = strncpy(tmp, "//anon", sizeof(tmp)); - goto got_name; - } - -got_name: - size = ALIGN(strlen(name)+1, sizeof(u64)); - - mmap_event->file_name = name; - mmap_event->file_size = size; - - mmap_event->event.header.size = sizeof(mmap_event->event) + size; - - cpuctx = &get_cpu_var(perf_cpu_context); - perf_counter_mmap_ctx(&cpuctx->ctx, mmap_event); - put_cpu_var(perf_cpu_context); - - rcu_read_lock(); - /* - * doesn't really matter which of the child contexts the - * events ends up in. - */ - ctx = rcu_dereference(current->perf_counter_ctxp); - if (ctx) - perf_counter_mmap_ctx(ctx, mmap_event); - rcu_read_unlock(); - - kfree(buf); -} - -void __perf_counter_mmap(struct vm_area_struct *vma) -{ - struct perf_mmap_event mmap_event; - - if (!atomic_read(&nr_mmap_counters)) - return; - - mmap_event = (struct perf_mmap_event){ - .vma = vma, - /* .file_name */ - /* .file_size */ - .event = { - .header = { - .type = PERF_EVENT_MMAP, - .misc = 0, - /* .size */ - }, - /* .pid */ - /* .tid */ - .start = vma->vm_start, - .len = vma->vm_end - vma->vm_start, - .pgoff = vma->vm_pgoff, - }, - }; - - perf_counter_mmap_event(&mmap_event); -} - -/* - * IRQ throttle logging - */ - -static void perf_log_throttle(struct perf_counter *counter, int enable) -{ - struct perf_output_handle handle; - int ret; - - struct { - struct perf_event_header header; - u64 time; - u64 id; - u64 stream_id; - } throttle_event = { - .header = { - .type = PERF_EVENT_THROTTLE, - .misc = 0, - .size = sizeof(throttle_event), - }, - .time = perf_clock(), - .id = primary_counter_id(counter), - .stream_id = counter->id, - }; - - if (enable) - throttle_event.header.type = PERF_EVENT_UNTHROTTLE; - - ret = perf_output_begin(&handle, counter, sizeof(throttle_event), 1, 0); - if (ret) - return; - - perf_output_put(&handle, throttle_event); - perf_output_end(&handle); -} - -/* - * Generic counter overflow handling, sampling. - */ - -static int __perf_counter_overflow(struct perf_counter *counter, int nmi, - int throttle, struct perf_sample_data *data, - struct pt_regs *regs) -{ - int events = atomic_read(&counter->event_limit); - struct hw_perf_counter *hwc = &counter->hw; - int ret = 0; - - throttle = (throttle && counter->pmu->unthrottle != NULL); - - if (!throttle) { - hwc->interrupts++; - } else { - if (hwc->interrupts != MAX_INTERRUPTS) { - hwc->interrupts++; - if (HZ * hwc->interrupts > - (u64)sysctl_perf_counter_sample_rate) { - hwc->interrupts = MAX_INTERRUPTS; - perf_log_throttle(counter, 0); - ret = 1; - } - } else { - /* - * Keep re-disabling counters even though on the previous - * pass we disabled it - just in case we raced with a - * sched-in and the counter got enabled again: - */ - ret = 1; - } - } - - if (counter->attr.freq) { - u64 now = perf_clock(); - s64 delta = now - hwc->freq_stamp; - - hwc->freq_stamp = now; - - if (delta > 0 && delta < TICK_NSEC) - perf_adjust_period(counter, NSEC_PER_SEC / (int)delta); - } - - /* - * XXX event_limit might not quite work as expected on inherited - * counters - */ - - counter->pending_kill = POLL_IN; - if (events && atomic_dec_and_test(&counter->event_limit)) { - ret = 1; - counter->pending_kill = POLL_HUP; - if (nmi) { - counter->pending_disable = 1; - perf_pending_queue(&counter->pending, - perf_pending_counter); - } else - perf_counter_disable(counter); - } - - perf_counter_output(counter, nmi, data, regs); - return ret; -} - -int perf_counter_overflow(struct perf_counter *counter, int nmi, - struct perf_sample_data *data, - struct pt_regs *regs) -{ - return __perf_counter_overflow(counter, nmi, 1, data, regs); -} - -/* - * Generic software counter infrastructure - */ - -/* - * We directly increment counter->count and keep a second value in - * counter->hw.period_left to count intervals. This period counter - * is kept in the range [-sample_period, 0] so that we can use the - * sign as trigger. - */ - -static u64 perf_swcounter_set_period(struct perf_counter *counter) -{ - struct hw_perf_counter *hwc = &counter->hw; - u64 period = hwc->last_period; - u64 nr, offset; - s64 old, val; - - hwc->last_period = hwc->sample_period; - -again: - old = val = atomic64_read(&hwc->period_left); - if (val < 0) - return 0; - - nr = div64_u64(period + val, period); - offset = nr * period; - val -= offset; - if (atomic64_cmpxchg(&hwc->period_left, old, val) != old) - goto again; - - return nr; -} - -static void perf_swcounter_overflow(struct perf_counter *counter, - int nmi, struct perf_sample_data *data, - struct pt_regs *regs) -{ - struct hw_perf_counter *hwc = &counter->hw; - int throttle = 0; - u64 overflow; - - data->period = counter->hw.last_period; - overflow = perf_swcounter_set_period(counter); - - if (hwc->interrupts == MAX_INTERRUPTS) - return; - - for (; overflow; overflow--) { - if (__perf_counter_overflow(counter, nmi, throttle, - data, regs)) { - /* - * We inhibit the overflow from happening when - * hwc->interrupts == MAX_INTERRUPTS. - */ - break; - } - throttle = 1; - } -} - -static void perf_swcounter_unthrottle(struct perf_counter *counter) -{ - /* - * Nothing to do, we already reset hwc->interrupts. - */ -} - -static void perf_swcounter_add(struct perf_counter *counter, u64 nr, - int nmi, struct perf_sample_data *data, - struct pt_regs *regs) -{ - struct hw_perf_counter *hwc = &counter->hw; - - atomic64_add(nr, &counter->count); - - if (!hwc->sample_period) - return; - - if (!regs) - return; - - if (!atomic64_add_negative(nr, &hwc->period_left)) - perf_swcounter_overflow(counter, nmi, data, regs); -} - -static int perf_swcounter_is_counting(struct perf_counter *counter) -{ - /* - * The counter is active, we're good! - */ - if (counter->state == PERF_COUNTER_STATE_ACTIVE) - return 1; - - /* - * The counter is off/error, not counting. - */ - if (counter->state != PERF_COUNTER_STATE_INACTIVE) - return 0; - - /* - * The counter is inactive, if the context is active - * we're part of a group that didn't make it on the 'pmu', - * not counting. - */ - if (counter->ctx->is_active) - return 0; - - /* - * We're inactive and the context is too, this means the - * task is scheduled out, we're counting events that happen - * to us, like migration events. - */ - return 1; -} - -static int perf_swcounter_match(struct perf_counter *counter, - enum perf_type_id type, - u32 event_id, struct pt_regs *regs) -{ - if (!perf_swcounter_is_counting(counter)) - return 0; - - if (counter->attr.type != type) - return 0; - if (counter->attr.config != event_id) - return 0; - - if (regs) { - if (counter->attr.exclude_user && user_mode(regs)) - return 0; - - if (counter->attr.exclude_kernel && !user_mode(regs)) - return 0; - } - - return 1; -} - -static void perf_swcounter_ctx_event(struct perf_counter_context *ctx, - enum perf_type_id type, - u32 event_id, u64 nr, int nmi, - struct perf_sample_data *data, - struct pt_regs *regs) -{ - struct perf_counter *counter; - - if (system_state != SYSTEM_RUNNING || list_empty(&ctx->event_list)) - return; - - rcu_read_lock(); - list_for_each_entry_rcu(counter, &ctx->event_list, event_entry) { - if (perf_swcounter_match(counter, type, event_id, regs)) - perf_swcounter_add(counter, nr, nmi, data, regs); - } - rcu_read_unlock(); -} - -static int *perf_swcounter_recursion_context(struct perf_cpu_context *cpuctx) -{ - if (in_nmi()) - return &cpuctx->recursion[3]; - - if (in_irq()) - return &cpuctx->recursion[2]; - - if (in_softirq()) - return &cpuctx->recursion[1]; - - return &cpuctx->recursion[0]; -} - -static void do_perf_swcounter_event(enum perf_type_id type, u32 event, - u64 nr, int nmi, - struct perf_sample_data *data, - struct pt_regs *regs) -{ - struct perf_cpu_context *cpuctx = &get_cpu_var(perf_cpu_context); - int *recursion = perf_swcounter_recursion_context(cpuctx); - struct perf_counter_context *ctx; - - if (*recursion) - goto out; - - (*recursion)++; - barrier(); - - perf_swcounter_ctx_event(&cpuctx->ctx, type, event, - nr, nmi, data, regs); - rcu_read_lock(); - /* - * doesn't really matter which of the child contexts the - * events ends up in. - */ - ctx = rcu_dereference(current->perf_counter_ctxp); - if (ctx) - perf_swcounter_ctx_event(ctx, type, event, nr, nmi, data, regs); - rcu_read_unlock(); - - barrier(); - (*recursion)--; - -out: - put_cpu_var(perf_cpu_context); -} - -void __perf_swcounter_event(u32 event, u64 nr, int nmi, - struct pt_regs *regs, u64 addr) -{ - struct perf_sample_data data = { - .addr = addr, - }; - - do_perf_swcounter_event(PERF_TYPE_SOFTWARE, event, nr, nmi, - &data, regs); -} - -static void perf_swcounter_read(struct perf_counter *counter) -{ -} - -static int perf_swcounter_enable(struct perf_counter *counter) -{ - struct hw_perf_counter *hwc = &counter->hw; - - if (hwc->sample_period) { - hwc->last_period = hwc->sample_period; - perf_swcounter_set_period(counter); - } - return 0; -} - -static void perf_swcounter_disable(struct perf_counter *counter) -{ -} - -static const struct pmu perf_ops_generic = { - .enable = perf_swcounter_enable, - .disable = perf_swcounter_disable, - .read = perf_swcounter_read, - .unthrottle = perf_swcounter_unthrottle, -}; - -/* - * hrtimer based swcounter callback - */ - -static enum hrtimer_restart perf_swcounter_hrtimer(struct hrtimer *hrtimer) -{ - enum hrtimer_restart ret = HRTIMER_RESTART; - struct perf_sample_data data; - struct pt_regs *regs; - struct perf_counter *counter; - u64 period; - - counter = container_of(hrtimer, struct perf_counter, hw.hrtimer); - counter->pmu->read(counter); - - data.addr = 0; - regs = get_irq_regs(); - /* - * In case we exclude kernel IPs or are somehow not in interrupt - * context, provide the next best thing, the user IP. - */ - if ((counter->attr.exclude_kernel || !regs) && - !counter->attr.exclude_user) - regs = task_pt_regs(current); - - if (regs) { - if (perf_counter_overflow(counter, 0, &data, regs)) - ret = HRTIMER_NORESTART; - } - - period = max_t(u64, 10000, counter->hw.sample_period); - hrtimer_forward_now(hrtimer, ns_to_ktime(period)); - - return ret; -} - -/* - * Software counter: cpu wall time clock - */ - -static void cpu_clock_perf_counter_update(struct perf_counter *counter) -{ - int cpu = raw_smp_processor_id(); - s64 prev; - u64 now; - - now = cpu_clock(cpu); - prev = atomic64_read(&counter->hw.prev_count); - atomic64_set(&counter->hw.prev_count, now); - atomic64_add(now - prev, &counter->count); -} - -static int cpu_clock_perf_counter_enable(struct perf_counter *counter) -{ - struct hw_perf_counter *hwc = &counter->hw; - int cpu = raw_smp_processor_id(); - - atomic64_set(&hwc->prev_count, cpu_clock(cpu)); - hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hwc->hrtimer.function = perf_swcounter_hrtimer; - if (hwc->sample_period) { - u64 period = max_t(u64, 10000, hwc->sample_period); - __hrtimer_start_range_ns(&hwc->hrtimer, - ns_to_ktime(period), 0, - HRTIMER_MODE_REL, 0); - } - - return 0; -} - -static void cpu_clock_perf_counter_disable(struct perf_counter *counter) -{ - if (counter->hw.sample_period) - hrtimer_cancel(&counter->hw.hrtimer); - cpu_clock_perf_counter_update(counter); -} - -static void cpu_clock_perf_counter_read(struct perf_counter *counter) -{ - cpu_clock_perf_counter_update(counter); -} - -static const struct pmu perf_ops_cpu_clock = { - .enable = cpu_clock_perf_counter_enable, - .disable = cpu_clock_perf_counter_disable, - .read = cpu_clock_perf_counter_read, -}; - -/* - * Software counter: task time clock - */ - -static void task_clock_perf_counter_update(struct perf_counter *counter, u64 now) -{ - u64 prev; - s64 delta; - - prev = atomic64_xchg(&counter->hw.prev_count, now); - delta = now - prev; - atomic64_add(delta, &counter->count); -} - -static int task_clock_perf_counter_enable(struct perf_counter *counter) -{ - struct hw_perf_counter *hwc = &counter->hw; - u64 now; - - now = counter->ctx->time; - - atomic64_set(&hwc->prev_count, now); - hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hwc->hrtimer.function = perf_swcounter_hrtimer; - if (hwc->sample_period) { - u64 period = max_t(u64, 10000, hwc->sample_period); - __hrtimer_start_range_ns(&hwc->hrtimer, - ns_to_ktime(period), 0, - HRTIMER_MODE_REL, 0); - } - - return 0; -} - -static void task_clock_perf_counter_disable(struct perf_counter *counter) -{ - if (counter->hw.sample_period) - hrtimer_cancel(&counter->hw.hrtimer); - task_clock_perf_counter_update(counter, counter->ctx->time); - -} - -static void task_clock_perf_counter_read(struct perf_counter *counter) -{ - u64 time; - - if (!in_nmi()) { - update_context_time(counter->ctx); - time = counter->ctx->time; - } else { - u64 now = perf_clock(); - u64 delta = now - counter->ctx->timestamp; - time = counter->ctx->time + delta; - } - - task_clock_perf_counter_update(counter, time); -} - -static const struct pmu perf_ops_task_clock = { - .enable = task_clock_perf_counter_enable, - .disable = task_clock_perf_counter_disable, - .read = task_clock_perf_counter_read, -}; - -#ifdef CONFIG_EVENT_PROFILE -void perf_tpcounter_event(int event_id, u64 addr, u64 count, void *record, - int entry_size) -{ - struct perf_raw_record raw = { - .size = entry_size, - .data = record, - }; - - struct perf_sample_data data = { - .addr = addr, - .raw = &raw, - }; - - struct pt_regs *regs = get_irq_regs(); - - if (!regs) - regs = task_pt_regs(current); - - do_perf_swcounter_event(PERF_TYPE_TRACEPOINT, event_id, count, 1, - &data, regs); -} -EXPORT_SYMBOL_GPL(perf_tpcounter_event); - -extern int ftrace_profile_enable(int); -extern void ftrace_profile_disable(int); - -static void tp_perf_counter_destroy(struct perf_counter *counter) -{ - ftrace_profile_disable(counter->attr.config); -} - -static const struct pmu *tp_perf_counter_init(struct perf_counter *counter) -{ - /* - * Raw tracepoint data is a severe data leak, only allow root to - * have these. - */ - if ((counter->attr.sample_type & PERF_SAMPLE_RAW) && - perf_paranoid_tracepoint_raw() && - !capable(CAP_SYS_ADMIN)) - return ERR_PTR(-EPERM); - - if (ftrace_profile_enable(counter->attr.config)) - return NULL; - - counter->destroy = tp_perf_counter_destroy; - - return &perf_ops_generic; -} -#else -static const struct pmu *tp_perf_counter_init(struct perf_counter *counter) -{ - return NULL; -} -#endif - -atomic_t perf_swcounter_enabled[PERF_COUNT_SW_MAX]; - -static void sw_perf_counter_destroy(struct perf_counter *counter) -{ - u64 event_id = counter->attr.config; - - WARN_ON(counter->parent); - - atomic_dec(&perf_swcounter_enabled[event_id]); -} - -static const struct pmu *sw_perf_counter_init(struct perf_counter *counter) -{ - const struct pmu *pmu = NULL; - u64 event_id = counter->attr.config; - - /* - * Software counters (currently) can't in general distinguish - * between user, kernel and hypervisor events. - * However, context switches and cpu migrations are considered - * to be kernel events, and page faults are never hypervisor - * events. - */ - switch (event_id) { - case PERF_COUNT_SW_CPU_CLOCK: - pmu = &perf_ops_cpu_clock; - - break; - case PERF_COUNT_SW_TASK_CLOCK: - /* - * If the user instantiates this as a per-cpu counter, - * use the cpu_clock counter instead. - */ - if (counter->ctx->task) - pmu = &perf_ops_task_clock; - else - pmu = &perf_ops_cpu_clock; - - break; - case PERF_COUNT_SW_PAGE_FAULTS: - case PERF_COUNT_SW_PAGE_FAULTS_MIN: - case PERF_COUNT_SW_PAGE_FAULTS_MAJ: - case PERF_COUNT_SW_CONTEXT_SWITCHES: - case PERF_COUNT_SW_CPU_MIGRATIONS: - if (!counter->parent) { - atomic_inc(&perf_swcounter_enabled[event_id]); - counter->destroy = sw_perf_counter_destroy; - } - pmu = &perf_ops_generic; - break; - } - - return pmu; -} - -/* - * Allocate and initialize a counter structure - */ -static struct perf_counter * -perf_counter_alloc(struct perf_counter_attr *attr, - int cpu, - struct perf_counter_context *ctx, - struct perf_counter *group_leader, - struct perf_counter *parent_counter, - gfp_t gfpflags) -{ - const struct pmu *pmu; - struct perf_counter *counter; - struct hw_perf_counter *hwc; - long err; - - counter = kzalloc(sizeof(*counter), gfpflags); - if (!counter) - return ERR_PTR(-ENOMEM); - - /* - * Single counters are their own group leaders, with an - * empty sibling list: - */ - if (!group_leader) - group_leader = counter; - - mutex_init(&counter->child_mutex); - INIT_LIST_HEAD(&counter->child_list); - - INIT_LIST_HEAD(&counter->group_entry); - INIT_LIST_HEAD(&counter->event_entry); - INIT_LIST_HEAD(&counter->sibling_list); - init_waitqueue_head(&counter->waitq); - - mutex_init(&counter->mmap_mutex); - - counter->cpu = cpu; - counter->attr = *attr; - counter->group_leader = group_leader; - counter->pmu = NULL; - counter->ctx = ctx; - counter->oncpu = -1; - - counter->parent = parent_counter; - - counter->ns = get_pid_ns(current->nsproxy->pid_ns); - counter->id = atomic64_inc_return(&perf_counter_id); - - counter->state = PERF_COUNTER_STATE_INACTIVE; - - if (attr->disabled) - counter->state = PERF_COUNTER_STATE_OFF; - - pmu = NULL; - - hwc = &counter->hw; - hwc->sample_period = attr->sample_period; - if (attr->freq && attr->sample_freq) - hwc->sample_period = 1; - hwc->last_period = hwc->sample_period; - - atomic64_set(&hwc->period_left, hwc->sample_period); - - /* - * we currently do not support PERF_FORMAT_GROUP on inherited counters - */ - if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP)) - goto done; - - switch (attr->type) { - case PERF_TYPE_RAW: - case PERF_TYPE_HARDWARE: - case PERF_TYPE_HW_CACHE: - pmu = hw_perf_counter_init(counter); - break; - - case PERF_TYPE_SOFTWARE: - pmu = sw_perf_counter_init(counter); - break; - - case PERF_TYPE_TRACEPOINT: - pmu = tp_perf_counter_init(counter); - break; - - default: - break; - } -done: - err = 0; - if (!pmu) - err = -EINVAL; - else if (IS_ERR(pmu)) - err = PTR_ERR(pmu); - - if (err) { - if (counter->ns) - put_pid_ns(counter->ns); - kfree(counter); - return ERR_PTR(err); - } - - counter->pmu = pmu; - - if (!counter->parent) { - atomic_inc(&nr_counters); - if (counter->attr.mmap) - atomic_inc(&nr_mmap_counters); - if (counter->attr.comm) - atomic_inc(&nr_comm_counters); - if (counter->attr.task) - atomic_inc(&nr_task_counters); - } - - return counter; -} - -static int perf_copy_attr(struct perf_counter_attr __user *uattr, - struct perf_counter_attr *attr) -{ - u32 size; - int ret; - - if (!access_ok(VERIFY_WRITE, uattr, PERF_ATTR_SIZE_VER0)) - return -EFAULT; - - /* - * zero the full structure, so that a short copy will be nice. - */ - memset(attr, 0, sizeof(*attr)); - - ret = get_user(size, &uattr->size); - if (ret) - return ret; - - if (size > PAGE_SIZE) /* silly large */ - goto err_size; - - if (!size) /* abi compat */ - size = PERF_ATTR_SIZE_VER0; - - if (size < PERF_ATTR_SIZE_VER0) - goto err_size; - - /* - * If we're handed a bigger struct than we know of, - * ensure all the unknown bits are 0 - i.e. new - * user-space does not rely on any kernel feature - * extensions we dont know about yet. - */ - if (size > sizeof(*attr)) { - unsigned char __user *addr; - unsigned char __user *end; - unsigned char val; - - addr = (void __user *)uattr + sizeof(*attr); - end = (void __user *)uattr + size; - - for (; addr < end; addr++) { - ret = get_user(val, addr); - if (ret) - return ret; - if (val) - goto err_size; - } - size = sizeof(*attr); - } - - ret = copy_from_user(attr, uattr, size); - if (ret) - return -EFAULT; - - /* - * If the type exists, the corresponding creation will verify - * the attr->config. - */ - if (attr->type >= PERF_TYPE_MAX) - return -EINVAL; - - if (attr->__reserved_1 || attr->__reserved_2 || attr->__reserved_3) - return -EINVAL; - - if (attr->sample_type & ~(PERF_SAMPLE_MAX-1)) - return -EINVAL; - - if (attr->read_format & ~(PERF_FORMAT_MAX-1)) - return -EINVAL; - -out: - return ret; - -err_size: - put_user(sizeof(*attr), &uattr->size); - ret = -E2BIG; - goto out; -} - -int perf_counter_set_output(struct perf_counter *counter, int output_fd) -{ - struct perf_counter *output_counter = NULL; - struct file *output_file = NULL; - struct perf_counter *old_output; - int fput_needed = 0; - int ret = -EINVAL; - - if (!output_fd) - goto set; - - output_file = fget_light(output_fd, &fput_needed); - if (!output_file) - return -EBADF; - - if (output_file->f_op != &perf_fops) - goto out; - - output_counter = output_file->private_data; - - /* Don't chain output fds */ - if (output_counter->output) - goto out; - - /* Don't set an output fd when we already have an output channel */ - if (counter->data) - goto out; - - atomic_long_inc(&output_file->f_count); - -set: - mutex_lock(&counter->mmap_mutex); - old_output = counter->output; - rcu_assign_pointer(counter->output, output_counter); - mutex_unlock(&counter->mmap_mutex); - - if (old_output) { - /* - * we need to make sure no existing perf_output_*() - * is still referencing this counter. - */ - synchronize_rcu(); - fput(old_output->filp); - } - - ret = 0; -out: - fput_light(output_file, fput_needed); - return ret; -} - -/** - * sys_perf_counter_open - open a performance counter, associate it to a task/cpu - * - * @attr_uptr: event type attributes for monitoring/sampling - * @pid: target pid - * @cpu: target cpu - * @group_fd: group leader counter fd - */ -SYSCALL_DEFINE5(perf_counter_open, - struct perf_counter_attr __user *, attr_uptr, - pid_t, pid, int, cpu, int, group_fd, unsigned long, flags) -{ - struct perf_counter *counter, *group_leader; - struct perf_counter_attr attr; - struct perf_counter_context *ctx; - struct file *counter_file = NULL; - struct file *group_file = NULL; - int fput_needed = 0; - int fput_needed2 = 0; - int err; - - /* for future expandability... */ - if (flags & ~(PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT)) - return -EINVAL; - - err = perf_copy_attr(attr_uptr, &attr); - if (err) - return err; - - if (!attr.exclude_kernel) { - if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) - return -EACCES; - } - - if (attr.freq) { - if (attr.sample_freq > sysctl_perf_counter_sample_rate) - return -EINVAL; - } - - /* - * Get the target context (task or percpu): - */ - ctx = find_get_context(pid, cpu); - if (IS_ERR(ctx)) - return PTR_ERR(ctx); - - /* - * Look up the group leader (we will attach this counter to it): - */ - group_leader = NULL; - if (group_fd != -1 && !(flags & PERF_FLAG_FD_NO_GROUP)) { - err = -EINVAL; - group_file = fget_light(group_fd, &fput_needed); - if (!group_file) - goto err_put_context; - if (group_file->f_op != &perf_fops) - goto err_put_context; - - group_leader = group_file->private_data; - /* - * Do not allow a recursive hierarchy (this new sibling - * becoming part of another group-sibling): - */ - if (group_leader->group_leader != group_leader) - goto err_put_context; - /* - * Do not allow to attach to a group in a different - * task or CPU context: - */ - if (group_leader->ctx != ctx) - goto err_put_context; - /* - * Only a group leader can be exclusive or pinned - */ - if (attr.exclusive || attr.pinned) - goto err_put_context; - } - - counter = perf_counter_alloc(&attr, cpu, ctx, group_leader, - NULL, GFP_KERNEL); - err = PTR_ERR(counter); - if (IS_ERR(counter)) - goto err_put_context; - - err = anon_inode_getfd("[perf_counter]", &perf_fops, counter, 0); - if (err < 0) - goto err_free_put_context; - - counter_file = fget_light(err, &fput_needed2); - if (!counter_file) - goto err_free_put_context; - - if (flags & PERF_FLAG_FD_OUTPUT) { - err = perf_counter_set_output(counter, group_fd); - if (err) - goto err_fput_free_put_context; - } - - counter->filp = counter_file; - WARN_ON_ONCE(ctx->parent_ctx); - mutex_lock(&ctx->mutex); - perf_install_in_context(ctx, counter, cpu); - ++ctx->generation; - mutex_unlock(&ctx->mutex); - - counter->owner = current; - get_task_struct(current); - mutex_lock(¤t->perf_counter_mutex); - list_add_tail(&counter->owner_entry, ¤t->perf_counter_list); - mutex_unlock(¤t->perf_counter_mutex); - -err_fput_free_put_context: - fput_light(counter_file, fput_needed2); - -err_free_put_context: - if (err < 0) - kfree(counter); - -err_put_context: - if (err < 0) - put_ctx(ctx); - - fput_light(group_file, fput_needed); - - return err; -} - -/* - * inherit a counter from parent task to child task: - */ -static struct perf_counter * -inherit_counter(struct perf_counter *parent_counter, - struct task_struct *parent, - struct perf_counter_context *parent_ctx, - struct task_struct *child, - struct perf_counter *group_leader, - struct perf_counter_context *child_ctx) -{ - struct perf_counter *child_counter; - - /* - * Instead of creating recursive hierarchies of counters, - * we link inherited counters back to the original parent, - * which has a filp for sure, which we use as the reference - * count: - */ - if (parent_counter->parent) - parent_counter = parent_counter->parent; - - child_counter = perf_counter_alloc(&parent_counter->attr, - parent_counter->cpu, child_ctx, - group_leader, parent_counter, - GFP_KERNEL); - if (IS_ERR(child_counter)) - return child_counter; - get_ctx(child_ctx); - - /* - * Make the child state follow the state of the parent counter, - * not its attr.disabled bit. We hold the parent's mutex, - * so we won't race with perf_counter_{en, dis}able_family. - */ - if (parent_counter->state >= PERF_COUNTER_STATE_INACTIVE) - child_counter->state = PERF_COUNTER_STATE_INACTIVE; - else - child_counter->state = PERF_COUNTER_STATE_OFF; - - if (parent_counter->attr.freq) - child_counter->hw.sample_period = parent_counter->hw.sample_period; - - /* - * Link it up in the child's context: - */ - add_counter_to_ctx(child_counter, child_ctx); - - /* - * Get a reference to the parent filp - we will fput it - * when the child counter exits. This is safe to do because - * we are in the parent and we know that the filp still - * exists and has a nonzero count: - */ - atomic_long_inc(&parent_counter->filp->f_count); - - /* - * Link this into the parent counter's child list - */ - WARN_ON_ONCE(parent_counter->ctx->parent_ctx); - mutex_lock(&parent_counter->child_mutex); - list_add_tail(&child_counter->child_list, &parent_counter->child_list); - mutex_unlock(&parent_counter->child_mutex); - - return child_counter; -} - -static int inherit_group(struct perf_counter *parent_counter, - struct task_struct *parent, - struct perf_counter_context *parent_ctx, - struct task_struct *child, - struct perf_counter_context *child_ctx) -{ - struct perf_counter *leader; - struct perf_counter *sub; - struct perf_counter *child_ctr; - - leader = inherit_counter(parent_counter, parent, parent_ctx, - child, NULL, child_ctx); - if (IS_ERR(leader)) - return PTR_ERR(leader); - list_for_each_entry(sub, &parent_counter->sibling_list, group_entry) { - child_ctr = inherit_counter(sub, parent, parent_ctx, - child, leader, child_ctx); - if (IS_ERR(child_ctr)) - return PTR_ERR(child_ctr); - } - return 0; -} - -static void sync_child_counter(struct perf_counter *child_counter, - struct task_struct *child) -{ - struct perf_counter *parent_counter = child_counter->parent; - u64 child_val; - - if (child_counter->attr.inherit_stat) - perf_counter_read_event(child_counter, child); - - child_val = atomic64_read(&child_counter->count); - - /* - * Add back the child's count to the parent's count: - */ - atomic64_add(child_val, &parent_counter->count); - atomic64_add(child_counter->total_time_enabled, - &parent_counter->child_total_time_enabled); - atomic64_add(child_counter->total_time_running, - &parent_counter->child_total_time_running); - - /* - * Remove this counter from the parent's list - */ - WARN_ON_ONCE(parent_counter->ctx->parent_ctx); - mutex_lock(&parent_counter->child_mutex); - list_del_init(&child_counter->child_list); - mutex_unlock(&parent_counter->child_mutex); - - /* - * Release the parent counter, if this was the last - * reference to it. - */ - fput(parent_counter->filp); -} - -static void -__perf_counter_exit_task(struct perf_counter *child_counter, - struct perf_counter_context *child_ctx, - struct task_struct *child) -{ - struct perf_counter *parent_counter; - - update_counter_times(child_counter); - perf_counter_remove_from_context(child_counter); - - parent_counter = child_counter->parent; - /* - * It can happen that parent exits first, and has counters - * that are still around due to the child reference. These - * counters need to be zapped - but otherwise linger. - */ - if (parent_counter) { - sync_child_counter(child_counter, child); - free_counter(child_counter); - } -} - -/* - * When a child task exits, feed back counter values to parent counters. - */ -void perf_counter_exit_task(struct task_struct *child) -{ - struct perf_counter *child_counter, *tmp; - struct perf_counter_context *child_ctx; - unsigned long flags; - - if (likely(!child->perf_counter_ctxp)) { - perf_counter_task(child, NULL, 0); - return; - } - - local_irq_save(flags); - /* - * We can't reschedule here because interrupts are disabled, - * and either child is current or it is a task that can't be - * scheduled, so we are now safe from rescheduling changing - * our context. - */ - child_ctx = child->perf_counter_ctxp; - __perf_counter_task_sched_out(child_ctx); - - /* - * Take the context lock here so that if find_get_context is - * reading child->perf_counter_ctxp, we wait until it has - * incremented the context's refcount before we do put_ctx below. - */ - spin_lock(&child_ctx->lock); - child->perf_counter_ctxp = NULL; - /* - * If this context is a clone; unclone it so it can't get - * swapped to another process while we're removing all - * the counters from it. - */ - unclone_ctx(child_ctx); - spin_unlock_irqrestore(&child_ctx->lock, flags); - - /* - * Report the task dead after unscheduling the counters so that we - * won't get any samples after PERF_EVENT_EXIT. We can however still - * get a few PERF_EVENT_READ events. - */ - perf_counter_task(child, child_ctx, 0); - - /* - * We can recurse on the same lock type through: - * - * __perf_counter_exit_task() - * sync_child_counter() - * fput(parent_counter->filp) - * perf_release() - * mutex_lock(&ctx->mutex) - * - * But since its the parent context it won't be the same instance. - */ - mutex_lock_nested(&child_ctx->mutex, SINGLE_DEPTH_NESTING); - -again: - list_for_each_entry_safe(child_counter, tmp, &child_ctx->group_list, - group_entry) - __perf_counter_exit_task(child_counter, child_ctx, child); - - /* - * If the last counter was a group counter, it will have appended all - * its siblings to the list, but we obtained 'tmp' before that which - * will still point to the list head terminating the iteration. - */ - if (!list_empty(&child_ctx->group_list)) - goto again; - - mutex_unlock(&child_ctx->mutex); - - put_ctx(child_ctx); -} - -/* - * free an unexposed, unused context as created by inheritance by - * init_task below, used by fork() in case of fail. - */ -void perf_counter_free_task(struct task_struct *task) -{ - struct perf_counter_context *ctx = task->perf_counter_ctxp; - struct perf_counter *counter, *tmp; - - if (!ctx) - return; - - mutex_lock(&ctx->mutex); -again: - list_for_each_entry_safe(counter, tmp, &ctx->group_list, group_entry) { - struct perf_counter *parent = counter->parent; - - if (WARN_ON_ONCE(!parent)) - continue; - - mutex_lock(&parent->child_mutex); - list_del_init(&counter->child_list); - mutex_unlock(&parent->child_mutex); - - fput(parent->filp); - - list_del_counter(counter, ctx); - free_counter(counter); - } - - if (!list_empty(&ctx->group_list)) - goto again; - - mutex_unlock(&ctx->mutex); - - put_ctx(ctx); -} - -/* - * Initialize the perf_counter context in task_struct - */ -int perf_counter_init_task(struct task_struct *child) -{ - struct perf_counter_context *child_ctx, *parent_ctx; - struct perf_counter_context *cloned_ctx; - struct perf_counter *counter; - struct task_struct *parent = current; - int inherited_all = 1; - int ret = 0; - - child->perf_counter_ctxp = NULL; - - mutex_init(&child->perf_counter_mutex); - INIT_LIST_HEAD(&child->perf_counter_list); - - if (likely(!parent->perf_counter_ctxp)) - return 0; - - /* - * This is executed from the parent task context, so inherit - * counters that have been marked for cloning. - * First allocate and initialize a context for the child. - */ - - child_ctx = kmalloc(sizeof(struct perf_counter_context), GFP_KERNEL); - if (!child_ctx) - return -ENOMEM; - - __perf_counter_init_context(child_ctx, child); - child->perf_counter_ctxp = child_ctx; - get_task_struct(child); - - /* - * If the parent's context is a clone, pin it so it won't get - * swapped under us. - */ - parent_ctx = perf_pin_task_context(parent); - - /* - * No need to check if parent_ctx != NULL here; since we saw - * it non-NULL earlier, the only reason for it to become NULL - * is if we exit, and since we're currently in the middle of - * a fork we can't be exiting at the same time. - */ - - /* - * Lock the parent list. No need to lock the child - not PID - * hashed yet and not running, so nobody can access it. - */ - mutex_lock(&parent_ctx->mutex); - - /* - * We dont have to disable NMIs - we are only looking at - * the list, not manipulating it: - */ - list_for_each_entry_rcu(counter, &parent_ctx->event_list, event_entry) { - if (counter != counter->group_leader) - continue; - - if (!counter->attr.inherit) { - inherited_all = 0; - continue; - } - - ret = inherit_group(counter, parent, parent_ctx, - child, child_ctx); - if (ret) { - inherited_all = 0; - break; - } - } - - if (inherited_all) { - /* - * Mark the child context as a clone of the parent - * context, or of whatever the parent is a clone of. - * Note that if the parent is a clone, it could get - * uncloned at any point, but that doesn't matter - * because the list of counters and the generation - * count can't have changed since we took the mutex. - */ - cloned_ctx = rcu_dereference(parent_ctx->parent_ctx); - if (cloned_ctx) { - child_ctx->parent_ctx = cloned_ctx; - child_ctx->parent_gen = parent_ctx->parent_gen; - } else { - child_ctx->parent_ctx = parent_ctx; - child_ctx->parent_gen = parent_ctx->generation; - } - get_ctx(child_ctx->parent_ctx); - } - - mutex_unlock(&parent_ctx->mutex); - - perf_unpin_context(parent_ctx); - - return ret; -} - -static void __cpuinit perf_counter_init_cpu(int cpu) -{ - struct perf_cpu_context *cpuctx; - - cpuctx = &per_cpu(perf_cpu_context, cpu); - __perf_counter_init_context(&cpuctx->ctx, NULL); - - spin_lock(&perf_resource_lock); - cpuctx->max_pertask = perf_max_counters - perf_reserved_percpu; - spin_unlock(&perf_resource_lock); - - hw_perf_counter_setup(cpu); -} - -#ifdef CONFIG_HOTPLUG_CPU -static void __perf_counter_exit_cpu(void *info) -{ - struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); - struct perf_counter_context *ctx = &cpuctx->ctx; - struct perf_counter *counter, *tmp; - - list_for_each_entry_safe(counter, tmp, &ctx->group_list, group_entry) - __perf_counter_remove_from_context(counter); -} -static void perf_counter_exit_cpu(int cpu) -{ - struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); - struct perf_counter_context *ctx = &cpuctx->ctx; - - mutex_lock(&ctx->mutex); - smp_call_function_single(cpu, __perf_counter_exit_cpu, NULL, 1); - mutex_unlock(&ctx->mutex); -} -#else -static inline void perf_counter_exit_cpu(int cpu) { } -#endif - -static int __cpuinit -perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) -{ - unsigned int cpu = (long)hcpu; - - switch (action) { - - case CPU_UP_PREPARE: - case CPU_UP_PREPARE_FROZEN: - perf_counter_init_cpu(cpu); - break; - - case CPU_ONLINE: - case CPU_ONLINE_FROZEN: - hw_perf_counter_setup_online(cpu); - break; - - case CPU_DOWN_PREPARE: - case CPU_DOWN_PREPARE_FROZEN: - perf_counter_exit_cpu(cpu); - break; - - default: - break; - } - - return NOTIFY_OK; -} - -/* - * This has to have a higher priority than migration_notifier in sched.c. - */ -static struct notifier_block __cpuinitdata perf_cpu_nb = { - .notifier_call = perf_cpu_notify, - .priority = 20, -}; - -void __init perf_counter_init(void) -{ - perf_cpu_notify(&perf_cpu_nb, (unsigned long)CPU_UP_PREPARE, - (void *)(long)smp_processor_id()); - perf_cpu_notify(&perf_cpu_nb, (unsigned long)CPU_ONLINE, - (void *)(long)smp_processor_id()); - register_cpu_notifier(&perf_cpu_nb); -} - -static ssize_t perf_show_reserve_percpu(struct sysdev_class *class, char *buf) -{ - return sprintf(buf, "%d\n", perf_reserved_percpu); -} - -static ssize_t -perf_set_reserve_percpu(struct sysdev_class *class, - const char *buf, - size_t count) -{ - struct perf_cpu_context *cpuctx; - unsigned long val; - int err, cpu, mpt; - - err = strict_strtoul(buf, 10, &val); - if (err) - return err; - if (val > perf_max_counters) - return -EINVAL; - - spin_lock(&perf_resource_lock); - perf_reserved_percpu = val; - for_each_online_cpu(cpu) { - cpuctx = &per_cpu(perf_cpu_context, cpu); - spin_lock_irq(&cpuctx->ctx.lock); - mpt = min(perf_max_counters - cpuctx->ctx.nr_counters, - perf_max_counters - perf_reserved_percpu); - cpuctx->max_pertask = mpt; - spin_unlock_irq(&cpuctx->ctx.lock); - } - spin_unlock(&perf_resource_lock); - - return count; -} - -static ssize_t perf_show_overcommit(struct sysdev_class *class, char *buf) -{ - return sprintf(buf, "%d\n", perf_overcommit); -} - -static ssize_t -perf_set_overcommit(struct sysdev_class *class, const char *buf, size_t count) -{ - unsigned long val; - int err; - - err = strict_strtoul(buf, 10, &val); - if (err) - return err; - if (val > 1) - return -EINVAL; - - spin_lock(&perf_resource_lock); - perf_overcommit = val; - spin_unlock(&perf_resource_lock); - - return count; -} - -static SYSDEV_CLASS_ATTR( - reserve_percpu, - 0644, - perf_show_reserve_percpu, - perf_set_reserve_percpu - ); - -static SYSDEV_CLASS_ATTR( - overcommit, - 0644, - perf_show_overcommit, - perf_set_overcommit - ); - -static struct attribute *perfclass_attrs[] = { - &attr_reserve_percpu.attr, - &attr_overcommit.attr, - NULL -}; - -static struct attribute_group perfclass_attr_group = { - .attrs = perfclass_attrs, - .name = "perf_counters", -}; - -static int __init perf_counter_sysfs_init(void) -{ - return sysfs_create_group(&cpu_sysdev_class.kset.kobj, - &perfclass_attr_group); -} -device_initcall(perf_counter_sysfs_init); diff --git a/kernel/perf_event.c b/kernel/perf_event.c new file mode 100644 index 000000000000..6e8b99a04e1e --- /dev/null +++ b/kernel/perf_event.c @@ -0,0 +1,5000 @@ +/* + * Performance event core code + * + * Copyright (C) 2008 Thomas Gleixner + * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar + * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra + * Copyright © 2009 Paul Mackerras, IBM Corp. + * + * For licensing details see kernel-base/COPYING + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Each CPU has a list of per CPU events: + */ +DEFINE_PER_CPU(struct perf_cpu_context, perf_cpu_context); + +int perf_max_events __read_mostly = 1; +static int perf_reserved_percpu __read_mostly; +static int perf_overcommit __read_mostly = 1; + +static atomic_t nr_events __read_mostly; +static atomic_t nr_mmap_events __read_mostly; +static atomic_t nr_comm_events __read_mostly; +static atomic_t nr_task_events __read_mostly; + +/* + * perf event paranoia level: + * -1 - not paranoid at all + * 0 - disallow raw tracepoint access for unpriv + * 1 - disallow cpu events for unpriv + * 2 - disallow kernel profiling for unpriv + */ +int sysctl_perf_event_paranoid __read_mostly = 1; + +static inline bool perf_paranoid_tracepoint_raw(void) +{ + return sysctl_perf_event_paranoid > -1; +} + +static inline bool perf_paranoid_cpu(void) +{ + return sysctl_perf_event_paranoid > 0; +} + +static inline bool perf_paranoid_kernel(void) +{ + return sysctl_perf_event_paranoid > 1; +} + +int sysctl_perf_event_mlock __read_mostly = 512; /* 'free' kb per user */ + +/* + * max perf event sample rate + */ +int sysctl_perf_event_sample_rate __read_mostly = 100000; + +static atomic64_t perf_event_id; + +/* + * Lock for (sysadmin-configurable) event reservations: + */ +static DEFINE_SPINLOCK(perf_resource_lock); + +/* + * Architecture provided APIs - weak aliases: + */ +extern __weak const struct pmu *hw_perf_event_init(struct perf_event *event) +{ + return NULL; +} + +void __weak hw_perf_disable(void) { barrier(); } +void __weak hw_perf_enable(void) { barrier(); } + +void __weak hw_perf_event_setup(int cpu) { barrier(); } +void __weak hw_perf_event_setup_online(int cpu) { barrier(); } + +int __weak +hw_perf_group_sched_in(struct perf_event *group_leader, + struct perf_cpu_context *cpuctx, + struct perf_event_context *ctx, int cpu) +{ + return 0; +} + +void __weak perf_event_print_debug(void) { } + +static DEFINE_PER_CPU(int, perf_disable_count); + +void __perf_disable(void) +{ + __get_cpu_var(perf_disable_count)++; +} + +bool __perf_enable(void) +{ + return !--__get_cpu_var(perf_disable_count); +} + +void perf_disable(void) +{ + __perf_disable(); + hw_perf_disable(); +} + +void perf_enable(void) +{ + if (__perf_enable()) + hw_perf_enable(); +} + +static void get_ctx(struct perf_event_context *ctx) +{ + WARN_ON(!atomic_inc_not_zero(&ctx->refcount)); +} + +static void free_ctx(struct rcu_head *head) +{ + struct perf_event_context *ctx; + + ctx = container_of(head, struct perf_event_context, rcu_head); + kfree(ctx); +} + +static void put_ctx(struct perf_event_context *ctx) +{ + if (atomic_dec_and_test(&ctx->refcount)) { + if (ctx->parent_ctx) + put_ctx(ctx->parent_ctx); + if (ctx->task) + put_task_struct(ctx->task); + call_rcu(&ctx->rcu_head, free_ctx); + } +} + +static void unclone_ctx(struct perf_event_context *ctx) +{ + if (ctx->parent_ctx) { + put_ctx(ctx->parent_ctx); + ctx->parent_ctx = NULL; + } +} + +/* + * If we inherit events we want to return the parent event id + * to userspace. + */ +static u64 primary_event_id(struct perf_event *event) +{ + u64 id = event->id; + + if (event->parent) + id = event->parent->id; + + return id; +} + +/* + * Get the perf_event_context for a task and lock it. + * This has to cope with with the fact that until it is locked, + * the context could get moved to another task. + */ +static struct perf_event_context * +perf_lock_task_context(struct task_struct *task, unsigned long *flags) +{ + struct perf_event_context *ctx; + + rcu_read_lock(); + retry: + ctx = rcu_dereference(task->perf_event_ctxp); + if (ctx) { + /* + * If this context is a clone of another, it might + * get swapped for another underneath us by + * perf_event_task_sched_out, though the + * rcu_read_lock() protects us from any context + * getting freed. Lock the context and check if it + * got swapped before we could get the lock, and retry + * if so. If we locked the right context, then it + * can't get swapped on us any more. + */ + spin_lock_irqsave(&ctx->lock, *flags); + if (ctx != rcu_dereference(task->perf_event_ctxp)) { + spin_unlock_irqrestore(&ctx->lock, *flags); + goto retry; + } + + if (!atomic_inc_not_zero(&ctx->refcount)) { + spin_unlock_irqrestore(&ctx->lock, *flags); + ctx = NULL; + } + } + rcu_read_unlock(); + return ctx; +} + +/* + * Get the context for a task and increment its pin_count so it + * can't get swapped to another task. This also increments its + * reference count so that the context can't get freed. + */ +static struct perf_event_context *perf_pin_task_context(struct task_struct *task) +{ + struct perf_event_context *ctx; + unsigned long flags; + + ctx = perf_lock_task_context(task, &flags); + if (ctx) { + ++ctx->pin_count; + spin_unlock_irqrestore(&ctx->lock, flags); + } + return ctx; +} + +static void perf_unpin_context(struct perf_event_context *ctx) +{ + unsigned long flags; + + spin_lock_irqsave(&ctx->lock, flags); + --ctx->pin_count; + spin_unlock_irqrestore(&ctx->lock, flags); + put_ctx(ctx); +} + +/* + * Add a event from the lists for its context. + * Must be called with ctx->mutex and ctx->lock held. + */ +static void +list_add_event(struct perf_event *event, struct perf_event_context *ctx) +{ + struct perf_event *group_leader = event->group_leader; + + /* + * Depending on whether it is a standalone or sibling event, + * add it straight to the context's event list, or to the group + * leader's sibling list: + */ + if (group_leader == event) + list_add_tail(&event->group_entry, &ctx->group_list); + else { + list_add_tail(&event->group_entry, &group_leader->sibling_list); + group_leader->nr_siblings++; + } + + list_add_rcu(&event->event_entry, &ctx->event_list); + ctx->nr_events++; + if (event->attr.inherit_stat) + ctx->nr_stat++; +} + +/* + * Remove a event from the lists for its context. + * Must be called with ctx->mutex and ctx->lock held. + */ +static void +list_del_event(struct perf_event *event, struct perf_event_context *ctx) +{ + struct perf_event *sibling, *tmp; + + if (list_empty(&event->group_entry)) + return; + ctx->nr_events--; + if (event->attr.inherit_stat) + ctx->nr_stat--; + + list_del_init(&event->group_entry); + list_del_rcu(&event->event_entry); + + if (event->group_leader != event) + event->group_leader->nr_siblings--; + + /* + * If this was a group event with sibling events then + * upgrade the siblings to singleton events by adding them + * to the context list directly: + */ + list_for_each_entry_safe(sibling, tmp, &event->sibling_list, group_entry) { + + list_move_tail(&sibling->group_entry, &ctx->group_list); + sibling->group_leader = sibling; + } +} + +static void +event_sched_out(struct perf_event *event, + struct perf_cpu_context *cpuctx, + struct perf_event_context *ctx) +{ + if (event->state != PERF_EVENT_STATE_ACTIVE) + return; + + event->state = PERF_EVENT_STATE_INACTIVE; + if (event->pending_disable) { + event->pending_disable = 0; + event->state = PERF_EVENT_STATE_OFF; + } + event->tstamp_stopped = ctx->time; + event->pmu->disable(event); + event->oncpu = -1; + + if (!is_software_event(event)) + cpuctx->active_oncpu--; + ctx->nr_active--; + if (event->attr.exclusive || !cpuctx->active_oncpu) + cpuctx->exclusive = 0; +} + +static void +group_sched_out(struct perf_event *group_event, + struct perf_cpu_context *cpuctx, + struct perf_event_context *ctx) +{ + struct perf_event *event; + + if (group_event->state != PERF_EVENT_STATE_ACTIVE) + return; + + event_sched_out(group_event, cpuctx, ctx); + + /* + * Schedule out siblings (if any): + */ + list_for_each_entry(event, &group_event->sibling_list, group_entry) + event_sched_out(event, cpuctx, ctx); + + if (group_event->attr.exclusive) + cpuctx->exclusive = 0; +} + +/* + * Cross CPU call to remove a performance event + * + * We disable the event on the hardware level first. After that we + * remove it from the context list. + */ +static void __perf_event_remove_from_context(void *info) +{ + struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); + struct perf_event *event = info; + struct perf_event_context *ctx = event->ctx; + + /* + * If this is a task context, we need to check whether it is + * the current task context of this cpu. If not it has been + * scheduled out before the smp call arrived. + */ + if (ctx->task && cpuctx->task_ctx != ctx) + return; + + spin_lock(&ctx->lock); + /* + * Protect the list operation against NMI by disabling the + * events on a global level. + */ + perf_disable(); + + event_sched_out(event, cpuctx, ctx); + + list_del_event(event, ctx); + + if (!ctx->task) { + /* + * Allow more per task events with respect to the + * reservation: + */ + cpuctx->max_pertask = + min(perf_max_events - ctx->nr_events, + perf_max_events - perf_reserved_percpu); + } + + perf_enable(); + spin_unlock(&ctx->lock); +} + + +/* + * Remove the event from a task's (or a CPU's) list of events. + * + * Must be called with ctx->mutex held. + * + * CPU events are removed with a smp call. For task events we only + * call when the task is on a CPU. + * + * If event->ctx is a cloned context, callers must make sure that + * every task struct that event->ctx->task could possibly point to + * remains valid. This is OK when called from perf_release since + * that only calls us on the top-level context, which can't be a clone. + * When called from perf_event_exit_task, it's OK because the + * context has been detached from its task. + */ +static void perf_event_remove_from_context(struct perf_event *event) +{ + struct perf_event_context *ctx = event->ctx; + struct task_struct *task = ctx->task; + + if (!task) { + /* + * Per cpu events are removed via an smp call and + * the removal is always sucessful. + */ + smp_call_function_single(event->cpu, + __perf_event_remove_from_context, + event, 1); + return; + } + +retry: + task_oncpu_function_call(task, __perf_event_remove_from_context, + event); + + spin_lock_irq(&ctx->lock); + /* + * If the context is active we need to retry the smp call. + */ + if (ctx->nr_active && !list_empty(&event->group_entry)) { + spin_unlock_irq(&ctx->lock); + goto retry; + } + + /* + * The lock prevents that this context is scheduled in so we + * can remove the event safely, if the call above did not + * succeed. + */ + if (!list_empty(&event->group_entry)) { + list_del_event(event, ctx); + } + spin_unlock_irq(&ctx->lock); +} + +static inline u64 perf_clock(void) +{ + return cpu_clock(smp_processor_id()); +} + +/* + * Update the record of the current time in a context. + */ +static void update_context_time(struct perf_event_context *ctx) +{ + u64 now = perf_clock(); + + ctx->time += now - ctx->timestamp; + ctx->timestamp = now; +} + +/* + * Update the total_time_enabled and total_time_running fields for a event. + */ +static void update_event_times(struct perf_event *event) +{ + struct perf_event_context *ctx = event->ctx; + u64 run_end; + + if (event->state < PERF_EVENT_STATE_INACTIVE || + event->group_leader->state < PERF_EVENT_STATE_INACTIVE) + return; + + event->total_time_enabled = ctx->time - event->tstamp_enabled; + + if (event->state == PERF_EVENT_STATE_INACTIVE) + run_end = event->tstamp_stopped; + else + run_end = ctx->time; + + event->total_time_running = run_end - event->tstamp_running; +} + +/* + * Update total_time_enabled and total_time_running for all events in a group. + */ +static void update_group_times(struct perf_event *leader) +{ + struct perf_event *event; + + update_event_times(leader); + list_for_each_entry(event, &leader->sibling_list, group_entry) + update_event_times(event); +} + +/* + * Cross CPU call to disable a performance event + */ +static void __perf_event_disable(void *info) +{ + struct perf_event *event = info; + struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); + struct perf_event_context *ctx = event->ctx; + + /* + * If this is a per-task event, need to check whether this + * event's task is the current task on this cpu. + */ + if (ctx->task && cpuctx->task_ctx != ctx) + return; + + spin_lock(&ctx->lock); + + /* + * If the event is on, turn it off. + * If it is in error state, leave it in error state. + */ + if (event->state >= PERF_EVENT_STATE_INACTIVE) { + update_context_time(ctx); + update_group_times(event); + if (event == event->group_leader) + group_sched_out(event, cpuctx, ctx); + else + event_sched_out(event, cpuctx, ctx); + event->state = PERF_EVENT_STATE_OFF; + } + + spin_unlock(&ctx->lock); +} + +/* + * Disable a event. + * + * If event->ctx is a cloned context, callers must make sure that + * every task struct that event->ctx->task could possibly point to + * remains valid. This condition is satisifed when called through + * perf_event_for_each_child or perf_event_for_each because they + * hold the top-level event's child_mutex, so any descendant that + * goes to exit will block in sync_child_event. + * When called from perf_pending_event it's OK because event->ctx + * is the current context on this CPU and preemption is disabled, + * hence we can't get into perf_event_task_sched_out for this context. + */ +static void perf_event_disable(struct perf_event *event) +{ + struct perf_event_context *ctx = event->ctx; + struct task_struct *task = ctx->task; + + if (!task) { + /* + * Disable the event on the cpu that it's on + */ + smp_call_function_single(event->cpu, __perf_event_disable, + event, 1); + return; + } + + retry: + task_oncpu_function_call(task, __perf_event_disable, event); + + spin_lock_irq(&ctx->lock); + /* + * If the event is still active, we need to retry the cross-call. + */ + if (event->state == PERF_EVENT_STATE_ACTIVE) { + spin_unlock_irq(&ctx->lock); + goto retry; + } + + /* + * Since we have the lock this context can't be scheduled + * in, so we can change the state safely. + */ + if (event->state == PERF_EVENT_STATE_INACTIVE) { + update_group_times(event); + event->state = PERF_EVENT_STATE_OFF; + } + + spin_unlock_irq(&ctx->lock); +} + +static int +event_sched_in(struct perf_event *event, + struct perf_cpu_context *cpuctx, + struct perf_event_context *ctx, + int cpu) +{ + if (event->state <= PERF_EVENT_STATE_OFF) + return 0; + + event->state = PERF_EVENT_STATE_ACTIVE; + event->oncpu = cpu; /* TODO: put 'cpu' into cpuctx->cpu */ + /* + * The new state must be visible before we turn it on in the hardware: + */ + smp_wmb(); + + if (event->pmu->enable(event)) { + event->state = PERF_EVENT_STATE_INACTIVE; + event->oncpu = -1; + return -EAGAIN; + } + + event->tstamp_running += ctx->time - event->tstamp_stopped; + + if (!is_software_event(event)) + cpuctx->active_oncpu++; + ctx->nr_active++; + + if (event->attr.exclusive) + cpuctx->exclusive = 1; + + return 0; +} + +static int +group_sched_in(struct perf_event *group_event, + struct perf_cpu_context *cpuctx, + struct perf_event_context *ctx, + int cpu) +{ + struct perf_event *event, *partial_group; + int ret; + + if (group_event->state == PERF_EVENT_STATE_OFF) + return 0; + + ret = hw_perf_group_sched_in(group_event, cpuctx, ctx, cpu); + if (ret) + return ret < 0 ? ret : 0; + + if (event_sched_in(group_event, cpuctx, ctx, cpu)) + return -EAGAIN; + + /* + * Schedule in siblings as one group (if any): + */ + list_for_each_entry(event, &group_event->sibling_list, group_entry) { + if (event_sched_in(event, cpuctx, ctx, cpu)) { + partial_group = event; + goto group_error; + } + } + + return 0; + +group_error: + /* + * Groups can be scheduled in as one unit only, so undo any + * partial group before returning: + */ + list_for_each_entry(event, &group_event->sibling_list, group_entry) { + if (event == partial_group) + break; + event_sched_out(event, cpuctx, ctx); + } + event_sched_out(group_event, cpuctx, ctx); + + return -EAGAIN; +} + +/* + * Return 1 for a group consisting entirely of software events, + * 0 if the group contains any hardware events. + */ +static int is_software_only_group(struct perf_event *leader) +{ + struct perf_event *event; + + if (!is_software_event(leader)) + return 0; + + list_for_each_entry(event, &leader->sibling_list, group_entry) + if (!is_software_event(event)) + return 0; + + return 1; +} + +/* + * Work out whether we can put this event group on the CPU now. + */ +static int group_can_go_on(struct perf_event *event, + struct perf_cpu_context *cpuctx, + int can_add_hw) +{ + /* + * Groups consisting entirely of software events can always go on. + */ + if (is_software_only_group(event)) + return 1; + /* + * If an exclusive group is already on, no other hardware + * events can go on. + */ + if (cpuctx->exclusive) + return 0; + /* + * If this group is exclusive and there are already + * events on the CPU, it can't go on. + */ + if (event->attr.exclusive && cpuctx->active_oncpu) + return 0; + /* + * Otherwise, try to add it if all previous groups were able + * to go on. + */ + return can_add_hw; +} + +static void add_event_to_ctx(struct perf_event *event, + struct perf_event_context *ctx) +{ + list_add_event(event, ctx); + event->tstamp_enabled = ctx->time; + event->tstamp_running = ctx->time; + event->tstamp_stopped = ctx->time; +} + +/* + * Cross CPU call to install and enable a performance event + * + * Must be called with ctx->mutex held + */ +static void __perf_install_in_context(void *info) +{ + struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); + struct perf_event *event = info; + struct perf_event_context *ctx = event->ctx; + struct perf_event *leader = event->group_leader; + int cpu = smp_processor_id(); + int err; + + /* + * If this is a task context, we need to check whether it is + * the current task context of this cpu. If not it has been + * scheduled out before the smp call arrived. + * Or possibly this is the right context but it isn't + * on this cpu because it had no events. + */ + if (ctx->task && cpuctx->task_ctx != ctx) { + if (cpuctx->task_ctx || ctx->task != current) + return; + cpuctx->task_ctx = ctx; + } + + spin_lock(&ctx->lock); + ctx->is_active = 1; + update_context_time(ctx); + + /* + * Protect the list operation against NMI by disabling the + * events on a global level. NOP for non NMI based events. + */ + perf_disable(); + + add_event_to_ctx(event, ctx); + + /* + * Don't put the event on if it is disabled or if + * it is in a group and the group isn't on. + */ + if (event->state != PERF_EVENT_STATE_INACTIVE || + (leader != event && leader->state != PERF_EVENT_STATE_ACTIVE)) + goto unlock; + + /* + * An exclusive event can't go on if there are already active + * hardware events, and no hardware event can go on if there + * is already an exclusive event on. + */ + if (!group_can_go_on(event, cpuctx, 1)) + err = -EEXIST; + else + err = event_sched_in(event, cpuctx, ctx, cpu); + + if (err) { + /* + * This event couldn't go on. If it is in a group + * then we have to pull the whole group off. + * If the event group is pinned then put it in error state. + */ + if (leader != event) + group_sched_out(leader, cpuctx, ctx); + if (leader->attr.pinned) { + update_group_times(leader); + leader->state = PERF_EVENT_STATE_ERROR; + } + } + + if (!err && !ctx->task && cpuctx->max_pertask) + cpuctx->max_pertask--; + + unlock: + perf_enable(); + + spin_unlock(&ctx->lock); +} + +/* + * Attach a performance event to a context + * + * First we add the event to the list with the hardware enable bit + * in event->hw_config cleared. + * + * If the event is attached to a task which is on a CPU we use a smp + * call to enable it in the task context. The task might have been + * scheduled away, but we check this in the smp call again. + * + * Must be called with ctx->mutex held. + */ +static void +perf_install_in_context(struct perf_event_context *ctx, + struct perf_event *event, + int cpu) +{ + struct task_struct *task = ctx->task; + + if (!task) { + /* + * Per cpu events are installed via an smp call and + * the install is always sucessful. + */ + smp_call_function_single(cpu, __perf_install_in_context, + event, 1); + return; + } + +retry: + task_oncpu_function_call(task, __perf_install_in_context, + event); + + spin_lock_irq(&ctx->lock); + /* + * we need to retry the smp call. + */ + if (ctx->is_active && list_empty(&event->group_entry)) { + spin_unlock_irq(&ctx->lock); + goto retry; + } + + /* + * The lock prevents that this context is scheduled in so we + * can add the event safely, if it the call above did not + * succeed. + */ + if (list_empty(&event->group_entry)) + add_event_to_ctx(event, ctx); + spin_unlock_irq(&ctx->lock); +} + +/* + * Put a event into inactive state and update time fields. + * Enabling the leader of a group effectively enables all + * the group members that aren't explicitly disabled, so we + * have to update their ->tstamp_enabled also. + * Note: this works for group members as well as group leaders + * since the non-leader members' sibling_lists will be empty. + */ +static void __perf_event_mark_enabled(struct perf_event *event, + struct perf_event_context *ctx) +{ + struct perf_event *sub; + + event->state = PERF_EVENT_STATE_INACTIVE; + event->tstamp_enabled = ctx->time - event->total_time_enabled; + list_for_each_entry(sub, &event->sibling_list, group_entry) + if (sub->state >= PERF_EVENT_STATE_INACTIVE) + sub->tstamp_enabled = + ctx->time - sub->total_time_enabled; +} + +/* + * Cross CPU call to enable a performance event + */ +static void __perf_event_enable(void *info) +{ + struct perf_event *event = info; + struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); + struct perf_event_context *ctx = event->ctx; + struct perf_event *leader = event->group_leader; + int err; + + /* + * If this is a per-task event, need to check whether this + * event's task is the current task on this cpu. + */ + if (ctx->task && cpuctx->task_ctx != ctx) { + if (cpuctx->task_ctx || ctx->task != current) + return; + cpuctx->task_ctx = ctx; + } + + spin_lock(&ctx->lock); + ctx->is_active = 1; + update_context_time(ctx); + + if (event->state >= PERF_EVENT_STATE_INACTIVE) + goto unlock; + __perf_event_mark_enabled(event, ctx); + + /* + * If the event is in a group and isn't the group leader, + * then don't put it on unless the group is on. + */ + if (leader != event && leader->state != PERF_EVENT_STATE_ACTIVE) + goto unlock; + + if (!group_can_go_on(event, cpuctx, 1)) { + err = -EEXIST; + } else { + perf_disable(); + if (event == leader) + err = group_sched_in(event, cpuctx, ctx, + smp_processor_id()); + else + err = event_sched_in(event, cpuctx, ctx, + smp_processor_id()); + perf_enable(); + } + + if (err) { + /* + * If this event can't go on and it's part of a + * group, then the whole group has to come off. + */ + if (leader != event) + group_sched_out(leader, cpuctx, ctx); + if (leader->attr.pinned) { + update_group_times(leader); + leader->state = PERF_EVENT_STATE_ERROR; + } + } + + unlock: + spin_unlock(&ctx->lock); +} + +/* + * Enable a event. + * + * If event->ctx is a cloned context, callers must make sure that + * every task struct that event->ctx->task could possibly point to + * remains valid. This condition is satisfied when called through + * perf_event_for_each_child or perf_event_for_each as described + * for perf_event_disable. + */ +static void perf_event_enable(struct perf_event *event) +{ + struct perf_event_context *ctx = event->ctx; + struct task_struct *task = ctx->task; + + if (!task) { + /* + * Enable the event on the cpu that it's on + */ + smp_call_function_single(event->cpu, __perf_event_enable, + event, 1); + return; + } + + spin_lock_irq(&ctx->lock); + if (event->state >= PERF_EVENT_STATE_INACTIVE) + goto out; + + /* + * If the event is in error state, clear that first. + * That way, if we see the event in error state below, we + * know that it has gone back into error state, as distinct + * from the task having been scheduled away before the + * cross-call arrived. + */ + if (event->state == PERF_EVENT_STATE_ERROR) + event->state = PERF_EVENT_STATE_OFF; + + retry: + spin_unlock_irq(&ctx->lock); + task_oncpu_function_call(task, __perf_event_enable, event); + + spin_lock_irq(&ctx->lock); + + /* + * If the context is active and the event is still off, + * we need to retry the cross-call. + */ + if (ctx->is_active && event->state == PERF_EVENT_STATE_OFF) + goto retry; + + /* + * Since we have the lock this context can't be scheduled + * in, so we can change the state safely. + */ + if (event->state == PERF_EVENT_STATE_OFF) + __perf_event_mark_enabled(event, ctx); + + out: + spin_unlock_irq(&ctx->lock); +} + +static int perf_event_refresh(struct perf_event *event, int refresh) +{ + /* + * not supported on inherited events + */ + if (event->attr.inherit) + return -EINVAL; + + atomic_add(refresh, &event->event_limit); + perf_event_enable(event); + + return 0; +} + +void __perf_event_sched_out(struct perf_event_context *ctx, + struct perf_cpu_context *cpuctx) +{ + struct perf_event *event; + + spin_lock(&ctx->lock); + ctx->is_active = 0; + if (likely(!ctx->nr_events)) + goto out; + update_context_time(ctx); + + perf_disable(); + if (ctx->nr_active) { + list_for_each_entry(event, &ctx->group_list, group_entry) { + if (event != event->group_leader) + event_sched_out(event, cpuctx, ctx); + else + group_sched_out(event, cpuctx, ctx); + } + } + perf_enable(); + out: + spin_unlock(&ctx->lock); +} + +/* + * Test whether two contexts are equivalent, i.e. whether they + * have both been cloned from the same version of the same context + * and they both have the same number of enabled events. + * If the number of enabled events is the same, then the set + * of enabled events should be the same, because these are both + * inherited contexts, therefore we can't access individual events + * in them directly with an fd; we can only enable/disable all + * events via prctl, or enable/disable all events in a family + * via ioctl, which will have the same effect on both contexts. + */ +static int context_equiv(struct perf_event_context *ctx1, + struct perf_event_context *ctx2) +{ + return ctx1->parent_ctx && ctx1->parent_ctx == ctx2->parent_ctx + && ctx1->parent_gen == ctx2->parent_gen + && !ctx1->pin_count && !ctx2->pin_count; +} + +static void __perf_event_read(void *event); + +static void __perf_event_sync_stat(struct perf_event *event, + struct perf_event *next_event) +{ + u64 value; + + if (!event->attr.inherit_stat) + return; + + /* + * Update the event value, we cannot use perf_event_read() + * because we're in the middle of a context switch and have IRQs + * disabled, which upsets smp_call_function_single(), however + * we know the event must be on the current CPU, therefore we + * don't need to use it. + */ + switch (event->state) { + case PERF_EVENT_STATE_ACTIVE: + __perf_event_read(event); + break; + + case PERF_EVENT_STATE_INACTIVE: + update_event_times(event); + break; + + default: + break; + } + + /* + * In order to keep per-task stats reliable we need to flip the event + * values when we flip the contexts. + */ + value = atomic64_read(&next_event->count); + value = atomic64_xchg(&event->count, value); + atomic64_set(&next_event->count, value); + + swap(event->total_time_enabled, next_event->total_time_enabled); + swap(event->total_time_running, next_event->total_time_running); + + /* + * Since we swizzled the values, update the user visible data too. + */ + perf_event_update_userpage(event); + perf_event_update_userpage(next_event); +} + +#define list_next_entry(pos, member) \ + list_entry(pos->member.next, typeof(*pos), member) + +static void perf_event_sync_stat(struct perf_event_context *ctx, + struct perf_event_context *next_ctx) +{ + struct perf_event *event, *next_event; + + if (!ctx->nr_stat) + return; + + event = list_first_entry(&ctx->event_list, + struct perf_event, event_entry); + + next_event = list_first_entry(&next_ctx->event_list, + struct perf_event, event_entry); + + while (&event->event_entry != &ctx->event_list && + &next_event->event_entry != &next_ctx->event_list) { + + __perf_event_sync_stat(event, next_event); + + event = list_next_entry(event, event_entry); + next_event = list_next_entry(next_event, event_entry); + } +} + +/* + * Called from scheduler to remove the events of the current task, + * with interrupts disabled. + * + * We stop each event and update the event value in event->count. + * + * This does not protect us against NMI, but disable() + * sets the disabled bit in the control field of event _before_ + * accessing the event control register. If a NMI hits, then it will + * not restart the event. + */ +void perf_event_task_sched_out(struct task_struct *task, + struct task_struct *next, int cpu) +{ + struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); + struct perf_event_context *ctx = task->perf_event_ctxp; + struct perf_event_context *next_ctx; + struct perf_event_context *parent; + struct pt_regs *regs; + int do_switch = 1; + + regs = task_pt_regs(task); + perf_sw_event(PERF_COUNT_SW_CONTEXT_SWITCHES, 1, 1, regs, 0); + + if (likely(!ctx || !cpuctx->task_ctx)) + return; + + update_context_time(ctx); + + rcu_read_lock(); + parent = rcu_dereference(ctx->parent_ctx); + next_ctx = next->perf_event_ctxp; + if (parent && next_ctx && + rcu_dereference(next_ctx->parent_ctx) == parent) { + /* + * Looks like the two contexts are clones, so we might be + * able to optimize the context switch. We lock both + * contexts and check that they are clones under the + * lock (including re-checking that neither has been + * uncloned in the meantime). It doesn't matter which + * order we take the locks because no other cpu could + * be trying to lock both of these tasks. + */ + spin_lock(&ctx->lock); + spin_lock_nested(&next_ctx->lock, SINGLE_DEPTH_NESTING); + if (context_equiv(ctx, next_ctx)) { + /* + * XXX do we need a memory barrier of sorts + * wrt to rcu_dereference() of perf_event_ctxp + */ + task->perf_event_ctxp = next_ctx; + next->perf_event_ctxp = ctx; + ctx->task = next; + next_ctx->task = task; + do_switch = 0; + + perf_event_sync_stat(ctx, next_ctx); + } + spin_unlock(&next_ctx->lock); + spin_unlock(&ctx->lock); + } + rcu_read_unlock(); + + if (do_switch) { + __perf_event_sched_out(ctx, cpuctx); + cpuctx->task_ctx = NULL; + } +} + +/* + * Called with IRQs disabled + */ +static void __perf_event_task_sched_out(struct perf_event_context *ctx) +{ + struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); + + if (!cpuctx->task_ctx) + return; + + if (WARN_ON_ONCE(ctx != cpuctx->task_ctx)) + return; + + __perf_event_sched_out(ctx, cpuctx); + cpuctx->task_ctx = NULL; +} + +/* + * Called with IRQs disabled + */ +static void perf_event_cpu_sched_out(struct perf_cpu_context *cpuctx) +{ + __perf_event_sched_out(&cpuctx->ctx, cpuctx); +} + +static void +__perf_event_sched_in(struct perf_event_context *ctx, + struct perf_cpu_context *cpuctx, int cpu) +{ + struct perf_event *event; + int can_add_hw = 1; + + spin_lock(&ctx->lock); + ctx->is_active = 1; + if (likely(!ctx->nr_events)) + goto out; + + ctx->timestamp = perf_clock(); + + perf_disable(); + + /* + * First go through the list and put on any pinned groups + * in order to give them the best chance of going on. + */ + list_for_each_entry(event, &ctx->group_list, group_entry) { + if (event->state <= PERF_EVENT_STATE_OFF || + !event->attr.pinned) + continue; + if (event->cpu != -1 && event->cpu != cpu) + continue; + + if (event != event->group_leader) + event_sched_in(event, cpuctx, ctx, cpu); + else { + if (group_can_go_on(event, cpuctx, 1)) + group_sched_in(event, cpuctx, ctx, cpu); + } + + /* + * If this pinned group hasn't been scheduled, + * put it in error state. + */ + if (event->state == PERF_EVENT_STATE_INACTIVE) { + update_group_times(event); + event->state = PERF_EVENT_STATE_ERROR; + } + } + + list_for_each_entry(event, &ctx->group_list, group_entry) { + /* + * Ignore events in OFF or ERROR state, and + * ignore pinned events since we did them already. + */ + if (event->state <= PERF_EVENT_STATE_OFF || + event->attr.pinned) + continue; + + /* + * Listen to the 'cpu' scheduling filter constraint + * of events: + */ + if (event->cpu != -1 && event->cpu != cpu) + continue; + + if (event != event->group_leader) { + if (event_sched_in(event, cpuctx, ctx, cpu)) + can_add_hw = 0; + } else { + if (group_can_go_on(event, cpuctx, can_add_hw)) { + if (group_sched_in(event, cpuctx, ctx, cpu)) + can_add_hw = 0; + } + } + } + perf_enable(); + out: + spin_unlock(&ctx->lock); +} + +/* + * Called from scheduler to add the events of the current task + * with interrupts disabled. + * + * We restore the event value and then enable it. + * + * This does not protect us against NMI, but enable() + * sets the enabled bit in the control field of event _before_ + * accessing the event control register. If a NMI hits, then it will + * keep the event running. + */ +void perf_event_task_sched_in(struct task_struct *task, int cpu) +{ + struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); + struct perf_event_context *ctx = task->perf_event_ctxp; + + if (likely(!ctx)) + return; + if (cpuctx->task_ctx == ctx) + return; + __perf_event_sched_in(ctx, cpuctx, cpu); + cpuctx->task_ctx = ctx; +} + +static void perf_event_cpu_sched_in(struct perf_cpu_context *cpuctx, int cpu) +{ + struct perf_event_context *ctx = &cpuctx->ctx; + + __perf_event_sched_in(ctx, cpuctx, cpu); +} + +#define MAX_INTERRUPTS (~0ULL) + +static void perf_log_throttle(struct perf_event *event, int enable); + +static void perf_adjust_period(struct perf_event *event, u64 events) +{ + struct hw_perf_event *hwc = &event->hw; + u64 period, sample_period; + s64 delta; + + events *= hwc->sample_period; + period = div64_u64(events, event->attr.sample_freq); + + delta = (s64)(period - hwc->sample_period); + delta = (delta + 7) / 8; /* low pass filter */ + + sample_period = hwc->sample_period + delta; + + if (!sample_period) + sample_period = 1; + + hwc->sample_period = sample_period; +} + +static void perf_ctx_adjust_freq(struct perf_event_context *ctx) +{ + struct perf_event *event; + struct hw_perf_event *hwc; + u64 interrupts, freq; + + spin_lock(&ctx->lock); + list_for_each_entry(event, &ctx->group_list, group_entry) { + if (event->state != PERF_EVENT_STATE_ACTIVE) + continue; + + hwc = &event->hw; + + interrupts = hwc->interrupts; + hwc->interrupts = 0; + + /* + * unthrottle events on the tick + */ + if (interrupts == MAX_INTERRUPTS) { + perf_log_throttle(event, 1); + event->pmu->unthrottle(event); + interrupts = 2*sysctl_perf_event_sample_rate/HZ; + } + + if (!event->attr.freq || !event->attr.sample_freq) + continue; + + /* + * if the specified freq < HZ then we need to skip ticks + */ + if (event->attr.sample_freq < HZ) { + freq = event->attr.sample_freq; + + hwc->freq_count += freq; + hwc->freq_interrupts += interrupts; + + if (hwc->freq_count < HZ) + continue; + + interrupts = hwc->freq_interrupts; + hwc->freq_interrupts = 0; + hwc->freq_count -= HZ; + } else + freq = HZ; + + perf_adjust_period(event, freq * interrupts); + + /* + * In order to avoid being stalled by an (accidental) huge + * sample period, force reset the sample period if we didn't + * get any events in this freq period. + */ + if (!interrupts) { + perf_disable(); + event->pmu->disable(event); + atomic64_set(&hwc->period_left, 0); + event->pmu->enable(event); + perf_enable(); + } + } + spin_unlock(&ctx->lock); +} + +/* + * Round-robin a context's events: + */ +static void rotate_ctx(struct perf_event_context *ctx) +{ + struct perf_event *event; + + if (!ctx->nr_events) + return; + + spin_lock(&ctx->lock); + /* + * Rotate the first entry last (works just fine for group events too): + */ + perf_disable(); + list_for_each_entry(event, &ctx->group_list, group_entry) { + list_move_tail(&event->group_entry, &ctx->group_list); + break; + } + perf_enable(); + + spin_unlock(&ctx->lock); +} + +void perf_event_task_tick(struct task_struct *curr, int cpu) +{ + struct perf_cpu_context *cpuctx; + struct perf_event_context *ctx; + + if (!atomic_read(&nr_events)) + return; + + cpuctx = &per_cpu(perf_cpu_context, cpu); + ctx = curr->perf_event_ctxp; + + perf_ctx_adjust_freq(&cpuctx->ctx); + if (ctx) + perf_ctx_adjust_freq(ctx); + + perf_event_cpu_sched_out(cpuctx); + if (ctx) + __perf_event_task_sched_out(ctx); + + rotate_ctx(&cpuctx->ctx); + if (ctx) + rotate_ctx(ctx); + + perf_event_cpu_sched_in(cpuctx, cpu); + if (ctx) + perf_event_task_sched_in(curr, cpu); +} + +/* + * Enable all of a task's events that have been marked enable-on-exec. + * This expects task == current. + */ +static void perf_event_enable_on_exec(struct task_struct *task) +{ + struct perf_event_context *ctx; + struct perf_event *event; + unsigned long flags; + int enabled = 0; + + local_irq_save(flags); + ctx = task->perf_event_ctxp; + if (!ctx || !ctx->nr_events) + goto out; + + __perf_event_task_sched_out(ctx); + + spin_lock(&ctx->lock); + + list_for_each_entry(event, &ctx->group_list, group_entry) { + if (!event->attr.enable_on_exec) + continue; + event->attr.enable_on_exec = 0; + if (event->state >= PERF_EVENT_STATE_INACTIVE) + continue; + __perf_event_mark_enabled(event, ctx); + enabled = 1; + } + + /* + * Unclone this context if we enabled any event. + */ + if (enabled) + unclone_ctx(ctx); + + spin_unlock(&ctx->lock); + + perf_event_task_sched_in(task, smp_processor_id()); + out: + local_irq_restore(flags); +} + +/* + * Cross CPU call to read the hardware event + */ +static void __perf_event_read(void *info) +{ + struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); + struct perf_event *event = info; + struct perf_event_context *ctx = event->ctx; + unsigned long flags; + + /* + * If this is a task context, we need to check whether it is + * the current task context of this cpu. If not it has been + * scheduled out before the smp call arrived. In that case + * event->count would have been updated to a recent sample + * when the event was scheduled out. + */ + if (ctx->task && cpuctx->task_ctx != ctx) + return; + + local_irq_save(flags); + if (ctx->is_active) + update_context_time(ctx); + event->pmu->read(event); + update_event_times(event); + local_irq_restore(flags); +} + +static u64 perf_event_read(struct perf_event *event) +{ + /* + * If event is enabled and currently active on a CPU, update the + * value in the event structure: + */ + if (event->state == PERF_EVENT_STATE_ACTIVE) { + smp_call_function_single(event->oncpu, + __perf_event_read, event, 1); + } else if (event->state == PERF_EVENT_STATE_INACTIVE) { + update_event_times(event); + } + + return atomic64_read(&event->count); +} + +/* + * Initialize the perf_event context in a task_struct: + */ +static void +__perf_event_init_context(struct perf_event_context *ctx, + struct task_struct *task) +{ + memset(ctx, 0, sizeof(*ctx)); + spin_lock_init(&ctx->lock); + mutex_init(&ctx->mutex); + INIT_LIST_HEAD(&ctx->group_list); + INIT_LIST_HEAD(&ctx->event_list); + atomic_set(&ctx->refcount, 1); + ctx->task = task; +} + +static struct perf_event_context *find_get_context(pid_t pid, int cpu) +{ + struct perf_event_context *ctx; + struct perf_cpu_context *cpuctx; + struct task_struct *task; + unsigned long flags; + int err; + + /* + * If cpu is not a wildcard then this is a percpu event: + */ + if (cpu != -1) { + /* Must be root to operate on a CPU event: */ + if (perf_paranoid_cpu() && !capable(CAP_SYS_ADMIN)) + return ERR_PTR(-EACCES); + + if (cpu < 0 || cpu > num_possible_cpus()) + return ERR_PTR(-EINVAL); + + /* + * We could be clever and allow to attach a event to an + * offline CPU and activate it when the CPU comes up, but + * that's for later. + */ + if (!cpu_isset(cpu, cpu_online_map)) + return ERR_PTR(-ENODEV); + + cpuctx = &per_cpu(perf_cpu_context, cpu); + ctx = &cpuctx->ctx; + get_ctx(ctx); + + return ctx; + } + + rcu_read_lock(); + if (!pid) + task = current; + else + task = find_task_by_vpid(pid); + if (task) + get_task_struct(task); + rcu_read_unlock(); + + if (!task) + return ERR_PTR(-ESRCH); + + /* + * Can't attach events to a dying task. + */ + err = -ESRCH; + if (task->flags & PF_EXITING) + goto errout; + + /* Reuse ptrace permission checks for now. */ + err = -EACCES; + if (!ptrace_may_access(task, PTRACE_MODE_READ)) + goto errout; + + retry: + ctx = perf_lock_task_context(task, &flags); + if (ctx) { + unclone_ctx(ctx); + spin_unlock_irqrestore(&ctx->lock, flags); + } + + if (!ctx) { + ctx = kmalloc(sizeof(struct perf_event_context), GFP_KERNEL); + err = -ENOMEM; + if (!ctx) + goto errout; + __perf_event_init_context(ctx, task); + get_ctx(ctx); + if (cmpxchg(&task->perf_event_ctxp, NULL, ctx)) { + /* + * We raced with some other task; use + * the context they set. + */ + kfree(ctx); + goto retry; + } + get_task_struct(task); + } + + put_task_struct(task); + return ctx; + + errout: + put_task_struct(task); + return ERR_PTR(err); +} + +static void free_event_rcu(struct rcu_head *head) +{ + struct perf_event *event; + + event = container_of(head, struct perf_event, rcu_head); + if (event->ns) + put_pid_ns(event->ns); + kfree(event); +} + +static void perf_pending_sync(struct perf_event *event); + +static void free_event(struct perf_event *event) +{ + perf_pending_sync(event); + + if (!event->parent) { + atomic_dec(&nr_events); + if (event->attr.mmap) + atomic_dec(&nr_mmap_events); + if (event->attr.comm) + atomic_dec(&nr_comm_events); + if (event->attr.task) + atomic_dec(&nr_task_events); + } + + if (event->output) { + fput(event->output->filp); + event->output = NULL; + } + + if (event->destroy) + event->destroy(event); + + put_ctx(event->ctx); + call_rcu(&event->rcu_head, free_event_rcu); +} + +/* + * Called when the last reference to the file is gone. + */ +static int perf_release(struct inode *inode, struct file *file) +{ + struct perf_event *event = file->private_data; + struct perf_event_context *ctx = event->ctx; + + file->private_data = NULL; + + WARN_ON_ONCE(ctx->parent_ctx); + mutex_lock(&ctx->mutex); + perf_event_remove_from_context(event); + mutex_unlock(&ctx->mutex); + + mutex_lock(&event->owner->perf_event_mutex); + list_del_init(&event->owner_entry); + mutex_unlock(&event->owner->perf_event_mutex); + put_task_struct(event->owner); + + free_event(event); + + return 0; +} + +static int perf_event_read_size(struct perf_event *event) +{ + int entry = sizeof(u64); /* value */ + int size = 0; + int nr = 1; + + if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + size += sizeof(u64); + + if (event->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + size += sizeof(u64); + + if (event->attr.read_format & PERF_FORMAT_ID) + entry += sizeof(u64); + + if (event->attr.read_format & PERF_FORMAT_GROUP) { + nr += event->group_leader->nr_siblings; + size += sizeof(u64); + } + + size += entry * nr; + + return size; +} + +static u64 perf_event_read_value(struct perf_event *event) +{ + struct perf_event *child; + u64 total = 0; + + total += perf_event_read(event); + list_for_each_entry(child, &event->child_list, child_list) + total += perf_event_read(child); + + return total; +} + +static int perf_event_read_entry(struct perf_event *event, + u64 read_format, char __user *buf) +{ + int n = 0, count = 0; + u64 values[2]; + + values[n++] = perf_event_read_value(event); + if (read_format & PERF_FORMAT_ID) + values[n++] = primary_event_id(event); + + count = n * sizeof(u64); + + if (copy_to_user(buf, values, count)) + return -EFAULT; + + return count; +} + +static int perf_event_read_group(struct perf_event *event, + u64 read_format, char __user *buf) +{ + struct perf_event *leader = event->group_leader, *sub; + int n = 0, size = 0, err = -EFAULT; + u64 values[3]; + + values[n++] = 1 + leader->nr_siblings; + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { + values[n++] = leader->total_time_enabled + + atomic64_read(&leader->child_total_time_enabled); + } + if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { + values[n++] = leader->total_time_running + + atomic64_read(&leader->child_total_time_running); + } + + size = n * sizeof(u64); + + if (copy_to_user(buf, values, size)) + return -EFAULT; + + err = perf_event_read_entry(leader, read_format, buf + size); + if (err < 0) + return err; + + size += err; + + list_for_each_entry(sub, &leader->sibling_list, group_entry) { + err = perf_event_read_entry(sub, read_format, + buf + size); + if (err < 0) + return err; + + size += err; + } + + return size; +} + +static int perf_event_read_one(struct perf_event *event, + u64 read_format, char __user *buf) +{ + u64 values[4]; + int n = 0; + + values[n++] = perf_event_read_value(event); + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { + values[n++] = event->total_time_enabled + + atomic64_read(&event->child_total_time_enabled); + } + if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { + values[n++] = event->total_time_running + + atomic64_read(&event->child_total_time_running); + } + if (read_format & PERF_FORMAT_ID) + values[n++] = primary_event_id(event); + + if (copy_to_user(buf, values, n * sizeof(u64))) + return -EFAULT; + + return n * sizeof(u64); +} + +/* + * Read the performance event - simple non blocking version for now + */ +static ssize_t +perf_read_hw(struct perf_event *event, char __user *buf, size_t count) +{ + u64 read_format = event->attr.read_format; + int ret; + + /* + * Return end-of-file for a read on a event that is in + * error state (i.e. because it was pinned but it couldn't be + * scheduled on to the CPU at some point). + */ + if (event->state == PERF_EVENT_STATE_ERROR) + return 0; + + if (count < perf_event_read_size(event)) + return -ENOSPC; + + WARN_ON_ONCE(event->ctx->parent_ctx); + mutex_lock(&event->child_mutex); + if (read_format & PERF_FORMAT_GROUP) + ret = perf_event_read_group(event, read_format, buf); + else + ret = perf_event_read_one(event, read_format, buf); + mutex_unlock(&event->child_mutex); + + return ret; +} + +static ssize_t +perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct perf_event *event = file->private_data; + + return perf_read_hw(event, buf, count); +} + +static unsigned int perf_poll(struct file *file, poll_table *wait) +{ + struct perf_event *event = file->private_data; + struct perf_mmap_data *data; + unsigned int events = POLL_HUP; + + rcu_read_lock(); + data = rcu_dereference(event->data); + if (data) + events = atomic_xchg(&data->poll, 0); + rcu_read_unlock(); + + poll_wait(file, &event->waitq, wait); + + return events; +} + +static void perf_event_reset(struct perf_event *event) +{ + (void)perf_event_read(event); + atomic64_set(&event->count, 0); + perf_event_update_userpage(event); +} + +/* + * Holding the top-level event's child_mutex means that any + * descendant process that has inherited this event will block + * in sync_child_event if it goes to exit, thus satisfying the + * task existence requirements of perf_event_enable/disable. + */ +static void perf_event_for_each_child(struct perf_event *event, + void (*func)(struct perf_event *)) +{ + struct perf_event *child; + + WARN_ON_ONCE(event->ctx->parent_ctx); + mutex_lock(&event->child_mutex); + func(event); + list_for_each_entry(child, &event->child_list, child_list) + func(child); + mutex_unlock(&event->child_mutex); +} + +static void perf_event_for_each(struct perf_event *event, + void (*func)(struct perf_event *)) +{ + struct perf_event_context *ctx = event->ctx; + struct perf_event *sibling; + + WARN_ON_ONCE(ctx->parent_ctx); + mutex_lock(&ctx->mutex); + event = event->group_leader; + + perf_event_for_each_child(event, func); + func(event); + list_for_each_entry(sibling, &event->sibling_list, group_entry) + perf_event_for_each_child(event, func); + mutex_unlock(&ctx->mutex); +} + +static int perf_event_period(struct perf_event *event, u64 __user *arg) +{ + struct perf_event_context *ctx = event->ctx; + unsigned long size; + int ret = 0; + u64 value; + + if (!event->attr.sample_period) + return -EINVAL; + + size = copy_from_user(&value, arg, sizeof(value)); + if (size != sizeof(value)) + return -EFAULT; + + if (!value) + return -EINVAL; + + spin_lock_irq(&ctx->lock); + if (event->attr.freq) { + if (value > sysctl_perf_event_sample_rate) { + ret = -EINVAL; + goto unlock; + } + + event->attr.sample_freq = value; + } else { + event->attr.sample_period = value; + event->hw.sample_period = value; + } +unlock: + spin_unlock_irq(&ctx->lock); + + return ret; +} + +int perf_event_set_output(struct perf_event *event, int output_fd); + +static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct perf_event *event = file->private_data; + void (*func)(struct perf_event *); + u32 flags = arg; + + switch (cmd) { + case PERF_EVENT_IOC_ENABLE: + func = perf_event_enable; + break; + case PERF_EVENT_IOC_DISABLE: + func = perf_event_disable; + break; + case PERF_EVENT_IOC_RESET: + func = perf_event_reset; + break; + + case PERF_EVENT_IOC_REFRESH: + return perf_event_refresh(event, arg); + + case PERF_EVENT_IOC_PERIOD: + return perf_event_period(event, (u64 __user *)arg); + + case PERF_EVENT_IOC_SET_OUTPUT: + return perf_event_set_output(event, arg); + + default: + return -ENOTTY; + } + + if (flags & PERF_IOC_FLAG_GROUP) + perf_event_for_each(event, func); + else + perf_event_for_each_child(event, func); + + return 0; +} + +int perf_event_task_enable(void) +{ + struct perf_event *event; + + mutex_lock(¤t->perf_event_mutex); + list_for_each_entry(event, ¤t->perf_event_list, owner_entry) + perf_event_for_each_child(event, perf_event_enable); + mutex_unlock(¤t->perf_event_mutex); + + return 0; +} + +int perf_event_task_disable(void) +{ + struct perf_event *event; + + mutex_lock(¤t->perf_event_mutex); + list_for_each_entry(event, ¤t->perf_event_list, owner_entry) + perf_event_for_each_child(event, perf_event_disable); + mutex_unlock(¤t->perf_event_mutex); + + return 0; +} + +#ifndef PERF_EVENT_INDEX_OFFSET +# define PERF_EVENT_INDEX_OFFSET 0 +#endif + +static int perf_event_index(struct perf_event *event) +{ + if (event->state != PERF_EVENT_STATE_ACTIVE) + return 0; + + return event->hw.idx + 1 - PERF_EVENT_INDEX_OFFSET; +} + +/* + * Callers need to ensure there can be no nesting of this function, otherwise + * the seqlock logic goes bad. We can not serialize this because the arch + * code calls this from NMI context. + */ +void perf_event_update_userpage(struct perf_event *event) +{ + struct perf_event_mmap_page *userpg; + struct perf_mmap_data *data; + + rcu_read_lock(); + data = rcu_dereference(event->data); + if (!data) + goto unlock; + + userpg = data->user_page; + + /* + * Disable preemption so as to not let the corresponding user-space + * spin too long if we get preempted. + */ + preempt_disable(); + ++userpg->lock; + barrier(); + userpg->index = perf_event_index(event); + userpg->offset = atomic64_read(&event->count); + if (event->state == PERF_EVENT_STATE_ACTIVE) + userpg->offset -= atomic64_read(&event->hw.prev_count); + + userpg->time_enabled = event->total_time_enabled + + atomic64_read(&event->child_total_time_enabled); + + userpg->time_running = event->total_time_running + + atomic64_read(&event->child_total_time_running); + + barrier(); + ++userpg->lock; + preempt_enable(); +unlock: + rcu_read_unlock(); +} + +static int perf_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct perf_event *event = vma->vm_file->private_data; + struct perf_mmap_data *data; + int ret = VM_FAULT_SIGBUS; + + if (vmf->flags & FAULT_FLAG_MKWRITE) { + if (vmf->pgoff == 0) + ret = 0; + return ret; + } + + rcu_read_lock(); + data = rcu_dereference(event->data); + if (!data) + goto unlock; + + if (vmf->pgoff == 0) { + vmf->page = virt_to_page(data->user_page); + } else { + int nr = vmf->pgoff - 1; + + if ((unsigned)nr > data->nr_pages) + goto unlock; + + if (vmf->flags & FAULT_FLAG_WRITE) + goto unlock; + + vmf->page = virt_to_page(data->data_pages[nr]); + } + + get_page(vmf->page); + vmf->page->mapping = vma->vm_file->f_mapping; + vmf->page->index = vmf->pgoff; + + ret = 0; +unlock: + rcu_read_unlock(); + + return ret; +} + +static int perf_mmap_data_alloc(struct perf_event *event, int nr_pages) +{ + struct perf_mmap_data *data; + unsigned long size; + int i; + + WARN_ON(atomic_read(&event->mmap_count)); + + size = sizeof(struct perf_mmap_data); + size += nr_pages * sizeof(void *); + + data = kzalloc(size, GFP_KERNEL); + if (!data) + goto fail; + + data->user_page = (void *)get_zeroed_page(GFP_KERNEL); + if (!data->user_page) + goto fail_user_page; + + for (i = 0; i < nr_pages; i++) { + data->data_pages[i] = (void *)get_zeroed_page(GFP_KERNEL); + if (!data->data_pages[i]) + goto fail_data_pages; + } + + data->nr_pages = nr_pages; + atomic_set(&data->lock, -1); + + if (event->attr.watermark) { + data->watermark = min_t(long, PAGE_SIZE * nr_pages, + event->attr.wakeup_watermark); + } + if (!data->watermark) + data->watermark = max(PAGE_SIZE, PAGE_SIZE * nr_pages / 4); + + rcu_assign_pointer(event->data, data); + + return 0; + +fail_data_pages: + for (i--; i >= 0; i--) + free_page((unsigned long)data->data_pages[i]); + + free_page((unsigned long)data->user_page); + +fail_user_page: + kfree(data); + +fail: + return -ENOMEM; +} + +static void perf_mmap_free_page(unsigned long addr) +{ + struct page *page = virt_to_page((void *)addr); + + page->mapping = NULL; + __free_page(page); +} + +static void __perf_mmap_data_free(struct rcu_head *rcu_head) +{ + struct perf_mmap_data *data; + int i; + + data = container_of(rcu_head, struct perf_mmap_data, rcu_head); + + perf_mmap_free_page((unsigned long)data->user_page); + for (i = 0; i < data->nr_pages; i++) + perf_mmap_free_page((unsigned long)data->data_pages[i]); + + kfree(data); +} + +static void perf_mmap_data_free(struct perf_event *event) +{ + struct perf_mmap_data *data = event->data; + + WARN_ON(atomic_read(&event->mmap_count)); + + rcu_assign_pointer(event->data, NULL); + call_rcu(&data->rcu_head, __perf_mmap_data_free); +} + +static void perf_mmap_open(struct vm_area_struct *vma) +{ + struct perf_event *event = vma->vm_file->private_data; + + atomic_inc(&event->mmap_count); +} + +static void perf_mmap_close(struct vm_area_struct *vma) +{ + struct perf_event *event = vma->vm_file->private_data; + + WARN_ON_ONCE(event->ctx->parent_ctx); + if (atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex)) { + struct user_struct *user = current_user(); + + atomic_long_sub(event->data->nr_pages + 1, &user->locked_vm); + vma->vm_mm->locked_vm -= event->data->nr_locked; + perf_mmap_data_free(event); + mutex_unlock(&event->mmap_mutex); + } +} + +static struct vm_operations_struct perf_mmap_vmops = { + .open = perf_mmap_open, + .close = perf_mmap_close, + .fault = perf_mmap_fault, + .page_mkwrite = perf_mmap_fault, +}; + +static int perf_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct perf_event *event = file->private_data; + unsigned long user_locked, user_lock_limit; + struct user_struct *user = current_user(); + unsigned long locked, lock_limit; + unsigned long vma_size; + unsigned long nr_pages; + long user_extra, extra; + int ret = 0; + + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + vma_size = vma->vm_end - vma->vm_start; + nr_pages = (vma_size / PAGE_SIZE) - 1; + + /* + * If we have data pages ensure they're a power-of-two number, so we + * can do bitmasks instead of modulo. + */ + if (nr_pages != 0 && !is_power_of_2(nr_pages)) + return -EINVAL; + + if (vma_size != PAGE_SIZE * (1 + nr_pages)) + return -EINVAL; + + if (vma->vm_pgoff != 0) + return -EINVAL; + + WARN_ON_ONCE(event->ctx->parent_ctx); + mutex_lock(&event->mmap_mutex); + if (event->output) { + ret = -EINVAL; + goto unlock; + } + + if (atomic_inc_not_zero(&event->mmap_count)) { + if (nr_pages != event->data->nr_pages) + ret = -EINVAL; + goto unlock; + } + + user_extra = nr_pages + 1; + user_lock_limit = sysctl_perf_event_mlock >> (PAGE_SHIFT - 10); + + /* + * Increase the limit linearly with more CPUs: + */ + user_lock_limit *= num_online_cpus(); + + user_locked = atomic_long_read(&user->locked_vm) + user_extra; + + extra = 0; + if (user_locked > user_lock_limit) + extra = user_locked - user_lock_limit; + + lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur; + lock_limit >>= PAGE_SHIFT; + locked = vma->vm_mm->locked_vm + extra; + + if ((locked > lock_limit) && perf_paranoid_tracepoint_raw() && + !capable(CAP_IPC_LOCK)) { + ret = -EPERM; + goto unlock; + } + + WARN_ON(event->data); + ret = perf_mmap_data_alloc(event, nr_pages); + if (ret) + goto unlock; + + atomic_set(&event->mmap_count, 1); + atomic_long_add(user_extra, &user->locked_vm); + vma->vm_mm->locked_vm += extra; + event->data->nr_locked = extra; + if (vma->vm_flags & VM_WRITE) + event->data->writable = 1; + +unlock: + mutex_unlock(&event->mmap_mutex); + + vma->vm_flags |= VM_RESERVED; + vma->vm_ops = &perf_mmap_vmops; + + return ret; +} + +static int perf_fasync(int fd, struct file *filp, int on) +{ + struct inode *inode = filp->f_path.dentry->d_inode; + struct perf_event *event = filp->private_data; + int retval; + + mutex_lock(&inode->i_mutex); + retval = fasync_helper(fd, filp, on, &event->fasync); + mutex_unlock(&inode->i_mutex); + + if (retval < 0) + return retval; + + return 0; +} + +static const struct file_operations perf_fops = { + .release = perf_release, + .read = perf_read, + .poll = perf_poll, + .unlocked_ioctl = perf_ioctl, + .compat_ioctl = perf_ioctl, + .mmap = perf_mmap, + .fasync = perf_fasync, +}; + +/* + * Perf event wakeup + * + * If there's data, ensure we set the poll() state and publish everything + * to user-space before waking everybody up. + */ + +void perf_event_wakeup(struct perf_event *event) +{ + wake_up_all(&event->waitq); + + if (event->pending_kill) { + kill_fasync(&event->fasync, SIGIO, event->pending_kill); + event->pending_kill = 0; + } +} + +/* + * Pending wakeups + * + * Handle the case where we need to wakeup up from NMI (or rq->lock) context. + * + * The NMI bit means we cannot possibly take locks. Therefore, maintain a + * single linked list and use cmpxchg() to add entries lockless. + */ + +static void perf_pending_event(struct perf_pending_entry *entry) +{ + struct perf_event *event = container_of(entry, + struct perf_event, pending); + + if (event->pending_disable) { + event->pending_disable = 0; + __perf_event_disable(event); + } + + if (event->pending_wakeup) { + event->pending_wakeup = 0; + perf_event_wakeup(event); + } +} + +#define PENDING_TAIL ((struct perf_pending_entry *)-1UL) + +static DEFINE_PER_CPU(struct perf_pending_entry *, perf_pending_head) = { + PENDING_TAIL, +}; + +static void perf_pending_queue(struct perf_pending_entry *entry, + void (*func)(struct perf_pending_entry *)) +{ + struct perf_pending_entry **head; + + if (cmpxchg(&entry->next, NULL, PENDING_TAIL) != NULL) + return; + + entry->func = func; + + head = &get_cpu_var(perf_pending_head); + + do { + entry->next = *head; + } while (cmpxchg(head, entry->next, entry) != entry->next); + + set_perf_event_pending(); + + put_cpu_var(perf_pending_head); +} + +static int __perf_pending_run(void) +{ + struct perf_pending_entry *list; + int nr = 0; + + list = xchg(&__get_cpu_var(perf_pending_head), PENDING_TAIL); + while (list != PENDING_TAIL) { + void (*func)(struct perf_pending_entry *); + struct perf_pending_entry *entry = list; + + list = list->next; + + func = entry->func; + entry->next = NULL; + /* + * Ensure we observe the unqueue before we issue the wakeup, + * so that we won't be waiting forever. + * -- see perf_not_pending(). + */ + smp_wmb(); + + func(entry); + nr++; + } + + return nr; +} + +static inline int perf_not_pending(struct perf_event *event) +{ + /* + * If we flush on whatever cpu we run, there is a chance we don't + * need to wait. + */ + get_cpu(); + __perf_pending_run(); + put_cpu(); + + /* + * Ensure we see the proper queue state before going to sleep + * so that we do not miss the wakeup. -- see perf_pending_handle() + */ + smp_rmb(); + return event->pending.next == NULL; +} + +static void perf_pending_sync(struct perf_event *event) +{ + wait_event(event->waitq, perf_not_pending(event)); +} + +void perf_event_do_pending(void) +{ + __perf_pending_run(); +} + +/* + * Callchain support -- arch specific + */ + +__weak struct perf_callchain_entry *perf_callchain(struct pt_regs *regs) +{ + return NULL; +} + +/* + * Output + */ +static bool perf_output_space(struct perf_mmap_data *data, unsigned long tail, + unsigned long offset, unsigned long head) +{ + unsigned long mask; + + if (!data->writable) + return true; + + mask = (data->nr_pages << PAGE_SHIFT) - 1; + + offset = (offset - tail) & mask; + head = (head - tail) & mask; + + if ((int)(head - offset) < 0) + return false; + + return true; +} + +static void perf_output_wakeup(struct perf_output_handle *handle) +{ + atomic_set(&handle->data->poll, POLL_IN); + + if (handle->nmi) { + handle->event->pending_wakeup = 1; + perf_pending_queue(&handle->event->pending, + perf_pending_event); + } else + perf_event_wakeup(handle->event); +} + +/* + * Curious locking construct. + * + * We need to ensure a later event_id doesn't publish a head when a former + * event_id isn't done writing. However since we need to deal with NMIs we + * cannot fully serialize things. + * + * What we do is serialize between CPUs so we only have to deal with NMI + * nesting on a single CPU. + * + * We only publish the head (and generate a wakeup) when the outer-most + * event_id completes. + */ +static void perf_output_lock(struct perf_output_handle *handle) +{ + struct perf_mmap_data *data = handle->data; + int cpu; + + handle->locked = 0; + + local_irq_save(handle->flags); + cpu = smp_processor_id(); + + if (in_nmi() && atomic_read(&data->lock) == cpu) + return; + + while (atomic_cmpxchg(&data->lock, -1, cpu) != -1) + cpu_relax(); + + handle->locked = 1; +} + +static void perf_output_unlock(struct perf_output_handle *handle) +{ + struct perf_mmap_data *data = handle->data; + unsigned long head; + int cpu; + + data->done_head = data->head; + + if (!handle->locked) + goto out; + +again: + /* + * The xchg implies a full barrier that ensures all writes are done + * before we publish the new head, matched by a rmb() in userspace when + * reading this position. + */ + while ((head = atomic_long_xchg(&data->done_head, 0))) + data->user_page->data_head = head; + + /* + * NMI can happen here, which means we can miss a done_head update. + */ + + cpu = atomic_xchg(&data->lock, -1); + WARN_ON_ONCE(cpu != smp_processor_id()); + + /* + * Therefore we have to validate we did not indeed do so. + */ + if (unlikely(atomic_long_read(&data->done_head))) { + /* + * Since we had it locked, we can lock it again. + */ + while (atomic_cmpxchg(&data->lock, -1, cpu) != -1) + cpu_relax(); + + goto again; + } + + if (atomic_xchg(&data->wakeup, 0)) + perf_output_wakeup(handle); +out: + local_irq_restore(handle->flags); +} + +void perf_output_copy(struct perf_output_handle *handle, + const void *buf, unsigned int len) +{ + unsigned int pages_mask; + unsigned int offset; + unsigned int size; + void **pages; + + offset = handle->offset; + pages_mask = handle->data->nr_pages - 1; + pages = handle->data->data_pages; + + do { + unsigned int page_offset; + int nr; + + nr = (offset >> PAGE_SHIFT) & pages_mask; + page_offset = offset & (PAGE_SIZE - 1); + size = min_t(unsigned int, PAGE_SIZE - page_offset, len); + + memcpy(pages[nr] + page_offset, buf, size); + + len -= size; + buf += size; + offset += size; + } while (len); + + handle->offset = offset; + + /* + * Check we didn't copy past our reservation window, taking the + * possible unsigned int wrap into account. + */ + WARN_ON_ONCE(((long)(handle->head - handle->offset)) < 0); +} + +int perf_output_begin(struct perf_output_handle *handle, + struct perf_event *event, unsigned int size, + int nmi, int sample) +{ + struct perf_event *output_event; + struct perf_mmap_data *data; + unsigned long tail, offset, head; + int have_lost; + struct { + struct perf_event_header header; + u64 id; + u64 lost; + } lost_event; + + rcu_read_lock(); + /* + * For inherited events we send all the output towards the parent. + */ + if (event->parent) + event = event->parent; + + output_event = rcu_dereference(event->output); + if (output_event) + event = output_event; + + data = rcu_dereference(event->data); + if (!data) + goto out; + + handle->data = data; + handle->event = event; + handle->nmi = nmi; + handle->sample = sample; + + if (!data->nr_pages) + goto fail; + + have_lost = atomic_read(&data->lost); + if (have_lost) + size += sizeof(lost_event); + + perf_output_lock(handle); + + do { + /* + * Userspace could choose to issue a mb() before updating the + * tail pointer. So that all reads will be completed before the + * write is issued. + */ + tail = ACCESS_ONCE(data->user_page->data_tail); + smp_rmb(); + offset = head = atomic_long_read(&data->head); + head += size; + if (unlikely(!perf_output_space(data, tail, offset, head))) + goto fail; + } while (atomic_long_cmpxchg(&data->head, offset, head) != offset); + + handle->offset = offset; + handle->head = head; + + if (head - tail > data->watermark) + atomic_set(&data->wakeup, 1); + + if (have_lost) { + lost_event.header.type = PERF_RECORD_LOST; + lost_event.header.misc = 0; + lost_event.header.size = sizeof(lost_event); + lost_event.id = event->id; + lost_event.lost = atomic_xchg(&data->lost, 0); + + perf_output_put(handle, lost_event); + } + + return 0; + +fail: + atomic_inc(&data->lost); + perf_output_unlock(handle); +out: + rcu_read_unlock(); + + return -ENOSPC; +} + +void perf_output_end(struct perf_output_handle *handle) +{ + struct perf_event *event = handle->event; + struct perf_mmap_data *data = handle->data; + + int wakeup_events = event->attr.wakeup_events; + + if (handle->sample && wakeup_events) { + int events = atomic_inc_return(&data->events); + if (events >= wakeup_events) { + atomic_sub(wakeup_events, &data->events); + atomic_set(&data->wakeup, 1); + } + } + + perf_output_unlock(handle); + rcu_read_unlock(); +} + +static u32 perf_event_pid(struct perf_event *event, struct task_struct *p) +{ + /* + * only top level events have the pid namespace they were created in + */ + if (event->parent) + event = event->parent; + + return task_tgid_nr_ns(p, event->ns); +} + +static u32 perf_event_tid(struct perf_event *event, struct task_struct *p) +{ + /* + * only top level events have the pid namespace they were created in + */ + if (event->parent) + event = event->parent; + + return task_pid_nr_ns(p, event->ns); +} + +static void perf_output_read_one(struct perf_output_handle *handle, + struct perf_event *event) +{ + u64 read_format = event->attr.read_format; + u64 values[4]; + int n = 0; + + values[n++] = atomic64_read(&event->count); + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) { + values[n++] = event->total_time_enabled + + atomic64_read(&event->child_total_time_enabled); + } + if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) { + values[n++] = event->total_time_running + + atomic64_read(&event->child_total_time_running); + } + if (read_format & PERF_FORMAT_ID) + values[n++] = primary_event_id(event); + + perf_output_copy(handle, values, n * sizeof(u64)); +} + +/* + * XXX PERF_FORMAT_GROUP vs inherited events seems difficult. + */ +static void perf_output_read_group(struct perf_output_handle *handle, + struct perf_event *event) +{ + struct perf_event *leader = event->group_leader, *sub; + u64 read_format = event->attr.read_format; + u64 values[5]; + int n = 0; + + values[n++] = 1 + leader->nr_siblings; + + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + values[n++] = leader->total_time_enabled; + + if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + values[n++] = leader->total_time_running; + + if (leader != event) + leader->pmu->read(leader); + + values[n++] = atomic64_read(&leader->count); + if (read_format & PERF_FORMAT_ID) + values[n++] = primary_event_id(leader); + + perf_output_copy(handle, values, n * sizeof(u64)); + + list_for_each_entry(sub, &leader->sibling_list, group_entry) { + n = 0; + + if (sub != event) + sub->pmu->read(sub); + + values[n++] = atomic64_read(&sub->count); + if (read_format & PERF_FORMAT_ID) + values[n++] = primary_event_id(sub); + + perf_output_copy(handle, values, n * sizeof(u64)); + } +} + +static void perf_output_read(struct perf_output_handle *handle, + struct perf_event *event) +{ + if (event->attr.read_format & PERF_FORMAT_GROUP) + perf_output_read_group(handle, event); + else + perf_output_read_one(handle, event); +} + +void perf_output_sample(struct perf_output_handle *handle, + struct perf_event_header *header, + struct perf_sample_data *data, + struct perf_event *event) +{ + u64 sample_type = data->type; + + perf_output_put(handle, *header); + + if (sample_type & PERF_SAMPLE_IP) + perf_output_put(handle, data->ip); + + if (sample_type & PERF_SAMPLE_TID) + perf_output_put(handle, data->tid_entry); + + if (sample_type & PERF_SAMPLE_TIME) + perf_output_put(handle, data->time); + + if (sample_type & PERF_SAMPLE_ADDR) + perf_output_put(handle, data->addr); + + if (sample_type & PERF_SAMPLE_ID) + perf_output_put(handle, data->id); + + if (sample_type & PERF_SAMPLE_STREAM_ID) + perf_output_put(handle, data->stream_id); + + if (sample_type & PERF_SAMPLE_CPU) + perf_output_put(handle, data->cpu_entry); + + if (sample_type & PERF_SAMPLE_PERIOD) + perf_output_put(handle, data->period); + + if (sample_type & PERF_SAMPLE_READ) + perf_output_read(handle, event); + + if (sample_type & PERF_SAMPLE_CALLCHAIN) { + if (data->callchain) { + int size = 1; + + if (data->callchain) + size += data->callchain->nr; + + size *= sizeof(u64); + + perf_output_copy(handle, data->callchain, size); + } else { + u64 nr = 0; + perf_output_put(handle, nr); + } + } + + if (sample_type & PERF_SAMPLE_RAW) { + if (data->raw) { + perf_output_put(handle, data->raw->size); + perf_output_copy(handle, data->raw->data, + data->raw->size); + } else { + struct { + u32 size; + u32 data; + } raw = { + .size = sizeof(u32), + .data = 0, + }; + perf_output_put(handle, raw); + } + } +} + +void perf_prepare_sample(struct perf_event_header *header, + struct perf_sample_data *data, + struct perf_event *event, + struct pt_regs *regs) +{ + u64 sample_type = event->attr.sample_type; + + data->type = sample_type; + + header->type = PERF_RECORD_SAMPLE; + header->size = sizeof(*header); + + header->misc = 0; + header->misc |= perf_misc_flags(regs); + + if (sample_type & PERF_SAMPLE_IP) { + data->ip = perf_instruction_pointer(regs); + + header->size += sizeof(data->ip); + } + + if (sample_type & PERF_SAMPLE_TID) { + /* namespace issues */ + data->tid_entry.pid = perf_event_pid(event, current); + data->tid_entry.tid = perf_event_tid(event, current); + + header->size += sizeof(data->tid_entry); + } + + if (sample_type & PERF_SAMPLE_TIME) { + data->time = perf_clock(); + + header->size += sizeof(data->time); + } + + if (sample_type & PERF_SAMPLE_ADDR) + header->size += sizeof(data->addr); + + if (sample_type & PERF_SAMPLE_ID) { + data->id = primary_event_id(event); + + header->size += sizeof(data->id); + } + + if (sample_type & PERF_SAMPLE_STREAM_ID) { + data->stream_id = event->id; + + header->size += sizeof(data->stream_id); + } + + if (sample_type & PERF_SAMPLE_CPU) { + data->cpu_entry.cpu = raw_smp_processor_id(); + data->cpu_entry.reserved = 0; + + header->size += sizeof(data->cpu_entry); + } + + if (sample_type & PERF_SAMPLE_PERIOD) + header->size += sizeof(data->period); + + if (sample_type & PERF_SAMPLE_READ) + header->size += perf_event_read_size(event); + + if (sample_type & PERF_SAMPLE_CALLCHAIN) { + int size = 1; + + data->callchain = perf_callchain(regs); + + if (data->callchain) + size += data->callchain->nr; + + header->size += size * sizeof(u64); + } + + if (sample_type & PERF_SAMPLE_RAW) { + int size = sizeof(u32); + + if (data->raw) + size += data->raw->size; + else + size += sizeof(u32); + + WARN_ON_ONCE(size & (sizeof(u64)-1)); + header->size += size; + } +} + +static void perf_event_output(struct perf_event *event, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct perf_output_handle handle; + struct perf_event_header header; + + perf_prepare_sample(&header, data, event, regs); + + if (perf_output_begin(&handle, event, header.size, nmi, 1)) + return; + + perf_output_sample(&handle, &header, data, event); + + perf_output_end(&handle); +} + +/* + * read event_id + */ + +struct perf_read_event { + struct perf_event_header header; + + u32 pid; + u32 tid; +}; + +static void +perf_event_read_event(struct perf_event *event, + struct task_struct *task) +{ + struct perf_output_handle handle; + struct perf_read_event read_event = { + .header = { + .type = PERF_RECORD_READ, + .misc = 0, + .size = sizeof(read_event) + perf_event_read_size(event), + }, + .pid = perf_event_pid(event, task), + .tid = perf_event_tid(event, task), + }; + int ret; + + ret = perf_output_begin(&handle, event, read_event.header.size, 0, 0); + if (ret) + return; + + perf_output_put(&handle, read_event); + perf_output_read(&handle, event); + + perf_output_end(&handle); +} + +/* + * task tracking -- fork/exit + * + * enabled by: attr.comm | attr.mmap | attr.task + */ + +struct perf_task_event { + struct task_struct *task; + struct perf_event_context *task_ctx; + + struct { + struct perf_event_header header; + + u32 pid; + u32 ppid; + u32 tid; + u32 ptid; + u64 time; + } event_id; +}; + +static void perf_event_task_output(struct perf_event *event, + struct perf_task_event *task_event) +{ + struct perf_output_handle handle; + int size; + struct task_struct *task = task_event->task; + int ret; + + size = task_event->event_id.header.size; + ret = perf_output_begin(&handle, event, size, 0, 0); + + if (ret) + return; + + task_event->event_id.pid = perf_event_pid(event, task); + task_event->event_id.ppid = perf_event_pid(event, current); + + task_event->event_id.tid = perf_event_tid(event, task); + task_event->event_id.ptid = perf_event_tid(event, current); + + task_event->event_id.time = perf_clock(); + + perf_output_put(&handle, task_event->event_id); + + perf_output_end(&handle); +} + +static int perf_event_task_match(struct perf_event *event) +{ + if (event->attr.comm || event->attr.mmap || event->attr.task) + return 1; + + return 0; +} + +static void perf_event_task_ctx(struct perf_event_context *ctx, + struct perf_task_event *task_event) +{ + struct perf_event *event; + + if (system_state != SYSTEM_RUNNING || list_empty(&ctx->event_list)) + return; + + rcu_read_lock(); + list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { + if (perf_event_task_match(event)) + perf_event_task_output(event, task_event); + } + rcu_read_unlock(); +} + +static void perf_event_task_event(struct perf_task_event *task_event) +{ + struct perf_cpu_context *cpuctx; + struct perf_event_context *ctx = task_event->task_ctx; + + cpuctx = &get_cpu_var(perf_cpu_context); + perf_event_task_ctx(&cpuctx->ctx, task_event); + put_cpu_var(perf_cpu_context); + + rcu_read_lock(); + if (!ctx) + ctx = rcu_dereference(task_event->task->perf_event_ctxp); + if (ctx) + perf_event_task_ctx(ctx, task_event); + rcu_read_unlock(); +} + +static void perf_event_task(struct task_struct *task, + struct perf_event_context *task_ctx, + int new) +{ + struct perf_task_event task_event; + + if (!atomic_read(&nr_comm_events) && + !atomic_read(&nr_mmap_events) && + !atomic_read(&nr_task_events)) + return; + + task_event = (struct perf_task_event){ + .task = task, + .task_ctx = task_ctx, + .event_id = { + .header = { + .type = new ? PERF_RECORD_FORK : PERF_RECORD_EXIT, + .misc = 0, + .size = sizeof(task_event.event_id), + }, + /* .pid */ + /* .ppid */ + /* .tid */ + /* .ptid */ + }, + }; + + perf_event_task_event(&task_event); +} + +void perf_event_fork(struct task_struct *task) +{ + perf_event_task(task, NULL, 1); +} + +/* + * comm tracking + */ + +struct perf_comm_event { + struct task_struct *task; + char *comm; + int comm_size; + + struct { + struct perf_event_header header; + + u32 pid; + u32 tid; + } event_id; +}; + +static void perf_event_comm_output(struct perf_event *event, + struct perf_comm_event *comm_event) +{ + struct perf_output_handle handle; + int size = comm_event->event_id.header.size; + int ret = perf_output_begin(&handle, event, size, 0, 0); + + if (ret) + return; + + comm_event->event_id.pid = perf_event_pid(event, comm_event->task); + comm_event->event_id.tid = perf_event_tid(event, comm_event->task); + + perf_output_put(&handle, comm_event->event_id); + perf_output_copy(&handle, comm_event->comm, + comm_event->comm_size); + perf_output_end(&handle); +} + +static int perf_event_comm_match(struct perf_event *event) +{ + if (event->attr.comm) + return 1; + + return 0; +} + +static void perf_event_comm_ctx(struct perf_event_context *ctx, + struct perf_comm_event *comm_event) +{ + struct perf_event *event; + + if (system_state != SYSTEM_RUNNING || list_empty(&ctx->event_list)) + return; + + rcu_read_lock(); + list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { + if (perf_event_comm_match(event)) + perf_event_comm_output(event, comm_event); + } + rcu_read_unlock(); +} + +static void perf_event_comm_event(struct perf_comm_event *comm_event) +{ + struct perf_cpu_context *cpuctx; + struct perf_event_context *ctx; + unsigned int size; + char comm[TASK_COMM_LEN]; + + memset(comm, 0, sizeof(comm)); + strncpy(comm, comm_event->task->comm, sizeof(comm)); + size = ALIGN(strlen(comm)+1, sizeof(u64)); + + comm_event->comm = comm; + comm_event->comm_size = size; + + comm_event->event_id.header.size = sizeof(comm_event->event_id) + size; + + cpuctx = &get_cpu_var(perf_cpu_context); + perf_event_comm_ctx(&cpuctx->ctx, comm_event); + put_cpu_var(perf_cpu_context); + + rcu_read_lock(); + /* + * doesn't really matter which of the child contexts the + * events ends up in. + */ + ctx = rcu_dereference(current->perf_event_ctxp); + if (ctx) + perf_event_comm_ctx(ctx, comm_event); + rcu_read_unlock(); +} + +void perf_event_comm(struct task_struct *task) +{ + struct perf_comm_event comm_event; + + if (task->perf_event_ctxp) + perf_event_enable_on_exec(task); + + if (!atomic_read(&nr_comm_events)) + return; + + comm_event = (struct perf_comm_event){ + .task = task, + /* .comm */ + /* .comm_size */ + .event_id = { + .header = { + .type = PERF_RECORD_COMM, + .misc = 0, + /* .size */ + }, + /* .pid */ + /* .tid */ + }, + }; + + perf_event_comm_event(&comm_event); +} + +/* + * mmap tracking + */ + +struct perf_mmap_event { + struct vm_area_struct *vma; + + const char *file_name; + int file_size; + + struct { + struct perf_event_header header; + + u32 pid; + u32 tid; + u64 start; + u64 len; + u64 pgoff; + } event_id; +}; + +static void perf_event_mmap_output(struct perf_event *event, + struct perf_mmap_event *mmap_event) +{ + struct perf_output_handle handle; + int size = mmap_event->event_id.header.size; + int ret = perf_output_begin(&handle, event, size, 0, 0); + + if (ret) + return; + + mmap_event->event_id.pid = perf_event_pid(event, current); + mmap_event->event_id.tid = perf_event_tid(event, current); + + perf_output_put(&handle, mmap_event->event_id); + perf_output_copy(&handle, mmap_event->file_name, + mmap_event->file_size); + perf_output_end(&handle); +} + +static int perf_event_mmap_match(struct perf_event *event, + struct perf_mmap_event *mmap_event) +{ + if (event->attr.mmap) + return 1; + + return 0; +} + +static void perf_event_mmap_ctx(struct perf_event_context *ctx, + struct perf_mmap_event *mmap_event) +{ + struct perf_event *event; + + if (system_state != SYSTEM_RUNNING || list_empty(&ctx->event_list)) + return; + + rcu_read_lock(); + list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { + if (perf_event_mmap_match(event, mmap_event)) + perf_event_mmap_output(event, mmap_event); + } + rcu_read_unlock(); +} + +static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) +{ + struct perf_cpu_context *cpuctx; + struct perf_event_context *ctx; + struct vm_area_struct *vma = mmap_event->vma; + struct file *file = vma->vm_file; + unsigned int size; + char tmp[16]; + char *buf = NULL; + const char *name; + + memset(tmp, 0, sizeof(tmp)); + + if (file) { + /* + * d_path works from the end of the buffer backwards, so we + * need to add enough zero bytes after the string to handle + * the 64bit alignment we do later. + */ + buf = kzalloc(PATH_MAX + sizeof(u64), GFP_KERNEL); + if (!buf) { + name = strncpy(tmp, "//enomem", sizeof(tmp)); + goto got_name; + } + name = d_path(&file->f_path, buf, PATH_MAX); + if (IS_ERR(name)) { + name = strncpy(tmp, "//toolong", sizeof(tmp)); + goto got_name; + } + } else { + if (arch_vma_name(mmap_event->vma)) { + name = strncpy(tmp, arch_vma_name(mmap_event->vma), + sizeof(tmp)); + goto got_name; + } + + if (!vma->vm_mm) { + name = strncpy(tmp, "[vdso]", sizeof(tmp)); + goto got_name; + } + + name = strncpy(tmp, "//anon", sizeof(tmp)); + goto got_name; + } + +got_name: + size = ALIGN(strlen(name)+1, sizeof(u64)); + + mmap_event->file_name = name; + mmap_event->file_size = size; + + mmap_event->event_id.header.size = sizeof(mmap_event->event_id) + size; + + cpuctx = &get_cpu_var(perf_cpu_context); + perf_event_mmap_ctx(&cpuctx->ctx, mmap_event); + put_cpu_var(perf_cpu_context); + + rcu_read_lock(); + /* + * doesn't really matter which of the child contexts the + * events ends up in. + */ + ctx = rcu_dereference(current->perf_event_ctxp); + if (ctx) + perf_event_mmap_ctx(ctx, mmap_event); + rcu_read_unlock(); + + kfree(buf); +} + +void __perf_event_mmap(struct vm_area_struct *vma) +{ + struct perf_mmap_event mmap_event; + + if (!atomic_read(&nr_mmap_events)) + return; + + mmap_event = (struct perf_mmap_event){ + .vma = vma, + /* .file_name */ + /* .file_size */ + .event_id = { + .header = { + .type = PERF_RECORD_MMAP, + .misc = 0, + /* .size */ + }, + /* .pid */ + /* .tid */ + .start = vma->vm_start, + .len = vma->vm_end - vma->vm_start, + .pgoff = vma->vm_pgoff, + }, + }; + + perf_event_mmap_event(&mmap_event); +} + +/* + * IRQ throttle logging + */ + +static void perf_log_throttle(struct perf_event *event, int enable) +{ + struct perf_output_handle handle; + int ret; + + struct { + struct perf_event_header header; + u64 time; + u64 id; + u64 stream_id; + } throttle_event = { + .header = { + .type = PERF_RECORD_THROTTLE, + .misc = 0, + .size = sizeof(throttle_event), + }, + .time = perf_clock(), + .id = primary_event_id(event), + .stream_id = event->id, + }; + + if (enable) + throttle_event.header.type = PERF_RECORD_UNTHROTTLE; + + ret = perf_output_begin(&handle, event, sizeof(throttle_event), 1, 0); + if (ret) + return; + + perf_output_put(&handle, throttle_event); + perf_output_end(&handle); +} + +/* + * Generic event overflow handling, sampling. + */ + +static int __perf_event_overflow(struct perf_event *event, int nmi, + int throttle, struct perf_sample_data *data, + struct pt_regs *regs) +{ + int events = atomic_read(&event->event_limit); + struct hw_perf_event *hwc = &event->hw; + int ret = 0; + + throttle = (throttle && event->pmu->unthrottle != NULL); + + if (!throttle) { + hwc->interrupts++; + } else { + if (hwc->interrupts != MAX_INTERRUPTS) { + hwc->interrupts++; + if (HZ * hwc->interrupts > + (u64)sysctl_perf_event_sample_rate) { + hwc->interrupts = MAX_INTERRUPTS; + perf_log_throttle(event, 0); + ret = 1; + } + } else { + /* + * Keep re-disabling events even though on the previous + * pass we disabled it - just in case we raced with a + * sched-in and the event got enabled again: + */ + ret = 1; + } + } + + if (event->attr.freq) { + u64 now = perf_clock(); + s64 delta = now - hwc->freq_stamp; + + hwc->freq_stamp = now; + + if (delta > 0 && delta < TICK_NSEC) + perf_adjust_period(event, NSEC_PER_SEC / (int)delta); + } + + /* + * XXX event_limit might not quite work as expected on inherited + * events + */ + + event->pending_kill = POLL_IN; + if (events && atomic_dec_and_test(&event->event_limit)) { + ret = 1; + event->pending_kill = POLL_HUP; + if (nmi) { + event->pending_disable = 1; + perf_pending_queue(&event->pending, + perf_pending_event); + } else + perf_event_disable(event); + } + + perf_event_output(event, nmi, data, regs); + return ret; +} + +int perf_event_overflow(struct perf_event *event, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + return __perf_event_overflow(event, nmi, 1, data, regs); +} + +/* + * Generic software event infrastructure + */ + +/* + * We directly increment event->count and keep a second value in + * event->hw.period_left to count intervals. This period event + * is kept in the range [-sample_period, 0] so that we can use the + * sign as trigger. + */ + +static u64 perf_swevent_set_period(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + u64 period = hwc->last_period; + u64 nr, offset; + s64 old, val; + + hwc->last_period = hwc->sample_period; + +again: + old = val = atomic64_read(&hwc->period_left); + if (val < 0) + return 0; + + nr = div64_u64(period + val, period); + offset = nr * period; + val -= offset; + if (atomic64_cmpxchg(&hwc->period_left, old, val) != old) + goto again; + + return nr; +} + +static void perf_swevent_overflow(struct perf_event *event, + int nmi, struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct hw_perf_event *hwc = &event->hw; + int throttle = 0; + u64 overflow; + + data->period = event->hw.last_period; + overflow = perf_swevent_set_period(event); + + if (hwc->interrupts == MAX_INTERRUPTS) + return; + + for (; overflow; overflow--) { + if (__perf_event_overflow(event, nmi, throttle, + data, regs)) { + /* + * We inhibit the overflow from happening when + * hwc->interrupts == MAX_INTERRUPTS. + */ + break; + } + throttle = 1; + } +} + +static void perf_swevent_unthrottle(struct perf_event *event) +{ + /* + * Nothing to do, we already reset hwc->interrupts. + */ +} + +static void perf_swevent_add(struct perf_event *event, u64 nr, + int nmi, struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct hw_perf_event *hwc = &event->hw; + + atomic64_add(nr, &event->count); + + if (!hwc->sample_period) + return; + + if (!regs) + return; + + if (!atomic64_add_negative(nr, &hwc->period_left)) + perf_swevent_overflow(event, nmi, data, regs); +} + +static int perf_swevent_is_counting(struct perf_event *event) +{ + /* + * The event is active, we're good! + */ + if (event->state == PERF_EVENT_STATE_ACTIVE) + return 1; + + /* + * The event is off/error, not counting. + */ + if (event->state != PERF_EVENT_STATE_INACTIVE) + return 0; + + /* + * The event is inactive, if the context is active + * we're part of a group that didn't make it on the 'pmu', + * not counting. + */ + if (event->ctx->is_active) + return 0; + + /* + * We're inactive and the context is too, this means the + * task is scheduled out, we're counting events that happen + * to us, like migration events. + */ + return 1; +} + +static int perf_swevent_match(struct perf_event *event, + enum perf_type_id type, + u32 event_id, struct pt_regs *regs) +{ + if (!perf_swevent_is_counting(event)) + return 0; + + if (event->attr.type != type) + return 0; + if (event->attr.config != event_id) + return 0; + + if (regs) { + if (event->attr.exclude_user && user_mode(regs)) + return 0; + + if (event->attr.exclude_kernel && !user_mode(regs)) + return 0; + } + + return 1; +} + +static void perf_swevent_ctx_event(struct perf_event_context *ctx, + enum perf_type_id type, + u32 event_id, u64 nr, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct perf_event *event; + + if (system_state != SYSTEM_RUNNING || list_empty(&ctx->event_list)) + return; + + rcu_read_lock(); + list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { + if (perf_swevent_match(event, type, event_id, regs)) + perf_swevent_add(event, nr, nmi, data, regs); + } + rcu_read_unlock(); +} + +static int *perf_swevent_recursion_context(struct perf_cpu_context *cpuctx) +{ + if (in_nmi()) + return &cpuctx->recursion[3]; + + if (in_irq()) + return &cpuctx->recursion[2]; + + if (in_softirq()) + return &cpuctx->recursion[1]; + + return &cpuctx->recursion[0]; +} + +static void do_perf_sw_event(enum perf_type_id type, u32 event_id, + u64 nr, int nmi, + struct perf_sample_data *data, + struct pt_regs *regs) +{ + struct perf_cpu_context *cpuctx = &get_cpu_var(perf_cpu_context); + int *recursion = perf_swevent_recursion_context(cpuctx); + struct perf_event_context *ctx; + + if (*recursion) + goto out; + + (*recursion)++; + barrier(); + + perf_swevent_ctx_event(&cpuctx->ctx, type, event_id, + nr, nmi, data, regs); + rcu_read_lock(); + /* + * doesn't really matter which of the child contexts the + * events ends up in. + */ + ctx = rcu_dereference(current->perf_event_ctxp); + if (ctx) + perf_swevent_ctx_event(ctx, type, event_id, nr, nmi, data, regs); + rcu_read_unlock(); + + barrier(); + (*recursion)--; + +out: + put_cpu_var(perf_cpu_context); +} + +void __perf_sw_event(u32 event_id, u64 nr, int nmi, + struct pt_regs *regs, u64 addr) +{ + struct perf_sample_data data = { + .addr = addr, + }; + + do_perf_sw_event(PERF_TYPE_SOFTWARE, event_id, nr, nmi, + &data, regs); +} + +static void perf_swevent_read(struct perf_event *event) +{ +} + +static int perf_swevent_enable(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + + if (hwc->sample_period) { + hwc->last_period = hwc->sample_period; + perf_swevent_set_period(event); + } + return 0; +} + +static void perf_swevent_disable(struct perf_event *event) +{ +} + +static const struct pmu perf_ops_generic = { + .enable = perf_swevent_enable, + .disable = perf_swevent_disable, + .read = perf_swevent_read, + .unthrottle = perf_swevent_unthrottle, +}; + +/* + * hrtimer based swevent callback + */ + +static enum hrtimer_restart perf_swevent_hrtimer(struct hrtimer *hrtimer) +{ + enum hrtimer_restart ret = HRTIMER_RESTART; + struct perf_sample_data data; + struct pt_regs *regs; + struct perf_event *event; + u64 period; + + event = container_of(hrtimer, struct perf_event, hw.hrtimer); + event->pmu->read(event); + + data.addr = 0; + regs = get_irq_regs(); + /* + * In case we exclude kernel IPs or are somehow not in interrupt + * context, provide the next best thing, the user IP. + */ + if ((event->attr.exclude_kernel || !regs) && + !event->attr.exclude_user) + regs = task_pt_regs(current); + + if (regs) { + if (perf_event_overflow(event, 0, &data, regs)) + ret = HRTIMER_NORESTART; + } + + period = max_t(u64, 10000, event->hw.sample_period); + hrtimer_forward_now(hrtimer, ns_to_ktime(period)); + + return ret; +} + +/* + * Software event: cpu wall time clock + */ + +static void cpu_clock_perf_event_update(struct perf_event *event) +{ + int cpu = raw_smp_processor_id(); + s64 prev; + u64 now; + + now = cpu_clock(cpu); + prev = atomic64_read(&event->hw.prev_count); + atomic64_set(&event->hw.prev_count, now); + atomic64_add(now - prev, &event->count); +} + +static int cpu_clock_perf_event_enable(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + int cpu = raw_smp_processor_id(); + + atomic64_set(&hwc->prev_count, cpu_clock(cpu)); + hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hwc->hrtimer.function = perf_swevent_hrtimer; + if (hwc->sample_period) { + u64 period = max_t(u64, 10000, hwc->sample_period); + __hrtimer_start_range_ns(&hwc->hrtimer, + ns_to_ktime(period), 0, + HRTIMER_MODE_REL, 0); + } + + return 0; +} + +static void cpu_clock_perf_event_disable(struct perf_event *event) +{ + if (event->hw.sample_period) + hrtimer_cancel(&event->hw.hrtimer); + cpu_clock_perf_event_update(event); +} + +static void cpu_clock_perf_event_read(struct perf_event *event) +{ + cpu_clock_perf_event_update(event); +} + +static const struct pmu perf_ops_cpu_clock = { + .enable = cpu_clock_perf_event_enable, + .disable = cpu_clock_perf_event_disable, + .read = cpu_clock_perf_event_read, +}; + +/* + * Software event: task time clock + */ + +static void task_clock_perf_event_update(struct perf_event *event, u64 now) +{ + u64 prev; + s64 delta; + + prev = atomic64_xchg(&event->hw.prev_count, now); + delta = now - prev; + atomic64_add(delta, &event->count); +} + +static int task_clock_perf_event_enable(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; + u64 now; + + now = event->ctx->time; + + atomic64_set(&hwc->prev_count, now); + hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + hwc->hrtimer.function = perf_swevent_hrtimer; + if (hwc->sample_period) { + u64 period = max_t(u64, 10000, hwc->sample_period); + __hrtimer_start_range_ns(&hwc->hrtimer, + ns_to_ktime(period), 0, + HRTIMER_MODE_REL, 0); + } + + return 0; +} + +static void task_clock_perf_event_disable(struct perf_event *event) +{ + if (event->hw.sample_period) + hrtimer_cancel(&event->hw.hrtimer); + task_clock_perf_event_update(event, event->ctx->time); + +} + +static void task_clock_perf_event_read(struct perf_event *event) +{ + u64 time; + + if (!in_nmi()) { + update_context_time(event->ctx); + time = event->ctx->time; + } else { + u64 now = perf_clock(); + u64 delta = now - event->ctx->timestamp; + time = event->ctx->time + delta; + } + + task_clock_perf_event_update(event, time); +} + +static const struct pmu perf_ops_task_clock = { + .enable = task_clock_perf_event_enable, + .disable = task_clock_perf_event_disable, + .read = task_clock_perf_event_read, +}; + +#ifdef CONFIG_EVENT_PROFILE +void perf_tp_event(int event_id, u64 addr, u64 count, void *record, + int entry_size) +{ + struct perf_raw_record raw = { + .size = entry_size, + .data = record, + }; + + struct perf_sample_data data = { + .addr = addr, + .raw = &raw, + }; + + struct pt_regs *regs = get_irq_regs(); + + if (!regs) + regs = task_pt_regs(current); + + do_perf_sw_event(PERF_TYPE_TRACEPOINT, event_id, count, 1, + &data, regs); +} +EXPORT_SYMBOL_GPL(perf_tp_event); + +extern int ftrace_profile_enable(int); +extern void ftrace_profile_disable(int); + +static void tp_perf_event_destroy(struct perf_event *event) +{ + ftrace_profile_disable(event->attr.config); +} + +static const struct pmu *tp_perf_event_init(struct perf_event *event) +{ + /* + * Raw tracepoint data is a severe data leak, only allow root to + * have these. + */ + if ((event->attr.sample_type & PERF_SAMPLE_RAW) && + perf_paranoid_tracepoint_raw() && + !capable(CAP_SYS_ADMIN)) + return ERR_PTR(-EPERM); + + if (ftrace_profile_enable(event->attr.config)) + return NULL; + + event->destroy = tp_perf_event_destroy; + + return &perf_ops_generic; +} +#else +static const struct pmu *tp_perf_event_init(struct perf_event *event) +{ + return NULL; +} +#endif + +atomic_t perf_swevent_enabled[PERF_COUNT_SW_MAX]; + +static void sw_perf_event_destroy(struct perf_event *event) +{ + u64 event_id = event->attr.config; + + WARN_ON(event->parent); + + atomic_dec(&perf_swevent_enabled[event_id]); +} + +static const struct pmu *sw_perf_event_init(struct perf_event *event) +{ + const struct pmu *pmu = NULL; + u64 event_id = event->attr.config; + + /* + * Software events (currently) can't in general distinguish + * between user, kernel and hypervisor events. + * However, context switches and cpu migrations are considered + * to be kernel events, and page faults are never hypervisor + * events. + */ + switch (event_id) { + case PERF_COUNT_SW_CPU_CLOCK: + pmu = &perf_ops_cpu_clock; + + break; + case PERF_COUNT_SW_TASK_CLOCK: + /* + * If the user instantiates this as a per-cpu event, + * use the cpu_clock event instead. + */ + if (event->ctx->task) + pmu = &perf_ops_task_clock; + else + pmu = &perf_ops_cpu_clock; + + break; + case PERF_COUNT_SW_PAGE_FAULTS: + case PERF_COUNT_SW_PAGE_FAULTS_MIN: + case PERF_COUNT_SW_PAGE_FAULTS_MAJ: + case PERF_COUNT_SW_CONTEXT_SWITCHES: + case PERF_COUNT_SW_CPU_MIGRATIONS: + if (!event->parent) { + atomic_inc(&perf_swevent_enabled[event_id]); + event->destroy = sw_perf_event_destroy; + } + pmu = &perf_ops_generic; + break; + } + + return pmu; +} + +/* + * Allocate and initialize a event structure + */ +static struct perf_event * +perf_event_alloc(struct perf_event_attr *attr, + int cpu, + struct perf_event_context *ctx, + struct perf_event *group_leader, + struct perf_event *parent_event, + gfp_t gfpflags) +{ + const struct pmu *pmu; + struct perf_event *event; + struct hw_perf_event *hwc; + long err; + + event = kzalloc(sizeof(*event), gfpflags); + if (!event) + return ERR_PTR(-ENOMEM); + + /* + * Single events are their own group leaders, with an + * empty sibling list: + */ + if (!group_leader) + group_leader = event; + + mutex_init(&event->child_mutex); + INIT_LIST_HEAD(&event->child_list); + + INIT_LIST_HEAD(&event->group_entry); + INIT_LIST_HEAD(&event->event_entry); + INIT_LIST_HEAD(&event->sibling_list); + init_waitqueue_head(&event->waitq); + + mutex_init(&event->mmap_mutex); + + event->cpu = cpu; + event->attr = *attr; + event->group_leader = group_leader; + event->pmu = NULL; + event->ctx = ctx; + event->oncpu = -1; + + event->parent = parent_event; + + event->ns = get_pid_ns(current->nsproxy->pid_ns); + event->id = atomic64_inc_return(&perf_event_id); + + event->state = PERF_EVENT_STATE_INACTIVE; + + if (attr->disabled) + event->state = PERF_EVENT_STATE_OFF; + + pmu = NULL; + + hwc = &event->hw; + hwc->sample_period = attr->sample_period; + if (attr->freq && attr->sample_freq) + hwc->sample_period = 1; + hwc->last_period = hwc->sample_period; + + atomic64_set(&hwc->period_left, hwc->sample_period); + + /* + * we currently do not support PERF_FORMAT_GROUP on inherited events + */ + if (attr->inherit && (attr->read_format & PERF_FORMAT_GROUP)) + goto done; + + switch (attr->type) { + case PERF_TYPE_RAW: + case PERF_TYPE_HARDWARE: + case PERF_TYPE_HW_CACHE: + pmu = hw_perf_event_init(event); + break; + + case PERF_TYPE_SOFTWARE: + pmu = sw_perf_event_init(event); + break; + + case PERF_TYPE_TRACEPOINT: + pmu = tp_perf_event_init(event); + break; + + default: + break; + } +done: + err = 0; + if (!pmu) + err = -EINVAL; + else if (IS_ERR(pmu)) + err = PTR_ERR(pmu); + + if (err) { + if (event->ns) + put_pid_ns(event->ns); + kfree(event); + return ERR_PTR(err); + } + + event->pmu = pmu; + + if (!event->parent) { + atomic_inc(&nr_events); + if (event->attr.mmap) + atomic_inc(&nr_mmap_events); + if (event->attr.comm) + atomic_inc(&nr_comm_events); + if (event->attr.task) + atomic_inc(&nr_task_events); + } + + return event; +} + +static int perf_copy_attr(struct perf_event_attr __user *uattr, + struct perf_event_attr *attr) +{ + u32 size; + int ret; + + if (!access_ok(VERIFY_WRITE, uattr, PERF_ATTR_SIZE_VER0)) + return -EFAULT; + + /* + * zero the full structure, so that a short copy will be nice. + */ + memset(attr, 0, sizeof(*attr)); + + ret = get_user(size, &uattr->size); + if (ret) + return ret; + + if (size > PAGE_SIZE) /* silly large */ + goto err_size; + + if (!size) /* abi compat */ + size = PERF_ATTR_SIZE_VER0; + + if (size < PERF_ATTR_SIZE_VER0) + goto err_size; + + /* + * If we're handed a bigger struct than we know of, + * ensure all the unknown bits are 0 - i.e. new + * user-space does not rely on any kernel feature + * extensions we dont know about yet. + */ + if (size > sizeof(*attr)) { + unsigned char __user *addr; + unsigned char __user *end; + unsigned char val; + + addr = (void __user *)uattr + sizeof(*attr); + end = (void __user *)uattr + size; + + for (; addr < end; addr++) { + ret = get_user(val, addr); + if (ret) + return ret; + if (val) + goto err_size; + } + size = sizeof(*attr); + } + + ret = copy_from_user(attr, uattr, size); + if (ret) + return -EFAULT; + + /* + * If the type exists, the corresponding creation will verify + * the attr->config. + */ + if (attr->type >= PERF_TYPE_MAX) + return -EINVAL; + + if (attr->__reserved_1 || attr->__reserved_2 || attr->__reserved_3) + return -EINVAL; + + if (attr->sample_type & ~(PERF_SAMPLE_MAX-1)) + return -EINVAL; + + if (attr->read_format & ~(PERF_FORMAT_MAX-1)) + return -EINVAL; + +out: + return ret; + +err_size: + put_user(sizeof(*attr), &uattr->size); + ret = -E2BIG; + goto out; +} + +int perf_event_set_output(struct perf_event *event, int output_fd) +{ + struct perf_event *output_event = NULL; + struct file *output_file = NULL; + struct perf_event *old_output; + int fput_needed = 0; + int ret = -EINVAL; + + if (!output_fd) + goto set; + + output_file = fget_light(output_fd, &fput_needed); + if (!output_file) + return -EBADF; + + if (output_file->f_op != &perf_fops) + goto out; + + output_event = output_file->private_data; + + /* Don't chain output fds */ + if (output_event->output) + goto out; + + /* Don't set an output fd when we already have an output channel */ + if (event->data) + goto out; + + atomic_long_inc(&output_file->f_count); + +set: + mutex_lock(&event->mmap_mutex); + old_output = event->output; + rcu_assign_pointer(event->output, output_event); + mutex_unlock(&event->mmap_mutex); + + if (old_output) { + /* + * we need to make sure no existing perf_output_*() + * is still referencing this event. + */ + synchronize_rcu(); + fput(old_output->filp); + } + + ret = 0; +out: + fput_light(output_file, fput_needed); + return ret; +} + +/** + * sys_perf_event_open - open a performance event, associate it to a task/cpu + * + * @attr_uptr: event_id type attributes for monitoring/sampling + * @pid: target pid + * @cpu: target cpu + * @group_fd: group leader event fd + */ +SYSCALL_DEFINE5(perf_event_open, + struct perf_event_attr __user *, attr_uptr, + pid_t, pid, int, cpu, int, group_fd, unsigned long, flags) +{ + struct perf_event *event, *group_leader; + struct perf_event_attr attr; + struct perf_event_context *ctx; + struct file *event_file = NULL; + struct file *group_file = NULL; + int fput_needed = 0; + int fput_needed2 = 0; + int err; + + /* for future expandability... */ + if (flags & ~(PERF_FLAG_FD_NO_GROUP | PERF_FLAG_FD_OUTPUT)) + return -EINVAL; + + err = perf_copy_attr(attr_uptr, &attr); + if (err) + return err; + + if (!attr.exclude_kernel) { + if (perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN)) + return -EACCES; + } + + if (attr.freq) { + if (attr.sample_freq > sysctl_perf_event_sample_rate) + return -EINVAL; + } + + /* + * Get the target context (task or percpu): + */ + ctx = find_get_context(pid, cpu); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + /* + * Look up the group leader (we will attach this event to it): + */ + group_leader = NULL; + if (group_fd != -1 && !(flags & PERF_FLAG_FD_NO_GROUP)) { + err = -EINVAL; + group_file = fget_light(group_fd, &fput_needed); + if (!group_file) + goto err_put_context; + if (group_file->f_op != &perf_fops) + goto err_put_context; + + group_leader = group_file->private_data; + /* + * Do not allow a recursive hierarchy (this new sibling + * becoming part of another group-sibling): + */ + if (group_leader->group_leader != group_leader) + goto err_put_context; + /* + * Do not allow to attach to a group in a different + * task or CPU context: + */ + if (group_leader->ctx != ctx) + goto err_put_context; + /* + * Only a group leader can be exclusive or pinned + */ + if (attr.exclusive || attr.pinned) + goto err_put_context; + } + + event = perf_event_alloc(&attr, cpu, ctx, group_leader, + NULL, GFP_KERNEL); + err = PTR_ERR(event); + if (IS_ERR(event)) + goto err_put_context; + + err = anon_inode_getfd("[perf_event]", &perf_fops, event, 0); + if (err < 0) + goto err_free_put_context; + + event_file = fget_light(err, &fput_needed2); + if (!event_file) + goto err_free_put_context; + + if (flags & PERF_FLAG_FD_OUTPUT) { + err = perf_event_set_output(event, group_fd); + if (err) + goto err_fput_free_put_context; + } + + event->filp = event_file; + WARN_ON_ONCE(ctx->parent_ctx); + mutex_lock(&ctx->mutex); + perf_install_in_context(ctx, event, cpu); + ++ctx->generation; + mutex_unlock(&ctx->mutex); + + event->owner = current; + get_task_struct(current); + mutex_lock(¤t->perf_event_mutex); + list_add_tail(&event->owner_entry, ¤t->perf_event_list); + mutex_unlock(¤t->perf_event_mutex); + +err_fput_free_put_context: + fput_light(event_file, fput_needed2); + +err_free_put_context: + if (err < 0) + kfree(event); + +err_put_context: + if (err < 0) + put_ctx(ctx); + + fput_light(group_file, fput_needed); + + return err; +} + +/* + * inherit a event from parent task to child task: + */ +static struct perf_event * +inherit_event(struct perf_event *parent_event, + struct task_struct *parent, + struct perf_event_context *parent_ctx, + struct task_struct *child, + struct perf_event *group_leader, + struct perf_event_context *child_ctx) +{ + struct perf_event *child_event; + + /* + * Instead of creating recursive hierarchies of events, + * we link inherited events back to the original parent, + * which has a filp for sure, which we use as the reference + * count: + */ + if (parent_event->parent) + parent_event = parent_event->parent; + + child_event = perf_event_alloc(&parent_event->attr, + parent_event->cpu, child_ctx, + group_leader, parent_event, + GFP_KERNEL); + if (IS_ERR(child_event)) + return child_event; + get_ctx(child_ctx); + + /* + * Make the child state follow the state of the parent event, + * not its attr.disabled bit. We hold the parent's mutex, + * so we won't race with perf_event_{en, dis}able_family. + */ + if (parent_event->state >= PERF_EVENT_STATE_INACTIVE) + child_event->state = PERF_EVENT_STATE_INACTIVE; + else + child_event->state = PERF_EVENT_STATE_OFF; + + if (parent_event->attr.freq) + child_event->hw.sample_period = parent_event->hw.sample_period; + + /* + * Link it up in the child's context: + */ + add_event_to_ctx(child_event, child_ctx); + + /* + * Get a reference to the parent filp - we will fput it + * when the child event exits. This is safe to do because + * we are in the parent and we know that the filp still + * exists and has a nonzero count: + */ + atomic_long_inc(&parent_event->filp->f_count); + + /* + * Link this into the parent event's child list + */ + WARN_ON_ONCE(parent_event->ctx->parent_ctx); + mutex_lock(&parent_event->child_mutex); + list_add_tail(&child_event->child_list, &parent_event->child_list); + mutex_unlock(&parent_event->child_mutex); + + return child_event; +} + +static int inherit_group(struct perf_event *parent_event, + struct task_struct *parent, + struct perf_event_context *parent_ctx, + struct task_struct *child, + struct perf_event_context *child_ctx) +{ + struct perf_event *leader; + struct perf_event *sub; + struct perf_event *child_ctr; + + leader = inherit_event(parent_event, parent, parent_ctx, + child, NULL, child_ctx); + if (IS_ERR(leader)) + return PTR_ERR(leader); + list_for_each_entry(sub, &parent_event->sibling_list, group_entry) { + child_ctr = inherit_event(sub, parent, parent_ctx, + child, leader, child_ctx); + if (IS_ERR(child_ctr)) + return PTR_ERR(child_ctr); + } + return 0; +} + +static void sync_child_event(struct perf_event *child_event, + struct task_struct *child) +{ + struct perf_event *parent_event = child_event->parent; + u64 child_val; + + if (child_event->attr.inherit_stat) + perf_event_read_event(child_event, child); + + child_val = atomic64_read(&child_event->count); + + /* + * Add back the child's count to the parent's count: + */ + atomic64_add(child_val, &parent_event->count); + atomic64_add(child_event->total_time_enabled, + &parent_event->child_total_time_enabled); + atomic64_add(child_event->total_time_running, + &parent_event->child_total_time_running); + + /* + * Remove this event from the parent's list + */ + WARN_ON_ONCE(parent_event->ctx->parent_ctx); + mutex_lock(&parent_event->child_mutex); + list_del_init(&child_event->child_list); + mutex_unlock(&parent_event->child_mutex); + + /* + * Release the parent event, if this was the last + * reference to it. + */ + fput(parent_event->filp); +} + +static void +__perf_event_exit_task(struct perf_event *child_event, + struct perf_event_context *child_ctx, + struct task_struct *child) +{ + struct perf_event *parent_event; + + update_event_times(child_event); + perf_event_remove_from_context(child_event); + + parent_event = child_event->parent; + /* + * It can happen that parent exits first, and has events + * that are still around due to the child reference. These + * events need to be zapped - but otherwise linger. + */ + if (parent_event) { + sync_child_event(child_event, child); + free_event(child_event); + } +} + +/* + * When a child task exits, feed back event values to parent events. + */ +void perf_event_exit_task(struct task_struct *child) +{ + struct perf_event *child_event, *tmp; + struct perf_event_context *child_ctx; + unsigned long flags; + + if (likely(!child->perf_event_ctxp)) { + perf_event_task(child, NULL, 0); + return; + } + + local_irq_save(flags); + /* + * We can't reschedule here because interrupts are disabled, + * and either child is current or it is a task that can't be + * scheduled, so we are now safe from rescheduling changing + * our context. + */ + child_ctx = child->perf_event_ctxp; + __perf_event_task_sched_out(child_ctx); + + /* + * Take the context lock here so that if find_get_context is + * reading child->perf_event_ctxp, we wait until it has + * incremented the context's refcount before we do put_ctx below. + */ + spin_lock(&child_ctx->lock); + child->perf_event_ctxp = NULL; + /* + * If this context is a clone; unclone it so it can't get + * swapped to another process while we're removing all + * the events from it. + */ + unclone_ctx(child_ctx); + spin_unlock_irqrestore(&child_ctx->lock, flags); + + /* + * Report the task dead after unscheduling the events so that we + * won't get any samples after PERF_RECORD_EXIT. We can however still + * get a few PERF_RECORD_READ events. + */ + perf_event_task(child, child_ctx, 0); + + /* + * We can recurse on the same lock type through: + * + * __perf_event_exit_task() + * sync_child_event() + * fput(parent_event->filp) + * perf_release() + * mutex_lock(&ctx->mutex) + * + * But since its the parent context it won't be the same instance. + */ + mutex_lock_nested(&child_ctx->mutex, SINGLE_DEPTH_NESTING); + +again: + list_for_each_entry_safe(child_event, tmp, &child_ctx->group_list, + group_entry) + __perf_event_exit_task(child_event, child_ctx, child); + + /* + * If the last event was a group event, it will have appended all + * its siblings to the list, but we obtained 'tmp' before that which + * will still point to the list head terminating the iteration. + */ + if (!list_empty(&child_ctx->group_list)) + goto again; + + mutex_unlock(&child_ctx->mutex); + + put_ctx(child_ctx); +} + +/* + * free an unexposed, unused context as created by inheritance by + * init_task below, used by fork() in case of fail. + */ +void perf_event_free_task(struct task_struct *task) +{ + struct perf_event_context *ctx = task->perf_event_ctxp; + struct perf_event *event, *tmp; + + if (!ctx) + return; + + mutex_lock(&ctx->mutex); +again: + list_for_each_entry_safe(event, tmp, &ctx->group_list, group_entry) { + struct perf_event *parent = event->parent; + + if (WARN_ON_ONCE(!parent)) + continue; + + mutex_lock(&parent->child_mutex); + list_del_init(&event->child_list); + mutex_unlock(&parent->child_mutex); + + fput(parent->filp); + + list_del_event(event, ctx); + free_event(event); + } + + if (!list_empty(&ctx->group_list)) + goto again; + + mutex_unlock(&ctx->mutex); + + put_ctx(ctx); +} + +/* + * Initialize the perf_event context in task_struct + */ +int perf_event_init_task(struct task_struct *child) +{ + struct perf_event_context *child_ctx, *parent_ctx; + struct perf_event_context *cloned_ctx; + struct perf_event *event; + struct task_struct *parent = current; + int inherited_all = 1; + int ret = 0; + + child->perf_event_ctxp = NULL; + + mutex_init(&child->perf_event_mutex); + INIT_LIST_HEAD(&child->perf_event_list); + + if (likely(!parent->perf_event_ctxp)) + return 0; + + /* + * This is executed from the parent task context, so inherit + * events that have been marked for cloning. + * First allocate and initialize a context for the child. + */ + + child_ctx = kmalloc(sizeof(struct perf_event_context), GFP_KERNEL); + if (!child_ctx) + return -ENOMEM; + + __perf_event_init_context(child_ctx, child); + child->perf_event_ctxp = child_ctx; + get_task_struct(child); + + /* + * If the parent's context is a clone, pin it so it won't get + * swapped under us. + */ + parent_ctx = perf_pin_task_context(parent); + + /* + * No need to check if parent_ctx != NULL here; since we saw + * it non-NULL earlier, the only reason for it to become NULL + * is if we exit, and since we're currently in the middle of + * a fork we can't be exiting at the same time. + */ + + /* + * Lock the parent list. No need to lock the child - not PID + * hashed yet and not running, so nobody can access it. + */ + mutex_lock(&parent_ctx->mutex); + + /* + * We dont have to disable NMIs - we are only looking at + * the list, not manipulating it: + */ + list_for_each_entry_rcu(event, &parent_ctx->event_list, event_entry) { + if (event != event->group_leader) + continue; + + if (!event->attr.inherit) { + inherited_all = 0; + continue; + } + + ret = inherit_group(event, parent, parent_ctx, + child, child_ctx); + if (ret) { + inherited_all = 0; + break; + } + } + + if (inherited_all) { + /* + * Mark the child context as a clone of the parent + * context, or of whatever the parent is a clone of. + * Note that if the parent is a clone, it could get + * uncloned at any point, but that doesn't matter + * because the list of events and the generation + * count can't have changed since we took the mutex. + */ + cloned_ctx = rcu_dereference(parent_ctx->parent_ctx); + if (cloned_ctx) { + child_ctx->parent_ctx = cloned_ctx; + child_ctx->parent_gen = parent_ctx->parent_gen; + } else { + child_ctx->parent_ctx = parent_ctx; + child_ctx->parent_gen = parent_ctx->generation; + } + get_ctx(child_ctx->parent_ctx); + } + + mutex_unlock(&parent_ctx->mutex); + + perf_unpin_context(parent_ctx); + + return ret; +} + +static void __cpuinit perf_event_init_cpu(int cpu) +{ + struct perf_cpu_context *cpuctx; + + cpuctx = &per_cpu(perf_cpu_context, cpu); + __perf_event_init_context(&cpuctx->ctx, NULL); + + spin_lock(&perf_resource_lock); + cpuctx->max_pertask = perf_max_events - perf_reserved_percpu; + spin_unlock(&perf_resource_lock); + + hw_perf_event_setup(cpu); +} + +#ifdef CONFIG_HOTPLUG_CPU +static void __perf_event_exit_cpu(void *info) +{ + struct perf_cpu_context *cpuctx = &__get_cpu_var(perf_cpu_context); + struct perf_event_context *ctx = &cpuctx->ctx; + struct perf_event *event, *tmp; + + list_for_each_entry_safe(event, tmp, &ctx->group_list, group_entry) + __perf_event_remove_from_context(event); +} +static void perf_event_exit_cpu(int cpu) +{ + struct perf_cpu_context *cpuctx = &per_cpu(perf_cpu_context, cpu); + struct perf_event_context *ctx = &cpuctx->ctx; + + mutex_lock(&ctx->mutex); + smp_call_function_single(cpu, __perf_event_exit_cpu, NULL, 1); + mutex_unlock(&ctx->mutex); +} +#else +static inline void perf_event_exit_cpu(int cpu) { } +#endif + +static int __cpuinit +perf_cpu_notify(struct notifier_block *self, unsigned long action, void *hcpu) +{ + unsigned int cpu = (long)hcpu; + + switch (action) { + + case CPU_UP_PREPARE: + case CPU_UP_PREPARE_FROZEN: + perf_event_init_cpu(cpu); + break; + + case CPU_ONLINE: + case CPU_ONLINE_FROZEN: + hw_perf_event_setup_online(cpu); + break; + + case CPU_DOWN_PREPARE: + case CPU_DOWN_PREPARE_FROZEN: + perf_event_exit_cpu(cpu); + break; + + default: + break; + } + + return NOTIFY_OK; +} + +/* + * This has to have a higher priority than migration_notifier in sched.c. + */ +static struct notifier_block __cpuinitdata perf_cpu_nb = { + .notifier_call = perf_cpu_notify, + .priority = 20, +}; + +void __init perf_event_init(void) +{ + perf_cpu_notify(&perf_cpu_nb, (unsigned long)CPU_UP_PREPARE, + (void *)(long)smp_processor_id()); + perf_cpu_notify(&perf_cpu_nb, (unsigned long)CPU_ONLINE, + (void *)(long)smp_processor_id()); + register_cpu_notifier(&perf_cpu_nb); +} + +static ssize_t perf_show_reserve_percpu(struct sysdev_class *class, char *buf) +{ + return sprintf(buf, "%d\n", perf_reserved_percpu); +} + +static ssize_t +perf_set_reserve_percpu(struct sysdev_class *class, + const char *buf, + size_t count) +{ + struct perf_cpu_context *cpuctx; + unsigned long val; + int err, cpu, mpt; + + err = strict_strtoul(buf, 10, &val); + if (err) + return err; + if (val > perf_max_events) + return -EINVAL; + + spin_lock(&perf_resource_lock); + perf_reserved_percpu = val; + for_each_online_cpu(cpu) { + cpuctx = &per_cpu(perf_cpu_context, cpu); + spin_lock_irq(&cpuctx->ctx.lock); + mpt = min(perf_max_events - cpuctx->ctx.nr_events, + perf_max_events - perf_reserved_percpu); + cpuctx->max_pertask = mpt; + spin_unlock_irq(&cpuctx->ctx.lock); + } + spin_unlock(&perf_resource_lock); + + return count; +} + +static ssize_t perf_show_overcommit(struct sysdev_class *class, char *buf) +{ + return sprintf(buf, "%d\n", perf_overcommit); +} + +static ssize_t +perf_set_overcommit(struct sysdev_class *class, const char *buf, size_t count) +{ + unsigned long val; + int err; + + err = strict_strtoul(buf, 10, &val); + if (err) + return err; + if (val > 1) + return -EINVAL; + + spin_lock(&perf_resource_lock); + perf_overcommit = val; + spin_unlock(&perf_resource_lock); + + return count; +} + +static SYSDEV_CLASS_ATTR( + reserve_percpu, + 0644, + perf_show_reserve_percpu, + perf_set_reserve_percpu + ); + +static SYSDEV_CLASS_ATTR( + overcommit, + 0644, + perf_show_overcommit, + perf_set_overcommit + ); + +static struct attribute *perfclass_attrs[] = { + &attr_reserve_percpu.attr, + &attr_overcommit.attr, + NULL +}; + +static struct attribute_group perfclass_attr_group = { + .attrs = perfclass_attrs, + .name = "perf_events", +}; + +static int __init perf_event_sysfs_init(void) +{ + return sysfs_create_group(&cpu_sysdev_class.kset.kobj, + &perfclass_attr_group); +} +device_initcall(perf_event_sysfs_init); diff --git a/kernel/sched.c b/kernel/sched.c index faf4d463bbff..291c8d213d13 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include #include @@ -2059,7 +2059,7 @@ void set_task_cpu(struct task_struct *p, unsigned int new_cpu) if (task_hot(p, old_rq->clock, NULL)) schedstat_inc(p, se.nr_forced2_migrations); #endif - perf_swcounter_event(PERF_COUNT_SW_CPU_MIGRATIONS, + perf_sw_event(PERF_COUNT_SW_CPU_MIGRATIONS, 1, 1, NULL, 0); } p->se.vruntime -= old_cfsrq->min_vruntime - @@ -2724,7 +2724,7 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev) */ prev_state = prev->state; finish_arch_switch(prev); - perf_counter_task_sched_in(current, cpu_of(rq)); + perf_event_task_sched_in(current, cpu_of(rq)); finish_lock_switch(rq, prev); fire_sched_in_preempt_notifiers(current); @@ -5199,7 +5199,7 @@ void scheduler_tick(void) curr->sched_class->task_tick(rq, curr, 0); spin_unlock(&rq->lock); - perf_counter_task_tick(curr, cpu); + perf_event_task_tick(curr, cpu); #ifdef CONFIG_SMP rq->idle_at_tick = idle_cpu(cpu); @@ -5415,7 +5415,7 @@ need_resched_nonpreemptible: if (likely(prev != next)) { sched_info_switch(prev, next); - perf_counter_task_sched_out(prev, next, cpu); + perf_event_task_sched_out(prev, next, cpu); rq->nr_switches++; rq->curr = next; @@ -7692,7 +7692,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) /* * Register at high priority so that task migration (migrate_all_tasks) * happens before everything else. This has to be lower priority than - * the notifier in the perf_counter subsystem, though. + * the notifier in the perf_event subsystem, though. */ static struct notifier_block __cpuinitdata migration_notifier = { .notifier_call = migration_call, @@ -9549,7 +9549,7 @@ void __init sched_init(void) alloc_cpumask_var(&cpu_isolated_map, GFP_NOWAIT); #endif /* SMP */ - perf_counter_init(); + perf_event_init(); scheduler_running = 1; } diff --git a/kernel/sys.c b/kernel/sys.c index b3f1097c76fa..ea5c3bcac881 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -1511,11 +1511,11 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, case PR_SET_TSC: error = SET_TSC_CTL(arg2); break; - case PR_TASK_PERF_COUNTERS_DISABLE: - error = perf_counter_task_disable(); + case PR_TASK_PERF_EVENTS_DISABLE: + error = perf_event_task_disable(); break; - case PR_TASK_PERF_COUNTERS_ENABLE: - error = perf_counter_task_enable(); + case PR_TASK_PERF_EVENTS_ENABLE: + error = perf_event_task_enable(); break; case PR_GET_TIMERSLACK: error = current->timer_slack_ns; diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 68320f6b07b5..515bc230ac2a 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -177,4 +177,4 @@ cond_syscall(sys_eventfd); cond_syscall(sys_eventfd2); /* performance counters: */ -cond_syscall(sys_perf_counter_open); +cond_syscall(sys_perf_event_open); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 1a631ba684a4..6ba49c7cb128 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include #include @@ -964,28 +964,28 @@ static struct ctl_table kern_table[] = { .child = slow_work_sysctls, }, #endif -#ifdef CONFIG_PERF_COUNTERS +#ifdef CONFIG_PERF_EVENTS { .ctl_name = CTL_UNNUMBERED, - .procname = "perf_counter_paranoid", - .data = &sysctl_perf_counter_paranoid, - .maxlen = sizeof(sysctl_perf_counter_paranoid), + .procname = "perf_event_paranoid", + .data = &sysctl_perf_event_paranoid, + .maxlen = sizeof(sysctl_perf_event_paranoid), .mode = 0644, .proc_handler = &proc_dointvec, }, { .ctl_name = CTL_UNNUMBERED, - .procname = "perf_counter_mlock_kb", - .data = &sysctl_perf_counter_mlock, - .maxlen = sizeof(sysctl_perf_counter_mlock), + .procname = "perf_event_mlock_kb", + .data = &sysctl_perf_event_mlock, + .maxlen = sizeof(sysctl_perf_event_mlock), .mode = 0644, .proc_handler = &proc_dointvec, }, { .ctl_name = CTL_UNNUMBERED, - .procname = "perf_counter_max_sample_rate", - .data = &sysctl_perf_counter_sample_rate, - .maxlen = sizeof(sysctl_perf_counter_sample_rate), + .procname = "perf_event_max_sample_rate", + .data = &sysctl_perf_event_sample_rate, + .maxlen = sizeof(sysctl_perf_event_sample_rate), .mode = 0644, .proc_handler = &proc_dointvec, }, diff --git a/kernel/timer.c b/kernel/timer.c index bbb51074680e..811e5c391456 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include @@ -1187,7 +1187,7 @@ static void run_timer_softirq(struct softirq_action *h) { struct tvec_base *base = __get_cpu_var(tvec_bases); - perf_counter_do_pending(); + perf_event_do_pending(); hrtimer_run_pending(); diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 8712ce3c6a0e..233f3483ac83 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include "trace_output.h" @@ -414,7 +414,7 @@ static void prof_syscall_enter(struct pt_regs *regs, long id) rec->nr = syscall_nr; syscall_get_arguments(current, regs, 0, sys_data->nb_args, (unsigned long *)&rec->args); - perf_tpcounter_event(sys_data->enter_id, 0, 1, rec, size); + perf_tp_event(sys_data->enter_id, 0, 1, rec, size); } while(0); } @@ -476,7 +476,7 @@ static void prof_syscall_exit(struct pt_regs *regs, long ret) rec.nr = syscall_nr; rec.ret = syscall_get_return_value(current, regs); - perf_tpcounter_event(sys_data->exit_id, 0, 1, &rec, sizeof(rec)); + perf_tp_event(sys_data->exit_id, 0, 1, &rec, sizeof(rec)); } int reg_prof_syscall_exit(char *name) diff --git a/mm/mmap.c b/mm/mmap.c index 26892e346d8f..376492ed08f4 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -28,7 +28,7 @@ #include #include #include -#include +#include #include #include @@ -1220,7 +1220,7 @@ munmap_back: if (correct_wcount) atomic_inc(&inode->i_writecount); out: - perf_counter_mmap(vma); + perf_event_mmap(vma); mm->total_vm += len >> PAGE_SHIFT; vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT); @@ -2308,7 +2308,7 @@ int install_special_mapping(struct mm_struct *mm, mm->total_vm += len >> PAGE_SHIFT; - perf_counter_mmap(vma); + perf_event_mmap(vma); return 0; } diff --git a/mm/mprotect.c b/mm/mprotect.c index d80311baeb2d..8bc969d8112d 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -300,7 +300,7 @@ SYSCALL_DEFINE3(mprotect, unsigned long, start, size_t, len, error = mprotect_fixup(vma, &prev, nstart, tmp, newflags); if (error) goto out; - perf_counter_mmap(vma); + perf_event_mmap(vma); nstart = tmp; if (nstart < prev->vm_end) diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 0aba8b6e9c54..b5f1953b6144 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -318,7 +318,7 @@ export PERL_PATH LIB_FILE=libperf.a -LIB_H += ../../include/linux/perf_counter.h +LIB_H += ../../include/linux/perf_event.h LIB_H += ../../include/linux/rbtree.h LIB_H += ../../include/linux/list.h LIB_H += util/include/linux/list.h diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 043d85b7e254..1ec741615814 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -505,7 +505,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) return -1; } - if (event->header.misc & PERF_EVENT_MISC_KERNEL) { + if (event->header.misc & PERF_RECORD_MISC_KERNEL) { show = SHOW_KERNEL; level = 'k'; @@ -513,7 +513,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) dump_printf(" ...... dso: %s\n", dso->name); - } else if (event->header.misc & PERF_EVENT_MISC_USER) { + } else if (event->header.misc & PERF_RECORD_MISC_USER) { show = SHOW_USER; level = '.'; @@ -565,7 +565,7 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) thread = threads__findnew(event->mmap.pid, &threads, &last_match); - dump_printf("%p [%p]: PERF_EVENT_MMAP %d: [%p(%p) @ %p]: %s\n", + dump_printf("%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n", (void *)(offset + head), (void *)(long)(event->header.size), event->mmap.pid, @@ -575,7 +575,7 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) event->mmap.filename); if (thread == NULL || map == NULL) { - dump_printf("problem processing PERF_EVENT_MMAP, skipping event.\n"); + dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); return 0; } @@ -591,14 +591,14 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) struct thread *thread; thread = threads__findnew(event->comm.pid, &threads, &last_match); - dump_printf("%p [%p]: PERF_EVENT_COMM: %s:%d\n", + dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n", (void *)(offset + head), (void *)(long)(event->header.size), event->comm.comm, event->comm.pid); if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { - dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n"); + dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); return -1; } total_comm++; @@ -614,7 +614,7 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) thread = threads__findnew(event->fork.pid, &threads, &last_match); parent = threads__findnew(event->fork.ppid, &threads, &last_match); - dump_printf("%p [%p]: PERF_EVENT_FORK: %d:%d\n", + dump_printf("%p [%p]: PERF_RECORD_FORK: %d:%d\n", (void *)(offset + head), (void *)(long)(event->header.size), event->fork.pid, event->fork.ppid); @@ -627,7 +627,7 @@ process_fork_event(event_t *event, unsigned long offset, unsigned long head) return 0; if (!thread || !parent || thread__fork(thread, parent)) { - dump_printf("problem processing PERF_EVENT_FORK, skipping event.\n"); + dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); return -1; } total_fork++; @@ -639,23 +639,23 @@ static int process_event(event_t *event, unsigned long offset, unsigned long head) { switch (event->header.type) { - case PERF_EVENT_SAMPLE: + case PERF_RECORD_SAMPLE: return process_sample_event(event, offset, head); - case PERF_EVENT_MMAP: + case PERF_RECORD_MMAP: return process_mmap_event(event, offset, head); - case PERF_EVENT_COMM: + case PERF_RECORD_COMM: return process_comm_event(event, offset, head); - case PERF_EVENT_FORK: + case PERF_RECORD_FORK: return process_fork_event(event, offset, head); /* * We dont process them right now but they are fine: */ - case PERF_EVENT_THROTTLE: - case PERF_EVENT_UNTHROTTLE: + case PERF_RECORD_THROTTLE: + case PERF_RECORD_UNTHROTTLE: return 0; default: diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 2459e5a22ed8..a5a050af8e7d 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -77,7 +77,7 @@ static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS]; static unsigned long mmap_read_head(struct mmap_data *md) { - struct perf_counter_mmap_page *pc = md->base; + struct perf_event_mmap_page *pc = md->base; long head; head = pc->data_head; @@ -88,7 +88,7 @@ static unsigned long mmap_read_head(struct mmap_data *md) static void mmap_write_tail(struct mmap_data *md, unsigned long tail) { - struct perf_counter_mmap_page *pc = md->base; + struct perf_event_mmap_page *pc = md->base; /* * ensure all reads are done before we write the tail out. @@ -233,7 +233,7 @@ static pid_t pid_synthesize_comm_event(pid_t pid, int full) } } - comm_ev.header.type = PERF_EVENT_COMM; + comm_ev.header.type = PERF_RECORD_COMM; size = ALIGN(size, sizeof(u64)); comm_ev.header.size = sizeof(comm_ev) - (sizeof(comm_ev.comm) - size); @@ -288,7 +288,7 @@ static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid) while (1) { char bf[BUFSIZ], *pbf = bf; struct mmap_event mmap_ev = { - .header = { .type = PERF_EVENT_MMAP }, + .header = { .type = PERF_RECORD_MMAP }, }; int n; size_t size; @@ -355,7 +355,7 @@ static void synthesize_all(void) static int group_fd; -static struct perf_header_attr *get_header_attr(struct perf_counter_attr *a, int nr) +static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr) { struct perf_header_attr *h_attr; @@ -371,7 +371,7 @@ static struct perf_header_attr *get_header_attr(struct perf_counter_attr *a, int static void create_counter(int counter, int cpu, pid_t pid) { - struct perf_counter_attr *attr = attrs + counter; + struct perf_event_attr *attr = attrs + counter; struct perf_header_attr *h_attr; int track = !counter; /* only the first counter needs these */ struct { @@ -417,7 +417,7 @@ static void create_counter(int counter, int cpu, pid_t pid) attr->disabled = 1; try_again: - fd[nr_cpu][counter] = sys_perf_counter_open(attr, pid, cpu, group_fd, 0); + fd[nr_cpu][counter] = sys_perf_event_open(attr, pid, cpu, group_fd, 0); if (fd[nr_cpu][counter] < 0) { int err = errno; @@ -444,7 +444,7 @@ try_again: printf("\n"); error("perfcounter syscall returned with %d (%s)\n", fd[nr_cpu][counter], strerror(err)); - die("No CONFIG_PERF_COUNTERS=y kernel support configured?\n"); + die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); exit(-1); } @@ -478,7 +478,7 @@ try_again: if (multiplex && fd[nr_cpu][counter] != multiplex_fd) { int ret; - ret = ioctl(fd[nr_cpu][counter], PERF_COUNTER_IOC_SET_OUTPUT, multiplex_fd); + ret = ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd); assert(ret != -1); } else { event_array[nr_poll].fd = fd[nr_cpu][counter]; @@ -496,7 +496,7 @@ try_again: } } - ioctl(fd[nr_cpu][counter], PERF_COUNTER_IOC_ENABLE); + ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_ENABLE); } static void open_counters(int cpu, pid_t pid) @@ -642,7 +642,7 @@ static int __cmd_record(int argc, const char **argv) if (done) { for (i = 0; i < nr_cpu; i++) { for (counter = 0; counter < nr_counters; counter++) - ioctl(fd[i][counter], PERF_COUNTER_IOC_DISABLE); + ioctl(fd[i][counter], PERF_EVENT_IOC_DISABLE); } } } diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index cdf9a8d27bb9..19669c20088e 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -1121,7 +1121,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) more_data += sizeof(u64); } - dump_printf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", + dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", (void *)(offset + head), (void *)(long)(event->header.size), event->header.misc, @@ -1158,9 +1158,9 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) if (comm_list && !strlist__has_entry(comm_list, thread->comm)) return 0; - cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK; + cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - if (cpumode == PERF_EVENT_MISC_KERNEL) { + if (cpumode == PERF_RECORD_MISC_KERNEL) { show = SHOW_KERNEL; level = 'k'; @@ -1168,7 +1168,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) dump_printf(" ...... dso: %s\n", dso->name); - } else if (cpumode == PERF_EVENT_MISC_USER) { + } else if (cpumode == PERF_RECORD_MISC_USER) { show = SHOW_USER; level = '.'; @@ -1210,7 +1210,7 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) thread = threads__findnew(event->mmap.pid, &threads, &last_match); - dump_printf("%p [%p]: PERF_EVENT_MMAP %d/%d: [%p(%p) @ %p]: %s\n", + dump_printf("%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n", (void *)(offset + head), (void *)(long)(event->header.size), event->mmap.pid, @@ -1221,7 +1221,7 @@ process_mmap_event(event_t *event, unsigned long offset, unsigned long head) event->mmap.filename); if (thread == NULL || map == NULL) { - dump_printf("problem processing PERF_EVENT_MMAP, skipping event.\n"); + dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n"); return 0; } @@ -1238,14 +1238,14 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) thread = threads__findnew(event->comm.pid, &threads, &last_match); - dump_printf("%p [%p]: PERF_EVENT_COMM: %s:%d\n", + dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n", (void *)(offset + head), (void *)(long)(event->header.size), event->comm.comm, event->comm.pid); if (thread == NULL || thread__set_comm_adjust(thread, event->comm.comm)) { - dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n"); + dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); return -1; } total_comm++; @@ -1262,10 +1262,10 @@ process_task_event(event_t *event, unsigned long offset, unsigned long head) thread = threads__findnew(event->fork.pid, &threads, &last_match); parent = threads__findnew(event->fork.ppid, &threads, &last_match); - dump_printf("%p [%p]: PERF_EVENT_%s: (%d:%d):(%d:%d)\n", + dump_printf("%p [%p]: PERF_RECORD_%s: (%d:%d):(%d:%d)\n", (void *)(offset + head), (void *)(long)(event->header.size), - event->header.type == PERF_EVENT_FORK ? "FORK" : "EXIT", + event->header.type == PERF_RECORD_FORK ? "FORK" : "EXIT", event->fork.pid, event->fork.tid, event->fork.ppid, event->fork.ptid); @@ -1276,11 +1276,11 @@ process_task_event(event_t *event, unsigned long offset, unsigned long head) if (thread == parent) return 0; - if (event->header.type == PERF_EVENT_EXIT) + if (event->header.type == PERF_RECORD_EXIT) return 0; if (!thread || !parent || thread__fork(thread, parent)) { - dump_printf("problem processing PERF_EVENT_FORK, skipping event.\n"); + dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n"); return -1; } total_fork++; @@ -1291,7 +1291,7 @@ process_task_event(event_t *event, unsigned long offset, unsigned long head) static int process_lost_event(event_t *event, unsigned long offset, unsigned long head) { - dump_printf("%p [%p]: PERF_EVENT_LOST: id:%Ld: lost:%Ld\n", + dump_printf("%p [%p]: PERF_RECORD_LOST: id:%Ld: lost:%Ld\n", (void *)(offset + head), (void *)(long)(event->header.size), event->lost.id, @@ -1305,7 +1305,7 @@ process_lost_event(event_t *event, unsigned long offset, unsigned long head) static int process_read_event(event_t *event, unsigned long offset, unsigned long head) { - struct perf_counter_attr *attr; + struct perf_event_attr *attr; attr = perf_header__find_attr(event->read.id, header); @@ -1319,7 +1319,7 @@ process_read_event(event_t *event, unsigned long offset, unsigned long head) event->read.value); } - dump_printf("%p [%p]: PERF_EVENT_READ: %d %d %s %Lu\n", + dump_printf("%p [%p]: PERF_RECORD_READ: %d %d %s %Lu\n", (void *)(offset + head), (void *)(long)(event->header.size), event->read.pid, @@ -1337,31 +1337,31 @@ process_event(event_t *event, unsigned long offset, unsigned long head) trace_event(event); switch (event->header.type) { - case PERF_EVENT_SAMPLE: + case PERF_RECORD_SAMPLE: return process_sample_event(event, offset, head); - case PERF_EVENT_MMAP: + case PERF_RECORD_MMAP: return process_mmap_event(event, offset, head); - case PERF_EVENT_COMM: + case PERF_RECORD_COMM: return process_comm_event(event, offset, head); - case PERF_EVENT_FORK: - case PERF_EVENT_EXIT: + case PERF_RECORD_FORK: + case PERF_RECORD_EXIT: return process_task_event(event, offset, head); - case PERF_EVENT_LOST: + case PERF_RECORD_LOST: return process_lost_event(event, offset, head); - case PERF_EVENT_READ: + case PERF_RECORD_READ: return process_read_event(event, offset, head); /* * We dont process them right now but they are fine: */ - case PERF_EVENT_THROTTLE: - case PERF_EVENT_UNTHROTTLE: + case PERF_RECORD_THROTTLE: + case PERF_RECORD_UNTHROTTLE: return 0; default: diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 275d79c6627a..ea9c15c0cdfe 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1573,7 +1573,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) more_data += sizeof(u64); } - dump_printf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", + dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", (void *)(offset + head), (void *)(long)(event->header.size), event->header.misc, @@ -1589,9 +1589,9 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) return -1; } - cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK; + cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - if (cpumode == PERF_EVENT_MISC_KERNEL) { + if (cpumode == PERF_RECORD_MISC_KERNEL) { show = SHOW_KERNEL; level = 'k'; @@ -1599,7 +1599,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) dump_printf(" ...... dso: %s\n", dso->name); - } else if (cpumode == PERF_EVENT_MISC_USER) { + } else if (cpumode == PERF_RECORD_MISC_USER) { show = SHOW_USER; level = '.'; @@ -1626,23 +1626,23 @@ process_event(event_t *event, unsigned long offset, unsigned long head) nr_events++; switch (event->header.type) { - case PERF_EVENT_MMAP: + case PERF_RECORD_MMAP: return 0; - case PERF_EVENT_LOST: + case PERF_RECORD_LOST: nr_lost_chunks++; nr_lost_events += event->lost.lost; return 0; - case PERF_EVENT_COMM: + case PERF_RECORD_COMM: return process_comm_event(event, offset, head); - case PERF_EVENT_EXIT ... PERF_EVENT_READ: + case PERF_RECORD_EXIT ... PERF_RECORD_READ: return 0; - case PERF_EVENT_SAMPLE: + case PERF_RECORD_SAMPLE: return process_sample_event(event, offset, head); - case PERF_EVENT_MAX: + case PERF_RECORD_MAX: default: return -1; } diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 61b828236c11..16af2d82e858 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -48,7 +48,7 @@ #include #include -static struct perf_counter_attr default_attrs[] = { +static struct perf_event_attr default_attrs[] = { { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES}, @@ -130,11 +130,11 @@ struct stats runtime_cycles_stats; attrs[counter].config == PERF_COUNT_##c) #define ERR_PERF_OPEN \ -"Error: counter %d, sys_perf_counter_open() syscall returned with %d (%s)\n" +"Error: counter %d, sys_perf_event_open() syscall returned with %d (%s)\n" static void create_perf_stat_counter(int counter, int pid) { - struct perf_counter_attr *attr = attrs + counter; + struct perf_event_attr *attr = attrs + counter; if (scale) attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | @@ -144,7 +144,7 @@ static void create_perf_stat_counter(int counter, int pid) unsigned int cpu; for (cpu = 0; cpu < nr_cpus; cpu++) { - fd[cpu][counter] = sys_perf_counter_open(attr, -1, cpu, -1, 0); + fd[cpu][counter] = sys_perf_event_open(attr, -1, cpu, -1, 0); if (fd[cpu][counter] < 0 && verbose) fprintf(stderr, ERR_PERF_OPEN, counter, fd[cpu][counter], strerror(errno)); @@ -154,7 +154,7 @@ static void create_perf_stat_counter(int counter, int pid) attr->disabled = 1; attr->enable_on_exec = 1; - fd[0][counter] = sys_perf_counter_open(attr, pid, -1, -1, 0); + fd[0][counter] = sys_perf_event_open(attr, pid, -1, -1, 0); if (fd[0][counter] < 0 && verbose) fprintf(stderr, ERR_PERF_OPEN, counter, fd[0][counter], strerror(errno)); diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 600406396274..4405681b3134 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -937,21 +937,21 @@ process_event(event_t *event) switch (event->header.type) { - case PERF_EVENT_COMM: + case PERF_RECORD_COMM: return process_comm_event(event); - case PERF_EVENT_FORK: + case PERF_RECORD_FORK: return process_fork_event(event); - case PERF_EVENT_EXIT: + case PERF_RECORD_EXIT: return process_exit_event(event); - case PERF_EVENT_SAMPLE: + case PERF_RECORD_SAMPLE: return queue_sample_event(event); /* * We dont process them right now but they are fine: */ - case PERF_EVENT_MMAP: - case PERF_EVENT_THROTTLE: - case PERF_EVENT_UNTHROTTLE: + case PERF_RECORD_MMAP: + case PERF_RECORD_THROTTLE: + case PERF_RECORD_UNTHROTTLE: return 0; default: diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 4002ccb36750..1ca88896eee4 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -901,7 +901,7 @@ struct mmap_data { static unsigned int mmap_read_head(struct mmap_data *md) { - struct perf_counter_mmap_page *pc = md->base; + struct perf_event_mmap_page *pc = md->base; int head; head = pc->data_head; @@ -977,9 +977,9 @@ static void mmap_read_counter(struct mmap_data *md) old += size; - if (event->header.type == PERF_EVENT_SAMPLE) { + if (event->header.type == PERF_RECORD_SAMPLE) { int user = - (event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK) == PERF_EVENT_MISC_USER; + (event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_USER; process_event(event->ip.ip, md->counter, user); } } @@ -1005,7 +1005,7 @@ int group_fd; static void start_counter(int i, int counter) { - struct perf_counter_attr *attr; + struct perf_event_attr *attr; int cpu; cpu = profile_cpu; @@ -1019,7 +1019,7 @@ static void start_counter(int i, int counter) attr->inherit = (cpu < 0) && inherit; try_again: - fd[i][counter] = sys_perf_counter_open(attr, target_pid, cpu, group_fd, 0); + fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0); if (fd[i][counter] < 0) { int err = errno; @@ -1044,7 +1044,7 @@ try_again: printf("\n"); error("perfcounter syscall returned with %d (%s)\n", fd[i][counter], strerror(err)); - die("No CONFIG_PERF_COUNTERS=y kernel support configured?\n"); + die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); exit(-1); } assert(fd[i][counter] >= 0); diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 914ab366e369..e9d256e2f47d 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -35,14 +35,14 @@ process_comm_event(event_t *event, unsigned long offset, unsigned long head) thread = threads__findnew(event->comm.pid, &threads, &last_match); - dump_printf("%p [%p]: PERF_EVENT_COMM: %s:%d\n", + dump_printf("%p [%p]: PERF_RECORD_COMM: %s:%d\n", (void *)(offset + head), (void *)(long)(event->header.size), event->comm.comm, event->comm.pid); if (thread == NULL || thread__set_comm(thread, event->comm.comm)) { - dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n"); + dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n"); return -1; } total_comm++; @@ -82,7 +82,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) more_data += sizeof(u64); } - dump_printf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", + dump_printf("%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n", (void *)(offset + head), (void *)(long)(event->header.size), event->header.misc, @@ -98,9 +98,9 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) return -1; } - cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK; + cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; - if (cpumode == PERF_EVENT_MISC_KERNEL) { + if (cpumode == PERF_RECORD_MISC_KERNEL) { show = SHOW_KERNEL; level = 'k'; @@ -108,7 +108,7 @@ process_sample_event(event_t *event, unsigned long offset, unsigned long head) dump_printf(" ...... dso: %s\n", dso->name); - } else if (cpumode == PERF_EVENT_MISC_USER) { + } else if (cpumode == PERF_RECORD_MISC_USER) { show = SHOW_USER; level = '.'; @@ -146,19 +146,19 @@ process_event(event_t *event, unsigned long offset, unsigned long head) trace_event(event); switch (event->header.type) { - case PERF_EVENT_MMAP ... PERF_EVENT_LOST: + case PERF_RECORD_MMAP ... PERF_RECORD_LOST: return 0; - case PERF_EVENT_COMM: + case PERF_RECORD_COMM: return process_comm_event(event, offset, head); - case PERF_EVENT_EXIT ... PERF_EVENT_READ: + case PERF_RECORD_EXIT ... PERF_RECORD_READ: return 0; - case PERF_EVENT_SAMPLE: + case PERF_RECORD_SAMPLE: return process_sample_event(event, offset, head); - case PERF_EVENT_MAX: + case PERF_RECORD_MAX: default: return -1; } diff --git a/tools/perf/design.txt b/tools/perf/design.txt index f71e0d245cba..f1946d107b10 100644 --- a/tools/perf/design.txt +++ b/tools/perf/design.txt @@ -18,10 +18,10 @@ underlying hardware counters. Performance counters are accessed via special file descriptors. There's one file descriptor per virtual counter used. -The special file descriptor is opened via the perf_counter_open() +The special file descriptor is opened via the perf_event_open() system call: - int sys_perf_counter_open(struct perf_counter_hw_event *hw_event_uptr, + int sys_perf_event_open(struct perf_event_hw_event *hw_event_uptr, pid_t pid, int cpu, int group_fd, unsigned long flags); @@ -32,9 +32,9 @@ can be used to set the blocking mode, etc. Multiple counters can be kept open at a time, and the counters can be poll()ed. -When creating a new counter fd, 'perf_counter_hw_event' is: +When creating a new counter fd, 'perf_event_hw_event' is: -struct perf_counter_hw_event { +struct perf_event_hw_event { /* * The MSB of the config word signifies if the rest contains cpu * specific (raw) counter configuration data, if unset, the next @@ -93,7 +93,7 @@ specified by 'event_id': /* * Generalized performance counter event types, used by the hw_event.event_id - * parameter of the sys_perf_counter_open() syscall: + * parameter of the sys_perf_event_open() syscall: */ enum hw_event_ids { /* @@ -159,7 +159,7 @@ in size. * reads on the counter should return the indicated quantities, * in increasing order of bit value, after the counter value. */ -enum perf_counter_read_format { +enum perf_event_read_format { PERF_FORMAT_TOTAL_TIME_ENABLED = 1, PERF_FORMAT_TOTAL_TIME_RUNNING = 2, }; @@ -178,7 +178,7 @@ interrupt: * Bits that can be set in hw_event.record_type to request information * in the overflow packets. */ -enum perf_counter_record_format { +enum perf_event_record_format { PERF_RECORD_IP = 1U << 0, PERF_RECORD_TID = 1U << 1, PERF_RECORD_TIME = 1U << 2, @@ -228,7 +228,7 @@ these events are recorded in the ring-buffer (see below). The 'comm' bit allows tracking of process comm data on process creation. This too is recorded in the ring-buffer (see below). -The 'pid' parameter to the perf_counter_open() system call allows the +The 'pid' parameter to the perf_event_open() system call allows the counter to be specific to a task: pid == 0: if the pid parameter is zero, the counter is attached to the @@ -258,7 +258,7 @@ The 'flags' parameter is currently unused and must be zero. The 'group_fd' parameter allows counter "groups" to be set up. A counter group has one counter which is the group "leader". The leader -is created first, with group_fd = -1 in the perf_counter_open call +is created first, with group_fd = -1 in the perf_event_open call that creates it. The rest of the group members are created subsequently, with group_fd giving the fd of the group leader. (A single counter on its own is created with group_fd = -1 and is @@ -277,13 +277,13 @@ tracking are logged into a ring-buffer. This ring-buffer is created and accessed through mmap(). The mmap size should be 1+2^n pages, where the first page is a meta-data page -(struct perf_counter_mmap_page) that contains various bits of information such +(struct perf_event_mmap_page) that contains various bits of information such as where the ring-buffer head is. /* * Structure of the page that can be mapped via mmap */ -struct perf_counter_mmap_page { +struct perf_event_mmap_page { __u32 version; /* version number of this structure */ __u32 compat_version; /* lowest version this is compat with */ @@ -317,7 +317,7 @@ struct perf_counter_mmap_page { * Control data for the mmap() data buffer. * * User-space reading this value should issue an rmb(), on SMP capable - * platforms, after reading this value -- see perf_counter_wakeup(). + * platforms, after reading this value -- see perf_event_wakeup(). */ __u32 data_head; /* head in the data section */ }; @@ -327,9 +327,9 @@ NOTE: the hw-counter userspace bits are arch specific and are currently only The following 2^n pages are the ring-buffer which contains events of the form: -#define PERF_EVENT_MISC_KERNEL (1 << 0) -#define PERF_EVENT_MISC_USER (1 << 1) -#define PERF_EVENT_MISC_OVERFLOW (1 << 2) +#define PERF_RECORD_MISC_KERNEL (1 << 0) +#define PERF_RECORD_MISC_USER (1 << 1) +#define PERF_RECORD_MISC_OVERFLOW (1 << 2) struct perf_event_header { __u32 type; @@ -353,8 +353,8 @@ enum perf_event_type { * char filename[]; * }; */ - PERF_EVENT_MMAP = 1, - PERF_EVENT_MUNMAP = 2, + PERF_RECORD_MMAP = 1, + PERF_RECORD_MUNMAP = 2, /* * struct { @@ -364,10 +364,10 @@ enum perf_event_type { * char comm[]; * }; */ - PERF_EVENT_COMM = 3, + PERF_RECORD_COMM = 3, /* - * When header.misc & PERF_EVENT_MISC_OVERFLOW the event_type field + * When header.misc & PERF_RECORD_MISC_OVERFLOW the event_type field * will be PERF_RECORD_* * * struct { @@ -397,7 +397,7 @@ Notification of new events is possible through poll()/select()/epoll() and fcntl() managing signals. Normally a notification is generated for every page filled, however one can -additionally set perf_counter_hw_event.wakeup_events to generate one every +additionally set perf_event_hw_event.wakeup_events to generate one every so many counter overflow events. Future work will include a splice() interface to the ring-buffer. @@ -409,11 +409,11 @@ events but does continue to exist and maintain its count value. An individual counter or counter group can be enabled with - ioctl(fd, PERF_COUNTER_IOC_ENABLE); + ioctl(fd, PERF_EVENT_IOC_ENABLE); or disabled with - ioctl(fd, PERF_COUNTER_IOC_DISABLE); + ioctl(fd, PERF_EVENT_IOC_DISABLE); Enabling or disabling the leader of a group enables or disables the whole group; that is, while the group leader is disabled, none of the @@ -424,16 +424,16 @@ other counter. Additionally, non-inherited overflow counters can use - ioctl(fd, PERF_COUNTER_IOC_REFRESH, nr); + ioctl(fd, PERF_EVENT_IOC_REFRESH, nr); to enable a counter for 'nr' events, after which it gets disabled again. A process can enable or disable all the counter groups that are attached to it, using prctl: - prctl(PR_TASK_PERF_COUNTERS_ENABLE); + prctl(PR_TASK_PERF_EVENTS_ENABLE); - prctl(PR_TASK_PERF_COUNTERS_DISABLE); + prctl(PR_TASK_PERF_EVENTS_DISABLE); This applies to all counters on the current process, whether created by this process or by another, and doesn't affect any counters that @@ -447,11 +447,11 @@ Arch requirements If your architecture does not have hardware performance metrics, you can still use the generic software counters based on hrtimers for sampling. -So to start with, in order to add HAVE_PERF_COUNTERS to your Kconfig, you +So to start with, in order to add HAVE_PERF_EVENTS to your Kconfig, you will need at least this: - - asm/perf_counter.h - a basic stub will suffice at first + - asm/perf_event.h - a basic stub will suffice at first - support for atomic64 types (and associated helper functions) - - set_perf_counter_pending() implemented + - set_perf_event_pending() implemented If your architecture does have hardware capabilities, you can override the -weak stub hw_perf_counter_init() to register hardware counters. +weak stub hw_perf_event_init() to register hardware counters. diff --git a/tools/perf/perf.h b/tools/perf/perf.h index 2abeb20d0bf3..8cc4623afd6f 100644 --- a/tools/perf/perf.h +++ b/tools/perf/perf.h @@ -52,15 +52,15 @@ #include #include -#include "../../include/linux/perf_counter.h" +#include "../../include/linux/perf_event.h" #include "util/types.h" /* - * prctl(PR_TASK_PERF_COUNTERS_DISABLE) will (cheaply) disable all + * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all * counters in the current task. */ -#define PR_TASK_PERF_COUNTERS_DISABLE 31 -#define PR_TASK_PERF_COUNTERS_ENABLE 32 +#define PR_TASK_PERF_EVENTS_DISABLE 31 +#define PR_TASK_PERF_EVENTS_ENABLE 32 #ifndef NSEC_PER_SEC # define NSEC_PER_SEC 1000000000ULL @@ -90,12 +90,12 @@ static inline unsigned long long rdclock(void) _min1 < _min2 ? _min1 : _min2; }) static inline int -sys_perf_counter_open(struct perf_counter_attr *attr, +sys_perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags) { attr->size = sizeof(*attr); - return syscall(__NR_perf_counter_open, attr, pid, cpu, + return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); } diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 018d414a09d1..2c9c26d6ded0 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -1,5 +1,5 @@ -#ifndef __PERF_EVENT_H -#define __PERF_EVENT_H +#ifndef __PERF_RECORD_H +#define __PERF_RECORD_H #include "../perf.h" #include "util.h" #include diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index bb4fca3efcc3..e306857b2c2b 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -9,7 +9,7 @@ /* * Create new perf.data header attribute: */ -struct perf_header_attr *perf_header_attr__new(struct perf_counter_attr *attr) +struct perf_header_attr *perf_header_attr__new(struct perf_event_attr *attr) { struct perf_header_attr *self = malloc(sizeof(*self)); @@ -134,7 +134,7 @@ struct perf_file_section { }; struct perf_file_attr { - struct perf_counter_attr attr; + struct perf_event_attr attr; struct perf_file_section ids; }; @@ -320,7 +320,7 @@ u64 perf_header__sample_type(struct perf_header *header) return type; } -struct perf_counter_attr * +struct perf_event_attr * perf_header__find_attr(u64 id, struct perf_header *header) { int i; diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 7b0e84a87179..a0761bc7863c 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -1,12 +1,12 @@ #ifndef _PERF_HEADER_H #define _PERF_HEADER_H -#include "../../../include/linux/perf_counter.h" +#include "../../../include/linux/perf_event.h" #include #include "types.h" struct perf_header_attr { - struct perf_counter_attr attr; + struct perf_event_attr attr; int ids, size; u64 *id; off_t id_offset; @@ -34,11 +34,11 @@ char *perf_header__find_event(u64 id); struct perf_header_attr * -perf_header_attr__new(struct perf_counter_attr *attr); +perf_header_attr__new(struct perf_event_attr *attr); void perf_header_attr__add_id(struct perf_header_attr *self, u64 id); u64 perf_header__sample_type(struct perf_header *header); -struct perf_counter_attr * +struct perf_event_attr * perf_header__find_attr(u64 id, struct perf_header *header); diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 89172fd0038b..13ab4b842d49 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -10,7 +10,7 @@ int nr_counters; -struct perf_counter_attr attrs[MAX_COUNTERS]; +struct perf_event_attr attrs[MAX_COUNTERS]; struct event_symbol { u8 type; @@ -48,13 +48,13 @@ static struct event_symbol event_symbols[] = { { CSW(CPU_MIGRATIONS), "cpu-migrations", "migrations" }, }; -#define __PERF_COUNTER_FIELD(config, name) \ - ((config & PERF_COUNTER_##name##_MASK) >> PERF_COUNTER_##name##_SHIFT) +#define __PERF_EVENT_FIELD(config, name) \ + ((config & PERF_EVENT_##name##_MASK) >> PERF_EVENT_##name##_SHIFT) -#define PERF_COUNTER_RAW(config) __PERF_COUNTER_FIELD(config, RAW) -#define PERF_COUNTER_CONFIG(config) __PERF_COUNTER_FIELD(config, CONFIG) -#define PERF_COUNTER_TYPE(config) __PERF_COUNTER_FIELD(config, TYPE) -#define PERF_COUNTER_ID(config) __PERF_COUNTER_FIELD(config, EVENT) +#define PERF_EVENT_RAW(config) __PERF_EVENT_FIELD(config, RAW) +#define PERF_EVENT_CONFIG(config) __PERF_EVENT_FIELD(config, CONFIG) +#define PERF_EVENT_TYPE(config) __PERF_EVENT_FIELD(config, TYPE) +#define PERF_EVENT_ID(config) __PERF_EVENT_FIELD(config, EVENT) static const char *hw_event_names[] = { "cycles", @@ -352,7 +352,7 @@ static int parse_aliases(const char **str, const char *names[][MAX_ALIASES], int } static enum event_result -parse_generic_hw_event(const char **str, struct perf_counter_attr *attr) +parse_generic_hw_event(const char **str, struct perf_event_attr *attr) { const char *s = *str; int cache_type = -1, cache_op = -1, cache_result = -1; @@ -417,7 +417,7 @@ parse_single_tracepoint_event(char *sys_name, const char *evt_name, unsigned int evt_length, char *flags, - struct perf_counter_attr *attr, + struct perf_event_attr *attr, const char **strp) { char evt_path[MAXPATHLEN]; @@ -505,7 +505,7 @@ parse_subsystem_tracepoint_event(char *sys_name, char *flags) static enum event_result parse_tracepoint_event(const char **strp, - struct perf_counter_attr *attr) + struct perf_event_attr *attr) { const char *evt_name; char *flags; @@ -563,7 +563,7 @@ static int check_events(const char *str, unsigned int i) } static enum event_result -parse_symbolic_event(const char **strp, struct perf_counter_attr *attr) +parse_symbolic_event(const char **strp, struct perf_event_attr *attr) { const char *str = *strp; unsigned int i; @@ -582,7 +582,7 @@ parse_symbolic_event(const char **strp, struct perf_counter_attr *attr) } static enum event_result -parse_raw_event(const char **strp, struct perf_counter_attr *attr) +parse_raw_event(const char **strp, struct perf_event_attr *attr) { const char *str = *strp; u64 config; @@ -601,7 +601,7 @@ parse_raw_event(const char **strp, struct perf_counter_attr *attr) } static enum event_result -parse_numeric_event(const char **strp, struct perf_counter_attr *attr) +parse_numeric_event(const char **strp, struct perf_event_attr *attr) { const char *str = *strp; char *endp; @@ -623,7 +623,7 @@ parse_numeric_event(const char **strp, struct perf_counter_attr *attr) } static enum event_result -parse_event_modifier(const char **strp, struct perf_counter_attr *attr) +parse_event_modifier(const char **strp, struct perf_event_attr *attr) { const char *str = *strp; int eu = 1, ek = 1, eh = 1; @@ -656,7 +656,7 @@ parse_event_modifier(const char **strp, struct perf_counter_attr *attr) * Symbolic names are (almost) exactly matched. */ static enum event_result -parse_event_symbols(const char **str, struct perf_counter_attr *attr) +parse_event_symbols(const char **str, struct perf_event_attr *attr) { enum event_result ret; @@ -711,7 +711,7 @@ static void store_event_type(const char *orgname) int parse_events(const struct option *opt __used, const char *str, int unset __used) { - struct perf_counter_attr attr; + struct perf_event_attr attr; enum event_result ret; if (strchr(str, ':')) diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 60704c15961f..30c608112845 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -16,7 +16,7 @@ extern struct tracepoint_path *tracepoint_id_to_path(u64 config); extern int nr_counters; -extern struct perf_counter_attr attrs[MAX_COUNTERS]; +extern struct perf_event_attr attrs[MAX_COUNTERS]; extern const char *event_name(int ctr); extern const char *__event_name(int type, u64 config); diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index 1fd824c1f1c4..af4b0573b37f 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -480,12 +480,12 @@ out: } static struct tracepoint_path * -get_tracepoints_path(struct perf_counter_attr *pattrs, int nb_counters) +get_tracepoints_path(struct perf_event_attr *pattrs, int nb_events) { struct tracepoint_path path, *ppath = &path; int i; - for (i = 0; i < nb_counters; i++) { + for (i = 0; i < nb_events; i++) { if (pattrs[i].type != PERF_TYPE_TRACEPOINT) continue; ppath->next = tracepoint_id_to_path(pattrs[i].config); @@ -496,7 +496,7 @@ get_tracepoints_path(struct perf_counter_attr *pattrs, int nb_counters) return path.next; } -void read_tracing_data(struct perf_counter_attr *pattrs, int nb_counters) +void read_tracing_data(struct perf_event_attr *pattrs, int nb_events) { char buf[BUFSIZ]; struct tracepoint_path *tps; @@ -530,7 +530,7 @@ void read_tracing_data(struct perf_counter_attr *pattrs, int nb_counters) page_size = getpagesize(); write_or_die(&page_size, 4); - tps = get_tracepoints_path(pattrs, nb_counters); + tps = get_tracepoints_path(pattrs, nb_events); read_header_files(); read_ftrace_files(tps); diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index d35ebf1e29ff..693f815c9429 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -240,6 +240,6 @@ unsigned long long raw_field_value(struct event *event, const char *name, void *data); void *raw_field_ptr(struct event *event, const char *name, void *data); -void read_tracing_data(struct perf_counter_attr *pattrs, int nb_counters); +void read_tracing_data(struct perf_event_attr *pattrs, int nb_events); #endif /* _TRACE_EVENTS_H */ -- cgit v1.2.3 From fd589a8f0a13f53a2dd580b1fe170633cf6b095f Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Thu, 16 Jul 2009 17:13:03 +0200 Subject: trivial: fix typo "to to" in multiple files Signed-off-by: Anand Gadiyar Signed-off-by: Jiri Kosina --- Documentation/hwmon/pc87427 | 2 +- Documentation/networking/regulatory.txt | 2 +- Documentation/scsi/scsi_fc_transport.txt | 2 +- arch/ia64/ia32/sys_ia32.c | 2 +- arch/um/include/shared/ptrace_user.h | 2 +- drivers/char/agp/uninorth-agp.c | 2 +- drivers/gpu/drm/mga/mga_state.c | 4 ++-- drivers/lguest/page_tables.c | 2 +- drivers/media/video/gspca/m5602/m5602_core.c | 2 +- drivers/mmc/host/mxcmmc.c | 2 +- drivers/mtd/maps/ixp2000.c | 2 +- drivers/mtd/ubi/eba.c | 2 +- drivers/mtd/ubi/ubi.h | 2 +- drivers/net/bonding/bond_3ad.c | 2 +- drivers/net/e1000/e1000_hw.c | 2 +- drivers/net/wireless/zd1211rw/zd_chip.c | 2 +- drivers/scsi/megaraid/megaraid_sas.c | 2 +- drivers/scsi/qla4xxx/ql4_os.c | 4 ++-- drivers/staging/rt2860/rtmp.h | 2 +- drivers/usb/serial/cypress_m8.h | 2 +- drivers/usb/serial/io_edgeport.c | 2 +- drivers/usb/serial/kl5kusb105.c | 2 +- fs/ext4/inode.c | 2 +- fs/gfs2/rgrp.c | 2 +- fs/ntfs/layout.h | 2 +- include/acpi/actypes.h | 2 +- include/acpi/platform/acgcc.h | 2 +- include/rdma/ib_cm.h | 2 +- kernel/tracepoint.c | 2 +- lib/zlib_deflate/deflate.c | 4 ++-- net/rxrpc/ar-call.c | 2 +- net/sched/sch_hfsc.c | 2 +- 32 files changed, 35 insertions(+), 35 deletions(-) (limited to 'drivers/char') diff --git a/Documentation/hwmon/pc87427 b/Documentation/hwmon/pc87427 index d1ebbe510f35..db5cc1227a83 100644 --- a/Documentation/hwmon/pc87427 +++ b/Documentation/hwmon/pc87427 @@ -34,5 +34,5 @@ Fan rotation speeds are reported as 14-bit values from a gated clock signal. Speeds down to 83 RPM can be measured. An alarm is triggered if the rotation speed drops below a programmable -limit. Another alarm is triggered if the speed is too low to to be measured +limit. Another alarm is triggered if the speed is too low to be measured (including stalled or missing fan). diff --git a/Documentation/networking/regulatory.txt b/Documentation/networking/regulatory.txt index eaa1a25946c1..ee31369e9e5b 100644 --- a/Documentation/networking/regulatory.txt +++ b/Documentation/networking/regulatory.txt @@ -96,7 +96,7 @@ Example code - drivers hinting an alpha2: This example comes from the zd1211rw device driver. You can start by having a mapping of your device's EEPROM country/regulatory -domain value to to a specific alpha2 as follows: +domain value to a specific alpha2 as follows: static struct zd_reg_alpha2_map reg_alpha2_map[] = { { ZD_REGDOMAIN_FCC, "US" }, diff --git a/Documentation/scsi/scsi_fc_transport.txt b/Documentation/scsi/scsi_fc_transport.txt index d7f181701dc2..aec6549ab097 100644 --- a/Documentation/scsi/scsi_fc_transport.txt +++ b/Documentation/scsi/scsi_fc_transport.txt @@ -378,7 +378,7 @@ Vport Disable/Enable: int vport_disable(struct fc_vport *vport, bool disable) where: - vport: Is vport to to be enabled or disabled + vport: Is vport to be enabled or disabled disable: If "true", the vport is to be disabled. If "false", the vport is to be enabled. diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 16ef61a91d95..625ed8f76fce 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -1270,7 +1270,7 @@ putreg (struct task_struct *child, int regno, unsigned int value) case PT_CS: if (value != __USER_CS) printk(KERN_ERR - "ia32.putreg: attempt to to set invalid segment register %d = %x\n", + "ia32.putreg: attempt to set invalid segment register %d = %x\n", regno, value); break; default: diff --git a/arch/um/include/shared/ptrace_user.h b/arch/um/include/shared/ptrace_user.h index 4bce6e012889..7fd8539bc19a 100644 --- a/arch/um/include/shared/ptrace_user.h +++ b/arch/um/include/shared/ptrace_user.h @@ -29,7 +29,7 @@ extern int ptrace_setregs(long pid, unsigned long *regs_in); * recompilation. So, we use PTRACE_OLDSETOPTIONS in UML. * We also want to be able to build the kernel on 2.4, which doesn't * have PTRACE_OLDSETOPTIONS. So, if it is missing, we declare - * PTRACE_OLDSETOPTIONS to to be the same as PTRACE_SETOPTIONS. + * PTRACE_OLDSETOPTIONS to be the same as PTRACE_SETOPTIONS. * * On architectures, that start to support PTRACE_O_TRACESYSGOOD on * linux 2.6, PTRACE_OLDSETOPTIONS never is defined, and also isn't diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 20ef1bf5e726..703959eba45a 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -270,7 +270,7 @@ static void uninorth_agp_enable(struct agp_bridge_data *bridge, u32 mode) if ((uninorth_rev >= 0x30) && (uninorth_rev <= 0x33)) { /* - * We need to to set REQ_DEPTH to 7 for U3 versions 1.0, 2.1, + * We need to set REQ_DEPTH to 7 for U3 versions 1.0, 2.1, * 2.2 and 2.3, Darwin do so. */ if ((command >> AGPSTAT_RQ_DEPTH_SHIFT) > 7) diff --git a/drivers/gpu/drm/mga/mga_state.c b/drivers/gpu/drm/mga/mga_state.c index b710fab21cb3..a53b848e0f17 100644 --- a/drivers/gpu/drm/mga/mga_state.c +++ b/drivers/gpu/drm/mga/mga_state.c @@ -239,7 +239,7 @@ static __inline__ void mga_g200_emit_pipe(drm_mga_private_t * dev_priv) MGA_WR34, 0x00000000, MGA_WR42, 0x0000ffff, MGA_WR60, 0x0000ffff); - /* Padding required to to hardware bug. + /* Padding required due to hardware bug. */ DMA_BLOCK(MGA_DMAPAD, 0xffffffff, MGA_DMAPAD, 0xffffffff, @@ -317,7 +317,7 @@ static __inline__ void mga_g400_emit_pipe(drm_mga_private_t * dev_priv) MGA_WR52, MGA_G400_WR_MAGIC, /* tex1 width */ MGA_WR60, MGA_G400_WR_MAGIC); /* tex1 height */ - /* Padding required to to hardware bug */ + /* Padding required due to hardware bug */ DMA_BLOCK(MGA_DMAPAD, 0xffffffff, MGA_DMAPAD, 0xffffffff, MGA_DMAPAD, 0xffffffff, diff --git a/drivers/lguest/page_tables.c b/drivers/lguest/page_tables.c index a8d0aee3bc0e..8aaad65c3bb5 100644 --- a/drivers/lguest/page_tables.c +++ b/drivers/lguest/page_tables.c @@ -894,7 +894,7 @@ void guest_set_pte(struct lg_cpu *cpu, * tells us they've changed. When the Guest tries to use the new entry it will * fault and demand_page() will fix it up. * - * So with that in mind here's our code to to update a (top-level) PGD entry: + * So with that in mind here's our code to update a (top-level) PGD entry: */ void guest_set_pgd(struct lguest *lg, unsigned long gpgdir, u32 idx) { diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index 8a5bba16ff32..7f1e5415850b 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -56,7 +56,7 @@ int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data) return (err < 0) ? err : 0; } -/* Writes a byte to to the m5602 */ +/* Writes a byte to the m5602 */ int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data) { int err; diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index bc14bb1b0579..88671529c45d 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -512,7 +512,7 @@ static void mxcmci_cmd_done(struct mxcmci_host *host, unsigned int stat) } /* For the DMA case the DMA engine handles the data transfer - * automatically. For non DMA we have to to it ourselves. + * automatically. For non DMA we have to do it ourselves. * Don't do it in interrupt context though. */ if (!mxcmci_use_dma(host) && host->data) diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index d4fb9a3ab4df..1bdf0ee6d0b6 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -184,7 +184,7 @@ static int ixp2000_flash_probe(struct platform_device *dev) info->map.bankwidth = 1; /* - * map_priv_2 is used to store a ptr to to the bank_setup routine + * map_priv_2 is used to store a ptr to the bank_setup routine */ info->map.map_priv_2 = (unsigned long) ixp_data->bank_setup; diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index e4d9ef0c965a..9f87c99189a9 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -1065,7 +1065,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, } /* - * Now we have got to calculate how much data we have to to copy. In + * Now we have got to calculate how much data we have to copy. In * case of a static volume it is fairly easy - the VID header contains * the data size. In case of a dynamic volume it is more difficult - we * have to read the contents, cut 0xFF bytes from the end and copy only diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 6a5fe9633783..47877942decc 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -570,7 +570,7 @@ void ubi_do_get_volume_info(struct ubi_device *ubi, struct ubi_volume *vol, /* * ubi_rb_for_each_entry - walk an RB-tree. - * @rb: a pointer to type 'struct rb_node' to to use as a loop counter + * @rb: a pointer to type 'struct rb_node' to use as a loop counter * @pos: a pointer to RB-tree entry type to use as a loop counter * @root: RB-tree's root * @member: the name of the 'struct rb_node' within the RB-tree entry diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index cea5cfe23b71..c3fa31c9f2a7 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1987,7 +1987,7 @@ void bond_3ad_unbind_slave(struct slave *slave) // find new aggregator for the related port(s) new_aggregator = __get_first_agg(port); for (; new_aggregator; new_aggregator = __get_next_agg(new_aggregator)) { - // if the new aggregator is empty, or it connected to to our port only + // if the new aggregator is empty, or it is connected to our port only if (!new_aggregator->lag_ports || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator)) { break; } diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c index cda6b397550d..45ac225a7aaa 100644 --- a/drivers/net/e1000/e1000_hw.c +++ b/drivers/net/e1000/e1000_hw.c @@ -3035,7 +3035,7 @@ s32 e1000_check_for_link(struct e1000_hw *hw) /* If TBI compatibility is was previously off, turn it on. For * compatibility with a TBI link partner, we will store bad * packets. Some frames have an additional byte on the end and - * will look like CRC errors to to the hardware. + * will look like CRC errors to the hardware. */ if (!hw->tbi_compatibility_on) { hw->tbi_compatibility_on = true; diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c index 5e110a2328ae..4e79a9800134 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.c +++ b/drivers/net/wireless/zd1211rw/zd_chip.c @@ -368,7 +368,7 @@ error: return r; } -/* MAC address: if custom mac addresses are to to be used CR_MAC_ADDR_P1 and +/* MAC address: if custom mac addresses are to be used CR_MAC_ADDR_P1 and * CR_MAC_ADDR_P2 must be overwritten */ int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr) diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index 7dc3d1894b1a..a39addc3a596 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -718,7 +718,7 @@ megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, * megasas_build_ldio - Prepares IOs to logical devices * @instance: Adapter soft state * @scp: SCSI command - * @cmd: Command to to be prepared + * @cmd: Command to be prepared * * Frames (and accompanying SGLs) for regular SCSI IOs use this function. */ diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 40e3cafb3a9c..83c8b5e4fc8b 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c @@ -1422,7 +1422,7 @@ static void qla4xxx_slave_destroy(struct scsi_device *sdev) /** * qla4xxx_del_from_active_array - returns an active srb * @ha: Pointer to host adapter structure. - * @index: index into to the active_array + * @index: index into the active_array * * This routine removes and returns the srb at the specified index **/ @@ -1500,7 +1500,7 @@ static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha) /** * qla4xxx_eh_wait_for_commands - wait for active cmds to finish. - * @ha: pointer to to HBA + * @ha: pointer to HBA * @t: target id * @l: lun id * diff --git a/drivers/staging/rt2860/rtmp.h b/drivers/staging/rt2860/rtmp.h index 3f498f6f3ff6..90fd40f24734 100644 --- a/drivers/staging/rt2860/rtmp.h +++ b/drivers/staging/rt2860/rtmp.h @@ -2060,7 +2060,7 @@ typedef struct _STA_ADMIN_CONFIG { BOOLEAN AdhocBGJoined; // Indicate Adhoc B/G Join. BOOLEAN Adhoc20NJoined; // Indicate Adhoc 20MHz N Join. #endif - // New for WPA, windows want us to to keep association information and + // New for WPA, windows want us to keep association information and // Fixed IEs from last association response NDIS_802_11_ASSOCIATION_INFORMATION AssocInfo; USHORT ReqVarIELen; // Length of next VIE include EID & Length diff --git a/drivers/usb/serial/cypress_m8.h b/drivers/usb/serial/cypress_m8.h index e772b01ac3ac..1fd360e04065 100644 --- a/drivers/usb/serial/cypress_m8.h +++ b/drivers/usb/serial/cypress_m8.h @@ -57,7 +57,7 @@ #define UART_RI 0x10 /* ring indicator - modem - device to host */ #define UART_CD 0x40 /* carrier detect - modem - device to host */ #define CYP_ERROR 0x08 /* received from input report - device to host */ -/* Note - the below has nothing to to with the "feature report" reset */ +/* Note - the below has nothing to do with the "feature report" reset */ #define CONTROL_RESET 0x08 /* sent with output report - host to device */ /* End of RS-232 protocol definitions */ diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index dc0f832657e6..b97960ac92f2 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2540,7 +2540,7 @@ static int calc_baud_rate_divisor(int baudrate, int *divisor) /***************************************************************************** * send_cmd_write_uart_register - * this function builds up a uart register message and sends to to the device. + * this function builds up a uart register message and sends to the device. *****************************************************************************/ static int send_cmd_write_uart_register(struct edgeport_port *edge_port, __u8 regNum, __u8 regValue) diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index a61673133d7d..f7373371b137 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -38,7 +38,7 @@ * 0.3a - implemented pools of write URBs * 0.3 - alpha version for public testing * 0.2 - TIOCMGET works, so autopilot(1) can be used! - * 0.1 - can be used to to pilot-xfer -p /dev/ttyUSB0 -l + * 0.1 - can be used to do pilot-xfer -p /dev/ttyUSB0 -l * * The driver skeleton is mainly based on mct_u232.c and various other * pieces of code shamelessly copied from the drivers/usb/serial/ directory. diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 4abd683b963d..3a798737e305 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -2337,7 +2337,7 @@ static int __mpage_da_writepage(struct page *page, /* * Rest of the page in the page_vec * redirty then and skip then. We will - * try to to write them again after + * try to write them again after * starting a new transaction */ redirty_page_for_writepage(wbc, page); diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 28c590b7c9da..8f1cfb02a6cb 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c @@ -179,7 +179,7 @@ static inline u64 gfs2_bit_search(const __le64 *ptr, u64 mask, u8 state) * always aligned to a 64 bit boundary. * * The size of the buffer is in bytes, but is it assumed that it is - * always ok to to read a complete multiple of 64 bits at the end + * always ok to read a complete multiple of 64 bits at the end * of the block in case the end is no aligned to a natural boundary. * * Return: the block number (bitmap buffer scope) that was found diff --git a/fs/ntfs/layout.h b/fs/ntfs/layout.h index 50931b1ce4b9..8b2549f672bf 100644 --- a/fs/ntfs/layout.h +++ b/fs/ntfs/layout.h @@ -829,7 +829,7 @@ enum { /* Note, FILE_ATTR_VALID_SET_FLAGS masks out the old DOS VolId, the F_A_DEVICE, F_A_DIRECTORY, F_A_SPARSE_FILE, F_A_REPARSE_POINT, F_A_COMPRESSED, and F_A_ENCRYPTED and preserves the rest. This mask - is used to to obtain all flags that are valid for setting. */ + is used to obtain all flags that are valid for setting. */ /* * The flag FILE_ATTR_DUP_FILENAME_INDEX_PRESENT is present in all * FILENAME_ATTR attributes but not in the STANDARD_INFORMATION diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 37ba576d06e8..8052236d1a3d 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -288,7 +288,7 @@ typedef u32 acpi_physical_address; /* * Some compilers complain about unused variables. Sometimes we don't want to * use all the variables (for example, _acpi_module_name). This allows us - * to to tell the compiler in a per-variable manner that a variable + * to tell the compiler in a per-variable manner that a variable * is unused */ #ifndef ACPI_UNUSED_VAR diff --git a/include/acpi/platform/acgcc.h b/include/acpi/platform/acgcc.h index 935c5d7fc86e..6aadbf84ae71 100644 --- a/include/acpi/platform/acgcc.h +++ b/include/acpi/platform/acgcc.h @@ -57,7 +57,7 @@ /* * Some compilers complain about unused variables. Sometimes we don't want to * use all the variables (for example, _acpi_module_name). This allows us - * to to tell the compiler warning in a per-variable manner that a variable + * to tell the compiler warning in a per-variable manner that a variable * is unused. */ #define ACPI_UNUSED_VAR __attribute__ ((unused)) diff --git a/include/rdma/ib_cm.h b/include/rdma/ib_cm.h index 938858304300..c8f94e8db69c 100644 --- a/include/rdma/ib_cm.h +++ b/include/rdma/ib_cm.h @@ -482,7 +482,7 @@ int ib_send_cm_rej(struct ib_cm_id *cm_id, * message. * @cm_id: Connection identifier associated with the connection message. * @service_timeout: The lower 5-bits specify the maximum time required for - * the sender to reply to to the connection message. The upper 3-bits + * the sender to reply to the connection message. The upper 3-bits * specify additional control flags. * @private_data: Optional user-defined private data sent with the * message receipt acknowledgement. diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 9489a0a9b1be..cc89be5bc0f8 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -48,7 +48,7 @@ static struct hlist_head tracepoint_table[TRACEPOINT_TABLE_SIZE]; /* * Note about RCU : - * It is used to to delay the free of multiple probes array until a quiescent + * It is used to delay the free of multiple probes array until a quiescent * state is reached. * Tracepoint entries modifications are protected by the tracepoints_mutex. */ diff --git a/lib/zlib_deflate/deflate.c b/lib/zlib_deflate/deflate.c index c3e4a2baf835..46a31e5f49c3 100644 --- a/lib/zlib_deflate/deflate.c +++ b/lib/zlib_deflate/deflate.c @@ -135,7 +135,7 @@ static const config configuration_table[10] = { /* =========================================================================== * Update a hash value with the given input byte - * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * IN assertion: all calls to UPDATE_HASH are made with consecutive * input characters, so that a running hash key can be computed from the * previous key instead of complete recalculation each time. */ @@ -146,7 +146,7 @@ static const config configuration_table[10] = { * Insert string str in the dictionary and set match_head to the previous head * of the hash chain (the most recent string with same hash key). Return * the previous length of the hash chain. - * IN assertion: all calls to to INSERT_STRING are made with consecutive + * IN assertion: all calls to INSERT_STRING are made with consecutive * input characters and the first MIN_MATCH bytes of str are valid * (except for the last MIN_MATCH-1 bytes of the input file). */ diff --git a/net/rxrpc/ar-call.c b/net/rxrpc/ar-call.c index d9231245a79a..bc0019f704fe 100644 --- a/net/rxrpc/ar-call.c +++ b/net/rxrpc/ar-call.c @@ -96,7 +96,7 @@ static struct rxrpc_call *rxrpc_alloc_call(gfp_t gfp) } /* - * allocate a new client call and attempt to to get a connection slot for it + * allocate a new client call and attempt to get a connection slot for it */ static struct rxrpc_call *rxrpc_alloc_client_call( struct rxrpc_sock *rx, diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 375d64cb1a3d..2c5c76be18f8 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -77,7 +77,7 @@ * The service curve parameters are converted to the internal * representation. The slope values are scaled to avoid overflow. * the inverse slope values as well as the y-projection of the 1st - * segment are kept in order to to avoid 64-bit divide operations + * segment are kept in order to avoid 64-bit divide operations * that are expensive on 32-bit architectures. */ -- cgit v1.2.3 From a419aef8b858a2bdb98df60336063d28df4b272f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 18 Aug 2009 11:18:35 -0700 Subject: trivial: remove unnecessary semicolons Signed-off-by: Joe Perches Signed-off-by: Jiri Kosina --- arch/s390/hypfs/inode.c | 2 +- arch/s390/kvm/interrupt.c | 2 +- arch/sparc/kernel/irq_64.c | 2 +- arch/um/drivers/net_kern.c | 2 +- drivers/block/DAC960.c | 4 ++-- drivers/block/swim3.c | 2 +- drivers/char/epca.c | 2 +- drivers/gpu/drm/i915/intel_dp.c | 2 +- drivers/gpu/drm/radeon/r300.c | 4 ++-- drivers/ide/ide-probe.c | 2 +- drivers/ide/umc8672.c | 4 ++-- drivers/isdn/capi/capiutil.c | 2 +- drivers/macintosh/rack-meter.c | 2 +- drivers/net/arcnet/arc-rawmode.c | 1 - drivers/net/arcnet/capmode.c | 1 - drivers/net/gianfar_ethtool.c | 2 +- drivers/net/ibm_newemac/core.c | 8 ++++---- drivers/net/igb/igb_main.c | 2 +- drivers/net/ll_temac_main.c | 2 +- drivers/net/ni52.c | 4 ++-- drivers/net/qlge/qlge_main.c | 4 ++-- drivers/net/skfp/pcmplc.c | 2 +- drivers/net/skfp/pmf.c | 8 ++++---- drivers/net/skge.c | 2 +- drivers/net/sky2.c | 2 +- drivers/net/vxge/vxge-config.h | 2 +- drivers/net/vxge/vxge-main.c | 2 +- drivers/rtc/rtc-omap.c | 2 +- drivers/s390/block/dasd_eckd.c | 2 +- drivers/s390/net/netiucv.c | 2 +- drivers/s390/scsi/zfcp_scsi.c | 2 +- drivers/scsi/bnx2i/bnx2i_hwi.c | 2 +- drivers/scsi/lpfc/lpfc_ct.c | 2 +- drivers/spi/omap_uwire.c | 2 +- drivers/spi/spi_s3c24xx.c | 2 +- drivers/usb/class/cdc-wdm.c | 2 -- drivers/usb/serial/spcp8x5.c | 2 +- drivers/uwb/i1480/i1480u-wlp/netdev.c | 2 +- drivers/video/cfbcopyarea.c | 2 +- drivers/video/imxfb.c | 2 +- drivers/video/s3c2410fb.c | 2 +- drivers/xen/balloon.c | 2 +- fs/autofs/dirhash.c | 2 +- fs/btrfs/tree-log.c | 2 +- fs/cifs/cifs_dfs_ref.c | 2 +- fs/nfs/callback_xdr.c | 2 +- fs/ocfs2/quota_global.c | 2 +- include/scsi/fc/fc_fc2.h | 3 +-- kernel/trace/trace_hw_branches.c | 2 +- net/wireless/wext-compat.c | 2 +- sound/oss/sys_timer.c | 3 --- sound/soc/codecs/wm9081.c | 2 +- sound/soc/pxa/pxa-ssp.c | 2 +- sound/soc/s3c24xx/s3c24xx_uda134x.c | 2 +- 54 files changed, 61 insertions(+), 69 deletions(-) (limited to 'drivers/char') diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index bd9914b89488..1baa635717b0 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c @@ -496,7 +496,7 @@ static int __init hypfs_init(void) } s390_kobj = kobject_create_and_add("s390", hypervisor_kobj); if (!s390_kobj) { - rc = -ENOMEM;; + rc = -ENOMEM; goto fail_sysfs; } rc = register_filesystem(&hypfs_type); diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 2c2f98353415..43486c2408e1 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -478,7 +478,7 @@ int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code) if (!inti) return -ENOMEM; - inti->type = KVM_S390_PROGRAM_INT;; + inti->type = KVM_S390_PROGRAM_INT; inti->pgm.code = code; VCPU_EVENT(vcpu, 3, "inject: program check %d (from kernel)", code); diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c index 8daab33fc17d..8ab1d4728a4b 100644 --- a/arch/sparc/kernel/irq_64.c +++ b/arch/sparc/kernel/irq_64.c @@ -229,7 +229,7 @@ static unsigned int sun4u_compute_tid(unsigned long imap, unsigned long cpuid) tid = ((a << IMAP_AID_SHIFT) | (n << IMAP_NID_SHIFT)); tid &= (IMAP_AID_SAFARI | - IMAP_NID_SAFARI);; + IMAP_NID_SAFARI); } } else { tid = cpuid << IMAP_TID_SHIFT; diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index f114813ae258..a74245ae3a84 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c @@ -533,7 +533,7 @@ static int eth_parse(char *str, int *index_out, char **str_out, char **error_out) { char *end; - int n, err = -EINVAL;; + int n, err = -EINVAL; n = simple_strtoul(str, &end, 0); if (end == str) { diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c index b839af1702e2..26caa3ffaff7 100644 --- a/drivers/block/DAC960.c +++ b/drivers/block/DAC960.c @@ -6653,7 +6653,7 @@ static long DAC960_gam_ioctl(struct file *file, unsigned int Request, else ErrorCode = get_user(ControllerNumber, &UserSpaceControllerInfo->ControllerNumber); if (ErrorCode != 0) - break;; + break; ErrorCode = -ENXIO; if (ControllerNumber < 0 || ControllerNumber > DAC960_ControllerCount - 1) { @@ -6661,7 +6661,7 @@ static long DAC960_gam_ioctl(struct file *file, unsigned int Request, } Controller = DAC960_Controllers[ControllerNumber]; if (Controller == NULL) - break;; + break; memset(&ControllerInfo, 0, sizeof(DAC960_ControllerInfo_T)); ControllerInfo.ControllerNumber = ControllerNumber; ControllerInfo.FirmwareType = Controller->FirmwareType; diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c index 80df93e3cdd0..572ec6164f2d 100644 --- a/drivers/block/swim3.c +++ b/drivers/block/swim3.c @@ -1062,7 +1062,7 @@ static int swim3_add_device(struct macio_dev *mdev, int index) goto out_release; } fs->swim3_intr = macio_irq(mdev, 0); - fs->dma_intr = macio_irq(mdev, 1);; + fs->dma_intr = macio_irq(mdev, 1); fs->cur_cyl = -1; fs->cur_sector = -1; fs->secpercyl = 36; diff --git a/drivers/char/epca.c b/drivers/char/epca.c index ff647ca1c489..9d589e3144de 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -2239,7 +2239,7 @@ static void do_softint(struct work_struct *work) struct channel *ch = container_of(work, struct channel, tqueue); /* Called in response to a modem change event */ if (ch && ch->magic == EPCA_MAGIC) { - struct tty_struct *tty = tty_port_tty_get(&ch->port);; + struct tty_struct *tty = tty_port_tty_get(&ch->port); if (tty && tty->driver_data) { if (test_and_clear_bit(EPCA_EVENT_HANGUP, &ch->event)) { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2b914d732076..f4856a510476 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -232,7 +232,7 @@ intel_dp_aux_ch(struct intel_output *intel_output, for (try = 0; try < 5; try++) { /* Load the send data into the aux channel data registers */ for (i = 0; i < send_bytes; i += 4) { - uint32_t d = pack_aux(send + i, send_bytes - i);; + uint32_t d = pack_aux(send + i, send_bytes - i); I915_WRITE(ch_data + i, d); } diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 051bca6e3a4f..b2f5d32efb0c 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -1319,11 +1319,11 @@ static int r300_packet0_check(struct radeon_cs_parser *p, case 0x443C: /* TX_FILTER0_[0-15] */ i = (reg - 0x4400) >> 2; - tmp = ib_chunk->kdata[idx] & 0x7;; + tmp = ib_chunk->kdata[idx] & 0x7; if (tmp == 2 || tmp == 4 || tmp == 6) { track->textures[i].roundup_w = false; } - tmp = (ib_chunk->kdata[idx] >> 3) & 0x7;; + tmp = (ib_chunk->kdata[idx] >> 3) & 0x7; if (tmp == 2 || tmp == 4 || tmp == 6) { track->textures[i].roundup_h = false; } diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 8de442cbee94..63c53d65e875 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -1212,7 +1212,7 @@ static int ide_find_port_slot(const struct ide_port_info *d) { int idx = -ENOENT; u8 bootable = (d && (d->host_flags & IDE_HFLAG_NON_BOOTABLE)) ? 0 : 1; - u8 i = (d && (d->host_flags & IDE_HFLAG_QD_2ND_PORT)) ? 1 : 0;; + u8 i = (d && (d->host_flags & IDE_HFLAG_QD_2ND_PORT)) ? 1 : 0; /* * Claim an unassigned slot. diff --git a/drivers/ide/umc8672.c b/drivers/ide/umc8672.c index 0608d41fb6d0..60f936e2319c 100644 --- a/drivers/ide/umc8672.c +++ b/drivers/ide/umc8672.c @@ -170,9 +170,9 @@ static int __init umc8672_init(void) goto out; if (umc8672_probe() == 0) - return 0;; + return 0; out: - return -ENODEV;; + return -ENODEV; } module_init(umc8672_init); diff --git a/drivers/isdn/capi/capiutil.c b/drivers/isdn/capi/capiutil.c index 16f2e465e5f9..26626eead828 100644 --- a/drivers/isdn/capi/capiutil.c +++ b/drivers/isdn/capi/capiutil.c @@ -1019,7 +1019,7 @@ int __init cdebug_init(void) if (!g_debbuf->buf) { kfree(g_cmsg); kfree(g_debbuf); - return -ENOMEM;; + return -ENOMEM; } g_debbuf->size = CDEBUG_GSIZE; g_debbuf->buf[0] = 0; diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c index a98ab72adf95..93fb32038b14 100644 --- a/drivers/macintosh/rack-meter.c +++ b/drivers/macintosh/rack-meter.c @@ -274,7 +274,7 @@ static void __devinit rackmeter_init_cpu_sniffer(struct rackmeter *rm) if (cpu > 1) continue; - rcpu = &rm->cpu[cpu];; + rcpu = &rm->cpu[cpu]; rcpu->prev_idle = get_cpu_idle_time(cpu); rcpu->prev_wall = jiffies64_to_cputime64(get_jiffies_64()); schedule_delayed_work_on(cpu, &rm->cpu[cpu].sniffer, diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c index 646dfc5f50c9..8ea9c7545c12 100644 --- a/drivers/net/arcnet/arc-rawmode.c +++ b/drivers/net/arcnet/arc-rawmode.c @@ -123,7 +123,6 @@ static void rx(struct net_device *dev, int bufnum, BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx"); skb->protocol = cpu_to_be16(ETH_P_ARCNET); -; netif_rx(skb); } diff --git a/drivers/net/arcnet/capmode.c b/drivers/net/arcnet/capmode.c index 083e21094b20..66bcbbb6babc 100644 --- a/drivers/net/arcnet/capmode.c +++ b/drivers/net/arcnet/capmode.c @@ -149,7 +149,6 @@ static void rx(struct net_device *dev, int bufnum, BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx"); skb->protocol = cpu_to_be16(ETH_P_ARCNET); -; netif_rx(skb); } diff --git a/drivers/net/gianfar_ethtool.c b/drivers/net/gianfar_ethtool.c index 2234118eedbb..6c144b525b47 100644 --- a/drivers/net/gianfar_ethtool.c +++ b/drivers/net/gianfar_ethtool.c @@ -293,7 +293,7 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals rxtime = get_ictt_value(priv->rxic); rxcount = get_icft_value(priv->rxic); txtime = get_ictt_value(priv->txic); - txcount = get_icft_value(priv->txic);; + txcount = get_icft_value(priv->txic); cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, rxtime); cvals->rx_max_coalesced_frames = rxcount; diff --git a/drivers/net/ibm_newemac/core.c b/drivers/net/ibm_newemac/core.c index 1d7d7fef414f..89c82c5e63e4 100644 --- a/drivers/net/ibm_newemac/core.c +++ b/drivers/net/ibm_newemac/core.c @@ -2556,13 +2556,13 @@ static int __devinit emac_init_config(struct emac_instance *dev) if (emac_read_uint_prop(np, "mdio-device", &dev->mdio_ph, 0)) dev->mdio_ph = 0; if (emac_read_uint_prop(np, "zmii-device", &dev->zmii_ph, 0)) - dev->zmii_ph = 0;; + dev->zmii_ph = 0; if (emac_read_uint_prop(np, "zmii-channel", &dev->zmii_port, 0)) - dev->zmii_port = 0xffffffff;; + dev->zmii_port = 0xffffffff; if (emac_read_uint_prop(np, "rgmii-device", &dev->rgmii_ph, 0)) - dev->rgmii_ph = 0;; + dev->rgmii_ph = 0; if (emac_read_uint_prop(np, "rgmii-channel", &dev->rgmii_port, 0)) - dev->rgmii_port = 0xffffffff;; + dev->rgmii_port = 0xffffffff; if (emac_read_uint_prop(np, "fifo-entry-size", &dev->fifo_entry_size, 0)) dev->fifo_entry_size = 16; if (emac_read_uint_prop(np, "mal-burst-size", &dev->mal_burst_size, 0)) diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index d2639c4a086d..5d6c1530a8c0 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -3966,7 +3966,7 @@ static int igb_set_vf_multicasts(struct igb_adapter *adapter, /* VFs are limited to using the MTA hash table for their multicast * addresses */ for (i = 0; i < n; i++) - vf_data->vf_mc_hashes[i] = hash_list[i];; + vf_data->vf_mc_hashes[i] = hash_list[i]; /* Flush and reset the mta with the new values */ igb_set_rx_mode(adapter->netdev); diff --git a/drivers/net/ll_temac_main.c b/drivers/net/ll_temac_main.c index da8d0a0ca94f..f2a197fd47a5 100644 --- a/drivers/net/ll_temac_main.c +++ b/drivers/net/ll_temac_main.c @@ -865,7 +865,7 @@ temac_of_probe(struct of_device *op, const struct of_device_id *match) dcrs = dcr_resource_start(np, 0); if (dcrs == 0) { dev_err(&op->dev, "could not get DMA register address\n"); - goto nodev;; + goto nodev; } lp->sdma_dcrs = dcr_map(np, dcrs, dcr_resource_len(np, 0)); dev_dbg(&op->dev, "DCR base: %x\n", dcrs); diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c index bd0ac690d12c..aad3b370c562 100644 --- a/drivers/net/ni52.c +++ b/drivers/net/ni52.c @@ -615,10 +615,10 @@ static int init586(struct net_device *dev) /* addr_len |!src_insert |pre-len |loopback */ writeb(0x2e, &cfg_cmd->adr_len); writeb(0x00, &cfg_cmd->priority); - writeb(0x60, &cfg_cmd->ifs);; + writeb(0x60, &cfg_cmd->ifs); writeb(0x00, &cfg_cmd->time_low); writeb(0xf2, &cfg_cmd->time_high); - writeb(0x00, &cfg_cmd->promisc);; + writeb(0x00, &cfg_cmd->promisc); if (dev->flags & IFF_ALLMULTI) { int len = ((char __iomem *)p->iscp - (char __iomem *)ptr - 8) / 6; if (num_addrs > len) { diff --git a/drivers/net/qlge/qlge_main.c b/drivers/net/qlge/qlge_main.c index 220529257828..7783c5db81dc 100644 --- a/drivers/net/qlge/qlge_main.c +++ b/drivers/net/qlge/qlge_main.c @@ -2630,7 +2630,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) FLAGS_LI; /* Load irq delay values */ if (rx_ring->lbq_len) { cqicb->flags |= FLAGS_LL; /* Load lbq values */ - tmp = (u64)rx_ring->lbq_base_dma;; + tmp = (u64)rx_ring->lbq_base_dma; base_indirect_ptr = (__le64 *) rx_ring->lbq_base_indirect; page_entries = 0; do { @@ -2654,7 +2654,7 @@ static int ql_start_rx_ring(struct ql_adapter *qdev, struct rx_ring *rx_ring) } if (rx_ring->sbq_len) { cqicb->flags |= FLAGS_LS; /* Load sbq values */ - tmp = (u64)rx_ring->sbq_base_dma;; + tmp = (u64)rx_ring->sbq_base_dma; base_indirect_ptr = (__le64 *) rx_ring->sbq_base_indirect; page_entries = 0; do { diff --git a/drivers/net/skfp/pcmplc.c b/drivers/net/skfp/pcmplc.c index f1df2ec8ad41..e6b33ee05ede 100644 --- a/drivers/net/skfp/pcmplc.c +++ b/drivers/net/skfp/pcmplc.c @@ -960,7 +960,7 @@ static void pcm_fsm(struct s_smc *smc, struct s_phy *phy, int cmd) /*PC88b*/ if (!phy->cf_join) { phy->cf_join = TRUE ; - queue_event(smc,EVENT_CFM,CF_JOIN+np) ; ; + queue_event(smc,EVENT_CFM,CF_JOIN+np) ; } if (cmd == PC_JOIN) GO_STATE(PC8_ACTIVE) ; diff --git a/drivers/net/skfp/pmf.c b/drivers/net/skfp/pmf.c index 79e665e0853d..a320fdb3727d 100644 --- a/drivers/net/skfp/pmf.c +++ b/drivers/net/skfp/pmf.c @@ -807,9 +807,9 @@ void smt_add_para(struct s_smc *smc, struct s_pcon *pcon, u_short para, mib_p->fddiPORTLerFlag ; sp->p4050_pad = 0 ; sp->p4050_cutoff = - mib_p->fddiPORTLer_Cutoff ; ; + mib_p->fddiPORTLer_Cutoff ; sp->p4050_alarm = - mib_p->fddiPORTLer_Alarm ; ; + mib_p->fddiPORTLer_Alarm ; sp->p4050_estimate = mib_p->fddiPORTLer_Estimate ; sp->p4050_reject_ct = @@ -829,7 +829,7 @@ void smt_add_para(struct s_smc *smc, struct s_pcon *pcon, u_short para, sp->p4051_porttype = mib_p->fddiPORTMy_Type ; sp->p4051_connectstate = - mib_p->fddiPORTConnectState ; ; + mib_p->fddiPORTConnectState ; sp->p4051_pc_neighbor = mib_p->fddiPORTNeighborType ; sp->p4051_pc_withhold = @@ -853,7 +853,7 @@ void smt_add_para(struct s_smc *smc, struct s_pcon *pcon, u_short para, struct smt_p_4053 *sp ; sp = (struct smt_p_4053 *) to ; sp->p4053_multiple = - mib_p->fddiPORTMultiple_P ; ; + mib_p->fddiPORTMultiple_P ; sp->p4053_availablepaths = mib_p->fddiPORTAvailablePaths ; sp->p4053_currentpath = diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 62e852e21ab2..55bad4081966 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -215,7 +215,7 @@ static void skge_wol_init(struct skge_port *skge) if (skge->wol & WAKE_MAGIC) ctrl |= WOL_CTL_ENA_PME_ON_MAGIC_PKT|WOL_CTL_ENA_MAGIC_PKT_UNIT; else - ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT;; + ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT; ctrl |= WOL_CTL_DIS_PME_ON_PATTERN|WOL_CTL_DIS_PATTERN_UNIT; skge_write16(hw, WOL_REGS(port, WOL_CTRL_STAT), ctrl); diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index 4bb52e9cd371..15140f9f2e92 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -765,7 +765,7 @@ static void sky2_wol_init(struct sky2_port *sky2) if (sky2->wol & WAKE_MAGIC) ctrl |= WOL_CTL_ENA_PME_ON_MAGIC_PKT|WOL_CTL_ENA_MAGIC_PKT_UNIT; else - ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT;; + ctrl |= WOL_CTL_DIS_PME_ON_MAGIC_PKT|WOL_CTL_DIS_MAGIC_PKT_UNIT; ctrl |= WOL_CTL_DIS_PME_ON_PATTERN|WOL_CTL_DIS_PATTERN_UNIT; sky2_write16(hw, WOL_REGS(port, WOL_CTRL_STAT), ctrl); diff --git a/drivers/net/vxge/vxge-config.h b/drivers/net/vxge/vxge-config.h index 62779a520ca1..3e94f0ce0900 100644 --- a/drivers/net/vxge/vxge-config.h +++ b/drivers/net/vxge/vxge-config.h @@ -1541,7 +1541,7 @@ void vxge_hw_ring_rxd_1b_info_get( rxd_info->l4_cksum_valid = (u32)VXGE_HW_RING_RXD_L4_CKSUM_CORRECT_GET(rxdp->control_0); rxd_info->l4_cksum = - (u32)VXGE_HW_RING_RXD_L4_CKSUM_GET(rxdp->control_0);; + (u32)VXGE_HW_RING_RXD_L4_CKSUM_GET(rxdp->control_0); rxd_info->frame = (u32)VXGE_HW_RING_RXD_ETHER_ENCAP_GET(rxdp->control_0); rxd_info->proto = diff --git a/drivers/net/vxge/vxge-main.c b/drivers/net/vxge/vxge-main.c index b378037a29b5..068d7a9d3e36 100644 --- a/drivers/net/vxge/vxge-main.c +++ b/drivers/net/vxge/vxge-main.c @@ -2350,7 +2350,7 @@ static int vxge_enable_msix(struct vxgedev *vdev) enum vxge_hw_status status; /* 0 - Tx, 1 - Rx */ int tim_msix_id[4]; - int alarm_msix_id = 0, msix_intr_vect = 0;; + int alarm_msix_id = 0, msix_intr_vect = 0; vdev->intr_cnt = 0; /* allocate msix vectors */ diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index bd1ce8e2bc18..0587d53987fe 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -430,7 +430,7 @@ fail: static int __exit omap_rtc_remove(struct platform_device *pdev) { - struct rtc_device *rtc = platform_get_drvdata(pdev);; + struct rtc_device *rtc = platform_get_drvdata(pdev); device_init_wakeup(&pdev->dev, 0); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index a1ce573648a2..bd9fe2e36dce 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -706,7 +706,7 @@ static int dasd_eckd_generate_uid(struct dasd_device *device, sizeof(uid->serial) - 1); EBCASC(uid->serial, sizeof(uid->serial) - 1); uid->ssid = private->gneq->subsystemID; - uid->real_unit_addr = private->ned->unit_addr;; + uid->real_unit_addr = private->ned->unit_addr; if (private->sneq) { uid->type = private->sneq->sua_flags; if (uid->type == UA_BASE_PAV_ALIAS) diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index a4b2c576144b..c84eadd3602a 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -2113,7 +2113,7 @@ static ssize_t remove_write (struct device_driver *drv, IUCV_DBF_TEXT(trace, 3, __func__); if (count >= IFNAMSIZ) - count = IFNAMSIZ - 1;; + count = IFNAMSIZ - 1; for (i = 0, p = buf; i < count && *p; i++, p++) { if (*p == '\n' || *p == ' ') diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index 3ff726afafc6..0e1a34627a2e 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -102,7 +102,7 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt, if (unlikely((status & ZFCP_STATUS_COMMON_ERP_FAILED) || !(status & ZFCP_STATUS_COMMON_RUNNING))) { zfcp_scsi_command_fail(scpnt, DID_ERROR); - return 0;; + return 0; } ret = zfcp_fsf_send_fcp_command_task(unit, scpnt); diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c index 906cef5cda86..41e1b0e7e2ef 100644 --- a/drivers/scsi/bnx2i/bnx2i_hwi.c +++ b/drivers/scsi/bnx2i/bnx2i_hwi.c @@ -1340,7 +1340,7 @@ static int bnx2i_process_login_resp(struct iscsi_session *session, resp_hdr->opcode = login->op_code; resp_hdr->flags = login->response_flags; resp_hdr->max_version = login->version_max; - resp_hdr->active_version = login->version_active;; + resp_hdr->active_version = login->version_active; resp_hdr->hlength = 0; hton24(resp_hdr->dlength, login->data_length); diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c index 9df7ed38e1be..9a1bd9534d74 100644 --- a/drivers/scsi/lpfc/lpfc_ct.c +++ b/drivers/scsi/lpfc/lpfc_ct.c @@ -1207,7 +1207,7 @@ lpfc_ns_cmd(struct lpfc_vport *vport, int cmdcode, vport->ct_flags &= ~FC_CT_RFF_ID; CtReq->CommandResponse.bits.CmdRsp = be16_to_cpu(SLI_CTNS_RFF_ID); - CtReq->un.rff.PortId = cpu_to_be32(vport->fc_myDID);; + CtReq->un.rff.PortId = cpu_to_be32(vport->fc_myDID); CtReq->un.rff.fbits = FC4_FEATURE_INIT; CtReq->un.rff.type_code = FC_FCP_DATA; cmpl = lpfc_cmpl_ct_cmd_rff_id; diff --git a/drivers/spi/omap_uwire.c b/drivers/spi/omap_uwire.c index 8980a5640bd9..e75ba9b28898 100644 --- a/drivers/spi/omap_uwire.c +++ b/drivers/spi/omap_uwire.c @@ -213,7 +213,7 @@ static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t) unsigned bits = ust->bits_per_word; unsigned bytes; u16 val, w; - int status = 0;; + int status = 0; if (!t->tx_buf && !t->rx_buf) return 0; diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 3f3119d760db..6ba8aece90b5 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -388,7 +388,7 @@ static int __init s3c24xx_spi_probe(struct platform_device *pdev) err_no_iores: err_no_pdata: - spi_master_put(hw->master);; + spi_master_put(hw->master); err_nomem: return err; diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index ba589d4ca8bc..8c64c018b676 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -506,8 +506,6 @@ static int wdm_open(struct inode *inode, struct file *file) desc = usb_get_intfdata(intf); if (test_bit(WDM_DISCONNECTING, &desc->flags)) goto out; - - ; file->private_data = desc; rv = usb_autopm_get_interface(desc->intf); diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c index 61e7c40b94fb..1e58220403d1 100644 --- a/drivers/usb/serial/spcp8x5.c +++ b/drivers/usb/serial/spcp8x5.c @@ -544,7 +544,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty, } /* Set Baud Rate */ - baud = tty_get_baud_rate(tty);; + baud = tty_get_baud_rate(tty); switch (baud) { case 300: buf[0] = 0x00; break; case 600: buf[0] = 0x01; break; diff --git a/drivers/uwb/i1480/i1480u-wlp/netdev.c b/drivers/uwb/i1480/i1480u-wlp/netdev.c index 73055530e60f..b236e6969942 100644 --- a/drivers/uwb/i1480/i1480u-wlp/netdev.c +++ b/drivers/uwb/i1480/i1480u-wlp/netdev.c @@ -214,7 +214,7 @@ int i1480u_open(struct net_device *net_dev) netif_wake_queue(net_dev); #ifdef i1480u_FLOW_CONTROL - result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL);; + result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL); if (result < 0) { dev_err(dev, "Can't submit notification URB: %d\n", result); goto error_notif_urb_submit; diff --git a/drivers/video/cfbcopyarea.c b/drivers/video/cfbcopyarea.c index df03f3776dcc..79e5f40e6486 100644 --- a/drivers/video/cfbcopyarea.c +++ b/drivers/video/cfbcopyarea.c @@ -114,7 +114,7 @@ bitcpy(struct fb_info *p, unsigned long __iomem *dst, int dst_idx, d0 >>= right; } else if (src_idx+n <= bits) { // Single source word - d0 <<= left;; + d0 <<= left; } else { // 2 source words d1 = FB_READL(src + 1); diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 30ae3022f633..66358fa825f3 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c @@ -710,7 +710,7 @@ static int __init imxfb_probe(struct platform_device *pdev) fbi->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(fbi->clk)) { - ret = PTR_ERR(fbi->clk);; + ret = PTR_ERR(fbi->clk); dev_err(&pdev->dev, "unable to get clock: %d\n", ret); goto failed_getclock; } diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c index 7da0027e2409..5ffca2adc6a8 100644 --- a/drivers/video/s3c2410fb.c +++ b/drivers/video/s3c2410fb.c @@ -1119,7 +1119,7 @@ int __init s3c2410fb_init(void) int ret = platform_driver_register(&s3c2410fb_driver); if (ret == 0) - ret = platform_driver_register(&s3c2412fb_driver);; + ret = platform_driver_register(&s3c2412fb_driver); return ret; } diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index f5bbd9e83416..4d1b322d2b45 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -214,7 +214,7 @@ static int increase_reservation(unsigned long nr_pages) page = balloon_first_page(); for (i = 0; i < nr_pages; i++) { BUG_ON(page == NULL); - frame_list[i] = page_to_pfn(page);; + frame_list[i] = page_to_pfn(page); page = balloon_next_page(page); } diff --git a/fs/autofs/dirhash.c b/fs/autofs/dirhash.c index 2316e944a109..e947915109e5 100644 --- a/fs/autofs/dirhash.c +++ b/fs/autofs/dirhash.c @@ -90,7 +90,7 @@ struct autofs_dir_ent *autofs_expire(struct super_block *sb, DPRINTK(("autofs: not expirable (not a mounted directory): %s\n", ent->name)); continue; } - while (d_mountpoint(path.dentry) && follow_down(&path)); + while (d_mountpoint(path.dentry) && follow_down(&path)) ; umount_ok = may_umount(path.mnt); path_put(&path); diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index d91b0de7c502..30c0d45c1b5e 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2605,7 +2605,7 @@ static noinline int copy_items(struct btrfs_trans_handle *trans, extent); cs = btrfs_file_extent_offset(src, extent); cl = btrfs_file_extent_num_bytes(src, - extent);; + extent); if (btrfs_file_extent_compression(src, extent)) { cs = 0; diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index 606912d8f2a8..5e8bc99221cb 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -142,7 +142,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata, rc = dns_resolve_server_name_to_ip(*devname, &srvIP); if (rc != 0) { cERROR(1, ("%s: Failed to resolve server part of %s to IP: %d", - __func__, *devname, rc));; + __func__, *devname, rc)); goto compose_mount_options_err; } /* md_len = strlen(...) + 12 for 'sep+prefixpath=' diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index e5a2dac5f715..76b0aa0f73bf 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -222,7 +222,7 @@ static unsigned decode_sessionid(struct xdr_stream *xdr, p = read_buf(xdr, len); if (unlikely(p == NULL)) - return htonl(NFS4ERR_RESOURCE);; + return htonl(NFS4ERR_RESOURCE); memcpy(sid->data, p, len); return 0; diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c index 44f2a5e1d042..0578cc14b7a3 100644 --- a/fs/ocfs2/quota_global.c +++ b/fs/ocfs2/quota_global.c @@ -154,7 +154,7 @@ static int ocfs2_get_quota_block(struct inode *inode, int block, err = -EIO; mlog_errno(err); } - return err;; + return err; } /* Read data from global quotafile - avoid pagecache and such because we cannot diff --git a/include/scsi/fc/fc_fc2.h b/include/scsi/fc/fc_fc2.h index cff8a8c22f50..f87777d0d5bd 100644 --- a/include/scsi/fc/fc_fc2.h +++ b/include/scsi/fc/fc_fc2.h @@ -92,8 +92,7 @@ struct fc_esb { __u8 _esb_resvd[4]; __u8 esb_service_params[112]; /* TBD */ __u8 esb_seq_status[8]; /* sequence statuses, 8 bytes each */ -} __attribute__((packed));; - +} __attribute__((packed)); /* * Define expected size for ASSERTs. diff --git a/kernel/trace/trace_hw_branches.c b/kernel/trace/trace_hw_branches.c index ca7d7c4d0c2a..23b63859130e 100644 --- a/kernel/trace/trace_hw_branches.c +++ b/kernel/trace/trace_hw_branches.c @@ -155,7 +155,7 @@ static enum print_line_t bts_trace_print_line(struct trace_iterator *iter) seq_print_ip_sym(seq, it->from, symflags) && trace_seq_printf(seq, "\n")) return TRACE_TYPE_HANDLED; - return TRACE_TYPE_PARTIAL_LINE;; + return TRACE_TYPE_PARTIAL_LINE; } return TRACE_TYPE_UNHANDLED; } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 429dd06a4ecc..561a45cf2a6a 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -834,7 +834,7 @@ int cfg80211_wext_siwtxpower(struct net_device *dev, return 0; } - return rdev->ops->set_tx_power(wdev->wiphy, type, dbm);; + return rdev->ops->set_tx_power(wdev->wiphy, type, dbm); } EXPORT_SYMBOL_GPL(cfg80211_wext_siwtxpower); diff --git a/sound/oss/sys_timer.c b/sound/oss/sys_timer.c index 107534477a2f..8db6aefe15e4 100644 --- a/sound/oss/sys_timer.c +++ b/sound/oss/sys_timer.c @@ -100,9 +100,6 @@ def_tmr_open(int dev, int mode) curr_tempo = 60; curr_timebase = 100; opened = 1; - - ; - { def_tmr.expires = (1) + jiffies; add_timer(&def_tmr); diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index c64e55aa63b6..686e5aa97206 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1027,7 +1027,7 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream, - wm9081->fs); for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) { cur_val = abs((wm9081->sysclk_rate / - clk_sys_rates[i].ratio) - wm9081->fs);; + clk_sys_rates[i].ratio) - wm9081->fs); if (cur_val < best_val) { best = i; best_val = cur_val; diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c index 5b9ed6464789..d11a6d7e384a 100644 --- a/sound/soc/pxa/pxa-ssp.c +++ b/sound/soc/pxa/pxa-ssp.c @@ -351,7 +351,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, do_div(tmp, freq_out); val = tmp; - val = (val << 16) | 64;; + val = (val << 16) | 64; ssp_write_reg(ssp, SSACDD, val); ssacd |= (0x6 << 4); diff --git a/sound/soc/s3c24xx/s3c24xx_uda134x.c b/sound/soc/s3c24xx/s3c24xx_uda134x.c index 8e79a416db57..c215d32d6322 100644 --- a/sound/soc/s3c24xx/s3c24xx_uda134x.c +++ b/sound/soc/s3c24xx/s3c24xx_uda134x.c @@ -67,7 +67,7 @@ static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream) { int ret = 0; #ifdef ENFORCE_RATES - struct snd_pcm_runtime *runtime = substream->runtime;; + struct snd_pcm_runtime *runtime = substream->runtime; #endif mutex_lock(&clk_lock); -- cgit v1.2.3 From 0e6b9e8c2c7b55569333a15e39d684dec986e226 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Sun, 23 Aug 2009 01:46:55 +0200 Subject: trivial: add __init macro/ fix of __exit macro location in ipmi_poweroff.c Trivial patch which adds the __init to the module_init function of drivers/char/ipmi/ipmy_poweroff.c and corrects the location of __exit for the cleanup function. Signed-off-by: Peter Huewe Acked-by: Corey Minyard Signed-off-by: Jiri Kosina --- drivers/char/ipmi/ipmi_poweroff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c index a261bd735dfb..2e66b5f773dd 100644 --- a/drivers/char/ipmi/ipmi_poweroff.c +++ b/drivers/char/ipmi/ipmi_poweroff.c @@ -691,7 +691,7 @@ static struct ctl_table_header *ipmi_table_header; /* * Startup and shutdown functions. */ -static int ipmi_poweroff_init(void) +static int __init ipmi_poweroff_init(void) { int rv; @@ -725,7 +725,7 @@ static int ipmi_poweroff_init(void) } #ifdef MODULE -static __exit void ipmi_poweroff_cleanup(void) +static void __exit ipmi_poweroff_cleanup(void) { int rv; -- cgit v1.2.3 From 4481374ce88ba8f460c8b89f2572027bd27057d0 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 21 Sep 2009 17:03:05 -0700 Subject: mm: replace various uses of num_physpages by totalram_pages Sizing of memory allocations shouldn't depend on the number of physical pages found in a system, as that generally includes (perhaps a huge amount of) non-RAM pages. The amount of what actually is usable as storage should instead be used as a basis here. Some of the calculations (i.e. those not intending to use high memory) should likely even use (totalram_pages - totalhigh_pages). Signed-off-by: Jan Beulich Acked-by: Rusty Russell Acked-by: Ingo Molnar Cc: Dave Airlie Cc: Kyle McMartin Cc: Jeremy Fitzhardinge Cc: Pekka Enberg Cc: Hugh Dickins Cc: "David S. Miller" Cc: Patrick McHardy Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/x86/kernel/microcode_core.c | 4 ++-- drivers/char/agp/backend.c | 4 ++-- drivers/parisc/ccio-dma.c | 4 ++-- drivers/parisc/sba_iommu.c | 4 ++-- drivers/xen/balloon.c | 4 ---- fs/ntfs/malloc.h | 2 +- include/linux/mm.h | 1 + init/main.c | 4 ++-- mm/slab.c | 2 +- mm/swap.c | 2 +- mm/vmalloc.c | 4 ++-- net/core/sock.c | 4 ++-- net/dccp/proto.c | 6 +++--- net/decnet/dn_route.c | 2 +- net/ipv4/route.c | 2 +- net/ipv4/tcp.c | 4 ++-- net/netfilter/nf_conntrack_core.c | 4 ++-- net/netfilter/x_tables.c | 2 +- net/netfilter/xt_hashlimit.c | 8 ++++---- net/netlink/af_netlink.c | 6 +++--- net/sctp/protocol.c | 6 +++--- 21 files changed, 38 insertions(+), 41 deletions(-) (limited to 'drivers/char') diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c index 0db7969b0dde..378e9a8f1bf8 100644 --- a/arch/x86/kernel/microcode_core.c +++ b/arch/x86/kernel/microcode_core.c @@ -210,8 +210,8 @@ static ssize_t microcode_write(struct file *file, const char __user *buf, { ssize_t ret = -EINVAL; - if ((len >> PAGE_SHIFT) > num_physpages) { - pr_err("microcode: too much data (max %ld pages)\n", num_physpages); + if ((len >> PAGE_SHIFT) > totalram_pages) { + pr_err("microcode: too much data (max %ld pages)\n", totalram_pages); return ret; } diff --git a/drivers/char/agp/backend.c b/drivers/char/agp/backend.c index ad87753f6de4..a56ca080e108 100644 --- a/drivers/char/agp/backend.c +++ b/drivers/char/agp/backend.c @@ -114,9 +114,9 @@ static int agp_find_max(void) long memory, index, result; #if PAGE_SHIFT < 20 - memory = num_physpages >> (20 - PAGE_SHIFT); + memory = totalram_pages >> (20 - PAGE_SHIFT); #else - memory = num_physpages << (PAGE_SHIFT - 20); + memory = totalram_pages << (PAGE_SHIFT - 20); #endif index = 1; diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index a45b0c0d574e..a6b4a5a53d40 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -1266,7 +1266,7 @@ ccio_ioc_init(struct ioc *ioc) ** Hot-Plug/Removal of PCI cards. (aka PCI OLARD). */ - iova_space_size = (u32) (num_physpages / count_parisc_driver(&ccio_driver)); + iova_space_size = (u32) (totalram_pages / count_parisc_driver(&ccio_driver)); /* limit IOVA space size to 1MB-1GB */ @@ -1305,7 +1305,7 @@ ccio_ioc_init(struct ioc *ioc) DBG_INIT("%s() hpa 0x%p mem %luMB IOV %dMB (%d bits)\n", __func__, ioc->ioc_regs, - (unsigned long) num_physpages >> (20 - PAGE_SHIFT), + (unsigned long) totalram_pages >> (20 - PAGE_SHIFT), iova_space_size>>20, iov_order + PAGE_SHIFT); diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index 123d8fe3427d..57a6d19eba4c 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -1390,7 +1390,7 @@ sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num) ** for DMA hints - ergo only 30 bits max. */ - iova_space_size = (u32) (num_physpages/global_ioc_cnt); + iova_space_size = (u32) (totalram_pages/global_ioc_cnt); /* limit IOVA space size to 1MB-1GB */ if (iova_space_size < (1 << (20 - PAGE_SHIFT))) { @@ -1415,7 +1415,7 @@ sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num) DBG_INIT("%s() hpa 0x%lx mem %ldMB IOV %dMB (%d bits)\n", __func__, ioc->ioc_hpa, - (unsigned long) num_physpages >> (20 - PAGE_SHIFT), + (unsigned long) totalram_pages >> (20 - PAGE_SHIFT), iova_space_size>>20, iov_order + PAGE_SHIFT); diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index f5bbd9e83416..1b7123eb5d7b 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -96,11 +96,7 @@ static struct balloon_stats balloon_stats; /* We increase/decrease in batches which fit in a page */ static unsigned long frame_list[PAGE_SIZE / sizeof(unsigned long)]; -/* VM /proc information for memory */ -extern unsigned long totalram_pages; - #ifdef CONFIG_HIGHMEM -extern unsigned long totalhigh_pages; #define inc_totalhigh_pages() (totalhigh_pages++) #define dec_totalhigh_pages() (totalhigh_pages--) #else diff --git a/fs/ntfs/malloc.h b/fs/ntfs/malloc.h index cd0be3f5c3cd..a44b14cbceeb 100644 --- a/fs/ntfs/malloc.h +++ b/fs/ntfs/malloc.h @@ -47,7 +47,7 @@ static inline void *__ntfs_malloc(unsigned long size, gfp_t gfp_mask) return kmalloc(PAGE_SIZE, gfp_mask & ~__GFP_HIGHMEM); /* return (void *)__get_free_page(gfp_mask); */ } - if (likely(size >> PAGE_SHIFT < num_physpages)) + if (likely((size >> PAGE_SHIFT) < totalram_pages)) return __vmalloc(size, gfp_mask, PAGE_KERNEL); return NULL; } diff --git a/include/linux/mm.h b/include/linux/mm.h index d808cf832c4d..19ff81c49ba6 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -25,6 +25,7 @@ extern unsigned long max_mapnr; #endif extern unsigned long num_physpages; +extern unsigned long totalram_pages; extern void * high_memory; extern int page_cluster; diff --git a/init/main.c b/init/main.c index 34971becbd3c..2c48c3153163 100644 --- a/init/main.c +++ b/init/main.c @@ -668,12 +668,12 @@ asmlinkage void __init start_kernel(void) #endif thread_info_cache_init(); cred_init(); - fork_init(num_physpages); + fork_init(totalram_pages); proc_caches_init(); buffer_init(); key_init(); security_init(); - vfs_caches_init(num_physpages); + vfs_caches_init(totalram_pages); radix_tree_init(); signals_init(); /* rootfs populating might need page-writeback */ diff --git a/mm/slab.c b/mm/slab.c index 7b5d4deacfcd..7dfa481c96ba 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1384,7 +1384,7 @@ void __init kmem_cache_init(void) * Fragmentation resistance on low memory - only use bigger * page orders on machines with more than 32MB of memory. */ - if (num_physpages > (32 << 20) >> PAGE_SHIFT) + if (totalram_pages > (32 << 20) >> PAGE_SHIFT) slab_break_gfp_order = BREAK_GFP_ORDER_HI; /* Bootstrap is tricky, because several objects are allocated diff --git a/mm/swap.c b/mm/swap.c index 4a8a59e671f7..308e57d8d7ed 100644 --- a/mm/swap.c +++ b/mm/swap.c @@ -496,7 +496,7 @@ EXPORT_SYMBOL(pagevec_lookup_tag); */ void __init swap_setup(void) { - unsigned long megs = num_physpages >> (20 - PAGE_SHIFT); + unsigned long megs = totalram_pages >> (20 - PAGE_SHIFT); #ifdef CONFIG_SWAP bdi_init(swapper_space.backing_dev_info); diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 9216b2555d07..5535da1d6961 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -1386,7 +1386,7 @@ void *vmap(struct page **pages, unsigned int count, might_sleep(); - if (count > num_physpages) + if (count > totalram_pages) return NULL; area = get_vm_area_caller((count << PAGE_SHIFT), flags, @@ -1493,7 +1493,7 @@ static void *__vmalloc_node(unsigned long size, gfp_t gfp_mask, pgprot_t prot, unsigned long real_size = size; size = PAGE_ALIGN(size); - if (!size || (size >> PAGE_SHIFT) > num_physpages) + if (!size || (size >> PAGE_SHIFT) > totalram_pages) return NULL; area = __get_vm_area_node(size, VM_ALLOC, VMALLOC_START, VMALLOC_END, diff --git a/net/core/sock.c b/net/core/sock.c index 30d5446512f9..524712a7b154 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1206,12 +1206,12 @@ EXPORT_SYMBOL_GPL(sk_setup_caps); void __init sk_init(void) { - if (num_physpages <= 4096) { + if (totalram_pages <= 4096) { sysctl_wmem_max = 32767; sysctl_rmem_max = 32767; sysctl_wmem_default = 32767; sysctl_rmem_default = 32767; - } else if (num_physpages >= 131072) { + } else if (totalram_pages >= 131072) { sysctl_wmem_max = 131071; sysctl_rmem_max = 131071; } diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 923db06c7e55..bc4467082a00 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -1049,10 +1049,10 @@ static int __init dccp_init(void) * * The methodology is similar to that of the buffer cache. */ - if (num_physpages >= (128 * 1024)) - goal = num_physpages >> (21 - PAGE_SHIFT); + if (totalram_pages >= (128 * 1024)) + goal = totalram_pages >> (21 - PAGE_SHIFT); else - goal = num_physpages >> (23 - PAGE_SHIFT); + goal = totalram_pages >> (23 - PAGE_SHIFT); if (thash_entries) goal = (thash_entries * diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 9383d3e5a1ab..57662cabaf9b 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1750,7 +1750,7 @@ void __init dn_route_init(void) dn_route_timer.expires = jiffies + decnet_dst_gc_interval * HZ; add_timer(&dn_route_timer); - goal = num_physpages >> (26 - PAGE_SHIFT); + goal = totalram_pages >> (26 - PAGE_SHIFT); for(order = 0; (1UL << order) < goal; order++) /* NOTHING */; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 91867d3e6328..df9347314538 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -3414,7 +3414,7 @@ int __init ip_rt_init(void) alloc_large_system_hash("IP route cache", sizeof(struct rt_hash_bucket), rhash_entries, - (num_physpages >= 128 * 1024) ? + (totalram_pages >= 128 * 1024) ? 15 : 17, 0, &rt_hash_log, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 19a0612b8a20..21387ebabf00 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2862,7 +2862,7 @@ void __init tcp_init(void) alloc_large_system_hash("TCP established", sizeof(struct inet_ehash_bucket), thash_entries, - (num_physpages >= 128 * 1024) ? + (totalram_pages >= 128 * 1024) ? 13 : 15, 0, &tcp_hashinfo.ehash_size, @@ -2879,7 +2879,7 @@ void __init tcp_init(void) alloc_large_system_hash("TCP bind", sizeof(struct inet_bind_hashbucket), tcp_hashinfo.ehash_size, - (num_physpages >= 128 * 1024) ? + (totalram_pages >= 128 * 1024) ? 13 : 15, 0, &tcp_hashinfo.bhash_size, diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index b37109817a98..7c9ec3dee96e 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1245,9 +1245,9 @@ static int nf_conntrack_init_init_net(void) * machine has 512 buckets. >= 1GB machines have 16384 buckets. */ if (!nf_conntrack_htable_size) { nf_conntrack_htable_size - = (((num_physpages << PAGE_SHIFT) / 16384) + = (((totalram_pages << PAGE_SHIFT) / 16384) / sizeof(struct hlist_head)); - if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) + if (totalram_pages > (1024 * 1024 * 1024 / PAGE_SIZE)) nf_conntrack_htable_size = 16384; if (nf_conntrack_htable_size < 32) nf_conntrack_htable_size = 32; diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index a6ac83a93348..f01955cce314 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -617,7 +617,7 @@ struct xt_table_info *xt_alloc_table_info(unsigned int size) int cpu; /* Pedantry: prevent them from hitting BUG() in vmalloc.c --RR */ - if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > num_physpages) + if ((SMP_ALIGN(size) >> PAGE_SHIFT) + 2 > totalram_pages) return NULL; newinfo = kzalloc(XT_TABLE_INFO_SZ, GFP_KERNEL); diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 219dcdbe388c..dd16e404424f 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -194,9 +194,9 @@ static int htable_create_v0(struct xt_hashlimit_info *minfo, u_int8_t family) if (minfo->cfg.size) size = minfo->cfg.size; else { - size = ((num_physpages << PAGE_SHIFT) / 16384) / + size = ((totalram_pages << PAGE_SHIFT) / 16384) / sizeof(struct list_head); - if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE)) + if (totalram_pages > (1024 * 1024 * 1024 / PAGE_SIZE)) size = 8192; if (size < 16) size = 16; @@ -266,9 +266,9 @@ static int htable_create(struct xt_hashlimit_mtinfo1 *minfo, u_int8_t family) if (minfo->cfg.size) { size = minfo->cfg.size; } else { - size = (num_physpages << PAGE_SHIFT) / 16384 / + size = (totalram_pages << PAGE_SHIFT) / 16384 / sizeof(struct list_head); - if (num_physpages > 1024 * 1024 * 1024 / PAGE_SIZE) + if (totalram_pages > 1024 * 1024 * 1024 / PAGE_SIZE) size = 8192; if (size < 16) size = 16; diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index c5aab6a368ce..55180b99562a 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -2091,10 +2091,10 @@ static int __init netlink_proto_init(void) if (!nl_table) goto panic; - if (num_physpages >= (128 * 1024)) - limit = num_physpages >> (21 - PAGE_SHIFT); + if (totalram_pages >= (128 * 1024)) + limit = totalram_pages >> (21 - PAGE_SHIFT); else - limit = num_physpages >> (23 - PAGE_SHIFT); + limit = totalram_pages >> (23 - PAGE_SHIFT); order = get_bitmask_order(limit) - 1 + PAGE_SHIFT; limit = (1UL << order) / sizeof(struct hlist_head); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index c557f1fb1c66..612dc878e05c 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -1184,10 +1184,10 @@ SCTP_STATIC __init int sctp_init(void) /* Size and allocate the association hash table. * The methodology is similar to that of the tcp hash tables. */ - if (num_physpages >= (128 * 1024)) - goal = num_physpages >> (22 - PAGE_SHIFT); + if (totalram_pages >= (128 * 1024)) + goal = totalram_pages >> (22 - PAGE_SHIFT); else - goal = num_physpages >> (24 - PAGE_SHIFT); + goal = totalram_pages >> (24 - PAGE_SHIFT); for (order = 0; (1UL << order) < goal; order++) ; -- cgit v1.2.3 From 470967dc6c38696f853b7f338eb9d743c28a9e11 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Mon, 21 Sep 2009 17:03:54 -0700 Subject: pcmcia: fix read buffer overflow If count > 0 and dev->rlen == dev->rpos and dev->proto == 0 then we read and write dev->rbuf[-1]; Signed-off-by: Roel Kluin Cc: Harald Welte Cc: Dominik Brodowski Cc: Greg KH Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/pcmcia/cm4000_cs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 881934c068c8..c250a31efa53 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1017,7 +1017,7 @@ static ssize_t cmm_read(struct file *filp, __user char *buf, size_t count, } } - if (dev->proto == 0 && count > dev->rlen - dev->rpos) { + if (dev->proto == 0 && count > dev->rlen - dev->rpos && i) { DEBUGP(4, dev, "T=0 and count > buffer\n"); dev->rbuf[i] = dev->rbuf[i - 1]; dev->rbuf[i - 1] = dev->procbyte; -- cgit v1.2.3 From 3c1b27d5043086a485f8526353ae9fe37bfa1065 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 23 Sep 2009 22:26:31 -0600 Subject: virtio: make add_buf return capacity remaining This API change means that virtio_net can tell how much capacity remains for buffers. It's necessarily fuzzy, since VIRTIO_RING_F_INDIRECT_DESC means we can fit any number of descriptors in one, *if* we can kmalloc. Signed-off-by: Rusty Russell Cc: Dinesh Subhraveti --- drivers/block/virtio_blk.c | 2 +- drivers/char/hw_random/virtio-rng.c | 2 +- drivers/char/virtio_console.c | 4 ++-- drivers/net/virtio_net.c | 14 +++++++------- drivers/virtio/virtio_balloon.c | 2 +- drivers/virtio/virtio_ring.c | 6 +++++- include/linux/virtio.h | 2 +- net/9p/trans_virtio.c | 2 +- 8 files changed, 19 insertions(+), 15 deletions(-) (limited to 'drivers/char') diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index aa1a3d5a3e2b..d739ee4201f9 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -139,7 +139,7 @@ static bool do_req(struct request_queue *q, struct virtio_blk *vblk, } } - if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr)) { + if (vblk->vq->vq_ops->add_buf(vblk->vq, vblk->sg, out, in, vbr) < 0) { mempool_free(vbr, vblk->pool); return false; } diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index 32216b623248..b6c24dcc987d 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -51,7 +51,7 @@ static void register_buffer(void) sg_init_one(&sg, random_data+data_left, RANDOM_DATA_SIZE-data_left); /* There should always be room for one buffer. */ - if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) != 0) + if (vq->vq_ops->add_buf(vq, &sg, 0, 1, random_data) < 0) BUG(); vq->vq_ops->kick(vq); } diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index c74dacfa6795..a035ae39a359 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -65,7 +65,7 @@ static int put_chars(u32 vtermno, const char *buf, int count) /* add_buf wants a token to identify this buffer: we hand it any * non-NULL pointer, since there's only ever one buffer. */ - if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) == 0) { + if (out_vq->vq_ops->add_buf(out_vq, sg, 1, 0, (void *)1) >= 0) { /* Tell Host to go! */ out_vq->vq_ops->kick(out_vq); /* Chill out until it's done with the buffer. */ @@ -85,7 +85,7 @@ static void add_inbuf(void) sg_init_one(sg, inbuf, PAGE_SIZE); /* We should always be able to add one buffer to an empty queue. */ - if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) != 0) + if (in_vq->vq_ops->add_buf(in_vq, sg, 0, 1, inbuf) < 0) BUG(); in_vq->vq_ops->kick(in_vq); } diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 32266fb89c20..fbf04a553f5a 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -320,7 +320,7 @@ static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp) skb_queue_head(&vi->recv, skb); err = vi->rvq->vq_ops->add_buf(vi->rvq, sg, 0, num, skb); - if (err) { + if (err < 0) { skb_unlink(skb, &vi->recv); trim_pages(vi, skb); kfree_skb(skb); @@ -373,7 +373,7 @@ static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp) skb_queue_head(&vi->recv, skb); err = vi->rvq->vq_ops->add_buf(vi->rvq, sg, 0, 1, skb); - if (err) { + if (err < 0) { skb_unlink(skb, &vi->recv); kfree_skb(skb); break; @@ -527,7 +527,7 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb) num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1; err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb); - if (!err && !vi->free_in_tasklet) + if (err >= 0 && !vi->free_in_tasklet) mod_timer(&vi->xmit_free_timer, jiffies + (HZ/10)); return err; @@ -538,7 +538,7 @@ static void xmit_tasklet(unsigned long data) struct virtnet_info *vi = (void *)data; netif_tx_lock_bh(vi->dev); - if (vi->last_xmit_skb && xmit_skb(vi, vi->last_xmit_skb) == 0) { + if (vi->last_xmit_skb && xmit_skb(vi, vi->last_xmit_skb) >= 0) { vi->svq->vq_ops->kick(vi->svq); vi->last_xmit_skb = NULL; } @@ -557,7 +557,7 @@ again: /* If we has a buffer left over from last time, send it now. */ if (unlikely(vi->last_xmit_skb) && - xmit_skb(vi, vi->last_xmit_skb) != 0) + xmit_skb(vi, vi->last_xmit_skb) < 0) goto stop_queue; vi->last_xmit_skb = NULL; @@ -565,7 +565,7 @@ again: /* Put new one in send queue and do transmit */ if (likely(skb)) { __skb_queue_head(&vi->send, skb); - if (xmit_skb(vi, skb) != 0) { + if (xmit_skb(vi, skb) < 0) { vi->last_xmit_skb = skb; skb = NULL; goto stop_queue; @@ -668,7 +668,7 @@ static bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, sg_set_buf(&sg[i + 1], sg_virt(s), s->length); sg_set_buf(&sg[out + in - 1], &status, sizeof(status)); - BUG_ON(vi->cvq->vq_ops->add_buf(vi->cvq, sg, out, in, vi)); + BUG_ON(vi->cvq->vq_ops->add_buf(vi->cvq, sg, out, in, vi) < 0); vi->cvq->vq_ops->kick(vi->cvq); diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 26b278264796..39789232646d 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -84,7 +84,7 @@ static void tell_host(struct virtio_balloon *vb, struct virtqueue *vq) init_completion(&vb->acked); /* We should always be able to add one buffer to an empty queue. */ - if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) != 0) + if (vq->vq_ops->add_buf(vq, &sg, 1, 0, vb) < 0) BUG(); vq->vq_ops->kick(vq); diff --git a/drivers/virtio/virtio_ring.c b/drivers/virtio/virtio_ring.c index a882f2606515..f53600580726 100644 --- a/drivers/virtio/virtio_ring.c +++ b/drivers/virtio/virtio_ring.c @@ -208,7 +208,11 @@ add_head: pr_debug("Added buffer head %i to %p\n", head, vq); END_USE(vq); - return 0; + + /* If we're indirect, we can fit many (assuming not OOM). */ + if (vq->indirect) + return vq->num_free ? vq->vring.num : 0; + return vq->num_free; } static void vring_kick(struct virtqueue *_vq) diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 4fca4f5440ba..057a2e010758 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -34,7 +34,7 @@ struct virtqueue { * out_num: the number of sg readable by other side * in_num: the number of sg which are writable (after readable ones) * data: the token identifying the buffer. - * Returns 0 or an error. + * Returns remaining capacity of queue (sg segments) or a negative error. * @kick: update after add_buf * vq: the struct virtqueue * After one or more add_buf calls, invoke this to kick the other side. diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 9bf0b737aa51..53c139d31a21 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -200,7 +200,7 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req) req->status = REQ_STATUS_SENT; - if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, req->tc)) { + if (chan->vq->vq_ops->add_buf(chan->vq, chan->sg, out, in, req->tc) < 0) { P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio rpc add_buf returned failure"); return -EIO; -- cgit v1.2.3 From 3ca4f5ca73057a617f9444a91022d7127041970a Mon Sep 17 00:00:00 2001 From: Fernando Luis Vazquez Cao Date: Fri, 31 Jul 2009 15:25:56 +0900 Subject: virtio: add virtio IDs file Virtio IDs are spread all over the tree which makes assigning new IDs bothersome. Putting them together should make the process less error-prone. Signed-off-by: Fernando Luis Vazquez Cao Signed-off-by: Rusty Russell --- Documentation/lguest/lguest.c | 1 + drivers/block/virtio_blk.c | 1 + drivers/char/hw_random/virtio-rng.c | 1 + drivers/char/virtio_console.c | 1 + drivers/net/virtio_net.c | 1 + drivers/virtio/virtio_balloon.c | 1 + include/linux/virtio_9p.h | 2 -- include/linux/virtio_balloon.h | 3 --- include/linux/virtio_blk.h | 3 --- include/linux/virtio_console.h | 3 --- include/linux/virtio_ids.h | 17 +++++++++++++++++ include/linux/virtio_net.h | 3 --- include/linux/virtio_rng.h | 3 --- net/9p/trans_virtio.c | 1 + 14 files changed, 24 insertions(+), 17 deletions(-) create mode 100644 include/linux/virtio_ids.h (limited to 'drivers/char') diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c index 950cde6d6e58..84bbb190bf7b 100644 --- a/Documentation/lguest/lguest.c +++ b/Documentation/lguest/lguest.c @@ -42,6 +42,7 @@ #include #include "linux/lguest_launcher.h" #include "linux/virtio_config.h" +#include #include "linux/virtio_net.h" #include "linux/virtio_blk.h" #include "linux/virtio_console.h" diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index d739ee4201f9..73de7532fcf5 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include diff --git a/drivers/char/hw_random/virtio-rng.c b/drivers/char/hw_random/virtio-rng.c index b6c24dcc987d..962968f05b94 100644 --- a/drivers/char/hw_random/virtio-rng.c +++ b/drivers/char/hw_random/virtio-rng.c @@ -21,6 +21,7 @@ #include #include #include +#include #include /* The host will fill any buffer we give it with sweet, sweet randomness. We diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index a035ae39a359..0d328b59568d 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "hvc_console.h" diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index fbf04a553f5a..5c498d2b043f 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c index 39789232646d..200c22f55130 100644 --- a/drivers/virtio/virtio_balloon.c +++ b/drivers/virtio/virtio_balloon.c @@ -19,6 +19,7 @@ */ //#define DEBUG #include +#include #include #include #include diff --git a/include/linux/virtio_9p.h b/include/linux/virtio_9p.h index b3c4a60ceeb3..ea7226a45acb 100644 --- a/include/linux/virtio_9p.h +++ b/include/linux/virtio_9p.h @@ -4,8 +4,6 @@ * compatible drivers/servers. */ #include -/* The ID for virtio console */ -#define VIRTIO_ID_9P 9 /* Maximum number of virtio channels per partition (1 for now) */ #define MAX_9P_CHAN 1 diff --git a/include/linux/virtio_balloon.h b/include/linux/virtio_balloon.h index 8726ff77763e..09d730085060 100644 --- a/include/linux/virtio_balloon.h +++ b/include/linux/virtio_balloon.h @@ -4,9 +4,6 @@ * compatible drivers/servers. */ #include -/* The ID for virtio_balloon */ -#define VIRTIO_ID_BALLOON 5 - /* The feature bitmap for virtio balloon */ #define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ diff --git a/include/linux/virtio_blk.h b/include/linux/virtio_blk.h index 8dab9f2b8832..25fbabfa90d4 100644 --- a/include/linux/virtio_blk.h +++ b/include/linux/virtio_blk.h @@ -5,9 +5,6 @@ #include #include -/* The ID for virtio_block */ -#define VIRTIO_ID_BLOCK 2 - /* Feature bits */ #define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */ #define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ diff --git a/include/linux/virtio_console.h b/include/linux/virtio_console.h index dc161115ae35..b5f519806014 100644 --- a/include/linux/virtio_console.h +++ b/include/linux/virtio_console.h @@ -5,9 +5,6 @@ /* This header, excluding the #ifdef __KERNEL__ part, is BSD licensed so * anyone can use the definitions to implement compatible drivers/servers. */ -/* The ID for virtio console */ -#define VIRTIO_ID_CONSOLE 3 - /* Feature bits */ #define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h new file mode 100644 index 000000000000..06660c0a78d7 --- /dev/null +++ b/include/linux/virtio_ids.h @@ -0,0 +1,17 @@ +#ifndef _LINUX_VIRTIO_IDS_H +#define _LINUX_VIRTIO_IDS_H +/* + * Virtio IDs + * + * This header is BSD licensed so anyone can use the definitions to implement + * compatible drivers/servers. + */ + +#define VIRTIO_ID_NET 1 /* virtio net */ +#define VIRTIO_ID_BLOCK 2 /* virtio block */ +#define VIRTIO_ID_CONSOLE 3 /* virtio console */ +#define VIRTIO_ID_RNG 4 /* virtio ring */ +#define VIRTIO_ID_BALLOON 5 /* virtio balloon */ +#define VIRTIO_ID_9P 9 /* 9p virtio console */ + +#endif /* _LINUX_VIRTIO_IDS_H */ diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index d8dd539c9f48..1f41734bbb77 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -6,9 +6,6 @@ #include #include -/* The ID for virtio_net */ -#define VIRTIO_ID_NET 1 - /* The feature bitmap for virtio net */ #define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ #define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ diff --git a/include/linux/virtio_rng.h b/include/linux/virtio_rng.h index 1a85dab8a940..48121c3c434b 100644 --- a/include/linux/virtio_rng.h +++ b/include/linux/virtio_rng.h @@ -4,7 +4,4 @@ * compatible drivers/servers. */ #include -/* The ID for virtio_rng */ -#define VIRTIO_ID_RNG 4 - #endif /* _LINUX_VIRTIO_RNG_H */ diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index ea1e3daabefe..b2e07f0dd298 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #define VIRTQUEUE_NUM 128 -- cgit v1.2.3 From 88e9d34c727883d7d6f02cf1475b3ec98b8480c7 Mon Sep 17 00:00:00 2001 From: James Morris Date: Tue, 22 Sep 2009 16:43:43 -0700 Subject: seq_file: constify seq_operations Make all seq_operations structs const, to help mitigate against revectoring user-triggerable function pointers. This is derived from the grsecurity patch, although generated from scratch because it's simpler than extracting the changes from there. Signed-off-by: James Morris Acked-by: Serge Hallyn Acked-by: Casey Schaufler Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/mn10300/kernel/setup.c | 2 +- arch/powerpc/kernel/setup-common.c | 2 +- arch/powerpc/platforms/pseries/hvCall_inst.c | 2 +- drivers/block/cciss.c | 2 +- drivers/char/misc.c | 2 +- drivers/char/tpm/tpm_bios.c | 4 ++-- drivers/isdn/capi/kcapi_proc.c | 10 +++++----- drivers/scsi/sg.c | 6 +++--- fs/afs/proc.c | 8 ++++---- fs/dlm/debug_fs.c | 12 ++++++------ fs/jbd2/journal.c | 4 ++-- fs/nfs/client.c | 4 ++-- fs/nfsd/export.c | 2 +- fs/ocfs2/cluster/netdebug.c | 4 ++-- fs/ocfs2/dlm/dlmdebug.c | 2 +- fs/proc/nommu.c | 2 +- include/linux/nfsd/nfsd.h | 2 +- ipc/util.c | 2 +- kernel/cgroup.c | 2 +- kernel/kprobes.c | 2 +- kernel/lockdep_proc.c | 2 +- kernel/trace/ftrace.c | 4 ++-- kernel/trace/trace.c | 4 ++-- net/ipv6/ip6mr.c | 2 +- security/integrity/ima/ima_fs.c | 4 ++-- security/smack/smackfs.c | 6 +++--- 26 files changed, 49 insertions(+), 49 deletions(-) (limited to 'drivers/char') diff --git a/arch/mn10300/kernel/setup.c b/arch/mn10300/kernel/setup.c index 79890edfd67a..3f24c298a3af 100644 --- a/arch/mn10300/kernel/setup.c +++ b/arch/mn10300/kernel/setup.c @@ -285,7 +285,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c index 02fed27af7f6..1d5570a1e456 100644 --- a/arch/powerpc/kernel/setup-common.c +++ b/arch/powerpc/kernel/setup-common.c @@ -328,7 +328,7 @@ static void c_stop(struct seq_file *m, void *v) { } -struct seq_operations cpuinfo_op = { +const struct seq_operations cpuinfo_op = { .start =c_start, .next = c_next, .stop = c_stop, diff --git a/arch/powerpc/platforms/pseries/hvCall_inst.c b/arch/powerpc/platforms/pseries/hvCall_inst.c index eae51ef9af24..3631a4f277eb 100644 --- a/arch/powerpc/platforms/pseries/hvCall_inst.c +++ b/arch/powerpc/platforms/pseries/hvCall_inst.c @@ -71,7 +71,7 @@ static int hc_show(struct seq_file *m, void *p) return 0; } -static struct seq_operations hcall_inst_seq_ops = { +static const struct seq_operations hcall_inst_seq_ops = { .start = hc_start, .next = hc_next, .stop = hc_stop, diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 4f19105f755c..24c3e21ab263 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -363,7 +363,7 @@ static void cciss_seq_stop(struct seq_file *seq, void *v) h->busy_configuring = 0; } -static struct seq_operations cciss_seq_ops = { +static const struct seq_operations cciss_seq_ops = { .start = cciss_seq_start, .show = cciss_seq_show, .next = cciss_seq_next, diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 1ee27cc23426..07fa612a58d5 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -91,7 +91,7 @@ static int misc_seq_show(struct seq_file *seq, void *v) } -static struct seq_operations misc_seq_ops = { +static const struct seq_operations misc_seq_ops = { .start = misc_seq_start, .next = misc_seq_next, .stop = misc_seq_stop, diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c index 0c2f55a38b95..bf2170fb1cdd 100644 --- a/drivers/char/tpm/tpm_bios.c +++ b/drivers/char/tpm/tpm_bios.c @@ -343,14 +343,14 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations tpm_ascii_b_measurments_seqops = { +static const struct seq_operations tpm_ascii_b_measurments_seqops = { .start = tpm_bios_measurements_start, .next = tpm_bios_measurements_next, .stop = tpm_bios_measurements_stop, .show = tpm_ascii_bios_measurements_show, }; -static struct seq_operations tpm_binary_b_measurments_seqops = { +static const struct seq_operations tpm_binary_b_measurments_seqops = { .start = tpm_bios_measurements_start, .next = tpm_bios_measurements_next, .stop = tpm_bios_measurements_stop, diff --git a/drivers/isdn/capi/kcapi_proc.c b/drivers/isdn/capi/kcapi_proc.c index 50ed778f63fc..09d4db764d22 100644 --- a/drivers/isdn/capi/kcapi_proc.c +++ b/drivers/isdn/capi/kcapi_proc.c @@ -89,14 +89,14 @@ static int contrstats_show(struct seq_file *seq, void *v) return 0; } -static struct seq_operations seq_controller_ops = { +static const struct seq_operations seq_controller_ops = { .start = controller_start, .next = controller_next, .stop = controller_stop, .show = controller_show, }; -static struct seq_operations seq_contrstats_ops = { +static const struct seq_operations seq_contrstats_ops = { .start = controller_start, .next = controller_next, .stop = controller_stop, @@ -194,14 +194,14 @@ applstats_show(struct seq_file *seq, void *v) return 0; } -static struct seq_operations seq_applications_ops = { +static const struct seq_operations seq_applications_ops = { .start = applications_start, .next = applications_next, .stop = applications_stop, .show = applications_show, }; -static struct seq_operations seq_applstats_ops = { +static const struct seq_operations seq_applstats_ops = { .start = applications_start, .next = applications_next, .stop = applications_stop, @@ -264,7 +264,7 @@ static int capi_driver_show(struct seq_file *seq, void *v) return 0; } -static struct seq_operations seq_capi_driver_ops = { +static const struct seq_operations seq_capi_driver_ops = { .start = capi_driver_start, .next = capi_driver_next, .stop = capi_driver_stop, diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 4968c4ced385..848b59466850 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -2233,7 +2233,7 @@ static struct file_operations dev_fops = { .open = sg_proc_open_dev, .release = seq_release, }; -static struct seq_operations dev_seq_ops = { +static const struct seq_operations dev_seq_ops = { .start = dev_seq_start, .next = dev_seq_next, .stop = dev_seq_stop, @@ -2246,7 +2246,7 @@ static struct file_operations devstrs_fops = { .open = sg_proc_open_devstrs, .release = seq_release, }; -static struct seq_operations devstrs_seq_ops = { +static const struct seq_operations devstrs_seq_ops = { .start = dev_seq_start, .next = dev_seq_next, .stop = dev_seq_stop, @@ -2259,7 +2259,7 @@ static struct file_operations debug_fops = { .open = sg_proc_open_debug, .release = seq_release, }; -static struct seq_operations debug_seq_ops = { +static const struct seq_operations debug_seq_ops = { .start = dev_seq_start, .next = dev_seq_next, .stop = dev_seq_stop, diff --git a/fs/afs/proc.c b/fs/afs/proc.c index 8630615e57fe..852739d262a9 100644 --- a/fs/afs/proc.c +++ b/fs/afs/proc.c @@ -28,7 +28,7 @@ static int afs_proc_cells_show(struct seq_file *m, void *v); static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, size_t size, loff_t *_pos); -static struct seq_operations afs_proc_cells_ops = { +static const struct seq_operations afs_proc_cells_ops = { .start = afs_proc_cells_start, .next = afs_proc_cells_next, .stop = afs_proc_cells_stop, @@ -70,7 +70,7 @@ static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v); static int afs_proc_cell_volumes_show(struct seq_file *m, void *v); -static struct seq_operations afs_proc_cell_volumes_ops = { +static const struct seq_operations afs_proc_cell_volumes_ops = { .start = afs_proc_cell_volumes_start, .next = afs_proc_cell_volumes_next, .stop = afs_proc_cell_volumes_stop, @@ -95,7 +95,7 @@ static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v); static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v); -static struct seq_operations afs_proc_cell_vlservers_ops = { +static const struct seq_operations afs_proc_cell_vlservers_ops = { .start = afs_proc_cell_vlservers_start, .next = afs_proc_cell_vlservers_next, .stop = afs_proc_cell_vlservers_stop, @@ -119,7 +119,7 @@ static void *afs_proc_cell_servers_next(struct seq_file *p, void *v, static void afs_proc_cell_servers_stop(struct seq_file *p, void *v); static int afs_proc_cell_servers_show(struct seq_file *m, void *v); -static struct seq_operations afs_proc_cell_servers_ops = { +static const struct seq_operations afs_proc_cell_servers_ops = { .start = afs_proc_cell_servers_start, .next = afs_proc_cell_servers_next, .stop = afs_proc_cell_servers_stop, diff --git a/fs/dlm/debug_fs.c b/fs/dlm/debug_fs.c index 1d1d27442235..1c8bb8c3a82e 100644 --- a/fs/dlm/debug_fs.c +++ b/fs/dlm/debug_fs.c @@ -386,9 +386,9 @@ static int table_seq_show(struct seq_file *seq, void *iter_ptr) return rv; } -static struct seq_operations format1_seq_ops; -static struct seq_operations format2_seq_ops; -static struct seq_operations format3_seq_ops; +static const struct seq_operations format1_seq_ops; +static const struct seq_operations format2_seq_ops; +static const struct seq_operations format3_seq_ops; static void *table_seq_start(struct seq_file *seq, loff_t *pos) { @@ -534,21 +534,21 @@ static void table_seq_stop(struct seq_file *seq, void *iter_ptr) } } -static struct seq_operations format1_seq_ops = { +static const struct seq_operations format1_seq_ops = { .start = table_seq_start, .next = table_seq_next, .stop = table_seq_stop, .show = table_seq_show, }; -static struct seq_operations format2_seq_ops = { +static const struct seq_operations format2_seq_ops = { .start = table_seq_start, .next = table_seq_next, .stop = table_seq_stop, .show = table_seq_show, }; -static struct seq_operations format3_seq_ops = { +static const struct seq_operations format3_seq_ops = { .start = table_seq_start, .next = table_seq_next, .stop = table_seq_stop, diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index a8a358bc0f21..53b86e16e5fe 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -768,7 +768,7 @@ static void jbd2_seq_history_stop(struct seq_file *seq, void *v) { } -static struct seq_operations jbd2_seq_history_ops = { +static const struct seq_operations jbd2_seq_history_ops = { .start = jbd2_seq_history_start, .next = jbd2_seq_history_next, .stop = jbd2_seq_history_stop, @@ -872,7 +872,7 @@ static void jbd2_seq_info_stop(struct seq_file *seq, void *v) { } -static struct seq_operations jbd2_seq_info_ops = { +static const struct seq_operations jbd2_seq_info_ops = { .start = jbd2_seq_info_start, .next = jbd2_seq_info_next, .stop = jbd2_seq_info_stop, diff --git a/fs/nfs/client.c b/fs/nfs/client.c index a7ce15d3c248..152025358dad 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1531,7 +1531,7 @@ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos); static void nfs_server_list_stop(struct seq_file *p, void *v); static int nfs_server_list_show(struct seq_file *m, void *v); -static struct seq_operations nfs_server_list_ops = { +static const struct seq_operations nfs_server_list_ops = { .start = nfs_server_list_start, .next = nfs_server_list_next, .stop = nfs_server_list_stop, @@ -1552,7 +1552,7 @@ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos); static void nfs_volume_list_stop(struct seq_file *p, void *v); static int nfs_volume_list_show(struct seq_file *m, void *v); -static struct seq_operations nfs_volume_list_ops = { +static const struct seq_operations nfs_volume_list_ops = { .start = nfs_volume_list_start, .next = nfs_volume_list_next, .stop = nfs_volume_list_stop, diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 984a5ebcc1d6..c1c9e035d4a4 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -1517,7 +1517,7 @@ static int e_show(struct seq_file *m, void *p) return svc_export_show(m, &svc_export_cache, cp); } -struct seq_operations nfs_exports_op = { +const struct seq_operations nfs_exports_op = { .start = e_start, .next = e_next, .stop = e_stop, diff --git a/fs/ocfs2/cluster/netdebug.c b/fs/ocfs2/cluster/netdebug.c index f8424874fa07..cfb2be708abe 100644 --- a/fs/ocfs2/cluster/netdebug.c +++ b/fs/ocfs2/cluster/netdebug.c @@ -163,7 +163,7 @@ static void nst_seq_stop(struct seq_file *seq, void *v) { } -static struct seq_operations nst_seq_ops = { +static const struct seq_operations nst_seq_ops = { .start = nst_seq_start, .next = nst_seq_next, .stop = nst_seq_stop, @@ -344,7 +344,7 @@ static void sc_seq_stop(struct seq_file *seq, void *v) { } -static struct seq_operations sc_seq_ops = { +static const struct seq_operations sc_seq_ops = { .start = sc_seq_start, .next = sc_seq_next, .stop = sc_seq_stop, diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index df52f706f669..c5c88124096d 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c @@ -683,7 +683,7 @@ static int lockres_seq_show(struct seq_file *s, void *v) return 0; } -static struct seq_operations debug_lockres_ops = { +static const struct seq_operations debug_lockres_ops = { .start = lockres_seq_start, .stop = lockres_seq_stop, .next = lockres_seq_next, diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c index 7e14d1a04001..9fe7d7ebe115 100644 --- a/fs/proc/nommu.c +++ b/fs/proc/nommu.c @@ -109,7 +109,7 @@ static void *nommu_region_list_next(struct seq_file *m, void *v, loff_t *pos) return rb_next((struct rb_node *) v); } -static struct seq_operations proc_nommu_region_list_seqop = { +static const struct seq_operations proc_nommu_region_list_seqop = { .start = nommu_region_list_start, .next = nommu_region_list_next, .stop = nommu_region_list_stop, diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h index 03bbe9039104..510ffdd5020e 100644 --- a/include/linux/nfsd/nfsd.h +++ b/include/linux/nfsd/nfsd.h @@ -60,7 +60,7 @@ extern spinlock_t nfsd_drc_lock; extern unsigned int nfsd_drc_max_mem; extern unsigned int nfsd_drc_mem_used; -extern struct seq_operations nfs_exports_op; +extern const struct seq_operations nfs_exports_op; /* * Function prototypes. diff --git a/ipc/util.c b/ipc/util.c index b8e4ba92f6d1..79ce84e890f7 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -942,7 +942,7 @@ static int sysvipc_proc_show(struct seq_file *s, void *it) return iface->show(s, it); } -static struct seq_operations sysvipc_proc_seqops = { +static const struct seq_operations sysvipc_proc_seqops = { .start = sysvipc_proc_start, .stop = sysvipc_proc_stop, .next = sysvipc_proc_next, diff --git a/kernel/cgroup.c b/kernel/cgroup.c index 213b7f92fcdd..cd83d9933b6b 100644 --- a/kernel/cgroup.c +++ b/kernel/cgroup.c @@ -2314,7 +2314,7 @@ static int cgroup_tasks_show(struct seq_file *s, void *v) return seq_printf(s, "%d\n", *(int *)v); } -static struct seq_operations cgroup_tasks_seq_operations = { +static const struct seq_operations cgroup_tasks_seq_operations = { .start = cgroup_tasks_start, .stop = cgroup_tasks_stop, .next = cgroup_tasks_next, diff --git a/kernel/kprobes.c b/kernel/kprobes.c index ef177d653b2c..cfadc1291d0b 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -1321,7 +1321,7 @@ static int __kprobes show_kprobe_addr(struct seq_file *pi, void *v) return 0; } -static struct seq_operations kprobes_seq_ops = { +static const struct seq_operations kprobes_seq_ops = { .start = kprobe_seq_start, .next = kprobe_seq_next, .stop = kprobe_seq_stop, diff --git a/kernel/lockdep_proc.c b/kernel/lockdep_proc.c index d4b3dbc79fdb..d4aba4f3584c 100644 --- a/kernel/lockdep_proc.c +++ b/kernel/lockdep_proc.c @@ -594,7 +594,7 @@ static int ls_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations lockstat_ops = { +static const struct seq_operations lockstat_ops = { .start = ls_start, .next = ls_next, .stop = ls_stop, diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index c71e91bf7372..23df7771c937 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1520,7 +1520,7 @@ static int t_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations show_ftrace_seq_ops = { +static const struct seq_operations show_ftrace_seq_ops = { .start = t_start, .next = t_next, .stop = t_stop, @@ -2459,7 +2459,7 @@ static int g_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations ftrace_graph_seq_ops = { +static const struct seq_operations ftrace_graph_seq_ops = { .start = g_start, .next = g_next, .stop = g_stop, diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index a35925d222ba..6c0f6a8a22eb 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1949,7 +1949,7 @@ static int s_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations tracer_seq_ops = { +static const struct seq_operations tracer_seq_ops = { .start = s_start, .next = s_next, .stop = s_stop, @@ -2163,7 +2163,7 @@ static int t_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations show_traces_seq_ops = { +static const struct seq_operations show_traces_seq_ops = { .start = t_start, .next = t_next, .stop = t_stop, diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 3907510c2ce3..090675e269ee 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -324,7 +324,7 @@ static int ipmr_mfc_seq_show(struct seq_file *seq, void *v) return 0; } -static struct seq_operations ipmr_mfc_seq_ops = { +static const struct seq_operations ipmr_mfc_seq_ops = { .start = ipmr_mfc_seq_start, .next = ipmr_mfc_seq_next, .stop = ipmr_mfc_seq_stop, diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 6bfc7eaebfda..8e9777b76405 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -146,7 +146,7 @@ static int ima_measurements_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations ima_measurments_seqops = { +static const struct seq_operations ima_measurments_seqops = { .start = ima_measurements_start, .next = ima_measurements_next, .stop = ima_measurements_stop, @@ -221,7 +221,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) return 0; } -static struct seq_operations ima_ascii_measurements_seqops = { +static const struct seq_operations ima_ascii_measurements_seqops = { .start = ima_measurements_start, .next = ima_measurements_next, .stop = ima_measurements_stop, diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index f83a80980726..aeead7585093 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -187,7 +187,7 @@ static void load_seq_stop(struct seq_file *s, void *v) /* No-op */ } -static struct seq_operations load_seq_ops = { +static const struct seq_operations load_seq_ops = { .start = load_seq_start, .next = load_seq_next, .show = load_seq_show, @@ -503,7 +503,7 @@ static void cipso_seq_stop(struct seq_file *s, void *v) /* No-op */ } -static struct seq_operations cipso_seq_ops = { +static const struct seq_operations cipso_seq_ops = { .start = cipso_seq_start, .stop = cipso_seq_stop, .next = cipso_seq_next, @@ -697,7 +697,7 @@ static void netlbladdr_seq_stop(struct seq_file *s, void *v) /* No-op */ } -static struct seq_operations netlbladdr_seq_ops = { +static const struct seq_operations netlbladdr_seq_ops = { .start = netlbladdr_seq_start, .stop = netlbladdr_seq_stop, .next = netlbladdr_seq_next, -- cgit v1.2.3 From 8c87df457cb58fe75b9b893007917cf8095660a0 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 22 Sep 2009 16:43:52 -0700 Subject: BUILD_BUG_ON(): fix it and a couple of bogus uses of it gcc permitting variable length arrays makes the current construct used for BUILD_BUG_ON() useless, as that doesn't produce any diagnostic if the controlling expression isn't really constant. Instead, this patch makes it so that a bit field gets used here. Consequently, those uses where the condition isn't really constant now also need fixing. Note that in the gfp.h, kmemcheck.h, and virtio_config.h cases MAYBE_BUILD_BUG_ON() really just serves documentation purposes - even if the expression is compile time constant (__builtin_constant_p() yields true), the array is still deemed of variable length by gcc, and hence the whole expression doesn't have the intended effect. [akpm@linux-foundation.org: make arch/sparc/include/asm/vio.h compile] [akpm@linux-foundation.org: more nonsensical assertions in tpm.c..] Signed-off-by: Jan Beulich Cc: Andi Kleen Cc: Rusty Russell Cc: Catalin Marinas Cc: "David S. Miller" Cc: Rajiv Andrade Cc: Mimi Zohar Cc: James Morris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/sparc/include/asm/vio.h | 2 +- drivers/char/tpm/tpm.c | 4 ++-- drivers/net/niu.c | 2 +- include/linux/gfp.h | 2 +- include/linux/kernel.h | 8 ++++++-- include/linux/kmemcheck.h | 2 +- include/linux/virtio_config.h | 3 +-- 7 files changed, 13 insertions(+), 10 deletions(-) (limited to 'drivers/char') diff --git a/arch/sparc/include/asm/vio.h b/arch/sparc/include/asm/vio.h index d4de32f0f8af..6cdbf7e7351d 100644 --- a/arch/sparc/include/asm/vio.h +++ b/arch/sparc/include/asm/vio.h @@ -258,7 +258,7 @@ static inline void *vio_dring_entry(struct vio_dring_state *dr, static inline u32 vio_dring_avail(struct vio_dring_state *dr, unsigned int ring_size) { - BUILD_BUG_ON(!is_power_of_2(ring_size)); + MAYBE_BUILD_BUG_ON(!is_power_of_2(ring_size)); return (dr->pending - ((dr->prod - dr->cons) & (ring_size - 1))); diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index b0603b2e5684..32b957efa420 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -696,7 +696,7 @@ int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) cmd.header.in = pcrread_header; cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx); - BUILD_BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE); + BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE); rc = transmit_cmd(chip, &cmd, cmd.header.in.length, "attempting to read a pcr value"); @@ -760,7 +760,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) return -ENODEV; cmd.header.in = pcrextend_header; - BUILD_BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE); + BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE); cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); rc = transmit_cmd(chip, &cmd, cmd.header.in.length, diff --git a/drivers/net/niu.c b/drivers/net/niu.c index 76cc2614f480..f9364d0678f2 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -5615,7 +5615,7 @@ static void niu_init_tx_mac(struct niu *np) /* The XMAC_MIN register only accepts values for TX min which * have the low 3 bits cleared. */ - BUILD_BUG_ON(min & 0x7); + BUG_ON(min & 0x7); if (np->flags & NIU_FLAGS_XMAC) niu_init_tx_xmac(np, min, max); diff --git a/include/linux/gfp.h b/include/linux/gfp.h index f53e9b868c26..557bdad320b6 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -220,7 +220,7 @@ static inline enum zone_type gfp_zone(gfp_t flags) ((1 << ZONES_SHIFT) - 1); if (__builtin_constant_p(bit)) - BUILD_BUG_ON((GFP_ZONE_BAD >> bit) & 1); + MAYBE_BUILD_BUG_ON((GFP_ZONE_BAD >> bit) & 1); else { #ifdef CONFIG_DEBUG_VM BUG_ON((GFP_ZONE_BAD >> bit) & 1); diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 55723afa097b..63dcaece1ac5 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -678,13 +678,17 @@ struct sysinfo { }; /* Force a compilation error if condition is true */ -#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) +#define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition)) + +/* Force a compilation error if condition is constant and true */ +#define MAYBE_BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)])) /* Force a compilation error if condition is true, but also produce a result (of value 0 and type size_t), so the expression can be used e.g. in a structure initializer (or where-ever else comma expressions aren't permitted). */ -#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1) +#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) +#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); })) /* Trap pasters of __FUNCTION__ at compile-time */ #define __FUNCTION__ (__func__) diff --git a/include/linux/kmemcheck.h b/include/linux/kmemcheck.h index 136cdcdf92ef..e880d4cf9e22 100644 --- a/include/linux/kmemcheck.h +++ b/include/linux/kmemcheck.h @@ -152,7 +152,7 @@ static inline bool kmemcheck_is_obj_initialized(unsigned long addr, size_t size) \ _n = (long) &((ptr)->name##_end) \ - (long) &((ptr)->name##_begin); \ - BUILD_BUG_ON(_n < 0); \ + MAYBE_BUILD_BUG_ON(_n < 0); \ \ kmemcheck_mark_initialized(&((ptr)->name##_begin), _n); \ } while (0) diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h index e547e3c8ee9a..0093dd7c1d6f 100644 --- a/include/linux/virtio_config.h +++ b/include/linux/virtio_config.h @@ -109,8 +109,7 @@ static inline bool virtio_has_feature(const struct virtio_device *vdev, unsigned int fbit) { /* Did you forget to fix assumptions on max features? */ - if (__builtin_constant_p(fbit)) - BUILD_BUG_ON(fbit >= 32); + MAYBE_BUILD_BUG_ON(fbit >= 32); if (fbit < VIRTIO_TRANSPORT_F_START) virtio_check_driver_offered_feature(vdev, fbit); -- cgit v1.2.3 From 0afd9056f1b43c9fcbfdf933b263d72023d382fe Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Fri, 18 Sep 2009 12:54:24 -0700 Subject: tpm-fixup-pcrs-sysfs-file-update Signed-off-by: Jason Gunthorpe Cc: Debora Velarde Cc: Rajiv Andrade Cc: Marcel Selhorst Cc: James Morris Signed-off-by: Andrew Morton Signed-off-by: James Morris --- drivers/char/tpm/tpm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index b0603b2e5684..1f32b520ca26 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -742,7 +742,7 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); * the module usage count. */ #define TPM_ORD_PCR_EXTEND cpu_to_be32(20) -#define EXTEND_PCR_SIZE 34 +#define EXTEND_PCR_RESULT_SIZE 34 static struct tpm_input_header pcrextend_header = { .tag = TPM_TAG_RQU_COMMAND, .length = cpu_to_be32(34), @@ -760,10 +760,9 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) return -ENODEV; cmd.header.in = pcrextend_header; - BUILD_BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE); cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx); memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE); - rc = transmit_cmd(chip, &cmd, cmd.header.in.length, + rc = transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, "attempting extend a PCR value"); module_put(chip->dev->driver->owner); -- cgit v1.2.3 From 254be490f257fc3f76ca5f869ac8d107b3827025 Mon Sep 17 00:00:00 2001 From: Hendrik Brueckner Date: Thu, 27 Aug 2009 01:45:39 +0000 Subject: hvc_console: Provide (un)locked version for hvc_resize() Rename the locking free hvc_resize() function to __hvc_resize() and provide an inline function that locks the hvc_struct and calls __hvc_resize(). The rationale for this patch is that virtio_console calls the hvc_resize() function without locking the hvc_struct. So it needs to call the lock itself. According to naming rules, the unlocked version is renamed and prefixed with "__". References to unlocked function calls in hvc back-ends has been updated. Signed-off-by: Hendrik Brueckner Acked-by: Christian Borntraeger Signed-off-by: Benjamin Herrenschmidt --- drivers/char/hvc_console.c | 6 +++--- drivers/char/hvc_console.h | 12 +++++++++++- drivers/char/hvc_iucv.c | 4 +++- 3 files changed, 17 insertions(+), 5 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index 25ce15bb1c08..a632f25f144a 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -678,7 +678,7 @@ int hvc_poll(struct hvc_struct *hp) EXPORT_SYMBOL_GPL(hvc_poll); /** - * hvc_resize() - Update terminal window size information. + * __hvc_resize() - Update terminal window size information. * @hp: HVC console pointer * @ws: Terminal window size structure * @@ -687,12 +687,12 @@ EXPORT_SYMBOL_GPL(hvc_poll); * * Locking: Locking free; the function MUST be called holding hp->lock */ -void hvc_resize(struct hvc_struct *hp, struct winsize ws) +void __hvc_resize(struct hvc_struct *hp, struct winsize ws) { hp->ws = ws; schedule_work(&hp->tty_resize); } -EXPORT_SYMBOL_GPL(hvc_resize); +EXPORT_SYMBOL_GPL(__hvc_resize); /* * This kthread is either polling or interrupt driven. This is determined by diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h index 3c85d78c975c..10950ca706d8 100644 --- a/drivers/char/hvc_console.h +++ b/drivers/char/hvc_console.h @@ -28,6 +28,7 @@ #define HVC_CONSOLE_H #include #include +#include /* * This is the max number of console adapters that can/will be found as @@ -88,7 +89,16 @@ int hvc_poll(struct hvc_struct *hp); void hvc_kick(void); /* Resize hvc tty terminal window */ -extern void hvc_resize(struct hvc_struct *hp, struct winsize ws); +extern void __hvc_resize(struct hvc_struct *hp, struct winsize ws); + +static inline void hvc_resize(struct hvc_struct *hp, struct winsize ws) +{ + unsigned long flags; + + spin_lock_irqsave(&hp->lock, flags); + __hvc_resize(hp, ws); + spin_unlock_irqrestore(&hp->lock, flags); +} /* default notifier for irq based notification */ extern int notifier_add_irq(struct hvc_struct *hp, int data); diff --git a/drivers/char/hvc_iucv.c b/drivers/char/hvc_iucv.c index 0ecac7e532f6..b8a5d654d3d0 100644 --- a/drivers/char/hvc_iucv.c +++ b/drivers/char/hvc_iucv.c @@ -273,7 +273,9 @@ static int hvc_iucv_write(struct hvc_iucv_private *priv, case MSG_TYPE_WINSIZE: if (rb->mbuf->datalen != sizeof(struct winsize)) break; - hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); + /* The caller must ensure that the hvc is locked, which + * is the case when called from hvc_iucv_get_chars() */ + __hvc_resize(priv->hvc, *((struct winsize *) rb->mbuf->data)); break; case MSG_TYPE_ERROR: /* ignored ... */ -- cgit v1.2.3 From bb521c5de070b86a1e049e2dbf62328f717ff1e8 Mon Sep 17 00:00:00 2001 From: Nikanth Karthikesan Date: Wed, 23 Sep 2009 15:57:09 -0700 Subject: /dev/zero: avoid repeated access_ok() checks In read_zero, we check for access_ok() once for the count bytes. It is unnecessarily checked again in clear_user. Use __clear_user, which does not check for access_ok(). Signed-off-by: Nikanth Karthikesan Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 0aede1d6a9ea..6c8b65d069e5 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -690,7 +690,7 @@ static ssize_t read_zero(struct file * file, char __user * buf, if (chunk > PAGE_SIZE) chunk = PAGE_SIZE; /* Just for latency reasons */ - unwritten = clear_user(buf, chunk); + unwritten = __clear_user(buf, chunk); written += chunk - unwritten; if (unwritten) break; -- cgit v1.2.3 From dc80df567dd04738ee8b3922feacf099ae81645e Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 23 Sep 2009 15:57:11 -0700 Subject: mwave: fix read buffer overflow Check whether index is within bounds before grabbing the element. Signed-off-by: Roel Kluin Cc: Kay Sievers Cc: Greg Kroah-Hartman Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/mwave/mwavedd.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c index 94ad2c3bfc4a..a4ec50c95072 100644 --- a/drivers/char/mwave/mwavedd.c +++ b/drivers/char/mwave/mwavedd.c @@ -281,12 +281,6 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, case IOCTL_MW_REGISTER_IPC: { unsigned int ipcnum = (unsigned int) ioarg; - PRINTK_3(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC" - " ipcnum %x entry usIntCount %x\n", - ipcnum, - pDrvData->IPCs[ipcnum].usIntCount); - if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:" @@ -295,6 +289,12 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, ipcnum); return -EINVAL; } + PRINTK_3(TRACE_MWAVE, + "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC" + " ipcnum %x entry usIntCount %x\n", + ipcnum, + pDrvData->IPCs[ipcnum].usIntCount); + lock_kernel(); pDrvData->IPCs[ipcnum].bIsHere = FALSE; pDrvData->IPCs[ipcnum].bIsEnabled = TRUE; @@ -310,11 +310,6 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, case IOCTL_MW_GET_IPC: { unsigned int ipcnum = (unsigned int) ioarg; - PRINTK_3(TRACE_MWAVE, - "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC" - " ipcnum %x, usIntCount %x\n", - ipcnum, - pDrvData->IPCs[ipcnum].usIntCount); if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { PRINTK_ERROR(KERN_ERR_MWAVE "mwavedd::mwave_ioctl:" @@ -322,6 +317,11 @@ static long mwave_ioctl(struct file *file, unsigned int iocmd, " Invalid ipcnum %x\n", ipcnum); return -EINVAL; } + PRINTK_3(TRACE_MWAVE, + "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC" + " ipcnum %x, usIntCount %x\n", + ipcnum, + pDrvData->IPCs[ipcnum].usIntCount); lock_kernel(); if (pDrvData->IPCs[ipcnum].bIsEnabled == TRUE) { -- cgit v1.2.3 From ae21cf9248584d9b3776bfe2ebec47256bf098f8 Mon Sep 17 00:00:00 2001 From: Nils Carlson Date: Wed, 23 Sep 2009 15:57:13 -0700 Subject: hpet: hpet driver periodic timer setup bug fixes The periodic interrupt from drivers/char/hpet.c does not work correctly, both when using the periodic capability of the hardware and while emulating the periodic interrupt (when hardware does not support periodic mode). With timers capable of periodic interrupts, the comparator field is first set with the period value followed by set of hidden accumulator, which has the side effect of overwriting the comparator value. This results in wrong periodicity for the interrupts. For, periodic interrupts to work, following steps are necessary, in that order. * Set config with Tn_VAL_SET_CNF bit * Write to hidden accumulator, the value written is the time when the first interrupt should be generated * Write compartor with period interval for subsequent interrupts (http://www.intel.com/hardwaredesign/hpetspec_1.pdf ) When emulating periodic timer with timers not capable of periodic interrupt, driver is adding the period to counter value instead of comparator value, which causes slow drift when using this emulation. Also, driver seems to add hpetp->hp_delta both while setting up periodic interrupt and while emulating periodic interrupts with timers not capable of doing periodic interrupts. This hp_delta will result in slower than expected interrupt rate and should not be used while setting the interval. Signed-off-by: Venkatesh Pallipadi Signed-off-by: Nils Carlson Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/hpet.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index 4a9f3492b921..70a770ac0138 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -166,9 +166,8 @@ static irqreturn_t hpet_interrupt(int irq, void *data) unsigned long m, t; t = devp->hd_ireqfreq; - m = read_counter(&devp->hd_hpet->hpet_mc); - write_counter(t + m + devp->hd_hpets->hp_delta, - &devp->hd_timer->hpet_compare); + m = read_counter(&devp->hd_timer->hpet_compare); + write_counter(t + m, &devp->hd_timer->hpet_compare); } if (devp->hd_flags & HPET_SHARED_IRQ) @@ -504,21 +503,25 @@ static int hpet_ioctl_ieon(struct hpet_dev *devp) g = v | Tn_32MODE_CNF_MASK | Tn_INT_ENB_CNF_MASK; if (devp->hd_flags & HPET_PERIODIC) { - write_counter(t, &timer->hpet_compare); g |= Tn_TYPE_CNF_MASK; - v |= Tn_TYPE_CNF_MASK; - writeq(v, &timer->hpet_config); - v |= Tn_VAL_SET_CNF_MASK; + v |= Tn_TYPE_CNF_MASK | Tn_VAL_SET_CNF_MASK; writeq(v, &timer->hpet_config); local_irq_save(flags); - /* NOTE: what we modify here is a hidden accumulator + /* + * NOTE: First we modify the hidden accumulator * register supported by periodic-capable comparators. * We never want to modify the (single) counter; that - * would affect all the comparators. + * would affect all the comparators. The value written + * is the counter value when the first interrupt is due. */ m = read_counter(&hpet->hpet_mc); write_counter(t + m + hpetp->hp_delta, &timer->hpet_compare); + /* + * Then we modify the comparator, indicating the period + * for subsequent interrupt. + */ + write_counter(t, &timer->hpet_compare); } else { local_irq_save(flags); m = read_counter(&hpet->hpet_mc); -- cgit v1.2.3 From 459ca8b4ed1889b0a69bbe21888e6af136d495f3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 23 Sep 2009 15:57:14 -0700 Subject: drivers/char/rio/rioctrl.c: off by one error in rioctrl.c If DownLoad.ProductCode == MAX_PRODUCT, that would be a problem when we do RIOBootTable[DownLoad.ProductCode] a couple lines down. Found by smatch (http://repo.or.cz/w/smatch.git). Signed-off-by: Dan Carpenter Cc: Jiri Slaby Cc: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/rio/rioctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/char') diff --git a/drivers/char/rio/rioctrl.c b/drivers/char/rio/rioctrl.c index eecee0f576d2..74339559f0b9 100644 --- a/drivers/char/rio/rioctrl.c +++ b/drivers/char/rio/rioctrl.c @@ -873,7 +873,7 @@ int riocontrol(struct rio_info *p, dev_t dev, int cmd, unsigned long arg, int su /* ** It is important that the product code is an unsigned object! */ - if (DownLoad.ProductCode > MAX_PRODUCT) { + if (DownLoad.ProductCode >= MAX_PRODUCT) { rio_dprintk(RIO_DEBUG_CTRL, "RIO_DOWNLOAD: Bad product code %d passed\n", DownLoad.ProductCode); p->RIOError.Error = NO_SUCH_PRODUCT; return -ENXIO; -- cgit v1.2.3 From fbd8ae106850b6a0215c2776e70a75a1b93cafc2 Mon Sep 17 00:00:00 2001 From: Dimitri Sivanich Date: Wed, 23 Sep 2009 15:57:15 -0700 Subject: drivers/char/uv_mmtimer.c: add memory mapped RTC driver for UV This driver memory maps the UV Hub RTC. Signed-off-by: Dimitri Sivanich Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/ioctl/ioctl-number.txt | 1 + drivers/char/Kconfig | 8 ++ drivers/char/Makefile | 1 + drivers/char/uv_mmtimer.c | 216 +++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+) create mode 100644 drivers/char/uv_mmtimer.c (limited to 'drivers/char') diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index aafca0a8f66a..947374977ca5 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -135,6 +135,7 @@ Code Seq# Include File Comments 'l' 40-7F linux/udf_fs_i.h in development: +'m' 00-09 linux/mmtimer.h 'm' all linux/mtio.h conflict! 'm' all linux/soundcard.h conflict! 'm' all linux/synclink.h conflict! diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 6a06913b01d3..08a6f50ae791 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -1087,6 +1087,14 @@ config MMTIMER The mmtimer device allows direct userspace access to the Altix system timer. +config UV_MMTIMER + tristate "UV_MMTIMER Memory mapped RTC for SGI UV" + depends on X86_UV + default m + help + The uv_mmtimer device allows direct userspace access to the + UV system timer. + source "drivers/char/tpm/Kconfig" config TELCLOCK diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 66f779ad4f4c..19a79dd79eee 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o obj-$(CONFIG_MSPEC) += mspec.o obj-$(CONFIG_MMTIMER) += mmtimer.o +obj-$(CONFIG_UV_MMTIMER) += uv_mmtimer.o obj-$(CONFIG_VIOTAPE) += viotape.o obj-$(CONFIG_HVCS) += hvcs.o obj-$(CONFIG_IBM_BSR) += bsr.o diff --git a/drivers/char/uv_mmtimer.c b/drivers/char/uv_mmtimer.c new file mode 100644 index 000000000000..867b67be9f0a --- /dev/null +++ b/drivers/char/uv_mmtimer.c @@ -0,0 +1,216 @@ +/* + * Timer device implementation for SGI UV platform. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2009 Silicon Graphics, Inc. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +MODULE_AUTHOR("Dimitri Sivanich "); +MODULE_DESCRIPTION("SGI UV Memory Mapped RTC Timer"); +MODULE_LICENSE("GPL"); + +/* name of the device, usually in /dev */ +#define UV_MMTIMER_NAME "mmtimer" +#define UV_MMTIMER_DESC "SGI UV Memory Mapped RTC Timer" +#define UV_MMTIMER_VERSION "1.0" + +static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); +static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma); + +/* + * Period in femtoseconds (10^-15 s) + */ +static unsigned long uv_mmtimer_femtoperiod; + +static const struct file_operations uv_mmtimer_fops = { + .owner = THIS_MODULE, + .mmap = uv_mmtimer_mmap, + .unlocked_ioctl = uv_mmtimer_ioctl, +}; + +/** + * uv_mmtimer_ioctl - ioctl interface for /dev/uv_mmtimer + * @file: file structure for the device + * @cmd: command to execute + * @arg: optional argument to command + * + * Executes the command specified by @cmd. Returns 0 for success, < 0 for + * failure. + * + * Valid commands: + * + * %MMTIMER_GETOFFSET - Should return the offset (relative to the start + * of the page where the registers are mapped) for the counter in question. + * + * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15) + * seconds + * + * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address + * specified by @arg + * + * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter + * + * %MMTIMER_MMAPAVAIL - Returns 1 if registers can be mmap'd into userspace + * + * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it + * in the address specified by @arg. + */ +static long uv_mmtimer_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + case MMTIMER_GETOFFSET: /* offset of the counter */ + /* + * UV RTC register is on its own page + */ + if (PAGE_SIZE <= (1 << 16)) + ret = ((UV_LOCAL_MMR_BASE | UVH_RTC) & (PAGE_SIZE-1)) + / 8; + else + ret = -ENOSYS; + break; + + case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */ + if (copy_to_user((unsigned long __user *)arg, + &uv_mmtimer_femtoperiod, sizeof(unsigned long))) + ret = -EFAULT; + break; + + case MMTIMER_GETFREQ: /* frequency in Hz */ + if (copy_to_user((unsigned long __user *)arg, + &sn_rtc_cycles_per_second, + sizeof(unsigned long))) + ret = -EFAULT; + break; + + case MMTIMER_GETBITS: /* number of bits in the clock */ + ret = hweight64(UVH_RTC_REAL_TIME_CLOCK_MASK); + break; + + case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */ + ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0; + break; + + case MMTIMER_GETCOUNTER: + if (copy_to_user((unsigned long __user *)arg, + (unsigned long *)uv_local_mmr_address(UVH_RTC), + sizeof(unsigned long))) + ret = -EFAULT; + break; + default: + ret = -ENOTTY; + break; + } + return ret; +} + +/** + * uv_mmtimer_mmap - maps the clock's registers into userspace + * @file: file structure for the device + * @vma: VMA to map the registers into + * + * Calls remap_pfn_range() to map the clock's registers into + * the calling process' address space. + */ +static int uv_mmtimer_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long uv_mmtimer_addr; + + if (vma->vm_end - vma->vm_start != PAGE_SIZE) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) + return -EPERM; + + if (PAGE_SIZE > (1 << 16)) + return -ENOSYS; + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + uv_mmtimer_addr = UV_LOCAL_MMR_BASE | UVH_RTC; + uv_mmtimer_addr &= ~(PAGE_SIZE - 1); + uv_mmtimer_addr &= 0xfffffffffffffffUL; + + if (remap_pfn_range(vma, vma->vm_start, uv_mmtimer_addr >> PAGE_SHIFT, + PAGE_SIZE, vma->vm_page_prot)) { + printk(KERN_ERR "remap_pfn_range failed in uv_mmtimer_mmap\n"); + return -EAGAIN; + } + + return 0; +} + +static struct miscdevice uv_mmtimer_miscdev = { + MISC_DYNAMIC_MINOR, + UV_MMTIMER_NAME, + &uv_mmtimer_fops +}; + + +/** + * uv_mmtimer_init - device initialization routine + * + * Does initial setup for the uv_mmtimer device. + */ +static int __init uv_mmtimer_init(void) +{ + if (!is_uv_system()) { + printk(KERN_ERR "%s: Hardware unsupported\n", UV_MMTIMER_NAME); + return -1; + } + + /* + * Sanity check the cycles/sec variable + */ + if (sn_rtc_cycles_per_second < 100000) { + printk(KERN_ERR "%s: unable to determine clock frequency\n", + UV_MMTIMER_NAME); + return -1; + } + + uv_mmtimer_femtoperiod = ((unsigned long)1E15 + + sn_rtc_cycles_per_second / 2) / + sn_rtc_cycles_per_second; + + if (misc_register(&uv_mmtimer_miscdev)) { + printk(KERN_ERR "%s: failed to register device\n", + UV_MMTIMER_NAME); + return -1; + } + + printk(KERN_INFO "%s: v%s, %ld MHz\n", UV_MMTIMER_DESC, + UV_MMTIMER_VERSION, + sn_rtc_cycles_per_second/(unsigned long)1E6); + + return 0; +} + +module_init(uv_mmtimer_init); -- cgit v1.2.3 From 156dd635e26ab2b356be139ec4b5651afd3805e2 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 23 Sep 2009 15:57:16 -0700 Subject: bfin-otp: add writing support The on-chip OTP may be written at runtime, so enable support for it in the driver. However, since writing should really be done only on development systems, don't bend over backwards to make sure the simple software lock is per-fd -- per-device is OK. Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/bfin-otp.c | 173 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 129 insertions(+), 44 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/bfin-otp.c b/drivers/char/bfin-otp.c index 0a01329451e4..e3dd24bff514 100644 --- a/drivers/char/bfin-otp.c +++ b/drivers/char/bfin-otp.c @@ -1,8 +1,7 @@ /* * Blackfin On-Chip OTP Memory Interface - * Supports BF52x/BF54x * - * Copyright 2007-2008 Analog Devices Inc. + * Copyright 2007-2009 Analog Devices Inc. * * Enter bugs at http://blackfin.uclinux.org/ * @@ -17,8 +16,10 @@ #include #include #include +#include #include +#include #include #define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args) @@ -30,39 +31,6 @@ static DEFINE_MUTEX(bfin_otp_lock); -/* OTP Boot ROM functions */ -#define _BOOTROM_OTP_COMMAND 0xEF000018 -#define _BOOTROM_OTP_READ 0xEF00001A -#define _BOOTROM_OTP_WRITE 0xEF00001C - -static u32 (* const otp_command)(u32 command, u32 value) = (void *)_BOOTROM_OTP_COMMAND; -static u32 (* const otp_read)(u32 page, u32 flags, u64 *page_content) = (void *)_BOOTROM_OTP_READ; -static u32 (* const otp_write)(u32 page, u32 flags, u64 *page_content) = (void *)_BOOTROM_OTP_WRITE; - -/* otp_command(): defines for "command" */ -#define OTP_INIT 0x00000001 -#define OTP_CLOSE 0x00000002 - -/* otp_{read,write}(): defines for "flags" */ -#define OTP_LOWER_HALF 0x00000000 /* select upper/lower 64-bit half (bit 0) */ -#define OTP_UPPER_HALF 0x00000001 -#define OTP_NO_ECC 0x00000010 /* do not use ECC */ -#define OTP_LOCK 0x00000020 /* sets page protection bit for page */ -#define OTP_ACCESS_READ 0x00001000 -#define OTP_ACCESS_READWRITE 0x00002000 - -/* Return values for all functions */ -#define OTP_SUCCESS 0x00000000 -#define OTP_MASTER_ERROR 0x001 -#define OTP_WRITE_ERROR 0x003 -#define OTP_READ_ERROR 0x005 -#define OTP_ACC_VIO_ERROR 0x009 -#define OTP_DATA_MULT_ERROR 0x011 -#define OTP_ECC_MULT_ERROR 0x021 -#define OTP_PREV_WR_ERROR 0x041 -#define OTP_DATA_SB_WARN 0x100 -#define OTP_ECC_SB_WARN 0x200 - /** * bfin_otp_read - Read OTP pages * @@ -86,9 +54,11 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, page = *pos / (sizeof(u64) * 2); while (bytes_done < count) { flags = (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF); - stamp("processing page %i (%s)", page, (flags == OTP_UPPER_HALF ? "upper" : "lower")); - ret = otp_read(page, flags, &content); + stamp("processing page %i (0x%x:%s)", page, flags, + (flags & OTP_UPPER_HALF ? "upper" : "lower")); + ret = bfrom_OtpRead(page, flags, &content); if (ret & OTP_MASTER_ERROR) { + stamp("error from otp: 0x%x", ret); bytes_done = -EIO; break; } @@ -96,7 +66,7 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, bytes_done = -EFAULT; break; } - if (flags == OTP_UPPER_HALF) + if (flags & OTP_UPPER_HALF) ++page; bytes_done += sizeof(content); *pos += sizeof(content); @@ -108,14 +78,53 @@ static ssize_t bfin_otp_read(struct file *file, char __user *buff, size_t count, } #ifdef CONFIG_BFIN_OTP_WRITE_ENABLE +static bool allow_writes; + +/** + * bfin_otp_init_timing - setup OTP timing parameters + * + * Required before doing any write operation. Algorithms from HRM. + */ +static u32 bfin_otp_init_timing(void) +{ + u32 tp1, tp2, tp3, timing; + + tp1 = get_sclk() / 1000000; + tp2 = (2 * get_sclk() / 10000000) << 8; + tp3 = (0x1401) << 15; + timing = tp1 | tp2 | tp3; + if (bfrom_OtpCommand(OTP_INIT, timing)) + return 0; + + return timing; +} + +/** + * bfin_otp_deinit_timing - set timings to only allow reads + * + * Should be called after all writes are done. + */ +static void bfin_otp_deinit_timing(u32 timing) +{ + /* mask bits [31:15] so that any attempts to write fail */ + bfrom_OtpCommand(OTP_CLOSE, 0); + bfrom_OtpCommand(OTP_INIT, timing & ~(-1 << 15)); + bfrom_OtpCommand(OTP_CLOSE, 0); +} + /** - * bfin_otp_write - Write OTP pages + * bfin_otp_write - write OTP pages * * All writes must be in half page chunks (half page == 64 bits). */ static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t count, loff_t *pos) { - stampit(); + ssize_t bytes_done; + u32 timing, page, base_flags, flags, ret; + u64 content; + + if (!allow_writes) + return -EACCES; if (count % sizeof(u64)) return -EMSGSIZE; @@ -123,20 +132,96 @@ static ssize_t bfin_otp_write(struct file *filp, const char __user *buff, size_t if (mutex_lock_interruptible(&bfin_otp_lock)) return -ERESTARTSYS; - /* need otp_init() documentation before this can be implemented */ + stampit(); + + timing = bfin_otp_init_timing(); + if (timing == 0) { + mutex_unlock(&bfin_otp_lock); + return -EIO; + } + + base_flags = OTP_CHECK_FOR_PREV_WRITE; + + bytes_done = 0; + page = *pos / (sizeof(u64) * 2); + while (bytes_done < count) { + flags = base_flags | (*pos % (sizeof(u64) * 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF); + stamp("processing page %i (0x%x:%s) from %p", page, flags, + (flags & OTP_UPPER_HALF ? "upper" : "lower"), buff + bytes_done); + if (copy_from_user(&content, buff + bytes_done, sizeof(content))) { + bytes_done = -EFAULT; + break; + } + ret = bfrom_OtpWrite(page, flags, &content); + if (ret & OTP_MASTER_ERROR) { + stamp("error from otp: 0x%x", ret); + bytes_done = -EIO; + break; + } + if (flags & OTP_UPPER_HALF) + ++page; + bytes_done += sizeof(content); + *pos += sizeof(content); + } + + bfin_otp_deinit_timing(timing); mutex_unlock(&bfin_otp_lock); + return bytes_done; +} + +static long bfin_otp_ioctl(struct file *filp, unsigned cmd, unsigned long arg) +{ + stampit(); + + switch (cmd) { + case OTPLOCK: { + u32 timing; + int ret = -EIO; + + if (!allow_writes) + return -EACCES; + + if (mutex_lock_interruptible(&bfin_otp_lock)) + return -ERESTARTSYS; + + timing = bfin_otp_init_timing(); + if (timing) { + u32 otp_result = bfrom_OtpWrite(arg, OTP_LOCK, NULL); + stamp("locking page %lu resulted in 0x%x", arg, otp_result); + if (!(otp_result & OTP_MASTER_ERROR)) + ret = 0; + + bfin_otp_deinit_timing(timing); + } + + mutex_unlock(&bfin_otp_lock); + + return ret; + } + + case MEMLOCK: + allow_writes = false; + return 0; + + case MEMUNLOCK: + allow_writes = true; + return 0; + } + return -EINVAL; } #else # define bfin_otp_write NULL +# define bfin_otp_ioctl NULL #endif static struct file_operations bfin_otp_fops = { - .owner = THIS_MODULE, - .read = bfin_otp_read, - .write = bfin_otp_write, + .owner = THIS_MODULE, + .unlocked_ioctl = bfin_otp_ioctl, + .read = bfin_otp_read, + .write = bfin_otp_write, }; static struct miscdevice bfin_otp_misc_device = { -- cgit v1.2.3 From 8d65af789f3e2cf4cfbdbf71a0f7a61ebcd41d38 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Wed, 23 Sep 2009 15:57:19 -0700 Subject: sysctl: remove "struct file *" argument of ->proc_handler It's unused. It isn't needed -- read or write flag is already passed and sysctl shouldn't care about the rest. It _was_ used in two places at arch/frv for some reason. Signed-off-by: Alexey Dobriyan Cc: David Howells Cc: "Eric W. Biederman" Cc: Al Viro Cc: Ralf Baechle Cc: Martin Schwidefsky Cc: Ingo Molnar Cc: "David S. Miller" Cc: James Morris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/frv/kernel/pm.c | 14 +++---- arch/mips/lasat/sysctl.c | 18 ++++----- arch/s390/appldata/appldata_base.c | 9 ++--- arch/s390/kernel/debug.c | 4 +- arch/s390/mm/cmm.c | 4 +- arch/x86/include/asm/nmi.h | 3 +- arch/x86/kernel/apic/nmi.c | 4 +- arch/x86/kernel/vsyscall_64.c | 10 +---- drivers/cdrom/cdrom.c | 8 ++-- drivers/char/random.c | 4 +- drivers/net/wireless/arlan-proc.c | 28 +++++++------- drivers/parport/procfs.c | 12 +++--- fs/coda/coda_int.h | 1 + fs/drop_caches.c | 4 +- fs/file_table.c | 6 +-- fs/proc/proc_sysctl.c | 2 +- fs/xfs/linux-2.6/xfs_sysctl.c | 3 +- include/linux/fs.h | 2 +- include/linux/ftrace.h | 4 +- include/linux/hugetlb.h | 6 +-- include/linux/mm.h | 2 +- include/linux/mmzone.h | 13 +++---- include/linux/sched.h | 8 ++-- include/linux/security.h | 2 +- include/linux/swap.h | 2 +- include/linux/sysctl.h | 19 +++++----- include/linux/writeback.h | 11 +++--- include/net/ip.h | 2 +- include/net/ndisc.h | 2 - ipc/ipc_sysctl.c | 16 ++++---- ipc/mq_sysctl.c | 8 ++-- kernel/hung_task.c | 4 +- kernel/sched.c | 4 +- kernel/sched_fair.c | 4 +- kernel/slow-work.c | 12 +++--- kernel/softlockup.c | 4 +- kernel/sysctl.c | 78 ++++++++++++++++---------------------- kernel/trace/ftrace.c | 4 +- kernel/trace/trace_stack.c | 4 +- kernel/utsname_sysctl.c | 4 +- mm/hugetlb.c | 12 +++--- mm/page-writeback.c | 20 +++++----- mm/page_alloc.c | 24 ++++++------ mm/vmscan.c | 4 +- net/bridge/br_netfilter.c | 4 +- net/decnet/dn_dev.c | 5 +-- net/decnet/sysctl_net_decnet.c | 2 - net/ipv4/devinet.c | 12 +++--- net/ipv4/route.c | 7 ++-- net/ipv4/sysctl_net_ipv4.c | 16 ++++---- net/ipv6/addrconf.c | 8 ++-- net/ipv6/ndisc.c | 8 ++-- net/ipv6/route.c | 4 +- net/irda/irsysctl.c | 8 ++-- net/netfilter/ipvs/ip_vs_ctl.c | 8 ++-- net/netfilter/nf_log.c | 4 +- net/phonet/sysctl.c | 4 +- net/sunrpc/sysctl.c | 4 +- net/sunrpc/xprtrdma/svc_rdma.c | 2 +- security/min_addr.c | 4 +- 60 files changed, 239 insertions(+), 270 deletions(-) (limited to 'drivers/char') diff --git a/arch/frv/kernel/pm.c b/arch/frv/kernel/pm.c index be722fc1acff..0d4d3e3a4cfc 100644 --- a/arch/frv/kernel/pm.c +++ b/arch/frv/kernel/pm.c @@ -150,7 +150,7 @@ static int user_atoi(char __user *ubuf, size_t len) /* * Send us to sleep. */ -static int sysctl_pm_do_suspend(ctl_table *ctl, int write, struct file *filp, +static int sysctl_pm_do_suspend(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *fpos) { int retval, mode; @@ -198,13 +198,13 @@ static int try_set_cmode(int new_cmode) } -static int cmode_procctl(ctl_table *ctl, int write, struct file *filp, +static int cmode_procctl(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *fpos) { int new_cmode; if (!write) - return proc_dointvec(ctl, write, filp, buffer, lenp, fpos); + return proc_dointvec(ctl, write, buffer, lenp, fpos); new_cmode = user_atoi(buffer, *lenp); @@ -301,13 +301,13 @@ static int try_set_cm(int new_cm) return 0; } -static int p0_procctl(ctl_table *ctl, int write, struct file *filp, +static int p0_procctl(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *fpos) { int new_p0; if (!write) - return proc_dointvec(ctl, write, filp, buffer, lenp, fpos); + return proc_dointvec(ctl, write, buffer, lenp, fpos); new_p0 = user_atoi(buffer, *lenp); @@ -345,13 +345,13 @@ static int p0_sysctl(ctl_table *table, return 1; } -static int cm_procctl(ctl_table *ctl, int write, struct file *filp, +static int cm_procctl(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *fpos) { int new_cm; if (!write) - return proc_dointvec(ctl, write, filp, buffer, lenp, fpos); + return proc_dointvec(ctl, write, buffer, lenp, fpos); new_cm = user_atoi(buffer, *lenp); diff --git a/arch/mips/lasat/sysctl.c b/arch/mips/lasat/sysctl.c index 3f04d4c406b7..b3deed8db619 100644 --- a/arch/mips/lasat/sysctl.c +++ b/arch/mips/lasat/sysctl.c @@ -56,12 +56,12 @@ int sysctl_lasatstring(ctl_table *table, /* And the same for proc */ -int proc_dolasatstring(ctl_table *table, int write, struct file *filp, +int proc_dolasatstring(ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { int r; - r = proc_dostring(table, write, filp, buffer, lenp, ppos); + r = proc_dostring(table, write, buffer, lenp, ppos); if ((!write) || r) return r; @@ -71,12 +71,12 @@ int proc_dolasatstring(ctl_table *table, int write, struct file *filp, } /* proc function to write EEPROM after changing int entry */ -int proc_dolasatint(ctl_table *table, int write, struct file *filp, +int proc_dolasatint(ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { int r; - r = proc_dointvec(table, write, filp, buffer, lenp, ppos); + r = proc_dointvec(table, write, buffer, lenp, ppos); if ((!write) || r) return r; @@ -89,7 +89,7 @@ int proc_dolasatint(ctl_table *table, int write, struct file *filp, static int rtctmp; /* proc function to read/write RealTime Clock */ -int proc_dolasatrtc(ctl_table *table, int write, struct file *filp, +int proc_dolasatrtc(ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { struct timespec ts; @@ -102,7 +102,7 @@ int proc_dolasatrtc(ctl_table *table, int write, struct file *filp, if (rtctmp < 0) rtctmp = 0; } - r = proc_dointvec(table, write, filp, buffer, lenp, ppos); + r = proc_dointvec(table, write, buffer, lenp, ppos); if (r) return r; @@ -154,7 +154,7 @@ int sysctl_lasat_rtc(ctl_table *table, #endif #ifdef CONFIG_INET -int proc_lasat_ip(ctl_table *table, int write, struct file *filp, +int proc_lasat_ip(ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { unsigned int ip; @@ -231,12 +231,12 @@ static int sysctl_lasat_prid(ctl_table *table, return 0; } -int proc_lasat_prid(ctl_table *table, int write, struct file *filp, +int proc_lasat_prid(ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { int r; - r = proc_dointvec(table, write, filp, buffer, lenp, ppos); + r = proc_dointvec(table, write, buffer, lenp, ppos); if (r < 0) return r; if (write) { diff --git a/arch/s390/appldata/appldata_base.c b/arch/s390/appldata/appldata_base.c index 264528e4f58d..b55fd7ed1c31 100644 --- a/arch/s390/appldata/appldata_base.c +++ b/arch/s390/appldata/appldata_base.c @@ -50,10 +50,9 @@ static struct platform_device *appldata_pdev; * /proc entries (sysctl) */ static const char appldata_proc_name[APPLDATA_PROC_NAME_LENGTH] = "appldata"; -static int appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, +static int appldata_timer_handler(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos); static int appldata_interval_handler(ctl_table *ctl, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos); @@ -247,7 +246,7 @@ __appldata_vtimer_setup(int cmd) * Start/Stop timer, show status of timer (0 = not active, 1 = active) */ static int -appldata_timer_handler(ctl_table *ctl, int write, struct file *filp, +appldata_timer_handler(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int len; @@ -289,7 +288,7 @@ out: * current timer interval. */ static int -appldata_interval_handler(ctl_table *ctl, int write, struct file *filp, +appldata_interval_handler(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int len, interval; @@ -335,7 +334,7 @@ out: * monitoring (0 = not in process, 1 = in process) */ static int -appldata_generic_handler(ctl_table *ctl, int write, struct file *filp, +appldata_generic_handler(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct appldata_ops *ops = NULL, *tmp_ops; diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c index 4c512561687d..20f282c911c2 100644 --- a/arch/s390/kernel/debug.c +++ b/arch/s390/kernel/debug.c @@ -881,11 +881,11 @@ static int debug_active=1; * if debug_active is already off */ static int -s390dbf_procactive(ctl_table *table, int write, struct file *filp, +s390dbf_procactive(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { if (!write || debug_stoppable || !debug_active) - return proc_dointvec(table, write, filp, buffer, lenp, ppos); + return proc_dointvec(table, write, buffer, lenp, ppos); else return 0; } diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c index 413c240cbca7..b201135cc18c 100644 --- a/arch/s390/mm/cmm.c +++ b/arch/s390/mm/cmm.c @@ -262,7 +262,7 @@ cmm_skip_blanks(char *cp, char **endp) static struct ctl_table cmm_table[]; static int -cmm_pages_handler(ctl_table *ctl, int write, struct file *filp, +cmm_pages_handler(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { char buf[16], *p; @@ -303,7 +303,7 @@ cmm_pages_handler(ctl_table *ctl, int write, struct file *filp, } static int -cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp, +cmm_timeout_handler(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { char buf[64], *p; diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h index e63cf7d441e1..139d4c1a33a7 100644 --- a/arch/x86/include/asm/nmi.h +++ b/arch/x86/include/asm/nmi.h @@ -40,8 +40,7 @@ extern unsigned int nmi_watchdog; #define NMI_INVALID 3 struct ctl_table; -struct file; -extern int proc_nmi_enabled(struct ctl_table *, int , struct file *, +extern int proc_nmi_enabled(struct ctl_table *, int , void __user *, size_t *, loff_t *); extern int unknown_nmi_panic; diff --git a/arch/x86/kernel/apic/nmi.c b/arch/x86/kernel/apic/nmi.c index cb66a22d98ad..7ff61d6a188a 100644 --- a/arch/x86/kernel/apic/nmi.c +++ b/arch/x86/kernel/apic/nmi.c @@ -508,14 +508,14 @@ static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu) /* * proc handler for /proc/sys/kernel/nmi */ -int proc_nmi_enabled(struct ctl_table *table, int write, struct file *file, +int proc_nmi_enabled(struct ctl_table *table, int write, void __user *buffer, size_t *length, loff_t *ppos) { int old_state; nmi_watchdog_enabled = (atomic_read(&nmi_active) > 0) ? 1 : 0; old_state = nmi_watchdog_enabled; - proc_dointvec(table, write, file, buffer, length, ppos); + proc_dointvec(table, write, buffer, length, ppos); if (!!old_state == !!nmi_watchdog_enabled) return 0; diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index cf53a78e2dcf..8cb4974ff599 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c @@ -228,19 +228,11 @@ static long __vsyscall(3) venosys_1(void) } #ifdef CONFIG_SYSCTL - -static int -vsyscall_sysctl_change(ctl_table *ctl, int write, struct file * filp, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - return proc_dointvec(ctl, write, filp, buffer, lenp, ppos); -} - static ctl_table kernel_table2[] = { { .procname = "vsyscall64", .data = &vsyscall_gtod_data.sysctl_enabled, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = vsyscall_sysctl_change }, + .proc_handler = proc_dointvec }, {} }; diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c index 71d1b9bab70b..614da5b8613a 100644 --- a/drivers/cdrom/cdrom.c +++ b/drivers/cdrom/cdrom.c @@ -3412,7 +3412,7 @@ static int cdrom_print_info(const char *header, int val, char *info, return 0; } -static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp, +static int cdrom_sysctl_info(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int pos; @@ -3489,7 +3489,7 @@ static int cdrom_sysctl_info(ctl_table *ctl, int write, struct file * filp, goto done; doit: mutex_unlock(&cdrom_mutex); - return proc_dostring(ctl, write, filp, buffer, lenp, ppos); + return proc_dostring(ctl, write, buffer, lenp, ppos); done: printk(KERN_INFO "cdrom: info buffer too small\n"); goto doit; @@ -3525,12 +3525,12 @@ static void cdrom_update_settings(void) mutex_unlock(&cdrom_mutex); } -static int cdrom_sysctl_handler(ctl_table *ctl, int write, struct file * filp, +static int cdrom_sysctl_handler(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; - ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); if (write) { diff --git a/drivers/char/random.c b/drivers/char/random.c index d8a9255e1a3f..04b505e5a5e2 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -1231,7 +1231,7 @@ static char sysctl_bootid[16]; * as an ASCII string in the standard UUID format. If accesses via the * sysctl system call, it is returned as 16 bytes of binary data. */ -static int proc_do_uuid(ctl_table *table, int write, struct file *filp, +static int proc_do_uuid(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { ctl_table fake_table; @@ -1254,7 +1254,7 @@ static int proc_do_uuid(ctl_table *table, int write, struct file *filp, fake_table.data = buf; fake_table.maxlen = sizeof(buf); - return proc_dostring(&fake_table, write, filp, buffer, lenp, ppos); + return proc_dostring(&fake_table, write, buffer, lenp, ppos); } static int uuid_strategy(ctl_table *table, diff --git a/drivers/net/wireless/arlan-proc.c b/drivers/net/wireless/arlan-proc.c index 2ab1d59870f4..a8b689635a3b 100644 --- a/drivers/net/wireless/arlan-proc.c +++ b/drivers/net/wireless/arlan-proc.c @@ -402,7 +402,7 @@ static int arlan_setup_card_by_book(struct net_device *dev) static char arlan_drive_info[ARLAN_STR_SIZE] = "A655\n\0"; -static int arlan_sysctl_info(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_info(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int i; @@ -629,7 +629,7 @@ final: *lenp = pos; if (!write) - retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos); + retv = proc_dostring(ctl, write, buffer, lenp, ppos); else { *lenp = 0; @@ -639,7 +639,7 @@ final: } -static int arlan_sysctl_info161719(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_info161719(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int i; @@ -669,11 +669,11 @@ static int arlan_sysctl_info161719(ctl_table * ctl, int write, struct file *filp final: *lenp = pos; - retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos); + retv = proc_dostring(ctl, write, buffer, lenp, ppos); return retv; } -static int arlan_sysctl_infotxRing(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_infotxRing(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int i; @@ -698,11 +698,11 @@ static int arlan_sysctl_infotxRing(ctl_table * ctl, int write, struct file *filp SARLBNpln(u_char, txBuffer, 0x800); final: *lenp = pos; - retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos); + retv = proc_dostring(ctl, write, buffer, lenp, ppos); return retv; } -static int arlan_sysctl_inforxRing(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_inforxRing(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int i; @@ -726,11 +726,11 @@ static int arlan_sysctl_inforxRing(ctl_table * ctl, int write, struct file *filp SARLBNpln(u_char, rxBuffer, 0x800); final: *lenp = pos; - retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos); + retv = proc_dostring(ctl, write, buffer, lenp, ppos); return retv; } -static int arlan_sysctl_info18(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_info18(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int i; @@ -756,7 +756,7 @@ static int arlan_sysctl_info18(ctl_table * ctl, int write, struct file *filp, final: *lenp = pos; - retv = proc_dostring(ctl, write, filp, buffer, lenp, ppos); + retv = proc_dostring(ctl, write, buffer, lenp, ppos); return retv; } @@ -766,7 +766,7 @@ final: static char conf_reset_result[200]; -static int arlan_configure(ctl_table * ctl, int write, struct file *filp, +static int arlan_configure(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int pos = 0; @@ -788,10 +788,10 @@ static int arlan_configure(ctl_table * ctl, int write, struct file *filp, return -1; *lenp = pos; - return proc_dostring(ctl, write, filp, buffer, lenp, ppos); + return proc_dostring(ctl, write, buffer, lenp, ppos); } -static int arlan_sysctl_reset(ctl_table * ctl, int write, struct file *filp, +static int arlan_sysctl_reset(ctl_table * ctl, int write, void __user *buffer, size_t * lenp, loff_t *ppos) { int pos = 0; @@ -811,7 +811,7 @@ static int arlan_sysctl_reset(ctl_table * ctl, int write, struct file *filp, } else return -1; *lenp = pos + 3; - return proc_dostring(ctl, write, filp, buffer, lenp, ppos); + return proc_dostring(ctl, write, buffer, lenp, ppos); } diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c index 554e11f9e1ce..8eefe56f1cbe 100644 --- a/drivers/parport/procfs.c +++ b/drivers/parport/procfs.c @@ -31,7 +31,7 @@ #define PARPORT_MIN_SPINTIME_VALUE 1 #define PARPORT_MAX_SPINTIME_VALUE 1000 -static int do_active_device(ctl_table *table, int write, struct file *filp, +static int do_active_device(ctl_table *table, int write, void __user *result, size_t *lenp, loff_t *ppos) { struct parport *port = (struct parport *)table->extra1; @@ -68,7 +68,7 @@ static int do_active_device(ctl_table *table, int write, struct file *filp, } #ifdef CONFIG_PARPORT_1284 -static int do_autoprobe(ctl_table *table, int write, struct file *filp, +static int do_autoprobe(ctl_table *table, int write, void __user *result, size_t *lenp, loff_t *ppos) { struct parport_device_info *info = table->extra2; @@ -111,7 +111,7 @@ static int do_autoprobe(ctl_table *table, int write, struct file *filp, #endif /* IEEE1284.3 support. */ static int do_hardware_base_addr (ctl_table *table, int write, - struct file *filp, void __user *result, + void __user *result, size_t *lenp, loff_t *ppos) { struct parport *port = (struct parport *)table->extra1; @@ -139,7 +139,7 @@ static int do_hardware_base_addr (ctl_table *table, int write, } static int do_hardware_irq (ctl_table *table, int write, - struct file *filp, void __user *result, + void __user *result, size_t *lenp, loff_t *ppos) { struct parport *port = (struct parport *)table->extra1; @@ -167,7 +167,7 @@ static int do_hardware_irq (ctl_table *table, int write, } static int do_hardware_dma (ctl_table *table, int write, - struct file *filp, void __user *result, + void __user *result, size_t *lenp, loff_t *ppos) { struct parport *port = (struct parport *)table->extra1; @@ -195,7 +195,7 @@ static int do_hardware_dma (ctl_table *table, int write, } static int do_hardware_modes (ctl_table *table, int write, - struct file *filp, void __user *result, + void __user *result, size_t *lenp, loff_t *ppos) { struct parport *port = (struct parport *)table->extra1; diff --git a/fs/coda/coda_int.h b/fs/coda/coda_int.h index 8ccd5ed81d9c..d99860a33890 100644 --- a/fs/coda/coda_int.h +++ b/fs/coda/coda_int.h @@ -2,6 +2,7 @@ #define _CODA_INT_ struct dentry; +struct file; extern struct file_system_type coda_fs_type; extern unsigned long coda_timeout; diff --git a/fs/drop_caches.c b/fs/drop_caches.c index a2edb7913447..31f4b0e6d72c 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -63,9 +63,9 @@ static void drop_slab(void) } int drop_caches_sysctl_handler(ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *length, loff_t *ppos) + void __user *buffer, size_t *length, loff_t *ppos) { - proc_dointvec_minmax(table, write, file, buffer, length, ppos); + proc_dointvec_minmax(table, write, buffer, length, ppos); if (write) { if (sysctl_drop_caches & 1) drop_pagecache(); diff --git a/fs/file_table.c b/fs/file_table.c index 334ce39881f8..8eb44042e009 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -74,14 +74,14 @@ EXPORT_SYMBOL_GPL(get_max_files); * Handle nr_files sysctl */ #if defined(CONFIG_SYSCTL) && defined(CONFIG_PROC_FS) -int proc_nr_files(ctl_table *table, int write, struct file *filp, +int proc_nr_files(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { files_stat.nr_files = get_nr_files(); - return proc_dointvec(table, write, filp, buffer, lenp, ppos); + return proc_dointvec(table, write, buffer, lenp, ppos); } #else -int proc_nr_files(ctl_table *table, int write, struct file *filp, +int proc_nr_files(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { return -ENOSYS; diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index 9b1e4e9a16bf..f667e8aeabdf 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c @@ -153,7 +153,7 @@ static ssize_t proc_sys_call_handler(struct file *filp, void __user *buf, /* careful: calling conventions are nasty here */ res = count; - error = table->proc_handler(table, write, filp, buf, &res, ppos); + error = table->proc_handler(table, write, buf, &res, ppos); if (!error) error = res; out: diff --git a/fs/xfs/linux-2.6/xfs_sysctl.c b/fs/xfs/linux-2.6/xfs_sysctl.c index 916c0ffb6083..c5bc67c4e3bb 100644 --- a/fs/xfs/linux-2.6/xfs_sysctl.c +++ b/fs/xfs/linux-2.6/xfs_sysctl.c @@ -26,7 +26,6 @@ STATIC int xfs_stats_clear_proc_handler( ctl_table *ctl, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) @@ -34,7 +33,7 @@ xfs_stats_clear_proc_handler( int c, ret, *valp = ctl->data; __uint32_t vn_active; - ret = proc_dointvec_minmax(ctl, write, filp, buffer, lenp, ppos); + ret = proc_dointvec_minmax(ctl, write, buffer, lenp, ppos); if (!ret && write && *valp) { printk("XFS Clearing xfsstats\n"); diff --git a/include/linux/fs.h b/include/linux/fs.h index 51803528b095..33ed6644abd0 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2467,7 +2467,7 @@ ssize_t simple_attr_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos); struct ctl_table; -int proc_nr_files(struct ctl_table *table, int write, struct file *filp, +int proc_nr_files(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); int __init get_filesystem_list(char *buf); diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 3c0924a18daf..cd3d2abaf30a 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -19,7 +19,7 @@ extern int ftrace_enabled; extern int ftrace_enable_sysctl(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos); typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip); @@ -94,7 +94,7 @@ static inline void ftrace_start(void) { } extern int stack_tracer_enabled; int stack_trace_sysctl(struct ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos); #endif diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 176e7ee73eff..11ab19ac6b3d 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -20,9 +20,9 @@ static inline int is_vm_hugetlb_page(struct vm_area_struct *vma) } void reset_vma_resv_huge_pages(struct vm_area_struct *vma); -int hugetlb_sysctl_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); -int hugetlb_overcommit_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); -int hugetlb_treat_movable_handler(struct ctl_table *, int, struct file *, void __user *, size_t *, loff_t *); +int hugetlb_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); +int hugetlb_overcommit_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); +int hugetlb_treat_movable_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *); int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, struct page **, struct vm_area_struct **, diff --git a/include/linux/mm.h b/include/linux/mm.h index b6eae5e3144b..87218ae84e36 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1279,7 +1279,7 @@ int in_gate_area_no_task(unsigned long addr); #define in_gate_area(task, addr) ({(void)task; in_gate_area_no_task(addr);}) #endif /* __HAVE_ARCH_GATE_AREA */ -int drop_caches_sysctl_handler(struct ctl_table *, int, struct file *, +int drop_caches_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); unsigned long shrink_slab(unsigned long scanned, gfp_t gfp_mask, unsigned long lru_pages); diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 652ef01be582..6f7561730d88 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -755,21 +755,20 @@ static inline int is_dma(struct zone *zone) /* These two functions are used to setup the per zone pages min values */ struct ctl_table; -struct file; -int min_free_kbytes_sysctl_handler(struct ctl_table *, int, struct file *, +int min_free_kbytes_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); extern int sysctl_lowmem_reserve_ratio[MAX_NR_ZONES-1]; -int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *, int, struct file *, +int lowmem_reserve_ratio_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); -int percpu_pagelist_fraction_sysctl_handler(struct ctl_table *, int, struct file *, +int percpu_pagelist_fraction_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); int sysctl_min_unmapped_ratio_sysctl_handler(struct ctl_table *, int, - struct file *, void __user *, size_t *, loff_t *); + void __user *, size_t *, loff_t *); int sysctl_min_slab_ratio_sysctl_handler(struct ctl_table *, int, - struct file *, void __user *, size_t *, loff_t *); + void __user *, size_t *, loff_t *); extern int numa_zonelist_order_handler(struct ctl_table *, int, - struct file *, void __user *, size_t *, loff_t *); + void __user *, size_t *, loff_t *); extern char numa_zonelist_order[]; #define NUMA_ZONELIST_ORDER_LEN 16 /* string buffer size */ diff --git a/include/linux/sched.h b/include/linux/sched.h index e951bd2eb9fc..811cd96524d7 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -309,7 +309,7 @@ extern void softlockup_tick(void); extern void touch_softlockup_watchdog(void); extern void touch_all_softlockup_watchdogs(void); extern int proc_dosoftlockup_thresh(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos); extern unsigned int softlockup_panic; extern int softlockup_thresh; @@ -331,7 +331,7 @@ extern unsigned long sysctl_hung_task_check_count; extern unsigned long sysctl_hung_task_timeout_secs; extern unsigned long sysctl_hung_task_warnings; extern int proc_dohung_task_timeout_secs(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos); #endif @@ -1906,7 +1906,7 @@ extern unsigned int sysctl_sched_time_avg; extern unsigned int sysctl_timer_migration; int sched_nr_latency_handler(struct ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *length, + void __user *buffer, size_t *length, loff_t *ppos); #endif #ifdef CONFIG_SCHED_DEBUG @@ -1924,7 +1924,7 @@ extern unsigned int sysctl_sched_rt_period; extern int sysctl_sched_rt_runtime; int sched_rt_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos); extern unsigned int sysctl_sched_compat_yield; diff --git a/include/linux/security.h b/include/linux/security.h index d050b66ab9ef..239e40d0450b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -133,7 +133,7 @@ static inline unsigned long round_hint_to_min(unsigned long hint) return PAGE_ALIGN(mmap_min_addr); return hint; } -extern int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp, +extern int mmap_min_addr_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #ifdef CONFIG_SECURITY diff --git a/include/linux/swap.h b/include/linux/swap.h index 4c78fea989b9..82232dbea3f7 100644 --- a/include/linux/swap.h +++ b/include/linux/swap.h @@ -245,7 +245,7 @@ extern int page_evictable(struct page *page, struct vm_area_struct *vma); extern void scan_mapping_unevictable_pages(struct address_space *); extern unsigned long scan_unevictable_pages; -extern int scan_unevictable_handler(struct ctl_table *, int, struct file *, +extern int scan_unevictable_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); extern int scan_unevictable_register_node(struct node *node); extern void scan_unevictable_unregister_node(struct node *node); diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index e76d3b22a466..1e4743ee6831 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -29,7 +29,6 @@ #include #include -struct file; struct completion; #define CTL_MAXNAME 10 /* how many path components do we allow in a @@ -977,25 +976,25 @@ typedef int ctl_handler (struct ctl_table *table, void __user *oldval, size_t __user *oldlenp, void __user *newval, size_t newlen); -typedef int proc_handler (struct ctl_table *ctl, int write, struct file * filp, +typedef int proc_handler (struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos); -extern int proc_dostring(struct ctl_table *, int, struct file *, +extern int proc_dostring(struct ctl_table *, int, void __user *, size_t *, loff_t *); -extern int proc_dointvec(struct ctl_table *, int, struct file *, +extern int proc_dointvec(struct ctl_table *, int, void __user *, size_t *, loff_t *); -extern int proc_dointvec_minmax(struct ctl_table *, int, struct file *, +extern int proc_dointvec_minmax(struct ctl_table *, int, void __user *, size_t *, loff_t *); -extern int proc_dointvec_jiffies(struct ctl_table *, int, struct file *, +extern int proc_dointvec_jiffies(struct ctl_table *, int, void __user *, size_t *, loff_t *); -extern int proc_dointvec_userhz_jiffies(struct ctl_table *, int, struct file *, +extern int proc_dointvec_userhz_jiffies(struct ctl_table *, int, void __user *, size_t *, loff_t *); -extern int proc_dointvec_ms_jiffies(struct ctl_table *, int, struct file *, +extern int proc_dointvec_ms_jiffies(struct ctl_table *, int, void __user *, size_t *, loff_t *); -extern int proc_doulongvec_minmax(struct ctl_table *, int, struct file *, +extern int proc_doulongvec_minmax(struct ctl_table *, int, void __user *, size_t *, loff_t *); extern int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int, - struct file *, void __user *, size_t *, loff_t *); + void __user *, size_t *, loff_t *); extern int do_sysctl (int __user *name, int nlen, void __user *oldval, size_t __user *oldlenp, diff --git a/include/linux/writeback.h b/include/linux/writeback.h index 75cf58666ff9..66ebddcff664 100644 --- a/include/linux/writeback.h +++ b/include/linux/writeback.h @@ -110,21 +110,20 @@ extern int laptop_mode; extern unsigned long determine_dirtyable_memory(void); extern int dirty_background_ratio_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos); extern int dirty_background_bytes_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos); extern int dirty_ratio_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos); extern int dirty_bytes_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos); struct ctl_table; -struct file; -int dirty_writeback_centisecs_handler(struct ctl_table *, int, struct file *, +int dirty_writeback_centisecs_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); void get_dirty_limits(unsigned long *pbackground, unsigned long *pdirty, diff --git a/include/net/ip.h b/include/net/ip.h index 72c36926c26d..5b26a0bd178e 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -399,7 +399,7 @@ extern void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport, * fed into the routing cache should use these handlers. */ int ipv4_doint_and_flush(ctl_table *ctl, int write, - struct file* filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos); int ipv4_doint_and_flush_strategy(ctl_table *table, void __user *oldval, size_t __user *oldlenp, diff --git a/include/net/ndisc.h b/include/net/ndisc.h index 1459ed3e2697..f76f22d05721 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -55,7 +55,6 @@ enum { #include struct ctl_table; -struct file; struct inet6_dev; struct net_device; struct net_proto_family; @@ -139,7 +138,6 @@ extern int igmp6_event_report(struct sk_buff *skb); #ifdef CONFIG_SYSCTL extern int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, - struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos); diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c index 40eab7314aeb..7d3704750efc 100644 --- a/ipc/ipc_sysctl.c +++ b/ipc/ipc_sysctl.c @@ -27,18 +27,18 @@ static void *get_ipc(ctl_table *table) } #ifdef CONFIG_PROC_SYSCTL -static int proc_ipc_dointvec(ctl_table *table, int write, struct file *filp, +static int proc_ipc_dointvec(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table ipc_table; memcpy(&ipc_table, table, sizeof(ipc_table)); ipc_table.data = get_ipc(table); - return proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos); + return proc_dointvec(&ipc_table, write, buffer, lenp, ppos); } static int proc_ipc_callback_dointvec(ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) + void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table ipc_table; size_t lenp_bef = *lenp; @@ -47,7 +47,7 @@ static int proc_ipc_callback_dointvec(ctl_table *table, int write, memcpy(&ipc_table, table, sizeof(ipc_table)); ipc_table.data = get_ipc(table); - rc = proc_dointvec(&ipc_table, write, filp, buffer, lenp, ppos); + rc = proc_dointvec(&ipc_table, write, buffer, lenp, ppos); if (write && !rc && lenp_bef == *lenp) /* @@ -61,13 +61,13 @@ static int proc_ipc_callback_dointvec(ctl_table *table, int write, } static int proc_ipc_doulongvec_minmax(ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) + void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table ipc_table; memcpy(&ipc_table, table, sizeof(ipc_table)); ipc_table.data = get_ipc(table); - return proc_doulongvec_minmax(&ipc_table, write, filp, buffer, + return proc_doulongvec_minmax(&ipc_table, write, buffer, lenp, ppos); } @@ -95,7 +95,7 @@ static void ipc_auto_callback(int val) } static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) + void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table ipc_table; size_t lenp_bef = *lenp; @@ -106,7 +106,7 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write, ipc_table.data = get_ipc(table); oldval = *((int *)(ipc_table.data)); - rc = proc_dointvec_minmax(&ipc_table, write, filp, buffer, lenp, ppos); + rc = proc_dointvec_minmax(&ipc_table, write, buffer, lenp, ppos); if (write && !rc && lenp_bef == *lenp) { int newval = *((int *)(ipc_table.data)); diff --git a/ipc/mq_sysctl.c b/ipc/mq_sysctl.c index 24ae46dfe45d..8a058711fc10 100644 --- a/ipc/mq_sysctl.c +++ b/ipc/mq_sysctl.c @@ -31,24 +31,24 @@ static void *get_mq(ctl_table *table) return which; } -static int proc_mq_dointvec(ctl_table *table, int write, struct file *filp, +static int proc_mq_dointvec(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table mq_table; memcpy(&mq_table, table, sizeof(mq_table)); mq_table.data = get_mq(table); - return proc_dointvec(&mq_table, write, filp, buffer, lenp, ppos); + return proc_dointvec(&mq_table, write, buffer, lenp, ppos); } static int proc_mq_dointvec_minmax(ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) + void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table mq_table; memcpy(&mq_table, table, sizeof(mq_table)); mq_table.data = get_mq(table); - return proc_dointvec_minmax(&mq_table, write, filp, buffer, + return proc_dointvec_minmax(&mq_table, write, buffer, lenp, ppos); } #else diff --git a/kernel/hung_task.c b/kernel/hung_task.c index 022a4927b785..d4e841747400 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c @@ -171,12 +171,12 @@ static unsigned long timeout_jiffies(unsigned long timeout) * Process updating of timeout sysctl */ int proc_dohung_task_timeout_secs(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; - ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos); + ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos); if (ret || !write) goto out; diff --git a/kernel/sched.c b/kernel/sched.c index 0d0361b9dbb3..ee61f454a98b 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -10312,7 +10312,7 @@ static int sched_rt_global_constraints(void) #endif /* CONFIG_RT_GROUP_SCHED */ int sched_rt_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; @@ -10323,7 +10323,7 @@ int sched_rt_handler(struct ctl_table *table, int write, old_period = sysctl_sched_rt_period; old_runtime = sysctl_sched_rt_runtime; - ret = proc_dointvec(table, write, filp, buffer, lenp, ppos); + ret = proc_dointvec(table, write, buffer, lenp, ppos); if (!ret && write) { ret = sched_rt_global_constraints(); diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c index ecc637a0d591..4e777b47eeda 100644 --- a/kernel/sched_fair.c +++ b/kernel/sched_fair.c @@ -384,10 +384,10 @@ static struct sched_entity *__pick_last_entity(struct cfs_rq *cfs_rq) #ifdef CONFIG_SCHED_DEBUG int sched_nr_latency_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos) { - int ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos); + int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret || !write) return ret; diff --git a/kernel/slow-work.c b/kernel/slow-work.c index 09d7519557d3..0d31135efbf4 100644 --- a/kernel/slow-work.c +++ b/kernel/slow-work.c @@ -26,10 +26,10 @@ static void slow_work_cull_timeout(unsigned long); static void slow_work_oom_timeout(unsigned long); #ifdef CONFIG_SYSCTL -static int slow_work_min_threads_sysctl(struct ctl_table *, int, struct file *, +static int slow_work_min_threads_sysctl(struct ctl_table *, int, void __user *, size_t *, loff_t *); -static int slow_work_max_threads_sysctl(struct ctl_table *, int , struct file *, +static int slow_work_max_threads_sysctl(struct ctl_table *, int , void __user *, size_t *, loff_t *); #endif @@ -493,10 +493,10 @@ static void slow_work_oom_timeout(unsigned long data) * Handle adjustment of the minimum number of threads */ static int slow_work_min_threads_sysctl(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos) { - int ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos); + int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); int n; if (ret == 0) { @@ -521,10 +521,10 @@ static int slow_work_min_threads_sysctl(struct ctl_table *table, int write, * Handle adjustment of the maximum number of threads */ static int slow_work_max_threads_sysctl(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos) { - int ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos); + int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); int n; if (ret == 0) { diff --git a/kernel/softlockup.c b/kernel/softlockup.c index 88796c330838..81324d12eb35 100644 --- a/kernel/softlockup.c +++ b/kernel/softlockup.c @@ -90,11 +90,11 @@ void touch_all_softlockup_watchdogs(void) EXPORT_SYMBOL(touch_all_softlockup_watchdogs); int proc_dosoftlockup_thresh(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos) { touch_all_softlockup_watchdogs(); - return proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos); + return proc_dointvec_minmax(table, write, buffer, lenp, ppos); } /* diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 37abb8c3995b..a02697b7cb97 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -163,9 +163,9 @@ extern int max_lock_depth; #endif #ifdef CONFIG_PROC_SYSCTL -static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp, +static int proc_do_cad_pid(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); -static int proc_taint(struct ctl_table *table, int write, struct file *filp, +static int proc_taint(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos); #endif @@ -2226,7 +2226,7 @@ void sysctl_head_put(struct ctl_table_header *head) #ifdef CONFIG_PROC_SYSCTL static int _proc_do_string(void* data, int maxlen, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos) { size_t len; @@ -2287,7 +2287,6 @@ static int _proc_do_string(void* data, int maxlen, int write, * proc_dostring - read a string sysctl * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file - * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position @@ -2301,10 +2300,10 @@ static int _proc_do_string(void* data, int maxlen, int write, * * Returns 0 on success. */ -int proc_dostring(struct ctl_table *table, int write, struct file *filp, +int proc_dostring(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return _proc_do_string(table->data, table->maxlen, write, filp, + return _proc_do_string(table->data, table->maxlen, write, buffer, lenp, ppos); } @@ -2329,7 +2328,7 @@ static int do_proc_dointvec_conv(int *negp, unsigned long *lvalp, } static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, - int write, struct file *filp, void __user *buffer, + int write, void __user *buffer, size_t *lenp, loff_t *ppos, int (*conv)(int *negp, unsigned long *lvalp, int *valp, int write, void *data), @@ -2436,13 +2435,13 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table, #undef TMPBUFLEN } -static int do_proc_dointvec(struct ctl_table *table, int write, struct file *filp, +static int do_proc_dointvec(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos, int (*conv)(int *negp, unsigned long *lvalp, int *valp, int write, void *data), void *data) { - return __do_proc_dointvec(table->data, table, write, filp, + return __do_proc_dointvec(table->data, table, write, buffer, lenp, ppos, conv, data); } @@ -2450,7 +2449,6 @@ static int do_proc_dointvec(struct ctl_table *table, int write, struct file *fil * proc_dointvec - read a vector of integers * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file - * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position @@ -2460,10 +2458,10 @@ static int do_proc_dointvec(struct ctl_table *table, int write, struct file *fil * * Returns 0 on success. */ -int proc_dointvec(struct ctl_table *table, int write, struct file *filp, +int proc_dointvec(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return do_proc_dointvec(table,write,filp,buffer,lenp,ppos, + return do_proc_dointvec(table,write,buffer,lenp,ppos, NULL,NULL); } @@ -2471,7 +2469,7 @@ int proc_dointvec(struct ctl_table *table, int write, struct file *filp, * Taint values can only be increased * This means we can safely use a temporary. */ -static int proc_taint(struct ctl_table *table, int write, struct file *filp, +static int proc_taint(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table t; @@ -2483,7 +2481,7 @@ static int proc_taint(struct ctl_table *table, int write, struct file *filp, t = *table; t.data = &tmptaint; - err = proc_doulongvec_minmax(&t, write, filp, buffer, lenp, ppos); + err = proc_doulongvec_minmax(&t, write, buffer, lenp, ppos); if (err < 0) return err; @@ -2535,7 +2533,6 @@ static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp, * proc_dointvec_minmax - read a vector of integers with min/max values * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file - * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position @@ -2548,19 +2545,18 @@ static int do_proc_dointvec_minmax_conv(int *negp, unsigned long *lvalp, * * Returns 0 on success. */ -int proc_dointvec_minmax(struct ctl_table *table, int write, struct file *filp, +int proc_dointvec_minmax(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct do_proc_dointvec_minmax_conv_param param = { .min = (int *) table->extra1, .max = (int *) table->extra2, }; - return do_proc_dointvec(table, write, filp, buffer, lenp, ppos, + return do_proc_dointvec(table, write, buffer, lenp, ppos, do_proc_dointvec_minmax_conv, ¶m); } static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos, unsigned long convmul, @@ -2665,21 +2661,19 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table, int } static int do_proc_doulongvec_minmax(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos, unsigned long convmul, unsigned long convdiv) { return __do_proc_doulongvec_minmax(table->data, table, write, - filp, buffer, lenp, ppos, convmul, convdiv); + buffer, lenp, ppos, convmul, convdiv); } /** * proc_doulongvec_minmax - read a vector of long integers with min/max values * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file - * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position @@ -2692,17 +2686,16 @@ static int do_proc_doulongvec_minmax(struct ctl_table *table, int write, * * Returns 0 on success. */ -int proc_doulongvec_minmax(struct ctl_table *table, int write, struct file *filp, +int proc_doulongvec_minmax(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return do_proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos, 1l, 1l); + return do_proc_doulongvec_minmax(table, write, buffer, lenp, ppos, 1l, 1l); } /** * proc_doulongvec_ms_jiffies_minmax - read a vector of millisecond values with min/max values * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file - * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position @@ -2717,11 +2710,10 @@ int proc_doulongvec_minmax(struct ctl_table *table, int write, struct file *filp * Returns 0 on success. */ int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) { - return do_proc_doulongvec_minmax(table, write, filp, buffer, + return do_proc_doulongvec_minmax(table, write, buffer, lenp, ppos, HZ, 1000l); } @@ -2797,7 +2789,6 @@ static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp, * proc_dointvec_jiffies - read a vector of integers as seconds * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file - * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position @@ -2809,10 +2800,10 @@ static int do_proc_dointvec_ms_jiffies_conv(int *negp, unsigned long *lvalp, * * Returns 0 on success. */ -int proc_dointvec_jiffies(struct ctl_table *table, int write, struct file *filp, +int proc_dointvec_jiffies(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return do_proc_dointvec(table,write,filp,buffer,lenp,ppos, + return do_proc_dointvec(table,write,buffer,lenp,ppos, do_proc_dointvec_jiffies_conv,NULL); } @@ -2820,7 +2811,6 @@ int proc_dointvec_jiffies(struct ctl_table *table, int write, struct file *filp, * proc_dointvec_userhz_jiffies - read a vector of integers as 1/USER_HZ seconds * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file - * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: pointer to the file position @@ -2832,10 +2822,10 @@ int proc_dointvec_jiffies(struct ctl_table *table, int write, struct file *filp, * * Returns 0 on success. */ -int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, struct file *filp, +int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return do_proc_dointvec(table,write,filp,buffer,lenp,ppos, + return do_proc_dointvec(table,write,buffer,lenp,ppos, do_proc_dointvec_userhz_jiffies_conv,NULL); } @@ -2843,7 +2833,6 @@ int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, struct file * proc_dointvec_ms_jiffies - read a vector of integers as 1 milliseconds * @table: the sysctl table * @write: %TRUE if this is a write to the sysctl file - * @filp: the file structure * @buffer: the user buffer * @lenp: the size of the user buffer * @ppos: file position @@ -2856,14 +2845,14 @@ int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, struct file * * Returns 0 on success. */ -int proc_dointvec_ms_jiffies(struct ctl_table *table, int write, struct file *filp, +int proc_dointvec_ms_jiffies(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - return do_proc_dointvec(table, write, filp, buffer, lenp, ppos, + return do_proc_dointvec(table, write, buffer, lenp, ppos, do_proc_dointvec_ms_jiffies_conv, NULL); } -static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp, +static int proc_do_cad_pid(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct pid *new_pid; @@ -2872,7 +2861,7 @@ static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp tmp = pid_vnr(cad_pid); - r = __do_proc_dointvec(&tmp, table, write, filp, buffer, + r = __do_proc_dointvec(&tmp, table, write, buffer, lenp, ppos, NULL, NULL); if (r || !write) return r; @@ -2887,50 +2876,49 @@ static int proc_do_cad_pid(struct ctl_table *table, int write, struct file *filp #else /* CONFIG_PROC_FS */ -int proc_dostring(struct ctl_table *table, int write, struct file *filp, +int proc_dostring(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { return -ENOSYS; } -int proc_dointvec(struct ctl_table *table, int write, struct file *filp, +int proc_dointvec(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { return -ENOSYS; } -int proc_dointvec_minmax(struct ctl_table *table, int write, struct file *filp, +int proc_dointvec_minmax(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { return -ENOSYS; } -int proc_dointvec_jiffies(struct ctl_table *table, int write, struct file *filp, +int proc_dointvec_jiffies(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { return -ENOSYS; } -int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, struct file *filp, +int proc_dointvec_userhz_jiffies(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { return -ENOSYS; } -int proc_dointvec_ms_jiffies(struct ctl_table *table, int write, struct file *filp, +int proc_dointvec_ms_jiffies(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { return -ENOSYS; } -int proc_doulongvec_minmax(struct ctl_table *table, int write, struct file *filp, +int proc_doulongvec_minmax(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { return -ENOSYS; } int proc_doulongvec_ms_jiffies_minmax(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) { diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 23df7771c937..a142579765bf 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -3015,7 +3015,7 @@ int unregister_ftrace_function(struct ftrace_ops *ops) int ftrace_enable_sysctl(struct ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; @@ -3025,7 +3025,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, mutex_lock(&ftrace_lock); - ret = proc_dointvec(table, write, file, buffer, lenp, ppos); + ret = proc_dointvec(table, write, buffer, lenp, ppos); if (ret || !write || (last_ftrace_enabled == !!ftrace_enabled)) goto out; diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c index 0f6facb050a1..8504ac71e4e8 100644 --- a/kernel/trace/trace_stack.c +++ b/kernel/trace/trace_stack.c @@ -296,14 +296,14 @@ static const struct file_operations stack_trace_fops = { int stack_trace_sysctl(struct ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; mutex_lock(&stack_sysctl_mutex); - ret = proc_dointvec(table, write, file, buffer, lenp, ppos); + ret = proc_dointvec(table, write, buffer, lenp, ppos); if (ret || !write || (last_stack_tracer_enabled == !!stack_tracer_enabled)) diff --git a/kernel/utsname_sysctl.c b/kernel/utsname_sysctl.c index 92359cc747a7..69eae358a726 100644 --- a/kernel/utsname_sysctl.c +++ b/kernel/utsname_sysctl.c @@ -42,14 +42,14 @@ static void put_uts(ctl_table *table, int write, void *which) * Special case of dostring for the UTS structure. This has locks * to observe. Should this be in kernel/sys.c ???? */ -static int proc_do_uts_string(ctl_table *table, int write, struct file *filp, +static int proc_do_uts_string(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table uts_table; int r; memcpy(&uts_table, table, sizeof(uts_table)); uts_table.data = get_uts(table, write); - r = proc_dostring(&uts_table,write,filp,buffer,lenp, ppos); + r = proc_dostring(&uts_table,write,buffer,lenp, ppos); put_uts(table, write, uts_table.data); return r; } diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 815dbd4a6dcb..6f048fcc749c 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1537,7 +1537,7 @@ static unsigned int cpuset_mems_nr(unsigned int *array) #ifdef CONFIG_SYSCTL int hugetlb_sysctl_handler(struct ctl_table *table, int write, - struct file *file, void __user *buffer, + void __user *buffer, size_t *length, loff_t *ppos) { struct hstate *h = &default_hstate; @@ -1548,7 +1548,7 @@ int hugetlb_sysctl_handler(struct ctl_table *table, int write, table->data = &tmp; table->maxlen = sizeof(unsigned long); - proc_doulongvec_minmax(table, write, file, buffer, length, ppos); + proc_doulongvec_minmax(table, write, buffer, length, ppos); if (write) h->max_huge_pages = set_max_huge_pages(h, tmp); @@ -1557,10 +1557,10 @@ int hugetlb_sysctl_handler(struct ctl_table *table, int write, } int hugetlb_treat_movable_handler(struct ctl_table *table, int write, - struct file *file, void __user *buffer, + void __user *buffer, size_t *length, loff_t *ppos) { - proc_dointvec(table, write, file, buffer, length, ppos); + proc_dointvec(table, write, buffer, length, ppos); if (hugepages_treat_as_movable) htlb_alloc_mask = GFP_HIGHUSER_MOVABLE; else @@ -1569,7 +1569,7 @@ int hugetlb_treat_movable_handler(struct ctl_table *table, int write, } int hugetlb_overcommit_handler(struct ctl_table *table, int write, - struct file *file, void __user *buffer, + void __user *buffer, size_t *length, loff_t *ppos) { struct hstate *h = &default_hstate; @@ -1580,7 +1580,7 @@ int hugetlb_overcommit_handler(struct ctl_table *table, int write, table->data = &tmp; table->maxlen = sizeof(unsigned long); - proc_doulongvec_minmax(table, write, file, buffer, length, ppos); + proc_doulongvec_minmax(table, write, buffer, length, ppos); if (write) { spin_lock(&hugetlb_lock); diff --git a/mm/page-writeback.c b/mm/page-writeback.c index 5f378dd58802..be197f71b096 100644 --- a/mm/page-writeback.c +++ b/mm/page-writeback.c @@ -155,37 +155,37 @@ static void update_completion_period(void) } int dirty_background_ratio_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; - ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos); + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret == 0 && write) dirty_background_bytes = 0; return ret; } int dirty_background_bytes_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; - ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos); + ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos); if (ret == 0 && write) dirty_background_ratio = 0; return ret; } int dirty_ratio_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos) { int old_ratio = vm_dirty_ratio; int ret; - ret = proc_dointvec_minmax(table, write, filp, buffer, lenp, ppos); + ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); if (ret == 0 && write && vm_dirty_ratio != old_ratio) { update_completion_period(); vm_dirty_bytes = 0; @@ -195,13 +195,13 @@ int dirty_ratio_handler(struct ctl_table *table, int write, int dirty_bytes_handler(struct ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos) { unsigned long old_bytes = vm_dirty_bytes; int ret; - ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos); + ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos); if (ret == 0 && write && vm_dirty_bytes != old_bytes) { update_completion_period(); vm_dirty_ratio = 0; @@ -686,9 +686,9 @@ static DEFINE_TIMER(laptop_mode_wb_timer, laptop_timer_fn, 0, 0); * sysctl handler for /proc/sys/vm/dirty_writeback_centisecs */ int dirty_writeback_centisecs_handler(ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *length, loff_t *ppos) + void __user *buffer, size_t *length, loff_t *ppos) { - proc_dointvec(table, write, file, buffer, length, ppos); + proc_dointvec(table, write, buffer, length, ppos); return 0; } diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 5717f27a0704..88248b3c20bb 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2373,7 +2373,7 @@ early_param("numa_zonelist_order", setup_numa_zonelist_order); * sysctl handler for numa_zonelist_order */ int numa_zonelist_order_handler(ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *length, + void __user *buffer, size_t *length, loff_t *ppos) { char saved_string[NUMA_ZONELIST_ORDER_LEN]; @@ -2382,7 +2382,7 @@ int numa_zonelist_order_handler(ctl_table *table, int write, if (write) strncpy(saved_string, (char*)table->data, NUMA_ZONELIST_ORDER_LEN); - ret = proc_dostring(table, write, file, buffer, length, ppos); + ret = proc_dostring(table, write, buffer, length, ppos); if (ret) return ret; if (write) { @@ -4706,9 +4706,9 @@ module_init(init_per_zone_wmark_min) * changes. */ int min_free_kbytes_sysctl_handler(ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *length, loff_t *ppos) + void __user *buffer, size_t *length, loff_t *ppos) { - proc_dointvec(table, write, file, buffer, length, ppos); + proc_dointvec(table, write, buffer, length, ppos); if (write) setup_per_zone_wmarks(); return 0; @@ -4716,12 +4716,12 @@ int min_free_kbytes_sysctl_handler(ctl_table *table, int write, #ifdef CONFIG_NUMA int sysctl_min_unmapped_ratio_sysctl_handler(ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *length, loff_t *ppos) + void __user *buffer, size_t *length, loff_t *ppos) { struct zone *zone; int rc; - rc = proc_dointvec_minmax(table, write, file, buffer, length, ppos); + rc = proc_dointvec_minmax(table, write, buffer, length, ppos); if (rc) return rc; @@ -4732,12 +4732,12 @@ int sysctl_min_unmapped_ratio_sysctl_handler(ctl_table *table, int write, } int sysctl_min_slab_ratio_sysctl_handler(ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *length, loff_t *ppos) + void __user *buffer, size_t *length, loff_t *ppos) { struct zone *zone; int rc; - rc = proc_dointvec_minmax(table, write, file, buffer, length, ppos); + rc = proc_dointvec_minmax(table, write, buffer, length, ppos); if (rc) return rc; @@ -4758,9 +4758,9 @@ int sysctl_min_slab_ratio_sysctl_handler(ctl_table *table, int write, * if in function of the boot time zone sizes. */ int lowmem_reserve_ratio_sysctl_handler(ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *length, loff_t *ppos) + void __user *buffer, size_t *length, loff_t *ppos) { - proc_dointvec_minmax(table, write, file, buffer, length, ppos); + proc_dointvec_minmax(table, write, buffer, length, ppos); setup_per_zone_lowmem_reserve(); return 0; } @@ -4772,13 +4772,13 @@ int lowmem_reserve_ratio_sysctl_handler(ctl_table *table, int write, */ int percpu_pagelist_fraction_sysctl_handler(ctl_table *table, int write, - struct file *file, void __user *buffer, size_t *length, loff_t *ppos) + void __user *buffer, size_t *length, loff_t *ppos) { struct zone *zone; unsigned int cpu; int ret; - ret = proc_dointvec_minmax(table, write, file, buffer, length, ppos); + ret = proc_dointvec_minmax(table, write, buffer, length, ppos); if (!write || (ret == -EINVAL)) return ret; for_each_populated_zone(zone) { diff --git a/mm/vmscan.c b/mm/vmscan.c index 2423782214ab..f444b7409085 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2844,10 +2844,10 @@ static void scan_all_zones_unevictable_pages(void) unsigned long scan_unevictable_pages; int scan_unevictable_handler(struct ctl_table *table, int write, - struct file *file, void __user *buffer, + void __user *buffer, size_t *length, loff_t *ppos) { - proc_doulongvec_minmax(table, write, file, buffer, length, ppos); + proc_doulongvec_minmax(table, write, buffer, length, ppos); if (write && *(unsigned long *)table->data) scan_all_zones_unevictable_pages(); diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 907a82e9023d..a16a2342f6bf 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -965,12 +965,12 @@ static struct nf_hook_ops br_nf_ops[] __read_mostly = { #ifdef CONFIG_SYSCTL static -int brnf_sysctl_call_tables(ctl_table * ctl, int write, struct file *filp, +int brnf_sysctl_call_tables(ctl_table * ctl, int write, void __user * buffer, size_t * lenp, loff_t * ppos) { int ret; - ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); if (write && *(int *)(ctl->data)) *(int *)(ctl->data) = 1; diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index 1c6a5bb6f0c8..6e1f085db06a 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -164,7 +164,7 @@ static int max_t3[] = { 8191 }; /* Must fit in 16 bits when multiplied by BCT3MU static int min_priority[1]; static int max_priority[] = { 127 }; /* From DECnet spec */ -static int dn_forwarding_proc(ctl_table *, int, struct file *, +static int dn_forwarding_proc(ctl_table *, int, void __user *, size_t *, loff_t *); static int dn_forwarding_sysctl(ctl_table *table, void __user *oldval, size_t __user *oldlenp, @@ -274,7 +274,6 @@ static void dn_dev_sysctl_unregister(struct dn_dev_parms *parms) } static int dn_forwarding_proc(ctl_table *table, int write, - struct file *filep, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -290,7 +289,7 @@ static int dn_forwarding_proc(ctl_table *table, int write, dn_db = dev->dn_ptr; old = dn_db->parms.forwarding; - err = proc_dointvec(table, write, filep, buffer, lenp, ppos); + err = proc_dointvec(table, write, buffer, lenp, ppos); if ((err >= 0) && write) { if (dn_db->parms.forwarding < 0) diff --git a/net/decnet/sysctl_net_decnet.c b/net/decnet/sysctl_net_decnet.c index 5bcd592ae6dd..26b0ab1e9f56 100644 --- a/net/decnet/sysctl_net_decnet.c +++ b/net/decnet/sysctl_net_decnet.c @@ -165,7 +165,6 @@ static int dn_node_address_strategy(ctl_table *table, } static int dn_node_address_handler(ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -276,7 +275,6 @@ static int dn_def_dev_strategy(ctl_table *table, static int dn_def_dev_handler(ctl_table *table, int write, - struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos) { diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 07336c6201f0..e92f1fd28aa5 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1270,10 +1270,10 @@ static void inet_forward_change(struct net *net) } static int devinet_conf_proc(ctl_table *ctl, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos) { - int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); if (write) { struct ipv4_devconf *cnf = ctl->extra1; @@ -1342,12 +1342,12 @@ static int devinet_conf_sysctl(ctl_table *table, } static int devinet_sysctl_forward(ctl_table *ctl, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos) { int *valp = ctl->data; int val = *valp; - int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); if (write && *valp != val) { struct net *net = ctl->extra2; @@ -1372,12 +1372,12 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write, } int ipv4_doint_and_flush(ctl_table *ctl, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos) { int *valp = ctl->data; int val = *valp; - int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); struct net *net = ctl->extra2; if (write && *valp != val) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index df9347314538..bb4199252026 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -3036,7 +3036,7 @@ void ip_rt_multicast_event(struct in_device *in_dev) #ifdef CONFIG_SYSCTL static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write, - struct file *filp, void __user *buffer, + void __user *buffer, size_t *lenp, loff_t *ppos) { if (write) { @@ -3046,7 +3046,7 @@ static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write, memcpy(&ctl, __ctl, sizeof(ctl)); ctl.data = &flush_delay; - proc_dointvec(&ctl, write, filp, buffer, lenp, ppos); + proc_dointvec(&ctl, write, buffer, lenp, ppos); net = (struct net *)__ctl->extra1; rt_cache_flush(net, flush_delay); @@ -3106,12 +3106,11 @@ static void rt_secret_reschedule(int old) } static int ipv4_sysctl_rt_secret_interval(ctl_table *ctl, int write, - struct file *filp, void __user *buffer, size_t *lenp, loff_t *ppos) { int old = ip_rt_secret_interval; - int ret = proc_dointvec_jiffies(ctl, write, filp, buffer, lenp, ppos); + int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos); rt_secret_reschedule(old); diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 4710d219f06a..2dcf04d9b005 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -36,7 +36,7 @@ static void set_local_port_range(int range[2]) } /* Validate changes from /proc interface. */ -static int ipv4_local_port_range(ctl_table *table, int write, struct file *filp, +static int ipv4_local_port_range(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -51,7 +51,7 @@ static int ipv4_local_port_range(ctl_table *table, int write, struct file *filp, }; inet_get_local_port_range(range, range + 1); - ret = proc_dointvec_minmax(&tmp, write, filp, buffer, lenp, ppos); + ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); if (write && ret == 0) { if (range[1] < range[0]) @@ -91,7 +91,7 @@ static int ipv4_sysctl_local_port_range(ctl_table *table, } -static int proc_tcp_congestion_control(ctl_table *ctl, int write, struct file * filp, +static int proc_tcp_congestion_control(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { char val[TCP_CA_NAME_MAX]; @@ -103,7 +103,7 @@ static int proc_tcp_congestion_control(ctl_table *ctl, int write, struct file * tcp_get_default_congestion_control(val); - ret = proc_dostring(&tbl, write, filp, buffer, lenp, ppos); + ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) ret = tcp_set_default_congestion_control(val); return ret; @@ -129,7 +129,7 @@ static int sysctl_tcp_congestion_control(ctl_table *table, } static int proc_tcp_available_congestion_control(ctl_table *ctl, - int write, struct file * filp, + int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -140,13 +140,13 @@ static int proc_tcp_available_congestion_control(ctl_table *ctl, if (!tbl.data) return -ENOMEM; tcp_get_available_congestion_control(tbl.data, TCP_CA_BUF_MAX); - ret = proc_dostring(&tbl, write, filp, buffer, lenp, ppos); + ret = proc_dostring(&tbl, write, buffer, lenp, ppos); kfree(tbl.data); return ret; } static int proc_allowed_congestion_control(ctl_table *ctl, - int write, struct file * filp, + int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -158,7 +158,7 @@ static int proc_allowed_congestion_control(ctl_table *ctl, return -ENOMEM; tcp_get_allowed_congestion_control(tbl.data, tbl.maxlen); - ret = proc_dostring(&tbl, write, filp, buffer, lenp, ppos); + ret = proc_dostring(&tbl, write, buffer, lenp, ppos); if (write && ret == 0) ret = tcp_set_allowed_congestion_control(tbl.data); kfree(tbl.data); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 55f486d89c88..1fd0a3d775d2 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3986,14 +3986,14 @@ static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp) #ifdef CONFIG_SYSCTL static -int addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp, +int addrconf_sysctl_forward(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int *valp = ctl->data; int val = *valp; int ret; - ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); if (write) ret = addrconf_fixup_forwarding(ctl, valp, val); @@ -4090,14 +4090,14 @@ static int addrconf_disable_ipv6(struct ctl_table *table, int *p, int old) } static -int addrconf_sysctl_disable(ctl_table *ctl, int write, struct file * filp, +int addrconf_sysctl_disable(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int *valp = ctl->data; int val = *valp; int ret; - ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); if (write) ret = addrconf_disable_ipv6(ctl, valp, val); diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 7015478797f6..498b9b0b0fad 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1735,7 +1735,7 @@ static void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl, } } -int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos) +int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct net_device *dev = ctl->extra1; struct inet6_dev *idev; @@ -1746,16 +1746,16 @@ int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * f ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default"); if (strcmp(ctl->procname, "retrans_time") == 0) - ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + ret = proc_dointvec(ctl, write, buffer, lenp, ppos); else if (strcmp(ctl->procname, "base_reachable_time") == 0) ret = proc_dointvec_jiffies(ctl, write, - filp, buffer, lenp, ppos); + buffer, lenp, ppos); else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) || (strcmp(ctl->procname, "base_reachable_time_ms") == 0)) ret = proc_dointvec_ms_jiffies(ctl, write, - filp, buffer, lenp, ppos); + buffer, lenp, ppos); else ret = -1; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 77aecbe8ff6c..d6fe7646a8ff 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -2524,13 +2524,13 @@ static const struct file_operations rt6_stats_seq_fops = { #ifdef CONFIG_SYSCTL static -int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp, +int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { struct net *net = current->nsproxy->net_ns; int delay = net->ipv6.sysctl.flush_delay; if (write) { - proc_dointvec(ctl, write, filp, buffer, lenp, ppos); + proc_dointvec(ctl, write, buffer, lenp, ppos); fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net); return 0; } else diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c index 57f8817c3979..5c86567e5a78 100644 --- a/net/irda/irsysctl.c +++ b/net/irda/irsysctl.c @@ -73,12 +73,12 @@ static int min_lap_keepalive_time = 100; /* 100us */ /* For other sysctl, I've no idea of the range. Maybe Dag could help * us on that - Jean II */ -static int do_devname(ctl_table *table, int write, struct file *filp, +static int do_devname(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; - ret = proc_dostring(table, write, filp, buffer, lenp, ppos); + ret = proc_dostring(table, write, buffer, lenp, ppos); if (ret == 0 && write) { struct ias_value *val; @@ -90,12 +90,12 @@ static int do_devname(ctl_table *table, int write, struct file *filp, } -static int do_discovery(ctl_table *table, int write, struct file *filp, +static int do_discovery(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; - ret = proc_dointvec(table, write, filp, buffer, lenp, ppos); + ret = proc_dointvec(table, write, buffer, lenp, ppos); if (ret) return ret; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index fba2892b99e1..446e9bd4b4bc 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -1496,14 +1496,14 @@ static int ip_vs_zero_all(void) static int -proc_do_defense_mode(ctl_table *table, int write, struct file * filp, +proc_do_defense_mode(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int *valp = table->data; int val = *valp; int rc; - rc = proc_dointvec(table, write, filp, buffer, lenp, ppos); + rc = proc_dointvec(table, write, buffer, lenp, ppos); if (write && (*valp != val)) { if ((*valp < 0) || (*valp > 3)) { /* Restore the correct value */ @@ -1517,7 +1517,7 @@ proc_do_defense_mode(ctl_table *table, int write, struct file * filp, static int -proc_do_sync_threshold(ctl_table *table, int write, struct file *filp, +proc_do_sync_threshold(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int *valp = table->data; @@ -1527,7 +1527,7 @@ proc_do_sync_threshold(ctl_table *table, int write, struct file *filp, /* backup the value first */ memcpy(val, valp, sizeof(val)); - rc = proc_dointvec(table, write, filp, buffer, lenp, ppos); + rc = proc_dointvec(table, write, buffer, lenp, ppos); if (write && (valp[0] < 0 || valp[1] < 0 || valp[0] >= valp[1])) { /* Restore the correct value */ memcpy(valp, val, sizeof(val)); diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 4e620305f28c..c93494fef8ef 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -226,7 +226,7 @@ static char nf_log_sysctl_fnames[NFPROTO_NUMPROTO-NFPROTO_UNSPEC][3]; static struct ctl_table nf_log_sysctl_table[NFPROTO_NUMPROTO+1]; static struct ctl_table_header *nf_log_dir_header; -static int nf_log_proc_dostring(ctl_table *table, int write, struct file *filp, +static int nf_log_proc_dostring(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { const struct nf_logger *logger; @@ -260,7 +260,7 @@ static int nf_log_proc_dostring(ctl_table *table, int write, struct file *filp, table->data = "NONE"; else table->data = logger->name; - r = proc_dostring(table, write, filp, buffer, lenp, ppos); + r = proc_dostring(table, write, buffer, lenp, ppos); mutex_unlock(&nf_log_mutex); } diff --git a/net/phonet/sysctl.c b/net/phonet/sysctl.c index 7b5749ee2765..2220f3322326 100644 --- a/net/phonet/sysctl.c +++ b/net/phonet/sysctl.c @@ -56,7 +56,7 @@ void phonet_get_local_port_range(int *min, int *max) } while (read_seqretry(&local_port_range_lock, seq)); } -static int proc_local_port_range(ctl_table *table, int write, struct file *filp, +static int proc_local_port_range(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { @@ -70,7 +70,7 @@ static int proc_local_port_range(ctl_table *table, int write, struct file *filp, .extra2 = &local_port_range_max, }; - ret = proc_dointvec_minmax(&tmp, write, filp, buffer, lenp, ppos); + ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); if (write && ret == 0) { if (range[1] < range[0]) diff --git a/net/sunrpc/sysctl.c b/net/sunrpc/sysctl.c index 5231f7aaac0e..42f9748ae093 100644 --- a/net/sunrpc/sysctl.c +++ b/net/sunrpc/sysctl.c @@ -56,7 +56,7 @@ rpc_unregister_sysctl(void) } } -static int proc_do_xprt(ctl_table *table, int write, struct file *file, +static int proc_do_xprt(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { char tmpbuf[256]; @@ -71,7 +71,7 @@ static int proc_do_xprt(ctl_table *table, int write, struct file *file, } static int -proc_dodebug(ctl_table *table, int write, struct file *file, +proc_dodebug(ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { char tmpbuf[20], c, *s; diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c index 87101177825b..35fb68b9c8ec 100644 --- a/net/sunrpc/xprtrdma/svc_rdma.c +++ b/net/sunrpc/xprtrdma/svc_rdma.c @@ -80,7 +80,7 @@ struct kmem_cache *svc_rdma_ctxt_cachep; * current value. */ static int read_reset_stat(ctl_table *table, int write, - struct file *filp, void __user *buffer, size_t *lenp, + void __user *buffer, size_t *lenp, loff_t *ppos) { atomic_t *stat = (atomic_t *)table->data; diff --git a/security/min_addr.c b/security/min_addr.c index 14cc7b3b8d03..c844eed7915d 100644 --- a/security/min_addr.c +++ b/security/min_addr.c @@ -28,12 +28,12 @@ static void update_mmap_min_addr(void) * sysctl handler which just sets dac_mmap_min_addr = the new value and then * calls update_mmap_min_addr() so non MAP_FIXED hints get rounded properly */ -int mmap_min_addr_handler(struct ctl_table *table, int write, struct file *filp, +int mmap_min_addr_handler(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { int ret; - ret = proc_doulongvec_minmax(table, write, filp, buffer, lenp, ppos); + ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos); update_mmap_min_addr(); -- cgit v1.2.3 From f0f37e2f77731b3473fa6bd5ee53255d9a9cdb40 Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Sun, 27 Sep 2009 22:29:37 +0400 Subject: const: mark struct vm_struct_operations * mark struct vm_area_struct::vm_ops as const * mark vm_ops in AGP code But leave TTM code alone, something is fishy there with global vm_ops being used. Signed-off-by: Alexey Dobriyan Signed-off-by: Linus Torvalds --- arch/ia64/ia32/binfmt_elf32.c | 4 ++-- arch/powerpc/platforms/cell/spufs/file.c | 14 +++++++------- arch/x86/pci/i386.c | 2 +- drivers/char/agp/agp.h | 2 +- drivers/char/agp/alpha-agp.c | 2 +- drivers/char/mem.c | 2 +- drivers/char/mspec.c | 2 +- drivers/gpu/drm/drm_vm.c | 8 ++++---- drivers/gpu/drm/radeon/radeon_ttm.c | 2 +- drivers/gpu/drm/ttm/ttm_bo_vm.c | 2 +- drivers/ieee1394/dma.c | 2 +- drivers/infiniband/hw/ehca/ehca_uverbs.c | 2 +- drivers/infiniband/hw/ipath/ipath_file_ops.c | 2 +- drivers/infiniband/hw/ipath/ipath_mmap.c | 2 +- drivers/media/video/cafe_ccic.c | 2 +- drivers/media/video/et61x251/et61x251_core.c | 2 +- drivers/media/video/gspca/gspca.c | 2 +- drivers/media/video/meye.c | 2 +- drivers/media/video/sn9c102/sn9c102_core.c | 2 +- drivers/media/video/stk-webcam.c | 2 +- drivers/media/video/uvc/uvc_v4l2.c | 2 +- drivers/media/video/videobuf-dma-contig.c | 2 +- drivers/media/video/videobuf-dma-sg.c | 2 +- drivers/media/video/videobuf-vmalloc.c | 2 +- drivers/media/video/vino.c | 2 +- drivers/media/video/zc0301/zc0301_core.c | 2 +- drivers/media/video/zoran/zoran_driver.c | 2 +- drivers/misc/sgi-gru/grufile.c | 2 +- drivers/misc/sgi-gru/grutables.h | 2 +- drivers/scsi/sg.c | 2 +- drivers/uio/uio.c | 2 +- drivers/usb/mon/mon_bin.c | 2 +- drivers/video/fb_defio.c | 2 +- drivers/video/omap/dispc.c | 2 +- fs/btrfs/file.c | 2 +- fs/ext4/file.c | 2 +- fs/fuse/file.c | 2 +- fs/gfs2/file.c | 2 +- fs/ncpfs/mmap.c | 2 +- fs/nfs/file.c | 4 ++-- fs/nilfs2/file.c | 2 +- fs/ocfs2/mmap.c | 2 +- fs/sysfs/bin.c | 4 ++-- fs/ubifs/file.c | 2 +- fs/xfs/linux-2.6/xfs_file.c | 4 ++-- include/linux/agp_backend.h | 2 +- include/linux/hugetlb.h | 2 +- include/linux/mm_types.h | 2 +- include/linux/ramfs.h | 2 +- ipc/shm.c | 4 ++-- kernel/perf_event.c | 2 +- kernel/relay.c | 2 +- mm/filemap.c | 2 +- mm/filemap_xip.c | 2 +- mm/hugetlb.c | 2 +- mm/mmap.c | 2 +- mm/nommu.c | 2 +- mm/shmem.c | 4 ++-- net/packet/af_packet.c | 2 +- sound/core/pcm_native.c | 8 ++++---- sound/usb/usx2y/us122l.c | 2 +- sound/usb/usx2y/usX2Yhwdep.c | 2 +- sound/usb/usx2y/usx2yhwdeppcm.c | 2 +- virt/kvm/kvm_main.c | 4 ++-- 64 files changed, 83 insertions(+), 83 deletions(-) (limited to 'drivers/char') diff --git a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c index f92bdaac8976..c69552bf893e 100644 --- a/arch/ia64/ia32/binfmt_elf32.c +++ b/arch/ia64/ia32/binfmt_elf32.c @@ -69,11 +69,11 @@ ia32_install_gate_page (struct vm_area_struct *vma, struct vm_fault *vmf) } -static struct vm_operations_struct ia32_shared_page_vm_ops = { +static const struct vm_operations_struct ia32_shared_page_vm_ops = { .fault = ia32_install_shared_page }; -static struct vm_operations_struct ia32_gate_page_vm_ops = { +static const struct vm_operations_struct ia32_gate_page_vm_ops = { .fault = ia32_install_gate_page }; diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 8f079b865ad0..961309446170 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c @@ -309,7 +309,7 @@ static int spufs_mem_mmap_access(struct vm_area_struct *vma, return len; } -static struct vm_operations_struct spufs_mem_mmap_vmops = { +static const struct vm_operations_struct spufs_mem_mmap_vmops = { .fault = spufs_mem_mmap_fault, .access = spufs_mem_mmap_access, }; @@ -436,7 +436,7 @@ static int spufs_cntl_mmap_fault(struct vm_area_struct *vma, return spufs_ps_fault(vma, vmf, 0x4000, SPUFS_CNTL_MAP_SIZE); } -static struct vm_operations_struct spufs_cntl_mmap_vmops = { +static const struct vm_operations_struct spufs_cntl_mmap_vmops = { .fault = spufs_cntl_mmap_fault, }; @@ -1143,7 +1143,7 @@ spufs_signal1_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) #endif } -static struct vm_operations_struct spufs_signal1_mmap_vmops = { +static const struct vm_operations_struct spufs_signal1_mmap_vmops = { .fault = spufs_signal1_mmap_fault, }; @@ -1279,7 +1279,7 @@ spufs_signal2_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) #endif } -static struct vm_operations_struct spufs_signal2_mmap_vmops = { +static const struct vm_operations_struct spufs_signal2_mmap_vmops = { .fault = spufs_signal2_mmap_fault, }; @@ -1397,7 +1397,7 @@ spufs_mss_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_MSS_MAP_SIZE); } -static struct vm_operations_struct spufs_mss_mmap_vmops = { +static const struct vm_operations_struct spufs_mss_mmap_vmops = { .fault = spufs_mss_mmap_fault, }; @@ -1458,7 +1458,7 @@ spufs_psmap_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return spufs_ps_fault(vma, vmf, 0x0000, SPUFS_PS_MAP_SIZE); } -static struct vm_operations_struct spufs_psmap_mmap_vmops = { +static const struct vm_operations_struct spufs_psmap_mmap_vmops = { .fault = spufs_psmap_mmap_fault, }; @@ -1517,7 +1517,7 @@ spufs_mfc_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return spufs_ps_fault(vma, vmf, 0x3000, SPUFS_MFC_MAP_SIZE); } -static struct vm_operations_struct spufs_mfc_mmap_vmops = { +static const struct vm_operations_struct spufs_mfc_mmap_vmops = { .fault = spufs_mfc_mmap_fault, }; diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 52e62e57fedd..b22d13b0c71d 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -266,7 +266,7 @@ void pcibios_set_master(struct pci_dev *dev) pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); } -static struct vm_operations_struct pci_mmap_ops = { +static const struct vm_operations_struct pci_mmap_ops = { .access = generic_access_phys, }; diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index d6f36c004d9b..870f12cfed93 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -131,7 +131,7 @@ struct agp_bridge_driver { struct agp_bridge_data { const struct agp_version *version; const struct agp_bridge_driver *driver; - struct vm_operations_struct *vm_ops; + const struct vm_operations_struct *vm_ops; void *previous_size; void *current_size; void *dev_private_data; diff --git a/drivers/char/agp/alpha-agp.c b/drivers/char/agp/alpha-agp.c index 5ea4da8e9954..dd84af4d4f7e 100644 --- a/drivers/char/agp/alpha-agp.c +++ b/drivers/char/agp/alpha-agp.c @@ -40,7 +40,7 @@ static struct aper_size_info_fixed alpha_core_agp_sizes[] = { 0, 0, 0 }, /* filled in by alpha_core_agp_setup */ }; -struct vm_operations_struct alpha_core_agp_vm_ops = { +static const struct vm_operations_struct alpha_core_agp_vm_ops = { .fault = alpha_core_agp_vm_fault, }; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6c8b65d069e5..a074fceb67d3 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -301,7 +301,7 @@ static inline int private_mapping_ok(struct vm_area_struct *vma) } #endif -static struct vm_operations_struct mmap_mem_ops = { +static const struct vm_operations_struct mmap_mem_ops = { #ifdef CONFIG_HAVE_IOREMAP_PROT .access = generic_access_phys #endif diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c index 30f095a8c2d4..1997270bb6f4 100644 --- a/drivers/char/mspec.c +++ b/drivers/char/mspec.c @@ -239,7 +239,7 @@ mspec_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_NOPAGE; } -static struct vm_operations_struct mspec_vm_ops = { +static const struct vm_operations_struct mspec_vm_ops = { .open = mspec_open, .close = mspec_close, .fault = mspec_fault, diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 7e1fbe5d4779..4ac900f4647f 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -369,28 +369,28 @@ static int drm_vm_sg_fault(struct vm_area_struct *vma, struct vm_fault *vmf) } /** AGP virtual memory operations */ -static struct vm_operations_struct drm_vm_ops = { +static const struct vm_operations_struct drm_vm_ops = { .fault = drm_vm_fault, .open = drm_vm_open, .close = drm_vm_close, }; /** Shared virtual memory operations */ -static struct vm_operations_struct drm_vm_shm_ops = { +static const struct vm_operations_struct drm_vm_shm_ops = { .fault = drm_vm_shm_fault, .open = drm_vm_open, .close = drm_vm_shm_close, }; /** DMA virtual memory operations */ -static struct vm_operations_struct drm_vm_dma_ops = { +static const struct vm_operations_struct drm_vm_dma_ops = { .fault = drm_vm_dma_fault, .open = drm_vm_open, .close = drm_vm_close, }; /** Scatter-gather virtual memory operations */ -static struct vm_operations_struct drm_vm_sg_ops = { +static const struct vm_operations_struct drm_vm_sg_ops = { .fault = drm_vm_sg_fault, .open = drm_vm_open, .close = drm_vm_close, diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index acd889c94549..5b1cf04a011a 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -530,7 +530,7 @@ void radeon_ttm_fini(struct radeon_device *rdev) } static struct vm_operations_struct radeon_ttm_vm_ops; -static struct vm_operations_struct *ttm_vm_ops = NULL; +static const struct vm_operations_struct *ttm_vm_ops = NULL; static int radeon_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 33de7637c0c6..1c040d040338 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -228,7 +228,7 @@ static void ttm_bo_vm_close(struct vm_area_struct *vma) vma->vm_private_data = NULL; } -static struct vm_operations_struct ttm_bo_vm_ops = { +static const struct vm_operations_struct ttm_bo_vm_ops = { .fault = ttm_bo_vm_fault, .open = ttm_bo_vm_open, .close = ttm_bo_vm_close diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c index 1aba8c13fe8f..8e7e3344c4b3 100644 --- a/drivers/ieee1394/dma.c +++ b/drivers/ieee1394/dma.c @@ -247,7 +247,7 @@ static int dma_region_pagefault(struct vm_area_struct *vma, return 0; } -static struct vm_operations_struct dma_region_vm_ops = { +static const struct vm_operations_struct dma_region_vm_ops = { .fault = dma_region_pagefault, }; diff --git a/drivers/infiniband/hw/ehca/ehca_uverbs.c b/drivers/infiniband/hw/ehca/ehca_uverbs.c index 3cb688d29131..f1565cae8ec6 100644 --- a/drivers/infiniband/hw/ehca/ehca_uverbs.c +++ b/drivers/infiniband/hw/ehca/ehca_uverbs.c @@ -95,7 +95,7 @@ static void ehca_mm_close(struct vm_area_struct *vma) vma->vm_start, vma->vm_end, *count); } -static struct vm_operations_struct vm_ops = { +static const struct vm_operations_struct vm_ops = { .open = ehca_mm_open, .close = ehca_mm_close, }; diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 38a287006612..40dbe54056c7 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -1151,7 +1151,7 @@ static int ipath_file_vma_fault(struct vm_area_struct *vma, return 0; } -static struct vm_operations_struct ipath_file_vm_ops = { +static const struct vm_operations_struct ipath_file_vm_ops = { .fault = ipath_file_vma_fault, }; diff --git a/drivers/infiniband/hw/ipath/ipath_mmap.c b/drivers/infiniband/hw/ipath/ipath_mmap.c index fa830e22002f..b28865faf435 100644 --- a/drivers/infiniband/hw/ipath/ipath_mmap.c +++ b/drivers/infiniband/hw/ipath/ipath_mmap.c @@ -74,7 +74,7 @@ static void ipath_vma_close(struct vm_area_struct *vma) kref_put(&ip->ref, ipath_release_mmap_info); } -static struct vm_operations_struct ipath_vm_ops = { +static const struct vm_operations_struct ipath_vm_ops = { .open = ipath_vma_open, .close = ipath_vma_close, }; diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 657c481d255c..10230cb3d210 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -1325,7 +1325,7 @@ static void cafe_v4l_vm_close(struct vm_area_struct *vma) mutex_unlock(&sbuf->cam->s_mutex); } -static struct vm_operations_struct cafe_v4l_vm_ops = { +static const struct vm_operations_struct cafe_v4l_vm_ops = { .open = cafe_v4l_vm_open, .close = cafe_v4l_vm_close }; diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index 74092f436be6..88987a57cf7b 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -1496,7 +1496,7 @@ static void et61x251_vm_close(struct vm_area_struct* vma) } -static struct vm_operations_struct et61x251_vm_ops = { +static const struct vm_operations_struct et61x251_vm_ops = { .open = et61x251_vm_open, .close = et61x251_vm_close, }; diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index cf6540da1e42..23d3fb776918 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -99,7 +99,7 @@ static void gspca_vm_close(struct vm_area_struct *vma) frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED; } -static struct vm_operations_struct gspca_vm_ops = { +static const struct vm_operations_struct gspca_vm_ops = { .open = gspca_vm_open, .close = gspca_vm_close, }; diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index d0765bed79c9..4b1bc05a462c 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1589,7 +1589,7 @@ static void meye_vm_close(struct vm_area_struct *vma) meye.vma_use_count[idx]--; } -static struct vm_operations_struct meye_vm_ops = { +static const struct vm_operations_struct meye_vm_ops = { .open = meye_vm_open, .close = meye_vm_close, }; diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 9d84c94e8a40..4a7711c3e745 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -2077,7 +2077,7 @@ static void sn9c102_vm_close(struct vm_area_struct* vma) } -static struct vm_operations_struct sn9c102_vm_ops = { +static const struct vm_operations_struct sn9c102_vm_ops = { .open = sn9c102_vm_open, .close = sn9c102_vm_close, }; diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index 0b996ea4134e..6b41865f42bd 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -790,7 +790,7 @@ static void stk_v4l_vm_close(struct vm_area_struct *vma) if (sbuf->mapcount == 0) sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; } -static struct vm_operations_struct stk_v4l_vm_ops = { +static const struct vm_operations_struct stk_v4l_vm_ops = { .open = stk_v4l_vm_open, .close = stk_v4l_vm_close }; diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 9e7351569b5d..a2bdd806efab 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -1069,7 +1069,7 @@ static void uvc_vm_close(struct vm_area_struct *vma) buffer->vma_use_count--; } -static struct vm_operations_struct uvc_vm_ops = { +static const struct vm_operations_struct uvc_vm_ops = { .open = uvc_vm_open, .close = uvc_vm_close, }; diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index d09ce83a9429..635ffc7b0391 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -105,7 +105,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) } } -static struct vm_operations_struct videobuf_vm_ops = { +static const struct vm_operations_struct videobuf_vm_ops = { .open = videobuf_vm_open, .close = videobuf_vm_close, }; diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index a8dd22ace3fb..53cdd67cebe1 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -394,7 +394,7 @@ videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static struct vm_operations_struct videobuf_vm_ops = +static const struct vm_operations_struct videobuf_vm_ops = { .open = videobuf_vm_open, .close = videobuf_vm_close, diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 30ae30f99ccc..35f3900c5633 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -116,7 +116,7 @@ static void videobuf_vm_close(struct vm_area_struct *vma) return; } -static struct vm_operations_struct videobuf_vm_ops = +static const struct vm_operations_struct videobuf_vm_ops = { .open = videobuf_vm_open, .close = videobuf_vm_close, diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index cd6a3446ab7e..b034a81d2b1c 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -3857,7 +3857,7 @@ static void vino_vm_close(struct vm_area_struct *vma) dprintk("vino_vm_close(): count = %d\n", fb->map_count); } -static struct vm_operations_struct vino_vm_ops = { +static const struct vm_operations_struct vino_vm_ops = { .open = vino_vm_open, .close = vino_vm_close, }; diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index b3c6436b33ba..312a71336fd0 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -935,7 +935,7 @@ static void zc0301_vm_close(struct vm_area_struct* vma) } -static struct vm_operations_struct zc0301_vm_ops = { +static const struct vm_operations_struct zc0301_vm_ops = { .open = zc0301_vm_open, .close = zc0301_vm_close, }; diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index bcdefb1bcb3d..47137deafcfd 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -3172,7 +3172,7 @@ zoran_vm_close (struct vm_area_struct *vma) mutex_unlock(&zr->resource_lock); } -static struct vm_operations_struct zoran_vm_ops = { +static const struct vm_operations_struct zoran_vm_ops = { .open = zoran_vm_open, .close = zoran_vm_close, }; diff --git a/drivers/misc/sgi-gru/grufile.c b/drivers/misc/sgi-gru/grufile.c index aed609832bc2..300e7ba391a0 100644 --- a/drivers/misc/sgi-gru/grufile.c +++ b/drivers/misc/sgi-gru/grufile.c @@ -438,7 +438,7 @@ static struct miscdevice gru_miscdev = { .fops = &gru_fops, }; -struct vm_operations_struct gru_vm_ops = { +const struct vm_operations_struct gru_vm_ops = { .close = gru_vma_close, .fault = gru_fault, }; diff --git a/drivers/misc/sgi-gru/grutables.h b/drivers/misc/sgi-gru/grutables.h index 34ab3d453919..46990bcfa536 100644 --- a/drivers/misc/sgi-gru/grutables.h +++ b/drivers/misc/sgi-gru/grutables.h @@ -624,7 +624,7 @@ static inline int is_kernel_context(struct gru_thread_state *gts) */ struct gru_unload_context_req; -extern struct vm_operations_struct gru_vm_ops; +extern const struct vm_operations_struct gru_vm_ops; extern struct device *grudev; extern struct gru_vma_data *gru_alloc_vma_data(struct vm_area_struct *vma, diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 848b59466850..0cb049f5cc56 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -1185,7 +1185,7 @@ sg_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return VM_FAULT_SIGBUS; } -static struct vm_operations_struct sg_mmap_vm_ops = { +static const struct vm_operations_struct sg_mmap_vm_ops = { .fault = sg_vma_fault, }; diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 03efb065455f..a9d707047202 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -658,7 +658,7 @@ static int uio_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static struct vm_operations_struct uio_vm_ops = { +static const struct vm_operations_struct uio_vm_ops = { .open = uio_vma_open, .close = uio_vma_close, .fault = uio_vma_fault, diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index dfdc43e2e00d..9ed3e741bee1 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1174,7 +1174,7 @@ static int mon_bin_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static struct vm_operations_struct mon_bin_vm_ops = { +static const struct vm_operations_struct mon_bin_vm_ops = { .open = mon_bin_vma_open, .close = mon_bin_vma_close, .fault = mon_bin_vma_fault, diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index 0a7a6679ee6e..c27ab1ed9604 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -125,7 +125,7 @@ page_already_added: return 0; } -static struct vm_operations_struct fb_deferred_io_vm_ops = { +static const struct vm_operations_struct fb_deferred_io_vm_ops = { .fault = fb_deferred_io_fault, .page_mkwrite = fb_deferred_io_mkwrite, }; diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c index 80a11d078df4..f16e42154229 100644 --- a/drivers/video/omap/dispc.c +++ b/drivers/video/omap/dispc.c @@ -1035,7 +1035,7 @@ static void mmap_user_close(struct vm_area_struct *vma) atomic_dec(&dispc.map_count[plane]); } -static struct vm_operations_struct mmap_user_ops = { +static const struct vm_operations_struct mmap_user_ops = { .open = mmap_user_open, .close = mmap_user_close, }; diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 571ad3c13b47..a3492a3ad96b 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -1184,7 +1184,7 @@ out: return ret > 0 ? EIO : ret; } -static struct vm_operations_struct btrfs_file_vm_ops = { +static const struct vm_operations_struct btrfs_file_vm_ops = { .fault = filemap_fault, .page_mkwrite = btrfs_page_mkwrite, }; diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 5ca3eca70a1e..9630583cef28 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -81,7 +81,7 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov, return generic_file_aio_write(iocb, iov, nr_segs, pos); } -static struct vm_operations_struct ext4_file_vm_ops = { +static const struct vm_operations_struct ext4_file_vm_ops = { .fault = filemap_fault, .page_mkwrite = ext4_page_mkwrite, }; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index cbc464043b6f..a3492f7d207c 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1313,7 +1313,7 @@ static int fuse_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static struct vm_operations_struct fuse_file_vm_ops = { +static const struct vm_operations_struct fuse_file_vm_ops = { .close = fuse_vma_close, .fault = filemap_fault, .page_mkwrite = fuse_page_mkwrite, diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 166f38fbd246..4eb308aa3234 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -418,7 +418,7 @@ out: return ret; } -static struct vm_operations_struct gfs2_vm_ops = { +static const struct vm_operations_struct gfs2_vm_ops = { .fault = filemap_fault, .page_mkwrite = gfs2_page_mkwrite, }; diff --git a/fs/ncpfs/mmap.c b/fs/ncpfs/mmap.c index 5d8dcb9ee326..15458decdb8a 100644 --- a/fs/ncpfs/mmap.c +++ b/fs/ncpfs/mmap.c @@ -95,7 +95,7 @@ static int ncp_file_mmap_fault(struct vm_area_struct *area, return VM_FAULT_MAJOR; } -static struct vm_operations_struct ncp_file_mmap = +static const struct vm_operations_struct ncp_file_mmap = { .fault = ncp_file_mmap_fault, }; diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 86d6b4db1096..f5fdd39e037a 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -59,7 +59,7 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl); static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl); static int nfs_setlease(struct file *file, long arg, struct file_lock **fl); -static struct vm_operations_struct nfs_file_vm_ops; +static const struct vm_operations_struct nfs_file_vm_ops; const struct file_operations nfs_file_operations = { .llseek = nfs_file_llseek, @@ -572,7 +572,7 @@ out_unlock: return VM_FAULT_SIGBUS; } -static struct vm_operations_struct nfs_file_vm_ops = { +static const struct vm_operations_struct nfs_file_vm_ops = { .fault = filemap_fault, .page_mkwrite = nfs_vm_page_mkwrite, }; diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c index fc8278c77cdd..7d7b4983dee3 100644 --- a/fs/nilfs2/file.c +++ b/fs/nilfs2/file.c @@ -117,7 +117,7 @@ static int nilfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -struct vm_operations_struct nilfs_file_vm_ops = { +static const struct vm_operations_struct nilfs_file_vm_ops = { .fault = filemap_fault, .page_mkwrite = nilfs_page_mkwrite, }; diff --git a/fs/ocfs2/mmap.c b/fs/ocfs2/mmap.c index b606496b72ec..39737613424a 100644 --- a/fs/ocfs2/mmap.c +++ b/fs/ocfs2/mmap.c @@ -202,7 +202,7 @@ out: return ret; } -static struct vm_operations_struct ocfs2_file_vm_ops = { +static const struct vm_operations_struct ocfs2_file_vm_ops = { .fault = ocfs2_fault, .page_mkwrite = ocfs2_page_mkwrite, }; diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 2524714bece1..60c702bc10ae 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -40,7 +40,7 @@ struct bin_buffer { struct mutex mutex; void *buffer; int mmapped; - struct vm_operations_struct *vm_ops; + const struct vm_operations_struct *vm_ops; struct file *file; struct hlist_node list; }; @@ -331,7 +331,7 @@ static int bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, } #endif -static struct vm_operations_struct bin_vm_ops = { +static const struct vm_operations_struct bin_vm_ops = { .open = bin_vma_open, .close = bin_vma_close, .fault = bin_fault, diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index 2e6481a7701c..1009adc8d602 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -1534,7 +1534,7 @@ out_unlock: return err; } -static struct vm_operations_struct ubifs_file_vm_ops = { +static const struct vm_operations_struct ubifs_file_vm_ops = { .fault = filemap_fault, .page_mkwrite = ubifs_vm_page_mkwrite, }; diff --git a/fs/xfs/linux-2.6/xfs_file.c b/fs/xfs/linux-2.6/xfs_file.c index 988d8f87bc0f..629370974e57 100644 --- a/fs/xfs/linux-2.6/xfs_file.c +++ b/fs/xfs/linux-2.6/xfs_file.c @@ -42,7 +42,7 @@ #include -static struct vm_operations_struct xfs_file_vm_ops; +static const struct vm_operations_struct xfs_file_vm_ops; STATIC ssize_t xfs_file_aio_read( @@ -280,7 +280,7 @@ const struct file_operations xfs_dir_file_operations = { .fsync = xfs_file_fsync, }; -static struct vm_operations_struct xfs_file_vm_ops = { +static const struct vm_operations_struct xfs_file_vm_ops = { .fault = filemap_fault, .page_mkwrite = xfs_vm_page_mkwrite, }; diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h index 880130f7311f..9101ed64f803 100644 --- a/include/linux/agp_backend.h +++ b/include/linux/agp_backend.h @@ -53,7 +53,7 @@ struct agp_kern_info { int current_memory; bool cant_use_aperture; unsigned long page_mask; - struct vm_operations_struct *vm_ops; + const struct vm_operations_struct *vm_ops; }; /* diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 16937995abd4..41a59afc70fa 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -163,7 +163,7 @@ static inline struct hugetlbfs_sb_info *HUGETLBFS_SB(struct super_block *sb) } extern const struct file_operations hugetlbfs_file_operations; -extern struct vm_operations_struct hugetlb_vm_ops; +extern const struct vm_operations_struct hugetlb_vm_ops; struct file *hugetlb_file_setup(const char *name, size_t size, int acct, struct user_struct **user, int creat_flags); int hugetlb_get_quota(struct address_space *mapping, long delta); diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 21d6aa45206a..84a524afb3dc 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -171,7 +171,7 @@ struct vm_area_struct { struct anon_vma *anon_vma; /* Serialized by page_table_lock */ /* Function pointers to deal with this struct. */ - struct vm_operations_struct * vm_ops; + const struct vm_operations_struct *vm_ops; /* Information about our backing store: */ unsigned long vm_pgoff; /* Offset (within vm_file) in PAGE_SIZE diff --git a/include/linux/ramfs.h b/include/linux/ramfs.h index 37aaf2b39863..4e768dda87b0 100644 --- a/include/linux/ramfs.h +++ b/include/linux/ramfs.h @@ -17,7 +17,7 @@ extern int ramfs_nommu_mmap(struct file *file, struct vm_area_struct *vma); #endif extern const struct file_operations ramfs_file_operations; -extern struct vm_operations_struct generic_file_vm_ops; +extern const struct vm_operations_struct generic_file_vm_ops; extern int __init init_rootfs(void); #endif diff --git a/ipc/shm.c b/ipc/shm.c index 9eb1488b543b..464694e0aa4a 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -55,7 +55,7 @@ struct shm_file_data { #define shm_file_data(file) (*((struct shm_file_data **)&(file)->private_data)) static const struct file_operations shm_file_operations; -static struct vm_operations_struct shm_vm_ops; +static const struct vm_operations_struct shm_vm_ops; #define shm_ids(ns) ((ns)->ids[IPC_SHM_IDS]) @@ -312,7 +312,7 @@ static const struct file_operations shm_file_operations = { .get_unmapped_area = shm_get_unmapped_area, }; -static struct vm_operations_struct shm_vm_ops = { +static const struct vm_operations_struct shm_vm_ops = { .open = shm_open, /* callback for a new vm-area open */ .close = shm_close, /* callback for when the vm-area is released */ .fault = shm_fault, diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 76ac4db405e9..0f86feb6db0c 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -2253,7 +2253,7 @@ static void perf_mmap_close(struct vm_area_struct *vma) } } -static struct vm_operations_struct perf_mmap_vmops = { +static const struct vm_operations_struct perf_mmap_vmops = { .open = perf_mmap_open, .close = perf_mmap_close, .fault = perf_mmap_fault, diff --git a/kernel/relay.c b/kernel/relay.c index bc188549788f..760c26209a3c 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -60,7 +60,7 @@ static int relay_buf_fault(struct vm_area_struct *vma, struct vm_fault *vmf) /* * vm_ops for relay file mappings. */ -static struct vm_operations_struct relay_file_mmap_ops = { +static const struct vm_operations_struct relay_file_mmap_ops = { .fault = relay_buf_fault, .close = relay_file_mmap_close, }; diff --git a/mm/filemap.c b/mm/filemap.c index 6c84e598b4a9..ef169f37156d 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -1611,7 +1611,7 @@ page_not_uptodate: } EXPORT_SYMBOL(filemap_fault); -struct vm_operations_struct generic_file_vm_ops = { +const struct vm_operations_struct generic_file_vm_ops = { .fault = filemap_fault, }; diff --git a/mm/filemap_xip.c b/mm/filemap_xip.c index 427dfe3ce78c..1888b2d71bb8 100644 --- a/mm/filemap_xip.c +++ b/mm/filemap_xip.c @@ -296,7 +296,7 @@ out: } } -static struct vm_operations_struct xip_file_vm_ops = { +static const struct vm_operations_struct xip_file_vm_ops = { .fault = xip_file_fault, }; diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 6f048fcc749c..5d7601b02874 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1721,7 +1721,7 @@ static int hugetlb_vm_op_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -struct vm_operations_struct hugetlb_vm_ops = { +const struct vm_operations_struct hugetlb_vm_ops = { .fault = hugetlb_vm_op_fault, .open = hugetlb_vm_op_open, .close = hugetlb_vm_op_close, diff --git a/mm/mmap.c b/mm/mmap.c index 21d4029a07b3..73f5e4b64010 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2282,7 +2282,7 @@ static void special_mapping_close(struct vm_area_struct *vma) { } -static struct vm_operations_struct special_mapping_vmops = { +static const struct vm_operations_struct special_mapping_vmops = { .close = special_mapping_close, .fault = special_mapping_fault, }; diff --git a/mm/nommu.c b/mm/nommu.c index c73aa4753d79..5189b5aed8c0 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -79,7 +79,7 @@ static struct kmem_cache *vm_region_jar; struct rb_root nommu_region_tree = RB_ROOT; DECLARE_RWSEM(nommu_region_sem); -struct vm_operations_struct generic_file_vm_ops = { +const struct vm_operations_struct generic_file_vm_ops = { }; /* diff --git a/mm/shmem.c b/mm/shmem.c index ccf446a9faa1..356dd99566ec 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -218,7 +218,7 @@ static const struct file_operations shmem_file_operations; static const struct inode_operations shmem_inode_operations; static const struct inode_operations shmem_dir_inode_operations; static const struct inode_operations shmem_special_inode_operations; -static struct vm_operations_struct shmem_vm_ops; +static const struct vm_operations_struct shmem_vm_ops; static struct backing_dev_info shmem_backing_dev_info __read_mostly = { .ra_pages = 0, /* No readahead */ @@ -2498,7 +2498,7 @@ static const struct super_operations shmem_ops = { .put_super = shmem_put_super, }; -static struct vm_operations_struct shmem_vm_ops = { +static const struct vm_operations_struct shmem_vm_ops = { .fault = shmem_fault, #ifdef CONFIG_NUMA .set_policy = shmem_set_policy, diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index d3d52c66cdc2..103d5611b818 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2084,7 +2084,7 @@ static void packet_mm_close(struct vm_area_struct *vma) atomic_dec(&pkt_sk(sk)->mapped); } -static struct vm_operations_struct packet_mmap_ops = { +static const struct vm_operations_struct packet_mmap_ops = { .open = packet_mm_open, .close = packet_mm_close, }; diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 561d6d95a2d3..ab73edf2c89a 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2985,7 +2985,7 @@ static int snd_pcm_mmap_status_fault(struct vm_area_struct *area, return 0; } -static struct vm_operations_struct snd_pcm_vm_ops_status = +static const struct vm_operations_struct snd_pcm_vm_ops_status = { .fault = snd_pcm_mmap_status_fault, }; @@ -3024,7 +3024,7 @@ static int snd_pcm_mmap_control_fault(struct vm_area_struct *area, return 0; } -static struct vm_operations_struct snd_pcm_vm_ops_control = +static const struct vm_operations_struct snd_pcm_vm_ops_control = { .fault = snd_pcm_mmap_control_fault, }; @@ -3094,7 +3094,7 @@ static int snd_pcm_mmap_data_fault(struct vm_area_struct *area, return 0; } -static struct vm_operations_struct snd_pcm_vm_ops_data = +static const struct vm_operations_struct snd_pcm_vm_ops_data = { .open = snd_pcm_mmap_data_open, .close = snd_pcm_mmap_data_close, @@ -3118,7 +3118,7 @@ static int snd_pcm_default_mmap(struct snd_pcm_substream *substream, * mmap the DMA buffer on I/O memory area */ #if SNDRV_PCM_INFO_MMAP_IOMEM -static struct vm_operations_struct snd_pcm_vm_ops_data_mmio = +static const struct vm_operations_struct snd_pcm_vm_ops_data_mmio = { .open = snd_pcm_mmap_data_open, .close = snd_pcm_mmap_data_close, diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c index fd44946ce4b3..99f33766cd51 100644 --- a/sound/usb/usx2y/us122l.c +++ b/sound/usb/usx2y/us122l.c @@ -154,7 +154,7 @@ static void usb_stream_hwdep_vm_close(struct vm_area_struct *area) snd_printdd(KERN_DEBUG "%i\n", atomic_read(&us122l->mmap_count)); } -static struct vm_operations_struct usb_stream_hwdep_vm_ops = { +static const struct vm_operations_struct usb_stream_hwdep_vm_ops = { .open = usb_stream_hwdep_vm_open, .fault = usb_stream_hwdep_vm_fault, .close = usb_stream_hwdep_vm_close, diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c index f3d8f71265dd..52e04b2f35d3 100644 --- a/sound/usb/usx2y/usX2Yhwdep.c +++ b/sound/usb/usx2y/usX2Yhwdep.c @@ -53,7 +53,7 @@ static int snd_us428ctls_vm_fault(struct vm_area_struct *area, return 0; } -static struct vm_operations_struct us428ctls_vm_ops = { +static const struct vm_operations_struct us428ctls_vm_ops = { .fault = snd_us428ctls_vm_fault, }; diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c index 117946f2debb..4b2304c2e02d 100644 --- a/sound/usb/usx2y/usx2yhwdeppcm.c +++ b/sound/usb/usx2y/usx2yhwdeppcm.c @@ -697,7 +697,7 @@ static int snd_usX2Y_hwdep_pcm_vm_fault(struct vm_area_struct *area, } -static struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = { +static const struct vm_operations_struct snd_usX2Y_hwdep_pcm_vm_ops = { .open = snd_usX2Y_hwdep_pcm_vm_open, .close = snd_usX2Y_hwdep_pcm_vm_close, .fault = snd_usX2Y_hwdep_pcm_vm_fault, diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 034a798b0431..b5e7e3f1183f 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1713,7 +1713,7 @@ static int kvm_vcpu_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static struct vm_operations_struct kvm_vcpu_vm_ops = { +static const struct vm_operations_struct kvm_vcpu_vm_ops = { .fault = kvm_vcpu_fault, }; @@ -2317,7 +2317,7 @@ static int kvm_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return 0; } -static struct vm_operations_struct kvm_vm_vm_ops = { +static const struct vm_operations_struct kvm_vm_vm_ops = { .fault = kvm_vm_fault, }; -- cgit v1.2.3 From f278a2f7bbc2239f479eaf63d0b3ae573b1d746c Mon Sep 17 00:00:00 2001 From: Dave Young Date: Sun, 27 Sep 2009 16:00:42 +0000 Subject: tty: Fix regressions caused by commit b50989dc The following commit made console open fails while booting: commit b50989dc444599c8b21edc23536fc305f4e9b7d5 Author: Alan Cox Date: Sat Sep 19 13:13:22 2009 -0700 tty: make the kref destructor occur asynchronously Due to tty release routines run in a workqueue now, error like the following will be reported while booting: INIT open /dev/console Input/output error It also causes hibernation regression to appear as reported at http://bugzilla.kernel.org/show_bug.cgi?id=14229 The reason is that now there's latency issue with closing, but when we open a "closing not finished" tty, -EIO will be returned. Fix it as per the following Alan's suggestion: Fun but it's actually not a bug and the fix is wrong in itself as the port may be closing but not yet being destructed, in which case it seems to do the wrong thing. Opening a tty that is closing (and could be closing for long periods) is supposed to return -EIO. I suspect a better way to deal with this and keep the old console timing is to split tty->shutdown into two functions. tty->shutdown() - called synchronously just before we dump the tty onto the waitqueue for destruction tty->cleanup() - called when the destructor runs. We would then do the shutdown part which can occur in IRQ context fine, before queueing the rest of the release (from tty->magic = 0 ... the end) to occur asynchronously The USB update in -next would then need a call like if (tty->cleanup) tty->cleanup(tty); at the top of the async function and the USB shutdown to be split between shutdown and cleanup as the USB resource cleanup and final tidy cannot occur synchronously as it needs to sleep. In other words the logic becomes final kref put make object unfindable async clean it up Signed-off-by: Dave Young [ rjw: Rebased on top of 2.6.31-git, reworked the changelog. ] Signed-off-by: "Rafael J. Wysocki" [ Changed serial naming to match new rules, dropped tty_shutdown as per comments from Alan Stern - Linus ] Signed-off-by: Linus Torvalds --- drivers/char/tty_io.c | 15 ++++++++++----- drivers/usb/serial/usb-serial.c | 14 ++++++-------- include/linux/tty_driver.h | 13 +++++++++++-- 3 files changed, 27 insertions(+), 15 deletions(-) (limited to 'drivers/char') diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index ea18a129b0b5..59499ee0fe6a 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1389,7 +1389,7 @@ EXPORT_SYMBOL(tty_shutdown); * of ttys that the driver keeps. * * This method gets called from a work queue so that the driver private - * shutdown ops can sleep (needed for USB at least) + * cleanup ops can sleep (needed for USB at least) */ static void release_one_tty(struct work_struct *work) { @@ -1397,10 +1397,9 @@ static void release_one_tty(struct work_struct *work) container_of(work, struct tty_struct, hangup_work); struct tty_driver *driver = tty->driver; - if (tty->ops->shutdown) - tty->ops->shutdown(tty); - else - tty_shutdown(tty); + if (tty->ops->cleanup) + tty->ops->cleanup(tty); + tty->magic = 0; tty_driver_kref_put(driver); module_put(driver->owner); @@ -1415,6 +1414,12 @@ static void release_one_tty(struct work_struct *work) static void queue_release_one_tty(struct kref *kref) { struct tty_struct *tty = container_of(kref, struct tty_struct, kref); + + if (tty->ops->shutdown) + tty->ops->shutdown(tty); + else + tty_shutdown(tty); + /* The hangup queue is now free so we can reuse it rather than waste a chunk of memory for each port */ INIT_WORK(&tty->hangup_work, release_one_tty); diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index ff75a3589e7e..aa6b2ae951ae 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -192,7 +192,7 @@ void usb_serial_put(struct usb_serial *serial) * This is the first place a new tty gets used. Hence this is where we * acquire references to the usb_serial structure and the driver module, * where we store a pointer to the port, and where we do an autoresume. - * All these actions are reversed in serial_release(). + * All these actions are reversed in serial_cleanup(). */ static int serial_install(struct tty_driver *driver, struct tty_struct *tty) { @@ -339,15 +339,16 @@ static void serial_close(struct tty_struct *tty, struct file *filp) } /** - * serial_release - free resources post close/hangup + * serial_cleanup - free resources post close/hangup * @port: port to free up * * Do the resource freeing and refcount dropping for the port. * Avoid freeing the console. * - * Called when the last tty kref is dropped. + * Called asynchronously after the last tty kref is dropped, + * and the tty layer has already done the tty_shutdown(tty); */ -static void serial_release(struct tty_struct *tty) +static void serial_cleanup(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial; @@ -361,9 +362,6 @@ static void serial_release(struct tty_struct *tty) dbg("%s - port %d", __func__, port->number); - /* Standard shutdown processing */ - tty_shutdown(tty); - tty->driver_data = NULL; serial = port->serial; @@ -1210,7 +1208,7 @@ static const struct tty_operations serial_ops = { .chars_in_buffer = serial_chars_in_buffer, .tiocmget = serial_tiocmget, .tiocmset = serial_tiocmset, - .shutdown = serial_release, + .cleanup = serial_cleanup, .install = serial_install, .proc_fops = &serial_proc_fops, }; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 3566129384a4..b08677982525 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -45,8 +45,16 @@ * * void (*shutdown)(struct tty_struct * tty); * - * This routine is called when a particular tty device is closed for - * the last time freeing up the resources. + * This routine is called synchronously when a particular tty device + * is closed for the last time freeing up the resources. + * + * + * void (*cleanup)(struct tty_struct * tty); + * + * This routine is called asynchronously when a particular tty device + * is closed for the last time freeing up the resources. This is + * actually the second part of shutdown for routines that might sleep. + * * * int (*write)(struct tty_struct * tty, * const unsigned char *buf, int count); @@ -233,6 +241,7 @@ struct tty_operations { int (*open)(struct tty_struct * tty, struct file * filp); void (*close)(struct tty_struct * tty, struct file * filp); void (*shutdown)(struct tty_struct *tty); + void (*cleanup)(struct tty_struct *tty); int (*write)(struct tty_struct * tty, const unsigned char *buf, int count); int (*put_char)(struct tty_struct *tty, unsigned char ch); -- cgit v1.2.3